@coreplane/switchboard 0.0.0 → 1.18.1
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/LICENSE +201 -0
- package/README.md +17 -1
- package/dist/assets/.dockerignore +27 -0
- package/dist/assets/.env.example +33 -0
- package/dist/assets/Dockerfile +111 -0
- package/dist/assets/config/config.example.yaml +359 -0
- package/dist/assets/deploy/bin/build-stamp.d.mts +15 -0
- package/dist/assets/deploy/bin/build-stamp.mjs +98 -0
- package/dist/assets/deploy/bin/cf-logs +32 -0
- package/dist/assets/deploy/cloudflare/package.json +29 -0
- package/dist/assets/deploy/cloudflare/preflight.mjs +243 -0
- package/dist/assets/deploy/cloudflare/tsconfig.json +18 -0
- package/dist/assets/deploy/cloudflare/worker.ts +382 -0
- package/dist/assets/deploy/cloudflare/wrangler.template.jsonc +67 -0
- package/dist/assets/deploy/cloudflare/write-build.d.mts +7 -0
- package/dist/assets/deploy/cloudflare/write-build.mjs +53 -0
- package/dist/assets/deploy/cloudflare-docs/package.json +18 -0
- package/dist/assets/deploy/cloudflare-docs/wrangler.template.jsonc +30 -0
- package/dist/assets/deploy/cloudflare-memory/package.json +25 -0
- package/dist/assets/deploy/cloudflare-memory/tsconfig.json +17 -0
- package/dist/assets/deploy/cloudflare-memory/worker.ts +2635 -0
- package/dist/assets/deploy/cloudflare-memory/wrangler.template.jsonc +50 -0
- package/dist/assets/deploy/cloudflare-resident/Dockerfile +91 -0
- package/dist/assets/deploy/cloudflare-resident/gc.ts +287 -0
- package/dist/assets/deploy/cloudflare-resident/node-async-hooks.d.ts +11 -0
- package/dist/assets/deploy/cloudflare-resident/package.json +29 -0
- package/dist/assets/deploy/cloudflare-resident/preflight.mjs +224 -0
- package/dist/assets/deploy/cloudflare-resident/tsconfig.json +19 -0
- package/dist/assets/deploy/cloudflare-resident/worker.ts +6637 -0
- package/dist/assets/deploy/cloudflare-resident/wrangler.template.jsonc +120 -0
- package/dist/assets/deploy/cloudflare-sandbox/Dockerfile +67 -0
- package/dist/assets/deploy/cloudflare-sandbox/docker-wrapper.sh +37 -0
- package/dist/assets/deploy/cloudflare-sandbox/package.json +26 -0
- package/dist/assets/deploy/cloudflare-sandbox/tsconfig.json +20 -0
- package/dist/assets/deploy/cloudflare-sandbox/worker.ts +410 -0
- package/dist/assets/deploy/cloudflare-sandbox/wrangler.template.jsonc +67 -0
- package/dist/assets/deploy/profile.example.json +13 -0
- package/dist/assets/deploy/secrets.manifest.json +108 -0
- package/dist/assets/docker-entrypoint.sh +15 -0
- package/dist/assets/package-lock.json +18407 -0
- package/dist/assets/package.json +104 -0
- package/dist/assets/project.json +219 -0
- package/dist/assets/source.json +5 -0
- package/dist/assets/src/core/authz/actor.ts +100 -0
- package/dist/assets/src/core/authz/authorize.ts +169 -0
- package/dist/assets/src/core/authz/grants.ts +347 -0
- package/dist/assets/src/core/authz/policy.ts +281 -0
- package/dist/assets/src/core/authz/resource.ts +147 -0
- package/dist/assets/src/core/authz/types.ts +164 -0
- package/dist/assets/src/core/drain.ts +54 -0
- package/dist/assets/src/core/ingressTokens.ts +64 -0
- package/dist/assets/src/core/memory/engine.ts +115 -0
- package/dist/assets/src/core/memory/scorer.ts +147 -0
- package/dist/assets/src/core/memory/types.ts +120 -0
- package/dist/assets/src/core/normalizeSpans.ts +299 -0
- package/dist/assets/src/core/prDescriptionTypes.ts +54 -0
- package/dist/assets/src/core/redact.ts +113 -0
- package/dist/assets/src/core/runEvents.ts +537 -0
- package/dist/assets/src/core/runFriction.ts +665 -0
- package/dist/assets/src/core/runLedger/decisions.ts +126 -0
- package/dist/assets/src/core/runLedger/types.ts +177 -0
- package/dist/assets/src/core/runRecord.ts +627 -0
- package/dist/assets/src/core/runShape.ts +61 -0
- package/dist/assets/src/core/schedules.ts +452 -0
- package/dist/assets/src/core/time/formatDuration.ts +61 -0
- package/dist/assets/src/core/trace/attrs.ts +203 -0
- package/dist/assets/src/core/trace/classify.ts +49 -0
- package/dist/assets/src/core/trace/clock.ts +6 -0
- package/dist/assets/src/core/trace/context.ts +9 -0
- package/dist/assets/src/core/trace/ids.ts +23 -0
- package/dist/assets/src/core/trace/partition.ts +235 -0
- package/dist/assets/src/core/trace/sinks.ts +68 -0
- package/dist/assets/src/core/trace/streamSpans.ts +163 -0
- package/dist/assets/src/core/trace/traceparent.ts +29 -0
- package/dist/assets/src/core/trace/tracer.ts +247 -0
- package/dist/assets/src/core/trace/types.ts +125 -0
- package/dist/assets/src/core/trace/workerTrace.ts +97 -0
- package/dist/assets/src/deploy/buildStamp.ts +93 -0
- package/dist/assets/src/deploy/liveGate.ts +203 -0
- package/dist/assets/src/deploy/profile.ts +162 -0
- package/dist/assets/src/deploy/restart.ts +393 -0
- package/dist/assets/src/effort.ts +17 -0
- package/dist/assets/src/execution/bashTimeout.ts +78 -0
- package/dist/assets/src/execution/bindingPurge.ts +43 -0
- package/dist/assets/src/execution/residentBackupTransfer.ts +50 -0
- package/dist/assets/src/execution/residentCleanliness.ts +95 -0
- package/dist/assets/src/execution/residentCredentials.ts +81 -0
- package/dist/assets/src/execution/residentDepCache.ts +321 -0
- package/dist/assets/src/execution/residentDepsStore.ts +326 -0
- package/dist/assets/src/execution/residentDetach.ts +48 -0
- package/dist/assets/src/execution/residentDisk.ts +107 -0
- package/dist/assets/src/execution/residentDiskBudget.ts +448 -0
- package/dist/assets/src/execution/residentExecWrap.ts +100 -0
- package/dist/assets/src/execution/residentHead.ts +85 -0
- package/dist/assets/src/execution/residentReadonly.ts +72 -0
- package/dist/assets/src/execution/residentRefresh.ts +429 -0
- package/dist/assets/src/execution/residentRestoreExtract.ts +130 -0
- package/dist/assets/src/execution/residentState.ts +47 -0
- package/dist/assets/src/execution/residentStepReport.ts +98 -0
- package/dist/assets/src/execution/residentStepTrace.ts +97 -0
- package/dist/assets/src/execution/residentSteps.ts +99 -0
- package/dist/assets/src/execution/residentText.ts +83 -0
- package/dist/assets/src/execution/residentTrace.ts +119 -0
- package/dist/assets/src/execution/sandboxEnv.ts +42 -0
- package/dist/assets/src/execution/sandboxErrors.ts +159 -0
- package/dist/assets/src/execution/sandboxKeepalive.ts +118 -0
- package/dist/assets/src/execution/shellQuote.ts +8 -0
- package/dist/assets/src/mcp/registry.ts +242 -0
- package/dist/assets/src/providers/types.ts +152 -0
- package/dist/assets/web/dist/.vite/manifest.json +176 -0
- package/dist/assets/web/dist/assets/AppShell-Bk2gbvet.js +1 -0
- package/dist/assets/web/dist/assets/CostsPage-CTZcMYYx.js +1 -0
- package/dist/assets/web/dist/assets/NotFoundPage-C-BuaSm8.js +1 -0
- package/dist/assets/web/dist/assets/ResidentDetailPage-DvQ05AGa.js +1 -0
- package/dist/assets/web/dist/assets/ResidentsIndexPage-B3uxKUne.js +1 -0
- package/dist/assets/web/dist/assets/RunRoutePage-XVFj0XDc.css +1 -0
- package/dist/assets/web/dist/assets/RunRoutePage-ty94olNM.js +126 -0
- package/dist/assets/web/dist/assets/RunsIndexPage-CM-qxyQm.js +1 -0
- package/dist/assets/web/dist/assets/RunsTabs-C4krAL9o.js +1 -0
- package/dist/assets/web/dist/assets/ScheduledPage-C1psvLD4.js +1 -0
- package/dist/assets/web/dist/assets/StatusDot-DuoQnQeU.js +1 -0
- package/dist/assets/web/dist/assets/Tooltip-BfLPyxQy.js +1 -0
- package/dist/assets/web/dist/assets/favicon-DL1rdWJt.js +1 -0
- package/dist/assets/web/dist/assets/localIso-L06jV29p.js +1 -0
- package/dist/assets/web/dist/assets/main-Bnbk_Rsg.js +28 -0
- package/dist/assets/web/dist/assets/main-BsBGUyMH.css +2 -0
- package/dist/assets/web/dist/assets/residentDiskBudget-BMBKlYRH.js +1 -0
- package/dist/assets/web/dist/assets/seed-BglCRKLA.js +6 -0
- package/dist/assets/web/dist/assets/wallClock-Ckv3sKoR.js +1 -0
- package/dist/cli.js +34494 -0
- package/package.json +43 -10
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "node_modules/wrangler/config-schema.json",
|
|
3
|
+
"name": "{{script}}",
|
|
4
|
+
"main": "worker.ts",
|
|
5
|
+
"compatibility_date": "2026-08-01",
|
|
6
|
+
// @cloudflare/sandbox@next needs Node builtins (same as the SDK's own examples).
|
|
7
|
+
"compatibility_flags": ["nodejs_compat"],
|
|
8
|
+
// The installation's Cloudflare account (deploy/profile.json `account`) — every Worker deploys to it.
|
|
9
|
+
"account_id": "{{account}}",
|
|
10
|
+
// Reachable only with the RESIDENT_ADMIN_TOKEN / RESIDENT_OPERATOR_TOKEN
|
|
11
|
+
// bearer secrets (plus an unauthenticated GET /healthz wake ping). Custom
|
|
12
|
+
// domain on the installation's zone so the bot config has a stable URL.
|
|
13
|
+
"workers_dev": false,
|
|
14
|
+
"routes": [{ "pattern": "{{hostname}}", "custom_domain": true }],
|
|
15
|
+
"observability": { "enabled": true },
|
|
16
|
+
"vars": {
|
|
17
|
+
// The state Worker (deploy/cloudflare-memory/): where scheduled() records
|
|
18
|
+
// each resident-watchdog firing. Public URL — a var, not a
|
|
19
|
+
// secret; the bearer is the MEMORY_TOKEN secret (deploy/secrets.manifest.json).
|
|
20
|
+
// A profile without a memory Worker renders no var: firings go unrecorded.
|
|
21
|
+
// {{#if urls.stateWorkerUrl}}
|
|
22
|
+
"STATE_WORKER_URL": "{{urls.stateWorkerUrl}}",
|
|
23
|
+
// {{/if}}
|
|
24
|
+
// Presigned snapshot transfers (docs/reference/specs/resident-repos.md item 61):
|
|
25
|
+
// with these two vars AND the R2_ACCESS_KEY_ID / R2_SECRET_ACCESS_KEY
|
|
26
|
+
// secrets (deploy/secrets.manifest.json) present, the container moves
|
|
27
|
+
// archive bytes itself over presigned R2 URLs and the Durable Object stays
|
|
28
|
+
// out of the data path; any one absent → the SDK's local-bucket mode.
|
|
29
|
+
// BACKUP_BUCKET_NAME MUST equal the BACKUP_BUCKET
|
|
30
|
+
// binding's bucket below — the SDK signs URLs for this name, the offboard
|
|
31
|
+
// sweep deletes through the binding. Public config, not secrets.
|
|
32
|
+
"CLOUDFLARE_ACCOUNT_ID": "{{account}}",
|
|
33
|
+
"BACKUP_BUCKET_NAME": "{{script}}-cache"
|
|
34
|
+
},
|
|
35
|
+
"containers": [
|
|
36
|
+
{
|
|
37
|
+
"class_name": "ResidentDO",
|
|
38
|
+
// The profile's image mode decides (src/deploy/images.ts): `build` renders
|
|
39
|
+
// ./Dockerfile, built by wrangler at deploy time; `registry` renders the
|
|
40
|
+
// release's published image as copied into this account's registry by
|
|
41
|
+
// `deploy images`. The Dockerfile extends docker.io/cloudflare/sandbox:<version>,
|
|
42
|
+
// whose tag MUST match the @cloudflare/sandbox version in package.json
|
|
43
|
+
// EXACTLY (no 'latest' tag exists) — bump both together.
|
|
44
|
+
"image": "{{image}}",
|
|
45
|
+
// Custom 4 vCPU / 12 GiB / 20 GB — the platform's custom-type ceiling
|
|
46
|
+
// (the same shape as standard-4, kept in object form so the sizing test
|
|
47
|
+
// can read it). This ONE type is every resident's container (one DO per
|
|
48
|
+
// repo, one container class), so it is sized for the largest onboarded
|
|
49
|
+
// repo and for 16 threads sharing it. The CPU is the decision now: with
|
|
50
|
+
// one vCPU every thread's test run and the refresh cycle's own install
|
|
51
|
+
// shared a single core (the fifty-concurrent-runs plan, D6). Memory
|
|
52
|
+
// follows the CPU (Cloudflare requires >=3 GiB per vCPU → 12 GiB), and
|
|
53
|
+
// the disk follows the memory (<=2 GB per GiB → 20 GB). The disk
|
|
54
|
+
// arithmetic is over measured parts, checked by instanceSizing.test.ts
|
|
55
|
+
// against this file: the resident of a large monorepo filled the
|
|
56
|
+
// original 8 GB in 45 min (provision, one thread attached 42 min later,
|
|
57
|
+
// ENOSPC 6 min after that). Measured inside the resident
|
|
58
|
+
// (docs/reference/specs/resident-repos.md item 55): image 552 MB + mirror 380
|
|
59
|
+
// + warm checkout 2772 (history 380, tree 94, pnpm node_modules 2298) +
|
|
60
|
+
// the reserve the attach admission holds back (snapshot staging + a
|
|
61
|
+
// 1 GiB floor, 2965) = 6669 MB base; a hardlinked thread tree 555 MB; a
|
|
62
|
+
// thread whose lockfile differs 2853 MB; 20 000 MB mounts as ~18 858 MB
|
|
63
|
+
// usable → 16 hardlinked + 1 installing trees (or 12 + 2) fit; the
|
|
64
|
+
// pool's 16 + 2 does not fit even this ceiling, so the disk stays a
|
|
65
|
+
// BUDGET (residentDiskBudget.ts): the next tree past it is refused
|
|
66
|
+
// `disk-pressure` and runs cold, never ENOSPC. The pnpm store holds no
|
|
67
|
+
// second copy of the deps (measured).
|
|
68
|
+
//
|
|
69
|
+
// Cost: vCPU bills on ACTIVE use only; memory and disk bill on the
|
|
70
|
+
// provisioned size while awake, and a resident is effectively always-on
|
|
71
|
+
// (10-min refresh alarm < 20m sleepAfter) — about $83/mo provisioned per
|
|
72
|
+
// resident (memory ~$79 + disk ~$4) vs ~$55 at 8 GiB / 16 GB, plus vCPU
|
|
73
|
+
// when the cores are busy (up to ~$210/mo with all four pinned, which
|
|
74
|
+
// under load they are meant to be). History: standard-1 (½ vCPU / 4 GiB
|
|
75
|
+
// / 8 GB) → 1 vCPU (a resident pinned at 100% of ½ vCPU, memory fine) →
|
|
76
|
+
// 8 GiB for the 16 GB of disk it unlocks → 4 vCPU / 12 GiB / 20 GB for
|
|
77
|
+
// 16 concurrent seats. That monorepo's console build asks for a 16 GB
|
|
78
|
+
// node heap, so if `build` ever becomes a real command this is still
|
|
79
|
+
// not the ceiling. Custom-type constraints: whole vCPUs 1-4, >=3 GiB per
|
|
80
|
+
// vCPU, <=2 GB disk per GiB, at most 12 GiB / 20 GB. (The thread-sandbox
|
|
81
|
+
// Worker is the predefined standard-4 — the same 4 vCPU / 12 GiB / 20 GB,
|
|
82
|
+
// reached from the other side: one cold thread must be able to typecheck
|
|
83
|
+
// a large monorepo alone — and it is awake only for one run.)
|
|
84
|
+
"instance_type": { "vcpu": 4, "memory_mib": 12288, "disk_mb": 20000 },
|
|
85
|
+
// Platform ceiling only. The enforced cap is RESIDENT_CAP (6) in
|
|
86
|
+
// worker.ts — deliberately below this so an onboard is always refused
|
|
87
|
+
// atomically by the registry, never by a platform scheduling failure.
|
|
88
|
+
"max_instances": 10
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
"durable_objects": {
|
|
92
|
+
"bindings": [
|
|
93
|
+
// One ResidentDO (Sandbox subclass = container) per onboarded resource;
|
|
94
|
+
// the DO name IS the resource id ("repo:<slug>").
|
|
95
|
+
{ "name": "RESIDENT", "class_name": "ResidentDO" },
|
|
96
|
+
// Singleton registry: onboarded set + command table + cap enforcement.
|
|
97
|
+
{ "name": "REGISTRY", "class_name": "ResidentRegistryDO" }
|
|
98
|
+
]
|
|
99
|
+
},
|
|
100
|
+
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["ResidentDO", "ResidentRegistryDO"] }],
|
|
101
|
+
// Resident cache + snapshots. The binding name BACKUP_BUCKET is load-bearing:
|
|
102
|
+
// metadata reads and the offboard sweep go through it, and the SDK's
|
|
103
|
+
// local-bucket mode (createBackup({ localBucket: true })) moves the archive
|
|
104
|
+
// bytes through it too — via the Durable Object, which is what put a 128 MB
|
|
105
|
+
// isolate in the data path of a 1.16 GB restore (item 61). Production
|
|
106
|
+
// mode is presigned: the CLOUDFLARE_ACCOUNT_ID / BACKUP_BUCKET_NAME vars
|
|
107
|
+
// above plus the R2_ACCESS_KEY_ID / R2_SECRET_ACCESS_KEY secrets let the
|
|
108
|
+
// container move the bytes itself; with any of the four absent the Worker
|
|
109
|
+
// falls back to local mode and says so on /healthz. The bucket is named after
|
|
110
|
+
// this Worker's script (`<script>-cache`), so two installations on one
|
|
111
|
+
// account never share one — and BACKUP_BUCKET_NAME must say the same name.
|
|
112
|
+
"r2_buckets": [{ "binding": "BACKUP_BUCKET", "bucket_name": "{{script}}-cache" }],
|
|
113
|
+
// Watchdog cadence: every 10 minutes, strictly BELOW the container
|
|
114
|
+
// sleep window (SLEEP_AFTER = "20m" in worker.ts) so a healthy resident is
|
|
115
|
+
// re-warmed before the platform can ever sleep it. Invariant: watchdog/alarm
|
|
116
|
+
// cadence < sleepAfter — bump the two together. This list must equal the
|
|
117
|
+
// `resident` entries of the schedule registry (src/core/schedules.ts) — a
|
|
118
|
+
// unit test enforces it; change the registry first.
|
|
119
|
+
"triggers": { "crons": ["*/10 * * * *"] }
|
|
120
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Sandbox container image: the @cloudflare/sandbox base plus git + gh, so the
|
|
2
|
+
# coding/review agents work out of the box.
|
|
3
|
+
# The tag MUST match the @cloudflare/sandbox version in package.json exactly
|
|
4
|
+
# (no 'latest' tag exists) — bump both together.
|
|
5
|
+
FROM docker.io/cloudflare/sandbox:0.12.9
|
|
6
|
+
USER root
|
|
7
|
+
# The SDK's container server kills any command after COMMAND_TIMEOUT_MS. On
|
|
8
|
+
# 0.12.x the default is unlimited (unset → no limit; 0.3.x defaulted to 30s),
|
|
9
|
+
# a per-exec `timeout` option wins over it, and a kill by it lands as a normal
|
|
10
|
+
# result with a synthetic exit code (143/137), not a rejection. worker.ts
|
|
11
|
+
# passes no per-call timeout, so this env var is the container-level lever.
|
|
12
|
+
# Pure BACKSTOP: the real per-command limit is coreutils `timeout` in
|
|
13
|
+
# worker.ts (280s default, raisable per call via the bash tool's timeoutMs up
|
|
14
|
+
# to the 20-min ceiling BASH_TIMEOUT_MAX_MS), which yields a genuine exit 124
|
|
15
|
+
# through the normal result path. This sits 40s above the 20-min ceiling so it
|
|
16
|
+
# never wins the race. Bump alongside BASH_TIMEOUT_MAX_MS
|
|
17
|
+
# (src/execution/bashTimeout.ts); changing it requires a container rebuild +
|
|
18
|
+
# redeploy.
|
|
19
|
+
ENV COMMAND_TIMEOUT_MS=1240000
|
|
20
|
+
# docker.io + iptables: a Docker engine for runs whose tests need containers
|
|
21
|
+
# (act's default job mode, testcontainers, compose, image builds). Nothing
|
|
22
|
+
# starts the daemon at boot — the wrapper below does, on the first `docker`
|
|
23
|
+
# call. The sandbox is already root with full capabilities inside its own
|
|
24
|
+
# microVM, so a container a run starts shares that run's blast radius and
|
|
25
|
+
# nothing more; no privileged flag or capability is added anywhere.
|
|
26
|
+
RUN apt-get update \
|
|
27
|
+
&& apt-get install -y --no-install-recommends git curl ca-certificates docker.io iptables \
|
|
28
|
+
&& curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
|
|
29
|
+
-o /usr/share/keyrings/githubcli-archive-keyring.gpg \
|
|
30
|
+
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
|
|
31
|
+
> /etc/apt/sources.list.d/github-cli.list \
|
|
32
|
+
&& apt-get update && apt-get install -y --no-install-recommends gh \
|
|
33
|
+
&& rm -rf /var/lib/apt/lists/* \
|
|
34
|
+
&& git config --system credential.helper '!gh auth git-credential' \
|
|
35
|
+
&& git config --system user.name "switchboard-bot" \
|
|
36
|
+
&& git config --system user.email "switchboard-bot@users.noreply.github.com"
|
|
37
|
+
|
|
38
|
+
# `docker` resolves to this wrapper (/usr/local/bin precedes /usr/bin on PATH):
|
|
39
|
+
# it starts dockerd in its own session on first use — a plain background
|
|
40
|
+
# process would die with the exec's process group — waits for the engine, then
|
|
41
|
+
# execs the real client. src/deploy/sandboxDocker.test.ts holds its shape.
|
|
42
|
+
COPY --chmod=0755 docker-wrapper.sh /usr/local/bin/docker
|
|
43
|
+
|
|
44
|
+
# Node comes from the base: the published 0.12.9 image ships Node 22.23 (copied
|
|
45
|
+
# from node:22-slim, with npm; no NodeSource deb), so the retarget-and-upgrade
|
|
46
|
+
# layer the 0.3.x image needed is gone. This repo pins `engines: node
|
|
47
|
+
# >=22` (undici@8 needs Node 22's `webidl.util.markAsUncloneable`), and a
|
|
48
|
+
# review run that falls back off the resident onto a fresh sandbox must load
|
|
49
|
+
# the full vitest suite; the assertion fails the build if a base bump ever
|
|
50
|
+
# lands below 22. (The resident image gets Node 24 from its 0.13.0-next base.)
|
|
51
|
+
RUN node --version | grep -Eq '^v(2[2-9]|[3-9][0-9])\.'
|
|
52
|
+
|
|
53
|
+
# pnpm: the base image ships Node + npm (corepack is under
|
|
54
|
+
# /usr/local/lib/node_modules but not on PATH) and not pnpm, so coding runs in pnpm repos hit
|
|
55
|
+
# `pnpm: command not found` on install/build. Installed globally with npm, the
|
|
56
|
+
# same way the resident image does it, so this version is here before any run
|
|
57
|
+
# needs it. It is not necessarily the version that RUNS: pnpm ≥10 honours a
|
|
58
|
+
# repo's own `packageManager` field and fetches it, so a pnpm repo that pins
|
|
59
|
+
# one still needs the registry on its first install.
|
|
60
|
+
#
|
|
61
|
+
# EXACT version, enforced by src/deploy/imagePins.test.ts, same pin and same
|
|
62
|
+
# reason as the resident image (deploy/cloudflare-resident/Dockerfile): with
|
|
63
|
+
# `@latest` here the pnpm major moved on a rebuild, and pnpm 11 stopped
|
|
64
|
+
# reading `package.json`'s `pnpm` field. pnpm still honours a repo's own
|
|
65
|
+
# `packageManager`, so this is the floor for repos that name none.
|
|
66
|
+
RUN npm install -g pnpm@10.34.5 \
|
|
67
|
+
&& pnpm --version | grep -qx '10\.34\.5'
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# docker(1) for the cold sandbox: starts the engine on first use, then hands
|
|
3
|
+
# over to the real client. Installed as /usr/local/bin/docker, ahead of
|
|
4
|
+
# /usr/bin/docker on PATH, so a run's `docker …` needs no setup step.
|
|
5
|
+
#
|
|
6
|
+
# Why lazy: the image ships dockerd but nothing starts it — the container has
|
|
7
|
+
# no init; the SDK's server only runs the commands it is sent.
|
|
8
|
+
# Why setsid: every /exec runs under `timeout -k 10 … bash -c`, whose process
|
|
9
|
+
# group is reaped when the command returns. A daemon started with `nohup … &`
|
|
10
|
+
# dies with it; one in its own session survives to the next command.
|
|
11
|
+
set -u
|
|
12
|
+
REAL=/usr/bin/docker
|
|
13
|
+
LOG=/var/log/dockerd.log
|
|
14
|
+
|
|
15
|
+
daemon_ready() { "$REAL" info >/dev/null 2>&1; }
|
|
16
|
+
|
|
17
|
+
if [ ! -S /var/run/docker.sock ] || ! daemon_ready; then
|
|
18
|
+
# Containers reach the network through the default bridge, which needs the
|
|
19
|
+
# kernel to forward between it and the outside interface.
|
|
20
|
+
sysctl -w net.ipv4.ip_forward=1 >/dev/null 2>&1 || echo 1 >/proc/sys/net/ipv4/ip_forward
|
|
21
|
+
# Two concurrent first calls both start a daemon; the second exits on the
|
|
22
|
+
# pid lock and both wait below for the one that won.
|
|
23
|
+
setsid -f dockerd >"$LOG" 2>&1
|
|
24
|
+
# The bridge and the socket take a few seconds on a fresh microVM. 40 s is
|
|
25
|
+
# generous, and a broken engine is reported with its log instead of hanging.
|
|
26
|
+
waited=0
|
|
27
|
+
until daemon_ready; do
|
|
28
|
+
waited=$((waited + 1))
|
|
29
|
+
if [ "$waited" -ge 40 ]; then
|
|
30
|
+
echo "docker: the engine did not come up within 40 s — see $LOG" >&2
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
sleep 1
|
|
34
|
+
done
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
exec "$REAL" "$@"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "switchboard-sandbox-worker",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"engines": {
|
|
6
|
+
"node": ">=22"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"check:image": "docker build --quiet .",
|
|
10
|
+
"deploy": "node ../bin/build-stamp.mjs",
|
|
11
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
12
|
+
"verify": "npm --prefix ../.. run --silent deploy:gen && npm run typecheck",
|
|
13
|
+
"dev": "wrangler dev",
|
|
14
|
+
"typegen": "wrangler types",
|
|
15
|
+
"tail": "wrangler tail",
|
|
16
|
+
"secrets": "npm --prefix ../.. run --silent cli -- deploy secrets sandbox"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@cloudflare/sandbox": "0.12.9"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@cloudflare/workers-types": "^5.20260904.1",
|
|
23
|
+
"typescript": "^5.9.3",
|
|
24
|
+
"wrangler": "^4.129.0"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Scoped config so `npx tsc --noEmit` here actually checks worker.ts —
|
|
3
|
+
// without it, tsc silently falls back to the repo-root tsconfig, which
|
|
4
|
+
// excludes this directory entirely.
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"target": "esnext",
|
|
7
|
+
"module": "esnext",
|
|
8
|
+
"moduleResolution": "bundler",
|
|
9
|
+
"lib": ["esnext"],
|
|
10
|
+
"types": ["@cloudflare/workers-types"],
|
|
11
|
+
"strict": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
// worker.ts imports its own package.json for the @cloudflare/sandbox pin
|
|
15
|
+
// (the version onStart compares the container against); wrangler bundles
|
|
16
|
+
// the JSON import, tsc needs this to type it.
|
|
17
|
+
"resolveJsonModule": true
|
|
18
|
+
},
|
|
19
|
+
"include": ["worker.ts"]
|
|
20
|
+
}
|