@brainervirus/workit-mcp 8.13.0 → 8.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +630 -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"],
|
|
@@ -21910,7 +22017,7 @@ var workspacesFileSchema = object2({ workspaces: array(workspaceConfigSchema).op
|
|
|
21910
22017
|
names.add(workspace.name);
|
|
21911
22018
|
}
|
|
21912
22019
|
});
|
|
21913
|
-
var workspacesRevision = (contents) =>
|
|
22020
|
+
var workspacesRevision = (contents) => createHash7("sha256").update(contents ?? "<missing>").digest("hex");
|
|
21914
22021
|
var validateWorkspacesDocument = (parsed, file) => {
|
|
21915
22022
|
if (!isConfigObject(parsed)) {
|
|
21916
22023
|
return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
|
|
@@ -21948,7 +22055,7 @@ var validateWorkspacesDocument = (parsed, file) => {
|
|
|
21948
22055
|
};
|
|
21949
22056
|
};
|
|
21950
22057
|
var readWorkspacesResult = (dir = configDir()) => {
|
|
21951
|
-
const file =
|
|
22058
|
+
const file = path12.join(dir, "workspaces.json");
|
|
21952
22059
|
let raw;
|
|
21953
22060
|
try {
|
|
21954
22061
|
raw = readFileSync3(file, "utf8");
|
|
@@ -22059,9 +22166,9 @@ var mainCheckoutOf = (cwd) => {
|
|
|
22059
22166
|
let main = null;
|
|
22060
22167
|
const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
|
|
22061
22168
|
const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
|
|
22062
|
-
if (gitDir && common &&
|
|
22169
|
+
if (gitDir && common && path12.resolve(gitDir) !== path12.resolve(common)) {
|
|
22063
22170
|
const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
|
|
22064
|
-
main = listed?.trim() || (
|
|
22171
|
+
main = listed?.trim() || (path12.basename(common) === ".git" ? path12.dirname(common) : common);
|
|
22065
22172
|
}
|
|
22066
22173
|
mainCheckouts.set(cwd, main);
|
|
22067
22174
|
return main;
|
|
@@ -22152,7 +22259,7 @@ var runtimeWorkspaceIndexSchema = object2({
|
|
|
22152
22259
|
workspaces: array(object2({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
22153
22260
|
}).passthrough();
|
|
22154
22261
|
var matchRuntimeWorkspace = (cwd, dir = configDir()) => {
|
|
22155
|
-
const file =
|
|
22262
|
+
const file = path12.join(dir, "workspaces.json");
|
|
22156
22263
|
let raw;
|
|
22157
22264
|
try {
|
|
22158
22265
|
raw = readFileSync3(file, "utf8");
|
|
@@ -22227,7 +22334,7 @@ function workspaceDefaultTarget(cwd, globalDefault) {
|
|
|
22227
22334
|
}
|
|
22228
22335
|
|
|
22229
22336
|
// packages/workit-core/src/core/release-tracks.ts
|
|
22230
|
-
import { createHash as
|
|
22337
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
22231
22338
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
22232
22339
|
var KNOWN_FIELDS = new Set([
|
|
22233
22340
|
"strategy",
|
|
@@ -22597,7 +22704,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
22597
22704
|
]) ?? "",
|
|
22598
22705
|
git3(cwd, ["config", "--get", recordedBaseKey(branch)]) ?? ""
|
|
22599
22706
|
] : [];
|
|
22600
|
-
key =
|
|
22707
|
+
key = createHash8("sha256").update(JSON.stringify({ cwd, input, refs, head, own })).digest("hex");
|
|
22601
22708
|
const hit = memo2.get(key);
|
|
22602
22709
|
if (hit)
|
|
22603
22710
|
return hit;
|
|
@@ -22612,7 +22719,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
22612
22719
|
}
|
|
22613
22720
|
|
|
22614
22721
|
// packages/workit-core/src/core/vcs-config.ts
|
|
22615
|
-
var workspacesPath = () =>
|
|
22722
|
+
var workspacesPath = () => path13.join(configDir(), "workspaces.json");
|
|
22616
22723
|
var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
22617
22724
|
var remoteProvider = (cwd) => {
|
|
22618
22725
|
const r = spawnSync8("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
|
|
@@ -22702,7 +22809,7 @@ var readVcsConfig = () => {
|
|
|
22702
22809
|
const file = vcsConfigPath();
|
|
22703
22810
|
let raw;
|
|
22704
22811
|
try {
|
|
22705
|
-
raw =
|
|
22812
|
+
raw = fs11.readFileSync(file, "utf8");
|
|
22706
22813
|
} catch {
|
|
22707
22814
|
return { status: "missing", path: file, config: {} };
|
|
22708
22815
|
}
|
|
@@ -22787,7 +22894,7 @@ function vcsConfig(mode, cwd, options = {}) {
|
|
|
22787
22894
|
const prov = cfg[provider] ?? {};
|
|
22788
22895
|
const out = {
|
|
22789
22896
|
ok: true,
|
|
22790
|
-
configPath:
|
|
22897
|
+
configPath: path13.resolve(cfgPath),
|
|
22791
22898
|
provider,
|
|
22792
22899
|
defaultTargetBranch: workspaceDefault,
|
|
22793
22900
|
pr: cfg.pr ?? {},
|
|
@@ -22998,7 +23105,7 @@ function diffHash(cwd, base, head) {
|
|
|
22998
23105
|
], GIT_TIMEOUTS.worktree);
|
|
22999
23106
|
if (!diff.ok || !diff.stdout)
|
|
23000
23107
|
return null;
|
|
23001
|
-
const hash =
|
|
23108
|
+
const hash = createHash9("sha256");
|
|
23002
23109
|
for (const line of diff.stdout.split(`
|
|
23003
23110
|
`)) {
|
|
23004
23111
|
if (line.startsWith("index "))
|
|
@@ -23010,16 +23117,16 @@ function diffHash(cwd, base, head) {
|
|
|
23010
23117
|
return `sha256:${hash.digest("hex")}`;
|
|
23011
23118
|
}
|
|
23012
23119
|
function storeRoot(cwd) {
|
|
23013
|
-
const location = resolveStore(
|
|
23120
|
+
const location = resolveStore(path14.resolve(cwd));
|
|
23014
23121
|
if (location instanceof Error)
|
|
23015
23122
|
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
23123
|
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
23017
23124
|
}
|
|
23018
23125
|
function ledgerPath(cwd) {
|
|
23019
23126
|
const root = storeRoot(cwd);
|
|
23020
|
-
return root.ok ? { ok: true, value:
|
|
23127
|
+
return root.ok ? { ok: true, value: path14.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
23021
23128
|
}
|
|
23022
|
-
var newId2 = () => `${Date.now().toString(36)}-${
|
|
23129
|
+
var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
|
|
23023
23130
|
var NETWORK_FS_TYPES = new Set([
|
|
23024
23131
|
26985,
|
|
23025
23132
|
20859,
|
|
@@ -23037,7 +23144,7 @@ var onNetworkFs = (dir) => {
|
|
|
23037
23144
|
if (process.platform !== "linux")
|
|
23038
23145
|
return false;
|
|
23039
23146
|
try {
|
|
23040
|
-
return isNetworkFsType(
|
|
23147
|
+
return isNetworkFsType(fs12.statfsSync(dir).type);
|
|
23041
23148
|
} catch {
|
|
23042
23149
|
return false;
|
|
23043
23150
|
}
|
|
@@ -23047,15 +23154,15 @@ var LOCK_STALE_MS = 30000;
|
|
|
23047
23154
|
var LOCK_OWNER = "owner";
|
|
23048
23155
|
var lockToken = (lock) => {
|
|
23049
23156
|
try {
|
|
23050
|
-
return
|
|
23157
|
+
return fs12.readFileSync(path14.join(lock, LOCK_OWNER), "utf8");
|
|
23051
23158
|
} catch {
|
|
23052
23159
|
return null;
|
|
23053
23160
|
}
|
|
23054
23161
|
};
|
|
23055
23162
|
var lockAgeMs = (lock) => {
|
|
23056
|
-
for (const target of [
|
|
23163
|
+
for (const target of [path14.join(lock, LOCK_OWNER), lock]) {
|
|
23057
23164
|
try {
|
|
23058
|
-
return Date.now() -
|
|
23165
|
+
return Date.now() - fs12.statSync(target).mtimeMs;
|
|
23059
23166
|
} catch {}
|
|
23060
23167
|
}
|
|
23061
23168
|
return null;
|
|
@@ -23063,55 +23170,55 @@ var lockAgeMs = (lock) => {
|
|
|
23063
23170
|
var ledgerLock = {
|
|
23064
23171
|
acquire(lock) {
|
|
23065
23172
|
try {
|
|
23066
|
-
|
|
23173
|
+
fs12.mkdirSync(lock);
|
|
23067
23174
|
} catch (error) {
|
|
23068
23175
|
if (error.code === "EEXIST")
|
|
23069
23176
|
return null;
|
|
23070
23177
|
throw error;
|
|
23071
23178
|
}
|
|
23072
|
-
const token = `${process.pid}-${
|
|
23073
|
-
|
|
23179
|
+
const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
|
|
23180
|
+
fs12.writeFileSync(path14.join(lock, LOCK_OWNER), token);
|
|
23074
23181
|
return token;
|
|
23075
23182
|
},
|
|
23076
23183
|
release(lock, token) {
|
|
23077
23184
|
if (lockToken(lock) !== token)
|
|
23078
23185
|
return false;
|
|
23079
|
-
const tomb = `${lock}.release-${
|
|
23186
|
+
const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
|
|
23080
23187
|
try {
|
|
23081
|
-
|
|
23188
|
+
fs12.renameSync(lock, tomb);
|
|
23082
23189
|
} catch {
|
|
23083
23190
|
return false;
|
|
23084
23191
|
}
|
|
23085
23192
|
if (lockToken(tomb) !== token) {
|
|
23086
23193
|
try {
|
|
23087
|
-
|
|
23194
|
+
fs12.renameSync(tomb, lock);
|
|
23088
23195
|
} catch {}
|
|
23089
23196
|
return false;
|
|
23090
23197
|
}
|
|
23091
|
-
|
|
23198
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
23092
23199
|
return true;
|
|
23093
23200
|
},
|
|
23094
23201
|
token: lockToken,
|
|
23095
23202
|
reap(lock, observed) {
|
|
23096
|
-
const tomb = `${lock}.reap-${
|
|
23203
|
+
const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
|
|
23097
23204
|
try {
|
|
23098
|
-
|
|
23205
|
+
fs12.renameSync(lock, tomb);
|
|
23099
23206
|
} catch {
|
|
23100
23207
|
return false;
|
|
23101
23208
|
}
|
|
23102
23209
|
if (lockToken(tomb) === observed) {
|
|
23103
|
-
|
|
23210
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
23104
23211
|
return true;
|
|
23105
23212
|
}
|
|
23106
23213
|
try {
|
|
23107
|
-
|
|
23214
|
+
fs12.renameSync(tomb, lock);
|
|
23108
23215
|
} catch {
|
|
23109
|
-
|
|
23216
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
23110
23217
|
}
|
|
23111
23218
|
return false;
|
|
23112
23219
|
}
|
|
23113
23220
|
};
|
|
23114
|
-
function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
23221
|
+
function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
|
|
23115
23222
|
const lock = `${file}.lock`;
|
|
23116
23223
|
const deadline = Date.now() + waitMs;
|
|
23117
23224
|
let token = null;
|
|
@@ -23133,14 +23240,14 @@ function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
|
23133
23240
|
ledgerLock.release(lock, token);
|
|
23134
23241
|
}
|
|
23135
23242
|
}
|
|
23136
|
-
var sha2562 = (text) =>
|
|
23243
|
+
var sha2562 = (text) => createHash9("sha256").update(text).digest("hex");
|
|
23137
23244
|
var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
|
|
23138
23245
|
${body}`);
|
|
23139
23246
|
var TAIL_BYTES = 64 * 1024;
|
|
23140
23247
|
var lastLine = (fd, size) => {
|
|
23141
23248
|
for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
|
|
23142
23249
|
const buffer = Buffer.alloc(span);
|
|
23143
|
-
|
|
23250
|
+
fs12.readSync(fd, buffer, 0, span, size - span);
|
|
23144
23251
|
const end = buffer.lastIndexOf(10);
|
|
23145
23252
|
if (end < 0) {
|
|
23146
23253
|
if (span === size)
|
|
@@ -23169,15 +23276,23 @@ var lastLine = (fd, size) => {
|
|
|
23169
23276
|
}
|
|
23170
23277
|
return null;
|
|
23171
23278
|
};
|
|
23279
|
+
var tighten = (fd) => {
|
|
23280
|
+
try {
|
|
23281
|
+
if ((fs12.fstatSync(fd).mode & 63) !== 0)
|
|
23282
|
+
fs12.fchmodSync(fd, 384);
|
|
23283
|
+
} catch {}
|
|
23284
|
+
};
|
|
23172
23285
|
function appendLine(file, build, options) {
|
|
23173
23286
|
const write = () => {
|
|
23174
|
-
const fd =
|
|
23287
|
+
const fd = fs12.openSync(file, "a+", 384);
|
|
23175
23288
|
try {
|
|
23176
|
-
|
|
23289
|
+
if (process.platform !== "win32")
|
|
23290
|
+
tighten(fd);
|
|
23291
|
+
const size = fs12.fstatSync(fd).size;
|
|
23177
23292
|
let fence = "";
|
|
23178
23293
|
if (size > 0) {
|
|
23179
23294
|
const last = Buffer.alloc(1);
|
|
23180
|
-
|
|
23295
|
+
fs12.readSync(fd, last, 0, 1, size - 1);
|
|
23181
23296
|
if (last[0] !== 10)
|
|
23182
23297
|
fence = `
|
|
23183
23298
|
`;
|
|
@@ -23186,17 +23301,17 @@ function appendLine(file, build, options) {
|
|
|
23186
23301
|
if (!line.ok)
|
|
23187
23302
|
return line;
|
|
23188
23303
|
const bytes = Buffer.from(fence + line.value);
|
|
23189
|
-
const written =
|
|
23304
|
+
const written = fs12.writeSync(fd, bytes);
|
|
23190
23305
|
if (written !== bytes.length)
|
|
23191
23306
|
return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
|
|
23192
23307
|
return { ok: true, value: undefined };
|
|
23193
23308
|
} finally {
|
|
23194
|
-
|
|
23309
|
+
fs12.closeSync(fd);
|
|
23195
23310
|
}
|
|
23196
23311
|
};
|
|
23197
23312
|
try {
|
|
23198
|
-
|
|
23199
|
-
if (!options.forceLock && !onNetworkFs(
|
|
23313
|
+
fs12.mkdirSync(path14.dirname(file), { recursive: true });
|
|
23314
|
+
if (!options.forceLock && !onNetworkFs(path14.dirname(file)))
|
|
23200
23315
|
return write();
|
|
23201
23316
|
const locked = withLedgerLock(file, write);
|
|
23202
23317
|
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
@@ -23264,8 +23379,10 @@ function readLedger(cwd, options = {}) {
|
|
|
23264
23379
|
if (!file.ok)
|
|
23265
23380
|
return file;
|
|
23266
23381
|
let text = "";
|
|
23382
|
+
let stat = null;
|
|
23267
23383
|
try {
|
|
23268
|
-
|
|
23384
|
+
stat = fs12.statSync(file.value);
|
|
23385
|
+
text = fs12.readFileSync(file.value, "utf8");
|
|
23269
23386
|
} catch (error) {
|
|
23270
23387
|
if (error.code === "ENOENT")
|
|
23271
23388
|
return {
|
|
@@ -23284,6 +23401,7 @@ function readLedger(cwd, options = {}) {
|
|
|
23284
23401
|
const integrity = { chained: 0, legacy: 0, unverified: [] };
|
|
23285
23402
|
const earlier = new Set([null]);
|
|
23286
23403
|
let chainStarted = false;
|
|
23404
|
+
const ids = new Set;
|
|
23287
23405
|
for (const raw of text.split(`
|
|
23288
23406
|
`)) {
|
|
23289
23407
|
const trimmed = raw.trim();
|
|
@@ -23317,6 +23435,10 @@ function readLedger(cwd, options = {}) {
|
|
|
23317
23435
|
reason = "unsigned";
|
|
23318
23436
|
else
|
|
23319
23437
|
rowIntegrity = "legacy";
|
|
23438
|
+
if (options.integrity && !reason && ids.has(parsed.id))
|
|
23439
|
+
reason = "duplicate_id";
|
|
23440
|
+
if (options.integrity)
|
|
23441
|
+
ids.add(parsed.id);
|
|
23320
23442
|
if (reason)
|
|
23321
23443
|
rowIntegrity = "unverified";
|
|
23322
23444
|
const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
|
|
@@ -23340,7 +23462,8 @@ function readLedger(cwd, options = {}) {
|
|
|
23340
23462
|
seq: rows.length + 1,
|
|
23341
23463
|
superseded: false,
|
|
23342
23464
|
supersedeIgnored: false,
|
|
23343
|
-
...rowIntegrity ? { integrity: rowIntegrity } : {}
|
|
23465
|
+
...rowIntegrity ? { integrity: rowIntegrity } : {},
|
|
23466
|
+
...reason ? { integrityReason: reason } : {}
|
|
23344
23467
|
};
|
|
23345
23468
|
rows.push(row);
|
|
23346
23469
|
if (!options.integrity)
|
|
@@ -23368,18 +23491,48 @@ function readLedger(cwd, options = {}) {
|
|
|
23368
23491
|
const byId = new Map;
|
|
23369
23492
|
for (const row of rows) {
|
|
23370
23493
|
const target = row.supersedes ? byId.get(row.supersedes) : undefined;
|
|
23371
|
-
if (target && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
23494
|
+
if (target && row.integrity !== "unverified" && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
23372
23495
|
target.superseded = true;
|
|
23373
23496
|
else if (row.supersedes)
|
|
23374
23497
|
row.supersedeIgnored = true;
|
|
23375
23498
|
if (!byId.has(row.id))
|
|
23376
23499
|
byId.set(row.id, row);
|
|
23377
23500
|
}
|
|
23501
|
+
if (options.integrity && stat && Buffer.byteLength(text) === stat.size)
|
|
23502
|
+
tamperCache.set(file.value, {
|
|
23503
|
+
size: stat.size,
|
|
23504
|
+
stamp: fileStamp(stat),
|
|
23505
|
+
breaks: integrity.unverified.filter((row) => TAMPER_REASONS.has(row.reason))
|
|
23506
|
+
});
|
|
23378
23507
|
return {
|
|
23379
23508
|
ok: true,
|
|
23380
23509
|
value: { path: file.value, rows, skipped, integrity: options.integrity ? integrity : null }
|
|
23381
23510
|
};
|
|
23382
23511
|
}
|
|
23512
|
+
var TAMPER_REASONS = new Set(["hash_mismatch", "broken_link"]);
|
|
23513
|
+
var tamperCache = new Map;
|
|
23514
|
+
var fileStamp = (stat) => `${stat.ino}:${stat.mtimeMs}:${stat.ctimeMs}`;
|
|
23515
|
+
function ledgerTampering(cwd) {
|
|
23516
|
+
const file = ledgerPath(cwd);
|
|
23517
|
+
if (!file.ok)
|
|
23518
|
+
return [];
|
|
23519
|
+
try {
|
|
23520
|
+
const stat = fs12.statSync(file.value);
|
|
23521
|
+
const cached = tamperCache.get(file.value);
|
|
23522
|
+
if (cached && cached.size === stat.size && cached.stamp === fileStamp(stat))
|
|
23523
|
+
return cached.breaks;
|
|
23524
|
+
} catch {
|
|
23525
|
+
return [];
|
|
23526
|
+
}
|
|
23527
|
+
const read = readLedger(cwd, { integrity: true });
|
|
23528
|
+
return read.ok ? (read.value.integrity?.unverified ?? []).filter((row) => TAMPER_REASONS.has(row.reason)) : [];
|
|
23529
|
+
}
|
|
23530
|
+
function tamperedUnblock(cwd) {
|
|
23531
|
+
const file = ledgerPath(cwd);
|
|
23532
|
+
const where = file.ok ? file.value : "the ledger file";
|
|
23533
|
+
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`;
|
|
23534
|
+
}
|
|
23535
|
+
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
23536
|
function verdictBasis(verdict, current) {
|
|
23384
23537
|
if (verdict.head && verdict.head === current.head)
|
|
23385
23538
|
return "fresh";
|
|
@@ -23387,7 +23540,7 @@ function verdictBasis(verdict, current) {
|
|
|
23387
23540
|
return "carried";
|
|
23388
23541
|
return "stale";
|
|
23389
23542
|
}
|
|
23390
|
-
var isLiveVerdict = (row) => row.type === "verdict" && typeof row.result === "string" && !row.
|
|
23543
|
+
var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id);
|
|
23391
23544
|
function trailerSessions(cwd, base, head) {
|
|
23392
23545
|
if (!base || !head || !safeRef(base) || !safeRef(head))
|
|
23393
23546
|
return [];
|
|
@@ -23468,6 +23621,56 @@ var authoredBy = (authors, session) => {
|
|
|
23468
23621
|
return true;
|
|
23469
23622
|
return false;
|
|
23470
23623
|
};
|
|
23624
|
+
var TRUST_REASONS = new Set([
|
|
23625
|
+
"unsigned",
|
|
23626
|
+
"hash_mismatch",
|
|
23627
|
+
"broken_link",
|
|
23628
|
+
"duplicate_id",
|
|
23629
|
+
"unissued_session",
|
|
23630
|
+
"unbound_session"
|
|
23631
|
+
]);
|
|
23632
|
+
var SHELL_VOUCHED_HOSTS = new Set([
|
|
23633
|
+
"cli",
|
|
23634
|
+
"opencode",
|
|
23635
|
+
"pi",
|
|
23636
|
+
"codex_cli",
|
|
23637
|
+
"codex_desktop"
|
|
23638
|
+
]);
|
|
23639
|
+
function sessionTrust(cwd, row) {
|
|
23640
|
+
const session = row.actor.session;
|
|
23641
|
+
if (!session || row.self === true)
|
|
23642
|
+
return null;
|
|
23643
|
+
const source = typeof row.sessionSource === "string" ? row.sessionSource : "env";
|
|
23644
|
+
const host = row.actor.host;
|
|
23645
|
+
const issuedOnly = source === "flag" ? host !== "cli" : source === "env" && session.includes(":") && !SHELL_VOUCHED_HOSTS.has(host);
|
|
23646
|
+
if (!issuedOnly)
|
|
23647
|
+
return null;
|
|
23648
|
+
const binding = issuedBinding(cwd, session);
|
|
23649
|
+
if (binding === null)
|
|
23650
|
+
return "unissued_session";
|
|
23651
|
+
return binding === "token" && row.sessionBound !== true ? "unbound_session" : null;
|
|
23652
|
+
}
|
|
23653
|
+
function trustReasons(cwd, row) {
|
|
23654
|
+
if (row.integrity === "unverified")
|
|
23655
|
+
return [row.integrityReason ?? "unsigned"];
|
|
23656
|
+
if (row.integrity !== "verified")
|
|
23657
|
+
return ["unsigned"];
|
|
23658
|
+
const session = sessionTrust(cwd, row);
|
|
23659
|
+
return session ? [session] : [];
|
|
23660
|
+
}
|
|
23661
|
+
function withIntegrity(cwd, rows) {
|
|
23662
|
+
if (rows.every((row) => row.integrity !== undefined))
|
|
23663
|
+
return rows;
|
|
23664
|
+
const read = readLedger(cwd, { integrity: true });
|
|
23665
|
+
const bySeq = new Map(read.ok ? read.value.rows.map((row) => [row.seq, row]) : []);
|
|
23666
|
+
return rows.map((row) => {
|
|
23667
|
+
if (row.integrity !== undefined)
|
|
23668
|
+
return row;
|
|
23669
|
+
const fresh = bySeq.get(row.seq);
|
|
23670
|
+
return fresh && fresh.id === row.id && fresh.at === row.at ? fresh : row;
|
|
23671
|
+
});
|
|
23672
|
+
}
|
|
23673
|
+
var untrusted = (reasons) => reasons.some((reason) => TRUST_REASONS.has(reason));
|
|
23471
23674
|
var independenceReasons = (row, authors) => {
|
|
23472
23675
|
const reasons = [];
|
|
23473
23676
|
if (!row.actor.session)
|
|
@@ -23478,15 +23681,17 @@ var independenceReasons = (row, authors) => {
|
|
|
23478
23681
|
reasons.push("self");
|
|
23479
23682
|
return reasons;
|
|
23480
23683
|
};
|
|
23481
|
-
function effectiveOf(rows,
|
|
23684
|
+
function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
|
|
23482
23685
|
let effective = null;
|
|
23483
23686
|
for (const row of rows) {
|
|
23484
23687
|
if (!effective) {
|
|
23485
23688
|
effective = row;
|
|
23486
23689
|
continue;
|
|
23487
23690
|
}
|
|
23488
|
-
|
|
23489
|
-
|
|
23691
|
+
if (untrusted(reasonsOf(row)) && !untrusted(reasonsOf(effective)))
|
|
23692
|
+
continue;
|
|
23693
|
+
const rowIndependent = reasonsOf(row).length === 0;
|
|
23694
|
+
const effIndependent = reasonsOf(effective).length === 0;
|
|
23490
23695
|
if (effIndependent && !rowIndependent && isCurrent(effective))
|
|
23491
23696
|
continue;
|
|
23492
23697
|
if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session && sameCode(effective, row))
|
|
@@ -23500,7 +23705,7 @@ function restackCarries(rows, branch, from, to) {
|
|
|
23500
23705
|
return false;
|
|
23501
23706
|
const edges = new Map;
|
|
23502
23707
|
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)
|
|
23708
|
+
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
23709
|
edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
|
|
23505
23710
|
const seen = new Set([from]);
|
|
23506
23711
|
const queue = [from];
|
|
@@ -23516,7 +23721,8 @@ function restackCarries(rows, branch, from, to) {
|
|
|
23516
23721
|
}
|
|
23517
23722
|
return false;
|
|
23518
23723
|
}
|
|
23519
|
-
function checkVerdicts(cwd, branch,
|
|
23724
|
+
function checkVerdicts(cwd, branch, ledgerRows) {
|
|
23725
|
+
const rows = withIntegrity(cwd, ledgerRows);
|
|
23520
23726
|
const head = branchHead(cwd, branch);
|
|
23521
23727
|
const fallbackBase = defaultBase(cwd);
|
|
23522
23728
|
const scope = branchScope(cwd, branch);
|
|
@@ -23546,21 +23752,47 @@ function checkVerdicts(cwd, branch, rows) {
|
|
|
23546
23752
|
return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
|
|
23547
23753
|
};
|
|
23548
23754
|
const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
|
|
23755
|
+
const failureAuthorCache = new Map;
|
|
23756
|
+
const failureAuthors = (row) => {
|
|
23757
|
+
let set = failureAuthorCache.get(row.seq);
|
|
23758
|
+
if (!set) {
|
|
23759
|
+
set = authorSessions(cwd, rows.filter((other) => other.integrity === "verified" && other.seq < row.seq), branch, { base: null, head: null }, scope);
|
|
23760
|
+
failureAuthorCache.set(row.seq, set);
|
|
23761
|
+
}
|
|
23762
|
+
return set;
|
|
23763
|
+
};
|
|
23764
|
+
const reasonCache = new Map;
|
|
23765
|
+
const reasonsOf = (row) => {
|
|
23766
|
+
let reasons = reasonCache.get(row);
|
|
23767
|
+
if (!reasons) {
|
|
23768
|
+
const failing = FAILING.has(String(row.result));
|
|
23769
|
+
reasons = [
|
|
23770
|
+
...trustReasons(cwd, row),
|
|
23771
|
+
...independenceReasons(row, failing ? failureAuthors(row) : authors)
|
|
23772
|
+
];
|
|
23773
|
+
reasonCache.set(row, reasons);
|
|
23774
|
+
}
|
|
23775
|
+
return reasons;
|
|
23776
|
+
};
|
|
23777
|
+
const retired = new Set;
|
|
23778
|
+
for (const row of rows)
|
|
23779
|
+
if (row.type === "verdict" && row.supersedes && !row.supersedeIgnored && trustReasons(cwd, row).length === 0)
|
|
23780
|
+
retired.add(row.supersedes);
|
|
23549
23781
|
const byKind = new Map;
|
|
23550
23782
|
for (const row of rows)
|
|
23551
|
-
if (isLiveVerdict(row) && scope(row)) {
|
|
23783
|
+
if (isLiveVerdict(row, retired) && scope(row)) {
|
|
23552
23784
|
const kind = typeof row.kind === "string" ? row.kind : "review";
|
|
23553
23785
|
byKind.set(kind, [...byKind.get(kind) ?? [], row]);
|
|
23554
23786
|
}
|
|
23555
23787
|
const verdicts = [];
|
|
23556
23788
|
const warnings = [];
|
|
23557
23789
|
for (const [kind, list] of byKind) {
|
|
23558
|
-
const row = effectiveOf(list,
|
|
23790
|
+
const row = effectiveOf(list, reasonsOf, sameCode, (candidate) => basisOf(candidate) !== "stale");
|
|
23559
23791
|
if (!row)
|
|
23560
23792
|
continue;
|
|
23561
23793
|
const basis = basisOf(row);
|
|
23562
23794
|
const current = basis !== "stale";
|
|
23563
|
-
const independence =
|
|
23795
|
+
const independence = reasonsOf(row);
|
|
23564
23796
|
const reasons = [
|
|
23565
23797
|
...current ? [] : ["stale"],
|
|
23566
23798
|
...PASSING.has(String(row.result)) ? [] : ["not_passing"],
|
|
@@ -23577,23 +23809,31 @@ function checkVerdicts(cwd, branch, rows) {
|
|
|
23577
23809
|
});
|
|
23578
23810
|
if (reasons.length === 0) {
|
|
23579
23811
|
for (const later of list)
|
|
23580
|
-
if (later.seq > row.seq &&
|
|
23812
|
+
if (later.seq > row.seq && reasonsOf(later).length > 0 && !untrusted(reasonsOf(later)) && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
|
|
23581
23813
|
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
23814
|
}
|
|
23583
23815
|
}
|
|
23584
23816
|
for (const row of rows)
|
|
23585
23817
|
if (row.integrity === "unverified" && scope(row))
|
|
23586
23818
|
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)`);
|
|
23819
|
+
const tampered = ledgerTampering(cwd);
|
|
23820
|
+
if (tampered.length) {
|
|
23821
|
+
warnings.push(tamperedWarning(cwd, tampered));
|
|
23822
|
+
for (const entry of verdicts) {
|
|
23823
|
+
entry.accepted = false;
|
|
23824
|
+
entry.reasons = [...entry.reasons, "ledger_tampered"];
|
|
23825
|
+
}
|
|
23826
|
+
}
|
|
23587
23827
|
verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
|
|
23588
23828
|
const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
|
|
23589
23829
|
const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
|
|
23590
23830
|
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: [] } : {
|
|
23831
|
+
const accepted = tampered.length ? { accepted: false, verdict: null, reasons: ["ledger_tampered"] } : newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
23592
23832
|
accepted: false,
|
|
23593
23833
|
verdict: failing?.verdict ?? null,
|
|
23594
23834
|
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
23595
23835
|
};
|
|
23596
|
-
const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
23836
|
+
const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
23597
23837
|
return {
|
|
23598
23838
|
branch,
|
|
23599
23839
|
head,
|
|
@@ -23756,7 +23996,7 @@ var productDecision = (task, ledger) => {
|
|
|
23756
23996
|
};
|
|
23757
23997
|
};
|
|
23758
23998
|
var planRecorded = (task, root) => {
|
|
23759
|
-
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(
|
|
23999
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path15.resolve(root, ref.path)));
|
|
23760
24000
|
if (cited)
|
|
23761
24001
|
return {
|
|
23762
24002
|
met: true,
|
|
@@ -23804,15 +24044,15 @@ function derivedRuleStatus(task, requirement, root, ledger) {
|
|
|
23804
24044
|
}
|
|
23805
24045
|
|
|
23806
24046
|
// 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) =>
|
|
24047
|
+
var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
|
|
24048
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path16.sep}`);
|
|
24049
|
+
var relative = (root, candidate) => path16.relative(root, candidate).split(path16.sep).join("/") || ".";
|
|
23810
24050
|
var canonicalPath = (value) => {
|
|
23811
24051
|
if (typeof value !== "string" || !value)
|
|
23812
24052
|
return null;
|
|
23813
24053
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
23814
24054
|
return null;
|
|
23815
|
-
const normalized =
|
|
24055
|
+
const normalized = path16.posix.normalize(value);
|
|
23816
24056
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
23817
24057
|
return null;
|
|
23818
24058
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -23860,21 +24100,21 @@ var gitPaths = (root) => {
|
|
|
23860
24100
|
git: !/not a git repository/i.test(stderr)
|
|
23861
24101
|
};
|
|
23862
24102
|
}
|
|
23863
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
24103
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")) : [];
|
|
23864
24104
|
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
23865
24105
|
if (stagedDeleted.status !== 0) {
|
|
23866
24106
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
23867
24107
|
if (!/not a git repository/i.test(stderr))
|
|
23868
24108
|
return { paths, uncertain: true, git: true };
|
|
23869
24109
|
} else if (stagedDeleted.stdout) {
|
|
23870
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
24110
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")));
|
|
23871
24111
|
}
|
|
23872
24112
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
23873
24113
|
};
|
|
23874
24114
|
var walk = (root, directory, output) => {
|
|
23875
24115
|
let names;
|
|
23876
24116
|
try {
|
|
23877
|
-
names =
|
|
24117
|
+
names = fs13.readdirSync(directory);
|
|
23878
24118
|
} catch {
|
|
23879
24119
|
return true;
|
|
23880
24120
|
}
|
|
@@ -23882,10 +24122,10 @@ var walk = (root, directory, output) => {
|
|
|
23882
24122
|
for (const name of names) {
|
|
23883
24123
|
if (name === ".git" || name === ".workit")
|
|
23884
24124
|
continue;
|
|
23885
|
-
const target =
|
|
24125
|
+
const target = path16.join(directory, name);
|
|
23886
24126
|
let stat;
|
|
23887
24127
|
try {
|
|
23888
|
-
stat =
|
|
24128
|
+
stat = fs13.lstatSync(target);
|
|
23889
24129
|
} catch {
|
|
23890
24130
|
uncertain = true;
|
|
23891
24131
|
continue;
|
|
@@ -23900,14 +24140,14 @@ var walk = (root, directory, output) => {
|
|
|
23900
24140
|
var scopeRoots = (root, scope) => {
|
|
23901
24141
|
const roots = [];
|
|
23902
24142
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
23903
|
-
const target =
|
|
24143
|
+
const target = path16.resolve(root, item);
|
|
23904
24144
|
if (!inside(root, target))
|
|
23905
24145
|
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
24146
|
let ancestor = target;
|
|
23907
|
-
while (!
|
|
23908
|
-
ancestor =
|
|
24147
|
+
while (!fs13.existsSync(ancestor) && ancestor !== root)
|
|
24148
|
+
ancestor = path16.dirname(ancestor);
|
|
23909
24149
|
try {
|
|
23910
|
-
if (!inside(root,
|
|
24150
|
+
if (!inside(root, fs13.realpathSync(ancestor)))
|
|
23911
24151
|
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
24152
|
} catch {
|
|
23913
24153
|
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
@@ -23919,7 +24159,7 @@ var scopeRoots = (root, scope) => {
|
|
|
23919
24159
|
function captureCandidate(root, scope, environment = []) {
|
|
23920
24160
|
let checkout;
|
|
23921
24161
|
try {
|
|
23922
|
-
checkout =
|
|
24162
|
+
checkout = fs13.realpathSync(root);
|
|
23923
24163
|
} catch {
|
|
23924
24164
|
return failure2("invalid_input", "candidate root does not exist");
|
|
23925
24165
|
}
|
|
@@ -23937,11 +24177,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
23937
24177
|
if (item === ".git" || item === ".workit")
|
|
23938
24178
|
continue;
|
|
23939
24179
|
names.add(item);
|
|
23940
|
-
if (!
|
|
24180
|
+
if (!fs13.existsSync(target))
|
|
23941
24181
|
continue;
|
|
23942
24182
|
let stat;
|
|
23943
24183
|
try {
|
|
23944
|
-
stat =
|
|
24184
|
+
stat = fs13.lstatSync(target);
|
|
23945
24185
|
} catch {
|
|
23946
24186
|
uncertain = true;
|
|
23947
24187
|
continue;
|
|
@@ -23957,17 +24197,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
23957
24197
|
for (const item of [...names].toSorted()) {
|
|
23958
24198
|
if (!scopeMatches(item, normalizedScope))
|
|
23959
24199
|
continue;
|
|
23960
|
-
const target =
|
|
24200
|
+
const target = path16.join(checkout, item);
|
|
23961
24201
|
try {
|
|
23962
|
-
const stat =
|
|
24202
|
+
const stat = fs13.lstatSync(target);
|
|
23963
24203
|
if (stat.isSymbolicLink()) {
|
|
23964
|
-
const link =
|
|
24204
|
+
const link = fs13.readlinkSync(target);
|
|
23965
24205
|
files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
|
|
23966
24206
|
} else if (stat.isFile()) {
|
|
23967
24207
|
files.push({
|
|
23968
24208
|
path: item,
|
|
23969
24209
|
kind: "file",
|
|
23970
|
-
digest: digestBytes(
|
|
24210
|
+
digest: digestBytes(fs13.readFileSync(target)),
|
|
23971
24211
|
executable: (stat.mode & 73) !== 0
|
|
23972
24212
|
});
|
|
23973
24213
|
} else if (!stat.isDirectory()) {
|
|
@@ -24132,7 +24372,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
|
|
|
24132
24372
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
24133
24373
|
let root;
|
|
24134
24374
|
try {
|
|
24135
|
-
root =
|
|
24375
|
+
root = fs13.realpathSync(checkoutRoot);
|
|
24136
24376
|
} catch {
|
|
24137
24377
|
return failure2("permission_denied", "checkout root is unavailable");
|
|
24138
24378
|
}
|
|
@@ -24141,23 +24381,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
24141
24381
|
continue;
|
|
24142
24382
|
if (!reference.digest)
|
|
24143
24383
|
return failure2("invalid_input", "document references require a byte digest");
|
|
24144
|
-
const target =
|
|
24145
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
24384
|
+
const target = path16.resolve(root, reference.path);
|
|
24385
|
+
if (target !== root && !target.startsWith(`${root}${path16.sep}`))
|
|
24146
24386
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
24147
24387
|
fields: [
|
|
24148
24388
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
24149
24389
|
]
|
|
24150
24390
|
});
|
|
24151
24391
|
try {
|
|
24152
|
-
const below =
|
|
24392
|
+
const below = path16.relative(root, target);
|
|
24153
24393
|
let current = root;
|
|
24154
|
-
for (const segment of below.split(
|
|
24394
|
+
for (const segment of below.split(path16.sep)) {
|
|
24155
24395
|
if (!segment)
|
|
24156
24396
|
continue;
|
|
24157
|
-
current =
|
|
24158
|
-
const stat =
|
|
24159
|
-
const real =
|
|
24160
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
24397
|
+
current = path16.join(current, segment);
|
|
24398
|
+
const stat = fs13.lstatSync(current);
|
|
24399
|
+
const real = fs13.realpathSync(current);
|
|
24400
|
+
if (real !== root && !real.startsWith(`${root}${path16.sep}`))
|
|
24161
24401
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
24162
24402
|
fields: [
|
|
24163
24403
|
{
|
|
@@ -24173,7 +24413,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
24173
24413
|
if (current !== target && !stat.isDirectory())
|
|
24174
24414
|
return failure2("invalid_input", "document reference ancestor is not a directory");
|
|
24175
24415
|
}
|
|
24176
|
-
if (digestBytes(
|
|
24416
|
+
if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
|
|
24177
24417
|
return failure2("permission_denied", "decision document bytes have changed");
|
|
24178
24418
|
} catch {
|
|
24179
24419
|
return failure2("invalid_input", "decision document is unavailable");
|
|
@@ -25031,7 +25271,7 @@ var judgeInputSummary = (raw) => {
|
|
|
25031
25271
|
|
|
25032
25272
|
// packages/workit-core/src/autonomy.ts
|
|
25033
25273
|
import os4 from "node:os";
|
|
25034
|
-
import
|
|
25274
|
+
import path17 from "node:path";
|
|
25035
25275
|
var accountHome = () => {
|
|
25036
25276
|
try {
|
|
25037
25277
|
return os4.userInfo().homedir;
|
|
@@ -25040,10 +25280,10 @@ var accountHome = () => {
|
|
|
25040
25280
|
}
|
|
25041
25281
|
};
|
|
25042
25282
|
var grantsHome = { resolve: accountHome };
|
|
25043
|
-
var grantsDir = () =>
|
|
25283
|
+
var grantsDir = () => path17.join(grantsHome.resolve(), ".config", "workit");
|
|
25044
25284
|
var grantsOverride = () => {
|
|
25045
|
-
const configured =
|
|
25046
|
-
return configured ===
|
|
25285
|
+
const configured = path17.resolve(resolveConfigDir());
|
|
25286
|
+
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
25287
|
};
|
|
25048
25288
|
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
25049
25289
|
var DEFAULT_GRANTS = Object.freeze({
|
|
@@ -26815,7 +27055,7 @@ class WorkitCore {
|
|
|
26815
27055
|
// packages/workit-core/src/core/repo-context.ts
|
|
26816
27056
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
26817
27057
|
import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
|
|
26818
|
-
import
|
|
27058
|
+
import path18 from "node:path";
|
|
26819
27059
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
26820
27060
|
try {
|
|
26821
27061
|
gitRevisionParts(value);
|
|
@@ -26949,7 +27189,7 @@ var findPrTemplate = (cwd) => {
|
|
|
26949
27189
|
]) {
|
|
26950
27190
|
const parts = rel.split("/");
|
|
26951
27191
|
const base = parts[parts.length - 1];
|
|
26952
|
-
const dir = parts.length > 1 ?
|
|
27192
|
+
const dir = parts.length > 1 ? path18.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
26953
27193
|
const actual = probeCaseInsensitive(dir, base);
|
|
26954
27194
|
if (actual)
|
|
26955
27195
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -26980,7 +27220,7 @@ var diffStatForRange = (cwd, range) => {
|
|
|
26980
27220
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
26981
27221
|
};
|
|
26982
27222
|
var packageScripts = (cwd, keys) => {
|
|
26983
|
-
const pkgPath =
|
|
27223
|
+
const pkgPath = path18.join(cwd, "package.json");
|
|
26984
27224
|
if (!existsSync6(pkgPath))
|
|
26985
27225
|
return [];
|
|
26986
27226
|
try {
|
|
@@ -27006,9 +27246,9 @@ var documentationFiles = (cwd) => {
|
|
|
27006
27246
|
if (entry.isDirectory()) {
|
|
27007
27247
|
if (excluded.has(entry.name))
|
|
27008
27248
|
continue;
|
|
27009
|
-
walk(
|
|
27249
|
+
walk(path18.join(dir, entry.name), depth + 1);
|
|
27010
27250
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
27011
|
-
matches.push("./" +
|
|
27251
|
+
matches.push("./" + path18.relative(cwd, path18.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
27012
27252
|
}
|
|
27013
27253
|
}
|
|
27014
27254
|
};
|
|
@@ -27024,7 +27264,7 @@ var readTrimmed2 = (file, maxLines) => {
|
|
|
27024
27264
|
`);
|
|
27025
27265
|
};
|
|
27026
27266
|
function prReadyContext(root, range) {
|
|
27027
|
-
const cwd =
|
|
27267
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
27028
27268
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
27029
27269
|
return invalidRange(cwd);
|
|
27030
27270
|
let stdout = "";
|
|
@@ -27082,7 +27322,7 @@ ${status.stderr}` : "");
|
|
|
27082
27322
|
stdout += `template_path: ${template}
|
|
27083
27323
|
|
|
27084
27324
|
`;
|
|
27085
|
-
stdout += readTrimmed2(
|
|
27325
|
+
stdout += readTrimmed2(path18.join(cwd, template), 220);
|
|
27086
27326
|
} else {
|
|
27087
27327
|
stdout += `template_path: none
|
|
27088
27328
|
|
|
@@ -27092,7 +27332,7 @@ ${status.stderr}` : "");
|
|
|
27092
27332
|
stdout += `
|
|
27093
27333
|
`;
|
|
27094
27334
|
stdout += printSection("Recent Validation Signals");
|
|
27095
|
-
if (existsSync6(
|
|
27335
|
+
if (existsSync6(path18.join(cwd, "package.json"))) {
|
|
27096
27336
|
stdout += `package.json detected. Common scripts:
|
|
27097
27337
|
`;
|
|
27098
27338
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -27101,7 +27341,7 @@ ${status.stderr}` : "");
|
|
|
27101
27341
|
`) + `
|
|
27102
27342
|
`;
|
|
27103
27343
|
}
|
|
27104
|
-
if (existsSync6(
|
|
27344
|
+
if (existsSync6(path18.join(cwd, "Cargo.toml")) || existsSync6(path18.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
27105
27345
|
stdout += `Rust project detected.
|
|
27106
27346
|
`;
|
|
27107
27347
|
}
|
|
@@ -27134,7 +27374,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
27134
27374
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
27135
27375
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
27136
27376
|
function changelogContext(root, range) {
|
|
27137
|
-
const cwd =
|
|
27377
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
27138
27378
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
27139
27379
|
return invalidRange(cwd);
|
|
27140
27380
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -27152,7 +27392,7 @@ function changelogContext(root, range) {
|
|
|
27152
27392
|
stdout += CHANGELOG_RULES + `
|
|
27153
27393
|
`;
|
|
27154
27394
|
stdout += printSection("Existing CHANGELOG.md");
|
|
27155
|
-
const changelogPath =
|
|
27395
|
+
const changelogPath = path18.join(cwd, "CHANGELOG.md");
|
|
27156
27396
|
if (existsSync6(changelogPath)) {
|
|
27157
27397
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
27158
27398
|
`;
|
|
@@ -27172,7 +27412,7 @@ function changelogContext(root, range) {
|
|
|
27172
27412
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
27173
27413
|
}
|
|
27174
27414
|
function docsRefreshContext(root, range) {
|
|
27175
|
-
const cwd =
|
|
27415
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
27176
27416
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
27177
27417
|
return invalidRange(cwd);
|
|
27178
27418
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -27194,7 +27434,7 @@ function docsRefreshContext(root, range) {
|
|
|
27194
27434
|
`) + `
|
|
27195
27435
|
`;
|
|
27196
27436
|
stdout += printSection("README Preview");
|
|
27197
|
-
const readme =
|
|
27437
|
+
const readme = path18.join(cwd, "README.md");
|
|
27198
27438
|
if (existsSync6(readme)) {
|
|
27199
27439
|
stdout += readTrimmed2(readme, 220) + `
|
|
27200
27440
|
`;
|
|
@@ -27203,7 +27443,7 @@ function docsRefreshContext(root, range) {
|
|
|
27203
27443
|
`;
|
|
27204
27444
|
}
|
|
27205
27445
|
stdout += printSection("Package Scripts");
|
|
27206
|
-
const pkgPath =
|
|
27446
|
+
const pkgPath = path18.join(cwd, "package.json");
|
|
27207
27447
|
if (existsSync6(pkgPath)) {
|
|
27208
27448
|
try {
|
|
27209
27449
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
@@ -27217,7 +27457,7 @@ function docsRefreshContext(root, range) {
|
|
|
27217
27457
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
27218
27458
|
}
|
|
27219
27459
|
function releaseNotesContext(root, rangeOrTag) {
|
|
27220
|
-
const cwd =
|
|
27460
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
27221
27461
|
if (!rangeOrTag) {
|
|
27222
27462
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
27223
27463
|
`, exitCode: 1, cwd };
|
|
@@ -27255,7 +27495,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
27255
27495
|
`;
|
|
27256
27496
|
stdout += printSection("Existing Release Files");
|
|
27257
27497
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
27258
|
-
if (existsSync6(
|
|
27498
|
+
if (existsSync6(path18.join(cwd, rel)))
|
|
27259
27499
|
stdout += rel + `
|
|
27260
27500
|
`;
|
|
27261
27501
|
}
|
|
@@ -27286,8 +27526,8 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
27286
27526
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
27287
27527
|
|
|
27288
27528
|
// packages/workit-core/src/core/pr-create.ts
|
|
27289
|
-
import
|
|
27290
|
-
import
|
|
27529
|
+
import fs14 from "node:fs";
|
|
27530
|
+
import path19 from "node:path";
|
|
27291
27531
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
27292
27532
|
// packages/workit-core/src/forge/pr-body.ts
|
|
27293
27533
|
function parseGhIssue(value) {
|
|
@@ -27298,13 +27538,13 @@ function parseGhIssue(value) {
|
|
|
27298
27538
|
// packages/workit-core/src/core/pr-create.ts
|
|
27299
27539
|
function whichOnPath(tool) {
|
|
27300
27540
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
27301
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
27541
|
+
for (const dir of (process.env.PATH ?? "").split(path19.delimiter)) {
|
|
27302
27542
|
if (!dir)
|
|
27303
27543
|
continue;
|
|
27304
27544
|
for (const name of names) {
|
|
27305
|
-
const candidate =
|
|
27545
|
+
const candidate = path19.join(dir, name);
|
|
27306
27546
|
try {
|
|
27307
|
-
|
|
27547
|
+
fs14.accessSync(candidate, fs14.constants.X_OK);
|
|
27308
27548
|
return candidate;
|
|
27309
27549
|
} catch {}
|
|
27310
27550
|
}
|
|
@@ -27422,8 +27662,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
|
27422
27662
|
};
|
|
27423
27663
|
|
|
27424
27664
|
// packages/workit-core/src/core/youtrack.ts
|
|
27425
|
-
import
|
|
27426
|
-
import
|
|
27665
|
+
import fs15 from "node:fs";
|
|
27666
|
+
import path20 from "node:path";
|
|
27427
27667
|
|
|
27428
27668
|
// packages/workit-core/src/core/templates.ts
|
|
27429
27669
|
var repoRoot2 = assetRoot();
|
|
@@ -27436,22 +27676,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
27436
27676
|
// packages/workit-core/src/core/youtrack.ts
|
|
27437
27677
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
27438
27678
|
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
27439
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
27440
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
27679
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path20.join(configDir(), "youtrack.json");
|
|
27680
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path20.join(resolveConfigDir(), "youtrack.json");
|
|
27441
27681
|
var youTrackTokenModeOk = (p) => {
|
|
27442
27682
|
if (process.platform === "win32")
|
|
27443
27683
|
return true;
|
|
27444
|
-
const mode =
|
|
27684
|
+
const mode = fs15.statSync(p).mode & 511;
|
|
27445
27685
|
return mode === 384;
|
|
27446
27686
|
};
|
|
27447
27687
|
function readYouTrackConfig(required) {
|
|
27448
27688
|
const cfgPath = youTrackConfigPath();
|
|
27449
|
-
if (!
|
|
27689
|
+
if (!fs15.existsSync(cfgPath)) {
|
|
27450
27690
|
return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
|
|
27451
27691
|
}
|
|
27452
27692
|
let parsed;
|
|
27453
27693
|
try {
|
|
27454
|
-
parsed = JSON.parse(
|
|
27694
|
+
parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
|
|
27455
27695
|
} catch {
|
|
27456
27696
|
return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
|
|
27457
27697
|
}
|
|
@@ -27462,11 +27702,11 @@ function readYouTrackConfig(required) {
|
|
|
27462
27702
|
}
|
|
27463
27703
|
var readYouTrackContextConfig = () => {
|
|
27464
27704
|
const cfgPath = youTrackReadOnlyConfigPath();
|
|
27465
|
-
if (!
|
|
27705
|
+
if (!fs15.existsSync(cfgPath))
|
|
27466
27706
|
return { data: {} };
|
|
27467
27707
|
let parsed;
|
|
27468
27708
|
try {
|
|
27469
|
-
parsed = JSON.parse(
|
|
27709
|
+
parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
|
|
27470
27710
|
} catch {
|
|
27471
27711
|
return { error: "YouTrack configuration is unavailable" };
|
|
27472
27712
|
}
|
|
@@ -27505,12 +27745,12 @@ var youTrackToken = () => {
|
|
|
27505
27745
|
if ("error" in loaded)
|
|
27506
27746
|
return loaded;
|
|
27507
27747
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
27508
|
-
const tokenPath = tokenFile ?
|
|
27509
|
-
if (!tokenPath || !
|
|
27748
|
+
const tokenPath = tokenFile ? path20.isAbsolute(tokenFile) ? path20.resolve(tokenFile) : path20.resolve(process.cwd(), tokenFile) : "";
|
|
27749
|
+
if (!tokenPath || !fs15.existsSync(tokenPath))
|
|
27510
27750
|
return { error: "missing youtrack.token" };
|
|
27511
27751
|
if (!youTrackTokenModeOk(tokenPath))
|
|
27512
27752
|
return { error: "youtrack.token mode must be 0600" };
|
|
27513
|
-
const token =
|
|
27753
|
+
const token = fs15.readFileSync(tokenPath, "utf8").trim();
|
|
27514
27754
|
if (!token)
|
|
27515
27755
|
return { error: "empty token file" };
|
|
27516
27756
|
if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
|
|
@@ -27575,9 +27815,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
|
|
|
27575
27815
|
} catch {
|
|
27576
27816
|
continue;
|
|
27577
27817
|
}
|
|
27578
|
-
if (!
|
|
27818
|
+
if (!fs15.existsSync(full))
|
|
27579
27819
|
continue;
|
|
27580
|
-
const text =
|
|
27820
|
+
const text = fs15.readFileSync(full, "utf8");
|
|
27581
27821
|
const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
|
|
27582
27822
|
if (m)
|
|
27583
27823
|
return m[1];
|
|
@@ -27847,15 +28087,15 @@ function resolveGitRevision(root, value) {
|
|
|
27847
28087
|
}
|
|
27848
28088
|
function resolveInside(root, candidate) {
|
|
27849
28089
|
const base = realpathSync5(root);
|
|
27850
|
-
const target =
|
|
27851
|
-
if (target !== base && !target.startsWith(base +
|
|
28090
|
+
const target = path21.resolve(base, candidate);
|
|
28091
|
+
if (target !== base && !target.startsWith(base + path21.sep)) {
|
|
27852
28092
|
throw new Error("path must stay inside repository root");
|
|
27853
28093
|
}
|
|
27854
28094
|
let ancestor = target;
|
|
27855
28095
|
while (!existsSync7(ancestor))
|
|
27856
|
-
ancestor =
|
|
28096
|
+
ancestor = path21.dirname(ancestor);
|
|
27857
28097
|
const canonicalAncestor = realpathSync5(ancestor);
|
|
27858
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
28098
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path21.sep)) {
|
|
27859
28099
|
throw new Error("path must stay inside repository root");
|
|
27860
28100
|
}
|
|
27861
28101
|
return target;
|
|
@@ -27913,13 +28153,13 @@ var rootVariants = (workspaceRoot) => {
|
|
|
27913
28153
|
if (!workspaceRoot)
|
|
27914
28154
|
return [];
|
|
27915
28155
|
const roots = new Set;
|
|
27916
|
-
for (const candidate of [workspaceRoot,
|
|
27917
|
-
if (
|
|
28156
|
+
for (const candidate of [workspaceRoot, path22.resolve(workspaceRoot)]) {
|
|
28157
|
+
if (path22.isAbsolute(candidate) && candidate !== path22.parse(candidate).root)
|
|
27918
28158
|
roots.add(candidate);
|
|
27919
28159
|
}
|
|
27920
28160
|
try {
|
|
27921
28161
|
const canonical = realpathSync6(workspaceRoot);
|
|
27922
|
-
if (canonical !==
|
|
28162
|
+
if (canonical !== path22.parse(canonical).root)
|
|
27923
28163
|
roots.add(canonical);
|
|
27924
28164
|
} catch {}
|
|
27925
28165
|
return [...roots].toSorted((left, right) => right.length - left.length);
|
|
@@ -28001,11 +28241,11 @@ var thrownResult = (tool, error, workspaceRoot) => {
|
|
|
28001
28241
|
};
|
|
28002
28242
|
function createMcpServer(host, contextProvider) {
|
|
28003
28243
|
assertMcpHost(host);
|
|
28004
|
-
const here =
|
|
28244
|
+
const here = path22.dirname(fileURLToPath2(import.meta.url));
|
|
28005
28245
|
const sourceMarker = markSourcesLoaded([
|
|
28006
|
-
|
|
28007
|
-
|
|
28008
|
-
|
|
28246
|
+
path22.join(here, "server.ts"),
|
|
28247
|
+
path22.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
|
|
28248
|
+
path22.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
|
|
28009
28249
|
].filter((file) => existsSync8(file)));
|
|
28010
28250
|
let staleWarned = false;
|
|
28011
28251
|
const server = new Server({ name: "workit", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
|
|
@@ -28185,8 +28425,8 @@ var main = async () => {
|
|
|
28185
28425
|
process.exitCode = 2;
|
|
28186
28426
|
}
|
|
28187
28427
|
};
|
|
28188
|
-
var entryPath = process.argv[1] ?
|
|
28189
|
-
if (entryPath ===
|
|
28428
|
+
var entryPath = process.argv[1] ? path23.resolve(process.argv[1]) : "";
|
|
28429
|
+
if (entryPath === path23.resolve(fileURLToPath3(import.meta.url)))
|
|
28190
28430
|
await main();
|
|
28191
28431
|
export {
|
|
28192
28432
|
McpCapabilityUnavailableError,
|