@oomol-lab/open-connector 1.6.2 → 1.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/README.md +27 -0
  2. package/assets/open-connector/catalog/apps/assured_platform.json +218 -0
  3. package/assets/open-connector/catalog/apps/baixiao.json +1080 -0
  4. package/assets/open-connector/catalog/apps/benchmarkone.json +956 -0
  5. package/assets/open-connector/catalog/apps/bing_webmaster.json +3097 -0
  6. package/assets/open-connector/catalog/apps/buzzsprout.json +1979 -0
  7. package/assets/open-connector/catalog/apps/feishu.json +13 -19
  8. package/assets/open-connector/catalog/apps/feishu_app_bot.json +10 -17
  9. package/assets/open-connector/catalog/apps/linkfox.json +836 -0
  10. package/assets/open-connector/catalog/apps/metatraderapi.json +1077 -0
  11. package/assets/open-connector/catalog/apps/mktindex.json +2411 -0
  12. package/assets/open-connector/catalog/apps/mojo_helpdesk.json +660 -0
  13. package/assets/open-connector/catalog/apps/mumble.json +446 -0
  14. package/assets/open-connector/catalog/apps/notion.json +112 -3
  15. package/assets/open-connector/catalog/apps/omise.json +438 -0
  16. package/assets/open-connector/catalog/apps/panjiva.json +2858 -0
  17. package/assets/open-connector/catalog/apps/papertrail.json +449 -0
  18. package/assets/open-connector/catalog/apps/peec.json +747 -0
  19. package/assets/open-connector/catalog/apps/polygon_io.json +5156 -584
  20. package/assets/open-connector/catalog/apps/quant_data.json +1928 -0
  21. package/assets/open-connector/catalog/apps/riddle_quiz_maker.json +621 -0
  22. package/assets/open-connector/catalog/apps/slack.json +327 -4
  23. package/assets/open-connector/catalog/apps/slackbot.json +280 -3
  24. package/assets/open-connector/catalog/apps/templatedocs.json +438 -0
  25. package/assets/open-connector/catalog/apps/terminus_app.json +510 -0
  26. package/assets/open-connector/catalog/apps/ticket_tailor.json +584 -0
  27. package/assets/open-connector/catalog/apps/tongdaxin.json +2612 -79
  28. package/assets/open-connector/catalog/apps/veremark.json +514 -0
  29. package/assets/open-connector/catalog/apps/walletap.json +549 -0
  30. package/assets/open-connector/catalog/apps/xiaohongshu_store.json +955 -39
  31. package/assets/open-connector/catalog/apps-index.json +1 -1
  32. package/package.json +5 -1
  33. package/src/connection-service.d.ts +2 -0
  34. package/src/connection-service.js +8 -1
  35. package/src/core/provider-id.d.ts +5 -0
  36. package/src/core/provider-id.js +12 -0
  37. package/src/oauth/oauth-credential-refresh-service.js +2 -0
  38. package/src/oauth/oauth-flow-service.js +2 -0
  39. package/src/providers/action-contracts.generated.d.ts +27 -7
  40. package/src/providers/assured_platform/executors.d.ts +4 -0
  41. package/src/providers/assured_platform/executors.js +18 -0
  42. package/src/providers/assured_platform/runtime.d.ts +7 -0
  43. package/src/providers/assured_platform/runtime.js +108 -0
  44. package/src/providers/baixiao/actions.d.ts +2 -0
  45. package/src/providers/baixiao/actions.js +377 -0
  46. package/src/providers/baixiao/executors.d.ts +4 -0
  47. package/src/providers/baixiao/executors.js +106 -0
  48. package/src/providers/benchmarkone/executors.d.ts +4 -0
  49. package/src/providers/benchmarkone/executors.js +26 -0
  50. package/src/providers/benchmarkone/runtime.d.ts +12 -0
  51. package/src/providers/benchmarkone/runtime.js +113 -0
  52. package/src/providers/bing_webmaster/actions.d.ts +2 -0
  53. package/src/providers/bing_webmaster/actions.js +113 -0
  54. package/src/providers/bing_webmaster/executors.d.ts +4 -0
  55. package/src/providers/bing_webmaster/executors.js +104 -0
  56. package/src/providers/bing_webmaster/management-actions.d.ts +2 -0
  57. package/src/providers/bing_webmaster/management-actions.js +241 -0
  58. package/src/providers/bing_webmaster/reporting-actions.d.ts +2 -0
  59. package/src/providers/bing_webmaster/reporting-actions.js +300 -0
  60. package/src/providers/bing_webmaster/schemas.d.ts +33 -0
  61. package/src/providers/bing_webmaster/schemas.js +313 -0
  62. package/src/providers/buzzsprout/executors.d.ts +4 -0
  63. package/src/providers/buzzsprout/executors.js +21 -0
  64. package/src/providers/buzzsprout/runtime.d.ts +12 -0
  65. package/src/providers/buzzsprout/runtime.js +237 -0
  66. package/src/providers/feishu/shared/drive-actions.d.ts +1 -0
  67. package/src/providers/feishu/shared/drive-actions.js +13 -9
  68. package/src/providers/feishu/shared/drive-runtime.js +15 -3
  69. package/src/providers/feishu/shared/wiki-actions.js +4 -2
  70. package/src/providers/hithink_finance/runtime.js +39 -60
  71. package/src/providers/linkfox/actions.js +2 -0
  72. package/src/providers/linkfox/runtime.js +70 -1
  73. package/src/providers/linkfox/sif-actions.d.ts +10 -0
  74. package/src/providers/linkfox/sif-actions.js +193 -0
  75. package/src/providers/metatraderapi/executors.d.ts +4 -0
  76. package/src/providers/metatraderapi/executors.js +18 -0
  77. package/src/providers/metatraderapi/runtime.d.ts +7 -0
  78. package/src/providers/metatraderapi/runtime.js +216 -0
  79. package/src/providers/mktindex/actions-mmi.d.ts +2 -0
  80. package/src/providers/mktindex/actions-mmi.js +510 -0
  81. package/src/providers/mktindex/actions.d.ts +2 -0
  82. package/src/providers/mktindex/actions.js +2 -0
  83. package/src/providers/mktindex/executors.d.ts +4 -0
  84. package/src/providers/mktindex/executors.js +27 -0
  85. package/src/providers/mktindex/request.d.ts +17 -0
  86. package/src/providers/mktindex/request.js +48 -0
  87. package/src/providers/mktindex/runtime.d.ts +9 -0
  88. package/src/providers/mktindex/runtime.js +492 -0
  89. package/src/providers/mojo_helpdesk/executors.d.ts +4 -0
  90. package/src/providers/mojo_helpdesk/executors.js +18 -0
  91. package/src/providers/mojo_helpdesk/runtime.d.ts +7 -0
  92. package/src/providers/mojo_helpdesk/runtime.js +173 -0
  93. package/src/providers/mumble/executors.d.ts +4 -0
  94. package/src/providers/mumble/executors.js +99 -0
  95. package/src/providers/notion/executors.d.ts +9 -0
  96. package/src/providers/notion/executors.js +161 -6
  97. package/src/providers/omise/executors.d.ts +4 -0
  98. package/src/providers/omise/executors.js +120 -0
  99. package/src/providers/panjiva/actions.d.ts +14 -0
  100. package/src/providers/panjiva/actions.js +389 -0
  101. package/src/providers/panjiva/executors.d.ts +4 -0
  102. package/src/providers/panjiva/executors.js +136 -0
  103. package/src/providers/papertrail/executors.d.ts +4 -0
  104. package/src/providers/papertrail/executors.js +15 -0
  105. package/src/providers/papertrail/runtime.d.ts +7 -0
  106. package/src/providers/papertrail/runtime.js +167 -0
  107. package/src/providers/peec/executors.d.ts +4 -0
  108. package/src/providers/peec/executors.js +15 -0
  109. package/src/providers/peec/runtime.d.ts +7 -0
  110. package/src/providers/peec/runtime.js +128 -0
  111. package/src/providers/polygon_io/actions.d.ts +2 -0
  112. package/src/providers/polygon_io/actions.js +282 -0
  113. package/src/providers/polygon_io/expanded-actions.d.ts +2 -0
  114. package/src/providers/polygon_io/expanded-actions.js +375 -0
  115. package/src/providers/polygon_io/expanded-runtime.d.ts +16 -0
  116. package/src/providers/polygon_io/expanded-runtime.js +277 -0
  117. package/src/providers/polygon_io/runtime.js +21 -2
  118. package/src/providers/quant_data/executors.d.ts +4 -0
  119. package/src/providers/quant_data/executors.js +15 -0
  120. package/src/providers/quant_data/runtime.d.ts +7 -0
  121. package/src/providers/quant_data/runtime.js +118 -0
  122. package/src/providers/registry.generated.js +20 -0
  123. package/src/providers/riddle_quiz_maker/executors.d.ts +4 -0
  124. package/src/providers/riddle_quiz_maker/executors.js +15 -0
  125. package/src/providers/riddle_quiz_maker/runtime.d.ts +7 -0
  126. package/src/providers/riddle_quiz_maker/runtime.js +145 -0
  127. package/src/providers/slack/actions.js +106 -3
  128. package/src/providers/slack/runtime.js +260 -36
  129. package/src/providers/slackbot/actions.js +1 -1
  130. package/src/providers/templatedocs/executors.d.ts +4 -0
  131. package/src/providers/templatedocs/executors.js +18 -0
  132. package/src/providers/templatedocs/runtime.d.ts +13 -0
  133. package/src/providers/templatedocs/runtime.js +151 -0
  134. package/src/providers/terminus_app/executors.d.ts +4 -0
  135. package/src/providers/terminus_app/executors.js +15 -0
  136. package/src/providers/terminus_app/runtime.d.ts +7 -0
  137. package/src/providers/terminus_app/runtime.js +172 -0
  138. package/src/providers/ticket_tailor/executors.d.ts +4 -0
  139. package/src/providers/ticket_tailor/executors.js +15 -0
  140. package/src/providers/ticket_tailor/runtime.d.ts +7 -0
  141. package/src/providers/ticket_tailor/runtime.js +152 -0
  142. package/src/providers/tongdaxin/actions.d.ts +1 -0
  143. package/src/providers/tongdaxin/actions.js +6 -3
  144. package/src/providers/tongdaxin/executors.js +51 -10
  145. package/src/providers/tongdaxin/named-action-output.d.ts +1 -0
  146. package/src/providers/tongdaxin/named-action-output.js +149 -0
  147. package/src/providers/tongdaxin/named-action-routing.d.ts +9 -0
  148. package/src/providers/tongdaxin/named-action-routing.js +417 -0
  149. package/src/providers/tongdaxin/named-actions.d.ts +2 -0
  150. package/src/providers/tongdaxin/named-actions.js +378 -0
  151. package/src/providers/veremark/executors.d.ts +4 -0
  152. package/src/providers/veremark/executors.js +129 -0
  153. package/src/providers/walletap/executors.d.ts +4 -0
  154. package/src/providers/walletap/executors.js +15 -0
  155. package/src/providers/walletap/runtime.d.ts +7 -0
  156. package/src/providers/walletap/runtime.js +147 -0
  157. package/src/providers/weixin_official_account/access-token.js +31 -7
  158. package/src/providers/weixin_store/access-token.js +33 -7
  159. package/src/providers/xiaohongshu_store/executors.js +152 -3
  160. package/src/server/api/openapi.js +3 -0
  161. package/src/server/connector-assets.d.ts +4 -0
  162. package/src/server/connector-assets.js +12 -0
  163. package/src/server/connector-build.d.ts +33 -0
  164. package/src/server/connector-build.js +73 -0
  165. package/src/server/connector-runtime.d.ts +1 -2
  166. package/src/server/connector-runtime.js +2 -8
  167. package/src/server/server-assets.d.ts +0 -28
  168. package/src/server/server-assets.js +0 -50
