Files
platform/src/servers/bootstrap.ts
T
2026-07-23 00:48:44 +00:00

94 lines
3.1 KiB
TypeScript

import { mkdirSync } from 'node:fs';
import { join } from 'node:path';
import { homedir } from 'node:os';
import { DATA_PATH, ensureItemDirs } from './data-path';
import { ensureToolLoader } from './ensure-tool-loader';
// Queue is now owned by the sidecar process
import { startDiscordBotIfConfigured } from './channels/discord/bot';
import { startTelegramBotIfConfigured } from './channels/telegram/bot';
import { startWhatsAppBotIfConfigured } from './channels/whatsapp/bot';
mkdirSync(DATA_PATH, { recursive: true });
ensureItemDirs();
/** Check common locations for the Pi package directory. */
async function findPiPackageDir(): Promise<string | null> {
const candidates = [
join(homedir(), '.npm-global', 'lib', 'node_modules', '@mariozechner', 'pi-coding-agent'),
'/usr/local/lib/node_modules/@mariozechner/pi-coding-agent',
];
for (const dir of candidates) {
if (await Bun.file(join(dir, 'package.json')).exists()) return dir;
}
return null;
}
async function ensurePiInstalled(): Promise<boolean> {
// If the `pi` binary is already on hand (e.g. a newer, manually-installed @earendil-works build),
// treat Pi as installed. Otherwise the old @mariozechner package gets reinstalled over it every boot
// and fails with EEXIST — noisy, and the "model discovery will not work" warning is a false alarm.
const localPi = join(homedir(), '.local', 'bin', 'pi');
if (Bun.which('pi') || (await Bun.file(localPi).exists())) {
console.log('[bootstrap] Pi already available — skipping install');
return true;
}
const dir = await findPiPackageDir();
if (!dir) return false;
try {
const pkg = await Bun.file(join(dir, 'package.json')).json();
if (pkg.version) {
console.log(`[bootstrap] Pi found: ${pkg.version}`);
return true;
}
} catch {}
return false;
}
async function installPi(): Promise<boolean> {
console.log('[bootstrap] Pi not found, installing...');
try {
const proc = Bun.spawn(['npm', 'install', '-g', '@mariozechner/pi-coding-agent'], {
stdout: 'pipe',
stderr: 'pipe',
});
const stderr = await new Response(proc.stderr).text();
await proc.exited;
if (proc.exitCode !== 0) {
console.error('[bootstrap] Pi installation failed:', stderr.trim());
return false;
}
console.log('[bootstrap] Pi installed successfully');
return true;
} catch (err) {
console.error('[bootstrap] Pi installation error:', err);
return false;
}
}
(async () => {
const installed = await ensurePiInstalled();
if (!installed) {
const ok = await installPi();
if (!ok) {
console.error('[bootstrap] Could not install Pi — model discovery will not work');
return;
}
}
ensureToolLoader();
// Queue is initialized by the sidecar process
await startDiscordBotIfConfigured().catch((err) => {
console.error('[channels] Failed to start Discord bot:', err);
});
await startTelegramBotIfConfigured().catch((err) => {
console.error('[channels] Failed to start Telegram bot:', err);
});
await startWhatsAppBotIfConfigured().catch((err) => {
console.error('[channels] Failed to start WhatsApp bot:', err);
});
})();