nomoreide 0.1.101 → 0.1.102
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/dist/core/agent-profiles/types.d.ts +3 -3
- package/dist/core/cloudflare-actions.d.ts +90 -0
- package/dist/core/cloudflare-actions.js +178 -0
- package/dist/core/cloudflare-actions.js.map +1 -0
- package/dist/core/cloudflare-auth.d.ts +61 -0
- package/dist/core/cloudflare-auth.js +149 -0
- package/dist/core/cloudflare-auth.js.map +1 -0
- package/dist/core/cloudflare-context.d.ts +2 -0
- package/dist/core/cloudflare-context.js +106 -0
- package/dist/core/cloudflare-context.js.map +1 -0
- package/dist/core/cloudflare-manager.d.ts +256 -0
- package/dist/core/cloudflare-manager.js +409 -0
- package/dist/core/cloudflare-manager.js.map +1 -0
- package/dist/core/cloudflare-provider.d.ts +64 -0
- package/dist/core/cloudflare-provider.js +343 -0
- package/dist/core/cloudflare-provider.js.map +1 -0
- package/dist/core/config-store.d.ts +14 -14
- package/dist/core/config-store.js +129 -42
- package/dist/core/config-store.js.map +1 -1
- package/dist/core/context-library.js +105 -10
- package/dist/core/context-library.js.map +1 -1
- package/dist/core/extensions.d.ts +78 -0
- package/dist/core/extensions.js +41 -0
- package/dist/core/extensions.js.map +1 -0
- package/dist/core/log-store.d.ts +10 -0
- package/dist/core/log-store.js +10 -1
- package/dist/core/log-store.js.map +1 -1
- package/dist/core/log-volume.d.ts +41 -0
- package/dist/core/log-volume.js +81 -0
- package/dist/core/log-volume.js.map +1 -0
- package/dist/core/project-overview.d.ts +2 -2
- package/dist/core/project-overview.js +3 -3
- package/dist/core/project-overview.js.map +1 -1
- package/dist/core/providers/credentials.d.ts +93 -0
- package/dist/core/providers/credentials.js +88 -0
- package/dist/core/providers/credentials.js.map +1 -0
- package/dist/core/providers/deploy-provider.d.ts +327 -0
- package/dist/core/providers/deploy-provider.js +25 -0
- package/dist/core/providers/deploy-provider.js.map +1 -0
- package/dist/core/providers/egress.d.ts +58 -0
- package/dist/core/providers/egress.js +115 -0
- package/dist/core/providers/egress.js.map +1 -0
- package/dist/core/providers/host-bridge.d.ts +20 -0
- package/dist/core/providers/host-bridge.js +57 -0
- package/dist/core/providers/host-bridge.js.map +1 -0
- package/dist/core/providers/host-provider.d.ts +160 -0
- package/dist/core/providers/host-provider.js +2 -0
- package/dist/core/providers/host-provider.js.map +1 -0
- package/dist/core/providers/oauth.d.ts +97 -0
- package/dist/core/providers/oauth.js +238 -0
- package/dist/core/providers/oauth.js.map +1 -0
- package/dist/core/providers/project-resolution.d.ts +81 -0
- package/dist/core/providers/project-resolution.js +101 -0
- package/dist/core/providers/project-resolution.js.map +1 -0
- package/dist/core/providers/registry.d.ts +73 -0
- package/dist/core/providers/registry.js +53 -0
- package/dist/core/providers/registry.js.map +1 -0
- package/dist/core/providers/strings.d.ts +95 -0
- package/dist/core/providers/strings.js +59 -0
- package/dist/core/providers/strings.js.map +1 -0
- package/dist/core/repo-onboard.d.ts +2 -2
- package/dist/core/ssh-servers.d.ts +16 -1
- package/dist/core/ssh-servers.js +23 -8
- package/dist/core/ssh-servers.js.map +1 -1
- package/dist/core/types.d.ts +25 -14
- package/dist/core/vercel-auth.d.ts +18 -36
- package/dist/core/vercel-auth.js +33 -75
- package/dist/core/vercel-auth.js.map +1 -1
- package/dist/core/vercel-context.d.ts +2 -25
- package/dist/core/vercel-context.js +73 -119
- package/dist/core/vercel-context.js.map +1 -1
- package/dist/core/vercel-manager.d.ts +13 -0
- package/dist/core/vercel-manager.js +3 -5
- package/dist/core/vercel-manager.js.map +1 -1
- package/dist/core/vercel-oauth.d.ts +24 -62
- package/dist/core/vercel-oauth.js +27 -199
- package/dist/core/vercel-oauth.js.map +1 -1
- package/dist/core/vercel-provider.d.ts +23 -0
- package/dist/core/vercel-provider.js +271 -0
- package/dist/core/vercel-provider.js.map +1 -0
- package/dist/core/vultr-actions.d.ts +52 -0
- package/dist/core/vultr-actions.js +62 -0
- package/dist/core/vultr-actions.js.map +1 -0
- package/dist/core/vultr-auth.d.ts +32 -0
- package/dist/core/vultr-auth.js +54 -0
- package/dist/core/vultr-auth.js.map +1 -0
- package/dist/core/vultr-context.d.ts +2 -0
- package/dist/core/vultr-context.js +55 -0
- package/dist/core/vultr-context.js.map +1 -0
- package/dist/core/vultr-manager.d.ts +105 -0
- package/dist/core/vultr-manager.js +159 -0
- package/dist/core/vultr-manager.js.map +1 -0
- package/dist/core/vultr-provider.d.ts +69 -0
- package/dist/core/vultr-provider.js +224 -0
- package/dist/core/vultr-provider.js.map +1 -0
- package/dist/mcp/tools/docs.js +1 -1
- package/dist/mcp/tools/docs.js.map +1 -1
- package/dist/mcp/tools/index.d.ts +1 -1
- package/dist/mcp/tools/index.js +3 -3
- package/dist/mcp/tools/index.js.map +1 -1
- package/dist/mcp/tools/provider.d.ts +16 -0
- package/dist/mcp/tools/provider.js +110 -0
- package/dist/mcp/tools/provider.js.map +1 -0
- package/dist/web/client/assets/{code-editor-rHWIXkKg.js → code-editor-gBOtcWXO.js} +1 -1
- package/dist/web/client/assets/context-graph-CTQ1LK6k.js +7 -0
- package/dist/web/client/assets/index-D_Lc5VHW.css +1 -0
- package/dist/web/client/assets/index-eKrMwpX9.js +352 -0
- package/dist/web/client/assets/{system-process-table-S4umMSEK.js → system-process-table-DOHmN7d0.js} +1 -1
- package/dist/web/client/index.html +2 -2
- package/dist/web/dashboard.d.ts +21 -2
- package/dist/web/dashboard.js +48 -4
- package/dist/web/dashboard.js.map +1 -1
- package/dist/web/routes/extensions-routes.d.ts +14 -0
- package/dist/web/routes/extensions-routes.js +21 -0
- package/dist/web/routes/extensions-routes.js.map +1 -0
- package/dist/web/routes/host-routes.d.ts +2 -0
- package/dist/web/routes/host-routes.js +188 -0
- package/dist/web/routes/host-routes.js.map +1 -0
- package/dist/web/routes/index.js +6 -2
- package/dist/web/routes/index.js.map +1 -1
- package/dist/web/routes/metrics-routes.js +19 -2
- package/dist/web/routes/metrics-routes.js.map +1 -1
- package/dist/web/routes/provider-routes.d.ts +2 -0
- package/dist/web/routes/provider-routes.js +558 -0
- package/dist/web/routes/provider-routes.js.map +1 -0
- package/dist/web/routes/shell-routes.d.ts +11 -0
- package/dist/web/routes/shell-routes.js +23 -3
- package/dist/web/routes/shell-routes.js.map +1 -1
- package/dist/web/routes/ssh-server-routes.d.ts +8 -1
- package/dist/web/routes/ssh-server-routes.js +15 -5
- package/dist/web/routes/ssh-server-routes.js.map +1 -1
- package/package.json +1 -1
- package/dist/mcp/tools/vercel.d.ts +0 -12
- package/dist/mcp/tools/vercel.js +0 -103
- package/dist/mcp/tools/vercel.js.map +0 -1
- package/dist/web/client/assets/context-graph-CcfJNAob.js +0 -7
- package/dist/web/client/assets/index-BnFtcUqj.js +0 -352
- package/dist/web/client/assets/index-CqsghUD9.css +0 -1
- package/dist/web/routes/vercel-routes.d.ts +0 -2
- package/dist/web/routes/vercel-routes.js +0 -476
- package/dist/web/routes/vercel-routes.js.map +0 -1
|
@@ -283,8 +283,8 @@ export declare const credentialSpecSchema: z.ZodObject<{
|
|
|
283
283
|
required: z.ZodDefault<z.ZodBoolean>;
|
|
284
284
|
description: z.ZodOptional<z.ZodString>;
|
|
285
285
|
}, "strip", z.ZodTypeAny, {
|
|
286
|
-
key: string;
|
|
287
286
|
required: boolean;
|
|
287
|
+
key: string;
|
|
288
288
|
description?: string | undefined;
|
|
289
289
|
}, {
|
|
290
290
|
key: string;
|
|
@@ -311,8 +311,8 @@ export declare const profileManifestSchema: z.ZodObject<{
|
|
|
311
311
|
required: z.ZodDefault<z.ZodBoolean>;
|
|
312
312
|
description: z.ZodOptional<z.ZodString>;
|
|
313
313
|
}, "strip", z.ZodTypeAny, {
|
|
314
|
-
key: string;
|
|
315
314
|
required: boolean;
|
|
315
|
+
key: string;
|
|
316
316
|
description?: string | undefined;
|
|
317
317
|
}, {
|
|
318
318
|
key: string;
|
|
@@ -327,8 +327,8 @@ export declare const profileManifestSchema: z.ZodObject<{
|
|
|
327
327
|
tool: string;
|
|
328
328
|
} | undefined;
|
|
329
329
|
credentials?: {
|
|
330
|
-
key: string;
|
|
331
330
|
required: boolean;
|
|
331
|
+
key: string;
|
|
332
332
|
description?: string | undefined;
|
|
333
333
|
}[] | undefined;
|
|
334
334
|
}, {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { type CloudflareEnvVar, type CloudflareRequestAuth } from "./cloudflare-manager.js";
|
|
2
|
+
/**
|
|
3
|
+
* Write-capable Cloudflare Pages operations, deliberately separate from the
|
|
4
|
+
* read-safe {@link ./cloudflare-manager.js CloudflareManager} — the same split
|
|
5
|
+
* as `git-manager` / `git-actions` and `db-peek` / `db-write`.
|
|
6
|
+
*
|
|
7
|
+
* Everything here changes what the internet is serving, so none of it is
|
|
8
|
+
* exposed as an MCP tool: these are reached only from the dashboard's own
|
|
9
|
+
* routes, where a human clicked the button.
|
|
10
|
+
*
|
|
11
|
+
* Still intentionally excludes the irreversible ones — Cloudflare's
|
|
12
|
+
* `DELETE .../deployments/{id}` and project deletion — which would need their
|
|
13
|
+
* own guarded surface.
|
|
14
|
+
*/
|
|
15
|
+
/** The only two environments Pages has. A third would be silently created by a PATCH. */
|
|
16
|
+
export declare const CLOUDFLARE_ENVIRONMENTS: readonly ["production", "preview"];
|
|
17
|
+
export type CloudflareEnvironment = (typeof CLOUDFLARE_ENVIRONMENTS)[number];
|
|
18
|
+
export declare class CloudflareActions {
|
|
19
|
+
private readonly auth;
|
|
20
|
+
private readonly accountId;
|
|
21
|
+
constructor(auth: CloudflareRequestAuth, accountId: string);
|
|
22
|
+
/**
|
|
23
|
+
* Rebuild an existing deployment.
|
|
24
|
+
*
|
|
25
|
+
* Cloudflare calls this "retry" and mints a new deployment id, inheriting the
|
|
26
|
+
* original's branch, commit and environment — so, unlike Vercel's redeploy,
|
|
27
|
+
* nothing has to be carried through by the caller to keep a production retry
|
|
28
|
+
* in production.
|
|
29
|
+
*/
|
|
30
|
+
retry(projectName: string, deploymentId: string): Promise<{
|
|
31
|
+
id: string;
|
|
32
|
+
url: string | null;
|
|
33
|
+
}>;
|
|
34
|
+
/**
|
|
35
|
+
* Point the project's production alias at an earlier deployment without
|
|
36
|
+
* rebuilding it. Cloudflare records this as a rollback, and it takes effect
|
|
37
|
+
* immediately.
|
|
38
|
+
*/
|
|
39
|
+
rollback(projectName: string, deploymentId: string): Promise<{
|
|
40
|
+
id: string;
|
|
41
|
+
url: string | null;
|
|
42
|
+
}>;
|
|
43
|
+
/**
|
|
44
|
+
* Add a variable. `secret` (Vercel's "encrypted") is the default, and its
|
|
45
|
+
* value never reads back over the API afterwards.
|
|
46
|
+
*/
|
|
47
|
+
createEnv(projectName: string, input: {
|
|
48
|
+
key: string;
|
|
49
|
+
value: string;
|
|
50
|
+
environments: string[];
|
|
51
|
+
secret?: boolean;
|
|
52
|
+
}): Promise<CloudflareEnvVar>;
|
|
53
|
+
/**
|
|
54
|
+
* Change a variable's value and/or the environments it applies to.
|
|
55
|
+
*
|
|
56
|
+
* Adding an environment needs a value: Cloudflare stores the variable
|
|
57
|
+
* separately per environment and never hands back a secret's value, so there
|
|
58
|
+
* is nothing to copy across. Saying that is better than writing an empty
|
|
59
|
+
* string into the new environment.
|
|
60
|
+
*/
|
|
61
|
+
updateEnv(projectName: string, key: string, input: {
|
|
62
|
+
value?: string;
|
|
63
|
+
environments?: string[];
|
|
64
|
+
}): Promise<CloudflareEnvVar>;
|
|
65
|
+
/** Remove a variable from every environment it is set in. */
|
|
66
|
+
deleteEnv(projectName: string, key: string): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* The one write to a project's variables.
|
|
69
|
+
*
|
|
70
|
+
* Cloudflare merges the `env_vars` map it is sent — a key set to `null` is
|
|
71
|
+
* deleted, and keys the patch does not mention are untouched — so this never
|
|
72
|
+
* has to read-modify-write the whole map and cannot clobber a variable added
|
|
73
|
+
* between the read and the write.
|
|
74
|
+
*/
|
|
75
|
+
private patchEnv;
|
|
76
|
+
private patchProject;
|
|
77
|
+
private currentEnv;
|
|
78
|
+
private projectPath;
|
|
79
|
+
private request;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Cloudflare's action vocabulary, mapped onto the names the shared UI already
|
|
83
|
+
* knows. "retry" is Cloudflare's word for what Vercel calls a redeploy, and
|
|
84
|
+
* there is no cancel endpoint for a Pages build at all — so the manifest is
|
|
85
|
+
* two actions, not four, and the deployment row hides what it cannot do.
|
|
86
|
+
*/
|
|
87
|
+
export declare const CLOUDFLARE_ACTIONS: readonly ["redeploy", "rollback"];
|
|
88
|
+
export type CloudflareActionName = (typeof CLOUDFLARE_ACTIONS)[number];
|
|
89
|
+
/** Actions that change what production serves — the UI confirms these first. */
|
|
90
|
+
export declare const PRODUCTION_AFFECTING_ACTIONS: ReadonlySet<CloudflareActionName>;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { cloudflareRequest, } from "./cloudflare-manager.js";
|
|
2
|
+
/**
|
|
3
|
+
* Write-capable Cloudflare Pages operations, deliberately separate from the
|
|
4
|
+
* read-safe {@link ./cloudflare-manager.js CloudflareManager} — the same split
|
|
5
|
+
* as `git-manager` / `git-actions` and `db-peek` / `db-write`.
|
|
6
|
+
*
|
|
7
|
+
* Everything here changes what the internet is serving, so none of it is
|
|
8
|
+
* exposed as an MCP tool: these are reached only from the dashboard's own
|
|
9
|
+
* routes, where a human clicked the button.
|
|
10
|
+
*
|
|
11
|
+
* Still intentionally excludes the irreversible ones — Cloudflare's
|
|
12
|
+
* `DELETE .../deployments/{id}` and project deletion — which would need their
|
|
13
|
+
* own guarded surface.
|
|
14
|
+
*/
|
|
15
|
+
/** The only two environments Pages has. A third would be silently created by a PATCH. */
|
|
16
|
+
export const CLOUDFLARE_ENVIRONMENTS = ["production", "preview"];
|
|
17
|
+
export class CloudflareActions {
|
|
18
|
+
auth;
|
|
19
|
+
accountId;
|
|
20
|
+
constructor(auth, accountId) {
|
|
21
|
+
this.auth = auth;
|
|
22
|
+
this.accountId = accountId;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Rebuild an existing deployment.
|
|
26
|
+
*
|
|
27
|
+
* Cloudflare calls this "retry" and mints a new deployment id, inheriting the
|
|
28
|
+
* original's branch, commit and environment — so, unlike Vercel's redeploy,
|
|
29
|
+
* nothing has to be carried through by the caller to keep a production retry
|
|
30
|
+
* in production.
|
|
31
|
+
*/
|
|
32
|
+
async retry(projectName, deploymentId) {
|
|
33
|
+
const created = await this.request(`${this.projectPath(projectName)}/deployments/${encodeURIComponent(deploymentId)}/retry`, { method: "POST" });
|
|
34
|
+
return { id: created.id ?? "", url: created.url ?? null };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Point the project's production alias at an earlier deployment without
|
|
38
|
+
* rebuilding it. Cloudflare records this as a rollback, and it takes effect
|
|
39
|
+
* immediately.
|
|
40
|
+
*/
|
|
41
|
+
async rollback(projectName, deploymentId) {
|
|
42
|
+
const rolled = await this.request(`${this.projectPath(projectName)}/deployments/${encodeURIComponent(deploymentId)}/rollback`, { method: "POST" });
|
|
43
|
+
return { id: rolled.id ?? deploymentId, url: rolled.url ?? null };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Add a variable. `secret` (Vercel's "encrypted") is the default, and its
|
|
47
|
+
* value never reads back over the API afterwards.
|
|
48
|
+
*/
|
|
49
|
+
createEnv(projectName, input) {
|
|
50
|
+
const type = input.secret === false ? "plain_text" : "secret_text";
|
|
51
|
+
const patch = {};
|
|
52
|
+
for (const environment of requireEnvironments(input.environments)) {
|
|
53
|
+
patch[environment] = { [input.key]: { type, value: input.value } };
|
|
54
|
+
}
|
|
55
|
+
return this.patchEnv(projectName, input.key, patch);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Change a variable's value and/or the environments it applies to.
|
|
59
|
+
*
|
|
60
|
+
* Adding an environment needs a value: Cloudflare stores the variable
|
|
61
|
+
* separately per environment and never hands back a secret's value, so there
|
|
62
|
+
* is nothing to copy across. Saying that is better than writing an empty
|
|
63
|
+
* string into the new environment.
|
|
64
|
+
*/
|
|
65
|
+
async updateEnv(projectName, key, input) {
|
|
66
|
+
const current = await this.currentEnv(projectName, key);
|
|
67
|
+
const wanted = input.environments
|
|
68
|
+
? requireEnvironments(input.environments)
|
|
69
|
+
: current.environments;
|
|
70
|
+
const patch = {};
|
|
71
|
+
for (const environment of wanted) {
|
|
72
|
+
if (input.value !== undefined) {
|
|
73
|
+
patch[environment] = { [key]: { type: current.type, value: input.value } };
|
|
74
|
+
}
|
|
75
|
+
else if (!current.environments.includes(environment)) {
|
|
76
|
+
throw new Error(`Adding "${key}" to ${environment} needs a value — Cloudflare stores it separately per environment.`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
for (const environment of current.environments) {
|
|
80
|
+
if (!wanted.includes(environment)) {
|
|
81
|
+
patch[environment] = { [key]: null };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return this.patchEnv(projectName, key, patch);
|
|
85
|
+
}
|
|
86
|
+
/** Remove a variable from every environment it is set in. */
|
|
87
|
+
async deleteEnv(projectName, key) {
|
|
88
|
+
const current = await this.currentEnv(projectName, key);
|
|
89
|
+
const patch = {};
|
|
90
|
+
for (const environment of current.environments)
|
|
91
|
+
patch[environment] = { [key]: null };
|
|
92
|
+
await this.patchProject(projectName, patch);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The one write to a project's variables.
|
|
96
|
+
*
|
|
97
|
+
* Cloudflare merges the `env_vars` map it is sent — a key set to `null` is
|
|
98
|
+
* deleted, and keys the patch does not mention are untouched — so this never
|
|
99
|
+
* has to read-modify-write the whole map and cannot clobber a variable added
|
|
100
|
+
* between the read and the write.
|
|
101
|
+
*/
|
|
102
|
+
async patchEnv(projectName, key, patch) {
|
|
103
|
+
const updated = await this.patchProject(projectName, patch);
|
|
104
|
+
const found = updated.find((entry) => entry.key === key);
|
|
105
|
+
if (!found)
|
|
106
|
+
throw new Error(`Cloudflare did not report "${key}" after the update.`);
|
|
107
|
+
return found;
|
|
108
|
+
}
|
|
109
|
+
async patchProject(projectName, patch) {
|
|
110
|
+
const deploymentConfigs = {};
|
|
111
|
+
for (const [environment, envVars] of Object.entries(patch)) {
|
|
112
|
+
if (envVars)
|
|
113
|
+
deploymentConfigs[environment] = { env_vars: envVars };
|
|
114
|
+
}
|
|
115
|
+
const project = await this.request(this.projectPath(projectName), {
|
|
116
|
+
method: "PATCH",
|
|
117
|
+
body: { deployment_configs: deploymentConfigs },
|
|
118
|
+
});
|
|
119
|
+
return mergeEnvVars(project.deployment_configs);
|
|
120
|
+
}
|
|
121
|
+
async currentEnv(projectName, key) {
|
|
122
|
+
const project = await this.request(this.projectPath(projectName));
|
|
123
|
+
const found = mergeEnvVars(project.deployment_configs).find((entry) => entry.key === key);
|
|
124
|
+
if (!found)
|
|
125
|
+
throw new Error(`No variable named "${key}" on this project.`);
|
|
126
|
+
return found;
|
|
127
|
+
}
|
|
128
|
+
projectPath(projectName) {
|
|
129
|
+
return `/accounts/${this.accountId}/pages/projects/${encodeURIComponent(projectName)}`;
|
|
130
|
+
}
|
|
131
|
+
request(path, opts) {
|
|
132
|
+
return cloudflareRequest(this.auth, path, opts);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Rejects anything that is not one of Pages' two environments. Vercel's env
|
|
137
|
+
* dialog offers `development` as well, and a PATCH naming it would quietly
|
|
138
|
+
* create a third deployment config that nothing ever reads.
|
|
139
|
+
*/
|
|
140
|
+
function requireEnvironments(environments) {
|
|
141
|
+
const invalid = environments.filter((entry) => !CLOUDFLARE_ENVIRONMENTS.includes(entry));
|
|
142
|
+
if (invalid.length > 0) {
|
|
143
|
+
throw new Error(`Cloudflare Pages only has ${CLOUDFLARE_ENVIRONMENTS.join(" and ")} environments, not ${invalid.join(", ")}.`);
|
|
144
|
+
}
|
|
145
|
+
return environments;
|
|
146
|
+
}
|
|
147
|
+
/** The read side's flattening, applied to whatever a write handed back. */
|
|
148
|
+
function mergeEnvVars(configs) {
|
|
149
|
+
const merged = new Map();
|
|
150
|
+
for (const [environment, config] of Object.entries(configs ?? {})) {
|
|
151
|
+
for (const [key, entry] of Object.entries(config?.env_vars ?? {})) {
|
|
152
|
+
if (!entry)
|
|
153
|
+
continue;
|
|
154
|
+
const type = entry.type === "plain_text" ? "plain_text" : "secret_text";
|
|
155
|
+
const existing = merged.get(key);
|
|
156
|
+
if (existing) {
|
|
157
|
+
existing.environments.push(environment);
|
|
158
|
+
if (type === "secret_text")
|
|
159
|
+
existing.type = "secret_text";
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
merged.set(key, { key, environments: [environment], type, value: entry.value });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return [...merged.values()].sort((a, b) => a.key.localeCompare(b.key));
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Cloudflare's action vocabulary, mapped onto the names the shared UI already
|
|
169
|
+
* knows. "retry" is Cloudflare's word for what Vercel calls a redeploy, and
|
|
170
|
+
* there is no cancel endpoint for a Pages build at all — so the manifest is
|
|
171
|
+
* two actions, not four, and the deployment row hides what it cannot do.
|
|
172
|
+
*/
|
|
173
|
+
export const CLOUDFLARE_ACTIONS = ["redeploy", "rollback"];
|
|
174
|
+
/** Actions that change what production serves — the UI confirms these first. */
|
|
175
|
+
export const PRODUCTION_AFFECTING_ACTIONS = new Set([
|
|
176
|
+
"rollback",
|
|
177
|
+
]);
|
|
178
|
+
//# sourceMappingURL=cloudflare-actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-actions.js","sourceRoot":"","sources":["../../src/core/cloudflare-actions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type ProviderAuthSpec, type ProviderCliSession, type ProviderCliStatus } from "./providers/credentials.js";
|
|
2
|
+
/** Provider id this integration stores its connection under. */
|
|
3
|
+
export declare const CLOUDFLARE_PROVIDER_ID = "cloudflare";
|
|
4
|
+
/**
|
|
5
|
+
* Candidate locations of Wrangler's `config/default.toml`, newest convention
|
|
6
|
+
* first. Wrangler resolves these through `xdg-app-paths` under the app name
|
|
7
|
+
* `.wrangler`; the trailing entry is the pre-XDG path that long-lived installs
|
|
8
|
+
* still carry, and `WRANGLER_HOME` overrides all of them.
|
|
9
|
+
*/
|
|
10
|
+
export declare function wranglerConfigDirs(env?: NodeJS.ProcessEnv): string[];
|
|
11
|
+
/**
|
|
12
|
+
* The ambient Cloudflare credential, or null when there is none.
|
|
13
|
+
*
|
|
14
|
+
* Two sources, in the order Wrangler itself prefers them:
|
|
15
|
+
*
|
|
16
|
+
* 1. `CLOUDFLARE_API_TOKEN` in the environment, which is how Wrangler runs in
|
|
17
|
+
* CI and in most shells that have been set up for it;
|
|
18
|
+
* 2. the OAuth token `wrangler login` wrote to `config/default.toml`.
|
|
19
|
+
*
|
|
20
|
+
* Neither is ever copied into NoMoreIDE's config — that is the shared `cli`
|
|
21
|
+
* policy, and it is what makes `wrangler logout` revoke us too.
|
|
22
|
+
*
|
|
23
|
+
* An expired stored token reads as "not logged in" rather than being handed
|
|
24
|
+
* out: Wrangler refreshes it roughly hourly and rotates the refresh token when
|
|
25
|
+
* it does, so renewing it here would invalidate Wrangler's own copy.
|
|
26
|
+
*/
|
|
27
|
+
export declare function readWranglerSession(env?: NodeJS.ProcessEnv): Promise<ProviderCliSession | null>;
|
|
28
|
+
/**
|
|
29
|
+
* Cloudflare's half of the shared credential resolver.
|
|
30
|
+
*
|
|
31
|
+
* **No `oauth` block — because of how our flow works, not because Cloudflare
|
|
32
|
+
* has no OAuth.** The shared `providers/oauth.ts` mints its own client at
|
|
33
|
+
* runtime via dynamic client registration (RFC 7591), which is what lets
|
|
34
|
+
* NoMoreIDE ship no client id at all. Cloudflare has no registration endpoint,
|
|
35
|
+
* so `registerOAuthClient()` would throw before a browser ever opened.
|
|
36
|
+
*
|
|
37
|
+
* That is a narrower obstacle than it was, and narrower than this comment used
|
|
38
|
+
* to claim: Cloudflare *does* serve OIDC discovery, at
|
|
39
|
+
* `dash.cloudflare.com/.well-known/openid-configuration`. It advertises PKCE
|
|
40
|
+
* (S256), `none` for token endpoint auth, and both the `authorization_code` and
|
|
41
|
+
* `refresh_token` grants — but no `registration_endpoint`. Since 2026-06-03 you
|
|
42
|
+
* register the client yourself instead, and a *public* client needs no secret.
|
|
43
|
+
*
|
|
44
|
+
* Adopting it is therefore a change of model rather than a new provider entry:
|
|
45
|
+
* NoMoreIDE would ship a pre-registered client id, which `ProviderAuthSpec`
|
|
46
|
+
* has no way to express, and public visibility additionally requires a verified
|
|
47
|
+
* client domain and is irreversible once granted.
|
|
48
|
+
*
|
|
49
|
+
* Loopback redirect URIs are accepted — verified against a live client, and the
|
|
50
|
+
* thing that would otherwise have ruled this out. Cloudflare also exposes a
|
|
51
|
+
* device authorization endpoint, so a provider that ever *does* refuse loopback
|
|
52
|
+
* has a redirect-free path available.
|
|
53
|
+
*
|
|
54
|
+
* Until then Cloudflare connects with an API token or by inheriting Wrangler's
|
|
55
|
+
* own login. The three-source model survives contact with provider #2; only
|
|
56
|
+
* this provider's subset of it is smaller.
|
|
57
|
+
*
|
|
58
|
+
* @see docs/plans/2026-08-13-provider-registry-design.md §11
|
|
59
|
+
*/
|
|
60
|
+
export declare const CLOUDFLARE_AUTH: ProviderAuthSpec;
|
|
61
|
+
export declare function cloudflareCliStatus(env?: NodeJS.ProcessEnv): Promise<ProviderCliStatus>;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { providerCliStatus, } from "./providers/credentials.js";
|
|
5
|
+
/** Provider id this integration stores its connection under. */
|
|
6
|
+
export const CLOUDFLARE_PROVIDER_ID = "cloudflare";
|
|
7
|
+
/**
|
|
8
|
+
* Candidate locations of Wrangler's `config/default.toml`, newest convention
|
|
9
|
+
* first. Wrangler resolves these through `xdg-app-paths` under the app name
|
|
10
|
+
* `.wrangler`; the trailing entry is the pre-XDG path that long-lived installs
|
|
11
|
+
* still carry, and `WRANGLER_HOME` overrides all of them.
|
|
12
|
+
*/
|
|
13
|
+
export function wranglerConfigDirs(env = process.env) {
|
|
14
|
+
// `env.HOME` rather than `homedir()` so an overridden environment redirects
|
|
15
|
+
// every candidate, not just the XDG one — otherwise a caller that points
|
|
16
|
+
// XDG_CONFIG_HOME somewhere still falls through to the real user's login.
|
|
17
|
+
const home = env.HOME?.trim() || homedir();
|
|
18
|
+
const dirs = [];
|
|
19
|
+
const wranglerHome = env.WRANGLER_HOME?.trim();
|
|
20
|
+
if (wranglerHome)
|
|
21
|
+
dirs.push(wranglerHome);
|
|
22
|
+
const xdgConfigHome = env.XDG_CONFIG_HOME?.trim();
|
|
23
|
+
if (xdgConfigHome)
|
|
24
|
+
dirs.push(join(xdgConfigHome, ".wrangler"));
|
|
25
|
+
if (process.platform === "darwin") {
|
|
26
|
+
dirs.push(join(home, "Library", "Preferences", ".wrangler"));
|
|
27
|
+
}
|
|
28
|
+
else if (process.platform === "win32") {
|
|
29
|
+
const appData = env.APPDATA?.trim();
|
|
30
|
+
if (appData)
|
|
31
|
+
dirs.push(join(appData, ".wrangler", "Config"));
|
|
32
|
+
}
|
|
33
|
+
dirs.push(join(home, ".config", ".wrangler"));
|
|
34
|
+
dirs.push(join(home, ".wrangler"));
|
|
35
|
+
return dirs;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The ambient Cloudflare credential, or null when there is none.
|
|
39
|
+
*
|
|
40
|
+
* Two sources, in the order Wrangler itself prefers them:
|
|
41
|
+
*
|
|
42
|
+
* 1. `CLOUDFLARE_API_TOKEN` in the environment, which is how Wrangler runs in
|
|
43
|
+
* CI and in most shells that have been set up for it;
|
|
44
|
+
* 2. the OAuth token `wrangler login` wrote to `config/default.toml`.
|
|
45
|
+
*
|
|
46
|
+
* Neither is ever copied into NoMoreIDE's config — that is the shared `cli`
|
|
47
|
+
* policy, and it is what makes `wrangler logout` revoke us too.
|
|
48
|
+
*
|
|
49
|
+
* An expired stored token reads as "not logged in" rather than being handed
|
|
50
|
+
* out: Wrangler refreshes it roughly hourly and rotates the refresh token when
|
|
51
|
+
* it does, so renewing it here would invalidate Wrangler's own copy.
|
|
52
|
+
*/
|
|
53
|
+
export async function readWranglerSession(env = process.env) {
|
|
54
|
+
const currentScope = env.CLOUDFLARE_ACCOUNT_ID?.trim() || env.CF_ACCOUNT_ID?.trim() || undefined;
|
|
55
|
+
const envToken = env.CLOUDFLARE_API_TOKEN?.trim() || env.CF_API_TOKEN?.trim();
|
|
56
|
+
if (envToken)
|
|
57
|
+
return { token: envToken, currentScope };
|
|
58
|
+
for (const dir of wranglerConfigDirs(env)) {
|
|
59
|
+
const config = await readTomlFields(join(dir, "config", "default.toml"));
|
|
60
|
+
const token = config.oauth_token;
|
|
61
|
+
if (!token)
|
|
62
|
+
continue;
|
|
63
|
+
const expiresAt = Date.parse(config.expiration_time ?? "");
|
|
64
|
+
if (Number.isFinite(expiresAt) && expiresAt <= Date.now())
|
|
65
|
+
return null;
|
|
66
|
+
return { token, currentScope };
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The top-level `key = "value"` pairs of a TOML file, or `{}` if anything goes
|
|
72
|
+
* wrong.
|
|
73
|
+
*
|
|
74
|
+
* Deliberately not a TOML parser and deliberately not shared: Wrangler's auth
|
|
75
|
+
* file is flat and quoted, and the only reason this exists rather than
|
|
76
|
+
* `readJsonField` is that Cloudflare's CLI happens to store its login as TOML.
|
|
77
|
+
* A malformed or missing file must read as "not logged in", never as an error
|
|
78
|
+
* the UI has to explain.
|
|
79
|
+
*/
|
|
80
|
+
async function readTomlFields(path) {
|
|
81
|
+
const fields = {};
|
|
82
|
+
try {
|
|
83
|
+
const raw = await readFile(path, "utf8");
|
|
84
|
+
for (const line of raw.split("\n")) {
|
|
85
|
+
// Stop at the first table header: everything Wrangler stores is top-level,
|
|
86
|
+
// and a nested table's keys are not the same keys.
|
|
87
|
+
if (/^\s*\[/.test(line))
|
|
88
|
+
break;
|
|
89
|
+
const match = /^\s*([A-Za-z_][\w-]*)\s*=\s*"([^"]*)"\s*$/.exec(line);
|
|
90
|
+
if (match?.[2])
|
|
91
|
+
fields[match[1]] = match[2];
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
/* an unreadable file is "not logged in" */
|
|
96
|
+
}
|
|
97
|
+
return fields;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Cloudflare's half of the shared credential resolver.
|
|
101
|
+
*
|
|
102
|
+
* **No `oauth` block — because of how our flow works, not because Cloudflare
|
|
103
|
+
* has no OAuth.** The shared `providers/oauth.ts` mints its own client at
|
|
104
|
+
* runtime via dynamic client registration (RFC 7591), which is what lets
|
|
105
|
+
* NoMoreIDE ship no client id at all. Cloudflare has no registration endpoint,
|
|
106
|
+
* so `registerOAuthClient()` would throw before a browser ever opened.
|
|
107
|
+
*
|
|
108
|
+
* That is a narrower obstacle than it was, and narrower than this comment used
|
|
109
|
+
* to claim: Cloudflare *does* serve OIDC discovery, at
|
|
110
|
+
* `dash.cloudflare.com/.well-known/openid-configuration`. It advertises PKCE
|
|
111
|
+
* (S256), `none` for token endpoint auth, and both the `authorization_code` and
|
|
112
|
+
* `refresh_token` grants — but no `registration_endpoint`. Since 2026-06-03 you
|
|
113
|
+
* register the client yourself instead, and a *public* client needs no secret.
|
|
114
|
+
*
|
|
115
|
+
* Adopting it is therefore a change of model rather than a new provider entry:
|
|
116
|
+
* NoMoreIDE would ship a pre-registered client id, which `ProviderAuthSpec`
|
|
117
|
+
* has no way to express, and public visibility additionally requires a verified
|
|
118
|
+
* client domain and is irreversible once granted.
|
|
119
|
+
*
|
|
120
|
+
* Loopback redirect URIs are accepted — verified against a live client, and the
|
|
121
|
+
* thing that would otherwise have ruled this out. Cloudflare also exposes a
|
|
122
|
+
* device authorization endpoint, so a provider that ever *does* refuse loopback
|
|
123
|
+
* has a redirect-free path available.
|
|
124
|
+
*
|
|
125
|
+
* Until then Cloudflare connects with an API token or by inheriting Wrangler's
|
|
126
|
+
* own login. The three-source model survives contact with provider #2; only
|
|
127
|
+
* this provider's subset of it is smaller.
|
|
128
|
+
*
|
|
129
|
+
* @see docs/plans/2026-08-13-provider-registry-design.md §11
|
|
130
|
+
*/
|
|
131
|
+
export const CLOUDFLARE_AUTH = {
|
|
132
|
+
id: CLOUDFLARE_PROVIDER_ID,
|
|
133
|
+
name: "Cloudflare",
|
|
134
|
+
cliSession: readWranglerSession,
|
|
135
|
+
messages: {
|
|
136
|
+
cliMissing: "No Wrangler login found. Run `wrangler login`, or paste a Cloudflare API token instead.",
|
|
137
|
+
noStoredToken: "No Cloudflare token stored. Reconnect Cloudflare with an API token.",
|
|
138
|
+
cliLoggedOut: "Wrangler is no longer logged in. Run `wrangler login` again, or connect with a Cloudflare API token.",
|
|
139
|
+
notConnected: "Cloudflare is not connected. Run `wrangler login`, or add a Cloudflare API token.",
|
|
140
|
+
// Reachable only if a connection were somehow saved as `oauth`; Cloudflare
|
|
141
|
+
// never creates one, so the message points at what does work.
|
|
142
|
+
signInExpired: "Cloudflare has no browser sign-in. Connect with a Cloudflare API token.",
|
|
143
|
+
refreshFailed: "Could not renew your Cloudflare credential ({error}). Add an API token.",
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
export function cloudflareCliStatus(env = process.env) {
|
|
147
|
+
return providerCliStatus(CLOUDFLARE_AUTH, env);
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=cloudflare-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-auth.js","sourceRoot":"","sources":["../../src/core/cloudflare-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { CloudflareActions } from "./cloudflare-actions.js";
|
|
2
|
+
import { CLOUDFLARE_AUTH, CLOUDFLARE_PROVIDER_ID } from "./cloudflare-auth.js";
|
|
3
|
+
import { CloudflareManager } from "./cloudflare-manager.js";
|
|
4
|
+
import { CLOUDFLARE_HOOKS, CLOUDFLARE_MANIFEST, createCloudflareDeployActions, createCloudflareDeployProvider, toProviderProject, } from "./cloudflare-provider.js";
|
|
5
|
+
import { resolveProviderCredential } from "./providers/credentials.js";
|
|
6
|
+
import { createProviderFetch } from "./providers/egress.js";
|
|
7
|
+
import { adoptSoleScope, resolveProviderProject, } from "./providers/project-resolution.js";
|
|
8
|
+
/**
|
|
9
|
+
* Cloudflare's registry entry — how a connected client is built for a working
|
|
10
|
+
* directory, and the write-capable half resolved separately from it.
|
|
11
|
+
*
|
|
12
|
+
* The registry imports this; this must not import the registry, or the two
|
|
13
|
+
* would cycle.
|
|
14
|
+
*
|
|
15
|
+
* Shorter than Vercel's counterpart in one place and longer in another, which
|
|
16
|
+
* is the point of doing provider #2 before publishing anything:
|
|
17
|
+
*
|
|
18
|
+
* - **No token persistence.** Cloudflare has no browser sign-in, so nothing
|
|
19
|
+
* here ever rotates a refresh token — the `onTokensRefreshed` plumbing Vercel
|
|
20
|
+
* needs is simply absent.
|
|
21
|
+
* - **The account is resolved before the project, not after.** Every Pages path
|
|
22
|
+
* contains an account id, so project resolution cannot even begin until the
|
|
23
|
+
* scope is known.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* A connected Cloudflare client for the given working directory.
|
|
27
|
+
*
|
|
28
|
+
* Throws when Cloudflare isn't connected. A missing *account* is not an error
|
|
29
|
+
* either: `account()` and `listScopes()` answer without one, which is what lets
|
|
30
|
+
* the dashboard show the account switcher rather than a dead end.
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* The scoped `fetch` every Cloudflare client here is built with, minted once
|
|
34
|
+
* from the manifest's `api.hosts`. This is the only file that mints it, which
|
|
35
|
+
* is what makes the allowlist a boundary rather than a suggestion — a manager
|
|
36
|
+
* built anywhere else gets global `fetch` and is a test, not a client.
|
|
37
|
+
*/
|
|
38
|
+
const cloudflareFetch = createProviderFetch(CLOUDFLARE_MANIFEST);
|
|
39
|
+
async function cloudflareContext(configStore, gitCwd) {
|
|
40
|
+
const config = await configStore.load();
|
|
41
|
+
const credential = await resolveProviderCredential(CLOUDFLARE_AUTH, config, process.env);
|
|
42
|
+
const accountId = credential.scopeId ?? (await adoptDefaultAccount(configStore, credential));
|
|
43
|
+
const manager = new CloudflareManager(credential.token, accountId, undefined, cloudflareFetch);
|
|
44
|
+
const project = await resolveProject(config, manager, gitCwd);
|
|
45
|
+
return {
|
|
46
|
+
// The project is resolved first and handed to the provider: Cloudflare has
|
|
47
|
+
// no global deployment endpoint, so the deployment reads need it.
|
|
48
|
+
provider: createCloudflareDeployProvider(manager, project?.name),
|
|
49
|
+
credential: { ...credential, scopeId: accountId },
|
|
50
|
+
project,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The account to use when the user has never chosen one.
|
|
55
|
+
*
|
|
56
|
+
* `cliSelectsScope: false` because Wrangler has no account switch to inherit —
|
|
57
|
+
* so unlike Vercel, a `cli` credential does adopt the sole account, or a
|
|
58
|
+
* `wrangler login` with one account could never read a single project. It is
|
|
59
|
+
* still not written to config, so `CLOUDFLARE_ACCOUNT_ID` keeps winning.
|
|
60
|
+
*/
|
|
61
|
+
function adoptDefaultAccount(configStore, credential) {
|
|
62
|
+
return adoptSoleScope({
|
|
63
|
+
source: credential.source,
|
|
64
|
+
cliSelectsScope: false,
|
|
65
|
+
listScopes: () => new CloudflareManager(credential.token, undefined, undefined, cloudflareFetch)
|
|
66
|
+
.listAccounts()
|
|
67
|
+
.then((accounts) =>
|
|
68
|
+
// Cloudflare accounts have no slug; the id addresses one.
|
|
69
|
+
accounts.map((account) => ({ id: account.id, slug: account.id }))),
|
|
70
|
+
persist: (scope) => configStore.setConnectionScope(CLOUDFLARE_PROVIDER_ID, scope).then(() => undefined),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/** Write-capable counterpart, resolved the same way but returned separately. */
|
|
74
|
+
async function cloudflareActions(configStore) {
|
|
75
|
+
const config = await configStore.load();
|
|
76
|
+
const credential = await resolveProviderCredential(CLOUDFLARE_AUTH, config, process.env);
|
|
77
|
+
const accountId = credential.scopeId ?? (await adoptDefaultAccount(configStore, credential));
|
|
78
|
+
if (!accountId) {
|
|
79
|
+
throw new Error("Choose a Cloudflare account before changing one of its projects.");
|
|
80
|
+
}
|
|
81
|
+
return createCloudflareDeployActions(new CloudflareActions({ token: credential.token, fetch: cloudflareFetch }, accountId));
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Which Cloudflare project this repository deploys — the shared three-tier
|
|
85
|
+
* ladder, with only two of its three tiers in play: Wrangler writes no link
|
|
86
|
+
* file, so an unpinned repo is matched on its git remote.
|
|
87
|
+
*/
|
|
88
|
+
async function resolveProject(config, manager, gitCwd) {
|
|
89
|
+
const project = await resolveProviderProject({
|
|
90
|
+
providerId: CLOUDFLARE_PROVIDER_ID,
|
|
91
|
+
hooks: CLOUDFLARE_HOOKS,
|
|
92
|
+
config,
|
|
93
|
+
gitCwd,
|
|
94
|
+
getProject: (id) => manager.getProject(id),
|
|
95
|
+
findByRepoUrl: (repoUrl) => manager.findProjectByRepo(repoUrl),
|
|
96
|
+
});
|
|
97
|
+
return project ? toProviderProject(project) : undefined;
|
|
98
|
+
}
|
|
99
|
+
export const cloudflareDeployProvider = {
|
|
100
|
+
manifest: CLOUDFLARE_MANIFEST,
|
|
101
|
+
auth: CLOUDFLARE_AUTH,
|
|
102
|
+
hooks: CLOUDFLARE_HOOKS,
|
|
103
|
+
context: cloudflareContext,
|
|
104
|
+
actions: cloudflareActions,
|
|
105
|
+
};
|
|
106
|
+
//# sourceMappingURL=cloudflare-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-context.js","sourceRoot":"","sources":["../../src/core/cloudflare-context.ts"],"names":[],"mappings":"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"}
|