@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.
- package/LICENSE +202 -0
- package/README.md +28 -0
- package/bin/vendo.mjs +4 -0
- package/dist/ai-sdk.d.ts +35 -0
- package/dist/ai-sdk.js +62 -0
- package/dist/auth-presets/auth-js.d.ts +14 -0
- package/dist/auth-presets/auth-js.js +91 -0
- package/dist/auth-presets/auth0.d.ts +23 -0
- package/dist/auth-presets/auth0.js +98 -0
- package/dist/auth-presets/clerk.d.ts +23 -0
- package/dist/auth-presets/clerk.js +65 -0
- package/dist/auth-presets/conformance.d.ts +42 -0
- package/dist/auth-presets/conformance.js +139 -0
- package/dist/auth-presets/identity.d.ts +56 -0
- package/dist/auth-presets/identity.js +152 -0
- package/dist/auth-presets/index.d.ts +14 -0
- package/dist/auth-presets/index.js +12 -0
- package/dist/auth-presets/jwt.d.ts +16 -0
- package/dist/auth-presets/jwt.js +54 -0
- package/dist/auth-presets/shared.d.ts +35 -0
- package/dist/auth-presets/shared.js +1 -0
- package/dist/auth-presets/supabase.d.ts +42 -0
- package/dist/auth-presets/supabase.js +229 -0
- package/dist/byo-approvals.d.ts +37 -0
- package/dist/byo-approvals.js +189 -0
- package/dist/capability-misses.d.ts +38 -0
- package/dist/capability-misses.js +172 -0
- package/dist/catalog.d.ts +19 -0
- package/dist/catalog.js +152 -0
- package/dist/cli/cloud/args.d.ts +3 -0
- package/dist/cli/cloud/args.js +32 -0
- package/dist/cli/cloud/auth.d.ts +17 -0
- package/dist/cli/cloud/auth.js +123 -0
- package/dist/cli/cloud/client.d.ts +28 -0
- package/dist/cli/cloud/client.js +133 -0
- package/dist/cli/cloud/command.d.ts +22 -0
- package/dist/cli/cloud/command.js +73 -0
- package/dist/cli/cloud/deploy.d.ts +26 -0
- package/dist/cli/cloud/deploy.js +187 -0
- package/dist/cli/cloud/device-login.d.ts +32 -0
- package/dist/cli/cloud/device-login.js +259 -0
- package/dist/cli/cloud/index.d.ts +5 -0
- package/dist/cli/cloud/index.js +59 -0
- package/dist/cli/cloud/keys.d.ts +2 -0
- package/dist/cli/cloud/keys.js +31 -0
- package/dist/cli/cloud/members.d.ts +3 -0
- package/dist/cli/cloud/members.js +25 -0
- package/dist/cli/cloud/output.d.ts +4 -0
- package/dist/cli/cloud/output.js +12 -0
- package/dist/cli/cloud/pending-claim.d.ts +29 -0
- package/dist/cli/cloud/pending-claim.js +38 -0
- package/dist/cli/cloud/read.d.ts +3 -0
- package/dist/cli/cloud/read.js +12 -0
- package/dist/cli/cloud/services.d.ts +2 -0
- package/dist/cli/cloud/services.js +9 -0
- package/dist/cli/cloud/session.d.ts +13 -0
- package/dist/cli/cloud/session.js +41 -0
- package/dist/cli/cloud-init.d.ts +60 -0
- package/dist/cli/cloud-init.js +149 -0
- package/dist/cli/dep-versions.d.ts +26 -0
- package/dist/cli/dep-versions.js +77 -0
- package/dist/cli/doctor-codes.d.ts +58 -0
- package/dist/cli/doctor-codes.js +60 -0
- package/dist/cli/doctor-live.d.ts +67 -0
- package/dist/cli/doctor-live.js +169 -0
- package/dist/cli/doctor.d.ts +52 -0
- package/dist/cli/doctor.js +467 -0
- package/dist/cli/eject.d.ts +29 -0
- package/dist/cli/eject.js +141 -0
- package/dist/cli/extract/apply.d.ts +26 -0
- package/dist/cli/extract/apply.js +123 -0
- package/dist/cli/extract/claude-cli-harness.d.ts +18 -0
- package/dist/cli/extract/claude-cli-harness.js +132 -0
- package/dist/cli/extract/claude-harness.d.ts +14 -0
- package/dist/cli/extract/claude-harness.js +127 -0
- package/dist/cli/extract/codex-cli-harness.d.ts +31 -0
- package/dist/cli/extract/codex-cli-harness.js +154 -0
- package/dist/cli/extract/delegate.d.ts +21 -0
- package/dist/cli/extract/delegate.js +77 -0
- package/dist/cli/extract/extraction.d.ts +65 -0
- package/dist/cli/extract/extraction.js +300 -0
- package/dist/cli/extract/gateway-fuel.d.ts +69 -0
- package/dist/cli/extract/gateway-fuel.js +69 -0
- package/dist/cli/extract/harness.d.ts +114 -0
- package/dist/cli/extract/harness.js +84 -0
- package/dist/cli/extract/index.d.ts +9 -0
- package/dist/cli/extract/index.js +9 -0
- package/dist/cli/extract/npx-engine-harness.d.ts +73 -0
- package/dist/cli/extract/npx-engine-harness.js +225 -0
- package/dist/cli/extract/stages.d.ts +149 -0
- package/dist/cli/extract/stages.js +398 -0
- package/dist/cli/framework.d.ts +8 -0
- package/dist/cli/framework.js +36 -0
- package/dist/cli/init-auth.d.ts +67 -0
- package/dist/cli/init-auth.js +142 -0
- package/dist/cli/init-scaffolds.d.ts +28 -0
- package/dist/cli/init-scaffolds.js +235 -0
- package/dist/cli/init.d.ts +115 -0
- package/dist/cli/init.js +1008 -0
- package/dist/cli/mcp/index.d.ts +7 -0
- package/dist/cli/mcp/index.js +59 -0
- package/dist/cli/mcp/registry.d.ts +9 -0
- package/dist/cli/mcp/registry.js +117 -0
- package/dist/cli/mcp/server-json.d.ts +12 -0
- package/dist/cli/mcp/server-json.js +65 -0
- package/dist/cli/mcp/server.schema.json +574 -0
- package/dist/cli/mcp/verify-domain.d.ts +11 -0
- package/dist/cli/mcp/verify-domain.js +48 -0
- package/dist/cli/playground/app/embed-entry.d.ts +20 -0
- package/dist/cli/playground/app/embed-entry.js +46 -0
- package/dist/cli/playground/app/fake-client.d.ts +3 -0
- package/dist/cli/playground/app/fake-client.js +215 -0
- package/dist/cli/playground/app/fixtures.d.ts +47 -0
- package/dist/cli/playground/app/fixtures.js +472 -0
- package/dist/cli/playground/app/main.d.ts +1 -0
- package/dist/cli/playground/app/main.js +108 -0
- package/dist/cli/playground/app/scenario-mount.d.ts +13 -0
- package/dist/cli/playground/app/scenario-mount.js +48 -0
- package/dist/cli/playground/app/scenarios.d.ts +25 -0
- package/dist/cli/playground/app/scenarios.js +152 -0
- package/dist/cli/playground/app/theme-editor.d.ts +13 -0
- package/dist/cli/playground/app/theme-editor.js +149 -0
- package/dist/cli/playground/app/theme-state.d.ts +29 -0
- package/dist/cli/playground/app/theme-state.js +151 -0
- package/dist/cli/playground/bundle.gen.d.ts +2 -0
- package/dist/cli/playground/bundle.gen.js +2 -0
- package/dist/cli/playground/embed-bundle.gen.d.ts +2 -0
- package/dist/cli/playground/embed-bundle.gen.js +2 -0
- package/dist/cli/playground.d.ts +33 -0
- package/dist/cli/playground.js +120 -0
- package/dist/cli/pretty.d.ts +48 -0
- package/dist/cli/pretty.js +330 -0
- package/dist/cli/refine.d.ts +43 -0
- package/dist/cli/refine.js +181 -0
- package/dist/cli/semantics.d.ts +31 -0
- package/dist/cli/semantics.js +122 -0
- package/dist/cli/shared.d.ts +68 -0
- package/dist/cli/shared.js +184 -0
- package/dist/cli/sync.d.ts +36 -0
- package/dist/cli/sync.js +202 -0
- package/dist/cli/theme/color.d.ts +15 -0
- package/dist/cli/theme/color.js +188 -0
- package/dist/cli/theme/css-vars.d.ts +13 -0
- package/dist/cli/theme/css-vars.js +40 -0
- package/dist/cli/theme/entry-candidates.d.ts +5 -0
- package/dist/cli/theme/entry-candidates.js +22 -0
- package/dist/cli/theme/extract-theme.d.ts +184 -0
- package/dist/cli/theme/extract-theme.js +358 -0
- package/dist/cli/theme/walk.d.ts +2 -0
- package/dist/cli/theme/walk.js +24 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +333 -0
- package/dist/cloud-apps.d.ts +17 -0
- package/dist/cloud-apps.js +46 -0
- package/dist/cloud-console.d.ts +32 -0
- package/dist/cloud-console.js +72 -0
- package/dist/cloud-tools.d.ts +23 -0
- package/dist/cloud-tools.js +196 -0
- package/dist/connections.d.ts +71 -0
- package/dist/connections.js +192 -0
- package/dist/deployment-identity.d.ts +12 -0
- package/dist/deployment-identity.js +69 -0
- package/dist/dev-creds/model.d.ts +65 -0
- package/dist/dev-creds/model.js +159 -0
- package/dist/dev-creds/resolve.d.ts +36 -0
- package/dist/dev-creds/resolve.js +57 -0
- package/dist/hosted-store.d.ts +52 -0
- package/dist/hosted-store.js +322 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +1 -0
- package/dist/mastra.d.ts +46 -0
- package/dist/mastra.js +88 -0
- package/dist/react.d.ts +10 -0
- package/dist/react.js +32 -0
- package/dist/refine.d.ts +244 -0
- package/dist/refine.js +599 -0
- package/dist/remixable.d.ts +18 -0
- package/dist/remixable.js +42 -0
- package/dist/runtime-capture.d.ts +18 -0
- package/dist/runtime-capture.js +111 -0
- package/dist/sandbox-wire.d.ts +101 -0
- package/dist/sandbox-wire.js +101 -0
- package/dist/sandbox.d.ts +38 -0
- package/dist/sandbox.js +389 -0
- package/dist/server.d.ts +216 -0
- package/dist/server.js +1320 -0
- package/dist/sync-impact.d.ts +14 -0
- package/dist/sync-impact.js +75 -0
- package/dist/turn-liveness.d.ts +33 -0
- package/dist/turn-liveness.js +105 -0
- package/dist/wire/approvals.d.ts +10 -0
- package/dist/wire/approvals.js +59 -0
- package/dist/wire/apps.d.ts +5 -0
- package/dist/wire/apps.js +155 -0
- package/dist/wire/automations.d.ts +5 -0
- package/dist/wire/automations.js +57 -0
- package/dist/wire/box.d.ts +3 -0
- package/dist/wire/box.js +268 -0
- package/dist/wire/connections.d.ts +5 -0
- package/dist/wire/connections.js +47 -0
- package/dist/wire/context.d.ts +22 -0
- package/dist/wire/context.js +190 -0
- package/dist/wire/doctor.d.ts +19 -0
- package/dist/wire/doctor.js +116 -0
- package/dist/wire/misc.d.ts +18 -0
- package/dist/wire/misc.js +227 -0
- package/dist/wire/shared.d.ts +158 -0
- package/dist/wire/shared.js +134 -0
- package/dist/wire/threads.d.ts +3 -0
- package/dist/wire/threads.js +68 -0
- package/package.json +151 -0
- 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;
|