1const REPLAY_WORKSPACE_PLACEHOLDER = "<workspace>";
2const REPLAY_HOME = "/home/replay-user";
3const REPLAY_CHECKOUT = "/home/replay-user/t3code";
4const REPLAY_HOSTNAME = "replay-host";
5const REPLAY_VALUES_BY_KEY: Readonly<Record<string, string>> = {
6 installationId: "00000000-0000-4000-8000-000000000000",
7 planType: "unknown",
8};
9
10interface CodexReplayRecordingMachine {
11 readonly home: string;
12 readonly hostname: string;
13 readonly checkout: string;
14}
15
16function withWorkspacePlaceholder(value: unknown, workspace: string): unknown {
17 if (value === workspace) {
18 return REPLAY_WORKSPACE_PLACEHOLDER;
19 }
20 if (Array.isArray(value)) {
21 return value.map((entry) => withWorkspacePlaceholder(entry, workspace));
22 }
23 if (typeof value !== "object" || value === null) {
24 return value;
25 }
26 return Object.fromEntries(
27 Object.entries(value).map(([key, entry]) => [key, withWorkspacePlaceholder(entry, workspace)]),
28 );
29}
30
31function withoutMachineIdentity(
32 value: unknown,
33 machine: CodexReplayRecordingMachine,
34 key?: string,
35): unknown {
36 if (typeof value === "string") {
37 const replacement = key === undefined ? undefined : REPLAY_VALUES_BY_KEY[key];
38 if (replacement !== undefined) return replacement;
39 return value === machine.hostname
40 ? REPLAY_HOSTNAME
41 : value.replaceAll(machine.checkout, REPLAY_CHECKOUT).replaceAll(machine.home, REPLAY_HOME);
42 }
43 if (Array.isArray(value)) {
44 return value.map((entry) => withoutMachineIdentity(entry, machine));
45 }
46 if (typeof value !== "object" || value === null) {
47 return value;
48 }
49 return Object.fromEntries(
50 Object.entries(value).map(([entryKey, entry]) => [
51 entryKey,
52 withoutMachineIdentity(entry, machine, entryKey),
53 ]),
54 );
55}
56
57export function codexReplayRecordingOutputRecords(
58 records: ReadonlyArray<Record<string, unknown>>,
59 options: {
60 readonly workspace: string;
61 readonly machine?: CodexReplayRecordingMachine;
62 },
63): ReadonlyArray<Record<string, unknown>> {
64 return records.map((record) => {
65 const named =
66 record.type === "expect_outbound"
67 ? { ...record, frame: withWorkspacePlaceholder(record.frame, options.workspace) }
68 : record;
69 return options.machine === undefined || !("frame" in named)
70 ? named
71 : { ...named, frame: withoutMachineIdentity(named.frame, options.machine) };
72 });
73}