@usepowerplant/cli 0.4.18 → 0.4.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.js +1958 -755
- package/dist/menubar/Powerplant.app.zip +0 -0
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
3
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="9f447c4d-0ccb-5f45-8960-6853e28aadfb")}catch(e){}}();
|
|
4
4
|
const __cliNodeMajor = Number(process.versions.node.split('.')[0]);
|
|
5
5
|
if (__cliNodeMajor < 20) {
|
|
6
6
|
console.error(`powerplant needs Node 20 or newer — this is Node ${process.versions.node}.`);
|
|
@@ -13562,16 +13562,16 @@ var require_connect = __commonJS({
|
|
|
13562
13562
|
}
|
|
13563
13563
|
});
|
|
13564
13564
|
}
|
|
13565
|
-
get(
|
|
13566
|
-
const ref = this._sessionCache.get(
|
|
13565
|
+
get(sessionKey2) {
|
|
13566
|
+
const ref = this._sessionCache.get(sessionKey2);
|
|
13567
13567
|
return ref ? ref.deref() : null;
|
|
13568
13568
|
}
|
|
13569
|
-
set(
|
|
13569
|
+
set(sessionKey2, session) {
|
|
13570
13570
|
if (this._maxCachedSessions === 0) {
|
|
13571
13571
|
return;
|
|
13572
13572
|
}
|
|
13573
|
-
if (this._sessionCache.has(
|
|
13574
|
-
this._sessionCache.delete(
|
|
13573
|
+
if (this._sessionCache.has(sessionKey2)) {
|
|
13574
|
+
this._sessionCache.delete(sessionKey2);
|
|
13575
13575
|
} else if (this._sessionCache.size >= this._maxCachedSessions) {
|
|
13576
13576
|
for (const [key, ref] of this._sessionCache) {
|
|
13577
13577
|
if (ref.deref() === void 0) {
|
|
@@ -13584,8 +13584,8 @@ var require_connect = __commonJS({
|
|
|
13584
13584
|
this._sessionCache.delete(oldest.value);
|
|
13585
13585
|
}
|
|
13586
13586
|
}
|
|
13587
|
-
this._sessionCache.set(
|
|
13588
|
-
this._sessionRegistry.register(session,
|
|
13587
|
+
this._sessionCache.set(sessionKey2, new WeakRef(session));
|
|
13588
|
+
this._sessionRegistry.register(session, sessionKey2);
|
|
13589
13589
|
}
|
|
13590
13590
|
};
|
|
13591
13591
|
function buildConnector({ allowH2, useH2c, maxCachedSessions, socketPath, timeout, session: customSession, ...opts }) {
|
|
@@ -13603,9 +13603,9 @@ var require_connect = __commonJS({
|
|
|
13603
13603
|
tls2 = __require("node:tls");
|
|
13604
13604
|
}
|
|
13605
13605
|
servername = servername || options.servername || util2.getServerName(host) || null;
|
|
13606
|
-
const
|
|
13607
|
-
assert2(
|
|
13608
|
-
const session = customSession || sessionCache.get(
|
|
13606
|
+
const sessionKey2 = servername || hostname4;
|
|
13607
|
+
assert2(sessionKey2);
|
|
13608
|
+
const session = customSession || sessionCache.get(sessionKey2) || null;
|
|
13609
13609
|
port = port || 443;
|
|
13610
13610
|
socket = tls2.connect({
|
|
13611
13611
|
highWaterMark: 16384,
|
|
@@ -13621,7 +13621,7 @@ var require_connect = __commonJS({
|
|
|
13621
13621
|
host: hostname4
|
|
13622
13622
|
});
|
|
13623
13623
|
socket.on("session", function(session2) {
|
|
13624
|
-
sessionCache.set(
|
|
13624
|
+
sessionCache.set(sessionKey2, session2);
|
|
13625
13625
|
});
|
|
13626
13626
|
} else {
|
|
13627
13627
|
assert2(!httpSocket, "httpSocket can only be sent on TLS update");
|
|
@@ -36589,11 +36589,11 @@ ${captureLines}` : capture2.stack;
|
|
|
36589
36589
|
});
|
|
36590
36590
|
|
|
36591
36591
|
// src/hookPreferences.ts
|
|
36592
|
-
import { mkdirSync as mkdirSync12, readFileSync as
|
|
36592
|
+
import { mkdirSync as mkdirSync12, readFileSync as readFileSync13, renameSync as renameSync7, writeFileSync as writeFileSync9 } from "node:fs";
|
|
36593
36593
|
import path24 from "node:path";
|
|
36594
36594
|
|
|
36595
36595
|
// ../../apps/syncservice/src/credentials.ts
|
|
36596
|
-
import { chmodSync as
|
|
36596
|
+
import { chmodSync as chmodSync3, existsSync as existsSync8, mkdirSync as mkdirSync9, readFileSync as readFileSync9, unlinkSync as unlinkSync3, writeFileSync as writeFileSync7 } from "node:fs";
|
|
36597
36597
|
import os12 from "node:os";
|
|
36598
36598
|
import path14 from "node:path";
|
|
36599
36599
|
|
|
@@ -56395,6 +56395,35 @@ function codexPromptOrigin(keptBlockOrigins) {
|
|
|
56395
56395
|
return keptBlockOrigins.every((origin) => origin === "human") ? "human" : "unknown";
|
|
56396
56396
|
}
|
|
56397
56397
|
|
|
56398
|
+
// ../core/src/rawTranscriptBundle.ts
|
|
56399
|
+
var RAW_TRANSCRIPT_BUNDLE_CONTENT_TYPE = "application/vnd.powerplant.raw-transcript-bundle";
|
|
56400
|
+
var RAW_TRANSCRIPT_PART_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
56401
|
+
var MAX_RAW_TRANSCRIPT_PARTS = 500;
|
|
56402
|
+
var MAX_HEADER_BYTES = 128 * 1024;
|
|
56403
|
+
var LENGTH_PREFIX_BYTES = 4;
|
|
56404
|
+
var headerSchema = external_exports.object({
|
|
56405
|
+
version: external_exports.literal(1),
|
|
56406
|
+
mainBytes: external_exports.number().int().positive(),
|
|
56407
|
+
parts: external_exports.array(external_exports.object({ part: external_exports.string().regex(RAW_TRANSCRIPT_PART_PATTERN), bytes: external_exports.number().int().positive() })).max(MAX_RAW_TRANSCRIPT_PARTS),
|
|
56408
|
+
subagents: external_exports.object({ found: external_exports.number().int().nonnegative(), skipped: external_exports.number().int().nonnegative() })
|
|
56409
|
+
}).refine((header) => new Set(header.parts.map((entry) => entry.part)).size === header.parts.length, {
|
|
56410
|
+
message: "part ids must be unique"
|
|
56411
|
+
}).refine((header) => header.subagents.found === header.parts.length + header.subagents.skipped, {
|
|
56412
|
+
message: "subagents.found must equal the parts carried plus the files skipped"
|
|
56413
|
+
});
|
|
56414
|
+
function encodeRawTranscriptBundle(bundle) {
|
|
56415
|
+
const fields = headerSchema.parse({
|
|
56416
|
+
version: 1,
|
|
56417
|
+
mainBytes: bundle.main.byteLength,
|
|
56418
|
+
parts: bundle.parts.map((entry) => ({ part: entry.part, bytes: entry.body.byteLength })),
|
|
56419
|
+
subagents: bundle.subagents
|
|
56420
|
+
});
|
|
56421
|
+
const header = Buffer.from(JSON.stringify(fields), "utf8");
|
|
56422
|
+
const prefix = Buffer.alloc(LENGTH_PREFIX_BYTES);
|
|
56423
|
+
prefix.writeUInt32BE(header.byteLength);
|
|
56424
|
+
return Buffer.concat([prefix, header, bundle.main, ...bundle.parts.map((entry) => entry.body)]);
|
|
56425
|
+
}
|
|
56426
|
+
|
|
56398
56427
|
// ../core/src/encodedImages.ts
|
|
56399
56428
|
var BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
56400
56429
|
function isEncodedImage(data, mediaType) {
|
|
@@ -56766,8 +56795,90 @@ var CONFIG_LINE_SECRET_VALUE = (
|
|
|
56766
56795
|
// eslint-disable-next-line security/detect-unsafe-regex
|
|
56767
56796
|
/^([+-]?(?:(?:[^\s:]{1,256}:)?\d+[:-])?[ \t]*(?:\d+(?:\t|\u2192)[ \t]*)?(?:(?:export|-)[ \t]+)?)([A-Za-z_][\w.-]{0,63})([ \t]*[=:][ \t]*)([^\s"'\\,;()[\]{}<>]{8,})(?=[ \t]*(?:#.*)?\r?$)/gm
|
|
56768
56797
|
);
|
|
56798
|
+
function jsonTokens(text) {
|
|
56799
|
+
const tokens = [];
|
|
56800
|
+
for (const { index } of text.matchAll(/["\r\n]/g)) {
|
|
56801
|
+
if (text[index] !== '"') {
|
|
56802
|
+
tokens.push({ index, level: -1 });
|
|
56803
|
+
continue;
|
|
56804
|
+
}
|
|
56805
|
+
let backslashes = 0;
|
|
56806
|
+
while (text[index - backslashes - 1] === "\\") backslashes += 1;
|
|
56807
|
+
let level = 0;
|
|
56808
|
+
for (let run = backslashes + 1; run % 2 === 0; run /= 2) level += 1;
|
|
56809
|
+
tokens.push({ index, level });
|
|
56810
|
+
}
|
|
56811
|
+
return tokens;
|
|
56812
|
+
}
|
|
56813
|
+
function stringEnds(tokens) {
|
|
56814
|
+
const ends = [];
|
|
56815
|
+
const pending = [];
|
|
56816
|
+
for (let i = tokens.length - 1; i >= 0; i -= 1) {
|
|
56817
|
+
const { level } = tokens[i];
|
|
56818
|
+
while (pending.length > 0 && tokens[pending.at(-1)].level > level) pending.pop();
|
|
56819
|
+
ends[i] = pending.at(-1) ?? -1;
|
|
56820
|
+
pending.push(i);
|
|
56821
|
+
}
|
|
56822
|
+
return ends;
|
|
56823
|
+
}
|
|
56824
|
+
function stringTextEnd(text, end) {
|
|
56825
|
+
if (end === void 0) return text.length;
|
|
56826
|
+
return end.level < 0 ? end.index : end.index - (2 ** end.level - 1);
|
|
56827
|
+
}
|
|
56828
|
+
function skipJsonSpace(text, from, level) {
|
|
56829
|
+
const escapeLength = level === 0 ? 0 : 2 ** (level - 1);
|
|
56830
|
+
let at = from;
|
|
56831
|
+
for (; ; ) {
|
|
56832
|
+
const char = text[at];
|
|
56833
|
+
if (char === " " || level === 0 && (char === " " || char === "\r" || char === "\n")) at += 1;
|
|
56834
|
+
else if (char === "\\" && level > 0 && text.startsWith("\\".repeat(escapeLength), at) && /^[nrt]$/.test(text[at + escapeLength] ?? "")) {
|
|
56835
|
+
at += escapeLength + 1;
|
|
56836
|
+
} else return at;
|
|
56837
|
+
}
|
|
56838
|
+
}
|
|
56839
|
+
function decodeJsonKey(raw, level) {
|
|
56840
|
+
if (!raw.includes("\\")) return raw;
|
|
56841
|
+
let key = raw;
|
|
56842
|
+
for (let round = 0; round <= level; round += 1) {
|
|
56843
|
+
try {
|
|
56844
|
+
const decoded = JSON.parse(`"${key}"`);
|
|
56845
|
+
if (typeof decoded !== "string") return key;
|
|
56846
|
+
key = decoded;
|
|
56847
|
+
} catch {
|
|
56848
|
+
return key;
|
|
56849
|
+
}
|
|
56850
|
+
}
|
|
56851
|
+
return key;
|
|
56852
|
+
}
|
|
56853
|
+
function redactJsonSecretValues(text) {
|
|
56854
|
+
if (!text.includes('"')) return text;
|
|
56855
|
+
const tokens = jsonTokens(text);
|
|
56856
|
+
const ends = stringEnds(tokens);
|
|
56857
|
+
let out = "";
|
|
56858
|
+
let copied = 0;
|
|
56859
|
+
for (const [i, { index, level }] of tokens.entries()) {
|
|
56860
|
+
if (level < 0 || index < copied) continue;
|
|
56861
|
+
const keyEnd = ends[i] ?? -1;
|
|
56862
|
+
const keyClose = tokens[keyEnd];
|
|
56863
|
+
if (keyClose?.level !== level) continue;
|
|
56864
|
+
const colon = skipJsonSpace(text, keyClose.index + 1, level);
|
|
56865
|
+
if (text[colon] !== ":") continue;
|
|
56866
|
+
const valueOpen = skipJsonSpace(text, colon + 1, level);
|
|
56867
|
+
let valueQuote = keyEnd + 1;
|
|
56868
|
+
while (tokens[valueQuote]?.level === -1 && (tokens[valueQuote]?.index ?? Infinity) < valueOpen) valueQuote += 1;
|
|
56869
|
+
const quote = tokens[valueQuote];
|
|
56870
|
+
if (quote?.level !== level || quote.index !== valueOpen + 2 ** level - 1) continue;
|
|
56871
|
+
if (!isSecretName(decodeJsonKey(text.slice(index + 1, stringTextEnd(text, keyClose)), level))) continue;
|
|
56872
|
+
const valueStart = quote.index + 1;
|
|
56873
|
+
const valueEnd = stringTextEnd(text, tokens[ends[valueQuote] ?? -1]);
|
|
56874
|
+
if (valueEnd <= valueStart) continue;
|
|
56875
|
+
out += text.slice(copied, valueStart) + REDACTED;
|
|
56876
|
+
copied = valueEnd;
|
|
56877
|
+
}
|
|
56878
|
+
return out + text.slice(copied);
|
|
56879
|
+
}
|
|
56769
56880
|
function redactSecretNamedValues(text) {
|
|
56770
|
-
return text.replace(
|
|
56881
|
+
return redactJsonSecretValues(text).replace(
|
|
56771
56882
|
QUOTED_SECRET_VALUE,
|
|
56772
56883
|
(match, name, separator, quote, value) => isSecretName(name) && isSecretValue(value) ? `${name}${separator}${quote}${REDACTED}${quote}` : match
|
|
56773
56884
|
).replace(
|
|
@@ -57354,6 +57465,44 @@ var TOOL_CALL_INPUT_MAX_BYTES = 65536;
|
|
|
57354
57465
|
function boundedToolInput(value) {
|
|
57355
57466
|
return boundRedactedText(JSON.stringify(value), TOOL_CALL_INPUT_MAX_BYTES);
|
|
57356
57467
|
}
|
|
57468
|
+
var tokenCount = external_exports.number().nonnegative();
|
|
57469
|
+
var costStateSchema = external_exports.object({
|
|
57470
|
+
sessionId: external_exports.string(),
|
|
57471
|
+
startTime: external_exports.number().positive(),
|
|
57472
|
+
hasUnknownModelCost: external_exports.boolean().optional(),
|
|
57473
|
+
modelUsage: external_exports.record(
|
|
57474
|
+
external_exports.string(),
|
|
57475
|
+
external_exports.object({
|
|
57476
|
+
costUSD: external_exports.number().nonnegative(),
|
|
57477
|
+
inputTokens: tokenCount,
|
|
57478
|
+
outputTokens: tokenCount,
|
|
57479
|
+
cacheReadInputTokens: tokenCount,
|
|
57480
|
+
cacheCreationInputTokens: tokenCount
|
|
57481
|
+
})
|
|
57482
|
+
)
|
|
57483
|
+
});
|
|
57484
|
+
function runCostOf(record2) {
|
|
57485
|
+
const parsed = costStateSchema.safeParse(record2);
|
|
57486
|
+
if (!parsed.success) return null;
|
|
57487
|
+
const runStartedAt = new Date(parsed.data.startTime);
|
|
57488
|
+
if (Number.isNaN(runStartedAt.getTime())) return null;
|
|
57489
|
+
return {
|
|
57490
|
+
sessionId: parsed.data.sessionId,
|
|
57491
|
+
runStartedAt: runStartedAt.toISOString(),
|
|
57492
|
+
// Claude Code's own restore reads a missing flag as false.
|
|
57493
|
+
unknownModelCost: parsed.data.hasUnknownModelCost ?? false,
|
|
57494
|
+
byModel: Object.entries(parsed.data.modelUsage).map(([model, usage]) => ({
|
|
57495
|
+
model,
|
|
57496
|
+
costUsd: usage.costUSD,
|
|
57497
|
+
tokens: {
|
|
57498
|
+
inputTokens: usage.inputTokens,
|
|
57499
|
+
outputTokens: usage.outputTokens,
|
|
57500
|
+
cacheReadTokens: usage.cacheReadInputTokens,
|
|
57501
|
+
cacheWriteTokens: usage.cacheCreationInputTokens
|
|
57502
|
+
}
|
|
57503
|
+
})).sort((a, b2) => a.model < b2.model ? -1 : a.model > b2.model ? 1 : 0)
|
|
57504
|
+
};
|
|
57505
|
+
}
|
|
57357
57506
|
function createDigestTurnParser(options = {}) {
|
|
57358
57507
|
const turns = [];
|
|
57359
57508
|
let pendingTools = [];
|
|
@@ -57363,12 +57512,37 @@ function createDigestTurnParser(options = {}) {
|
|
|
57363
57512
|
let currentCarrier = null;
|
|
57364
57513
|
let currentBase = null;
|
|
57365
57514
|
let currentCounted = false;
|
|
57515
|
+
const countedEnvelopes = /* @__PURE__ */ new Map();
|
|
57516
|
+
let currentReplay = null;
|
|
57517
|
+
let transcriptStartedAt = null;
|
|
57518
|
+
let transcriptSessionId = null;
|
|
57519
|
+
let lastRecordAt = null;
|
|
57520
|
+
let runCost = null;
|
|
57521
|
+
let recordedThrough = null;
|
|
57522
|
+
let laterEnvelopes = [];
|
|
57523
|
+
let laterSet = /* @__PURE__ */ new Set();
|
|
57524
|
+
let usageRevisedAfter = false;
|
|
57366
57525
|
const envelopes = [];
|
|
57367
57526
|
let pendingEnvelopes = [];
|
|
57368
57527
|
let currentEnvelope = null;
|
|
57369
57528
|
const evidence = new PrEvidenceCollector();
|
|
57370
57529
|
const backgroundTasks = { started: /* @__PURE__ */ new Map(), reads: /* @__PURE__ */ new Map() };
|
|
57371
57530
|
let line = -1;
|
|
57531
|
+
const resum = (envelope) => {
|
|
57532
|
+
const landed = envelope.turn === -1 ? pendingEnvelopes : envelopes.filter((e) => e.turn === envelope.turn);
|
|
57533
|
+
const usage = landed.reduce((sum2, e) => mergeUsage(sum2, e.usage), null);
|
|
57534
|
+
if (envelope.turn === -1) pendingUsage = usage;
|
|
57535
|
+
else turns[envelope.turn].usage = usage;
|
|
57536
|
+
};
|
|
57537
|
+
const noteRevision = (envelope, recorded, at) => {
|
|
57538
|
+
if (sameUsage(recorded, envelope.usage)) return;
|
|
57539
|
+
const time3 = at === null ? Number.NaN : Date.parse(at);
|
|
57540
|
+
if (Number.isNaN(time3)) envelope.revisedAt = null;
|
|
57541
|
+
else if (envelope.revisedAt !== null && (envelope.revisedAt === void 0 || Date.parse(envelope.revisedAt) < time3)) {
|
|
57542
|
+
envelope.revisedAt = at;
|
|
57543
|
+
}
|
|
57544
|
+
if (runCost !== null && !laterSet.has(envelope)) usageRevisedAfter = true;
|
|
57545
|
+
};
|
|
57372
57546
|
const landPending = (turn) => {
|
|
57373
57547
|
for (const envelope of pendingEnvelopes) {
|
|
57374
57548
|
envelope.turn = turn;
|
|
@@ -57389,6 +57563,18 @@ function createDigestTurnParser(options = {}) {
|
|
|
57389
57563
|
} catch {
|
|
57390
57564
|
return;
|
|
57391
57565
|
}
|
|
57566
|
+
transcriptStartedAt ??= recordTime(record2);
|
|
57567
|
+
if (record2.type === "cost-state") {
|
|
57568
|
+
runCost = runCostOf(record2);
|
|
57569
|
+
recordedThrough = lastRecordAt;
|
|
57570
|
+
laterEnvelopes = [];
|
|
57571
|
+
laterSet = /* @__PURE__ */ new Set();
|
|
57572
|
+
usageRevisedAfter = false;
|
|
57573
|
+
return;
|
|
57574
|
+
}
|
|
57575
|
+
if (typeof record2.sessionId === "string") transcriptSessionId ??= record2.sessionId;
|
|
57576
|
+
const recordAt = recordTime(record2);
|
|
57577
|
+
if (recordAt !== null && (lastRecordAt === null || Date.parse(recordAt) > Date.parse(lastRecordAt))) lastRecordAt = recordAt;
|
|
57392
57578
|
if (record2.type === "pr-link") {
|
|
57393
57579
|
const ref = pullRequestRefFromUrl(typeof record2.prUrl === "string" ? record2.prUrl : "");
|
|
57394
57580
|
if (ref !== null) evidence.linked(ref, recordTime(record2), line);
|
|
@@ -57414,32 +57600,43 @@ function createDigestTurnParser(options = {}) {
|
|
|
57414
57600
|
currentId = id;
|
|
57415
57601
|
currentCarrier = null;
|
|
57416
57602
|
currentBase = pendingUsage;
|
|
57417
|
-
|
|
57603
|
+
currentReplay = id === null ? null : countedEnvelopes.get(id) ?? null;
|
|
57604
|
+
currentCounted = currentReplay !== null;
|
|
57418
57605
|
}
|
|
57419
57606
|
const parsedUsage = recordTurnUsage(message2);
|
|
57420
57607
|
if (parsedUsage) {
|
|
57421
|
-
if (
|
|
57608
|
+
if (currentReplay) {
|
|
57609
|
+
const recorded = currentReplay.usage;
|
|
57610
|
+
keepRecordedTierAndSplit(parsedUsage, recorded);
|
|
57611
|
+
currentReplay.usage = parsedUsage;
|
|
57612
|
+
noteRevision(currentReplay, recorded, at);
|
|
57613
|
+
resum(currentReplay);
|
|
57614
|
+
} else if (currentCounted) {
|
|
57422
57615
|
const recorded = currentEnvelope?.usage;
|
|
57423
|
-
if (
|
|
57424
|
-
parsedUsage.serviceTier = recorded.serviceTier;
|
|
57425
|
-
}
|
|
57426
|
-
if (parsedUsage.cacheWrite1hTokens === null && recorded?.cacheWriteTokens === parsedUsage.cacheWriteTokens) {
|
|
57427
|
-
parsedUsage.cacheWrite1hTokens = recorded.cacheWrite1hTokens;
|
|
57428
|
-
}
|
|
57616
|
+
if (recorded !== void 0) keepRecordedTierAndSplit(parsedUsage, recorded);
|
|
57429
57617
|
const updated = mergeUsage(currentBase, parsedUsage);
|
|
57430
57618
|
if (currentCarrier) currentCarrier.usage = updated;
|
|
57431
57619
|
else pendingUsage = updated;
|
|
57432
|
-
if (currentEnvelope
|
|
57620
|
+
if (currentEnvelope !== null && recorded !== void 0) {
|
|
57621
|
+
currentEnvelope.usage = parsedUsage;
|
|
57622
|
+
noteRevision(currentEnvelope, recorded, at);
|
|
57623
|
+
}
|
|
57433
57624
|
} else if (currentCarrier) {
|
|
57434
57625
|
currentCarrier.usage = mergeUsage(currentCarrier.usage, parsedUsage);
|
|
57435
57626
|
currentCounted = true;
|
|
57436
57627
|
currentEnvelope = { turn: turns.indexOf(currentCarrier), at, usage: parsedUsage, branch: recordBranch(record2) };
|
|
57437
57628
|
envelopes.push(currentEnvelope);
|
|
57629
|
+
laterEnvelopes.push(currentEnvelope);
|
|
57630
|
+
laterSet.add(currentEnvelope);
|
|
57631
|
+
if (id !== null) countedEnvelopes.set(id, currentEnvelope);
|
|
57438
57632
|
} else {
|
|
57439
57633
|
pendingUsage = mergeUsage(pendingUsage, parsedUsage);
|
|
57440
57634
|
currentCounted = true;
|
|
57441
57635
|
currentEnvelope = { turn: -1, at, usage: parsedUsage, branch: recordBranch(record2) };
|
|
57442
57636
|
pendingEnvelopes.push(currentEnvelope);
|
|
57637
|
+
laterEnvelopes.push(currentEnvelope);
|
|
57638
|
+
laterSet.add(currentEnvelope);
|
|
57639
|
+
if (id !== null) countedEnvelopes.set(id, currentEnvelope);
|
|
57443
57640
|
}
|
|
57444
57641
|
}
|
|
57445
57642
|
const model = typeof message2.model === "string" ? message2.model : null;
|
|
@@ -57465,7 +57662,9 @@ function createDigestTurnParser(options = {}) {
|
|
|
57465
57662
|
text: part.text,
|
|
57466
57663
|
origin,
|
|
57467
57664
|
toolCalls: pendingTools,
|
|
57468
|
-
|
|
57665
|
+
// A message written again carries zero of its own: its money
|
|
57666
|
+
// landed where it was first counted.
|
|
57667
|
+
usage: pendingUsage ?? (currentReplay ? zeroUsage(model) : null),
|
|
57469
57668
|
at: pendingAt
|
|
57470
57669
|
};
|
|
57471
57670
|
turns.push(turn);
|
|
@@ -57501,6 +57700,12 @@ function createDigestTurnParser(options = {}) {
|
|
|
57501
57700
|
usageEnvelopes() {
|
|
57502
57701
|
return envelopes;
|
|
57503
57702
|
},
|
|
57703
|
+
harnessCost() {
|
|
57704
|
+
if (runCost === null || runCost.sessionId !== transcriptSessionId) return null;
|
|
57705
|
+
const { runStartedAt, unknownModelCost, byModel } = runCost;
|
|
57706
|
+
const laterUsage = laterEnvelopes.map((envelope) => envelope.usage);
|
|
57707
|
+
return { runStartedAt, transcriptStartedAt, recordedThrough, unknownModelCost, byModel, usageRevisedAfter, laterUsage };
|
|
57708
|
+
},
|
|
57504
57709
|
prEvidence() {
|
|
57505
57710
|
return evidence.finish();
|
|
57506
57711
|
},
|
|
@@ -57581,6 +57786,14 @@ function messageIdOf(record2, message2) {
|
|
|
57581
57786
|
if (typeof record2.uuid === "string") return record2.uuid;
|
|
57582
57787
|
return null;
|
|
57583
57788
|
}
|
|
57789
|
+
function keepRecordedTierAndSplit(later, recorded) {
|
|
57790
|
+
if ((later.serviceTier === void 0 || later.serviceTier === "unknown") && recorded.serviceTier !== void 0) {
|
|
57791
|
+
later.serviceTier = recorded.serviceTier;
|
|
57792
|
+
}
|
|
57793
|
+
if (later.cacheWrite1hTokens === null && recorded.cacheWriteTokens === later.cacheWriteTokens) {
|
|
57794
|
+
later.cacheWrite1hTokens = recorded.cacheWrite1hTokens;
|
|
57795
|
+
}
|
|
57796
|
+
}
|
|
57584
57797
|
function recordTurnUsage(message2) {
|
|
57585
57798
|
const usage = message2.usage;
|
|
57586
57799
|
if (!usage || typeof usage !== "object") return null;
|
|
@@ -57608,6 +57821,9 @@ function recordTurnUsage(message2) {
|
|
|
57608
57821
|
function zeroUsage(model) {
|
|
57609
57822
|
return { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, cacheWrite1hTokens: 0, model };
|
|
57610
57823
|
}
|
|
57824
|
+
function sameUsage(a, b2) {
|
|
57825
|
+
return a.inputTokens === b2.inputTokens && a.outputTokens === b2.outputTokens && a.cacheReadTokens === b2.cacheReadTokens && a.cacheWriteTokens === b2.cacheWriteTokens && a.cacheWrite1hTokens === b2.cacheWrite1hTokens && a.model === b2.model && a.serviceTier === b2.serviceTier;
|
|
57826
|
+
}
|
|
57611
57827
|
function mergeUsage(a, b2) {
|
|
57612
57828
|
if (!a) return b2;
|
|
57613
57829
|
if (!b2) return a;
|
|
@@ -58021,7 +58237,7 @@ function createCodexUsageReader(options = {}) {
|
|
|
58021
58237
|
const startPage = () => {
|
|
58022
58238
|
recordSeenOnPage = false;
|
|
58023
58239
|
previousTotal = null;
|
|
58024
|
-
onFirstPage =
|
|
58240
|
+
onFirstPage = onFirstPage && counted === 0;
|
|
58025
58241
|
};
|
|
58026
58242
|
return {
|
|
58027
58243
|
startPage,
|
|
@@ -58655,6 +58871,141 @@ function parseCursorRunEventsDetailed(runs, options = {}) {
|
|
|
58655
58871
|
return { turns, turnTokens };
|
|
58656
58872
|
}
|
|
58657
58873
|
|
|
58874
|
+
// ../core/src/cursorRunRedaction.ts
|
|
58875
|
+
var REDACTED2 = "[REDACTED]";
|
|
58876
|
+
var SECRET_REST = /^[^\s\\"',;]+/;
|
|
58877
|
+
var CUT_PRIVATE_KEY_BODY = /\[REDACTED\][A-Za-z0-9+/=\s\\]*-----END [A-Z0-9 ]*PRIVATE KEY-----/g;
|
|
58878
|
+
function asRecord4(value) {
|
|
58879
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
58880
|
+
}
|
|
58881
|
+
function nonEmptyString(value) {
|
|
58882
|
+
return typeof value === "string" && value !== "";
|
|
58883
|
+
}
|
|
58884
|
+
function streamedText(event) {
|
|
58885
|
+
const data = asRecord4(event.data);
|
|
58886
|
+
if (data === null) return null;
|
|
58887
|
+
if (event.event === "assistant" || event.event === "thinking") {
|
|
58888
|
+
if (typeof data.text !== "string") return null;
|
|
58889
|
+
return {
|
|
58890
|
+
stream: event.event,
|
|
58891
|
+
served: true,
|
|
58892
|
+
text: data.text,
|
|
58893
|
+
withText: (text) => ({ ...event, data: { ...data, text } })
|
|
58894
|
+
};
|
|
58895
|
+
}
|
|
58896
|
+
if (event.event !== "interaction_update") return null;
|
|
58897
|
+
if (data.type === "text-delta" || data.type === "thinking-delta") {
|
|
58898
|
+
if (typeof data.text !== "string") return null;
|
|
58899
|
+
return {
|
|
58900
|
+
stream: data.type,
|
|
58901
|
+
served: false,
|
|
58902
|
+
text: data.text,
|
|
58903
|
+
withText: (text) => ({ ...event, data: { ...data, text } })
|
|
58904
|
+
};
|
|
58905
|
+
}
|
|
58906
|
+
const update = asRecord4(data.taskUpdate);
|
|
58907
|
+
if (data.type !== "tool-call-delta" || !nonEmptyString(data.callId) || update?.type !== "text-delta" && update?.type !== "thinking-delta" || typeof update.text !== "string") {
|
|
58908
|
+
return null;
|
|
58909
|
+
}
|
|
58910
|
+
return {
|
|
58911
|
+
stream: `${data.callId}/${update.type}`,
|
|
58912
|
+
served: false,
|
|
58913
|
+
text: update.text,
|
|
58914
|
+
withText: (text) => ({ ...event, data: { ...data, taskUpdate: { ...update, text } } })
|
|
58915
|
+
};
|
|
58916
|
+
}
|
|
58917
|
+
function endsTurnText(event, callIds) {
|
|
58918
|
+
const data = asRecord4(event.data);
|
|
58919
|
+
if (event.event === "tool_call") {
|
|
58920
|
+
if (!nonEmptyString(data?.callId) || !nonEmptyString(data.name) || callIds.has(data.callId)) return false;
|
|
58921
|
+
callIds.add(data.callId);
|
|
58922
|
+
return true;
|
|
58923
|
+
}
|
|
58924
|
+
return event.event === "interaction_update" && data?.type === "user-message-appended" && nonEmptyString(asRecord4(data.userMessage)?.text);
|
|
58925
|
+
}
|
|
58926
|
+
function splitLikeParts(parts, redacted) {
|
|
58927
|
+
const joined = parts.join("");
|
|
58928
|
+
if (joined === redacted) return [...parts];
|
|
58929
|
+
const shorter = Math.min(joined.length, redacted.length);
|
|
58930
|
+
let start = 0;
|
|
58931
|
+
while (start < shorter && joined[start] === redacted[start]) start += 1;
|
|
58932
|
+
let same = 0;
|
|
58933
|
+
while (same < shorter - start && joined.at(-1 - same) === redacted.at(-1 - same)) same += 1;
|
|
58934
|
+
const changedEnd = joined.length - same;
|
|
58935
|
+
const changed = redacted.slice(start, redacted.length - same);
|
|
58936
|
+
let at = 0;
|
|
58937
|
+
let placed = false;
|
|
58938
|
+
return parts.map((part, index) => {
|
|
58939
|
+
const end = at + part.length;
|
|
58940
|
+
let text = joined.slice(at, Math.min(end, start));
|
|
58941
|
+
if (!placed && (end > start || index === parts.length - 1)) {
|
|
58942
|
+
text += changed;
|
|
58943
|
+
placed = true;
|
|
58944
|
+
}
|
|
58945
|
+
text += joined.slice(Math.max(at, changedEnd), end);
|
|
58946
|
+
at = end;
|
|
58947
|
+
return text;
|
|
58948
|
+
});
|
|
58949
|
+
}
|
|
58950
|
+
function removeCutSecretRests(texts) {
|
|
58951
|
+
let cut = false;
|
|
58952
|
+
return texts.map((text) => {
|
|
58953
|
+
const part = cut ? text.replace(SECRET_REST, "") : text;
|
|
58954
|
+
if (part !== "") cut = part.endsWith(REDACTED2);
|
|
58955
|
+
return part;
|
|
58956
|
+
});
|
|
58957
|
+
}
|
|
58958
|
+
function keepTextAfterRedactions(parts) {
|
|
58959
|
+
const out = [...parts];
|
|
58960
|
+
let open3 = null;
|
|
58961
|
+
for (const k2 of out.keys()) {
|
|
58962
|
+
if (open3 !== null) {
|
|
58963
|
+
const rest = SECRET_REST.exec(out[k2])?.[0] ?? "";
|
|
58964
|
+
out[open3] += rest;
|
|
58965
|
+
out[k2] = out[k2].slice(rest.length);
|
|
58966
|
+
if (out[k2] === "") continue;
|
|
58967
|
+
}
|
|
58968
|
+
open3 = out[k2].endsWith(REDACTED2) ? k2 : null;
|
|
58969
|
+
}
|
|
58970
|
+
return out;
|
|
58971
|
+
}
|
|
58972
|
+
function redactStreamTexts(texts) {
|
|
58973
|
+
const parts = removeCutSecretRests(texts);
|
|
58974
|
+
const joined = parts.join("");
|
|
58975
|
+
const redacted = redactText(joined.replace(CUT_PRIVATE_KEY_BODY, REDACTED2));
|
|
58976
|
+
const alone = redacted === joined ? parts : parts.map(redactText);
|
|
58977
|
+
const split = alone.join("") === redacted ? alone : splitLikeParts(parts, redacted);
|
|
58978
|
+
return keepTextAfterRedactions(split);
|
|
58979
|
+
}
|
|
58980
|
+
function mapStreams(events, servedOnly, map2) {
|
|
58981
|
+
const out = [...events];
|
|
58982
|
+
const callIds = /* @__PURE__ */ new Set();
|
|
58983
|
+
let streams = /* @__PURE__ */ new Map();
|
|
58984
|
+
const mapStreamsSoFar = () => {
|
|
58985
|
+
for (const indices of streams.values()) {
|
|
58986
|
+
const deltas = indices.map((index) => streamedText(out[index]));
|
|
58987
|
+
const texts = map2(deltas.map((delta) => delta.text));
|
|
58988
|
+
for (const [k2, delta] of deltas.entries()) {
|
|
58989
|
+
if (texts[k2] !== delta.text) out[indices[k2]] = delta.withText(texts[k2]);
|
|
58990
|
+
}
|
|
58991
|
+
}
|
|
58992
|
+
streams = /* @__PURE__ */ new Map();
|
|
58993
|
+
};
|
|
58994
|
+
for (const [index, event] of events.entries()) {
|
|
58995
|
+
if (endsTurnText(event, callIds)) mapStreamsSoFar();
|
|
58996
|
+
const delta = streamedText(event);
|
|
58997
|
+
if (delta === null || servedOnly && !delta.served) continue;
|
|
58998
|
+
const indices = streams.get(delta.stream);
|
|
58999
|
+
if (indices) indices.push(index);
|
|
59000
|
+
else streams.set(delta.stream, [index]);
|
|
59001
|
+
}
|
|
59002
|
+
mapStreamsSoFar();
|
|
59003
|
+
return out;
|
|
59004
|
+
}
|
|
59005
|
+
function redactCursorRunEvents(events, { servedOnly = false } = {}) {
|
|
59006
|
+
return mapStreams(events, servedOnly, redactStreamTexts);
|
|
59007
|
+
}
|
|
59008
|
+
|
|
58658
59009
|
// ../core/src/previewLinks.ts
|
|
58659
59010
|
var SKILL_LINE = /^(?:[-*+]\s+)?[*`]*PREVIEW:\s+(\S+)\s+(https?:\/\/\S+)\s*$/gm;
|
|
58660
59011
|
var MARKDOWN_LINE = (
|
|
@@ -58761,7 +59112,7 @@ function envelopeRuns(transcript) {
|
|
|
58761
59112
|
for (const item of parsed.runs) {
|
|
58762
59113
|
const run = item;
|
|
58763
59114
|
if (typeof run?.runId !== "string" || typeof run.sse !== "string") continue;
|
|
58764
|
-
runs.push({ runId: run.runId, events: parseSseEvents(run.sse) });
|
|
59115
|
+
runs.push({ runId: run.runId, events: redactCursorRunEvents(parseSseEvents(run.sse), { servedOnly: true }) });
|
|
58765
59116
|
}
|
|
58766
59117
|
return runs;
|
|
58767
59118
|
}
|
|
@@ -58774,7 +59125,7 @@ function safeJson(text) {
|
|
|
58774
59125
|
}
|
|
58775
59126
|
|
|
58776
59127
|
// ../core/src/scrubSentryEvent.ts
|
|
58777
|
-
var
|
|
59128
|
+
var REDACTED3 = "[Filtered]";
|
|
58778
59129
|
var SECRET_KEY_PATTERN = /^(?:authorization|proxy-authorization|cookie|set-cookie|x-api-key|x-auth-token|x-csrf-token|x-supabase-auth|api[_-]?key|access[_-]?token|refresh[_-]?token|id[_-]?token|session[_-]?token|client[_-]?secret|password|passwd|secret|signature|token|jwt)$/i;
|
|
58779
59130
|
var SECRET_QUERY_PARAM_PATTERN = /^(?:token|access_token|refresh_token|id_token|api_key|apikey|key|code|signature|password|secret|client_secret|state)$/i;
|
|
58780
59131
|
var BEARER_TOKEN_PATTERN = /\bBearer\s+[\w\-._~+/=]+/gi;
|
|
@@ -58783,7 +59134,7 @@ var EMBEDDED_QUERY_SECRET_PATTERN = /([?&](?:token|access_token|refresh_token|id
|
|
|
58783
59134
|
var URL_FIELD_NAMES = /* @__PURE__ */ new Set(["url", "to", "from", "href", "request_url"]);
|
|
58784
59135
|
var MAX_DEPTH = 10;
|
|
58785
59136
|
function scrubString(value) {
|
|
58786
|
-
return value.replace(POWERPLANT_TOKEN_PATTERN,
|
|
59137
|
+
return value.replace(POWERPLANT_TOKEN_PATTERN, REDACTED3).replace(BEARER_TOKEN_PATTERN, `Bearer ${REDACTED3}`).replace(JWT_PATTERN, REDACTED3).replace(EMBEDDED_QUERY_SECRET_PATTERN, `$1${REDACTED3}`);
|
|
58787
59138
|
}
|
|
58788
59139
|
function scrubUrl(url2) {
|
|
58789
59140
|
if (!url2) return url2;
|
|
@@ -58797,7 +59148,7 @@ function scrubUrl(url2) {
|
|
|
58797
59148
|
let mutated = false;
|
|
58798
59149
|
for (const key of [...parsed.searchParams.keys()]) {
|
|
58799
59150
|
if (SECRET_QUERY_PARAM_PATTERN.test(key)) {
|
|
58800
|
-
parsed.searchParams.set(key,
|
|
59151
|
+
parsed.searchParams.set(key, REDACTED3);
|
|
58801
59152
|
mutated = true;
|
|
58802
59153
|
}
|
|
58803
59154
|
}
|
|
@@ -58813,7 +59164,7 @@ function scrubValue(value, depth = 0) {
|
|
|
58813
59164
|
const out = {};
|
|
58814
59165
|
for (const [key, child] of Object.entries(value)) {
|
|
58815
59166
|
if (SECRET_KEY_PATTERN.test(key) || SECRET_QUERY_PARAM_PATTERN.test(key)) {
|
|
58816
|
-
out[key] =
|
|
59167
|
+
out[key] = REDACTED3;
|
|
58817
59168
|
} else if (URL_FIELD_NAMES.has(key.toLowerCase()) && typeof child === "string") {
|
|
58818
59169
|
out[key] = scrubUrl(child);
|
|
58819
59170
|
} else {
|
|
@@ -58834,7 +59185,7 @@ function scrubSentryEvent(event) {
|
|
|
58834
59185
|
event.request.headers = scrubValue(event.request.headers);
|
|
58835
59186
|
}
|
|
58836
59187
|
if (event.request.cookies !== void 0) {
|
|
58837
|
-
event.request.cookies =
|
|
59188
|
+
event.request.cookies = REDACTED3;
|
|
58838
59189
|
}
|
|
58839
59190
|
if (event.request.data !== void 0) {
|
|
58840
59191
|
event.request.data = scrubValue(event.request.data);
|
|
@@ -58970,35 +59321,6 @@ function describeSyncSignals(member, now, noUpload = "no sessions uploaded yet")
|
|
|
58970
59321
|
return `${line.charAt(0).toUpperCase()}${line.slice(1)}.`;
|
|
58971
59322
|
}
|
|
58972
59323
|
|
|
58973
|
-
// ../core/src/rawTranscriptBundle.ts
|
|
58974
|
-
var RAW_TRANSCRIPT_BUNDLE_CONTENT_TYPE = "application/vnd.powerplant.raw-transcript-bundle";
|
|
58975
|
-
var RAW_TRANSCRIPT_PART_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
58976
|
-
var MAX_RAW_TRANSCRIPT_PARTS = 500;
|
|
58977
|
-
var MAX_HEADER_BYTES = 128 * 1024;
|
|
58978
|
-
var LENGTH_PREFIX_BYTES = 4;
|
|
58979
|
-
var headerSchema = external_exports.object({
|
|
58980
|
-
version: external_exports.literal(1),
|
|
58981
|
-
mainBytes: external_exports.number().int().positive(),
|
|
58982
|
-
parts: external_exports.array(external_exports.object({ part: external_exports.string().regex(RAW_TRANSCRIPT_PART_PATTERN), bytes: external_exports.number().int().positive() })).max(MAX_RAW_TRANSCRIPT_PARTS),
|
|
58983
|
-
subagents: external_exports.object({ found: external_exports.number().int().nonnegative(), skipped: external_exports.number().int().nonnegative() })
|
|
58984
|
-
}).refine((header) => new Set(header.parts.map((entry) => entry.part)).size === header.parts.length, {
|
|
58985
|
-
message: "part ids must be unique"
|
|
58986
|
-
}).refine((header) => header.subagents.found === header.parts.length + header.subagents.skipped, {
|
|
58987
|
-
message: "subagents.found must equal the parts carried plus the files skipped"
|
|
58988
|
-
});
|
|
58989
|
-
function encodeRawTranscriptBundle(bundle) {
|
|
58990
|
-
const fields = headerSchema.parse({
|
|
58991
|
-
version: 1,
|
|
58992
|
-
mainBytes: bundle.main.byteLength,
|
|
58993
|
-
parts: bundle.parts.map((entry) => ({ part: entry.part, bytes: entry.body.byteLength })),
|
|
58994
|
-
subagents: bundle.subagents
|
|
58995
|
-
});
|
|
58996
|
-
const header = Buffer.from(JSON.stringify(fields), "utf8");
|
|
58997
|
-
const prefix = Buffer.alloc(LENGTH_PREFIX_BYTES);
|
|
58998
|
-
prefix.writeUInt32BE(header.byteLength);
|
|
58999
|
-
return Buffer.concat([prefix, header, bundle.main, ...bundle.parts.map((entry) => entry.body)]);
|
|
59000
|
-
}
|
|
59001
|
-
|
|
59002
59324
|
// ../core/src/version.ts
|
|
59003
59325
|
function parseReleaseVersion(version2) {
|
|
59004
59326
|
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version2);
|
|
@@ -59026,6 +59348,8 @@ var HARNESS_SESSION_FLAG_RULES = {
|
|
|
59026
59348
|
evidence: external_exports.object({ thread_source: external_exports.enum(["subagent", "guardian_review"]) })
|
|
59027
59349
|
},
|
|
59028
59350
|
{ flag: "subagent", evidence: external_exports.object({ source: external_exports.object({ subagent: external_exports.object({}) }) }) },
|
|
59351
|
+
// A review run's reviewer names its kind as a string: {subagent: "review"}.
|
|
59352
|
+
{ flag: "subagent", evidence: external_exports.object({ source: external_exports.object({ subagent: external_exports.string().min(1) }) }) },
|
|
59029
59353
|
{
|
|
59030
59354
|
flag: "standaloneReview",
|
|
59031
59355
|
evidence: external_exports.object({
|
|
@@ -59058,6 +59382,36 @@ function parseHarnessSessionFlags(source, evidence) {
|
|
|
59058
59382
|
}
|
|
59059
59383
|
return flags;
|
|
59060
59384
|
}
|
|
59385
|
+
var CODEX_CHILD_THREAD_RULES = [
|
|
59386
|
+
{ kind: "review", evidence: external_exports.object({ source: external_exports.object({ subagent: external_exports.literal("review") }) }) },
|
|
59387
|
+
{
|
|
59388
|
+
kind: "spawned-agent",
|
|
59389
|
+
evidence: external_exports.object({ source: external_exports.object({ subagent: external_exports.object({ thread_spawn: external_exports.object({}) }) }) })
|
|
59390
|
+
},
|
|
59391
|
+
{ kind: "judge", evidence: external_exports.object({ thread_source: external_exports.literal("guardian_review") }) },
|
|
59392
|
+
{ kind: "judge", evidence: external_exports.object({ source: external_exports.object({ subagent: external_exports.object({ other: external_exports.literal("guardian") }) }) }) }
|
|
59393
|
+
];
|
|
59394
|
+
var codexThreadId = external_exports.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i).transform((threadId2) => threadId2.toLowerCase());
|
|
59395
|
+
var CODEX_PARENT_THREAD_RULES = [
|
|
59396
|
+
external_exports.object({ parent_thread_id: codexThreadId }).transform((meta3) => meta3.parent_thread_id),
|
|
59397
|
+
external_exports.object({ source: external_exports.object({ subagent: external_exports.object({ thread_spawn: external_exports.object({ parent_thread_id: codexThreadId }) }) }) }).transform((meta3) => meta3.source.subagent.thread_spawn.parent_thread_id)
|
|
59398
|
+
];
|
|
59399
|
+
function parseCodexChildThread(sessionMeta) {
|
|
59400
|
+
if (parseHarnessSessionFlags("codex", sessionMeta).subagent !== true) return {};
|
|
59401
|
+
const kind = CODEX_CHILD_THREAD_RULES.find((rule) => rule.evidence.safeParse(sessionMeta).success)?.kind;
|
|
59402
|
+
let parentThreadId;
|
|
59403
|
+
for (const rule of CODEX_PARENT_THREAD_RULES) {
|
|
59404
|
+
const parsed = rule.safeParse(sessionMeta);
|
|
59405
|
+
if (parsed.success) {
|
|
59406
|
+
parentThreadId = parsed.data;
|
|
59407
|
+
break;
|
|
59408
|
+
}
|
|
59409
|
+
}
|
|
59410
|
+
return {
|
|
59411
|
+
...kind !== void 0 && { kind },
|
|
59412
|
+
...parentThreadId !== void 0 && { parentThreadId }
|
|
59413
|
+
};
|
|
59414
|
+
}
|
|
59061
59415
|
|
|
59062
59416
|
// ../core/src/recentSessionPage.ts
|
|
59063
59417
|
var RECENT_SESSION_WINDOW_MS = 72 * 60 * 60 * 1e3;
|
|
@@ -78691,11 +79045,11 @@ function enrichInvokeAgentFromStream(callId, final, recordOutputs) {
|
|
|
78691
79045
|
}
|
|
78692
79046
|
const usage = isObjectLike(final.usage) ? final.usage : void 0;
|
|
78693
79047
|
if (usage) {
|
|
78694
|
-
const input2 =
|
|
78695
|
-
const output2 =
|
|
79048
|
+
const input2 = tokenCount2(usage.inputTokens) ?? tokenCount2(usage.promptTokens) ?? tokenCount2(usage.tokens);
|
|
79049
|
+
const output2 = tokenCount2(usage.outputTokens) ?? tokenCount2(usage.completionTokens);
|
|
78696
79050
|
addTokensToSpan(span, Ra, input2);
|
|
78697
79051
|
addTokensToSpan(span, Pa, output2);
|
|
78698
|
-
addTokensToSpan(span, Ma,
|
|
79052
|
+
addTokensToSpan(span, Ma, tokenCount2(usage.totalTokens) ?? sum(input2, output2));
|
|
78699
79053
|
}
|
|
78700
79054
|
if (recordOutputs) {
|
|
78701
79055
|
const parts = partsFromTextAndToolCalls(final.text, final.toolCalls);
|
|
@@ -78823,9 +79177,9 @@ function enrichSpanOnEnd(span, data, channelOptions) {
|
|
|
78823
79177
|
}
|
|
78824
79178
|
const usage = isObjectLike(result.usage) ? result.usage : void 0;
|
|
78825
79179
|
if (usage) {
|
|
78826
|
-
const inputTokens =
|
|
78827
|
-
const outputTokens =
|
|
78828
|
-
const totalTokens =
|
|
79180
|
+
const inputTokens = tokenCount2(usage.inputTokens) ?? tokenCount2(usage.promptTokens) ?? tokenCount2(usage.tokens);
|
|
79181
|
+
const outputTokens = tokenCount2(usage.outputTokens) ?? tokenCount2(usage.completionTokens);
|
|
79182
|
+
const totalTokens = tokenCount2(usage.totalTokens) ?? sum(inputTokens, outputTokens);
|
|
78829
79183
|
if (inputTokens !== void 0) {
|
|
78830
79184
|
span.setAttribute(Ra, inputTokens);
|
|
78831
79185
|
}
|
|
@@ -78874,7 +79228,7 @@ function getFinishReason(result) {
|
|
|
78874
79228
|
}
|
|
78875
79229
|
return isObjectLike(finishReason) ? asString(finishReason.unified) : void 0;
|
|
78876
79230
|
}
|
|
78877
|
-
function
|
|
79231
|
+
function tokenCount2(value) {
|
|
78878
79232
|
return asNumber(value) ?? (isObjectLike(value) ? asNumber(value.total) : void 0);
|
|
78879
79233
|
}
|
|
78880
79234
|
function buildOutputMessages2(parts, finishReason) {
|
|
@@ -85910,9 +86264,9 @@ import {
|
|
|
85910
86264
|
fsyncSync,
|
|
85911
86265
|
mkdirSync as mkdirSync8,
|
|
85912
86266
|
openSync as openSync6,
|
|
85913
|
-
renameSync as
|
|
86267
|
+
renameSync as renameSync5,
|
|
85914
86268
|
unlinkSync as unlinkSync2,
|
|
85915
|
-
writeFileSync as
|
|
86269
|
+
writeFileSync as writeFileSync6
|
|
85916
86270
|
} from "node:fs";
|
|
85917
86271
|
import os11 from "node:os";
|
|
85918
86272
|
import path13 from "node:path";
|
|
@@ -86173,7 +86527,7 @@ function xmlEscape(value) {
|
|
|
86173
86527
|
}
|
|
86174
86528
|
|
|
86175
86529
|
// ../../apps/syncservice/src/status.ts
|
|
86176
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync7, readdirSync as readdirSync5, readFileSync as
|
|
86530
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync7, readdirSync as readdirSync5, readFileSync as readFileSync8, renameSync as renameSync4, rmSync as rmSync7, writeFileSync as writeFileSync5 } from "node:fs";
|
|
86177
86531
|
import os10 from "node:os";
|
|
86178
86532
|
import path12 from "node:path";
|
|
86179
86533
|
|
|
@@ -86270,7 +86624,7 @@ import { createHmac } from "node:crypto";
|
|
|
86270
86624
|
// ../../apps/syncservice/src/http.ts
|
|
86271
86625
|
var import_undici = __toESM(require_undici(), 1);
|
|
86272
86626
|
var CLI_VERSION_HEADER = "x-powerplant-cli-version";
|
|
86273
|
-
var cliVersion = true ? "0.4.
|
|
86627
|
+
var cliVersion = true ? "0.4.20" : "0.0.0-checkout";
|
|
86274
86628
|
function cliVersionHeaders() {
|
|
86275
86629
|
return { [CLI_VERSION_HEADER]: cliVersion, "user-agent": `powerplant-cli/${cliVersion}` };
|
|
86276
86630
|
}
|
|
@@ -86604,7 +86958,8 @@ function bindDiagnosticEnrollment(diagnostic, token) {
|
|
|
86604
86958
|
}
|
|
86605
86959
|
|
|
86606
86960
|
// ../../apps/syncservice/src/codexSessions.ts
|
|
86607
|
-
import {
|
|
86961
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
86962
|
+
import { closeSync as closeSync3, mkdirSync as mkdirSync2, openSync as openSync3, renameSync, rmSync as rmSync2, writeSync } from "node:fs";
|
|
86608
86963
|
import os6 from "node:os";
|
|
86609
86964
|
import path5 from "node:path";
|
|
86610
86965
|
import { setImmediate as yieldToEventLoop } from "node:timers/promises";
|
|
@@ -86810,7 +87165,8 @@ function isPermissionError(cause) {
|
|
|
86810
87165
|
}
|
|
86811
87166
|
|
|
86812
87167
|
// ../../apps/syncservice/src/scan.ts
|
|
86813
|
-
import {
|
|
87168
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
87169
|
+
import { closeSync as closeSync2, fstatSync, openSync as openSync2, readdirSync as readdirSync2, readFileSync as readFileSync5, readSync as readSync2, statSync as statSync2 } from "node:fs";
|
|
86814
87170
|
import { StringDecoder } from "node:string_decoder";
|
|
86815
87171
|
var SESSION_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
86816
87172
|
var ACTIVE_GRACE_MS = SESSION_ACTIVE_WINDOW_MS;
|
|
@@ -86860,6 +87216,87 @@ function readFirstRecord(file2, matches) {
|
|
|
86860
87216
|
closeSync2(fd);
|
|
86861
87217
|
}
|
|
86862
87218
|
}
|
|
87219
|
+
var TranscriptChangedError = class extends Error {
|
|
87220
|
+
};
|
|
87221
|
+
function sha256(bytes) {
|
|
87222
|
+
return createHash2("sha256").update(bytes).digest("hex");
|
|
87223
|
+
}
|
|
87224
|
+
function readTranscriptIfExists(file2) {
|
|
87225
|
+
let buffer;
|
|
87226
|
+
try {
|
|
87227
|
+
buffer = readFileSync5(file2);
|
|
87228
|
+
} catch (error63) {
|
|
87229
|
+
if (error63.code === "ENOENT") return null;
|
|
87230
|
+
throw error63;
|
|
87231
|
+
}
|
|
87232
|
+
const records = buffer.subarray(0, wholeRecordsEnd(buffer));
|
|
87233
|
+
return {
|
|
87234
|
+
text: records.toString("utf8"),
|
|
87235
|
+
prefix: { bytes: records.byteLength, sha256: sha256(records), fileBytes: buffer.byteLength }
|
|
87236
|
+
};
|
|
87237
|
+
}
|
|
87238
|
+
function wholeRecordsEnd(buffer) {
|
|
87239
|
+
const afterLastNewline = buffer.lastIndexOf(10) + 1;
|
|
87240
|
+
if (afterLastNewline === buffer.byteLength) return afterLastNewline;
|
|
87241
|
+
const tail = buffer.subarray(afterLastNewline).toString("utf8");
|
|
87242
|
+
return parseJsonlLine(tail) === null ? afterLastNewline : buffer.byteLength;
|
|
87243
|
+
}
|
|
87244
|
+
function readTranscriptPrefix(file2, prefix, unchangedSinceRead) {
|
|
87245
|
+
let fd;
|
|
87246
|
+
try {
|
|
87247
|
+
fd = openSync2(file2, "r");
|
|
87248
|
+
} catch (error63) {
|
|
87249
|
+
if (error63.code === "ENOENT") {
|
|
87250
|
+
throw new TranscriptChangedError("The transcript is gone since the collector read it. The next pass reads it again.");
|
|
87251
|
+
}
|
|
87252
|
+
throw error63;
|
|
87253
|
+
}
|
|
87254
|
+
try {
|
|
87255
|
+
if (unchangedSinceRead && fstatSync(fd).size !== prefix.fileBytes) {
|
|
87256
|
+
throw new TranscriptChangedError("The transcript changed after the collector read it. The next pass reads it again.");
|
|
87257
|
+
}
|
|
87258
|
+
const buffer = Buffer.alloc(prefix.bytes);
|
|
87259
|
+
let filled = 0;
|
|
87260
|
+
while (filled < prefix.bytes) {
|
|
87261
|
+
const read = readSync2(fd, buffer, filled, prefix.bytes - filled, filled);
|
|
87262
|
+
if (read === 0) break;
|
|
87263
|
+
filled += read;
|
|
87264
|
+
}
|
|
87265
|
+
if (filled < prefix.bytes || sha256(buffer) !== prefix.sha256) {
|
|
87266
|
+
throw new TranscriptChangedError("The transcript changed after the collector read it. The next pass reads it again.");
|
|
87267
|
+
}
|
|
87268
|
+
return buffer.toString("utf8");
|
|
87269
|
+
} finally {
|
|
87270
|
+
closeSync2(fd);
|
|
87271
|
+
}
|
|
87272
|
+
}
|
|
87273
|
+
var FirstLineTooLongError = class extends Error {
|
|
87274
|
+
};
|
|
87275
|
+
function readFirstLine(file2, maxBytes) {
|
|
87276
|
+
let fd;
|
|
87277
|
+
try {
|
|
87278
|
+
fd = openSync2(file2, "r");
|
|
87279
|
+
} catch (error63) {
|
|
87280
|
+
if (error63.code === "ENOENT") return null;
|
|
87281
|
+
throw error63;
|
|
87282
|
+
}
|
|
87283
|
+
const chunk = Buffer.alloc(READ_CHUNK_BYTES);
|
|
87284
|
+
const read = [];
|
|
87285
|
+
let total = 0;
|
|
87286
|
+
try {
|
|
87287
|
+
for (; ; ) {
|
|
87288
|
+
const bytes = readSync2(fd, chunk, 0, chunk.length, null);
|
|
87289
|
+
if (bytes === 0) return { line: Buffer.concat(read).toString("utf8"), complete: false };
|
|
87290
|
+
const newline = chunk.subarray(0, bytes).indexOf(10);
|
|
87291
|
+
read.push(Buffer.from(chunk.subarray(0, newline === -1 ? bytes : newline)));
|
|
87292
|
+
if (newline !== -1) return { line: Buffer.concat(read).toString("utf8"), complete: true };
|
|
87293
|
+
total += bytes;
|
|
87294
|
+
if (total >= maxBytes) throw new FirstLineTooLongError(`the first line is longer than ${maxBytes} bytes`);
|
|
87295
|
+
}
|
|
87296
|
+
} finally {
|
|
87297
|
+
closeSync2(fd);
|
|
87298
|
+
}
|
|
87299
|
+
}
|
|
86863
87300
|
function listDirs(dir) {
|
|
86864
87301
|
return listEntries(dir).filter((e) => e.isDirectory()).map((e) => `${dir}/${e.name}`);
|
|
86865
87302
|
}
|
|
@@ -86890,6 +87327,10 @@ function dominantKey(counts) {
|
|
|
86890
87327
|
}
|
|
86891
87328
|
return best;
|
|
86892
87329
|
}
|
|
87330
|
+
function laterTimestamp(a, b2) {
|
|
87331
|
+
if (a === void 0 || b2 === void 0) return a ?? b2;
|
|
87332
|
+
return Date.parse(b2) > Date.parse(a) ? b2 : a;
|
|
87333
|
+
}
|
|
86893
87334
|
function describeReadError(error63) {
|
|
86894
87335
|
return error63 instanceof Error ? error63.message : String(error63);
|
|
86895
87336
|
}
|
|
@@ -87143,33 +87584,58 @@ function rolloutIdentity(rolloutPath) {
|
|
|
87143
87584
|
function codexMergedTranscriptsDir(home) {
|
|
87144
87585
|
return path5.join(home, ".config", "powerplant", "codex");
|
|
87145
87586
|
}
|
|
87146
|
-
|
|
87147
|
-
|
|
87148
|
-
|
|
87587
|
+
var MERGED_VERSION_HEX = 32;
|
|
87588
|
+
var MERGED_TRANSCRIPT_NAME = new RegExp(`^(?<threadId>.+)\\.[0-9a-f]{${MERGED_VERSION_HEX}}\\.jsonl$`);
|
|
87589
|
+
function mergedTranscriptPath(mergedTranscriptsDir, threadId2, pagesKey) {
|
|
87590
|
+
const version2 = createHash3("sha256").update(pagesKey).digest("hex").slice(0, MERGED_VERSION_HEX);
|
|
87591
|
+
return path5.join(mergedTranscriptsDir, `${threadId2}.${version2}.jsonl`);
|
|
87592
|
+
}
|
|
87593
|
+
function mergedThreadIds(mergedTranscriptsDir) {
|
|
87594
|
+
const threadIds = /* @__PURE__ */ new Set();
|
|
87595
|
+
for (const name of listFiles(mergedTranscriptsDir, ".jsonl")) {
|
|
87596
|
+
threadIds.add(MERGED_TRANSCRIPT_NAME.exec(name)?.groups?.threadId ?? name.slice(0, -".jsonl".length));
|
|
87597
|
+
}
|
|
87598
|
+
return threadIds;
|
|
87599
|
+
}
|
|
87600
|
+
function removeOtherMergedTranscripts(keep) {
|
|
87601
|
+
const dir = path5.dirname(keep);
|
|
87602
|
+
const name = path5.basename(keep);
|
|
87603
|
+
const threadId2 = MERGED_TRANSCRIPT_NAME.exec(name)?.groups?.threadId;
|
|
87604
|
+
if (threadId2 === void 0) return;
|
|
87605
|
+
for (const other of listFiles(dir, "")) {
|
|
87606
|
+
if (other === name || !other.startsWith(`${threadId2}.`) || other.endsWith(".tmp")) continue;
|
|
87607
|
+
rmSync2(path5.join(dir, other), { force: true });
|
|
87608
|
+
}
|
|
87609
|
+
}
|
|
87610
|
+
function mergedPage(pageText2) {
|
|
87611
|
+
return pageText2.endsWith("\n") ? pageText2 : `${pageText2}
|
|
87612
|
+
`;
|
|
87149
87613
|
}
|
|
87150
87614
|
var MergedTranscriptWriter = class {
|
|
87151
|
-
constructor(
|
|
87152
|
-
this.mergedTranscriptPath =
|
|
87153
|
-
mkdirSync2(path5.dirname(
|
|
87154
|
-
this.tmpPath = `${
|
|
87615
|
+
constructor(mergedTranscriptPath2) {
|
|
87616
|
+
this.mergedTranscriptPath = mergedTranscriptPath2;
|
|
87617
|
+
mkdirSync2(path5.dirname(mergedTranscriptPath2), { recursive: true, mode: 448 });
|
|
87618
|
+
this.tmpPath = `${mergedTranscriptPath2}.${process.pid}.tmp`;
|
|
87155
87619
|
this.fd = openSync3(this.tmpPath, "w", 384);
|
|
87156
87620
|
}
|
|
87157
87621
|
mergedTranscriptPath;
|
|
87158
87622
|
tmpPath;
|
|
87159
87623
|
fd;
|
|
87160
|
-
|
|
87161
|
-
append(
|
|
87624
|
+
/** Writes one page as mergedPage returned it. */
|
|
87625
|
+
append(page) {
|
|
87162
87626
|
if (this.fd === void 0) throw new Error("merged transcript already closed");
|
|
87163
|
-
|
|
87164
|
-
|
|
87165
|
-
|
|
87166
|
-
|
|
87167
|
-
|
|
87168
|
-
|
|
87627
|
+
writeSync(this.fd, page);
|
|
87628
|
+
}
|
|
87629
|
+
/** Publishes the file under its version's name in one rename, then
|
|
87630
|
+
* removes the thread's other merged transcripts. A pass that saw older
|
|
87631
|
+
* pages can publish after this one and remove this file: this version is
|
|
87632
|
+
* then missing, so the next pass writes it again, and an upload from it
|
|
87633
|
+
* waits for that pass (TranscriptChangedError). No pass reads another
|
|
87634
|
+
* version's bytes under this name. */
|
|
87635
|
+
finish() {
|
|
87169
87636
|
this.close();
|
|
87170
|
-
utimesSync(this.tmpPath, mtimeMs / 1e3, mtimeMs / 1e3);
|
|
87171
87637
|
renameSync(this.tmpPath, this.mergedTranscriptPath);
|
|
87172
|
-
|
|
87638
|
+
removeOtherMergedTranscripts(this.mergedTranscriptPath);
|
|
87173
87639
|
}
|
|
87174
87640
|
abandon() {
|
|
87175
87641
|
this.close();
|
|
@@ -87181,13 +87647,40 @@ var MergedTranscriptWriter = class {
|
|
|
87181
87647
|
this.fd = void 0;
|
|
87182
87648
|
}
|
|
87183
87649
|
};
|
|
87184
|
-
|
|
87185
|
-
|
|
87186
|
-
|
|
87187
|
-
|
|
87650
|
+
var MAX_SESSION_META_BYTES = 1024 * 1024;
|
|
87651
|
+
function readFirstSessionMeta(rolloutPath) {
|
|
87652
|
+
let first;
|
|
87653
|
+
try {
|
|
87654
|
+
first = readFirstLine(rolloutPath, MAX_SESSION_META_BYTES);
|
|
87655
|
+
} catch (error63) {
|
|
87656
|
+
if (error63 instanceof FirstLineTooLongError) return { state: "fault", reason: error63.message };
|
|
87657
|
+
throw error63;
|
|
87658
|
+
}
|
|
87659
|
+
if (first === null) return { state: "pending" };
|
|
87660
|
+
let record2;
|
|
87661
|
+
try {
|
|
87662
|
+
record2 = JSON.parse(first.line);
|
|
87663
|
+
} catch {
|
|
87664
|
+
record2 = void 0;
|
|
87665
|
+
}
|
|
87666
|
+
if (!isRecord2(record2)) {
|
|
87667
|
+
return first.complete ? { state: "fault", reason: "the first line is not a JSON object (expected a session_meta record)" } : { state: "pending" };
|
|
87668
|
+
}
|
|
87669
|
+
if (record2.type !== "session_meta" || !isRecord2(record2.payload)) {
|
|
87670
|
+
return { state: "fault", reason: "the first record is not a session_meta record with a payload object" };
|
|
87671
|
+
}
|
|
87672
|
+
return { state: "session_meta", timestamp: record2.timestamp, meta: record2.payload };
|
|
87673
|
+
}
|
|
87674
|
+
function rolloutLocation(rolloutPath, first = readFirstSessionMeta(rolloutPath)) {
|
|
87675
|
+
const unknown2 = { kind: "session", codexClient: "unknown", totals: null, fastTotals: null };
|
|
87676
|
+
if (first.state === "session_meta") return locationOf(first.timestamp, first.meta);
|
|
87677
|
+
if (first.state === "pending") return unknown2;
|
|
87678
|
+
const record2 = readFirstRecord(rolloutPath, (candidate) => candidate.type === "session_meta" && isRecord2(candidate.payload));
|
|
87679
|
+
return record2 === null ? unknown2 : locationOf(record2.timestamp, record2.payload);
|
|
87680
|
+
}
|
|
87681
|
+
function locationOf(startedAt, meta3) {
|
|
87188
87682
|
const { cwd, id, session_id: fallbackId } = meta3;
|
|
87189
87683
|
const sessionId = id ?? fallbackId;
|
|
87190
|
-
const startedAt = record2?.timestamp;
|
|
87191
87684
|
return {
|
|
87192
87685
|
codexClient: classifyCodexClient(meta3),
|
|
87193
87686
|
kind: isSidecarThread(meta3) ? "subagent" : "session",
|
|
@@ -87198,9 +87691,314 @@ function rolloutLocation(rolloutPath) {
|
|
|
87198
87691
|
...typeof cwd === "string" && cwd !== "" && { cwd }
|
|
87199
87692
|
};
|
|
87200
87693
|
}
|
|
87694
|
+
var FirstLineFormatError = class extends Error {
|
|
87695
|
+
};
|
|
87696
|
+
function readThreadLink(rolloutPath) {
|
|
87697
|
+
const first = readFirstSessionMeta(rolloutPath);
|
|
87698
|
+
if (first.state === "fault") throw new FirstLineFormatError(first.reason);
|
|
87699
|
+
return first.state === "pending" ? void 0 : parseCodexChildThread(first.meta);
|
|
87700
|
+
}
|
|
87201
87701
|
function isSidecarThread(meta3) {
|
|
87202
87702
|
return parseHarnessSessionFlags("codex", meta3).subagent === true;
|
|
87203
87703
|
}
|
|
87704
|
+
var ATTACHED_CHILD_KINDS = /* @__PURE__ */ new Set(["review"]);
|
|
87705
|
+
function attachedParentOf(link) {
|
|
87706
|
+
return link?.kind !== void 0 && ATTACHED_CHILD_KINDS.has(link.kind) ? link.parentThreadId : void 0;
|
|
87707
|
+
}
|
|
87708
|
+
function threadIdTimeMs(threadId2) {
|
|
87709
|
+
if (!SESSION_UUID.test(threadId2) || threadId2[14] !== "7") return void 0;
|
|
87710
|
+
return Number.parseInt(threadId2.slice(0, 8) + threadId2.slice(9, 13), 16);
|
|
87711
|
+
}
|
|
87712
|
+
var THREAD_ID_TIME_MARGIN_MS = 24 * 60 * 60 * 1e3;
|
|
87713
|
+
var PARENT_BRING_IN_MARGIN_MS = 60 * 1e3;
|
|
87714
|
+
function mayBeParentOf(parent, childMadeMs) {
|
|
87715
|
+
return (parent.madeMs === void 0 || parent.madeMs <= childMadeMs + PARENT_BRING_IN_MARGIN_MS) && parent.writtenMs >= childMadeMs;
|
|
87716
|
+
}
|
|
87717
|
+
function mayBeChildOf(threadTimeMs, parentTimeMs) {
|
|
87718
|
+
return threadTimeMs === void 0 || parentTimeMs === void 0 || threadTimeMs + THREAD_ID_TIME_MARGIN_MS >= parentTimeMs;
|
|
87719
|
+
}
|
|
87720
|
+
var CodexThreadLinks = class {
|
|
87721
|
+
/** `readLink` is a test seam for the first-record read. */
|
|
87722
|
+
constructor(readLink = readThreadLink) {
|
|
87723
|
+
this.readLink = readLink;
|
|
87724
|
+
}
|
|
87725
|
+
readLink;
|
|
87726
|
+
byPath = /* @__PURE__ */ new Map();
|
|
87727
|
+
/** Rollouts whose read failed, with the file's changed time at that
|
|
87728
|
+
* failure: a failure is reported once for each changed time. */
|
|
87729
|
+
failedAt = /* @__PURE__ */ new Map();
|
|
87730
|
+
/** Rollouts whose first line was not whole yet, with the file's changed
|
|
87731
|
+
* time and how many reads found it so. */
|
|
87732
|
+
incomplete = /* @__PURE__ */ new Map();
|
|
87733
|
+
/** Rollouts whose first line is a format fault. Codex writes a rollout's
|
|
87734
|
+
* first line once and only appends after it, so the fault belongs to the
|
|
87735
|
+
* file, not to one of its changed times: it is reported once. */
|
|
87736
|
+
faulted = /* @__PURE__ */ new Set();
|
|
87737
|
+
/** Unlinked rollouts counted as an unreadable child of their plausible
|
|
87738
|
+
* parents, by thread id, with the time in that id. */
|
|
87739
|
+
unresolved = /* @__PURE__ */ new Map();
|
|
87740
|
+
/** Threads whose capture counted such a rollout that has since linked or
|
|
87741
|
+
* gone: each comes into every pass until its corrected capture is
|
|
87742
|
+
* uploaded, refused, or has failed CORRECTION_UPLOAD_ATTEMPTS times
|
|
87743
|
+
* (settleRecoveries), with the session the last pass made of it and how
|
|
87744
|
+
* many uploads of it failed. */
|
|
87745
|
+
recovering = /* @__PURE__ */ new Map();
|
|
87746
|
+
/** Keeps what another read of the same first record found. */
|
|
87747
|
+
remember(rolloutPath, link) {
|
|
87748
|
+
this.byPath.set(rolloutPath, link);
|
|
87749
|
+
this.failedAt.delete(rolloutPath);
|
|
87750
|
+
this.incomplete.delete(rolloutPath);
|
|
87751
|
+
this.faulted.delete(rolloutPath);
|
|
87752
|
+
}
|
|
87753
|
+
/** What an earlier read of this rollout's first record found, if any. */
|
|
87754
|
+
known(rolloutPath) {
|
|
87755
|
+
return this.byPath.get(rolloutPath);
|
|
87756
|
+
}
|
|
87757
|
+
/** Unlinked rollouts that made their plausible parents partial: thread
|
|
87758
|
+
* id to the time in that id. */
|
|
87759
|
+
unresolvedRollouts() {
|
|
87760
|
+
return [...this.unresolved];
|
|
87761
|
+
}
|
|
87762
|
+
/** The rollout linked or is gone: its plausible parents are to be sent
|
|
87763
|
+
* again (recover). */
|
|
87764
|
+
resolve(threadId2) {
|
|
87765
|
+
this.unresolved.delete(threadId2);
|
|
87766
|
+
}
|
|
87767
|
+
/**
|
|
87768
|
+
* Reads again the first record of each unlinked rollout still on this
|
|
87769
|
+
* machine (unresolved), in a pass's window or not, and whether or not a
|
|
87770
|
+
* possible parent uploads now: only a read can link it, and until it links
|
|
87771
|
+
* its guessed parents stay partial. A read failure that clears with no
|
|
87772
|
+
* file changing (EMFILE, EIO, a permission restored) so clears on the next
|
|
87773
|
+
* pass. A read that failed earlier this pass (`unread`) is not repeated, a
|
|
87774
|
+
* first line found torn is left to its rule (isTorn), and a format fault
|
|
87775
|
+
* is final for the file. Returns the failures to report, once for each
|
|
87776
|
+
* change to a file, and every thread whose read failed this call, so the
|
|
87777
|
+
* pass does not read it again (childrenOf).
|
|
87778
|
+
*/
|
|
87779
|
+
async readUnresolved(threads, unread) {
|
|
87780
|
+
const failed2 = [];
|
|
87781
|
+
const stillUnread = [];
|
|
87782
|
+
for (const threadId2 of this.unresolved.keys()) {
|
|
87783
|
+
const thread = threads.get(threadId2);
|
|
87784
|
+
if (thread === void 0 || unread.has(threadId2)) continue;
|
|
87785
|
+
const { rolloutPath, changedMs } = thread;
|
|
87786
|
+
if (this.byPath.has(rolloutPath) || this.faulted.has(rolloutPath) || this.isTorn(rolloutPath, changedMs)) continue;
|
|
87787
|
+
if (!await this.read(rolloutPath, changedMs, failed2)) stillUnread.push(threadId2);
|
|
87788
|
+
}
|
|
87789
|
+
return { failed: failed2, unread: stillUnread };
|
|
87790
|
+
}
|
|
87791
|
+
recover(threadId2) {
|
|
87792
|
+
if (!this.recovering.has(threadId2)) this.recovering.set(threadId2, { session: null, failedUploads: 0 });
|
|
87793
|
+
}
|
|
87794
|
+
/** The threads owed a corrected capture. */
|
|
87795
|
+
recoveringThreads() {
|
|
87796
|
+
return [...this.recovering.keys()];
|
|
87797
|
+
}
|
|
87798
|
+
/** The session this pass made of a thread owed a corrected capture. */
|
|
87799
|
+
noteCollected(threadId2, session) {
|
|
87800
|
+
const owed = this.recovering.get(threadId2);
|
|
87801
|
+
if (owed !== void 0) owed.session = session;
|
|
87802
|
+
}
|
|
87803
|
+
/** Drops a thread owed a corrected capture that has nothing to upload
|
|
87804
|
+
* (gone, out of scope, not a session, or its own read failed): nothing
|
|
87805
|
+
* corrects it until it changes, and a change admits it through the
|
|
87806
|
+
* window. */
|
|
87807
|
+
forgetRecovery(threadId2) {
|
|
87808
|
+
this.recovering.delete(threadId2);
|
|
87809
|
+
}
|
|
87810
|
+
/**
|
|
87811
|
+
* After a pass's uploads, from what became of each owed thread's session
|
|
87812
|
+
* this pass: "shipped" (the server holds it) and "refused" (this machine
|
|
87813
|
+
* will never ship it: no raw storage, another machine owns it, its
|
|
87814
|
+
* metadata is out of policy or deferred) settle it. "failed" counts one
|
|
87815
|
+
* failed upload; a thread whose uploads failed CORRECTION_UPLOAD_ATTEMPTS
|
|
87816
|
+
* times is settled too, and returned so the pass can name it once. A
|
|
87817
|
+
* thread the pass left out ("waiting") stays owed at no cost.
|
|
87818
|
+
*/
|
|
87819
|
+
settleRecoveries(outcome) {
|
|
87820
|
+
const givenUp = [];
|
|
87821
|
+
for (const [threadId2, owed] of this.recovering) {
|
|
87822
|
+
const result = owed.session === null ? "waiting" : outcome(owed.session);
|
|
87823
|
+
owed.session = null;
|
|
87824
|
+
if (result === "waiting") continue;
|
|
87825
|
+
if (result === "failed") {
|
|
87826
|
+
owed.failedUploads += 1;
|
|
87827
|
+
if (owed.failedUploads < CORRECTION_UPLOAD_ATTEMPTS) continue;
|
|
87828
|
+
givenUp.push(threadId2);
|
|
87829
|
+
}
|
|
87830
|
+
this.recovering.delete(threadId2);
|
|
87831
|
+
}
|
|
87832
|
+
return givenUp;
|
|
87833
|
+
}
|
|
87834
|
+
/** Records that a read of the rollout failed at the thread's changed time;
|
|
87835
|
+
* true when the failure is new and so is to be reported: the first for
|
|
87836
|
+
* the rollout, or the first since the thread changed, as a read error
|
|
87837
|
+
* (EACCES, EIO) can clear when the file changes. Every reader of a Codex
|
|
87838
|
+
* rollout reports through this or noteFormatFault, so a failure is named
|
|
87839
|
+
* once whether the thread is in the pass's window or not. */
|
|
87840
|
+
noteFailure(rolloutPath, changedMs) {
|
|
87841
|
+
const known = this.failedAt.get(rolloutPath) === changedMs;
|
|
87842
|
+
this.failedAt.set(rolloutPath, changedMs);
|
|
87843
|
+
return !known;
|
|
87844
|
+
}
|
|
87845
|
+
/** Records that the rollout's first line is a format fault; true the
|
|
87846
|
+
* first time for the file, whatever Codex appends to it later. */
|
|
87847
|
+
noteFormatFault(rolloutPath) {
|
|
87848
|
+
const known = this.faulted.has(rolloutPath);
|
|
87849
|
+
this.faulted.add(rolloutPath);
|
|
87850
|
+
return !known;
|
|
87851
|
+
}
|
|
87852
|
+
/**
|
|
87853
|
+
* The child threads of each parent uploading now. `threads` maps each
|
|
87854
|
+
* thread on this machine to its lead rollout, the one its first record is
|
|
87855
|
+
* read from, and the thread's newest change; what is known about other
|
|
87856
|
+
* paths is forgotten. Reads the first record of each thread not yet known
|
|
87857
|
+
* that could be such a child: a thread whose id time is more than
|
|
87858
|
+
* THREAD_ID_TIME_MARGIN_MS older than every parent's is not read.
|
|
87859
|
+
* `parentsWrittenMs` gives each uploading thread's newest write, and each
|
|
87860
|
+
* entry of `threads` its own (`writtenMs`). A rollout whose read fails, or
|
|
87861
|
+
* that is in `unread` (its read failed earlier this pass, already
|
|
87862
|
+
* reported), counts as an unreadable child of each uploading thread that
|
|
87863
|
+
* may be its parent, or whose attached child, or a thread that child
|
|
87864
|
+
* started, may be (mayBeParentOf), so no capture is recorded complete
|
|
87865
|
+
* while one of its children may be missing. Only a Codex rollout
|
|
87866
|
+
* named by a UUIDv7 thread id can be such a child: a failed read of any
|
|
87867
|
+
* other file in the store is reported and counts for no parent. A failed
|
|
87868
|
+
* read is tried again on every pass and returned in `failed` once for each
|
|
87869
|
+
* change to the file, so one bad file never ends the pass or repeats in
|
|
87870
|
+
* every pass's report.
|
|
87871
|
+
*/
|
|
87872
|
+
async childrenOf(threads, parentsWrittenMs, unread = /* @__PURE__ */ new Set()) {
|
|
87873
|
+
const leadPaths = new Set([...threads.values()].map((thread) => thread.rolloutPath));
|
|
87874
|
+
for (const known of [this.byPath, this.failedAt, this.incomplete, this.faulted]) {
|
|
87875
|
+
for (const rolloutPath of known.keys()) if (!leadPaths.has(rolloutPath)) known.delete(rolloutPath);
|
|
87876
|
+
}
|
|
87877
|
+
const children = /* @__PURE__ */ new Map();
|
|
87878
|
+
const failed2 = [];
|
|
87879
|
+
const parents = new Map([...parentsWrittenMs].map(([threadId2, writtenMs]) => [threadId2, { madeMs: threadIdTimeMs(threadId2), writtenMs }]));
|
|
87880
|
+
if (parents.size === 0) return { children, failed: failed2 };
|
|
87881
|
+
const parentTimes = [...parents];
|
|
87882
|
+
const childrenOfParent = (parentThreadId) => {
|
|
87883
|
+
const found = children.get(parentThreadId) ?? { attached: [], leftOut: [], unreadable: [] };
|
|
87884
|
+
children.set(parentThreadId, found);
|
|
87885
|
+
return found;
|
|
87886
|
+
};
|
|
87887
|
+
const unreadable = [];
|
|
87888
|
+
for (const [threadId2, { rolloutPath, changedMs }] of threads) {
|
|
87889
|
+
if (parents.has(threadId2)) continue;
|
|
87890
|
+
if (!this.byPath.has(rolloutPath)) {
|
|
87891
|
+
const rollout = SESSION_UUID.test(threadId2);
|
|
87892
|
+
const madeMs = threadIdTimeMs(threadId2);
|
|
87893
|
+
if (rollout && !parentTimes.some(([, parent]) => mayBeChildOf(madeMs, parent.madeMs))) continue;
|
|
87894
|
+
if (this.isTorn(rolloutPath, changedMs)) continue;
|
|
87895
|
+
if (unread.has(threadId2) || !await this.read(rolloutPath, changedMs, failed2)) {
|
|
87896
|
+
if (rollout && madeMs !== void 0) unreadable.push([threadId2, madeMs]);
|
|
87897
|
+
continue;
|
|
87898
|
+
}
|
|
87899
|
+
}
|
|
87900
|
+
const link = this.byPath.get(rolloutPath);
|
|
87901
|
+
if (link?.parentThreadId === void 0 || !parents.has(link.parentThreadId)) continue;
|
|
87902
|
+
const found = childrenOfParent(link.parentThreadId);
|
|
87903
|
+
(attachedParentOf(link) === void 0 ? found.leftOut : found.attached).push(threadId2);
|
|
87904
|
+
}
|
|
87905
|
+
const covered = this.addDescendantsOfAttached(threads, parents, children);
|
|
87906
|
+
for (const [threadId2, madeMs] of unreadable) {
|
|
87907
|
+
let counted = false;
|
|
87908
|
+
for (const [rootId, root] of parentTimes) {
|
|
87909
|
+
const possible = [
|
|
87910
|
+
root,
|
|
87911
|
+
...(covered.get(rootId) ?? []).map((id) => ({ madeMs: threadIdTimeMs(id), writtenMs: threads.get(id).writtenMs }))
|
|
87912
|
+
];
|
|
87913
|
+
if (!possible.some((parent) => mayBeParentOf(parent, madeMs))) continue;
|
|
87914
|
+
childrenOfParent(rootId).unreadable.push(threadId2);
|
|
87915
|
+
counted = true;
|
|
87916
|
+
}
|
|
87917
|
+
if (counted) this.unresolved.set(threadId2, madeMs);
|
|
87918
|
+
}
|
|
87919
|
+
return { children, failed: failed2 };
|
|
87920
|
+
}
|
|
87921
|
+
/** The threads an attached child started (a reviewer's own agents and
|
|
87922
|
+
* judges), and theirs in turn, name a thread that uploads as a part, not
|
|
87923
|
+
* as a session: they are left out of the root session's capture. A thread
|
|
87924
|
+
* met twice is counted once, so a malformed link cycle ends. Returns, for
|
|
87925
|
+
* each root, its attached children and the threads they started: the
|
|
87926
|
+
* threads whose own children count under the root. */
|
|
87927
|
+
addDescendantsOfAttached(threads, parents, children) {
|
|
87928
|
+
const childrenByParent = /* @__PURE__ */ new Map();
|
|
87929
|
+
for (const [threadId2, { rolloutPath }] of threads) {
|
|
87930
|
+
const parentThreadId = this.byPath.get(rolloutPath)?.parentThreadId;
|
|
87931
|
+
if (parentThreadId === void 0 || parents.has(parentThreadId)) continue;
|
|
87932
|
+
childrenByParent.set(parentThreadId, [...childrenByParent.get(parentThreadId) ?? [], threadId2]);
|
|
87933
|
+
}
|
|
87934
|
+
const covered = /* @__PURE__ */ new Map();
|
|
87935
|
+
for (const [rootId, found] of children) {
|
|
87936
|
+
const seen = new Set(found.attached);
|
|
87937
|
+
const pending = [...found.attached];
|
|
87938
|
+
for (let threadId2 = pending.pop(); threadId2 !== void 0; threadId2 = pending.pop()) {
|
|
87939
|
+
for (const descendant of childrenByParent.get(threadId2) ?? []) {
|
|
87940
|
+
if (seen.has(descendant)) continue;
|
|
87941
|
+
seen.add(descendant);
|
|
87942
|
+
found.leftOut.push(descendant);
|
|
87943
|
+
pending.push(descendant);
|
|
87944
|
+
}
|
|
87945
|
+
}
|
|
87946
|
+
covered.set(rootId, [...seen]);
|
|
87947
|
+
}
|
|
87948
|
+
return covered;
|
|
87949
|
+
}
|
|
87950
|
+
/** Whether the rollout's first line stayed unwhole through
|
|
87951
|
+
* INCOMPLETE_FIRST_LINE_READS reads and the file has not changed since:
|
|
87952
|
+
* Codex left it torn, so reading it again would find the same. Such a file
|
|
87953
|
+
* is part of one line and holds no turns or usage, so no capture misses
|
|
87954
|
+
* anything without it: it is reported, never counted as a child. */
|
|
87955
|
+
isTorn(rolloutPath, changedMs) {
|
|
87956
|
+
const seen = this.incomplete.get(rolloutPath);
|
|
87957
|
+
return seen?.changedMs === changedMs && seen.reads >= INCOMPLETE_FIRST_LINE_READS;
|
|
87958
|
+
}
|
|
87959
|
+
/** One read of a rollout's first record; false when it failed. */
|
|
87960
|
+
async read(rolloutPath, changedMs, failed2) {
|
|
87961
|
+
await yieldToEventLoop();
|
|
87962
|
+
let link;
|
|
87963
|
+
try {
|
|
87964
|
+
link = this.readLink(rolloutPath);
|
|
87965
|
+
} catch (error63) {
|
|
87966
|
+
const fault = error63 instanceof FirstLineFormatError;
|
|
87967
|
+
if (fault ? this.noteFormatFault(rolloutPath) : this.noteFailure(rolloutPath, changedMs)) failed2.push({ rolloutPath, error: error63 });
|
|
87968
|
+
return false;
|
|
87969
|
+
}
|
|
87970
|
+
if (link !== void 0) {
|
|
87971
|
+
this.remember(rolloutPath, link);
|
|
87972
|
+
return true;
|
|
87973
|
+
}
|
|
87974
|
+
const seen = this.incomplete.get(rolloutPath);
|
|
87975
|
+
const reads = seen?.changedMs === changedMs ? seen.reads + 1 : 1;
|
|
87976
|
+
this.incomplete.set(rolloutPath, { changedMs, reads });
|
|
87977
|
+
if (reads >= INCOMPLETE_FIRST_LINE_READS && this.noteFailure(rolloutPath, changedMs)) {
|
|
87978
|
+
failed2.push({ rolloutPath, error: new Error(`the first line is still not whole after ${reads} reads of the unchanged file`) });
|
|
87979
|
+
}
|
|
87980
|
+
return true;
|
|
87981
|
+
}
|
|
87982
|
+
};
|
|
87983
|
+
var CodexThreadReads = class {
|
|
87984
|
+
byThread = /* @__PURE__ */ new Map();
|
|
87985
|
+
/** What a full read found when the thread's files were `files`
|
|
87986
|
+
* (threadFilesKey): its summary and prefix, null for a thread holding no
|
|
87987
|
+
* session; undefined when none is kept for those files. */
|
|
87988
|
+
get(threadId2, files) {
|
|
87989
|
+
const kept = this.byThread.get(threadId2);
|
|
87990
|
+
return kept?.files === files ? kept.read : void 0;
|
|
87991
|
+
}
|
|
87992
|
+
set(threadId2, files, read) {
|
|
87993
|
+
this.byThread.set(threadId2, { files, read });
|
|
87994
|
+
}
|
|
87995
|
+
/** Forgets the threads no longer on this machine. */
|
|
87996
|
+
keepOnly(threadIds) {
|
|
87997
|
+
for (const threadId2 of this.byThread.keys()) if (!threadIds.has(threadId2)) this.byThread.delete(threadId2);
|
|
87998
|
+
}
|
|
87999
|
+
};
|
|
88000
|
+
var CORRECTION_UPLOAD_ATTEMPTS = 3;
|
|
88001
|
+
var INCOMPLETE_FIRST_LINE_READS = 3;
|
|
87204
88002
|
function repoFullNameFrom(url2) {
|
|
87205
88003
|
const fullName = parseGithubRepo(url2);
|
|
87206
88004
|
return fullName ? { repoFullName: fullName } : {};
|
|
@@ -87240,8 +88038,9 @@ async function collectCodexSessions(options = {}) {
|
|
|
87240
88038
|
const capture2 = options.capture ?? captureEverythingUnresolved;
|
|
87241
88039
|
const maxBytes = options.maxTranscriptBytes ?? MAX_TRANSCRIPT_FILE_BYTES;
|
|
87242
88040
|
const mergedTranscriptsDir = options.mergedTranscriptsDir ?? codexMergedTranscriptsDir(options.homeDir ?? os6.homedir());
|
|
88041
|
+
const links = options.threadLinks ?? new CodexThreadLinks();
|
|
87243
88042
|
const items = [];
|
|
87244
|
-
const candidates =
|
|
88043
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
87245
88044
|
const unreadable = [];
|
|
87246
88045
|
const skips = { skippedOutOfScope: 0, skippedNoRepo: 0 };
|
|
87247
88046
|
let skippedSubagentThreads = 0;
|
|
@@ -87287,80 +88086,135 @@ async function collectCodexSessions(options = {}) {
|
|
|
87287
88086
|
if (thread === void 0) byThread.set(file2.identity.threadId, [file2]);
|
|
87288
88087
|
else thread.push(file2);
|
|
87289
88088
|
}
|
|
87290
|
-
|
|
88089
|
+
const threadOf = (threadId2, stored) => {
|
|
88090
|
+
const named = stored.filter((file2) => {
|
|
88091
|
+
if (file2.discoverySessionIds === void 0) return true;
|
|
88092
|
+
const { sessionId } = rolloutLocation(file2.rolloutPath);
|
|
88093
|
+
return sessionId !== void 0 && file2.discoverySessionIds.includes(sessionId);
|
|
88094
|
+
});
|
|
88095
|
+
if (named.length === 0) return null;
|
|
88096
|
+
const threadFiles = named.length > 1 ? orderPages(named) : named;
|
|
88097
|
+
return {
|
|
88098
|
+
threadId: threadId2,
|
|
88099
|
+
harnessSessionId: harnessSessionIdOf(threadFiles[0].rolloutPath),
|
|
88100
|
+
files: threadFiles,
|
|
88101
|
+
...threadFiles.length > 1 && {
|
|
88102
|
+
mergedTranscriptPath: mergedTranscriptPath(mergedTranscriptsDir, threadId2, threadFilesKey(threadFiles))
|
|
88103
|
+
},
|
|
88104
|
+
mtimeMs: Math.max(...threadFiles.map((file2) => file2.mtimeMs)),
|
|
88105
|
+
sizeBytes: threadFiles.reduce((sum2, file2) => sum2 + file2.sizeBytes, 0),
|
|
88106
|
+
archived: threadFiles.every((file2) => file2.archived)
|
|
88107
|
+
};
|
|
88108
|
+
};
|
|
88109
|
+
const countedTooLarge = /* @__PURE__ */ new Set();
|
|
88110
|
+
const tooLarge = (thread) => {
|
|
88111
|
+
if (thread.sizeBytes <= maxBytes) return false;
|
|
88112
|
+
if (countedTooLarge.has(thread.threadId)) return true;
|
|
88113
|
+
countedTooLarge.add(thread.threadId);
|
|
88114
|
+
skippedTooLarge += 1;
|
|
88115
|
+
observeFileDiagnostic(options.onFileDiagnostic, {
|
|
88116
|
+
path: thread.files[0].rolloutPath,
|
|
88117
|
+
reason: "too_large",
|
|
88118
|
+
sizeBytes: thread.sizeBytes
|
|
88119
|
+
});
|
|
88120
|
+
return true;
|
|
88121
|
+
};
|
|
88122
|
+
const mergedBefore = options.locationsOnly ? /* @__PURE__ */ new Set() : mergedThreadIds(mergedTranscriptsDir);
|
|
88123
|
+
const evaluated = /* @__PURE__ */ new Set();
|
|
88124
|
+
const failedThreads = /* @__PURE__ */ new Set();
|
|
88125
|
+
const report = (rolloutPath, name, error63) => {
|
|
88126
|
+
unreadable.push(`${name}: ${describeReadError(error63)}`);
|
|
88127
|
+
observeFileDiagnostic(options.onFileDiagnostic, {
|
|
88128
|
+
path: rolloutPath,
|
|
88129
|
+
reason: "unreadable",
|
|
88130
|
+
sizeBytes: null,
|
|
88131
|
+
errorCode: diagnosticErrorCode(error63)
|
|
88132
|
+
});
|
|
88133
|
+
};
|
|
88134
|
+
const reportOnce = (files2, name, error63) => {
|
|
88135
|
+
const lead = leadRollout(files2);
|
|
88136
|
+
if (links.noteFailure(lead.rolloutPath, newestChange(files2))) report(lead.rolloutPath, name, error63);
|
|
88137
|
+
};
|
|
88138
|
+
const namedParents = [];
|
|
88139
|
+
const bringInParentOf = (link) => {
|
|
88140
|
+
if (link?.parentThreadId !== void 0) namedParents.push(link.parentThreadId);
|
|
88141
|
+
};
|
|
88142
|
+
const parentsOfAttachedChildren = [];
|
|
88143
|
+
const unlinkedInWindow = [];
|
|
88144
|
+
const admit = async (threadId2, observed, admission) => {
|
|
88145
|
+
const applyWindow = admission === "window";
|
|
88146
|
+
const guess = admission === "possible-parent";
|
|
87291
88147
|
const stored = dedupeStoreCopies(observed);
|
|
87292
|
-
|
|
88148
|
+
const lead = leadRollout(stored);
|
|
88149
|
+
let harnessSessionId = harnessSessionIdOf(lead.rolloutPath);
|
|
87293
88150
|
try {
|
|
87294
|
-
|
|
87295
|
-
|
|
87296
|
-
|
|
87297
|
-
const neverMerged = mergedTranscriptPath !== void 0 && !options.locationsOnly && statIfExists(mergedTranscriptPath) === null;
|
|
87298
|
-
if (!neverMerged) continue;
|
|
88151
|
+
if (applyWindow && options.sinceMs !== void 0 && newestChange(stored) < options.sinceMs) {
|
|
88152
|
+
const neverMerged = stored.length > 1 && !options.locationsOnly && !mergedBefore.has(threadId2);
|
|
88153
|
+
if (!neverMerged) return;
|
|
87299
88154
|
}
|
|
88155
|
+
evaluated.add(threadId2);
|
|
87300
88156
|
await yieldToEventLoop();
|
|
87301
|
-
const
|
|
87302
|
-
|
|
87303
|
-
|
|
87304
|
-
|
|
87305
|
-
|
|
87306
|
-
|
|
87307
|
-
|
|
87308
|
-
|
|
87309
|
-
|
|
87310
|
-
|
|
87311
|
-
|
|
87312
|
-
|
|
87313
|
-
|
|
87314
|
-
|
|
87315
|
-
|
|
87316
|
-
|
|
87317
|
-
|
|
87318
|
-
|
|
87319
|
-
continue;
|
|
87320
|
-
}
|
|
87321
|
-
const location = rolloutLocation(base.rolloutPath);
|
|
88157
|
+
const thread = threadOf(threadId2, stored);
|
|
88158
|
+
if (thread === null) return;
|
|
88159
|
+
harnessSessionId = thread.harnessSessionId;
|
|
88160
|
+
const base = thread.files[0];
|
|
88161
|
+
const first = readFirstSessionMeta(base.rolloutPath);
|
|
88162
|
+
const link = first.state === "session_meta" ? parseCodexChildThread(first.meta) : void 0;
|
|
88163
|
+
if (link !== void 0) links.remember(base.rolloutPath, link);
|
|
88164
|
+
if (guess && (first.state === "pending" || link?.parentThreadId !== void 0)) return;
|
|
88165
|
+
bringInParentOf(link);
|
|
88166
|
+
if (first.state === "fault") {
|
|
88167
|
+
if (applyWindow) unlinkedInWindow.push(threadId2);
|
|
88168
|
+
if (links.noteFormatFault(lead.rolloutPath)) {
|
|
88169
|
+
report(lead.rolloutPath, `${harnessSessionId} (child-thread link)`, new Error(first.reason));
|
|
88170
|
+
}
|
|
88171
|
+
}
|
|
88172
|
+
if (guess ? thread.sizeBytes > maxBytes : tooLarge(thread)) return;
|
|
88173
|
+
const location = rolloutLocation(base.rolloutPath, first);
|
|
88174
|
+
if (guess && location.kind === "subagent") return;
|
|
87322
88175
|
if (location.kind === "subagent") {
|
|
87323
|
-
|
|
87324
|
-
|
|
88176
|
+
const parentThreadId = attachedParentOf(link);
|
|
88177
|
+
if (!options.locationsOnly && parentThreadId !== void 0) parentsOfAttachedChildren.push(parentThreadId);
|
|
88178
|
+
else skippedSubagentThreads += 1;
|
|
88179
|
+
return;
|
|
87325
88180
|
}
|
|
87326
88181
|
const decision = captureDecision(location.cwd, capture2);
|
|
87327
88182
|
if (!decision.capture) {
|
|
87328
|
-
countCaptureSkip(skips, decision.reason);
|
|
87329
|
-
|
|
88183
|
+
if (!guess) countCaptureSkip(skips, decision.reason);
|
|
88184
|
+
return;
|
|
87330
88185
|
}
|
|
87331
|
-
const thread = {
|
|
87332
|
-
harnessSessionId,
|
|
87333
|
-
files: threadFiles,
|
|
87334
|
-
...threadFiles.length > 1 && mergedTranscriptPath !== void 0 && { mergedTranscriptPath },
|
|
87335
|
-
mtimeMs,
|
|
87336
|
-
archived: threadFiles.every((file2) => file2.archived)
|
|
87337
|
-
};
|
|
87338
88186
|
if (options.locationsOnly) {
|
|
87339
88187
|
items.push({
|
|
87340
|
-
mtimeMs,
|
|
88188
|
+
mtimeMs: thread.mtimeMs,
|
|
87341
88189
|
transcriptPath: base.rolloutPath,
|
|
87342
88190
|
...location.cwd && { cwd: location.cwd },
|
|
87343
88191
|
item: {
|
|
87344
88192
|
harness: "codex-local",
|
|
87345
88193
|
harnessSessionId,
|
|
87346
|
-
status: now - mtimeMs < ACTIVE_GRACE_MS ? "active" : "inactive",
|
|
88194
|
+
status: now - thread.mtimeMs < ACTIVE_GRACE_MS ? "active" : "inactive",
|
|
87347
88195
|
archived: thread.archived,
|
|
87348
88196
|
capturedVia: decision.via
|
|
87349
88197
|
}
|
|
87350
88198
|
});
|
|
87351
|
-
|
|
88199
|
+
return;
|
|
87352
88200
|
}
|
|
87353
|
-
candidates.
|
|
87354
|
-
|
|
87355
|
-
|
|
87356
|
-
|
|
87357
|
-
|
|
87358
|
-
|
|
87359
|
-
|
|
87360
|
-
errorCode: diagnosticErrorCode(error63)
|
|
88201
|
+
candidates.set(threadId2, {
|
|
88202
|
+
thread,
|
|
88203
|
+
...location.cwd && { cwd: location.cwd },
|
|
88204
|
+
decision,
|
|
88205
|
+
children: [],
|
|
88206
|
+
leftOutChildren: 0,
|
|
88207
|
+
unreadableChildren: 0
|
|
87361
88208
|
});
|
|
88209
|
+
} catch (error63) {
|
|
88210
|
+
const known = links.known(lead.rolloutPath);
|
|
88211
|
+
bringInParentOf(known);
|
|
88212
|
+
if (applyWindow && known === void 0) unlinkedInWindow.push(threadId2);
|
|
88213
|
+
failedThreads.add(threadId2);
|
|
88214
|
+
reportOnce(stored, harnessSessionId, error63);
|
|
87362
88215
|
}
|
|
87363
|
-
}
|
|
88216
|
+
};
|
|
88217
|
+
for (const [threadId2, observed] of byThread) await admit(threadId2, observed, "window");
|
|
87364
88218
|
if (options.locationsOnly) {
|
|
87365
88219
|
return {
|
|
87366
88220
|
sessions: items.sort((a, b2) => b2.mtimeMs - a.mtimeMs),
|
|
@@ -87373,91 +88227,259 @@ async function collectCodexSessions(options = {}) {
|
|
|
87373
88227
|
largestMergedBytes: 0
|
|
87374
88228
|
};
|
|
87375
88229
|
}
|
|
87376
|
-
|
|
87377
|
-
|
|
88230
|
+
for (let index = 0; index < namedParents.length; index += 1) {
|
|
88231
|
+
const parentThreadId = namedParents[index];
|
|
88232
|
+
const observed = byThread.get(parentThreadId);
|
|
88233
|
+
if (!evaluated.has(parentThreadId) && observed !== void 0) await admit(parentThreadId, observed, "named");
|
|
88234
|
+
}
|
|
88235
|
+
const plausibleParentsOf = (childId, childMadeMs) => [...byThread].filter(
|
|
88236
|
+
([threadId2, observed]) => threadId2 !== childId && mayBeParentOf({ madeMs: threadIdTimeMs(threadId2), writtenMs: Math.max(...observed.map((file2) => file2.mtimeMs)) }, childMadeMs)
|
|
88237
|
+
);
|
|
88238
|
+
const rootThroughAttached = (threadId2) => {
|
|
88239
|
+
const seen = /* @__PURE__ */ new Set();
|
|
88240
|
+
let current = threadId2;
|
|
88241
|
+
let lastLink;
|
|
88242
|
+
while (!seen.has(current)) {
|
|
88243
|
+
seen.add(current);
|
|
88244
|
+
const observed = byThread.get(current);
|
|
88245
|
+
if (observed === void 0) return void 0;
|
|
88246
|
+
const link = links.known(leadRollout(dedupeStoreCopies(observed)).rolloutPath);
|
|
88247
|
+
if (link?.parentThreadId === void 0) return lastLink === void 0 || attachedParentOf(lastLink) !== void 0 ? current : void 0;
|
|
88248
|
+
lastLink = link;
|
|
88249
|
+
current = link.parentThreadId;
|
|
88250
|
+
}
|
|
88251
|
+
return void 0;
|
|
88252
|
+
};
|
|
88253
|
+
for (const childId of unlinkedInWindow) {
|
|
88254
|
+
const childMadeMs = threadIdTimeMs(childId);
|
|
88255
|
+
if (childMadeMs === void 0 || candidates.has(childId)) continue;
|
|
88256
|
+
for (const [threadId2, observed] of plausibleParentsOf(childId, childMadeMs)) {
|
|
88257
|
+
if (!evaluated.has(threadId2)) await admit(threadId2, observed, "possible-parent");
|
|
88258
|
+
const root = rootThroughAttached(threadId2);
|
|
88259
|
+
const rootObserved = root === void 0 ? void 0 : byThread.get(root);
|
|
88260
|
+
if (root !== void 0 && rootObserved !== void 0 && !evaluated.has(root)) await admit(root, rootObserved, "possible-parent");
|
|
88261
|
+
}
|
|
88262
|
+
}
|
|
88263
|
+
const leads = /* @__PURE__ */ new Map();
|
|
88264
|
+
for (const [threadId2, observed] of byThread) {
|
|
88265
|
+
const vouched = dedupeStoreCopies(observed).filter((file2) => file2.discoverySessionIds === void 0);
|
|
88266
|
+
if (vouched.length === 0) continue;
|
|
88267
|
+
leads.set(threadId2, {
|
|
88268
|
+
rolloutPath: leadRollout(vouched).rolloutPath,
|
|
88269
|
+
changedMs: newestChange(vouched),
|
|
88270
|
+
writtenMs: Math.max(...vouched.map((file2) => file2.mtimeMs))
|
|
88271
|
+
});
|
|
88272
|
+
}
|
|
88273
|
+
const retried = await links.readUnresolved(leads, failedThreads);
|
|
88274
|
+
for (const { rolloutPath, error: error63 } of retried.failed) report(rolloutPath, harnessSessionIdOf(rolloutPath), error63);
|
|
88275
|
+
for (const threadId2 of retried.unread) failedThreads.add(threadId2);
|
|
88276
|
+
for (const [childId, childMadeMs] of links.unresolvedRollouts()) {
|
|
88277
|
+
const observed = byThread.get(childId);
|
|
88278
|
+
if (observed !== void 0 && links.known(leadRollout(dedupeStoreCopies(observed)).rolloutPath) === void 0) continue;
|
|
88279
|
+
links.resolve(childId);
|
|
88280
|
+
for (const [threadId2] of plausibleParentsOf(childId, childMadeMs)) {
|
|
88281
|
+
const root = rootThroughAttached(threadId2);
|
|
88282
|
+
if (root !== void 0) links.recover(root);
|
|
88283
|
+
}
|
|
88284
|
+
}
|
|
88285
|
+
for (const threadId2 of links.recoveringThreads()) {
|
|
88286
|
+
const observed = byThread.get(threadId2);
|
|
88287
|
+
if (observed !== void 0 && !evaluated.has(threadId2)) await admit(threadId2, observed, "possible-parent");
|
|
88288
|
+
if (!candidates.has(threadId2)) links.forgetRecovery(threadId2);
|
|
88289
|
+
}
|
|
88290
|
+
for (const parentThreadId of parentsOfAttachedChildren) {
|
|
88291
|
+
if (!candidates.has(parentThreadId)) skippedSubagentThreads += 1;
|
|
88292
|
+
}
|
|
88293
|
+
const countReviewersLeftBehind = (parentThreadId) => {
|
|
88294
|
+
skippedSubagentThreads += parentsOfAttachedChildren.filter((threadId2) => threadId2 === parentThreadId).length;
|
|
88295
|
+
};
|
|
88296
|
+
const parentsWrittenMs = new Map([...candidates].map(([threadId2, candidate]) => [threadId2, candidate.thread.mtimeMs]));
|
|
88297
|
+
const { children, failed: failed2 } = await links.childrenOf(leads, parentsWrittenMs, failedThreads);
|
|
88298
|
+
for (const { rolloutPath, error: error63 } of failed2) report(rolloutPath, harnessSessionIdOf(rolloutPath), error63);
|
|
88299
|
+
for (const [parentThreadId, found] of children) {
|
|
88300
|
+
const parent = candidates.get(parentThreadId);
|
|
88301
|
+
parent.children = found.attached;
|
|
88302
|
+
parent.leftOutChildren = found.leftOut.length;
|
|
88303
|
+
parent.unreadableChildren = found.unreadable.length;
|
|
88304
|
+
}
|
|
88305
|
+
options.threadReads?.keepOnly(new Set(byThread.keys()));
|
|
88306
|
+
const rankOf = (candidate) => {
|
|
88307
|
+
let rank = candidate.thread.mtimeMs;
|
|
88308
|
+
for (const childId of candidate.children) {
|
|
88309
|
+
if (failedThreads.has(childId) || !RAW_TRANSCRIPT_PART_PATTERN.test(childId)) continue;
|
|
88310
|
+
const files2 = dedupeStoreCopies(byThread.get(childId));
|
|
88311
|
+
if (files2.reduce((sum2, file2) => sum2 + file2.sizeBytes, 0) > maxBytes) continue;
|
|
88312
|
+
rank = Math.max(rank, ...files2.map((file2) => file2.mtimeMs));
|
|
88313
|
+
}
|
|
88314
|
+
return rank;
|
|
88315
|
+
};
|
|
88316
|
+
const ranks = new Map([...candidates.values()].map((candidate) => [candidate, rankOf(candidate)]));
|
|
88317
|
+
const ordered = [...candidates.values()].sort((a, b2) => ranks.get(b2) - ranks.get(a));
|
|
88318
|
+
const chosen = options.maxSessions === void 0 ? ordered : ordered.slice(0, options.maxSessions);
|
|
88319
|
+
for (const candidate of ordered.slice(chosen.length)) countReviewersLeftBehind(candidate.thread.threadId);
|
|
87378
88320
|
const threadNames = chosen.length > 0 ? loadThreadNames(sessionsDir) : /* @__PURE__ */ new Map();
|
|
87379
|
-
|
|
87380
|
-
|
|
87381
|
-
const
|
|
88321
|
+
const readThread = (thread) => {
|
|
88322
|
+
const files2 = threadFilesKey(thread.files);
|
|
88323
|
+
const merged = thread.mergedTranscriptPath;
|
|
88324
|
+
const mergedFresh = merged === void 0 || statIfExists(merged) !== null;
|
|
88325
|
+
let read = mergedFresh ? options.threadReads?.get(thread.threadId, files2) : void 0;
|
|
88326
|
+
if (read === void 0) read = readThreadInFull(thread, !mergedFresh);
|
|
88327
|
+
if (read !== null && read !== "vanished" && merged !== void 0 && statIfExists(merged)?.size !== read.prefix.bytes) {
|
|
88328
|
+
read = readThreadInFull(thread, true);
|
|
88329
|
+
}
|
|
88330
|
+
if (read === "vanished") return "vanished";
|
|
88331
|
+
options.threadReads?.set(thread.threadId, files2, read);
|
|
88332
|
+
if (read === null) return null;
|
|
88333
|
+
const { summary, prefix } = read;
|
|
88334
|
+
if (merged === void 0) return { summary, prefix, mtimeMs: thread.mtimeMs, transcriptPath: thread.files[0].rolloutPath };
|
|
88335
|
+
mergedThreads += 1;
|
|
88336
|
+
largestMergedBytes = Math.max(largestMergedBytes, prefix.bytes);
|
|
88337
|
+
return { summary, prefix, mtimeMs: thread.mtimeMs, transcriptPath: merged };
|
|
88338
|
+
};
|
|
88339
|
+
const readThreadInFull = (thread, rewrite) => {
|
|
87382
88340
|
let writer;
|
|
87383
88341
|
try {
|
|
87384
|
-
const rewrite = thread.mergedTranscriptPath !== void 0 && !mergedTranscriptIsFresh(thread.mergedTranscriptPath, thread.mtimeMs);
|
|
87385
88342
|
if (thread.mergedTranscriptPath !== void 0 && rewrite) writer = new MergedTranscriptWriter(thread.mergedTranscriptPath);
|
|
87386
88343
|
const pages = [];
|
|
87387
88344
|
const usage = createCodexUsageReader();
|
|
87388
|
-
let
|
|
88345
|
+
let rolloutPrefix;
|
|
88346
|
+
const merged = { bytes: 0, sha256: createHash3("sha256") };
|
|
87389
88347
|
for (const [index, file2] of thread.files.entries()) {
|
|
87390
|
-
const
|
|
87391
|
-
if (
|
|
87392
|
-
|
|
87393
|
-
|
|
88348
|
+
const rollout = readTranscriptIfExists(file2.rolloutPath);
|
|
88349
|
+
if (rollout === null) {
|
|
88350
|
+
writer?.abandon();
|
|
88351
|
+
return "vanished";
|
|
87394
88352
|
}
|
|
87395
88353
|
if (index > 0) usage.startPage();
|
|
87396
|
-
const page = parseCodexRollout(text, usage);
|
|
88354
|
+
const page = parseCodexRollout(rollout.text, usage);
|
|
87397
88355
|
if (page === null) continue;
|
|
87398
88356
|
pages.push(page);
|
|
87399
|
-
|
|
88357
|
+
if (thread.mergedTranscriptPath === void 0) {
|
|
88358
|
+
rolloutPrefix = rollout.prefix;
|
|
88359
|
+
} else {
|
|
88360
|
+
const text = mergedPage(rollout.text);
|
|
88361
|
+
merged.bytes += Buffer.byteLength(text);
|
|
88362
|
+
merged.sha256.update(text);
|
|
88363
|
+
writer?.append(text);
|
|
88364
|
+
}
|
|
87400
88365
|
}
|
|
87401
|
-
|
|
88366
|
+
const prefix = thread.mergedTranscriptPath === void 0 ? rolloutPrefix : { bytes: merged.bytes, sha256: merged.sha256.digest("hex"), fileBytes: merged.bytes };
|
|
88367
|
+
if (pages.length === 0 || prefix === void 0) {
|
|
87402
88368
|
writer?.abandon();
|
|
87403
|
-
|
|
88369
|
+
return null;
|
|
88370
|
+
}
|
|
88371
|
+
writer?.finish();
|
|
88372
|
+
return { summary: foldRolloutPages(pages, usage.finish()), prefix };
|
|
88373
|
+
} catch (error63) {
|
|
88374
|
+
writer?.abandon();
|
|
88375
|
+
throw error63;
|
|
88376
|
+
}
|
|
88377
|
+
};
|
|
88378
|
+
const readChildren = (candidate) => {
|
|
88379
|
+
const parent = candidate.thread;
|
|
88380
|
+
const reads = [];
|
|
88381
|
+
const subagents = {
|
|
88382
|
+
files: [],
|
|
88383
|
+
skipped: candidate.leftOutChildren + candidate.unreadableChildren,
|
|
88384
|
+
skippedUnreadable: candidate.unreadableChildren
|
|
88385
|
+
};
|
|
88386
|
+
for (const childId of candidate.children) {
|
|
88387
|
+
const stored = dedupeStoreCopies(byThread.get(childId));
|
|
88388
|
+
let childName = harnessSessionIdOf(stored[0].rolloutPath);
|
|
88389
|
+
try {
|
|
88390
|
+
const child = threadOf(childId, stored);
|
|
88391
|
+
if (child === null) {
|
|
88392
|
+
subagents.skipped += 1;
|
|
88393
|
+
continue;
|
|
88394
|
+
}
|
|
88395
|
+
childName = child.harnessSessionId;
|
|
88396
|
+
if (tooLarge(child) || !RAW_TRANSCRIPT_PART_PATTERN.test(childId)) {
|
|
88397
|
+
subagents.skipped += 1;
|
|
88398
|
+
continue;
|
|
88399
|
+
}
|
|
88400
|
+
const read = readThread(child);
|
|
88401
|
+
if (read === "vanished") return "vanished";
|
|
88402
|
+
if (read === null) {
|
|
88403
|
+
subagents.skipped += 1;
|
|
88404
|
+
subagents.skippedUnreadable += 1;
|
|
88405
|
+
continue;
|
|
88406
|
+
}
|
|
88407
|
+
reads.push(read);
|
|
88408
|
+
subagents.files.push({ part: childId, path: read.transcriptPath, prefix: read.prefix });
|
|
88409
|
+
} catch (error63) {
|
|
88410
|
+
subagents.skipped += 1;
|
|
88411
|
+
subagents.skippedUnreadable += 1;
|
|
88412
|
+
reportOnce(stored, `${parent.harnessSessionId} (subagent ${childName})`, error63);
|
|
87404
88413
|
}
|
|
87405
|
-
|
|
87406
|
-
|
|
87407
|
-
|
|
87408
|
-
|
|
87409
|
-
|
|
87410
|
-
|
|
87411
|
-
|
|
87412
|
-
|
|
88414
|
+
}
|
|
88415
|
+
return { reads, capture: subagents };
|
|
88416
|
+
};
|
|
88417
|
+
for (const candidate of chosen) {
|
|
88418
|
+
await yieldToEventLoop();
|
|
88419
|
+
const { thread, cwd, decision } = candidate;
|
|
88420
|
+
try {
|
|
88421
|
+
const children2 = readChildren(candidate);
|
|
88422
|
+
if (children2 === "vanished") continue;
|
|
88423
|
+
const parent = readThread(thread);
|
|
88424
|
+
if (parent === "vanished") continue;
|
|
88425
|
+
if (parent === null) {
|
|
88426
|
+
countReviewersLeftBehind(thread.threadId);
|
|
88427
|
+
links.forgetRecovery(thread.threadId);
|
|
88428
|
+
continue;
|
|
87413
88429
|
}
|
|
88430
|
+
const mtimeMs = Math.max(parent.mtimeMs, ...children2.reads.map((read) => read.mtimeMs));
|
|
88431
|
+
const summary = parent.summary;
|
|
88432
|
+
const latestChildFirst = [...children2.reads].sort((a, b2) => b2.mtimeMs - a.mtimeMs).map((read) => read.summary);
|
|
88433
|
+
const model = summary.model ?? latestChildFirst.find((child) => child.model !== void 0)?.model;
|
|
88434
|
+
const speedMode = summary.speedMode ?? latestChildFirst.find((child) => child.speedMode !== void 0)?.speedMode;
|
|
88435
|
+
const endedAt = latestChildFirst.reduce((latest2, child) => laterTimestamp(latest2, child.endedAt), summary.endedAt);
|
|
88436
|
+
const usage = sessionUsage([summary, ...latestChildFirst]);
|
|
87414
88437
|
const repoDir = summary.repoName ?? (cwd ? path5.basename(cwd) : void 0);
|
|
87415
88438
|
const repoFullName = decision.repoFullName ?? summary.repoFullName;
|
|
87416
88439
|
const title = summary.sessionId ? threadNames.get(summary.sessionId) : void 0;
|
|
87417
|
-
|
|
87418
|
-
mtimeMs
|
|
88440
|
+
const collected = {
|
|
88441
|
+
mtimeMs,
|
|
87419
88442
|
codexClient: summary.codexClient,
|
|
87420
88443
|
...summary.noninteractive && { codexNoninteractive: true },
|
|
87421
|
-
transcriptPath:
|
|
88444
|
+
transcriptPath: parent.transcriptPath,
|
|
88445
|
+
transcriptPrefix: parent.prefix,
|
|
88446
|
+
subagents: children2.capture,
|
|
87422
88447
|
...cwd && { cwd },
|
|
87423
88448
|
item: {
|
|
87424
88449
|
harness: "codex-local",
|
|
87425
88450
|
harnessSessionId: thread.harnessSessionId,
|
|
87426
|
-
status: now -
|
|
88451
|
+
status: now - mtimeMs < ACTIVE_GRACE_MS ? "active" : "inactive",
|
|
87427
88452
|
archived: thread.archived,
|
|
87428
88453
|
...summary.standaloneReview && { automation: true },
|
|
87429
88454
|
...summary.startedAt && { startedAt: summary.startedAt },
|
|
87430
|
-
...
|
|
87431
|
-
...
|
|
87432
|
-
...
|
|
88455
|
+
...endedAt && { endedAt },
|
|
88456
|
+
...model && { model },
|
|
88457
|
+
...speedMode && { speedMode },
|
|
87433
88458
|
...title && { title: redactText(title).slice(0, 500) },
|
|
87434
88459
|
// input_tokens already includes the cached subset — it IS the
|
|
87435
88460
|
// all-in input-class total. No cache-write figure exists, so
|
|
87436
88461
|
// none is sent (unmeasured, never 0).
|
|
87437
|
-
...
|
|
87438
|
-
...
|
|
87439
|
-
...
|
|
87440
|
-
...
|
|
87441
|
-
...
|
|
87442
|
-
...
|
|
88462
|
+
...usage.totals && { inputTokens: usage.totals.input },
|
|
88463
|
+
...usage.totals && { outputTokens: usage.totals.output },
|
|
88464
|
+
...usage.totals && { cacheReadTokens: usage.totals.cached },
|
|
88465
|
+
...usage.fastTotals && { fastInputTokens: usage.fastTotals.input },
|
|
88466
|
+
...usage.fastTotals && { fastOutputTokens: usage.fastTotals.output },
|
|
88467
|
+
...usage.fastTotals && { fastCacheReadTokens: usage.fastTotals.cached },
|
|
87443
88468
|
// OpenAI prompt-cache writes have no separate price in the current
|
|
87444
88469
|
// session cost model; zero is measured here, not an invented count.
|
|
87445
|
-
...
|
|
88470
|
+
...usage.fastTotals && { fastCacheWriteTokens: 0 },
|
|
87446
88471
|
...repoDir && { repoDir },
|
|
87447
88472
|
...summary.gitBranch && { gitBranch: summary.gitBranch },
|
|
87448
88473
|
...repoFullName && { repoFullName },
|
|
87449
88474
|
capturedVia: decision.via
|
|
87450
88475
|
}
|
|
87451
|
-
}
|
|
88476
|
+
};
|
|
88477
|
+
items.push(collected);
|
|
88478
|
+
links.noteCollected(thread.threadId, collected);
|
|
87452
88479
|
} catch (error63) {
|
|
87453
|
-
|
|
87454
|
-
|
|
87455
|
-
|
|
87456
|
-
path: thread.files[0].rolloutPath,
|
|
87457
|
-
reason: "unreadable",
|
|
87458
|
-
sizeBytes: null,
|
|
87459
|
-
errorCode: diagnosticErrorCode(error63)
|
|
87460
|
-
});
|
|
88480
|
+
countReviewersLeftBehind(thread.threadId);
|
|
88481
|
+
links.forgetRecovery(thread.threadId);
|
|
88482
|
+
reportOnce(thread.files, thread.harnessSessionId, error63);
|
|
87461
88483
|
}
|
|
87462
88484
|
}
|
|
87463
88485
|
return {
|
|
@@ -87466,11 +88488,33 @@ async function collectCodexSessions(options = {}) {
|
|
|
87466
88488
|
skippedSubagentThreads,
|
|
87467
88489
|
skippedTooLarge,
|
|
87468
88490
|
unreadable,
|
|
87469
|
-
heldBack:
|
|
88491
|
+
heldBack: ordered.length - chosen.length,
|
|
87470
88492
|
mergedThreads,
|
|
87471
88493
|
largestMergedBytes
|
|
87472
88494
|
};
|
|
87473
88495
|
}
|
|
88496
|
+
function sessionUsage(threads) {
|
|
88497
|
+
if (threads.some((thread) => thread.model !== void 0 && thread.totals === null)) return { totals: null, fastTotals: null };
|
|
88498
|
+
const measured = threads.filter((thread) => thread.totals !== null);
|
|
88499
|
+
if (measured.length === 0) return { totals: null, fastTotals: null };
|
|
88500
|
+
const add = (a, b2) => ({ input: a.input + b2.input, cached: a.cached + b2.cached, output: a.output + b2.output });
|
|
88501
|
+
const zero = { input: 0, cached: 0, output: 0 };
|
|
88502
|
+
return {
|
|
88503
|
+
totals: measured.map((thread) => thread.totals).reduce(add, zero),
|
|
88504
|
+
fastTotals: measured.every((thread) => thread.fastTotals !== null) ? measured.map((thread) => thread.fastTotals).reduce(add, zero) : null
|
|
88505
|
+
};
|
|
88506
|
+
}
|
|
88507
|
+
function leadRollout(threadFiles) {
|
|
88508
|
+
return threadFiles.find((file2) => !file2.identity.page) ?? threadFiles[0];
|
|
88509
|
+
}
|
|
88510
|
+
function newestChange(threadFiles) {
|
|
88511
|
+
return Math.max(...threadFiles.map((file2) => file2.changedMs));
|
|
88512
|
+
}
|
|
88513
|
+
function threadFilesKey(threadFiles) {
|
|
88514
|
+
return threadFiles.map((file2) => `${file2.rolloutPath}
|
|
88515
|
+
${file2.mtimeMs}
|
|
88516
|
+
${file2.sizeBytes}`).join("\n");
|
|
88517
|
+
}
|
|
87474
88518
|
function dedupeStoreCopies(threadFiles) {
|
|
87475
88519
|
const byName = /* @__PURE__ */ new Map();
|
|
87476
88520
|
for (const file2 of threadFiles) {
|
|
@@ -87500,9 +88544,8 @@ function codexThreadCwd(threadId2, sessionsDir = path5.join(os6.homedir(), ".cod
|
|
|
87500
88544
|
}
|
|
87501
88545
|
|
|
87502
88546
|
// ../../apps/syncservice/src/cursorTranscriptFile.ts
|
|
87503
|
-
import { createHash as
|
|
88547
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
87504
88548
|
import {
|
|
87505
|
-
chmodSync as chmodSync3,
|
|
87506
88549
|
closeSync as closeSync4,
|
|
87507
88550
|
existsSync as existsSync3,
|
|
87508
88551
|
lstatSync as lstatSync2,
|
|
@@ -87510,9 +88553,7 @@ import {
|
|
|
87510
88553
|
openSync as openSync4,
|
|
87511
88554
|
readSync as readSync3,
|
|
87512
88555
|
readdirSync as readdirSync3,
|
|
87513
|
-
realpathSync as realpathSync2
|
|
87514
|
-
utimesSync as utimesSync3,
|
|
87515
|
-
writeFileSync as writeFileSync3
|
|
88556
|
+
realpathSync as realpathSync2
|
|
87516
88557
|
} from "node:fs";
|
|
87517
88558
|
import os8 from "node:os";
|
|
87518
88559
|
import path7 from "node:path";
|
|
@@ -87569,8 +88610,8 @@ function unhandledObservations(shapes, externalId) {
|
|
|
87569
88610
|
}
|
|
87570
88611
|
|
|
87571
88612
|
// ../../apps/syncservice/src/cursorLocal.ts
|
|
87572
|
-
import { createHash as
|
|
87573
|
-
import { chmodSync as chmodSync2, existsSync as existsSync2, mkdirSync as mkdirSync3,
|
|
88613
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
88614
|
+
import { chmodSync as chmodSync2, existsSync as existsSync2, mkdirSync as mkdirSync3, renameSync as renameSync2, rmSync as rmSync3, utimesSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
87574
88615
|
import os7 from "node:os";
|
|
87575
88616
|
import path6 from "node:path";
|
|
87576
88617
|
import { fileURLToPath } from "node:url";
|
|
@@ -87644,9 +88685,20 @@ var cachedMetaSchema = external_exports.object({
|
|
|
87644
88685
|
});
|
|
87645
88686
|
function extractedTranscriptFileName(composerId) {
|
|
87646
88687
|
const sanitized = composerId.replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 80);
|
|
87647
|
-
const hash2 =
|
|
88688
|
+
const hash2 = createHash4("sha256").update(composerId).digest("hex").slice(0, 8);
|
|
87648
88689
|
return `${sanitized}-${hash2}.jsonl`;
|
|
87649
88690
|
}
|
|
88691
|
+
function replaceExtractedTranscript(transcriptPath, body, atime, mtime) {
|
|
88692
|
+
const tmpPath = `${transcriptPath}.${process.pid}.tmp`;
|
|
88693
|
+
try {
|
|
88694
|
+
writeFileSync2(tmpPath, body, { encoding: "utf8", mode: 384 });
|
|
88695
|
+
utimesSync(tmpPath, atime, mtime);
|
|
88696
|
+
renameSync2(tmpPath, transcriptPath);
|
|
88697
|
+
} catch (error63) {
|
|
88698
|
+
rmSync3(tmpPath, { force: true });
|
|
88699
|
+
throw error63;
|
|
88700
|
+
}
|
|
88701
|
+
}
|
|
87650
88702
|
function workspaceFolderOf(cursorUserDir, workspaceId) {
|
|
87651
88703
|
if (workspaceId === "" || workspaceId.includes(path6.sep) || workspaceId.includes("/") || workspaceId.includes("..")) {
|
|
87652
88704
|
return void 0;
|
|
@@ -88143,9 +89195,7 @@ function collectFromStore(db, userDir, options, result, knownIds) {
|
|
|
88143
89195
|
extractedTranscriptsDirReady = true;
|
|
88144
89196
|
}
|
|
88145
89197
|
const body = [JSON.stringify(meta3), ...transcript.serialized, ""].join("\n");
|
|
88146
|
-
|
|
88147
|
-
writeFileSync2(transcriptUploadSourcePath, body, { encoding: "utf8", mode: 384 });
|
|
88148
|
-
utimesSync2(transcriptUploadSourcePath, new Date(lastUpdatedAt), new Date(lastUpdatedAt));
|
|
89198
|
+
replaceExtractedTranscript(transcriptUploadSourcePath, body, new Date(lastUpdatedAt), new Date(lastUpdatedAt));
|
|
88149
89199
|
}
|
|
88150
89200
|
if (hasCaptureGaps(omissions)) result.partiallyInterpreted += 1;
|
|
88151
89201
|
for (const observation of unhandledObservations(unhandled, raw.composerId)) {
|
|
@@ -88358,7 +89408,7 @@ function cursorTranscriptRecordsDir(home) {
|
|
|
88358
89408
|
function cursorTranscriptRecordPath(home, sessionId) {
|
|
88359
89409
|
return path7.join(
|
|
88360
89410
|
cursorTranscriptRecordsDir(home),
|
|
88361
|
-
`${
|
|
89411
|
+
`${createHash5("sha256").update(sessionId).digest("hex")}.json`
|
|
88362
89412
|
);
|
|
88363
89413
|
}
|
|
88364
89414
|
function collectCursorTranscriptFiles(options, isDatabaseSession, result, databaseExtractedTranscriptsDir, extractedTranscriptFileName2) {
|
|
@@ -88381,7 +89431,7 @@ function collectCursorTranscriptFiles(options, isDatabaseSession, result, databa
|
|
|
88381
89431
|
if (!lstatSync2(recordPath).isFile()) continue;
|
|
88382
89432
|
const text = readBounded(recordPath, 40 * 1024);
|
|
88383
89433
|
const record2 = transcriptDiscoveryRecordSchema2.parse(text === null ? null : JSON.parse(text));
|
|
88384
|
-
if (file2 !== `${
|
|
89434
|
+
if (file2 !== `${createHash5("sha256").update(record2.sessionId).digest("hex")}.json`) continue;
|
|
88385
89435
|
if (isDatabaseSession(record2.sessionId) || existsSync3(path7.join(databaseExtractedTranscriptsDir, extractedTranscriptFileName2(record2.sessionId))))
|
|
88386
89436
|
continue;
|
|
88387
89437
|
if (record2.transcriptPath === null || record2.workspaceRoots.length !== 1) continue;
|
|
@@ -88475,9 +89525,7 @@ function collectCursorTranscriptFiles(options, isDatabaseSession, result, databa
|
|
|
88475
89525
|
continue;
|
|
88476
89526
|
}
|
|
88477
89527
|
mkdirSync4(nativeExtractedTranscriptsDir, { recursive: true, mode: 448 });
|
|
88478
|
-
|
|
88479
|
-
writeFileSync3(transcriptUploadSourcePath, body, { mode: 384 });
|
|
88480
|
-
utimesSync3(transcriptUploadSourcePath, stat.atime, stat.mtime);
|
|
89528
|
+
replaceExtractedTranscript(transcriptUploadSourcePath, body, stat.atime, stat.mtime);
|
|
88481
89529
|
omissions = Object.keys(written).length > 0 ? written : void 0;
|
|
88482
89530
|
unhandled = prepared.unhandled;
|
|
88483
89531
|
}
|
|
@@ -88504,7 +89552,7 @@ function collectCursorTranscriptFiles(options, isDatabaseSession, result, databa
|
|
|
88504
89552
|
}
|
|
88505
89553
|
|
|
88506
89554
|
// ../../apps/syncservice/src/hookInstall.ts
|
|
88507
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as
|
|
89555
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync6, rmSync as rmSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
88508
89556
|
import os9 from "node:os";
|
|
88509
89557
|
import path8 from "node:path";
|
|
88510
89558
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
@@ -88597,7 +89645,7 @@ function installCollectorHooks(homeDir = os9.homedir()) {
|
|
|
88597
89645
|
const hookDir = path8.join(homeDir, ".config", "powerplant", "hooks");
|
|
88598
89646
|
mkdirSync5(hookDir, { recursive: true });
|
|
88599
89647
|
for (const script of HOOK_SCRIPTS) {
|
|
88600
|
-
|
|
89648
|
+
writeFileSync3(path8.join(hookDir, script), readFileSync6(hookSourcePath(script), "utf8"), {
|
|
88601
89649
|
mode: 493
|
|
88602
89650
|
});
|
|
88603
89651
|
}
|
|
@@ -88605,18 +89653,18 @@ function installCollectorHooks(homeDir = os9.homedir()) {
|
|
|
88605
89653
|
const settingsPath = claudeSettingsPath(homeDir);
|
|
88606
89654
|
let settingsText;
|
|
88607
89655
|
try {
|
|
88608
|
-
settingsText =
|
|
89656
|
+
settingsText = readFileSync6(settingsPath, "utf8");
|
|
88609
89657
|
} catch {
|
|
88610
89658
|
settingsText = null;
|
|
88611
89659
|
}
|
|
88612
89660
|
const merged = mergeCollectorHooks(settingsText, (script) => `node "${path8.join(hookDir, script)}"`);
|
|
88613
89661
|
if (merged.action === "write") {
|
|
88614
89662
|
mkdirSync5(settingsDir, { recursive: true });
|
|
88615
|
-
|
|
89663
|
+
writeFileSync3(settingsPath, merged.text);
|
|
88616
89664
|
}
|
|
88617
89665
|
if (merged.action !== "skip") {
|
|
88618
89666
|
for (const retired of RETIRED_HOOK_WIRINGS) {
|
|
88619
|
-
|
|
89667
|
+
rmSync4(path8.join(hookDir, retired.script), { force: true });
|
|
88620
89668
|
}
|
|
88621
89669
|
}
|
|
88622
89670
|
return {
|
|
@@ -88627,7 +89675,7 @@ function installCollectorHooks(homeDir = os9.homedir()) {
|
|
|
88627
89675
|
}
|
|
88628
89676
|
|
|
88629
89677
|
// ../../apps/syncservice/src/lifecycleJournal.ts
|
|
88630
|
-
import { closeSync as closeSync5, fstatSync, openSync as openSync5, readdirSync as readdirSync4, readSync as readSync4, rmSync as
|
|
89678
|
+
import { closeSync as closeSync5, fstatSync as fstatSync2, openSync as openSync5, readdirSync as readdirSync4, readSync as readSync4, rmSync as rmSync5 } from "node:fs";
|
|
88631
89679
|
import path9 from "node:path";
|
|
88632
89680
|
var LIFECYCLE_JOURNAL_DIRNAME = "lifecycle-journal";
|
|
88633
89681
|
var LIFECYCLE_JOURNAL_RETENTION_DAYS = 7;
|
|
@@ -88694,7 +89742,7 @@ function readAppendedLines(file2, offset) {
|
|
|
88694
89742
|
throw error63;
|
|
88695
89743
|
}
|
|
88696
89744
|
try {
|
|
88697
|
-
const size =
|
|
89745
|
+
const size = fstatSync2(fd).size;
|
|
88698
89746
|
const start = size < offset ? 0 : offset;
|
|
88699
89747
|
const buffer = Buffer.alloc(size - start);
|
|
88700
89748
|
const bytes = readSync4(fd, buffer, 0, buffer.length, start);
|
|
@@ -88752,10 +89800,10 @@ function createLifecycleJournalReader(home, harnesses = COLLECTOR_SESSION_HARNES
|
|
|
88752
89800
|
deleteExpired(nowMs = Date.now()) {
|
|
88753
89801
|
const floorMs = lifecycleJournalRetentionFloorMs(nowMs);
|
|
88754
89802
|
for (const file2 of allDayFiles()) {
|
|
88755
|
-
if (Date.parse(`${path9.basename(file2).slice(0, 10)}T00:00:00Z`) < floorMs)
|
|
89803
|
+
if (Date.parse(`${path9.basename(file2).slice(0, 10)}T00:00:00Z`) < floorMs) rmSync5(file2, { force: true });
|
|
88756
89804
|
}
|
|
88757
89805
|
if (legacyFile !== null && (statIfExists(legacyFile)?.mtimeMs ?? floorMs) < floorMs) {
|
|
88758
|
-
|
|
89806
|
+
rmSync5(legacyFile, { force: true });
|
|
88759
89807
|
}
|
|
88760
89808
|
for (const held of [latest2, latestBoundary]) {
|
|
88761
89809
|
for (const [key, entry] of held) {
|
|
@@ -88779,10 +89827,10 @@ import {
|
|
|
88779
89827
|
existsSync as existsSync5,
|
|
88780
89828
|
lstatSync as lstatSync3,
|
|
88781
89829
|
mkdirSync as mkdirSync6,
|
|
88782
|
-
readFileSync as
|
|
88783
|
-
renameSync as
|
|
88784
|
-
rmSync as
|
|
88785
|
-
writeFileSync as
|
|
89830
|
+
readFileSync as readFileSync7,
|
|
89831
|
+
renameSync as renameSync3,
|
|
89832
|
+
rmSync as rmSync6,
|
|
89833
|
+
writeFileSync as writeFileSync4
|
|
88786
89834
|
} from "node:fs";
|
|
88787
89835
|
import path10 from "node:path";
|
|
88788
89836
|
import { randomUUID } from "node:crypto";
|
|
@@ -88887,7 +89935,7 @@ function installHarnessHook(harness, home) {
|
|
|
88887
89935
|
return { provider: nativeName, settings: "skip", detail: NON_REGULAR_CONFIG };
|
|
88888
89936
|
}
|
|
88889
89937
|
mode = stat.mode & 511;
|
|
88890
|
-
original =
|
|
89938
|
+
original = readFileSync7(configPath, "utf8");
|
|
88891
89939
|
} catch (error63) {
|
|
88892
89940
|
if (!(error63 instanceof Error && "code" in error63 && error63.code === "ENOENT")) throw error63;
|
|
88893
89941
|
}
|
|
@@ -88903,9 +89951,9 @@ function installHarnessHook(harness, home) {
|
|
|
88903
89951
|
return { provider: nativeName, settings: "skip", detail: merged.reason };
|
|
88904
89952
|
}
|
|
88905
89953
|
mkdirSync6(hookDir, { recursive: true });
|
|
88906
|
-
writeAtomic(scriptPath,
|
|
89954
|
+
writeAtomic(scriptPath, readFileSync7(hookSourcePath(script), "utf8"), 448);
|
|
88907
89955
|
if (merged.action === "write") {
|
|
88908
|
-
const current = existsSync5(configPath) ?
|
|
89956
|
+
const current = existsSync5(configPath) ? readFileSync7(configPath, "utf8") : null;
|
|
88909
89957
|
if (current !== original)
|
|
88910
89958
|
return {
|
|
88911
89959
|
provider: nativeName,
|
|
@@ -88919,10 +89967,10 @@ function installHarnessHook(harness, home) {
|
|
|
88919
89967
|
function writeAtomic(file2, text, mode) {
|
|
88920
89968
|
const temporary = `${file2}.${randomUUID()}.tmp`;
|
|
88921
89969
|
try {
|
|
88922
|
-
|
|
88923
|
-
|
|
89970
|
+
writeFileSync4(temporary, text, { mode, flag: "wx" });
|
|
89971
|
+
renameSync3(temporary, file2);
|
|
88924
89972
|
} finally {
|
|
88925
|
-
|
|
89973
|
+
rmSync6(temporary, { force: true });
|
|
88926
89974
|
}
|
|
88927
89975
|
}
|
|
88928
89976
|
function installHarnessHooks(home) {
|
|
@@ -89287,8 +90335,8 @@ function controlFilePath(appEnv, homeDir) {
|
|
|
89287
90335
|
function writeAtomically(file2, payload) {
|
|
89288
90336
|
mkdirSync7(path12.dirname(file2), { recursive: true });
|
|
89289
90337
|
const tmp = `${file2}.${process.pid}.tmp`;
|
|
89290
|
-
|
|
89291
|
-
|
|
90338
|
+
writeFileSync5(tmp, payload);
|
|
90339
|
+
renameSync4(tmp, file2);
|
|
89292
90340
|
}
|
|
89293
90341
|
function writeCollectorStatus(appEnv, status, homeDir) {
|
|
89294
90342
|
writeAtomically(statusFilePath(appEnv, homeDir), JSON.stringify(status, null, 2));
|
|
@@ -89339,7 +90387,7 @@ function menuOpenRequestPath(appEnv, homeDir) {
|
|
|
89339
90387
|
function writeRefreshRequest(menuDir) {
|
|
89340
90388
|
const dir = path12.join(menuDir, MENU_REFRESH_DIRNAME);
|
|
89341
90389
|
if (!existsSync7(dir)) return;
|
|
89342
|
-
|
|
90390
|
+
writeFileSync5(path12.join(dir, "request"), (/* @__PURE__ */ new Date()).toISOString(), { mode: 384 });
|
|
89343
90391
|
}
|
|
89344
90392
|
function menuShownStatus(status) {
|
|
89345
90393
|
const passFailed = status.lastPass?.ok === false;
|
|
@@ -89408,7 +90456,7 @@ function carriedUploadClock(previous, machineId) {
|
|
|
89408
90456
|
function readCollectorStatus(appEnv, homeDir) {
|
|
89409
90457
|
let raw;
|
|
89410
90458
|
try {
|
|
89411
|
-
raw =
|
|
90459
|
+
raw = readFileSync8(statusFilePath(appEnv, homeDir), "utf8");
|
|
89412
90460
|
} catch {
|
|
89413
90461
|
return { file: "missing" };
|
|
89414
90462
|
}
|
|
@@ -89424,7 +90472,7 @@ function readCollectorStatus(appEnv, homeDir) {
|
|
|
89424
90472
|
function readCollectorControl(appEnv, homeDir) {
|
|
89425
90473
|
let raw;
|
|
89426
90474
|
try {
|
|
89427
|
-
raw =
|
|
90475
|
+
raw = readFileSync8(controlFilePath(appEnv, homeDir), "utf8");
|
|
89428
90476
|
} catch {
|
|
89429
90477
|
return { paused: false };
|
|
89430
90478
|
}
|
|
@@ -89441,10 +90489,10 @@ function writeCollectorControl(appEnv, control, homeDir) {
|
|
|
89441
90489
|
writeAtomically(controlFilePath(appEnv, homeDir), JSON.stringify(control, null, 2));
|
|
89442
90490
|
}
|
|
89443
90491
|
function clearCollectorControl(appEnv, homeDir) {
|
|
89444
|
-
|
|
90492
|
+
rmSync7(controlFilePath(appEnv, homeDir), { force: true });
|
|
89445
90493
|
}
|
|
89446
90494
|
function clearCollectorStatus(appEnv, homeDir) {
|
|
89447
|
-
|
|
90495
|
+
rmSync7(statusFilePath(appEnv, homeDir), { force: true });
|
|
89448
90496
|
}
|
|
89449
90497
|
function describeDaemonState(read, probes = {}) {
|
|
89450
90498
|
if (read.file === "invalid") return { state: "unreadable" };
|
|
@@ -89457,6 +90505,9 @@ function describeDaemonState(read, probes = {}) {
|
|
|
89457
90505
|
}
|
|
89458
90506
|
return { state: "running", status: read.status };
|
|
89459
90507
|
}
|
|
90508
|
+
function backgroundSyncIsRunning(appEnv, homeDir) {
|
|
90509
|
+
return describeDaemonState(readCollectorStatus(appEnv, homeDir)).state === "running";
|
|
90510
|
+
}
|
|
89460
90511
|
function isPidAlive(pid) {
|
|
89461
90512
|
try {
|
|
89462
90513
|
process.kill(pid, 0);
|
|
@@ -89584,12 +90635,12 @@ var MacosCredentialStore = class {
|
|
|
89584
90635
|
try {
|
|
89585
90636
|
const fileFd = openSync6(temporary, "wx", 384);
|
|
89586
90637
|
try {
|
|
89587
|
-
|
|
90638
|
+
writeFileSync6(fileFd, envelope);
|
|
89588
90639
|
fsyncSync(fileFd);
|
|
89589
90640
|
} finally {
|
|
89590
90641
|
closeSync6(fileFd);
|
|
89591
90642
|
}
|
|
89592
|
-
|
|
90643
|
+
renameSync5(temporary, this.file);
|
|
89593
90644
|
const directoryFd = openSync6(this.directory, "r");
|
|
89594
90645
|
try {
|
|
89595
90646
|
fsyncSync(directoryFd);
|
|
@@ -89809,8 +90860,8 @@ function saveCredentials(appEnv, credentials) {
|
|
|
89809
90860
|
}
|
|
89810
90861
|
const file2 = fallbackFile(appEnv);
|
|
89811
90862
|
mkdirSync9(path14.dirname(file2), { recursive: true });
|
|
89812
|
-
|
|
89813
|
-
|
|
90863
|
+
writeFileSync7(file2, payload, { mode: 384 });
|
|
90864
|
+
chmodSync3(file2, 384);
|
|
89814
90865
|
return file2;
|
|
89815
90866
|
}
|
|
89816
90867
|
function loadCredentials(appEnv) {
|
|
@@ -89820,7 +90871,7 @@ function loadCredentials(appEnv) {
|
|
|
89820
90871
|
}
|
|
89821
90872
|
let raw;
|
|
89822
90873
|
try {
|
|
89823
|
-
raw =
|
|
90874
|
+
raw = readFileSync9(fallbackFile(appEnv), "utf8");
|
|
89824
90875
|
} catch (cause) {
|
|
89825
90876
|
if (isCredentialStoreAbsent(cause)) return null;
|
|
89826
90877
|
throw new Error(
|
|
@@ -89855,13 +90906,407 @@ function fallbackFile(appEnv) {
|
|
|
89855
90906
|
|
|
89856
90907
|
// ../../apps/syncservice/src/enroll.ts
|
|
89857
90908
|
import { spawn } from "node:child_process";
|
|
89858
|
-
import { createHash as
|
|
90909
|
+
import { createHash as createHash6, randomBytes as randomBytes2 } from "node:crypto";
|
|
89859
90910
|
import http5 from "node:http";
|
|
89860
|
-
import
|
|
90911
|
+
import os14 from "node:os";
|
|
89861
90912
|
|
|
89862
90913
|
// ../../apps/syncservice/src/upload.ts
|
|
89863
|
-
import { readFileSync as
|
|
90914
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
89864
90915
|
import { gzipSync } from "node:zlib";
|
|
90916
|
+
|
|
90917
|
+
// ../../apps/syncservice/src/claudeCode.ts
|
|
90918
|
+
import { readdirSync as readdirSync6 } from "node:fs";
|
|
90919
|
+
import os13 from "node:os";
|
|
90920
|
+
import path15 from "node:path";
|
|
90921
|
+
import { setImmediate as yieldToEventLoop2 } from "node:timers/promises";
|
|
90922
|
+
function parseClaudeCodeTranscript(text) {
|
|
90923
|
+
const usageByMessage = /* @__PURE__ */ new Map();
|
|
90924
|
+
const modelCounts = /* @__PURE__ */ new Map();
|
|
90925
|
+
const timestamps = [];
|
|
90926
|
+
let title;
|
|
90927
|
+
let cwd;
|
|
90928
|
+
let gitBranch;
|
|
90929
|
+
let entrypoint;
|
|
90930
|
+
let anonCounter = 0;
|
|
90931
|
+
let speedMode;
|
|
90932
|
+
for (const record2 of parseJsonl(text)) {
|
|
90933
|
+
if (record2.type === "custom-title" && typeof record2.customTitle === "string") {
|
|
90934
|
+
title = record2.customTitle;
|
|
90935
|
+
continue;
|
|
90936
|
+
}
|
|
90937
|
+
if (record2.type === "ai-title" && typeof record2.aiTitle === "string") {
|
|
90938
|
+
title = record2.aiTitle;
|
|
90939
|
+
continue;
|
|
90940
|
+
}
|
|
90941
|
+
if (cwd === void 0 && typeof record2.cwd === "string" && record2.cwd !== "") cwd = record2.cwd;
|
|
90942
|
+
if (typeof record2.entrypoint === "string" && record2.entrypoint !== "") {
|
|
90943
|
+
entrypoint ??= record2.entrypoint;
|
|
90944
|
+
}
|
|
90945
|
+
if (typeof record2.gitBranch === "string" && record2.gitBranch !== "") {
|
|
90946
|
+
gitBranch ??= record2.gitBranch;
|
|
90947
|
+
}
|
|
90948
|
+
if (typeof record2.timestamp === "string") timestamps.push(record2.timestamp);
|
|
90949
|
+
if (record2.type !== "assistant") continue;
|
|
90950
|
+
const message2 = record2.message;
|
|
90951
|
+
if (!message2 || message2.model === "<synthetic>") continue;
|
|
90952
|
+
const id = message2.id ?? (typeof record2.requestId === "string" ? record2.requestId : void 0) ?? (typeof record2.uuid === "string" ? record2.uuid : void 0) ?? `anon-${anonCounter++}`;
|
|
90953
|
+
const previousMessage = usageByMessage.get(id);
|
|
90954
|
+
const isNewMessage = previousMessage === void 0;
|
|
90955
|
+
const speedValue = message2.usage?.speed;
|
|
90956
|
+
const observedSpeed = speedModeOf(speedValue);
|
|
90957
|
+
const messageSpeed = observedSpeed ?? previousMessage?.speedMode;
|
|
90958
|
+
if (speedValue !== void 0) speedMode = observedSpeed ?? "unknown";
|
|
90959
|
+
if (message2.usage) {
|
|
90960
|
+
usageByMessage.set(id, {
|
|
90961
|
+
usage: toUsage(message2.usage),
|
|
90962
|
+
...messageSpeed && { speedMode: messageSpeed }
|
|
90963
|
+
});
|
|
90964
|
+
} else if (isNewMessage) {
|
|
90965
|
+
usageByMessage.set(id, {
|
|
90966
|
+
usage: { input: 0, cacheRead: 0, cacheCreation: 0, output: 0 }
|
|
90967
|
+
});
|
|
90968
|
+
}
|
|
90969
|
+
if (isNewMessage && typeof message2.model === "string") {
|
|
90970
|
+
modelCounts.set(message2.model, (modelCounts.get(message2.model) ?? 0) + 1);
|
|
90971
|
+
}
|
|
90972
|
+
}
|
|
90973
|
+
if (usageByMessage.size === 0 && timestamps.length === 0) return null;
|
|
90974
|
+
const summed = [...usageByMessage.values()].map((entry) => entry.usage).reduce(addUsage, {
|
|
90975
|
+
input: 0,
|
|
90976
|
+
cacheRead: 0,
|
|
90977
|
+
cacheCreation: 0,
|
|
90978
|
+
output: 0
|
|
90979
|
+
});
|
|
90980
|
+
const speedFullyMeasured = usageByMessage.size > 0 && [...usageByMessage.values()].every((entry) => entry.speedMode !== void 0);
|
|
90981
|
+
const fastUsage = speedFullyMeasured ? [...usageByMessage.values()].filter((entry) => entry.speedMode === "fast").map((entry) => entry.usage).reduce(addUsage, { input: 0, cacheRead: 0, cacheCreation: 0, output: 0 }) : null;
|
|
90982
|
+
return {
|
|
90983
|
+
startedAt: timestamps[0],
|
|
90984
|
+
endedAt: timestamps[timestamps.length - 1],
|
|
90985
|
+
model: dominantKey(modelCounts),
|
|
90986
|
+
usage: usageByMessage.size > 0 ? summed : null,
|
|
90987
|
+
fastUsage,
|
|
90988
|
+
...speedMode && { speedMode },
|
|
90989
|
+
title,
|
|
90990
|
+
turnCount: usageByMessage.size,
|
|
90991
|
+
cwd,
|
|
90992
|
+
gitBranch,
|
|
90993
|
+
entrypoint
|
|
90994
|
+
};
|
|
90995
|
+
}
|
|
90996
|
+
var AGENT_SDK_ENTRYPOINTS = /* @__PURE__ */ new Set(["sdk-py", "sdk-ts"]);
|
|
90997
|
+
function isAutomationEntrypoint(entrypoint) {
|
|
90998
|
+
return entrypoint !== void 0 && AGENT_SDK_ENTRYPOINTS.has(entrypoint);
|
|
90999
|
+
}
|
|
91000
|
+
function sessionKey(identity) {
|
|
91001
|
+
return JSON.stringify([identity.harness, identity.harnessSessionId]);
|
|
91002
|
+
}
|
|
91003
|
+
async function collectClaudeCodeSessions(options = {}) {
|
|
91004
|
+
const claudeDir = options.claudeDir ?? path15.join(os13.homedir(), ".claude");
|
|
91005
|
+
const now = options.now ?? Date.now();
|
|
91006
|
+
const capture2 = options.capture ?? captureEverythingUnresolved;
|
|
91007
|
+
const maxBytes = options.maxTranscriptBytes ?? MAX_TRANSCRIPT_FILE_BYTES;
|
|
91008
|
+
const projectsDir = path15.join(claudeDir, "projects");
|
|
91009
|
+
const items = [];
|
|
91010
|
+
const skips = { skippedOutOfScope: 0, skippedNoRepo: 0 };
|
|
91011
|
+
const candidates = [];
|
|
91012
|
+
const unreadable = [];
|
|
91013
|
+
let skippedTooLarge = 0;
|
|
91014
|
+
const home = path15.dirname(claudeDir);
|
|
91015
|
+
const lifecycle = options.lifecycle ?? readLifecycleJournal(home, now, ["claude-local"]);
|
|
91016
|
+
for (const projectDir of listDirs(projectsDir)) {
|
|
91017
|
+
for (const entry of listFiles(projectDir, ".jsonl")) {
|
|
91018
|
+
await yieldToEventLoop2();
|
|
91019
|
+
const transcriptPath = path15.join(projectDir, entry);
|
|
91020
|
+
const stat = statIfExists(transcriptPath);
|
|
91021
|
+
if (!stat) continue;
|
|
91022
|
+
const sessionId = path15.basename(entry, ".jsonl");
|
|
91023
|
+
const subagents = options.locationsOnly ? NO_SUBAGENTS : listSubagents(path15.join(projectDir, sessionId, "subagents"));
|
|
91024
|
+
const mtimeMs = Math.max(stat.mtimeMs, subagents.workflows.mtimeMs, ...subagents.files.map((file2) => file2.mtimeMs ?? 0));
|
|
91025
|
+
if (options.sinceMs !== void 0 && mtimeMs < options.sinceMs) continue;
|
|
91026
|
+
if (stat.size > maxBytes) {
|
|
91027
|
+
skippedTooLarge += 1;
|
|
91028
|
+
observeFileDiagnostic(options.onFileDiagnostic, {
|
|
91029
|
+
path: transcriptPath,
|
|
91030
|
+
reason: "too_large",
|
|
91031
|
+
sizeBytes: stat.size
|
|
91032
|
+
});
|
|
91033
|
+
continue;
|
|
91034
|
+
}
|
|
91035
|
+
const endObservedAt = observedSessionEnd(lifecycle.latestBoundary("claude-local", sessionId), mtimeMs);
|
|
91036
|
+
const status = endObservedAt === null && now - mtimeMs < ACTIVE_GRACE_MS ? "active" : "inactive";
|
|
91037
|
+
try {
|
|
91038
|
+
const cwd = transcriptCwd(transcriptPath);
|
|
91039
|
+
const decision = captureDecision(cwd, capture2);
|
|
91040
|
+
if (!decision.capture) {
|
|
91041
|
+
countCaptureSkip(skips, decision.reason);
|
|
91042
|
+
continue;
|
|
91043
|
+
}
|
|
91044
|
+
if (options.locationsOnly) {
|
|
91045
|
+
items.push({
|
|
91046
|
+
mtimeMs: stat.mtimeMs,
|
|
91047
|
+
transcriptPath,
|
|
91048
|
+
...cwd && { cwd },
|
|
91049
|
+
item: { harness: "claude-local", harnessSessionId: sessionId, status, capturedVia: decision.via }
|
|
91050
|
+
});
|
|
91051
|
+
continue;
|
|
91052
|
+
}
|
|
91053
|
+
candidates.push({
|
|
91054
|
+
transcriptPath,
|
|
91055
|
+
sessionId,
|
|
91056
|
+
mtimeMs,
|
|
91057
|
+
subagents,
|
|
91058
|
+
status,
|
|
91059
|
+
endObservedAt,
|
|
91060
|
+
...cwd && { cwd },
|
|
91061
|
+
decision
|
|
91062
|
+
});
|
|
91063
|
+
} catch (error63) {
|
|
91064
|
+
unreadable.push(`${sessionId}: ${describeReadError(error63)}`);
|
|
91065
|
+
observeFileDiagnostic(options.onFileDiagnostic, {
|
|
91066
|
+
path: transcriptPath,
|
|
91067
|
+
reason: "unreadable",
|
|
91068
|
+
sizeBytes: null,
|
|
91069
|
+
errorCode: diagnosticErrorCode(error63)
|
|
91070
|
+
});
|
|
91071
|
+
}
|
|
91072
|
+
}
|
|
91073
|
+
}
|
|
91074
|
+
if (options.locationsOnly) {
|
|
91075
|
+
return {
|
|
91076
|
+
sessions: items.sort((a, b2) => b2.mtimeMs - a.mtimeMs),
|
|
91077
|
+
...skips,
|
|
91078
|
+
skippedTooLarge,
|
|
91079
|
+
unreadable,
|
|
91080
|
+
heldBack: 0
|
|
91081
|
+
};
|
|
91082
|
+
}
|
|
91083
|
+
candidates.sort((a, b2) => b2.mtimeMs - a.mtimeMs);
|
|
91084
|
+
const chosen = options.maxSessions === void 0 ? candidates : candidates.slice(0, options.maxSessions);
|
|
91085
|
+
for (const candidate of chosen) {
|
|
91086
|
+
await yieldToEventLoop2();
|
|
91087
|
+
const { transcriptPath, sessionId, mtimeMs, subagents, status, endObservedAt, cwd, decision } = candidate;
|
|
91088
|
+
try {
|
|
91089
|
+
const transcript = readTranscriptIfExists(transcriptPath);
|
|
91090
|
+
if (transcript === null) continue;
|
|
91091
|
+
const summary = parseClaudeCodeTranscript(transcript.text);
|
|
91092
|
+
if (!summary) continue;
|
|
91093
|
+
const usage = addSubagentUsage(
|
|
91094
|
+
summary.usage,
|
|
91095
|
+
summary.fastUsage,
|
|
91096
|
+
subagents.files,
|
|
91097
|
+
{ maxBytes, sessionId, onFileDiagnostic: options.onFileDiagnostic }
|
|
91098
|
+
);
|
|
91099
|
+
skippedTooLarge += usage.skippedTooLarge;
|
|
91100
|
+
unreadable.push(...usage.unreadable);
|
|
91101
|
+
const endedAt = endObservedAt ?? laterTimestamp(summary.endedAt, usage.endedAt);
|
|
91102
|
+
items.push({
|
|
91103
|
+
mtimeMs,
|
|
91104
|
+
transcriptPath,
|
|
91105
|
+
transcriptPrefix: transcript.prefix,
|
|
91106
|
+
subagents: {
|
|
91107
|
+
files: usage.capture.files,
|
|
91108
|
+
skipped: usage.capture.skipped + subagents.workflows.count,
|
|
91109
|
+
skippedUnreadable: usage.capture.skippedUnreadable + subagents.workflows.unreadable
|
|
91110
|
+
},
|
|
91111
|
+
...cwd && { cwd },
|
|
91112
|
+
item: {
|
|
91113
|
+
harness: "claude-local",
|
|
91114
|
+
harnessSessionId: sessionId,
|
|
91115
|
+
status,
|
|
91116
|
+
...summary.startedAt && { startedAt: summary.startedAt },
|
|
91117
|
+
...endedAt && { endedAt },
|
|
91118
|
+
...summary.model && { model: summary.model },
|
|
91119
|
+
...summary.speedMode && { speedMode: summary.speedMode },
|
|
91120
|
+
// Total input-class tokens the source reported (uncached + cache
|
|
91121
|
+
// reads + cache writes) — a real reported total, not an estimate.
|
|
91122
|
+
// The cache breakdown rides along: pricing bills reads at 0.1×
|
|
91123
|
+
// and writes at 1.25×, so collapsing it would inflate estimates.
|
|
91124
|
+
...usage.total && {
|
|
91125
|
+
inputTokens: usage.total.input + usage.total.cacheRead + usage.total.cacheCreation
|
|
91126
|
+
},
|
|
91127
|
+
...usage.total && { outputTokens: usage.total.output },
|
|
91128
|
+
...usage.total && { cacheReadTokens: usage.total.cacheRead },
|
|
91129
|
+
...usage.total && { cacheWriteTokens: usage.total.cacheCreation },
|
|
91130
|
+
...usage.fast && {
|
|
91131
|
+
fastInputTokens: usage.fast.input + usage.fast.cacheRead + usage.fast.cacheCreation,
|
|
91132
|
+
fastOutputTokens: usage.fast.output,
|
|
91133
|
+
fastCacheReadTokens: usage.fast.cacheRead,
|
|
91134
|
+
fastCacheWriteTokens: usage.fast.cacheCreation
|
|
91135
|
+
},
|
|
91136
|
+
...summary.title && { title: redactText(summary.title).slice(0, 500) },
|
|
91137
|
+
...isAutomationEntrypoint(summary.entrypoint) && { automation: true },
|
|
91138
|
+
// The PR-join coordinates: the checkout's directory name (bare
|
|
91139
|
+
// repo, not the local path) and the branch the work started on.
|
|
91140
|
+
...cwd && { repoDir: path15.basename(cwd) },
|
|
91141
|
+
...summary.gitBranch && { gitBranch: summary.gitBranch },
|
|
91142
|
+
...decision.repoFullName && { repoFullName: decision.repoFullName },
|
|
91143
|
+
capturedVia: decision.via
|
|
91144
|
+
}
|
|
91145
|
+
});
|
|
91146
|
+
} catch (error63) {
|
|
91147
|
+
unreadable.push(`${sessionId}: ${describeReadError(error63)}`);
|
|
91148
|
+
observeFileDiagnostic(options.onFileDiagnostic, {
|
|
91149
|
+
path: transcriptPath,
|
|
91150
|
+
reason: "unreadable",
|
|
91151
|
+
sizeBytes: null,
|
|
91152
|
+
errorCode: diagnosticErrorCode(error63)
|
|
91153
|
+
});
|
|
91154
|
+
}
|
|
91155
|
+
}
|
|
91156
|
+
return {
|
|
91157
|
+
sessions: items.sort((a, b2) => b2.mtimeMs - a.mtimeMs),
|
|
91158
|
+
...skips,
|
|
91159
|
+
skippedTooLarge,
|
|
91160
|
+
unreadable,
|
|
91161
|
+
heldBack: candidates.length - chosen.length
|
|
91162
|
+
};
|
|
91163
|
+
}
|
|
91164
|
+
function transcriptCwd(transcriptPath) {
|
|
91165
|
+
const record2 = readFirstRecord(
|
|
91166
|
+
transcriptPath,
|
|
91167
|
+
(candidate) => typeof candidate.cwd === "string" && candidate.cwd !== ""
|
|
91168
|
+
);
|
|
91169
|
+
return typeof record2?.cwd === "string" ? record2.cwd : void 0;
|
|
91170
|
+
}
|
|
91171
|
+
function toUsage(u) {
|
|
91172
|
+
const n = (value) => typeof value === "number" ? value : 0;
|
|
91173
|
+
return {
|
|
91174
|
+
input: n(u.input_tokens),
|
|
91175
|
+
cacheRead: n(u.cache_read_input_tokens),
|
|
91176
|
+
cacheCreation: n(u.cache_creation_input_tokens),
|
|
91177
|
+
output: n(u.output_tokens)
|
|
91178
|
+
};
|
|
91179
|
+
}
|
|
91180
|
+
function speedModeOf(value) {
|
|
91181
|
+
return value === "fast" || value === "standard" ? value : void 0;
|
|
91182
|
+
}
|
|
91183
|
+
function addUsage(a, b2) {
|
|
91184
|
+
return {
|
|
91185
|
+
input: a.input + b2.input,
|
|
91186
|
+
cacheRead: a.cacheRead + b2.cacheRead,
|
|
91187
|
+
cacheCreation: a.cacheCreation + b2.cacheCreation,
|
|
91188
|
+
output: a.output + b2.output
|
|
91189
|
+
};
|
|
91190
|
+
}
|
|
91191
|
+
var NO_SUBAGENTS = { files: [], workflows: { count: 0, unreadable: 0, mtimeMs: 0 } };
|
|
91192
|
+
function listSubagents(subagentsDir) {
|
|
91193
|
+
const entries = directoryEntries(subagentsDir);
|
|
91194
|
+
if (!Array.isArray(entries)) {
|
|
91195
|
+
return { ...NO_SUBAGENTS, files: [{ entry: "subagents/", path: subagentsDir, mtimeMs: null, size: null, statError: entries.error }] };
|
|
91196
|
+
}
|
|
91197
|
+
const names = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => entry.name).sort();
|
|
91198
|
+
const files = names.flatMap((entry) => {
|
|
91199
|
+
const filePath = path15.join(subagentsDir, entry);
|
|
91200
|
+
try {
|
|
91201
|
+
const stat = statIfExists(filePath);
|
|
91202
|
+
return stat === null ? [] : [{ entry, path: filePath, mtimeMs: stat.mtimeMs, size: stat.size, statError: null }];
|
|
91203
|
+
} catch (statError) {
|
|
91204
|
+
return [{ entry, path: filePath, mtimeMs: null, size: null, statError }];
|
|
91205
|
+
}
|
|
91206
|
+
});
|
|
91207
|
+
const hasWorkflows = entries.some((entry) => entry.isDirectory() && entry.name === "workflows");
|
|
91208
|
+
return { files, workflows: hasWorkflows ? listWorkflowTranscripts(path15.join(subagentsDir, "workflows")) : NO_SUBAGENTS.workflows };
|
|
91209
|
+
}
|
|
91210
|
+
function listWorkflowTranscripts(workflowsDir) {
|
|
91211
|
+
const found = { count: 0, unreadable: 0, mtimeMs: 0 };
|
|
91212
|
+
const failedToRead = () => {
|
|
91213
|
+
found.count += 1;
|
|
91214
|
+
found.unreadable += 1;
|
|
91215
|
+
};
|
|
91216
|
+
const list = (dir) => {
|
|
91217
|
+
const entries = directoryEntries(dir);
|
|
91218
|
+
if (Array.isArray(entries)) return entries;
|
|
91219
|
+
failedToRead();
|
|
91220
|
+
return [];
|
|
91221
|
+
};
|
|
91222
|
+
for (const run of list(workflowsDir)) {
|
|
91223
|
+
if (!run.isDirectory()) continue;
|
|
91224
|
+
const runDir = path15.join(workflowsDir, run.name);
|
|
91225
|
+
for (const file2 of list(runDir)) {
|
|
91226
|
+
if (!file2.isFile() || !file2.name.endsWith(".jsonl")) continue;
|
|
91227
|
+
try {
|
|
91228
|
+
const stat = statIfExists(path15.join(runDir, file2.name));
|
|
91229
|
+
if (stat === null) continue;
|
|
91230
|
+
found.count += 1;
|
|
91231
|
+
found.mtimeMs = Math.max(found.mtimeMs, stat.mtimeMs);
|
|
91232
|
+
} catch {
|
|
91233
|
+
failedToRead();
|
|
91234
|
+
}
|
|
91235
|
+
}
|
|
91236
|
+
}
|
|
91237
|
+
return found;
|
|
91238
|
+
}
|
|
91239
|
+
function directoryEntries(dir) {
|
|
91240
|
+
try {
|
|
91241
|
+
return readdirSync6(dir, { withFileTypes: true });
|
|
91242
|
+
} catch (error63) {
|
|
91243
|
+
const code = error63.code;
|
|
91244
|
+
return code === "ENOENT" || code === "ENOTDIR" ? [] : { error: error63 };
|
|
91245
|
+
}
|
|
91246
|
+
}
|
|
91247
|
+
function subagentPart(entry) {
|
|
91248
|
+
const match = /^agent-(.+)\.jsonl$/.exec(entry);
|
|
91249
|
+
return match !== null && RAW_TRANSCRIPT_PART_PATTERN.test(match[1]) ? match[1] : null;
|
|
91250
|
+
}
|
|
91251
|
+
function addSubagentUsage(main2, mainFast, subagentFiles, bounds) {
|
|
91252
|
+
let total = main2;
|
|
91253
|
+
let fast = mainFast;
|
|
91254
|
+
let fastCoverageComplete = main2 === null || mainFast !== null;
|
|
91255
|
+
let skippedTooLarge = 0;
|
|
91256
|
+
const unreadable = [];
|
|
91257
|
+
const capture2 = { files: [], skipped: 0, skippedUnreadable: 0 };
|
|
91258
|
+
let endedAt;
|
|
91259
|
+
for (const file2 of subagentFiles) {
|
|
91260
|
+
const { entry, path: sidecarPath } = file2;
|
|
91261
|
+
try {
|
|
91262
|
+
if (file2.size === null) throw file2.statError;
|
|
91263
|
+
const { size } = file2;
|
|
91264
|
+
if (size > bounds.maxBytes) {
|
|
91265
|
+
skippedTooLarge += 1;
|
|
91266
|
+
capture2.skipped += 1;
|
|
91267
|
+
observeFileDiagnostic(bounds.onFileDiagnostic, { path: sidecarPath, reason: "too_large", sizeBytes: size });
|
|
91268
|
+
continue;
|
|
91269
|
+
}
|
|
91270
|
+
const transcript = readTranscriptIfExists(sidecarPath);
|
|
91271
|
+
if (transcript === null) {
|
|
91272
|
+
continue;
|
|
91273
|
+
}
|
|
91274
|
+
const part = subagentPart(entry);
|
|
91275
|
+
if (part === null) capture2.skipped += 1;
|
|
91276
|
+
else capture2.files.push({ part, path: sidecarPath, prefix: transcript.prefix });
|
|
91277
|
+
const summary = parseClaudeCodeTranscript(transcript.text);
|
|
91278
|
+
endedAt = laterTimestamp(endedAt, summary?.endedAt);
|
|
91279
|
+
if (summary?.usage) {
|
|
91280
|
+
total = addUsage(total ?? { input: 0, cacheRead: 0, cacheCreation: 0, output: 0 }, summary.usage);
|
|
91281
|
+
if (summary.fastUsage === null) fastCoverageComplete = false;
|
|
91282
|
+
}
|
|
91283
|
+
if (summary?.fastUsage) {
|
|
91284
|
+
fast = addUsage(fast ?? { input: 0, cacheRead: 0, cacheCreation: 0, output: 0 }, summary.fastUsage);
|
|
91285
|
+
}
|
|
91286
|
+
} catch (error63) {
|
|
91287
|
+
capture2.skipped += 1;
|
|
91288
|
+
capture2.skippedUnreadable += 1;
|
|
91289
|
+
unreadable.push(`${bounds.sessionId} (subagent ${entry}): ${describeReadError(error63)}`);
|
|
91290
|
+
observeFileDiagnostic(bounds.onFileDiagnostic, {
|
|
91291
|
+
path: sidecarPath,
|
|
91292
|
+
reason: "unreadable",
|
|
91293
|
+
sizeBytes: null,
|
|
91294
|
+
errorCode: diagnosticErrorCode(error63)
|
|
91295
|
+
});
|
|
91296
|
+
}
|
|
91297
|
+
}
|
|
91298
|
+
return { capture: capture2, total, fast: fastCoverageComplete ? fast : null, skippedTooLarge, unreadable, endedAt };
|
|
91299
|
+
}
|
|
91300
|
+
function claudeCodeSessionCwd(sessionId, claudeDir = path15.join(os13.homedir(), ".claude")) {
|
|
91301
|
+
if (!SESSION_UUID.test(sessionId)) return void 0;
|
|
91302
|
+
for (const projectDir of listDirs(path15.join(claudeDir, "projects"))) {
|
|
91303
|
+
const cwd = transcriptCwd(path15.join(projectDir, `${sessionId}.jsonl`));
|
|
91304
|
+
if (cwd !== void 0) return cwd;
|
|
91305
|
+
}
|
|
91306
|
+
return void 0;
|
|
91307
|
+
}
|
|
91308
|
+
|
|
91309
|
+
// ../../apps/syncservice/src/upload.ts
|
|
89865
91310
|
function parseApiResponse(schema, body, route) {
|
|
89866
91311
|
const parsed = schema.safeParse(body);
|
|
89867
91312
|
if (!parsed.success) {
|
|
@@ -90048,10 +91493,16 @@ async function uploadPreviewRegistration(apiUrl, machineToken, registration) {
|
|
|
90048
91493
|
function encodeRawTranscript(text) {
|
|
90049
91494
|
return gzipSync(redactJsonl(text));
|
|
90050
91495
|
}
|
|
91496
|
+
function readCollectedTranscript(file2, prefix) {
|
|
91497
|
+
return prefix === void 0 ? readFileSync10(file2, "utf8") : readTranscriptPrefix(file2, prefix, false);
|
|
91498
|
+
}
|
|
91499
|
+
function readUnchangedTranscript(file2, prefix) {
|
|
91500
|
+
return prefix === void 0 ? readFileSync10(file2, "utf8") : readTranscriptPrefix(file2, prefix, true);
|
|
91501
|
+
}
|
|
90051
91502
|
var BUNDLE_HEADER_BYTES = 256;
|
|
90052
91503
|
var BUNDLE_HEADER_BYTES_PER_PART = 256;
|
|
90053
91504
|
function rawUploadBody(session, readTranscript, maxBytes = MAX_RAW_UPLOAD_BYTES) {
|
|
90054
|
-
const main2 = encodeRawTranscript(readTranscript(session.transcriptPath));
|
|
91505
|
+
const main2 = encodeRawTranscript(readTranscript(session.transcriptPath, session.transcriptPrefix));
|
|
90055
91506
|
if (session.subagents === void 0) {
|
|
90056
91507
|
return main2.byteLength > maxBytes ? "too-large" : { body: main2, main: main2, contentType: "application/gzip", skippedSubagentFiles: 0, unreadableSubagentFiles: 0 };
|
|
90057
91508
|
}
|
|
@@ -90063,8 +91514,9 @@ function rawUploadBody(session, readTranscript, maxBytes = MAX_RAW_UPLOAD_BYTES)
|
|
|
90063
91514
|
for (const file2 of session.subagents.files) {
|
|
90064
91515
|
let body;
|
|
90065
91516
|
try {
|
|
90066
|
-
body = encodeRawTranscript(readTranscript(file2.path));
|
|
90067
|
-
} catch {
|
|
91517
|
+
body = encodeRawTranscript(readTranscript(file2.path, file2.prefix));
|
|
91518
|
+
} catch (error63) {
|
|
91519
|
+
if (error63 instanceof TranscriptChangedError) throw error63;
|
|
90068
91520
|
skipped += 1;
|
|
90069
91521
|
unreadable += 1;
|
|
90070
91522
|
continue;
|
|
@@ -90090,33 +91542,61 @@ function rawUploadBody(session, readTranscript, maxBytes = MAX_RAW_UPLOAD_BYTES)
|
|
|
90090
91542
|
unreadableSubagentFiles: unreadable
|
|
90091
91543
|
};
|
|
90092
91544
|
}
|
|
91545
|
+
function shippedContent(session) {
|
|
91546
|
+
if (session.subagents === void 0 && session.transcriptPrefix === void 0) return void 0;
|
|
91547
|
+
const prefixOf = (prefix) => [prefix.bytes, prefix.sha256];
|
|
91548
|
+
const parts = session.subagents?.files.map((file2) => JSON.stringify([file2.part, ...prefixOf(file2.prefix)])).sort();
|
|
91549
|
+
return JSON.stringify({
|
|
91550
|
+
transcript: session.transcriptPrefix === void 0 ? null : prefixOf(session.transcriptPrefix),
|
|
91551
|
+
parts: parts ?? null,
|
|
91552
|
+
skipped: session.subagents?.skipped ?? null,
|
|
91553
|
+
skippedUnreadable: session.subagents?.skippedUnreadable ?? null
|
|
91554
|
+
});
|
|
91555
|
+
}
|
|
91556
|
+
function unchangedSince(acknowledged, session) {
|
|
91557
|
+
if (acknowledged.mtimeMs !== session.mtimeMs) return false;
|
|
91558
|
+
const now = shippedContent(session);
|
|
91559
|
+
return now === void 0 || acknowledged.content === now;
|
|
91560
|
+
}
|
|
91561
|
+
function isRawShipped(shipped, session) {
|
|
91562
|
+
const acknowledged = shipped.get(sessionKey(session.item));
|
|
91563
|
+
return acknowledged !== void 0 && unchangedSince(acknowledged, session);
|
|
91564
|
+
}
|
|
90093
91565
|
var UNREADABLE_SUBAGENT_RETRIES = 3;
|
|
90094
|
-
async function uploadRawSessions(apiUrl, machineToken, transcriptUploadSources, readTranscript =
|
|
91566
|
+
async function uploadRawSessions(apiUrl, machineToken, transcriptUploadSources, readTranscript = readCollectedTranscript, shipped, progress) {
|
|
90095
91567
|
const summary = {
|
|
90096
91568
|
uploaded: 0,
|
|
90097
91569
|
skippedTooLarge: 0,
|
|
90098
91570
|
skippedSubagentFiles: 0,
|
|
90099
91571
|
skippedUnchanged: 0,
|
|
90100
91572
|
missingMetadata: 0,
|
|
91573
|
+
changedSinceRead: 0,
|
|
90101
91574
|
failed: [],
|
|
90102
|
-
unconfigured: false
|
|
91575
|
+
unconfigured: false,
|
|
91576
|
+
outcomes: /* @__PURE__ */ new Map()
|
|
90103
91577
|
};
|
|
90104
91578
|
const initiallyShipped = new Map(shipped);
|
|
90105
91579
|
let rateLimited;
|
|
90106
91580
|
for (const session of transcriptUploadSources) {
|
|
90107
91581
|
summary.uploaded += await progress?.beforeUpload?.() ?? 0;
|
|
90108
|
-
const
|
|
90109
|
-
const acknowledged = shipped?.get(
|
|
90110
|
-
const unchanged = acknowledged
|
|
90111
|
-
if (unchanged && acknowledged.unreadableRetries === 0 || acknowledged !== void 0 && acknowledged !== initiallyShipped.get(
|
|
91582
|
+
const key = sessionKey(session.item);
|
|
91583
|
+
const acknowledged = shipped?.get(key);
|
|
91584
|
+
const unchanged = acknowledged !== void 0 && unchangedSince(acknowledged, session);
|
|
91585
|
+
if (unchanged && acknowledged.unreadableRetries === 0 || acknowledged !== void 0 && acknowledged !== initiallyShipped.get(key)) {
|
|
90112
91586
|
summary.skippedUnchanged += 1;
|
|
91587
|
+
summary.outcomes.set(key, "unchanged");
|
|
90113
91588
|
continue;
|
|
90114
91589
|
}
|
|
90115
91590
|
try {
|
|
90116
91591
|
const encoded = rawUploadBody(session, readTranscript);
|
|
90117
91592
|
if (encoded === "too-large") {
|
|
90118
91593
|
summary.skippedTooLarge += 1;
|
|
90119
|
-
|
|
91594
|
+
summary.outcomes.set(key, "too-large");
|
|
91595
|
+
shipped?.set(key, {
|
|
91596
|
+
mtimeMs: session.mtimeMs,
|
|
91597
|
+
unreadableRetries: 0,
|
|
91598
|
+
content: shippedContent(session)
|
|
91599
|
+
});
|
|
90120
91600
|
continue;
|
|
90121
91601
|
}
|
|
90122
91602
|
const query = `harness=${session.item.harness}&harnessSessionId=${encodeURIComponent(session.item.harnessSessionId)}`;
|
|
@@ -90148,6 +91628,7 @@ async function uploadRawSessions(apiUrl, machineToken, transcriptUploadSources,
|
|
|
90148
91628
|
}
|
|
90149
91629
|
if (response.status === 404) {
|
|
90150
91630
|
summary.missingMetadata += 1;
|
|
91631
|
+
summary.outcomes.set(key, "missing-metadata");
|
|
90151
91632
|
continue;
|
|
90152
91633
|
}
|
|
90153
91634
|
if (response.status === 429) {
|
|
@@ -90158,12 +91639,20 @@ async function uploadRawSessions(apiUrl, machineToken, transcriptUploadSources,
|
|
|
90158
91639
|
}
|
|
90159
91640
|
summary.uploaded += 1;
|
|
90160
91641
|
summary.skippedSubagentFiles += skippedSubagentFiles;
|
|
91642
|
+
summary.outcomes.set(key, "uploaded");
|
|
90161
91643
|
progress?.onUploaded?.();
|
|
90162
|
-
shipped?.set(
|
|
91644
|
+
shipped?.set(key, {
|
|
90163
91645
|
mtimeMs: session.mtimeMs,
|
|
90164
|
-
unreadableRetries: unreadableSubagentFiles === 0 ? 0 : unchanged ? acknowledged.unreadableRetries - 1 : UNREADABLE_SUBAGENT_RETRIES
|
|
91646
|
+
unreadableRetries: unreadableSubagentFiles === 0 ? 0 : unchanged ? acknowledged.unreadableRetries - 1 : UNREADABLE_SUBAGENT_RETRIES,
|
|
91647
|
+
content: shippedContent(session)
|
|
90165
91648
|
});
|
|
90166
91649
|
} catch (error63) {
|
|
91650
|
+
if (error63 instanceof TranscriptChangedError) {
|
|
91651
|
+
summary.changedSinceRead += 1;
|
|
91652
|
+
summary.outcomes.set(key, "changed-since-read");
|
|
91653
|
+
continue;
|
|
91654
|
+
}
|
|
91655
|
+
summary.outcomes.set(key, "failed");
|
|
90167
91656
|
if (error63 instanceof RateLimitedError) rateLimited ??= error63;
|
|
90168
91657
|
summary.failed.push(
|
|
90169
91658
|
`${session.item.harnessSessionId}: ${error63 instanceof Error ? error63.message : String(error63)}`
|
|
@@ -90228,7 +91717,7 @@ function gotrueAuthClient(url2, key) {
|
|
|
90228
91717
|
}
|
|
90229
91718
|
async function runEnrollment(config2, options) {
|
|
90230
91719
|
const log3 = options.log ?? console.log;
|
|
90231
|
-
const name = options.name?.trim() ||
|
|
91720
|
+
const name = options.name?.trim() || os14.hostname();
|
|
90232
91721
|
let scopePaths = (options.scope ?? []).map(normalizeScopePath);
|
|
90233
91722
|
let captureMode = scopePaths.length > 0 ? "scope" : "org-repos";
|
|
90234
91723
|
if (captureMode === "scope") {
|
|
@@ -90245,7 +91734,7 @@ async function runEnrollment(config2, options) {
|
|
|
90245
91734
|
const anyWorkspaceClaims = (orgPolicy?.workspaces ?? []).some(workspaceClaimsAnyRepo);
|
|
90246
91735
|
if (orgPolicy !== void 0 && orgPolicy.owners.length === 0 && orgPolicy.repos === null && !anyWorkspaceClaims) {
|
|
90247
91736
|
if (config2.appEnv === "development") {
|
|
90248
|
-
const home = normalizeScopePath(
|
|
91737
|
+
const home = normalizeScopePath(os14.homedir());
|
|
90249
91738
|
log3(
|
|
90250
91739
|
`No GitHub integration on org "${result.org.name}", syncing sessions under ${home} (your home folder, local DB only). Re-enroll with --scope <path> to narrow.`
|
|
90251
91740
|
);
|
|
@@ -90319,7 +91808,7 @@ async function enroll(config2, options, createAuthClient = gotrueAuthClient, lis
|
|
|
90319
91808
|
{ ...listener, log: options.log, redeem: (code) => redeemApproval(config2.apiUrl, code, verifier) }
|
|
90320
91809
|
);
|
|
90321
91810
|
}
|
|
90322
|
-
var challengeFor = (verifier) =>
|
|
91811
|
+
var challengeFor = (verifier) => createHash6("sha256").update(verifier).digest("hex");
|
|
90323
91812
|
function enrollApprovalUrl(webUrl, {
|
|
90324
91813
|
name,
|
|
90325
91814
|
state,
|
|
@@ -90557,398 +92046,6 @@ function removeFolder(config2, rawPath, enrollCommand, log3, deps = defaultDeps)
|
|
|
90557
92046
|
log3(`Removed ${folder}, sessions there stay local from the next sync pass.`);
|
|
90558
92047
|
}
|
|
90559
92048
|
|
|
90560
|
-
// ../../apps/syncservice/src/claudeCode.ts
|
|
90561
|
-
import { readdirSync as readdirSync6 } from "node:fs";
|
|
90562
|
-
import os14 from "node:os";
|
|
90563
|
-
import path15 from "node:path";
|
|
90564
|
-
import { setImmediate as yieldToEventLoop2 } from "node:timers/promises";
|
|
90565
|
-
function parseClaudeCodeTranscript(text) {
|
|
90566
|
-
const usageByMessage = /* @__PURE__ */ new Map();
|
|
90567
|
-
const modelCounts = /* @__PURE__ */ new Map();
|
|
90568
|
-
const timestamps = [];
|
|
90569
|
-
let title;
|
|
90570
|
-
let cwd;
|
|
90571
|
-
let gitBranch;
|
|
90572
|
-
let entrypoint;
|
|
90573
|
-
let anonCounter = 0;
|
|
90574
|
-
let speedMode;
|
|
90575
|
-
for (const record2 of parseJsonl(text)) {
|
|
90576
|
-
if (record2.type === "custom-title" && typeof record2.customTitle === "string") {
|
|
90577
|
-
title = record2.customTitle;
|
|
90578
|
-
continue;
|
|
90579
|
-
}
|
|
90580
|
-
if (record2.type === "ai-title" && typeof record2.aiTitle === "string") {
|
|
90581
|
-
title = record2.aiTitle;
|
|
90582
|
-
continue;
|
|
90583
|
-
}
|
|
90584
|
-
if (cwd === void 0 && typeof record2.cwd === "string" && record2.cwd !== "") cwd = record2.cwd;
|
|
90585
|
-
if (typeof record2.entrypoint === "string" && record2.entrypoint !== "") {
|
|
90586
|
-
entrypoint ??= record2.entrypoint;
|
|
90587
|
-
}
|
|
90588
|
-
if (typeof record2.gitBranch === "string" && record2.gitBranch !== "") {
|
|
90589
|
-
gitBranch ??= record2.gitBranch;
|
|
90590
|
-
}
|
|
90591
|
-
if (typeof record2.timestamp === "string") timestamps.push(record2.timestamp);
|
|
90592
|
-
if (record2.type !== "assistant") continue;
|
|
90593
|
-
const message2 = record2.message;
|
|
90594
|
-
if (!message2 || message2.model === "<synthetic>") continue;
|
|
90595
|
-
const id = message2.id ?? (typeof record2.requestId === "string" ? record2.requestId : void 0) ?? (typeof record2.uuid === "string" ? record2.uuid : void 0) ?? `anon-${anonCounter++}`;
|
|
90596
|
-
const previousMessage = usageByMessage.get(id);
|
|
90597
|
-
const isNewMessage = previousMessage === void 0;
|
|
90598
|
-
const speedValue = message2.usage?.speed;
|
|
90599
|
-
const observedSpeed = speedModeOf(speedValue);
|
|
90600
|
-
const messageSpeed = observedSpeed ?? previousMessage?.speedMode;
|
|
90601
|
-
if (speedValue !== void 0) speedMode = observedSpeed ?? "unknown";
|
|
90602
|
-
if (message2.usage) {
|
|
90603
|
-
usageByMessage.set(id, {
|
|
90604
|
-
usage: toUsage(message2.usage),
|
|
90605
|
-
...messageSpeed && { speedMode: messageSpeed }
|
|
90606
|
-
});
|
|
90607
|
-
} else if (isNewMessage) {
|
|
90608
|
-
usageByMessage.set(id, {
|
|
90609
|
-
usage: { input: 0, cacheRead: 0, cacheCreation: 0, output: 0 }
|
|
90610
|
-
});
|
|
90611
|
-
}
|
|
90612
|
-
if (isNewMessage && typeof message2.model === "string") {
|
|
90613
|
-
modelCounts.set(message2.model, (modelCounts.get(message2.model) ?? 0) + 1);
|
|
90614
|
-
}
|
|
90615
|
-
}
|
|
90616
|
-
if (usageByMessage.size === 0 && timestamps.length === 0) return null;
|
|
90617
|
-
const summed = [...usageByMessage.values()].map((entry) => entry.usage).reduce(addUsage, {
|
|
90618
|
-
input: 0,
|
|
90619
|
-
cacheRead: 0,
|
|
90620
|
-
cacheCreation: 0,
|
|
90621
|
-
output: 0
|
|
90622
|
-
});
|
|
90623
|
-
const speedFullyMeasured = usageByMessage.size > 0 && [...usageByMessage.values()].every((entry) => entry.speedMode !== void 0);
|
|
90624
|
-
const fastUsage = speedFullyMeasured ? [...usageByMessage.values()].filter((entry) => entry.speedMode === "fast").map((entry) => entry.usage).reduce(addUsage, { input: 0, cacheRead: 0, cacheCreation: 0, output: 0 }) : null;
|
|
90625
|
-
return {
|
|
90626
|
-
startedAt: timestamps[0],
|
|
90627
|
-
endedAt: timestamps[timestamps.length - 1],
|
|
90628
|
-
model: dominantKey(modelCounts),
|
|
90629
|
-
usage: usageByMessage.size > 0 ? summed : null,
|
|
90630
|
-
fastUsage,
|
|
90631
|
-
...speedMode && { speedMode },
|
|
90632
|
-
title,
|
|
90633
|
-
turnCount: usageByMessage.size,
|
|
90634
|
-
cwd,
|
|
90635
|
-
gitBranch,
|
|
90636
|
-
entrypoint
|
|
90637
|
-
};
|
|
90638
|
-
}
|
|
90639
|
-
var AGENT_SDK_ENTRYPOINTS = /* @__PURE__ */ new Set(["sdk-py", "sdk-ts"]);
|
|
90640
|
-
function isAutomationEntrypoint(entrypoint) {
|
|
90641
|
-
return entrypoint !== void 0 && AGENT_SDK_ENTRYPOINTS.has(entrypoint);
|
|
90642
|
-
}
|
|
90643
|
-
async function collectClaudeCodeSessions(options = {}) {
|
|
90644
|
-
const claudeDir = options.claudeDir ?? path15.join(os14.homedir(), ".claude");
|
|
90645
|
-
const now = options.now ?? Date.now();
|
|
90646
|
-
const capture2 = options.capture ?? captureEverythingUnresolved;
|
|
90647
|
-
const maxBytes = options.maxTranscriptBytes ?? MAX_TRANSCRIPT_FILE_BYTES;
|
|
90648
|
-
const projectsDir = path15.join(claudeDir, "projects");
|
|
90649
|
-
const items = [];
|
|
90650
|
-
const skips = { skippedOutOfScope: 0, skippedNoRepo: 0 };
|
|
90651
|
-
const candidates = [];
|
|
90652
|
-
const unreadable = [];
|
|
90653
|
-
let skippedTooLarge = 0;
|
|
90654
|
-
const home = path15.dirname(claudeDir);
|
|
90655
|
-
const lifecycle = options.lifecycle ?? readLifecycleJournal(home, now, ["claude-local"]);
|
|
90656
|
-
for (const projectDir of listDirs(projectsDir)) {
|
|
90657
|
-
for (const entry of listFiles(projectDir, ".jsonl")) {
|
|
90658
|
-
await yieldToEventLoop2();
|
|
90659
|
-
const transcriptPath = path15.join(projectDir, entry);
|
|
90660
|
-
const stat = statIfExists(transcriptPath);
|
|
90661
|
-
if (!stat) continue;
|
|
90662
|
-
const sessionId = path15.basename(entry, ".jsonl");
|
|
90663
|
-
const subagents = options.locationsOnly ? NO_SUBAGENTS : listSubagents(path15.join(projectDir, sessionId, "subagents"));
|
|
90664
|
-
const mtimeMs = Math.max(stat.mtimeMs, subagents.workflows.mtimeMs, ...subagents.files.map((file2) => file2.mtimeMs ?? 0));
|
|
90665
|
-
if (options.sinceMs !== void 0 && mtimeMs < options.sinceMs) continue;
|
|
90666
|
-
if (stat.size > maxBytes) {
|
|
90667
|
-
skippedTooLarge += 1;
|
|
90668
|
-
observeFileDiagnostic(options.onFileDiagnostic, {
|
|
90669
|
-
path: transcriptPath,
|
|
90670
|
-
reason: "too_large",
|
|
90671
|
-
sizeBytes: stat.size
|
|
90672
|
-
});
|
|
90673
|
-
continue;
|
|
90674
|
-
}
|
|
90675
|
-
const endObservedAt = observedSessionEnd(lifecycle.latestBoundary("claude-local", sessionId), mtimeMs);
|
|
90676
|
-
const status = endObservedAt === null && now - mtimeMs < ACTIVE_GRACE_MS ? "active" : "inactive";
|
|
90677
|
-
try {
|
|
90678
|
-
const cwd = transcriptCwd(transcriptPath);
|
|
90679
|
-
const decision = captureDecision(cwd, capture2);
|
|
90680
|
-
if (!decision.capture) {
|
|
90681
|
-
countCaptureSkip(skips, decision.reason);
|
|
90682
|
-
continue;
|
|
90683
|
-
}
|
|
90684
|
-
if (options.locationsOnly) {
|
|
90685
|
-
items.push({
|
|
90686
|
-
mtimeMs: stat.mtimeMs,
|
|
90687
|
-
transcriptPath,
|
|
90688
|
-
...cwd && { cwd },
|
|
90689
|
-
item: { harness: "claude-local", harnessSessionId: sessionId, status, capturedVia: decision.via }
|
|
90690
|
-
});
|
|
90691
|
-
continue;
|
|
90692
|
-
}
|
|
90693
|
-
candidates.push({
|
|
90694
|
-
transcriptPath,
|
|
90695
|
-
sessionId,
|
|
90696
|
-
mtimeMs,
|
|
90697
|
-
subagents,
|
|
90698
|
-
status,
|
|
90699
|
-
endObservedAt,
|
|
90700
|
-
...cwd && { cwd },
|
|
90701
|
-
decision
|
|
90702
|
-
});
|
|
90703
|
-
} catch (error63) {
|
|
90704
|
-
unreadable.push(`${sessionId}: ${describeReadError(error63)}`);
|
|
90705
|
-
observeFileDiagnostic(options.onFileDiagnostic, {
|
|
90706
|
-
path: transcriptPath,
|
|
90707
|
-
reason: "unreadable",
|
|
90708
|
-
sizeBytes: null,
|
|
90709
|
-
errorCode: diagnosticErrorCode(error63)
|
|
90710
|
-
});
|
|
90711
|
-
}
|
|
90712
|
-
}
|
|
90713
|
-
}
|
|
90714
|
-
if (options.locationsOnly) {
|
|
90715
|
-
return {
|
|
90716
|
-
sessions: items.sort((a, b2) => b2.mtimeMs - a.mtimeMs),
|
|
90717
|
-
...skips,
|
|
90718
|
-
skippedTooLarge,
|
|
90719
|
-
unreadable,
|
|
90720
|
-
heldBack: 0
|
|
90721
|
-
};
|
|
90722
|
-
}
|
|
90723
|
-
candidates.sort((a, b2) => b2.mtimeMs - a.mtimeMs);
|
|
90724
|
-
const chosen = options.maxSessions === void 0 ? candidates : candidates.slice(0, options.maxSessions);
|
|
90725
|
-
for (const candidate of chosen) {
|
|
90726
|
-
await yieldToEventLoop2();
|
|
90727
|
-
const { transcriptPath, sessionId, mtimeMs, subagents, status, endObservedAt, cwd, decision } = candidate;
|
|
90728
|
-
try {
|
|
90729
|
-
const text = readIfExists(transcriptPath);
|
|
90730
|
-
if (text === null) continue;
|
|
90731
|
-
const summary = parseClaudeCodeTranscript(text);
|
|
90732
|
-
if (!summary) continue;
|
|
90733
|
-
const usage = addSubagentUsage(
|
|
90734
|
-
summary.usage,
|
|
90735
|
-
summary.fastUsage,
|
|
90736
|
-
subagents.files,
|
|
90737
|
-
{ maxBytes, sessionId, onFileDiagnostic: options.onFileDiagnostic }
|
|
90738
|
-
);
|
|
90739
|
-
skippedTooLarge += usage.skippedTooLarge;
|
|
90740
|
-
unreadable.push(...usage.unreadable);
|
|
90741
|
-
const endedAt = endObservedAt ?? laterTimestamp(summary.endedAt, usage.endedAt);
|
|
90742
|
-
items.push({
|
|
90743
|
-
mtimeMs,
|
|
90744
|
-
transcriptPath,
|
|
90745
|
-
subagents: {
|
|
90746
|
-
files: usage.capture.files,
|
|
90747
|
-
skipped: usage.capture.skipped + subagents.workflows.count,
|
|
90748
|
-
skippedUnreadable: usage.capture.skippedUnreadable + subagents.workflows.unreadable
|
|
90749
|
-
},
|
|
90750
|
-
...cwd && { cwd },
|
|
90751
|
-
item: {
|
|
90752
|
-
harness: "claude-local",
|
|
90753
|
-
harnessSessionId: sessionId,
|
|
90754
|
-
status,
|
|
90755
|
-
...summary.startedAt && { startedAt: summary.startedAt },
|
|
90756
|
-
...endedAt && { endedAt },
|
|
90757
|
-
...summary.model && { model: summary.model },
|
|
90758
|
-
...summary.speedMode && { speedMode: summary.speedMode },
|
|
90759
|
-
// Total input-class tokens the source reported (uncached + cache
|
|
90760
|
-
// reads + cache writes) — a real reported total, not an estimate.
|
|
90761
|
-
// The cache breakdown rides along: pricing bills reads at 0.1×
|
|
90762
|
-
// and writes at 1.25×, so collapsing it would inflate estimates.
|
|
90763
|
-
...usage.total && {
|
|
90764
|
-
inputTokens: usage.total.input + usage.total.cacheRead + usage.total.cacheCreation
|
|
90765
|
-
},
|
|
90766
|
-
...usage.total && { outputTokens: usage.total.output },
|
|
90767
|
-
...usage.total && { cacheReadTokens: usage.total.cacheRead },
|
|
90768
|
-
...usage.total && { cacheWriteTokens: usage.total.cacheCreation },
|
|
90769
|
-
...usage.fast && {
|
|
90770
|
-
fastInputTokens: usage.fast.input + usage.fast.cacheRead + usage.fast.cacheCreation,
|
|
90771
|
-
fastOutputTokens: usage.fast.output,
|
|
90772
|
-
fastCacheReadTokens: usage.fast.cacheRead,
|
|
90773
|
-
fastCacheWriteTokens: usage.fast.cacheCreation
|
|
90774
|
-
},
|
|
90775
|
-
...summary.title && { title: redactText(summary.title).slice(0, 500) },
|
|
90776
|
-
...isAutomationEntrypoint(summary.entrypoint) && { automation: true },
|
|
90777
|
-
// The PR-join coordinates: the checkout's directory name (bare
|
|
90778
|
-
// repo, not the local path) and the branch the work started on.
|
|
90779
|
-
...cwd && { repoDir: path15.basename(cwd) },
|
|
90780
|
-
...summary.gitBranch && { gitBranch: summary.gitBranch },
|
|
90781
|
-
...decision.repoFullName && { repoFullName: decision.repoFullName },
|
|
90782
|
-
capturedVia: decision.via
|
|
90783
|
-
}
|
|
90784
|
-
});
|
|
90785
|
-
} catch (error63) {
|
|
90786
|
-
unreadable.push(`${sessionId}: ${describeReadError(error63)}`);
|
|
90787
|
-
observeFileDiagnostic(options.onFileDiagnostic, {
|
|
90788
|
-
path: transcriptPath,
|
|
90789
|
-
reason: "unreadable",
|
|
90790
|
-
sizeBytes: null,
|
|
90791
|
-
errorCode: diagnosticErrorCode(error63)
|
|
90792
|
-
});
|
|
90793
|
-
}
|
|
90794
|
-
}
|
|
90795
|
-
return {
|
|
90796
|
-
sessions: items.sort((a, b2) => b2.mtimeMs - a.mtimeMs),
|
|
90797
|
-
...skips,
|
|
90798
|
-
skippedTooLarge,
|
|
90799
|
-
unreadable,
|
|
90800
|
-
heldBack: candidates.length - chosen.length
|
|
90801
|
-
};
|
|
90802
|
-
}
|
|
90803
|
-
function transcriptCwd(transcriptPath) {
|
|
90804
|
-
const record2 = readFirstRecord(
|
|
90805
|
-
transcriptPath,
|
|
90806
|
-
(candidate) => typeof candidate.cwd === "string" && candidate.cwd !== ""
|
|
90807
|
-
);
|
|
90808
|
-
return typeof record2?.cwd === "string" ? record2.cwd : void 0;
|
|
90809
|
-
}
|
|
90810
|
-
function toUsage(u) {
|
|
90811
|
-
const n = (value) => typeof value === "number" ? value : 0;
|
|
90812
|
-
return {
|
|
90813
|
-
input: n(u.input_tokens),
|
|
90814
|
-
cacheRead: n(u.cache_read_input_tokens),
|
|
90815
|
-
cacheCreation: n(u.cache_creation_input_tokens),
|
|
90816
|
-
output: n(u.output_tokens)
|
|
90817
|
-
};
|
|
90818
|
-
}
|
|
90819
|
-
function speedModeOf(value) {
|
|
90820
|
-
return value === "fast" || value === "standard" ? value : void 0;
|
|
90821
|
-
}
|
|
90822
|
-
function addUsage(a, b2) {
|
|
90823
|
-
return {
|
|
90824
|
-
input: a.input + b2.input,
|
|
90825
|
-
cacheRead: a.cacheRead + b2.cacheRead,
|
|
90826
|
-
cacheCreation: a.cacheCreation + b2.cacheCreation,
|
|
90827
|
-
output: a.output + b2.output
|
|
90828
|
-
};
|
|
90829
|
-
}
|
|
90830
|
-
var NO_SUBAGENTS = { files: [], workflows: { count: 0, unreadable: 0, mtimeMs: 0 } };
|
|
90831
|
-
function listSubagents(subagentsDir) {
|
|
90832
|
-
const entries = directoryEntries(subagentsDir);
|
|
90833
|
-
if (!Array.isArray(entries)) {
|
|
90834
|
-
return { ...NO_SUBAGENTS, files: [{ entry: "subagents/", path: subagentsDir, mtimeMs: null, size: null, statError: entries.error }] };
|
|
90835
|
-
}
|
|
90836
|
-
const names = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => entry.name).sort();
|
|
90837
|
-
const files = names.flatMap((entry) => {
|
|
90838
|
-
const filePath = path15.join(subagentsDir, entry);
|
|
90839
|
-
try {
|
|
90840
|
-
const stat = statIfExists(filePath);
|
|
90841
|
-
return stat === null ? [] : [{ entry, path: filePath, mtimeMs: stat.mtimeMs, size: stat.size, statError: null }];
|
|
90842
|
-
} catch (statError) {
|
|
90843
|
-
return [{ entry, path: filePath, mtimeMs: null, size: null, statError }];
|
|
90844
|
-
}
|
|
90845
|
-
});
|
|
90846
|
-
const hasWorkflows = entries.some((entry) => entry.isDirectory() && entry.name === "workflows");
|
|
90847
|
-
return { files, workflows: hasWorkflows ? listWorkflowTranscripts(path15.join(subagentsDir, "workflows")) : NO_SUBAGENTS.workflows };
|
|
90848
|
-
}
|
|
90849
|
-
function listWorkflowTranscripts(workflowsDir) {
|
|
90850
|
-
const found = { count: 0, unreadable: 0, mtimeMs: 0 };
|
|
90851
|
-
const failedToRead = () => {
|
|
90852
|
-
found.count += 1;
|
|
90853
|
-
found.unreadable += 1;
|
|
90854
|
-
};
|
|
90855
|
-
const list = (dir) => {
|
|
90856
|
-
const entries = directoryEntries(dir);
|
|
90857
|
-
if (Array.isArray(entries)) return entries;
|
|
90858
|
-
failedToRead();
|
|
90859
|
-
return [];
|
|
90860
|
-
};
|
|
90861
|
-
for (const run of list(workflowsDir)) {
|
|
90862
|
-
if (!run.isDirectory()) continue;
|
|
90863
|
-
const runDir = path15.join(workflowsDir, run.name);
|
|
90864
|
-
for (const file2 of list(runDir)) {
|
|
90865
|
-
if (!file2.isFile() || !file2.name.endsWith(".jsonl")) continue;
|
|
90866
|
-
try {
|
|
90867
|
-
const stat = statIfExists(path15.join(runDir, file2.name));
|
|
90868
|
-
if (stat === null) continue;
|
|
90869
|
-
found.count += 1;
|
|
90870
|
-
found.mtimeMs = Math.max(found.mtimeMs, stat.mtimeMs);
|
|
90871
|
-
} catch {
|
|
90872
|
-
failedToRead();
|
|
90873
|
-
}
|
|
90874
|
-
}
|
|
90875
|
-
}
|
|
90876
|
-
return found;
|
|
90877
|
-
}
|
|
90878
|
-
function directoryEntries(dir) {
|
|
90879
|
-
try {
|
|
90880
|
-
return readdirSync6(dir, { withFileTypes: true });
|
|
90881
|
-
} catch (error63) {
|
|
90882
|
-
const code = error63.code;
|
|
90883
|
-
return code === "ENOENT" || code === "ENOTDIR" ? [] : { error: error63 };
|
|
90884
|
-
}
|
|
90885
|
-
}
|
|
90886
|
-
function subagentPart(entry) {
|
|
90887
|
-
const match = /^agent-(.+)\.jsonl$/.exec(entry);
|
|
90888
|
-
return match !== null && RAW_TRANSCRIPT_PART_PATTERN.test(match[1]) ? match[1] : null;
|
|
90889
|
-
}
|
|
90890
|
-
function laterTimestamp(a, b2) {
|
|
90891
|
-
if (a === void 0 || b2 === void 0) return a ?? b2;
|
|
90892
|
-
return Date.parse(b2) > Date.parse(a) ? b2 : a;
|
|
90893
|
-
}
|
|
90894
|
-
function addSubagentUsage(main2, mainFast, subagentFiles, bounds) {
|
|
90895
|
-
let total = main2;
|
|
90896
|
-
let fast = mainFast;
|
|
90897
|
-
let fastCoverageComplete = main2 === null || mainFast !== null;
|
|
90898
|
-
let skippedTooLarge = 0;
|
|
90899
|
-
const unreadable = [];
|
|
90900
|
-
const capture2 = { files: [], skipped: 0, skippedUnreadable: 0 };
|
|
90901
|
-
let endedAt;
|
|
90902
|
-
for (const file2 of subagentFiles) {
|
|
90903
|
-
const { entry, path: sidecarPath } = file2;
|
|
90904
|
-
try {
|
|
90905
|
-
if (file2.size === null) throw file2.statError;
|
|
90906
|
-
const { size } = file2;
|
|
90907
|
-
if (size > bounds.maxBytes) {
|
|
90908
|
-
skippedTooLarge += 1;
|
|
90909
|
-
capture2.skipped += 1;
|
|
90910
|
-
observeFileDiagnostic(bounds.onFileDiagnostic, { path: sidecarPath, reason: "too_large", sizeBytes: size });
|
|
90911
|
-
continue;
|
|
90912
|
-
}
|
|
90913
|
-
const text = readIfExists(sidecarPath);
|
|
90914
|
-
if (text === null) {
|
|
90915
|
-
continue;
|
|
90916
|
-
}
|
|
90917
|
-
const part = subagentPart(entry);
|
|
90918
|
-
if (part === null) capture2.skipped += 1;
|
|
90919
|
-
else capture2.files.push({ part, path: sidecarPath });
|
|
90920
|
-
const summary = parseClaudeCodeTranscript(text);
|
|
90921
|
-
endedAt = laterTimestamp(endedAt, summary?.endedAt);
|
|
90922
|
-
if (summary?.usage) {
|
|
90923
|
-
total = addUsage(total ?? { input: 0, cacheRead: 0, cacheCreation: 0, output: 0 }, summary.usage);
|
|
90924
|
-
if (summary.fastUsage === null) fastCoverageComplete = false;
|
|
90925
|
-
}
|
|
90926
|
-
if (summary?.fastUsage) {
|
|
90927
|
-
fast = addUsage(fast ?? { input: 0, cacheRead: 0, cacheCreation: 0, output: 0 }, summary.fastUsage);
|
|
90928
|
-
}
|
|
90929
|
-
} catch (error63) {
|
|
90930
|
-
capture2.skipped += 1;
|
|
90931
|
-
capture2.skippedUnreadable += 1;
|
|
90932
|
-
unreadable.push(`${bounds.sessionId} (subagent ${entry}): ${describeReadError(error63)}`);
|
|
90933
|
-
observeFileDiagnostic(bounds.onFileDiagnostic, {
|
|
90934
|
-
path: sidecarPath,
|
|
90935
|
-
reason: "unreadable",
|
|
90936
|
-
sizeBytes: null,
|
|
90937
|
-
errorCode: diagnosticErrorCode(error63)
|
|
90938
|
-
});
|
|
90939
|
-
}
|
|
90940
|
-
}
|
|
90941
|
-
return { capture: capture2, total, fast: fastCoverageComplete ? fast : null, skippedTooLarge, unreadable, endedAt };
|
|
90942
|
-
}
|
|
90943
|
-
function claudeCodeSessionCwd(sessionId, claudeDir = path15.join(os14.homedir(), ".claude")) {
|
|
90944
|
-
if (!SESSION_UUID.test(sessionId)) return void 0;
|
|
90945
|
-
for (const projectDir of listDirs(path15.join(claudeDir, "projects"))) {
|
|
90946
|
-
const cwd = transcriptCwd(path15.join(projectDir, `${sessionId}.jsonl`));
|
|
90947
|
-
if (cwd !== void 0) return cwd;
|
|
90948
|
-
}
|
|
90949
|
-
return void 0;
|
|
90950
|
-
}
|
|
90951
|
-
|
|
90952
92049
|
// ../../apps/syncservice/src/protectedRepoDiscovery.ts
|
|
90953
92050
|
import os15 from "node:os";
|
|
90954
92051
|
import path16 from "node:path";
|
|
@@ -91379,7 +92476,9 @@ var claudeLocal = {
|
|
|
91379
92476
|
const dirs = new Set(installs.map((install) => install.configDir ?? homeDir));
|
|
91380
92477
|
return [...dirs].map((dir) => path18.join(dir, ".claude.json"));
|
|
91381
92478
|
},
|
|
91382
|
-
registration: jsonServerEntry
|
|
92479
|
+
registration: jsonServerEntry,
|
|
92480
|
+
// <project folder>/<session id>.jsonl
|
|
92481
|
+
transcriptSession: (transcriptPath) => ({ externalId: path18.basename(transcriptPath, ".jsonl") })
|
|
91383
92482
|
};
|
|
91384
92483
|
var codexLocal = {
|
|
91385
92484
|
harness: "codex-local",
|
|
@@ -91419,7 +92518,12 @@ var codexLocal = {
|
|
|
91419
92518
|
registration: (content) => content,
|
|
91420
92519
|
// The stored ID is the base rollout's file name, and hooks name the
|
|
91421
92520
|
// thread, whose ID the file name ends with.
|
|
91422
|
-
hookSessionId: (externalId) => rolloutIdentity(`${externalId}.jsonl`).threadId
|
|
92521
|
+
hookSessionId: (externalId) => rolloutIdentity(`${externalId}.jsonl`).threadId,
|
|
92522
|
+
// A page file continues a thread whose base rollout names the session.
|
|
92523
|
+
transcriptSession(transcriptPath) {
|
|
92524
|
+
const identity = rolloutIdentity(transcriptPath);
|
|
92525
|
+
return identity.page ? { hookId: identity.threadId } : { externalId: path18.basename(transcriptPath, ".jsonl") };
|
|
92526
|
+
}
|
|
91423
92527
|
};
|
|
91424
92528
|
var cursorLocal = {
|
|
91425
92529
|
harness: "cursor-local",
|
|
@@ -91714,7 +92818,7 @@ async function checkCursor(mcpJsonPath, context2) {
|
|
|
91714
92818
|
}
|
|
91715
92819
|
|
|
91716
92820
|
// ../../apps/syncservice/src/harnessConnections.ts
|
|
91717
|
-
import { mkdirSync as mkdirSync10, readFileSync as
|
|
92821
|
+
import { mkdirSync as mkdirSync10, readFileSync as readFileSync11, renameSync as renameSync6, watch as fsWatch, writeFileSync as writeFileSync8 } from "node:fs";
|
|
91718
92822
|
import os18 from "node:os";
|
|
91719
92823
|
import path19 from "node:path";
|
|
91720
92824
|
var CONNECTION_STATES = [
|
|
@@ -91873,7 +92977,7 @@ function readSavedFile(readTextFile, homeDir) {
|
|
|
91873
92977
|
}
|
|
91874
92978
|
function bestEffortRead(filePath) {
|
|
91875
92979
|
try {
|
|
91876
|
-
return
|
|
92980
|
+
return readFileSync11(filePath, "utf8");
|
|
91877
92981
|
} catch {
|
|
91878
92982
|
return null;
|
|
91879
92983
|
}
|
|
@@ -91882,8 +92986,8 @@ function writeSavedFile(record2, homeDir) {
|
|
|
91882
92986
|
const file2 = detectedClientsPath(homeDir);
|
|
91883
92987
|
mkdirSync10(path19.dirname(file2), { recursive: true });
|
|
91884
92988
|
const temporary = `${file2}.${process.pid}.tmp`;
|
|
91885
|
-
|
|
91886
|
-
|
|
92989
|
+
writeFileSync8(temporary, JSON.stringify(record2, null, 2));
|
|
92990
|
+
renameSync6(temporary, file2);
|
|
91887
92991
|
}
|
|
91888
92992
|
function loadDetectedClients(readTextFile = bestEffortRead, homeDir) {
|
|
91889
92993
|
const parsed = detectedClientsSchema.safeParse(readSavedFile(readTextFile, homeDir));
|
|
@@ -92233,7 +93337,7 @@ function recentCatchupFloor(sinceMs, sessions) {
|
|
|
92233
93337
|
function sessionsForCatchup(sessions, selection) {
|
|
92234
93338
|
if (!selection) return sessions;
|
|
92235
93339
|
return sessions.filter(
|
|
92236
|
-
(session) => session.mtimeMs >= selection.sinceMs || selection.
|
|
93340
|
+
(session) => session.mtimeMs >= selection.sinceMs || selection.sessions.has(sessionKey(session.item))
|
|
92237
93341
|
);
|
|
92238
93342
|
}
|
|
92239
93343
|
function recentSyncOptions(shared, narrowSinceMs, selection) {
|
|
@@ -92247,7 +93351,7 @@ function recentSyncOptions(shared, narrowSinceMs, selection) {
|
|
|
92247
93351
|
}
|
|
92248
93352
|
|
|
92249
93353
|
// ../../apps/syncservice/src/branchFacts.ts
|
|
92250
|
-
import { readFileSync as
|
|
93354
|
+
import { readFileSync as readFileSync12 } from "node:fs";
|
|
92251
93355
|
import path20 from "node:path";
|
|
92252
93356
|
var SHA_SHAPE = /^[0-9a-f]{40}$/;
|
|
92253
93357
|
function observeBranch(cwd, environment = {}) {
|
|
@@ -92318,7 +93422,7 @@ function resolveRef2(commonDir, ref) {
|
|
|
92318
93422
|
}
|
|
92319
93423
|
function readIfExists2(p) {
|
|
92320
93424
|
try {
|
|
92321
|
-
return
|
|
93425
|
+
return readFileSync12(p, "utf8");
|
|
92322
93426
|
} catch {
|
|
92323
93427
|
return null;
|
|
92324
93428
|
}
|
|
@@ -92336,7 +93440,8 @@ async function registerAnnouncedPreviews(apiUrl, machineToken, collected, memo2,
|
|
|
92336
93440
|
};
|
|
92337
93441
|
for (const session of collected) {
|
|
92338
93442
|
if (session.item.status !== "active") continue;
|
|
92339
|
-
const
|
|
93443
|
+
const key = sessionKey(session.item);
|
|
93444
|
+
const state = memo2.get(key);
|
|
92340
93445
|
if (state?.foreignOwner || state?.previewsOff) continue;
|
|
92341
93446
|
if (state?.examinedMtimeMs === session.mtimeMs) continue;
|
|
92342
93447
|
try {
|
|
@@ -92354,7 +93459,7 @@ async function registerAnnouncedPreviews(apiUrl, machineToken, collected, memo2,
|
|
|
92354
93459
|
}));
|
|
92355
93460
|
const registeredJson = JSON.stringify(previews);
|
|
92356
93461
|
if (previews.length === 0 || state?.registeredJson === registeredJson) {
|
|
92357
|
-
memo2.set(
|
|
93462
|
+
memo2.set(key, { ...state, examinedMtimeMs: session.mtimeMs });
|
|
92358
93463
|
continue;
|
|
92359
93464
|
}
|
|
92360
93465
|
const outcome = await upload(apiUrl, machineToken, {
|
|
@@ -92363,16 +93468,16 @@ async function registerAnnouncedPreviews(apiUrl, machineToken, collected, memo2,
|
|
|
92363
93468
|
previews
|
|
92364
93469
|
});
|
|
92365
93470
|
if (outcome === "foreign-owner") {
|
|
92366
|
-
memo2.set(
|
|
93471
|
+
memo2.set(key, { examinedMtimeMs: session.mtimeMs, foreignOwner: true });
|
|
92367
93472
|
summary.foreignOwned.push(session.item.harnessSessionId);
|
|
92368
93473
|
continue;
|
|
92369
93474
|
}
|
|
92370
93475
|
if (outcome === "previews-off") {
|
|
92371
|
-
memo2.set(
|
|
93476
|
+
memo2.set(key, { examinedMtimeMs: session.mtimeMs, previewsOff: true });
|
|
92372
93477
|
summary.previewsOff.push(session.item.harnessSessionId);
|
|
92373
93478
|
continue;
|
|
92374
93479
|
}
|
|
92375
|
-
memo2.set(
|
|
93480
|
+
memo2.set(key, { examinedMtimeMs: session.mtimeMs, registeredJson });
|
|
92376
93481
|
summary.registered += 1;
|
|
92377
93482
|
} catch (error63) {
|
|
92378
93483
|
summary.failed.push(
|
|
@@ -92531,6 +93636,8 @@ async function runSyncPassInternal(config2, options, diagnostic) {
|
|
|
92531
93636
|
sinceMs,
|
|
92532
93637
|
capture: capture2,
|
|
92533
93638
|
maxSessions,
|
|
93639
|
+
...options.codexThreadLinks && { threadLinks: options.codexThreadLinks },
|
|
93640
|
+
...options.codexThreadReads && { threadReads: options.codexThreadReads },
|
|
92534
93641
|
onFileDiagnostic: files.observe("codex")
|
|
92535
93642
|
});
|
|
92536
93643
|
diagnostic.codex = {
|
|
@@ -92619,12 +93726,8 @@ async function runSyncPassInternal(config2, options, diagnostic) {
|
|
|
92619
93726
|
credentials.token,
|
|
92620
93727
|
items
|
|
92621
93728
|
);
|
|
92622
|
-
const foreignKeys = new Set(
|
|
92623
|
-
|
|
92624
|
-
);
|
|
92625
|
-
const writable = collected.filter(
|
|
92626
|
-
({ item }) => !foreignKeys.has(JSON.stringify([item.harness, item.harnessSessionId]))
|
|
92627
|
-
);
|
|
93729
|
+
const foreignKeys = new Set(foreignOwned.map(sessionKey));
|
|
93730
|
+
const writable = collected.filter(({ item }) => !foreignKeys.has(sessionKey(item)));
|
|
92628
93731
|
for (const { harness, harnessSessionId } of foreignOwned) {
|
|
92629
93732
|
log3(`Skipped ${harness} session ${harnessSessionId}: already synced from another machine.`);
|
|
92630
93733
|
}
|
|
@@ -92679,17 +93782,31 @@ async function runSyncPassInternal(config2, options, diagnostic) {
|
|
|
92679
93782
|
const rawWindowSkipped = writable.length - rawEligible.length;
|
|
92680
93783
|
diagnostic.rawWindowSkipped = rawWindowSkipped;
|
|
92681
93784
|
diagnostic.stage = "raw";
|
|
93785
|
+
const besideBackgroundSync = options.rawShipped === void 0 && (options.backgroundSyncIsRunning ?? (() => backgroundSyncIsRunning(config2.appEnv)))();
|
|
92682
93786
|
const raw = await uploadRawSessions(
|
|
92683
93787
|
credentials.apiUrl,
|
|
92684
93788
|
credentials.token,
|
|
92685
93789
|
rawEligible,
|
|
92686
|
-
|
|
93790
|
+
besideBackgroundSync ? readUnchangedTranscript : readCollectedTranscript,
|
|
92687
93791
|
options.rawShipped,
|
|
92688
93792
|
{
|
|
92689
93793
|
beforeUpload: options.beforeRawUpload ? () => options.beforeRawUpload(collected) : void 0,
|
|
92690
93794
|
onUploaded: options.onRawUploaded
|
|
92691
93795
|
}
|
|
92692
93796
|
);
|
|
93797
|
+
const givenUp = options.codexThreadLinks?.settleRecoveries((session) => {
|
|
93798
|
+
const key = sessionKey(session.item);
|
|
93799
|
+
if (raw.unconfigured || foreignKeys.has(key) || rawFloor !== null && session.mtimeMs < rawFloor) return "refused";
|
|
93800
|
+
if (options.rawShipped !== void 0 && isRawShipped(options.rawShipped, session)) return "shipped";
|
|
93801
|
+
const outcome = raw.outcomes.get(key);
|
|
93802
|
+
if (outcome === void 0) return "waiting";
|
|
93803
|
+
return outcome === "missing-metadata" ? "refused" : "failed";
|
|
93804
|
+
}) ?? [];
|
|
93805
|
+
for (const threadId2 of givenUp) {
|
|
93806
|
+
log3(
|
|
93807
|
+
`\u26A0 Codex thread ${threadId2} is still owed a corrected capture after ${CORRECTION_UPLOAD_ATTEMPTS} failed uploads; it is sent again when it changes.`
|
|
93808
|
+
);
|
|
93809
|
+
}
|
|
92693
93810
|
diagnostic.rawUnconfigured = raw.unconfigured;
|
|
92694
93811
|
if (!raw.unconfigured) {
|
|
92695
93812
|
diagnostic.rawUploaded = raw.uploaded;
|
|
@@ -92706,6 +93823,11 @@ async function runSyncPassInternal(config2, options, diagnostic) {
|
|
|
92706
93823
|
if (rawWindowSkipped > 0) parts.push(`${rawWindowSkipped} outside the dev raw window`);
|
|
92707
93824
|
if (raw.skippedTooLarge > 0) parts.push(`${raw.skippedTooLarge} skipped (too large)`);
|
|
92708
93825
|
if (raw.skippedSubagentFiles > 0) parts.push(`${raw.skippedSubagentFiles} subagent file(s) left out`);
|
|
93826
|
+
if (raw.changedSinceRead > 0) {
|
|
93827
|
+
parts.push(
|
|
93828
|
+
besideBackgroundSync ? `${raw.changedSinceRead} changed while syncing, left for the background sync` : `${raw.changedSinceRead} changed while syncing, next pass`
|
|
93829
|
+
);
|
|
93830
|
+
}
|
|
92709
93831
|
if (raw.failed.length > 0) parts.push(`${raw.failed.length} failed`);
|
|
92710
93832
|
log3(`Raw content: ${parts.join(", ")}.`);
|
|
92711
93833
|
for (const failure2 of raw.failed) log3(` raw upload failed, ${failure2}`);
|
|
@@ -92714,7 +93836,7 @@ async function runSyncPassInternal(config2, options, diagnostic) {
|
|
|
92714
93836
|
`${raw.missingMetadata} raw transcript(s) waiting on their session metadata, they re-send once it lands.`
|
|
92715
93837
|
);
|
|
92716
93838
|
}
|
|
92717
|
-
const nothingElseAccountedFor = raw.skippedUnchanged === 0 && raw.skippedTooLarge === 0 && rawWindowSkipped === 0 && raw.missingMetadata === 0;
|
|
93839
|
+
const nothingElseAccountedFor = raw.skippedUnchanged === 0 && raw.skippedTooLarge === 0 && rawWindowSkipped === 0 && raw.missingMetadata === 0 && raw.changedSinceRead === 0;
|
|
92718
93840
|
if (raw.uploaded === 0 && (raw.failed.length > 1 || raw.failed.length === 1 && nothingElseAccountedFor)) {
|
|
92719
93841
|
throw new Error(`all ${raw.failed.length} raw upload(s) failed this pass, ${raw.failed[0]}`);
|
|
92720
93842
|
}
|
|
@@ -92850,6 +93972,9 @@ function defaultStat(file2) {
|
|
|
92850
93972
|
}
|
|
92851
93973
|
}
|
|
92852
93974
|
|
|
93975
|
+
// ../../apps/syncservice/src/sessionStatusFeed.ts
|
|
93976
|
+
import { statSync as statSync5 } from "node:fs";
|
|
93977
|
+
|
|
92853
93978
|
// ../../apps/syncservice/src/sessionStatusModel.ts
|
|
92854
93979
|
function createSessionStatusModel(now = Date.now) {
|
|
92855
93980
|
const sessions = /* @__PURE__ */ new Map();
|
|
@@ -92897,6 +94022,7 @@ function createSessionStatusModel(now = Date.now) {
|
|
|
92897
94022
|
}
|
|
92898
94023
|
|
|
92899
94024
|
// ../../apps/syncservice/src/sessionStatusFeed.ts
|
|
94025
|
+
var TRANSCRIPT_WRITE_DELAY_MS = 500;
|
|
92900
94026
|
var HELD_HOOK_ENTRIES_MAX = 500;
|
|
92901
94027
|
var HOOK_ID_PAIRS_MAX = 1e4;
|
|
92902
94028
|
var MAX_TIMER_MS = 2 ** 31 - 1;
|
|
@@ -92904,7 +94030,8 @@ function createSessionStatusFeed(deps) {
|
|
|
92904
94030
|
const now = deps.now ?? Date.now;
|
|
92905
94031
|
const adapters = deps.adapters ?? HARNESS_ADAPTERS;
|
|
92906
94032
|
const model = createSessionStatusModel(now);
|
|
92907
|
-
const
|
|
94033
|
+
const adapterOf = (harness) => adapters.find((adapter) => adapter.harness === harness);
|
|
94034
|
+
const hookIdOf = (harness) => adapterOf(harness)?.hookSessionId;
|
|
92908
94035
|
const pairKey = (harness, hookId) => `${harness}:${hookId.toLowerCase()}`;
|
|
92909
94036
|
const storedIds = /* @__PURE__ */ new Map();
|
|
92910
94037
|
let held = [];
|
|
@@ -92912,6 +94039,8 @@ function createSessionStatusFeed(deps) {
|
|
|
92912
94039
|
let deadlineTimer = null;
|
|
92913
94040
|
let readQueued = false;
|
|
92914
94041
|
let readFailing = false;
|
|
94042
|
+
let statFailing = false;
|
|
94043
|
+
const writeTimers = /* @__PURE__ */ new Map();
|
|
92915
94044
|
let stopped = false;
|
|
92916
94045
|
function stored(entry) {
|
|
92917
94046
|
if (hookIdOf(entry.harness) === void 0) return entry;
|
|
@@ -92927,6 +94056,61 @@ function createSessionStatusFeed(deps) {
|
|
|
92927
94056
|
}
|
|
92928
94057
|
if (model.applyLifecycleEntry(mapped) !== null) changedInBatch = true;
|
|
92929
94058
|
}
|
|
94059
|
+
function learnPair(harness, externalId) {
|
|
94060
|
+
const hookId = hookIdOf(harness)?.(externalId);
|
|
94061
|
+
if (hookId === void 0) return false;
|
|
94062
|
+
const key = pairKey(harness, hookId);
|
|
94063
|
+
const known = storedIds.get(key);
|
|
94064
|
+
storedIds.delete(key);
|
|
94065
|
+
storedIds.set(key, externalId);
|
|
94066
|
+
if (storedIds.size > HOOK_ID_PAIRS_MAX) storedIds.delete(storedIds.keys().next().value);
|
|
94067
|
+
return known !== externalId;
|
|
94068
|
+
}
|
|
94069
|
+
function releaseHeld() {
|
|
94070
|
+
const waiting = held;
|
|
94071
|
+
held = [];
|
|
94072
|
+
for (const entry of waiting) apply(entry);
|
|
94073
|
+
}
|
|
94074
|
+
function applyWrite(harness, sessionId, atMs) {
|
|
94075
|
+
if (model.applyTranscriptWrite({ harness, sessionId, atMs }) !== null) changedInBatch = true;
|
|
94076
|
+
}
|
|
94077
|
+
function applyWatchedWrite(harness, transcriptPath) {
|
|
94078
|
+
const session = adapterOf(harness)?.transcriptSession?.(transcriptPath);
|
|
94079
|
+
if (session === void 0) return;
|
|
94080
|
+
const externalId = "externalId" in session ? session.externalId : storedIds.get(pairKey(harness, session.hookId));
|
|
94081
|
+
if (externalId === void 0) return;
|
|
94082
|
+
const mtimeMs = modifiedMs(transcriptPath);
|
|
94083
|
+
if (mtimeMs === null) return;
|
|
94084
|
+
const learned = "externalId" in session && learnPair(harness, externalId);
|
|
94085
|
+
applyWrite(harness, externalId, mtimeMs);
|
|
94086
|
+
if (learned) releaseHeld();
|
|
94087
|
+
}
|
|
94088
|
+
function modifiedMs(transcriptPath) {
|
|
94089
|
+
try {
|
|
94090
|
+
const mtimeMs = statSync5(transcriptPath).mtimeMs;
|
|
94091
|
+
if (statFailing) deps.log("transcript files readable again");
|
|
94092
|
+
statFailing = false;
|
|
94093
|
+
return mtimeMs;
|
|
94094
|
+
} catch (error63) {
|
|
94095
|
+
if (errorCode(error63) === "ENOENT") return null;
|
|
94096
|
+
if (!statFailing) {
|
|
94097
|
+
deps.log(`\u26A0 transcript file not read (${errorCode(error63)}), its status waits for the next sync pass`);
|
|
94098
|
+
}
|
|
94099
|
+
statFailing = true;
|
|
94100
|
+
return null;
|
|
94101
|
+
}
|
|
94102
|
+
}
|
|
94103
|
+
function noteWrite(harness, transcriptPath) {
|
|
94104
|
+
if (writeTimers.has(transcriptPath)) return;
|
|
94105
|
+
const timer = setTimeout(() => {
|
|
94106
|
+
writeTimers.delete(transcriptPath);
|
|
94107
|
+
if (stopped) return;
|
|
94108
|
+
applyWatchedWrite(harness, transcriptPath);
|
|
94109
|
+
settle2();
|
|
94110
|
+
}, TRANSCRIPT_WRITE_DELAY_MS);
|
|
94111
|
+
timer.unref?.();
|
|
94112
|
+
writeTimers.set(transcriptPath, timer);
|
|
94113
|
+
}
|
|
92930
94114
|
const journal = createLifecycleJournalReader(deps.home, void 0, apply);
|
|
92931
94115
|
function settle2() {
|
|
92932
94116
|
armDeadline();
|
|
@@ -92963,6 +94147,7 @@ function createSessionStatusFeed(deps) {
|
|
|
92963
94147
|
return {
|
|
92964
94148
|
journal,
|
|
92965
94149
|
forwardWatchEvents: (onEvent) => (change) => {
|
|
94150
|
+
if (typeof change === "object" && !stopped) noteWrite(change.harness, change.transcript);
|
|
92966
94151
|
if (change === "lifecycle" && !stopped && !readQueued) {
|
|
92967
94152
|
readQueued = true;
|
|
92968
94153
|
queueMicrotask(() => {
|
|
@@ -92974,19 +94159,10 @@ function createSessionStatusFeed(deps) {
|
|
|
92974
94159
|
},
|
|
92975
94160
|
observeSessions(sessions) {
|
|
92976
94161
|
for (const { item, mtimeMs } of sessions) {
|
|
92977
|
-
|
|
92978
|
-
|
|
92979
|
-
|
|
92980
|
-
|
|
92981
|
-
storedIds.set(key, item.harnessSessionId);
|
|
92982
|
-
if (storedIds.size > HOOK_ID_PAIRS_MAX) storedIds.delete(storedIds.keys().next().value);
|
|
92983
|
-
}
|
|
92984
|
-
const change = model.applyTranscriptWrite({ harness: item.harness, sessionId: item.harnessSessionId, atMs: mtimeMs });
|
|
92985
|
-
if (change !== null) changedInBatch = true;
|
|
92986
|
-
}
|
|
92987
|
-
const waiting = held;
|
|
92988
|
-
held = [];
|
|
92989
|
-
for (const entry of waiting) apply(entry);
|
|
94162
|
+
learnPair(item.harness, item.harnessSessionId);
|
|
94163
|
+
applyWrite(item.harness, item.harnessSessionId, mtimeMs);
|
|
94164
|
+
}
|
|
94165
|
+
releaseHeld();
|
|
92990
94166
|
settle2();
|
|
92991
94167
|
},
|
|
92992
94168
|
deleteExpired() {
|
|
@@ -92999,6 +94175,8 @@ function createSessionStatusFeed(deps) {
|
|
|
92999
94175
|
stop() {
|
|
93000
94176
|
stopped = true;
|
|
93001
94177
|
if (deadlineTimer) clearTimeout(deadlineTimer);
|
|
94178
|
+
for (const timer of writeTimers.values()) clearTimeout(timer);
|
|
94179
|
+
writeTimers.clear();
|
|
93002
94180
|
}
|
|
93003
94181
|
};
|
|
93004
94182
|
}
|
|
@@ -93043,14 +94221,20 @@ function createWatchLoop(deps) {
|
|
|
93043
94221
|
const pendingCompletions = /* @__PURE__ */ new Map();
|
|
93044
94222
|
let lifecycleChanged = false;
|
|
93045
94223
|
const knownTranscripts = /* @__PURE__ */ new Set();
|
|
93046
|
-
const
|
|
93047
|
-
|
|
93048
|
-
|
|
93049
|
-
|
|
93050
|
-
|
|
93051
|
-
|
|
94224
|
+
const knownSessions = /* @__PURE__ */ new Set();
|
|
94225
|
+
const remember = (known, value) => {
|
|
94226
|
+
known.delete(value);
|
|
94227
|
+
known.add(value);
|
|
94228
|
+
if (known.size > KNOWN_TRANSCRIPTS_MAX) {
|
|
94229
|
+
const [oldest] = known;
|
|
94230
|
+
if (oldest !== void 0) known.delete(oldest);
|
|
93052
94231
|
}
|
|
93053
94232
|
};
|
|
94233
|
+
const rememberTranscript = (transcript) => remember(knownTranscripts, transcript);
|
|
94234
|
+
const rememberSession = (session) => {
|
|
94235
|
+
rememberTranscript(session.transcriptPath);
|
|
94236
|
+
remember(knownSessions, sessionKey(session.item));
|
|
94237
|
+
};
|
|
93054
94238
|
const newTranscripts = /* @__PURE__ */ new Map();
|
|
93055
94239
|
let newSessionTimer = null;
|
|
93056
94240
|
let newSessionPassDue = false;
|
|
@@ -93093,13 +94277,13 @@ function createWatchLoop(deps) {
|
|
|
93093
94277
|
running = true;
|
|
93094
94278
|
deps.log(`${transcripts.length} new transcript file(s), uploading new session records now`);
|
|
93095
94279
|
const startedAt = now();
|
|
93096
|
-
const known = new Set(
|
|
94280
|
+
const known = new Set(knownSessions);
|
|
93097
94281
|
try {
|
|
93098
94282
|
const summary = await runNewSessionPass({
|
|
93099
94283
|
sinceMs: Math.min(...transcripts.map(([, firstWriteAt]) => firstWriteAt)) - NARROW_OVERLAP_MS,
|
|
93100
|
-
isNew: (
|
|
94284
|
+
isNew: (session) => !known.has(sessionKey(session.item))
|
|
93101
94285
|
});
|
|
93102
|
-
for (const session of summary.sessions)
|
|
94286
|
+
for (const session of summary.sessions) rememberSession(session);
|
|
93103
94287
|
if (summary.upserted > 0) deps.onNewSessionsUploaded?.(now());
|
|
93104
94288
|
} catch (error63) {
|
|
93105
94289
|
const holdMs = Math.max(FAILED_PASS_RETRY_MS, retryAfterMs(error63) ?? 0);
|
|
@@ -93179,15 +94363,15 @@ function createWatchLoop(deps) {
|
|
|
93179
94363
|
if (narrowSinceMs === void 0) startupOwed = false;
|
|
93180
94364
|
for (const [key] of due) pendingCompletions.delete(key);
|
|
93181
94365
|
for (const session of summary.sessions) {
|
|
93182
|
-
|
|
94366
|
+
rememberSession(session);
|
|
93183
94367
|
newTranscripts.delete(session.transcriptPath);
|
|
93184
94368
|
if (session.item.status === "active") {
|
|
93185
|
-
pendingCompletions.set(session.
|
|
94369
|
+
pendingCompletions.set(sessionKey(session.item), {
|
|
93186
94370
|
mtimeMs: session.mtimeMs,
|
|
93187
94371
|
dueAt: session.mtimeMs + ACTIVE_GRACE_MS + COMPLETION_SLACK_MS
|
|
93188
94372
|
});
|
|
93189
94373
|
} else {
|
|
93190
|
-
pendingCompletions.delete(session.
|
|
94374
|
+
pendingCompletions.delete(sessionKey(session.item));
|
|
93191
94375
|
}
|
|
93192
94376
|
}
|
|
93193
94377
|
consecutiveFailures = 0;
|
|
@@ -93280,8 +94464,16 @@ function createWatchLoop(deps) {
|
|
|
93280
94464
|
function sessionStoreWatchRoots(home) {
|
|
93281
94465
|
return [
|
|
93282
94466
|
// <project folder>/<session id>.jsonl. Subagent files are one level deeper.
|
|
93283
|
-
{
|
|
93284
|
-
|
|
94467
|
+
{
|
|
94468
|
+
dir: path21.join(home, ".claude", "projects"),
|
|
94469
|
+
recursive: true,
|
|
94470
|
+
transcripts: { harness: "claude-local", path: /^[^/\\]+[/\\][^/\\]+\.jsonl$/ }
|
|
94471
|
+
},
|
|
94472
|
+
{
|
|
94473
|
+
dir: path21.join(home, ".codex", "sessions"),
|
|
94474
|
+
recursive: true,
|
|
94475
|
+
transcripts: { harness: "codex-local", path: /(?:^|[/\\])rollout-[^/\\]+\.jsonl$/ }
|
|
94476
|
+
},
|
|
93285
94477
|
{ dir: path21.join(home, ".codex", "archived_sessions"), recursive: true },
|
|
93286
94478
|
// Cursor keeps every local session in one SQLite store here. Its WAL
|
|
93287
94479
|
// file changes on session writes, so a non-recursive watch on the
|
|
@@ -93294,7 +94486,10 @@ function sessionStoreWatchRoots(home) {
|
|
|
93294
94486
|
dir: path21.join(home, ".cursor", "projects"),
|
|
93295
94487
|
recursive: true,
|
|
93296
94488
|
cursorTranscripts: true,
|
|
93297
|
-
transcripts:
|
|
94489
|
+
transcripts: {
|
|
94490
|
+
harness: "cursor-local",
|
|
94491
|
+
path: /(?:^|[/\\])agent-transcripts[/\\](?:([^/\\]+)[/\\]\1|[^/\\]+)\.jsonl$/
|
|
94492
|
+
}
|
|
93298
94493
|
}
|
|
93299
94494
|
];
|
|
93300
94495
|
}
|
|
@@ -93314,7 +94509,9 @@ function lifecycleWatchRoots(home) {
|
|
|
93314
94509
|
function watchEventChange(root, filename) {
|
|
93315
94510
|
const name = filename?.toString();
|
|
93316
94511
|
if (root.cursorTranscripts && name !== void 0 && !/(?:^|[/\\])agent-transcripts[/\\].*\.jsonl$/.test(name)) return "ignore";
|
|
93317
|
-
if (name !== void 0 && root.transcripts?.test(name))
|
|
94512
|
+
if (name !== void 0 && root.transcripts?.path.test(name)) {
|
|
94513
|
+
return { transcript: path21.join(root.dir, name), harness: root.transcripts.harness };
|
|
94514
|
+
}
|
|
93318
94515
|
if (!root.lifecycle) return void 0;
|
|
93319
94516
|
return name === void 0 || isLifecycleJournalFile(name) || name === SESSION_CHANGE_FILENAME ? "lifecycle" : void 0;
|
|
93320
94517
|
}
|
|
@@ -93334,11 +94531,13 @@ function watchSessionStores(roots, onEvent, onLost) {
|
|
|
93334
94531
|
function newSessionSyncOptions(shared, sinceMs, isNew) {
|
|
93335
94532
|
return {
|
|
93336
94533
|
lifecycleJournal: shared.lifecycleJournal,
|
|
94534
|
+
codexThreadLinks: shared.codexThreadLinks,
|
|
94535
|
+
codexThreadReads: shared.codexThreadReads,
|
|
93337
94536
|
log: shared.log,
|
|
93338
94537
|
enrollCommand: shared.enrollCommand,
|
|
93339
94538
|
explicitSince: String(STEADY_SYNC_WINDOW_DAYS),
|
|
93340
94539
|
narrowSinceMs: sinceMs,
|
|
93341
|
-
isNewSession:
|
|
94540
|
+
isNewSession: isNew
|
|
93342
94541
|
};
|
|
93343
94542
|
}
|
|
93344
94543
|
function startWatch(config2, homeDir, runtime = { version: "checkout" }) {
|
|
@@ -93533,15 +94732,19 @@ function startWatch(config2, homeDir, runtime = { version: "checkout" }) {
|
|
|
93533
94732
|
});
|
|
93534
94733
|
const rawShipped = /* @__PURE__ */ new Map();
|
|
93535
94734
|
const previewRegistrations = /* @__PURE__ */ new Map();
|
|
94735
|
+
const codexThreadLinks = new CodexThreadLinks();
|
|
94736
|
+
const codexThreadReads = new CodexThreadReads();
|
|
93536
94737
|
const loop = createWatchLoop({
|
|
93537
94738
|
runPass: async (options) => {
|
|
93538
94739
|
let uploaded = 0;
|
|
93539
94740
|
let activeSinceMs = Infinity;
|
|
93540
|
-
const
|
|
94741
|
+
const activeSessions = /* @__PURE__ */ new Set();
|
|
93541
94742
|
let lastCatchupReportAt = -Infinity;
|
|
93542
94743
|
const shared = {
|
|
93543
94744
|
rawShipped,
|
|
93544
94745
|
previewRegistrations,
|
|
94746
|
+
codexThreadLinks,
|
|
94747
|
+
codexThreadReads,
|
|
93545
94748
|
lifecycleJournal: journal,
|
|
93546
94749
|
log: log3,
|
|
93547
94750
|
enrollCommand: runtime.enrollCommand,
|
|
@@ -93558,11 +94761,11 @@ function startWatch(config2, homeDir, runtime = { version: "checkout" }) {
|
|
|
93558
94761
|
const recent = await runSyncPass(config2, recentSyncOptions(
|
|
93559
94762
|
shared,
|
|
93560
94763
|
Math.min(narrowSinceMs, activeSinceMs),
|
|
93561
|
-
{ sinceMs: narrowSinceMs,
|
|
94764
|
+
{ sinceMs: narrowSinceMs, sessions: activeSessions }
|
|
93562
94765
|
));
|
|
93563
94766
|
activeSinceMs = recentCatchupFloor(activeSinceMs, recent.sessions);
|
|
93564
94767
|
for (const session of recent.sessions) {
|
|
93565
|
-
if (session.item.status === "active")
|
|
94768
|
+
if (session.item.status === "active") activeSessions.add(sessionKey(session.item));
|
|
93566
94769
|
}
|
|
93567
94770
|
return recent;
|
|
93568
94771
|
});
|
|
@@ -93572,7 +94775,7 @@ function startWatch(config2, homeDir, runtime = { version: "checkout" }) {
|
|
|
93572
94775
|
beforeRawUpload: async (sessions) => {
|
|
93573
94776
|
activeSinceMs = recentCatchupFloor(activeSinceMs, sessions);
|
|
93574
94777
|
for (const session of sessions) {
|
|
93575
|
-
if (session.item.status === "active")
|
|
94778
|
+
if (session.item.status === "active") activeSessions.add(sessionKey(session.item));
|
|
93576
94779
|
}
|
|
93577
94780
|
const beforeCatchup = uploaded;
|
|
93578
94781
|
try {
|
|
@@ -93595,7 +94798,7 @@ function startWatch(config2, homeDir, runtime = { version: "checkout" }) {
|
|
|
93595
94798
|
}
|
|
93596
94799
|
return summary;
|
|
93597
94800
|
},
|
|
93598
|
-
runNewSessionPass: ({ sinceMs, isNew }) => runSyncPass(config2, newSessionSyncOptions({ lifecycleJournal: journal, log: log3, enrollCommand: runtime.enrollCommand }, sinceMs, isNew)),
|
|
94801
|
+
runNewSessionPass: ({ sinceMs, isNew }) => runSyncPass(config2, newSessionSyncOptions({ lifecycleJournal: journal, codexThreadLinks, codexThreadReads, log: log3, enrollCommand: runtime.enrollCommand }, sinceMs, isNew)),
|
|
93599
94802
|
// An upload changes what the menu shows, so this write wakes the menu,
|
|
93600
94803
|
// and the menu reads the new session from the server.
|
|
93601
94804
|
onNewSessionsUploaded: (at) => writeStatus({ lastUploadAt: new Date(at).toISOString() }),
|
|
@@ -93865,7 +95068,7 @@ function hookPreferencesPath(appEnv, home) {
|
|
|
93865
95068
|
function readHookPreferences(appEnv, home) {
|
|
93866
95069
|
const file2 = hookPreferencesPath(appEnv, home);
|
|
93867
95070
|
try {
|
|
93868
|
-
return preferencesSchema.parse(JSON.parse(
|
|
95071
|
+
return preferencesSchema.parse(JSON.parse(readFileSync13(file2, "utf8")));
|
|
93869
95072
|
} catch (error63) {
|
|
93870
95073
|
if (error63 instanceof Error && "code" in error63 && error63.code === "ENOENT") {
|
|
93871
95074
|
return { version: 1, codexNotFiringDismissed: false };
|
|
@@ -93887,12 +95090,12 @@ function dismissCodexHookWarning(appEnv, home) {
|
|
|
93887
95090
|
const file2 = hookPreferencesPath(appEnv, home);
|
|
93888
95091
|
mkdirSync12(path24.dirname(file2), { recursive: true });
|
|
93889
95092
|
const temporary = `${file2}.${process.pid}.tmp`;
|
|
93890
|
-
|
|
93891
|
-
|
|
95093
|
+
writeFileSync9(temporary, JSON.stringify({ ...preferences, codexNotFiringDismissed: true }, null, 2), { mode: 384 });
|
|
95094
|
+
renameSync7(temporary, file2);
|
|
93892
95095
|
}
|
|
93893
95096
|
|
|
93894
95097
|
// src/mcpPreferences.ts
|
|
93895
|
-
import { mkdirSync as mkdirSync13, readFileSync as
|
|
95098
|
+
import { mkdirSync as mkdirSync13, readFileSync as readFileSync14, renameSync as renameSync8, writeFileSync as writeFileSync10 } from "node:fs";
|
|
93896
95099
|
import path25 from "node:path";
|
|
93897
95100
|
var preferenceSchema = external_exports.object({
|
|
93898
95101
|
optedIn: external_exports.boolean().optional(),
|
|
@@ -93913,7 +95116,7 @@ function mcpPreferencesPath(appEnv, home) {
|
|
|
93913
95116
|
function readMcpPreferences(appEnv, home) {
|
|
93914
95117
|
const file2 = mcpPreferencesPath(appEnv, home);
|
|
93915
95118
|
try {
|
|
93916
|
-
return preferencesSchema2.parse(JSON.parse(
|
|
95119
|
+
return preferencesSchema2.parse(JSON.parse(readFileSync14(file2, "utf8")));
|
|
93917
95120
|
} catch (error63) {
|
|
93918
95121
|
if (error63 instanceof Error && "code" in error63 && error63.code === "ENOENT") return emptyMcpPreferences();
|
|
93919
95122
|
throw new ExpectedCliError(`Cannot read MCP preferences at ${file2}. Restore or remove that file, then retry.`, { cause: error63 });
|
|
@@ -93923,8 +95126,8 @@ function writeMcpPreferences(appEnv, preferences, home) {
|
|
|
93923
95126
|
const file2 = mcpPreferencesPath(appEnv, home);
|
|
93924
95127
|
mkdirSync13(path25.dirname(file2), { recursive: true });
|
|
93925
95128
|
const temporary = `${file2}.${process.pid}.tmp`;
|
|
93926
|
-
|
|
93927
|
-
|
|
95129
|
+
writeFileSync10(temporary, JSON.stringify(preferencesSchema2.parse(preferences), null, 2), { mode: 384 });
|
|
95130
|
+
renameSync8(temporary, file2);
|
|
93928
95131
|
}
|
|
93929
95132
|
function recordMcpChoices(appEnv, offered, selected, home) {
|
|
93930
95133
|
const preferences = readMcpPreferences(appEnv, home);
|
|
@@ -94087,7 +95290,7 @@ function sessionLinks(session, webUrl, options) {
|
|
|
94087
95290
|
}
|
|
94088
95291
|
};
|
|
94089
95292
|
case "codex": {
|
|
94090
|
-
const threadId2 =
|
|
95293
|
+
const threadId2 = codexThreadId2(session.externalId);
|
|
94091
95294
|
if (threadId2 === null || !options.localMachine) return { powerplantUrl };
|
|
94092
95295
|
return {
|
|
94093
95296
|
powerplantUrl,
|
|
@@ -94104,7 +95307,7 @@ function sessionLinks(session, webUrl, options) {
|
|
|
94104
95307
|
}
|
|
94105
95308
|
}
|
|
94106
95309
|
var TRAILING_UUID = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i;
|
|
94107
|
-
function
|
|
95310
|
+
function codexThreadId2(externalId) {
|
|
94108
95311
|
return TRAILING_UUID.exec(externalId)?.[1] ?? null;
|
|
94109
95312
|
}
|
|
94110
95313
|
var CODEX_APP_BUNDLES = ["ChatGPT.app", "Codex.app"];
|
|
@@ -94254,7 +95457,7 @@ async function probeClaudeDesktopSessions(wanted, installed, reportFault, root =
|
|
|
94254
95457
|
|
|
94255
95458
|
// src/menuModel.ts
|
|
94256
95459
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
94257
|
-
import { mkdirSync as mkdirSync17, rmSync as
|
|
95460
|
+
import { mkdirSync as mkdirSync17, rmSync as rmSync10, watch as fsWatch3 } from "node:fs";
|
|
94258
95461
|
import path34 from "node:path";
|
|
94259
95462
|
|
|
94260
95463
|
// src/menu.ts
|
|
@@ -94276,15 +95479,15 @@ function ago(ms) {
|
|
|
94276
95479
|
}
|
|
94277
95480
|
|
|
94278
95481
|
// src/update.ts
|
|
94279
|
-
import { mkdirSync as mkdirSync14, readFileSync as
|
|
95482
|
+
import { mkdirSync as mkdirSync14, readFileSync as readFileSync16, rmSync as rmSync8, writeFileSync as writeFileSync11 } from "node:fs";
|
|
94280
95483
|
import path29 from "node:path";
|
|
94281
95484
|
|
|
94282
95485
|
// src/installOwner.ts
|
|
94283
|
-
import { accessSync, constants as constants2, readFileSync as
|
|
95486
|
+
import { accessSync, constants as constants2, readFileSync as readFileSync15 } from "node:fs";
|
|
94284
95487
|
import path28 from "node:path";
|
|
94285
95488
|
|
|
94286
95489
|
// src/version.ts
|
|
94287
|
-
var CLI_VERSION = true ? "0.4.
|
|
95490
|
+
var CLI_VERSION = true ? "0.4.20" : "0.0.0-checkout";
|
|
94288
95491
|
var CLI_RELEASE_BUILD = true ? true : false;
|
|
94289
95492
|
var CLI_PACKAGE_NAME = "@usepowerplant/cli";
|
|
94290
95493
|
|
|
@@ -94385,7 +95588,7 @@ function realInspectEnvironment() {
|
|
|
94385
95588
|
pnpmHome: process.env.PNPM_HOME,
|
|
94386
95589
|
readTextFile: (file2) => {
|
|
94387
95590
|
try {
|
|
94388
|
-
return
|
|
95591
|
+
return readFileSync15(file2, "utf8");
|
|
94389
95592
|
} catch (error63) {
|
|
94390
95593
|
if (error63 instanceof Error && "code" in error63 && error63.code === "ENOENT") return null;
|
|
94391
95594
|
throw error63;
|
|
@@ -94451,18 +95654,18 @@ function updateProgressFile(report, homeDir) {
|
|
|
94451
95654
|
return {
|
|
94452
95655
|
read: () => readUpdateProgress((filePath) => {
|
|
94453
95656
|
try {
|
|
94454
|
-
return
|
|
95657
|
+
return readFileSync16(filePath, "utf8");
|
|
94455
95658
|
} catch {
|
|
94456
95659
|
return null;
|
|
94457
95660
|
}
|
|
94458
95661
|
}, file2),
|
|
94459
95662
|
write: (record2) => {
|
|
94460
95663
|
mkdirSync14(path29.dirname(file2), { recursive: true });
|
|
94461
|
-
|
|
95664
|
+
writeFileSync11(file2, JSON.stringify(record2, null, 2));
|
|
94462
95665
|
wakeMenus2();
|
|
94463
95666
|
},
|
|
94464
95667
|
clear: () => {
|
|
94465
|
-
|
|
95668
|
+
rmSync8(file2, { force: true });
|
|
94466
95669
|
wakeMenus2();
|
|
94467
95670
|
}
|
|
94468
95671
|
};
|
|
@@ -95231,7 +96434,7 @@ function writeVersionCache(options) {
|
|
|
95231
96434
|
}
|
|
95232
96435
|
|
|
95233
96436
|
// src/menuSnapshot.ts
|
|
95234
|
-
import { chmodSync as
|
|
96437
|
+
import { chmodSync as chmodSync4, mkdirSync as mkdirSync15, readFileSync as readFileSync17, renameSync as renameSync9, rmSync as rmSync9, statSync as statSync6, writeFileSync as writeFileSync12 } from "node:fs";
|
|
95235
96438
|
import path31 from "node:path";
|
|
95236
96439
|
var MENU_SNAPSHOT_VERSION = 1;
|
|
95237
96440
|
var MENU_SNAPSHOT_STALE_AFTER_MS = 3 * 6e4;
|
|
@@ -95276,19 +96479,19 @@ var snapshotFileSchema = external_exports.object({
|
|
|
95276
96479
|
function writeMenuSnapshot(file2, snapshot) {
|
|
95277
96480
|
const dir = path31.dirname(file2);
|
|
95278
96481
|
mkdirSync15(dir, { recursive: true, mode: 448 });
|
|
95279
|
-
|
|
96482
|
+
chmodSync4(dir, 448);
|
|
95280
96483
|
const temporary = `${file2}.${process.pid}.tmp`;
|
|
95281
96484
|
try {
|
|
95282
|
-
|
|
95283
|
-
|
|
96485
|
+
writeFileSync12(temporary, encodeMenuPayload(snapshot), { mode: 384 });
|
|
96486
|
+
renameSync9(temporary, file2);
|
|
95284
96487
|
} finally {
|
|
95285
|
-
|
|
96488
|
+
rmSync9(temporary, { force: true });
|
|
95286
96489
|
}
|
|
95287
96490
|
}
|
|
95288
96491
|
function readMenuSnapshot(options) {
|
|
95289
96492
|
let raw;
|
|
95290
96493
|
try {
|
|
95291
|
-
raw =
|
|
96494
|
+
raw = readFileSync17(options.file, "utf8");
|
|
95292
96495
|
} catch (error63) {
|
|
95293
96496
|
return { outcome: error63 instanceof Error && "code" in error63 && error63.code === "ENOENT" ? "missing" : "unreadable" };
|
|
95294
96497
|
}
|
|
@@ -95328,7 +96531,7 @@ function isPidAlive2(pid) {
|
|
|
95328
96531
|
}
|
|
95329
96532
|
function requestedAfter(file2, writtenAtMs) {
|
|
95330
96533
|
try {
|
|
95331
|
-
return
|
|
96534
|
+
return statSync6(file2).mtimeMs > writtenAtMs;
|
|
95332
96535
|
} catch {
|
|
95333
96536
|
return false;
|
|
95334
96537
|
}
|
|
@@ -95336,7 +96539,7 @@ function requestedAfter(file2, writtenAtMs) {
|
|
|
95336
96539
|
|
|
95337
96540
|
// src/connect.ts
|
|
95338
96541
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
95339
|
-
import { mkdirSync as mkdirSync16, readFileSync as
|
|
96542
|
+
import { mkdirSync as mkdirSync16, readFileSync as readFileSync18, statSync as statSync7, writeFileSync as writeFileSync13 } from "node:fs";
|
|
95340
96543
|
import os23 from "node:os";
|
|
95341
96544
|
import path32 from "node:path";
|
|
95342
96545
|
import { emitKeypressEvents } from "node:readline";
|
|
@@ -95445,7 +96648,7 @@ function unsigned(candidate, verifySignature) {
|
|
|
95445
96648
|
}
|
|
95446
96649
|
function isExecutableFile2(filePath) {
|
|
95447
96650
|
try {
|
|
95448
|
-
const stats =
|
|
96651
|
+
const stats = statSync7(filePath);
|
|
95449
96652
|
return stats.isFile() && (stats.mode & 73) !== 0;
|
|
95450
96653
|
} catch {
|
|
95451
96654
|
return false;
|
|
@@ -96132,14 +97335,14 @@ async function fetchOfficialClients(apiUrl, log3) {
|
|
|
96132
97335
|
}
|
|
96133
97336
|
function realReadTextFile(filePath) {
|
|
96134
97337
|
try {
|
|
96135
|
-
return
|
|
97338
|
+
return readFileSync18(filePath, "utf8");
|
|
96136
97339
|
} catch {
|
|
96137
97340
|
return null;
|
|
96138
97341
|
}
|
|
96139
97342
|
}
|
|
96140
97343
|
function realReadTextFileStrict(filePath) {
|
|
96141
97344
|
try {
|
|
96142
|
-
return
|
|
97345
|
+
return readFileSync18(filePath, "utf8");
|
|
96143
97346
|
} catch (error63) {
|
|
96144
97347
|
if (error63 instanceof Error && error63.code === "ENOENT") return null;
|
|
96145
97348
|
throw error63;
|
|
@@ -96147,7 +97350,7 @@ function realReadTextFileStrict(filePath) {
|
|
|
96147
97350
|
}
|
|
96148
97351
|
function realWriteTextFile(filePath, content) {
|
|
96149
97352
|
mkdirSync16(path32.dirname(filePath), { recursive: true });
|
|
96150
|
-
|
|
97353
|
+
writeFileSync13(filePath, content);
|
|
96151
97354
|
}
|
|
96152
97355
|
function realOpenUrl(url2) {
|
|
96153
97356
|
const bin = process.platform === "darwin" ? "open" : process.platform === "linux" ? "xdg-open" : null;
|
|
@@ -96685,7 +97888,7 @@ function startMenuModel(sources) {
|
|
|
96685
97888
|
})
|
|
96686
97889
|
),
|
|
96687
97890
|
writeSnapshot: (snapshot) => writeMenuSnapshot(snapshotFile, snapshot),
|
|
96688
|
-
removeSnapshot: () =>
|
|
97891
|
+
removeSnapshot: () => rmSync10(snapshotFile, { force: true }),
|
|
96689
97892
|
wake: () => signalMenu(appEnv, host.home),
|
|
96690
97893
|
watchRefreshRequests: (onRequest, onLost) => {
|
|
96691
97894
|
const dir = path34.dirname(menuRefreshRequestPath(appEnv, host.home));
|
|
@@ -96708,7 +97911,7 @@ import path37 from "node:path";
|
|
|
96708
97911
|
import { parseArgs } from "node:util";
|
|
96709
97912
|
|
|
96710
97913
|
// src/doctor.ts
|
|
96711
|
-
import { closeSync as closeSync8, fstatSync as
|
|
97914
|
+
import { closeSync as closeSync8, fstatSync as fstatSync3, openSync as openSync8, readSync as readSync6 } from "node:fs";
|
|
96712
97915
|
var DOCTOR_LOG_TAIL_LINES = 50;
|
|
96713
97916
|
var LOG_TAIL_READ_BYTES = 256 * 1024;
|
|
96714
97917
|
function installLine(install) {
|
|
@@ -96730,7 +97933,7 @@ function readDoctorLogTail(path38) {
|
|
|
96730
97933
|
return { path: path38, lines: null, readError: error63 instanceof Error ? error63.message : String(error63) };
|
|
96731
97934
|
}
|
|
96732
97935
|
try {
|
|
96733
|
-
const size =
|
|
97936
|
+
const size = fstatSync3(fd).size;
|
|
96734
97937
|
const offset = Math.max(0, size - LOG_TAIL_READ_BYTES);
|
|
96735
97938
|
const buffer = Buffer.alloc(Math.min(size, LOG_TAIL_READ_BYTES));
|
|
96736
97939
|
readSync6(fd, buffer, 0, buffer.length, offset);
|
|
@@ -96867,16 +98070,16 @@ var CLI_ENVIRONMENTS = {
|
|
|
96867
98070
|
appEnv: "production",
|
|
96868
98071
|
supabaseUrl: "https://jcwzvorqlsquudjoqbza.supabase.co",
|
|
96869
98072
|
supabasePublishableKey: "sb_publishable_vcGj0Spjl7oyXzrgLHUfBA_8fbpMuWb",
|
|
96870
|
-
apiUrl: "https://api.
|
|
96871
|
-
webUrl: "https://app.
|
|
98073
|
+
apiUrl: "https://api.usepowerplant.com",
|
|
98074
|
+
webUrl: "https://app.usepowerplant.com",
|
|
96872
98075
|
sentryDsn: SHARED_SENTRY_DSN
|
|
96873
98076
|
},
|
|
96874
98077
|
staging: {
|
|
96875
98078
|
appEnv: "staging",
|
|
96876
98079
|
supabaseUrl: "https://zadwpjlzfwqbrvegurfz.supabase.co",
|
|
96877
98080
|
supabasePublishableKey: "sb_publishable_z6Ey6GkxmpPHaNbsu3tZtA_TQoNXZJF",
|
|
96878
|
-
apiUrl: "https://staging.api.
|
|
96879
|
-
webUrl: "https://staging.app.
|
|
98081
|
+
apiUrl: "https://staging.api.usepowerplant.com",
|
|
98082
|
+
webUrl: "https://staging.app.usepowerplant.com",
|
|
96880
98083
|
sentryDsn: SHARED_SENTRY_DSN
|
|
96881
98084
|
},
|
|
96882
98085
|
agent: {
|
|
@@ -97813,7 +99016,7 @@ async function tryCredentialUpgradeLock(file2) {
|
|
|
97813
99016
|
}
|
|
97814
99017
|
|
|
97815
99018
|
// src/menubarApp.ts
|
|
97816
|
-
import { existsSync as existsSync14, mkdirSync as mkdirSync19, renameSync as
|
|
99019
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync19, renameSync as renameSync10, rmSync as rmSync11 } from "node:fs";
|
|
97817
99020
|
import os25 from "node:os";
|
|
97818
99021
|
import path36 from "node:path";
|
|
97819
99022
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
@@ -97883,8 +99086,8 @@ function realMenubarAppOps(input2) {
|
|
|
97883
99086
|
mkdirSync19(parent, { recursive: true });
|
|
97884
99087
|
const staging = path36.join(parent, `.${MENUBAR_APP_NAME}.swap-${process.pid}`);
|
|
97885
99088
|
const retired = path36.join(parent, `.${MENUBAR_APP_NAME}.old-${process.pid}`);
|
|
97886
|
-
|
|
97887
|
-
|
|
99089
|
+
rmSync11(staging, { recursive: true, force: true });
|
|
99090
|
+
rmSync11(retired, { recursive: true, force: true });
|
|
97888
99091
|
try {
|
|
97889
99092
|
const extract = spawnSync4("ditto", ["-xk", zip, staging]);
|
|
97890
99093
|
if (extract.status !== 0) {
|
|
@@ -97897,16 +99100,16 @@ function realMenubarAppOps(input2) {
|
|
|
97897
99100
|
throw new Error(`${zip} does not contain ${MENUBAR_APP_NAME}`);
|
|
97898
99101
|
}
|
|
97899
99102
|
const hadOld = existsSync14(appPath);
|
|
97900
|
-
if (hadOld)
|
|
99103
|
+
if (hadOld) renameSync10(appPath, retired);
|
|
97901
99104
|
try {
|
|
97902
|
-
|
|
99105
|
+
renameSync10(extracted, appPath);
|
|
97903
99106
|
} catch (error63) {
|
|
97904
|
-
if (hadOld)
|
|
99107
|
+
if (hadOld) renameSync10(retired, appPath);
|
|
97905
99108
|
throw error63;
|
|
97906
99109
|
}
|
|
97907
99110
|
} finally {
|
|
97908
|
-
|
|
97909
|
-
|
|
99111
|
+
rmSync11(staging, { recursive: true, force: true });
|
|
99112
|
+
rmSync11(retired, { recursive: true, force: true });
|
|
97910
99113
|
}
|
|
97911
99114
|
},
|
|
97912
99115
|
launch: (appPath) => {
|
|
@@ -98890,4 +100093,4 @@ undici/lib/web/websocket/frame.js:
|
|
|
98890
100093
|
(*! ws. MIT License. Einar Otto Stangvik <einaros@gmail.com> *)
|
|
98891
100094
|
*/
|
|
98892
100095
|
|
|
98893
|
-
//# debugId=
|
|
100096
|
+
//# debugId=9f447c4d-0ccb-5f45-8960-6853e28aadfb
|