import packageJson from "../../package.json" with { type: "json" }; import { migrationManifest } from "../persistence/Migrations.ts"; import { SERVICE_LAUNCHER_PROTOCOL } from "./serviceProtocol.ts"; export type ServicePreflightResult = | { readonly status: "ready"; readonly version: string; readonly launcherProtocol: typeof SERVICE_LAUNCHER_PROTOCOL; /** The launcher compares these with the database to decide whether the update needs a backup. */ readonly migrations?: typeof migrationManifest; } | { readonly status: "blocked"; readonly version: string; readonly reason: string; }; export function runServicePreflight(input: { readonly databasePath: string; readonly launcherProtocol: number; readonly version?: string; }): ServicePreflightResult { const version = input.version ?? packageJson.version; if (input.launcherProtocol !== SERVICE_LAUNCHER_PROTOCOL) { return { status: "blocked", version, // A stale `t3` earlier on PATH (an old npm/nvm global) makes `t3 service install` // reinstall itself and report success, so name the exact install and the path check. reason: [ `The installed service launcher speaks protocol ${input.launcherProtocol}, but t3@${version} requires protocol ${SERVICE_LAUNCHER_PROTOCOL}.`, `On the server machine, install this exact CLI: \`curl -fsSL https://t3.codes/install.sh | T3CODE_VERSION=${version} sh\`.`, `Check that \`t3 --version\` prints ${version} and that \`which -a t3\` lists no older t3 (such as an npm or nvm install) first, then run \`~/.local/bin/t3 service install\` (the installer's default path).`, "Updating the desktop app does not update this CLI, and `t3 service update` only repairs the service with whichever CLI runs it.", ].join(" "), }; } return { status: "ready", version, launcherProtocol: SERVICE_LAUNCHER_PROTOCOL, migrations: migrationManifest, }; } export function decodeServicePreflightResult(value: unknown): ServicePreflightResult | undefined { if (typeof value !== "object" || value === null) { return undefined; } const record = value as Record; if ( record.status === "ready" && record.launcherProtocol === SERVICE_LAUNCHER_PROTOCOL && typeof record.version === "string" ) { return { status: "ready", version: record.version, launcherProtocol: SERVICE_LAUNCHER_PROTOCOL, }; } if ( record.status === "blocked" && typeof record.version === "string" && typeof record.reason === "string" ) { return { status: "blocked", version: record.version, reason: record.reason }; } return undefined; }