feat(cli): add mosaic gateway init command with tier selection wizard
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clack/prompts": "^0.9.0",
|
"@clack/prompts": "^0.9.0",
|
||||||
|
"@mosaic/config": "workspace:^",
|
||||||
"@mosaic/mosaic": "workspace:^",
|
"@mosaic/mosaic": "workspace:^",
|
||||||
"@mosaic/prdy": "workspace:^",
|
"@mosaic/prdy": "workspace:^",
|
||||||
"@mosaic/quality-rails": "workspace:^",
|
"@mosaic/quality-rails": "workspace:^",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { registerAgentCommand } from './commands/agent.js';
|
|||||||
import { registerMissionCommand } from './commands/mission.js';
|
import { registerMissionCommand } from './commands/mission.js';
|
||||||
// prdy is registered via launch.ts
|
// prdy is registered via launch.ts
|
||||||
import { registerLaunchCommands } from './commands/launch.js';
|
import { registerLaunchCommands } from './commands/launch.js';
|
||||||
|
import { registerGatewayCommand } from './commands/gateway.js';
|
||||||
|
|
||||||
const _require = createRequire(import.meta.url);
|
const _require = createRequire(import.meta.url);
|
||||||
const CLI_VERSION: string = (_require('../package.json') as { version: string }).version;
|
const CLI_VERSION: string = (_require('../package.json') as { version: string }).version;
|
||||||
@@ -290,6 +291,10 @@ sessionsCmd
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ─── gateway ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
registerGatewayCommand(program);
|
||||||
|
|
||||||
// ─── agent ─────────────────────────────────────────────────────────────
|
// ─── agent ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
registerAgentCommand(program);
|
registerAgentCommand(program);
|
||||||
|
|||||||
78
packages/cli/src/commands/gateway.ts
Normal file
78
packages/cli/src/commands/gateway.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import { createInterface } from 'node:readline';
|
||||||
|
import { writeFileSync } from 'node:fs';
|
||||||
|
import { resolve } from 'node:path';
|
||||||
|
import type { Command } from 'commander';
|
||||||
|
import {
|
||||||
|
DEFAULT_LOCAL_CONFIG,
|
||||||
|
DEFAULT_TEAM_CONFIG,
|
||||||
|
type MosaicConfig,
|
||||||
|
type StorageTier,
|
||||||
|
} from '@mosaic/config';
|
||||||
|
|
||||||
|
function ask(rl: ReturnType<typeof createInterface>, question: string): Promise<string> {
|
||||||
|
return new Promise((res) => rl.question(question, res));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runInit(opts: { tier?: string; output: string }): Promise<void> {
|
||||||
|
const outputPath = resolve(opts.output);
|
||||||
|
let tier: StorageTier;
|
||||||
|
|
||||||
|
if (opts.tier) {
|
||||||
|
if (opts.tier !== 'local' && opts.tier !== 'team') {
|
||||||
|
console.error(`Invalid tier "${opts.tier}" — expected "local" or "team"`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
tier = opts.tier;
|
||||||
|
} else {
|
||||||
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
||||||
|
const answer = await ask(rl, 'Select tier (local/team) [local]: ');
|
||||||
|
rl.close();
|
||||||
|
const trimmed = answer.trim().toLowerCase();
|
||||||
|
tier = trimmed === 'team' ? 'team' : 'local';
|
||||||
|
}
|
||||||
|
|
||||||
|
let config: MosaicConfig;
|
||||||
|
|
||||||
|
if (tier === 'local') {
|
||||||
|
config = DEFAULT_LOCAL_CONFIG;
|
||||||
|
} else {
|
||||||
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
||||||
|
const dbUrl = await ask(
|
||||||
|
rl,
|
||||||
|
'DATABASE_URL [postgresql://mosaic:mosaic@localhost:5432/mosaic]: ',
|
||||||
|
);
|
||||||
|
const valkeyUrl = await ask(rl, 'VALKEY_URL [redis://localhost:6379]: ');
|
||||||
|
rl.close();
|
||||||
|
|
||||||
|
config = {
|
||||||
|
...DEFAULT_TEAM_CONFIG,
|
||||||
|
storage: {
|
||||||
|
type: 'postgres',
|
||||||
|
url: dbUrl.trim() || 'postgresql://mosaic:mosaic@localhost:5432/mosaic',
|
||||||
|
},
|
||||||
|
queue: {
|
||||||
|
type: 'bullmq',
|
||||||
|
url: valkeyUrl.trim() || 'redis://localhost:6379',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFileSync(outputPath, JSON.stringify(config, null, 2) + '\n');
|
||||||
|
console.log(`\nWrote ${outputPath}`);
|
||||||
|
console.log('\nNext steps:');
|
||||||
|
console.log(' 1. Review the generated config');
|
||||||
|
console.log(' 2. Run: pnpm --filter @mosaic/gateway exec tsx src/main.ts');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerGatewayCommand(program: Command): void {
|
||||||
|
const gateway = program.command('gateway').description('Gateway management commands');
|
||||||
|
|
||||||
|
gateway
|
||||||
|
.command('init')
|
||||||
|
.description('Generate a mosaic.config.json for the gateway')
|
||||||
|
.option('--tier <tier>', 'Storage tier: local or team (skips interactive prompt)')
|
||||||
|
.option('--output <path>', 'Output file path', './mosaic.config.json')
|
||||||
|
.action(async (opts: { tier?: string; output: string }) => {
|
||||||
|
await runInit(opts);
|
||||||
|
});
|
||||||
|
}
|
||||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -307,6 +307,9 @@ importers:
|
|||||||
'@clack/prompts':
|
'@clack/prompts':
|
||||||
specifier: ^0.9.0
|
specifier: ^0.9.0
|
||||||
version: 0.9.1
|
version: 0.9.1
|
||||||
|
'@mosaic/config':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../config
|
||||||
'@mosaic/mosaic':
|
'@mosaic/mosaic':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../mosaic
|
version: link:../mosaic
|
||||||
|
|||||||
Reference in New Issue
Block a user