feat(cli): add global entrypoint with doctor/update/uninstall commands
This commit is contained in:
parent
7945ee8d3c
commit
4a98ab2976
4 changed files with 126 additions and 1 deletions
11
bin/beadboard.js
Normal file
11
bin/beadboard.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const { spawnSync } = require('node:child_process');
|
||||
const path = require('node:path');
|
||||
|
||||
const cliPath = path.resolve(__dirname, '../src/cli/beadboard-cli.ts');
|
||||
const result = spawnSync(process.execPath, ['--import', 'tsx', cliPath, ...process.argv.slice(2)], {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
process.exit(result.status ?? 1);
|
||||
|
|
@ -3,13 +3,17 @@
|
|||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"beadboard": "bin/beadboard.js",
|
||||
"bb": "bin/beadboard.js"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "eslint .",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "node --test tests/bootstrap.test.mjs && node --import tsx --test tests/components/shared/base-card.test.tsx && node --import tsx --test tests/components/shared/agent-avatar.test.tsx && node --import tsx --test tests/components/sessions/sessions-header.test.ts && node --import tsx --test tests/components/sessions/agent-station-logic.test.ts && node --import tsx --test tests/lib/parser.test.ts && node --import tsx --test tests/lib/pathing.test.ts && node --import tsx --test tests/components/shared/left-panel.test.tsx && node --import tsx --test tests/components/shared/top-bar.test.tsx && node --import tsx --test tests/components/shared/mobile-nav.test.tsx && node --import tsx --test tests/components/swarm/swarm-card.test.tsx && node --import tsx --test tests/hooks/url-state-integration.test.ts && node --import tsx --test tests/hooks/use-graph-analysis.test.ts && node --import tsx --test tests/components/graph/smart-dag.test.tsx && node --import tsx --test tests/components/unified-shell.test.tsx && node --import tsx --test tests/components/blocked-triage-modal.test.tsx && node --import tsx --test tests/components/graph/graph-node-labels.test.tsx && node --import tsx --test tests/components/graph/graph-node-assign.test.tsx && node --import tsx --test tests/components/graph/graph-node-conversation.test.tsx && node --import tsx --test tests/lib/coord-schema.test.ts && node --import tsx --test tests/lib/install-manifest.test.ts && node --import tsx --test tests/lib/runtime-manager.test.ts && node --import tsx --test tests/lib/coord-events.test.ts && node --import tsx --test tests/api/coord-events-route.test.ts && node --import tsx --test tests/lib/coord-projections-inbox.test.ts && node --import tsx --test tests/lib/coord-projections-reservations.test.ts && node --import tsx --test tests/components/sessions/conversation-drawer-coord.test.tsx && node --import tsx --test tests/scripts/beadboard-launcher.test.ts && node --import tsx --test tests/scripts/beadboard-launcher-runtime.test.ts && node --import tsx --test tests/scripts/install-wrappers-contract.test.ts && node --import tsx --test tests/scripts/install-sh-smoke.test.ts && node --import tsx --test tests/scripts/install-legacy-migration.test.ts && node --import tsx --test tests/scripts/installer-ci-contract.test.ts && node --import tsx --test tests/docs/installer-quickstart-contract.test.ts && node --import tsx --test tests/docs/runtime-manager-adr-contract.test.ts && node --import tsx --test tests/skills/beadboard-driver/resolve-bb.test.ts && node --import tsx --test tests/skills/beadboard-driver/session-preflight.test.ts && node --import tsx --test tests/skills/beadboard-driver/generate-agent-name.test.ts && node --import tsx --test tests/skills/beadboard-driver/readiness-report.test.ts && node --import tsx --test tests/skills/beadboard-driver/skill-local-runner.test.ts && node --import tsx --test tests/skills/beadboard-driver/diagnose-env.test.ts && node --import tsx --test tests/skills/beadboard-driver/heal-common-issues.test.ts && node --import tsx --test tests/lib/epic-graph.test.ts && node --import tsx --test tests/components/shared/left-panel-filtering.test.ts && node --import tsx --test tests/hooks/use-beads-subscription-contract.test.ts && node --import tsx --test tests/components/graph/dependency-graph-hide-closed-contract.test.ts && node --import tsx --test tests/components/shared/unified-shell-hide-closed-contract.test.ts",
|
||||
"test": "node --test tests/bootstrap.test.mjs && node --import tsx --test tests/components/shared/base-card.test.tsx && node --import tsx --test tests/components/shared/agent-avatar.test.tsx && node --import tsx --test tests/components/sessions/sessions-header.test.ts && node --import tsx --test tests/components/sessions/agent-station-logic.test.ts && node --import tsx --test tests/lib/parser.test.ts && node --import tsx --test tests/lib/pathing.test.ts && node --import tsx --test tests/components/shared/left-panel.test.tsx && node --import tsx --test tests/components/shared/top-bar.test.tsx && node --import tsx --test tests/components/shared/mobile-nav.test.tsx && node --import tsx --test tests/components/swarm/swarm-card.test.tsx && node --import tsx --test tests/hooks/url-state-integration.test.ts && node --import tsx --test tests/hooks/use-graph-analysis.test.ts && node --import tsx --test tests/components/graph/smart-dag.test.tsx && node --import tsx --test tests/components/unified-shell.test.tsx && node --import tsx --test tests/components/blocked-triage-modal.test.tsx && node --import tsx --test tests/components/graph/graph-node-labels.test.tsx && node --import tsx --test tests/components/graph/graph-node-assign.test.tsx && node --import tsx --test tests/components/graph/graph-node-conversation.test.tsx && node --import tsx --test tests/lib/coord-schema.test.ts && node --import tsx --test tests/lib/install-manifest.test.ts && node --import tsx --test tests/lib/runtime-manager.test.ts && node --import tsx --test tests/lib/coord-events.test.ts && node --import tsx --test tests/api/coord-events-route.test.ts && node --import tsx --test tests/lib/coord-projections-inbox.test.ts && node --import tsx --test tests/lib/coord-projections-reservations.test.ts && node --import tsx --test tests/components/sessions/conversation-drawer-coord.test.tsx && node --import tsx --test tests/scripts/beadboard-launcher.test.ts && node --import tsx --test tests/scripts/beadboard-launcher-runtime.test.ts && node --import tsx --test tests/scripts/install-wrappers-contract.test.ts && node --import tsx --test tests/scripts/install-sh-smoke.test.ts && node --import tsx --test tests/scripts/install-legacy-migration.test.ts && node --import tsx --test tests/scripts/installer-ci-contract.test.ts && node --import tsx --test tests/docs/installer-quickstart-contract.test.ts && node --import tsx --test tests/docs/runtime-manager-adr-contract.test.ts && node --import tsx --test tests/cli/beadboard-cli.test.ts && node --import tsx --test tests/skills/beadboard-driver/resolve-bb.test.ts && node --import tsx --test tests/skills/beadboard-driver/session-preflight.test.ts && node --import tsx --test tests/skills/beadboard-driver/generate-agent-name.test.ts && node --import tsx --test tests/skills/beadboard-driver/readiness-report.test.ts && node --import tsx --test tests/skills/beadboard-driver/skill-local-runner.test.ts && node --import tsx --test tests/skills/beadboard-driver/diagnose-env.test.ts && node --import tsx --test tests/skills/beadboard-driver/heal-common-issues.test.ts && node --import tsx --test tests/lib/epic-graph.test.ts && node --import tsx --test tests/components/shared/left-panel-filtering.test.ts && node --import tsx --test tests/hooks/use-beads-subscription-contract.test.ts && node --import tsx --test tests/components/graph/dependency-graph-hide-closed-contract.test.ts && node --import tsx --test tests/components/shared/unified-shell-hide-closed-contract.test.ts",
|
||||
"video": "remotion preview src/video/index.ts",
|
||||
"video:render": "remotion render src/video/index.ts Main out/video.mp4",
|
||||
"video:thumbnail": "remotion still src/video/index.ts Main out/thumbnail.png --frame=60"
|
||||
|
|
|
|||
88
src/cli/beadboard-cli.ts
Normal file
88
src/cli/beadboard-cli.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import fs from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { getRuntimePaths, resolveInstallHome } from '../lib/runtime-manager';
|
||||
|
||||
export type CliResult = {
|
||||
ok: boolean;
|
||||
command: string;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
function parseVersion(env: NodeJS.ProcessEnv): string {
|
||||
const raw = (env.BB_RUNTIME_VERSION || env.npm_package_version || '0.1.0').trim();
|
||||
return raw.startsWith('v') ? raw.slice(1) : raw;
|
||||
}
|
||||
|
||||
export async function runCli(argv: string[], env: NodeJS.ProcessEnv = process.env): Promise<CliResult> {
|
||||
const args = [...argv];
|
||||
const asJson = args.includes('--json');
|
||||
const yes = args.includes('--yes');
|
||||
const command = args.find((arg) => !arg.startsWith('-')) || 'help';
|
||||
|
||||
const installHome = resolveInstallHome({ ...env, HOME: env.HOME || os.homedir() });
|
||||
const version = parseVersion(env);
|
||||
const runtime = getRuntimePaths(installHome, version);
|
||||
|
||||
if (command === 'doctor') {
|
||||
return {
|
||||
ok: true,
|
||||
command,
|
||||
json: asJson,
|
||||
installMode: env.BB_INSTALL_MODE || 'npm-global-or-wrapper',
|
||||
installHome,
|
||||
runtimeRoot: runtime.runtimeRoot,
|
||||
runtimeCurrentMetadata: runtime.runtimeCurrentMetadata,
|
||||
shimDir: runtime.shimDir,
|
||||
};
|
||||
}
|
||||
|
||||
if (command === 'self-update') {
|
||||
return {
|
||||
ok: true,
|
||||
command,
|
||||
updated: false,
|
||||
message: 'Self-update is not configured for this distribution yet. Reinstall with npm i -g beadboard when published.',
|
||||
};
|
||||
}
|
||||
|
||||
if (command === 'uninstall') {
|
||||
if (!yes) {
|
||||
return {
|
||||
ok: false,
|
||||
command,
|
||||
error: 'Refusing uninstall without --yes',
|
||||
};
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
fs.rm(runtime.runtimeBase, { recursive: true, force: true }),
|
||||
fs.rm(runtime.shimDir, { recursive: true, force: true }),
|
||||
]);
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
command,
|
||||
removed: [runtime.runtimeBase, runtime.shimDir],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
command: 'help',
|
||||
usage: 'beadboard <doctor|self-update|uninstall> [--json] [--yes]',
|
||||
};
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const result = await runCli(process.argv.slice(2));
|
||||
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
||||
if (!result.ok) {
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
||||
void main();
|
||||
}
|
||||
22
tests/cli/beadboard-cli.test.ts
Normal file
22
tests/cli/beadboard-cli.test.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { runCli } from '../../src/cli/beadboard-cli';
|
||||
|
||||
test('doctor returns structured install diagnostics', async () => {
|
||||
const out = await runCli(['doctor', '--json']);
|
||||
assert.equal(out.ok, true);
|
||||
assert.ok(out.installMode);
|
||||
});
|
||||
|
||||
test('self-update returns explicit placeholder result', async () => {
|
||||
const out = await runCli(['self-update', '--json']);
|
||||
assert.equal(out.ok, true);
|
||||
assert.equal(out.command, 'self-update');
|
||||
assert.equal(out.updated, false);
|
||||
});
|
||||
|
||||
test('uninstall requires --yes', async () => {
|
||||
const out = await runCli(['uninstall', '--json']);
|
||||
assert.equal(out.ok, false);
|
||||
assert.match(out.error, /--yes/);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue