@zackbart/connecta 0.15.0 → 0.16.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/AGENTS.md +28 -15
- package/CHANGELOG.md +493 -0
- package/README.md +19 -4
- package/bin/connecta.mjs +44 -3
- package/dist/access-tokens.d.ts +0 -1
- package/dist/access-tokens.js +0 -1
- package/dist/activity.d.ts +50 -2
- package/dist/activity.js +34 -1
- package/dist/apps-shell.d.ts +0 -1
- package/dist/apps-shell.js +0 -1
- package/dist/auth/bearer.d.ts +0 -1
- package/dist/auth/bearer.js +0 -1
- package/dist/auth/clerk.d.ts +0 -1
- package/dist/auth/clerk.js +45 -9
- package/dist/auth/downstream-oauth.d.ts +0 -1
- package/dist/auth/downstream-oauth.js +0 -1
- package/dist/call-admission.d.ts +0 -1
- package/dist/call-admission.js +0 -1
- package/dist/catalog-drift.d.ts +99 -0
- package/dist/catalog-drift.js +251 -0
- package/dist/catalog-fingerprint.d.ts +0 -1
- package/dist/catalog-fingerprint.js +0 -1
- package/dist/catalog-limits.d.ts +0 -1
- package/dist/catalog-limits.js +0 -1
- package/dist/catalog-service.d.ts +0 -1
- package/dist/catalog-service.js +0 -1
- package/dist/catalog.d.ts +0 -1
- package/dist/catalog.js +0 -1
- package/dist/concurrency.d.ts +0 -1
- package/dist/concurrency.js +0 -1
- package/dist/connector-scope.d.ts +0 -1
- package/dist/connector-scope.js +0 -1
- package/dist/connectors/api.d.ts +29 -21
- package/dist/connectors/api.js +45 -19
- package/dist/connectors/guarded-fetch.d.ts +106 -0
- package/dist/connectors/guarded-fetch.js +289 -0
- package/dist/connectors/remote-mcp.d.ts +0 -1
- package/dist/connectors/remote-mcp.js +0 -1
- package/dist/credentials.d.ts +0 -1
- package/dist/credentials.js +0 -1
- package/dist/errors.d.ts +0 -1
- package/dist/errors.js +0 -1
- package/dist/execute.d.ts +0 -1
- package/dist/execute.js +0 -1
- package/dist/executor-admission.d.ts +11 -1
- package/dist/executor-admission.js +28 -1
- package/dist/executor-result.d.ts +0 -1
- package/dist/executor-result.js +0 -1
- package/dist/executors/quickjs-child.d.ts +0 -1
- package/dist/executors/quickjs-child.js +0 -1
- package/dist/executors/quickjs-protocol.d.ts +0 -1
- package/dist/executors/quickjs-protocol.js +0 -1
- package/dist/executors/quickjs-runtime.d.ts +0 -1
- package/dist/executors/quickjs-runtime.js +0 -1
- package/dist/executors/quickjs.d.ts +0 -1
- package/dist/executors/quickjs.js +2 -1
- package/dist/favicon.d.ts +0 -1
- package/dist/favicon.js +0 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.js +24 -7
- package/dist/invocation.d.ts +0 -1
- package/dist/invocation.js +0 -1
- package/dist/json-schema.d.ts +0 -1
- package/dist/json-schema.js +0 -1
- package/dist/mcp-result.d.ts +0 -1
- package/dist/mcp-result.js +0 -1
- package/dist/meta-tools.d.ts +0 -1
- package/dist/meta-tools.js +0 -1
- package/dist/node.d.ts +0 -1
- package/dist/node.js +0 -1
- package/dist/operator-ui/generated.d.ts +2 -3
- package/dist/operator-ui/generated.js +3 -4
- package/dist/operator-ui/model.d.ts +10 -1
- package/dist/operator-ui/model.js +0 -1
- package/dist/operator-ui/view.d.ts +167 -0
- package/dist/operator-ui/view.js +270 -0
- package/dist/providers/cloudflare.d.ts +5 -2
- package/dist/providers/cloudflare.js +435 -328
- package/dist/providers/linear.d.ts +26 -5
- package/dist/providers/linear.js +32 -60
- package/dist/providers/mixpanel.d.ts +35 -4
- package/dist/providers/mixpanel.js +53 -78
- package/dist/providers/notion.d.ts +0 -1
- package/dist/providers/notion.js +87 -42
- package/dist/providers/stripe.d.ts +13 -1
- package/dist/providers/stripe.js +31 -59
- package/dist/registry.d.ts +34 -2
- package/dist/registry.js +85 -5
- package/dist/retired-toolkits.d.ts +0 -1
- package/dist/retired-toolkits.js +0 -1
- package/dist/routes/access-tokens.d.ts +0 -1
- package/dist/routes/access-tokens.js +0 -1
- package/dist/routes/activity.d.ts +0 -1
- package/dist/routes/activity.js +0 -1
- package/dist/routes/credentials.d.ts +0 -1
- package/dist/routes/credentials.js +0 -1
- package/dist/routes/mcp.d.ts +0 -1
- package/dist/routes/mcp.js +0 -1
- package/dist/routes/oauth.d.ts +0 -1
- package/dist/routes/oauth.js +0 -1
- package/dist/routes/shared.d.ts +2 -1
- package/dist/routes/shared.js +0 -1
- package/dist/routes/ui.d.ts +0 -1
- package/dist/routes/ui.js +0 -1
- package/dist/server.d.ts +0 -1
- package/dist/server.js +11 -1
- package/dist/skills.d.ts +0 -1
- package/dist/skills.js +0 -1
- package/dist/storage/file.d.ts +0 -1
- package/dist/storage/file.js +0 -1
- package/dist/storage/memory.d.ts +0 -1
- package/dist/storage/memory.js +0 -1
- package/dist/timeout.d.ts +0 -1
- package/dist/timeout.js +0 -1
- package/dist/tool-safety.d.ts +0 -1
- package/dist/tool-safety.js +0 -1
- package/dist/types.d.ts +45 -1
- package/dist/types.js +0 -1
- package/dist/ui.d.ts +0 -1
- package/dist/ui.js +13 -137
- package/dist/validate.d.ts +13 -15
- package/dist/validate.js +24 -11
- package/dist/version.d.ts +1 -2
- package/dist/version.js +1 -2
- package/documentation/architecture.md +186 -5
- package/documentation/auth.md +11 -0
- package/documentation/call-admission.md +142 -6
- package/documentation/cloudflare.md +95 -27
- package/documentation/code-mode.md +12 -12
- package/documentation/connector-guides.md +172 -5
- package/documentation/connectors.md +98 -7
- package/documentation/linear.md +26 -9
- package/documentation/mcp-ui-design.md +2 -2
- package/documentation/mixpanel.md +48 -16
- package/documentation/notion.md +16 -0
- package/documentation/operations.md +283 -5
- package/documentation/operator-ui.md +120 -5
- package/documentation/provider-audit.md +160 -0
- package/documentation/provider-conventions.md +631 -0
- package/documentation/request-admission.md +167 -5
- package/documentation/rich-output-design.md +1 -2
- package/documentation/stripe.md +27 -12
- package/ethos.md +14 -9
- package/examples/worker/README.md +96 -12
- package/examples/worker/src/index.ts +46 -1
- package/examples/worker/wrangler.jsonc +14 -1
- package/package.json +4 -5
- package/templates/node/.dockerignore +10 -0
- package/templates/node/.env.example +40 -3
- package/templates/node/AGENTS.md +11 -1
- package/templates/node/Dockerfile +49 -0
- package/templates/node/README.md +117 -2
- package/templates/node/docker-compose.yml +39 -0
- package/templates/node/package.json +1 -1
- package/templates/node/src/file-activity.ts +182 -0
- package/templates/node/src/index.ts +80 -4
- package/assets/connecta-clay-hero.png +0 -0
- package/dist/access-tokens.d.ts.map +0 -1
- package/dist/access-tokens.js.map +0 -1
- package/dist/activity.d.ts.map +0 -1
- package/dist/activity.js.map +0 -1
- package/dist/apps-shell.d.ts.map +0 -1
- package/dist/apps-shell.js.map +0 -1
- package/dist/auth/bearer.d.ts.map +0 -1
- package/dist/auth/bearer.js.map +0 -1
- package/dist/auth/clerk.d.ts.map +0 -1
- package/dist/auth/clerk.js.map +0 -1
- package/dist/auth/downstream-oauth.d.ts.map +0 -1
- package/dist/auth/downstream-oauth.js.map +0 -1
- package/dist/call-admission.d.ts.map +0 -1
- package/dist/call-admission.js.map +0 -1
- package/dist/catalog-fingerprint.d.ts.map +0 -1
- package/dist/catalog-fingerprint.js.map +0 -1
- package/dist/catalog-limits.d.ts.map +0 -1
- package/dist/catalog-limits.js.map +0 -1
- package/dist/catalog-service.d.ts.map +0 -1
- package/dist/catalog-service.js.map +0 -1
- package/dist/catalog.d.ts.map +0 -1
- package/dist/catalog.js.map +0 -1
- package/dist/concurrency.d.ts.map +0 -1
- package/dist/concurrency.js.map +0 -1
- package/dist/connector-scope.d.ts.map +0 -1
- package/dist/connector-scope.js.map +0 -1
- package/dist/connectors/api.d.ts.map +0 -1
- package/dist/connectors/api.js.map +0 -1
- package/dist/connectors/remote-mcp.d.ts.map +0 -1
- package/dist/connectors/remote-mcp.js.map +0 -1
- package/dist/credentials.d.ts.map +0 -1
- package/dist/credentials.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/execute.d.ts.map +0 -1
- package/dist/execute.js.map +0 -1
- package/dist/executor-admission.d.ts.map +0 -1
- package/dist/executor-admission.js.map +0 -1
- package/dist/executor-result.d.ts.map +0 -1
- package/dist/executor-result.js.map +0 -1
- package/dist/executors/quickjs-child.d.ts.map +0 -1
- package/dist/executors/quickjs-child.js.map +0 -1
- package/dist/executors/quickjs-protocol.d.ts.map +0 -1
- package/dist/executors/quickjs-protocol.js.map +0 -1
- package/dist/executors/quickjs-runtime.d.ts.map +0 -1
- package/dist/executors/quickjs-runtime.js.map +0 -1
- package/dist/executors/quickjs.d.ts.map +0 -1
- package/dist/executors/quickjs.js.map +0 -1
- package/dist/favicon.d.ts.map +0 -1
- package/dist/favicon.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/invocation.d.ts.map +0 -1
- package/dist/invocation.js.map +0 -1
- package/dist/json-schema.d.ts.map +0 -1
- package/dist/json-schema.js.map +0 -1
- package/dist/mcp-result.d.ts.map +0 -1
- package/dist/mcp-result.js.map +0 -1
- package/dist/meta-tools.d.ts.map +0 -1
- package/dist/meta-tools.js.map +0 -1
- package/dist/node.d.ts.map +0 -1
- package/dist/node.js.map +0 -1
- package/dist/operator-ui/generated.d.ts.map +0 -1
- package/dist/operator-ui/generated.js.map +0 -1
- package/dist/operator-ui/model.d.ts.map +0 -1
- package/dist/operator-ui/model.js.map +0 -1
- package/dist/providers/cloudflare.d.ts.map +0 -1
- package/dist/providers/cloudflare.js.map +0 -1
- package/dist/providers/linear.d.ts.map +0 -1
- package/dist/providers/linear.js.map +0 -1
- package/dist/providers/mixpanel.d.ts.map +0 -1
- package/dist/providers/mixpanel.js.map +0 -1
- package/dist/providers/notion.d.ts.map +0 -1
- package/dist/providers/notion.js.map +0 -1
- package/dist/providers/stripe.d.ts.map +0 -1
- package/dist/providers/stripe.js.map +0 -1
- package/dist/registry.d.ts.map +0 -1
- package/dist/registry.js.map +0 -1
- package/dist/retired-toolkits.d.ts.map +0 -1
- package/dist/retired-toolkits.js.map +0 -1
- package/dist/routes/access-tokens.d.ts.map +0 -1
- package/dist/routes/access-tokens.js.map +0 -1
- package/dist/routes/activity.d.ts.map +0 -1
- package/dist/routes/activity.js.map +0 -1
- package/dist/routes/credentials.d.ts.map +0 -1
- package/dist/routes/credentials.js.map +0 -1
- package/dist/routes/mcp.d.ts.map +0 -1
- package/dist/routes/mcp.js.map +0 -1
- package/dist/routes/oauth.d.ts.map +0 -1
- package/dist/routes/oauth.js.map +0 -1
- package/dist/routes/shared.d.ts.map +0 -1
- package/dist/routes/shared.js.map +0 -1
- package/dist/routes/ui.d.ts.map +0 -1
- package/dist/routes/ui.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/skills.d.ts.map +0 -1
- package/dist/skills.js.map +0 -1
- package/dist/storage/file.d.ts.map +0 -1
- package/dist/storage/file.js.map +0 -1
- package/dist/storage/memory.d.ts.map +0 -1
- package/dist/storage/memory.js.map +0 -1
- package/dist/timeout.d.ts.map +0 -1
- package/dist/timeout.js.map +0 -1
- package/dist/tool-safety.d.ts.map +0 -1
- package/dist/tool-safety.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/ui.d.ts.map +0 -1
- package/dist/ui.js.map +0 -1
- package/dist/validate.d.ts.map +0 -1
- package/dist/validate.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
- package/examples/node/README.md +0 -52
- package/examples/node/src/index.ts +0 -71
- package/src/access-tokens.ts +0 -289
- package/src/activity.ts +0 -234
- package/src/apps-shell.ts +0 -370
- package/src/auth/bearer.ts +0 -61
- package/src/auth/clerk.ts +0 -553
- package/src/auth/downstream-oauth.ts +0 -575
- package/src/call-admission.ts +0 -521
- package/src/catalog-fingerprint.ts +0 -52
- package/src/catalog-limits.ts +0 -11
- package/src/catalog-service.ts +0 -1153
- package/src/catalog.ts +0 -790
- package/src/concurrency.ts +0 -39
- package/src/connector-scope.ts +0 -66
- package/src/connectors/api.ts +0 -159
- package/src/connectors/remote-mcp.ts +0 -1090
- package/src/credentials.ts +0 -428
- package/src/errors.ts +0 -364
- package/src/execute.ts +0 -1406
- package/src/executor-admission.ts +0 -328
- package/src/executor-result.ts +0 -107
- package/src/executors/quickjs-child.ts +0 -172
- package/src/executors/quickjs-protocol.ts +0 -99
- package/src/executors/quickjs-runtime.ts +0 -470
- package/src/executors/quickjs.ts +0 -776
- package/src/favicon.ts +0 -53
- package/src/index.ts +0 -666
- package/src/invocation.ts +0 -655
- package/src/json-schema.ts +0 -11
- package/src/mcp-result.ts +0 -43
- package/src/meta-tools.ts +0 -1607
- package/src/node.ts +0 -206
- package/src/operator-ui/browser.css +0 -423
- package/src/operator-ui/browser.ts +0 -1281
- package/src/operator-ui/generated.ts +0 -4
- package/src/operator-ui/model.ts +0 -111
- package/src/providers/cloudflare.ts +0 -3803
- package/src/providers/linear.ts +0 -301
- package/src/providers/mixpanel.ts +0 -228
- package/src/providers/notion.ts +0 -1879
- package/src/providers/stripe.ts +0 -306
- package/src/registry.ts +0 -1085
- package/src/retired-toolkits.ts +0 -14
- package/src/routes/access-tokens.ts +0 -115
- package/src/routes/activity.ts +0 -215
- package/src/routes/credentials.ts +0 -254
- package/src/routes/mcp.ts +0 -429
- package/src/routes/oauth.ts +0 -322
- package/src/routes/shared.ts +0 -247
- package/src/routes/ui.ts +0 -152
- package/src/server.ts +0 -195
- package/src/skills.ts +0 -271
- package/src/storage/file.ts +0 -123
- package/src/storage/memory.ts +0 -44
- package/src/timeout.ts +0 -84
- package/src/tool-safety.ts +0 -15
- package/src/types.ts +0 -523
- package/src/ui.ts +0 -744
- package/src/validate.ts +0 -387
- package/src/version.ts +0 -7
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* required-key list, and a declared output shape.
|
|
17
17
|
*/
|
|
18
18
|
import { api } from "../connectors/api.js";
|
|
19
|
+
import { guardedFetch, } from "../connectors/guarded-fetch.js";
|
|
19
20
|
import { ConnectorCallError } from "../errors.js";
|
|
20
21
|
/** Cloudflare's v4 REST base. Override only for a proxy or a test double. */
|
|
21
22
|
export const CLOUDFLARE_API_BASE = "https://api.cloudflare.com/client/v4";
|
|
@@ -91,11 +92,52 @@ function admissionPolicy(maxConcurrency) {
|
|
|
91
92
|
],
|
|
92
93
|
};
|
|
93
94
|
}
|
|
94
|
-
const
|
|
95
|
+
const API_TOKEN_CREDENTIAL = {
|
|
95
96
|
label: "Cloudflare API token",
|
|
96
97
|
description: "A scoped API token (My Profile → API Tokens → Create Token), not a Global API Key. Grant only the permissions the deployment needs: zone-scoped \"Zone Read\", \"Zone Settings Write\", \"DNS Write\", \"Cache Purge\", and the phase-specific Rules product Read permissions as needed; account-scoped \"Workers Scripts Read/Write\", \"Workers KV Storage Read/Write\", \"Workers R2 Storage Read/Write\", or \"Cloudflare Pages Read/Write\" for the platform tools.",
|
|
97
98
|
placeholder: "Paste API token",
|
|
98
99
|
};
|
|
100
|
+
const GLOBAL_API_KEY_CREDENTIAL = {
|
|
101
|
+
label: "Cloudflare Global API Key",
|
|
102
|
+
description: "Legacy user-scoped authentication. The key has the same access as its Cloudflare user across every account and zone that user can reach. Prefer a scoped API token when possible.",
|
|
103
|
+
fields: [
|
|
104
|
+
{
|
|
105
|
+
name: "email",
|
|
106
|
+
label: "Account email",
|
|
107
|
+
description: "The verified email address for the Cloudflare user that owns the Global API Key.",
|
|
108
|
+
placeholder: "you@example.com",
|
|
109
|
+
inputType: "email",
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: "apiKey",
|
|
113
|
+
label: "Global API Key",
|
|
114
|
+
description: "The legacy Global API Key from My Profile → API Tokens.",
|
|
115
|
+
placeholder: "Paste Global API Key",
|
|
116
|
+
inputType: "password",
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
};
|
|
120
|
+
function credentialConfig(authentication, override) {
|
|
121
|
+
if (authentication === "apiToken") {
|
|
122
|
+
const credential = override ?? API_TOKEN_CREDENTIAL;
|
|
123
|
+
if (credential.fields?.length) {
|
|
124
|
+
throw new Error("cloudflare() API token authentication requires a single-value credential.");
|
|
125
|
+
}
|
|
126
|
+
return credential;
|
|
127
|
+
}
|
|
128
|
+
const credential = override
|
|
129
|
+
? {
|
|
130
|
+
...GLOBAL_API_KEY_CREDENTIAL,
|
|
131
|
+
...override,
|
|
132
|
+
fields: override.fields ?? GLOBAL_API_KEY_CREDENTIAL.fields,
|
|
133
|
+
}
|
|
134
|
+
: GLOBAL_API_KEY_CREDENTIAL;
|
|
135
|
+
const fields = credential.fields?.map((field) => field.name).sort();
|
|
136
|
+
if (fields?.join(",") !== "apiKey,email") {
|
|
137
|
+
throw new Error('cloudflare() Global API Key authentication requires credential fields named "email" and "apiKey".');
|
|
138
|
+
}
|
|
139
|
+
return credential;
|
|
140
|
+
}
|
|
99
141
|
function asRecord(value) {
|
|
100
142
|
return value && typeof value === "object" && !Array.isArray(value)
|
|
101
143
|
? value
|
|
@@ -188,7 +230,7 @@ function failureFor(status, headers, errors) {
|
|
|
188
230
|
}
|
|
189
231
|
const authCoded = [...codes].some((code) => AUTH_ERROR_CODES.has(code));
|
|
190
232
|
if (status === 401 || status === 403 || authCoded) {
|
|
191
|
-
return new ConnectorCallError("auth_required", `Cloudflare rejected the
|
|
233
|
+
return new ConnectorCallError("auth_required", `Cloudflare rejected the configured credential (HTTP ${status}). ${detail} Check that it is valid and has permission to access this resource.`);
|
|
192
234
|
}
|
|
193
235
|
if (status === 400 || status === 409 || status === 422) {
|
|
194
236
|
return new ConnectorCallError("invalid_args", `Cloudflare rejected the request (HTTP ${status}). ${detail}`);
|
|
@@ -201,72 +243,93 @@ function failureFor(status, headers, errors) {
|
|
|
201
243
|
}
|
|
202
244
|
return new ConnectorCallError("connector_call_failed", `Cloudflare request failed (HTTP ${status}). ${detail}`);
|
|
203
245
|
}
|
|
204
|
-
|
|
246
|
+
// --- The request path --------------------------------------------------------
|
|
247
|
+
/**
|
|
248
|
+
* The largest response this connection will read.
|
|
249
|
+
*
|
|
250
|
+
* Generous rather than tight, because `cloudflare_api_get` legitimately
|
|
251
|
+
* downloads Worker scripts and R2 objects. It is a ceiling on absurdity, not a
|
|
252
|
+
* quota: anything approaching it is already far past whatever `maxResultBytes`
|
|
253
|
+
* the deployment set, so the caller was never going to see it whole.
|
|
254
|
+
*/
|
|
255
|
+
const CLOUDFLARE_MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
|
|
256
|
+
const AUTHENTICATION_CONTEXT = Symbol("cloudflareAuthentication");
|
|
257
|
+
function withAuthentication(ctx, authentication) {
|
|
258
|
+
return { ...ctx, [AUTHENTICATION_CONTEXT]: authentication };
|
|
259
|
+
}
|
|
260
|
+
async function readAuthenticationHeaders(ctx) {
|
|
261
|
+
if (ctx[AUTHENTICATION_CONTEXT] === "globalApiKey") {
|
|
262
|
+
const values = await ctx.credential?.getAll();
|
|
263
|
+
const email = values?.["email"];
|
|
264
|
+
const apiKey = values?.["apiKey"];
|
|
265
|
+
if (!email || !apiKey) {
|
|
266
|
+
throw new ConnectorCallError("auth_required", "No Cloudflare Global API Key and account email are configured for this connector. An operator must add both before any call can run.");
|
|
267
|
+
}
|
|
268
|
+
return { "X-Auth-Email": email, "X-Auth-Key": apiKey };
|
|
269
|
+
}
|
|
205
270
|
const token = await ctx.credential?.get();
|
|
206
271
|
if (!token) {
|
|
207
272
|
throw new ConnectorCallError("auth_required", "No Cloudflare API token is configured for this connector. An operator must add one before any call can run.");
|
|
208
273
|
}
|
|
209
|
-
return token;
|
|
274
|
+
return { Authorization: `Bearer ${token}` };
|
|
210
275
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
response = await fetch(buildUrl(base, spec), {
|
|
228
|
-
method: spec.method,
|
|
229
|
-
headers: {
|
|
230
|
-
Authorization: `Bearer ${token}`,
|
|
231
|
-
Accept: "application/json",
|
|
232
|
-
...(spec.body !== undefined
|
|
233
|
-
? { "Content-Type": "application/json" }
|
|
234
|
-
: {}),
|
|
235
|
-
...Object.fromEntries(Object.entries(spec.headers ?? {}).filter((entry) => entry[1] !== undefined)),
|
|
236
|
-
},
|
|
237
|
-
...(spec.body !== undefined
|
|
238
|
-
? { body: JSON.stringify(spec.body) }
|
|
239
|
-
: spec.rawBody !== undefined
|
|
240
|
-
? { body: spec.rawBody }
|
|
241
|
-
: {}),
|
|
242
|
-
...(ctx.signal ? { signal: ctx.signal } : {}),
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
catch (cause) {
|
|
246
|
-
throw new ConnectorCallError("unavailable", `Could not reach the Cloudflare API: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
|
|
247
|
-
}
|
|
248
|
-
return response;
|
|
276
|
+
/**
|
|
277
|
+
* The one transport every Cloudflare tool goes through.
|
|
278
|
+
*
|
|
279
|
+
* URL confinement, `ctx.signal`, redirect refusal, bounded reads, and the
|
|
280
|
+
* "could not reach the provider" normalization all live in the shared helper.
|
|
281
|
+
* What stays here is what only Cloudflare knows: which headers prove identity,
|
|
282
|
+
* and what a status code means once it arrives.
|
|
283
|
+
*/
|
|
284
|
+
function cloudflareTransport(baseUrl) {
|
|
285
|
+
return guardedFetch({
|
|
286
|
+
provider: "Cloudflare",
|
|
287
|
+
baseUrl,
|
|
288
|
+
headers: { Accept: "application/json" },
|
|
289
|
+
maxResponseBytes: CLOUDFLARE_MAX_RESPONSE_BYTES,
|
|
290
|
+
authenticate: (ctx) => readAuthenticationHeaders(ctx),
|
|
291
|
+
});
|
|
249
292
|
}
|
|
250
|
-
async function callCloudflare(
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
293
|
+
async function callCloudflare(send, spec, ctx) {
|
|
294
|
+
return await send(spec, ctx, async (response) => {
|
|
295
|
+
let envelope;
|
|
296
|
+
let parseFailure;
|
|
297
|
+
try {
|
|
298
|
+
envelope = (await response.json());
|
|
299
|
+
}
|
|
300
|
+
catch (cause) {
|
|
301
|
+
// A transport failure is not a parse failure. The connector's byte
|
|
302
|
+
// ceiling fires from inside this read and is deliberately non-retryable;
|
|
303
|
+
// routing it through the branch below would relabel it as a retryable
|
|
304
|
+
// `unavailable` and tell an agent to retry a response that will exceed
|
|
305
|
+
// the ceiling every time.
|
|
306
|
+
if (cause instanceof ConnectorCallError)
|
|
307
|
+
throw cause;
|
|
308
|
+
parseFailure = cause;
|
|
309
|
+
}
|
|
310
|
+
if (envelope === undefined) {
|
|
311
|
+
// A gateway error page or an empty body, not an envelope: the status is
|
|
312
|
+
// the only real signal left.
|
|
313
|
+
throw response.ok
|
|
314
|
+
? new ConnectorCallError("unavailable", "Cloudflare returned a non-JSON body for a successful status.", parseFailure !== undefined ? { cause: parseFailure } : {})
|
|
315
|
+
: failureFor(response.status, response.headers, []);
|
|
316
|
+
}
|
|
317
|
+
const errors = Array.isArray(envelope.errors) ? envelope.errors : [];
|
|
318
|
+
if (!response.ok || envelope.success === false) {
|
|
319
|
+
throw failureFor(response.status, response.headers, errors);
|
|
320
|
+
}
|
|
321
|
+
const isV4Envelope = "success" in envelope ||
|
|
322
|
+
"result" in envelope ||
|
|
323
|
+
"result_info" in envelope ||
|
|
324
|
+
"messages" in envelope;
|
|
325
|
+
return {
|
|
326
|
+
// `/graphql` and a small number of product APIs return ordinary JSON
|
|
327
|
+
// instead of the standard v4 envelope. Preserve that document whole so
|
|
328
|
+
// the raw tools cover them too.
|
|
329
|
+
result: isV4Envelope ? envelope.result : envelope,
|
|
330
|
+
resultInfo: isV4Envelope ? envelope.result_info : undefined,
|
|
331
|
+
};
|
|
332
|
+
});
|
|
270
333
|
}
|
|
271
334
|
function base64FromBytes(bytes) {
|
|
272
335
|
let binary = "";
|
|
@@ -275,33 +338,34 @@ function base64FromBytes(bytes) {
|
|
|
275
338
|
}
|
|
276
339
|
return btoa(binary);
|
|
277
340
|
}
|
|
278
|
-
async function callCloudflareContent(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
341
|
+
async function callCloudflareContent(send, spec, ctx, responseType) {
|
|
342
|
+
return await send(spec, ctx, async (response) => {
|
|
343
|
+
if (!response.ok) {
|
|
344
|
+
let errors = [];
|
|
345
|
+
try {
|
|
346
|
+
const envelope = (await response.json());
|
|
347
|
+
if (envelope && Array.isArray(envelope.errors))
|
|
348
|
+
errors = envelope.errors;
|
|
349
|
+
}
|
|
350
|
+
catch {
|
|
351
|
+
// A raw or gateway error body has no structured detail to preserve,
|
|
352
|
+
// and one past the byte ceiling has none worth reporting over the
|
|
353
|
+
// status that already failed this call. Either way the throw below
|
|
354
|
+
// classifies the status, so nothing is swallowed into a success.
|
|
355
|
+
}
|
|
356
|
+
throw failureFor(response.status, response.headers, errors);
|
|
286
357
|
}
|
|
287
|
-
|
|
288
|
-
|
|
358
|
+
const common = {
|
|
359
|
+
contentType: response.headers.get("content-type") ?? "application/octet-stream",
|
|
360
|
+
...(response.headers.get("etag")
|
|
361
|
+
? { etag: response.headers.get("etag") }
|
|
362
|
+
: {}),
|
|
363
|
+
};
|
|
364
|
+
if (responseType === "text") {
|
|
365
|
+
return { ...common, text: await response.text() };
|
|
289
366
|
}
|
|
290
|
-
|
|
291
|
-
}
|
|
292
|
-
const common = {
|
|
293
|
-
contentType: response.headers.get("content-type") ?? "application/octet-stream",
|
|
294
|
-
...(response.headers.get("etag")
|
|
295
|
-
? { etag: response.headers.get("etag") }
|
|
296
|
-
: {}),
|
|
297
|
-
};
|
|
298
|
-
if (responseType === "text") {
|
|
299
|
-
return { ...common, text: await response.text() };
|
|
300
|
-
}
|
|
301
|
-
return {
|
|
302
|
-
...common,
|
|
303
|
-
base64: base64FromBytes(new Uint8Array(await response.arrayBuffer())),
|
|
304
|
-
};
|
|
367
|
+
return { ...common, base64: base64FromBytes(await response.bytes()) };
|
|
368
|
+
});
|
|
305
369
|
}
|
|
306
370
|
/**
|
|
307
371
|
* Cloudflare's `result_info` reshaped into the one question an agent actually
|
|
@@ -570,8 +634,8 @@ const RAW_INPUT_PROPERTY = {
|
|
|
570
634
|
* an out-of-range value with a 400, so each caller passes its own. Encoding
|
|
571
635
|
* them in the schema turns a wasted round trip into a local repair — but only
|
|
572
636
|
* where the bound is really Cloudflare's. `bounds` records who chose the
|
|
573
|
-
* range, because `
|
|
574
|
-
*
|
|
637
|
+
* range, because `api()` refuses an out-of-range `perPage` locally and an
|
|
638
|
+
* agent deserves to know whether the wall it hit is the API's or ours:
|
|
575
639
|
*
|
|
576
640
|
* - `"cloudflare"` — the schema's own documented minimum and maximum.
|
|
577
641
|
* - `"clamped"` — Cloudflare accepts more; this connection caps it lower.
|
|
@@ -602,6 +666,24 @@ function pagingInputProperties(minPerPage, maxPerPage, options = {}) {
|
|
|
602
666
|
},
|
|
603
667
|
};
|
|
604
668
|
}
|
|
669
|
+
/**
|
|
670
|
+
* The second pagination convention, and the schemas say so.
|
|
671
|
+
*
|
|
672
|
+
* Rulesets, KV keys, and both R2 listings page by cursor rather than by page
|
|
673
|
+
* number, because that is how Cloudflare built them. The guide names the four,
|
|
674
|
+
* but an agent reading one tool's schema should not have to fetch the guide to
|
|
675
|
+
* learn that the `page` object it expected is not coming: absence of
|
|
676
|
+
* `nextCursor` is the loop condition here, and it is stated on both ends
|
|
677
|
+
* ([#342](https://github.com/zackbart/connecta/issues/342)).
|
|
678
|
+
*/
|
|
679
|
+
const CURSOR_INPUT_PROPERTY = {
|
|
680
|
+
type: "string",
|
|
681
|
+
description: "Opaque cursor from a previous call's nextCursor. This endpoint pages by cursor, not page number.",
|
|
682
|
+
};
|
|
683
|
+
const NEXT_CURSOR_OUTPUT_PROPERTY = {
|
|
684
|
+
type: "string",
|
|
685
|
+
description: "Pass back as `cursor` to continue. Absent when the listing is complete — this is the only signal; there is no page object.",
|
|
686
|
+
};
|
|
605
687
|
function listOutputSchema(key, item) {
|
|
606
688
|
return {
|
|
607
689
|
type: "object",
|
|
@@ -787,6 +869,8 @@ function headersFromArgs(value) {
|
|
|
787
869
|
const headers = {};
|
|
788
870
|
const forbidden = new Set([
|
|
789
871
|
"authorization",
|
|
872
|
+
"x-auth-email",
|
|
873
|
+
"x-auth-key",
|
|
790
874
|
"cookie",
|
|
791
875
|
"host",
|
|
792
876
|
"content-length",
|
|
@@ -865,7 +949,7 @@ const OPEN_OBJECT_OUTPUT_SCHEMA = {
|
|
|
865
949
|
};
|
|
866
950
|
const QUERY_INPUT_PROPERTY = {
|
|
867
951
|
type: "array",
|
|
868
|
-
description: "
|
|
952
|
+
description: "Query parameters as name/value pairs; each name may appear once.",
|
|
869
953
|
items: {
|
|
870
954
|
type: "object",
|
|
871
955
|
properties: {
|
|
@@ -876,9 +960,14 @@ const QUERY_INPUT_PROPERTY = {
|
|
|
876
960
|
additionalProperties: false,
|
|
877
961
|
},
|
|
878
962
|
};
|
|
963
|
+
// Shared by all three escape hatches, and the compact renderer inlines it
|
|
964
|
+
// three times over. The refused-header list is the connector's boundary rather
|
|
965
|
+
// than a fact the caller composes with, so it lives in the usage guide; keeping
|
|
966
|
+
// it here pushed cloudflare_api_upload's compact input past the 1,024-byte
|
|
967
|
+
// budget ([#342](https://github.com/zackbart/connecta/issues/342)).
|
|
879
968
|
const HEADERS_INPUT_PROPERTY = {
|
|
880
969
|
type: "array",
|
|
881
|
-
description: "
|
|
970
|
+
description: "Endpoint headers as name/value pairs, e.g. cf-r2-jurisdiction or Range. Connector-owned headers are refused.",
|
|
882
971
|
items: {
|
|
883
972
|
type: "object",
|
|
884
973
|
properties: {
|
|
@@ -924,53 +1013,82 @@ const R2_OBJECT_SCHEMA = {
|
|
|
924
1013
|
},
|
|
925
1014
|
required: ["key"],
|
|
926
1015
|
};
|
|
927
|
-
function buildTools(scope) {
|
|
928
|
-
const {
|
|
1016
|
+
function buildTools(scope, authentication) {
|
|
1017
|
+
const { send } = scope;
|
|
929
1018
|
const zoneArg = (args) => requireScope(args["zoneId"], scope.zoneId, "zoneId");
|
|
930
1019
|
const accountArg = (args) => requireScope(args["accountId"], scope.accountId, "accountId");
|
|
931
1020
|
const readOnly = { readOnlyHint: true, destructiveHint: false };
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1021
|
+
const tools = [
|
|
1022
|
+
authentication === "apiToken"
|
|
1023
|
+
? {
|
|
1024
|
+
name: "verify_api_token",
|
|
1025
|
+
description: "Verify the configured Cloudflare API token and report its status. Use this first when any other tool fails with an authentication error, to separate a bad token from a missing permission.",
|
|
1026
|
+
annotations: readOnly,
|
|
1027
|
+
inputSchema: {
|
|
1028
|
+
type: "object",
|
|
1029
|
+
properties: {},
|
|
1030
|
+
required: [],
|
|
1031
|
+
additionalProperties: false,
|
|
1032
|
+
},
|
|
1033
|
+
outputSchema: {
|
|
1034
|
+
type: "object",
|
|
1035
|
+
properties: {
|
|
1036
|
+
id: { type: "string" },
|
|
1037
|
+
status: {
|
|
1038
|
+
type: "string",
|
|
1039
|
+
description: "\"active\" for a usable token.",
|
|
1040
|
+
},
|
|
1041
|
+
notBefore: { type: "string" },
|
|
1042
|
+
expiresOn: { type: "string" },
|
|
1043
|
+
},
|
|
1044
|
+
required: ["status"],
|
|
1045
|
+
},
|
|
1046
|
+
handler: async (_args, ctx) => {
|
|
1047
|
+
const { result } = await callCloudflare(send, { method: "GET", path: "/user/tokens/verify" }, ctx);
|
|
1048
|
+
const token = asRecord(result);
|
|
1049
|
+
return {
|
|
1050
|
+
id: token["id"],
|
|
1051
|
+
status: token["status"],
|
|
1052
|
+
...(token["not_before"] !== undefined
|
|
1053
|
+
? { notBefore: token["not_before"] }
|
|
1054
|
+
: {}),
|
|
1055
|
+
...(token["expires_on"] !== undefined
|
|
1056
|
+
? { expiresOn: token["expires_on"] }
|
|
1057
|
+
: {}),
|
|
1058
|
+
};
|
|
1059
|
+
},
|
|
1060
|
+
}
|
|
1061
|
+
: {
|
|
1062
|
+
name: "verify_global_api_key",
|
|
1063
|
+
description: "Verify the configured Cloudflare Global API Key and account email by retrieving the authenticated user. Use this first when another tool fails with an authentication error.",
|
|
1064
|
+
annotations: readOnly,
|
|
1065
|
+
inputSchema: {
|
|
1066
|
+
type: "object",
|
|
1067
|
+
properties: {},
|
|
1068
|
+
required: [],
|
|
1069
|
+
additionalProperties: false,
|
|
1070
|
+
},
|
|
1071
|
+
outputSchema: {
|
|
1072
|
+
type: "object",
|
|
1073
|
+
properties: {
|
|
1074
|
+
id: { type: "string" },
|
|
1075
|
+
email: { type: "string" },
|
|
1076
|
+
status: {
|
|
1077
|
+
type: "string",
|
|
1078
|
+
description: "\"active\" when Cloudflare accepts the email and key.",
|
|
1079
|
+
},
|
|
950
1080
|
},
|
|
951
|
-
|
|
952
|
-
|
|
1081
|
+
required: ["email", "status"],
|
|
1082
|
+
},
|
|
1083
|
+
handler: async (_args, ctx) => {
|
|
1084
|
+
const { result } = await callCloudflare(send, { method: "GET", path: "/user" }, ctx);
|
|
1085
|
+
const user = asRecord(result);
|
|
1086
|
+
return { id: user["id"], email: user["email"], status: "active" };
|
|
953
1087
|
},
|
|
954
|
-
required: ["status"],
|
|
955
|
-
},
|
|
956
|
-
handler: async (_args, ctx) => {
|
|
957
|
-
const { result } = await callCloudflare(base, { method: "GET", path: "/user/tokens/verify" }, ctx);
|
|
958
|
-
const token = asRecord(result);
|
|
959
|
-
return {
|
|
960
|
-
id: token["id"],
|
|
961
|
-
status: token["status"],
|
|
962
|
-
...(token["not_before"] !== undefined
|
|
963
|
-
? { notBefore: token["not_before"] }
|
|
964
|
-
: {}),
|
|
965
|
-
...(token["expires_on"] !== undefined
|
|
966
|
-
? { expiresOn: token["expires_on"] }
|
|
967
|
-
: {}),
|
|
968
|
-
};
|
|
969
1088
|
},
|
|
970
|
-
},
|
|
971
1089
|
{
|
|
972
1090
|
name: "cloudflare_api_get",
|
|
973
|
-
description: "Call any GET endpoint under Cloudflare's v4 API with this connector's
|
|
1091
|
+
description: "Call any GET endpoint under Cloudflare's v4 API with this connector's credential. Prefer a named tool when one exists; this read-only hatch covers the products the named surface does not reach, such as Images, Stream, D1, and Queues.",
|
|
974
1092
|
annotations: readOnly,
|
|
975
1093
|
inputSchema: {
|
|
976
1094
|
type: "object",
|
|
@@ -1019,9 +1137,9 @@ function buildTools(scope) {
|
|
|
1019
1137
|
...(headers !== undefined ? { headers } : {}),
|
|
1020
1138
|
};
|
|
1021
1139
|
if (responseType === "text" || responseType === "base64") {
|
|
1022
|
-
return await callCloudflareContent(
|
|
1140
|
+
return await callCloudflareContent(send, spec, ctx, responseType);
|
|
1023
1141
|
}
|
|
1024
|
-
const { result, resultInfo } = await callCloudflare(
|
|
1142
|
+
const { result, resultInfo } = await callCloudflare(send, spec, ctx);
|
|
1025
1143
|
return {
|
|
1026
1144
|
result,
|
|
1027
1145
|
...(resultInfo !== undefined ? { resultInfo } : {}),
|
|
@@ -1030,7 +1148,7 @@ function buildTools(scope) {
|
|
|
1030
1148
|
},
|
|
1031
1149
|
{
|
|
1032
1150
|
name: "cloudflare_api_mutate",
|
|
1033
|
-
description: "Call any JSON POST, PUT, PATCH, or DELETE endpoint under Cloudflare's v4 API with this connector's
|
|
1151
|
+
description: "Call any JSON POST, PUT, PATCH, or DELETE endpoint under Cloudflare's v4 API with this connector's credential. The approval-gated write hatch for products the named surface does not reach. No multipart or binary uploads.",
|
|
1034
1152
|
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
1035
1153
|
inputSchema: {
|
|
1036
1154
|
type: "object",
|
|
@@ -1072,7 +1190,7 @@ function buildTools(scope) {
|
|
|
1072
1190
|
const method = String(args["method"]);
|
|
1073
1191
|
const query = queryFromArgs(args["query"]);
|
|
1074
1192
|
const headers = headersFromArgs(args["headers"]);
|
|
1075
|
-
const { result, resultInfo } = await callCloudflare(
|
|
1193
|
+
const { result, resultInfo } = await callCloudflare(send, {
|
|
1076
1194
|
method,
|
|
1077
1195
|
path: cloudflareApiPath(args["path"]),
|
|
1078
1196
|
...(query !== undefined ? { query } : {}),
|
|
@@ -1095,31 +1213,31 @@ function buildTools(scope) {
|
|
|
1095
1213
|
method: {
|
|
1096
1214
|
type: "string",
|
|
1097
1215
|
enum: ["POST", "PUT"],
|
|
1098
|
-
description: "
|
|
1216
|
+
description: "Upload method the Cloudflare endpoint requires.",
|
|
1099
1217
|
},
|
|
1100
1218
|
path: {
|
|
1101
1219
|
type: "string",
|
|
1102
1220
|
minLength: 1,
|
|
1103
|
-
description: "
|
|
1221
|
+
description: "Path below /client/v4, beginning with '/'. No query string.",
|
|
1104
1222
|
},
|
|
1105
1223
|
query: QUERY_INPUT_PROPERTY,
|
|
1106
1224
|
headers: HEADERS_INPUT_PROPERTY,
|
|
1107
1225
|
contentType: {
|
|
1108
1226
|
type: "string",
|
|
1109
1227
|
minLength: 1,
|
|
1110
|
-
description: "Content-Type for a raw text
|
|
1228
|
+
description: "Content-Type for a raw text or base64 body. Omit for multipart.",
|
|
1111
1229
|
},
|
|
1112
1230
|
textBody: {
|
|
1113
1231
|
type: "string",
|
|
1114
|
-
description: "Raw UTF-8
|
|
1232
|
+
description: "Raw UTF-8 body. Exclusive with base64Body and fields/files.",
|
|
1115
1233
|
},
|
|
1116
1234
|
base64Body: {
|
|
1117
1235
|
type: "string",
|
|
1118
|
-
description: "Base64-encoded
|
|
1236
|
+
description: "Base64-encoded body bytes. Exclusive with textBody and fields/files.",
|
|
1119
1237
|
},
|
|
1120
1238
|
fields: {
|
|
1121
1239
|
type: "array",
|
|
1122
|
-
description: "String fields
|
|
1240
|
+
description: "String fields of a multipart/form-data request.",
|
|
1123
1241
|
items: {
|
|
1124
1242
|
type: "object",
|
|
1125
1243
|
properties: {
|
|
@@ -1134,7 +1252,7 @@ function buildTools(scope) {
|
|
|
1134
1252
|
},
|
|
1135
1253
|
files: {
|
|
1136
1254
|
type: "array",
|
|
1137
|
-
description: "
|
|
1255
|
+
description: "Multipart file parts. Each needs exactly one of text or base64.",
|
|
1138
1256
|
items: {
|
|
1139
1257
|
type: "object",
|
|
1140
1258
|
properties: {
|
|
@@ -1165,7 +1283,7 @@ function buildTools(scope) {
|
|
|
1165
1283
|
const query = queryFromArgs(args["query"]);
|
|
1166
1284
|
const headers = headersFromArgs(args["headers"]);
|
|
1167
1285
|
const upload = uploadBody(args);
|
|
1168
|
-
const { result } = await callCloudflare(
|
|
1286
|
+
const { result } = await callCloudflare(send, {
|
|
1169
1287
|
method: String(args["method"]),
|
|
1170
1288
|
path: cloudflareApiPath(args["path"]),
|
|
1171
1289
|
...(query !== undefined ? { query } : {}),
|
|
@@ -1196,7 +1314,7 @@ function buildTools(scope) {
|
|
|
1196
1314
|
},
|
|
1197
1315
|
outputSchema: listOutputSchema("accounts", ACCOUNT_SCHEMA),
|
|
1198
1316
|
handler: async (args, ctx) => {
|
|
1199
|
-
const { result, resultInfo } = await callCloudflare(
|
|
1317
|
+
const { result, resultInfo } = await callCloudflare(send, {
|
|
1200
1318
|
method: "GET",
|
|
1201
1319
|
path: "/accounts",
|
|
1202
1320
|
query: {
|
|
@@ -1241,7 +1359,7 @@ function buildTools(scope) {
|
|
|
1241
1359
|
},
|
|
1242
1360
|
outputSchema: listOutputSchema("zones", ZONE_SCHEMA),
|
|
1243
1361
|
handler: async (args, ctx) => {
|
|
1244
|
-
const { result, resultInfo } = await callCloudflare(
|
|
1362
|
+
const { result, resultInfo } = await callCloudflare(send, {
|
|
1245
1363
|
method: "GET",
|
|
1246
1364
|
path: "/zones",
|
|
1247
1365
|
query: {
|
|
@@ -1279,7 +1397,7 @@ function buildTools(scope) {
|
|
|
1279
1397
|
},
|
|
1280
1398
|
outputSchema: ZONE_SCHEMA,
|
|
1281
1399
|
handler: async (args, ctx) => {
|
|
1282
|
-
const { result } = await callCloudflare(
|
|
1400
|
+
const { result } = await callCloudflare(send, { method: "GET", path: `/zones/${encodeURIComponent(zoneArg(args))}` }, ctx);
|
|
1283
1401
|
return args["raw"] === true ? result : projectZone(result);
|
|
1284
1402
|
},
|
|
1285
1403
|
},
|
|
@@ -1297,7 +1415,7 @@ function buildTools(scope) {
|
|
|
1297
1415
|
},
|
|
1298
1416
|
outputSchema: listOutputSchema("settings", OPEN_OBJECT_OUTPUT_SCHEMA),
|
|
1299
1417
|
handler: async (args, ctx) => {
|
|
1300
|
-
const { result, resultInfo } = await callCloudflare(
|
|
1418
|
+
const { result, resultInfo } = await callCloudflare(send, {
|
|
1301
1419
|
method: "GET",
|
|
1302
1420
|
path: `/zones/${encodePathSegment(zoneArg(args))}/settings`,
|
|
1303
1421
|
}, ctx);
|
|
@@ -1323,7 +1441,7 @@ function buildTools(scope) {
|
|
|
1323
1441
|
},
|
|
1324
1442
|
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1325
1443
|
handler: async (args, ctx) => {
|
|
1326
|
-
const { result } = await callCloudflare(
|
|
1444
|
+
const { result } = await callCloudflare(send, {
|
|
1327
1445
|
method: "GET",
|
|
1328
1446
|
path: `/zones/${encodePathSegment(zoneArg(args))}/settings/${encodePathSegment(requireString(args, "settingId"))}`,
|
|
1329
1447
|
}, ctx);
|
|
@@ -1354,7 +1472,7 @@ function buildTools(scope) {
|
|
|
1354
1472
|
},
|
|
1355
1473
|
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1356
1474
|
handler: async (args, ctx) => {
|
|
1357
|
-
const { result } = await callCloudflare(
|
|
1475
|
+
const { result } = await callCloudflare(send, {
|
|
1358
1476
|
method: "PATCH",
|
|
1359
1477
|
path: `/zones/${encodePathSegment(zoneArg(args))}/settings/${encodePathSegment(requireString(args, "settingId"))}`,
|
|
1360
1478
|
body: { value: args["value"] },
|
|
@@ -1376,10 +1494,7 @@ function buildTools(scope) {
|
|
|
1376
1494
|
maximum: 50,
|
|
1377
1495
|
description: "Rulesets per request, 1 to 50.",
|
|
1378
1496
|
},
|
|
1379
|
-
cursor:
|
|
1380
|
-
type: "string",
|
|
1381
|
-
description: "Opaque cursor returned as nextCursor by the previous call.",
|
|
1382
|
-
},
|
|
1497
|
+
cursor: CURSOR_INPUT_PROPERTY,
|
|
1383
1498
|
},
|
|
1384
1499
|
required: scopeRequired("zoneId", scope.zoneId),
|
|
1385
1500
|
additionalProperties: false,
|
|
@@ -1388,12 +1503,12 @@ function buildTools(scope) {
|
|
|
1388
1503
|
type: "object",
|
|
1389
1504
|
properties: {
|
|
1390
1505
|
rulesets: { type: "array", items: OPEN_OBJECT_OUTPUT_SCHEMA },
|
|
1391
|
-
nextCursor:
|
|
1506
|
+
nextCursor: NEXT_CURSOR_OUTPUT_PROPERTY,
|
|
1392
1507
|
},
|
|
1393
1508
|
required: ["rulesets"],
|
|
1394
1509
|
},
|
|
1395
1510
|
handler: async (args, ctx) => {
|
|
1396
|
-
const { result, resultInfo } = await callCloudflare(
|
|
1511
|
+
const { result, resultInfo } = await callCloudflare(send, {
|
|
1397
1512
|
method: "GET",
|
|
1398
1513
|
path: `/zones/${encodePathSegment(zoneArg(args))}/rulesets`,
|
|
1399
1514
|
query: {
|
|
@@ -1429,7 +1544,7 @@ function buildTools(scope) {
|
|
|
1429
1544
|
},
|
|
1430
1545
|
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1431
1546
|
handler: async (args, ctx) => {
|
|
1432
|
-
const { result } = await callCloudflare(
|
|
1547
|
+
const { result } = await callCloudflare(send, {
|
|
1433
1548
|
method: "GET",
|
|
1434
1549
|
path: `/zones/${encodePathSegment(zoneArg(args))}/rulesets/${encodePathSegment(requireString(args, "rulesetId"))}`,
|
|
1435
1550
|
}, ctx);
|
|
@@ -1481,7 +1596,7 @@ function buildTools(scope) {
|
|
|
1481
1596
|
},
|
|
1482
1597
|
outputSchema: listOutputSchema("records", DNS_RECORD_SCHEMA),
|
|
1483
1598
|
handler: async (args, ctx) => {
|
|
1484
|
-
const { result, resultInfo } = await callCloudflare(
|
|
1599
|
+
const { result, resultInfo } = await callCloudflare(send, {
|
|
1485
1600
|
method: "GET",
|
|
1486
1601
|
path: `/zones/${encodeURIComponent(zoneArg(args))}/dns_records`,
|
|
1487
1602
|
query: {
|
|
@@ -1521,7 +1636,7 @@ function buildTools(scope) {
|
|
|
1521
1636
|
},
|
|
1522
1637
|
outputSchema: DNS_RECORD_SCHEMA,
|
|
1523
1638
|
handler: async (args, ctx) => {
|
|
1524
|
-
const { result } = await callCloudflare(
|
|
1639
|
+
const { result } = await callCloudflare(send, {
|
|
1525
1640
|
method: "GET",
|
|
1526
1641
|
path: `/zones/${encodeURIComponent(zoneArg(args))}/dns_records/${encodeURIComponent(String(args["recordId"]))}`,
|
|
1527
1642
|
}, ctx);
|
|
@@ -1552,7 +1667,7 @@ function buildTools(scope) {
|
|
|
1552
1667
|
required: ["id"],
|
|
1553
1668
|
}),
|
|
1554
1669
|
handler: async (args, ctx) => {
|
|
1555
|
-
const { result, resultInfo } = await callCloudflare(
|
|
1670
|
+
const { result, resultInfo } = await callCloudflare(send, {
|
|
1556
1671
|
method: "GET",
|
|
1557
1672
|
path: `/accounts/${encodeURIComponent(accountArg(args))}/workers/scripts`,
|
|
1558
1673
|
}, ctx);
|
|
@@ -1583,7 +1698,7 @@ function buildTools(scope) {
|
|
|
1583
1698
|
},
|
|
1584
1699
|
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1585
1700
|
handler: async (args, ctx) => {
|
|
1586
|
-
const { result } = await callCloudflare(
|
|
1701
|
+
const { result } = await callCloudflare(send, {
|
|
1587
1702
|
method: "GET",
|
|
1588
1703
|
path: `/accounts/${encodePathSegment(accountArg(args))}/workers/scripts/${encodePathSegment(requireString(args, "scriptName"))}/settings`,
|
|
1589
1704
|
}, ctx);
|
|
@@ -1609,7 +1724,7 @@ function buildTools(scope) {
|
|
|
1609
1724
|
},
|
|
1610
1725
|
outputSchema: listOutputSchema("deployments", OPEN_OBJECT_OUTPUT_SCHEMA),
|
|
1611
1726
|
handler: async (args, ctx) => {
|
|
1612
|
-
const { result } = await callCloudflare(
|
|
1727
|
+
const { result } = await callCloudflare(send, {
|
|
1613
1728
|
method: "GET",
|
|
1614
1729
|
path: `/accounts/${encodePathSegment(accountArg(args))}/workers/scripts/${encodePathSegment(requireString(args, "scriptName"))}/deployments`,
|
|
1615
1730
|
}, ctx);
|
|
@@ -1648,7 +1763,7 @@ function buildTools(scope) {
|
|
|
1648
1763
|
},
|
|
1649
1764
|
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1650
1765
|
handler: async (args, ctx) => {
|
|
1651
|
-
const { result } = await callCloudflare(
|
|
1766
|
+
const { result } = await callCloudflare(send, {
|
|
1652
1767
|
method: "GET",
|
|
1653
1768
|
path: `/accounts/${encodePathSegment(accountArg(args))}/workers/scripts/${encodePathSegment(requireString(args, "scriptName"))}/deployments/${encodePathSegment(requireString(args, "deploymentId"))}`,
|
|
1654
1769
|
}, ctx);
|
|
@@ -1686,7 +1801,7 @@ function buildTools(scope) {
|
|
|
1686
1801
|
},
|
|
1687
1802
|
handler: async (args, ctx) => {
|
|
1688
1803
|
const scriptName = requireString(args, "scriptName");
|
|
1689
|
-
await callCloudflare(
|
|
1804
|
+
await callCloudflare(send, {
|
|
1690
1805
|
method: "DELETE",
|
|
1691
1806
|
path: `/accounts/${encodePathSegment(accountArg(args))}/workers/scripts/${encodePathSegment(scriptName)}`,
|
|
1692
1807
|
query: { force: optionalBoolean(args, "force") },
|
|
@@ -1718,7 +1833,7 @@ function buildTools(scope) {
|
|
|
1718
1833
|
required: ["id", "title"],
|
|
1719
1834
|
}),
|
|
1720
1835
|
handler: async (args, ctx) => {
|
|
1721
|
-
const { result, resultInfo } = await callCloudflare(
|
|
1836
|
+
const { result, resultInfo } = await callCloudflare(send, {
|
|
1722
1837
|
method: "GET",
|
|
1723
1838
|
path: `/accounts/${encodeURIComponent(accountArg(args))}/storage/kv/namespaces`,
|
|
1724
1839
|
query: {
|
|
@@ -1753,7 +1868,7 @@ function buildTools(scope) {
|
|
|
1753
1868
|
},
|
|
1754
1869
|
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1755
1870
|
handler: async (args, ctx) => {
|
|
1756
|
-
const { result } = await callCloudflare(
|
|
1871
|
+
const { result } = await callCloudflare(send, {
|
|
1757
1872
|
method: "GET",
|
|
1758
1873
|
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(requireString(args, "namespaceId"))}`,
|
|
1759
1874
|
}, ctx);
|
|
@@ -1780,7 +1895,7 @@ function buildTools(scope) {
|
|
|
1780
1895
|
},
|
|
1781
1896
|
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1782
1897
|
handler: async (args, ctx) => {
|
|
1783
|
-
const { result } = await callCloudflare(
|
|
1898
|
+
const { result } = await callCloudflare(send, {
|
|
1784
1899
|
method: "POST",
|
|
1785
1900
|
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces`,
|
|
1786
1901
|
body: { title: requireString(args, "title") },
|
|
@@ -1817,7 +1932,7 @@ function buildTools(scope) {
|
|
|
1817
1932
|
},
|
|
1818
1933
|
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1819
1934
|
handler: async (args, ctx) => {
|
|
1820
|
-
const { result } = await callCloudflare(
|
|
1935
|
+
const { result } = await callCloudflare(send, {
|
|
1821
1936
|
method: "PUT",
|
|
1822
1937
|
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(requireString(args, "namespaceId"))}`,
|
|
1823
1938
|
body: { title: requireString(args, "title") },
|
|
@@ -1852,7 +1967,7 @@ function buildTools(scope) {
|
|
|
1852
1967
|
},
|
|
1853
1968
|
handler: async (args, ctx) => {
|
|
1854
1969
|
const namespaceId = requireString(args, "namespaceId");
|
|
1855
|
-
await callCloudflare(
|
|
1970
|
+
await callCloudflare(send, {
|
|
1856
1971
|
method: "DELETE",
|
|
1857
1972
|
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(namespaceId)}`,
|
|
1858
1973
|
}, ctx);
|
|
@@ -1882,10 +1997,7 @@ function buildTools(scope) {
|
|
|
1882
1997
|
maximum: 1000,
|
|
1883
1998
|
description: "Keys per request, 10 to 1000. Defaults to 1000.",
|
|
1884
1999
|
},
|
|
1885
|
-
cursor:
|
|
1886
|
-
type: "string",
|
|
1887
|
-
description: "Opaque cursor returned as nextCursor by the previous call.",
|
|
1888
|
-
},
|
|
2000
|
+
cursor: CURSOR_INPUT_PROPERTY,
|
|
1889
2001
|
},
|
|
1890
2002
|
required: [...scopeRequired("accountId", scope.accountId), "namespaceId"],
|
|
1891
2003
|
additionalProperties: false,
|
|
@@ -1894,12 +2006,12 @@ function buildTools(scope) {
|
|
|
1894
2006
|
type: "object",
|
|
1895
2007
|
properties: {
|
|
1896
2008
|
keys: { type: "array", items: OPEN_OBJECT_OUTPUT_SCHEMA },
|
|
1897
|
-
nextCursor:
|
|
2009
|
+
nextCursor: NEXT_CURSOR_OUTPUT_PROPERTY,
|
|
1898
2010
|
},
|
|
1899
2011
|
required: ["keys"],
|
|
1900
2012
|
},
|
|
1901
2013
|
handler: async (args, ctx) => {
|
|
1902
|
-
const { result, resultInfo } = await callCloudflare(
|
|
2014
|
+
const { result, resultInfo } = await callCloudflare(send, {
|
|
1903
2015
|
method: "GET",
|
|
1904
2016
|
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(requireString(args, "namespaceId"))}/keys`,
|
|
1905
2017
|
query: {
|
|
@@ -1956,7 +2068,7 @@ function buildTools(scope) {
|
|
|
1956
2068
|
},
|
|
1957
2069
|
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
1958
2070
|
handler: async (args, ctx) => {
|
|
1959
|
-
const { result } = await callCloudflare(
|
|
2071
|
+
const { result } = await callCloudflare(send, {
|
|
1960
2072
|
method: "POST",
|
|
1961
2073
|
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(requireString(args, "namespaceId"))}/bulk/get`,
|
|
1962
2074
|
body: {
|
|
@@ -1991,12 +2103,34 @@ function buildTools(scope) {
|
|
|
1991
2103
|
items: {
|
|
1992
2104
|
type: "object",
|
|
1993
2105
|
properties: {
|
|
1994
|
-
key: {
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2106
|
+
key: {
|
|
2107
|
+
type: "string",
|
|
2108
|
+
minLength: 1,
|
|
2109
|
+
maxLength: 512,
|
|
2110
|
+
description: "Key name, up to Cloudflare's 512 bytes.",
|
|
2111
|
+
},
|
|
2112
|
+
value: {
|
|
2113
|
+
type: "string",
|
|
2114
|
+
maxLength: 26_214_400,
|
|
2115
|
+
description: "Value, up to Cloudflare's 25 MiB.",
|
|
2116
|
+
},
|
|
2117
|
+
expiration: {
|
|
2118
|
+
type: "number",
|
|
2119
|
+
description: "Absolute expiry as a Unix timestamp in seconds.",
|
|
2120
|
+
},
|
|
2121
|
+
expiration_ttl: {
|
|
2122
|
+
type: "number",
|
|
2123
|
+
minimum: 60,
|
|
2124
|
+
description: "Relative expiry in seconds; Cloudflare's floor is 60.",
|
|
2125
|
+
},
|
|
2126
|
+
metadata: {
|
|
2127
|
+
type: ["object", "array", "string", "number", "boolean", "null"],
|
|
2128
|
+
description: "JSON metadata returned beside the key by list_kv_keys.",
|
|
2129
|
+
},
|
|
2130
|
+
base64: {
|
|
2131
|
+
type: "boolean",
|
|
2132
|
+
description: "Treat value as base64 and store the decoded bytes.",
|
|
2133
|
+
},
|
|
2000
2134
|
},
|
|
2001
2135
|
required: ["key", "value"],
|
|
2002
2136
|
additionalProperties: false,
|
|
@@ -2012,7 +2146,7 @@ function buildTools(scope) {
|
|
|
2012
2146
|
},
|
|
2013
2147
|
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2014
2148
|
handler: async (args, ctx) => {
|
|
2015
|
-
const { result } = await callCloudflare(
|
|
2149
|
+
const { result } = await callCloudflare(send, {
|
|
2016
2150
|
method: "PUT",
|
|
2017
2151
|
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(requireString(args, "namespaceId"))}/bulk`,
|
|
2018
2152
|
body: args["entries"],
|
|
@@ -2050,7 +2184,7 @@ function buildTools(scope) {
|
|
|
2050
2184
|
},
|
|
2051
2185
|
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2052
2186
|
handler: async (args, ctx) => {
|
|
2053
|
-
const { result } = await callCloudflare(
|
|
2187
|
+
const { result } = await callCloudflare(send, {
|
|
2054
2188
|
method: "POST",
|
|
2055
2189
|
path: `/accounts/${encodePathSegment(accountArg(args))}/storage/kv/namespaces/${encodePathSegment(requireString(args, "namespaceId"))}/bulk/delete`,
|
|
2056
2190
|
body: args["keys"],
|
|
@@ -2076,10 +2210,7 @@ function buildTools(scope) {
|
|
|
2076
2210
|
maximum: 1000,
|
|
2077
2211
|
description: "Buckets per request, 1 to 1000. Defaults to 20.",
|
|
2078
2212
|
},
|
|
2079
|
-
cursor:
|
|
2080
|
-
type: "string",
|
|
2081
|
-
description: "Opaque cursor from a previous call's nextCursor. R2 paginates by cursor, not page number.",
|
|
2082
|
-
},
|
|
2213
|
+
cursor: CURSOR_INPUT_PROPERTY,
|
|
2083
2214
|
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2084
2215
|
raw: RAW_INPUT_PROPERTY,
|
|
2085
2216
|
},
|
|
@@ -2103,15 +2234,12 @@ function buildTools(scope) {
|
|
|
2103
2234
|
required: ["name"],
|
|
2104
2235
|
},
|
|
2105
2236
|
},
|
|
2106
|
-
nextCursor:
|
|
2107
|
-
type: "string",
|
|
2108
|
-
description: "Pass back as `cursor` to continue. Absent when the listing is complete.",
|
|
2109
|
-
},
|
|
2237
|
+
nextCursor: NEXT_CURSOR_OUTPUT_PROPERTY,
|
|
2110
2238
|
},
|
|
2111
2239
|
required: ["buckets"],
|
|
2112
2240
|
},
|
|
2113
2241
|
handler: async (args, ctx) => {
|
|
2114
|
-
const { result, resultInfo } = await callCloudflare(
|
|
2242
|
+
const { result, resultInfo } = await callCloudflare(send, {
|
|
2115
2243
|
method: "GET",
|
|
2116
2244
|
path: `/accounts/${encodeURIComponent(accountArg(args))}/r2/buckets`,
|
|
2117
2245
|
query: {
|
|
@@ -2151,7 +2279,7 @@ function buildTools(scope) {
|
|
|
2151
2279
|
},
|
|
2152
2280
|
outputSchema: R2_BUCKET_SCHEMA,
|
|
2153
2281
|
handler: async (args, ctx) => {
|
|
2154
|
-
const { result } = await callCloudflare(
|
|
2282
|
+
const { result } = await callCloudflare(send, {
|
|
2155
2283
|
method: "GET",
|
|
2156
2284
|
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}`,
|
|
2157
2285
|
headers: r2Headers(args),
|
|
@@ -2185,7 +2313,7 @@ function buildTools(scope) {
|
|
|
2185
2313
|
},
|
|
2186
2314
|
outputSchema: R2_BUCKET_SCHEMA,
|
|
2187
2315
|
handler: async (args, ctx) => {
|
|
2188
|
-
const { result } = await callCloudflare(
|
|
2316
|
+
const { result } = await callCloudflare(send, {
|
|
2189
2317
|
method: "POST",
|
|
2190
2318
|
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets`,
|
|
2191
2319
|
headers: r2Headers(args),
|
|
@@ -2227,7 +2355,7 @@ function buildTools(scope) {
|
|
|
2227
2355
|
},
|
|
2228
2356
|
outputSchema: R2_BUCKET_SCHEMA,
|
|
2229
2357
|
handler: async (args, ctx) => {
|
|
2230
|
-
const { result } = await callCloudflare(
|
|
2358
|
+
const { result } = await callCloudflare(send, {
|
|
2231
2359
|
method: "PATCH",
|
|
2232
2360
|
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}`,
|
|
2233
2361
|
headers: {
|
|
@@ -2262,7 +2390,7 @@ function buildTools(scope) {
|
|
|
2262
2390
|
},
|
|
2263
2391
|
handler: async (args, ctx) => {
|
|
2264
2392
|
const bucketName = requireString(args, "bucketName");
|
|
2265
|
-
await callCloudflare(
|
|
2393
|
+
await callCloudflare(send, {
|
|
2266
2394
|
method: "DELETE",
|
|
2267
2395
|
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(bucketName)}`,
|
|
2268
2396
|
headers: r2Headers(args),
|
|
@@ -2300,10 +2428,7 @@ function buildTools(scope) {
|
|
|
2300
2428
|
maximum: 1000,
|
|
2301
2429
|
description: "Objects per request, 1 to 1000.",
|
|
2302
2430
|
},
|
|
2303
|
-
cursor:
|
|
2304
|
-
type: "string",
|
|
2305
|
-
description: "Opaque cursor returned as nextCursor by the previous call.",
|
|
2306
|
-
},
|
|
2431
|
+
cursor: CURSOR_INPUT_PROPERTY,
|
|
2307
2432
|
},
|
|
2308
2433
|
required: [...scopeRequired("accountId", scope.accountId), "bucketName"],
|
|
2309
2434
|
additionalProperties: false,
|
|
@@ -2313,13 +2438,13 @@ function buildTools(scope) {
|
|
|
2313
2438
|
properties: {
|
|
2314
2439
|
objects: { type: "array", items: R2_OBJECT_SCHEMA },
|
|
2315
2440
|
commonPrefixes: { type: "array", items: { type: "string" } },
|
|
2316
|
-
nextCursor:
|
|
2441
|
+
nextCursor: NEXT_CURSOR_OUTPUT_PROPERTY,
|
|
2317
2442
|
truncated: { type: "boolean" },
|
|
2318
2443
|
},
|
|
2319
2444
|
required: ["objects", "truncated"],
|
|
2320
2445
|
},
|
|
2321
2446
|
handler: async (args, ctx) => {
|
|
2322
|
-
const { result, resultInfo } = await callCloudflare(
|
|
2447
|
+
const { result, resultInfo } = await callCloudflare(send, {
|
|
2323
2448
|
method: "GET",
|
|
2324
2449
|
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}/objects`,
|
|
2325
2450
|
headers: r2Headers(args),
|
|
@@ -2377,7 +2502,7 @@ function buildTools(scope) {
|
|
|
2377
2502
|
},
|
|
2378
2503
|
handler: async (args, ctx) => {
|
|
2379
2504
|
const objectKey = requireString(args, "objectKey");
|
|
2380
|
-
await callCloudflare(
|
|
2505
|
+
await callCloudflare(send, {
|
|
2381
2506
|
method: "DELETE",
|
|
2382
2507
|
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}/objects/${encodeObjectKey(objectKey)}`,
|
|
2383
2508
|
headers: r2Headers(args),
|
|
@@ -2385,25 +2510,17 @@ function buildTools(scope) {
|
|
|
2385
2510
|
return { deleted: true, objectKey };
|
|
2386
2511
|
},
|
|
2387
2512
|
},
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
handler: async (args, ctx) => {
|
|
2400
|
-
const { result } = await callCloudflare(base, {
|
|
2401
|
-
method: "GET",
|
|
2402
|
-
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/metrics`,
|
|
2403
|
-
}, ctx);
|
|
2404
|
-
return asRecord(result);
|
|
2405
|
-
},
|
|
2406
|
-
},
|
|
2513
|
+
// No `get_r2_metrics`, `set_r2_cors`, or `delete_r2_cors` here on purpose.
|
|
2514
|
+
// The #350 measurement found `get_r2_metrics` an unprojected,
|
|
2515
|
+
// output-schema-less wrapper around a path, and `set_r2_cors` carrying a
|
|
2516
|
+
// free-form `additionalProperties: true` rule body, so its schema did not
|
|
2517
|
+
// validate the part of the call that actually fails. `delete_r2_cors`
|
|
2518
|
+
// measured clean — a fixed `{deleted}` confirmation behind a closed output
|
|
2519
|
+
// schema — and went anyway, as the other half of one policy pair: with the
|
|
2520
|
+
// write unnamed, a named delete would change a CORS policy through a
|
|
2521
|
+
// different route than setting it does. Reading a policy is still named;
|
|
2522
|
+
// changing one takes the approval-gated raw route, exactly as
|
|
2523
|
+
// structured-data DNS records already do.
|
|
2407
2524
|
{
|
|
2408
2525
|
name: "get_r2_cors",
|
|
2409
2526
|
description: "Get the browser CORS rules configured on an R2 bucket.",
|
|
@@ -2420,7 +2537,7 @@ function buildTools(scope) {
|
|
|
2420
2537
|
},
|
|
2421
2538
|
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2422
2539
|
handler: async (args, ctx) => {
|
|
2423
|
-
const { result } = await callCloudflare(
|
|
2540
|
+
const { result } = await callCloudflare(send, {
|
|
2424
2541
|
method: "GET",
|
|
2425
2542
|
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}/cors`,
|
|
2426
2543
|
headers: r2Headers(args),
|
|
@@ -2428,65 +2545,6 @@ function buildTools(scope) {
|
|
|
2428
2545
|
return asRecord(result);
|
|
2429
2546
|
},
|
|
2430
2547
|
},
|
|
2431
|
-
{
|
|
2432
|
-
name: "set_r2_cors",
|
|
2433
|
-
description: "Replace an R2 bucket's CORS policy. Supply the complete desired rule list; omitted existing rules are removed.",
|
|
2434
|
-
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2435
|
-
inputSchema: {
|
|
2436
|
-
type: "object",
|
|
2437
|
-
properties: {
|
|
2438
|
-
accountId: scopeProperty("accountId", scope.accountId),
|
|
2439
|
-
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2440
|
-
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2441
|
-
rules: {
|
|
2442
|
-
type: "array",
|
|
2443
|
-
maxItems: 100,
|
|
2444
|
-
description: "Complete CORS rule list using Cloudflare fields: allowed.methods, allowed.origins, optional allowed.headers, id, exposeHeaders, and maxAgeSeconds.",
|
|
2445
|
-
items: { type: "object", additionalProperties: true },
|
|
2446
|
-
},
|
|
2447
|
-
},
|
|
2448
|
-
required: [...scopeRequired("accountId", scope.accountId), "bucketName", "rules"],
|
|
2449
|
-
additionalProperties: false,
|
|
2450
|
-
},
|
|
2451
|
-
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2452
|
-
handler: async (args, ctx) => {
|
|
2453
|
-
const { result } = await callCloudflare(base, {
|
|
2454
|
-
method: "PUT",
|
|
2455
|
-
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}/cors`,
|
|
2456
|
-
headers: r2Headers(args),
|
|
2457
|
-
body: { rules: args["rules"] },
|
|
2458
|
-
}, ctx);
|
|
2459
|
-
return asRecord(result);
|
|
2460
|
-
},
|
|
2461
|
-
},
|
|
2462
|
-
{
|
|
2463
|
-
name: "delete_r2_cors",
|
|
2464
|
-
description: "Remove the complete CORS policy from an R2 bucket.",
|
|
2465
|
-
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
2466
|
-
inputSchema: {
|
|
2467
|
-
type: "object",
|
|
2468
|
-
properties: {
|
|
2469
|
-
accountId: scopeProperty("accountId", scope.accountId),
|
|
2470
|
-
bucketName: R2_BUCKET_NAME_PROPERTY,
|
|
2471
|
-
jurisdiction: R2_JURISDICTION_PROPERTY,
|
|
2472
|
-
},
|
|
2473
|
-
required: [...scopeRequired("accountId", scope.accountId), "bucketName"],
|
|
2474
|
-
additionalProperties: false,
|
|
2475
|
-
},
|
|
2476
|
-
outputSchema: {
|
|
2477
|
-
type: "object",
|
|
2478
|
-
properties: { deleted: { type: "boolean" } },
|
|
2479
|
-
required: ["deleted"],
|
|
2480
|
-
},
|
|
2481
|
-
handler: async (args, ctx) => {
|
|
2482
|
-
await callCloudflare(base, {
|
|
2483
|
-
method: "DELETE",
|
|
2484
|
-
path: `/accounts/${encodePathSegment(accountArg(args))}/r2/buckets/${encodePathSegment(requireString(args, "bucketName"))}/cors`,
|
|
2485
|
-
headers: r2Headers(args),
|
|
2486
|
-
}, ctx);
|
|
2487
|
-
return { deleted: true };
|
|
2488
|
-
},
|
|
2489
|
-
},
|
|
2490
2548
|
{
|
|
2491
2549
|
name: "list_pages_projects",
|
|
2492
2550
|
description: "List Cloudflare Pages projects in an account, with their production branch and latest deployment.",
|
|
@@ -2522,7 +2580,7 @@ function buildTools(scope) {
|
|
|
2522
2580
|
required: ["name"],
|
|
2523
2581
|
}),
|
|
2524
2582
|
handler: async (args, ctx) => {
|
|
2525
|
-
const { result, resultInfo } = await callCloudflare(
|
|
2583
|
+
const { result, resultInfo } = await callCloudflare(send, {
|
|
2526
2584
|
method: "GET",
|
|
2527
2585
|
path: `/accounts/${encodeURIComponent(accountArg(args))}/pages/projects`,
|
|
2528
2586
|
query: {
|
|
@@ -2558,7 +2616,7 @@ function buildTools(scope) {
|
|
|
2558
2616
|
},
|
|
2559
2617
|
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2560
2618
|
handler: async (args, ctx) => {
|
|
2561
|
-
const { result } = await callCloudflare(
|
|
2619
|
+
const { result } = await callCloudflare(send, {
|
|
2562
2620
|
method: "GET",
|
|
2563
2621
|
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}`,
|
|
2564
2622
|
}, ctx);
|
|
@@ -2590,7 +2648,7 @@ function buildTools(scope) {
|
|
|
2590
2648
|
},
|
|
2591
2649
|
outputSchema: listOutputSchema("deployments", OPEN_OBJECT_OUTPUT_SCHEMA),
|
|
2592
2650
|
handler: async (args, ctx) => {
|
|
2593
|
-
const { result, resultInfo } = await callCloudflare(
|
|
2651
|
+
const { result, resultInfo } = await callCloudflare(send, {
|
|
2594
2652
|
method: "GET",
|
|
2595
2653
|
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/deployments`,
|
|
2596
2654
|
query: {
|
|
@@ -2634,7 +2692,7 @@ function buildTools(scope) {
|
|
|
2634
2692
|
},
|
|
2635
2693
|
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2636
2694
|
handler: async (args, ctx) => {
|
|
2637
|
-
const { result } = await callCloudflare(
|
|
2695
|
+
const { result } = await callCloudflare(send, {
|
|
2638
2696
|
method: "GET",
|
|
2639
2697
|
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/deployments/${encodePathSegment(requireString(args, "deploymentId"))}`,
|
|
2640
2698
|
}, ctx);
|
|
@@ -2657,7 +2715,7 @@ function buildTools(scope) {
|
|
|
2657
2715
|
},
|
|
2658
2716
|
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2659
2717
|
handler: async (args, ctx) => {
|
|
2660
|
-
const { result } = await callCloudflare(
|
|
2718
|
+
const { result } = await callCloudflare(send, {
|
|
2661
2719
|
method: "POST",
|
|
2662
2720
|
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/deployments/${encodePathSegment(requireString(args, "deploymentId"))}/retry`,
|
|
2663
2721
|
}, ctx);
|
|
@@ -2680,7 +2738,7 @@ function buildTools(scope) {
|
|
|
2680
2738
|
},
|
|
2681
2739
|
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2682
2740
|
handler: async (args, ctx) => {
|
|
2683
|
-
const { result } = await callCloudflare(
|
|
2741
|
+
const { result } = await callCloudflare(send, {
|
|
2684
2742
|
method: "POST",
|
|
2685
2743
|
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/deployments/${encodePathSegment(requireString(args, "deploymentId"))}/rollback`,
|
|
2686
2744
|
}, ctx);
|
|
@@ -2704,7 +2762,7 @@ function buildTools(scope) {
|
|
|
2704
2762
|
outputSchema: { type: "object", properties: { deleted: { type: "boolean" }, deploymentId: { type: "string" } }, required: ["deleted", "deploymentId"] },
|
|
2705
2763
|
handler: async (args, ctx) => {
|
|
2706
2764
|
const deploymentId = requireString(args, "deploymentId");
|
|
2707
|
-
await callCloudflare(
|
|
2765
|
+
await callCloudflare(send, {
|
|
2708
2766
|
method: "DELETE",
|
|
2709
2767
|
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/deployments/${encodePathSegment(deploymentId)}`,
|
|
2710
2768
|
}, ctx);
|
|
@@ -2726,7 +2784,7 @@ function buildTools(scope) {
|
|
|
2726
2784
|
},
|
|
2727
2785
|
outputSchema: listOutputSchema("domains", OPEN_OBJECT_OUTPUT_SCHEMA),
|
|
2728
2786
|
handler: async (args, ctx) => {
|
|
2729
|
-
const { result } = await callCloudflare(
|
|
2787
|
+
const { result } = await callCloudflare(send, {
|
|
2730
2788
|
method: "GET",
|
|
2731
2789
|
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/domains`,
|
|
2732
2790
|
}, ctx);
|
|
@@ -2749,7 +2807,7 @@ function buildTools(scope) {
|
|
|
2749
2807
|
},
|
|
2750
2808
|
outputSchema: OPEN_OBJECT_OUTPUT_SCHEMA,
|
|
2751
2809
|
handler: async (args, ctx) => {
|
|
2752
|
-
const { result } = await callCloudflare(
|
|
2810
|
+
const { result } = await callCloudflare(send, {
|
|
2753
2811
|
method: "POST",
|
|
2754
2812
|
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/domains`,
|
|
2755
2813
|
body: { name: requireString(args, "domain") },
|
|
@@ -2774,7 +2832,7 @@ function buildTools(scope) {
|
|
|
2774
2832
|
outputSchema: { type: "object", properties: { deleted: { type: "boolean" }, domain: { type: "string" } }, required: ["deleted", "domain"] },
|
|
2775
2833
|
handler: async (args, ctx) => {
|
|
2776
2834
|
const domain = requireString(args, "domain");
|
|
2777
|
-
await callCloudflare(
|
|
2835
|
+
await callCloudflare(send, {
|
|
2778
2836
|
method: "DELETE",
|
|
2779
2837
|
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/domains/${encodePathSegment(domain)}`,
|
|
2780
2838
|
}, ctx);
|
|
@@ -2796,7 +2854,7 @@ function buildTools(scope) {
|
|
|
2796
2854
|
},
|
|
2797
2855
|
outputSchema: { type: "object", properties: { purged: { type: "boolean" } }, required: ["purged"] },
|
|
2798
2856
|
handler: async (args, ctx) => {
|
|
2799
|
-
await callCloudflare(
|
|
2857
|
+
await callCloudflare(send, {
|
|
2800
2858
|
method: "POST",
|
|
2801
2859
|
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(requireString(args, "projectName"))}/purge_build_cache`,
|
|
2802
2860
|
}, ctx);
|
|
@@ -2819,7 +2877,7 @@ function buildTools(scope) {
|
|
|
2819
2877
|
outputSchema: { type: "object", properties: { deleted: { type: "boolean" }, projectName: { type: "string" } }, required: ["deleted", "projectName"] },
|
|
2820
2878
|
handler: async (args, ctx) => {
|
|
2821
2879
|
const projectName = requireString(args, "projectName");
|
|
2822
|
-
await callCloudflare(
|
|
2880
|
+
await callCloudflare(send, {
|
|
2823
2881
|
method: "DELETE",
|
|
2824
2882
|
path: `/accounts/${encodePathSegment(accountArg(args))}/pages/projects/${encodePathSegment(projectName)}`,
|
|
2825
2883
|
}, ctx);
|
|
@@ -2831,7 +2889,7 @@ function buildTools(scope) {
|
|
|
2831
2889
|
// `destructiveHint` stays unset. `readOnlyHint: false` already routes it
|
|
2832
2890
|
// through call_destructive_tool.
|
|
2833
2891
|
name: "create_dns_record",
|
|
2834
|
-
description: "Create a content-based DNS record
|
|
2892
|
+
description: "Create a content-based DNS record in a zone; the type enum lists the creatable types. Check list_dns_records first — Cloudflare rejects a duplicate rather than replacing it. Structured types like SRV and CAA are readable but not creatable.",
|
|
2835
2893
|
annotations: { readOnlyHint: false },
|
|
2836
2894
|
inputSchema: {
|
|
2837
2895
|
type: "object",
|
|
@@ -2886,7 +2944,7 @@ function buildTools(scope) {
|
|
|
2886
2944
|
},
|
|
2887
2945
|
outputSchema: DNS_RECORD_SCHEMA,
|
|
2888
2946
|
handler: async (args, ctx) => {
|
|
2889
|
-
const { result } = await callCloudflare(
|
|
2947
|
+
const { result } = await callCloudflare(send, {
|
|
2890
2948
|
method: "POST",
|
|
2891
2949
|
path: `/zones/${encodeURIComponent(zoneArg(args))}/dns_records`,
|
|
2892
2950
|
body: {
|
|
@@ -2987,7 +3045,7 @@ function buildTools(scope) {
|
|
|
2987
3045
|
},
|
|
2988
3046
|
});
|
|
2989
3047
|
}
|
|
2990
|
-
const { result } = await callCloudflare(
|
|
3048
|
+
const { result } = await callCloudflare(send, {
|
|
2991
3049
|
method: "PATCH",
|
|
2992
3050
|
path: `/zones/${encodeURIComponent(zoneArg(args))}/dns_records/${encodeURIComponent(String(args["recordId"]))}`,
|
|
2993
3051
|
body,
|
|
@@ -3021,7 +3079,7 @@ function buildTools(scope) {
|
|
|
3021
3079
|
},
|
|
3022
3080
|
handler: async (args, ctx) => {
|
|
3023
3081
|
const recordId = String(args["recordId"]);
|
|
3024
|
-
await callCloudflare(
|
|
3082
|
+
await callCloudflare(send, {
|
|
3025
3083
|
method: "DELETE",
|
|
3026
3084
|
path: `/zones/${encodeURIComponent(zoneArg(args))}/dns_records/${encodeURIComponent(recordId)}`,
|
|
3027
3085
|
}, ctx);
|
|
@@ -3121,7 +3179,7 @@ function buildTools(scope) {
|
|
|
3121
3179
|
});
|
|
3122
3180
|
}
|
|
3123
3181
|
const variant = everything ? "everything" : targeted[0];
|
|
3124
|
-
await callCloudflare(
|
|
3182
|
+
await callCloudflare(send, {
|
|
3125
3183
|
method: "POST",
|
|
3126
3184
|
path: `/zones/${encodeURIComponent(zoneId)}/purge_cache`,
|
|
3127
3185
|
body: everything
|
|
@@ -3132,8 +3190,12 @@ function buildTools(scope) {
|
|
|
3132
3190
|
},
|
|
3133
3191
|
},
|
|
3134
3192
|
];
|
|
3193
|
+
return tools.map((tool) => ({
|
|
3194
|
+
...tool,
|
|
3195
|
+
handler: (args, ctx) => tool.handler(args, withAuthentication(ctx, authentication)),
|
|
3196
|
+
}));
|
|
3135
3197
|
}
|
|
3136
|
-
function usageGuide(purpose, scope, instructions) {
|
|
3198
|
+
function usageGuide(purpose, scope, instructions, authentication) {
|
|
3137
3199
|
const accountInstructions = instructions?.trim();
|
|
3138
3200
|
const zoneLine = scope.zoneId
|
|
3139
3201
|
? `This connector defaults to zone \`${scope.zoneId}\`; omit \`zoneId\` unless the request names a different domain.`
|
|
@@ -3141,18 +3203,22 @@ function usageGuide(purpose, scope, instructions) {
|
|
|
3141
3203
|
const accountLine = scope.accountId
|
|
3142
3204
|
? `It defaults to account \`${scope.accountId}\`; omit \`accountId\` unless the request names a different account.`
|
|
3143
3205
|
: "It declares no default account. `list_accounts` supplies the `accountId` the Workers, KV, R2, and Pages tools need.";
|
|
3206
|
+
const authenticationLine = authentication === "apiToken"
|
|
3207
|
+
? "The API token is operator-managed and scoped by permission. An `auth_required` failure means the token is missing, invalid, or lacks that call's permission. Call `verify_api_token` first."
|
|
3208
|
+
: "The Global API Key and account email are operator-managed. The key has the same access as its Cloudflare user. An `auth_required` failure means one field is missing, the pair is invalid, or the user lacks access. Call `verify_global_api_key` first.";
|
|
3144
3209
|
return `# Cloudflare usage
|
|
3145
3210
|
|
|
3146
3211
|
Account purpose: ${purpose}
|
|
3147
3212
|
|
|
3148
3213
|
- ${zoneLine}
|
|
3149
3214
|
- ${accountLine}
|
|
3150
|
-
- Prefer a named tool: its schema is complete, projected, and enough to call it without provider documentation. For an operation without a named tool, use \`cloudflare_api_get\` for GET, \`cloudflare_api_mutate\` for JSON POST/PUT/PATCH/DELETE, or \`cloudflare_api_upload\` for raw and multipart content. Raw tools take a path below \`/client/v4\`; their argument schemas are complete, but endpoint-specific query, header, and body fields come from Cloudflare's API reference. Use \`headers\` for endpoint-specific controls such as \`cf-r2-jurisdiction\`,
|
|
3151
|
-
- The raw tools cover the wider control plane without weakening routing: GET is explicitly read-only; every mutation and upload is destructive and must cross the host's approval boundary. The
|
|
3215
|
+
- Prefer a named tool: its schema is complete, projected, and enough to call it without provider documentation. For an operation without a named tool, use \`cloudflare_api_get\` for GET, \`cloudflare_api_mutate\` for JSON POST/PUT/PATCH/DELETE, or \`cloudflare_api_upload\` for raw and multipart content. Raw tools take a path below \`/client/v4\`; their argument schemas are complete, but endpoint-specific query, header, and body fields come from Cloudflare's API reference. Use \`headers\` for endpoint-specific controls such as \`cf-r2-jurisdiction\`, \`Range\`, \`If-None-Match\`, and Cloudflare product metadata. \`Authorization\`, \`Cookie\`, \`Host\`, \`Content-Length\`, \`Content-Type\`, and \`Transfer-Encoding\` are connector-owned and refused: authentication, host, content type, and request framing are not the caller's to set.
|
|
3216
|
+
- The raw tools cover the wider control plane without weakening routing: GET is explicitly read-only; every mutation and upload is destructive and must cross the host's approval boundary. The configured Cloudflare credential remains the hard provider-side permission boundary. Absolute URLs, traversal, and query strings embedded in \`path\` are refused locally.
|
|
3152
3217
|
- Useful raw paths include \`/accounts/{accountId}/images/v1\` (Images), \`/accounts/{accountId}/stream\` (Stream), \`/zones/{zoneId}/email/routing/rules\` (Email Routing), \`/accounts/{accountId}/d1/database\` (D1), and \`/accounts/{accountId}/queues\` (Queues). On GET, use \`responseType: "text"\` or \`"base64"\` for non-JSON content. Direct-upload endpoints can issue upload URLs; \`cloudflare_api_upload\` can also send explicit text, base64 bytes, or multipart fields/files.
|
|
3153
|
-
-
|
|
3218
|
+
- Two R2 areas are deliberately unnamed. Read a bucket's CORS policy with \`get_r2_cors\`, then change it with \`cloudflare_api_mutate\` — \`PUT\` or \`DELETE /accounts/{accountId}/r2/buckets/{bucketName}/cors\`, rule fields per Cloudflare's reference — and read account storage totals with \`cloudflare_api_get\` at \`/accounts/{accountId}/r2/metrics\`.
|
|
3219
|
+
- Lists paginate with \`page\` and \`perPage\` and return a \`page\` object; request the next page only when \`page.hasMore\` is true. \`list_zone_rulesets\`, \`list_r2_buckets\`, \`list_r2_objects\`, and \`list_kv_keys\` page by cursor instead: pass \`cursor\`, continue while \`nextCursor\` is present, and expect no \`page\` object. Their schemas say so too. \`list_worker_scripts\` is unpaginated.
|
|
3154
3220
|
- Results are projected to the fields that identify and describe a resource. Pass \`raw: true\` on a read when you genuinely need a field the projection drops.
|
|
3155
|
-
-
|
|
3221
|
+
- ${authenticationLine}
|
|
3156
3222
|
- A \`rate_limited\` failure carries the wait window. Cloudflare's limit is 1,200 requests per five minutes per user, counted across the dashboard and every token, so do not fan out speculatively; filter server-side with \`name\`, \`type\`, and \`content\` instead of listing everything and filtering locally.
|
|
3157
3223
|
- Named creates that only add a resource are write-routed without claiming destruction. Updates, overwrites, deletes, rollbacks, cache purges, \`cloudflare_api_mutate\`, and \`cloudflare_api_upload\` are destructive. Read current state before changing it, and prefer a targeted \`purge_cache\` over \`everything\`.
|
|
3158
3224
|
${accountInstructions
|
|
@@ -3169,42 +3235,83 @@ export function cloudflare(id, options) {
|
|
|
3169
3235
|
if (!Number.isInteger(maxConcurrency) || maxConcurrency < 1) {
|
|
3170
3236
|
throw new Error("cloudflare() maxConcurrency must be a positive integer.");
|
|
3171
3237
|
}
|
|
3238
|
+
const authentication = options.authentication ?? "apiToken";
|
|
3239
|
+
if (authentication !== "apiToken" && authentication !== "globalApiKey") {
|
|
3240
|
+
throw new Error('cloudflare() authentication must be "apiToken" or "globalApiKey".');
|
|
3241
|
+
}
|
|
3172
3242
|
const scope = {
|
|
3173
|
-
|
|
3243
|
+
send: cloudflareTransport(options.baseUrl?.trim() || CLOUDFLARE_API_BASE),
|
|
3174
3244
|
accountId: options.accountId?.trim() || undefined,
|
|
3175
3245
|
zoneId: options.zoneId?.trim() || undefined,
|
|
3176
3246
|
};
|
|
3177
3247
|
return api(id, {
|
|
3178
3248
|
title: options.title ?? "Cloudflare",
|
|
3179
3249
|
description: `Cloudflare control-plane access for zones, DNS, Workers, KV, R2, Pages, media, email, and other v4 APIs — ${purpose}`,
|
|
3180
|
-
credential: options.credential
|
|
3250
|
+
credential: credentialConfig(authentication, options.credential),
|
|
3181
3251
|
callAdmission: admissionPolicy(maxConcurrency),
|
|
3182
|
-
usageGuide:
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3252
|
+
usageGuide: {
|
|
3253
|
+
content: usageGuide(purpose, scope, options.instructions, authentication),
|
|
3254
|
+
// Explicit rather than derived: the first content line is the zone
|
|
3255
|
+
// scoping rule, which varies per deployment and reads as an instruction
|
|
3256
|
+
// rather than as the routing fact a browsing agent needs.
|
|
3257
|
+
summary: "Zone and account scoping, named-vs-raw routing, two pagination shapes, and lean-vs-raw results.",
|
|
3258
|
+
// Deliberately not `required`. Every named tool's schema is complete
|
|
3259
|
+
// enough to call it correctly on its own, and the scoping convention the
|
|
3260
|
+
// guide carries is repeated on each `zoneId` and `accountId` property —
|
|
3261
|
+
// so forcing the guide into context before every operation would spend
|
|
3262
|
+
// tokens on a sequence the schemas already express.
|
|
3263
|
+
},
|
|
3186
3264
|
...(options.maxResultBytes !== undefined
|
|
3187
3265
|
? { maxResultBytes: options.maxResultBytes }
|
|
3188
3266
|
: {}),
|
|
3189
|
-
tools: buildTools(scope),
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3267
|
+
tools: buildTools(scope, authentication),
|
|
3268
|
+
...(authentication === "apiToken"
|
|
3269
|
+
? {
|
|
3270
|
+
async testCredential(value, ctx) {
|
|
3271
|
+
try {
|
|
3272
|
+
const { result } = await callCloudflare(scope.send, { method: "GET", path: "/user/tokens/verify" }, withAuthentication({
|
|
3273
|
+
...ctx,
|
|
3274
|
+
credential: {
|
|
3275
|
+
get: async () => value,
|
|
3276
|
+
getAll: async () => ({ value }),
|
|
3277
|
+
},
|
|
3278
|
+
}, authentication));
|
|
3279
|
+
const status = asRecord(result)["status"];
|
|
3280
|
+
return status === "active"
|
|
3281
|
+
? { ok: true, message: "Token verified: active." }
|
|
3282
|
+
: { ok: false, message: `Token status is "${String(status)}".` };
|
|
3283
|
+
}
|
|
3284
|
+
catch (error) {
|
|
3285
|
+
return {
|
|
3286
|
+
ok: false,
|
|
3287
|
+
message: error instanceof Error ? error.message : String(error),
|
|
3288
|
+
};
|
|
3289
|
+
}
|
|
3290
|
+
},
|
|
3206
3291
|
}
|
|
3207
|
-
|
|
3292
|
+
: {
|
|
3293
|
+
async testCredentials(values, ctx) {
|
|
3294
|
+
try {
|
|
3295
|
+
const { result } = await callCloudflare(scope.send, { method: "GET", path: "/user" }, withAuthentication({
|
|
3296
|
+
...ctx,
|
|
3297
|
+
credential: {
|
|
3298
|
+
get: async (field) => field ? values[field] ?? null : null,
|
|
3299
|
+
getAll: async () => values,
|
|
3300
|
+
},
|
|
3301
|
+
}, authentication));
|
|
3302
|
+
const email = asRecord(result)["email"];
|
|
3303
|
+
return {
|
|
3304
|
+
ok: true,
|
|
3305
|
+
message: `Global API Key verified for ${String(email)}.`,
|
|
3306
|
+
};
|
|
3307
|
+
}
|
|
3308
|
+
catch (error) {
|
|
3309
|
+
return {
|
|
3310
|
+
ok: false,
|
|
3311
|
+
message: error instanceof Error ? error.message : String(error),
|
|
3312
|
+
};
|
|
3313
|
+
}
|
|
3314
|
+
},
|
|
3315
|
+
}),
|
|
3208
3316
|
});
|
|
3209
3317
|
}
|
|
3210
|
-
//# sourceMappingURL=cloudflare.js.map
|