@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.
- 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 +10 -17
- 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 +2612 -79
- 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/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/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/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/slack/actions.js +106 -3
- package/src/providers/slack/runtime.js +260 -36
- 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 +51 -10
- package/src/providers/tongdaxin/named-action-output.d.ts +1 -0
- package/src/providers/tongdaxin/named-action-output.js +149 -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 +378 -0
- 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
|
+
}
|
|
@@ -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,
|
|
@@ -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) =>
|
|
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 =
|
|
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:
|
|
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:
|
|
135
|
+
thscode: requiredInputString(input.thscode, "thscode"),
|
|
157
136
|
interval: "1d",
|
|
158
|
-
start:
|
|
159
|
-
end:
|
|
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:
|
|
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:
|
|
183
|
-
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:
|
|
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:
|
|
182
|
+
thscode: requiredInputString(input.thscode, "thscode"),
|
|
204
183
|
interval: "1d",
|
|
205
|
-
start:
|
|
206
|
-
end:
|
|
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:
|
|
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:
|
|
228
|
+
thscode: requiredInputString(input.thscode, "thscode"),
|
|
250
229
|
interval: "1d",
|
|
251
|
-
start:
|
|
252
|
-
end:
|
|
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:
|
|
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:
|
|
264
|
-
end:
|
|
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:
|
|
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:
|
|
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:
|
|
333
|
-
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:
|
|
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:
|
|
356
|
-
end_date:
|
|
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:
|
|
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:
|
|
390
|
-
start_date:
|
|
391
|
-
end_date:
|
|
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:
|
|
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 =
|
|
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
|
-
|
|
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
|
}
|