@runcofactor/cli 0.10.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/README.md +125 -0
- package/dist/api-catalog-GFGCLVS4.js +401 -0
- package/dist/api-catalog-GFGCLVS4.js.map +1 -0
- package/dist/attachment-SJC7D7FY.js +276 -0
- package/dist/attachment-SJC7D7FY.js.map +1 -0
- package/dist/auth-EN6NQCJY.js +31 -0
- package/dist/auth-EN6NQCJY.js.map +1 -0
- package/dist/bin.js +7 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-3CZU7XCS.js +272 -0
- package/dist/chunk-3CZU7XCS.js.map +1 -0
- package/dist/chunk-4NRDONSC.js +1048 -0
- package/dist/chunk-4NRDONSC.js.map +1 -0
- package/dist/chunk-4PNOV7OV.js +51980 -0
- package/dist/chunk-4PNOV7OV.js.map +1 -0
- package/dist/chunk-52IO7ACJ.js +2941 -0
- package/dist/chunk-52IO7ACJ.js.map +1 -0
- package/dist/chunk-6LEZCKKV.js +663 -0
- package/dist/chunk-6LEZCKKV.js.map +1 -0
- package/dist/chunk-A4OFTUVV.js +170 -0
- package/dist/chunk-A4OFTUVV.js.map +1 -0
- package/dist/chunk-B2VISN2V.js +2459 -0
- package/dist/chunk-B2VISN2V.js.map +1 -0
- package/dist/chunk-CLAWT7BJ.js +271 -0
- package/dist/chunk-CLAWT7BJ.js.map +1 -0
- package/dist/chunk-GKY2KXO5.js +281 -0
- package/dist/chunk-GKY2KXO5.js.map +1 -0
- package/dist/chunk-I4QLI3JO.js +413 -0
- package/dist/chunk-I4QLI3JO.js.map +1 -0
- package/dist/chunk-IMPZAX6L.js +180 -0
- package/dist/chunk-IMPZAX6L.js.map +1 -0
- package/dist/chunk-J6SH7GQT.js +241 -0
- package/dist/chunk-J6SH7GQT.js.map +1 -0
- package/dist/chunk-JDBCZ3Z2.js +298 -0
- package/dist/chunk-JDBCZ3Z2.js.map +1 -0
- package/dist/chunk-JICSYFRJ.js +46 -0
- package/dist/chunk-JICSYFRJ.js.map +1 -0
- package/dist/chunk-JWHYXEA2.js +32 -0
- package/dist/chunk-JWHYXEA2.js.map +1 -0
- package/dist/chunk-N7HFALX3.js +88 -0
- package/dist/chunk-N7HFALX3.js.map +1 -0
- package/dist/chunk-Q4BO4Z5V.js +178 -0
- package/dist/chunk-Q4BO4Z5V.js.map +1 -0
- package/dist/chunk-QFDOWABX.js +195 -0
- package/dist/chunk-QFDOWABX.js.map +1 -0
- package/dist/chunk-SJJW77WN.js +605 -0
- package/dist/chunk-SJJW77WN.js.map +1 -0
- package/dist/chunk-VKVL7WBN.js +50 -0
- package/dist/chunk-VKVL7WBN.js.map +1 -0
- package/dist/chunk-WOHL4FTB.js +353 -0
- package/dist/chunk-WOHL4FTB.js.map +1 -0
- package/dist/chunk-XQXVG7RA.js +426 -0
- package/dist/chunk-XQXVG7RA.js.map +1 -0
- package/dist/chunk-ZD3KHESI.js +94 -0
- package/dist/chunk-ZD3KHESI.js.map +1 -0
- package/dist/credential-COLTBV6D.js +450 -0
- package/dist/credential-COLTBV6D.js.map +1 -0
- package/dist/credential-template-EEPYFG7R.js +291 -0
- package/dist/credential-template-EEPYFG7R.js.map +1 -0
- package/dist/debug-workflow-KOVH5GOP.js +259 -0
- package/dist/debug-workflow-KOVH5GOP.js.map +1 -0
- package/dist/dist-44PZR7BQ.js +423 -0
- package/dist/dist-44PZR7BQ.js.map +1 -0
- package/dist/docs-3SSORHB5.js +366 -0
- package/dist/docs-3SSORHB5.js.map +1 -0
- package/dist/doctor-OTSIWV6G.js +132 -0
- package/dist/doctor-OTSIWV6G.js.map +1 -0
- package/dist/feature-flag-T2LRZME3.js +283 -0
- package/dist/feature-flag-T2LRZME3.js.map +1 -0
- package/dist/file-type-S3UUBVCT.js +4205 -0
- package/dist/file-type-S3UUBVCT.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +982 -0
- package/dist/index.js.map +1 -0
- package/dist/init-TGWRECT2.js +208 -0
- package/dist/init-TGWRECT2.js.map +1 -0
- package/dist/install-DXSNWZKO.js +295 -0
- package/dist/install-DXSNWZKO.js.map +1 -0
- package/dist/issues-XIZOPX6T.js +564 -0
- package/dist/issues-XIZOPX6T.js.map +1 -0
- package/dist/knobs-NBINZIUB.js +295 -0
- package/dist/knobs-NBINZIUB.js.map +1 -0
- package/dist/list-3TDHK637.js +59 -0
- package/dist/list-3TDHK637.js.map +1 -0
- package/dist/local-HICVD6PU.js +100 -0
- package/dist/local-HICVD6PU.js.map +1 -0
- package/dist/login-UOIUXVUK.js +315 -0
- package/dist/login-UOIUXVUK.js.map +1 -0
- package/dist/mcp-54IJEOUA.js +1956 -0
- package/dist/mcp-54IJEOUA.js.map +1 -0
- package/dist/pdf-extract-ABWV3L5X.js +13 -0
- package/dist/pdf-extract-ABWV3L5X.js.map +1 -0
- package/dist/pdfjs-DQ2YA5S3.js +41561 -0
- package/dist/pdfjs-DQ2YA5S3.js.map +1 -0
- package/dist/property-UXLOLPTD.js +701 -0
- package/dist/property-UXLOLPTD.js.map +1 -0
- package/dist/property-skill-guide.md +189 -0
- package/dist/psql-BYUUNLGG.js +115 -0
- package/dist/psql-BYUUNLGG.js.map +1 -0
- package/dist/record-4TR2UIIS.js +233 -0
- package/dist/record-4TR2UIIS.js.map +1 -0
- package/dist/redis-35VBUYW7.js +116 -0
- package/dist/redis-35VBUYW7.js.map +1 -0
- package/dist/release-OOLTUBUT.js +354 -0
- package/dist/release-OOLTUBUT.js.map +1 -0
- package/dist/sandbox-XGSUU6G7.js +2046 -0
- package/dist/sandbox-XGSUU6G7.js.map +1 -0
- package/dist/scenarios-GWLJEGHA.js +280 -0
- package/dist/scenarios-GWLJEGHA.js.map +1 -0
- package/dist/session-MYFNZPRP.js +2414 -0
- package/dist/session-MYFNZPRP.js.map +1 -0
- package/dist/skill-KM34LDMO.js +1839 -0
- package/dist/skill-KM34LDMO.js.map +1 -0
- package/dist/skills/debug-run/SKILL.md +262 -0
- package/dist/skills/debug-run/history/1.0.0.md +244 -0
- package/dist/skills/debug-run/history/1.0.1.md +245 -0
- package/dist/skills/debug-run/history/1.0.2.md +254 -0
- package/dist/skills/debug-run/history/1.1.0.md +262 -0
- package/dist/skills/debug-run/version.json +3 -0
- package/dist/spec-RA3CFEKG.js +951 -0
- package/dist/spec-RA3CFEKG.js.map +1 -0
- package/dist/src-LB4HBTJQ.js +1516 -0
- package/dist/src-LB4HBTJQ.js.map +1 -0
- package/dist/src-N3TPKGTY.js +244 -0
- package/dist/src-N3TPKGTY.js.map +1 -0
- package/dist/src-UVDX5UP6.js +17 -0
- package/dist/src-UVDX5UP6.js.map +1 -0
- package/dist/start-PSEQSQA3.js +151 -0
- package/dist/start-PSEQSQA3.js.map +1 -0
- package/dist/tag-YY6YAABQ.js +22 -0
- package/dist/tag-YY6YAABQ.js.map +1 -0
- package/dist/task-IQC3YR5K.js +922 -0
- package/dist/task-IQC3YR5K.js.map +1 -0
- package/dist/test-suite-DZNJFXDW.js +394 -0
- package/dist/test-suite-DZNJFXDW.js.map +1 -0
- package/dist/trace-HXIW7U2D.js +14 -0
- package/dist/trace-HXIW7U2D.js.map +1 -0
- package/dist/triage-ORG45R5W.js +16 -0
- package/dist/triage-ORG45R5W.js.map +1 -0
- package/dist/upgrade-L2VYMP2V.js +61 -0
- package/dist/upgrade-L2VYMP2V.js.map +1 -0
- package/dist/workflow-XYFH5LTP.js +3708 -0
- package/dist/workflow-XYFH5LTP.js.map +1 -0
- package/dist/workflow-cache-NQJQFWMV.js +253 -0
- package/dist/workflow-cache-NQJQFWMV.js.map +1 -0
- package/dist/workflow-debug-BGD4UIXU.js +927 -0
- package/dist/workflow-debug-BGD4UIXU.js.map +1 -0
- package/dist/workflow-proposals-MTUDX46O.js +382 -0
- package/dist/workflow-proposals-MTUDX46O.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,1048 @@
|
|
|
1
|
+
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// ../content/src/print-guide.ts
|
|
4
|
+
function printGuide(allSections, section, json) {
|
|
5
|
+
const sections = section ? allSections.filter((s) => s.slug === section) : allSections;
|
|
6
|
+
if (section && sections.length === 0) {
|
|
7
|
+
console.error(
|
|
8
|
+
`Unknown guide section: "${section}". Available: ${allSections.map((s) => s.slug).join(", ")}`
|
|
9
|
+
);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
if (json) {
|
|
13
|
+
console.log(JSON.stringify(sections, null, 2));
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
for (const s of sections) {
|
|
17
|
+
console.log(`
|
|
18
|
+
${"=".repeat(72)}`);
|
|
19
|
+
console.log(` ${s.title}`);
|
|
20
|
+
console.log(`${"=".repeat(72)}
|
|
21
|
+
`);
|
|
22
|
+
console.log(s.content);
|
|
23
|
+
console.log("");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ../content/src/sandbox-guide.ts
|
|
28
|
+
var GOAL = {
|
|
29
|
+
title: "Goal & Tools",
|
|
30
|
+
slug: "goal",
|
|
31
|
+
content: `
|
|
32
|
+
Your goal is to turn a git repository into a running, containerized development
|
|
33
|
+
environment. The end state is a sandbox URL that loads the app in a browser.
|
|
34
|
+
|
|
35
|
+
WORKFLOW:
|
|
36
|
+
1. canary sandbox create <property> Create a template + authoring instance
|
|
37
|
+
2. Research the app Read the codebase to understand what it needs
|
|
38
|
+
3. Set config values API keys, secrets \u2192 pushed to instance as .canary/.env
|
|
39
|
+
4. Upload artifacts Dockerfile, docker-compose, setup script, etc. \u2192 pushed to instance
|
|
40
|
+
5. canary sandbox build <instance-id> Build images, start containers, generate nginx
|
|
41
|
+
6. Check diagnostics & logs Verify the app is running
|
|
42
|
+
7. Fix artifacts, re-upload, re-build Iterate until the app loads in browser
|
|
43
|
+
8. Mark checklist items complete Record what you did
|
|
44
|
+
|
|
45
|
+
Each artifact upload and config set immediately pushes the file to the instance.
|
|
46
|
+
The "build" command triggers the actual rebuild and restart.
|
|
47
|
+
Batch your changes (upload all artifacts, set all config), then build once.
|
|
48
|
+
|
|
49
|
+
YOUR TOOLS:
|
|
50
|
+
|
|
51
|
+
Config \u2014 set environment variables (pushed to instance .canary/.env immediately):
|
|
52
|
+
canary sandbox config set <template-id> <key> <value>
|
|
53
|
+
canary sandbox config list <template-id>
|
|
54
|
+
|
|
55
|
+
SANDBOX_URL is auto-injected \u2014 use it in compose for browser-facing URLs.
|
|
56
|
+
|
|
57
|
+
Artifacts \u2014 files that define the sandbox (pushed to instance on upload):
|
|
58
|
+
canary sandbox artifact create <template-id> <type> <file>
|
|
59
|
+
canary sandbox artifact get <template-id> <type>
|
|
60
|
+
|
|
61
|
+
Types: dockerfile, docker-compose, setup-script, startup-script, seed-script, file
|
|
62
|
+
|
|
63
|
+
Build \u2014 rebuild and start services:
|
|
64
|
+
canary sandbox build <instance-id>
|
|
65
|
+
canary sandbox build <instance-id> --no-wait
|
|
66
|
+
|
|
67
|
+
Generates nginx routing, injects Docker networking, writes file artifacts,
|
|
68
|
+
runs setup script, then runs compose up, then runs startup script.
|
|
69
|
+
Call this after uploading artifacts.
|
|
70
|
+
|
|
71
|
+
First builds on large projects may exceed gateway timeouts. The CLI handles
|
|
72
|
+
this automatically \u2014 it detects the timeout and polls diagnostics until
|
|
73
|
+
containers are healthy. Use --no-wait to fire-and-forget and poll manually.
|
|
74
|
+
|
|
75
|
+
Diagnostics & logs \u2014 check container health and read service output:
|
|
76
|
+
canary sandbox diagnostics <instance-id>
|
|
77
|
+
canary sandbox logs <instance-id> --service <name>
|
|
78
|
+
|
|
79
|
+
Run-command \u2014 escape hatch for debugging and one-off commands:
|
|
80
|
+
canary sandbox run-command <instance-id> "<command>"
|
|
81
|
+
|
|
82
|
+
Checklist \u2014 record your progress:
|
|
83
|
+
canary sandbox checklist <template-id>
|
|
84
|
+
canary sandbox checklist complete <template-id> <item-id> [options]
|
|
85
|
+
canary sandbox checklist skip <template-id> <item-id>
|
|
86
|
+
|
|
87
|
+
PRINCIPLES:
|
|
88
|
+
- Artifacts are the source of truth. Every fix must update the artifact file and re-upload.
|
|
89
|
+
- Never patch source files. Use Docker env vars, CMD overrides, setup/startup scripts.
|
|
90
|
+
- Code is volume-mounted at /workspace, not baked into the image.
|
|
91
|
+
- The platform handles nginx routing and Docker networking \u2014 you don't need to.
|
|
92
|
+
`.trim()
|
|
93
|
+
};
|
|
94
|
+
var RESEARCH = {
|
|
95
|
+
title: "Research the App",
|
|
96
|
+
slug: "research",
|
|
97
|
+
content: `
|
|
98
|
+
Before writing any artifacts, research the application thoroughly.
|
|
99
|
+
|
|
100
|
+
1. ENV VAR VALIDATION
|
|
101
|
+
Find the app's environment validation schema (Zod, Joi, dotenv, etc.).
|
|
102
|
+
List ALL required (non-optional) variables. Many apps fail fast on missing vars.
|
|
103
|
+
|
|
104
|
+
Common locations:
|
|
105
|
+
- src/env.ts, src/config.ts, src/environment.ts
|
|
106
|
+
- .env.example, .env.sample
|
|
107
|
+
|
|
108
|
+
Classify each var:
|
|
109
|
+
- AUTO-WIRED: Internal service URLs (DATABASE_URL, REDIS_URL) \u2014 hardcode in compose
|
|
110
|
+
- PLACEHOLDER: OAuth creds, S3 buckets \u2014 use dummy values, app boots without them
|
|
111
|
+
- SENSITIVE: API keys (ANTHROPIC_API_KEY) \u2014 goes in template configSchema
|
|
112
|
+
- DISABLED: Worker flags, schedulers \u2014 set to false in compose
|
|
113
|
+
|
|
114
|
+
2. REQUIRED SERVICES
|
|
115
|
+
What databases, caches, queues does the app need?
|
|
116
|
+
Check: docker-compose.yml, package.json deps, infra/ configs, README
|
|
117
|
+
Common: PostgreSQL, Redis, MongoDB, RabbitMQ, Elasticsearch
|
|
118
|
+
|
|
119
|
+
3. MIGRATION STRATEGY
|
|
120
|
+
Does the migrate script depend on cloud tooling (SST, AWS SDK, Terraform)?
|
|
121
|
+
If yes, use the ORM's CLI directly in the startup script via "sb-compose exec":
|
|
122
|
+
- Drizzle: sb-compose exec -T app-api bash -c 'cd /app/packages/db && bun run drizzle-kit migrate'
|
|
123
|
+
- Prisma: sb-compose exec -T app-api bash -c 'bunx prisma migrate deploy'
|
|
124
|
+
- Rails: sb-compose exec -T app-api rails db:migrate
|
|
125
|
+
- Django: sb-compose exec -T app-api python manage.py migrate
|
|
126
|
+
Put migrations in the startup script, NOT as a compose service (see Artifacts).
|
|
127
|
+
Do NOT use bunx for JS migrations \u2014 it downloads a fresh copy. Use "bun run" instead.
|
|
128
|
+
|
|
129
|
+
4. PACKAGE BUILD ORDER (monorepos)
|
|
130
|
+
Which workspace packages export from dist/ and must be pre-built?
|
|
131
|
+
Check package.json "main" or "exports" fields for "./dist/" references.
|
|
132
|
+
These builds go in the startup script via sb-compose exec, NOT the Dockerfile.
|
|
133
|
+
When running scripts via sb-compose exec, cd to the package directory
|
|
134
|
+
where the dependency is declared. Bun workspace module resolution won't
|
|
135
|
+
find deps from the monorepo root.
|
|
136
|
+
|
|
137
|
+
5. DEV SERVER HOST RESTRICTIONS
|
|
138
|
+
Modern dev servers block unknown hostnames:
|
|
139
|
+
- Vite 5.4+: upload a wrapper config via "file" artifact with allowedHosts: true
|
|
140
|
+
- Vite 6+: --allowedHosts all works as a CLI flag in compose command
|
|
141
|
+
- Next.js: hostname config in next.config.js
|
|
142
|
+
- Webpack: allowedHosts in devServer config
|
|
143
|
+
Fix via file artifact or compose CMD override \u2014 never patch the config file.
|
|
144
|
+
Check the Vite version in package.json \u2014 if >= 5.4, create the allowedHosts
|
|
145
|
+
file artifact PROACTIVELY before the first build, not after a browser error.
|
|
146
|
+
|
|
147
|
+
GOTCHA \u2014 relative imports from the wrapper config:
|
|
148
|
+
File artifacts can only land under /workspace/.canary/ (see Artifacts \u2192
|
|
149
|
+
PLATFORM ARCHITECTURE). But Vite resolves esbuild imports inside a
|
|
150
|
+
--config file relative to the config file's *own location*, not the
|
|
151
|
+
working_dir. So if your wrapper does
|
|
152
|
+
import baseConfig from "./vite.config";
|
|
153
|
+
and you point vite at /app/.canary/vite.sandbox.config.ts, esbuild
|
|
154
|
+
looks for /app/.canary/vite.config.ts and fails.
|
|
155
|
+
Fix: stage the wrapper into the repo tree at setup time. Add a
|
|
156
|
+
setup-script artifact that does
|
|
157
|
+
cp /workspace/.canary/vite.sandbox.config.ts \\
|
|
158
|
+
/workspace/apps/web/vite.sandbox.config.ts
|
|
159
|
+
and reference the file via its repo-tree name in compose
|
|
160
|
+
(\`--config vite.sandbox.config.ts\` with working_dir /app/apps/web).
|
|
161
|
+
The .canary/ copy stays the source of truth; the staged copy is just
|
|
162
|
+
a build-time stamp.
|
|
163
|
+
|
|
164
|
+
6. WORKERS & DAEMONS TO DISABLE
|
|
165
|
+
Sandbox typically only needs the API server + web frontend.
|
|
166
|
+
Disable: job runners, schedulers, stall detectors, cron, coordinators.
|
|
167
|
+
Look for env flags like WORKER_ENABLED, SCHEDULER_ENABLED, etc.
|
|
168
|
+
`.trim()
|
|
169
|
+
};
|
|
170
|
+
var ARTIFACTS = {
|
|
171
|
+
title: "Artifacts",
|
|
172
|
+
slug: "artifacts",
|
|
173
|
+
content: `
|
|
174
|
+
Artifacts are the files that define your sandbox. Write them locally, then upload:
|
|
175
|
+
canary sandbox artifact create <template-id> <type> <local-file>
|
|
176
|
+
|
|
177
|
+
IMPORTANT \u2014 ARTIFACT SCOPE:
|
|
178
|
+
Artifact uploads are TEMPLATE-level, not instance-level. Uploading an artifact
|
|
179
|
+
updates the template definition and pushes the file to the AUTHORING instance
|
|
180
|
+
only. Running instances provisioned from the template are NOT updated \u2014 they
|
|
181
|
+
use whatever was baked in at provision time. To apply artifact changes to an
|
|
182
|
+
existing non-authoring instance, you must rebuild it:
|
|
183
|
+
canary sandbox build <instance-id>
|
|
184
|
+
|
|
185
|
+
PLATFORM ARCHITECTURE:
|
|
186
|
+
The sandbox filesystem is split into two areas:
|
|
187
|
+
- /workspace checked-out repo and user-owned files
|
|
188
|
+
- /workspace/.canary platform-managed compose, config, and helper scripts
|
|
189
|
+
|
|
190
|
+
Your compose file is saved as /workspace/.canary/docker-compose.override.yml and
|
|
191
|
+
merged with the platform's base compose at /workspace/.canary/docker-compose.yml,
|
|
192
|
+
which provides:
|
|
193
|
+
- nginx-proxy: reverse proxy on port 80 (routes based on sandbox.route labels)
|
|
194
|
+
- sandbox-agent: platform agent on port 8080
|
|
195
|
+
- minio: S3-compatible object store at http://minio:9000 (creds:
|
|
196
|
+
minioadmin / minioadmin). See MINIO BUCKET below \u2014 the platform
|
|
197
|
+
doesn't auto-wire the endpoint env var or create buckets for you.
|
|
198
|
+
|
|
199
|
+
You do NOT need to declare any of these in your docker-compose.yml \u2014 the
|
|
200
|
+
platform injects them. Defining a service with one of these reserved
|
|
201
|
+
names is rejected at artifact-upload time with code
|
|
202
|
+
SANDBOX_RESERVED_SERVICE_NAME. The full reserved list is:
|
|
203
|
+
nginx-proxy
|
|
204
|
+
sandbox-agent
|
|
205
|
+
minio
|
|
206
|
+
minio-init
|
|
207
|
+
docker-socket-proxy
|
|
208
|
+
|
|
209
|
+
CONTAINER PATHS \u2014 the bind mount maps the repo root to /app inside
|
|
210
|
+
containers, while file artifacts are uploaded under /workspace/.canary
|
|
211
|
+
on the host. So the same file has two paths:
|
|
212
|
+
/workspace/.canary/foo.ts \u2190 the path you upload to from the CLI
|
|
213
|
+
/app/.canary/foo.ts \u2190 the path your compose command/services
|
|
214
|
+
reference inside the container
|
|
215
|
+
Reference the in-container path in any compose \`command\` array, exec
|
|
216
|
+
call, or script file path that runs inside a service. The host-side
|
|
217
|
+
/workspace path only appears in startup.sh / setup.sh (which run on
|
|
218
|
+
the host, not inside a container).
|
|
219
|
+
|
|
220
|
+
MINIO BUCKET (S3 emulation):
|
|
221
|
+
The platform provides a MinIO container at http://minio:9000 but does
|
|
222
|
+
NOT inject AWS_ENDPOINT_URL_S3 or create your bucket. You must:
|
|
223
|
+
|
|
224
|
+
1. Wire the endpoint into your service env in compose:
|
|
225
|
+
AWS_ENDPOINT_URL_S3: http://minio:9000
|
|
226
|
+
AWS_ACCESS_KEY_ID: minioadmin
|
|
227
|
+
AWS_SECRET_ACCESS_KEY: minioadmin
|
|
228
|
+
AWS_REGION: us-east-1
|
|
229
|
+
S3_BUCKET_NAME: <your-bucket>
|
|
230
|
+
|
|
231
|
+
2. Create the bucket on every build via a one-shot service:
|
|
232
|
+
mc-init:
|
|
233
|
+
image: minio/mc
|
|
234
|
+
restart: "no"
|
|
235
|
+
entrypoint:
|
|
236
|
+
- sh
|
|
237
|
+
- -c
|
|
238
|
+
- >
|
|
239
|
+
mc alias set local http://minio:9000 minioadmin minioadmin &&
|
|
240
|
+
mc mb -p local/\${S3_BUCKET_NAME:-app-bucket} ||
|
|
241
|
+
true
|
|
242
|
+
|
|
243
|
+
Most AWS SDK v3 clients honor AWS_ENDPOINT_URL_S3 natively. If your
|
|
244
|
+
app passes a custom S3 client, also set forcePathStyle: true (MinIO
|
|
245
|
+
doesn't support virtual-host-style bucket addressing).
|
|
246
|
+
|
|
247
|
+
Compose still runs with /workspace as the project directory. That means user
|
|
248
|
+
compose content keeps the same repo-root-relative behavior for build contexts,
|
|
249
|
+
bind mounts, and referenced files.
|
|
250
|
+
|
|
251
|
+
COMPOSE INVOCATION: use the pre-installed "sb-compose" wrapper on the host.
|
|
252
|
+
It expands to docker compose with the canonical flag set:
|
|
253
|
+
docker compose --project-directory /workspace \\
|
|
254
|
+
--env-file /workspace/.canary/.env \\
|
|
255
|
+
-f /workspace/.canary/docker-compose.yml \\
|
|
256
|
+
-f /workspace/.canary/docker-compose.override.yml \\
|
|
257
|
+
"$@"
|
|
258
|
+
Call it from startup scripts, action scripts, and run-command the same way:
|
|
259
|
+
sb-compose exec -T app-api bash -c 'bun run build'
|
|
260
|
+
sb-compose ps
|
|
261
|
+
sb-compose logs --tail 50 app-api
|
|
262
|
+
The wrapper is available as /usr/local/bin/sb-compose (symlink) and
|
|
263
|
+
/workspace/.canary/sb-compose. If you invoke raw "docker compose" yourself
|
|
264
|
+
you must pass the same flags \u2014 otherwise paths and variable interpolation
|
|
265
|
+
(\${SANDBOX_URL} et al.) will resolve incorrectly.
|
|
266
|
+
|
|
267
|
+
The platform injects a shared Docker network. On "build", the sequence is:
|
|
268
|
+
1. Write file artifacts to their destination paths
|
|
269
|
+
2. Run setup script (if present)
|
|
270
|
+
3. docker compose down (cleanup previous containers)
|
|
271
|
+
4. docker compose build + up
|
|
272
|
+
5. Reload nginx
|
|
273
|
+
6. Wait for long-running services to accept "sb-compose exec" before continuing
|
|
274
|
+
7. Run startup script (if present)
|
|
275
|
+
|
|
276
|
+
Step 6 is automatic. You don't need to add your own "wait for postgres"
|
|
277
|
+
loops at the top of startup.sh \u2014 by the time startup.sh runs, every
|
|
278
|
+
non-one-shot service is exec-able. Service healthchecks inside the
|
|
279
|
+
containers (e.g. postgres pg_isready) are still your responsibility if
|
|
280
|
+
your app needs to wait on them.
|
|
281
|
+
|
|
282
|
+
Do NOT define services named "nginx-proxy", "sandbox-agent", or "minio"
|
|
283
|
+
in your compose.
|
|
284
|
+
|
|
285
|
+
DOCKERFILE (type: dockerfile)
|
|
286
|
+
A base image that provides the runtime. Source code is volume-mounted, NOT copied in.
|
|
287
|
+
Written to /workspace/.canary/Dockerfile.sandbox (avoids conflicting with the repo's own Dockerfile).
|
|
288
|
+
Reference it in compose as: build: { context: ., dockerfile: .canary/Dockerfile.sandbox }
|
|
289
|
+
|
|
290
|
+
Structure:
|
|
291
|
+
FROM <runtime-base>
|
|
292
|
+
RUN apt-get update && apt-get install -y <system-deps>
|
|
293
|
+
RUN npm install -g <global-tools>
|
|
294
|
+
WORKDIR /app
|
|
295
|
+
|
|
296
|
+
Rules:
|
|
297
|
+
- NO "COPY . ." \u2014 code comes via volume mount at /workspace
|
|
298
|
+
- NO "RUN npm build" \u2014 builds happen in the setup script
|
|
299
|
+
- Include ALL system deps (chromium, ffmpeg, python, etc.)
|
|
300
|
+
- For browser automation: use mcr.microsoft.com/playwright as base
|
|
301
|
+
- Do NOT use ENTRYPOINT \u2014 it breaks one-shot services (deps, migrate) whose
|
|
302
|
+
command gets passed as args to the entrypoint instead of running directly
|
|
303
|
+
|
|
304
|
+
DOCKER COMPOSE (type: docker-compose)
|
|
305
|
+
Defines all services. Platform handles networking and nginx routing.
|
|
306
|
+
|
|
307
|
+
Typical services:
|
|
308
|
+
deps (one-shot) \u2014 installs dependencies into named volume, restart: "no"
|
|
309
|
+
database \u2014 official image with healthcheck (postgres, mysql, etc.)
|
|
310
|
+
cache \u2014 redis/memcached with healthcheck
|
|
311
|
+
app-api \u2014 application server in dev mode (hot reload)
|
|
312
|
+
app-web \u2014 frontend dev server bound to 0.0.0.0
|
|
313
|
+
|
|
314
|
+
Do NOT use a migrate service in compose. If the migrate container fails once
|
|
315
|
+
(bad SQL, missing env var, wrong path), depends_on with
|
|
316
|
+
service_completed_successfully is permanently unmet \u2014 app-api will never
|
|
317
|
+
start, even after you fix the migration. "docker compose restart" and
|
|
318
|
+
"docker compose up" won't help; only "docker compose down" + fresh up works.
|
|
319
|
+
Put migrations in the startup script instead (runs after compose up).
|
|
320
|
+
|
|
321
|
+
ROUTING LABELS \u2014 declare how nginx should route to your services:
|
|
322
|
+
services:
|
|
323
|
+
app-api:
|
|
324
|
+
labels:
|
|
325
|
+
sandbox.route: "/api"
|
|
326
|
+
sandbox.port: "3000"
|
|
327
|
+
app-web:
|
|
328
|
+
labels:
|
|
329
|
+
sandbox.route: "/"
|
|
330
|
+
sandbox.port: "5173"
|
|
331
|
+
|
|
332
|
+
The platform reads these labels and generates nginx config on "build".
|
|
333
|
+
If no labels, all traffic routes to the first non-infrastructure service.
|
|
334
|
+
|
|
335
|
+
PREFIX STRIPPING: the generated nginx location uses a trailing-slash
|
|
336
|
+
proxy_pass (proxy_pass http://<service>:<port>/), so the matched prefix
|
|
337
|
+
is stripped from the upstream request. With sandbox.route: "/api", a
|
|
338
|
+
browser hit to "/api/health" is delivered to the upstream as "/health".
|
|
339
|
+
Pick VITE_API_URL, OAuth redirect URIs, etc. accordingly.
|
|
340
|
+
|
|
341
|
+
ENV VAR STRATEGY \u2014 use \${VAR} syntax, values come from config + .env:
|
|
342
|
+
- Auto-wired (hardcode): DATABASE_URL=postgres://user:pass@postgres:5432/db
|
|
343
|
+
- From config: ANTHROPIC_API_KEY=\${ANTHROPIC_API_KEY}
|
|
344
|
+
- Platform-injected: VITE_API_URL=\${SANDBOX_URL}/api
|
|
345
|
+
- Disabled: WORKER_ENABLED=false
|
|
346
|
+
|
|
347
|
+
SANDBOX_URL is auto-injected by the platform (e.g. https://<id>.env.trycanary.ai).
|
|
348
|
+
Use it for any URL a browser needs to reach the sandbox.
|
|
349
|
+
|
|
350
|
+
Rules:
|
|
351
|
+
- Use named volumes for data persistence (postgres-data, redis-data, etc.)
|
|
352
|
+
- Bind-mount the repo (.:/app) rather than using named volumes for
|
|
353
|
+
node_modules. Per-package node_modules named volumes silently shadow the
|
|
354
|
+
bind mount on any service that doesn't mount them \u2014 a common monorepo
|
|
355
|
+
trap where "bun run build" fails because tsup is nowhere to be found.
|
|
356
|
+
The sandbox host is Linux EC2; bind mounts are fast.
|
|
357
|
+
- DO NOT define networks \u2014 the platform handles networking
|
|
358
|
+
- DO NOT expose ports to the host \u2014 nginx handles routing
|
|
359
|
+
- Keep relative paths repo-root-relative; compose runs from /workspace even
|
|
360
|
+
though the platform-managed compose files live in /workspace/.canary
|
|
361
|
+
- Escape shell variables in "command:" strings with $$ so compose leaves
|
|
362
|
+
them for the shell (e.g. command: ["bash", "-c", "exec foo $$BAR"]).
|
|
363
|
+
Unescaped \${VAR} is substituted at compose-time from the env file.
|
|
364
|
+
|
|
365
|
+
SETUP SCRIPT (type: setup-script)
|
|
366
|
+
Runs BEFORE docker compose up on every build. Runs on the HOST, not in a container.
|
|
367
|
+
Written to /workspace/.canary/setup.sh.
|
|
368
|
+
|
|
369
|
+
IMPORTANT: The host is a bare EC2 instance with only docker and basic linux tools.
|
|
370
|
+
App runtimes (node, bun, python, ruby) are NOT available on the host \u2014 they live
|
|
371
|
+
inside your Docker images. Do NOT run app commands directly here.
|
|
372
|
+
|
|
373
|
+
Script authoring rules:
|
|
374
|
+
- Treat /workspace as the repo root for any file access or repo command
|
|
375
|
+
- Do not assume the current directory matches the script's own path
|
|
376
|
+
- Use explicit paths or start with "cd /workspace" before repo-relative work
|
|
377
|
+
|
|
378
|
+
Use for host-level preparation only:
|
|
379
|
+
- Installing system packages (apt-get, yum)
|
|
380
|
+
- Downloading external tools or binaries
|
|
381
|
+
- Generating config files programmatically (prefer file artifacts for static files)
|
|
382
|
+
|
|
383
|
+
For most apps, you won't need a setup script \u2014 file artifacts and the startup
|
|
384
|
+
script cover the common cases. Skip this if you don't need host-level prep.
|
|
385
|
+
|
|
386
|
+
Rules:
|
|
387
|
+
- Must be idempotent \u2014 safe to run multiple times
|
|
388
|
+
- Never patch source files
|
|
389
|
+
- Non-zero exit is logged as a warning but does not block the build
|
|
390
|
+
|
|
391
|
+
STARTUP SCRIPT (type: startup-script)
|
|
392
|
+
Runs AFTER docker compose up on every build. Runs on the HOST, not in a container.
|
|
393
|
+
Written to /workspace/.canary/startup.sh.
|
|
394
|
+
|
|
395
|
+
This is where most app setup happens. Containers are running, so you can use
|
|
396
|
+
"sb-compose exec" to run commands inside them with the app runtime.
|
|
397
|
+
|
|
398
|
+
Script authoring rules:
|
|
399
|
+
- Start with "cd /workspace" or use explicit /workspace/... paths
|
|
400
|
+
- Do not assume the script runs from /workspace/.canary
|
|
401
|
+
- Prefer sb-compose exec for anything that needs the app runtime
|
|
402
|
+
- Use -T with exec (no TTY allocation \u2014 startup runs headless)
|
|
403
|
+
|
|
404
|
+
Use for:
|
|
405
|
+
- DB migrations via ORM CLI (use "bun run", NOT bunx \u2014 bunx downloads fresh)
|
|
406
|
+
- Building workspace packages that export from dist/
|
|
407
|
+
- Waiting for services to be healthy
|
|
408
|
+
- Triggering initial data loads
|
|
409
|
+
|
|
410
|
+
POST-MIGRATION RESTART (REQUIRED for many ORMs):
|
|
411
|
+
ORMs that introspect the schema at boot (Drizzle/effect-sql, Prisma with
|
|
412
|
+
prepared statements, SQLAlchemy with autoload, etc.) cache the pre-migration
|
|
413
|
+
schema on the first query. After you run migrations, you MUST restart the
|
|
414
|
+
API service, otherwise you'll see errors like:
|
|
415
|
+
relation "<table>" does not exist
|
|
416
|
+
even though psql confirms the table is there. Add "sb-compose restart" for
|
|
417
|
+
any service whose client introspects the schema at boot.
|
|
418
|
+
|
|
419
|
+
Example (Bun monorepo with Drizzle):
|
|
420
|
+
#!/bin/bash
|
|
421
|
+
set -e
|
|
422
|
+
# Build dist-exporting workspace packages the app/web import from
|
|
423
|
+
sb-compose exec -T app-api bash -c \\
|
|
424
|
+
'cd /app/packages/ui && bun run build'
|
|
425
|
+
# Run DB migrations inside the API container
|
|
426
|
+
sb-compose exec -T app-api bash -c \\
|
|
427
|
+
'cd /app/packages/db && bun run drizzle-kit migrate'
|
|
428
|
+
# Restart services so they pick up the fresh schema + built UI
|
|
429
|
+
sb-compose restart app-api app-web
|
|
430
|
+
# Wait for API to be healthy
|
|
431
|
+
for i in $(seq 1 30); do
|
|
432
|
+
sb-compose exec -T app-api curl -sf http://localhost:3000/health && break
|
|
433
|
+
sleep 2
|
|
434
|
+
done
|
|
435
|
+
|
|
436
|
+
Rules:
|
|
437
|
+
- Must be idempotent \u2014 safe to run multiple times
|
|
438
|
+
- Never patch source files
|
|
439
|
+
- Don't pre-wait for services to START \u2014 the platform handles that before
|
|
440
|
+
startup.sh runs (see Compose Architecture section). Do wait for service
|
|
441
|
+
*readiness* (app health, schema migrations applied) when your app
|
|
442
|
+
depends on it.
|
|
443
|
+
|
|
444
|
+
FILE (type: file)
|
|
445
|
+
Arbitrary sandbox-owned files written to the instance at a specific path.
|
|
446
|
+
Use for config files that need to exist before services start (e.g. Vite
|
|
447
|
+
wrapper configs, one-shot scripts that need app dependencies).
|
|
448
|
+
|
|
449
|
+
!!! File artifacts MUST be written under /workspace/.canary/ !!!
|
|
450
|
+
The repo tree (everything outside .canary/) is user-owned. Sandbox
|
|
451
|
+
helpers live in .canary/ so they never collide with real files and
|
|
452
|
+
are never committed back to the repo. The API will reject any
|
|
453
|
+
--path value outside /workspace/.canary/ with
|
|
454
|
+
SANDBOX_FILE_ARTIFACT_OUTSIDE_CANARY.
|
|
455
|
+
|
|
456
|
+
If your script needs app dependencies (e.g. \`postgres\` from the api
|
|
457
|
+
package), put a tiny package.json next to it in .canary/scripts/ and
|
|
458
|
+
\`bun install\` in the setup-script, OR invoke it via
|
|
459
|
+
\`sb-compose exec -T <svc> ...\` so it runs inside a container that
|
|
460
|
+
already has them.
|
|
461
|
+
|
|
462
|
+
Upload with --path to specify the destination:
|
|
463
|
+
canary sandbox artifact create <template-id> file <local-file> --path /workspace/.canary/<dest>
|
|
464
|
+
|
|
465
|
+
Example \u2014 Vite 5.x allowedHosts wrapper:
|
|
466
|
+
canary sandbox artifact create <template-id> file vite.sandbox.config.ts \\
|
|
467
|
+
--path /workspace/.canary/vite.sandbox.config.ts
|
|
468
|
+
|
|
469
|
+
ONE ROW PER FILENAME. File artifacts are keyed by (template, filename).
|
|
470
|
+
Uploading a file with the same filename as an earlier upload REPLACES
|
|
471
|
+
the earlier row. The CLI prints "(replaced)" on such uploads, and the
|
|
472
|
+
next build removes the previous destination path so the instance
|
|
473
|
+
filesystem mirrors the template set exactly.
|
|
474
|
+
|
|
475
|
+
If you want to retire a file, just upload something else with a
|
|
476
|
+
different filename \u2014 the stale file is cleaned up on next build via
|
|
477
|
+
.canary/.artifact-paths (a manifest the platform maintains).
|
|
478
|
+
|
|
479
|
+
File artifacts are written before the setup script runs, so they're
|
|
480
|
+
available for both setup and compose services.
|
|
481
|
+
|
|
482
|
+
SEED SCRIPT (type: seed-script)
|
|
483
|
+
Runs once on first provision to populate demo data. Optional.
|
|
484
|
+
Written to /workspace/.canary/seed.sh.
|
|
485
|
+
Skipped on subsequent builds (checks /workspace/.canary/.seeded).
|
|
486
|
+
|
|
487
|
+
CONFIG SCHEMA
|
|
488
|
+
Define which env vars need user input. Set when completing the docker-compose checklist item:
|
|
489
|
+
canary sandbox checklist complete <template-id> docker-compose-created \\
|
|
490
|
+
--status success \\
|
|
491
|
+
--summary "Compose with postgres, redis, api, web" \\
|
|
492
|
+
--config-schema '[
|
|
493
|
+
{"key": "ANTHROPIC_API_KEY", "required": true, "sensitive": true, "description": "Anthropic API key"},
|
|
494
|
+
{"key": "OPENAI_API_KEY", "required": false, "sensitive": true, "description": "OpenAI API key"}
|
|
495
|
+
]'
|
|
496
|
+
|
|
497
|
+
Set defaults:
|
|
498
|
+
canary sandbox config set <template-id> <key> <value>
|
|
499
|
+
canary sandbox config list <template-id>
|
|
500
|
+
`.trim()
|
|
501
|
+
};
|
|
502
|
+
var VERIFICATION = {
|
|
503
|
+
title: "Verification & Iteration",
|
|
504
|
+
slug: "verify",
|
|
505
|
+
content: `
|
|
506
|
+
After running "canary sandbox build", verify the sandbox is working. The iteration loop:
|
|
507
|
+
upload artifacts \u2192 build \u2192 check diagnostics \u2192 read logs \u2192 fix \u2192 re-upload \u2192 rebuild
|
|
508
|
+
|
|
509
|
+
COMMANDS:
|
|
510
|
+
# Check all container statuses at a glance
|
|
511
|
+
canary sandbox diagnostics <instance-id>
|
|
512
|
+
|
|
513
|
+
# Read logs for a specific service
|
|
514
|
+
canary sandbox logs <instance-id> --service app-api
|
|
515
|
+
canary sandbox logs <instance-id> --service app-web
|
|
516
|
+
canary sandbox logs <instance-id> --service postgres
|
|
517
|
+
|
|
518
|
+
# Run arbitrary commands on the instance (runs on the host VM by default)
|
|
519
|
+
canary sandbox run-command <instance-id> "docker compose ps"
|
|
520
|
+
canary sandbox run-command <instance-id> "echo hello"
|
|
521
|
+
|
|
522
|
+
# Run commands inside a specific Docker container
|
|
523
|
+
canary sandbox run-command <instance-id> --service app-api "bun --version"
|
|
524
|
+
canary sandbox run-command <instance-id> --service app-api "bun test"
|
|
525
|
+
|
|
526
|
+
# Test health endpoints
|
|
527
|
+
canary sandbox run-command <instance-id> "curl -s http://localhost:3000/health"
|
|
528
|
+
canary sandbox run-command <instance-id> "curl -s -o /dev/null -w '%{http_code}' http://localhost:80/"
|
|
529
|
+
|
|
530
|
+
# Test with sandbox hostname (for host-restriction issues)
|
|
531
|
+
canary sandbox run-command <instance-id> \\
|
|
532
|
+
"curl -s -H 'Host: <instance-id>.env.trycanary.ai' http://localhost:80/"
|
|
533
|
+
|
|
534
|
+
SANDBOX URL:
|
|
535
|
+
https://<instance-id>.env.trycanary.ai
|
|
536
|
+
|
|
537
|
+
IF SOMETHING FAILS:
|
|
538
|
+
1. Read the logs for the failing service
|
|
539
|
+
2. Diagnose the root cause (missing env var, failed build, wrong port, etc.)
|
|
540
|
+
3. Fix the ARTIFACT \u2014 not just the running container
|
|
541
|
+
4. Re-upload: canary sandbox artifact create <template-id> <type> <file>
|
|
542
|
+
5. Rebuild: canary sandbox build <instance-id>
|
|
543
|
+
6. Verify again
|
|
544
|
+
|
|
545
|
+
WHEN A CLI COMMAND TIMES OUT AT 30s:
|
|
546
|
+
Long sandbox operations (build, edit, artifact create on first push,
|
|
547
|
+
occasionally diagnostics) can exceed the gateway window even though
|
|
548
|
+
the work is still running server-side. The CLI prints:
|
|
549
|
+
Error: Request to https://api.trycanary.ai/... timed out after 30000ms.
|
|
550
|
+
|
|
551
|
+
Don't retry blindly \u2014 a retry can spawn duplicate work (e.g. a second
|
|
552
|
+
authoring instance). Instead, poll for the actual state:
|
|
553
|
+
|
|
554
|
+
canary sandbox template <template-id> # status: provisioning/authoring/verified
|
|
555
|
+
canary sandbox diagnostics <instance-id> # container/app health
|
|
556
|
+
canary sandbox list # running instances
|
|
557
|
+
|
|
558
|
+
Only retry once you've confirmed via polling that the prior call
|
|
559
|
+
truly didn't take effect.
|
|
560
|
+
|
|
561
|
+
WHEN VERIFY HANGS:
|
|
562
|
+
\`canary sandbox verify\` now streams container state and failing-service
|
|
563
|
+
log tails every ~30s while the probe's app is unreachable. If you see:
|
|
564
|
+
|
|
565
|
+
>> nginx-proxy is restart-looping because upstream '<service>' (...) is exited.
|
|
566
|
+
Read that service's logs above, not nginx's.
|
|
567
|
+
|
|
568
|
+
ignore nginx entirely and read the upstream's tail \u2014 nginx is the symptom,
|
|
569
|
+
not the cause. Common underlying causes:
|
|
570
|
+
- the upstream service crashed at startup (bad env var, missing file,
|
|
571
|
+
import error)
|
|
572
|
+
- a file artifact was lost because you re-uploaded over its filename
|
|
573
|
+
(shouldn't happen any more \u2014 the build-time reconcile handles this \u2014
|
|
574
|
+
but watch for it if you're on an old template)
|
|
575
|
+
|
|
576
|
+
On failure, the probe instance is preserved by default so you can
|
|
577
|
+
\`canary sandbox run-command <id>\` into it. Pass
|
|
578
|
+
--auto-terminate-on-failure for CI.
|
|
579
|
+
|
|
580
|
+
COMMON ISSUES:
|
|
581
|
+
|
|
582
|
+
"Invalid environment" / env validation fails
|
|
583
|
+
App has strict env var validation. Read the env schema. Add ALL required vars to compose.
|
|
584
|
+
|
|
585
|
+
Migration fails with "SST links not active" / cloud SDK error
|
|
586
|
+
Migrate script depends on cloud tooling. Use the ORM CLI directly in the startup script
|
|
587
|
+
via sb-compose exec: sb-compose exec -T app-api bash -c 'bun run drizzle-kit migrate'
|
|
588
|
+
DATABASE_URL should be hardcoded in the compose environment section.
|
|
589
|
+
|
|
590
|
+
"Cannot find module" / package not built
|
|
591
|
+
Monorepo package exports from dist/ but hasn't been built.
|
|
592
|
+
Add build step to startup script via sb-compose exec (runs after compose up).
|
|
593
|
+
|
|
594
|
+
"Cannot find package 'X'" from a file-artifact script
|
|
595
|
+
Bun resolves node_modules starting from the script's directory. File
|
|
596
|
+
artifacts live under /workspace/.canary/ which has no node_modules by
|
|
597
|
+
default. Two fixes:
|
|
598
|
+
(a) add a /workspace/.canary/scripts/package.json declaring the
|
|
599
|
+
dep (e.g. "postgres") and run \`bun install\` from the
|
|
600
|
+
setup-script / inside a sb-compose exec.
|
|
601
|
+
(b) don't run the script on the host at all \u2014 invoke it via
|
|
602
|
+
\`sb-compose exec -T <app-service> bun /app/.canary/scripts/foo.ts\`
|
|
603
|
+
so it resolves against the workspace's own node_modules. Replace
|
|
604
|
+
<app-service> with your template's actual service name.
|
|
605
|
+
|
|
606
|
+
"This host is not allowed" (Vite, Next.js)
|
|
607
|
+
Dev server blocks requests from sandbox subdomain.
|
|
608
|
+
For Vite 5.x, upload a wrapper config as a file artifact (into .canary/).
|
|
609
|
+
|
|
610
|
+
IMPORTANT: the wrapper must be self-contained \u2014 DO NOT import 'vite' or
|
|
611
|
+
a sibling config file. Vite evaluates the config via Node CJS bundled
|
|
612
|
+
by esbuild; from /workspace/.canary/ Node can't resolve packages
|
|
613
|
+
through Bun's hoisted .bun/<pkg>@<ver>/... layout, so any 'import ... from
|
|
614
|
+
"vite"' in the wrapper fails with "Cannot find module 'vite'". The
|
|
615
|
+
'defineConfig' helper is just a typed identity, so plain object export
|
|
616
|
+
works fine.
|
|
617
|
+
|
|
618
|
+
Note: the file artifact is at /workspace/.canary/... but the bind mount
|
|
619
|
+
inside containers is /app, so reference it as /app/.canary/... in your
|
|
620
|
+
compose command.
|
|
621
|
+
|
|
622
|
+
# Create the wrapper locally \u2014 no imports
|
|
623
|
+
cat > vite.sandbox.config.ts <<'EOF'
|
|
624
|
+
// Mirror your apps/web/vite.config.ts settings inline here.
|
|
625
|
+
// Reference paths use the in-container path (/app/...), not /workspace.
|
|
626
|
+
export default {
|
|
627
|
+
root: '/app/apps/web',
|
|
628
|
+
server: {
|
|
629
|
+
host: '0.0.0.0',
|
|
630
|
+
port: 5173,
|
|
631
|
+
allowedHosts: true,
|
|
632
|
+
},
|
|
633
|
+
// ...copy 'define', 'optimizeDeps', 'resolve' from your base config
|
|
634
|
+
};
|
|
635
|
+
EOF
|
|
636
|
+
# Upload it into .canary/
|
|
637
|
+
canary sandbox artifact create <template-id> file vite.sandbox.config.ts \\
|
|
638
|
+
--path /workspace/.canary/vite.sandbox.config.ts
|
|
639
|
+
Then in compose:
|
|
640
|
+
command: ["bash","-c","cd /app/apps/web && bunx vite --host 0.0.0.0 --port 5173 --config /app/.canary/vite.sandbox.config.ts"]
|
|
641
|
+
For Vite 6+: --allowedHosts all works as a CLI flag (no wrapper needed).
|
|
642
|
+
|
|
643
|
+
Browser gets CORS / "loopback" error when frontend calls API
|
|
644
|
+
Frontend is trying to reach localhost:3000 instead of going through nginx.
|
|
645
|
+
Use \${SANDBOX_URL}/api as the API URL in compose env vars:
|
|
646
|
+
VITE_API_URL: \${SANDBOX_URL}/api
|
|
647
|
+
|
|
648
|
+
run-command times out
|
|
649
|
+
Long-running operation exceeds the 55s SSM command timeout. Write a script file and run in background:
|
|
650
|
+
canary sandbox run-command <id> "cat > /tmp/run.sh << 'SCRIPT'
|
|
651
|
+
#!/bin/bash
|
|
652
|
+
<commands>
|
|
653
|
+
SCRIPT
|
|
654
|
+
chmod +x /tmp/run.sh && nohup /tmp/run.sh > /tmp/run.log 2>&1 & echo started"
|
|
655
|
+
Then check: canary sandbox run-command <id> "tail -20 /tmp/run.log"
|
|
656
|
+
|
|
657
|
+
"Resolving dependencies" hangs in migration container
|
|
658
|
+
bunx/npx downloads a fresh copy instead of using project deps.
|
|
659
|
+
Use "bun run drizzle-kit migrate" instead of "bunx drizzle-kit migrate".
|
|
660
|
+
|
|
661
|
+
Containers can't resolve each other by name
|
|
662
|
+
Don't use docker run \u2014 use docker compose for everything.
|
|
663
|
+
|
|
664
|
+
RUNNING TESTS:
|
|
665
|
+
Two approaches depending on your needs:
|
|
666
|
+
|
|
667
|
+
Option 1 \u2014 Install runtime on host (no Docker needed):
|
|
668
|
+
Use the setup script to install the runtime on the host VM, then run
|
|
669
|
+
tests directly. Good for unit tests that don't need services like a database.
|
|
670
|
+
|
|
671
|
+
Example setup-script artifact:
|
|
672
|
+
#!/bin/bash
|
|
673
|
+
curl -fsSL https://bun.sh/install | bash
|
|
674
|
+
export PATH="$HOME/.bun/bin:$PATH"
|
|
675
|
+
|
|
676
|
+
Then: canary sandbox run-command <instance-id> "bun test"
|
|
677
|
+
|
|
678
|
+
Option 2 \u2014 Run inside a container (--service flag):
|
|
679
|
+
Route commands into a running Docker container. Good for integration
|
|
680
|
+
tests that need access to the database or other services.
|
|
681
|
+
|
|
682
|
+
canary sandbox run-command <instance-id> --service app-api "bun test"
|
|
683
|
+
canary sandbox run-command <instance-id> --service app-api "bun test:integration"
|
|
684
|
+
|
|
685
|
+
\${SANDBOX_URL} is blank / env vars from config not in containers
|
|
686
|
+
The "build" command writes /workspace/.canary/.env (with SANDBOX_URL and all config values)
|
|
687
|
+
before running docker compose up. If the .env is missing or stale, re-run:
|
|
688
|
+
canary sandbox build <instance-id>
|
|
689
|
+
You can also trigger a .env refresh by setting any config value:
|
|
690
|
+
canary sandbox config set <template-id> <key> <value>
|
|
691
|
+
|
|
692
|
+
.env values look empty when I cat the file
|
|
693
|
+
Values are written single-quoted (KEY='value'). Short values can look like
|
|
694
|
+
KEY='' at a glance even when they have content \u2014 it's just the quotes. Run
|
|
695
|
+
"wc -c" on a line, or "docker compose exec app-api env | grep KEY", to
|
|
696
|
+
confirm the actual value landed.
|
|
697
|
+
`.trim()
|
|
698
|
+
};
|
|
699
|
+
var CHECKLIST = {
|
|
700
|
+
title: "Checklist",
|
|
701
|
+
slug: "checklist",
|
|
702
|
+
content: `
|
|
703
|
+
The checklist tracks your progress toward a working sandbox. View it with:
|
|
704
|
+
canary sandbox checklist <template-id>
|
|
705
|
+
|
|
706
|
+
Each item has a status: pending, completed, skipped, or failed.
|
|
707
|
+
Items marked with * are actionable (their dependencies are met).
|
|
708
|
+
|
|
709
|
+
COMPLETING ITEMS:
|
|
710
|
+
canary sandbox checklist complete <template-id> <item-id> \\
|
|
711
|
+
--status <success|failure> \\
|
|
712
|
+
--summary "What you did"
|
|
713
|
+
|
|
714
|
+
Example:
|
|
715
|
+
canary sandbox checklist complete <template-id> environment-analyzed \\
|
|
716
|
+
--status success \\
|
|
717
|
+
--summary "Bun monorepo: Postgres, Redis. Vite frontend on 5173, API on 3000."
|
|
718
|
+
|
|
719
|
+
SKIPPING ITEMS:
|
|
720
|
+
canary sandbox checklist skip <template-id> <item-id>
|
|
721
|
+
|
|
722
|
+
Use when an item doesn't apply (e.g., no seed data needed, no integration tests).
|
|
723
|
+
|
|
724
|
+
SPECIAL: docker-compose-created accepts --config-schema to define user-configurable env vars:
|
|
725
|
+
canary sandbox checklist complete <template-id> docker-compose-created \\
|
|
726
|
+
--status success \\
|
|
727
|
+
--summary "Compose with postgres, redis, api, web" \\
|
|
728
|
+
--config-schema '[{"key": "ANTHROPIC_API_KEY", "required": true, "sensitive": true, "description": "API key"}]'
|
|
729
|
+
|
|
730
|
+
TYPICAL CHECKLIST ORDER:
|
|
731
|
+
1. Provisioned \u2014 done automatically when you create the template
|
|
732
|
+
2. Code Cloned \u2014 done automatically
|
|
733
|
+
3. Environment Analyzed \u2014 complete after your research phase
|
|
734
|
+
4. Setup Script Created \u2014 complete after uploading setup-script artifact
|
|
735
|
+
5. Dockerfile Created \u2014 complete after uploading dockerfile artifact
|
|
736
|
+
6. Docker Compose Created \u2014 complete after uploading docker-compose artifact (with config-schema)
|
|
737
|
+
7. Unit Tests Runnable \u2014 complete or skip. Install runtime on host via setup script,
|
|
738
|
+
or use --service to run inside a container
|
|
739
|
+
8. Integration Tests Runnable \u2014 complete or skip. Use --service to run inside a
|
|
740
|
+
container with access to database and other services
|
|
741
|
+
9. Seed Script Created \u2014 complete or skip
|
|
742
|
+
10. Server Running \u2014 complete after API responds to health checks
|
|
743
|
+
11. Server Accessible \u2014 complete after nginx routes to the app successfully
|
|
744
|
+
12. Template Verified \u2014 provision a fresh instance and verify it comes up healthy
|
|
745
|
+
|
|
746
|
+
TEMPLATE VERIFICATION (final step):
|
|
747
|
+
Run one command \u2014 it provisions a fresh instance, waits for the app to
|
|
748
|
+
come up, marks template-verified, and terminates the instance:
|
|
749
|
+
canary sandbox verify <template-id>
|
|
750
|
+
|
|
751
|
+
Pass --keep-instance to leave the instance running afterwards (useful if
|
|
752
|
+
you want to poke at it).
|
|
753
|
+
|
|
754
|
+
This catches issues that authoring hides:
|
|
755
|
+
- Config values set manually but not in configDefaults
|
|
756
|
+
- SANDBOX_URL or other platform vars not properly referenced
|
|
757
|
+
- Startup scripts that assume manual setup already happened
|
|
758
|
+
|
|
759
|
+
Template status flow:
|
|
760
|
+
provisioning \u2192 authoring \u2192 verified
|
|
761
|
+
|
|
762
|
+
'verify' is the one transition from authoring to verified, and it is one-way
|
|
763
|
+
\u2014 there is no re-authoring path back to 'authoring' from 'verified'. To make
|
|
764
|
+
an AI-assisted edit to a verified template's artifacts, provision a sandbox
|
|
765
|
+
with the agent attached:
|
|
766
|
+
|
|
767
|
+
canary sandbox provision <template-id> --with-agent
|
|
768
|
+
|
|
769
|
+
then iterate via in-sandbox agent chat or 'canary sandbox artifact create',
|
|
770
|
+
and re-run 'canary sandbox verify' against the same template id when you're
|
|
771
|
+
happy with the changes. To start over from scratch, use 'regenerate' to
|
|
772
|
+
delete existing artifacts, or create a new template.
|
|
773
|
+
`.trim()
|
|
774
|
+
};
|
|
775
|
+
var ACTIONS = {
|
|
776
|
+
title: "Actions",
|
|
777
|
+
slug: "actions",
|
|
778
|
+
content: `
|
|
779
|
+
Actions are template-defined, parameterized operations that run against sandbox
|
|
780
|
+
instances. Unlike run-command (arbitrary shell), actions have typed input/output
|
|
781
|
+
schemas, are reusable across instances, and produce an auditable execution log.
|
|
782
|
+
|
|
783
|
+
USE CASES:
|
|
784
|
+
- Provision a user credential (username, password, role)
|
|
785
|
+
- Seed demo data for a specific scenario
|
|
786
|
+
- Run database migrations
|
|
787
|
+
- Reset application state
|
|
788
|
+
- Toggle feature flags
|
|
789
|
+
|
|
790
|
+
MANAGING ACTIONS:
|
|
791
|
+
|
|
792
|
+
List actions on a template:
|
|
793
|
+
canary sandbox action list <template-id>
|
|
794
|
+
|
|
795
|
+
View an action definition (including script):
|
|
796
|
+
canary sandbox action get <template-id> <slug>
|
|
797
|
+
|
|
798
|
+
Create or update an action:
|
|
799
|
+
canary sandbox action create <template-id> <slug> \\
|
|
800
|
+
--label "Provision Credential" \\
|
|
801
|
+
--description "Create a user account in the sandbox app" \\
|
|
802
|
+
--icon UserPlus \\
|
|
803
|
+
--script ./scripts/provision-user.sh \\
|
|
804
|
+
--input-schema ./schemas/provision-user-input.json \\
|
|
805
|
+
--credential-output-config ./schemas/provision-user-credential.json
|
|
806
|
+
|
|
807
|
+
--credential-output-config is optional. When set, a successful execution
|
|
808
|
+
also creates/updates a credential in the property's vault \u2014 see "Linking
|
|
809
|
+
actions to Canary credentials" below.
|
|
810
|
+
|
|
811
|
+
Delete an action:
|
|
812
|
+
canary sandbox action delete <template-id> <slug>
|
|
813
|
+
|
|
814
|
+
EXECUTING ACTIONS:
|
|
815
|
+
|
|
816
|
+
Execute against a running instance:
|
|
817
|
+
canary sandbox action execute <instance-id> <slug> \\
|
|
818
|
+
--param username=testuser \\
|
|
819
|
+
--param password=s3cret \\
|
|
820
|
+
--param role=admin
|
|
821
|
+
|
|
822
|
+
Or pass all params from a JSON file:
|
|
823
|
+
canary sandbox action execute <instance-id> <slug> \\
|
|
824
|
+
--params-file ./params.json
|
|
825
|
+
|
|
826
|
+
View execution history:
|
|
827
|
+
canary sandbox action executions <instance-id>
|
|
828
|
+
|
|
829
|
+
SLUG NAMING:
|
|
830
|
+
Slugs must be lowercase alphanumeric with hyphens, starting with a letter:
|
|
831
|
+
provision-credential \u2713
|
|
832
|
+
seed-demo-data \u2713
|
|
833
|
+
reset-state \u2713
|
|
834
|
+
123-bad \u2717
|
|
835
|
+
UPPER_CASE \u2717
|
|
836
|
+
|
|
837
|
+
INPUT SCHEMA:
|
|
838
|
+
A JSON Schema file defining the action's parameters. The schema drives
|
|
839
|
+
validation, CLI prompting, and UI form generation.
|
|
840
|
+
|
|
841
|
+
Example (provision-user-input.json):
|
|
842
|
+
{
|
|
843
|
+
"type": "object",
|
|
844
|
+
"properties": {
|
|
845
|
+
"username": {
|
|
846
|
+
"type": "string",
|
|
847
|
+
"description": "Username to create",
|
|
848
|
+
"minLength": 1
|
|
849
|
+
},
|
|
850
|
+
"password": {
|
|
851
|
+
"type": "string",
|
|
852
|
+
"description": "Password for the user",
|
|
853
|
+
"sensitive": true
|
|
854
|
+
},
|
|
855
|
+
"role": {
|
|
856
|
+
"type": "string",
|
|
857
|
+
"description": "Role to assign",
|
|
858
|
+
"enum": ["admin", "user", "viewer"],
|
|
859
|
+
"default": "user"
|
|
860
|
+
}
|
|
861
|
+
},
|
|
862
|
+
"required": ["username", "password"]
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
The "sensitive" extension marks params that should be masked in UI and
|
|
866
|
+
injected securely (via tempfile, not env var) on the instance.
|
|
867
|
+
|
|
868
|
+
SCRIPT CONVENTIONS:
|
|
869
|
+
Action scripts are bash, executed on the instance via SSM. Parameters are
|
|
870
|
+
injected as INPUT_<KEY> environment variables, where <KEY> is the param
|
|
871
|
+
name uppercased with non-alphanumeric chars replaced by '_'. There is NO
|
|
872
|
+
camelCase \u2192 snake_case conversion. Examples:
|
|
873
|
+
orgName \u2192 INPUT_ORGNAME (NOT INPUT_ORG_NAME)
|
|
874
|
+
org_name \u2192 INPUT_ORG_NAME
|
|
875
|
+
api-key \u2192 INPUT_API_KEY
|
|
876
|
+
If you want INPUT_FOO_BAR, name the input schema key 'foo_bar' or 'foo-bar'.
|
|
877
|
+
|
|
878
|
+
The last line of stdout that is valid JSON is parsed as the structured result.
|
|
879
|
+
|
|
880
|
+
EXECUTION ENVIRONMENT \u2014 same architecture as startup scripts:
|
|
881
|
+
Scripts run on the HOST (bare EC2 instance). The host has docker, curl,
|
|
882
|
+
and basic linux tools \u2014 but NO app runtimes (node, bun, python, ruby).
|
|
883
|
+
Those live inside your Docker containers.
|
|
884
|
+
|
|
885
|
+
Use "docker compose exec" to run commands that need the app runtime:
|
|
886
|
+
sb-compose exec -T app-api node -e "..." # Node.js
|
|
887
|
+
sb-compose exec -T app-api bun run seed.ts # Bun
|
|
888
|
+
sb-compose exec -T app-db psql -U app -d db # Database CLI
|
|
889
|
+
|
|
890
|
+
Use bare host commands for docker/system operations:
|
|
891
|
+
sb-compose restart app-api # Restart a service
|
|
892
|
+
curl -s http://localhost:3000/health # Health check
|
|
893
|
+
cat /workspace/.canary/.env # Read config
|
|
894
|
+
|
|
895
|
+
The -T flag is required for docker compose exec in non-interactive (SSM)
|
|
896
|
+
contexts. Without it, exec tries to allocate a TTY and hangs.
|
|
897
|
+
|
|
898
|
+
Working directory is /workspace (the mounted repo root). Keep script logic
|
|
899
|
+
repo-root-relative by starting with "cd /workspace" when needed.
|
|
900
|
+
|
|
901
|
+
AVOID INLINE CODE IN ACTION SCRIPTS:
|
|
902
|
+
Never embed application-language code (JS, Python, etc.) directly in the
|
|
903
|
+
bash script via "bun -e" or "node -e". Shell escaping of quotes, template
|
|
904
|
+
literals, and variable interpolation is fragile and hard to debug.
|
|
905
|
+
|
|
906
|
+
Instead, write complex logic as a separate file artifact and execute it:
|
|
907
|
+
1. Write a script file (e.g. provision-user.ts)
|
|
908
|
+
2. Upload as a file artifact:
|
|
909
|
+
canary sandbox artifact create <template-id> file provision-user.ts \\
|
|
910
|
+
--path /workspace/scripts/provision-user.ts
|
|
911
|
+
3. Reference it from the action script:
|
|
912
|
+
sb-compose exec -T app-api bun /app/scripts/provision-user.ts
|
|
913
|
+
|
|
914
|
+
Parameters are available in the action script as INPUT_<KEY> env vars.
|
|
915
|
+
Pass them to the file artifact via env or args:
|
|
916
|
+
sb-compose exec -T -e EMAIL="$INPUT_EMAIL" app-api bun /app/scripts/provision-user.ts
|
|
917
|
+
|
|
918
|
+
Example \u2014 provision a user (two-file pattern):
|
|
919
|
+
|
|
920
|
+
Place the .ts file under the workspace whose node_modules has the deps
|
|
921
|
+
(e.g. apps/api when only apps/api depends on \`postgres\`). Bun resolves
|
|
922
|
+
modules from the script's directory walking up \u2014 if the dep isn't hoisted
|
|
923
|
+
to the repo-root node_modules, a script under /workspace/scripts/ will
|
|
924
|
+
fail with "Cannot find package 'postgres'". Use \`-w\` to set cwd inside
|
|
925
|
+
the right workspace.
|
|
926
|
+
|
|
927
|
+
Action script (provision-user.sh):
|
|
928
|
+
#!/bin/bash
|
|
929
|
+
set -euo pipefail
|
|
930
|
+
cd /workspace
|
|
931
|
+
sb-compose exec -T \\
|
|
932
|
+
-w /app/apps/api \\
|
|
933
|
+
-e EMAIL="$INPUT_EMAIL" \\
|
|
934
|
+
-e PASSWORD="$INPUT_PASSWORD" \\
|
|
935
|
+
-e NAME="$INPUT_NAME" \\
|
|
936
|
+
app-api bun /app/apps/api/scripts/provision-user.ts
|
|
937
|
+
|
|
938
|
+
File artifact (uploaded to /workspace/apps/api/scripts/provision-user.ts):
|
|
939
|
+
import postgres from "postgres";
|
|
940
|
+
const sql = postgres(process.env.DATABASE_URL!);
|
|
941
|
+
const hash = await Bun.password.hash(process.env.PASSWORD!, { algorithm: "argon2id" });
|
|
942
|
+
const email = process.env.EMAIL!.toLowerCase();
|
|
943
|
+
const [org] = await sql\`INSERT INTO organizations (name) VALUES (\${email + "'s Org"}) RETURNING id\`;
|
|
944
|
+
const [user] = await sql\`
|
|
945
|
+
INSERT INTO users (auth_provider, provider_id, primary_email, password_hash, email_verified_at)
|
|
946
|
+
VALUES ('email', \${email}, \${email}, \${hash}, now()) RETURNING id\`;
|
|
947
|
+
await sql\`INSERT INTO org_memberships (user_id, org_id, role) VALUES (\${user.id}, \${org.id}, 'admin')\`;
|
|
948
|
+
const loginUrl = \`\${process.env.SANDBOX_PUBLIC_URL ?? ''}/login\`;
|
|
949
|
+
// Echo password back so credentialOutputConfig (below) can resolve $output.password.
|
|
950
|
+
// Output keys referenced by credentialOutputConfig.fieldMapping are auto-redacted
|
|
951
|
+
// from stored stdout/logs by the platform \u2014 safe to include.
|
|
952
|
+
console.log(JSON.stringify({ ok: true, email, password: process.env.PASSWORD, userId: user.id, orgId: org.id, loginUrl }));
|
|
953
|
+
await sql.end();
|
|
954
|
+
|
|
955
|
+
LINKING ACTIONS TO CANARY CREDENTIALS:
|
|
956
|
+
|
|
957
|
+
When you want a successful action to also produce a credential in the
|
|
958
|
+
property's vault (so it can drive browser logins, seed workflows, etc.),
|
|
959
|
+
attach a credentialOutputConfig. The platform resolves \`$output.<key>\`
|
|
960
|
+
tokens against the action's last-line JSON stdout, then upserts a
|
|
961
|
+
credential scoped to (sandboxActionId, environmentId) \u2014 re-running the
|
|
962
|
+
action replaces the credential rather than duplicating it.
|
|
963
|
+
|
|
964
|
+
Provide it with --credential-output-config on action create/update:
|
|
965
|
+
|
|
966
|
+
canary sandbox action create <template-id> provision-credential \\
|
|
967
|
+
--label "Provision Credential" \\
|
|
968
|
+
--script ./scripts/provision-user.sh \\
|
|
969
|
+
--input-schema ./schemas/provision-user-input.json \\
|
|
970
|
+
--credential-output-config ./schemas/provision-user-credential.json
|
|
971
|
+
|
|
972
|
+
Example provision-user-credential.json (matches the script above):
|
|
973
|
+
{
|
|
974
|
+
"fieldMapping": {
|
|
975
|
+
"username": "$output.email",
|
|
976
|
+
"password": "$output.password"
|
|
977
|
+
},
|
|
978
|
+
"loginUrl": "$output.loginUrl",
|
|
979
|
+
"loginInstructions": "Sign in via the email/password form."
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
Field-mapping values are either literal strings or \`$output.<key>\` tokens
|
|
983
|
+
resolved from the action's parsed JSON output. Output keys referenced by
|
|
984
|
+
fieldMapping/loginUrl are automatically masked in stored stdout and result.
|
|
985
|
+
|
|
986
|
+
After execution, \`canary sandbox action execute\` prints the linked
|
|
987
|
+
credentialId, and \`canary credential list --property <name>\` shows the
|
|
988
|
+
vault entry. \`canary credential test <credId>\` runs the browser login
|
|
989
|
+
against the sandbox URL.
|
|
990
|
+
|
|
991
|
+
Example \u2014 simple SQL-only action (inline is fine for short commands):
|
|
992
|
+
#!/bin/bash
|
|
993
|
+
set -euo pipefail
|
|
994
|
+
cd /workspace
|
|
995
|
+
sb-compose exec -T app-db psql -U app -d appdb -t --csv -c \\
|
|
996
|
+
"INSERT INTO users (username, password_hash, role)
|
|
997
|
+
VALUES ('$INPUT_USERNAME', crypt('$INPUT_PASSWORD', gen_salt('bf')), '$INPUT_ROLE')
|
|
998
|
+
RETURNING id" | while read -r user_id; do
|
|
999
|
+
echo "{\\"userId\\":\\"$user_id\\",\\"username\\":\\"$INPUT_USERNAME\\",\\"loginUrl\\":\\"https://$SANDBOX_PUBLIC_URL/login\\"}"
|
|
1000
|
+
done
|
|
1001
|
+
|
|
1002
|
+
Example \u2014 seed data via app CLI:
|
|
1003
|
+
#!/bin/bash
|
|
1004
|
+
set -euo pipefail
|
|
1005
|
+
cd /workspace
|
|
1006
|
+
sb-compose exec -T app-api bun run scripts/seed.ts \\
|
|
1007
|
+
--scenario "$INPUT_SCENARIO" \\
|
|
1008
|
+
--count "$INPUT_COUNT"
|
|
1009
|
+
|
|
1010
|
+
Example \u2014 run migrations (no params needed):
|
|
1011
|
+
#!/bin/bash
|
|
1012
|
+
set -euo pipefail
|
|
1013
|
+
cd /workspace
|
|
1014
|
+
sb-compose exec -T app-api bash -c \\
|
|
1015
|
+
'cd /app/packages/db && bun run drizzle-kit migrate'
|
|
1016
|
+
echo '{"status":"completed"}'
|
|
1017
|
+
|
|
1018
|
+
Available context variables:
|
|
1019
|
+
SANDBOX_PUBLIC_URL \u2014 the instance's public URL
|
|
1020
|
+
SANDBOX_INSTANCE_ID \u2014 the instance UUID
|
|
1021
|
+
|
|
1022
|
+
AGENT AUTHORING:
|
|
1023
|
+
During template creation, the sandbox agent can publish actions alongside
|
|
1024
|
+
artifacts using the publish_action tool. The agent analyzes the codebase
|
|
1025
|
+
and generates action definitions for common operations (user creation,
|
|
1026
|
+
data seeding, migrations, etc.). These are stored on the template and
|
|
1027
|
+
available on all instances provisioned from it.
|
|
1028
|
+
|
|
1029
|
+
publish_action accepts an optional credentialOutputConfig \u2014 for any action
|
|
1030
|
+
that provisions login material (e.g. a user with email/password), the agent
|
|
1031
|
+
should set this so the resulting credential lands in the property vault and
|
|
1032
|
+
can be exercised by browser automation.
|
|
1033
|
+
`.trim()
|
|
1034
|
+
};
|
|
1035
|
+
var SANDBOX_GUIDE_SECTIONS = [
|
|
1036
|
+
GOAL,
|
|
1037
|
+
RESEARCH,
|
|
1038
|
+
ARTIFACTS,
|
|
1039
|
+
ACTIONS,
|
|
1040
|
+
VERIFICATION,
|
|
1041
|
+
CHECKLIST
|
|
1042
|
+
];
|
|
1043
|
+
|
|
1044
|
+
export {
|
|
1045
|
+
printGuide,
|
|
1046
|
+
SANDBOX_GUIDE_SECTIONS
|
|
1047
|
+
};
|
|
1048
|
+
//# sourceMappingURL=chunk-4NRDONSC.js.map
|