@runcofactor/cli 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{account-ZMV7NEFM.js → account-ZSM2Q3RB.js} +4 -4
- package/dist/{api-catalog-6HNOGVPA.js → api-catalog-MMPFWS52.js} +4 -4
- package/dist/{attachment-QAHR54HQ.js → attachment-NSYL5X3S.js} +4 -4
- package/dist/{augmentor-7T3L6ZR4.js → augmentor-LSJLMJYP.js} +4 -4
- package/dist/{auth-DURHUZDW.js → auth-OYF5A4BY.js} +4 -2
- package/dist/{chunk-RGDIVW3O.js → chunk-2R6544Z3.js} +2 -2
- package/dist/{chunk-AI3Y3AZH.js → chunk-7OO3RJYR.js} +1396 -185
- package/dist/chunk-7OO3RJYR.js.map +1 -0
- package/dist/{chunk-4NRDONSC.js → chunk-A6TH5VUI.js} +209 -79
- package/dist/chunk-A6TH5VUI.js.map +1 -0
- package/dist/{chunk-CEXV3MOC.js → chunk-E2FUQJVO.js} +4 -4
- package/dist/{chunk-D6AVOPOS.js → chunk-GFP4UEUT.js} +3 -3
- package/dist/{chunk-G5SXUXSU.js → chunk-H7ZML5QL.js} +2 -2
- package/dist/{chunk-LFFWD5JZ.js → chunk-IHCPMYKU.js} +3 -3
- package/dist/{chunk-UAJUZLDE.js → chunk-PVWG3V62.js} +3 -3
- package/dist/{chunk-3CZU7XCS.js → chunk-PWZ4ULCB.js} +20 -14
- package/dist/chunk-PWZ4ULCB.js.map +1 -0
- package/dist/{chunk-A4ISG3VX.js → chunk-U7OMQJ7V.js} +52 -4
- package/dist/chunk-U7OMQJ7V.js.map +1 -0
- package/dist/{chunk-DT2CEH2V.js → chunk-VHVOTGBQ.js} +3 -3
- package/dist/{chunk-IGHDIWZU.js → chunk-VYTIJ6HJ.js} +17 -2
- package/dist/{chunk-IGHDIWZU.js.map → chunk-VYTIJ6HJ.js.map} +1 -1
- package/dist/{credential-X6QKX473.js → credential-WOATCWOU.js} +4 -4
- package/dist/{credential-template-GD3D4MBI.js → credential-template-Q3DQE2AH.js} +4 -4
- package/dist/{debug-workflow-AAEOAME2.js → debug-workflow-ZNP5XZQF.js} +4 -4
- package/dist/{dist-QLTVBUMD.js → dist-4COOAKY3.js} +22 -2
- package/dist/{docs-BVUEXBY3.js → docs-XVU4LA7G.js} +4 -4
- package/dist/{doctor-RHMKD4W5.js → doctor-PGNHD3TI.js} +3 -3
- package/dist/{environment-WE7VVE6I.js → environment-MRDNWU47.js} +4 -4
- package/dist/{feature-flag-VJO2TJFT.js → feature-flag-D3DQZ6DX.js} +4 -4
- package/dist/index.js +51 -48
- package/dist/index.js.map +1 -1
- package/dist/{init-RQHHKJJD.js → init-DHWKUYIR.js} +4 -4
- package/dist/{install-CQLHNZ6U.js → install-DMEFGQHD.js} +4 -4
- package/dist/{issues-HK2JA5JS.js → issues-E4YQ4DVC.js} +4 -4
- package/dist/{knobs-ETJAV6YM.js → knobs-NA6SPLHX.js} +4 -4
- package/dist/{list-F6DGGW3Z.js → list-GJNGFQMC.js} +4 -4
- package/dist/{local-CMAY54I5.js → local-NSKOZHHA.js} +6 -6
- package/dist/{login-6CKG664C.js → login-AMSRVPXG.js} +4 -4
- package/dist/{mcp-GC2WOOZO.js → mcp-57VVZTXM.js} +5 -5
- package/dist/{metrics-IXICURAA.js → metrics-DM4YQQZN.js} +4 -4
- package/dist/{product-BUJWPYKB.js → product-WGOLTBRI.js} +4 -4
- package/dist/{property-4EXPWRNT.js → property-B7QMGLVW.js} +4 -4
- package/dist/{psql-XGLBI6T7.js → psql-4JHPJ2CO.js} +3 -3
- package/dist/{record-HL4QA7OX.js → record-RWVGZZSW.js} +5 -5
- package/dist/{redis-VJGCUJPV.js → redis-N44ZZ6VB.js} +3 -3
- package/dist/{release-2SMK5Y3V.js → release-R7GKLEAI.js} +4 -4
- package/dist/{sandbox-ORAPKOH3.js → sandbox-6D54ZQCG.js} +5 -5
- package/dist/{scenarios-DM5FDCAF.js → scenarios-WUUUKP2S.js} +4 -4
- package/dist/{session-X6IYGWYG.js → session-GDCMTNB3.js} +6 -6
- package/dist/{skill-ZU5V2EE5.js → skill-3SP7MAA4.js} +153 -2
- package/dist/skill-3SP7MAA4.js.map +1 -0
- package/dist/skills/optimize-cache/SKILL.md +25 -0
- package/dist/{spec-KO5QD6AY.js → spec-ESPA3H26.js} +3 -3
- package/dist/{src-DSD34JIX.js → src-5YXGM2KB.js} +18 -2
- package/dist/src-5YXGM2KB.js.map +1 -0
- package/dist/{src-PDYXACRP.js → src-B7CPYFLS.js} +4 -2
- package/dist/{src-HTGNKBUF.js → src-GRO2QR7U.js} +61 -23
- package/dist/{src-HTGNKBUF.js.map → src-GRO2QR7U.js.map} +1 -1
- package/dist/{start-2ECN4LX5.js → start-KDDRLUFN.js} +4 -4
- package/dist/{tag-R5I72HJ6.js → tag-DAGHQZAR.js} +5 -5
- package/dist/{task-V3JMSJIM.js → task-PRR75HI4.js} +4 -4
- package/dist/{test-suite-NOYVK6YX.js → test-suite-O3UY7EER.js} +6 -6
- package/dist/{trace-NOYEKNO4.js → trace-7LRHSPWN.js} +4 -4
- package/dist/{triage-4RFATHBE.js → triage-2BKUBMMX.js} +5 -5
- package/dist/{utilities-3WMXE743.js → utilities-O3OO64T5.js} +4 -4
- package/dist/{workflow-DC7WOTTW.js → workflow-5BFRS4CE.js} +10 -11
- package/dist/workflow-5BFRS4CE.js.map +1 -0
- package/dist/{workflow-cache-SDSI6SMF.js → workflow-cache-VVP3SPFM.js} +7 -7
- package/dist/{workflow-debug-6UQYP67Q.js → workflow-debug-XGOHNZ4E.js} +7 -7
- package/dist/{workflow-proposals-67AVGMHX.js → workflow-proposals-PGURJ6ZI.js} +4 -4
- package/package.json +5 -5
- package/dist/chunk-3CZU7XCS.js.map +0 -1
- package/dist/chunk-4NRDONSC.js.map +0 -1
- package/dist/chunk-A4ISG3VX.js.map +0 -1
- package/dist/chunk-AI3Y3AZH.js.map +0 -1
- package/dist/skill-ZU5V2EE5.js.map +0 -1
- package/dist/src-DSD34JIX.js.map +0 -1
- package/dist/workflow-DC7WOTTW.js.map +0 -1
- /package/dist/{account-ZMV7NEFM.js.map → account-ZSM2Q3RB.js.map} +0 -0
- /package/dist/{api-catalog-6HNOGVPA.js.map → api-catalog-MMPFWS52.js.map} +0 -0
- /package/dist/{attachment-QAHR54HQ.js.map → attachment-NSYL5X3S.js.map} +0 -0
- /package/dist/{augmentor-7T3L6ZR4.js.map → augmentor-LSJLMJYP.js.map} +0 -0
- /package/dist/{auth-DURHUZDW.js.map → auth-OYF5A4BY.js.map} +0 -0
- /package/dist/{chunk-RGDIVW3O.js.map → chunk-2R6544Z3.js.map} +0 -0
- /package/dist/{chunk-CEXV3MOC.js.map → chunk-E2FUQJVO.js.map} +0 -0
- /package/dist/{chunk-D6AVOPOS.js.map → chunk-GFP4UEUT.js.map} +0 -0
- /package/dist/{chunk-G5SXUXSU.js.map → chunk-H7ZML5QL.js.map} +0 -0
- /package/dist/{chunk-LFFWD5JZ.js.map → chunk-IHCPMYKU.js.map} +0 -0
- /package/dist/{chunk-UAJUZLDE.js.map → chunk-PVWG3V62.js.map} +0 -0
- /package/dist/{chunk-DT2CEH2V.js.map → chunk-VHVOTGBQ.js.map} +0 -0
- /package/dist/{credential-X6QKX473.js.map → credential-WOATCWOU.js.map} +0 -0
- /package/dist/{credential-template-GD3D4MBI.js.map → credential-template-Q3DQE2AH.js.map} +0 -0
- /package/dist/{debug-workflow-AAEOAME2.js.map → debug-workflow-ZNP5XZQF.js.map} +0 -0
- /package/dist/{dist-QLTVBUMD.js.map → dist-4COOAKY3.js.map} +0 -0
- /package/dist/{docs-BVUEXBY3.js.map → docs-XVU4LA7G.js.map} +0 -0
- /package/dist/{doctor-RHMKD4W5.js.map → doctor-PGNHD3TI.js.map} +0 -0
- /package/dist/{environment-WE7VVE6I.js.map → environment-MRDNWU47.js.map} +0 -0
- /package/dist/{feature-flag-VJO2TJFT.js.map → feature-flag-D3DQZ6DX.js.map} +0 -0
- /package/dist/{init-RQHHKJJD.js.map → init-DHWKUYIR.js.map} +0 -0
- /package/dist/{install-CQLHNZ6U.js.map → install-DMEFGQHD.js.map} +0 -0
- /package/dist/{issues-HK2JA5JS.js.map → issues-E4YQ4DVC.js.map} +0 -0
- /package/dist/{knobs-ETJAV6YM.js.map → knobs-NA6SPLHX.js.map} +0 -0
- /package/dist/{list-F6DGGW3Z.js.map → list-GJNGFQMC.js.map} +0 -0
- /package/dist/{local-CMAY54I5.js.map → local-NSKOZHHA.js.map} +0 -0
- /package/dist/{login-6CKG664C.js.map → login-AMSRVPXG.js.map} +0 -0
- /package/dist/{mcp-GC2WOOZO.js.map → mcp-57VVZTXM.js.map} +0 -0
- /package/dist/{metrics-IXICURAA.js.map → metrics-DM4YQQZN.js.map} +0 -0
- /package/dist/{product-BUJWPYKB.js.map → product-WGOLTBRI.js.map} +0 -0
- /package/dist/{property-4EXPWRNT.js.map → property-B7QMGLVW.js.map} +0 -0
- /package/dist/{psql-XGLBI6T7.js.map → psql-4JHPJ2CO.js.map} +0 -0
- /package/dist/{record-HL4QA7OX.js.map → record-RWVGZZSW.js.map} +0 -0
- /package/dist/{redis-VJGCUJPV.js.map → redis-N44ZZ6VB.js.map} +0 -0
- /package/dist/{release-2SMK5Y3V.js.map → release-R7GKLEAI.js.map} +0 -0
- /package/dist/{sandbox-ORAPKOH3.js.map → sandbox-6D54ZQCG.js.map} +0 -0
- /package/dist/{scenarios-DM5FDCAF.js.map → scenarios-WUUUKP2S.js.map} +0 -0
- /package/dist/{session-X6IYGWYG.js.map → session-GDCMTNB3.js.map} +0 -0
- /package/dist/{spec-KO5QD6AY.js.map → spec-ESPA3H26.js.map} +0 -0
- /package/dist/{src-PDYXACRP.js.map → src-B7CPYFLS.js.map} +0 -0
- /package/dist/{start-2ECN4LX5.js.map → start-KDDRLUFN.js.map} +0 -0
- /package/dist/{tag-R5I72HJ6.js.map → tag-DAGHQZAR.js.map} +0 -0
- /package/dist/{task-V3JMSJIM.js.map → task-PRR75HI4.js.map} +0 -0
- /package/dist/{test-suite-NOYVK6YX.js.map → test-suite-O3UY7EER.js.map} +0 -0
- /package/dist/{trace-NOYEKNO4.js.map → trace-7LRHSPWN.js.map} +0 -0
- /package/dist/{triage-4RFATHBE.js.map → triage-2BKUBMMX.js.map} +0 -0
- /package/dist/{utilities-3WMXE743.js.map → utilities-O3OO64T5.js.map} +0 -0
- /package/dist/{workflow-cache-SDSI6SMF.js.map → workflow-cache-VVP3SPFM.js.map} +0 -0
- /package/dist/{workflow-debug-6UQYP67Q.js.map → workflow-debug-XGOHNZ4E.js.map} +0 -0
- /package/dist/{workflow-proposals-67AVGMHX.js.map → workflow-proposals-PGURJ6ZI.js.map} +0 -0
|
@@ -134,32 +134,38 @@ Before writing any artifacts, research the application thoroughly.
|
|
|
134
134
|
where the dependency is declared. Bun workspace module resolution won't
|
|
135
135
|
find deps from the monorepo root.
|
|
136
136
|
|
|
137
|
-
5.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
137
|
+
5. PRODUCTION BUILD STRATEGY (frontends)
|
|
138
|
+
Sandboxes serve a PRODUCTION build of the frontend, not a dev server.
|
|
139
|
+
This is faster and far more responsive, ships compressed/hashed/cacheable
|
|
140
|
+
assets, and sidesteps dev-server host-allowlist headaches entirely (a static
|
|
141
|
+
file server has no notion of "allowed hosts"). HMR / hot-reload is
|
|
142
|
+
intentionally dropped \u2014 a sandbox demos and QAs a BUILT app; it is not a
|
|
143
|
+
live-edit environment.
|
|
144
|
+
|
|
145
|
+
Identify, from package.json scripts and the framework:
|
|
146
|
+
- The production build command (vite build, next build, ng build,
|
|
147
|
+
webpack --mode production, etc.) and its output directory
|
|
148
|
+
(commonly dist/, build/, or .next/).
|
|
149
|
+
- Whether the framework produces a STATIC bundle (Vite, CRA, Angular,
|
|
150
|
+
plain Webpack \u2192 a dist/ of static files) or needs a NODE SERVER at
|
|
151
|
+
runtime (Next.js server mode, Remix, Nuxt SSR). Static bundles are
|
|
152
|
+
served by nginx; server-rendered apps run their production start
|
|
153
|
+
command (next start, etc.) as a normal routed service.
|
|
154
|
+
|
|
155
|
+
BUILD-TIME ENV VARS:
|
|
156
|
+
Bundlers BAKE build-time public vars into the output, so they must be set
|
|
157
|
+
in the environment BEFORE the build runs \u2014 setting them only in compose
|
|
158
|
+
at runtime is too late for a static bundle. Find every build-time-public
|
|
159
|
+
var (VITE_*, NEXT_PUBLIC_*, REACT_APP_*, ng-injected config, etc.),
|
|
160
|
+
especially the browser-facing API base URL, and set them just before the
|
|
161
|
+
build using \${SANDBOX_URL} for any URL the browser must reach
|
|
162
|
+
(e.g. VITE_API_URL=\${SANDBOX_URL}/api). See Artifacts \u2192 PRODUCTION BUILD
|
|
163
|
+
for where the build runs.
|
|
164
|
+
|
|
165
|
+
COST: production builds are slower and more memory-hungry than starting a
|
|
166
|
+
dev server. Vite/webpack builds can OOM the default Node heap on large
|
|
167
|
+
apps \u2014 be ready to raise it (NODE_OPTIONS=--max-old-space-size=8192) and
|
|
168
|
+
expect the build to add minutes to the build step.
|
|
163
169
|
|
|
164
170
|
6. WORKERS & DAEMONS TO DISABLE
|
|
165
171
|
Sandbox typically only needs the API server + web frontend.
|
|
@@ -295,7 +301,10 @@ DOCKERFILE (type: dockerfile)
|
|
|
295
301
|
|
|
296
302
|
Rules:
|
|
297
303
|
- NO "COPY . ." \u2014 code comes via volume mount at /workspace
|
|
298
|
-
- NO "RUN npm build" \u2014
|
|
304
|
+
- NO "RUN npm build" \u2014 the production build runs on the instance as a
|
|
305
|
+
compose service (see PRODUCTION BUILD), where \${SANDBOX_URL} and
|
|
306
|
+
build-time env are available. Baking it into the image would freeze
|
|
307
|
+
build-time env to image-build values.
|
|
299
308
|
- Include ALL system deps (chromium, ffmpeg, python, etc.)
|
|
300
309
|
- For browser automation: use mcr.microsoft.com/playwright as base
|
|
301
310
|
- Do NOT use ENTRYPOINT \u2014 it breaks one-shot services (deps, migrate) whose
|
|
@@ -308,8 +317,14 @@ DOCKER COMPOSE (type: docker-compose)
|
|
|
308
317
|
deps (one-shot) \u2014 installs dependencies into named volume, restart: "no"
|
|
309
318
|
database \u2014 official image with healthcheck (postgres, mysql, etc.)
|
|
310
319
|
cache \u2014 redis/memcached with healthcheck
|
|
311
|
-
|
|
312
|
-
|
|
320
|
+
web-build (one-shot) \u2014 runs the production frontend build into the bind-mounted
|
|
321
|
+
repo tree, restart: "no" (see PRODUCTION BUILD)
|
|
322
|
+
app-api \u2014 application server, PRODUCTION command (NODE_ENV=production,
|
|
323
|
+
no --hot / --watch)
|
|
324
|
+
app-web \u2014 static frontend served by nginx:alpine from the build
|
|
325
|
+
output (see PRODUCTION BUILD). SSR frameworks (Next.js
|
|
326
|
+
server mode, etc.) run their production start command
|
|
327
|
+
instead of an nginx static server.
|
|
313
328
|
|
|
314
329
|
Do NOT use a migrate service in compose. If the migrate container fails once
|
|
315
330
|
(bad SQL, missing env var, wrong path), depends_on with
|
|
@@ -327,7 +342,7 @@ DOCKER COMPOSE (type: docker-compose)
|
|
|
327
342
|
app-web:
|
|
328
343
|
labels:
|
|
329
344
|
sandbox.route: "/"
|
|
330
|
-
sandbox.port: "
|
|
345
|
+
sandbox.port: "80"
|
|
331
346
|
|
|
332
347
|
The platform reads these labels and generates nginx config on "build".
|
|
333
348
|
If no labels, all traffic routes to the first non-infrastructure service.
|
|
@@ -344,7 +359,7 @@ DOCKER COMPOSE (type: docker-compose)
|
|
|
344
359
|
- Platform-injected: VITE_API_URL=\${SANDBOX_URL}/api
|
|
345
360
|
- Disabled: WORKER_ENABLED=false
|
|
346
361
|
|
|
347
|
-
SANDBOX_URL is auto-injected by the platform (e.g. https://<id>.env.
|
|
362
|
+
SANDBOX_URL is auto-injected by the platform (e.g. https://<id>.env.runcofactor.com).
|
|
348
363
|
Use it for any URL a browser needs to reach the sandbox.
|
|
349
364
|
|
|
350
365
|
Rules:
|
|
@@ -362,6 +377,109 @@ DOCKER COMPOSE (type: docker-compose)
|
|
|
362
377
|
them for the shell (e.g. command: ["bash", "-c", "exec foo $$BAR"]).
|
|
363
378
|
Unescaped \${VAR} is substituted at compose-time from the env file.
|
|
364
379
|
|
|
380
|
+
PRODUCTION BUILD (static frontends)
|
|
381
|
+
Serve the frontend as a production build, not a dev server. Two pieces:
|
|
382
|
+
|
|
383
|
+
1. BUILD as a one-shot compose service \u2014 in its OWN container, NOT inside the
|
|
384
|
+
api/app container. A production build is CPU + disk-I/O heavy; if it runs
|
|
385
|
+
inside the api container it starves the api's /health endpoint and the
|
|
386
|
+
provision health gate fails the whole sandbox (nginx 502). The setup script
|
|
387
|
+
can't do it either (it runs on the HOST, where app runtimes don't exist, and
|
|
388
|
+
before deps installs). So: a dedicated one-shot service that depends on deps.
|
|
389
|
+
|
|
390
|
+
Build the RIGHT set of packages. In a monorepo, build EVERY workspace
|
|
391
|
+
package that exports from dist/ that is imported by BOTH the frontend AND
|
|
392
|
+
the BACKEND \u2014 not just the UI. If the api imports a dist-exporting package
|
|
393
|
+
whose dist is missing, the api crash-loops "Cannot find module @scope/pkg"
|
|
394
|
+
and never answers /health. Find them: grep the api's imports for @scope/*
|
|
395
|
+
and cross-check each package.json "main"/"exports" for "./dist/". Build the
|
|
396
|
+
leaf packages first, then the app:
|
|
397
|
+
web-build:
|
|
398
|
+
build: { context: ., dockerfile: .canary/Dockerfile.sandbox }
|
|
399
|
+
working_dir: /app
|
|
400
|
+
volumes:
|
|
401
|
+
- .:/app
|
|
402
|
+
environment:
|
|
403
|
+
VITE_API_URL: \${SANDBOX_URL}/api # build-time public vars FIRST
|
|
404
|
+
NODE_OPTIONS: --max-old-space-size=8192 # raise heap if the build OOMs
|
|
405
|
+
# nice -n 19 keeps the api responsive if anything overlaps. Build the
|
|
406
|
+
# api's dist-dependencies AND the ui, then the app bundle.
|
|
407
|
+
command: ["bash","-c","nice -n 19 bash -c 'cd /app/packages/foo && bun run build && cd /app/packages/bar && bun run build && cd /app/apps/web && bun run build'"]
|
|
408
|
+
depends_on:
|
|
409
|
+
deps:
|
|
410
|
+
condition: service_completed_successfully
|
|
411
|
+
restart: "no"
|
|
412
|
+
Compose interpolates \${SANDBOX_URL} at "up" time, so the bundle bakes the
|
|
413
|
+
correct browser-facing API URL. The output (dist/) lands in the
|
|
414
|
+
bind-mounted repo tree, where the nginx service below mounts it.
|
|
415
|
+
|
|
416
|
+
SERIALIZE the build BEFORE the health gate: give the nginx service a
|
|
417
|
+
depends_on web-build (service_completed_successfully). That makes
|
|
418
|
+
"docker compose up" block until the build finishes, so the gate only probes
|
|
419
|
+
the api once the host is idle. Restart the api in the STARTUP script
|
|
420
|
+
afterwards so it boots onto the idle host with all dist packages present.
|
|
421
|
+
|
|
422
|
+
2. SERVE the output with a lightweight nginx:alpine service that depends on
|
|
423
|
+
the build completing. Mount the PARENT of the output dir (e.g. apps/web),
|
|
424
|
+
NOT the dist dir directly, and point nginx root at the dist SUBPATH:
|
|
425
|
+
app-web:
|
|
426
|
+
image: nginx:alpine
|
|
427
|
+
volumes:
|
|
428
|
+
- ./apps/web:/srv:ro # mount the PARENT, root is /srv/dist
|
|
429
|
+
- ./.canary/nginx-spa.conf:/etc/nginx/conf.d/default.conf:ro
|
|
430
|
+
depends_on:
|
|
431
|
+
web-build:
|
|
432
|
+
condition: service_completed_successfully
|
|
433
|
+
labels:
|
|
434
|
+
sandbox.route: "/"
|
|
435
|
+
sandbox.port: "80"
|
|
436
|
+
|
|
437
|
+
WHY mount the parent: bundlers (vite, webpack) DELETE and recreate the
|
|
438
|
+
output dir on build, giving it a NEW inode. Docker creates an empty
|
|
439
|
+
dist bind-source at compose-up time; a direct "dist:/usr/share/nginx/html"
|
|
440
|
+
bind pins nginx to that stale empty inode and it serves 403 "directory
|
|
441
|
+
index forbidden" forever. Mounting the parent (a stable inode) and letting
|
|
442
|
+
nginx resolve root/<uri> per request always sees the current dist.
|
|
443
|
+
|
|
444
|
+
Provide an SPA-fallback nginx config as a file artifact so client-side
|
|
445
|
+
routes (deep links / hard refresh) don't 404 \u2014 unknown paths serve
|
|
446
|
+
index.html and the client router takes over:
|
|
447
|
+
canary sandbox artifact create <template-id> file nginx-spa.conf \\
|
|
448
|
+
--path /workspace/.canary/nginx-spa.conf
|
|
449
|
+
|
|
450
|
+
nginx-spa.conf:
|
|
451
|
+
server {
|
|
452
|
+
listen 80;
|
|
453
|
+
root /srv/dist; # the dist SUBPATH of the mounted parent
|
|
454
|
+
index index.html;
|
|
455
|
+
gzip on;
|
|
456
|
+
gzip_types text/css application/javascript application/json image/svg+xml;
|
|
457
|
+
location /assets/ {
|
|
458
|
+
try_files $uri =404;
|
|
459
|
+
expires 1y;
|
|
460
|
+
add_header Cache-Control "public, immutable";
|
|
461
|
+
}
|
|
462
|
+
location / {
|
|
463
|
+
try_files $uri $uri/ /index.html;
|
|
464
|
+
add_header Cache-Control "no-cache";
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
(Match root to your framework's output dir under the mounted parent \u2014 e.g.
|
|
469
|
+
/srv/build for a build/ output. The hashed-asset location block assumes a
|
|
470
|
+
/assets/ dir \u2014 adjust to your bundler's asset path.)
|
|
471
|
+
|
|
472
|
+
WHY: nginx serving static files has no host allowlist (no "This host is not
|
|
473
|
+
allowed"), ships compressed/hashed/cacheable assets, and is far more
|
|
474
|
+
responsive than a dev server streaming hundreds of uncompressed modules. The
|
|
475
|
+
trade-off is no hot-reload and a longer, heavier build step \u2014 acceptable
|
|
476
|
+
because sandboxes demo a BUILT app.
|
|
477
|
+
|
|
478
|
+
SSR / server-rendered frameworks (Next.js server mode, Remix, Nuxt SSR):
|
|
479
|
+
skip the nginx static server. Build in the one-shot service, then run the
|
|
480
|
+
framework's production start command (e.g. next start) as a normal routed
|
|
481
|
+
service with sandbox.route "/" and its listen port.
|
|
482
|
+
|
|
365
483
|
SETUP SCRIPT (type: setup-script)
|
|
366
484
|
Runs BEFORE docker compose up on every build. Runs on the HOST, not in a container.
|
|
367
485
|
Written to /workspace/.canary/setup.sh.
|
|
@@ -462,9 +580,9 @@ FILE (type: file)
|
|
|
462
580
|
Upload with --path to specify the destination:
|
|
463
581
|
canary sandbox artifact create <template-id> file <local-file> --path /workspace/.canary/<dest>
|
|
464
582
|
|
|
465
|
-
Example \u2014
|
|
466
|
-
canary sandbox artifact create <template-id> file
|
|
467
|
-
--path /workspace/.canary/
|
|
583
|
+
Example \u2014 SPA fallback config for the static web server (see PRODUCTION BUILD):
|
|
584
|
+
canary sandbox artifact create <template-id> file nginx-spa.conf \\
|
|
585
|
+
--path /workspace/.canary/nginx-spa.conf
|
|
468
586
|
|
|
469
587
|
ONE ROW PER FILENAME. File artifacts are keyed by (template, filename).
|
|
470
588
|
Uploading a file with the same filename as an earlier upload REPLACES
|
|
@@ -529,10 +647,10 @@ COMMANDS:
|
|
|
529
647
|
|
|
530
648
|
# Test with sandbox hostname (for host-restriction issues)
|
|
531
649
|
canary sandbox run-command <instance-id> \\
|
|
532
|
-
"curl -s -H 'Host: <instance-id>.env.
|
|
650
|
+
"curl -s -H 'Host: <instance-id>.env.runcofactor.com' http://localhost:80/"
|
|
533
651
|
|
|
534
652
|
SANDBOX URL:
|
|
535
|
-
https://<instance-id>.env.
|
|
653
|
+
https://<instance-id>.env.runcofactor.com
|
|
536
654
|
|
|
537
655
|
IF SOMETHING FAILS:
|
|
538
656
|
1. Read the logs for the failing service
|
|
@@ -603,47 +721,59 @@ COMMON ISSUES:
|
|
|
603
721
|
so it resolves against the workspace's own node_modules. Replace
|
|
604
722
|
<app-service> with your template's actual service name.
|
|
605
723
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
724
|
+
Blank page \u2014 frontend never renders (served by a dev server)
|
|
725
|
+
The web service is a dev server (Vite/webpack-dev-server) shipping hundreds
|
|
726
|
+
of uncompressed, no-cache modules. On a remote sandbox these trickle in too
|
|
727
|
+
slowly for the SPA to mount, so the page stays blank. Confirm with:
|
|
728
|
+
curl -s <SANDBOX_URL>/ | grep '<script'
|
|
729
|
+
Dev mode references /@vite/client or /src/main.tsx; a production build
|
|
730
|
+
references hashed /assets/*.js. Fix: serve a production build (see
|
|
731
|
+
Artifacts \u2192 PRODUCTION BUILD), not a dev server.
|
|
732
|
+
|
|
733
|
+
Deep links / page refresh 404 (SPA routing)
|
|
734
|
+
nginx serves static files but a client-side route (e.g. /settings) or a
|
|
735
|
+
hard refresh returns 404 \u2014 the static server has no SPA fallback. Add
|
|
736
|
+
"try_files $uri $uri/ /index.html;" to the nginx config (see Artifacts \u2192
|
|
737
|
+
PRODUCTION BUILD) so unknown paths serve index.html and the client router
|
|
738
|
+
takes over.
|
|
739
|
+
|
|
740
|
+
Frontend loads but API calls hit the wrong URL / localhost
|
|
741
|
+
The browser-facing API URL was not set as a BUILD-TIME var before the
|
|
742
|
+
build, so the bundle baked a default (often localhost:3000). Static
|
|
743
|
+
bundles read build-time vars at BUILD time, not runtime \u2014 setting them
|
|
744
|
+
only in the app-web/runtime compose env is too late. Set the public var on
|
|
745
|
+
the web-build service env (or before the build command), using
|
|
746
|
+
\${SANDBOX_URL}, then rebuild:
|
|
747
|
+
VITE_API_URL: \${SANDBOX_URL}/api # or NEXT_PUBLIC_*, REACT_APP_*, etc.
|
|
748
|
+
|
|
749
|
+
API 502 / "Cannot find module '@scope/pkg'" crash-loop after going prod
|
|
750
|
+
The api imports a workspace package that exports from dist/, but that
|
|
751
|
+
package's dist was never built \u2014 so the api crash-loops and the health gate
|
|
752
|
+
fails the whole provision with a 502. Easy to miss because the FRONTEND
|
|
753
|
+
build (ui) works. Build EVERY dist-exporting package the api imports in the
|
|
754
|
+
web-build service (see Artifacts \u2192 PRODUCTION BUILD), not just the ui ones.
|
|
755
|
+
A warm AMI can hide this (it baked the dist in); a fresh provision exposes it.
|
|
756
|
+
|
|
757
|
+
403 "directory index forbidden" from the static server
|
|
758
|
+
nginx is bound to the stale empty dist inode the bundler replaced. Mount the
|
|
759
|
+
PARENT dir and set root to the dist subpath (see Artifacts \u2192 PRODUCTION
|
|
760
|
+
BUILD), or restart the nginx service after the build.
|
|
761
|
+
|
|
762
|
+
Build is killed / "JavaScript heap out of memory"
|
|
763
|
+
The production build exhausted the Node heap (common on large Vite/webpack
|
|
764
|
+
builds). Raise it on the web-build service before the build:
|
|
765
|
+
NODE_OPTIONS: --max-old-space-size=8192
|
|
766
|
+
If it still OOMs, bump the instance size (canary sandbox set-size) and/or
|
|
767
|
+
the heap further.
|
|
768
|
+
|
|
769
|
+
Stale page after a rebuild (old dist served)
|
|
770
|
+
nginx is serving a previous build because the build step didn't re-run or
|
|
771
|
+
its output dir wasn't refreshed. Confirm the web-build service ran in this
|
|
772
|
+
build cycle (canary sandbox logs <id> --service web-build), that it wrote
|
|
773
|
+
to the dir nginx mounts, and \u2014 if you ever build via the startup script
|
|
774
|
+
instead \u2014 that nginx was restarted (sb-compose restart app-web). Hashed
|
|
775
|
+
asset filenames change per build; if index.html still references old
|
|
776
|
+
hashes, the build didn't land.
|
|
647
777
|
|
|
648
778
|
run-command times out
|
|
649
779
|
Long-running operation exceeds the 55s SSM command timeout. Write a script file and run in background:
|
|
@@ -714,7 +844,7 @@ COMPLETING ITEMS:
|
|
|
714
844
|
Example:
|
|
715
845
|
canary sandbox checklist complete <template-id> environment-analyzed \\
|
|
716
846
|
--status success \\
|
|
717
|
-
--summary "Bun monorepo: Postgres, Redis. Vite
|
|
847
|
+
--summary "Bun monorepo: Postgres, Redis. Vite production build served by nginx, API on 3000."
|
|
718
848
|
|
|
719
849
|
SKIPPING ITEMS:
|
|
720
850
|
canary sandbox checklist skip <template-id> <item-id>
|
|
@@ -1045,4 +1175,4 @@ export {
|
|
|
1045
1175
|
printGuide,
|
|
1046
1176
|
SANDBOX_GUIDE_SECTIONS
|
|
1047
1177
|
};
|
|
1048
|
-
//# sourceMappingURL=chunk-
|
|
1178
|
+
//# sourceMappingURL=chunk-A6TH5VUI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../content/src/print-guide.ts","../../content/src/sandbox-guide.ts"],"sourcesContent":["import type { GuideSection } from './types.js';\n\nexport function printGuide(\n allSections: GuideSection[],\n section?: string,\n json?: boolean,\n): void {\n const sections = section\n ? allSections.filter((s) => s.slug === section)\n : allSections;\n\n if (section && sections.length === 0) {\n console.error(\n `Unknown guide section: \"${section}\". Available: ${allSections.map((s) => s.slug).join(', ')}`,\n );\n process.exit(1);\n }\n\n if (json) {\n console.log(JSON.stringify(sections, null, 2));\n return;\n }\n\n for (const s of sections) {\n console.log(`\\n${'='.repeat(72)}`);\n console.log(` ${s.title}`);\n console.log(`${'='.repeat(72)}\\n`);\n console.log(s.content);\n console.log('');\n }\n}\n","// Sandbox authoring guide — printed by `canary sandbox guide [section]`\n// and also rendered (in part) in the web UI on the sandbox detail page.\n\nimport type { GuideSection } from './types.js';\n\nconst GOAL: GuideSection = {\n title: 'Goal & Tools',\n slug: 'goal',\n content: `\nYour goal is to turn a git repository into a running, containerized development\nenvironment. The end state is a sandbox URL that loads the app in a browser.\n\nWORKFLOW:\n 1. canary sandbox create <property> Create a template + authoring instance\n 2. Research the app Read the codebase to understand what it needs\n 3. Set config values API keys, secrets → pushed to instance as .canary/.env\n 4. Upload artifacts Dockerfile, docker-compose, setup script, etc. → pushed to instance\n 5. canary sandbox build <instance-id> Build images, start containers, generate nginx\n 6. Check diagnostics & logs Verify the app is running\n 7. Fix artifacts, re-upload, re-build Iterate until the app loads in browser\n 8. Mark checklist items complete Record what you did\n\nEach artifact upload and config set immediately pushes the file to the instance.\nThe \"build\" command triggers the actual rebuild and restart.\nBatch your changes (upload all artifacts, set all config), then build once.\n\nYOUR TOOLS:\n\n Config — set environment variables (pushed to instance .canary/.env immediately):\n canary sandbox config set <template-id> <key> <value>\n canary sandbox config list <template-id>\n\n SANDBOX_URL is auto-injected — use it in compose for browser-facing URLs.\n\n Artifacts — files that define the sandbox (pushed to instance on upload):\n canary sandbox artifact create <template-id> <type> <file>\n canary sandbox artifact get <template-id> <type>\n\n Types: dockerfile, docker-compose, setup-script, startup-script, seed-script, file\n\n Build — rebuild and start services:\n canary sandbox build <instance-id>\n canary sandbox build <instance-id> --no-wait\n\n Generates nginx routing, injects Docker networking, writes file artifacts,\n runs setup script, then runs compose up, then runs startup script.\n Call this after uploading artifacts.\n\n First builds on large projects may exceed gateway timeouts. The CLI handles\n this automatically — it detects the timeout and polls diagnostics until\n containers are healthy. Use --no-wait to fire-and-forget and poll manually.\n\n Diagnostics & logs — check container health and read service output:\n canary sandbox diagnostics <instance-id>\n canary sandbox logs <instance-id> --service <name>\n\n Run-command — escape hatch for debugging and one-off commands:\n canary sandbox run-command <instance-id> \"<command>\"\n\n Checklist — record your progress:\n canary sandbox checklist <template-id>\n canary sandbox checklist complete <template-id> <item-id> [options]\n canary sandbox checklist skip <template-id> <item-id>\n\nPRINCIPLES:\n - Artifacts are the source of truth. Every fix must update the artifact file and re-upload.\n - Never patch source files. Use Docker env vars, CMD overrides, setup/startup scripts.\n - Code is volume-mounted at /workspace, not baked into the image.\n - The platform handles nginx routing and Docker networking — you don't need to.\n`.trim(),\n};\n\nconst RESEARCH: GuideSection = {\n title: 'Research the App',\n slug: 'research',\n content: `\nBefore writing any artifacts, research the application thoroughly.\n\n1. ENV VAR VALIDATION\n Find the app's environment validation schema (Zod, Joi, dotenv, etc.).\n List ALL required (non-optional) variables. Many apps fail fast on missing vars.\n\n Common locations:\n - src/env.ts, src/config.ts, src/environment.ts\n - .env.example, .env.sample\n\n Classify each var:\n - AUTO-WIRED: Internal service URLs (DATABASE_URL, REDIS_URL) — hardcode in compose\n - PLACEHOLDER: OAuth creds, S3 buckets — use dummy values, app boots without them\n - SENSITIVE: API keys (ANTHROPIC_API_KEY) — goes in template configSchema\n - DISABLED: Worker flags, schedulers — set to false in compose\n\n2. REQUIRED SERVICES\n What databases, caches, queues does the app need?\n Check: docker-compose.yml, package.json deps, infra/ configs, README\n Common: PostgreSQL, Redis, MongoDB, RabbitMQ, Elasticsearch\n\n3. MIGRATION STRATEGY\n Does the migrate script depend on cloud tooling (SST, AWS SDK, Terraform)?\n If yes, use the ORM's CLI directly in the startup script via \"sb-compose exec\":\n - Drizzle: sb-compose exec -T app-api bash -c 'cd /app/packages/db && bun run drizzle-kit migrate'\n - Prisma: sb-compose exec -T app-api bash -c 'bunx prisma migrate deploy'\n - Rails: sb-compose exec -T app-api rails db:migrate\n - Django: sb-compose exec -T app-api python manage.py migrate\n Put migrations in the startup script, NOT as a compose service (see Artifacts).\n Do NOT use bunx for JS migrations — it downloads a fresh copy. Use \"bun run\" instead.\n\n4. PACKAGE BUILD ORDER (monorepos)\n Which workspace packages export from dist/ and must be pre-built?\n Check package.json \"main\" or \"exports\" fields for \"./dist/\" references.\n These builds go in the startup script via sb-compose exec, NOT the Dockerfile.\n When running scripts via sb-compose exec, cd to the package directory\n where the dependency is declared. Bun workspace module resolution won't\n find deps from the monorepo root.\n\n5. PRODUCTION BUILD STRATEGY (frontends)\n Sandboxes serve a PRODUCTION build of the frontend, not a dev server.\n This is faster and far more responsive, ships compressed/hashed/cacheable\n assets, and sidesteps dev-server host-allowlist headaches entirely (a static\n file server has no notion of \"allowed hosts\"). HMR / hot-reload is\n intentionally dropped — a sandbox demos and QAs a BUILT app; it is not a\n live-edit environment.\n\n Identify, from package.json scripts and the framework:\n - The production build command (vite build, next build, ng build,\n webpack --mode production, etc.) and its output directory\n (commonly dist/, build/, or .next/).\n - Whether the framework produces a STATIC bundle (Vite, CRA, Angular,\n plain Webpack → a dist/ of static files) or needs a NODE SERVER at\n runtime (Next.js server mode, Remix, Nuxt SSR). Static bundles are\n served by nginx; server-rendered apps run their production start\n command (next start, etc.) as a normal routed service.\n\n BUILD-TIME ENV VARS:\n Bundlers BAKE build-time public vars into the output, so they must be set\n in the environment BEFORE the build runs — setting them only in compose\n at runtime is too late for a static bundle. Find every build-time-public\n var (VITE_*, NEXT_PUBLIC_*, REACT_APP_*, ng-injected config, etc.),\n especially the browser-facing API base URL, and set them just before the\n build using \\${SANDBOX_URL} for any URL the browser must reach\n (e.g. VITE_API_URL=\\${SANDBOX_URL}/api). See Artifacts → PRODUCTION BUILD\n for where the build runs.\n\n COST: production builds are slower and more memory-hungry than starting a\n dev server. Vite/webpack builds can OOM the default Node heap on large\n apps — be ready to raise it (NODE_OPTIONS=--max-old-space-size=8192) and\n expect the build to add minutes to the build step.\n\n6. WORKERS & DAEMONS TO DISABLE\n Sandbox typically only needs the API server + web frontend.\n Disable: job runners, schedulers, stall detectors, cron, coordinators.\n Look for env flags like WORKER_ENABLED, SCHEDULER_ENABLED, etc.\n`.trim(),\n};\n\nconst ARTIFACTS: GuideSection = {\n title: 'Artifacts',\n slug: 'artifacts',\n content: `\nArtifacts are the files that define your sandbox. Write them locally, then upload:\n canary sandbox artifact create <template-id> <type> <local-file>\n\nIMPORTANT — ARTIFACT SCOPE:\n Artifact uploads are TEMPLATE-level, not instance-level. Uploading an artifact\n updates the template definition and pushes the file to the AUTHORING instance\n only. Running instances provisioned from the template are NOT updated — they\n use whatever was baked in at provision time. To apply artifact changes to an\n existing non-authoring instance, you must rebuild it:\n canary sandbox build <instance-id>\n\nPLATFORM ARCHITECTURE:\n The sandbox filesystem is split into two areas:\n - /workspace checked-out repo and user-owned files\n - /workspace/.canary platform-managed compose, config, and helper scripts\n\n Your compose file is saved as /workspace/.canary/docker-compose.override.yml and\n merged with the platform's base compose at /workspace/.canary/docker-compose.yml,\n which provides:\n - nginx-proxy: reverse proxy on port 80 (routes based on sandbox.route labels)\n - sandbox-agent: platform agent on port 8080\n - minio: S3-compatible object store at http://minio:9000 (creds:\n minioadmin / minioadmin). See MINIO BUCKET below — the platform\n doesn't auto-wire the endpoint env var or create buckets for you.\n\n You do NOT need to declare any of these in your docker-compose.yml — the\n platform injects them. Defining a service with one of these reserved\n names is rejected at artifact-upload time with code\n SANDBOX_RESERVED_SERVICE_NAME. The full reserved list is:\n nginx-proxy\n sandbox-agent\n minio\n minio-init\n docker-socket-proxy\n\n CONTAINER PATHS — the bind mount maps the repo root to /app inside\n containers, while file artifacts are uploaded under /workspace/.canary\n on the host. So the same file has two paths:\n /workspace/.canary/foo.ts ← the path you upload to from the CLI\n /app/.canary/foo.ts ← the path your compose command/services\n reference inside the container\n Reference the in-container path in any compose \\`command\\` array, exec\n call, or script file path that runs inside a service. The host-side\n /workspace path only appears in startup.sh / setup.sh (which run on\n the host, not inside a container).\n\n MINIO BUCKET (S3 emulation):\n The platform provides a MinIO container at http://minio:9000 but does\n NOT inject AWS_ENDPOINT_URL_S3 or create your bucket. You must:\n\n 1. Wire the endpoint into your service env in compose:\n AWS_ENDPOINT_URL_S3: http://minio:9000\n AWS_ACCESS_KEY_ID: minioadmin\n AWS_SECRET_ACCESS_KEY: minioadmin\n AWS_REGION: us-east-1\n S3_BUCKET_NAME: <your-bucket>\n\n 2. Create the bucket on every build via a one-shot service:\n mc-init:\n image: minio/mc\n restart: \"no\"\n entrypoint:\n - sh\n - -c\n - >\n mc alias set local http://minio:9000 minioadmin minioadmin &&\n mc mb -p local/\\${S3_BUCKET_NAME:-app-bucket} ||\n true\n\n Most AWS SDK v3 clients honor AWS_ENDPOINT_URL_S3 natively. If your\n app passes a custom S3 client, also set forcePathStyle: true (MinIO\n doesn't support virtual-host-style bucket addressing).\n\n Compose still runs with /workspace as the project directory. That means user\n compose content keeps the same repo-root-relative behavior for build contexts,\n bind mounts, and referenced files.\n\n COMPOSE INVOCATION: use the pre-installed \"sb-compose\" wrapper on the host.\n It expands to docker compose with the canonical flag set:\n docker compose --project-directory /workspace \\\\\n --env-file /workspace/.canary/.env \\\\\n -f /workspace/.canary/docker-compose.yml \\\\\n -f /workspace/.canary/docker-compose.override.yml \\\\\n \"$@\"\n Call it from startup scripts, action scripts, and run-command the same way:\n sb-compose exec -T app-api bash -c 'bun run build'\n sb-compose ps\n sb-compose logs --tail 50 app-api\n The wrapper is available as /usr/local/bin/sb-compose (symlink) and\n /workspace/.canary/sb-compose. If you invoke raw \"docker compose\" yourself\n you must pass the same flags — otherwise paths and variable interpolation\n (\\${SANDBOX_URL} et al.) will resolve incorrectly.\n\n The platform injects a shared Docker network. On \"build\", the sequence is:\n 1. Write file artifacts to their destination paths\n 2. Run setup script (if present)\n 3. docker compose down (cleanup previous containers)\n 4. docker compose build + up\n 5. Reload nginx\n 6. Wait for long-running services to accept \"sb-compose exec\" before continuing\n 7. Run startup script (if present)\n\n Step 6 is automatic. You don't need to add your own \"wait for postgres\"\n loops at the top of startup.sh — by the time startup.sh runs, every\n non-one-shot service is exec-able. Service healthchecks inside the\n containers (e.g. postgres pg_isready) are still your responsibility if\n your app needs to wait on them.\n\n Do NOT define services named \"nginx-proxy\", \"sandbox-agent\", or \"minio\"\n in your compose.\n\nDOCKERFILE (type: dockerfile)\n A base image that provides the runtime. Source code is volume-mounted, NOT copied in.\n Written to /workspace/.canary/Dockerfile.sandbox (avoids conflicting with the repo's own Dockerfile).\n Reference it in compose as: build: { context: ., dockerfile: .canary/Dockerfile.sandbox }\n\n Structure:\n FROM <runtime-base>\n RUN apt-get update && apt-get install -y <system-deps>\n RUN npm install -g <global-tools>\n WORKDIR /app\n\n Rules:\n - NO \"COPY . .\" — code comes via volume mount at /workspace\n - NO \"RUN npm build\" — the production build runs on the instance as a\n compose service (see PRODUCTION BUILD), where \\${SANDBOX_URL} and\n build-time env are available. Baking it into the image would freeze\n build-time env to image-build values.\n - Include ALL system deps (chromium, ffmpeg, python, etc.)\n - For browser automation: use mcr.microsoft.com/playwright as base\n - Do NOT use ENTRYPOINT — it breaks one-shot services (deps, migrate) whose\n command gets passed as args to the entrypoint instead of running directly\n\nDOCKER COMPOSE (type: docker-compose)\n Defines all services. Platform handles networking and nginx routing.\n\n Typical services:\n deps (one-shot) — installs dependencies into named volume, restart: \"no\"\n database — official image with healthcheck (postgres, mysql, etc.)\n cache — redis/memcached with healthcheck\n web-build (one-shot) — runs the production frontend build into the bind-mounted\n repo tree, restart: \"no\" (see PRODUCTION BUILD)\n app-api — application server, PRODUCTION command (NODE_ENV=production,\n no --hot / --watch)\n app-web — static frontend served by nginx:alpine from the build\n output (see PRODUCTION BUILD). SSR frameworks (Next.js\n server mode, etc.) run their production start command\n instead of an nginx static server.\n\n Do NOT use a migrate service in compose. If the migrate container fails once\n (bad SQL, missing env var, wrong path), depends_on with\n service_completed_successfully is permanently unmet — app-api will never\n start, even after you fix the migration. \"docker compose restart\" and\n \"docker compose up\" won't help; only \"docker compose down\" + fresh up works.\n Put migrations in the startup script instead (runs after compose up).\n\n ROUTING LABELS — declare how nginx should route to your services:\n services:\n app-api:\n labels:\n sandbox.route: \"/api\"\n sandbox.port: \"3000\"\n app-web:\n labels:\n sandbox.route: \"/\"\n sandbox.port: \"80\"\n\n The platform reads these labels and generates nginx config on \"build\".\n If no labels, all traffic routes to the first non-infrastructure service.\n\n PREFIX STRIPPING: the generated nginx location uses a trailing-slash\n proxy_pass (proxy_pass http://<service>:<port>/), so the matched prefix\n is stripped from the upstream request. With sandbox.route: \"/api\", a\n browser hit to \"/api/health\" is delivered to the upstream as \"/health\".\n Pick VITE_API_URL, OAuth redirect URIs, etc. accordingly.\n\n ENV VAR STRATEGY — use \\${VAR} syntax, values come from config + .env:\n - Auto-wired (hardcode): DATABASE_URL=postgres://user:pass@postgres:5432/db\n - From config: ANTHROPIC_API_KEY=\\${ANTHROPIC_API_KEY}\n - Platform-injected: VITE_API_URL=\\${SANDBOX_URL}/api\n - Disabled: WORKER_ENABLED=false\n\n SANDBOX_URL is auto-injected by the platform (e.g. https://<id>.env.runcofactor.com).\n Use it for any URL a browser needs to reach the sandbox.\n\n Rules:\n - Use named volumes for data persistence (postgres-data, redis-data, etc.)\n - Bind-mount the repo (.:/app) rather than using named volumes for\n node_modules. Per-package node_modules named volumes silently shadow the\n bind mount on any service that doesn't mount them — a common monorepo\n trap where \"bun run build\" fails because tsup is nowhere to be found.\n The sandbox host is Linux EC2; bind mounts are fast.\n - DO NOT define networks — the platform handles networking\n - DO NOT expose ports to the host — nginx handles routing\n - Keep relative paths repo-root-relative; compose runs from /workspace even\n though the platform-managed compose files live in /workspace/.canary\n - Escape shell variables in \"command:\" strings with $$ so compose leaves\n them for the shell (e.g. command: [\"bash\", \"-c\", \"exec foo $$BAR\"]).\n Unescaped \\${VAR} is substituted at compose-time from the env file.\n\nPRODUCTION BUILD (static frontends)\n Serve the frontend as a production build, not a dev server. Two pieces:\n\n 1. BUILD as a one-shot compose service — in its OWN container, NOT inside the\n api/app container. A production build is CPU + disk-I/O heavy; if it runs\n inside the api container it starves the api's /health endpoint and the\n provision health gate fails the whole sandbox (nginx 502). The setup script\n can't do it either (it runs on the HOST, where app runtimes don't exist, and\n before deps installs). So: a dedicated one-shot service that depends on deps.\n\n Build the RIGHT set of packages. In a monorepo, build EVERY workspace\n package that exports from dist/ that is imported by BOTH the frontend AND\n the BACKEND — not just the UI. If the api imports a dist-exporting package\n whose dist is missing, the api crash-loops \"Cannot find module @scope/pkg\"\n and never answers /health. Find them: grep the api's imports for @scope/*\n and cross-check each package.json \"main\"/\"exports\" for \"./dist/\". Build the\n leaf packages first, then the app:\n web-build:\n build: { context: ., dockerfile: .canary/Dockerfile.sandbox }\n working_dir: /app\n volumes:\n - .:/app\n environment:\n VITE_API_URL: \\${SANDBOX_URL}/api # build-time public vars FIRST\n NODE_OPTIONS: --max-old-space-size=8192 # raise heap if the build OOMs\n # nice -n 19 keeps the api responsive if anything overlaps. Build the\n # api's dist-dependencies AND the ui, then the app bundle.\n command: [\"bash\",\"-c\",\"nice -n 19 bash -c 'cd /app/packages/foo && bun run build && cd /app/packages/bar && bun run build && cd /app/apps/web && bun run build'\"]\n depends_on:\n deps:\n condition: service_completed_successfully\n restart: \"no\"\n Compose interpolates \\${SANDBOX_URL} at \"up\" time, so the bundle bakes the\n correct browser-facing API URL. The output (dist/) lands in the\n bind-mounted repo tree, where the nginx service below mounts it.\n\n SERIALIZE the build BEFORE the health gate: give the nginx service a\n depends_on web-build (service_completed_successfully). That makes\n \"docker compose up\" block until the build finishes, so the gate only probes\n the api once the host is idle. Restart the api in the STARTUP script\n afterwards so it boots onto the idle host with all dist packages present.\n\n 2. SERVE the output with a lightweight nginx:alpine service that depends on\n the build completing. Mount the PARENT of the output dir (e.g. apps/web),\n NOT the dist dir directly, and point nginx root at the dist SUBPATH:\n app-web:\n image: nginx:alpine\n volumes:\n - ./apps/web:/srv:ro # mount the PARENT, root is /srv/dist\n - ./.canary/nginx-spa.conf:/etc/nginx/conf.d/default.conf:ro\n depends_on:\n web-build:\n condition: service_completed_successfully\n labels:\n sandbox.route: \"/\"\n sandbox.port: \"80\"\n\n WHY mount the parent: bundlers (vite, webpack) DELETE and recreate the\n output dir on build, giving it a NEW inode. Docker creates an empty\n dist bind-source at compose-up time; a direct \"dist:/usr/share/nginx/html\"\n bind pins nginx to that stale empty inode and it serves 403 \"directory\n index forbidden\" forever. Mounting the parent (a stable inode) and letting\n nginx resolve root/<uri> per request always sees the current dist.\n\n Provide an SPA-fallback nginx config as a file artifact so client-side\n routes (deep links / hard refresh) don't 404 — unknown paths serve\n index.html and the client router takes over:\n canary sandbox artifact create <template-id> file nginx-spa.conf \\\\\n --path /workspace/.canary/nginx-spa.conf\n\n nginx-spa.conf:\n server {\n listen 80;\n root /srv/dist; # the dist SUBPATH of the mounted parent\n index index.html;\n gzip on;\n gzip_types text/css application/javascript application/json image/svg+xml;\n location /assets/ {\n try_files $uri =404;\n expires 1y;\n add_header Cache-Control \"public, immutable\";\n }\n location / {\n try_files $uri $uri/ /index.html;\n add_header Cache-Control \"no-cache\";\n }\n }\n\n (Match root to your framework's output dir under the mounted parent — e.g.\n /srv/build for a build/ output. The hashed-asset location block assumes a\n /assets/ dir — adjust to your bundler's asset path.)\n\n WHY: nginx serving static files has no host allowlist (no \"This host is not\n allowed\"), ships compressed/hashed/cacheable assets, and is far more\n responsive than a dev server streaming hundreds of uncompressed modules. The\n trade-off is no hot-reload and a longer, heavier build step — acceptable\n because sandboxes demo a BUILT app.\n\n SSR / server-rendered frameworks (Next.js server mode, Remix, Nuxt SSR):\n skip the nginx static server. Build in the one-shot service, then run the\n framework's production start command (e.g. next start) as a normal routed\n service with sandbox.route \"/\" and its listen port.\n\nSETUP SCRIPT (type: setup-script)\n Runs BEFORE docker compose up on every build. Runs on the HOST, not in a container.\n Written to /workspace/.canary/setup.sh.\n\n IMPORTANT: The host is a bare EC2 instance with only docker and basic linux tools.\n App runtimes (node, bun, python, ruby) are NOT available on the host — they live\n inside your Docker images. Do NOT run app commands directly here.\n\n Script authoring rules:\n - Treat /workspace as the repo root for any file access or repo command\n - Do not assume the current directory matches the script's own path\n - Use explicit paths or start with \"cd /workspace\" before repo-relative work\n\n Use for host-level preparation only:\n - Installing system packages (apt-get, yum)\n - Downloading external tools or binaries\n - Generating config files programmatically (prefer file artifacts for static files)\n\n For most apps, you won't need a setup script — file artifacts and the startup\n script cover the common cases. Skip this if you don't need host-level prep.\n\n Rules:\n - Must be idempotent — safe to run multiple times\n - Never patch source files\n - Non-zero exit is logged as a warning but does not block the build\n\nSTARTUP SCRIPT (type: startup-script)\n Runs AFTER docker compose up on every build. Runs on the HOST, not in a container.\n Written to /workspace/.canary/startup.sh.\n\n This is where most app setup happens. Containers are running, so you can use\n \"sb-compose exec\" to run commands inside them with the app runtime.\n\n Script authoring rules:\n - Start with \"cd /workspace\" or use explicit /workspace/... paths\n - Do not assume the script runs from /workspace/.canary\n - Prefer sb-compose exec for anything that needs the app runtime\n - Use -T with exec (no TTY allocation — startup runs headless)\n\n Use for:\n - DB migrations via ORM CLI (use \"bun run\", NOT bunx — bunx downloads fresh)\n - Building workspace packages that export from dist/\n - Waiting for services to be healthy\n - Triggering initial data loads\n\n POST-MIGRATION RESTART (REQUIRED for many ORMs):\n ORMs that introspect the schema at boot (Drizzle/effect-sql, Prisma with\n prepared statements, SQLAlchemy with autoload, etc.) cache the pre-migration\n schema on the first query. After you run migrations, you MUST restart the\n API service, otherwise you'll see errors like:\n relation \"<table>\" does not exist\n even though psql confirms the table is there. Add \"sb-compose restart\" for\n any service whose client introspects the schema at boot.\n\n Example (Bun monorepo with Drizzle):\n #!/bin/bash\n set -e\n # Build dist-exporting workspace packages the app/web import from\n sb-compose exec -T app-api bash -c \\\\\n 'cd /app/packages/ui && bun run build'\n # Run DB migrations inside the API container\n sb-compose exec -T app-api bash -c \\\\\n 'cd /app/packages/db && bun run drizzle-kit migrate'\n # Restart services so they pick up the fresh schema + built UI\n sb-compose restart app-api app-web\n # Wait for API to be healthy\n for i in $(seq 1 30); do\n sb-compose exec -T app-api curl -sf http://localhost:3000/health && break\n sleep 2\n done\n\n Rules:\n - Must be idempotent — safe to run multiple times\n - Never patch source files\n - Don't pre-wait for services to START — the platform handles that before\n startup.sh runs (see Compose Architecture section). Do wait for service\n *readiness* (app health, schema migrations applied) when your app\n depends on it.\n\nFILE (type: file)\n Arbitrary sandbox-owned files written to the instance at a specific path.\n Use for config files that need to exist before services start (e.g. Vite\n wrapper configs, one-shot scripts that need app dependencies).\n\n !!! File artifacts MUST be written under /workspace/.canary/ !!!\n The repo tree (everything outside .canary/) is user-owned. Sandbox\n helpers live in .canary/ so they never collide with real files and\n are never committed back to the repo. The API will reject any\n --path value outside /workspace/.canary/ with\n SANDBOX_FILE_ARTIFACT_OUTSIDE_CANARY.\n\n If your script needs app dependencies (e.g. \\`postgres\\` from the api\n package), put a tiny package.json next to it in .canary/scripts/ and\n \\`bun install\\` in the setup-script, OR invoke it via\n \\`sb-compose exec -T <svc> ...\\` so it runs inside a container that\n already has them.\n\n Upload with --path to specify the destination:\n canary sandbox artifact create <template-id> file <local-file> --path /workspace/.canary/<dest>\n\n Example — SPA fallback config for the static web server (see PRODUCTION BUILD):\n canary sandbox artifact create <template-id> file nginx-spa.conf \\\\\n --path /workspace/.canary/nginx-spa.conf\n\n ONE ROW PER FILENAME. File artifacts are keyed by (template, filename).\n Uploading a file with the same filename as an earlier upload REPLACES\n the earlier row. The CLI prints \"(replaced)\" on such uploads, and the\n next build removes the previous destination path so the instance\n filesystem mirrors the template set exactly.\n\n If you want to retire a file, just upload something else with a\n different filename — the stale file is cleaned up on next build via\n .canary/.artifact-paths (a manifest the platform maintains).\n\n File artifacts are written before the setup script runs, so they're\n available for both setup and compose services.\n\nSEED SCRIPT (type: seed-script)\n Runs once on first provision to populate demo data. Optional.\n Written to /workspace/.canary/seed.sh.\n Skipped on subsequent builds (checks /workspace/.canary/.seeded).\n\nCONFIG SCHEMA\n Define which env vars need user input. Set when completing the docker-compose checklist item:\n canary sandbox checklist complete <template-id> docker-compose-created \\\\\n --status success \\\\\n --summary \"Compose with postgres, redis, api, web\" \\\\\n --config-schema '[\n {\"key\": \"ANTHROPIC_API_KEY\", \"required\": true, \"sensitive\": true, \"description\": \"Anthropic API key\"},\n {\"key\": \"OPENAI_API_KEY\", \"required\": false, \"sensitive\": true, \"description\": \"OpenAI API key\"}\n ]'\n\n Set defaults:\n canary sandbox config set <template-id> <key> <value>\n canary sandbox config list <template-id>\n`.trim(),\n};\n\nconst VERIFICATION: GuideSection = {\n title: 'Verification & Iteration',\n slug: 'verify',\n content: `\nAfter running \"canary sandbox build\", verify the sandbox is working. The iteration loop:\n upload artifacts → build → check diagnostics → read logs → fix → re-upload → rebuild\n\nCOMMANDS:\n # Check all container statuses at a glance\n canary sandbox diagnostics <instance-id>\n\n # Read logs for a specific service\n canary sandbox logs <instance-id> --service app-api\n canary sandbox logs <instance-id> --service app-web\n canary sandbox logs <instance-id> --service postgres\n\n # Run arbitrary commands on the instance (runs on the host VM by default)\n canary sandbox run-command <instance-id> \"docker compose ps\"\n canary sandbox run-command <instance-id> \"echo hello\"\n\n # Run commands inside a specific Docker container\n canary sandbox run-command <instance-id> --service app-api \"bun --version\"\n canary sandbox run-command <instance-id> --service app-api \"bun test\"\n\n # Test health endpoints\n canary sandbox run-command <instance-id> \"curl -s http://localhost:3000/health\"\n canary sandbox run-command <instance-id> \"curl -s -o /dev/null -w '%{http_code}' http://localhost:80/\"\n\n # Test with sandbox hostname (for host-restriction issues)\n canary sandbox run-command <instance-id> \\\\\n \"curl -s -H 'Host: <instance-id>.env.runcofactor.com' http://localhost:80/\"\n\nSANDBOX URL:\n https://<instance-id>.env.runcofactor.com\n\nIF SOMETHING FAILS:\n 1. Read the logs for the failing service\n 2. Diagnose the root cause (missing env var, failed build, wrong port, etc.)\n 3. Fix the ARTIFACT — not just the running container\n 4. Re-upload: canary sandbox artifact create <template-id> <type> <file>\n 5. Rebuild: canary sandbox build <instance-id>\n 6. Verify again\n\nWHEN A CLI COMMAND TIMES OUT AT 30s:\n Long sandbox operations (build, edit, artifact create on first push,\n occasionally diagnostics) can exceed the gateway window even though\n the work is still running server-side. The CLI prints:\n Error: Request to https://api.trycanary.ai/... timed out after 30000ms.\n\n Don't retry blindly — a retry can spawn duplicate work (e.g. a second\n authoring instance). Instead, poll for the actual state:\n\n canary sandbox template <template-id> # status: provisioning/authoring/verified\n canary sandbox diagnostics <instance-id> # container/app health\n canary sandbox list # running instances\n\n Only retry once you've confirmed via polling that the prior call\n truly didn't take effect.\n\nWHEN VERIFY HANGS:\n \\`canary sandbox verify\\` now streams container state and failing-service\n log tails every ~30s while the probe's app is unreachable. If you see:\n\n >> nginx-proxy is restart-looping because upstream '<service>' (...) is exited.\n Read that service's logs above, not nginx's.\n\n ignore nginx entirely and read the upstream's tail — nginx is the symptom,\n not the cause. Common underlying causes:\n - the upstream service crashed at startup (bad env var, missing file,\n import error)\n - a file artifact was lost because you re-uploaded over its filename\n (shouldn't happen any more — the build-time reconcile handles this —\n but watch for it if you're on an old template)\n\n On failure, the probe instance is preserved by default so you can\n \\`canary sandbox run-command <id>\\` into it. Pass\n --auto-terminate-on-failure for CI.\n\nCOMMON ISSUES:\n\n \"Invalid environment\" / env validation fails\n App has strict env var validation. Read the env schema. Add ALL required vars to compose.\n\n Migration fails with \"SST links not active\" / cloud SDK error\n Migrate script depends on cloud tooling. Use the ORM CLI directly in the startup script\n via sb-compose exec: sb-compose exec -T app-api bash -c 'bun run drizzle-kit migrate'\n DATABASE_URL should be hardcoded in the compose environment section.\n\n \"Cannot find module\" / package not built\n Monorepo package exports from dist/ but hasn't been built.\n Add build step to startup script via sb-compose exec (runs after compose up).\n\n \"Cannot find package 'X'\" from a file-artifact script\n Bun resolves node_modules starting from the script's directory. File\n artifacts live under /workspace/.canary/ which has no node_modules by\n default. Two fixes:\n (a) add a /workspace/.canary/scripts/package.json declaring the\n dep (e.g. \"postgres\") and run \\`bun install\\` from the\n setup-script / inside a sb-compose exec.\n (b) don't run the script on the host at all — invoke it via\n \\`sb-compose exec -T <app-service> bun /app/.canary/scripts/foo.ts\\`\n so it resolves against the workspace's own node_modules. Replace\n <app-service> with your template's actual service name.\n\n Blank page — frontend never renders (served by a dev server)\n The web service is a dev server (Vite/webpack-dev-server) shipping hundreds\n of uncompressed, no-cache modules. On a remote sandbox these trickle in too\n slowly for the SPA to mount, so the page stays blank. Confirm with:\n curl -s <SANDBOX_URL>/ | grep '<script'\n Dev mode references /@vite/client or /src/main.tsx; a production build\n references hashed /assets/*.js. Fix: serve a production build (see\n Artifacts → PRODUCTION BUILD), not a dev server.\n\n Deep links / page refresh 404 (SPA routing)\n nginx serves static files but a client-side route (e.g. /settings) or a\n hard refresh returns 404 — the static server has no SPA fallback. Add\n \"try_files $uri $uri/ /index.html;\" to the nginx config (see Artifacts →\n PRODUCTION BUILD) so unknown paths serve index.html and the client router\n takes over.\n\n Frontend loads but API calls hit the wrong URL / localhost\n The browser-facing API URL was not set as a BUILD-TIME var before the\n build, so the bundle baked a default (often localhost:3000). Static\n bundles read build-time vars at BUILD time, not runtime — setting them\n only in the app-web/runtime compose env is too late. Set the public var on\n the web-build service env (or before the build command), using\n \\${SANDBOX_URL}, then rebuild:\n VITE_API_URL: \\${SANDBOX_URL}/api # or NEXT_PUBLIC_*, REACT_APP_*, etc.\n\n API 502 / \"Cannot find module '@scope/pkg'\" crash-loop after going prod\n The api imports a workspace package that exports from dist/, but that\n package's dist was never built — so the api crash-loops and the health gate\n fails the whole provision with a 502. Easy to miss because the FRONTEND\n build (ui) works. Build EVERY dist-exporting package the api imports in the\n web-build service (see Artifacts → PRODUCTION BUILD), not just the ui ones.\n A warm AMI can hide this (it baked the dist in); a fresh provision exposes it.\n\n 403 \"directory index forbidden\" from the static server\n nginx is bound to the stale empty dist inode the bundler replaced. Mount the\n PARENT dir and set root to the dist subpath (see Artifacts → PRODUCTION\n BUILD), or restart the nginx service after the build.\n\n Build is killed / \"JavaScript heap out of memory\"\n The production build exhausted the Node heap (common on large Vite/webpack\n builds). Raise it on the web-build service before the build:\n NODE_OPTIONS: --max-old-space-size=8192\n If it still OOMs, bump the instance size (canary sandbox set-size) and/or\n the heap further.\n\n Stale page after a rebuild (old dist served)\n nginx is serving a previous build because the build step didn't re-run or\n its output dir wasn't refreshed. Confirm the web-build service ran in this\n build cycle (canary sandbox logs <id> --service web-build), that it wrote\n to the dir nginx mounts, and — if you ever build via the startup script\n instead — that nginx was restarted (sb-compose restart app-web). Hashed\n asset filenames change per build; if index.html still references old\n hashes, the build didn't land.\n\n run-command times out\n Long-running operation exceeds the 55s SSM command timeout. Write a script file and run in background:\n canary sandbox run-command <id> \"cat > /tmp/run.sh << 'SCRIPT'\n #!/bin/bash\n <commands>\n SCRIPT\n chmod +x /tmp/run.sh && nohup /tmp/run.sh > /tmp/run.log 2>&1 & echo started\"\n Then check: canary sandbox run-command <id> \"tail -20 /tmp/run.log\"\n\n \"Resolving dependencies\" hangs in migration container\n bunx/npx downloads a fresh copy instead of using project deps.\n Use \"bun run drizzle-kit migrate\" instead of \"bunx drizzle-kit migrate\".\n\n Containers can't resolve each other by name\n Don't use docker run — use docker compose for everything.\n\nRUNNING TESTS:\n Two approaches depending on your needs:\n\n Option 1 — Install runtime on host (no Docker needed):\n Use the setup script to install the runtime on the host VM, then run\n tests directly. Good for unit tests that don't need services like a database.\n\n Example setup-script artifact:\n #!/bin/bash\n curl -fsSL https://bun.sh/install | bash\n export PATH=\"$HOME/.bun/bin:$PATH\"\n\n Then: canary sandbox run-command <instance-id> \"bun test\"\n\n Option 2 — Run inside a container (--service flag):\n Route commands into a running Docker container. Good for integration\n tests that need access to the database or other services.\n\n canary sandbox run-command <instance-id> --service app-api \"bun test\"\n canary sandbox run-command <instance-id> --service app-api \"bun test:integration\"\n\n \\${SANDBOX_URL} is blank / env vars from config not in containers\n The \"build\" command writes /workspace/.canary/.env (with SANDBOX_URL and all config values)\n before running docker compose up. If the .env is missing or stale, re-run:\n canary sandbox build <instance-id>\n You can also trigger a .env refresh by setting any config value:\n canary sandbox config set <template-id> <key> <value>\n\n .env values look empty when I cat the file\n Values are written single-quoted (KEY='value'). Short values can look like\n KEY='' at a glance even when they have content — it's just the quotes. Run\n \"wc -c\" on a line, or \"docker compose exec app-api env | grep KEY\", to\n confirm the actual value landed.\n`.trim(),\n};\n\nconst CHECKLIST: GuideSection = {\n title: 'Checklist',\n slug: 'checklist',\n content: `\nThe checklist tracks your progress toward a working sandbox. View it with:\n canary sandbox checklist <template-id>\n\nEach item has a status: pending, completed, skipped, or failed.\nItems marked with * are actionable (their dependencies are met).\n\nCOMPLETING ITEMS:\n canary sandbox checklist complete <template-id> <item-id> \\\\\n --status <success|failure> \\\\\n --summary \"What you did\"\n\n Example:\n canary sandbox checklist complete <template-id> environment-analyzed \\\\\n --status success \\\\\n --summary \"Bun monorepo: Postgres, Redis. Vite production build served by nginx, API on 3000.\"\n\nSKIPPING ITEMS:\n canary sandbox checklist skip <template-id> <item-id>\n\n Use when an item doesn't apply (e.g., no seed data needed, no integration tests).\n\nSPECIAL: docker-compose-created accepts --config-schema to define user-configurable env vars:\n canary sandbox checklist complete <template-id> docker-compose-created \\\\\n --status success \\\\\n --summary \"Compose with postgres, redis, api, web\" \\\\\n --config-schema '[{\"key\": \"ANTHROPIC_API_KEY\", \"required\": true, \"sensitive\": true, \"description\": \"API key\"}]'\n\nTYPICAL CHECKLIST ORDER:\n 1. Provisioned — done automatically when you create the template\n 2. Code Cloned — done automatically\n 3. Environment Analyzed — complete after your research phase\n 4. Setup Script Created — complete after uploading setup-script artifact\n 5. Dockerfile Created — complete after uploading dockerfile artifact\n 6. Docker Compose Created — complete after uploading docker-compose artifact (with config-schema)\n 7. Unit Tests Runnable — complete or skip. Install runtime on host via setup script,\n or use --service to run inside a container\n 8. Integration Tests Runnable — complete or skip. Use --service to run inside a\n container with access to database and other services\n 9. Seed Script Created — complete or skip\n 10. Server Running — complete after API responds to health checks\n 11. Server Accessible — complete after nginx routes to the app successfully\n 12. Template Verified — provision a fresh instance and verify it comes up healthy\n\nTEMPLATE VERIFICATION (final step):\n Run one command — it provisions a fresh instance, waits for the app to\n come up, marks template-verified, and terminates the instance:\n canary sandbox verify <template-id>\n\n Pass --keep-instance to leave the instance running afterwards (useful if\n you want to poke at it).\n\n This catches issues that authoring hides:\n - Config values set manually but not in configDefaults\n - SANDBOX_URL or other platform vars not properly referenced\n - Startup scripts that assume manual setup already happened\n\n Template status flow:\n provisioning → authoring → verified\n\n 'verify' is the one transition from authoring to verified, and it is one-way\n — there is no re-authoring path back to 'authoring' from 'verified'. To make\n an AI-assisted edit to a verified template's artifacts, provision a sandbox\n with the agent attached:\n\n canary sandbox provision <template-id> --with-agent\n\n then iterate via in-sandbox agent chat or 'canary sandbox artifact create',\n and re-run 'canary sandbox verify' against the same template id when you're\n happy with the changes. To start over from scratch, use 'regenerate' to\n delete existing artifacts, or create a new template.\n`.trim(),\n};\n\nconst ACTIONS: GuideSection = {\n title: 'Actions',\n slug: 'actions',\n content: `\nActions are template-defined, parameterized operations that run against sandbox\ninstances. Unlike run-command (arbitrary shell), actions have typed input/output\nschemas, are reusable across instances, and produce an auditable execution log.\n\nUSE CASES:\n - Provision a user credential (username, password, role)\n - Seed demo data for a specific scenario\n - Run database migrations\n - Reset application state\n - Toggle feature flags\n\nMANAGING ACTIONS:\n\n List actions on a template:\n canary sandbox action list <template-id>\n\n View an action definition (including script):\n canary sandbox action get <template-id> <slug>\n\n Create or update an action:\n canary sandbox action create <template-id> <slug> \\\\\n --label \"Provision Credential\" \\\\\n --description \"Create a user account in the sandbox app\" \\\\\n --icon UserPlus \\\\\n --script ./scripts/provision-user.sh \\\\\n --input-schema ./schemas/provision-user-input.json \\\\\n --credential-output-config ./schemas/provision-user-credential.json\n\n --credential-output-config is optional. When set, a successful execution\n also creates/updates a credential in the property's vault — see \"Linking\n actions to Canary credentials\" below.\n\n Delete an action:\n canary sandbox action delete <template-id> <slug>\n\nEXECUTING ACTIONS:\n\n Execute against a running instance:\n canary sandbox action execute <instance-id> <slug> \\\\\n --param username=testuser \\\\\n --param password=s3cret \\\\\n --param role=admin\n\n Or pass all params from a JSON file:\n canary sandbox action execute <instance-id> <slug> \\\\\n --params-file ./params.json\n\n View execution history:\n canary sandbox action executions <instance-id>\n\nSLUG NAMING:\n Slugs must be lowercase alphanumeric with hyphens, starting with a letter:\n provision-credential ✓\n seed-demo-data ✓\n reset-state ✓\n 123-bad ✗\n UPPER_CASE ✗\n\nINPUT SCHEMA:\n A JSON Schema file defining the action's parameters. The schema drives\n validation, CLI prompting, and UI form generation.\n\n Example (provision-user-input.json):\n {\n \"type\": \"object\",\n \"properties\": {\n \"username\": {\n \"type\": \"string\",\n \"description\": \"Username to create\",\n \"minLength\": 1\n },\n \"password\": {\n \"type\": \"string\",\n \"description\": \"Password for the user\",\n \"sensitive\": true\n },\n \"role\": {\n \"type\": \"string\",\n \"description\": \"Role to assign\",\n \"enum\": [\"admin\", \"user\", \"viewer\"],\n \"default\": \"user\"\n }\n },\n \"required\": [\"username\", \"password\"]\n }\n\n The \"sensitive\" extension marks params that should be masked in UI and\n injected securely (via tempfile, not env var) on the instance.\n\nSCRIPT CONVENTIONS:\n Action scripts are bash, executed on the instance via SSM. Parameters are\n injected as INPUT_<KEY> environment variables, where <KEY> is the param\n name uppercased with non-alphanumeric chars replaced by '_'. There is NO\n camelCase → snake_case conversion. Examples:\n orgName → INPUT_ORGNAME (NOT INPUT_ORG_NAME)\n org_name → INPUT_ORG_NAME\n api-key → INPUT_API_KEY\n If you want INPUT_FOO_BAR, name the input schema key 'foo_bar' or 'foo-bar'.\n\n The last line of stdout that is valid JSON is parsed as the structured result.\n\n EXECUTION ENVIRONMENT — same architecture as startup scripts:\n Scripts run on the HOST (bare EC2 instance). The host has docker, curl,\n and basic linux tools — but NO app runtimes (node, bun, python, ruby).\n Those live inside your Docker containers.\n\n Use \"docker compose exec\" to run commands that need the app runtime:\n sb-compose exec -T app-api node -e \"...\" # Node.js\n sb-compose exec -T app-api bun run seed.ts # Bun\n sb-compose exec -T app-db psql -U app -d db # Database CLI\n\n Use bare host commands for docker/system operations:\n sb-compose restart app-api # Restart a service\n curl -s http://localhost:3000/health # Health check\n cat /workspace/.canary/.env # Read config\n\n The -T flag is required for docker compose exec in non-interactive (SSM)\n contexts. Without it, exec tries to allocate a TTY and hangs.\n\n Working directory is /workspace (the mounted repo root). Keep script logic\n repo-root-relative by starting with \"cd /workspace\" when needed.\n\n AVOID INLINE CODE IN ACTION SCRIPTS:\n Never embed application-language code (JS, Python, etc.) directly in the\n bash script via \"bun -e\" or \"node -e\". Shell escaping of quotes, template\n literals, and variable interpolation is fragile and hard to debug.\n\n Instead, write complex logic as a separate file artifact and execute it:\n 1. Write a script file (e.g. provision-user.ts)\n 2. Upload as a file artifact:\n canary sandbox artifact create <template-id> file provision-user.ts \\\\\n --path /workspace/scripts/provision-user.ts\n 3. Reference it from the action script:\n sb-compose exec -T app-api bun /app/scripts/provision-user.ts\n\n Parameters are available in the action script as INPUT_<KEY> env vars.\n Pass them to the file artifact via env or args:\n sb-compose exec -T -e EMAIL=\"$INPUT_EMAIL\" app-api bun /app/scripts/provision-user.ts\n\n Example — provision a user (two-file pattern):\n\n Place the .ts file under the workspace whose node_modules has the deps\n (e.g. apps/api when only apps/api depends on \\`postgres\\`). Bun resolves\n modules from the script's directory walking up — if the dep isn't hoisted\n to the repo-root node_modules, a script under /workspace/scripts/ will\n fail with \"Cannot find package 'postgres'\". Use \\`-w\\` to set cwd inside\n the right workspace.\n\n Action script (provision-user.sh):\n #!/bin/bash\n set -euo pipefail\n cd /workspace\n sb-compose exec -T \\\\\n -w /app/apps/api \\\\\n -e EMAIL=\"$INPUT_EMAIL\" \\\\\n -e PASSWORD=\"$INPUT_PASSWORD\" \\\\\n -e NAME=\"$INPUT_NAME\" \\\\\n app-api bun /app/apps/api/scripts/provision-user.ts\n\n File artifact (uploaded to /workspace/apps/api/scripts/provision-user.ts):\n import postgres from \"postgres\";\n const sql = postgres(process.env.DATABASE_URL!);\n const hash = await Bun.password.hash(process.env.PASSWORD!, { algorithm: \"argon2id\" });\n const email = process.env.EMAIL!.toLowerCase();\n const [org] = await sql\\`INSERT INTO organizations (name) VALUES (\\${email + \"'s Org\"}) RETURNING id\\`;\n const [user] = await sql\\`\n INSERT INTO users (auth_provider, provider_id, primary_email, password_hash, email_verified_at)\n VALUES ('email', \\${email}, \\${email}, \\${hash}, now()) RETURNING id\\`;\n await sql\\`INSERT INTO org_memberships (user_id, org_id, role) VALUES (\\${user.id}, \\${org.id}, 'admin')\\`;\n const loginUrl = \\`\\${process.env.SANDBOX_PUBLIC_URL ?? ''}/login\\`;\n // Echo password back so credentialOutputConfig (below) can resolve $output.password.\n // Output keys referenced by credentialOutputConfig.fieldMapping are auto-redacted\n // from stored stdout/logs by the platform — safe to include.\n console.log(JSON.stringify({ ok: true, email, password: process.env.PASSWORD, userId: user.id, orgId: org.id, loginUrl }));\n await sql.end();\n\nLINKING ACTIONS TO CANARY CREDENTIALS:\n\n When you want a successful action to also produce a credential in the\n property's vault (so it can drive browser logins, seed workflows, etc.),\n attach a credentialOutputConfig. The platform resolves \\`$output.<key>\\`\n tokens against the action's last-line JSON stdout, then upserts a\n credential scoped to (sandboxActionId, environmentId) — re-running the\n action replaces the credential rather than duplicating it.\n\n Provide it with --credential-output-config on action create/update:\n\n canary sandbox action create <template-id> provision-credential \\\\\n --label \"Provision Credential\" \\\\\n --script ./scripts/provision-user.sh \\\\\n --input-schema ./schemas/provision-user-input.json \\\\\n --credential-output-config ./schemas/provision-user-credential.json\n\n Example provision-user-credential.json (matches the script above):\n {\n \"fieldMapping\": {\n \"username\": \"$output.email\",\n \"password\": \"$output.password\"\n },\n \"loginUrl\": \"$output.loginUrl\",\n \"loginInstructions\": \"Sign in via the email/password form.\"\n }\n\n Field-mapping values are either literal strings or \\`$output.<key>\\` tokens\n resolved from the action's parsed JSON output. Output keys referenced by\n fieldMapping/loginUrl are automatically masked in stored stdout and result.\n\n After execution, \\`canary sandbox action execute\\` prints the linked\n credentialId, and \\`canary credential list --property <name>\\` shows the\n vault entry. \\`canary credential test <credId>\\` runs the browser login\n against the sandbox URL.\n\n Example — simple SQL-only action (inline is fine for short commands):\n #!/bin/bash\n set -euo pipefail\n cd /workspace\n sb-compose exec -T app-db psql -U app -d appdb -t --csv -c \\\\\n \"INSERT INTO users (username, password_hash, role)\n VALUES ('$INPUT_USERNAME', crypt('$INPUT_PASSWORD', gen_salt('bf')), '$INPUT_ROLE')\n RETURNING id\" | while read -r user_id; do\n echo \"{\\\\\"userId\\\\\":\\\\\"$user_id\\\\\",\\\\\"username\\\\\":\\\\\"$INPUT_USERNAME\\\\\",\\\\\"loginUrl\\\\\":\\\\\"https://$SANDBOX_PUBLIC_URL/login\\\\\"}\"\n done\n\n Example — seed data via app CLI:\n #!/bin/bash\n set -euo pipefail\n cd /workspace\n sb-compose exec -T app-api bun run scripts/seed.ts \\\\\n --scenario \"$INPUT_SCENARIO\" \\\\\n --count \"$INPUT_COUNT\"\n\n Example — run migrations (no params needed):\n #!/bin/bash\n set -euo pipefail\n cd /workspace\n sb-compose exec -T app-api bash -c \\\\\n 'cd /app/packages/db && bun run drizzle-kit migrate'\n echo '{\"status\":\"completed\"}'\n\n Available context variables:\n SANDBOX_PUBLIC_URL — the instance's public URL\n SANDBOX_INSTANCE_ID — the instance UUID\n\nAGENT AUTHORING:\n During template creation, the sandbox agent can publish actions alongside\n artifacts using the publish_action tool. The agent analyzes the codebase\n and generates action definitions for common operations (user creation,\n data seeding, migrations, etc.). These are stored on the template and\n available on all instances provisioned from it.\n\n publish_action accepts an optional credentialOutputConfig — for any action\n that provisions login material (e.g. a user with email/password), the agent\n should set this so the resulting credential lands in the property vault and\n can be exercised by browser automation.\n`.trim(),\n};\n\nexport const SANDBOX_GUIDE_SECTIONS: GuideSection[] = [\n GOAL,\n RESEARCH,\n ARTIFACTS,\n ACTIONS,\n VERIFICATION,\n CHECKLIST,\n];\n"],"mappings":"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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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","names":[]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
getArgValue
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-VYTIJ6HJ.js";
|
|
5
5
|
import {
|
|
6
6
|
formatSemanticSnapshot
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-7OO3RJYR.js";
|
|
8
8
|
|
|
9
9
|
// src/trace.ts
|
|
10
10
|
import fs from "fs";
|
|
@@ -55,7 +55,7 @@ async function runTraceSnapshot(argv) {
|
|
|
55
55
|
process.exit(1);
|
|
56
56
|
}
|
|
57
57
|
const filter = filterArg;
|
|
58
|
-
const { reconstructSnapshots, downloadTrace, cleanupTrace } = await import("./src-
|
|
58
|
+
const { reconstructSnapshots, downloadTrace, cleanupTrace } = await import("./src-B7CPYFLS.js");
|
|
59
59
|
let localPath = tracePath;
|
|
60
60
|
let isTemp = false;
|
|
61
61
|
if (tracePath.startsWith("http://") || tracePath.startsWith("https://")) {
|
|
@@ -175,4 +175,4 @@ function slugify(text) {
|
|
|
175
175
|
export {
|
|
176
176
|
runTrace
|
|
177
177
|
};
|
|
178
|
-
//# sourceMappingURL=chunk-
|
|
178
|
+
//# sourceMappingURL=chunk-E2FUQJVO.js.map
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
apiRequest
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PVWG3V62.js";
|
|
5
5
|
import {
|
|
6
6
|
getArgValue,
|
|
7
7
|
hasFlag,
|
|
8
8
|
resolveConfig
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-VYTIJ6HJ.js";
|
|
10
10
|
|
|
11
11
|
// src/tag.ts
|
|
12
12
|
import process from "process";
|
|
@@ -423,4 +423,4 @@ export {
|
|
|
423
423
|
applyWorkflowTagFlags,
|
|
424
424
|
runTag
|
|
425
425
|
};
|
|
426
|
-
//# sourceMappingURL=chunk-
|
|
426
|
+
//# sourceMappingURL=chunk-GFP4UEUT.js.map
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
envToProfile,
|
|
5
5
|
getArgValue,
|
|
6
6
|
saveAuth
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-VYTIJ6HJ.js";
|
|
8
8
|
import {
|
|
9
9
|
readBrandEnv
|
|
10
10
|
} from "./chunk-32P26BZT.js";
|
|
@@ -195,4 +195,4 @@ export {
|
|
|
195
195
|
switchOrg,
|
|
196
196
|
runLogin
|
|
197
197
|
};
|
|
198
|
-
//# sourceMappingURL=chunk-
|
|
198
|
+
//# sourceMappingURL=chunk-H7ZML5QL.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
isTriageEnabledForExecution
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-VHVOTGBQ.js";
|
|
5
5
|
import {
|
|
6
6
|
resolveConfig
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-VYTIJ6HJ.js";
|
|
8
8
|
|
|
9
9
|
// src/remote-test.ts
|
|
10
10
|
import process from "process";
|
|
@@ -395,4 +395,4 @@ async function runRemoteTest(argv) {
|
|
|
395
395
|
export {
|
|
396
396
|
runRemoteTest
|
|
397
397
|
};
|
|
398
|
-
//# sourceMappingURL=chunk-
|
|
398
|
+
//# sourceMappingURL=chunk-IHCPMYKU.js.map
|