@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.
- package/README.md +27 -0
- package/assets/open-connector/catalog/apps/assured_platform.json +218 -0
- package/assets/open-connector/catalog/apps/baixiao.json +1080 -0
- package/assets/open-connector/catalog/apps/benchmarkone.json +956 -0
- package/assets/open-connector/catalog/apps/bing_webmaster.json +3097 -0
- package/assets/open-connector/catalog/apps/buzzsprout.json +1979 -0
- package/assets/open-connector/catalog/apps/feishu.json +13 -19
- package/assets/open-connector/catalog/apps/feishu_app_bot.json +12 -19
- package/assets/open-connector/catalog/apps/jin10.json +1 -1
- package/assets/open-connector/catalog/apps/linkfox.json +836 -0
- package/assets/open-connector/catalog/apps/metatraderapi.json +1077 -0
- package/assets/open-connector/catalog/apps/mktindex.json +2411 -0
- package/assets/open-connector/catalog/apps/mojo_helpdesk.json +660 -0
- package/assets/open-connector/catalog/apps/mumble.json +446 -0
- package/assets/open-connector/catalog/apps/notion.json +112 -3
- package/assets/open-connector/catalog/apps/omise.json +438 -0
- package/assets/open-connector/catalog/apps/panjiva.json +2858 -0
- package/assets/open-connector/catalog/apps/papertrail.json +449 -0
- package/assets/open-connector/catalog/apps/peec.json +747 -0
- package/assets/open-connector/catalog/apps/polygon_io.json +5156 -584
- package/assets/open-connector/catalog/apps/quant_data.json +1928 -0
- package/assets/open-connector/catalog/apps/riddle_quiz_maker.json +621 -0
- package/assets/open-connector/catalog/apps/slack.json +327 -4
- package/assets/open-connector/catalog/apps/slackbot.json +280 -3
- package/assets/open-connector/catalog/apps/templatedocs.json +438 -0
- package/assets/open-connector/catalog/apps/terminus_app.json +510 -0
- package/assets/open-connector/catalog/apps/ticket_tailor.json +584 -0
- package/assets/open-connector/catalog/apps/tongdaxin.json +2186 -82
- package/assets/open-connector/catalog/apps/veremark.json +514 -0
- package/assets/open-connector/catalog/apps/walletap.json +549 -0
- package/assets/open-connector/catalog/apps/xiaohongshu_store.json +955 -39
- package/assets/open-connector/catalog/apps-index.json +1 -1
- package/package.json +5 -1
- package/src/connection-service.d.ts +2 -0
- package/src/connection-service.js +8 -1
- package/src/core/provider-id.d.ts +5 -0
- package/src/core/provider-id.js +12 -0
- package/src/oauth/oauth-credential-refresh-service.js +2 -0
- package/src/oauth/oauth-flow-service.js +2 -0
- package/src/providers/action-contracts.generated.d.ts +27 -7
- package/src/providers/assured_platform/executors.d.ts +4 -0
- package/src/providers/assured_platform/executors.js +18 -0
- package/src/providers/assured_platform/runtime.d.ts +7 -0
- package/src/providers/assured_platform/runtime.js +108 -0
- package/src/providers/baixiao/actions.d.ts +2 -0
- package/src/providers/baixiao/actions.js +377 -0
- package/src/providers/baixiao/executors.d.ts +4 -0
- package/src/providers/baixiao/executors.js +106 -0
- package/src/providers/benchmarkone/executors.d.ts +4 -0
- package/src/providers/benchmarkone/executors.js +26 -0
- package/src/providers/benchmarkone/runtime.d.ts +12 -0
- package/src/providers/benchmarkone/runtime.js +113 -0
- package/src/providers/bing_webmaster/actions.d.ts +2 -0
- package/src/providers/bing_webmaster/actions.js +113 -0
- package/src/providers/bing_webmaster/executors.d.ts +4 -0
- package/src/providers/bing_webmaster/executors.js +104 -0
- package/src/providers/bing_webmaster/management-actions.d.ts +2 -0
- package/src/providers/bing_webmaster/management-actions.js +241 -0
- package/src/providers/bing_webmaster/reporting-actions.d.ts +2 -0
- package/src/providers/bing_webmaster/reporting-actions.js +300 -0
- package/src/providers/bing_webmaster/schemas.d.ts +33 -0
- package/src/providers/bing_webmaster/schemas.js +313 -0
- package/src/providers/buzzsprout/executors.d.ts +4 -0
- package/src/providers/buzzsprout/executors.js +21 -0
- package/src/providers/buzzsprout/runtime.d.ts +12 -0
- package/src/providers/buzzsprout/runtime.js +237 -0
- package/src/providers/dropbox/executors.js +21 -3
- package/src/providers/feishu/shared/drive-actions.d.ts +1 -0
- package/src/providers/feishu/shared/drive-actions.js +13 -9
- package/src/providers/feishu/shared/drive-runtime.js +15 -3
- package/src/providers/feishu/shared/wiki-actions.js +4 -2
- package/src/providers/feishu_app_bot/scopes.js +1 -1
- package/src/providers/hithink_finance/runtime.js +39 -60
- package/src/providers/linkfox/actions.js +2 -0
- package/src/providers/linkfox/runtime.js +70 -1
- package/src/providers/linkfox/sif-actions.d.ts +10 -0
- package/src/providers/linkfox/sif-actions.js +193 -0
- package/src/providers/mcp-client.d.ts +1 -0
- package/src/providers/mcp-client.js +41 -27
- package/src/providers/metatraderapi/executors.d.ts +4 -0
- package/src/providers/metatraderapi/executors.js +18 -0
- package/src/providers/metatraderapi/runtime.d.ts +7 -0
- package/src/providers/metatraderapi/runtime.js +216 -0
- package/src/providers/mktindex/actions-mmi.d.ts +2 -0
- package/src/providers/mktindex/actions-mmi.js +510 -0
- package/src/providers/mktindex/actions.d.ts +2 -0
- package/src/providers/mktindex/actions.js +2 -0
- package/src/providers/mktindex/executors.d.ts +4 -0
- package/src/providers/mktindex/executors.js +27 -0
- package/src/providers/mktindex/request.d.ts +17 -0
- package/src/providers/mktindex/request.js +48 -0
- package/src/providers/mktindex/runtime.d.ts +9 -0
- package/src/providers/mktindex/runtime.js +492 -0
- package/src/providers/mojo_helpdesk/executors.d.ts +4 -0
- package/src/providers/mojo_helpdesk/executors.js +18 -0
- package/src/providers/mojo_helpdesk/runtime.d.ts +7 -0
- package/src/providers/mojo_helpdesk/runtime.js +173 -0
- package/src/providers/mumble/executors.d.ts +4 -0
- package/src/providers/mumble/executors.js +99 -0
- package/src/providers/notion/executors.d.ts +9 -0
- package/src/providers/notion/executors.js +161 -6
- package/src/providers/omise/executors.d.ts +4 -0
- package/src/providers/omise/executors.js +120 -0
- package/src/providers/panjiva/actions.d.ts +14 -0
- package/src/providers/panjiva/actions.js +389 -0
- package/src/providers/panjiva/executors.d.ts +4 -0
- package/src/providers/panjiva/executors.js +136 -0
- package/src/providers/papertrail/executors.d.ts +4 -0
- package/src/providers/papertrail/executors.js +15 -0
- package/src/providers/papertrail/runtime.d.ts +7 -0
- package/src/providers/papertrail/runtime.js +167 -0
- package/src/providers/peec/executors.d.ts +4 -0
- package/src/providers/peec/executors.js +15 -0
- package/src/providers/peec/runtime.d.ts +7 -0
- package/src/providers/peec/runtime.js +128 -0
- package/src/providers/polygon_io/actions.d.ts +2 -0
- package/src/providers/polygon_io/actions.js +282 -0
- package/src/providers/polygon_io/expanded-actions.d.ts +2 -0
- package/src/providers/polygon_io/expanded-actions.js +375 -0
- package/src/providers/polygon_io/expanded-runtime.d.ts +16 -0
- package/src/providers/polygon_io/expanded-runtime.js +277 -0
- package/src/providers/polygon_io/runtime.js +21 -2
- package/src/providers/quant_data/executors.d.ts +4 -0
- package/src/providers/quant_data/executors.js +15 -0
- package/src/providers/quant_data/runtime.d.ts +7 -0
- package/src/providers/quant_data/runtime.js +118 -0
- package/src/providers/registry.generated.js +20 -0
- package/src/providers/riddle_quiz_maker/executors.d.ts +4 -0
- package/src/providers/riddle_quiz_maker/executors.js +15 -0
- package/src/providers/riddle_quiz_maker/runtime.d.ts +7 -0
- package/src/providers/riddle_quiz_maker/runtime.js +145 -0
- package/src/providers/sellersprite_mcp/executors.js +2 -1
- package/src/providers/slack/actions.js +106 -3
- package/src/providers/slack/runtime.js +262 -38
- package/src/providers/slackbot/actions.js +1 -1
- package/src/providers/templatedocs/executors.d.ts +4 -0
- package/src/providers/templatedocs/executors.js +18 -0
- package/src/providers/templatedocs/runtime.d.ts +13 -0
- package/src/providers/templatedocs/runtime.js +151 -0
- package/src/providers/terminus_app/executors.d.ts +4 -0
- package/src/providers/terminus_app/executors.js +15 -0
- package/src/providers/terminus_app/runtime.d.ts +7 -0
- package/src/providers/terminus_app/runtime.js +172 -0
- package/src/providers/ticket_tailor/executors.d.ts +4 -0
- package/src/providers/ticket_tailor/executors.js +15 -0
- package/src/providers/ticket_tailor/runtime.d.ts +7 -0
- package/src/providers/ticket_tailor/runtime.js +152 -0
- package/src/providers/tongdaxin/actions.d.ts +1 -0
- package/src/providers/tongdaxin/actions.js +6 -3
- package/src/providers/tongdaxin/executors.js +55 -13
- package/src/providers/tongdaxin/named-action-output.d.ts +1 -0
- package/src/providers/tongdaxin/named-action-output.js +143 -0
- package/src/providers/tongdaxin/named-action-routing.d.ts +9 -0
- package/src/providers/tongdaxin/named-action-routing.js +417 -0
- package/src/providers/tongdaxin/named-actions.d.ts +2 -0
- package/src/providers/tongdaxin/named-actions.js +373 -0
- package/src/providers/twenty_crm/runtime.js +4 -2
- package/src/providers/veremark/executors.d.ts +4 -0
- package/src/providers/veremark/executors.js +129 -0
- package/src/providers/walletap/executors.d.ts +4 -0
- package/src/providers/walletap/executors.js +15 -0
- package/src/providers/walletap/runtime.d.ts +7 -0
- package/src/providers/walletap/runtime.js +147 -0
- package/src/providers/weixin_official_account/access-token.js +31 -7
- package/src/providers/weixin_store/access-token.js +33 -7
- package/src/providers/xiaohongshu_store/executors.js +152 -3
- package/src/server/api/openapi.js +3 -0
- package/src/server/connector-assets.d.ts +4 -0
- package/src/server/connector-assets.js +12 -0
- package/src/server/connector-build.d.ts +33 -0
- package/src/server/connector-build.js +73 -0
- package/src/server/connector-runtime.d.ts +1 -2
- package/src/server/connector-runtime.js +2 -8
- package/src/server/server-assets.d.ts +0 -28
- 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":
|
|
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":
|
|
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":
|
|
512
|
+
"Dropbox-API-Arg": dropboxApiArg(arg),
|
|
495
513
|
},
|
|
496
514
|
signal: context.signal,
|
|
497
515
|
});
|
|
@@ -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:
|
|
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
|
-
|
|
158
|
-
|
|
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
|
-
|
|
299
|
-
|
|
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
|
-
|
|
105
|
-
|
|
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:
|
|
18
|
+
applicationRead: "application:application:self_manage",
|
|
19
19
|
applicationVersionRead: "application:application.app_version:readonly",
|
|
20
20
|
};
|