iteration-cli 0.9.1 → 0.10.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.
Files changed (41) hide show
  1. package/README.md +4 -0
  2. package/dist/cli.js +18207 -429
  3. package/dist/cursor-hook-entry.js +11667 -139
  4. package/dist/launcher.js +6731 -0
  5. package/package.json +4 -3
  6. package/dist/auto-connect.d.ts +0 -32
  7. package/dist/auto-connect.js +0 -235
  8. package/dist/background-service.d.ts +0 -7
  9. package/dist/background-service.js +0 -37
  10. package/dist/claude-transcript.d.ts +0 -6
  11. package/dist/claude-transcript.js +0 -256
  12. package/dist/clerk-device-auth.d.ts +0 -7
  13. package/dist/clerk-device-auth.js +0 -279
  14. package/dist/cli.d.ts +0 -2
  15. package/dist/collector-fetch.d.ts +0 -9
  16. package/dist/collector-fetch.js +0 -57
  17. package/dist/collector.d.ts +0 -17
  18. package/dist/collector.js +0 -268
  19. package/dist/cursor-hook-entry.d.ts +0 -1
  20. package/dist/cursor-hooks.d.ts +0 -7
  21. package/dist/cursor-hooks.js +0 -163
  22. package/dist/cursor-transcript.d.ts +0 -8
  23. package/dist/cursor-transcript.js +0 -294
  24. package/dist/history-import.d.ts +0 -17
  25. package/dist/history-import.js +0 -63
  26. package/dist/launch-agent.d.ts +0 -13
  27. package/dist/launch-agent.js +0 -183
  28. package/dist/redact-trace.d.ts +0 -2
  29. package/dist/redact-trace.js +0 -186
  30. package/dist/rollout.d.ts +0 -11
  31. package/dist/rollout.js +0 -259
  32. package/dist/service.d.ts +0 -5
  33. package/dist/service.js +0 -249
  34. package/dist/status-view.d.ts +0 -33
  35. package/dist/status-view.js +0 -257
  36. package/dist/storage.d.ts +0 -94
  37. package/dist/storage.js +0 -809
  38. package/dist/systemd-user.d.ts +0 -7
  39. package/dist/systemd-user.js +0 -137
  40. package/dist/turn-summary.d.ts +0 -23
  41. package/dist/turn-summary.js +0 -365
