iteration-cli 0.14.2 → 0.14.3
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 +981 -765
- package/dist/cursor-hook-entry.js +66 -29
- package/dist/launcher.js +93 -38
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -939,7 +939,7 @@ import { promises as fs20 } from "node:fs";
|
|
|
939
939
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
940
940
|
import { parseArgs } from "node:util";
|
|
941
941
|
|
|
942
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
942
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/util.js
|
|
943
943
|
var util_exports = {};
|
|
944
944
|
__export(util_exports, {
|
|
945
945
|
BIGINT_FORMAT_RANGES: () => BIGINT_FORMAT_RANGES,
|
|
@@ -1010,6 +1010,7 @@ __export(util_exports, {
|
|
|
1010
1010
|
required: () => required,
|
|
1011
1011
|
safeExtend: () => safeExtend,
|
|
1012
1012
|
shallowClone: () => shallowClone,
|
|
1013
|
+
sizeCaps: () => sizeCaps,
|
|
1013
1014
|
slugify: () => slugify,
|
|
1014
1015
|
stringifyPrimitive: () => stringifyPrimitive,
|
|
1015
1016
|
toZod: () => toZod,
|
|
@@ -1529,6 +1530,18 @@ function prefixIssues(path25, issues) {
|
|
|
1529
1530
|
function unwrapMessage(message) {
|
|
1530
1531
|
return typeof message === "string" ? message : message?.message;
|
|
1531
1532
|
}
|
|
1533
|
+
function sizeCaps(defChecks) {
|
|
1534
|
+
const caps = [];
|
|
1535
|
+
for (const ch of defChecks ?? []) {
|
|
1536
|
+
const d = ch._zod.def;
|
|
1537
|
+
if (!/_(length|size)$|_equals$/.test(d.check))
|
|
1538
|
+
break;
|
|
1539
|
+
const max = d.maximum ?? d.length ?? d.size;
|
|
1540
|
+
if (max >= 0)
|
|
1541
|
+
caps.push([max, ch]);
|
|
1542
|
+
}
|
|
1543
|
+
return caps;
|
|
1544
|
+
}
|
|
1532
1545
|
function attachSchema(issues, start2, inst) {
|
|
1533
1546
|
var _a3;
|
|
1534
1547
|
for (let i = start2; i < issues.length; i++) {
|
|
@@ -1782,7 +1795,7 @@ function constantCatch(value) {
|
|
|
1782
1795
|
return fn;
|
|
1783
1796
|
}
|
|
1784
1797
|
|
|
1785
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
1798
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/core.js
|
|
1786
1799
|
var _a;
|
|
1787
1800
|
var _zodDesc = { value: void 0, enumerable: false };
|
|
1788
1801
|
var _E = "captureStackTrace" in Error ? Error : null;
|
|
@@ -1900,7 +1913,7 @@ function config(newConfig) {
|
|
|
1900
1913
|
return globalConfig;
|
|
1901
1914
|
}
|
|
1902
1915
|
|
|
1903
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
1916
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/errors.js
|
|
1904
1917
|
function _getMessage() {
|
|
1905
1918
|
const internals = this._zod;
|
|
1906
1919
|
internals.message ?? (internals.message = JSON.stringify(internals.def, jsonStringifyReplacer, 2));
|
|
@@ -2015,7 +2028,7 @@ function formatError(error2, mapper = (issue2) => issue2.message) {
|
|
|
2015
2028
|
return fieldErrors;
|
|
2016
2029
|
}
|
|
2017
2030
|
|
|
2018
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
2031
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/parse.js
|
|
2019
2032
|
function finalizeParams(callee, params) {
|
|
2020
2033
|
return { callee: params?.callee ?? callee, Err: params?.Err };
|
|
2021
2034
|
}
|
|
@@ -2163,7 +2176,7 @@ var _safeDecodeAsync = (_Err) => async (schema, value, _ctx) => {
|
|
|
2163
2176
|
return _safeParseAsync(_Err)(schema, value, _ctx);
|
|
2164
2177
|
};
|
|
2165
2178
|
|
|
2166
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
2179
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/regexes.js
|
|
2167
2180
|
var cuid = /^[cC][0-9a-z]{6,}$/;
|
|
2168
2181
|
var cuid2 = /^[0-9a-z]+$/;
|
|
2169
2182
|
var ulid = /^[0-7][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{25}$/;
|
|
@@ -2223,7 +2236,7 @@ var _null = /^null$/i;
|
|
|
2223
2236
|
var lowercase = /^[^A-Z]*$/;
|
|
2224
2237
|
var uppercase = /^[^a-z]*$/;
|
|
2225
2238
|
|
|
2226
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
2239
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/checks.js
|
|
2227
2240
|
var $ZodCheck = /* @__PURE__ */ $constructor("$ZodCheck", (inst, def) => {
|
|
2228
2241
|
var _a3;
|
|
2229
2242
|
inst._zod ?? (inst._zod = {});
|
|
@@ -2543,7 +2556,7 @@ var $ZodCheckOverwrite = /* @__PURE__ */ $constructor("$ZodCheckOverwrite", (ins
|
|
|
2543
2556
|
};
|
|
2544
2557
|
});
|
|
2545
2558
|
|
|
2546
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
2559
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/doc.js
|
|
2547
2560
|
var Doc = class {
|
|
2548
2561
|
constructor(args = [], closed = {}) {
|
|
2549
2562
|
this.content = [];
|
|
@@ -2584,14 +2597,14 @@ ${content.join("\n")}
|
|
|
2584
2597
|
}
|
|
2585
2598
|
};
|
|
2586
2599
|
|
|
2587
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
2600
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/versions.js
|
|
2588
2601
|
var version = {
|
|
2589
2602
|
major: 4,
|
|
2590
2603
|
minor: 6,
|
|
2591
|
-
patch:
|
|
2604
|
+
patch: 6
|
|
2592
2605
|
};
|
|
2593
2606
|
|
|
2594
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
2607
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/schemas.js
|
|
2595
2608
|
var $ZodType = /* @__PURE__ */ $constructor("$ZodType", (inst, def) => {
|
|
2596
2609
|
var _a3;
|
|
2597
2610
|
inst ?? (inst = {});
|
|
@@ -3206,6 +3219,29 @@ var $ZodDate = /* @__PURE__ */ $constructor("$ZodDate", (inst, def) => {
|
|
|
3206
3219
|
return payload;
|
|
3207
3220
|
};
|
|
3208
3221
|
});
|
|
3222
|
+
function capSize(inst, size) {
|
|
3223
|
+
const caps = sizeCaps(inst._zod.def.checks);
|
|
3224
|
+
if (!caps.length)
|
|
3225
|
+
return;
|
|
3226
|
+
const cap = Math.min(...caps.map((c) => c[0]));
|
|
3227
|
+
const parse2 = inst._zod.parse;
|
|
3228
|
+
inst._zod.parse = (payload, ctx) => {
|
|
3229
|
+
const n = size(payload.value);
|
|
3230
|
+
if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
|
|
3231
|
+
for (const [max, ch] of sizeCaps(inst._zod.def.checks)) {
|
|
3232
|
+
if (n > max && ch._zod.def.when(payload)) {
|
|
3233
|
+
const start2 = payload.issues.length;
|
|
3234
|
+
ch._zod.check(payload);
|
|
3235
|
+
payload.issues[start2].continue = false;
|
|
3236
|
+
attachSchema(payload.issues, start2, inst);
|
|
3237
|
+
return payload;
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
return parse2(payload, ctx);
|
|
3242
|
+
};
|
|
3243
|
+
}
|
|
3244
|
+
var arraySize = (input) => Array.isArray(input) ? input.length : -1;
|
|
3209
3245
|
function handleArrayResult(result, final, index) {
|
|
3210
3246
|
if (result.issues.length) {
|
|
3211
3247
|
final.issues.push(...prefixIssues(index, result.issues));
|
|
@@ -3249,6 +3285,7 @@ var $ZodArray = /* @__PURE__ */ $constructor("$ZodArray", (inst, def) => {
|
|
|
3249
3285
|
}
|
|
3250
3286
|
return payload;
|
|
3251
3287
|
};
|
|
3288
|
+
capSize(inst, arraySize);
|
|
3252
3289
|
});
|
|
3253
3290
|
function handlePropertyResult(result, final, key, input, optin, optout) {
|
|
3254
3291
|
const isPresent = key in input;
|
|
@@ -4292,7 +4329,7 @@ function handleRefineResult(result, payload, input, inst) {
|
|
|
4292
4329
|
}
|
|
4293
4330
|
}
|
|
4294
4331
|
|
|
4295
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
4332
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/memoizer.js
|
|
4296
4333
|
var $ZodCyclicError = class extends Error {
|
|
4297
4334
|
constructor() {
|
|
4298
4335
|
super(`Cannot parse a reference cycle that closes through a transform`);
|
|
@@ -4566,7 +4603,7 @@ function isBackEdge(ctx, value) {
|
|
|
4566
4603
|
return backEdges !== void 0 && isRef(value) && backEdges.has(value);
|
|
4567
4604
|
}
|
|
4568
4605
|
|
|
4569
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
4606
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/locales/en.js
|
|
4570
4607
|
var error = () => {
|
|
4571
4608
|
const Sizable = {
|
|
4572
4609
|
string: { unit: "characters", verb: "to have" },
|
|
@@ -4691,7 +4728,7 @@ function en_default() {
|
|
|
4691
4728
|
};
|
|
4692
4729
|
}
|
|
4693
4730
|
|
|
4694
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
4731
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/registries.js
|
|
4695
4732
|
var _a2;
|
|
4696
4733
|
var $ZodRegistry = class {
|
|
4697
4734
|
constructor() {
|
|
@@ -4739,7 +4776,7 @@ function registry() {
|
|
|
4739
4776
|
(_a2 = globalThis).__zod_globalRegistry ?? (_a2.__zod_globalRegistry = registry());
|
|
4740
4777
|
var globalRegistry = globalThis.__zod_globalRegistry;
|
|
4741
4778
|
|
|
4742
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
4779
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/api.js
|
|
4743
4780
|
function snapshotChecks(def) {
|
|
4744
4781
|
if (def.checks)
|
|
4745
4782
|
def.checks = [...def.checks];
|
|
@@ -5286,7 +5323,7 @@ function _check(fn, params) {
|
|
|
5286
5323
|
return ch;
|
|
5287
5324
|
}
|
|
5288
5325
|
|
|
5289
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
5326
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/to-json-schema.js
|
|
5290
5327
|
function assignProps(target, ...sources) {
|
|
5291
5328
|
for (const source of sources) {
|
|
5292
5329
|
for (const key of Reflect.ownKeys(source)) {
|
|
@@ -5816,7 +5853,7 @@ var createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params) =
|
|
|
5816
5853
|
return finalize(ctx, schema);
|
|
5817
5854
|
};
|
|
5818
5855
|
|
|
5819
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
5856
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/core/json-schema-processors.js
|
|
5820
5857
|
var narrowMin = (agg, key, value) => {
|
|
5821
5858
|
if (agg[key] === void 0 || value > agg[key])
|
|
5822
5859
|
agg[key] = value;
|
|
@@ -6366,7 +6403,7 @@ var lazyProcessor = (schema, ctx, _json, params) => {
|
|
|
6366
6403
|
seen.ref = innerType;
|
|
6367
6404
|
};
|
|
6368
6405
|
|
|
6369
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
6406
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/errors.js
|
|
6370
6407
|
var _installedErrorProtos = /* @__PURE__ */ new WeakSet([Object.prototype, Error.prototype]);
|
|
6371
6408
|
function _lazyMethod(proto, key, make) {
|
|
6372
6409
|
Object.defineProperty(proto, key, {
|
|
@@ -6411,7 +6448,7 @@ var ZodRealError = /* @__PURE__ */ $constructor("ZodError", initializer2, void 0
|
|
|
6411
6448
|
Parent: Error
|
|
6412
6449
|
});
|
|
6413
6450
|
|
|
6414
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
6451
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/parse.js
|
|
6415
6452
|
var parse = /* @__PURE__ */ _parse(ZodRealError);
|
|
6416
6453
|
var parseAsync = /* @__PURE__ */ _parseAsync(ZodRealError);
|
|
6417
6454
|
var safeParse = /* @__PURE__ */ _safeParse(ZodRealError);
|
|
@@ -6425,7 +6462,7 @@ var safeDecode = /* @__PURE__ */ _safeDecode(ZodRealError);
|
|
|
6425
6462
|
var safeEncodeAsync = /* @__PURE__ */ _safeEncodeAsync(ZodRealError);
|
|
6426
6463
|
var safeDecodeAsync = /* @__PURE__ */ _safeDecodeAsync(ZodRealError);
|
|
6427
6464
|
|
|
6428
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
6465
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/schemas.js
|
|
6429
6466
|
function _ensureDefaultLocale() {
|
|
6430
6467
|
if (!globalConfig.localeError)
|
|
6431
6468
|
config(en_default());
|
|
@@ -7447,7 +7484,7 @@ function json(params) {
|
|
|
7447
7484
|
return jsonSchema;
|
|
7448
7485
|
}
|
|
7449
7486
|
|
|
7450
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
7487
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/iso.js
|
|
7451
7488
|
var iso_exports = {};
|
|
7452
7489
|
__export(iso_exports, {
|
|
7453
7490
|
ZodISODate: () => ZodISODate,
|
|
@@ -7472,7 +7509,7 @@ function duration2(params) {
|
|
|
7472
7509
|
return _isoDuration(ZodISODuration, params);
|
|
7473
7510
|
}
|
|
7474
7511
|
|
|
7475
|
-
// ../../node_modules/.pnpm/zod@4.6.
|
|
7512
|
+
// ../../node_modules/.pnpm/zod@4.6.6/node_modules/zod/v4/classic/coerce.js
|
|
7476
7513
|
var coerce_exports = {};
|
|
7477
7514
|
__export(coerce_exports, {
|
|
7478
7515
|
bigint: () => bigint2,
|
|
@@ -7497,7 +7534,7 @@ function date3(params) {
|
|
|
7497
7534
|
return _coercedDate(ZodDate, params);
|
|
7498
7535
|
}
|
|
7499
7536
|
|
|
7500
|
-
// ../trace-contracts/dist/
|
|
7537
|
+
// ../trace-contracts/dist/contracts.js
|
|
7501
7538
|
var TRACE_EVENT_TYPES = [
|
|
7502
7539
|
"user_message",
|
|
7503
7540
|
"assistant_message",
|
|
@@ -7585,10 +7622,10 @@ var codexBillingRequestSchema = object({
|
|
|
7585
7622
|
mode: _enum(["chatgpt", "api_key", "conflicting"])
|
|
7586
7623
|
}).strict()).max(5e3)
|
|
7587
7624
|
}).strict();
|
|
7588
|
-
var UUID_PATTERN = /^[0-9a-
|
|
7625
|
+
var UUID_PATTERN = /^[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}$/;
|
|
7589
7626
|
var TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
7590
7627
|
var unpairedSurrogate = /[\uD800-\uDFFF]/u;
|
|
7591
|
-
var traceIdentitySchema = string2().min(1).max(200).refine((value) => !value.includes("\0") && !unpairedSurrogate.test(value), "Trace identifiers cannot contain NUL or unpaired Unicode surrogates.");
|
|
7628
|
+
var traceIdentitySchema = string2().min(1).max(200).refine((value) => !value.includes("\0") && !unpairedSurrogate.test(value), "Trace identifiers cannot contain NUL or unpaired Unicode surrogates.").describe("Must not contain NUL or unpaired Unicode surrogates.");
|
|
7592
7629
|
var jsonValueSchema = lazy(() => union([
|
|
7593
7630
|
string2(),
|
|
7594
7631
|
number2().finite(),
|
|
@@ -7619,28 +7656,28 @@ var traceEventSchema = object({
|
|
|
7619
7656
|
sequence: number2().int().min(0).max(Number.MAX_SAFE_INTEGER),
|
|
7620
7657
|
callId: string2().max(255).nullable(),
|
|
7621
7658
|
projectId: string2().regex(UUID_PATTERN).nullable(),
|
|
7622
|
-
occurredAt: string2().regex(TIMESTAMP_PATTERN).refine((value) => !Number.isNaN(Date.parse(value))).nullable(),
|
|
7659
|
+
occurredAt: string2().regex(TIMESTAMP_PATTERN).refine((value) => !Number.isNaN(Date.parse(value))).describe("Must also be a timestamp accepted by Date.parse.").nullable(),
|
|
7623
7660
|
eventType: _enum(TRACE_EVENT_TYPES),
|
|
7624
7661
|
payload: jsonPayloadSchema,
|
|
7625
7662
|
run: runMarkerSchema.optional()
|
|
7626
7663
|
}).strict();
|
|
7627
7664
|
var traceEventBatchSchema = object({
|
|
7628
7665
|
events: array(traceEventSchema).min(1).max(MAX_TRACE_EVENTS_PER_BATCH)
|
|
7629
|
-
}).strict();
|
|
7666
|
+
}).strict().describe("Event IDs must be unique within the batch.");
|
|
7630
7667
|
var acknowledgementSchema = object({
|
|
7631
7668
|
deviceId: string2().regex(UUID_PATTERN),
|
|
7632
7669
|
acceptedEventIds: array(string2())
|
|
7633
|
-
}).strict();
|
|
7670
|
+
}).strict().describe("Accepted event IDs must match the submitted batch exactly, without duplicates.");
|
|
7634
7671
|
var episodeCountSchema = number2().int().min(0).max(Number.MAX_SAFE_INTEGER);
|
|
7635
|
-
var collectorFrictionSummarySchema = object({ weekEpisodes: episodeCountSchema, dayEpisodes: episodeCountSchema }).strict().refine((summary) => summary.dayEpisodes <= summary.weekEpisodes, "The last day cannot have more friction episodes than the last week.");
|
|
7672
|
+
var collectorFrictionSummarySchema = object({ weekEpisodes: episodeCountSchema, dayEpisodes: episodeCountSchema }).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.");
|
|
7636
7673
|
var sessionCountSchema = number2().int().min(0).max(1e6);
|
|
7637
7674
|
var collectorHistoryProgressSchema = object({
|
|
7638
7675
|
sessions: sessionCountSchema,
|
|
7639
7676
|
readSessions: sessionCountSchema,
|
|
7640
7677
|
complete: boolean2()
|
|
7641
|
-
}).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.");
|
|
7678
|
+
}).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.");
|
|
7642
7679
|
var collectorEnrollmentRequestSchema = object({
|
|
7643
|
-
deviceName: string2().trim().min(1).max(160),
|
|
7680
|
+
deviceName: string2().trim().min(1).max(160).describe("Trimmed before length validation."),
|
|
7644
7681
|
organizationId: string2().regex(UUID_PATTERN).optional(),
|
|
7645
7682
|
source: _enum(COLLECTOR_SOURCES).optional()
|
|
7646
7683
|
}).strict();
|
|
@@ -7771,6 +7808,129 @@ import { execFile as execFile2 } from "node:child_process";
|
|
|
7771
7808
|
import { createInterface } from "node:readline/promises";
|
|
7772
7809
|
import { promisify as promisify2 } from "node:util";
|
|
7773
7810
|
|
|
7811
|
+
// src/collector-fetch.ts
|
|
7812
|
+
import { randomUUID } from "node:crypto";
|
|
7813
|
+
import http from "node:http";
|
|
7814
|
+
import https from "node:https";
|
|
7815
|
+
|
|
7816
|
+
// src/logger.ts
|
|
7817
|
+
function writeLog(line, level) {
|
|
7818
|
+
if (level === "info") console.info(line);
|
|
7819
|
+
else console.error(line);
|
|
7820
|
+
}
|
|
7821
|
+
function createLogger(context, write = writeLog) {
|
|
7822
|
+
return (entry) => {
|
|
7823
|
+
write(
|
|
7824
|
+
JSON.stringify({
|
|
7825
|
+
...entry,
|
|
7826
|
+
...context,
|
|
7827
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7828
|
+
pid: process.pid,
|
|
7829
|
+
runtimeVersion: process.env.ITERATION_RUNTIME_VERSION ?? null
|
|
7830
|
+
}),
|
|
7831
|
+
entry.level
|
|
7832
|
+
);
|
|
7833
|
+
};
|
|
7834
|
+
}
|
|
7835
|
+
var collectorLogger = createLogger({ component: "collector" });
|
|
7836
|
+
|
|
7837
|
+
// src/collector-fetch.ts
|
|
7838
|
+
var MAX_RESPONSE_BYTES = 1024 * 1024;
|
|
7839
|
+
var NULL_BODY_STATUSES = /* @__PURE__ */ new Set([101, 103, 204, 205, 304]);
|
|
7840
|
+
var agents = {
|
|
7841
|
+
"http:": new http.Agent({ keepAlive: false }),
|
|
7842
|
+
"https:": new https.Agent({ keepAlive: false })
|
|
7843
|
+
};
|
|
7844
|
+
var sendCollectorRequest = (input, init = {}) => {
|
|
7845
|
+
const url2 = new URL(input);
|
|
7846
|
+
if (url2.protocol !== "https:" && url2.protocol !== "http:") {
|
|
7847
|
+
throw new TypeError(
|
|
7848
|
+
`Unsupported collector request protocol ${url2.protocol}`
|
|
7849
|
+
);
|
|
7850
|
+
}
|
|
7851
|
+
const body = init.body ?? null;
|
|
7852
|
+
const headers = { ...init.headers };
|
|
7853
|
+
if (body !== null) {
|
|
7854
|
+
headers["content-length"] = String(Buffer.byteLength(body, "utf8"));
|
|
7855
|
+
}
|
|
7856
|
+
const transport = url2.protocol === "https:" ? https : http;
|
|
7857
|
+
return new Promise((resolve, reject) => {
|
|
7858
|
+
const request2 = transport.request(
|
|
7859
|
+
url2,
|
|
7860
|
+
{
|
|
7861
|
+
method: init.method ?? "GET",
|
|
7862
|
+
headers,
|
|
7863
|
+
agent: agents[url2.protocol],
|
|
7864
|
+
signal: init.signal
|
|
7865
|
+
},
|
|
7866
|
+
(response) => {
|
|
7867
|
+
const chunks = [];
|
|
7868
|
+
let size = 0;
|
|
7869
|
+
response.on("data", (chunk) => {
|
|
7870
|
+
size += chunk.byteLength;
|
|
7871
|
+
if (size > MAX_RESPONSE_BYTES) {
|
|
7872
|
+
response.destroy(
|
|
7873
|
+
new Error("Iteration returned an oversized response.")
|
|
7874
|
+
);
|
|
7875
|
+
return;
|
|
7876
|
+
}
|
|
7877
|
+
chunks.push(chunk);
|
|
7878
|
+
});
|
|
7879
|
+
response.on("error", reject);
|
|
7880
|
+
response.on("end", () => {
|
|
7881
|
+
const status2 = response.statusCode ?? 0;
|
|
7882
|
+
const responseHeaders = new Headers();
|
|
7883
|
+
for (const [name, value] of Object.entries(response.headers)) {
|
|
7884
|
+
if (value === void 0) continue;
|
|
7885
|
+
for (const item of Array.isArray(value) ? value : [value]) {
|
|
7886
|
+
responseHeaders.append(name, item);
|
|
7887
|
+
}
|
|
7888
|
+
}
|
|
7889
|
+
resolve(
|
|
7890
|
+
new Response(
|
|
7891
|
+
NULL_BODY_STATUSES.has(status2) ? null : Buffer.concat(chunks),
|
|
7892
|
+
{ status: status2, headers: responseHeaders }
|
|
7893
|
+
)
|
|
7894
|
+
);
|
|
7895
|
+
});
|
|
7896
|
+
}
|
|
7897
|
+
);
|
|
7898
|
+
request2.on("error", reject);
|
|
7899
|
+
request2.end(body ?? void 0);
|
|
7900
|
+
});
|
|
7901
|
+
};
|
|
7902
|
+
var collectorFetch = async (input, init = {}) => {
|
|
7903
|
+
const requestId = randomUUID();
|
|
7904
|
+
const started = performance.now();
|
|
7905
|
+
const headers = new Headers(init.headers);
|
|
7906
|
+
headers.set("X-Request-ID", requestId);
|
|
7907
|
+
try {
|
|
7908
|
+
const response = await sendCollectorRequest(input, {
|
|
7909
|
+
...init,
|
|
7910
|
+
headers: Object.fromEntries(headers)
|
|
7911
|
+
});
|
|
7912
|
+
collectorLogger({
|
|
7913
|
+
level: response.ok ? "info" : "warning",
|
|
7914
|
+
operation: "http_request",
|
|
7915
|
+
requestId,
|
|
7916
|
+
method: init.method ?? "GET",
|
|
7917
|
+
status: response.status,
|
|
7918
|
+
durationMs: Math.round(performance.now() - started)
|
|
7919
|
+
});
|
|
7920
|
+
return response;
|
|
7921
|
+
} catch (error2) {
|
|
7922
|
+
collectorLogger({
|
|
7923
|
+
level: "error",
|
|
7924
|
+
operation: "http_request",
|
|
7925
|
+
requestId,
|
|
7926
|
+
method: init.method ?? "GET",
|
|
7927
|
+
errorName: error2 instanceof Error ? error2.name : "UnknownError",
|
|
7928
|
+
durationMs: Math.round(performance.now() - started)
|
|
7929
|
+
});
|
|
7930
|
+
throw error2;
|
|
7931
|
+
}
|
|
7932
|
+
};
|
|
7933
|
+
|
|
7774
7934
|
// src/windows-powershell.ts
|
|
7775
7935
|
import { execFile as execFileCallback } from "node:child_process";
|
|
7776
7936
|
import path from "node:path";
|
|
@@ -7878,9 +8038,12 @@ async function responseJson(response) {
|
|
|
7878
8038
|
}
|
|
7879
8039
|
}
|
|
7880
8040
|
async function loadOAuthConfiguration(endpoint) {
|
|
7881
|
-
const response = await
|
|
7882
|
-
|
|
7883
|
-
|
|
8041
|
+
const response = await collectorFetch(
|
|
8042
|
+
apiRoute(endpoint, "./collector-auth/config"),
|
|
8043
|
+
{
|
|
8044
|
+
signal: AbortSignal.timeout(1e4)
|
|
8045
|
+
}
|
|
8046
|
+
);
|
|
7884
8047
|
const body = await responseJson(response);
|
|
7885
8048
|
if (!response.ok) {
|
|
7886
8049
|
throw new Error(
|
|
@@ -8036,15 +8199,18 @@ async function enrollDevice(endpoint, accessToken, deviceName, organizationId, s
|
|
|
8036
8199
|
const body = { deviceName };
|
|
8037
8200
|
if (source !== "codex") body.source = source;
|
|
8038
8201
|
if (organizationId !== null) body.organizationId = organizationId;
|
|
8039
|
-
const response = await
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
8043
|
-
|
|
8044
|
-
|
|
8045
|
-
|
|
8046
|
-
|
|
8047
|
-
|
|
8202
|
+
const response = await collectorFetch(
|
|
8203
|
+
apiRoute(endpoint, "./collector-auth/enroll"),
|
|
8204
|
+
{
|
|
8205
|
+
method: "POST",
|
|
8206
|
+
headers: {
|
|
8207
|
+
Authorization: "Bearer " + accessToken,
|
|
8208
|
+
"Content-Type": "application/json"
|
|
8209
|
+
},
|
|
8210
|
+
body: JSON.stringify(body),
|
|
8211
|
+
signal: AbortSignal.timeout(15e3)
|
|
8212
|
+
}
|
|
8213
|
+
);
|
|
8048
8214
|
const payload = await responseJson(response);
|
|
8049
8215
|
const parsed = parseCollectorEnrollmentResponse(payload);
|
|
8050
8216
|
if (response.ok && parsed.success) return parsed.value;
|
|
@@ -8824,8 +8990,8 @@ import { promisify as promisify5 } from "node:util";
|
|
|
8824
8990
|
import { execFile as execFileCallback3 } from "node:child_process";
|
|
8825
8991
|
import { createHash as createHash2 } from "node:crypto";
|
|
8826
8992
|
import { createWriteStream, promises as fs4 } from "node:fs";
|
|
8827
|
-
import
|
|
8828
|
-
import
|
|
8993
|
+
import http2 from "node:http";
|
|
8994
|
+
import https2 from "node:https";
|
|
8829
8995
|
import path4 from "node:path";
|
|
8830
8996
|
import { pipeline } from "node:stream/promises";
|
|
8831
8997
|
import { Transform } from "node:stream";
|
|
@@ -8871,7 +9037,7 @@ function validateRegistryUrl(value) {
|
|
|
8871
9037
|
return url2;
|
|
8872
9038
|
}
|
|
8873
9039
|
function request(url2, accept, signal) {
|
|
8874
|
-
const transport = url2.protocol === "https:" ?
|
|
9040
|
+
const transport = url2.protocol === "https:" ? https2 : http2;
|
|
8875
9041
|
return new Promise((resolve, reject) => {
|
|
8876
9042
|
const outgoing = transport.request(
|
|
8877
9043
|
url2,
|
|
@@ -9859,74 +10025,6 @@ async function backgroundServiceUsesRuntime(directory, source) {
|
|
|
9859
10025
|
) === true;
|
|
9860
10026
|
}
|
|
9861
10027
|
|
|
9862
|
-
// src/collector-fetch.ts
|
|
9863
|
-
import http2 from "node:http";
|
|
9864
|
-
import https2 from "node:https";
|
|
9865
|
-
var MAX_RESPONSE_BYTES = 1024 * 1024;
|
|
9866
|
-
var NULL_BODY_STATUSES = /* @__PURE__ */ new Set([101, 103, 204, 205, 304]);
|
|
9867
|
-
var agents = {
|
|
9868
|
-
"http:": new http2.Agent({ keepAlive: false }),
|
|
9869
|
-
"https:": new https2.Agent({ keepAlive: false })
|
|
9870
|
-
};
|
|
9871
|
-
var collectorFetch = (input, init = {}) => {
|
|
9872
|
-
const url2 = new URL(input);
|
|
9873
|
-
if (url2.protocol !== "https:" && url2.protocol !== "http:") {
|
|
9874
|
-
throw new TypeError(
|
|
9875
|
-
`Unsupported collector request protocol ${url2.protocol}`
|
|
9876
|
-
);
|
|
9877
|
-
}
|
|
9878
|
-
const body = init.body ?? null;
|
|
9879
|
-
const headers = { ...init.headers };
|
|
9880
|
-
if (body !== null) {
|
|
9881
|
-
headers["content-length"] = String(Buffer.byteLength(body, "utf8"));
|
|
9882
|
-
}
|
|
9883
|
-
const transport = url2.protocol === "https:" ? https2 : http2;
|
|
9884
|
-
return new Promise((resolve, reject) => {
|
|
9885
|
-
const request2 = transport.request(
|
|
9886
|
-
url2,
|
|
9887
|
-
{
|
|
9888
|
-
method: init.method ?? "GET",
|
|
9889
|
-
headers,
|
|
9890
|
-
agent: agents[url2.protocol],
|
|
9891
|
-
signal: init.signal
|
|
9892
|
-
},
|
|
9893
|
-
(response) => {
|
|
9894
|
-
const chunks = [];
|
|
9895
|
-
let size = 0;
|
|
9896
|
-
response.on("data", (chunk) => {
|
|
9897
|
-
size += chunk.byteLength;
|
|
9898
|
-
if (size > MAX_RESPONSE_BYTES) {
|
|
9899
|
-
response.destroy(
|
|
9900
|
-
new Error("Iteration returned an oversized response.")
|
|
9901
|
-
);
|
|
9902
|
-
return;
|
|
9903
|
-
}
|
|
9904
|
-
chunks.push(chunk);
|
|
9905
|
-
});
|
|
9906
|
-
response.on("error", reject);
|
|
9907
|
-
response.on("end", () => {
|
|
9908
|
-
const status2 = response.statusCode ?? 0;
|
|
9909
|
-
const responseHeaders = new Headers();
|
|
9910
|
-
for (const [name, value] of Object.entries(response.headers)) {
|
|
9911
|
-
if (value === void 0) continue;
|
|
9912
|
-
for (const item of Array.isArray(value) ? value : [value]) {
|
|
9913
|
-
responseHeaders.append(name, item);
|
|
9914
|
-
}
|
|
9915
|
-
}
|
|
9916
|
-
resolve(
|
|
9917
|
-
new Response(
|
|
9918
|
-
NULL_BODY_STATUSES.has(status2) ? null : Buffer.concat(chunks),
|
|
9919
|
-
{ status: status2, headers: responseHeaders }
|
|
9920
|
-
)
|
|
9921
|
-
);
|
|
9922
|
-
});
|
|
9923
|
-
}
|
|
9924
|
-
);
|
|
9925
|
-
request2.on("error", reject);
|
|
9926
|
-
request2.end(body ?? void 0);
|
|
9927
|
-
});
|
|
9928
|
-
};
|
|
9929
|
-
|
|
9930
10028
|
// src/turn-summary.ts
|
|
9931
10029
|
import { execFile as execFileCallback7 } from "node:child_process";
|
|
9932
10030
|
import { createHash as createHash3 } from "node:crypto";
|
|
@@ -9936,7 +10034,7 @@ import { promisify as promisify8 } from "node:util";
|
|
|
9936
10034
|
import { crc32 } from "node:zlib";
|
|
9937
10035
|
|
|
9938
10036
|
// src/claude-git-hook.ts
|
|
9939
|
-
import { randomUUID } from "node:crypto";
|
|
10037
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
9940
10038
|
import { promises as fs10 } from "node:fs";
|
|
9941
10039
|
import path9 from "node:path";
|
|
9942
10040
|
|
|
@@ -10058,7 +10156,7 @@ async function captureClaudeGit(dataDirectory2) {
|
|
|
10058
10156
|
(name2) => name2.endsWith(".jsonl")
|
|
10059
10157
|
);
|
|
10060
10158
|
if (files.length >= 1e4) throw new Error("Claude Git hook queue is full.");
|
|
10061
|
-
const name =
|
|
10159
|
+
const name = randomUUID2();
|
|
10062
10160
|
const temporary = path9.join(directory, `${name}.tmp`);
|
|
10063
10161
|
const filename = path9.join(directory, `${name}.jsonl`);
|
|
10064
10162
|
try {
|
|
@@ -10096,7 +10194,7 @@ async function claudeGitHookFiles(dataDirectory2) {
|
|
|
10096
10194
|
async function recordClaudeGitHookFailure(dataDirectory2) {
|
|
10097
10195
|
const filename = claudeGitHookPaths(dataDirectory2).failure;
|
|
10098
10196
|
await ensurePrivateDirectory(path9.dirname(filename));
|
|
10099
|
-
const temporary = `${filename}.${
|
|
10197
|
+
const temporary = `${filename}.${randomUUID2()}.tmp`;
|
|
10100
10198
|
await fs10.writeFile(temporary, `${JSON.stringify({ atMs: Date.now() })}
|
|
10101
10199
|
`, {
|
|
10102
10200
|
mode: 384
|
|
@@ -16651,14 +16749,12 @@ async function connectNewDetectedSources(anchor2, consent) {
|
|
|
16651
16749
|
try {
|
|
16652
16750
|
if (source !== "cursor") await installTurnSummary(config2);
|
|
16653
16751
|
} catch (error2) {
|
|
16654
|
-
|
|
16655
|
-
|
|
16656
|
-
|
|
16657
|
-
|
|
16658
|
-
|
|
16659
|
-
|
|
16660
|
-
})
|
|
16661
|
-
);
|
|
16752
|
+
collectorLogger({
|
|
16753
|
+
level: "warning",
|
|
16754
|
+
source,
|
|
16755
|
+
operation: "install_turn_summary",
|
|
16756
|
+
message: error2 instanceof Error ? error2.message : "The turn summary could not be installed."
|
|
16757
|
+
});
|
|
16662
16758
|
}
|
|
16663
16759
|
}
|
|
16664
16760
|
return connected;
|
|
@@ -16798,12 +16894,17 @@ function parseClaudeLine(line, filename, byteOffset, repositoryUrl = gitReposito
|
|
|
16798
16894
|
const record3 = object2(decoded);
|
|
16799
16895
|
if (record3 === null) return { events: [], gaps: ["record_is_not_an_object"] };
|
|
16800
16896
|
const recordType = string4(record3.type);
|
|
16801
|
-
const
|
|
16802
|
-
|
|
16803
|
-
|
|
16804
|
-
|
|
16805
|
-
|
|
16806
|
-
|
|
16897
|
+
const identityGap = () => {
|
|
16898
|
+
const sourceSessionId = string4(record3.sessionId);
|
|
16899
|
+
if (identity.agentId !== null && sourceSessionId === null || sourceSessionId !== null && `claude:${sourceSessionId}` !== (identity.parentSessionId ?? identity.sessionId)) {
|
|
16900
|
+
return "session_id_does_not_match_path";
|
|
16901
|
+
}
|
|
16902
|
+
if (identity.agentId !== null && string4(record3.agentId) !== identity.agentId)
|
|
16903
|
+
return "agent_id_does_not_match_path";
|
|
16904
|
+
return null;
|
|
16905
|
+
};
|
|
16906
|
+
const gap = identityGap();
|
|
16907
|
+
if (gap !== null) return { events: [], gaps: [gap] };
|
|
16807
16908
|
const gaps = [];
|
|
16808
16909
|
if (record3.timestamp !== void 0 && timestamp(record3) === null)
|
|
16809
16910
|
gaps.push("invalid_source_timestamp");
|
|
@@ -16841,7 +16942,7 @@ function parseClaudeLine(line, filename, byteOffset, repositoryUrl = gitReposito
|
|
|
16841
16942
|
}
|
|
16842
16943
|
});
|
|
16843
16944
|
}
|
|
16844
|
-
|
|
16945
|
+
const appendUserMessage = () => {
|
|
16845
16946
|
const message = object2(record3.message);
|
|
16846
16947
|
const content = message === null ? null : string4(message.content);
|
|
16847
16948
|
if (message === null) gaps.push("user_without_message");
|
|
@@ -16889,7 +16990,8 @@ function parseClaudeLine(line, filename, byteOffset, repositoryUrl = gitReposito
|
|
|
16889
16990
|
}
|
|
16890
16991
|
}
|
|
16891
16992
|
} else gaps.push("unsupported_user_content");
|
|
16892
|
-
}
|
|
16993
|
+
};
|
|
16994
|
+
const appendAssistantMessage = () => {
|
|
16893
16995
|
const message = object2(record3.message);
|
|
16894
16996
|
if (message === null || !Array.isArray(message.content))
|
|
16895
16997
|
gaps.push("assistant_without_content_blocks");
|
|
@@ -16942,7 +17044,10 @@ function parseClaudeLine(line, filename, byteOffset, repositoryUrl = gitReposito
|
|
|
16942
17044
|
);
|
|
16943
17045
|
}
|
|
16944
17046
|
}
|
|
16945
|
-
}
|
|
17047
|
+
};
|
|
17048
|
+
if (recordType === "user") appendUserMessage();
|
|
17049
|
+
else if (recordType === "assistant") appendAssistantMessage();
|
|
17050
|
+
else if (recordType !== null && CONTEXT_RECORD_TYPES.has(recordType)) {
|
|
16946
17051
|
const attachment = object2(record3.attachment);
|
|
16947
17052
|
events.push(
|
|
16948
17053
|
event(identity, record3, byteOffset, 1, "source_record", {
|
|
@@ -17060,8 +17165,7 @@ function parseCursorTranscriptLine(line, filename, byteOffset) {
|
|
|
17060
17165
|
}
|
|
17061
17166
|
});
|
|
17062
17167
|
}
|
|
17063
|
-
const
|
|
17064
|
-
if (role === "user" || role === "assistant") {
|
|
17168
|
+
const appendMessage = (role2) => {
|
|
17065
17169
|
const message = record2(source.message);
|
|
17066
17170
|
if (message === null || !Array.isArray(message.content))
|
|
17067
17171
|
gaps.push("cursor_message_without_content");
|
|
@@ -17073,7 +17177,7 @@ function parseCursorTranscriptLine(line, filename, byteOffset) {
|
|
|
17073
17177
|
continue;
|
|
17074
17178
|
}
|
|
17075
17179
|
const type = string5(block.type);
|
|
17076
|
-
const eventType = type === "tool_use" ? "tool_call" : type === "thinking" ? "reasoning" : type === "text" ?
|
|
17180
|
+
const eventType = type === "tool_use" ? "tool_call" : type === "thinking" ? "reasoning" : type === "text" ? role2 === "user" ? "user_message" : "assistant_message" : "source_record";
|
|
17077
17181
|
events.push(
|
|
17078
17182
|
event2(
|
|
17079
17183
|
conversationId,
|
|
@@ -17095,7 +17199,10 @@ function parseCursorTranscriptLine(line, filename, byteOffset) {
|
|
|
17095
17199
|
gaps.push(`unsupported_cursor_content_block:${type ?? "unknown"}`);
|
|
17096
17200
|
}
|
|
17097
17201
|
}
|
|
17098
|
-
}
|
|
17202
|
+
};
|
|
17203
|
+
const role = string5(source.role);
|
|
17204
|
+
if (role === "user" || role === "assistant") appendMessage(role);
|
|
17205
|
+
else if (source.type === "turn_ended") {
|
|
17099
17206
|
events.push(
|
|
17100
17207
|
event2(
|
|
17101
17208
|
conversationId,
|
|
@@ -17130,45 +17237,64 @@ function parseCursorHookLine(line, filename) {
|
|
|
17130
17237
|
const hook = wrapper === null ? null : string5(wrapper.hook);
|
|
17131
17238
|
const capturedAt = wrapper === null ? null : string5(wrapper.capturedAt);
|
|
17132
17239
|
if (wrapper?.kind === "subagent_transcript_chunk") {
|
|
17133
|
-
|
|
17134
|
-
|
|
17135
|
-
|
|
17136
|
-
if (conversationId === null || !UUID.test(conversationId) || capturedAt === null || Number.isNaN(Date.parse(capturedAt)) || subagent === null || part === null || totalParts === null || part < 1 || part > totalParts || string5(subagent.base64) === null)
|
|
17137
|
-
return { events: [], gaps: ["invalid_cursor_subagent_transcript_chunk"] };
|
|
17138
|
-
const transcriptBytes2 = nonnegativeInteger(wrapper.transcriptBytes) ?? 0;
|
|
17139
|
-
const candidate = event2(
|
|
17240
|
+
return parseSubagentChunk(
|
|
17241
|
+
wrapper,
|
|
17242
|
+
input,
|
|
17140
17243
|
conversationId,
|
|
17141
|
-
|
|
17142
|
-
|
|
17143
|
-
part,
|
|
17144
|
-
"source_record",
|
|
17145
|
-
{
|
|
17146
|
-
sourceType: "cursor_subagent_transcript",
|
|
17147
|
-
text: string5(subagent.text),
|
|
17148
|
-
base64: string5(subagent.base64),
|
|
17149
|
-
path: string5(subagent.path),
|
|
17150
|
-
part,
|
|
17151
|
-
totalParts,
|
|
17152
|
-
parent_conversation_id: string5(input?.parent_conversation_id),
|
|
17153
|
-
subagent_id: string5(input?.subagent_id)
|
|
17154
|
-
},
|
|
17155
|
-
null,
|
|
17156
|
-
capturedAt
|
|
17157
|
-
);
|
|
17158
|
-
const hash = candidate.eventId.split(":").at(-1);
|
|
17159
|
-
return validated(
|
|
17160
|
-
[
|
|
17161
|
-
{
|
|
17162
|
-
...candidate,
|
|
17163
|
-
sequence: transcriptBytes2 * 1e3 + 500,
|
|
17164
|
-
eventId: `${String(Date.parse(capturedAt)).padStart(13, "0")}:${String(2e3 + part).padStart(4, "0")}:${hash}`
|
|
17165
|
-
}
|
|
17166
|
-
],
|
|
17167
|
-
[]
|
|
17244
|
+
capturedAt,
|
|
17245
|
+
filename
|
|
17168
17246
|
);
|
|
17169
17247
|
}
|
|
17170
17248
|
if (input === null || conversationId === null || !UUID.test(conversationId) || hook === null || capturedAt === null || Number.isNaN(Date.parse(capturedAt)))
|
|
17171
17249
|
return { events: [], gaps: ["invalid_cursor_hook_record"] };
|
|
17250
|
+
return parseHookEvents(
|
|
17251
|
+
wrapper,
|
|
17252
|
+
input,
|
|
17253
|
+
conversationId,
|
|
17254
|
+
hook,
|
|
17255
|
+
capturedAt,
|
|
17256
|
+
filename
|
|
17257
|
+
);
|
|
17258
|
+
}
|
|
17259
|
+
function parseSubagentChunk(wrapper, input, conversationId, capturedAt, filename) {
|
|
17260
|
+
const subagent = record2(wrapper.subagent);
|
|
17261
|
+
const part = nonnegativeInteger(subagent?.part);
|
|
17262
|
+
const totalParts = nonnegativeInteger(subagent?.totalParts);
|
|
17263
|
+
if (conversationId === null || !UUID.test(conversationId) || capturedAt === null || Number.isNaN(Date.parse(capturedAt)) || subagent === null || part === null || totalParts === null || part < 1 || part > totalParts || string5(subagent.base64) === null)
|
|
17264
|
+
return { events: [], gaps: ["invalid_cursor_subagent_transcript_chunk"] };
|
|
17265
|
+
const transcriptBytes = nonnegativeInteger(wrapper.transcriptBytes) ?? 0;
|
|
17266
|
+
const candidate = event2(
|
|
17267
|
+
conversationId,
|
|
17268
|
+
filename,
|
|
17269
|
+
transcriptBytes,
|
|
17270
|
+
part,
|
|
17271
|
+
"source_record",
|
|
17272
|
+
{
|
|
17273
|
+
sourceType: "cursor_subagent_transcript",
|
|
17274
|
+
text: string5(subagent.text),
|
|
17275
|
+
base64: string5(subagent.base64),
|
|
17276
|
+
path: string5(subagent.path),
|
|
17277
|
+
part,
|
|
17278
|
+
totalParts,
|
|
17279
|
+
parent_conversation_id: string5(input?.parent_conversation_id),
|
|
17280
|
+
subagent_id: string5(input?.subagent_id)
|
|
17281
|
+
},
|
|
17282
|
+
null,
|
|
17283
|
+
capturedAt
|
|
17284
|
+
);
|
|
17285
|
+
const hash = candidate.eventId.split(":").at(-1);
|
|
17286
|
+
return validated(
|
|
17287
|
+
[
|
|
17288
|
+
{
|
|
17289
|
+
...candidate,
|
|
17290
|
+
sequence: transcriptBytes * 1e3 + 500,
|
|
17291
|
+
eventId: `${String(Date.parse(capturedAt)).padStart(13, "0")}:${String(2e3 + part).padStart(4, "0")}:${hash}`
|
|
17292
|
+
}
|
|
17293
|
+
],
|
|
17294
|
+
[]
|
|
17295
|
+
);
|
|
17296
|
+
}
|
|
17297
|
+
function parseHookEvents(wrapper, input, conversationId, hook, capturedAt, filename) {
|
|
17172
17298
|
const transcriptBytes = wrapper?.transcriptBytes;
|
|
17173
17299
|
const sequence = nonnegativeInteger(transcriptBytes) ?? 0;
|
|
17174
17300
|
const events = [];
|
|
@@ -17243,82 +17369,85 @@ function parseCursorHookLine(line, filename) {
|
|
|
17243
17369
|
)
|
|
17244
17370
|
);
|
|
17245
17371
|
}
|
|
17246
|
-
const
|
|
17247
|
-
|
|
17248
|
-
|
|
17249
|
-
event2(
|
|
17250
|
-
conversationId,
|
|
17251
|
-
filename,
|
|
17252
|
-
sequence,
|
|
17253
|
-
next,
|
|
17254
|
-
"tool_call",
|
|
17255
|
-
{
|
|
17256
|
-
name: string5(input.tool_name),
|
|
17257
|
-
input: input.tool_input ?? null
|
|
17258
|
-
},
|
|
17259
|
-
string5(input.tool_use_id),
|
|
17260
|
-
capturedAt
|
|
17261
|
-
)
|
|
17262
|
-
);
|
|
17263
|
-
}
|
|
17264
|
-
if (hook === "postToolUse" || hook === "postToolUseFailure") {
|
|
17265
|
-
const output = string5(input.tool_output) ?? string5(input.error_message) ?? string5(input.error) ?? (input.tool_output === void 0 && input.error === void 0 ? null : JSON.stringify(input.tool_output ?? input.error));
|
|
17266
|
-
const chunks = output === null ? [null] : splitText(output);
|
|
17267
|
-
for (const [index, text] of chunks.entries())
|
|
17372
|
+
const appendHookPayload = () => {
|
|
17373
|
+
const next = events.length + 1;
|
|
17374
|
+
if (hook === "preToolUse") {
|
|
17268
17375
|
events.push(
|
|
17269
17376
|
event2(
|
|
17270
17377
|
conversationId,
|
|
17271
17378
|
filename,
|
|
17272
17379
|
sequence,
|
|
17273
|
-
next
|
|
17274
|
-
"
|
|
17380
|
+
next,
|
|
17381
|
+
"tool_call",
|
|
17275
17382
|
{
|
|
17276
|
-
text,
|
|
17277
17383
|
name: string5(input.tool_name),
|
|
17278
|
-
|
|
17279
|
-
totalParts: chunks.length
|
|
17384
|
+
input: input.tool_input ?? null
|
|
17280
17385
|
},
|
|
17281
17386
|
string5(input.tool_use_id),
|
|
17282
17387
|
capturedAt
|
|
17283
17388
|
)
|
|
17284
17389
|
);
|
|
17285
|
-
|
|
17286
|
-
|
|
17287
|
-
|
|
17288
|
-
|
|
17289
|
-
|
|
17290
|
-
|
|
17291
|
-
|
|
17292
|
-
|
|
17293
|
-
|
|
17294
|
-
|
|
17295
|
-
|
|
17296
|
-
|
|
17297
|
-
|
|
17298
|
-
|
|
17299
|
-
|
|
17300
|
-
|
|
17301
|
-
|
|
17302
|
-
|
|
17303
|
-
|
|
17304
|
-
|
|
17305
|
-
|
|
17306
|
-
|
|
17307
|
-
|
|
17308
|
-
|
|
17309
|
-
|
|
17310
|
-
|
|
17311
|
-
|
|
17312
|
-
|
|
17313
|
-
|
|
17314
|
-
|
|
17315
|
-
|
|
17316
|
-
|
|
17317
|
-
|
|
17318
|
-
|
|
17319
|
-
|
|
17320
|
-
|
|
17321
|
-
|
|
17390
|
+
}
|
|
17391
|
+
if (hook === "postToolUse" || hook === "postToolUseFailure") {
|
|
17392
|
+
const output = hookToolOutput(input);
|
|
17393
|
+
const chunks = output === null ? [null] : splitText(output);
|
|
17394
|
+
for (const [index, text] of chunks.entries())
|
|
17395
|
+
events.push(
|
|
17396
|
+
event2(
|
|
17397
|
+
conversationId,
|
|
17398
|
+
filename,
|
|
17399
|
+
sequence,
|
|
17400
|
+
next + index,
|
|
17401
|
+
"tool_result",
|
|
17402
|
+
{
|
|
17403
|
+
text,
|
|
17404
|
+
name: string5(input.tool_name),
|
|
17405
|
+
part: index + 1,
|
|
17406
|
+
totalParts: chunks.length
|
|
17407
|
+
},
|
|
17408
|
+
string5(input.tool_use_id),
|
|
17409
|
+
capturedAt
|
|
17410
|
+
)
|
|
17411
|
+
);
|
|
17412
|
+
} else if (hook === "afterAgentThought") {
|
|
17413
|
+
events.push(
|
|
17414
|
+
event2(
|
|
17415
|
+
conversationId,
|
|
17416
|
+
filename,
|
|
17417
|
+
sequence,
|
|
17418
|
+
next,
|
|
17419
|
+
"reasoning",
|
|
17420
|
+
{
|
|
17421
|
+
text: string5(input.text) ?? string5(input.thought)
|
|
17422
|
+
},
|
|
17423
|
+
null,
|
|
17424
|
+
capturedAt
|
|
17425
|
+
)
|
|
17426
|
+
);
|
|
17427
|
+
} else if (hook === "afterAgentResponse" && (input.usage !== void 0 || input.input_tokens !== void 0 || input.output_tokens !== void 0)) {
|
|
17428
|
+
events.push(
|
|
17429
|
+
event2(
|
|
17430
|
+
conversationId,
|
|
17431
|
+
filename,
|
|
17432
|
+
sequence,
|
|
17433
|
+
next,
|
|
17434
|
+
"token_usage_record",
|
|
17435
|
+
{
|
|
17436
|
+
model: string5(input.model),
|
|
17437
|
+
usage: input.usage ?? {
|
|
17438
|
+
input_tokens: input.input_tokens ?? null,
|
|
17439
|
+
output_tokens: input.output_tokens ?? null,
|
|
17440
|
+
cache_read_tokens: input.cache_read_tokens ?? null,
|
|
17441
|
+
cache_write_tokens: input.cache_write_tokens ?? null
|
|
17442
|
+
}
|
|
17443
|
+
},
|
|
17444
|
+
null,
|
|
17445
|
+
capturedAt
|
|
17446
|
+
)
|
|
17447
|
+
);
|
|
17448
|
+
}
|
|
17449
|
+
};
|
|
17450
|
+
appendHookPayload();
|
|
17322
17451
|
const capturedAtMs = Date.parse(capturedAt);
|
|
17323
17452
|
const ordered = events.map((candidate, index) => {
|
|
17324
17453
|
const digest3 = candidate.eventId.split(":").at(-1);
|
|
@@ -17333,6 +17462,9 @@ function parseCursorHookLine(line, filename) {
|
|
|
17333
17462
|
});
|
|
17334
17463
|
return validated(ordered, []);
|
|
17335
17464
|
}
|
|
17465
|
+
function hookToolOutput(input) {
|
|
17466
|
+
return string5(input.tool_output) ?? string5(input.error_message) ?? string5(input.error) ?? (input.tool_output === void 0 && input.error === void 0 ? null : JSON.stringify(input.tool_output ?? input.error));
|
|
17467
|
+
}
|
|
17336
17468
|
function splitText(value) {
|
|
17337
17469
|
if (value.length === 0) return [""];
|
|
17338
17470
|
const chunks = [];
|
|
@@ -17569,83 +17701,86 @@ function parseRolloutLine(line, filename, byteOffset) {
|
|
|
17569
17701
|
);
|
|
17570
17702
|
}
|
|
17571
17703
|
if (recordType === "event_msg" && payload !== null) {
|
|
17572
|
-
|
|
17573
|
-
|
|
17574
|
-
|
|
17575
|
-
|
|
17576
|
-
|
|
17577
|
-
|
|
17578
|
-
|
|
17579
|
-
|
|
17580
|
-
|
|
17581
|
-
|
|
17582
|
-
}
|
|
17583
|
-
if (messageType === "agent_message") {
|
|
17584
|
-
return capturedEvent(
|
|
17585
|
-
record3,
|
|
17586
|
-
filename,
|
|
17587
|
-
byteOffset,
|
|
17588
|
-
"assistant_message",
|
|
17589
|
-
payload,
|
|
17590
|
-
null
|
|
17591
|
-
);
|
|
17592
|
-
}
|
|
17593
|
-
if (oneOf(messageType, [
|
|
17594
|
-
"mcp_tool_call_end",
|
|
17595
|
-
"web_search_end",
|
|
17596
|
-
"image_generation_end",
|
|
17597
|
-
"patch_apply_end"
|
|
17598
|
-
])) {
|
|
17599
|
-
return capturedEvent(
|
|
17600
|
-
record3,
|
|
17601
|
-
filename,
|
|
17602
|
-
byteOffset,
|
|
17603
|
-
"tool_result",
|
|
17604
|
-
payload,
|
|
17605
|
-
callIdFrom(payload)
|
|
17606
|
-
);
|
|
17607
|
-
}
|
|
17608
|
-
if (oneOf(messageType, IGNORED_EVENT_MESSAGES)) return { kind: "ignored" };
|
|
17609
|
-
if (oneOf(messageType, EVENT_CONTEXT_TYPES)) {
|
|
17610
|
-
const ordinal = sourceOrdinal(record3.ordinal);
|
|
17611
|
-
const run = messageType === "thread_settings_applied" && parseJsonString(payload.thread_id) === sessionFromPath(filename).slice(-36) && ordinal !== null ? { kind: "start", sourceOrdinal: ordinal } : void 0;
|
|
17612
|
-
return capturedEvent(
|
|
17613
|
-
record3,
|
|
17614
|
-
filename,
|
|
17615
|
-
byteOffset,
|
|
17616
|
-
messageType,
|
|
17617
|
-
payload,
|
|
17618
|
-
null,
|
|
17619
|
-
run
|
|
17620
|
-
);
|
|
17621
|
-
}
|
|
17622
|
-
if (messageType !== "item_completed") {
|
|
17623
|
-
return {
|
|
17624
|
-
kind: "gap",
|
|
17625
|
-
reason: `unsupported_event_message:${messageType ?? "unknown"}`
|
|
17626
|
-
};
|
|
17627
|
-
}
|
|
17628
|
-
const item = parseJsonRecord(payload.item);
|
|
17629
|
-
if (item === null)
|
|
17630
|
-
return { kind: "gap", reason: "item_completed_without_item" };
|
|
17631
|
-
const mapped = mapCompletedItem(item);
|
|
17632
|
-
if (mapped === null) {
|
|
17633
|
-
const itemType = parseJsonString(item.type) ?? "unknown";
|
|
17634
|
-
return { kind: "gap", reason: `unsupported_completed_item:${itemType}` };
|
|
17635
|
-
}
|
|
17704
|
+
return parseEventMessage(record3, payload, filename, byteOffset);
|
|
17705
|
+
}
|
|
17706
|
+
return {
|
|
17707
|
+
kind: "gap",
|
|
17708
|
+
reason: `unsupported_rollout_record:${recordType ?? "unknown"}`
|
|
17709
|
+
};
|
|
17710
|
+
}
|
|
17711
|
+
function parseEventMessage(record3, payload, filename, byteOffset) {
|
|
17712
|
+
const messageType = parseJsonString(payload.type);
|
|
17713
|
+
if (messageType === "user_message") {
|
|
17636
17714
|
return capturedEvent(
|
|
17637
17715
|
record3,
|
|
17638
17716
|
filename,
|
|
17639
17717
|
byteOffset,
|
|
17640
|
-
|
|
17641
|
-
|
|
17642
|
-
|
|
17718
|
+
"user_message",
|
|
17719
|
+
payload,
|
|
17720
|
+
null
|
|
17643
17721
|
);
|
|
17644
17722
|
}
|
|
17645
|
-
|
|
17646
|
-
|
|
17647
|
-
|
|
17648
|
-
|
|
17723
|
+
if (messageType === "agent_message") {
|
|
17724
|
+
return capturedEvent(
|
|
17725
|
+
record3,
|
|
17726
|
+
filename,
|
|
17727
|
+
byteOffset,
|
|
17728
|
+
"assistant_message",
|
|
17729
|
+
payload,
|
|
17730
|
+
null
|
|
17731
|
+
);
|
|
17732
|
+
}
|
|
17733
|
+
if (oneOf(messageType, [
|
|
17734
|
+
"mcp_tool_call_end",
|
|
17735
|
+
"web_search_end",
|
|
17736
|
+
"image_generation_end",
|
|
17737
|
+
"patch_apply_end"
|
|
17738
|
+
])) {
|
|
17739
|
+
return capturedEvent(
|
|
17740
|
+
record3,
|
|
17741
|
+
filename,
|
|
17742
|
+
byteOffset,
|
|
17743
|
+
"tool_result",
|
|
17744
|
+
payload,
|
|
17745
|
+
callIdFrom(payload)
|
|
17746
|
+
);
|
|
17747
|
+
}
|
|
17748
|
+
if (oneOf(messageType, IGNORED_EVENT_MESSAGES)) return { kind: "ignored" };
|
|
17749
|
+
if (oneOf(messageType, EVENT_CONTEXT_TYPES)) {
|
|
17750
|
+
const ordinal = sourceOrdinal(record3.ordinal);
|
|
17751
|
+
const run = messageType === "thread_settings_applied" && parseJsonString(payload.thread_id) === sessionFromPath(filename).slice(-36) && ordinal !== null ? { kind: "start", sourceOrdinal: ordinal } : void 0;
|
|
17752
|
+
return capturedEvent(
|
|
17753
|
+
record3,
|
|
17754
|
+
filename,
|
|
17755
|
+
byteOffset,
|
|
17756
|
+
messageType,
|
|
17757
|
+
payload,
|
|
17758
|
+
null,
|
|
17759
|
+
run
|
|
17760
|
+
);
|
|
17761
|
+
}
|
|
17762
|
+
if (messageType !== "item_completed") {
|
|
17763
|
+
return {
|
|
17764
|
+
kind: "gap",
|
|
17765
|
+
reason: `unsupported_event_message:${messageType ?? "unknown"}`
|
|
17766
|
+
};
|
|
17767
|
+
}
|
|
17768
|
+
const item = parseJsonRecord(payload.item);
|
|
17769
|
+
if (item === null)
|
|
17770
|
+
return { kind: "gap", reason: "item_completed_without_item" };
|
|
17771
|
+
const mapped = mapCompletedItem(item);
|
|
17772
|
+
if (mapped === null) {
|
|
17773
|
+
const itemType = parseJsonString(item.type) ?? "unknown";
|
|
17774
|
+
return { kind: "gap", reason: `unsupported_completed_item:${itemType}` };
|
|
17775
|
+
}
|
|
17776
|
+
return capturedEvent(
|
|
17777
|
+
record3,
|
|
17778
|
+
filename,
|
|
17779
|
+
byteOffset,
|
|
17780
|
+
mapped.eventType,
|
|
17781
|
+
item,
|
|
17782
|
+
mapped.callId
|
|
17783
|
+
);
|
|
17649
17784
|
}
|
|
17650
17785
|
|
|
17651
17786
|
// src/collector.ts
|
|
@@ -17669,15 +17804,15 @@ var TraceUploadError = class extends Error {
|
|
|
17669
17804
|
this.status = status2;
|
|
17670
17805
|
}
|
|
17671
17806
|
};
|
|
17672
|
-
function logGap(filename, offset, reason) {
|
|
17673
|
-
|
|
17674
|
-
|
|
17675
|
-
|
|
17676
|
-
|
|
17677
|
-
|
|
17678
|
-
|
|
17679
|
-
|
|
17680
|
-
);
|
|
17807
|
+
function logGap(source, filename, offset, reason) {
|
|
17808
|
+
collectorLogger({
|
|
17809
|
+
level: "warning",
|
|
17810
|
+
operation: "capture_gap",
|
|
17811
|
+
source,
|
|
17812
|
+
filename,
|
|
17813
|
+
offset,
|
|
17814
|
+
reason
|
|
17815
|
+
});
|
|
17681
17816
|
}
|
|
17682
17817
|
function parseSourceRecord(source, line, filename, byteOffset) {
|
|
17683
17818
|
if (source === "claude")
|
|
@@ -17716,11 +17851,37 @@ async function scanSourceOnce(config2, paths, signal) {
|
|
|
17716
17851
|
const queuedIds = await pendingEventIds(paths);
|
|
17717
17852
|
const tickIndexes = /* @__PURE__ */ new Map();
|
|
17718
17853
|
const events = [];
|
|
17854
|
+
const queueRecordEvents = async (parsedEvents, filename, byteOffset) => {
|
|
17855
|
+
for (const parsedEvent of parsedEvents) {
|
|
17856
|
+
const queuedAt = tickIndexes.get(parsedEvent.eventId);
|
|
17857
|
+
if (queuedAt !== void 0 && parsedEvent.eventType === "token_usage_record") {
|
|
17858
|
+
const event4 = await redactTraceEvent(parsedEvent);
|
|
17859
|
+
if (Buffer.byteLength(JSON.stringify(event4), "utf8") <= MAX_EVENT_BYTES)
|
|
17860
|
+
events[queuedAt] = event4;
|
|
17861
|
+
continue;
|
|
17862
|
+
}
|
|
17863
|
+
if (queuedIds.has(parsedEvent.eventId)) continue;
|
|
17864
|
+
const event3 = await redactTraceEvent(parsedEvent);
|
|
17865
|
+
const eventBytes = Buffer.byteLength(JSON.stringify(event3), "utf8");
|
|
17866
|
+
if (eventBytes > MAX_EVENT_BYTES) {
|
|
17867
|
+
logGap(
|
|
17868
|
+
source,
|
|
17869
|
+
filename,
|
|
17870
|
+
byteOffset,
|
|
17871
|
+
"normalized_event_exceeds_upload_limit"
|
|
17872
|
+
);
|
|
17873
|
+
} else {
|
|
17874
|
+
tickIndexes.set(event3.eventId, events.length);
|
|
17875
|
+
events.push(event3);
|
|
17876
|
+
queuedIds.add(event3.eventId);
|
|
17877
|
+
}
|
|
17878
|
+
}
|
|
17879
|
+
};
|
|
17719
17880
|
let remainingBudget = MAX_SCAN_BYTES_PER_TICK;
|
|
17720
17881
|
let caughtUp = true;
|
|
17721
17882
|
for (const filename of files) {
|
|
17722
17883
|
signal?.throwIfAborted();
|
|
17723
|
-
const recordLimit = source
|
|
17884
|
+
const recordLimit = isCursorHookFile(source, filename) ? 12 * 1024 * 1024 : MAX_RECORD_BYTES;
|
|
17724
17885
|
if (remainingBudget === 0) {
|
|
17725
17886
|
caughtUp = false;
|
|
17726
17887
|
break;
|
|
@@ -17728,7 +17889,7 @@ async function scanSourceOnce(config2, paths, signal) {
|
|
|
17728
17889
|
const size = listedSizes.get(filename) ?? (await fs15.stat(filename)).size;
|
|
17729
17890
|
let offset = cursors[filename] ?? 0;
|
|
17730
17891
|
if (offset > size) {
|
|
17731
|
-
logGap(filename, offset, "rollout_file_shrank_resetting_cursor");
|
|
17892
|
+
logGap(source, filename, offset, "rollout_file_shrank_resetting_cursor");
|
|
17732
17893
|
offset = 0;
|
|
17733
17894
|
skipping.delete(filename);
|
|
17734
17895
|
}
|
|
@@ -17764,59 +17925,51 @@ async function scanSourceOnce(config2, paths, signal) {
|
|
|
17764
17925
|
buffer = buffer.subarray(newline + 1);
|
|
17765
17926
|
skipping.delete(filename);
|
|
17766
17927
|
}
|
|
17767
|
-
|
|
17768
|
-
|
|
17769
|
-
|
|
17770
|
-
|
|
17771
|
-
|
|
17772
|
-
|
|
17773
|
-
|
|
17774
|
-
|
|
17775
|
-
|
|
17776
|
-
|
|
17777
|
-
|
|
17778
|
-
|
|
17779
|
-
|
|
17780
|
-
|
|
17781
|
-
|
|
17782
|
-
logGap(filename, byteOffset, "rollout_record_is_not_utf8");
|
|
17783
|
-
lineStart = index + 1;
|
|
17784
|
-
continue;
|
|
17785
|
-
}
|
|
17786
|
-
const parsed = parseSourceRecord(source, line, filename, byteOffset);
|
|
17787
|
-
for (const reason of parsed.gaps) logGap(filename, byteOffset, reason);
|
|
17788
|
-
for (const parsedEvent of parsed.events) {
|
|
17789
|
-
const queuedAt = tickIndexes.get(parsedEvent.eventId);
|
|
17790
|
-
if (queuedAt !== void 0 && parsedEvent.eventType === "token_usage_record") {
|
|
17791
|
-
const event4 = await redactTraceEvent(parsedEvent);
|
|
17792
|
-
if (Buffer.byteLength(JSON.stringify(event4), "utf8") <= MAX_EVENT_BYTES)
|
|
17793
|
-
events[queuedAt] = event4;
|
|
17928
|
+
const scanRecords = async () => {
|
|
17929
|
+
let lineStart = 0;
|
|
17930
|
+
for (let index = 0; index < buffer.length; index += 1) {
|
|
17931
|
+
if (buffer[index] !== 10) continue;
|
|
17932
|
+
let lineBytes = buffer.subarray(lineStart, index);
|
|
17933
|
+
if (lineBytes.at(-1) === 13) lineBytes = lineBytes.subarray(0, -1);
|
|
17934
|
+
const byteOffset = offset + lineStart;
|
|
17935
|
+
if (lineBytes.length > recordLimit) {
|
|
17936
|
+
logGap(
|
|
17937
|
+
source,
|
|
17938
|
+
filename,
|
|
17939
|
+
byteOffset,
|
|
17940
|
+
"rollout_record_exceeds_scan_limit"
|
|
17941
|
+
);
|
|
17942
|
+
lineStart = index + 1;
|
|
17794
17943
|
continue;
|
|
17795
17944
|
}
|
|
17796
|
-
|
|
17797
|
-
|
|
17798
|
-
|
|
17799
|
-
|
|
17800
|
-
logGap(filename, byteOffset, "
|
|
17801
|
-
|
|
17802
|
-
|
|
17803
|
-
events.push(event3);
|
|
17804
|
-
queuedIds.add(event3.eventId);
|
|
17945
|
+
let line;
|
|
17946
|
+
try {
|
|
17947
|
+
line = UTF8_DECODER.decode(lineBytes);
|
|
17948
|
+
} catch {
|
|
17949
|
+
logGap(source, filename, byteOffset, "rollout_record_is_not_utf8");
|
|
17950
|
+
lineStart = index + 1;
|
|
17951
|
+
continue;
|
|
17805
17952
|
}
|
|
17953
|
+
const parsed = parseSourceRecord(source, line, filename, byteOffset);
|
|
17954
|
+
for (const reason of parsed.gaps)
|
|
17955
|
+
logGap(source, filename, byteOffset, reason);
|
|
17956
|
+
await queueRecordEvents(parsed.events, filename, byteOffset);
|
|
17957
|
+
lineStart = index + 1;
|
|
17806
17958
|
}
|
|
17807
|
-
lineStart
|
|
17808
|
-
|
|
17809
|
-
|
|
17810
|
-
|
|
17811
|
-
|
|
17812
|
-
|
|
17813
|
-
|
|
17814
|
-
|
|
17815
|
-
|
|
17816
|
-
|
|
17817
|
-
|
|
17818
|
-
|
|
17819
|
-
|
|
17959
|
+
if (buffer.length - lineStart > recordLimit) {
|
|
17960
|
+
logGap(
|
|
17961
|
+
source,
|
|
17962
|
+
filename,
|
|
17963
|
+
offset + lineStart,
|
|
17964
|
+
"incomplete_rollout_record_exceeds_scan_limit"
|
|
17965
|
+
);
|
|
17966
|
+
skipping.add(filename);
|
|
17967
|
+
lineStart = buffer.length;
|
|
17968
|
+
}
|
|
17969
|
+
cursors[filename] = offset + lineStart;
|
|
17970
|
+
};
|
|
17971
|
+
await scanRecords();
|
|
17972
|
+
if (isHookFile(source, filename) && cursors[filename] === size)
|
|
17820
17973
|
completedHooks.push(filename);
|
|
17821
17974
|
}
|
|
17822
17975
|
await appendEvents(events, paths);
|
|
@@ -17845,6 +17998,12 @@ async function scanSourceOnce(config2, paths, signal) {
|
|
|
17845
17998
|
}
|
|
17846
17999
|
};
|
|
17847
18000
|
}
|
|
18001
|
+
function isHookFile(source, filename) {
|
|
18002
|
+
return isCursorHookFile(source, filename) || source === "claude" && filename.includes(`${path18.sep}git-queue${path18.sep}`);
|
|
18003
|
+
}
|
|
18004
|
+
function isCursorHookFile(source, filename) {
|
|
18005
|
+
return source === "cursor" && filename.includes(`${path18.sep}cursor-hooks${path18.sep}`);
|
|
18006
|
+
}
|
|
17848
18007
|
function retryAfterMs(response) {
|
|
17849
18008
|
const header = response.headers.get("retry-after");
|
|
17850
18009
|
if (header === null) return null;
|
|
@@ -18206,57 +18365,64 @@ async function syncCursorBilling(config2, fetcher = collectorFetch, signal) {
|
|
|
18206
18365
|
nowMs
|
|
18207
18366
|
);
|
|
18208
18367
|
const byConversation = /* @__PURE__ */ new Map();
|
|
18209
|
-
|
|
18210
|
-
|
|
18211
|
-
|
|
18212
|
-
|
|
18213
|
-
|
|
18214
|
-
|
|
18215
|
-
|
|
18216
|
-
|
|
18217
|
-
|
|
18218
|
-
|
|
18219
|
-
|
|
18220
|
-
|
|
18221
|
-
|
|
18222
|
-
|
|
18223
|
-
|
|
18224
|
-
AbortSignal.
|
|
18225
|
-
|
|
18226
|
-
|
|
18227
|
-
|
|
18228
|
-
|
|
18229
|
-
|
|
18230
|
-
|
|
18231
|
-
|
|
18232
|
-
|
|
18233
|
-
|
|
18234
|
-
if (
|
|
18368
|
+
const readUsagePages = async () => {
|
|
18369
|
+
for (let page = 1; page <= MAX_PAGES; page += 1) {
|
|
18370
|
+
signal?.throwIfAborted();
|
|
18371
|
+
const response = await fetch(CURSOR_USAGE_URL, {
|
|
18372
|
+
method: "POST",
|
|
18373
|
+
headers: {
|
|
18374
|
+
Authorization: `Bearer ${auth.token}`,
|
|
18375
|
+
"Content-Type": "application/json",
|
|
18376
|
+
"Connect-Protocol-Version": "1"
|
|
18377
|
+
},
|
|
18378
|
+
body: JSON.stringify({
|
|
18379
|
+
page,
|
|
18380
|
+
pageSize: PAGE_SIZE,
|
|
18381
|
+
startDate: String(plan.startDateMs)
|
|
18382
|
+
}),
|
|
18383
|
+
signal: AbortSignal.any([
|
|
18384
|
+
AbortSignal.timeout(2e4),
|
|
18385
|
+
...signal ? [signal] : []
|
|
18386
|
+
])
|
|
18387
|
+
});
|
|
18388
|
+
if (!response.ok)
|
|
18389
|
+
throw new Error(
|
|
18390
|
+
`Cursor billing lookup failed with HTTP ${response.status}.`
|
|
18391
|
+
);
|
|
18392
|
+
const rawPage = await response.json();
|
|
18393
|
+
if (emptyUsagePageSchema.safeParse(rawPage).success) {
|
|
18394
|
+
if (page !== 1)
|
|
18395
|
+
throw new Error(
|
|
18396
|
+
"Cursor billing history ended before its reported count."
|
|
18397
|
+
);
|
|
18398
|
+
break;
|
|
18399
|
+
}
|
|
18400
|
+
const decoded = usagePageSchema.safeParse(rawPage);
|
|
18401
|
+
if (!decoded.success)
|
|
18402
|
+
throw new Error("Cursor returned an invalid billing page.");
|
|
18403
|
+
for (const value of decoded.data.usageEventsDisplay) {
|
|
18404
|
+
const normalized = normalizeCursorUsageEvent(value, localConversations);
|
|
18405
|
+
if (normalized === null) continue;
|
|
18406
|
+
const events = byConversation.get(normalized.conversationId) ?? [];
|
|
18407
|
+
events.push(normalized.event);
|
|
18408
|
+
if (events.length > 5e3)
|
|
18409
|
+
throw new Error(
|
|
18410
|
+
"Cursor conversation exceeds the billing sync limit."
|
|
18411
|
+
);
|
|
18412
|
+
byConversation.set(normalized.conversationId, events);
|
|
18413
|
+
}
|
|
18414
|
+
if (page * PAGE_SIZE >= decoded.data.totalUsageEventsCount) break;
|
|
18415
|
+
if (page === MAX_PAGES)
|
|
18416
|
+
throw new Error(
|
|
18417
|
+
"Cursor account exceeds the billing history page limit."
|
|
18418
|
+
);
|
|
18419
|
+
if (decoded.data.usageEventsDisplay.length === 0)
|
|
18235
18420
|
throw new Error(
|
|
18236
18421
|
"Cursor billing history ended before its reported count."
|
|
18237
18422
|
);
|
|
18238
|
-
break;
|
|
18239
18423
|
}
|
|
18240
|
-
|
|
18241
|
-
|
|
18242
|
-
throw new Error("Cursor returned an invalid billing page.");
|
|
18243
|
-
for (const value of decoded.data.usageEventsDisplay) {
|
|
18244
|
-
const normalized = normalizeCursorUsageEvent(value, localConversations);
|
|
18245
|
-
if (normalized === null) continue;
|
|
18246
|
-
const events = byConversation.get(normalized.conversationId) ?? [];
|
|
18247
|
-
events.push(normalized.event);
|
|
18248
|
-
if (events.length > 5e3)
|
|
18249
|
-
throw new Error("Cursor conversation exceeds the billing sync limit.");
|
|
18250
|
-
byConversation.set(normalized.conversationId, events);
|
|
18251
|
-
}
|
|
18252
|
-
if (page * PAGE_SIZE >= decoded.data.totalUsageEventsCount) break;
|
|
18253
|
-
if (page === MAX_PAGES)
|
|
18254
|
-
throw new Error("Cursor account exceeds the billing history page limit.");
|
|
18255
|
-
if (decoded.data.usageEventsDisplay.length === 0)
|
|
18256
|
-
throw new Error(
|
|
18257
|
-
"Cursor billing history ended before its reported count."
|
|
18258
|
-
);
|
|
18259
|
-
}
|
|
18424
|
+
};
|
|
18425
|
+
await readUsagePages();
|
|
18260
18426
|
const endpoint = new URL(config2.endpoint);
|
|
18261
18427
|
endpoint.pathname = "/api/traces/cursor-billing";
|
|
18262
18428
|
let matched = 0;
|
|
@@ -18631,11 +18797,14 @@ async function syncCodexBilling(config2, fetcher = collectorFetch, signal) {
|
|
|
18631
18797
|
);
|
|
18632
18798
|
const filenames = await sourceFiles("codex", config2.codexHome);
|
|
18633
18799
|
const threads = [];
|
|
18634
|
-
|
|
18635
|
-
|
|
18636
|
-
|
|
18637
|
-
|
|
18638
|
-
|
|
18800
|
+
const readLocalThreads = async () => {
|
|
18801
|
+
for (const filename of filenames) {
|
|
18802
|
+
signal?.throwIfAborted();
|
|
18803
|
+
const thread = await localThread(filename);
|
|
18804
|
+
if (thread !== null) threads.push(thread);
|
|
18805
|
+
}
|
|
18806
|
+
};
|
|
18807
|
+
await readLocalThreads();
|
|
18639
18808
|
const allTasks = groupCodexTasks(threads, auth.accountId);
|
|
18640
18809
|
const retryIds = new Set(audit ? prior?.unavailableThreadIds ?? [] : []);
|
|
18641
18810
|
const selected = allTasks.filter(
|
|
@@ -18673,36 +18842,40 @@ async function syncCodexBilling(config2, fetcher = collectorFetch, signal) {
|
|
|
18673
18842
|
let apiTurns = 0;
|
|
18674
18843
|
const endpoint = new URL(config2.endpoint);
|
|
18675
18844
|
endpoint.pathname = "/api/traces/codex-billing";
|
|
18676
|
-
const
|
|
18677
|
-
|
|
18678
|
-
|
|
18679
|
-
|
|
18680
|
-
|
|
18681
|
-
const
|
|
18682
|
-
|
|
18683
|
-
|
|
18684
|
-
|
|
18685
|
-
|
|
18686
|
-
|
|
18687
|
-
|
|
18688
|
-
|
|
18689
|
-
if (current.length >
|
|
18690
|
-
|
|
18691
|
-
|
|
18692
|
-
|
|
18693
|
-
|
|
18694
|
-
|
|
18695
|
-
|
|
18696
|
-
|
|
18697
|
-
|
|
18698
|
-
|
|
18699
|
-
|
|
18700
|
-
|
|
18701
|
-
|
|
18702
|
-
|
|
18703
|
-
|
|
18704
|
-
|
|
18705
|
-
|
|
18845
|
+
const createBatches = () => {
|
|
18846
|
+
const batches2 = [];
|
|
18847
|
+
let current = [];
|
|
18848
|
+
let currentIds = 0;
|
|
18849
|
+
let currentTurns = 0;
|
|
18850
|
+
for (const task of selected) {
|
|
18851
|
+
const size = 1 + task.descendantThreadIds.length;
|
|
18852
|
+
const turns = [task.threadId, ...task.descendantThreadIds].reduce(
|
|
18853
|
+
(total, threadId) => total + (byThread.get(threadId)?.length ?? 0),
|
|
18854
|
+
0
|
|
18855
|
+
);
|
|
18856
|
+
if (size > 1e3)
|
|
18857
|
+
throw new Error("Codex task exceeds the billing thread limit.");
|
|
18858
|
+
if (current.length >= TASK_BATCH_SIZE || currentIds + size > 1e3 || currentTurns + turns > 5e3) {
|
|
18859
|
+
if (current.length > 0) batches2.push(current);
|
|
18860
|
+
current = [];
|
|
18861
|
+
currentIds = 0;
|
|
18862
|
+
currentTurns = 0;
|
|
18863
|
+
}
|
|
18864
|
+
current.push(task);
|
|
18865
|
+
currentIds += size;
|
|
18866
|
+
currentTurns += turns;
|
|
18867
|
+
if (turns > 5e3) {
|
|
18868
|
+
batches2.push(current);
|
|
18869
|
+
current = [];
|
|
18870
|
+
currentIds = 0;
|
|
18871
|
+
currentTurns = 0;
|
|
18872
|
+
}
|
|
18873
|
+
}
|
|
18874
|
+
if (current.length > 0) batches2.push(current);
|
|
18875
|
+
return batches2;
|
|
18876
|
+
};
|
|
18877
|
+
const batches = createBatches();
|
|
18878
|
+
const syncBatch = async (batch) => {
|
|
18706
18879
|
signal?.throwIfAborted();
|
|
18707
18880
|
const responseTasks = auth.accessToken === null || auth.accountId === null ? /* @__PURE__ */ new Map() : await queryTaskUsage(batch, auth.accessToken, auth.accountId, signal);
|
|
18708
18881
|
const tasks = batch.map((task) => {
|
|
@@ -18750,7 +18923,7 @@ async function syncCodexBilling(config2, fetcher = collectorFetch, signal) {
|
|
|
18750
18923
|
throw new Error("Iteration returned an invalid Codex billing result.");
|
|
18751
18924
|
if (result.data.matched < tasks.length) serverIncomplete = true;
|
|
18752
18925
|
}
|
|
18753
|
-
|
|
18926
|
+
const reconcileTask = (task) => {
|
|
18754
18927
|
if (task.dataStatus === "available" && task.balanceUsageCredits !== null && (task.usageSource === "included_plan" || task.usageSource === "plan_and_credits"))
|
|
18755
18928
|
unavailable.delete(task.threadId);
|
|
18756
18929
|
else if (task.dataStatus !== "not_queried")
|
|
@@ -18762,9 +18935,11 @@ async function syncCodexBilling(config2, fetcher = collectorFetch, signal) {
|
|
|
18762
18935
|
else if (codexCreditSign(task.balanceUsageCredits) > 0)
|
|
18763
18936
|
creditTasks += 1;
|
|
18764
18937
|
}
|
|
18765
|
-
}
|
|
18938
|
+
};
|
|
18939
|
+
for (const task of tasks) reconcileTask(task);
|
|
18766
18940
|
apiTurns += batchTurns.filter((turn) => turn.mode === "api_key").length;
|
|
18767
|
-
}
|
|
18941
|
+
};
|
|
18942
|
+
for (const batch of batches) await syncBatch(batch);
|
|
18768
18943
|
await saveState2(stateFile, {
|
|
18769
18944
|
accountFingerprint: auth.accountFingerprint,
|
|
18770
18945
|
lastSyncedAtMs: nowMs,
|
|
@@ -18811,13 +18986,11 @@ async function persistHealth(health, paths) {
|
|
|
18811
18986
|
try {
|
|
18812
18987
|
await saveHealth(health, paths);
|
|
18813
18988
|
} catch {
|
|
18814
|
-
|
|
18815
|
-
|
|
18816
|
-
|
|
18817
|
-
|
|
18818
|
-
|
|
18819
|
-
})
|
|
18820
|
-
);
|
|
18989
|
+
collectorLogger({
|
|
18990
|
+
level: "warning",
|
|
18991
|
+
operation: "save_collector_health",
|
|
18992
|
+
message: "Local collector status could not be saved; collection continues."
|
|
18993
|
+
});
|
|
18821
18994
|
}
|
|
18822
18995
|
}
|
|
18823
18996
|
async function acquireCollectorLock(paths) {
|
|
@@ -18889,15 +19062,12 @@ async function runtimeStep(version2, signal) {
|
|
|
18889
19062
|
signal
|
|
18890
19063
|
});
|
|
18891
19064
|
if (outcome.kind !== "updated") return "continue";
|
|
18892
|
-
|
|
18893
|
-
|
|
18894
|
-
|
|
18895
|
-
|
|
18896
|
-
|
|
18897
|
-
|
|
18898
|
-
to: outcome.to
|
|
18899
|
-
})
|
|
18900
|
-
);
|
|
19065
|
+
collectorLogger({
|
|
19066
|
+
level: "info",
|
|
19067
|
+
operation: "runtime_update",
|
|
19068
|
+
from: outcome.from,
|
|
19069
|
+
to: outcome.to
|
|
19070
|
+
});
|
|
18901
19071
|
return "restart";
|
|
18902
19072
|
}
|
|
18903
19073
|
function versionFailure(error2) {
|
|
@@ -18927,13 +19097,11 @@ async function runCollector(directory, expectedSource) {
|
|
|
18927
19097
|
const loadedHealth = await healthSnapshot(paths);
|
|
18928
19098
|
let health = loadedHealth.health;
|
|
18929
19099
|
if (loadedHealth.kind === "unavailable") {
|
|
18930
|
-
|
|
18931
|
-
|
|
18932
|
-
|
|
18933
|
-
|
|
18934
|
-
|
|
18935
|
-
})
|
|
18936
|
-
);
|
|
19100
|
+
collectorLogger({
|
|
19101
|
+
level: "warning",
|
|
19102
|
+
operation: "load_collector_health",
|
|
19103
|
+
message: "Local collector status could not be read; collection continues."
|
|
19104
|
+
});
|
|
18937
19105
|
}
|
|
18938
19106
|
if ("cursorHome" in config2) {
|
|
18939
19107
|
await ensureCursorHistoryBackfill(config2.cursorHome, paths);
|
|
@@ -18949,37 +19117,34 @@ async function runCollector(directory, expectedSource) {
|
|
|
18949
19117
|
const runtimeVersion = process.env.ITERATION_RUNTIME_VERSION;
|
|
18950
19118
|
let lastRuntimeCheckAt = 0;
|
|
18951
19119
|
let trialSettled = runtimeVersion === void 0;
|
|
18952
|
-
|
|
19120
|
+
const checkRuntime = async () => {
|
|
18953
19121
|
if (runtimeVersion !== void 0 && Date.now() - lastRuntimeCheckAt >= RUNTIME_CHECK_INTERVAL_MS) {
|
|
18954
19122
|
lastRuntimeCheckAt = Date.now();
|
|
18955
19123
|
let step = "continue";
|
|
18956
19124
|
try {
|
|
18957
19125
|
step = await runtimeStep(runtimeVersion, controller.signal);
|
|
18958
19126
|
} catch (error2) {
|
|
18959
|
-
if (controller.signal.aborted)
|
|
18960
|
-
|
|
18961
|
-
|
|
18962
|
-
|
|
18963
|
-
|
|
18964
|
-
|
|
18965
|
-
|
|
18966
|
-
|
|
18967
|
-
message: error2 instanceof Error ? error2.message : "The update check failed."
|
|
18968
|
-
})
|
|
18969
|
-
);
|
|
19127
|
+
if (controller.signal.aborted) return false;
|
|
19128
|
+
collectorLogger({
|
|
19129
|
+
level: "error",
|
|
19130
|
+
source,
|
|
19131
|
+
operation: "runtime_update",
|
|
19132
|
+
causes: errorCauses(error2),
|
|
19133
|
+
message: error2 instanceof Error ? error2.message : "The update check failed."
|
|
19134
|
+
});
|
|
18970
19135
|
}
|
|
18971
19136
|
if (step === "restart") {
|
|
18972
|
-
|
|
18973
|
-
|
|
18974
|
-
|
|
18975
|
-
|
|
18976
|
-
|
|
18977
|
-
|
|
18978
|
-
})
|
|
18979
|
-
);
|
|
18980
|
-
break;
|
|
19137
|
+
collectorLogger({
|
|
19138
|
+
level: "info",
|
|
19139
|
+
source,
|
|
19140
|
+
operation: "runtime_restart"
|
|
19141
|
+
});
|
|
19142
|
+
return false;
|
|
18981
19143
|
}
|
|
18982
19144
|
}
|
|
19145
|
+
return true;
|
|
19146
|
+
};
|
|
19147
|
+
const connectDetectedSources = async () => {
|
|
18983
19148
|
if (Date.now() - lastAutoCheckAt >= 6e4) {
|
|
18984
19149
|
lastAutoCheckAt = Date.now();
|
|
18985
19150
|
try {
|
|
@@ -18987,25 +19152,50 @@ async function runCollector(directory, expectedSource) {
|
|
|
18987
19152
|
if (consent?.anchorSource === source) {
|
|
18988
19153
|
const connected = await connectNewDetectedSources(config2, consent);
|
|
18989
19154
|
if (connected.length > 0) {
|
|
18990
|
-
|
|
18991
|
-
|
|
18992
|
-
|
|
18993
|
-
|
|
18994
|
-
|
|
18995
|
-
})
|
|
18996
|
-
);
|
|
19155
|
+
collectorLogger({
|
|
19156
|
+
level: "info",
|
|
19157
|
+
operation: "auto_connect_sources",
|
|
19158
|
+
connected
|
|
19159
|
+
});
|
|
18997
19160
|
}
|
|
18998
19161
|
}
|
|
18999
19162
|
} catch (error2) {
|
|
19000
|
-
|
|
19001
|
-
|
|
19002
|
-
|
|
19003
|
-
|
|
19004
|
-
|
|
19005
|
-
})
|
|
19006
|
-
);
|
|
19163
|
+
collectorLogger({
|
|
19164
|
+
level: "error",
|
|
19165
|
+
operation: "auto_connect_sources",
|
|
19166
|
+
message: error2 instanceof Error ? error2.message : "Automatic source connection failed."
|
|
19167
|
+
});
|
|
19007
19168
|
}
|
|
19008
19169
|
}
|
|
19170
|
+
};
|
|
19171
|
+
const recordTickSuccess = async (result, acknowledgedAtMs) => {
|
|
19172
|
+
if (acknowledgedAtMs !== null || health.lastFailure !== null || result.historyCaughtUp && health.historyCaughtUpAtMs === null) {
|
|
19173
|
+
health = {
|
|
19174
|
+
lastUploadAtMs: acknowledgedAtMs ?? health.lastUploadAtMs,
|
|
19175
|
+
historyCaughtUpAtMs: health.historyCaughtUpAtMs ?? (result.historyCaughtUp ? Date.now() : null),
|
|
19176
|
+
lastFailure: null
|
|
19177
|
+
};
|
|
19178
|
+
await persistHealth(health, paths);
|
|
19179
|
+
}
|
|
19180
|
+
if (!trialSettled && runtimeVersion !== void 0) {
|
|
19181
|
+
await confirmRuntimeTrial(runtimeVersion, source);
|
|
19182
|
+
trialSettled = true;
|
|
19183
|
+
}
|
|
19184
|
+
if (result.discovered > 0 || result.uploaded > 0) {
|
|
19185
|
+
collectorLogger({
|
|
19186
|
+
level: "info",
|
|
19187
|
+
source,
|
|
19188
|
+
operation: "collector_tick",
|
|
19189
|
+
discovered: result.discovered,
|
|
19190
|
+
uploaded: result.uploaded,
|
|
19191
|
+
outboxBytes: result.outboxBytes
|
|
19192
|
+
});
|
|
19193
|
+
}
|
|
19194
|
+
retryDelayMs = Math.max(5e3, result.retryAfterMs);
|
|
19195
|
+
if (result.retryAfterMs > 0)
|
|
19196
|
+
retryDelayMs += Math.floor(Math.random() * 1e3);
|
|
19197
|
+
};
|
|
19198
|
+
const collect = async () => {
|
|
19009
19199
|
let acknowledgedAtMs = null;
|
|
19010
19200
|
let billingReady = false;
|
|
19011
19201
|
let history = null;
|
|
@@ -19026,35 +19216,9 @@ async function runCollector(directory, expectedSource) {
|
|
|
19026
19216
|
complete: billingReady && result.history.readSessions === result.history.sessions
|
|
19027
19217
|
};
|
|
19028
19218
|
}
|
|
19029
|
-
|
|
19030
|
-
health = {
|
|
19031
|
-
lastUploadAtMs: acknowledgedAtMs ?? health.lastUploadAtMs,
|
|
19032
|
-
historyCaughtUpAtMs: health.historyCaughtUpAtMs ?? (result.historyCaughtUp ? Date.now() : null),
|
|
19033
|
-
lastFailure: null
|
|
19034
|
-
};
|
|
19035
|
-
await persistHealth(health, paths);
|
|
19036
|
-
}
|
|
19037
|
-
if (!trialSettled && runtimeVersion !== void 0) {
|
|
19038
|
-
await confirmRuntimeTrial(runtimeVersion, source);
|
|
19039
|
-
trialSettled = true;
|
|
19040
|
-
}
|
|
19041
|
-
if (result.discovered > 0 || result.uploaded > 0) {
|
|
19042
|
-
console.info(
|
|
19043
|
-
JSON.stringify({
|
|
19044
|
-
level: "info",
|
|
19045
|
-
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19046
|
-
source,
|
|
19047
|
-
discovered: result.discovered,
|
|
19048
|
-
uploaded: result.uploaded,
|
|
19049
|
-
outboxBytes: result.outboxBytes
|
|
19050
|
-
})
|
|
19051
|
-
);
|
|
19052
|
-
}
|
|
19053
|
-
retryDelayMs = Math.max(5e3, result.retryAfterMs);
|
|
19054
|
-
if (result.retryAfterMs > 0)
|
|
19055
|
-
retryDelayMs += Math.floor(Math.random() * 1e3);
|
|
19219
|
+
await recordTickSuccess(result, acknowledgedAtMs);
|
|
19056
19220
|
} catch (error2) {
|
|
19057
|
-
if (controller.signal.aborted)
|
|
19221
|
+
if (controller.signal.aborted) return null;
|
|
19058
19222
|
if (!trialSettled && runtimeVersion !== void 0) {
|
|
19059
19223
|
if (versionFailure(error2) && await onTrial(runtimeVersion))
|
|
19060
19224
|
throw error2;
|
|
@@ -19066,20 +19230,20 @@ async function runCollector(directory, expectedSource) {
|
|
|
19066
19230
|
historyCaughtUpAtMs: health.historyCaughtUpAtMs,
|
|
19067
19231
|
lastFailure: collectorFailure(error2)
|
|
19068
19232
|
};
|
|
19069
|
-
|
|
19070
|
-
|
|
19071
|
-
|
|
19072
|
-
|
|
19073
|
-
|
|
19074
|
-
|
|
19075
|
-
|
|
19076
|
-
|
|
19077
|
-
message: error2 instanceof Error ? error2.message : "Unknown collector failure."
|
|
19078
|
-
})
|
|
19079
|
-
);
|
|
19233
|
+
collectorLogger({
|
|
19234
|
+
level: "error",
|
|
19235
|
+
source,
|
|
19236
|
+
operation: "collector_tick",
|
|
19237
|
+
errorName: error2 instanceof Error ? error2.name : "UnknownError",
|
|
19238
|
+
causes: errorCauses(error2),
|
|
19239
|
+
message: error2 instanceof Error ? error2.message : "Unknown collector failure."
|
|
19240
|
+
});
|
|
19080
19241
|
await persistHealth(health, paths);
|
|
19081
19242
|
retryDelayMs = Math.min(retryDelayMs * 2, 6e4);
|
|
19082
19243
|
}
|
|
19244
|
+
return { history, billingReady };
|
|
19245
|
+
};
|
|
19246
|
+
const reportHistory = async (history) => {
|
|
19083
19247
|
if (history !== null) {
|
|
19084
19248
|
try {
|
|
19085
19249
|
await historyProgress.report(
|
|
@@ -19090,20 +19254,20 @@ async function runCollector(directory, expectedSource) {
|
|
|
19090
19254
|
Date.now()
|
|
19091
19255
|
);
|
|
19092
19256
|
} catch (error2) {
|
|
19093
|
-
if (controller.signal.aborted)
|
|
19094
|
-
|
|
19095
|
-
|
|
19096
|
-
|
|
19097
|
-
|
|
19098
|
-
|
|
19099
|
-
|
|
19100
|
-
|
|
19101
|
-
|
|
19102
|
-
message: error2 instanceof Error ? error2.message : "Unknown history progress failure."
|
|
19103
|
-
})
|
|
19104
|
-
);
|
|
19257
|
+
if (controller.signal.aborted) return false;
|
|
19258
|
+
collectorLogger({
|
|
19259
|
+
level: "warning",
|
|
19260
|
+
source,
|
|
19261
|
+
operation: "report_history_progress",
|
|
19262
|
+
errorName: error2 instanceof Error ? error2.name : "UnknownError",
|
|
19263
|
+
causes: errorCauses(error2),
|
|
19264
|
+
message: error2 instanceof Error ? error2.message : "Unknown history progress failure."
|
|
19265
|
+
});
|
|
19105
19266
|
}
|
|
19106
19267
|
}
|
|
19268
|
+
return true;
|
|
19269
|
+
};
|
|
19270
|
+
const refreshSummaries = async () => {
|
|
19107
19271
|
try {
|
|
19108
19272
|
if (source !== "cursor")
|
|
19109
19273
|
await refreshTurnSummaries(
|
|
@@ -19114,19 +19278,19 @@ async function runCollector(directory, expectedSource) {
|
|
|
19114
19278
|
controller.signal
|
|
19115
19279
|
);
|
|
19116
19280
|
} catch (error2) {
|
|
19117
|
-
if (controller.signal.aborted)
|
|
19118
|
-
|
|
19119
|
-
|
|
19120
|
-
|
|
19121
|
-
|
|
19122
|
-
|
|
19123
|
-
|
|
19124
|
-
|
|
19125
|
-
|
|
19126
|
-
message: error2 instanceof Error ? error2.message : "Unknown turn summary failure."
|
|
19127
|
-
})
|
|
19128
|
-
);
|
|
19281
|
+
if (controller.signal.aborted) return false;
|
|
19282
|
+
collectorLogger({
|
|
19283
|
+
level: "warning",
|
|
19284
|
+
source,
|
|
19285
|
+
operation: "refresh_turn_summaries",
|
|
19286
|
+
errorName: error2 instanceof Error ? error2.name : "UnknownError",
|
|
19287
|
+
causes: errorCauses(error2),
|
|
19288
|
+
message: error2 instanceof Error ? error2.message : "Unknown turn summary failure."
|
|
19289
|
+
});
|
|
19129
19290
|
}
|
|
19291
|
+
return true;
|
|
19292
|
+
};
|
|
19293
|
+
const syncBilling = async (billingReady) => {
|
|
19130
19294
|
if ("cursorHome" in config2 && billingReady && Date.now() - lastBillingSyncAt >= 6 * 60 * 60 * 1e3) {
|
|
19131
19295
|
try {
|
|
19132
19296
|
const billing = await syncCursorBilling(
|
|
@@ -19135,23 +19299,19 @@ async function runCollector(directory, expectedSource) {
|
|
|
19135
19299
|
controller.signal
|
|
19136
19300
|
);
|
|
19137
19301
|
lastBillingSyncAt = billing.skipped ? Date.now() - 6 * 60 * 60 * 1e3 + 5 * 60 * 1e3 : Date.now();
|
|
19138
|
-
|
|
19139
|
-
|
|
19140
|
-
|
|
19141
|
-
|
|
19142
|
-
|
|
19143
|
-
})
|
|
19144
|
-
);
|
|
19302
|
+
collectorLogger({
|
|
19303
|
+
level: "info",
|
|
19304
|
+
operation: "sync_cursor_billing",
|
|
19305
|
+
...billing
|
|
19306
|
+
});
|
|
19145
19307
|
} catch (error2) {
|
|
19146
|
-
if (controller.signal.aborted)
|
|
19308
|
+
if (controller.signal.aborted) return false;
|
|
19147
19309
|
lastBillingSyncAt = Date.now() - 6 * 60 * 60 * 1e3 + 5 * 60 * 1e3;
|
|
19148
|
-
|
|
19149
|
-
|
|
19150
|
-
|
|
19151
|
-
|
|
19152
|
-
|
|
19153
|
-
})
|
|
19154
|
-
);
|
|
19310
|
+
collectorLogger({
|
|
19311
|
+
level: "warning",
|
|
19312
|
+
operation: "sync_cursor_billing",
|
|
19313
|
+
message: error2 instanceof Error ? error2.message : "Cursor billing sync failed."
|
|
19314
|
+
});
|
|
19155
19315
|
}
|
|
19156
19316
|
}
|
|
19157
19317
|
if ("codexHome" in config2 && billingReady && Date.now() - lastBillingSyncAt >= 6 * 60 * 60 * 1e3) {
|
|
@@ -19162,25 +19322,31 @@ async function runCollector(directory, expectedSource) {
|
|
|
19162
19322
|
controller.signal
|
|
19163
19323
|
);
|
|
19164
19324
|
lastBillingSyncAt = billing.skipped ? Date.now() - 6 * 60 * 60 * 1e3 + 5 * 60 * 1e3 : Date.now();
|
|
19165
|
-
|
|
19166
|
-
|
|
19167
|
-
|
|
19168
|
-
|
|
19169
|
-
|
|
19170
|
-
})
|
|
19171
|
-
);
|
|
19325
|
+
collectorLogger({
|
|
19326
|
+
level: "info",
|
|
19327
|
+
operation: "sync_codex_billing",
|
|
19328
|
+
...billing
|
|
19329
|
+
});
|
|
19172
19330
|
} catch (error2) {
|
|
19173
|
-
if (controller.signal.aborted)
|
|
19331
|
+
if (controller.signal.aborted) return false;
|
|
19174
19332
|
lastBillingSyncAt = Date.now() - 6 * 60 * 60 * 1e3 + 5 * 60 * 1e3;
|
|
19175
|
-
|
|
19176
|
-
|
|
19177
|
-
|
|
19178
|
-
|
|
19179
|
-
|
|
19180
|
-
})
|
|
19181
|
-
);
|
|
19333
|
+
collectorLogger({
|
|
19334
|
+
level: "warning",
|
|
19335
|
+
operation: "sync_codex_billing",
|
|
19336
|
+
message: error2 instanceof Error ? error2.message : "Codex billing sync failed."
|
|
19337
|
+
});
|
|
19182
19338
|
}
|
|
19183
19339
|
}
|
|
19340
|
+
return true;
|
|
19341
|
+
};
|
|
19342
|
+
while (!controller.signal.aborted) {
|
|
19343
|
+
if (!await checkRuntime()) break;
|
|
19344
|
+
await connectDetectedSources();
|
|
19345
|
+
const result = await collect();
|
|
19346
|
+
if (result === null) break;
|
|
19347
|
+
if (!await reportHistory(result.history)) break;
|
|
19348
|
+
if (!await refreshSummaries()) break;
|
|
19349
|
+
if (!await syncBilling(result.billingReady)) break;
|
|
19184
19350
|
try {
|
|
19185
19351
|
await delay4(retryDelayMs, void 0, { signal: controller.signal });
|
|
19186
19352
|
} catch (error2) {
|
|
@@ -19390,76 +19556,90 @@ function renderHome(status2, nowMs = Date.now(), source = "codex") {
|
|
|
19390
19556
|
workspaceName === null ? "Iteration" : `Iteration \xB7 ${workspaceName}`,
|
|
19391
19557
|
""
|
|
19392
19558
|
];
|
|
19393
|
-
|
|
19394
|
-
|
|
19395
|
-
|
|
19396
|
-
|
|
19397
|
-
|
|
19398
|
-
|
|
19399
|
-
|
|
19400
|
-
|
|
19401
|
-
|
|
19402
|
-
|
|
19403
|
-
|
|
19404
|
-
|
|
19405
|
-
|
|
19406
|
-
|
|
19407
|
-
|
|
19408
|
-
|
|
19409
|
-
|
|
19410
|
-
|
|
19411
|
-
|
|
19412
|
-
if (status2.
|
|
19413
|
-
|
|
19559
|
+
const appendConnectionStatus = () => {
|
|
19560
|
+
if (!status2.running) {
|
|
19561
|
+
lines.push(`${name} is stopped`, "New sessions are not being collected.");
|
|
19562
|
+
if (status2.pendingEvents > 0)
|
|
19563
|
+
lines.push(`${pendingDescription(status2)}.`);
|
|
19564
|
+
lines.push(`Resume with iteration start ${source}.`);
|
|
19565
|
+
} else if (status2.hookFailureAtMs !== null && status2.hookFailureAtMs !== void 0) {
|
|
19566
|
+
lines.push(
|
|
19567
|
+
`${name} hook capture needs attention`,
|
|
19568
|
+
`A ${name} hook failed ${relativeTime(status2.hookFailureAtMs, nowMs)}. Some data may be missing. See iteration status ${source}.`
|
|
19569
|
+
);
|
|
19570
|
+
} else if (!status2.healthAvailable) {
|
|
19571
|
+
lines.push(
|
|
19572
|
+
`${name} is running; local status needs attention`,
|
|
19573
|
+
"Upload history could not be read. Collection continues in the background."
|
|
19574
|
+
);
|
|
19575
|
+
if (status2.pendingEvents > 0)
|
|
19576
|
+
lines.push(`${pendingDescription(status2)}.`);
|
|
19577
|
+
lines.push(`Details: iteration status ${source}`);
|
|
19578
|
+
} else if (status2.health.lastFailure !== null) {
|
|
19579
|
+
const failure2 = failureDescription(status2, source);
|
|
19580
|
+
lines.push(failure2.heading, failure2.explanation);
|
|
19581
|
+
if (status2.pendingEvents > 0)
|
|
19582
|
+
lines.push(`${pendingDescription(status2)}.`);
|
|
19583
|
+
if (status2.health.lastUploadAtMs !== null) {
|
|
19584
|
+
lines.push(
|
|
19585
|
+
`Last successful upload ${relativeTime(status2.health.lastUploadAtMs, nowMs)}.`
|
|
19586
|
+
);
|
|
19587
|
+
}
|
|
19588
|
+
lines.push(`Details: iteration status ${source}`);
|
|
19589
|
+
} else return false;
|
|
19590
|
+
return true;
|
|
19591
|
+
};
|
|
19592
|
+
const appendScanStatus = () => {
|
|
19593
|
+
if (status2.scan.kind === "waiting") {
|
|
19594
|
+
lines.push(
|
|
19595
|
+
`${name} is preparing your history`,
|
|
19596
|
+
progressDescription(status2.scan, source)
|
|
19597
|
+
);
|
|
19598
|
+
} else if (status2.scan.kind === "scanning" && status2.scan.totalBytes > 0 && status2.scan.scannedBytes < status2.scan.totalBytes && status2.health.historyCaughtUpAtMs === null) {
|
|
19599
|
+
lines.push(
|
|
19600
|
+
`${name} is importing existing sessions`,
|
|
19601
|
+
historyDescription(status2, source),
|
|
19602
|
+
"New activity is collected automatically."
|
|
19603
|
+
);
|
|
19604
|
+
} else if (status2.scan.kind === "scanning" && status2.scan.totalBytes - status2.scan.scannedBytes >= 1e6) {
|
|
19605
|
+
lines.push(
|
|
19606
|
+
`${name} is catching up with new activity`,
|
|
19607
|
+
historyDescription(status2, source)
|
|
19608
|
+
);
|
|
19609
|
+
if (status2.pendingEvents > 0)
|
|
19610
|
+
lines.push(`${pendingDescription(status2)}.`);
|
|
19611
|
+
} else if (status2.pendingEvents > 0) {
|
|
19612
|
+
lines.push(
|
|
19613
|
+
`${name} is uploading captured sessions`,
|
|
19614
|
+
`${pendingDescription(status2)}.`
|
|
19615
|
+
);
|
|
19616
|
+
} else if (status2.scan.totalBytes === 0) {
|
|
19617
|
+
lines.push(
|
|
19618
|
+
`${name} is connected`,
|
|
19619
|
+
`Waiting for ${name} sessions on this computer.`
|
|
19620
|
+
);
|
|
19621
|
+
} else if (status2.scan.kind === "scanning" && status2.scan.scannedBytes < status2.scan.totalBytes) {
|
|
19622
|
+
lines.push(
|
|
19623
|
+
`${name} is collecting new activity`,
|
|
19624
|
+
"Local history is imported; current activity is being scanned."
|
|
19625
|
+
);
|
|
19626
|
+
} else {
|
|
19627
|
+
lines.push(
|
|
19628
|
+
`${name} is collecting`,
|
|
19629
|
+
"Local scan is caught up and no uploads are waiting.",
|
|
19630
|
+
"New activity is collected automatically."
|
|
19631
|
+
);
|
|
19632
|
+
}
|
|
19633
|
+
};
|
|
19634
|
+
if (!appendConnectionStatus()) appendScanStatus();
|
|
19635
|
+
const appendLastUpload = () => {
|
|
19636
|
+
if (status2.running && status2.health.lastFailure === null && status2.health.lastUploadAtMs !== null) {
|
|
19414
19637
|
lines.push(
|
|
19415
19638
|
`Last successful upload ${relativeTime(status2.health.lastUploadAtMs, nowMs)}.`
|
|
19416
19639
|
);
|
|
19417
19640
|
}
|
|
19418
|
-
|
|
19419
|
-
|
|
19420
|
-
lines.push(
|
|
19421
|
-
`${name} is preparing your history`,
|
|
19422
|
-
progressDescription(status2.scan, source)
|
|
19423
|
-
);
|
|
19424
|
-
} else if (status2.scan.kind === "scanning" && status2.scan.totalBytes > 0 && status2.scan.scannedBytes < status2.scan.totalBytes && status2.health.historyCaughtUpAtMs === null) {
|
|
19425
|
-
lines.push(
|
|
19426
|
-
`${name} is importing existing sessions`,
|
|
19427
|
-
historyDescription(status2, source),
|
|
19428
|
-
"New activity is collected automatically."
|
|
19429
|
-
);
|
|
19430
|
-
} else if (status2.scan.kind === "scanning" && status2.scan.totalBytes - status2.scan.scannedBytes >= 1e6) {
|
|
19431
|
-
lines.push(
|
|
19432
|
-
`${name} is catching up with new activity`,
|
|
19433
|
-
historyDescription(status2, source)
|
|
19434
|
-
);
|
|
19435
|
-
if (status2.pendingEvents > 0) lines.push(`${pendingDescription(status2)}.`);
|
|
19436
|
-
} else if (status2.pendingEvents > 0) {
|
|
19437
|
-
lines.push(
|
|
19438
|
-
`${name} is uploading captured sessions`,
|
|
19439
|
-
`${pendingDescription(status2)}.`
|
|
19440
|
-
);
|
|
19441
|
-
} else if (status2.scan.totalBytes === 0) {
|
|
19442
|
-
lines.push(
|
|
19443
|
-
`${name} is connected`,
|
|
19444
|
-
`Waiting for ${name} sessions on this computer.`
|
|
19445
|
-
);
|
|
19446
|
-
} else if (status2.scan.kind === "scanning" && status2.scan.scannedBytes < status2.scan.totalBytes) {
|
|
19447
|
-
lines.push(
|
|
19448
|
-
`${name} is collecting new activity`,
|
|
19449
|
-
"Local history is imported; current activity is being scanned."
|
|
19450
|
-
);
|
|
19451
|
-
} else {
|
|
19452
|
-
lines.push(
|
|
19453
|
-
`${name} is collecting`,
|
|
19454
|
-
"Local scan is caught up and no uploads are waiting.",
|
|
19455
|
-
"New activity is collected automatically."
|
|
19456
|
-
);
|
|
19457
|
-
}
|
|
19458
|
-
if (status2.running && status2.health.lastFailure === null && status2.health.lastUploadAtMs !== null) {
|
|
19459
|
-
lines.push(
|
|
19460
|
-
`Last successful upload ${relativeTime(status2.health.lastUploadAtMs, nowMs)}.`
|
|
19461
|
-
);
|
|
19462
|
-
}
|
|
19641
|
+
};
|
|
19642
|
+
appendLastUpload();
|
|
19463
19643
|
if (status2.turnSummaryNeedsApproval) lines.push("", CODEX_APPROVAL_STEP);
|
|
19464
19644
|
lines.push("", `Explore traces ${status2.tracesUrl}`);
|
|
19465
19645
|
return lines.join("\n");
|
|
@@ -19902,7 +20082,7 @@ async function connectAll(values) {
|
|
|
19902
20082
|
);
|
|
19903
20083
|
}
|
|
19904
20084
|
const configs = await Promise.all(COLLECTOR_SOURCES.map(connectedConfig));
|
|
19905
|
-
const
|
|
20085
|
+
const resolveSourceHomes = () => ({
|
|
19906
20086
|
codex: path24.resolve(
|
|
19907
20087
|
optionalString(values, "codex-home") ?? (configs[0] && "codexHome" in configs[0] ? configs[0].codexHome : sourceHome("codex"))
|
|
19908
20088
|
),
|
|
@@ -19912,7 +20092,8 @@ async function connectAll(values) {
|
|
|
19912
20092
|
cursor: path24.resolve(
|
|
19913
20093
|
optionalString(values, "cursor-home") ?? (configs[2] && "cursorHome" in configs[2] ? configs[2].cursorHome : sourceHome("cursor"))
|
|
19914
20094
|
)
|
|
19915
|
-
};
|
|
20095
|
+
});
|
|
20096
|
+
const sourceHomes = resolveSourceHomes();
|
|
19916
20097
|
const detected = await detectedSources(sourceHomes);
|
|
19917
20098
|
const existing = COLLECTOR_SOURCES.flatMap(
|
|
19918
20099
|
(source, index) => configs[index] ? [{ source, config: configs[index] }] : []
|
|
@@ -19934,63 +20115,76 @@ async function connectAll(values) {
|
|
|
19934
20115
|
scope: await readCollectorScope(item.config)
|
|
19935
20116
|
}))
|
|
19936
20117
|
);
|
|
19937
|
-
|
|
19938
|
-
|
|
19939
|
-
|
|
19940
|
-
|
|
19941
|
-
|
|
20118
|
+
const validateWorkspace = () => {
|
|
20119
|
+
for (const item of existingScopes) {
|
|
20120
|
+
if (item.config.endpoint !== endpoint) {
|
|
20121
|
+
throw new Error(
|
|
20122
|
+
"Existing sources use different Iteration servers. Connect one source at a time."
|
|
20123
|
+
);
|
|
20124
|
+
}
|
|
20125
|
+
if (existingScopes[0]?.scope.organizationId !== item.scope.organizationId) {
|
|
20126
|
+
throw new Error(
|
|
20127
|
+
"Existing sources use different workspaces. Connect one source at a time."
|
|
20128
|
+
);
|
|
20129
|
+
}
|
|
19942
20130
|
}
|
|
19943
|
-
|
|
20131
|
+
const requestedOrganizationId2 = optionalString(values, "workspace");
|
|
20132
|
+
if (requestedOrganizationId2 && existingScopes[0] && requestedOrganizationId2 !== existingScopes[0].scope.organizationId) {
|
|
19944
20133
|
throw new Error(
|
|
19945
|
-
"
|
|
20134
|
+
"The existing collector belongs to a different workspace."
|
|
19946
20135
|
);
|
|
19947
20136
|
}
|
|
19948
|
-
}
|
|
20137
|
+
};
|
|
20138
|
+
validateWorkspace();
|
|
19949
20139
|
const requestedOrganizationId = optionalString(values, "workspace");
|
|
19950
|
-
if (requestedOrganizationId && existingScopes[0] && requestedOrganizationId !== existingScopes[0].scope.organizationId) {
|
|
19951
|
-
throw new Error("The existing collector belongs to a different workspace.");
|
|
19952
|
-
}
|
|
19953
20140
|
const previousConsent = await loadMachineConsent();
|
|
19954
|
-
|
|
19955
|
-
|
|
19956
|
-
|
|
19957
|
-
|
|
19958
|
-
|
|
19959
|
-
|
|
19960
|
-
|
|
19961
|
-
|
|
19962
|
-
|
|
19963
|
-
|
|
19964
|
-
|
|
19965
|
-
|
|
20141
|
+
const approveConnection = async () => {
|
|
20142
|
+
const needsApproval = () => previousConsent === null || anchorExisting === null || previousConsent.endpoint !== endpoint || previousConsent.anchorSource !== anchorSource || previousConsent.anchorDataDirectory !== anchorExisting.dataDirectory || previousConsent.organizationId !== existingScopes[0]?.scope.organizationId || previousConsent.sourceHomes.codex !== sourceHomes.codex || previousConsent.sourceHomes.claude !== sourceHomes.claude || previousConsent.sourceHomes.cursor !== sourceHomes.cursor;
|
|
20143
|
+
if (needsApproval()) {
|
|
20144
|
+
await approveAllSources(
|
|
20145
|
+
await Promise.all(
|
|
20146
|
+
detected.map(async (source) => ({
|
|
20147
|
+
source,
|
|
20148
|
+
sessions: await countSavedSessions(source, sourceHomes[source])
|
|
20149
|
+
}))
|
|
20150
|
+
),
|
|
20151
|
+
existingScopes[0]?.scope.organizationName ?? null,
|
|
20152
|
+
values.yes === true
|
|
20153
|
+
);
|
|
20154
|
+
}
|
|
20155
|
+
};
|
|
20156
|
+
await approveConnection();
|
|
19966
20157
|
await prepareBackgroundPersistence();
|
|
19967
20158
|
let anchor2 = anchorExisting ?? await initialize(anchorSource, values);
|
|
19968
20159
|
let organizationId = requestedOrganizationId ?? existingScopes[0]?.scope.organizationId ?? anchor2.organizationId ?? null;
|
|
19969
|
-
|
|
19970
|
-
|
|
19971
|
-
|
|
19972
|
-
|
|
19973
|
-
|
|
19974
|
-
|
|
19975
|
-
|
|
19976
|
-
|
|
19977
|
-
|
|
19978
|
-
|
|
19979
|
-
|
|
19980
|
-
|
|
19981
|
-
|
|
19982
|
-
|
|
19983
|
-
|
|
19984
|
-
|
|
19985
|
-
|
|
19986
|
-
|
|
19987
|
-
|
|
19988
|
-
|
|
19989
|
-
|
|
19990
|
-
|
|
20160
|
+
const connectDetectedSources = async () => {
|
|
20161
|
+
for (const source of detected) {
|
|
20162
|
+
if (source === anchorSource || configs[COLLECTOR_SOURCES.indexOf(source)] !== null)
|
|
20163
|
+
continue;
|
|
20164
|
+
const paths = collectorPaths(defaultCollectorDirectory(source));
|
|
20165
|
+
await assertFreshCollectorDirectory(paths, source);
|
|
20166
|
+
const state = await prepareSourceInitialState(
|
|
20167
|
+
source,
|
|
20168
|
+
sourceHomes[source],
|
|
20169
|
+
true
|
|
20170
|
+
);
|
|
20171
|
+
const enrollment = await expandSource(anchor2, source, organizationId);
|
|
20172
|
+
const config2 = await activateSource(
|
|
20173
|
+
source,
|
|
20174
|
+
sourceHomes[source],
|
|
20175
|
+
endpoint,
|
|
20176
|
+
enrollment,
|
|
20177
|
+
state
|
|
20178
|
+
);
|
|
20179
|
+
organizationId = enrollment.organizationId;
|
|
20180
|
+
const notice = await turnSummaryNotice(config2);
|
|
20181
|
+
process.stdout.write(
|
|
20182
|
+
`${collectorSourceLabel(source)} is connected to ${enrollment.organizationName}.${notice}
|
|
19991
20183
|
`
|
|
19992
|
-
|
|
19993
|
-
|
|
20184
|
+
);
|
|
20185
|
+
}
|
|
20186
|
+
};
|
|
20187
|
+
await connectDetectedSources();
|
|
19994
20188
|
if (organizationId === null) {
|
|
19995
20189
|
organizationId = (await readCollectorScope(anchor2)).organizationId;
|
|
19996
20190
|
}
|
|
@@ -20260,15 +20454,15 @@ async function update(setting, values) {
|
|
|
20260
20454
|
process.stdout.write(`${await waitForTrial(outcome.to)}
|
|
20261
20455
|
`);
|
|
20262
20456
|
}
|
|
20263
|
-
async function
|
|
20457
|
+
async function runInternalCommand() {
|
|
20264
20458
|
if (process.argv[2] === "supervise") {
|
|
20265
|
-
const
|
|
20459
|
+
const source = process.argv[3];
|
|
20266
20460
|
const directory = process.argv[5];
|
|
20267
|
-
if (
|
|
20461
|
+
if (source !== "codex" && source !== "claude" && source !== "cursor" || process.argv[4] !== "--data-directory" || !directory || process.argv.length !== 6) {
|
|
20268
20462
|
throw new Error("Invalid collector supervisor command.");
|
|
20269
20463
|
}
|
|
20270
|
-
await superviseCollector(
|
|
20271
|
-
return;
|
|
20464
|
+
await superviseCollector(source, directory);
|
|
20465
|
+
return true;
|
|
20272
20466
|
}
|
|
20273
20467
|
if (process.argv[2] === "claude-git-hook") {
|
|
20274
20468
|
const directory = process.argv[3];
|
|
@@ -20280,21 +20474,31 @@ async function main() {
|
|
|
20280
20474
|
await recordClaudeGitHookFailure(directory);
|
|
20281
20475
|
throw error2;
|
|
20282
20476
|
}
|
|
20283
|
-
return;
|
|
20477
|
+
return true;
|
|
20284
20478
|
}
|
|
20285
20479
|
if (process.argv[2] === "turn-summary-hook") {
|
|
20286
20480
|
const directory = process.argv[3];
|
|
20287
20481
|
if (!directory || process.argv.length !== 4)
|
|
20288
20482
|
throw new Error("Invalid turn summary hook command.");
|
|
20289
20483
|
await captureTurnSummary(directory);
|
|
20290
|
-
return;
|
|
20484
|
+
return true;
|
|
20291
20485
|
}
|
|
20486
|
+
return false;
|
|
20487
|
+
}
|
|
20488
|
+
async function main() {
|
|
20489
|
+
if (await runInternalCommand()) return;
|
|
20292
20490
|
const delegated = await delegateToInstalledVersion();
|
|
20293
20491
|
if (delegated !== null) {
|
|
20294
20492
|
process.exitCode = delegated;
|
|
20295
20493
|
return;
|
|
20296
20494
|
}
|
|
20297
|
-
|
|
20495
|
+
return dispatchCommand(argumentsForCli());
|
|
20496
|
+
}
|
|
20497
|
+
async function dispatchCommand({
|
|
20498
|
+
command,
|
|
20499
|
+
source,
|
|
20500
|
+
values
|
|
20501
|
+
}) {
|
|
20298
20502
|
const selectedSource = source === "codex" || source === "claude" || source === "cursor" ? source : null;
|
|
20299
20503
|
if (values.help === true || command === "help") {
|
|
20300
20504
|
return usage(command === "help" ? source : command);
|
|
@@ -20308,18 +20512,7 @@ async function main() {
|
|
|
20308
20512
|
return connectAll(values);
|
|
20309
20513
|
}
|
|
20310
20514
|
if (command === "") {
|
|
20311
|
-
|
|
20312
|
-
throw new Error(
|
|
20313
|
-
"Options require a source. Run iteration connect --help for details."
|
|
20314
|
-
);
|
|
20315
|
-
}
|
|
20316
|
-
if (isCollectorPlatform()) await adoptRuntime();
|
|
20317
|
-
await defaultCommand();
|
|
20318
|
-
const summary = await runtimeSummaryText();
|
|
20319
|
-
if (summary !== null) process.stdout.write(`
|
|
20320
|
-
${summary}
|
|
20321
|
-
`);
|
|
20322
|
-
return;
|
|
20515
|
+
return showHome(values);
|
|
20323
20516
|
}
|
|
20324
20517
|
if (command === "version") {
|
|
20325
20518
|
const state = await readRuntimeState(runtimePaths());
|
|
@@ -20329,13 +20522,45 @@ ${summary}
|
|
|
20329
20522
|
}
|
|
20330
20523
|
if (command === "self-check") return selfCheck2();
|
|
20331
20524
|
if (command === "update") return update(source, values);
|
|
20332
|
-
|
|
20333
|
-
|
|
20334
|
-
|
|
20335
|
-
|
|
20336
|
-
|
|
20337
|
-
|
|
20525
|
+
return dispatchSourceCommand(command, source, values, selectedSource);
|
|
20526
|
+
}
|
|
20527
|
+
async function showHome(values) {
|
|
20528
|
+
if (Object.keys(values).length > 0) {
|
|
20529
|
+
throw new Error(
|
|
20530
|
+
"Options require a source. Run iteration connect --help for details."
|
|
20531
|
+
);
|
|
20532
|
+
}
|
|
20533
|
+
if (isCollectorPlatform()) await adoptRuntime();
|
|
20534
|
+
await defaultCommand();
|
|
20535
|
+
const summary = await runtimeSummaryText();
|
|
20536
|
+
if (summary !== null) process.stdout.write(`
|
|
20537
|
+
${summary}
|
|
20538
|
+
`);
|
|
20539
|
+
}
|
|
20540
|
+
async function disconnect(selectedSource, values) {
|
|
20541
|
+
await uninstallBackgroundService(selectedSource);
|
|
20542
|
+
const paths = collectorPaths(dataDirectory(selectedSource, values));
|
|
20543
|
+
if (await fileExists(paths.config)) {
|
|
20544
|
+
const config2 = await loadConfig(paths);
|
|
20545
|
+
if ("cursorHome" in config2)
|
|
20546
|
+
await uninstallCursorHooks(config2.cursorHome, config2.dataDirectory);
|
|
20547
|
+
else await uninstallTurnSummary(config2);
|
|
20338
20548
|
}
|
|
20549
|
+
process.stdout.write(
|
|
20550
|
+
"Collector startup removed. Local pending data was retained.\n"
|
|
20551
|
+
);
|
|
20552
|
+
}
|
|
20553
|
+
async function dispatchSourceCommand(command, source, values, selectedSource) {
|
|
20554
|
+
const validateSourceOptions = () => {
|
|
20555
|
+
if (selectedSource !== null) {
|
|
20556
|
+
for (const name of ["codex", "claude", "cursor"])
|
|
20557
|
+
if (selectedSource !== name && values[`${name}-home`] !== void 0)
|
|
20558
|
+
throw new Error(
|
|
20559
|
+
`--${name}-home requires the ${collectorSourceLabel(name)} source.`
|
|
20560
|
+
);
|
|
20561
|
+
}
|
|
20562
|
+
};
|
|
20563
|
+
validateSourceOptions();
|
|
20339
20564
|
if (command === "connect" && selectedSource !== null) {
|
|
20340
20565
|
await initialize(selectedSource, values);
|
|
20341
20566
|
return;
|
|
@@ -20349,39 +20574,30 @@ ${summary}
|
|
|
20349
20574
|
);
|
|
20350
20575
|
}
|
|
20351
20576
|
if (command === "status" && (source === void 0 || selectedSource !== null)) {
|
|
20352
|
-
|
|
20353
|
-
throw new Error("--data-directory requires a source with status.");
|
|
20354
|
-
requireCollectorPlatform();
|
|
20355
|
-
await adoptRuntime();
|
|
20356
|
-
await status(selectedSource ?? void 0, values);
|
|
20357
|
-
const summary = await runtimeSummaryText();
|
|
20358
|
-
if (summary !== null) process.stdout.write(`
|
|
20359
|
-
${summary}
|
|
20360
|
-
`);
|
|
20361
|
-
return;
|
|
20577
|
+
return showStatus(source, selectedSource, values);
|
|
20362
20578
|
}
|
|
20363
20579
|
if (command === "import-history" && selectedSource !== null)
|
|
20364
20580
|
return importHistory(selectedSource, values);
|
|
20365
20581
|
if (command === "start" && selectedSource !== null)
|
|
20366
20582
|
return start(selectedSource, values);
|
|
20367
20583
|
if (command === "disconnect" && selectedSource !== null) {
|
|
20368
|
-
|
|
20369
|
-
const paths = collectorPaths(dataDirectory(selectedSource, values));
|
|
20370
|
-
if (await fileExists(paths.config)) {
|
|
20371
|
-
const config2 = await loadConfig(paths);
|
|
20372
|
-
if ("cursorHome" in config2)
|
|
20373
|
-
await uninstallCursorHooks(config2.cursorHome, config2.dataDirectory);
|
|
20374
|
-
else await uninstallTurnSummary(config2);
|
|
20375
|
-
}
|
|
20376
|
-
process.stdout.write(
|
|
20377
|
-
"Collector startup removed. Local pending data was retained.\n"
|
|
20378
|
-
);
|
|
20379
|
-
return;
|
|
20584
|
+
return disconnect(selectedSource, values);
|
|
20380
20585
|
}
|
|
20381
20586
|
throw new Error(
|
|
20382
20587
|
`Unknown Iteration command: ${[command, source].filter(Boolean).join(" ")}. Run iteration help for usage.`
|
|
20383
20588
|
);
|
|
20384
20589
|
}
|
|
20590
|
+
async function showStatus(source, selectedSource, values) {
|
|
20591
|
+
if (source === void 0 && values["data-directory"] !== void 0)
|
|
20592
|
+
throw new Error("--data-directory requires a source with status.");
|
|
20593
|
+
requireCollectorPlatform();
|
|
20594
|
+
await adoptRuntime();
|
|
20595
|
+
await status(selectedSource ?? void 0, values);
|
|
20596
|
+
const summary = await runtimeSummaryText();
|
|
20597
|
+
if (summary !== null) process.stdout.write(`
|
|
20598
|
+
${summary}
|
|
20599
|
+
`);
|
|
20600
|
+
}
|
|
20385
20601
|
var background = process.argv[2] === "run";
|
|
20386
20602
|
main().then(
|
|
20387
20603
|
() => {
|