iteration-cli 0.14.4 → 0.15.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 +3 -3
- package/dist/cli.js +265 -54
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Iteration CLI (private source)
|
|
2
2
|
|
|
3
|
-
This package owns the `iteration-cli@0.
|
|
3
|
+
This package owns the `iteration-cli@0.15.0` product executable. Clerk sign-in,
|
|
4
4
|
workspace enrollment and automatic expansion, the default Iteration endpoint,
|
|
5
5
|
commands/status UI, history progress, friction summaries and Codex/Cursor billing
|
|
6
6
|
clients live here, not in the public Trace Vault repository.
|
|
@@ -28,9 +28,9 @@ package installs only `iteration`.
|
|
|
28
28
|
From the private repository root after workspace initialization:
|
|
29
29
|
|
|
30
30
|
```sh
|
|
31
|
-
pnpm --filter iteration-cli build
|
|
31
|
+
pnpm --filter "iteration-cli..." --recursive --workspace-concurrency=4 run build
|
|
32
32
|
pnpm --filter iteration-cli pack --pack-destination /tmp/iteration-cli-pack
|
|
33
|
-
node packages/cli/smoke.mjs /tmp/iteration-cli-pack/iteration-cli-0.
|
|
33
|
+
node packages/cli/smoke.mjs /tmp/iteration-cli-pack/iteration-cli-0.15.0.tgz
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
The package smoke installs the tarball offline under a disposable home and checks
|
package/dist/cli.js
CHANGED
|
@@ -7,9 +7,9 @@ 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
|
|
10
|
+
import { promises as fs24 } from "node:fs";
|
|
11
|
+
import os11 from "node:os";
|
|
12
|
+
import path30 from "node:path";
|
|
13
13
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
14
14
|
import { parseArgs } from "node:util";
|
|
15
15
|
|
|
@@ -15850,10 +15850,10 @@ function mergeDefs2(...defs) {
|
|
|
15850
15850
|
function cloneDef2(schema) {
|
|
15851
15851
|
return mergeDefs2(schema._zod.def);
|
|
15852
15852
|
}
|
|
15853
|
-
function getElementAtPath2(obj,
|
|
15854
|
-
if (!
|
|
15853
|
+
function getElementAtPath2(obj, path31) {
|
|
15854
|
+
if (!path31)
|
|
15855
15855
|
return obj;
|
|
15856
|
-
return
|
|
15856
|
+
return path31.reduce((acc, key) => acc?.[key], obj);
|
|
15857
15857
|
}
|
|
15858
15858
|
function promiseAllObject2(promisesObj) {
|
|
15859
15859
|
const keys = Object.keys(promisesObj);
|
|
@@ -16193,11 +16193,11 @@ function explicitlyAborted2(x, startIndex = 0) {
|
|
|
16193
16193
|
}
|
|
16194
16194
|
return false;
|
|
16195
16195
|
}
|
|
16196
|
-
function prefixIssues2(
|
|
16196
|
+
function prefixIssues2(path31, issues) {
|
|
16197
16197
|
return issues.map((iss) => {
|
|
16198
16198
|
var _a5;
|
|
16199
16199
|
(_a5 = iss).path ?? (_a5.path = []);
|
|
16200
|
-
iss.path.unshift(
|
|
16200
|
+
iss.path.unshift(path31);
|
|
16201
16201
|
return iss;
|
|
16202
16202
|
});
|
|
16203
16203
|
}
|
|
@@ -16655,16 +16655,16 @@ function flattenError2(error3, mapper = (issue3) => issue3.message) {
|
|
|
16655
16655
|
}
|
|
16656
16656
|
function formatError2(error3, mapper = (issue3) => issue3.message) {
|
|
16657
16657
|
const fieldErrors = { _errors: [] };
|
|
16658
|
-
const processError = (error4,
|
|
16658
|
+
const processError = (error4, path31 = []) => {
|
|
16659
16659
|
for (const issue3 of error4.issues) {
|
|
16660
16660
|
if (issue3.code === "invalid_union" && issue3.errors.length) {
|
|
16661
|
-
issue3.errors.map((issues) => processError({ issues }, [...
|
|
16661
|
+
issue3.errors.map((issues) => processError({ issues }, [...path31, ...issue3.path]));
|
|
16662
16662
|
} else if (issue3.code === "invalid_key") {
|
|
16663
|
-
processError({ issues: issue3.issues }, [...
|
|
16663
|
+
processError({ issues: issue3.issues }, [...path31, ...issue3.path]);
|
|
16664
16664
|
} else if (issue3.code === "invalid_element") {
|
|
16665
|
-
processError({ issues: issue3.issues }, [...
|
|
16665
|
+
processError({ issues: issue3.issues }, [...path31, ...issue3.path]);
|
|
16666
16666
|
} else {
|
|
16667
|
-
const fullpath = [...
|
|
16667
|
+
const fullpath = [...path31, ...issue3.path];
|
|
16668
16668
|
if (fullpath.length === 0) {
|
|
16669
16669
|
fieldErrors._errors.push(mapper(issue3));
|
|
16670
16670
|
} else {
|
|
@@ -24059,6 +24059,53 @@ async function backgroundServiceDefinition(source) {
|
|
|
24059
24059
|
throw error3;
|
|
24060
24060
|
}
|
|
24061
24061
|
}
|
|
24062
|
+
function decodeXml(value) {
|
|
24063
|
+
return value.replace(/&(amp|lt|gt|quot|apos);/g, (entity) => {
|
|
24064
|
+
const entities = {
|
|
24065
|
+
"&": "&",
|
|
24066
|
+
"<": "<",
|
|
24067
|
+
">": ">",
|
|
24068
|
+
""": '"',
|
|
24069
|
+
"'": "'"
|
|
24070
|
+
};
|
|
24071
|
+
return entities[entity];
|
|
24072
|
+
});
|
|
24073
|
+
}
|
|
24074
|
+
function serviceDataDirectory(definition, platform) {
|
|
24075
|
+
let directory;
|
|
24076
|
+
if (platform === "darwin") {
|
|
24077
|
+
const value = /<string>--data-directory<\/string>\s*<string>([^<]*)<\/string>/.exec(
|
|
24078
|
+
definition
|
|
24079
|
+
)?.[1];
|
|
24080
|
+
if (value !== void 0) directory = decodeXml(value);
|
|
24081
|
+
} else if (platform === "linux") {
|
|
24082
|
+
const value = /^ExecStart=.*?"--data-directory" "((?:\\.|[^"\\])*)"\s*$/m.exec(
|
|
24083
|
+
definition
|
|
24084
|
+
)?.[1];
|
|
24085
|
+
if (value !== void 0)
|
|
24086
|
+
directory = value.replace(/\\([\\"])/g, "$1").replaceAll("%%", "%");
|
|
24087
|
+
} else if (platform === "win32") {
|
|
24088
|
+
const encoded = /-EncodedCommand ([A-Za-z0-9+/=]+)/.exec(definition)?.[1];
|
|
24089
|
+
const script = encoded === void 0 ? "" : Buffer.from(encoded, "base64").toString("utf16le");
|
|
24090
|
+
const args = /\$start\.Arguments = '((?:''|[^'])*)'/.exec(script)?.[1]?.replaceAll("''", "'");
|
|
24091
|
+
const value = /"--data-directory" "(.*)"$/.exec(args ?? "")?.[1];
|
|
24092
|
+
if (value !== void 0)
|
|
24093
|
+
directory = value.replace(
|
|
24094
|
+
/(\\+)"/g,
|
|
24095
|
+
(_, slashes) => `${slashes.slice(0, (slashes.length - 1) / 2)}"`
|
|
24096
|
+
).replace(
|
|
24097
|
+
/(\\+)$/,
|
|
24098
|
+
(_, slashes) => slashes.slice(0, slashes.length / 2)
|
|
24099
|
+
);
|
|
24100
|
+
}
|
|
24101
|
+
if (!directory || !(platform === "win32" ? path20.win32 : path20.posix).isAbsolute(directory))
|
|
24102
|
+
throw new Error("Could not read the collector service data directory.");
|
|
24103
|
+
return directory;
|
|
24104
|
+
}
|
|
24105
|
+
async function backgroundServiceDataDirectory(source) {
|
|
24106
|
+
const definition = await backgroundServiceDefinition(source);
|
|
24107
|
+
return definition === null ? null : serviceDataDirectory(definition, process.platform);
|
|
24108
|
+
}
|
|
24062
24109
|
async function installedServiceSources() {
|
|
24063
24110
|
const definitions = await Promise.all(
|
|
24064
24111
|
COLLECTOR_SOURCES2.map(backgroundServiceDefinition)
|
|
@@ -24713,6 +24760,9 @@ var consentSchema = object3({
|
|
|
24713
24760
|
function consentPath() {
|
|
24714
24761
|
return path24.join(os8.homedir(), ".config", "iteration", "auto-connect.json");
|
|
24715
24762
|
}
|
|
24763
|
+
async function removeMachineConsent() {
|
|
24764
|
+
await fs19.rm(consentPath(), { force: true });
|
|
24765
|
+
}
|
|
24716
24766
|
async function detectedSources(sourceHomes) {
|
|
24717
24767
|
const detected = [];
|
|
24718
24768
|
for (const source of COLLECTOR_SOURCES2) {
|
|
@@ -25289,6 +25339,147 @@ async function importSourceHistory(config3, paths, confirmed, control = backgrou
|
|
|
25289
25339
|
return { imported: true, size, backgroundRunning: wasRunning };
|
|
25290
25340
|
}
|
|
25291
25341
|
|
|
25342
|
+
// src/logout.ts
|
|
25343
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
25344
|
+
import { promises as fs20 } from "node:fs";
|
|
25345
|
+
import os9 from "node:os";
|
|
25346
|
+
import path26 from "node:path";
|
|
25347
|
+
async function pendingDirectories(filename) {
|
|
25348
|
+
let contents;
|
|
25349
|
+
try {
|
|
25350
|
+
contents = await fs20.readFile(filename, "utf8");
|
|
25351
|
+
} catch (error3) {
|
|
25352
|
+
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
25353
|
+
return [];
|
|
25354
|
+
throw error3;
|
|
25355
|
+
}
|
|
25356
|
+
const parsed = array2(string5().refine(path26.isAbsolute)).max(64).safeParse(JSON.parse(contents));
|
|
25357
|
+
if (!parsed.success)
|
|
25358
|
+
throw new Error("Pending logout directories are invalid.");
|
|
25359
|
+
return parsed.data;
|
|
25360
|
+
}
|
|
25361
|
+
async function savePendingDirectories(filename, directories) {
|
|
25362
|
+
await ensurePrivateDirectory(path26.dirname(filename));
|
|
25363
|
+
const temporary = `${filename}.${process.pid}.tmp`;
|
|
25364
|
+
await fs20.writeFile(temporary, JSON.stringify([...directories]), {
|
|
25365
|
+
mode: 384
|
|
25366
|
+
});
|
|
25367
|
+
await fs20.rename(temporary, filename);
|
|
25368
|
+
}
|
|
25369
|
+
async function clearConnection(directory) {
|
|
25370
|
+
const paths = collectorPaths(directory);
|
|
25371
|
+
let names;
|
|
25372
|
+
try {
|
|
25373
|
+
names = await fs20.readdir(directory);
|
|
25374
|
+
} catch (error3) {
|
|
25375
|
+
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
25376
|
+
return;
|
|
25377
|
+
throw error3;
|
|
25378
|
+
}
|
|
25379
|
+
const release = await waitForCollectorLock(paths);
|
|
25380
|
+
try {
|
|
25381
|
+
for (const name of names)
|
|
25382
|
+
if (/^config\.json\.\d+\.tmp$/.test(name))
|
|
25383
|
+
await fs20.rm(path26.join(directory, name), { force: true });
|
|
25384
|
+
if (!names.includes(path26.basename(paths.config))) return;
|
|
25385
|
+
const config3 = await loadConfig(paths);
|
|
25386
|
+
if (config3.destination === "vault")
|
|
25387
|
+
throw new Error("This directory belongs to Trace Vault, not Iteration.");
|
|
25388
|
+
if ("cursorHome" in config3)
|
|
25389
|
+
await uninstallCursorHooks(config3.cursorHome, config3.dataDirectory);
|
|
25390
|
+
else await uninstallTurnSummary(config3);
|
|
25391
|
+
const retained = [
|
|
25392
|
+
paths.state,
|
|
25393
|
+
paths.outbox,
|
|
25394
|
+
paths.redactionMarker,
|
|
25395
|
+
paths.historyBackfill,
|
|
25396
|
+
paths.health,
|
|
25397
|
+
...[
|
|
25398
|
+
"cursor-hooks",
|
|
25399
|
+
"cursor-hook-failure.json",
|
|
25400
|
+
"claude-git",
|
|
25401
|
+
"turn-summary",
|
|
25402
|
+
"codex-billing-sync.json",
|
|
25403
|
+
"cursor-billing-sync.json"
|
|
25404
|
+
].map((name) => path26.join(directory, name))
|
|
25405
|
+
];
|
|
25406
|
+
const archive = path26.join(directory, "logged-out", randomUUID3());
|
|
25407
|
+
await ensurePrivateDirectory(archive);
|
|
25408
|
+
for (const filename of retained) {
|
|
25409
|
+
try {
|
|
25410
|
+
await fs20.rename(filename, path26.join(archive, path26.basename(filename)));
|
|
25411
|
+
} catch (error3) {
|
|
25412
|
+
if (!(error3 instanceof Error && "code" in error3 && error3.code === "ENOENT"))
|
|
25413
|
+
throw error3;
|
|
25414
|
+
}
|
|
25415
|
+
}
|
|
25416
|
+
await fs20.rm(paths.config);
|
|
25417
|
+
} finally {
|
|
25418
|
+
await release();
|
|
25419
|
+
}
|
|
25420
|
+
}
|
|
25421
|
+
async function logout(extraDirectory) {
|
|
25422
|
+
requireCollectorPlatform();
|
|
25423
|
+
const pending = path26.join(
|
|
25424
|
+
os9.homedir(),
|
|
25425
|
+
".config",
|
|
25426
|
+
"iteration",
|
|
25427
|
+
"logout-pending.json"
|
|
25428
|
+
);
|
|
25429
|
+
const directories = /* @__PURE__ */ new Set([
|
|
25430
|
+
...COLLECTOR_SOURCES2.map(defaultCollectorDirectory),
|
|
25431
|
+
...await pendingDirectories(pending)
|
|
25432
|
+
]);
|
|
25433
|
+
if (extraDirectory !== void 0)
|
|
25434
|
+
directories.add(path26.resolve(extraDirectory));
|
|
25435
|
+
const failures = [];
|
|
25436
|
+
try {
|
|
25437
|
+
await removeMachineConsent();
|
|
25438
|
+
} catch (error3) {
|
|
25439
|
+
failures.push(
|
|
25440
|
+
`Automatic connection approval: ${error3 instanceof Error ? error3.message : "Could not remove approval."}`
|
|
25441
|
+
);
|
|
25442
|
+
}
|
|
25443
|
+
const unreadableServices = /* @__PURE__ */ new Set();
|
|
25444
|
+
for (const source of COLLECTOR_SOURCES2) {
|
|
25445
|
+
try {
|
|
25446
|
+
const directory = await backgroundServiceDataDirectory(source);
|
|
25447
|
+
if (directory !== null) directories.add(directory);
|
|
25448
|
+
} catch (error3) {
|
|
25449
|
+
unreadableServices.add(source);
|
|
25450
|
+
failures.push(
|
|
25451
|
+
`${source}: ${error3 instanceof Error ? error3.message : "Could not inspect collector startup."}`
|
|
25452
|
+
);
|
|
25453
|
+
}
|
|
25454
|
+
}
|
|
25455
|
+
await savePendingDirectories(pending, directories);
|
|
25456
|
+
for (const source of COLLECTOR_SOURCES2) {
|
|
25457
|
+
if (unreadableServices.has(source)) continue;
|
|
25458
|
+
try {
|
|
25459
|
+
await uninstallBackgroundService(source);
|
|
25460
|
+
} catch (error3) {
|
|
25461
|
+
failures.push(
|
|
25462
|
+
`${source}: ${error3 instanceof Error ? error3.message : "Could not stop collector startup."}`
|
|
25463
|
+
);
|
|
25464
|
+
}
|
|
25465
|
+
}
|
|
25466
|
+
for (const directory of directories) {
|
|
25467
|
+
try {
|
|
25468
|
+
await clearConnection(directory);
|
|
25469
|
+
} catch (error3) {
|
|
25470
|
+
failures.push(
|
|
25471
|
+
`${directory}: ${error3 instanceof Error ? error3.message : "Could not clear the collector connection."}`
|
|
25472
|
+
);
|
|
25473
|
+
}
|
|
25474
|
+
}
|
|
25475
|
+
if (failures.length > 0)
|
|
25476
|
+
throw new Error(
|
|
25477
|
+
`Logout did not finish. Resolve these errors and run iteration logout again:
|
|
25478
|
+
${failures.join("\n")}`
|
|
25479
|
+
);
|
|
25480
|
+
await fs20.rm(pending);
|
|
25481
|
+
}
|
|
25482
|
+
|
|
25292
25483
|
// src/runtime-services.ts
|
|
25293
25484
|
async function refreshBackgroundServices(restart) {
|
|
25294
25485
|
const refreshed = [];
|
|
@@ -25317,8 +25508,8 @@ import { setTimeout as delay5 } from "node:timers/promises";
|
|
|
25317
25508
|
|
|
25318
25509
|
// src/codex-billing.ts
|
|
25319
25510
|
import { createHash as createHash7 } from "node:crypto";
|
|
25320
|
-
import { promises as
|
|
25321
|
-
import
|
|
25511
|
+
import { promises as fs21 } from "node:fs";
|
|
25512
|
+
import path27 from "node:path";
|
|
25322
25513
|
var TASK_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage/thread_usage/query_v2";
|
|
25323
25514
|
var UUID2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
25324
25515
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -25370,7 +25561,7 @@ function fingerprint(value) {
|
|
|
25370
25561
|
async function readAuth(codexHome) {
|
|
25371
25562
|
let contents;
|
|
25372
25563
|
try {
|
|
25373
|
-
contents = await
|
|
25564
|
+
contents = await fs21.readFile(path27.join(codexHome, "auth.json"), "utf8");
|
|
25374
25565
|
} catch (error3) {
|
|
25375
25566
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
25376
25567
|
return {
|
|
@@ -25405,7 +25596,7 @@ async function readAuth(codexHome) {
|
|
|
25405
25596
|
async function readState(filename) {
|
|
25406
25597
|
let contents;
|
|
25407
25598
|
try {
|
|
25408
|
-
contents = await
|
|
25599
|
+
contents = await fs21.readFile(filename, "utf8");
|
|
25409
25600
|
} catch (error3) {
|
|
25410
25601
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
25411
25602
|
return null;
|
|
@@ -25427,7 +25618,7 @@ async function readState(filename) {
|
|
|
25427
25618
|
}
|
|
25428
25619
|
async function saveState2(filename, state) {
|
|
25429
25620
|
const temporary = `${filename}.${process.pid}.tmp`;
|
|
25430
|
-
const handle = await
|
|
25621
|
+
const handle = await fs21.open(temporary, "w", 384);
|
|
25431
25622
|
try {
|
|
25432
25623
|
await handle.writeFile(`${JSON.stringify(state)}
|
|
25433
25624
|
`);
|
|
@@ -25435,11 +25626,11 @@ async function saveState2(filename, state) {
|
|
|
25435
25626
|
} finally {
|
|
25436
25627
|
await handle.close();
|
|
25437
25628
|
}
|
|
25438
|
-
await
|
|
25629
|
+
await fs21.rename(temporary, filename);
|
|
25439
25630
|
}
|
|
25440
25631
|
async function localThread(filename) {
|
|
25441
|
-
const stat = await
|
|
25442
|
-
const handle = await
|
|
25632
|
+
const stat = await fs21.stat(filename);
|
|
25633
|
+
const handle = await fs21.open(filename, "r");
|
|
25443
25634
|
let header = null;
|
|
25444
25635
|
try {
|
|
25445
25636
|
const buffer = Buffer.alloc(64 * 1024);
|
|
@@ -25539,9 +25730,9 @@ function parseCodexAuthLog(threadId, body) {
|
|
|
25539
25730
|
return { threadId, turnId: turn[1], mode };
|
|
25540
25731
|
}
|
|
25541
25732
|
async function localAuthTurns(codexHome, modelProviders) {
|
|
25542
|
-
const filename =
|
|
25733
|
+
const filename = path27.join(codexHome, "logs_2.sqlite");
|
|
25543
25734
|
try {
|
|
25544
|
-
await
|
|
25735
|
+
await fs21.access(filename);
|
|
25545
25736
|
} catch (error3) {
|
|
25546
25737
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
25547
25738
|
return [];
|
|
@@ -25634,7 +25825,7 @@ async function queryTaskUsage(tasks, accessToken, accountId, signal) {
|
|
|
25634
25825
|
}
|
|
25635
25826
|
async function syncCodexBilling(config3, fetcher = collectorFetch, signal) {
|
|
25636
25827
|
const auth = await readAuth(config3.codexHome);
|
|
25637
|
-
const stateFile =
|
|
25828
|
+
const stateFile = path27.join(config3.dataDirectory, "codex-billing-sync.json");
|
|
25638
25829
|
const prior = await readState(stateFile);
|
|
25639
25830
|
const nowMs = Date.now();
|
|
25640
25831
|
const history = prior === null || prior.accountFingerprint !== auth.accountFingerprint;
|
|
@@ -25805,9 +25996,9 @@ async function syncCodexBilling(config3, fetcher = collectorFetch, signal) {
|
|
|
25805
25996
|
|
|
25806
25997
|
// src/cursor-billing.ts
|
|
25807
25998
|
import { createHash as createHash8 } from "node:crypto";
|
|
25808
|
-
import { promises as
|
|
25809
|
-
import
|
|
25810
|
-
import
|
|
25999
|
+
import { promises as fs22 } from "node:fs";
|
|
26000
|
+
import os10 from "node:os";
|
|
26001
|
+
import path28 from "node:path";
|
|
25811
26002
|
var CURSOR_USAGE_URL = "https://api2.cursor.sh/aiserver.v1.DashboardService/GetFilteredUsageEvents";
|
|
25812
26003
|
var PAGE_SIZE = 500;
|
|
25813
26004
|
var MAX_PAGES = 1e3;
|
|
@@ -25838,18 +26029,18 @@ var usagePageSchema = object3({
|
|
|
25838
26029
|
var emptyUsagePageSchema = object3({}).strict();
|
|
25839
26030
|
function cursorAuthDatabase() {
|
|
25840
26031
|
if (process.platform === "darwin")
|
|
25841
|
-
return
|
|
25842
|
-
|
|
26032
|
+
return path28.join(
|
|
26033
|
+
os10.homedir(),
|
|
25843
26034
|
"Library/Application Support/Cursor/User/globalStorage/state.vscdb"
|
|
25844
26035
|
);
|
|
25845
26036
|
if (process.platform === "linux")
|
|
25846
|
-
return
|
|
25847
|
-
|
|
26037
|
+
return path28.join(
|
|
26038
|
+
os10.homedir(),
|
|
25848
26039
|
".config/Cursor/User/globalStorage/state.vscdb"
|
|
25849
26040
|
);
|
|
25850
26041
|
if (process.platform === "win32")
|
|
25851
|
-
return
|
|
25852
|
-
process.env.APPDATA ??
|
|
26042
|
+
return path28.join(
|
|
26043
|
+
process.env.APPDATA ?? path28.join(os10.homedir(), "AppData", "Roaming"),
|
|
25853
26044
|
"Cursor/User/globalStorage/state.vscdb"
|
|
25854
26045
|
);
|
|
25855
26046
|
return null;
|
|
@@ -25891,7 +26082,7 @@ async function readCursorAuth() {
|
|
|
25891
26082
|
async function loadBillingState(filename) {
|
|
25892
26083
|
let contents;
|
|
25893
26084
|
try {
|
|
25894
|
-
contents = await
|
|
26085
|
+
contents = await fs22.readFile(filename, "utf8");
|
|
25895
26086
|
} catch (error3) {
|
|
25896
26087
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
25897
26088
|
return null;
|
|
@@ -25907,7 +26098,7 @@ async function loadBillingState(filename) {
|
|
|
25907
26098
|
}
|
|
25908
26099
|
async function saveBillingState(filename, state) {
|
|
25909
26100
|
const temporary = `${filename}.${process.pid}.tmp`;
|
|
25910
|
-
const handle = await
|
|
26101
|
+
const handle = await fs22.open(temporary, "w", 384);
|
|
25911
26102
|
try {
|
|
25912
26103
|
await handle.writeFile(`${JSON.stringify(state)}
|
|
25913
26104
|
`, "utf8");
|
|
@@ -25915,7 +26106,7 @@ async function saveBillingState(filename, state) {
|
|
|
25915
26106
|
} finally {
|
|
25916
26107
|
await handle.close();
|
|
25917
26108
|
}
|
|
25918
|
-
await
|
|
26109
|
+
await fs22.rename(temporary, filename);
|
|
25919
26110
|
}
|
|
25920
26111
|
function planCursorBillingSync(state, accountFingerprint, nowMs) {
|
|
25921
26112
|
if (state === null || state.accountFingerprint !== accountFingerprint)
|
|
@@ -25937,7 +26128,7 @@ function planCursorBillingSync(state, accountFingerprint, nowMs) {
|
|
|
25937
26128
|
}
|
|
25938
26129
|
function cursorConversationIds(filenames) {
|
|
25939
26130
|
return new Set(
|
|
25940
|
-
filenames.map((filename) =>
|
|
26131
|
+
filenames.map((filename) => path28.basename(filename, ".jsonl")).filter((id) => UUID3.test(id))
|
|
25941
26132
|
);
|
|
25942
26133
|
}
|
|
25943
26134
|
function billingKind(kind) {
|
|
@@ -25995,11 +26186,11 @@ async function syncCursorBilling(config3, fetcher = collectorFetch, signal) {
|
|
|
25995
26186
|
if (auth === null)
|
|
25996
26187
|
return { sessions: 0, matched: 0, skipped: true, mode: null };
|
|
25997
26188
|
const localConversations = cursorConversationIds(
|
|
25998
|
-
await cursorFiles(
|
|
26189
|
+
await cursorFiles(path28.join(config3.cursorHome, "projects"))
|
|
25999
26190
|
);
|
|
26000
26191
|
if (localConversations.size === 0)
|
|
26001
26192
|
return { sessions: 0, matched: 0, skipped: true, mode: null };
|
|
26002
|
-
const stateFile =
|
|
26193
|
+
const stateFile = path28.join(config3.dataDirectory, "cursor-billing-sync.json");
|
|
26003
26194
|
const priorState = await loadBillingState(stateFile);
|
|
26004
26195
|
const nowMs = Date.now();
|
|
26005
26196
|
const plan = planCursorBillingSync(
|
|
@@ -26804,26 +26995,26 @@ function renderRuntime(summary) {
|
|
|
26804
26995
|
|
|
26805
26996
|
// src/windows-supervisor.ts
|
|
26806
26997
|
import { spawn } from "node:child_process";
|
|
26807
|
-
import { promises as
|
|
26808
|
-
import
|
|
26998
|
+
import { promises as fs23 } from "node:fs";
|
|
26999
|
+
import path29 from "node:path";
|
|
26809
27000
|
import { setTimeout as delay6 } from "node:timers/promises";
|
|
26810
27001
|
var MAX_LOG_BYTES = 8 * 1024 * 1024;
|
|
26811
27002
|
var RESTART_DELAY_MS = 5e3;
|
|
26812
27003
|
async function appendLog(filename, bytes) {
|
|
26813
27004
|
let offset = 0;
|
|
26814
27005
|
while (offset < bytes.length) {
|
|
26815
|
-
const size = await
|
|
27006
|
+
const size = await fs23.stat(filename).then((stat) => stat.size).catch((error3) => {
|
|
26816
27007
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT")
|
|
26817
27008
|
return 0;
|
|
26818
27009
|
throw error3;
|
|
26819
27010
|
});
|
|
26820
27011
|
if (size >= MAX_LOG_BYTES) {
|
|
26821
|
-
await
|
|
26822
|
-
await
|
|
27012
|
+
await fs23.rm(`${filename}.1`, { force: true });
|
|
27013
|
+
await fs23.rename(filename, `${filename}.1`);
|
|
26823
27014
|
continue;
|
|
26824
27015
|
}
|
|
26825
27016
|
const count = Math.min(bytes.length - offset, MAX_LOG_BYTES - size);
|
|
26826
|
-
await
|
|
27017
|
+
await fs23.appendFile(filename, bytes.subarray(offset, offset + count), {
|
|
26827
27018
|
mode: 384
|
|
26828
27019
|
});
|
|
26829
27020
|
offset += count;
|
|
@@ -26839,7 +27030,7 @@ async function copyOutput(stream, filename) {
|
|
|
26839
27030
|
async function superviseCollector(source, dataDirectory2) {
|
|
26840
27031
|
const output = windowsOutputPath(source);
|
|
26841
27032
|
const errors = windowsLogPath(source);
|
|
26842
|
-
await ensurePrivateDirectory(
|
|
27033
|
+
await ensurePrivateDirectory(path29.dirname(output));
|
|
26843
27034
|
const controller = new AbortController();
|
|
26844
27035
|
const stop = () => controller.abort();
|
|
26845
27036
|
process.once("SIGTERM", stop);
|
|
@@ -26950,6 +27141,12 @@ function optionalString(values, name) {
|
|
|
26950
27141
|
return parsed.data;
|
|
26951
27142
|
}
|
|
26952
27143
|
function usage(command) {
|
|
27144
|
+
if (command === "logout") {
|
|
27145
|
+
process.stdout.write(
|
|
27146
|
+
"Sign out of Iteration on this computer\n iteration logout\n\nStops all collectors and removes saved credentials and automatic connection approval.\nLocal pending data is archived and will not be uploaded after reconnecting.\nAgent session files and previously uploaded data are kept.\n\n --data-directory DIR Also clear a custom collector directory with no installed service\n\nRun iteration connect to sign in again.\n"
|
|
27147
|
+
);
|
|
27148
|
+
return;
|
|
27149
|
+
}
|
|
26953
27150
|
if (command === "connect") {
|
|
26954
27151
|
process.stdout.write(
|
|
26955
27152
|
[
|
|
@@ -26986,6 +27183,7 @@ function usage(command) {
|
|
|
26986
27183
|
" iteration status See scan, upload, and error details",
|
|
26987
27184
|
" iteration connect codex|claude|cursor Connect one source (advanced)",
|
|
26988
27185
|
" iteration disconnect codex|claude|cursor Stop a source",
|
|
27186
|
+
" iteration logout Sign out and stop all sources on this computer",
|
|
26989
27187
|
" iteration update Update now (updates also install automatically)",
|
|
26990
27188
|
" iteration update off|on Turn automatic updates off or on",
|
|
26991
27189
|
" iteration version Show the installed version",
|
|
@@ -26996,7 +27194,7 @@ function usage(command) {
|
|
|
26996
27194
|
}
|
|
26997
27195
|
async function fileExists(filename) {
|
|
26998
27196
|
try {
|
|
26999
|
-
await
|
|
27197
|
+
await fs24.access(filename);
|
|
27000
27198
|
return true;
|
|
27001
27199
|
} catch (error3) {
|
|
27002
27200
|
if (error3 instanceof Error && "code" in error3 && error3.code === "ENOENT") {
|
|
@@ -27046,7 +27244,7 @@ ${missing2.map(collectorSourceLabel).join(" and ")} found on this computer but n
|
|
|
27046
27244
|
await connectAll({});
|
|
27047
27245
|
}
|
|
27048
27246
|
function dataDirectory(source, values) {
|
|
27049
|
-
return
|
|
27247
|
+
return path30.resolve(
|
|
27050
27248
|
optionalString(values, "data-directory") ?? defaultCollectorDirectory(source)
|
|
27051
27249
|
);
|
|
27052
27250
|
}
|
|
@@ -27067,12 +27265,12 @@ async function initialize(source, values) {
|
|
|
27067
27265
|
requireCollectorPlatform();
|
|
27068
27266
|
const endpoint = optionalString(values, "endpoint") ?? DEFAULT_INGEST_ENDPOINT;
|
|
27069
27267
|
const endpointUrl = validateCollectorEndpoint(endpoint);
|
|
27070
|
-
const deviceName = optionalString(values, "device-name") ??
|
|
27268
|
+
const deviceName = optionalString(values, "device-name") ?? os11.hostname();
|
|
27071
27269
|
const organizationId = optionalString(values, "workspace");
|
|
27072
27270
|
const directory = dataDirectory(source, values);
|
|
27073
27271
|
const paths = collectorPaths(directory);
|
|
27074
|
-
const selectedHome =
|
|
27075
|
-
source === "codex" ? optionalString(values, "codex-home") ?? process.env.CODEX_HOME ??
|
|
27272
|
+
const selectedHome = path30.resolve(
|
|
27273
|
+
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")
|
|
27076
27274
|
);
|
|
27077
27275
|
await assertFreshCollectorDirectory(paths, source);
|
|
27078
27276
|
const preparedState = await prepareSourceInitialState(
|
|
@@ -27157,13 +27355,13 @@ async function connectAll(values) {
|
|
|
27157
27355
|
}
|
|
27158
27356
|
const configs = await Promise.all(COLLECTOR_SOURCES2.map(connectedConfig));
|
|
27159
27357
|
const resolveSourceHomes = () => ({
|
|
27160
|
-
codex:
|
|
27358
|
+
codex: path30.resolve(
|
|
27161
27359
|
optionalString(values, "codex-home") ?? (configs[0] && "codexHome" in configs[0] ? configs[0].codexHome : sourceHome("codex"))
|
|
27162
27360
|
),
|
|
27163
|
-
claude:
|
|
27361
|
+
claude: path30.resolve(
|
|
27164
27362
|
optionalString(values, "claude-home") ?? (configs[1] && "claudeHome" in configs[1] ? configs[1].claudeHome : sourceHome("claude"))
|
|
27165
27363
|
),
|
|
27166
|
-
cursor:
|
|
27364
|
+
cursor: path30.resolve(
|
|
27167
27365
|
optionalString(values, "cursor-home") ?? (configs[2] && "cursorHome" in configs[2] ? configs[2].cursorHome : sourceHome("cursor"))
|
|
27168
27366
|
)
|
|
27169
27367
|
});
|
|
@@ -27585,6 +27783,9 @@ async function dispatchCommand({
|
|
|
27585
27783
|
if (command === "connect" && source === void 0) {
|
|
27586
27784
|
return connectAll(values);
|
|
27587
27785
|
}
|
|
27786
|
+
if (command === "logout") {
|
|
27787
|
+
return signOut(source, values);
|
|
27788
|
+
}
|
|
27588
27789
|
if (command === "") {
|
|
27589
27790
|
return showHome(values);
|
|
27590
27791
|
}
|
|
@@ -27598,6 +27799,16 @@ async function dispatchCommand({
|
|
|
27598
27799
|
if (command === "update") return update(source, values);
|
|
27599
27800
|
return dispatchSourceCommand(command, source, values, selectedSource);
|
|
27600
27801
|
}
|
|
27802
|
+
async function signOut(source, values) {
|
|
27803
|
+
if (source !== void 0 || Object.keys(values).some((name) => name !== "data-directory"))
|
|
27804
|
+
throw new Error(
|
|
27805
|
+
"Run iteration logout without a source. Only --data-directory is supported."
|
|
27806
|
+
);
|
|
27807
|
+
await logout(optionalString(values, "data-directory") ?? void 0);
|
|
27808
|
+
process.stdout.write(
|
|
27809
|
+
"Signed out of Iteration on this computer. Collection stopped and saved credentials removed.\nLocal pending data was archived. Agent sessions and uploaded data were kept.\nRun iteration connect to sign in again.\n"
|
|
27810
|
+
);
|
|
27811
|
+
}
|
|
27601
27812
|
async function showHome(values) {
|
|
27602
27813
|
if (Object.keys(values).length > 0) {
|
|
27603
27814
|
throw new Error(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iteration-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"esbuild": "0.28.2",
|
|
26
26
|
"zod": "4.6.6",
|
|
27
|
-
"iteration-collector-contracts": "0.1.0",
|
|
28
27
|
"iteration-trace-collector": "0.1.0",
|
|
29
|
-
"iteration-trace-contracts": "0.12.0"
|
|
28
|
+
"iteration-trace-contracts": "0.12.0",
|
|
29
|
+
"iteration-collector-contracts": "0.1.0"
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
|
-
"build": "
|
|
32
|
+
"build": "tsc -p tsconfig.json --noEmit && node build.mjs"
|
|
33
33
|
}
|
|
34
34
|
}
|