iteration-cli 0.11.1 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -934,8 +934,8 @@ var require_src = __commonJS({
934
934
  // src/cli.ts
935
935
  import { spawnSync } from "node:child_process";
936
936
  import os8 from "node:os";
937
- import path20 from "node:path";
938
- import { promises as fs16 } from "node:fs";
937
+ import path21 from "node:path";
938
+ import { promises as fs17 } from "node:fs";
939
939
  import { createInterface as createInterface3 } from "node:readline/promises";
940
940
  import { parseArgs } from "node:util";
941
941
 
@@ -1175,10 +1175,10 @@ function mergeDefs(...defs) {
1175
1175
  function cloneDef(schema) {
1176
1176
  return mergeDefs(schema._zod.def);
1177
1177
  }
1178
- function getElementAtPath(obj, path21) {
1179
- if (!path21)
1178
+ function getElementAtPath(obj, path22) {
1179
+ if (!path22)
1180
1180
  return obj;
1181
- return path21.reduce((acc, key) => acc?.[key], obj);
1181
+ return path22.reduce((acc, key) => acc?.[key], obj);
1182
1182
  }
1183
1183
  function promiseAllObject(promisesObj) {
1184
1184
  const keys = Object.keys(promisesObj);
@@ -1518,11 +1518,11 @@ function explicitlyAborted(x, startIndex = 0) {
1518
1518
  }
1519
1519
  return false;
1520
1520
  }
1521
- function prefixIssues(path21, issues) {
1521
+ function prefixIssues(path22, issues) {
1522
1522
  return issues.map((iss) => {
1523
1523
  var _a3;
1524
1524
  (_a3 = iss).path ?? (_a3.path = []);
1525
- iss.path.unshift(path21);
1525
+ iss.path.unshift(path22);
1526
1526
  return iss;
1527
1527
  });
1528
1528
  }
@@ -1968,16 +1968,16 @@ function flattenError(error2, mapper = (issue2) => issue2.message) {
1968
1968
  }
1969
1969
  function formatError(error2, mapper = (issue2) => issue2.message) {
1970
1970
  const fieldErrors = { _errors: [] };
1971
- const processError = (error3, path21 = []) => {
1971
+ const processError = (error3, path22 = []) => {
1972
1972
  for (const issue2 of error3.issues) {
1973
1973
  if (issue2.code === "invalid_union" && issue2.errors.length) {
1974
- issue2.errors.map((issues) => processError({ issues }, [...path21, ...issue2.path]));
1974
+ issue2.errors.map((issues) => processError({ issues }, [...path22, ...issue2.path]));
1975
1975
  } else if (issue2.code === "invalid_key") {
1976
- processError({ issues: issue2.issues }, [...path21, ...issue2.path]);
1976
+ processError({ issues: issue2.issues }, [...path22, ...issue2.path]);
1977
1977
  } else if (issue2.code === "invalid_element") {
1978
- processError({ issues: issue2.issues }, [...path21, ...issue2.path]);
1978
+ processError({ issues: issue2.issues }, [...path22, ...issue2.path]);
1979
1979
  } else {
1980
- const fullpath = [...path21, ...issue2.path];
1980
+ const fullpath = [...path22, ...issue2.path];
1981
1981
  if (fullpath.length === 0) {
1982
1982
  fieldErrors._errors.push(mapper(issue2));
1983
1983
  } else {
@@ -7557,6 +7557,34 @@ var cursorBillingRequestSchema = object({
7557
7557
  paidUsd: number2().finite().nonnegative().nullable()
7558
7558
  }).strict()).max(5e3)
7559
7559
  }).strict();
7560
+ function codexCreditSign(value) {
7561
+ const mantissa = value.split(/[eE]/, 1)[0];
7562
+ if (!/[1-9]/.test(mantissa))
7563
+ return 0;
7564
+ return mantissa.startsWith("-") ? -1 : 1;
7565
+ }
7566
+ var codexBillingRequestSchema = object({
7567
+ accountFingerprint: string2().regex(/^[0-9a-f]{64}$/),
7568
+ reconcile: boolean2(),
7569
+ tasks: array(object({
7570
+ threadId: uuid2(),
7571
+ createdAt: iso_exports.datetime().nullable(),
7572
+ descendantThreadIds: array(uuid2()).max(900),
7573
+ dataStatus: _enum([
7574
+ "available",
7575
+ "partial",
7576
+ "unavailable",
7577
+ "not_queried"
7578
+ ]),
7579
+ usageSource: string2().max(100),
7580
+ balanceUsageCredits: string2().regex(/^[+-]?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d{1,3})?$/).max(128).nullable()
7581
+ }).strict()).max(100),
7582
+ authTurns: array(object({
7583
+ threadId: uuid2(),
7584
+ turnId: uuid2(),
7585
+ mode: _enum(["chatgpt", "api_key", "conflicting"])
7586
+ }).strict()).max(5e3)
7587
+ }).strict();
7560
7588
  var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
7561
7589
  var TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
7562
7590
  var unpairedSurrogate = /[\uD800-\uDFFF]/u;
@@ -11278,11 +11306,11 @@ function requireLodash_sortby() {
11278
11306
  function baseForOwn(object3, iteratee) {
11279
11307
  return object3 && baseFor(object3, iteratee, keys);
11280
11308
  }
11281
- function baseGet(object3, path21) {
11282
- path21 = isKey(path21, object3) ? [path21] : castPath(path21);
11283
- var index = 0, length = path21.length;
11309
+ function baseGet(object3, path22) {
11310
+ path22 = isKey(path22, object3) ? [path22] : castPath(path22);
11311
+ var index = 0, length = path22.length;
11284
11312
  while (object3 != null && index < length) {
11285
- object3 = object3[toKey(path21[index++])];
11313
+ object3 = object3[toKey(path22[index++])];
11286
11314
  }
11287
11315
  return index && index == length ? object3 : void 0;
11288
11316
  }
@@ -11409,13 +11437,13 @@ function requireLodash_sortby() {
11409
11437
  return object3 === source || baseIsMatch(object3, source, matchData);
11410
11438
  };
11411
11439
  }
11412
- function baseMatchesProperty(path21, srcValue) {
11413
- if (isKey(path21) && isStrictComparable(srcValue)) {
11414
- return matchesStrictComparable(toKey(path21), srcValue);
11440
+ function baseMatchesProperty(path22, srcValue) {
11441
+ if (isKey(path22) && isStrictComparable(srcValue)) {
11442
+ return matchesStrictComparable(toKey(path22), srcValue);
11415
11443
  }
11416
11444
  return function(object3) {
11417
- var objValue = get(object3, path21);
11418
- return objValue === void 0 && objValue === srcValue ? hasIn(object3, path21) : baseIsEqual(srcValue, objValue, void 0, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG);
11445
+ var objValue = get(object3, path22);
11446
+ return objValue === void 0 && objValue === srcValue ? hasIn(object3, path22) : baseIsEqual(srcValue, objValue, void 0, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG);
11419
11447
  };
11420
11448
  }
11421
11449
  function baseOrderBy(collection, iteratees, orders) {
@@ -11431,9 +11459,9 @@ function requireLodash_sortby() {
11431
11459
  return compareMultiple(object3, other, orders);
11432
11460
  });
11433
11461
  }
11434
- function basePropertyDeep(path21) {
11462
+ function basePropertyDeep(path22) {
11435
11463
  return function(object3) {
11436
- return baseGet(object3, path21);
11464
+ return baseGet(object3, path22);
11437
11465
  };
11438
11466
  }
11439
11467
  function baseRest(func, start2) {
@@ -11688,11 +11716,11 @@ function requireLodash_sortby() {
11688
11716
  return result;
11689
11717
  };
11690
11718
  }
11691
- function hasPath(object3, path21, hasFunc) {
11692
- path21 = isKey(path21, object3) ? [path21] : castPath(path21);
11693
- var result, index = -1, length = path21.length;
11719
+ function hasPath(object3, path22, hasFunc) {
11720
+ path22 = isKey(path22, object3) ? [path22] : castPath(path22);
11721
+ var result, index = -1, length = path22.length;
11694
11722
  while (++index < length) {
11695
- var key = toKey(path21[index]);
11723
+ var key = toKey(path22[index]);
11696
11724
  if (!(result = object3 != null && hasFunc(object3, key))) {
11697
11725
  break;
11698
11726
  }
@@ -11847,12 +11875,12 @@ function requireLodash_sortby() {
11847
11875
  function toString(value) {
11848
11876
  return value == null ? "" : baseToString(value);
11849
11877
  }
11850
- function get(object3, path21, defaultValue) {
11851
- var result = object3 == null ? void 0 : baseGet(object3, path21);
11878
+ function get(object3, path22, defaultValue) {
11879
+ var result = object3 == null ? void 0 : baseGet(object3, path22);
11852
11880
  return result === void 0 ? defaultValue : result;
11853
11881
  }
11854
- function hasIn(object3, path21) {
11855
- return object3 != null && hasPath(object3, path21, baseHasIn);
11882
+ function hasIn(object3, path22) {
11883
+ return object3 != null && hasPath(object3, path22, baseHasIn);
11856
11884
  }
11857
11885
  function keys(object3) {
11858
11886
  return isArrayLike(object3) ? arrayLikeKeys(object3) : baseKeys(object3);
@@ -11860,8 +11888,8 @@ function requireLodash_sortby() {
11860
11888
  function identity(value) {
11861
11889
  return value;
11862
11890
  }
11863
- function property(path21) {
11864
- return isKey(path21) ? baseProperty(toKey(path21)) : basePropertyDeep(path21);
11891
+ function property(path22) {
11892
+ return isKey(path22) ? baseProperty(toKey(path22)) : basePropertyDeep(path22);
11865
11893
  }
11866
11894
  module.exports = sortBy;
11867
11895
  })(lodash_sortby, lodash_sortby.exports);
@@ -12292,16 +12320,16 @@ async function reportIfFoundPrivateKeyP12Format({ source, context, t }) {
12292
12320
  return;
12293
12321
  }
12294
12322
  try {
12295
- const fs17 = await import("node:fs");
12296
- const path21 = await import("node:path");
12297
- const pfxBytes = new Uint8Array(fs17.readFileSync(source.filePath));
12323
+ const fs18 = await import("node:fs");
12324
+ const path22 = await import("node:path");
12325
+ const pfxBytes = new Uint8Array(fs18.readFileSync(source.filePath));
12298
12326
  const isGcpServiceAccountP12 = await verifyPkcs12Mac(pfxBytes, GCP_SERVICE_ACCOUNT_P12_PASSWORD);
12299
12327
  if (!isGcpServiceAccountP12) {
12300
12328
  return;
12301
12329
  }
12302
12330
  context.report({
12303
12331
  message: t("PrivateKeyP12", {
12304
- FILE_NAME: path21.basename(source.filePath)
12332
+ FILE_NAME: path22.basename(source.filePath)
12305
12333
  }),
12306
12334
  range: [0, source.content.length]
12307
12335
  });
@@ -15966,11 +15994,11 @@ async function connectedConfig(source) {
15966
15994
  }
15967
15995
 
15968
15996
  // src/history-import.ts
15969
- import path19 from "node:path";
15997
+ import path20 from "node:path";
15970
15998
 
15971
15999
  // src/service.ts
15972
- import { promises as fs15 } from "node:fs";
15973
- import path18 from "node:path";
16000
+ import { promises as fs16 } from "node:fs";
16001
+ import path19 from "node:path";
15974
16002
  import { setTimeout as delay3 } from "node:timers/promises";
15975
16003
 
15976
16004
  // src/collector.ts
@@ -17500,6 +17528,485 @@ async function syncCursorBilling(config2, fetcher = collectorFetch, signal) {
17500
17528
  };
17501
17529
  }
17502
17530
 
17531
+ // src/codex-billing.ts
17532
+ import { createHash as createHash8 } from "node:crypto";
17533
+ import { promises as fs15 } from "node:fs";
17534
+ import path18 from "node:path";
17535
+ var TASK_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage/thread_usage/query_v2";
17536
+ var UUID3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
17537
+ var DAY_MS2 = 24 * 60 * 60 * 1e3;
17538
+ var TASK_BATCH_SIZE = 50;
17539
+ var MAX_HEADER_BYTES = 3 * 1024 * 1024;
17540
+ var stateSchema2 = object({
17541
+ accountFingerprint: string2().regex(/^[0-9a-f]{64}$/),
17542
+ lastSyncedAtMs: number2().int().nonnegative(),
17543
+ lastAuditAtMs: number2().int().nonnegative(),
17544
+ unavailableThreadIds: array(uuid2())
17545
+ });
17546
+ var authSchema = object({
17547
+ auth_mode: string2().optional(),
17548
+ tokens: object({
17549
+ access_token: string2().min(1),
17550
+ account_id: string2().min(1)
17551
+ }).optional()
17552
+ });
17553
+ var taskUsageSchema = object({
17554
+ threads: array(
17555
+ object({
17556
+ thread_id: uuid2(),
17557
+ data_status: _enum(["available", "partial", "unavailable"]),
17558
+ usage_source: string2(),
17559
+ balance_usage_credits: string2().nullable().optional()
17560
+ })
17561
+ ).max(TASK_BATCH_SIZE)
17562
+ });
17563
+ var sessionMetaSchema = object({
17564
+ type: literal("session_meta"),
17565
+ payload: object({
17566
+ id: uuid2(),
17567
+ timestamp: string2().optional(),
17568
+ creator_account_id: string2().nullable().optional(),
17569
+ model_provider: string2().nullable().optional(),
17570
+ parent_thread_id: string2().nullable().optional(),
17571
+ source: unknown().optional()
17572
+ })
17573
+ });
17574
+ var subagentSourceSchema = object({
17575
+ subagent: object({
17576
+ thread_spawn: object({ parent_thread_id: string2() }).optional()
17577
+ }).optional()
17578
+ });
17579
+ var syncResultSchema = object({ matched: number2().int().nonnegative() });
17580
+ function fingerprint(value) {
17581
+ return createHash8("sha256").update(value).digest("hex");
17582
+ }
17583
+ async function readAuth(codexHome) {
17584
+ let contents;
17585
+ try {
17586
+ contents = await fs15.readFile(path18.join(codexHome, "auth.json"), "utf8");
17587
+ } catch (error2) {
17588
+ if (error2 instanceof Error && "code" in error2 && error2.code === "ENOENT")
17589
+ return {
17590
+ accessToken: null,
17591
+ accountId: null,
17592
+ accountFingerprint: fingerprint(
17593
+ "codex-local-auth-logs:no-chatgpt-token"
17594
+ )
17595
+ };
17596
+ throw new Error("Could not read Codex authentication.", { cause: error2 });
17597
+ }
17598
+ let decoded;
17599
+ try {
17600
+ decoded = JSON.parse(contents);
17601
+ } catch (error2) {
17602
+ throw new Error("Codex authentication is not valid JSON.", {
17603
+ cause: error2
17604
+ });
17605
+ }
17606
+ const parsed = authSchema.safeParse(decoded);
17607
+ if (!parsed.success)
17608
+ throw new Error("Codex authentication has an unexpected shape.");
17609
+ const accountId = parsed.data.tokens?.account_id ?? null;
17610
+ return {
17611
+ accessToken: parsed.data.auth_mode === "chatgpt" ? parsed.data.tokens?.access_token ?? null : null,
17612
+ accountId,
17613
+ accountFingerprint: fingerprint(
17614
+ `${accountId ?? "codex-local-auth-logs"}:${parsed.data.auth_mode === "chatgpt" && parsed.data.tokens?.access_token ? "chatgpt" : "no-chatgpt-token"}`
17615
+ )
17616
+ };
17617
+ }
17618
+ async function readState(filename) {
17619
+ let contents;
17620
+ try {
17621
+ contents = await fs15.readFile(filename, "utf8");
17622
+ } catch (error2) {
17623
+ if (error2 instanceof Error && "code" in error2 && error2.code === "ENOENT")
17624
+ return null;
17625
+ throw new Error("Could not read Codex billing sync state.", {
17626
+ cause: error2
17627
+ });
17628
+ }
17629
+ let decoded;
17630
+ try {
17631
+ decoded = JSON.parse(contents);
17632
+ } catch (error2) {
17633
+ throw new Error("Codex billing sync state is not valid JSON.", {
17634
+ cause: error2
17635
+ });
17636
+ }
17637
+ const parsed = stateSchema2.safeParse(decoded);
17638
+ if (!parsed.success) throw new Error("Codex billing sync state is invalid.");
17639
+ return parsed.data;
17640
+ }
17641
+ async function saveState2(filename, state) {
17642
+ const temporary = `${filename}.${process.pid}.tmp`;
17643
+ const handle = await fs15.open(temporary, "w", 384);
17644
+ try {
17645
+ await handle.writeFile(`${JSON.stringify(state)}
17646
+ `);
17647
+ await handle.sync();
17648
+ } finally {
17649
+ await handle.close();
17650
+ }
17651
+ await fs15.rename(temporary, filename);
17652
+ }
17653
+ async function localThread(filename) {
17654
+ const stat = await fs15.stat(filename);
17655
+ const handle = await fs15.open(filename, "r");
17656
+ let header = null;
17657
+ try {
17658
+ const buffer = Buffer.alloc(64 * 1024);
17659
+ const chunks = [];
17660
+ let offset = 0;
17661
+ while (offset < Math.min(stat.size, MAX_HEADER_BYTES)) {
17662
+ const read = await handle.read(
17663
+ buffer,
17664
+ 0,
17665
+ Math.min(buffer.length, MAX_HEADER_BYTES - offset),
17666
+ offset
17667
+ );
17668
+ if (read.bytesRead === 0) break;
17669
+ const newline = buffer.subarray(0, read.bytesRead).indexOf(10);
17670
+ if (newline >= 0) {
17671
+ chunks.push(Buffer.from(buffer.subarray(0, newline)));
17672
+ header = Buffer.concat(chunks);
17673
+ break;
17674
+ }
17675
+ chunks.push(Buffer.from(buffer.subarray(0, read.bytesRead)));
17676
+ offset += read.bytesRead;
17677
+ }
17678
+ } finally {
17679
+ await handle.close();
17680
+ }
17681
+ if (header === null) return null;
17682
+ let raw;
17683
+ try {
17684
+ raw = JSON.parse(header.toString("utf8"));
17685
+ } catch {
17686
+ return null;
17687
+ }
17688
+ const record3 = sessionMetaSchema.safeParse(raw);
17689
+ if (!record3.success) return null;
17690
+ const payload = record3.data.payload;
17691
+ if (!filename.endsWith(`${payload.id}.jsonl`)) return null;
17692
+ const source = subagentSourceSchema.safeParse(payload.source);
17693
+ const parentThreadId = payload.parent_thread_id ?? (source.success ? source.data.subagent?.thread_spawn?.parent_thread_id : null) ?? null;
17694
+ if (parentThreadId !== null && !UUID3.test(parentThreadId)) return null;
17695
+ return {
17696
+ threadId: payload.id,
17697
+ parentThreadId,
17698
+ createdAt: payload.timestamp && !Number.isNaN(Date.parse(payload.timestamp)) ? new Date(payload.timestamp).toISOString() : null,
17699
+ accountId: payload.creator_account_id ?? null,
17700
+ modelProvider: payload.model_provider ?? null,
17701
+ mtimeMs: stat.mtimeMs
17702
+ };
17703
+ }
17704
+ function groupCodexTasks(threads, accountId) {
17705
+ const byId = new Map(
17706
+ threads.filter(
17707
+ (thread) => thread.accountId === null || thread.accountId === accountId
17708
+ ).map((thread) => [thread.threadId, thread])
17709
+ );
17710
+ const tasks = /* @__PURE__ */ new Map();
17711
+ for (const thread of byId.values()) {
17712
+ if (thread.parentThreadId === null)
17713
+ tasks.set(thread.threadId, {
17714
+ threadId: thread.threadId,
17715
+ createdAt: thread.createdAt,
17716
+ descendantThreadIds: [],
17717
+ mtimeMs: thread.mtimeMs
17718
+ });
17719
+ }
17720
+ for (const thread of byId.values()) {
17721
+ if (thread.parentThreadId === null) continue;
17722
+ let ancestor = thread.parentThreadId;
17723
+ const seen = /* @__PURE__ */ new Set([thread.threadId]);
17724
+ while (ancestor !== null && !seen.has(ancestor)) {
17725
+ seen.add(ancestor);
17726
+ const parent = byId.get(ancestor);
17727
+ if (parent === void 0) break;
17728
+ if (parent.parentThreadId === null) {
17729
+ const task = tasks.get(parent.threadId);
17730
+ if (task !== void 0) {
17731
+ task.descendantThreadIds.push(thread.threadId);
17732
+ task.mtimeMs = Math.max(task.mtimeMs, thread.mtimeMs);
17733
+ }
17734
+ break;
17735
+ }
17736
+ ancestor = parent.parentThreadId;
17737
+ }
17738
+ }
17739
+ return [...tasks.values()].map((task) => ({
17740
+ ...task,
17741
+ descendantThreadIds: task.descendantThreadIds.sort()
17742
+ }));
17743
+ }
17744
+ function parseCodexAuthLog(threadId, body) {
17745
+ if (threadId === null || !UUID3.test(threadId) || body === null) return null;
17746
+ const turn = /try_run_sampling_request\{turn_id=([0-9a-f-]{36})\b/i.exec(
17747
+ body
17748
+ );
17749
+ const auth = /\bauth_mode=Some\(([^)]+)\)/.exec(body);
17750
+ if (turn === null || auth === null || !UUID3.test(turn[1])) return null;
17751
+ const mode = auth[1] === "Chatgpt" ? "chatgpt" : auth[1] === "ApiKey" || auth[1] === "BedrockApiKey" || auth[1] === "BedrockAccessKeys" ? "api_key" : "conflicting";
17752
+ return { threadId, turnId: turn[1], mode };
17753
+ }
17754
+ async function localAuthTurns(codexHome, modelProviders) {
17755
+ const filename = path18.join(codexHome, "logs_2.sqlite");
17756
+ try {
17757
+ await fs15.access(filename);
17758
+ } catch (error2) {
17759
+ if (error2 instanceof Error && "code" in error2 && error2.code === "ENOENT")
17760
+ return [];
17761
+ throw new Error("Could not access Codex's local logs.", { cause: error2 });
17762
+ }
17763
+ const { DatabaseSync } = await import("node:sqlite");
17764
+ let database;
17765
+ try {
17766
+ database = new DatabaseSync(filename, { readOnly: true });
17767
+ } catch (error2) {
17768
+ throw new Error("Could not open Codex's local logs.", { cause: error2 });
17769
+ }
17770
+ const turns = /* @__PURE__ */ new Map();
17771
+ const logRowSchema = object({
17772
+ thread_id: string2().nullable(),
17773
+ feedback_log_body: string2().nullable()
17774
+ });
17775
+ try {
17776
+ const rows = database.prepare(
17777
+ "SELECT thread_id, feedback_log_body FROM logs WHERE target = 'feedback_tags' AND feedback_log_body LIKE '%auth_mode=Some(%'"
17778
+ ).iterate();
17779
+ for (const raw of rows) {
17780
+ const row = logRowSchema.safeParse(raw);
17781
+ if (!row.success)
17782
+ throw new Error("Codex's local log row has an invalid shape.");
17783
+ if (row.data.thread_id === null || !modelProviders.has(row.data.thread_id))
17784
+ continue;
17785
+ const parsed = parseCodexAuthLog(
17786
+ row.data.thread_id,
17787
+ row.data.feedback_log_body
17788
+ );
17789
+ if (parsed === null) continue;
17790
+ const mode = parsed.mode === "chatgpt" && modelProviders.get(parsed.threadId) !== "openai" ? "conflicting" : parsed.mode;
17791
+ const key = `${parsed.threadId}:${parsed.turnId}`;
17792
+ const prior = turns.get(key);
17793
+ turns.set(key, {
17794
+ ...parsed,
17795
+ mode: prior === void 0 || prior.mode === mode ? mode : "conflicting"
17796
+ });
17797
+ }
17798
+ } finally {
17799
+ database.close();
17800
+ }
17801
+ return [...turns.values()];
17802
+ }
17803
+ async function queryTaskUsage(tasks, accessToken, accountId, signal) {
17804
+ const response = await fetch(TASK_USAGE_URL, {
17805
+ method: "POST",
17806
+ headers: {
17807
+ Authorization: `Bearer ${accessToken}`,
17808
+ "ChatGPT-Account-Id": accountId,
17809
+ "Content-Type": "application/json",
17810
+ "User-Agent": "codex-cli"
17811
+ },
17812
+ body: JSON.stringify({
17813
+ threads: tasks.map((task) => ({
17814
+ thread_id: task.threadId,
17815
+ created_at: task.createdAt,
17816
+ descendant_thread_ids: task.descendantThreadIds
17817
+ }))
17818
+ }),
17819
+ signal: AbortSignal.any([
17820
+ AbortSignal.timeout(2e4),
17821
+ ...signal ? [signal] : []
17822
+ ])
17823
+ });
17824
+ if (!response.ok)
17825
+ throw new Error(
17826
+ `Codex task usage lookup failed with HTTP ${response.status}.`
17827
+ );
17828
+ const parsed = taskUsageSchema.safeParse(await response.json());
17829
+ if (!parsed.success) throw new Error("Codex returned invalid task usage.");
17830
+ const requested = new Set(tasks.map((task) => task.threadId));
17831
+ const byId = new Map(tasks.map((task) => [task.threadId, task]));
17832
+ const result = /* @__PURE__ */ new Map();
17833
+ for (const row of parsed.data.threads) {
17834
+ if (!requested.has(row.thread_id) || result.has(row.thread_id))
17835
+ throw new Error("Codex returned an unexpected task usage ID.");
17836
+ const task = byId.get(row.thread_id);
17837
+ result.set(row.thread_id, {
17838
+ threadId: row.thread_id,
17839
+ createdAt: task.createdAt,
17840
+ descendantThreadIds: [...task.descendantThreadIds],
17841
+ dataStatus: row.data_status,
17842
+ usageSource: row.usage_source,
17843
+ balanceUsageCredits: row.balance_usage_credits ?? null
17844
+ });
17845
+ }
17846
+ return result;
17847
+ }
17848
+ async function syncCodexBilling(config2, fetcher = collectorFetch, signal) {
17849
+ const auth = await readAuth(config2.codexHome);
17850
+ const stateFile = path18.join(config2.dataDirectory, "codex-billing-sync.json");
17851
+ const prior = await readState(stateFile);
17852
+ const nowMs = Date.now();
17853
+ const history = prior === null || prior.accountFingerprint !== auth.accountFingerprint;
17854
+ const audit = !history && nowMs - prior.lastAuditAtMs >= 7 * DAY_MS2;
17855
+ const cutoff = history ? 0 : Math.min(
17856
+ prior.lastSyncedAtMs - DAY_MS2,
17857
+ nowMs - (audit ? 35 : 7) * DAY_MS2
17858
+ );
17859
+ const filenames = await sourceFiles("codex", config2.codexHome);
17860
+ const threads = [];
17861
+ for (const filename of filenames) {
17862
+ signal?.throwIfAborted();
17863
+ const thread = await localThread(filename);
17864
+ if (thread !== null) threads.push(thread);
17865
+ }
17866
+ const allTasks = groupCodexTasks(threads, auth.accountId);
17867
+ const retryIds = new Set(audit ? prior?.unavailableThreadIds ?? [] : []);
17868
+ const selected = allTasks.filter(
17869
+ (task) => task.mtimeMs >= cutoff || retryIds.has(task.threadId)
17870
+ );
17871
+ if (selected.length === 0)
17872
+ return {
17873
+ tasks: 0,
17874
+ included: 0,
17875
+ creditTasks: 0,
17876
+ apiTurns: 0,
17877
+ skipped: false
17878
+ };
17879
+ const selectedIds = new Set(
17880
+ selected.flatMap((task) => [task.threadId, ...task.descendantThreadIds])
17881
+ );
17882
+ const modelProviders = new Map(
17883
+ threads.filter((thread) => selectedIds.has(thread.threadId)).map((thread) => [thread.threadId, thread.modelProvider])
17884
+ );
17885
+ const authTurns = await localAuthTurns(config2.codexHome, modelProviders);
17886
+ const byThread = /* @__PURE__ */ new Map();
17887
+ for (const turn of authTurns) {
17888
+ const turns = byThread.get(turn.threadId) ?? [];
17889
+ turns.push(turn);
17890
+ byThread.set(turn.threadId, turns);
17891
+ }
17892
+ const localTaskIds = new Set(allTasks.map((task) => task.threadId));
17893
+ const unavailable = new Set(
17894
+ history ? [] : (prior?.unavailableThreadIds ?? []).filter(
17895
+ (id) => localTaskIds.has(id)
17896
+ )
17897
+ );
17898
+ let included = 0;
17899
+ let creditTasks = 0;
17900
+ let apiTurns = 0;
17901
+ const endpoint = new URL(config2.endpoint);
17902
+ endpoint.pathname = "/api/traces/codex-billing";
17903
+ const batches = [];
17904
+ let current = [];
17905
+ let currentIds = 0;
17906
+ let currentTurns = 0;
17907
+ for (const task of selected) {
17908
+ const size = 1 + task.descendantThreadIds.length;
17909
+ const turns = [task.threadId, ...task.descendantThreadIds].reduce(
17910
+ (total, threadId) => total + (byThread.get(threadId)?.length ?? 0),
17911
+ 0
17912
+ );
17913
+ if (size > 1e3)
17914
+ throw new Error("Codex task exceeds the billing thread limit.");
17915
+ if (current.length >= TASK_BATCH_SIZE || currentIds + size > 1e3 || currentTurns + turns > 5e3) {
17916
+ if (current.length > 0) batches.push(current);
17917
+ current = [];
17918
+ currentIds = 0;
17919
+ currentTurns = 0;
17920
+ }
17921
+ current.push(task);
17922
+ currentIds += size;
17923
+ currentTurns += turns;
17924
+ if (turns > 5e3) {
17925
+ batches.push(current);
17926
+ current = [];
17927
+ currentIds = 0;
17928
+ currentTurns = 0;
17929
+ }
17930
+ }
17931
+ if (current.length > 0) batches.push(current);
17932
+ for (const batch of batches) {
17933
+ signal?.throwIfAborted();
17934
+ const responseTasks = auth.accessToken === null || auth.accountId === null ? /* @__PURE__ */ new Map() : await queryTaskUsage(batch, auth.accessToken, auth.accountId, signal);
17935
+ const tasks = batch.map((task) => {
17936
+ const response = responseTasks.get(task.threadId);
17937
+ return response ?? {
17938
+ threadId: task.threadId,
17939
+ createdAt: task.createdAt,
17940
+ descendantThreadIds: [...task.descendantThreadIds],
17941
+ dataStatus: auth.accessToken === null || auth.accountId === null ? "not_queried" : "unavailable",
17942
+ usageSource: "",
17943
+ balanceUsageCredits: null
17944
+ };
17945
+ });
17946
+ const batchTurns = batch.flatMap(
17947
+ (task) => [task.threadId, ...task.descendantThreadIds].flatMap(
17948
+ (threadId) => byThread.get(threadId) ?? []
17949
+ )
17950
+ );
17951
+ let serverIncomplete = false;
17952
+ for (let offset = 0; offset < Math.max(1, batchTurns.length); offset += 5e3) {
17953
+ const request2 = codexBillingRequestSchema.parse({
17954
+ accountFingerprint: auth.accountFingerprint,
17955
+ reconcile: offset === 0,
17956
+ tasks,
17957
+ authTurns: batchTurns.slice(offset, offset + 5e3)
17958
+ });
17959
+ const response = await fetcher(endpoint, {
17960
+ method: "POST",
17961
+ headers: {
17962
+ Authorization: `Bearer ${config2.token}`,
17963
+ "Content-Type": "application/json"
17964
+ },
17965
+ body: JSON.stringify(request2),
17966
+ signal: AbortSignal.any([
17967
+ AbortSignal.timeout(9e4),
17968
+ ...signal ? [signal] : []
17969
+ ])
17970
+ });
17971
+ if (!response.ok)
17972
+ throw new Error(
17973
+ `Codex billing sync failed with HTTP ${response.status}.`
17974
+ );
17975
+ const result = syncResultSchema.safeParse(await response.json());
17976
+ if (!result.success || result.data.matched > tasks.length)
17977
+ throw new Error("Iteration returned an invalid Codex billing result.");
17978
+ if (result.data.matched < tasks.length) serverIncomplete = true;
17979
+ }
17980
+ for (const task of tasks) {
17981
+ if (task.dataStatus === "available" && task.balanceUsageCredits !== null && (task.usageSource === "included_plan" || task.usageSource === "plan_and_credits"))
17982
+ unavailable.delete(task.threadId);
17983
+ else if (task.dataStatus !== "not_queried")
17984
+ unavailable.add(task.threadId);
17985
+ if (serverIncomplete) unavailable.add(task.threadId);
17986
+ if (task.dataStatus === "available" && task.balanceUsageCredits !== null && (task.usageSource === "included_plan" || task.usageSource === "plan_and_credits")) {
17987
+ if (codexCreditSign(task.balanceUsageCredits) === 0 && task.usageSource === "included_plan")
17988
+ included += 1;
17989
+ else if (codexCreditSign(task.balanceUsageCredits) > 0)
17990
+ creditTasks += 1;
17991
+ }
17992
+ }
17993
+ apiTurns += batchTurns.filter((turn) => turn.mode === "api_key").length;
17994
+ }
17995
+ await saveState2(stateFile, {
17996
+ accountFingerprint: auth.accountFingerprint,
17997
+ lastSyncedAtMs: nowMs,
17998
+ lastAuditAtMs: history || audit ? nowMs : prior.lastAuditAtMs,
17999
+ unavailableThreadIds: [...unavailable]
18000
+ });
18001
+ return {
18002
+ tasks: selected.length,
18003
+ included,
18004
+ creditTasks,
18005
+ apiTurns,
18006
+ skipped: false
18007
+ };
18008
+ }
18009
+
17503
18010
  // src/service.ts
17504
18011
  function errorCode2(error2) {
17505
18012
  if (error2 instanceof Error && "code" in error2) return String(error2.code);
@@ -17541,26 +18048,26 @@ async function persistHealth(health, paths) {
17541
18048
  }
17542
18049
  }
17543
18050
  async function acquireCollectorLock(paths) {
17544
- await ensurePrivateDirectory(path18.dirname(paths.lock));
18051
+ await ensurePrivateDirectory(path19.dirname(paths.lock));
17545
18052
  for (let attempt = 0; attempt < 2; attempt += 1) {
17546
18053
  try {
17547
- const handle = await fs15.open(paths.lock, "wx", 384);
18054
+ const handle = await fs16.open(paths.lock, "wx", 384);
17548
18055
  await handle.writeFile(`${process.pid}
17549
18056
  `, "utf8");
17550
18057
  await handle.sync();
17551
18058
  await handle.close();
17552
18059
  return async () => {
17553
- const current = await fs15.readFile(paths.lock, "utf8").catch(() => "");
18060
+ const current = await fs16.readFile(paths.lock, "utf8").catch(() => "");
17554
18061
  if (current.trim() === String(process.pid)) {
17555
- await fs15.rm(paths.lock, { force: true });
18062
+ await fs16.rm(paths.lock, { force: true });
17556
18063
  }
17557
18064
  };
17558
18065
  } catch (error2) {
17559
18066
  if (errorCode2(error2) !== "EEXIST") throw error2;
17560
- const lockContents = await fs15.readFile(paths.lock, "utf8").catch(() => "");
18067
+ const lockContents = await fs16.readFile(paths.lock, "utf8").catch(() => "");
17561
18068
  const lockPid = Number(lockContents.trim());
17562
18069
  if (!Number.isSafeInteger(lockPid) || lockPid < 2) {
17563
- await fs15.rm(paths.lock, { force: true });
18070
+ await fs16.rm(paths.lock, { force: true });
17564
18071
  continue;
17565
18072
  }
17566
18073
  try {
@@ -17568,7 +18075,7 @@ async function acquireCollectorLock(paths) {
17568
18075
  throw new CollectorLockBusyError("Collector is already running.");
17569
18076
  } catch (processError) {
17570
18077
  if (errorCode2(processError) !== "ESRCH") throw processError;
17571
- await fs15.rm(paths.lock, { force: true });
18078
+ await fs16.rm(paths.lock, { force: true });
17572
18079
  }
17573
18080
  }
17574
18081
  }
@@ -17840,6 +18347,33 @@ async function runCollector(directory, expectedSource) {
17840
18347
  );
17841
18348
  }
17842
18349
  }
18350
+ if ("codexHome" in config2 && billingReady && Date.now() - lastBillingSyncAt >= 6 * 60 * 60 * 1e3) {
18351
+ try {
18352
+ const billing = await syncCodexBilling(
18353
+ config2,
18354
+ collectorFetch,
18355
+ controller.signal
18356
+ );
18357
+ lastBillingSyncAt = billing.skipped ? Date.now() - 6 * 60 * 60 * 1e3 + 5 * 60 * 1e3 : Date.now();
18358
+ console.info(
18359
+ JSON.stringify({
18360
+ level: "info",
18361
+ operation: "sync_codex_billing",
18362
+ ...billing
18363
+ })
18364
+ );
18365
+ } catch (error2) {
18366
+ if (controller.signal.aborted) break;
18367
+ lastBillingSyncAt = Date.now() - 6 * 60 * 60 * 1e3 + 5 * 60 * 1e3;
18368
+ console.error(
18369
+ JSON.stringify({
18370
+ level: "warning",
18371
+ operation: "sync_codex_billing",
18372
+ message: error2 instanceof Error ? error2.message : "Codex billing sync failed."
18373
+ })
18374
+ );
18375
+ }
18376
+ }
17843
18377
  try {
17844
18378
  await delay3(retryDelayMs, void 0, { signal: controller.signal });
17845
18379
  } catch (error2) {
@@ -17867,7 +18401,7 @@ async function importSourceHistory(config2, paths, confirmed, control = backgrou
17867
18401
  const source = collectorSource(config2);
17868
18402
  const directory = collectorSourceDirectory(config2);
17869
18403
  const state = await loadState(paths);
17870
- if (!state.initialized || state.sourceDirectory !== path19.resolve(directory)) {
18404
+ if (!state.initialized || state.sourceDirectory !== path20.resolve(directory)) {
17871
18405
  throw new Error(
17872
18406
  `Connect the ${source} source before importing its history.`
17873
18407
  );
@@ -18296,7 +18830,7 @@ function usage(command) {
18296
18830
  }
18297
18831
  async function fileExists(filename) {
18298
18832
  try {
18299
- await fs16.access(filename);
18833
+ await fs17.access(filename);
18300
18834
  return true;
18301
18835
  } catch (error2) {
18302
18836
  if (error2 instanceof Error && "code" in error2 && error2.code === "ENOENT") {
@@ -18346,7 +18880,7 @@ ${missing2.map(collectorSourceLabel).join(" and ")} found on this computer but n
18346
18880
  await connectAll({});
18347
18881
  }
18348
18882
  function dataDirectory(source, values) {
18349
- return path20.resolve(
18883
+ return path21.resolve(
18350
18884
  optionalString(values, "data-directory") ?? defaultCollectorDirectory(source)
18351
18885
  );
18352
18886
  }
@@ -18371,8 +18905,8 @@ async function initialize(source, values) {
18371
18905
  const organizationId = optionalString(values, "organization-id");
18372
18906
  const directory = dataDirectory(source, values);
18373
18907
  const paths = collectorPaths(directory);
18374
- const selectedHome = path20.resolve(
18375
- source === "codex" ? optionalString(values, "codex-home") ?? process.env.CODEX_HOME ?? path20.join(os8.homedir(), ".codex") : source === "claude" ? optionalString(values, "claude-home") ?? process.env.CLAUDE_CONFIG_DIR ?? path20.join(os8.homedir(), ".claude") : optionalString(values, "cursor-home") ?? path20.join(os8.homedir(), ".cursor")
18908
+ const selectedHome = path21.resolve(
18909
+ source === "codex" ? optionalString(values, "codex-home") ?? process.env.CODEX_HOME ?? path21.join(os8.homedir(), ".codex") : source === "claude" ? optionalString(values, "claude-home") ?? process.env.CLAUDE_CONFIG_DIR ?? path21.join(os8.homedir(), ".claude") : optionalString(values, "cursor-home") ?? path21.join(os8.homedir(), ".cursor")
18376
18910
  );
18377
18911
  await assertFreshCollectorDirectory(paths, source);
18378
18912
  const preparedState = await prepareSourceInitialState(
@@ -18455,13 +18989,13 @@ async function connectAll(values) {
18455
18989
  }
18456
18990
  const configs = await Promise.all(COLLECTOR_SOURCES.map(connectedConfig));
18457
18991
  const sourceHomes = {
18458
- codex: path20.resolve(
18992
+ codex: path21.resolve(
18459
18993
  optionalString(values, "codex-home") ?? (configs[0] && "codexHome" in configs[0] ? configs[0].codexHome : sourceHome("codex"))
18460
18994
  ),
18461
- claude: path20.resolve(
18995
+ claude: path21.resolve(
18462
18996
  optionalString(values, "claude-home") ?? (configs[1] && "claudeHome" in configs[1] ? configs[1].claudeHome : sourceHome("claude"))
18463
18997
  ),
18464
- cursor: path20.resolve(
18998
+ cursor: path21.resolve(
18465
18999
  optionalString(values, "cursor-home") ?? (configs[2] && "cursorHome" in configs[2] ? configs[2].cursorHome : sourceHome("cursor"))
18466
19000
  )
18467
19001
  };
@@ -7285,6 +7285,31 @@ function superRefine(fn, params) {
7285
7285
  return _superRefine(fn, params);
7286
7286
  }
7287
7287
 
7288
+ // ../../node_modules/.pnpm/zod@4.6.4/node_modules/zod/v4/classic/iso.js
7289
+ var iso_exports = {};
7290
+ __export(iso_exports, {
7291
+ ZodISODate: () => ZodISODate,
7292
+ ZodISODateTime: () => ZodISODateTime,
7293
+ ZodISODuration: () => ZodISODuration,
7294
+ ZodISOTime: () => ZodISOTime,
7295
+ date: () => date2,
7296
+ datetime: () => datetime2,
7297
+ duration: () => duration2,
7298
+ time: () => time2
7299
+ });
7300
+ function datetime2(params) {
7301
+ return _isoDateTime(ZodISODateTime, params);
7302
+ }
7303
+ function date2(params) {
7304
+ return _isoDate(ZodISODate, params);
7305
+ }
7306
+ function time2(params) {
7307
+ return _isoTime(ZodISOTime, params);
7308
+ }
7309
+ function duration2(params) {
7310
+ return _isoDuration(ZodISODuration, params);
7311
+ }
7312
+
7288
7313
  // src/cursor-hooks.ts
7289
7314
  import path2 from "node:path";
7290
7315
 
@@ -7340,6 +7365,28 @@ var cursorBillingRequestSchema = object({
7340
7365
  paidUsd: number2().finite().nonnegative().nullable()
7341
7366
  }).strict()).max(5e3)
7342
7367
  }).strict();
7368
+ var codexBillingRequestSchema = object({
7369
+ accountFingerprint: string2().regex(/^[0-9a-f]{64}$/),
7370
+ reconcile: boolean2(),
7371
+ tasks: array(object({
7372
+ threadId: uuid2(),
7373
+ createdAt: iso_exports.datetime().nullable(),
7374
+ descendantThreadIds: array(uuid2()).max(900),
7375
+ dataStatus: _enum([
7376
+ "available",
7377
+ "partial",
7378
+ "unavailable",
7379
+ "not_queried"
7380
+ ]),
7381
+ usageSource: string2().max(100),
7382
+ balanceUsageCredits: string2().regex(/^[+-]?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d{1,3})?$/).max(128).nullable()
7383
+ }).strict()).max(100),
7384
+ authTurns: array(object({
7385
+ threadId: uuid2(),
7386
+ turnId: uuid2(),
7387
+ mode: _enum(["chatgpt", "api_key", "conflicting"])
7388
+ }).strict()).max(5e3)
7389
+ }).strict();
7343
7390
  var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
7344
7391
  var TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
7345
7392
  var unpairedSurrogate = /[\uD800-\uDFFF]/u;
package/dist/launcher.js CHANGED
@@ -6364,6 +6364,31 @@ function superRefine(fn, params) {
6364
6364
  return _superRefine(fn, params);
6365
6365
  }
6366
6366
 
6367
+ // ../../node_modules/.pnpm/zod@4.6.4/node_modules/zod/v4/classic/iso.js
6368
+ var iso_exports = {};
6369
+ __export(iso_exports, {
6370
+ ZodISODate: () => ZodISODate,
6371
+ ZodISODateTime: () => ZodISODateTime,
6372
+ ZodISODuration: () => ZodISODuration,
6373
+ ZodISOTime: () => ZodISOTime,
6374
+ date: () => date2,
6375
+ datetime: () => datetime2,
6376
+ duration: () => duration2,
6377
+ time: () => time2
6378
+ });
6379
+ function datetime2(params) {
6380
+ return _isoDateTime(ZodISODateTime, params);
6381
+ }
6382
+ function date2(params) {
6383
+ return _isoDate(ZodISODate, params);
6384
+ }
6385
+ function time2(params) {
6386
+ return _isoTime(ZodISOTime, params);
6387
+ }
6388
+ function duration2(params) {
6389
+ return _isoDuration(ZodISODuration, params);
6390
+ }
6391
+
6367
6392
  // ../trace-contracts/dist/index.js
6368
6393
  var TRACE_EVENT_TYPES = [
6369
6394
  "user_message",
@@ -6416,6 +6441,28 @@ var cursorBillingRequestSchema = object({
6416
6441
  paidUsd: number2().finite().nonnegative().nullable()
6417
6442
  }).strict()).max(5e3)
6418
6443
  }).strict();
6444
+ var codexBillingRequestSchema = object({
6445
+ accountFingerprint: string2().regex(/^[0-9a-f]{64}$/),
6446
+ reconcile: boolean2(),
6447
+ tasks: array(object({
6448
+ threadId: uuid2(),
6449
+ createdAt: iso_exports.datetime().nullable(),
6450
+ descendantThreadIds: array(uuid2()).max(900),
6451
+ dataStatus: _enum([
6452
+ "available",
6453
+ "partial",
6454
+ "unavailable",
6455
+ "not_queried"
6456
+ ]),
6457
+ usageSource: string2().max(100),
6458
+ balanceUsageCredits: string2().regex(/^[+-]?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d{1,3})?$/).max(128).nullable()
6459
+ }).strict()).max(100),
6460
+ authTurns: array(object({
6461
+ threadId: uuid2(),
6462
+ turnId: uuid2(),
6463
+ mode: _enum(["chatgpt", "api_key", "conflicting"])
6464
+ }).strict()).max(5e3)
6465
+ }).strict();
6419
6466
  var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
6420
6467
  var TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
6421
6468
  var unpairedSurrogate = /[\uD800-\uDFFF]/u;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iteration-cli",
3
- "version": "0.11.1",
3
+ "version": "0.12.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -22,7 +22,7 @@
22
22
  "@secretlint/secretlint-rule-preset-recommend": "13.0.6",
23
23
  "esbuild": "0.28.2",
24
24
  "zod": "4.6.4",
25
- "iteration-trace-contracts": "0.8.0"
25
+ "iteration-trace-contracts": "0.9.0"
26
26
  },
27
27
  "scripts": {
28
28
  "build": "pnpm --filter iteration-trace-contracts build && tsc -p tsconfig.json --noEmit && node build.mjs"