@@ -0,0 +1,21 @@
1
+ import { defineApiKeyProviderExecutors, defineProviderProxy } from "../provider-runtime.js";
2
+ import { buzzsproutActionHandlers, buzzsproutApiBaseUrl, validateBuzzsproutCredential } from "./runtime.js";
3
+ const service = "buzzsprout";
4
+ export const executors = defineApiKeyProviderExecutors(service, buzzsproutActionHandlers, {
5
+ skipDnsValidation: true,
6
+ });
7
+ export const proxy = defineProviderProxy({
8
+ service,
9
+ baseUrl: buzzsproutApiBaseUrl,
10
+ auth: { type: "api_key_authorization", prefix: "Token token=" },
11
+ skipDnsValidation: true,
12
+ customizeRequest({ headers }) {
13
+ if (!headers.has("accept"))
14
+ headers.set("accept", "application/json");
15
+ },
16
+ });
17
+ export const credentialValidators = {
18
+ async apiKey(input, { fetcher, signal }) {
19
+ return validateBuzzsproutCredential(input.apiKey, fetcher, signal);
20
+ },
21
+ };
@@ -0,0 +1,12 @@
1
+ import type { ApiKeyProviderContext, ProviderActionHandlers } from "../provider-runtime.ts";
2
+ export declare const buzzsproutApiBaseUrl = "https://www.buzzsprout.com/api";
3
+ type Handler = (input: Record<string, unknown>, context: ApiKeyProviderContext) => Promise<unknown>;
4
+ export declare const buzzsproutActionHandlers: ProviderActionHandlers<"buzzsprout", Handler>;
5
+ export declare function validateBuzzsproutCredential(apiKey: string, fetcher: typeof fetch, signal?: AbortSignal): Promise<{
6
+ profile: {
7
+ displayName: string;
8
+ };
9
+ grantedScopes: string[];
10
+ metadata: Record<string, unknown>;
11
+ }>;
12
+ export {};
@@ -0,0 +1,237 @@
1
+ import { compactObject, objectArray, optionalInteger, optionalRecord, optionalString, rawStringOrNull, requiredBoolean, requiredNumber, requiredRecord, requiredString, } from "../../core/cast.js";
2
+ import { readProviderTextBody } from "../provider-runtime.js";
3
+ import { providerResponseError, providerUserAgent, ProviderRequestError } from "../provider-runtime.js";
4
+ export const buzzsproutApiBaseUrl = "https://www.buzzsprout.com/api";
5
+ export const buzzsproutActionHandlers = {
6
+ async list_podcasts(_input, context) {
7
+ return { podcasts: objects(await request("/podcasts", context), "Buzzsprout podcasts").map(normalizePodcast) };
8
+ },
9
+ async get_podcast(input, context) {
10
+ return { podcast: normalizePodcast(await request(`/podcasts/${id(input.podcastId, "podcastId")}`, context)) };
11
+ },
12
+ async update_podcast(input, context) {
13
+ return {
14
+ podcast: normalizePodcast(await request(`/podcasts/${id(input.podcastId, "podcastId")}`, context, "PATCH", podcastBody(input))),
15
+ };
16
+ },
17
+ async list_episodes(input, context) {
18
+ return {
19
+ episodes: objects(await request(`/${id(input.podcastId, "podcastId")}/episodes`, context), "Buzzsprout episodes").map(normalizeEpisode),
20
+ };
21
+ },
22
+ async get_episode(input, context) {
23
+ return {
24
+ episode: normalizeEpisode(await request(`/${id(input.podcastId, "podcastId")}/episodes/${id(input.episodeId, "episodeId")}`, context)),
25
+ };
26
+ },
27
+ async create_episode(input, context) {
28
+ return {
29
+ episode: normalizeEpisode(await request(`/${id(input.podcastId, "podcastId")}/episodes`, context, "POST", episodeBody(input))),
30
+ };
31
+ },
32
+ async update_episode(input, context) {
33
+ return {
34
+ episode: normalizeEpisode(await request(`/${id(input.podcastId, "podcastId")}/episodes/${id(input.episodeId, "episodeId")}`, context, "PATCH", episodeBody(input))),
35
+ };
36
+ },
37
+ async list_podcast_downloads(input, context) {
38
+ const payload = objects(await request(`/${id(input.podcastId, "podcastId")}/downloads`, context, "GET", undefined, {
39
+ date: optionalString(input.date),
40
+ }), "Buzzsprout podcast downloads");
41
+ return {
42
+ downloads: payload.map((value) => {
43
+ const record = requiredRecord(value, "Buzzsprout podcast download", providerResponseError);
44
+ return {
45
+ episodeId: positive(record.episode_id, "Buzzsprout download episode_id"),
46
+ total: nonNegative(record.total, "Buzzsprout download total"),
47
+ };
48
+ }),
49
+ };
50
+ },
51
+ async list_episode_downloads(input, context) {
52
+ const payload = requiredRecord(await request(`/${id(input.podcastId, "podcastId")}/${id(input.episodeId, "episodeId")}/downloads`, context, "GET", undefined, { date: optionalString(input.date), page: stringInteger(input.page), per_page: stringInteger(input.perPage) }), "Buzzsprout episode downloads", providerResponseError);
53
+ const episode = objects(payload.episodes, "Buzzsprout episode downloads episodes")[0];
54
+ if (!episode)
55
+ throw providerResponseError("Buzzsprout episode downloads response is empty");
56
+ const pagination = requiredRecord(payload.pagination, "Buzzsprout pagination", providerResponseError);
57
+ return {
58
+ episodeId: positive(episode.id, "Buzzsprout episode id"),
59
+ downloads: objects(episode.downloads, "Buzzsprout downloads").map(normalizeDownload),
60
+ page: positive(pagination.page, "Buzzsprout pagination page"),
61
+ perPage: positive(pagination.per_page, "Buzzsprout pagination per_page"),
62
+ hasMore: requiredBoolean(pagination.has_more, "Buzzsprout pagination has_more", providerResponseError),
63
+ nextPage: pagination.next_page == null ? null : positive(pagination.next_page, "Buzzsprout pagination next_page"),
64
+ };
65
+ },
66
+ };
67
+ export async function validateBuzzsproutCredential(apiKey, fetcher, signal) {
68
+ const context = { apiKey, fetcher, signal };
69
+ const podcasts = objects(await request("/podcasts", context), "Buzzsprout podcasts");
70
+ return {
71
+ profile: { displayName: optionalString(optionalRecord(podcasts[0])?.title) ?? "Buzzsprout API Token" },
72
+ grantedScopes: [],
73
+ metadata: { apiBaseUrl: buzzsproutApiBaseUrl, validationEndpoint: "/podcasts", podcastCount: podcasts.length },
74
+ };
75
+ }
76
+ function podcastBody(input) {
77
+ return compactObject({
78
+ title: input.title,
79
+ description: input.description,
80
+ keywords: input.keywords,
81
+ author: input.author,
82
+ explicit: input.explicit,
83
+ language: input.language,
84
+ timezone: input.timezone,
85
+ website_address: input.websiteAddress,
86
+ contact_email: input.contactEmail,
87
+ category: input.category,
88
+ category2: input.category2,
89
+ category3: input.category3,
90
+ artwork_url: input.artworkUrl,
91
+ });
92
+ }
93
+ function episodeBody(input) {
94
+ return compactObject({
95
+ title: input.title,
96
+ description: input.description,
97
+ artist: input.artist,
98
+ tags: input.tags,
99
+ published_at: input.publishedAt,
100
+ season_number: input.seasonNumber,
101
+ episode_number: input.episodeNumber,
102
+ episode_type: input.episodeType,
103
+ explicit: input.explicit,
104
+ private: input.private,
105
+ custom_url: input.customUrl,
106
+ artwork_url: input.artworkUrl,
107
+ });
108
+ }
109
+ async function request(path, context, method = "GET", body, query) {
110
+ const url = new URL(`${buzzsproutApiBaseUrl}${path}`);
111
+ for (const [key, value] of Object.entries(query ?? {}))
112
+ if (value !== undefined)
113
+ url.searchParams.set(key, value);
114
+ const response = await context.fetcher(url, {
115
+ method,
116
+ headers: {
117
+ accept: "application/json",
118
+ authorization: `Token token=${context.apiKey}`,
119
+ "content-type": "application/json; charset=utf-8",
120
+ "user-agent": providerUserAgent,
121
+ },
122
+ body: body ? JSON.stringify(body) : undefined,
123
+ signal: context.signal,
124
+ });
125
+ const text = await readProviderTextBody(response, "Buzzsprout response");
126
+ let payload = null;
127
+ try {
128
+ payload = text ? JSON.parse(text) : null;
129
+ }
130
+ catch {
131
+ if (response.ok)
132
+ throw providerResponseError("Buzzsprout returned invalid JSON");
133
+ payload = text;
134
+ }
135
+ if (!response.ok)
136
+ throw new ProviderRequestError(response.status, errorMessage(payload) ?? `Buzzsprout request failed with status ${response.status}`, payload);
137
+ return payload;
138
+ }
139
+ function errorMessage(payload) {
140
+ if (typeof payload === "string")
141
+ return payload.trim() || undefined;
142
+ const record = optionalRecord(payload), nested = optionalRecord(record?.error);
143
+ return optionalString(nested?.message) ?? optionalString(record?.message) ?? optionalString(record?.error);
144
+ }
145
+ function normalizePodcast(value) {
146
+ const r = requiredRecord(value, "Buzzsprout podcast", providerResponseError);
147
+ return {
148
+ id: positive(r.id, "podcast id"),
149
+ title: requiredString(r.title, "podcast title", providerResponseError),
150
+ author: rawStringOrNull(r.author),
151
+ description: rawStringOrNull(r.description),
152
+ websiteAddress: rawStringOrNull(r.website_address),
153
+ websiteUrl: rawStringOrNull(r.website_url),
154
+ contactEmail: rawStringOrNull(r.contact_email),
155
+ keywords: rawStringOrNull(r.keywords),
156
+ explicit: requiredBoolean(r.explicit, "podcast explicit", providerResponseError),
157
+ mainCategory: rawStringOrNull(r.main_category),
158
+ subCategory: rawStringOrNull(r.sub_category),
159
+ mainCategory2: rawStringOrNull(r.main_category2),
160
+ subCategory2: rawStringOrNull(r.sub_category2),
161
+ mainCategory3: rawStringOrNull(r.main_category3),
162
+ subCategory3: rawStringOrNull(r.sub_category3),
163
+ language: rawStringOrNull(r.language),
164
+ timezone: rawStringOrNull(r.timezone),
165
+ artworkUrl: rawStringOrNull(r.artwork_url),
166
+ backgroundUrl: rawStringOrNull(r.background_url),
167
+ rssUrl: rawStringOrNull(r.rss_url),
168
+ raw: r,
169
+ };
170
+ }
171
+ function normalizeEpisode(value) {
172
+ const r = requiredRecord(value, "Buzzsprout episode", providerResponseError);
173
+ return {
174
+ id: positive(r.id, "episode id"),
175
+ title: requiredString(r.title, "episode title", providerResponseError),
176
+ audioUrl: rawStringOrNull(r.audio_url),
177
+ artworkUrl: rawStringOrNull(r.artwork_url),
178
+ description: rawStringOrNull(r.description),
179
+ artist: rawStringOrNull(r.artist),
180
+ tags: rawStringOrNull(r.tags),
181
+ publishedAt: rawStringOrNull(r.published_at),
182
+ duration: optionalIntegerAtLeast(r.duration, "episode duration", -1),
183
+ guid: rawStringOrNull(r.guid),
184
+ customUrl: rawStringOrNull(r.custom_url),
185
+ episodeNumber: optionalIntegerAtLeast(r.episode_number, "episode number", 0),
186
+ seasonNumber: optionalIntegerAtLeast(r.season_number, "season number", 0),
187
+ episodeType: rawStringOrNull(r.episode_type),
188
+ explicit: requiredBoolean(r.explicit, "episode explicit", providerResponseError),
189
+ private: requiredBoolean(r.private, "episode private", providerResponseError),
190
+ totalPlays: optionalIntegerAtLeast(r.total_plays, "episode total plays", 0),
191
+ raw: r,
192
+ };
193
+ }
194
+ function normalizeDownload(value) {
195
+ const r = requiredRecord(value, "Buzzsprout download", providerResponseError);
196
+ return {
197
+ app: requiredString(r.app, "download app", providerResponseError),
198
+ device: requiredString(r.device, "download device", providerResponseError),
199
+ deviceType: requiredString(r.device_type, "download device_type", providerResponseError),
200
+ countryCode: rawStringOrNull(r.country_code),
201
+ region: rawStringOrNull(r.region),
202
+ city: rawStringOrNull(r.city),
203
+ continent: rawStringOrNull(r.continent),
204
+ mediaType: requiredString(r.media_type, "download media_type", providerResponseError),
205
+ };
206
+ }
207
+ function objects(value, label) {
208
+ return objectArray(value, label, providerResponseError);
209
+ }
210
+ function id(value, label) {
211
+ return positive(value, label);
212
+ }
213
+ function positive(value, label) {
214
+ const number = requiredNumber(value, label, providerResponseError);
215
+ if (!Number.isInteger(number) || number <= 0)
216
+ throw providerResponseError(`${label} must be a positive integer`);
217
+ return number;
218
+ }
219
+ function nonNegative(value, label) {
220
+ const number = requiredNumber(value, label, providerResponseError);
221
+ if (!Number.isInteger(number) || number < 0)
222
+ throw providerResponseError(`${label} must be a non-negative integer`);
223
+ return number;
224
+ }
225
+ function stringInteger(value) {
226
+ const number = optionalInteger(value);
227
+ return number === undefined ? undefined : String(number);
228
+ }
229
+ function optionalIntegerAtLeast(value, label, minimum) {
230
+ if (value == null)
231
+ return null;
232
+ const number = requiredNumber(value, label, providerResponseError);
233
+ if (!Number.isInteger(number) || number < minimum) {
234
+ throw providerResponseError(`${label} must be an integer of at least ${minimum}`);
235
+ }
236
+ return number;
237
+ }
@@ -1,6 +1,7 @@
1
1
  import type { ActionDefinition } from "../../../core/types.ts";
