@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
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { looseArray, optionalRecord, optionalString } from "../../core/cast.js";
|
|
2
|
+
import { encodePathSegment } from "../../core/request.js";
|
|
3
|
+
import { providerInputError, providerUserAgent, ProviderRequestError, readProviderJsonBody, runProviderRequest, } from "../provider-runtime.js";
|
|
4
|
+
/** Legacy Central base URL; requests authenticate with `Authorization: token <key>`. */
|
|
5
|
+
export const zerotierV1BaseUrl = "https://api.zerotier.com/api/v1";
|
|
6
|
+
/** New Central base URL; requests authenticate with `Authorization: Bearer <key>`. */
|
|
7
|
+
export const zerotierV2BaseUrl = "https://central.zerotier.com/api/v2";
|
|
8
|
+
const zerotierV2BetaBaseUrl = "https://central.zerotier.com/api/v2beta";
|
|
9
|
+
export function parseZerotierApiVersion(value) {
|
|
10
|
+
const version = optionalString(value)?.toLowerCase();
|
|
11
|
+
if (version === "v1" || version === "v2") {
|
|
12
|
+
return version;
|
|
13
|
+
}
|
|
14
|
+
throw providerInputError('zerotier apiVersion must be "v1" (Legacy Central) or "v2" (New Central).');
|
|
15
|
+
}
|
|
16
|
+
export function createZerotierContext(values, fetcher, signal) {
|
|
17
|
+
const apiVersion = parseZerotierApiVersion(values.apiVersion);
|
|
18
|
+
const apiKey = optionalString(values.apiKey);
|
|
19
|
+
if (!apiKey) {
|
|
20
|
+
throw providerInputError("zerotier apiKey is required.");
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
apiVersion,
|
|
24
|
+
apiKey,
|
|
25
|
+
orgId: optionalString(values.orgId),
|
|
26
|
+
fetcher,
|
|
27
|
+
signal,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Encode one ID as a single path segment. encodeURIComponent leaves "." and
|
|
32
|
+
* ".." intact and new URL() collapses them, so memberId ".." would turn
|
|
33
|
+
* delete_member into DELETE /network/{id}; reject dot segments and separators.
|
|
34
|
+
*/
|
|
35
|
+
export function zerotierPathSegment(value, field) {
|
|
36
|
+
if (value === "." || value === ".." || value.includes("/") || value.includes("\\")) {
|
|
37
|
+
throw providerInputError(`${field} must be a single path segment.`);
|
|
38
|
+
}
|
|
39
|
+
return encodePathSegment(value);
|
|
40
|
+
}
|
|
41
|
+
export function zerotierAuthorizationHeader(credential) {
|
|
42
|
+
return credential.apiVersion === "v2" ? `Bearer ${credential.apiKey}` : `token ${credential.apiKey}`;
|
|
43
|
+
}
|
|
44
|
+
function zerotierBaseUrl(context, beta) {
|
|
45
|
+
if (context.apiVersion === "v1") {
|
|
46
|
+
if (beta) {
|
|
47
|
+
throw providerInputError("ZeroTier beta endpoints are only available on the v2 (New Central) API.");
|
|
48
|
+
}
|
|
49
|
+
return zerotierV1BaseUrl;
|
|
50
|
+
}
|
|
51
|
+
return beta ? zerotierV2BetaBaseUrl : zerotierV2BaseUrl;
|
|
52
|
+
}
|
|
53
|
+
/** Run one ZeroTier Central API request and return the parsed JSON payload, or null for empty bodies. */
|
|
54
|
+
export async function zerotierRequest(context, options) {
|
|
55
|
+
return runProviderRequest({ signal: context.signal, label: "ZeroTier" }, async (signal) => {
|
|
56
|
+
const url = new URL(zerotierBaseUrl(context, options.beta) + options.path);
|
|
57
|
+
if (options.query) {
|
|
58
|
+
for (const [key, value] of Object.entries(options.query)) {
|
|
59
|
+
if (value === undefined)
|
|
60
|
+
continue;
|
|
61
|
+
if (Array.isArray(value)) {
|
|
62
|
+
for (const item of value) {
|
|
63
|
+
url.searchParams.append(key, item);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
url.searchParams.set(key, value);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const response = await context.fetcher(url, {
|
|
72
|
+
method: options.method ?? "GET",
|
|
73
|
+
headers: {
|
|
74
|
+
accept: "application/json",
|
|
75
|
+
authorization: zerotierAuthorizationHeader(context),
|
|
76
|
+
"content-type": "application/json",
|
|
77
|
+
"user-agent": providerUserAgent,
|
|
78
|
+
},
|
|
79
|
+
body: options.body === undefined ? undefined : JSON.stringify(options.body),
|
|
80
|
+
signal,
|
|
81
|
+
});
|
|
82
|
+
const payload = await readProviderJsonBody(response, {
|
|
83
|
+
emptyBody: null,
|
|
84
|
+
invalidJsonMessage: "ZeroTier returned invalid JSON",
|
|
85
|
+
// A non-JSON error body (such as a proxy's HTML page) keeps the upstream
|
|
86
|
+
// status instead of becoming a 502 parse failure; only its start is kept.
|
|
87
|
+
invalidJsonFallback: response.ok ? undefined : (text) => ({ message: text.trim().slice(0, 500) }),
|
|
88
|
+
});
|
|
89
|
+
if (!response.ok) {
|
|
90
|
+
const record = optionalRecord(payload);
|
|
91
|
+
const summary = optionalString(record?.message) ?? `ZeroTier request failed with status ${response.status}`;
|
|
92
|
+
// v2 validation failures list the offending fields under details[{field, message}].
|
|
93
|
+
const details = looseArray(record?.details).flatMap((item) => {
|
|
94
|
+
const detail = optionalRecord(item);
|
|
95
|
+
const field = optionalString(detail?.field);
|
|
96
|
+
const reason = optionalString(detail?.message);
|
|
97
|
+
return reason ? [field ? `${field}: ${reason}` : reason] : [];
|
|
98
|
+
});
|
|
99
|
+
const message = details.length > 0 ? `${summary}: ${details.join("; ")}` : summary;
|
|
100
|
+
throw new ProviderRequestError(response.status, message);
|
|
101
|
+
}
|
|
102
|
+
return payload;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/** Require a specific ZeroTier API version on the connection. */
|
|
106
|
+
export function requireZerotierApiVersion(context, version) {
|
|
107
|
+
if (context.apiVersion !== version) {
|
|
108
|
+
throw providerInputError(`This action requires a ${version} ZeroTier connection; the connection is configured for ${context.apiVersion}.`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Wrap a ZeroTier array response into the action list envelope. v2 list
|
|
113
|
+
* endpoints answer `{ items, stats? }`; the aggregate `stats` requested with
|
|
114
|
+
* `stats=true` is kept next to the items.
|
|
115
|
+
*/
|
|
116
|
+
export function zerotierList(payload) {
|
|
117
|
+
if (payload === null || payload === undefined) {
|
|
118
|
+
return { items: [] };
|
|
119
|
+
}
|
|
120
|
+
if (Array.isArray(payload)) {
|
|
121
|
+
return { items: payload };
|
|
122
|
+
}
|
|
123
|
+
const record = optionalRecord(payload);
|
|
124
|
+
if (record && Array.isArray(record.items)) {
|
|
125
|
+
return { items: record.items, stats: record.stats };
|
|
126
|
+
}
|
|
127
|
+
return { items: [payload] };
|
|
128
|
+
}
|
|
129
|
+
/** Wrap a ZeroTier object response into the action single-resource envelope. */
|
|
130
|
+
export function zerotierResult(payload) {
|
|
131
|
+
return { result: payload };
|
|
132
|
+
}
|
|
133
|
+
/** Wrap a mutating ZeroTier response that may have an empty body. */
|
|
134
|
+
export function zerotierStatus(payload) {
|
|
135
|
+
return { ok: true, result: payload };
|
|
136
|
+
}
|
|
137
|
+
/** Organization ID from the action input, falling back to the connection's orgId field. */
|
|
138
|
+
export function zerotierOrgId(context, input) {
|
|
139
|
+
return optionalString(input.orgId) ?? context.orgId;
|
|
140
|
+
}
|
|
141
|
+
const v1ValidationEndpoint = "/status";
|
|
142
|
+
const v2ValidationEndpoint = "/org";
|
|
143
|
+
/**
|
|
144
|
+
* Legacy Central reports the token's user under `user` on GET /status. The
|
|
145
|
+
* status document itself is not an authorization check, so a response without
|
|
146
|
+
* a user means the token did not authenticate.
|
|
147
|
+
*/
|
|
148
|
+
function readV1Account(payload) {
|
|
149
|
+
const user = optionalRecord(optionalRecord(payload)?.user);
|
|
150
|
+
const accountId = optionalString(user?.id);
|
|
151
|
+
if (!accountId) {
|
|
152
|
+
throw new ProviderRequestError(401, "ZeroTier did not return a user for this API token.");
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
accountId,
|
|
156
|
+
displayName: optionalString(user?.displayName) ?? optionalString(user?.email) ?? "ZeroTier Central v1",
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* New Central GET /org lists the organizations the service account can reach.
|
|
161
|
+
* A service account belongs to one organization, which identifies the account.
|
|
162
|
+
* A configured orgId must name an organization the key can reach, because the
|
|
163
|
+
* list actions and get_org keep using it; one missing from the list is looked
|
|
164
|
+
* up directly so the upstream 403/404 rejects a mistyped id. Without an orgId,
|
|
165
|
+
* a single listed organization identifies the account, otherwise the runtime
|
|
166
|
+
* default account id applies.
|
|
167
|
+
*/
|
|
168
|
+
async function readV2Account(context, payload) {
|
|
169
|
+
const orgs = looseArray(optionalRecord(payload)?.items).map((item) => optionalRecord(item));
|
|
170
|
+
const orgId = context.orgId;
|
|
171
|
+
if (!orgId) {
|
|
172
|
+
const onlyOrg = orgs.length === 1 ? orgs[0] : undefined;
|
|
173
|
+
return {
|
|
174
|
+
accountId: optionalString(onlyOrg?.id),
|
|
175
|
+
displayName: optionalString(onlyOrg?.name) ?? "ZeroTier New Central v2",
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const org = orgs.find((item) => optionalString(item?.id) === orgId) ??
|
|
179
|
+
optionalRecord(await zerotierRequest(context, { path: `/org/${zerotierPathSegment(orgId, "orgId")}` }));
|
|
180
|
+
return {
|
|
181
|
+
accountId: optionalString(org?.id) ?? orgId,
|
|
182
|
+
displayName: optionalString(org?.name) ?? "ZeroTier New Central v2",
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Verify a ZeroTier credential against the API generation it selects: v1 calls
|
|
187
|
+
* GET /status on Legacy Central, v2 calls GET /org on New Central (plus
|
|
188
|
+
* GET /org/{orgId} when the configured orgId is not in that list).
|
|
189
|
+
*/
|
|
190
|
+
export async function validateZerotierCredential(values, fetcher, signal) {
|
|
191
|
+
const context = createZerotierContext(values, fetcher, signal);
|
|
192
|
+
const path = context.apiVersion === "v1" ? v1ValidationEndpoint : v2ValidationEndpoint;
|
|
193
|
+
const payload = await zerotierRequest(context, { path });
|
|
194
|
+
const account = context.apiVersion === "v1" ? readV1Account(payload) : await readV2Account(context, payload);
|
|
195
|
+
return {
|
|
196
|
+
profile: {
|
|
197
|
+
accountId: account.accountId,
|
|
198
|
+
displayName: account.displayName,
|
|
199
|
+
},
|
|
200
|
+
grantedScopes: [],
|
|
201
|
+
metadata: {
|
|
202
|
+
apiVersion: context.apiVersion,
|
|
203
|
+
apiBaseUrl: context.apiVersion === "v1" ? zerotierV1BaseUrl : zerotierV2BaseUrl,
|
|
204
|
+
validationEndpoint: path,
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
}
|
|
@@ -42,6 +42,9 @@ const oauthClientConfigRequestSchema = jsonSchema.object({
|
|
|
42
42
|
minItems: 1,
|
|
43
43
|
description: "Non-empty provider-declared scope subset to request. Omit to use every provider default.",
|
|
44
44
|
}),
|
|
45
|
+
redirectUri: jsonSchema.string({
|
|
46
|
+
description: "Absolute redirect URI registered with the provider instead of the runtime callback, such as a native app's custom scheme. Must not carry user info or a fragment; javascript, vbscript, data, file, blob, and about schemes are rejected. Omit or send an empty string to use the runtime callback.",
|
|
47
|
+
}),
|
|
45
48
|
extra: {
|
|
46
49
|
type: "object",
|
|
47
50
|
additionalProperties: { type: "string" },
|
|
@@ -457,8 +460,11 @@ export function createOpenApiDocument(providers, options = {}) {
|
|
|
457
460
|
}),
|
|
458
461
|
clientId: jsonSchema.nullable(jsonSchema.string({ description: "Configured OAuth client id." })),
|
|
459
462
|
expectedRedirectUri: jsonSchema.string({
|
|
460
|
-
description: "Callback URL to configure in the provider OAuth app.",
|
|
463
|
+
description: "Callback URL to configure in the provider OAuth app: the configured override, else the runtime callback.",
|
|
461
464
|
}),
|
|
465
|
+
redirectUri: jsonSchema.nullable(jsonSchema.string({
|
|
466
|
+
description: "Configured redirect URI override, or null when the runtime callback is used.",
|
|
467
|
+
})),
|
|
462
468
|
auth: jsonSchema.unknownObject("Provider OAuth capability metadata."),
|
|
463
469
|
requestedScopes: jsonSchema.nullable(jsonSchema.array(jsonSchema.string(), {
|
|
464
470
|
description: "Configured scope subset, or null when provider defaults are used.",
|
|
@@ -473,6 +479,7 @@ export function createOpenApiDocument(providers, options = {}) {
|
|
|
473
479
|
"customClientAvailable",
|
|
474
480
|
"clientId",
|
|
475
481
|
"expectedRedirectUri",
|
|
482
|
+
"redirectUri",
|
|
476
483
|
"auth",
|
|
477
484
|
"requestedScopes",
|
|
478
485
|
"effectiveScopes",
|
|
@@ -1082,6 +1089,9 @@ function createOAuthAuthorizationPath() {
|
|
|
1082
1089
|
minItems: 1,
|
|
1083
1090
|
description: "Optional non-empty provider-declared scope subset to request.",
|
|
1084
1091
|
}),
|
|
1092
|
+
redirectUri: jsonSchema.string({
|
|
1093
|
+
description: "Optional redirect URI registered with the connection-scoped OAuth app instead of the runtime callback. Same rules as OAuthClientConfigRequest.redirectUri.",
|
|
1094
|
+
}),
|
|
1085
1095
|
authorizationOptionIds: jsonSchema.array(jsonSchema.string(), {
|
|
1086
1096
|
description: "Optional provider authorization option ids selected for this connection.",
|
|
1087
1097
|
}),
|
|
@@ -1342,7 +1352,7 @@ function connectionManagementPaths() {
|
|
|
1342
1352
|
oauthClient: jsonSchema.optional(jsonSchema.object("OAuth client configuration state; present when the provider supports OAuth.", {
|
|
1343
1353
|
configured: jsonSchema.boolean(),
|
|
1344
1354
|
customClientAvailable: jsonSchema.boolean("Whether connections may carry their own OAuth client."),
|
|
1345
|
-
expectedRedirectUri: jsonSchema.string("Callback URL to register with the provider."),
|
|
1355
|
+
expectedRedirectUri: jsonSchema.string("Callback URL to register with the provider: the configured override, else the runtime callback."),
|
|
1346
1356
|
missingFields: jsonSchema.stringArray("Required client inputs absent from the stored configuration."),
|
|
1347
1357
|
})),
|
|
1348
1358
|
});
|
|
@@ -100,7 +100,13 @@ export declare function serializeRuntimeFailure(input: RuntimeFailureInput): Run
|
|
|
100
100
|
export declare function serializeRuntimeActionResult(input: RuntimeActionResultInput): RuntimeActionHttpResult;
|
|
101
101
|
/** Validate an action response decoded from persistent storage. */
|
|
102
102
|
export declare function parseRuntimeActionHttpResult(value: unknown): RuntimeActionHttpResult;
|
|
103
|
-
/**
|
|
103
|
+
/**
|
|
104
|
+
* Write a newly serialized or replayed action response; runtime failures,
|
|
105
|
+
* including proxy failures, go through here as well. A 429 whose provider
|
|
106
|
+
* details carry `retryAfterSeconds` also answers with the `Retry-After`
|
|
107
|
+
* header, so HTTP callers pace on the provider's own hint; the body keeps it,
|
|
108
|
+
* which is what an idempotent replay re-emits the header from.
|
|
109
|
+
*/
|
|
104
110
|
export declare function writeRuntimeActionHttpResult(context: Context, result: RuntimeActionHttpResult): Response;
|
|
105
111
|
export declare function mapConnectionErrorStatus(error: ConnectionError): 400 | 404 | 409;
|
|
106
112
|
/**
|
|
@@ -129,6 +135,7 @@ export interface RuntimeProviderSetup {
|
|
|
129
135
|
interface RuntimeOAuthClientSetup {
|
|
130
136
|
configured: boolean;
|
|
131
137
|
customClientAvailable: boolean;
|
|
138
|
+
/** Redirect URI to register with the provider: the configured override, else the runtime callback. */
|
|
132
139
|
expectedRedirectUri: string;
|
|
133
140
|
missingFields: string[];
|
|
134
141
|
}
|
|
@@ -128,10 +128,28 @@ export function parseRuntimeActionHttpResult(value) {
|
|
|
128
128
|
}
|
|
129
129
|
throw invalid("status and body envelope do not match");
|
|
130
130
|
}
|
|
131
|
-
/**
|
|
131
|
+
/**
|
|
132
|
+
* Write a newly serialized or replayed action response; runtime failures,
|
|
133
|
+
* including proxy failures, go through here as well. A 429 whose provider
|
|
134
|
+
* details carry `retryAfterSeconds` also answers with the `Retry-After`
|
|
135
|
+
* header, so HTTP callers pace on the provider's own hint; the body keeps it,
|
|
136
|
+
* which is what an idempotent replay re-emits the header from.
|
|
137
|
+
*/
|
|
132
138
|
export function writeRuntimeActionHttpResult(context, result) {
|
|
139
|
+
const retryAfterSeconds = readRuntimeRetryAfterSeconds(result);
|
|
140
|
+
if (retryAfterSeconds !== undefined) {
|
|
141
|
+
context.header("Retry-After", String(retryAfterSeconds));
|
|
142
|
+
}
|
|
133
143
|
return context.json(result.body, result.status);
|
|
134
144
|
}
|
|
145
|
+
function readRuntimeRetryAfterSeconds(result) {
|
|
146
|
+
if (result.status !== 429) {
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
const seconds = optionalInteger(optionalRecord(optionalRecord(result.body.data)?.details)?.retryAfterSeconds);
|
|
150
|
+
// A safe integer is what keeps String() in plain delay-seconds digits: 1e21 would print as "1e+21".
|
|
151
|
+
return seconds !== undefined && Number.isSafeInteger(seconds) && seconds >= 0 ? seconds : undefined;
|
|
152
|
+
}
|
|
135
153
|
export function mapConnectionErrorStatus(error) {
|
|
136
154
|
if (error.code === "unknown_service" || error.code === "connection_not_found") {
|
|
137
155
|
return 404;
|
|
@@ -3,7 +3,7 @@ import { compress } from "hono/compress";
|
|
|
3
3
|
import { ConnectionError, defaultConnectionName } from "../connection-service.js";
|
|
4
4
|
import { ActionPolicyService, emptyPolicyRules } from "../core/action-policy.js";
|
|
5
5
|
import { DEFAULT_ACTION_SEARCH_LIMIT, createActionSearchIndexProvider, searchActions } from "../core/action-search.js";
|
|
6
|
-
import { optionalRecord, optionalString, requiredString, requiredStringArray } from "../core/cast.js";
|
|
6
|
+
import { optionalRecord, optionalString, requiredRawString, requiredString, requiredStringArray, } from "../core/cast.js";
|
|
7
7
|
import { PromiseCache } from "../core/promise-cache.js";
|
|
8
8
|
import { MarketplaceError } from "../marketplace/marketplace-service.js";
|
|
9
9
|
import { OAuthClientConfigError, OAuthClientConfigService } from "../oauth/oauth-client-config-service.js";
|
|
@@ -845,6 +845,7 @@ export class ConnectServer {
|
|
|
845
845
|
clientId: optionalString(body.clientId) ?? "",
|
|
846
846
|
clientSecret: optionalString(body.clientSecret) ?? "",
|
|
847
847
|
requestedScopes: readOptionalStringArray(body, "requestedScopes"),
|
|
848
|
+
redirectUri: readOptionalRawString(body, "redirectUri"),
|
|
848
849
|
extra: optionalRecord(body.extra),
|
|
849
850
|
secretExtra: optionalRecord(body.secretExtra),
|
|
850
851
|
}));
|
|
@@ -981,7 +982,7 @@ export class ConnectServer {
|
|
|
981
982
|
}
|
|
982
983
|
}
|
|
983
984
|
function readOAuthClientConfigInput(body) {
|
|
984
|
-
const keys = ["clientId", "clientSecret", "requestedScopes", "extra", "secretExtra"];
|
|
985
|
+
const keys = ["clientId", "clientSecret", "requestedScopes", "redirectUri", "extra", "secretExtra"];
|
|
985
986
|
if (!keys.some((key) => key in body)) {
|
|
986
987
|
return undefined;
|
|
987
988
|
}
|
|
@@ -989,10 +990,17 @@ function readOAuthClientConfigInput(body) {
|
|
|
989
990
|
clientId: optionalString(body.clientId) ?? "",
|
|
990
991
|
clientSecret: optionalString(body.clientSecret) ?? "",
|
|
991
992
|
requestedScopes: readOptionalStringArray(body, "requestedScopes"),
|
|
993
|
+
redirectUri: readOptionalRawString(body, "redirectUri"),
|
|
992
994
|
extra: optionalRecord(body.extra),
|
|
993
995
|
secretExtra: optionalRecord(body.secretExtra),
|
|
994
996
|
};
|
|
995
997
|
}
|
|
998
|
+
/** An absent field is undefined; a present one, null included, must be a string (blank is kept for the caller to read as unset). */
|
|
999
|
+
function readOptionalRawString(body, fieldName) {
|
|
1000
|
+
if (!(fieldName in body))
|
|
1001
|
+
return undefined;
|
|
1002
|
+
return requiredRawString(body[fieldName], fieldName, (message) => new HttpRequestError("invalid_input", `${message}.`));
|
|
1003
|
+
}
|
|
996
1004
|
function readOptionalStringArray(body, fieldName) {
|
|
997
1005
|
if (!(fieldName in body))
|
|
998
1006
|
return undefined;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const sentryProviderScopes: string[];
|
|
File without changes
|