iteration-cli 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,17 @@
1
+ import { type CollectorConfig, type CollectorPaths } from "./storage.js";
2
+ type HistoryImportSize = Readonly<{
3
+ files: number;
4
+ bytes: number;
5
+ }>;
6
+ export type HistoryImportControl = Readonly<{
7
+ isRunning: () => Promise<boolean>;
8
+ stop: () => Promise<void>;
9
+ start: (directory: string) => Promise<unknown>;
10
+ acquireLock: (paths: CollectorPaths) => Promise<() => Promise<void>>;
11
+ }>;
12
+ export declare function importCodexHistory(config: CollectorConfig, paths: CollectorPaths, confirmed: boolean, control?: HistoryImportControl): Promise<Readonly<{
13
+ imported: boolean;
14
+ size: HistoryImportSize;
15
+ backgroundRunning: boolean | null;
16
+ }>>;
17
+ export {};
@@ -0,0 +1,60 @@
1
+ import path from "node:path";
2
+ import { installLaunchAgent, launchAgentStatus, uninstallLaunchAgent, } from "./launch-agent.js";
3
+ import { waitForCollectorLock } from "./service.js";
4
+ import { historyImportSize, loadState, resetHistoryCursors, } from "./storage.js";
5
+ const macControl = {
6
+ isRunning: launchAgentStatus,
7
+ stop: uninstallLaunchAgent,
8
+ start: installLaunchAgent,
9
+ acquireLock: waitForCollectorLock,
10
+ };
11
+ export async function importCodexHistory(config, paths, confirmed, control = macControl) {
12
+ const state = await loadState(paths);
13
+ if (!state.initialized ||
14
+ state.sourceDirectory !== path.resolve(config.codexHome)) {
15
+ throw new Error("Connect the Codex source before importing its history.");
16
+ }
17
+ const estimate = await historyImportSize(config.codexHome);
18
+ if (!confirmed)
19
+ return { imported: false, size: estimate, backgroundRunning: null };
20
+ const wasRunning = await control.isRunning();
21
+ let size = null;
22
+ let stopAttempted = false;
23
+ let rewound = false;
24
+ let failure = null;
25
+ try {
26
+ if (wasRunning) {
27
+ stopAttempted = true;
28
+ await control.stop();
29
+ }
30
+ const releaseLock = await control.acquireLock(paths);
31
+ try {
32
+ size = await resetHistoryCursors(config.codexHome, paths);
33
+ rewound = true;
34
+ }
35
+ finally {
36
+ await releaseLock();
37
+ }
38
+ }
39
+ catch (error) {
40
+ failure = { error };
41
+ }
42
+ if (stopAttempted) {
43
+ try {
44
+ await control.start(config.dataDirectory);
45
+ }
46
+ catch (error) {
47
+ throw new Error(rewound
48
+ ? "History checkpoints were rewound, but the collector could not restart. Run iteration start codex to resume uploads."
49
+ : "The collector could not be restored after a failed history import. Run iteration start codex to resume uploads.", {
50
+ cause: failure ? new AggregateError([failure.error, error]) : error,
51
+ });
52
+ }
53
+ }
54
+ if (failure)
55
+ throw failure.error;
56
+ if (size === null) {
57
+ throw new Error("History import did not update checkpoints.");
58
+ }
59
+ return { imported: true, size, backgroundRunning: wasRunning };
60
+ }
@@ -0,0 +1,4 @@
1
+ export declare function launchAgentPlist(cliPath?: string, nodePath?: string, logDirectory?: string, dataDirectory?: string): string;
2
+ export declare function installLaunchAgent(dataDirectory?: string): Promise<string>;
3
+ export declare function uninstallLaunchAgent(): Promise<void>;
4
+ export declare function launchAgentStatus(): Promise<boolean>;
@@ -0,0 +1,102 @@
1
+ import { promisify } from "node:util";
2
+ import { execFile as execFileCallback } from "node:child_process";
3
+ import { promises as fs } from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ const execFile = promisify(execFileCallback);
8
+ const LABEL = "com.iteration.codex-collector";
9
+ function xml(value) {
10
+ return value
11
+ .replaceAll("&", "&amp;")
12
+ .replaceAll("<", "&lt;")
13
+ .replaceAll(">", "&gt;")
14
+ .replaceAll('"', "&quot;")
15
+ .replaceAll("'", "&apos;");
16
+ }
17
+ function plistPath(homeDirectory = os.homedir()) {
18
+ return path.join(homeDirectory, "Library", "LaunchAgents", `${LABEL}.plist`);
19
+ }
20
+ export function launchAgentPlist(cliPath = fileURLToPath(new URL("./cli.js", import.meta.url)), nodePath = process.execPath, logDirectory = path.join(os.homedir(), "Library", "Logs"), dataDirectory = path.join(os.homedir(), ".config", "iteration", "codex-collector")) {
21
+ const argumentsList = [
22
+ nodePath,
23
+ cliPath,
24
+ "run",
25
+ "--data-directory",
26
+ dataDirectory,
27
+ ];
28
+ const argumentsXml = argumentsList
29
+ .map((argument) => `<string>${xml(argument)}</string>`)
30
+ .join("");
31
+ return [
32
+ '<?xml version="1.0" encoding="UTF-8"?>',
33
+ '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
34
+ '<plist version="1.0"><dict>',
35
+ `<key>Label</key><string>${LABEL}</string>`,
36
+ `<key>ProgramArguments</key><array>${argumentsXml}</array>`,
37
+ "<key>RunAtLoad</key><true/>",
38
+ "<key>KeepAlive</key><true/>",
39
+ "<key>ThrottleInterval</key><integer>10</integer>",
40
+ `<key>StandardOutPath</key><string>${xml(path.join(logDirectory, "iteration-codex-collector.log"))}</string>`,
41
+ `<key>StandardErrorPath</key><string>${xml(path.join(logDirectory, "iteration-codex-collector.error.log"))}</string>`,
42
+ "</dict></plist>",
43
+ ].join("");
44
+ }
45
+ function launchctlTarget() {
46
+ const getUserId = process.getuid;
47
+ if (getUserId === undefined) {
48
+ throw new Error("Could not determine the current macOS user id.");
49
+ }
50
+ const userId = getUserId();
51
+ if (userId === undefined)
52
+ throw new Error("Could not determine the current macOS user id.");
53
+ return `gui/${userId}`;
54
+ }
55
+ async function bootoutIfLoaded(target) {
56
+ try {
57
+ await execFile("launchctl", ["print", `${target}/${LABEL}`]);
58
+ }
59
+ catch (error) {
60
+ const details = error;
61
+ if (String(details.code) === "113" ||
62
+ details.stderr?.includes("Could not find service"))
63
+ return;
64
+ throw error;
65
+ }
66
+ await execFile("launchctl", ["bootout", `${target}/${LABEL}`]);
67
+ }
68
+ export async function installLaunchAgent(dataDirectory) {
69
+ if (process.platform !== "darwin") {
70
+ throw new Error("Automatic startup is currently supported on macOS only.");
71
+ }
72
+ const file = plistPath();
73
+ const launchAgents = path.dirname(file);
74
+ const logs = path.join(os.homedir(), "Library", "Logs");
75
+ await fs.mkdir(launchAgents, { recursive: true, mode: 0o755 });
76
+ await fs.mkdir(logs, { recursive: true, mode: 0o755 });
77
+ await fs.writeFile(file, launchAgentPlist(undefined, undefined, undefined, dataDirectory ??
78
+ path.join(os.homedir(), ".config", "iteration", "codex-collector")), { encoding: "utf8", mode: 0o644 });
79
+ const target = launchctlTarget();
80
+ await bootoutIfLoaded(target);
81
+ await execFile("launchctl", ["bootstrap", target, file]);
82
+ return file;
83
+ }
84
+ export async function uninstallLaunchAgent() {
85
+ if (process.platform !== "darwin") {
86
+ throw new Error("Automatic startup is currently supported on macOS only.");
87
+ }
88
+ const file = plistPath();
89
+ await bootoutIfLoaded(launchctlTarget());
90
+ await fs.rm(file, { force: true });
91
+ }
92
+ export async function launchAgentStatus() {
93
+ if (process.platform !== "darwin")
94
+ return false;
95
+ try {
96
+ await execFile("launchctl", ["print", `${launchctlTarget()}/${LABEL}`]);
97
+ return true;
98
+ }
99
+ catch {
100
+ return false;
101
+ }
102
+ }
@@ -0,0 +1,11 @@
1
+ import { type TraceEvent } from "iteration-trace-contracts";
2
+ export type RolloutParseResult = Readonly<{
3
+ kind: "event";
4
+ event: TraceEvent;
5
+ }> | Readonly<{
6
+ kind: "ignored";
7
+ }> | Readonly<{
8
+ kind: "gap";
9
+ reason: string;
10
+ }>;
11
+ export declare function parseRolloutLine(line: string, filename: string, byteOffset: number): RolloutParseResult;
@@ -0,0 +1,272 @@
1
+ import { createHash } from "node:crypto";
2
+ import path from "node:path";
3
+ import { parseTracePayload, } from "iteration-trace-contracts";
4
+ import * as z from "zod";
5
+ const recordSchema = z.record(z.string(), z.unknown());
6
+ function parseJsonRecord(value) {
7
+ const parsed = recordSchema.safeParse(value);
8
+ return parsed.success ? parsed.data : null;
9
+ }
10
+ function parseJsonString(value) {
11
+ const parsed = z.string().safeParse(value);
12
+ return parsed.success ? parsed.data : null;
13
+ }
14
+ function sessionFromPath(filename) {
15
+ const basename = path.basename(filename, ".jsonl");
16
+ return basename.startsWith("rollout-")
17
+ ? basename.slice("rollout-".length)
18
+ : basename;
19
+ }
20
+ function callIdFrom(record) {
21
+ return (parseJsonString(record.call_id) ?? parseJsonString(record.tool_call_id));
22
+ }
23
+ function sha256(value) {
24
+ return createHash("sha256").update(value, "utf8").digest("hex");
25
+ }
26
+ function safePayload(value) {
27
+ try {
28
+ const normalized = JSON.parse(JSON.stringify(value));
29
+ const parsed = parseTracePayload(normalized);
30
+ return parsed.success ? parsed.value : null;
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ function mapResponseItem(payload) {
37
+ const type = parseJsonString(payload.type);
38
+ if (type === "message") {
39
+ const role = parseJsonString(payload.role);
40
+ if (role === "user")
41
+ return { eventType: "user_message", callId: null };
42
+ if (role === "assistant")
43
+ return { eventType: "assistant_message", callId: null };
44
+ return null;
45
+ }
46
+ if (type === "agent_message") {
47
+ return { eventType: "assistant_message", callId: null };
48
+ }
49
+ if (["reasoning", "todo_list"].includes(type ?? ""))
50
+ return null;
51
+ if ([
52
+ "function_call",
53
+ "custom_tool_call",
54
+ "mcp_tool_call",
55
+ "web_search_call",
56
+ "tool_search_call",
57
+ "image_generation_call",
58
+ "local_shell_call",
59
+ ].includes(type ?? "")) {
60
+ return { eventType: "tool_call", callId: callIdFrom(payload) };
61
+ }
62
+ if ([
63
+ "function_call_output",
64
+ "custom_tool_call_output",
65
+ "mcp_tool_call_output",
66
+ "web_search_output",
67
+ "tool_search_output",
68
+ ].includes(type ?? "")) {
69
+ return { eventType: "tool_result", callId: callIdFrom(payload) };
70
+ }
71
+ return null;
72
+ }
73
+ function mapCompletedItem(item) {
74
+ const type = parseJsonString(item.type);
75
+ if ([
76
+ "command_execution",
77
+ "mcp_tool_call",
78
+ "collab_tool_call",
79
+ "web_search",
80
+ "file_change",
81
+ ].includes(type ?? "")) {
82
+ return { eventType: "tool_result", callId: callIdFrom(item) };
83
+ }
84
+ if (type === "agent_message") {
85
+ return { eventType: "assistant_message", callId: null };
86
+ }
87
+ return null;
88
+ }
89
+ function eventTimestamp(record) {
90
+ const timestamp = parseJsonString(record.timestamp);
91
+ return timestamp !== null && !Number.isNaN(Date.parse(timestamp))
92
+ ? timestamp
93
+ : null;
94
+ }
95
+ export function parseRolloutLine(line, filename, byteOffset) {
96
+ let decoded;
97
+ try {
98
+ decoded = JSON.parse(line);
99
+ }
100
+ catch {
101
+ return { kind: "gap", reason: "malformed_jsonl_record" };
102
+ }
103
+ const record = parseJsonRecord(decoded);
104
+ if (record === null)
105
+ return { kind: "gap", reason: "record_is_not_an_object" };
106
+ const recordType = parseJsonString(record.type);
107
+ const payload = parseJsonRecord(record.payload);
108
+ if ([
109
+ "session_meta",
110
+ "turn_context",
111
+ "compacted",
112
+ "token_usage_record",
113
+ "world_state",
114
+ "retained_context",
115
+ "security_risk_score",
116
+ ].includes(recordType ?? "")) {
117
+ return { kind: "ignored" };
118
+ }
119
+ if (recordType === "response_item" && payload !== null) {
120
+ const mapped = mapResponseItem(payload);
121
+ if (mapped === null) {
122
+ const payloadType = parseJsonString(payload.type) ?? "unknown";
123
+ if ([
124
+ "reasoning",
125
+ "compaction",
126
+ "context_compaction",
127
+ "configuration_update",
128
+ "additional_tools",
129
+ ].includes(payloadType)) {
130
+ return { kind: "ignored" };
131
+ }
132
+ return {
133
+ kind: "gap",
134
+ reason: `unsupported_response_item:${payloadType}`,
135
+ };
136
+ }
137
+ const eventPayload = safePayload(payload);
138
+ if (eventPayload === null)
139
+ return { kind: "gap", reason: "invalid_response_item_payload" };
140
+ const sessionId = sessionFromPath(filename);
141
+ return {
142
+ kind: "event",
143
+ event: {
144
+ eventId: sha256(`${sessionId}:${byteOffset}`),
145
+ sessionId,
146
+ sequence: byteOffset,
147
+ callId: mapped.callId,
148
+ projectId: null,
149
+ occurredAt: eventTimestamp(record),
150
+ eventType: mapped.eventType,
151
+ payload: eventPayload,
152
+ },
153
+ };
154
+ }
155
+ if (recordType === "event_msg" && payload !== null) {
156
+ const messageType = parseJsonString(payload.type);
157
+ if (messageType === "user_message") {
158
+ const eventPayload = safePayload(payload);
159
+ if (eventPayload === null)
160
+ return { kind: "gap", reason: "invalid_user_message_payload" };
161
+ const sessionId = sessionFromPath(filename);
162
+ return {
163
+ kind: "event",
164
+ event: {
165
+ eventId: sha256(`${sessionId}:${byteOffset}`),
166
+ sessionId,
167
+ sequence: byteOffset,
168
+ callId: null,
169
+ projectId: null,
170
+ occurredAt: eventTimestamp(record),
171
+ eventType: "user_message",
172
+ payload: eventPayload,
173
+ },
174
+ };
175
+ }
176
+ if (messageType === "agent_message") {
177
+ const eventPayload = safePayload(payload);
178
+ if (eventPayload === null)
179
+ return { kind: "gap", reason: "invalid_agent_message_payload" };
180
+ const sessionId = sessionFromPath(filename);
181
+ return {
182
+ kind: "event",
183
+ event: {
184
+ eventId: sha256(`${sessionId}:${byteOffset}`),
185
+ sessionId,
186
+ sequence: byteOffset,
187
+ callId: null,
188
+ projectId: null,
189
+ occurredAt: eventTimestamp(record),
190
+ eventType: "assistant_message",
191
+ payload: eventPayload,
192
+ },
193
+ };
194
+ }
195
+ if ([
196
+ "mcp_tool_call_end",
197
+ "web_search_end",
198
+ "image_generation_end",
199
+ "patch_apply_end",
200
+ ].includes(messageType ?? "")) {
201
+ const eventPayload = safePayload(payload);
202
+ if (eventPayload === null)
203
+ return { kind: "gap", reason: "invalid_tool_result_payload" };
204
+ const sessionId = sessionFromPath(filename);
205
+ return {
206
+ kind: "event",
207
+ event: {
208
+ eventId: sha256(`${sessionId}:${byteOffset}`),
209
+ sessionId,
210
+ sequence: byteOffset,
211
+ callId: callIdFrom(payload),
212
+ projectId: null,
213
+ occurredAt: eventTimestamp(record),
214
+ eventType: "tool_result",
215
+ payload: eventPayload,
216
+ },
217
+ };
218
+ }
219
+ if ([
220
+ "task_started",
221
+ "turn_started",
222
+ "task_complete",
223
+ "turn_complete",
224
+ "token_count",
225
+ "thread_goal_updated",
226
+ "thread_rolled_back",
227
+ "turn_aborted",
228
+ "thread_settings_applied",
229
+ "agent_reasoning",
230
+ "agent_reasoning_raw_content",
231
+ ].includes(messageType ?? "")) {
232
+ return { kind: "ignored" };
233
+ }
234
+ if (messageType !== "item_completed") {
235
+ return {
236
+ kind: "gap",
237
+ reason: `unsupported_event_message:${messageType ?? "unknown"}`,
238
+ };
239
+ }
240
+ const item = parseJsonRecord(payload.item);
241
+ if (item === null)
242
+ return { kind: "gap", reason: "item_completed_without_item" };
243
+ const mapped = mapCompletedItem(item);
244
+ if (mapped === null) {
245
+ const itemType = parseJsonString(item.type) ?? "unknown";
246
+ if (["reasoning", "todo_list"].includes(itemType))
247
+ return { kind: "ignored" };
248
+ return { kind: "gap", reason: `unsupported_completed_item:${itemType}` };
249
+ }
250
+ const eventPayload = safePayload(item);
251
+ if (eventPayload === null)
252
+ return { kind: "gap", reason: "invalid_completed_item_payload" };
253
+ const sessionId = sessionFromPath(filename);
254
+ return {
255
+ kind: "event",
256
+ event: {
257
+ eventId: sha256(`${sessionId}:${byteOffset}`),
258
+ sessionId,
259
+ sequence: byteOffset,
260
+ callId: mapped.callId,
261
+ projectId: null,
262
+ occurredAt: eventTimestamp(record),
263
+ eventType: mapped.eventType,
264
+ payload: eventPayload,
265
+ },
266
+ };
267
+ }
268
+ return {
269
+ kind: "gap",
270
+ reason: `unsupported_rollout_record:${recordType ?? "unknown"}`,
271
+ };
272
+ }
@@ -0,0 +1,4 @@
1
+ import { type CollectorPaths } from "./storage.js";
2
+ export declare function acquireCollectorLock(paths: CollectorPaths): Promise<() => Promise<void>>;
3
+ export declare function waitForCollectorLock(paths: CollectorPaths): Promise<() => Promise<void>>;
4
+ export declare function runCollector(directory?: string): Promise<void>;
@@ -0,0 +1,119 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import { setTimeout as delay } from "node:timers/promises";
4
+ import { collectorTick } from "./collector.js";
5
+ import { collectorPaths, ensurePrivateDirectory, loadConfig, } from "./storage.js";
6
+ function errorCode(error) {
7
+ if (error instanceof Error && "code" in error)
8
+ return String(error.code);
9
+ return null;
10
+ }
11
+ class CollectorLockBusyError extends Error {
12
+ }
13
+ export async function acquireCollectorLock(paths) {
14
+ await ensurePrivateDirectory(path.dirname(paths.lock));
15
+ for (let attempt = 0; attempt < 2; attempt += 1) {
16
+ try {
17
+ const handle = await fs.open(paths.lock, "wx", 0o600);
18
+ await handle.writeFile(`${process.pid}\n`, "utf8");
19
+ await handle.sync();
20
+ await handle.close();
21
+ return async () => {
22
+ const current = await fs.readFile(paths.lock, "utf8").catch(() => "");
23
+ if (current.trim() === String(process.pid)) {
24
+ await fs.rm(paths.lock, { force: true });
25
+ }
26
+ };
27
+ }
28
+ catch (error) {
29
+ if (errorCode(error) !== "EEXIST")
30
+ throw error;
31
+ const lockContents = await fs
32
+ .readFile(paths.lock, "utf8")
33
+ .catch(() => "");
34
+ const lockPid = Number(lockContents.trim());
35
+ if (!Number.isSafeInteger(lockPid) || lockPid < 2) {
36
+ await fs.rm(paths.lock, { force: true });
37
+ continue;
38
+ }
39
+ try {
40
+ process.kill(lockPid, 0);
41
+ throw new CollectorLockBusyError("Codex collector is already running.");
42
+ }
43
+ catch (processError) {
44
+ if (errorCode(processError) !== "ESRCH")
45
+ throw processError;
46
+ await fs.rm(paths.lock, { force: true });
47
+ }
48
+ }
49
+ }
50
+ throw new Error("Could not obtain the Codex collector lock.");
51
+ }
52
+ export async function waitForCollectorLock(paths) {
53
+ for (let attempt = 0; attempt < 81; attempt += 1) {
54
+ try {
55
+ return await acquireCollectorLock(paths);
56
+ }
57
+ catch (error) {
58
+ if (!(error instanceof CollectorLockBusyError))
59
+ throw error;
60
+ if (attempt === 80) {
61
+ throw new Error("Collector did not stop within 20 seconds; history was not changed.", { cause: error });
62
+ }
63
+ await delay(250);
64
+ }
65
+ }
66
+ throw new Error("Could not obtain the collector lock.");
67
+ }
68
+ export async function runCollector(directory) {
69
+ const paths = collectorPaths(directory);
70
+ const releaseLock = await acquireCollectorLock(paths);
71
+ const controller = new AbortController();
72
+ const stop = () => controller.abort();
73
+ process.on("SIGTERM", stop);
74
+ process.on("SIGINT", stop);
75
+ let retryDelayMs = 5000;
76
+ try {
77
+ const config = await loadConfig(paths);
78
+ while (!controller.signal.aborted) {
79
+ try {
80
+ const result = await collectorTick(config, paths, fetch, controller.signal);
81
+ if (result.discovered > 0 || result.uploaded > 0) {
82
+ console.info(JSON.stringify({
83
+ level: "info",
84
+ discovered: result.discovered,
85
+ uploaded: result.uploaded,
86
+ outboxBytes: result.outboxBytes,
87
+ }));
88
+ }
89
+ retryDelayMs = 5000;
90
+ }
91
+ catch (error) {
92
+ if (controller.signal.aborted)
93
+ break;
94
+ console.error(JSON.stringify({
95
+ level: "error",
96
+ operation: "collector_tick",
97
+ errorName: error instanceof Error ? error.name : "UnknownError",
98
+ message: error instanceof Error
99
+ ? error.message
100
+ : "Unknown collector failure.",
101
+ }));
102
+ retryDelayMs = Math.min(retryDelayMs * 2, 60_000);
103
+ }
104
+ try {
105
+ await delay(retryDelayMs, undefined, { signal: controller.signal });
106
+ }
107
+ catch (error) {
108
+ if (error instanceof Error && error.name === "AbortError")
109
+ break;
110
+ throw error;
111
+ }
112
+ }
113
+ }
114
+ finally {
115
+ process.off("SIGTERM", stop);
116
+ process.off("SIGINT", stop);
117
+ await releaseLock();
118
+ }
119
+ }
@@ -0,0 +1,44 @@
1
+ import { type TraceEvent } from "iteration-trace-contracts";
2
+ export type CollectorConfig = Readonly<{
3
+ endpoint: string;
4
+ token: string;
5
+ codexHome: string;
6
+ dataDirectory: string;
7
+ }>;
8
+ export type CollectorState = Readonly<{
9
+ cursors: Readonly<Record<string, number>>;
10
+ skipping: readonly string[];
11
+ sourceDirectory: string | null;
12
+ initialized: boolean;
13
+ }>;
14
+ export type CollectorPaths = Readonly<{
15
+ config: string;
16
+ state: string;
17
+ outbox: string;
18
+ lock: string;
19
+ }>;
20
+ export declare function validateCollectorEndpoint(endpoint: string): URL;
21
+ export declare function collectorPaths(directory?: string): CollectorPaths;
22
+ export declare function assertFreshCollectorDirectory(paths: CollectorPaths): Promise<void>;
23
+ export declare function ensurePrivateDirectory(directory: string): Promise<void>;
24
+ export declare function saveConfig(config: CollectorConfig, paths: CollectorPaths): Promise<void>;
25
+ export declare function loadConfig(paths: CollectorPaths): Promise<CollectorConfig>;
26
+ export declare function loadState(paths: CollectorPaths): Promise<CollectorState>;
27
+ export declare function saveState(state: CollectorState, paths: CollectorPaths): Promise<void>;
28
+ export declare function outboxSize(paths: CollectorPaths): Promise<number>;
29
+ export declare function pendingEventIds(paths: CollectorPaths): Promise<Set<string>>;
30
+ export declare function appendEvents(events: readonly TraceEvent[], paths: CollectorPaths): Promise<void>;
31
+ export declare function nextBatch(paths: CollectorPaths, maxBytes: number): Promise<readonly TraceEvent[]>;
32
+ export declare function acknowledgeEvents(acknowledgedIds: ReadonlySet<string>, paths: CollectorPaths): Promise<void>;
33
+ export declare function initializeState(codexHome: string, paths: CollectorPaths, includeHistory: boolean): Promise<void>;
34
+ export declare function prepareInitialState(codexHome: string, includeHistory: boolean): Promise<CollectorState>;
35
+ export declare function ensureStartableState(config: CollectorConfig, paths: CollectorPaths): Promise<boolean>;
36
+ export declare function resetHistoryCursors(codexHome: string, paths: CollectorPaths): Promise<Readonly<{
37
+ files: number;
38
+ bytes: number;
39
+ }>>;
40
+ export declare function historyImportSize(codexHome: string): Promise<Readonly<{
41
+ files: number;
42
+ bytes: number;
43
+ }>>;
44
+ export declare function rolloutFiles(root: string): Promise<string[]>;