wiwiway / src /lib /db /versionManager.ts
Claude Code
Sync gateway codebase without binaries
116b4cb
Raw
History Blame Contribute Delete
10.3 kB
/** Version manager tool state persistence. */
import { getDbInstance } from "./core";
interface VersionManagerRow {
id?: unknown;
tool?: unknown;
current_version?: unknown;
installed_version?: unknown;
pinned_version?: unknown;
binary_path?: unknown;
status?: unknown;
pid?: unknown;
port?: unknown;
api_key?: unknown;
management_key?: unknown;
auto_update?: unknown;
auto_start?: unknown;
last_health_check?: unknown;
last_update_check?: unknown;
health_status?: unknown;
config_overrides?: unknown;
error_message?: unknown;
created_at?: unknown;
updated_at?: unknown;
}
function toRecord(value: unknown): Record<string, unknown> {
return value && typeof value === "object" ? (value as Record<string, unknown>) : {};
}
function parseConfigOverrides(value: unknown): Record<string, unknown> | null {
if (!value || typeof value !== "string" || value.trim() === "") return null;
try {
const parsed = JSON.parse(value);
return typeof parsed === "object" && parsed !== null ? parsed : null;
} catch {
return null;
}
}
function stringifyConfigOverrides(value: Record<string, unknown> | null): string | null {
if (value === null) return null;
try {
return JSON.stringify(value);
} catch {
return null;
}
}
interface VersionManagerTool {
id: number;
tool: string;
currentVersion: string | null;
installedVersion: string | null;
pinnedVersion: string | null;
binaryPath: string | null;
status: string;
pid: number | null;
port: number;
apiKey: string | null;
managementKey: string | null;
autoUpdate: boolean;
autoStart: boolean;
lastHealthCheck: string | null;
lastUpdateCheck: string | null;
healthStatus: string;
configOverrides: Record<string, unknown> | null;
errorMessage: string | null;
createdAt: string;
updatedAt: string;
}
function rowToVersionManager(row: VersionManagerRow): VersionManagerTool {
const record = toRecord(row);
return {
id: typeof record.id === "number" ? record.id : 0,
tool: typeof record.tool === "string" ? record.tool : "",
currentVersion:
record.current_version === null
? null
: typeof record.current_version === "string"
? record.current_version
: null,
installedVersion:
record.installed_version === null
? null
: typeof record.installed_version === "string"
? record.installed_version
: null,
pinnedVersion:
record.pinned_version === null
? null
: typeof record.pinned_version === "string"
? record.pinned_version
: null,
binaryPath:
record.binary_path === null
? null
: typeof record.binary_path === "string"
? record.binary_path
: null,
status: typeof record.status === "string" ? record.status : "not_installed",
pid: record.pid === null ? null : typeof record.pid === "number" ? record.pid : null,
port: typeof record.port === "number" ? record.port : 8317,
apiKey:
record.api_key === null ? null : typeof record.api_key === "string" ? record.api_key : null,
managementKey:
record.management_key === null
? null
: typeof record.management_key === "string"
? record.management_key
: null,
autoUpdate:
record.auto_update === 1 || record.auto_update === true || record.auto_update === "1",
autoStart: record.auto_start === 1 || record.auto_start === true || record.auto_start === "1",
lastHealthCheck:
record.last_health_check === null
? null
: typeof record.last_health_check === "string"
? record.last_health_check
: null,
lastUpdateCheck:
record.last_update_check === null
? null
: typeof record.last_update_check === "string"
? record.last_update_check
: null,
healthStatus: typeof record.health_status === "string" ? record.health_status : "unknown",
configOverrides: parseConfigOverrides(record.config_overrides),
errorMessage:
record.error_message === null
? null
: typeof record.error_message === "string"
? record.error_message
: null,
createdAt: typeof record.created_at === "string" ? record.created_at : "",
updatedAt: typeof record.updated_at === "string" ? record.updated_at : "",
};
}
export async function getVersionManagerStatus(): Promise<VersionManagerTool[]> {
const db = getDbInstance();
const rows = db.prepare("SELECT * FROM version_manager").all() as VersionManagerRow[];
return rows.map(rowToVersionManager);
}
export async function getVersionManagerTool(tool: string): Promise<VersionManagerTool | null> {
const db = getDbInstance();
const row = db.prepare("SELECT * FROM version_manager WHERE tool = ?").get(tool) as
| VersionManagerRow
| undefined;
if (!row) return null;
return rowToVersionManager(row);
}
export async function upsertVersionManagerTool(data: {
tool: string;
currentVersion?: string | null;
installedVersion?: string | null;
pinnedVersion?: string | null;
binaryPath?: string | null;
status?: string;
pid?: number | null;
port?: number;
apiKey?: string | null;
managementKey?: string | null;
autoUpdate?: boolean;
autoStart?: boolean;
healthStatus?: string;
configOverrides?: Record<string, unknown> | null;
errorMessage?: string | null;
}): Promise<VersionManagerTool> {
const db = getDbInstance();
db.prepare(
`
INSERT INTO version_manager (
tool, current_version, installed_version, pinned_version, binary_path,
status, pid, port, api_key, management_key, auto_update, auto_start,
health_status, config_overrides, error_message, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))
ON CONFLICT(tool) DO UPDATE SET
current_version = excluded.current_version,
installed_version = excluded.installed_version,
pinned_version = excluded.pinned_version,
binary_path = excluded.binary_path,
status = excluded.status,
pid = excluded.pid,
port = excluded.port,
api_key = excluded.api_key,
management_key = excluded.management_key,
auto_update = excluded.auto_update,
auto_start = excluded.auto_start,
health_status = excluded.health_status,
config_overrides = excluded.config_overrides,
error_message = excluded.error_message,
updated_at = datetime('now')
`
).run(
data.tool,
data.currentVersion ?? null,
data.installedVersion ?? null,
data.pinnedVersion ?? null,
data.binaryPath ?? null,
data.status ?? "not_installed",
data.pid ?? null,
data.port ?? 8317,
data.apiKey ?? null,
data.managementKey ?? null,
data.autoUpdate !== undefined ? (data.autoUpdate ? 1 : 0) : 1,
data.autoStart !== undefined ? (data.autoStart ? 1 : 0) : 0,
data.healthStatus ?? "unknown",
stringifyConfigOverrides(data.configOverrides ?? null),
data.errorMessage ?? null
);
const result = await getVersionManagerTool(data.tool);
if (!result) throw new Error("Failed to retrieve inserted version manager tool");
return result;
}
export async function updateVersionManagerTool(
tool: string,
updates: Record<string, unknown>
): Promise<VersionManagerTool | null> {
const db = getDbInstance();
const existing = await getVersionManagerTool(tool);
if (!existing) return null;
const ALLOWED_COLUMNS = new Set([
"currentVersion",
"installedVersion",
"pinnedVersion",
"binaryPath",
"status",
"pid",
"port",
"apiKey",
"managementKey",
"autoUpdate",
"autoStart",
"healthStatus",
"configOverrides",
"errorMessage",
]);
const sets: string[] = ["updated_at = datetime('now')"];
const params: Record<string, unknown> = { tool };
for (const [key, value] of Object.entries(updates)) {
if (!ALLOWED_COLUMNS.has(key)) continue;
const dbKey = key.replace(/([A-Z])/g, "_$1").toLowerCase();
if (key === "configOverrides") {
sets.push("config_overrides = @configOverrides");
params.configOverrides = stringifyConfigOverrides(value as Record<string, unknown> | null);
} else if (key === "autoUpdate" || key === "autoStart") {
sets.push(`${dbKey} = @${key}`);
params[key] = value === true ? 1 : 0;
} else if (value === null) {
sets.push(`${dbKey} = null`);
} else {
sets.push(`${dbKey} = @${key}`);
params[key] = value;
}
}
db.prepare(`UPDATE version_manager SET ${sets.join(", ")} WHERE tool = @tool`).run(params);
return getVersionManagerTool(tool);
}
export async function deleteVersionManagerTool(tool: string): Promise<boolean> {
const db = getDbInstance();
const result = db.prepare("DELETE FROM version_manager WHERE tool = ?").run(tool);
return result.changes > 0;
}
export async function updateToolHealth(tool: string, healthStatus: string): Promise<boolean> {
const db = getDbInstance();
const result = db
.prepare(
"UPDATE version_manager SET health_status = ?, last_health_check = datetime('now') WHERE tool = ?"
)
.run(healthStatus, tool);
return result.changes > 0;
}
export async function updateToolVersion(
tool: string,
field: "current_version" | "installed_version",
version: string
): Promise<boolean> {
const db = getDbInstance();
const result = db
.prepare(`UPDATE version_manager SET ${field} = ?, updated_at = datetime('now') WHERE tool = ?`)
.run(version, tool);
return result.changes > 0;
}
export async function setToolStatus(
tool: string,
status: string,
pid?: number,
errorMessage?: string
): Promise<boolean> {
const db = getDbInstance();
const result = db
.prepare(
pid !== undefined
? "UPDATE version_manager SET status = ?, pid = ?, error_message = ?, updated_at = datetime('now') WHERE tool = ?"
: "UPDATE version_manager SET status = ?, error_message = ?, updated_at = datetime('now') WHERE tool = ?"
)
.run(
...(pid !== undefined
? [status, pid, errorMessage ?? null, tool]
: [status, errorMessage ?? null, tool])
);
return result.changes > 0;
}