iteration-cli 0.17.1 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/dist/cli.js +1972 -788
- 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 path30 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,
|
|
@@ -7973,16 +7972,16 @@ function consoleWriter(line, level) {
|
|
|
7973
7972
|
}
|
|
7974
7973
|
var loggingContext = () => ({});
|
|
7975
7974
|
var logWriter = consoleWriter;
|
|
7976
|
-
function configureCollectorLogging(context2,
|
|
7975
|
+
function configureCollectorLogging(context2, write2 = consoleWriter) {
|
|
7977
7976
|
loggingContext = context2;
|
|
7978
|
-
logWriter =
|
|
7977
|
+
logWriter = write2;
|
|
7979
7978
|
}
|
|
7980
7979
|
function writeLog(line, level) {
|
|
7981
7980
|
logWriter(line, level);
|
|
7982
7981
|
}
|
|
7983
|
-
function createLogger(context2,
|
|
7982
|
+
function createLogger(context2, write2 = writeLog) {
|
|
7984
7983
|
return (entry) => {
|
|
7985
|
-
|
|
7984
|
+
write2(
|
|
7986
7985
|
JSON.stringify({
|
|
7987
7986
|
...entry,
|
|
7988
7987
|
...context2,
|
|
@@ -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;
|
|
@@ -22550,11 +22687,148 @@ function isLaunchDirectory(value) {
|
|
|
22550
22687
|
return true;
|
|
22551
22688
|
}
|
|
22552
22689
|
|
|
22553
|
-
// src/
|
|
22554
|
-
import {
|
|
22555
|
-
import
|
|
22556
|
-
import
|
|
22557
|
-
import
|
|
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
|
|
22697
|
+
import { promises as fs11 } from "node:fs";
|
|
22698
|
+
import path8 from "node:path";
|
|
22699
|
+
|
|
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();
|
|
22558
22832
|
|
|
22559
22833
|
// ../collector-contracts/dist/index.js
|
|
22560
22834
|
var cursorBillingRequestSchema = object3({
|
|
@@ -22621,10 +22895,12 @@ var collectorHistoryProgressSchema = object3({
|
|
|
22621
22895
|
complete: boolean4()
|
|
22622
22896
|
}).strict().optional().describe("The newest messages sent ahead of the rest of the history; collectors before 0.17.0 omit it.")
|
|
22623
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);
|
|
22624
22899
|
var collectorEnrollmentRequestSchema = object3({
|
|
22625
22900
|
deviceName: string5().trim().min(1).max(160).describe("Trimmed before length validation."),
|
|
22626
22901
|
organizationId: string5().regex(UUID_PATTERN3).optional(),
|
|
22627
|
-
|
|
22902
|
+
computerId: uuid5().optional(),
|
|
22903
|
+
source: collectorSourceSchema.optional()
|
|
22628
22904
|
}).strict();
|
|
22629
22905
|
var collectorExpansionRequestSchema = object3({
|
|
22630
22906
|
source: _enum2(COLLECTOR_SOURCES2),
|
|
@@ -22663,141 +22939,476 @@ function parseCollectorFrictionSummary(input) {
|
|
|
22663
22939
|
};
|
|
22664
22940
|
}
|
|
22665
22941
|
|
|
22666
|
-
// src/
|
|
22667
|
-
import {
|
|
22668
|
-
import os6 from "node:os";
|
|
22669
|
-
import path20 from "node:path";
|
|
22670
|
-
|
|
22671
|
-
// src/launch-agent.ts
|
|
22672
|
-
import { execFile as execFileCallback2 } from "node:child_process";
|
|
22673
|
-
import { promises as fs11 } from "node:fs";
|
|
22674
|
-
import os3 from "node:os";
|
|
22675
|
-
import path8 from "node:path";
|
|
22676
|
-
import { setTimeout as sleep } from "node:timers/promises";
|
|
22677
|
-
import { promisify as promisify2 } from "node:util";
|
|
22678
|
-
|
|
22679
|
-
// src/runtime.ts
|
|
22680
|
-
import { createHash as createHash4 } from "node:crypto";
|
|
22942
|
+
// src/sharing-storage.ts
|
|
22943
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
22681
22944
|
import { promises as fs6 } from "node:fs";
|
|
22682
22945
|
import os from "node:os";
|
|
22683
22946
|
import path7 from "node:path";
|
|
22684
|
-
|
|
22685
|
-
|
|
22686
|
-
|
|
22687
|
-
|
|
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) {
|
|
22688
22963
|
return {
|
|
22689
|
-
|
|
22690
|
-
|
|
22691
|
-
|
|
22692
|
-
|
|
22693
|
-
|
|
22694
|
-
settings: path7.join(root, "settings.json"),
|
|
22695
|
-
lock: path7.join(root, "state.lock"),
|
|
22696
|
-
updateLock: path7.join(root, "update.lock"),
|
|
22697
|
-
launcher: path7.join(root, "launcher.js")
|
|
22964
|
+
token: enrollment.token,
|
|
22965
|
+
organizationId: enrollment.organizationId,
|
|
22966
|
+
organizationName: enrollment.organizationName,
|
|
22967
|
+
source,
|
|
22968
|
+
endpoint
|
|
22698
22969
|
};
|
|
22699
22970
|
}
|
|
22700
|
-
|
|
22701
|
-
|
|
22702
|
-
var versionSchema = string5().regex(VERSION_PATTERN);
|
|
22703
|
-
var sourceSchema = _enum2(COLLECTOR_SOURCES2);
|
|
22704
|
-
var timeSchema = number4().int().min(0);
|
|
22705
|
-
var trialSchema = object3({
|
|
22706
|
-
version: versionSchema,
|
|
22707
|
-
previous: versionSchema,
|
|
22708
|
-
startedAtMs: timeSchema,
|
|
22709
|
-
expected: array2(sourceSchema),
|
|
22710
|
-
starts: partialRecord(sourceSchema, number4().int().min(0)),
|
|
22711
|
-
confirmed: array2(sourceSchema)
|
|
22712
|
-
}).loose();
|
|
22713
|
-
var runtimeStateSchema = object3({
|
|
22714
|
-
version: literal2(1),
|
|
22715
|
-
current: versionSchema,
|
|
22716
|
-
previous: versionSchema.nullable(),
|
|
22717
|
-
trial: trialSchema.nullable(),
|
|
22718
|
-
rejected: array2(
|
|
22719
|
-
object3({
|
|
22720
|
-
version: versionSchema,
|
|
22721
|
-
reason: string5().max(500),
|
|
22722
|
-
atMs: timeSchema
|
|
22723
|
-
}).loose()
|
|
22724
|
-
).max(20),
|
|
22725
|
-
lastCheck: object3({ atMs: timeSchema, error: string5().max(500).nullable() }).loose().nullable(),
|
|
22726
|
-
lastUpdate: object3({ from: versionSchema, to: versionSchema, atMs: timeSchema }).loose().nullable()
|
|
22727
|
-
}).loose();
|
|
22728
|
-
var settingsSchema2 = object3({
|
|
22729
|
-
autoUpdate: boolean4(),
|
|
22730
|
-
registry: string5().url()
|
|
22731
|
-
}).loose();
|
|
22732
|
-
function errorCode2(error3) {
|
|
22733
|
-
if (error3 instanceof Error && "code" in error3) return String(error3.code);
|
|
22734
|
-
return null;
|
|
22735
|
-
}
|
|
22736
|
-
function isVersion(value) {
|
|
22737
|
-
return VERSION_PATTERN.test(value);
|
|
22738
|
-
}
|
|
22739
|
-
function versionParts(value) {
|
|
22740
|
-
const match = VERSION_PATTERN.exec(value);
|
|
22741
|
-
if (match === null) throw new Error(`Invalid Iteration version ${value}.`);
|
|
22742
|
-
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
22971
|
+
function sharingDirectory() {
|
|
22972
|
+
return path7.join(os.homedir(), ".config", "iteration", "sharing");
|
|
22743
22973
|
}
|
|
22744
|
-
function
|
|
22745
|
-
|
|
22746
|
-
|
|
22747
|
-
|
|
22748
|
-
|
|
22749
|
-
|
|
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;
|
|
22750
22985
|
}
|
|
22751
|
-
return
|
|
22986
|
+
return registrySchema.parse(JSON.parse(contents));
|
|
22752
22987
|
}
|
|
22753
|
-
function
|
|
22754
|
-
const
|
|
22755
|
-
|
|
22756
|
-
|
|
22757
|
-
|
|
22758
|
-
|
|
22759
|
-
|
|
22760
|
-
|
|
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: []
|
|
22761
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();
|
|
22762
23013
|
}
|
|
22763
|
-
const required3 = [
|
|
22764
|
-
Number(match[1]),
|
|
22765
|
-
Number(match[2] ?? 0),
|
|
22766
|
-
Number(match[3] ?? 0)
|
|
22767
|
-
];
|
|
22768
|
-
const actual = nodeVersion.split(".").map(Number);
|
|
22769
|
-
for (let index = 0; index < 3; index += 1) {
|
|
22770
|
-
const difference = (actual[index] ?? 0) - required3[index];
|
|
22771
|
-
if (difference > 0) return { ok: true };
|
|
22772
|
-
if (difference < 0) {
|
|
22773
|
-
return {
|
|
22774
|
-
ok: false,
|
|
22775
|
-
reason: `it needs Node.js ${range} and this computer runs ${nodeVersion}`
|
|
22776
|
-
};
|
|
22777
|
-
}
|
|
22778
|
-
}
|
|
22779
|
-
return { ok: true };
|
|
22780
23014
|
}
|
|
22781
|
-
async function
|
|
22782
|
-
|
|
22783
|
-
|
|
22784
|
-
|
|
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
|
+
async function fetchSharingSnapshot(config3, request2 = collectorFetch, signal) {
|
|
23029
|
+
const response = await request2(
|
|
23030
|
+
new URL("./collector-sharing", config3.endpoint),
|
|
23031
|
+
{
|
|
23032
|
+
headers: { authorization: `Bearer ${config3.token}` },
|
|
23033
|
+
signal: AbortSignal.any([
|
|
23034
|
+
AbortSignal.timeout(15e3),
|
|
23035
|
+
...signal ? [signal] : []
|
|
23036
|
+
])
|
|
23037
|
+
}
|
|
23038
|
+
);
|
|
23039
|
+
if (!response.ok)
|
|
23040
|
+
throw new Error(
|
|
23041
|
+
`Could not check project sharing (HTTP ${response.status}). Collection is waiting.`
|
|
23042
|
+
);
|
|
23043
|
+
return collectorSharingSnapshotSchema.parse(await response.json());
|
|
23044
|
+
}
|
|
23045
|
+
function workspaceForSource(snapshot, location, source) {
|
|
23046
|
+
const organizationId = sharingWorkspaceForLocation(
|
|
23047
|
+
snapshot,
|
|
23048
|
+
resolveCaptureLocation(location)
|
|
23049
|
+
);
|
|
23050
|
+
return snapshot.workspaces.some(
|
|
23051
|
+
(workspace) => workspace.organizationId === organizationId && workspace.connectedSources.includes(source)
|
|
23052
|
+
) ? organizationId : null;
|
|
23053
|
+
}
|
|
23054
|
+
function hostCapturePolicy(snapshot, registry3, config3) {
|
|
23055
|
+
const source = collectorSource(config3);
|
|
23056
|
+
const permitted = (location) => {
|
|
23057
|
+
const organizationId = workspaceForSource(snapshot, location, source);
|
|
23058
|
+
return organizationId !== null && sharingConnection(registry3, organizationId, source) !== null;
|
|
23059
|
+
};
|
|
23060
|
+
return {
|
|
23061
|
+
revision: snapshot.revision,
|
|
23062
|
+
batchKey: (event3) => event3.capture ? sharingWorkspaceForLocation(snapshot, event3.capture.reference) ?? "unshared" : "unshared",
|
|
23063
|
+
authorizeRecord: (recordSource, line, filename, cwds) => {
|
|
23064
|
+
const location = captureRecordLocation(
|
|
23065
|
+
recordSource,
|
|
23066
|
+
line,
|
|
23067
|
+
filename,
|
|
23068
|
+
cwds
|
|
23069
|
+
);
|
|
23070
|
+
return location !== null && permitted(location) ? {
|
|
23071
|
+
revision: snapshot.revision,
|
|
23072
|
+
basis: "location",
|
|
23073
|
+
reference: location
|
|
23074
|
+
} : null;
|
|
23075
|
+
},
|
|
23076
|
+
authorizeEvent: (event3) => event3.capture?.basis === "location" && event3.capture.revision === snapshot.revision && permitted(event3.capture.reference) ? event3 : null
|
|
23077
|
+
};
|
|
23078
|
+
}
|
|
23079
|
+
var sessionIndexSchema = record3(
|
|
23080
|
+
string5(),
|
|
23081
|
+
object3({ location: string5().nullable(), at: number4() }).strict()
|
|
23082
|
+
);
|
|
23083
|
+
async function loadSessionIndex(config3) {
|
|
23084
|
+
try {
|
|
23085
|
+
return sessionIndexSchema.parse(
|
|
23086
|
+
JSON.parse(
|
|
23087
|
+
await fs11.readFile(
|
|
23088
|
+
path8.join(config3.dataDirectory, "shared-sessions.json"),
|
|
23089
|
+
"utf8"
|
|
23090
|
+
)
|
|
23091
|
+
)
|
|
23092
|
+
);
|
|
23093
|
+
} catch (error3) {
|
|
23094
|
+
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
23095
|
+
return {};
|
|
23096
|
+
throw error3;
|
|
23097
|
+
}
|
|
23098
|
+
}
|
|
23099
|
+
async function sharingTransport(config3, request2 = collectorFetch, signal, savedRegistry) {
|
|
23100
|
+
const registry3 = savedRegistry ?? await readSharingRegistry();
|
|
23101
|
+
if (registry3 === null)
|
|
23102
|
+
throw new Error(
|
|
23103
|
+
"Choose projects in Iteration before sharing this computer."
|
|
23104
|
+
);
|
|
23105
|
+
const connections = registry3.connections.filter(
|
|
23106
|
+
(connection) => connection.source === collectorSource(config3) && connection.endpoint === config3.endpoint
|
|
23107
|
+
);
|
|
23108
|
+
let snapshot = null;
|
|
23109
|
+
for (const connection of connections) {
|
|
23110
|
+
try {
|
|
23111
|
+
snapshot = await fetchSharingSnapshot(connection, request2, signal);
|
|
23112
|
+
break;
|
|
23113
|
+
} catch (error3) {
|
|
23114
|
+
if (signal?.aborted) throw error3;
|
|
23115
|
+
}
|
|
23116
|
+
}
|
|
23117
|
+
if (snapshot === null || snapshot.computerId !== registry3.computerId || snapshot.ownerId !== registry3.ownerId)
|
|
23118
|
+
throw new Error("Could not verify project sharing. Collection is waiting.");
|
|
23119
|
+
const current = snapshot;
|
|
23120
|
+
const capture = hostCapturePolicy(current, registry3, config3);
|
|
23121
|
+
const index = await loadSessionIndex(config3);
|
|
23122
|
+
const destination = (location) => {
|
|
23123
|
+
const organizationId = workspaceForSource(
|
|
23124
|
+
current,
|
|
23125
|
+
location,
|
|
23126
|
+
collectorSource(config3)
|
|
23127
|
+
);
|
|
23128
|
+
return organizationId === null ? null : sharingConnection(registry3, organizationId, collectorSource(config3));
|
|
23129
|
+
};
|
|
23130
|
+
const forSession = (id) => {
|
|
23131
|
+
const location = index[id]?.location;
|
|
23132
|
+
return location ? destination(location) : null;
|
|
23133
|
+
};
|
|
23134
|
+
const upload = async (init, sendTo) => {
|
|
23135
|
+
const body = string5().parse(init?.body);
|
|
23136
|
+
const parsed = parseTraceEventBatch2(JSON.parse(body));
|
|
23137
|
+
if (!parsed.success) throw new Error(parsed.error);
|
|
23138
|
+
const batches = /* @__PURE__ */ new Map();
|
|
23139
|
+
for (const event3 of parsed.value.events) {
|
|
23140
|
+
if (capture.authorizeEvent(event3) === null)
|
|
23141
|
+
throw new Error("Project sharing changed. Waiting for the next scan.");
|
|
23142
|
+
const connection = destination(event3.capture.reference);
|
|
23143
|
+
if (!connection) throw new Error("The project is no longer shared.");
|
|
23144
|
+
const events = batches.get(connection.organizationId) ?? [];
|
|
23145
|
+
events.push(event3);
|
|
23146
|
+
batches.set(connection.organizationId, events);
|
|
23147
|
+
const previous = index[event3.sessionId];
|
|
23148
|
+
index[event3.sessionId] = {
|
|
23149
|
+
location: previous && previous.location !== event3.capture.reference ? null : event3.capture.reference,
|
|
23150
|
+
at: Date.now()
|
|
23151
|
+
};
|
|
23152
|
+
}
|
|
23153
|
+
const acceptedEventIds = [];
|
|
23154
|
+
let deviceId = "";
|
|
23155
|
+
let retryAfter = null;
|
|
23156
|
+
for (const [organizationId, events] of batches) {
|
|
23157
|
+
const response = await sendTo(organizationId, JSON.stringify({ events }));
|
|
23158
|
+
if (response.status !== 202) return response;
|
|
23159
|
+
retryAfter = response.headers.get("retry-after");
|
|
23160
|
+
const acknowledgement = parseTraceEventAcknowledgement2(
|
|
23161
|
+
await response.json(),
|
|
23162
|
+
events.map((event3) => event3.eventId)
|
|
23163
|
+
);
|
|
23164
|
+
if (!acknowledgement.success) throw new Error(acknowledgement.error);
|
|
23165
|
+
acceptedEventIds.push(...acknowledgement.value.acceptedEventIds);
|
|
23166
|
+
deviceId = acknowledgement.value.deviceId;
|
|
23167
|
+
}
|
|
23168
|
+
const retained = Object.fromEntries(
|
|
23169
|
+
Object.entries(index).sort((left, right) => right[1].at - left[1].at).slice(0, 1e5)
|
|
23170
|
+
);
|
|
23171
|
+
const filename = path8.join(config3.dataDirectory, "shared-sessions.json");
|
|
23172
|
+
const temporary = `${filename}.${process.pid}.tmp`;
|
|
23173
|
+
await fs11.writeFile(temporary, JSON.stringify(retained), { mode: 384 });
|
|
23174
|
+
await fs11.rename(temporary, filename);
|
|
23175
|
+
return Response.json(
|
|
23176
|
+
{ deviceId, acceptedEventIds },
|
|
23177
|
+
{
|
|
23178
|
+
status: 202,
|
|
23179
|
+
headers: retryAfter === null ? {} : { "Retry-After": retryAfter }
|
|
23180
|
+
}
|
|
23181
|
+
);
|
|
23182
|
+
};
|
|
23183
|
+
const send = (input, init) => request2(input, init);
|
|
23184
|
+
const routed = async (input, init) => {
|
|
23185
|
+
const url2 = new URL(String(input));
|
|
23186
|
+
const sendTo = (organizationId, body) => {
|
|
23187
|
+
const connection = sharingConnection(
|
|
23188
|
+
registry3,
|
|
23189
|
+
organizationId,
|
|
23190
|
+
collectorSource(config3)
|
|
23191
|
+
);
|
|
23192
|
+
if (!connection)
|
|
23193
|
+
throw new Error(
|
|
23194
|
+
"The project workspace has no agent connection. Run iteration connect."
|
|
23195
|
+
);
|
|
23196
|
+
return send(input, {
|
|
23197
|
+
...init,
|
|
23198
|
+
headers: {
|
|
23199
|
+
...Object.fromEntries(new Headers(init?.headers)),
|
|
23200
|
+
authorization: `Bearer ${connection.token}`
|
|
23201
|
+
},
|
|
23202
|
+
...body === void 0 ? {} : { body }
|
|
23203
|
+
});
|
|
23204
|
+
};
|
|
23205
|
+
if (url2.pathname === new URL(config3.endpoint).pathname)
|
|
23206
|
+
return upload(init, sendTo);
|
|
23207
|
+
if (url2.pathname.endsWith("/friction-summary")) {
|
|
23208
|
+
const directory = url2.searchParams.get("directory");
|
|
23209
|
+
const connection = directory === null ? null : destination(directory);
|
|
23210
|
+
return connection ? sendTo(connection.organizationId) : Response.json({ weekEpisodes: 0, dayEpisodes: 0 });
|
|
23211
|
+
}
|
|
23212
|
+
if (url2.pathname.endsWith("/codex-billing")) {
|
|
23213
|
+
const body = codexBillingRequestSchema.parse(
|
|
23214
|
+
JSON.parse(String(init?.body))
|
|
23215
|
+
);
|
|
23216
|
+
let matched = 0;
|
|
23217
|
+
for (const workspace of current.workspaces.filter(
|
|
23218
|
+
(workspace2) => workspace2.active && workspace2.connectedSources.includes(collectorSource(config3))
|
|
23219
|
+
)) {
|
|
23220
|
+
const tasks = body.tasks.filter(
|
|
23221
|
+
(task) => forSession(task.threadId)?.organizationId === workspace.organizationId
|
|
23222
|
+
).map((task) => ({
|
|
23223
|
+
...task,
|
|
23224
|
+
descendantThreadIds: task.descendantThreadIds.filter(
|
|
23225
|
+
(id) => forSession(id)?.organizationId === workspace.organizationId
|
|
23226
|
+
)
|
|
23227
|
+
}));
|
|
23228
|
+
if (tasks.length === 0) continue;
|
|
23229
|
+
const ids = new Set(
|
|
23230
|
+
tasks.flatMap((task) => [task.threadId, ...task.descendantThreadIds])
|
|
23231
|
+
);
|
|
23232
|
+
const response = await sendTo(
|
|
23233
|
+
workspace.organizationId,
|
|
23234
|
+
JSON.stringify({
|
|
23235
|
+
...body,
|
|
23236
|
+
tasks,
|
|
23237
|
+
authTurns: body.authTurns.filter((turn) => ids.has(turn.threadId))
|
|
23238
|
+
})
|
|
23239
|
+
);
|
|
23240
|
+
if (!response.ok) return response;
|
|
23241
|
+
matched += object3({ matched: number4() }).parse(await response.json()).matched;
|
|
23242
|
+
}
|
|
23243
|
+
return Response.json({ matched });
|
|
23244
|
+
}
|
|
23245
|
+
if (url2.pathname.endsWith("/cursor-billing")) {
|
|
23246
|
+
const body = cursorBillingRequestSchema.parse(
|
|
23247
|
+
JSON.parse(String(init?.body))
|
|
23248
|
+
);
|
|
23249
|
+
const connection = forSession(`cursor:${body.conversationId}`);
|
|
23250
|
+
return connection ? sendTo(connection.organizationId) : Response.json({ matched: 0 });
|
|
23251
|
+
}
|
|
23252
|
+
if (url2.pathname.endsWith("/collector-progress")) {
|
|
23253
|
+
for (const workspace of current.workspaces.filter(
|
|
23254
|
+
(workspace2) => workspace2.active
|
|
23255
|
+
)) {
|
|
23256
|
+
if (!sharingConnection(
|
|
23257
|
+
registry3,
|
|
23258
|
+
workspace.organizationId,
|
|
23259
|
+
collectorSource(config3)
|
|
23260
|
+
))
|
|
23261
|
+
continue;
|
|
23262
|
+
const response = await sendTo(workspace.organizationId);
|
|
23263
|
+
if (!response.ok) return response;
|
|
23264
|
+
await response.body?.cancel();
|
|
23265
|
+
}
|
|
23266
|
+
return Response.json({ ok: true });
|
|
23267
|
+
}
|
|
23268
|
+
throw new Error("This collector request does not support project sharing.");
|
|
23269
|
+
};
|
|
23270
|
+
return { capture, fetcher: routed, snapshot: current };
|
|
23271
|
+
}
|
|
23272
|
+
async function configForSharedDirectory(config3, directory) {
|
|
23273
|
+
if (config3.destination !== "host") return config3;
|
|
23274
|
+
const transport = await sharingTransport(config3);
|
|
23275
|
+
const organizationId = workspaceForSource(
|
|
23276
|
+
transport.snapshot,
|
|
23277
|
+
directory,
|
|
23278
|
+
collectorSource(config3)
|
|
23279
|
+
);
|
|
23280
|
+
const registry3 = await readSharingRegistry();
|
|
23281
|
+
const connection = organizationId && registry3 ? sharingConnection(registry3, organizationId, collectorSource(config3)) : null;
|
|
23282
|
+
return connection ? {
|
|
23283
|
+
...config3,
|
|
23284
|
+
token: connection.token,
|
|
23285
|
+
organizationId: connection.organizationId,
|
|
23286
|
+
organizationName: connection.organizationName
|
|
23287
|
+
} : null;
|
|
23288
|
+
}
|
|
23289
|
+
|
|
23290
|
+
// src/runtime.ts
|
|
23291
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
23292
|
+
import { promises as fs12 } from "node:fs";
|
|
23293
|
+
import os3 from "node:os";
|
|
23294
|
+
import path17 from "node:path";
|
|
23295
|
+
import { setTimeout as delay2 } from "node:timers/promises";
|
|
23296
|
+
import { fileURLToPath } from "node:url";
|
|
23297
|
+
function runtimePaths(home = os3.homedir()) {
|
|
23298
|
+
const root = path17.join(home, ".config", "iteration", "runtime");
|
|
23299
|
+
return {
|
|
23300
|
+
home,
|
|
23301
|
+
root,
|
|
23302
|
+
versions: path17.join(root, "versions"),
|
|
23303
|
+
staging: path17.join(root, "staging"),
|
|
23304
|
+
state: path17.join(root, "state.json"),
|
|
23305
|
+
settings: path17.join(root, "settings.json"),
|
|
23306
|
+
lock: path17.join(root, "state.lock"),
|
|
23307
|
+
updateLock: path17.join(root, "update.lock"),
|
|
23308
|
+
launcher: path17.join(root, "launcher.js")
|
|
23309
|
+
};
|
|
23310
|
+
}
|
|
23311
|
+
var DEFAULT_UPDATE_REGISTRY = "https://registry.npmjs.org/";
|
|
23312
|
+
var VERSION_PATTERN = /^(0|[1-9]\d{0,5})\.(0|[1-9]\d{0,5})\.(0|[1-9]\d{0,5})$/;
|
|
23313
|
+
var versionSchema = string5().regex(VERSION_PATTERN);
|
|
23314
|
+
var sourceSchema = _enum2(COLLECTOR_SOURCES2);
|
|
23315
|
+
var timeSchema = number4().int().min(0);
|
|
23316
|
+
var trialSchema = object3({
|
|
23317
|
+
version: versionSchema,
|
|
23318
|
+
previous: versionSchema,
|
|
23319
|
+
startedAtMs: timeSchema,
|
|
23320
|
+
expected: array2(sourceSchema),
|
|
23321
|
+
starts: partialRecord(sourceSchema, number4().int().min(0)),
|
|
23322
|
+
confirmed: array2(sourceSchema)
|
|
23323
|
+
}).loose();
|
|
23324
|
+
var runtimeStateSchema = object3({
|
|
23325
|
+
version: literal2(1),
|
|
23326
|
+
current: versionSchema,
|
|
23327
|
+
previous: versionSchema.nullable(),
|
|
23328
|
+
trial: trialSchema.nullable(),
|
|
23329
|
+
rejected: array2(
|
|
23330
|
+
object3({
|
|
23331
|
+
version: versionSchema,
|
|
23332
|
+
reason: string5().max(500),
|
|
23333
|
+
atMs: timeSchema
|
|
23334
|
+
}).loose()
|
|
23335
|
+
).max(20),
|
|
23336
|
+
lastCheck: object3({ atMs: timeSchema, error: string5().max(500).nullable() }).loose().nullable(),
|
|
23337
|
+
lastUpdate: object3({ from: versionSchema, to: versionSchema, atMs: timeSchema }).loose().nullable()
|
|
23338
|
+
}).loose();
|
|
23339
|
+
var settingsSchema2 = object3({
|
|
23340
|
+
autoUpdate: boolean4(),
|
|
23341
|
+
registry: string5().url()
|
|
23342
|
+
}).loose();
|
|
23343
|
+
function errorCode2(error3) {
|
|
23344
|
+
if (error3 instanceof Error && "code" in error3) return String(error3.code);
|
|
23345
|
+
return null;
|
|
23346
|
+
}
|
|
23347
|
+
function isVersion(value) {
|
|
23348
|
+
return VERSION_PATTERN.test(value);
|
|
23349
|
+
}
|
|
23350
|
+
function versionParts(value) {
|
|
23351
|
+
const match = VERSION_PATTERN.exec(value);
|
|
23352
|
+
if (match === null) throw new Error(`Invalid Iteration version ${value}.`);
|
|
23353
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
23354
|
+
}
|
|
23355
|
+
function compareVersions(left, right) {
|
|
23356
|
+
const a = versionParts(left);
|
|
23357
|
+
const b = versionParts(right);
|
|
23358
|
+
for (let index = 0; index < 3; index += 1) {
|
|
23359
|
+
const difference = a[index] - b[index];
|
|
23360
|
+
if (difference !== 0) return Math.sign(difference);
|
|
23361
|
+
}
|
|
23362
|
+
return 0;
|
|
23363
|
+
}
|
|
23364
|
+
function nodeRequirement(range, nodeVersion) {
|
|
23365
|
+
const match = /^>=\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?$/.exec(
|
|
23366
|
+
range?.trim() ?? ""
|
|
23367
|
+
);
|
|
23368
|
+
if (match === null) {
|
|
23369
|
+
return {
|
|
23370
|
+
ok: false,
|
|
23371
|
+
reason: `it declares an unsupported Node.js requirement (${range ?? "none"})`
|
|
23372
|
+
};
|
|
23373
|
+
}
|
|
23374
|
+
const required3 = [
|
|
23375
|
+
Number(match[1]),
|
|
23376
|
+
Number(match[2] ?? 0),
|
|
23377
|
+
Number(match[3] ?? 0)
|
|
23378
|
+
];
|
|
23379
|
+
const actual = nodeVersion.split(".").map(Number);
|
|
23380
|
+
for (let index = 0; index < 3; index += 1) {
|
|
23381
|
+
const difference = (actual[index] ?? 0) - required3[index];
|
|
23382
|
+
if (difference > 0) return { ok: true };
|
|
23383
|
+
if (difference < 0) {
|
|
23384
|
+
return {
|
|
23385
|
+
ok: false,
|
|
23386
|
+
reason: `it needs Node.js ${range} and this computer runs ${nodeVersion}`
|
|
23387
|
+
};
|
|
23388
|
+
}
|
|
23389
|
+
}
|
|
23390
|
+
return { ok: true };
|
|
23391
|
+
}
|
|
23392
|
+
async function ensureRuntimeDirectory(paths) {
|
|
23393
|
+
for (const directory of [paths.root, paths.versions, paths.staging]) {
|
|
23394
|
+
await ensurePrivateDirectory(directory);
|
|
23395
|
+
}
|
|
22785
23396
|
}
|
|
22786
23397
|
async function writeFileAtomic(filename, contents, mode) {
|
|
22787
23398
|
const temporary = `${filename}.${process.pid}.${Date.now()}.tmp`;
|
|
22788
|
-
const handle = await
|
|
23399
|
+
const handle = await fs12.open(temporary, "w", mode);
|
|
22789
23400
|
try {
|
|
22790
23401
|
await handle.writeFile(contents, "utf8");
|
|
22791
23402
|
await handle.sync();
|
|
22792
23403
|
} finally {
|
|
22793
23404
|
await handle.close();
|
|
22794
23405
|
}
|
|
22795
|
-
await
|
|
23406
|
+
await fs12.rename(temporary, filename);
|
|
22796
23407
|
}
|
|
22797
23408
|
async function readRuntimeState(paths) {
|
|
22798
23409
|
let contents;
|
|
22799
23410
|
try {
|
|
22800
|
-
contents = await
|
|
23411
|
+
contents = await fs12.readFile(paths.state, "utf8");
|
|
22801
23412
|
} catch (error3) {
|
|
22802
23413
|
if (errorCode2(error3) === "ENOENT") return null;
|
|
22803
23414
|
throw error3;
|
|
@@ -22829,7 +23440,7 @@ async function writeRuntimeState(paths, state) {
|
|
|
22829
23440
|
async function readRuntimeSettings(paths) {
|
|
22830
23441
|
let contents;
|
|
22831
23442
|
try {
|
|
22832
|
-
contents = await
|
|
23443
|
+
contents = await fs12.readFile(paths.settings, "utf8");
|
|
22833
23444
|
} catch (error3) {
|
|
22834
23445
|
if (errorCode2(error3) === "ENOENT")
|
|
22835
23446
|
return { autoUpdate: true, registry: DEFAULT_UPDATE_REGISTRY };
|
|
@@ -22858,19 +23469,19 @@ var LockBusyError = class extends Error {
|
|
|
22858
23469
|
async function tryLock(filename) {
|
|
22859
23470
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
22860
23471
|
try {
|
|
22861
|
-
const handle = await
|
|
23472
|
+
const handle = await fs12.open(filename, "wx", 384);
|
|
22862
23473
|
await handle.writeFile(`${process.pid}
|
|
22863
23474
|
`, "utf8");
|
|
22864
23475
|
await handle.close();
|
|
22865
23476
|
return async () => {
|
|
22866
|
-
const owner = await
|
|
23477
|
+
const owner = await fs12.readFile(filename, "utf8").catch(() => "");
|
|
22867
23478
|
if (owner.trim() === String(process.pid))
|
|
22868
|
-
await
|
|
23479
|
+
await fs12.rm(filename, { force: true });
|
|
22869
23480
|
};
|
|
22870
23481
|
} catch (error3) {
|
|
22871
23482
|
if (errorCode2(error3) !== "EEXIST") throw error3;
|
|
22872
23483
|
const owner = Number(
|
|
22873
|
-
(await
|
|
23484
|
+
(await fs12.readFile(filename, "utf8").catch(() => "")).trim()
|
|
22874
23485
|
);
|
|
22875
23486
|
if (Number.isSafeInteger(owner) && owner > 1) {
|
|
22876
23487
|
try {
|
|
@@ -22880,7 +23491,7 @@ async function tryLock(filename) {
|
|
|
22880
23491
|
if (errorCode2(processError) !== "ESRCH") throw processError;
|
|
22881
23492
|
}
|
|
22882
23493
|
}
|
|
22883
|
-
await
|
|
23494
|
+
await fs12.rm(filename, { force: true });
|
|
22884
23495
|
}
|
|
22885
23496
|
}
|
|
22886
23497
|
throw new LockBusyError(`Could not obtain ${filename}.`);
|
|
@@ -22930,15 +23541,15 @@ function confirmTrialSource(state, version3, source, nowMs) {
|
|
|
22930
23541
|
function versionDirectory(paths, version3) {
|
|
22931
23542
|
if (!isVersion(version3))
|
|
22932
23543
|
throw new Error(`Invalid Iteration version ${version3}.`);
|
|
22933
|
-
return
|
|
23544
|
+
return path17.join(paths.versions, version3);
|
|
22934
23545
|
}
|
|
22935
23546
|
function versionEntry(paths, version3, entry) {
|
|
22936
|
-
return
|
|
23547
|
+
return path17.join(versionDirectory(paths, version3), "dist", entry);
|
|
22937
23548
|
}
|
|
22938
23549
|
var packageSchema = object3({ name: literal2("iteration-cli"), version: versionSchema }).loose();
|
|
22939
23550
|
async function readPackageVersion(root) {
|
|
22940
23551
|
const parsed = packageSchema.safeParse(
|
|
22941
|
-
JSON.parse(await
|
|
23552
|
+
JSON.parse(await fs12.readFile(path17.join(root, "package.json"), "utf8"))
|
|
22942
23553
|
);
|
|
22943
23554
|
if (!parsed.success) {
|
|
22944
23555
|
throw new Error(`${root} does not contain an Iteration CLI package.`, {
|
|
@@ -22948,7 +23559,7 @@ async function readPackageVersion(root) {
|
|
|
22948
23559
|
return parsed.data.version;
|
|
22949
23560
|
}
|
|
22950
23561
|
function ownPackageRoot() {
|
|
22951
|
-
return
|
|
23562
|
+
return path17.dirname(path17.dirname(fileURLToPath(import.meta.url)));
|
|
22952
23563
|
}
|
|
22953
23564
|
async function assertInstallablePackage(root, version3) {
|
|
22954
23565
|
const packageVersion = await readPackageVersion(root);
|
|
@@ -22957,12 +23568,12 @@ async function assertInstallablePackage(root, version3) {
|
|
|
22957
23568
|
`The downloaded package is version ${packageVersion}, not ${version3}.`
|
|
22958
23569
|
);
|
|
22959
23570
|
}
|
|
22960
|
-
const
|
|
22961
|
-
while (
|
|
22962
|
-
const directory =
|
|
22963
|
-
for (const entry of await
|
|
22964
|
-
const filename =
|
|
22965
|
-
if (entry.isDirectory())
|
|
23571
|
+
const pending2 = [root];
|
|
23572
|
+
while (pending2.length > 0) {
|
|
23573
|
+
const directory = pending2.pop();
|
|
23574
|
+
for (const entry of await fs12.readdir(directory, { withFileTypes: true })) {
|
|
23575
|
+
const filename = path17.join(directory, entry.name);
|
|
23576
|
+
if (entry.isDirectory()) pending2.push(filename);
|
|
22966
23577
|
else if (!entry.isFile())
|
|
22967
23578
|
throw new Error(
|
|
22968
23579
|
`The package contains an unexpected entry ${filename}.`
|
|
@@ -22970,7 +23581,7 @@ async function assertInstallablePackage(root, version3) {
|
|
|
22970
23581
|
}
|
|
22971
23582
|
}
|
|
22972
23583
|
for (const entry of ["cli.js", "cursor-hook-entry.js", "launcher.js"]) {
|
|
22973
|
-
const stats = await
|
|
23584
|
+
const stats = await fs12.stat(path17.join(root, "dist", entry));
|
|
22974
23585
|
if (!stats.isFile())
|
|
22975
23586
|
throw new Error(`The package is missing dist/${entry}.`);
|
|
22976
23587
|
}
|
|
@@ -22978,48 +23589,48 @@ async function assertInstallablePackage(root, version3) {
|
|
|
22978
23589
|
async function installVersionDirectory(paths, staged, version3) {
|
|
22979
23590
|
await assertInstallablePackage(staged, version3);
|
|
22980
23591
|
const target = versionDirectory(paths, version3);
|
|
22981
|
-
const displaced =
|
|
23592
|
+
const displaced = path17.join(
|
|
22982
23593
|
paths.staging,
|
|
22983
23594
|
`displaced-${version3}-${process.pid}-${Date.now()}`
|
|
22984
23595
|
);
|
|
22985
23596
|
try {
|
|
22986
|
-
await
|
|
23597
|
+
await fs12.rename(target, displaced);
|
|
22987
23598
|
} catch (error3) {
|
|
22988
23599
|
if (errorCode2(error3) !== "ENOENT") throw error3;
|
|
22989
23600
|
}
|
|
22990
|
-
await
|
|
22991
|
-
await
|
|
23601
|
+
await fs12.rename(staged, target);
|
|
23602
|
+
await fs12.rm(displaced, { recursive: true, force: true });
|
|
22992
23603
|
}
|
|
22993
23604
|
async function copyOwnPackage(paths, root, version3) {
|
|
22994
23605
|
await ensureRuntimeDirectory(paths);
|
|
22995
|
-
const staged = await
|
|
23606
|
+
const staged = await fs12.mkdtemp(path17.join(paths.staging, `copy-${version3}-`));
|
|
22996
23607
|
try {
|
|
22997
|
-
const packageRoot =
|
|
22998
|
-
await
|
|
22999
|
-
await
|
|
23000
|
-
|
|
23001
|
-
|
|
23608
|
+
const packageRoot = path17.join(staged, "package");
|
|
23609
|
+
await fs12.mkdir(packageRoot);
|
|
23610
|
+
await fs12.copyFile(
|
|
23611
|
+
path17.join(root, "package.json"),
|
|
23612
|
+
path17.join(packageRoot, "package.json")
|
|
23002
23613
|
);
|
|
23003
|
-
await
|
|
23614
|
+
await fs12.cp(path17.join(root, "dist"), path17.join(packageRoot, "dist"), {
|
|
23004
23615
|
recursive: true,
|
|
23005
23616
|
dereference: true
|
|
23006
23617
|
});
|
|
23007
23618
|
await installVersionDirectory(paths, packageRoot, version3);
|
|
23008
23619
|
} finally {
|
|
23009
|
-
await
|
|
23620
|
+
await fs12.rm(staged, { recursive: true, force: true });
|
|
23010
23621
|
}
|
|
23011
23622
|
}
|
|
23012
23623
|
async function pruneVersions(paths, keep) {
|
|
23013
23624
|
let entries2;
|
|
23014
23625
|
try {
|
|
23015
|
-
entries2 = await
|
|
23626
|
+
entries2 = await fs12.readdir(paths.versions);
|
|
23016
23627
|
} catch (error3) {
|
|
23017
23628
|
if (errorCode2(error3) === "ENOENT") return;
|
|
23018
23629
|
throw error3;
|
|
23019
23630
|
}
|
|
23020
23631
|
for (const entry of entries2) {
|
|
23021
23632
|
if (keep.includes(entry)) continue;
|
|
23022
|
-
await
|
|
23633
|
+
await fs12.rm(path17.join(paths.versions, entry), {
|
|
23023
23634
|
recursive: true,
|
|
23024
23635
|
force: true
|
|
23025
23636
|
});
|
|
@@ -23028,8 +23639,8 @@ async function pruneVersions(paths, keep) {
|
|
|
23028
23639
|
async function launcherMatches(paths, version3) {
|
|
23029
23640
|
try {
|
|
23030
23641
|
const [installed, packaged] = await Promise.all([
|
|
23031
|
-
|
|
23032
|
-
|
|
23642
|
+
fs12.readFile(paths.launcher),
|
|
23643
|
+
fs12.readFile(versionEntry(paths, version3, "launcher.js"))
|
|
23033
23644
|
]);
|
|
23034
23645
|
return installed.equals(packaged);
|
|
23035
23646
|
} catch (error3) {
|
|
@@ -23038,35 +23649,35 @@ async function launcherMatches(paths, version3) {
|
|
|
23038
23649
|
}
|
|
23039
23650
|
}
|
|
23040
23651
|
async function stageLauncher(paths, version3) {
|
|
23041
|
-
const staged =
|
|
23652
|
+
const staged = path17.join(
|
|
23042
23653
|
paths.staging,
|
|
23043
23654
|
`launcher-${version3}-${process.pid}-${Date.now()}.js`
|
|
23044
23655
|
);
|
|
23045
|
-
await
|
|
23046
|
-
await
|
|
23656
|
+
await fs12.copyFile(versionEntry(paths, version3, "launcher.js"), staged);
|
|
23657
|
+
await fs12.chmod(staged, 420);
|
|
23047
23658
|
return staged;
|
|
23048
23659
|
}
|
|
23049
23660
|
async function commitLauncher(paths, staged) {
|
|
23050
|
-
await
|
|
23661
|
+
await fs12.rename(staged, paths.launcher);
|
|
23051
23662
|
}
|
|
23052
23663
|
function rolloutDelayMs(deviceKey) {
|
|
23053
23664
|
const digest3 = createHash4("sha256").update(deviceKey).digest();
|
|
23054
23665
|
return digest3.readUInt32BE(0) % 360 * 6e4;
|
|
23055
23666
|
}
|
|
23056
23667
|
function deviceRolloutKey() {
|
|
23057
|
-
return `${
|
|
23668
|
+
return `${os3.hostname()}\0${os3.homedir()}`;
|
|
23058
23669
|
}
|
|
23059
23670
|
async function stableNodePath() {
|
|
23060
|
-
const actual = await
|
|
23671
|
+
const actual = await fs12.realpath(process.execPath);
|
|
23061
23672
|
const invoked = process.argv0;
|
|
23062
23673
|
const binary = process.platform === "win32" ? "node.exe" : "node";
|
|
23063
23674
|
const candidates = [
|
|
23064
|
-
...invoked !== void 0 &&
|
|
23065
|
-
...(process.env.PATH ?? "").split(
|
|
23675
|
+
...invoked !== void 0 && path17.isAbsolute(invoked) ? [invoked] : [],
|
|
23676
|
+
...(process.env.PATH ?? "").split(path17.delimiter).filter((directory) => path17.isAbsolute(directory)).map((directory) => path17.join(directory, binary))
|
|
23066
23677
|
];
|
|
23067
23678
|
for (const candidate of candidates) {
|
|
23068
23679
|
try {
|
|
23069
|
-
const resolved = await
|
|
23680
|
+
const resolved = await fs12.realpath(candidate);
|
|
23070
23681
|
const same = process.platform === "win32" ? resolved.toLowerCase() === actual.toLowerCase() : resolved === actual;
|
|
23071
23682
|
if (same) return candidate;
|
|
23072
23683
|
} catch (error3) {
|
|
@@ -23078,7 +23689,258 @@ async function stableNodePath() {
|
|
|
23078
23689
|
return process.execPath;
|
|
23079
23690
|
}
|
|
23080
23691
|
|
|
23692
|
+
// src/agent-tools.ts
|
|
23693
|
+
var SERVER_NAME = "iteration";
|
|
23694
|
+
var RENEW_BEFORE_MS = 5 * 60 * 1e3;
|
|
23695
|
+
var accessSchema = object3({
|
|
23696
|
+
url: url(),
|
|
23697
|
+
token: string5().min(1),
|
|
23698
|
+
expiresAt: iso_exports2.datetime()
|
|
23699
|
+
});
|
|
23700
|
+
var versioned = object3({ protocolVersion: string5().optional() }).loose().optional();
|
|
23701
|
+
var messageSchema = object3({
|
|
23702
|
+
id: union2([string5(), number4()]).optional(),
|
|
23703
|
+
method: string5().optional(),
|
|
23704
|
+
params: versioned,
|
|
23705
|
+
result: versioned
|
|
23706
|
+
}).loose();
|
|
23707
|
+
function write(message) {
|
|
23708
|
+
process.stdout.write(`${JSON.stringify(message)}
|
|
23709
|
+
`);
|
|
23710
|
+
}
|
|
23711
|
+
async function serveAgentTools(config3) {
|
|
23712
|
+
let access = null;
|
|
23713
|
+
let scopedConfig = config3;
|
|
23714
|
+
let version3;
|
|
23715
|
+
let renewing = null;
|
|
23716
|
+
const fetchAccess = async () => {
|
|
23717
|
+
if (scopedConfig === null)
|
|
23718
|
+
throw new Error(
|
|
23719
|
+
"This computer is not connected to Iteration. Run iteration connect."
|
|
23720
|
+
);
|
|
23721
|
+
const response = await collectorFetch(
|
|
23722
|
+
new URL(
|
|
23723
|
+
"./tool-access",
|
|
23724
|
+
validateCollectorEndpoint(scopedConfig.endpoint)
|
|
23725
|
+
),
|
|
23726
|
+
{
|
|
23727
|
+
method: "POST",
|
|
23728
|
+
headers: { Authorization: `Bearer ${scopedConfig.token}` },
|
|
23729
|
+
signal: AbortSignal.timeout(15e3)
|
|
23730
|
+
}
|
|
23731
|
+
);
|
|
23732
|
+
const payload = await response.json().catch(() => null);
|
|
23733
|
+
if (!response.ok) {
|
|
23734
|
+
const error3 = object3({ error: string5() }).safeParse(payload);
|
|
23735
|
+
throw new Error(
|
|
23736
|
+
`Iteration's tools are unavailable: ${error3.success ? error3.data.error : `HTTP ${response.status}`}`
|
|
23737
|
+
);
|
|
23738
|
+
}
|
|
23739
|
+
const parsed = accessSchema.parse(payload);
|
|
23740
|
+
access = { ...parsed, expiresAt: Date.parse(parsed.expiresAt) };
|
|
23741
|
+
return access;
|
|
23742
|
+
};
|
|
23743
|
+
const renew = (force) => {
|
|
23744
|
+
if (!force && access !== null && access.expiresAt - Date.now() > RENEW_BEFORE_MS)
|
|
23745
|
+
return Promise.resolve(access);
|
|
23746
|
+
renewing ??= fetchAccess().finally(() => {
|
|
23747
|
+
renewing = null;
|
|
23748
|
+
});
|
|
23749
|
+
return renewing;
|
|
23750
|
+
};
|
|
23751
|
+
const post = async (body, method, force) => {
|
|
23752
|
+
if (config3?.destination === "host") {
|
|
23753
|
+
const selected = await configForSharedDirectory(config3, process.cwd());
|
|
23754
|
+
if (selected === null)
|
|
23755
|
+
throw new Error(
|
|
23756
|
+
"This project is not shared with Iteration. Choose it in project sharing first."
|
|
23757
|
+
);
|
|
23758
|
+
if (selected.organizationId !== scopedConfig?.organizationId || selected.token !== scopedConfig?.token)
|
|
23759
|
+
access = null;
|
|
23760
|
+
scopedConfig = selected;
|
|
23761
|
+
}
|
|
23762
|
+
const current = await renew(force);
|
|
23763
|
+
return fetch(current.url, {
|
|
23764
|
+
method: "POST",
|
|
23765
|
+
headers: {
|
|
23766
|
+
Authorization: `Bearer ${current.token}`,
|
|
23767
|
+
"Content-Type": "application/json",
|
|
23768
|
+
Accept: "application/json, text/event-stream",
|
|
23769
|
+
...version3 !== void 0 && method !== "initialize" ? { "MCP-Protocol-Version": version3 } : {},
|
|
23770
|
+
...method === void 0 ? {} : { "MCP-Method": method }
|
|
23771
|
+
},
|
|
23772
|
+
body,
|
|
23773
|
+
signal: AbortSignal.timeout(31e4)
|
|
23774
|
+
});
|
|
23775
|
+
};
|
|
23776
|
+
const relay = (text) => {
|
|
23777
|
+
const message = messageSchema.safeParse(JSON.parse(text));
|
|
23778
|
+
const negotiated = message.success ? message.data.result?.protocolVersion : void 0;
|
|
23779
|
+
if (negotiated !== void 0) version3 = negotiated;
|
|
23780
|
+
write(JSON.parse(text));
|
|
23781
|
+
};
|
|
23782
|
+
const forward = async (line) => {
|
|
23783
|
+
const message = messageSchema.parse(JSON.parse(line));
|
|
23784
|
+
if (message.method === "initialize")
|
|
23785
|
+
version3 = message.params?.protocolVersion ?? version3;
|
|
23786
|
+
try {
|
|
23787
|
+
let response = await post(line, message.method, false);
|
|
23788
|
+
if (response.status === 401)
|
|
23789
|
+
response = await post(line, message.method, true);
|
|
23790
|
+
if (response.status === 202 || message.id === void 0) return;
|
|
23791
|
+
const type = response.headers.get("content-type") ?? "";
|
|
23792
|
+
if (type.includes("text/event-stream")) {
|
|
23793
|
+
const text2 = await response.text();
|
|
23794
|
+
for (const event3 of text2.split(/\r?\n\r?\n/)) {
|
|
23795
|
+
const data = event3.split(/\r?\n/).filter((row) => row.startsWith("data:")).map((row) => row.slice(5).trimStart()).join("\n");
|
|
23796
|
+
if (data.trim()) relay(data);
|
|
23797
|
+
}
|
|
23798
|
+
return;
|
|
23799
|
+
}
|
|
23800
|
+
const text = await response.text();
|
|
23801
|
+
if (text.trim()) relay(text);
|
|
23802
|
+
else if (!response.ok)
|
|
23803
|
+
throw new Error(`Iteration's tools answered HTTP ${response.status}.`);
|
|
23804
|
+
} catch (error3) {
|
|
23805
|
+
if (message.id === void 0) return;
|
|
23806
|
+
write({
|
|
23807
|
+
jsonrpc: "2.0",
|
|
23808
|
+
id: message.id,
|
|
23809
|
+
error: {
|
|
23810
|
+
code: -32603,
|
|
23811
|
+
message: error3 instanceof Error ? error3.message : "Iteration's tools are unavailable."
|
|
23812
|
+
}
|
|
23813
|
+
});
|
|
23814
|
+
}
|
|
23815
|
+
};
|
|
23816
|
+
const pending2 = /* @__PURE__ */ new Set();
|
|
23817
|
+
for await (const line of readline.createInterface({
|
|
23818
|
+
input: process.stdin
|
|
23819
|
+
})) {
|
|
23820
|
+
if (!line.trim()) continue;
|
|
23821
|
+
const task = forward(line).catch(() => void 0);
|
|
23822
|
+
pending2.add(task);
|
|
23823
|
+
void task.finally(() => pending2.delete(task));
|
|
23824
|
+
}
|
|
23825
|
+
await Promise.allSettled(pending2);
|
|
23826
|
+
}
|
|
23827
|
+
async function writeAtomically(filename, content) {
|
|
23828
|
+
await fs13.mkdir(path18.dirname(filename), { recursive: true });
|
|
23829
|
+
const mode = await fs13.stat(filename).then((stat) => stat.mode & 511).catch(() => 384);
|
|
23830
|
+
const temporary = `${filename}.${process.pid}.${Date.now()}.tmp`;
|
|
23831
|
+
await fs13.writeFile(temporary, content, { mode });
|
|
23832
|
+
await fs13.rename(temporary, filename);
|
|
23833
|
+
}
|
|
23834
|
+
async function readText2(filename) {
|
|
23835
|
+
try {
|
|
23836
|
+
return await fs13.readFile(filename, "utf8");
|
|
23837
|
+
} catch (error3) {
|
|
23838
|
+
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
23839
|
+
return null;
|
|
23840
|
+
throw error3;
|
|
23841
|
+
}
|
|
23842
|
+
}
|
|
23843
|
+
var jsonServersSchema = object3({
|
|
23844
|
+
mcpServers: record3(string5(), unknown2()).optional()
|
|
23845
|
+
}).loose();
|
|
23846
|
+
async function updateJsonServers(filename, server) {
|
|
23847
|
+
const text = await readText2(filename);
|
|
23848
|
+
if (text === null && server === null) return;
|
|
23849
|
+
let parsed;
|
|
23850
|
+
try {
|
|
23851
|
+
parsed = jsonServersSchema.parse(text === null ? {} : JSON.parse(text));
|
|
23852
|
+
} catch (error3) {
|
|
23853
|
+
throw new Error(`Could not read MCP settings at ${filename}.`, {
|
|
23854
|
+
cause: error3
|
|
23855
|
+
});
|
|
23856
|
+
}
|
|
23857
|
+
const { [SERVER_NAME]: existing, ...others } = parsed.mcpServers ?? {};
|
|
23858
|
+
if (server === null && existing === void 0) return;
|
|
23859
|
+
if (server !== null && JSON.stringify(existing) === JSON.stringify(server))
|
|
23860
|
+
return;
|
|
23861
|
+
const mcpServers = server === null ? others : { ...others, [SERVER_NAME]: server };
|
|
23862
|
+
await writeAtomically(
|
|
23863
|
+
filename,
|
|
23864
|
+
`${JSON.stringify({ ...parsed, mcpServers }, null, 2)}
|
|
23865
|
+
`
|
|
23866
|
+
);
|
|
23867
|
+
}
|
|
23868
|
+
var CODEX_TABLE = `[mcp_servers.${SERVER_NAME}]`;
|
|
23869
|
+
function withoutCodexServer(text) {
|
|
23870
|
+
const lines = text.split(/\r?\n/);
|
|
23871
|
+
const kept = [];
|
|
23872
|
+
let skipping = false;
|
|
23873
|
+
for (const line of lines) {
|
|
23874
|
+
const table = /^\s*\[([^\]]+)\]\s*(?:#.*)?$/.exec(line)?.[1]?.trim();
|
|
23875
|
+
if (table !== void 0)
|
|
23876
|
+
skipping = table === `mcp_servers.${SERVER_NAME}` || table.startsWith(`mcp_servers.${SERVER_NAME}.`);
|
|
23877
|
+
if (!skipping) kept.push(line);
|
|
23878
|
+
}
|
|
23879
|
+
return kept.join("\n").replace(/\n{3,}$/, "\n\n");
|
|
23880
|
+
}
|
|
23881
|
+
async function updateCodexServer(filename, command) {
|
|
23882
|
+
const text = await readText2(filename);
|
|
23883
|
+
if (text === null && command === null) return;
|
|
23884
|
+
const base = withoutCodexServer(text ?? "");
|
|
23885
|
+
const [program, ...args] = command ?? [];
|
|
23886
|
+
const next = command === null ? base : `${base.trimEnd()}${base.trim() ? "\n\n" : ""}${CODEX_TABLE}
|
|
23887
|
+
command = ${JSON.stringify(program)}
|
|
23888
|
+
args = ${JSON.stringify(args)}
|
|
23889
|
+
`;
|
|
23890
|
+
if (next === text) return;
|
|
23891
|
+
await writeAtomically(filename, next);
|
|
23892
|
+
}
|
|
23893
|
+
function claudeSettingsFile(claudeHome) {
|
|
23894
|
+
return path18.resolve(claudeHome) === path18.join(os4.homedir(), ".claude") ? path18.join(os4.homedir(), ".claude.json") : path18.join(claudeHome, ".claude.json");
|
|
23895
|
+
}
|
|
23896
|
+
var cursorSettingsFile = () => path18.join(os4.homedir(), ".cursor", "mcp.json");
|
|
23897
|
+
async function installAgentTools(homes) {
|
|
23898
|
+
const command = [
|
|
23899
|
+
await stableNodePath(),
|
|
23900
|
+
runtimePaths().launcher,
|
|
23901
|
+
"mcp"
|
|
23902
|
+
];
|
|
23903
|
+
const [program, ...args] = command;
|
|
23904
|
+
if (homes.claude !== void 0)
|
|
23905
|
+
await updateJsonServers(claudeSettingsFile(homes.claude), {
|
|
23906
|
+
type: "stdio",
|
|
23907
|
+
command: program,
|
|
23908
|
+
args
|
|
23909
|
+
});
|
|
23910
|
+
if (homes.codex !== void 0)
|
|
23911
|
+
await updateCodexServer(path18.join(homes.codex, "config.toml"), command);
|
|
23912
|
+
if (homes.cursor)
|
|
23913
|
+
await updateJsonServers(cursorSettingsFile(), { command: program, args });
|
|
23914
|
+
}
|
|
23915
|
+
async function uninstallAgentTools(homes) {
|
|
23916
|
+
if (homes.claude !== void 0)
|
|
23917
|
+
await updateJsonServers(claudeSettingsFile(homes.claude), null);
|
|
23918
|
+
if (homes.codex !== void 0)
|
|
23919
|
+
await updateCodexServer(path18.join(homes.codex, "config.toml"), null);
|
|
23920
|
+
if (homes.cursor) await updateJsonServers(cursorSettingsFile(), null);
|
|
23921
|
+
}
|
|
23922
|
+
function agentHomes(config3) {
|
|
23923
|
+
return "cursorHome" in config3 ? { cursor: true } : "claudeHome" in config3 ? { claude: config3.claudeHome } : { codex: config3.codexHome };
|
|
23924
|
+
}
|
|
23925
|
+
|
|
23926
|
+
// src/auto-connect.ts
|
|
23927
|
+
import { createHmac } from "node:crypto";
|
|
23928
|
+
import { promises as fs22 } from "node:fs";
|
|
23929
|
+
import os10 from "node:os";
|
|
23930
|
+
import path27 from "node:path";
|
|
23931
|
+
|
|
23932
|
+
// src/background-service.ts
|
|
23933
|
+
import { promises as fs19 } from "node:fs";
|
|
23934
|
+
import os8 from "node:os";
|
|
23935
|
+
import path23 from "node:path";
|
|
23936
|
+
|
|
23081
23937
|
// src/launch-agent.ts
|
|
23938
|
+
import { execFile as execFileCallback2 } from "node:child_process";
|
|
23939
|
+
import { promises as fs14 } from "node:fs";
|
|
23940
|
+
import os5 from "node:os";
|
|
23941
|
+
import path19 from "node:path";
|
|
23942
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
23943
|
+
import { promisify as promisify2 } from "node:util";
|
|
23082
23944
|
var execFile2 = promisify2(execFileCallback2);
|
|
23083
23945
|
function label(source) {
|
|
23084
23946
|
return `com.iteration.${source}-collector`;
|
|
@@ -23089,16 +23951,16 @@ var RESTART_POLL_MS = 200;
|
|
|
23089
23951
|
function xml(value) {
|
|
23090
23952
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23091
23953
|
}
|
|
23092
|
-
function plistPath(source, homeDirectory =
|
|
23093
|
-
return
|
|
23954
|
+
function plistPath(source, homeDirectory = os5.homedir()) {
|
|
23955
|
+
return path19.join(
|
|
23094
23956
|
homeDirectory,
|
|
23095
23957
|
"Library",
|
|
23096
23958
|
"LaunchAgents",
|
|
23097
23959
|
`${label(source)}.plist`
|
|
23098
23960
|
);
|
|
23099
23961
|
}
|
|
23100
|
-
function launchAgentPlist(launcherPath = runtimePaths().launcher, nodePath = process.execPath, logDirectory =
|
|
23101
|
-
|
|
23962
|
+
function launchAgentPlist(launcherPath = runtimePaths().launcher, nodePath = process.execPath, logDirectory = path19.join(os5.homedir(), "Library", "Logs"), dataDirectory2 = path19.join(
|
|
23963
|
+
os5.homedir(),
|
|
23102
23964
|
".config",
|
|
23103
23965
|
"iteration",
|
|
23104
23966
|
"codex-collector"
|
|
@@ -23121,8 +23983,8 @@ function launchAgentPlist(launcherPath = runtimePaths().launcher, nodePath = pro
|
|
|
23121
23983
|
"<key>RunAtLoad</key><true/>",
|
|
23122
23984
|
"<key>KeepAlive</key><true/>",
|
|
23123
23985
|
"<key>ThrottleInterval</key><integer>10</integer>",
|
|
23124
|
-
`<key>StandardOutPath</key><string>${xml(
|
|
23125
|
-
`<key>StandardErrorPath</key><string>${xml(
|
|
23986
|
+
`<key>StandardOutPath</key><string>${xml(path19.join(logDirectory, `iteration-${source}-collector.log`))}</string>`,
|
|
23987
|
+
`<key>StandardErrorPath</key><string>${xml(path19.join(logDirectory, `iteration-${source}-collector.error.log`))}</string>`,
|
|
23126
23988
|
"</dict></plist>"
|
|
23127
23989
|
].join("");
|
|
23128
23990
|
}
|
|
@@ -23202,17 +24064,17 @@ async function installLaunchAgent(dataDirectory2, source = "codex") {
|
|
|
23202
24064
|
throw new Error("Automatic startup is currently supported on macOS only.");
|
|
23203
24065
|
}
|
|
23204
24066
|
const file = plistPath(source);
|
|
23205
|
-
const launchAgents =
|
|
23206
|
-
const logs =
|
|
23207
|
-
await
|
|
23208
|
-
await
|
|
23209
|
-
await
|
|
24067
|
+
const launchAgents = path19.dirname(file);
|
|
24068
|
+
const logs = path19.join(os5.homedir(), "Library", "Logs");
|
|
24069
|
+
await fs14.mkdir(launchAgents, { recursive: true, mode: 493 });
|
|
24070
|
+
await fs14.mkdir(logs, { recursive: true, mode: 493 });
|
|
24071
|
+
await fs14.writeFile(
|
|
23210
24072
|
file,
|
|
23211
24073
|
launchAgentPlist(
|
|
23212
24074
|
void 0,
|
|
23213
24075
|
await stableNodePath(),
|
|
23214
24076
|
void 0,
|
|
23215
|
-
dataDirectory2 ??
|
|
24077
|
+
dataDirectory2 ?? path19.join(os5.homedir(), ".config", "iteration", `${source}-collector`),
|
|
23216
24078
|
source
|
|
23217
24079
|
),
|
|
23218
24080
|
{ encoding: "utf8", mode: 420 }
|
|
@@ -23244,7 +24106,7 @@ async function uninstallLaunchAgent(source = "codex") {
|
|
|
23244
24106
|
}
|
|
23245
24107
|
const file = plistPath(source);
|
|
23246
24108
|
await bootoutIfLoaded(launchctlTarget(), source);
|
|
23247
|
-
await
|
|
24109
|
+
await fs14.rm(file, { force: true });
|
|
23248
24110
|
}
|
|
23249
24111
|
async function launchAgentStatus(source = "codex") {
|
|
23250
24112
|
if (process.platform !== "darwin") return false;
|
|
@@ -23265,21 +24127,21 @@ function launchAgentRunning(output) {
|
|
|
23265
24127
|
|
|
23266
24128
|
// src/runtime-install.ts
|
|
23267
24129
|
import { execFile as execFileCallback4 } from "node:child_process";
|
|
23268
|
-
import { promises as
|
|
24130
|
+
import { promises as fs16 } from "node:fs";
|
|
23269
24131
|
import { promisify as promisify4 } from "node:util";
|
|
23270
24132
|
|
|
23271
24133
|
// src/updater.ts
|
|
23272
24134
|
import { execFile as execFileCallback3 } from "node:child_process";
|
|
23273
24135
|
import { createHash as createHash5 } from "node:crypto";
|
|
23274
|
-
import { createWriteStream as createWriteStream2, promises as
|
|
24136
|
+
import { createWriteStream as createWriteStream2, promises as fs15 } from "node:fs";
|
|
23275
24137
|
import http from "node:http";
|
|
23276
24138
|
import https2 from "node:https";
|
|
23277
|
-
import
|
|
24139
|
+
import path20 from "node:path";
|
|
23278
24140
|
import { Transform } from "node:stream";
|
|
23279
24141
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
23280
24142
|
import { promisify as promisify3 } from "node:util";
|
|
23281
24143
|
var execFile3 = promisify3(execFileCallback3);
|
|
23282
|
-
var TAR = process.platform === "win32" ?
|
|
24144
|
+
var TAR = process.platform === "win32" ? path20.join(process.env.SystemRoot ?? "C:\\Windows", "System32", "tar.exe") : "tar";
|
|
23283
24145
|
var PACKAGE_NAME = "iteration-cli";
|
|
23284
24146
|
var MAX_METADATA_BYTES = 16 * 1024 * 1024;
|
|
23285
24147
|
var MAX_TARBALL_BYTES = 64 * 1024 * 1024;
|
|
@@ -23483,32 +24345,32 @@ async function selfCheck(paths, cliPath, version3) {
|
|
|
23483
24345
|
}
|
|
23484
24346
|
async function installCandidate(paths, candidate, signal) {
|
|
23485
24347
|
await ensureRuntimeDirectory(paths);
|
|
23486
|
-
const staged = await
|
|
23487
|
-
|
|
24348
|
+
const staged = await fs15.mkdtemp(
|
|
24349
|
+
path20.join(paths.staging, `download-${candidate.version}-`)
|
|
23488
24350
|
);
|
|
23489
24351
|
try {
|
|
23490
|
-
const archive =
|
|
24352
|
+
const archive = path20.join(staged, "package.tgz");
|
|
23491
24353
|
await downloadVerified(
|
|
23492
24354
|
candidate.tarball,
|
|
23493
24355
|
candidate.integrity,
|
|
23494
24356
|
archive,
|
|
23495
24357
|
signal
|
|
23496
24358
|
);
|
|
23497
|
-
const extracted =
|
|
23498
|
-
await
|
|
24359
|
+
const extracted = path20.join(staged, "extracted");
|
|
24360
|
+
await fs15.mkdir(extracted, { mode: 448 });
|
|
23499
24361
|
await execFile3(TAR, ["-xzf", archive, "-C", extracted], {
|
|
23500
24362
|
timeout: REQUEST_TIMEOUT_MS,
|
|
23501
24363
|
windowsHide: true
|
|
23502
24364
|
});
|
|
23503
|
-
const packageRoot =
|
|
24365
|
+
const packageRoot = path20.join(extracted, "package");
|
|
23504
24366
|
await selfCheck(
|
|
23505
24367
|
paths,
|
|
23506
|
-
|
|
24368
|
+
path20.join(packageRoot, "dist", "cli.js"),
|
|
23507
24369
|
candidate.version
|
|
23508
24370
|
);
|
|
23509
24371
|
await installVersionDirectory(paths, packageRoot, candidate.version);
|
|
23510
24372
|
} finally {
|
|
23511
|
-
await
|
|
24373
|
+
await fs15.rm(staged, { recursive: true, force: true });
|
|
23512
24374
|
}
|
|
23513
24375
|
}
|
|
23514
24376
|
async function switchVersion(paths, from, version3, previous, expected, nowMs) {
|
|
@@ -23616,7 +24478,7 @@ async function ownVersion() {
|
|
|
23616
24478
|
}
|
|
23617
24479
|
async function exists(filename) {
|
|
23618
24480
|
try {
|
|
23619
|
-
await
|
|
24481
|
+
await fs16.access(filename);
|
|
23620
24482
|
return true;
|
|
23621
24483
|
} catch (error3) {
|
|
23622
24484
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
@@ -23643,7 +24505,7 @@ async function installLauncher(paths, version3) {
|
|
|
23643
24505
|
);
|
|
23644
24506
|
await commitLauncher(paths, staged);
|
|
23645
24507
|
} finally {
|
|
23646
|
-
await
|
|
24508
|
+
await fs16.rm(staged, { force: true });
|
|
23647
24509
|
}
|
|
23648
24510
|
}
|
|
23649
24511
|
async function ensureRuntimeInstalled(expected, paths = runtimePaths()) {
|
|
@@ -23725,9 +24587,9 @@ async function expireRuntimeTrial(nowMs, paths = runtimePaths()) {
|
|
|
23725
24587
|
|
|
23726
24588
|
// src/systemd-user.ts
|
|
23727
24589
|
import { execFile as execFileCallback5 } from "node:child_process";
|
|
23728
|
-
import { promises as
|
|
23729
|
-
import
|
|
23730
|
-
import
|
|
24590
|
+
import { promises as fs17 } from "node:fs";
|
|
24591
|
+
import os6 from "node:os";
|
|
24592
|
+
import path21 from "node:path";
|
|
23731
24593
|
import { promisify as promisify5 } from "node:util";
|
|
23732
24594
|
var execFile5 = promisify5(execFileCallback5);
|
|
23733
24595
|
var SYSTEMCTL_TIMEOUT_MS = 1e4;
|
|
@@ -23797,8 +24659,8 @@ function unitName(source) {
|
|
|
23797
24659
|
return `iteration-${source}-collector.service`;
|
|
23798
24660
|
}
|
|
23799
24661
|
function unitPath(source) {
|
|
23800
|
-
return
|
|
23801
|
-
|
|
24662
|
+
return path21.join(
|
|
24663
|
+
os6.homedir(),
|
|
23802
24664
|
".config",
|
|
23803
24665
|
"systemd",
|
|
23804
24666
|
"user",
|
|
@@ -23806,8 +24668,8 @@ function unitPath(source) {
|
|
|
23806
24668
|
);
|
|
23807
24669
|
}
|
|
23808
24670
|
function systemdLogPath(source) {
|
|
23809
|
-
return
|
|
23810
|
-
|
|
24671
|
+
return path21.join(
|
|
24672
|
+
os6.homedir(),
|
|
23811
24673
|
".config",
|
|
23812
24674
|
"iteration",
|
|
23813
24675
|
"logs",
|
|
@@ -23819,13 +24681,13 @@ function unitValue(value) {
|
|
|
23819
24681
|
throw new Error("Collector paths cannot contain line breaks.");
|
|
23820
24682
|
return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("%", "%%")}"`;
|
|
23821
24683
|
}
|
|
23822
|
-
function systemdUserUnit(launcherPath = runtimePaths().launcher, nodePath = process.execPath, dataDirectory2 =
|
|
23823
|
-
|
|
24684
|
+
function systemdUserUnit(launcherPath = runtimePaths().launcher, nodePath = process.execPath, dataDirectory2 = path21.join(
|
|
24685
|
+
os6.homedir(),
|
|
23824
24686
|
".config",
|
|
23825
24687
|
"iteration",
|
|
23826
24688
|
"codex-collector"
|
|
23827
|
-
), source = "codex", outputPath =
|
|
23828
|
-
|
|
24689
|
+
), source = "codex", outputPath = path21.join(
|
|
24690
|
+
os6.homedir(),
|
|
23829
24691
|
".config",
|
|
23830
24692
|
"iteration",
|
|
23831
24693
|
"logs",
|
|
@@ -23855,10 +24717,10 @@ async function systemctl(...args) {
|
|
|
23855
24717
|
async function installSystemdUserService(dataDirectory2, source) {
|
|
23856
24718
|
await ensureSystemdUserLinger();
|
|
23857
24719
|
const file = unitPath(source);
|
|
23858
|
-
const logDirectory =
|
|
23859
|
-
await
|
|
23860
|
-
await
|
|
23861
|
-
await
|
|
24720
|
+
const logDirectory = path21.dirname(systemdLogPath(source));
|
|
24721
|
+
await fs17.mkdir(path21.dirname(file), { recursive: true, mode: 448 });
|
|
24722
|
+
await fs17.mkdir(logDirectory, { recursive: true, mode: 448 });
|
|
24723
|
+
await fs17.writeFile(
|
|
23862
24724
|
file,
|
|
23863
24725
|
systemdUserUnit(void 0, await stableNodePath(), dataDirectory2, source),
|
|
23864
24726
|
{
|
|
@@ -23874,14 +24736,14 @@ async function installSystemdUserService(dataDirectory2, source) {
|
|
|
23874
24736
|
async function uninstallSystemdUserService(source) {
|
|
23875
24737
|
const file = unitPath(source);
|
|
23876
24738
|
try {
|
|
23877
|
-
await
|
|
24739
|
+
await fs17.access(file);
|
|
23878
24740
|
} catch (error3) {
|
|
23879
24741
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
23880
24742
|
return;
|
|
23881
24743
|
throw error3;
|
|
23882
24744
|
}
|
|
23883
24745
|
await systemctl("disable", "--now", unitName(source));
|
|
23884
|
-
await
|
|
24746
|
+
await fs17.rm(file);
|
|
23885
24747
|
await systemctl("daemon-reload");
|
|
23886
24748
|
}
|
|
23887
24749
|
async function systemdUserServiceStatus(source) {
|
|
@@ -23900,9 +24762,9 @@ async function systemdUserServiceStatus(source) {
|
|
|
23900
24762
|
|
|
23901
24763
|
// src/task-scheduler.ts
|
|
23902
24764
|
import { execFile as execFileCallback6 } from "node:child_process";
|
|
23903
|
-
import { promises as
|
|
23904
|
-
import
|
|
23905
|
-
import
|
|
24765
|
+
import { promises as fs18 } from "node:fs";
|
|
24766
|
+
import os7 from "node:os";
|
|
24767
|
+
import path22 from "node:path";
|
|
23906
24768
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
23907
24769
|
import { promisify as promisify6 } from "node:util";
|
|
23908
24770
|
|
|
@@ -24004,14 +24866,14 @@ function scheduledTaskName(source, sid) {
|
|
|
24004
24866
|
return `iteration-${sid}-${source}-collector`;
|
|
24005
24867
|
}
|
|
24006
24868
|
function servicesDirectory() {
|
|
24007
|
-
return
|
|
24869
|
+
return path22.join(os7.homedir(), ".config", "iteration", "services");
|
|
24008
24870
|
}
|
|
24009
24871
|
function taskDefinitionPath(source) {
|
|
24010
|
-
return
|
|
24872
|
+
return path22.join(servicesDirectory(), `iteration-${source}-collector.xml`);
|
|
24011
24873
|
}
|
|
24012
24874
|
function windowsLogPath(source) {
|
|
24013
|
-
return
|
|
24014
|
-
|
|
24875
|
+
return path22.join(
|
|
24876
|
+
os7.homedir(),
|
|
24015
24877
|
".config",
|
|
24016
24878
|
"iteration",
|
|
24017
24879
|
"logs",
|
|
@@ -24019,8 +24881,8 @@ function windowsLogPath(source) {
|
|
|
24019
24881
|
);
|
|
24020
24882
|
}
|
|
24021
24883
|
function windowsOutputPath(source) {
|
|
24022
|
-
return
|
|
24023
|
-
|
|
24884
|
+
return path22.join(
|
|
24885
|
+
os7.homedir(),
|
|
24024
24886
|
".config",
|
|
24025
24887
|
"iteration",
|
|
24026
24888
|
"logs",
|
|
@@ -24035,7 +24897,7 @@ function scheduledTaskScript(task) {
|
|
|
24035
24897
|
"--data-directory",
|
|
24036
24898
|
task.dataDirectory
|
|
24037
24899
|
].map(windowsArgument).join(" ");
|
|
24038
|
-
const errorLog =
|
|
24900
|
+
const errorLog = path22.join(
|
|
24039
24901
|
task.home,
|
|
24040
24902
|
".config",
|
|
24041
24903
|
"iteration",
|
|
@@ -24155,7 +25017,7 @@ function AccountSid([string]$account) {
|
|
|
24155
25017
|
async function schtasks(source, operation, ...args) {
|
|
24156
25018
|
try {
|
|
24157
25019
|
await execFile6(
|
|
24158
|
-
|
|
25020
|
+
path22.join(
|
|
24159
25021
|
process.env.SystemRoot ?? "C:\\Windows",
|
|
24160
25022
|
"System32",
|
|
24161
25023
|
"schtasks.exe"
|
|
@@ -24199,7 +25061,7 @@ async function desiredTask(dataDirectory2, source) {
|
|
|
24199
25061
|
userSid: await currentUserSid(),
|
|
24200
25062
|
nodePath: await stableNodePath(),
|
|
24201
25063
|
launcher: runtimePaths().launcher,
|
|
24202
|
-
home:
|
|
25064
|
+
home: os7.homedir()
|
|
24203
25065
|
};
|
|
24204
25066
|
}
|
|
24205
25067
|
async function scheduledTaskUsesRuntime(dataDirectory2, source) {
|
|
@@ -24211,10 +25073,10 @@ async function scheduledTaskUsesRuntime(dataDirectory2, source) {
|
|
|
24211
25073
|
async function installScheduledTask(dataDirectory2, source) {
|
|
24212
25074
|
const file = taskDefinitionPath(source);
|
|
24213
25075
|
await ensurePrivateDirectory(servicesDirectory());
|
|
24214
|
-
await ensurePrivateDirectory(
|
|
25076
|
+
await ensurePrivateDirectory(path22.dirname(windowsLogPath(source)));
|
|
24215
25077
|
await stopTask(source);
|
|
24216
25078
|
const xml2 = scheduledTaskXml(await desiredTask(dataDirectory2, source));
|
|
24217
|
-
await
|
|
25079
|
+
await fs18.writeFile(
|
|
24218
25080
|
file,
|
|
24219
25081
|
Buffer.concat([Buffer.from([255, 254]), Buffer.from(xml2, "utf16le")])
|
|
24220
25082
|
);
|
|
@@ -24224,7 +25086,7 @@ async function installScheduledTask(dataDirectory2, source) {
|
|
|
24224
25086
|
}
|
|
24225
25087
|
async function uninstallScheduledTask(source) {
|
|
24226
25088
|
if (await stopTask(source)) await schtasks(source, "/Delete", "/F");
|
|
24227
|
-
await
|
|
25089
|
+
await fs18.rm(taskDefinitionPath(source), { force: true });
|
|
24228
25090
|
}
|
|
24229
25091
|
async function scheduledTaskStatus(source) {
|
|
24230
25092
|
return (await observedTask(source))?.state === 4;
|
|
@@ -24248,8 +25110,8 @@ async function prepareBackgroundPersistence() {
|
|
|
24248
25110
|
function collectorLogPath(source) {
|
|
24249
25111
|
if (process.platform === "linux") return systemdLogPath(source);
|
|
24250
25112
|
if (process.platform === "win32") return windowsLogPath(source);
|
|
24251
|
-
return
|
|
24252
|
-
|
|
25113
|
+
return path23.join(
|
|
25114
|
+
os8.homedir(),
|
|
24253
25115
|
"Library",
|
|
24254
25116
|
"Logs",
|
|
24255
25117
|
`iteration-${source}-collector.error.log`
|
|
@@ -24283,7 +25145,7 @@ async function backgroundServiceDefinition(source) {
|
|
|
24283
25145
|
requireCollectorPlatform();
|
|
24284
25146
|
const file = process.platform === "darwin" ? plistPath(source) : process.platform === "linux" ? unitPath(source) : taskDefinitionPath(source);
|
|
24285
25147
|
try {
|
|
24286
|
-
return await
|
|
25148
|
+
return await fs19.readFile(
|
|
24287
25149
|
file,
|
|
24288
25150
|
process.platform === "win32" ? "utf16le" : "utf8"
|
|
24289
25151
|
);
|
|
@@ -24332,7 +25194,7 @@ function serviceDataDirectory(definition, platform) {
|
|
|
24332
25194
|
(_, slashes) => slashes.slice(0, slashes.length / 2)
|
|
24333
25195
|
);
|
|
24334
25196
|
}
|
|
24335
|
-
if (!directory || !(platform === "win32" ?
|
|
25197
|
+
if (!directory || !(platform === "win32" ? path23.win32 : path23.posix).isAbsolute(directory))
|
|
24336
25198
|
throw new Error("Could not read the collector service data directory.");
|
|
24337
25199
|
return directory;
|
|
24338
25200
|
}
|
|
@@ -24355,15 +25217,15 @@ async function backgroundServiceUsesRuntime(directory, source) {
|
|
|
24355
25217
|
}
|
|
24356
25218
|
|
|
24357
25219
|
// src/config-paths.ts
|
|
24358
|
-
import
|
|
24359
|
-
import
|
|
25220
|
+
import os9 from "node:os";
|
|
25221
|
+
import path24 from "node:path";
|
|
24360
25222
|
function defaultCollectorDirectory(source) {
|
|
24361
|
-
return
|
|
25223
|
+
return path24.join(os9.homedir(), ".config", "iteration", `${source}-collector`);
|
|
24362
25224
|
}
|
|
24363
25225
|
|
|
24364
25226
|
// src/cursor-hooks.ts
|
|
24365
|
-
import { promises as
|
|
24366
|
-
import
|
|
25227
|
+
import { promises as fs20 } from "node:fs";
|
|
25228
|
+
import path25 from "node:path";
|
|
24367
25229
|
function shellQuote(value) {
|
|
24368
25230
|
if (process.platform === "win32") return `"${value}"`;
|
|
24369
25231
|
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
@@ -24398,8 +25260,8 @@ async function readHooks(filename) {
|
|
|
24398
25260
|
let contents;
|
|
24399
25261
|
let mode = 384;
|
|
24400
25262
|
try {
|
|
24401
|
-
contents = await
|
|
24402
|
-
mode = (await
|
|
25263
|
+
contents = await fs20.readFile(filename, "utf8");
|
|
25264
|
+
mode = (await fs20.stat(filename)).mode & 511;
|
|
24403
25265
|
} catch (error3) {
|
|
24404
25266
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
24405
25267
|
return { file: {}, mode };
|
|
@@ -24421,14 +25283,14 @@ async function readHooks(filename) {
|
|
|
24421
25283
|
return { file: checked.data, mode };
|
|
24422
25284
|
}
|
|
24423
25285
|
async function writeHooks(filename, file, mode) {
|
|
24424
|
-
await
|
|
24425
|
-
const target = await
|
|
25286
|
+
await fs20.mkdir(path25.dirname(filename), { recursive: true, mode: 448 });
|
|
25287
|
+
const target = await fs20.realpath(filename).catch((error3) => {
|
|
24426
25288
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
24427
25289
|
return filename;
|
|
24428
25290
|
throw error3;
|
|
24429
25291
|
});
|
|
24430
25292
|
const temporary = `${target}.iteration-${process.pid}.tmp`;
|
|
24431
|
-
const handle = await
|
|
25293
|
+
const handle = await fs20.open(temporary, "w", mode);
|
|
24432
25294
|
try {
|
|
24433
25295
|
await handle.writeFile(`${JSON.stringify(file, null, 2)}
|
|
24434
25296
|
`, "utf8");
|
|
@@ -24436,14 +25298,14 @@ async function writeHooks(filename, file, mode) {
|
|
|
24436
25298
|
} finally {
|
|
24437
25299
|
await handle.close();
|
|
24438
25300
|
}
|
|
24439
|
-
await
|
|
25301
|
+
await fs20.rename(temporary, target);
|
|
24440
25302
|
}
|
|
24441
25303
|
function entries(value) {
|
|
24442
25304
|
return value ?? [];
|
|
24443
25305
|
}
|
|
24444
25306
|
async function installCursorHooks(cursorHome, dataDirectory2) {
|
|
24445
25307
|
await ensurePrivateDirectory(cursorHookDirectory(dataDirectory2));
|
|
24446
|
-
const filename =
|
|
25308
|
+
const filename = path25.join(cursorHome, "hooks.json");
|
|
24447
25309
|
const { file, mode } = await readHooks(filename);
|
|
24448
25310
|
const hooks = { ...file.hooks };
|
|
24449
25311
|
const nodePath = await stableNodePath();
|
|
@@ -24457,7 +25319,7 @@ async function installCursorHooks(cursorHome, dataDirectory2) {
|
|
|
24457
25319
|
await writeHooks(filename, { ...file, version: 1, hooks }, mode);
|
|
24458
25320
|
}
|
|
24459
25321
|
async function uninstallCursorHooks(cursorHome, dataDirectory2) {
|
|
24460
|
-
const filename =
|
|
25322
|
+
const filename = path25.join(cursorHome, "hooks.json");
|
|
24461
25323
|
const { file, mode } = await readHooks(filename);
|
|
24462
25324
|
const hooks = { ...file.hooks };
|
|
24463
25325
|
let changed = false;
|
|
@@ -24487,8 +25349,8 @@ function silenceCollectorLogs() {
|
|
|
24487
25349
|
// src/turn-summary.ts
|
|
24488
25350
|
import { execFile as execFileCallback7 } from "node:child_process";
|
|
24489
25351
|
import { createHash as createHash6 } from "node:crypto";
|
|
24490
|
-
import { promises as
|
|
24491
|
-
import
|
|
25352
|
+
import { promises as fs21 } from "node:fs";
|
|
25353
|
+
import path26 from "node:path";
|
|
24492
25354
|
import { promisify as promisify7 } from "node:util";
|
|
24493
25355
|
import { crc32 } from "node:zlib";
|
|
24494
25356
|
var execFile7 = promisify7(execFileCallback7);
|
|
@@ -24499,13 +25361,13 @@ var REQUEST_TIMEOUT_MS2 = 1e4;
|
|
|
24499
25361
|
var HOOK_TIMEOUT_SECONDS = 1;
|
|
24500
25362
|
var REQUEST_KEY_PATTERN = /^\d{1,10}$/;
|
|
24501
25363
|
function turnSummaryPaths(dataDirectory2) {
|
|
24502
|
-
const directory =
|
|
25364
|
+
const directory = path26.join(dataDirectory2, "turn-summary");
|
|
24503
25365
|
return {
|
|
24504
25366
|
directory,
|
|
24505
|
-
script:
|
|
24506
|
-
requests:
|
|
24507
|
-
lines:
|
|
24508
|
-
shown:
|
|
25367
|
+
script: path26.join(directory, "stop-hook.sh"),
|
|
25368
|
+
requests: path26.join(directory, "requests"),
|
|
25369
|
+
lines: path26.join(directory, "lines"),
|
|
25370
|
+
shown: path26.join(directory, "shown")
|
|
24509
25371
|
};
|
|
24510
25372
|
}
|
|
24511
25373
|
function shellQuote2(value) {
|
|
@@ -24561,7 +25423,7 @@ async function sessionStartCommand(config3) {
|
|
|
24561
25423
|
function agentHookFile(config3) {
|
|
24562
25424
|
if ("cursorHome" in config3)
|
|
24563
25425
|
throw new Error("Cursor uses its own trace hooks.");
|
|
24564
|
-
return "claudeHome" in config3 ?
|
|
25426
|
+
return "claudeHome" in config3 ? path26.join(config3.claudeHome, "settings.json") : path26.join(config3.codexHome, "hooks.json");
|
|
24565
25427
|
}
|
|
24566
25428
|
var hookEntrySchema2 = object3({
|
|
24567
25429
|
command: string5().optional(),
|
|
@@ -24578,8 +25440,8 @@ async function readHookFile(filename) {
|
|
|
24578
25440
|
let contents;
|
|
24579
25441
|
let mode = 384;
|
|
24580
25442
|
try {
|
|
24581
|
-
contents = await
|
|
24582
|
-
mode = (await
|
|
25443
|
+
contents = await fs21.readFile(filename, "utf8");
|
|
25444
|
+
mode = (await fs21.stat(filename)).mode & 511;
|
|
24583
25445
|
} catch (error3) {
|
|
24584
25446
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT") {
|
|
24585
25447
|
return { file: {}, mode };
|
|
@@ -24605,19 +25467,19 @@ async function readHookFile(filename) {
|
|
|
24605
25467
|
return { file: parsed.data, mode };
|
|
24606
25468
|
}
|
|
24607
25469
|
async function writeHookFile(filename, file, mode) {
|
|
24608
|
-
await
|
|
24609
|
-
const target = await
|
|
25470
|
+
await fs21.mkdir(path26.dirname(filename), { recursive: true });
|
|
25471
|
+
const target = await fs21.realpath(filename).catch((error3) => {
|
|
24610
25472
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
24611
25473
|
return filename;
|
|
24612
25474
|
throw error3;
|
|
24613
25475
|
});
|
|
24614
25476
|
const temporary = `${target}.iteration-${process.pid}.tmp`;
|
|
24615
|
-
await
|
|
25477
|
+
await fs21.writeFile(temporary, `${JSON.stringify(file, null, 2)}
|
|
24616
25478
|
`, {
|
|
24617
25479
|
encoding: "utf8",
|
|
24618
25480
|
mode
|
|
24619
25481
|
});
|
|
24620
|
-
await
|
|
25482
|
+
await fs21.rename(temporary, target);
|
|
24621
25483
|
}
|
|
24622
25484
|
function withoutCommand(groups, command) {
|
|
24623
25485
|
return groups.flatMap((group) => {
|
|
@@ -24642,24 +25504,24 @@ async function installTurnSummary(config3) {
|
|
|
24642
25504
|
[paths.requests, paths.lines, paths.shown].map(ensurePrivateDirectory)
|
|
24643
25505
|
);
|
|
24644
25506
|
if (process.platform !== "win32") {
|
|
24645
|
-
await
|
|
25507
|
+
await fs21.writeFile(paths.script, hookScript(paths), {
|
|
24646
25508
|
encoding: "utf8",
|
|
24647
25509
|
mode: 448
|
|
24648
25510
|
});
|
|
24649
|
-
await
|
|
25511
|
+
await fs21.chmod(paths.script, 448);
|
|
24650
25512
|
}
|
|
24651
25513
|
if ("claudeHome" in config3) {
|
|
24652
25514
|
const gitPaths = claudeGitHookPaths(config3.dataDirectory);
|
|
24653
25515
|
await ensurePrivateDirectory(gitPaths.queue);
|
|
24654
25516
|
if (process.platform !== "win32") {
|
|
24655
|
-
await
|
|
25517
|
+
await fs21.writeFile(
|
|
24656
25518
|
gitPaths.script,
|
|
24657
25519
|
`#!/bin/sh
|
|
24658
25520
|
exec ${shellQuote2(await stableNodePath())} ${shellQuote2(runtimePaths().launcher)} claude-git-hook ${shellQuote2(config3.dataDirectory)}
|
|
24659
25521
|
`,
|
|
24660
25522
|
{ encoding: "utf8", mode: 448 }
|
|
24661
25523
|
);
|
|
24662
|
-
await
|
|
25524
|
+
await fs21.chmod(gitPaths.script, 448);
|
|
24663
25525
|
}
|
|
24664
25526
|
}
|
|
24665
25527
|
const filename = agentHookFile(config3);
|
|
@@ -24749,19 +25611,19 @@ async function captureTurnSummary(dataDirectory2) {
|
|
|
24749
25611
|
windowsHide: true
|
|
24750
25612
|
}
|
|
24751
25613
|
));
|
|
24752
|
-
directory =
|
|
25614
|
+
directory = path26.normalize(directory.trim());
|
|
24753
25615
|
} catch {
|
|
24754
25616
|
quiet();
|
|
24755
25617
|
return;
|
|
24756
25618
|
}
|
|
24757
|
-
if (!
|
|
25619
|
+
if (!path26.isAbsolute(directory)) {
|
|
24758
25620
|
quiet();
|
|
24759
25621
|
return;
|
|
24760
25622
|
}
|
|
24761
25623
|
const key = String(crc32(directory));
|
|
24762
25624
|
const paths = turnSummaryPaths(dataDirectory2);
|
|
24763
25625
|
try {
|
|
24764
|
-
await
|
|
25626
|
+
await fs21.writeFile(path26.join(paths.requests, key), `${directory}
|
|
24765
25627
|
`, {
|
|
24766
25628
|
encoding: "utf8",
|
|
24767
25629
|
mode: 384
|
|
@@ -24770,7 +25632,7 @@ async function captureTurnSummary(dataDirectory2) {
|
|
|
24770
25632
|
}
|
|
24771
25633
|
let lines;
|
|
24772
25634
|
try {
|
|
24773
|
-
lines = await
|
|
25635
|
+
lines = await fs21.readFile(path26.join(paths.lines, key), "utf8");
|
|
24774
25636
|
} catch {
|
|
24775
25637
|
quiet();
|
|
24776
25638
|
return;
|
|
@@ -24782,7 +25644,7 @@ async function captureTurnSummary(dataDirectory2) {
|
|
|
24782
25644
|
}
|
|
24783
25645
|
let last;
|
|
24784
25646
|
try {
|
|
24785
|
-
last = (await
|
|
25647
|
+
last = (await fs21.readFile(path26.join(paths.shown, key), "utf8")).split("\n")[0] ?? null;
|
|
24786
25648
|
} catch {
|
|
24787
25649
|
last = null;
|
|
24788
25650
|
}
|
|
@@ -24791,7 +25653,7 @@ async function captureTurnSummary(dataDirectory2) {
|
|
|
24791
25653
|
return;
|
|
24792
25654
|
}
|
|
24793
25655
|
try {
|
|
24794
|
-
await
|
|
25656
|
+
await fs21.writeFile(path26.join(paths.shown, key), `${message}
|
|
24795
25657
|
`, {
|
|
24796
25658
|
encoding: "utf8",
|
|
24797
25659
|
mode: 384
|
|
@@ -24851,7 +25713,7 @@ async function fetchSummary(config3, directory, fetcher, signal) {
|
|
|
24851
25713
|
}
|
|
24852
25714
|
async function readRequest(filename) {
|
|
24853
25715
|
try {
|
|
24854
|
-
const [directory] = (await
|
|
25716
|
+
const [directory] = (await fs21.readFile(filename, "utf8")).split("\n");
|
|
24855
25717
|
return directory ?? null;
|
|
24856
25718
|
} catch (error3) {
|
|
24857
25719
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
@@ -24860,33 +25722,40 @@ async function readRequest(filename) {
|
|
|
24860
25722
|
}
|
|
24861
25723
|
}
|
|
24862
25724
|
async function writeLine(paths, key, directory, message) {
|
|
24863
|
-
const filename =
|
|
25725
|
+
const filename = path26.join(paths.lines, key);
|
|
24864
25726
|
if (message === null) {
|
|
24865
|
-
await
|
|
25727
|
+
await fs21.rm(filename, { force: true });
|
|
24866
25728
|
return;
|
|
24867
25729
|
}
|
|
24868
25730
|
const temporary = `${filename}.${process.pid}.tmp`;
|
|
24869
|
-
await
|
|
25731
|
+
await fs21.writeFile(
|
|
24870
25732
|
temporary,
|
|
24871
25733
|
`${directory}
|
|
24872
25734
|
${JSON.stringify({ systemMessage: message })}
|
|
24873
25735
|
`,
|
|
24874
25736
|
{ encoding: "utf8", mode: 384 }
|
|
24875
25737
|
);
|
|
24876
|
-
await
|
|
25738
|
+
await fs21.rename(temporary, filename);
|
|
24877
25739
|
}
|
|
24878
25740
|
async function forget(paths, key) {
|
|
24879
25741
|
await Promise.all(
|
|
24880
25742
|
[paths.requests, paths.lines, paths.shown].map(
|
|
24881
|
-
(directory) =>
|
|
25743
|
+
(directory) => fs21.rm(path26.join(directory, key), { force: true })
|
|
24882
25744
|
)
|
|
24883
25745
|
);
|
|
24884
25746
|
}
|
|
25747
|
+
async function clearTurnSummaries(config3) {
|
|
25748
|
+
const paths = turnSummaryPaths(config3.dataDirectory);
|
|
25749
|
+
for (const directory of [paths.lines, paths.shown]) {
|
|
25750
|
+
await fs21.rm(directory, { recursive: true, force: true });
|
|
25751
|
+
await ensurePrivateDirectory(directory);
|
|
25752
|
+
}
|
|
25753
|
+
}
|
|
24885
25754
|
async function refreshTurnSummaries(config3, refreshedAt, nowMs, fetcher = collectorFetch, signal) {
|
|
24886
25755
|
const paths = turnSummaryPaths(config3.dataDirectory);
|
|
24887
25756
|
let keys;
|
|
24888
25757
|
try {
|
|
24889
|
-
keys = await
|
|
25758
|
+
keys = await fs21.readdir(paths.requests);
|
|
24890
25759
|
} catch (error3) {
|
|
24891
25760
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
24892
25761
|
return 0;
|
|
@@ -24896,8 +25765,8 @@ async function refreshTurnSummaries(config3, refreshedAt, nowMs, fetcher = colle
|
|
|
24896
25765
|
for (const key of keys.sort()) {
|
|
24897
25766
|
signal?.throwIfAborted();
|
|
24898
25767
|
if (!REQUEST_KEY_PATTERN.test(key)) continue;
|
|
24899
|
-
const filename =
|
|
24900
|
-
const stats = await
|
|
25768
|
+
const filename = path26.join(paths.requests, key);
|
|
25769
|
+
const stats = await fs21.stat(filename).catch((error3) => {
|
|
24901
25770
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
24902
25771
|
return null;
|
|
24903
25772
|
throw error3;
|
|
@@ -24964,8 +25833,8 @@ async function codexTurnSummaryApproval(config3) {
|
|
|
24964
25833
|
if (handlerIndex < 0) continue;
|
|
24965
25834
|
let configToml;
|
|
24966
25835
|
try {
|
|
24967
|
-
configToml = await
|
|
24968
|
-
|
|
25836
|
+
configToml = await fs21.readFile(
|
|
25837
|
+
path26.join(config3.codexHome, "config.toml"),
|
|
24969
25838
|
"utf8"
|
|
24970
25839
|
);
|
|
24971
25840
|
} catch (error3) {
|
|
@@ -24992,25 +25861,25 @@ var consentSchema = object3({
|
|
|
24992
25861
|
sourceHomes: object3({
|
|
24993
25862
|
codex: string5(),
|
|
24994
25863
|
claude: string5(),
|
|
24995
|
-
cursor: string5().default(
|
|
25864
|
+
cursor: string5().default(path27.join(os10.homedir(), ".cursor"))
|
|
24996
25865
|
}).strict()
|
|
24997
25866
|
}).strict();
|
|
24998
25867
|
function consentPath() {
|
|
24999
|
-
return
|
|
25868
|
+
return path27.join(os10.homedir(), ".config", "iteration", "auto-connect.json");
|
|
25000
25869
|
}
|
|
25001
25870
|
async function removeMachineConsent() {
|
|
25002
|
-
await
|
|
25871
|
+
await fs22.rm(consentPath(), { force: true });
|
|
25003
25872
|
}
|
|
25004
25873
|
async function detectedSources(sourceHomes) {
|
|
25005
25874
|
const detected = [];
|
|
25006
25875
|
for (const source of COLLECTOR_SOURCES2) {
|
|
25007
|
-
const directory =
|
|
25876
|
+
const directory = path27.join(
|
|
25008
25877
|
sourceHomes[source],
|
|
25009
25878
|
source === "codex" ? "sessions" : "projects"
|
|
25010
25879
|
);
|
|
25011
25880
|
let stats;
|
|
25012
25881
|
try {
|
|
25013
|
-
stats = await
|
|
25882
|
+
stats = await fs22.stat(directory);
|
|
25014
25883
|
} catch (error3) {
|
|
25015
25884
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
25016
25885
|
continue;
|
|
@@ -25030,7 +25899,7 @@ async function detectedSources(sourceHomes) {
|
|
|
25030
25899
|
async function loadMachineConsent() {
|
|
25031
25900
|
let text;
|
|
25032
25901
|
try {
|
|
25033
|
-
text = await
|
|
25902
|
+
text = await fs22.readFile(consentPath(), "utf8");
|
|
25034
25903
|
} catch (error3) {
|
|
25035
25904
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
25036
25905
|
return null;
|
|
@@ -25052,43 +25921,6 @@ async function loadMachineConsent() {
|
|
|
25052
25921
|
validateCollectorEndpoint(parsed.data.endpoint);
|
|
25053
25922
|
return parsed.data;
|
|
25054
25923
|
}
|
|
25055
|
-
async function saveMachineConsent(consent) {
|
|
25056
|
-
const parsed = consentSchema.parse(consent);
|
|
25057
|
-
validateCollectorEndpoint(parsed.endpoint);
|
|
25058
|
-
const filename = consentPath();
|
|
25059
|
-
await ensurePrivateDirectory(path24.dirname(filename));
|
|
25060
|
-
const temporary = `${filename}.${process.pid}.tmp`;
|
|
25061
|
-
const handle = await fs19.open(temporary, "w", 384);
|
|
25062
|
-
try {
|
|
25063
|
-
await handle.writeFile(`${JSON.stringify(parsed)}
|
|
25064
|
-
`, "utf8");
|
|
25065
|
-
await handle.sync();
|
|
25066
|
-
} finally {
|
|
25067
|
-
await handle.close();
|
|
25068
|
-
}
|
|
25069
|
-
await fs19.rename(temporary, filename);
|
|
25070
|
-
}
|
|
25071
|
-
async function readCollectorScope(anchor3) {
|
|
25072
|
-
const endpoint = validateCollectorEndpoint(anchor3.endpoint);
|
|
25073
|
-
const response = await collectorFetch(
|
|
25074
|
-
new URL("./collector-auth/expand", endpoint),
|
|
25075
|
-
{
|
|
25076
|
-
headers: { Authorization: `Bearer ${anchor3.token}` },
|
|
25077
|
-
signal: AbortSignal.timeout(1e4)
|
|
25078
|
-
}
|
|
25079
|
-
);
|
|
25080
|
-
const payload = await response.json();
|
|
25081
|
-
if (!response.ok) {
|
|
25082
|
-
const error3 = object3({ error: string5() }).safeParse(payload);
|
|
25083
|
-
throw new Error(
|
|
25084
|
-
`Could not identify the collector workspace: ${error3.success ? error3.data.error : `HTTP ${response.status}`}`
|
|
25085
|
-
);
|
|
25086
|
-
}
|
|
25087
|
-
const parsed = object3({ organizationId: uuid5(), organizationName: string5().min(1) }).strict().safeParse(payload);
|
|
25088
|
-
if (!parsed.success)
|
|
25089
|
-
throw new Error("Iteration returned an invalid collector workspace.");
|
|
25090
|
-
return parsed.data;
|
|
25091
|
-
}
|
|
25092
25924
|
async function expandSource(anchor3, source, organizationId, request2 = collectorFetch) {
|
|
25093
25925
|
const endpoint = validateCollectorEndpoint(anchor3.endpoint);
|
|
25094
25926
|
const token = createHmac("sha256", anchor3.token).update(`iteration-source:${source}`, "utf8").digest("base64url");
|
|
@@ -25162,7 +25994,7 @@ async function connectNewDetectedSources(anchor3, consent) {
|
|
|
25162
25994
|
if (source === consent.anchorSource) continue;
|
|
25163
25995
|
const paths = collectorPaths(defaultCollectorDirectory(source));
|
|
25164
25996
|
try {
|
|
25165
|
-
await
|
|
25997
|
+
await fs22.access(paths.config);
|
|
25166
25998
|
continue;
|
|
25167
25999
|
} catch (error3) {
|
|
25168
26000
|
if (!(error3 instanceof Error && "code" in error3 && error3.code === "ENOENT"))
|
|
@@ -25200,18 +26032,28 @@ async function connectNewDetectedSources(anchor3, consent) {
|
|
|
25200
26032
|
}
|
|
25201
26033
|
return connected;
|
|
25202
26034
|
}
|
|
25203
|
-
async function connectedConfig(source) {
|
|
25204
|
-
const paths = collectorPaths(
|
|
26035
|
+
async function connectedConfig(source, directory = defaultCollectorDirectory(source)) {
|
|
26036
|
+
const paths = collectorPaths(directory);
|
|
25205
26037
|
try {
|
|
25206
|
-
await
|
|
26038
|
+
await fs22.access(paths.config);
|
|
25207
26039
|
} catch (error3) {
|
|
25208
26040
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
25209
26041
|
return null;
|
|
25210
26042
|
throw error3;
|
|
25211
26043
|
}
|
|
25212
|
-
|
|
26044
|
+
const config3 = await loadConfig(paths);
|
|
26045
|
+
if (collectorSource(config3) !== source)
|
|
26046
|
+
throw new Error(
|
|
26047
|
+
`The collector directory belongs to a different coding agent: ${directory}.`
|
|
26048
|
+
);
|
|
26049
|
+
return config3;
|
|
25213
26050
|
}
|
|
25214
26051
|
|
|
26052
|
+
// src/sharing-connect.ts
|
|
26053
|
+
import os12 from "node:os";
|
|
26054
|
+
import path30 from "node:path";
|
|
26055
|
+
import { setTimeout as delay6 } from "node:timers/promises";
|
|
26056
|
+
|
|
25215
26057
|
// src/clerk-device-auth.ts
|
|
25216
26058
|
import { execFile as execFile8 } from "node:child_process";
|
|
25217
26059
|
import { promisify as promisify8 } from "node:util";
|
|
@@ -25586,8 +26428,8 @@ async function clerkDeviceSignIn(configuration) {
|
|
|
25586
26428
|
stopSpinner();
|
|
25587
26429
|
}
|
|
25588
26430
|
}
|
|
25589
|
-
async function enrollDevice(endpoint, accessToken, deviceName, organizationId, source) {
|
|
25590
|
-
const body = { deviceName };
|
|
26431
|
+
async function enrollDevice(endpoint, accessToken, deviceName, organizationId, source, computerId) {
|
|
26432
|
+
const body = { deviceName, ...computerId === void 0 ? {} : { computerId } };
|
|
25591
26433
|
if (source !== "codex") body.source = source;
|
|
25592
26434
|
if (organizationId !== null) body.organizationId = organizationId;
|
|
25593
26435
|
const response = await collectorFetch(
|
|
@@ -25658,7 +26500,7 @@ async function chooseOrganization(organizations, hasMore) {
|
|
|
25658
26500
|
}
|
|
25659
26501
|
return organization.id;
|
|
25660
26502
|
}
|
|
25661
|
-
async function authenticateAndEnrollCollector(endpoint, deviceNameValue, organizationId, source = "codex") {
|
|
26503
|
+
async function authenticateAndEnrollCollector(endpoint, deviceNameValue, organizationId, source = "codex", computerId) {
|
|
25662
26504
|
const deviceName = string5().trim().min(1).max(160).safeParse(deviceNameValue);
|
|
25663
26505
|
if (!deviceName.success) {
|
|
25664
26506
|
throw new Error("--device-name must contain 1 to 160 characters.");
|
|
@@ -25670,34 +26512,617 @@ async function authenticateAndEnrollCollector(endpoint, deviceNameValue, organiz
|
|
|
25670
26512
|
clerkAccessToken,
|
|
25671
26513
|
deviceName.data,
|
|
25672
26514
|
organizationId,
|
|
25673
|
-
source
|
|
26515
|
+
source,
|
|
26516
|
+
computerId
|
|
26517
|
+
);
|
|
26518
|
+
if (result.status === "choose_organization") {
|
|
26519
|
+
const selectedOrganizationId = await chooseOrganization(
|
|
26520
|
+
result.organizations,
|
|
26521
|
+
result.hasMore
|
|
26522
|
+
);
|
|
26523
|
+
result = await enrollDevice(
|
|
26524
|
+
endpoint,
|
|
26525
|
+
clerkAccessToken,
|
|
26526
|
+
deviceName.data,
|
|
26527
|
+
selectedOrganizationId,
|
|
26528
|
+
source,
|
|
26529
|
+
computerId
|
|
26530
|
+
);
|
|
26531
|
+
}
|
|
26532
|
+
if (result.status !== "enrolled") {
|
|
26533
|
+
throw new Error("The collector could not be assigned to that workspace.");
|
|
26534
|
+
}
|
|
26535
|
+
return {
|
|
26536
|
+
token: result.token,
|
|
26537
|
+
deviceId: result.deviceId,
|
|
26538
|
+
organizationId: result.organizationId,
|
|
26539
|
+
organizationName: result.organizationName
|
|
26540
|
+
};
|
|
26541
|
+
}
|
|
26542
|
+
|
|
26543
|
+
// src/sharing-inventory.ts
|
|
26544
|
+
import { promises as fs23 } from "node:fs";
|
|
26545
|
+
import path28 from "node:path";
|
|
26546
|
+
var MAX_FILES = 1e4;
|
|
26547
|
+
var MAX_HEADER_BYTES = 256 * 1024;
|
|
26548
|
+
async function projectInventory(computerId, homes) {
|
|
26549
|
+
const projects = /* @__PURE__ */ new Map();
|
|
26550
|
+
let inventoryBytes = 256;
|
|
26551
|
+
let truncated = false;
|
|
26552
|
+
for (const source of ["codex", "claude", "cursor"]) {
|
|
26553
|
+
const home = homes[source];
|
|
26554
|
+
if (home === void 0) continue;
|
|
26555
|
+
const files = await sourceFilesNewestFirst(source, home);
|
|
26556
|
+
truncated ||= files.length > MAX_FILES;
|
|
26557
|
+
for (const file of files.slice(0, MAX_FILES)) {
|
|
26558
|
+
const handle = await fs23.open(file.filename, "r");
|
|
26559
|
+
let contents;
|
|
26560
|
+
try {
|
|
26561
|
+
const buffer = Buffer.alloc(Math.min(file.size, MAX_HEADER_BYTES));
|
|
26562
|
+
const result = await handle.read(buffer, 0, buffer.length, 0);
|
|
26563
|
+
contents = buffer.subarray(0, result.bytesRead).toString("utf8");
|
|
26564
|
+
} finally {
|
|
26565
|
+
await handle.close();
|
|
26566
|
+
}
|
|
26567
|
+
const cwds = {};
|
|
26568
|
+
const locations = /* @__PURE__ */ new Set();
|
|
26569
|
+
for (const line of contents.split("\n")) {
|
|
26570
|
+
const location = captureRecordLocation(
|
|
26571
|
+
source,
|
|
26572
|
+
line,
|
|
26573
|
+
file.filename,
|
|
26574
|
+
cwds
|
|
26575
|
+
);
|
|
26576
|
+
if (location !== null) locations.add(location);
|
|
26577
|
+
}
|
|
26578
|
+
for (const location of locations) {
|
|
26579
|
+
const bytes = Buffer.byteLength(JSON.stringify(location), "utf8") + 1200;
|
|
26580
|
+
if (!projects.has(location) && (projects.size >= 1e3 || inventoryBytes + bytes > 480 * 1024)) {
|
|
26581
|
+
truncated = true;
|
|
26582
|
+
continue;
|
|
26583
|
+
}
|
|
26584
|
+
if (!projects.has(location)) inventoryBytes += bytes;
|
|
26585
|
+
const project = projects.get(location) ?? {
|
|
26586
|
+
name: (path28.basename(location) || location).slice(0, 160),
|
|
26587
|
+
sessions: 0,
|
|
26588
|
+
sources: /* @__PURE__ */ new Set()
|
|
26589
|
+
};
|
|
26590
|
+
project.sessions += file.session ? 1 : 0;
|
|
26591
|
+
project.sources.add(source);
|
|
26592
|
+
projects.set(location, project);
|
|
26593
|
+
}
|
|
26594
|
+
}
|
|
26595
|
+
}
|
|
26596
|
+
return {
|
|
26597
|
+
computerId,
|
|
26598
|
+
truncated,
|
|
26599
|
+
projects: [...projects].map(([folder, project]) => ({
|
|
26600
|
+
path: folder,
|
|
26601
|
+
name: project.name,
|
|
26602
|
+
sessions: project.sessions,
|
|
26603
|
+
sources: [...project.sources]
|
|
26604
|
+
})).sort(
|
|
26605
|
+
(left, right) => left.name.localeCompare(right.name) || left.path.localeCompare(right.path)
|
|
26606
|
+
)
|
|
26607
|
+
};
|
|
26608
|
+
}
|
|
26609
|
+
|
|
26610
|
+
// src/folder-browser.ts
|
|
26611
|
+
import { promises as fs24 } from "node:fs";
|
|
26612
|
+
import os11 from "node:os";
|
|
26613
|
+
import path29 from "node:path";
|
|
26614
|
+
import { setTimeout as delay5 } from "node:timers/promises";
|
|
26615
|
+
function breadcrumbs(directory, home) {
|
|
26616
|
+
const entries2 = [];
|
|
26617
|
+
let current = directory;
|
|
26618
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
26619
|
+
entries2.unshift({
|
|
26620
|
+
path: current,
|
|
26621
|
+
name: current === home ? "Home" : path29.basename(current) || current
|
|
26622
|
+
});
|
|
26623
|
+
const parent = path29.dirname(current);
|
|
26624
|
+
if (parent === current) break;
|
|
26625
|
+
current = parent;
|
|
26626
|
+
}
|
|
26627
|
+
return entries2;
|
|
26628
|
+
}
|
|
26629
|
+
async function folderEntryIsDirectory(entry, filename) {
|
|
26630
|
+
if (!entry.isSymbolicLink()) return entry.isDirectory();
|
|
26631
|
+
try {
|
|
26632
|
+
return (await fs24.stat(filename)).isDirectory();
|
|
26633
|
+
} catch (error3) {
|
|
26634
|
+
if (error3 instanceof Error && "code" in error3 && ["ENOENT", "EACCES", "EPERM", "ELOOP"].includes(String(error3.code)))
|
|
26635
|
+
return false;
|
|
26636
|
+
throw error3;
|
|
26637
|
+
}
|
|
26638
|
+
}
|
|
26639
|
+
async function readFolderListing(request2, home = os11.homedir(), signal) {
|
|
26640
|
+
signal?.throwIfAborted();
|
|
26641
|
+
const directory = await fs24.realpath(request2.directory ?? home);
|
|
26642
|
+
const parent = path29.dirname(directory);
|
|
26643
|
+
const entries2 = [];
|
|
26644
|
+
let scanned = 0, bytes = 0, truncated = false;
|
|
26645
|
+
const deadline = Date.now() + 3e3;
|
|
26646
|
+
const handle = await fs24.opendir(directory);
|
|
26647
|
+
for await (const entry of handle) {
|
|
26648
|
+
signal?.throwIfAborted();
|
|
26649
|
+
if (++scanned > 1e4 || Date.now() > deadline) {
|
|
26650
|
+
truncated = true;
|
|
26651
|
+
break;
|
|
26652
|
+
}
|
|
26653
|
+
if (!request2.showHidden && entry.name.startsWith(".") || !entry.name.toLowerCase().includes(request2.query.toLowerCase()))
|
|
26654
|
+
continue;
|
|
26655
|
+
const filename = path29.join(directory, entry.name);
|
|
26656
|
+
if (!captureLocationSchema.safeParse(filename).success) continue;
|
|
26657
|
+
if (!await folderEntryIsDirectory(entry, filename)) continue;
|
|
26658
|
+
const folder = { name: entry.name, path: filename };
|
|
26659
|
+
bytes += Buffer.byteLength(JSON.stringify(folder), "utf8");
|
|
26660
|
+
if (entries2.length === 200 || bytes > 384 * 1024) {
|
|
26661
|
+
truncated = true;
|
|
26662
|
+
break;
|
|
26663
|
+
}
|
|
26664
|
+
entries2.push(folder);
|
|
26665
|
+
}
|
|
26666
|
+
entries2.sort((left, right) => left.name.localeCompare(right.name));
|
|
26667
|
+
return {
|
|
26668
|
+
directory,
|
|
26669
|
+
name: path29.basename(directory) || directory,
|
|
26670
|
+
parent: parent === directory ? null : parent,
|
|
26671
|
+
breadcrumbs: breadcrumbs(directory, home),
|
|
26672
|
+
entries: entries2,
|
|
26673
|
+
truncated
|
|
26674
|
+
};
|
|
26675
|
+
}
|
|
26676
|
+
async function listingState(request2, signal) {
|
|
26677
|
+
try {
|
|
26678
|
+
return {
|
|
26679
|
+
status: "ready",
|
|
26680
|
+
request: request2,
|
|
26681
|
+
listing: await readFolderListing(request2, void 0, signal)
|
|
26682
|
+
};
|
|
26683
|
+
} catch (error3) {
|
|
26684
|
+
if (signal.aborted) throw error3;
|
|
26685
|
+
const code = error3 instanceof Error && "code" in error3 ? String(error3.code) : "";
|
|
26686
|
+
return {
|
|
26687
|
+
status: "error",
|
|
26688
|
+
request: request2,
|
|
26689
|
+
reason: ["EACCES", "EPERM"].includes(code) ? "permission" : ["ENOENT", "ENOTDIR", "ELOOP"].includes(code) ? "missing" : "unavailable"
|
|
26690
|
+
};
|
|
26691
|
+
}
|
|
26692
|
+
}
|
|
26693
|
+
async function folderBrowserCycle(config3, signal, fetcher) {
|
|
26694
|
+
const registry3 = await readSharingRegistry();
|
|
26695
|
+
if (!registry3 || registry3.ownerId === null)
|
|
26696
|
+
throw new Error(
|
|
26697
|
+
"The computer connection is unavailable for folder selection."
|
|
26698
|
+
);
|
|
26699
|
+
const deadline = AbortSignal.any([signal, AbortSignal.timeout(1e4)]);
|
|
26700
|
+
for (const connection of registry3.connections.filter(
|
|
26701
|
+
(connection2) => connection2.endpoint === config3.endpoint
|
|
26702
|
+
)) {
|
|
26703
|
+
const url2 = new URL("./collector-folders", connection.endpoint);
|
|
26704
|
+
const headers = { authorization: `Bearer ${connection.token}` };
|
|
26705
|
+
const response = await fetcher(url2, { headers, signal: deadline });
|
|
26706
|
+
if (response.status === 401) {
|
|
26707
|
+
await response.body?.cancel();
|
|
26708
|
+
continue;
|
|
26709
|
+
}
|
|
26710
|
+
if (!response.ok)
|
|
26711
|
+
throw new Error(`Folder selection is waiting (HTTP ${response.status}).`);
|
|
26712
|
+
const command = collectorFolderCommandSchema.nullable().parse(await response.json());
|
|
26713
|
+
if (command === null) return false;
|
|
26714
|
+
if (command.request.expiresAtMs <= Date.now()) return true;
|
|
26715
|
+
const state = await listingState(command.request, deadline);
|
|
26716
|
+
const reply = await fetcher(url2, {
|
|
26717
|
+
method: "POST",
|
|
26718
|
+
headers: { ...headers, "content-type": "application/json" },
|
|
26719
|
+
body: JSON.stringify(
|
|
26720
|
+
collectorFolderReplySchema.parse({
|
|
26721
|
+
organizationId: command.organizationId,
|
|
26722
|
+
state
|
|
26723
|
+
})
|
|
26724
|
+
),
|
|
26725
|
+
signal: deadline
|
|
26726
|
+
});
|
|
26727
|
+
if (!reply.ok)
|
|
26728
|
+
throw new Error(
|
|
26729
|
+
`Folder selection could not finish (HTTP ${reply.status}).`
|
|
26730
|
+
);
|
|
26731
|
+
object3({ accepted: boolean4() }).strict().parse(await reply.json());
|
|
26732
|
+
return true;
|
|
26733
|
+
}
|
|
26734
|
+
throw new Error("Reconnect this computer to use folder selection.");
|
|
26735
|
+
}
|
|
26736
|
+
async function runFolderBrowser(config3, signal, fetcher = collectorFetch) {
|
|
26737
|
+
const paths = collectorPaths(path29.join(sharingDirectory(), "folder-browser"));
|
|
26738
|
+
try {
|
|
26739
|
+
while (!signal.aborted) {
|
|
26740
|
+
const release = await tryAcquireCollectorLock(paths);
|
|
26741
|
+
if (release === null) {
|
|
26742
|
+
await delay5(1500, void 0, { signal });
|
|
26743
|
+
continue;
|
|
26744
|
+
}
|
|
26745
|
+
try {
|
|
26746
|
+
while (!signal.aborted) {
|
|
26747
|
+
let pause = 1500;
|
|
26748
|
+
try {
|
|
26749
|
+
if (await folderBrowserCycle(config3, signal, fetcher)) pause = 250;
|
|
26750
|
+
} catch (error3) {
|
|
26751
|
+
if (signal.aborted) throw error3;
|
|
26752
|
+
collectorLogger({
|
|
26753
|
+
level: "warning",
|
|
26754
|
+
operation: "folder_browser",
|
|
26755
|
+
message: "Folder selection is waiting for the computer connection. Retrying.",
|
|
26756
|
+
errorName: error3 instanceof Error ? error3.name : "UnknownError"
|
|
26757
|
+
});
|
|
26758
|
+
pause = 5e3;
|
|
26759
|
+
}
|
|
26760
|
+
await delay5(pause, void 0, { signal });
|
|
26761
|
+
}
|
|
26762
|
+
} finally {
|
|
26763
|
+
await release();
|
|
26764
|
+
}
|
|
26765
|
+
}
|
|
26766
|
+
} catch (error3) {
|
|
26767
|
+
if (!signal.aborted) throw error3;
|
|
26768
|
+
}
|
|
26769
|
+
}
|
|
26770
|
+
|
|
26771
|
+
// src/sharing-connect.ts
|
|
26772
|
+
function textOption(options, key) {
|
|
26773
|
+
const value = options[key];
|
|
26774
|
+
if (value === void 0) return null;
|
|
26775
|
+
const parsed = string5().trim().min(1).safeParse(value);
|
|
26776
|
+
if (!parsed.success) throw new Error(`--${key} requires a value.`);
|
|
26777
|
+
return parsed.data;
|
|
26778
|
+
}
|
|
26779
|
+
function sourceConfig(source, home, directory, endpoint, enrollment) {
|
|
26780
|
+
const common = {
|
|
26781
|
+
destination: "host",
|
|
26782
|
+
endpoint,
|
|
26783
|
+
dataDirectory: directory,
|
|
26784
|
+
token: enrollment.token,
|
|
26785
|
+
organizationId: enrollment.organizationId,
|
|
26786
|
+
organizationName: enrollment.organizationName
|
|
26787
|
+
};
|
|
26788
|
+
return source === "codex" ? { ...common, codexHome: home } : source === "claude" ? { ...common, source, claudeHome: home } : { ...common, source, cursorHome: home };
|
|
26789
|
+
}
|
|
26790
|
+
async function prepareSource(config3, previous) {
|
|
26791
|
+
const source = collectorSource(config3);
|
|
26792
|
+
const paths = collectorPaths(config3.dataDirectory);
|
|
26793
|
+
if (previous !== null) await uninstallBackgroundService(source);
|
|
26794
|
+
const release = await waitForCollectorLock(paths);
|
|
26795
|
+
try {
|
|
26796
|
+
if (previous === null) {
|
|
26797
|
+
const home = "codexHome" in config3 ? config3.codexHome : "claudeHome" in config3 ? config3.claudeHome : config3.cursorHome;
|
|
26798
|
+
await saveState(
|
|
26799
|
+
await prepareSourceInitialState(source, home, true),
|
|
26800
|
+
paths
|
|
26801
|
+
);
|
|
26802
|
+
} else await ensureStartableState(previous, paths);
|
|
26803
|
+
await saveConfig(config3, paths);
|
|
26804
|
+
} finally {
|
|
26805
|
+
await release();
|
|
26806
|
+
}
|
|
26807
|
+
}
|
|
26808
|
+
async function startSource(config3) {
|
|
26809
|
+
const source = collectorSource(config3);
|
|
26810
|
+
if ("cursorHome" in config3)
|
|
26811
|
+
await installCursorHooks(config3.cursorHome, config3.dataDirectory);
|
|
26812
|
+
await installBackgroundService(config3.dataDirectory, source);
|
|
26813
|
+
try {
|
|
26814
|
+
if (source !== "cursor") await installTurnSummary(config3);
|
|
26815
|
+
await installAgentTools(agentHomes(config3));
|
|
26816
|
+
} catch (error3) {
|
|
26817
|
+
process.stdout.write(
|
|
26818
|
+
`Connected, but the agent integration needs attention: ${error3 instanceof Error ? error3.message : "setup failed"}
|
|
26819
|
+
`
|
|
26820
|
+
);
|
|
26821
|
+
}
|
|
26822
|
+
}
|
|
26823
|
+
async function localConnectionOptions(options, selectedSource) {
|
|
26824
|
+
const existing = await Promise.all(
|
|
26825
|
+
COLLECTOR_SOURCES2.map(
|
|
26826
|
+
(source) => connectedConfig(
|
|
26827
|
+
source,
|
|
26828
|
+
source === selectedSource ? textOption(options, "data-directory") ?? void 0 : void 0
|
|
26829
|
+
)
|
|
26830
|
+
)
|
|
26831
|
+
);
|
|
26832
|
+
const homes = Object.fromEntries(
|
|
26833
|
+
COLLECTOR_SOURCES2.map((source, index) => {
|
|
26834
|
+
const previous = existing[index];
|
|
26835
|
+
const home = previous ? "codexHome" in previous ? previous.codexHome : "claudeHome" in previous ? previous.claudeHome : previous.cursorHome : sourceHome(source);
|
|
26836
|
+
return [
|
|
26837
|
+
source,
|
|
26838
|
+
path30.resolve(textOption(options, `${source}-home`) ?? home)
|
|
26839
|
+
];
|
|
26840
|
+
})
|
|
26841
|
+
);
|
|
26842
|
+
const detected = await detectedSources(homes);
|
|
26843
|
+
const sources = selectedSource ? [selectedSource] : [
|
|
26844
|
+
.../* @__PURE__ */ new Set([
|
|
26845
|
+
...detected,
|
|
26846
|
+
...COLLECTOR_SOURCES2.filter((_, index) => existing[index] !== null)
|
|
26847
|
+
])
|
|
26848
|
+
];
|
|
26849
|
+
const endpoint = validateCollectorEndpoint(
|
|
26850
|
+
textOption(options, "endpoint") ?? existing.find((config3) => config3 !== null)?.endpoint ?? "https://useiteration.com/api/traces/ingest"
|
|
26851
|
+
).toString();
|
|
26852
|
+
if (existing.some((config3) => config3 !== null && config3.endpoint !== endpoint))
|
|
26853
|
+
throw new Error(
|
|
26854
|
+
"This computer is connected to another Iteration server. Sign out before changing servers."
|
|
26855
|
+
);
|
|
26856
|
+
return { existing, homes, sources, endpoint };
|
|
26857
|
+
}
|
|
26858
|
+
function assertSharingOwner(ownerId, nextOwnerId) {
|
|
26859
|
+
if (ownerId !== null && ownerId !== nextOwnerId)
|
|
26860
|
+
throw new Error(
|
|
26861
|
+
"This computer is connected to another Iteration account. Run iteration logout before switching accounts."
|
|
26862
|
+
);
|
|
26863
|
+
}
|
|
26864
|
+
async function waitForSharing(anchor3, alreadyApproved, signal) {
|
|
26865
|
+
const deadline = Date.now() + 15 * 60 * 1e3;
|
|
26866
|
+
let approved = alreadyApproved;
|
|
26867
|
+
while (!approved && Date.now() < deadline) {
|
|
26868
|
+
await delay6(2e3, void 0, { signal });
|
|
26869
|
+
approved = (await fetchSharingSnapshot(anchor3, void 0, signal)).approved;
|
|
26870
|
+
}
|
|
26871
|
+
if (!approved)
|
|
26872
|
+
throw new Error(
|
|
26873
|
+
"Sharing is still waiting for your project choice. Finish it in Iteration, then run iteration connect again."
|
|
26874
|
+
);
|
|
26875
|
+
}
|
|
26876
|
+
async function connectWorkspace(options, selectedSource) {
|
|
26877
|
+
requireCollectorPlatform();
|
|
26878
|
+
if (selectedSource === void 0 && options["data-directory"] !== void 0)
|
|
26879
|
+
throw new Error(
|
|
26880
|
+
"--data-directory requires a source-specific connect command."
|
|
26881
|
+
);
|
|
26882
|
+
const local = await localConnectionOptions(options, selectedSource);
|
|
26883
|
+
if (local.sources.length === 0) {
|
|
26884
|
+
process.stdout.write(
|
|
26885
|
+
"No supported local coding agents found yet. Run iteration connect after using Claude Code, Codex or Cursor.\n"
|
|
26886
|
+
);
|
|
26887
|
+
return;
|
|
26888
|
+
}
|
|
26889
|
+
if ((!process.stdin.isTTY || !process.stdout.isTTY) && options.yes !== true)
|
|
26890
|
+
throw new Error(
|
|
26891
|
+
"Run iteration connect interactively or pass --yes to open browser sign-in. Project sharing still requires approval in Iteration."
|
|
26892
|
+
);
|
|
26893
|
+
await prepareBackgroundPersistence();
|
|
26894
|
+
const prepared = await withSharingRegistry(async (initial, save) => {
|
|
26895
|
+
let registry3 = initial;
|
|
26896
|
+
const { existing, homes, sources, endpoint } = local;
|
|
26897
|
+
const first = sources[0];
|
|
26898
|
+
const requested = textOption(options, "workspace");
|
|
26899
|
+
let anchor3 = requested === null ? null : sharingConnection(registry3, requested, first);
|
|
26900
|
+
if (anchor3 !== null) {
|
|
26901
|
+
try {
|
|
26902
|
+
await fetchSharingSnapshot(anchor3);
|
|
26903
|
+
} catch {
|
|
26904
|
+
anchor3 = null;
|
|
26905
|
+
}
|
|
26906
|
+
}
|
|
26907
|
+
if (anchor3 === null) {
|
|
26908
|
+
const enrollment = await authenticateAndEnrollCollector(
|
|
26909
|
+
new URL(endpoint),
|
|
26910
|
+
textOption(options, "device-name") ?? os12.hostname(),
|
|
26911
|
+
requested,
|
|
26912
|
+
first,
|
|
26913
|
+
registry3.computerId
|
|
26914
|
+
);
|
|
26915
|
+
anchor3 = enrolledSharingConnection(enrollment, first, endpoint);
|
|
26916
|
+
}
|
|
26917
|
+
const anchorConfig = sourceConfig(
|
|
26918
|
+
first,
|
|
26919
|
+
homes[first],
|
|
26920
|
+
defaultCollectorDirectory(first),
|
|
26921
|
+
endpoint,
|
|
26922
|
+
anchor3
|
|
26923
|
+
);
|
|
26924
|
+
const inventory = collectorProjectInventorySchema.parse(
|
|
26925
|
+
await projectInventory(registry3.computerId, homes)
|
|
26926
|
+
);
|
|
26927
|
+
const response = await collectorFetch(
|
|
26928
|
+
new URL("./collector-sharing", endpoint),
|
|
26929
|
+
{
|
|
26930
|
+
method: "POST",
|
|
26931
|
+
headers: {
|
|
26932
|
+
authorization: `Bearer ${anchor3.token}`,
|
|
26933
|
+
"content-type": "application/json"
|
|
26934
|
+
},
|
|
26935
|
+
body: JSON.stringify(inventory),
|
|
26936
|
+
signal: AbortSignal.timeout(2e4)
|
|
26937
|
+
}
|
|
26938
|
+
);
|
|
26939
|
+
if (!response.ok)
|
|
26940
|
+
throw new Error(
|
|
26941
|
+
`Could not prepare project sharing (HTTP ${response.status}). No new sharing has started.`
|
|
26942
|
+
);
|
|
26943
|
+
const snapshot = collectorSharingSnapshotSchema.parse(
|
|
26944
|
+
await response.json()
|
|
26945
|
+
);
|
|
26946
|
+
assertSharingOwner(registry3.ownerId, snapshot.ownerId);
|
|
26947
|
+
registry3 = {
|
|
26948
|
+
...registry3,
|
|
26949
|
+
ownerId: snapshot.ownerId,
|
|
26950
|
+
sourceHomes: homes,
|
|
26951
|
+
connections: [
|
|
26952
|
+
...registry3.connections.filter(
|
|
26953
|
+
(connection) => connection.organizationId !== anchor3.organizationId || connection.source !== first
|
|
26954
|
+
),
|
|
26955
|
+
anchor3
|
|
26956
|
+
]
|
|
26957
|
+
};
|
|
26958
|
+
await save(registry3);
|
|
26959
|
+
await prepareBackgroundPersistence();
|
|
26960
|
+
for (const previous of existing) {
|
|
26961
|
+
if (previous !== null && previous.destination !== "host")
|
|
26962
|
+
await prepareSource({ ...previous, destination: "host" }, previous);
|
|
26963
|
+
}
|
|
26964
|
+
const configs = [];
|
|
26965
|
+
for (const source of sources) {
|
|
26966
|
+
const enrollment = source === first ? anchor3 : await expandSource(anchorConfig, source, anchor3.organizationId);
|
|
26967
|
+
const connection = enrolledSharingConnection(
|
|
26968
|
+
enrollment,
|
|
26969
|
+
source,
|
|
26970
|
+
endpoint
|
|
26971
|
+
);
|
|
26972
|
+
registry3 = {
|
|
26973
|
+
...registry3,
|
|
26974
|
+
connections: [
|
|
26975
|
+
...registry3.connections.filter(
|
|
26976
|
+
(saved) => saved.organizationId !== connection.organizationId || saved.source !== source
|
|
26977
|
+
),
|
|
26978
|
+
connection
|
|
26979
|
+
]
|
|
26980
|
+
};
|
|
26981
|
+
await save(registry3);
|
|
26982
|
+
const previous = existing[COLLECTOR_SOURCES2.indexOf(source)] ?? null;
|
|
26983
|
+
const directory = selectedSource ? path30.resolve(
|
|
26984
|
+
textOption(options, "data-directory") ?? defaultCollectorDirectory(source)
|
|
26985
|
+
) : defaultCollectorDirectory(source);
|
|
26986
|
+
const config3 = previous === null ? sourceConfig(source, homes[source], directory, endpoint, enrollment) : { ...previous, destination: "host" };
|
|
26987
|
+
if (previous === null || previous.destination !== "host")
|
|
26988
|
+
await prepareSource(config3, previous);
|
|
26989
|
+
registry3 = {
|
|
26990
|
+
...registry3,
|
|
26991
|
+
directories: {
|
|
26992
|
+
...registry3.directories,
|
|
26993
|
+
[source]: config3.dataDirectory
|
|
26994
|
+
}
|
|
26995
|
+
};
|
|
26996
|
+
await save(registry3);
|
|
26997
|
+
configs.push(config3);
|
|
26998
|
+
}
|
|
26999
|
+
return {
|
|
27000
|
+
configs,
|
|
27001
|
+
anchor: anchor3,
|
|
27002
|
+
approved: snapshot.approved,
|
|
27003
|
+
computerId: registry3.computerId
|
|
27004
|
+
};
|
|
27005
|
+
});
|
|
27006
|
+
const url2 = new URL("/app/collectors/share", prepared.anchor.endpoint);
|
|
27007
|
+
url2.searchParams.set("workspace", prepared.anchor.organizationId);
|
|
27008
|
+
url2.searchParams.set("computer", prepared.computerId);
|
|
27009
|
+
process.stdout.write(
|
|
27010
|
+
`
|
|
27011
|
+
Choose which projects ${prepared.anchor.organizationName} can see.
|
|
27012
|
+
${url2}
|
|
27013
|
+
|
|
27014
|
+
Nothing new uploads until you approve sharing in Iteration.
|
|
27015
|
+
`
|
|
25674
27016
|
);
|
|
25675
|
-
|
|
25676
|
-
|
|
25677
|
-
|
|
25678
|
-
|
|
27017
|
+
const folderController = new AbortController();
|
|
27018
|
+
const failures = [];
|
|
27019
|
+
const folderTask = runFolderBrowser(
|
|
27020
|
+
prepared.anchor,
|
|
27021
|
+
folderController.signal
|
|
27022
|
+
).then(
|
|
27023
|
+
() => null,
|
|
27024
|
+
(error3) => {
|
|
27025
|
+
folderController.abort();
|
|
27026
|
+
return { error: error3 };
|
|
27027
|
+
}
|
|
27028
|
+
);
|
|
27029
|
+
try {
|
|
27030
|
+
await openBrowser(url2.toString());
|
|
27031
|
+
await waitForSharing(
|
|
27032
|
+
prepared.anchor,
|
|
27033
|
+
prepared.approved,
|
|
27034
|
+
folderController.signal
|
|
25679
27035
|
);
|
|
25680
|
-
|
|
25681
|
-
|
|
25682
|
-
|
|
25683
|
-
|
|
25684
|
-
|
|
25685
|
-
|
|
27036
|
+
for (const config3 of prepared.configs) {
|
|
27037
|
+
await startSource(config3);
|
|
27038
|
+
process.stdout.write(
|
|
27039
|
+
`${collectorSourceLabel(collectorSource(config3))} connected \xB7 sharing approved projects
|
|
27040
|
+
`
|
|
27041
|
+
);
|
|
27042
|
+
}
|
|
27043
|
+
} catch (error3) {
|
|
27044
|
+
failures.push(error3);
|
|
27045
|
+
} finally {
|
|
27046
|
+
folderController.abort();
|
|
27047
|
+
const outcome = await folderTask;
|
|
27048
|
+
if (outcome !== null) failures.push(outcome.error);
|
|
27049
|
+
}
|
|
27050
|
+
if (failures.length === 1) throw failures[0];
|
|
27051
|
+
if (failures.length > 1)
|
|
27052
|
+
throw new AggregateError(
|
|
27053
|
+
failures,
|
|
27054
|
+
"The computer connection could not finish cleanly."
|
|
25686
27055
|
);
|
|
25687
|
-
|
|
25688
|
-
|
|
25689
|
-
|
|
25690
|
-
|
|
25691
|
-
|
|
25692
|
-
|
|
25693
|
-
|
|
25694
|
-
|
|
25695
|
-
|
|
25696
|
-
|
|
27056
|
+
process.stdout.write(
|
|
27057
|
+
"Saved sessions from those projects import first. New activity follows the same choice, whichever coding account you use.\n"
|
|
27058
|
+
);
|
|
27059
|
+
}
|
|
27060
|
+
async function connectSharedDetectedSources(config3) {
|
|
27061
|
+
await withSharingRegistry(async (registry3, save) => {
|
|
27062
|
+
if (registry3.connections.length === 0) return;
|
|
27063
|
+
const { snapshot } = await sharingTransport(
|
|
27064
|
+
config3,
|
|
27065
|
+
void 0,
|
|
27066
|
+
void 0,
|
|
27067
|
+
registry3
|
|
27068
|
+
);
|
|
27069
|
+
const homes = registry3.sourceHomes ?? {
|
|
27070
|
+
codex: sourceHome("codex"),
|
|
27071
|
+
claude: sourceHome("claude"),
|
|
27072
|
+
cursor: sourceHome("cursor")
|
|
27073
|
+
};
|
|
27074
|
+
let next = registry3;
|
|
27075
|
+
for (const source of await detectedSources(homes)) {
|
|
27076
|
+
const previous = await connectedConfig(
|
|
27077
|
+
source,
|
|
27078
|
+
registry3.directories?.[source]
|
|
27079
|
+
);
|
|
27080
|
+
for (const workspace of snapshot.workspaces.filter(
|
|
27081
|
+
(workspace2) => workspace2.active
|
|
27082
|
+
)) {
|
|
27083
|
+
if (sharingConnection(next, workspace.organizationId, source)) continue;
|
|
27084
|
+
const anchor3 = next.connections.find(
|
|
27085
|
+
(connection2) => connection2.organizationId === workspace.organizationId
|
|
27086
|
+
);
|
|
27087
|
+
if (!anchor3) continue;
|
|
27088
|
+
const enrollment = await expandSource(
|
|
27089
|
+
{
|
|
27090
|
+
...config3,
|
|
27091
|
+
token: anchor3.token,
|
|
27092
|
+
organizationId: anchor3.organizationId,
|
|
27093
|
+
organizationName: anchor3.organizationName
|
|
27094
|
+
},
|
|
27095
|
+
source,
|
|
27096
|
+
workspace.organizationId
|
|
27097
|
+
);
|
|
27098
|
+
next = {
|
|
27099
|
+
...next,
|
|
27100
|
+
connections: [
|
|
27101
|
+
...next.connections,
|
|
27102
|
+
enrolledSharingConnection(enrollment, source, config3.endpoint)
|
|
27103
|
+
]
|
|
27104
|
+
};
|
|
27105
|
+
await save(next);
|
|
27106
|
+
}
|
|
27107
|
+
const connection = next.connections.find(
|
|
27108
|
+
(item) => item.source === source
|
|
27109
|
+
);
|
|
27110
|
+
if (previous !== null || !connection) continue;
|
|
27111
|
+
const added = sourceConfig(
|
|
27112
|
+
source,
|
|
27113
|
+
homes[source],
|
|
27114
|
+
defaultCollectorDirectory(source),
|
|
27115
|
+
config3.endpoint,
|
|
27116
|
+
connection
|
|
27117
|
+
);
|
|
27118
|
+
await prepareSource(added, null);
|
|
27119
|
+
await startSource(added);
|
|
27120
|
+
}
|
|
27121
|
+
});
|
|
25697
27122
|
}
|
|
25698
27123
|
|
|
25699
27124
|
// src/history-import.ts
|
|
25700
|
-
import
|
|
27125
|
+
import path31 from "node:path";
|
|
25701
27126
|
function backgroundControl(source) {
|
|
25702
27127
|
return {
|
|
25703
27128
|
isRunning: () => backgroundServiceStatus(source),
|
|
@@ -25710,7 +27135,7 @@ async function importSourceHistory(config3, paths, confirmed, control = backgrou
|
|
|
25710
27135
|
const source = collectorSource(config3);
|
|
25711
27136
|
const directory = collectorSourceDirectory(config3);
|
|
25712
27137
|
const state = await loadState(paths);
|
|
25713
|
-
if (!state.initialized || state.sourceDirectory !==
|
|
27138
|
+
if (!state.initialized || state.sourceDirectory !== path31.resolve(directory)) {
|
|
25714
27139
|
throw new Error(
|
|
25715
27140
|
`Connect the ${source} source before importing its history.`
|
|
25716
27141
|
);
|
|
@@ -25758,37 +27183,37 @@ async function importSourceHistory(config3, paths, confirmed, control = backgrou
|
|
|
25758
27183
|
}
|
|
25759
27184
|
|
|
25760
27185
|
// src/logout.ts
|
|
25761
|
-
import { randomUUID as
|
|
25762
|
-
import { promises as
|
|
25763
|
-
import
|
|
25764
|
-
import
|
|
27186
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
27187
|
+
import { promises as fs25 } from "node:fs";
|
|
27188
|
+
import os13 from "node:os";
|
|
27189
|
+
import path32 from "node:path";
|
|
25765
27190
|
async function pendingDirectories(filename) {
|
|
25766
27191
|
let contents;
|
|
25767
27192
|
try {
|
|
25768
|
-
contents = await
|
|
27193
|
+
contents = await fs25.readFile(filename, "utf8");
|
|
25769
27194
|
} catch (error3) {
|
|
25770
27195
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
25771
27196
|
return [];
|
|
25772
27197
|
throw error3;
|
|
25773
27198
|
}
|
|
25774
|
-
const parsed = array2(string5().refine(
|
|
27199
|
+
const parsed = array2(string5().refine(path32.isAbsolute)).max(64).safeParse(JSON.parse(contents));
|
|
25775
27200
|
if (!parsed.success)
|
|
25776
27201
|
throw new Error("Pending logout directories are invalid.");
|
|
25777
27202
|
return parsed.data;
|
|
25778
27203
|
}
|
|
25779
27204
|
async function savePendingDirectories(filename, directories) {
|
|
25780
|
-
await ensurePrivateDirectory(
|
|
27205
|
+
await ensurePrivateDirectory(path32.dirname(filename));
|
|
25781
27206
|
const temporary = `${filename}.${process.pid}.tmp`;
|
|
25782
|
-
await
|
|
27207
|
+
await fs25.writeFile(temporary, JSON.stringify([...directories]), {
|
|
25783
27208
|
mode: 384
|
|
25784
27209
|
});
|
|
25785
|
-
await
|
|
27210
|
+
await fs25.rename(temporary, filename);
|
|
25786
27211
|
}
|
|
25787
27212
|
async function clearConnection(directory) {
|
|
25788
27213
|
const paths = collectorPaths(directory);
|
|
25789
27214
|
let names;
|
|
25790
27215
|
try {
|
|
25791
|
-
names = await
|
|
27216
|
+
names = await fs25.readdir(directory);
|
|
25792
27217
|
} catch (error3) {
|
|
25793
27218
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
25794
27219
|
return;
|
|
@@ -25798,14 +27223,15 @@ async function clearConnection(directory) {
|
|
|
25798
27223
|
try {
|
|
25799
27224
|
for (const name of names)
|
|
25800
27225
|
if (/^config\.json\.\d+\.tmp$/.test(name))
|
|
25801
|
-
await
|
|
25802
|
-
if (!names.includes(
|
|
27226
|
+
await fs25.rm(path32.join(directory, name), { force: true });
|
|
27227
|
+
if (!names.includes(path32.basename(paths.config))) return;
|
|
25803
27228
|
const config3 = await loadConfig(paths);
|
|
25804
27229
|
if (config3.destination === "vault")
|
|
25805
27230
|
throw new Error("This directory belongs to Trace Vault, not Iteration.");
|
|
25806
27231
|
if ("cursorHome" in config3)
|
|
25807
27232
|
await uninstallCursorHooks(config3.cursorHome, config3.dataDirectory);
|
|
25808
27233
|
else await uninstallTurnSummary(config3);
|
|
27234
|
+
await uninstallAgentTools(agentHomes(config3));
|
|
25809
27235
|
const retained = [
|
|
25810
27236
|
paths.state,
|
|
25811
27237
|
paths.outbox,
|
|
@@ -25819,41 +27245,43 @@ async function clearConnection(directory) {
|
|
|
25819
27245
|
"claude-git",
|
|
25820
27246
|
"turn-summary",
|
|
25821
27247
|
"codex-billing-sync.json",
|
|
25822
|
-
"cursor-billing-sync.json"
|
|
25823
|
-
|
|
27248
|
+
"cursor-billing-sync.json",
|
|
27249
|
+
"shared-sessions.json"
|
|
27250
|
+
].map((name) => path32.join(directory, name))
|
|
25824
27251
|
];
|
|
25825
|
-
const archive =
|
|
27252
|
+
const archive = path32.join(directory, "logged-out", randomUUID4());
|
|
25826
27253
|
await ensurePrivateDirectory(archive);
|
|
25827
27254
|
for (const filename of retained) {
|
|
25828
27255
|
try {
|
|
25829
|
-
await
|
|
27256
|
+
await fs25.rename(filename, path32.join(archive, path32.basename(filename)));
|
|
25830
27257
|
} catch (error3) {
|
|
25831
27258
|
if (!(error3 instanceof Error && "code" in error3 && error3.code === "ENOENT"))
|
|
25832
27259
|
throw error3;
|
|
25833
27260
|
}
|
|
25834
27261
|
}
|
|
25835
|
-
await
|
|
27262
|
+
await fs25.rm(paths.config);
|
|
25836
27263
|
} finally {
|
|
25837
27264
|
await release();
|
|
25838
27265
|
}
|
|
25839
27266
|
}
|
|
25840
27267
|
async function logout(extraDirectory) {
|
|
25841
27268
|
requireCollectorPlatform();
|
|
25842
|
-
const
|
|
25843
|
-
|
|
27269
|
+
const pending2 = path32.join(
|
|
27270
|
+
os13.homedir(),
|
|
25844
27271
|
".config",
|
|
25845
27272
|
"iteration",
|
|
25846
27273
|
"logout-pending.json"
|
|
25847
27274
|
);
|
|
25848
27275
|
const directories = /* @__PURE__ */ new Set([
|
|
25849
27276
|
...COLLECTOR_SOURCES2.map(defaultCollectorDirectory),
|
|
25850
|
-
...await pendingDirectories(
|
|
27277
|
+
...await pendingDirectories(pending2)
|
|
25851
27278
|
]);
|
|
25852
27279
|
if (extraDirectory !== void 0)
|
|
25853
|
-
directories.add(
|
|
27280
|
+
directories.add(path32.resolve(extraDirectory));
|
|
25854
27281
|
const failures = [];
|
|
25855
27282
|
try {
|
|
25856
27283
|
await removeMachineConsent();
|
|
27284
|
+
await forgetSharingConnections();
|
|
25857
27285
|
} catch (error3) {
|
|
25858
27286
|
failures.push(
|
|
25859
27287
|
`Automatic connection approval: ${error3 instanceof Error ? error3.message : "Could not remove approval."}`
|
|
@@ -25871,7 +27299,7 @@ async function logout(extraDirectory) {
|
|
|
25871
27299
|
);
|
|
25872
27300
|
}
|
|
25873
27301
|
}
|
|
25874
|
-
await savePendingDirectories(
|
|
27302
|
+
await savePendingDirectories(pending2, directories);
|
|
25875
27303
|
const stopped = await Promise.all(
|
|
25876
27304
|
COLLECTOR_SOURCES2.filter((source) => !unreadableServices.has(source)).map(
|
|
25877
27305
|
(source) => uninstallBackgroundService(source).then(
|
|
@@ -25895,7 +27323,7 @@ async function logout(extraDirectory) {
|
|
|
25895
27323
|
`Logout did not finish. Resolve these errors and run iteration logout again:
|
|
25896
27324
|
${failures.join("\n")}`
|
|
25897
27325
|
);
|
|
25898
|
-
await
|
|
27326
|
+
await fs25.rm(pending2);
|
|
25899
27327
|
}
|
|
25900
27328
|
|
|
25901
27329
|
// src/runtime-services.ts
|
|
@@ -25922,17 +27350,17 @@ async function adoptRuntime() {
|
|
|
25922
27350
|
}
|
|
25923
27351
|
|
|
25924
27352
|
// src/service.ts
|
|
25925
|
-
import { setTimeout as
|
|
27353
|
+
import { setTimeout as delay7 } from "node:timers/promises";
|
|
25926
27354
|
|
|
25927
27355
|
// src/codex-billing.ts
|
|
25928
27356
|
import { createHash as createHash7 } from "node:crypto";
|
|
25929
|
-
import { promises as
|
|
25930
|
-
import
|
|
27357
|
+
import { promises as fs26 } from "node:fs";
|
|
27358
|
+
import path33 from "node:path";
|
|
25931
27359
|
var TASK_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage/thread_usage/query_v2";
|
|
25932
27360
|
var UUID2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
25933
27361
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
25934
27362
|
var TASK_BATCH_SIZE = 50;
|
|
25935
|
-
var
|
|
27363
|
+
var MAX_HEADER_BYTES2 = 3 * 1024 * 1024;
|
|
25936
27364
|
var stateSchema2 = object3({
|
|
25937
27365
|
accountFingerprint: string5().regex(/^[0-9a-f]{64}$/),
|
|
25938
27366
|
lastSyncedAtMs: number4().int().nonnegative(),
|
|
@@ -25979,7 +27407,7 @@ function fingerprint(value) {
|
|
|
25979
27407
|
async function readAuth(codexHome) {
|
|
25980
27408
|
let contents;
|
|
25981
27409
|
try {
|
|
25982
|
-
contents = await
|
|
27410
|
+
contents = await fs26.readFile(path33.join(codexHome, "auth.json"), "utf8");
|
|
25983
27411
|
} catch (error3) {
|
|
25984
27412
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
25985
27413
|
return {
|
|
@@ -26014,7 +27442,7 @@ async function readAuth(codexHome) {
|
|
|
26014
27442
|
async function readState(filename) {
|
|
26015
27443
|
let contents;
|
|
26016
27444
|
try {
|
|
26017
|
-
contents = await
|
|
27445
|
+
contents = await fs26.readFile(filename, "utf8");
|
|
26018
27446
|
} catch (error3) {
|
|
26019
27447
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
26020
27448
|
return null;
|
|
@@ -26036,7 +27464,7 @@ async function readState(filename) {
|
|
|
26036
27464
|
}
|
|
26037
27465
|
async function saveState2(filename, state) {
|
|
26038
27466
|
const temporary = `${filename}.${process.pid}.tmp`;
|
|
26039
|
-
const handle = await
|
|
27467
|
+
const handle = await fs26.open(temporary, "w", 384);
|
|
26040
27468
|
try {
|
|
26041
27469
|
await handle.writeFile(`${JSON.stringify(state)}
|
|
26042
27470
|
`);
|
|
@@ -26044,21 +27472,21 @@ async function saveState2(filename, state) {
|
|
|
26044
27472
|
} finally {
|
|
26045
27473
|
await handle.close();
|
|
26046
27474
|
}
|
|
26047
|
-
await
|
|
27475
|
+
await fs26.rename(temporary, filename);
|
|
26048
27476
|
}
|
|
26049
27477
|
async function localThread(filename) {
|
|
26050
|
-
const stat = await
|
|
26051
|
-
const handle = await
|
|
27478
|
+
const stat = await fs26.stat(filename);
|
|
27479
|
+
const handle = await fs26.open(filename, "r");
|
|
26052
27480
|
let header = null;
|
|
26053
27481
|
try {
|
|
26054
27482
|
const buffer = Buffer.alloc(64 * 1024);
|
|
26055
27483
|
const chunks = [];
|
|
26056
27484
|
let offset = 0;
|
|
26057
|
-
while (offset < Math.min(stat.size,
|
|
27485
|
+
while (offset < Math.min(stat.size, MAX_HEADER_BYTES2)) {
|
|
26058
27486
|
const read = await handle.read(
|
|
26059
27487
|
buffer,
|
|
26060
27488
|
0,
|
|
26061
|
-
Math.min(buffer.length,
|
|
27489
|
+
Math.min(buffer.length, MAX_HEADER_BYTES2 - offset),
|
|
26062
27490
|
offset
|
|
26063
27491
|
);
|
|
26064
27492
|
if (read.bytesRead === 0) break;
|
|
@@ -26148,9 +27576,9 @@ function parseCodexAuthLog(threadId, body) {
|
|
|
26148
27576
|
return { threadId, turnId: turn[1], mode };
|
|
26149
27577
|
}
|
|
26150
27578
|
async function localAuthTurns(codexHome, modelProviders) {
|
|
26151
|
-
const filename =
|
|
27579
|
+
const filename = path33.join(codexHome, "logs_2.sqlite");
|
|
26152
27580
|
try {
|
|
26153
|
-
await
|
|
27581
|
+
await fs26.access(filename);
|
|
26154
27582
|
} catch (error3) {
|
|
26155
27583
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
26156
27584
|
return [];
|
|
@@ -26243,7 +27671,7 @@ async function queryTaskUsage(tasks, accessToken, accountId, signal) {
|
|
|
26243
27671
|
}
|
|
26244
27672
|
async function syncCodexBilling(config3, fetcher = collectorFetch, signal) {
|
|
26245
27673
|
const auth = await readAuth(config3.codexHome);
|
|
26246
|
-
const stateFile =
|
|
27674
|
+
const stateFile = path33.join(config3.dataDirectory, "codex-billing-sync.json");
|
|
26247
27675
|
const prior = await readState(stateFile);
|
|
26248
27676
|
const nowMs = Date.now();
|
|
26249
27677
|
const history = prior === null || prior.accountFingerprint !== auth.accountFingerprint;
|
|
@@ -26414,9 +27842,9 @@ async function syncCodexBilling(config3, fetcher = collectorFetch, signal) {
|
|
|
26414
27842
|
|
|
26415
27843
|
// src/cursor-billing.ts
|
|
26416
27844
|
import { createHash as createHash8 } from "node:crypto";
|
|
26417
|
-
import { promises as
|
|
26418
|
-
import
|
|
26419
|
-
import
|
|
27845
|
+
import { promises as fs27 } from "node:fs";
|
|
27846
|
+
import os14 from "node:os";
|
|
27847
|
+
import path34 from "node:path";
|
|
26420
27848
|
var CURSOR_USAGE_URL = "https://api2.cursor.sh/aiserver.v1.DashboardService/GetFilteredUsageEvents";
|
|
26421
27849
|
var PAGE_SIZE = 500;
|
|
26422
27850
|
var MAX_PAGES = 1e3;
|
|
@@ -26447,18 +27875,18 @@ var usagePageSchema = object3({
|
|
|
26447
27875
|
var emptyUsagePageSchema = object3({}).strict();
|
|
26448
27876
|
function cursorAuthDatabase() {
|
|
26449
27877
|
if (process.platform === "darwin")
|
|
26450
|
-
return
|
|
26451
|
-
|
|
27878
|
+
return path34.join(
|
|
27879
|
+
os14.homedir(),
|
|
26452
27880
|
"Library/Application Support/Cursor/User/globalStorage/state.vscdb"
|
|
26453
27881
|
);
|
|
26454
27882
|
if (process.platform === "linux")
|
|
26455
|
-
return
|
|
26456
|
-
|
|
27883
|
+
return path34.join(
|
|
27884
|
+
os14.homedir(),
|
|
26457
27885
|
".config/Cursor/User/globalStorage/state.vscdb"
|
|
26458
27886
|
);
|
|
26459
27887
|
if (process.platform === "win32")
|
|
26460
|
-
return
|
|
26461
|
-
process.env.APPDATA ??
|
|
27888
|
+
return path34.join(
|
|
27889
|
+
process.env.APPDATA ?? path34.join(os14.homedir(), "AppData", "Roaming"),
|
|
26462
27890
|
"Cursor/User/globalStorage/state.vscdb"
|
|
26463
27891
|
);
|
|
26464
27892
|
return null;
|
|
@@ -26500,7 +27928,7 @@ async function readCursorAuth() {
|
|
|
26500
27928
|
async function loadBillingState(filename) {
|
|
26501
27929
|
let contents;
|
|
26502
27930
|
try {
|
|
26503
|
-
contents = await
|
|
27931
|
+
contents = await fs27.readFile(filename, "utf8");
|
|
26504
27932
|
} catch (error3) {
|
|
26505
27933
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
26506
27934
|
return null;
|
|
@@ -26516,7 +27944,7 @@ async function loadBillingState(filename) {
|
|
|
26516
27944
|
}
|
|
26517
27945
|
async function saveBillingState(filename, state) {
|
|
26518
27946
|
const temporary = `${filename}.${process.pid}.tmp`;
|
|
26519
|
-
const handle = await
|
|
27947
|
+
const handle = await fs27.open(temporary, "w", 384);
|
|
26520
27948
|
try {
|
|
26521
27949
|
await handle.writeFile(`${JSON.stringify(state)}
|
|
26522
27950
|
`, "utf8");
|
|
@@ -26524,7 +27952,7 @@ async function saveBillingState(filename, state) {
|
|
|
26524
27952
|
} finally {
|
|
26525
27953
|
await handle.close();
|
|
26526
27954
|
}
|
|
26527
|
-
await
|
|
27955
|
+
await fs27.rename(temporary, filename);
|
|
26528
27956
|
}
|
|
26529
27957
|
function planCursorBillingSync(state, accountFingerprint, nowMs) {
|
|
26530
27958
|
if (state === null || state.accountFingerprint !== accountFingerprint)
|
|
@@ -26546,7 +27974,7 @@ function planCursorBillingSync(state, accountFingerprint, nowMs) {
|
|
|
26546
27974
|
}
|
|
26547
27975
|
function cursorConversationIds(filenames) {
|
|
26548
27976
|
return new Set(
|
|
26549
|
-
filenames.map((filename) =>
|
|
27977
|
+
filenames.map((filename) => path34.basename(filename, ".jsonl")).filter((id) => UUID3.test(id))
|
|
26550
27978
|
);
|
|
26551
27979
|
}
|
|
26552
27980
|
function billingKind(kind) {
|
|
@@ -26604,11 +28032,11 @@ async function syncCursorBilling(config3, fetcher = collectorFetch, signal) {
|
|
|
26604
28032
|
if (auth === null)
|
|
26605
28033
|
return { sessions: 0, matched: 0, skipped: true, mode: null };
|
|
26606
28034
|
const localConversations = cursorConversationIds(
|
|
26607
|
-
await cursorFiles(
|
|
28035
|
+
await cursorFiles(path34.join(config3.cursorHome, "projects"))
|
|
26608
28036
|
);
|
|
26609
28037
|
if (localConversations.size === 0)
|
|
26610
28038
|
return { sessions: 0, matched: 0, skipped: true, mode: null };
|
|
26611
|
-
const stateFile =
|
|
28039
|
+
const stateFile = path34.join(config3.dataDirectory, "cursor-billing-sync.json");
|
|
26612
28040
|
const priorState = await loadBillingState(stateFile);
|
|
26613
28041
|
const nowMs = Date.now();
|
|
26614
28042
|
const plan = planCursorBillingSync(
|
|
@@ -26850,6 +28278,14 @@ async function onTrial(version3) {
|
|
|
26850
28278
|
const state = await readRuntimeState(runtimePaths());
|
|
26851
28279
|
return state?.trial?.version === version3;
|
|
26852
28280
|
}
|
|
28281
|
+
function throwCollectorFailures(failures) {
|
|
28282
|
+
if (failures.length === 1) throw failures[0];
|
|
28283
|
+
if (failures.length > 1)
|
|
28284
|
+
throw new AggregateError(
|
|
28285
|
+
failures,
|
|
28286
|
+
"The collector could not finish cleanly."
|
|
28287
|
+
);
|
|
28288
|
+
}
|
|
26853
28289
|
async function runCollector(directory = defaultCollectorDirectory("codex"), expectedSource) {
|
|
26854
28290
|
const paths = collectorPaths(directory);
|
|
26855
28291
|
const releaseLock = await acquireCollectorLock(paths);
|
|
@@ -26858,8 +28294,18 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
|
|
|
26858
28294
|
process.on("SIGTERM", stop);
|
|
26859
28295
|
process.on("SIGINT", stop);
|
|
26860
28296
|
let retryDelayMs = 5e3;
|
|
28297
|
+
let folderTask = null;
|
|
28298
|
+
const failures = [];
|
|
26861
28299
|
try {
|
|
26862
28300
|
const config3 = await loadConfig(paths);
|
|
28301
|
+
if (config3.destination === "host")
|
|
28302
|
+
folderTask = runFolderBrowser(config3, controller.signal).then(
|
|
28303
|
+
() => null,
|
|
28304
|
+
(error3) => {
|
|
28305
|
+
controller.abort();
|
|
28306
|
+
return { error: error3 };
|
|
28307
|
+
}
|
|
28308
|
+
);
|
|
26863
28309
|
const source = collectorSource(config3);
|
|
26864
28310
|
if (expectedSource !== void 0 && expectedSource !== source) {
|
|
26865
28311
|
throw new Error(
|
|
@@ -26884,6 +28330,16 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
|
|
|
26884
28330
|
} else await ensureHistoryBackfill(config3.codexHome, paths);
|
|
26885
28331
|
const summariesRefreshedAt = /* @__PURE__ */ new Map();
|
|
26886
28332
|
let lastAutoCheckAt = 0;
|
|
28333
|
+
let scoped = null;
|
|
28334
|
+
let sharingRevision = null;
|
|
28335
|
+
const fetcher = (input, init) => {
|
|
28336
|
+
if (config3.destination !== "host") return collectorFetch(input, init);
|
|
28337
|
+
if (scoped === null)
|
|
28338
|
+
throw new Error(
|
|
28339
|
+
"Project sharing could not be verified. Collection is waiting."
|
|
28340
|
+
);
|
|
28341
|
+
return scoped.fetcher(input, init);
|
|
28342
|
+
};
|
|
26887
28343
|
let lastBillingSyncAt = 0;
|
|
26888
28344
|
const historyProgress2 = new HistoryProgressReporter();
|
|
26889
28345
|
const runtimeVersion = process.env.ITERATION_RUNTIME_VERSION;
|
|
@@ -26920,6 +28376,10 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
|
|
|
26920
28376
|
if (Date.now() - lastAutoCheckAt >= 6e4) {
|
|
26921
28377
|
lastAutoCheckAt = Date.now();
|
|
26922
28378
|
try {
|
|
28379
|
+
if (config3.destination === "host") {
|
|
28380
|
+
await connectSharedDetectedSources(config3);
|
|
28381
|
+
return;
|
|
28382
|
+
}
|
|
26923
28383
|
const consent = await loadMachineConsent();
|
|
26924
28384
|
if (consent?.anchorSource === source) {
|
|
26925
28385
|
const connected = await connectNewDetectedSources(config3, consent);
|
|
@@ -26972,15 +28432,31 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
|
|
|
26972
28432
|
let billingReady = false;
|
|
26973
28433
|
let history = null;
|
|
26974
28434
|
try {
|
|
28435
|
+
if (config3.destination === "host") {
|
|
28436
|
+
scoped = null;
|
|
28437
|
+
scoped = await sharingTransport(
|
|
28438
|
+
config3,
|
|
28439
|
+
collectorFetch,
|
|
28440
|
+
controller.signal
|
|
28441
|
+
);
|
|
28442
|
+
if (sharingRevision !== scoped.snapshot.revision) {
|
|
28443
|
+
await clearTurnSummaries(config3);
|
|
28444
|
+
summariesRefreshedAt.clear();
|
|
28445
|
+
sharingRevision = scoped.snapshot.revision;
|
|
28446
|
+
}
|
|
28447
|
+
}
|
|
26975
28448
|
const result = await collectorTick(
|
|
26976
28449
|
config3,
|
|
26977
28450
|
paths,
|
|
26978
|
-
|
|
28451
|
+
fetcher,
|
|
26979
28452
|
controller.signal,
|
|
26980
28453
|
() => {
|
|
26981
28454
|
acknowledgedAtMs = Date.now();
|
|
26982
28455
|
},
|
|
26983
|
-
{
|
|
28456
|
+
{
|
|
28457
|
+
recentMessages: RECENT_MESSAGE_LIMIT,
|
|
28458
|
+
...scoped === null ? {} : { capture: scoped.capture }
|
|
28459
|
+
}
|
|
26984
28460
|
);
|
|
26985
28461
|
billingReady = result.historyCaughtUp && result.outboxBytes === 0;
|
|
26986
28462
|
history = {
|
|
@@ -27021,7 +28497,7 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
|
|
|
27021
28497
|
await historyProgress2.report(
|
|
27022
28498
|
config3,
|
|
27023
28499
|
history,
|
|
27024
|
-
|
|
28500
|
+
fetcher,
|
|
27025
28501
|
controller.signal,
|
|
27026
28502
|
Date.now()
|
|
27027
28503
|
);
|
|
@@ -27046,7 +28522,7 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
|
|
|
27046
28522
|
config3,
|
|
27047
28523
|
summariesRefreshedAt,
|
|
27048
28524
|
Date.now(),
|
|
27049
|
-
|
|
28525
|
+
fetcher,
|
|
27050
28526
|
controller.signal
|
|
27051
28527
|
);
|
|
27052
28528
|
} catch (error3) {
|
|
@@ -27067,7 +28543,7 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
|
|
|
27067
28543
|
try {
|
|
27068
28544
|
const billing = await syncCursorBilling(
|
|
27069
28545
|
config3,
|
|
27070
|
-
|
|
28546
|
+
fetcher,
|
|
27071
28547
|
controller.signal
|
|
27072
28548
|
);
|
|
27073
28549
|
lastBillingSyncAt = billing.skipped ? Date.now() - 6 * 60 * 60 * 1e3 + 5 * 60 * 1e3 : Date.now();
|
|
@@ -27090,7 +28566,7 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
|
|
|
27090
28566
|
try {
|
|
27091
28567
|
const billing = await syncCodexBilling(
|
|
27092
28568
|
config3,
|
|
27093
|
-
|
|
28569
|
+
fetcher,
|
|
27094
28570
|
controller.signal
|
|
27095
28571
|
);
|
|
27096
28572
|
lastBillingSyncAt = billing.skipped ? Date.now() - 6 * 60 * 60 * 1e3 + 5 * 60 * 1e3 : Date.now();
|
|
@@ -27120,17 +28596,23 @@ async function runCollector(directory = defaultCollectorDirectory("codex"), expe
|
|
|
27120
28596
|
if (!await refreshSummaries()) break;
|
|
27121
28597
|
if (!await syncBilling(result.billingReady)) break;
|
|
27122
28598
|
try {
|
|
27123
|
-
await
|
|
28599
|
+
await delay7(retryDelayMs, void 0, { signal: controller.signal });
|
|
27124
28600
|
} catch (error3) {
|
|
27125
28601
|
if (error3 instanceof Error && error3.name === "AbortError") break;
|
|
27126
28602
|
throw error3;
|
|
27127
28603
|
}
|
|
27128
28604
|
}
|
|
28605
|
+
} catch (error3) {
|
|
28606
|
+
failures.push(error3);
|
|
27129
28607
|
} finally {
|
|
28608
|
+
controller.abort();
|
|
27130
28609
|
process.off("SIGTERM", stop);
|
|
27131
28610
|
process.off("SIGINT", stop);
|
|
27132
|
-
await
|
|
28611
|
+
const outcome = await folderTask;
|
|
28612
|
+
if (outcome) failures.push(outcome.error);
|
|
28613
|
+
await releaseLock().catch((error3) => failures.push(error3));
|
|
27133
28614
|
}
|
|
28615
|
+
throwCollectorFailures(failures);
|
|
27134
28616
|
}
|
|
27135
28617
|
|
|
27136
28618
|
// src/status-view.ts
|
|
@@ -27255,14 +28737,14 @@ function failureSummary(terminal2, status2, source, nowMs) {
|
|
|
27255
28737
|
}
|
|
27256
28738
|
function connectionSummary(terminal2, status2, source, nowMs) {
|
|
27257
28739
|
const name = sourceName(source);
|
|
27258
|
-
const
|
|
28740
|
+
const pending2 = status2.pendingEvents > 0 ? [`${pendingDescription(status2)}.`] : [];
|
|
27259
28741
|
const details = `Details: ${terminal2.bold(`iteration status ${source}`)}`;
|
|
27260
28742
|
if (!status2.running)
|
|
27261
28743
|
return {
|
|
27262
28744
|
tone: "warning",
|
|
27263
28745
|
headline: "Stopped",
|
|
27264
28746
|
facts: [],
|
|
27265
|
-
details: ["New sessions are not being collected.", ...
|
|
28747
|
+
details: ["New sessions are not being collected.", ...pending2],
|
|
27266
28748
|
next: `Resume with ${terminal2.bold(`iteration start ${source}`)}`
|
|
27267
28749
|
};
|
|
27268
28750
|
if (status2.hookFailureAtMs !== null && status2.hookFailureAtMs !== void 0)
|
|
@@ -27282,7 +28764,7 @@ function connectionSummary(terminal2, status2, source, nowMs) {
|
|
|
27282
28764
|
facts: [],
|
|
27283
28765
|
details: [
|
|
27284
28766
|
"Upload history could not be read. Collection continues in the background.",
|
|
27285
|
-
...
|
|
28767
|
+
...pending2
|
|
27286
28768
|
],
|
|
27287
28769
|
next: details
|
|
27288
28770
|
};
|
|
@@ -27292,7 +28774,7 @@ function connectionSummary(terminal2, status2, source, nowMs) {
|
|
|
27292
28774
|
}
|
|
27293
28775
|
function scanSummary(status2, source, nowMs) {
|
|
27294
28776
|
const name = sourceName(source);
|
|
27295
|
-
const
|
|
28777
|
+
const pending2 = status2.pendingEvents > 0 ? [`${pendingDescription(status2)}.`] : [];
|
|
27296
28778
|
const lastUpload = status2.health.lastUploadAtMs === null ? [] : [`last upload ${relativeTime(status2.health.lastUploadAtMs, nowMs)}`];
|
|
27297
28779
|
const summary = (headline, details, facts = []) => ({
|
|
27298
28780
|
tone: "success",
|
|
@@ -27311,10 +28793,10 @@ function scanSummary(status2, source, nowMs) {
|
|
|
27311
28793
|
if (scan.totalBytes - scan.scannedBytes >= 1e6)
|
|
27312
28794
|
return summary("Catching up with new activity", [
|
|
27313
28795
|
historyDescription(status2, source),
|
|
27314
|
-
...
|
|
28796
|
+
...pending2
|
|
27315
28797
|
]);
|
|
27316
28798
|
if (status2.pendingEvents > 0)
|
|
27317
|
-
return summary("Uploading captured sessions",
|
|
28799
|
+
return summary("Uploading captured sessions", pending2);
|
|
27318
28800
|
if (scan.totalBytes === 0)
|
|
27319
28801
|
return summary("Connected", [
|
|
27320
28802
|
`Waiting for ${name} sessions on this computer.`
|
|
@@ -27534,26 +29016,26 @@ function renderRuntime(terminal2, summary) {
|
|
|
27534
29016
|
|
|
27535
29017
|
// src/windows-supervisor.ts
|
|
27536
29018
|
import { spawn } from "node:child_process";
|
|
27537
|
-
import { promises as
|
|
27538
|
-
import
|
|
27539
|
-
import { setTimeout as
|
|
29019
|
+
import { promises as fs28 } from "node:fs";
|
|
29020
|
+
import path35 from "node:path";
|
|
29021
|
+
import { setTimeout as delay8 } from "node:timers/promises";
|
|
27540
29022
|
var MAX_LOG_BYTES = 8 * 1024 * 1024;
|
|
27541
29023
|
var RESTART_DELAY_MS = 5e3;
|
|
27542
29024
|
async function appendLog(filename, bytes) {
|
|
27543
29025
|
let offset = 0;
|
|
27544
29026
|
while (offset < bytes.length) {
|
|
27545
|
-
const size = await
|
|
29027
|
+
const size = await fs28.stat(filename).then((stat) => stat.size).catch((error3) => {
|
|
27546
29028
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
27547
29029
|
return 0;
|
|
27548
29030
|
throw error3;
|
|
27549
29031
|
});
|
|
27550
29032
|
if (size >= MAX_LOG_BYTES) {
|
|
27551
|
-
await
|
|
27552
|
-
await
|
|
29033
|
+
await fs28.rm(`${filename}.1`, { force: true });
|
|
29034
|
+
await fs28.rename(filename, `${filename}.1`);
|
|
27553
29035
|
continue;
|
|
27554
29036
|
}
|
|
27555
29037
|
const count = Math.min(bytes.length - offset, MAX_LOG_BYTES - size);
|
|
27556
|
-
await
|
|
29038
|
+
await fs28.appendFile(filename, bytes.subarray(offset, offset + count), {
|
|
27557
29039
|
mode: 384
|
|
27558
29040
|
});
|
|
27559
29041
|
offset += count;
|
|
@@ -27569,7 +29051,7 @@ async function copyOutput(stream, filename) {
|
|
|
27569
29051
|
async function superviseCollector(source, dataDirectory2) {
|
|
27570
29052
|
const output = windowsOutputPath(source);
|
|
27571
29053
|
const errors = windowsLogPath(source);
|
|
27572
|
-
await ensurePrivateDirectory(
|
|
29054
|
+
await ensurePrivateDirectory(path35.dirname(output));
|
|
27573
29055
|
const controller = new AbortController();
|
|
27574
29056
|
const stop = () => controller.abort();
|
|
27575
29057
|
process.once("SIGTERM", stop);
|
|
@@ -27626,7 +29108,7 @@ async function superviseCollector(source, dataDirectory2) {
|
|
|
27626
29108
|
`
|
|
27627
29109
|
)
|
|
27628
29110
|
);
|
|
27629
|
-
await
|
|
29111
|
+
await delay8(RESTART_DELAY_MS, void 0, {
|
|
27630
29112
|
signal: controller.signal
|
|
27631
29113
|
}).catch((error3) => {
|
|
27632
29114
|
if (!controller.signal.aborted) throw error3;
|
|
@@ -27639,7 +29121,6 @@ async function superviseCollector(source, dataDirectory2) {
|
|
|
27639
29121
|
}
|
|
27640
29122
|
|
|
27641
29123
|
// src/cli.ts
|
|
27642
|
-
var DEFAULT_INGEST_ENDPOINT = "https://useiteration.com/api/traces/ingest";
|
|
27643
29124
|
var terminal = terminalFor(process.stdout);
|
|
27644
29125
|
function print(...lines) {
|
|
27645
29126
|
process.stdout.write(`${lines.join("\n")}
|
|
@@ -27733,7 +29214,7 @@ function usage(command) {
|
|
|
27733
29214
|
"",
|
|
27734
29215
|
wrap(
|
|
27735
29216
|
terminal,
|
|
27736
|
-
"
|
|
29217
|
+
"Choose projects in Iteration before any sessions upload."
|
|
27737
29218
|
),
|
|
27738
29219
|
"",
|
|
27739
29220
|
...section("Options for advanced setups", [
|
|
@@ -27742,7 +29223,7 @@ function usage(command) {
|
|
|
27742
29223
|
["--codex-home DIR", "Use a different Codex data directory"],
|
|
27743
29224
|
["--claude-home DIR", "Use a different Claude data directory"],
|
|
27744
29225
|
["--cursor-home DIR", "Use a different Cursor data directory"],
|
|
27745
|
-
["--yes", "
|
|
29226
|
+
["--yes", "Project sharing still requires approval in Iteration"],
|
|
27746
29227
|
[
|
|
27747
29228
|
"--data-directory DIR",
|
|
27748
29229
|
"Use a different state directory with a one-agent command"
|
|
@@ -27781,7 +29262,7 @@ function usage(command) {
|
|
|
27781
29262
|
}
|
|
27782
29263
|
async function fileExists(filename) {
|
|
27783
29264
|
try {
|
|
27784
|
-
await
|
|
29265
|
+
await fs29.access(filename);
|
|
27785
29266
|
return true;
|
|
27786
29267
|
} catch (error3) {
|
|
27787
29268
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT") {
|
|
@@ -27822,7 +29303,7 @@ async function defaultCommand() {
|
|
|
27822
29303
|
await connectAll({});
|
|
27823
29304
|
}
|
|
27824
29305
|
function dataDirectory(source, values) {
|
|
27825
|
-
return
|
|
29306
|
+
return path36.resolve(
|
|
27826
29307
|
optionalString(values, "data-directory") ?? defaultCollectorDirectory(source)
|
|
27827
29308
|
);
|
|
27828
29309
|
}
|
|
@@ -27869,312 +29350,8 @@ function turnSummaryLines(results) {
|
|
|
27869
29350
|
...warnings
|
|
27870
29351
|
];
|
|
27871
29352
|
}
|
|
27872
|
-
var CONNECTED_WIDTH = Math.max(
|
|
27873
|
-
...COLLECTOR_SOURCES2.map(
|
|
27874
|
-
(source) => `${collectorSourceLabel(source)} connected`.length
|
|
27875
|
-
)
|
|
27876
|
-
);
|
|
27877
|
-
function connectedLine(source, sessions) {
|
|
27878
|
-
return mark(
|
|
27879
|
-
terminal,
|
|
27880
|
-
"success",
|
|
27881
|
-
`${padEnd(`${collectorSourceLabel(source)} connected`, CONNECTED_WIDTH)} ${terminal.dim(
|
|
27882
|
-
sessions === 0 ? "no saved sessions yet" : `importing ${sessions.toLocaleString("en-US")} ${sessions === 1 ? "session" : "sessions"}`
|
|
27883
|
-
)}`
|
|
27884
|
-
);
|
|
27885
|
-
}
|
|
27886
|
-
function connectionSummary2(results, imported) {
|
|
27887
|
-
const links = [
|
|
27888
|
-
...new Set(results.map(({ endpoint }) => tracesUrl(endpoint)))
|
|
27889
|
-
];
|
|
27890
|
-
const notes = turnSummaryLines(results);
|
|
27891
|
-
return [
|
|
27892
|
-
...notes.length === 0 ? [] : ["", ...notes],
|
|
27893
|
-
"",
|
|
27894
|
-
wrap(
|
|
27895
|
-
terminal,
|
|
27896
|
-
imported ? `Sessions import newest first. Run ${terminal.bold("iteration")} to follow progress.` : `Run ${terminal.bold("iteration")} to see their status.`
|
|
27897
|
-
),
|
|
27898
|
-
...links.map((url2) => ` Open Iteration ${terminal.link(url2)}`)
|
|
27899
|
-
];
|
|
27900
|
-
}
|
|
27901
|
-
async function initialize(source, values) {
|
|
27902
|
-
requireCollectorPlatform();
|
|
27903
|
-
const endpoint = optionalString(values, "endpoint") ?? DEFAULT_INGEST_ENDPOINT;
|
|
27904
|
-
const endpointUrl = validateCollectorEndpoint(endpoint);
|
|
27905
|
-
const deviceName = optionalString(values, "device-name") ?? os11.hostname();
|
|
27906
|
-
const organizationId = optionalString(values, "workspace");
|
|
27907
|
-
const directory = dataDirectory(source, values);
|
|
27908
|
-
const paths = collectorPaths(directory);
|
|
27909
|
-
const selectedHome = path30.resolve(
|
|
27910
|
-
source === "codex" ? optionalString(values, "codex-home") ?? process.env.CODEX_HOME ?? path30.join(os11.homedir(), ".codex") : source === "claude" ? optionalString(values, "claude-home") ?? process.env.CLAUDE_CONFIG_DIR ?? path30.join(os11.homedir(), ".claude") : optionalString(values, "cursor-home") ?? path30.join(os11.homedir(), ".cursor")
|
|
27911
|
-
);
|
|
27912
|
-
await assertFreshCollectorDirectory(paths, source);
|
|
27913
|
-
const preparedState = await prepareSourceInitialState(
|
|
27914
|
-
source,
|
|
27915
|
-
selectedHome,
|
|
27916
|
-
true
|
|
27917
|
-
);
|
|
27918
|
-
await prepareBackgroundPersistence();
|
|
27919
|
-
const enrollment = await authenticateAndEnrollCollector(
|
|
27920
|
-
endpointUrl,
|
|
27921
|
-
deviceName,
|
|
27922
|
-
organizationId,
|
|
27923
|
-
source
|
|
27924
|
-
);
|
|
27925
|
-
print(
|
|
27926
|
-
mark(
|
|
27927
|
-
terminal,
|
|
27928
|
-
"success",
|
|
27929
|
-
`Signed in to ${terminal.bold(safeWorkspaceName(enrollment.organizationName) ?? "your workspace")}`
|
|
27930
|
-
)
|
|
27931
|
-
);
|
|
27932
|
-
const connection = {
|
|
27933
|
-
endpoint: endpointUrl.toString(),
|
|
27934
|
-
token: enrollment.token,
|
|
27935
|
-
dataDirectory: directory,
|
|
27936
|
-
organizationId: enrollment.organizationId,
|
|
27937
|
-
organizationName: enrollment.organizationName
|
|
27938
|
-
};
|
|
27939
|
-
const config3 = source === "codex" ? { ...connection, codexHome: selectedHome } : source === "claude" ? { ...connection, source: "claude", claudeHome: selectedHome } : { ...connection, source: "cursor", cursorHome: selectedHome };
|
|
27940
|
-
await saveConfig(config3, paths);
|
|
27941
|
-
try {
|
|
27942
|
-
await saveState(preparedState, paths);
|
|
27943
|
-
if (source === "cursor") await installCursorHooks(selectedHome, directory);
|
|
27944
|
-
} catch (error3) {
|
|
27945
|
-
throw new Error(
|
|
27946
|
-
`Collector access was approved, but local setup did not finish. Run iteration start ${source} to repair it.`,
|
|
27947
|
-
{ cause: error3 }
|
|
27948
|
-
);
|
|
27949
|
-
}
|
|
27950
|
-
try {
|
|
27951
|
-
await installBackgroundService(directory, source);
|
|
27952
|
-
} catch (error3) {
|
|
27953
|
-
throw new Error(
|
|
27954
|
-
`Collector access was approved, but background startup failed. Run iteration start ${source} to retry it.`,
|
|
27955
|
-
{ cause: error3 }
|
|
27956
|
-
);
|
|
27957
|
-
}
|
|
27958
|
-
const turnSummary = await setUpTurnSummary(config3);
|
|
27959
|
-
print(connectedLine(source, await countSavedSessions(source, selectedHome)));
|
|
27960
|
-
return {
|
|
27961
|
-
config: config3,
|
|
27962
|
-
result: { source, endpoint: config3.endpoint, turnSummary }
|
|
27963
|
-
};
|
|
27964
|
-
}
|
|
27965
|
-
async function approveAllSources(sources, workspaceName, yes) {
|
|
27966
|
-
const safeName = safeWorkspaceName(workspaceName);
|
|
27967
|
-
print(
|
|
27968
|
-
wrap(
|
|
27969
|
-
terminal,
|
|
27970
|
-
sources.length === 0 ? "No saved agent sessions found on this computer" : `Found ${sources.length} coding ${sources.length === 1 ? "agent" : "agents"} on this computer`,
|
|
27971
|
-
{ style: terminal.bold }
|
|
27972
|
-
),
|
|
27973
|
-
...sources.length === 0 ? [] : [
|
|
27974
|
-
"",
|
|
27975
|
-
rows(
|
|
27976
|
-
terminal,
|
|
27977
|
-
sources.map(({ source, sessions }) => [
|
|
27978
|
-
collectorSourceLabel(source),
|
|
27979
|
-
terminal.dim(
|
|
27980
|
-
`${sessions.toLocaleString("en-US")} saved ${sessions === 1 ? "session" : "sessions"}`
|
|
27981
|
-
)
|
|
27982
|
-
]),
|
|
27983
|
-
" "
|
|
27984
|
-
)
|
|
27985
|
-
],
|
|
27986
|
-
"",
|
|
27987
|
-
wrap(
|
|
27988
|
-
terminal,
|
|
27989
|
-
`Existing sessions and new activity, including prompts, code and tool output, upload to ${safeName ?? "your workspace"}, where its members can read them. Supported agents you install later connect automatically.${process.platform === "linux" ? " Collection continues after you log out." : ""}`
|
|
27990
|
-
),
|
|
27991
|
-
""
|
|
27992
|
-
);
|
|
27993
|
-
if (yes) return;
|
|
27994
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
27995
|
-
throw new Error(
|
|
27996
|
-
"Run iteration connect interactively or pass --yes to approve uploads."
|
|
27997
|
-
);
|
|
27998
|
-
}
|
|
27999
|
-
const answer = await ask(
|
|
28000
|
-
terminal,
|
|
28001
|
-
`${terminal.bold(sources.length === 1 ? "Connect this agent?" : "Connect these agents?")} ${terminal.dim("(y/N)")}`
|
|
28002
|
-
);
|
|
28003
|
-
const approved = answer?.trim().toLowerCase();
|
|
28004
|
-
if (approved !== "y" && approved !== "yes")
|
|
28005
|
-
throw new Error(
|
|
28006
|
-
"No agents were connected. Run iteration connect when you are ready."
|
|
28007
|
-
);
|
|
28008
|
-
}
|
|
28009
29353
|
async function connectAll(values) {
|
|
28010
|
-
|
|
28011
|
-
if (values["data-directory"] !== void 0) {
|
|
28012
|
-
throw new Error(
|
|
28013
|
-
"--data-directory requires a source-specific connect command."
|
|
28014
|
-
);
|
|
28015
|
-
}
|
|
28016
|
-
const configs = await Promise.all(COLLECTOR_SOURCES2.map(connectedConfig));
|
|
28017
|
-
const resolveSourceHomes = () => ({
|
|
28018
|
-
codex: path30.resolve(
|
|
28019
|
-
optionalString(values, "codex-home") ?? (configs[0] && "codexHome" in configs[0] ? configs[0].codexHome : sourceHome("codex"))
|
|
28020
|
-
),
|
|
28021
|
-
claude: path30.resolve(
|
|
28022
|
-
optionalString(values, "claude-home") ?? (configs[1] && "claudeHome" in configs[1] ? configs[1].claudeHome : sourceHome("claude"))
|
|
28023
|
-
),
|
|
28024
|
-
cursor: path30.resolve(
|
|
28025
|
-
optionalString(values, "cursor-home") ?? (configs[2] && "cursorHome" in configs[2] ? configs[2].cursorHome : sourceHome("cursor"))
|
|
28026
|
-
)
|
|
28027
|
-
});
|
|
28028
|
-
const sourceHomes = resolveSourceHomes();
|
|
28029
|
-
const detected = await detectedSources(sourceHomes);
|
|
28030
|
-
const existing = COLLECTOR_SOURCES2.flatMap(
|
|
28031
|
-
(source, index) => configs[index] ? [{ source, config: configs[index] }] : []
|
|
28032
|
-
);
|
|
28033
|
-
print(brand(terminal), "");
|
|
28034
|
-
if (detected.length === 0 && existing.length === 0) {
|
|
28035
|
-
print(
|
|
28036
|
-
mark(terminal, "warning", "No supported local coding agents found yet"),
|
|
28037
|
-
wrap(
|
|
28038
|
-
terminal,
|
|
28039
|
-
`Run ${terminal.bold("iteration connect")} after using Codex, Claude Code or Cursor on this computer.`
|
|
28040
|
-
)
|
|
28041
|
-
);
|
|
28042
|
-
return;
|
|
28043
|
-
}
|
|
28044
|
-
const sessions = await Promise.all(
|
|
28045
|
-
detected.map(async (source) => ({
|
|
28046
|
-
source,
|
|
28047
|
-
sessions: await countSavedSessions(source, sourceHomes[source])
|
|
28048
|
-
}))
|
|
28049
|
-
);
|
|
28050
|
-
const anchorSource = existing[0]?.source ?? detected[0];
|
|
28051
|
-
const anchorExisting = existing[0]?.config ?? null;
|
|
28052
|
-
const endpoint = validateCollectorEndpoint(
|
|
28053
|
-
optionalString(values, "endpoint") ?? anchorExisting?.endpoint ?? DEFAULT_INGEST_ENDPOINT
|
|
28054
|
-
).toString();
|
|
28055
|
-
const existingScopes = await Promise.all(
|
|
28056
|
-
existing.map(async (item) => ({
|
|
28057
|
-
...item,
|
|
28058
|
-
scope: await readCollectorScope(item.config)
|
|
28059
|
-
}))
|
|
28060
|
-
);
|
|
28061
|
-
const validateWorkspace = () => {
|
|
28062
|
-
for (const item of existingScopes) {
|
|
28063
|
-
if (item.config.endpoint !== endpoint) {
|
|
28064
|
-
throw new Error(
|
|
28065
|
-
"Existing sources use different Iteration servers. Connect one source at a time."
|
|
28066
|
-
);
|
|
28067
|
-
}
|
|
28068
|
-
if (existingScopes[0]?.scope.organizationId !== item.scope.organizationId) {
|
|
28069
|
-
throw new Error(
|
|
28070
|
-
"Existing sources use different workspaces. Connect one source at a time."
|
|
28071
|
-
);
|
|
28072
|
-
}
|
|
28073
|
-
}
|
|
28074
|
-
const requestedOrganizationId2 = optionalString(values, "workspace");
|
|
28075
|
-
if (requestedOrganizationId2 && existingScopes[0] && requestedOrganizationId2 !== existingScopes[0].scope.organizationId) {
|
|
28076
|
-
throw new Error(
|
|
28077
|
-
"The existing collector belongs to a different workspace."
|
|
28078
|
-
);
|
|
28079
|
-
}
|
|
28080
|
-
};
|
|
28081
|
-
validateWorkspace();
|
|
28082
|
-
const requestedOrganizationId = optionalString(values, "workspace");
|
|
28083
|
-
const previousConsent = await loadMachineConsent();
|
|
28084
|
-
const approveConnection = async () => {
|
|
28085
|
-
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;
|
|
28086
|
-
if (needsApproval()) {
|
|
28087
|
-
await approveAllSources(
|
|
28088
|
-
sessions,
|
|
28089
|
-
existingScopes[0]?.scope.organizationName ?? null,
|
|
28090
|
-
values.yes === true
|
|
28091
|
-
);
|
|
28092
|
-
}
|
|
28093
|
-
};
|
|
28094
|
-
await approveConnection();
|
|
28095
|
-
await prepareBackgroundPersistence();
|
|
28096
|
-
const results = [];
|
|
28097
|
-
const initializeAnchor = async () => {
|
|
28098
|
-
const initialized = await initialize(anchorSource, values);
|
|
28099
|
-
results.push(initialized.result);
|
|
28100
|
-
return initialized.config;
|
|
28101
|
-
};
|
|
28102
|
-
let anchor3 = anchorExisting ?? await initializeAnchor();
|
|
28103
|
-
let organizationId = requestedOrganizationId ?? existingScopes[0]?.scope.organizationId ?? anchor3.organizationId ?? null;
|
|
28104
|
-
const connectDetectedSources = async () => {
|
|
28105
|
-
for (const source of detected) {
|
|
28106
|
-
if (source === anchorSource || configs[COLLECTOR_SOURCES2.indexOf(source)] !== null)
|
|
28107
|
-
continue;
|
|
28108
|
-
const paths = collectorPaths(defaultCollectorDirectory(source));
|
|
28109
|
-
await assertFreshCollectorDirectory(paths, source);
|
|
28110
|
-
const state = await prepareSourceInitialState(
|
|
28111
|
-
source,
|
|
28112
|
-
sourceHomes[source],
|
|
28113
|
-
true
|
|
28114
|
-
);
|
|
28115
|
-
const enrollment = await expandSource(anchor3, source, organizationId);
|
|
28116
|
-
const config3 = await activateSource(
|
|
28117
|
-
source,
|
|
28118
|
-
sourceHomes[source],
|
|
28119
|
-
endpoint,
|
|
28120
|
-
enrollment,
|
|
28121
|
-
state
|
|
28122
|
-
);
|
|
28123
|
-
organizationId = enrollment.organizationId;
|
|
28124
|
-
results.push({
|
|
28125
|
-
source,
|
|
28126
|
-
endpoint: config3.endpoint,
|
|
28127
|
-
turnSummary: await setUpTurnSummary(config3)
|
|
28128
|
-
});
|
|
28129
|
-
print(
|
|
28130
|
-
connectedLine(
|
|
28131
|
-
source,
|
|
28132
|
-
sessions.find((item) => item.source === source)?.sessions ?? 0
|
|
28133
|
-
)
|
|
28134
|
-
);
|
|
28135
|
-
}
|
|
28136
|
-
};
|
|
28137
|
-
await connectDetectedSources();
|
|
28138
|
-
if (organizationId === null) {
|
|
28139
|
-
organizationId = (await readCollectorScope(anchor3)).organizationId;
|
|
28140
|
-
}
|
|
28141
|
-
if (anchor3.organizationId === void 0) {
|
|
28142
|
-
anchor3 = { ...anchor3, organizationId };
|
|
28143
|
-
await saveConfig(anchor3, collectorPaths(anchor3.dataDirectory));
|
|
28144
|
-
}
|
|
28145
|
-
await saveMachineConsent({
|
|
28146
|
-
version: 1,
|
|
28147
|
-
endpoint,
|
|
28148
|
-
organizationId,
|
|
28149
|
-
anchorSource,
|
|
28150
|
-
anchorDataDirectory: anchor3.dataDirectory,
|
|
28151
|
-
sourceHomes
|
|
28152
|
-
});
|
|
28153
|
-
for (const item of existing) {
|
|
28154
|
-
await ensureStartableState(
|
|
28155
|
-
item.config,
|
|
28156
|
-
collectorPaths(item.config.dataDirectory)
|
|
28157
|
-
);
|
|
28158
|
-
if ("cursorHome" in item.config)
|
|
28159
|
-
await installCursorHooks(
|
|
28160
|
-
item.config.cursorHome,
|
|
28161
|
-
item.config.dataDirectory
|
|
28162
|
-
);
|
|
28163
|
-
await installBackgroundService(item.config.dataDirectory, item.source);
|
|
28164
|
-
results.push({
|
|
28165
|
-
source: item.source,
|
|
28166
|
-
endpoint: item.config.endpoint,
|
|
28167
|
-
turnSummary: await setUpTurnSummary(item.config)
|
|
28168
|
-
});
|
|
28169
|
-
print(
|
|
28170
|
-
mark(
|
|
28171
|
-
terminal,
|
|
28172
|
-
"success",
|
|
28173
|
-
`${collectorSourceLabel(item.source)} is still connected`
|
|
28174
|
-
)
|
|
28175
|
-
);
|
|
28176
|
-
}
|
|
28177
|
-
print(...connectionSummary2(results, results.length > existing.length));
|
|
29354
|
+
await connectWorkspace(values);
|
|
28178
29355
|
}
|
|
28179
29356
|
async function importHistory(source, values) {
|
|
28180
29357
|
requireCollectorPlatform();
|
|
@@ -28505,6 +29682,14 @@ async function runInternalCommand() {
|
|
|
28505
29682
|
}
|
|
28506
29683
|
return true;
|
|
28507
29684
|
}
|
|
29685
|
+
if (process.argv[2] === "mcp") {
|
|
29686
|
+
if (process.argv.length !== 3) throw new Error("Invalid mcp command.");
|
|
29687
|
+
const configs = await Promise.all(
|
|
29688
|
+
COLLECTOR_SOURCES2.map((source) => connectedConfig(source))
|
|
29689
|
+
);
|
|
29690
|
+
await serveAgentTools(configs.find((config3) => config3 !== null) ?? null);
|
|
29691
|
+
return true;
|
|
29692
|
+
}
|
|
28508
29693
|
if (process.argv[2] === "turn-summary-hook") {
|
|
28509
29694
|
const directory = process.argv[3];
|
|
28510
29695
|
if (!directory || process.argv.length !== 4)
|
|
@@ -28606,6 +29791,7 @@ async function disconnect(selectedSource, values) {
|
|
|
28606
29791
|
if ("cursorHome" in config3)
|
|
28607
29792
|
await uninstallCursorHooks(config3.cursorHome, config3.dataDirectory);
|
|
28608
29793
|
else await uninstallTurnSummary(config3);
|
|
29794
|
+
await uninstallAgentTools(agentHomes(config3));
|
|
28609
29795
|
}
|
|
28610
29796
|
print(
|
|
28611
29797
|
mark(
|
|
@@ -28628,9 +29814,7 @@ async function dispatchSourceCommand(command, source, values, selectedSource) {
|
|
|
28628
29814
|
};
|
|
28629
29815
|
validateSourceOptions();
|
|
28630
29816
|
if (command === "connect" && selectedSource !== null) {
|
|
28631
|
-
|
|
28632
|
-
const { result } = await initialize(selectedSource, values);
|
|
28633
|
-
print(...connectionSummary2([result], true));
|
|
29817
|
+
await connectWorkspace(values, selectedSource);
|
|
28634
29818
|
return;
|
|
28635
29819
|
}
|
|
28636
29820
|
if (command === "run") {
|