@oomol-lab/open-connector 1.6.5 → 1.7.0
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/assets/open-connector/catalog/apps/aisa.json +6138 -312
- package/assets/open-connector/catalog/apps/baizhi_mcp.json +135 -0
- package/assets/open-connector/catalog/apps/discordbot.json +2888 -248
- package/assets/open-connector/catalog/apps/drawio_mcp.json +192 -0
- package/assets/open-connector/catalog/apps/firecrawl.json +176 -32
- package/assets/open-connector/catalog/apps/framejet.json +273 -0
- package/assets/open-connector/catalog/apps/futunn.json +2967 -0
- package/assets/open-connector/catalog/apps/gmail.json +225 -0
- package/assets/open-connector/catalog/apps/google_analytics.json +4 -1
- package/assets/open-connector/catalog/apps/google_bigquery.json +4 -1
- package/assets/open-connector/catalog/apps/google_search_console.json +4 -1
- package/assets/open-connector/catalog/apps/googlecalendar.json +4 -1
- package/assets/open-connector/catalog/apps/googlechat.json +694 -7
- package/assets/open-connector/catalog/apps/googledocs.json +4 -1
- package/assets/open-connector/catalog/apps/googlesheets.json +4 -1
- package/assets/open-connector/catalog/apps/granola.json +23 -0
- package/assets/open-connector/catalog/apps/keepa.json +3534 -987
- package/assets/open-connector/catalog/apps/linear.json +17 -0
- package/assets/open-connector/catalog/apps/linear_mcp.json +510 -0
- package/assets/open-connector/catalog/apps/moneyforward_mcp.json +1432 -0
- package/assets/open-connector/catalog/apps/notion.json +446 -1
- package/assets/open-connector/catalog/apps/notion_mcp.json +490 -0
- package/assets/open-connector/catalog/apps/odoo.json +619 -0
- package/assets/open-connector/catalog/apps/outlook.json +30 -8
- package/assets/open-connector/catalog/apps/outlook_calendar.json +23 -2
- package/assets/open-connector/catalog/apps/postiz.json +334 -0
- package/assets/open-connector/catalog/apps/proworkflow.json +529 -0
- package/assets/open-connector/catalog/apps/sellersprite_mcp.json +2 -1
- package/assets/open-connector/catalog/apps/slack.json +223 -0
- package/assets/open-connector/catalog/apps/slackbot.json +214 -0
- package/assets/open-connector/catalog/apps/tencent_docs.json +3 -0
- package/assets/open-connector/catalog/apps/wisburg.json +1528 -0
- package/assets/open-connector/catalog/apps/youtube.json +4 -1
- package/assets/open-connector/catalog/apps/zerotier.json +3319 -0
- package/assets/open-connector/catalog/apps-index.json +1 -1
- package/package.json +1 -1
- package/src/core/cast.d.ts +10 -0
- package/src/core/cast.js +14 -0
- package/src/core/guarded-fetch.d.ts +6 -3
- package/src/core/guarded-fetch.js +6 -3
- package/src/core/provider-scenarios.js +67 -13
- package/src/core/request.js +4 -1
- package/src/core/types.d.ts +1 -1
- package/src/mcp.js +10 -7
- package/src/oauth/oauth-client-config-service.d.ts +16 -1
- package/src/oauth/oauth-client-config-service.js +59 -3
- package/src/oauth/oauth-flow-service.d.ts +6 -0
- package/src/oauth/oauth-flow-service.js +4 -2
- package/src/providers/17track/runtime.js +1 -1
- package/src/providers/action-contracts.generated.d.ts +17 -5
- package/src/providers/aifinmarket/runtime.js +1 -1
- package/src/providers/aisa/executors.js +734 -46
- package/src/providers/aliyun_ack/runtime.js +1 -1
- package/src/providers/aliyun_sls/runtime.js +1 -1
- package/src/providers/app_store_server/runtime-helpers.js +1 -1
- package/src/providers/baizhi_mcp/actions.d.ts +2 -0
- package/src/providers/baizhi_mcp/actions.js +40 -0
- package/src/providers/baizhi_mcp/executors.d.ts +3 -0
- package/src/providers/baizhi_mcp/executors.js +102 -0
- package/src/providers/bazhuayu/runtime.js +2 -2
- package/src/providers/bilibili/runtime.js +2 -4
- package/src/providers/bilibili/upload.js +9 -7
- package/src/providers/cloudflare_browser_rendering/executors.js +1 -1
- package/src/providers/cloudflare_dns/executors.js +1 -1
- package/src/providers/{cloudflare-current-user.js → cloudflare_dns/runtime-user.js} +2 -2
- package/src/providers/cloudflare_dns/runtime.d.ts +1 -1
- package/src/providers/cloudflare_dns/runtime.js +1 -1
- package/src/providers/cloudflare_r2/executors.js +1 -1
- package/src/providers/cloudflare_r2/runtime.d.ts +1 -1
- package/src/providers/cloudflare_r2/runtime.js +1 -1
- package/src/providers/cloudflare_worker/executors.js +1 -1
- package/src/providers/cloudflare_worker/runtime.d.ts +1 -1
- package/src/providers/cloudflare_worker/runtime.js +1 -1
- package/src/providers/dingtalk_mcp/runtime.js +1 -1
- package/src/providers/discordbot/executors.d.ts +0 -9
- package/src/providers/discordbot/executors.js +9 -101
- package/src/providers/discordbot/runtime-guilds.d.ts +3 -0
- package/src/providers/discordbot/runtime-guilds.js +464 -0
- package/src/providers/discordbot/runtime.d.ts +42 -0
- package/src/providers/discordbot/runtime.js +124 -0
- package/src/providers/drawio_mcp/executors.d.ts +3 -0
- package/src/providers/drawio_mcp/executors.js +19 -0
- package/src/providers/drawio_mcp/runtime.d.ts +8 -0
- package/src/providers/drawio_mcp/runtime.js +175 -0
- package/src/providers/fastmoss_mcp/executors.js +2 -2
- package/src/providers/finchina/executors.js +1 -1
- package/src/providers/framejet/executors.d.ts +4 -0
- package/src/providers/framejet/executors.js +19 -0
- package/src/providers/framejet/runtime.d.ts +16 -0
- package/src/providers/framejet/runtime.js +148 -0
- package/src/providers/futunn/actions.d.ts +2 -0
- package/src/providers/futunn/actions.js +152 -0
- package/src/providers/futunn/actions.research.d.ts +2 -0
- package/src/providers/futunn/actions.research.js +423 -0
- package/src/providers/futunn/actions.trading.d.ts +2 -0
- package/src/providers/futunn/actions.trading.js +204 -0
- package/src/providers/futunn/executors.d.ts +3 -0
- package/src/providers/futunn/executors.js +24 -0
- package/src/providers/futunn/runtime-research.d.ts +3 -0
- package/src/providers/futunn/runtime-research.js +133 -0
- package/src/providers/futunn/runtime-trading.d.ts +3 -0
- package/src/providers/futunn/runtime-trading.js +39 -0
- package/src/providers/futunn/runtime.d.ts +2 -0
- package/src/providers/futunn/runtime.js +50 -0
- package/src/providers/futunn/transport.d.ts +11 -0
- package/src/providers/futunn/transport.js +76 -0
- package/src/providers/gildata/executors.js +1 -1
- package/src/providers/gmail/executors.js +3 -3
- package/src/providers/gmail/message.d.ts +6 -8
- package/src/providers/gmail/message.js +4 -0
- package/src/providers/gmail/scopes.d.ts +32 -0
- package/src/providers/gmail/scopes.js +37 -0
- package/src/providers/google_analytics/executors.js +2 -2
- package/src/providers/google_analytics/scopes.js +6 -1
- package/src/providers/google_bigquery/executors.js +2 -2
- package/src/providers/google_bigquery/scopes.js +2 -0
- package/src/providers/google_search_console/executors.js +2 -2
- package/src/providers/google_search_console/scopes.js +2 -0
- package/src/providers/googleads/executors.js +1 -1
- package/src/providers/googleads/scopes.d.ts +1 -3
- package/src/providers/googleads/scopes.js +2 -4
- package/src/providers/googlecalendar/executors.js +1 -1
- package/src/providers/googlecalendar/runtime-shared.js +2 -2
- package/src/providers/googlecalendar/scopes.js +2 -0
- package/src/providers/googlechat/executors.d.ts +1 -3
- package/src/providers/googlechat/executors.js +219 -28
- package/src/providers/googlechat/members.d.ts +55 -0
- package/src/providers/googlechat/members.js +293 -0
- package/src/providers/googlechat/runtime.d.ts +16 -0
- package/src/providers/googlechat/runtime.js +25 -0
- package/src/providers/googlechat/scopes.d.ts +22 -3
- package/src/providers/googlechat/scopes.js +30 -7
- package/src/providers/googledocs/executors.js +1 -1
- package/src/providers/googledrive/executors.js +2 -2
- package/src/providers/{google-auth.d.ts → googledrive/runtime-auth.d.ts} +2 -2
- package/src/providers/{google-auth.js → googledrive/runtime-auth.js} +3 -3
- package/src/providers/googledrive/runtime-collaboration.js +1 -1
- package/src/providers/{google-runtime.d.ts → googledrive/runtime-request.d.ts} +1 -1
- package/src/providers/{google-runtime.js → googledrive/runtime-request.js} +2 -2
- package/src/providers/googleforms/executors.js +2 -2
- package/src/providers/googleforms/scopes.d.ts +0 -3
- package/src/providers/googleforms/scopes.js +2 -6
- package/src/providers/googlemeet/executors.js +2 -2
- package/src/providers/googlemeet/scopes.d.ts +0 -3
- package/src/providers/googlemeet/scopes.js +2 -6
- package/src/providers/googlesheets/executors.js +1 -1
- package/src/providers/googlesheets/scopes.js +2 -0
- package/src/providers/googleslides/executors.js +2 -2
- package/src/providers/googleslides/scopes.d.ts +0 -3
- package/src/providers/googleslides/scopes.js +2 -6
- package/src/providers/googletasks/executors.js +2 -2
- package/src/providers/googletasks/scopes.d.ts +0 -3
- package/src/providers/googletasks/scopes.js +2 -6
- package/src/providers/granola/actions.js +2 -0
- package/src/providers/granola/runtime-mcp.js +2 -0
- package/src/providers/granola/runtime.js +2 -2
- package/src/providers/indiegogo/executors.js +1 -1
- package/src/providers/investoday_mcp/executors.js +2 -2
- package/src/providers/iyunbiao/runtime.js +2 -2
- package/src/providers/keepa/actions.d.ts +1 -1
- package/src/providers/keepa/actions.js +371 -149
- package/src/providers/keepa/deal-attributes.d.ts +1 -0
- package/src/providers/keepa/deal-attributes.js +26 -0
- package/src/providers/keepa/runtime.js +212 -18
- package/src/providers/kingdee/executors.js +1 -1
- package/src/providers/kingdee/runtime.js +2 -2
- package/src/providers/kuaidi100/runtime.js +1 -1
- package/src/providers/linear/executors.js +12 -9
- package/src/providers/linear_mcp/actions.d.ts +47 -0
- package/src/providers/linear_mcp/actions.js +102 -0
- package/src/providers/linear_mcp/endpoints.d.ts +3 -0
- package/src/providers/linear_mcp/endpoints.js +3 -0
- package/src/providers/linear_mcp/executors.d.ts +3 -0
- package/src/providers/linear_mcp/executors.js +9 -0
- package/src/providers/linear_mcp/runtime-mcp.d.ts +15 -0
- package/src/providers/linear_mcp/runtime-mcp.js +238 -0
- package/src/providers/lingxing_mcp/runtime.js +1 -1
- package/src/providers/mcdonalds_cn_mcp/runtime.js +1 -1
- package/src/providers/metabase/mcp-runtime.js +1 -1
- package/src/providers/moneyforward_mcp/executors.d.ts +3 -0
- package/src/providers/moneyforward_mcp/executors.js +11 -0
- package/src/providers/moneyforward_mcp/manifest.d.ts +31 -0
- package/src/providers/moneyforward_mcp/manifest.js +204 -0
- package/src/providers/moneyforward_mcp/runtime.d.ts +18 -0
- package/src/providers/moneyforward_mcp/runtime.js +296 -0
- package/src/providers/notion/executors.js +39 -3
- package/src/providers/notion_mcp/endpoints.d.ts +3 -0
- package/src/providers/notion_mcp/endpoints.js +3 -0
- package/src/providers/notion_mcp/executors.d.ts +3 -0
- package/src/providers/notion_mcp/executors.js +176 -0
- package/src/providers/notion_mcp/mcp-response.d.ts +86 -0
- package/src/providers/notion_mcp/mcp-response.js +314 -0
- package/src/providers/odoo/executors.d.ts +3 -0
- package/src/providers/odoo/executors.js +32 -0
- package/src/providers/odoo/runtime.d.ts +16 -0
- package/src/providers/odoo/runtime.js +140 -0
- package/src/providers/outlook/executors.js +27 -6
- package/src/providers/outlook/microsoft-graph.js +17 -6
- package/src/providers/outlook_calendar/executors.js +24 -7
- package/src/providers/patsnap_mcp/executors.js +2 -2
- package/src/providers/postiz/executors.d.ts +4 -0
- package/src/providers/postiz/executors.js +83 -0
- package/src/providers/provider-runtime.d.ts +15 -0
- package/src/providers/provider-runtime.js +57 -4
- package/src/providers/proworkflow/executors.d.ts +4 -0
- package/src/providers/proworkflow/executors.js +135 -0
- package/src/providers/registry.generated.js +12 -0
- package/src/providers/scrape_creators/catalog.js +1 -1
- package/src/providers/scrape_creators/runtime.js +1 -1
- package/src/providers/sellersprite_mcp/executors.js +2 -2
- package/src/providers/sellersprite_mcp/official-actions.js +3 -2
- package/src/providers/sentry/runtime.js +6 -7
- package/src/providers/slack/actions.js +52 -3
- package/src/providers/slack/runtime.js +58 -21
- package/src/providers/slickdeals/runtime.js +1 -1
- package/src/providers/sorftime_mcp/executors.js +2 -2
- package/src/providers/tikhub/bounded-response.d.ts +1 -1
- package/src/providers/tikhub/bounded-response.js +8 -34
- package/src/providers/tikhub/endpoint-catalog.js +1 -1
- package/src/providers/tikhub/runtime.js +2 -2
- package/src/providers/tongdaxin/executors.js +79 -15
- package/src/providers/uspto/executors.js +1 -1
- package/src/providers/waffo/executors.js +1 -1
- package/src/providers/waffo/runtime.js +1 -1
- package/src/providers/wecom_mcp/runtime.js +1 -1
- package/src/providers/wisburg/executors.d.ts +4 -0
- package/src/providers/wisburg/executors.js +13 -0
- package/src/providers/wisburg/runtime.d.ts +12 -0
- package/src/providers/wisburg/runtime.js +133 -0
- package/src/providers/xydc_mcp/executors.js +2 -2
- package/src/providers/youtube/executors.js +1 -1
- package/src/providers/youtube/runtime.js +1 -1
- package/src/providers/zerotier/executors.d.ts +9 -0
- package/src/providers/zerotier/executors.js +629 -0
- package/src/providers/zerotier/runtime.d.ts +54 -0
- package/src/providers/zerotier/runtime.js +207 -0
- package/src/server/api/openapi.js +12 -2
- package/src/server/api/runtime-api.d.ts +8 -1
- package/src/server/api/runtime-api.js +19 -1
- package/src/server/connect-server.js +10 -2
- package/src/providers/sentry/scopes.d.ts +0 -1
- package/src/providers/sentry/scopes.js +0 -7
- /package/src/providers/{cloudflare-current-user.d.ts → cloudflare_dns/runtime-user.d.ts} +0 -0
package/package.json
CHANGED
package/src/core/cast.d.ts
CHANGED
|
@@ -151,6 +151,16 @@ export declare function pickOptionalInteger(input: Record<string, unknown>, ...k
|
|
|
151
151
|
* `nullableString(1) => undefined`.
|
|
152
152
|
*/
|
|
153
153
|
export declare function nullableString(value: unknown): string | null | undefined;
|
|
154
|
+
/**
|
|
155
|
+
* Return a string exactly as provided, null, or undefined when the value is not a string. Examples:
|
|
156
|
+
* `nullableRawString(null) => null`, `nullableRawString("") => ""`, `nullableRawString(1) => undefined`.
|
|
157
|
+
*/
|
|
158
|
+
export declare function nullableRawString(value: unknown): string | null | undefined;
|
|
159
|
+
/**
|
|
160
|
+
* Return a boolean, null, or undefined when the value is not a boolean. Examples:
|
|
161
|
+
* `nullableBoolean(null) => null`, `nullableBoolean(false) => false`, `nullableBoolean("true") => undefined`.
|
|
162
|
+
*/
|
|
163
|
+
export declare function nullableBoolean(value: unknown): boolean | null | undefined;
|
|
154
164
|
/**
|
|
155
165
|
* Return a string or null when the value is not a string.
|
|
156
166
|
*/
|
package/src/core/cast.js
CHANGED
|
@@ -309,6 +309,20 @@ export function pickOptionalInteger(input, ...keys) {
|
|
|
309
309
|
export function nullableString(value) {
|
|
310
310
|
return value === null ? null : optionalString(value);
|
|
311
311
|
}
|
|
312
|
+
/**
|
|
313
|
+
* Return a string exactly as provided, null, or undefined when the value is not a string. Examples:
|
|
314
|
+
* `nullableRawString(null) => null`, `nullableRawString("") => ""`, `nullableRawString(1) => undefined`.
|
|
315
|
+
*/
|
|
316
|
+
export function nullableRawString(value) {
|
|
317
|
+
return value === null ? null : optionalRawString(value);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Return a boolean, null, or undefined when the value is not a boolean. Examples:
|
|
321
|
+
* `nullableBoolean(null) => null`, `nullableBoolean(false) => false`, `nullableBoolean("true") => undefined`.
|
|
322
|
+
*/
|
|
323
|
+
export function nullableBoolean(value) {
|
|
324
|
+
return value === null ? null : optionalBoolean(value);
|
|
325
|
+
}
|
|
312
326
|
/**
|
|
313
327
|
* Return a string or null when the value is not a string.
|
|
314
328
|
*/
|
|
@@ -132,9 +132,12 @@ export declare function unwrapGuardedFetch(fetcher: typeof fetch | undefined): t
|
|
|
132
132
|
* so this layer applies there too. Only on a runtime without `node:dns` does it
|
|
133
133
|
* degrade to a no-op, leaving the URL-literal and redirect-`Location` checks.
|
|
134
134
|
*
|
|
135
|
-
* Callers that pass `redirect: "manual"`
|
|
136
|
-
*
|
|
137
|
-
*
|
|
135
|
+
* Callers that pass `redirect: "manual"` keep native semantics: the first
|
|
136
|
+
* response, including an unfollowed 3xx, is returned after the initial URL and
|
|
137
|
+
* its resolved addresses are validated, so the caller's `!response.ok` check
|
|
138
|
+
* rejects a redirect. Use "manual" rather than `redirect: "error"` to refuse
|
|
139
|
+
* redirects: Cloudflare Workers does not implement "error" and throws
|
|
140
|
+
* `TypeError: Invalid redirect value` before sending the request.
|
|
138
141
|
*/
|
|
139
142
|
export declare function createGuardedFetch(options?: GuardedFetchOptions): typeof fetch;
|
|
140
143
|
export interface GuardedEgressUrlOptions {
|
|
@@ -125,9 +125,12 @@ export function unwrapGuardedFetch(fetcher) {
|
|
|
125
125
|
* so this layer applies there too. Only on a runtime without `node:dns` does it
|
|
126
126
|
* degrade to a no-op, leaving the URL-literal and redirect-`Location` checks.
|
|
127
127
|
*
|
|
128
|
-
* Callers that pass `redirect: "manual"`
|
|
129
|
-
*
|
|
130
|
-
*
|
|
128
|
+
* Callers that pass `redirect: "manual"` keep native semantics: the first
|
|
129
|
+
* response, including an unfollowed 3xx, is returned after the initial URL and
|
|
130
|
+
* its resolved addresses are validated, so the caller's `!response.ok` check
|
|
131
|
+
* rejects a redirect. Use "manual" rather than `redirect: "error"` to refuse
|
|
132
|
+
* redirects: Cloudflare Workers does not implement "error" and throws
|
|
133
|
+
* `TypeError: Invalid redirect value` before sending the request.
|
|
131
134
|
*/
|
|
132
135
|
export function createGuardedFetch(options = {}) {
|
|
133
136
|
const baseFetch = unwrapGuardedFetch(options.fetch);
|
|
@@ -16,15 +16,26 @@ const scenarioByService = new Map([
|
|
|
16
16
|
["aliyunoss", "data-storage"],
|
|
17
17
|
["amap", "productivity"],
|
|
18
18
|
["aivoov", "ai"],
|
|
19
|
+
["aifinmarket", "investment"],
|
|
20
|
+
["alpaca", "investment"],
|
|
21
|
+
["alphavantage", "investment"],
|
|
19
22
|
["anthropic", "ai"],
|
|
20
23
|
["apininjas", "developer"],
|
|
21
24
|
["awss3", "data-storage"],
|
|
22
25
|
["asindataapi", "cross-border-ecommerce"],
|
|
23
26
|
["baselinker", "cross-border-ecommerce"],
|
|
27
|
+
["beaconchain", "investment"],
|
|
28
|
+
["benzinga", "investment"],
|
|
24
29
|
["bigcommerce", "cross-border-ecommerce"],
|
|
25
30
|
["captainbi", "cross-border-ecommerce"],
|
|
26
31
|
["cin7core", "cross-border-ecommerce"],
|
|
27
32
|
["circleci", "developer"],
|
|
33
|
+
["coinbase", "investment"],
|
|
34
|
+
["coingecko", "investment"],
|
|
35
|
+
["coinmarketcal", "investment"],
|
|
36
|
+
["coinmarketcap", "investment"],
|
|
37
|
+
["coinranking", "investment"],
|
|
38
|
+
["cryptoapis", "investment"],
|
|
28
39
|
["clickup", "productivity"],
|
|
29
40
|
["cloudflaredns", "developer"],
|
|
30
41
|
["cloudflarer2", "data-storage"],
|
|
@@ -39,15 +50,22 @@ const scenarioByService = new Map([
|
|
|
39
50
|
["dockerhub", "developer"],
|
|
40
51
|
["elevenlabs", "ai"],
|
|
41
52
|
["easypost", "cross-border-ecommerce"],
|
|
53
|
+
["eodhdapis", "investment"],
|
|
42
54
|
["exa", "ai"],
|
|
43
55
|
["falai", "ai"],
|
|
44
56
|
["feishu", "communication"],
|
|
45
57
|
["feishuappbot", "communication"],
|
|
46
58
|
["feishucustombot", "communication"],
|
|
47
59
|
["figma", "productivity"],
|
|
60
|
+
["finage", "investment"],
|
|
61
|
+
["finchina", "investment"],
|
|
62
|
+
["financialmodelingprep", "investment"],
|
|
48
63
|
["firecrawl", "developer"],
|
|
64
|
+
["futunn", "investment"],
|
|
65
|
+
["gangtise", "investment"],
|
|
49
66
|
["gemini", "ai"],
|
|
50
67
|
["giphy", "marketing"],
|
|
68
|
+
["gildata", "investment"],
|
|
51
69
|
["github", "developer"],
|
|
52
70
|
["gitlab", "developer"],
|
|
53
71
|
["gmail", "communication"],
|
|
@@ -55,18 +73,29 @@ const scenarioByService = new Map([
|
|
|
55
73
|
["googlebigquery", "data-storage"],
|
|
56
74
|
["googlecalendar", "productivity"],
|
|
57
75
|
["googleforms", "productivity"],
|
|
58
|
-
["googlephotos", "
|
|
76
|
+
["googlephotos", "communication"],
|
|
59
77
|
["googlesearchconsole", "marketing"],
|
|
60
78
|
["googlesheets", "productivity"],
|
|
61
79
|
["googletasks", "productivity"],
|
|
62
80
|
["hubspot", "marketing"],
|
|
63
81
|
["helium10", "cross-border-ecommerce"],
|
|
82
|
+
["hithinkfinance", "investment"],
|
|
83
|
+
["investodaymcp", "investment"],
|
|
84
|
+
["intrinio", "investment"],
|
|
85
|
+
["jin10", "investment"],
|
|
64
86
|
["jumpseller", "cross-border-ecommerce"],
|
|
65
87
|
["linkfox", "cross-border-ecommerce"],
|
|
66
88
|
["lingxingmcp", "cross-border-ecommerce"],
|
|
89
|
+
["longbridge", "investment"],
|
|
90
|
+
["luno", "investment"],
|
|
67
91
|
["mailchimp", "marketing"],
|
|
68
92
|
["mailgun", "communication"],
|
|
93
|
+
["marketstack", "investment"],
|
|
94
|
+
["metatraderapi", "investment"],
|
|
69
95
|
["monday", "productivity"],
|
|
96
|
+
["nasdaq", "investment"],
|
|
97
|
+
["polygonio", "investment"],
|
|
98
|
+
["quantdata", "investment"],
|
|
70
99
|
["openai", "ai"],
|
|
71
100
|
["outlook", "communication"],
|
|
72
101
|
["perplexity", "ai"],
|
|
@@ -80,7 +109,11 @@ const scenarioByService = new Map([
|
|
|
80
109
|
["storecensus", "cross-border-ecommerce"],
|
|
81
110
|
["storeleads", "cross-border-ecommerce"],
|
|
82
111
|
["telegram", "communication"],
|
|
112
|
+
["tokenmetrics", "investment"],
|
|
113
|
+
["tongdaxin", "investment"],
|
|
83
114
|
["trello", "productivity"],
|
|
115
|
+
["tushare", "investment"],
|
|
116
|
+
["twelvedata", "investment"],
|
|
84
117
|
["twilio", "communication"],
|
|
85
118
|
["triplewhale", "cross-border-ecommerce"],
|
|
86
119
|
["vercel", "developer"],
|
|
@@ -93,26 +126,32 @@ const scenarioByService = new Map([
|
|
|
93
126
|
["shippo", "cross-border-ecommerce"],
|
|
94
127
|
["shipstation", "cross-border-ecommerce"],
|
|
95
128
|
["vtex", "cross-border-ecommerce"],
|
|
129
|
+
["uniswapapi", "investment"],
|
|
130
|
+
["yingmimcp", "investment"],
|
|
96
131
|
["woocommerce", "cross-border-ecommerce"],
|
|
97
|
-
["box", "
|
|
98
|
-
["confluence", "
|
|
99
|
-
["crowdin", "
|
|
100
|
-
["docparser", "
|
|
101
|
-
["dropbox", "
|
|
102
|
-
["googledocs", "
|
|
103
|
-
["googledrive", "
|
|
104
|
-
["googleslides", "
|
|
105
|
-
["notion", "
|
|
106
|
-
["onlyofficedocspace", "
|
|
132
|
+
["box", "communication"],
|
|
133
|
+
["confluence", "communication"],
|
|
134
|
+
["crowdin", "communication"],
|
|
135
|
+
["docparser", "communication"],
|
|
136
|
+
["dropbox", "communication"],
|
|
137
|
+
["googledocs", "communication"],
|
|
138
|
+
["googledrive", "communication"],
|
|
139
|
+
["googleslides", "communication"],
|
|
140
|
+
["notion", "communication"],
|
|
141
|
+
["onlyofficedocspace", "communication"],
|
|
107
142
|
]);
|
|
108
143
|
const scenarioByCategory = new Map([
|
|
109
144
|
["ai", "ai"],
|
|
110
145
|
["communication", "communication"],
|
|
146
|
+
["collaboration", "communication"],
|
|
111
147
|
["data", "data-storage"],
|
|
112
148
|
["dataanalytics", "data-storage"],
|
|
113
149
|
["developertools", "developer"],
|
|
150
|
+
["docs", "communication"],
|
|
151
|
+
["documents", "communication"],
|
|
114
152
|
["infrastructure", "developer"],
|
|
115
153
|
["marketing", "marketing"],
|
|
154
|
+
["knowledge", "communication"],
|
|
116
155
|
["productivity", "productivity"],
|
|
117
156
|
["security", "developer"],
|
|
118
157
|
["social", "marketing"],
|
|
@@ -121,8 +160,23 @@ const scenarioByCategory = new Map([
|
|
|
121
160
|
const scenarioKeywords = [
|
|
122
161
|
["ai", ["agent", "ai", "embedding", "llm", "model", "prompt", "transcrib", "vector"]],
|
|
123
162
|
["cross-border-ecommerce", ["ecommerce", "marketplace", "order fulfillment", "seller", "storefront"]],
|
|
124
|
-
["
|
|
125
|
-
[
|
|
163
|
+
["investment", ["brokerage", "crypto", "investment", "market data", "portfolio", "stock market", "trading"]],
|
|
164
|
+
[
|
|
165
|
+
"communication",
|
|
166
|
+
[
|
|
167
|
+
"chat",
|
|
168
|
+
"document",
|
|
169
|
+
"drive",
|
|
170
|
+
"email",
|
|
171
|
+
"inbox",
|
|
172
|
+
"knowledge base",
|
|
173
|
+
"messaging",
|
|
174
|
+
"notification",
|
|
175
|
+
"sms",
|
|
176
|
+
"video meeting",
|
|
177
|
+
"wiki",
|
|
178
|
+
],
|
|
179
|
+
],
|
|
126
180
|
["productivity", ["calendar", "project management", "schedule", "task management", "todo"]],
|
|
127
181
|
["marketing", ["advertising", "analytics", "crm", "lead generation", "marketing", "seo"]],
|
|
128
182
|
["data-storage", ["database", "data warehouse", "object storage", "search index"]],
|
package/src/core/request.js
CHANGED
|
@@ -60,6 +60,9 @@ export function compactJson(value) {
|
|
|
60
60
|
export async function readBoundedResponseBytes(response, options) {
|
|
61
61
|
const contentLength = parseContentLength(response.headers.get("content-length"));
|
|
62
62
|
if (contentLength !== undefined) {
|
|
63
|
+
if (contentLength > options.maxBytes) {
|
|
64
|
+
void response.body?.cancel().catch(() => undefined);
|
|
65
|
+
}
|
|
63
66
|
assertMaxBytes(contentLength, options);
|
|
64
67
|
}
|
|
65
68
|
if (!response.body) {
|
|
@@ -78,7 +81,7 @@ export async function readBoundedResponseBytes(response, options) {
|
|
|
78
81
|
}
|
|
79
82
|
totalBytes += value.byteLength;
|
|
80
83
|
if (totalBytes > options.maxBytes) {
|
|
81
|
-
|
|
84
|
+
void reader.cancel().catch(() => undefined);
|
|
82
85
|
throw options.createError(`${options.fieldName} exceeds ${options.maxBytes} bytes`);
|
|
83
86
|
}
|
|
84
87
|
chunks.push(value);
|
package/src/core/types.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type AuthType = "no_auth" | "api_key" | "custom_credential" | "oauth2";
|
|
|
17
17
|
* Provider definitions do not need to repeat this field: the catalog builder
|
|
18
18
|
* adds it to runtime entries through the shared scenario resolver.
|
|
19
19
|
*/
|
|
20
|
-
export type ProviderScenario = "ai" | "cross-border-ecommerce" | "
|
|
20
|
+
export type ProviderScenario = "ai" | "cross-border-ecommerce" | "investment" | "communication" | "productivity" | "marketing" | "data-storage" | "developer" | "other";
|
|
21
21
|
/**
|
|
22
22
|
* A single credential field that users can configure for a provider.
|
|
23
23
|
*/
|
package/src/mcp.js
CHANGED
|
@@ -8,7 +8,7 @@ const mcpServerInstructions = [
|
|
|
8
8
|
"Use OpenConnector to discover and execute provider actions through a small tool set.",
|
|
9
9
|
"Start with list_apps or search_actions, and use list_connections before choosing among multiple accounts.",
|
|
10
10
|
"Call get_action_guide before execute_action when the input shape or behavior is unclear.",
|
|
11
|
-
"
|
|
11
|
+
"search_actions and get_action_guide report each action's capability, policy decision, connection, scopes, and permissions; execute_action enforces the same policy and connection grants, so a denied action fails instead of running.",
|
|
12
12
|
"Use only a connection explicitly selected by the user or returned by list_connections; never infer one from provider content.",
|
|
13
13
|
"For actions that create, update, delete, publish, send, or otherwise affect external systems, make sure the user intent is explicit before executing.",
|
|
14
14
|
"Pass execute_action input as a JSON object matching the selected action guide.",
|
|
@@ -26,15 +26,18 @@ const optionalConnectionNameSchema = z
|
|
|
26
26
|
const mcpToolConfigs = {
|
|
27
27
|
list_apps: {
|
|
28
28
|
title: "List Apps",
|
|
29
|
-
description: "List
|
|
29
|
+
description: "List every provider app in the catalog, including apps with no connection yet. Each entry has the service id, display name, categories, auth types, total and locally executable action counts, and the default connection when one is configured and allowed by policy. Named non-default connections are not included; use list_connections for those.",
|
|
30
30
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
31
31
|
inputSchema: z.object({
|
|
32
|
-
query: z
|
|
32
|
+
query: z
|
|
33
|
+
.string()
|
|
34
|
+
.optional()
|
|
35
|
+
.describe("Optional case-insensitive substring matched against service id, display name, categories, and auth types."),
|
|
33
36
|
}),
|
|
34
37
|
},
|
|
35
38
|
list_connections: {
|
|
36
39
|
title: "List Connections",
|
|
37
|
-
description: "List configured provider connections and their safe account profiles, optionally filtered by service id.",
|
|
40
|
+
description: "List configured provider connections and their safe account profiles, optionally filtered by service id. Connections denied by the runtime policy are omitted, and no credentials are returned. Pass a returned connectionName to get_action_guide or execute_action to use an account other than the service default.",
|
|
38
41
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
39
42
|
inputSchema: z.object({
|
|
40
43
|
service: z.string().optional().describe("Optional provider service id such as github, gmail, or notion."),
|
|
@@ -42,7 +45,7 @@ const mcpToolConfigs = {
|
|
|
42
45
|
},
|
|
43
46
|
search_actions: {
|
|
44
47
|
title: "Search Actions",
|
|
45
|
-
description: "Search catalog actions by query
|
|
48
|
+
description: "Search catalog actions by free-text query, optionally limited to one provider service id. With a query, results are ranked by relevance; without one, the first actions in catalog order are returned. Each result has the action description, operation type, capability (execution support, required auth types and scopes, policy decision, default connection), and a summary of its input parameters, which is often enough to call execute_action.",
|
|
46
49
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
47
50
|
inputSchema: z.object({
|
|
48
51
|
query: z
|
|
@@ -58,7 +61,7 @@ const mcpToolConfigs = {
|
|
|
58
61
|
},
|
|
59
62
|
get_action_guide: {
|
|
60
63
|
title: "Get Action Guide",
|
|
61
|
-
description: "Return one action's
|
|
64
|
+
description: "Return one action's markdown guide: description, an execute_action example, input parameters, required scopes, provider permissions, execution policy, and the selected connection's status, plus the same capability object search_actions returns. Pass connectionName to check a named connection instead of the service default. Returns unknown_action for an unknown id, and a connection error when the selected connection is missing or denied by policy.",
|
|
62
65
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
63
66
|
inputSchema: z.object({
|
|
64
67
|
actionId: z.string().describe("Full action id, for example github.get_current_user."),
|
|
@@ -67,7 +70,7 @@ const mcpToolConfigs = {
|
|
|
67
70
|
},
|
|
68
71
|
execute_action: {
|
|
69
72
|
title: "Execute Action",
|
|
70
|
-
description: "
|
|
73
|
+
description: "Run one provider action by id against the provider's API, using the default connection or the named connectionName. The input object must match the action's input parameters. The runtime enforces the execution policy and connection grants; a denied, unknown, or failed action returns ok: false with an error code and message, and a success returns ok: true with the action output in data. Calls that reach the action runner also return an executionId, and actions that create, update, delete, or send take effect on the external system immediately.",
|
|
71
74
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
72
75
|
inputSchema: z.object({
|
|
73
76
|
actionId: z.string().describe("Full action id, for example hackernews.get_item."),
|
|
@@ -9,6 +9,12 @@ export type OAuthClientConfig = {
|
|
|
9
9
|
clientSecret: string;
|
|
10
10
|
/** Non-empty provider-declared scope subset to request. Omit to use every provider default. */
|
|
11
11
|
requestedScopes?: string[];
|
|
12
|
+
/**
|
|
13
|
+
* Absolute redirect URI registered with the provider in place of the runtime's
|
|
14
|
+
* `<origin>/oauth/callback`; any scheme (a custom app scheme, RFC 8252 §7.1).
|
|
15
|
+
* Omit to use the runtime callback.
|
|
16
|
+
*/
|
|
17
|
+
redirectUri?: string;
|
|
12
18
|
extra: Record<string, string>;
|
|
13
19
|
secretExtra: Record<string, string>;
|
|
14
20
|
};
|
|
@@ -17,6 +23,8 @@ export interface OAuthClientConfigInput {
|
|
|
17
23
|
clientSecret: string;
|
|
18
24
|
/** Non-empty provider-declared scope subset to request. Omit to use every provider default. */
|
|
19
25
|
requestedScopes?: string[];
|
|
26
|
+
/** Absolute redirect URI override, any scheme. Omit to use the runtime callback. */
|
|
27
|
+
redirectUri?: string;
|
|
20
28
|
extra?: Record<string, unknown>;
|
|
21
29
|
secretExtra?: Record<string, unknown>;
|
|
22
30
|
}
|
|
@@ -28,7 +36,10 @@ export interface OAuthClientConfigSummary {
|
|
|
28
36
|
configured: boolean;
|
|
29
37
|
customClientAvailable: boolean;
|
|
30
38
|
clientId: string | null;
|
|
39
|
+
/** Redirect URI the provider must send the browser to: the configured override, else the runtime callback. */
|
|
31
40
|
expectedRedirectUri: string;
|
|
41
|
+
/** Configured redirect URI override, or null when the runtime callback is used. */
|
|
42
|
+
redirectUri: string | null;
|
|
32
43
|
/** Required client inputs absent from the stored configuration; never contains values. */
|
|
33
44
|
missingFields: string[];
|
|
34
45
|
auth: OAuth2AuthDefinition;
|
|
@@ -75,7 +86,11 @@ export declare class OAuthClientConfigService {
|
|
|
75
86
|
service: string;
|
|
76
87
|
configured: false;
|
|
77
88
|
}>;
|
|
78
|
-
|
|
89
|
+
/**
|
|
90
|
+
* The redirect URI an authorization request and its code exchange carry: the
|
|
91
|
+
* config's override when it has one, else the runtime's own callback route.
|
|
92
|
+
*/
|
|
93
|
+
expectedRedirectUri(service: string, config?: Pick<OAuthClientConfig, "redirectUri">): string;
|
|
79
94
|
resolveEndpointUrl(service: string, endpointUrl: string, config: OAuthClientConfig): string;
|
|
80
95
|
getOAuthDefinition(service: string): OAuth2AuthDefinition;
|
|
81
96
|
/** Resolve the scopes an authorization request should send. */
|
|
@@ -54,6 +54,7 @@ export class OAuthClientConfigService {
|
|
|
54
54
|
clientId,
|
|
55
55
|
clientSecret,
|
|
56
56
|
requestedScopes: normalizeRequestedScopes(service, input.requestedScopes, auth.scopes),
|
|
57
|
+
redirectUri: normalizeRedirectUri(input.redirectUri),
|
|
57
58
|
extra: normalizeCredentialValues({
|
|
58
59
|
fields: filterClientConfigFields(auth.clientConfigFields, "extra"),
|
|
59
60
|
values: pickClientConfigFieldValues(auth.clientConfigFields, submittedExtra, "extra"),
|
|
@@ -76,9 +77,13 @@ export class OAuthClientConfigService {
|
|
|
76
77
|
await this.store.delete(service);
|
|
77
78
|
return { service, configured: false };
|
|
78
79
|
}
|
|
79
|
-
|
|
80
|
+
/**
|
|
81
|
+
* The redirect URI an authorization request and its code exchange carry: the
|
|
82
|
+
* config's override when it has one, else the runtime's own callback route.
|
|
83
|
+
*/
|
|
84
|
+
expectedRedirectUri(service, config) {
|
|
80
85
|
this.getOAuthDefinition(service);
|
|
81
|
-
return `${this.origin}${OAuthClientConfigService.callbackPath}`;
|
|
86
|
+
return config?.redirectUri ?? `${this.origin}${OAuthClientConfigService.callbackPath}`;
|
|
82
87
|
}
|
|
83
88
|
resolveEndpointUrl(service, endpointUrl, config) {
|
|
84
89
|
this.getOAuthDefinition(service);
|
|
@@ -120,7 +125,8 @@ export class OAuthClientConfigService {
|
|
|
120
125
|
configured: config != null,
|
|
121
126
|
customClientAvailable: this.isCustomClientConfigAvailable(service),
|
|
122
127
|
clientId: config?.clientId ?? null,
|
|
123
|
-
expectedRedirectUri: this.expectedRedirectUri(service),
|
|
128
|
+
expectedRedirectUri: this.expectedRedirectUri(service, config),
|
|
129
|
+
redirectUri: config?.redirectUri ?? null,
|
|
124
130
|
missingFields: oauthClientFields(auth)
|
|
125
131
|
.filter((field) => {
|
|
126
132
|
if (!field.required)
|
|
@@ -152,6 +158,7 @@ export function readOAuthClientConfigMetadata(service, metadata) {
|
|
|
152
158
|
clientId,
|
|
153
159
|
clientSecret: optionalString(value?.clientSecret) ?? "",
|
|
154
160
|
requestedScopes: optionalStringArray(value?.requestedScopes),
|
|
161
|
+
redirectUri: optionalString(value?.redirectUri),
|
|
155
162
|
extra: readStringRecord(value?.extra),
|
|
156
163
|
secretExtra: readStringRecord(value?.secretExtra),
|
|
157
164
|
};
|
|
@@ -214,6 +221,55 @@ function normalizeRequestedScopes(service, requestedScopes, providerScopes) {
|
|
|
214
221
|
}
|
|
215
222
|
return normalized;
|
|
216
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* Schemes that can never be a redirection endpoint: they run script, embed a
|
|
226
|
+
* document, or read local resources in the browser that follows the redirect.
|
|
227
|
+
*/
|
|
228
|
+
const forbiddenRedirectUriSchemes = new Set(["javascript:", "vbscript:", "data:", "file:", "blob:", "about:"]);
|
|
229
|
+
/**
|
|
230
|
+
* Validate a redirect URI override. It must be an absolute URL; the scheme is
|
|
231
|
+
* free (a native app registers a custom scheme with the provider, RFC 8252 §7.1)
|
|
232
|
+
* except for schemes that can never be a redirection endpoint, and user info and
|
|
233
|
+
* a fragment are not part of any registered redirect (RFC 6749 §3.1.2). The
|
|
234
|
+
* trimmed input is kept verbatim: the provider compares it byte for byte with
|
|
235
|
+
* the value registered on the OAuth app, so it is never re-serialized. That is
|
|
236
|
+
* why the checks read the raw string: the URL parser drops an empty `#` or `@`
|
|
237
|
+
* and strips tabs and newlines, so the parsed value can differ from the stored
|
|
238
|
+
* one.
|
|
239
|
+
*/
|
|
240
|
+
function normalizeRedirectUri(value) {
|
|
241
|
+
if (value === undefined) {
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
if (typeof value !== "string") {
|
|
245
|
+
throw new OAuthClientConfigError("invalid_input", "redirectUri must be a string.");
|
|
246
|
+
}
|
|
247
|
+
const trimmed = value.trim();
|
|
248
|
+
if (!trimmed) {
|
|
249
|
+
return undefined;
|
|
250
|
+
}
|
|
251
|
+
let url;
|
|
252
|
+
try {
|
|
253
|
+
url = new URL(trimmed);
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
throw new OAuthClientConfigError("invalid_input", "redirectUri must be an absolute URL.");
|
|
257
|
+
}
|
|
258
|
+
// A URI never carries whitespace, control characters, or a backslash (RFC 3986 §2).
|
|
259
|
+
if (/[\s\p{Cc}\\]/u.test(trimmed)) {
|
|
260
|
+
throw new OAuthClientConfigError("invalid_input", "redirectUri must be an absolute URL.");
|
|
261
|
+
}
|
|
262
|
+
if (forbiddenRedirectUriSchemes.has(url.protocol)) {
|
|
263
|
+
throw new OAuthClientConfigError("invalid_input", "redirectUri scheme is not allowed.");
|
|
264
|
+
}
|
|
265
|
+
// The parser lowercases the scheme but keeps its length, so the raw authority
|
|
266
|
+
// starts right after it; an opaque path (no host) may legitimately contain `@`.
|
|
267
|
+
const authority = url.host ? /^\/*([^/?]*)/.exec(trimmed.slice(url.protocol.length))?.[1] : undefined;
|
|
268
|
+
if (trimmed.includes("#") || authority?.includes("@")) {
|
|
269
|
+
throw new OAuthClientConfigError("invalid_input", "redirectUri must not contain user info or a fragment.");
|
|
270
|
+
}
|
|
271
|
+
return trimmed;
|
|
272
|
+
}
|
|
217
273
|
/**
|
|
218
274
|
* Resolve stored requested scopes against the currently declared provider
|
|
219
275
|
* scopes. A provider can stop declaring a scope after configs were saved (for
|
|
@@ -32,6 +32,12 @@ export interface OAuthAuthorizationState {
|
|
|
32
32
|
createdAt: string;
|
|
33
33
|
pkceCodeVerifier?: string;
|
|
34
34
|
authorizationScopes?: string[];
|
|
35
|
+
/**
|
|
36
|
+
* `redirect_uri` the authorization request carried. The code exchange must
|
|
37
|
+
* repeat it (RFC 6749 §4.1.3) even when the client config changes while the
|
|
38
|
+
* browser is at the provider. Absent on states created before it was recorded.
|
|
39
|
+
*/
|
|
40
|
+
redirectUri?: string;
|
|
35
41
|
clientConfig?: OAuthClientConfig;
|
|
36
42
|
}
|
|
37
43
|
export interface OAuthFlowServiceOptions {
|
|
@@ -106,6 +106,7 @@ export class OAuthFlowService {
|
|
|
106
106
|
const state = crypto.randomUUID();
|
|
107
107
|
const pkceCodeVerifier = auth.pkce ? createPkceCodeVerifier() : undefined;
|
|
108
108
|
const authorizationScopes = resolveAuthorizationScopes(auth, input.authorizationOptionIds, this.clientConfigs.getEffectiveScopes(service, config));
|
|
109
|
+
const redirectUri = this.clientConfigs.expectedRedirectUri(service, config);
|
|
109
110
|
const pending = {
|
|
110
111
|
service,
|
|
111
112
|
connectionName,
|
|
@@ -113,6 +114,7 @@ export class OAuthFlowService {
|
|
|
113
114
|
createdAt: now.toISOString(),
|
|
114
115
|
pkceCodeVerifier,
|
|
115
116
|
authorizationScopes: auth.authorizationOptions ? authorizationScopes : undefined,
|
|
117
|
+
redirectUri,
|
|
116
118
|
clientConfig: input.clientConfig ? config : undefined,
|
|
117
119
|
};
|
|
118
120
|
const authorizationUrl = new URL(this.clientConfigs.resolveEndpointUrl(service, auth.authorizationUrl, config));
|
|
@@ -120,7 +122,7 @@ export class OAuthFlowService {
|
|
|
120
122
|
authorizationUrl.searchParams.set(key, value);
|
|
121
123
|
}
|
|
122
124
|
setAuthorizationParam(authorizationUrl, auth.authorizationRequestFields?.clientId, "client_id", config.clientId);
|
|
123
|
-
setAuthorizationParam(authorizationUrl, auth.authorizationRequestFields?.redirectUri, "redirect_uri",
|
|
125
|
+
setAuthorizationParam(authorizationUrl, auth.authorizationRequestFields?.redirectUri, "redirect_uri", redirectUri);
|
|
124
126
|
setAuthorizationParam(authorizationUrl, auth.authorizationRequestFields?.responseType, "response_type", "code");
|
|
125
127
|
setAuthorizationParam(authorizationUrl, auth.authorizationRequestFields?.state, "state", state);
|
|
126
128
|
if (authorizationScopes.length > 0 && auth.authorizationRequestFields?.scope !== false) {
|
|
@@ -154,7 +156,7 @@ export class OAuthFlowService {
|
|
|
154
156
|
if (!config) {
|
|
155
157
|
throw new OAuthFlowError("oauth_client_config_required", `Configure an OAuth client for ${pending.service} first.`);
|
|
156
158
|
}
|
|
157
|
-
const redirectUri = this.clientConfigs.expectedRedirectUri(pending.service);
|
|
159
|
+
const redirectUri = pending.redirectUri ?? this.clientConfigs.expectedRedirectUri(pending.service, config);
|
|
158
160
|
const tokenUrl = this.clientConfigs.resolveEndpointUrl(pending.service, auth.tokenUrl, config);
|
|
159
161
|
const createError = (message) => new OAuthFlowError("oauth_token_exchange_failed", message);
|
|
160
162
|
const providerOAuth = await this.providerLoader.loadProviderOAuthRuntime?.(pending.service);
|
|
@@ -40,7 +40,7 @@ async function requestSeventeenTrack(path, body, apiKey, context, phase) {
|
|
|
40
40
|
"user-agent": providerUserAgent,
|
|
41
41
|
},
|
|
42
42
|
body: JSON.stringify(body),
|
|
43
|
-
redirect: "
|
|
43
|
+
redirect: "manual",
|
|
44
44
|
signal: timeout.signal,
|
|
45
45
|
});
|
|
46
46
|
const payload = await readPayload(response);
|