Files
AgentCompany/src/main/services/migration-cutover-journal-service.ts
T
ZacharyZhang-NY 07d0b4587d Promote AgentCompany-New to the repository root and retire the old app
The legacy application - its src, tests, landing site, promo assets, docs,
release tooling, CI workflows, and root configs - leaves the tree; it stays
reachable in history. Everything under AgentCompany-New moves up unchanged:
same package name, so the production userData/database path is untouched,
and e2e/build scripts already resolved paths relative to the project. The
developer cred.txt stays gitignored at its new location. Full verify:ci
passes from the new root.
2026-07-07 00:04:13 -04:00

104 lines
2.7 KiB
TypeScript

import type { DatabaseSync } from "node:sqlite";
import { createId } from "../../shared/ids";
const REPLAYABLE_ENTITY_TYPES = new Set([
"agent",
"approval",
"browser_action",
"chat_message",
"chat_space",
"file",
"scheduled_post",
"social_account",
"task",
"workspace",
]);
export interface CutoverDeltaEvent {
id: string;
entityType: string;
entityId: string;
actorType: string;
actorId: string | null;
action: string;
payload: Record<string, unknown>;
createdAt: string;
}
export interface RecordCutoverEventInput {
orgId: string;
migrationReportId: string;
rollbackSnapshotId: string | null;
eventType: string;
actorType: string;
actorId: string | null;
payload: Record<string, unknown>;
createdAt?: string;
}
export class MigrationCutoverJournalService {
constructor(private readonly db: DatabaseSync) {}
recordEvent(input: RecordCutoverEventInput): string {
const id = createId("mce");
this.db.prepare(`
insert into migration_cutover_events(
id, org_id, migration_report_id, rollback_snapshot_id, event_type,
actor_type, actor_id, payload_json, created_at
) values (?, ?, ?, ?, ?, ?, ?, ?, ?)
`).run(
id,
input.orgId,
input.migrationReportId,
input.rollbackSnapshotId,
input.eventType,
input.actorType,
input.actorId,
JSON.stringify(input.payload),
input.createdAt ?? new Date().toISOString(),
);
return id;
}
queueReconciliation(input: {
orgId: string;
migrationReportId: string;
rollbackSnapshotId: string;
events: CutoverDeltaEvent[];
createdAt?: string;
}): number {
let queued = 0;
const createdAt = input.createdAt ?? new Date().toISOString();
const statement = this.db.prepare(`
insert or ignore into migration_reconciliation_items(
id, org_id, migration_report_id, rollback_snapshot_id, source_event_id,
target_type, target_id, reason, state, payload_json, created_at, resolved_at, resolved_by_employee_id
) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, null, null)
`);
for (const event of input.events) {
if (!REPLAYABLE_ENTITY_TYPES.has(event.entityType)) continue;
const result = statement.run(
createId("mri"),
input.orgId,
input.migrationReportId,
input.rollbackSnapshotId,
event.id,
event.entityType,
event.entityId,
"manual_delta_replay",
"open",
JSON.stringify({
action: event.action,
actorType: event.actorType,
actorId: event.actorId,
payload: event.payload,
createdAt: event.createdAt,
}),
createdAt,
);
queued += Number(result.changes);
}
return queued;
}
}