/** * Local dev bootstrap for an Alpha Silo on Windows / non-systemd hosts. * * The production bootstrap-silo CLI is Linux-only (requires root uid 0, * systemctl, root-owned files). This script calls the same * `bootstrapAlphaSilo` invariants directly, sourcing credentials from the * local `.env` and the dev keyring. Idempotent: re-running is a no-op once * the Silo Organization exists. * * Usage: npx tsx scripts/dev-bootstrap-silo.ts */ import "dotenv/config"; import { PrismaClient } from "@prisma/client"; import { bootstrapAlphaSilo } from "../src/deployment/bootstrap-silo.js"; import { loadLocalSecretKeyring, LocalSecretEnvelope } from "../src/security/secretEnvelope.js"; function requiredEnv(name: string): string { const v = process.env[name]?.trim(); if (v === undefined || v === "") throw new Error(`missing required env: ${name}`); return v; } async function main(): Promise { const databaseUrl = requiredEnv("DATABASE_URL"); const keyring = await loadLocalSecretKeyring(); const secrets = new LocalSecretEnvelope(keyring); const prisma = new PrismaClient({ datasources: { db: { url: databaseUrl } }, log: [] }); const organizationId = requiredEnv("HUB_SILO_ORGANIZATION_ID"); const feishuAppId = requiredEnv("FEISHU_APP_ID"); const feishuAppSecret = requiredEnv("FEISHU_APP_SECRET"); const feishuBotOpenId = requiredEnv("FEISHU_BOT_OPEN_ID"); const providerAuthToken = requiredEnv("ANTHROPIC_AUTH_TOKEN"); const providerBaseUrl = process.env["ANTHROPIC_BASE_URL"]?.trim() || "https://openrouter.ai/api"; const anthropicApiKey = process.env["ANTHROPIC_API_KEY"]?.trim() || ""; try { const result = await bootstrapAlphaSilo( prisma, secrets, { organization: { id: organizationId, slug: "local-dev", name: "Local Dev Silo", }, owner: { openId: feishuBotOpenId, displayName: "Local Dev Owner", }, feishu: { appId: feishuAppId, appSecret: feishuAppSecret, botOpenId: feishuBotOpenId, }, provider: { providerId: "openrouter", baseUrl: providerBaseUrl, authToken: providerAuthToken, ...(anthropicApiKey !== "" ? { anthropicApiKey } : {}), }, }, // Skip live network probes in local dev — Feishu/OpenRouter reachability // is not required to seed the encrypted envelope rows. { feishu: async () => {}, provider: async () => {} }, ); console.log("bootstrap result:", JSON.stringify(result, null, 2)); } finally { await prisma.$disconnect(); } } main().catch((error: unknown) => { console.error("[dev-bootstrap-silo] failed:", error instanceof Error ? error.message : String(error)); process.exitCode = 1; });