cf-orbs 0.2.3 → 0.2.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 +4 -3
- package/dist/commands/init.js +21 -1
- package/dist/index.js +3 -0
- package/dist/wrangler.js +21 -0
- package/package.json +1 -1
- package/templates/worker/src/api.ts +1 -1
package/README.md
CHANGED
|
@@ -120,6 +120,7 @@ Before the deploy, `init` stops with one `Failed: … — fix: …` line when:
|
|
|
120
120
|
- An extra image key has no `image/<key>.Dockerfile`.
|
|
121
121
|
- The account has no workers.dev subdomain and you gave no `--subdomain` (in a terminal, `init` asks for one).
|
|
122
122
|
- `wrangler containers list` fails with a clear plan or login error (Workers Paid is needed). Any other error is a warning.
|
|
123
|
+
- The folder has no saved deployment and a Worker of the same name already exists in the account. `init` stops before it makes a control token, sets a secret or deploys, so it cannot overwrite a Worker that another folder made. Pass `--adopt` to take it over: a new control token replaces the old one, and this folder's config and `image/` are deployed. A rerun in the folder that made the Worker is not checked. If the check cannot run, it is a warning.
|
|
123
124
|
|
|
124
125
|
After the deploy, `init` checks that `/v1/health` answers, that `/v1/status` accepts the control token, that at least one prewarm instance starts from the new image (self-test; skipped when `prewarm` is 0, and with `--no-deploy` until a build has run), and that the provider runs for 15 s with no token rejection and no second provider on the token.
|
|
125
126
|
|
|
@@ -184,7 +185,7 @@ Example: 120 awake hours a month at 20% CPU costs about $27.50 a month in total
|
|
|
184
185
|
|
|
185
186
|
| Command | Does |
|
|
186
187
|
| --- | --- |
|
|
187
|
-
| `cf-orbs init [dir]` | Scaffold, deploy and check a provider. Flags: `--provider cloudflare\|external`, `--token-file`, `--account-id`, `--provider-name`, `--subdomain`, `--vcpu <1\|2\|3\|4>`, `--name <worker>`, `--no-deploy` (preview: [Workers Builds](https://github.com/meirdick/cf-orbs/blob/main/docs/install-workers-builds.md), no Docker) |
|
|
188
|
+
| `cf-orbs init [dir]` | Scaffold, deploy and check a provider. Flags: `--provider cloudflare\|external`, `--token-file`, `--account-id`, `--provider-name`, `--subdomain`, `--vcpu <1\|2\|3\|4>`, `--name <worker>`, `--no-deploy` (preview: [Workers Builds](https://github.com/meirdick/cf-orbs/blob/main/docs/install-workers-builds.md), no Docker), `--adopt` (take over an existing Worker of the same name; see Back up) |
|
|
188
189
|
| `cf-orbs status [orb]` | Install status (provider, awake orbs, snapshots), or one orb |
|
|
189
190
|
| `cf-orbs logs <orb>` | Redacted bootstrap log of one orb. `--lines N` (default 200) |
|
|
190
191
|
| `cf-orbs logs --provider` | Redacted provider log (last 20 lines) |
|
|
@@ -227,7 +228,7 @@ After `cf-orbs deploy`, an orb that is awake keeps the old Worker code until it
|
|
|
227
228
|
|
|
228
229
|
### Back up
|
|
229
230
|
|
|
230
|
-
Running orbs need nothing on your machine: the Worker, the orbs and the snapshots are in Cloudflare. Back up `cf-orbs.config.jsonc` and `image/` (a private git repo is enough; the `.gitignore` that `init` writes keeps the control token out). Keep the Amp provider token in a password manager if you want to restore without making a new one. The control token can be made again. See [Back up and restore](https://cf-orbs-site.mdick85.workers.dev/docs/guides/back-up-and-restore/).
|
|
231
|
+
Running orbs need nothing on your machine: the Worker, the orbs and the snapshots are in Cloudflare. Back up `cf-orbs.config.jsonc` and `image/` (a private git repo is enough; the `.gitignore` that `init` writes keeps the control token out). Keep the Amp provider token in a password manager if you want to restore without making a new one. The control token can be made again. To restore in a new folder, run `init --adopt`: without it, `init` stops because the Worker already exists. See [Back up and restore](https://cf-orbs-site.mdick85.workers.dev/docs/guides/back-up-and-restore/).
|
|
231
232
|
|
|
232
233
|
## Troubleshooting
|
|
233
234
|
|
|
@@ -279,7 +280,7 @@ To report a vulnerability, see [SECURITY.md](https://github.com/meirdick/cf-orbs
|
|
|
279
280
|
|
|
280
281
|
## Status and known gaps
|
|
281
282
|
|
|
282
|
-
v0.2.
|
|
283
|
+
v0.2.4. The control plane, dispatch program, provider host and CLI are built and unit tested. On 2026-10-30 a real Amp thread ran end to end on a fresh install: spawn from the image, setup, pause with a project snapshot, wake from the snapshot in 2.3 s, first answer 41 s after the message.
|
|
283
284
|
|
|
284
285
|
Known gaps:
|
|
285
286
|
|
package/dist/commands/init.js
CHANGED
|
@@ -14,7 +14,7 @@ import { dockerOk, nodeMajor, whoami } from "../probes.js";
|
|
|
14
14
|
import { cliVersion, GIT_DEPLOY_COMMAND, generateToken, gitPackageJson, materialize, missingDockerfiles, readControlToken, readState, withSecretsFile, writeControlToken, writeState, writeWranglerConfig, } from "../project.js";
|
|
15
15
|
import { checkSubdomain, registerSubdomain, suggestSubdomain, wranglerBearer } from "../subdomain.js";
|
|
16
16
|
import { ask, askHidden } from "../ui.js";
|
|
17
|
-
import { classifyDeployFailure, deployFailureError, lastErrorLine, makeWrangler, WRANGLER_PIN, } from "../wrangler.js";
|
|
17
|
+
import { classifyDeployFailure, deployFailureError, lastErrorLine, lookupWorker, makeWrangler, WRANGLER_PIN, } from "../wrangler.js";
|
|
18
18
|
import { deployProject, startPrewarm } from "./deploy.js";
|
|
19
19
|
export const PROVIDER_WAIT_MS = 90_000;
|
|
20
20
|
/** How long init waits for the prewarm instances (a first start on a cold host pulls the image). */
|
|
@@ -134,6 +134,12 @@ export function plan(o, p) {
|
|
|
134
134
|
{ kind: "plan-check" },
|
|
135
135
|
{ kind: "scaffold", mode: o.mode, git: Boolean(o.noDeploy) },
|
|
136
136
|
];
|
|
137
|
+
// A folder with no saved deployment and no control token must not overwrite a Worker it did not create.
|
|
138
|
+
// A folder with a control token made the Worker before (a run that failed before it saved the URL) or holds a
|
|
139
|
+
// restored backup: it keeps the same token, so nothing is lost. Runs after scaffold (the Worker name is in the
|
|
140
|
+
// config then) and before the control token, the secrets and the deploy.
|
|
141
|
+
if (!p.deployed && !p.hasControlToken)
|
|
142
|
+
steps.push({ kind: "adopt-check", adopt: Boolean(o.adopt) });
|
|
137
143
|
steps.push({ kind: "control-token", action: p.hasControlToken ? "reuse" : "generate" });
|
|
138
144
|
if (ampSource)
|
|
139
145
|
steps.push({ kind: "amp-token", source: ampSource });
|
|
@@ -330,6 +336,19 @@ export async function runInit(ctx, o, steps) {
|
|
|
330
336
|
await writeState(paths, state);
|
|
331
337
|
break;
|
|
332
338
|
}
|
|
339
|
+
case "adopt-check": {
|
|
340
|
+
if (!config)
|
|
341
|
+
throw new Error("init plan bug: adopt-check before scaffold");
|
|
342
|
+
const found = await lookupWorker(makeWrangler(ctx.run, { accountId }), config.name);
|
|
343
|
+
if (found.kind === "found") {
|
|
344
|
+
if (!s.adopt)
|
|
345
|
+
throw new CliError(`Worker ${config.name} already exists in this account and this folder did not create it`, "run init in the folder that made it, restore that folder (see Back up and restore), pass --name <new> for a second install, or pass --adopt to take it over");
|
|
346
|
+
ui.warn(`adopt: taking over Worker ${config.name}; a new control token replaces the old one (the folder that made it loses CLI access); this folder's config and image/ are deployed`);
|
|
347
|
+
}
|
|
348
|
+
else if (found.kind === "unknown")
|
|
349
|
+
ui.warn(`existing-Worker check could not run (${found.reason}); init goes on`);
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
333
352
|
case "control-token":
|
|
334
353
|
controlToken = s.action === "reuse" ? await readControlToken(paths) : undefined;
|
|
335
354
|
if (!controlToken) {
|
|
@@ -765,6 +784,7 @@ export function initOptions(ctx, existing) {
|
|
|
765
784
|
vcpu: parseVcpuFlag(flagString(ctx.args, "vcpu")),
|
|
766
785
|
name,
|
|
767
786
|
noDeploy: flagBool(ctx.args, "no-deploy"),
|
|
787
|
+
adopt: flagBool(ctx.args, "adopt"),
|
|
768
788
|
mode: modeFlag ?? existing?.providerHost ?? "cloudflare",
|
|
769
789
|
nonInteractive: flagBool(ctx.args, "non-interactive") || Boolean(ctx.env.CI),
|
|
770
790
|
tokenFile: flagString(ctx.args, "token-file"),
|
package/dist/index.js
CHANGED
|
@@ -36,6 +36,8 @@ export const COMMANDS = {
|
|
|
36
36
|
"--name <name> Worker name (default cf-orbs; a second install needs another)",
|
|
37
37
|
"--no-deploy preview: no Docker, no deploy. Make a git repo for Workers Builds,",
|
|
38
38
|
" create the Worker and its secrets, print the dashboard steps",
|
|
39
|
+
"--adopt take over a Worker of the same name that this folder did not create;",
|
|
40
|
+
" init stops without it. A new control token replaces the old one",
|
|
39
41
|
],
|
|
40
42
|
flags: {
|
|
41
43
|
provider: { type: "string" },
|
|
@@ -46,6 +48,7 @@ export const COMMANDS = {
|
|
|
46
48
|
vcpu: { type: "string" },
|
|
47
49
|
name: { type: "string" },
|
|
48
50
|
"no-deploy": { type: "boolean" },
|
|
51
|
+
adopt: { type: "boolean" },
|
|
49
52
|
},
|
|
50
53
|
dirPositional: true,
|
|
51
54
|
run: initCommand,
|
package/dist/wrangler.js
CHANGED
|
@@ -135,6 +135,27 @@ export function deployFailureError(kind, output) {
|
|
|
135
135
|
return new CliError(`wrangler deploy failed: ${lastErrorLine(output)}`, "fix the error above, then rerun cf-orbs deploy");
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Does a Worker with this name exist in the account? Runs `wrangler deployments list --json --name <name>`.
|
|
140
|
+
* Verified with wrangler 4.156.0: an existing Worker exits 0 and prints a JSON array. A missing one exits 1,
|
|
141
|
+
* prints nothing on stdout and prints `This Worker does not exist on your account. [code: 10007]` on stderr.
|
|
142
|
+
* Any other result (auth, network, bad account id, odd output) is "unknown".
|
|
143
|
+
*/
|
|
144
|
+
export async function lookupWorker(w, name) {
|
|
145
|
+
const r = await w.run(["deployments", "list", "--json", "--name", name], { timeoutMs: 60_000 });
|
|
146
|
+
const out = `${r.stdout}\n${r.stderr}`;
|
|
147
|
+
if (r.code === 0) {
|
|
148
|
+
try {
|
|
149
|
+
if (Array.isArray(JSON.parse(r.stdout.slice(Math.max(0, r.stdout.indexOf("["))))))
|
|
150
|
+
return { kind: "found" };
|
|
151
|
+
}
|
|
152
|
+
catch { }
|
|
153
|
+
return { kind: "unknown", reason: "unreadable wrangler output" };
|
|
154
|
+
}
|
|
155
|
+
if (/\[code: 10007\]|This Worker does not exist/.test(out))
|
|
156
|
+
return { kind: "missing" };
|
|
157
|
+
return { kind: "unknown", reason: lastErrorLine(out) };
|
|
158
|
+
}
|
|
138
159
|
/** Last meaningful line of wrangler output, preferring the "✘ [ERROR]" line. */
|
|
139
160
|
export function lastErrorLine(output) {
|
|
140
161
|
const clean = output.replace(new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g"), "");
|
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@ import { type Env, orbDoName, PROVIDER_DO_NAME, REGISTRY_DO_NAME } from "./env";
|
|
|
18
18
|
import { BENCH_ORB_RE, type BenchStartRequest, parseBenchStart } from "./orb/bench";
|
|
19
19
|
import { restartProvider } from "./provider-restart";
|
|
20
20
|
|
|
21
|
-
export const API_VERSION = "0.2.
|
|
21
|
+
export const API_VERSION = "0.2.4";
|
|
22
22
|
|
|
23
23
|
/** D2. Amp sends `amp-<thread id>`. Never starts with `_` or `.`, so it cannot look like an internal name. */
|
|
24
24
|
export const ORB_ID_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
|