db: split runtime facade by domain

This commit is contained in:
ejclaw
2026-04-11 08:09:29 +09:00
parent 10fa7fb747
commit af04fe04cf
5 changed files with 1117 additions and 956 deletions

View File

@@ -0,0 +1,375 @@
import fs from 'fs';
import {
CLAUDE_SERVICE_ID,
CODEX_MAIN_SERVICE_ID,
CODEX_REVIEW_SERVICE_ID,
SERVICE_ID,
SERVICE_SESSION_SCOPE,
} from '../config.js';
import {
resolveServiceTaskSessionsPath as resolveServiceTaskSessionsPathFromGroup,
resolveTaskRuntimeIpcPath as resolveTaskRuntimeIpcPathFromGroup,
resolveTaskSessionsPath as resolveTaskSessionsPathFromGroup,
} from '../group-folder.js';
import { logger } from '../logger.js';
import { inferAgentTypeFromServiceShadow } from '../role-service-shadow.js';
import { getTaskRuntimeTaskId } from '../task-watch-status.js';
import {
AgentType,
RegisteredGroup,
RoomMode,
ScheduledTask,
TaskRunLog,
} from '../types.js';
import {
type AssignRoomInput,
assignRoomInDatabase,
clearExplicitRoomModeInDatabase,
deleteStoredRoomSettingsForTestsInDatabase,
getAllRoomBindingsFromDatabase,
getEffectiveRoomModeFromDatabase,
getEffectiveRuntimeRoomModeFromDatabase,
getExplicitRoomModeFromDatabase,
getRegisteredAgentTypesForJidFromDatabase,
getRegisteredGroupFromDatabase,
getStoredRoomSettingsFromDatabase,
setExplicitRoomModeInDatabase,
setRegisteredGroupForTestsInDatabase,
setStoredRoomOwnerAgentTypeForTestsInDatabase,
updateRegisteredGroupNameInDatabase,
} from './rooms.js';
import { type StoredRoomSettings } from './room-registration.js';
import {
deleteAllSessionsForGroupFromDatabase,
deleteSessionFromDatabase,
getAllSessionsForAgentTypeFromDatabase,
getSessionFromDatabase,
setSessionInDatabase,
} from './sessions.js';
import {
getLastRespondingAgentTypeFromDatabase,
getRouterStateForServiceFromDatabase,
getRouterStateFromDatabase,
setRouterStateForServiceInDatabase,
setRouterStateInDatabase,
} from './router-state.js';
import {
getDatabaseIfInitialized,
requireDatabase,
} from './runtime-database.js';
import {
type CreateScheduledTaskInput,
type ScheduledTaskStatusTrackingUpdates,
type ScheduledTaskUpdates,
createTaskInDatabase,
findDuplicateCiWatcherInDatabase,
getAllTasksFromDatabase,
getDueTasksFromDatabase,
getRecentConsecutiveErrorsFromDatabase,
getTaskByIdFromDatabase,
getTasksForGroupFromDatabase,
hasActiveCiWatcherForChatInDatabase,
logTaskRunInDatabase,
updateTaskAfterRunInDatabase,
updateTaskInDatabase,
updateTaskStatusTrackingInDatabase,
} from './tasks.js';
/** @internal - for tests only. */
export function _setStoredRoomOwnerAgentTypeForTests(
chatJid: string,
ownerAgentType: AgentType | null,
): void {
setStoredRoomOwnerAgentTypeForTestsInDatabase(
requireDatabase(),
chatJid,
ownerAgentType,
);
}
/** @internal - for tests only. */
export function _deleteStoredRoomSettingsForTests(chatJid: string): void {
deleteStoredRoomSettingsForTestsInDatabase(requireDatabase(), chatJid);
}
export function createTask(task: CreateScheduledTaskInput): void {
createTaskInDatabase(requireDatabase(), task);
}
export function getTaskById(id: string): ScheduledTask | undefined {
return getTaskByIdFromDatabase(requireDatabase(), id);
}
export function findDuplicateCiWatcher(
chatJid: string,
ciProvider: string,
ciMetadata: string,
): ScheduledTask | undefined {
return findDuplicateCiWatcherInDatabase(
requireDatabase(),
chatJid,
ciProvider,
ciMetadata,
);
}
export function getTasksForGroup(
groupFolder: string,
agentType?: AgentType,
): ScheduledTask[] {
return getTasksForGroupFromDatabase(
requireDatabase(),
groupFolder,
agentType,
);
}
export function getAllTasks(agentType?: AgentType): ScheduledTask[] {
return getAllTasksFromDatabase(requireDatabase(), agentType);
}
export function updateTask(id: string, updates: ScheduledTaskUpdates): void {
updateTaskInDatabase(requireDatabase(), id, updates);
}
export function updateTaskStatusTracking(
id: string,
updates: ScheduledTaskStatusTrackingUpdates,
): void {
updateTaskStatusTrackingInDatabase(requireDatabase(), id, updates);
}
export function deleteTask(id: string): void {
const db = requireDatabase();
const task = getTaskByIdFromDatabase(db, id);
db.prepare('DELETE FROM task_run_logs WHERE task_id = ?').run(id);
db.prepare('DELETE FROM scheduled_tasks WHERE id = ?').run(id);
if (!task) return;
const runtimeTaskId = getTaskRuntimeTaskId(task);
if (!runtimeTaskId) return;
const cleanupTargets: string[] = [];
try {
cleanupTargets.push(
resolveTaskRuntimeIpcPathFromGroup(task.group_folder, runtimeTaskId),
resolveTaskSessionsPathFromGroup(task.group_folder, runtimeTaskId),
resolveServiceTaskSessionsPathFromGroup(
task.group_folder,
CLAUDE_SERVICE_ID,
runtimeTaskId,
),
resolveServiceTaskSessionsPathFromGroup(
task.group_folder,
CODEX_MAIN_SERVICE_ID,
runtimeTaskId,
),
resolveServiceTaskSessionsPathFromGroup(
task.group_folder,
CODEX_REVIEW_SERVICE_ID,
runtimeTaskId,
),
);
} catch (err) {
logger.warn(
{ taskId: id, groupFolder: task.group_folder, err },
'Failed to resolve task-scoped cleanup paths',
);
return;
}
for (const cleanupPath of cleanupTargets) {
try {
fs.rmSync(cleanupPath, { recursive: true, force: true });
} catch (err) {
logger.warn(
{ taskId: id, cleanupPath, err },
'Failed to remove task-scoped runtime artifacts',
);
}
}
}
export function hasActiveCiWatcherForChat(chatJid: string): boolean {
return hasActiveCiWatcherForChatInDatabase(requireDatabase(), chatJid);
}
export function getDueTasks(): ScheduledTask[] {
return getDueTasksFromDatabase(requireDatabase());
}
export function updateTaskAfterRun(
id: string,
nextRun: string | null,
lastResult: string,
): void {
updateTaskAfterRunInDatabase(requireDatabase(), id, nextRun, lastResult);
}
export function logTaskRun(log: TaskRunLog): void {
logTaskRunInDatabase(requireDatabase(), log);
}
export function getRecentConsecutiveErrors(
taskId: string,
limit: number = 5,
): string[] {
return getRecentConsecutiveErrorsFromDatabase(
requireDatabase(),
taskId,
limit,
);
}
export function getRouterState(key: string): string | undefined {
return getRouterStateFromDatabase(requireDatabase(), key, SERVICE_ID);
}
export function getRouterStateForService(
key: string,
serviceId: string,
): string | undefined {
return getRouterStateForServiceFromDatabase(
requireDatabase(),
key,
serviceId,
);
}
export function setRouterState(key: string, value: string): void {
setRouterStateInDatabase(requireDatabase(), key, value);
}
export function setRouterStateForService(
key: string,
value: string,
serviceId: string,
): void {
setRouterStateForServiceInDatabase(requireDatabase(), key, value, serviceId);
}
export function getSession(
groupFolder: string,
agentType: AgentType = 'claude-code',
): string | undefined {
return getSessionFromDatabase(requireDatabase(), groupFolder, agentType);
}
export function setSession(
groupFolder: string,
sessionId: string,
agentType: AgentType = 'claude-code',
): void {
setSessionInDatabase(requireDatabase(), groupFolder, agentType, sessionId);
}
export function deleteSession(
groupFolder: string,
agentType: AgentType = 'claude-code',
): void {
deleteSessionFromDatabase(requireDatabase(), groupFolder, agentType);
}
export function deleteAllSessionsForGroup(groupFolder: string): void {
deleteAllSessionsForGroupFromDatabase(requireDatabase(), groupFolder);
}
export function getAllSessions(): Record<string, string> {
const currentAgentType =
inferAgentTypeFromServiceShadow(SERVICE_SESSION_SCOPE) ?? 'claude-code';
return getAllSessionsForAgentTypeFromDatabase(
requireDatabase(),
currentAgentType,
);
}
export function getSessionForAgentType(
groupFolder: string,
agentType: string,
): string | undefined {
return getSessionFromDatabase(requireDatabase(), groupFolder, agentType);
}
export function setSessionForAgentType(
groupFolder: string,
agentType: string,
sessionId: string,
): void {
setSessionInDatabase(requireDatabase(), groupFolder, agentType, sessionId);
}
export function getLastRespondingAgentType(
chatJid: string,
): AgentType | undefined {
return getLastRespondingAgentTypeFromDatabase(requireDatabase(), chatJid);
}
export function getRegisteredGroup(
jid: string,
agentType?: string,
): (RegisteredGroup & { jid: string }) | undefined {
return getRegisteredGroupFromDatabase(requireDatabase(), jid, agentType);
}
/** @internal Test helper for seeding canonical room bindings. */
export function _setRegisteredGroupForTests(
jid: string,
group: RegisteredGroup,
): void {
setRegisteredGroupForTestsInDatabase(requireDatabase(), jid, group);
}
export function assignRoom(
chatJid: string,
input: AssignRoomInput,
): (RegisteredGroup & { jid: string }) | undefined {
return assignRoomInDatabase(requireDatabase(), chatJid, input);
}
export function updateRegisteredGroupName(jid: string, name: string): void {
updateRegisteredGroupNameInDatabase(requireDatabase(), jid, name);
}
export function getAllRoomBindings(
agentTypeFilter?: string,
): Record<string, RegisteredGroup> {
return getAllRoomBindingsFromDatabase(requireDatabase(), agentTypeFilter);
}
export function getRegisteredAgentTypesForJid(jid: string): AgentType[] {
const db = getDatabaseIfInitialized();
if (!db) return [];
return getRegisteredAgentTypesForJidFromDatabase(db, jid);
}
export function getStoredRoomSettings(
chatJid: string,
): StoredRoomSettings | undefined {
const db = getDatabaseIfInitialized();
if (!db) return undefined;
return getStoredRoomSettingsFromDatabase(db, chatJid);
}
export function getExplicitRoomMode(chatJid: string): RoomMode | undefined {
return getExplicitRoomModeFromDatabase(requireDatabase(), chatJid);
}
export function setExplicitRoomMode(chatJid: string, roomMode: RoomMode): void {
setExplicitRoomModeInDatabase(requireDatabase(), chatJid, roomMode);
}
export function clearExplicitRoomMode(chatJid: string): void {
clearExplicitRoomModeInDatabase(requireDatabase(), chatJid);
}
export function getEffectiveRoomMode(chatJid: string): RoomMode {
return getEffectiveRoomModeFromDatabase(requireDatabase(), chatJid);
}
export function getEffectiveRuntimeRoomMode(chatJid: string): RoomMode {
return getEffectiveRuntimeRoomModeFromDatabase(requireDatabase(), chatJid);
}