@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,38 @@
|
|
|
1
|
+
import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
function pendingClaimPath(options = {}) {
|
|
5
|
+
return join(options.home ?? homedir(), ".vendo", "pending-claim.json");
|
|
6
|
+
}
|
|
7
|
+
function isPendingClaim(value) {
|
|
8
|
+
if (typeof value !== "object" || value === null)
|
|
9
|
+
return false;
|
|
10
|
+
const claim = value;
|
|
11
|
+
return typeof claim.claim_token === "string"
|
|
12
|
+
&& typeof claim.user_code === "string"
|
|
13
|
+
&& typeof claim.verification_uri_complete === "string"
|
|
14
|
+
&& typeof claim.expires_at === "number"
|
|
15
|
+
&& typeof claim.interval === "number"
|
|
16
|
+
&& typeof claim.api_url === "string"
|
|
17
|
+
&& typeof claim.cwd === "string";
|
|
18
|
+
}
|
|
19
|
+
/** An unreadable or malformed file reads as "no pending claim" — the caller
|
|
20
|
+
discards it by opening (and persisting) a fresh ceremony. */
|
|
21
|
+
export async function readPendingClaim(options = {}) {
|
|
22
|
+
try {
|
|
23
|
+
const value = JSON.parse(await readFile(pendingClaimPath(options), "utf8"));
|
|
24
|
+
return isPendingClaim(value) ? value : null;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export async function writePendingClaim(claim, options = {}) {
|
|
31
|
+
const path = pendingClaimPath(options);
|
|
32
|
+
await mkdir(join(path, ".."), { recursive: true, mode: 0o700 });
|
|
33
|
+
await writeFile(path, `${JSON.stringify(claim, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
34
|
+
await chmod(path, 0o600);
|
|
35
|
+
}
|
|
36
|
+
export async function deletePendingClaim(options = {}) {
|
|
37
|
+
await rm(pendingClaimPath(options), { force: true });
|
|
38
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { option } from "./args.js";
|
|
2
|
+
import { resolveProjectId, runCommand, userOptions, } from "./command.js";
|
|
3
|
+
export function runOrgs(args, options = {}) {
|
|
4
|
+
return runCommand(options, (context) => context.fetcher("/api/v1/orgs", userOptions(args, context)));
|
|
5
|
+
}
|
|
6
|
+
export function runUsage(args, options = {}) {
|
|
7
|
+
const days = option(args, "--days") ?? "30";
|
|
8
|
+
return runCommand(options, async (context) => {
|
|
9
|
+
const projectId = await resolveProjectId(args, context);
|
|
10
|
+
return context.fetcher(`/api/v1/projects/${encodeURIComponent(projectId)}/usage?days=${encodeURIComponent(days)}`, userOptions(args, context));
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface CloudSession {
|
|
2
|
+
access_token: string;
|
|
3
|
+
refresh_token?: string;
|
|
4
|
+
/** Supabase-compatible Unix timestamp in seconds. */
|
|
5
|
+
expires_at?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface SessionOptions {
|
|
8
|
+
home?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function readCloudSession(options?: SessionOptions): Promise<CloudSession | null>;
|
|
11
|
+
export declare function writeCloudSession(session: CloudSession, options?: SessionOptions): Promise<void>;
|
|
12
|
+
export declare function deleteCloudSession(options?: SessionOptions): Promise<void>;
|
|
13
|
+
export declare function isSessionExpired(session: CloudSession, now?: number): boolean;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
function cloudSessionPath(options = {}) {
|
|
5
|
+
return join(options.home ?? homedir(), ".vendo", "cloud-session.json");
|
|
6
|
+
}
|
|
7
|
+
function isCloudSession(value) {
|
|
8
|
+
if (typeof value !== "object" || value === null)
|
|
9
|
+
return false;
|
|
10
|
+
const session = value;
|
|
11
|
+
return typeof session.access_token === "string"
|
|
12
|
+
&& (session.refresh_token === undefined || typeof session.refresh_token === "string")
|
|
13
|
+
&& (session.expires_at === undefined || typeof session.expires_at === "number");
|
|
14
|
+
}
|
|
15
|
+
export async function readCloudSession(options = {}) {
|
|
16
|
+
try {
|
|
17
|
+
const value = JSON.parse(await readFile(cloudSessionPath(options), "utf8"));
|
|
18
|
+
if (!isCloudSession(value))
|
|
19
|
+
throw new Error("Invalid Vendo Cloud session file");
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (error.code === "ENOENT")
|
|
24
|
+
return null;
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export async function writeCloudSession(session, options = {}) {
|
|
29
|
+
const path = cloudSessionPath(options);
|
|
30
|
+
await mkdir(join(path, ".."), { recursive: true, mode: 0o700 });
|
|
31
|
+
await writeFile(path, `${JSON.stringify(session, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
32
|
+
await chmod(path, 0o600);
|
|
33
|
+
}
|
|
34
|
+
export async function deleteCloudSession(options = {}) {
|
|
35
|
+
await rm(cloudSessionPath(options), { force: true });
|
|
36
|
+
}
|
|
37
|
+
export function isSessionExpired(session, now = Date.now()) {
|
|
38
|
+
if (session.expires_at === undefined)
|
|
39
|
+
return false;
|
|
40
|
+
return session.expires_at * 1_000 <= now + 30_000;
|
|
41
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { DevCredential } from "../dev-creds/resolve.js";
|
|
2
|
+
import { type CloudDoctorResult } from "./doctor-live.js";
|
|
3
|
+
import { type Output, type TelemetryOptions } from "./shared.js";
|
|
4
|
+
/**
|
|
5
|
+
* ENG-339 (install-dx design §6) — cloud in init. Detect VENDO_API_KEY when
|
|
6
|
+
* present and check its shape locally (key problems surface on the first real
|
|
7
|
+
* service call), one calm line when absent, and — when a starter model key
|
|
8
|
+
* would actually help the ladder — offer `vendo login` inline: the auth.md
|
|
9
|
+
* user-claimed ceremony approves a code in the browser and lands the minted
|
|
10
|
+
* VENDO_API_KEY in .env.local, so the dev never pastes a key.
|
|
11
|
+
*/
|
|
12
|
+
/** Upsert one NAME=value line in .env.local without clobbering other lines.
|
|
13
|
+
Exported for init's --cloud-key flag, which lands a supplied key exactly
|
|
14
|
+
where the mint below would. */
|
|
15
|
+
export declare function upsertEnvLocal(root: string, name: string, value: string): Promise<void>;
|
|
16
|
+
/** The auth.md protocol file on Vendo Cloud (Agent Install DX, Layer 2). */
|
|
17
|
+
export declare const AUTH_MD_URL = "https://vendo.run/auth.md";
|
|
18
|
+
/** The agent-path key pointer: when an agent-driven init needs a Cloud key
|
|
19
|
+
and none exists, this block is the whole story — discovery URL, the CLI
|
|
20
|
+
command that runs the user-claimed ceremony, and both fallbacks (paste a
|
|
21
|
+
key with --cloud-key; stay keyless with --byo). Deterministic lines an
|
|
22
|
+
agent parses; exported so init's tail and the tests share one source. */
|
|
23
|
+
export declare function agentKeyPointerLines(): string[];
|
|
24
|
+
export interface CloudStepOptions {
|
|
25
|
+
root: string;
|
|
26
|
+
output: Output;
|
|
27
|
+
yes: boolean;
|
|
28
|
+
/** --byo: the explicit "no Cloud" answer — skip the offer AND the agent
|
|
29
|
+
pointer (bring-your-own stays first-class, no nudging past it). */
|
|
30
|
+
byo?: boolean;
|
|
31
|
+
/** TTY seam for the decline path (tests pin both sides). */
|
|
32
|
+
isTty?: boolean;
|
|
33
|
+
/** What the model ladder resolved — decides whether a starter key helps. */
|
|
34
|
+
credential: DevCredential;
|
|
35
|
+
env?: Record<string, string | undefined>;
|
|
36
|
+
apiUrl?: string;
|
|
37
|
+
/** Fetch seam for the default ceremony (tests script the console with it). */
|
|
38
|
+
fetchImpl?: typeof fetch;
|
|
39
|
+
/** Seams (tests). */
|
|
40
|
+
confirm?: (question: string, defaultYes?: boolean) => Promise<boolean>;
|
|
41
|
+
cloudProbe?: (options: {
|
|
42
|
+
env?: Record<string, string | undefined>;
|
|
43
|
+
}) => Promise<CloudDoctorResult>;
|
|
44
|
+
/** The whole ceremony in one seam (default: runDeviceLogin). */
|
|
45
|
+
deviceLogin?: () => Promise<number>;
|
|
46
|
+
sleep?: (ms: number) => Promise<void>;
|
|
47
|
+
/** Injectable telemetry deps (matches init/doctor). */
|
|
48
|
+
telemetry?: TelemetryOptions;
|
|
49
|
+
}
|
|
50
|
+
export interface CloudStepResult {
|
|
51
|
+
keyPresent: boolean;
|
|
52
|
+
keyValid: boolean;
|
|
53
|
+
wroteEnvLocal: boolean;
|
|
54
|
+
}
|
|
55
|
+
/** init's cloud step (design §6). Never changes init's exit code. Tracked as
|
|
56
|
+
`command_run` command "cloud-init" (TELEMETRY.md): ok is "the step ended
|
|
57
|
+
in a non-error outcome" — a valid key, a clean skip/decline, or a minted
|
|
58
|
+
starter key; failures name their step. Telemetry never changes the step's
|
|
59
|
+
behavior: the tracker is fully guarded and a thrown error still rethrows. */
|
|
60
|
+
export declare function runCloudStep(options: CloudStepOptions): Promise<CloudStepResult>;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { stdin, stdout } from "node:process";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { runDeviceLogin } from "./cloud/device-login.js";
|
|
4
|
+
import { cloudDoctor } from "./doctor-live.js";
|
|
5
|
+
import { askYesNo, cloudProjectProps, errorClass, readOptional, toolingTelemetry, writeText, } from "./shared.js";
|
|
6
|
+
/**
|
|
7
|
+
* ENG-339 (install-dx design §6) — cloud in init. Detect VENDO_API_KEY when
|
|
8
|
+
* present and check its shape locally (key problems surface on the first real
|
|
9
|
+
* service call), one calm line when absent, and — when a starter model key
|
|
10
|
+
* would actually help the ladder — offer `vendo login` inline: the auth.md
|
|
11
|
+
* user-claimed ceremony approves a code in the browser and lands the minted
|
|
12
|
+
* VENDO_API_KEY in .env.local, so the dev never pastes a key.
|
|
13
|
+
*/
|
|
14
|
+
/** Upsert one NAME=value line in .env.local without clobbering other lines.
|
|
15
|
+
Exported for init's --cloud-key flag, which lands a supplied key exactly
|
|
16
|
+
where the mint below would. */
|
|
17
|
+
export async function upsertEnvLocal(root, name, value) {
|
|
18
|
+
const path = join(root, ".env.local");
|
|
19
|
+
const current = await readOptional(path);
|
|
20
|
+
const line = `${name}=${value}`;
|
|
21
|
+
if (current === null || current.length === 0) {
|
|
22
|
+
await writeText(path, `${line}\n`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const pattern = new RegExp(`^\\s*${name}\\s*=.*$`, "m");
|
|
26
|
+
if (pattern.test(current)) {
|
|
27
|
+
await writeText(path, current.replace(pattern, line));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const separator = current.endsWith("\n") ? "" : "\n";
|
|
31
|
+
await writeText(path, `${current}${separator}${line}\n`);
|
|
32
|
+
}
|
|
33
|
+
/** The auth.md protocol file on Vendo Cloud (Agent Install DX, Layer 2). */
|
|
34
|
+
export const AUTH_MD_URL = "https://vendo.run/auth.md";
|
|
35
|
+
/** The agent-path key pointer: when an agent-driven init needs a Cloud key
|
|
36
|
+
and none exists, this block is the whole story — discovery URL, the CLI
|
|
37
|
+
command that runs the user-claimed ceremony, and both fallbacks (paste a
|
|
38
|
+
key with --cloud-key; stay keyless with --byo). Deterministic lines an
|
|
39
|
+
agent parses; exported so init's tail and the tests share one source. */
|
|
40
|
+
export function agentKeyPointerLines() {
|
|
41
|
+
return [
|
|
42
|
+
`Vendo Cloud key (agent path): fetch ${AUTH_MD_URL} and follow the user-claimed flow —`,
|
|
43
|
+
" 1. run `vendo login` — it prints a code your human approves in the browser",
|
|
44
|
+
" 2. the minted VENDO_API_KEY lands in .env.local automatically (never printed)",
|
|
45
|
+
" 3. re-run `vendo init` (it picks the key up from .env.local) or pass --cloud-key <key>",
|
|
46
|
+
"No Cloud account wanted? Re-run with --byo and set a provider key (ANTHROPIC_API_KEY / OPENAI_API_KEY / GOOGLE_GENERATIVE_AI_API_KEY).",
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
/** init's cloud step (design §6). Never changes init's exit code. Tracked as
|
|
50
|
+
`command_run` command "cloud-init" (TELEMETRY.md): ok is "the step ended
|
|
51
|
+
in a non-error outcome" — a valid key, a clean skip/decline, or a minted
|
|
52
|
+
starter key; failures name their step. Telemetry never changes the step's
|
|
53
|
+
behavior: the tracker is fully guarded and a thrown error still rethrows. */
|
|
54
|
+
export async function runCloudStep(options) {
|
|
55
|
+
const started = Date.now();
|
|
56
|
+
// The step's target root is the client's cwd: projectIdHash and the
|
|
57
|
+
// .env.local cloud-key read attribute to the project init runs against.
|
|
58
|
+
const telemetry = toolingTelemetry({ cwd: options.root, ...(options.telemetry ?? {}) });
|
|
59
|
+
const failure = {};
|
|
60
|
+
const track = async (thrown) => {
|
|
61
|
+
try {
|
|
62
|
+
await telemetry.track("command_run", {
|
|
63
|
+
command: "cloud-init",
|
|
64
|
+
ok: thrown === undefined && failure.failedStep === undefined,
|
|
65
|
+
durationMs: Date.now() - started,
|
|
66
|
+
...(failure.failedStep === undefined ? {} : { failedStep: failure.failedStep }),
|
|
67
|
+
...(thrown === undefined ? {} : { errorClass: errorClass(thrown.error) }),
|
|
68
|
+
...(await cloudProjectProps(options.root)),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Telemetry must never break init. Intentional silent failure.
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
try {
|
|
76
|
+
const result = await cloudStep(options, failure);
|
|
77
|
+
await track();
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
await track({ error });
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function cloudStep(options, failure) {
|
|
86
|
+
const { root, output, credential } = options;
|
|
87
|
+
const env = options.env ?? process.env;
|
|
88
|
+
const cloud = await (options.cloudProbe ?? cloudDoctor)({ env });
|
|
89
|
+
if (cloud.present && cloud.ok) {
|
|
90
|
+
output.log("\nVendo Cloud: VENDO_API_KEY present and well-formed.");
|
|
91
|
+
return { keyPresent: true, keyValid: true, wroteEnvLocal: false };
|
|
92
|
+
}
|
|
93
|
+
if (cloud.present) {
|
|
94
|
+
failure.failedStep = "key-invalid";
|
|
95
|
+
output.error(`\nVendo Cloud: VENDO_API_KEY is set but not usable (${cloud.error ?? "malformed"}). Fix or remove it; \`vendo login\` can issue a fresh one.`);
|
|
96
|
+
return { keyPresent: true, keyValid: false, wroteEnvLocal: false };
|
|
97
|
+
}
|
|
98
|
+
// Absent — one calm line stating what Cloud unlocks.
|
|
99
|
+
output.log(`\nVendo Cloud (optional): not configured. A key unlocks ${cloud.unlocks.join("; ")}.`);
|
|
100
|
+
// A starter model key only helps when the local ladder has nothing better.
|
|
101
|
+
const laddersWantKey = credential.rung === "none" || credential.rung === "vendo-cloud";
|
|
102
|
+
if (options.yes || options.byo === true || !laddersWantKey) {
|
|
103
|
+
if (laddersWantKey) {
|
|
104
|
+
if (options.byo === true) {
|
|
105
|
+
output.log("Run `vendo login` to claim a free dev-mode key; it lands in .env.local.");
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
// --yes / agent-driven runs get the full auth.md pointer: the agent
|
|
109
|
+
// can complete the whole key story in-band from these lines.
|
|
110
|
+
for (const line of agentKeyPointerLines())
|
|
111
|
+
output.log(line);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return { keyPresent: false, keyValid: false, wroteEnvLocal: false };
|
|
115
|
+
}
|
|
116
|
+
const tty = options.isTty ?? (stdin.isTTY === true && stdout.isTTY === true);
|
|
117
|
+
const confirm = options.confirm ?? askYesNo;
|
|
118
|
+
if (!(await confirm("Log in to Vendo Cloud now for a free dev-mode model key?", false))) {
|
|
119
|
+
if (tty) {
|
|
120
|
+
output.log("Skipped — run `vendo login` any time; the key lands in .env.local.");
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Nobody saw that prompt (non-TTY): this is an agent-driven run that
|
|
124
|
+
// reached here without --yes — point it at the auth.md flow instead.
|
|
125
|
+
for (const line of agentKeyPointerLines())
|
|
126
|
+
output.log(line);
|
|
127
|
+
}
|
|
128
|
+
return { keyPresent: false, keyValid: false, wroteEnvLocal: false };
|
|
129
|
+
}
|
|
130
|
+
// The `vendo login` ceremony end to end: approve a code in the browser
|
|
131
|
+
// (TTY opens it), and the minted key lands in .env.local — init picks it
|
|
132
|
+
// up in this same run, so the standalone re-run hint is suppressed.
|
|
133
|
+
const deviceLogin = options.deviceLogin ?? (() => runDeviceLogin(options.apiUrl === undefined ? [] : ["--api-url", options.apiUrl], {
|
|
134
|
+
output,
|
|
135
|
+
env,
|
|
136
|
+
root,
|
|
137
|
+
isTty: tty,
|
|
138
|
+
rerunHint: false,
|
|
139
|
+
...(options.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),
|
|
140
|
+
...(options.sleep === undefined ? {} : { sleep: options.sleep }),
|
|
141
|
+
}));
|
|
142
|
+
if ((await deviceLogin()) !== 0) {
|
|
143
|
+
failure.failedStep = "login";
|
|
144
|
+
output.error("Vendo Cloud login did not complete; run `vendo login` and re-run `vendo init`.");
|
|
145
|
+
return { keyPresent: false, keyValid: false, wroteEnvLocal: false };
|
|
146
|
+
}
|
|
147
|
+
output.log("Production always needs a real server-side key.");
|
|
148
|
+
return { keyPresent: true, keyValid: true, wroteEnvLocal: true };
|
|
149
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { HostFramework } from "./framework.js";
|
|
2
|
+
/**
|
|
3
|
+
* Bare dependency versions for telemetry (posthog-analytics design §3):
|
|
4
|
+
* version strings are non-identifying, in line with what Astro/Nx collect
|
|
5
|
+
* anonymously. Missing dependencies are omitted, never sent as placeholders.
|
|
6
|
+
*/
|
|
7
|
+
export interface DepVersions {
|
|
8
|
+
frameworkVersion?: string;
|
|
9
|
+
reactVersion?: string;
|
|
10
|
+
zodVersion?: string;
|
|
11
|
+
typescriptVersion?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The HOST's installed `ai` version (#478 short-term): @vendoai/vendo speaks
|
|
15
|
+
* AI SDK v6 to the host's `ai` package (peer `ai >=6 <7`), but npm installs
|
|
16
|
+
* the peer conflict anyway — so the version must be read from the TARGET
|
|
17
|
+
* dir's node_modules, never the CLI's own dependency tree. Non-throwing:
|
|
18
|
+
* an absent install or malformed metadata returns null (the missing-
|
|
19
|
+
* dependency story belongs to the wiring checks, not this reader).
|
|
20
|
+
*/
|
|
21
|
+
export declare function installedAiVersion(root: string): Promise<string | null>;
|
|
22
|
+
/**
|
|
23
|
+
* Read the host project's dependency versions (deps + devDeps) for telemetry.
|
|
24
|
+
* Non-throwing: a missing or malformed package.json returns {}.
|
|
25
|
+
*/
|
|
26
|
+
export declare function detectDepVersions(root: string, framework: HostFramework): Promise<DepVersions>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/** The npm package that IS each detected framework (detectFramework's own
|
|
4
|
+
evidence source, framework.ts). */
|
|
5
|
+
const FRAMEWORK_PACKAGE = {
|
|
6
|
+
next: "next",
|
|
7
|
+
express: "express",
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Normalize a semver range to its bare version: "^15.3.1" → "15.3.1",
|
|
11
|
+
* "~4.2" → "4.2", ">=3.0.0-beta.1" → "3.0.0-beta.1", "workspace:^0.3.0" →
|
|
12
|
+
* "0.3.0", "npm:foo@^2.1.0" → "2.1.0". The version must start the specifier
|
|
13
|
+
* (after a workspace:/catalog:/npm-alias prefix and range operator) — a digit
|
|
14
|
+
* run elsewhere is not a version, so named catalogs ("catalog:react19"),
|
|
15
|
+
* file:/link:/git:/URL specifiers (paths and ports carry digits), and
|
|
16
|
+
* versionless ranges ("*", "latest") all → undefined and the field is omitted.
|
|
17
|
+
*/
|
|
18
|
+
function bareVersion(range) {
|
|
19
|
+
if (typeof range !== "string")
|
|
20
|
+
return undefined;
|
|
21
|
+
let s = range.trim();
|
|
22
|
+
if (s.startsWith("npm:")) {
|
|
23
|
+
// npm alias: the range follows the LAST "@" ("npm:@scope/name@^2.1.0").
|
|
24
|
+
const at = s.lastIndexOf("@");
|
|
25
|
+
s = at > 4 ? s.slice(at + 1) : "";
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
s = s.replace(/^(?:workspace|catalog):/, "");
|
|
29
|
+
}
|
|
30
|
+
s = s.replace(/^(?:>=|<=|>|<|\^|~|=)?\s*/, "").replace(/^v(?=\d)/, "");
|
|
31
|
+
return /^\d+(?:\.\d+)*(?:[-+][0-9A-Za-z.-]+)?/.exec(s)?.[0];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The HOST's installed `ai` version (#478 short-term): @vendoai/vendo speaks
|
|
35
|
+
* AI SDK v6 to the host's `ai` package (peer `ai >=6 <7`), but npm installs
|
|
36
|
+
* the peer conflict anyway — so the version must be read from the TARGET
|
|
37
|
+
* dir's node_modules, never the CLI's own dependency tree. Non-throwing:
|
|
38
|
+
* an absent install or malformed metadata returns null (the missing-
|
|
39
|
+
* dependency story belongs to the wiring checks, not this reader).
|
|
40
|
+
*/
|
|
41
|
+
export async function installedAiVersion(root) {
|
|
42
|
+
try {
|
|
43
|
+
const manifest = JSON.parse(await readFile(join(root, "node_modules", "ai", "package.json"), "utf8"));
|
|
44
|
+
return typeof manifest.version === "string" ? manifest.version : null;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Read the host project's dependency versions (deps + devDeps) for telemetry.
|
|
52
|
+
* Non-throwing: a missing or malformed package.json returns {}.
|
|
53
|
+
*/
|
|
54
|
+
export async function detectDepVersions(root, framework) {
|
|
55
|
+
try {
|
|
56
|
+
const manifest = JSON.parse(await readFile(join(root, "package.json"), "utf8"));
|
|
57
|
+
const dependencies = { ...manifest.devDependencies, ...manifest.dependencies };
|
|
58
|
+
const out = {};
|
|
59
|
+
const frameworkPackage = framework === "unknown" ? undefined : FRAMEWORK_PACKAGE[framework];
|
|
60
|
+
const frameworkVersion = frameworkPackage === undefined ? undefined : bareVersion(dependencies[frameworkPackage]);
|
|
61
|
+
if (frameworkVersion !== undefined)
|
|
62
|
+
out.frameworkVersion = frameworkVersion;
|
|
63
|
+
const reactVersion = bareVersion(dependencies["react"]);
|
|
64
|
+
if (reactVersion !== undefined)
|
|
65
|
+
out.reactVersion = reactVersion;
|
|
66
|
+
const zodVersion = bareVersion(dependencies["zod"]);
|
|
67
|
+
if (zodVersion !== undefined)
|
|
68
|
+
out.zodVersion = zodVersion;
|
|
69
|
+
const typescriptVersion = bareVersion(dependencies["typescript"]);
|
|
70
|
+
if (typescriptVersion !== undefined)
|
|
71
|
+
out.typescriptVersion = typescriptVersion;
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return {};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-install DX (design 2026-07-19 §CLI-3) — the doctor error-code
|
|
3
|
+
* registry. Every failure mode doctor can report (static and live) has one
|
|
4
|
+
* stable, grep-able code here: E-<AREA>-<NNN>, where the area groups related
|
|
5
|
+
* checks (WIRE wiring, CFG config files, DEP host dependency versions, UI
|
|
6
|
+
* eject drift, DEV probe server, LIVE composition/status, AUTH credentials,
|
|
7
|
+
* MCP door, TURN model turn, CLOUD key). Codes are append-only: never renumber or reuse one — the
|
|
8
|
+
* verify page anchors (`fix_ref`) and agents' remediation notes depend on
|
|
9
|
+
* them staying put.
|
|
10
|
+
*
|
|
11
|
+
* This is the ONE module a CI check enumerates to assert every code has a
|
|
12
|
+
* matching verify-page anchor (no registry rot).
|
|
13
|
+
*/
|
|
14
|
+
export declare const DOCTOR_ERROR_CODES: {
|
|
15
|
+
readonly "E-WIRE-001": "Express server is not wired with createVendo from @vendoai/vendo/server";
|
|
16
|
+
readonly "E-WIRE-002": "Express client is not wrapped in <VendoRoot>";
|
|
17
|
+
readonly "E-WIRE-003": "the Next.js catch-all handler app/api/vendo/[...vendo]/route.ts is missing";
|
|
18
|
+
readonly "E-WIRE-004": "the Next.js root layout is not wrapped in <VendoRoot>";
|
|
19
|
+
readonly "E-WIRE-005": "the @vendoai/vendo (or vendoai alias) dependency is not declared";
|
|
20
|
+
readonly "E-CFG-001": "a required .vendo/ config file is missing";
|
|
21
|
+
readonly "E-CFG-002": ".vendo/data/.gitignore is missing";
|
|
22
|
+
readonly "E-DEP-001": "the installed ai package is a major version @vendoai/vendo does not support";
|
|
23
|
+
readonly "E-UI-001": "an ejected surface predates the installed @vendoai/ui";
|
|
24
|
+
readonly "E-DEV-001": "the dev server could not be started for the probe";
|
|
25
|
+
readonly "E-LIVE-001": "/status returned an invalid composition response";
|
|
26
|
+
readonly "E-LIVE-002": "/status is unreachable";
|
|
27
|
+
readonly "E-LIVE-003": "/status returned an invalid execution venue";
|
|
28
|
+
readonly "E-LIVE-004": "no execution venue is configured";
|
|
29
|
+
readonly "E-LIVE-005": "the host /status does not report an execution venue (version skew)";
|
|
30
|
+
readonly "E-AUTH-001": "present credentials did not reach the host API";
|
|
31
|
+
readonly "E-AUTH-002": "the present credential probe is unreachable";
|
|
32
|
+
readonly "E-AUTH-003": "the present credential probe cannot run while the dev server is down";
|
|
33
|
+
readonly "E-AUTH-004": "actAs mint + host verification failed";
|
|
34
|
+
readonly "E-AUTH-005": "the actAs probe is unreachable";
|
|
35
|
+
readonly "E-AUTH-006": "the actAs probe cannot run while the dev server is down";
|
|
36
|
+
readonly "E-AUTH-007": "actAs is not configured";
|
|
37
|
+
readonly "E-MCP-001": "MCP protected-resource metadata did not resolve";
|
|
38
|
+
readonly "E-MCP-002": "MCP authorization-server metadata did not resolve";
|
|
39
|
+
readonly "E-MCP-003": "the MCP server card did not parse";
|
|
40
|
+
readonly "E-MCP-004": "server.json does not meet MCP registry discovery requirements";
|
|
41
|
+
readonly "E-MCP-005": "the server.json remote does not match the live MCP door";
|
|
42
|
+
readonly "E-MCP-006": "server.json is invalid JSON";
|
|
43
|
+
readonly "E-MCP-007": "the local MCP registry auth challenge is malformed";
|
|
44
|
+
readonly "E-MCP-008": "the live MCP registry auth challenge is malformed";
|
|
45
|
+
readonly "E-SCHED-001": "apps declare vendo.json schedules but no schedule caller is configured";
|
|
46
|
+
readonly "E-TURN-001": "the live model turn did not answer";
|
|
47
|
+
readonly "E-TURN-002": "the live model turn cannot run while the dev server is down";
|
|
48
|
+
readonly "E-CLOUD-001": "VENDO_API_KEY is set but not usable";
|
|
49
|
+
};
|
|
50
|
+
export type DoctorErrorCode = keyof typeof DOCTOR_ERROR_CODES;
|
|
51
|
+
/** Complete list of every code doctor can emit, for CI enumeration. */
|
|
52
|
+
export declare const doctorErrorCodes: readonly DoctorErrorCode[];
|
|
53
|
+
/** The verify playbook page the fix_ref URLs anchor into. */
|
|
54
|
+
export declare const VERIFY_URL = "https://vendo.run/agents/verify";
|
|
55
|
+
/** Full fix URL for a code: the installed vendoai version rides as a query
|
|
56
|
+
* param BEFORE the fragment so the URL stays valid and the verify page can
|
|
57
|
+
* version-match its guidance. */
|
|
58
|
+
export declare function doctorFixRef(code: DoctorErrorCode, version?: string): string;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { CLI_VERSION } from "./shared.js";
|
|
2
|
+
/**
|
|
3
|
+
* Agent-install DX (design 2026-07-19 §CLI-3) — the doctor error-code
|
|
4
|
+
* registry. Every failure mode doctor can report (static and live) has one
|
|
5
|
+
* stable, grep-able code here: E-<AREA>-<NNN>, where the area groups related
|
|
6
|
+
* checks (WIRE wiring, CFG config files, DEP host dependency versions, UI
|
|
7
|
+
* eject drift, DEV probe server, LIVE composition/status, AUTH credentials,
|
|
8
|
+
* MCP door, TURN model turn, CLOUD key). Codes are append-only: never renumber or reuse one — the
|
|
9
|
+
* verify page anchors (`fix_ref`) and agents' remediation notes depend on
|
|
10
|
+
* them staying put.
|
|
11
|
+
*
|
|
12
|
+
* This is the ONE module a CI check enumerates to assert every code has a
|
|
13
|
+
* matching verify-page anchor (no registry rot).
|
|
14
|
+
*/
|
|
15
|
+
export const DOCTOR_ERROR_CODES = {
|
|
16
|
+
"E-WIRE-001": "Express server is not wired with createVendo from @vendoai/vendo/server",
|
|
17
|
+
"E-WIRE-002": "Express client is not wrapped in <VendoRoot>",
|
|
18
|
+
"E-WIRE-003": "the Next.js catch-all handler app/api/vendo/[...vendo]/route.ts is missing",
|
|
19
|
+
"E-WIRE-004": "the Next.js root layout is not wrapped in <VendoRoot>",
|
|
20
|
+
"E-WIRE-005": "the @vendoai/vendo (or vendoai alias) dependency is not declared",
|
|
21
|
+
"E-CFG-001": "a required .vendo/ config file is missing",
|
|
22
|
+
"E-CFG-002": ".vendo/data/.gitignore is missing",
|
|
23
|
+
"E-DEP-001": "the installed ai package is a major version @vendoai/vendo does not support",
|
|
24
|
+
"E-UI-001": "an ejected surface predates the installed @vendoai/ui",
|
|
25
|
+
"E-DEV-001": "the dev server could not be started for the probe",
|
|
26
|
+
"E-LIVE-001": "/status returned an invalid composition response",
|
|
27
|
+
"E-LIVE-002": "/status is unreachable",
|
|
28
|
+
"E-LIVE-003": "/status returned an invalid execution venue",
|
|
29
|
+
"E-LIVE-004": "no execution venue is configured",
|
|
30
|
+
"E-LIVE-005": "the host /status does not report an execution venue (version skew)",
|
|
31
|
+
"E-AUTH-001": "present credentials did not reach the host API",
|
|
32
|
+
"E-AUTH-002": "the present credential probe is unreachable",
|
|
33
|
+
"E-AUTH-003": "the present credential probe cannot run while the dev server is down",
|
|
34
|
+
"E-AUTH-004": "actAs mint + host verification failed",
|
|
35
|
+
"E-AUTH-005": "the actAs probe is unreachable",
|
|
36
|
+
"E-AUTH-006": "the actAs probe cannot run while the dev server is down",
|
|
37
|
+
"E-AUTH-007": "actAs is not configured",
|
|
38
|
+
"E-MCP-001": "MCP protected-resource metadata did not resolve",
|
|
39
|
+
"E-MCP-002": "MCP authorization-server metadata did not resolve",
|
|
40
|
+
"E-MCP-003": "the MCP server card did not parse",
|
|
41
|
+
"E-MCP-004": "server.json does not meet MCP registry discovery requirements",
|
|
42
|
+
"E-MCP-005": "the server.json remote does not match the live MCP door",
|
|
43
|
+
"E-MCP-006": "server.json is invalid JSON",
|
|
44
|
+
"E-MCP-007": "the local MCP registry auth challenge is malformed",
|
|
45
|
+
"E-MCP-008": "the live MCP registry auth challenge is malformed",
|
|
46
|
+
"E-SCHED-001": "apps declare vendo.json schedules but no schedule caller is configured",
|
|
47
|
+
"E-TURN-001": "the live model turn did not answer",
|
|
48
|
+
"E-TURN-002": "the live model turn cannot run while the dev server is down",
|
|
49
|
+
"E-CLOUD-001": "VENDO_API_KEY is set but not usable",
|
|
50
|
+
};
|
|
51
|
+
/** Complete list of every code doctor can emit, for CI enumeration. */
|
|
52
|
+
export const doctorErrorCodes = Object.keys(DOCTOR_ERROR_CODES);
|
|
53
|
+
/** The verify playbook page the fix_ref URLs anchor into. */
|
|
54
|
+
export const VERIFY_URL = "https://vendo.run/agents/verify";
|
|
55
|
+
/** Full fix URL for a code: the installed vendoai version rides as a query
|
|
56
|
+
* param BEFORE the fragment so the URL stays valid and the verify page can
|
|
57
|
+
* version-match its guidance. */
|
|
58
|
+
export function doctorFixRef(code, version = CLI_VERSION) {
|
|
59
|
+
return `${VERIFY_URL}?v=${encodeURIComponent(version)}#${code}`;
|
|
60
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type ChildProcess } from "node:child_process";
|
|
2
|
+
import { type DevCredential, type ResolveDevCredentialOptions } from "../dev-creds/resolve.js";
|
|
3
|
+
/**
|
|
4
|
+
* ENG-339 (install-dx design §5-6) — doctor's live surface: one real model
|
|
5
|
+
* turn through the wired HTTP route, a local VENDO_API_KEY shape check with
|
|
6
|
+
* what Cloud unlocks, and a consent-gated dev-server starter for the probe.
|
|
7
|
+
* All seam-driven so doctor stays testable without live keys or a running
|
|
8
|
+
* server.
|
|
9
|
+
*/
|
|
10
|
+
export interface LiveTurnResult {
|
|
11
|
+
attempted: boolean;
|
|
12
|
+
ok: boolean;
|
|
13
|
+
/** Which ladder rung the runtime resolved (doctor and runtime read the same
|
|
14
|
+
* resolver, so this is what the answering turn used). */
|
|
15
|
+
rung: DevCredential["rung"];
|
|
16
|
+
credential: string;
|
|
17
|
+
reply?: string;
|
|
18
|
+
error?: string;
|
|
19
|
+
elapsedMs: number;
|
|
20
|
+
}
|
|
21
|
+
export interface LiveTurnOptions {
|
|
22
|
+
/** Wire base, e.g. http://localhost:3000/api/vendo. */
|
|
23
|
+
base: string;
|
|
24
|
+
fetchImpl?: typeof fetch;
|
|
25
|
+
env?: Record<string, string | undefined>;
|
|
26
|
+
/** Stream the reply to the terminal as it arrives. */
|
|
27
|
+
onDelta?: (delta: string) => void;
|
|
28
|
+
timeoutMs?: number;
|
|
29
|
+
/** Test seam: skip the real resolver probe. */
|
|
30
|
+
resolveCredential?: (options: ResolveDevCredentialOptions) => Promise<DevCredential>;
|
|
31
|
+
}
|
|
32
|
+
/** POST one seeded turn to the live wire and stream the reply — mirrors the
|
|
33
|
+
* init finale's route (`POST {base}/threads`, UI-message SSE frames). Exit 0
|
|
34
|
+
* means a user would have gotten an answer: a non-empty text reply arrived. */
|
|
35
|
+
export declare function liveModelTurn(options: LiveTurnOptions): Promise<LiveTurnResult>;
|
|
36
|
+
/** Human-facing list of what a Cloud key unlocks over OSS single-player. Shown
|
|
37
|
+
* whether or not a key is present, so a keyless dev sees the offer. */
|
|
38
|
+
export declare const CLOUD_UNLOCKS: readonly string[];
|
|
39
|
+
export interface CloudDoctorResult {
|
|
40
|
+
present: boolean;
|
|
41
|
+
ok: boolean;
|
|
42
|
+
unlocks: readonly string[];
|
|
43
|
+
error?: string;
|
|
44
|
+
}
|
|
45
|
+
export interface CloudDoctorOptions {
|
|
46
|
+
env?: Record<string, string | undefined>;
|
|
47
|
+
}
|
|
48
|
+
/** Check VENDO_API_KEY presence and shape locally; always surface what Cloud
|
|
49
|
+
* unlocks. Key problems surface on the first real service call — there is no
|
|
50
|
+
* validate endpoint. */
|
|
51
|
+
export declare function cloudDoctor(options?: CloudDoctorOptions): Promise<CloudDoctorResult>;
|
|
52
|
+
export interface StartedDevServer {
|
|
53
|
+
ok: boolean;
|
|
54
|
+
stop: () => void;
|
|
55
|
+
log: string[];
|
|
56
|
+
}
|
|
57
|
+
export interface StartDevServerOptions {
|
|
58
|
+
root: string;
|
|
59
|
+
/** Wire base whose /status decides "up". */
|
|
60
|
+
statusUrl: string;
|
|
61
|
+
env?: Record<string, string | undefined>;
|
|
62
|
+
fetchImpl?: typeof fetch;
|
|
63
|
+
timeoutMs?: number;
|
|
64
|
+
spawnDev?: (packageManager: string, root: string) => ChildProcess;
|
|
65
|
+
}
|
|
66
|
+
/** Spawn `run dev` and wait for the wire to answer. Caller stop()s it. */
|
|
67
|
+
export declare function startDevServerForProbe(options: StartDevServerOptions): Promise<StartedDevServer>;
|