pi-cloudflare 0.5.4 → 0.6.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/README.md +50 -21
- package/bin/setup.mjs +25 -9
- package/dist/index.d.ts +6 -31
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -161
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.d.ts +2 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +106 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/proxy.d.ts +7 -6
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +3 -2
- package/dist/proxy.js.map +1 -1
- package/dist/runtime.d.ts +52 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +186 -0
- package/dist/runtime.js.map +1 -0
- package/dist/token-store.d.ts +12 -7
- package/dist/token-store.d.ts.map +1 -1
- package/dist/token-store.js +18 -10
- package/dist/token-store.js.map +1 -1
- package/mcp.json +15 -0
- package/package.json +7 -3
- package/plugin.json +20 -0
- package/skills/cloudflare-auth-setup/SKILL.md +24 -18
- package/skills/cloudflare-token-roll/SKILL.md +7 -8
- package/skills/sandbox-migrate-to-next/SKILL.md +1 -1
package/dist/proxy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"
|
|
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;AAEnD,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
|
@@ -11,8 +11,8 @@ function toToolContent(result) {
|
|
|
11
11
|
: { content: output };
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
|
-
* Build
|
|
15
|
-
*
|
|
14
|
+
* Build host-neutral tool registrations for one upstream server. The raw
|
|
15
|
+
* schema is retained for MCP while Pi receives the TypeBox representation.
|
|
16
16
|
*/
|
|
17
17
|
export function buildToolRegistrations(prefix, tools, callUpstream) {
|
|
18
18
|
return tools.map((tool) => {
|
|
@@ -24,6 +24,7 @@ export function buildToolRegistrations(prefix, tools, callUpstream) {
|
|
|
24
24
|
name,
|
|
25
25
|
label: name,
|
|
26
26
|
description,
|
|
27
|
+
inputSchema: tool.inputSchema,
|
|
27
28
|
parameters: Type.Unsafe(tool.inputSchema),
|
|
28
29
|
execute: async (_toolCallId, params) => {
|
|
29
30
|
const result = await callUpstream(tool.name, params ?? {});
|
package/dist/proxy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAyB9B,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,aAAa,CAAC,MAAM,CAAC,CAAA;YAC9B,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,52 @@
|
|
|
1
|
+
import { type PiCloudflareConfig } from './config.js';
|
|
2
|
+
import { type ToolRegistration } from './proxy.js';
|
|
3
|
+
import { type CloudflareServerId } from './servers.js';
|
|
4
|
+
import { type StoredServerTokens } from './token-store.js';
|
|
5
|
+
export interface CloudflareRuntimeOptions {
|
|
6
|
+
/** Shared runtime configuration. Native Pi supplies settings; portable hosts normally use defaults. */
|
|
7
|
+
config?: PiCloudflareConfig;
|
|
8
|
+
/** OAuth token file. Defaults to Pi's legacy ~/.pi/cloudflare-tokens.json for compatibility. */
|
|
9
|
+
tokenFile?: string;
|
|
10
|
+
/** Optional local setup script used in portable re-authentication hints. */
|
|
11
|
+
setupScript?: string;
|
|
12
|
+
/** Warning sink. Native Pi uses console.warn; the stdio server uses stderr. */
|
|
13
|
+
warn?: (message: string) => void;
|
|
14
|
+
}
|
|
15
|
+
/** Exact command that re-authorizes one server; shown to agents, never guessed. */
|
|
16
|
+
export declare function reauthHint(serverId: string, options?: {
|
|
17
|
+
tokenFile?: string;
|
|
18
|
+
setupScript?: string;
|
|
19
|
+
}): string;
|
|
20
|
+
/** True when a failure smells like rejected credentials rather than a broken server. */
|
|
21
|
+
export declare function isAuthFailure(error: unknown): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Adopt rotations performed by sibling native Pi sessions. Kept as a public
|
|
24
|
+
* compatibility helper; portable runtimes use their explicit plugin-data path.
|
|
25
|
+
*/
|
|
26
|
+
export declare function syncFromFile(entry: {
|
|
27
|
+
definition: {
|
|
28
|
+
id: CloudflareServerId;
|
|
29
|
+
};
|
|
30
|
+
stored?: StoredServerTokens;
|
|
31
|
+
}, home?: string): void;
|
|
32
|
+
export declare class CloudflareRuntime {
|
|
33
|
+
private entries;
|
|
34
|
+
private readonly config;
|
|
35
|
+
private readonly tokenFile;
|
|
36
|
+
private readonly setupScript;
|
|
37
|
+
private readonly warn;
|
|
38
|
+
constructor(options?: CloudflareRuntimeOptions);
|
|
39
|
+
private hint;
|
|
40
|
+
private reconnect;
|
|
41
|
+
/** Refresh unconditionally after a credential rejection, persist, and reconnect. */
|
|
42
|
+
private refreshNow;
|
|
43
|
+
/** Refresh an OAuth token that is expired (or nearly so). Static tokens skip refresh. */
|
|
44
|
+
private ensureFreshToken;
|
|
45
|
+
/**
|
|
46
|
+
* Connect enabled Cloudflare servers independently and expose one curated
|
|
47
|
+
* cf_* tool surface. One unavailable server never prevents the others.
|
|
48
|
+
*/
|
|
49
|
+
start(): Promise<ToolRegistration[]>;
|
|
50
|
+
stop(): Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAGA,OAAO,EAAiB,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAEpE,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAA;AACzE,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,cAAc,CAAA;AACrB,OAAO,EAKL,KAAK,kBAAkB,EACxB,MAAM,kBAAkB,CAAA;AAazB,MAAM,WAAW,wBAAwB;IACvC,uGAAuG;IACvG,MAAM,CAAC,EAAE,kBAAkB,CAAA;IAC3B,gGAAgG;IAChG,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,+EAA+E;IAC/E,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC;AAMD,mFAAmF;AACnF,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GACzD,MAAM,CAYR;AAED,wFAAwF;AACxF,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAKrD;AAYD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE;IAAE,UAAU,EAAE;QAAE,EAAE,EAAE,kBAAkB,CAAA;KAAE,CAAC;IAAC,MAAM,CAAC,EAAE,kBAAkB,CAAA;CAAE,EAC9E,IAAI,CAAC,EAAE,MAAM,GACZ,IAAI,CAEN;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;IAChD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA2B;gBAEpC,OAAO,GAAE,wBAA6B;IAOlD,OAAO,CAAC,IAAI;YAOE,SAAS;IAUvB,oFAAoF;YACtE,UAAU;IAgBxB,yFAAyF;YAC3E,gBAAgB;IAe9B;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAiFpC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAI5B"}
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { Type } from 'typebox';
|
|
2
|
+
import { connectServer } from './client.js';
|
|
3
|
+
import { resolveConfig } from './config.js';
|
|
4
|
+
import { refreshAccessToken } from './oauth.js';
|
|
5
|
+
import { buildAllRegistrations } from './proxy.js';
|
|
6
|
+
import { CLOUDFLARE_SERVERS, } from './servers.js';
|
|
7
|
+
import { isExpired, readTokenFileAt, tokenFilePath, writeTokenFileAt, } from './token-store.js';
|
|
8
|
+
function quoteArg(value) {
|
|
9
|
+
return JSON.stringify(value);
|
|
10
|
+
}
|
|
11
|
+
/** Exact command that re-authorizes one server; shown to agents, never guessed. */
|
|
12
|
+
export function reauthHint(serverId, options = {}) {
|
|
13
|
+
const setupCommand = options.tokenFile && options.setupScript
|
|
14
|
+
? `node ${quoteArg(options.setupScript)} --token-file ${quoteArg(options.tokenFile)} --only ${serverId}`
|
|
15
|
+
: `npx -p pi-cloudflare pi-cloudflare-setup --only ${serverId}`;
|
|
16
|
+
return (`${serverId} needs re-authentication (token expired, revoked, or never granted). ` +
|
|
17
|
+
`For temporary access, run in a terminal (approve in the browser when it opens): ` +
|
|
18
|
+
`${setupCommand}. ` +
|
|
19
|
+
`For persistent access across sessions, create an API token instead — ` +
|
|
20
|
+
`see the cloudflare-auth-setup skill (it asks once, then sets up either path).`);
|
|
21
|
+
}
|
|
22
|
+
/** True when a failure smells like rejected credentials rather than a broken server. */
|
|
23
|
+
export function isAuthFailure(error) {
|
|
24
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
25
|
+
return /\b401\b|\b403\b|unauthorized|invalid_token|invalid_grant|token refresh failed|expired|forbidden|authenticate/i.test(message);
|
|
26
|
+
}
|
|
27
|
+
function syncFromTokenFile(entry, path) {
|
|
28
|
+
const latest = readTokenFileAt(path)?.servers[entry.definition.id];
|
|
29
|
+
if (latest && latest.refreshToken !== entry.stored?.refreshToken) {
|
|
30
|
+
entry.stored = latest;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Adopt rotations performed by sibling native Pi sessions. Kept as a public
|
|
35
|
+
* compatibility helper; portable runtimes use their explicit plugin-data path.
|
|
36
|
+
*/
|
|
37
|
+
export function syncFromFile(entry, home) {
|
|
38
|
+
syncFromTokenFile(entry, tokenFilePath(home));
|
|
39
|
+
}
|
|
40
|
+
export class CloudflareRuntime {
|
|
41
|
+
entries = [];
|
|
42
|
+
config;
|
|
43
|
+
tokenFile;
|
|
44
|
+
setupScript;
|
|
45
|
+
warn;
|
|
46
|
+
constructor(options = {}) {
|
|
47
|
+
this.config = options.config ?? {};
|
|
48
|
+
this.tokenFile = options.tokenFile ?? tokenFilePath();
|
|
49
|
+
this.setupScript = options.setupScript;
|
|
50
|
+
this.warn = options.warn ?? (() => undefined);
|
|
51
|
+
}
|
|
52
|
+
hint(serverId) {
|
|
53
|
+
return reauthHint(serverId, {
|
|
54
|
+
tokenFile: this.setupScript ? this.tokenFile : undefined,
|
|
55
|
+
setupScript: this.setupScript,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
async reconnect(entry) {
|
|
59
|
+
await entry.connection?.close().catch(() => undefined);
|
|
60
|
+
const token = entry.staticToken ?? entry.stored?.accessToken;
|
|
61
|
+
entry.connection = await connectServer(entry.definition, {
|
|
62
|
+
apiToken: token,
|
|
63
|
+
timeoutMs: entry.timeoutMs,
|
|
64
|
+
});
|
|
65
|
+
entry.connectedToken = token;
|
|
66
|
+
}
|
|
67
|
+
/** Refresh unconditionally after a credential rejection, persist, and reconnect. */
|
|
68
|
+
async refreshNow(entry) {
|
|
69
|
+
syncFromTokenFile(entry, this.tokenFile);
|
|
70
|
+
const stored = entry.stored;
|
|
71
|
+
if (!stored?.refreshToken)
|
|
72
|
+
throw new Error(`${entry.definition.id}: no refresh token stored`);
|
|
73
|
+
const fresh = await refreshAccessToken({
|
|
74
|
+
tokenEndpoint: stored.tokenEndpoint,
|
|
75
|
+
clientId: stored.clientId,
|
|
76
|
+
refreshToken: stored.refreshToken,
|
|
77
|
+
});
|
|
78
|
+
entry.stored = { ...stored, ...fresh };
|
|
79
|
+
const file = readTokenFileAt(this.tokenFile) ?? { version: 1, servers: {} };
|
|
80
|
+
file.servers[entry.definition.id] = entry.stored;
|
|
81
|
+
writeTokenFileAt(file.servers, this.tokenFile);
|
|
82
|
+
await this.reconnect(entry);
|
|
83
|
+
}
|
|
84
|
+
/** Refresh an OAuth token that is expired (or nearly so). Static tokens skip refresh. */
|
|
85
|
+
async ensureFreshToken(entry) {
|
|
86
|
+
if (entry.staticToken)
|
|
87
|
+
return entry.staticToken;
|
|
88
|
+
syncFromTokenFile(entry, this.tokenFile);
|
|
89
|
+
const stored = entry.stored;
|
|
90
|
+
if (!stored)
|
|
91
|
+
return undefined;
|
|
92
|
+
if (!isExpired(stored) || !stored.refreshToken) {
|
|
93
|
+
if (!entry.connection || entry.connectedToken !== stored.accessToken) {
|
|
94
|
+
await this.reconnect(entry);
|
|
95
|
+
}
|
|
96
|
+
return stored.accessToken;
|
|
97
|
+
}
|
|
98
|
+
await this.refreshNow(entry);
|
|
99
|
+
return entry.stored?.accessToken;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Connect enabled Cloudflare servers independently and expose one curated
|
|
103
|
+
* cf_* tool surface. One unavailable server never prevents the others.
|
|
104
|
+
*/
|
|
105
|
+
async start() {
|
|
106
|
+
await this.stop();
|
|
107
|
+
const registrations = [];
|
|
108
|
+
const config = resolveConfig(this.config);
|
|
109
|
+
const file = readTokenFileAt(this.tokenFile);
|
|
110
|
+
const definitions = CLOUDFLARE_SERVERS.filter((server) => config.enabledServerIds.includes(server.id));
|
|
111
|
+
for (const definition of definitions) {
|
|
112
|
+
const entry = {
|
|
113
|
+
definition,
|
|
114
|
+
timeoutMs: config.connectTimeoutMs,
|
|
115
|
+
staticToken: definition.id === 'api' ? this.config.apiToken : undefined,
|
|
116
|
+
};
|
|
117
|
+
try {
|
|
118
|
+
entry.stored = file?.servers[definition.id];
|
|
119
|
+
await this.ensureFreshToken(entry);
|
|
120
|
+
if (!entry.connection)
|
|
121
|
+
await this.reconnect(entry);
|
|
122
|
+
const tools = (await entry.connection?.listTools()) ?? [];
|
|
123
|
+
const serverRegistrations = buildAllRegistrations([
|
|
124
|
+
{
|
|
125
|
+
prefix: definition.prefix,
|
|
126
|
+
tools,
|
|
127
|
+
callTool: async (name, params) => {
|
|
128
|
+
if (!entry.connection)
|
|
129
|
+
throw new Error(`${definition.id}: not connected`);
|
|
130
|
+
return entry.connection.callTool(name, params);
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
]);
|
|
134
|
+
for (const registration of serverRegistrations) {
|
|
135
|
+
registrations.push({
|
|
136
|
+
...registration,
|
|
137
|
+
execute: async (toolCallId, params, signal) => {
|
|
138
|
+
try {
|
|
139
|
+
await this.ensureFreshToken(entry);
|
|
140
|
+
return await registration.execute(toolCallId, params, signal);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
if (!isAuthFailure(error) || entry.staticToken || !entry.stored?.refreshToken) {
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
await this.refreshNow(entry);
|
|
148
|
+
return await registration.execute(toolCallId, params, signal);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
152
|
+
throw new Error(`${definition.id}: request rejected (${detail}). ${this.hint(definition.id)}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
this.entries.push(entry);
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
this.warn(`${definition.id}: unavailable (${error instanceof Error ? error.message : String(error)}).`);
|
|
162
|
+
if (isAuthFailure(error)) {
|
|
163
|
+
const hint = this.hint(definition.id);
|
|
164
|
+
registrations.push({
|
|
165
|
+
name: `${definition.prefix}reauthenticate`,
|
|
166
|
+
label: `${definition.prefix}reauthenticate`,
|
|
167
|
+
description: `${definition.id} is not authenticated. Call this tool for exact re-authentication instructions.`,
|
|
168
|
+
inputSchema: { type: 'object', properties: {}, additionalProperties: false },
|
|
169
|
+
parameters: Type.Object({}),
|
|
170
|
+
execute: async () => ({
|
|
171
|
+
content: [{ type: 'text', text: hint }],
|
|
172
|
+
}),
|
|
173
|
+
});
|
|
174
|
+
this.warn(hint);
|
|
175
|
+
}
|
|
176
|
+
await entry.connection?.close().catch(() => undefined);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return registrations;
|
|
180
|
+
}
|
|
181
|
+
async stop() {
|
|
182
|
+
await Promise.all(this.entries.map((entry) => entry.connection?.close().catch(() => undefined)));
|
|
183
|
+
this.entries = [];
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
|
package/dist/token-store.d.ts
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
|
-
import type { CloudflareServerId } from './servers.js';
|
|
2
1
|
import type { OAuthTokens } from './oauth.js';
|
|
2
|
+
import type { CloudflareServerId } from './servers.js';
|
|
3
3
|
export interface StoredServerTokens extends OAuthTokens {
|
|
4
4
|
clientId: string;
|
|
5
5
|
tokenEndpoint: string;
|
|
6
6
|
}
|
|
7
|
-
interface TokenFile {
|
|
7
|
+
export interface TokenFile {
|
|
8
8
|
version: 1;
|
|
9
9
|
servers: Partial<Record<CloudflareServerId, StoredServerTokens>>;
|
|
10
10
|
}
|
|
11
11
|
export declare function tokenFilePath(home?: string): string;
|
|
12
|
-
/** Read stored per-server OAuth tokens
|
|
12
|
+
/** Read stored per-server OAuth tokens from an explicit file path. */
|
|
13
|
+
export declare function readTokenFileAt(path: string): TokenFile | undefined;
|
|
14
|
+
/** Read the legacy native-Pi token store. Missing file means not authenticated. */
|
|
13
15
|
export declare function readTokenFile(home?: string): TokenFile | undefined;
|
|
14
16
|
/**
|
|
15
|
-
* Persist per-server tokens with owner-only permissions.
|
|
16
|
-
* Writes atomically
|
|
17
|
-
|
|
17
|
+
* Persist per-server tokens at an explicit path with owner-only permissions.
|
|
18
|
+
* Writes atomically so concurrent agent sessions cannot leave a partial file.
|
|
19
|
+
*/
|
|
20
|
+
export declare function writeTokenFileAt(servers: Partial<Record<CloudflareServerId, StoredServerTokens>>, path: string): void;
|
|
21
|
+
/**
|
|
22
|
+
* Persist per-server tokens with owner-only permissions in native Pi's
|
|
23
|
+
* ~/.pi/cloudflare-tokens.json.
|
|
18
24
|
*/
|
|
19
25
|
export declare function writeTokenFile(servers: Partial<Record<CloudflareServerId, StoredServerTokens>>, home?: string): void;
|
|
20
26
|
export declare function isExpired(tokens: Pick<OAuthTokens, 'expiresAt'>, skewMs?: number): boolean;
|
|
@@ -23,5 +29,4 @@ export declare function partitionServers(ids: CloudflareServerId[], stored: Part
|
|
|
23
29
|
fresh: CloudflareServerId[];
|
|
24
30
|
needed: CloudflareServerId[];
|
|
25
31
|
};
|
|
26
|
-
export {};
|
|
27
32
|
//# sourceMappingURL=token-store.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../src/token-store.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../src/token-store.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAC7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAEtD,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACrD,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,CAAC,CAAA;IACV,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,CAAC,CAAA;CACjE;AAED,wBAAgB,aAAa,CAAC,IAAI,SAAY,GAAG,MAAM,CAEtD;AAED,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CASnE;AAED,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,IAAI,SAAY,GAAG,SAAS,GAAG,SAAS,CAErE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,CAAC,EAChE,IAAI,EAAE,MAAM,GACX,IAAI,CAQN;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,CAAC,EAChE,IAAI,SAAY,GACf,IAAI,CAEN;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,MAAM,SAAS,GAAG,OAAO,CAE1F;AAED,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,kBAAkB,EAAE,EACzB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,CAAC,GAAG,SAAS,GAC1E;IAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAAC,MAAM,EAAE,kBAAkB,EAAE,CAAA;CAAE,CAS/D"}
|
package/dist/token-store.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
|
-
import { join } from 'node:path';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
4
|
export function tokenFilePath(home = homedir()) {
|
|
5
5
|
return join(home, '.pi', 'cloudflare-tokens.json');
|
|
6
6
|
}
|
|
7
|
-
/** Read stored per-server OAuth tokens
|
|
8
|
-
export function
|
|
9
|
-
const path = tokenFilePath(home);
|
|
7
|
+
/** Read stored per-server OAuth tokens from an explicit file path. */
|
|
8
|
+
export function readTokenFileAt(path) {
|
|
10
9
|
if (!existsSync(path))
|
|
11
10
|
return undefined;
|
|
12
11
|
try {
|
|
@@ -19,14 +18,16 @@ export function readTokenFile(home = homedir()) {
|
|
|
19
18
|
return undefined;
|
|
20
19
|
}
|
|
21
20
|
}
|
|
21
|
+
/** Read the legacy native-Pi token store. Missing file means not authenticated. */
|
|
22
|
+
export function readTokenFile(home = homedir()) {
|
|
23
|
+
return readTokenFileAt(tokenFilePath(home));
|
|
24
|
+
}
|
|
22
25
|
/**
|
|
23
|
-
* Persist per-server tokens with owner-only permissions.
|
|
24
|
-
* Writes atomically
|
|
25
|
-
* cannot interleave partial writes into a corrupt token file.
|
|
26
|
+
* Persist per-server tokens at an explicit path with owner-only permissions.
|
|
27
|
+
* Writes atomically so concurrent agent sessions cannot leave a partial file.
|
|
26
28
|
*/
|
|
27
|
-
export function
|
|
28
|
-
|
|
29
|
-
mkdirSync(join(home, '.pi'), { recursive: true });
|
|
29
|
+
export function writeTokenFileAt(servers, path) {
|
|
30
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
30
31
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
31
32
|
writeFileSync(tmp, JSON.stringify({ version: 1, servers }, null, 2) + '\n', {
|
|
32
33
|
mode: 0o600,
|
|
@@ -34,6 +35,13 @@ export function writeTokenFile(servers, home = homedir()) {
|
|
|
34
35
|
chmodSync(tmp, 0o600);
|
|
35
36
|
renameSync(tmp, path);
|
|
36
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Persist per-server tokens with owner-only permissions in native Pi's
|
|
40
|
+
* ~/.pi/cloudflare-tokens.json.
|
|
41
|
+
*/
|
|
42
|
+
export function writeTokenFile(servers, home = homedir()) {
|
|
43
|
+
writeTokenFileAt(servers, tokenFilePath(home));
|
|
44
|
+
}
|
|
37
45
|
export function isExpired(tokens, skewMs = 30_000) {
|
|
38
46
|
return Date.now() + skewMs >= tokens.expiresAt;
|
|
39
47
|
}
|
package/dist/token-store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-store.js","sourceRoot":"","sources":["../src/token-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACnG,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"token-store.js","sourceRoot":"","sources":["../src/token-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACnG,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAezC,MAAM,UAAU,aAAa,CAAC,IAAI,GAAG,OAAO,EAAE;IAC5C,OAAO,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,wBAAwB,CAAC,CAAA;AACpD,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAA;IACvC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAc,CAAA;QAClE,IAAI,MAAM,EAAE,OAAO,KAAK,CAAC,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAA;QACjF,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,aAAa,CAAC,IAAI,GAAG,OAAO,EAAE;IAC5C,OAAO,eAAe,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAA;AAC7C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAgE,EAChE,IAAY;IAEZ,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC7C,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,MAAM,CAAA;IACxC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE;QAC1E,IAAI,EAAE,KAAK;KACZ,CAAC,CAAA;IACF,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IACrB,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAgE,EAChE,IAAI,GAAG,OAAO,EAAE;IAEhB,gBAAgB,CAAC,OAAO,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,CAAA;AAChD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,MAAsC,EAAE,MAAM,GAAG,MAAM;IAC/E,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,IAAI,MAAM,CAAC,SAAS,CAAA;AAChD,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,gBAAgB,CAC9B,GAAyB,EACzB,MAA2E;IAE3E,MAAM,KAAK,GAAyB,EAAE,CAAA;IACtC,MAAM,MAAM,GAAyB,EAAE,CAAA;IACvC,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,EAAE,CAAC,CAAA;QAC1B,IAAI,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;;YACzC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACtB,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;AAC1B,CAAC"}
|
package/mcp.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
|
|
3
|
+
"mcpServers": {
|
|
4
|
+
"cloudflare": {
|
|
5
|
+
"type": "stdio",
|
|
6
|
+
"command": "node",
|
|
7
|
+
"args": ["${PLUGIN_ROOT}/dist/mcp-server.js"],
|
|
8
|
+
"env": {
|
|
9
|
+
"PI_CLOUDFLARE_PLUGIN_ROOT": "${PLUGIN_ROOT}",
|
|
10
|
+
"PI_CLOUDFLARE_TOKEN_FILE": "${PLUGIN_DATA}/cloudflare-tokens.json"
|
|
11
|
+
},
|
|
12
|
+
"cwd": "${PLUGIN_ROOT}"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cloudflare",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Cloudflare Agent Plugin and native Pi extension providing official skills and cf_-prefixed MCP tools.",
|
|
5
5
|
"keywords": [
|
|
6
|
+
"agent-plugin",
|
|
7
|
+
"agent-skills",
|
|
6
8
|
"cloudflare",
|
|
7
9
|
"extension",
|
|
8
10
|
"mcp",
|
|
@@ -23,6 +25,8 @@
|
|
|
23
25
|
"files": [
|
|
24
26
|
"dist",
|
|
25
27
|
"skills",
|
|
28
|
+
"plugin.json",
|
|
29
|
+
"mcp.json",
|
|
26
30
|
"README.md"
|
|
27
31
|
],
|
|
28
32
|
"type": "module",
|
|
@@ -47,7 +51,7 @@
|
|
|
47
51
|
"perf:check": "node scripts/performance-audit.mjs --check",
|
|
48
52
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
49
53
|
"prepublishOnly": "npm run build",
|
|
50
|
-
"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
|
|
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/plugin.test.mjs",
|
|
51
55
|
"pretest": "npm run build"
|
|
52
56
|
},
|
|
53
57
|
"dependencies": {
|
package/plugin.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
|
+
"name": "pi-cloudflare",
|
|
4
|
+
"version": "0.6.0",
|
|
5
|
+
"description": "Cloudflare skills and cf_-prefixed MCP tools, with a native Pi adapter and an Agent Plugins 1.0 portable runtime.",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "0xPlayerOne",
|
|
8
|
+
"url": "https://github.com/0xPlayerOne"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/0xPlayerOne/pi-cloudflare#readme",
|
|
11
|
+
"repository": "https://github.com/0xPlayerOne/pi-cloudflare",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"cloudflare",
|
|
15
|
+
"agent-plugin",
|
|
16
|
+
"mcp",
|
|
17
|
+
"agent-skills",
|
|
18
|
+
"pi"
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -5,14 +5,14 @@ description: Set up Cloudflare authentication from any state. Use before any Clo
|
|
|
5
5
|
|
|
6
6
|
# Cloudflare Auth Setup (Router)
|
|
7
7
|
|
|
8
|
-
This skill runs **before**
|
|
8
|
+
This skill runs **before** Cloudflare work when authentication is not already proven. Never guess at credentials and never loop a rejected auth wall — triage, ask once, then set up exactly one path. The package can run as native Pi or as an Agent Plugin, so never assume a `~/.pi` path unless the host is actually Pi.
|
|
9
9
|
|
|
10
10
|
## Step 0 — What state are we in?
|
|
11
11
|
|
|
12
12
|
Check in order, stop at the first hit:
|
|
13
13
|
|
|
14
|
-
1. **API
|
|
15
|
-
2. **Browser OAuth
|
|
14
|
+
1. **API access already works?** Verify with one cheap `cf_api_*` read. A native Pi host may source this from `pi-cloudflare.apiToken`; an Agent Plugin host may inject `CLOUDFLARE_API_TOKEN` through its own secret/environment mechanism. Do not inspect or print the secret.
|
|
15
|
+
2. **Browser OAuth already works?** Run one cheap tool in the required `cf_<server>_*` namespace. Silent refresh is automatic when stored credentials are usable.
|
|
16
16
|
3. **Otherwise → unauthenticated.** Go to Step 1. This includes expired/revoked tokens and any 401/403/`invalid_grant` from a tool call.
|
|
17
17
|
|
|
18
18
|
## Step 1 — Stop and ask (one question, three options)
|
|
@@ -20,34 +20,40 @@ Check in order, stop at the first hit:
|
|
|
20
20
|
Do not pick for the user. Ask:
|
|
21
21
|
|
|
22
22
|
> Cloudflare isn't authenticated. How should I set it up?
|
|
23
|
-
> 1. **API token (recommended for repeat
|
|
24
|
-
> 2. **Browser OAuth (quickest
|
|
25
|
-
> 3. **I
|
|
23
|
+
> 1. **API token (recommended for repeat automation)** — create a scoped token, store it through this host's secret mechanism, then verify it.
|
|
24
|
+
> 2. **Browser OAuth (quickest)** — run the exact re-authentication command returned by the Cloudflare tool and approve once in a browser.
|
|
25
|
+
> 3. **I already have a token** — enter it locally through the host's secret/environment mechanism, never in chat, then verify it.
|
|
26
26
|
|
|
27
|
-
If the user already answered implicitly (
|
|
27
|
+
If the user already answered implicitly (for example, "use OAuth" or "use a token"), skip the question and proceed while noting the assumption.
|
|
28
28
|
|
|
29
29
|
## Step 2 — Check what this environment can do
|
|
30
30
|
|
|
31
31
|
| Capability | How to tell | If missing |
|
|
32
32
|
|---|---|---|
|
|
33
|
-
| Drive a browser (`browser_*` tools or equivalent) | Tool list contains browser automation | Token creation becomes
|
|
34
|
-
| Open a terminal
|
|
35
|
-
| Existing dashboard session |
|
|
33
|
+
| Drive a browser (`browser_*` tools or equivalent) | Tool list contains browser automation | Token creation becomes guided manual: give exact dashboard clicks from `cloudflare-api-token`. OAuth still works by having the user run the returned setup command in their own terminal. |
|
|
34
|
+
| Open a terminal | Shell execution is available | Otherwise provide the exact local command and let the human run it. Never ask for an OAuth token or API token in chat. |
|
|
35
|
+
| Existing dashboard session | Browser profile is already signed in | Without it, the human completes sign-in/SSO/2FA/passkeys before continuing. |
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
Do not declare authentication impossible merely because browser automation is unavailable; degrade to the manual local flow with precise steps.
|
|
38
38
|
|
|
39
39
|
## Step 3 — Execute exactly one path
|
|
40
40
|
|
|
41
|
-
- **API token** → `cloudflare-api-token`
|
|
42
|
-
- **
|
|
43
|
-
- **
|
|
41
|
+
- **API token** → load `cloudflare-api-token` for scopes and creation. Store the resulting secret according to the active host:
|
|
42
|
+
- **Native Pi:** reference `CLOUDFLARE_API_TOKEN` from `pi-cloudflare.apiToken`; never put the literal in settings.
|
|
43
|
+
- **Agent Plugin:** Agent Plugins 1.0 has no portable secret-reference field. Use the client's secret/environment configuration so the MCP subprocess receives `CLOUDFLARE_API_TOKEN`.
|
|
44
|
+
- **Browser OAuth** → call `cf_<server>_reauthenticate` and run the **exact command returned by that tool**. Native Pi points at its Pi token store; Agent Plugin hosts point at their client-managed `${PLUGIN_DATA}` token file. Do not compose or substitute a token path yourself. The human approves in the browser, then re-verify with a cheap read.
|
|
45
|
+
- **Existing API token** → enter it locally using the same host-specific secret mechanism above. Never paste it into chat or write it to repository files, logs, issues, or docs.
|
|
46
|
+
|
|
47
|
+
On a lost API-token secret later, load `cloudflare-token-roll`. On scope changes, load `cloudflare-token-scopes`.
|
|
44
48
|
|
|
45
49
|
## Step 4 — Prove it before the real task
|
|
46
50
|
|
|
47
|
-
|
|
51
|
+
Run one cheap read in the task's domain (identity or one list call). Only then continue. If it fails, restart triage instead of retry-looping.
|
|
48
52
|
|
|
49
53
|
## Rules
|
|
50
54
|
|
|
51
|
-
- One auth question per session unless the state changes
|
|
52
|
-
- Secrets
|
|
53
|
-
-
|
|
55
|
+
- One auth question per session unless the state changes.
|
|
56
|
+
- Secrets never belong in chat, repository files, issues, logs, or documentation.
|
|
57
|
+
- Native Pi settings hold environment references (`${...}`), never secret literals.
|
|
58
|
+
- Agent Plugin hosts use client-managed secret/environment configuration for static API tokens and `${PLUGIN_DATA}` only for the package-managed OAuth token store.
|
|
59
|
+
- A 403 naming a missing permission group after setup is a scoping task (`cloudflare-token-scopes`), not a reason to retry auth indefinitely.
|
|
@@ -16,19 +16,18 @@ There is no public roll endpoint (`POST /user/tokens/:id/roll` returns `7000 No
|
|
|
16
16
|
|
|
17
17
|
1. My Profile → API Tokens → token row Actions → Roll.
|
|
18
18
|
2. Menu interaction notes: open the Actions menu, then drive the dialog with DOM clicks if a11y clicks stall (dialog buttons sometimes need `querySelector` + `.click()` while a11y clicks dismiss). Confirm only the intended token (dialog names it — read it back before confirming).
|
|
19
|
-
3. The secret modal (`Token rolled successfully` / `Copy your API token now`) holds the **only copy**. Capture `cfut_...` immediately.
|
|
19
|
+
3. The secret modal (`Token rolled successfully` / `Copy your API token now`) holds the **only copy**. Capture `cfut_...` immediately without printing it in chat or logs.
|
|
20
20
|
4. Click Done. Verify the token row still shows Active.
|
|
21
21
|
5. Validate before handoff: `GET /user` (identity), one scoped read per newly relied-upon group (see cloudflare-api-token skill matrix). `GET /user/tokens/:id` confirming policies is safe (never returns the secret).
|
|
22
|
-
6. **Store it
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
- Tell the user to open a new terminal (or `source ~/.zshrc`) and restart Pi sessions so the new environment is picked up.
|
|
28
|
-
7. Destroy temp copies. Report stored + verified **without printing the secret**.
|
|
22
|
+
6. **Store it through the active host's secret mechanism — never put the literal in repository configuration.**
|
|
23
|
+
- **Native Pi:** write `export CLOUDFLARE_API_TOKEN="<secret>"` to `~/.pi/cloudflare-api-token` (mode `0600`); ensure `~/.zshrc` sources it inside the marked idempotent block; set `~/.pi/agent/settings.json` → `pi-cloudflare.apiToken` to the literal reference `${CLOUDFLARE_API_TOKEN}`; preserve every other setting; then verify a fresh shell and one live API read.
|
|
24
|
+
- **Agent Plugin:** Agent Plugins 1.0 has no portable secret-reference field. Store the value with the client's own secret/environment facility so the plugin MCP subprocess receives `CLOUDFLARE_API_TOKEN`. Do not write the API token into `${PLUGIN_DATA}` yourself; that directory is reserved here for the package-managed browser OAuth token store. Restart or reload the plugin if the client only snapshots environment at process launch, then verify one live API read.
|
|
25
|
+
- **Unknown host:** stop before writing. Ask where that client stores MCP environment secrets; never guess a Pi path.
|
|
26
|
+
7. Destroy temporary copies. Report stored + verified **without printing the secret**.
|
|
29
27
|
|
|
30
28
|
## What success looks like
|
|
31
29
|
|
|
32
30
|
- Exactly one active token under the expected name (list and check for duplicates — a failed first attempt often leaves one behind).
|
|
33
31
|
- `modified_on` bumped, old secret rejected (spot-check one call with it only if exposure is suspected; otherwise just rotate wiring).
|
|
32
|
+
- The active host can resolve the new token after a fresh MCP/session start and one live scoped read succeeds.
|
|
34
33
|
- Never retry-loop the confirm dialog. One confirm, one capture.
|
|
@@ -151,7 +151,7 @@ Delete transport settings entirely. Remove session APIs. Isolate users with **se
|
|
|
151
151
|
|
|
152
152
|
### Deploy cutover
|
|
153
153
|
|
|
154
|
-
|
|
154
|
+
Validate a preview deployment first. Production is **one** deploy of matching Worker + image:
|
|
155
155
|
|
|
156
156
|
```sh
|
|
157
157
|
npx wrangler deploy --containers-rollout=immediate
|