mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-05 07:54:38 +00:00
fix(usage): read OpenCode v2 Go API keys from its database
This commit is contained in:
committed by
paseo-bot[bot]
parent
66676d49e6
commit
0ac8175015
@@ -0,0 +1,78 @@
|
||||
import { existsSync } from "node:fs";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { homedir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import { z } from "zod";
|
||||
|
||||
interface Database {
|
||||
prepare(sql: string): { all(...args: string[]): unknown[] };
|
||||
close(): void;
|
||||
}
|
||||
interface Sqlite {
|
||||
DatabaseSync: new (path: string, options: { readOnly: boolean }) => Database;
|
||||
}
|
||||
interface DatabaseLogin {
|
||||
key: string | null;
|
||||
}
|
||||
export interface StorePaths {
|
||||
file: string;
|
||||
database: string;
|
||||
}
|
||||
|
||||
const fileSchema = z.object({
|
||||
"opencode-go": z.object({ type: z.literal("api"), key: z.string().min(1) }).optional(),
|
||||
});
|
||||
const rowSchema = z.object({ value: z.string() });
|
||||
const keySchema = z.object({ type: z.literal("key"), key: z.string().min(1) });
|
||||
|
||||
export function defaultStorePaths(env: NodeJS.ProcessEnv = process.env): StorePaths {
|
||||
const dataHome = env.XDG_DATA_HOME || join(env.HOME || homedir(), ".local", "share");
|
||||
const directory = join(dataHome, "opencode");
|
||||
const database = env.OPENCODE_DB ? resolve(env.OPENCODE_DB) : join(directory, "opencode.db");
|
||||
return { file: join(directory, "auth.json"), database };
|
||||
}
|
||||
|
||||
export async function readFileKey(path: string): Promise<string | null> {
|
||||
try {
|
||||
const auth = fileSchema.parse(JSON.parse(await readFile(path, "utf8")));
|
||||
return auth["opencode-go"]?.key ?? null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function readDatabaseLogin(path: string): DatabaseLogin | null {
|
||||
// Keep the legacy source loadable on Node versions without node:sqlite.
|
||||
const sqlite = z
|
||||
.object({
|
||||
DatabaseSync: z.custom<Sqlite["DatabaseSync"]>((value) => typeof value === "function"),
|
||||
})
|
||||
.safeParse(process.getBuiltinModule?.("node:sqlite"));
|
||||
if (!sqlite.success || !existsSync(path)) return null;
|
||||
const db = new sqlite.data.DatabaseSync(path, { readOnly: true });
|
||||
try {
|
||||
// V1 also has opencode.db, but only v2 has the credential table.
|
||||
const tables = db
|
||||
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'credential'")
|
||||
.all();
|
||||
if (!tables.length) return null;
|
||||
const [value] = db
|
||||
.prepare(
|
||||
"SELECT value FROM credential WHERE integration_id = ? AND active = 1 ORDER BY time_updated DESC, id DESC LIMIT 1",
|
||||
)
|
||||
.all("opencode-go");
|
||||
const row = rowSchema.safeParse(value);
|
||||
if (!row.success) return { key: null };
|
||||
let credential: unknown;
|
||||
try {
|
||||
credential = JSON.parse(row.data.value);
|
||||
} catch (error) {
|
||||
if (error instanceof SyntaxError) return { key: null };
|
||||
throw error;
|
||||
}
|
||||
const parsed = keySchema.safeParse(credential);
|
||||
return { key: parsed.success ? parsed.data.key : null };
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user