@vendoai/vendo 0.4.1 → 0.4.3
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/dist/cli/cloud/device-login.d.ts +11 -0
- package/dist/cli/cloud/device-login.js +65 -11
- package/dist/cli/cloud/pending-claim.d.ts +6 -3
- package/dist/cli/cloud/pending-claim.js +32 -8
- package/dist/cli/doctor-codes.d.ts +3 -0
- package/dist/cli/doctor-codes.js +3 -0
- package/dist/cli/doctor-live.js +11 -1
- package/dist/cli/doctor.js +70 -10
- package/dist/cli/framework.d.ts +17 -2
- package/dist/cli/framework.js +32 -5
- package/dist/cli/init-scaffolds.d.ts +22 -0
- package/dist/cli/init-scaffolds.js +60 -8
- package/dist/cli/init.d.ts +14 -1
- package/dist/cli/init.js +171 -39
- package/dist/cli/playground/bundle.gen.d.ts +1 -1
- package/dist/cli/playground/bundle.gen.js +1 -1
- package/dist/cli/playground/embed-bundle.gen.d.ts +1 -1
- package/dist/cli/playground/embed-bundle.gen.js +1 -1
- package/dist/cli/provider-deps.d.ts +26 -0
- package/dist/cli/provider-deps.js +100 -0
- package/dist/cli/shared.d.ts +1 -1
- package/dist/cli/shared.js +1 -1
- package/dist/mastra.js +71 -2
- package/dist/react.d.ts +1 -0
- package/dist/react.js +7 -0
- package/dist/wire/apps.js +30 -1
- package/dist/wire/shared.d.ts +1 -1
- package/dist/wire/shared.js +1 -1
- package/package.json +10 -10
package/dist/cli/init.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { type AiExtractionOptions } from "./extract/extraction.js";
|
|
|
4
4
|
import { type DevCredential } from "../dev-creds/resolve.js";
|
|
5
5
|
import { type HostFramework } from "./framework.js";
|
|
6
6
|
import { type AuthPresetName } from "./init-auth.js";
|
|
7
|
+
import { type InstallRunner } from "./provider-deps.js";
|
|
7
8
|
import { type SelectOption } from "./pretty.js";
|
|
8
9
|
import { type ThemeSummary } from "./theme/extract-theme.js";
|
|
9
10
|
import { type Output } from "./shared.js";
|
|
@@ -20,7 +21,8 @@ export interface InitPlan {
|
|
|
20
21
|
path: string;
|
|
21
22
|
diff: string;
|
|
22
23
|
}>;
|
|
23
|
-
/**
|
|
24
|
+
/** Whatever wiring the run could not do safely itself (empty when the
|
|
25
|
+
layout auto-wire landed): the paste lines for the user. */
|
|
24
26
|
manualSteps: string[];
|
|
25
27
|
/** --agent only: deterministic extraction results, so an agent can act on
|
|
26
28
|
real tool names instead of re-deriving them. */
|
|
@@ -76,6 +78,8 @@ export interface InitOptions {
|
|
|
76
78
|
resolveCredential?: (options: {
|
|
77
79
|
env: Record<string, string | undefined>;
|
|
78
80
|
}) => Promise<DevCredential>;
|
|
81
|
+
/** Test seam: the provider-dependency install subprocess (provider-deps.ts). */
|
|
82
|
+
installProvider?: InstallRunner;
|
|
79
83
|
/** Test seam (ENG-339): cloud-in-init step overrides. */
|
|
80
84
|
cloud?: Partial<Omit<CloudStepOptions, "root" | "output" | "yes" | "credential">>;
|
|
81
85
|
/** Test seam: AI extraction step overrides (harnesses, consent). */
|
|
@@ -111,5 +115,14 @@ export interface StarProcess {
|
|
|
111
115
|
repo URL instead, one line, no error noise. Exported for the timeout's
|
|
112
116
|
direct unit test only. */
|
|
113
117
|
export declare function starViaGh(spawnStar: NonNullable<InitOptions["spawnStar"]>, timeoutMs?: number): Promise<boolean>;
|
|
118
|
+
/**
|
|
119
|
+
* The one bounded edit init makes to user-authored code (visible-surface fix,
|
|
120
|
+
* 0.4.1 E2E cert B3): mount the generated client wrapper around the layout's
|
|
121
|
+
* `{children}` and import it. Null — leave the file untouched, fall back to
|
|
122
|
+
* the printed paste — whenever a Vendo mount already exists (idempotence) or
|
|
123
|
+
* the file doesn't have exactly one JSX `{children}` to wrap (ambiguity).
|
|
124
|
+
* Exported for direct unit tests.
|
|
125
|
+
*/
|
|
126
|
+
export declare function wireNextLayout(source: string, wrapperSpecifier: string): string | null;
|
|
114
127
|
/** 09-vendo §5 — idempotent, zero-question setup. */
|
|
115
128
|
export declare function runInit(options: InitOptions): Promise<number>;
|
package/dist/cli/init.js
CHANGED
|
@@ -14,7 +14,8 @@ import { BRIEF_TEMPLATE } from "./extract/stages.js";
|
|
|
14
14
|
import { ENV_KEY_VARS, resolveDevCredential, describeDevCredential } from "../dev-creds/resolve.js";
|
|
15
15
|
import { detectFramework, detectVendoWiring } from "./framework.js";
|
|
16
16
|
import { resolveScaffoldAuth } from "./init-auth.js";
|
|
17
|
-
import {
|
|
17
|
+
import { ensureProviderDeps } from "./provider-deps.js";
|
|
18
|
+
import { expressServerSource, registrySource, routeSource, serverActionsModuleSource, VENDO_ENV_EXAMPLE, vendoRootWrapperSource, wiringServerActions, } from "./init-scaffolds.js";
|
|
18
19
|
import { createPrettyOutput, plainSelect, usePrettyOutput } from "./pretty.js";
|
|
19
20
|
import { contrastingText } from "./theme/color.js";
|
|
20
21
|
import { applyThemeDraft, extractTheme as extractThemeSlots, validateSlotValue, } from "./theme/extract-theme.js";
|
|
@@ -23,17 +24,24 @@ import { cloudProjectProps, consoleOutput, envLocalValueSync, errorClass, exists
|
|
|
23
24
|
* `vendo init` (install-dx v1, re-derived 2026-07-18): one command, zero
|
|
24
25
|
* questions on the happy path, no ceremony.
|
|
25
26
|
*
|
|
26
|
-
* scan → wire (the
|
|
27
|
-
* catch-all handler wired to
|
|
28
|
-
*
|
|
29
|
-
*
|
|
27
|
+
* scan → wire (the surface files — empty vendo/registry.tsx, the client
|
|
28
|
+
* mount vendo/vendo-root.tsx, the catch-all handler wired to the registry,
|
|
29
|
+
* and the ONE bounded layout edit that mounts <VendoRoot> + <VendoOverlay />
|
|
30
|
+
* so a by-the-book install is actually visible (0.4.1 E2E cert B3); a
|
|
31
|
+
* detected auth preset gets one consent-style confirm in interactive runs,
|
|
32
|
+
* --yes/non-interactive accept it silently — plus package.json hooks)
|
|
30
33
|
* → key (env stated, else the cloud starter offer) → done summary (files
|
|
31
|
-
* changed,
|
|
34
|
+
* changed, any remaining paste, next steps).
|
|
32
35
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
36
|
+
* The layout edit is the one place init touches user-authored code: it is
|
|
37
|
+
* idempotent (skipped when <VendoRoot> or <VendoOverlay> is already mounted),
|
|
38
|
+
* bounded (wrap the single JSX `{children}` + one import line), and degrades
|
|
39
|
+
* to the printed paste when the layout can't be edited safely.
|
|
40
|
+
*
|
|
41
|
+
* Removed by design: the interview, per-diff y/N approvals, the lib/ai.ts
|
|
42
|
+
* scaffold (createVendo's `model` is optional now), remix offers, the
|
|
43
|
+
* encryption-key step, the refine offer, and the finale ceremony (doctor owns
|
|
44
|
+
* verification and the live turn).
|
|
37
45
|
*/
|
|
38
46
|
const DEFAULT_RADIUS = { small: "4px", large: "12px" };
|
|
39
47
|
const BRIEF_PLACEHOLDER = `${BRIEF_TEMPLATE}\n`;
|
|
@@ -308,27 +316,49 @@ async function setupSkillSource() {
|
|
|
308
316
|
return null;
|
|
309
317
|
}
|
|
310
318
|
}
|
|
311
|
-
/** The
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
319
|
+
/** The remaining manual wiring lines, derived from how far the auto-wire got.
|
|
320
|
+
A layout wired this run (or one that already mounts a surface) leaves
|
|
321
|
+
nothing to paste; an uneditable layout gets the wrapper paste (the wrapper
|
|
322
|
+
owns the registry/theme imports — pasting them into a Server Component
|
|
323
|
+
layout is the RSC-serialization crash the wrapper exists to avoid); a
|
|
324
|
+
<VendoRoot>-without-surface host gets the one overlay line; Express keeps
|
|
325
|
+
its printed wiring lines. */
|
|
326
|
+
async function manualWiringLines(root, layout, withRegistry) {
|
|
327
|
+
if (layout.kind === "express") {
|
|
317
328
|
const wrap = withRegistry
|
|
318
|
-
? `<VendoRoot components={registry} theme={theme}
|
|
319
|
-
: `<VendoRoot theme={theme}
|
|
329
|
+
? `<VendoRoot components={registry} theme={theme}>…<VendoOverlay /></VendoRoot>`
|
|
330
|
+
: `<VendoRoot theme={theme}>…<VendoOverlay /></VendoRoot>`;
|
|
320
331
|
return [
|
|
321
332
|
`app.use("/api/vendo", mountVendo()); // in your server`,
|
|
322
|
-
`${wrap} // around your client root (see vendo/server for the imports)`,
|
|
333
|
+
`${wrap} // around your client root (see vendo/server for the imports; <VendoOverlay /> is the visible launcher + panel)`,
|
|
334
|
+
];
|
|
335
|
+
}
|
|
336
|
+
if (layout.kind === "wired" || layout.kind === "already")
|
|
337
|
+
return [];
|
|
338
|
+
if (layout.kind === "overlay-missing") {
|
|
339
|
+
return [
|
|
340
|
+
`In ${layout.layoutPath}: nothing renders yet — <VendoRoot> is a context provider. Mount the visible surface inside it:`,
|
|
341
|
+
` import { VendoOverlay } from "@vendoai/vendo/react";`,
|
|
342
|
+
` … then add: <VendoOverlay /> (the launcher pill + panel users open)`,
|
|
323
343
|
];
|
|
324
344
|
}
|
|
325
345
|
const app = await appDirectory(root);
|
|
346
|
+
const layoutRel = relative(root, join(app, "layout.tsx"));
|
|
347
|
+
if (withRegistry) {
|
|
348
|
+
const wrapperSpecifier = relative(app, join(dirname(app), "vendo", "vendo-root")).split(sep).join("/");
|
|
349
|
+
return [
|
|
350
|
+
`In ${layoutRel}:`,
|
|
351
|
+
` import { VendoRoot } from ${JSON.stringify(wrapperSpecifier)};`,
|
|
352
|
+
` … then wrap: <VendoRoot>{children}</VendoRoot>`,
|
|
353
|
+
` (${join("vendo", "vendo-root.tsx")} mounts <VendoOverlay />, the visible launcher + panel)`,
|
|
354
|
+
];
|
|
355
|
+
}
|
|
356
|
+
// No registry consumer (a hand-wired route that ignores it): the direct
|
|
357
|
+
// provider + overlay paste — theme.json is serializable, so it may cross
|
|
358
|
+
// the Server Component boundary; the registry may not.
|
|
326
359
|
const specifier = await themeImportSpecifier(root, app);
|
|
327
|
-
const layout = relative(root, join(app, "layout.tsx"));
|
|
328
|
-
const registrySpecifier = relative(app, join(dirname(app), "vendo", "registry")).split(sep).join("/");
|
|
329
360
|
const importLines = [
|
|
330
|
-
`import { VendoRoot } from "@vendoai/vendo/react";`,
|
|
331
|
-
...(withRegistry ? [`import { registry } from ${JSON.stringify(registrySpecifier)};`] : []),
|
|
361
|
+
`import { VendoOverlay, VendoRoot } from "@vendoai/vendo/react";`,
|
|
332
362
|
...(specifier === null
|
|
333
363
|
? []
|
|
334
364
|
: [
|
|
@@ -336,12 +366,8 @@ async function vendoRootPasteLines(root, framework, withRegistry) {
|
|
|
336
366
|
`import type { VendoTheme } from "@vendoai/vendo";`,
|
|
337
367
|
]),
|
|
338
368
|
];
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
...(specifier === null ? [] : ["theme={theme as VendoTheme}"]),
|
|
342
|
-
];
|
|
343
|
-
const wrap = `<VendoRoot${props.length === 0 ? "" : ` ${props.join(" ")}`}>{children}</VendoRoot>`;
|
|
344
|
-
return [`In ${layout}:`, ...importLines.map((line) => ` ${line}`), ` … then wrap: ${wrap}`];
|
|
369
|
+
const wrap = `<VendoRoot${specifier === null ? "" : " theme={theme as VendoTheme}"}>{children}<VendoOverlay /></VendoRoot>`;
|
|
370
|
+
return [`In ${layoutRel}:`, ...importLines.map((line) => ` ${line}`), ` … then wrap: ${wrap}`];
|
|
345
371
|
}
|
|
346
372
|
/** The repo-specific agent tail (agent-install-dx): a non-interactive
|
|
347
373
|
scaffold run is agent-driven, so the run ends with plain deterministic
|
|
@@ -373,11 +399,17 @@ async function agentTailLines(args) {
|
|
|
373
399
|
if (args.framework === "express") {
|
|
374
400
|
// No exact entry file exists to name on Express — point at the printed
|
|
375
401
|
// wiring lines instead of guessing a path.
|
|
376
|
-
lines.push("edit your server and client entries — paste the mountVendo() and <VendoRoot
|
|
402
|
+
lines.push("edit your server and client entries — paste the mountVendo() and <VendoRoot>/<VendoOverlay /> lines above (without a mounted surface, users see nothing)");
|
|
377
403
|
}
|
|
378
|
-
else {
|
|
404
|
+
else if (args.layout.kind === "wired") {
|
|
405
|
+
lines.push(`surface: ${args.layout.layoutPath} wired this run — <VendoRoot> wraps the app and mounts <VendoOverlay /> (the visible launcher + panel) via ${join("vendo", "vendo-root.tsx")}; review the diff`);
|
|
406
|
+
}
|
|
407
|
+
else if (args.layout.kind === "overlay-missing") {
|
|
408
|
+
lines.push(`edit ${args.layout.layoutPath} — add <VendoOverlay /> inside your <VendoRoot> (see the lines above; <VendoRoot> alone renders NOTHING visible)`);
|
|
409
|
+
}
|
|
410
|
+
else if (args.layout.kind === "manual") {
|
|
379
411
|
const layout = relative(args.root, join(await appDirectory(args.root), "layout.tsx"));
|
|
380
|
-
lines.push(`edit ${layout} — wrap the app in the <VendoRoot> lines above`);
|
|
412
|
+
lines.push(`edit ${layout} — wrap the app in the <VendoRoot> lines above (it mounts <VendoOverlay />, the visible surface; without it users see nothing)`);
|
|
381
413
|
}
|
|
382
414
|
if (await readOptional(join(args.root, ".vendo", "brief.md")) === BRIEF_PLACEHOLDER) {
|
|
383
415
|
lines.push(`edit ${join(".vendo", "brief.md")} — replace the placeholder with what this product does and for whom`);
|
|
@@ -419,6 +451,48 @@ export function starViaGh(spawnStar, timeoutMs = 5_000) {
|
|
|
419
451
|
child.on("exit", (code) => settle(code === 0));
|
|
420
452
|
});
|
|
421
453
|
}
|
|
454
|
+
/** Where the wrapped `{children}` insert lands: the SINGLE JSX-rendered
|
|
455
|
+
`{children}` (between a `>` and a `<`). Requiring the JSX position keeps
|
|
456
|
+
the layout's own `({ children })` parameter destructure out of the count. */
|
|
457
|
+
const LAYOUT_CHILDREN = /(>)(\s*\{\s*children\s*\}\s*)(<)/g;
|
|
458
|
+
/** End index for the wrapper import: after the last top-level import
|
|
459
|
+
statement (lazy up to its module-specifier string literal, so multi-line
|
|
460
|
+
destructures work), else after a leading directive ("use client"), else 0. */
|
|
461
|
+
function importInsertionIndex(source) {
|
|
462
|
+
const heads = [...source.matchAll(/^import\b/gm)];
|
|
463
|
+
if (heads.length === 0) {
|
|
464
|
+
const directive = /^(?:\s*(?:'[^']*'|"[^"]*");?[^\S\n]*\n)+/.exec(source);
|
|
465
|
+
return directive === null ? 0 : directive[0].length;
|
|
466
|
+
}
|
|
467
|
+
const last = heads[heads.length - 1].index;
|
|
468
|
+
const statement = /import\s[\s\S]*?["'][^"']*["'][^\S\n]*;?/.exec(source.slice(last));
|
|
469
|
+
const end = last + (statement === null ? 0 : statement[0].length);
|
|
470
|
+
const newline = source.indexOf("\n", end);
|
|
471
|
+
return newline === -1 ? source.length : newline + 1;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* The one bounded edit init makes to user-authored code (visible-surface fix,
|
|
475
|
+
* 0.4.1 E2E cert B3): mount the generated client wrapper around the layout's
|
|
476
|
+
* `{children}` and import it. Null — leave the file untouched, fall back to
|
|
477
|
+
* the printed paste — whenever a Vendo mount already exists (idempotence) or
|
|
478
|
+
* the file doesn't have exactly one JSX `{children}` to wrap (ambiguity).
|
|
479
|
+
* Exported for direct unit tests.
|
|
480
|
+
*/
|
|
481
|
+
export function wireNextLayout(source, wrapperSpecifier) {
|
|
482
|
+
if (source.includes("VendoRoot") || source.includes("VendoOverlay"))
|
|
483
|
+
return null;
|
|
484
|
+
const matches = [...source.matchAll(LAYOUT_CHILDREN)];
|
|
485
|
+
if (matches.length !== 1)
|
|
486
|
+
return null;
|
|
487
|
+
const match = matches[0];
|
|
488
|
+
const open = match.index + match[1].length;
|
|
489
|
+
const close = match.index + match[0].length - match[3].length;
|
|
490
|
+
let next = `${source.slice(0, open)}<VendoRoot>${source.slice(open, close)}</VendoRoot>${source.slice(close)}`;
|
|
491
|
+
const at = importInsertionIndex(next);
|
|
492
|
+
const importLine = `import { VendoRoot } from ${JSON.stringify(wrapperSpecifier)};\n`;
|
|
493
|
+
next = `${next.slice(0, at)}${importLine}${next.slice(at)}`;
|
|
494
|
+
return next;
|
|
495
|
+
}
|
|
422
496
|
async function buildPlan(options, confirmAuth, selectAuth) {
|
|
423
497
|
const root = resolve(options.targetDir);
|
|
424
498
|
const framework = options.framework ?? await detectFramework(root);
|
|
@@ -428,7 +502,9 @@ async function buildPlan(options, confirmAuth, selectAuth) {
|
|
|
428
502
|
let compositionPath = null;
|
|
429
503
|
let registryPath = null;
|
|
430
504
|
let withRegistry = false;
|
|
505
|
+
let layout = { kind: "manual" };
|
|
431
506
|
if (framework === "express") {
|
|
507
|
+
layout = { kind: "express" };
|
|
432
508
|
const wiring = await detectVendoWiring(root);
|
|
433
509
|
if (!wiring.server || !wiring.client) {
|
|
434
510
|
const typescript = await exists(join(root, "tsconfig.json"));
|
|
@@ -521,6 +597,45 @@ async function buildPlan(options, confirmAuth, selectAuth) {
|
|
|
521
597
|
changes.push({ absolute: route, path, before: routeBefore, after: wiredRoute, diff: diff(path, routeBefore, wiredRoute) });
|
|
522
598
|
}
|
|
523
599
|
}
|
|
600
|
+
// Visible surface (0.4.1 E2E cert B3): the client mount wrapper next to
|
|
601
|
+
// the registry — the "use client" boundary that owns the registry + theme
|
|
602
|
+
// imports (passing the registry from the Server Component layout into the
|
|
603
|
+
// client provider fails RSC serialization) and mounts <VendoOverlay /> —
|
|
604
|
+
// plus the ONE bounded layout edit that wraps `{children}` in it. Both
|
|
605
|
+
// idempotent: an existing wrapper is never clobbered, a layout already
|
|
606
|
+
// mounting <VendoRoot> or <VendoOverlay> is never re-edited, and an
|
|
607
|
+
// uneditable layout degrades to the printed paste.
|
|
608
|
+
const wrapperFile = join(dirname(app), "vendo", "vendo-root.tsx");
|
|
609
|
+
const wrapperBefore = await readOptional(wrapperFile);
|
|
610
|
+
const layoutFile = join(app, "layout.tsx");
|
|
611
|
+
const layoutBefore = await readOptional(layoutFile);
|
|
612
|
+
// The generated wrapper doesn't count as a host mount: its overlay is
|
|
613
|
+
// only real once a layout mounts the wrapper itself.
|
|
614
|
+
const mounts = await detectVendoWiring(root, { exclude: [wrapperFile] });
|
|
615
|
+
if (mounts.client || mounts.surface) {
|
|
616
|
+
// A mounted <VendoRoot> next to an existing wrapper IS the surface —
|
|
617
|
+
// the wrapper renders <VendoOverlay /> (the re-run after an auto-wire).
|
|
618
|
+
layout = mounts.surface || wrapperBefore !== null
|
|
619
|
+
? { kind: "already" }
|
|
620
|
+
: { kind: "overlay-missing", layoutPath: relative(root, layoutFile) };
|
|
621
|
+
}
|
|
622
|
+
else if (withRegistry) {
|
|
623
|
+
// The wrapper consumes ./registry, so it exists only alongside one —
|
|
624
|
+
// a hand-wired host that ignores the registry keeps the manual paste.
|
|
625
|
+
if (wrapperBefore === null) {
|
|
626
|
+
const path = relative(root, wrapperFile);
|
|
627
|
+
const themeSpecifier = await themeImportSpecifier(root, dirname(wrapperFile));
|
|
628
|
+
const wrapperAfter = vendoRootWrapperSource({ themeSpecifier });
|
|
629
|
+
changes.push({ absolute: wrapperFile, path, before: null, after: wrapperAfter, diff: diff(path, null, wrapperAfter) });
|
|
630
|
+
}
|
|
631
|
+
const wrapperSpecifier = relative(app, join(dirname(app), "vendo", "vendo-root")).split(sep).join("/");
|
|
632
|
+
const layoutAfter = layoutBefore === null ? null : wireNextLayout(layoutBefore, wrapperSpecifier);
|
|
633
|
+
if (layoutAfter !== null) {
|
|
634
|
+
const path = relative(root, layoutFile);
|
|
635
|
+
changes.push({ absolute: layoutFile, path, before: layoutBefore, after: layoutAfter, diff: diff(path, layoutBefore, layoutAfter) });
|
|
636
|
+
layout = { kind: "wired", layoutPath: path };
|
|
637
|
+
}
|
|
638
|
+
}
|
|
524
639
|
}
|
|
525
640
|
const packageJson = join(root, "package.json");
|
|
526
641
|
const packageBefore = await readOptional(packageJson);
|
|
@@ -560,7 +675,7 @@ async function buildPlan(options, confirmAuth, selectAuth) {
|
|
|
560
675
|
".vendo/theme.json",
|
|
561
676
|
".vendo/data/.gitignore",
|
|
562
677
|
];
|
|
563
|
-
const manualSteps = await
|
|
678
|
+
const manualSteps = await manualWiringLines(root, layout, withRegistry);
|
|
564
679
|
return {
|
|
565
680
|
changes,
|
|
566
681
|
manualSteps,
|
|
@@ -568,6 +683,7 @@ async function buildPlan(options, confirmAuth, selectAuth) {
|
|
|
568
683
|
authWired,
|
|
569
684
|
compositionPath,
|
|
570
685
|
registryPath,
|
|
686
|
+
layout,
|
|
571
687
|
plan: {
|
|
572
688
|
framework,
|
|
573
689
|
root,
|
|
@@ -670,7 +786,7 @@ export async function runInit(options) {
|
|
|
670
786
|
? undefined
|
|
671
787
|
: (options.selectAuth ?? (pretty === null ? plainSelect : pretty.select));
|
|
672
788
|
const detectStarted = Date.now();
|
|
673
|
-
const { plan, changes, manualSteps, authAdvice, authWired, compositionPath, registryPath } = await buildPlan(options, confirmAuth, selectAuth);
|
|
789
|
+
const { plan, changes, manualSteps, authAdvice, authWired, compositionPath, registryPath, layout } = await buildPlan(options, confirmAuth, selectAuth);
|
|
674
790
|
const detectMs = Date.now() - detectStarted;
|
|
675
791
|
let telemetry = telemetryFor(options, output, root);
|
|
676
792
|
await telemetry.track("init_started", { framework: plan.framework });
|
|
@@ -927,6 +1043,16 @@ export async function runInit(options) {
|
|
|
927
1043
|
wiringMs,
|
|
928
1044
|
...(await cloudProjectProps(root)),
|
|
929
1045
|
});
|
|
1046
|
+
// The credential's runtime provider must be resolvable from the host or
|
|
1047
|
+
// the FIRST turn 500s (dev-creds/model.ts loads it host-side; nothing
|
|
1048
|
+
// declares @ai-sdk/* — 0.4.1 E2E cert finding). Install exactly what the
|
|
1049
|
+
// resolved credential needs; a failure degrades to the manual command.
|
|
1050
|
+
await ensureProviderDeps({
|
|
1051
|
+
root,
|
|
1052
|
+
credential,
|
|
1053
|
+
output,
|
|
1054
|
+
...(options.installProvider === undefined ? {} : { run: options.installProvider }),
|
|
1055
|
+
});
|
|
930
1056
|
// The one short Cloud reminder in the end-of-run summary — ONLY while no
|
|
931
1057
|
// key exists (the full emphasized block already ran up top; no repeat).
|
|
932
1058
|
if (credential.rung === "none") {
|
|
@@ -939,10 +1065,16 @@ export async function runInit(options) {
|
|
|
939
1065
|
if (aiVersion !== null && Number.parseInt(aiVersion, 10) >= 7) {
|
|
940
1066
|
output.error(`warning: installed ai@${aiVersion} is unsupported — Vendo supports ai@6; downgrade (npm install ai@^6 @ai-sdk/anthropic@^3 @ai-sdk/react@^3) or track github.com/runvendo/vendo/issues/478`);
|
|
941
1067
|
}
|
|
942
|
-
// Done —
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
output.log(
|
|
1068
|
+
// Done — whatever paste remains (none when the layout auto-wire landed),
|
|
1069
|
+
// then their own dev server.
|
|
1070
|
+
if (layout.kind === "wired") {
|
|
1071
|
+
output.log(`\nMounted <VendoRoot> + <VendoOverlay /> (the visible launcher + panel) in ${layout.layoutPath} — review the diff before committing.`);
|
|
1072
|
+
}
|
|
1073
|
+
if (manualSteps.length > 0) {
|
|
1074
|
+
output.log("\nLast steps are yours:");
|
|
1075
|
+
for (const line of manualSteps)
|
|
1076
|
+
output.log(` ${line}`);
|
|
1077
|
+
}
|
|
946
1078
|
output.log("\nThen start your dev server — the agent is live in your app.");
|
|
947
1079
|
output.log("Verify everything: `npx vendo doctor` (it can start the server and run a live turn).");
|
|
948
1080
|
// Agent tail (agent-install-dx): the --yes-or-non-TTY path is agent-driven
|
|
@@ -951,7 +1083,7 @@ export async function runInit(options) {
|
|
|
951
1083
|
// never reaches here (its read-only JSON plan returned above).
|
|
952
1084
|
if (options.yes === true || !interactive) {
|
|
953
1085
|
output.log("\nAgent tail:");
|
|
954
|
-
const tail = await agentTailLines({ root, framework: plan.framework, registryPath, compositionPath, authWired, cloudKeyMissing: credential.rung === "none" });
|
|
1086
|
+
const tail = await agentTailLines({ root, framework: plan.framework, registryPath, compositionPath, authWired, layout, cloudKeyMissing: credential.rung === "none" });
|
|
955
1087
|
for (const line of tail)
|
|
956
1088
|
output.log(` ${line}`);
|
|
957
1089
|
}
|