refactor: remove legacy config and state aliases (PR5)
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import { Database } from 'bun:sqlite';
|
||||
|
||||
import { DATA_DIR, STORE_DIR } from '../src/config.js';
|
||||
import { listUnexpectedDataStateFiles } from '../src/data-state-files.js';
|
||||
import {
|
||||
initializeDatabaseSchema,
|
||||
openPersistentDatabase,
|
||||
@@ -11,112 +11,16 @@ import {
|
||||
import {
|
||||
buildLegacyRoomMigrationPlan,
|
||||
getPendingLegacyRegisteredGroupJids,
|
||||
getStoredRoomSettingsRowFromDatabase,
|
||||
insertStoredRoomSettingsFromMigration,
|
||||
normalizeStoredAgentType,
|
||||
upsertRoomRoleOverride,
|
||||
} from '../src/db/room-registration.js';
|
||||
import type { RegisteredGroup } from '../src/types.js';
|
||||
import { isValidGroupFolder } from '../src/group-folder.js';
|
||||
import { logger } from '../src/logger.js';
|
||||
import { readJsonFile } from '../src/utils.js';
|
||||
import { emitStatus } from './status.js';
|
||||
|
||||
interface MigrationReport {
|
||||
migratedRooms: number;
|
||||
migratedRoleOverrides: number;
|
||||
skippedRooms: number;
|
||||
migratedJsonRooms: number;
|
||||
skippedJsonRooms: number;
|
||||
backedUpLegacyRows: number;
|
||||
renamedLegacyJson: boolean;
|
||||
}
|
||||
|
||||
interface StoredOwnerRoleOverride {
|
||||
agentType: string;
|
||||
agentConfig?: RegisteredGroup['agentConfig'];
|
||||
}
|
||||
|
||||
function normalizeAgentConfigValue(
|
||||
agentConfig: RegisteredGroup['agentConfig'] | undefined,
|
||||
): string {
|
||||
return JSON.stringify(agentConfig ?? null);
|
||||
}
|
||||
|
||||
function jsonEntryMatchesStoredRoom(
|
||||
jid: string,
|
||||
group: RegisteredGroup,
|
||||
database: Database,
|
||||
): boolean {
|
||||
const existing = getStoredRoomSettingsRowFromDatabase(database, jid);
|
||||
if (!existing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ownerAgentType =
|
||||
normalizeStoredAgentType(group.agentType) ?? 'claude-code';
|
||||
return (
|
||||
existing.roomMode === 'single' &&
|
||||
existing.name === group.name &&
|
||||
existing.folder === group.folder &&
|
||||
existing.trigger === group.trigger &&
|
||||
(existing.requiresTrigger ?? true) === (group.requiresTrigger ?? true) &&
|
||||
(existing.isMain ?? false) === (group.isMain ?? false) &&
|
||||
existing.ownerAgentType === ownerAgentType &&
|
||||
(existing.workDir ?? null) === (group.workDir ?? null)
|
||||
);
|
||||
}
|
||||
|
||||
function getStoredOwnerRoleOverride(
|
||||
database: Database,
|
||||
jid: string,
|
||||
): StoredOwnerRoleOverride | undefined {
|
||||
try {
|
||||
const row = database
|
||||
.prepare(
|
||||
`SELECT agent_type, agent_config_json
|
||||
FROM room_role_overrides
|
||||
WHERE chat_jid = ?
|
||||
AND role = 'owner'`,
|
||||
)
|
||||
.get(jid) as
|
||||
| {
|
||||
agent_type: string | null;
|
||||
agent_config_json: string | null;
|
||||
}
|
||||
| undefined;
|
||||
const agentType = normalizeStoredAgentType(row?.agent_type);
|
||||
if (!agentType) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
agentType,
|
||||
agentConfig: row?.agent_config_json
|
||||
? JSON.parse(row.agent_config_json)
|
||||
: undefined,
|
||||
};
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function jsonEntryMatchesStoredOwnerOverride(
|
||||
database: Database,
|
||||
jid: string,
|
||||
group: RegisteredGroup,
|
||||
): boolean {
|
||||
const existingOverride = getStoredOwnerRoleOverride(database, jid);
|
||||
if (!existingOverride) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ownerAgentType =
|
||||
normalizeStoredAgentType(group.agentType) ?? 'claude-code';
|
||||
return (
|
||||
existingOverride.agentType === ownerAgentType &&
|
||||
normalizeAgentConfigValue(existingOverride.agentConfig) ===
|
||||
normalizeAgentConfigValue(group.agentConfig)
|
||||
);
|
||||
}
|
||||
|
||||
function ensureLegacyBackupTable(database: Database): void {
|
||||
@@ -217,125 +121,15 @@ function migrateLegacyRegisteredGroupsTable(
|
||||
}
|
||||
}
|
||||
|
||||
function loadValidatedLegacyRegisteredGroupsJson(
|
||||
database: Database,
|
||||
): Array<{ jid: string; group: RegisteredGroup }> {
|
||||
const legacyJsonPath = path.join(DATA_DIR, 'registered_groups.json');
|
||||
const legacyJson = readJsonFile(legacyJsonPath) as Record<
|
||||
string,
|
||||
RegisteredGroup
|
||||
> | null;
|
||||
|
||||
if (!legacyJson) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const entries = Object.entries(legacyJson);
|
||||
for (const [jid, group] of entries) {
|
||||
if (!isValidGroupFolder(group.folder)) {
|
||||
throw new Error(
|
||||
`Invalid legacy registered_groups.json folder for ${jid}: ${group.folder}`,
|
||||
);
|
||||
}
|
||||
|
||||
const existing = getStoredRoomSettingsRowFromDatabase(database, jid);
|
||||
if (existing && !jsonEntryMatchesStoredRoom(jid, group, database)) {
|
||||
throw new Error(
|
||||
`Legacy registered_groups.json entry collides with existing room_settings row for ${jid}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return entries.map(([jid, group]) => ({ jid, group }));
|
||||
}
|
||||
|
||||
function migrateLegacyRegisteredGroupsJson(
|
||||
database: Database,
|
||||
projectRoot: string,
|
||||
report: MigrationReport,
|
||||
): void {
|
||||
const legacyJsonPath = path.join(DATA_DIR, 'registered_groups.json');
|
||||
const legacyJsonEntries = loadValidatedLegacyRegisteredGroupsJson(database);
|
||||
if (legacyJsonEntries.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const insertRoom = database.prepare(
|
||||
`INSERT INTO room_settings (
|
||||
chat_jid,
|
||||
room_mode,
|
||||
mode_source,
|
||||
name,
|
||||
folder,
|
||||
trigger_pattern,
|
||||
requires_trigger,
|
||||
is_main,
|
||||
owner_agent_type,
|
||||
work_dir,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(chat_jid) DO NOTHING`,
|
||||
);
|
||||
|
||||
for (const { jid, group } of legacyJsonEntries) {
|
||||
const createdAt = group.added_at || new Date().toISOString();
|
||||
const ownerAgentType =
|
||||
normalizeStoredAgentType(group.agentType) ?? 'claude-code';
|
||||
const existing = getStoredRoomSettingsRowFromDatabase(database, jid);
|
||||
const existingOwnerOverride = getStoredOwnerRoleOverride(database, jid);
|
||||
|
||||
if (!existing) {
|
||||
insertRoom.run(
|
||||
jid,
|
||||
'single',
|
||||
'inferred',
|
||||
group.name,
|
||||
group.folder,
|
||||
group.trigger,
|
||||
(group.requiresTrigger ?? true) ? 1 : 0,
|
||||
(group.isMain ?? false) ? 1 : 0,
|
||||
ownerAgentType,
|
||||
group.workDir ?? null,
|
||||
createdAt,
|
||||
createdAt,
|
||||
);
|
||||
report.migratedRooms += 1;
|
||||
report.migratedJsonRooms += 1;
|
||||
}
|
||||
|
||||
if (
|
||||
existingOwnerOverride &&
|
||||
!jsonEntryMatchesStoredOwnerOverride(database, jid, group)
|
||||
) {
|
||||
throw new Error(
|
||||
`Legacy registered_groups.json owner override conflicts with existing owner override for ${jid}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!existingOwnerOverride) {
|
||||
upsertRoomRoleOverride(database, jid, {
|
||||
role: 'owner',
|
||||
agentType: ownerAgentType,
|
||||
agentConfig: group.agentConfig,
|
||||
createdAt,
|
||||
updatedAt: createdAt,
|
||||
});
|
||||
report.migratedRoleOverrides += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fs.renameSync(legacyJsonPath, `${legacyJsonPath}.migrated`);
|
||||
report.renamedLegacyJson = true;
|
||||
logger.info(
|
||||
{ legacyJsonPath, projectRoot },
|
||||
'Renamed registered_groups.json after explicit migration',
|
||||
);
|
||||
}
|
||||
|
||||
export async function run(_args: string[]): Promise<void> {
|
||||
fs.mkdirSync(STORE_DIR, { recursive: true });
|
||||
const projectRoot = process.cwd();
|
||||
const unexpectedDataStateFiles = listUnexpectedDataStateFiles(DATA_DIR);
|
||||
if (unexpectedDataStateFiles.length > 0) {
|
||||
throw new Error(
|
||||
`Unexpected data state files detected before room migration (files=${unexpectedDataStateFiles.join(',')})`,
|
||||
);
|
||||
}
|
||||
|
||||
const database = openPersistentDatabase();
|
||||
initializeDatabaseSchema(database);
|
||||
|
||||
@@ -343,16 +137,12 @@ export async function run(_args: string[]): Promise<void> {
|
||||
migratedRooms: 0,
|
||||
migratedRoleOverrides: 0,
|
||||
skippedRooms: 0,
|
||||
migratedJsonRooms: 0,
|
||||
skippedJsonRooms: 0,
|
||||
backedUpLegacyRows: 0,
|
||||
renamedLegacyJson: false,
|
||||
};
|
||||
|
||||
database.transaction(() => {
|
||||
report.backedUpLegacyRows = backupPendingLegacyRows(database);
|
||||
migrateLegacyRegisteredGroupsTable(database, report);
|
||||
migrateLegacyRegisteredGroupsJson(database, projectRoot, report);
|
||||
})();
|
||||
|
||||
database.close();
|
||||
@@ -361,10 +151,7 @@ export async function run(_args: string[]): Promise<void> {
|
||||
MIGRATED_ROOMS: report.migratedRooms,
|
||||
MIGRATED_ROLE_OVERRIDES: report.migratedRoleOverrides,
|
||||
SKIPPED_ROOMS: report.skippedRooms,
|
||||
MIGRATED_JSON_ROOMS: report.migratedJsonRooms,
|
||||
SKIPPED_JSON_ROOMS: report.skippedJsonRooms,
|
||||
BACKED_UP_LEGACY_ROWS: report.backedUpLegacyRows,
|
||||
RENAMED_LEGACY_JSON: report.renamedLegacyJson,
|
||||
STATUS: 'success',
|
||||
LOG: 'logs/setup.log',
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user