iteration-cli 0.12.0 → 0.14.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 +8 -2
- package/dist/cli.js +5657 -4741
- package/dist/cursor-hook-entry.js +72 -5049
- package/dist/launcher.js +169 -34
- package/package.json +2 -2
package/dist/launcher.js
CHANGED
|
@@ -6,13 +6,13 @@ var __export = (target, all) => {
|
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
// src/launcher.ts
|
|
9
|
-
import { promises as
|
|
9
|
+
import { promises as fs3 } from "node:fs";
|
|
10
10
|
import { pathToFileURL } from "node:url";
|
|
11
11
|
|
|
12
12
|
// src/runtime.ts
|
|
13
|
-
import { promises as
|
|
13
|
+
import { promises as fs2 } from "node:fs";
|
|
14
14
|
import os from "node:os";
|
|
15
|
-
import
|
|
15
|
+
import path2 from "node:path";
|
|
16
16
|
import { setTimeout as delay } from "node:timers/promises";
|
|
17
17
|
|
|
18
18
|
// ../../node_modules/.pnpm/zod@4.6.4/node_modules/zod/v4/core/util.js
|
|
@@ -251,10 +251,10 @@ function mergeDefs(...defs) {
|
|
|
251
251
|
function cloneDef(schema) {
|
|
252
252
|
return mergeDefs(schema._zod.def);
|
|
253
253
|
}
|
|
254
|
-
function getElementAtPath(obj,
|
|
255
|
-
if (!
|
|
254
|
+
function getElementAtPath(obj, path3) {
|
|
255
|
+
if (!path3)
|
|
256
256
|
return obj;
|
|
257
|
-
return
|
|
257
|
+
return path3.reduce((acc, key) => acc?.[key], obj);
|
|
258
258
|
}
|
|
259
259
|
function promiseAllObject(promisesObj) {
|
|
260
260
|
const keys = Object.keys(promisesObj);
|
|
@@ -594,11 +594,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
594
594
|
}
|
|
595
595
|
return false;
|
|
596
596
|
}
|
|
597
|
-
function prefixIssues(
|
|
597
|
+
function prefixIssues(path3, issues) {
|
|
598
598
|
return issues.map((iss) => {
|
|
599
599
|
var _a3;
|
|
600
600
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
601
|
-
iss.path.unshift(
|
|
601
|
+
iss.path.unshift(path3);
|
|
602
602
|
return iss;
|
|
603
603
|
});
|
|
604
604
|
}
|
|
@@ -1044,16 +1044,16 @@ function flattenError(error2, mapper = (issue2) => issue2.message) {
|
|
|
1044
1044
|
}
|
|
1045
1045
|
function formatError(error2, mapper = (issue2) => issue2.message) {
|
|
1046
1046
|
const fieldErrors = { _errors: [] };
|
|
1047
|
-
const processError = (error3,
|
|
1047
|
+
const processError = (error3, path3 = []) => {
|
|
1048
1048
|
for (const issue2 of error3.issues) {
|
|
1049
1049
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
1050
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
1050
|
+
issue2.errors.map((issues) => processError({ issues }, [...path3, ...issue2.path]));
|
|
1051
1051
|
} else if (issue2.code === "invalid_key") {
|
|
1052
|
-
processError({ issues: issue2.issues }, [...
|
|
1052
|
+
processError({ issues: issue2.issues }, [...path3, ...issue2.path]);
|
|
1053
1053
|
} else if (issue2.code === "invalid_element") {
|
|
1054
|
-
processError({ issues: issue2.issues }, [...
|
|
1054
|
+
processError({ issues: issue2.issues }, [...path3, ...issue2.path]);
|
|
1055
1055
|
} else {
|
|
1056
|
-
const fullpath = [...
|
|
1056
|
+
const fullpath = [...path3, ...issue2.path];
|
|
1057
1057
|
if (fullpath.length === 0) {
|
|
1058
1058
|
fieldErrors._errors.push(mapper(issue2));
|
|
1059
1059
|
} else {
|
|
@@ -6511,6 +6511,12 @@ var acknowledgementSchema = object({
|
|
|
6511
6511
|
}).strict();
|
|
6512
6512
|
var episodeCountSchema = number2().int().min(0).max(Number.MAX_SAFE_INTEGER);
|
|
6513
6513
|
var collectorFrictionSummarySchema = object({ weekEpisodes: episodeCountSchema, dayEpisodes: episodeCountSchema }).strict().refine((summary) => summary.dayEpisodes <= summary.weekEpisodes, "The last day cannot have more friction episodes than the last week.");
|
|
6514
|
+
var sessionCountSchema = number2().int().min(0).max(1e6);
|
|
6515
|
+
var collectorHistoryProgressSchema = object({
|
|
6516
|
+
sessions: sessionCountSchema,
|
|
6517
|
+
readSessions: sessionCountSchema,
|
|
6518
|
+
complete: boolean2()
|
|
6519
|
+
}).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.");
|
|
6514
6520
|
var collectorEnrollmentRequestSchema = object({
|
|
6515
6521
|
deviceName: string2().trim().min(1).max(160),
|
|
6516
6522
|
organizationId: string2().regex(UUID_PATTERN).optional(),
|
|
@@ -6539,19 +6545,149 @@ var collectorEnrollmentResponseSchema = discriminatedUnion("status", [
|
|
|
6539
6545
|
}).strict()
|
|
6540
6546
|
]);
|
|
6541
6547
|
|
|
6548
|
+
// src/private-storage.ts
|
|
6549
|
+
import { promises as fs } from "node:fs";
|
|
6550
|
+
|
|
6551
|
+
// src/windows-powershell.ts
|
|
6552
|
+
import { execFile as execFileCallback } from "node:child_process";
|
|
6553
|
+
import path from "node:path";
|
|
6554
|
+
import { promisify } from "node:util";
|
|
6555
|
+
var execFile = promisify(execFileCallback);
|
|
6556
|
+
function windowsPowerShellPath() {
|
|
6557
|
+
return path.join(
|
|
6558
|
+
process.env.SystemRoot ?? "C:\\Windows",
|
|
6559
|
+
"System32",
|
|
6560
|
+
"WindowsPowerShell",
|
|
6561
|
+
"v1.0",
|
|
6562
|
+
"powershell.exe"
|
|
6563
|
+
);
|
|
6564
|
+
}
|
|
6565
|
+
function powershellLiteral(value) {
|
|
6566
|
+
return `'${value.replaceAll("'", "''")}'`;
|
|
6567
|
+
}
|
|
6568
|
+
function encodedPowerShell(script) {
|
|
6569
|
+
return Buffer.from(script, "utf16le").toString("base64");
|
|
6570
|
+
}
|
|
6571
|
+
async function runWindowsPowerShell(operation, script) {
|
|
6572
|
+
try {
|
|
6573
|
+
const { stdout } = await execFile(
|
|
6574
|
+
windowsPowerShellPath(),
|
|
6575
|
+
[
|
|
6576
|
+
"-NoLogo",
|
|
6577
|
+
"-NoProfile",
|
|
6578
|
+
"-NonInteractive",
|
|
6579
|
+
"-EncodedCommand",
|
|
6580
|
+
encodedPowerShell(
|
|
6581
|
+
`$ErrorActionPreference = 'Stop'
|
|
6582
|
+
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
|
6583
|
+
${script}`
|
|
6584
|
+
)
|
|
6585
|
+
],
|
|
6586
|
+
{
|
|
6587
|
+
timeout: 3e4,
|
|
6588
|
+
maxBuffer: 1024 * 1024,
|
|
6589
|
+
windowsHide: true
|
|
6590
|
+
}
|
|
6591
|
+
);
|
|
6592
|
+
return stdout.trim();
|
|
6593
|
+
} catch (error2) {
|
|
6594
|
+
const failure2 = object({ stderr: string2() }).safeParse(error2);
|
|
6595
|
+
const detail = failure2.success ? failure2.data.stderr.trim().slice(0, 4096) : "";
|
|
6596
|
+
throw new Error(
|
|
6597
|
+
`Windows could not ${operation}.${detail ? `
|
|
6598
|
+
${detail}` : ""}`,
|
|
6599
|
+
{ cause: error2 }
|
|
6600
|
+
);
|
|
6601
|
+
}
|
|
6602
|
+
}
|
|
6603
|
+
|
|
6604
|
+
// src/private-storage.ts
|
|
6605
|
+
var securedDirectories = /* @__PURE__ */ new Map();
|
|
6606
|
+
var MAX_SECURED_DIRECTORIES = 128;
|
|
6607
|
+
async function secureWindowsDirectory(directory) {
|
|
6608
|
+
await runWindowsPowerShell(
|
|
6609
|
+
"secure private collector storage",
|
|
6610
|
+
`
|
|
6611
|
+
$root = ${powershellLiteral(directory)}
|
|
6612
|
+
$owner = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
|
|
6613
|
+
$principals = @($owner, [System.Security.Principal.SecurityIdentifier]::new('S-1-5-18'), [System.Security.Principal.SecurityIdentifier]::new('S-1-5-32-544'))
|
|
6614
|
+
$pending = [System.Collections.Generic.Stack[string]]::new()
|
|
6615
|
+
$pending.Push($root)
|
|
6616
|
+
$count = 0
|
|
6617
|
+
while ($pending.Count -gt 0) {
|
|
6618
|
+
$entry = $pending.Pop()
|
|
6619
|
+
try {
|
|
6620
|
+
$item = Get-Item -LiteralPath $entry -Force
|
|
6621
|
+
if (($item.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0) { throw 'Private storage cannot contain filesystem links.' }
|
|
6622
|
+
if (++$count -gt 65536) { throw 'Private storage has too many entries to secure.' }
|
|
6623
|
+
if ($item.PSIsContainer) {
|
|
6624
|
+
$acl = [System.Security.AccessControl.DirectorySecurity]::new()
|
|
6625
|
+
$inheritance = [System.Security.AccessControl.InheritanceFlags]'ContainerInherit, ObjectInherit'
|
|
6626
|
+
} else {
|
|
6627
|
+
$acl = [System.Security.AccessControl.FileSecurity]::new()
|
|
6628
|
+
$inheritance = [System.Security.AccessControl.InheritanceFlags]::None
|
|
6629
|
+
}
|
|
6630
|
+
$acl.SetOwner($owner)
|
|
6631
|
+
$acl.SetAccessRuleProtection($true, $false)
|
|
6632
|
+
foreach ($principal in $principals) {
|
|
6633
|
+
$acl.AddAccessRule([System.Security.AccessControl.FileSystemAccessRule]::new($principal, 'FullControl', $inheritance, 'None', 'Allow'))
|
|
6634
|
+
}
|
|
6635
|
+
# Set-Acl may request audit privileges on a protected DACL. Persist only
|
|
6636
|
+
# the changed owner and access rules so repair needs no elevation.
|
|
6637
|
+
$item.SetAccessControl($acl)
|
|
6638
|
+
$actual = Get-Acl -LiteralPath $item.FullName
|
|
6639
|
+
if (!$actual.AreAccessRulesProtected -or $actual.GetOwner([System.Security.Principal.SecurityIdentifier]).Value -ne $owner.Value) { throw 'Private storage ACL verification failed.' }
|
|
6640
|
+
foreach ($rule in $actual.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier])) {
|
|
6641
|
+
if ($rule.AccessControlType -ne 'Allow' -or $principals.Value -notcontains $rule.IdentityReference.Value) { throw 'Private storage contains an unexpected access rule.' }
|
|
6642
|
+
}
|
|
6643
|
+
if ($item.PSIsContainer) {
|
|
6644
|
+
foreach ($child in [System.IO.Directory]::EnumerateFileSystemEntries($item.FullName)) {
|
|
6645
|
+
if ($count + $pending.Count -ge 65536) { throw 'Private storage has too many entries to secure.' }
|
|
6646
|
+
$pending.Push($child)
|
|
6647
|
+
}
|
|
6648
|
+
}
|
|
6649
|
+
} catch [System.Management.Automation.ItemNotFoundException] {
|
|
6650
|
+
if ($entry -eq $root) { throw }
|
|
6651
|
+
}
|
|
6652
|
+
}
|
|
6653
|
+
`
|
|
6654
|
+
);
|
|
6655
|
+
}
|
|
6656
|
+
async function ensurePrivateDirectory(directory) {
|
|
6657
|
+
await fs.mkdir(directory, { recursive: true, mode: 448 });
|
|
6658
|
+
if (process.platform !== "win32") {
|
|
6659
|
+
await fs.chmod(directory, 448);
|
|
6660
|
+
return;
|
|
6661
|
+
}
|
|
6662
|
+
const resolved = await fs.realpath(directory);
|
|
6663
|
+
let secured = securedDirectories.get(resolved);
|
|
6664
|
+
if (secured === void 0) {
|
|
6665
|
+
if (securedDirectories.size >= MAX_SECURED_DIRECTORIES) {
|
|
6666
|
+
const oldest = securedDirectories.keys().next().value;
|
|
6667
|
+
if (oldest !== void 0) securedDirectories.delete(oldest);
|
|
6668
|
+
}
|
|
6669
|
+
secured = secureWindowsDirectory(resolved).catch((error2) => {
|
|
6670
|
+
securedDirectories.delete(resolved);
|
|
6671
|
+
throw error2;
|
|
6672
|
+
});
|
|
6673
|
+
securedDirectories.set(resolved, secured);
|
|
6674
|
+
}
|
|
6675
|
+
await secured;
|
|
6676
|
+
}
|
|
6677
|
+
|
|
6542
6678
|
// src/runtime.ts
|
|
6543
6679
|
function runtimePaths(home = os.homedir()) {
|
|
6544
|
-
const root =
|
|
6680
|
+
const root = path2.join(home, ".config", "iteration", "runtime");
|
|
6545
6681
|
return {
|
|
6546
6682
|
home,
|
|
6547
6683
|
root,
|
|
6548
|
-
versions:
|
|
6549
|
-
staging:
|
|
6550
|
-
state:
|
|
6551
|
-
settings:
|
|
6552
|
-
lock:
|
|
6553
|
-
updateLock:
|
|
6554
|
-
launcher:
|
|
6684
|
+
versions: path2.join(root, "versions"),
|
|
6685
|
+
staging: path2.join(root, "staging"),
|
|
6686
|
+
state: path2.join(root, "state.json"),
|
|
6687
|
+
settings: path2.join(root, "settings.json"),
|
|
6688
|
+
lock: path2.join(root, "state.lock"),
|
|
6689
|
+
updateLock: path2.join(root, "update.lock"),
|
|
6690
|
+
launcher: path2.join(root, "launcher.js")
|
|
6555
6691
|
};
|
|
6556
6692
|
}
|
|
6557
6693
|
var MAX_TRIAL_STARTS = 3;
|
|
@@ -6595,25 +6731,24 @@ function isVersion(value) {
|
|
|
6595
6731
|
}
|
|
6596
6732
|
async function ensureRuntimeDirectory(paths) {
|
|
6597
6733
|
for (const directory of [paths.root, paths.versions, paths.staging]) {
|
|
6598
|
-
await
|
|
6599
|
-
await fs.chmod(directory, 448);
|
|
6734
|
+
await ensurePrivateDirectory(directory);
|
|
6600
6735
|
}
|
|
6601
6736
|
}
|
|
6602
6737
|
async function writeFileAtomic(filename, contents, mode) {
|
|
6603
6738
|
const temporary = `${filename}.${process.pid}.${Date.now()}.tmp`;
|
|
6604
|
-
const handle = await
|
|
6739
|
+
const handle = await fs2.open(temporary, "w", mode);
|
|
6605
6740
|
try {
|
|
6606
6741
|
await handle.writeFile(contents, "utf8");
|
|
6607
6742
|
await handle.sync();
|
|
6608
6743
|
} finally {
|
|
6609
6744
|
await handle.close();
|
|
6610
6745
|
}
|
|
6611
|
-
await
|
|
6746
|
+
await fs2.rename(temporary, filename);
|
|
6612
6747
|
}
|
|
6613
6748
|
async function readRuntimeState(paths) {
|
|
6614
6749
|
let contents;
|
|
6615
6750
|
try {
|
|
6616
|
-
contents = await
|
|
6751
|
+
contents = await fs2.readFile(paths.state, "utf8");
|
|
6617
6752
|
} catch (error2) {
|
|
6618
6753
|
if (errorCode(error2) === "ENOENT") return null;
|
|
6619
6754
|
throw error2;
|
|
@@ -6647,19 +6782,19 @@ var LockBusyError = class extends Error {
|
|
|
6647
6782
|
async function tryLock(filename) {
|
|
6648
6783
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
6649
6784
|
try {
|
|
6650
|
-
const handle = await
|
|
6785
|
+
const handle = await fs2.open(filename, "wx", 384);
|
|
6651
6786
|
await handle.writeFile(`${process.pid}
|
|
6652
6787
|
`, "utf8");
|
|
6653
6788
|
await handle.close();
|
|
6654
6789
|
return async () => {
|
|
6655
|
-
const owner = await
|
|
6790
|
+
const owner = await fs2.readFile(filename, "utf8").catch(() => "");
|
|
6656
6791
|
if (owner.trim() === String(process.pid))
|
|
6657
|
-
await
|
|
6792
|
+
await fs2.rm(filename, { force: true });
|
|
6658
6793
|
};
|
|
6659
6794
|
} catch (error2) {
|
|
6660
6795
|
if (errorCode(error2) !== "EEXIST") throw error2;
|
|
6661
6796
|
const owner = Number(
|
|
6662
|
-
(await
|
|
6797
|
+
(await fs2.readFile(filename, "utf8").catch(() => "")).trim()
|
|
6663
6798
|
);
|
|
6664
6799
|
if (Number.isSafeInteger(owner) && owner > 1) {
|
|
6665
6800
|
try {
|
|
@@ -6669,7 +6804,7 @@ async function tryLock(filename) {
|
|
|
6669
6804
|
if (errorCode(processError) !== "ESRCH") throw processError;
|
|
6670
6805
|
}
|
|
6671
6806
|
}
|
|
6672
|
-
await
|
|
6807
|
+
await fs2.rm(filename, { force: true });
|
|
6673
6808
|
}
|
|
6674
6809
|
}
|
|
6675
6810
|
throw new LockBusyError(`Could not obtain ${filename}.`);
|
|
@@ -6734,10 +6869,10 @@ function launchDecision(state, command, source, nowMs) {
|
|
|
6734
6869
|
function versionDirectory(paths, version2) {
|
|
6735
6870
|
if (!isVersion(version2))
|
|
6736
6871
|
throw new Error(`Invalid Iteration version ${version2}.`);
|
|
6737
|
-
return
|
|
6872
|
+
return path2.join(paths.versions, version2);
|
|
6738
6873
|
}
|
|
6739
6874
|
function versionEntry(paths, version2, entry) {
|
|
6740
|
-
return
|
|
6875
|
+
return path2.join(versionDirectory(paths, version2), "dist", entry);
|
|
6741
6876
|
}
|
|
6742
6877
|
var packageSchema = object({ name: literal("iteration-cli"), version: versionSchema }).loose();
|
|
6743
6878
|
|
|
@@ -6778,7 +6913,7 @@ async function launch() {
|
|
|
6778
6913
|
command === "cursor-hook" ? "cursor-hook-entry.js" : "cli.js"
|
|
6779
6914
|
);
|
|
6780
6915
|
if (command === "launcher-check") {
|
|
6781
|
-
await
|
|
6916
|
+
await fs3.access(entry);
|
|
6782
6917
|
process.stdout.write(`${version2}
|
|
6783
6918
|
`);
|
|
6784
6919
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iteration-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"@secretlint/secretlint-rule-preset-recommend": "13.0.6",
|
|
23
23
|
"esbuild": "0.28.2",
|
|
24
24
|
"zod": "4.6.4",
|
|
25
|
-
"iteration-trace-contracts": "0.
|
|
25
|
+
"iteration-trace-contracts": "0.11.0"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build": "pnpm --filter iteration-trace-contracts build && tsc -p tsconfig.json --noEmit && node build.mjs"
|