mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-03 20:44:07 +00:00
* feat: add per-agent sqlite cache store * fix: preserve sqlite cache adapter scope * chore: mark sqlite cache scaffold intentional
162 lines
4.7 KiB
JavaScript
162 lines
4.7 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import fs from "node:fs";
|
|
import process from "node:process";
|
|
import { DatabaseSync } from "node:sqlite";
|
|
|
|
const SCHEMAS = [
|
|
{
|
|
name: "openclaw-state",
|
|
schema: "src/state/openclaw-state-schema.sql",
|
|
outFile: "src/state/openclaw-state-db.generated.d.ts",
|
|
schemaOutFile: "src/state/openclaw-state-schema.generated.ts",
|
|
schemaExport: "OPENCLAW_STATE_SCHEMA_SQL",
|
|
},
|
|
{
|
|
name: "openclaw-agent",
|
|
schema: "src/state/openclaw-agent-schema.sql",
|
|
outFile: "src/state/openclaw-agent-db.generated.d.ts",
|
|
schemaOutFile: "src/state/openclaw-agent-schema.generated.ts",
|
|
schemaExport: "OPENCLAW_AGENT_SCHEMA_SQL",
|
|
},
|
|
];
|
|
|
|
const verify = process.argv.includes("--verify") || process.argv.includes("--check");
|
|
|
|
function toInterfaceName(tableName) {
|
|
return tableName
|
|
.split("_")
|
|
.map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`)
|
|
.join("");
|
|
}
|
|
|
|
function columnBaseType(columnType) {
|
|
const normalized = columnType.toUpperCase();
|
|
if (normalized.includes("BLOB")) {
|
|
return "Uint8Array";
|
|
}
|
|
if (
|
|
normalized.includes("INT") ||
|
|
normalized.includes("REAL") ||
|
|
normalized.includes("FLOA") ||
|
|
normalized.includes("DOUB") ||
|
|
normalized.includes("NUM") ||
|
|
normalized.includes("DEC")
|
|
) {
|
|
return "number";
|
|
}
|
|
return "string";
|
|
}
|
|
|
|
function columnType(column, primaryKeyColumnCount) {
|
|
const baseType = columnBaseType(String(column.type ?? ""));
|
|
const generated =
|
|
column.dflt_value != null ||
|
|
(primaryKeyColumnCount === 1 &&
|
|
Number(column.pk) > 0 &&
|
|
String(column.type ?? "")
|
|
.toUpperCase()
|
|
.includes("INT"));
|
|
const nullable = Number(column.notnull) !== 1 && !generated;
|
|
const valueType = nullable ? `${baseType} | null` : baseType;
|
|
return generated ? `Generated<${valueType}>` : valueType;
|
|
}
|
|
|
|
function quoteSqliteIdentifier(identifier) {
|
|
return `"${identifier.replaceAll('"', '""')}"`;
|
|
}
|
|
|
|
function generateTypes(db) {
|
|
const tables = db
|
|
.prepare(
|
|
"SELECT name FROM sqlite_schema WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name;",
|
|
)
|
|
.all()
|
|
.map((row) => String(row.name));
|
|
const lines = [
|
|
"/**",
|
|
" * This file was generated by kysely-codegen.",
|
|
" * Please do not edit it manually.",
|
|
" */",
|
|
"",
|
|
'import type { ColumnType } from "kysely";',
|
|
"",
|
|
"export type Generated<T> =",
|
|
" T extends ColumnType<infer S, infer I, infer U>",
|
|
" ? ColumnType<S, I | undefined, U>",
|
|
" : ColumnType<T, T | undefined, T>;",
|
|
"",
|
|
];
|
|
|
|
const interfaces = [];
|
|
for (const table of tables) {
|
|
const interfaceName = toInterfaceName(table);
|
|
interfaces.push({ interfaceName, table });
|
|
lines.push(`export interface ${interfaceName} {`);
|
|
const columns = db
|
|
.prepare(`PRAGMA table_xinfo(${quoteSqliteIdentifier(table)});`)
|
|
.all()
|
|
.filter((column) => Number(column.hidden) === 0)
|
|
.toSorted((left, right) => String(left.name).localeCompare(String(right.name)));
|
|
const primaryKeyColumnCount = columns.filter((column) => Number(column.pk) > 0).length;
|
|
for (const column of columns) {
|
|
lines.push(` ${column.name}: ${columnType(column, primaryKeyColumnCount)};`);
|
|
}
|
|
lines.push("}", "");
|
|
}
|
|
|
|
lines.push("export interface DB {");
|
|
for (const { interfaceName, table } of interfaces) {
|
|
lines.push(` ${table}: ${interfaceName};`);
|
|
}
|
|
lines.push("}", "");
|
|
return lines.join("\n");
|
|
}
|
|
|
|
function readUtf8(file) {
|
|
return fs.readFileSync(file, "utf8");
|
|
}
|
|
|
|
function generatedSchemaModule(schema) {
|
|
const source = readUtf8(schema.schema).trimEnd();
|
|
const literal = source.replaceAll("\\", "\\\\").replaceAll("`", "\\`").replaceAll("${", "\\${");
|
|
return [
|
|
"/**",
|
|
" * This file was generated from the SQLite schema source.",
|
|
" * Please do not edit it manually.",
|
|
" */",
|
|
"",
|
|
`export const ${schema.schemaExport} = \`${literal}\\n\`;`,
|
|
"",
|
|
].join("\n");
|
|
}
|
|
|
|
function generate(schema) {
|
|
const db = new DatabaseSync(":memory:");
|
|
try {
|
|
db.exec(readUtf8(schema.schema));
|
|
const typesSource = generateTypes(db);
|
|
const schemaSource = generatedSchemaModule(schema);
|
|
|
|
if (verify) {
|
|
if (typesSource !== readUtf8(schema.outFile)) {
|
|
console.error(`${schema.outFile} is out of date. Run pnpm db:kysely:gen.`);
|
|
process.exitCode = 1;
|
|
}
|
|
if (schemaSource !== readUtf8(schema.schemaOutFile)) {
|
|
console.error(`${schema.schemaOutFile} is out of date. Run pnpm db:kysely:gen.`);
|
|
process.exitCode = 1;
|
|
}
|
|
} else {
|
|
fs.writeFileSync(schema.outFile, typesSource);
|
|
fs.writeFileSync(schema.schemaOutFile, schemaSource);
|
|
}
|
|
} finally {
|
|
db.close();
|
|
}
|
|
}
|
|
|
|
for (const schema of SCHEMAS) {
|
|
generate(schema);
|
|
}
|