openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
1,352 lines • 54.8 kB
JavaScript
import { c as isRecord } from "../../../record-coerce-TIsquoVR.mjs";
import { a as iterateSqliteQuerySync, i as getNodeSqliteKysely, l as sqliteStringSet, r as executeSqliteQueryTakeFirstSync, t as compileSqliteQueryBindings } from "../../../kysely-sync-BMSZn9yz.mjs";
import { t as openNodeSqliteDatabase } from "../../../node-sqlite-DWTcT2ek.mjs";
import { a as runSqliteImmediateTransactionSync, r as runSqliteDeferredTransactionSync } from "../../../sqlite-transaction-DlnLAQiX.mjs";
import { t as configureSqliteConnectionPragmas } from "../../../sqlite-wal-BeoOh_h5.mjs";
import { t as migrateSqliteSchemaToStrict } from "../../../sqlite-strict-CZdPlBht.mjs";
import "../../../string-coerce-runtime-B1b8UNzb.mjs";
import "../../../sqlite-worker-runtime-CSSFY-RJ.mjs";
import "../../../plugin-state-runtime-CTOZJyIN.mjs";
import { m as workboardCardSlotOwner, p as workboardCardConsumesOwnerSlot } from "../../../store-constants-CRrpX9yG.mjs";
import { t as encodeWorkboardSqliteFailure } from "../../../sqlite-store-errors-fETz-1pd.mjs";
import fs from "node:fs";
import path from "node:path";
//#region extensions/workboard/src/sqlite-store-records.ts
function jsonValue(value) {
return value === void 0 ? null : JSON.stringify(value);
}
function parseJson(value) {
if (typeof value !== "string" || !value) return;
return JSON.parse(value);
}
function stringValue(row, key) {
const value = row[key];
return typeof value === "string" && value.length > 0 ? value : void 0;
}
function numberValue(row, key) {
const value = row[key];
if (typeof value === "number") return Number.isFinite(value) ? value : void 0;
if (typeof value === "bigint") return Number(value);
}
function requiredString(row, key) {
const value = stringValue(row, key);
if (!value) throw new Error(`workboard sqlite row missing ${key}`);
return value;
}
function requiredNumber(row, key) {
const value = numberValue(row, key);
if (value === void 0) throw new Error(`workboard sqlite row missing ${key}`);
return value;
}
function optional(value) {
return Object.keys(value).length > 0 ? value : void 0;
}
function definedFields(fields) {
for (const key in fields) if (fields[key] === void 0) delete fields[key];
return fields;
}
function asBlobContent(value) {
return Buffer.from(value, "base64");
}
function blobToBase64(value) {
if (value instanceof Uint8Array) return Buffer.from(value).toString("base64");
if (typeof value === "string") return Buffer.from(value).toString("base64");
return "";
}
const CARD_CHILD_TABLES = [
"workboard_card_labels",
"workboard_card_events",
"workboard_card_attempts",
"workboard_card_comments",
"workboard_card_links",
"workboard_card_proof",
"workboard_card_artifacts",
"workboard_card_attachments",
"workboard_worker_logs",
"workboard_card_diagnostics",
"workboard_card_notifications"
];
function groupByCardId(rows) {
const grouped = /* @__PURE__ */ new Map();
for (const row of rows) {
const cardId = stringValue(row, "card_id");
if (!cardId) continue;
const bucket = grouped.get(cardId);
if (bucket) bucket.push(row);
else grouped.set(cardId, [row]);
}
return grouped;
}
function loadCardChildRows(db, cardIds) {
const query = getNodeSqliteKysely(db);
const selectedIds = cardIds ? sqliteStringSet(cardIds) : void 0;
const byTable = /* @__PURE__ */ new Map();
for (const table of CARD_CHILD_TABLES) {
let rows = query.selectFrom(table).selectAll().orderBy("card_id", "asc").orderBy("ordinal", "asc");
if (selectedIds) rows = rows.where("card_id", "in", selectedIds);
byTable.set(table, groupByCardId(iterateSqliteQuerySync(db, rows)));
}
const workerProtocol = /* @__PURE__ */ new Map();
let protocols = query.selectFrom("workboard_worker_protocol").selectAll();
if (selectedIds) protocols = protocols.where("card_id", "in", selectedIds);
for (const row of iterateSqliteQuerySync(db, protocols)) {
const cardId = stringValue(row, "card_id");
if (cardId) workerProtocol.set(cardId, row);
}
return {
byTable,
workerProtocol
};
}
function childRows(db, table, cardId, preloaded) {
const cached = preloaded?.byTable.get(table);
if (cached) {
const rows = cached.get(cardId) ?? [];
cached.delete(cardId);
return rows;
}
return db.prepare(`SELECT * FROM ${table} WHERE card_id = ? ORDER BY ordinal ASC`).all(cardId);
}
function workerProtocolRow(db, cardId, preloaded) {
if (preloaded) {
const row = preloaded.workerProtocol.get(cardId);
preloaded.workerProtocol.delete(cardId);
return row;
}
return db.prepare("SELECT * FROM workboard_worker_protocol WHERE card_id = ?").get(cardId);
}
function readLabels(db, cardId, preloaded) {
return childRows(db, "workboard_card_labels", cardId, preloaded).flatMap((row) => {
const label = stringValue(row, "label");
return label ? [label] : [];
});
}
function readEvents(db, cardId, preloaded) {
const events = childRows(db, "workboard_card_events", cardId, preloaded).map((row) => {
return definedFields({
id: requiredString(row, "id"),
kind: requiredString(row, "kind"),
at: requiredNumber(row, "at"),
fromStatus: stringValue(row, "from_status"),
toStatus: stringValue(row, "to_status"),
sessionKey: stringValue(row, "session_key"),
runId: stringValue(row, "run_id")
});
});
return events.length > 0 ? events : void 0;
}
function readExecution(row) {
const id = stringValue(row, "execution_id");
if (!id) return;
return definedFields({
id,
kind: "agent-session",
mode: requiredString(row, "execution_mode"),
status: requiredString(row, "execution_status"),
engine: stringValue(row, "execution_engine"),
model: stringValue(row, "execution_model"),
sessionKey: stringValue(row, "execution_session_key"),
runId: stringValue(row, "execution_run_id"),
startedAt: requiredNumber(row, "execution_started_at"),
updatedAt: requiredNumber(row, "execution_updated_at")
});
}
function readAttachment(row) {
return definedFields({
id: requiredString(row, "id"),
cardId: requiredString(row, "card_id"),
createdAt: requiredNumber(row, "created_at"),
fileName: requiredString(row, "file_name"),
byteSize: requiredNumber(row, "byte_size"),
mimeType: stringValue(row, "mime_type"),
note: stringValue(row, "note")
});
}
function readMetadata(db, row, preloaded) {
const cardId = requiredString(row, "id");
const attempts = childRows(db, "workboard_card_attempts", cardId, preloaded).map((child) => {
return definedFields({
id: requiredString(child, "id"),
status: requiredString(child, "status"),
startedAt: requiredNumber(child, "started_at"),
endedAt: numberValue(child, "ended_at"),
engine: stringValue(child, "engine"),
mode: stringValue(child, "mode"),
model: stringValue(child, "model"),
sessionKey: stringValue(child, "session_key"),
runId: stringValue(child, "run_id"),
error: stringValue(child, "error")
});
});
const comments = childRows(db, "workboard_card_comments", cardId, preloaded).map((child) => {
return definedFields({
id: requiredString(child, "id"),
body: requiredString(child, "body"),
createdAt: requiredNumber(child, "created_at"),
updatedAt: numberValue(child, "updated_at")
});
});
const links = childRows(db, "workboard_card_links", cardId, preloaded).map((child) => {
return definedFields({
id: requiredString(child, "id"),
type: requiredString(child, "type"),
createdAt: requiredNumber(child, "created_at"),
targetCardId: stringValue(child, "target_card_id"),
title: stringValue(child, "title"),
url: stringValue(child, "url")
});
});
const proof = childRows(db, "workboard_card_proof", cardId, preloaded).map((child) => {
return definedFields({
id: requiredString(child, "id"),
status: requiredString(child, "status"),
createdAt: requiredNumber(child, "created_at"),
label: stringValue(child, "label"),
command: stringValue(child, "command"),
url: stringValue(child, "url"),
note: stringValue(child, "note")
});
});
const artifacts = childRows(db, "workboard_card_artifacts", cardId, preloaded).map((child) => {
return definedFields({
id: requiredString(child, "id"),
createdAt: requiredNumber(child, "created_at"),
label: stringValue(child, "label"),
url: stringValue(child, "url"),
path: stringValue(child, "path"),
mimeType: stringValue(child, "mime_type")
});
});
const attachments = childRows(db, "workboard_card_attachments", cardId, preloaded).map(readAttachment);
const workerLogs = childRows(db, "workboard_worker_logs", cardId, preloaded).map((child) => {
return definedFields({
id: requiredString(child, "id"),
createdAt: requiredNumber(child, "created_at"),
level: requiredString(child, "level"),
message: requiredString(child, "message"),
sessionKey: stringValue(child, "session_key"),
runId: stringValue(child, "run_id")
});
});
const diagnostics = childRows(db, "workboard_card_diagnostics", cardId, preloaded).map((child) => ({
kind: requiredString(child, "kind"),
severity: requiredString(child, "severity"),
title: requiredString(child, "title"),
detail: requiredString(child, "detail"),
firstSeenAt: requiredNumber(child, "first_seen_at"),
lastSeenAt: requiredNumber(child, "last_seen_at"),
count: requiredNumber(child, "count"),
actions: parseJson(child.actions_json) ?? []
}));
const notifications = childRows(db, "workboard_card_notifications", cardId, preloaded).map((child) => {
return definedFields({
id: requiredString(child, "id"),
kind: requiredString(child, "kind"),
createdAt: requiredNumber(child, "created_at"),
message: requiredString(child, "message"),
sequence: numberValue(child, "sequence"),
sessionKey: stringValue(child, "session_key"),
runId: stringValue(child, "run_id")
});
});
const protocol = workerProtocolRow(db, cardId, preloaded);
const automation = parseJson(row.automation_json);
const claim = parseJson(row.claim_json);
const stale = parseJson(row.stale_json);
const lifecycleStatusSourceUpdatedAt = numberValue(row, "lifecycle_status_source_updated_at");
return optional({
...attempts.length > 0 ? { attempts } : {},
...comments.length > 0 ? { comments } : {},
...links.length > 0 ? { links } : {},
...proof.length > 0 ? { proof } : {},
...artifacts.length > 0 ? { artifacts } : {},
...attachments.length > 0 ? { attachments } : {},
...workerLogs.length > 0 ? { workerLogs } : {},
...protocol ? { workerProtocol: {
state: requiredString(protocol, "state"),
updatedAt: requiredNumber(protocol, "updated_at"),
...stringValue(protocol, "detail") ? { detail: stringValue(protocol, "detail") } : {}
} } : {},
...automation ? { automation } : {},
...claim ? { claim } : {},
...diagnostics.length > 0 ? { diagnostics } : {},
...notifications.length > 0 ? { notifications } : {},
...stringValue(row, "template_id") ? { templateId: stringValue(row, "template_id") } : {},
...numberValue(row, "archived_at") !== void 0 ? { archivedAt: numberValue(row, "archived_at") } : {},
...stale ? { stale } : {},
...lifecycleStatusSourceUpdatedAt !== void 0 ? { lifecycleStatusSourceUpdatedAt } : {},
...numberValue(row, "failure_count") !== void 0 ? { failureCount: numberValue(row, "failure_count") } : {}
});
}
function readCard(db, row, preloaded) {
const card = {
id: requiredString(row, "id"),
title: requiredString(row, "title"),
status: requiredString(row, "status"),
priority: requiredString(row, "priority"),
labels: readLabels(db, requiredString(row, "id"), preloaded),
position: requiredNumber(row, "position"),
createdAt: requiredNumber(row, "created_at"),
updatedAt: requiredNumber(row, "updated_at")
};
const metadata = readMetadata(db, row, preloaded);
const events = readEvents(db, card.id, preloaded);
const execution = readExecution(row);
return definedFields({
...card,
notes: stringValue(row, "notes"),
agentId: stringValue(row, "agent_id"),
sessionKey: stringValue(row, "session_key"),
runId: stringValue(row, "run_id"),
sourceUrl: stringValue(row, "source_url"),
execution,
startedAt: numberValue(row, "started_at"),
completedAt: numberValue(row, "completed_at"),
events,
metadata
});
}
//#endregion
//#region extensions/workboard/src/sqlite-store-schema.ts
const SCHEMA_VERSION = 3;
const WORKBOARD_SQLITE_BUSY_TIMEOUT_MS = 5e3;
const WORKBOARD_SQLITE_DIR_MODE = 448;
const WORKBOARD_SQLITE_FILE_MODE = 384;
function tableColumns(db, tableName) {
return new Set(db.prepare(`PRAGMA table_info(${tableName})`).all().flatMap((row) => typeof row.name === "string" ? [row.name] : []));
}
function ensureColumn(db, tableName, columnName, definition) {
if (tableColumns(db, tableName).has(columnName)) return;
db.exec(`ALTER TABLE ${tableName} ADD COLUMN ${definition}`);
}
const WORKBOARD_SCHEMA_SQL = `
CREATE TABLE IF NOT EXISTS workboard_schema_migrations (
id TEXT PRIMARY KEY,
applied_at INTEGER NOT NULL
) STRICT;
CREATE TABLE IF NOT EXISTS workboard_boards (
id TEXT PRIMARY KEY,
name TEXT,
description TEXT,
icon TEXT,
color TEXT,
automation_job_id TEXT,
default_workspace_json TEXT,
orchestration_json TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
archived_at INTEGER
) STRICT;
CREATE TABLE IF NOT EXISTS workboard_cards (
id TEXT PRIMARY KEY,
board_id TEXT NOT NULL,
title TEXT NOT NULL,
notes TEXT,
status TEXT NOT NULL,
priority TEXT NOT NULL,
agent_id TEXT,
session_key TEXT,
run_id TEXT,
task_id TEXT,
source_url TEXT,
position REAL NOT NULL,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
started_at INTEGER,
completed_at INTEGER,
execution_id TEXT,
execution_kind TEXT,
execution_engine TEXT,
execution_mode TEXT,
execution_status TEXT,
execution_model TEXT,
execution_session_key TEXT,
execution_run_id TEXT,
execution_started_at INTEGER,
execution_updated_at INTEGER,
automation_json TEXT,
claim_json TEXT,
template_id TEXT,
archived_at INTEGER,
stale_json TEXT,
lifecycle_status_source_updated_at INTEGER,
failure_count INTEGER
) STRICT;
CREATE INDEX IF NOT EXISTS workboard_cards_board_status_idx
ON workboard_cards(board_id, status, position);
CREATE INDEX IF NOT EXISTS workboard_cards_session_idx
ON workboard_cards(session_key, run_id);
CREATE TABLE IF NOT EXISTS workboard_card_labels (
card_id TEXT NOT NULL REFERENCES workboard_cards(id) ON DELETE CASCADE,
ordinal INTEGER NOT NULL,
label TEXT NOT NULL,
PRIMARY KEY(card_id, ordinal)
) STRICT;
CREATE TABLE IF NOT EXISTS workboard_card_events (
id TEXT PRIMARY KEY,
card_id TEXT NOT NULL REFERENCES workboard_cards(id) ON DELETE CASCADE,
ordinal INTEGER NOT NULL,
kind TEXT NOT NULL,
at INTEGER NOT NULL,
from_status TEXT,
to_status TEXT,
session_key TEXT,
run_id TEXT
) STRICT;
CREATE INDEX IF NOT EXISTS workboard_card_events_card_idx
ON workboard_card_events(card_id, ordinal);
CREATE TABLE IF NOT EXISTS workboard_card_attempts (
id TEXT PRIMARY KEY,
card_id TEXT NOT NULL REFERENCES workboard_cards(id) ON DELETE CASCADE,
ordinal INTEGER NOT NULL,
status TEXT NOT NULL,
started_at INTEGER NOT NULL,
ended_at INTEGER,
engine TEXT,
mode TEXT,
model TEXT,
session_key TEXT,
run_id TEXT,
error TEXT
) STRICT;
CREATE INDEX IF NOT EXISTS workboard_card_attempts_card_idx
ON workboard_card_attempts(card_id, ordinal);
CREATE TABLE IF NOT EXISTS workboard_card_comments (
id TEXT PRIMARY KEY,
card_id TEXT NOT NULL REFERENCES workboard_cards(id) ON DELETE CASCADE,
ordinal INTEGER NOT NULL,
body TEXT NOT NULL,
created_at INTEGER NOT NULL,
updated_at INTEGER
) STRICT;
CREATE INDEX IF NOT EXISTS workboard_card_comments_card_idx
ON workboard_card_comments(card_id, ordinal);
CREATE TABLE IF NOT EXISTS workboard_card_links (
id TEXT PRIMARY KEY,
card_id TEXT NOT NULL REFERENCES workboard_cards(id) ON DELETE CASCADE,
ordinal INTEGER NOT NULL,
type TEXT NOT NULL,
target_card_id TEXT,
title TEXT,
url TEXT,
created_at INTEGER NOT NULL
) STRICT;
CREATE INDEX IF NOT EXISTS workboard_card_links_card_idx
ON workboard_card_links(card_id, ordinal);
CREATE TABLE IF NOT EXISTS workboard_card_proof (
id TEXT PRIMARY KEY,
card_id TEXT NOT NULL REFERENCES workboard_cards(id) ON DELETE CASCADE,
ordinal INTEGER NOT NULL,
status TEXT NOT NULL,
label TEXT,
command TEXT,
url TEXT,
note TEXT,
created_at INTEGER NOT NULL
) STRICT;
CREATE INDEX IF NOT EXISTS workboard_card_proof_card_idx
ON workboard_card_proof(card_id, ordinal);
CREATE TABLE IF NOT EXISTS workboard_card_artifacts (
id TEXT PRIMARY KEY,
card_id TEXT NOT NULL REFERENCES workboard_cards(id) ON DELETE CASCADE,
ordinal INTEGER NOT NULL,
label TEXT,
url TEXT,
path TEXT,
mime_type TEXT,
created_at INTEGER NOT NULL
) STRICT;
CREATE INDEX IF NOT EXISTS workboard_card_artifacts_card_idx
ON workboard_card_artifacts(card_id, ordinal);
CREATE TABLE IF NOT EXISTS workboard_card_diagnostics (
card_id TEXT NOT NULL REFERENCES workboard_cards(id) ON DELETE CASCADE,
ordinal INTEGER NOT NULL,
kind TEXT NOT NULL,
severity TEXT NOT NULL,
title TEXT NOT NULL,
detail TEXT NOT NULL,
first_seen_at INTEGER NOT NULL,
last_seen_at INTEGER NOT NULL,
count INTEGER NOT NULL,
actions_json TEXT NOT NULL,
PRIMARY KEY(card_id, ordinal)
) STRICT;
CREATE TABLE IF NOT EXISTS workboard_card_notifications (
id TEXT PRIMARY KEY,
card_id TEXT NOT NULL REFERENCES workboard_cards(id) ON DELETE CASCADE,
ordinal INTEGER NOT NULL,
kind TEXT NOT NULL,
message TEXT NOT NULL,
created_at INTEGER NOT NULL,
sequence INTEGER,
session_key TEXT,
run_id TEXT
) STRICT;
CREATE INDEX IF NOT EXISTS workboard_card_notifications_card_idx
ON workboard_card_notifications(card_id, ordinal);
CREATE TABLE IF NOT EXISTS workboard_worker_logs (
id TEXT PRIMARY KEY,
card_id TEXT NOT NULL REFERENCES workboard_cards(id) ON DELETE CASCADE,
ordinal INTEGER NOT NULL,
level TEXT NOT NULL,
message TEXT NOT NULL,
created_at INTEGER NOT NULL,
session_key TEXT,
run_id TEXT
) STRICT;
CREATE INDEX IF NOT EXISTS workboard_worker_logs_card_idx
ON workboard_worker_logs(card_id, ordinal);
CREATE TABLE IF NOT EXISTS workboard_worker_protocol (
card_id TEXT PRIMARY KEY REFERENCES workboard_cards(id) ON DELETE CASCADE,
state TEXT NOT NULL,
updated_at INTEGER NOT NULL,
detail TEXT
) STRICT;
CREATE TABLE IF NOT EXISTS workboard_card_attachments (
id TEXT PRIMARY KEY,
card_id TEXT NOT NULL REFERENCES workboard_cards(id) ON DELETE CASCADE,
ordinal INTEGER NOT NULL,
file_name TEXT NOT NULL,
byte_size INTEGER NOT NULL,
mime_type TEXT,
note TEXT,
created_at INTEGER NOT NULL
) STRICT;
CREATE INDEX IF NOT EXISTS workboard_card_attachments_card_idx
ON workboard_card_attachments(card_id, ordinal);
CREATE TABLE IF NOT EXISTS workboard_attachment_blobs (
attachment_id TEXT PRIMARY KEY,
content BLOB NOT NULL
) STRICT;
CREATE TABLE IF NOT EXISTS workboard_notification_subscriptions (
id TEXT PRIMARY KEY,
board_id TEXT NOT NULL,
card_id TEXT,
session_key TEXT,
run_id TEXT,
target TEXT,
event_kinds_json TEXT,
last_event_at INTEGER,
last_event_id TEXT,
last_event_sequence INTEGER,
delivered_event_ids_json TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
) STRICT;
`;
function ensureWorkboardSchema(db) {
db.exec(WORKBOARD_SCHEMA_SQL);
ensureColumn(db, "workboard_boards", "automation_job_id", "automation_job_id TEXT");
ensureColumn(db, "workboard_cards", "lifecycle_status_source_updated_at", "lifecycle_status_source_updated_at INTEGER");
const migrationId = `schema-${SCHEMA_VERSION}`;
if (!db.prepare("SELECT 1 AS found FROM workboard_schema_migrations WHERE id = ?").get(migrationId)) {
migrateSqliteSchemaToStrict(db, WORKBOARD_SCHEMA_SQL, { databaseLabel: "workboard database" });
db.prepare("INSERT OR IGNORE INTO workboard_schema_migrations (id, applied_at) VALUES (?, ?)").run(migrationId, Date.now());
}
}
function chmodIfExists(targetPath, mode) {
try {
fs.chmodSync(targetPath, mode);
} catch (err) {
if (err.code !== "ENOENT") throw err;
}
}
function hardenWorkboardDatabaseFiles(dbPath) {
fs.chmodSync(path.dirname(dbPath), WORKBOARD_SQLITE_DIR_MODE);
chmodIfExists(dbPath, WORKBOARD_SQLITE_FILE_MODE);
chmodIfExists(`${dbPath}-wal`, WORKBOARD_SQLITE_FILE_MODE);
chmodIfExists(`${dbPath}-shm`, WORKBOARD_SQLITE_FILE_MODE);
chmodIfExists(`${dbPath}-journal`, WORKBOARD_SQLITE_FILE_MODE);
}
function prepareWorkboardDatabasePath(dbPath) {
fs.mkdirSync(path.dirname(dbPath), {
recursive: true,
mode: WORKBOARD_SQLITE_DIR_MODE
});
chmodIfExists(path.dirname(dbPath), WORKBOARD_SQLITE_DIR_MODE);
if (!fs.existsSync(dbPath)) fs.closeSync(fs.openSync(dbPath, "a", WORKBOARD_SQLITE_FILE_MODE));
}
function createWorkboardDatabase(dbPath, retainClose) {
prepareWorkboardDatabasePath(dbPath);
const db = openNodeSqliteDatabase(dbPath);
let maintenance;
let maintenanceClosed = false;
let databaseClosed = false;
const close = () => {
if (!maintenanceClosed) {
maintenance?.close();
maintenanceClosed = true;
}
if (!databaseClosed) {
db.close();
databaseClosed = true;
}
};
try {
retainClose?.(close);
maintenance = configureSqliteConnectionPragmas(db, {
busyTimeoutMs: WORKBOARD_SQLITE_BUSY_TIMEOUT_MS,
checkpointIntervalMs: 0,
databaseLabel: "workboard database",
databasePath: dbPath,
foreignKeys: true,
synchronous: "NORMAL"
});
ensureWorkboardSchema(db);
hardenWorkboardDatabaseFiles(dbPath);
return {
db,
close
};
} catch (error) {
if (!retainClose) try {
maintenance?.close();
} finally {
db.close();
}
throw error;
}
}
//#endregion
//#region extensions/workboard/src/sqlite-store-write.ts
function cardBoardId(card) {
return card.metadata?.automation?.boardId ?? "default";
}
function bindNull(value) {
if (value === void 0 || value === null || typeof value === "string" || typeof value === "number" || typeof value === "bigint" || value instanceof Uint8Array) return value ?? null;
return JSON.stringify(value);
}
function insertChildren(db, table, cardId, entries, insert) {
const { compiled, bind } = compileSqliteQueryBindings(() => getNodeSqliteKysely(db).deleteFrom(table).where("card_id", "=", cardId));
db.prepare(compiled.sql).run(...bind());
entries?.forEach(insert);
}
function insertCard(db, card) {
const execution = card.execution;
const metadata = card.metadata;
const query = getNodeSqliteKysely(db);
const parent = compileSqliteQueryBindings((p) => query.insertInto("workboard_cards").values({
id: p(() => card.id),
board_id: p(() => cardBoardId(card)),
title: p(() => card.title),
notes: p(() => bindNull(card.notes)),
status: p(() => card.status),
priority: p(() => card.priority),
agent_id: p(() => bindNull(card.agentId)),
session_key: p(() => bindNull(card.sessionKey)),
run_id: p(() => bindNull(card.runId)),
source_url: p(() => bindNull(card.sourceUrl)),
position: p(() => card.position),
created_at: p(() => card.createdAt),
updated_at: p(() => card.updatedAt),
started_at: p(() => bindNull(card.startedAt)),
completed_at: p(() => bindNull(card.completedAt)),
execution_id: p(() => bindNull(execution?.id)),
execution_kind: p(() => bindNull(execution?.kind)),
execution_engine: p(() => bindNull(execution?.engine)),
execution_mode: p(() => bindNull(execution?.mode)),
execution_status: p(() => bindNull(execution?.status)),
execution_model: p(() => bindNull(execution?.model)),
execution_session_key: p(() => bindNull(execution?.sessionKey)),
execution_run_id: p(() => bindNull(execution?.runId)),
execution_started_at: p(() => bindNull(execution?.startedAt)),
execution_updated_at: p(() => bindNull(execution?.updatedAt)),
automation_json: p(() => jsonValue(metadata?.automation)),
claim_json: p(() => jsonValue(metadata?.claim)),
template_id: p(() => bindNull(metadata?.templateId)),
archived_at: p(() => bindNull(metadata?.archivedAt)),
stale_json: p(() => jsonValue(metadata?.stale)),
lifecycle_status_source_updated_at: p(() => bindNull(metadata?.lifecycleStatusSourceUpdatedAt)),
failure_count: p(() => bindNull(metadata?.failureCount))
}).onConflict((conflict) => conflict.column("id").doUpdateSet((eb) => ({
board_id: eb.ref("excluded.board_id"),
title: eb.ref("excluded.title"),
notes: eb.ref("excluded.notes"),
status: eb.ref("excluded.status"),
priority: eb.ref("excluded.priority"),
agent_id: eb.ref("excluded.agent_id"),
session_key: eb.ref("excluded.session_key"),
run_id: eb.ref("excluded.run_id"),
source_url: eb.ref("excluded.source_url"),
position: eb.ref("excluded.position"),
created_at: eb.ref("excluded.created_at"),
updated_at: eb.ref("excluded.updated_at"),
started_at: eb.ref("excluded.started_at"),
completed_at: eb.ref("excluded.completed_at"),
execution_id: eb.ref("excluded.execution_id"),
execution_kind: eb.ref("excluded.execution_kind"),
execution_engine: eb.ref("excluded.execution_engine"),
execution_mode: eb.ref("excluded.execution_mode"),
execution_status: eb.ref("excluded.execution_status"),
execution_model: eb.ref("excluded.execution_model"),
execution_session_key: eb.ref("excluded.execution_session_key"),
execution_run_id: eb.ref("excluded.execution_run_id"),
execution_started_at: eb.ref("excluded.execution_started_at"),
execution_updated_at: eb.ref("excluded.execution_updated_at"),
automation_json: eb.ref("excluded.automation_json"),
claim_json: eb.ref("excluded.claim_json"),
template_id: eb.ref("excluded.template_id"),
archived_at: eb.ref("excluded.archived_at"),
stale_json: eb.ref("excluded.stale_json"),
lifecycle_status_source_updated_at: eb.ref("excluded.lifecycle_status_source_updated_at"),
failure_count: eb.ref("excluded.failure_count")
}))));
db.prepare(parent.compiled.sql).run(...parent.bind());
insertChildren(db, "workboard_card_labels", card.id, card.labels, (label, ordinal) => {
const { compiled, bind } = compileSqliteQueryBindings((p) => query.insertInto("workboard_card_labels").values({
card_id: p(() => card.id),
ordinal: p(() => ordinal),
label: p(() => label)
}));
db.prepare(compiled.sql).run(...bind());
});
insertChildren(db, "workboard_card_events", card.id, card.events, (event, ordinal) => {
const { compiled, bind } = compileSqliteQueryBindings((p) => query.insertInto("workboard_card_events").values({
id: p(() => event.id),
card_id: p(() => card.id),
ordinal: p(() => ordinal),
kind: p(() => event.kind),
at: p(() => event.at),
from_status: p(() => bindNull(event.fromStatus)),
to_status: p(() => bindNull(event.toStatus)),
session_key: p(() => bindNull(event.sessionKey)),
run_id: p(() => bindNull(event.runId))
}));
db.prepare(compiled.sql).run(...bind());
});
insertChildren(db, "workboard_card_attempts", card.id, metadata?.attempts, (entry, ordinal) => {
const { compiled, bind } = compileSqliteQueryBindings((p) => query.insertInto("workboard_card_attempts").values({
id: p(() => entry.id),
card_id: p(() => card.id),
ordinal: p(() => ordinal),
status: p(() => entry.status),
started_at: p(() => entry.startedAt),
ended_at: p(() => bindNull(entry.endedAt)),
engine: p(() => bindNull(entry.engine)),
mode: p(() => bindNull(entry.mode)),
model: p(() => bindNull(entry.model)),
session_key: p(() => bindNull(entry.sessionKey)),
run_id: p(() => bindNull(entry.runId)),
error: p(() => bindNull(entry.error))
}));
db.prepare(compiled.sql).run(...bind());
});
insertChildren(db, "workboard_card_comments", card.id, metadata?.comments, (entry, ordinal) => {
const { compiled, bind } = compileSqliteQueryBindings((p) => query.insertInto("workboard_card_comments").values({
id: p(() => entry.id),
card_id: p(() => card.id),
ordinal: p(() => ordinal),
body: p(() => entry.body),
created_at: p(() => entry.createdAt),
updated_at: p(() => bindNull(entry.updatedAt))
}));
db.prepare(compiled.sql).run(...bind());
});
insertChildren(db, "workboard_card_links", card.id, metadata?.links, (entry, ordinal) => {
const { compiled, bind } = compileSqliteQueryBindings((p) => query.insertInto("workboard_card_links").values({
id: p(() => entry.id),
card_id: p(() => card.id),
ordinal: p(() => ordinal),
type: p(() => entry.type),
target_card_id: p(() => bindNull(entry.targetCardId)),
title: p(() => bindNull(entry.title)),
url: p(() => bindNull(entry.url)),
created_at: p(() => entry.createdAt)
}));
db.prepare(compiled.sql).run(...bind());
});
insertChildren(db, "workboard_card_proof", card.id, metadata?.proof, (entry, ordinal) => {
const { compiled, bind } = compileSqliteQueryBindings((p) => query.insertInto("workboard_card_proof").values({
id: p(() => entry.id),
card_id: p(() => card.id),
ordinal: p(() => ordinal),
status: p(() => entry.status),
label: p(() => bindNull(entry.label)),
command: p(() => bindNull(entry.command)),
url: p(() => bindNull(entry.url)),
note: p(() => bindNull(entry.note)),
created_at: p(() => entry.createdAt)
}));
db.prepare(compiled.sql).run(...bind());
});
insertChildren(db, "workboard_card_artifacts", card.id, metadata?.artifacts, (entry, ordinal) => {
const { compiled, bind } = compileSqliteQueryBindings((p) => query.insertInto("workboard_card_artifacts").values({
id: p(() => entry.id),
card_id: p(() => card.id),
ordinal: p(() => ordinal),
label: p(() => bindNull(entry.label)),
url: p(() => bindNull(entry.url)),
path: p(() => bindNull(entry.path)),
mime_type: p(() => bindNull(entry.mimeType)),
created_at: p(() => entry.createdAt)
}));
db.prepare(compiled.sql).run(...bind());
});
insertChildren(db, "workboard_card_attachments", card.id, metadata?.attachments, (entry, ordinal) => {
const { compiled, bind } = compileSqliteQueryBindings((p) => query.insertInto("workboard_card_attachments").values({
id: p(() => entry.id),
card_id: p(() => entry.cardId),
ordinal: p(() => ordinal),
file_name: p(() => entry.fileName),
byte_size: p(() => entry.byteSize),
mime_type: p(() => bindNull(entry.mimeType)),
note: p(() => bindNull(entry.note)),
created_at: p(() => entry.createdAt)
}));
db.prepare(compiled.sql).run(...bind());
});
insertChildren(db, "workboard_card_diagnostics", card.id, metadata?.diagnostics, (entry, ordinal) => {
const { compiled, bind } = compileSqliteQueryBindings((p) => query.insertInto("workboard_card_diagnostics").values({
card_id: p(() => card.id),
ordinal: p(() => ordinal),
kind: p(() => entry.kind),
severity: p(() => entry.severity),
title: p(() => entry.title),
detail: p(() => entry.detail),
first_seen_at: p(() => entry.firstSeenAt),
last_seen_at: p(() => entry.lastSeenAt),
count: p(() => entry.count),
actions_json: p(() => JSON.stringify(entry.actions))
}));
db.prepare(compiled.sql).run(...bind());
});
insertChildren(db, "workboard_card_notifications", card.id, metadata?.notifications, (entry, ordinal) => {
const { compiled, bind } = compileSqliteQueryBindings((p) => query.insertInto("workboard_card_notifications").values({
id: p(() => entry.id),
card_id: p(() => card.id),
ordinal: p(() => ordinal),
kind: p(() => entry.kind),
message: p(() => entry.message),
created_at: p(() => entry.createdAt),
sequence: p(() => bindNull(entry.sequence)),
session_key: p(() => bindNull(entry.sessionKey)),
run_id: p(() => bindNull(entry.runId))
}));
db.prepare(compiled.sql).run(...bind());
});
insertChildren(db, "workboard_worker_logs", card.id, metadata?.workerLogs, (entry, ordinal) => {
const { compiled, bind } = compileSqliteQueryBindings((p) => query.insertInto("workboard_worker_logs").values({
id: p(() => entry.id),
card_id: p(() => card.id),
ordinal: p(() => ordinal),
level: p(() => entry.level),
message: p(() => entry.message),
created_at: p(() => entry.createdAt),
session_key: p(() => bindNull(entry.sessionKey)),
run_id: p(() => bindNull(entry.runId))
}));
db.prepare(compiled.sql).run(...bind());
});
const protocolDelete = compileSqliteQueryBindings((p) => query.deleteFrom("workboard_worker_protocol").where("card_id", "=", p(() => card.id)));
db.prepare(protocolDelete.compiled.sql).run(...protocolDelete.bind());
if (metadata?.workerProtocol) {
const { compiled, bind } = compileSqliteQueryBindings((p) => query.insertInto("workboard_worker_protocol").values({
card_id: p(() => card.id),
state: p(() => metadata.workerProtocol.state),
updated_at: p(() => metadata.workerProtocol.updatedAt),
detail: p(() => bindNull(metadata.workerProtocol.detail))
}));
db.prepare(compiled.sql).run(...bind());
}
}
//#endregion
//#region extensions/workboard/src/sqlite-store-kernel.ts
var WorkboardSqliteCardStore = class {
constructor(db) {
this.db = db;
}
matchesUpdatedAt(key, expectedUpdatedAt) {
const { compiled, bind } = compileSqliteQueryBindings((parameter) => getNodeSqliteKysely(this.db).selectFrom("workboard_cards").select("updated_at").where("id", "=", parameter((value) => value)));
const current = this.db.prepare(compiled.sql).get(...bind(key));
return isRecord(current) && numberValue(current, "updated_at") === expectedUpdatedAt;
}
validatePayload(key, value) {
if (value.version !== 1 || value.card.id !== key) throw new Error("invalid workboard card payload");
}
register(key, value) {
this.validatePayload(key, value);
runSqliteImmediateTransactionSync(this.db, () => insertCard(this.db, value.card));
}
registerIfAbsent(key, value) {
this.validatePayload(key, value);
return runSqliteImmediateTransactionSync(this.db, () => {
if (this.db.prepare("SELECT 1 FROM workboard_cards WHERE id = ?").get(key)) return false;
insertCard(this.db, value.card);
return true;
});
}
registerIfUpdatedAt(key, value, expectedUpdatedAt) {
this.validatePayload(key, value);
return runSqliteImmediateTransactionSync(this.db, () => {
if (!this.matchesUpdatedAt(key, expectedUpdatedAt)) return false;
insertCard(this.db, value.card);
return true;
});
}
claimIfOwnerAvailable(key, value, expectedUpdatedAt, ownerId, now) {
this.validatePayload(key, value);
return runSqliteImmediateTransactionSync(this.db, () => {
const query = getNodeSqliteKysely(this.db);
const current = executeSqliteQueryTakeFirstSync(this.db, query.selectFrom("workboard_cards").selectAll().where("id", "=", key).where("updated_at", "=", expectedUpdatedAt));
if (!current) return "conflict";
const candidates = query.selectFrom("workboard_cards").select([
"status",
"agent_id",
"claim_json",
"execution_id",
"execution_status"
]).where("id", "!=", key).where((eb) => eb.or([eb("archived_at", "is", null), eb("archived_at", "=", 0)])).where((eb) => eb.or([
eb("status", "=", "running"),
eb.and([eb("execution_id", "!=", ""), eb("execution_status", "=", "running")]),
eb.and([eb("claim_json", "is not", null), eb("status", "!=", "done")])
]));
for (const row of iterateSqliteQuerySync(this.db, candidates)) {
const card = {
status: requiredString(row, "status"),
agentId: stringValue(row, "agent_id"),
metadata: { claim: parseJson(row.claim_json) },
execution: stringValue(row, "execution_id") ? { status: requiredString(row, "execution_status") } : void 0
};
if (workboardCardConsumesOwnerSlot(card, now) && workboardCardSlotOwner(card) === ownerId) return "owner_busy";
}
readCard(this.db, current);
insertCard(this.db, value.card);
return "updated";
});
}
deleteIfUpdatedAt(key, expectedUpdatedAt) {
return runSqliteImmediateTransactionSync(this.db, () => {
if (!this.matchesUpdatedAt(key, expectedUpdatedAt)) return false;
this.deleteCard(key);
return true;
});
}
lookup(key) {
const row = this.db.prepare("SELECT * FROM workboard_cards WHERE id = ?").get(key);
return row ? {
version: 1,
card: readCard(this.db, row)
} : void 0;
}
delete(key) {
return runSqliteImmediateTransactionSync(this.db, () => this.deleteCard(key)).changes > 0;
}
deleteCard(key) {
this.db.prepare(`
DELETE FROM workboard_attachment_blobs
WHERE attachment_id IN (
SELECT id FROM workboard_card_attachments WHERE card_id = ?
)
`).run(key);
return this.db.prepare("DELETE FROM workboard_cards WHERE id = ?").run(key);
}
workerContextCardIds(scope) {
const query = getNodeSqliteKysely(this.db);
const doneParents = new Set(scope.parentIds.length === 0 ? [] : Array.from(iterateSqliteQuerySync(this.db, query.selectFrom("workboard_cards").select("id").where("id", "in", sqliteStringSet(scope.parentIds)).where("status", "=", "done")), (row) => requiredString(row, "id")));
const parents = scope.parentIds.filter((id) => doneParents.has(id)).slice(-6);
const recent = scope.agentId ? Array.from(iterateSqliteQuerySync(this.db, query.selectFrom("workboard_cards").select("id").where("board_id", "=", scope.boardId).where("agent_id", "=", scope.agentId).where("status", "=", "done").where("id", "!=", scope.cardId).orderBy("updated_at", "desc").orderBy("position", "asc").orderBy("created_at", "asc").orderBy("id", "asc").limit(5)), (row) => requiredString(row, "id")) : [];
return [.../* @__PURE__ */ new Set([...parents, ...recent])];
}
entries(scope) {
return scope?.kind === "worker-context" ? runSqliteDeferredTransactionSync(this.db, () => this.readEntries(scope)) : this.readEntries(scope);
}
readEntries(scope) {
let query = getNodeSqliteKysely(this.db).selectFrom("workboard_cards").selectAll().orderBy("created_at", "asc").orderBy("id", "asc");
if (scope?.kind === "board") query = query.where("board_id", "=", scope.boardId);
else if (scope?.kind === "session") query = query.where((eb) => eb.or([eb("session_key", "=", scope.sessionKey), eb.and([
eb.or([eb("session_key", "is", null), eb("session_key", "=", "")]),
eb("execution_id", "!=", ""),
eb("execution_session_key", "=", scope.sessionKey)
])]));
else if (scope?.kind === "worker-context") {
const ids = this.workerContextCardIds(scope);
if (ids.length === 0) return [];
query = query.where("id", "in", sqliteStringSet(ids));
}
const rows = Array.from(iterateSqliteQuerySync(this.db, query));
if (scope !== void 0 && rows.length === 0) return [];
const preloaded = loadCardChildRows(this.db, scope === void 0 ? void 0 : rows.map((row) => requiredString(row, "id")));
return rows.map((row) => ({
key: requiredString(row, "id"),
value: {
version: 1,
card: readCard(this.db, row, preloaded)
}
}));
}
listCardStatuses(ids) {
if (ids.length === 0) return [];
const query = getNodeSqliteKysely(this.db).selectFrom("workboard_cards").select(["id", "status"]).where("id", "in", sqliteStringSet(ids));
return Array.from(iterateSqliteQuerySync(this.db, query), (row) => ({
id: requiredString(row, "id"),
status: requiredString(row, "status")
}));
}
listBoardAggregates() {
return this.db.prepare(`
SELECT
board_id,
status,
COUNT(*) AS total,
SUM(CASE WHEN archived_at IS NOT NULL AND archived_at <> 0 THEN 1 ELSE 0 END) AS archived,
MAX(updated_at) AS updated_at
FROM workboard_cards
GROUP BY board_id, status
ORDER BY board_id ASC, status ASC
`).all().map((row) => ({
boardId: requiredString(row, "board_id"),
status: requiredString(row, "status"),
total: requiredNumber(row, "total"),
archived: requiredNumber(row, "archived"),
updatedAt: requiredNumber(row, "updated_at")
}));
}
listStatsAggregates(boardId) {
let query = getNodeSqliteKysely(this.db).selectFrom("workboard_cards").select((eb) => [
"status",
"agent_id",
eb.fn.countAll().as("total"),
eb.fn.sum(eb.case().when(eb.and([eb("archived_at", "is not", null), eb("archived_at", "!=", 0)])).then(1).else(0).end()).as("archived"),
eb.fn.max("updated_at").as("updated_at"),
eb.fn.min(eb.case().when(eb.and([eb("status", "=", "ready"), eb.or([eb("archived_at", "is", null), eb("archived_at", "=", 0)])])).then(eb.ref("updated_at")).else(null).end()).as("oldest_ready_at")
]).groupBy(["status", "agent_id"]);
if (boardId !== void 0) query = query.where("board_id", "=", boardId);
return Array.from(iterateSqliteQuerySync(this.db, query), (row) => ({
status: requiredString(row, "status"),
agentId: stringValue(row, "agent_id"),
total: requiredNumber(row, "total"),
archived: requiredNumber(row, "archived"),
updatedAt: requiredNumber(row, "updated_at"),
oldestReadyAt: numberValue(row, "oldest_ready_at")
}));
}
hasCards(boardId) {
return executeSqliteQueryTakeFirstSync(this.db, getNodeSqliteKysely(this.db).selectFrom("workboard_cards").select("id").where("board_id", "=", boardId).limit(1)) !== void 0;
}
};
function readBoard(row) {
const defaultWorkspace = parseJson(row.default_workspace_json);
const orchestration = parseJson(row.orchestration_json);
return {
version: 1,
board: definedFields({
id: requiredString(row, "id"),
name: stringValue(row, "name"),
description: stringValue(row, "description"),
icon: stringValue(row, "icon"),
color: stringValue(row, "color"),
automationJobId: stringValue(row, "automation_job_id"),
...defaultWorkspace ? { defaultWorkspace } : {},
...orchestration ? { orchestration } : {},
createdAt: requiredNumber(row, "created_at"),
updatedAt: requiredNumber(row, "updated_at"),
archivedAt: numberValue(row, "archived_at")
})
};
}
var WorkboardSqliteBoardStore = class {
constructor(db) {
this.db = db;
this.rowsQuery = getNodeSqliteKysely(db).selectFrom("workboard_boards").selectAll();
}
register(key, value) {
if (value.version !== 1 || value.board.id !== key) throw new Error("invalid workboard board payload");
const board = value.board;
const { compiled, bind } = compileSqliteQueryBindings((parameter) => getNodeSqliteKysely(this.db).insertInto("workboard_boards").values({
id: parameter(() => board.id),
name: parameter(() => bindNull(board.name)),
description: parameter(() => bindNull(board.description)),
icon: parameter(() => bindNull(board.icon)),
color: parameter(() => bindNull(board.color)),
automation_job_id: parameter(() => bindNull(board.automationJobId)),
default_workspace_json: parameter(() => jsonValue(board.defaultWorkspace)),
orchestration_json: parameter(() => jsonValue(board.orchestration)),
created_at: parameter(() => board.createdAt),
updated_at: parameter(() => board.updatedAt),
archived_at: parameter(() => bindNull(board.archivedAt))
}).onConflict((conflict) => conflict.column("id").doUpdateSet((eb) => ({
name: eb.ref("excluded.name"),
description: eb.ref("excluded.description"),
icon: eb.ref("excluded.icon"),
color: eb.ref("excluded.color"),
automation_job_id: eb.ref("excluded.automation_job_id"),
default_workspace_json: eb.ref("excluded.default_workspace_json"),
orchestration_json: eb.ref("excluded.orchestration_json"),
created_at: eb.ref("excluded.created_at"),
updated_at: eb.ref("excluded.updated_at"),
archived_at: eb.ref("excluded.archived_at")
}))));
this.db.prepare(compiled.sql).run(...bind());
}
lookup(key) {
const row = executeSqliteQueryTakeFirstSync(this.db, this.rowsQuery.where("id", "=", key));
return row ? readBoard(row) : void 0;
}
delete(key) {
return this.db.prepare("DELETE FROM workboard_boards WHERE id = ?").run(key).changes > 0;
}
entries() {
return Array.from(iterateSqliteQuerySync(this.db, this.rowsQuery.orderBy("id", "asc")), (row) => ({
key: requiredString(row, "id"),
value: readBoard(row)
}));
}
};
function readSubscription(row) {
const eventKinds = parseJson(row.event_kinds_json);
const deliveredEventIds = parseJson(row.delivered_event_ids_json);
return {
version: 1,
subscription: definedFields({
id: requiredString(row, "id"),
boardId: requiredString(row, "board_id"),
cardId: stringValue(row, "card_id"),
sessionKey: stringValue(row, "session_key"),
runId: stringValue(row, "run_id"),
target: stringValue(row, "target"),
...eventKinds ? { eventKinds } : {},
lastEventAt: numberValue(row, "last_event_at"),
lastEventId: stringValue(row, "last_event_id"),
lastEventSequence: numberValue(row, "last_event_sequence"),
...deliveredEventIds ? { deliveredEventIds } : {},
createdAt: requiredNumber(row, "created_at"),
updatedAt: requiredNumber(row, "updated_at")
})
};
}
var WorkboardSqliteSubscriptionStore = class {
constructor(db) {
this.db = db;
this.rowsQuery = getNodeSqliteKysely(db).selectFrom("workboard_notification_subscriptions").selectAll();
}
register(key, value) {
if (value.version !== 1 || value.subscription.id !== key) throw new Error("invalid workboard notification subscription payload");
const subscription = value.subscription;
const { compiled, bind } = compileSqliteQueryBindings((parameter) => getNodeSqliteKysely(this.db).insertInto("workboard_notification_subscriptions").values({
id: parameter(() => subscription.id),
board_id: parameter(() => subscription.boardId),
card_id: parameter(() => bindNull(subscription.cardId)),
session_key: parameter(() => bindNull(subscription.sessionKey)),
run_id: parameter(() => bindNull(subscription.runId)),
target: parameter(() => bindNull(subscription.target)),
event_kinds_json: parameter(() => jsonValue(subscription.eventKinds)),
last_event_at: parameter(() => bindNull(subscription.lastEventAt)),
last_event_id: parameter(() => bindNull(subscription.lastEventId)),
last_event_sequence: parameter(() => bindNull(subscription.lastEventSequence)),
delivered_event_ids_json: parameter(() => jsonValue(subscription.deliveredEventIds)),
created_at: parameter(() => subscription.createdAt),
updated_at: parameter(() => subscription.updatedAt)
}).onConflict((conflict) => conflict.column("id").doUpdateSet((eb) => ({
board_id: eb.ref("excluded.board_id"),
card_id: eb.ref("excluded.card_id"),
session_key: eb.ref("excluded.session_key"),
run_id: eb.ref("excluded.run_id"),
target: eb.ref("excluded.target"),
event_kinds_json: eb.ref("excluded.event_kinds_json"),
last_event_at: eb.ref("excluded.last_event_at"),
last_event_id: eb.ref("excluded.last_event_id"),
last_event_sequence: eb.ref("excluded.last_event_sequence"),
delivered_event_ids_json: eb.ref("excluded.delivered_event_ids_json"),
created_at: eb.ref("excluded.created_at"),
updated_at: eb.ref("excluded.updated_at")
}))));
this.db.prepare(compiled.sql).run(...bind());
}
lookup(key) {
const row = executeSqliteQueryTakeFirstSync(this.db, this.rowsQuery.where("id", "=", key));
return row ? readSubscription(row) : void 0;
}
delete(key) {
return this.db.prepare("DELETE FROM workboard_notification_subscriptions WHERE id = ?").run(key).changes > 0;
}
entries(options = {}) {
let query = this.rowsQuery;
if (options.boardId) query = query.where("board_id", "=", options.boardId);
if (options.cardId) query = query.where("card_id", "=", options.cardId);
return Array.from(iterateSqliteQuerySync(this.db, query.orderBy("created_at", "asc").orderBy("id", "asc")), (row) => ({
key: requiredString(row, "id"),
value: readSubscription(row)
}));
}
};
function readPersistedAttachment(row) {
return {
version: 1,
attachment: readAttachment(row),
contentBase64: blobToBase64(row.content)
};
}
var WorkboardSqliteAttachmentStore = class {
constructor(db) {
this.db = db;
this.rowsQuery = getNodeSqliteKysely(db).selectFrom("workboard_card_attachments as a").innerJoin("workboard_attachment_blobs as b", "b.attachment_id", "a.id").selectAll("a").select("b.content");
}
register(key, value) {
if (value.version !== 1 || value.attachment.id !== key) throw new Error("invalid workboard attachment payload");
const attachment = value.attachment;
this.db.prepare(`
INSERT INTO workboard_attachment_blobs (attachment_id, content)
VALUES (?, ?)
ON CONFLICT(attachment_id) DO UPDATE SET content = excluded.content
`).run(attachment.id, asBlobContent(value.contentBase64));
}
lookup(key) {
const row = executeSqliteQueryTakeFirstSync(this.db, this.rowsQuery.where("a.id", "=", key));
return row ? readPersistedAttachment(row) : void 0;
}
delete(key) {
return runSqliteImmediateTransactionSync(this.db, () => {
this.db.prepare("DELETE FROM workboard_attachment_blobs WHERE attachment_id = ?").run(key);
return this.db.prepare("DELETE FROM workboard_card_attachments WHERE id = ?").run(key);
}).changes > 0;
}
entries() {
return Array.from(iterateSqliteQuerySync(this.db, this.rowsQuery.orderBy("a.created_at", "asc").orderBy("a.id", "asc")), (row) => ({
key: requiredString(row, "id"),
value: readPersistedAttachment(row)
}));
}
};
function createWorkboardSqliteKernel(dbPath, retainClose) {
const { db, close } = createWorkboardDatabase(dbPath, retainClose);
return {
cards: new WorkboardSqliteCardStore(db),
boards: new WorkboardSqliteBoardStore(db),
subscriptions: new WorkboardSqliteSubscriptionStore(db),
attachments: new WorkboardSqliteAttachmentStore(db),
dataVersion: () => requiredNumber(db.prepare("PRAGMA data_version").get(), "data_version"),
close
};
}
//#endregion
//#region extensions/workboard/src/sqlite-store.worker.ts
function createSqliteWorkerBackend(_input, context) {
const initial = createWorkboardSqliteKernel(context.databasePath);
const connections = /* @__PURE__ */ new Map();
connections.set(0, {
kernel: initial,
close: initial.close
});
let nextConnection = 0;
function execute(command) {
const connection = connections.get(command.input.connection);
if (!connection) throw new Error("Workboard SQLite connection is closed.");
if (command.type === "connection.close") {
connection.close();
connections.delete(command.input.connection);
return;
}
const kernel = connection.kernel;
if (!kernel) throw new Error("Workboard SQLite connection initialization failed.");
switch (command.type) {
case "dataVersion": return kernel.dataVersion();
case "cards.register": return kernel.cards.register(...command.input.args);
case "cards.registerIfAbsent": return kernel.cards.registerIfAbsent(...command.input.args);
case "cards.registerIfUpdatedAt": return kernel.cards.registerIfUpdatedAt(...command.input.args);
case "cards.claimIfOwnerAvailable": return kernel.cards.claimIfOwnerAvailable(...command.input.args);
case "cards.deleteIfUpdatedAt": return kernel.cards.deleteIfUpdatedAt(...command.input.args);
case "cards.lookup": return kernel.cards.lookup(...command.input.args);
case "cards.delete": return kernel.cards.delete(...command.input.args);
case "cards.entries": return kernel.cards.entries(...command.input.args);
case "cards.listCardStatuses": return kernel.cards.listCardStatuses(...command.input.args);
case "cards.listBoardAggregates": return kernel.cards.listBoardAggregates(...command.input.args);
case "cards.listStatsAggregates": return kernel.cards.listStatsAggregates(...command.input.args);
case "cards.hasCards": return kernel.cards.hasCards(...command.input.args);
case "boards.register": return kernel.boards.register(...command.input.args);
case "boards.lookup": return kernel.boards.lookup(...command.input.args);
case "boards.delete": return kernel.boards.delete(...command.input.args);
case "boards.entries": return kernel.boards.entries(...command.input.args);
case "subscriptions.register": return kernel.subscriptions.register(...command.input.args);
case "subscriptions.lookup": return kernel.subscriptions.lookup(...command.input.args);
case "subscriptions.delete": return kernel.subscriptions.delete(...command.input.args);
case "subscriptions.entries": return kernel.subscriptions.entries(...command.input.args);
case "attachments.register": return kernel.attachments.register(...command.input.args);
case "attachments.lookup": return kernel.attachments.lookup(...command.input.args);
case "attachments.delete": return kernel.attachments.delete(...command.input.args);
case "attachments.entries": return kernel.attachments.entries(...command.input.args);
}
}
return {
execute(command) {
if (command.type === "connection.open") {
const connection = ++nextConnection;
try {
const unclaimed = connections.get(0);
connections.delete(0);
const kernel = unclaimed?.kernel ?? createWorkboardSqliteKernel(context.databasePath, (close) => {
connections.set(connection, { close });
});
connections.set(connection, {
kernel,
close: kernel.close
});
return {
ok: true,
value: {
connection,
dataVersion: kernel.dataVersion()
}
};
} catch (error) {
const owned = connections.get(connection);
if (owned) try {
owned.close();
connections.delete(connection);
} catch {
return {
ok: false,
failure: {
...encodeWorkboardSqliteFailure(error),
cleanupConnection: connection
}
};
}
return {
ok: false,
failure: encodeWorkboardSqliteFailure(error)
};
}
}
try {
return {
ok: true,
value: execute(command)
};
} catch (error) {
return {
ok: false,
failure: encodeWorkboardSqliteFailure(error)
};
}
},
close() {
const failures = [];
for (const [id, kernel] of connections) try {
kernel.close();
connections.delete(id);
} catch (error) {
failures.push(error);
}
if (failures.length) throw new AggregateError(failures, "Workboard SQLite cleanup failed");
}
};
}
//#endregion
export { createSqliteWorkerBackend };