refactor(database): split 5 god modules into domain-scoped files
Five files under `src/main/database/` exceeded 400 LOC by combining multiple responsibilities. Split along natural fault lines. Zero behavior change — each original `build*Methods` factory still returns the same method surface, now via spread of smaller builders. | Original | Split into | LOC each | |-----------------------------------|-------------------------------------|----------------| | tasks-crud.ts (640) | tasks-crud / task-sessions-crud / | 250 / 252 / | | | task-queries | 177 | | agents-hierarchy.ts (487) | agents-hierarchy / agent-permissions| 350 / 151 | | company-settings.ts (476) | company-settings / company-metrics | 122 / 370 | | documents.ts (426) | documents / config-revisions | 318 / 126 | | runs.ts (413) | runs / run-logs / run-events / | 245 / 96 / | | | cost-entries | 82 / 52 | AppDatabase call sites unchanged: - db.tasks.saveTask, db.tasks.ensureAgentSession (task-sessions-crud), db.tasks.listAgentTasks (task-queries) - db.agents.getChainOfCommand, db.agents.hasAgentPermission - db.company.getCompanySettings, db.company.getStandupReport (company-metrics) - db.documentsMod.saveDocument, db.documentsMod.recordConfigRevision - db.runs.createRun, db.runs.appendRunLog (run-logs), db.runs.appendRunEvent (run-events), db.runs.recordCostEntry (cost-entries) `database-base.ts` (449) kept as-is — thin facade where every pass-through is one line; splitting adds mechanical files without behavior benefit. Will revisit if it grows further. Aggregator files (`tasks.ts`, `agents.ts`, `company.ts`) each import and spread the new sub-builders. No renamed exports.
This commit is contained in:
1 parent
f012f8e880
commit
dbdec86cdd
16 files changed
+1329
-1172
No files matched your search
@@ -0,0 +1,151 @@
|
||||
import type { DatabaseSync } from "node:sqlite";
|
||||
import { type ReadAll, type Transaction, nowIso, parseJson, toBoolean } from "./helpers";
|
||||
|
||||
/**
|
||||
* Agent permission, runtime-state, and heartbeat-policy helpers extracted
|
||||
* from agents-hierarchy.ts. Every method is spread into the final
|
||||
* `buildAgentMethods` result so AppDatabase call-sites remain unchanged.
|
||||
*/
|
||||
export function buildAgentPermissionMethods(
|
||||
getDb: () => DatabaseSync,
|
||||
_readAll: ReadAll,
|
||||
_transaction: Transaction,
|
||||
) {
|
||||
return {
|
||||
getAgentPermissions(agentId: string): Record<string, boolean> {
|
||||
const row = getDb()
|
||||
.prepare("SELECT permissions_json FROM agents WHERE id = ?")
|
||||
.get(agentId) as { permissions_json: string } | undefined;
|
||||
return parseJson<Record<string, boolean>>(row?.permissions_json, {});
|
||||
},
|
||||
|
||||
setAgentPermission(
|
||||
agentId: string,
|
||||
companyId: string,
|
||||
permissionKey: string,
|
||||
enabled: boolean,
|
||||
): void {
|
||||
const row = getDb()
|
||||
.prepare("SELECT permissions_json FROM agents WHERE id = ?")
|
||||
.get(agentId) as { permissions_json: string } | undefined;
|
||||
const current = parseJson<Record<string, boolean>>(row?.permissions_json, {});
|
||||
if (enabled) {
|
||||
current[permissionKey] = true;
|
||||
} else {
|
||||
delete current[permissionKey];
|
||||
}
|
||||
const ts = nowIso();
|
||||
getDb()
|
||||
.prepare(
|
||||
"UPDATE agents SET permissions_json = ?, updated_at = ? WHERE id = ? AND company_id = ?",
|
||||
)
|
||||
.run(JSON.stringify(current), ts, agentId, companyId);
|
||||
},
|
||||
|
||||
hasAgentPermission(agentId: string, permissionKey: string): boolean {
|
||||
const row = getDb()
|
||||
.prepare("SELECT permissions_json FROM agents WHERE id = ?")
|
||||
.get(agentId) as { permissions_json: string } | undefined;
|
||||
const perms = parseJson<Record<string, boolean>>(row?.permissions_json, {});
|
||||
return perms[permissionKey] === true;
|
||||
},
|
||||
|
||||
getRuntimeState(agentId: string): Record<string, unknown> {
|
||||
const row = getDb()
|
||||
.prepare("SELECT runtime_state_json FROM agents WHERE id = ?")
|
||||
.get(agentId) as { runtime_state_json: string | null } | undefined;
|
||||
return parseJson<Record<string, unknown>>(row?.runtime_state_json, {});
|
||||
},
|
||||
|
||||
setRuntimeState(
|
||||
agentId: string,
|
||||
companyId: string,
|
||||
state: Record<string, unknown>,
|
||||
): void {
|
||||
const ts = nowIso();
|
||||
getDb()
|
||||
.prepare(
|
||||
"UPDATE agents SET runtime_state_json = ?, updated_at = ? WHERE id = ? AND company_id = ?",
|
||||
)
|
||||
.run(JSON.stringify(state), ts, agentId, companyId);
|
||||
},
|
||||
|
||||
getHeartbeatPolicy(agentId: string): {
|
||||
enabled: boolean;
|
||||
intervalSec: number;
|
||||
cooldownSec: number;
|
||||
maxConcurrentRuns: number;
|
||||
wakeOnDemand: boolean;
|
||||
} {
|
||||
const row = getDb()
|
||||
.prepare(
|
||||
"SELECT heartbeat_enabled, heartbeat_interval_sec, heartbeat_policy_json FROM agents WHERE id = ?",
|
||||
)
|
||||
.get(agentId) as
|
||||
| {
|
||||
heartbeat_enabled: number;
|
||||
heartbeat_interval_sec: number;
|
||||
heartbeat_policy_json: string | null;
|
||||
}
|
||||
| undefined;
|
||||
if (!row) throw new Error(`Agent ${agentId} not found.`);
|
||||
const policy = parseJson<Record<string, unknown>>(row.heartbeat_policy_json, {});
|
||||
return {
|
||||
enabled: toBoolean(row.heartbeat_enabled),
|
||||
intervalSec: row.heartbeat_interval_sec ?? 120,
|
||||
cooldownSec: typeof policy.cooldownSec === "number" ? policy.cooldownSec : 10,
|
||||
maxConcurrentRuns:
|
||||
typeof policy.maxConcurrentRuns === "number" ? policy.maxConcurrentRuns : 1,
|
||||
wakeOnDemand: policy.wakeOnDemand !== false,
|
||||
};
|
||||
},
|
||||
|
||||
setHeartbeatPolicy(
|
||||
agentId: string,
|
||||
companyId: string,
|
||||
policy: {
|
||||
enabled?: boolean;
|
||||
intervalSec?: number;
|
||||
cooldownSec?: number;
|
||||
maxConcurrentRuns?: number;
|
||||
wakeOnDemand?: boolean;
|
||||
},
|
||||
): void {
|
||||
const row = getDb()
|
||||
.prepare(
|
||||
"SELECT heartbeat_enabled, heartbeat_interval_sec, heartbeat_policy_json FROM agents WHERE id = ?",
|
||||
)
|
||||
.get(agentId) as
|
||||
| {
|
||||
heartbeat_enabled: number;
|
||||
heartbeat_interval_sec: number;
|
||||
heartbeat_policy_json: string | null;
|
||||
}
|
||||
| undefined;
|
||||
if (!row) throw new Error(`Agent ${agentId} not found.`);
|
||||
const current = parseJson<Record<string, unknown>>(row.heartbeat_policy_json, {});
|
||||
const merged = {
|
||||
enabled: policy.enabled ?? toBoolean(row.heartbeat_enabled),
|
||||
intervalSec: policy.intervalSec ?? row.heartbeat_interval_sec ?? 120,
|
||||
cooldownSec:
|
||||
policy.cooldownSec ??
|
||||
(typeof current.cooldownSec === "number" ? current.cooldownSec : 10),
|
||||
maxConcurrentRuns:
|
||||
policy.maxConcurrentRuns ??
|
||||
(typeof current.maxConcurrentRuns === "number" ? current.maxConcurrentRuns : 1),
|
||||
wakeOnDemand: policy.wakeOnDemand ?? (current.wakeOnDemand !== false),
|
||||
};
|
||||
const policyJson = JSON.stringify({
|
||||
cooldownSec: merged.cooldownSec,
|
||||
maxConcurrentRuns: merged.maxConcurrentRuns,
|
||||
wakeOnDemand: merged.wakeOnDemand,
|
||||
});
|
||||
const ts = nowIso();
|
||||
getDb()
|
||||
.prepare(
|
||||
"UPDATE agents SET heartbeat_enabled = ?, heartbeat_interval_sec = ?, heartbeat_policy_json = ?, updated_at = ? WHERE id = ? AND company_id = ?",
|
||||
)
|
||||
.run(merged.enabled ? 1 : 0, merged.intervalSec, policyJson, ts, agentId, companyId);
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user