@brainervirus/workit-mcp 8.13.0 → 8.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/dist/index.js +643 -390
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -6620,11 +6620,11 @@ var require_dist = __commonJS(function(exports, module) {
|
|
|
6620
6620
|
|
|
6621
6621
|
// packages/workit-mcp/src/index.ts
|
|
6622
6622
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
6623
|
-
import
|
|
6623
|
+
import path23 from "node:path";
|
|
6624
6624
|
|
|
6625
6625
|
// packages/workit-mcp/src/server.ts
|
|
6626
6626
|
import { existsSync as existsSync8, readFileSync as readFileSync6, realpathSync as realpathSync6 } from "node:fs";
|
|
6627
|
-
import
|
|
6627
|
+
import path22 from "node:path";
|
|
6628
6628
|
|
|
6629
6629
|
// node_modules/zod/v4/core/util.js
|
|
6630
6630
|
function getEnumValues(entries) {
|
|
@@ -16576,7 +16576,7 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
|
16576
16576
|
// packages/workit-core/src/core.ts
|
|
16577
16577
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
16578
16578
|
import { existsSync as existsSync7, realpathSync as realpathSync5 } from "node:fs";
|
|
16579
|
-
import
|
|
16579
|
+
import path21 from "node:path";
|
|
16580
16580
|
|
|
16581
16581
|
// packages/workit-core/src/core/task-contract.ts
|
|
16582
16582
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -17831,9 +17831,9 @@ function parseAdvertisedOperation(family, input, host) {
|
|
|
17831
17831
|
});
|
|
17832
17832
|
}
|
|
17833
17833
|
// packages/workit-core/src/core/task-store.ts
|
|
17834
|
-
import { createHash as
|
|
17835
|
-
import * as
|
|
17836
|
-
import
|
|
17834
|
+
import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
|
|
17835
|
+
import * as fs8 from "node:fs";
|
|
17836
|
+
import path8 from "node:path";
|
|
17837
17837
|
|
|
17838
17838
|
// node_modules/@openclaw/fs-safe/dist/errors.js
|
|
17839
17839
|
var OPERATIONAL_CODES = new Set([
|
|
@@ -18436,8 +18436,14 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
18436
18436
|
}
|
|
18437
18437
|
}
|
|
18438
18438
|
}
|
|
18439
|
+
// packages/workit-core/src/host-session.ts
|
|
18440
|
+
import { createHash as createHash3, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
|
|
18441
|
+
import fs5 from "node:fs";
|
|
18442
|
+
import path5 from "node:path";
|
|
18443
|
+
|
|
18439
18444
|
// packages/workit-core/src/core/logger.ts
|
|
18440
18445
|
import os from "node:os";
|
|
18446
|
+
import path3 from "node:path";
|
|
18441
18447
|
var REDACTED = "[REDACTED]";
|
|
18442
18448
|
var SECRET_VALUE = /\b(?:Bearer|Basic|Digest|Token)\s+\S+/gi;
|
|
18443
18449
|
var KEY_EQ_VALUE = /\b([A-Za-z0-9_-]*(?:token|secret|password|passwd|apikey|api[_-]?key|authorization|credential|bearer)[A-Za-z0-9_-]*)([:=]\s*).+/gi;
|
|
@@ -18480,6 +18486,22 @@ var SENSITIVE_WORDS = new Set([
|
|
|
18480
18486
|
]);
|
|
18481
18487
|
var STACK_WORDS = new Set(["stack", "stacktrace", "trace"]);
|
|
18482
18488
|
var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
18489
|
+
var resolveStateDir = () => {
|
|
18490
|
+
const override = process.env.WORKFLOW_TOOLKIT_STATE;
|
|
18491
|
+
if (override)
|
|
18492
|
+
return override;
|
|
18493
|
+
const home = os.homedir();
|
|
18494
|
+
if (process.env.XDG_STATE_HOME)
|
|
18495
|
+
return path3.join(process.env.XDG_STATE_HOME, "workit");
|
|
18496
|
+
switch (os.platform()) {
|
|
18497
|
+
case "darwin":
|
|
18498
|
+
return path3.join(home, "Library", "Application Support", "workit");
|
|
18499
|
+
case "win32":
|
|
18500
|
+
return path3.join(process.env.LOCALAPPDATA ?? path3.join(home, "AppData", "Local"), "workit");
|
|
18501
|
+
default:
|
|
18502
|
+
return path3.join(process.env.HOME ?? home, ".local", "state", "workit");
|
|
18503
|
+
}
|
|
18504
|
+
};
|
|
18483
18505
|
var homePattern = new RegExp(`${escapeRegExp(os.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
|
|
18484
18506
|
var applyPatterns = (value) => {
|
|
18485
18507
|
let out = value;
|
|
@@ -18497,7 +18519,7 @@ var redactSecrets = (value) => applyPatterns(value);
|
|
|
18497
18519
|
import { spawnSync } from "node:child_process";
|
|
18498
18520
|
import { createHash as createHash2 } from "node:crypto";
|
|
18499
18521
|
import fs4 from "node:fs";
|
|
18500
|
-
import
|
|
18522
|
+
import path4 from "node:path";
|
|
18501
18523
|
var NOT_A_REPO = /not a git repository \(or any (of the parent directories|parent up to)/u;
|
|
18502
18524
|
var gitRun = (cwd, args) => {
|
|
18503
18525
|
const run = spawnSync("git", args, {
|
|
@@ -18539,14 +18561,14 @@ var DISCOVERY_ENV = [
|
|
|
18539
18561
|
];
|
|
18540
18562
|
var isGitDir = (dir) => {
|
|
18541
18563
|
try {
|
|
18542
|
-
return fs4.statSync(
|
|
18564
|
+
return fs4.statSync(path4.join(dir, "HEAD")).isFile() && fs4.statSync(path4.join(dir, "objects")).isDirectory() && fs4.statSync(path4.join(dir, "refs")).isDirectory();
|
|
18543
18565
|
} catch {
|
|
18544
18566
|
return false;
|
|
18545
18567
|
}
|
|
18546
18568
|
};
|
|
18547
18569
|
var reftableRefs = (common) => {
|
|
18548
18570
|
try {
|
|
18549
|
-
return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs4.readFileSync(
|
|
18571
|
+
return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs4.readFileSync(path4.join(common, "config"), "utf8"));
|
|
18550
18572
|
} catch {
|
|
18551
18573
|
return false;
|
|
18552
18574
|
}
|
|
@@ -18566,7 +18588,7 @@ function fastGit(root) {
|
|
|
18566
18588
|
if (device !== null && stat.dev !== device)
|
|
18567
18589
|
return { kind: "none" };
|
|
18568
18590
|
device = stat.dev;
|
|
18569
|
-
const dotGit =
|
|
18591
|
+
const dotGit = path4.join(dir, ".git");
|
|
18570
18592
|
let entry = null;
|
|
18571
18593
|
try {
|
|
18572
18594
|
entry = fs4.statSync(dotGit);
|
|
@@ -18587,19 +18609,19 @@ function fastGit(root) {
|
|
|
18587
18609
|
const match = /^gitdir: (.+)$/mu.exec(text);
|
|
18588
18610
|
if (!match)
|
|
18589
18611
|
return null;
|
|
18590
|
-
gitDir =
|
|
18612
|
+
gitDir = path4.resolve(dir, match[1].trim());
|
|
18591
18613
|
} else
|
|
18592
18614
|
return null;
|
|
18593
|
-
if (!fs4.existsSync(
|
|
18615
|
+
if (!fs4.existsSync(path4.join(gitDir, "HEAD")))
|
|
18594
18616
|
return null;
|
|
18595
18617
|
} else if (isGitDir(dir))
|
|
18596
18618
|
gitDir = dir;
|
|
18597
18619
|
if (gitDir) {
|
|
18598
18620
|
let common = gitDir;
|
|
18599
18621
|
try {
|
|
18600
|
-
common =
|
|
18622
|
+
common = path4.resolve(gitDir, fs4.readFileSync(path4.join(gitDir, "commondir"), "utf8").trim());
|
|
18601
18623
|
} catch {}
|
|
18602
|
-
if (!fs4.existsSync(
|
|
18624
|
+
if (!fs4.existsSync(path4.join(common, "objects")))
|
|
18603
18625
|
return null;
|
|
18604
18626
|
return {
|
|
18605
18627
|
kind: "repo",
|
|
@@ -18609,26 +18631,26 @@ function fastGit(root) {
|
|
|
18609
18631
|
reftable: reftableRefs(common)
|
|
18610
18632
|
};
|
|
18611
18633
|
}
|
|
18612
|
-
const parent =
|
|
18634
|
+
const parent = path4.dirname(dir);
|
|
18613
18635
|
if (parent === dir)
|
|
18614
18636
|
return { kind: "none" };
|
|
18615
18637
|
dir = parent;
|
|
18616
18638
|
}
|
|
18617
18639
|
}
|
|
18618
18640
|
function resolveStore(root) {
|
|
18619
|
-
root =
|
|
18641
|
+
root = path4.resolve(root);
|
|
18620
18642
|
let directory = false;
|
|
18621
18643
|
try {
|
|
18622
18644
|
directory = fs4.statSync(root).isDirectory();
|
|
18623
18645
|
} catch {}
|
|
18624
18646
|
if (!directory)
|
|
18625
|
-
return { dir:
|
|
18647
|
+
return { dir: path4.join(root, ".workit"), shared: false, top: null };
|
|
18626
18648
|
const fast = fastGit(root);
|
|
18627
18649
|
if (fast?.kind === "none")
|
|
18628
|
-
return { dir:
|
|
18650
|
+
return { dir: path4.join(root, ".workit"), shared: false, top: null };
|
|
18629
18651
|
if (fast?.kind === "repo")
|
|
18630
18652
|
return {
|
|
18631
|
-
dir:
|
|
18653
|
+
dir: path4.join(fast.common, "workit"),
|
|
18632
18654
|
shared: true,
|
|
18633
18655
|
top: fast.top,
|
|
18634
18656
|
git: { gitDir: fast.gitDir, top: fast.top, reftable: fast.reftable }
|
|
@@ -18638,20 +18660,20 @@ function resolveStore(root) {
|
|
|
18638
18660
|
if (run.ok && common) {
|
|
18639
18661
|
const top = gitRun(root, ["rev-parse", "--path-format=absolute", "--show-toplevel"]);
|
|
18640
18662
|
return {
|
|
18641
|
-
dir:
|
|
18663
|
+
dir: path4.join(real(path4.resolve(root, common)), "workit"),
|
|
18642
18664
|
shared: true,
|
|
18643
18665
|
top: top.ok && top.stdout.trim() ? real(top.stdout.trim()) : null
|
|
18644
18666
|
};
|
|
18645
18667
|
}
|
|
18646
18668
|
if (run.missing || !run.ok && NOT_A_REPO.test(run.stderr))
|
|
18647
|
-
return { dir:
|
|
18669
|
+
return { dir: path4.join(root, ".workit"), shared: false, top: null };
|
|
18648
18670
|
return new Error(`cannot locate the workit store: git rev-parse failed (${run.stderr.trim().split(`
|
|
18649
18671
|
`)[0] || "no output"}); fix the repository (git status should work here)`);
|
|
18650
18672
|
}
|
|
18651
18673
|
var rebasingBranch = (gitDir) => {
|
|
18652
18674
|
for (const state of ["rebase-merge", "rebase-apply"]) {
|
|
18653
18675
|
try {
|
|
18654
|
-
const ref = fs4.readFileSync(
|
|
18676
|
+
const ref = fs4.readFileSync(path4.join(gitDir, state, "head-name"), "utf8").trim();
|
|
18655
18677
|
const match = /^refs\/heads\/(.+)$/u.exec(ref);
|
|
18656
18678
|
if (match)
|
|
18657
18679
|
return match[1];
|
|
@@ -18666,7 +18688,7 @@ function resolveTaskKey(root, location) {
|
|
|
18666
18688
|
if (location.git && !location.git.reftable) {
|
|
18667
18689
|
let head = "";
|
|
18668
18690
|
try {
|
|
18669
|
-
head = fs4.readFileSync(
|
|
18691
|
+
head = fs4.readFileSync(path4.join(location.git.gitDir, "HEAD"), "utf8").trim();
|
|
18670
18692
|
} catch {}
|
|
18671
18693
|
const ref = /^ref: refs\/heads\/(.+)$/u.exec(head);
|
|
18672
18694
|
if (ref && ref[1] !== ".invalid")
|
|
@@ -18697,7 +18719,7 @@ function resolveTaskKey(root, location) {
|
|
|
18697
18719
|
return { key: `detached-${shortHash(real(worktree))}`, kind: "detached", branch: null };
|
|
18698
18720
|
}
|
|
18699
18721
|
function checkoutRootOf(root, location) {
|
|
18700
|
-
const resolved =
|
|
18722
|
+
const resolved = path4.resolve(root);
|
|
18701
18723
|
let canonical = resolved;
|
|
18702
18724
|
try {
|
|
18703
18725
|
canonical = fs4.realpathSync(resolved);
|
|
@@ -18728,13 +18750,13 @@ function branchRenames(location) {
|
|
|
18728
18750
|
}
|
|
18729
18751
|
for (const entry of entries)
|
|
18730
18752
|
if (entry.isDirectory())
|
|
18731
|
-
walk(
|
|
18753
|
+
walk(path4.join(dir, entry.name));
|
|
18732
18754
|
else
|
|
18733
|
-
files.push(
|
|
18755
|
+
files.push(path4.join(dir, entry.name));
|
|
18734
18756
|
};
|
|
18735
|
-
walk(
|
|
18757
|
+
walk(path4.join(path4.dirname(location.dir), "logs", "refs", "heads"));
|
|
18736
18758
|
if (location.git)
|
|
18737
|
-
files.push(
|
|
18759
|
+
files.push(path4.join(location.git.gitDir, "logs", "HEAD"));
|
|
18738
18760
|
const seen = new Set;
|
|
18739
18761
|
const renames = [];
|
|
18740
18762
|
for (const file of files) {
|
|
@@ -18769,7 +18791,7 @@ var checkoutSlug = (root) => {
|
|
|
18769
18791
|
let canonical = real(root);
|
|
18770
18792
|
if (process.platform === "win32")
|
|
18771
18793
|
canonical = canonical.toLowerCase();
|
|
18772
|
-
const base =
|
|
18794
|
+
const base = path4.basename(canonical).replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 40) || "root";
|
|
18773
18795
|
return `${base}-${shortHash(canonical)}`;
|
|
18774
18796
|
};
|
|
18775
18797
|
|
|
@@ -18801,30 +18823,113 @@ function hostSessionFromEnv(env) {
|
|
|
18801
18823
|
}
|
|
18802
18824
|
return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
|
|
18803
18825
|
}
|
|
18826
|
+
var hash = (value) => createHash3("sha256").update(value).digest("hex").slice(0, 32);
|
|
18827
|
+
var userIssuedRoot = () => path5.join(resolveStateDir(), "issued");
|
|
18828
|
+
var userTokenRoot = () => path5.join(resolveStateDir(), "issued-tokens");
|
|
18829
|
+
function migrateRepoIssued(cwd) {
|
|
18830
|
+
if (cwd === undefined)
|
|
18831
|
+
return;
|
|
18832
|
+
let old;
|
|
18833
|
+
try {
|
|
18834
|
+
const location = resolveStore(cwd);
|
|
18835
|
+
if (location instanceof Error)
|
|
18836
|
+
return;
|
|
18837
|
+
old = path5.join(location.dir, "hooks", "issued");
|
|
18838
|
+
if (!fs5.existsSync(old))
|
|
18839
|
+
return;
|
|
18840
|
+
} catch {
|
|
18841
|
+
return;
|
|
18842
|
+
}
|
|
18843
|
+
for (const lead of safeList(old)) {
|
|
18844
|
+
try {
|
|
18845
|
+
const from = path5.join(old, lead);
|
|
18846
|
+
if (!fs5.statSync(from).isDirectory())
|
|
18847
|
+
continue;
|
|
18848
|
+
const to = path5.join(userIssuedRoot(), lead);
|
|
18849
|
+
fs5.mkdirSync(to, { recursive: true, mode: 448 });
|
|
18850
|
+
for (const id of fs5.readdirSync(from)) {
|
|
18851
|
+
try {
|
|
18852
|
+
fs5.copyFileSync(path5.join(from, id), path5.join(to, id), fs5.constants.COPYFILE_EXCL);
|
|
18853
|
+
} catch (error) {
|
|
18854
|
+
if (error.code !== "EEXIST")
|
|
18855
|
+
throw error;
|
|
18856
|
+
}
|
|
18857
|
+
fs5.rmSync(path5.join(from, id), { force: true });
|
|
18858
|
+
}
|
|
18859
|
+
removeIfEmpty(from);
|
|
18860
|
+
} catch {}
|
|
18861
|
+
}
|
|
18862
|
+
removeIfEmpty(old);
|
|
18863
|
+
}
|
|
18864
|
+
var safeList = (dir) => {
|
|
18865
|
+
try {
|
|
18866
|
+
return fs5.readdirSync(dir);
|
|
18867
|
+
} catch {
|
|
18868
|
+
return [];
|
|
18869
|
+
}
|
|
18870
|
+
};
|
|
18871
|
+
var removeIfEmpty = (dir) => {
|
|
18872
|
+
try {
|
|
18873
|
+
fs5.rmdirSync(dir);
|
|
18874
|
+
} catch {}
|
|
18875
|
+
};
|
|
18876
|
+
var issuedRecord = (cwd, claimed) => {
|
|
18877
|
+
const cut = claimed.lastIndexOf(":");
|
|
18878
|
+
if (cut <= 0)
|
|
18879
|
+
return null;
|
|
18880
|
+
migrateRepoIssued(cwd);
|
|
18881
|
+
try {
|
|
18882
|
+
return fs5.readFileSync(path5.join(userIssuedRoot(), hash(claimed.slice(0, cut)), hash(claimed)), "utf8");
|
|
18883
|
+
} catch {
|
|
18884
|
+
return null;
|
|
18885
|
+
}
|
|
18886
|
+
};
|
|
18887
|
+
var TOKEN_HASH = /^([0-9a-f]{64})$/mu;
|
|
18888
|
+
var issuedToken = (claimed) => {
|
|
18889
|
+
const cut = claimed.lastIndexOf(":");
|
|
18890
|
+
if (cut <= 0)
|
|
18891
|
+
return { stored: null, leadBound: false };
|
|
18892
|
+
const dir = path5.join(userTokenRoot(), hash(claimed.slice(0, cut)));
|
|
18893
|
+
try {
|
|
18894
|
+
const text = fs5.readFileSync(path5.join(dir, hash(claimed)), "utf8");
|
|
18895
|
+
return { stored: TOKEN_HASH.exec(text)?.[1] ?? null, leadBound: true };
|
|
18896
|
+
} catch {
|
|
18897
|
+
return { stored: null, leadBound: safeList(dir).length > 0 };
|
|
18898
|
+
}
|
|
18899
|
+
};
|
|
18900
|
+
function issuedBinding(cwd, claimed) {
|
|
18901
|
+
const record = issuedRecord(cwd, claimed);
|
|
18902
|
+
const { stored, leadBound } = issuedToken(claimed);
|
|
18903
|
+
if (stored)
|
|
18904
|
+
return "token";
|
|
18905
|
+
if (record === null)
|
|
18906
|
+
return null;
|
|
18907
|
+
return leadBound ? "token" : "legacy";
|
|
18908
|
+
}
|
|
18804
18909
|
var OWN_CHILD_SHELL = new Set(["opencode", "pi"]);
|
|
18805
18910
|
|
|
18806
18911
|
// packages/workit-core/src/core/package-root.ts
|
|
18807
18912
|
import { existsSync } from "node:fs";
|
|
18808
|
-
import
|
|
18913
|
+
import path6 from "node:path";
|
|
18809
18914
|
import { fileURLToPath } from "node:url";
|
|
18810
18915
|
var packageRoot = () => {
|
|
18811
|
-
let dir =
|
|
18916
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
18812
18917
|
while (true) {
|
|
18813
|
-
const parent =
|
|
18814
|
-
if (existsSync(
|
|
18918
|
+
const parent = path6.dirname(dir);
|
|
18919
|
+
if (existsSync(path6.join(dir, "package.json")) || parent === dir)
|
|
18815
18920
|
return dir;
|
|
18816
18921
|
dir = parent;
|
|
18817
18922
|
}
|
|
18818
18923
|
};
|
|
18819
18924
|
var assetRoot = () => {
|
|
18820
18925
|
const root = packageRoot();
|
|
18821
|
-
const assets =
|
|
18926
|
+
const assets = path6.join(root, "assets");
|
|
18822
18927
|
return existsSync(assets) ? assets : root;
|
|
18823
18928
|
};
|
|
18824
18929
|
|
|
18825
18930
|
// packages/workit-core/src/core/store-lock.ts
|
|
18826
18931
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
18827
|
-
import
|
|
18932
|
+
import fs6 from "node:fs";
|
|
18828
18933
|
import { hostname } from "node:os";
|
|
18829
18934
|
var metadataLockSchema = object2({
|
|
18830
18935
|
pid: number2().int().nonnegative().safe(),
|
|
@@ -18836,8 +18941,10 @@ var metadataLockSchema = object2({
|
|
|
18836
18941
|
var FOREIGN_LOCK_TTL_MS = 10 * 60000;
|
|
18837
18942
|
var UNREADABLE_LOCK_TTL_MS = 30000;
|
|
18838
18943
|
var RECLAIM_GUARD_TTL_MS = 30000;
|
|
18839
|
-
var
|
|
18944
|
+
var IN_PROCESS_LOCK_TIMEOUT_MS = 250;
|
|
18945
|
+
var defaultLockTimeoutMs = IN_PROCESS_LOCK_TIMEOUT_MS;
|
|
18840
18946
|
var defaultLockTimeout = () => defaultLockTimeoutMs;
|
|
18947
|
+
var hostAwareLockTimeout = (cliMs) => defaultLockTimeoutMs > IN_PROCESS_LOCK_TIMEOUT_MS ? cliMs : IN_PROCESS_LOCK_TIMEOUT_MS;
|
|
18841
18948
|
var parseMetadataLock = (raw) => {
|
|
18842
18949
|
let value;
|
|
18843
18950
|
try {
|
|
@@ -18866,7 +18973,7 @@ var parseProcStatStart = (stat) => {
|
|
|
18866
18973
|
var processStartOf = (pid) => {
|
|
18867
18974
|
if (process.platform === "linux") {
|
|
18868
18975
|
try {
|
|
18869
|
-
return parseProcStatStart(
|
|
18976
|
+
return parseProcStatStart(fs6.readFileSync(`/proc/${pid}/stat`, "utf8"));
|
|
18870
18977
|
} catch {
|
|
18871
18978
|
return null;
|
|
18872
18979
|
}
|
|
@@ -18887,7 +18994,7 @@ var processStartOf = (pid) => {
|
|
|
18887
18994
|
};
|
|
18888
18995
|
var readTrimmed = (file) => {
|
|
18889
18996
|
try {
|
|
18890
|
-
return
|
|
18997
|
+
return fs6.readFileSync(file, "utf8").trim() || null;
|
|
18891
18998
|
} catch {
|
|
18892
18999
|
return null;
|
|
18893
19000
|
}
|
|
@@ -18898,7 +19005,7 @@ var localLockHost = () => {
|
|
|
18898
19005
|
return cachedLockHost;
|
|
18899
19006
|
let pidns = null;
|
|
18900
19007
|
try {
|
|
18901
|
-
pidns = /\[(\d+)\]/.exec(
|
|
19008
|
+
pidns = /\[(\d+)\]/.exec(fs6.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
|
|
18902
19009
|
} catch {}
|
|
18903
19010
|
const boot = readTrimmed("/proc/sys/kernel/random/boot_id");
|
|
18904
19011
|
cachedLockHost = pidns || boot ? `${hostname()}#${pidns ?? "?"}:${boot ?? "?"}` : hostname();
|
|
@@ -18944,7 +19051,7 @@ var classifyLockOwner = (payload, ageMs, localHost = localLockHost(), options =
|
|
|
18944
19051
|
};
|
|
18945
19052
|
var ageOf = (file, nowMs) => {
|
|
18946
19053
|
try {
|
|
18947
|
-
return nowMs -
|
|
19054
|
+
return nowMs - fs6.lstatSync(file).mtimeMs;
|
|
18948
19055
|
} catch {
|
|
18949
19056
|
return null;
|
|
18950
19057
|
}
|
|
@@ -18955,7 +19062,7 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
|
|
|
18955
19062
|
if (age === null || age <= RECLAIM_GUARD_TTL_MS)
|
|
18956
19063
|
return false;
|
|
18957
19064
|
try {
|
|
18958
|
-
|
|
19065
|
+
fs6.rmdirSync(guard);
|
|
18959
19066
|
return true;
|
|
18960
19067
|
} catch {
|
|
18961
19068
|
return false;
|
|
@@ -18964,8 +19071,8 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
|
|
|
18964
19071
|
|
|
18965
19072
|
// packages/workit-core/src/store/event-log.ts
|
|
18966
19073
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
18967
|
-
import
|
|
18968
|
-
import
|
|
19074
|
+
import fs7 from "node:fs";
|
|
19075
|
+
import path7 from "node:path";
|
|
18969
19076
|
var EVENT_VERSION = 1;
|
|
18970
19077
|
|
|
18971
19078
|
class LogDamage extends Error {
|
|
@@ -18994,7 +19101,7 @@ var parseEvent = (line, file, offset) => {
|
|
|
18994
19101
|
function readLog(file, from = 0, expectSeq = null) {
|
|
18995
19102
|
let fd;
|
|
18996
19103
|
try {
|
|
18997
|
-
fd =
|
|
19104
|
+
fd = fs7.openSync(file, "r");
|
|
18998
19105
|
} catch (error) {
|
|
18999
19106
|
if (error.code === "ENOENT")
|
|
19000
19107
|
return null;
|
|
@@ -19003,13 +19110,13 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
19003
19110
|
let bytes;
|
|
19004
19111
|
let size;
|
|
19005
19112
|
try {
|
|
19006
|
-
size =
|
|
19113
|
+
size = fs7.fstatSync(fd).size;
|
|
19007
19114
|
if (from > size)
|
|
19008
19115
|
throw new LogDamage(`${file}: shorter than its snapshot`);
|
|
19009
19116
|
bytes = Buffer.alloc(size - from);
|
|
19010
19117
|
let read = 0;
|
|
19011
19118
|
while (read < bytes.length) {
|
|
19012
|
-
const got =
|
|
19119
|
+
const got = fs7.readSync(fd, bytes, read, bytes.length - read, from + read);
|
|
19013
19120
|
if (got === 0)
|
|
19014
19121
|
break;
|
|
19015
19122
|
read += got;
|
|
@@ -19017,7 +19124,7 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
19017
19124
|
bytes = bytes.subarray(0, read);
|
|
19018
19125
|
size = from + read;
|
|
19019
19126
|
} finally {
|
|
19020
|
-
|
|
19127
|
+
fs7.closeSync(fd);
|
|
19021
19128
|
}
|
|
19022
19129
|
const events = [];
|
|
19023
19130
|
let start = 0;
|
|
@@ -19042,11 +19149,11 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
19042
19149
|
}
|
|
19043
19150
|
var fsyncDirectory = (dir) => {
|
|
19044
19151
|
try {
|
|
19045
|
-
const fd =
|
|
19152
|
+
const fd = fs7.openSync(dir, "r");
|
|
19046
19153
|
try {
|
|
19047
|
-
|
|
19154
|
+
fs7.fsyncSync(fd);
|
|
19048
19155
|
} finally {
|
|
19049
|
-
|
|
19156
|
+
fs7.closeSync(fd);
|
|
19050
19157
|
}
|
|
19051
19158
|
} catch (error) {
|
|
19052
19159
|
if (process.platform !== "win32")
|
|
@@ -19056,46 +19163,46 @@ var fsyncDirectory = (dir) => {
|
|
|
19056
19163
|
function appendEvent(file, event, validEnd) {
|
|
19057
19164
|
const line = Buffer.from(`${JSON.stringify(event)}
|
|
19058
19165
|
`, "utf8");
|
|
19059
|
-
const created = !
|
|
19060
|
-
if (!created &&
|
|
19061
|
-
const cut =
|
|
19166
|
+
const created = !fs7.existsSync(file);
|
|
19167
|
+
if (!created && fs7.statSync(file).size !== validEnd) {
|
|
19168
|
+
const cut = fs7.openSync(file, "r+");
|
|
19062
19169
|
try {
|
|
19063
|
-
|
|
19064
|
-
|
|
19170
|
+
fs7.ftruncateSync(cut, validEnd);
|
|
19171
|
+
fs7.fsyncSync(cut);
|
|
19065
19172
|
} finally {
|
|
19066
|
-
|
|
19173
|
+
fs7.closeSync(cut);
|
|
19067
19174
|
}
|
|
19068
19175
|
}
|
|
19069
|
-
const fd =
|
|
19176
|
+
const fd = fs7.openSync(file, "a", 384);
|
|
19070
19177
|
try {
|
|
19071
19178
|
let written = 0;
|
|
19072
19179
|
while (written < line.length)
|
|
19073
|
-
written +=
|
|
19074
|
-
|
|
19180
|
+
written += fs7.writeSync(fd, line, written, line.length - written);
|
|
19181
|
+
fs7.fsyncSync(fd);
|
|
19075
19182
|
} finally {
|
|
19076
|
-
|
|
19183
|
+
fs7.closeSync(fd);
|
|
19077
19184
|
}
|
|
19078
19185
|
if (created)
|
|
19079
|
-
fsyncDirectory(
|
|
19186
|
+
fsyncDirectory(path7.dirname(file));
|
|
19080
19187
|
return validEnd;
|
|
19081
19188
|
}
|
|
19082
19189
|
function replaceFile(file, bytes, durable) {
|
|
19083
19190
|
const temporary = `${file}.${process.pid}.${randomUUID2()}.tmp`;
|
|
19084
19191
|
try {
|
|
19085
|
-
const fd =
|
|
19192
|
+
const fd = fs7.openSync(temporary, "wx", 384);
|
|
19086
19193
|
try {
|
|
19087
|
-
|
|
19194
|
+
fs7.writeSync(fd, bytes);
|
|
19088
19195
|
if (durable)
|
|
19089
|
-
|
|
19196
|
+
fs7.fsyncSync(fd);
|
|
19090
19197
|
} finally {
|
|
19091
|
-
|
|
19198
|
+
fs7.closeSync(fd);
|
|
19092
19199
|
}
|
|
19093
19200
|
renameWithRetry(temporary, file);
|
|
19094
19201
|
if (durable)
|
|
19095
|
-
fsyncDirectory(
|
|
19202
|
+
fsyncDirectory(path7.dirname(file));
|
|
19096
19203
|
} catch (error) {
|
|
19097
19204
|
try {
|
|
19098
|
-
|
|
19205
|
+
fs7.unlinkSync(temporary);
|
|
19099
19206
|
} catch {}
|
|
19100
19207
|
throw error;
|
|
19101
19208
|
}
|
|
@@ -19120,7 +19227,7 @@ var retryTransient = (run) => {
|
|
|
19120
19227
|
}
|
|
19121
19228
|
}
|
|
19122
19229
|
};
|
|
19123
|
-
var renameWithRetry = (from, to) => retryTransient(() =>
|
|
19230
|
+
var renameWithRetry = (from, to) => retryTransient(() => fs7.renameSync(from, to));
|
|
19124
19231
|
|
|
19125
19232
|
// packages/workit-core/src/store/patch.ts
|
|
19126
19233
|
var isPlainObject3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -19233,7 +19340,7 @@ var isPatch = (value) => Array.isArray(value) && value.every((op) => {
|
|
|
19233
19340
|
});
|
|
19234
19341
|
|
|
19235
19342
|
// packages/workit-core/src/store/reduce.ts
|
|
19236
|
-
import { createHash as
|
|
19343
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
19237
19344
|
var OPENING_TYPES = new Set([
|
|
19238
19345
|
"task.opened",
|
|
19239
19346
|
"task.imported",
|
|
@@ -19262,7 +19369,7 @@ var isObject4 = (value) => typeof value === "object" && value !== null && !Array
|
|
|
19262
19369
|
var isKey = (value) => isObject4(value) && typeof value.key === "string" && (value.kind === "branch" || value.kind === "detached" || value.kind === "dir") && (value.branch === null || typeof value.branch === "string");
|
|
19263
19370
|
var BLOB_REF = "$blob";
|
|
19264
19371
|
var blobRef = (value) => isObject4(value) && Object.keys(value).length === 1 && typeof value[BLOB_REF] === "string" && /^[0-9a-f]{64}$/.test(value[BLOB_REF]) ? value[BLOB_REF] : null;
|
|
19265
|
-
var blobDigest = (value) =>
|
|
19372
|
+
var blobDigest = (value) => createHash4("sha256").update(JSON.stringify(value)).digest("hex");
|
|
19266
19373
|
var toBlob = (value, blobs) => {
|
|
19267
19374
|
if (!isObject4(value))
|
|
19268
19375
|
return value;
|
|
@@ -19417,7 +19524,7 @@ var indexEntry = (task, key, legacy, file) => {
|
|
|
19417
19524
|
};
|
|
19418
19525
|
var fileSignature = (file) => {
|
|
19419
19526
|
try {
|
|
19420
|
-
const stat =
|
|
19527
|
+
const stat = fs8.statSync(file, { bigint: true });
|
|
19421
19528
|
return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
|
|
19422
19529
|
} catch {
|
|
19423
19530
|
return null;
|
|
@@ -19435,7 +19542,7 @@ var cachedRuntimeVersion = null;
|
|
|
19435
19542
|
var runtimeVersion = () => {
|
|
19436
19543
|
if (cachedRuntimeVersion === null) {
|
|
19437
19544
|
try {
|
|
19438
|
-
const pkg = JSON.parse(
|
|
19545
|
+
const pkg = JSON.parse(fs8.readFileSync(path8.join(packageRoot(), "package.json"), "utf8"));
|
|
19439
19546
|
cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
|
|
19440
19547
|
} catch {
|
|
19441
19548
|
cachedRuntimeVersion = "0.0.0";
|
|
@@ -19461,14 +19568,14 @@ var isNewerVersion = (candidate, current) => {
|
|
|
19461
19568
|
var validId = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(value);
|
|
19462
19569
|
var readText = (file) => {
|
|
19463
19570
|
try {
|
|
19464
|
-
return
|
|
19571
|
+
return fs8.readFileSync(file, "utf8");
|
|
19465
19572
|
} catch {
|
|
19466
19573
|
return "";
|
|
19467
19574
|
}
|
|
19468
19575
|
};
|
|
19469
19576
|
var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
|
|
19470
19577
|
var branchStart = (location, branch, aliases) => {
|
|
19471
|
-
const first = readText(
|
|
19578
|
+
const first = readText(path8.join(path8.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
|
|
19472
19579
|
`, 1)[0];
|
|
19473
19580
|
const created = REFLOG_LINE.exec(first);
|
|
19474
19581
|
const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
|
|
@@ -19478,7 +19585,7 @@ var branchStart = (location, branch, aliases) => {
|
|
|
19478
19585
|
return start;
|
|
19479
19586
|
if (!location.git)
|
|
19480
19587
|
return null;
|
|
19481
|
-
for (const line of readText(
|
|
19588
|
+
for (const line of readText(path8.join(location.git.gitDir, "logs", "HEAD")).split(`
|
|
19482
19589
|
`)) {
|
|
19483
19590
|
const entry = REFLOG_LINE.exec(line);
|
|
19484
19591
|
const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
|
|
@@ -19488,23 +19595,23 @@ var branchStart = (location, branch, aliases) => {
|
|
|
19488
19595
|
}
|
|
19489
19596
|
return null;
|
|
19490
19597
|
};
|
|
19491
|
-
var refExists = (common, ref) =>
|
|
19598
|
+
var refExists = (common, ref) => fs8.existsSync(path8.join(common, ref)) || readText(path8.join(common, "packed-refs")).includes(` ${ref}
|
|
19492
19599
|
`);
|
|
19493
19600
|
var origins = new Map;
|
|
19494
19601
|
var sameDirectoryIdentity = (left, right) => {
|
|
19495
|
-
if (!
|
|
19602
|
+
if (!path8.isAbsolute(left) || !path8.isAbsolute(right))
|
|
19496
19603
|
return false;
|
|
19497
|
-
const normalizedLeft =
|
|
19498
|
-
const normalizedRight =
|
|
19604
|
+
const normalizedLeft = path8.resolve(left);
|
|
19605
|
+
const normalizedRight = path8.resolve(right);
|
|
19499
19606
|
try {
|
|
19500
|
-
const a =
|
|
19501
|
-
const b =
|
|
19607
|
+
const a = fs8.statSync(normalizedLeft, { bigint: true });
|
|
19608
|
+
const b = fs8.statSync(normalizedRight, { bigint: true });
|
|
19502
19609
|
if (!a.isDirectory() || !b.isDirectory())
|
|
19503
19610
|
return false;
|
|
19504
19611
|
if (process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight)
|
|
19505
19612
|
return true;
|
|
19506
|
-
const canonicalLeft =
|
|
19507
|
-
const canonicalRight =
|
|
19613
|
+
const canonicalLeft = fs8.realpathSync(normalizedLeft);
|
|
19614
|
+
const canonicalRight = fs8.realpathSync(normalizedRight);
|
|
19508
19615
|
if (process.platform === "win32" ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase() : canonicalLeft === canonicalRight)
|
|
19509
19616
|
return true;
|
|
19510
19617
|
return a.dev === b.dev && a.ino !== 0n && a.ino === b.ino;
|
|
@@ -19541,8 +19648,8 @@ class TaskStore {
|
|
|
19541
19648
|
migrationChecked = false;
|
|
19542
19649
|
compactDue = new Set;
|
|
19543
19650
|
constructor(root, options = {}) {
|
|
19544
|
-
const resolved =
|
|
19545
|
-
this.origin =
|
|
19651
|
+
const resolved = path8.resolve(root);
|
|
19652
|
+
this.origin = fs8.existsSync(resolved) ? fs8.realpathSync(resolved) : resolved;
|
|
19546
19653
|
this.located = resolveStore(this.origin);
|
|
19547
19654
|
this.root = checkoutRootOf(this.origin, this.located);
|
|
19548
19655
|
this.lockTimeoutMs = options.lockTimeoutMs ?? defaultLockTimeout();
|
|
@@ -19550,7 +19657,7 @@ class TaskStore {
|
|
|
19550
19657
|
this.migrateOnRead = options.migrateOnRead;
|
|
19551
19658
|
}
|
|
19552
19659
|
location() {
|
|
19553
|
-
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared &&
|
|
19660
|
+
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs8.existsSync(path8.join(this.root, ".git"))) {
|
|
19554
19661
|
this.located = undefined;
|
|
19555
19662
|
this.migrationChecked = false;
|
|
19556
19663
|
}
|
|
@@ -19614,7 +19721,7 @@ class TaskStore {
|
|
|
19614
19721
|
const tasksDir = this.paths().tasks;
|
|
19615
19722
|
let names;
|
|
19616
19723
|
try {
|
|
19617
|
-
names =
|
|
19724
|
+
names = fs8.existsSync(tasksDir) ? fs8.readdirSync(tasksDir).filter((name) => validId(name)) : [];
|
|
19618
19725
|
} catch (error) {
|
|
19619
19726
|
return failure2("storage_error", `unable to list tasks: ${String(error)}`, {
|
|
19620
19727
|
path: tasksDir
|
|
@@ -19655,7 +19762,7 @@ class TaskStore {
|
|
|
19655
19762
|
const file = this.paths().workspace;
|
|
19656
19763
|
let bytes;
|
|
19657
19764
|
try {
|
|
19658
|
-
bytes = retryTransient(() =>
|
|
19765
|
+
bytes = retryTransient(() => fs8.readFileSync(file, "utf8"));
|
|
19659
19766
|
} catch (error) {
|
|
19660
19767
|
if (error.code === "ENOENT")
|
|
19661
19768
|
return success(null, null, null);
|
|
@@ -19706,7 +19813,7 @@ class TaskStore {
|
|
|
19706
19813
|
const moved = this.bindLocked(again.data.id, key, input.now);
|
|
19707
19814
|
return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
|
|
19708
19815
|
}
|
|
19709
|
-
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" :
|
|
19816
|
+
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path8.basename(this.root) || this.root;
|
|
19710
19817
|
const intent = {
|
|
19711
19818
|
objective: input.objective?.trim() || `Work on ${label}`,
|
|
19712
19819
|
scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
|
|
@@ -19766,14 +19873,14 @@ class TaskStore {
|
|
|
19766
19873
|
const branch = key.ok ? key.data.branch : null;
|
|
19767
19874
|
if (!branch || !location.ok)
|
|
19768
19875
|
return success(null, null, null);
|
|
19769
|
-
const common =
|
|
19770
|
-
const heads =
|
|
19771
|
-
const headLog = location.data.git ?
|
|
19876
|
+
const common = path8.dirname(location.data.dir);
|
|
19877
|
+
const heads = path8.join(common, "logs", "refs", "heads");
|
|
19878
|
+
const headLog = location.data.git ? path8.join(location.data.git.gitDir, "logs", "HEAD") : "";
|
|
19772
19879
|
const signatures = [
|
|
19773
|
-
|
|
19880
|
+
path8.join(heads, branch),
|
|
19774
19881
|
headLog,
|
|
19775
19882
|
heads,
|
|
19776
|
-
|
|
19883
|
+
path8.join(common, "packed-refs")
|
|
19777
19884
|
].map((file) => fileSignature(file) ?? "-");
|
|
19778
19885
|
const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
|
|
19779
19886
|
let resolved = origins.get(slot);
|
|
@@ -19785,7 +19892,7 @@ class TaskStore {
|
|
|
19785
19892
|
aliases.push(rename.from);
|
|
19786
19893
|
const start = branchStart(location.data, branch, aliases);
|
|
19787
19894
|
const slash = start?.indexOf("/") ?? -1;
|
|
19788
|
-
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(
|
|
19895
|
+
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path8.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
|
|
19789
19896
|
const name = start && (remote ? start.slice(slash + 1) : start);
|
|
19790
19897
|
resolved = {
|
|
19791
19898
|
signature: signatures.join("|"),
|
|
@@ -20023,13 +20130,13 @@ class TaskStore {
|
|
|
20023
20130
|
if (!ready.ok)
|
|
20024
20131
|
return ready;
|
|
20025
20132
|
const dirs = this.paths();
|
|
20026
|
-
const recovery =
|
|
20027
|
-
if (
|
|
20133
|
+
const recovery = path8.join(this.root, ".workit", "recovery");
|
|
20134
|
+
if (fs8.existsSync(recovery)) {
|
|
20028
20135
|
let files = 0;
|
|
20029
20136
|
let bytes = 0;
|
|
20030
20137
|
try {
|
|
20031
|
-
for (const name of
|
|
20032
|
-
const stat =
|
|
20138
|
+
for (const name of fs8.readdirSync(recovery)) {
|
|
20139
|
+
const stat = fs8.lstatSync(path8.join(recovery, name));
|
|
20033
20140
|
if (!stat.isFile())
|
|
20034
20141
|
continue;
|
|
20035
20142
|
files += 1;
|
|
@@ -20038,17 +20145,17 @@ class TaskStore {
|
|
|
20038
20145
|
} catch {}
|
|
20039
20146
|
const remove = options.pruneRecovery === true && !dryRun;
|
|
20040
20147
|
if (remove)
|
|
20041
|
-
|
|
20148
|
+
fs8.rmSync(recovery, { recursive: true, force: true });
|
|
20042
20149
|
report.legacyRecovery = { path: recovery, files, bytes, removed: remove };
|
|
20043
20150
|
}
|
|
20044
|
-
if (!
|
|
20151
|
+
if (!fs8.existsSync(dirs.tasks))
|
|
20045
20152
|
return success(null, null, report);
|
|
20046
|
-
const names =
|
|
20153
|
+
const names = fs8.readdirSync(dirs.tasks).filter((name) => validId(name));
|
|
20047
20154
|
for (const name of names) {
|
|
20048
20155
|
const file = this.eventsPath(name);
|
|
20049
20156
|
let size = 0;
|
|
20050
20157
|
try {
|
|
20051
|
-
size =
|
|
20158
|
+
size = fs8.statSync(file).size;
|
|
20052
20159
|
} catch {
|
|
20053
20160
|
continue;
|
|
20054
20161
|
}
|
|
@@ -20076,16 +20183,16 @@ class TaskStore {
|
|
|
20076
20183
|
const references = new Set;
|
|
20077
20184
|
for (const name of names)
|
|
20078
20185
|
try {
|
|
20079
|
-
for (const match of
|
|
20186
|
+
for (const match of fs8.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
|
|
20080
20187
|
references.add(match[1]);
|
|
20081
20188
|
} catch {}
|
|
20082
|
-
if (
|
|
20083
|
-
for (const name of
|
|
20189
|
+
if (fs8.existsSync(dirs.blobs))
|
|
20190
|
+
for (const name of fs8.readdirSync(dirs.blobs)) {
|
|
20084
20191
|
const digest = name.replace(/\.json$/, "");
|
|
20085
|
-
const file =
|
|
20192
|
+
const file = path8.join(dirs.blobs, name);
|
|
20086
20193
|
let stat;
|
|
20087
20194
|
try {
|
|
20088
|
-
stat =
|
|
20195
|
+
stat = fs8.lstatSync(file);
|
|
20089
20196
|
} catch {
|
|
20090
20197
|
continue;
|
|
20091
20198
|
}
|
|
@@ -20096,11 +20203,11 @@ class TaskStore {
|
|
|
20096
20203
|
report.blobs.removed += 1;
|
|
20097
20204
|
report.blobs.removedBytes += stat.size;
|
|
20098
20205
|
if (!dryRun)
|
|
20099
|
-
|
|
20206
|
+
fs8.rmSync(file, { force: true });
|
|
20100
20207
|
}
|
|
20101
20208
|
return success(null, null, null);
|
|
20102
20209
|
};
|
|
20103
|
-
const swept = dryRun ? sweep() : this.withLock(
|
|
20210
|
+
const swept = dryRun ? sweep() : this.withLock(path8.join(dirs.dir, "gc.lock"), sweep);
|
|
20104
20211
|
if (!swept.ok)
|
|
20105
20212
|
return swept;
|
|
20106
20213
|
const temporaryDirs = [
|
|
@@ -20108,17 +20215,17 @@ class TaskStore {
|
|
|
20108
20215
|
dirs.tasks,
|
|
20109
20216
|
dirs.blobs,
|
|
20110
20217
|
dirs.checkout,
|
|
20111
|
-
...names.map((name) =>
|
|
20218
|
+
...names.map((name) => path8.join(dirs.tasks, name))
|
|
20112
20219
|
];
|
|
20113
20220
|
for (const directory of temporaryDirs) {
|
|
20114
|
-
if (!
|
|
20221
|
+
if (!fs8.existsSync(directory))
|
|
20115
20222
|
continue;
|
|
20116
|
-
for (const name of
|
|
20223
|
+
for (const name of fs8.readdirSync(directory)) {
|
|
20117
20224
|
if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
|
|
20118
20225
|
continue;
|
|
20119
|
-
const file =
|
|
20226
|
+
const file = path8.join(directory, name);
|
|
20120
20227
|
try {
|
|
20121
|
-
const stat =
|
|
20228
|
+
const stat = fs8.lstatSync(file);
|
|
20122
20229
|
if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
|
|
20123
20230
|
continue;
|
|
20124
20231
|
} catch {
|
|
@@ -20126,37 +20233,37 @@ class TaskStore {
|
|
|
20126
20233
|
}
|
|
20127
20234
|
report.temporary.removed += 1;
|
|
20128
20235
|
if (!dryRun)
|
|
20129
|
-
|
|
20236
|
+
fs8.rmSync(file, { force: true });
|
|
20130
20237
|
}
|
|
20131
20238
|
}
|
|
20132
20239
|
return success(null, null, report);
|
|
20133
20240
|
}
|
|
20134
20241
|
paths() {
|
|
20135
20242
|
const location = this.location();
|
|
20136
|
-
const dir = location.ok ? location.data.dir :
|
|
20137
|
-
const checkout =
|
|
20243
|
+
const dir = location.ok ? location.data.dir : path8.join(this.root, ".workit");
|
|
20244
|
+
const checkout = path8.join(dir, "checkouts", checkoutSlug(this.root));
|
|
20138
20245
|
return {
|
|
20139
20246
|
dir,
|
|
20140
20247
|
shared: location.ok ? location.data.shared : false,
|
|
20141
|
-
marker:
|
|
20248
|
+
marker: path8.join(dir, "store.json"),
|
|
20142
20249
|
checkout,
|
|
20143
|
-
workspace:
|
|
20144
|
-
checkoutLock:
|
|
20145
|
-
tasks:
|
|
20146
|
-
index:
|
|
20147
|
-
blobs:
|
|
20148
|
-
legacy:
|
|
20149
|
-
v2:
|
|
20250
|
+
workspace: path8.join(checkout, "workspace.json"),
|
|
20251
|
+
checkoutLock: path8.join(checkout, "metadata.lock"),
|
|
20252
|
+
tasks: path8.join(dir, "tasks"),
|
|
20253
|
+
index: path8.join(dir, "task-index.json"),
|
|
20254
|
+
blobs: path8.join(dir, "blobs", "candidates"),
|
|
20255
|
+
legacy: path8.join(dir, "legacy", checkoutSlug(this.root), "v2"),
|
|
20256
|
+
v2: path8.join(this.root, ".workit")
|
|
20150
20257
|
};
|
|
20151
20258
|
}
|
|
20152
20259
|
eventsPath(taskId) {
|
|
20153
|
-
return
|
|
20260
|
+
return path8.join(this.paths().tasks, taskId, "events.jsonl");
|
|
20154
20261
|
}
|
|
20155
20262
|
snapshotPath(taskId) {
|
|
20156
|
-
return
|
|
20263
|
+
return path8.join(this.paths().tasks, taskId, "snapshot.json");
|
|
20157
20264
|
}
|
|
20158
20265
|
taskLockPath(taskId) {
|
|
20159
|
-
return
|
|
20266
|
+
return path8.join(this.paths().tasks, taskId, "lock");
|
|
20160
20267
|
}
|
|
20161
20268
|
ready(write = false) {
|
|
20162
20269
|
const location = this.location();
|
|
@@ -20166,7 +20273,7 @@ class TaskStore {
|
|
|
20166
20273
|
return success(null, null, null);
|
|
20167
20274
|
const dirs = this.paths();
|
|
20168
20275
|
try {
|
|
20169
|
-
const marker = JSON.parse(
|
|
20276
|
+
const marker = JSON.parse(fs8.readFileSync(dirs.marker, "utf8"));
|
|
20170
20277
|
if (typeof marker.version === "number" && marker.version > STORE_FORMAT)
|
|
20171
20278
|
return failure2("recovery_required", `the workit store at ${dirs.dir} has format ${marker.version}; upgrade Workit to use it`, { path: dirs.marker });
|
|
20172
20279
|
} catch {}
|
|
@@ -20195,21 +20302,21 @@ class TaskStore {
|
|
|
20195
20302
|
return {
|
|
20196
20303
|
read: (digest) => {
|
|
20197
20304
|
try {
|
|
20198
|
-
return JSON.parse(
|
|
20305
|
+
return JSON.parse(fs8.readFileSync(path8.join(dir, `${digest}.json`), "utf8"));
|
|
20199
20306
|
} catch (error) {
|
|
20200
20307
|
throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
|
|
20201
20308
|
}
|
|
20202
20309
|
},
|
|
20203
20310
|
write: (digest, value) => {
|
|
20204
|
-
const file =
|
|
20205
|
-
if (
|
|
20311
|
+
const file = path8.join(dir, `${digest}.json`);
|
|
20312
|
+
if (fs8.existsSync(file)) {
|
|
20206
20313
|
const stamp = new Date;
|
|
20207
20314
|
try {
|
|
20208
|
-
|
|
20315
|
+
fs8.utimesSync(file, stamp, stamp);
|
|
20209
20316
|
return;
|
|
20210
20317
|
} catch {}
|
|
20211
20318
|
}
|
|
20212
|
-
|
|
20319
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
20213
20320
|
replaceFile(file, JSON.stringify(value), true);
|
|
20214
20321
|
}
|
|
20215
20322
|
};
|
|
@@ -20291,7 +20398,7 @@ class TaskStore {
|
|
|
20291
20398
|
}
|
|
20292
20399
|
readSnapshot(taskId) {
|
|
20293
20400
|
try {
|
|
20294
|
-
const value = JSON.parse(
|
|
20401
|
+
const value = JSON.parse(fs8.readFileSync(this.snapshotPath(taskId), "utf8"));
|
|
20295
20402
|
if (!isObject5(value) || value.v !== 1 || value.task !== taskId || typeof value.seq !== "number" || typeof value.lastId !== "string" || typeof value.lastStart !== "number" || typeof value.end !== "number" || !isObject5(value.record))
|
|
20296
20403
|
return null;
|
|
20297
20404
|
return value;
|
|
@@ -20393,8 +20500,8 @@ class TaskStore {
|
|
|
20393
20500
|
}
|
|
20394
20501
|
}
|
|
20395
20502
|
openTask(record, type, key, legacy = null) {
|
|
20396
|
-
const dir =
|
|
20397
|
-
|
|
20503
|
+
const dir = path8.join(this.paths().tasks, record.id);
|
|
20504
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
20398
20505
|
const blobs = this.blobStore();
|
|
20399
20506
|
const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
|
|
20400
20507
|
const event = this.event(record.id, 1, type, {
|
|
@@ -20446,11 +20553,11 @@ class TaskStore {
|
|
|
20446
20553
|
} catch {}
|
|
20447
20554
|
if (!latest || latest.events[0]?.id !== last.id)
|
|
20448
20555
|
return failure2("busy", "the task log changed during compaction; retry", { taskId });
|
|
20449
|
-
const appended =
|
|
20556
|
+
const appended = fs8.readFileSync(file).subarray(log.end, latest.end);
|
|
20450
20557
|
const bytes = Buffer.concat([Buffer.from(prefix, "utf8"), appended]);
|
|
20451
20558
|
replaceFile(file, bytes.toString("utf8"), true);
|
|
20452
20559
|
try {
|
|
20453
|
-
|
|
20560
|
+
fs8.rmSync(this.snapshotPath(taskId), { force: true });
|
|
20454
20561
|
} catch {}
|
|
20455
20562
|
const reloaded = this.load(taskId);
|
|
20456
20563
|
if (reloaded.ok && reloaded.data)
|
|
@@ -20463,7 +20570,7 @@ class TaskStore {
|
|
|
20463
20570
|
}
|
|
20464
20571
|
countEvents(file) {
|
|
20465
20572
|
try {
|
|
20466
|
-
const bytes =
|
|
20573
|
+
const bytes = fs8.readFileSync(file);
|
|
20467
20574
|
let count = 0;
|
|
20468
20575
|
for (let index = bytes.indexOf(10);index >= 0; index = bytes.indexOf(10, index + 1))
|
|
20469
20576
|
count += 1;
|
|
@@ -20485,8 +20592,8 @@ class TaskStore {
|
|
|
20485
20592
|
return bound.ok ? success(null, null, bound.data[0] ?? null) : bound;
|
|
20486
20593
|
}
|
|
20487
20594
|
withKeyLock(key, operation) {
|
|
20488
|
-
const digest =
|
|
20489
|
-
return this.withLock(
|
|
20595
|
+
const digest = createHash5("sha256").update(key).digest("hex").slice(0, 32);
|
|
20596
|
+
return this.withLock(path8.join(this.paths().dir, "keys", `${digest}.lock`), operation);
|
|
20490
20597
|
}
|
|
20491
20598
|
bindLocked(taskId, key, timestamp) {
|
|
20492
20599
|
const current = this.readWorkspace();
|
|
@@ -20627,7 +20734,7 @@ class TaskStore {
|
|
|
20627
20734
|
writeWorkspace(record) {
|
|
20628
20735
|
const file = this.paths().workspace;
|
|
20629
20736
|
try {
|
|
20630
|
-
|
|
20737
|
+
fs8.mkdirSync(path8.dirname(file), { recursive: true });
|
|
20631
20738
|
replaceFile(file, `${canonicalJson(record)}
|
|
20632
20739
|
`, true);
|
|
20633
20740
|
return success(null, null, null);
|
|
@@ -20637,7 +20744,7 @@ class TaskStore {
|
|
|
20637
20744
|
}
|
|
20638
20745
|
readIndex() {
|
|
20639
20746
|
try {
|
|
20640
|
-
const value = JSON.parse(
|
|
20747
|
+
const value = JSON.parse(fs8.readFileSync(this.paths().index, "utf8"));
|
|
20641
20748
|
if (!isObject5(value) || value.version !== INDEX_VERSION || !isObject5(value.tasks))
|
|
20642
20749
|
return {};
|
|
20643
20750
|
const tasks = {};
|
|
@@ -20651,7 +20758,7 @@ class TaskStore {
|
|
|
20651
20758
|
}
|
|
20652
20759
|
writeIndex(entries) {
|
|
20653
20760
|
try {
|
|
20654
|
-
|
|
20761
|
+
fs8.mkdirSync(this.paths().tasks, { recursive: true });
|
|
20655
20762
|
replaceFile(this.paths().index, JSON.stringify({
|
|
20656
20763
|
version: INDEX_VERSION,
|
|
20657
20764
|
tasks: Object.fromEntries(entries.map((entry) => [entry.id, entry]))
|
|
@@ -20705,23 +20812,23 @@ class TaskStore {
|
|
|
20705
20812
|
return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
|
|
20706
20813
|
}
|
|
20707
20814
|
v2Dirs() {
|
|
20708
|
-
return [...new Set([this.root, this.origin])].map((dir) =>
|
|
20815
|
+
return [...new Set([this.root, this.origin])].map((dir) => path8.join(dir, ".workit"));
|
|
20709
20816
|
}
|
|
20710
20817
|
pendingMigrations() {
|
|
20711
20818
|
return this.v2Dirs().filter((dir) => {
|
|
20712
|
-
const raw = this.readV2Workspace(
|
|
20819
|
+
const raw = this.readV2Workspace(path8.join(dir, "workspace.json"));
|
|
20713
20820
|
return raw !== null && raw !== TOMBSTONE;
|
|
20714
20821
|
});
|
|
20715
20822
|
}
|
|
20716
20823
|
migrateV2(v2) {
|
|
20717
20824
|
const dirs = this.paths();
|
|
20718
|
-
const v2Workspace =
|
|
20719
|
-
const v2Tasks =
|
|
20720
|
-
const legacy =
|
|
20825
|
+
const v2Workspace = path8.join(v2, "workspace.json");
|
|
20826
|
+
const v2Tasks = path8.join(v2, "tasks");
|
|
20827
|
+
const legacy = path8.join(dirs.dir, "legacy", checkoutSlug(path8.dirname(v2)), "v2");
|
|
20721
20828
|
const raw = this.readV2Workspace(v2Workspace);
|
|
20722
20829
|
if (raw === null || raw === TOMBSTONE)
|
|
20723
20830
|
return success(null, null, null);
|
|
20724
|
-
return this.withLock(dirs.checkoutLock, () => this.withLock(
|
|
20831
|
+
return this.withLock(dirs.checkoutLock, () => this.withLock(path8.join(v2, "metadata.lock"), () => {
|
|
20725
20832
|
const workspace = this.readV2Workspace(v2Workspace);
|
|
20726
20833
|
if (workspace === null || workspace === TOMBSTONE)
|
|
20727
20834
|
return success(null, null, null);
|
|
@@ -20730,7 +20837,7 @@ class TaskStore {
|
|
|
20730
20837
|
return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
|
|
20731
20838
|
let files = [];
|
|
20732
20839
|
try {
|
|
20733
|
-
files =
|
|
20840
|
+
files = fs8.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path8.join(v2Tasks, name)));
|
|
20734
20841
|
} catch {}
|
|
20735
20842
|
const current = this.readWorkspace();
|
|
20736
20843
|
if (!current.ok)
|
|
@@ -20748,22 +20855,22 @@ class TaskStore {
|
|
|
20748
20855
|
let skipped = 0;
|
|
20749
20856
|
const done = new Map;
|
|
20750
20857
|
try {
|
|
20751
|
-
|
|
20858
|
+
fs8.mkdirSync(path8.join(legacy, "tasks"), { recursive: true });
|
|
20752
20859
|
for (const name of ["workspace.json", "index.json"])
|
|
20753
|
-
if (
|
|
20754
|
-
|
|
20860
|
+
if (fs8.existsSync(path8.join(v2, name)))
|
|
20861
|
+
fs8.copyFileSync(path8.join(v2, name), path8.join(legacy, name));
|
|
20755
20862
|
for (const name of files) {
|
|
20756
20863
|
const id = name.slice(0, -5);
|
|
20757
|
-
const file =
|
|
20758
|
-
const bytes =
|
|
20864
|
+
const file = path8.join(v2Tasks, name);
|
|
20865
|
+
const bytes = fs8.readFileSync(file, "utf8");
|
|
20759
20866
|
const digest = sha256(bytes);
|
|
20760
|
-
|
|
20867
|
+
fs8.writeFileSync(path8.join(legacy, "tasks", name), bytes);
|
|
20761
20868
|
const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
|
|
20762
20869
|
if (!parsed.ok || parsed.data.id !== id)
|
|
20763
20870
|
return failure2("recovery_required", `cannot migrate 2.x task ${id}: ${parsed.ok ? "file name and record ID differ" : parsed.error}; move ${file} aside to continue`, { path: file });
|
|
20764
20871
|
const record = { ...parsed.data, workspaceId: target.id };
|
|
20765
|
-
const origin = { path:
|
|
20766
|
-
if (!
|
|
20872
|
+
const origin = { path: path8.join(legacy, "tasks", name), digest };
|
|
20873
|
+
if (!fs8.existsSync(this.eventsPath(id))) {
|
|
20767
20874
|
this.openTask(record, "migrated.from_v2", null, origin);
|
|
20768
20875
|
migrated += 1;
|
|
20769
20876
|
} else {
|
|
@@ -20798,8 +20905,8 @@ class TaskStore {
|
|
|
20798
20905
|
try {
|
|
20799
20906
|
let complete = true;
|
|
20800
20907
|
for (const [name, digest] of done) {
|
|
20801
|
-
const file =
|
|
20802
|
-
if (sha256(
|
|
20908
|
+
const file = path8.join(v2Tasks, name);
|
|
20909
|
+
if (sha256(fs8.readFileSync(file, "utf8")) !== digest) {
|
|
20803
20910
|
complete = false;
|
|
20804
20911
|
continue;
|
|
20805
20912
|
}
|
|
@@ -20809,7 +20916,7 @@ class TaskStore {
|
|
|
20809
20916
|
return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
|
|
20810
20917
|
path: v2
|
|
20811
20918
|
});
|
|
20812
|
-
|
|
20919
|
+
fs8.rmSync(path8.join(v2, "index.json"), { force: true });
|
|
20813
20920
|
this.writeTombstone(v2, target.id);
|
|
20814
20921
|
} catch (error) {
|
|
20815
20922
|
return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
|
|
@@ -20829,45 +20936,45 @@ class TaskStore {
|
|
|
20829
20936
|
}
|
|
20830
20937
|
adoptLocalStore() {
|
|
20831
20938
|
const dirs = this.paths();
|
|
20832
|
-
const marker =
|
|
20833
|
-
if (!dirs.shared || !
|
|
20939
|
+
const marker = path8.join(dirs.v2, "store.json");
|
|
20940
|
+
if (!dirs.shared || !fs8.existsSync(marker))
|
|
20834
20941
|
return success(null, null, null);
|
|
20835
20942
|
return this.withLock(dirs.checkoutLock, () => {
|
|
20836
|
-
if (!
|
|
20943
|
+
if (!fs8.existsSync(marker))
|
|
20837
20944
|
return success(null, null, null);
|
|
20838
20945
|
let tasks = 0;
|
|
20839
20946
|
try {
|
|
20840
20947
|
const merge = (from, to) => {
|
|
20841
|
-
if (!
|
|
20842
|
-
|
|
20948
|
+
if (!fs8.existsSync(to)) {
|
|
20949
|
+
fs8.mkdirSync(path8.dirname(to), { recursive: true });
|
|
20843
20950
|
try {
|
|
20844
|
-
|
|
20951
|
+
fs8.renameSync(from, to);
|
|
20845
20952
|
} catch {
|
|
20846
|
-
|
|
20847
|
-
|
|
20953
|
+
fs8.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
|
|
20954
|
+
fs8.rmSync(from, { recursive: true, force: true });
|
|
20848
20955
|
}
|
|
20849
20956
|
return;
|
|
20850
20957
|
}
|
|
20851
|
-
if (
|
|
20852
|
-
for (const name of
|
|
20853
|
-
merge(
|
|
20958
|
+
if (fs8.lstatSync(from).isDirectory() && fs8.lstatSync(to).isDirectory())
|
|
20959
|
+
for (const name of fs8.readdirSync(from))
|
|
20960
|
+
merge(path8.join(from, name), path8.join(to, name));
|
|
20854
20961
|
};
|
|
20855
20962
|
for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
|
|
20856
|
-
const from =
|
|
20857
|
-
if (!
|
|
20963
|
+
const from = path8.join(dirs.v2, sub);
|
|
20964
|
+
if (!fs8.existsSync(from))
|
|
20858
20965
|
continue;
|
|
20859
20966
|
if (sub === "tasks") {
|
|
20860
|
-
for (const name of
|
|
20861
|
-
if (validId(name) && !
|
|
20967
|
+
for (const name of fs8.readdirSync(from))
|
|
20968
|
+
if (validId(name) && !fs8.existsSync(path8.join(dirs.dir, sub, name)))
|
|
20862
20969
|
tasks += 1;
|
|
20863
20970
|
}
|
|
20864
|
-
merge(from,
|
|
20971
|
+
merge(from, path8.join(dirs.dir, sub));
|
|
20865
20972
|
}
|
|
20866
|
-
const ledger =
|
|
20867
|
-
if (
|
|
20868
|
-
merge(ledger,
|
|
20869
|
-
|
|
20870
|
-
|
|
20973
|
+
const ledger = path8.join(dirs.v2, "ledger");
|
|
20974
|
+
if (fs8.existsSync(ledger) && !fs8.existsSync(path8.join(dirs.dir, "ledger")))
|
|
20975
|
+
merge(ledger, path8.join(dirs.dir, "ledger"));
|
|
20976
|
+
fs8.rmSync(path8.join(dirs.v2, "task-index.json"), { force: true });
|
|
20977
|
+
fs8.renameSync(marker, path8.join(dirs.v2, "store.moved.json"));
|
|
20871
20978
|
} catch (error) {
|
|
20872
20979
|
return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
|
|
20873
20980
|
path: dirs.v2
|
|
@@ -20887,7 +20994,7 @@ class TaskStore {
|
|
|
20887
20994
|
readV2Workspace(file) {
|
|
20888
20995
|
let bytes;
|
|
20889
20996
|
try {
|
|
20890
|
-
bytes =
|
|
20997
|
+
bytes = fs8.readFileSync(file, "utf8");
|
|
20891
20998
|
} catch {
|
|
20892
20999
|
return null;
|
|
20893
21000
|
}
|
|
@@ -20905,7 +21012,7 @@ class TaskStore {
|
|
|
20905
21012
|
schemaVersion: SCHEMA_VERSION,
|
|
20906
21013
|
id: workspaceId ?? newId(),
|
|
20907
21014
|
revision: newRevision(),
|
|
20908
|
-
root:
|
|
21015
|
+
root: path8.dirname(v2),
|
|
20909
21016
|
runtime: { createdWith: version, updatedWith: version },
|
|
20910
21017
|
writer: null,
|
|
20911
21018
|
store: {
|
|
@@ -20916,12 +21023,12 @@ class TaskStore {
|
|
|
20916
21023
|
},
|
|
20917
21024
|
critical: ["store"]
|
|
20918
21025
|
};
|
|
20919
|
-
|
|
20920
|
-
const ignore =
|
|
20921
|
-
if (!
|
|
20922
|
-
|
|
21026
|
+
fs8.mkdirSync(v2, { recursive: true });
|
|
21027
|
+
const ignore = path8.join(v2, ".gitignore");
|
|
21028
|
+
if (!fs8.existsSync(ignore))
|
|
21029
|
+
fs8.writeFileSync(ignore, `*
|
|
20923
21030
|
`);
|
|
20924
|
-
replaceFile(
|
|
21031
|
+
replaceFile(path8.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
|
|
20925
21032
|
`, true);
|
|
20926
21033
|
}
|
|
20927
21034
|
stubFor(taskId) {
|
|
@@ -20937,7 +21044,7 @@ class TaskStore {
|
|
|
20937
21044
|
}
|
|
20938
21045
|
isStub(file) {
|
|
20939
21046
|
try {
|
|
20940
|
-
const value = JSON.parse(
|
|
21047
|
+
const value = JSON.parse(fs8.readFileSync(file, "utf8"));
|
|
20941
21048
|
return isObject5(value) && isObject5(value.store) && value.store.format === "workit-store";
|
|
20942
21049
|
} catch {
|
|
20943
21050
|
return false;
|
|
@@ -20945,32 +21052,32 @@ class TaskStore {
|
|
|
20945
21052
|
}
|
|
20946
21053
|
writeTombstoneIfMissing() {
|
|
20947
21054
|
try {
|
|
20948
|
-
const v2 =
|
|
20949
|
-
if (
|
|
21055
|
+
const v2 = path8.join(this.root, ".workit");
|
|
21056
|
+
if (fs8.existsSync(path8.join(v2, "workspace.json")))
|
|
20950
21057
|
return;
|
|
20951
21058
|
let id = null;
|
|
20952
21059
|
try {
|
|
20953
|
-
id = JSON.parse(
|
|
21060
|
+
id = JSON.parse(fs8.readFileSync(this.paths().workspace, "utf8")).id ?? null;
|
|
20954
21061
|
} catch {}
|
|
20955
21062
|
this.writeTombstone(v2, id);
|
|
20956
21063
|
} catch {}
|
|
20957
21064
|
}
|
|
20958
21065
|
initializeStorage() {
|
|
20959
21066
|
const dirs = this.paths();
|
|
20960
|
-
retryTransient(() =>
|
|
20961
|
-
retryTransient(() =>
|
|
20962
|
-
if (!
|
|
21067
|
+
retryTransient(() => fs8.mkdirSync(dirs.checkout, { recursive: true }));
|
|
21068
|
+
retryTransient(() => fs8.mkdirSync(dirs.tasks, { recursive: true }));
|
|
21069
|
+
if (!fs8.existsSync(dirs.marker))
|
|
20963
21070
|
replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
|
|
20964
21071
|
`, false);
|
|
20965
21072
|
if (!dirs.shared) {
|
|
20966
|
-
const ignore =
|
|
21073
|
+
const ignore = path8.join(dirs.dir, ".gitignore");
|
|
20967
21074
|
let current = null;
|
|
20968
21075
|
try {
|
|
20969
|
-
current =
|
|
21076
|
+
current = fs8.readFileSync(ignore, "utf8");
|
|
20970
21077
|
} catch {}
|
|
20971
21078
|
if (current !== `*
|
|
20972
21079
|
`)
|
|
20973
|
-
retryTransient(() =>
|
|
21080
|
+
retryTransient(() => fs8.writeFileSync(ignore, `*
|
|
20974
21081
|
`));
|
|
20975
21082
|
}
|
|
20976
21083
|
this.writeTombstoneIfMissing();
|
|
@@ -20986,7 +21093,7 @@ class TaskStore {
|
|
|
20986
21093
|
shouldReclaim: ({ payload, nowMs }) => {
|
|
20987
21094
|
let ageMs = null;
|
|
20988
21095
|
try {
|
|
20989
|
-
ageMs = nowMs -
|
|
21096
|
+
ageMs = nowMs - fs8.lstatSync(lockPath).mtimeMs;
|
|
20990
21097
|
} catch {}
|
|
20991
21098
|
return classifyLockOwner(payload, ageMs, localLockHost(), TASK_STORE_LOCK).state === "stale";
|
|
20992
21099
|
},
|
|
@@ -21023,7 +21130,7 @@ class TaskStore {
|
|
|
21023
21130
|
withLock(lockPath, operation) {
|
|
21024
21131
|
try {
|
|
21025
21132
|
this.initializeStorage();
|
|
21026
|
-
|
|
21133
|
+
fs8.mkdirSync(path8.dirname(lockPath), { recursive: true });
|
|
21027
21134
|
} catch (error) {
|
|
21028
21135
|
return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
|
|
21029
21136
|
path: this.paths().dir
|
|
@@ -21063,18 +21170,18 @@ class TaskStore {
|
|
|
21063
21170
|
lockDirWritable(lockPath) {
|
|
21064
21171
|
const probe = `${lockPath}.${process.pid}.${randomUUID3()}.probe`;
|
|
21065
21172
|
try {
|
|
21066
|
-
|
|
21173
|
+
fs8.closeSync(fs8.openSync(probe, "wx", 384));
|
|
21067
21174
|
} catch {
|
|
21068
21175
|
return false;
|
|
21069
21176
|
}
|
|
21070
21177
|
try {
|
|
21071
|
-
|
|
21178
|
+
fs8.rmSync(probe, { force: true });
|
|
21072
21179
|
} catch {}
|
|
21073
21180
|
return true;
|
|
21074
21181
|
}
|
|
21075
21182
|
lockHolderPresent(lockPath) {
|
|
21076
21183
|
try {
|
|
21077
|
-
|
|
21184
|
+
fs8.lstatSync(lockPath);
|
|
21078
21185
|
return true;
|
|
21079
21186
|
} catch (error) {
|
|
21080
21187
|
return error?.code !== "ENOENT";
|
|
@@ -21086,12 +21193,12 @@ class TaskStore {
|
|
|
21086
21193
|
if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
|
|
21087
21194
|
return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
|
|
21088
21195
|
path: lockPath,
|
|
21089
|
-
guidance: `Check permissions and the read-only attribute on ${
|
|
21196
|
+
guidance: `Check permissions and the read-only attribute on ${path8.dirname(lockPath)}.`
|
|
21090
21197
|
});
|
|
21091
21198
|
if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
|
|
21092
21199
|
let lock = null;
|
|
21093
21200
|
try {
|
|
21094
|
-
lock = parseMetadataLockOrNull(
|
|
21201
|
+
lock = parseMetadataLockOrNull(fs8.readFileSync(lockPath, "utf8"));
|
|
21095
21202
|
} catch {}
|
|
21096
21203
|
return failure2("busy", `workit lock is held by another Workit call${lock ? ` (pid ${lock.pid} on ${lock.host})` : ""}; retry shortly`, {
|
|
21097
21204
|
outcome: "not_started",
|
|
@@ -21109,15 +21216,15 @@ var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
|
|
|
21109
21216
|
var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
|
|
21110
21217
|
|
|
21111
21218
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
21112
|
-
import { createHash as
|
|
21113
|
-
import * as
|
|
21219
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
21220
|
+
import * as fs13 from "node:fs";
|
|
21114
21221
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
21115
|
-
import
|
|
21222
|
+
import path16 from "node:path";
|
|
21116
21223
|
|
|
21117
21224
|
// packages/workit-core/src/check-config.ts
|
|
21118
21225
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
21119
|
-
import
|
|
21120
|
-
import
|
|
21226
|
+
import fs9 from "node:fs";
|
|
21227
|
+
import path9 from "node:path";
|
|
21121
21228
|
var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
|
|
21122
21229
|
var CHECKS_FILE = "workit.checks.json";
|
|
21123
21230
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -21170,10 +21277,10 @@ var git = (cwd, args) => {
|
|
|
21170
21277
|
return value || null;
|
|
21171
21278
|
};
|
|
21172
21279
|
function checkRoot(cwd) {
|
|
21173
|
-
return
|
|
21280
|
+
return path9.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
|
|
21174
21281
|
}
|
|
21175
21282
|
var packageManager = (root) => {
|
|
21176
|
-
const has = (file) =>
|
|
21283
|
+
const has = (file) => fs9.existsSync(path9.join(root, file));
|
|
21177
21284
|
if (has("bun.lock") || has("bun.lockb"))
|
|
21178
21285
|
return "bun";
|
|
21179
21286
|
if (has("pnpm-lock.yaml"))
|
|
@@ -21193,7 +21300,7 @@ var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
|
|
|
21193
21300
|
function detectedChecks(root) {
|
|
21194
21301
|
const read = (file) => {
|
|
21195
21302
|
try {
|
|
21196
|
-
return
|
|
21303
|
+
return fs9.readFileSync(path9.join(root, file), "utf8");
|
|
21197
21304
|
} catch {
|
|
21198
21305
|
return null;
|
|
21199
21306
|
}
|
|
@@ -21232,7 +21339,7 @@ function detectedChecks(root) {
|
|
|
21232
21339
|
var GATES = ["testing", "verification"];
|
|
21233
21340
|
function loadCheckConfig(cwd) {
|
|
21234
21341
|
const root = checkRoot(cwd);
|
|
21235
|
-
const file =
|
|
21342
|
+
const file = path9.join(root, CHECKS_FILE);
|
|
21236
21343
|
const none = (error = null) => ({
|
|
21237
21344
|
source: error ? CHECKS_FILE : "none",
|
|
21238
21345
|
root,
|
|
@@ -21242,7 +21349,7 @@ function loadCheckConfig(cwd) {
|
|
|
21242
21349
|
});
|
|
21243
21350
|
let text = null;
|
|
21244
21351
|
try {
|
|
21245
|
-
text =
|
|
21352
|
+
text = fs9.readFileSync(file, "utf8");
|
|
21246
21353
|
} catch (error) {
|
|
21247
21354
|
if (error.code !== "ENOENT")
|
|
21248
21355
|
return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
|
|
@@ -21305,10 +21412,10 @@ function workitAvailable(env = process.env) {
|
|
|
21305
21412
|
return workitOnPath;
|
|
21306
21413
|
const windows = process.platform === "win32";
|
|
21307
21414
|
const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
|
|
21308
|
-
const dirs = (env.PATH ?? env.Path ?? "").split(
|
|
21415
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(path9.delimiter).filter(Boolean);
|
|
21309
21416
|
workitOnPath = dirs.some((dir) => exts.some((ext) => {
|
|
21310
21417
|
try {
|
|
21311
|
-
return
|
|
21418
|
+
return fs9.statSync(path9.join(dir, `workit${ext}`)).isFile();
|
|
21312
21419
|
} catch {
|
|
21313
21420
|
return false;
|
|
21314
21421
|
}
|
|
@@ -21321,10 +21428,10 @@ function checkCommand(args, version, onPath = workitAvailable()) {
|
|
|
21321
21428
|
|
|
21322
21429
|
// packages/workit-core/src/git/rev.ts
|
|
21323
21430
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
21324
|
-
import { createHash as
|
|
21325
|
-
import
|
|
21431
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
21432
|
+
import fs10 from "node:fs";
|
|
21326
21433
|
import os2 from "node:os";
|
|
21327
|
-
import
|
|
21434
|
+
import path10 from "node:path";
|
|
21328
21435
|
var MAX_BUFFER = 256 * 1024 * 1024;
|
|
21329
21436
|
var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
|
|
21330
21437
|
var git2 = (cwd, args, options = {}) => {
|
|
@@ -21359,10 +21466,10 @@ function headSha(cwd) {
|
|
|
21359
21466
|
var emptyTree = (cwd) => line(git2(cwd, ["mktree"], { input: "" }));
|
|
21360
21467
|
var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
|
|
21361
21468
|
var seedIndex = (from, to) => {
|
|
21362
|
-
|
|
21363
|
-
const { atimeNs, mtimeNs } =
|
|
21469
|
+
fs10.copyFileSync(from, to);
|
|
21470
|
+
const { atimeNs, mtimeNs } = fs10.statSync(from, { bigint: true });
|
|
21364
21471
|
const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
|
|
21365
|
-
|
|
21472
|
+
fs10.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
|
|
21366
21473
|
};
|
|
21367
21474
|
function worktreeTree(cwd, options = {}) {
|
|
21368
21475
|
const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
|
|
@@ -21371,10 +21478,10 @@ function worktreeTree(cwd, options = {}) {
|
|
|
21371
21478
|
return null;
|
|
21372
21479
|
const cap = options.maxUntrackedBytes ?? MAX_UNTRACKED_BYTES;
|
|
21373
21480
|
const timeoutMs = options.timeoutMs ?? GIT_TIMEOUTS.worktree;
|
|
21374
|
-
const scratch =
|
|
21481
|
+
const scratch = fs10.mkdtempSync(path10.join(os2.tmpdir(), "workit-tree-"));
|
|
21375
21482
|
try {
|
|
21376
|
-
const tempIndex =
|
|
21377
|
-
if (
|
|
21483
|
+
const tempIndex = path10.join(scratch, "index");
|
|
21484
|
+
if (fs10.existsSync(indexPath))
|
|
21378
21485
|
seedIndex(indexPath, tempIndex);
|
|
21379
21486
|
const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
|
|
21380
21487
|
const untracked = git2(top, ["ls-files", "--others", "--exclude-standard", "-z"], {
|
|
@@ -21386,7 +21493,7 @@ function worktreeTree(cwd, options = {}) {
|
|
|
21386
21493
|
const skipped = [];
|
|
21387
21494
|
for (const file of untracked.stdout.split("\x00").filter(Boolean)) {
|
|
21388
21495
|
try {
|
|
21389
|
-
const stat =
|
|
21496
|
+
const stat = fs10.statSync(path10.join(top, file));
|
|
21390
21497
|
if (stat.isFile() && stat.size > cap)
|
|
21391
21498
|
skipped.push({ path: file, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) });
|
|
21392
21499
|
} catch {}
|
|
@@ -21403,10 +21510,10 @@ function worktreeTree(cwd, options = {}) {
|
|
|
21403
21510
|
if (!tree)
|
|
21404
21511
|
return null;
|
|
21405
21512
|
const base = line(git2(top, ["rev-parse", "--verify", "-q", "HEAD^{tree}"])) ?? emptyTree(top);
|
|
21406
|
-
const key = skipped.length ? `sha256:${
|
|
21513
|
+
const key = skipped.length ? `sha256:${createHash6("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
|
|
21407
21514
|
return { tree, key, dirty: tree !== base || skipped.length > 0, skipped };
|
|
21408
21515
|
} finally {
|
|
21409
|
-
|
|
21516
|
+
fs10.rmSync(scratch, { recursive: true, force: true });
|
|
21410
21517
|
}
|
|
21411
21518
|
}
|
|
21412
21519
|
function worktreeSignal(cwd, options = {}) {
|
|
@@ -21426,16 +21533,16 @@ function worktreeSignal(cwd, options = {}) {
|
|
|
21426
21533
|
});
|
|
21427
21534
|
if (!status.ok)
|
|
21428
21535
|
return null;
|
|
21429
|
-
const hash =
|
|
21536
|
+
const hash = createHash6("sha256");
|
|
21430
21537
|
hash.update(`${headSha(top) ?? "unborn"}\x00`);
|
|
21431
21538
|
for (const entry of status.stdout.split("\x00")) {
|
|
21432
21539
|
if (entry.length < 4)
|
|
21433
21540
|
continue;
|
|
21434
21541
|
const file = entry.slice(3);
|
|
21435
|
-
const absolute =
|
|
21542
|
+
const absolute = path10.join(top, file);
|
|
21436
21543
|
let stat = "-";
|
|
21437
21544
|
try {
|
|
21438
|
-
const info =
|
|
21545
|
+
const info = fs10.lstatSync(absolute, { bigint: true });
|
|
21439
21546
|
stat = `${info.mode.toString(8)}:${info.size}:${info.mtimeNs}`;
|
|
21440
21547
|
if (info.isDirectory())
|
|
21441
21548
|
stat += `:${headSha(absolute) ?? "-"}`;
|
|
@@ -21502,17 +21609,17 @@ function pushRemoteName(cwd, branch) {
|
|
|
21502
21609
|
|
|
21503
21610
|
// packages/workit-core/src/core/policy/derive.ts
|
|
21504
21611
|
import { existsSync as existsSync4 } from "node:fs";
|
|
21505
|
-
import
|
|
21612
|
+
import path15 from "node:path";
|
|
21506
21613
|
|
|
21507
21614
|
// packages/workit-core/src/ledger.ts
|
|
21508
21615
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
21509
|
-
import { createHash as
|
|
21510
|
-
import
|
|
21511
|
-
import
|
|
21616
|
+
import { createHash as createHash9, randomBytes as randomBytes3 } from "node:crypto";
|
|
21617
|
+
import fs12 from "node:fs";
|
|
21618
|
+
import path14 from "node:path";
|
|
21512
21619
|
|
|
21513
21620
|
// packages/workit-core/src/core/vcs-config.ts
|
|
21514
|
-
import
|
|
21515
|
-
import
|
|
21621
|
+
import fs11 from "node:fs";
|
|
21622
|
+
import path13 from "node:path";
|
|
21516
21623
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
21517
21624
|
|
|
21518
21625
|
// packages/workit-core/src/core/config.ts
|
|
@@ -21526,7 +21633,7 @@ import {
|
|
|
21526
21633
|
writeFileSync as writeFileSync2
|
|
21527
21634
|
} from "node:fs";
|
|
21528
21635
|
import os3 from "node:os";
|
|
21529
|
-
import
|
|
21636
|
+
import path11 from "node:path";
|
|
21530
21637
|
|
|
21531
21638
|
// packages/workit-core/src/core/boundary.ts
|
|
21532
21639
|
import { statSync as statSync2 } from "node:fs";
|
|
@@ -21585,7 +21692,7 @@ var mergePreset = (preset, input = {}, current = {
|
|
|
21585
21692
|
protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
|
|
21586
21693
|
};
|
|
21587
21694
|
};
|
|
21588
|
-
var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ??
|
|
21695
|
+
var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path11.join(process.env.XDG_CONFIG_HOME || path11.join(os3.homedir(), ".config"), "workit");
|
|
21589
21696
|
var migratedDir = null;
|
|
21590
21697
|
var migrationFailed = false;
|
|
21591
21698
|
var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
@@ -21595,7 +21702,7 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
21595
21702
|
migratedDir = dir;
|
|
21596
21703
|
return dir;
|
|
21597
21704
|
}
|
|
21598
|
-
const legacy =
|
|
21705
|
+
const legacy = path11.join(process.env.XDG_CONFIG_HOME || path11.join(os3.homedir(), ".config"), "workflow-toolkit");
|
|
21599
21706
|
if (!existsSync3(legacy)) {
|
|
21600
21707
|
migratedDir = dir;
|
|
21601
21708
|
return dir;
|
|
@@ -21608,8 +21715,8 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
21608
21715
|
mkdirSync2(dir, { recursive: true });
|
|
21609
21716
|
diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
|
|
21610
21717
|
for (const entry of readdirSync2(legacy, { withFileTypes: true })) {
|
|
21611
|
-
const src =
|
|
21612
|
-
const dest =
|
|
21718
|
+
const src = path11.join(legacy, entry.name);
|
|
21719
|
+
const dest = path11.join(dir, entry.name);
|
|
21613
21720
|
if (existsSync3(dest))
|
|
21614
21721
|
continue;
|
|
21615
21722
|
try {
|
|
@@ -21681,7 +21788,7 @@ var parseConfigResult = (raw, file) => {
|
|
|
21681
21788
|
return { status: "valid", path: file, config };
|
|
21682
21789
|
};
|
|
21683
21790
|
var readConfigTyped = (dir) => {
|
|
21684
|
-
const file =
|
|
21791
|
+
const file = path11.join(dir ?? configDir(), "config.json");
|
|
21685
21792
|
return parseConfigResult(readSafe(file), file);
|
|
21686
21793
|
};
|
|
21687
21794
|
var readConfig = () => {
|
|
@@ -21690,7 +21797,7 @@ var readConfig = () => {
|
|
|
21690
21797
|
throw new Error(result.error);
|
|
21691
21798
|
return result.config ?? DEFAULTS;
|
|
21692
21799
|
};
|
|
21693
|
-
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ??
|
|
21800
|
+
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path11.join(configDir(), "vcs.json");
|
|
21694
21801
|
var branchGlobPattern = (glob) => new RegExp(`^${glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i");
|
|
21695
21802
|
var resolveBranchPolicy = (config, workspace, repoPreset) => {
|
|
21696
21803
|
const wp = workspace?.branchPolicy ?? {};
|
|
@@ -21722,9 +21829,9 @@ var COMMIT_PRESETS = [
|
|
|
21722
21829
|
|
|
21723
21830
|
// packages/workit-core/src/core/workspaces.ts
|
|
21724
21831
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
21725
|
-
import { createHash as
|
|
21832
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
21726
21833
|
import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
21727
|
-
import
|
|
21834
|
+
import path12 from "node:path";
|
|
21728
21835
|
|
|
21729
21836
|
// packages/workit-core/src/core/branch-policy.ts
|
|
21730
21837
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
@@ -21849,7 +21956,7 @@ var releaseTrackSchema = object2({
|
|
|
21849
21956
|
context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
|
|
21850
21957
|
}
|
|
21851
21958
|
}
|
|
21852
|
-
if (track.versionSource?.kind === "package-json" && (
|
|
21959
|
+
if (track.versionSource?.kind === "package-json" && (path12.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
|
|
21853
21960
|
context.addIssue({
|
|
21854
21961
|
code: "custom",
|
|
21855
21962
|
path: ["versionSource", "path"],
|
|
@@ -21870,6 +21977,8 @@ var workspaceConfigSchema = object2({
|
|
|
21870
21977
|
defaultEndpoint: unknown().optional(),
|
|
21871
21978
|
verification: _enum(["self", "independent"]).optional(),
|
|
21872
21979
|
trailers: boolean2().optional(),
|
|
21980
|
+
liveSessionGuard: boolean2().optional(),
|
|
21981
|
+
fanout: object2({ tierModels: unknown().optional() }).passthrough().optional(),
|
|
21873
21982
|
autonomy: object2(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
|
|
21874
21983
|
kind,
|
|
21875
21984
|
union([boolean2(), literal("verified")]).optional()
|
|
@@ -21910,7 +22019,7 @@ var workspacesFileSchema = object2({ workspaces: array(workspaceConfigSchema).op
|
|
|
21910
22019
|
names.add(workspace.name);
|
|
21911
22020
|
}
|
|
21912
22021
|
});
|
|
21913
|
-
var workspacesRevision = (contents) =>
|
|
22022
|
+
var workspacesRevision = (contents) => createHash7("sha256").update(contents ?? "<missing>").digest("hex");
|
|
21914
22023
|
var validateWorkspacesDocument = (parsed, file) => {
|
|
21915
22024
|
if (!isConfigObject(parsed)) {
|
|
21916
22025
|
return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
|
|
@@ -21948,7 +22057,7 @@ var validateWorkspacesDocument = (parsed, file) => {
|
|
|
21948
22057
|
};
|
|
21949
22058
|
};
|
|
21950
22059
|
var readWorkspacesResult = (dir = configDir()) => {
|
|
21951
|
-
const file =
|
|
22060
|
+
const file = path12.join(dir, "workspaces.json");
|
|
21952
22061
|
let raw;
|
|
21953
22062
|
try {
|
|
21954
22063
|
raw = readFileSync3(file, "utf8");
|
|
@@ -22059,9 +22168,9 @@ var mainCheckoutOf = (cwd) => {
|
|
|
22059
22168
|
let main = null;
|
|
22060
22169
|
const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
|
|
22061
22170
|
const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
|
|
22062
|
-
if (gitDir && common &&
|
|
22171
|
+
if (gitDir && common && path12.resolve(gitDir) !== path12.resolve(common)) {
|
|
22063
22172
|
const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
|
|
22064
|
-
main = listed?.trim() || (
|
|
22173
|
+
main = listed?.trim() || (path12.basename(common) === ".git" ? path12.dirname(common) : common);
|
|
22065
22174
|
}
|
|
22066
22175
|
mainCheckouts.set(cwd, main);
|
|
22067
22176
|
return main;
|
|
@@ -22152,7 +22261,7 @@ var runtimeWorkspaceIndexSchema = object2({
|
|
|
22152
22261
|
workspaces: array(object2({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
22153
22262
|
}).passthrough();
|
|
22154
22263
|
var matchRuntimeWorkspace = (cwd, dir = configDir()) => {
|
|
22155
|
-
const file =
|
|
22264
|
+
const file = path12.join(dir, "workspaces.json");
|
|
22156
22265
|
let raw;
|
|
22157
22266
|
try {
|
|
22158
22267
|
raw = readFileSync3(file, "utf8");
|
|
@@ -22187,6 +22296,17 @@ var resolveRuntimeReleaseTracks = (cwd) => {
|
|
|
22187
22296
|
const matched = matchRuntimeWorkspace(cwd);
|
|
22188
22297
|
return matched ? { workspace: matched.selected.name, releaseTracks: matched.selected.releaseTracks } : null;
|
|
22189
22298
|
};
|
|
22299
|
+
var TIER_KEYS = new Set(["mundane", "standard", "hard"]);
|
|
22300
|
+
var TIER_HOSTS = new Set([
|
|
22301
|
+
"claude_code",
|
|
22302
|
+
"opencode",
|
|
22303
|
+
"codex",
|
|
22304
|
+
"codex_cli",
|
|
22305
|
+
"codex_desktop",
|
|
22306
|
+
"cursor",
|
|
22307
|
+
"pi",
|
|
22308
|
+
"default"
|
|
22309
|
+
]);
|
|
22190
22310
|
var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
22191
22311
|
const matched = matchRuntimeWorkspace(cwd);
|
|
22192
22312
|
if (!matched)
|
|
@@ -22227,7 +22347,7 @@ function workspaceDefaultTarget(cwd, globalDefault) {
|
|
|
22227
22347
|
}
|
|
22228
22348
|
|
|
22229
22349
|
// packages/workit-core/src/core/release-tracks.ts
|
|
22230
|
-
import { createHash as
|
|
22350
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
22231
22351
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
22232
22352
|
var KNOWN_FIELDS = new Set([
|
|
22233
22353
|
"strategy",
|
|
@@ -22597,7 +22717,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
22597
22717
|
]) ?? "",
|
|
22598
22718
|
git3(cwd, ["config", "--get", recordedBaseKey(branch)]) ?? ""
|
|
22599
22719
|
] : [];
|
|
22600
|
-
key =
|
|
22720
|
+
key = createHash8("sha256").update(JSON.stringify({ cwd, input, refs, head, own })).digest("hex");
|
|
22601
22721
|
const hit = memo2.get(key);
|
|
22602
22722
|
if (hit)
|
|
22603
22723
|
return hit;
|
|
@@ -22612,7 +22732,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
22612
22732
|
}
|
|
22613
22733
|
|
|
22614
22734
|
// packages/workit-core/src/core/vcs-config.ts
|
|
22615
|
-
var workspacesPath = () =>
|
|
22735
|
+
var workspacesPath = () => path13.join(configDir(), "workspaces.json");
|
|
22616
22736
|
var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
22617
22737
|
var remoteProvider = (cwd) => {
|
|
22618
22738
|
const r = spawnSync8("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
|
|
@@ -22702,7 +22822,7 @@ var readVcsConfig = () => {
|
|
|
22702
22822
|
const file = vcsConfigPath();
|
|
22703
22823
|
let raw;
|
|
22704
22824
|
try {
|
|
22705
|
-
raw =
|
|
22825
|
+
raw = fs11.readFileSync(file, "utf8");
|
|
22706
22826
|
} catch {
|
|
22707
22827
|
return { status: "missing", path: file, config: {} };
|
|
22708
22828
|
}
|
|
@@ -22787,7 +22907,7 @@ function vcsConfig(mode, cwd, options = {}) {
|
|
|
22787
22907
|
const prov = cfg[provider] ?? {};
|
|
22788
22908
|
const out = {
|
|
22789
22909
|
ok: true,
|
|
22790
|
-
configPath:
|
|
22910
|
+
configPath: path13.resolve(cfgPath),
|
|
22791
22911
|
provider,
|
|
22792
22912
|
defaultTargetBranch: workspaceDefault,
|
|
22793
22913
|
pr: cfg.pr ?? {},
|
|
@@ -22998,7 +23118,7 @@ function diffHash(cwd, base, head) {
|
|
|
22998
23118
|
], GIT_TIMEOUTS.worktree);
|
|
22999
23119
|
if (!diff.ok || !diff.stdout)
|
|
23000
23120
|
return null;
|
|
23001
|
-
const hash =
|
|
23121
|
+
const hash = createHash9("sha256");
|
|
23002
23122
|
for (const line of diff.stdout.split(`
|
|
23003
23123
|
`)) {
|
|
23004
23124
|
if (line.startsWith("index "))
|
|
@@ -23010,16 +23130,16 @@ function diffHash(cwd, base, head) {
|
|
|
23010
23130
|
return `sha256:${hash.digest("hex")}`;
|
|
23011
23131
|
}
|
|
23012
23132
|
function storeRoot(cwd) {
|
|
23013
|
-
const location = resolveStore(
|
|
23133
|
+
const location = resolveStore(path14.resolve(cwd));
|
|
23014
23134
|
if (location instanceof Error)
|
|
23015
23135
|
return err("unavailable", location.message.replace(/; fix the repository.*$/u, ""), "fix the repository (git status should work here) or run from a non-git directory");
|
|
23016
23136
|
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
23017
23137
|
}
|
|
23018
23138
|
function ledgerPath(cwd) {
|
|
23019
23139
|
const root = storeRoot(cwd);
|
|
23020
|
-
return root.ok ? { ok: true, value:
|
|
23140
|
+
return root.ok ? { ok: true, value: path14.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
23021
23141
|
}
|
|
23022
|
-
var newId2 = () => `${Date.now().toString(36)}-${
|
|
23142
|
+
var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
|
|
23023
23143
|
var NETWORK_FS_TYPES = new Set([
|
|
23024
23144
|
26985,
|
|
23025
23145
|
20859,
|
|
@@ -23037,7 +23157,7 @@ var onNetworkFs = (dir) => {
|
|
|
23037
23157
|
if (process.platform !== "linux")
|
|
23038
23158
|
return false;
|
|
23039
23159
|
try {
|
|
23040
|
-
return isNetworkFsType(
|
|
23160
|
+
return isNetworkFsType(fs12.statfsSync(dir).type);
|
|
23041
23161
|
} catch {
|
|
23042
23162
|
return false;
|
|
23043
23163
|
}
|
|
@@ -23047,15 +23167,15 @@ var LOCK_STALE_MS = 30000;
|
|
|
23047
23167
|
var LOCK_OWNER = "owner";
|
|
23048
23168
|
var lockToken = (lock) => {
|
|
23049
23169
|
try {
|
|
23050
|
-
return
|
|
23170
|
+
return fs12.readFileSync(path14.join(lock, LOCK_OWNER), "utf8");
|
|
23051
23171
|
} catch {
|
|
23052
23172
|
return null;
|
|
23053
23173
|
}
|
|
23054
23174
|
};
|
|
23055
23175
|
var lockAgeMs = (lock) => {
|
|
23056
|
-
for (const target of [
|
|
23176
|
+
for (const target of [path14.join(lock, LOCK_OWNER), lock]) {
|
|
23057
23177
|
try {
|
|
23058
|
-
return Date.now() -
|
|
23178
|
+
return Date.now() - fs12.statSync(target).mtimeMs;
|
|
23059
23179
|
} catch {}
|
|
23060
23180
|
}
|
|
23061
23181
|
return null;
|
|
@@ -23063,55 +23183,55 @@ var lockAgeMs = (lock) => {
|
|
|
23063
23183
|
var ledgerLock = {
|
|
23064
23184
|
acquire(lock) {
|
|
23065
23185
|
try {
|
|
23066
|
-
|
|
23186
|
+
fs12.mkdirSync(lock);
|
|
23067
23187
|
} catch (error) {
|
|
23068
23188
|
if (error.code === "EEXIST")
|
|
23069
23189
|
return null;
|
|
23070
23190
|
throw error;
|
|
23071
23191
|
}
|
|
23072
|
-
const token = `${process.pid}-${
|
|
23073
|
-
|
|
23192
|
+
const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
|
|
23193
|
+
fs12.writeFileSync(path14.join(lock, LOCK_OWNER), token);
|
|
23074
23194
|
return token;
|
|
23075
23195
|
},
|
|
23076
23196
|
release(lock, token) {
|
|
23077
23197
|
if (lockToken(lock) !== token)
|
|
23078
23198
|
return false;
|
|
23079
|
-
const tomb = `${lock}.release-${
|
|
23199
|
+
const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
|
|
23080
23200
|
try {
|
|
23081
|
-
|
|
23201
|
+
fs12.renameSync(lock, tomb);
|
|
23082
23202
|
} catch {
|
|
23083
23203
|
return false;
|
|
23084
23204
|
}
|
|
23085
23205
|
if (lockToken(tomb) !== token) {
|
|
23086
23206
|
try {
|
|
23087
|
-
|
|
23207
|
+
fs12.renameSync(tomb, lock);
|
|
23088
23208
|
} catch {}
|
|
23089
23209
|
return false;
|
|
23090
23210
|
}
|
|
23091
|
-
|
|
23211
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
23092
23212
|
return true;
|
|
23093
23213
|
},
|
|
23094
23214
|
token: lockToken,
|
|
23095
23215
|
reap(lock, observed) {
|
|
23096
|
-
const tomb = `${lock}.reap-${
|
|
23216
|
+
const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
|
|
23097
23217
|
try {
|
|
23098
|
-
|
|
23218
|
+
fs12.renameSync(lock, tomb);
|
|
23099
23219
|
} catch {
|
|
23100
23220
|
return false;
|
|
23101
23221
|
}
|
|
23102
23222
|
if (lockToken(tomb) === observed) {
|
|
23103
|
-
|
|
23223
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
23104
23224
|
return true;
|
|
23105
23225
|
}
|
|
23106
23226
|
try {
|
|
23107
|
-
|
|
23227
|
+
fs12.renameSync(tomb, lock);
|
|
23108
23228
|
} catch {
|
|
23109
|
-
|
|
23229
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
23110
23230
|
}
|
|
23111
23231
|
return false;
|
|
23112
23232
|
}
|
|
23113
23233
|
};
|
|
23114
|
-
function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
23234
|
+
function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
|
|
23115
23235
|
const lock = `${file}.lock`;
|
|
23116
23236
|
const deadline = Date.now() + waitMs;
|
|
23117
23237
|
let token = null;
|
|
@@ -23133,14 +23253,14 @@ function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
|
23133
23253
|
ledgerLock.release(lock, token);
|
|
23134
23254
|
}
|
|
23135
23255
|
}
|
|
23136
|
-
var sha2562 = (text) =>
|
|
23256
|
+
var sha2562 = (text) => createHash9("sha256").update(text).digest("hex");
|
|
23137
23257
|
var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
|
|
23138
23258
|
${body}`);
|
|
23139
23259
|
var TAIL_BYTES = 64 * 1024;
|
|
23140
23260
|
var lastLine = (fd, size) => {
|
|
23141
23261
|
for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
|
|
23142
23262
|
const buffer = Buffer.alloc(span);
|
|
23143
|
-
|
|
23263
|
+
fs12.readSync(fd, buffer, 0, span, size - span);
|
|
23144
23264
|
const end = buffer.lastIndexOf(10);
|
|
23145
23265
|
if (end < 0) {
|
|
23146
23266
|
if (span === size)
|
|
@@ -23169,15 +23289,23 @@ var lastLine = (fd, size) => {
|
|
|
23169
23289
|
}
|
|
23170
23290
|
return null;
|
|
23171
23291
|
};
|
|
23292
|
+
var tighten = (fd) => {
|
|
23293
|
+
try {
|
|
23294
|
+
if ((fs12.fstatSync(fd).mode & 63) !== 0)
|
|
23295
|
+
fs12.fchmodSync(fd, 384);
|
|
23296
|
+
} catch {}
|
|
23297
|
+
};
|
|
23172
23298
|
function appendLine(file, build, options) {
|
|
23173
23299
|
const write = () => {
|
|
23174
|
-
const fd =
|
|
23300
|
+
const fd = fs12.openSync(file, "a+", 384);
|
|
23175
23301
|
try {
|
|
23176
|
-
|
|
23302
|
+
if (process.platform !== "win32")
|
|
23303
|
+
tighten(fd);
|
|
23304
|
+
const size = fs12.fstatSync(fd).size;
|
|
23177
23305
|
let fence = "";
|
|
23178
23306
|
if (size > 0) {
|
|
23179
23307
|
const last = Buffer.alloc(1);
|
|
23180
|
-
|
|
23308
|
+
fs12.readSync(fd, last, 0, 1, size - 1);
|
|
23181
23309
|
if (last[0] !== 10)
|
|
23182
23310
|
fence = `
|
|
23183
23311
|
`;
|
|
@@ -23186,17 +23314,17 @@ function appendLine(file, build, options) {
|
|
|
23186
23314
|
if (!line.ok)
|
|
23187
23315
|
return line;
|
|
23188
23316
|
const bytes = Buffer.from(fence + line.value);
|
|
23189
|
-
const written =
|
|
23317
|
+
const written = fs12.writeSync(fd, bytes);
|
|
23190
23318
|
if (written !== bytes.length)
|
|
23191
23319
|
return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
|
|
23192
23320
|
return { ok: true, value: undefined };
|
|
23193
23321
|
} finally {
|
|
23194
|
-
|
|
23322
|
+
fs12.closeSync(fd);
|
|
23195
23323
|
}
|
|
23196
23324
|
};
|
|
23197
23325
|
try {
|
|
23198
|
-
|
|
23199
|
-
if (!options.forceLock && !onNetworkFs(
|
|
23326
|
+
fs12.mkdirSync(path14.dirname(file), { recursive: true });
|
|
23327
|
+
if (!options.forceLock && !onNetworkFs(path14.dirname(file)))
|
|
23200
23328
|
return write();
|
|
23201
23329
|
const locked = withLedgerLock(file, write);
|
|
23202
23330
|
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
@@ -23264,8 +23392,10 @@ function readLedger(cwd, options = {}) {
|
|
|
23264
23392
|
if (!file.ok)
|
|
23265
23393
|
return file;
|
|
23266
23394
|
let text = "";
|
|
23395
|
+
let stat = null;
|
|
23267
23396
|
try {
|
|
23268
|
-
|
|
23397
|
+
stat = fs12.statSync(file.value);
|
|
23398
|
+
text = fs12.readFileSync(file.value, "utf8");
|
|
23269
23399
|
} catch (error) {
|
|
23270
23400
|
if (error.code === "ENOENT")
|
|
23271
23401
|
return {
|
|
@@ -23284,6 +23414,7 @@ function readLedger(cwd, options = {}) {
|
|
|
23284
23414
|
const integrity = { chained: 0, legacy: 0, unverified: [] };
|
|
23285
23415
|
const earlier = new Set([null]);
|
|
23286
23416
|
let chainStarted = false;
|
|
23417
|
+
const ids = new Set;
|
|
23287
23418
|
for (const raw of text.split(`
|
|
23288
23419
|
`)) {
|
|
23289
23420
|
const trimmed = raw.trim();
|
|
@@ -23317,6 +23448,10 @@ function readLedger(cwd, options = {}) {
|
|
|
23317
23448
|
reason = "unsigned";
|
|
23318
23449
|
else
|
|
23319
23450
|
rowIntegrity = "legacy";
|
|
23451
|
+
if (options.integrity && !reason && ids.has(parsed.id))
|
|
23452
|
+
reason = "duplicate_id";
|
|
23453
|
+
if (options.integrity)
|
|
23454
|
+
ids.add(parsed.id);
|
|
23320
23455
|
if (reason)
|
|
23321
23456
|
rowIntegrity = "unverified";
|
|
23322
23457
|
const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
|
|
@@ -23340,7 +23475,8 @@ function readLedger(cwd, options = {}) {
|
|
|
23340
23475
|
seq: rows.length + 1,
|
|
23341
23476
|
superseded: false,
|
|
23342
23477
|
supersedeIgnored: false,
|
|
23343
|
-
...rowIntegrity ? { integrity: rowIntegrity } : {}
|
|
23478
|
+
...rowIntegrity ? { integrity: rowIntegrity } : {},
|
|
23479
|
+
...reason ? { integrityReason: reason } : {}
|
|
23344
23480
|
};
|
|
23345
23481
|
rows.push(row);
|
|
23346
23482
|
if (!options.integrity)
|
|
@@ -23368,18 +23504,48 @@ function readLedger(cwd, options = {}) {
|
|
|
23368
23504
|
const byId = new Map;
|
|
23369
23505
|
for (const row of rows) {
|
|
23370
23506
|
const target = row.supersedes ? byId.get(row.supersedes) : undefined;
|
|
23371
|
-
if (target && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
23507
|
+
if (target && row.integrity !== "unverified" && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
23372
23508
|
target.superseded = true;
|
|
23373
23509
|
else if (row.supersedes)
|
|
23374
23510
|
row.supersedeIgnored = true;
|
|
23375
23511
|
if (!byId.has(row.id))
|
|
23376
23512
|
byId.set(row.id, row);
|
|
23377
23513
|
}
|
|
23514
|
+
if (options.integrity && stat && Buffer.byteLength(text) === stat.size)
|
|
23515
|
+
tamperCache.set(file.value, {
|
|
23516
|
+
size: stat.size,
|
|
23517
|
+
stamp: fileStamp(stat),
|
|
23518
|
+
breaks: integrity.unverified.filter((row) => TAMPER_REASONS.has(row.reason))
|
|
23519
|
+
});
|
|
23378
23520
|
return {
|
|
23379
23521
|
ok: true,
|
|
23380
23522
|
value: { path: file.value, rows, skipped, integrity: options.integrity ? integrity : null }
|
|
23381
23523
|
};
|
|
23382
23524
|
}
|
|
23525
|
+
var TAMPER_REASONS = new Set(["hash_mismatch", "broken_link"]);
|
|
23526
|
+
var tamperCache = new Map;
|
|
23527
|
+
var fileStamp = (stat) => `${stat.ino}:${stat.mtimeMs}:${stat.ctimeMs}`;
|
|
23528
|
+
function ledgerTampering(cwd) {
|
|
23529
|
+
const file = ledgerPath(cwd);
|
|
23530
|
+
if (!file.ok)
|
|
23531
|
+
return [];
|
|
23532
|
+
try {
|
|
23533
|
+
const stat = fs12.statSync(file.value);
|
|
23534
|
+
const cached = tamperCache.get(file.value);
|
|
23535
|
+
if (cached && cached.size === stat.size && cached.stamp === fileStamp(stat))
|
|
23536
|
+
return cached.breaks;
|
|
23537
|
+
} catch {
|
|
23538
|
+
return [];
|
|
23539
|
+
}
|
|
23540
|
+
const read = readLedger(cwd, { integrity: true });
|
|
23541
|
+
return read.ok ? (read.value.integrity?.unverified ?? []).filter((row) => TAMPER_REASONS.has(row.reason)) : [];
|
|
23542
|
+
}
|
|
23543
|
+
function tamperedUnblock(cwd) {
|
|
23544
|
+
const file = ledgerPath(cwd);
|
|
23545
|
+
const where = file.ok ? file.value : "the ledger file";
|
|
23546
|
+
return `workit ledger verify-integrity # lists the edited or missing rows; if the edit is understood, move ${where} aside (mv ${where} ${where}.tampered-<date>) and record the verdicts again`;
|
|
23547
|
+
}
|
|
23548
|
+
var tamperedWarning = (cwd, breaks) => `the ledger was edited, or a row was removed or moved (${breaks.slice(0, 3).map((row) => `${row.reason} at row ${row.seq}`).join(", ")}${breaks.length > 3 ? ", …" : ""}): no verdict is accepted until it is repaired. ${tamperedUnblock(cwd)}`;
|
|
23383
23549
|
function verdictBasis(verdict, current) {
|
|
23384
23550
|
if (verdict.head && verdict.head === current.head)
|
|
23385
23551
|
return "fresh";
|
|
@@ -23387,7 +23553,7 @@ function verdictBasis(verdict, current) {
|
|
|
23387
23553
|
return "carried";
|
|
23388
23554
|
return "stale";
|
|
23389
23555
|
}
|
|
23390
|
-
var isLiveVerdict = (row) => row.type === "verdict" && typeof row.result === "string" && !row.
|
|
23556
|
+
var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id);
|
|
23391
23557
|
function trailerSessions(cwd, base, head) {
|
|
23392
23558
|
if (!base || !head || !safeRef(base) || !safeRef(head))
|
|
23393
23559
|
return [];
|
|
@@ -23468,6 +23634,56 @@ var authoredBy = (authors, session) => {
|
|
|
23468
23634
|
return true;
|
|
23469
23635
|
return false;
|
|
23470
23636
|
};
|
|
23637
|
+
var TRUST_REASONS = new Set([
|
|
23638
|
+
"unsigned",
|
|
23639
|
+
"hash_mismatch",
|
|
23640
|
+
"broken_link",
|
|
23641
|
+
"duplicate_id",
|
|
23642
|
+
"unissued_session",
|
|
23643
|
+
"unbound_session"
|
|
23644
|
+
]);
|
|
23645
|
+
var SHELL_VOUCHED_HOSTS = new Set([
|
|
23646
|
+
"cli",
|
|
23647
|
+
"opencode",
|
|
23648
|
+
"pi",
|
|
23649
|
+
"codex_cli",
|
|
23650
|
+
"codex_desktop"
|
|
23651
|
+
]);
|
|
23652
|
+
function sessionTrust(cwd, row) {
|
|
23653
|
+
const session = row.actor.session;
|
|
23654
|
+
if (!session || row.self === true)
|
|
23655
|
+
return null;
|
|
23656
|
+
const source = typeof row.sessionSource === "string" ? row.sessionSource : "env";
|
|
23657
|
+
const host = row.actor.host;
|
|
23658
|
+
const issuedOnly = source === "flag" ? host !== "cli" : source === "env" && session.includes(":") && !SHELL_VOUCHED_HOSTS.has(host);
|
|
23659
|
+
if (!issuedOnly)
|
|
23660
|
+
return null;
|
|
23661
|
+
const binding = issuedBinding(cwd, session);
|
|
23662
|
+
if (binding === null)
|
|
23663
|
+
return "unissued_session";
|
|
23664
|
+
return binding === "token" && row.sessionBound !== true ? "unbound_session" : null;
|
|
23665
|
+
}
|
|
23666
|
+
function trustReasons(cwd, row) {
|
|
23667
|
+
if (row.integrity === "unverified")
|
|
23668
|
+
return [row.integrityReason ?? "unsigned"];
|
|
23669
|
+
if (row.integrity !== "verified")
|
|
23670
|
+
return ["unsigned"];
|
|
23671
|
+
const session = sessionTrust(cwd, row);
|
|
23672
|
+
return session ? [session] : [];
|
|
23673
|
+
}
|
|
23674
|
+
function withIntegrity(cwd, rows) {
|
|
23675
|
+
if (rows.every((row) => row.integrity !== undefined))
|
|
23676
|
+
return rows;
|
|
23677
|
+
const read = readLedger(cwd, { integrity: true });
|
|
23678
|
+
const bySeq = new Map(read.ok ? read.value.rows.map((row) => [row.seq, row]) : []);
|
|
23679
|
+
return rows.map((row) => {
|
|
23680
|
+
if (row.integrity !== undefined)
|
|
23681
|
+
return row;
|
|
23682
|
+
const fresh = bySeq.get(row.seq);
|
|
23683
|
+
return fresh && fresh.id === row.id && fresh.at === row.at ? fresh : row;
|
|
23684
|
+
});
|
|
23685
|
+
}
|
|
23686
|
+
var untrusted = (reasons) => reasons.some((reason) => TRUST_REASONS.has(reason));
|
|
23471
23687
|
var independenceReasons = (row, authors) => {
|
|
23472
23688
|
const reasons = [];
|
|
23473
23689
|
if (!row.actor.session)
|
|
@@ -23478,15 +23694,17 @@ var independenceReasons = (row, authors) => {
|
|
|
23478
23694
|
reasons.push("self");
|
|
23479
23695
|
return reasons;
|
|
23480
23696
|
};
|
|
23481
|
-
function effectiveOf(rows,
|
|
23697
|
+
function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
|
|
23482
23698
|
let effective = null;
|
|
23483
23699
|
for (const row of rows) {
|
|
23484
23700
|
if (!effective) {
|
|
23485
23701
|
effective = row;
|
|
23486
23702
|
continue;
|
|
23487
23703
|
}
|
|
23488
|
-
|
|
23489
|
-
|
|
23704
|
+
if (untrusted(reasonsOf(row)) && !untrusted(reasonsOf(effective)))
|
|
23705
|
+
continue;
|
|
23706
|
+
const rowIndependent = reasonsOf(row).length === 0;
|
|
23707
|
+
const effIndependent = reasonsOf(effective).length === 0;
|
|
23490
23708
|
if (effIndependent && !rowIndependent && isCurrent(effective))
|
|
23491
23709
|
continue;
|
|
23492
23710
|
if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session && sameCode(effective, row))
|
|
@@ -23500,7 +23718,7 @@ function restackCarries(rows, branch, from, to) {
|
|
|
23500
23718
|
return false;
|
|
23501
23719
|
const edges = new Map;
|
|
23502
23720
|
for (const row of rows)
|
|
23503
|
-
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
|
|
23721
|
+
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
|
|
23504
23722
|
edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
|
|
23505
23723
|
const seen = new Set([from]);
|
|
23506
23724
|
const queue = [from];
|
|
@@ -23516,7 +23734,8 @@ function restackCarries(rows, branch, from, to) {
|
|
|
23516
23734
|
}
|
|
23517
23735
|
return false;
|
|
23518
23736
|
}
|
|
23519
|
-
function checkVerdicts(cwd, branch,
|
|
23737
|
+
function checkVerdicts(cwd, branch, ledgerRows) {
|
|
23738
|
+
const rows = withIntegrity(cwd, ledgerRows);
|
|
23520
23739
|
const head = branchHead(cwd, branch);
|
|
23521
23740
|
const fallbackBase = defaultBase(cwd);
|
|
23522
23741
|
const scope = branchScope(cwd, branch);
|
|
@@ -23546,21 +23765,47 @@ function checkVerdicts(cwd, branch, rows) {
|
|
|
23546
23765
|
return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
|
|
23547
23766
|
};
|
|
23548
23767
|
const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
|
|
23768
|
+
const failureAuthorCache = new Map;
|
|
23769
|
+
const failureAuthors = (row) => {
|
|
23770
|
+
let set = failureAuthorCache.get(row.seq);
|
|
23771
|
+
if (!set) {
|
|
23772
|
+
set = authorSessions(cwd, rows.filter((other) => other.integrity === "verified" && other.seq < row.seq), branch, { base: null, head: null }, scope);
|
|
23773
|
+
failureAuthorCache.set(row.seq, set);
|
|
23774
|
+
}
|
|
23775
|
+
return set;
|
|
23776
|
+
};
|
|
23777
|
+
const reasonCache = new Map;
|
|
23778
|
+
const reasonsOf = (row) => {
|
|
23779
|
+
let reasons = reasonCache.get(row);
|
|
23780
|
+
if (!reasons) {
|
|
23781
|
+
const failing = FAILING.has(String(row.result));
|
|
23782
|
+
reasons = [
|
|
23783
|
+
...trustReasons(cwd, row),
|
|
23784
|
+
...independenceReasons(row, failing ? failureAuthors(row) : authors)
|
|
23785
|
+
];
|
|
23786
|
+
reasonCache.set(row, reasons);
|
|
23787
|
+
}
|
|
23788
|
+
return reasons;
|
|
23789
|
+
};
|
|
23790
|
+
const retired = new Set;
|
|
23791
|
+
for (const row of rows)
|
|
23792
|
+
if (row.type === "verdict" && row.supersedes && !row.supersedeIgnored && trustReasons(cwd, row).length === 0)
|
|
23793
|
+
retired.add(row.supersedes);
|
|
23549
23794
|
const byKind = new Map;
|
|
23550
23795
|
for (const row of rows)
|
|
23551
|
-
if (isLiveVerdict(row) && scope(row)) {
|
|
23796
|
+
if (isLiveVerdict(row, retired) && scope(row)) {
|
|
23552
23797
|
const kind = typeof row.kind === "string" ? row.kind : "review";
|
|
23553
23798
|
byKind.set(kind, [...byKind.get(kind) ?? [], row]);
|
|
23554
23799
|
}
|
|
23555
23800
|
const verdicts = [];
|
|
23556
23801
|
const warnings = [];
|
|
23557
23802
|
for (const [kind, list] of byKind) {
|
|
23558
|
-
const row = effectiveOf(list,
|
|
23803
|
+
const row = effectiveOf(list, reasonsOf, sameCode, (candidate) => basisOf(candidate) !== "stale");
|
|
23559
23804
|
if (!row)
|
|
23560
23805
|
continue;
|
|
23561
23806
|
const basis = basisOf(row);
|
|
23562
23807
|
const current = basis !== "stale";
|
|
23563
|
-
const independence =
|
|
23808
|
+
const independence = reasonsOf(row);
|
|
23564
23809
|
const reasons = [
|
|
23565
23810
|
...current ? [] : ["stale"],
|
|
23566
23811
|
...PASSING.has(String(row.result)) ? [] : ["not_passing"],
|
|
@@ -23577,23 +23822,31 @@ function checkVerdicts(cwd, branch, rows) {
|
|
|
23577
23822
|
});
|
|
23578
23823
|
if (reasons.length === 0) {
|
|
23579
23824
|
for (const later of list)
|
|
23580
|
-
if (later.seq > row.seq &&
|
|
23825
|
+
if (later.seq > row.seq && reasonsOf(later).length > 0 && !untrusted(reasonsOf(later)) && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
|
|
23581
23826
|
warnings.push(`the author's own (self) ${String(later.result)} ${kind} verdict ${later.id} by ${later.actor.session ?? "no session"} on the current code is newer than the accepted pass ${row.id}: read it before merging`);
|
|
23582
23827
|
}
|
|
23583
23828
|
}
|
|
23584
23829
|
for (const row of rows)
|
|
23585
23830
|
if (row.integrity === "unverified" && scope(row))
|
|
23586
23831
|
warnings.push(`ledger row ${row.id} (${row.type}) on ${row.branch ?? branch} is unverified: the workit CLI did not write it or it changed afterwards (workit ledger verify-integrity)`);
|
|
23832
|
+
const tampered = ledgerTampering(cwd);
|
|
23833
|
+
if (tampered.length) {
|
|
23834
|
+
warnings.push(tamperedWarning(cwd, tampered));
|
|
23835
|
+
for (const entry of verdicts) {
|
|
23836
|
+
entry.accepted = false;
|
|
23837
|
+
entry.reasons = [...entry.reasons, "ledger_tampered"];
|
|
23838
|
+
}
|
|
23839
|
+
}
|
|
23587
23840
|
verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
|
|
23588
23841
|
const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
|
|
23589
23842
|
const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
|
|
23590
23843
|
const newestAccepted = verdicts.findLast((entry) => entry.accepted && STRONG_RESULTS.has(String(entry.verdict.result))) ?? null;
|
|
23591
|
-
const accepted = newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
23844
|
+
const accepted = tampered.length ? { accepted: false, verdict: null, reasons: ["ledger_tampered"] } : newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
23592
23845
|
accepted: false,
|
|
23593
23846
|
verdict: failing?.verdict ?? null,
|
|
23594
23847
|
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
23595
23848
|
};
|
|
23596
|
-
const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
23849
|
+
const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
23597
23850
|
return {
|
|
23598
23851
|
branch,
|
|
23599
23852
|
head,
|
|
@@ -23756,7 +24009,7 @@ var productDecision = (task, ledger) => {
|
|
|
23756
24009
|
};
|
|
23757
24010
|
};
|
|
23758
24011
|
var planRecorded = (task, root) => {
|
|
23759
|
-
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(
|
|
24012
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path15.resolve(root, ref.path)));
|
|
23760
24013
|
if (cited)
|
|
23761
24014
|
return {
|
|
23762
24015
|
met: true,
|
|
@@ -23804,15 +24057,15 @@ function derivedRuleStatus(task, requirement, root, ledger) {
|
|
|
23804
24057
|
}
|
|
23805
24058
|
|
|
23806
24059
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
23807
|
-
var digestBytes = (value) =>
|
|
23808
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
23809
|
-
var relative = (root, candidate) =>
|
|
24060
|
+
var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
|
|
24061
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path16.sep}`);
|
|
24062
|
+
var relative = (root, candidate) => path16.relative(root, candidate).split(path16.sep).join("/") || ".";
|
|
23810
24063
|
var canonicalPath = (value) => {
|
|
23811
24064
|
if (typeof value !== "string" || !value)
|
|
23812
24065
|
return null;
|
|
23813
24066
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
23814
24067
|
return null;
|
|
23815
|
-
const normalized =
|
|
24068
|
+
const normalized = path16.posix.normalize(value);
|
|
23816
24069
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
23817
24070
|
return null;
|
|
23818
24071
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -23860,21 +24113,21 @@ var gitPaths = (root) => {
|
|
|
23860
24113
|
git: !/not a git repository/i.test(stderr)
|
|
23861
24114
|
};
|
|
23862
24115
|
}
|
|
23863
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
24116
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")) : [];
|
|
23864
24117
|
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
23865
24118
|
if (stagedDeleted.status !== 0) {
|
|
23866
24119
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
23867
24120
|
if (!/not a git repository/i.test(stderr))
|
|
23868
24121
|
return { paths, uncertain: true, git: true };
|
|
23869
24122
|
} else if (stagedDeleted.stdout) {
|
|
23870
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
24123
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")));
|
|
23871
24124
|
}
|
|
23872
24125
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
23873
24126
|
};
|
|
23874
24127
|
var walk = (root, directory, output) => {
|
|
23875
24128
|
let names;
|
|
23876
24129
|
try {
|
|
23877
|
-
names =
|
|
24130
|
+
names = fs13.readdirSync(directory);
|
|
23878
24131
|
} catch {
|
|
23879
24132
|
return true;
|
|
23880
24133
|
}
|
|
@@ -23882,10 +24135,10 @@ var walk = (root, directory, output) => {
|
|
|
23882
24135
|
for (const name of names) {
|
|
23883
24136
|
if (name === ".git" || name === ".workit")
|
|
23884
24137
|
continue;
|
|
23885
|
-
const target =
|
|
24138
|
+
const target = path16.join(directory, name);
|
|
23886
24139
|
let stat;
|
|
23887
24140
|
try {
|
|
23888
|
-
stat =
|
|
24141
|
+
stat = fs13.lstatSync(target);
|
|
23889
24142
|
} catch {
|
|
23890
24143
|
uncertain = true;
|
|
23891
24144
|
continue;
|
|
@@ -23900,14 +24153,14 @@ var walk = (root, directory, output) => {
|
|
|
23900
24153
|
var scopeRoots = (root, scope) => {
|
|
23901
24154
|
const roots = [];
|
|
23902
24155
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
23903
|
-
const target =
|
|
24156
|
+
const target = path16.resolve(root, item);
|
|
23904
24157
|
if (!inside(root, target))
|
|
23905
24158
|
return failure2("invalid_input", `candidate scope escapes checkout: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} is outside the checkout` }] });
|
|
23906
24159
|
let ancestor = target;
|
|
23907
|
-
while (!
|
|
23908
|
-
ancestor =
|
|
24160
|
+
while (!fs13.existsSync(ancestor) && ancestor !== root)
|
|
24161
|
+
ancestor = path16.dirname(ancestor);
|
|
23909
24162
|
try {
|
|
23910
|
-
if (!inside(root,
|
|
24163
|
+
if (!inside(root, fs13.realpathSync(ancestor)))
|
|
23911
24164
|
return failure2("invalid_input", `candidate scope escapes checkout through a symlink: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} resolves outside the checkout` }] });
|
|
23912
24165
|
} catch {
|
|
23913
24166
|
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
@@ -23919,7 +24172,7 @@ var scopeRoots = (root, scope) => {
|
|
|
23919
24172
|
function captureCandidate(root, scope, environment = []) {
|
|
23920
24173
|
let checkout;
|
|
23921
24174
|
try {
|
|
23922
|
-
checkout =
|
|
24175
|
+
checkout = fs13.realpathSync(root);
|
|
23923
24176
|
} catch {
|
|
23924
24177
|
return failure2("invalid_input", "candidate root does not exist");
|
|
23925
24178
|
}
|
|
@@ -23937,11 +24190,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
23937
24190
|
if (item === ".git" || item === ".workit")
|
|
23938
24191
|
continue;
|
|
23939
24192
|
names.add(item);
|
|
23940
|
-
if (!
|
|
24193
|
+
if (!fs13.existsSync(target))
|
|
23941
24194
|
continue;
|
|
23942
24195
|
let stat;
|
|
23943
24196
|
try {
|
|
23944
|
-
stat =
|
|
24197
|
+
stat = fs13.lstatSync(target);
|
|
23945
24198
|
} catch {
|
|
23946
24199
|
uncertain = true;
|
|
23947
24200
|
continue;
|
|
@@ -23957,17 +24210,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
23957
24210
|
for (const item of [...names].toSorted()) {
|
|
23958
24211
|
if (!scopeMatches(item, normalizedScope))
|
|
23959
24212
|
continue;
|
|
23960
|
-
const target =
|
|
24213
|
+
const target = path16.join(checkout, item);
|
|
23961
24214
|
try {
|
|
23962
|
-
const stat =
|
|
24215
|
+
const stat = fs13.lstatSync(target);
|
|
23963
24216
|
if (stat.isSymbolicLink()) {
|
|
23964
|
-
const link =
|
|
24217
|
+
const link = fs13.readlinkSync(target);
|
|
23965
24218
|
files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
|
|
23966
24219
|
} else if (stat.isFile()) {
|
|
23967
24220
|
files.push({
|
|
23968
24221
|
path: item,
|
|
23969
24222
|
kind: "file",
|
|
23970
|
-
digest: digestBytes(
|
|
24223
|
+
digest: digestBytes(fs13.readFileSync(target)),
|
|
23971
24224
|
executable: (stat.mode & 73) !== 0
|
|
23972
24225
|
});
|
|
23973
24226
|
} else if (!stat.isDirectory()) {
|
|
@@ -24132,7 +24385,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
|
|
|
24132
24385
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
24133
24386
|
let root;
|
|
24134
24387
|
try {
|
|
24135
|
-
root =
|
|
24388
|
+
root = fs13.realpathSync(checkoutRoot);
|
|
24136
24389
|
} catch {
|
|
24137
24390
|
return failure2("permission_denied", "checkout root is unavailable");
|
|
24138
24391
|
}
|
|
@@ -24141,23 +24394,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
24141
24394
|
continue;
|
|
24142
24395
|
if (!reference.digest)
|
|
24143
24396
|
return failure2("invalid_input", "document references require a byte digest");
|
|
24144
|
-
const target =
|
|
24145
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
24397
|
+
const target = path16.resolve(root, reference.path);
|
|
24398
|
+
if (target !== root && !target.startsWith(`${root}${path16.sep}`))
|
|
24146
24399
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
24147
24400
|
fields: [
|
|
24148
24401
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
24149
24402
|
]
|
|
24150
24403
|
});
|
|
24151
24404
|
try {
|
|
24152
|
-
const below =
|
|
24405
|
+
const below = path16.relative(root, target);
|
|
24153
24406
|
let current = root;
|
|
24154
|
-
for (const segment of below.split(
|
|
24407
|
+
for (const segment of below.split(path16.sep)) {
|
|
24155
24408
|
if (!segment)
|
|
24156
24409
|
continue;
|
|
24157
|
-
current =
|
|
24158
|
-
const stat =
|
|
24159
|
-
const real =
|
|
24160
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
24410
|
+
current = path16.join(current, segment);
|
|
24411
|
+
const stat = fs13.lstatSync(current);
|
|
24412
|
+
const real = fs13.realpathSync(current);
|
|
24413
|
+
if (real !== root && !real.startsWith(`${root}${path16.sep}`))
|
|
24161
24414
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
24162
24415
|
fields: [
|
|
24163
24416
|
{
|
|
@@ -24173,7 +24426,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
24173
24426
|
if (current !== target && !stat.isDirectory())
|
|
24174
24427
|
return failure2("invalid_input", "document reference ancestor is not a directory");
|
|
24175
24428
|
}
|
|
24176
|
-
if (digestBytes(
|
|
24429
|
+
if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
|
|
24177
24430
|
return failure2("permission_denied", "decision document bytes have changed");
|
|
24178
24431
|
} catch {
|
|
24179
24432
|
return failure2("invalid_input", "decision document is unavailable");
|
|
@@ -25031,7 +25284,7 @@ var judgeInputSummary = (raw) => {
|
|
|
25031
25284
|
|
|
25032
25285
|
// packages/workit-core/src/autonomy.ts
|
|
25033
25286
|
import os4 from "node:os";
|
|
25034
|
-
import
|
|
25287
|
+
import path17 from "node:path";
|
|
25035
25288
|
var accountHome = () => {
|
|
25036
25289
|
try {
|
|
25037
25290
|
return os4.userInfo().homedir;
|
|
@@ -25040,10 +25293,10 @@ var accountHome = () => {
|
|
|
25040
25293
|
}
|
|
25041
25294
|
};
|
|
25042
25295
|
var grantsHome = { resolve: accountHome };
|
|
25043
|
-
var grantsDir = () =>
|
|
25296
|
+
var grantsDir = () => path17.join(grantsHome.resolve(), ".config", "workit");
|
|
25044
25297
|
var grantsOverride = () => {
|
|
25045
|
-
const configured =
|
|
25046
|
-
return configured ===
|
|
25298
|
+
const configured = path17.resolve(resolveConfigDir());
|
|
25299
|
+
return configured === path17.resolve(grantsDir()) ? null : `the config directory is redirected to ${configured} (WORKFLOW_TOOLKIT_CONFIG, WORKFLOW_TOOLKIT_CONFIG_DIR or XDG_CONFIG_HOME); grants are read only from ${path17.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
|
|
25047
25300
|
};
|
|
25048
25301
|
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
25049
25302
|
var DEFAULT_GRANTS = Object.freeze({
|
|
@@ -26815,7 +27068,7 @@ class WorkitCore {
|
|
|
26815
27068
|
// packages/workit-core/src/core/repo-context.ts
|
|
26816
27069
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
26817
27070
|
import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
|
|
26818
|
-
import
|
|
27071
|
+
import path18 from "node:path";
|
|
26819
27072
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
26820
27073
|
try {
|
|
26821
27074
|
gitRevisionParts(value);
|
|
@@ -26949,7 +27202,7 @@ var findPrTemplate = (cwd) => {
|
|
|
26949
27202
|
]) {
|
|
26950
27203
|
const parts = rel.split("/");
|
|
26951
27204
|
const base = parts[parts.length - 1];
|
|
26952
|
-
const dir = parts.length > 1 ?
|
|
27205
|
+
const dir = parts.length > 1 ? path18.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
26953
27206
|
const actual = probeCaseInsensitive(dir, base);
|
|
26954
27207
|
if (actual)
|
|
26955
27208
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -26980,7 +27233,7 @@ var diffStatForRange = (cwd, range) => {
|
|
|
26980
27233
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
26981
27234
|
};
|
|
26982
27235
|
var packageScripts = (cwd, keys) => {
|
|
26983
|
-
const pkgPath =
|
|
27236
|
+
const pkgPath = path18.join(cwd, "package.json");
|
|
26984
27237
|
if (!existsSync6(pkgPath))
|
|
26985
27238
|
return [];
|
|
26986
27239
|
try {
|
|
@@ -27006,9 +27259,9 @@ var documentationFiles = (cwd) => {
|
|
|
27006
27259
|
if (entry.isDirectory()) {
|
|
27007
27260
|
if (excluded.has(entry.name))
|
|
27008
27261
|
continue;
|
|
27009
|
-
walk(
|
|
27262
|
+
walk(path18.join(dir, entry.name), depth + 1);
|
|
27010
27263
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
27011
|
-
matches.push("./" +
|
|
27264
|
+
matches.push("./" + path18.relative(cwd, path18.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
27012
27265
|
}
|
|
27013
27266
|
}
|
|
27014
27267
|
};
|
|
@@ -27024,7 +27277,7 @@ var readTrimmed2 = (file, maxLines) => {
|
|
|
27024
27277
|
`);
|
|
27025
27278
|
};
|
|
27026
27279
|
function prReadyContext(root, range) {
|
|
27027
|
-
const cwd =
|
|
27280
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
27028
27281
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
27029
27282
|
return invalidRange(cwd);
|
|
27030
27283
|
let stdout = "";
|
|
@@ -27082,7 +27335,7 @@ ${status.stderr}` : "");
|
|
|
27082
27335
|
stdout += `template_path: ${template}
|
|
27083
27336
|
|
|
27084
27337
|
`;
|
|
27085
|
-
stdout += readTrimmed2(
|
|
27338
|
+
stdout += readTrimmed2(path18.join(cwd, template), 220);
|
|
27086
27339
|
} else {
|
|
27087
27340
|
stdout += `template_path: none
|
|
27088
27341
|
|
|
@@ -27092,7 +27345,7 @@ ${status.stderr}` : "");
|
|
|
27092
27345
|
stdout += `
|
|
27093
27346
|
`;
|
|
27094
27347
|
stdout += printSection("Recent Validation Signals");
|
|
27095
|
-
if (existsSync6(
|
|
27348
|
+
if (existsSync6(path18.join(cwd, "package.json"))) {
|
|
27096
27349
|
stdout += `package.json detected. Common scripts:
|
|
27097
27350
|
`;
|
|
27098
27351
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -27101,7 +27354,7 @@ ${status.stderr}` : "");
|
|
|
27101
27354
|
`) + `
|
|
27102
27355
|
`;
|
|
27103
27356
|
}
|
|
27104
|
-
if (existsSync6(
|
|
27357
|
+
if (existsSync6(path18.join(cwd, "Cargo.toml")) || existsSync6(path18.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
27105
27358
|
stdout += `Rust project detected.
|
|
27106
27359
|
`;
|
|
27107
27360
|
}
|
|
@@ -27134,7 +27387,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
27134
27387
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
27135
27388
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
27136
27389
|
function changelogContext(root, range) {
|
|
27137
|
-
const cwd =
|
|
27390
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
27138
27391
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
27139
27392
|
return invalidRange(cwd);
|
|
27140
27393
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -27152,7 +27405,7 @@ function changelogContext(root, range) {
|
|
|
27152
27405
|
stdout += CHANGELOG_RULES + `
|
|
27153
27406
|
`;
|
|
27154
27407
|
stdout += printSection("Existing CHANGELOG.md");
|
|
27155
|
-
const changelogPath =
|
|
27408
|
+
const changelogPath = path18.join(cwd, "CHANGELOG.md");
|
|
27156
27409
|
if (existsSync6(changelogPath)) {
|
|
27157
27410
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
27158
27411
|
`;
|
|
@@ -27172,7 +27425,7 @@ function changelogContext(root, range) {
|
|
|
27172
27425
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
27173
27426
|
}
|
|
27174
27427
|
function docsRefreshContext(root, range) {
|
|
27175
|
-
const cwd =
|
|
27428
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
27176
27429
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
27177
27430
|
return invalidRange(cwd);
|
|
27178
27431
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -27194,7 +27447,7 @@ function docsRefreshContext(root, range) {
|
|
|
27194
27447
|
`) + `
|
|
27195
27448
|
`;
|
|
27196
27449
|
stdout += printSection("README Preview");
|
|
27197
|
-
const readme =
|
|
27450
|
+
const readme = path18.join(cwd, "README.md");
|
|
27198
27451
|
if (existsSync6(readme)) {
|
|
27199
27452
|
stdout += readTrimmed2(readme, 220) + `
|
|
27200
27453
|
`;
|
|
@@ -27203,7 +27456,7 @@ function docsRefreshContext(root, range) {
|
|
|
27203
27456
|
`;
|
|
27204
27457
|
}
|
|
27205
27458
|
stdout += printSection("Package Scripts");
|
|
27206
|
-
const pkgPath =
|
|
27459
|
+
const pkgPath = path18.join(cwd, "package.json");
|
|
27207
27460
|
if (existsSync6(pkgPath)) {
|
|
27208
27461
|
try {
|
|
27209
27462
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
@@ -27217,7 +27470,7 @@ function docsRefreshContext(root, range) {
|
|
|
27217
27470
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
27218
27471
|
}
|
|
27219
27472
|
function releaseNotesContext(root, rangeOrTag) {
|
|
27220
|
-
const cwd =
|
|
27473
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
27221
27474
|
if (!rangeOrTag) {
|
|
27222
27475
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
27223
27476
|
`, exitCode: 1, cwd };
|
|
@@ -27255,7 +27508,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
27255
27508
|
`;
|
|
27256
27509
|
stdout += printSection("Existing Release Files");
|
|
27257
27510
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
27258
|
-
if (existsSync6(
|
|
27511
|
+
if (existsSync6(path18.join(cwd, rel)))
|
|
27259
27512
|
stdout += rel + `
|
|
27260
27513
|
`;
|
|
27261
27514
|
}
|
|
@@ -27286,8 +27539,8 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
27286
27539
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
27287
27540
|
|
|
27288
27541
|
// packages/workit-core/src/core/pr-create.ts
|
|
27289
|
-
import
|
|
27290
|
-
import
|
|
27542
|
+
import fs14 from "node:fs";
|
|
27543
|
+
import path19 from "node:path";
|
|
27291
27544
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
27292
27545
|
// packages/workit-core/src/forge/pr-body.ts
|
|
27293
27546
|
function parseGhIssue(value) {
|
|
@@ -27298,13 +27551,13 @@ function parseGhIssue(value) {
|
|
|
27298
27551
|
// packages/workit-core/src/core/pr-create.ts
|
|
27299
27552
|
function whichOnPath(tool) {
|
|
27300
27553
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
27301
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
27554
|
+
for (const dir of (process.env.PATH ?? "").split(path19.delimiter)) {
|
|
27302
27555
|
if (!dir)
|
|
27303
27556
|
continue;
|
|
27304
27557
|
for (const name of names) {
|
|
27305
|
-
const candidate =
|
|
27558
|
+
const candidate = path19.join(dir, name);
|
|
27306
27559
|
try {
|
|
27307
|
-
|
|
27560
|
+
fs14.accessSync(candidate, fs14.constants.X_OK);
|
|
27308
27561
|
return candidate;
|
|
27309
27562
|
} catch {}
|
|
27310
27563
|
}
|
|
@@ -27422,8 +27675,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
|
27422
27675
|
};
|
|
27423
27676
|
|
|
27424
27677
|
// packages/workit-core/src/core/youtrack.ts
|
|
27425
|
-
import
|
|
27426
|
-
import
|
|
27678
|
+
import fs15 from "node:fs";
|
|
27679
|
+
import path20 from "node:path";
|
|
27427
27680
|
|
|
27428
27681
|
// packages/workit-core/src/core/templates.ts
|
|
27429
27682
|
var repoRoot2 = assetRoot();
|
|
@@ -27436,22 +27689,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
27436
27689
|
// packages/workit-core/src/core/youtrack.ts
|
|
27437
27690
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
27438
27691
|
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
27439
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
27440
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
27692
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path20.join(configDir(), "youtrack.json");
|
|
27693
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path20.join(resolveConfigDir(), "youtrack.json");
|
|
27441
27694
|
var youTrackTokenModeOk = (p) => {
|
|
27442
27695
|
if (process.platform === "win32")
|
|
27443
27696
|
return true;
|
|
27444
|
-
const mode =
|
|
27697
|
+
const mode = fs15.statSync(p).mode & 511;
|
|
27445
27698
|
return mode === 384;
|
|
27446
27699
|
};
|
|
27447
27700
|
function readYouTrackConfig(required) {
|
|
27448
27701
|
const cfgPath = youTrackConfigPath();
|
|
27449
|
-
if (!
|
|
27702
|
+
if (!fs15.existsSync(cfgPath)) {
|
|
27450
27703
|
return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
|
|
27451
27704
|
}
|
|
27452
27705
|
let parsed;
|
|
27453
27706
|
try {
|
|
27454
|
-
parsed = JSON.parse(
|
|
27707
|
+
parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
|
|
27455
27708
|
} catch {
|
|
27456
27709
|
return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
|
|
27457
27710
|
}
|
|
@@ -27462,11 +27715,11 @@ function readYouTrackConfig(required) {
|
|
|
27462
27715
|
}
|
|
27463
27716
|
var readYouTrackContextConfig = () => {
|
|
27464
27717
|
const cfgPath = youTrackReadOnlyConfigPath();
|
|
27465
|
-
if (!
|
|
27718
|
+
if (!fs15.existsSync(cfgPath))
|
|
27466
27719
|
return { data: {} };
|
|
27467
27720
|
let parsed;
|
|
27468
27721
|
try {
|
|
27469
|
-
parsed = JSON.parse(
|
|
27722
|
+
parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
|
|
27470
27723
|
} catch {
|
|
27471
27724
|
return { error: "YouTrack configuration is unavailable" };
|
|
27472
27725
|
}
|
|
@@ -27505,12 +27758,12 @@ var youTrackToken = () => {
|
|
|
27505
27758
|
if ("error" in loaded)
|
|
27506
27759
|
return loaded;
|
|
27507
27760
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
27508
|
-
const tokenPath = tokenFile ?
|
|
27509
|
-
if (!tokenPath || !
|
|
27761
|
+
const tokenPath = tokenFile ? path20.isAbsolute(tokenFile) ? path20.resolve(tokenFile) : path20.resolve(process.cwd(), tokenFile) : "";
|
|
27762
|
+
if (!tokenPath || !fs15.existsSync(tokenPath))
|
|
27510
27763
|
return { error: "missing youtrack.token" };
|
|
27511
27764
|
if (!youTrackTokenModeOk(tokenPath))
|
|
27512
27765
|
return { error: "youtrack.token mode must be 0600" };
|
|
27513
|
-
const token =
|
|
27766
|
+
const token = fs15.readFileSync(tokenPath, "utf8").trim();
|
|
27514
27767
|
if (!token)
|
|
27515
27768
|
return { error: "empty token file" };
|
|
27516
27769
|
if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
|
|
@@ -27575,9 +27828,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
|
|
|
27575
27828
|
} catch {
|
|
27576
27829
|
continue;
|
|
27577
27830
|
}
|
|
27578
|
-
if (!
|
|
27831
|
+
if (!fs15.existsSync(full))
|
|
27579
27832
|
continue;
|
|
27580
|
-
const text =
|
|
27833
|
+
const text = fs15.readFileSync(full, "utf8");
|
|
27581
27834
|
const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
|
|
27582
27835
|
if (m)
|
|
27583
27836
|
return m[1];
|
|
@@ -27847,15 +28100,15 @@ function resolveGitRevision(root, value) {
|
|
|
27847
28100
|
}
|
|
27848
28101
|
function resolveInside(root, candidate) {
|
|
27849
28102
|
const base = realpathSync5(root);
|
|
27850
|
-
const target =
|
|
27851
|
-
if (target !== base && !target.startsWith(base +
|
|
28103
|
+
const target = path21.resolve(base, candidate);
|
|
28104
|
+
if (target !== base && !target.startsWith(base + path21.sep)) {
|
|
27852
28105
|
throw new Error("path must stay inside repository root");
|
|
27853
28106
|
}
|
|
27854
28107
|
let ancestor = target;
|
|
27855
28108
|
while (!existsSync7(ancestor))
|
|
27856
|
-
ancestor =
|
|
28109
|
+
ancestor = path21.dirname(ancestor);
|
|
27857
28110
|
const canonicalAncestor = realpathSync5(ancestor);
|
|
27858
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
28111
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path21.sep)) {
|
|
27859
28112
|
throw new Error("path must stay inside repository root");
|
|
27860
28113
|
}
|
|
27861
28114
|
return target;
|
|
@@ -27913,13 +28166,13 @@ var rootVariants = (workspaceRoot) => {
|
|
|
27913
28166
|
if (!workspaceRoot)
|
|
27914
28167
|
return [];
|
|
27915
28168
|
const roots = new Set;
|
|
27916
|
-
for (const candidate of [workspaceRoot,
|
|
27917
|
-
if (
|
|
28169
|
+
for (const candidate of [workspaceRoot, path22.resolve(workspaceRoot)]) {
|
|
28170
|
+
if (path22.isAbsolute(candidate) && candidate !== path22.parse(candidate).root)
|
|
27918
28171
|
roots.add(candidate);
|
|
27919
28172
|
}
|
|
27920
28173
|
try {
|
|
27921
28174
|
const canonical = realpathSync6(workspaceRoot);
|
|
27922
|
-
if (canonical !==
|
|
28175
|
+
if (canonical !== path22.parse(canonical).root)
|
|
27923
28176
|
roots.add(canonical);
|
|
27924
28177
|
} catch {}
|
|
27925
28178
|
return [...roots].toSorted((left, right) => right.length - left.length);
|
|
@@ -28001,11 +28254,11 @@ var thrownResult = (tool, error, workspaceRoot) => {
|
|
|
28001
28254
|
};
|
|
28002
28255
|
function createMcpServer(host, contextProvider) {
|
|
28003
28256
|
assertMcpHost(host);
|
|
28004
|
-
const here =
|
|
28257
|
+
const here = path22.dirname(fileURLToPath2(import.meta.url));
|
|
28005
28258
|
const sourceMarker = markSourcesLoaded([
|
|
28006
|
-
|
|
28007
|
-
|
|
28008
|
-
|
|
28259
|
+
path22.join(here, "server.ts"),
|
|
28260
|
+
path22.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
|
|
28261
|
+
path22.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
|
|
28009
28262
|
].filter((file) => existsSync8(file)));
|
|
28010
28263
|
let staleWarned = false;
|
|
28011
28264
|
const server = new Server({ name: "workit", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
|
|
@@ -28185,8 +28438,8 @@ var main = async () => {
|
|
|
28185
28438
|
process.exitCode = 2;
|
|
28186
28439
|
}
|
|
28187
28440
|
};
|
|
28188
|
-
var entryPath = process.argv[1] ?
|
|
28189
|
-
if (entryPath ===
|
|
28441
|
+
var entryPath = process.argv[1] ? path23.resolve(process.argv[1]) : "";
|
|
28442
|
+
if (entryPath === path23.resolve(fileURLToPath3(import.meta.url)))
|
|
28190
28443
|
await main();
|
|
28191
28444
|
export {
|
|
28192
28445
|
McpCapabilityUnavailableError,
|