@oomol-lab/open-connector 1.6.2 → 1.6.4

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 (175) 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 +12 -19
  9. package/assets/open-connector/catalog/apps/jin10.json +1 -1
  10. package/assets/open-connector/catalog/apps/linkfox.json +836 -0
  11. package/assets/open-connector/catalog/apps/metatraderapi.json +1077 -0
  12. package/assets/open-connector/catalog/apps/mktindex.json +2411 -0
  13. package/assets/open-connector/catalog/apps/mojo_helpdesk.json +660 -0
  14. package/assets/open-connector/catalog/apps/mumble.json +446 -0
  15. package/assets/open-connector/catalog/apps/notion.json +112 -3
  16. package/assets/open-connector/catalog/apps/omise.json +438 -0
  17. package/assets/open-connector/catalog/apps/panjiva.json +2858 -0
  18. package/assets/open-connector/catalog/apps/papertrail.json +449 -0
  19. package/assets/open-connector/catalog/apps/peec.json +747 -0
  20. package/assets/open-connector/catalog/apps/polygon_io.json +5156 -584
  21. package/assets/open-connector/catalog/apps/quant_data.json +1928 -0
  22. package/assets/open-connector/catalog/apps/riddle_quiz_maker.json +621 -0
  23. package/assets/open-connector/catalog/apps/slack.json +327 -4
  24. package/assets/open-connector/catalog/apps/slackbot.json +280 -3
  25. package/assets/open-connector/catalog/apps/templatedocs.json +438 -0
  26. package/assets/open-connector/catalog/apps/terminus_app.json +510 -0
  27. package/assets/open-connector/catalog/apps/ticket_tailor.json +584 -0
  28. package/assets/open-connector/catalog/apps/tongdaxin.json +2186 -82
  29. package/assets/open-connector/catalog/apps/veremark.json +514 -0
  30. package/assets/open-connector/catalog/apps/walletap.json +549 -0
  31. package/assets/open-connector/catalog/apps/xiaohongshu_store.json +955 -39
  32. package/assets/open-connector/catalog/apps-index.json +1 -1
  33. package/package.json +5 -1
  34. package/src/connection-service.d.ts +2 -0
  35. package/src/connection-service.js +8 -1
  36. package/src/core/provider-id.d.ts +5 -0
  37. package/src/core/provider-id.js +12 -0
  38. package/src/oauth/oauth-credential-refresh-service.js +2 -0
  39. package/src/oauth/oauth-flow-service.js +2 -0
  40. package/src/providers/action-contracts.generated.d.ts +27 -7
  41. package/src/providers/assured_platform/executors.d.ts +4 -0
  42. package/src/providers/assured_platform/executors.js +18 -0
  43. package/src/providers/assured_platform/runtime.d.ts +7 -0
  44. package/src/providers/assured_platform/runtime.js +108 -0
  45. package/src/providers/baixiao/actions.d.ts +2 -0
  46. package/src/providers/baixiao/actions.js +377 -0
  47. package/src/providers/baixiao/executors.d.ts +4 -0
  48. package/src/providers/baixiao/executors.js +106 -0
  49. package/src/providers/benchmarkone/executors.d.ts +4 -0
  50. package/src/providers/benchmarkone/executors.js +26 -0
  51. package/src/providers/benchmarkone/runtime.d.ts +12 -0
  52. package/src/providers/benchmarkone/runtime.js +113 -0
  53. package/src/providers/bing_webmaster/actions.d.ts +2 -0
  54. package/src/providers/bing_webmaster/actions.js +113 -0
  55. package/src/providers/bing_webmaster/executors.d.ts +4 -0
  56. package/src/providers/bing_webmaster/executors.js +104 -0
  57. package/src/providers/bing_webmaster/management-actions.d.ts +2 -0
  58. package/src/providers/bing_webmaster/management-actions.js +241 -0
  59. package/src/providers/bing_webmaster/reporting-actions.d.ts +2 -0
  60. package/src/providers/bing_webmaster/reporting-actions.js +300 -0
  61. package/src/providers/bing_webmaster/schemas.d.ts +33 -0
  62. package/src/providers/bing_webmaster/schemas.js +313 -0
  63. package/src/providers/buzzsprout/executors.d.ts +4 -0
  64. package/src/providers/buzzsprout/executors.js +21 -0
  65. package/src/providers/buzzsprout/runtime.d.ts +12 -0
  66. package/src/providers/buzzsprout/runtime.js +237 -0
  67. package/src/providers/dropbox/executors.js +21 -3
  68. package/src/providers/feishu/shared/drive-actions.d.ts +1 -0
  69. package/src/providers/feishu/shared/drive-actions.js +13 -9
  70. package/src/providers/feishu/shared/drive-runtime.js +15 -3
  71. package/src/providers/feishu/shared/wiki-actions.js +4 -2
  72. package/src/providers/feishu_app_bot/scopes.js +1 -1
  73. package/src/providers/hithink_finance/runtime.js +39 -60
  74. package/src/providers/linkfox/actions.js +2 -0
  75. package/src/providers/linkfox/runtime.js +70 -1
  76. package/src/providers/linkfox/sif-actions.d.ts +10 -0
  77. package/src/providers/linkfox/sif-actions.js +193 -0
  78. package/src/providers/mcp-client.d.ts +1 -0
  79. package/src/providers/mcp-client.js +41 -27
  80. package/src/providers/metatraderapi/executors.d.ts +4 -0
  81. package/src/providers/metatraderapi/executors.js +18 -0
  82. package/src/providers/metatraderapi/runtime.d.ts +7 -0
  83. package/src/providers/metatraderapi/runtime.js +216 -0
  84. package/src/providers/mktindex/actions-mmi.d.ts +2 -0
  85. package/src/providers/mktindex/actions-mmi.js +510 -0
  86. package/src/providers/mktindex/actions.d.ts +2 -0
  87. package/src/providers/mktindex/actions.js +2 -0
  88. package/src/providers/mktindex/executors.d.ts +4 -0
  89. package/src/providers/mktindex/executors.js +27 -0
  90. package/src/providers/mktindex/request.d.ts +17 -0
  91. package/src/providers/mktindex/request.js +48 -0
  92. package/src/providers/mktindex/runtime.d.ts +9 -0
  93. package/src/providers/mktindex/runtime.js +492 -0
  94. package/src/providers/mojo_helpdesk/executors.d.ts +4 -0
  95. package/src/providers/mojo_helpdesk/executors.js +18 -0
  96. package/src/providers/mojo_helpdesk/runtime.d.ts +7 -0
  97. package/src/providers/mojo_helpdesk/runtime.js +173 -0
  98. package/src/providers/mumble/executors.d.ts +4 -0
  99. package/src/providers/mumble/executors.js +99 -0
  100. package/src/providers/notion/executors.d.ts +9 -0
  101. package/src/providers/notion/executors.js +161 -6
  102. package/src/providers/omise/executors.d.ts +4 -0
  103. package/src/providers/omise/executors.js +120 -0
  104. package/src/providers/panjiva/actions.d.ts +14 -0
  105. package/src/providers/panjiva/actions.js +389 -0
  106. package/src/providers/panjiva/executors.d.ts +4 -0
  107. package/src/providers/panjiva/executors.js +136 -0
  108. package/src/providers/papertrail/executors.d.ts +4 -0
  109. package/src/providers/papertrail/executors.js +15 -0
  110. package/src/providers/papertrail/runtime.d.ts +7 -0
  111. package/src/providers/papertrail/runtime.js +167 -0
  112. package/src/providers/peec/executors.d.ts +4 -0
  113. package/src/providers/peec/executors.js +15 -0
  114. package/src/providers/peec/runtime.d.ts +7 -0
  115. package/src/providers/peec/runtime.js +128 -0
  116. package/src/providers/polygon_io/actions.d.ts +2 -0
  117. package/src/providers/polygon_io/actions.js +282 -0
  118. package/src/providers/polygon_io/expanded-actions.d.ts +2 -0
  119. package/src/providers/polygon_io/expanded-actions.js +375 -0
  120. package/src/providers/polygon_io/expanded-runtime.d.ts +16 -0
  121. package/src/providers/polygon_io/expanded-runtime.js +277 -0
  122. package/src/providers/polygon_io/runtime.js +21 -2
  123. package/src/providers/quant_data/executors.d.ts +4 -0
  124. package/src/providers/quant_data/executors.js +15 -0
  125. package/src/providers/quant_data/runtime.d.ts +7 -0
  126. package/src/providers/quant_data/runtime.js +118 -0
  127. package/src/providers/registry.generated.js +20 -0
  128. package/src/providers/riddle_quiz_maker/executors.d.ts +4 -0
  129. package/src/providers/riddle_quiz_maker/executors.js +15 -0
  130. package/src/providers/riddle_quiz_maker/runtime.d.ts +7 -0
  131. package/src/providers/riddle_quiz_maker/runtime.js +145 -0
  132. package/src/providers/sellersprite_mcp/executors.js +2 -1
  133. package/src/providers/slack/actions.js +106 -3
  134. package/src/providers/slack/runtime.js +262 -38
  135. package/src/providers/slackbot/actions.js +1 -1
  136. package/src/providers/templatedocs/executors.d.ts +4 -0
  137. package/src/providers/templatedocs/executors.js +18 -0
  138. package/src/providers/templatedocs/runtime.d.ts +13 -0
  139. package/src/providers/templatedocs/runtime.js +151 -0
  140. package/src/providers/terminus_app/executors.d.ts +4 -0
  141. package/src/providers/terminus_app/executors.js +15 -0
  142. package/src/providers/terminus_app/runtime.d.ts +7 -0
  143. package/src/providers/terminus_app/runtime.js +172 -0
  144. package/src/providers/ticket_tailor/executors.d.ts +4 -0
  145. package/src/providers/ticket_tailor/executors.js +15 -0
  146. package/src/providers/ticket_tailor/runtime.d.ts +7 -0
  147. package/src/providers/ticket_tailor/runtime.js +152 -0
  148. package/src/providers/tongdaxin/actions.d.ts +1 -0
  149. package/src/providers/tongdaxin/actions.js +6 -3
  150. package/src/providers/tongdaxin/executors.js +55 -13
  151. package/src/providers/tongdaxin/named-action-output.d.ts +1 -0
  152. package/src/providers/tongdaxin/named-action-output.js +143 -0
  153. package/src/providers/tongdaxin/named-action-routing.d.ts +9 -0
  154. package/src/providers/tongdaxin/named-action-routing.js +417 -0
  155. package/src/providers/tongdaxin/named-actions.d.ts +2 -0
  156. package/src/providers/tongdaxin/named-actions.js +373 -0
  157. package/src/providers/twenty_crm/runtime.js +4 -2
  158. package/src/providers/veremark/executors.d.ts +4 -0
  159. package/src/providers/veremark/executors.js +129 -0
  160. package/src/providers/walletap/executors.d.ts +4 -0
  161. package/src/providers/walletap/executors.js +15 -0
  162. package/src/providers/walletap/runtime.d.ts +7 -0
  163. package/src/providers/walletap/runtime.js +147 -0
  164. package/src/providers/weixin_official_account/access-token.js +31 -7
  165. package/src/providers/weixin_store/access-token.js +33 -7
  166. package/src/providers/xiaohongshu_store/executors.js +152 -3
  167. package/src/server/api/openapi.js +3 -0
  168. package/src/server/connector-assets.d.ts +4 -0
  169. package/src/server/connector-assets.js +12 -0
  170. package/src/server/connector-build.d.ts +33 -0
  171. package/src/server/connector-build.js +73 -0
  172. package/src/server/connector-runtime.d.ts +1 -2
  173. package/src/server/connector-runtime.js +2 -8
  174. package/src/server/server-assets.d.ts +0 -28
  175. 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