2
2
  export declare const feishuDriveProviderScopes: {
3
3
  metadataRead: string;
4
+ folderList: string;
4
5
  search: string;
5
6
  folderCreate: string;
6
7
  copy: string;
@@ -2,6 +2,7 @@ import { s } from "../../../core/json-schema.js";
2
2
  import { defineProviderAction } from "../../../core/provider-definition.js";
3
3
  export const feishuDriveProviderScopes = {
4
4
  metadataRead: "drive:drive.metadata:readonly",
5
+ folderList: "space:document:retrieve",
5
6
  search: "search:docs:read",
6
7
  folderCreate: "space:folder:create",
7
8
  copy: "docs:document:copy",
@@ -12,7 +13,7 @@ export const feishuDriveProviderScopes = {
12
13
  commentCreate: "docs:document.comment:create",
13
14
  commentUpdate: "docs:document.comment:update",
14
15
  commentDelete: "docs:document.comment:delete",
15
- permissionRead: "docs:permission.member:readonly",
16
+ permissionRead: "docs:permission.member:retrieve",
16
17
  permissionCreate: "docs:permission.member:create",
17
18
  permissionUpdate: "docs:permission.member:update",
18
19
  permissionDelete: "docs:permission.member:delete",
@@ -154,8 +155,11 @@ export function createFeishuDriveActions(service) {
154
155
  name: "list_drive_files",
155
156
  operationType: "read",
156
157
  description: "List files, folders, and online documents inside a Feishu Drive folder.",
157
- requiredScopes: [feishuDriveProviderScopes.metadataRead],
158
- providerPermissions: [feishuDriveProviderScopes.metadataRead],
158
+ // Measured against Feishu with a user_access_token on 2026-09-14:
159
+ // metadataRead is rejected with 99991679, which names folderList as the
160
+ // narrowest accepted permission for this endpoint.
161
+ requiredScopes: [feishuDriveProviderScopes.folderList],
162
+ providerPermissions: [feishuDriveProviderScopes.folderList],
159
163
  inputSchema: s.object("Choose a folder, sort order, and page.", {
160
164
  folderToken: s.string("The folder token to list; omit it to list the caller's root folder.", {
161
165
  minLength: 1,
@@ -381,7 +385,7 @@ export function createFeishuDriveActions(service) {
381
385
  inputSchema: permissionResourceSchema({
382
386
  fields: s.string("A comma-separated projection of permission member fields."),
383
387
  permType: s.stringEnum("The Wiki permission scope.", ["container", "single_page"]),
384
- }),
388
+ }, ["fields", "permType"]),
385
389
  outputSchema: s.object("The permission members on a Drive resource.", {
386
390
  members: s.array("The permission members.", permissionSchema),
387
391
  }, {
@@ -405,7 +409,7 @@ export function createFeishuDriveActions(service) {
405
409
  "wiki_space_editor",
406
410
  ]),
407
411
  needNotification: s.boolean("Whether Feishu should notify the collaborator."),
408
- }),
412
+ }, ["permType", "memberKind", "needNotification"]),
409
413
  outputSchema: s.object("The granted Drive permission.", {
410
414
  member: {
411
415
  ...permissionSchema,
@@ -426,7 +430,7 @@ export function createFeishuDriveActions(service) {
426
430
  memberType: memberTypeSchema,
427
431
  permission: permissionRoleSchema,
428
432
  needNotification: s.boolean("Whether Feishu should notify the collaborator."),
429
- }),
433
+ }, ["needNotification"]),
430
434
  outputSchema: s.object("The updated Drive permission.", {
431
435
  member: {
432
436
  ...permissionSchema,
@@ -446,7 +450,7 @@ export function createFeishuDriveActions(service) {
446
450
  memberId: s.string("The collaborator identifier.", { minLength: 1 }),
447
451
  memberType: memberTypeSchema,
448
452
  permType: s.stringEnum("The Wiki permission scope.", ["container", "single_page"]),
449
- }),
453
+ }, ["permType"]),
450
454
  outputSchema: deletionOutputSchema("Whether the Drive permission was removed."),
451
455
  }),
452
456
  ];
@@ -491,12 +495,12 @@ function commentIdentitySchema(extra) {
491
495
  optional: [],
492
496
  });
