cf-orbs 0.1.1 → 0.2.2
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 +51 -15
- package/dist/commands/deploy.js +4 -2
- package/dist/commands/doctor.js +13 -3
- package/dist/commands/init.js +365 -18
- package/dist/commands/upgrade.js +11 -2
- package/dist/config.js +1 -1
- package/dist/file-checks.js +194 -0
- package/dist/index.js +6 -1
- package/dist/project.js +34 -3
- package/dist/wrangler-template.js +3 -0
- package/dist/wrangler.js +3 -1
- package/package.json +1 -1
- package/templates/cf-orbs.config.example.jsonc +15 -1
- package/templates/dispatch/dispatch.sh +3 -3
- package/templates/image/Dockerfile +2 -2
- package/templates/provider/Dockerfile +2 -2
- package/templates/provider/entry.sh +1 -1
- package/templates/worker/src/api.ts +1 -1
- package/templates/worker/src/core/config.ts +3 -2
- package/templates/worker/src/core/types.ts +7 -3
package/README.md
CHANGED
|
@@ -2,21 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
Run [Amp](https://ampcode.com) orbs on your own Cloudflare account. cf-orbs is a self-hosted Amp orb provider built on Cloudflare Containers. You install it with one command.
|
|
4
4
|
|
|
5
|
+
Docs: [cf-orbs-site.mdick85.workers.dev/docs](https://cf-orbs-site.mdick85.workers.dev/docs/)
|
|
6
|
+
|
|
5
7
|
Community project. Not affiliated with Cloudflare or Amp (Sourcegraph). "Cloudflare" and "Amp" are trademarks of their owners.
|
|
6
8
|
|
|
7
9
|
## Why cf-orbs
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
Amp's self-hosted orbs work like this: you provide the compute, and Amp turns it into orbs. cf-orbs makes your Cloudflare account that compute. You keep the same Amp threads and the same orb experience, on machines you own.
|
|
12
|
+
|
|
13
|
+
- **Your account.** Orbs, snapshots and logs stay in your Cloudflare account. The provider connects out to Amp. Nothing connects in.
|
|
14
|
+
- **No server to run.** One CLI, no cluster, no extra API keys. Cloudflare runs the machines.
|
|
15
|
+
- **Your image.** Orbs start from your own image and toolchain (`image/Dockerfile`).
|
|
16
|
+
- **Your bill.** You pay Cloudflare. A paused orb has no compute charge.
|
|
17
|
+
- **New threads start ready.** Repo setup runs once. Each new thread then starts from that project snapshot, with your dependencies already installed.
|
|
18
|
+
- **Recovers by itself.** A lost orb comes back and the agent runs the failed tool call again. Paused orbs restore after an image rebuild.
|
|
10
19
|
|
|
11
|
-
|
|
12
|
-
- **Fast wake.** Warm pause keeps the orb and its Amp executor alive for 10 min after Amp pauses it (snapshot taken first, `warmMinutes` to change). A follow-up in that window runs its first tool in 0.3-0.5 s; Amp-hosted takes 0.85 s, a cold restore 1.5-2 s plus process start.
|
|
13
|
-
- **Fast network.** git clone 0.26 s, npm install of express 7.7 s; Amp-hosted a1.small 0.54 s and 15.2 s; E2B 0.44 s and 10.1 s.
|
|
14
|
-
- **Same interactive speed.** Tool round trips on a live orb are 0.8-0.9 s on every provider.
|
|
15
|
-
- **Reliable.** 10 of 10 threads in every benchmark round. A lost orb comes back in 22 s and the agent retries. Paused orbs restore after an image rebuild.
|
|
16
|
-
- **Cheapest cloud option at this size.** About $0.11/h running idle and $0.17/h at 20% CPU for 4 vCPU / 12 GiB / 20 GB; no compute charge while paused (snapshot storage has no published rate yet). Amp-hosted a1.small is $0.17/h for 2 CPU / 4 GiB; E2B about $0.33/h for 4 vCPU / 8 GiB.
|
|
17
|
-
- **Yours.** One CLI, no cluster, no extra API keys. Orbs, snapshots and logs stay in your Cloudflare account.
|
|
20
|
+
Benchmarked as fast or faster than Amp-hosted orbs on first response and installs ([benchmarks](https://cf-orbs-site.mdick85.workers.dev/benchmarks), [method](https://github.com/meirdick/cf-orbs/blob/main/docs/benchmarks.md#method)).
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
Good to know: a paused orb keeps its files, not running processes (Cloudflare saves the disk only). One orb is at most 4 vCPU, 12 GiB memory and 20 GB disk.
|
|
20
23
|
|
|
21
24
|
## What it is
|
|
22
25
|
|
|
@@ -35,7 +38,7 @@ When Amp pauses an orb, cf-orbs saves the disk as a snapshot and stops the conta
|
|
|
35
38
|
| You need | Check |
|
|
36
39
|
| --- | --- |
|
|
37
40
|
| Node 22 or later (wrangler 4 needs it) | `node --version` |
|
|
38
|
-
| Docker, running. wrangler builds the orb image on your machine | `docker info` |
|
|
41
|
+
| Docker, running. wrangler builds the orb image on your machine (not for the [Workers Builds path](#install-without-docker-workers-builds-preview)) | `docker info` |
|
|
39
42
|
| A Cloudflare account on **Workers Paid** ($5/month; Containers need it) | Dashboard → Workers & Pages → Plans |
|
|
40
43
|
| wrangler logged in to that account | `npx wrangler login`, then `npx wrangler whoami` |
|
|
41
44
|
| An Amp provider token (`aop_live_…`) | Amp → Settings → Orbs → Add Provider. Note the provider name you type there |
|
|
@@ -49,7 +52,7 @@ mkdir my-orbs && cd my-orbs
|
|
|
49
52
|
npx cf-orbs init --provider-name <name from Amp>
|
|
50
53
|
```
|
|
51
54
|
|
|
52
|
-
`init` asks for the Amp provider token. It does not show what you type, and the token does not go into your shell history.
|
|
55
|
+
`init` asks for the Amp provider token. The prompt names the Amp page where you create it. It does not show what you type, and the token does not go into your shell history.
|
|
53
56
|
|
|
54
57
|
Measured on 2026-10-30 in an empty directory: **4 min 41 s** from `init` to a running provider. That includes the push of the 1.8 GB orb image (the whole deploy took 247 s). A slow uplink takes longer. The Cloudflare registry sometimes times out on a large push, or answers `Unauthorized` after image preparation. `init` retries both automatically, up to 3 times, and keeps the layers that it already pushed.
|
|
55
58
|
|
|
@@ -95,6 +98,38 @@ node cli/bin/cf-orbs.js init ../my-orbs --provider-name <name> # asks for the
|
|
|
95
98
|
|
|
96
99
|
Then use `node <repo>/cli/bin/cf-orbs.js` wherever this README says `cf-orbs`, with `--dir ../my-orbs` or from inside that directory.
|
|
97
100
|
|
|
101
|
+
### Install without Docker (Workers Builds, preview)
|
|
102
|
+
|
|
103
|
+
No Docker on your machine? `init --no-deploy` makes a git repo and the Worker with its secrets, but does not deploy. You push the repo to GitHub or GitLab and connect it in the Cloudflare dashboard. Workers Builds then builds both images in your account and deploys on each push. Measured: first build 10.6 min, upgrade 5.6 min, plus 2-4 min in the queue.
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
npx cf-orbs init --no-deploy --provider-name <name from Amp> --token-file <file>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
It prints the dashboard settings to use. Full procedure and caveats: [docs/install-workers-builds.md](https://github.com/meirdick/cf-orbs/blob/main/docs/install-workers-builds.md). This is a preview: the default install above (local Docker) and all defaults do not change.
|
|
110
|
+
|
|
111
|
+
### What init validates
|
|
112
|
+
|
|
113
|
+
Before the deploy, `init` stops with one `Failed: … — fix: …` line when:
|
|
114
|
+
|
|
115
|
+
- Node is older than 22.
|
|
116
|
+
- Docker does not answer `docker info` (not checked with `--no-deploy`).
|
|
117
|
+
- wrangler is not logged in, sees no account, or sees several and you gave no `--account-id` (or an id that `wrangler whoami` does not list).
|
|
118
|
+
- There is no Amp token, or it is empty or has spaces (a token without `aop_live_` gives a warning). With `--no-deploy` the token is optional.
|
|
119
|
+
- `cf-orbs.config.jsonc` is not valid: unknown keys, sizes out of range, a bad name or image key.
|
|
120
|
+
- An extra image key has no `image/<key>.Dockerfile`.
|
|
121
|
+
- The account has no workers.dev subdomain and you gave no `--subdomain` (in a terminal, `init` asks for one).
|
|
122
|
+
- `wrangler containers list` fails with a clear plan or login error (Workers Paid is needed). Any other error is a warning.
|
|
123
|
+
|
|
124
|
+
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
|
+
`cf-orbs doctor` also checks the project files, in every mode:
|
|
127
|
+
|
|
128
|
+
- `image/Dockerfile` (and each extra image key) and the provider image use `FROM <image>@sha256:<digest>`.
|
|
129
|
+
- `debugExec` is `false` (WARN when `true`).
|
|
130
|
+
- No `aop_live_` token and no control token value is in a file that git tracks (SKIP outside a git repo).
|
|
131
|
+
- `--no-deploy` projects only: `.gitignore` keeps out `.cf-orbs/control-token` and `.cf-orbs/state.json`, and `package.json` pins the wrangler version that cf-orbs uses.
|
|
132
|
+
|
|
98
133
|
## What you get
|
|
99
134
|
|
|
100
135
|
- Orbs that run on your Cloudflare account. The repo checkout, build output and saved disks stay there.
|
|
@@ -149,7 +184,7 @@ Example: 120 awake hours a month at 20% CPU costs about $27.50 a month in total
|
|
|
149
184
|
|
|
150
185
|
| Command | Does |
|
|
151
186
|
| --- | --- |
|
|
152
|
-
| `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
|
|
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) |
|
|
153
188
|
| `cf-orbs status [orb]` | Install status (provider, awake orbs, snapshots), or one orb |
|
|
154
189
|
| `cf-orbs logs <orb>` | Redacted bootstrap log of one orb. `--lines N` (default 200) |
|
|
155
190
|
| `cf-orbs logs --provider` | Redacted provider log (last 20 lines) |
|
|
@@ -170,7 +205,7 @@ Every command takes `--json`, `--dir <project>` and `--non-interactive`. Orb ids
|
|
|
170
205
|
| Key | Default | Meaning |
|
|
171
206
|
| --- | --- | --- |
|
|
172
207
|
| `name` | `cf-orbs` | Worker name. A second install needs another name |
|
|
173
|
-
| `instance` | `{vcpu: 4, memoryMib: 12288, diskMb: 20000}` | Orb size: custom (1-4 vCPU, at least 3 GiB per vCPU, up to 12 GiB, 8-20 GB disk) or `standard-1`..`standard-4`. `init --vcpu N` writes `{vcpu: N, memoryMib: N*3072, diskMb: 20000}` |
|
|
208
|
+
| `instance` | `{vcpu: 4, memoryMib: 12288, diskMb: 20000}` | Orb size: custom (1-4 vCPU, fractions allowed, at least 3 GiB per vCPU, up to 12 GiB, 8-20 GB disk) or `standard-1`..`standard-4`. `init --vcpu N` writes `{vcpu: N, memoryMib: N*3072, diskMb: 20000}` |
|
|
174
209
|
| `maxRunning` | 20 | Cap on awake orbs. The next spawn fails with a clear message |
|
|
175
210
|
| `maxAwakeHours` | 12 | The watchdog pauses an orb that is awake longer than this |
|
|
176
211
|
| `checkpointMinutes` | 0 (off) | Snapshot an awake orb with changes every N minutes (15 or more) |
|
|
@@ -240,11 +275,11 @@ To report a vulnerability, see [SECURITY.md](https://github.com/meirdick/cf-orbs
|
|
|
240
275
|
|
|
241
276
|
## Status and known gaps
|
|
242
277
|
|
|
243
|
-
v0.
|
|
278
|
+
v0.2.2. 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.
|
|
244
279
|
|
|
245
280
|
Known gaps:
|
|
246
281
|
|
|
247
|
-
-
|
|
282
|
+
- The plan check before the first deploy is best effort: it asks wrangler to list Containers and stops only on a clear plan or login error. Any other error is a warning, and a Free account may still find out from the deploy error.
|
|
248
283
|
- Paused orbs restore after an image rebuild. A restored disk keeps its old content; new image content reaches only orbs started from the image.
|
|
249
284
|
- Orb lost mid-turn: Amp spawns the orb again and the agent runs the failed tool call again. The disk is kept; running processes and shell state are lost.
|
|
250
285
|
- Not tested yet: the provider's memory on `lite` over 24 h, restore after the source image is deleted from the registry, the Desktop pane, and Docker inside an orb. Snapshot storage has no published price (beta).
|
|
@@ -263,6 +298,7 @@ Method, results and costs: [docs/benchmarks.md](https://github.com/meirdick/cf-o
|
|
|
263
298
|
| Doc | Covers |
|
|
264
299
|
| --- | --- |
|
|
265
300
|
| [AGENTS.md](https://github.com/meirdick/cf-orbs/blob/main/AGENTS.md) | Install guide for an AI agent at a terminal |
|
|
301
|
+
| [Install without Docker](https://github.com/meirdick/cf-orbs/blob/main/docs/install-workers-builds.md) | Preview: Workers Builds builds and deploys from a git repo |
|
|
266
302
|
| [Provider host](https://github.com/meirdick/cf-orbs/blob/main/provider/README.md) | Cloudflare-hosted vs external provider, token rotation |
|
|
267
303
|
| [Orb image](https://github.com/meirdick/cf-orbs/blob/main/image/README.md) | The pinned image, toolchains, why snapshots are tied to it |
|
|
268
304
|
| [Dispatch](https://github.com/meirdick/cf-orbs/blob/main/dispatch/README.md) | The program Amp runs per job, and its protocol |
|
package/dist/commands/deploy.js
CHANGED
|
@@ -12,7 +12,8 @@ import { deployWithRetry, lastErrorLine, makeWrangler } from "../wrangler.js";
|
|
|
12
12
|
export async function deployProject(ctx, input) {
|
|
13
13
|
const { ui, paths } = ctx;
|
|
14
14
|
const { config, state } = input;
|
|
15
|
-
|
|
15
|
+
const git = state.mode === "git";
|
|
16
|
+
await materialize(paths, { templates: ctx.templates, git });
|
|
16
17
|
const missing = missingDockerfiles(paths, config);
|
|
17
18
|
if (missing.length > 0) {
|
|
18
19
|
throw new CliError(`image Dockerfile missing: ${missing.join(", ")}`, "create it, or remove its key from images in cf-orbs.config.jsonc");
|
|
@@ -38,7 +39,8 @@ export async function deployProject(ctx, input) {
|
|
|
38
39
|
...state,
|
|
39
40
|
url,
|
|
40
41
|
workerName: config.name,
|
|
41
|
-
mode:
|
|
42
|
+
// git mode stays git mode: Workers Builds still deploys from the repo
|
|
43
|
+
mode: git ? "git" : config.providerHost,
|
|
42
44
|
deployedAt: new Date(ctx.now()).toISOString(),
|
|
43
45
|
cliVersion: await cliVersion(),
|
|
44
46
|
});
|
package/dist/commands/doctor.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
// cf-orbs doctor: PASS/FAIL per check with a one-line fix, plus INFO hints
|
|
1
|
+
// cf-orbs doctor: PASS/FAIL per check with a one-line fix, plus INFO hints and WARN lines that never fail.
|
|
2
|
+
// Read-only: never starts a provider or an orb.
|
|
2
3
|
import { existsSync } from "node:fs";
|
|
3
4
|
import { collectImageKeys, expectOk, providerInfo } from "../api.js";
|
|
4
5
|
import { CONFIG_FILE, loadConfig, orbImageKeys } from "../config.js";
|
|
5
6
|
import { connect } from "../connect.js";
|
|
6
7
|
import { CliError, toFailedLine } from "../errors.js";
|
|
8
|
+
import { fileChecks } from "../file-checks.js";
|
|
7
9
|
import { fileMode, readState } from "../project.js";
|
|
8
10
|
import { classifyDeployFailure, lastErrorLine, makeWrangler } from "../wrangler.js";
|
|
9
11
|
import { AMP_CACHE_HINT } from "./snapshots.js";
|
|
@@ -77,6 +79,10 @@ export async function runDoctor(ctx) {
|
|
|
77
79
|
}
|
|
78
80
|
else
|
|
79
81
|
checks.push(pass("control token", "present, mode 600"));
|
|
82
|
+
checks.push(...(await fileChecks(ctx.run, ctx.paths, config, {
|
|
83
|
+
git: state.mode === "git",
|
|
84
|
+
templates: ctx.templates,
|
|
85
|
+
})));
|
|
80
86
|
const name = config?.name ?? state.workerName ?? "cf-orbs";
|
|
81
87
|
const w = makeWrangler(ctx.run, { accountId: state.accountId });
|
|
82
88
|
const d = await w.run(["deployments", "list", "--json", "--name", name], { timeoutMs: 60_000 });
|
|
@@ -106,20 +112,24 @@ export async function runDoctor(ctx) {
|
|
|
106
112
|
checks.push(failed("health", e));
|
|
107
113
|
}
|
|
108
114
|
let status;
|
|
115
|
+
// git mode: Workers Builds deploys, so the fix is the build, not cf-orbs deploy
|
|
116
|
+
const apiFailed = (n, e) => state.mode === "git" && e instanceof CliError
|
|
117
|
+
? failed(n, new CliError(e.cause_, `wait for the Workers Builds build to finish (Workers & Pages → ${name} → Deployments), or open its build log`))
|
|
118
|
+
: failed(n, e);
|
|
109
119
|
if (api) {
|
|
110
120
|
try {
|
|
111
121
|
expectOk(await api.get("/v1/health"), "health");
|
|
112
122
|
checks.push(pass("health", `${api.url}/v1/health ok`));
|
|
113
123
|
}
|
|
114
124
|
catch (e) {
|
|
115
|
-
checks.push(
|
|
125
|
+
checks.push(apiFailed("health", e));
|
|
116
126
|
}
|
|
117
127
|
try {
|
|
118
128
|
status = expectOk(await api.get("/v1/status"), "status");
|
|
119
129
|
checks.push(pass("status", "/v1/status ok (control token accepted)"));
|
|
120
130
|
}
|
|
121
131
|
catch (e) {
|
|
122
|
-
checks.push(
|
|
132
|
+
checks.push(apiFailed("status", e));
|
|
123
133
|
}
|
|
124
134
|
}
|
|
125
135
|
// Names only: wrangler secret list never returns values.
|