+ }
@@ -209,6 +209,24 @@ async function getMetadata(input, accessToken, fetcher) {
209
209
  metadata: mapDropboxMetadata(payload),
210
210
  };
211
211
  }
212
+ /**
213
+ * Serialize a `Dropbox-API-Arg` header value.
214
+ *
215
+ * Dropbox requires this header to be ASCII: its own docs say `0x7F` and every
216
+ * non-ASCII character must be escaped as `\uXXXX`, which is also the range the
217
+ * official JS SDK escapes. `JSON.stringify` does not escape, and Node's `Headers`
218
+ * refuses a value outside ByteString, so a path containing any non-ASCII
219
+ * character threw `TypeError: Cannot convert argument to a ByteString` inside
220
+ * the runtime BEFORE the request was made. `provider-runtime.ts`'s fallback
221
+ * then reported it as a bare `internal_error`, discarding the cause.
222
+ *
223
+ * Measured 2026-09-18 against a real account: `/kkndme 天涯.pdf` failed in 1ms
224
+ * with no network call, while ASCII-path siblings downloaded in ~400ms. Dropbox
225
+ * itself accepts either spelling on the wire; only the header build was broken.
226
+ */
227
+ function dropboxApiArg(arg) {
228
+ return JSON.stringify(arg).replace(/[\u007f-\uffff]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
229
+ }
212
230
  async function downloadFile(input, context) {
213
231
  const { accessToken, fetcher, transitFiles } = context;
214
232
  if (!transitFiles) {
@@ -218,7 +236,7 @@ async function downloadFile(input, context) {
218
236
  method: "POST",
219
237
  headers: {
220
238
  ...dropboxAuthHeaders(accessToken),
221
- "Dropbox-API-Arg": JSON.stringify({
239
+ "Dropbox-API-Arg": dropboxApiArg({
222
240
  path: requireString(input.path, "dropbox download path"),
223
241
  }),
224
242
  },
@@ -251,7 +269,7 @@ async function uploadFile(input, accessToken, fetcher) {
251
269
  headers: {
252
270
  ...dropboxAuthHeaders(accessToken),
253
271
  "Content-Type": source.mimeType || "application/octet-stream",
254
- "Dropbox-API-Arg": JSON.stringify(arg),
272
+ "Dropbox-API-Arg": dropboxApiArg(arg),
255
273
  },
256
274
  body: Buffer.from(source.bytes),
257
275
  });
@@ -491,7 +509,7 @@ async function getSharedLinkFile(input, context) {
491
509
  method: "POST",
492
510
  headers: {
493
511
  ...dropboxAuthHeaders(accessToken),
494
- "Dropbox-API-Arg": JSON.stringify(arg),
512
+ "Dropbox-API-Arg": dropboxApiArg(arg),
495
513
  },
496
514
  signal: context.signal,
497
515
  });
@@ -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,
@@ -15,6 +15,6 @@ export const feishuAppBotScopes = {
15
15
  groupAtReadonly: "im:message.group_at_msg:readonly",
16
16
  groupAtWithBotReadonly: "im:message.group_at_msg.include_bot:readonly",
17
17
  groupReadonly: "im:message.group_msg:readonly",
18
- applicationRead: "application:application:readonly",
18
+ applicationRead: "application:application:self_manage",
19
19
  applicationVersionRead: "application:application.app_version:readonly",
20
20
  };