fix: bootstrap hotfix — DTO erasure, wizard failure, port prefill, Pi SDK copy (mosaic-v0.0.26) (#440)
Some checks failed
ci/woodpecker/push/publish Pipeline failed
ci/woodpecker/push/ci Pipeline was successful

This commit was merged in pull request #440.
This commit is contained in:
2026-04-05 21:43:30 +00:00
parent a8cd52e88c
commit 0ae932ab34
13 changed files with 608 additions and 17 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@mosaicstack/mosaic",
"version": "0.0.25",
"version": "0.0.26",
"repository": {
"type": "git",
"url": "https://git.mosaicstack.dev/mosaicstack/mosaic-stack.git",

View File

@@ -39,6 +39,7 @@ export class ClackPrompter implements WizardPrompter {
message: string;
placeholder?: string;
defaultValue?: string;
initialValue?: string;
validate?: (value: string) => string | void;
}): Promise<string> {
const validate = opts.validate
@@ -51,6 +52,7 @@ export class ClackPrompter implements WizardPrompter {
message: opts.message,
placeholder: opts.placeholder,
defaultValue: opts.defaultValue,
initialValue: opts.initialValue,
validate,
});
return guardCancel(result);

View File

@@ -35,15 +35,18 @@ export class HeadlessPrompter implements WizardPrompter {
message: string;
placeholder?: string;
defaultValue?: string;
initialValue?: string;
validate?: (value: string) => string | void;
}): Promise<string> {
const answer = this.answers.get(opts.message);
const value =
typeof answer === 'string'
? answer
: opts.defaultValue !== undefined
? opts.defaultValue
: undefined;
: opts.initialValue !== undefined
? opts.initialValue
: opts.defaultValue !== undefined
? opts.defaultValue
: undefined;
if (value === undefined) {
throw new Error(`HeadlessPrompter: no answer for "${opts.message}"`);

View File

@@ -24,6 +24,8 @@ export interface WizardPrompter {
message: string;
placeholder?: string;
defaultValue?: string;
/** Prefills the input buffer so the user sees the value and can press Enter to accept. */
initialValue?: string;
validate?: (value: string) => string | void;
}): Promise<string>;

View File

@@ -77,6 +77,10 @@ async function promptTier(p: WizardPrompter): Promise<GatewayStorageTier> {
async function promptPort(p: WizardPrompter, defaultPort: number): Promise<number> {
const raw = await p.text({
message: 'Gateway port',
// initialValue prefills the input buffer so the user sees 14242 and can
// press Enter to accept it. defaultValue is only used when the user submits
// an empty string, which never shows in the field.
initialValue: defaultPort.toString(),
defaultValue: defaultPort.toString(),
validate: (v) => {
const n = parseInt(v, 10);
@@ -423,10 +427,12 @@ async function collectAndWriteConfig(
if (tier === 'team') {
databaseUrl = await p.text({
message: 'DATABASE_URL',
initialValue: 'postgresql://mosaic:mosaic@localhost:5433/mosaic',
defaultValue: 'postgresql://mosaic:mosaic@localhost:5433/mosaic',
});
valkeyUrl = await p.text({
message: 'VALKEY_URL',
initialValue: 'redis://localhost:6380',
defaultValue: 'redis://localhost:6380',
});
}
@@ -438,6 +444,7 @@ async function collectAndWriteConfig(
corsOrigin = await p.text({
message: 'CORS origin',
initialValue: 'http://localhost:3000',
defaultValue: 'http://localhost:3000',
});
}

View File

@@ -7,7 +7,7 @@ export async function welcomeStage(p: WizardPrompter, _state: WizardState): Prom
p.note(
`Mosaic is an agent framework that gives AI coding assistants\n` +
`a persistent identity, shared skills, and structured workflows.\n\n` +
`It works with Claude Code, Codex, and OpenCode.\n\n` +
`It works with Claude Code, Codex, OpenCode, and Pi SDK.\n\n` +
`All config is stored locally in ~/.config/mosaic/.\n` +
`No data is sent anywhere. No accounts required.`,
'What is Mosaic?',

View File

@@ -144,8 +144,8 @@ export async function runWizard(options: WizardOptions): Promise<void> {
host: configResult.host,
port: configResult.port,
});
if (!bootstrapResult.completed && headlessRun) {
prompter.warn('Admin bootstrap failed in headless mode — aborting wizard.');
if (!bootstrapResult.completed) {
prompter.warn('Admin bootstrap failed — aborting wizard.');
process.exit(1);
}
}