@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
package/dist/sandbox.js
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import { VendoError } from "@vendoai/core";
|
|
2
|
+
import { deploymentIdentityHeaders } from "./deployment-identity.js";
|
|
3
|
+
import { CLOUD_BOX_PORT, CLOUD_SANDBOX_PATH, CLOUD_SNAPSHOT_REF_PREFIX, CLOUD_SNAPSHOTS_SUBPATH, CONSOLE_SNAPSHOT_REF_PREFIX, } from "./sandbox-wire.js";
|
|
4
|
+
/** Console error codes forwarded as-is when they are wire-legal VendoError
|
|
5
|
+
* codes (same posture as the apps block's cloud share/publish client). The
|
|
6
|
+
* console's "unavailable"/"quota-exhausted" have no VendoError twin; they fall
|
|
7
|
+
* to sandbox-unavailable and the 402 → cloud-required mapping respectively. */
|
|
8
|
+
const CLOUD_ERROR_CODES = new Set([
|
|
9
|
+
"validation",
|
|
10
|
+
"blocked",
|
|
11
|
+
"not-implemented",
|
|
12
|
+
"cloud-required",
|
|
13
|
+
"not-found",
|
|
14
|
+
"conflict",
|
|
15
|
+
]);
|
|
16
|
+
/** Same default as the e2b adapter and the retired ENG-295 broker client:
|
|
17
|
+
* generous enough for a slow machine boot, small enough that a hung console
|
|
18
|
+
* request can't wedge a generation forever. */
|
|
19
|
+
const DEFAULT_TIMEOUT_MS = 300_000;
|
|
20
|
+
const encoder = new TextEncoder();
|
|
21
|
+
const decoder = new TextDecoder();
|
|
22
|
+
const toBytes = (data) => typeof data === "string" ? encoder.encode(data) : data;
|
|
23
|
+
const toArrayBuffer = (value) => value.slice().buffer;
|
|
24
|
+
/** btoa/atob-based codecs (the console speaks base64 JSON envelopes); chunked
|
|
25
|
+
* like the console's own encoder, and Buffer-free so the umbrella's server
|
|
26
|
+
* surface keeps loading on edge/Worker targets. */
|
|
27
|
+
function encodeBase64(bytes) {
|
|
28
|
+
let binary = "";
|
|
29
|
+
for (let offset = 0; offset < bytes.length; offset += 32_768) {
|
|
30
|
+
binary += String.fromCharCode(...bytes.subarray(offset, offset + 32_768));
|
|
31
|
+
}
|
|
32
|
+
return btoa(binary);
|
|
33
|
+
}
|
|
34
|
+
function decodeBase64(value) {
|
|
35
|
+
try {
|
|
36
|
+
return Uint8Array.from(atob(value), (character) => character.charCodeAt(0));
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Console garbage is the SERVICE misbehaving, never the caller's fault —
|
|
40
|
+
// same posture as every malformed-success branch below.
|
|
41
|
+
throw new VendoError("sandbox-unavailable", "Vendo Cloud sandbox returned invalid base64 content");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function raiseCloudError(response) {
|
|
45
|
+
let payload;
|
|
46
|
+
try {
|
|
47
|
+
payload = JSON.parse(await response.text());
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
payload = undefined;
|
|
51
|
+
}
|
|
52
|
+
const error = typeof payload === "object" && payload !== null && "error" in payload
|
|
53
|
+
? payload.error
|
|
54
|
+
: undefined;
|
|
55
|
+
const message = typeof error?.message === "string"
|
|
56
|
+
? error.message
|
|
57
|
+
: `Vendo Cloud sandbox request failed with ${response.status}`;
|
|
58
|
+
// The console's meter gate (quota-exhausted) rides HTTP 402 — the one
|
|
59
|
+
// "pay/upgrade to proceed" signal, same mapping as cloudConnections. 401
|
|
60
|
+
// (bad/revoked key) is the same "fix your Cloud standing" story for the
|
|
61
|
+
// host operator, so it keeps the ENG-295 client's cloud-required mapping —
|
|
62
|
+
// with the server's own message preserved.
|
|
63
|
+
if (response.status === 402 || response.status === 401) {
|
|
64
|
+
throw new VendoError("cloud-required", message);
|
|
65
|
+
}
|
|
66
|
+
const code = typeof error?.code === "string" && CLOUD_ERROR_CODES.has(error.code)
|
|
67
|
+
? error.code
|
|
68
|
+
: "sandbox-unavailable";
|
|
69
|
+
throw new VendoError(code, message);
|
|
70
|
+
}
|
|
71
|
+
const parsePort = (env) => {
|
|
72
|
+
const port = Number(env.PORT ?? CLOUD_BOX_PORT);
|
|
73
|
+
return Number.isInteger(port) && port > 0 && port <= 65_535 ? port : CLOUD_BOX_PORT;
|
|
74
|
+
};
|
|
75
|
+
const toBase64Url = (value) => encodeBase64(encoder.encode(value)).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
|
|
76
|
+
const encodeSnapshotRef = (state) => `${CLOUD_SNAPSHOT_REF_PREFIX}${toBase64Url(JSON.stringify(state))}`;
|
|
77
|
+
const decodeSnapshotRef = (snapshotRef) => {
|
|
78
|
+
try {
|
|
79
|
+
if (!snapshotRef.startsWith(CLOUD_SNAPSHOT_REF_PREFIX)
|
|
80
|
+
|| snapshotRef.length <= CLOUD_SNAPSHOT_REF_PREFIX.length) {
|
|
81
|
+
throw new Error("unknown prefix");
|
|
82
|
+
}
|
|
83
|
+
const payload = snapshotRef.slice(CLOUD_SNAPSHOT_REF_PREFIX.length)
|
|
84
|
+
.replaceAll("-", "+").replaceAll("_", "/");
|
|
85
|
+
const state = JSON.parse(decoder.decode(Uint8Array.from(atob(payload), (character) => character.charCodeAt(0))));
|
|
86
|
+
if (state.version !== 2
|
|
87
|
+
|| typeof state.machineId !== "string" || state.machineId.length === 0
|
|
88
|
+
|| typeof state.ref !== "string" || !state.ref.startsWith(CONSOLE_SNAPSHOT_REF_PREFIX)) {
|
|
89
|
+
throw new Error("invalid payload");
|
|
90
|
+
}
|
|
91
|
+
if (state.allowedDomains !== undefined && !(Array.isArray(state.allowedDomains)
|
|
92
|
+
&& state.allowedDomains.every((host) => typeof host === "string"))) {
|
|
93
|
+
throw new Error("invalid allowedDomains policy");
|
|
94
|
+
}
|
|
95
|
+
if (state.port !== undefined && !(Number.isInteger(state.port)
|
|
96
|
+
&& state.port > 0 && state.port <= 65_535)) {
|
|
97
|
+
throw new Error("invalid port");
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
version: 2,
|
|
101
|
+
machineId: state.machineId,
|
|
102
|
+
ref: state.ref,
|
|
103
|
+
...(state.allowedDomains === undefined ? {} : { allowedDomains: [...state.allowedDomains] }),
|
|
104
|
+
...(state.port === undefined ? {} : { port: state.port }),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
throw new VendoError("validation", `Vendo Cloud snapshot references must start with "${CLOUD_SNAPSHOT_REF_PREFIX}" and carry a valid payload`);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
/** True exactly for the "that state is already gone" answer that the seam's
|
|
112
|
+
* idempotent transitions (destroy twice, stop of a dead machine) absorb. */
|
|
113
|
+
const isGone = (error) => error instanceof VendoError && error.code === "not-found";
|
|
114
|
+
/** The Cloud sandbox adapter — the OSS side of the managed-sandbox seam: the
|
|
115
|
+
* execution-v2 SandboxAdapter speaking HTTP to the console's /api/v1/sandboxes
|
|
116
|
+
* routes (Vendo's pooled provider capacity, metered as sandbox_minutes). The
|
|
117
|
+
* wire contract — the ARTIFACT model, verified live — lives in
|
|
118
|
+
* sandbox-wire.ts. Cloned from cloudConnections' shape: behavior comes ONLY
|
|
119
|
+
* from constructor arguments (adapter rule — see selectSandbox in server.ts);
|
|
120
|
+
* the adapter never reads the environment.
|
|
121
|
+
*
|
|
122
|
+
* Provider particulars, versus the e2b reference port:
|
|
123
|
+
* - Snapshots are persistent artifacts that survive the machine; resume
|
|
124
|
+
* boots a NEW machine from one (fork when the source lives, wake when it
|
|
125
|
+
* is gone) and inherits NO network config, so every resume sends the
|
|
126
|
+
* applicable allowlist explicitly — the ref-recorded one bare, the
|
|
127
|
+
* caller's SandboxResumePolicy when a wake re-polices (Lane E replace
|
|
128
|
+
* semantics, native on the wire).
|
|
129
|
+
* - stop() destroys the machine: Cloud has no pause, and with artifacts
|
|
130
|
+
* surviving it, snapshot-then-destroy IS the sleep semantics; previously
|
|
131
|
+
* minted refs stay valid through it (the seam law).
|
|
132
|
+
* - Composite refs: the seam sees `vendo:v2:<base64url state>` carrying the
|
|
133
|
+
* console artifact ref, the source machine id (destroy-by-ref reaps a
|
|
134
|
+
* still-running source best-effort before the artifact GC), and the
|
|
135
|
+
* snapshot-time allowlist a bare resume re-applies.
|
|
136
|
+
* - `spec.template` is dropped from the wire: the create route takes none —
|
|
137
|
+
* the pooled base image (Node + the in-box agent) is Cloud's own.
|
|
138
|
+
*
|
|
139
|
+
* The machine object also carries adapter-private exec/files/url used for
|
|
140
|
+
* live-lane bootstrap and diagnostics — NOT part of the public seam (the
|
|
141
|
+
* in-box agent owns the inside of the box). */
|
|
142
|
+
export function cloudSandbox(options) {
|
|
143
|
+
const base = (options.baseUrl ?? "https://console.vendo.run").replace(/\/$/, "");
|
|
144
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
145
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
146
|
+
const send = async (path, init = {}) => {
|
|
147
|
+
const response = await fetchImpl(`${base}${CLOUD_SANDBOX_PATH}${path}`, {
|
|
148
|
+
...init,
|
|
149
|
+
headers: {
|
|
150
|
+
authorization: `Bearer ${options.apiKey}`,
|
|
151
|
+
accept: "application/json",
|
|
152
|
+
// Interaction model: key-authed Cloud requests carry the deployment
|
|
153
|
+
// identity; the console meters usage from real traffic.
|
|
154
|
+
...(await deploymentIdentityHeaders()),
|
|
155
|
+
...init.headers,
|
|
156
|
+
},
|
|
157
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
158
|
+
});
|
|
159
|
+
if (!response.ok)
|
|
160
|
+
await raiseCloudError(response);
|
|
161
|
+
return response;
|
|
162
|
+
};
|
|
163
|
+
const sendJson = async (path, method, body) => {
|
|
164
|
+
const response = await send(path, {
|
|
165
|
+
method,
|
|
166
|
+
...(body === undefined ? {} : {
|
|
167
|
+
headers: { "content-type": "application/json" },
|
|
168
|
+
body: JSON.stringify(body),
|
|
169
|
+
}),
|
|
170
|
+
});
|
|
171
|
+
try {
|
|
172
|
+
return await response.json();
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return {};
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
const parseHandle = (payload) => {
|
|
179
|
+
const handle = payload;
|
|
180
|
+
if (typeof handle.id !== "string" || typeof handle.url !== "string") {
|
|
181
|
+
throw new VendoError("sandbox-unavailable", "Vendo Cloud sandbox returned no machine handle");
|
|
182
|
+
}
|
|
183
|
+
return { id: handle.id, url: handle.url };
|
|
184
|
+
};
|
|
185
|
+
const wrap = (handle, state) => {
|
|
186
|
+
const prefix = `/${encodeURIComponent(handle.id)}`;
|
|
187
|
+
/** POST /{id}/snapshot — mint a persistent artifact; the source keeps running. */
|
|
188
|
+
const mintArtifact = async () => {
|
|
189
|
+
const payload = await sendJson(`${prefix}/snapshot`, "POST");
|
|
190
|
+
if (typeof payload.ref !== "string" || payload.ref.length === 0) {
|
|
191
|
+
throw new VendoError("sandbox-unavailable", "Vendo Cloud sandbox returned no snapshot reference");
|
|
192
|
+
}
|
|
193
|
+
// A ref this adapter would itself refuse to carry must never reach a
|
|
194
|
+
// document — reject it as console garbage here instead.
|
|
195
|
+
if (!payload.ref.startsWith(CONSOLE_SNAPSHOT_REF_PREFIX)
|
|
196
|
+
|| payload.ref.length <= CONSOLE_SNAPSHOT_REF_PREFIX.length) {
|
|
197
|
+
throw new VendoError("sandbox-unavailable", `Vendo Cloud sandbox returned a foreign snapshot reference (expected the "${CONSOLE_SNAPSHOT_REF_PREFIX}" prefix)`);
|
|
198
|
+
}
|
|
199
|
+
return payload.ref;
|
|
200
|
+
};
|
|
201
|
+
const remove = async () => {
|
|
202
|
+
try {
|
|
203
|
+
await sendJson(prefix, "DELETE");
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
if (!isGone(error))
|
|
207
|
+
throw error;
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
// Seam rule: sleeping or destroying twice is not an error, and destroy is
|
|
211
|
+
// final. The one-shot promises make that hold under concurrency too —
|
|
212
|
+
// each transition is assigned synchronously, so racing callers share one
|
|
213
|
+
// console call chain, and a destroy during an in-flight stop serializes
|
|
214
|
+
// after it (e2b-adapter pattern).
|
|
215
|
+
let sleeping;
|
|
216
|
+
let destroying;
|
|
217
|
+
return {
|
|
218
|
+
id: handle.id,
|
|
219
|
+
async request(req) {
|
|
220
|
+
let payload;
|
|
221
|
+
try {
|
|
222
|
+
payload = await sendJson(`${prefix}/request`, "POST", {
|
|
223
|
+
method: req.method,
|
|
224
|
+
path: req.path.startsWith("/") ? req.path : `/${req.path}`,
|
|
225
|
+
// Absent port targets the canonical box port server-side; explicit
|
|
226
|
+
// ports (e.g. the in-box agent control port) route as-is.
|
|
227
|
+
...(req.port === undefined ? {} : { port: req.port }),
|
|
228
|
+
...(req.headers === undefined ? {} : { headers: req.headers }),
|
|
229
|
+
...(req.body === undefined ? {} : { body_b64: encodeBase64(toBytes(req.body)) }),
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
// Wave 7 — the seam's dead-machine signal. Cloud stop is final (no
|
|
234
|
+
// pause), so a conflict ("Sandbox is stopped") on the DATA path
|
|
235
|
+
// means the sweep destroyed the machine out from under this handle,
|
|
236
|
+
// exactly like a purged id's not-found: both become the thrown
|
|
237
|
+
// not-found the lifecycle's eviction/re-wake recovery keys on.
|
|
238
|
+
if (error instanceof VendoError && error.code === "conflict") {
|
|
239
|
+
throw new VendoError("not-found", `Vendo Cloud sandbox ${handle.id} is gone (destroyed by the provider): ${error.message}`);
|
|
240
|
+
}
|
|
241
|
+
throw error;
|
|
242
|
+
}
|
|
243
|
+
const proxied = payload;
|
|
244
|
+
if (typeof proxied.status !== "number" || typeof proxied.body_b64 !== "string") {
|
|
245
|
+
throw new VendoError("sandbox-unavailable", "Vendo Cloud sandbox returned an invalid proxy response");
|
|
246
|
+
}
|
|
247
|
+
const headers = typeof proxied.headers === "object" && proxied.headers !== null
|
|
248
|
+
? Object.fromEntries(Object.entries(proxied.headers)
|
|
249
|
+
.filter((entry) => typeof entry[1] === "string"))
|
|
250
|
+
: {};
|
|
251
|
+
return { status: proxied.status, headers, body: decodeBase64(proxied.body_b64) };
|
|
252
|
+
},
|
|
253
|
+
async snapshot() {
|
|
254
|
+
// A checkpoint through a machine object that already slept or was
|
|
255
|
+
// destroyed is a caller bug — say so crisply instead of relaying
|
|
256
|
+
// whatever the console answers for the dead machine.
|
|
257
|
+
if (sleeping !== undefined || destroying !== undefined) {
|
|
258
|
+
throw new VendoError("conflict", "the machine is asleep or destroyed; resume its snapshot ref instead of checkpointing it");
|
|
259
|
+
}
|
|
260
|
+
return encodeSnapshotRef({
|
|
261
|
+
version: 2,
|
|
262
|
+
machineId: handle.id,
|
|
263
|
+
ref: await mintArtifact(),
|
|
264
|
+
...(state.allowedDomains === undefined ? {} : { allowedDomains: [...state.allowedDomains] }),
|
|
265
|
+
port: state.port,
|
|
266
|
+
});
|
|
267
|
+
},
|
|
268
|
+
async stop() {
|
|
269
|
+
if (destroying !== undefined) {
|
|
270
|
+
await destroying;
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
// Cloud sleep IS destruction: there is no pause, and snapshot
|
|
274
|
+
// artifacts survive the machine — the sleep flows mint their ref
|
|
275
|
+
// BEFORE stopping (machine-lifecycle.ts) and wake by resuming it.
|
|
276
|
+
sleeping ??= remove();
|
|
277
|
+
await sleeping;
|
|
278
|
+
},
|
|
279
|
+
async destroy() {
|
|
280
|
+
destroying ??= (sleeping ?? Promise.resolve())
|
|
281
|
+
.catch(() => undefined)
|
|
282
|
+
.then(remove);
|
|
283
|
+
await destroying;
|
|
284
|
+
},
|
|
285
|
+
// ——— adapter-private below this line (live-lane bootstrap + diagnostics) ———
|
|
286
|
+
async exec(cmd, execOptions) {
|
|
287
|
+
const payload = await sendJson(`${prefix}/exec`, "POST", {
|
|
288
|
+
cmd,
|
|
289
|
+
...(execOptions?.cwd === undefined ? {} : { cwd: execOptions.cwd }),
|
|
290
|
+
...(execOptions?.timeoutMs === undefined ? {} : { timeout_ms: execOptions.timeoutMs }),
|
|
291
|
+
});
|
|
292
|
+
if (typeof payload.code !== "number") {
|
|
293
|
+
throw new VendoError("sandbox-unavailable", "Vendo Cloud sandbox returned an invalid exec response");
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
code: payload.code,
|
|
297
|
+
stdout: typeof payload.stdout === "string" ? payload.stdout : "",
|
|
298
|
+
stderr: typeof payload.stderr === "string" ? payload.stderr : "",
|
|
299
|
+
};
|
|
300
|
+
},
|
|
301
|
+
files: {
|
|
302
|
+
async read(path) {
|
|
303
|
+
const response = await send(`${prefix}/files?path=${encodeURIComponent(path)}`);
|
|
304
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
305
|
+
},
|
|
306
|
+
async write(path, bytes) {
|
|
307
|
+
await send(`${prefix}/files?path=${encodeURIComponent(path)}`, {
|
|
308
|
+
method: "PUT",
|
|
309
|
+
headers: { "content-type": "application/octet-stream" },
|
|
310
|
+
body: toArrayBuffer(toBytes(bytes)),
|
|
311
|
+
});
|
|
312
|
+
},
|
|
313
|
+
async list(dir) {
|
|
314
|
+
const payload = await sendJson(`${prefix}/files/list?dir=${encodeURIComponent(dir)}`, "GET");
|
|
315
|
+
return Array.isArray(payload.entries)
|
|
316
|
+
? payload.entries.filter((entry) => typeof entry === "string")
|
|
317
|
+
: [];
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
async url(port) {
|
|
321
|
+
// Wave 4 (layer 3) — the browser→box serving path. The handle URL
|
|
322
|
+
// from create/resume IS the canonical-port ingress: single-label
|
|
323
|
+
// `<id-suffix>-m.vendo.run` as shipped by the console (vendo-web
|
|
324
|
+
// #85; -m is a SUFFIX because Cloudflare routes only allow leading
|
|
325
|
+
// wildcards, `*-m.vendo.run/*`). Other ports insert before the
|
|
326
|
+
// suffix — `<id-suffix>-<port>-m.vendo.run` — matching the
|
|
327
|
+
// machine-proxy parse (sandbox-wire.ts ingress entry). Hosts
|
|
328
|
+
// without a -m label (custom consoles) keep the e2b-style prefix.
|
|
329
|
+
const target = port ?? state.port;
|
|
330
|
+
if (target === CLOUD_BOX_PORT)
|
|
331
|
+
return handle.url;
|
|
332
|
+
const ingress = new URL(handle.url);
|
|
333
|
+
const suffixed = /^(.+)-m(\..+)$/.exec(ingress.host);
|
|
334
|
+
ingress.host = suffixed === null
|
|
335
|
+
? `${target}-${ingress.host}`
|
|
336
|
+
: `${suffixed[1]}-${target}-m${suffixed[2]}`;
|
|
337
|
+
return ingress.origin;
|
|
338
|
+
},
|
|
339
|
+
};
|
|
340
|
+
};
|
|
341
|
+
return {
|
|
342
|
+
async create(spec) {
|
|
343
|
+
// spec.template is dropped: the create route takes none — the pooled
|
|
344
|
+
// base image (Node + the in-box agent harness) is Cloud's own
|
|
345
|
+
// (sandbox-wire.ts).
|
|
346
|
+
return wrap(parseHandle(await sendJson("", "POST", {
|
|
347
|
+
env: spec.env,
|
|
348
|
+
// Seam semantics carried verbatim: absent = unrestricted, [] = deny-all
|
|
349
|
+
// (deny-by-default lives ABOVE the seam — Lane E's grant flow).
|
|
350
|
+
...(spec.allowedDomains === undefined ? {} : { egress: [...spec.allowedDomains] }),
|
|
351
|
+
// Defensive copy: later refs must record the policy the machine was
|
|
352
|
+
// CREATED with, immune to caller-side mutation of the array.
|
|
353
|
+
})), {
|
|
354
|
+
allowedDomains: spec.allowedDomains === undefined ? undefined : [...spec.allowedDomains],
|
|
355
|
+
port: parsePort(spec.env),
|
|
356
|
+
});
|
|
357
|
+
},
|
|
358
|
+
async resume(snapshotRef, policy) {
|
|
359
|
+
const state = decodeSnapshotRef(snapshotRef);
|
|
360
|
+
// The new machine inherits NO network config from the artifact
|
|
361
|
+
// (sandbox-wire.ts), so every resume states the applicable allowlist:
|
|
362
|
+
// Lane E's replace semantics when the caller re-polices the wake, the
|
|
363
|
+
// ref-recorded snapshot-time policy otherwise. undefined stays the
|
|
364
|
+
// seam's "unrestricted" (absent field on the wire).
|
|
365
|
+
const allowedDomains = policy === undefined ? state.allowedDomains : policy.allowedDomains;
|
|
366
|
+
return wrap(parseHandle(await sendJson("/resume", "POST", {
|
|
367
|
+
ref: state.ref,
|
|
368
|
+
...(allowedDomains === undefined ? {} : { egress: [...allowedDomains] }),
|
|
369
|
+
})), {
|
|
370
|
+
allowedDomains: allowedDomains === undefined ? undefined : [...allowedDomains],
|
|
371
|
+
port: state.port ?? CLOUD_BOX_PORT,
|
|
372
|
+
});
|
|
373
|
+
},
|
|
374
|
+
async destroy(snapshotRef) {
|
|
375
|
+
const state = decodeSnapshotRef(snapshotRef);
|
|
376
|
+
// Best-effort reap of the recorded source machine (it is usually
|
|
377
|
+
// already gone — the sleep flow destroyed it), then the artifact GC.
|
|
378
|
+
// A 404 from either is the seam's idempotent no-op.
|
|
379
|
+
await sendJson(`/${encodeURIComponent(state.machineId)}`, "DELETE").catch(() => undefined);
|
|
380
|
+
try {
|
|
381
|
+
await sendJson(`${CLOUD_SNAPSHOTS_SUBPATH}/${encodeURIComponent(state.ref)}`, "DELETE");
|
|
382
|
+
}
|
|
383
|
+
catch (error) {
|
|
384
|
+
if (!isGone(error))
|
|
385
|
+
throw error;
|
|
386
|
+
}
|
|
387
|
+
},
|
|
388
|
+
};
|
|
389
|
+
}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { type ActionsRegistry, type Connector, type ServerActionHandler } from "@vendoai/actions";
|
|
2
|
+
import { type VendoAgent } from "@vendoai/agent";
|
|
3
|
+
import { type AppsConfig, type AppsRuntime, type SandboxAdapter } from "@vendoai/apps";
|
|
4
|
+
import { type AutomationsEngine } from "@vendoai/automations";
|
|
5
|
+
import { type ActAs, type ComponentCatalog, type ComponentRegistry, type Json, type Principal, type RunId, type SecretsProvider, type ToolRegistry } from "@vendoai/core";
|
|
6
|
+
import { type Judge, type PolicyConfig, type VendoGuard } from "@vendoai/guard";
|
|
7
|
+
import { type HostOAuthAdapter } from "@vendoai/mcp";
|
|
8
|
+
import { type VendoStore } from "@vendoai/store";
|
|
9
|
+
export { eraseStore, type EraseReport, type EraseTable } from "@vendoai/store";
|
|
10
|
+
export { createStore, envSecrets, secretStore, storeSecrets } from "@vendoai/store";
|
|
11
|
+
export { hostAuthPresetConformance, type HostAuthPreset, type HostAuthPresetConformanceOptions, type HostAuthPresetOptions, type HostAuthPresetUser, type HostAuthPresetUserResolver, type SupabaseHostAuthPresetOptions, } from "./auth-presets/index.js";
|
|
12
|
+
import type { HostAuthPreset } from "./auth-presets/index.js";
|
|
13
|
+
import type { LanguageModel } from "ai";
|
|
14
|
+
export { devModel, type DevModelOptions } from "./dev-creds/model.js";
|
|
15
|
+
import { type ConnectionsService } from "./connections.js";
|
|
16
|
+
export { byoConnections, cloudConnections, unconfiguredConnections, type CloudConnectionsOptions, type ConnectionsService, } from "./connections.js";
|
|
17
|
+
export { cloudSandbox, type CloudSandboxOptions } from "./sandbox.js";
|
|
18
|
+
export { cloudTools, type CloudToolsOptions } from "./cloud-tools.js";
|
|
19
|
+
export { hostedStore, type HostedStore, type HostedStoreOptions } from "./hosted-store.js";
|
|
20
|
+
export interface Vendo {
|
|
21
|
+
handler: (req: Request) => Promise<Response>;
|
|
22
|
+
emit(event: string, payload: Json, principal: Principal): Promise<RunId[]>;
|
|
23
|
+
agent: VendoAgent;
|
|
24
|
+
guard: VendoGuard;
|
|
25
|
+
/** Existing-agents — the guard-bound registry with BYO approval parking:
|
|
26
|
+
the registry the `vendo_*` tool pack executes through. Same binding
|
|
27
|
+
chat, apps, and automations ride (no unguarded route); the one addition
|
|
28
|
+
is that a `pending-approval` outcome parks the exact call so the wire
|
|
29
|
+
resumes it on approve, discards it on deny, and expires it on the
|
|
30
|
+
parked-call TTL sweep. */
|
|
31
|
+
guardedTools: ToolRegistry;
|
|
32
|
+
apps: AppsRuntime;
|
|
33
|
+
automations: AutomationsEngine;
|
|
34
|
+
actions: ActionsRegistry;
|
|
35
|
+
connections: ConnectionsService;
|
|
36
|
+
store: VendoStore;
|
|
37
|
+
}
|
|
38
|
+
export interface CreateVendoConfig {
|
|
39
|
+
/** The agent's LLM — the inference adapter seam (03-agent §1): any ai-SDK
|
|
40
|
+
LanguageModel. Optional since install-dx v1: an explicitly passed model
|
|
41
|
+
always wins (BYO-LLM); when absent the seam resolves a real key from the
|
|
42
|
+
environment — provider keys via devModel's ladder, then VENDO_API_KEY →
|
|
43
|
+
Vendo Cloud managed inference — and fails honestly with instructions
|
|
44
|
+
when none exists (precedence: selectModel). */
|
|
45
|
+
model?: LanguageModel;
|
|
46
|
+
/** v2 spec §4 — tier-0 paint lane knob for app generation. `model` is the
|
|
47
|
+
no-think switch (a thinking-disabled model instance for the instant
|
|
48
|
+
paint); `disabled` forces single-lane generation. */
|
|
49
|
+
paint?: AppsConfig["paint"];
|
|
50
|
+
/** 09-vendo §2.1 — ONE host-identity preset filling the principal, actAs, and
|
|
51
|
+
oauth seams from one config key. Mutually exclusive with all three:
|
|
52
|
+
mixing throws VendoError("validation") at compose time. */
|
|
53
|
+
auth?: HostAuthPreset;
|
|
54
|
+
/** Per-seam escape hatch: host session → principal; null → the per-client
|
|
55
|
+
ephemeral anonymous principal. With neither `auth` nor `principal`, every
|
|
56
|
+
session is anonymous (the null path is the default resolver — 09 §2). */
|
|
57
|
+
principal?: (req: Request) => Promise<Principal | null>;
|
|
58
|
+
/** Host components available to generated apps: the name-keyed registry
|
|
59
|
+
object (01 §14 — the same object serves <VendoRoot>; the server ignores
|
|
60
|
+
each entry's `component` reference) or the array form. Entry names must
|
|
61
|
+
mirror the client-side components map 1:1. */
|
|
62
|
+
catalog?: ComponentCatalog | ComponentRegistry;
|
|
63
|
+
store?: VendoStore;
|
|
64
|
+
sandbox?: SandboxAdapter;
|
|
65
|
+
connectors?: Connector[];
|
|
66
|
+
/** 04-actions §3 — an explicit connections adapter; always wins over the
|
|
67
|
+
defaults (precedence: selectConnections). */
|
|
68
|
+
connections?: ConnectionsService;
|
|
69
|
+
actAs?: ActAs;
|
|
70
|
+
/** 04-actions §1 (ENG-248): the server-action registration map emitted by the
|
|
71
|
+
generated wiring file, keyed `"<module>#<exportName>"`. Server-action tools
|
|
72
|
+
dispatch in-process through it; a missing key fails closed at execution. */
|
|
73
|
+
serverActions?: Record<string, ServerActionHandler>;
|
|
74
|
+
policy?: PolicyConfig;
|
|
75
|
+
judge?: Judge;
|
|
76
|
+
secrets?: SecretsProvider;
|
|
77
|
+
telemetry?: boolean;
|
|
78
|
+
/** Development-only source capture. NODE_ENV=development enables this with
|
|
79
|
+
cwd/.vendo defaults; an explicit object supplies a host root for adapters
|
|
80
|
+
whose process cwd differs. `false` disables the environment default. */
|
|
81
|
+
development?: boolean | {
|
|
82
|
+
root?: string;
|
|
83
|
+
out?: string;
|
|
84
|
+
};
|
|
85
|
+
/** 10-mcp §1 — the one flag: open the MCP door so outside agents (Claude,
|
|
86
|
+
ChatGPT, Cursor) reach the host's tools through the SAME guard-bound path.
|
|
87
|
+
Opening it is a host decision (10-mcp §2), so it is off by default.
|
|
88
|
+
The additive object form opens the door with options: `baseUrl` is the
|
|
89
|
+
canonical PUBLIC base URL the door's discovery metadata, issuer, resource
|
|
90
|
+
identifiers, and RFC 8707 audience binding derive from — set it (or
|
|
91
|
+
`VENDO_BASE_URL`, the default) behind a reverse proxy, where the request
|
|
92
|
+
URL carries the proxy-internal origin. Forwarded headers are never
|
|
93
|
+
trusted. `remoteAs` (10-mcp §3.1) trusts an external authorization server
|
|
94
|
+
— e.g. the hosted broker at `{tenant}.mcp.vendo.run` — instead of serving
|
|
95
|
+
the door's local OAuth surface, and `federation` (10-mcp §3.2) answers
|
|
96
|
+
that server's signed login handshake at `{mount}/federate`. */
|
|
97
|
+
mcp?: boolean | {
|
|
98
|
+
baseUrl?: string;
|
|
99
|
+
remoteAs?: {
|
|
100
|
+
issuer: string;
|
|
101
|
+
jwksUri?: string;
|
|
102
|
+
audience: string;
|
|
103
|
+
};
|
|
104
|
+
federation?: {
|
|
105
|
+
secret: string;
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
/** 10-mcp §3 plus its additive prebuilt flow — the host's session + identity seam. Threaded top-level like
|
|
109
|
+
`actAs`/`principal` (the door is agnostic; the umbrella owns the shape).
|
|
110
|
+
REQUIRED when `mcp` is true: the door cannot mint principals without it. */
|
|
111
|
+
oauth?: HostOAuthAdapter;
|
|
112
|
+
/** 03-agent — chat context controls. All optional. `toolOutputCap` defaults to
|
|
113
|
+
DEFAULT_TOOL_OUTPUT_CAP so one huge host-tool response can't blow the context;
|
|
114
|
+
pass 0 to disable. `historyWindow` bounds messages re-sent per turn (default: full). */
|
|
115
|
+
agent?: {
|
|
116
|
+
toolOutputCap?: number;
|
|
117
|
+
maxOutputTokens?: number;
|
|
118
|
+
historyWindow?: number;
|
|
119
|
+
/** ENG-252 — cap on the uncurated initial tool loadout; the rest stay
|
|
120
|
+
discoverable via `vendo_tools_search`. Defaults to the agent block's
|
|
121
|
+
DEFAULT_MAX_INITIAL_TOOLS. */
|
|
122
|
+
maxInitialTools?: number;
|
|
123
|
+
/** ENG-252 — explicit curated initial loadout by tool name. When set,
|
|
124
|
+
exactly these host tools (that exist and are enabled) start active —
|
|
125
|
+
the cap is not applied; the rest stay discoverable via
|
|
126
|
+
`vendo_tools_search`. Vendo's own `vendo_*` tools are always active. */
|
|
127
|
+
loadout?: string[];
|
|
128
|
+
/** AGENT-7: agent-loop step cap per turn (default 20). Exhaustion streams a
|
|
129
|
+
renderable `data-vendo-step-limit` part instead of ending silently. */
|
|
130
|
+
maxSteps?: number;
|
|
131
|
+
};
|
|
132
|
+
/** 02-store §4 (kill-list B3) — ephemeral (anonymous) session lifecycle.
|
|
133
|
+
Anonymous visitors get a TTL-based session on disk: every request touches
|
|
134
|
+
it; an idle session is swept — its rows erased from the store and its
|
|
135
|
+
in-memory threads cascaded away. All optional.
|
|
136
|
+
- `ttlMs` idle timeout before a session is swept (default 30 min). `0`
|
|
137
|
+
disables TTL eviction.
|
|
138
|
+
- `sweepIntervalMs` how often the amortized on-request sweep and the
|
|
139
|
+
unref'd background timer run (default 60 s).
|
|
140
|
+
- `now` internal clock seam (tests only). */
|
|
141
|
+
sessions?: {
|
|
142
|
+
ttlMs?: number;
|
|
143
|
+
sweepIntervalMs?: number;
|
|
144
|
+
now?: () => number;
|
|
145
|
+
};
|
|
146
|
+
/** Existing-agents — approval lifecycle knobs.
|
|
147
|
+
- `parkedCallTtlMs` idle timeout for a guarded call parked from a BYO
|
|
148
|
+
agent loop (a `vendo/approval-ref@1` envelope with no Vendo thread to
|
|
149
|
+
resume through). Past it, the sweep denies the approval through the
|
|
150
|
+
existing abandonment semantics and `<VendoApprovalEmbed>` reads
|
|
151
|
+
"expired". Default 60 min; `0` disables expiry. Vendo-thread approvals
|
|
152
|
+
are untouched — their abandonment stays turn-driven (AGENT-6). */
|
|
153
|
+
approvals?: {
|
|
154
|
+
parkedCallTtlMs?: number;
|
|
155
|
+
};
|
|
156
|
+
/** execution-v2 Waves 4+9 — apps-block options. `experimentalMachines` is
|
|
157
|
+
the per-project layer-2 opt-in: NEW box graduation (the escalation
|
|
158
|
+
ladder's last rung) and machine provisioning refuse with a typed
|
|
159
|
+
VendoError naming this flag until the host enables it; steps/agentic
|
|
160
|
+
automations (the ladder's first two rungs) never need it, and apps that
|
|
161
|
+
already carry a machine keep every runtime path. `experimentalServedApps`
|
|
162
|
+
is the layer-3 opt-in on top: a machine may serve the app surface itself
|
|
163
|
+
(the host embeds its URL in a sandboxed iframe) — it REQUIRES
|
|
164
|
+
`experimentalMachines` (layer 3 is served by a layer-2 machine). OFF by
|
|
165
|
+
default — layer-3 generation, the 2→3 surface flip, and open() on a
|
|
166
|
+
served app all refuse with a typed VendoError naming the flag. */
|
|
167
|
+
apps?: {
|
|
168
|
+
experimentalServedApps?: boolean;
|
|
169
|
+
experimentalMachines?: boolean;
|
|
170
|
+
/** Host design rules for app generation (spec 2026-07-20): the same prose
|
|
171
|
+
`.vendo/design-rules.md` carries, for hosts that prefer programmatic
|
|
172
|
+
config. A non-blank string wins over the file and is fixed for the
|
|
173
|
+
instance lifetime; unset/blank falls through to a PER-GENERATION read
|
|
174
|
+
of the file, so editing it applies to the next create/edit without a
|
|
175
|
+
restart. */
|
|
176
|
+
designRules?: string;
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/** 09-vendo §2 — compose every live block around the guard choke point. */
|
|
180
|
+
export declare function createVendo(config: CreateVendoConfig): Vendo;
|
|
181
|
+
/** 09-vendo §2 — adapt the fetch handler to a Next.js catch-all route module.
|
|
182
|
+
PATCH stays exported even with no PATCH-only wire route left: Next.js
|
|
183
|
+
405s any method the module does not export before the request ever
|
|
184
|
+
reaches `vendo.handler`, so dropping it would turn e.g. `PATCH
|
|
185
|
+
/api/vendo/orgs/:id/members/:subject` into a framework 405 instead of
|
|
186
|
+
the wire's own `cloud-required` seam (the org routes matched ANY
|
|
187
|
+
method — orgsRoutes in wire/misc.ts). PUT carries the box callback
|
|
188
|
+
surface's durable-row writes (execution-v2 Lane C:
|
|
189
|
+
PUT /api/vendo/box/rows/:collection/:id). */
|
|
190
|
+
export declare function nextVendoHandler(vendo: Vendo): {
|
|
191
|
+
GET(request: Request): Promise<Response>;
|
|
192
|
+
POST(request: Request): Promise<Response>;
|
|
193
|
+
PUT(request: Request): Promise<Response>;
|
|
194
|
+
PATCH(request: Request): Promise<Response>;
|
|
195
|
+
DELETE(request: Request): Promise<Response>;
|
|
196
|
+
};
|
|
197
|
+
/** 10-mcp §5 — adapt the fetch handler to a Next.js `app/.well-known/[...vendo]/
|
|
198
|
+
route.ts` module. The four discovery documents the door serves (RFC 9728/
|
|
199
|
+
8414 metadata for its fixed mount, plus the SEP-2127 server card) live at
|
|
200
|
+
ORIGIN-ROOT paths, outside BASE_PATH — a host's `/api/vendo` catch-all route
|
|
201
|
+
never sees them, because Next.js dispatches by directory structure, not by
|
|
202
|
+
the wire's own routing. This file exists so that directory gets a handler
|
|
203
|
+
too, one that shares DOOR_WELL_KNOWN_PATHS with the wire itself (the SAME
|
|
204
|
+
set `isDoorPath` matches) instead of a hand-copied allowlist that can drift
|
|
205
|
+
from it. A request whose pathname is exactly one of those four paths
|
|
206
|
+
forwards to `vendo.handler` (which independently confirms it's a door path
|
|
207
|
+
and, if `mcp` is configured, serves it — the check here is only about
|
|
208
|
+
which requests reach the wire at all); anything else answers 404 with an
|
|
209
|
+
empty body, mirroring the hand-written route this replaces. With `mcp` left
|
|
210
|
+
unconfigured, `vendo.handler` still recognizes these four paths but has no
|
|
211
|
+
door to serve them, so the request falls through to the wire's ordinary
|
|
212
|
+
not-found response — never a 500. */
|
|
213
|
+
export declare function wellKnownVendoHandler(vendo: Vendo): {
|
|
214
|
+
GET(request: Request): Promise<Response>;
|
|
215
|
+
POST(request: Request): Promise<Response>;
|
|
216
|
+
};
|