493
497
  }
494
- function permissionResourceSchema(extra) {
498
+ function permissionResourceSchema(extra, optional) {
495
499
  return s.object("Identify a Feishu Drive permission resource and operation.", {
496
500
  token: resourceTokenSchema,
497
501
  resourceType: permissionResourceTypeSchema,
498
502
  ...extra,
499
503
  }, {
500
- optional: [],
504
+ optional,
501
505
  });
502
506
  }
@@ -295,9 +295,21 @@ async function listDrivePermissions(input, request) {
295
295
  perm_type: optionalString(input.permType),
296
296
  },
297
297
  });
298
- return {
299
- members: Array.isArray(data.members) ? data.members : [],
300
- };
298
+ // Feishu answers `GET /drive/v1/permissions/:token/members` with the
299
+ // collaborators under `data.items`, not `data.members`. Reading the output
300
+ // name as if it were the wire key missed every time, and the `: []` below
301
+ // turned the miss into a successful empty list — so every ACL read reported
302
+ // that nobody has access, with `success: true`.
303
+ //
304
+ // Every other list endpoint in this file already names the wire key
305
+ // explicitly (`normalizePage(data, "files")`, `normalizePage(data,
306
+ // "items")`); this one assumed it matched the field it returns, and was the
307
+ // only place that did.
308
+ //
309
+ // `members` is still read after `items`, so a wire that ever answers under
310
+ // that name keeps working rather than breaking in the other direction.
311
+ const rows = Array.isArray(data.items) ? data.items : Array.isArray(data.members) ? data.members : [];
312
+ return { members: rows };
301
313
  }
