beadboard/src/lib/pi-runtime-detection.ts
zenchantlive d335e5bf71 fix: orchestrator button + Pi SDK session error
- Move leftSidebarMode from URL state to local useState in unified-shell,
    avoiding force-dynamic router round-trip that made the button appear broken                                           - Replace fileURLToPath(new URL(..., import.meta.url)) with process.cwd()
    in bb-pi-bootstrap.ts — import.meta.url is a webpack:// URL in Next.js,
    causing cross-realm TypeError when passed to Node.js fileURLToPath()
2026-03-24 19:02:04 -05:00

103 lines
3.4 KiB
TypeScript

import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { getRuntimePaths } from './runtime-manager';
export type PiRuntimeMode = 'linked-pi' | 'bb-managed-pi';
export type PiInstallState = 'ready' | 'bootstrap-required';
export interface PiRuntimeResolution {
mode: PiRuntimeMode;
installState: PiInstallState;
sdkPath: string | null;
authPath: string | null;
agentDir: string;
version: string;
managedRoot: string;
reason: string;
}
async function pathExists(target: string): Promise<boolean> {
try {
await fs.access(target);
return true;
} catch {
return false;
}
}
export function getManagedPiPaths(version: string, home: string = os.homedir()) {
const runtime = getRuntimePaths(home, version);
const managedRoot = path.join(runtime.runtimeRoot, 'pi');
return {
managedRoot,
sdkPath: path.join(managedRoot, 'node_modules', '@mariozechner', 'pi-coding-agent', 'dist', 'index.js'),
agentDir: path.join(managedRoot, 'agent'),
authPath: path.join(managedRoot, 'agent', 'auth.json'),
};
}
export async function detectPiRuntimeStrategy(params: {
cwd?: string;
version?: string;
home?: string;
globalPiRoot?: string;
allowLinkedPi?: boolean;
} = {}): Promise<PiRuntimeResolution> {
const cwd = params.cwd ?? process.cwd();
const version = params.version ?? '0.1.0';
const home = params.home ?? os.homedir();
const globalPiRoot = params.globalPiRoot ?? '/home/clawdbot/npm-global/lib/node_modules/@mariozechner/pi-coding-agent';
const allowLinkedPi = params.allowLinkedPi ?? false;
const localSdkPath = path.join(cwd, 'node_modules', '@mariozechner', 'pi-coding-agent', 'dist', 'index.js');
const globalSdkPath = path.join(globalPiRoot, 'dist', 'index.js');
const linkedAuthPath = path.join(home, '.pi', 'agent', 'auth.json');
const linkedAgentDir = path.join(home, '.pi', 'agent');
const managed = getManagedPiPaths(version, home);
if (allowLinkedPi && await pathExists(localSdkPath)) {
return {
mode: 'linked-pi',
installState: 'ready',
sdkPath: localSdkPath,
authPath: (await pathExists(linkedAuthPath)) ? linkedAuthPath : null,
agentDir: linkedAgentDir,
version,
managedRoot: managed.managedRoot,
reason: 'Using project-local Pi SDK and linked ~/.pi/agent state when available.',
};
}
if (allowLinkedPi && await pathExists(globalSdkPath)) {
return {
mode: 'linked-pi',
installState: 'ready',
sdkPath: globalSdkPath,
authPath: (await pathExists(linkedAuthPath)) ? linkedAuthPath : null,
agentDir: linkedAgentDir,
version,
managedRoot: managed.managedRoot,
reason: 'Using globally installed Pi SDK and linked ~/.pi/agent state when available.',
};
}
const managedSdkExists = await pathExists(managed.sdkPath);
const managedAuthExists = await pathExists(managed.authPath);
return {
mode: 'bb-managed-pi',
installState: managedSdkExists ? 'ready' : 'bootstrap-required',
sdkPath: managedSdkExists ? managed.sdkPath : null,
authPath: managedAuthExists ? managed.authPath : null,
agentDir: managed.agentDir,
version,
managedRoot: managed.managedRoot,
reason: managedSdkExists
? 'Using BeadBoard-managed Pi runtime.'
: allowLinkedPi
? 'No linked Pi installation found. BeadBoard-managed Pi bootstrap is required.'
: 'BeadBoard-managed Pi bootstrap is required.',
};
}