iteration-cli 0.4.3 → 0.5.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.
- package/README.md +7 -2
- package/dist/claude-transcript.d.ts +6 -0
- package/dist/claude-transcript.js +255 -0
- package/dist/clerk-device-auth.d.ts +2 -1
- package/dist/clerk-device-auth.js +6 -4
- package/dist/cli.js +135 -77
- package/dist/collector.d.ts +11 -2
- package/dist/collector.js +86 -38
- package/dist/history-import.d.ts +1 -1
- package/dist/history-import.js +18 -15
- package/dist/launch-agent.d.ts +5 -4
- package/dist/launch-agent.js +26 -23
- package/dist/rollout.js +122 -135
- package/dist/service.d.ts +2 -1
- package/dist/service.js +64 -7
- package/dist/status-view.d.ts +30 -0
- package/dist/status-view.js +246 -0
- package/dist/storage.d.ts +43 -10
- package/dist/storage.js +215 -51
- package/package.json +2 -2
package/dist/storage.js
CHANGED
|
@@ -1,19 +1,33 @@
|
|
|
1
|
+
import { once } from "node:events";
|
|
1
2
|
import { createWriteStream, promises as fs } from "node:fs";
|
|
2
3
|
import os from "node:os";
|
|
3
4
|
import path from "node:path";
|
|
4
|
-
import { once } from "node:events";
|
|
5
5
|
import { createInterface } from "node:readline";
|
|
6
|
-
import * as z from "zod";
|
|
7
6
|
import { MAX_TRACE_EVENTS_PER_BATCH, parseTraceEventBatch, } from "iteration-trace-contracts";
|
|
7
|
+
import * as z from "zod";
|
|
8
8
|
const MAX_OUTBOX_BYTES = 64 * 1024 * 1024;
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
export function collectorSource(config) {
|
|
10
|
+
return "claudeHome" in config ? "claude" : "codex";
|
|
11
|
+
}
|
|
12
|
+
export function collectorSourceDirectory(config) {
|
|
13
|
+
return "claudeHome" in config ? config.claudeHome : config.codexHome;
|
|
14
|
+
}
|
|
15
|
+
const connectionSchema = {
|
|
11
16
|
endpoint: z.string(),
|
|
12
17
|
token: z.string(),
|
|
13
|
-
codexHome: z.string(),
|
|
14
18
|
dataDirectory: z.string(),
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
organizationName: z.string().min(1).max(160).optional(),
|
|
20
|
+
};
|
|
21
|
+
const configSchema = z.union([
|
|
22
|
+
z.object({ ...connectionSchema, codexHome: z.string() }).strict(),
|
|
23
|
+
z
|
|
24
|
+
.object({
|
|
25
|
+
...connectionSchema,
|
|
26
|
+
source: z.literal("claude"),
|
|
27
|
+
claudeHome: z.string(),
|
|
28
|
+
})
|
|
29
|
+
.strict(),
|
|
30
|
+
]);
|
|
17
31
|
const stateSchema = z
|
|
18
32
|
.object({
|
|
19
33
|
cursors: z.record(z.string(), z.number().int().min(0).max(Number.MAX_SAFE_INTEGER)),
|
|
@@ -22,6 +36,20 @@ const stateSchema = z
|
|
|
22
36
|
initialized: z.boolean(),
|
|
23
37
|
})
|
|
24
38
|
.strict();
|
|
39
|
+
const healthSchema = z
|
|
40
|
+
.object({
|
|
41
|
+
lastUploadAtMs: z.number().int().min(0).nullable(),
|
|
42
|
+
historyCaughtUpAtMs: z.number().int().min(0).nullable(),
|
|
43
|
+
lastFailure: z
|
|
44
|
+
.object({
|
|
45
|
+
atMs: z.number().int().min(0),
|
|
46
|
+
kind: z.enum(["http", "network", "response", "collector"]),
|
|
47
|
+
status: z.number().int().min(100).max(599).nullable(),
|
|
48
|
+
})
|
|
49
|
+
.strict()
|
|
50
|
+
.nullable(),
|
|
51
|
+
})
|
|
52
|
+
.strict();
|
|
25
53
|
function parseOutboxEvent(line) {
|
|
26
54
|
let decoded;
|
|
27
55
|
try {
|
|
@@ -40,8 +68,8 @@ function parseOutboxEvent(line) {
|
|
|
40
68
|
throw new Error("Trace outbox event is missing.");
|
|
41
69
|
return event;
|
|
42
70
|
}
|
|
43
|
-
function
|
|
44
|
-
return path.join(os.homedir(), ".config", "iteration",
|
|
71
|
+
export function defaultCollectorDirectory(source) {
|
|
72
|
+
return path.join(os.homedir(), ".config", "iteration", `${source}-collector`);
|
|
45
73
|
}
|
|
46
74
|
export function validateCollectorEndpoint(endpoint) {
|
|
47
75
|
let parsed;
|
|
@@ -67,16 +95,17 @@ export function validateCollectorEndpoint(endpoint) {
|
|
|
67
95
|
}
|
|
68
96
|
return parsed;
|
|
69
97
|
}
|
|
70
|
-
export function collectorPaths(directory =
|
|
98
|
+
export function collectorPaths(directory = defaultCollectorDirectory("codex")) {
|
|
71
99
|
return {
|
|
72
100
|
config: path.join(directory, "config.json"),
|
|
73
101
|
state: path.join(directory, "state.json"),
|
|
74
102
|
outbox: path.join(directory, "outbox.jsonl"),
|
|
75
103
|
lock: path.join(directory, "collector.lock"),
|
|
76
104
|
historyBackfill: path.join(directory, "history-backfill.json"),
|
|
105
|
+
health: path.join(directory, "health.json"),
|
|
77
106
|
};
|
|
78
107
|
}
|
|
79
|
-
export async function assertFreshCollectorDirectory(paths) {
|
|
108
|
+
export async function assertFreshCollectorDirectory(paths, source = "codex") {
|
|
80
109
|
const state = await loadState(paths);
|
|
81
110
|
const configExists = await fs
|
|
82
111
|
.access(paths.config)
|
|
@@ -87,7 +116,7 @@ export async function assertFreshCollectorDirectory(paths) {
|
|
|
87
116
|
throw error;
|
|
88
117
|
});
|
|
89
118
|
if (configExists) {
|
|
90
|
-
throw new Error(
|
|
119
|
+
throw new Error(`This collector data directory is already configured. Run iteration status ${source} or iteration start ${source} to repair startup. Use a different --data-directory for another workspace, or iteration import-history ${source} for earlier sessions.`);
|
|
91
120
|
}
|
|
92
121
|
if (state.initialized || (await outboxSize(paths)) > 0) {
|
|
93
122
|
throw new Error("Collector state or pending events exist without a config. Use a different --data-directory; this data cannot be safely assigned to a new workspace.");
|
|
@@ -113,6 +142,57 @@ export async function saveConfig(config, paths) {
|
|
|
113
142
|
await ensurePrivateDirectory(path.dirname(paths.config));
|
|
114
143
|
await writeJsonAtomic(paths.config, config);
|
|
115
144
|
}
|
|
145
|
+
export async function loadHealth(paths) {
|
|
146
|
+
let contents;
|
|
147
|
+
try {
|
|
148
|
+
contents = await fs.readFile(paths.health, "utf8");
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
152
|
+
return {
|
|
153
|
+
lastUploadAtMs: null,
|
|
154
|
+
historyCaughtUpAtMs: null,
|
|
155
|
+
lastFailure: null,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
throw new Error(`Could not read collector health at ${paths.health}.`, {
|
|
159
|
+
cause: error,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
let decoded;
|
|
163
|
+
try {
|
|
164
|
+
decoded = JSON.parse(contents);
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
throw new Error("Collector health is not valid JSON.", { cause: error });
|
|
168
|
+
}
|
|
169
|
+
const parsed = healthSchema.safeParse(decoded);
|
|
170
|
+
if (!parsed.success) {
|
|
171
|
+
throw new Error("Collector health has an invalid shape.", {
|
|
172
|
+
cause: parsed.error,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
return parsed.data;
|
|
176
|
+
}
|
|
177
|
+
export async function healthSnapshot(paths) {
|
|
178
|
+
try {
|
|
179
|
+
return { kind: "available", health: await loadHealth(paths) };
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return {
|
|
183
|
+
kind: "unavailable",
|
|
184
|
+
health: {
|
|
185
|
+
lastUploadAtMs: null,
|
|
186
|
+
historyCaughtUpAtMs: null,
|
|
187
|
+
lastFailure: null,
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
export async function saveHealth(health, paths) {
|
|
193
|
+
await ensurePrivateDirectory(path.dirname(paths.health));
|
|
194
|
+
await writeJsonAtomic(paths.health, health);
|
|
195
|
+
}
|
|
116
196
|
export async function loadConfig(paths) {
|
|
117
197
|
let contents;
|
|
118
198
|
try {
|
|
@@ -135,12 +215,12 @@ export async function loadConfig(paths) {
|
|
|
135
215
|
throw new Error("Collector config has an invalid shape.", {
|
|
136
216
|
cause: parsed.error,
|
|
137
217
|
});
|
|
138
|
-
|
|
139
|
-
|
|
218
|
+
validateCollectorEndpoint(parsed.data.endpoint);
|
|
219
|
+
const { token } = parsed.data;
|
|
140
220
|
if (token.length !== 43 || !/^[A-Za-z0-9_-]+$/.test(token)) {
|
|
141
221
|
throw new Error("Collector token has an invalid format.");
|
|
142
222
|
}
|
|
143
|
-
return
|
|
223
|
+
return parsed.data;
|
|
144
224
|
}
|
|
145
225
|
export async function loadState(paths) {
|
|
146
226
|
let contents;
|
|
@@ -305,24 +385,23 @@ export async function acknowledgeEvents(acknowledgedIds, paths) {
|
|
|
305
385
|
throw error;
|
|
306
386
|
}
|
|
307
387
|
}
|
|
308
|
-
export async function initializeState(
|
|
388
|
+
export async function initializeState(source, home, paths, includeHistory) {
|
|
309
389
|
const state = await loadState(paths);
|
|
310
|
-
const sourceDirectory = path.resolve(
|
|
390
|
+
const sourceDirectory = path.resolve(home);
|
|
311
391
|
if (state.initialized && state.sourceDirectory !== sourceDirectory) {
|
|
312
|
-
throw new Error(
|
|
392
|
+
throw new Error(`Collector state belongs to another ${source} directory; use a separate --data-directory.`);
|
|
313
393
|
}
|
|
314
394
|
if (state.initialized && state.sourceDirectory === sourceDirectory) {
|
|
315
395
|
if (includeHistory) {
|
|
316
|
-
throw new Error(
|
|
396
|
+
throw new Error(`${source} is already connected. Run iteration import-history ${source} to import existing sessions.`);
|
|
317
397
|
}
|
|
318
398
|
return;
|
|
319
399
|
}
|
|
320
|
-
await saveState(await
|
|
400
|
+
await saveState(await prepareSourceInitialState(source, home, includeHistory), paths);
|
|
321
401
|
}
|
|
322
|
-
export async function
|
|
323
|
-
const sourceDirectory = path.resolve(
|
|
324
|
-
const
|
|
325
|
-
const files = await rolloutFiles(sessionsRoot);
|
|
402
|
+
export async function prepareSourceInitialState(source, home, includeHistory) {
|
|
403
|
+
const sourceDirectory = path.resolve(home);
|
|
404
|
+
const files = await sourceFiles(source, sourceDirectory);
|
|
326
405
|
const cursors = {};
|
|
327
406
|
if (!includeHistory) {
|
|
328
407
|
for (const filename of files) {
|
|
@@ -337,20 +416,26 @@ export async function prepareInitialState(codexHome, includeHistory) {
|
|
|
337
416
|
};
|
|
338
417
|
}
|
|
339
418
|
export async function ensureStartableState(config, paths) {
|
|
419
|
+
const source = collectorSource(config);
|
|
420
|
+
const directory = collectorSourceDirectory(config);
|
|
340
421
|
const state = await loadState(paths);
|
|
341
422
|
if (state.initialized) {
|
|
342
|
-
if (state.sourceDirectory !== path.resolve(
|
|
343
|
-
throw new Error(
|
|
423
|
+
if (state.sourceDirectory !== path.resolve(directory)) {
|
|
424
|
+
throw new Error(`Collector state belongs to another ${source} directory; use a separate --data-directory.`);
|
|
344
425
|
}
|
|
345
426
|
return false;
|
|
346
427
|
}
|
|
347
428
|
if ((await outboxSize(paths)) > 0) {
|
|
348
429
|
throw new Error("Pending events exist without an initialized checkpoint; repair the collector state before starting uploads.");
|
|
349
430
|
}
|
|
350
|
-
await
|
|
431
|
+
await saveState(await prepareSourceInitialState(source, directory, true), paths);
|
|
351
432
|
return true;
|
|
352
433
|
}
|
|
353
|
-
|
|
434
|
+
const HISTORY_BACKFILL_VERSION = 4;
|
|
435
|
+
const historyBackfillMarkerSchema = z
|
|
436
|
+
.object({ version: z.number().int().min(1).max(HISTORY_BACKFILL_VERSION) })
|
|
437
|
+
.strict();
|
|
438
|
+
async function readHistoryBackfillVersion(paths) {
|
|
354
439
|
let marker;
|
|
355
440
|
try {
|
|
356
441
|
marker = await fs.readFile(paths.historyBackfill, "utf8");
|
|
@@ -358,18 +443,28 @@ async function hasHistoryBackfillMarker(paths) {
|
|
|
358
443
|
catch (error) {
|
|
359
444
|
if (!(error instanceof Error && "code" in error && error.code === "ENOENT"))
|
|
360
445
|
throw error;
|
|
361
|
-
|
|
446
|
+
return null;
|
|
362
447
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
448
|
+
let decoded;
|
|
449
|
+
try {
|
|
450
|
+
decoded = JSON.parse(marker);
|
|
451
|
+
}
|
|
452
|
+
catch (error) {
|
|
453
|
+
throw new Error("Collector history backfill marker is not valid JSON.", {
|
|
454
|
+
cause: error,
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
const parsed = historyBackfillMarkerSchema.safeParse(decoded);
|
|
458
|
+
if (!parsed.success) {
|
|
459
|
+
throw new Error("Collector history backfill marker has an invalid shape.", {
|
|
460
|
+
cause: parsed.error,
|
|
461
|
+
});
|
|
368
462
|
}
|
|
369
|
-
return
|
|
463
|
+
return parsed.data.version;
|
|
370
464
|
}
|
|
371
465
|
export async function ensureHistoryBackfill(codexHome, paths) {
|
|
372
|
-
|
|
466
|
+
const version = await readHistoryBackfillVersion(paths);
|
|
467
|
+
if (version !== null && version >= HISTORY_BACKFILL_VERSION)
|
|
373
468
|
return false;
|
|
374
469
|
const state = await loadState(paths);
|
|
375
470
|
if (!state.initialized || state.sourceDirectory !== path.resolve(codexHome)) {
|
|
@@ -377,17 +472,33 @@ export async function ensureHistoryBackfill(codexHome, paths) {
|
|
|
377
472
|
}
|
|
378
473
|
await rolloutFiles(path.join(codexHome, "sessions"));
|
|
379
474
|
await saveState({ ...state, cursors: {}, skipping: [] }, paths);
|
|
475
|
+
await writeJsonAtomic(paths.historyBackfill, {
|
|
476
|
+
version: HISTORY_BACKFILL_VERSION,
|
|
477
|
+
});
|
|
478
|
+
return true;
|
|
479
|
+
}
|
|
480
|
+
export async function ensureClaudeHistoryBackfill(claudeHome, paths) {
|
|
481
|
+
const version = await readHistoryBackfillVersion(paths);
|
|
482
|
+
if (version !== null && version >= 1)
|
|
483
|
+
return false;
|
|
484
|
+
const state = await loadState(paths);
|
|
485
|
+
if (!state.initialized ||
|
|
486
|
+
state.sourceDirectory !== path.resolve(claudeHome)) {
|
|
487
|
+
throw new Error("Collector state does not match the Claude directory; reconnect Claude before uploading.");
|
|
488
|
+
}
|
|
489
|
+
await claudeFiles(path.join(claudeHome, "projects"));
|
|
490
|
+
await saveState({ ...state, cursors: {}, skipping: [] }, paths);
|
|
380
491
|
await writeJsonAtomic(paths.historyBackfill, { version: 1 });
|
|
381
492
|
return true;
|
|
382
493
|
}
|
|
383
|
-
export async function historyScanProgress(
|
|
384
|
-
if (
|
|
494
|
+
export async function historyScanProgress(home, paths, source = "codex") {
|
|
495
|
+
if ((await readHistoryBackfillVersion(paths)) === null)
|
|
385
496
|
return { kind: "waiting" };
|
|
386
497
|
const state = await loadState(paths);
|
|
387
|
-
if (!state.initialized || state.sourceDirectory !== path.resolve(
|
|
388
|
-
throw new Error(
|
|
498
|
+
if (!state.initialized || state.sourceDirectory !== path.resolve(home)) {
|
|
499
|
+
throw new Error(`Collector state does not match the ${source} directory.`);
|
|
389
500
|
}
|
|
390
|
-
const files = await
|
|
501
|
+
const files = await sourceFiles(source, home);
|
|
391
502
|
let scannedBytes = 0;
|
|
392
503
|
let totalBytes = 0;
|
|
393
504
|
for (let start = 0; start < files.length; start += 64) {
|
|
@@ -415,28 +526,33 @@ export async function historyScanProgress(codexHome, paths) {
|
|
|
415
526
|
scannedBytes += cursor <= size ? cursor : 0;
|
|
416
527
|
if (!Number.isSafeInteger(totalBytes) ||
|
|
417
528
|
!Number.isSafeInteger(scannedBytes)) {
|
|
418
|
-
throw new Error(
|
|
529
|
+
throw new Error(`${source} history size exceeds the supported limit.`);
|
|
419
530
|
}
|
|
420
531
|
}
|
|
421
532
|
}
|
|
422
533
|
return { kind: "scanning", scannedBytes, totalBytes };
|
|
423
534
|
}
|
|
424
|
-
export async function resetHistoryCursors(
|
|
535
|
+
export async function resetHistoryCursors(home, paths, source = "codex") {
|
|
425
536
|
const state = await loadState(paths);
|
|
426
|
-
const sourceDirectory = path.resolve(
|
|
537
|
+
const sourceDirectory = path.resolve(home);
|
|
427
538
|
if (!state.initialized || state.sourceDirectory !== sourceDirectory) {
|
|
428
|
-
throw new Error(
|
|
539
|
+
throw new Error(`Connect the ${source} source before importing its history.`);
|
|
429
540
|
}
|
|
430
|
-
const files = await
|
|
541
|
+
const files = await sourceFiles(source, sourceDirectory);
|
|
431
542
|
const cursors = {};
|
|
432
543
|
let bytes = 0;
|
|
433
544
|
for (const filename of files) {
|
|
434
545
|
cursors[filename] = 0;
|
|
435
546
|
bytes += (await fs.stat(filename)).size;
|
|
436
547
|
if (!Number.isSafeInteger(bytes)) {
|
|
437
|
-
throw new Error(
|
|
548
|
+
throw new Error(`${source} history size exceeds the supported limit.`);
|
|
438
549
|
}
|
|
439
550
|
}
|
|
551
|
+
const localHealth = await healthSnapshot(paths);
|
|
552
|
+
if (localHealth.kind === "unavailable" ||
|
|
553
|
+
localHealth.health.historyCaughtUpAtMs !== null) {
|
|
554
|
+
await saveHealth({ ...localHealth.health, historyCaughtUpAtMs: null }, paths);
|
|
555
|
+
}
|
|
440
556
|
await saveState({
|
|
441
557
|
cursors,
|
|
442
558
|
skipping: [],
|
|
@@ -445,13 +561,13 @@ export async function resetHistoryCursors(codexHome, paths) {
|
|
|
445
561
|
}, paths);
|
|
446
562
|
return { files: files.length, bytes };
|
|
447
563
|
}
|
|
448
|
-
export async function historyImportSize(
|
|
449
|
-
const files = await
|
|
564
|
+
export async function historyImportSize(home, source = "codex") {
|
|
565
|
+
const files = await sourceFiles(source, home);
|
|
450
566
|
let bytes = 0;
|
|
451
567
|
for (const filename of files) {
|
|
452
568
|
bytes += (await fs.stat(filename)).size;
|
|
453
569
|
if (!Number.isSafeInteger(bytes)) {
|
|
454
|
-
throw new Error(
|
|
570
|
+
throw new Error(`${source} history size exceeds the supported limit.`);
|
|
455
571
|
}
|
|
456
572
|
}
|
|
457
573
|
return { files: files.length, bytes };
|
|
@@ -485,7 +601,7 @@ async function lastCompleteRecordOffset(filename, size) {
|
|
|
485
601
|
await handle.close();
|
|
486
602
|
}
|
|
487
603
|
}
|
|
488
|
-
|
|
604
|
+
async function rolloutFiles(root) {
|
|
489
605
|
const files = [];
|
|
490
606
|
const pending = [root];
|
|
491
607
|
while (pending.length > 0) {
|
|
@@ -515,3 +631,51 @@ export async function rolloutFiles(root) {
|
|
|
515
631
|
}
|
|
516
632
|
return files.sort();
|
|
517
633
|
}
|
|
634
|
+
async function directoryEntries(directory) {
|
|
635
|
+
try {
|
|
636
|
+
return await fs.readdir(directory, { withFileTypes: true });
|
|
637
|
+
}
|
|
638
|
+
catch (error) {
|
|
639
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
640
|
+
return [];
|
|
641
|
+
throw new Error(`Could not scan Claude session directory ${directory}.`, {
|
|
642
|
+
cause: error,
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
export async function claudeFiles(root) {
|
|
647
|
+
const files = [];
|
|
648
|
+
for (const project of await directoryEntries(root)) {
|
|
649
|
+
if (!project.isDirectory())
|
|
650
|
+
continue;
|
|
651
|
+
const projectDirectory = path.join(root, project.name);
|
|
652
|
+
for (const entry of await directoryEntries(projectDirectory)) {
|
|
653
|
+
const filename = path.join(projectDirectory, entry.name);
|
|
654
|
+
if (entry.isFile() &&
|
|
655
|
+
/^[^/]+\.jsonl$/.test(entry.name) &&
|
|
656
|
+
!entry.name.includes(".orphaned-") &&
|
|
657
|
+
!entry.name.includes(".superseded-")) {
|
|
658
|
+
files.push(filename);
|
|
659
|
+
}
|
|
660
|
+
else if (entry.isDirectory()) {
|
|
661
|
+
const subagents = path.join(filename, "subagents");
|
|
662
|
+
for (const agent of await directoryEntries(subagents)) {
|
|
663
|
+
if (agent.isFile() && /^agent-.+\.jsonl$/.test(agent.name))
|
|
664
|
+
files.push(path.join(subagents, agent.name));
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
if (files.length > 10_000)
|
|
668
|
+
throw new Error("Claude session file count exceeds the scan limit.");
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return files.sort();
|
|
672
|
+
}
|
|
673
|
+
export function sourceFiles(source, home) {
|
|
674
|
+
const directory = path.resolve(home);
|
|
675
|
+
if (source === "codex")
|
|
676
|
+
return rolloutFiles(path.join(directory, "sessions"));
|
|
677
|
+
if (source === "claude")
|
|
678
|
+
return claudeFiles(path.join(directory, "projects"));
|
|
679
|
+
const unhandled = source;
|
|
680
|
+
throw new Error(`Unsupported collector source: ${unhandled}`);
|
|
681
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iteration-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"zod": "4.6.4",
|
|
21
|
-
"iteration-trace-contracts": "0.
|
|
21
|
+
"iteration-trace-contracts": "0.5.0"
|
|
22
22
|
},
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "pnpm --filter iteration-trace-contracts build && tsc -p tsconfig.json"
|