iteration-cli 0.18.0 → 0.19.1
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 +9 -2
- package/dist/cli.js +1873 -850
- package/dist/cursor-hook-entry.js +2 -1
- package/dist/launcher.js +2 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -7,9 +7,8 @@ var __export = (target, all) => {
|
|
|
7
7
|
|
|
8
8
|
// src/cli.ts
|
|
9
9
|
import { spawnSync } from "node:child_process";
|
|
10
|
-
import { promises as
|
|
11
|
-
import
|
|
12
|
-
import path31 from "node:path";
|
|
10
|
+
import { promises as fs29 } from "node:fs";
|
|
11
|
+
import path36 from "node:path";
|
|
13
12
|
import { parseArgs } from "node:util";
|
|
14
13
|
|
|
15
14
|
// ../trace-vault/packages/cli/dist/index.js
|
|
@@ -1580,8 +1579,8 @@ function finalizeIssue(iss, ctx, config22) {
|
|
|
1580
1579
|
else
|
|
1581
1580
|
iss.schema = iss.inst;
|
|
1582
1581
|
}
|
|
1583
|
-
const
|
|
1584
|
-
const message = iss.message ? iss.message : unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage(
|
|
1582
|
+
const schemaError22 = iss.schema !== iss.inst ? iss.schema?._zod.def?.error : void 0;
|
|
1583
|
+
const message = iss.message ? iss.message : unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage(schemaError22?.(iss)) ?? unwrapMessage(ctx?.error?.(iss)) ?? unwrapMessage(config22.customError?.(iss)) ?? unwrapMessage(config22.localeError?.(iss)) ?? "Invalid input";
|
|
1585
1584
|
const full = {};
|
|
1586
1585
|
for (const k of Object.keys(iss)) {
|
|
1587
1586
|
if (k === "inst" || k === "schema" || k === "continue" || k === "input" || k === "__proto__")
|
|
@@ -6197,13 +6196,13 @@ var recordProcessor = (schema, ctx, _json, params) => {
|
|
|
6197
6196
|
...params,
|
|
6198
6197
|
path: [...params.path, "propertyNames"]
|
|
6199
6198
|
});
|
|
6200
|
-
let
|
|
6201
|
-
if (!
|
|
6202
|
-
|
|
6203
|
-
pendingRecords.set(ctx,
|
|
6199
|
+
let pending2 = pendingRecords.get(ctx);
|
|
6200
|
+
if (!pending2) {
|
|
6201
|
+
pending2 = [];
|
|
6202
|
+
pendingRecords.set(ctx, pending2);
|
|
6204
6203
|
ctx.deferred.push(() => rewriteKeyNames(ctx));
|
|
6205
6204
|
}
|
|
6206
|
-
|
|
6205
|
+
pending2.push(schema);
|
|
6207
6206
|
}
|
|
6208
6207
|
json2.additionalProperties = processSchema(def.valueType, ctx, {
|
|
6209
6208
|
...params,
|
|
@@ -12927,7 +12926,7 @@ var connectionSchema = {
|
|
|
12927
12926
|
dataDirectory: string2(),
|
|
12928
12927
|
organizationId: uuid2().optional(),
|
|
12929
12928
|
organizationName: string2().min(1).max(160).optional(),
|
|
12930
|
-
destination:
|
|
12929
|
+
destination: _enum(["vault", "host"]).optional()
|
|
12931
12930
|
};
|
|
12932
12931
|
var configSchema = union([
|
|
12933
12932
|
object({ ...connectionSchema, codexHome: string2() }).strict(),
|
|
@@ -13347,6 +13346,7 @@ async function nextBatch(paths, maxBytes, first = /* @__PURE__ */ new Set()) {
|
|
|
13347
13346
|
}
|
|
13348
13347
|
var recentMessagesSchema = object({
|
|
13349
13348
|
version: literal(1),
|
|
13349
|
+
captureCwds: record(string2(), string2()).optional(),
|
|
13350
13350
|
cursors: record(
|
|
13351
13351
|
string2(),
|
|
13352
13352
|
number2().int().min(0).max(Number.MAX_SAFE_INTEGER)
|
|
@@ -13644,9 +13644,9 @@ async function lastCompleteRecordOffset(filename, size) {
|
|
|
13644
13644
|
}
|
|
13645
13645
|
async function rolloutFiles(root) {
|
|
13646
13646
|
const files = [];
|
|
13647
|
-
const
|
|
13648
|
-
while (
|
|
13649
|
-
const directory =
|
|
13647
|
+
const pending2 = [root];
|
|
13648
|
+
while (pending2.length > 0) {
|
|
13649
|
+
const directory = pending2.pop();
|
|
13650
13650
|
if (directory === void 0) continue;
|
|
13651
13651
|
let entries2;
|
|
13652
13652
|
try {
|
|
@@ -13660,7 +13660,7 @@ async function rolloutFiles(root) {
|
|
|
13660
13660
|
}
|
|
13661
13661
|
for (const entry of entries2) {
|
|
13662
13662
|
const filename = path4.join(directory, entry.name);
|
|
13663
|
-
if (entry.isDirectory())
|
|
13663
|
+
if (entry.isDirectory()) pending2.push(filename);
|
|
13664
13664
|
else if (entry.isFile() && /^rollout-.+\.jsonl$/.test(entry.name))
|
|
13665
13665
|
files.push(filename);
|
|
13666
13666
|
if (files.length > 1e4)
|
|
@@ -13705,9 +13705,6 @@ async function claudeFiles(root) {
|
|
|
13705
13705
|
function isSessionFile(filename) {
|
|
13706
13706
|
return !filename.includes(`${path4.sep}subagents${path4.sep}`);
|
|
13707
13707
|
}
|
|
13708
|
-
async function countSavedSessions(source, home) {
|
|
13709
|
-
return (await sourceFiles(source, home)).filter(isSessionFile).length;
|
|
13710
|
-
}
|
|
13711
13708
|
async function sourceFilesNewestFirst(source, home) {
|
|
13712
13709
|
const files = await sourceFiles(source, home);
|
|
13713
13710
|
const listed = [];
|
|
@@ -13951,6 +13948,22 @@ function recordScope(source, record32, filename, cwds) {
|
|
|
13951
13948
|
cwd: record32.input?.cwd ?? (record32.input?.workspace_roots?.length === 1 ? record32.input.workspace_roots[0] : void 0)
|
|
13952
13949
|
};
|
|
13953
13950
|
}
|
|
13951
|
+
function captureRecordLocation(source, line, filename, cwds) {
|
|
13952
|
+
let decoded;
|
|
13953
|
+
try {
|
|
13954
|
+
decoded = JSON.parse(line);
|
|
13955
|
+
} catch {
|
|
13956
|
+
delete cwds[filename];
|
|
13957
|
+
return null;
|
|
13958
|
+
}
|
|
13959
|
+
const parsed = scopeRecordSchema.safeParse(decoded);
|
|
13960
|
+
if (!parsed.success) {
|
|
13961
|
+
delete cwds[filename];
|
|
13962
|
+
return null;
|
|
13963
|
+
}
|
|
13964
|
+
const cwd = recordScope(source, parsed.data, filename, cwds).cwd;
|
|
13965
|
+
return cwd && path6.isAbsolute(cwd) ? resolveCaptureLocation(cwd) : null;
|
|
13966
|
+
}
|
|
13954
13967
|
function authorizeVaultRecord(policy, source, line, filename, cwds, accounts = null) {
|
|
13955
13968
|
let decoded;
|
|
13956
13969
|
try {
|
|
@@ -15216,11 +15229,14 @@ async function vaultAccountIndex(config22) {
|
|
|
15216
15229
|
await loadClaudeAccountLedger(config22.dataDirectory)
|
|
15217
15230
|
) : null;
|
|
15218
15231
|
}
|
|
15219
|
-
async function scanCaptureState(config22, state, policy, accounts) {
|
|
15232
|
+
async function scanCaptureState(config22, state, policy, accounts, host) {
|
|
15220
15233
|
if (config22.destination === "vault" && policy === void 0)
|
|
15221
15234
|
throw new Error(
|
|
15222
15235
|
"Company vault scanning requires a current capture policy."
|
|
15223
15236
|
);
|
|
15237
|
+
if (config22.destination === "host" && host === void 0)
|
|
15238
|
+
throw new Error("Host collection requires a current capture policy.");
|
|
15239
|
+
const revision = host?.revision ?? policy?.revision;
|
|
15224
15240
|
const cwds = { ...state.captureCwds };
|
|
15225
15241
|
const accountIndex = accounts === void 0 ? await vaultAccountIndex(config22) : accounts;
|
|
15226
15242
|
return {
|
|
@@ -15229,15 +15245,21 @@ async function scanCaptureState(config22, state, policy, accounts) {
|
|
|
15229
15245
|
persistEvidence: async () => {
|
|
15230
15246
|
await accountIndex?.persistNativeTools(config22.dataDirectory);
|
|
15231
15247
|
},
|
|
15232
|
-
checkpoint:
|
|
15248
|
+
checkpoint: revision === void 0 ? {} : { captureCwds: cwds, captureRevision: revision }
|
|
15233
15249
|
};
|
|
15234
15250
|
}
|
|
15235
|
-
async function scanSourceOnce(config22, paths, signal, policy, accounts) {
|
|
15251
|
+
async function scanSourceOnce(config22, paths, signal, policy, accounts, host) {
|
|
15236
15252
|
signal?.throwIfAborted();
|
|
15237
15253
|
const source = collectorSource(config22);
|
|
15238
15254
|
const sourceDirectory = path14.resolve(collectorSourceDirectory(config22));
|
|
15239
15255
|
const state = await loadState(paths);
|
|
15240
|
-
const captureState = await scanCaptureState(
|
|
15256
|
+
const captureState = await scanCaptureState(
|
|
15257
|
+
config22,
|
|
15258
|
+
state,
|
|
15259
|
+
policy,
|
|
15260
|
+
accounts,
|
|
15261
|
+
host
|
|
15262
|
+
);
|
|
15241
15263
|
if (!state.initialized || state.sourceDirectory !== sourceDirectory) {
|
|
15242
15264
|
throw new Error(
|
|
15243
15265
|
`Collector state does not match the ${source} directory; reconnect this source.`
|
|
@@ -15363,7 +15385,7 @@ async function scanSourceOnce(config22, paths, signal, policy, accounts) {
|
|
|
15363
15385
|
lineStart = index + 1;
|
|
15364
15386
|
continue;
|
|
15365
15387
|
}
|
|
15366
|
-
const capture = policy === void 0 ? void 0 : authorizeVaultRecord(
|
|
15388
|
+
const capture = host !== void 0 ? host.authorizeRecord(source, line, filename, captureCwds) : policy === void 0 ? void 0 : authorizeVaultRecord(
|
|
15367
15389
|
policy,
|
|
15368
15390
|
source,
|
|
15369
15391
|
line,
|
|
@@ -15453,7 +15475,7 @@ async function readRange(filename, start2, length) {
|
|
|
15453
15475
|
await handle.close();
|
|
15454
15476
|
}
|
|
15455
15477
|
}
|
|
15456
|
-
async function messagesInRecords(source, filename, buffer, start2, room, queuedIds) {
|
|
15478
|
+
async function messagesInRecords(source, filename, buffer, start2, room, queuedIds, host, cwds) {
|
|
15457
15479
|
const events = [];
|
|
15458
15480
|
let lineStart = 0;
|
|
15459
15481
|
for (let index = 0; index < buffer.length; index += 1) {
|
|
@@ -15476,7 +15498,9 @@ async function messagesInRecords(source, filename, buffer, start2, room, queuedI
|
|
|
15476
15498
|
filename,
|
|
15477
15499
|
byteOffset
|
|
15478
15500
|
);
|
|
15479
|
-
|
|
15501
|
+
const capture = host?.authorizeRecord(source, line, filename, cwds);
|
|
15502
|
+
if (capture === null) continue;
|
|
15503
|
+
for (const record32 of scopeVaultEvents(parsed, capture, line)) {
|
|
15480
15504
|
if (record32.eventType !== "user_message" || queuedIds.has(record32.eventId))
|
|
15481
15505
|
continue;
|
|
15482
15506
|
const event3 = await redactTraceEvent(record32);
|
|
@@ -15486,17 +15510,19 @@ async function messagesInRecords(source, filename, buffer, start2, room, queuedI
|
|
|
15486
15510
|
}
|
|
15487
15511
|
return { events, consumed: lineStart };
|
|
15488
15512
|
}
|
|
15489
|
-
async function queueRecentMessages(config22, paths, limit2, signal) {
|
|
15513
|
+
async function queueRecentMessages(config22, paths, limit2, signal, host) {
|
|
15490
15514
|
if (limit2 === void 0) return void 0;
|
|
15491
15515
|
const recent = await loadRecentMessages(paths) ?? {
|
|
15492
15516
|
version: 1,
|
|
15493
15517
|
cursors: {},
|
|
15494
15518
|
queued: [...await queuedUserMessageIds(paths, limit2)],
|
|
15495
15519
|
sent: 0,
|
|
15520
|
+
captureCwds: {},
|
|
15496
15521
|
scanned: false
|
|
15497
15522
|
};
|
|
15498
15523
|
if (recent.scanned) return new Set(recent.queued);
|
|
15499
15524
|
const source = collectorSource(config22);
|
|
15525
|
+
const captureCwds = { ...recent.captureCwds };
|
|
15500
15526
|
const mainCursors = (await loadState(paths)).cursors;
|
|
15501
15527
|
const files = await sourceFilesNewestFirst(
|
|
15502
15528
|
source,
|
|
@@ -15525,7 +15551,9 @@ async function queueRecentMessages(config22, paths, limit2, signal) {
|
|
|
15525
15551
|
buffer,
|
|
15526
15552
|
start2,
|
|
15527
15553
|
room,
|
|
15528
|
-
queuedIds
|
|
15554
|
+
queuedIds,
|
|
15555
|
+
host,
|
|
15556
|
+
captureCwds
|
|
15529
15557
|
);
|
|
15530
15558
|
for (const event3 of found.events) {
|
|
15531
15559
|
events.push(event3);
|
|
@@ -15543,6 +15571,7 @@ async function queueRecentMessages(config22, paths, limit2, signal) {
|
|
|
15543
15571
|
await saveRecentMessages(
|
|
15544
15572
|
{
|
|
15545
15573
|
version: 1,
|
|
15574
|
+
...host === void 0 ? {} : { captureCwds },
|
|
15546
15575
|
cursors,
|
|
15547
15576
|
queued,
|
|
15548
15577
|
sent: recent.sent,
|
|
@@ -15558,8 +15587,8 @@ async function settleRecentMessages(paths, limit2) {
|
|
|
15558
15587
|
if (recent === null) return { sent: 0, complete: false };
|
|
15559
15588
|
if (recent.queued.length === 0)
|
|
15560
15589
|
return { sent: recent.sent, complete: recent.scanned };
|
|
15561
|
-
const
|
|
15562
|
-
const queued = recent.queued.filter((id) =>
|
|
15590
|
+
const pending2 = await pendingEventIds(paths);
|
|
15591
|
+
const queued = recent.queued.filter((id) => pending2.has(id));
|
|
15563
15592
|
const sent = recent.sent + recent.queued.length - queued.length;
|
|
15564
15593
|
if (queued.length !== recent.queued.length)
|
|
15565
15594
|
await saveRecentMessages(
|
|
@@ -15594,12 +15623,20 @@ function retryAfterMs(response) {
|
|
|
15594
15623
|
const milliseconds = /^\d+$/.test(header) ? Number(header) * 1e3 : Date.parse(header) - Date.now();
|
|
15595
15624
|
return Number.isFinite(milliseconds) ? Math.max(1e3, Math.min(milliseconds, 3e5)) : 5e3;
|
|
15596
15625
|
}
|
|
15597
|
-
|
|
15626
|
+
function captureBatch(pending2, host) {
|
|
15627
|
+
const first = pending2[0];
|
|
15628
|
+
const batchKey = host?.batchKey;
|
|
15629
|
+
if (first === void 0 || batchKey === void 0) return pending2;
|
|
15630
|
+
const key = batchKey(first);
|
|
15631
|
+
return pending2.filter((event3) => batchKey(event3) === key);
|
|
15632
|
+
}
|
|
15633
|
+
async function uploadPendingOnce(config22, paths, fetcher = collectorFetch, signal, onAcknowledged, policy, first, host) {
|
|
15598
15634
|
let uploaded = 0;
|
|
15599
15635
|
for (let attempt = 0; attempt < MAX_UPLOADS_PER_TICK; attempt += 1) {
|
|
15600
15636
|
signal?.throwIfAborted();
|
|
15601
15637
|
await recheckQueuedAccounts(config22, paths, policy);
|
|
15602
|
-
const
|
|
15638
|
+
const pending2 = await nextBatch(paths, MAX_UPLOAD_BYTES, first);
|
|
15639
|
+
const batch = captureBatch(pending2, host);
|
|
15603
15640
|
if (batch.length === 0) return { uploaded, retryAfterMs: null };
|
|
15604
15641
|
const body = JSON.stringify({ events: batch });
|
|
15605
15642
|
if (Buffer.byteLength(body, "utf8") > MAX_REQUEST_BYTES) {
|
|
@@ -15668,8 +15705,33 @@ async function recheckQueuedAccounts(config22, paths, policy) {
|
|
|
15668
15705
|
(event3) => reauthorizeVaultEvent(policy, event3, accounts)
|
|
15669
15706
|
);
|
|
15670
15707
|
}
|
|
15708
|
+
async function prepareHostCapture(config22, paths, host) {
|
|
15709
|
+
if (config22.destination === "host" && host === void 0)
|
|
15710
|
+
throw new Error("Host collection requires a current capture policy.");
|
|
15711
|
+
if (host !== void 0) {
|
|
15712
|
+
if (!Number.isSafeInteger(host.revision) || host.revision <= 0)
|
|
15713
|
+
throw new Error("Capture revisions must be positive safe integers.");
|
|
15714
|
+
await rewritePendingEvents(paths, host.authorizeEvent);
|
|
15715
|
+
const state = await loadState(paths);
|
|
15716
|
+
if (state.captureRevision !== host.revision) {
|
|
15717
|
+
await saveState(
|
|
15718
|
+
{
|
|
15719
|
+
...state,
|
|
15720
|
+
cursors: {},
|
|
15721
|
+
skipping: [],
|
|
15722
|
+
captureCwds: {},
|
|
15723
|
+
captureRevision: host.revision
|
|
15724
|
+
},
|
|
15725
|
+
paths
|
|
15726
|
+
);
|
|
15727
|
+
await fs9.rm(paths.recentMessages, { force: true });
|
|
15728
|
+
}
|
|
15729
|
+
}
|
|
15730
|
+
}
|
|
15671
15731
|
async function collectorTick(config22, paths, fetcher = collectorFetch, signal, onAcknowledged, options = {}) {
|
|
15672
15732
|
const recentLimit = checkedRecentLimit(config22, options.recentMessages);
|
|
15733
|
+
const host = options.capture;
|
|
15734
|
+
await prepareHostCapture(config22, paths, host);
|
|
15673
15735
|
const accounts = await vaultAccountIndex(config22);
|
|
15674
15736
|
const policy = config22.destination === "vault" ? await fetchVaultPolicy(config22.endpoint, config22.token, fetcher, signal) : void 0;
|
|
15675
15737
|
if (policy !== void 0) {
|
|
@@ -15694,7 +15756,13 @@ async function collectorTick(config22, paths, fetcher = collectorFetch, signal,
|
|
|
15694
15756
|
}
|
|
15695
15757
|
}
|
|
15696
15758
|
await redactPendingOutbox(paths);
|
|
15697
|
-
const first = await queueRecentMessages(
|
|
15759
|
+
const first = await queueRecentMessages(
|
|
15760
|
+
config22,
|
|
15761
|
+
paths,
|
|
15762
|
+
recentLimit,
|
|
15763
|
+
signal,
|
|
15764
|
+
host
|
|
15765
|
+
);
|
|
15698
15766
|
let upload = await uploadPendingOnce(
|
|
15699
15767
|
config22,
|
|
15700
15768
|
paths,
|
|
@@ -15702,14 +15770,15 @@ async function collectorTick(config22, paths, fetcher = collectorFetch, signal,
|
|
|
15702
15770
|
signal,
|
|
15703
15771
|
onAcknowledged,
|
|
15704
15772
|
policy,
|
|
15705
|
-
first
|
|
15773
|
+
first,
|
|
15774
|
+
host
|
|
15706
15775
|
);
|
|
15707
15776
|
const scan = await outboxSize(paths) >= SCAN_PAUSE_OUTBOX_BYTES ? {
|
|
15708
15777
|
discovered: 0,
|
|
15709
15778
|
excludedRecords: 0,
|
|
15710
15779
|
caughtUp: false,
|
|
15711
15780
|
history: await savedHistoryProgress(config22, paths)
|
|
15712
|
-
} : await scanSourceOnce(config22, paths, signal, policy, accounts);
|
|
15781
|
+
} : await scanSourceOnce(config22, paths, signal, policy, accounts, host);
|
|
15713
15782
|
if (policy !== void 0 && scan.caughtUp) {
|
|
15714
15783
|
const state = await loadState(paths);
|
|
15715
15784
|
if (state.captureRecheckAt === void 0)
|
|
@@ -15726,7 +15795,8 @@ async function collectorTick(config22, paths, fetcher = collectorFetch, signal,
|
|
|
15726
15795
|
signal,
|
|
15727
15796
|
onAcknowledged,
|
|
15728
15797
|
policy,
|
|
15729
|
-
first
|
|
15798
|
+
first,
|
|
15799
|
+
host
|
|
15730
15800
|
);
|
|
15731
15801
|
upload = { ...next, uploaded: upload.uploaded + next.uploaded };
|
|
15732
15802
|
}
|
|
@@ -15756,6 +15826,14 @@ async function lockIdentity(filename) {
|
|
|
15756
15826
|
}
|
|
15757
15827
|
var CollectorLockBusyError = class extends Error {
|
|
15758
15828
|
};
|
|
15829
|
+
async function tryAcquireCollectorLock(paths) {
|
|
15830
|
+
try {
|
|
15831
|
+
return await acquireCollectorLock(paths);
|
|
15832
|
+
} catch (error22) {
|
|
15833
|
+
if (error22 instanceof CollectorLockBusyError) return null;
|
|
15834
|
+
throw error22;
|
|
15835
|
+
}
|
|
15836
|
+
}
|
|
15759
15837
|
async function acquireCollectorLock(paths) {
|
|
15760
15838
|
await ensurePrivateDirectory(path15.dirname(paths.lock));
|
|
15761
15839
|
const databasePath = `${paths.lock}.sqlite`;
|
|
@@ -16079,10 +16157,10 @@ function mergeDefs2(...defs) {
|
|
|
16079
16157
|
function cloneDef2(schema) {
|
|
16080
16158
|
return mergeDefs2(schema._zod.def);
|
|
16081
16159
|
}
|
|
16082
|
-
function getElementAtPath2(obj,
|
|
16083
|
-
if (!
|
|
16160
|
+
function getElementAtPath2(obj, path37) {
|
|
16161
|
+
if (!path37)
|
|
16084
16162
|
return obj;
|
|
16085
|
-
return
|
|
16163
|
+
return path37.reduce((acc, key) => acc?.[key], obj);
|
|
16086
16164
|
}
|
|
16087
16165
|
function promiseAllObject2(promisesObj) {
|
|
16088
16166
|
const keys = Object.keys(promisesObj);
|
|
@@ -16422,11 +16500,11 @@ function explicitlyAborted2(x, startIndex = 0) {
|
|
|
16422
16500
|
}
|
|
16423
16501
|
return false;
|
|
16424
16502
|
}
|
|
16425
|
-
function prefixIssues2(
|
|
16503
|
+
function prefixIssues2(path37, issues) {
|
|
16426
16504
|
return issues.map((iss) => {
|
|
16427
16505
|
var _a5;
|
|
16428
16506
|
(_a5 = iss).path ?? (_a5.path = []);
|
|
16429
|
-
iss.path.unshift(
|
|
16507
|
+
iss.path.unshift(path37);
|
|
16430
16508
|
return iss;
|
|
16431
16509
|
});
|
|
16432
16510
|
}
|
|
@@ -16460,8 +16538,8 @@ function finalizeIssue2(iss, ctx, config3) {
|
|
|
16460
16538
|
else
|
|
16461
16539
|
iss.schema = iss.inst;
|
|
16462
16540
|
}
|
|
16463
|
-
const
|
|
16464
|
-
const message = iss.message ? iss.message : unwrapMessage2(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage2(
|
|
16541
|
+
const schemaError3 = iss.schema !== iss.inst ? iss.schema?._zod.def?.error : void 0;
|
|
16542
|
+
const message = iss.message ? iss.message : unwrapMessage2(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage2(schemaError3?.(iss)) ?? unwrapMessage2(ctx?.error?.(iss)) ?? unwrapMessage2(config3.customError?.(iss)) ?? unwrapMessage2(config3.localeError?.(iss)) ?? "Invalid input";
|
|
16465
16543
|
const full = {};
|
|
16466
16544
|
for (const k of Object.keys(iss)) {
|
|
16467
16545
|
if (k === "inst" || k === "schema" || k === "continue" || k === "input" || k === "__proto__")
|
|
@@ -16884,16 +16962,16 @@ function flattenError2(error3, mapper = (issue3) => issue3.message) {
|
|
|
16884
16962
|
}
|
|
16885
16963
|
function formatError2(error3, mapper = (issue3) => issue3.message) {
|
|
16886
16964
|
const fieldErrors = { _errors: [] };
|
|
16887
|
-
const processError = (error4,
|
|
16965
|
+
const processError = (error4, path37 = []) => {
|
|
16888
16966
|
for (const issue3 of error4.issues) {
|
|
16889
16967
|
if (issue3.code === "invalid_union" && issue3.errors.length) {
|
|
16890
|
-
issue3.errors.map((issues) => processError({ issues }, [...
|
|
16968
|
+
issue3.errors.map((issues) => processError({ issues }, [...path37, ...issue3.path]));
|
|
16891
16969
|
} else if (issue3.code === "invalid_key") {
|
|
16892
|
-
processError({ issues: issue3.issues }, [...
|
|
16970
|
+
processError({ issues: issue3.issues }, [...path37, ...issue3.path]);
|
|
16893
16971
|
} else if (issue3.code === "invalid_element") {
|
|
16894
|
-
processError({ issues: issue3.issues }, [...
|
|
16972
|
+
processError({ issues: issue3.issues }, [...path37, ...issue3.path]);
|
|
16895
16973
|
} else {
|
|
16896
|
-
const fullpath = [...
|
|
16974
|
+
const fullpath = [...path37, ...issue3.path];
|
|
16897
16975
|
if (fullpath.length === 0) {
|
|
16898
16976
|
fieldErrors._errors.push(mapper(issue3));
|
|
16899
16977
|
} else {
|
|
@@ -21191,13 +21269,13 @@ var recordProcessor2 = (schema, ctx, _json, params) => {
|
|
|
21191
21269
|
...params,
|
|
21192
21270
|
path: [...params.path, "propertyNames"]
|
|
21193
21271
|
});
|
|
21194
|
-
let
|
|
21195
|
-
if (!
|
|
21196
|
-
|
|
21197
|
-
pendingRecords2.set(ctx,
|
|
21272
|
+
let pending2 = pendingRecords2.get(ctx);
|
|
21273
|
+
if (!pending2) {
|
|
21274
|
+
pending2 = [];
|
|
21275
|
+
pendingRecords2.set(ctx, pending2);
|
|
21198
21276
|
ctx.deferred.push(() => rewriteKeyNames2(ctx));
|
|
21199
21277
|
}
|
|
21200
|
-
|
|
21278
|
+
pending2.push(schema);
|
|
21201
21279
|
}
|
|
21202
21280
|
json2.additionalProperties = processSchema2(def.valueType, ctx, {
|
|
21203
21281
|
...params,
|
|
@@ -22539,6 +22617,65 @@ var acknowledgementSchema2 = object3({
|
|
|
22539
22617
|
deviceId: string5().regex(UUID_PATTERN2),
|
|
22540
22618
|
acceptedEventIds: array2(string5())
|
|
22541
22619
|
}).strict().describe("Accepted event IDs must match the submitted batch exactly, without duplicates.");
|
|
22620
|
+
function schemaError2(error3, fallback) {
|
|
22621
|
+
return error3.issues[0]?.message ?? fallback;
|
|
22622
|
+
}
|
|
22623
|
+
function parseTraceEventBatch2(input) {
|
|
22624
|
+
const parsed = traceEventBatchSchema2.safeParse(input);
|
|
22625
|
+
if (!parsed.success) {
|
|
22626
|
+
return {
|
|
22627
|
+
success: false,
|
|
22628
|
+
error: schemaError2(parsed.error, "Trace event batch is invalid.")
|
|
22629
|
+
};
|
|
22630
|
+
}
|
|
22631
|
+
const eventIds = /* @__PURE__ */ new Set();
|
|
22632
|
+
for (const event3 of parsed.data.events) {
|
|
22633
|
+
if (eventIds.has(event3.eventId)) {
|
|
22634
|
+
return {
|
|
22635
|
+
success: false,
|
|
22636
|
+
error: "A batch cannot contain duplicate event ids."
|
|
22637
|
+
};
|
|
22638
|
+
}
|
|
22639
|
+
eventIds.add(event3.eventId);
|
|
22640
|
+
}
|
|
22641
|
+
return { success: true, value: parsed.data };
|
|
22642
|
+
}
|
|
22643
|
+
function parseTraceEventAcknowledgement2(input, expectedEventIds) {
|
|
22644
|
+
const parsed = acknowledgementSchema2.safeParse(input);
|
|
22645
|
+
if (!parsed.success) {
|
|
22646
|
+
return {
|
|
22647
|
+
success: false,
|
|
22648
|
+
error: schemaError2(parsed.error, "Acknowledgement has an invalid shape.")
|
|
22649
|
+
};
|
|
22650
|
+
}
|
|
22651
|
+
const { acceptedEventIds } = parsed.data;
|
|
22652
|
+
if (acceptedEventIds.length !== expectedEventIds.length) {
|
|
22653
|
+
return {
|
|
22654
|
+
success: false,
|
|
22655
|
+
error: "Acknowledgement event ids do not match the batch."
|
|
22656
|
+
};
|
|
22657
|
+
}
|
|
22658
|
+
const actual = /* @__PURE__ */ new Set();
|
|
22659
|
+
for (const eventId of acceptedEventIds) {
|
|
22660
|
+
if (!expectedEventIds.includes(eventId)) {
|
|
22661
|
+
return {
|
|
22662
|
+
success: false,
|
|
22663
|
+
error: "Acknowledgement includes an unknown event id."
|
|
22664
|
+
};
|
|
22665
|
+
}
|
|
22666
|
+
actual.add(eventId);
|
|
22667
|
+
}
|
|
22668
|
+
if (actual.size !== expectedEventIds.length) {
|
|
22669
|
+
return {
|
|
22670
|
+
success: false,
|
|
22671
|
+
error: "Acknowledgement contains duplicate event ids."
|
|
22672
|
+
};
|
|
22673
|
+
}
|
|
22674
|
+
return {
|
|
22675
|
+
success: true,
|
|
22676
|
+
value: { ...parsed.data, acceptedEventIds: [...actual] }
|
|
22677
|
+
};
|
|
22678
|
+
}
|
|
22542
22679
|
function isLaunchDirectory(value) {
|
|
22543
22680
|
if (!(value.startsWith("/") || /^[A-Za-z]:[\\/]/.test(value) || /^\\\\[^\\/]+[\\/][^\\/]+(?:[\\/]|$)/.test(value)) || value.length > 4096)
|
|
22544
22681
|
return false;
|
|
@@ -22551,36 +22688,640 @@ function isLaunchDirectory(value) {
|
|
|
22551
22688
|
}
|
|
22552
22689
|
|
|
22553
22690
|
// src/agent-tools.ts
|
|
22691
|
+
import { promises as fs13 } from "node:fs";
|
|
22692
|
+
import os4 from "node:os";
|
|
22693
|
+
import path18 from "node:path";
|
|
22694
|
+
import readline from "node:readline";
|
|
22695
|
+
|
|
22696
|
+
// src/sharing.ts
|
|
22554
22697
|
import { promises as fs11 } from "node:fs";
|
|
22555
|
-
import os3 from "node:os";
|
|
22556
22698
|
import path8 from "node:path";
|
|
22557
|
-
import readline from "node:readline";
|
|
22558
22699
|
|
|
22559
|
-
//
|
|
22560
|
-
|
|
22700
|
+
// ../collector-contracts/dist/sharing.js
|
|
22701
|
+
var collectorComputerIdSchema = uuid5();
|
|
22702
|
+
var collectorSharingApprovalIdentitySchema = object3({
|
|
22703
|
+
workspaceId: uuid5(),
|
|
22704
|
+
computerId: collectorComputerIdSchema,
|
|
22705
|
+
revision: coerce_exports.number().int().positive()
|
|
22706
|
+
}).strict();
|
|
22707
|
+
var captureLocationSchema = string5().min(1).max(4096).refine((value) => !value.split("").some((character) => character.charCodeAt(0) < 32) && (/^\//.test(value) || /^[A-Za-z]:[\\/]/.test(value)), "Choose an absolute folder path.").describe("An absolute Unix or Windows folder path without control characters.");
|
|
22708
|
+
var collectorProjectInventorySchema = object3({
|
|
22709
|
+
computerId: uuid5(),
|
|
22710
|
+
projects: array2(object3({
|
|
22711
|
+
path: captureLocationSchema,
|
|
22712
|
+
name: string5().min(1).max(160),
|
|
22713
|
+
sessions: number4().int().nonnegative(),
|
|
22714
|
+
sources: array2(_enum2(COLLECTOR_SOURCES2)).min(1).max(3)
|
|
22715
|
+
}).strict()).max(1e3),
|
|
22716
|
+
truncated: boolean4()
|
|
22717
|
+
}).strict();
|
|
22718
|
+
var collectorSharingSelectionSchema = object3({
|
|
22719
|
+
mode: _enum2(["selected", "all"]),
|
|
22720
|
+
locations: array2(captureLocationSchema).max(1e3),
|
|
22721
|
+
excludedLocations: array2(captureLocationSchema).max(1e3),
|
|
22722
|
+
paused: boolean4().default(false)
|
|
22723
|
+
}).strict().refine((value) => value.mode === "all" || value.locations.length > 0, "Choose at least one project or folder.").describe("Selected mode requires at least one folder. All mode shares unmatched projects except exclusions.");
|
|
22724
|
+
var collectorWorkspaceSharingSchema = object3({
|
|
22725
|
+
organizationId: uuid5(),
|
|
22726
|
+
organizationName: string5().min(1).max(160),
|
|
22727
|
+
selection: collectorSharingSelectionSchema,
|
|
22728
|
+
active: boolean4(),
|
|
22729
|
+
connectedSources: array2(_enum2(COLLECTOR_SOURCES2)).max(3)
|
|
22730
|
+
}).strict();
|
|
22731
|
+
var collectorSharingSnapshotSchema = object3({
|
|
22732
|
+
computerId: uuid5(),
|
|
22733
|
+
ownerId: uuid5(),
|
|
22734
|
+
revision: number4().int().positive(),
|
|
22735
|
+
approved: boolean4(),
|
|
22736
|
+
workspaces: array2(collectorWorkspaceSharingSchema).max(100)
|
|
22737
|
+
}).strict();
|
|
22738
|
+
|
|
22739
|
+
// ../collector-contracts/dist/sharing-policy.js
|
|
22740
|
+
function captureLocationContains(root, location) {
|
|
22741
|
+
const normalize = (value) => {
|
|
22742
|
+
const windows = /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
|
|
22743
|
+
const normalized = value.replaceAll("\\", "/").replace(/\/+$/, "");
|
|
22744
|
+
return windows ? normalized.toLowerCase() : normalized;
|
|
22745
|
+
};
|
|
22746
|
+
const parent = normalize(root);
|
|
22747
|
+
const child = normalize(location);
|
|
22748
|
+
return child === parent || child.startsWith(parent + "/");
|
|
22749
|
+
}
|
|
22750
|
+
function sharingWorkspaceForLocation(snapshot, location) {
|
|
22751
|
+
const candidates = snapshot.workspaces.flatMap((workspace) => {
|
|
22752
|
+
const specificity = sharingSelectionSpecificity(workspace.selection, location);
|
|
22753
|
+
if (specificity === null)
|
|
22754
|
+
return [];
|
|
22755
|
+
return [
|
|
22756
|
+
{
|
|
22757
|
+
workspace,
|
|
22758
|
+
specificity
|
|
22759
|
+
}
|
|
22760
|
+
];
|
|
22761
|
+
}).sort((left, right) => right.specificity - left.specificity);
|
|
22762
|
+
const first = candidates[0];
|
|
22763
|
+
if (!first || candidates[1]?.specificity === first.specificity || !first.workspace.active)
|
|
22764
|
+
return null;
|
|
22765
|
+
return first.workspace.organizationId;
|
|
22766
|
+
}
|
|
22767
|
+
function sharingSelectionSpecificity(selection, location) {
|
|
22768
|
+
const included = selection.locations.filter((root) => captureLocationContains(root, location));
|
|
22769
|
+
if (!included.length && selection.mode !== "all")
|
|
22770
|
+
return null;
|
|
22771
|
+
const specificity = Math.max(0, ...included.map((root) => root.replace(/[/\\]+$/, "").length));
|
|
22772
|
+
const excluded = selection.excludedLocations.filter((root) => captureLocationContains(root, location));
|
|
22773
|
+
return excluded.some((root) => root.replace(/[/\\]+$/, "").length >= specificity) ? null : specificity;
|
|
22774
|
+
}
|
|
22775
|
+
|
|
22776
|
+
// ../collector-contracts/dist/folders.js
|
|
22777
|
+
var folderEntrySchema = object3({ name: string5().min(1).max(255), path: captureLocationSchema }).strict();
|
|
22778
|
+
var collectorFolderRequestSchema = object3({
|
|
22779
|
+
id: uuid5(),
|
|
22780
|
+
directory: captureLocationSchema.nullable(),
|
|
22781
|
+
query: string5().trim().max(80),
|
|
22782
|
+
showHidden: boolean4(),
|
|
22783
|
+
expiresAtMs: number4().int().positive()
|
|
22784
|
+
}).strict();
|
|
22785
|
+
var collectorFolderListingSchema = object3({
|
|
22786
|
+
directory: captureLocationSchema,
|
|
22787
|
+
name: string5().min(1).max(255),
|
|
22788
|
+
parent: captureLocationSchema.nullable(),
|
|
22789
|
+
breadcrumbs: array2(folderEntrySchema).min(1).max(8),
|
|
22790
|
+
entries: array2(folderEntrySchema).max(200),
|
|
22791
|
+
truncated: boolean4()
|
|
22792
|
+
}).strict();
|
|
22793
|
+
var pending = object3({
|
|
22794
|
+
status: literal2("pending"),
|
|
22795
|
+
request: collectorFolderRequestSchema
|
|
22796
|
+
}).strict();
|
|
22797
|
+
var ready = object3({
|
|
22798
|
+
status: literal2("ready"),
|
|
22799
|
+
request: collectorFolderRequestSchema,
|
|
22800
|
+
listing: collectorFolderListingSchema
|
|
22801
|
+
}).strict();
|
|
22802
|
+
var failed = object3({
|
|
22803
|
+
status: literal2("error"),
|
|
22804
|
+
request: collectorFolderRequestSchema,
|
|
22805
|
+
reason: _enum2(["permission", "missing", "unavailable"])
|
|
22806
|
+
}).strict();
|
|
22807
|
+
var collectorFolderStateSchema = discriminatedUnion2("status", [
|
|
22808
|
+
pending,
|
|
22809
|
+
ready,
|
|
22810
|
+
failed
|
|
22811
|
+
]);
|
|
22812
|
+
var collectorFolderCommandSchema = object3({ organizationId: uuid5(), request: collectorFolderRequestSchema }).strict();
|
|
22813
|
+
var collectorFolderReplySchema = object3({
|
|
22814
|
+
organizationId: uuid5(),
|
|
22815
|
+
state: discriminatedUnion2("status", [ready, failed])
|
|
22816
|
+
}).strict();
|
|
22817
|
+
var collectorFolderActionSchema = object3({
|
|
22818
|
+
workspaceId: uuid5(),
|
|
22819
|
+
computerId: uuid5(),
|
|
22820
|
+
intent: discriminatedUnion2("type", [
|
|
22821
|
+
object3({
|
|
22822
|
+
type: literal2("browse"),
|
|
22823
|
+
id: uuid5(),
|
|
22824
|
+
directory: captureLocationSchema.nullable(),
|
|
22825
|
+
query: string5().trim().max(80),
|
|
22826
|
+
showHidden: boolean4()
|
|
22827
|
+
}).strict(),
|
|
22828
|
+
object3({ type: literal2("read"), id: uuid5() }).strict(),
|
|
22829
|
+
object3({ type: literal2("close"), id: uuid5() }).strict()
|
|
22830
|
+
])
|
|
22831
|
+
}).strict();
|
|
22832
|
+
|
|
22833
|
+
// ../collector-contracts/dist/index.js
|
|
22834
|
+
var cursorBillingRequestSchema = object3({
|
|
22835
|
+
conversationId: uuid5(),
|
|
22836
|
+
reconcileFromMs: number4().int().nonnegative().nullable(),
|
|
22837
|
+
events: array2(object3({
|
|
22838
|
+
timestamp: number4().int().nonnegative(),
|
|
22839
|
+
kind: _enum2([
|
|
22840
|
+
"included",
|
|
22841
|
+
"paid",
|
|
22842
|
+
"byok",
|
|
22843
|
+
"not_charged",
|
|
22844
|
+
"unknown"
|
|
22845
|
+
]),
|
|
22846
|
+
inputTokens: number4().int().nonnegative().nullable(),
|
|
22847
|
+
outputTokens: number4().int().nonnegative().nullable(),
|
|
22848
|
+
cacheReadTokens: number4().int().nonnegative().nullable(),
|
|
22849
|
+
cacheWriteTokens: number4().int().nonnegative().nullable(),
|
|
22850
|
+
paidUsd: number4().finite().nonnegative().nullable()
|
|
22851
|
+
}).strict()).max(5e3)
|
|
22852
|
+
}).strict();
|
|
22853
|
+
function codexCreditSign(value) {
|
|
22854
|
+
const mantissa = value.split(/[eE]/, 1)[0];
|
|
22855
|
+
if (!/[1-9]/.test(mantissa))
|
|
22856
|
+
return 0;
|
|
22857
|
+
return mantissa.startsWith("-") ? -1 : 1;
|
|
22858
|
+
}
|
|
22859
|
+
var codexBillingRequestSchema = object3({
|
|
22860
|
+
accountFingerprint: string5().regex(/^[0-9a-f]{64}$/),
|
|
22861
|
+
reconcile: boolean4(),
|
|
22862
|
+
tasks: array2(object3({
|
|
22863
|
+
threadId: uuid5(),
|
|
22864
|
+
createdAt: iso_exports2.datetime().nullable(),
|
|
22865
|
+
descendantThreadIds: array2(uuid5()).max(900),
|
|
22866
|
+
dataStatus: _enum2([
|
|
22867
|
+
"available",
|
|
22868
|
+
"partial",
|
|
22869
|
+
"unavailable",
|
|
22870
|
+
"not_queried"
|
|
22871
|
+
]),
|
|
22872
|
+
usageSource: string5().max(100),
|
|
22873
|
+
balanceUsageCredits: string5().regex(/^[+-]?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d{1,3})?$/).max(128).nullable()
|
|
22874
|
+
}).strict()).max(100),
|
|
22875
|
+
authTurns: array2(object3({
|
|
22876
|
+
threadId: uuid5(),
|
|
22877
|
+
turnId: uuid5(),
|
|
22878
|
+
mode: _enum2(["chatgpt", "api_key", "conflicting"])
|
|
22879
|
+
}).strict()).max(5e3)
|
|
22880
|
+
}).strict();
|
|
22881
|
+
var UUID_PATTERN3 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;
|
|
22882
|
+
var episodeCountSchema = number4().int().min(0).max(Number.MAX_SAFE_INTEGER);
|
|
22883
|
+
var collectorFrictionSummarySchema = object3({
|
|
22884
|
+
weekEpisodes: episodeCountSchema,
|
|
22885
|
+
dayEpisodes: episodeCountSchema
|
|
22886
|
+
}).strict().refine((summary) => summary.dayEpisodes <= summary.weekEpisodes, "The last day cannot have more friction episodes than the last week.").describe("dayEpisodes must not exceed weekEpisodes.");
|
|
22887
|
+
var RECENT_MESSAGE_LIMIT = 400;
|
|
22888
|
+
var sessionCountSchema = number4().int().min(0).max(1e6);
|
|
22889
|
+
var collectorHistoryProgressSchema = object3({
|
|
22890
|
+
sessions: sessionCountSchema,
|
|
22891
|
+
readSessions: sessionCountSchema,
|
|
22892
|
+
complete: boolean4(),
|
|
22893
|
+
recentMessages: object3({
|
|
22894
|
+
sent: number4().int().min(0).max(RECENT_MESSAGE_LIMIT),
|
|
22895
|
+
complete: boolean4()
|
|
22896
|
+
}).strict().optional().describe("The newest messages sent ahead of the rest of the history; collectors before 0.17.0 omit it.")
|
|
22897
|
+
}).strict().refine((progress) => progress.readSessions <= progress.sessions, "Read sessions cannot exceed listed sessions.").refine((progress) => !progress.complete || progress.readSessions === progress.sessions, "A complete import has read every listed session.").describe("readSessions must not exceed sessions; complete requires equal counts.");
|
|
22898
|
+
var collectorSourceSchema = _enum2(COLLECTOR_SOURCES2);
|
|
22899
|
+
var collectorEnrollmentRequestSchema = object3({
|
|
22900
|
+
deviceName: string5().trim().min(1).max(160).describe("Trimmed before length validation."),
|
|
22901
|
+
organizationId: string5().regex(UUID_PATTERN3).optional(),
|
|
22902
|
+
computerId: uuid5().optional(),
|
|
22903
|
+
source: collectorSourceSchema.optional()
|
|
22904
|
+
}).strict();
|
|
22905
|
+
var collectorExpansionRequestSchema = object3({
|
|
22906
|
+
source: _enum2(COLLECTOR_SOURCES2),
|
|
22907
|
+
token: string5().regex(/^[A-Za-z0-9_-]{43}$/),
|
|
22908
|
+
organizationId: string5().regex(UUID_PATTERN3).optional()
|
|
22909
|
+
}).strict();
|
|
22910
|
+
var collectorEnrollmentResponseSchema = discriminatedUnion2("status", [
|
|
22911
|
+
object3({
|
|
22912
|
+
status: literal2("enrolled"),
|
|
22913
|
+
token: string5().regex(/^[A-Za-z0-9_-]{43}$/),
|
|
22914
|
+
deviceId: string5().regex(UUID_PATTERN3),
|
|
22915
|
+
organizationId: string5().regex(UUID_PATTERN3),
|
|
22916
|
+
organizationName: string5().min(1).max(160)
|
|
22917
|
+
}).strict(),
|
|
22918
|
+
object3({
|
|
22919
|
+
status: literal2("choose_organization"),
|
|
22920
|
+
organizations: array2(object3({
|
|
22921
|
+
id: string5().regex(UUID_PATTERN3),
|
|
22922
|
+
name: string5().min(1).max(160)
|
|
22923
|
+
}).strict()).max(20),
|
|
22924
|
+
hasMore: boolean4()
|
|
22925
|
+
}).strict()
|
|
22926
|
+
]);
|
|
22927
|
+
function parseCollectorEnrollmentResponse(input) {
|
|
22928
|
+
const parsed = collectorEnrollmentResponseSchema.safeParse(input);
|
|
22929
|
+
return parsed.success ? { success: true, value: parsed.data } : {
|
|
22930
|
+
success: false,
|
|
22931
|
+
error: parsed.error.issues[0]?.message ?? "Collector enrollment response is invalid."
|
|
22932
|
+
};
|
|
22933
|
+
}
|
|
22934
|
+
function parseCollectorFrictionSummary(input) {
|
|
22935
|
+
const parsed = collectorFrictionSummarySchema.safeParse(input);
|
|
22936
|
+
return parsed.success ? { success: true, value: parsed.data } : {
|
|
22937
|
+
success: false,
|
|
22938
|
+
error: parsed.error.issues[0]?.message ?? "Friction summary is invalid."
|
|
22939
|
+
};
|
|
22940
|
+
}
|
|
22941
|
+
|
|
22942
|
+
// src/sharing-storage.ts
|
|
22943
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
22561
22944
|
import { promises as fs6 } from "node:fs";
|
|
22562
22945
|
import os from "node:os";
|
|
22563
22946
|
import path7 from "node:path";
|
|
22564
|
-
|
|
22565
|
-
|
|
22566
|
-
|
|
22567
|
-
|
|
22947
|
+
var connectionSchema2 = object3({
|
|
22948
|
+
organizationId: uuid5(),
|
|
22949
|
+
organizationName: string5().min(1).max(160),
|
|
22950
|
+
source: _enum2(COLLECTOR_SOURCES2),
|
|
22951
|
+
token: string5().min(1),
|
|
22952
|
+
endpoint: url()
|
|
22953
|
+
}).strict();
|
|
22954
|
+
var registrySchema = object3({
|
|
22955
|
+
version: literal2(1),
|
|
22956
|
+
computerId: uuid5(),
|
|
22957
|
+
ownerId: uuid5().nullable(),
|
|
22958
|
+
connections: array2(connectionSchema2).max(300),
|
|
22959
|
+
sourceHomes: object3({ codex: string5(), claude: string5(), cursor: string5() }).strict().optional(),
|
|
22960
|
+
directories: partialRecord(_enum2(COLLECTOR_SOURCES2), string5()).optional()
|
|
22961
|
+
}).strict();
|
|
22962
|
+
function enrolledSharingConnection(enrollment, source, endpoint) {
|
|
22568
22963
|
return {
|
|
22569
|
-
|
|
22570
|
-
|
|
22571
|
-
|
|
22572
|
-
|
|
22573
|
-
|
|
22574
|
-
settings: path7.join(root, "settings.json"),
|
|
22575
|
-
lock: path7.join(root, "state.lock"),
|
|
22576
|
-
updateLock: path7.join(root, "update.lock"),
|
|
22577
|
-
launcher: path7.join(root, "launcher.js")
|
|
22964
|
+
token: enrollment.token,
|
|
22965
|
+
organizationId: enrollment.organizationId,
|
|
22966
|
+
organizationName: enrollment.organizationName,
|
|
22967
|
+
source,
|
|
22968
|
+
endpoint
|
|
22578
22969
|
};
|
|
22579
22970
|
}
|
|
22580
|
-
|
|
22581
|
-
|
|
22582
|
-
|
|
22583
|
-
|
|
22971
|
+
function sharingDirectory() {
|
|
22972
|
+
return path7.join(os.homedir(), ".config", "iteration", "sharing");
|
|
22973
|
+
}
|
|
22974
|
+
async function readSharingRegistry() {
|
|
22975
|
+
let contents;
|
|
22976
|
+
try {
|
|
22977
|
+
contents = await fs6.readFile(
|
|
22978
|
+
path7.join(sharingDirectory(), "connections.json"),
|
|
22979
|
+
"utf8"
|
|
22980
|
+
);
|
|
22981
|
+
} catch (error3) {
|
|
22982
|
+
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
22983
|
+
return null;
|
|
22984
|
+
throw error3;
|
|
22985
|
+
}
|
|
22986
|
+
return registrySchema.parse(JSON.parse(contents));
|
|
22987
|
+
}
|
|
22988
|
+
async function withSharingRegistry(work) {
|
|
22989
|
+
const directory = sharingDirectory();
|
|
22990
|
+
const release = await waitForCollectorLock(collectorPaths(directory));
|
|
22991
|
+
try {
|
|
22992
|
+
const registry3 = await readSharingRegistry() ?? {
|
|
22993
|
+
version: 1,
|
|
22994
|
+
computerId: randomUUID3(),
|
|
22995
|
+
ownerId: null,
|
|
22996
|
+
connections: []
|
|
22997
|
+
};
|
|
22998
|
+
const save = async (next) => {
|
|
22999
|
+
await ensurePrivateDirectory(directory);
|
|
23000
|
+
const filename = path7.join(directory, "connections.json");
|
|
23001
|
+
const temporary = `${filename}.${process.pid}.tmp`;
|
|
23002
|
+
await fs6.writeFile(
|
|
23003
|
+
temporary,
|
|
23004
|
+
JSON.stringify(registrySchema.parse(next)),
|
|
23005
|
+
{ mode: 384 }
|
|
23006
|
+
);
|
|
23007
|
+
await fs6.rename(temporary, filename);
|
|
23008
|
+
};
|
|
23009
|
+
await save(registry3);
|
|
23010
|
+
return await work(registry3, save);
|
|
23011
|
+
} finally {
|
|
23012
|
+
await release();
|
|
23013
|
+
}
|
|
23014
|
+
}
|
|
23015
|
+
async function forgetSharingConnections() {
|
|
23016
|
+
if (await readSharingRegistry() === null) return;
|
|
23017
|
+
await withSharingRegistry(
|
|
23018
|
+
async (registry3, save) => save({ ...registry3, ownerId: null, connections: [] })
|
|
23019
|
+
);
|
|
23020
|
+
}
|
|
23021
|
+
function sharingConnection(registry3, organizationId, source) {
|
|
23022
|
+
return registry3.connections.find(
|
|
23023
|
+
(connection) => connection.organizationId === organizationId && connection.source === source
|
|
23024
|
+
) ?? null;
|
|
23025
|
+
}
|
|
23026
|
+
|
|
23027
|
+
// src/sharing.ts
|
|
23028
|
+
var SharingSnapshotError = class extends Error {
|
|
23029
|
+
status;
|
|
23030
|
+
constructor(status2) {
|
|
23031
|
+
super(
|
|
23032
|
+
`Could not check project sharing (HTTP ${status2}). Collection is waiting.`
|
|
23033
|
+
);
|
|
23034
|
+
this.name = "SharingSnapshotError";
|
|
23035
|
+
this.status = status2;
|
|
23036
|
+
}
|
|
23037
|
+
};
|
|
23038
|
+
async function fetchSharingSnapshot(config3, request2 = collectorFetch, signal) {
|
|
23039
|
+
const response = await request2(
|
|
23040
|
+
new URL("./collector-sharing", config3.endpoint),
|
|
23041
|
+
{
|
|
23042
|
+
headers: { authorization: `Bearer ${config3.token}` },
|
|
23043
|
+
signal: AbortSignal.any([
|
|
23044
|
+
AbortSignal.timeout(15e3),
|
|
23045
|
+
...signal ? [signal] : []
|
|
23046
|
+
])
|
|
23047
|
+
}
|
|
23048
|
+
);
|
|
23049
|
+
if (!response.ok) {
|
|
23050
|
+
await response.body?.cancel();
|
|
23051
|
+
throw new SharingSnapshotError(response.status);
|
|
23052
|
+
}
|
|
23053
|
+
return collectorSharingSnapshotSchema.parse(await response.json());
|
|
23054
|
+
}
|
|
23055
|
+
function workspaceForSource(snapshot, location, source) {
|
|
23056
|
+
const organizationId = sharingWorkspaceForLocation(
|
|
23057
|
+
snapshot,
|
|
23058
|
+
resolveCaptureLocation(location)
|
|
23059
|
+
);
|
|
23060
|
+
return snapshot.workspaces.some(
|
|
23061
|
+
(workspace) => workspace.organizationId === organizationId && workspace.connectedSources.includes(source)
|
|
23062
|
+
) ? organizationId : null;
|
|
23063
|
+
}
|
|
23064
|
+
function hostCapturePolicy(snapshot, registry3, config3) {
|
|
23065
|
+
const source = collectorSource(config3);
|
|
23066
|
+
const permitted = (location) => {
|
|
23067
|
+
const organizationId = workspaceForSource(snapshot, location, source);
|
|
23068
|
+
return organizationId !== null && sharingConnection(registry3, organizationId, source) !== null;
|
|
23069
|
+
};
|
|
23070
|
+
return {
|
|
23071
|
+
revision: snapshot.revision,
|
|
23072
|
+
batchKey: (event3) => event3.capture ? sharingWorkspaceForLocation(snapshot, event3.capture.reference) ?? "unshared" : "unshared",
|
|
23073
|
+
authorizeRecord: (recordSource, line, filename, cwds) => {
|
|
23074
|
+
const location = captureRecordLocation(
|
|
23075
|
+
recordSource,
|
|
23076
|
+
line,
|
|
23077
|
+
filename,
|
|
23078
|
+
cwds
|
|
23079
|
+
);
|
|
23080
|
+
return location !== null && permitted(location) ? {
|
|
23081
|
+
revision: snapshot.revision,
|
|
23082
|
+
basis: "location",
|
|
23083
|
+
reference: location
|
|
23084
|
+
} : null;
|
|
23085
|
+
},
|
|
23086
|
+
authorizeEvent: (event3) => event3.capture?.basis === "location" && event3.capture.revision === snapshot.revision && permitted(event3.capture.reference) ? event3 : null
|
|
23087
|
+
};
|
|
23088
|
+
}
|
|
23089
|
+
var sessionIndexSchema = record3(
|
|
23090
|
+
string5(),
|
|
23091
|
+
object3({ location: string5().nullable(), at: number4() }).strict()
|
|
23092
|
+
);
|
|
23093
|
+
async function loadSessionIndex(config3) {
|
|
23094
|
+
try {
|
|
23095
|
+
return sessionIndexSchema.parse(
|
|
23096
|
+
JSON.parse(
|
|
23097
|
+
await fs11.readFile(
|
|
23098
|
+
path8.join(config3.dataDirectory, "shared-sessions.json"),
|
|
23099
|
+
"utf8"
|
|
23100
|
+
)
|
|
23101
|
+
)
|
|
23102
|
+
);
|
|
23103
|
+
} catch (error3) {
|
|
23104
|
+
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
23105
|
+
return {};
|
|
23106
|
+
throw error3;
|
|
23107
|
+
}
|
|
23108
|
+
}
|
|
23109
|
+
async function sharingTransport(config3, request2 = collectorFetch, signal, savedRegistry) {
|
|
23110
|
+
const registry3 = savedRegistry ?? await readSharingRegistry();
|
|
23111
|
+
if (registry3 === null)
|
|
23112
|
+
throw new Error(
|
|
23113
|
+
"Choose projects in Iteration before sharing this computer."
|
|
23114
|
+
);
|
|
23115
|
+
const connections = registry3.connections.filter(
|
|
23116
|
+
(connection) => connection.source === collectorSource(config3) && connection.endpoint === config3.endpoint
|
|
23117
|
+
);
|
|
23118
|
+
let snapshot = null;
|
|
23119
|
+
for (const connection of connections) {
|
|
23120
|
+
try {
|
|
23121
|
+
snapshot = await fetchSharingSnapshot(connection, request2, signal);
|
|
23122
|
+
break;
|
|
23123
|
+
} catch (error3) {
|
|
23124
|
+
if (signal?.aborted) throw error3;
|
|
23125
|
+
}
|
|
23126
|
+
}
|
|
23127
|
+
if (snapshot === null || snapshot.computerId !== registry3.computerId || snapshot.ownerId !== registry3.ownerId)
|
|
23128
|
+
throw new Error("Could not verify project sharing. Collection is waiting.");
|
|
23129
|
+
const current = snapshot;
|
|
23130
|
+
const capture = hostCapturePolicy(current, registry3, config3);
|
|
23131
|
+
const index = await loadSessionIndex(config3);
|
|
23132
|
+
const destination = (location) => {
|
|
23133
|
+
const organizationId = workspaceForSource(
|
|
23134
|
+
current,
|
|
23135
|
+
location,
|
|
23136
|
+
collectorSource(config3)
|
|
23137
|
+
);
|
|
23138
|
+
return organizationId === null ? null : sharingConnection(registry3, organizationId, collectorSource(config3));
|
|
23139
|
+
};
|
|
23140
|
+
const forSession = (id) => {
|
|
23141
|
+
const location = index[id]?.location;
|
|
23142
|
+
return location ? destination(location) : null;
|
|
23143
|
+
};
|
|
23144
|
+
const upload = async (init, sendTo) => {
|
|
23145
|
+
const body = string5().parse(init?.body);
|
|
23146
|
+
const parsed = parseTraceEventBatch2(JSON.parse(body));
|
|
23147
|
+
if (!parsed.success) throw new Error(parsed.error);
|
|
23148
|
+
const batches = /* @__PURE__ */ new Map();
|
|
23149
|
+
for (const event3 of parsed.value.events) {
|
|
23150
|
+
if (capture.authorizeEvent(event3) === null)
|
|
23151
|
+
throw new Error("Project sharing changed. Waiting for the next scan.");
|
|
23152
|
+
const connection = destination(event3.capture.reference);
|
|
23153
|
+
if (!connection) throw new Error("The project is no longer shared.");
|
|
23154
|
+
const events = batches.get(connection.organizationId) ?? [];
|
|
23155
|
+
events.push(event3);
|
|
23156
|
+
batches.set(connection.organizationId, events);
|
|
23157
|
+
const previous = index[event3.sessionId];
|
|
23158
|
+
index[event3.sessionId] = {
|
|
23159
|
+
location: previous && previous.location !== event3.capture.reference ? null : event3.capture.reference,
|
|
23160
|
+
at: Date.now()
|
|
23161
|
+
};
|
|
23162
|
+
}
|
|
23163
|
+
const acceptedEventIds = [];
|
|
23164
|
+
let deviceId = "";
|
|
23165
|
+
let retryAfter = null;
|
|
23166
|
+
for (const [organizationId, events] of batches) {
|
|
23167
|
+
const response = await sendTo(organizationId, JSON.stringify({ events }));
|
|
23168
|
+
if (response.status !== 202) return response;
|
|
23169
|
+
retryAfter = response.headers.get("retry-after");
|
|
23170
|
+
const acknowledgement = parseTraceEventAcknowledgement2(
|
|
23171
|
+
await response.json(),
|
|
23172
|
+
events.map((event3) => event3.eventId)
|
|
23173
|
+
);
|
|
23174
|
+
if (!acknowledgement.success) throw new Error(acknowledgement.error);
|
|
23175
|
+
acceptedEventIds.push(...acknowledgement.value.acceptedEventIds);
|
|
23176
|
+
deviceId = acknowledgement.value.deviceId;
|
|
23177
|
+
}
|
|
23178
|
+
const retained = Object.fromEntries(
|
|
23179
|
+
Object.entries(index).sort((left, right) => right[1].at - left[1].at).slice(0, 1e5)
|
|
23180
|
+
);
|
|
23181
|
+
const filename = path8.join(config3.dataDirectory, "shared-sessions.json");
|
|
23182
|
+
const temporary = `${filename}.${process.pid}.tmp`;
|
|
23183
|
+
await fs11.writeFile(temporary, JSON.stringify(retained), { mode: 384 });
|
|
23184
|
+
await fs11.rename(temporary, filename);
|
|
23185
|
+
return Response.json(
|
|
23186
|
+
{ deviceId, acceptedEventIds },
|
|
23187
|
+
{
|
|
23188
|
+
status: 202,
|
|
23189
|
+
headers: retryAfter === null ? {} : { "Retry-After": retryAfter }
|
|
23190
|
+
}
|
|
23191
|
+
);
|
|
23192
|
+
};
|
|
23193
|
+
const send = (input, init) => request2(input, init);
|
|
23194
|
+
const routed = async (input, init) => {
|
|
23195
|
+
const url2 = new URL(String(input));
|
|
23196
|
+
const sendTo = (organizationId, body) => {
|
|
23197
|
+
const connection = sharingConnection(
|
|
23198
|
+
registry3,
|
|
23199
|
+
organizationId,
|
|
23200
|
+
collectorSource(config3)
|
|
23201
|
+
);
|
|
23202
|
+
if (!connection)
|
|
23203
|
+
throw new Error(
|
|
23204
|
+
"The project workspace has no agent connection. Run iteration connect."
|
|
23205
|
+
);
|
|
23206
|
+
return send(input, {
|
|
23207
|
+
...init,
|
|
23208
|
+
headers: {
|
|
23209
|
+
...Object.fromEntries(new Headers(init?.headers)),
|
|
23210
|
+
authorization: `Bearer ${connection.token}`
|
|
23211
|
+
},
|
|
23212
|
+
...body === void 0 ? {} : { body }
|
|
23213
|
+
});
|
|
23214
|
+
};
|
|
23215
|
+
if (url2.pathname === new URL(config3.endpoint).pathname)
|
|
23216
|
+
return upload(init, sendTo);
|
|
23217
|
+
if (url2.pathname.endsWith("/friction-summary")) {
|
|
23218
|
+
const directory = url2.searchParams.get("directory");
|
|
23219
|
+
const connection = directory === null ? null : destination(directory);
|
|
23220
|
+
return connection ? sendTo(connection.organizationId) : Response.json({ weekEpisodes: 0, dayEpisodes: 0 });
|
|
23221
|
+
}
|
|
23222
|
+
if (url2.pathname.endsWith("/codex-billing")) {
|
|
23223
|
+
const body = codexBillingRequestSchema.parse(
|
|
23224
|
+
JSON.parse(String(init?.body))
|
|
23225
|
+
);
|
|
23226
|
+
let matched = 0;
|
|
23227
|
+
for (const workspace of current.workspaces.filter(
|
|
23228
|
+
(workspace2) => workspace2.active && workspace2.connectedSources.includes(collectorSource(config3))
|
|
23229
|
+
)) {
|
|
23230
|
+
const tasks = body.tasks.filter(
|
|
23231
|
+
(task) => forSession(task.threadId)?.organizationId === workspace.organizationId
|
|
23232
|
+
).map((task) => ({
|
|
23233
|
+
...task,
|
|
23234
|
+
descendantThreadIds: task.descendantThreadIds.filter(
|
|
23235
|
+
(id) => forSession(id)?.organizationId === workspace.organizationId
|
|
23236
|
+
)
|
|
23237
|
+
}));
|
|
23238
|
+
if (tasks.length === 0) continue;
|
|
23239
|
+
const ids = new Set(
|
|
23240
|
+
tasks.flatMap((task) => [task.threadId, ...task.descendantThreadIds])
|
|
23241
|
+
);
|
|
23242
|
+
const response = await sendTo(
|
|
23243
|
+
workspace.organizationId,
|
|
23244
|
+
JSON.stringify({
|
|
23245
|
+
...body,
|
|
23246
|
+
tasks,
|
|
23247
|
+
authTurns: body.authTurns.filter((turn) => ids.has(turn.threadId))
|
|
23248
|
+
})
|
|
23249
|
+
);
|
|
23250
|
+
if (!response.ok) return response;
|
|
23251
|
+
matched += object3({ matched: number4() }).parse(await response.json()).matched;
|
|
23252
|
+
}
|
|
23253
|
+
return Response.json({ matched });
|
|
23254
|
+
}
|
|
23255
|
+
if (url2.pathname.endsWith("/cursor-billing")) {
|
|
23256
|
+
const body = cursorBillingRequestSchema.parse(
|
|
23257
|
+
JSON.parse(String(init?.body))
|
|
23258
|
+
);
|
|
23259
|
+
const connection = forSession(`cursor:${body.conversationId}`);
|
|
23260
|
+
return connection ? sendTo(connection.organizationId) : Response.json({ matched: 0 });
|
|
23261
|
+
}
|
|
23262
|
+
if (url2.pathname.endsWith("/collector-progress")) {
|
|
23263
|
+
for (const workspace of current.workspaces.filter(
|
|
23264
|
+
(workspace2) => workspace2.active
|
|
23265
|
+
)) {
|
|
23266
|
+
if (!sharingConnection(
|
|
23267
|
+
registry3,
|
|
23268
|
+
workspace.organizationId,
|
|
23269
|
+
collectorSource(config3)
|
|
23270
|
+
))
|
|
23271
|
+
continue;
|
|
23272
|
+
const response = await sendTo(workspace.organizationId);
|
|
23273
|
+
if (!response.ok) return response;
|
|
23274
|
+
await response.body?.cancel();
|
|
23275
|
+
}
|
|
23276
|
+
return Response.json({ ok: true });
|
|
23277
|
+
}
|
|
23278
|
+
throw new Error("This collector request does not support project sharing.");
|
|
23279
|
+
};
|
|
23280
|
+
return { capture, fetcher: routed, snapshot: current };
|
|
23281
|
+
}
|
|
23282
|
+
async function configForSharedDirectory(config3, directory) {
|
|
23283
|
+
if (config3.destination !== "host") return config3;
|
|
23284
|
+
const transport = await sharingTransport(config3);
|
|
23285
|
+
const organizationId = workspaceForSource(
|
|
23286
|
+
transport.snapshot,
|
|
23287
|
+
directory,
|
|
23288
|
+
collectorSource(config3)
|
|
23289
|
+
);
|
|
23290
|
+
const registry3 = await readSharingRegistry();
|
|
23291
|
+
const connection = organizationId && registry3 ? sharingConnection(registry3, organizationId, collectorSource(config3)) : null;
|
|
23292
|
+
return connection ? {
|
|
23293
|
+
...config3,
|
|
23294
|
+
token: connection.token,
|
|
23295
|
+
organizationId: connection.organizationId,
|
|
23296
|
+
organizationName: connection.organizationName
|
|
23297
|
+
} : null;
|
|
23298
|
+
}
|
|
23299
|
+
|
|
23300
|
+
// src/runtime.ts
|
|
23301
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
23302
|
+
import { promises as fs12 } from "node:fs";
|
|
23303
|
+
import os3 from "node:os";
|
|
23304
|
+
import path17 from "node:path";
|
|
23305
|
+
import { setTimeout as delay2 } from "node:timers/promises";
|
|
23306
|
+
import { fileURLToPath } from "node:url";
|
|
23307
|
+
function runtimePaths(home = os3.homedir()) {
|
|
23308
|
+
const root = path17.join(home, ".config", "iteration", "runtime");
|
|
23309
|
+
return {
|
|
23310
|
+
home,
|
|
23311
|
+
root,
|
|
23312
|
+
versions: path17.join(root, "versions"),
|
|
23313
|
+
staging: path17.join(root, "staging"),
|
|
23314
|
+
state: path17.join(root, "state.json"),
|
|
23315
|
+
settings: path17.join(root, "settings.json"),
|
|
23316
|
+
lock: path17.join(root, "state.lock"),
|
|
23317
|
+
updateLock: path17.join(root, "update.lock"),
|
|
23318
|
+
launcher: path17.join(root, "launcher.js")
|
|
23319
|
+
};
|
|
23320
|
+
}
|
|
23321
|
+
var DEFAULT_UPDATE_REGISTRY = "https://registry.npmjs.org/";
|
|
23322
|
+
var VERSION_PATTERN = /^(0|[1-9]\d{0,5})\.(0|[1-9]\d{0,5})\.(0|[1-9]\d{0,5})$/;
|
|
23323
|
+
var versionSchema = string5().regex(VERSION_PATTERN);
|
|
23324
|
+
var sourceSchema = _enum2(COLLECTOR_SOURCES2);
|
|
22584
23325
|
var timeSchema = number4().int().min(0);
|
|
22585
23326
|
var trialSchema = object3({
|
|
22586
23327
|
version: versionSchema,
|
|
@@ -22665,19 +23406,19 @@ async function ensureRuntimeDirectory(paths) {
|
|
|
22665
23406
|
}
|
|
22666
23407
|
async function writeFileAtomic(filename, contents, mode) {
|
|
22667
23408
|
const temporary = `${filename}.${process.pid}.${Date.now()}.tmp`;
|
|
22668
|
-
const handle = await
|
|
23409
|
+
const handle = await fs12.open(temporary, "w", mode);
|
|
22669
23410
|
try {
|
|
22670
23411
|
await handle.writeFile(contents, "utf8");
|
|
22671
23412
|
await handle.sync();
|
|
22672
23413
|
} finally {
|
|
22673
23414
|
await handle.close();
|
|
22674
23415
|
}
|
|
22675
|
-
await
|
|
23416
|
+
await fs12.rename(temporary, filename);
|
|
22676
23417
|
}
|
|
22677
23418
|
async function readRuntimeState(paths) {
|
|
22678
23419
|
let contents;
|
|
22679
23420
|
try {
|
|
22680
|
-
contents = await
|
|
23421
|
+
contents = await fs12.readFile(paths.state, "utf8");
|
|
22681
23422
|
} catch (error3) {
|
|
22682
23423
|
if (errorCode2(error3) === "ENOENT") return null;
|
|
22683
23424
|
throw error3;
|
|
@@ -22709,7 +23450,7 @@ async function writeRuntimeState(paths, state) {
|
|
|
22709
23450
|
async function readRuntimeSettings(paths) {
|
|
22710
23451
|
let contents;
|
|
22711
23452
|
try {
|
|
22712
|
-
contents = await
|
|
23453
|
+
contents = await fs12.readFile(paths.settings, "utf8");
|
|
22713
23454
|
} catch (error3) {
|
|
22714
23455
|
if (errorCode2(error3) === "ENOENT")
|
|
22715
23456
|
return { autoUpdate: true, registry: DEFAULT_UPDATE_REGISTRY };
|
|
@@ -22738,19 +23479,19 @@ var LockBusyError = class extends Error {
|
|
|
22738
23479
|
async function tryLock(filename) {
|
|
22739
23480
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
22740
23481
|
try {
|
|
22741
|
-
const handle = await
|
|
23482
|
+
const handle = await fs12.open(filename, "wx", 384);
|
|
22742
23483
|
await handle.writeFile(`${process.pid}
|
|
22743
23484
|
`, "utf8");
|
|
22744
23485
|
await handle.close();
|
|
22745
23486
|
return async () => {
|
|
22746
|
-
const owner = await
|
|
23487
|
+
const owner = await fs12.readFile(filename, "utf8").catch(() => "");
|
|
22747
23488
|
if (owner.trim() === String(process.pid))
|
|
22748
|
-
await
|
|
23489
|
+
await fs12.rm(filename, { force: true });
|
|
22749
23490
|
};
|
|
22750
23491
|
} catch (error3) {
|
|
22751
23492
|
if (errorCode2(error3) !== "EEXIST") throw error3;
|
|
22752
23493
|
const owner = Number(
|
|
22753
|
-
(await
|
|
23494
|
+
(await fs12.readFile(filename, "utf8").catch(() => "")).trim()
|
|
22754
23495
|
);
|
|
22755
23496
|
if (Number.isSafeInteger(owner) && owner > 1) {
|
|
22756
23497
|
try {
|
|
@@ -22760,7 +23501,7 @@ async function tryLock(filename) {
|
|
|
22760
23501
|
if (errorCode2(processError) !== "ESRCH") throw processError;
|
|
22761
23502
|
}
|
|
22762
23503
|
}
|
|
22763
|
-
await
|
|
23504
|
+
await fs12.rm(filename, { force: true });
|
|
22764
23505
|
}
|
|
22765
23506
|
}
|
|
22766
23507
|
throw new LockBusyError(`Could not obtain ${filename}.`);
|
|
@@ -22810,15 +23551,15 @@ function confirmTrialSource(state, version3, source, nowMs) {
|
|
|
22810
23551
|
function versionDirectory(paths, version3) {
|
|
22811
23552
|
if (!isVersion(version3))
|
|
22812
23553
|
throw new Error(`Invalid Iteration version ${version3}.`);
|
|
22813
|
-
return
|
|
23554
|
+
return path17.join(paths.versions, version3);
|
|
22814
23555
|
}
|
|
22815
23556
|
function versionEntry(paths, version3, entry) {
|
|
22816
|
-
return
|
|
23557
|
+
return path17.join(versionDirectory(paths, version3), "dist", entry);
|
|
22817
23558
|
}
|
|
22818
23559
|
var packageSchema = object3({ name: literal2("iteration-cli"), version: versionSchema }).loose();
|
|
22819
23560
|
async function readPackageVersion(root) {
|
|
22820
23561
|
const parsed = packageSchema.safeParse(
|
|
22821
|
-
JSON.parse(await
|
|
23562
|
+
JSON.parse(await fs12.readFile(path17.join(root, "package.json"), "utf8"))
|
|
22822
23563
|
);
|
|
22823
23564
|
if (!parsed.success) {
|
|
22824
23565
|
throw new Error(`${root} does not contain an Iteration CLI package.`, {
|
|
@@ -22828,7 +23569,7 @@ async function readPackageVersion(root) {
|
|
|
22828
23569
|
return parsed.data.version;
|
|
22829
23570
|
}
|
|
22830
23571
|
function ownPackageRoot() {
|
|
22831
|
-
return
|
|
23572
|
+
return path17.dirname(path17.dirname(fileURLToPath(import.meta.url)));
|
|
22832
23573
|
}
|
|
22833
23574
|
async function assertInstallablePackage(root, version3) {
|
|
22834
23575
|
const packageVersion = await readPackageVersion(root);
|
|
@@ -22837,12 +23578,12 @@ async function assertInstallablePackage(root, version3) {
|
|
|
22837
23578
|
`The downloaded package is version ${packageVersion}, not ${version3}.`
|
|
22838
23579
|
);
|
|
22839
23580
|
}
|
|
22840
|
-
const
|
|
22841
|
-
while (
|
|
22842
|
-
const directory =
|
|
22843
|
-
for (const entry of await
|
|
22844
|
-
const filename =
|
|
22845
|
-
if (entry.isDirectory())
|
|
23581
|
+
const pending2 = [root];
|
|
23582
|
+
while (pending2.length > 0) {
|
|
23583
|
+
const directory = pending2.pop();
|
|
23584
|
+
for (const entry of await fs12.readdir(directory, { withFileTypes: true })) {
|
|
23585
|
+
const filename = path17.join(directory, entry.name);
|
|
23586
|
+
if (entry.isDirectory()) pending2.push(filename);
|
|
22846
23587
|
else if (!entry.isFile())
|
|
22847
23588
|
throw new Error(
|
|
22848
23589
|
`The package contains an unexpected entry ${filename}.`
|
|
@@ -22850,7 +23591,7 @@ async function assertInstallablePackage(root, version3) {
|
|
|
22850
23591
|
}
|
|
22851
23592
|
}
|
|
22852
23593
|
for (const entry of ["cli.js", "cursor-hook-entry.js", "launcher.js"]) {
|
|
22853
|
-
const stats = await
|
|
23594
|
+
const stats = await fs12.stat(path17.join(root, "dist", entry));
|
|
22854
23595
|
if (!stats.isFile())
|
|
22855
23596
|
throw new Error(`The package is missing dist/${entry}.`);
|
|
22856
23597
|
}
|
|
@@ -22858,48 +23599,48 @@ async function assertInstallablePackage(root, version3) {
|
|
|
22858
23599
|
async function installVersionDirectory(paths, staged, version3) {
|
|
22859
23600
|
await assertInstallablePackage(staged, version3);
|
|
22860
23601
|
const target = versionDirectory(paths, version3);
|
|
22861
|
-
const displaced =
|
|
23602
|
+
const displaced = path17.join(
|
|
22862
23603
|
paths.staging,
|
|
22863
23604
|
`displaced-${version3}-${process.pid}-${Date.now()}`
|
|
22864
23605
|
);
|
|
22865
23606
|
try {
|
|
22866
|
-
await
|
|
23607
|
+
await fs12.rename(target, displaced);
|
|
22867
23608
|
} catch (error3) {
|
|
22868
23609
|
if (errorCode2(error3) !== "ENOENT") throw error3;
|
|
22869
23610
|
}
|
|
22870
|
-
await
|
|
22871
|
-
await
|
|
23611
|
+
await fs12.rename(staged, target);
|
|
23612
|
+
await fs12.rm(displaced, { recursive: true, force: true });
|
|
22872
23613
|
}
|
|
22873
23614
|
async function copyOwnPackage(paths, root, version3) {
|
|
22874
23615
|
await ensureRuntimeDirectory(paths);
|
|
22875
|
-
const staged = await
|
|
23616
|
+
const staged = await fs12.mkdtemp(path17.join(paths.staging, `copy-${version3}-`));
|
|
22876
23617
|
try {
|
|
22877
|
-
const packageRoot =
|
|
22878
|
-
await
|
|
22879
|
-
await
|
|
22880
|
-
|
|
22881
|
-
|
|
23618
|
+
const packageRoot = path17.join(staged, "package");
|
|
23619
|
+
await fs12.mkdir(packageRoot);
|
|
23620
|
+
await fs12.copyFile(
|
|
23621
|
+
path17.join(root, "package.json"),
|
|
23622
|
+
path17.join(packageRoot, "package.json")
|
|
22882
23623
|
);
|
|
22883
|
-
await
|
|
23624
|
+
await fs12.cp(path17.join(root, "dist"), path17.join(packageRoot, "dist"), {
|
|
22884
23625
|
recursive: true,
|
|
22885
23626
|
dereference: true
|
|
22886
23627
|
});
|
|
22887
23628
|
await installVersionDirectory(paths, packageRoot, version3);
|
|
22888
23629
|
} finally {
|
|
22889
|
-
await
|
|
23630
|
+
await fs12.rm(staged, { recursive: true, force: true });
|
|
22890
23631
|
}
|
|
22891
23632
|
}
|
|
22892
23633
|
async function pruneVersions(paths, keep) {
|
|
22893
23634
|
let entries2;
|
|
22894
23635
|
try {
|
|
22895
|
-
entries2 = await
|
|
23636
|
+
entries2 = await fs12.readdir(paths.versions);
|
|
22896
23637
|
} catch (error3) {
|
|
22897
23638
|
if (errorCode2(error3) === "ENOENT") return;
|
|
22898
23639
|
throw error3;
|
|
22899
23640
|
}
|
|
22900
23641
|
for (const entry of entries2) {
|
|
22901
23642
|
if (keep.includes(entry)) continue;
|
|
22902
|
-
await
|
|
23643
|
+
await fs12.rm(path17.join(paths.versions, entry), {
|
|
22903
23644
|
recursive: true,
|
|
22904
23645
|
force: true
|
|
22905
23646
|
});
|
|
@@ -22908,8 +23649,8 @@ async function pruneVersions(paths, keep) {
|
|
|
22908
23649
|
async function launcherMatches(paths, version3) {
|
|
22909
23650
|
try {
|
|
22910
23651
|
const [installed, packaged] = await Promise.all([
|
|
22911
|
-
|
|
22912
|
-
|
|
23652
|
+
fs12.readFile(paths.launcher),
|
|
23653
|
+
fs12.readFile(versionEntry(paths, version3, "launcher.js"))
|
|
22913
23654
|
]);
|
|
22914
23655
|
return installed.equals(packaged);
|
|
22915
23656
|
} catch (error3) {
|
|
@@ -22918,35 +23659,35 @@ async function launcherMatches(paths, version3) {
|
|
|
22918
23659
|
}
|
|
22919
23660
|
}
|
|
22920
23661
|
async function stageLauncher(paths, version3) {
|
|
22921
|
-
const staged =
|
|
23662
|
+
const staged = path17.join(
|
|
22922
23663
|
paths.staging,
|
|
22923
23664
|
`launcher-${version3}-${process.pid}-${Date.now()}.js`
|
|
22924
23665
|
);
|
|
22925
|
-
await
|
|
22926
|
-
await
|
|
23666
|
+
await fs12.copyFile(versionEntry(paths, version3, "launcher.js"), staged);
|
|
23667
|
+
await fs12.chmod(staged, 420);
|
|
22927
23668
|
return staged;
|
|
22928
23669
|
}
|
|
22929
23670
|
async function commitLauncher(paths, staged) {
|
|
22930
|
-
await
|
|
23671
|
+
await fs12.rename(staged, paths.launcher);
|
|
22931
23672
|
}
|
|
22932
23673
|
function rolloutDelayMs(deviceKey) {
|
|
22933
23674
|
const digest3 = createHash4("sha256").update(deviceKey).digest();
|
|
22934
23675
|
return digest3.readUInt32BE(0) % 360 * 6e4;
|
|
22935
23676
|
}
|
|
22936
23677
|
function deviceRolloutKey() {
|
|
22937
|
-
return `${
|
|
23678
|
+
return `${os3.hostname()}\0${os3.homedir()}`;
|
|
22938
23679
|
}
|
|
22939
23680
|
async function stableNodePath() {
|
|
22940
|
-
const actual = await
|
|
23681
|
+
const actual = await fs12.realpath(process.execPath);
|
|
22941
23682
|
const invoked = process.argv0;
|
|
22942
23683
|
const binary = process.platform === "win32" ? "node.exe" : "node";
|
|
22943
23684
|
const candidates = [
|
|
22944
|
-
...invoked !== void 0 &&
|
|
22945
|
-
...(process.env.PATH ?? "").split(
|
|
23685
|
+
...invoked !== void 0 && path17.isAbsolute(invoked) ? [invoked] : [],
|
|
23686
|
+
...(process.env.PATH ?? "").split(path17.delimiter).filter((directory) => path17.isAbsolute(directory)).map((directory) => path17.join(directory, binary))
|
|
22946
23687
|
];
|
|
22947
23688
|
for (const candidate of candidates) {
|
|
22948
23689
|
try {
|
|
22949
|
-
const resolved = await
|
|
23690
|
+
const resolved = await fs12.realpath(candidate);
|
|
22950
23691
|
const same = process.platform === "win32" ? resolved.toLowerCase() === actual.toLowerCase() : resolved === actual;
|
|
22951
23692
|
if (same) return candidate;
|
|
22952
23693
|
} catch (error3) {
|
|
@@ -22979,18 +23720,22 @@ function write(message) {
|
|
|
22979
23720
|
}
|
|
22980
23721
|
async function serveAgentTools(config3) {
|
|
22981
23722
|
let access = null;
|
|
23723
|
+
let scopedConfig = config3;
|
|
22982
23724
|
let version3;
|
|
22983
23725
|
let renewing = null;
|
|
22984
23726
|
const fetchAccess = async () => {
|
|
22985
|
-
if (
|
|
23727
|
+
if (scopedConfig === null)
|
|
22986
23728
|
throw new Error(
|
|
22987
23729
|
"This computer is not connected to Iteration. Run iteration connect."
|
|
22988
23730
|
);
|
|
22989
23731
|
const response = await collectorFetch(
|
|
22990
|
-
new URL(
|
|
23732
|
+
new URL(
|
|
23733
|
+
"./tool-access",
|
|
23734
|
+
validateCollectorEndpoint(scopedConfig.endpoint)
|
|
23735
|
+
),
|
|
22991
23736
|
{
|
|
22992
23737
|
method: "POST",
|
|
22993
|
-
headers: { Authorization: `Bearer ${
|
|
23738
|
+
headers: { Authorization: `Bearer ${scopedConfig.token}` },
|
|
22994
23739
|
signal: AbortSignal.timeout(15e3)
|
|
22995
23740
|
}
|
|
22996
23741
|
);
|
|
@@ -23014,6 +23759,16 @@ async function serveAgentTools(config3) {
|
|
|
23014
23759
|
return renewing;
|
|
23015
23760
|
};
|
|
23016
23761
|
const post = async (body, method, force) => {
|
|
23762
|
+
if (config3?.destination === "host") {
|
|
23763
|
+
const selected = await configForSharedDirectory(config3, process.cwd());
|
|
23764
|
+
if (selected === null)
|
|
23765
|
+
throw new Error(
|
|
23766
|
+
"This project is not shared with Iteration. Choose it in project sharing first."
|
|
23767
|
+
);
|
|
23768
|
+
if (selected.organizationId !== scopedConfig?.organizationId || selected.token !== scopedConfig?.token)
|
|
23769
|
+
access = null;
|
|
23770
|
+
scopedConfig = selected;
|
|
23771
|
+
}
|
|
23017
23772
|
const current = await renew(force);
|
|
23018
23773
|
return fetch(current.url, {
|
|
23019
23774
|
method: "POST",
|
|
@@ -23068,27 +23823,27 @@ async function serveAgentTools(config3) {
|
|
|
23068
23823
|
});
|
|
23069
23824
|
}
|
|
23070
23825
|
};
|
|
23071
|
-
const
|
|
23826
|
+
const pending2 = /* @__PURE__ */ new Set();
|
|
23072
23827
|
for await (const line of readline.createInterface({
|
|
23073
23828
|
input: process.stdin
|
|
23074
23829
|
})) {
|
|
23075
23830
|
if (!line.trim()) continue;
|
|
23076
23831
|
const task = forward(line).catch(() => void 0);
|
|
23077
|
-
|
|
23078
|
-
void task.finally(() =>
|
|
23832
|
+
pending2.add(task);
|
|
23833
|
+
void task.finally(() => pending2.delete(task));
|
|
23079
23834
|
}
|
|
23080
|
-
await Promise.allSettled(
|
|
23835
|
+
await Promise.allSettled(pending2);
|
|
23081
23836
|
}
|
|
23082
23837
|
async function writeAtomically(filename, content) {
|
|
23083
|
-
await
|
|
23084
|
-
const mode = await
|
|
23838
|
+
await fs13.mkdir(path18.dirname(filename), { recursive: true });
|
|
23839
|
+
const mode = await fs13.stat(filename).then((stat) => stat.mode & 511).catch(() => 384);
|
|
23085
23840
|
const temporary = `${filename}.${process.pid}.${Date.now()}.tmp`;
|
|
23086
|
-
await
|
|
23087
|
-
await
|
|
23841
|
+
await fs13.writeFile(temporary, content, { mode });
|
|
23842
|
+
await fs13.rename(temporary, filename);
|
|
23088
23843
|
}
|
|
23089
23844
|
async function readText2(filename) {
|
|
23090
23845
|
try {
|
|
23091
|
-
return await
|
|
23846
|
+
return await fs13.readFile(filename, "utf8");
|
|
23092
23847
|
} catch (error3) {
|
|
23093
23848
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
23094
23849
|
return null;
|
|
@@ -23146,9 +23901,9 @@ args = ${JSON.stringify(args)}
|
|
|
23146
23901
|
await writeAtomically(filename, next);
|
|
23147
23902
|
}
|
|
23148
23903
|
function claudeSettingsFile(claudeHome) {
|
|
23149
|
-
return
|
|
23904
|
+
return path18.resolve(claudeHome) === path18.join(os4.homedir(), ".claude") ? path18.join(os4.homedir(), ".claude.json") : path18.join(claudeHome, ".claude.json");
|
|
23150
23905
|
}
|
|
23151
|
-
var cursorSettingsFile = () =>
|
|
23906
|
+
var cursorSettingsFile = () => path18.join(os4.homedir(), ".cursor", "mcp.json");
|
|
23152
23907
|
async function installAgentTools(homes) {
|
|
23153
23908
|
const command = [
|
|
23154
23909
|
await stableNodePath(),
|
|
@@ -23163,7 +23918,7 @@ async function installAgentTools(homes) {
|
|
|
23163
23918
|
args
|
|
23164
23919
|
});
|
|
23165
23920
|
if (homes.codex !== void 0)
|
|
23166
|
-
await updateCodexServer(
|
|
23921
|
+
await updateCodexServer(path18.join(homes.codex, "config.toml"), command);
|
|
23167
23922
|
if (homes.cursor)
|
|
23168
23923
|
await updateJsonServers(cursorSettingsFile(), { command: program, args });
|
|
23169
23924
|
}
|
|
@@ -23171,7 +23926,7 @@ async function uninstallAgentTools(homes) {
|
|
|
23171
23926
|
if (homes.claude !== void 0)
|
|
23172
23927
|
await updateJsonServers(claudeSettingsFile(homes.claude), null);
|
|
23173
23928
|
if (homes.codex !== void 0)
|
|
23174
|
-
await updateCodexServer(
|
|
23929
|
+
await updateCodexServer(path18.join(homes.codex, "config.toml"), null);
|
|
23175
23930
|
if (homes.cursor) await updateJsonServers(cursorSettingsFile(), null);
|
|
23176
23931
|
}
|
|
23177
23932
|
function agentHomes(config3) {
|
|
@@ -23180,127 +23935,20 @@ function agentHomes(config3) {
|
|
|
23180
23935
|
|
|
23181
23936
|
// src/auto-connect.ts
|
|
23182
23937
|
import { createHmac } from "node:crypto";
|
|
23183
|
-
import { promises as
|
|
23184
|
-
import
|
|
23185
|
-
import
|
|
23186
|
-
|
|
23187
|
-
// ../collector-contracts/dist/index.js
|
|
23188
|
-
var cursorBillingRequestSchema = object3({
|
|
23189
|
-
conversationId: uuid5(),
|
|
23190
|
-
reconcileFromMs: number4().int().nonnegative().nullable(),
|
|
23191
|
-
events: array2(object3({
|
|
23192
|
-
timestamp: number4().int().nonnegative(),
|
|
23193
|
-
kind: _enum2([
|
|
23194
|
-
"included",
|
|
23195
|
-
"paid",
|
|
23196
|
-
"byok",
|
|
23197
|
-
"not_charged",
|
|
23198
|
-
"unknown"
|
|
23199
|
-
]),
|
|
23200
|
-
inputTokens: number4().int().nonnegative().nullable(),
|
|
23201
|
-
outputTokens: number4().int().nonnegative().nullable(),
|
|
23202
|
-
cacheReadTokens: number4().int().nonnegative().nullable(),
|
|
23203
|
-
cacheWriteTokens: number4().int().nonnegative().nullable(),
|
|
23204
|
-
paidUsd: number4().finite().nonnegative().nullable()
|
|
23205
|
-
}).strict()).max(5e3)
|
|
23206
|
-
}).strict();
|
|
23207
|
-
function codexCreditSign(value) {
|
|
23208
|
-
const mantissa = value.split(/[eE]/, 1)[0];
|
|
23209
|
-
if (!/[1-9]/.test(mantissa))
|
|
23210
|
-
return 0;
|
|
23211
|
-
return mantissa.startsWith("-") ? -1 : 1;
|
|
23212
|
-
}
|
|
23213
|
-
var codexBillingRequestSchema = object3({
|
|
23214
|
-
accountFingerprint: string5().regex(/^[0-9a-f]{64}$/),
|
|
23215
|
-
reconcile: boolean4(),
|
|
23216
|
-
tasks: array2(object3({
|
|
23217
|
-
threadId: uuid5(),
|
|
23218
|
-
createdAt: iso_exports2.datetime().nullable(),
|
|
23219
|
-
descendantThreadIds: array2(uuid5()).max(900),
|
|
23220
|
-
dataStatus: _enum2([
|
|
23221
|
-
"available",
|
|
23222
|
-
"partial",
|
|
23223
|
-
"unavailable",
|
|
23224
|
-
"not_queried"
|
|
23225
|
-
]),
|
|
23226
|
-
usageSource: string5().max(100),
|
|
23227
|
-
balanceUsageCredits: string5().regex(/^[+-]?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d{1,3})?$/).max(128).nullable()
|
|
23228
|
-
}).strict()).max(100),
|
|
23229
|
-
authTurns: array2(object3({
|
|
23230
|
-
threadId: uuid5(),
|
|
23231
|
-
turnId: uuid5(),
|
|
23232
|
-
mode: _enum2(["chatgpt", "api_key", "conflicting"])
|
|
23233
|
-
}).strict()).max(5e3)
|
|
23234
|
-
}).strict();
|
|
23235
|
-
var UUID_PATTERN3 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;
|
|
23236
|
-
var episodeCountSchema = number4().int().min(0).max(Number.MAX_SAFE_INTEGER);
|
|
23237
|
-
var collectorFrictionSummarySchema = object3({
|
|
23238
|
-
weekEpisodes: episodeCountSchema,
|
|
23239
|
-
dayEpisodes: episodeCountSchema
|
|
23240
|
-
}).strict().refine((summary) => summary.dayEpisodes <= summary.weekEpisodes, "The last day cannot have more friction episodes than the last week.").describe("dayEpisodes must not exceed weekEpisodes.");
|
|
23241
|
-
var RECENT_MESSAGE_LIMIT = 400;
|
|
23242
|
-
var sessionCountSchema = number4().int().min(0).max(1e6);
|
|
23243
|
-
var collectorHistoryProgressSchema = object3({
|
|
23244
|
-
sessions: sessionCountSchema,
|
|
23245
|
-
readSessions: sessionCountSchema,
|
|
23246
|
-
complete: boolean4(),
|
|
23247
|
-
recentMessages: object3({
|
|
23248
|
-
sent: number4().int().min(0).max(RECENT_MESSAGE_LIMIT),
|
|
23249
|
-
complete: boolean4()
|
|
23250
|
-
}).strict().optional().describe("The newest messages sent ahead of the rest of the history; collectors before 0.17.0 omit it.")
|
|
23251
|
-
}).strict().refine((progress) => progress.readSessions <= progress.sessions, "Read sessions cannot exceed listed sessions.").refine((progress) => !progress.complete || progress.readSessions === progress.sessions, "A complete import has read every listed session.").describe("readSessions must not exceed sessions; complete requires equal counts.");
|
|
23252
|
-
var collectorEnrollmentRequestSchema = object3({
|
|
23253
|
-
deviceName: string5().trim().min(1).max(160).describe("Trimmed before length validation."),
|
|
23254
|
-
organizationId: string5().regex(UUID_PATTERN3).optional(),
|
|
23255
|
-
source: _enum2(COLLECTOR_SOURCES2).optional()
|
|
23256
|
-
}).strict();
|
|
23257
|
-
var collectorExpansionRequestSchema = object3({
|
|
23258
|
-
source: _enum2(COLLECTOR_SOURCES2),
|
|
23259
|
-
token: string5().regex(/^[A-Za-z0-9_-]{43}$/),
|
|
23260
|
-
organizationId: string5().regex(UUID_PATTERN3).optional()
|
|
23261
|
-
}).strict();
|
|
23262
|
-
var collectorEnrollmentResponseSchema = discriminatedUnion2("status", [
|
|
23263
|
-
object3({
|
|
23264
|
-
status: literal2("enrolled"),
|
|
23265
|
-
token: string5().regex(/^[A-Za-z0-9_-]{43}$/),
|
|
23266
|
-
deviceId: string5().regex(UUID_PATTERN3),
|
|
23267
|
-
organizationId: string5().regex(UUID_PATTERN3),
|
|
23268
|
-
organizationName: string5().min(1).max(160)
|
|
23269
|
-
}).strict(),
|
|
23270
|
-
object3({
|
|
23271
|
-
status: literal2("choose_organization"),
|
|
23272
|
-
organizations: array2(object3({
|
|
23273
|
-
id: string5().regex(UUID_PATTERN3),
|
|
23274
|
-
name: string5().min(1).max(160)
|
|
23275
|
-
}).strict()).max(20),
|
|
23276
|
-
hasMore: boolean4()
|
|
23277
|
-
}).strict()
|
|
23278
|
-
]);
|
|
23279
|
-
function parseCollectorEnrollmentResponse(input) {
|
|
23280
|
-
const parsed = collectorEnrollmentResponseSchema.safeParse(input);
|
|
23281
|
-
return parsed.success ? { success: true, value: parsed.data } : {
|
|
23282
|
-
success: false,
|
|
23283
|
-
error: parsed.error.issues[0]?.message ?? "Collector enrollment response is invalid."
|
|
23284
|
-
};
|
|
23285
|
-
}
|
|
23286
|
-
function parseCollectorFrictionSummary(input) {
|
|
23287
|
-
const parsed = collectorFrictionSummarySchema.safeParse(input);
|
|
23288
|
-
return parsed.success ? { success: true, value: parsed.data } : {
|
|
23289
|
-
success: false,
|
|
23290
|
-
error: parsed.error.issues[0]?.message ?? "Friction summary is invalid."
|
|
23291
|
-
};
|
|
23292
|
-
}
|
|
23938
|
+
import { promises as fs22 } from "node:fs";
|
|
23939
|
+
import os10 from "node:os";
|
|
23940
|
+
import path27 from "node:path";
|
|
23293
23941
|
|
|
23294
23942
|
// src/background-service.ts
|
|
23295
|
-
import { promises as
|
|
23296
|
-
import
|
|
23297
|
-
import
|
|
23943
|
+
import { promises as fs19 } from "node:fs";
|
|
23944
|
+
import os8 from "node:os";
|
|
23945
|
+
import path23 from "node:path";
|
|
23298
23946
|
|
|
23299
23947
|
// src/launch-agent.ts
|
|
23300
23948
|
import { execFile as execFileCallback2 } from "node:child_process";
|
|
23301
|
-
import { promises as
|
|
23302
|
-
import
|
|
23303
|
-
import
|
|
23949
|
+
import { promises as fs14 } from "node:fs";
|
|
23950
|
+
import os5 from "node:os";
|
|
23951
|
+
import path19 from "node:path";
|
|
23304
23952
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
23305
23953
|
import { promisify as promisify2 } from "node:util";
|
|
23306
23954
|
var execFile2 = promisify2(execFileCallback2);
|
|
@@ -23313,16 +23961,16 @@ var RESTART_POLL_MS = 200;
|
|
|
23313
23961
|
function xml(value) {
|
|
23314
23962
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23315
23963
|
}
|
|
23316
|
-
function plistPath(source, homeDirectory =
|
|
23317
|
-
return
|
|
23964
|
+
function plistPath(source, homeDirectory = os5.homedir()) {
|
|
23965
|
+
return path19.join(
|
|
23318
23966
|
homeDirectory,
|
|
23319
23967
|
"Library",
|
|
23320
23968
|
"LaunchAgents",
|
|
23321
23969
|
`${label(source)}.plist`
|
|
23322
23970
|
);
|
|
23323
23971
|
}
|
|
23324
|
-
function launchAgentPlist(launcherPath = runtimePaths().launcher, nodePath = process.execPath, logDirectory =
|
|
23325
|
-
|
|
23972
|
+
function launchAgentPlist(launcherPath = runtimePaths().launcher, nodePath = process.execPath, logDirectory = path19.join(os5.homedir(), "Library", "Logs"), dataDirectory2 = path19.join(
|
|
23973
|
+
os5.homedir(),
|
|
23326
23974
|
".config",
|
|
23327
23975
|
"iteration",
|
|
23328
23976
|
"codex-collector"
|
|
@@ -23345,8 +23993,8 @@ function launchAgentPlist(launcherPath = runtimePaths().launcher, nodePath = pro
|
|
|
23345
23993
|
"<key>RunAtLoad</key><true/>",
|
|
23346
23994
|
"<key>KeepAlive</key><true/>",
|
|
23347
23995
|
"<key>ThrottleInterval</key><integer>10</integer>",
|
|
23348
|
-
`<key>StandardOutPath</key><string>${xml(
|
|
23349
|
-
`<key>StandardErrorPath</key><string>${xml(
|
|
23996
|
+
`<key>StandardOutPath</key><string>${xml(path19.join(logDirectory, `iteration-${source}-collector.log`))}</string>`,
|
|
23997
|
+
`<key>StandardErrorPath</key><string>${xml(path19.join(logDirectory, `iteration-${source}-collector.error.log`))}</string>`,
|
|
23350
23998
|
"</dict></plist>"
|
|
23351
23999
|
].join("");
|
|
23352
24000
|
}
|
|
@@ -23426,17 +24074,17 @@ async function installLaunchAgent(dataDirectory2, source = "codex") {
|
|
|
23426
24074
|
throw new Error("Automatic startup is currently supported on macOS only.");
|
|
23427
24075
|
}
|
|
23428
24076
|
const file = plistPath(source);
|
|
23429
|
-
const launchAgents =
|
|
23430
|
-
const logs =
|
|
23431
|
-
await
|
|
23432
|
-
await
|
|
23433
|
-
await
|
|
24077
|
+
const launchAgents = path19.dirname(file);
|
|
24078
|
+
const logs = path19.join(os5.homedir(), "Library", "Logs");
|
|
24079
|
+
await fs14.mkdir(launchAgents, { recursive: true, mode: 493 });
|
|
24080
|
+
await fs14.mkdir(logs, { recursive: true, mode: 493 });
|
|
24081
|
+
await fs14.writeFile(
|
|
23434
24082
|
file,
|
|
23435
24083
|
launchAgentPlist(
|
|
23436
24084
|
void 0,
|
|
23437
24085
|
await stableNodePath(),
|
|
23438
24086
|
void 0,
|
|
23439
|
-
dataDirectory2 ??
|
|
24087
|
+
dataDirectory2 ?? path19.join(os5.homedir(), ".config", "iteration", `${source}-collector`),
|
|
23440
24088
|
source
|
|
23441
24089
|
),
|
|
23442
24090
|
{ encoding: "utf8", mode: 420 }
|
|
@@ -23468,7 +24116,7 @@ async function uninstallLaunchAgent(source = "codex") {
|
|
|
23468
24116
|
}
|
|
23469
24117
|
const file = plistPath(source);
|
|
23470
24118
|
await bootoutIfLoaded(launchctlTarget(), source);
|
|
23471
|
-
await
|
|
24119
|
+
await fs14.rm(file, { force: true });
|
|
23472
24120
|
}
|
|
23473
24121
|
async function launchAgentStatus(source = "codex") {
|
|
23474
24122
|
if (process.platform !== "darwin") return false;
|
|
@@ -23489,21 +24137,21 @@ function launchAgentRunning(output) {
|
|
|
23489
24137
|
|
|
23490
24138
|
// src/runtime-install.ts
|
|
23491
24139
|
import { execFile as execFileCallback4 } from "node:child_process";
|
|
23492
|
-
import { promises as
|
|
24140
|
+
import { promises as fs16 } from "node:fs";
|
|
23493
24141
|
import { promisify as promisify4 } from "node:util";
|
|
23494
24142
|
|
|
23495
24143
|
// src/updater.ts
|
|
23496
24144
|
import { execFile as execFileCallback3 } from "node:child_process";
|
|
23497
24145
|
import { createHash as createHash5 } from "node:crypto";
|
|
23498
|
-
import { createWriteStream as createWriteStream2, promises as
|
|
24146
|
+
import { createWriteStream as createWriteStream2, promises as fs15 } from "node:fs";
|
|
23499
24147
|
import http from "node:http";
|
|
23500
24148
|
import https2 from "node:https";
|
|
23501
|
-
import
|
|
24149
|
+
import path20 from "node:path";
|
|
23502
24150
|
import { Transform } from "node:stream";
|
|
23503
24151
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
23504
24152
|
import { promisify as promisify3 } from "node:util";
|
|
23505
24153
|
var execFile3 = promisify3(execFileCallback3);
|
|
23506
|
-
var TAR = process.platform === "win32" ?
|
|
24154
|
+
var TAR = process.platform === "win32" ? path20.join(process.env.SystemRoot ?? "C:\\Windows", "System32", "tar.exe") : "tar";
|
|
23507
24155
|
var PACKAGE_NAME = "iteration-cli";
|
|
23508
24156
|
var MAX_METADATA_BYTES = 16 * 1024 * 1024;
|
|
23509
24157
|
var MAX_TARBALL_BYTES = 64 * 1024 * 1024;
|
|
@@ -23707,32 +24355,32 @@ async function selfCheck(paths, cliPath, version3) {
|
|
|
23707
24355
|
}
|
|
23708
24356
|
async function installCandidate(paths, candidate, signal) {
|
|
23709
24357
|
await ensureRuntimeDirectory(paths);
|
|
23710
|
-
const staged = await
|
|
23711
|
-
|
|
24358
|
+
const staged = await fs15.mkdtemp(
|
|
24359
|
+
path20.join(paths.staging, `download-${candidate.version}-`)
|
|
23712
24360
|
);
|
|
23713
24361
|
try {
|
|
23714
|
-
const archive =
|
|
24362
|
+
const archive = path20.join(staged, "package.tgz");
|
|
23715
24363
|
await downloadVerified(
|
|
23716
24364
|
candidate.tarball,
|
|
23717
24365
|
candidate.integrity,
|
|
23718
24366
|
archive,
|
|
23719
24367
|
signal
|
|
23720
24368
|
);
|
|
23721
|
-
const extracted =
|
|
23722
|
-
await
|
|
24369
|
+
const extracted = path20.join(staged, "extracted");
|
|
24370
|
+
await fs15.mkdir(extracted, { mode: 448 });
|
|
23723
24371
|
await execFile3(TAR, ["-xzf", archive, "-C", extracted], {
|
|
23724
24372
|
timeout: REQUEST_TIMEOUT_MS,
|
|
23725
24373
|
windowsHide: true
|
|
23726
24374
|
});
|
|
23727
|
-
const packageRoot =
|
|
24375
|
+
const packageRoot = path20.join(extracted, "package");
|
|
23728
24376
|
await selfCheck(
|
|
23729
24377
|
paths,
|
|
23730
|
-
|
|
24378
|
+
path20.join(packageRoot, "dist", "cli.js"),
|
|
23731
24379
|
candidate.version
|
|
23732
24380
|
);
|
|
23733
24381
|
await installVersionDirectory(paths, packageRoot, candidate.version);
|
|
23734
24382
|
} finally {
|
|
23735
|
-
await
|
|
24383
|
+
await fs15.rm(staged, { recursive: true, force: true });
|
|
23736
24384
|
}
|
|
23737
24385
|
}
|
|
23738
24386
|
async function switchVersion(paths, from, version3, previous, expected, nowMs) {
|
|
@@ -23840,7 +24488,7 @@ async function ownVersion() {
|
|
|
23840
24488
|
}
|
|
23841
24489
|
async function exists(filename) {
|
|
23842
24490
|
try {
|
|
23843
|
-
await
|
|
24491
|
+
await fs16.access(filename);
|
|
23844
24492
|
return true;
|
|
23845
24493
|
} catch (error3) {
|
|
23846
24494
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
@@ -23867,7 +24515,7 @@ async function installLauncher(paths, version3) {
|
|
|
23867
24515
|
);
|
|
23868
24516
|
await commitLauncher(paths, staged);
|
|
23869
24517
|
} finally {
|
|
23870
|
-
await
|
|
24518
|
+
await fs16.rm(staged, { force: true });
|
|
23871
24519
|
}
|
|
23872
24520
|
}
|
|
23873
24521
|
async function ensureRuntimeInstalled(expected, paths = runtimePaths()) {
|
|
@@ -23949,9 +24597,9 @@ async function expireRuntimeTrial(nowMs, paths = runtimePaths()) {
|
|
|
23949
24597
|
|
|
23950
24598
|
// src/systemd-user.ts
|
|
23951
24599
|
import { execFile as execFileCallback5 } from "node:child_process";
|
|
23952
|
-
import { promises as
|
|
23953
|
-
import
|
|
23954
|
-
import
|
|
24600
|
+
import { promises as fs17 } from "node:fs";
|
|
24601
|
+
import os6 from "node:os";
|
|
24602
|
+
import path21 from "node:path";
|
|
23955
24603
|
import { promisify as promisify5 } from "node:util";
|
|
23956
24604
|
var execFile5 = promisify5(execFileCallback5);
|
|
23957
24605
|
var SYSTEMCTL_TIMEOUT_MS = 1e4;
|
|
@@ -24021,8 +24669,8 @@ function unitName(source) {
|
|
|
24021
24669
|
return `iteration-${source}-collector.service`;
|
|
24022
24670
|
}
|
|
24023
24671
|
function unitPath(source) {
|
|
24024
|
-
return
|
|
24025
|
-
|
|
24672
|
+
return path21.join(
|
|
24673
|
+
os6.homedir(),
|
|
24026
24674
|
".config",
|
|
24027
24675
|
"systemd",
|
|
24028
24676
|
"user",
|
|
@@ -24030,8 +24678,8 @@ function unitPath(source) {
|
|
|
24030
24678
|
);
|
|
24031
24679
|
}
|
|
24032
24680
|
function systemdLogPath(source) {
|
|
24033
|
-
return
|
|
24034
|
-
|
|
24681
|
+
return path21.join(
|
|
24682
|
+
os6.homedir(),
|
|
24035
24683
|
".config",
|
|
24036
24684
|
"iteration",
|
|
24037
24685
|
"logs",
|
|
@@ -24043,13 +24691,13 @@ function unitValue(value) {
|
|
|
24043
24691
|
throw new Error("Collector paths cannot contain line breaks.");
|
|
24044
24692
|
return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("%", "%%")}"`;
|
|
24045
24693
|
}
|
|
24046
|
-
function systemdUserUnit(launcherPath = runtimePaths().launcher, nodePath = process.execPath, dataDirectory2 =
|
|
24047
|
-
|
|
24694
|
+
function systemdUserUnit(launcherPath = runtimePaths().launcher, nodePath = process.execPath, dataDirectory2 = path21.join(
|
|
24695
|
+
os6.homedir(),
|
|
24048
24696
|
".config",
|
|
24049
24697
|
"iteration",
|
|
24050
24698
|
"codex-collector"
|
|
24051
|
-
), source = "codex", outputPath =
|
|
24052
|
-
|
|
24699
|
+
), source = "codex", outputPath = path21.join(
|
|
24700
|
+
os6.homedir(),
|
|
24053
24701
|
".config",
|
|
24054
24702
|
"iteration",
|
|
24055
24703
|
"logs",
|
|
@@ -24079,10 +24727,10 @@ async function systemctl(...args) {
|
|
|
24079
24727
|
async function installSystemdUserService(dataDirectory2, source) {
|
|
24080
24728
|
await ensureSystemdUserLinger();
|
|
24081
24729
|
const file = unitPath(source);
|
|
24082
|
-
const logDirectory =
|
|
24083
|
-
await
|
|
24084
|
-
await
|
|
24085
|
-
await
|
|
24730
|
+
const logDirectory = path21.dirname(systemdLogPath(source));
|
|
24731
|
+
await fs17.mkdir(path21.dirname(file), { recursive: true, mode: 448 });
|
|
24732
|
+
await fs17.mkdir(logDirectory, { recursive: true, mode: 448 });
|
|
24733
|
+
await fs17.writeFile(
|
|
24086
24734
|
file,
|
|
24087
24735
|
systemdUserUnit(void 0, await stableNodePath(), dataDirectory2, source),
|
|
24088
24736
|
{
|
|
@@ -24098,14 +24746,14 @@ async function installSystemdUserService(dataDirectory2, source) {
|
|
|
24098
24746
|
async function uninstallSystemdUserService(source) {
|
|
24099
24747
|
const file = unitPath(source);
|
|
24100
24748
|
try {
|
|
24101
|
-
await
|
|
24749
|
+
await fs17.access(file);
|
|
24102
24750
|
} catch (error3) {
|
|
24103
24751
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
24104
24752
|
return;
|
|
24105
24753
|
throw error3;
|
|
24106
24754
|
}
|
|
24107
24755
|
await systemctl("disable", "--now", unitName(source));
|
|
24108
|
-
await
|
|
24756
|
+
await fs17.rm(file);
|
|
24109
24757
|
await systemctl("daemon-reload");
|
|
24110
24758
|
}
|
|
24111
24759
|
async function systemdUserServiceStatus(source) {
|
|
@@ -24124,9 +24772,9 @@ async function systemdUserServiceStatus(source) {
|
|
|
24124
24772
|
|
|
24125
24773
|
// src/task-scheduler.ts
|
|
24126
24774
|
import { execFile as execFileCallback6 } from "node:child_process";
|
|
24127
|
-
import { promises as
|
|
24128
|
-
import
|
|
24129
|
-
import
|
|
24775
|
+
import { promises as fs18 } from "node:fs";
|
|
24776
|
+
import os7 from "node:os";
|
|
24777
|
+
import path22 from "node:path";
|
|
24130
24778
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
24131
24779
|
import { promisify as promisify6 } from "node:util";
|
|
24132
24780
|
|
|
@@ -24228,14 +24876,14 @@ function scheduledTaskName(source, sid) {
|
|
|
24228
24876
|
return `iteration-${sid}-${source}-collector`;
|
|
24229
24877
|
}
|
|
24230
24878
|
function servicesDirectory() {
|
|
24231
|
-
return
|
|
24879
|
+
return path22.join(os7.homedir(), ".config", "iteration", "services");
|
|
24232
24880
|
}
|
|
24233
24881
|
function taskDefinitionPath(source) {
|
|
24234
|
-
return
|
|
24882
|
+
return path22.join(servicesDirectory(), `iteration-${source}-collector.xml`);
|
|
24235
24883
|
}
|
|
24236
24884
|
function windowsLogPath(source) {
|
|
24237
|
-
return
|
|
24238
|
-
|
|
24885
|
+
return path22.join(
|
|
24886
|
+
os7.homedir(),
|
|
24239
24887
|
".config",
|
|
24240
24888
|
"iteration",
|
|
24241
24889
|
"logs",
|
|
@@ -24243,8 +24891,8 @@ function windowsLogPath(source) {
|
|
|
24243
24891
|
);
|
|
24244
24892
|
}
|
|
24245
24893
|
function windowsOutputPath(source) {
|
|
24246
|
-
return
|
|
24247
|
-
|
|
24894
|
+
return path22.join(
|
|
24895
|
+
os7.homedir(),
|
|
24248
24896
|
".config",
|
|
24249
24897
|
"iteration",
|
|
24250
24898
|
"logs",
|
|
@@ -24259,7 +24907,7 @@ function scheduledTaskScript(task) {
|
|
|
24259
24907
|
"--data-directory",
|
|
24260
24908
|
task.dataDirectory
|
|
24261
24909
|
].map(windowsArgument).join(" ");
|
|
24262
|
-
const errorLog =
|
|
24910
|
+
const errorLog = path22.join(
|
|
24263
24911
|
task.home,
|
|
24264
24912
|
".config",
|
|
24265
24913
|
"iteration",
|
|
@@ -24379,7 +25027,7 @@ function AccountSid([string]$account) {
|
|
|
24379
25027
|
async function schtasks(source, operation, ...args) {
|
|
24380
25028
|
try {
|
|
24381
25029
|
await execFile6(
|
|
24382
|
-
|
|
25030
|
+
path22.join(
|
|
24383
25031
|
process.env.SystemRoot ?? "C:\\Windows",
|
|
24384
25032
|
"System32",
|
|
24385
25033
|
"schtasks.exe"
|
|
@@ -24423,7 +25071,7 @@ async function desiredTask(dataDirectory2, source) {
|
|
|
24423
25071
|
userSid: await currentUserSid(),
|
|
24424
25072
|
nodePath: await stableNodePath(),
|
|
24425
25073
|
launcher: runtimePaths().launcher,
|
|
24426
|
-
home:
|
|
25074
|
+
home: os7.homedir()
|
|
24427
25075
|
};
|
|
24428
25076
|
}
|
|
24429
25077
|
async function scheduledTaskUsesRuntime(dataDirectory2, source) {
|
|
@@ -24435,10 +25083,10 @@ async function scheduledTaskUsesRuntime(dataDirectory2, source) {
|
|
|
24435
25083
|
async function installScheduledTask(dataDirectory2, source) {
|
|
24436
25084
|
const file = taskDefinitionPath(source);
|
|
24437
25085
|
await ensurePrivateDirectory(servicesDirectory());
|
|
24438
|
-
await ensurePrivateDirectory(
|
|
25086
|
+
await ensurePrivateDirectory(path22.dirname(windowsLogPath(source)));
|
|
24439
25087
|
await stopTask(source);
|
|
24440
25088
|
const xml2 = scheduledTaskXml(await desiredTask(dataDirectory2, source));
|
|
24441
|
-
await
|
|
25089
|
+
await fs18.writeFile(
|
|
24442
25090
|
file,
|
|
24443
25091
|
Buffer.concat([Buffer.from([255, 254]), Buffer.from(xml2, "utf16le")])
|
|
24444
25092
|
);
|
|
@@ -24448,7 +25096,7 @@ async function installScheduledTask(dataDirectory2, source) {
|
|
|
24448
25096
|
}
|
|
24449
25097
|
async function uninstallScheduledTask(source) {
|
|
24450
25098
|
if (await stopTask(source)) await schtasks(source, "/Delete", "/F");
|
|
24451
|
-
await
|
|
25099
|
+
await fs18.rm(taskDefinitionPath(source), { force: true });
|
|
24452
25100
|
}
|
|
24453
25101
|
async function scheduledTaskStatus(source) {
|
|
24454
25102
|
return (await observedTask(source))?.state === 4;
|
|
@@ -24472,8 +25120,8 @@ async function prepareBackgroundPersistence() {
|
|
|
24472
25120
|
function collectorLogPath(source) {
|
|
24473
25121
|
if (process.platform === "linux") return systemdLogPath(source);
|
|
24474
25122
|
if (process.platform === "win32") return windowsLogPath(source);
|
|
24475
|
-
return
|
|
24476
|
-
|
|
25123
|
+
return path23.join(
|
|
25124
|
+
os8.homedir(),
|
|
24477
25125
|
"Library",
|
|
24478
25126
|
"Logs",
|
|
24479
25127
|
`iteration-${source}-collector.error.log`
|
|
@@ -24507,7 +25155,7 @@ async function backgroundServiceDefinition(source) {
|
|
|
24507
25155
|
requireCollectorPlatform();
|
|
24508
25156
|
const file = process.platform === "darwin" ? plistPath(source) : process.platform === "linux" ? unitPath(source) : taskDefinitionPath(source);
|
|
24509
25157
|
try {
|
|
24510
|
-
return await
|
|
25158
|
+
return await fs19.readFile(
|
|
24511
25159
|
file,
|
|
24512
25160
|
process.platform === "win32" ? "utf16le" : "utf8"
|
|
24513
25161
|
);
|
|
@@ -24556,7 +25204,7 @@ function serviceDataDirectory(definition, platform) {
|
|
|
24556
25204
|
(_, slashes) => slashes.slice(0, slashes.length / 2)
|
|
24557
25205
|
);
|
|
24558
25206
|
}
|
|
24559
|
-
if (!directory || !(platform === "win32" ?
|
|
25207
|
+
if (!directory || !(platform === "win32" ? path23.win32 : path23.posix).isAbsolute(directory))
|
|
24560
25208
|
throw new Error("Could not read the collector service data directory.");
|
|
24561
25209
|
return directory;
|
|
24562
25210
|
}
|
|
@@ -24579,15 +25227,15 @@ async function backgroundServiceUsesRuntime(directory, source) {
|
|
|
24579
25227
|
}
|
|
24580
25228
|
|
|
24581
25229
|
// src/config-paths.ts
|
|
24582
|
-
import
|
|
24583
|
-
import
|
|
25230
|
+
import os9 from "node:os";
|
|
25231
|
+
import path24 from "node:path";
|
|
24584
25232
|
function defaultCollectorDirectory(source) {
|
|
24585
|
-
return
|
|
25233
|
+
return path24.join(os9.homedir(), ".config", "iteration", `${source}-collector`);
|
|
24586
25234
|
}
|
|
24587
25235
|
|
|
24588
25236
|
// src/cursor-hooks.ts
|
|
24589
|
-
import { promises as
|
|
24590
|
-
import
|
|
25237
|
+
import { promises as fs20 } from "node:fs";
|
|
25238
|
+
import path25 from "node:path";
|
|
24591
25239
|
function shellQuote(value) {
|
|
24592
25240
|
if (process.platform === "win32") return `"${value}"`;
|
|
24593
25241
|
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
@@ -24622,8 +25270,8 @@ async function readHooks(filename) {
|
|
|
24622
25270
|
let contents;
|
|
24623
25271
|
let mode = 384;
|
|
24624
25272
|
try {
|
|
24625
|
-
contents = await
|
|
24626
|
-
mode = (await
|
|
25273
|
+
contents = await fs20.readFile(filename, "utf8");
|
|
25274
|
+
mode = (await fs20.stat(filename)).mode & 511;
|
|
24627
25275
|
} catch (error3) {
|
|
24628
25276
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
24629
25277
|
return { file: {}, mode };
|
|
@@ -24645,14 +25293,14 @@ async function readHooks(filename) {
|
|
|
24645
25293
|
return { file: checked.data, mode };
|
|
24646
25294
|
}
|
|
24647
25295
|
async function writeHooks(filename, file, mode) {
|
|
24648
|
-
await
|
|
24649
|
-
const target = await
|
|
25296
|
+
await fs20.mkdir(path25.dirname(filename), { recursive: true, mode: 448 });
|
|
25297
|
+
const target = await fs20.realpath(filename).catch((error3) => {
|
|
24650
25298
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
24651
25299
|
return filename;
|
|
24652
25300
|
throw error3;
|
|
24653
25301
|
});
|
|
24654
25302
|
const temporary = `${target}.iteration-${process.pid}.tmp`;
|
|
24655
|
-
const handle = await
|
|
25303
|
+
const handle = await fs20.open(temporary, "w", mode);
|
|
24656
25304
|
try {
|
|
24657
25305
|
await handle.writeFile(`${JSON.stringify(file, null, 2)}
|
|
24658
25306
|
`, "utf8");
|
|
@@ -24660,14 +25308,14 @@ async function writeHooks(filename, file, mode) {
|
|
|
24660
25308
|
} finally {
|
|
24661
25309
|
await handle.close();
|
|
24662
25310
|
}
|
|
24663
|
-
await
|
|
25311
|
+
await fs20.rename(temporary, target);
|
|
24664
25312
|
}
|
|
24665
25313
|
function entries(value) {
|
|
24666
25314
|
return value ?? [];
|
|
24667
25315
|
}
|
|
24668
25316
|
async function installCursorHooks(cursorHome, dataDirectory2) {
|
|
24669
25317
|
await ensurePrivateDirectory(cursorHookDirectory(dataDirectory2));
|
|
24670
|
-
const filename =
|
|
25318
|
+
const filename = path25.join(cursorHome, "hooks.json");
|
|
24671
25319
|
const { file, mode } = await readHooks(filename);
|
|
24672
25320
|
const hooks = { ...file.hooks };
|
|
24673
25321
|
const nodePath = await stableNodePath();
|
|
@@ -24681,7 +25329,7 @@ async function installCursorHooks(cursorHome, dataDirectory2) {
|
|
|
24681
25329
|
await writeHooks(filename, { ...file, version: 1, hooks }, mode);
|
|
24682
25330
|
}
|
|
24683
25331
|
async function uninstallCursorHooks(cursorHome, dataDirectory2) {
|
|
24684
|
-
const filename =
|
|
25332
|
+
const filename = path25.join(cursorHome, "hooks.json");
|
|
24685
25333
|
const { file, mode } = await readHooks(filename);
|
|
24686
25334
|
const hooks = { ...file.hooks };
|
|
24687
25335
|
let changed = false;
|
|
@@ -24711,8 +25359,8 @@ function silenceCollectorLogs() {
|
|
|
24711
25359
|
// src/turn-summary.ts
|
|
24712
25360
|
import { execFile as execFileCallback7 } from "node:child_process";
|
|
24713
25361
|
import { createHash as createHash6 } from "node:crypto";
|
|
24714
|
-
import { promises as
|
|
24715
|
-
import
|
|
25362
|
+
import { promises as fs21 } from "node:fs";
|
|
25363
|
+
import path26 from "node:path";
|
|
24716
25364
|
import { promisify as promisify7 } from "node:util";
|
|
24717
25365
|
import { crc32 } from "node:zlib";
|
|
24718
25366
|
var execFile7 = promisify7(execFileCallback7);
|
|
@@ -24723,13 +25371,13 @@ var REQUEST_TIMEOUT_MS2 = 1e4;
|
|
|
24723
25371
|
var HOOK_TIMEOUT_SECONDS = 1;
|
|
24724
25372
|
var REQUEST_KEY_PATTERN = /^\d{1,10}$/;
|
|
24725
25373
|
function turnSummaryPaths(dataDirectory2) {
|
|
24726
|
-
const directory =
|
|
25374
|
+
const directory = path26.join(dataDirectory2, "turn-summary");
|
|
24727
25375
|
return {
|
|
24728
25376
|
directory,
|
|
24729
|
-
script:
|
|
24730
|
-
requests:
|
|
24731
|
-
lines:
|
|
24732
|
-
shown:
|
|
25377
|
+
script: path26.join(directory, "stop-hook.sh"),
|
|
25378
|
+
requests: path26.join(directory, "requests"),
|
|
25379
|
+
lines: path26.join(directory, "lines"),
|
|
25380
|
+
shown: path26.join(directory, "shown")
|
|
24733
25381
|
};
|
|
24734
25382
|
}
|
|
24735
25383
|
function shellQuote2(value) {
|
|
@@ -24785,7 +25433,7 @@ async function sessionStartCommand(config3) {
|
|
|
24785
25433
|
function agentHookFile(config3) {
|
|
24786
25434
|
if ("cursorHome" in config3)
|
|
24787
25435
|
throw new Error("Cursor uses its own trace hooks.");
|
|
24788
|
-
return "claudeHome" in config3 ?
|
|
25436
|
+
return "claudeHome" in config3 ? path26.join(config3.claudeHome, "settings.json") : path26.join(config3.codexHome, "hooks.json");
|
|
24789
25437
|
}
|
|
24790
25438
|
var hookEntrySchema2 = object3({
|
|
24791
25439
|
command: string5().optional(),
|
|
@@ -24802,8 +25450,8 @@ async function readHookFile(filename) {
|
|
|
24802
25450
|
let contents;
|
|
24803
25451
|
let mode = 384;
|
|
24804
25452
|
try {
|
|
24805
|
-
contents = await
|
|
24806
|
-
mode = (await
|
|
25453
|
+
contents = await fs21.readFile(filename, "utf8");
|
|
25454
|
+
mode = (await fs21.stat(filename)).mode & 511;
|
|
24807
25455
|
} catch (error3) {
|
|
24808
25456
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT") {
|
|
24809
25457
|
return { file: {}, mode };
|
|
@@ -24829,19 +25477,19 @@ async function readHookFile(filename) {
|
|
|
24829
25477
|
return { file: parsed.data, mode };
|
|
24830
25478
|
}
|
|
24831
25479
|
async function writeHookFile(filename, file, mode) {
|
|
24832
|
-
await
|
|
24833
|
-
const target = await
|
|
25480
|
+
await fs21.mkdir(path26.dirname(filename), { recursive: true });
|
|
25481
|
+
const target = await fs21.realpath(filename).catch((error3) => {
|
|
24834
25482
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
24835
25483
|
return filename;
|
|
24836
25484
|
throw error3;
|
|
24837
25485
|
});
|
|
24838
25486
|
const temporary = `${target}.iteration-${process.pid}.tmp`;
|
|
24839
|
-
await
|
|
25487
|
+
await fs21.writeFile(temporary, `${JSON.stringify(file, null, 2)}
|
|
24840
25488
|
`, {
|
|
24841
25489
|
encoding: "utf8",
|
|
24842
25490
|
mode
|
|
24843
25491
|
});
|
|
24844
|
-
await
|
|
25492
|
+
await fs21.rename(temporary, target);
|
|
24845
25493
|
}
|
|
24846
25494
|
function withoutCommand(groups, command) {
|
|
24847
25495
|
return groups.flatMap((group) => {
|
|
@@ -24866,24 +25514,24 @@ async function installTurnSummary(config3) {
|
|
|
24866
25514
|
[paths.requests, paths.lines, paths.shown].map(ensurePrivateDirectory)
|
|
24867
25515
|
);
|
|
24868
25516
|
if (process.platform !== "win32") {
|
|
24869
|
-
await
|
|
25517
|
+
await fs21.writeFile(paths.script, hookScript(paths), {
|
|
24870
25518
|
encoding: "utf8",
|
|
24871
25519
|
mode: 448
|
|
24872
25520
|
});
|
|
24873
|
-
await
|
|
25521
|
+
await fs21.chmod(paths.script, 448);
|
|
24874
25522
|
}
|
|
24875
25523
|
if ("claudeHome" in config3) {
|
|
24876
25524
|
const gitPaths = claudeGitHookPaths(config3.dataDirectory);
|
|
24877
25525
|
await ensurePrivateDirectory(gitPaths.queue);
|
|
24878
25526
|
if (process.platform !== "win32") {
|
|
24879
|
-
await
|
|
25527
|
+
await fs21.writeFile(
|
|
24880
25528
|
gitPaths.script,
|
|
24881
25529
|
`#!/bin/sh
|
|
24882
25530
|
exec ${shellQuote2(await stableNodePath())} ${shellQuote2(runtimePaths().launcher)} claude-git-hook ${shellQuote2(config3.dataDirectory)}
|
|
24883
25531
|
`,
|
|
24884
25532
|
{ encoding: "utf8", mode: 448 }
|
|
24885
25533
|
);
|
|
24886
|
-
await
|
|
25534
|
+
await fs21.chmod(gitPaths.script, 448);
|
|
24887
25535
|
}
|
|
24888
25536
|
}
|
|
24889
25537
|
const filename = agentHookFile(config3);
|
|
@@ -24973,19 +25621,19 @@ async function captureTurnSummary(dataDirectory2) {
|
|
|
24973
25621
|
windowsHide: true
|
|
24974
25622
|
}
|
|
24975
25623
|
));
|
|
24976
|
-
directory =
|
|
25624
|
+
directory = path26.normalize(directory.trim());
|
|
24977
25625
|
} catch {
|
|
24978
25626
|
quiet();
|
|
24979
25627
|
return;
|
|
24980
25628
|
}
|
|
24981
|
-
if (!
|
|
25629
|
+
if (!path26.isAbsolute(directory)) {
|
|
24982
25630
|
quiet();
|
|
24983
25631
|
return;
|
|
24984
25632
|
}
|
|
24985
25633
|
const key = String(crc32(directory));
|
|
24986
25634
|
const paths = turnSummaryPaths(dataDirectory2);
|
|
24987
25635
|
try {
|
|
24988
|
-
await
|
|
25636
|
+
await fs21.writeFile(path26.join(paths.requests, key), `${directory}
|
|
24989
25637
|
`, {
|
|
24990
25638
|
encoding: "utf8",
|
|
24991
25639
|
mode: 384
|
|
@@ -24994,7 +25642,7 @@ async function captureTurnSummary(dataDirectory2) {
|
|
|
24994
25642
|
}
|
|
24995
25643
|
let lines;
|
|
24996
25644
|
try {
|
|
24997
|
-
lines = await
|
|
25645
|
+
lines = await fs21.readFile(path26.join(paths.lines, key), "utf8");
|
|
24998
25646
|
} catch {
|
|
24999
25647
|
quiet();
|
|
25000
25648
|
return;
|
|
@@ -25006,7 +25654,7 @@ async function captureTurnSummary(dataDirectory2) {
|
|
|
25006
25654
|
}
|
|
25007
25655
|
let last;
|
|
25008
25656
|
try {
|
|
25009
|
-
last = (await
|
|
25657
|
+
last = (await fs21.readFile(path26.join(paths.shown, key), "utf8")).split("\n")[0] ?? null;
|
|
25010
25658
|
} catch {
|
|
25011
25659
|
last = null;
|
|
25012
25660
|
}
|
|
@@ -25015,7 +25663,7 @@ async function captureTurnSummary(dataDirectory2) {
|
|
|
25015
25663
|
return;
|
|
25016
25664
|
}
|
|
25017
25665
|
try {
|
|
25018
|
-
await
|
|
25666
|
+
await fs21.writeFile(path26.join(paths.shown, key), `${message}
|
|
25019
25667
|
`, {
|
|
25020
25668
|
encoding: "utf8",
|
|
25021
25669
|
mode: 384
|
|
@@ -25075,7 +25723,7 @@ async function fetchSummary(config3, directory, fetcher, signal) {
|
|
|
25075
25723
|
}
|
|
25076
25724
|
async function readRequest(filename) {
|
|
25077
25725
|
try {
|
|
25078
|
-
const [directory] = (await
|
|
25726
|
+
const [directory] = (await fs21.readFile(filename, "utf8")).split("\n");
|
|
25079
25727
|
return directory ?? null;
|
|
25080
25728
|
} catch (error3) {
|
|
25081
25729
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
@@ -25084,33 +25732,40 @@ async function readRequest(filename) {
|
|
|
25084
25732
|
}
|
|
25085
25733
|
}
|
|
25086
25734
|
async function writeLine(paths, key, directory, message) {
|
|
25087
|
-
const filename =
|
|
25735
|
+
const filename = path26.join(paths.lines, key);
|
|
25088
25736
|
if (message === null) {
|
|
25089
|
-
await
|
|
25737
|
+
await fs21.rm(filename, { force: true });
|
|
25090
25738
|
return;
|
|
25091
25739
|
}
|
|
25092
25740
|
const temporary = `${filename}.${process.pid}.tmp`;
|
|
25093
|
-
await
|
|
25741
|
+
await fs21.writeFile(
|
|
25094
25742
|
temporary,
|
|
25095
25743
|
`${directory}
|
|
25096
25744
|
${JSON.stringify({ systemMessage: message })}
|
|
25097
25745
|
`,
|
|
25098
25746
|
{ encoding: "utf8", mode: 384 }
|
|
25099
25747
|
);
|
|
25100
|
-
await
|
|
25748
|
+
await fs21.rename(temporary, filename);
|
|
25101
25749
|
}
|
|
25102
25750
|
async function forget(paths, key) {
|
|
25103
25751
|
await Promise.all(
|
|
25104
25752
|
[paths.requests, paths.lines, paths.shown].map(
|
|
25105
|
-
(directory) =>
|
|
25753
|
+
(directory) => fs21.rm(path26.join(directory, key), { force: true })
|
|
25106
25754
|
)
|
|
25107
25755
|
);
|
|
25108
25756
|
}
|
|
25757
|
+
async function clearTurnSummaries(config3) {
|
|
25758
|
+
const paths = turnSummaryPaths(config3.dataDirectory);
|
|
25759
|
+
for (const directory of [paths.lines, paths.shown]) {
|
|
25760
|
+
await fs21.rm(directory, { recursive: true, force: true });
|
|
25761
|
+
await ensurePrivateDirectory(directory);
|
|
25762
|
+
}
|
|
25763
|
+
}
|
|
25109
25764
|
async function refreshTurnSummaries(config3, refreshedAt, nowMs, fetcher = collectorFetch, signal) {
|
|
25110
25765
|
const paths = turnSummaryPaths(config3.dataDirectory);
|
|
25111
25766
|
let keys;
|
|
25112
25767
|
try {
|
|
25113
|
-
keys = await
|
|
25768
|
+
keys = await fs21.readdir(paths.requests);
|
|
25114
25769
|
} catch (error3) {
|
|
25115
25770
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
25116
25771
|
return 0;
|
|
@@ -25120,8 +25775,8 @@ async function refreshTurnSummaries(config3, refreshedAt, nowMs, fetcher = colle
|
|
|
25120
25775
|
for (const key of keys.sort()) {
|
|
25121
25776
|
signal?.throwIfAborted();
|
|
25122
25777
|
if (!REQUEST_KEY_PATTERN.test(key)) continue;
|
|
25123
|
-
const filename =
|
|
25124
|
-
const stats = await
|
|
25778
|
+
const filename = path26.join(paths.requests, key);
|
|
25779
|
+
const stats = await fs21.stat(filename).catch((error3) => {
|
|
25125
25780
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
25126
25781
|
return null;
|
|
25127
25782
|
throw error3;
|
|
@@ -25188,8 +25843,8 @@ async function codexTurnSummaryApproval(config3) {
|
|
|
25188
25843
|
if (handlerIndex < 0) continue;
|
|
25189
25844
|
let configToml;
|
|
25190
25845
|
try {
|
|
25191
|
-
configToml = await
|
|
25192
|
-
|
|
25846
|
+
configToml = await fs21.readFile(
|
|
25847
|
+
path26.join(config3.codexHome, "config.toml"),
|
|
25193
25848
|
"utf8"
|
|
25194
25849
|
);
|
|
25195
25850
|
} catch (error3) {
|
|
@@ -25216,25 +25871,25 @@ var consentSchema = object3({
|
|
|
25216
25871
|
sourceHomes: object3({
|
|
25217
25872
|
codex: string5(),
|
|
25218
25873
|
claude: string5(),
|
|
25219
|
-
cursor: string5().default(
|
|
25874
|
+
cursor: string5().default(path27.join(os10.homedir(), ".cursor"))
|
|
25220
25875
|
}).strict()
|
|
25221
25876
|
}).strict();
|
|
25222
25877
|
function consentPath() {
|
|
25223
|
-
return
|
|
25878
|
+
return path27.join(os10.homedir(), ".config", "iteration", "auto-connect.json");
|
|
25224
25879
|
}
|
|
25225
25880
|
async function removeMachineConsent() {
|
|
25226
|
-
await
|
|
25881
|
+
await fs22.rm(consentPath(), { force: true });
|
|
25227
25882
|
}
|
|
25228
25883
|
async function detectedSources(sourceHomes) {
|
|
25229
25884
|
const detected = [];
|
|
25230
25885
|
for (const source of COLLECTOR_SOURCES2) {
|
|
25231
|
-
const directory =
|
|
25886
|
+
const directory = path27.join(
|
|
25232
25887
|
sourceHomes[source],
|
|
25233
25888
|
source === "codex" ? "sessions" : "projects"
|
|
25234
25889
|
);
|
|
25235
25890
|
let stats;
|
|
25236
25891
|
try {
|
|
25237
|
-
stats = await
|
|
25892
|
+
stats = await fs22.stat(directory);
|
|
25238
25893
|
} catch (error3) {
|
|
25239
25894
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
25240
25895
|
continue;
|
|
@@ -25254,7 +25909,7 @@ async function detectedSources(sourceHomes) {
|
|
|
25254
25909
|
async function loadMachineConsent() {
|
|
25255
25910
|
let text;
|
|
25256
25911
|
try {
|
|
25257
|
-
text = await
|
|
25912
|
+
text = await fs22.readFile(consentPath(), "utf8");
|
|
25258
25913
|
} catch (error3) {
|
|
25259
25914
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
25260
25915
|
return null;
|
|
@@ -25276,43 +25931,6 @@ async function loadMachineConsent() {
|
|
|
25276
25931
|
validateCollectorEndpoint(parsed.data.endpoint);
|
|
25277
25932
|
return parsed.data;
|
|
25278
25933
|
}
|
|
25279
|
-
async function saveMachineConsent(consent) {
|
|
25280
|
-
const parsed = consentSchema.parse(consent);
|
|
25281
|
-
validateCollectorEndpoint(parsed.endpoint);
|
|
25282
|
-
const filename = consentPath();
|
|
25283
|
-
await ensurePrivateDirectory(path25.dirname(filename));
|
|
25284
|
-
const temporary = `${filename}.${process.pid}.tmp`;
|
|
25285
|
-
const handle = await fs20.open(temporary, "w", 384);
|
|
25286
|
-
try {
|
|
25287
|
-
await handle.writeFile(`${JSON.stringify(parsed)}
|
|
25288
|
-
`, "utf8");
|
|
25289
|
-
await handle.sync();
|
|
25290
|
-
} finally {
|
|
25291
|
-
await handle.close();
|
|
25292
|
-
}
|
|
25293
|
-
await fs20.rename(temporary, filename);
|
|
25294
|
-
}
|
|
25295
|
-
async function readCollectorScope(anchor3) {
|
|
25296
|
-
const endpoint = validateCollectorEndpoint(anchor3.endpoint);
|
|
25297
|
-
const response = await collectorFetch(
|
|
25298
|
-
new URL("./collector-auth/expand", endpoint),
|
|
25299
|
-
{
|
|
25300
|
-
headers: { Authorization: `Bearer ${anchor3.token}` },
|
|
25301
|
-
signal: AbortSignal.timeout(1e4)
|
|
25302
|
-
}
|
|
25303
|
-
);
|
|
25304
|
-
const payload = await response.json();
|
|
25305
|
-
if (!response.ok) {
|
|
25306
|
-
const error3 = object3({ error: string5() }).safeParse(payload);
|
|
25307
|
-
throw new Error(
|
|
25308
|
-
`Could not identify the collector workspace: ${error3.success ? error3.data.error : `HTTP ${response.status}`}`
|
|
25309
|
-
);
|
|
25310
|
-
}
|
|
25311
|
-
const parsed = object3({ organizationId: uuid5(), organizationName: string5().min(1) }).strict().safeParse(payload);
|
|
25312
|
-
if (!parsed.success)
|
|
25313
|
-
throw new Error("Iteration returned an invalid collector workspace.");
|
|
25314
|
-
return parsed.data;
|
|
25315
|
-
}
|
|
25316
25934
|
async function expandSource(anchor3, source, organizationId, request2 = collectorFetch) {
|
|
25317
25935
|
const endpoint = validateCollectorEndpoint(anchor3.endpoint);
|
|
25318
25936
|
const token = createHmac("sha256", anchor3.token).update(`iteration-source:${source}`, "utf8").digest("base64url");
|
|
@@ -25386,7 +26004,7 @@ async function connectNewDetectedSources(anchor3, consent) {
|
|
|
25386
26004
|
if (source === consent.anchorSource) continue;
|
|
25387
26005
|
const paths = collectorPaths(defaultCollectorDirectory(source));
|
|
25388
26006
|
try {
|
|
25389
|
-
await
|
|
26007
|
+
await fs22.access(paths.config);
|
|
25390
26008
|
continue;
|
|
25391
26009
|
} catch (error3) {
|
|
25392
26010
|
if (!(error3 instanceof Error && "code" in error3 && error3.code === "ENOENT"))
|
|
@@ -25424,18 +26042,28 @@ async function connectNewDetectedSources(anchor3, consent) {
|
|
|
25424
26042
|
}
|
|
25425
26043
|
return connected;
|
|
25426
26044
|
}
|
|
25427
|
-
async function connectedConfig(source) {
|
|
25428
|
-
const paths = collectorPaths(
|
|
26045
|
+
async function connectedConfig(source, directory = defaultCollectorDirectory(source)) {
|
|
26046
|
+
const paths = collectorPaths(directory);
|
|
25429
26047
|
try {
|
|
25430
|
-
await
|
|
26048
|
+
await fs22.access(paths.config);
|
|
25431
26049
|
} catch (error3) {
|
|
25432
26050
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
25433
26051
|
return null;
|
|
25434
26052
|
throw error3;
|
|
25435
26053
|
}
|
|
25436
|
-
|
|
26054
|
+
const config3 = await loadConfig(paths);
|
|
26055
|
+
if (collectorSource(config3) !== source)
|
|
26056
|
+
throw new Error(
|
|
26057
|
+
`The collector directory belongs to a different coding agent: ${directory}.`
|
|
26058
|
+
);
|
|
26059
|
+
return config3;
|
|
25437
26060
|
}
|
|
25438
26061
|
|
|
26062
|
+
// src/sharing-connect.ts
|
|
26063
|
+
import os12 from "node:os";
|
|
26064
|
+
import path30 from "node:path";
|
|
26065
|
+
import { setTimeout as delay6 } from "node:timers/promises";
|
|
26066
|
+
|
|
25439
26067
|
// src/clerk-device-auth.ts
|
|
25440
26068
|
import { execFile as execFile8 } from "node:child_process";
|
|
25441
26069
|
import { promisify as promisify8 } from "node:util";
|
|
@@ -25810,8 +26438,8 @@ async function clerkDeviceSignIn(configuration) {
|
|
|
25810
26438
|
stopSpinner();
|
|
25811
26439
|
}
|
|
25812
26440
|
}
|
|
25813
|
-
async function enrollDevice(endpoint, accessToken, deviceName, organizationId, source) {
|
|
25814
|
-
const body = { deviceName };
|
|
26441
|
+
async function enrollDevice(endpoint, accessToken, deviceName, organizationId, source, computerId) {
|
|
26442
|
+
const body = { deviceName, ...computerId === void 0 ? {} : { computerId } };
|
|
25815
26443
|
if (source !== "codex") body.source = source;
|
|
25816
26444
|
if (organizationId !== null) body.organizationId = organizationId;
|
|
25817
26445
|
const response = await collectorFetch(
|
|
@@ -25882,7 +26510,7 @@ async function chooseOrganization(organizations, hasMore) {
|
|
|
25882
26510
|
}
|
|
25883
26511
|
return organization.id;
|
|
25884
26512
|
}
|
|
25885
|
-
async function authenticateAndEnrollCollector(endpoint, deviceNameValue, organizationId, source = "codex") {
|
|
26513
|
+
async function authenticateAndEnrollCollector(endpoint, deviceNameValue, organizationId, source = "codex", computerId) {
|
|
25886
26514
|
const deviceName = string5().trim().min(1).max(160).safeParse(deviceNameValue);
|
|
25887
26515
|
if (!deviceName.success) {
|
|
25888
26516
|
throw new Error("--device-name must contain 1 to 160 characters.");
|
|
@@ -25894,34 +26522,703 @@ async function authenticateAndEnrollCollector(endpoint, deviceNameValue, organiz
|
|
|
25894
26522
|
clerkAccessToken,
|
|
25895
26523
|
deviceName.data,
|
|
25896
26524
|
organizationId,
|
|
25897
|
-
source
|
|
26525
|
+
source,
|
|
26526
|
+
computerId
|
|
26527
|
+
);
|
|
26528
|
+
if (result.status === "choose_organization") {
|
|
26529
|
+
const selectedOrganizationId = await chooseOrganization(
|
|
26530
|
+
result.organizations,
|
|
26531
|
+
result.hasMore
|
|
26532
|
+
);
|
|
26533
|
+
result = await enrollDevice(
|
|
26534
|
+
endpoint,
|
|
26535
|
+
clerkAccessToken,
|
|
26536
|
+
deviceName.data,
|
|
26537
|
+
selectedOrganizationId,
|
|
26538
|
+
source,
|
|
26539
|
+
computerId
|
|
26540
|
+
);
|
|
26541
|
+
}
|
|
26542
|
+
if (result.status !== "enrolled") {
|
|
26543
|
+
throw new Error("The collector could not be assigned to that workspace.");
|
|
26544
|
+
}
|
|
26545
|
+
return {
|
|
26546
|
+
token: result.token,
|
|
26547
|
+
deviceId: result.deviceId,
|
|
26548
|
+
organizationId: result.organizationId,
|
|
26549
|
+
organizationName: result.organizationName
|
|
26550
|
+
};
|
|
26551
|
+
}
|
|
26552
|
+
|
|
26553
|
+
// src/sharing-inventory.ts
|
|
26554
|
+
import { promises as fs23 } from "node:fs";
|
|
26555
|
+
import path28 from "node:path";
|
|
26556
|
+
var MAX_FILES = 1e4;
|
|
26557
|
+
var MAX_HEADER_BYTES = 256 * 1024;
|
|
26558
|
+
async function projectInventory(computerId, homes) {
|
|
26559
|
+
const projects = /* @__PURE__ */ new Map();
|
|
26560
|
+
let inventoryBytes = 256;
|
|
26561
|
+
let truncated = false;
|
|
26562
|
+
for (const source of ["codex", "claude", "cursor"]) {
|
|
26563
|
+
const home = homes[source];
|
|
26564
|
+
if (home === void 0) continue;
|
|
26565
|
+
const files = await sourceFilesNewestFirst(source, home);
|
|
26566
|
+
truncated ||= files.length > MAX_FILES;
|
|
26567
|
+
for (const file of files.slice(0, MAX_FILES)) {
|
|
26568
|
+
const handle = await fs23.open(file.filename, "r");
|
|
26569
|
+
let contents;
|
|
26570
|
+
try {
|
|
26571
|
+
const buffer = Buffer.alloc(Math.min(file.size, MAX_HEADER_BYTES));
|
|
26572
|
+
const result = await handle.read(buffer, 0, buffer.length, 0);
|
|
26573
|
+
contents = buffer.subarray(0, result.bytesRead).toString("utf8");
|
|
26574
|
+
} finally {
|
|
26575
|
+
await handle.close();
|
|
26576
|
+
}
|
|
26577
|
+
const cwds = {};
|
|
26578
|
+
const locations = /* @__PURE__ */ new Set();
|
|
26579
|
+
for (const line of contents.split("\n")) {
|
|
26580
|
+
const location = captureRecordLocation(
|
|
26581
|
+
source,
|
|
26582
|
+
line,
|
|
26583
|
+
file.filename,
|
|
26584
|
+
cwds
|
|
26585
|
+
);
|
|
26586
|
+
if (location !== null) locations.add(location);
|
|
26587
|
+
}
|
|
26588
|
+
for (const location of locations) {
|
|
26589
|
+
const bytes = Buffer.byteLength(JSON.stringify(location), "utf8") + 1200;
|
|
26590
|
+
if (!projects.has(location) && (projects.size >= 1e3 || inventoryBytes + bytes > 480 * 1024)) {
|
|
26591
|
+
truncated = true;
|
|
26592
|
+
continue;
|
|
26593
|
+
}
|
|
26594
|
+
if (!projects.has(location)) inventoryBytes += bytes;
|
|
26595
|
+
const project = projects.get(location) ?? {
|
|
26596
|
+
name: (path28.basename(location) || location).slice(0, 160),
|
|
26597
|
+
sessions: 0,
|
|
26598
|
+
sources: /* @__PURE__ */ new Set()
|
|
26599
|
+
};
|
|
26600
|
+
project.sessions += file.session ? 1 : 0;
|
|
26601
|
+
project.sources.add(source);
|
|
26602
|
+
projects.set(location, project);
|
|
26603
|
+
}
|
|
26604
|
+
}
|
|
26605
|
+
}
|
|
26606
|
+
return {
|
|
26607
|
+
computerId,
|
|
26608
|
+
truncated,
|
|
26609
|
+
projects: [...projects].map(([folder, project]) => ({
|
|
26610
|
+
path: folder,
|
|
26611
|
+
name: project.name,
|
|
26612
|
+
sessions: project.sessions,
|
|
26613
|
+
sources: [...project.sources]
|
|
26614
|
+
})).sort(
|
|
26615
|
+
(left, right) => left.name.localeCompare(right.name) || left.path.localeCompare(right.path)
|
|
26616
|
+
)
|
|
26617
|
+
};
|
|
26618
|
+
}
|
|
26619
|
+
|
|
26620
|
+
// src/folder-browser.ts
|
|
26621
|
+
import { promises as fs24 } from "node:fs";
|
|
26622
|
+
import os11 from "node:os";
|
|
26623
|
+
import path29 from "node:path";
|
|
26624
|
+
import { setTimeout as delay5 } from "node:timers/promises";
|
|
26625
|
+
function breadcrumbs(directory, home) {
|
|
26626
|
+
const entries2 = [];
|
|
26627
|
+
let current = directory;
|
|
26628
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
26629
|
+
entries2.unshift({
|
|
26630
|
+
path: current,
|
|
26631
|
+
name: current === home ? "Home" : path29.basename(current) || current
|
|
26632
|
+
});
|
|
26633
|
+
const parent = path29.dirname(current);
|
|
26634
|
+
if (parent === current) break;
|
|
26635
|
+
current = parent;
|
|
26636
|
+
}
|
|
26637
|
+
return entries2;
|
|
26638
|
+
}
|
|
26639
|
+
async function folderEntryIsDirectory(entry, filename) {
|
|
26640
|
+
if (!entry.isSymbolicLink()) return entry.isDirectory();
|
|
26641
|
+
try {
|
|
26642
|
+
return (await fs24.stat(filename)).isDirectory();
|
|
26643
|
+
} catch (error3) {
|
|
26644
|
+
if (error3 instanceof Error && "code" in error3 && ["ENOENT", "EACCES", "EPERM", "ELOOP"].includes(String(error3.code)))
|
|
26645
|
+
return false;
|
|
26646
|
+
throw error3;
|
|
26647
|
+
}
|
|
26648
|
+
}
|
|
26649
|
+
async function readFolderListing(request2, home = os11.homedir(), signal) {
|
|
26650
|
+
signal?.throwIfAborted();
|
|
26651
|
+
const directory = await fs24.realpath(request2.directory ?? home);
|
|
26652
|
+
const parent = path29.dirname(directory);
|
|
26653
|
+
const entries2 = [];
|
|
26654
|
+
let scanned = 0, bytes = 0, truncated = false;
|
|
26655
|
+
const deadline = Date.now() + 3e3;
|
|
26656
|
+
const handle = await fs24.opendir(directory);
|
|
26657
|
+
for await (const entry of handle) {
|
|
26658
|
+
signal?.throwIfAborted();
|
|
26659
|
+
if (++scanned > 1e4 || Date.now() > deadline) {
|
|
26660
|
+
truncated = true;
|
|
26661
|
+
break;
|
|
26662
|
+
}
|
|
26663
|
+
if (!request2.showHidden && entry.name.startsWith(".") || !entry.name.toLowerCase().includes(request2.query.toLowerCase()))
|
|
26664
|
+
continue;
|
|
26665
|
+
const filename = path29.join(directory, entry.name);
|
|
26666
|
+
if (!captureLocationSchema.safeParse(filename).success) continue;
|
|
26667
|
+
if (!await folderEntryIsDirectory(entry, filename)) continue;
|
|
26668
|
+
const folder = { name: entry.name, path: filename };
|
|
26669
|
+
bytes += Buffer.byteLength(JSON.stringify(folder), "utf8");
|
|
26670
|
+
if (entries2.length === 200 || bytes > 384 * 1024) {
|
|
26671
|
+
truncated = true;
|
|
26672
|
+
break;
|
|
26673
|
+
}
|
|
26674
|
+
entries2.push(folder);
|
|
26675
|
+
}
|
|
26676
|
+
entries2.sort((left, right) => left.name.localeCompare(right.name));
|
|
26677
|
+
return {
|
|
26678
|
+
directory,
|
|
26679
|
+
name: path29.basename(directory) || directory,
|
|
26680
|
+
parent: parent === directory ? null : parent,
|
|
26681
|
+
breadcrumbs: breadcrumbs(directory, home),
|
|
26682
|
+
entries: entries2,
|
|
26683
|
+
truncated
|
|
26684
|
+
};
|
|
26685
|
+
}
|
|
26686
|
+
async function listingState(request2, signal) {
|
|
26687
|
+
try {
|
|
26688
|
+
return {
|
|
26689
|
+
status: "ready",
|
|
26690
|
+
request: request2,
|
|
26691
|
+
listing: await readFolderListing(request2, void 0, signal)
|
|
26692
|
+
};
|
|
26693
|
+
} catch (error3) {
|
|
26694
|
+
if (signal.aborted) throw error3;
|
|
26695
|
+
const code = error3 instanceof Error && "code" in error3 ? String(error3.code) : "";
|
|
26696
|
+
return {
|
|
26697
|
+
status: "error",
|
|
26698
|
+
request: request2,
|
|
26699
|
+
reason: ["EACCES", "EPERM"].includes(code) ? "permission" : ["ENOENT", "ENOTDIR", "ELOOP"].includes(code) ? "missing" : "unavailable"
|
|
26700
|
+
};
|
|
26701
|
+
}
|
|
26702
|
+
}
|
|
26703
|
+
async function folderBrowserCycle(config3, signal, fetcher) {
|
|
26704
|
+
const registry3 = await readSharingRegistry();
|
|
26705
|
+
if (!registry3 || registry3.ownerId === null)
|
|
26706
|
+
throw new Error(
|
|
26707
|
+
"The computer connection is unavailable for folder selection."
|
|
26708
|
+
);
|
|
26709
|
+
const deadline = AbortSignal.any([signal, AbortSignal.timeout(1e4)]);
|
|
26710
|
+
for (const connection of registry3.connections.filter(
|
|
26711
|
+
(connection2) => connection2.endpoint === config3.endpoint
|
|
26712
|
+
)) {
|
|
26713
|
+
const url2 = new URL("./collector-folders", connection.endpoint);
|
|
26714
|
+
const headers = { authorization: `Bearer ${connection.token}` };
|
|
26715
|
+
const response = await fetcher(url2, { headers, signal: deadline });
|
|
26716
|
+
if (response.status === 401) {
|
|
26717
|
+
await response.body?.cancel();
|
|
26718
|
+
continue;
|
|
26719
|
+
}
|
|
26720
|
+
if (!response.ok)
|
|
26721
|
+
throw new Error(`Folder selection is waiting (HTTP ${response.status}).`);
|
|
26722
|
+
const command = collectorFolderCommandSchema.nullable().parse(await response.json());
|
|
26723
|
+
if (command === null) return false;
|
|
26724
|
+
if (command.request.expiresAtMs <= Date.now()) return true;
|
|
26725
|
+
const state = await listingState(command.request, deadline);
|
|
26726
|
+
const reply = await fetcher(url2, {
|
|
26727
|
+
method: "POST",
|
|
26728
|
+
headers: { ...headers, "content-type": "application/json" },
|
|
26729
|
+
body: JSON.stringify(
|
|
26730
|
+
collectorFolderReplySchema.parse({
|
|
26731
|
+
organizationId: command.organizationId,
|
|
26732
|
+
state
|
|
26733
|
+
})
|
|
26734
|
+
),
|
|
26735
|
+
signal: deadline
|
|
26736
|
+
});
|
|
26737
|
+
if (!reply.ok)
|
|
26738
|
+
throw new Error(
|
|
26739
|
+
`Folder selection could not finish (HTTP ${reply.status}).`
|
|
26740
|
+
);
|
|
26741
|
+
object3({ accepted: boolean4() }).strict().parse(await reply.json());
|
|
26742
|
+
return true;
|
|
26743
|
+
}
|
|
26744
|
+
throw new Error("Reconnect this computer to use folder selection.");
|
|
26745
|
+
}
|
|
26746
|
+
async function runFolderBrowser(config3, signal, fetcher = collectorFetch) {
|
|
26747
|
+
const paths = collectorPaths(path29.join(sharingDirectory(), "folder-browser"));
|
|
26748
|
+
try {
|
|
26749
|
+
while (!signal.aborted) {
|
|
26750
|
+
const release = await tryAcquireCollectorLock(paths);
|
|
26751
|
+
if (release === null) {
|
|
26752
|
+
await delay5(1500, void 0, { signal });
|
|
26753
|
+
continue;
|
|
26754
|
+
}
|
|
26755
|
+
try {
|
|
26756
|
+
while (!signal.aborted) {
|
|
26757
|
+
let pause = 1500;
|
|
26758
|
+
try {
|
|
26759
|
+
if (await folderBrowserCycle(config3, signal, fetcher)) pause = 250;
|
|
26760
|
+
} catch (error3) {
|
|
26761
|
+
if (signal.aborted) throw error3;
|
|
26762
|
+
collectorLogger({
|
|
26763
|
+
level: "warning",
|
|
26764
|
+
operation: "folder_browser",
|
|
26765
|
+
message: "Folder selection is waiting for the computer connection. Retrying.",
|
|
26766
|
+
errorName: error3 instanceof Error ? error3.name : "UnknownError"
|
|
26767
|
+
});
|
|
26768
|
+
pause = 5e3;
|
|
26769
|
+
}
|
|
26770
|
+
await delay5(pause, void 0, { signal });
|
|
26771
|
+
}
|
|
26772
|
+
} finally {
|
|
26773
|
+
await release();
|
|
26774
|
+
}
|
|
26775
|
+
}
|
|
26776
|
+
} catch (error3) {
|
|
26777
|
+
if (!signal.aborted) throw error3;
|
|
26778
|
+
}
|
|
26779
|
+
}
|
|
26780
|
+
|
|
26781
|
+
// src/sharing-connect.ts
|
|
26782
|
+
function textOption(options, key) {
|
|
26783
|
+
const value = options[key];
|
|
26784
|
+
if (value === void 0) return null;
|
|
26785
|
+
const parsed = string5().trim().min(1).safeParse(value);
|
|
26786
|
+
if (!parsed.success) throw new Error(`--${key} requires a value.`);
|
|
26787
|
+
return parsed.data;
|
|
26788
|
+
}
|
|
26789
|
+
function sourceConfig(source, home, directory, endpoint, enrollment) {
|
|
26790
|
+
const common = {
|
|
26791
|
+
destination: "host",
|
|
26792
|
+
endpoint,
|
|
26793
|
+
dataDirectory: directory,
|
|
26794
|
+
token: enrollment.token,
|
|
26795
|
+
organizationId: enrollment.organizationId,
|
|
26796
|
+
organizationName: enrollment.organizationName
|
|
26797
|
+
};
|
|
26798
|
+
return source === "codex" ? { ...common, codexHome: home } : source === "claude" ? { ...common, source, claudeHome: home } : { ...common, source, cursorHome: home };
|
|
26799
|
+
}
|
|
26800
|
+
async function prepareSource(config3, previous) {
|
|
26801
|
+
const source = collectorSource(config3);
|
|
26802
|
+
const paths = collectorPaths(config3.dataDirectory);
|
|
26803
|
+
if (previous !== null) await uninstallBackgroundService(source);
|
|
26804
|
+
const release = await waitForCollectorLock(paths);
|
|
26805
|
+
try {
|
|
26806
|
+
if (previous === null) {
|
|
26807
|
+
const home = "codexHome" in config3 ? config3.codexHome : "claudeHome" in config3 ? config3.claudeHome : config3.cursorHome;
|
|
26808
|
+
await saveState(
|
|
26809
|
+
await prepareSourceInitialState(source, home, true),
|
|
26810
|
+
paths
|
|
26811
|
+
);
|
|
26812
|
+
} else await ensureStartableState(previous, paths);
|
|
26813
|
+
await saveConfig(config3, paths);
|
|
26814
|
+
} finally {
|
|
26815
|
+
await release();
|
|
26816
|
+
}
|
|
26817
|
+
}
|
|
26818
|
+
async function startSource(config3) {
|
|
26819
|
+
const source = collectorSource(config3);
|
|
26820
|
+
if ("cursorHome" in config3)
|
|
26821
|
+
await installCursorHooks(config3.cursorHome, config3.dataDirectory);
|
|
26822
|
+
await installBackgroundService(config3.dataDirectory, source);
|
|
26823
|
+
try {
|
|
26824
|
+
if (source !== "cursor") await installTurnSummary(config3);
|
|
26825
|
+
await installAgentTools(agentHomes(config3));
|
|
26826
|
+
} catch (error3) {
|
|
26827
|
+
process.stdout.write(
|
|
26828
|
+
`Connected, but the agent integration needs attention: ${error3 instanceof Error ? error3.message : "setup failed"}
|
|
26829
|
+
`
|
|
26830
|
+
);
|
|
26831
|
+
}
|
|
26832
|
+
}
|
|
26833
|
+
async function prepareConnectedSource(source, home, directory, connection, previous) {
|
|
26834
|
+
const config3 = previous === null ? sourceConfig(source, home, directory, connection.endpoint, connection) : {
|
|
26835
|
+
...previous,
|
|
26836
|
+
destination: "host",
|
|
26837
|
+
...previous.organizationId === connection.organizationId ? {
|
|
26838
|
+
token: connection.token,
|
|
26839
|
+
organizationName: connection.organizationName
|
|
26840
|
+
} : {}
|
|
26841
|
+
};
|
|
26842
|
+
if (previous === null || previous.destination !== "host" || previous.token !== config3.token)
|
|
26843
|
+
await prepareSource(config3, previous);
|
|
26844
|
+
return config3;
|
|
26845
|
+
}
|
|
26846
|
+
async function localConnectionOptions(options, selectedSource) {
|
|
26847
|
+
const existing = await Promise.all(
|
|
26848
|
+
COLLECTOR_SOURCES2.map(
|
|
26849
|
+
(source) => connectedConfig(
|
|
26850
|
+
source,
|
|
26851
|
+
source === selectedSource ? textOption(options, "data-directory") ?? void 0 : void 0
|
|
26852
|
+
)
|
|
26853
|
+
)
|
|
26854
|
+
);
|
|
26855
|
+
const homes = Object.fromEntries(
|
|
26856
|
+
COLLECTOR_SOURCES2.map((source, index) => {
|
|
26857
|
+
const previous = existing[index];
|
|
26858
|
+
const home = previous ? "codexHome" in previous ? previous.codexHome : "claudeHome" in previous ? previous.claudeHome : previous.cursorHome : sourceHome(source);
|
|
26859
|
+
return [
|
|
26860
|
+
source,
|
|
26861
|
+
path30.resolve(textOption(options, `${source}-home`) ?? home)
|
|
26862
|
+
];
|
|
26863
|
+
})
|
|
26864
|
+
);
|
|
26865
|
+
const detected = await detectedSources(homes);
|
|
26866
|
+
const sources = selectedSource ? [selectedSource] : [
|
|
26867
|
+
.../* @__PURE__ */ new Set([
|
|
26868
|
+
...detected,
|
|
26869
|
+
...COLLECTOR_SOURCES2.filter((_, index) => existing[index] !== null)
|
|
26870
|
+
])
|
|
26871
|
+
];
|
|
26872
|
+
const endpoint = validateCollectorEndpoint(
|
|
26873
|
+
textOption(options, "endpoint") ?? existing.find((config3) => config3 !== null)?.endpoint ?? "https://useiteration.com/api/traces/ingest"
|
|
26874
|
+
).toString();
|
|
26875
|
+
if (existing.some((config3) => config3 !== null && config3.endpoint !== endpoint))
|
|
26876
|
+
throw new Error(
|
|
26877
|
+
"This computer is connected to another Iteration server. Sign out before changing servers."
|
|
26878
|
+
);
|
|
26879
|
+
return { existing, homes, sources, endpoint };
|
|
26880
|
+
}
|
|
26881
|
+
function assertSharingOwner(ownerId, nextOwnerId) {
|
|
26882
|
+
if (ownerId !== null && ownerId !== nextOwnerId)
|
|
26883
|
+
throw new Error(
|
|
26884
|
+
"This computer is connected to another Iteration account. Run iteration logout before switching accounts."
|
|
26885
|
+
);
|
|
26886
|
+
}
|
|
26887
|
+
async function connectedWorkspace(registry3, requested, endpoint) {
|
|
26888
|
+
if (requested !== null) return requested;
|
|
26889
|
+
const workspaces = [
|
|
26890
|
+
...new Map(
|
|
26891
|
+
registry3.connections.filter((connection) => connection.endpoint === endpoint).map((connection) => [connection.organizationId, connection])
|
|
26892
|
+
).values()
|
|
26893
|
+
];
|
|
26894
|
+
if (workspaces.length < 2) return workspaces[0]?.organizationId ?? null;
|
|
26895
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
|
26896
|
+
throw new Error(
|
|
26897
|
+
"This computer has several workspaces. Run iteration connect interactively to choose one, or pass --workspace ID."
|
|
26898
|
+
);
|
|
26899
|
+
process.stdout.write(
|
|
26900
|
+
"\nChoose a connected workspace:\n" + workspaces.map(
|
|
26901
|
+
(workspace, index) => ` ${index + 1}. ${safeWorkspaceName(workspace.organizationName)}`
|
|
26902
|
+
).join("\n") + "\n"
|
|
26903
|
+
);
|
|
26904
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
26905
|
+
const answer = await ask(terminalFor(process.stdout), "Workspace number:");
|
|
26906
|
+
if (answer === null) throw new Error("Connection canceled.");
|
|
26907
|
+
const selected = /^\d+$/.test(answer.trim()) ? workspaces[Number(answer.trim()) - 1] : void 0;
|
|
26908
|
+
if (selected) return selected.organizationId;
|
|
26909
|
+
}
|
|
26910
|
+
throw new Error("Choose one of the listed workspaces to connect.");
|
|
26911
|
+
}
|
|
26912
|
+
async function verifySavedConnection(connection, registry3) {
|
|
26913
|
+
const snapshot = await fetchSharingSnapshot(connection);
|
|
26914
|
+
if (snapshot.computerId !== registry3.computerId)
|
|
26915
|
+
throw new Error(
|
|
26916
|
+
"The saved connection belongs to a different computer. Sign in again before connecting it."
|
|
26917
|
+
);
|
|
26918
|
+
assertSharingOwner(registry3.ownerId, snapshot.ownerId);
|
|
26919
|
+
return connection;
|
|
26920
|
+
}
|
|
26921
|
+
async function connectionForSource(registry3, anchor3, anchorConfig, source) {
|
|
26922
|
+
if (source === anchor3.source) return anchor3;
|
|
26923
|
+
const saved = sharingConnection(registry3, anchor3.organizationId, source);
|
|
26924
|
+
if (saved !== null) {
|
|
26925
|
+
try {
|
|
26926
|
+
return await verifySavedConnection(saved, registry3);
|
|
26927
|
+
} catch (error3) {
|
|
26928
|
+
if (error3 instanceof SharingSnapshotError && [401, 403].includes(error3.status))
|
|
26929
|
+
throw new Error(
|
|
26930
|
+
`${collectorSourceLabel(source)} needs sign-in again. Run iteration connect ${source} --workspace ${anchor3.organizationId}.`,
|
|
26931
|
+
{ cause: error3 }
|
|
26932
|
+
);
|
|
26933
|
+
throw error3;
|
|
26934
|
+
}
|
|
26935
|
+
}
|
|
26936
|
+
return enrolledSharingConnection(
|
|
26937
|
+
await expandSource(anchorConfig, source, anchor3.organizationId),
|
|
26938
|
+
source,
|
|
26939
|
+
anchor3.endpoint
|
|
26940
|
+
);
|
|
26941
|
+
}
|
|
26942
|
+
async function waitForSharing(anchor3, alreadyApproved, signal) {
|
|
26943
|
+
const deadline = Date.now() + 15 * 60 * 1e3;
|
|
26944
|
+
let approved = alreadyApproved;
|
|
26945
|
+
while (!approved && Date.now() < deadline) {
|
|
26946
|
+
await delay6(2e3, void 0, { signal });
|
|
26947
|
+
approved = (await fetchSharingSnapshot(anchor3, void 0, signal)).approved;
|
|
26948
|
+
}
|
|
26949
|
+
if (!approved)
|
|
26950
|
+
throw new Error(
|
|
26951
|
+
"Sharing is still waiting for your project choice. Finish it in Iteration, then run iteration connect again."
|
|
26952
|
+
);
|
|
26953
|
+
}
|
|
26954
|
+
async function connectWorkspace(options, selectedSource) {
|
|
26955
|
+
requireCollectorPlatform();
|
|
26956
|
+
if (selectedSource === void 0 && options["data-directory"] !== void 0)
|
|
26957
|
+
throw new Error(
|
|
26958
|
+
"--data-directory requires a source-specific connect command."
|
|
26959
|
+
);
|
|
26960
|
+
const local = await localConnectionOptions(options, selectedSource);
|
|
26961
|
+
if (local.sources.length === 0) {
|
|
26962
|
+
process.stdout.write(
|
|
26963
|
+
"No supported local coding agents found yet. Run iteration connect after using Claude Code, Codex or Cursor.\n"
|
|
26964
|
+
);
|
|
26965
|
+
return;
|
|
26966
|
+
}
|
|
26967
|
+
if ((!process.stdin.isTTY || !process.stdout.isTTY) && options.yes !== true)
|
|
26968
|
+
throw new Error(
|
|
26969
|
+
"Run iteration connect interactively or pass --yes to open browser sign-in. Project sharing still requires approval in Iteration."
|
|
26970
|
+
);
|
|
26971
|
+
await prepareBackgroundPersistence();
|
|
26972
|
+
const prepared = await withSharingRegistry(async (initial, save) => {
|
|
26973
|
+
let registry3 = initial;
|
|
26974
|
+
const { existing, homes, sources, endpoint } = local;
|
|
26975
|
+
if (registry3.connections.some(
|
|
26976
|
+
(connection) => connection.endpoint !== endpoint
|
|
26977
|
+
))
|
|
26978
|
+
throw new Error(
|
|
26979
|
+
"This computer is connected to another Iteration server. Sign out before changing servers."
|
|
26980
|
+
);
|
|
26981
|
+
const first = sources[0];
|
|
26982
|
+
const requested = await connectedWorkspace(
|
|
26983
|
+
registry3,
|
|
26984
|
+
textOption(options, "workspace"),
|
|
26985
|
+
endpoint
|
|
26986
|
+
);
|
|
26987
|
+
let anchor3 = requested === null ? null : sharingConnection(registry3, requested, first) ?? registry3.connections.find(
|
|
26988
|
+
(connection) => connection.organizationId === requested && connection.endpoint === endpoint
|
|
26989
|
+
) ?? null;
|
|
26990
|
+
if (anchor3 !== null) {
|
|
26991
|
+
try {
|
|
26992
|
+
await verifySavedConnection(anchor3, registry3);
|
|
26993
|
+
} catch (error3) {
|
|
26994
|
+
if (!(error3 instanceof SharingSnapshotError && [401, 403].includes(error3.status)))
|
|
26995
|
+
throw error3;
|
|
26996
|
+
anchor3 = null;
|
|
26997
|
+
}
|
|
26998
|
+
}
|
|
26999
|
+
if (anchor3 === null) {
|
|
27000
|
+
const enrollment = await authenticateAndEnrollCollector(
|
|
27001
|
+
new URL(endpoint),
|
|
27002
|
+
textOption(options, "device-name") ?? os12.hostname(),
|
|
27003
|
+
requested,
|
|
27004
|
+
first,
|
|
27005
|
+
registry3.computerId
|
|
27006
|
+
);
|
|
27007
|
+
anchor3 = enrolledSharingConnection(enrollment, first, endpoint);
|
|
27008
|
+
}
|
|
27009
|
+
const anchorConfig = sourceConfig(
|
|
27010
|
+
anchor3.source,
|
|
27011
|
+
homes[anchor3.source],
|
|
27012
|
+
defaultCollectorDirectory(anchor3.source),
|
|
27013
|
+
endpoint,
|
|
27014
|
+
anchor3
|
|
27015
|
+
);
|
|
27016
|
+
const inventory = collectorProjectInventorySchema.parse(
|
|
27017
|
+
await projectInventory(registry3.computerId, homes)
|
|
27018
|
+
);
|
|
27019
|
+
const response = await collectorFetch(
|
|
27020
|
+
new URL("./collector-sharing", endpoint),
|
|
27021
|
+
{
|
|
27022
|
+
method: "POST",
|
|
27023
|
+
headers: {
|
|
27024
|
+
authorization: `Bearer ${anchor3.token}`,
|
|
27025
|
+
"content-type": "application/json"
|
|
27026
|
+
},
|
|
27027
|
+
body: JSON.stringify(inventory),
|
|
27028
|
+
signal: AbortSignal.timeout(2e4)
|
|
27029
|
+
}
|
|
27030
|
+
);
|
|
27031
|
+
if (!response.ok)
|
|
27032
|
+
throw new Error(
|
|
27033
|
+
`Could not prepare project sharing (HTTP ${response.status}). No new sharing has started.`
|
|
27034
|
+
);
|
|
27035
|
+
const snapshot = collectorSharingSnapshotSchema.parse(
|
|
27036
|
+
await response.json()
|
|
27037
|
+
);
|
|
27038
|
+
assertSharingOwner(registry3.ownerId, snapshot.ownerId);
|
|
27039
|
+
registry3 = {
|
|
27040
|
+
...registry3,
|
|
27041
|
+
ownerId: snapshot.ownerId,
|
|
27042
|
+
sourceHomes: homes,
|
|
27043
|
+
connections: [
|
|
27044
|
+
...registry3.connections.filter(
|
|
27045
|
+
(connection) => connection.organizationId !== anchor3.organizationId || connection.source !== anchor3.source
|
|
27046
|
+
),
|
|
27047
|
+
anchor3
|
|
27048
|
+
]
|
|
27049
|
+
};
|
|
27050
|
+
await save(registry3);
|
|
27051
|
+
await prepareBackgroundPersistence();
|
|
27052
|
+
for (const previous of existing) {
|
|
27053
|
+
if (previous !== null && previous.destination !== "host")
|
|
27054
|
+
await prepareSource({ ...previous, destination: "host" }, previous);
|
|
27055
|
+
}
|
|
27056
|
+
const configs = [];
|
|
27057
|
+
for (const source of sources) {
|
|
27058
|
+
const connection = await connectionForSource(
|
|
27059
|
+
registry3,
|
|
27060
|
+
anchor3,
|
|
27061
|
+
anchorConfig,
|
|
27062
|
+
source
|
|
27063
|
+
);
|
|
27064
|
+
registry3 = {
|
|
27065
|
+
...registry3,
|
|
27066
|
+
connections: [
|
|
27067
|
+
...registry3.connections.filter(
|
|
27068
|
+
(saved) => saved.organizationId !== connection.organizationId || saved.source !== source
|
|
27069
|
+
),
|
|
27070
|
+
connection
|
|
27071
|
+
]
|
|
27072
|
+
};
|
|
27073
|
+
await save(registry3);
|
|
27074
|
+
const previous = existing[COLLECTOR_SOURCES2.indexOf(source)] ?? null;
|
|
27075
|
+
const directory = selectedSource ? path30.resolve(
|
|
27076
|
+
textOption(options, "data-directory") ?? defaultCollectorDirectory(source)
|
|
27077
|
+
) : defaultCollectorDirectory(source);
|
|
27078
|
+
const config3 = await prepareConnectedSource(
|
|
27079
|
+
source,
|
|
27080
|
+
homes[source],
|
|
27081
|
+
directory,
|
|
27082
|
+
connection,
|
|
27083
|
+
previous
|
|
27084
|
+
);
|
|
27085
|
+
registry3 = {
|
|
27086
|
+
...registry3,
|
|
27087
|
+
directories: {
|
|
27088
|
+
...registry3.directories,
|
|
27089
|
+
[source]: config3.dataDirectory
|
|
27090
|
+
}
|
|
27091
|
+
};
|
|
27092
|
+
await save(registry3);
|
|
27093
|
+
configs.push(config3);
|
|
27094
|
+
}
|
|
27095
|
+
return {
|
|
27096
|
+
configs,
|
|
27097
|
+
anchor: anchor3,
|
|
27098
|
+
approved: snapshot.approved,
|
|
27099
|
+
computerId: registry3.computerId
|
|
27100
|
+
};
|
|
27101
|
+
});
|
|
27102
|
+
const url2 = new URL("/app/collectors/share", prepared.anchor.endpoint);
|
|
27103
|
+
url2.searchParams.set("workspace", prepared.anchor.organizationId);
|
|
27104
|
+
url2.searchParams.set("computer", prepared.computerId);
|
|
27105
|
+
process.stdout.write(
|
|
27106
|
+
`
|
|
27107
|
+
Choose which projects ${prepared.anchor.organizationName} can see.
|
|
27108
|
+
${url2}
|
|
27109
|
+
|
|
27110
|
+
Nothing new uploads until you approve sharing in Iteration.
|
|
27111
|
+
`
|
|
27112
|
+
);
|
|
27113
|
+
const folderController = new AbortController();
|
|
27114
|
+
const failures = [];
|
|
27115
|
+
const folderTask = runFolderBrowser(
|
|
27116
|
+
prepared.anchor,
|
|
27117
|
+
folderController.signal
|
|
27118
|
+
).then(
|
|
27119
|
+
() => null,
|
|
27120
|
+
(error3) => {
|
|
27121
|
+
folderController.abort();
|
|
27122
|
+
return { error: error3 };
|
|
27123
|
+
}
|
|
25898
27124
|
);
|
|
25899
|
-
|
|
25900
|
-
|
|
25901
|
-
|
|
25902
|
-
|
|
27125
|
+
try {
|
|
27126
|
+
await openBrowser(url2.toString());
|
|
27127
|
+
await waitForSharing(
|
|
27128
|
+
prepared.anchor,
|
|
27129
|
+
prepared.approved,
|
|
27130
|
+
folderController.signal
|
|
25903
27131
|
);
|
|
25904
|
-
|
|
25905
|
-
|
|
25906
|
-
|
|
25907
|
-
|
|
25908
|
-
|
|
25909
|
-
|
|
27132
|
+
for (const config3 of prepared.configs) {
|
|
27133
|
+
await startSource(config3);
|
|
27134
|
+
process.stdout.write(
|
|
27135
|
+
`${collectorSourceLabel(collectorSource(config3))} connected \xB7 sharing approved projects
|
|
27136
|
+
`
|
|
27137
|
+
);
|
|
27138
|
+
}
|
|
27139
|
+
} catch (error3) {
|
|
27140
|
+
failures.push(error3);
|
|
27141
|
+
} finally {
|
|
27142
|
+
folderController.abort();
|
|
27143
|
+
const outcome = await folderTask;
|
|
27144
|
+
if (outcome !== null) failures.push(outcome.error);
|
|
27145
|
+
}
|
|
27146
|
+
if (failures.length === 1) throw failures[0];
|
|
27147
|
+
if (failures.length > 1)
|
|
27148
|
+
throw new AggregateError(
|
|
27149
|
+
failures,
|
|
27150
|
+
"The computer connection could not finish cleanly."
|
|
25910
27151
|
);
|
|
25911
|
-
|
|
25912
|
-
|
|
25913
|
-
|
|
25914
|
-
|
|
25915
|
-
|
|
25916
|
-
|
|
25917
|
-
|
|
25918
|
-
|
|
25919
|
-
|
|
25920
|
-
|
|
27152
|
+
process.stdout.write(
|
|
27153
|
+
"Saved sessions from those projects import first. New activity follows the same choice, whichever coding account you use.\n"
|
|
27154
|
+
);
|
|
27155
|
+
}
|
|
27156
|
+
async function connectSharedDetectedSources(config3) {
|
|
27157
|
+
await withSharingRegistry(async (registry3, save) => {
|
|
27158
|
+
if (registry3.connections.length === 0) return;
|
|
27159
|
+
const { snapshot } = await sharingTransport(
|
|
27160
|
+
config3,
|
|
27161
|
+
void 0,
|
|
27162
|
+
void 0,
|
|
27163
|
+
registry3
|
|
27164
|
+
);
|
|
27165
|
+
const homes = registry3.sourceHomes ?? {
|
|
27166
|
+
codex: sourceHome("codex"),
|
|
27167
|
+
claude: sourceHome("claude"),
|
|
27168
|
+
cursor: sourceHome("cursor")
|
|
27169
|
+
};
|
|
27170
|
+
let next = registry3;
|
|
27171
|
+
for (const source of await detectedSources(homes)) {
|
|
27172
|
+
const previous = await connectedConfig(
|
|
27173
|
+
source,
|
|
27174
|
+
registry3.directories?.[source]
|
|
27175
|
+
);
|
|
27176
|
+
for (const workspace of snapshot.workspaces.filter(
|
|
27177
|
+
(workspace2) => workspace2.active
|
|
27178
|
+
)) {
|
|
27179
|
+
if (sharingConnection(next, workspace.organizationId, source)) continue;
|
|
27180
|
+
const anchor3 = next.connections.find(
|
|
27181
|
+
(connection2) => connection2.organizationId === workspace.organizationId
|
|
27182
|
+
);
|
|
27183
|
+
if (!anchor3) continue;
|
|
27184
|
+
const enrollment = await expandSource(
|
|
27185
|
+
{
|
|
27186
|
+
...config3,
|
|
27187
|
+
token: anchor3.token,
|
|
27188
|
+
organizationId: anchor3.organizationId,
|
|
27189
|
+
organizationName: anchor3.organizationName
|
|
27190
|
+
},
|
|
27191
|
+
source,
|
|
27192
|
+
workspace.organizationId
|
|
27193
|
+
);
|
|
27194
|
+
next = {
|
|
27195
|
+
...next,
|
|
27196
|
+
connections: [
|
|
27197
|
+
...next.connections,
|
|
27198
|
+
enrolledSharingConnection(enrollment, source, config3.endpoint)
|
|
27199
|
+
]
|
|
27200
|
+
};
|
|
27201
|
+
await save(next);
|
|
27202
|
+
}
|
|
27203
|
+
const connection = next.connections.find(
|
|
27204
|
+
(item) => item.source === source
|
|
27205
|
+
);
|
|
27206
|
+
if (previous !== null || !connection) continue;
|
|
27207
|
+
const added = sourceConfig(
|
|
27208
|
+
source,
|
|
27209
|
+
homes[source],
|
|
27210
|
+
defaultCollectorDirectory(source),
|
|
27211
|
+
config3.endpoint,
|
|
27212
|
+
connection
|
|
27213
|
+
);
|
|
27214
|
+
await prepareSource(added, null);
|
|
27215
|
+
await startSource(added);
|
|
27216
|
+
}
|
|
27217
|
+
});
|
|
25921
27218
|
}
|
|
25922
27219
|
|
|
25923
27220
|
// src/history-import.ts
|
|
25924
|
-
import
|
|
27221
|
+
import path31 from "node:path";
|
|
25925
27222
|
function backgroundControl(source) {
|
|
25926
27223
|
return {
|
|
25927
27224
|
isRunning: () => backgroundServiceStatus(source),
|
|
@@ -25934,7 +27231,7 @@ async function importSourceHistory(config3, paths, confirmed, control = backgrou
|
|
|
25934
27231
|
const source = collectorSource(config3);
|
|
25935
27232
|
const directory = collectorSourceDirectory(config3);
|
|
25936
27233
|
const state = await loadState(paths);
|
|
25937
|
-
if (!state.initialized || state.sourceDirectory !==
|
|
27234
|
+
if (!state.initialized || state.sourceDirectory !== path31.resolve(directory)) {
|
|
25938
27235
|
throw new Error(
|
|
25939
27236
|
`Connect the ${source} source before importing its history.`
|
|
25940
27237
|
);
|
|
@@ -25982,37 +27279,37 @@ async function importSourceHistory(config3, paths, confirmed, control = backgrou
|
|
|
25982
27279
|
}
|
|
25983
27280
|
|
|
25984
27281
|
// src/logout.ts
|
|
25985
|
-
import { randomUUID as
|
|
25986
|
-
import { promises as
|
|
25987
|
-
import
|
|
25988
|
-
import
|
|
27282
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
27283
|
+
import { promises as fs25 } from "node:fs";
|
|
27284
|
+
import os13 from "node:os";
|
|
27285
|
+
import path32 from "node:path";
|
|
25989
27286
|
async function pendingDirectories(filename) {
|
|
25990
27287
|
let contents;
|
|
25991
27288
|
try {
|
|
25992
|
-
contents = await
|
|
27289
|
+
contents = await fs25.readFile(filename, "utf8");
|
|
25993
27290
|
} catch (error3) {
|
|
25994
27291
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
25995
27292
|
return [];
|
|
25996
27293
|
throw error3;
|
|
25997
27294
|
}
|
|
25998
|
-
const parsed = array2(string5().refine(
|
|
27295
|
+
const parsed = array2(string5().refine(path32.isAbsolute)).max(64).safeParse(JSON.parse(contents));
|
|
25999
27296
|
if (!parsed.success)
|
|
26000
27297
|
throw new Error("Pending logout directories are invalid.");
|
|
26001
27298
|
return parsed.data;
|
|
26002
27299
|
}
|
|
26003
27300
|
async function savePendingDirectories(filename, directories) {
|
|
26004
|
-
await ensurePrivateDirectory(
|
|
27301
|
+
await ensurePrivateDirectory(path32.dirname(filename));
|
|
26005
27302
|
const temporary = `${filename}.${process.pid}.tmp`;
|
|
26006
|
-
await
|
|
27303
|
+
await fs25.writeFile(temporary, JSON.stringify([...directories]), {
|
|
26007
27304
|
mode: 384
|
|
26008
27305
|
});
|
|
26009
|
-
await
|
|
27306
|
+
await fs25.rename(temporary, filename);
|
|
26010
27307
|
}
|
|
26011
27308
|
async function clearConnection(directory) {
|
|
26012
27309
|
const paths = collectorPaths(directory);
|
|
26013
27310
|
let names;
|
|
26014
27311
|
try {
|
|
26015
|
-
names = await
|
|
27312
|
+
names = await fs25.readdir(directory);
|
|
26016
27313
|
} catch (error3) {
|
|
26017
27314
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
26018
27315
|
return;
|
|
@@ -26022,8 +27319,8 @@ async function clearConnection(directory) {
|
|
|
26022
27319
|
try {
|
|
26023
27320
|
for (const name of names)
|
|
26024
27321
|
if (/^config\.json\.\d+\.tmp$/.test(name))
|
|
26025
|
-
await
|
|
26026
|
-
if (!names.includes(
|
|
27322
|
+
await fs25.rm(path32.join(directory, name), { force: true });
|
|
27323
|
+
if (!names.includes(path32.basename(paths.config))) return;
|
|
26027
27324
|
const config3 = await loadConfig(paths);
|
|
26028
27325
|
if (config3.destination === "vault")
|
|
26029
27326
|
throw new Error("This directory belongs to Trace Vault, not Iteration.");
|
|
@@ -26044,41 +27341,43 @@ async function clearConnection(directory) {
|
|
|
26044
27341
|
"claude-git",
|
|
26045
27342
|
"turn-summary",
|
|
26046
27343
|
"codex-billing-sync.json",
|
|
26047
|
-
"cursor-billing-sync.json"
|
|
26048
|
-
|
|
27344
|
+
"cursor-billing-sync.json",
|
|
27345
|
+
"shared-sessions.json"
|
|
27346
|
+
].map((name) => path32.join(directory, name))
|
|
26049
27347
|
];
|
|
26050
|
-
const archive =
|
|
27348
|
+
const archive = path32.join(directory, "logged-out", randomUUID4());
|
|
26051
27349
|
await ensurePrivateDirectory(archive);
|
|
26052
27350
|
for (const filename of retained) {
|
|
26053
27351
|
try {
|
|
26054
|
-
await
|
|
27352
|
+
await fs25.rename(filename, path32.join(archive, path32.basename(filename)));
|
|
26055
27353
|
} catch (error3) {
|
|
26056
27354
|
if (!(error3 instanceof Error && "code" in error3 && error3.code === "ENOENT"))
|
|
26057
27355
|
throw error3;
|
|
26058
27356
|
}
|
|
26059
27357
|
}
|
|
26060
|
-
await
|
|
27358
|
+
await fs25.rm(paths.config);
|
|
26061
27359
|
} finally {
|
|
26062
27360
|
await release();
|
|
26063
27361
|
}
|
|
26064
27362
|
}
|
|
26065
27363
|
async function logout(extraDirectory) {
|
|
26066
27364
|
requireCollectorPlatform();
|
|
26067
|
-
const
|
|
26068
|
-
|
|
27365
|
+
const pending2 = path32.join(
|
|
27366
|
+
os13.homedir(),
|
|
26069
27367
|
".config",
|
|
26070
27368
|
"iteration",
|
|
26071
27369
|
"logout-pending.json"
|
|
26072
27370
|
);
|
|
26073
27371
|
const directories = /* @__PURE__ */ new Set([
|
|
26074
27372
|
...COLLECTOR_SOURCES2.map(defaultCollectorDirectory),
|
|
26075
|
-
...await pendingDirectories(
|
|
27373
|
+
...await pendingDirectories(pending2)
|
|
26076
27374
|
]);
|
|
26077
27375
|
if (extraDirectory !== void 0)
|
|
26078
|
-
directories.add(
|
|
27376
|
+
directories.add(path32.resolve(extraDirectory));
|
|
26079
27377
|
const failures = [];
|
|
26080
27378
|
try {
|
|
26081
27379
|
await removeMachineConsent();
|
|
27380
|
+
await forgetSharingConnections();
|
|
26082
27381
|
} catch (error3) {
|
|
26083
27382
|
failures.push(
|
|
26084
27383
|
`Automatic connection approval: ${error3 instanceof Error ? error3.message : "Could not remove approval."}`
|
|
@@ -26096,7 +27395,7 @@ async function logout(extraDirectory) {
|
|
|
26096
27395
|
);
|
|
26097
27396
|
}
|
|
26098
27397
|
}
|
|
26099
|
-
await savePendingDirectories(
|
|
27398
|
+
await savePendingDirectories(pending2, directories);
|
|
26100
27399
|
const stopped = await Promise.all(
|
|
26101
27400
|
COLLECTOR_SOURCES2.filter((source) => !unreadableServices.has(source)).map(
|
|
26102
27401
|
(source) => uninstallBackgroundService(source).then(
|
|
@@ -26120,7 +27419,7 @@ async function logout(extraDirectory) {
|
|
|
26120
27419
|
`Logout did not finish. Resolve these errors and run iteration logout again:
|
|
26121
27420
|
${failures.join("\n")}`
|
|
26122
27421
|
);
|
|
26123
|
-
await
|
|
27422
|
+
await fs25.rm(pending2);
|
|
26124
27423
|
}
|
|
26125
27424
|
|
|
26126
27425
|
// src/runtime-services.ts
|
|
@@ -26147,17 +27446,17 @@ async function adoptRuntime() {
|
|
|
26147
27446
|
}
|
|
26148
27447
|
|
|
26149
27448
|
// src/service.ts
|
|
26150
|
-
import { setTimeout as
|
|
27449
|
+
import { setTimeout as delay7 } from "node:timers/promises";
|
|
26151
27450
|
|
|
26152
27451
|
// src/codex-billing.ts
|
|
26153
27452
|
import { createHash as createHash7 } from "node:crypto";
|
|
26154
|
-
import { promises as
|
|
26155
|
-
import
|
|
27453
|
+
import { promises as fs26 } from "node:fs";
|
|
27454
|
+
import path33 from "node:path";
|
|
26156
27455
|
var TASK_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage/thread_usage/query_v2";
|
|
26157
27456
|
var UUID2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
26158
27457
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
26159
27458
|
var TASK_BATCH_SIZE = 50;
|
|
26160
|
-
var
|
|
27459
|
+
var MAX_HEADER_BYTES2 = 3 * 1024 * 1024;
|
|
26161
27460
|
var stateSchema2 = object3({
|
|
26162
27461
|
accountFingerprint: string5().regex(/^[0-9a-f]{64}$/),
|
|
26163
27462
|
lastSyncedAtMs: number4().int().nonnegative(),
|
|
@@ -26204,7 +27503,7 @@ function fingerprint(value) {
|
|
|
26204
27503
|
async function readAuth(codexHome) {
|
|
26205
27504
|
let contents;
|
|
26206
27505
|
try {
|
|
26207
|
-
contents = await
|
|
27506
|
+
contents = await fs26.readFile(path33.join(codexHome, "auth.json"), "utf8");
|
|
26208
27507
|
} catch (error3) {
|
|
26209
27508
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
26210
27509
|
return {
|
|
@@ -26239,7 +27538,7 @@ async function readAuth(codexHome) {
|
|
|
26239
27538
|
async function readState(filename) {
|
|
26240
27539
|
let contents;
|
|
26241
27540
|
try {
|
|
26242
|
-
contents = await
|
|
27541
|
+
contents = await fs26.readFile(filename, "utf8");
|
|
26243
27542
|
} catch (error3) {
|
|
26244
27543
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
26245
27544
|
return null;
|
|
@@ -26261,7 +27560,7 @@ async function readState(filename) {
|
|
|
26261
27560
|
}
|
|
26262
27561
|
async function saveState2(filename, state) {
|
|
26263
27562
|
const temporary = `${filename}.${process.pid}.tmp`;
|
|
26264
|
-
const handle = await
|
|
27563
|
+
const handle = await fs26.open(temporary, "w", 384);
|
|
26265
27564
|
try {
|
|
26266
27565
|
await handle.writeFile(`${JSON.stringify(state)}
|
|
26267
27566
|
`);
|
|
@@ -26269,21 +27568,21 @@ async function saveState2(filename, state) {
|
|
|
26269
27568
|
} finally {
|
|
26270
27569
|
await handle.close();
|
|
26271
27570
|
}
|
|
26272
|
-
await
|
|
27571
|
+
await fs26.rename(temporary, filename);
|
|
26273
27572
|
}
|
|
26274
27573
|
async function localThread(filename) {
|
|
26275
|
-
const stat = await
|
|
26276
|
-
const handle = await
|
|
27574
|
+
const stat = await fs26.stat(filename);
|
|
27575
|
+
const handle = await fs26.open(filename, "r");
|
|
26277
27576
|
let header = null;
|
|
26278
27577
|
try {
|
|
26279
27578
|
const buffer = Buffer.alloc(64 * 1024);
|
|
26280
27579
|
const chunks = [];
|
|
26281
27580
|
let offset = 0;
|
|
26282
|
-
while (offset < Math.min(stat.size,
|
|
27581
|
+
while (offset < Math.min(stat.size, MAX_HEADER_BYTES2)) {
|
|
26283
27582
|
const read = await handle.read(
|
|
26284
27583
|
buffer,
|
|
26285
27584
|
0,
|
|
26286
|
-
Math.min(buffer.length,
|
|
27585
|
+
Math.min(buffer.length, MAX_HEADER_BYTES2 - offset),
|
|
26287
27586
|
offset
|
|
26288
27587
|
);
|
|
26289
27588
|
if (read.bytesRead === 0) break;
|
|
@@ -26373,9 +27672,9 @@ function parseCodexAuthLog(threadId, body) {
|
|
|
26373
27672
|
return { threadId, turnId: turn[1], mode };
|
|
26374
27673
|
}
|
|
26375
27674
|
async function localAuthTurns(codexHome, modelProviders) {
|
|
26376
|
-
const filename =
|
|
27675
|
+
const filename = path33.join(codexHome, "logs_2.sqlite");
|
|
26377
27676
|
try {
|
|
26378
|
-
await
|
|
27677
|
+
await fs26.access(filename);
|
|
26379
27678
|
} catch (error3) {
|
|
26380
27679
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
26381
27680
|
return [];
|
|
@@ -26468,7 +27767,7 @@ async function queryTaskUsage(tasks, accessToken, accountId, signal) {
|
|
|
26468
27767
|
}
|
|
26469
27768
|
async function syncCodexBilling(config3, fetcher = collectorFetch, signal) {
|
|
26470
27769
|
const auth = await readAuth(config3.codexHome);
|
|
26471
|
-
const stateFile =
|
|
27770
|
+
const stateFile = path33.join(config3.dataDirectory, "codex-billing-sync.json");
|
|
26472
27771
|
const prior = await readState(stateFile);
|
|
26473
27772
|
const nowMs = Date.now();
|
|
26474
27773
|
const history = prior === null || prior.accountFingerprint !== auth.accountFingerprint;
|
|
@@ -26639,9 +27938,9 @@ async function syncCodexBilling(config3, fetcher = collectorFetch, signal) {
|
|
|
26639
27938
|
|
|
26640
27939
|
// src/cursor-billing.ts
|
|
26641
27940
|
import { createHash as createHash8 } from "node:crypto";
|
|
26642
|
-
import { promises as
|
|
26643
|
-
import
|
|
26644
|
-
import
|
|
27941
|
+
import { promises as fs27 } from "node:fs";
|
|
27942
|
+
import os14 from "node:os";
|
|
27943
|
+
import path34 from "node:path";
|
|
26645
27944
|
var CURSOR_USAGE_URL = "https://api2.cursor.sh/aiserver.v1.DashboardService/GetFilteredUsageEvents";
|
|
26646
27945
|
var PAGE_SIZE = 500;
|
|
26647
27946
|
var MAX_PAGES = 1e3;
|
|
@@ -26672,18 +27971,18 @@ var usagePageSchema = object3({
|
|
|
26672
27971
|
var emptyUsagePageSchema = object3({}).strict();
|
|
26673
27972
|
function cursorAuthDatabase() {
|
|
26674
27973
|
if (process.platform === "darwin")
|
|
26675
|
-
return
|
|
26676
|
-
|
|
27974
|
+
return path34.join(
|
|
27975
|
+
os14.homedir(),
|
|
26677
27976
|
"Library/Application Support/Cursor/User/globalStorage/state.vscdb"
|
|
26678
27977
|
);
|
|
26679
27978
|
if (process.platform === "linux")
|
|
26680
|
-
return
|
|
26681
|
-
|
|
27979
|
+
return path34.join(
|
|
27980
|
+
os14.homedir(),
|
|
26682
27981
|
".config/Cursor/User/globalStorage/state.vscdb"
|
|
26683
27982
|
);
|
|
26684
27983
|
if (process.platform === "win32")
|
|
26685
|
-
return
|
|
26686
|
-
process.env.APPDATA ??
|
|
27984
|
+
return path34.join(
|
|
27985
|
+
process.env.APPDATA ?? path34.join(os14.homedir(), "AppData", "Roaming"),
|
|
26687
27986
|
"Cursor/User/globalStorage/state.vscdb"
|
|
26688
27987
|
);
|
|
26689
27988
|
return null;
|
|
@@ -26725,7 +28024,7 @@ async function readCursorAuth() {
|
|
|
26725
28024
|
async function loadBillingState(filename) {
|
|
26726
28025
|
let contents;
|
|
26727
28026
|
try {
|
|
26728
|
-
contents = await
|
|
28027
|
+
contents = await fs27.readFile(filename, "utf8");
|
|
26729
28028
|
} catch (error3) {
|
|
26730
28029
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
26731
28030
|
return null;
|
|
@@ -26741,7 +28040,7 @@ async function loadBillingState(filename) {
|
|
|
26741
28040
|
}
|
|
26742
28041
|
async function saveBillingState(filename, state) {
|
|
26743
28042
|
const temporary = `${filename}.${process.pid}.tmp`;
|
|
26744
|
-
const handle = await
|
|
28043
|
+
const handle = await fs27.open(temporary, "w", 384);
|
|
26745
28044
|
try {
|
|
26746
28045
|
await handle.writeFile(`${JSON.stringify(state)}
|
|
26747
28046
|
`, "utf8");
|
|
@@ -26749,7 +28048,7 @@ async function saveBillingState(filename, state) {
|
|
|
26749
28048
|
} finally {
|
|
26750
28049
|
await handle.close();
|
|
26751
28050
|
}
|
|
26752
|
-
await
|
|
28051
|
+
await fs27.rename(temporary, filename);
|
|
26753
28052
|
}
|
|
26754
28053
|
function planCursorBillingSync(state, accountFingerprint, nowMs) {
|
|
26755
28054
|
if (state === null || state.accountFingerprint !== accountFingerprint)
|
|
@@ -26771,7 +28070,7 @@ function planCursorBillingSync(state, accountFingerprint, nowMs) {
|
|
|
26771
28070
|
}
|
|
26772
28071
|
function cursorConversationIds(filenames) {
|
|
26773
28072
|
return new Set(
|
|
26774
|
-
filenames.map((filename) =>
|
|
28073
|
+
filenames.map((filename) => path34.basename(filename, ".jsonl")).filter((id) => UUID3.test(id))
|
|
26775
28074
|
);
|
|
26776
28075
|
}
|
|
26777
28076
|
function billingKind(kind) {
|
|
@@ -26829,11 +28128,11 @@ async function syncCursorBilling(config3, fetcher = collectorFetch, signal) {
|
|
|
26829
28128
|
if (auth === null)
|
|
26830
28129
|
return { sessions: 0, matched: 0, skipped: true, mode: null };
|
|
26831
28130
|
const localConversations = cursorConversationIds(
|
|
26832
|
-
await cursorFiles(
|
|
28131
|
+
await cursorFiles(path34.join(config3.cursorHome, "projects"))
|
|
26833
28132
|
);
|
|
26834
28133
|
if (localConversations.size === 0)
|
|
26835
28134
|
return { sessions: 0, matched: 0, skipped: true, mode: null };
|
|
26836
|
-
const stateFile =
|
|
28135
|
+
const stateFile = path34.join(config3.dataDirectory, "cursor-billing-sync.json");
|
|
26837
28136
|
const priorState = await loadBillingState(stateFile);
|
|
26838
28137
|
const nowMs = Date.now();
|
|
26839
28138
|
const plan = planCursorBillingSync(
|
|
@@ -27075,6 +28374,14 @@ async function onTrial(version3) {
|
|
|
27075
28374
|
const state = await readRuntimeState(runtimePaths());
|
|
27076
28375
|
return state?.trial?.version === version3;
|
|
27077
28376
|
}
|
|
28377
|
+
function throwCollectorFailures(failures) {
|
|
28378
|
+
if (failures.length === 1) throw failures[0];
|
|
28379
|
+
if (failures.length > 1)
|
|
28380
|
+
throw new AggregateError(
|
|
28381
|
+
failures,
|
|
28382
|
+
"The collector could not finish cleanly."
|
|
28383
|
+
);
|
|
28384
|
+
}
|
|
27078
28385
|
async function runCollector(directory = defaultCollectorDirectory("codex"), expectedSource) {
|
|
27079
28386
|
const paths = collectorPaths(directory);
|
|
27080
28387
|
const releaseLock = await acquireCollectorLock(paths);
|
|
@@ -27083,8 +28390,18 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
|
|
|
27083
28390
|
process.on("SIGTERM", stop);
|
|
27084
28391
|
process.on("SIGINT", stop);
|
|
27085
28392
|
let retryDelayMs = 5e3;
|
|
28393
|
+
let folderTask = null;
|
|
28394
|
+
const failures = [];
|
|
27086
28395
|
try {
|
|
27087
28396
|
const config3 = await loadConfig(paths);
|
|
28397
|
+
if (config3.destination === "host")
|
|
28398
|
+
folderTask = runFolderBrowser(config3, controller.signal).then(
|
|
28399
|
+
() => null,
|
|
28400
|
+
(error3) => {
|
|
28401
|
+
controller.abort();
|
|
28402
|
+
return { error: error3 };
|
|
28403
|
+
}
|
|
28404
|
+
);
|
|
27088
28405
|
const source = collectorSource(config3);
|
|
27089
28406
|
if (expectedSource !== void 0 && expectedSource !== source) {
|
|
27090
28407
|
throw new Error(
|
|
@@ -27109,6 +28426,16 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
|
|
|
27109
28426
|
} else await ensureHistoryBackfill(config3.codexHome, paths);
|
|
27110
28427
|
const summariesRefreshedAt = /* @__PURE__ */ new Map();
|
|
27111
28428
|
let lastAutoCheckAt = 0;
|
|
28429
|
+
let scoped = null;
|
|
28430
|
+
let sharingRevision = null;
|
|
28431
|
+
const fetcher = (input, init) => {
|
|
28432
|
+
if (config3.destination !== "host") return collectorFetch(input, init);
|
|
28433
|
+
if (scoped === null)
|
|
28434
|
+
throw new Error(
|
|
28435
|
+
"Project sharing could not be verified. Collection is waiting."
|
|
28436
|
+
);
|
|
28437
|
+
return scoped.fetcher(input, init);
|
|
28438
|
+
};
|
|
27112
28439
|
let lastBillingSyncAt = 0;
|
|
27113
28440
|
const historyProgress2 = new HistoryProgressReporter();
|
|
27114
28441
|
const runtimeVersion = process.env.ITERATION_RUNTIME_VERSION;
|
|
@@ -27145,6 +28472,10 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
|
|
|
27145
28472
|
if (Date.now() - lastAutoCheckAt >= 6e4) {
|
|
27146
28473
|
lastAutoCheckAt = Date.now();
|
|
27147
28474
|
try {
|
|
28475
|
+
if (config3.destination === "host") {
|
|
28476
|
+
await connectSharedDetectedSources(config3);
|
|
28477
|
+
return;
|
|
28478
|
+
}
|
|
27148
28479
|
const consent = await loadMachineConsent();
|
|
27149
28480
|
if (consent?.anchorSource === source) {
|
|
27150
28481
|
const connected = await connectNewDetectedSources(config3, consent);
|
|
@@ -27197,15 +28528,31 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
|
|
|
27197
28528
|
let billingReady = false;
|
|
27198
28529
|
let history = null;
|
|
27199
28530
|
try {
|
|
28531
|
+
if (config3.destination === "host") {
|
|
28532
|
+
scoped = null;
|
|
28533
|
+
scoped = await sharingTransport(
|
|
28534
|
+
config3,
|
|
28535
|
+
collectorFetch,
|
|
28536
|
+
controller.signal
|
|
28537
|
+
);
|
|
28538
|
+
if (sharingRevision !== scoped.snapshot.revision) {
|
|
28539
|
+
await clearTurnSummaries(config3);
|
|
28540
|
+
summariesRefreshedAt.clear();
|
|
28541
|
+
sharingRevision = scoped.snapshot.revision;
|
|
28542
|
+
}
|
|
28543
|
+
}
|
|
27200
28544
|
const result = await collectorTick(
|
|
27201
28545
|
config3,
|
|
27202
28546
|
paths,
|
|
27203
|
-
|
|
28547
|
+
fetcher,
|
|
27204
28548
|
controller.signal,
|
|
27205
28549
|
() => {
|
|
27206
28550
|
acknowledgedAtMs = Date.now();
|
|
27207
28551
|
},
|
|
27208
|
-
{
|
|
28552
|
+
{
|
|
28553
|
+
recentMessages: RECENT_MESSAGE_LIMIT,
|
|
28554
|
+
...scoped === null ? {} : { capture: scoped.capture }
|
|
28555
|
+
}
|
|
27209
28556
|
);
|
|
27210
28557
|
billingReady = result.historyCaughtUp && result.outboxBytes === 0;
|
|
27211
28558
|
history = {
|
|
@@ -27246,7 +28593,7 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
|
|
|
27246
28593
|
await historyProgress2.report(
|
|
27247
28594
|
config3,
|
|
27248
28595
|
history,
|
|
27249
|
-
|
|
28596
|
+
fetcher,
|
|
27250
28597
|
controller.signal,
|
|
27251
28598
|
Date.now()
|
|
27252
28599
|
);
|
|
@@ -27271,7 +28618,7 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
|
|
|
27271
28618
|
config3,
|
|
27272
28619
|
summariesRefreshedAt,
|
|
27273
28620
|
Date.now(),
|
|
27274
|
-
|
|
28621
|
+
fetcher,
|
|
27275
28622
|
controller.signal
|
|
27276
28623
|
);
|
|
27277
28624
|
} catch (error3) {
|
|
@@ -27292,7 +28639,7 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
|
|
|
27292
28639
|
try {
|
|
27293
28640
|
const billing = await syncCursorBilling(
|
|
27294
28641
|
config3,
|
|
27295
|
-
|
|
28642
|
+
fetcher,
|
|
27296
28643
|
controller.signal
|
|
27297
28644
|
);
|
|
27298
28645
|
lastBillingSyncAt = billing.skipped ? Date.now() - 6 * 60 * 60 * 1e3 + 5 * 60 * 1e3 : Date.now();
|
|
@@ -27315,7 +28662,7 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
|
|
|
27315
28662
|
try {
|
|
27316
28663
|
const billing = await syncCodexBilling(
|
|
27317
28664
|
config3,
|
|
27318
|
-
|
|
28665
|
+
fetcher,
|
|
27319
28666
|
controller.signal
|
|
27320
28667
|
);
|
|
27321
28668
|
lastBillingSyncAt = billing.skipped ? Date.now() - 6 * 60 * 60 * 1e3 + 5 * 60 * 1e3 : Date.now();
|
|
@@ -27345,17 +28692,23 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
|
|
|
27345
28692
|
if (!await refreshSummaries()) break;
|
|
27346
28693
|
if (!await syncBilling(result.billingReady)) break;
|
|
27347
28694
|
try {
|
|
27348
|
-
await
|
|
28695
|
+
await delay7(retryDelayMs, void 0, { signal: controller.signal });
|
|
27349
28696
|
} catch (error3) {
|
|
27350
28697
|
if (error3 instanceof Error && error3.name === "AbortError") break;
|
|
27351
28698
|
throw error3;
|
|
27352
28699
|
}
|
|
27353
28700
|
}
|
|
28701
|
+
} catch (error3) {
|
|
28702
|
+
failures.push(error3);
|
|
27354
28703
|
} finally {
|
|
28704
|
+
controller.abort();
|
|
27355
28705
|
process.off("SIGTERM", stop);
|
|
27356
28706
|
process.off("SIGINT", stop);
|
|
27357
|
-
await
|
|
28707
|
+
const outcome = await folderTask;
|
|
28708
|
+
if (outcome) failures.push(outcome.error);
|
|
28709
|
+
await releaseLock().catch((error3) => failures.push(error3));
|
|
27358
28710
|
}
|
|
28711
|
+
throwCollectorFailures(failures);
|
|
27359
28712
|
}
|
|
27360
28713
|
|
|
27361
28714
|
// src/status-view.ts
|
|
@@ -27480,14 +28833,14 @@ function failureSummary(terminal2, status2, source, nowMs) {
|
|
|
27480
28833
|
}
|
|
27481
28834
|
function connectionSummary(terminal2, status2, source, nowMs) {
|
|
27482
28835
|
const name = sourceName(source);
|
|
27483
|
-
const
|
|
28836
|
+
const pending2 = status2.pendingEvents > 0 ? [`${pendingDescription(status2)}.`] : [];
|
|
27484
28837
|
const details = `Details: ${terminal2.bold(`iteration status ${source}`)}`;
|
|
27485
28838
|
if (!status2.running)
|
|
27486
28839
|
return {
|
|
27487
28840
|
tone: "warning",
|
|
27488
28841
|
headline: "Stopped",
|
|
27489
28842
|
facts: [],
|
|
27490
|
-
details: ["New sessions are not being collected.", ...
|
|
28843
|
+
details: ["New sessions are not being collected.", ...pending2],
|
|
27491
28844
|
next: `Resume with ${terminal2.bold(`iteration start ${source}`)}`
|
|
27492
28845
|
};
|
|
27493
28846
|
if (status2.hookFailureAtMs !== null && status2.hookFailureAtMs !== void 0)
|
|
@@ -27507,7 +28860,7 @@ function connectionSummary(terminal2, status2, source, nowMs) {
|
|
|
27507
28860
|
facts: [],
|
|
27508
28861
|
details: [
|
|
27509
28862
|
"Upload history could not be read. Collection continues in the background.",
|
|
27510
|
-
...
|
|
28863
|
+
...pending2
|
|
27511
28864
|
],
|
|
27512
28865
|
next: details
|
|
27513
28866
|
};
|
|
@@ -27517,7 +28870,7 @@ function connectionSummary(terminal2, status2, source, nowMs) {
|
|
|
27517
28870
|
}
|
|
27518
28871
|
function scanSummary(status2, source, nowMs) {
|
|
27519
28872
|
const name = sourceName(source);
|
|
27520
|
-
const
|
|
28873
|
+
const pending2 = status2.pendingEvents > 0 ? [`${pendingDescription(status2)}.`] : [];
|
|
27521
28874
|
const lastUpload = status2.health.lastUploadAtMs === null ? [] : [`last upload ${relativeTime(status2.health.lastUploadAtMs, nowMs)}`];
|
|
27522
28875
|
const summary = (headline, details, facts = []) => ({
|
|
27523
28876
|
tone: "success",
|
|
@@ -27536,10 +28889,10 @@ function scanSummary(status2, source, nowMs) {
|
|
|
27536
28889
|
if (scan.totalBytes - scan.scannedBytes >= 1e6)
|
|
27537
28890
|
return summary("Catching up with new activity", [
|
|
27538
28891
|
historyDescription(status2, source),
|
|
27539
|
-
...
|
|
28892
|
+
...pending2
|
|
27540
28893
|
]);
|
|
27541
28894
|
if (status2.pendingEvents > 0)
|
|
27542
|
-
return summary("Uploading captured sessions",
|
|
28895
|
+
return summary("Uploading captured sessions", pending2);
|
|
27543
28896
|
if (scan.totalBytes === 0)
|
|
27544
28897
|
return summary("Connected", [
|
|
27545
28898
|
`Waiting for ${name} sessions on this computer.`
|
|
@@ -27759,26 +29112,26 @@ function renderRuntime(terminal2, summary) {
|
|
|
27759
29112
|
|
|
27760
29113
|
// src/windows-supervisor.ts
|
|
27761
29114
|
import { spawn } from "node:child_process";
|
|
27762
|
-
import { promises as
|
|
27763
|
-
import
|
|
27764
|
-
import { setTimeout as
|
|
29115
|
+
import { promises as fs28 } from "node:fs";
|
|
29116
|
+
import path35 from "node:path";
|
|
29117
|
+
import { setTimeout as delay8 } from "node:timers/promises";
|
|
27765
29118
|
var MAX_LOG_BYTES = 8 * 1024 * 1024;
|
|
27766
29119
|
var RESTART_DELAY_MS = 5e3;
|
|
27767
29120
|
async function appendLog(filename, bytes) {
|
|
27768
29121
|
let offset = 0;
|
|
27769
29122
|
while (offset < bytes.length) {
|
|
27770
|
-
const size = await
|
|
29123
|
+
const size = await fs28.stat(filename).then((stat) => stat.size).catch((error3) => {
|
|
27771
29124
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
27772
29125
|
return 0;
|
|
27773
29126
|
throw error3;
|
|
27774
29127
|
});
|
|
27775
29128
|
if (size >= MAX_LOG_BYTES) {
|
|
27776
|
-
await
|
|
27777
|
-
await
|
|
29129
|
+
await fs28.rm(`${filename}.1`, { force: true });
|
|
29130
|
+
await fs28.rename(filename, `${filename}.1`);
|
|
27778
29131
|
continue;
|
|
27779
29132
|
}
|
|
27780
29133
|
const count = Math.min(bytes.length - offset, MAX_LOG_BYTES - size);
|
|
27781
|
-
await
|
|
29134
|
+
await fs28.appendFile(filename, bytes.subarray(offset, offset + count), {
|
|
27782
29135
|
mode: 384
|
|
27783
29136
|
});
|
|
27784
29137
|
offset += count;
|
|
@@ -27794,7 +29147,7 @@ async function copyOutput(stream, filename) {
|
|
|
27794
29147
|
async function superviseCollector(source, dataDirectory2) {
|
|
27795
29148
|
const output = windowsOutputPath(source);
|
|
27796
29149
|
const errors = windowsLogPath(source);
|
|
27797
|
-
await ensurePrivateDirectory(
|
|
29150
|
+
await ensurePrivateDirectory(path35.dirname(output));
|
|
27798
29151
|
const controller = new AbortController();
|
|
27799
29152
|
const stop = () => controller.abort();
|
|
27800
29153
|
process.once("SIGTERM", stop);
|
|
@@ -27851,7 +29204,7 @@ async function superviseCollector(source, dataDirectory2) {
|
|
|
27851
29204
|
`
|
|
27852
29205
|
)
|
|
27853
29206
|
);
|
|
27854
|
-
await
|
|
29207
|
+
await delay8(RESTART_DELAY_MS, void 0, {
|
|
27855
29208
|
signal: controller.signal
|
|
27856
29209
|
}).catch((error3) => {
|
|
27857
29210
|
if (!controller.signal.aborted) throw error3;
|
|
@@ -27864,7 +29217,6 @@ async function superviseCollector(source, dataDirectory2) {
|
|
|
27864
29217
|
}
|
|
27865
29218
|
|
|
27866
29219
|
// src/cli.ts
|
|
27867
|
-
var DEFAULT_INGEST_ENDPOINT = "https://useiteration.com/api/traces/ingest";
|
|
27868
29220
|
var terminal = terminalFor(process.stdout);
|
|
27869
29221
|
function print(...lines) {
|
|
27870
29222
|
process.stdout.write(`${lines.join("\n")}
|
|
@@ -27958,7 +29310,7 @@ function usage(command) {
|
|
|
27958
29310
|
"",
|
|
27959
29311
|
wrap(
|
|
27960
29312
|
terminal,
|
|
27961
|
-
"
|
|
29313
|
+
"Choose projects in Iteration before any sessions upload."
|
|
27962
29314
|
),
|
|
27963
29315
|
"",
|
|
27964
29316
|
...section("Options for advanced setups", [
|
|
@@ -27967,7 +29319,7 @@ function usage(command) {
|
|
|
27967
29319
|
["--codex-home DIR", "Use a different Codex data directory"],
|
|
27968
29320
|
["--claude-home DIR", "Use a different Claude data directory"],
|
|
27969
29321
|
["--cursor-home DIR", "Use a different Cursor data directory"],
|
|
27970
|
-
["--yes", "
|
|
29322
|
+
["--yes", "Project sharing still requires approval in Iteration"],
|
|
27971
29323
|
[
|
|
27972
29324
|
"--data-directory DIR",
|
|
27973
29325
|
"Use a different state directory with a one-agent command"
|
|
@@ -28006,7 +29358,7 @@ function usage(command) {
|
|
|
28006
29358
|
}
|
|
28007
29359
|
async function fileExists(filename) {
|
|
28008
29360
|
try {
|
|
28009
|
-
await
|
|
29361
|
+
await fs29.access(filename);
|
|
28010
29362
|
return true;
|
|
28011
29363
|
} catch (error3) {
|
|
28012
29364
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT") {
|
|
@@ -28047,7 +29399,7 @@ async function defaultCommand() {
|
|
|
28047
29399
|
await connectAll({});
|
|
28048
29400
|
}
|
|
28049
29401
|
function dataDirectory(source, values) {
|
|
28050
|
-
return
|
|
29402
|
+
return path36.resolve(
|
|
28051
29403
|
optionalString(values, "data-directory") ?? defaultCollectorDirectory(source)
|
|
28052
29404
|
);
|
|
28053
29405
|
}
|
|
@@ -28094,337 +29446,8 @@ function turnSummaryLines(results) {
|
|
|
28094
29446
|
...warnings
|
|
28095
29447
|
];
|
|
28096
29448
|
}
|
|
28097
|
-
var CONNECTED_WIDTH = Math.max(
|
|
28098
|
-
...COLLECTOR_SOURCES2.map(
|
|
28099
|
-
(source) => `${collectorSourceLabel(source)} connected`.length
|
|
28100
|
-
)
|
|
28101
|
-
);
|
|
28102
|
-
function connectedLine(source, sessions) {
|
|
28103
|
-
return mark(
|
|
28104
|
-
terminal,
|
|
28105
|
-
"success",
|
|
28106
|
-
`${padEnd(`${collectorSourceLabel(source)} connected`, CONNECTED_WIDTH)} ${terminal.dim(
|
|
28107
|
-
sessions === 0 ? "no saved sessions yet" : `importing ${sessions.toLocaleString("en-US")} ${sessions === 1 ? "session" : "sessions"}`
|
|
28108
|
-
)}`
|
|
28109
|
-
);
|
|
28110
|
-
}
|
|
28111
|
-
function connectionSummary2(results, imported) {
|
|
28112
|
-
const links = [
|
|
28113
|
-
...new Set(results.map(({ endpoint }) => tracesUrl(endpoint)))
|
|
28114
|
-
];
|
|
28115
|
-
const notes = turnSummaryLines(results);
|
|
28116
|
-
return [
|
|
28117
|
-
...notes.length === 0 ? [] : ["", ...notes],
|
|
28118
|
-
"",
|
|
28119
|
-
wrap(
|
|
28120
|
-
terminal,
|
|
28121
|
-
imported ? `Sessions import newest first. Run ${terminal.bold("iteration")} to follow progress.` : `Run ${terminal.bold("iteration")} to see their status.`
|
|
28122
|
-
),
|
|
28123
|
-
...links.map((url2) => ` Open Iteration ${terminal.link(url2)}`)
|
|
28124
|
-
];
|
|
28125
|
-
}
|
|
28126
|
-
async function initialize(source, values) {
|
|
28127
|
-
requireCollectorPlatform();
|
|
28128
|
-
const endpoint = optionalString(values, "endpoint") ?? DEFAULT_INGEST_ENDPOINT;
|
|
28129
|
-
const endpointUrl = validateCollectorEndpoint(endpoint);
|
|
28130
|
-
const deviceName = optionalString(values, "device-name") ?? os12.hostname();
|
|
28131
|
-
const organizationId = optionalString(values, "workspace");
|
|
28132
|
-
const directory = dataDirectory(source, values);
|
|
28133
|
-
const paths = collectorPaths(directory);
|
|
28134
|
-
const selectedHome = path31.resolve(
|
|
28135
|
-
source === "codex" ? optionalString(values, "codex-home") ?? process.env.CODEX_HOME ?? path31.join(os12.homedir(), ".codex") : source === "claude" ? optionalString(values, "claude-home") ?? process.env.CLAUDE_CONFIG_DIR ?? path31.join(os12.homedir(), ".claude") : optionalString(values, "cursor-home") ?? path31.join(os12.homedir(), ".cursor")
|
|
28136
|
-
);
|
|
28137
|
-
await assertFreshCollectorDirectory(paths, source);
|
|
28138
|
-
const preparedState = await prepareSourceInitialState(
|
|
28139
|
-
source,
|
|
28140
|
-
selectedHome,
|
|
28141
|
-
true
|
|
28142
|
-
);
|
|
28143
|
-
await prepareBackgroundPersistence();
|
|
28144
|
-
const enrollment = await authenticateAndEnrollCollector(
|
|
28145
|
-
endpointUrl,
|
|
28146
|
-
deviceName,
|
|
28147
|
-
organizationId,
|
|
28148
|
-
source
|
|
28149
|
-
);
|
|
28150
|
-
print(
|
|
28151
|
-
mark(
|
|
28152
|
-
terminal,
|
|
28153
|
-
"success",
|
|
28154
|
-
`Signed in to ${terminal.bold(safeWorkspaceName(enrollment.organizationName) ?? "your workspace")}`
|
|
28155
|
-
)
|
|
28156
|
-
);
|
|
28157
|
-
const connection = {
|
|
28158
|
-
endpoint: endpointUrl.toString(),
|
|
28159
|
-
token: enrollment.token,
|
|
28160
|
-
dataDirectory: directory,
|
|
28161
|
-
organizationId: enrollment.organizationId,
|
|
28162
|
-
organizationName: enrollment.organizationName
|
|
28163
|
-
};
|
|
28164
|
-
const config3 = source === "codex" ? { ...connection, codexHome: selectedHome } : source === "claude" ? { ...connection, source: "claude", claudeHome: selectedHome } : { ...connection, source: "cursor", cursorHome: selectedHome };
|
|
28165
|
-
await saveConfig(config3, paths);
|
|
28166
|
-
try {
|
|
28167
|
-
await saveState(preparedState, paths);
|
|
28168
|
-
if (source === "cursor") await installCursorHooks(selectedHome, directory);
|
|
28169
|
-
} catch (error3) {
|
|
28170
|
-
throw new Error(
|
|
28171
|
-
`Collector access was approved, but local setup did not finish. Run iteration start ${source} to repair it.`,
|
|
28172
|
-
{ cause: error3 }
|
|
28173
|
-
);
|
|
28174
|
-
}
|
|
28175
|
-
try {
|
|
28176
|
-
await installBackgroundService(directory, source);
|
|
28177
|
-
} catch (error3) {
|
|
28178
|
-
throw new Error(
|
|
28179
|
-
`Collector access was approved, but background startup failed. Run iteration start ${source} to retry it.`,
|
|
28180
|
-
{ cause: error3 }
|
|
28181
|
-
);
|
|
28182
|
-
}
|
|
28183
|
-
const turnSummary = await setUpTurnSummary(config3);
|
|
28184
|
-
print(connectedLine(source, await countSavedSessions(source, selectedHome)));
|
|
28185
|
-
return {
|
|
28186
|
-
config: config3,
|
|
28187
|
-
result: { source, endpoint: config3.endpoint, turnSummary }
|
|
28188
|
-
};
|
|
28189
|
-
}
|
|
28190
|
-
async function approveAllSources(sources, workspaceName, yes) {
|
|
28191
|
-
const safeName = safeWorkspaceName(workspaceName);
|
|
28192
|
-
print(
|
|
28193
|
-
wrap(
|
|
28194
|
-
terminal,
|
|
28195
|
-
sources.length === 0 ? "No saved agent sessions found on this computer" : `Found ${sources.length} coding ${sources.length === 1 ? "agent" : "agents"} on this computer`,
|
|
28196
|
-
{ style: terminal.bold }
|
|
28197
|
-
),
|
|
28198
|
-
...sources.length === 0 ? [] : [
|
|
28199
|
-
"",
|
|
28200
|
-
rows(
|
|
28201
|
-
terminal,
|
|
28202
|
-
sources.map(({ source, sessions }) => [
|
|
28203
|
-
collectorSourceLabel(source),
|
|
28204
|
-
terminal.dim(
|
|
28205
|
-
`${sessions.toLocaleString("en-US")} saved ${sessions === 1 ? "session" : "sessions"}`
|
|
28206
|
-
)
|
|
28207
|
-
]),
|
|
28208
|
-
" "
|
|
28209
|
-
)
|
|
28210
|
-
],
|
|
28211
|
-
"",
|
|
28212
|
-
wrap(
|
|
28213
|
-
terminal,
|
|
28214
|
-
`Existing sessions and new activity, including prompts, code and tool output, upload to ${safeName ?? "your workspace"}, where its members can read them. Your agents also get the workspace's tools. Supported agents you install later connect automatically.${process.platform === "linux" ? " Collection continues after you log out." : ""}`
|
|
28215
|
-
),
|
|
28216
|
-
""
|
|
28217
|
-
);
|
|
28218
|
-
if (yes) return;
|
|
28219
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
28220
|
-
throw new Error(
|
|
28221
|
-
"Run iteration connect interactively or pass --yes to approve uploads."
|
|
28222
|
-
);
|
|
28223
|
-
}
|
|
28224
|
-
const answer = await ask(
|
|
28225
|
-
terminal,
|
|
28226
|
-
`${terminal.bold(sources.length === 1 ? "Connect this agent?" : "Connect these agents?")} ${terminal.dim("(y/N)")}`
|
|
28227
|
-
);
|
|
28228
|
-
const approved = answer?.trim().toLowerCase();
|
|
28229
|
-
if (approved !== "y" && approved !== "yes")
|
|
28230
|
-
throw new Error(
|
|
28231
|
-
"No agents were connected. Run iteration connect when you are ready."
|
|
28232
|
-
);
|
|
28233
|
-
}
|
|
28234
29449
|
async function connectAll(values) {
|
|
28235
|
-
|
|
28236
|
-
if (values["data-directory"] !== void 0) {
|
|
28237
|
-
throw new Error(
|
|
28238
|
-
"--data-directory requires a source-specific connect command."
|
|
28239
|
-
);
|
|
28240
|
-
}
|
|
28241
|
-
const configs = await Promise.all(COLLECTOR_SOURCES2.map(connectedConfig));
|
|
28242
|
-
const resolveSourceHomes = () => ({
|
|
28243
|
-
codex: path31.resolve(
|
|
28244
|
-
optionalString(values, "codex-home") ?? (configs[0] && "codexHome" in configs[0] ? configs[0].codexHome : sourceHome("codex"))
|
|
28245
|
-
),
|
|
28246
|
-
claude: path31.resolve(
|
|
28247
|
-
optionalString(values, "claude-home") ?? (configs[1] && "claudeHome" in configs[1] ? configs[1].claudeHome : sourceHome("claude"))
|
|
28248
|
-
),
|
|
28249
|
-
cursor: path31.resolve(
|
|
28250
|
-
optionalString(values, "cursor-home") ?? (configs[2] && "cursorHome" in configs[2] ? configs[2].cursorHome : sourceHome("cursor"))
|
|
28251
|
-
)
|
|
28252
|
-
});
|
|
28253
|
-
const sourceHomes = resolveSourceHomes();
|
|
28254
|
-
const detected = await detectedSources(sourceHomes);
|
|
28255
|
-
const existing = COLLECTOR_SOURCES2.flatMap(
|
|
28256
|
-
(source, index) => configs[index] ? [{ source, config: configs[index] }] : []
|
|
28257
|
-
);
|
|
28258
|
-
print(brand(terminal), "");
|
|
28259
|
-
if (detected.length === 0 && existing.length === 0) {
|
|
28260
|
-
print(
|
|
28261
|
-
mark(terminal, "warning", "No supported local coding agents found yet"),
|
|
28262
|
-
wrap(
|
|
28263
|
-
terminal,
|
|
28264
|
-
`Run ${terminal.bold("iteration connect")} after using Codex, Claude Code or Cursor on this computer.`
|
|
28265
|
-
)
|
|
28266
|
-
);
|
|
28267
|
-
return;
|
|
28268
|
-
}
|
|
28269
|
-
const sessions = await Promise.all(
|
|
28270
|
-
detected.map(async (source) => ({
|
|
28271
|
-
source,
|
|
28272
|
-
sessions: await countSavedSessions(source, sourceHomes[source])
|
|
28273
|
-
}))
|
|
28274
|
-
);
|
|
28275
|
-
const anchorSource = existing[0]?.source ?? detected[0];
|
|
28276
|
-
const anchorExisting = existing[0]?.config ?? null;
|
|
28277
|
-
const endpoint = validateCollectorEndpoint(
|
|
28278
|
-
optionalString(values, "endpoint") ?? anchorExisting?.endpoint ?? DEFAULT_INGEST_ENDPOINT
|
|
28279
|
-
).toString();
|
|
28280
|
-
const existingScopes = await Promise.all(
|
|
28281
|
-
existing.map(async (item) => ({
|
|
28282
|
-
...item,
|
|
28283
|
-
scope: await readCollectorScope(item.config)
|
|
28284
|
-
}))
|
|
28285
|
-
);
|
|
28286
|
-
const validateWorkspace = () => {
|
|
28287
|
-
for (const item of existingScopes) {
|
|
28288
|
-
if (item.config.endpoint !== endpoint) {
|
|
28289
|
-
throw new Error(
|
|
28290
|
-
"Existing sources use different Iteration servers. Connect one source at a time."
|
|
28291
|
-
);
|
|
28292
|
-
}
|
|
28293
|
-
if (existingScopes[0]?.scope.organizationId !== item.scope.organizationId) {
|
|
28294
|
-
throw new Error(
|
|
28295
|
-
"Existing sources use different workspaces. Connect one source at a time."
|
|
28296
|
-
);
|
|
28297
|
-
}
|
|
28298
|
-
}
|
|
28299
|
-
const requestedOrganizationId2 = optionalString(values, "workspace");
|
|
28300
|
-
if (requestedOrganizationId2 && existingScopes[0] && requestedOrganizationId2 !== existingScopes[0].scope.organizationId) {
|
|
28301
|
-
throw new Error(
|
|
28302
|
-
"The existing collector belongs to a different workspace."
|
|
28303
|
-
);
|
|
28304
|
-
}
|
|
28305
|
-
};
|
|
28306
|
-
validateWorkspace();
|
|
28307
|
-
const requestedOrganizationId = optionalString(values, "workspace");
|
|
28308
|
-
const previousConsent = await loadMachineConsent();
|
|
28309
|
-
const approveConnection = async () => {
|
|
28310
|
-
const needsApproval = () => previousConsent === null || anchorExisting === null || previousConsent.endpoint !== endpoint || previousConsent.anchorSource !== anchorSource || previousConsent.anchorDataDirectory !== anchorExisting.dataDirectory || previousConsent.organizationId !== existingScopes[0]?.scope.organizationId || previousConsent.sourceHomes.codex !== sourceHomes.codex || previousConsent.sourceHomes.claude !== sourceHomes.claude || previousConsent.sourceHomes.cursor !== sourceHomes.cursor;
|
|
28311
|
-
if (needsApproval()) {
|
|
28312
|
-
await approveAllSources(
|
|
28313
|
-
sessions,
|
|
28314
|
-
existingScopes[0]?.scope.organizationName ?? null,
|
|
28315
|
-
values.yes === true
|
|
28316
|
-
);
|
|
28317
|
-
}
|
|
28318
|
-
};
|
|
28319
|
-
await approveConnection();
|
|
28320
|
-
await prepareBackgroundPersistence();
|
|
28321
|
-
const results = [];
|
|
28322
|
-
const initializeAnchor = async () => {
|
|
28323
|
-
const initialized = await initialize(anchorSource, values);
|
|
28324
|
-
results.push(initialized.result);
|
|
28325
|
-
return initialized.config;
|
|
28326
|
-
};
|
|
28327
|
-
let anchor3 = anchorExisting ?? await initializeAnchor();
|
|
28328
|
-
let organizationId = requestedOrganizationId ?? existingScopes[0]?.scope.organizationId ?? anchor3.organizationId ?? null;
|
|
28329
|
-
const connectDetectedSources = async () => {
|
|
28330
|
-
for (const source of detected) {
|
|
28331
|
-
if (source === anchorSource || configs[COLLECTOR_SOURCES2.indexOf(source)] !== null)
|
|
28332
|
-
continue;
|
|
28333
|
-
const paths = collectorPaths(defaultCollectorDirectory(source));
|
|
28334
|
-
await assertFreshCollectorDirectory(paths, source);
|
|
28335
|
-
const state = await prepareSourceInitialState(
|
|
28336
|
-
source,
|
|
28337
|
-
sourceHomes[source],
|
|
28338
|
-
true
|
|
28339
|
-
);
|
|
28340
|
-
const enrollment = await expandSource(anchor3, source, organizationId);
|
|
28341
|
-
const config3 = await activateSource(
|
|
28342
|
-
source,
|
|
28343
|
-
sourceHomes[source],
|
|
28344
|
-
endpoint,
|
|
28345
|
-
enrollment,
|
|
28346
|
-
state
|
|
28347
|
-
);
|
|
28348
|
-
organizationId = enrollment.organizationId;
|
|
28349
|
-
results.push({
|
|
28350
|
-
source,
|
|
28351
|
-
endpoint: config3.endpoint,
|
|
28352
|
-
turnSummary: await setUpTurnSummary(config3)
|
|
28353
|
-
});
|
|
28354
|
-
print(
|
|
28355
|
-
connectedLine(
|
|
28356
|
-
source,
|
|
28357
|
-
sessions.find((item) => item.source === source)?.sessions ?? 0
|
|
28358
|
-
)
|
|
28359
|
-
);
|
|
28360
|
-
}
|
|
28361
|
-
};
|
|
28362
|
-
await connectDetectedSources();
|
|
28363
|
-
if (organizationId === null) {
|
|
28364
|
-
organizationId = (await readCollectorScope(anchor3)).organizationId;
|
|
28365
|
-
}
|
|
28366
|
-
if (anchor3.organizationId === void 0) {
|
|
28367
|
-
anchor3 = { ...anchor3, organizationId };
|
|
28368
|
-
await saveConfig(anchor3, collectorPaths(anchor3.dataDirectory));
|
|
28369
|
-
}
|
|
28370
|
-
await saveMachineConsent({
|
|
28371
|
-
version: 1,
|
|
28372
|
-
endpoint,
|
|
28373
|
-
organizationId,
|
|
28374
|
-
anchorSource,
|
|
28375
|
-
anchorDataDirectory: anchor3.dataDirectory,
|
|
28376
|
-
sourceHomes
|
|
28377
|
-
});
|
|
28378
|
-
for (const item of existing) {
|
|
28379
|
-
await ensureStartableState(
|
|
28380
|
-
item.config,
|
|
28381
|
-
collectorPaths(item.config.dataDirectory)
|
|
28382
|
-
);
|
|
28383
|
-
if ("cursorHome" in item.config)
|
|
28384
|
-
await installCursorHooks(
|
|
28385
|
-
item.config.cursorHome,
|
|
28386
|
-
item.config.dataDirectory
|
|
28387
|
-
);
|
|
28388
|
-
await installBackgroundService(item.config.dataDirectory, item.source);
|
|
28389
|
-
results.push({
|
|
28390
|
-
source: item.source,
|
|
28391
|
-
endpoint: item.config.endpoint,
|
|
28392
|
-
turnSummary: await setUpTurnSummary(item.config)
|
|
28393
|
-
});
|
|
28394
|
-
print(
|
|
28395
|
-
mark(
|
|
28396
|
-
terminal,
|
|
28397
|
-
"success",
|
|
28398
|
-
`${collectorSourceLabel(item.source)} is still connected`
|
|
28399
|
-
)
|
|
28400
|
-
);
|
|
28401
|
-
}
|
|
28402
|
-
await addAgentTools(
|
|
28403
|
-
results.map((result) => result.source),
|
|
28404
|
-
sourceHomes
|
|
28405
|
-
);
|
|
28406
|
-
print(...connectionSummary2(results, results.length > existing.length));
|
|
28407
|
-
}
|
|
28408
|
-
async function addAgentTools(sources, sourceHomes) {
|
|
28409
|
-
for (const source of new Set(sources)) {
|
|
28410
|
-
try {
|
|
28411
|
-
await installAgentTools(
|
|
28412
|
-
source === "cursor" ? { cursor: true } : source === "claude" ? { claude: sourceHomes.claude } : { codex: sourceHomes.codex }
|
|
28413
|
-
);
|
|
28414
|
-
} catch (error3) {
|
|
28415
|
-
print(
|
|
28416
|
-
mark(
|
|
28417
|
-
terminal,
|
|
28418
|
-
"warning",
|
|
28419
|
-
`Could not add Iteration's tools to ${collectorSourceLabel(source)}`
|
|
28420
|
-
),
|
|
28421
|
-
wrap(
|
|
28422
|
-
terminal,
|
|
28423
|
-
error3 instanceof Error ? error3.message : "Its settings could not be updated."
|
|
28424
|
-
)
|
|
28425
|
-
);
|
|
28426
|
-
}
|
|
28427
|
-
}
|
|
29450
|
+
await connectWorkspace(values);
|
|
28428
29451
|
}
|
|
28429
29452
|
async function importHistory(source, values) {
|
|
28430
29453
|
requireCollectorPlatform();
|
|
@@ -28757,7 +29780,9 @@ async function runInternalCommand() {
|
|
|
28757
29780
|
}
|
|
28758
29781
|
if (process.argv[2] === "mcp") {
|
|
28759
29782
|
if (process.argv.length !== 3) throw new Error("Invalid mcp command.");
|
|
28760
|
-
const configs = await Promise.all(
|
|
29783
|
+
const configs = await Promise.all(
|
|
29784
|
+
COLLECTOR_SOURCES2.map((source) => connectedConfig(source))
|
|
29785
|
+
);
|
|
28761
29786
|
await serveAgentTools(configs.find((config3) => config3 !== null) ?? null);
|
|
28762
29787
|
return true;
|
|
28763
29788
|
}
|
|
@@ -28885,9 +29910,7 @@ async function dispatchSourceCommand(command, source, values, selectedSource) {
|
|
|
28885
29910
|
};
|
|
28886
29911
|
validateSourceOptions();
|
|
28887
29912
|
if (command === "connect" && selectedSource !== null) {
|
|
28888
|
-
|
|
28889
|
-
const { result } = await initialize(selectedSource, values);
|
|
28890
|
-
print(...connectionSummary2([result], true));
|
|
29913
|
+
await connectWorkspace(values, selectedSource);
|
|
28891
29914
|
return;
|
|
28892
29915
|
}
|
|
28893
29916
|
if (command === "run") {
|