pi-cloudflare 0.6.2 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +2 -1
- package/dist/proxy.js.map +1 -1
- package/dist/result-caps.d.ts +23 -0
- package/dist/result-caps.d.ts.map +1 -0
- package/dist/result-caps.js +75 -0
- package/dist/result-caps.js.map +1 -0
- package/package.json +4 -4
- package/plugin.json +1 -1
- package/skills/cloudflare-api-token/SKILL.md +4 -2
- package/skills/cloudflare-token-scopes/SKILL.md +17 -0
package/README.md
CHANGED
|
@@ -22,6 +22,11 @@ helper**, distributed both as a native Pi package and an
|
|
|
22
22
|
| `cf_builds_*` | Workers Builds insights |
|
|
23
23
|
| `cf_obs_*` | Workers logs/metrics/traces |
|
|
24
24
|
|
|
25
|
+
Large upstream text results (e.g. full Worker bundles) are truncated to
|
|
26
|
+
32 KiB per text block with a recovery hint instead of landing verbatim in
|
|
27
|
+
agent context. Override locally with `PI_CLOUDFLARE_MAX_TEXT_BYTES` when a
|
|
28
|
+
task genuinely needs more.
|
|
29
|
+
|
|
25
30
|
Requires Node 20.19+ (or 22.12+) and network access. Native Pi installation
|
|
26
31
|
also requires Pi package support. No browser, wrangler CLI, or API token is
|
|
27
32
|
needed to install; the docs tools work immediately after install.
|
|
@@ -86,6 +91,7 @@ In `~/.pi/agent/settings.json`:
|
|
|
86
91
|
"pi-cloudflare": {
|
|
87
92
|
"servers": { "builds": false }, // disable individual servers
|
|
88
93
|
"connectTimeoutMs": 30000,
|
|
94
|
+
// Env override also available: PI_CLOUDFLARE_MAX_TEXT_BYTES (default 32768)
|
|
89
95
|
},
|
|
90
96
|
}
|
|
91
97
|
```
|
package/dist/proxy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AAGxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AAGxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAGnD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACpC,0DAA0D;IAC1D,UAAU,EAAE,OAAO,CAAA;IACnB,OAAO,EAAE,CACP,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,MAAM,CAAC,EAAE,WAAW,KACjB,OAAO,CAAC,cAAc,CAAC,CAAA;CAC7B;AAED,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACrC;AAeD;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,YAAY,EAAE,EACrB,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,GAChF,gBAAgB,EAAE,CAkBpB;AAED,iFAAiF;AACjF,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,KAAK,CAAC;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,YAAY,EAAE,CAAA;IACrB,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAA;CACvC,CAAC,GACD,gBAAgB,EAAE,CAIpB"}
|
package/dist/proxy.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Type } from 'typebox';
|
|
2
|
+
import { capToolResultText } from './result-caps.js';
|
|
2
3
|
function toToolContent(result) {
|
|
3
4
|
if (typeof result !== 'object' || result === null) {
|
|
4
5
|
return { content: [{ type: 'text', text: String(result) }] };
|
|
@@ -28,7 +29,7 @@ export function buildToolRegistrations(prefix, tools, callUpstream) {
|
|
|
28
29
|
parameters: Type.Unsafe(tool.inputSchema),
|
|
29
30
|
execute: async (_toolCallId, params) => {
|
|
30
31
|
const result = await callUpstream(tool.name, params ?? {});
|
|
31
|
-
return toToolContent(result);
|
|
32
|
+
return capToolResultText(name, toToolContent(result));
|
|
32
33
|
},
|
|
33
34
|
};
|
|
34
35
|
});
|
package/dist/proxy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAG9B,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAuBpD,SAAS,aAAa,CAAC,MAAe;IACpC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QAClD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAA;IAC9D,CAAC;IACD,MAAM,MAAM,GAAG,MAAkD,CAAA;IACjE,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAE,MAAM,CAAC,OAAqC,CAAC,CAAC,CAAC,EAAE,CAAA;IAClG,MAAM,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAA+B,CAAA;IAC5F,OAAO,OAAO,MAAM,CAAC,OAAO,KAAK,SAAS,IAAI,MAAM,CAAC,OAAO;QAC1D,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QACpC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAA;AACzB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAc,EACd,KAAqB,EACrB,YAAiF;IAEjF,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACxB,MAAM,IAAI,GAAG,GAAG,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;QACpC,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE;YAC1C,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,WAAW,EAAE;YACrD,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAA;QAChD,OAAO;YACL,IAAI;YACJ,KAAK,EAAE,IAAI;YACX,WAAW;YACX,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC;YACzC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE;gBACrC,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,IAAI,EAAE,CAAC,CAAA;gBAC1D,OAAO,iBAAiB,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;YACvD,CAAC;SACF,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,qBAAqB,CACnC,WAIE;IAEF,OAAO,WAAW,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,EAAE,CACxC,sBAAsB,CAAC,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,QAAQ,CAAC,CACjF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Guardrail against unbounded upstream MCP text results landing verbatim in
|
|
4
|
+
* agent context. Provenance: one `cf_bindings_workers_get_worker_code` call
|
|
5
|
+
* returned the entire deployed Worker bundle as multipart text (~6 MB), and
|
|
6
|
+
* the agent fetched it twice — 12 MB of session JSONL for a metadata
|
|
7
|
+
* question. Every proxied tool result passes through here.
|
|
8
|
+
*
|
|
9
|
+
* Text blocks larger than the cap are replaced with a bounded preview plus
|
|
10
|
+
* an exact recovery hint (which tool was called, total vs returned bytes).
|
|
11
|
+
* Errors and non-text content pass through untouched.
|
|
12
|
+
*/
|
|
13
|
+
export declare const DEFAULT_MAX_TEXT_BYTES = 32768;
|
|
14
|
+
export declare const MAX_TEXT_BYTES_ENV = "PI_CLOUDFLARE_MAX_TEXT_BYTES";
|
|
15
|
+
/**
|
|
16
|
+
* Cap every text block of a proxied `CallToolResult`. Returns the original
|
|
17
|
+
* object when nothing exceeds the cap so small results stay referentially
|
|
18
|
+
* identical.
|
|
19
|
+
*/
|
|
20
|
+
export declare function capToolResultText(toolName: string, result: CallToolResult, maxBytes?: number): CallToolResult;
|
|
21
|
+
/** Resolve the effective per-text-block cap (exported for tests and docs). */
|
|
22
|
+
export declare function maxTextBytes(): number;
|
|
23
|
+
//# sourceMappingURL=result-caps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-caps.d.ts","sourceRoot":"","sources":["../src/result-caps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AAExE;;;;;;;;;;GAUG;AAEH,eAAO,MAAM,sBAAsB,QAAS,CAAA;AAC5C,eAAO,MAAM,kBAAkB,iCAAiC,CAAA;AA4ChE;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,cAAc,EACtB,QAAQ,GAAE,MAA6B,GACtC,cAAc,CAchB;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,IAAI,MAAM,CAErC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guardrail against unbounded upstream MCP text results landing verbatim in
|
|
3
|
+
* agent context. Provenance: one `cf_bindings_workers_get_worker_code` call
|
|
4
|
+
* returned the entire deployed Worker bundle as multipart text (~6 MB), and
|
|
5
|
+
* the agent fetched it twice — 12 MB of session JSONL for a metadata
|
|
6
|
+
* question. Every proxied tool result passes through here.
|
|
7
|
+
*
|
|
8
|
+
* Text blocks larger than the cap are replaced with a bounded preview plus
|
|
9
|
+
* an exact recovery hint (which tool was called, total vs returned bytes).
|
|
10
|
+
* Errors and non-text content pass through untouched.
|
|
11
|
+
*/
|
|
12
|
+
export const DEFAULT_MAX_TEXT_BYTES = 32_768;
|
|
13
|
+
export const MAX_TEXT_BYTES_ENV = 'PI_CLOUDFLARE_MAX_TEXT_BYTES';
|
|
14
|
+
const TRUNCATION_SUFFIX_RESERVE = 1024;
|
|
15
|
+
function configuredMaxBytes() {
|
|
16
|
+
const raw = process.env[MAX_TEXT_BYTES_ENV];
|
|
17
|
+
if (!raw)
|
|
18
|
+
return DEFAULT_MAX_TEXT_BYTES;
|
|
19
|
+
const parsed = Number(raw);
|
|
20
|
+
if (!Number.isInteger(parsed) || parsed <= 0)
|
|
21
|
+
return DEFAULT_MAX_TEXT_BYTES;
|
|
22
|
+
return parsed;
|
|
23
|
+
}
|
|
24
|
+
function byteLength(text) {
|
|
25
|
+
return Buffer.byteLength(text, 'utf8');
|
|
26
|
+
}
|
|
27
|
+
function truncateUtf8(text, maxBytes) {
|
|
28
|
+
if (byteLength(text) <= maxBytes)
|
|
29
|
+
return text;
|
|
30
|
+
const buffer = Buffer.from(text, 'utf8').subarray(0, maxBytes);
|
|
31
|
+
// Avoid emitting a half-decoded trailing character.
|
|
32
|
+
return buffer.toString('utf8').replace(/\uFFFD+$/, '');
|
|
33
|
+
}
|
|
34
|
+
function truncationNotice(toolName, totalBytes, returnedBytes) {
|
|
35
|
+
return (`\n\n[pi-cloudflare] TRUNCATED: '${toolName}' returned ${totalBytes} bytes of text; ` +
|
|
36
|
+
`showing the first ${returnedBytes} bytes to protect agent context. ` +
|
|
37
|
+
`Re-run with a narrower query, a specific module/path argument, or pagination ` +
|
|
38
|
+
`instead of fetching the full payload again. ` +
|
|
39
|
+
`Override locally with ${MAX_TEXT_BYTES_ENV} if you genuinely need more.`);
|
|
40
|
+
}
|
|
41
|
+
function isTextBlock(block) {
|
|
42
|
+
return (!!block &&
|
|
43
|
+
typeof block === 'object' &&
|
|
44
|
+
block.type === 'text' &&
|
|
45
|
+
typeof block.text === 'string');
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Cap every text block of a proxied `CallToolResult`. Returns the original
|
|
49
|
+
* object when nothing exceeds the cap so small results stay referentially
|
|
50
|
+
* identical.
|
|
51
|
+
*/
|
|
52
|
+
export function capToolResultText(toolName, result, maxBytes = configuredMaxBytes()) {
|
|
53
|
+
if (!result || !Array.isArray(result.content))
|
|
54
|
+
return result;
|
|
55
|
+
let truncated = false;
|
|
56
|
+
const content = result.content.map((block) => {
|
|
57
|
+
if (!isTextBlock(block))
|
|
58
|
+
return block;
|
|
59
|
+
const total = byteLength(block.text);
|
|
60
|
+
if (total <= maxBytes)
|
|
61
|
+
return block;
|
|
62
|
+
truncated = true;
|
|
63
|
+
const notice = truncationNotice(toolName, total, maxBytes);
|
|
64
|
+
const previewBytes = Math.max(0, maxBytes - byteLength(notice) - TRUNCATION_SUFFIX_RESERVE);
|
|
65
|
+
return { ...block, text: `${truncateUtf8(block.text, previewBytes)}${notice}` };
|
|
66
|
+
});
|
|
67
|
+
if (!truncated)
|
|
68
|
+
return result;
|
|
69
|
+
return { ...result, content };
|
|
70
|
+
}
|
|
71
|
+
/** Resolve the effective per-text-block cap (exported for tests and docs). */
|
|
72
|
+
export function maxTextBytes() {
|
|
73
|
+
return configuredMaxBytes();
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=result-caps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-caps.js","sourceRoot":"","sources":["../src/result-caps.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cloudflare",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
4
4
|
"description": "Cloudflare Agent Plugin and native Pi extension providing official skills and cf_-prefixed MCP tools.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-plugin",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"perf:check": "node scripts/performance-audit.mjs --check",
|
|
52
52
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
53
53
|
"prepublishOnly": "npm run build",
|
|
54
|
-
"test": "node --test test/config.test.mjs test/servers.test.mjs test/proxy.test.mjs test/oauth.test.mjs test/token-store.test.mjs test/auth-recovery.test.mjs test/api-token.test.mjs test/plugin.test.mjs",
|
|
54
|
+
"test": "node --test test/config.test.mjs test/servers.test.mjs test/proxy.test.mjs test/oauth.test.mjs test/token-store.test.mjs test/auth-recovery.test.mjs test/api-token.test.mjs test/result-caps.test.mjs test/plugin.test.mjs",
|
|
55
55
|
"pretest": "npm run build"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
@@ -62,10 +62,10 @@
|
|
|
62
62
|
"@types/node": "^22.0.0",
|
|
63
63
|
"oxfmt": "^0.66.0",
|
|
64
64
|
"oxlint": "^1.81.0",
|
|
65
|
-
"typescript": "^
|
|
65
|
+
"typescript": "^6.0.3"
|
|
66
66
|
},
|
|
67
67
|
"engines": {
|
|
68
|
-
"node": "
|
|
68
|
+
"node": ">=24"
|
|
69
69
|
},
|
|
70
70
|
"pi": {
|
|
71
71
|
"extensions": [
|
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "pi-cloudflare",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.4",
|
|
5
5
|
"description": "Cloudflare skills and cf_-prefixed MCP tools, with a native Pi adapter and an Agent Plugins 1.0 portable runtime.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "0xPlayerOne",
|
|
@@ -57,7 +57,9 @@ Same-origin `/api/v4/*` in page context carries the session:
|
|
|
57
57
|
## After creation: validation matrix (live-proven 2026-09-06)
|
|
58
58
|
|
|
59
59
|
- Run `CLOUDFLARE_API_TOKEN=<paste-once> node scripts/verify-api-token.mjs` (identity, token status, Workers scripts read).
|
|
60
|
-
- Then one live read per relied-upon group, all passing on the reference token: `GET /user`, `GET /accounts`, Workers scripts list, KV namespaces list, R2 buckets list, D1 databases list, Hyperdrive configs list, zones list, zone routes list, account read, memberships list, audit-logs list (proves Logs Read).
|
|
61
|
-
-
|
|
60
|
+
- Then one live read per relied-upon group, all passing on the reference token: `GET /user`, `GET /accounts`, Workers scripts list, KV namespaces list, R2 buckets list, D1 databases list, Hyperdrive configs list, zones list, zone routes list, account read, memberships list, audit-logs list (proves Logs Read), `GET /accounts/{id}/workers/observability/usage` with millisecond-epoch `from`/`to` (proves Workers Observability Read — live-proven 2026-09-10 on both accounts).
|
|
61
|
+
- Zone Cache Rules Edit (only when granted): `GET /zones/:id/rulesets/phases/http_request_cache_settings/entrypoint` — expect `200` with the entrypoint, or `10003` (no entrypoint yet) which also proves the group is present. Live-proven 2026-09-09: created zone rule `landing-marketplace-edge-300s` (`http.host eq "pinkbinder.shop" and starts_with(http.request.uri.path, "/api/marketplace")`, edge+browser TTL 300s override-origin) after adding this group.
|
|
62
|
+
- Known non-gaps, do not chase: `GET /user/tokens/:id` returns `9109` by design (tokens cannot manage tokens); zone-analytics `10000` without the Zone Analytics group is correct least privilege (observability rollups go through the separate obs MCP, not this token); Workers Tail needs a websocket (group presence + dashboard UI is the check); Logpush jobs `401` (no Logpush group exposed to API tokens — dashboard only, verified 2026-09-10).
|
|
63
|
+
- Workers Builds (live-proven 2026-09-07): `GET /accounts/{id}/builds/builds/{uuid}` plus `/logs` read; trigger PATCH and build-token DELETE verified with the Edit level.
|
|
62
64
|
- Template drift is real: the dashboard template grows over time (Builds/Agents/Containers/Observability/Pages appeared after the doc was written). Enumerate required groups per task from `docs/api-token.md`; never assume the template equals the need.
|
|
63
65
|
- A 403 naming a missing group later means extend-then-verify, not a new token: same flow, one more row.
|
|
@@ -26,6 +26,23 @@ Prefer updating in place: the secret survives, wiring stays intact, and the chan
|
|
|
26
26
|
- Same-origin `/api/v4/*` in page context carries the session (persistent browser, headed for dashboard pages). `Content-Type: application/json` required.
|
|
27
27
|
- Verify after: re-GET the token and diff policies, then exercise one live read per changed group. A 403 afterward means the group mapping was wrong, not the mechanism.
|
|
28
28
|
|
|
29
|
+
## When you cannot self-serve: hand off with the exact spec
|
|
30
|
+
|
|
31
|
+
PUT needs a token that can manage tokens (API tokens get `9109` by design),
|
|
32
|
+
and the dashboard path needs a headed browser plus a human for Turnstile.
|
|
33
|
+
When neither is available, do not guess — hand the human the exact row to
|
|
34
|
+
add:
|
|
35
|
+
|
|
36
|
+
- Derive the group from the failing call, not from memory: the tool plus
|
|
37
|
+
endpoint determines it (e.g. `/accounts/{id}/builds/*` → Workers Builds
|
|
38
|
+
Configuration).
|
|
39
|
+
- Derive the level from the HTTP method: GET → Read, anything else → Edit.
|
|
40
|
+
- Include the scope: account-scoped groups need every managed account
|
|
41
|
+
included under Account Resources.
|
|
42
|
+
- Format it as dashboard rows so the human pastes without translating:
|
|
43
|
+
`Account | Workers Builds Configuration | Edit`, one line per row, exact
|
|
44
|
+
labels.
|
|
45
|
+
|
|
29
46
|
## When update is the wrong tool
|
|
30
47
|
|
|
31
48
|
- Secret lost or leaked → cloudflare-token-roll (no public roll route; UI flow).
|