package/dist/service.js DELETED
@@ -1,249 +0,0 @@
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 { connectNewDetectedSources, loadMachineConsent, } from "./auto-connect.js";
5
- import { collectorFetch } from "./collector-fetch.js";
6
- import { collectorTick, TraceUploadError } from "./collector.js";
7
- import { refreshTurnSummaries } from "./turn-summary.js";
8
- import { installCursorHooks } from "./cursor-hooks.js";
9
- import { collectorSource, collectorPaths, ensureClaudeHistoryBackfill, ensureCursorHistoryBackfill, ensureHistoryBackfill, ensurePrivateDirectory, healthSnapshot, loadConfig, saveHealth, } from "./storage.js";
10
- function errorCode(error) {
11
- if (error instanceof Error && "code" in error)
12
- return String(error.code);
13
- return null;
14
- }
15
- class CollectorLockBusyError extends Error {
16
- }
17
- // Fetch reports network failures as a generic TypeError; the actionable
18
- // reason (DNS, TLS, reset, timeout) is only in its cause chain.
19
- function errorCauses(error) {
20
- const causes = [];
21
- let current = error instanceof Error ? error.cause : undefined;
22
- while (current instanceof Error && causes.length < 4) {
23
- causes.push({
24
- name: current.name,
25
- code: errorCode(current),
26
- message: current.message.slice(0, 300),
27
- });
28
- current = current.cause;
29
- }
30
- return causes;
31
- }
32
- function collectorFailure(error) {
33
- return {
34
- atMs: Date.now(),
35
- kind: error instanceof TraceUploadError ? error.kind : "collector",
36
- status: error instanceof TraceUploadError ? error.status : null,
37
- };
38
- }
39
- async function persistHealth(health, paths) {
40
- try {
41
- await saveHealth(health, paths);
42
- }
43
- catch {
44
- console.error(JSON.stringify({
45
- level: "warning",
46
- operation: "save_collector_health",
47
- message: "Local collector status could not be saved; collection continues.",
48
- }));
49
- }
50
- }
51
- export async function acquireCollectorLock(paths) {
52
- await ensurePrivateDirectory(path.dirname(paths.lock));
53
- for (let attempt = 0; attempt < 2; attempt += 1) {
54
- try {
55
- const handle = await fs.open(paths.lock, "wx", 0o600);
56
- await handle.writeFile(`${process.pid}\n`, "utf8");
57
- await handle.sync();
58
- await handle.close();
59
- return async () => {
60
- const current = await fs.readFile(paths.lock, "utf8").catch(() => "");
61
- if (current.trim() === String(process.pid)) {
62
- await fs.rm(paths.lock, { force: true });
63
- }
64
- };
65
- }
66
- catch (error) {
67
- if (errorCode(error) !== "EEXIST")
68
- throw error;
69
- const lockContents = await fs
70
- .readFile(paths.lock, "utf8")
71
- .catch(() => "");
72
- const lockPid = Number(lockContents.trim());
73
- if (!Number.isSafeInteger(lockPid) || lockPid < 2) {
74
- await fs.rm(paths.lock, { force: true });
75
- continue;
76
- }
77
- try {
78
- process.kill(lockPid, 0);
79
- throw new CollectorLockBusyError("Collector is already running.");
80
- }
81
- catch (processError) {
82
- if (errorCode(processError) !== "ESRCH")
83
- throw processError;
84
- await fs.rm(paths.lock, { force: true });
85
- }
86
- }
87
- }
88
- throw new Error("Could not obtain the collector lock.");
89
- }
90
- export async function waitForCollectorLock(paths) {
91
- for (let attempt = 0; attempt < 81; attempt += 1) {
92
- try {
93
- return await acquireCollectorLock(paths);
94
- }
95
- catch (error) {
96
- if (!(error instanceof CollectorLockBusyError))
97
- throw error;
98
- if (attempt === 80) {
99
- throw new Error("Collector did not stop within 20 seconds; history was not changed.", { cause: error });
100
- }
101
- await delay(250);
102
- }
103
- }
104
- throw new Error("Could not obtain the collector lock.");
105
- }
106
- export async function runCollector(directory, expectedSource) {
107
- const paths = collectorPaths(directory);
108
- const releaseLock = await acquireCollectorLock(paths);
109
- const controller = new AbortController();
110
- const stop = () => controller.abort();
111
- process.on("SIGTERM", stop);
112
- process.on("SIGINT", stop);
113
- let retryDelayMs = 5000;
114
- try {
115
- const config = await loadConfig(paths);
116
- const source = collectorSource(config);
117
- if (expectedSource !== undefined && expectedSource !== source) {
118
- throw new Error(`Collector directory belongs to ${source}, not ${expectedSource}.`);
119
- }
120
- const loadedHealth = await healthSnapshot(paths);
121
- let health = loadedHealth.health;
122
- if (loadedHealth.kind === "unavailable") {
123
- console.error(JSON.stringify({
124
- level: "warning",
125
- operation: "load_collector_health",
126
- message: "Local collector status could not be read; collection continues.",
127
- }));
128
- }
129
- if ("cursorHome" in config) {
130
- await ensureCursorHistoryBackfill(config.cursorHome, paths);
131
- await installCursorHooks(config.cursorHome, config.dataDirectory);
132
- }
133
- else if ("claudeHome" in config)
134
- await ensureClaudeHistoryBackfill(config.claudeHome, paths);
135
- else
136
- await ensureHistoryBackfill(config.codexHome, paths);
137
- const summariesRefreshedAt = new Map();
138
- let lastAutoCheckAt = 0;
139
- while (!controller.signal.aborted) {
140
- if (Date.now() - lastAutoCheckAt >= 60_000) {
141
- lastAutoCheckAt = Date.now();
142
- try {
143
- const consent = await loadMachineConsent();
144
- if (consent?.anchorSource === source) {
145
- const connected = await connectNewDetectedSources(config, consent);
146
- if (connected.length > 0) {
147
- console.info(JSON.stringify({
148
- level: "info",
149
- operation: "auto_connect_sources",
150
- connected,
151
- }));
152
- }
153
- }
154
- }
155
- catch (error) {
156
- console.error(JSON.stringify({
157
- level: "error",
158
- operation: "auto_connect_sources",
159
- message: error instanceof Error
160
- ? error.message
161
- : "Automatic source connection failed.",
162
- }));
163
- }
164
- }
165
- let acknowledgedAtMs = null;
166
- try {
167
- const result = await collectorTick(config, paths, collectorFetch, controller.signal, () => {
168
- acknowledgedAtMs = Date.now();
169
- });
170
- if (acknowledgedAtMs !== null ||
171
- health.lastFailure !== null ||
172
- (result.historyCaughtUp && health.historyCaughtUpAtMs === null)) {
173
- health = {
174
- lastUploadAtMs: acknowledgedAtMs ?? health.lastUploadAtMs,
175
- historyCaughtUpAtMs: health.historyCaughtUpAtMs ??
176
- (result.historyCaughtUp ? Date.now() : null),
177
- lastFailure: null,
178
- };
179
- await persistHealth(health, paths);
180
- }
181
- if (result.discovered > 0 || result.uploaded > 0) {
182
- console.info(JSON.stringify({
183
- level: "info",
184
- at: new Date().toISOString(),
185
- source,
186
- discovered: result.discovered,
187
- uploaded: result.uploaded,
188
- outboxBytes: result.outboxBytes,
189
- }));
190
- }
191
- retryDelayMs = 5000;
192
- }
193
- catch (error) {
194
- if (controller.signal.aborted)
195
- break;
196
- health = {
197
- lastUploadAtMs: acknowledgedAtMs ?? health.lastUploadAtMs,
198
- historyCaughtUpAtMs: health.historyCaughtUpAtMs,
199
- lastFailure: collectorFailure(error),
200
- };
201
- console.error(JSON.stringify({
202
- level: "error",
203
- at: new Date().toISOString(),
204
- source,
205
- operation: "collector_tick",
206
- errorName: error instanceof Error ? error.name : "UnknownError",
207
- causes: errorCauses(error),
208
- message: error instanceof Error
209
- ? error.message
210
- : "Unknown collector failure.",
211
- }));
212
- await persistHealth(health, paths);
213
- retryDelayMs = Math.min(retryDelayMs * 2, 60_000);
214
- }
215
- try {
216
- if (source !== "cursor")
217
- await refreshTurnSummaries(config, summariesRefreshedAt, Date.now(), collectorFetch, controller.signal);
218
- }
219
- catch (error) {
220
- if (controller.signal.aborted)
221
- break;
222
- console.error(JSON.stringify({
223
- level: "warning",
224
- at: new Date().toISOString(),
225
- source,
226
- operation: "refresh_turn_summaries",
227
- errorName: error instanceof Error ? error.name : "UnknownError",
228
- causes: errorCauses(error),
229
- message: error instanceof Error
230
- ? error.message
231
- : "Unknown turn summary failure.",
232
- }));
233
- }
234
- try {
235
- await delay(retryDelayMs, undefined, { signal: controller.signal });
236
- }
237
- catch (error) {
238
- if (error instanceof Error && error.name === "AbortError")
239
- break;
240
- throw error;
241
- }
242
- }
243
- }
244
- finally {
245
- process.off("SIGTERM", stop);
246
- process.off("SIGINT", stop);
247
- await releaseLock();
248
- }
249
- }
@@ -1,33 +0,0 @@
1
- import { type CollectorSource } from "iteration-trace-contracts";
2
- import type { CollectorHealth } from "./storage.js";
3
- type ScanProgress = Readonly<{
4
- kind: "waiting";
5
- } | {
6
- kind: "scanning";
7
- scannedBytes: number;
8
- totalBytes: number;
9
- }>;
10
- export type CliStatus = Readonly<{
11
- kind: "not_connected";
12
- }> | Readonly<{
13
- kind: "needs_repair";
14
- pendingBytes: number;
15
- configPath: string;
16
- }> | Readonly<{
17
- kind: "connected";
18
- workspaceName: string | null;
19
- running: boolean;
20
- scan: ScanProgress;
21
- pendingEvents: number;
22
- pendingBytes: number;
23
- health: CollectorHealth;
24
- healthAvailable: boolean;
25
- turnSummaryNeedsApproval: boolean;
26
- hookFailureAtMs?: number | null;
27
- tracesUrl: string;
28
- logPath: string;
29
- }>;
30
- export declare function safeWorkspaceName(value: string | null): string | null;
31
- export declare function renderHome(status: CliStatus, nowMs?: number, source?: CollectorSource): string;
32
- export declare function renderDetailedStatus(status: CliStatus, nowMs?: number, source?: CollectorSource): string;
33
- export {};
@@ -1,257 +0,0 @@
1
- import { collectorSourceLabel, } from "iteration-trace-contracts";
2
- const CODEX_APPROVAL_STEP = "Friction summary is off: in Codex, type /hooks and approve Iteration.";
3
- function sourceName(source) {
4
- return collectorSourceLabel(source);
5
- }
6
- export function safeWorkspaceName(value) {
7
- const cleaned = value
8
- ?.split("")
9
- .map((character) => {
10
- const code = character.charCodeAt(0);
11
- return code < 32 || (code >= 127 && code <= 159) ? " " : character;
12
- })
13
- .join("")
14
- .replace(/\s+/g, " ")
15
- .trim();
16
- return cleaned === undefined || cleaned === "" ? null : cleaned;
17
- }
18
- function readableBytes(bytes) {
19
- if (bytes < 1_000)
20
- return `${bytes} B`;
21
- const units = ["KB", "MB", "GB", "TB"];
22
- let amount = bytes;
23
- let index = -1;
24
- do {
25
- amount /= 1_000;
26
- index += 1;
27
- } while (amount >= 1_000 && index < units.length - 1);
28
- const precision = amount < 10 ? 1 : 0;
29
- return `${amount.toFixed(precision).replace(/\.0$/, "")} ${units[index]}`;
30
- }
31
- function relativeTime(atMs, nowMs) {
32
- const elapsedSeconds = Math.max(0, Math.floor((nowMs - atMs) / 1_000));
33
- if (elapsedSeconds < 5)
34
- return "just now";
35
- if (elapsedSeconds < 60)
36
- return `${elapsedSeconds} second${elapsedSeconds === 1 ? "" : "s"} ago`;
37
- const elapsedMinutes = Math.floor(elapsedSeconds / 60);
38
- if (elapsedMinutes < 60)
39
- return `${elapsedMinutes} minute${elapsedMinutes === 1 ? "" : "s"} ago`;
40
- const elapsedHours = Math.floor(elapsedMinutes / 60);
41
- if (elapsedHours < 24)
42
- return `${elapsedHours} hour${elapsedHours === 1 ? "" : "s"} ago`;
43
- const elapsedDays = Math.floor(elapsedHours / 24);
44
- return `${elapsedDays} day${elapsedDays === 1 ? "" : "s"} ago`;
45
- }
46
- function pendingDescription(status) {
47
- return `${status.pendingEvents} event${status.pendingEvents === 1 ? "" : "s"} (${readableBytes(status.pendingBytes)}) waiting on this computer`;
48
- }
49
- function progressDescription(scan, source) {
50
- if (scan.kind === "waiting")
51
- return "Local history is preparing to scan.";
52
- if (scan.totalBytes === 0)
53
- return `No local ${sourceName(source)} sessions found yet.`;
54
- if (scan.scannedBytes >= scan.totalBytes)
55
- return `Local history scanned (${readableBytes(scan.totalBytes)}).`;
56
- const remainingBytes = scan.totalBytes - scan.scannedBytes;
57
- if (remainingBytes < 1_000_000)
58
- return "Local scan is nearly caught up (less than 1 MB remains).";
59
- const percentage = Math.min(99, Math.floor((scan.scannedBytes / scan.totalBytes) * 100));
60
- return `About ${percentage}% of local history scanned (${readableBytes(scan.scannedBytes)} of ${readableBytes(scan.totalBytes)}).`;
61
- }
62
- function historyDescription(status, source) {
63
- if (status.health.historyCaughtUpAtMs === null) {
64
- return progressDescription(status.scan, source);
65
- }
66
- if (status.scan.kind === "waiting")
67
- return "History is preparing to scan.";
68
- const remainingBytes = status.scan.totalBytes - status.scan.scannedBytes;
69
- if (remainingBytes <= 0)
70
- return "History imported; local files are caught up.";
71
- if (remainingBytes < 1_000_000)
72
- return "History imported; scanning current activity.";
73
- return `History imported; ${readableBytes(remainingBytes)} of new activity waiting to scan.`;
74
- }
75
- function failureDescription(status, source) {
76
- const name = sourceName(source);
77
- const failure = status.health.lastFailure;
78
- if (failure === null) {
79
- throw new Error("A collector failure is required to describe it.");
80
- }
81
- if (failure.kind === "http") {
82
- const code = failure.status === null ? "HTTP error" : `HTTP ${failure.status}`;
83
- if (failure.status === 401 || failure.status === 403) {
84
- return {
85
- heading: `${name} upload access needs attention`,
86
- explanation: `Iteration denied this collector (${code}). Queued data stays on this computer. Contact Iteration support to reconnect it safely.`,
87
- };
88
- }
89
- if (failure.status !== null &&
90
- failure.status < 500 &&
91
- failure.status !== 429) {
92
- return {
93
- heading: `${name} uploads need attention`,
94
- explanation: `Iteration rejected an event batch (${code}). Check the collector log for details.`,
95
- };
96
- }
97
- return {
98
- heading: `${name} uploads are delayed`,
99
- explanation: `Iteration could not accept an upload (${code}). Retrying automatically.`,
100
- };
101
- }
102
- if (failure.kind === "network") {
103
- return {
104
- heading: `${name} uploads are delayed`,
105
- explanation: "Could not reach Iteration. Retrying automatically.",
106
- };
107
- }
108
- if (failure.kind === "response") {
109
- return {
110
- heading: `${name} uploads need attention`,
111
- explanation: "Iteration returned an unexpected upload response. Retrying automatically.",
112
- };
113
- }
114
- return {
115
- heading: `${name} collector needs attention`,
116
- explanation: `A local error interrupted collection. See iteration status ${source} for details.`,
117
- };
118
- }
119
- export function renderHome(status, nowMs = Date.now(), source = "codex") {
120
- const name = sourceName(source);
121
- if (status.kind === "not_connected") {
122
- return [
123
- "Iteration",
124
- "",
125
- `${name} is not connected.`,
126
- "Run iteration connect to import your existing sessions.",
127
- ].join("\n");
128
- }
129
- if (status.kind === "needs_repair") {
130
- return [
131
- "Iteration",
132
- "",
133
- `${name} connection needs repair`,
134
- "Local collector data exists, but its connection details are missing.",
135
- status.pendingBytes > 0
136
- ? `${readableBytes(status.pendingBytes)} of queued data remains on this computer.`
137
- : "Existing local progress remains on this computer.",
138
- `Run iteration status ${source} for recovery details.`,
139
- ].join("\n");
140
- }
141
- const workspaceName = safeWorkspaceName(status.workspaceName);
142
- const lines = [
143
- workspaceName === null ? "Iteration" : `Iteration · ${workspaceName}`,
144
- "",
145
- ];
146
- if (!status.running) {
147
- lines.push(`${name} is stopped`, "New sessions are not being collected.");
148
- if (status.pendingEvents > 0)
149
- lines.push(`${pendingDescription(status)}.`);
150
- lines.push(`Resume with iteration start ${source}.`);
151
- }
152
- else if (status.hookFailureAtMs !== null &&
153
- status.hookFailureAtMs !== undefined) {
154
- lines.push(`${name} hook capture needs attention`, `A Cursor hook failed ${relativeTime(status.hookFailureAtMs, nowMs)}. Some data may be missing. See iteration status cursor.`);
155
- }
156
- else if (!status.healthAvailable) {
157
- lines.push(`${name} is running; local status needs attention`, "Upload history could not be read. Collection continues in the background.");
158
- if (status.pendingEvents > 0)
159
- lines.push(`${pendingDescription(status)}.`);
160
- lines.push(`Details: iteration status ${source}`);
161
- }
162
- else if (status.health.lastFailure !== null) {
163
- const failure = failureDescription(status, source);
164
- lines.push(failure.heading, failure.explanation);
165
- if (status.pendingEvents > 0)
166
- lines.push(`${pendingDescription(status)}.`);
167
- if (status.health.lastUploadAtMs !== null) {
168
- lines.push(`Last successful upload ${relativeTime(status.health.lastUploadAtMs, nowMs)}.`);
169
- }
170
- lines.push(`Details: iteration status ${source}`);
171
- }
172
- else if (status.scan.kind === "waiting") {
173
- lines.push(`${name} is preparing your history`, progressDescription(status.scan, source));
174
- }
175
- else if (status.scan.kind === "scanning" &&
176
- status.scan.totalBytes > 0 &&
177
- status.scan.scannedBytes < status.scan.totalBytes &&
178
- status.health.historyCaughtUpAtMs === null) {
179
- lines.push(`${name} is importing existing sessions`, historyDescription(status, source), "New activity is collected automatically.");
180
- }
181
- else if (status.scan.kind === "scanning" &&
182
- status.scan.totalBytes - status.scan.scannedBytes >= 1_000_000) {
183
- lines.push(`${name} is catching up with new activity`, historyDescription(status, source));
184
- if (status.pendingEvents > 0)
185
- lines.push(`${pendingDescription(status)}.`);
186
- }
187
- else if (status.pendingEvents > 0) {
188
- lines.push(`${name} is uploading captured sessions`, `${pendingDescription(status)}.`);
189
- }
190
- else if (status.scan.totalBytes === 0) {
191
- lines.push(`${name} is connected`, `Waiting for ${name} sessions on this computer.`);
192
- }
193
- else if (status.scan.kind === "scanning" &&
194
- status.scan.scannedBytes < status.scan.totalBytes) {
195
- lines.push(`${name} is collecting new activity`, "Local history is imported; current activity is being scanned.");
196
- }
197
- else {
198
- lines.push(`${name} is collecting`, "Local scan is caught up and no uploads are waiting.", "New activity is collected automatically.");
199
- }
200
- if (status.running &&
201
- status.health.lastFailure === null &&
202
- status.health.lastUploadAtMs !== null) {
203
- lines.push(`Last successful upload ${relativeTime(status.health.lastUploadAtMs, nowMs)}.`);
204
- }
205
- if (status.turnSummaryNeedsApproval)
206
- lines.push("", CODEX_APPROVAL_STEP);
207
- lines.push("", `Explore traces ${status.tracesUrl}`);
208
- return lines.join("\n");
209
- }
210
- export function renderDetailedStatus(status, nowMs = Date.now(), source = "codex") {
211
- const name = sourceName(source);
212
- if (status.kind === "not_connected") {
213
- return `${name} is not connected. Run iteration connect to connect.`;
214
- }
215
- if (status.kind === "needs_repair") {
216
- return [
217
- `${name} connection needs repair`,
218
- "Local collector data exists without its connection details.",
219
- `Queued data: ${readableBytes(status.pendingBytes)}`,
220
- `Restore the missing config from backup: ${status.configPath}`,
221
- "If you do not have it, contact Iteration support before reconnecting; existing data has been kept.",
222
- ].join("\n");
223
- }
224
- const workspaceName = safeWorkspaceName(status.workspaceName);
225
- const lines = [
226
- `${name} collector`,
227
- ...(workspaceName === null ? [] : [`Workspace: ${workspaceName}`]),
228
- `Background process: ${status.running ? "Running" : "Stopped"}`,
229
- `History: ${historyDescription(status, source).replace(/^History /, "")}`,
230
- `Uploads: ${status.pendingEvents === 0 ? "None waiting" : pendingDescription(status)}`,
231
- ];
232
- if (!status.healthAvailable) {
233
- lines.push("Local status: unavailable; check collector file permissions", `Collector log: ${status.logPath}`);
234
- }
235
- if (status.health.lastUploadAtMs !== null) {
236
- lines.push(`Last successful upload: ${relativeTime(status.health.lastUploadAtMs, nowMs)}`);
237
- }
238
- if (status.health.lastFailure !== null) {
239
- const failure = status.health.lastFailure;
240
- const label = failure.kind === "http" && failure.status !== null
241
- ? `HTTP ${failure.status}`
242
- : failure.kind === "network"
243
- ? "Network unavailable"
244
- : failure.kind === "response"
245
- ? "Unexpected upload response"
246
- : "Local collector error";
247
- lines.push(`Last issue: ${label} (${relativeTime(failure.atMs, nowMs)})`, `Collector log: ${status.logPath}`);
248
- }
249
- if (status.hookFailureAtMs !== null && status.hookFailureAtMs !== undefined)
250
- lines.push(`Last Cursor hook capture failure: ${relativeTime(status.hookFailureAtMs, nowMs)}. Some data may be missing; inspect Cursor hook logs.`);
251
- if (status.turnSummaryNeedsApproval)
252
- lines.push(CODEX_APPROVAL_STEP);
253
- if (!status.running)
254
- lines.push(`Resume: iteration start ${source}`);
255
- lines.push(`Explore traces: ${status.tracesUrl}`);
256
- return lines.join("\n");
257
- }
package/dist/storage.d.ts DELETED
@@ -1,94 +0,0 @@
1
- import { type CollectorSource, type TraceEvent } from "iteration-trace-contracts";
2
- type CollectorConnection = Readonly<{
3
- endpoint: string;
4
- token: string;
5
- dataDirectory: string;
6
- organizationId?: string;
7
- organizationName?: string;
8
- }>;
9
- export type CollectorConfig = (CollectorConnection & Readonly<{
10
- codexHome: string;
11
- }>) | (CollectorConnection & Readonly<{
12
- source: "claude";
13
- claudeHome: string;
14
- }>) | (CollectorConnection & Readonly<{
15
- source: "cursor";
16
- cursorHome: string;
17
- }>);
18
- export declare function collectorSource(config: CollectorConfig): CollectorSource;
19
- export declare function collectorSourceDirectory(config: CollectorConfig): string;
20
- export type CollectorState = Readonly<{
21
- cursors: Readonly<Record<string, number>>;
22
- skipping: readonly string[];
23
- sourceDirectory: string | null;
24
- initialized: boolean;
25
- }>;
26
- export type CollectorFailure = Readonly<{
27
- atMs: number;
28
- kind: "http" | "network" | "response" | "collector";
29
- status: number | null;
30
- }>;
31
- export type CollectorHealth = Readonly<{
32
- lastUploadAtMs: number | null;
33
- historyCaughtUpAtMs: number | null;
34
- lastFailure: CollectorFailure | null;
35
- }>;
36
- export type HealthSnapshot = Readonly<{
37
- kind: "available";
38
- health: CollectorHealth;
39
- } | {
40
- kind: "unavailable";
41
- health: CollectorHealth;
42
- }>;
43
- export type CollectorPaths = Readonly<{
44
- config: string;
45
- state: string;
46
- outbox: string;
47
- redactionMarker: string;
48
- lock: string;
49
- historyBackfill: string;
50
- health: string;
51
- }>;
52
- export declare function defaultCollectorDirectory(source: CollectorSource): string;
53
- export declare function validateCollectorEndpoint(endpoint: string): URL;
54
- export declare function collectorPaths(directory?: string): CollectorPaths;
55
- export declare function assertFreshCollectorDirectory(paths: CollectorPaths, source?: CollectorSource): Promise<void>;
56
- export declare function ensurePrivateDirectory(directory: string): Promise<void>;
57
- export declare function saveConfig(config: CollectorConfig, paths: CollectorPaths): Promise<void>;
58
- export declare function loadHealth(paths: CollectorPaths): Promise<CollectorHealth>;
59
- export declare function healthSnapshot(paths: CollectorPaths): Promise<HealthSnapshot>;
60
- export declare function saveHealth(health: CollectorHealth, paths: CollectorPaths): Promise<void>;
61
- export declare function loadConfig(paths: CollectorPaths): Promise<CollectorConfig>;
62
- export declare function loadState(paths: CollectorPaths): Promise<CollectorState>;
63
- export declare function saveState(state: CollectorState, paths: CollectorPaths): Promise<void>;
64
- export declare function outboxSize(paths: CollectorPaths): Promise<number>;
65
- export declare function pendingEventIds(paths: CollectorPaths): Promise<Set<string>>;
66
- export declare function appendEvents(events: readonly TraceEvent[], paths: CollectorPaths): Promise<void>;
67
- export declare function redactPendingOutbox(paths: CollectorPaths): Promise<void>;
68
- export declare function nextBatch(paths: CollectorPaths, maxBytes: number): Promise<readonly TraceEvent[]>;
69
- export declare function acknowledgeEvents(acknowledgedIds: ReadonlySet<string>, paths: CollectorPaths): Promise<void>;
70
- export declare function initializeState(source: CollectorSource, home: string, paths: CollectorPaths, includeHistory: boolean): Promise<void>;
71
- export declare function prepareSourceInitialState(source: CollectorSource, home: string, includeHistory: boolean): Promise<CollectorState>;
72
- export declare function ensureStartableState(config: CollectorConfig, paths: CollectorPaths): Promise<boolean>;
73
- export declare function ensureHistoryBackfill(codexHome: string, paths: CollectorPaths): Promise<boolean>;
74
- export declare function ensureClaudeHistoryBackfill(claudeHome: string, paths: CollectorPaths): Promise<boolean>;
75
- export declare function ensureCursorHistoryBackfill(cursorHome: string, paths: CollectorPaths): Promise<boolean>;
76
- export declare function historyScanProgress(home: string, paths: CollectorPaths, source?: CollectorSource): Promise<Readonly<{
77
- kind: "waiting";
78
- } | {
79
- kind: "scanning";
80
- scannedBytes: number;
81
- totalBytes: number;
82
- }>>;
83
- export declare function resetHistoryCursors(home: string, paths: CollectorPaths, source?: CollectorSource): Promise<Readonly<{
84
- files: number;
85
- bytes: number;
86
- }>>;
87
- export declare function historyImportSize(home: string, source?: CollectorSource): Promise<Readonly<{
88
- files: number;
89
- bytes: number;
90
- }>>;
91
- export declare function claudeFiles(root: string): Promise<string[]>;
92
- export declare function sourceFiles(source: CollectorSource, home: string): Promise<string[]>;
93
- export declare function cursorFiles(root: string): Promise<string[]>;
94
- export {};