@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,28 @@
|
|
|
1
|
+
import { type ServerActionRegistration } from "@vendoai/actions";
|
|
2
|
+
import { type AuthMatch } from "./init-auth.js";
|
|
3
|
+
/** The wired preset line plus its escape-hatch comment. The lead-in stays
|
|
4
|
+
honest about how the preset got here: detection cites the found
|
|
5
|
+
dependency, a picker pick says "Selected". */
|
|
6
|
+
export declare function authConfigLines(auth: AuthMatch): string;
|
|
7
|
+
/** The empty shared registry (one file, two consumers): `createVendo` reads it
|
|
8
|
+
as `catalog` (data fields only), `<VendoRoot components={registry}>` reads
|
|
9
|
+
the component references. Generated only while absent — never clobbered. */
|
|
10
|
+
export declare function registrySource(variant: "tsx" | "mjs"): string;
|
|
11
|
+
export declare function routeSource(options: {
|
|
12
|
+
serverActions: boolean;
|
|
13
|
+
auth: AuthMatch | null;
|
|
14
|
+
registrySpecifier: string;
|
|
15
|
+
}): string;
|
|
16
|
+
/** Best-effort detection of the host's registrable server actions for the
|
|
17
|
+
* wiring map. Failure degrades to no map — sync reports extraction problems
|
|
18
|
+
* loudly, and runtime execution fails closed on the missing registration. */
|
|
19
|
+
export declare function wiringServerActions(root: string): Promise<ServerActionRegistration[]>;
|
|
20
|
+
/**
|
|
21
|
+
* The generated server-action registration map (04-actions §1, ENG-248): the
|
|
22
|
+
* wiring file imports each detected `"use server"` action module and passes
|
|
23
|
+
* the map into `createVendo({ serverActions })`. Deterministic content —
|
|
24
|
+
* sorted registrations, stable aliases — so re-init stays idempotent.
|
|
25
|
+
*/
|
|
26
|
+
export declare function serverActionsModuleSource(root: string, wiringDir: string, registrations: ServerActionRegistration[]): string;
|
|
27
|
+
export declare function expressServerSource(typescript: boolean, auth?: AuthMatch | null): string;
|
|
28
|
+
export declare const VENDO_ENV_EXAMPLE: string;
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { join, relative, sep } from "node:path";
|
|
2
|
+
import { extractServerActions, serverActionRegistrations, } from "@vendoai/actions";
|
|
3
|
+
import { AUTH_FAMILY_INFO, AUTH_PRESET_SPECIFIER } from "./init-auth.js";
|
|
4
|
+
/** The wired preset line plus its escape-hatch comment. The lead-in stays
|
|
5
|
+
honest about how the preset got here: detection cites the found
|
|
6
|
+
dependency, a picker pick says "Selected". */
|
|
7
|
+
export function authConfigLines(auth) {
|
|
8
|
+
const origin = auth.source === "picked"
|
|
9
|
+
? `Selected ${AUTH_FAMILY_INFO[auth.preset].name}`
|
|
10
|
+
: `Detected ${auth.dependency}`;
|
|
11
|
+
return ` // ${origin} — ${auth.preset}() fills the identity seams\n` +
|
|
12
|
+
` // (request→user, actAs, door OAuth); options and the per-seam escape\n` +
|
|
13
|
+
` // hatch: docs/act-as-presets.md.\n` +
|
|
14
|
+
` auth: ${auth.preset}(),\n`;
|
|
15
|
+
}
|
|
16
|
+
/** The empty shared registry (one file, two consumers): `createVendo` reads it
|
|
17
|
+
as `catalog` (data fields only), `<VendoRoot components={registry}>` reads
|
|
18
|
+
the component references. Generated only while absent — never clobbered. */
|
|
19
|
+
export function registrySource(variant) {
|
|
20
|
+
const header = `/**\n` +
|
|
21
|
+
` * The Vendo component registry — generated empty by \`vendo init\`, then yours.\n` +
|
|
22
|
+
` * One file, two consumers: \`createVendo\` takes this object as \`catalog\` and\n` +
|
|
23
|
+
` * reads only the data fields (description, props, examples); <VendoRoot\n` +
|
|
24
|
+
` * components={registry}> takes the same object and reads only the component\n` +
|
|
25
|
+
` * references. There is no second map to keep in sync.\n` +
|
|
26
|
+
` *\n` +
|
|
27
|
+
` * Add entries keyed by component name, e.g.:\n` +
|
|
28
|
+
` *\n` +
|
|
29
|
+
` * SpendingDonut: {\n` +
|
|
30
|
+
` * component: SpendingDonut,\n` +
|
|
31
|
+
` * description: "Spending by category. Use for where-did-my-money-go requests.",\n` +
|
|
32
|
+
` * props: z.object({\n` +
|
|
33
|
+
` * slices: z.array(z.object({ category: z.string(), amount: z.number() })),\n` +
|
|
34
|
+
` * }),\n` +
|
|
35
|
+
` * examples: ['{"slices":[{"category":"dining","amount":342.18}]}'],\n` +
|
|
36
|
+
` * },\n` +
|
|
37
|
+
` *\n` +
|
|
38
|
+
` * (\`props\` is an optional zod schema; a schema-less entry is legal.)\n` +
|
|
39
|
+
` */\n`;
|
|
40
|
+
// The type comes from @vendoai/vendo, not @vendoai/core: a host only gets
|
|
41
|
+
// @vendoai/vendo (and @vendoai/ui) as a direct dependency, so under pnpm
|
|
42
|
+
// strict linking @vendoai/core (transitive) doesn't resolve for the host
|
|
43
|
+
// (TS2307). @vendoai/vendo's root entry already re-exports the full
|
|
44
|
+
// @vendoai/core type surface (index.ts: `export type * from "@vendoai/core"`),
|
|
45
|
+
// and the bare root — not /server or /react — is the neutral entry for a
|
|
46
|
+
// type this file needs in both the server route and the client wrap.
|
|
47
|
+
return variant === "tsx"
|
|
48
|
+
? `${header}import type { ComponentRegistry } from "@vendoai/vendo";\n\nexport const registry = {} satisfies ComponentRegistry;\n`
|
|
49
|
+
: `${header}export const registry = {};\n`;
|
|
50
|
+
}
|
|
51
|
+
/** The preset's own import line (its own subpath, never "@vendoai/vendo/server"
|
|
52
|
+
— corpus-triage Task 9: a shared barrel meant any host importing the
|
|
53
|
+
server entry statically re-resolved every preset's optional peer dep,
|
|
54
|
+
even unused ones), or empty when no preset was wired. */
|
|
55
|
+
function authImportLine(auth) {
|
|
56
|
+
return auth === null ? "" : `import { ${auth.preset} } from ${JSON.stringify(AUTH_PRESET_SPECIFIER[auth.preset])};\n`;
|
|
57
|
+
}
|
|
58
|
+
export function routeSource(options) {
|
|
59
|
+
return authImportLine(options.auth) +
|
|
60
|
+
`import { createVendo, nextVendoHandler } from "@vendoai/vendo/server";\n` +
|
|
61
|
+
(options.serverActions ? `import { serverActions } from "./vendo-actions";\n` : "") +
|
|
62
|
+
`import { registry } from ${JSON.stringify(options.registrySpecifier)};\n` +
|
|
63
|
+
`\nconst vendo = createVendo({\n` +
|
|
64
|
+
(options.auth === null ? ` principal: async () => null,\n` : authConfigLines(options.auth)) +
|
|
65
|
+
` catalog: registry,\n` +
|
|
66
|
+
(options.serverActions ? ` serverActions,\n` : "") +
|
|
67
|
+
` policy: {}, // .vendo/policy.json: destructive asks, reads run\n` +
|
|
68
|
+
`});\n\n` +
|
|
69
|
+
`export const { GET, POST, PUT, PATCH, DELETE } = nextVendoHandler(vendo);\n`;
|
|
70
|
+
}
|
|
71
|
+
/** Best-effort detection of the host's registrable server actions for the
|
|
72
|
+
* wiring map. Failure degrades to no map — sync reports extraction problems
|
|
73
|
+
* loudly, and runtime execution fails closed on the missing registration. */
|
|
74
|
+
export async function wiringServerActions(root) {
|
|
75
|
+
try {
|
|
76
|
+
const { tools } = await extractServerActions(root);
|
|
77
|
+
return serverActionRegistrations(tools);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The generated server-action registration map (04-actions §1, ENG-248): the
|
|
85
|
+
* wiring file imports each detected `"use server"` action module and passes
|
|
86
|
+
* the map into `createVendo({ serverActions })`. Deterministic content —
|
|
87
|
+
* sorted registrations, stable aliases — so re-init stays idempotent.
|
|
88
|
+
*/
|
|
89
|
+
export function serverActionsModuleSource(root, wiringDir, registrations) {
|
|
90
|
+
const header = `/**\n` +
|
|
91
|
+
` * Server-action registration map — generated by \`vendo init\`; re-run init\n` +
|
|
92
|
+
` * when the "use server" surface changes. createVendo dispatches\n` +
|
|
93
|
+
` * server-action tools in-process through this map; an action missing here\n` +
|
|
94
|
+
` * fails closed at execution time (no work performed).\n` +
|
|
95
|
+
` */\n`;
|
|
96
|
+
if (registrations.length === 0)
|
|
97
|
+
return `${header}export const serverActions = {};\n`;
|
|
98
|
+
const imports = [];
|
|
99
|
+
const entries = [];
|
|
100
|
+
registrations.forEach((registration, index) => {
|
|
101
|
+
const alias = `action${index}`;
|
|
102
|
+
const target = relative(wiringDir, join(root, registration.module))
|
|
103
|
+
.split(sep).join("/")
|
|
104
|
+
.replace(/\.(?:tsx|ts|jsx|js)$/, "");
|
|
105
|
+
const specifier = target.startsWith(".") ? target : `./${target}`;
|
|
106
|
+
imports.push(registration.exportName === "default"
|
|
107
|
+
? `import ${alias} from ${JSON.stringify(specifier)};`
|
|
108
|
+
: `import { ${registration.exportName} as ${alias} } from ${JSON.stringify(specifier)};`);
|
|
109
|
+
entries.push(` ${JSON.stringify(`${registration.module}#${registration.exportName}`)}: ${alias},`);
|
|
110
|
+
});
|
|
111
|
+
return `${header}${imports.join("\n")}\n\n` +
|
|
112
|
+
`export const serverActions = {\n${entries.join("\n")}\n};\n`;
|
|
113
|
+
}
|
|
114
|
+
export function expressServerSource(typescript, auth = null) {
|
|
115
|
+
const imports = typescript
|
|
116
|
+
? `import { once } from "node:events";\n` +
|
|
117
|
+
`import type { IncomingHttpHeaders, IncomingMessage, ServerResponse } from "node:http";\n` +
|
|
118
|
+
`import { Readable } from "node:stream";\n`
|
|
119
|
+
: `import { once } from "node:events";\n` +
|
|
120
|
+
`import { Readable } from "node:stream";\n`;
|
|
121
|
+
const types = typescript
|
|
122
|
+
? `\ntype ExpressRequest = IncomingMessage & { originalUrl?: string };\n` +
|
|
123
|
+
`type ExpressNext = (error?: unknown) => void;\n`
|
|
124
|
+
: "";
|
|
125
|
+
const signatures = typescript
|
|
126
|
+
? {
|
|
127
|
+
requestHeaders: `(headers: IncomingHttpHeaders): Headers`,
|
|
128
|
+
absoluteUrl: `(request: ExpressRequest): string`,
|
|
129
|
+
sendResponse: `(source: Response, target: ServerResponse): Promise<void>`,
|
|
130
|
+
handle: `(request: ExpressRequest, response: ServerResponse): Promise<void>`,
|
|
131
|
+
mountReturn: `: (request: ExpressRequest, response: ServerResponse, next: ExpressNext) => void`,
|
|
132
|
+
}
|
|
133
|
+
: { requestHeaders: "(headers)", absoluteUrl: "(request)", sendResponse: "(source, target)", handle: "(request, response)", mountReturn: "" };
|
|
134
|
+
const requestInit = typescript
|
|
135
|
+
? ` const init: RequestInit & { duplex?: "half" } = { method, headers: requestHeaders(request.headers) };\n`
|
|
136
|
+
: ` const init = { method, headers: requestHeaders(request.headers) };\n`;
|
|
137
|
+
const body = typescript
|
|
138
|
+
? ` init.body = Readable.toWeb(request) as ReadableStream<Uint8Array>;\n`
|
|
139
|
+
: ` init.body = Readable.toWeb(request);\n`;
|
|
140
|
+
// The client-entry hint mirrors the host's language: the TS variant needs the
|
|
141
|
+
// VendoTheme cast (JSON-module literals widen to string), the JS variant must
|
|
142
|
+
// not show type-only syntax a JavaScript host cannot paste.
|
|
143
|
+
const registrySpecifier = typescript ? "./registry" : "./registry.mjs";
|
|
144
|
+
const clientHint = typescript
|
|
145
|
+
? ` * // in the client entry — theme.json adopts the host brand (08 §4);\n` +
|
|
146
|
+
` * // the cast narrows TypeScript's widened JSON-module string literals:\n` +
|
|
147
|
+
` * import { VendoRoot } from "@vendoai/vendo/react";\n` +
|
|
148
|
+
` * import { registry } from "<path-to>/vendo/registry";\n` +
|
|
149
|
+
` * import theme from "<path-to>/.vendo/theme.json";\n` +
|
|
150
|
+
` * import type { VendoTheme } from "@vendoai/vendo";\n` +
|
|
151
|
+
` * root.render(<VendoRoot components={registry} theme={theme as VendoTheme}><App /></VendoRoot>);\n`
|
|
152
|
+
: ` * // in the client entry — theme.json adopts the host brand (08 §4):\n` +
|
|
153
|
+
` * import { VendoRoot } from "@vendoai/vendo/react";\n` +
|
|
154
|
+
` * import { registry } from "<path-to>/vendo/registry.mjs";\n` +
|
|
155
|
+
` * import theme from "<path-to>/.vendo/theme.json";\n` +
|
|
156
|
+
` * root.render(<VendoRoot components={registry} theme={theme}><App /></VendoRoot>);\n`;
|
|
157
|
+
return `/**\n` +
|
|
158
|
+
` * Add these wiring lines in your host:\n` +
|
|
159
|
+
` * app.use("/api/vendo", mountVendo());\n` +
|
|
160
|
+
clientHint +
|
|
161
|
+
` */\n` +
|
|
162
|
+
imports +
|
|
163
|
+
authImportLine(auth) +
|
|
164
|
+
`import { createVendo } from "@vendoai/vendo/server";\n` +
|
|
165
|
+
`import { registry } from ${JSON.stringify(registrySpecifier)};\n` +
|
|
166
|
+
types +
|
|
167
|
+
`\nconst vendo = createVendo({\n` +
|
|
168
|
+
(auth === null ? ` principal: async () => null,\n` : authConfigLines(auth)) +
|
|
169
|
+
` catalog: registry,\n` +
|
|
170
|
+
` policy: {}, // .vendo/policy.json: destructive asks, reads run\n` +
|
|
171
|
+
`});\n\n` +
|
|
172
|
+
`function requestHeaders${signatures.requestHeaders} {\n` +
|
|
173
|
+
` const result = new Headers();\n` +
|
|
174
|
+
` for (const [name, value] of Object.entries(headers)) {\n` +
|
|
175
|
+
` if (Array.isArray(value)) for (const item of value) result.append(name, item);\n` +
|
|
176
|
+
` else if (value !== undefined) result.set(name, value);\n` +
|
|
177
|
+
` }\n` +
|
|
178
|
+
` return result;\n` +
|
|
179
|
+
`}\n\n` +
|
|
180
|
+
`function absoluteUrl${signatures.absoluteUrl} {\n` +
|
|
181
|
+
` const encrypted = "encrypted" in request.socket && request.socket.encrypted === true;\n` +
|
|
182
|
+
` const protocol = encrypted ? "https" : "http";\n` +
|
|
183
|
+
` const host = request.headers.host ?? "localhost";\n` +
|
|
184
|
+
` // Behind a trusted proxy, set VENDO_BASE_URL explicitly or validate forwarded headers in the host.\n` +
|
|
185
|
+
` return new URL(request.originalUrl ?? request.url ?? "/", \`${"${protocol}"}://${"${host}"}\`).href;\n` +
|
|
186
|
+
`}\n\n` +
|
|
187
|
+
`async function sendResponse${signatures.sendResponse} {\n` +
|
|
188
|
+
` target.statusCode = source.status;\n` +
|
|
189
|
+
` source.headers.forEach((value, name) => {\n` +
|
|
190
|
+
` if (name.toLowerCase() !== "set-cookie") target.setHeader(name, value);\n` +
|
|
191
|
+
` });\n` +
|
|
192
|
+
` const getSetCookie = (source.headers as Headers & { getSetCookie?: () => string[] }).getSetCookie;\n` +
|
|
193
|
+
` const fallbackCookie = source.headers.get("set-cookie");\n` +
|
|
194
|
+
` const cookies = typeof getSetCookie === "function"\n` +
|
|
195
|
+
` ? getSetCookie.call(source.headers)\n` +
|
|
196
|
+
` : fallbackCookie === null ? [] : [fallbackCookie];\n` +
|
|
197
|
+
` if (cookies.length > 0) target.setHeader("set-cookie", cookies);\n` +
|
|
198
|
+
` if (source.body === null) {\n` +
|
|
199
|
+
` target.end();\n` +
|
|
200
|
+
` return;\n` +
|
|
201
|
+
` }\n` +
|
|
202
|
+
` target.flushHeaders();\n` +
|
|
203
|
+
` const reader = source.body.getReader();\n` +
|
|
204
|
+
` try {\n` +
|
|
205
|
+
` while (true) {\n` +
|
|
206
|
+
` const chunk = await reader.read();\n` +
|
|
207
|
+
` if (chunk.done) break;\n` +
|
|
208
|
+
` if (!target.write(chunk.value)) await once(target, "drain");\n` +
|
|
209
|
+
` }\n` +
|
|
210
|
+
` target.end();\n` +
|
|
211
|
+
` } finally {\n` +
|
|
212
|
+
` reader.releaseLock();\n` +
|
|
213
|
+
` }\n` +
|
|
214
|
+
`}\n\n` +
|
|
215
|
+
`async function handle${signatures.handle} {\n` +
|
|
216
|
+
` const method = request.method ?? "GET";\n` +
|
|
217
|
+
requestInit +
|
|
218
|
+
` if (method !== "GET" && method !== "HEAD") {\n` +
|
|
219
|
+
body +
|
|
220
|
+
` init.duplex = "half";\n` +
|
|
221
|
+
` }\n` +
|
|
222
|
+
` await sendResponse(await vendo.handler(new Request(absoluteUrl(request), init)), response);\n` +
|
|
223
|
+
`}\n\n` +
|
|
224
|
+
`export function mountVendo()${signatures.mountReturn} {\n` +
|
|
225
|
+
` return (request, response, next) => {\n` +
|
|
226
|
+
` void handle(request, response).catch(next);\n` +
|
|
227
|
+
` };\n` +
|
|
228
|
+
`}\n`;
|
|
229
|
+
}
|
|
230
|
+
export const VENDO_ENV_EXAMPLE = "# Trusted host origin for same-origin API calls. Dev trusts the request's own\n" +
|
|
231
|
+
"# origin automatically; production fails loud without this set (a credential-\n" +
|
|
232
|
+
"# forwarding call errors instead of silently running unauthenticated).\n" +
|
|
233
|
+
"VENDO_BASE_URL=http://localhost:3000\n" +
|
|
234
|
+
"# Model key — REQUIRED in production. In dev, `vendo init` can mint a free starter key instead.\n" +
|
|
235
|
+
"# ANTHROPIC_API_KEY=\n";
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { type ExtractedTool } from "@vendoai/actions";
|
|
2
|
+
import { type CloudStepOptions } from "./cloud-init.js";
|
|
3
|
+
import { type AiExtractionOptions } from "./extract/extraction.js";
|
|
4
|
+
import { type DevCredential } from "../dev-creds/resolve.js";
|
|
5
|
+
import { type HostFramework } from "./framework.js";
|
|
6
|
+
import { type AuthPresetName } from "./init-auth.js";
|
|
7
|
+
import { type SelectOption } from "./pretty.js";
|
|
8
|
+
import { type ThemeSummary } from "./theme/extract-theme.js";
|
|
9
|
+
import { type Output } from "./shared.js";
|
|
10
|
+
export interface RiskRecommendation {
|
|
11
|
+
tool: string;
|
|
12
|
+
risk: ExtractedTool["risk"];
|
|
13
|
+
recommendation: string;
|
|
14
|
+
}
|
|
15
|
+
export interface InitPlan {
|
|
16
|
+
framework: HostFramework;
|
|
17
|
+
root: string;
|
|
18
|
+
writes: string[];
|
|
19
|
+
codeChanges: Array<{
|
|
20
|
+
path: string;
|
|
21
|
+
diff: string;
|
|
22
|
+
}>;
|
|
23
|
+
/** The one line init never writes itself: the user pastes it. */
|
|
24
|
+
manualSteps: string[];
|
|
25
|
+
/** --agent only: deterministic extraction results, so an agent can act on
|
|
26
|
+
real tool names instead of re-deriving them. */
|
|
27
|
+
extraction?: {
|
|
28
|
+
tools: ExtractedTool[];
|
|
29
|
+
warnings: string[];
|
|
30
|
+
};
|
|
31
|
+
riskRecommendations?: RiskRecommendation[];
|
|
32
|
+
/** --agent only: the delegated AI-polish contract. An external coding agent
|
|
33
|
+
reads the codebase against `instructions`, writes a draft matching
|
|
34
|
+
`draftSchema`, and lands it with `apply` — the SAME deterministic guards
|
|
35
|
+
as init's built-in pass decide what applies. */
|
|
36
|
+
aiPolish?: {
|
|
37
|
+
instructions: string;
|
|
38
|
+
draftSchema: Record<string, unknown>;
|
|
39
|
+
apply: string;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export interface InitOptions {
|
|
43
|
+
targetDir: string;
|
|
44
|
+
agent?: boolean;
|
|
45
|
+
yes?: boolean;
|
|
46
|
+
force?: boolean;
|
|
47
|
+
/** Agent-install-dx value flags: each one answers exactly one wizard
|
|
48
|
+
question, so a non-interactive run never needs the prompt it replaces. */
|
|
49
|
+
/** --auth: the auth answer — wires like the equivalent interactive pick. */
|
|
50
|
+
auth?: AuthPresetName | "jwt" | "none";
|
|
51
|
+
/** --framework: detection override; required non-interactively when
|
|
52
|
+
detection comes back "unknown" (there is no safe default to guess).
|
|
53
|
+
"unknown" is excluded: an override that answers nothing would silently
|
|
54
|
+
bypass the non-interactive framework guard. */
|
|
55
|
+
framework?: Exclude<HostFramework, "unknown">;
|
|
56
|
+
/** --cloud-key: answer the cloud-login offer with an existing key — landed
|
|
57
|
+
in .env.local exactly where the mint would put it. */
|
|
58
|
+
cloudKey?: string;
|
|
59
|
+
/** --byo: answer the cloud-login offer with "no — bring my own key". */
|
|
60
|
+
byo?: boolean;
|
|
61
|
+
/** --ai-polish: consent to the AI extraction pass without the prompt. */
|
|
62
|
+
aiPolish?: boolean;
|
|
63
|
+
/** --engine: pin the AI-polish rung family (claude | codex | npx). */
|
|
64
|
+
engine?: string;
|
|
65
|
+
/** --theme slot=value answers for the uncertain-slot review. */
|
|
66
|
+
themeAnswers?: Record<string, string>;
|
|
67
|
+
output?: Output;
|
|
68
|
+
telemetry?: {
|
|
69
|
+
home?: string;
|
|
70
|
+
env?: Record<string, string | undefined>;
|
|
71
|
+
posthogKey?: string;
|
|
72
|
+
fetchImpl?: typeof fetch;
|
|
73
|
+
};
|
|
74
|
+
env?: Record<string, string | undefined>;
|
|
75
|
+
/** Test seam: credential detection for the key step. */
|
|
76
|
+
resolveCredential?: (options: {
|
|
77
|
+
env: Record<string, string | undefined>;
|
|
78
|
+
}) => Promise<DevCredential>;
|
|
79
|
+
/** Test seam (ENG-339): cloud-in-init step overrides. */
|
|
80
|
+
cloud?: Partial<Omit<CloudStepOptions, "root" | "output" | "yes" | "credential">>;
|
|
81
|
+
/** Test seam: AI extraction step overrides (harnesses, consent). */
|
|
82
|
+
extract?: Partial<Omit<AiExtractionOptions, "root" | "output" | "yes" | "env">>;
|
|
83
|
+
/** Test seam: the detect+confirm auth question, asked only in interactive
|
|
84
|
+
runs when exactly one auth family is detected and init is creating the
|
|
85
|
+
composition. Mirrors the AI-polish consent's confirm shape. */
|
|
86
|
+
confirmAuth?: (question: string, defaultYes: boolean) => Promise<boolean>;
|
|
87
|
+
/** Test seam: the auth picker shown when the confirm is declined or when
|
|
88
|
+
several families are detected. Receives the choice list (value/label/
|
|
89
|
+
hint) and resolves the chosen value. */
|
|
90
|
+
selectAuth?: (question: string, options: SelectOption[]) => Promise<string>;
|
|
91
|
+
/** Test seam: interactivity override for the auth confirm (default: TTY),
|
|
92
|
+
mirroring runAiExtraction's `interactive`. */
|
|
93
|
+
interactive?: boolean;
|
|
94
|
+
/** Test seam: the star ask — the ONE consent question that ends a fully
|
|
95
|
+
successful interactive run. Mirrors the auth confirm's shape. */
|
|
96
|
+
confirmStar?: (question: string, defaultYes: boolean) => Promise<boolean>;
|
|
97
|
+
/** Test seam: the gh spawn behind a "yes" to the star ask. */
|
|
98
|
+
spawnStar?: (command: string, args: string[]) => StarProcess;
|
|
99
|
+
/** Uncertain-slot review — asked ONLY when the model reports uncertainty. */
|
|
100
|
+
themeReview?: (summary: ThemeSummary) => Promise<Record<string, string>>;
|
|
101
|
+
}
|
|
102
|
+
/** The slice of the spawned gh process the star step observes (injectable —
|
|
103
|
+
tests drive it with a plain EventEmitter). */
|
|
104
|
+
export interface StarProcess {
|
|
105
|
+
on(event: "error", listener: (error: Error) => void): unknown;
|
|
106
|
+
on(event: "exit", listener: (code: number | null) => void): unknown;
|
|
107
|
+
}
|
|
108
|
+
/** Star the repo via gh (agent-install-dx §CLI-5). Every failure mode — gh
|
|
109
|
+
not installed (spawn error), a non-zero exit, a throwing seam, or a gh
|
|
110
|
+
that hangs past `timeoutMs` — is plain `false`: the caller prints the
|
|
111
|
+
repo URL instead, one line, no error noise. Exported for the timeout's
|
|
112
|
+
direct unit test only. */
|
|
113
|
+
export declare function starViaGh(spawnStar: NonNullable<InitOptions["spawnStar"]>, timeoutMs?: number): Promise<boolean>;
|
|
114
|
+
/** 09-vendo §5 — idempotent, zero-question setup. */
|
|
115
|
+
export declare function runInit(options: InitOptions): Promise<number>;
|