feat(installer): add runtime manager core library

This commit is contained in:
ZenchantLive 2026-03-02 20:36:09 -08:00
parent a3ca82bb5a
commit 0f33a653d3
3 changed files with 56 additions and 1 deletions

View file

@ -9,7 +9,7 @@
"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/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",
"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/install-wrappers-contract.test.ts && node --import tsx --test tests/scripts/install-sh-smoke.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",
"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"

View file

@ -0,0 +1,41 @@
import path from 'node:path';
const SEMVER_PATTERN = /^v?\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/;
function ensureNonEmpty(value: string, field: string): string {
const normalized = value.trim();
if (!normalized) {
throw new Error(`${field} must be a non-empty string`);
}
return normalized;
}
export function normalizeVersion(version: string): string {
const normalized = ensureNonEmpty(version, 'version');
if (!SEMVER_PATTERN.test(normalized)) {
throw new Error(`version must be semver-compatible: ${version}`);
}
return normalized.startsWith('v') ? normalized.slice(1) : normalized;
}
export function resolveInstallHome(env: NodeJS.ProcessEnv): string {
const home = env.BB_INSTALL_HOME ?? env.HOME ?? env.USERPROFILE;
return ensureNonEmpty(home ?? '', 'install home');
}
export function getRuntimePaths(home: string, version: string) {
const installHome = ensureNonEmpty(home, 'home');
const normalizedVersion = normalizeVersion(version);
const beadboardHome = path.join(installHome, '.beadboard');
const runtimeBase = path.join(beadboardHome, 'runtime');
return {
installHome,
beadboardHome,
runtimeBase,
runtimeRoot: path.join(runtimeBase, normalizedVersion),
runtimeCurrentMetadata: path.join(runtimeBase, 'current.json'),
shimDir: path.join(beadboardHome, 'bin'),
shimNames: ['bb', 'beadboard'] as const,
};
}

View file

@ -0,0 +1,14 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { getRuntimePaths, normalizeVersion } from '../../src/lib/runtime-manager';
test('normalizeVersion supports semver and rejects empty', () => {
assert.equal(normalizeVersion('1.2.3'), '1.2.3');
assert.throws(() => normalizeVersion(''));
});
test('getRuntimePaths builds ~/.beadboard/runtime/<version> layout', () => {
const p = getRuntimePaths('/tmp/home', '1.2.3');
assert.match(p.runtimeRoot, /runtime\/1\.2\.3$/);
assert.match(p.shimDir, /\.beadboard\/bin$/);
});