@vendoai/vendo 0.4.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.
Files changed (212) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +28 -0
  3. package/bin/vendo.mjs +4 -0
  4. package/dist/ai-sdk.d.ts +35 -0
  5. package/dist/ai-sdk.js +62 -0
  6. package/dist/auth-presets/auth-js.d.ts +14 -0
  7. package/dist/auth-presets/auth-js.js +91 -0
  8. package/dist/auth-presets/auth0.d.ts +23 -0
  9. package/dist/auth-presets/auth0.js +98 -0
  10. package/dist/auth-presets/clerk.d.ts +23 -0
  11. package/dist/auth-presets/clerk.js +65 -0
  12. package/dist/auth-presets/conformance.d.ts +42 -0
  13. package/dist/auth-presets/conformance.js +139 -0
  14. package/dist/auth-presets/identity.d.ts +56 -0
  15. package/dist/auth-presets/identity.js +152 -0
  16. package/dist/auth-presets/index.d.ts +14 -0
  17. package/dist/auth-presets/index.js +12 -0
  18. package/dist/auth-presets/jwt.d.ts +16 -0
  19. package/dist/auth-presets/jwt.js +54 -0
  20. package/dist/auth-presets/shared.d.ts +35 -0
  21. package/dist/auth-presets/shared.js +1 -0
  22. package/dist/auth-presets/supabase.d.ts +42 -0
  23. package/dist/auth-presets/supabase.js +229 -0
  24. package/dist/byo-approvals.d.ts +37 -0
  25. package/dist/byo-approvals.js +189 -0
  26. package/dist/capability-misses.d.ts +38 -0
  27. package/dist/capability-misses.js +172 -0
  28. package/dist/catalog.d.ts +19 -0
  29. package/dist/catalog.js +152 -0
  30. package/dist/cli/cloud/args.d.ts +3 -0
  31. package/dist/cli/cloud/args.js +32 -0
  32. package/dist/cli/cloud/auth.d.ts +17 -0
  33. package/dist/cli/cloud/auth.js +123 -0
  34. package/dist/cli/cloud/client.d.ts +28 -0
  35. package/dist/cli/cloud/client.js +133 -0
  36. package/dist/cli/cloud/command.d.ts +22 -0
  37. package/dist/cli/cloud/command.js +73 -0
  38. package/dist/cli/cloud/deploy.d.ts +26 -0
  39. package/dist/cli/cloud/deploy.js +187 -0
  40. package/dist/cli/cloud/device-login.d.ts +32 -0
  41. package/dist/cli/cloud/device-login.js +259 -0
  42. package/dist/cli/cloud/index.d.ts +5 -0
  43. package/dist/cli/cloud/index.js +59 -0
  44. package/dist/cli/cloud/keys.d.ts +2 -0
  45. package/dist/cli/cloud/keys.js +31 -0
  46. package/dist/cli/cloud/members.d.ts +3 -0
  47. package/dist/cli/cloud/members.js +25 -0
  48. package/dist/cli/cloud/output.d.ts +4 -0
  49. package/dist/cli/cloud/output.js +12 -0
  50. package/dist/cli/cloud/pending-claim.d.ts +29 -0
  51. package/dist/cli/cloud/pending-claim.js +38 -0
  52. package/dist/cli/cloud/read.d.ts +3 -0
  53. package/dist/cli/cloud/read.js +12 -0
  54. package/dist/cli/cloud/services.d.ts +2 -0
  55. package/dist/cli/cloud/services.js +9 -0
  56. package/dist/cli/cloud/session.d.ts +13 -0
  57. package/dist/cli/cloud/session.js +41 -0
  58. package/dist/cli/cloud-init.d.ts +60 -0
  59. package/dist/cli/cloud-init.js +149 -0
  60. package/dist/cli/dep-versions.d.ts +26 -0
  61. package/dist/cli/dep-versions.js +77 -0
  62. package/dist/cli/doctor-codes.d.ts +58 -0
  63. package/dist/cli/doctor-codes.js +60 -0
  64. package/dist/cli/doctor-live.d.ts +67 -0
  65. package/dist/cli/doctor-live.js +169 -0
  66. package/dist/cli/doctor.d.ts +52 -0
  67. package/dist/cli/doctor.js +467 -0
  68. package/dist/cli/eject.d.ts +29 -0
  69. package/dist/cli/eject.js +141 -0
  70. package/dist/cli/extract/apply.d.ts +26 -0
  71. package/dist/cli/extract/apply.js +123 -0
  72. package/dist/cli/extract/claude-cli-harness.d.ts +18 -0
  73. package/dist/cli/extract/claude-cli-harness.js +132 -0
  74. package/dist/cli/extract/claude-harness.d.ts +14 -0
  75. package/dist/cli/extract/claude-harness.js +127 -0
  76. package/dist/cli/extract/codex-cli-harness.d.ts +31 -0
  77. package/dist/cli/extract/codex-cli-harness.js +154 -0
  78. package/dist/cli/extract/delegate.d.ts +21 -0
  79. package/dist/cli/extract/delegate.js +77 -0
  80. package/dist/cli/extract/extraction.d.ts +65 -0
  81. package/dist/cli/extract/extraction.js +300 -0
  82. package/dist/cli/extract/gateway-fuel.d.ts +69 -0
  83. package/dist/cli/extract/gateway-fuel.js +69 -0
  84. package/dist/cli/extract/harness.d.ts +114 -0
  85. package/dist/cli/extract/harness.js +84 -0
  86. package/dist/cli/extract/index.d.ts +9 -0
  87. package/dist/cli/extract/index.js +9 -0
  88. package/dist/cli/extract/npx-engine-harness.d.ts +73 -0
  89. package/dist/cli/extract/npx-engine-harness.js +225 -0
  90. package/dist/cli/extract/stages.d.ts +149 -0
  91. package/dist/cli/extract/stages.js +398 -0
  92. package/dist/cli/framework.d.ts +8 -0
  93. package/dist/cli/framework.js +36 -0
  94. package/dist/cli/init-auth.d.ts +67 -0
  95. package/dist/cli/init-auth.js +142 -0
  96. package/dist/cli/init-scaffolds.d.ts +28 -0
  97. package/dist/cli/init-scaffolds.js +235 -0
  98. package/dist/cli/init.d.ts +115 -0
  99. package/dist/cli/init.js +1008 -0
  100. package/dist/cli/mcp/index.d.ts +7 -0
  101. package/dist/cli/mcp/index.js +59 -0
  102. package/dist/cli/mcp/registry.d.ts +9 -0
  103. package/dist/cli/mcp/registry.js +117 -0
  104. package/dist/cli/mcp/server-json.d.ts +12 -0
  105. package/dist/cli/mcp/server-json.js +65 -0
  106. package/dist/cli/mcp/server.schema.json +574 -0
  107. package/dist/cli/mcp/verify-domain.d.ts +11 -0
  108. package/dist/cli/mcp/verify-domain.js +48 -0
  109. package/dist/cli/playground/app/embed-entry.d.ts +20 -0
  110. package/dist/cli/playground/app/embed-entry.js +46 -0
  111. package/dist/cli/playground/app/fake-client.d.ts +3 -0
  112. package/dist/cli/playground/app/fake-client.js +215 -0
  113. package/dist/cli/playground/app/fixtures.d.ts +47 -0
  114. package/dist/cli/playground/app/fixtures.js +472 -0
  115. package/dist/cli/playground/app/main.d.ts +1 -0
  116. package/dist/cli/playground/app/main.js +108 -0
  117. package/dist/cli/playground/app/scenario-mount.d.ts +13 -0
  118. package/dist/cli/playground/app/scenario-mount.js +48 -0
  119. package/dist/cli/playground/app/scenarios.d.ts +25 -0
  120. package/dist/cli/playground/app/scenarios.js +152 -0
  121. package/dist/cli/playground/app/theme-editor.d.ts +13 -0
  122. package/dist/cli/playground/app/theme-editor.js +149 -0
  123. package/dist/cli/playground/app/theme-state.d.ts +29 -0
  124. package/dist/cli/playground/app/theme-state.js +151 -0
  125. package/dist/cli/playground/bundle.gen.d.ts +2 -0
  126. package/dist/cli/playground/bundle.gen.js +2 -0
  127. package/dist/cli/playground/embed-bundle.gen.d.ts +2 -0
  128. package/dist/cli/playground/embed-bundle.gen.js +2 -0
  129. package/dist/cli/playground.d.ts +33 -0
  130. package/dist/cli/playground.js +120 -0
  131. package/dist/cli/pretty.d.ts +48 -0
  132. package/dist/cli/pretty.js +330 -0
  133. package/dist/cli/refine.d.ts +43 -0
  134. package/dist/cli/refine.js +181 -0
  135. package/dist/cli/semantics.d.ts +31 -0
  136. package/dist/cli/semantics.js +122 -0
  137. package/dist/cli/shared.d.ts +68 -0
  138. package/dist/cli/shared.js +184 -0
  139. package/dist/cli/sync.d.ts +36 -0
  140. package/dist/cli/sync.js +202 -0
  141. package/dist/cli/theme/color.d.ts +15 -0
  142. package/dist/cli/theme/color.js +188 -0
  143. package/dist/cli/theme/css-vars.d.ts +13 -0
  144. package/dist/cli/theme/css-vars.js +40 -0
  145. package/dist/cli/theme/entry-candidates.d.ts +5 -0
  146. package/dist/cli/theme/entry-candidates.js +22 -0
  147. package/dist/cli/theme/extract-theme.d.ts +184 -0
  148. package/dist/cli/theme/extract-theme.js +358 -0
  149. package/dist/cli/theme/walk.d.ts +2 -0
  150. package/dist/cli/theme/walk.js +24 -0
  151. package/dist/cli.d.ts +2 -0
  152. package/dist/cli.js +333 -0
  153. package/dist/cloud-apps.d.ts +17 -0
  154. package/dist/cloud-apps.js +46 -0
  155. package/dist/cloud-console.d.ts +32 -0
  156. package/dist/cloud-console.js +72 -0
  157. package/dist/cloud-tools.d.ts +23 -0
  158. package/dist/cloud-tools.js +196 -0
  159. package/dist/connections.d.ts +71 -0
  160. package/dist/connections.js +192 -0
  161. package/dist/deployment-identity.d.ts +12 -0
  162. package/dist/deployment-identity.js +69 -0
  163. package/dist/dev-creds/model.d.ts +65 -0
  164. package/dist/dev-creds/model.js +159 -0
  165. package/dist/dev-creds/resolve.d.ts +36 -0
  166. package/dist/dev-creds/resolve.js +57 -0
  167. package/dist/hosted-store.d.ts +52 -0
  168. package/dist/hosted-store.js +322 -0
  169. package/dist/index.d.ts +13 -0
  170. package/dist/index.js +1 -0
  171. package/dist/mastra.d.ts +46 -0
  172. package/dist/mastra.js +88 -0
  173. package/dist/react.d.ts +10 -0
  174. package/dist/react.js +32 -0
  175. package/dist/refine.d.ts +244 -0
  176. package/dist/refine.js +599 -0
  177. package/dist/remixable.d.ts +18 -0
  178. package/dist/remixable.js +42 -0
  179. package/dist/runtime-capture.d.ts +18 -0
  180. package/dist/runtime-capture.js +111 -0
  181. package/dist/sandbox-wire.d.ts +101 -0
  182. package/dist/sandbox-wire.js +101 -0
  183. package/dist/sandbox.d.ts +38 -0
  184. package/dist/sandbox.js +389 -0
  185. package/dist/server.d.ts +216 -0
  186. package/dist/server.js +1320 -0
  187. package/dist/sync-impact.d.ts +14 -0
  188. package/dist/sync-impact.js +75 -0
  189. package/dist/turn-liveness.d.ts +33 -0
  190. package/dist/turn-liveness.js +105 -0
  191. package/dist/wire/approvals.d.ts +10 -0
  192. package/dist/wire/approvals.js +59 -0
  193. package/dist/wire/apps.d.ts +5 -0
  194. package/dist/wire/apps.js +155 -0
  195. package/dist/wire/automations.d.ts +5 -0
  196. package/dist/wire/automations.js +57 -0
  197. package/dist/wire/box.d.ts +3 -0
  198. package/dist/wire/box.js +268 -0
  199. package/dist/wire/connections.d.ts +5 -0
  200. package/dist/wire/connections.js +47 -0
  201. package/dist/wire/context.d.ts +22 -0
  202. package/dist/wire/context.js +190 -0
  203. package/dist/wire/doctor.d.ts +19 -0
  204. package/dist/wire/doctor.js +116 -0
  205. package/dist/wire/misc.d.ts +18 -0
  206. package/dist/wire/misc.js +227 -0
  207. package/dist/wire/shared.d.ts +158 -0
  208. package/dist/wire/shared.js +134 -0
  209. package/dist/wire/threads.d.ts +3 -0
  210. package/dist/wire/threads.js +68 -0
  211. package/package.json +151 -0
  212. package/skills/vendo-setup/SKILL.md +128 -0
@@ -0,0 +1,111 @@
1
+ import { capturedPinBaselineSchema } from "@vendoai/actions";
2
+ import { sha256Hex, VendoError } from "@vendoai/core";
3
+ function nodeModules() {
4
+ const proc = globalThis.process;
5
+ if (!proc?.cwd || !proc.getBuiltinModule)
6
+ return null;
7
+ const fs = proc.getBuiltinModule("node:fs");
8
+ const path = proc.getBuiltinModule("node:path");
9
+ const url = proc.getBuiltinModule("node:url");
10
+ return fs && path && url ? { fs, path, url, cwd: proc.cwd } : null;
11
+ }
12
+ function isInside(path, root, candidate) {
13
+ const relative = path.relative(path.resolve(root), path.resolve(candidate));
14
+ return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
15
+ }
16
+ function sourceCandidates(path, url, root, source) {
17
+ if (source.startsWith("file:")) {
18
+ try {
19
+ return [url.fileURLToPath(source)];
20
+ }
21
+ catch {
22
+ return [];
23
+ }
24
+ }
25
+ let clean = source.split(/[?#]/, 1)[0] ?? source;
26
+ // A dev browser's import.meta.url is an http(s) module URL (Vite serves
27
+ // /src/... under the dev origin). Only its pathname matters: the realpath
28
+ // root-confinement below decides what is actually readable.
29
+ if (/^https?:\/\//iu.test(clean)) {
30
+ try {
31
+ clean = decodeURIComponent(new URL(clean).pathname);
32
+ }
33
+ catch {
34
+ return [];
35
+ }
36
+ }
37
+ if (clean.startsWith("/@fs/"))
38
+ return [path.resolve(clean.slice(4))];
39
+ if (path.isAbsolute(clean)) {
40
+ const candidates = [path.resolve(clean)];
41
+ // Vite-style project-root ids use `/src/...`, not an absolute filesystem path.
42
+ if (clean.startsWith("/src/"))
43
+ candidates.push(path.resolve(root, clean.slice(1)));
44
+ return candidates;
45
+ }
46
+ return [path.resolve(root, clean)];
47
+ }
48
+ async function existingBaseline(fs, file) {
49
+ try {
50
+ return capturedPinBaselineSchema.parse(JSON.parse(await fs.promises.readFile(file, "utf8")));
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ }
56
+ /** Node-only capture primitive. Returning null keeps edge/Worker bundles file-system free. */
57
+ export function createRuntimeCapture(config) {
58
+ const modules = nodeModules();
59
+ if (!modules)
60
+ return null;
61
+ const { fs, path, url } = modules;
62
+ const root = path.resolve(config.root ?? modules.cwd());
63
+ const out = path.resolve(config.out ?? path.join(root, ".vendo"));
64
+ let realRootPromise;
65
+ return {
66
+ async capture(registration) {
67
+ const remixableDir = path.resolve(out, "remixable");
68
+ const baselineFile = path.resolve(remixableDir, `${registration.slot}.json`);
69
+ if (!isInside(path, remixableDir, baselineFile)) {
70
+ throw new VendoError("validation", "remixable slot is not a safe baseline filename");
71
+ }
72
+ realRootPromise ??= fs.promises.realpath(root);
73
+ const realRoot = await realRootPromise;
74
+ let realSource;
75
+ for (const candidate of sourceCandidates(path, url, root, registration.source)) {
76
+ try {
77
+ const resolved = await fs.promises.realpath(candidate);
78
+ if (isInside(path, realRoot, resolved)) {
79
+ realSource = resolved;
80
+ break;
81
+ }
82
+ }
83
+ catch {
84
+ // Try the next bundler-id interpretation.
85
+ }
86
+ }
87
+ if (realSource === undefined) {
88
+ throw new VendoError("validation", "runtime capture source must resolve inside the host root");
89
+ }
90
+ const source = await fs.promises.readFile(realSource, "utf8");
91
+ const hash = `sha256:${sha256Hex(source)}`;
92
+ const existing = await existingBaseline(fs, baselineFile);
93
+ if (existing?.hash === hash)
94
+ return { slot: registration.slot, hash, status: "unchanged" };
95
+ // Static extraction is primary. A valid baseline may have been refreshed by
96
+ // sync after this browser bundle loaded, so runtime capture only fills gaps.
97
+ if (existing !== null)
98
+ return { slot: registration.slot, hash: existing.hash, status: "preserved" };
99
+ const baseline = {
100
+ slot: registration.slot,
101
+ source,
102
+ hash,
103
+ exportable: registration.exportable,
104
+ capturedAt: new Date().toISOString(),
105
+ };
106
+ await fs.promises.mkdir(remixableDir, { recursive: true });
107
+ await fs.promises.writeFile(baselineFile, `${JSON.stringify(baseline, null, 2)}\n`, "utf8");
108
+ return { slot: registration.slot, hash, status: "captured" };
109
+ },
110
+ };
111
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * WIRE-CONTRACT — the Vendo Cloud hosted-sandbox HTTP surface.
3
+ *
4
+ * execution-v2 Wave 5: every Cloud-side wire fact the adapter and its mock
5
+ * rely on lives HERE, in one module, so a Cloud-side change lands in one
6
+ * place. Authoritative per the Cloud session's 2026-07-19 ship note (the
7
+ * ARTIFACT model — persistent snapshot artifacts, verified live in prod
8
+ * with a full snapshot → destroy → resume → fork cycle) plus this lane's
9
+ * own live probes and conformance runs against console.vendo.run.
10
+ *
11
+ * The surface (under `{base}/api/v1/sandboxes`, key-authed with a
12
+ * `Bearer VENDO_API_KEY` header; errors are `{error:{code,message}}`
13
+ * envelopes; 402 = metered quota, 401 = bad key):
14
+ *
15
+ * - create `POST /` body `{env, files?, egress?: string[]}` →
16
+ * 201 `{id, url}`. No template field — the pooled base image is Cloud's
17
+ * own; the adapter drops `spec.template` and documents it. `egress`
18
+ * absent = unrestricted, `[]` = deny-all — mirroring the
19
+ * SandboxAdapter.create seam contract verbatim; deny-by-default is
20
+ * enforced ABOVE the seam (Wave 2 Lane E always passes the grant-derived
21
+ * list). The filter is HTTP(S)-only: raw TCP is severed even to
22
+ * allowlisted hosts (raw Postgres never works; the HTTPS store API does).
23
+ * - snapshot `POST /{id}/snapshot` → 200 `{ref}` with ref
24
+ * `vendo:snap_<40hex>` — a persistent ARTIFACT of the machine's state.
25
+ * The source machine keeps running (and billing); artifacts survive the
26
+ * machine's destruction. (Cloud-side caveat, no wire impact: artifact
27
+ * storage meters 0 GB for now.)
28
+ * - resume `POST /resume` body `{ref, egress?: string[]}` → 200
29
+ * `{id, url}` booting a NEW machine from the artifact (a live source
30
+ * makes it a fork; a destroyed one a wake). BREAKING vs create-config
31
+ * intuition: the new machine does NOT inherit network config — `egress`
32
+ * absent = unrestricted (Free orgs coerce deny-all) — so the adapter
33
+ * sends the applicable allowlist EXPLICITLY on every resume: the
34
+ * ref-recorded one for a bare resume, the SandboxResumePolicy one when
35
+ * the caller re-polices a wake (Lane E replace semantics, native).
36
+ * Resume of a GC'd or pre-artifact ref answers 404.
37
+ * - destroy `DELETE /{id}` → 200 `{ok:true}`; MACHINE ids only (a
38
+ * URL-encoded ref answers 404); repeat-delete = 200. Snapshot artifacts
39
+ * SURVIVE the machine — sleep is snapshot-then-destroy, wake is resume.
40
+ * - snapshot GC `DELETE /snapshots/{url-encoded ref}` → 200 `{ok:true}`,
41
+ * 404 = already gone (treat as the seam's idempotent no-op); reclaims
42
+ * the artifact and its storage row.
43
+ * - request `POST /{id}/request` body `{method, path, port?, headers?,
44
+ * body_b64}` → `{status, headers, body_b64}`, relayed into the box.
45
+ * `port` absent targets the canonical box port {@link CLOUD_BOX_PORT}
46
+ * (NOT the box's $PORT — the one provider divergence the conformance
47
+ * multiPort flag covers); any explicit 1-65535 routes (probed — an
48
+ * out-of-range port answers a clean 400 validation error), so the
49
+ * in-box agent control port (8811) works.
50
+ - exec/files also exist server-side (`POST /{id}/exec`,
51
+ * `GET|PUT /{id}/files?path=`, `GET /{id}/files/list?dir=`) — adapter-
52
+ * private, used for live-lane bootstrap and diagnostics only.
53
+ * - ingress the create/resume handle `url`
54
+ * (`https://<id-suffix>-m.vendo.run`, single-label scheme SHIPPED by
55
+ * vendo-web #85 on 2026-07-20; the -m is a SUFFIX, not a prefix —
56
+ * Cloudflare worker routes only allow leading wildcards, so the route is
57
+ * `*-m.vendo.run/*`) is the canonical-port public surface;
58
+ * `machine.url(port)` inserts other ports before the suffix
59
+ * (`https://<id-suffix>-<port>-m.vendo.run`), matching the machine-proxy
60
+ * parse `^([a-z0-9]{24})(?:-(port))?-m\.vendo\.run$`. Single-label hosts
61
+ * ride the existing `*.vendo.run` Universal SSL cert — no advanced
62
+ * certificate needed (live-verified 2026-07-20: TLS fails on the legacy
63
+ * dot shape `test123.m.vendo.run`, succeeds single-label; unknown ids
64
+ * answer a JSON 404). The console mints `url`, so the hostname shape is
65
+ * console-side; this adapter echoes whatever handle it is given.
66
+ *
67
+ * Adapter mapping:
68
+ * - `machine.snapshot()` = the artifact mint, wrapped in the adapter's
69
+ * composite ref {@link CLOUD_SNAPSHOT_REF_PREFIX} + base64url(JSON
70
+ * {machineId, ref, allowedDomains?}): the machine id lets destroy-by-ref
71
+ * reap a still-running source, and the allowlist is what a bare resume
72
+ * re-applies (the wire inherits nothing).
73
+ * - `machine.stop()` = destroy: Cloud has no pause — with artifacts
74
+ * surviving the machine, snapshot-then-destroy IS the sleep semantics
75
+ * (exactly the machine-lifecycle flow), and previously minted refs stay
76
+ * valid through it (the seam law).
77
+ * - `adapter.destroy(ref)` = best-effort reap of the recorded source
78
+ * machine, then artifact GC.
79
+ *
80
+ * Lifecycle interaction: Vendo auto-sleeps an idle machine after 5 minutes
81
+ * (snapshot → destroy); Cloud independently sweeps at 10 minutes idle and
82
+ * 24 hours max age. Our sleep normally wins; when the Cloud sweep gets
83
+ * there first the next wake resumes from the last stored ref, losing at
84
+ * most scratch state since it — acceptable, because the data rule keeps
85
+ * anything durable in the Vendo store, never on the VM disk.
86
+ */
87
+ /** The console mounts the managed-sandbox surface here
88
+ * (apps/console/app/api/v1/sandboxes/*). */
89
+ export declare const CLOUD_SANDBOX_PATH = "/api/v1/sandboxes";
90
+ /** Snapshot-artifact GC route (under {@link CLOUD_SANDBOX_PATH}). */
91
+ export declare const CLOUD_SNAPSHOTS_SUBPATH = "/snapshots";
92
+ /** Adapter-minted snapshot refs: this prefix + base64url(JSON state).
93
+ * These are what the seam (and app documents) carry. */
94
+ export declare const CLOUD_SNAPSHOT_REF_PREFIX = "vendo:v2:";
95
+ /** Console-minted artifact refs (`vendo:snap_<40hex>`) — carried INSIDE the
96
+ * adapter's composite ref, never handed to the seam bare. */
97
+ export declare const CONSOLE_SNAPSHOT_REF_PREFIX = "vendo:";
98
+ /** The canonical box port the relay targets when no port rides the wire
99
+ * (and the one the public ingress `https://<id-suffix>-m.vendo.run`
100
+ * serves). */
101
+ export declare const CLOUD_BOX_PORT = 8080;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * WIRE-CONTRACT — the Vendo Cloud hosted-sandbox HTTP surface.
3
+ *
4
+ * execution-v2 Wave 5: every Cloud-side wire fact the adapter and its mock
5
+ * rely on lives HERE, in one module, so a Cloud-side change lands in one
6
+ * place. Authoritative per the Cloud session's 2026-07-19 ship note (the
7
+ * ARTIFACT model — persistent snapshot artifacts, verified live in prod
8
+ * with a full snapshot → destroy → resume → fork cycle) plus this lane's
9
+ * own live probes and conformance runs against console.vendo.run.
10
+ *
11
+ * The surface (under `{base}/api/v1/sandboxes`, key-authed with a
12
+ * `Bearer VENDO_API_KEY` header; errors are `{error:{code,message}}`
13
+ * envelopes; 402 = metered quota, 401 = bad key):
14
+ *
15
+ * - create `POST /` body `{env, files?, egress?: string[]}` →
16
+ * 201 `{id, url}`. No template field — the pooled base image is Cloud's
17
+ * own; the adapter drops `spec.template` and documents it. `egress`
18
+ * absent = unrestricted, `[]` = deny-all — mirroring the
19
+ * SandboxAdapter.create seam contract verbatim; deny-by-default is
20
+ * enforced ABOVE the seam (Wave 2 Lane E always passes the grant-derived
21
+ * list). The filter is HTTP(S)-only: raw TCP is severed even to
22
+ * allowlisted hosts (raw Postgres never works; the HTTPS store API does).
23
+ * - snapshot `POST /{id}/snapshot` → 200 `{ref}` with ref
24
+ * `vendo:snap_<40hex>` — a persistent ARTIFACT of the machine's state.
25
+ * The source machine keeps running (and billing); artifacts survive the
26
+ * machine's destruction. (Cloud-side caveat, no wire impact: artifact
27
+ * storage meters 0 GB for now.)
28
+ * - resume `POST /resume` body `{ref, egress?: string[]}` → 200
29
+ * `{id, url}` booting a NEW machine from the artifact (a live source
30
+ * makes it a fork; a destroyed one a wake). BREAKING vs create-config
31
+ * intuition: the new machine does NOT inherit network config — `egress`
32
+ * absent = unrestricted (Free orgs coerce deny-all) — so the adapter
33
+ * sends the applicable allowlist EXPLICITLY on every resume: the
34
+ * ref-recorded one for a bare resume, the SandboxResumePolicy one when
35
+ * the caller re-polices a wake (Lane E replace semantics, native).
36
+ * Resume of a GC'd or pre-artifact ref answers 404.
37
+ * - destroy `DELETE /{id}` → 200 `{ok:true}`; MACHINE ids only (a
38
+ * URL-encoded ref answers 404); repeat-delete = 200. Snapshot artifacts
39
+ * SURVIVE the machine — sleep is snapshot-then-destroy, wake is resume.
40
+ * - snapshot GC `DELETE /snapshots/{url-encoded ref}` → 200 `{ok:true}`,
41
+ * 404 = already gone (treat as the seam's idempotent no-op); reclaims
42
+ * the artifact and its storage row.
43
+ * - request `POST /{id}/request` body `{method, path, port?, headers?,
44
+ * body_b64}` → `{status, headers, body_b64}`, relayed into the box.
45
+ * `port` absent targets the canonical box port {@link CLOUD_BOX_PORT}
46
+ * (NOT the box's $PORT — the one provider divergence the conformance
47
+ * multiPort flag covers); any explicit 1-65535 routes (probed — an
48
+ * out-of-range port answers a clean 400 validation error), so the
49
+ * in-box agent control port (8811) works.
50
+ - exec/files also exist server-side (`POST /{id}/exec`,
51
+ * `GET|PUT /{id}/files?path=`, `GET /{id}/files/list?dir=`) — adapter-
52
+ * private, used for live-lane bootstrap and diagnostics only.
53
+ * - ingress the create/resume handle `url`
54
+ * (`https://<id-suffix>-m.vendo.run`, single-label scheme SHIPPED by
55
+ * vendo-web #85 on 2026-07-20; the -m is a SUFFIX, not a prefix —
56
+ * Cloudflare worker routes only allow leading wildcards, so the route is
57
+ * `*-m.vendo.run/*`) is the canonical-port public surface;
58
+ * `machine.url(port)` inserts other ports before the suffix
59
+ * (`https://<id-suffix>-<port>-m.vendo.run`), matching the machine-proxy
60
+ * parse `^([a-z0-9]{24})(?:-(port))?-m\.vendo\.run$`. Single-label hosts
61
+ * ride the existing `*.vendo.run` Universal SSL cert — no advanced
62
+ * certificate needed (live-verified 2026-07-20: TLS fails on the legacy
63
+ * dot shape `test123.m.vendo.run`, succeeds single-label; unknown ids
64
+ * answer a JSON 404). The console mints `url`, so the hostname shape is
65
+ * console-side; this adapter echoes whatever handle it is given.
66
+ *
67
+ * Adapter mapping:
68
+ * - `machine.snapshot()` = the artifact mint, wrapped in the adapter's
69
+ * composite ref {@link CLOUD_SNAPSHOT_REF_PREFIX} + base64url(JSON
70
+ * {machineId, ref, allowedDomains?}): the machine id lets destroy-by-ref
71
+ * reap a still-running source, and the allowlist is what a bare resume
72
+ * re-applies (the wire inherits nothing).
73
+ * - `machine.stop()` = destroy: Cloud has no pause — with artifacts
74
+ * surviving the machine, snapshot-then-destroy IS the sleep semantics
75
+ * (exactly the machine-lifecycle flow), and previously minted refs stay
76
+ * valid through it (the seam law).
77
+ * - `adapter.destroy(ref)` = best-effort reap of the recorded source
78
+ * machine, then artifact GC.
79
+ *
80
+ * Lifecycle interaction: Vendo auto-sleeps an idle machine after 5 minutes
81
+ * (snapshot → destroy); Cloud independently sweeps at 10 minutes idle and
82
+ * 24 hours max age. Our sleep normally wins; when the Cloud sweep gets
83
+ * there first the next wake resumes from the last stored ref, losing at
84
+ * most scratch state since it — acceptable, because the data rule keeps
85
+ * anything durable in the Vendo store, never on the VM disk.
86
+ */
87
+ /** The console mounts the managed-sandbox surface here
88
+ * (apps/console/app/api/v1/sandboxes/*). */
89
+ export const CLOUD_SANDBOX_PATH = "/api/v1/sandboxes";
90
+ /** Snapshot-artifact GC route (under {@link CLOUD_SANDBOX_PATH}). */
91
+ export const CLOUD_SNAPSHOTS_SUBPATH = "/snapshots";
92
+ /** Adapter-minted snapshot refs: this prefix + base64url(JSON state).
93
+ * These are what the seam (and app documents) carry. */
94
+ export const CLOUD_SNAPSHOT_REF_PREFIX = "vendo:v2:";
95
+ /** Console-minted artifact refs (`vendo:snap_<40hex>`) — carried INSIDE the
96
+ * adapter's composite ref, never handed to the seam bare. */
97
+ export const CONSOLE_SNAPSHOT_REF_PREFIX = "vendo:";
98
+ /** The canonical box port the relay targets when no port rides the wire
99
+ * (and the one the public ingress `https://<id-suffix>-m.vendo.run`
100
+ * serves). */
101
+ export const CLOUD_BOX_PORT = 8080;
@@ -0,0 +1,38 @@
1
+ import type { SandboxAdapter } from "@vendoai/apps";
2
+ export interface CloudSandboxOptions {
3
+ apiKey: string;
4
+ /** Defaults to the Vendo console; the composition seam passes VENDO_CLOUD_URL. */
5
+ baseUrl?: string;
6
+ /** Per-request abort timeout, in milliseconds. */
7
+ timeoutMs?: number;
8
+ fetch?: typeof fetch;
9
+ }
10
+ /** The Cloud sandbox adapter — the OSS side of the managed-sandbox seam: the
11
+ * execution-v2 SandboxAdapter speaking HTTP to the console's /api/v1/sandboxes
12
+ * routes (Vendo's pooled provider capacity, metered as sandbox_minutes). The
13
+ * wire contract — the ARTIFACT model, verified live — lives in
14
+ * sandbox-wire.ts. Cloned from cloudConnections' shape: behavior comes ONLY
15
+ * from constructor arguments (adapter rule — see selectSandbox in server.ts);
16
+ * the adapter never reads the environment.
17
+ *
18
+ * Provider particulars, versus the e2b reference port:
19
+ * - Snapshots are persistent artifacts that survive the machine; resume
20
+ * boots a NEW machine from one (fork when the source lives, wake when it
21
+ * is gone) and inherits NO network config, so every resume sends the
22
+ * applicable allowlist explicitly — the ref-recorded one bare, the
23
+ * caller's SandboxResumePolicy when a wake re-polices (Lane E replace
24
+ * semantics, native on the wire).
25
+ * - stop() destroys the machine: Cloud has no pause, and with artifacts
26
+ * surviving it, snapshot-then-destroy IS the sleep semantics; previously
27
+ * minted refs stay valid through it (the seam law).
28
+ * - Composite refs: the seam sees `vendo:v2:<base64url state>` carrying the
29
+ * console artifact ref, the source machine id (destroy-by-ref reaps a
30
+ * still-running source best-effort before the artifact GC), and the
31
+ * snapshot-time allowlist a bare resume re-applies.
32
+ * - `spec.template` is dropped from the wire: the create route takes none —
33
+ * the pooled base image (Node + the in-box agent) is Cloud's own.
34
+ *
35
+ * The machine object also carries adapter-private exec/files/url used for
36
+ * live-lane bootstrap and diagnostics — NOT part of the public seam (the
37
+ * in-box agent owns the inside of the box). */
38
+ export declare function cloudSandbox(options: CloudSandboxOptions): SandboxAdapter;