iteration-cli 0.17.0 → 0.18.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 (3) hide show
  1. package/README.md +5 -4
  2. package/dist/cli.js +611 -343
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -7,9 +7,9 @@ var __export = (target, all) => {
7
7
 
8
8
  // src/cli.ts
9
9
  import { spawnSync } from "node:child_process";
10
- import { promises as fs24 } from "node:fs";
11
- import os11 from "node:os";
12
- import path30 from "node:path";
10
+ import { promises as fs25 } from "node:fs";
11
+ import os12 from "node:os";
12
+ import path31 from "node:path";
13
13
  import { parseArgs } from "node:util";
14
14
 
15
15
  // ../trace-vault/packages/cli/dist/index.js
@@ -7973,16 +7973,16 @@ function consoleWriter(line, level) {
7973
7973
  }
7974
7974
  var loggingContext = () => ({});
7975
7975
  var logWriter = consoleWriter;
7976
- function configureCollectorLogging(context2, write = consoleWriter) {
7976
+ function configureCollectorLogging(context2, write2 = consoleWriter) {
7977
7977
  loggingContext = context2;
7978
- logWriter = write;
7978
+ logWriter = write2;
7979
7979
  }
7980
7980
  function writeLog(line, level) {
7981
7981
  logWriter(line, level);
7982
7982
  }
7983
- function createLogger(context2, write = writeLog) {
7983
+ function createLogger(context2, write2 = writeLog) {
7984
7984
  return (entry) => {
7985
- write(
7985
+ write2(
7986
7986
  JSON.stringify({
7987
7987
  ...entry,
7988
7988
  ...context2,
@@ -15420,19 +15420,29 @@ async function scanSourceOnce(config22, paths, signal, policy, accounts) {
15420
15420
  },
15421
15421
  paths
15422
15422
  );
15423
- const sessions = sessionFiles.filter((file) => file.session);
15424
15423
  return {
15425
15424
  discovered: events.length,
15426
15425
  excludedRecords,
15427
15426
  caughtUp,
15428
- history: {
15429
- sessions: sessions.length,
15430
- readSessions: sessions.filter(
15431
- (file) => (cursors[file.filename] ?? 0) >= file.size
15432
- ).length
15433
- }
15427
+ history: historyProgress(sessionFiles, cursors)
15428
+ };
15429
+ }
15430
+ function historyProgress(files, cursors) {
15431
+ const sessions = files.filter((file) => file.session);
15432
+ return {
15433
+ sessions: sessions.length,
15434
+ readSessions: sessions.filter(
15435
+ (file) => (cursors[file.filename] ?? 0) >= file.size
15436
+ ).length
15434
15437
  };
15435
15438
  }
15439
+ async function savedHistoryProgress(config22, paths) {
15440
+ const files = await sourceFilesNewestFirst(
15441
+ collectorSource(config22),
15442
+ path14.resolve(collectorSourceDirectory(config22))
15443
+ );
15444
+ return historyProgress(files, (await loadState(paths)).cursors);
15445
+ }
15436
15446
  async function readRange(filename, start2, length) {
15437
15447
  const handle = await fs9.open(filename, "r");
15438
15448
  try {
@@ -15694,7 +15704,12 @@ async function collectorTick(config22, paths, fetcher = collectorFetch, signal,
15694
15704
  policy,
15695
15705
  first
15696
15706
  );
15697
- const scan = await outboxSize(paths) >= SCAN_PAUSE_OUTBOX_BYTES ? { discovered: 0, excludedRecords: 0, caughtUp: false, history: null } : await scanSourceOnce(config22, paths, signal, policy, accounts);
15707
+ const scan = await outboxSize(paths) >= SCAN_PAUSE_OUTBOX_BYTES ? {
15708
+ discovered: 0,
15709
+ excludedRecords: 0,
15710
+ caughtUp: false,
15711
+ history: await savedHistoryProgress(config22, paths)
15712
+ } : await scanSourceOnce(config22, paths, signal, policy, accounts);
15698
15713
  if (policy !== void 0 && scan.caughtUp) {
15699
15714
  const state = await loadState(paths);
15700
15715
  if (state.captureRecheckAt === void 0)
@@ -16064,10 +16079,10 @@ function mergeDefs2(...defs) {
16064
16079
  function cloneDef2(schema) {
16065
16080
  return mergeDefs2(schema._zod.def);
16066
16081
  }
16067
- function getElementAtPath2(obj, path31) {
16068
- if (!path31)
16082
+ function getElementAtPath2(obj, path32) {
16083
+ if (!path32)
16069
16084
  return obj;
16070
- return path31.reduce((acc, key) => acc?.[key], obj);
16085
+ return path32.reduce((acc, key) => acc?.[key], obj);
16071
16086
  }
16072
16087
  function promiseAllObject2(promisesObj) {
16073
16088
  const keys = Object.keys(promisesObj);
@@ -16407,11 +16422,11 @@ function explicitlyAborted2(x, startIndex = 0) {
16407
16422
  }
16408
16423
  return false;
16409
16424
  }
16410
- function prefixIssues2(path31, issues) {
16425
+ function prefixIssues2(path32, issues) {
16411
16426
  return issues.map((iss) => {
16412
16427
  var _a5;
16413
16428
  (_a5 = iss).path ?? (_a5.path = []);
16414
- iss.path.unshift(path31);
16429
+ iss.path.unshift(path32);
16415
16430
  return iss;
16416
16431
  });
16417
16432
  }
@@ -16869,16 +16884,16 @@ function flattenError2(error3, mapper = (issue3) => issue3.message) {
16869
16884
  }
16870
16885
  function formatError2(error3, mapper = (issue3) => issue3.message) {
16871
16886
  const fieldErrors = { _errors: [] };
16872
- const processError = (error4, path31 = []) => {
16887
+ const processError = (error4, path32 = []) => {
16873
16888
  for (const issue3 of error4.issues) {
16874
16889
  if (issue3.code === "invalid_union" && issue3.errors.length) {
16875
- issue3.errors.map((issues) => processError({ issues }, [...path31, ...issue3.path]));
16890
+ issue3.errors.map((issues) => processError({ issues }, [...path32, ...issue3.path]));
16876
16891
  } else if (issue3.code === "invalid_key") {
16877
- processError({ issues: issue3.issues }, [...path31, ...issue3.path]);
16892
+ processError({ issues: issue3.issues }, [...path32, ...issue3.path]);
16878
16893
  } else if (issue3.code === "invalid_element") {
16879
- processError({ issues: issue3.issues }, [...path31, ...issue3.path]);
16894
+ processError({ issues: issue3.issues }, [...path32, ...issue3.path]);
16880
16895
  } else {
16881
- const fullpath = [...path31, ...issue3.path];
16896
+ const fullpath = [...path32, ...issue3.path];
16882
16897
  if (fullpath.length === 0) {
16883
16898
  fieldErrors._errors.push(mapper(issue3));
16884
16899
  } else {
@@ -22535,131 +22550,11 @@ function isLaunchDirectory(value) {
22535
22550
  return true;
22536
22551
  }
22537
22552
 
22538
- // src/auto-connect.ts
22539
- import { createHmac } from "node:crypto";
22540
- import { promises as fs19 } from "node:fs";
22541
- import os8 from "node:os";
22542
- import path24 from "node:path";
22543
-
22544
- // ../collector-contracts/dist/index.js
22545
- var cursorBillingRequestSchema = object3({
22546
- conversationId: uuid5(),
22547
- reconcileFromMs: number4().int().nonnegative().nullable(),
22548
- events: array2(object3({
22549
- timestamp: number4().int().nonnegative(),
22550
- kind: _enum2([
22551
- "included",
22552
- "paid",
22553
- "byok",
22554
- "not_charged",
22555
- "unknown"
22556
- ]),
22557
- inputTokens: number4().int().nonnegative().nullable(),
22558
- outputTokens: number4().int().nonnegative().nullable(),
22559
- cacheReadTokens: number4().int().nonnegative().nullable(),
22560
- cacheWriteTokens: number4().int().nonnegative().nullable(),
22561
- paidUsd: number4().finite().nonnegative().nullable()
22562
- }).strict()).max(5e3)
22563
- }).strict();
22564
- function codexCreditSign(value) {
22565
- const mantissa = value.split(/[eE]/, 1)[0];
22566
- if (!/[1-9]/.test(mantissa))
22567
- return 0;
22568
- return mantissa.startsWith("-") ? -1 : 1;
22569
- }
22570
- var codexBillingRequestSchema = object3({
22571
- accountFingerprint: string5().regex(/^[0-9a-f]{64}$/),
22572
- reconcile: boolean4(),
22573
- tasks: array2(object3({
22574
- threadId: uuid5(),
22575
- createdAt: iso_exports2.datetime().nullable(),
22576
- descendantThreadIds: array2(uuid5()).max(900),
22577
- dataStatus: _enum2([
22578
- "available",
22579
- "partial",
22580
- "unavailable",
22581
- "not_queried"
22582
- ]),
22583
- usageSource: string5().max(100),
22584
- balanceUsageCredits: string5().regex(/^[+-]?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d{1,3})?$/).max(128).nullable()
22585
- }).strict()).max(100),
22586
- authTurns: array2(object3({
22587
- threadId: uuid5(),
22588
- turnId: uuid5(),
22589
- mode: _enum2(["chatgpt", "api_key", "conflicting"])
22590
- }).strict()).max(5e3)
22591
- }).strict();
22592
- var UUID_PATTERN3 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;
22593
- var episodeCountSchema = number4().int().min(0).max(Number.MAX_SAFE_INTEGER);
22594
- var collectorFrictionSummarySchema = object3({
22595
- weekEpisodes: episodeCountSchema,
22596
- dayEpisodes: episodeCountSchema
22597
- }).strict().refine((summary) => summary.dayEpisodes <= summary.weekEpisodes, "The last day cannot have more friction episodes than the last week.").describe("dayEpisodes must not exceed weekEpisodes.");
22598
- var RECENT_MESSAGE_LIMIT = 400;
22599
- var sessionCountSchema = number4().int().min(0).max(1e6);
22600
- var collectorHistoryProgressSchema = object3({
22601
- sessions: sessionCountSchema,
22602
- readSessions: sessionCountSchema,
22603
- complete: boolean4(),
22604
- recentMessages: object3({
22605
- sent: number4().int().min(0).max(RECENT_MESSAGE_LIMIT),
22606
- complete: boolean4()
22607
- }).strict().optional().describe("The newest messages sent ahead of the rest of the history; collectors before 0.17.0 omit it.")
22608
- }).strict().refine((progress) => progress.readSessions <= progress.sessions, "Read sessions cannot exceed listed sessions.").refine((progress) => !progress.complete || progress.readSessions === progress.sessions, "A complete import has read every listed session.").describe("readSessions must not exceed sessions; complete requires equal counts.");
22609
- var collectorEnrollmentRequestSchema = object3({
22610
- deviceName: string5().trim().min(1).max(160).describe("Trimmed before length validation."),
22611
- organizationId: string5().regex(UUID_PATTERN3).optional(),
22612
- source: _enum2(COLLECTOR_SOURCES2).optional()
22613
- }).strict();
22614
- var collectorExpansionRequestSchema = object3({
22615
- source: _enum2(COLLECTOR_SOURCES2),
22616
- token: string5().regex(/^[A-Za-z0-9_-]{43}$/),
22617
- organizationId: string5().regex(UUID_PATTERN3).optional()
22618
- }).strict();
22619
- var collectorEnrollmentResponseSchema = discriminatedUnion2("status", [
22620
- object3({
22621
- status: literal2("enrolled"),
22622
- token: string5().regex(/^[A-Za-z0-9_-]{43}$/),
22623
- deviceId: string5().regex(UUID_PATTERN3),
22624
- organizationId: string5().regex(UUID_PATTERN3),
22625
- organizationName: string5().min(1).max(160)
22626
- }).strict(),
22627
- object3({
22628
- status: literal2("choose_organization"),
22629
- organizations: array2(object3({
22630
- id: string5().regex(UUID_PATTERN3),
22631
- name: string5().min(1).max(160)
22632
- }).strict()).max(20),
22633
- hasMore: boolean4()
22634
- }).strict()
22635
- ]);
22636
- function parseCollectorEnrollmentResponse(input) {
22637
- const parsed = collectorEnrollmentResponseSchema.safeParse(input);
22638
- return parsed.success ? { success: true, value: parsed.data } : {
22639
- success: false,
22640
- error: parsed.error.issues[0]?.message ?? "Collector enrollment response is invalid."
22641
- };
22642
- }
22643
- function parseCollectorFrictionSummary(input) {
22644
- const parsed = collectorFrictionSummarySchema.safeParse(input);
22645
- return parsed.success ? { success: true, value: parsed.data } : {
22646
- success: false,
22647
- error: parsed.error.issues[0]?.message ?? "Friction summary is invalid."
22648
- };
22649
- }
22650
-
22651
- // src/background-service.ts
22652
- import { promises as fs16 } from "node:fs";
22653
- import os6 from "node:os";
22654
- import path20 from "node:path";
22655
-
22656
- // src/launch-agent.ts
22657
- import { execFile as execFileCallback2 } from "node:child_process";
22553
+ // src/agent-tools.ts
22658
22554
  import { promises as fs11 } from "node:fs";
22659
22555
  import os3 from "node:os";
22660
22556
  import path8 from "node:path";
22661
- import { setTimeout as sleep } from "node:timers/promises";
22662
- import { promisify as promisify2 } from "node:util";
22557
+ import readline from "node:readline";
22663
22558
 
22664
22559
  // src/runtime.ts
22665
22560
  import { createHash as createHash4 } from "node:crypto";
@@ -23063,7 +22958,351 @@ async function stableNodePath() {
23063
22958
  return process.execPath;
23064
22959
  }
23065
22960
 
22961
+ // src/agent-tools.ts
22962
+ var SERVER_NAME = "iteration";
22963
+ var RENEW_BEFORE_MS = 5 * 60 * 1e3;
22964
+ var accessSchema = object3({
22965
+ url: url(),
22966
+ token: string5().min(1),
22967
+ expiresAt: iso_exports2.datetime()
22968
+ });
22969
+ var versioned = object3({ protocolVersion: string5().optional() }).loose().optional();
22970
+ var messageSchema = object3({
22971
+ id: union2([string5(), number4()]).optional(),
22972
+ method: string5().optional(),
22973
+ params: versioned,
22974
+ result: versioned
22975
+ }).loose();
22976
+ function write(message) {
22977
+ process.stdout.write(`${JSON.stringify(message)}
22978
+ `);
22979
+ }
22980
+ async function serveAgentTools(config3) {
22981
+ let access = null;
22982
+ let version3;
22983
+ let renewing = null;
22984
+ const fetchAccess = async () => {
22985
+ if (config3 === null)
22986
+ throw new Error(
22987
+ "This computer is not connected to Iteration. Run iteration connect."
22988
+ );
22989
+ const response = await collectorFetch(
22990
+ new URL("./tool-access", validateCollectorEndpoint(config3.endpoint)),
22991
+ {
22992
+ method: "POST",
22993
+ headers: { Authorization: `Bearer ${config3.token}` },
22994
+ signal: AbortSignal.timeout(15e3)
22995
+ }
22996
+ );
22997
+ const payload = await response.json().catch(() => null);
22998
+ if (!response.ok) {
22999
+ const error3 = object3({ error: string5() }).safeParse(payload);
23000
+ throw new Error(
23001
+ `Iteration's tools are unavailable: ${error3.success ? error3.data.error : `HTTP ${response.status}`}`
23002
+ );
23003
+ }
23004
+ const parsed = accessSchema.parse(payload);
23005
+ access = { ...parsed, expiresAt: Date.parse(parsed.expiresAt) };
23006
+ return access;
23007
+ };
23008
+ const renew = (force) => {
23009
+ if (!force && access !== null && access.expiresAt - Date.now() > RENEW_BEFORE_MS)
23010
+ return Promise.resolve(access);
23011
+ renewing ??= fetchAccess().finally(() => {
23012
+ renewing = null;
23013
+ });
23014
+ return renewing;
23015
+ };
23016
+ const post = async (body, method, force) => {
23017
+ const current = await renew(force);
23018
+ return fetch(current.url, {
23019
+ method: "POST",
23020
+ headers: {
23021
+ Authorization: `Bearer ${current.token}`,
23022
+ "Content-Type": "application/json",
23023
+ Accept: "application/json, text/event-stream",
23024
+ ...version3 !== void 0 && method !== "initialize" ? { "MCP-Protocol-Version": version3 } : {},
23025
+ ...method === void 0 ? {} : { "MCP-Method": method }
23026
+ },
23027
+ body,
23028
+ signal: AbortSignal.timeout(31e4)
23029
+ });
23030
+ };
23031
+ const relay = (text) => {
23032
+ const message = messageSchema.safeParse(JSON.parse(text));
23033
+ const negotiated = message.success ? message.data.result?.protocolVersion : void 0;
23034
+ if (negotiated !== void 0) version3 = negotiated;
23035
+ write(JSON.parse(text));
23036
+ };
23037
+ const forward = async (line) => {
23038
+ const message = messageSchema.parse(JSON.parse(line));
23039
+ if (message.method === "initialize")
23040
+ version3 = message.params?.protocolVersion ?? version3;
23041
+ try {
23042
+ let response = await post(line, message.method, false);
23043
+ if (response.status === 401)
23044
+ response = await post(line, message.method, true);
23045
+ if (response.status === 202 || message.id === void 0) return;
23046
+ const type = response.headers.get("content-type") ?? "";
23047
+ if (type.includes("text/event-stream")) {
23048
+ const text2 = await response.text();
23049
+ for (const event3 of text2.split(/\r?\n\r?\n/)) {
23050
+ const data = event3.split(/\r?\n/).filter((row) => row.startsWith("data:")).map((row) => row.slice(5).trimStart()).join("\n");
23051
+ if (data.trim()) relay(data);
23052
+ }
23053
+ return;
23054
+ }
23055
+ const text = await response.text();
23056
+ if (text.trim()) relay(text);
23057
+ else if (!response.ok)
23058
+ throw new Error(`Iteration's tools answered HTTP ${response.status}.`);
23059
+ } catch (error3) {
23060
+ if (message.id === void 0) return;
23061
+ write({
23062
+ jsonrpc: "2.0",
23063
+ id: message.id,
23064
+ error: {
23065
+ code: -32603,
23066
+ message: error3 instanceof Error ? error3.message : "Iteration's tools are unavailable."
23067
+ }
23068
+ });
23069
+ }
23070
+ };
23071
+ const pending = /* @__PURE__ */ new Set();
23072
+ for await (const line of readline.createInterface({
23073
+ input: process.stdin
23074
+ })) {
23075
+ if (!line.trim()) continue;
23076
+ const task = forward(line).catch(() => void 0);
23077
+ pending.add(task);
23078
+ void task.finally(() => pending.delete(task));
23079
+ }
23080
+ await Promise.allSettled(pending);
23081
+ }
23082
+ async function writeAtomically(filename, content) {
23083
+ await fs11.mkdir(path8.dirname(filename), { recursive: true });
23084
+ const mode = await fs11.stat(filename).then((stat) => stat.mode & 511).catch(() => 384);
23085
+ const temporary = `${filename}.${process.pid}.${Date.now()}.tmp`;
23086
+ await fs11.writeFile(temporary, content, { mode });
23087
+ await fs11.rename(temporary, filename);
23088
+ }
23089
+ async function readText2(filename) {
23090
+ try {
23091
+ return await fs11.readFile(filename, "utf8");
23092
+ } catch (error3) {
23093
+ if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
23094
+ return null;
23095
+ throw error3;
23096
+ }
23097
+ }
23098
+ var jsonServersSchema = object3({
23099
+ mcpServers: record3(string5(), unknown2()).optional()
23100
+ }).loose();
23101
+ async function updateJsonServers(filename, server) {
23102
+ const text = await readText2(filename);
23103
+ if (text === null && server === null) return;
23104
+ let parsed;
23105
+ try {
23106
+ parsed = jsonServersSchema.parse(text === null ? {} : JSON.parse(text));
23107
+ } catch (error3) {
23108
+ throw new Error(`Could not read MCP settings at ${filename}.`, {
23109
+ cause: error3
23110
+ });
23111
+ }
23112
+ const { [SERVER_NAME]: existing, ...others } = parsed.mcpServers ?? {};
23113
+ if (server === null && existing === void 0) return;
23114
+ if (server !== null && JSON.stringify(existing) === JSON.stringify(server))
23115
+ return;
23116
+ const mcpServers = server === null ? others : { ...others, [SERVER_NAME]: server };
23117
+ await writeAtomically(
23118
+ filename,
23119
+ `${JSON.stringify({ ...parsed, mcpServers }, null, 2)}
23120
+ `
23121
+ );
23122
+ }
23123
+ var CODEX_TABLE = `[mcp_servers.${SERVER_NAME}]`;
23124
+ function withoutCodexServer(text) {
23125
+ const lines = text.split(/\r?\n/);
23126
+ const kept = [];
23127
+ let skipping = false;
23128
+ for (const line of lines) {
23129
+ const table = /^\s*\[([^\]]+)\]\s*(?:#.*)?$/.exec(line)?.[1]?.trim();
23130
+ if (table !== void 0)
23131
+ skipping = table === `mcp_servers.${SERVER_NAME}` || table.startsWith(`mcp_servers.${SERVER_NAME}.`);
23132
+ if (!skipping) kept.push(line);
23133
+ }
23134
+ return kept.join("\n").replace(/\n{3,}$/, "\n\n");
23135
+ }
23136
+ async function updateCodexServer(filename, command) {
23137
+ const text = await readText2(filename);
23138
+ if (text === null && command === null) return;
23139
+ const base = withoutCodexServer(text ?? "");
23140
+ const [program, ...args] = command ?? [];
23141
+ const next = command === null ? base : `${base.trimEnd()}${base.trim() ? "\n\n" : ""}${CODEX_TABLE}
23142
+ command = ${JSON.stringify(program)}
23143
+ args = ${JSON.stringify(args)}
23144
+ `;
23145
+ if (next === text) return;
23146
+ await writeAtomically(filename, next);
23147
+ }
23148
+ function claudeSettingsFile(claudeHome) {
23149
+ return path8.resolve(claudeHome) === path8.join(os3.homedir(), ".claude") ? path8.join(os3.homedir(), ".claude.json") : path8.join(claudeHome, ".claude.json");
23150
+ }
23151
+ var cursorSettingsFile = () => path8.join(os3.homedir(), ".cursor", "mcp.json");
23152
+ async function installAgentTools(homes) {
23153
+ const command = [
23154
+ await stableNodePath(),
23155
+ runtimePaths().launcher,
23156
+ "mcp"
23157
+ ];
23158
+ const [program, ...args] = command;
23159
+ if (homes.claude !== void 0)
23160
+ await updateJsonServers(claudeSettingsFile(homes.claude), {
23161
+ type: "stdio",
23162
+ command: program,
23163
+ args
23164
+ });
23165
+ if (homes.codex !== void 0)
23166
+ await updateCodexServer(path8.join(homes.codex, "config.toml"), command);
23167
+ if (homes.cursor)
23168
+ await updateJsonServers(cursorSettingsFile(), { command: program, args });
23169
+ }
23170
+ async function uninstallAgentTools(homes) {
23171
+ if (homes.claude !== void 0)
23172
+ await updateJsonServers(claudeSettingsFile(homes.claude), null);
23173
+ if (homes.codex !== void 0)
23174
+ await updateCodexServer(path8.join(homes.codex, "config.toml"), null);
23175
+ if (homes.cursor) await updateJsonServers(cursorSettingsFile(), null);
23176
+ }
23177
+ function agentHomes(config3) {
23178
+ return "cursorHome" in config3 ? { cursor: true } : "claudeHome" in config3 ? { claude: config3.claudeHome } : { codex: config3.codexHome };
23179
+ }
23180
+
23181
+ // src/auto-connect.ts
23182
+ import { createHmac } from "node:crypto";
23183
+ import { promises as fs20 } from "node:fs";
23184
+ import os9 from "node:os";
23185
+ import path25 from "node:path";
23186
+
23187
+ // ../collector-contracts/dist/index.js
23188
+ var cursorBillingRequestSchema = object3({
23189
+ conversationId: uuid5(),
23190
+ reconcileFromMs: number4().int().nonnegative().nullable(),
23191
+ events: array2(object3({
23192
+ timestamp: number4().int().nonnegative(),
23193
+ kind: _enum2([
23194
+ "included",
23195
+ "paid",
23196
+ "byok",
23197
+ "not_charged",
23198
+ "unknown"
23199
+ ]),
23200
+ inputTokens: number4().int().nonnegative().nullable(),
23201
+ outputTokens: number4().int().nonnegative().nullable(),
23202
+ cacheReadTokens: number4().int().nonnegative().nullable(),
23203
+ cacheWriteTokens: number4().int().nonnegative().nullable(),
23204
+ paidUsd: number4().finite().nonnegative().nullable()
23205
+ }).strict()).max(5e3)
23206
+ }).strict();
23207
+ function codexCreditSign(value) {
23208
+ const mantissa = value.split(/[eE]/, 1)[0];
23209
+ if (!/[1-9]/.test(mantissa))
23210
+ return 0;
23211
+ return mantissa.startsWith("-") ? -1 : 1;
23212
+ }
23213
+ var codexBillingRequestSchema = object3({
23214
+ accountFingerprint: string5().regex(/^[0-9a-f]{64}$/),
23215
+ reconcile: boolean4(),
23216
+ tasks: array2(object3({
23217
+ threadId: uuid5(),
23218
+ createdAt: iso_exports2.datetime().nullable(),
23219
+ descendantThreadIds: array2(uuid5()).max(900),
23220
+ dataStatus: _enum2([
23221
+ "available",
23222
+ "partial",
23223
+ "unavailable",
23224
+ "not_queried"
23225
+ ]),
23226
+ usageSource: string5().max(100),
23227
+ balanceUsageCredits: string5().regex(/^[+-]?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d{1,3})?$/).max(128).nullable()
23228
+ }).strict()).max(100),
23229
+ authTurns: array2(object3({
23230
+ threadId: uuid5(),
23231
+ turnId: uuid5(),
23232
+ mode: _enum2(["chatgpt", "api_key", "conflicting"])
23233
+ }).strict()).max(5e3)
23234
+ }).strict();
23235
+ var UUID_PATTERN3 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;
23236
+ var episodeCountSchema = number4().int().min(0).max(Number.MAX_SAFE_INTEGER);
23237
+ var collectorFrictionSummarySchema = object3({
23238
+ weekEpisodes: episodeCountSchema,
23239
+ dayEpisodes: episodeCountSchema
23240
+ }).strict().refine((summary) => summary.dayEpisodes <= summary.weekEpisodes, "The last day cannot have more friction episodes than the last week.").describe("dayEpisodes must not exceed weekEpisodes.");
23241
+ var RECENT_MESSAGE_LIMIT = 400;
23242
+ var sessionCountSchema = number4().int().min(0).max(1e6);
23243
+ var collectorHistoryProgressSchema = object3({
23244
+ sessions: sessionCountSchema,
23245
+ readSessions: sessionCountSchema,
23246
+ complete: boolean4(),
23247
+ recentMessages: object3({
23248
+ sent: number4().int().min(0).max(RECENT_MESSAGE_LIMIT),
23249
+ complete: boolean4()
23250
+ }).strict().optional().describe("The newest messages sent ahead of the rest of the history; collectors before 0.17.0 omit it.")
23251
+ }).strict().refine((progress) => progress.readSessions <= progress.sessions, "Read sessions cannot exceed listed sessions.").refine((progress) => !progress.complete || progress.readSessions === progress.sessions, "A complete import has read every listed session.").describe("readSessions must not exceed sessions; complete requires equal counts.");
23252
+ var collectorEnrollmentRequestSchema = object3({
23253
+ deviceName: string5().trim().min(1).max(160).describe("Trimmed before length validation."),
23254
+ organizationId: string5().regex(UUID_PATTERN3).optional(),
23255
+ source: _enum2(COLLECTOR_SOURCES2).optional()
23256
+ }).strict();
23257
+ var collectorExpansionRequestSchema = object3({
23258
+ source: _enum2(COLLECTOR_SOURCES2),
23259
+ token: string5().regex(/^[A-Za-z0-9_-]{43}$/),
23260
+ organizationId: string5().regex(UUID_PATTERN3).optional()
23261
+ }).strict();
23262
+ var collectorEnrollmentResponseSchema = discriminatedUnion2("status", [
23263
+ object3({
23264
+ status: literal2("enrolled"),
23265
+ token: string5().regex(/^[A-Za-z0-9_-]{43}$/),
23266
+ deviceId: string5().regex(UUID_PATTERN3),
23267
+ organizationId: string5().regex(UUID_PATTERN3),
23268
+ organizationName: string5().min(1).max(160)
23269
+ }).strict(),
23270
+ object3({
23271
+ status: literal2("choose_organization"),
23272
+ organizations: array2(object3({
23273
+ id: string5().regex(UUID_PATTERN3),
23274
+ name: string5().min(1).max(160)
23275
+ }).strict()).max(20),
23276
+ hasMore: boolean4()
23277
+ }).strict()
23278
+ ]);
23279
+ function parseCollectorEnrollmentResponse(input) {
23280
+ const parsed = collectorEnrollmentResponseSchema.safeParse(input);
23281
+ return parsed.success ? { success: true, value: parsed.data } : {
23282
+ success: false,
23283
+ error: parsed.error.issues[0]?.message ?? "Collector enrollment response is invalid."
23284
+ };
23285
+ }
23286
+ function parseCollectorFrictionSummary(input) {
23287
+ const parsed = collectorFrictionSummarySchema.safeParse(input);
23288
+ return parsed.success ? { success: true, value: parsed.data } : {
23289
+ success: false,
23290
+ error: parsed.error.issues[0]?.message ?? "Friction summary is invalid."
23291
+ };
23292
+ }
23293
+
23294
+ // src/background-service.ts
23295
+ import { promises as fs17 } from "node:fs";
23296
+ import os7 from "node:os";
23297
+ import path21 from "node:path";
23298
+
23066
23299
  // src/launch-agent.ts
23300
+ import { execFile as execFileCallback2 } from "node:child_process";
23301
+ import { promises as fs12 } from "node:fs";
23302
+ import os4 from "node:os";
23303
+ import path17 from "node:path";
23304
+ import { setTimeout as sleep } from "node:timers/promises";
23305
+ import { promisify as promisify2 } from "node:util";
23067
23306
  var execFile2 = promisify2(execFileCallback2);
23068
23307
  function label(source) {
23069
23308
  return `com.iteration.${source}-collector`;
@@ -23074,16 +23313,16 @@ var RESTART_POLL_MS = 200;
23074
23313
  function xml(value) {
23075
23314
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
23076
23315
  }
23077
- function plistPath(source, homeDirectory = os3.homedir()) {
23078
- return path8.join(
23316
+ function plistPath(source, homeDirectory = os4.homedir()) {
23317
+ return path17.join(
23079
23318
  homeDirectory,
23080
23319
  "Library",
23081
23320
  "LaunchAgents",
23082
23321
  `${label(source)}.plist`
23083
23322
  );
23084
23323
  }
23085
- function launchAgentPlist(launcherPath = runtimePaths().launcher, nodePath = process.execPath, logDirectory = path8.join(os3.homedir(), "Library", "Logs"), dataDirectory2 = path8.join(
23086
- os3.homedir(),
23324
+ function launchAgentPlist(launcherPath = runtimePaths().launcher, nodePath = process.execPath, logDirectory = path17.join(os4.homedir(), "Library", "Logs"), dataDirectory2 = path17.join(
23325
+ os4.homedir(),
23087
23326
  ".config",
23088
23327
  "iteration",
23089
23328
  "codex-collector"
@@ -23106,8 +23345,8 @@ function launchAgentPlist(launcherPath = runtimePaths().launcher, nodePath = pro
23106
23345
  "<key>RunAtLoad</key><true/>",
23107
23346
  "<key>KeepAlive</key><true/>",
23108
23347
  "<key>ThrottleInterval</key><integer>10</integer>",
23109
- `<key>StandardOutPath</key><string>${xml(path8.join(logDirectory, `iteration-${source}-collector.log`))}</string>`,
23110
- `<key>StandardErrorPath</key><string>${xml(path8.join(logDirectory, `iteration-${source}-collector.error.log`))}</string>`,
23348
+ `<key>StandardOutPath</key><string>${xml(path17.join(logDirectory, `iteration-${source}-collector.log`))}</string>`,
23349
+ `<key>StandardErrorPath</key><string>${xml(path17.join(logDirectory, `iteration-${source}-collector.error.log`))}</string>`,
23111
23350
  "</dict></plist>"
23112
23351
  ].join("");
23113
23352
  }
@@ -23187,17 +23426,17 @@ async function installLaunchAgent(dataDirectory2, source = "codex") {
23187
23426
  throw new Error("Automatic startup is currently supported on macOS only.");
23188
23427
  }
23189
23428
  const file = plistPath(source);
23190
- const launchAgents = path8.dirname(file);
23191
- const logs = path8.join(os3.homedir(), "Library", "Logs");
23192
- await fs11.mkdir(launchAgents, { recursive: true, mode: 493 });
23193
- await fs11.mkdir(logs, { recursive: true, mode: 493 });
23194
- await fs11.writeFile(
23429
+ const launchAgents = path17.dirname(file);
23430
+ const logs = path17.join(os4.homedir(), "Library", "Logs");
23431
+ await fs12.mkdir(launchAgents, { recursive: true, mode: 493 });
23432
+ await fs12.mkdir(logs, { recursive: true, mode: 493 });
23433
+ await fs12.writeFile(
23195
23434
  file,
23196
23435
  launchAgentPlist(
23197
23436
  void 0,
23198
23437
  await stableNodePath(),
23199
23438
  void 0,
23200
- dataDirectory2 ?? path8.join(os3.homedir(), ".config", "iteration", `${source}-collector`),
23439
+ dataDirectory2 ?? path17.join(os4.homedir(), ".config", "iteration", `${source}-collector`),
23201
23440
  source
23202
23441
  ),
23203
23442
  { encoding: "utf8", mode: 420 }
@@ -23229,7 +23468,7 @@ async function uninstallLaunchAgent(source = "codex") {
23229
23468
  }
23230
23469
  const file = plistPath(source);
23231
23470
  await bootoutIfLoaded(launchctlTarget(), source);
23232
- await fs11.rm(file, { force: true });
23471
+ await fs12.rm(file, { force: true });
23233
23472
  }
23234
23473
  async function launchAgentStatus(source = "codex") {
23235
23474
  if (process.platform !== "darwin") return false;
@@ -23250,21 +23489,21 @@ function launchAgentRunning(output) {
23250
23489
 
23251
23490
  // src/runtime-install.ts
23252
23491
  import { execFile as execFileCallback4 } from "node:child_process";
23253
- import { promises as fs13 } from "node:fs";
23492
+ import { promises as fs14 } from "node:fs";
23254
23493
  import { promisify as promisify4 } from "node:util";
23255
23494
 
23256
23495
  // src/updater.ts
23257
23496
  import { execFile as execFileCallback3 } from "node:child_process";
23258
23497
  import { createHash as createHash5 } from "node:crypto";
23259
- import { createWriteStream as createWriteStream2, promises as fs12 } from "node:fs";
23498
+ import { createWriteStream as createWriteStream2, promises as fs13 } from "node:fs";
23260
23499
  import http from "node:http";
23261
23500
  import https2 from "node:https";
23262
- import path17 from "node:path";
23501
+ import path18 from "node:path";
23263
23502
  import { Transform } from "node:stream";
23264
23503
  import { pipeline as pipeline2 } from "node:stream/promises";
23265
23504
  import { promisify as promisify3 } from "node:util";
23266
23505
  var execFile3 = promisify3(execFileCallback3);
23267
- var TAR = process.platform === "win32" ? path17.join(process.env.SystemRoot ?? "C:\\Windows", "System32", "tar.exe") : "tar";
23506
+ var TAR = process.platform === "win32" ? path18.join(process.env.SystemRoot ?? "C:\\Windows", "System32", "tar.exe") : "tar";
23268
23507
  var PACKAGE_NAME = "iteration-cli";
23269
23508
  var MAX_METADATA_BYTES = 16 * 1024 * 1024;
23270
23509
  var MAX_TARBALL_BYTES = 64 * 1024 * 1024;
@@ -23468,32 +23707,32 @@ async function selfCheck(paths, cliPath, version3) {
23468
23707
  }
23469
23708
  async function installCandidate(paths, candidate, signal) {
23470
23709
  await ensureRuntimeDirectory(paths);
23471
- const staged = await fs12.mkdtemp(
23472
- path17.join(paths.staging, `download-${candidate.version}-`)
23710
+ const staged = await fs13.mkdtemp(
23711
+ path18.join(paths.staging, `download-${candidate.version}-`)
23473
23712
  );
23474
23713
  try {
23475
- const archive = path17.join(staged, "package.tgz");
23714
+ const archive = path18.join(staged, "package.tgz");
23476
23715
  await downloadVerified(
23477
23716
  candidate.tarball,
23478
23717
  candidate.integrity,
23479
23718
  archive,
23480
23719
  signal
23481
23720
  );
23482
- const extracted = path17.join(staged, "extracted");
23483
- await fs12.mkdir(extracted, { mode: 448 });
23721
+ const extracted = path18.join(staged, "extracted");
23722
+ await fs13.mkdir(extracted, { mode: 448 });
23484
23723
  await execFile3(TAR, ["-xzf", archive, "-C", extracted], {
23485
23724
  timeout: REQUEST_TIMEOUT_MS,
23486
23725
  windowsHide: true
23487
23726
  });
23488
- const packageRoot = path17.join(extracted, "package");
23727
+ const packageRoot = path18.join(extracted, "package");
23489
23728
  await selfCheck(
23490
23729
  paths,
23491
- path17.join(packageRoot, "dist", "cli.js"),
23730
+ path18.join(packageRoot, "dist", "cli.js"),
23492
23731
  candidate.version
23493
23732
  );
23494
23733
  await installVersionDirectory(paths, packageRoot, candidate.version);
23495
23734
  } finally {
23496
- await fs12.rm(staged, { recursive: true, force: true });
23735
+ await fs13.rm(staged, { recursive: true, force: true });
23497
23736
  }
23498
23737
  }
23499
23738
  async function switchVersion(paths, from, version3, previous, expected, nowMs) {
@@ -23601,7 +23840,7 @@ async function ownVersion() {
23601
23840
  }
23602
23841
  async function exists(filename) {
23603
23842
  try {
23604
- await fs13.access(filename);
23843
+ await fs14.access(filename);
23605
23844
  return true;
23606
23845
  } catch (error3) {
23607
23846
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
@@ -23628,7 +23867,7 @@ async function installLauncher(paths, version3) {
23628
23867
  );
23629
23868
  await commitLauncher(paths, staged);
23630
23869
  } finally {
23631
- await fs13.rm(staged, { force: true });
23870
+ await fs14.rm(staged, { force: true });
23632
23871
  }
23633
23872
  }
23634
23873
  async function ensureRuntimeInstalled(expected, paths = runtimePaths()) {
@@ -23710,9 +23949,9 @@ async function expireRuntimeTrial(nowMs, paths = runtimePaths()) {
23710
23949
 
23711
23950
  // src/systemd-user.ts
23712
23951
  import { execFile as execFileCallback5 } from "node:child_process";
23713
- import { promises as fs14 } from "node:fs";
23714
- import os4 from "node:os";
23715
- import path18 from "node:path";
23952
+ import { promises as fs15 } from "node:fs";
23953
+ import os5 from "node:os";
23954
+ import path19 from "node:path";
23716
23955
  import { promisify as promisify5 } from "node:util";
23717
23956
  var execFile5 = promisify5(execFileCallback5);
23718
23957
  var SYSTEMCTL_TIMEOUT_MS = 1e4;
@@ -23782,8 +24021,8 @@ function unitName(source) {
23782
24021
  return `iteration-${source}-collector.service`;
23783
24022
  }
23784
24023
  function unitPath(source) {
23785
- return path18.join(
23786
- os4.homedir(),
24024
+ return path19.join(
24025
+ os5.homedir(),
23787
24026
  ".config",
23788
24027
  "systemd",
23789
24028
  "user",
@@ -23791,8 +24030,8 @@ function unitPath(source) {
23791
24030
  );
23792
24031
  }
23793
24032
  function systemdLogPath(source) {
23794
- return path18.join(
23795
- os4.homedir(),
24033
+ return path19.join(
24034
+ os5.homedir(),
23796
24035
  ".config",
23797
24036
  "iteration",
23798
24037
  "logs",
@@ -23804,13 +24043,13 @@ function unitValue(value) {
23804
24043
  throw new Error("Collector paths cannot contain line breaks.");
23805
24044
  return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("%", "%%")}"`;
23806
24045
  }
23807
- function systemdUserUnit(launcherPath = runtimePaths().launcher, nodePath = process.execPath, dataDirectory2 = path18.join(
23808
- os4.homedir(),
24046
+ function systemdUserUnit(launcherPath = runtimePaths().launcher, nodePath = process.execPath, dataDirectory2 = path19.join(
24047
+ os5.homedir(),
23809
24048
  ".config",
23810
24049
  "iteration",
23811
24050
  "codex-collector"
23812
- ), source = "codex", outputPath = path18.join(
23813
- os4.homedir(),
24051
+ ), source = "codex", outputPath = path19.join(
24052
+ os5.homedir(),
23814
24053
  ".config",
23815
24054
  "iteration",
23816
24055
  "logs",
@@ -23840,10 +24079,10 @@ async function systemctl(...args) {
23840
24079
  async function installSystemdUserService(dataDirectory2, source) {
23841
24080
  await ensureSystemdUserLinger();
23842
24081
  const file = unitPath(source);
23843
- const logDirectory = path18.dirname(systemdLogPath(source));
23844
- await fs14.mkdir(path18.dirname(file), { recursive: true, mode: 448 });
23845
- await fs14.mkdir(logDirectory, { recursive: true, mode: 448 });
23846
- await fs14.writeFile(
24082
+ const logDirectory = path19.dirname(systemdLogPath(source));
24083
+ await fs15.mkdir(path19.dirname(file), { recursive: true, mode: 448 });
24084
+ await fs15.mkdir(logDirectory, { recursive: true, mode: 448 });
24085
+ await fs15.writeFile(
23847
24086
  file,
23848
24087
  systemdUserUnit(void 0, await stableNodePath(), dataDirectory2, source),
23849
24088
  {
@@ -23859,14 +24098,14 @@ async function installSystemdUserService(dataDirectory2, source) {
23859
24098
  async function uninstallSystemdUserService(source) {
23860
24099
  const file = unitPath(source);
23861
24100
  try {
23862
- await fs14.access(file);
24101
+ await fs15.access(file);
23863
24102
  } catch (error3) {
23864
24103
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
23865
24104
  return;
23866
24105
  throw error3;
23867
24106
  }
23868
24107
  await systemctl("disable", "--now", unitName(source));
23869
- await fs14.rm(file);
24108
+ await fs15.rm(file);
23870
24109
  await systemctl("daemon-reload");
23871
24110
  }
23872
24111
  async function systemdUserServiceStatus(source) {
@@ -23885,9 +24124,9 @@ async function systemdUserServiceStatus(source) {
23885
24124
 
23886
24125
  // src/task-scheduler.ts
23887
24126
  import { execFile as execFileCallback6 } from "node:child_process";
23888
- import { promises as fs15 } from "node:fs";
23889
- import os5 from "node:os";
23890
- import path19 from "node:path";
24127
+ import { promises as fs16 } from "node:fs";
24128
+ import os6 from "node:os";
24129
+ import path20 from "node:path";
23891
24130
  import { setTimeout as delay3 } from "node:timers/promises";
23892
24131
  import { promisify as promisify6 } from "node:util";
23893
24132
 
@@ -23989,14 +24228,14 @@ function scheduledTaskName(source, sid) {
23989
24228
  return `iteration-${sid}-${source}-collector`;
23990
24229
  }
23991
24230
  function servicesDirectory() {
23992
- return path19.join(os5.homedir(), ".config", "iteration", "services");
24231
+ return path20.join(os6.homedir(), ".config", "iteration", "services");
23993
24232
  }
23994
24233
  function taskDefinitionPath(source) {
23995
- return path19.join(servicesDirectory(), `iteration-${source}-collector.xml`);
24234
+ return path20.join(servicesDirectory(), `iteration-${source}-collector.xml`);
23996
24235
  }
23997
24236
  function windowsLogPath(source) {
23998
- return path19.join(
23999
- os5.homedir(),
24237
+ return path20.join(
24238
+ os6.homedir(),
24000
24239
  ".config",
24001
24240
  "iteration",
24002
24241
  "logs",
@@ -24004,8 +24243,8 @@ function windowsLogPath(source) {
24004
24243
  );
24005
24244
  }
24006
24245
  function windowsOutputPath(source) {
24007
- return path19.join(
24008
- os5.homedir(),
24246
+ return path20.join(
24247
+ os6.homedir(),
24009
24248
  ".config",
24010
24249
  "iteration",
24011
24250
  "logs",
@@ -24020,7 +24259,7 @@ function scheduledTaskScript(task) {
24020
24259
  "--data-directory",
24021
24260
  task.dataDirectory
24022
24261
  ].map(windowsArgument).join(" ");
24023
- const errorLog = path19.join(
24262
+ const errorLog = path20.join(
24024
24263
  task.home,
24025
24264
  ".config",
24026
24265
  "iteration",
@@ -24140,7 +24379,7 @@ function AccountSid([string]$account) {
24140
24379
  async function schtasks(source, operation, ...args) {
24141
24380
  try {
24142
24381
  await execFile6(
24143
- path19.join(
24382
+ path20.join(
24144
24383
  process.env.SystemRoot ?? "C:\\Windows",
24145
24384
  "System32",
24146
24385
  "schtasks.exe"
@@ -24184,7 +24423,7 @@ async function desiredTask(dataDirectory2, source) {
24184
24423
  userSid: await currentUserSid(),
24185
24424
  nodePath: await stableNodePath(),
24186
24425
  launcher: runtimePaths().launcher,
24187
- home: os5.homedir()
24426
+ home: os6.homedir()
24188
24427
  };
24189
24428
  }
24190
24429
  async function scheduledTaskUsesRuntime(dataDirectory2, source) {
@@ -24196,10 +24435,10 @@ async function scheduledTaskUsesRuntime(dataDirectory2, source) {
24196
24435
  async function installScheduledTask(dataDirectory2, source) {
24197
24436
  const file = taskDefinitionPath(source);
24198
24437
  await ensurePrivateDirectory(servicesDirectory());
24199
- await ensurePrivateDirectory(path19.dirname(windowsLogPath(source)));
24438
+ await ensurePrivateDirectory(path20.dirname(windowsLogPath(source)));
24200
24439
  await stopTask(source);
24201
24440
  const xml2 = scheduledTaskXml(await desiredTask(dataDirectory2, source));
24202
- await fs15.writeFile(
24441
+ await fs16.writeFile(
24203
24442
  file,
24204
24443
  Buffer.concat([Buffer.from([255, 254]), Buffer.from(xml2, "utf16le")])
24205
24444
  );
@@ -24209,7 +24448,7 @@ async function installScheduledTask(dataDirectory2, source) {
24209
24448
  }
24210
24449
  async function uninstallScheduledTask(source) {
24211
24450
  if (await stopTask(source)) await schtasks(source, "/Delete", "/F");
24212
- await fs15.rm(taskDefinitionPath(source), { force: true });
24451
+ await fs16.rm(taskDefinitionPath(source), { force: true });
24213
24452
  }
24214
24453
  async function scheduledTaskStatus(source) {
24215
24454
  return (await observedTask(source))?.state === 4;
@@ -24233,8 +24472,8 @@ async function prepareBackgroundPersistence() {
24233
24472
  function collectorLogPath(source) {
24234
24473
  if (process.platform === "linux") return systemdLogPath(source);
24235
24474
  if (process.platform === "win32") return windowsLogPath(source);
24236
- return path20.join(
24237
- os6.homedir(),
24475
+ return path21.join(
24476
+ os7.homedir(),
24238
24477
  "Library",
24239
24478
  "Logs",
24240
24479
  `iteration-${source}-collector.error.log`
@@ -24268,7 +24507,7 @@ async function backgroundServiceDefinition(source) {
24268
24507
  requireCollectorPlatform();
24269
24508
  const file = process.platform === "darwin" ? plistPath(source) : process.platform === "linux" ? unitPath(source) : taskDefinitionPath(source);
24270
24509
  try {
24271
- return await fs16.readFile(
24510
+ return await fs17.readFile(
24272
24511
  file,
24273
24512
  process.platform === "win32" ? "utf16le" : "utf8"
24274
24513
  );
@@ -24317,7 +24556,7 @@ function serviceDataDirectory(definition, platform) {
24317
24556
  (_, slashes) => slashes.slice(0, slashes.length / 2)
24318
24557
  );
24319
24558
  }
24320
- if (!directory || !(platform === "win32" ? path20.win32 : path20.posix).isAbsolute(directory))
24559
+ if (!directory || !(platform === "win32" ? path21.win32 : path21.posix).isAbsolute(directory))
24321
24560
  throw new Error("Could not read the collector service data directory.");
24322
24561
  return directory;
24323
24562
  }
@@ -24340,15 +24579,15 @@ async function backgroundServiceUsesRuntime(directory, source) {
24340
24579
  }
24341
24580
 
24342
24581
  // src/config-paths.ts
24343
- import os7 from "node:os";
24344
- import path21 from "node:path";
24582
+ import os8 from "node:os";
24583
+ import path22 from "node:path";
24345
24584
  function defaultCollectorDirectory(source) {
24346
- return path21.join(os7.homedir(), ".config", "iteration", `${source}-collector`);
24585
+ return path22.join(os8.homedir(), ".config", "iteration", `${source}-collector`);
24347
24586
  }
24348
24587
 
24349
24588
  // src/cursor-hooks.ts
24350
- import { promises as fs17 } from "node:fs";
24351
- import path22 from "node:path";
24589
+ import { promises as fs18 } from "node:fs";
24590
+ import path23 from "node:path";
24352
24591
  function shellQuote(value) {
24353
24592
  if (process.platform === "win32") return `"${value}"`;
24354
24593
  return `'${value.replaceAll("'", `'\\''`)}'`;
@@ -24383,8 +24622,8 @@ async function readHooks(filename) {
24383
24622
  let contents;
24384
24623
  let mode = 384;
24385
24624
  try {
24386
- contents = await fs17.readFile(filename, "utf8");
24387
- mode = (await fs17.stat(filename)).mode & 511;
24625
+ contents = await fs18.readFile(filename, "utf8");
24626
+ mode = (await fs18.stat(filename)).mode & 511;
24388
24627
  } catch (error3) {
24389
24628
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
24390
24629
  return { file: {}, mode };
@@ -24406,14 +24645,14 @@ async function readHooks(filename) {
24406
24645
  return { file: checked.data, mode };
24407
24646
  }
24408
24647
  async function writeHooks(filename, file, mode) {
24409
- await fs17.mkdir(path22.dirname(filename), { recursive: true, mode: 448 });
24410
- const target = await fs17.realpath(filename).catch((error3) => {
24648
+ await fs18.mkdir(path23.dirname(filename), { recursive: true, mode: 448 });
24649
+ const target = await fs18.realpath(filename).catch((error3) => {
24411
24650
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
24412
24651
  return filename;
24413
24652
  throw error3;
24414
24653
  });
24415
24654
  const temporary = `${target}.iteration-${process.pid}.tmp`;
24416
- const handle = await fs17.open(temporary, "w", mode);
24655
+ const handle = await fs18.open(temporary, "w", mode);
24417
24656
  try {
24418
24657
  await handle.writeFile(`${JSON.stringify(file, null, 2)}
24419
24658
  `, "utf8");
@@ -24421,14 +24660,14 @@ async function writeHooks(filename, file, mode) {
24421
24660
  } finally {
24422
24661
  await handle.close();
24423
24662
  }
24424
- await fs17.rename(temporary, target);
24663
+ await fs18.rename(temporary, target);
24425
24664
  }
24426
24665
  function entries(value) {
24427
24666
  return value ?? [];
24428
24667
  }
24429
24668
  async function installCursorHooks(cursorHome, dataDirectory2) {
24430
24669
  await ensurePrivateDirectory(cursorHookDirectory(dataDirectory2));
24431
- const filename = path22.join(cursorHome, "hooks.json");
24670
+ const filename = path23.join(cursorHome, "hooks.json");
24432
24671
  const { file, mode } = await readHooks(filename);
24433
24672
  const hooks = { ...file.hooks };
24434
24673
  const nodePath = await stableNodePath();
@@ -24442,7 +24681,7 @@ async function installCursorHooks(cursorHome, dataDirectory2) {
24442
24681
  await writeHooks(filename, { ...file, version: 1, hooks }, mode);
24443
24682
  }
24444
24683
  async function uninstallCursorHooks(cursorHome, dataDirectory2) {
24445
- const filename = path22.join(cursorHome, "hooks.json");
24684
+ const filename = path23.join(cursorHome, "hooks.json");
24446
24685
  const { file, mode } = await readHooks(filename);
24447
24686
  const hooks = { ...file.hooks };
24448
24687
  let changed = false;
@@ -24472,8 +24711,8 @@ function silenceCollectorLogs() {
24472
24711
  // src/turn-summary.ts
24473
24712
  import { execFile as execFileCallback7 } from "node:child_process";
24474
24713
  import { createHash as createHash6 } from "node:crypto";
24475
- import { promises as fs18 } from "node:fs";
24476
- import path23 from "node:path";
24714
+ import { promises as fs19 } from "node:fs";
24715
+ import path24 from "node:path";
24477
24716
  import { promisify as promisify7 } from "node:util";
24478
24717
  import { crc32 } from "node:zlib";
24479
24718
  var execFile7 = promisify7(execFileCallback7);
@@ -24484,13 +24723,13 @@ var REQUEST_TIMEOUT_MS2 = 1e4;
24484
24723
  var HOOK_TIMEOUT_SECONDS = 1;
24485
24724
  var REQUEST_KEY_PATTERN = /^\d{1,10}$/;
24486
24725
  function turnSummaryPaths(dataDirectory2) {
24487
- const directory = path23.join(dataDirectory2, "turn-summary");
24726
+ const directory = path24.join(dataDirectory2, "turn-summary");
24488
24727
  return {
24489
24728
  directory,
24490
- script: path23.join(directory, "stop-hook.sh"),
24491
- requests: path23.join(directory, "requests"),
24492
- lines: path23.join(directory, "lines"),
24493
- shown: path23.join(directory, "shown")
24729
+ script: path24.join(directory, "stop-hook.sh"),
24730
+ requests: path24.join(directory, "requests"),
24731
+ lines: path24.join(directory, "lines"),
24732
+ shown: path24.join(directory, "shown")
24494
24733
  };
24495
24734
  }
24496
24735
  function shellQuote2(value) {
@@ -24546,7 +24785,7 @@ async function sessionStartCommand(config3) {
24546
24785
  function agentHookFile(config3) {
24547
24786
  if ("cursorHome" in config3)
24548
24787
  throw new Error("Cursor uses its own trace hooks.");
24549
- return "claudeHome" in config3 ? path23.join(config3.claudeHome, "settings.json") : path23.join(config3.codexHome, "hooks.json");
24788
+ return "claudeHome" in config3 ? path24.join(config3.claudeHome, "settings.json") : path24.join(config3.codexHome, "hooks.json");
24550
24789
  }
24551
24790
  var hookEntrySchema2 = object3({
24552
24791
  command: string5().optional(),
@@ -24563,8 +24802,8 @@ async function readHookFile(filename) {
24563
24802
  let contents;
24564
24803
  let mode = 384;
24565
24804
  try {
24566
- contents = await fs18.readFile(filename, "utf8");
24567
- mode = (await fs18.stat(filename)).mode & 511;
24805
+ contents = await fs19.readFile(filename, "utf8");
24806
+ mode = (await fs19.stat(filename)).mode & 511;
24568
24807
  } catch (error3) {
24569
24808
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT") {
24570
24809
  return { file: {}, mode };
@@ -24590,19 +24829,19 @@ async function readHookFile(filename) {
24590
24829
  return { file: parsed.data, mode };
24591
24830
  }
24592
24831
  async function writeHookFile(filename, file, mode) {
24593
- await fs18.mkdir(path23.dirname(filename), { recursive: true });
24594
- const target = await fs18.realpath(filename).catch((error3) => {
24832
+ await fs19.mkdir(path24.dirname(filename), { recursive: true });
24833
+ const target = await fs19.realpath(filename).catch((error3) => {
24595
24834
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
24596
24835
  return filename;
24597
24836
  throw error3;
24598
24837
  });
24599
24838
  const temporary = `${target}.iteration-${process.pid}.tmp`;
24600
- await fs18.writeFile(temporary, `${JSON.stringify(file, null, 2)}
24839
+ await fs19.writeFile(temporary, `${JSON.stringify(file, null, 2)}
24601
24840
  `, {
24602
24841
  encoding: "utf8",
24603
24842
  mode
24604
24843
  });
24605
- await fs18.rename(temporary, target);
24844
+ await fs19.rename(temporary, target);
24606
24845
  }
24607
24846
  function withoutCommand(groups, command) {
24608
24847
  return groups.flatMap((group) => {
@@ -24627,24 +24866,24 @@ async function installTurnSummary(config3) {
24627
24866
  [paths.requests, paths.lines, paths.shown].map(ensurePrivateDirectory)
24628
24867
  );
24629
24868
  if (process.platform !== "win32") {
24630
- await fs18.writeFile(paths.script, hookScript(paths), {
24869
+ await fs19.writeFile(paths.script, hookScript(paths), {
24631
24870
  encoding: "utf8",
24632
24871
  mode: 448
24633
24872
  });
24634
- await fs18.chmod(paths.script, 448);
24873
+ await fs19.chmod(paths.script, 448);
24635
24874
  }
24636
24875
  if ("claudeHome" in config3) {
24637
24876
  const gitPaths = claudeGitHookPaths(config3.dataDirectory);
24638
24877
  await ensurePrivateDirectory(gitPaths.queue);
24639
24878
  if (process.platform !== "win32") {
24640
- await fs18.writeFile(
24879
+ await fs19.writeFile(
24641
24880
  gitPaths.script,
24642
24881
  `#!/bin/sh
24643
24882
  exec ${shellQuote2(await stableNodePath())} ${shellQuote2(runtimePaths().launcher)} claude-git-hook ${shellQuote2(config3.dataDirectory)}
24644
24883
  `,
24645
24884
  { encoding: "utf8", mode: 448 }
24646
24885
  );
24647
- await fs18.chmod(gitPaths.script, 448);
24886
+ await fs19.chmod(gitPaths.script, 448);
24648
24887
  }
24649
24888
  }
24650
24889
  const filename = agentHookFile(config3);
@@ -24734,19 +24973,19 @@ async function captureTurnSummary(dataDirectory2) {
24734
24973
  windowsHide: true
24735
24974
  }
24736
24975
  ));
24737
- directory = path23.normalize(directory.trim());
24976
+ directory = path24.normalize(directory.trim());
24738
24977
  } catch {
24739
24978
  quiet();
24740
24979
  return;
24741
24980
  }
24742
- if (!path23.isAbsolute(directory)) {
24981
+ if (!path24.isAbsolute(directory)) {
24743
24982
  quiet();
24744
24983
  return;
24745
24984
  }
24746
24985
  const key = String(crc32(directory));
24747
24986
  const paths = turnSummaryPaths(dataDirectory2);
24748
24987
  try {
24749
- await fs18.writeFile(path23.join(paths.requests, key), `${directory}
24988
+ await fs19.writeFile(path24.join(paths.requests, key), `${directory}
24750
24989
  `, {
24751
24990
  encoding: "utf8",
24752
24991
  mode: 384
@@ -24755,7 +24994,7 @@ async function captureTurnSummary(dataDirectory2) {
24755
24994
  }
24756
24995
  let lines;
24757
24996
  try {
24758
- lines = await fs18.readFile(path23.join(paths.lines, key), "utf8");
24997
+ lines = await fs19.readFile(path24.join(paths.lines, key), "utf8");
24759
24998
  } catch {
24760
24999
  quiet();
24761
25000
  return;
@@ -24767,7 +25006,7 @@ async function captureTurnSummary(dataDirectory2) {
24767
25006
  }
24768
25007
  let last;
24769
25008
  try {
24770
- last = (await fs18.readFile(path23.join(paths.shown, key), "utf8")).split("\n")[0] ?? null;
25009
+ last = (await fs19.readFile(path24.join(paths.shown, key), "utf8")).split("\n")[0] ?? null;
24771
25010
  } catch {
24772
25011
  last = null;
24773
25012
  }
@@ -24776,7 +25015,7 @@ async function captureTurnSummary(dataDirectory2) {
24776
25015
  return;
24777
25016
  }
24778
25017
  try {
24779
- await fs18.writeFile(path23.join(paths.shown, key), `${message}
25018
+ await fs19.writeFile(path24.join(paths.shown, key), `${message}
24780
25019
  `, {
24781
25020
  encoding: "utf8",
24782
25021
  mode: 384
@@ -24836,7 +25075,7 @@ async function fetchSummary(config3, directory, fetcher, signal) {
24836
25075
  }
24837
25076
  async function readRequest(filename) {
24838
25077
  try {
24839
- const [directory] = (await fs18.readFile(filename, "utf8")).split("\n");
25078
+ const [directory] = (await fs19.readFile(filename, "utf8")).split("\n");
24840
25079
  return directory ?? null;
24841
25080
  } catch (error3) {
24842
25081
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
@@ -24845,25 +25084,25 @@ async function readRequest(filename) {
24845
25084
  }
24846
25085
  }
24847
25086
  async function writeLine(paths, key, directory, message) {
24848
- const filename = path23.join(paths.lines, key);
25087
+ const filename = path24.join(paths.lines, key);
24849
25088
  if (message === null) {
24850
- await fs18.rm(filename, { force: true });
25089
+ await fs19.rm(filename, { force: true });
24851
25090
  return;
24852
25091
  }
24853
25092
  const temporary = `${filename}.${process.pid}.tmp`;
24854
- await fs18.writeFile(
25093
+ await fs19.writeFile(
24855
25094
  temporary,
24856
25095
  `${directory}
24857
25096
  ${JSON.stringify({ systemMessage: message })}
24858
25097
  `,
24859
25098
  { encoding: "utf8", mode: 384 }
24860
25099
  );
24861
- await fs18.rename(temporary, filename);
25100
+ await fs19.rename(temporary, filename);
24862
25101
  }
24863
25102
  async function forget(paths, key) {
24864
25103
  await Promise.all(
24865
25104
  [paths.requests, paths.lines, paths.shown].map(
24866
- (directory) => fs18.rm(path23.join(directory, key), { force: true })
25105
+ (directory) => fs19.rm(path24.join(directory, key), { force: true })
24867
25106
  )
24868
25107
  );
24869
25108
  }
@@ -24871,7 +25110,7 @@ async function refreshTurnSummaries(config3, refreshedAt, nowMs, fetcher = colle
24871
25110
  const paths = turnSummaryPaths(config3.dataDirectory);
24872
25111
  let keys;
24873
25112
  try {
24874
- keys = await fs18.readdir(paths.requests);
25113
+ keys = await fs19.readdir(paths.requests);
24875
25114
  } catch (error3) {
24876
25115
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
24877
25116
  return 0;
@@ -24881,8 +25120,8 @@ async function refreshTurnSummaries(config3, refreshedAt, nowMs, fetcher = colle
24881
25120
  for (const key of keys.sort()) {
24882
25121
  signal?.throwIfAborted();
24883
25122
  if (!REQUEST_KEY_PATTERN.test(key)) continue;
24884
- const filename = path23.join(paths.requests, key);
24885
- const stats = await fs18.stat(filename).catch((error3) => {
25123
+ const filename = path24.join(paths.requests, key);
25124
+ const stats = await fs19.stat(filename).catch((error3) => {
24886
25125
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
24887
25126
  return null;
24888
25127
  throw error3;
@@ -24949,8 +25188,8 @@ async function codexTurnSummaryApproval(config3) {
24949
25188
  if (handlerIndex < 0) continue;
24950
25189
  let configToml;
24951
25190
  try {
24952
- configToml = await fs18.readFile(
24953
- path23.join(config3.codexHome, "config.toml"),
25191
+ configToml = await fs19.readFile(
25192
+ path24.join(config3.codexHome, "config.toml"),
24954
25193
  "utf8"
24955
25194
  );
24956
25195
  } catch (error3) {
@@ -24977,25 +25216,25 @@ var consentSchema = object3({
24977
25216
  sourceHomes: object3({
24978
25217
  codex: string5(),
24979
25218
  claude: string5(),
24980
- cursor: string5().default(path24.join(os8.homedir(), ".cursor"))
25219
+ cursor: string5().default(path25.join(os9.homedir(), ".cursor"))
24981
25220
  }).strict()
24982
25221
  }).strict();
24983
25222
  function consentPath() {
24984
- return path24.join(os8.homedir(), ".config", "iteration", "auto-connect.json");
25223
+ return path25.join(os9.homedir(), ".config", "iteration", "auto-connect.json");
24985
25224
  }
24986
25225
  async function removeMachineConsent() {
24987
- await fs19.rm(consentPath(), { force: true });
25226
+ await fs20.rm(consentPath(), { force: true });
24988
25227
  }
24989
25228
  async function detectedSources(sourceHomes) {
24990
25229
  const detected = [];
24991
25230
  for (const source of COLLECTOR_SOURCES2) {
24992
- const directory = path24.join(
25231
+ const directory = path25.join(
24993
25232
  sourceHomes[source],
24994
25233
  source === "codex" ? "sessions" : "projects"
24995
25234
  );
24996
25235
  let stats;
24997
25236
  try {
24998
- stats = await fs19.stat(directory);
25237
+ stats = await fs20.stat(directory);
24999
25238
  } catch (error3) {
25000
25239
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
25001
25240
  continue;
@@ -25015,7 +25254,7 @@ async function detectedSources(sourceHomes) {
25015
25254
  async function loadMachineConsent() {
25016
25255
  let text;
25017
25256
  try {
25018
- text = await fs19.readFile(consentPath(), "utf8");
25257
+ text = await fs20.readFile(consentPath(), "utf8");
25019
25258
  } catch (error3) {
25020
25259
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
25021
25260
  return null;
@@ -25041,9 +25280,9 @@ async function saveMachineConsent(consent) {
25041
25280
  const parsed = consentSchema.parse(consent);
25042
25281
  validateCollectorEndpoint(parsed.endpoint);
25043
25282
  const filename = consentPath();
25044
- await ensurePrivateDirectory(path24.dirname(filename));
25283
+ await ensurePrivateDirectory(path25.dirname(filename));
25045
25284
  const temporary = `${filename}.${process.pid}.tmp`;
25046
- const handle = await fs19.open(temporary, "w", 384);
25285
+ const handle = await fs20.open(temporary, "w", 384);
25047
25286
  try {
25048
25287
  await handle.writeFile(`${JSON.stringify(parsed)}
25049
25288
  `, "utf8");
@@ -25051,7 +25290,7 @@ async function saveMachineConsent(consent) {
25051
25290
  } finally {
25052
25291
  await handle.close();
25053
25292
  }
25054
- await fs19.rename(temporary, filename);
25293
+ await fs20.rename(temporary, filename);
25055
25294
  }
25056
25295
  async function readCollectorScope(anchor3) {
25057
25296
  const endpoint = validateCollectorEndpoint(anchor3.endpoint);
@@ -25147,7 +25386,7 @@ async function connectNewDetectedSources(anchor3, consent) {
25147
25386
  if (source === consent.anchorSource) continue;
25148
25387
  const paths = collectorPaths(defaultCollectorDirectory(source));
25149
25388
  try {
25150
- await fs19.access(paths.config);
25389
+ await fs20.access(paths.config);
25151
25390
  continue;
25152
25391
  } catch (error3) {
25153
25392
  if (!(error3 instanceof Error && "code" in error3 && error3.code === "ENOENT"))
@@ -25188,7 +25427,7 @@ async function connectNewDetectedSources(anchor3, consent) {
25188
25427
  async function connectedConfig(source) {
25189
25428
  const paths = collectorPaths(defaultCollectorDirectory(source));
25190
25429
  try {
25191
- await fs19.access(paths.config);
25430
+ await fs20.access(paths.config);
25192
25431
  } catch (error3) {
25193
25432
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
25194
25433
  return null;
@@ -25682,7 +25921,7 @@ async function authenticateAndEnrollCollector(endpoint, deviceNameValue, organiz
25682
25921
  }
25683
25922
 
25684
25923
  // src/history-import.ts
25685
- import path25 from "node:path";
25924
+ import path26 from "node:path";
25686
25925
  function backgroundControl(source) {
25687
25926
  return {
25688
25927
  isRunning: () => backgroundServiceStatus(source),
@@ -25695,7 +25934,7 @@ async function importSourceHistory(config3, paths, confirmed, control = backgrou
25695
25934
  const source = collectorSource(config3);
25696
25935
  const directory = collectorSourceDirectory(config3);
25697
25936
  const state = await loadState(paths);
25698
- if (!state.initialized || state.sourceDirectory !== path25.resolve(directory)) {
25937
+ if (!state.initialized || state.sourceDirectory !== path26.resolve(directory)) {
25699
25938
  throw new Error(
25700
25939
  `Connect the ${source} source before importing its history.`
25701
25940
  );
@@ -25744,36 +25983,36 @@ async function importSourceHistory(config3, paths, confirmed, control = backgrou
25744
25983
 
25745
25984
  // src/logout.ts
25746
25985
  import { randomUUID as randomUUID3 } from "node:crypto";
25747
- import { promises as fs20 } from "node:fs";
25748
- import os9 from "node:os";
25749
- import path26 from "node:path";
25986
+ import { promises as fs21 } from "node:fs";
25987
+ import os10 from "node:os";
25988
+ import path27 from "node:path";
25750
25989
  async function pendingDirectories(filename) {
25751
25990
  let contents;
25752
25991
  try {
25753
- contents = await fs20.readFile(filename, "utf8");
25992
+ contents = await fs21.readFile(filename, "utf8");
25754
25993
  } catch (error3) {
25755
25994
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
25756
25995
  return [];
25757
25996
  throw error3;
25758
25997
  }
25759
- const parsed = array2(string5().refine(path26.isAbsolute)).max(64).safeParse(JSON.parse(contents));
25998
+ const parsed = array2(string5().refine(path27.isAbsolute)).max(64).safeParse(JSON.parse(contents));
25760
25999
  if (!parsed.success)
25761
26000
  throw new Error("Pending logout directories are invalid.");
25762
26001
  return parsed.data;
25763
26002
  }
25764
26003
  async function savePendingDirectories(filename, directories) {
25765
- await ensurePrivateDirectory(path26.dirname(filename));
26004
+ await ensurePrivateDirectory(path27.dirname(filename));
25766
26005
  const temporary = `${filename}.${process.pid}.tmp`;
25767
- await fs20.writeFile(temporary, JSON.stringify([...directories]), {
26006
+ await fs21.writeFile(temporary, JSON.stringify([...directories]), {
25768
26007
  mode: 384
25769
26008
  });
25770
- await fs20.rename(temporary, filename);
26009
+ await fs21.rename(temporary, filename);
25771
26010
  }
25772
26011
  async function clearConnection(directory) {
25773
26012
  const paths = collectorPaths(directory);
25774
26013
  let names;
25775
26014
  try {
25776
- names = await fs20.readdir(directory);
26015
+ names = await fs21.readdir(directory);
25777
26016
  } catch (error3) {
25778
26017
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
25779
26018
  return;
@@ -25783,14 +26022,15 @@ async function clearConnection(directory) {
25783
26022
  try {
25784
26023
  for (const name of names)
25785
26024
  if (/^config\.json\.\d+\.tmp$/.test(name))
25786
- await fs20.rm(path26.join(directory, name), { force: true });
25787
- if (!names.includes(path26.basename(paths.config))) return;
26025
+ await fs21.rm(path27.join(directory, name), { force: true });
26026
+ if (!names.includes(path27.basename(paths.config))) return;
25788
26027
  const config3 = await loadConfig(paths);
25789
26028
  if (config3.destination === "vault")
25790
26029
  throw new Error("This directory belongs to Trace Vault, not Iteration.");
25791
26030
  if ("cursorHome" in config3)
25792
26031
  await uninstallCursorHooks(config3.cursorHome, config3.dataDirectory);
25793
26032
  else await uninstallTurnSummary(config3);
26033
+ await uninstallAgentTools(agentHomes(config3));
25794
26034
  const retained = [
25795
26035
  paths.state,
25796
26036
  paths.outbox,
@@ -25805,27 +26045,27 @@ async function clearConnection(directory) {
25805
26045
  "turn-summary",
25806
26046
  "codex-billing-sync.json",
25807
26047
  "cursor-billing-sync.json"
25808
- ].map((name) => path26.join(directory, name))
26048
+ ].map((name) => path27.join(directory, name))
25809
26049
  ];
25810
- const archive = path26.join(directory, "logged-out", randomUUID3());
26050
+ const archive = path27.join(directory, "logged-out", randomUUID3());
25811
26051
  await ensurePrivateDirectory(archive);
25812
26052
  for (const filename of retained) {
25813
26053
  try {
25814
- await fs20.rename(filename, path26.join(archive, path26.basename(filename)));
26054
+ await fs21.rename(filename, path27.join(archive, path27.basename(filename)));
25815
26055
  } catch (error3) {
25816
26056
  if (!(error3 instanceof Error && "code" in error3 && error3.code === "ENOENT"))
25817
26057
  throw error3;
25818
26058
  }
25819
26059
  }
25820
- await fs20.rm(paths.config);
26060
+ await fs21.rm(paths.config);
25821
26061
  } finally {
25822
26062
  await release();
25823
26063
  }
25824
26064
  }
25825
26065
  async function logout(extraDirectory) {
25826
26066
  requireCollectorPlatform();
25827
- const pending = path26.join(
25828
- os9.homedir(),
26067
+ const pending = path27.join(
26068
+ os10.homedir(),
25829
26069
  ".config",
25830
26070
  "iteration",
25831
26071
  "logout-pending.json"
@@ -25835,7 +26075,7 @@ async function logout(extraDirectory) {
25835
26075
  ...await pendingDirectories(pending)
25836
26076
  ]);
25837
26077
  if (extraDirectory !== void 0)
25838
- directories.add(path26.resolve(extraDirectory));
26078
+ directories.add(path27.resolve(extraDirectory));
25839
26079
  const failures = [];
25840
26080
  try {
25841
26081
  await removeMachineConsent();
@@ -25880,7 +26120,7 @@ async function logout(extraDirectory) {
25880
26120
  `Logout did not finish. Resolve these errors and run iteration logout again:
25881
26121
  ${failures.join("\n")}`
25882
26122
  );
25883
- await fs20.rm(pending);
26123
+ await fs21.rm(pending);
25884
26124
  }
25885
26125
 
25886
26126
  // src/runtime-services.ts
@@ -25911,8 +26151,8 @@ import { setTimeout as delay5 } from "node:timers/promises";
25911
26151
 
25912
26152
  // src/codex-billing.ts
25913
26153
  import { createHash as createHash7 } from "node:crypto";
25914
- import { promises as fs21 } from "node:fs";
25915
- import path27 from "node:path";
26154
+ import { promises as fs22 } from "node:fs";
26155
+ import path28 from "node:path";
25916
26156
  var TASK_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage/thread_usage/query_v2";
25917
26157
  var UUID2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
25918
26158
  var DAY_MS = 24 * 60 * 60 * 1e3;
@@ -25964,7 +26204,7 @@ function fingerprint(value) {
25964
26204
  async function readAuth(codexHome) {
25965
26205
  let contents;
25966
26206
  try {
25967
- contents = await fs21.readFile(path27.join(codexHome, "auth.json"), "utf8");
26207
+ contents = await fs22.readFile(path28.join(codexHome, "auth.json"), "utf8");
25968
26208
  } catch (error3) {
25969
26209
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
25970
26210
  return {
@@ -25999,7 +26239,7 @@ async function readAuth(codexHome) {
25999
26239
  async function readState(filename) {
26000
26240
  let contents;
26001
26241
  try {
26002
- contents = await fs21.readFile(filename, "utf8");
26242
+ contents = await fs22.readFile(filename, "utf8");
26003
26243
  } catch (error3) {
26004
26244
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
26005
26245
  return null;
@@ -26021,7 +26261,7 @@ async function readState(filename) {
26021
26261
  }
26022
26262
  async function saveState2(filename, state) {
26023
26263
  const temporary = `${filename}.${process.pid}.tmp`;
26024
- const handle = await fs21.open(temporary, "w", 384);
26264
+ const handle = await fs22.open(temporary, "w", 384);
26025
26265
  try {
26026
26266
  await handle.writeFile(`${JSON.stringify(state)}
26027
26267
  `);
@@ -26029,11 +26269,11 @@ async function saveState2(filename, state) {
26029
26269
  } finally {
26030
26270
  await handle.close();
26031
26271
  }
26032
- await fs21.rename(temporary, filename);
26272
+ await fs22.rename(temporary, filename);
26033
26273
  }
26034
26274
  async function localThread(filename) {
26035
- const stat = await fs21.stat(filename);
26036
- const handle = await fs21.open(filename, "r");
26275
+ const stat = await fs22.stat(filename);
26276
+ const handle = await fs22.open(filename, "r");
26037
26277
  let header = null;
26038
26278
  try {
26039
26279
  const buffer = Buffer.alloc(64 * 1024);
@@ -26133,9 +26373,9 @@ function parseCodexAuthLog(threadId, body) {
26133
26373
  return { threadId, turnId: turn[1], mode };
26134
26374
  }
26135
26375
  async function localAuthTurns(codexHome, modelProviders) {
26136
- const filename = path27.join(codexHome, "logs_2.sqlite");
26376
+ const filename = path28.join(codexHome, "logs_2.sqlite");
26137
26377
  try {
26138
- await fs21.access(filename);
26378
+ await fs22.access(filename);
26139
26379
  } catch (error3) {
26140
26380
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
26141
26381
  return [];
@@ -26228,7 +26468,7 @@ async function queryTaskUsage(tasks, accessToken, accountId, signal) {
26228
26468
  }
26229
26469
  async function syncCodexBilling(config3, fetcher = collectorFetch, signal) {
26230
26470
  const auth = await readAuth(config3.codexHome);
26231
- const stateFile = path27.join(config3.dataDirectory, "codex-billing-sync.json");
26471
+ const stateFile = path28.join(config3.dataDirectory, "codex-billing-sync.json");
26232
26472
  const prior = await readState(stateFile);
26233
26473
  const nowMs = Date.now();
26234
26474
  const history = prior === null || prior.accountFingerprint !== auth.accountFingerprint;
@@ -26399,9 +26639,9 @@ async function syncCodexBilling(config3, fetcher = collectorFetch, signal) {
26399
26639
 
26400
26640
  // src/cursor-billing.ts
26401
26641
  import { createHash as createHash8 } from "node:crypto";
26402
- import { promises as fs22 } from "node:fs";
26403
- import os10 from "node:os";
26404
- import path28 from "node:path";
26642
+ import { promises as fs23 } from "node:fs";
26643
+ import os11 from "node:os";
26644
+ import path29 from "node:path";
26405
26645
  var CURSOR_USAGE_URL = "https://api2.cursor.sh/aiserver.v1.DashboardService/GetFilteredUsageEvents";
26406
26646
  var PAGE_SIZE = 500;
26407
26647
  var MAX_PAGES = 1e3;
@@ -26432,18 +26672,18 @@ var usagePageSchema = object3({
26432
26672
  var emptyUsagePageSchema = object3({}).strict();
26433
26673
  function cursorAuthDatabase() {
26434
26674
  if (process.platform === "darwin")
26435
- return path28.join(
26436
- os10.homedir(),
26675
+ return path29.join(
26676
+ os11.homedir(),
26437
26677
  "Library/Application Support/Cursor/User/globalStorage/state.vscdb"
26438
26678
  );
26439
26679
  if (process.platform === "linux")
26440
- return path28.join(
26441
- os10.homedir(),
26680
+ return path29.join(
26681
+ os11.homedir(),
26442
26682
  ".config/Cursor/User/globalStorage/state.vscdb"
26443
26683
  );
26444
26684
  if (process.platform === "win32")
26445
- return path28.join(
26446
- process.env.APPDATA ?? path28.join(os10.homedir(), "AppData", "Roaming"),
26685
+ return path29.join(
26686
+ process.env.APPDATA ?? path29.join(os11.homedir(), "AppData", "Roaming"),
26447
26687
  "Cursor/User/globalStorage/state.vscdb"
26448
26688
  );
26449
26689
  return null;
@@ -26485,7 +26725,7 @@ async function readCursorAuth() {
26485
26725
  async function loadBillingState(filename) {
26486
26726
  let contents;
26487
26727
  try {
26488
- contents = await fs22.readFile(filename, "utf8");
26728
+ contents = await fs23.readFile(filename, "utf8");
26489
26729
  } catch (error3) {
26490
26730
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
26491
26731
  return null;
@@ -26501,7 +26741,7 @@ async function loadBillingState(filename) {
26501
26741
  }
26502
26742
  async function saveBillingState(filename, state) {
26503
26743
  const temporary = `${filename}.${process.pid}.tmp`;
26504
- const handle = await fs22.open(temporary, "w", 384);
26744
+ const handle = await fs23.open(temporary, "w", 384);
26505
26745
  try {
26506
26746
  await handle.writeFile(`${JSON.stringify(state)}
26507
26747
  `, "utf8");
@@ -26509,7 +26749,7 @@ async function saveBillingState(filename, state) {
26509
26749
  } finally {
26510
26750
  await handle.close();
26511
26751
  }
26512
- await fs22.rename(temporary, filename);
26752
+ await fs23.rename(temporary, filename);
26513
26753
  }
26514
26754
  function planCursorBillingSync(state, accountFingerprint, nowMs) {
26515
26755
  if (state === null || state.accountFingerprint !== accountFingerprint)
@@ -26531,7 +26771,7 @@ function planCursorBillingSync(state, accountFingerprint, nowMs) {
26531
26771
  }
26532
26772
  function cursorConversationIds(filenames) {
26533
26773
  return new Set(
26534
- filenames.map((filename) => path28.basename(filename, ".jsonl")).filter((id) => UUID3.test(id))
26774
+ filenames.map((filename) => path29.basename(filename, ".jsonl")).filter((id) => UUID3.test(id))
26535
26775
  );
26536
26776
  }
26537
26777
  function billingKind(kind) {
@@ -26589,11 +26829,11 @@ async function syncCursorBilling(config3, fetcher = collectorFetch, signal) {
26589
26829
  if (auth === null)
26590
26830
  return { sessions: 0, matched: 0, skipped: true, mode: null };
26591
26831
  const localConversations = cursorConversationIds(
26592
- await cursorFiles(path28.join(config3.cursorHome, "projects"))
26832
+ await cursorFiles(path29.join(config3.cursorHome, "projects"))
26593
26833
  );
26594
26834
  if (localConversations.size === 0)
26595
26835
  return { sessions: 0, matched: 0, skipped: true, mode: null };
26596
- const stateFile = path28.join(config3.dataDirectory, "cursor-billing-sync.json");
26836
+ const stateFile = path29.join(config3.dataDirectory, "cursor-billing-sync.json");
26597
26837
  const priorState = await loadBillingState(stateFile);
26598
26838
  const nowMs = Date.now();
26599
26839
  const plan = planCursorBillingSync(
@@ -26870,8 +27110,7 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
26870
27110
  const summariesRefreshedAt = /* @__PURE__ */ new Map();
26871
27111
  let lastAutoCheckAt = 0;
26872
27112
  let lastBillingSyncAt = 0;
26873
- const historyProgress = new HistoryProgressReporter();
26874
- let scanned = null;
27113
+ const historyProgress2 = new HistoryProgressReporter();
26875
27114
  const runtimeVersion = process.env.ITERATION_RUNTIME_VERSION;
26876
27115
  let lastRuntimeCheckAt = 0;
26877
27116
  let trialSettled = runtimeVersion === void 0;
@@ -26969,14 +27208,11 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
26969
27208
  { recentMessages: RECENT_MESSAGE_LIMIT }
26970
27209
  );
26971
27210
  billingReady = result.historyCaughtUp && result.outboxBytes === 0;
26972
- scanned = result.history ?? scanned;
26973
- if (scanned !== null) {
26974
- history = {
26975
- ...scanned,
26976
- complete: billingReady && scanned.readSessions === scanned.sessions,
26977
- ...result.recentMessages === null ? {} : { recentMessages: result.recentMessages }
26978
- };
26979
- }
27211
+ history = {
27212
+ ...result.history,
27213
+ complete: billingReady && result.history.readSessions === result.history.sessions,
27214
+ ...result.recentMessages === null ? {} : { recentMessages: result.recentMessages }
27215
+ };
26980
27216
  await recordTickSuccess(result, acknowledgedAtMs);
26981
27217
  } catch (error3) {
26982
27218
  if (controller.signal.aborted) return null;
@@ -27007,7 +27243,7 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
27007
27243
  const reportHistory = async (history) => {
27008
27244
  if (history !== null) {
27009
27245
  try {
27010
- await historyProgress.report(
27246
+ await historyProgress2.report(
27011
27247
  config3,
27012
27248
  history,
27013
27249
  collectorFetch,
@@ -27523,26 +27759,26 @@ function renderRuntime(terminal2, summary) {
27523
27759
 
27524
27760
  // src/windows-supervisor.ts
27525
27761
  import { spawn } from "node:child_process";
27526
- import { promises as fs23 } from "node:fs";
27527
- import path29 from "node:path";
27762
+ import { promises as fs24 } from "node:fs";
27763
+ import path30 from "node:path";
27528
27764
  import { setTimeout as delay6 } from "node:timers/promises";
27529
27765
  var MAX_LOG_BYTES = 8 * 1024 * 1024;
27530
27766
  var RESTART_DELAY_MS = 5e3;
27531
27767
  async function appendLog(filename, bytes) {
27532
27768
  let offset = 0;
27533
27769
  while (offset < bytes.length) {
27534
- const size = await fs23.stat(filename).then((stat) => stat.size).catch((error3) => {
27770
+ const size = await fs24.stat(filename).then((stat) => stat.size).catch((error3) => {
27535
27771
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
27536
27772
  return 0;
27537
27773
  throw error3;
27538
27774
  });
27539
27775
  if (size >= MAX_LOG_BYTES) {
27540
- await fs23.rm(`${filename}.1`, { force: true });
27541
- await fs23.rename(filename, `${filename}.1`);
27776
+ await fs24.rm(`${filename}.1`, { force: true });
27777
+ await fs24.rename(filename, `${filename}.1`);
27542
27778
  continue;
27543
27779
  }
27544
27780
  const count = Math.min(bytes.length - offset, MAX_LOG_BYTES - size);
27545
- await fs23.appendFile(filename, bytes.subarray(offset, offset + count), {
27781
+ await fs24.appendFile(filename, bytes.subarray(offset, offset + count), {
27546
27782
  mode: 384
27547
27783
  });
27548
27784
  offset += count;
@@ -27558,7 +27794,7 @@ async function copyOutput(stream, filename) {
27558
27794
  async function superviseCollector(source, dataDirectory2) {
27559
27795
  const output = windowsOutputPath(source);
27560
27796
  const errors = windowsLogPath(source);
27561
- await ensurePrivateDirectory(path29.dirname(output));
27797
+ await ensurePrivateDirectory(path30.dirname(output));
27562
27798
  const controller = new AbortController();
27563
27799
  const stop = () => controller.abort();
27564
27800
  process.once("SIGTERM", stop);
@@ -27770,7 +28006,7 @@ function usage(command) {
27770
28006
  }
27771
28007
  async function fileExists(filename) {
27772
28008
  try {
27773
- await fs24.access(filename);
28009
+ await fs25.access(filename);
27774
28010
  return true;
27775
28011
  } catch (error3) {
27776
28012
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT") {
@@ -27811,7 +28047,7 @@ async function defaultCommand() {
27811
28047
  await connectAll({});
27812
28048
  }
27813
28049
  function dataDirectory(source, values) {
27814
- return path30.resolve(
28050
+ return path31.resolve(
27815
28051
  optionalString(values, "data-directory") ?? defaultCollectorDirectory(source)
27816
28052
  );
27817
28053
  }
@@ -27891,12 +28127,12 @@ async function initialize(source, values) {
27891
28127
  requireCollectorPlatform();
27892
28128
  const endpoint = optionalString(values, "endpoint") ?? DEFAULT_INGEST_ENDPOINT;
27893
28129
  const endpointUrl = validateCollectorEndpoint(endpoint);
27894
- const deviceName = optionalString(values, "device-name") ?? os11.hostname();
28130
+ const deviceName = optionalString(values, "device-name") ?? os12.hostname();
27895
28131
  const organizationId = optionalString(values, "workspace");
27896
28132
  const directory = dataDirectory(source, values);
27897
28133
  const paths = collectorPaths(directory);
27898
- const selectedHome = path30.resolve(
27899
- source === "codex" ? optionalString(values, "codex-home") ?? process.env.CODEX_HOME ?? path30.join(os11.homedir(), ".codex") : source === "claude" ? optionalString(values, "claude-home") ?? process.env.CLAUDE_CONFIG_DIR ?? path30.join(os11.homedir(), ".claude") : optionalString(values, "cursor-home") ?? path30.join(os11.homedir(), ".cursor")
28134
+ const selectedHome = path31.resolve(
28135
+ source === "codex" ? optionalString(values, "codex-home") ?? process.env.CODEX_HOME ?? path31.join(os12.homedir(), ".codex") : source === "claude" ? optionalString(values, "claude-home") ?? process.env.CLAUDE_CONFIG_DIR ?? path31.join(os12.homedir(), ".claude") : optionalString(values, "cursor-home") ?? path31.join(os12.homedir(), ".cursor")
27900
28136
  );
27901
28137
  await assertFreshCollectorDirectory(paths, source);
27902
28138
  const preparedState = await prepareSourceInitialState(
@@ -27975,7 +28211,7 @@ async function approveAllSources(sources, workspaceName, yes) {
27975
28211
  "",
27976
28212
  wrap(
27977
28213
  terminal,
27978
- `Existing sessions and new activity, including prompts, code and tool output, upload to ${safeName ?? "your workspace"}, where its members can read them. Supported agents you install later connect automatically.${process.platform === "linux" ? " Collection continues after you log out." : ""}`
28214
+ `Existing sessions and new activity, including prompts, code and tool output, upload to ${safeName ?? "your workspace"}, where its members can read them. Your agents also get the workspace's tools. Supported agents you install later connect automatically.${process.platform === "linux" ? " Collection continues after you log out." : ""}`
27979
28215
  ),
27980
28216
  ""
27981
28217
  );
@@ -28004,13 +28240,13 @@ async function connectAll(values) {
28004
28240
  }
28005
28241
  const configs = await Promise.all(COLLECTOR_SOURCES2.map(connectedConfig));
28006
28242
  const resolveSourceHomes = () => ({
28007
- codex: path30.resolve(
28243
+ codex: path31.resolve(
28008
28244
  optionalString(values, "codex-home") ?? (configs[0] && "codexHome" in configs[0] ? configs[0].codexHome : sourceHome("codex"))
28009
28245
  ),
28010
- claude: path30.resolve(
28246
+ claude: path31.resolve(
28011
28247
  optionalString(values, "claude-home") ?? (configs[1] && "claudeHome" in configs[1] ? configs[1].claudeHome : sourceHome("claude"))
28012
28248
  ),
28013
- cursor: path30.resolve(
28249
+ cursor: path31.resolve(
28014
28250
  optionalString(values, "cursor-home") ?? (configs[2] && "cursorHome" in configs[2] ? configs[2].cursorHome : sourceHome("cursor"))
28015
28251
  )
28016
28252
  });
@@ -28163,8 +28399,33 @@ async function connectAll(values) {
28163
28399
  )
28164
28400
  );
28165
28401
  }
28402
+ await addAgentTools(
28403
+ results.map((result) => result.source),
28404
+ sourceHomes
28405
+ );
28166
28406
  print(...connectionSummary2(results, results.length > existing.length));
28167
28407
  }
28408
+ async function addAgentTools(sources, sourceHomes) {
28409
+ for (const source of new Set(sources)) {
28410
+ try {
28411
+ await installAgentTools(
28412
+ source === "cursor" ? { cursor: true } : source === "claude" ? { claude: sourceHomes.claude } : { codex: sourceHomes.codex }
28413
+ );
28414
+ } catch (error3) {
28415
+ print(
28416
+ mark(
28417
+ terminal,
28418
+ "warning",
28419
+ `Could not add Iteration's tools to ${collectorSourceLabel(source)}`
28420
+ ),
28421
+ wrap(
28422
+ terminal,
28423
+ error3 instanceof Error ? error3.message : "Its settings could not be updated."
28424
+ )
28425
+ );
28426
+ }
28427
+ }
28428
+ }
28168
28429
  async function importHistory(source, values) {
28169
28430
  requireCollectorPlatform();
28170
28431
  const directory = dataDirectory(source, values);
@@ -28494,6 +28755,12 @@ async function runInternalCommand() {
28494
28755
  }
28495
28756
  return true;
28496
28757
  }
28758
+ if (process.argv[2] === "mcp") {
28759
+ if (process.argv.length !== 3) throw new Error("Invalid mcp command.");
28760
+ const configs = await Promise.all(COLLECTOR_SOURCES2.map(connectedConfig));
28761
+ await serveAgentTools(configs.find((config3) => config3 !== null) ?? null);
28762
+ return true;
28763
+ }
28497
28764
  if (process.argv[2] === "turn-summary-hook") {
28498
28765
  const directory = process.argv[3];
28499
28766
  if (!directory || process.argv.length !== 4)
@@ -28595,6 +28862,7 @@ async function disconnect(selectedSource, values) {
28595
28862
  if ("cursorHome" in config3)
28596
28863
  await uninstallCursorHooks(config3.cursorHome, config3.dataDirectory);
28597
28864
  else await uninstallTurnSummary(config3);
28865
+ await uninstallAgentTools(agentHomes(config3));
28598
28866
  }
28599
28867
  print(
28600
28868
  mark(