@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.
Files changed (212) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +28 -0
  3. package/bin/vendo.mjs +4 -0
  4. package/dist/ai-sdk.d.ts +35 -0
  5. package/dist/ai-sdk.js +62 -0
  6. package/dist/auth-presets/auth-js.d.ts +14 -0
  7. package/dist/auth-presets/auth-js.js +91 -0
  8. package/dist/auth-presets/auth0.d.ts +23 -0
  9. package/dist/auth-presets/auth0.js +98 -0
  10. package/dist/auth-presets/clerk.d.ts +23 -0
  11. package/dist/auth-presets/clerk.js +65 -0
  12. package/dist/auth-presets/conformance.d.ts +42 -0
  13. package/dist/auth-presets/conformance.js +139 -0
  14. package/dist/auth-presets/identity.d.ts +56 -0
  15. package/dist/auth-presets/identity.js +152 -0
  16. package/dist/auth-presets/index.d.ts +14 -0
  17. package/dist/auth-presets/index.js +12 -0
  18. package/dist/auth-presets/jwt.d.ts +16 -0
  19. package/dist/auth-presets/jwt.js +54 -0
  20. package/dist/auth-presets/shared.d.ts +35 -0
  21. package/dist/auth-presets/shared.js +1 -0
  22. package/dist/auth-presets/supabase.d.ts +42 -0
  23. package/dist/auth-presets/supabase.js +229 -0
  24. package/dist/byo-approvals.d.ts +37 -0
  25. package/dist/byo-approvals.js +189 -0
  26. package/dist/capability-misses.d.ts +38 -0
  27. package/dist/capability-misses.js +172 -0
  28. package/dist/catalog.d.ts +19 -0
  29. package/dist/catalog.js +152 -0
  30. package/dist/cli/cloud/args.d.ts +3 -0
  31. package/dist/cli/cloud/args.js +32 -0
  32. package/dist/cli/cloud/auth.d.ts +17 -0
  33. package/dist/cli/cloud/auth.js +123 -0
  34. package/dist/cli/cloud/client.d.ts +28 -0
  35. package/dist/cli/cloud/client.js +133 -0
  36. package/dist/cli/cloud/command.d.ts +22 -0
  37. package/dist/cli/cloud/command.js +73 -0
  38. package/dist/cli/cloud/deploy.d.ts +26 -0
  39. package/dist/cli/cloud/deploy.js +187 -0
  40. package/dist/cli/cloud/device-login.d.ts +32 -0
  41. package/dist/cli/cloud/device-login.js +259 -0
  42. package/dist/cli/cloud/index.d.ts +5 -0
  43. package/dist/cli/cloud/index.js +59 -0
  44. package/dist/cli/cloud/keys.d.ts +2 -0
  45. package/dist/cli/cloud/keys.js +31 -0
  46. package/dist/cli/cloud/members.d.ts +3 -0
  47. package/dist/cli/cloud/members.js +25 -0
  48. package/dist/cli/cloud/output.d.ts +4 -0
  49. package/dist/cli/cloud/output.js +12 -0
  50. package/dist/cli/cloud/pending-claim.d.ts +29 -0
  51. package/dist/cli/cloud/pending-claim.js +38 -0
  52. package/dist/cli/cloud/read.d.ts +3 -0
  53. package/dist/cli/cloud/read.js +12 -0
  54. package/dist/cli/cloud/services.d.ts +2 -0
  55. package/dist/cli/cloud/services.js +9 -0
  56. package/dist/cli/cloud/session.d.ts +13 -0
  57. package/dist/cli/cloud/session.js +41 -0
  58. package/dist/cli/cloud-init.d.ts +60 -0
  59. package/dist/cli/cloud-init.js +149 -0
  60. package/dist/cli/dep-versions.d.ts +26 -0
  61. package/dist/cli/dep-versions.js +77 -0
  62. package/dist/cli/doctor-codes.d.ts +58 -0
  63. package/dist/cli/doctor-codes.js +60 -0
  64. package/dist/cli/doctor-live.d.ts +67 -0
  65. package/dist/cli/doctor-live.js +169 -0
  66. package/dist/cli/doctor.d.ts +52 -0
  67. package/dist/cli/doctor.js +467 -0
  68. package/dist/cli/eject.d.ts +29 -0
  69. package/dist/cli/eject.js +141 -0
  70. package/dist/cli/extract/apply.d.ts +26 -0
  71. package/dist/cli/extract/apply.js +123 -0
  72. package/dist/cli/extract/claude-cli-harness.d.ts +18 -0
  73. package/dist/cli/extract/claude-cli-harness.js +132 -0
  74. package/dist/cli/extract/claude-harness.d.ts +14 -0
  75. package/dist/cli/extract/claude-harness.js +127 -0
  76. package/dist/cli/extract/codex-cli-harness.d.ts +31 -0
  77. package/dist/cli/extract/codex-cli-harness.js +154 -0
  78. package/dist/cli/extract/delegate.d.ts +21 -0
  79. package/dist/cli/extract/delegate.js +77 -0
  80. package/dist/cli/extract/extraction.d.ts +65 -0
  81. package/dist/cli/extract/extraction.js +300 -0
  82. package/dist/cli/extract/gateway-fuel.d.ts +69 -0
  83. package/dist/cli/extract/gateway-fuel.js +69 -0
  84. package/dist/cli/extract/harness.d.ts +114 -0
  85. package/dist/cli/extract/harness.js +84 -0
  86. package/dist/cli/extract/index.d.ts +9 -0
  87. package/dist/cli/extract/index.js +9 -0
  88. package/dist/cli/extract/npx-engine-harness.d.ts +73 -0
  89. package/dist/cli/extract/npx-engine-harness.js +225 -0
  90. package/dist/cli/extract/stages.d.ts +149 -0
  91. package/dist/cli/extract/stages.js +398 -0
  92. package/dist/cli/framework.d.ts +8 -0
  93. package/dist/cli/framework.js +36 -0
  94. package/dist/cli/init-auth.d.ts +67 -0
  95. package/dist/cli/init-auth.js +142 -0
  96. package/dist/cli/init-scaffolds.d.ts +28 -0
  97. package/dist/cli/init-scaffolds.js +235 -0
  98. package/dist/cli/init.d.ts +115 -0
  99. package/dist/cli/init.js +1008 -0
  100. package/dist/cli/mcp/index.d.ts +7 -0
  101. package/dist/cli/mcp/index.js +59 -0
  102. package/dist/cli/mcp/registry.d.ts +9 -0
  103. package/dist/cli/mcp/registry.js +117 -0
  104. package/dist/cli/mcp/server-json.d.ts +12 -0
  105. package/dist/cli/mcp/server-json.js +65 -0
  106. package/dist/cli/mcp/server.schema.json +574 -0
  107. package/dist/cli/mcp/verify-domain.d.ts +11 -0
  108. package/dist/cli/mcp/verify-domain.js +48 -0
  109. package/dist/cli/playground/app/embed-entry.d.ts +20 -0
  110. package/dist/cli/playground/app/embed-entry.js +46 -0
  111. package/dist/cli/playground/app/fake-client.d.ts +3 -0
  112. package/dist/cli/playground/app/fake-client.js +215 -0
  113. package/dist/cli/playground/app/fixtures.d.ts +47 -0
  114. package/dist/cli/playground/app/fixtures.js +472 -0
  115. package/dist/cli/playground/app/main.d.ts +1 -0
  116. package/dist/cli/playground/app/main.js +108 -0
  117. package/dist/cli/playground/app/scenario-mount.d.ts +13 -0
  118. package/dist/cli/playground/app/scenario-mount.js +48 -0
  119. package/dist/cli/playground/app/scenarios.d.ts +25 -0
  120. package/dist/cli/playground/app/scenarios.js +152 -0
  121. package/dist/cli/playground/app/theme-editor.d.ts +13 -0
  122. package/dist/cli/playground/app/theme-editor.js +149 -0
  123. package/dist/cli/playground/app/theme-state.d.ts +29 -0
  124. package/dist/cli/playground/app/theme-state.js +151 -0
  125. package/dist/cli/playground/bundle.gen.d.ts +2 -0
  126. package/dist/cli/playground/bundle.gen.js +2 -0
  127. package/dist/cli/playground/embed-bundle.gen.d.ts +2 -0
  128. package/dist/cli/playground/embed-bundle.gen.js +2 -0
  129. package/dist/cli/playground.d.ts +33 -0
  130. package/dist/cli/playground.js +120 -0
  131. package/dist/cli/pretty.d.ts +48 -0
  132. package/dist/cli/pretty.js +330 -0
  133. package/dist/cli/refine.d.ts +43 -0
  134. package/dist/cli/refine.js +181 -0
  135. package/dist/cli/semantics.d.ts +31 -0
  136. package/dist/cli/semantics.js +122 -0
  137. package/dist/cli/shared.d.ts +68 -0
  138. package/dist/cli/shared.js +184 -0
  139. package/dist/cli/sync.d.ts +36 -0
  140. package/dist/cli/sync.js +202 -0
  141. package/dist/cli/theme/color.d.ts +15 -0
  142. package/dist/cli/theme/color.js +188 -0
  143. package/dist/cli/theme/css-vars.d.ts +13 -0
  144. package/dist/cli/theme/css-vars.js +40 -0
  145. package/dist/cli/theme/entry-candidates.d.ts +5 -0
  146. package/dist/cli/theme/entry-candidates.js +22 -0
  147. package/dist/cli/theme/extract-theme.d.ts +184 -0
  148. package/dist/cli/theme/extract-theme.js +358 -0
  149. package/dist/cli/theme/walk.d.ts +2 -0
  150. package/dist/cli/theme/walk.js +24 -0
  151. package/dist/cli.d.ts +2 -0
  152. package/dist/cli.js +333 -0
  153. package/dist/cloud-apps.d.ts +17 -0
  154. package/dist/cloud-apps.js +46 -0
  155. package/dist/cloud-console.d.ts +32 -0
  156. package/dist/cloud-console.js +72 -0
  157. package/dist/cloud-tools.d.ts +23 -0
  158. package/dist/cloud-tools.js +196 -0
  159. package/dist/connections.d.ts +71 -0
  160. package/dist/connections.js +192 -0
  161. package/dist/deployment-identity.d.ts +12 -0
  162. package/dist/deployment-identity.js +69 -0
  163. package/dist/dev-creds/model.d.ts +65 -0
  164. package/dist/dev-creds/model.js +159 -0
  165. package/dist/dev-creds/resolve.d.ts +36 -0
  166. package/dist/dev-creds/resolve.js +57 -0
  167. package/dist/hosted-store.d.ts +52 -0
  168. package/dist/hosted-store.js +322 -0
  169. package/dist/index.d.ts +13 -0
  170. package/dist/index.js +1 -0
  171. package/dist/mastra.d.ts +46 -0
  172. package/dist/mastra.js +88 -0
  173. package/dist/react.d.ts +10 -0
  174. package/dist/react.js +32 -0
  175. package/dist/refine.d.ts +244 -0
  176. package/dist/refine.js +599 -0
  177. package/dist/remixable.d.ts +18 -0
  178. package/dist/remixable.js +42 -0
  179. package/dist/runtime-capture.d.ts +18 -0
  180. package/dist/runtime-capture.js +111 -0
  181. package/dist/sandbox-wire.d.ts +101 -0
  182. package/dist/sandbox-wire.js +101 -0
  183. package/dist/sandbox.d.ts +38 -0
  184. package/dist/sandbox.js +389 -0
  185. package/dist/server.d.ts +216 -0
  186. package/dist/server.js +1320 -0
  187. package/dist/sync-impact.d.ts +14 -0
  188. package/dist/sync-impact.js +75 -0
  189. package/dist/turn-liveness.d.ts +33 -0
  190. package/dist/turn-liveness.js +105 -0
  191. package/dist/wire/approvals.d.ts +10 -0
  192. package/dist/wire/approvals.js +59 -0
  193. package/dist/wire/apps.d.ts +5 -0
  194. package/dist/wire/apps.js +155 -0
  195. package/dist/wire/automations.d.ts +5 -0
  196. package/dist/wire/automations.js +57 -0
  197. package/dist/wire/box.d.ts +3 -0
  198. package/dist/wire/box.js +268 -0
  199. package/dist/wire/connections.d.ts +5 -0
  200. package/dist/wire/connections.js +47 -0
  201. package/dist/wire/context.d.ts +22 -0
  202. package/dist/wire/context.js +190 -0
  203. package/dist/wire/doctor.d.ts +19 -0
  204. package/dist/wire/doctor.js +116 -0
  205. package/dist/wire/misc.d.ts +18 -0
  206. package/dist/wire/misc.js +227 -0
  207. package/dist/wire/shared.d.ts +158 -0
  208. package/dist/wire/shared.js +134 -0
  209. package/dist/wire/threads.d.ts +3 -0
  210. package/dist/wire/threads.js +68 -0
  211. package/package.json +151 -0
  212. 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>;