@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,159 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { resolveCloudBaseUrl } from "../cli/cloud/client.js";
|
|
5
|
+
import { describeDevCredential, resolveDevCredential, } from "./resolve.js";
|
|
6
|
+
const DEFAULT_MODELS = {
|
|
7
|
+
anthropic: {
|
|
8
|
+
module: "@ai-sdk/anthropic",
|
|
9
|
+
factory: "createAnthropic",
|
|
10
|
+
model: "claude-sonnet-4-6",
|
|
11
|
+
modelEnv: "VENDO_DEV_ANTHROPIC_MODEL",
|
|
12
|
+
install: "npm install ai@^6 @ai-sdk/anthropic@^3",
|
|
13
|
+
},
|
|
14
|
+
openai: {
|
|
15
|
+
module: "@ai-sdk/openai",
|
|
16
|
+
factory: "createOpenAI",
|
|
17
|
+
model: "gpt-5",
|
|
18
|
+
modelEnv: "VENDO_DEV_OPENAI_MODEL",
|
|
19
|
+
install: "npm install ai@^6 @ai-sdk/openai@^3",
|
|
20
|
+
},
|
|
21
|
+
google: {
|
|
22
|
+
module: "@ai-sdk/google",
|
|
23
|
+
factory: "createGoogleGenerativeAI",
|
|
24
|
+
model: "gemini-2.5-flash",
|
|
25
|
+
modelEnv: "VENDO_DEV_GOOGLE_MODEL",
|
|
26
|
+
install: "npm install ai@^6 @ai-sdk/google@^3",
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
/** The Cloud gateway serves curated model aliases, never raw provider ids:
|
|
30
|
+
* `vendo-default` (Sonnet), `vendo-fast` (Haiku), `vendo-strong` (Opus).
|
|
31
|
+
* VENDO_CLOUD_MODEL picks among them; the gateway remaps any other value to
|
|
32
|
+
* vendo-default (with an `x-vendo-model-remapped` warning header) during the
|
|
33
|
+
* grace window, and will hard-reject non-aliases after it. Same module/
|
|
34
|
+
* factory/install as anthropic — the gateway speaks the Anthropic Messages
|
|
35
|
+
* wire. */
|
|
36
|
+
const CLOUD_MODEL = {
|
|
37
|
+
module: "@ai-sdk/anthropic",
|
|
38
|
+
factory: "createAnthropic",
|
|
39
|
+
model: "vendo-default",
|
|
40
|
+
modelEnv: "VENDO_CLOUD_MODEL",
|
|
41
|
+
install: "npm install ai@^6 @ai-sdk/anthropic@^3",
|
|
42
|
+
};
|
|
43
|
+
export const NO_CREDENTIAL_MESSAGE = "Vendo found no model key. Set ANTHROPIC_API_KEY / OPENAI_API_KEY / GOOGLE_GENERATIVE_AI_API_KEY "
|
|
44
|
+
+ "in .env.local (with the matching @ai-sdk provider installed), or run `vendo login` for a "
|
|
45
|
+
+ "free dev key. Production always needs a real server-side key.";
|
|
46
|
+
/** Bundler-proof dynamic import: this module runs inside the host's dev server
|
|
47
|
+
* bundle (Next/webpack/turbopack), where a computed `import(...)` becomes a
|
|
48
|
+
* runtime stub throwing "expression is too dynamic". Native import first
|
|
49
|
+
* (plain Node, test VMs), Function-constructed import as the bundler-blind
|
|
50
|
+
* fallback. The Function body is a FIXED literal — the specifier is a
|
|
51
|
+
* parameter, never interpolated into code. */
|
|
52
|
+
async function dynamicImport(url) {
|
|
53
|
+
try {
|
|
54
|
+
return await import(url);
|
|
55
|
+
}
|
|
56
|
+
catch (nativeError) {
|
|
57
|
+
try {
|
|
58
|
+
const escaped = new Function("specifier", "return import(specifier)");
|
|
59
|
+
return await escaped(url);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
throw nativeError;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function importHostModule(root, specifier) {
|
|
67
|
+
const require = createRequire(join(root, "package.json"));
|
|
68
|
+
return await dynamicImport(pathToFileURL(require.resolve(specifier)).href);
|
|
69
|
+
}
|
|
70
|
+
export class DevModelController {
|
|
71
|
+
root;
|
|
72
|
+
env;
|
|
73
|
+
importModule;
|
|
74
|
+
resolution = null;
|
|
75
|
+
announced = false;
|
|
76
|
+
constructor(options = {}) {
|
|
77
|
+
this.root = options.root ?? process.cwd();
|
|
78
|
+
this.env = options.env ?? process.env;
|
|
79
|
+
this.importModule = options.importModule ?? importHostModule;
|
|
80
|
+
}
|
|
81
|
+
/** Resolve the credential once per process; state it on the server log once.
|
|
82
|
+
* An unavailable resolution logs its full instructions HERE — the wire
|
|
83
|
+
* deliberately shows clients only a generic error, so the operator's
|
|
84
|
+
* terminal is where the honest message must land. */
|
|
85
|
+
resolve() {
|
|
86
|
+
this.resolution ??= this.resolveOnce().then((resolution) => {
|
|
87
|
+
if (resolution.mode === "unavailable")
|
|
88
|
+
console.error(`[vendo] ${resolution.message}`);
|
|
89
|
+
return resolution;
|
|
90
|
+
});
|
|
91
|
+
return this.resolution;
|
|
92
|
+
}
|
|
93
|
+
announce(credential, suffix = "") {
|
|
94
|
+
if (this.announced)
|
|
95
|
+
return;
|
|
96
|
+
this.announced = true;
|
|
97
|
+
console.log(`[vendo] model: ${describeDevCredential(credential)}${suffix}`);
|
|
98
|
+
}
|
|
99
|
+
/** The shared delegate rung: load the provider module (an install failure
|
|
100
|
+
* resolves unavailable with the exact install command), pick the model id
|
|
101
|
+
* (env override or the spec default), and hand the factory-built model back. */
|
|
102
|
+
async delegate(credential, spec, keyName, config, announceSuffix) {
|
|
103
|
+
let loaded;
|
|
104
|
+
try {
|
|
105
|
+
loaded = await this.importModule(this.root, spec.module);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
const message = `${keyName} is set but ${spec.module} is not installed in this app; install it (\`${spec.install}\`).`;
|
|
109
|
+
this.announce(credential, ` — but ${spec.module} is missing`);
|
|
110
|
+
return { mode: "unavailable", credential, message };
|
|
111
|
+
}
|
|
112
|
+
const factory = loaded[spec.factory];
|
|
113
|
+
const modelId = this.env[spec.modelEnv] ?? spec.model;
|
|
114
|
+
const model = factory(config)(modelId);
|
|
115
|
+
this.announce(credential, ` → ${modelId}${announceSuffix}`);
|
|
116
|
+
return { mode: "delegate", credential, model };
|
|
117
|
+
}
|
|
118
|
+
async resolveOnce() {
|
|
119
|
+
const options = { env: this.env };
|
|
120
|
+
const credential = await resolveDevCredential(options);
|
|
121
|
+
if (credential.rung === "env-key") {
|
|
122
|
+
return this.delegate(credential, DEFAULT_MODELS[credential.provider], credential.envVar, { apiKey: this.env[credential.envVar] }, "");
|
|
123
|
+
}
|
|
124
|
+
if (credential.rung === "vendo-cloud") {
|
|
125
|
+
// The gateway speaks the Anthropic Messages wire, so the anthropic
|
|
126
|
+
// provider serves it — pointed at the console instead of Anthropic.
|
|
127
|
+
const base = resolveCloudBaseUrl({ env: this.env });
|
|
128
|
+
const baseURL = base.endsWith("/api/v1") ? base : `${base}/api/v1`;
|
|
129
|
+
return this.delegate(credential, CLOUD_MODEL, "VENDO_API_KEY", { apiKey: this.env["VENDO_API_KEY"], baseURL }, " via the Cloud gateway");
|
|
130
|
+
}
|
|
131
|
+
this.announce(credential);
|
|
132
|
+
return { mode: "unavailable", credential, message: NO_CREDENTIAL_MESSAGE };
|
|
133
|
+
}
|
|
134
|
+
async doGenerate(callOptions) {
|
|
135
|
+
const resolution = await this.resolve();
|
|
136
|
+
if (resolution.mode === "delegate")
|
|
137
|
+
return resolution.model.doGenerate(callOptions);
|
|
138
|
+
throw new Error(resolution.message);
|
|
139
|
+
}
|
|
140
|
+
async doStream(callOptions) {
|
|
141
|
+
const resolution = await this.resolve();
|
|
142
|
+
if (resolution.mode === "delegate")
|
|
143
|
+
return resolution.model.doStream(callOptions);
|
|
144
|
+
throw new Error(resolution.message);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** The env-resolving model (see module doc). */
|
|
148
|
+
export function devModel(options = {}) {
|
|
149
|
+
const controller = new DevModelController(options);
|
|
150
|
+
const model = {
|
|
151
|
+
specificationVersion: "v3",
|
|
152
|
+
provider: "vendo-dev",
|
|
153
|
+
modelId: "dev-env",
|
|
154
|
+
supportedUrls: {},
|
|
155
|
+
doGenerate: (callOptions) => controller.doGenerate(callOptions),
|
|
156
|
+
doStream: (callOptions) => controller.doStream(callOptions),
|
|
157
|
+
};
|
|
158
|
+
return model;
|
|
159
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dev-mode model-credential resolver (install-dx v1, re-derived 2026-07-18).
|
|
3
|
+
* Runtime model credentials are REAL KEYS ONLY — CLI-session rungs were removed
|
|
4
|
+
* (a coding-agent login helps at init time only, never serves product turns).
|
|
5
|
+
* Detection is PURE and read-only — no network, no writes, no key material in
|
|
6
|
+
* the result (consumers read the env variable themselves). Order (explicit
|
|
7
|
+
* beats implicit):
|
|
8
|
+
*
|
|
9
|
+
* 1. explicit env key (ANTHROPIC / OPENAI / GOOGLE)
|
|
10
|
+
* 2. VENDO_API_KEY (Vendo Cloud starter allowance / gateway)
|
|
11
|
+
* 3. none (honest failure with exact instructions)
|
|
12
|
+
*/
|
|
13
|
+
export type EnvKeyProvider = "anthropic" | "openai" | "google";
|
|
14
|
+
export type DevCredential = {
|
|
15
|
+
rung: "env-key";
|
|
16
|
+
provider: EnvKeyProvider;
|
|
17
|
+
envVar: string;
|
|
18
|
+
} | {
|
|
19
|
+
rung: "vendo-cloud";
|
|
20
|
+
} | {
|
|
21
|
+
rung: "none";
|
|
22
|
+
};
|
|
23
|
+
export declare const ENV_KEY_VARS: ReadonlyArray<{
|
|
24
|
+
envVar: string;
|
|
25
|
+
provider: EnvKeyProvider;
|
|
26
|
+
}>;
|
|
27
|
+
export interface ResolveDevCredentialOptions {
|
|
28
|
+
env?: Record<string, string | undefined>;
|
|
29
|
+
}
|
|
30
|
+
/** Detect the best available model credential. `VENDO_DEV_CREDENTIAL`
|
|
31
|
+
* (env-key:anthropic | vendo-cloud | none) pins the rung explicitly — used by
|
|
32
|
+
* E2E rung matrices and escape hatches. Async for seam stability (callers and
|
|
33
|
+
* test seams predate the session-rung removal). */
|
|
34
|
+
export declare function resolveDevCredential(options?: ResolveDevCredentialOptions): Promise<DevCredential>;
|
|
35
|
+
/** One human line for the wizard / doctor / runtime log. */
|
|
36
|
+
export declare function describeDevCredential(credential: DevCredential): string;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dev-mode model-credential resolver (install-dx v1, re-derived 2026-07-18).
|
|
3
|
+
* Runtime model credentials are REAL KEYS ONLY — CLI-session rungs were removed
|
|
4
|
+
* (a coding-agent login helps at init time only, never serves product turns).
|
|
5
|
+
* Detection is PURE and read-only — no network, no writes, no key material in
|
|
6
|
+
* the result (consumers read the env variable themselves). Order (explicit
|
|
7
|
+
* beats implicit):
|
|
8
|
+
*
|
|
9
|
+
* 1. explicit env key (ANTHROPIC / OPENAI / GOOGLE)
|
|
10
|
+
* 2. VENDO_API_KEY (Vendo Cloud starter allowance / gateway)
|
|
11
|
+
* 3. none (honest failure with exact instructions)
|
|
12
|
+
*/
|
|
13
|
+
export const ENV_KEY_VARS = [
|
|
14
|
+
{ envVar: "ANTHROPIC_API_KEY", provider: "anthropic" },
|
|
15
|
+
{ envVar: "OPENAI_API_KEY", provider: "openai" },
|
|
16
|
+
{ envVar: "GOOGLE_GENERATIVE_AI_API_KEY", provider: "google" },
|
|
17
|
+
];
|
|
18
|
+
function present(env, name) {
|
|
19
|
+
const value = env[name];
|
|
20
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
21
|
+
}
|
|
22
|
+
/** Detect the best available model credential. `VENDO_DEV_CREDENTIAL`
|
|
23
|
+
* (env-key:anthropic | vendo-cloud | none) pins the rung explicitly — used by
|
|
24
|
+
* E2E rung matrices and escape hatches. Async for seam stability (callers and
|
|
25
|
+
* test seams predate the session-rung removal). */
|
|
26
|
+
export async function resolveDevCredential(options = {}) {
|
|
27
|
+
const env = options.env ?? process.env;
|
|
28
|
+
const pinned = env["VENDO_DEV_CREDENTIAL"]?.trim();
|
|
29
|
+
if (pinned !== undefined && pinned.length > 0) {
|
|
30
|
+
if (pinned === "vendo-cloud" || pinned === "none")
|
|
31
|
+
return { rung: pinned };
|
|
32
|
+
const match = /^env-key:(anthropic|openai|google)$/.exec(pinned);
|
|
33
|
+
if (match !== null) {
|
|
34
|
+
const provider = match[1];
|
|
35
|
+
const envVar = ENV_KEY_VARS.find((entry) => entry.provider === provider).envVar;
|
|
36
|
+
return present(env, envVar) ? { rung: "env-key", provider, envVar } : { rung: "none" };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
for (const { envVar, provider } of ENV_KEY_VARS) {
|
|
40
|
+
if (present(env, envVar))
|
|
41
|
+
return { rung: "env-key", provider, envVar };
|
|
42
|
+
}
|
|
43
|
+
if (present(env, "VENDO_API_KEY"))
|
|
44
|
+
return { rung: "vendo-cloud" };
|
|
45
|
+
return { rung: "none" };
|
|
46
|
+
}
|
|
47
|
+
/** One human line for the wizard / doctor / runtime log. */
|
|
48
|
+
export function describeDevCredential(credential) {
|
|
49
|
+
switch (credential.rung) {
|
|
50
|
+
case "env-key":
|
|
51
|
+
return `explicit ${credential.envVar} (${credential.provider})`;
|
|
52
|
+
case "vendo-cloud":
|
|
53
|
+
return "VENDO_API_KEY (Vendo Cloud)";
|
|
54
|
+
case "none":
|
|
55
|
+
return "no model credential found";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type EraseReport, type SubjectMergeReport, type VendoStore } from "@vendoai/store";
|
|
2
|
+
export interface HostedStoreOptions {
|
|
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 hosted store handle: a plain StoreAdapter over the console wire, plus
|
|
11
|
+
* the erase door (02-store §5 — the console cascades exactly like eraseStore;
|
|
12
|
+
* the host-side TTL sweep is built on this call). `ensureSchema` is a client
|
|
13
|
+
* no-op (the service owns its migrations), `close` holds no local resources,
|
|
14
|
+
* and `raw()` fails loudly — there is no local database handle to hand out. */
|
|
15
|
+
export interface HostedStore extends VendoStore {
|
|
16
|
+
erase: {
|
|
17
|
+
bySubject(subject: string): Promise<EraseReport>;
|
|
18
|
+
byApp(appId: string): Promise<EraseReport>;
|
|
19
|
+
};
|
|
20
|
+
/** The ephemeral-session doors (02-store §4, hosted): registration == touch
|
|
21
|
+
* on every ephemeral request, adoption on sign-in, and the list/claim legs
|
|
22
|
+
* of the HOST-driven TTL sweep — the sweep claims a stale subject, then
|
|
23
|
+
* finishes through `erase.bySubject` (hosted-store one-pager). Millisecond
|
|
24
|
+
* clocks ride the wire so an injected session clock stays authoritative. */
|
|
25
|
+
sessions: {
|
|
26
|
+
register(subject: string, now?: number): Promise<void>;
|
|
27
|
+
adopt(from: string, to: string): Promise<SubjectMergeReport | null>;
|
|
28
|
+
stale(idleMs: number, now?: number): Promise<string[]>;
|
|
29
|
+
claim(subject: string, idleMs: number, now?: number): Promise<boolean>;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** vendo-web@7cd0a02 (2026-07-19) deleted the console's ephemeral-session op
|
|
33
|
+
* family (/api/v1/store/sessions/*) per spec — the removed routes answer
|
|
34
|
+
* Next.js's BARE 404 page, no error envelope. Typed so the composition layer
|
|
35
|
+
* (hostedSessionOps in server.ts) can disable the session doors gracefully
|
|
36
|
+
* instead of failing anonymous traffic; an ENVELOPED 404 is a live console
|
|
37
|
+
* answering "not-found" and keeps the loud path, same for every other
|
|
38
|
+
* failure. */
|
|
39
|
+
export declare class HostedSessionDoorsMissingError extends Error {
|
|
40
|
+
constructor();
|
|
41
|
+
}
|
|
42
|
+
/** The Cloud hosted-store adapter — the OSS side of the hosted-store seam
|
|
43
|
+
* (docs/superpowers/specs/2026-07-18-hosted-store-onepager.md): a plain
|
|
44
|
+
* StoreAdapter speaking RPC-over-HTTP to the console's /api/v1/store routes,
|
|
45
|
+
* method for method. Tenant = the key's org, resolved server-side on every
|
|
46
|
+
* call; reserved-collection semantics are enforced server-side by the same
|
|
47
|
+
* engine rules as packages/store's routing. Secrets are excluded by
|
|
48
|
+
* construction: the wire has no secrets surface, and storeSecrets/secretStore
|
|
49
|
+
* keep requiring the local store handle. Cloned from cloudSandbox's shape:
|
|
50
|
+
* behavior comes ONLY from constructor arguments (adapter rule — see
|
|
51
|
+
* selectStore in server.ts); the adapter never reads the environment. */
|
|
52
|
+
export declare function hostedStore(options: HostedStoreOptions): HostedStore;
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { vendoRecordSchema, } from "@vendoai/core";
|
|
2
|
+
import { DEDICATED_RECORD_COLLECTIONS, RESERVED_COLLECTIONS, } from "@vendoai/store";
|
|
3
|
+
import { consoleSender, raiseCloudError, toArrayBuffer } from "./cloud-console.js";
|
|
4
|
+
import { deploymentIdentityHeaders } from "./deployment-identity.js";
|
|
5
|
+
/** The console mounts the hosted-store surface here
|
|
6
|
+
* (apps/console/app/api/v1/store/*). */
|
|
7
|
+
const CONSOLE_STORE_PATH = "/api/v1/store";
|
|
8
|
+
/** Store calls are row/blob CRUD, not machine boots: generous enough for a
|
|
9
|
+
* large blob transfer on a slow link, small enough that a hung console
|
|
10
|
+
* request can't wedge a chat turn the way cloudSandbox's 300s budget would. */
|
|
11
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
12
|
+
/** Console garbage on a 2xx is the SERVICE misbehaving, never the caller's
|
|
13
|
+
* fault — same posture as cloudSandbox's malformed-200 defenses. No VendoError
|
|
14
|
+
* code fits "your storage backend answered nonsense", so this stays a plain
|
|
15
|
+
* Error: the wire layer logs it server-side instead of blaming the client. */
|
|
16
|
+
const invalidResponse = (what) => {
|
|
17
|
+
throw new Error(`Vendo Cloud store returned an ${what} response`);
|
|
18
|
+
};
|
|
19
|
+
/** The console's "unauthorized"/"quota-exhausted" have no VendoError twin;
|
|
20
|
+
* both ride the shared 402/401 → cloud-required mapping. Anything else
|
|
21
|
+
* (unknown codes, 5xx, non-JSON bodies) is carried on a plain Error with the
|
|
22
|
+
* server's code attached — the packages/apps cloud client's posture. */
|
|
23
|
+
const raiseStoreError = (response) => raiseCloudError(response, "store", (code, message) => {
|
|
24
|
+
throw Object.assign(new Error(message), { code: code ?? "unavailable" });
|
|
25
|
+
});
|
|
26
|
+
/** vendo-web@7cd0a02 (2026-07-19) deleted the console's ephemeral-session op
|
|
27
|
+
* family (/api/v1/store/sessions/*) per spec — the removed routes answer
|
|
28
|
+
* Next.js's BARE 404 page, no error envelope. Typed so the composition layer
|
|
29
|
+
* (hostedSessionOps in server.ts) can disable the session doors gracefully
|
|
30
|
+
* instead of failing anonymous traffic; an ENVELOPED 404 is a live console
|
|
31
|
+
* answering "not-found" and keeps the loud path, same for every other
|
|
32
|
+
* failure. */
|
|
33
|
+
export class HostedSessionDoorsMissingError extends Error {
|
|
34
|
+
constructor() {
|
|
35
|
+
super("Vendo Cloud console does not serve /api/v1/store/sessions/* (removed in vendo-web@7cd0a02)");
|
|
36
|
+
this.name = "HostedSessionDoorsMissingError";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** The session doors' raise: a bare 404 (no envelope) is the one
|
|
40
|
+
* removed-surface signal; everything else defers to the store mapping. */
|
|
41
|
+
const raiseSessionsError = async (response) => {
|
|
42
|
+
if (response.status === 404) {
|
|
43
|
+
let payload;
|
|
44
|
+
try {
|
|
45
|
+
payload = JSON.parse(await response.clone().text());
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
payload = undefined;
|
|
49
|
+
}
|
|
50
|
+
const enveloped = typeof payload === "object" && payload !== null && "error" in payload;
|
|
51
|
+
if (!enveloped)
|
|
52
|
+
throw new HostedSessionDoorsMissingError();
|
|
53
|
+
}
|
|
54
|
+
return raiseStoreError(response);
|
|
55
|
+
};
|
|
56
|
+
function parseRecord(value) {
|
|
57
|
+
const parsed = vendoRecordSchema.safeParse(value);
|
|
58
|
+
if (!parsed.success)
|
|
59
|
+
invalidResponse("invalid record");
|
|
60
|
+
return parsed.data;
|
|
61
|
+
}
|
|
62
|
+
function parseNullableRecord(value) {
|
|
63
|
+
if (value === null)
|
|
64
|
+
return null;
|
|
65
|
+
return parseRecord(value);
|
|
66
|
+
}
|
|
67
|
+
/** The Cloud hosted-store adapter — the OSS side of the hosted-store seam
|
|
68
|
+
* (docs/superpowers/specs/2026-07-18-hosted-store-onepager.md): a plain
|
|
69
|
+
* StoreAdapter speaking RPC-over-HTTP to the console's /api/v1/store routes,
|
|
70
|
+
* method for method. Tenant = the key's org, resolved server-side on every
|
|
71
|
+
* call; reserved-collection semantics are enforced server-side by the same
|
|
72
|
+
* engine rules as packages/store's routing. Secrets are excluded by
|
|
73
|
+
* construction: the wire has no secrets surface, and storeSecrets/secretStore
|
|
74
|
+
* keep requiring the local store handle. Cloned from cloudSandbox's shape:
|
|
75
|
+
* behavior comes ONLY from constructor arguments (adapter rule — see
|
|
76
|
+
* selectStore in server.ts); the adapter never reads the environment. */
|
|
77
|
+
export function hostedStore(options) {
|
|
78
|
+
const base = (options.baseUrl ?? "https://console.vendo.run").replace(/\/$/, "");
|
|
79
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
80
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
81
|
+
const send = consoleSender({
|
|
82
|
+
base,
|
|
83
|
+
mountPath: CONSOLE_STORE_PATH,
|
|
84
|
+
apiKey: options.apiKey,
|
|
85
|
+
timeoutMs,
|
|
86
|
+
fetchImpl,
|
|
87
|
+
raise: raiseStoreError,
|
|
88
|
+
});
|
|
89
|
+
// Same wire, sessions-only raise — the doors are the one surface the prod
|
|
90
|
+
// console may legitimately not serve (vendo-web@7cd0a02).
|
|
91
|
+
const sendSessions = consoleSender({
|
|
92
|
+
base,
|
|
93
|
+
mountPath: CONSOLE_STORE_PATH,
|
|
94
|
+
apiKey: options.apiKey,
|
|
95
|
+
timeoutMs,
|
|
96
|
+
fetchImpl,
|
|
97
|
+
raise: raiseSessionsError,
|
|
98
|
+
});
|
|
99
|
+
const postJson = (sender) => async (path, body) => {
|
|
100
|
+
const response = await sender(path, {
|
|
101
|
+
method: "POST",
|
|
102
|
+
headers: { "content-type": "application/json" },
|
|
103
|
+
body: JSON.stringify(body),
|
|
104
|
+
});
|
|
105
|
+
try {
|
|
106
|
+
return await response.json();
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return {};
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const sendJson = postJson(send);
|
|
113
|
+
const sendSessionsJson = postJson(sendSessions);
|
|
114
|
+
const records = (collection) => {
|
|
115
|
+
const prefix = `/records/${encodeURIComponent(collection)}`;
|
|
116
|
+
const store = {
|
|
117
|
+
async get(id) {
|
|
118
|
+
const payload = await sendJson(`${prefix}/get`, { id });
|
|
119
|
+
if (payload.record === undefined)
|
|
120
|
+
invalidResponse("invalid record");
|
|
121
|
+
return parseNullableRecord(payload.record);
|
|
122
|
+
},
|
|
123
|
+
async put(record) {
|
|
124
|
+
const payload = await sendJson(`${prefix}/put`, { record });
|
|
125
|
+
if (payload.record === undefined || payload.record === null)
|
|
126
|
+
invalidResponse("invalid record");
|
|
127
|
+
return parseRecord(payload.record);
|
|
128
|
+
},
|
|
129
|
+
async delete(id) {
|
|
130
|
+
await sendJson(`${prefix}/delete`, { id });
|
|
131
|
+
},
|
|
132
|
+
async list(query) {
|
|
133
|
+
const payload = await sendJson(`${prefix}/list`, { query: query ?? {} });
|
|
134
|
+
if (!Array.isArray(payload.records))
|
|
135
|
+
invalidResponse("invalid list");
|
|
136
|
+
return {
|
|
137
|
+
records: payload.records.map(parseRecord),
|
|
138
|
+
...(typeof payload.cursor === "string" ? { cursor: payload.cursor } : {}),
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
// Capability mirror of the store engine's routing (02-store §2): routed
|
|
143
|
+
// reserved collections expose no claim; atomic rides generic collections
|
|
144
|
+
// and vendo_threads' revision counter only. Mirroring the shape here keeps
|
|
145
|
+
// feature detection (`records.atomic !== undefined`) identical on both
|
|
146
|
+
// sides of the wire.
|
|
147
|
+
const reserved = RESERVED_COLLECTIONS.includes(collection);
|
|
148
|
+
const dedicated = DEDICATED_RECORD_COLLECTIONS.includes(collection);
|
|
149
|
+
if (!reserved) {
|
|
150
|
+
store.claim = async (expected, replacement) => {
|
|
151
|
+
const payload = await sendJson(`${prefix}/claim`, {
|
|
152
|
+
expected,
|
|
153
|
+
...(replacement === undefined ? {} : { replacement }),
|
|
154
|
+
});
|
|
155
|
+
if (typeof payload.claimed !== "boolean")
|
|
156
|
+
invalidResponse("invalid claim");
|
|
157
|
+
return payload.claimed;
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if ((!reserved && !dedicated) || collection === "vendo_threads") {
|
|
161
|
+
store.atomic = {
|
|
162
|
+
async insertIfAbsent(record) {
|
|
163
|
+
const payload = await sendJson(`${prefix}/atomic/insert-if-absent`, { record });
|
|
164
|
+
if (payload.record === undefined)
|
|
165
|
+
invalidResponse("invalid record");
|
|
166
|
+
return parseNullableRecord(payload.record);
|
|
167
|
+
},
|
|
168
|
+
async compareAndSwap(record, expectedRevision) {
|
|
169
|
+
const payload = await sendJson(`${prefix}/atomic/compare-and-swap`, {
|
|
170
|
+
record,
|
|
171
|
+
expectedRevision,
|
|
172
|
+
});
|
|
173
|
+
if (payload.record === undefined)
|
|
174
|
+
invalidResponse("invalid record");
|
|
175
|
+
return parseNullableRecord(payload.record);
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
return store;
|
|
180
|
+
};
|
|
181
|
+
const blobs = (namespace) => {
|
|
182
|
+
const prefix = `/blobs/${encodeURIComponent(namespace)}`;
|
|
183
|
+
// Blob keys are paths ("images/a.png"); encode per segment so the key's
|
|
184
|
+
// own separators survive as URL structure while each segment stays safe.
|
|
185
|
+
const keyPath = (key) => `${prefix}/${key.split("/").map(encodeURIComponent).join("/")}`;
|
|
186
|
+
return {
|
|
187
|
+
async put(key, bytes, meta) {
|
|
188
|
+
await send(keyPath(key), {
|
|
189
|
+
method: "PUT",
|
|
190
|
+
...(meta?.contentType === undefined ? {} : { headers: { "content-type": meta.contentType } }),
|
|
191
|
+
body: toArrayBuffer(bytes),
|
|
192
|
+
});
|
|
193
|
+
},
|
|
194
|
+
async get(key) {
|
|
195
|
+
const response = await fetchImpl(`${base}${CONSOLE_STORE_PATH}${keyPath(key)}`, {
|
|
196
|
+
headers: {
|
|
197
|
+
authorization: `Bearer ${options.apiKey}`,
|
|
198
|
+
...(await deploymentIdentityHeaders()),
|
|
199
|
+
},
|
|
200
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
201
|
+
});
|
|
202
|
+
// A missing blob is null at the seam (01-core §12) — but ONLY the
|
|
203
|
+
// console's enveloped not-found says "missing blob". A bare 404 (no
|
|
204
|
+
// envelope) is some other server answering — a misdeployed base URL
|
|
205
|
+
// must fail loudly, not read as an empty blob store forever.
|
|
206
|
+
if (response.status === 404) {
|
|
207
|
+
let payload;
|
|
208
|
+
try {
|
|
209
|
+
payload = JSON.parse(await response.text());
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
payload = undefined;
|
|
213
|
+
}
|
|
214
|
+
const code = typeof payload === "object" && payload !== null && "error" in payload
|
|
215
|
+
? payload.error?.code
|
|
216
|
+
: undefined;
|
|
217
|
+
if (code === "not-found")
|
|
218
|
+
return null;
|
|
219
|
+
throw new Error("Vendo Cloud store request failed with a bare 404 (no error envelope) — is the base URL a Vendo console?");
|
|
220
|
+
}
|
|
221
|
+
if (!response.ok)
|
|
222
|
+
await raiseStoreError(response);
|
|
223
|
+
const contentType = response.headers.get("content-type");
|
|
224
|
+
return {
|
|
225
|
+
bytes: new Uint8Array(await response.arrayBuffer()),
|
|
226
|
+
...(contentType === null ? {} : { contentType }),
|
|
227
|
+
};
|
|
228
|
+
},
|
|
229
|
+
async delete(key) {
|
|
230
|
+
await send(keyPath(key), { method: "DELETE" });
|
|
231
|
+
},
|
|
232
|
+
async list(prefixFilter) {
|
|
233
|
+
const query = prefixFilter === undefined || prefixFilter === ""
|
|
234
|
+
? ""
|
|
235
|
+
: `?prefix=${encodeURIComponent(prefixFilter)}`;
|
|
236
|
+
const response = await send(`${prefix}${query}`);
|
|
237
|
+
let payload;
|
|
238
|
+
try {
|
|
239
|
+
payload = await response.json();
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
payload = {};
|
|
243
|
+
}
|
|
244
|
+
if (!Array.isArray(payload.keys) || !payload.keys.every((key) => typeof key === "string")) {
|
|
245
|
+
invalidResponse("invalid blob list");
|
|
246
|
+
}
|
|
247
|
+
return payload.keys;
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
const parseReport = (payload) => {
|
|
252
|
+
const report = typeof payload === "object" && payload !== null && "report" in payload
|
|
253
|
+
? payload.report
|
|
254
|
+
: undefined;
|
|
255
|
+
if (typeof report !== "object" || report === null || Array.isArray(report)
|
|
256
|
+
|| !Object.values(report).every((count) => typeof count === "number")) {
|
|
257
|
+
invalidResponse("invalid erase");
|
|
258
|
+
}
|
|
259
|
+
return report;
|
|
260
|
+
};
|
|
261
|
+
const parseMergeReport = (payload) => {
|
|
262
|
+
const report = typeof payload === "object" && payload !== null && "report" in payload
|
|
263
|
+
? payload.report
|
|
264
|
+
: undefined;
|
|
265
|
+
if (report === null)
|
|
266
|
+
return null;
|
|
267
|
+
if (typeof report !== "object" || report === undefined || Array.isArray(report)
|
|
268
|
+
|| !Object.values(report).every((count) => typeof count === "number")) {
|
|
269
|
+
invalidResponse("invalid adopt");
|
|
270
|
+
}
|
|
271
|
+
return report;
|
|
272
|
+
};
|
|
273
|
+
return {
|
|
274
|
+
records,
|
|
275
|
+
blobs,
|
|
276
|
+
sessions: {
|
|
277
|
+
async register(subject, now) {
|
|
278
|
+
await sendSessionsJson("/sessions/register", { subject, ...(now === undefined ? {} : { now }) });
|
|
279
|
+
},
|
|
280
|
+
async adopt(from, to) {
|
|
281
|
+
return parseMergeReport(await sendSessionsJson("/sessions/adopt", { from, to }));
|
|
282
|
+
},
|
|
283
|
+
async stale(idleMs, now) {
|
|
284
|
+
const payload = await sendSessionsJson("/sessions/stale", {
|
|
285
|
+
idleMs,
|
|
286
|
+
...(now === undefined ? {} : { now }),
|
|
287
|
+
});
|
|
288
|
+
if (!Array.isArray(payload.subjects) || !payload.subjects.every((subject) => typeof subject === "string")) {
|
|
289
|
+
invalidResponse("invalid stale");
|
|
290
|
+
}
|
|
291
|
+
return payload.subjects;
|
|
292
|
+
},
|
|
293
|
+
async claim(subject, idleMs, now) {
|
|
294
|
+
const payload = await sendSessionsJson("/sessions/claim", {
|
|
295
|
+
subject,
|
|
296
|
+
idleMs,
|
|
297
|
+
...(now === undefined ? {} : { now }),
|
|
298
|
+
});
|
|
299
|
+
if (typeof payload.claimed !== "boolean")
|
|
300
|
+
invalidResponse("invalid claim");
|
|
301
|
+
return payload.claimed;
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
// 02-store §5 — the subject/app cascade runs server-side with eraseStore
|
|
305
|
+
// parity; the host-side ephemeral TTL sweep is built on bySubject.
|
|
306
|
+
erase: {
|
|
307
|
+
async bySubject(subject) {
|
|
308
|
+
return parseReport(await sendJson("/erase", { subject }));
|
|
309
|
+
},
|
|
310
|
+
async byApp(appId) {
|
|
311
|
+
return parseReport(await sendJson("/erase", { appId }));
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
// The service owns its migrations; there is nothing to migrate from here.
|
|
315
|
+
async ensureSchema() { },
|
|
316
|
+
// No local pool, no PGlite handle — nothing to release.
|
|
317
|
+
async close() { },
|
|
318
|
+
raw() {
|
|
319
|
+
throw new Error("[vendo] hostedStore has no local database handle — raw() requires a local createStore store");
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** @vendoai/vendo — root contract types (09-vendo §1). */
|
|
2
|
+
export type * from "@vendoai/core";
|
|
3
|
+
export type { VendoStore } from "@vendoai/store";
|
|
4
|
+
export type { Thread, ThreadSummary, VendoAgent } from "@vendoai/agent";
|
|
5
|
+
export type { ActionsRegistry, Connector, ConnectorAccount, ConnectorConnections, ExtractedTool, SyncReport, } from "@vendoai/actions";
|
|
6
|
+
export type { ConnectionsService, InitiatedConnection, InitiateOptions } from "./connections.js";
|
|
7
|
+
export type { Judge, PolicyConfig, PolicyFile, PolicyFn, PolicyRule, VendoGuard, } from "@vendoai/guard";
|
|
8
|
+
export type { AppsRuntime, EditResult, InClientApproval, InClientVerdict, OpenSurface, PinDrift, PinRebaseResult, SandboxAdapter, SandboxMachine, ShipDiff, VersionEntry, } from "@vendoai/apps";
|
|
9
|
+
export type { AutomationsEngine, RunPlan, RunRecord, RunStatus, } from "@vendoai/automations";
|
|
10
|
+
export type { VendoClient, VendoClientConfig } from "@vendoai/ui";
|
|
11
|
+
export type { HostOAuthAdapter } from "@vendoai/mcp";
|
|
12
|
+
export { remixable, type RemixableRegistration, type RemixableReportOptions } from "./remixable.js";
|
|
13
|
+
export type { ByoApprovalResolution } from "./byo-approvals.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { remixable } from "./remixable.js";
|