iteration-cli 0.17.1 → 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 +581 -324
  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,
@@ -16079,10 +16079,10 @@ function mergeDefs2(...defs) {
16079
16079
  function cloneDef2(schema) {
16080
16080
  return mergeDefs2(schema._zod.def);
16081
16081
  }
16082
- function getElementAtPath2(obj, path31) {
16083
- if (!path31)
16082
+ function getElementAtPath2(obj, path32) {
16083
+ if (!path32)
16084
16084
  return obj;
16085
- return path31.reduce((acc, key) => acc?.[key], obj);
16085
+ return path32.reduce((acc, key) => acc?.[key], obj);
16086
16086
  }
16087
16087
  function promiseAllObject2(promisesObj) {
16088
16088
  const keys = Object.keys(promisesObj);
@@ -16422,11 +16422,11 @@ function explicitlyAborted2(x, startIndex = 0) {
16422
16422
  }
16423
16423
  return false;
16424
16424
  }
16425
- function prefixIssues2(path31, issues) {
16425
+ function prefixIssues2(path32, issues) {
16426
16426
  return issues.map((iss) => {
16427
16427
  var _a5;
16428
16428
  (_a5 = iss).path ?? (_a5.path = []);
16429
- iss.path.unshift(path31);
16429
+ iss.path.unshift(path32);
16430
16430
  return iss;
16431
16431
  });
16432
16432
  }
@@ -16884,16 +16884,16 @@ function flattenError2(error3, mapper = (issue3) => issue3.message) {
16884
16884
  }
16885
16885
  function formatError2(error3, mapper = (issue3) => issue3.message) {
16886
16886
  const fieldErrors = { _errors: [] };
16887
- const processError = (error4, path31 = []) => {
16887
+ const processError = (error4, path32 = []) => {
16888
16888
  for (const issue3 of error4.issues) {
16889
16889
  if (issue3.code === "invalid_union" && issue3.errors.length) {
16890
- issue3.errors.map((issues) => processError({ issues }, [...path31, ...issue3.path]));
16890
+ issue3.errors.map((issues) => processError({ issues }, [...path32, ...issue3.path]));
16891
16891
  } else if (issue3.code === "invalid_key") {
16892
- processError({ issues: issue3.issues }, [...path31, ...issue3.path]);
16892
+ processError({ issues: issue3.issues }, [...path32, ...issue3.path]);
16893
16893
  } else if (issue3.code === "invalid_element") {
16894
- processError({ issues: issue3.issues }, [...path31, ...issue3.path]);
16894
+ processError({ issues: issue3.issues }, [...path32, ...issue3.path]);
16895
16895
  } else {
16896
- const fullpath = [...path31, ...issue3.path];
16896
+ const fullpath = [...path32, ...issue3.path];
16897
16897
  if (fullpath.length === 0) {
16898
16898
  fieldErrors._errors.push(mapper(issue3));
16899
16899
  } else {
@@ -22550,131 +22550,11 @@ function isLaunchDirectory(value) {
22550
22550
  return true;
22551
22551
  }
22552
22552
 
22553
- // src/auto-connect.ts
22554
- import { createHmac } from "node:crypto";
22555
- import { promises as fs19 } from "node:fs";
22556
- import os8 from "node:os";
22557
- import path24 from "node:path";
22558
-
22559
- // ../collector-contracts/dist/index.js
22560
- var cursorBillingRequestSchema = object3({
22561
- conversationId: uuid5(),
22562
- reconcileFromMs: number4().int().nonnegative().nullable(),
22563
- events: array2(object3({
22564
- timestamp: number4().int().nonnegative(),
22565
- kind: _enum2([
22566
- "included",
22567
- "paid",
22568
- "byok",
22569
- "not_charged",
22570
- "unknown"
22571
- ]),
22572
- inputTokens: number4().int().nonnegative().nullable(),
22573
- outputTokens: number4().int().nonnegative().nullable(),
22574
- cacheReadTokens: number4().int().nonnegative().nullable(),
22575
- cacheWriteTokens: number4().int().nonnegative().nullable(),
22576
- paidUsd: number4().finite().nonnegative().nullable()
22577
- }).strict()).max(5e3)
22578
- }).strict();
22579
- function codexCreditSign(value) {
22580
- const mantissa = value.split(/[eE]/, 1)[0];
22581
- if (!/[1-9]/.test(mantissa))
22582
- return 0;
22583
- return mantissa.startsWith("-") ? -1 : 1;
22584
- }
22585
- var codexBillingRequestSchema = object3({
22586
- accountFingerprint: string5().regex(/^[0-9a-f]{64}$/),
22587
- reconcile: boolean4(),
22588
- tasks: array2(object3({
22589
- threadId: uuid5(),
22590
- createdAt: iso_exports2.datetime().nullable(),
22591
- descendantThreadIds: array2(uuid5()).max(900),
22592
- dataStatus: _enum2([
22593
- "available",
22594
- "partial",
22595
- "unavailable",
22596
- "not_queried"
22597
- ]),
22598
- usageSource: string5().max(100),
22599
- balanceUsageCredits: string5().regex(/^[+-]?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d{1,3})?$/).max(128).nullable()
22600
- }).strict()).max(100),
22601
- authTurns: array2(object3({
22602
- threadId: uuid5(),
22603
- turnId: uuid5(),
22604
- mode: _enum2(["chatgpt", "api_key", "conflicting"])
22605
- }).strict()).max(5e3)
22606
- }).strict();
22607
- 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}$/;
22608
- var episodeCountSchema = number4().int().min(0).max(Number.MAX_SAFE_INTEGER);
22609
- var collectorFrictionSummarySchema = object3({
22610
- weekEpisodes: episodeCountSchema,
22611
- dayEpisodes: episodeCountSchema
22612
- }).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.");
22613
- var RECENT_MESSAGE_LIMIT = 400;
22614
- var sessionCountSchema = number4().int().min(0).max(1e6);
22615
- var collectorHistoryProgressSchema = object3({
22616
- sessions: sessionCountSchema,
22617
- readSessions: sessionCountSchema,
22618
- complete: boolean4(),
22619
- recentMessages: object3({
22620
- sent: number4().int().min(0).max(RECENT_MESSAGE_LIMIT),
22621
- complete: boolean4()
22622
- }).strict().optional().describe("The newest messages sent ahead of the rest of the history; collectors before 0.17.0 omit it.")
22623
- }).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.");
22624
- var collectorEnrollmentRequestSchema = object3({
22625
- deviceName: string5().trim().min(1).max(160).describe("Trimmed before length validation."),
22626
- organizationId: string5().regex(UUID_PATTERN3).optional(),
22627
- source: _enum2(COLLECTOR_SOURCES2).optional()
22628
- }).strict();
22629
- var collectorExpansionRequestSchema = object3({
22630
- source: _enum2(COLLECTOR_SOURCES2),
22631
- token: string5().regex(/^[A-Za-z0-9_-]{43}$/),
22632
- organizationId: string5().regex(UUID_PATTERN3).optional()
22633
- }).strict();
22634
- var collectorEnrollmentResponseSchema = discriminatedUnion2("status", [
22635
- object3({
22636
- status: literal2("enrolled"),
22637
- token: string5().regex(/^[A-Za-z0-9_-]{43}$/),
22638
- deviceId: string5().regex(UUID_PATTERN3),
22639
- organizationId: string5().regex(UUID_PATTERN3),
22640
- organizationName: string5().min(1).max(160)
22641
- }).strict(),
22642
- object3({
22643
- status: literal2("choose_organization"),
22644
- organizations: array2(object3({
22645
- id: string5().regex(UUID_PATTERN3),
22646
- name: string5().min(1).max(160)
22647
- }).strict()).max(20),
22648
- hasMore: boolean4()
22649
- }).strict()
22650
- ]);
22651
- function parseCollectorEnrollmentResponse(input) {
22652
- const parsed = collectorEnrollmentResponseSchema.safeParse(input);
22653
- return parsed.success ? { success: true, value: parsed.data } : {
22654
- success: false,
22655
- error: parsed.error.issues[0]?.message ?? "Collector enrollment response is invalid."
22656
- };
22657
- }
22658
- function parseCollectorFrictionSummary(input) {
22659
- const parsed = collectorFrictionSummarySchema.safeParse(input);
22660
- return parsed.success ? { success: true, value: parsed.data } : {
22661
- success: false,
22662
- error: parsed.error.issues[0]?.message ?? "Friction summary is invalid."
22663
- };
22664
- }
22665
-
22666
- // src/background-service.ts
22667
- import { promises as fs16 } from "node:fs";
22668
- import os6 from "node:os";
22669
- import path20 from "node:path";
22670
-
22671
- // src/launch-agent.ts
22672
- import { execFile as execFileCallback2 } from "node:child_process";
22553
+ // src/agent-tools.ts
22673
22554
  import { promises as fs11 } from "node:fs";
22674
22555
  import os3 from "node:os";
22675
22556
  import path8 from "node:path";
22676
- import { setTimeout as sleep } from "node:timers/promises";
22677
- import { promisify as promisify2 } from "node:util";
22557
+ import readline from "node:readline";
22678
22558
 
22679
22559
  // src/runtime.ts
22680
22560
  import { createHash as createHash4 } from "node:crypto";
@@ -23078,7 +22958,351 @@ async function stableNodePath() {
23078
22958
  return process.execPath;
23079
22959
  }
23080
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
+
23081
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";
23082
23306
  var execFile2 = promisify2(execFileCallback2);
23083
23307
  function label(source) {
23084
23308
  return `com.iteration.${source}-collector`;
@@ -23089,16 +23313,16 @@ var RESTART_POLL_MS = 200;
23089
23313
  function xml(value) {
23090
23314
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
23091
23315
  }
23092
- function plistPath(source, homeDirectory = os3.homedir()) {
23093
- return path8.join(
23316
+ function plistPath(source, homeDirectory = os4.homedir()) {
23317
+ return path17.join(
23094
23318
  homeDirectory,
23095
23319
  "Library",
23096
23320
  "LaunchAgents",
23097
23321
  `${label(source)}.plist`
23098
23322
  );
23099
23323
  }
23100
- function launchAgentPlist(launcherPath = runtimePaths().launcher, nodePath = process.execPath, logDirectory = path8.join(os3.homedir(), "Library", "Logs"), dataDirectory2 = path8.join(
23101
- os3.homedir(),
23324
+ function launchAgentPlist(launcherPath = runtimePaths().launcher, nodePath = process.execPath, logDirectory = path17.join(os4.homedir(), "Library", "Logs"), dataDirectory2 = path17.join(
23325
+ os4.homedir(),
23102
23326
  ".config",
23103
23327
  "iteration",
23104
23328
  "codex-collector"
@@ -23121,8 +23345,8 @@ function launchAgentPlist(launcherPath = runtimePaths().launcher, nodePath = pro
23121
23345
  "<key>RunAtLoad</key><true/>",
23122
23346
  "<key>KeepAlive</key><true/>",
23123
23347
  "<key>ThrottleInterval</key><integer>10</integer>",
23124
- `<key>StandardOutPath</key><string>${xml(path8.join(logDirectory, `iteration-${source}-collector.log`))}</string>`,
23125
- `<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>`,
23126
23350
  "</dict></plist>"
23127
23351
  ].join("");
23128
23352
  }
@@ -23202,17 +23426,17 @@ async function installLaunchAgent(dataDirectory2, source = "codex") {
23202
23426
  throw new Error("Automatic startup is currently supported on macOS only.");
23203
23427
  }
23204
23428
  const file = plistPath(source);
23205
- const launchAgents = path8.dirname(file);
23206
- const logs = path8.join(os3.homedir(), "Library", "Logs");
23207
- await fs11.mkdir(launchAgents, { recursive: true, mode: 493 });
23208
- await fs11.mkdir(logs, { recursive: true, mode: 493 });
23209
- 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(
23210
23434
  file,
23211
23435
  launchAgentPlist(
23212
23436
  void 0,
23213
23437
  await stableNodePath(),
23214
23438
  void 0,
23215
- dataDirectory2 ?? path8.join(os3.homedir(), ".config", "iteration", `${source}-collector`),
23439
+ dataDirectory2 ?? path17.join(os4.homedir(), ".config", "iteration", `${source}-collector`),
23216
23440
  source
23217
23441
  ),
23218
23442
  { encoding: "utf8", mode: 420 }
@@ -23244,7 +23468,7 @@ async function uninstallLaunchAgent(source = "codex") {
23244
23468
  }
23245
23469
  const file = plistPath(source);
23246
23470
  await bootoutIfLoaded(launchctlTarget(), source);
23247
- await fs11.rm(file, { force: true });
23471
+ await fs12.rm(file, { force: true });
23248
23472
  }
23249
23473
  async function launchAgentStatus(source = "codex") {
23250
23474
  if (process.platform !== "darwin") return false;
@@ -23265,21 +23489,21 @@ function launchAgentRunning(output) {
23265
23489
 
23266
23490
  // src/runtime-install.ts
23267
23491
  import { execFile as execFileCallback4 } from "node:child_process";
23268
- import { promises as fs13 } from "node:fs";
23492
+ import { promises as fs14 } from "node:fs";
23269
23493
  import { promisify as promisify4 } from "node:util";
23270
23494
 
23271
23495
  // src/updater.ts
23272
23496
  import { execFile as execFileCallback3 } from "node:child_process";
23273
23497
  import { createHash as createHash5 } from "node:crypto";
23274
- import { createWriteStream as createWriteStream2, promises as fs12 } from "node:fs";
23498
+ import { createWriteStream as createWriteStream2, promises as fs13 } from "node:fs";
23275
23499
  import http from "node:http";
23276
23500
  import https2 from "node:https";
23277
- import path17 from "node:path";
23501
+ import path18 from "node:path";
23278
23502
  import { Transform } from "node:stream";
23279
23503
  import { pipeline as pipeline2 } from "node:stream/promises";
23280
23504
  import { promisify as promisify3 } from "node:util";
23281
23505
  var execFile3 = promisify3(execFileCallback3);
23282
- 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";
23283
23507
  var PACKAGE_NAME = "iteration-cli";
23284
23508
  var MAX_METADATA_BYTES = 16 * 1024 * 1024;
23285
23509
  var MAX_TARBALL_BYTES = 64 * 1024 * 1024;
@@ -23483,32 +23707,32 @@ async function selfCheck(paths, cliPath, version3) {
23483
23707
  }
23484
23708
  async function installCandidate(paths, candidate, signal) {
23485
23709
  await ensureRuntimeDirectory(paths);
23486
- const staged = await fs12.mkdtemp(
23487
- path17.join(paths.staging, `download-${candidate.version}-`)
23710
+ const staged = await fs13.mkdtemp(
23711
+ path18.join(paths.staging, `download-${candidate.version}-`)
23488
23712
  );
23489
23713
  try {
23490
- const archive = path17.join(staged, "package.tgz");
23714
+ const archive = path18.join(staged, "package.tgz");
23491
23715
  await downloadVerified(
23492
23716
  candidate.tarball,
23493
23717
  candidate.integrity,
23494
23718
  archive,
23495
23719
  signal
23496
23720
  );
23497
- const extracted = path17.join(staged, "extracted");
23498
- await fs12.mkdir(extracted, { mode: 448 });
23721
+ const extracted = path18.join(staged, "extracted");
23722
+ await fs13.mkdir(extracted, { mode: 448 });
23499
23723
  await execFile3(TAR, ["-xzf", archive, "-C", extracted], {
23500
23724
  timeout: REQUEST_TIMEOUT_MS,
23501
23725
  windowsHide: true
23502
23726
  });
23503
- const packageRoot = path17.join(extracted, "package");
23727
+ const packageRoot = path18.join(extracted, "package");
23504
23728
  await selfCheck(
23505
23729
  paths,
23506
- path17.join(packageRoot, "dist", "cli.js"),
23730
+ path18.join(packageRoot, "dist", "cli.js"),
23507
23731
  candidate.version
23508
23732
  );
23509
23733
  await installVersionDirectory(paths, packageRoot, candidate.version);
23510
23734
  } finally {
23511
- await fs12.rm(staged, { recursive: true, force: true });
23735
+ await fs13.rm(staged, { recursive: true, force: true });
23512
23736
  }
23513
23737
  }
23514
23738
  async function switchVersion(paths, from, version3, previous, expected, nowMs) {
@@ -23616,7 +23840,7 @@ async function ownVersion() {
23616
23840
  }
23617
23841
  async function exists(filename) {
23618
23842
  try {
23619
- await fs13.access(filename);
23843
+ await fs14.access(filename);
23620
23844
  return true;
23621
23845
  } catch (error3) {
23622
23846
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
@@ -23643,7 +23867,7 @@ async function installLauncher(paths, version3) {
23643
23867
  );
23644
23868
  await commitLauncher(paths, staged);
23645
23869
  } finally {
23646
- await fs13.rm(staged, { force: true });
23870
+ await fs14.rm(staged, { force: true });
23647
23871
  }
23648
23872
  }
23649
23873
  async function ensureRuntimeInstalled(expected, paths = runtimePaths()) {
@@ -23725,9 +23949,9 @@ async function expireRuntimeTrial(nowMs, paths = runtimePaths()) {
23725
23949
 
23726
23950
  // src/systemd-user.ts
23727
23951
  import { execFile as execFileCallback5 } from "node:child_process";
23728
- import { promises as fs14 } from "node:fs";
23729
- import os4 from "node:os";
23730
- 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";
23731
23955
  import { promisify as promisify5 } from "node:util";
23732
23956
  var execFile5 = promisify5(execFileCallback5);
23733
23957
  var SYSTEMCTL_TIMEOUT_MS = 1e4;
@@ -23797,8 +24021,8 @@ function unitName(source) {
23797
24021
  return `iteration-${source}-collector.service`;
23798
24022
  }
23799
24023
  function unitPath(source) {
23800
- return path18.join(
23801
- os4.homedir(),
24024
+ return path19.join(
24025
+ os5.homedir(),
23802
24026
  ".config",
23803
24027
  "systemd",
23804
24028
  "user",
@@ -23806,8 +24030,8 @@ function unitPath(source) {
23806
24030
  );
23807
24031
  }
23808
24032
  function systemdLogPath(source) {
23809
- return path18.join(
23810
- os4.homedir(),
24033
+ return path19.join(
24034
+ os5.homedir(),
23811
24035
  ".config",
23812
24036
  "iteration",
23813
24037
  "logs",
@@ -23819,13 +24043,13 @@ function unitValue(value) {
23819
24043
  throw new Error("Collector paths cannot contain line breaks.");
23820
24044
  return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("%", "%%")}"`;
23821
24045
  }
23822
- function systemdUserUnit(launcherPath = runtimePaths().launcher, nodePath = process.execPath, dataDirectory2 = path18.join(
23823
- os4.homedir(),
24046
+ function systemdUserUnit(launcherPath = runtimePaths().launcher, nodePath = process.execPath, dataDirectory2 = path19.join(
24047
+ os5.homedir(),
23824
24048
  ".config",
23825
24049
  "iteration",
23826
24050
  "codex-collector"
23827
- ), source = "codex", outputPath = path18.join(
23828
- os4.homedir(),
24051
+ ), source = "codex", outputPath = path19.join(
24052
+ os5.homedir(),
23829
24053
  ".config",
23830
24054
  "iteration",
23831
24055
  "logs",
@@ -23855,10 +24079,10 @@ async function systemctl(...args) {
23855
24079
  async function installSystemdUserService(dataDirectory2, source) {
23856
24080
  await ensureSystemdUserLinger();
23857
24081
  const file = unitPath(source);
23858
- const logDirectory = path18.dirname(systemdLogPath(source));
23859
- await fs14.mkdir(path18.dirname(file), { recursive: true, mode: 448 });
23860
- await fs14.mkdir(logDirectory, { recursive: true, mode: 448 });
23861
- 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(
23862
24086
  file,
23863
24087
  systemdUserUnit(void 0, await stableNodePath(), dataDirectory2, source),
23864
24088
  {
@@ -23874,14 +24098,14 @@ async function installSystemdUserService(dataDirectory2, source) {
23874
24098
  async function uninstallSystemdUserService(source) {
23875
24099
  const file = unitPath(source);
23876
24100
  try {
23877
- await fs14.access(file);
24101
+ await fs15.access(file);
23878
24102
  } catch (error3) {
23879
24103
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
23880
24104
  return;
23881
24105
  throw error3;
23882
24106
  }
23883
24107
  await systemctl("disable", "--now", unitName(source));
23884
- await fs14.rm(file);
24108
+ await fs15.rm(file);
23885
24109
  await systemctl("daemon-reload");
23886
24110
  }
23887
24111
  async function systemdUserServiceStatus(source) {
@@ -23900,9 +24124,9 @@ async function systemdUserServiceStatus(source) {
23900
24124
 
23901
24125
  // src/task-scheduler.ts
23902
24126
  import { execFile as execFileCallback6 } from "node:child_process";
23903
- import { promises as fs15 } from "node:fs";
23904
- import os5 from "node:os";
23905
- 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";
23906
24130
  import { setTimeout as delay3 } from "node:timers/promises";
23907
24131
  import { promisify as promisify6 } from "node:util";
23908
24132
 
@@ -24004,14 +24228,14 @@ function scheduledTaskName(source, sid) {
24004
24228
  return `iteration-${sid}-${source}-collector`;
24005
24229
  }
24006
24230
  function servicesDirectory() {
24007
- return path19.join(os5.homedir(), ".config", "iteration", "services");
24231
+ return path20.join(os6.homedir(), ".config", "iteration", "services");
24008
24232
  }
24009
24233
  function taskDefinitionPath(source) {
24010
- return path19.join(servicesDirectory(), `iteration-${source}-collector.xml`);
24234
+ return path20.join(servicesDirectory(), `iteration-${source}-collector.xml`);
24011
24235
  }
24012
24236
  function windowsLogPath(source) {
24013
- return path19.join(
24014
- os5.homedir(),
24237
+ return path20.join(
24238
+ os6.homedir(),
24015
24239
  ".config",
24016
24240
  "iteration",
24017
24241
  "logs",
@@ -24019,8 +24243,8 @@ function windowsLogPath(source) {
24019
24243
  );
24020
24244
  }
24021
24245
  function windowsOutputPath(source) {
24022
- return path19.join(
24023
- os5.homedir(),
24246
+ return path20.join(
24247
+ os6.homedir(),
24024
24248
  ".config",
24025
24249
  "iteration",
24026
24250
  "logs",
@@ -24035,7 +24259,7 @@ function scheduledTaskScript(task) {
24035
24259
  "--data-directory",
24036
24260
  task.dataDirectory
24037
24261
  ].map(windowsArgument).join(" ");
24038
- const errorLog = path19.join(
24262
+ const errorLog = path20.join(
24039
24263
  task.home,
24040
24264
  ".config",
24041
24265
  "iteration",
@@ -24155,7 +24379,7 @@ function AccountSid([string]$account) {
24155
24379
  async function schtasks(source, operation, ...args) {
24156
24380
  try {
24157
24381
  await execFile6(
24158
- path19.join(
24382
+ path20.join(
24159
24383
  process.env.SystemRoot ?? "C:\\Windows",
24160
24384
  "System32",
24161
24385
  "schtasks.exe"
@@ -24199,7 +24423,7 @@ async function desiredTask(dataDirectory2, source) {
24199
24423
  userSid: await currentUserSid(),
24200
24424
  nodePath: await stableNodePath(),
24201
24425
  launcher: runtimePaths().launcher,
24202
- home: os5.homedir()
24426
+ home: os6.homedir()
24203
24427
  };
24204
24428
  }
24205
24429
  async function scheduledTaskUsesRuntime(dataDirectory2, source) {
@@ -24211,10 +24435,10 @@ async function scheduledTaskUsesRuntime(dataDirectory2, source) {
24211
24435
  async function installScheduledTask(dataDirectory2, source) {
24212
24436
  const file = taskDefinitionPath(source);
24213
24437
  await ensurePrivateDirectory(servicesDirectory());
24214
- await ensurePrivateDirectory(path19.dirname(windowsLogPath(source)));
24438
+ await ensurePrivateDirectory(path20.dirname(windowsLogPath(source)));
24215
24439
  await stopTask(source);
24216
24440
  const xml2 = scheduledTaskXml(await desiredTask(dataDirectory2, source));
24217
- await fs15.writeFile(
24441
+ await fs16.writeFile(
24218
24442
  file,
24219
24443
  Buffer.concat([Buffer.from([255, 254]), Buffer.from(xml2, "utf16le")])
24220
24444
  );
@@ -24224,7 +24448,7 @@ async function installScheduledTask(dataDirectory2, source) {
24224
24448
  }
24225
24449
  async function uninstallScheduledTask(source) {
24226
24450
  if (await stopTask(source)) await schtasks(source, "/Delete", "/F");
24227
- await fs15.rm(taskDefinitionPath(source), { force: true });
24451
+ await fs16.rm(taskDefinitionPath(source), { force: true });
24228
24452
  }
24229
24453
  async function scheduledTaskStatus(source) {
24230
24454
  return (await observedTask(source))?.state === 4;
@@ -24248,8 +24472,8 @@ async function prepareBackgroundPersistence() {
24248
24472
  function collectorLogPath(source) {
24249
24473
  if (process.platform === "linux") return systemdLogPath(source);
24250
24474
  if (process.platform === "win32") return windowsLogPath(source);
24251
- return path20.join(
24252
- os6.homedir(),
24475
+ return path21.join(
24476
+ os7.homedir(),
24253
24477
  "Library",
24254
24478
  "Logs",
24255
24479
  `iteration-${source}-collector.error.log`
@@ -24283,7 +24507,7 @@ async function backgroundServiceDefinition(source) {
24283
24507
  requireCollectorPlatform();
24284
24508
  const file = process.platform === "darwin" ? plistPath(source) : process.platform === "linux" ? unitPath(source) : taskDefinitionPath(source);
24285
24509
  try {
24286
- return await fs16.readFile(
24510
+ return await fs17.readFile(
24287
24511
  file,
24288
24512
  process.platform === "win32" ? "utf16le" : "utf8"
24289
24513
  );
@@ -24332,7 +24556,7 @@ function serviceDataDirectory(definition, platform) {
24332
24556
  (_, slashes) => slashes.slice(0, slashes.length / 2)
24333
24557
  );
24334
24558
  }
24335
- if (!directory || !(platform === "win32" ? path20.win32 : path20.posix).isAbsolute(directory))
24559
+ if (!directory || !(platform === "win32" ? path21.win32 : path21.posix).isAbsolute(directory))
24336
24560
  throw new Error("Could not read the collector service data directory.");
24337
24561
  return directory;
24338
24562
  }
@@ -24355,15 +24579,15 @@ async function backgroundServiceUsesRuntime(directory, source) {
24355
24579
  }
24356
24580
 
24357
24581
  // src/config-paths.ts
24358
- import os7 from "node:os";
24359
- import path21 from "node:path";
24582
+ import os8 from "node:os";
24583
+ import path22 from "node:path";
24360
24584
  function defaultCollectorDirectory(source) {
24361
- return path21.join(os7.homedir(), ".config", "iteration", `${source}-collector`);
24585
+ return path22.join(os8.homedir(), ".config", "iteration", `${source}-collector`);
24362
24586
  }
24363
24587
 
24364
24588
  // src/cursor-hooks.ts
24365
- import { promises as fs17 } from "node:fs";
24366
- import path22 from "node:path";
24589
+ import { promises as fs18 } from "node:fs";
24590
+ import path23 from "node:path";
24367
24591
  function shellQuote(value) {
24368
24592
  if (process.platform === "win32") return `"${value}"`;
24369
24593
  return `'${value.replaceAll("'", `'\\''`)}'`;
@@ -24398,8 +24622,8 @@ async function readHooks(filename) {
24398
24622
  let contents;
24399
24623
  let mode = 384;
24400
24624
  try {
24401
- contents = await fs17.readFile(filename, "utf8");
24402
- mode = (await fs17.stat(filename)).mode & 511;
24625
+ contents = await fs18.readFile(filename, "utf8");
24626
+ mode = (await fs18.stat(filename)).mode & 511;
24403
24627
  } catch (error3) {
24404
24628
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
24405
24629
  return { file: {}, mode };
@@ -24421,14 +24645,14 @@ async function readHooks(filename) {
24421
24645
  return { file: checked.data, mode };
24422
24646
  }
24423
24647
  async function writeHooks(filename, file, mode) {
24424
- await fs17.mkdir(path22.dirname(filename), { recursive: true, mode: 448 });
24425
- 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) => {
24426
24650
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
24427
24651
  return filename;
24428
24652
  throw error3;
24429
24653
  });
24430
24654
  const temporary = `${target}.iteration-${process.pid}.tmp`;
24431
- const handle = await fs17.open(temporary, "w", mode);
24655
+ const handle = await fs18.open(temporary, "w", mode);
24432
24656
  try {
24433
24657
  await handle.writeFile(`${JSON.stringify(file, null, 2)}
24434
24658
  `, "utf8");
@@ -24436,14 +24660,14 @@ async function writeHooks(filename, file, mode) {
24436
24660
  } finally {
24437
24661
  await handle.close();
24438
24662
  }
24439
- await fs17.rename(temporary, target);
24663
+ await fs18.rename(temporary, target);
24440
24664
  }
24441
24665
  function entries(value) {
24442
24666
  return value ?? [];
24443
24667
  }
24444
24668
  async function installCursorHooks(cursorHome, dataDirectory2) {
24445
24669
  await ensurePrivateDirectory(cursorHookDirectory(dataDirectory2));
24446
- const filename = path22.join(cursorHome, "hooks.json");
24670
+ const filename = path23.join(cursorHome, "hooks.json");
24447
24671
  const { file, mode } = await readHooks(filename);
24448
24672
  const hooks = { ...file.hooks };
24449
24673
  const nodePath = await stableNodePath();
@@ -24457,7 +24681,7 @@ async function installCursorHooks(cursorHome, dataDirectory2) {
24457
24681
  await writeHooks(filename, { ...file, version: 1, hooks }, mode);
24458
24682
  }
24459
24683
  async function uninstallCursorHooks(cursorHome, dataDirectory2) {
24460
- const filename = path22.join(cursorHome, "hooks.json");
24684
+ const filename = path23.join(cursorHome, "hooks.json");
24461
24685
  const { file, mode } = await readHooks(filename);
24462
24686
  const hooks = { ...file.hooks };
24463
24687
  let changed = false;
@@ -24487,8 +24711,8 @@ function silenceCollectorLogs() {
24487
24711
  // src/turn-summary.ts
24488
24712
  import { execFile as execFileCallback7 } from "node:child_process";
24489
24713
  import { createHash as createHash6 } from "node:crypto";
24490
- import { promises as fs18 } from "node:fs";
24491
- import path23 from "node:path";
24714
+ import { promises as fs19 } from "node:fs";
24715
+ import path24 from "node:path";
24492
24716
  import { promisify as promisify7 } from "node:util";
24493
24717
  import { crc32 } from "node:zlib";
24494
24718
  var execFile7 = promisify7(execFileCallback7);
@@ -24499,13 +24723,13 @@ var REQUEST_TIMEOUT_MS2 = 1e4;
24499
24723
  var HOOK_TIMEOUT_SECONDS = 1;
24500
24724
  var REQUEST_KEY_PATTERN = /^\d{1,10}$/;
24501
24725
  function turnSummaryPaths(dataDirectory2) {
24502
- const directory = path23.join(dataDirectory2, "turn-summary");
24726
+ const directory = path24.join(dataDirectory2, "turn-summary");
24503
24727
  return {
24504
24728
  directory,
24505
- script: path23.join(directory, "stop-hook.sh"),
24506
- requests: path23.join(directory, "requests"),
24507
- lines: path23.join(directory, "lines"),
24508
- 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")
24509
24733
  };
24510
24734
  }
24511
24735
  function shellQuote2(value) {
@@ -24561,7 +24785,7 @@ async function sessionStartCommand(config3) {
24561
24785
  function agentHookFile(config3) {
24562
24786
  if ("cursorHome" in config3)
24563
24787
  throw new Error("Cursor uses its own trace hooks.");
24564
- 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");
24565
24789
  }
24566
24790
  var hookEntrySchema2 = object3({
24567
24791
  command: string5().optional(),
@@ -24578,8 +24802,8 @@ async function readHookFile(filename) {
24578
24802
  let contents;
24579
24803
  let mode = 384;
24580
24804
  try {
24581
- contents = await fs18.readFile(filename, "utf8");
24582
- mode = (await fs18.stat(filename)).mode & 511;
24805
+ contents = await fs19.readFile(filename, "utf8");
24806
+ mode = (await fs19.stat(filename)).mode & 511;
24583
24807
  } catch (error3) {
24584
24808
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT") {
24585
24809
  return { file: {}, mode };
@@ -24605,19 +24829,19 @@ async function readHookFile(filename) {
24605
24829
  return { file: parsed.data, mode };
24606
24830
  }
24607
24831
  async function writeHookFile(filename, file, mode) {
24608
- await fs18.mkdir(path23.dirname(filename), { recursive: true });
24609
- 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) => {
24610
24834
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
24611
24835
  return filename;
24612
24836
  throw error3;
24613
24837
  });
24614
24838
  const temporary = `${target}.iteration-${process.pid}.tmp`;
24615
- await fs18.writeFile(temporary, `${JSON.stringify(file, null, 2)}
24839
+ await fs19.writeFile(temporary, `${JSON.stringify(file, null, 2)}
24616
24840
  `, {
24617
24841
  encoding: "utf8",
24618
24842
  mode
24619
24843
  });
24620
- await fs18.rename(temporary, target);
24844
+ await fs19.rename(temporary, target);
24621
24845
  }
24622
24846
  function withoutCommand(groups, command) {
24623
24847
  return groups.flatMap((group) => {
@@ -24642,24 +24866,24 @@ async function installTurnSummary(config3) {
24642
24866
  [paths.requests, paths.lines, paths.shown].map(ensurePrivateDirectory)
24643
24867
  );
24644
24868
  if (process.platform !== "win32") {
24645
- await fs18.writeFile(paths.script, hookScript(paths), {
24869
+ await fs19.writeFile(paths.script, hookScript(paths), {
24646
24870
  encoding: "utf8",
24647
24871
  mode: 448
24648
24872
  });
24649
- await fs18.chmod(paths.script, 448);
24873
+ await fs19.chmod(paths.script, 448);
24650
24874
  }
24651
24875
  if ("claudeHome" in config3) {
24652
24876
  const gitPaths = claudeGitHookPaths(config3.dataDirectory);
24653
24877
  await ensurePrivateDirectory(gitPaths.queue);
24654
24878
  if (process.platform !== "win32") {
24655
- await fs18.writeFile(
24879
+ await fs19.writeFile(
24656
24880
  gitPaths.script,
24657
24881
  `#!/bin/sh
24658
24882
  exec ${shellQuote2(await stableNodePath())} ${shellQuote2(runtimePaths().launcher)} claude-git-hook ${shellQuote2(config3.dataDirectory)}
24659
24883
  `,
24660
24884
  { encoding: "utf8", mode: 448 }
24661
24885
  );
24662
- await fs18.chmod(gitPaths.script, 448);
24886
+ await fs19.chmod(gitPaths.script, 448);
24663
24887
  }
24664
24888
  }
24665
24889
  const filename = agentHookFile(config3);
@@ -24749,19 +24973,19 @@ async function captureTurnSummary(dataDirectory2) {
24749
24973
  windowsHide: true
24750
24974
  }
24751
24975
  ));
24752
- directory = path23.normalize(directory.trim());
24976
+ directory = path24.normalize(directory.trim());
24753
24977
  } catch {
24754
24978
  quiet();
24755
24979
  return;
24756
24980
  }
24757
- if (!path23.isAbsolute(directory)) {
24981
+ if (!path24.isAbsolute(directory)) {
24758
24982
  quiet();
24759
24983
  return;
24760
24984
  }
24761
24985
  const key = String(crc32(directory));
24762
24986
  const paths = turnSummaryPaths(dataDirectory2);
24763
24987
  try {
24764
- await fs18.writeFile(path23.join(paths.requests, key), `${directory}
24988
+ await fs19.writeFile(path24.join(paths.requests, key), `${directory}
24765
24989
  `, {
24766
24990
  encoding: "utf8",
24767
24991
  mode: 384
@@ -24770,7 +24994,7 @@ async function captureTurnSummary(dataDirectory2) {
24770
24994
  }
24771
24995
  let lines;
24772
24996
  try {
24773
- lines = await fs18.readFile(path23.join(paths.lines, key), "utf8");
24997
+ lines = await fs19.readFile(path24.join(paths.lines, key), "utf8");
24774
24998
  } catch {
24775
24999
  quiet();
24776
25000
  return;
@@ -24782,7 +25006,7 @@ async function captureTurnSummary(dataDirectory2) {
24782
25006
  }
24783
25007
  let last;
24784
25008
  try {
24785
- 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;
24786
25010
  } catch {
24787
25011
  last = null;
24788
25012
  }
@@ -24791,7 +25015,7 @@ async function captureTurnSummary(dataDirectory2) {
24791
25015
  return;
24792
25016
  }
24793
25017
  try {
24794
- await fs18.writeFile(path23.join(paths.shown, key), `${message}
25018
+ await fs19.writeFile(path24.join(paths.shown, key), `${message}
24795
25019
  `, {
24796
25020
  encoding: "utf8",
24797
25021
  mode: 384
@@ -24851,7 +25075,7 @@ async function fetchSummary(config3, directory, fetcher, signal) {
24851
25075
  }
24852
25076
  async function readRequest(filename) {
24853
25077
  try {
24854
- const [directory] = (await fs18.readFile(filename, "utf8")).split("\n");
25078
+ const [directory] = (await fs19.readFile(filename, "utf8")).split("\n");
24855
25079
  return directory ?? null;
24856
25080
  } catch (error3) {
24857
25081
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
@@ -24860,25 +25084,25 @@ async function readRequest(filename) {
24860
25084
  }
24861
25085
  }
24862
25086
  async function writeLine(paths, key, directory, message) {
24863
- const filename = path23.join(paths.lines, key);
25087
+ const filename = path24.join(paths.lines, key);
24864
25088
  if (message === null) {
24865
- await fs18.rm(filename, { force: true });
25089
+ await fs19.rm(filename, { force: true });
24866
25090
  return;
24867
25091
  }
24868
25092
  const temporary = `${filename}.${process.pid}.tmp`;
24869
- await fs18.writeFile(
25093
+ await fs19.writeFile(
24870
25094
  temporary,
24871
25095
  `${directory}
24872
25096
  ${JSON.stringify({ systemMessage: message })}
24873
25097
  `,
24874
25098
  { encoding: "utf8", mode: 384 }
24875
25099
  );
24876
- await fs18.rename(temporary, filename);
25100
+ await fs19.rename(temporary, filename);
24877
25101
  }
24878
25102
  async function forget(paths, key) {
24879
25103
  await Promise.all(
24880
25104
  [paths.requests, paths.lines, paths.shown].map(
24881
- (directory) => fs18.rm(path23.join(directory, key), { force: true })
25105
+ (directory) => fs19.rm(path24.join(directory, key), { force: true })
24882
25106
  )
24883
25107
  );
24884
25108
  }
@@ -24886,7 +25110,7 @@ async function refreshTurnSummaries(config3, refreshedAt, nowMs, fetcher = colle
24886
25110
  const paths = turnSummaryPaths(config3.dataDirectory);
24887
25111
  let keys;
24888
25112
  try {
24889
- keys = await fs18.readdir(paths.requests);
25113
+ keys = await fs19.readdir(paths.requests);
24890
25114
  } catch (error3) {
24891
25115
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
24892
25116
  return 0;
@@ -24896,8 +25120,8 @@ async function refreshTurnSummaries(config3, refreshedAt, nowMs, fetcher = colle
24896
25120
  for (const key of keys.sort()) {
24897
25121
  signal?.throwIfAborted();
24898
25122
  if (!REQUEST_KEY_PATTERN.test(key)) continue;
24899
- const filename = path23.join(paths.requests, key);
24900
- 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) => {
24901
25125
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
24902
25126
  return null;
24903
25127
  throw error3;
@@ -24964,8 +25188,8 @@ async function codexTurnSummaryApproval(config3) {
24964
25188
  if (handlerIndex < 0) continue;
24965
25189
  let configToml;
24966
25190
  try {
24967
- configToml = await fs18.readFile(
24968
- path23.join(config3.codexHome, "config.toml"),
25191
+ configToml = await fs19.readFile(
25192
+ path24.join(config3.codexHome, "config.toml"),
24969
25193
  "utf8"
24970
25194
  );
24971
25195
  } catch (error3) {
@@ -24992,25 +25216,25 @@ var consentSchema = object3({
24992
25216
  sourceHomes: object3({
24993
25217
  codex: string5(),
24994
25218
  claude: string5(),
24995
- cursor: string5().default(path24.join(os8.homedir(), ".cursor"))
25219
+ cursor: string5().default(path25.join(os9.homedir(), ".cursor"))
24996
25220
  }).strict()
24997
25221
  }).strict();
24998
25222
  function consentPath() {
24999
- return path24.join(os8.homedir(), ".config", "iteration", "auto-connect.json");
25223
+ return path25.join(os9.homedir(), ".config", "iteration", "auto-connect.json");
25000
25224
  }
25001
25225
  async function removeMachineConsent() {
25002
- await fs19.rm(consentPath(), { force: true });
25226
+ await fs20.rm(consentPath(), { force: true });
25003
25227
  }
25004
25228
  async function detectedSources(sourceHomes) {
25005
25229
  const detected = [];
25006
25230
  for (const source of COLLECTOR_SOURCES2) {
25007
- const directory = path24.join(
25231
+ const directory = path25.join(
25008
25232
  sourceHomes[source],
25009
25233
  source === "codex" ? "sessions" : "projects"
25010
25234
  );
25011
25235
  let stats;
25012
25236
  try {
25013
- stats = await fs19.stat(directory);
25237
+ stats = await fs20.stat(directory);
25014
25238
  } catch (error3) {
25015
25239
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
25016
25240
  continue;
@@ -25030,7 +25254,7 @@ async function detectedSources(sourceHomes) {
25030
25254
  async function loadMachineConsent() {
25031
25255
  let text;
25032
25256
  try {
25033
- text = await fs19.readFile(consentPath(), "utf8");
25257
+ text = await fs20.readFile(consentPath(), "utf8");
25034
25258
  } catch (error3) {
25035
25259
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
25036
25260
  return null;
@@ -25056,9 +25280,9 @@ async function saveMachineConsent(consent) {
25056
25280
  const parsed = consentSchema.parse(consent);
25057
25281
  validateCollectorEndpoint(parsed.endpoint);
25058
25282
  const filename = consentPath();
25059
- await ensurePrivateDirectory(path24.dirname(filename));
25283
+ await ensurePrivateDirectory(path25.dirname(filename));
25060
25284
  const temporary = `${filename}.${process.pid}.tmp`;
25061
- const handle = await fs19.open(temporary, "w", 384);
25285
+ const handle = await fs20.open(temporary, "w", 384);
25062
25286
  try {
25063
25287
  await handle.writeFile(`${JSON.stringify(parsed)}
25064
25288
  `, "utf8");
@@ -25066,7 +25290,7 @@ async function saveMachineConsent(consent) {
25066
25290
  } finally {
25067
25291
  await handle.close();
25068
25292
  }
25069
- await fs19.rename(temporary, filename);
25293
+ await fs20.rename(temporary, filename);
25070
25294
  }
25071
25295
  async function readCollectorScope(anchor3) {
25072
25296
  const endpoint = validateCollectorEndpoint(anchor3.endpoint);
@@ -25162,7 +25386,7 @@ async function connectNewDetectedSources(anchor3, consent) {
25162
25386
  if (source === consent.anchorSource) continue;
25163
25387
  const paths = collectorPaths(defaultCollectorDirectory(source));
25164
25388
  try {
25165
- await fs19.access(paths.config);
25389
+ await fs20.access(paths.config);
25166
25390
  continue;
25167
25391
  } catch (error3) {
25168
25392
  if (!(error3 instanceof Error && "code" in error3 && error3.code === "ENOENT"))
@@ -25203,7 +25427,7 @@ async function connectNewDetectedSources(anchor3, consent) {
25203
25427
  async function connectedConfig(source) {
25204
25428
  const paths = collectorPaths(defaultCollectorDirectory(source));
25205
25429
  try {
25206
- await fs19.access(paths.config);
25430
+ await fs20.access(paths.config);
25207
25431
  } catch (error3) {
25208
25432
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
25209
25433
  return null;
@@ -25697,7 +25921,7 @@ async function authenticateAndEnrollCollector(endpoint, deviceNameValue, organiz
25697
25921
  }
25698
25922
 
25699
25923
  // src/history-import.ts
25700
- import path25 from "node:path";
25924
+ import path26 from "node:path";
25701
25925
  function backgroundControl(source) {
25702
25926
  return {
25703
25927
  isRunning: () => backgroundServiceStatus(source),
@@ -25710,7 +25934,7 @@ async function importSourceHistory(config3, paths, confirmed, control = backgrou
25710
25934
  const source = collectorSource(config3);
25711
25935
  const directory = collectorSourceDirectory(config3);
25712
25936
  const state = await loadState(paths);
25713
- if (!state.initialized || state.sourceDirectory !== path25.resolve(directory)) {
25937
+ if (!state.initialized || state.sourceDirectory !== path26.resolve(directory)) {
25714
25938
  throw new Error(
25715
25939
  `Connect the ${source} source before importing its history.`
25716
25940
  );
@@ -25759,36 +25983,36 @@ async function importSourceHistory(config3, paths, confirmed, control = backgrou
25759
25983
 
25760
25984
  // src/logout.ts
25761
25985
  import { randomUUID as randomUUID3 } from "node:crypto";
25762
- import { promises as fs20 } from "node:fs";
25763
- import os9 from "node:os";
25764
- 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";
25765
25989
  async function pendingDirectories(filename) {
25766
25990
  let contents;
25767
25991
  try {
25768
- contents = await fs20.readFile(filename, "utf8");
25992
+ contents = await fs21.readFile(filename, "utf8");
25769
25993
  } catch (error3) {
25770
25994
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
25771
25995
  return [];
25772
25996
  throw error3;
25773
25997
  }
25774
- 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));
25775
25999
  if (!parsed.success)
25776
26000
  throw new Error("Pending logout directories are invalid.");
25777
26001
  return parsed.data;
25778
26002
  }
25779
26003
  async function savePendingDirectories(filename, directories) {
25780
- await ensurePrivateDirectory(path26.dirname(filename));
26004
+ await ensurePrivateDirectory(path27.dirname(filename));
25781
26005
  const temporary = `${filename}.${process.pid}.tmp`;
25782
- await fs20.writeFile(temporary, JSON.stringify([...directories]), {
26006
+ await fs21.writeFile(temporary, JSON.stringify([...directories]), {
25783
26007
  mode: 384
25784
26008
  });
25785
- await fs20.rename(temporary, filename);
26009
+ await fs21.rename(temporary, filename);
25786
26010
  }
25787
26011
  async function clearConnection(directory) {
25788
26012
  const paths = collectorPaths(directory);
25789
26013
  let names;
25790
26014
  try {
25791
- names = await fs20.readdir(directory);
26015
+ names = await fs21.readdir(directory);
25792
26016
  } catch (error3) {
25793
26017
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
25794
26018
  return;
@@ -25798,14 +26022,15 @@ async function clearConnection(directory) {
25798
26022
  try {
25799
26023
  for (const name of names)
25800
26024
  if (/^config\.json\.\d+\.tmp$/.test(name))
25801
- await fs20.rm(path26.join(directory, name), { force: true });
25802
- 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;
25803
26027
  const config3 = await loadConfig(paths);
25804
26028
  if (config3.destination === "vault")
25805
26029
  throw new Error("This directory belongs to Trace Vault, not Iteration.");
25806
26030
  if ("cursorHome" in config3)
25807
26031
  await uninstallCursorHooks(config3.cursorHome, config3.dataDirectory);
25808
26032
  else await uninstallTurnSummary(config3);
26033
+ await uninstallAgentTools(agentHomes(config3));
25809
26034
  const retained = [
25810
26035
  paths.state,
25811
26036
  paths.outbox,
@@ -25820,27 +26045,27 @@ async function clearConnection(directory) {
25820
26045
  "turn-summary",
25821
26046
  "codex-billing-sync.json",
25822
26047
  "cursor-billing-sync.json"
25823
- ].map((name) => path26.join(directory, name))
26048
+ ].map((name) => path27.join(directory, name))
25824
26049
  ];
25825
- const archive = path26.join(directory, "logged-out", randomUUID3());
26050
+ const archive = path27.join(directory, "logged-out", randomUUID3());
25826
26051
  await ensurePrivateDirectory(archive);
25827
26052
  for (const filename of retained) {
25828
26053
  try {
25829
- await fs20.rename(filename, path26.join(archive, path26.basename(filename)));
26054
+ await fs21.rename(filename, path27.join(archive, path27.basename(filename)));
25830
26055
  } catch (error3) {
25831
26056
  if (!(error3 instanceof Error && "code" in error3 && error3.code === "ENOENT"))
25832
26057
  throw error3;
25833
26058
  }
25834
26059
  }
25835
- await fs20.rm(paths.config);
26060
+ await fs21.rm(paths.config);
25836
26061
  } finally {
25837
26062
  await release();
25838
26063
  }
25839
26064
  }
25840
26065
  async function logout(extraDirectory) {
25841
26066
  requireCollectorPlatform();
25842
- const pending = path26.join(
25843
- os9.homedir(),
26067
+ const pending = path27.join(
26068
+ os10.homedir(),
25844
26069
  ".config",
25845
26070
  "iteration",
25846
26071
  "logout-pending.json"
@@ -25850,7 +26075,7 @@ async function logout(extraDirectory) {
25850
26075
  ...await pendingDirectories(pending)
25851
26076
  ]);
25852
26077
  if (extraDirectory !== void 0)
25853
- directories.add(path26.resolve(extraDirectory));
26078
+ directories.add(path27.resolve(extraDirectory));
25854
26079
  const failures = [];
25855
26080
  try {
25856
26081
  await removeMachineConsent();
@@ -25895,7 +26120,7 @@ async function logout(extraDirectory) {
25895
26120
  `Logout did not finish. Resolve these errors and run iteration logout again:
25896
26121
  ${failures.join("\n")}`
25897
26122
  );
25898
- await fs20.rm(pending);
26123
+ await fs21.rm(pending);
25899
26124
  }
25900
26125
 
25901
26126
  // src/runtime-services.ts
@@ -25926,8 +26151,8 @@ import { setTimeout as delay5 } from "node:timers/promises";
25926
26151
 
25927
26152
  // src/codex-billing.ts
25928
26153
  import { createHash as createHash7 } from "node:crypto";
25929
- import { promises as fs21 } from "node:fs";
25930
- import path27 from "node:path";
26154
+ import { promises as fs22 } from "node:fs";
26155
+ import path28 from "node:path";
25931
26156
  var TASK_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage/thread_usage/query_v2";
25932
26157
  var UUID2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
25933
26158
  var DAY_MS = 24 * 60 * 60 * 1e3;
@@ -25979,7 +26204,7 @@ function fingerprint(value) {
25979
26204
  async function readAuth(codexHome) {
25980
26205
  let contents;
25981
26206
  try {
25982
- contents = await fs21.readFile(path27.join(codexHome, "auth.json"), "utf8");
26207
+ contents = await fs22.readFile(path28.join(codexHome, "auth.json"), "utf8");
25983
26208
  } catch (error3) {
25984
26209
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
25985
26210
  return {
@@ -26014,7 +26239,7 @@ async function readAuth(codexHome) {
26014
26239
  async function readState(filename) {
26015
26240
  let contents;
26016
26241
  try {
26017
- contents = await fs21.readFile(filename, "utf8");
26242
+ contents = await fs22.readFile(filename, "utf8");
26018
26243
  } catch (error3) {
26019
26244
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
26020
26245
  return null;
@@ -26036,7 +26261,7 @@ async function readState(filename) {
26036
26261
  }
26037
26262
  async function saveState2(filename, state) {
26038
26263
  const temporary = `${filename}.${process.pid}.tmp`;
26039
- const handle = await fs21.open(temporary, "w", 384);
26264
+ const handle = await fs22.open(temporary, "w", 384);
26040
26265
  try {
26041
26266
  await handle.writeFile(`${JSON.stringify(state)}
26042
26267
  `);
@@ -26044,11 +26269,11 @@ async function saveState2(filename, state) {
26044
26269
  } finally {
26045
26270
  await handle.close();
26046
26271
  }
26047
- await fs21.rename(temporary, filename);
26272
+ await fs22.rename(temporary, filename);
26048
26273
  }
26049
26274
  async function localThread(filename) {
26050
- const stat = await fs21.stat(filename);
26051
- const handle = await fs21.open(filename, "r");
26275
+ const stat = await fs22.stat(filename);
26276
+ const handle = await fs22.open(filename, "r");
26052
26277
  let header = null;
26053
26278
  try {
26054
26279
  const buffer = Buffer.alloc(64 * 1024);
@@ -26148,9 +26373,9 @@ function parseCodexAuthLog(threadId, body) {
26148
26373
  return { threadId, turnId: turn[1], mode };
26149
26374
  }
26150
26375
  async function localAuthTurns(codexHome, modelProviders) {
26151
- const filename = path27.join(codexHome, "logs_2.sqlite");
26376
+ const filename = path28.join(codexHome, "logs_2.sqlite");
26152
26377
  try {
26153
- await fs21.access(filename);
26378
+ await fs22.access(filename);
26154
26379
  } catch (error3) {
26155
26380
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
26156
26381
  return [];
@@ -26243,7 +26468,7 @@ async function queryTaskUsage(tasks, accessToken, accountId, signal) {
26243
26468
  }
26244
26469
  async function syncCodexBilling(config3, fetcher = collectorFetch, signal) {
26245
26470
  const auth = await readAuth(config3.codexHome);
26246
- const stateFile = path27.join(config3.dataDirectory, "codex-billing-sync.json");
26471
+ const stateFile = path28.join(config3.dataDirectory, "codex-billing-sync.json");
26247
26472
  const prior = await readState(stateFile);
26248
26473
  const nowMs = Date.now();
26249
26474
  const history = prior === null || prior.accountFingerprint !== auth.accountFingerprint;
@@ -26414,9 +26639,9 @@ async function syncCodexBilling(config3, fetcher = collectorFetch, signal) {
26414
26639
 
26415
26640
  // src/cursor-billing.ts
26416
26641
  import { createHash as createHash8 } from "node:crypto";
26417
- import { promises as fs22 } from "node:fs";
26418
- import os10 from "node:os";
26419
- 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";
26420
26645
  var CURSOR_USAGE_URL = "https://api2.cursor.sh/aiserver.v1.DashboardService/GetFilteredUsageEvents";
26421
26646
  var PAGE_SIZE = 500;
26422
26647
  var MAX_PAGES = 1e3;
@@ -26447,18 +26672,18 @@ var usagePageSchema = object3({
26447
26672
  var emptyUsagePageSchema = object3({}).strict();
26448
26673
  function cursorAuthDatabase() {
26449
26674
  if (process.platform === "darwin")
26450
- return path28.join(
26451
- os10.homedir(),
26675
+ return path29.join(
26676
+ os11.homedir(),
26452
26677
  "Library/Application Support/Cursor/User/globalStorage/state.vscdb"
26453
26678
  );
26454
26679
  if (process.platform === "linux")
26455
- return path28.join(
26456
- os10.homedir(),
26680
+ return path29.join(
26681
+ os11.homedir(),
26457
26682
  ".config/Cursor/User/globalStorage/state.vscdb"
26458
26683
  );
26459
26684
  if (process.platform === "win32")
26460
- return path28.join(
26461
- process.env.APPDATA ?? path28.join(os10.homedir(), "AppData", "Roaming"),
26685
+ return path29.join(
26686
+ process.env.APPDATA ?? path29.join(os11.homedir(), "AppData", "Roaming"),
26462
26687
  "Cursor/User/globalStorage/state.vscdb"
26463
26688
  );
26464
26689
  return null;
@@ -26500,7 +26725,7 @@ async function readCursorAuth() {
26500
26725
  async function loadBillingState(filename) {
26501
26726
  let contents;
26502
26727
  try {
26503
- contents = await fs22.readFile(filename, "utf8");
26728
+ contents = await fs23.readFile(filename, "utf8");
26504
26729
  } catch (error3) {
26505
26730
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
26506
26731
  return null;
@@ -26516,7 +26741,7 @@ async function loadBillingState(filename) {
26516
26741
  }
26517
26742
  async function saveBillingState(filename, state) {
26518
26743
  const temporary = `${filename}.${process.pid}.tmp`;
26519
- const handle = await fs22.open(temporary, "w", 384);
26744
+ const handle = await fs23.open(temporary, "w", 384);
26520
26745
  try {
26521
26746
  await handle.writeFile(`${JSON.stringify(state)}
26522
26747
  `, "utf8");
@@ -26524,7 +26749,7 @@ async function saveBillingState(filename, state) {
26524
26749
  } finally {
26525
26750
  await handle.close();
26526
26751
  }
26527
- await fs22.rename(temporary, filename);
26752
+ await fs23.rename(temporary, filename);
26528
26753
  }
26529
26754
  function planCursorBillingSync(state, accountFingerprint, nowMs) {
26530
26755
  if (state === null || state.accountFingerprint !== accountFingerprint)
@@ -26546,7 +26771,7 @@ function planCursorBillingSync(state, accountFingerprint, nowMs) {
26546
26771
  }
26547
26772
  function cursorConversationIds(filenames) {
26548
26773
  return new Set(
26549
- 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))
26550
26775
  );
26551
26776
  }
26552
26777
  function billingKind(kind) {
@@ -26604,11 +26829,11 @@ async function syncCursorBilling(config3, fetcher = collectorFetch, signal) {
26604
26829
  if (auth === null)
26605
26830
  return { sessions: 0, matched: 0, skipped: true, mode: null };
26606
26831
  const localConversations = cursorConversationIds(
26607
- await cursorFiles(path28.join(config3.cursorHome, "projects"))
26832
+ await cursorFiles(path29.join(config3.cursorHome, "projects"))
26608
26833
  );
26609
26834
  if (localConversations.size === 0)
26610
26835
  return { sessions: 0, matched: 0, skipped: true, mode: null };
26611
- const stateFile = path28.join(config3.dataDirectory, "cursor-billing-sync.json");
26836
+ const stateFile = path29.join(config3.dataDirectory, "cursor-billing-sync.json");
26612
26837
  const priorState = await loadBillingState(stateFile);
26613
26838
  const nowMs = Date.now();
26614
26839
  const plan = planCursorBillingSync(
@@ -27534,26 +27759,26 @@ function renderRuntime(terminal2, summary) {
27534
27759
 
27535
27760
  // src/windows-supervisor.ts
27536
27761
  import { spawn } from "node:child_process";
27537
- import { promises as fs23 } from "node:fs";
27538
- import path29 from "node:path";
27762
+ import { promises as fs24 } from "node:fs";
27763
+ import path30 from "node:path";
27539
27764
  import { setTimeout as delay6 } from "node:timers/promises";
27540
27765
  var MAX_LOG_BYTES = 8 * 1024 * 1024;
27541
27766
  var RESTART_DELAY_MS = 5e3;
27542
27767
  async function appendLog(filename, bytes) {
27543
27768
  let offset = 0;
27544
27769
  while (offset < bytes.length) {
27545
- 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) => {
27546
27771
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
27547
27772
  return 0;
27548
27773
  throw error3;
27549
27774
  });
27550
27775
  if (size >= MAX_LOG_BYTES) {
27551
- await fs23.rm(`${filename}.1`, { force: true });
27552
- await fs23.rename(filename, `${filename}.1`);
27776
+ await fs24.rm(`${filename}.1`, { force: true });
27777
+ await fs24.rename(filename, `${filename}.1`);
27553
27778
  continue;
27554
27779
  }
27555
27780
  const count = Math.min(bytes.length - offset, MAX_LOG_BYTES - size);
27556
- await fs23.appendFile(filename, bytes.subarray(offset, offset + count), {
27781
+ await fs24.appendFile(filename, bytes.subarray(offset, offset + count), {
27557
27782
  mode: 384
27558
27783
  });
27559
27784
  offset += count;
@@ -27569,7 +27794,7 @@ async function copyOutput(stream, filename) {
27569
27794
  async function superviseCollector(source, dataDirectory2) {
27570
27795
  const output = windowsOutputPath(source);
27571
27796
  const errors = windowsLogPath(source);
27572
- await ensurePrivateDirectory(path29.dirname(output));
27797
+ await ensurePrivateDirectory(path30.dirname(output));
27573
27798
  const controller = new AbortController();
27574
27799
  const stop = () => controller.abort();
27575
27800
  process.once("SIGTERM", stop);
@@ -27781,7 +28006,7 @@ function usage(command) {
27781
28006
  }
27782
28007
  async function fileExists(filename) {
27783
28008
  try {
27784
- await fs24.access(filename);
28009
+ await fs25.access(filename);
27785
28010
  return true;
27786
28011
  } catch (error3) {
27787
28012
  if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT") {
@@ -27822,7 +28047,7 @@ async function defaultCommand() {
27822
28047
  await connectAll({});
27823
28048
  }
27824
28049
  function dataDirectory(source, values) {
27825
- return path30.resolve(
28050
+ return path31.resolve(
27826
28051
  optionalString(values, "data-directory") ?? defaultCollectorDirectory(source)
27827
28052
  );
27828
28053
  }
@@ -27902,12 +28127,12 @@ async function initialize(source, values) {
27902
28127
  requireCollectorPlatform();
27903
28128
  const endpoint = optionalString(values, "endpoint") ?? DEFAULT_INGEST_ENDPOINT;
27904
28129
  const endpointUrl = validateCollectorEndpoint(endpoint);
27905
- const deviceName = optionalString(values, "device-name") ?? os11.hostname();
28130
+ const deviceName = optionalString(values, "device-name") ?? os12.hostname();
27906
28131
  const organizationId = optionalString(values, "workspace");
27907
28132
  const directory = dataDirectory(source, values);
27908
28133
  const paths = collectorPaths(directory);
27909
- const selectedHome = path30.resolve(
27910
- 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")
27911
28136
  );
27912
28137
  await assertFreshCollectorDirectory(paths, source);
27913
28138
  const preparedState = await prepareSourceInitialState(
@@ -27986,7 +28211,7 @@ async function approveAllSources(sources, workspaceName, yes) {
27986
28211
  "",
27987
28212
  wrap(
27988
28213
  terminal,
27989
- `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." : ""}`
27990
28215
  ),
27991
28216
  ""
27992
28217
  );
@@ -28015,13 +28240,13 @@ async function connectAll(values) {
28015
28240
  }
28016
28241
  const configs = await Promise.all(COLLECTOR_SOURCES2.map(connectedConfig));
28017
28242
  const resolveSourceHomes = () => ({
28018
- codex: path30.resolve(
28243
+ codex: path31.resolve(
28019
28244
  optionalString(values, "codex-home") ?? (configs[0] && "codexHome" in configs[0] ? configs[0].codexHome : sourceHome("codex"))
28020
28245
  ),
28021
- claude: path30.resolve(
28246
+ claude: path31.resolve(
28022
28247
  optionalString(values, "claude-home") ?? (configs[1] && "claudeHome" in configs[1] ? configs[1].claudeHome : sourceHome("claude"))
28023
28248
  ),
28024
- cursor: path30.resolve(
28249
+ cursor: path31.resolve(
28025
28250
  optionalString(values, "cursor-home") ?? (configs[2] && "cursorHome" in configs[2] ? configs[2].cursorHome : sourceHome("cursor"))
28026
28251
  )
28027
28252
  });
@@ -28174,8 +28399,33 @@ async function connectAll(values) {
28174
28399
  )
28175
28400
  );
28176
28401
  }
28402
+ await addAgentTools(
28403
+ results.map((result) => result.source),
28404
+ sourceHomes
28405
+ );
28177
28406
  print(...connectionSummary2(results, results.length > existing.length));
28178
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
+ }
28179
28429
  async function importHistory(source, values) {
28180
28430
  requireCollectorPlatform();
28181
28431
  const directory = dataDirectory(source, values);
@@ -28505,6 +28755,12 @@ async function runInternalCommand() {
28505
28755
  }
28506
28756
  return true;
28507
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
+ }
28508
28764
  if (process.argv[2] === "turn-summary-hook") {
28509
28765
  const directory = process.argv[3];
28510
28766
  if (!directory || process.argv.length !== 4)
@@ -28606,6 +28862,7 @@ async function disconnect(selectedSource, values) {
28606
28862
  if ("cursorHome" in config3)
28607
28863
  await uninstallCursorHooks(config3.cursorHome, config3.dataDirectory);
28608
28864
  else await uninstallTurnSummary(config3);
28865
+ await uninstallAgentTools(agentHomes(config3));
28609
28866
  }
28610
28867
  print(
28611
28868
  mark(