302
314
  async function addDrivePermission(input, request) {
303
315
  const memberType = requireString(input.memberType, "memberType");
@@ -101,8 +101,10 @@ export function createFeishuWikiActions(service) {
101
101
  name: "get_wiki_node",
102
102
  operationType: "read",
103
103
  description: "Resolve and get a Feishu Wiki node by token and object type.",
104
- requiredScopes: ["wiki:node:retrieve"],
105
- providerPermissions: ["wiki:node:retrieve"],
104
+ // Feishu refuses `wiki:node:retrieve` for this endpoint with 99991679
105
+ // and names `wiki:node:read` as the narrow accepted permission.
106
+ requiredScopes: ["wiki:node:read"],
107
+ providerPermissions: ["wiki:node:read"],
106
108
  inputSchema: s.object("Identify the Wiki node.", {
107
109
  token: s.string("A Wiki node_token or document obj_token.", { minLength: 1 }),
108
110
  objectType,
@@ -1,33 +1,12 @@
1
- import { compactObject } from "../../core/cast.js";
2
- import { runProviderRequest, ProviderRequestError, providerUserAgent } from "../provider-runtime.js";
1
+ import { compactObject, optionalInteger, optionalNumber, optionalString, optionalStringArray, } from "../../core/cast.js";
2
+ import { requiredInputNumber, requiredInputString, runProviderRequest, ProviderRequestError, providerUserAgent, } from "../provider-runtime.js";
3
3
  import { createMarketDumpHandlers } from "./market-dumps.js";
4
- function requiredString(value, fieldName) {
5
- if (typeof value !== "string" || !value.trim()) {
6
- throw hithinkError("invalid_input", `${fieldName} is required`, 400);
7
- }
8
- return value.trim();
9
- }
10
- function optionalString(value) {
11
- return typeof value === "string" && value.trim() ? value.trim() : undefined;
12
- }
13
- function optionalStringArray(value) {
14
- return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : undefined;
15
- }
16
4
  function requiredStringArray(value, fieldName) {
17
5
  const result = optionalStringArray(value);
18
6
  if (!result || result.length === 0) {
19
7
  throw hithinkError("invalid_input", `${fieldName} is required`, 400);
20
8
  }
21
- return result.map((item, index) => requiredString(item, `${fieldName}[${index}]`));
22
- }
23
- function optionalNumber(value) {
24
- return typeof value === "number" ? value : undefined;
25
- }
26
- function requiredNumber(value, fieldName) {
27
- if (typeof value !== "number" || !Number.isFinite(value)) {
28
- throw hithinkError("invalid_input", `${fieldName} is required`, 400);
29
- }
30
- return value;
9
+ return result.map((item, index) => requiredInputString(item, `${fieldName}[${index}]`));
31
10
  }
32
11
  export const hithinkFinanceProxyMaxResponseBytes = 4 * 1024 * 1024;
33
12
  const hithinkFinanceActionMaxResponseBytes = 16 * 1024 * 1024;
@@ -75,7 +54,7 @@ export async function fetchHithinkFinanceProxy(input) {
75
54
  }
76
55
  throw error;
77
56
  }
78
- const code = readInteger(payload.code);
57
+ const code = optionalInteger(payload.code);
79
58
  if (!response.ok || code !== 0) {
80
59
  throw buildProviderError(response.status, code, payload, "execute");
81
60
  }
@@ -130,7 +109,7 @@ async function readBoundedResponse(response, maximumBytes, tooLargeError, emptyM
130
109
  export const hithinkFinanceActionHandlers = {
131
110
  search_tickers(input, context) {
132
111
  return get("/api/meta/tickers/search", {
133
- q: requiredString(input.query, "query"),
112
+ q: requiredInputString(input.query, "query"),
134
113
  exchange: optionalString(input.exchange),
135
114
  asset_type: optionalStringArray(input.assetTypes)?.join(","),
136
115
  limit: optionalNumber(input.limit),
@@ -153,17 +132,17 @@ export const hithinkFinanceActionHandlers = {
153
132
  },
154
133
  get_stock_history(input, context) {
155
134
  return get("/api/a-share/prices/historical", {
156
- thscode: requiredString(input.thscode, "thscode"),
135
+ thscode: requiredInputString(input.thscode, "thscode"),
157
136
  interval: "1d",
158
- start: requiredNumber(input.startTimeMs, "startTimeMs"),
159
- end: requiredNumber(input.endTimeMs, "endTimeMs"),
137
+ start: requiredInputNumber(input.startTimeMs, "startTimeMs"),
138
+ end: requiredInputNumber(input.endTimeMs, "endTimeMs"),
160
139
  adjust: optionalString(input.adjust),
161
140
  offset: optionalNumber(input.offset),
162
141
  }, context);
163
142
  },
164
143
  get_adjustment_factors(input, context) {
165
144
  return get("/api/a-share/corporate-actions/adjustment-factors", {
166
- thscode: requiredString(input.thscode, "thscode"),
145
+ thscode: requiredInputString(input.thscode, "thscode"),
167
146
  from: optionalString(input.from),
168
147
  to: optionalString(input.to),
169
148
  }, context);
@@ -179,8 +158,8 @@ export const hithinkFinanceActionHandlers = {
179
158
  },
180
159
  get_financial_indicators(input, context) {
181
160
  return get("/api/a-share/financials/indicators", {
182
- thscode: requiredString(input.thscode, "thscode"),
183
- report: requiredString(input.report, "report"),
161
+ thscode: requiredInputString(input.thscode, "thscode"),
162
+ report: requiredInputString(input.report, "report"),
184
163
  }, context);
185
164
  },
186
165
  get_valuation_snapshot(input, context) {
@@ -193,17 +172,17 @@ export const hithinkFinanceActionHandlers = {
193
172
  return get("/api/a-share-index/catalog/ths-index-list", { tag: optionalString(input.tag) }, context);
194
173
  },
195
174
  get_index_constituents(input, context) {
196
- return get("/api/a-share-index/constituents/ths-stock-list", { thscode: requiredString(input.thscode, "thscode") }, context);
175
+ return get("/api/a-share-index/constituents/ths-stock-list", { thscode: requiredInputString(input.thscode, "thscode") }, context);
197
176
  },
198
177
  get_index_snapshot(input, context) {
199
178
  return get("/api/a-share-index/prices/snapshot", { thscodes: requiredStringArray(input.thscodes, "thscodes").join(",") }, context);
200
179
  },
201
180
  get_index_history(input, context) {
202
181
  return get("/api/a-share-index/prices/historical", {
203
- thscode: requiredString(input.thscode, "thscode"),
182
+ thscode: requiredInputString(input.thscode, "thscode"),
204
183
  interval: "1d",
205
- start: requiredNumber(input.startTimeMs, "startTimeMs"),
206
- end: requiredNumber(input.endTimeMs, "endTimeMs"),
184
+ start: requiredInputNumber(input.startTimeMs, "startTimeMs"),
185
+ end: requiredInputNumber(input.endTimeMs, "endTimeMs"),
207
186
  }, context);
208
187
  },
209
188
  get_auction_snapshot(input, context) {
@@ -242,26 +221,26 @@ function createFundHandlers(get) {
242
221
  });
243
222
  },
244
223
  get_fund_market_snapshot(input, context) {
245
- return get("/api/fund/market/snapshot", { thscode: requiredString(input.thscode, "thscode") }, context);
224
+ return get("/api/fund/market/snapshot", { thscode: requiredInputString(input.thscode, "thscode") }, context);
246
225
  },
247
226
  get_fund_market_history(input, context) {
248
227
  return get("/api/fund/market/historical", {
249
- thscode: requiredString(input.thscode, "thscode"),
228
+ thscode: requiredInputString(input.thscode, "thscode"),
250
229
  interval: "1d",
251
- start: requiredNumber(input.startTimeMs, "startTimeMs"),
252
- end: requiredNumber(input.endTimeMs, "endTimeMs"),
230
+ start: requiredInputNumber(input.startTimeMs, "startTimeMs"),
231
+ end: requiredInputNumber(input.endTimeMs, "endTimeMs"),
253
232
  }, context);
254
233
  },
255
234
  get_fund_company(input, context) {
256
- return get("/api/fund/companies/detail", { company_id: requiredString(input.companyId, "companyId") }, context);
235
+ return get("/api/fund/companies/detail", { company_id: requiredInputString(input.companyId, "companyId") }, context);
257
236
  },
258
237
  get_fund_industry_allocation(input, context) {
259
238
  return getFund(get, "/api/fund/portfolio/industry-allocation", input, context);
260
239
  },
261
240
  get_fund_performance_indicators(input, context) {
262
241
  return getFund(get, "/api/fund/performance/indicators-historical", input, context, {
263
- start: requiredNumber(input.startTimeMs, "startTimeMs"),
264
- end: requiredNumber(input.endTimeMs, "endTimeMs"),
242
+ start: requiredInputNumber(input.startTimeMs, "startTimeMs"),
243
+ end: requiredInputNumber(input.endTimeMs, "endTimeMs"),
265
244
  });
266
245
  },
267
246
  get_fund_drawdowns(input, context) {
@@ -292,7 +271,7 @@ function createFundHandlers(get) {
292
271
  },
293
272
  get_fund_manager_performance(input, context) {
294
273
  return getManager(get, "/api/fund/managers/performance", input, context, {
295
- range: requiredString(input.range, "range"),
274
+ range: requiredInputString(input.range, "range"),
296
275
  });
297
276
  },
298
277
  get_fund_manager_experience(input, context) {
@@ -308,7 +287,7 @@ function createFundHandlers(get) {
308
287
  });
309
288
  },
310
289
  list_fund_offerings(input, context) {
311
- return get("/api/fund/offerings/list", { subscribe: requiredString(input.subscriptionStatus, "subscriptionStatus") }, context);
290
+ return get("/api/fund/offerings/list", { subscribe: requiredInputString(input.subscriptionStatus, "subscriptionStatus") }, context);
312
291
  },
313
292
  get_fund_stock_holdings_history(input, context) {
314
293
  return getFundHoldingsHistory(get, "/api/fund/portfolio/stock-history", input, context);
@@ -329,8 +308,8 @@ function createFundHandlers(get) {
329
308
  }
330
309
  function getFund(get, path, input, context, extra = {}) {
331
310
  return get(path, {
332
- fund_type: requiredString(input.fundType, "fundType"),
333
- thscode: requiredString(input.thscode, "thscode"),
311
+ fund_type: requiredInputString(input.fundType, "fundType"),
312
+ thscode: requiredInputString(input.thscode, "thscode"),
334
313
  ...extra,
335
314
  }, context);
336
315
  }
@@ -348,12 +327,12 @@ function normalizeNavTypes(value) {
348
327
  return undefined;
349
328
  }
350
329
  function getManager(get, path, input, context, extra = {}) {
351
- return get(path, { manager_id: requiredString(input.managerId, "managerId"), ...extra }, context);
330
+ return get(path, { manager_id: requiredInputString(input.managerId, "managerId"), ...extra }, context);
352
331
  }
353
332
  function getFundHoldingsHistory(get, path, input, context) {
354
333
  return getFund(get, path, input, context, {
355
- report_type: requiredString(input.reportType, "reportType"),
356
- end_date: requiredString(input.endDate, "endDate"),
334
+ report_type: requiredInputString(input.reportType, "reportType"),
335
+ end_date: requiredInputString(input.endDate, "endDate"),
357
336
  });
358
337
  }
359
338
  function getFundReportDates(get, path, input, context) {
@@ -382,13 +361,13 @@ function createSpecialDataHandlers(get) {
382
361
  return get("/api/a-share/special-data/hot-stock-list", { period: optionalString(input.period) }, context);
383
362
  },
384
363
  get_hot_stock_history(input, context) {
385
- return get("/api/a-share/special-data/hot-stock-list-history", { date: requiredString(input.date, "date") }, context);
364
+ return get("/api/a-share/special-data/hot-stock-list-history", { date: requiredInputString(input.date, "date") }, context);
386
365
  },
387
366
  get_hot_stock_rank_trend(input, context) {
388
367
  return get("/api/a-share/special-data/hot-stock-rank-trend", {
389
- thscode: requiredString(input.thscode, "thscode"),
390
- start_date: requiredString(input.startDate, "startDate"),
391
- end_date: requiredString(input.endDate, "endDate"),
368
+ thscode: requiredInputString(input.thscode, "thscode"),
369
+ start_date: requiredInputString(input.startDate, "startDate"),
370
+ end_date: requiredInputString(input.endDate, "endDate"),
392
371
  }, context);
393
372
  },
394
373
  get_dragon_tiger_list(input, context) {
@@ -413,7 +392,7 @@ function getPool(get, path, input, context) {
413
392
  }
414
393
  function getFinancialStatements(path, input, context) {
415
394
  return get(path, {
416
- thscode: requiredString(input.thscode, "thscode"),
395
+ thscode: requiredInputString(input.thscode, "thscode"),
417
396
  period: optionalString(input.period),
418
397
  limit: optionalNumber(input.limit),
419
398
  start: optionalNumber(input.startTimeMs),
@@ -455,7 +434,7 @@ async function hithinkFinanceGet(path, query, apiKey, fetcher, phase, signal) {
455
434
  }
456
435
  throw error;
457
436
  }
458
- const code = readInteger(payload.code);
437
+ const code = optionalInteger(payload.code);
459
438
  if (!response.ok || code !== 0) {
460
439
  throw buildProviderError(response.status, code, payload, phase);
461
440
  }
@@ -500,7 +479,10 @@ function buildProviderError(status, code, payload, phase) {
500
479
  return hithinkError(phase === "validate" ? "invalid_input" : "scope_missing", detail, phase === "validate" ? 400 : 403);
501
480
  }
502
481
  if (code === 4001 || status === 429) {
503
- return hithinkError("rate_limited", `${detail}. Reduce request frequency and concurrency, then retry with exponential backoff (up to 3 attempts)`, 429);
482
+ const retryAdvice = message.startsWith("Global request rate limit exceeded")
483
+ ? "This upstream limit is shared across the account. Wait before retrying; reconnecting or sending concurrent retries will not reset it"
484
+ : "Reduce request frequency and concurrency, then retry with exponential backoff (up to 3 attempts)";
485
+ return hithinkError("rate_limited", `${detail}. ${retryAdvice}`, 429);
504
486
  }
505
487
  if (code === 4040) {
506
488
  return hithinkError("provider_error", requestId
@@ -522,9 +504,6 @@ function buildProviderError(status, code, payload, phase) {
522
504
  }
523
505
  return hithinkError("provider_error", detail, status >= 500 ? 502 : 503);
524
506
  }
525
- function readInteger(value) {
526
- return typeof value === "number" && Number.isInteger(value) ? value : undefined;
527
- }
528
507
  function hithinkError(code, message, status) {
529
508
  return new ProviderRequestError(status, message, { code });
530
509
  }