@brainervirus/workit-pi 8.9.1 → 8.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/workit.js
CHANGED
|
@@ -5,7 +5,7 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
|
5
5
|
// packages/workit-core/src/core.ts
|
|
6
6
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
7
7
|
import { existsSync as existsSync7, realpathSync as realpathSync5 } from "node:fs";
|
|
8
|
-
import
|
|
8
|
+
import path20 from "node:path";
|
|
9
9
|
|
|
10
10
|
// packages/workit-core/src/core/task-contract.ts
|
|
11
11
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -8991,9 +8991,9 @@ function parseAdvertisedOperation(family, input, host) {
|
|
|
8991
8991
|
});
|
|
8992
8992
|
}
|
|
8993
8993
|
// packages/workit-core/src/core/task-store.ts
|
|
8994
|
-
import { createHash as
|
|
8995
|
-
import * as
|
|
8996
|
-
import
|
|
8994
|
+
import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
|
|
8995
|
+
import * as fs8 from "node:fs";
|
|
8996
|
+
import path7 from "node:path";
|
|
8997
8997
|
|
|
8998
8998
|
// node_modules/@openclaw/fs-safe/dist/errors.js
|
|
8999
8999
|
var OPERATIONAL_CODES = new Set([
|
|
@@ -9597,63 +9597,15 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
9597
9597
|
}
|
|
9598
9598
|
}
|
|
9599
9599
|
// packages/workit-core/src/host-session.ts
|
|
9600
|
-
|
|
9601
|
-
["OPENCODE_SESSION_ID", () => "opencode"],
|
|
9602
|
-
["PI_SESSION_ID", () => "pi"],
|
|
9603
|
-
[
|
|
9604
|
-
"CODEX_THREAD_ID",
|
|
9605
|
-
(env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
|
|
9606
|
-
]
|
|
9607
|
-
];
|
|
9608
|
-
var family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null;
|
|
9609
|
-
function hostSessionFromEnv(env) {
|
|
9610
|
-
const value = (key) => env[key]?.trim() || null;
|
|
9611
|
-
let inner = null;
|
|
9612
|
-
for (const [key, host] of HOST_SESSION_VARS) {
|
|
9613
|
-
const session = value(key);
|
|
9614
|
-
if (session) {
|
|
9615
|
-
inner = { host: host(env), session };
|
|
9616
|
-
break;
|
|
9617
|
-
}
|
|
9618
|
-
}
|
|
9619
|
-
const declared = value("WORKIT_HOST");
|
|
9620
|
-
if (env.WORKIT_SESSION_ID !== undefined) {
|
|
9621
|
-
const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
|
|
9622
|
-
if (!inherited)
|
|
9623
|
-
return { host: declared, session: value("WORKIT_SESSION_ID") };
|
|
9624
|
-
}
|
|
9625
|
-
return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
|
|
9626
|
-
}
|
|
9627
|
-
|
|
9628
|
-
// packages/workit-core/src/core/package-root.ts
|
|
9629
|
-
import { existsSync } from "node:fs";
|
|
9630
|
-
import path3 from "node:path";
|
|
9631
|
-
import { fileURLToPath } from "node:url";
|
|
9632
|
-
var packageRoot = () => {
|
|
9633
|
-
let dir = path3.dirname(fileURLToPath(import.meta.url));
|
|
9634
|
-
while (true) {
|
|
9635
|
-
const parent = path3.dirname(dir);
|
|
9636
|
-
if (existsSync(path3.join(dir, "package.json")) || parent === dir)
|
|
9637
|
-
return dir;
|
|
9638
|
-
dir = parent;
|
|
9639
|
-
}
|
|
9640
|
-
};
|
|
9641
|
-
var assetRoot = () => {
|
|
9642
|
-
const root = packageRoot();
|
|
9643
|
-
const assets = path3.join(root, "assets");
|
|
9644
|
-
return existsSync(assets) ? assets : root;
|
|
9645
|
-
};
|
|
9646
|
-
|
|
9647
|
-
// packages/workit-core/src/core/store-lock.ts
|
|
9648
|
-
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
9600
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
9649
9601
|
import fs5 from "node:fs";
|
|
9650
|
-
import
|
|
9602
|
+
import path4 from "node:path";
|
|
9651
9603
|
|
|
9652
9604
|
// packages/workit-core/src/store/paths.ts
|
|
9653
9605
|
import { spawnSync } from "node:child_process";
|
|
9654
9606
|
import { createHash as createHash2 } from "node:crypto";
|
|
9655
9607
|
import fs4 from "node:fs";
|
|
9656
|
-
import
|
|
9608
|
+
import path3 from "node:path";
|
|
9657
9609
|
var NOT_A_REPO = /not a git repository \(or any (of the parent directories|parent up to)/u;
|
|
9658
9610
|
var gitRun = (cwd, args) => {
|
|
9659
9611
|
const run = spawnSync("git", args, {
|
|
@@ -9695,14 +9647,14 @@ var DISCOVERY_ENV = [
|
|
|
9695
9647
|
];
|
|
9696
9648
|
var isGitDir = (dir) => {
|
|
9697
9649
|
try {
|
|
9698
|
-
return fs4.statSync(
|
|
9650
|
+
return fs4.statSync(path3.join(dir, "HEAD")).isFile() && fs4.statSync(path3.join(dir, "objects")).isDirectory() && fs4.statSync(path3.join(dir, "refs")).isDirectory();
|
|
9699
9651
|
} catch {
|
|
9700
9652
|
return false;
|
|
9701
9653
|
}
|
|
9702
9654
|
};
|
|
9703
9655
|
var reftableRefs = (common) => {
|
|
9704
9656
|
try {
|
|
9705
|
-
return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs4.readFileSync(
|
|
9657
|
+
return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs4.readFileSync(path3.join(common, "config"), "utf8"));
|
|
9706
9658
|
} catch {
|
|
9707
9659
|
return false;
|
|
9708
9660
|
}
|
|
@@ -9722,7 +9674,7 @@ function fastGit(root) {
|
|
|
9722
9674
|
if (device !== null && stat.dev !== device)
|
|
9723
9675
|
return { kind: "none" };
|
|
9724
9676
|
device = stat.dev;
|
|
9725
|
-
const dotGit =
|
|
9677
|
+
const dotGit = path3.join(dir, ".git");
|
|
9726
9678
|
let entry = null;
|
|
9727
9679
|
try {
|
|
9728
9680
|
entry = fs4.statSync(dotGit);
|
|
@@ -9743,19 +9695,19 @@ function fastGit(root) {
|
|
|
9743
9695
|
const match = /^gitdir: (.+)$/mu.exec(text);
|
|
9744
9696
|
if (!match)
|
|
9745
9697
|
return null;
|
|
9746
|
-
gitDir =
|
|
9698
|
+
gitDir = path3.resolve(dir, match[1].trim());
|
|
9747
9699
|
} else
|
|
9748
9700
|
return null;
|
|
9749
|
-
if (!fs4.existsSync(
|
|
9701
|
+
if (!fs4.existsSync(path3.join(gitDir, "HEAD")))
|
|
9750
9702
|
return null;
|
|
9751
9703
|
} else if (isGitDir(dir))
|
|
9752
9704
|
gitDir = dir;
|
|
9753
9705
|
if (gitDir) {
|
|
9754
9706
|
let common = gitDir;
|
|
9755
9707
|
try {
|
|
9756
|
-
common =
|
|
9708
|
+
common = path3.resolve(gitDir, fs4.readFileSync(path3.join(gitDir, "commondir"), "utf8").trim());
|
|
9757
9709
|
} catch {}
|
|
9758
|
-
if (!fs4.existsSync(
|
|
9710
|
+
if (!fs4.existsSync(path3.join(common, "objects")))
|
|
9759
9711
|
return null;
|
|
9760
9712
|
return {
|
|
9761
9713
|
kind: "repo",
|
|
@@ -9765,26 +9717,26 @@ function fastGit(root) {
|
|
|
9765
9717
|
reftable: reftableRefs(common)
|
|
9766
9718
|
};
|
|
9767
9719
|
}
|
|
9768
|
-
const parent =
|
|
9720
|
+
const parent = path3.dirname(dir);
|
|
9769
9721
|
if (parent === dir)
|
|
9770
9722
|
return { kind: "none" };
|
|
9771
9723
|
dir = parent;
|
|
9772
9724
|
}
|
|
9773
9725
|
}
|
|
9774
9726
|
function resolveStore(root) {
|
|
9775
|
-
root =
|
|
9727
|
+
root = path3.resolve(root);
|
|
9776
9728
|
let directory = false;
|
|
9777
9729
|
try {
|
|
9778
9730
|
directory = fs4.statSync(root).isDirectory();
|
|
9779
9731
|
} catch {}
|
|
9780
9732
|
if (!directory)
|
|
9781
|
-
return { dir:
|
|
9733
|
+
return { dir: path3.join(root, ".workit"), shared: false, top: null };
|
|
9782
9734
|
const fast = fastGit(root);
|
|
9783
9735
|
if (fast?.kind === "none")
|
|
9784
|
-
return { dir:
|
|
9736
|
+
return { dir: path3.join(root, ".workit"), shared: false, top: null };
|
|
9785
9737
|
if (fast?.kind === "repo")
|
|
9786
9738
|
return {
|
|
9787
|
-
dir:
|
|
9739
|
+
dir: path3.join(fast.common, "workit"),
|
|
9788
9740
|
shared: true,
|
|
9789
9741
|
top: fast.top,
|
|
9790
9742
|
git: { gitDir: fast.gitDir, top: fast.top, reftable: fast.reftable }
|
|
@@ -9794,20 +9746,20 @@ function resolveStore(root) {
|
|
|
9794
9746
|
if (run.ok && common) {
|
|
9795
9747
|
const top = gitRun(root, ["rev-parse", "--path-format=absolute", "--show-toplevel"]);
|
|
9796
9748
|
return {
|
|
9797
|
-
dir:
|
|
9749
|
+
dir: path3.join(real(path3.resolve(root, common)), "workit"),
|
|
9798
9750
|
shared: true,
|
|
9799
9751
|
top: top.ok && top.stdout.trim() ? real(top.stdout.trim()) : null
|
|
9800
9752
|
};
|
|
9801
9753
|
}
|
|
9802
9754
|
if (run.missing || !run.ok && NOT_A_REPO.test(run.stderr))
|
|
9803
|
-
return { dir:
|
|
9755
|
+
return { dir: path3.join(root, ".workit"), shared: false, top: null };
|
|
9804
9756
|
return new Error(`cannot locate the workit store: git rev-parse failed (${run.stderr.trim().split(`
|
|
9805
9757
|
`)[0] || "no output"}); fix the repository (git status should work here)`);
|
|
9806
9758
|
}
|
|
9807
9759
|
var rebasingBranch = (gitDir) => {
|
|
9808
9760
|
for (const state of ["rebase-merge", "rebase-apply"]) {
|
|
9809
9761
|
try {
|
|
9810
|
-
const ref = fs4.readFileSync(
|
|
9762
|
+
const ref = fs4.readFileSync(path3.join(gitDir, state, "head-name"), "utf8").trim();
|
|
9811
9763
|
const match = /^refs\/heads\/(.+)$/u.exec(ref);
|
|
9812
9764
|
if (match)
|
|
9813
9765
|
return match[1];
|
|
@@ -9822,7 +9774,7 @@ function resolveTaskKey(root, location) {
|
|
|
9822
9774
|
if (location.git && !location.git.reftable) {
|
|
9823
9775
|
let head = "";
|
|
9824
9776
|
try {
|
|
9825
|
-
head = fs4.readFileSync(
|
|
9777
|
+
head = fs4.readFileSync(path3.join(location.git.gitDir, "HEAD"), "utf8").trim();
|
|
9826
9778
|
} catch {}
|
|
9827
9779
|
const ref = /^ref: refs\/heads\/(.+)$/u.exec(head);
|
|
9828
9780
|
if (ref && ref[1] !== ".invalid")
|
|
@@ -9853,7 +9805,7 @@ function resolveTaskKey(root, location) {
|
|
|
9853
9805
|
return { key: `detached-${shortHash(real(worktree))}`, kind: "detached", branch: null };
|
|
9854
9806
|
}
|
|
9855
9807
|
function checkoutRootOf(root, location) {
|
|
9856
|
-
const resolved =
|
|
9808
|
+
const resolved = path3.resolve(root);
|
|
9857
9809
|
let canonical = resolved;
|
|
9858
9810
|
try {
|
|
9859
9811
|
canonical = fs4.realpathSync(resolved);
|
|
@@ -9884,13 +9836,13 @@ function branchRenames(location) {
|
|
|
9884
9836
|
}
|
|
9885
9837
|
for (const entry of entries)
|
|
9886
9838
|
if (entry.isDirectory())
|
|
9887
|
-
walk(
|
|
9839
|
+
walk(path3.join(dir, entry.name));
|
|
9888
9840
|
else
|
|
9889
|
-
files.push(
|
|
9841
|
+
files.push(path3.join(dir, entry.name));
|
|
9890
9842
|
};
|
|
9891
|
-
walk(
|
|
9843
|
+
walk(path3.join(path3.dirname(location.dir), "logs", "refs", "heads"));
|
|
9892
9844
|
if (location.git)
|
|
9893
|
-
files.push(
|
|
9845
|
+
files.push(path3.join(location.git.gitDir, "logs", "HEAD"));
|
|
9894
9846
|
const seen = new Set;
|
|
9895
9847
|
const renames = [];
|
|
9896
9848
|
for (const file of files) {
|
|
@@ -9925,11 +9877,96 @@ var checkoutSlug = (root) => {
|
|
|
9925
9877
|
let canonical = real(root);
|
|
9926
9878
|
if (process.platform === "win32")
|
|
9927
9879
|
canonical = canonical.toLowerCase();
|
|
9928
|
-
const base =
|
|
9880
|
+
const base = path3.basename(canonical).replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 40) || "root";
|
|
9929
9881
|
return `${base}-${shortHash(canonical)}`;
|
|
9930
9882
|
};
|
|
9931
9883
|
|
|
9884
|
+
// packages/workit-core/src/host-session.ts
|
|
9885
|
+
var HOST_SESSION_VARS = [
|
|
9886
|
+
["OPENCODE_SESSION_ID", () => "opencode"],
|
|
9887
|
+
["PI_SESSION_ID", () => "pi"],
|
|
9888
|
+
[
|
|
9889
|
+
"CODEX_THREAD_ID",
|
|
9890
|
+
(env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
|
|
9891
|
+
]
|
|
9892
|
+
];
|
|
9893
|
+
var family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null;
|
|
9894
|
+
function hostSessionFromEnv(env) {
|
|
9895
|
+
const value = (key) => env[key]?.trim() || null;
|
|
9896
|
+
let inner = null;
|
|
9897
|
+
for (const [key, host] of HOST_SESSION_VARS) {
|
|
9898
|
+
const session = value(key);
|
|
9899
|
+
if (session) {
|
|
9900
|
+
inner = { host: host(env), session };
|
|
9901
|
+
break;
|
|
9902
|
+
}
|
|
9903
|
+
}
|
|
9904
|
+
const declared = value("WORKIT_HOST");
|
|
9905
|
+
if (env.WORKIT_SESSION_ID !== undefined) {
|
|
9906
|
+
const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
|
|
9907
|
+
if (!inherited)
|
|
9908
|
+
return { host: declared, session: value("WORKIT_SESSION_ID") };
|
|
9909
|
+
}
|
|
9910
|
+
return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
|
|
9911
|
+
}
|
|
9912
|
+
var ISSUED_PER_LEAD = 64;
|
|
9913
|
+
var ISSUED_SESSIONS_MARKER = "workit-issued-sessions/1";
|
|
9914
|
+
var hash = (value) => createHash3("sha256").update(value).digest("hex").slice(0, 32);
|
|
9915
|
+
var issuedRoot = (cwd) => {
|
|
9916
|
+
const location = resolveStore(cwd);
|
|
9917
|
+
return location instanceof Error ? null : path4.join(location.dir, "hooks", "issued");
|
|
9918
|
+
};
|
|
9919
|
+
var issuedDir = (cwd, lead) => {
|
|
9920
|
+
const root = issuedRoot(cwd);
|
|
9921
|
+
return root === null ? null : path4.join(root, hash(lead));
|
|
9922
|
+
};
|
|
9923
|
+
function recordIssuedSession(cwd, lead, issued) {
|
|
9924
|
+
try {
|
|
9925
|
+
const dir = issuedDir(cwd, lead);
|
|
9926
|
+
if (!dir)
|
|
9927
|
+
return;
|
|
9928
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
9929
|
+
const own = hash(issued);
|
|
9930
|
+
fs5.writeFileSync(path4.join(dir, own), ISSUED_SESSIONS_MARKER);
|
|
9931
|
+
const entries = fs5.readdirSync(dir);
|
|
9932
|
+
if (entries.length <= ISSUED_PER_LEAD)
|
|
9933
|
+
return;
|
|
9934
|
+
const byAge = entries.filter((name) => name !== own).map((name) => ({ name, at: fs5.statSync(path4.join(dir, name)).mtimeMs })).toSorted((a, b) => a.at - b.at);
|
|
9935
|
+
for (const { name } of byAge.slice(0, entries.length - ISSUED_PER_LEAD))
|
|
9936
|
+
fs5.rmSync(path4.join(dir, name), { force: true });
|
|
9937
|
+
} catch {}
|
|
9938
|
+
}
|
|
9939
|
+
var OWN_CHILD_SHELL = new Set(["opencode", "pi"]);
|
|
9940
|
+
var SESSION_UNSAFE = /[^A-Za-z0-9_.:@/+-]/g;
|
|
9941
|
+
function childSessionId(lead, child) {
|
|
9942
|
+
const leaf = child.replace(SESSION_UNSAFE, "-").slice(-120);
|
|
9943
|
+
const prefix = lead.replace(SESSION_UNSAFE, "-").slice(0, 127 - leaf.length);
|
|
9944
|
+
return prefix ? `${prefix}:${leaf}` : leaf;
|
|
9945
|
+
}
|
|
9946
|
+
|
|
9947
|
+
// packages/workit-core/src/core/package-root.ts
|
|
9948
|
+
import { existsSync } from "node:fs";
|
|
9949
|
+
import path5 from "node:path";
|
|
9950
|
+
import { fileURLToPath } from "node:url";
|
|
9951
|
+
var packageRoot = () => {
|
|
9952
|
+
let dir = path5.dirname(fileURLToPath(import.meta.url));
|
|
9953
|
+
while (true) {
|
|
9954
|
+
const parent = path5.dirname(dir);
|
|
9955
|
+
if (existsSync(path5.join(dir, "package.json")) || parent === dir)
|
|
9956
|
+
return dir;
|
|
9957
|
+
dir = parent;
|
|
9958
|
+
}
|
|
9959
|
+
};
|
|
9960
|
+
var assetRoot = () => {
|
|
9961
|
+
const root = packageRoot();
|
|
9962
|
+
const assets = path5.join(root, "assets");
|
|
9963
|
+
return existsSync(assets) ? assets : root;
|
|
9964
|
+
};
|
|
9965
|
+
|
|
9932
9966
|
// packages/workit-core/src/core/store-lock.ts
|
|
9967
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
9968
|
+
import fs6 from "node:fs";
|
|
9969
|
+
import { hostname } from "node:os";
|
|
9933
9970
|
var metadataLockSchema = object({
|
|
9934
9971
|
pid: number2().int().nonnegative().safe(),
|
|
9935
9972
|
processStart: string2().nullable(),
|
|
@@ -9970,7 +10007,7 @@ var parseProcStatStart = (stat) => {
|
|
|
9970
10007
|
var processStartOf = (pid) => {
|
|
9971
10008
|
if (process.platform === "linux") {
|
|
9972
10009
|
try {
|
|
9973
|
-
return parseProcStatStart(
|
|
10010
|
+
return parseProcStatStart(fs6.readFileSync(`/proc/${pid}/stat`, "utf8"));
|
|
9974
10011
|
} catch {
|
|
9975
10012
|
return null;
|
|
9976
10013
|
}
|
|
@@ -9991,7 +10028,7 @@ var processStartOf = (pid) => {
|
|
|
9991
10028
|
};
|
|
9992
10029
|
var readTrimmed = (file) => {
|
|
9993
10030
|
try {
|
|
9994
|
-
return
|
|
10031
|
+
return fs6.readFileSync(file, "utf8").trim() || null;
|
|
9995
10032
|
} catch {
|
|
9996
10033
|
return null;
|
|
9997
10034
|
}
|
|
@@ -10002,7 +10039,7 @@ var localLockHost = () => {
|
|
|
10002
10039
|
return cachedLockHost;
|
|
10003
10040
|
let pidns = null;
|
|
10004
10041
|
try {
|
|
10005
|
-
pidns = /\[(\d+)\]/.exec(
|
|
10042
|
+
pidns = /\[(\d+)\]/.exec(fs6.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
|
|
10006
10043
|
} catch {}
|
|
10007
10044
|
const boot = readTrimmed("/proc/sys/kernel/random/boot_id");
|
|
10008
10045
|
cachedLockHost = pidns || boot ? `${hostname()}#${pidns ?? "?"}:${boot ?? "?"}` : hostname();
|
|
@@ -10048,7 +10085,7 @@ var classifyLockOwner = (payload, ageMs, localHost = localLockHost(), options =
|
|
|
10048
10085
|
};
|
|
10049
10086
|
var ageOf = (file, nowMs) => {
|
|
10050
10087
|
try {
|
|
10051
|
-
return nowMs -
|
|
10088
|
+
return nowMs - fs6.lstatSync(file).mtimeMs;
|
|
10052
10089
|
} catch {
|
|
10053
10090
|
return null;
|
|
10054
10091
|
}
|
|
@@ -10059,7 +10096,7 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
|
|
|
10059
10096
|
if (age === null || age <= RECLAIM_GUARD_TTL_MS)
|
|
10060
10097
|
return false;
|
|
10061
10098
|
try {
|
|
10062
|
-
|
|
10099
|
+
fs6.rmdirSync(guard);
|
|
10063
10100
|
return true;
|
|
10064
10101
|
} catch {
|
|
10065
10102
|
return false;
|
|
@@ -10068,8 +10105,8 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
|
|
|
10068
10105
|
|
|
10069
10106
|
// packages/workit-core/src/store/event-log.ts
|
|
10070
10107
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
10071
|
-
import
|
|
10072
|
-
import
|
|
10108
|
+
import fs7 from "node:fs";
|
|
10109
|
+
import path6 from "node:path";
|
|
10073
10110
|
var EVENT_VERSION = 1;
|
|
10074
10111
|
|
|
10075
10112
|
class LogDamage extends Error {
|
|
@@ -10098,7 +10135,7 @@ var parseEvent = (line, file, offset) => {
|
|
|
10098
10135
|
function readLog(file, from = 0, expectSeq = null) {
|
|
10099
10136
|
let fd;
|
|
10100
10137
|
try {
|
|
10101
|
-
fd =
|
|
10138
|
+
fd = fs7.openSync(file, "r");
|
|
10102
10139
|
} catch (error) {
|
|
10103
10140
|
if (error.code === "ENOENT")
|
|
10104
10141
|
return null;
|
|
@@ -10107,13 +10144,13 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
10107
10144
|
let bytes;
|
|
10108
10145
|
let size;
|
|
10109
10146
|
try {
|
|
10110
|
-
size =
|
|
10147
|
+
size = fs7.fstatSync(fd).size;
|
|
10111
10148
|
if (from > size)
|
|
10112
10149
|
throw new LogDamage(`${file}: shorter than its snapshot`);
|
|
10113
10150
|
bytes = Buffer.alloc(size - from);
|
|
10114
10151
|
let read = 0;
|
|
10115
10152
|
while (read < bytes.length) {
|
|
10116
|
-
const got =
|
|
10153
|
+
const got = fs7.readSync(fd, bytes, read, bytes.length - read, from + read);
|
|
10117
10154
|
if (got === 0)
|
|
10118
10155
|
break;
|
|
10119
10156
|
read += got;
|
|
@@ -10121,7 +10158,7 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
10121
10158
|
bytes = bytes.subarray(0, read);
|
|
10122
10159
|
size = from + read;
|
|
10123
10160
|
} finally {
|
|
10124
|
-
|
|
10161
|
+
fs7.closeSync(fd);
|
|
10125
10162
|
}
|
|
10126
10163
|
const events = [];
|
|
10127
10164
|
let start = 0;
|
|
@@ -10146,11 +10183,11 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
10146
10183
|
}
|
|
10147
10184
|
var fsyncDirectory = (dir) => {
|
|
10148
10185
|
try {
|
|
10149
|
-
const fd =
|
|
10186
|
+
const fd = fs7.openSync(dir, "r");
|
|
10150
10187
|
try {
|
|
10151
|
-
|
|
10188
|
+
fs7.fsyncSync(fd);
|
|
10152
10189
|
} finally {
|
|
10153
|
-
|
|
10190
|
+
fs7.closeSync(fd);
|
|
10154
10191
|
}
|
|
10155
10192
|
} catch (error) {
|
|
10156
10193
|
if (process.platform !== "win32")
|
|
@@ -10160,46 +10197,46 @@ var fsyncDirectory = (dir) => {
|
|
|
10160
10197
|
function appendEvent(file, event, validEnd) {
|
|
10161
10198
|
const line = Buffer.from(`${JSON.stringify(event)}
|
|
10162
10199
|
`, "utf8");
|
|
10163
|
-
const created = !
|
|
10164
|
-
if (!created &&
|
|
10165
|
-
const cut =
|
|
10200
|
+
const created = !fs7.existsSync(file);
|
|
10201
|
+
if (!created && fs7.statSync(file).size !== validEnd) {
|
|
10202
|
+
const cut = fs7.openSync(file, "r+");
|
|
10166
10203
|
try {
|
|
10167
|
-
|
|
10168
|
-
|
|
10204
|
+
fs7.ftruncateSync(cut, validEnd);
|
|
10205
|
+
fs7.fsyncSync(cut);
|
|
10169
10206
|
} finally {
|
|
10170
|
-
|
|
10207
|
+
fs7.closeSync(cut);
|
|
10171
10208
|
}
|
|
10172
10209
|
}
|
|
10173
|
-
const fd =
|
|
10210
|
+
const fd = fs7.openSync(file, "a", 384);
|
|
10174
10211
|
try {
|
|
10175
10212
|
let written = 0;
|
|
10176
10213
|
while (written < line.length)
|
|
10177
|
-
written +=
|
|
10178
|
-
|
|
10214
|
+
written += fs7.writeSync(fd, line, written, line.length - written);
|
|
10215
|
+
fs7.fsyncSync(fd);
|
|
10179
10216
|
} finally {
|
|
10180
|
-
|
|
10217
|
+
fs7.closeSync(fd);
|
|
10181
10218
|
}
|
|
10182
10219
|
if (created)
|
|
10183
|
-
fsyncDirectory(
|
|
10220
|
+
fsyncDirectory(path6.dirname(file));
|
|
10184
10221
|
return validEnd;
|
|
10185
10222
|
}
|
|
10186
10223
|
function replaceFile(file, bytes, durable) {
|
|
10187
10224
|
const temporary = `${file}.${process.pid}.${randomUUID2()}.tmp`;
|
|
10188
10225
|
try {
|
|
10189
|
-
const fd =
|
|
10226
|
+
const fd = fs7.openSync(temporary, "wx", 384);
|
|
10190
10227
|
try {
|
|
10191
|
-
|
|
10228
|
+
fs7.writeSync(fd, bytes);
|
|
10192
10229
|
if (durable)
|
|
10193
|
-
|
|
10230
|
+
fs7.fsyncSync(fd);
|
|
10194
10231
|
} finally {
|
|
10195
|
-
|
|
10232
|
+
fs7.closeSync(fd);
|
|
10196
10233
|
}
|
|
10197
10234
|
renameWithRetry(temporary, file);
|
|
10198
10235
|
if (durable)
|
|
10199
|
-
fsyncDirectory(
|
|
10236
|
+
fsyncDirectory(path6.dirname(file));
|
|
10200
10237
|
} catch (error) {
|
|
10201
10238
|
try {
|
|
10202
|
-
|
|
10239
|
+
fs7.unlinkSync(temporary);
|
|
10203
10240
|
} catch {}
|
|
10204
10241
|
throw error;
|
|
10205
10242
|
}
|
|
@@ -10224,7 +10261,7 @@ var retryTransient = (run) => {
|
|
|
10224
10261
|
}
|
|
10225
10262
|
}
|
|
10226
10263
|
};
|
|
10227
|
-
var renameWithRetry = (from, to) => retryTransient(() =>
|
|
10264
|
+
var renameWithRetry = (from, to) => retryTransient(() => fs7.renameSync(from, to));
|
|
10228
10265
|
|
|
10229
10266
|
// packages/workit-core/src/store/patch.ts
|
|
10230
10267
|
var isPlainObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -10337,7 +10374,7 @@ var isPatch = (value) => Array.isArray(value) && value.every((op) => {
|
|
|
10337
10374
|
});
|
|
10338
10375
|
|
|
10339
10376
|
// packages/workit-core/src/store/reduce.ts
|
|
10340
|
-
import { createHash as
|
|
10377
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
10341
10378
|
var OPENING_TYPES = new Set([
|
|
10342
10379
|
"task.opened",
|
|
10343
10380
|
"task.imported",
|
|
@@ -10366,7 +10403,7 @@ var isObject4 = (value) => typeof value === "object" && value !== null && !Array
|
|
|
10366
10403
|
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");
|
|
10367
10404
|
var BLOB_REF = "$blob";
|
|
10368
10405
|
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;
|
|
10369
|
-
var blobDigest = (value) =>
|
|
10406
|
+
var blobDigest = (value) => createHash4("sha256").update(JSON.stringify(value)).digest("hex");
|
|
10370
10407
|
var toBlob = (value, blobs) => {
|
|
10371
10408
|
if (!isObject4(value))
|
|
10372
10409
|
return value;
|
|
@@ -10521,7 +10558,7 @@ var indexEntry = (task, key, legacy, file) => {
|
|
|
10521
10558
|
};
|
|
10522
10559
|
var fileSignature = (file) => {
|
|
10523
10560
|
try {
|
|
10524
|
-
const stat =
|
|
10561
|
+
const stat = fs8.statSync(file, { bigint: true });
|
|
10525
10562
|
return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
|
|
10526
10563
|
} catch {
|
|
10527
10564
|
return null;
|
|
@@ -10539,7 +10576,7 @@ var cachedRuntimeVersion = null;
|
|
|
10539
10576
|
var runtimeVersion = () => {
|
|
10540
10577
|
if (cachedRuntimeVersion === null) {
|
|
10541
10578
|
try {
|
|
10542
|
-
const pkg = JSON.parse(
|
|
10579
|
+
const pkg = JSON.parse(fs8.readFileSync(path7.join(packageRoot(), "package.json"), "utf8"));
|
|
10543
10580
|
cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
|
|
10544
10581
|
} catch {
|
|
10545
10582
|
cachedRuntimeVersion = "0.0.0";
|
|
@@ -10565,14 +10602,14 @@ var isNewerVersion = (candidate, current) => {
|
|
|
10565
10602
|
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);
|
|
10566
10603
|
var readText = (file) => {
|
|
10567
10604
|
try {
|
|
10568
|
-
return
|
|
10605
|
+
return fs8.readFileSync(file, "utf8");
|
|
10569
10606
|
} catch {
|
|
10570
10607
|
return "";
|
|
10571
10608
|
}
|
|
10572
10609
|
};
|
|
10573
10610
|
var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
|
|
10574
10611
|
var branchStart = (location, branch, aliases) => {
|
|
10575
|
-
const first = readText(
|
|
10612
|
+
const first = readText(path7.join(path7.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
|
|
10576
10613
|
`, 1)[0];
|
|
10577
10614
|
const created = REFLOG_LINE.exec(first);
|
|
10578
10615
|
const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
|
|
@@ -10582,7 +10619,7 @@ var branchStart = (location, branch, aliases) => {
|
|
|
10582
10619
|
return start;
|
|
10583
10620
|
if (!location.git)
|
|
10584
10621
|
return null;
|
|
10585
|
-
for (const line of readText(
|
|
10622
|
+
for (const line of readText(path7.join(location.git.gitDir, "logs", "HEAD")).split(`
|
|
10586
10623
|
`)) {
|
|
10587
10624
|
const entry = REFLOG_LINE.exec(line);
|
|
10588
10625
|
const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
|
|
@@ -10592,23 +10629,23 @@ var branchStart = (location, branch, aliases) => {
|
|
|
10592
10629
|
}
|
|
10593
10630
|
return null;
|
|
10594
10631
|
};
|
|
10595
|
-
var refExists = (common, ref) =>
|
|
10632
|
+
var refExists = (common, ref) => fs8.existsSync(path7.join(common, ref)) || readText(path7.join(common, "packed-refs")).includes(` ${ref}
|
|
10596
10633
|
`);
|
|
10597
10634
|
var origins = new Map;
|
|
10598
10635
|
var sameDirectoryIdentity = (left, right) => {
|
|
10599
|
-
if (!
|
|
10636
|
+
if (!path7.isAbsolute(left) || !path7.isAbsolute(right))
|
|
10600
10637
|
return false;
|
|
10601
|
-
const normalizedLeft =
|
|
10602
|
-
const normalizedRight =
|
|
10638
|
+
const normalizedLeft = path7.resolve(left);
|
|
10639
|
+
const normalizedRight = path7.resolve(right);
|
|
10603
10640
|
try {
|
|
10604
|
-
const a =
|
|
10605
|
-
const b =
|
|
10641
|
+
const a = fs8.statSync(normalizedLeft, { bigint: true });
|
|
10642
|
+
const b = fs8.statSync(normalizedRight, { bigint: true });
|
|
10606
10643
|
if (!a.isDirectory() || !b.isDirectory())
|
|
10607
10644
|
return false;
|
|
10608
10645
|
if (process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight)
|
|
10609
10646
|
return true;
|
|
10610
|
-
const canonicalLeft =
|
|
10611
|
-
const canonicalRight =
|
|
10647
|
+
const canonicalLeft = fs8.realpathSync(normalizedLeft);
|
|
10648
|
+
const canonicalRight = fs8.realpathSync(normalizedRight);
|
|
10612
10649
|
if (process.platform === "win32" ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase() : canonicalLeft === canonicalRight)
|
|
10613
10650
|
return true;
|
|
10614
10651
|
return a.dev === b.dev && a.ino !== 0n && a.ino === b.ino;
|
|
@@ -10645,8 +10682,8 @@ class TaskStore {
|
|
|
10645
10682
|
migrationChecked = false;
|
|
10646
10683
|
compactDue = new Set;
|
|
10647
10684
|
constructor(root, options = {}) {
|
|
10648
|
-
const resolved =
|
|
10649
|
-
this.origin =
|
|
10685
|
+
const resolved = path7.resolve(root);
|
|
10686
|
+
this.origin = fs8.existsSync(resolved) ? fs8.realpathSync(resolved) : resolved;
|
|
10650
10687
|
this.located = resolveStore(this.origin);
|
|
10651
10688
|
this.root = checkoutRootOf(this.origin, this.located);
|
|
10652
10689
|
this.lockTimeoutMs = options.lockTimeoutMs ?? defaultLockTimeout();
|
|
@@ -10654,7 +10691,7 @@ class TaskStore {
|
|
|
10654
10691
|
this.migrateOnRead = options.migrateOnRead;
|
|
10655
10692
|
}
|
|
10656
10693
|
location() {
|
|
10657
|
-
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared &&
|
|
10694
|
+
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs8.existsSync(path7.join(this.root, ".git"))) {
|
|
10658
10695
|
this.located = undefined;
|
|
10659
10696
|
this.migrationChecked = false;
|
|
10660
10697
|
}
|
|
@@ -10718,7 +10755,7 @@ class TaskStore {
|
|
|
10718
10755
|
const tasksDir = this.paths().tasks;
|
|
10719
10756
|
let names;
|
|
10720
10757
|
try {
|
|
10721
|
-
names =
|
|
10758
|
+
names = fs8.existsSync(tasksDir) ? fs8.readdirSync(tasksDir).filter((name) => validId(name)) : [];
|
|
10722
10759
|
} catch (error) {
|
|
10723
10760
|
return failure2("storage_error", `unable to list tasks: ${String(error)}`, {
|
|
10724
10761
|
path: tasksDir
|
|
@@ -10759,7 +10796,7 @@ class TaskStore {
|
|
|
10759
10796
|
const file = this.paths().workspace;
|
|
10760
10797
|
let bytes;
|
|
10761
10798
|
try {
|
|
10762
|
-
bytes = retryTransient(() =>
|
|
10799
|
+
bytes = retryTransient(() => fs8.readFileSync(file, "utf8"));
|
|
10763
10800
|
} catch (error) {
|
|
10764
10801
|
if (error.code === "ENOENT")
|
|
10765
10802
|
return success(null, null, null);
|
|
@@ -10810,7 +10847,7 @@ class TaskStore {
|
|
|
10810
10847
|
const moved = this.bindLocked(again.data.id, key, input.now);
|
|
10811
10848
|
return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
|
|
10812
10849
|
}
|
|
10813
|
-
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" :
|
|
10850
|
+
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path7.basename(this.root) || this.root;
|
|
10814
10851
|
const intent = {
|
|
10815
10852
|
objective: input.objective?.trim() || `Work on ${label}`,
|
|
10816
10853
|
scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
|
|
@@ -10870,14 +10907,14 @@ class TaskStore {
|
|
|
10870
10907
|
const branch = key.ok ? key.data.branch : null;
|
|
10871
10908
|
if (!branch || !location.ok)
|
|
10872
10909
|
return success(null, null, null);
|
|
10873
|
-
const common =
|
|
10874
|
-
const heads =
|
|
10875
|
-
const headLog = location.data.git ?
|
|
10910
|
+
const common = path7.dirname(location.data.dir);
|
|
10911
|
+
const heads = path7.join(common, "logs", "refs", "heads");
|
|
10912
|
+
const headLog = location.data.git ? path7.join(location.data.git.gitDir, "logs", "HEAD") : "";
|
|
10876
10913
|
const signatures = [
|
|
10877
|
-
|
|
10914
|
+
path7.join(heads, branch),
|
|
10878
10915
|
headLog,
|
|
10879
10916
|
heads,
|
|
10880
|
-
|
|
10917
|
+
path7.join(common, "packed-refs")
|
|
10881
10918
|
].map((file) => fileSignature(file) ?? "-");
|
|
10882
10919
|
const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
|
|
10883
10920
|
let resolved = origins.get(slot);
|
|
@@ -10889,7 +10926,7 @@ class TaskStore {
|
|
|
10889
10926
|
aliases.push(rename.from);
|
|
10890
10927
|
const start = branchStart(location.data, branch, aliases);
|
|
10891
10928
|
const slash = start?.indexOf("/") ?? -1;
|
|
10892
|
-
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(
|
|
10929
|
+
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path7.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
|
|
10893
10930
|
const name = start && (remote ? start.slice(slash + 1) : start);
|
|
10894
10931
|
resolved = {
|
|
10895
10932
|
signature: signatures.join("|"),
|
|
@@ -11127,13 +11164,13 @@ class TaskStore {
|
|
|
11127
11164
|
if (!ready.ok)
|
|
11128
11165
|
return ready;
|
|
11129
11166
|
const dirs = this.paths();
|
|
11130
|
-
const recovery =
|
|
11131
|
-
if (
|
|
11167
|
+
const recovery = path7.join(this.root, ".workit", "recovery");
|
|
11168
|
+
if (fs8.existsSync(recovery)) {
|
|
11132
11169
|
let files = 0;
|
|
11133
11170
|
let bytes = 0;
|
|
11134
11171
|
try {
|
|
11135
|
-
for (const name of
|
|
11136
|
-
const stat =
|
|
11172
|
+
for (const name of fs8.readdirSync(recovery)) {
|
|
11173
|
+
const stat = fs8.lstatSync(path7.join(recovery, name));
|
|
11137
11174
|
if (!stat.isFile())
|
|
11138
11175
|
continue;
|
|
11139
11176
|
files += 1;
|
|
@@ -11142,17 +11179,17 @@ class TaskStore {
|
|
|
11142
11179
|
} catch {}
|
|
11143
11180
|
const remove = options.pruneRecovery === true && !dryRun;
|
|
11144
11181
|
if (remove)
|
|
11145
|
-
|
|
11182
|
+
fs8.rmSync(recovery, { recursive: true, force: true });
|
|
11146
11183
|
report.legacyRecovery = { path: recovery, files, bytes, removed: remove };
|
|
11147
11184
|
}
|
|
11148
|
-
if (!
|
|
11185
|
+
if (!fs8.existsSync(dirs.tasks))
|
|
11149
11186
|
return success(null, null, report);
|
|
11150
|
-
const names =
|
|
11187
|
+
const names = fs8.readdirSync(dirs.tasks).filter((name) => validId(name));
|
|
11151
11188
|
for (const name of names) {
|
|
11152
11189
|
const file = this.eventsPath(name);
|
|
11153
11190
|
let size = 0;
|
|
11154
11191
|
try {
|
|
11155
|
-
size =
|
|
11192
|
+
size = fs8.statSync(file).size;
|
|
11156
11193
|
} catch {
|
|
11157
11194
|
continue;
|
|
11158
11195
|
}
|
|
@@ -11180,16 +11217,16 @@ class TaskStore {
|
|
|
11180
11217
|
const references = new Set;
|
|
11181
11218
|
for (const name of names)
|
|
11182
11219
|
try {
|
|
11183
|
-
for (const match of
|
|
11220
|
+
for (const match of fs8.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
|
|
11184
11221
|
references.add(match[1]);
|
|
11185
11222
|
} catch {}
|
|
11186
|
-
if (
|
|
11187
|
-
for (const name of
|
|
11223
|
+
if (fs8.existsSync(dirs.blobs))
|
|
11224
|
+
for (const name of fs8.readdirSync(dirs.blobs)) {
|
|
11188
11225
|
const digest = name.replace(/\.json$/, "");
|
|
11189
|
-
const file =
|
|
11226
|
+
const file = path7.join(dirs.blobs, name);
|
|
11190
11227
|
let stat;
|
|
11191
11228
|
try {
|
|
11192
|
-
stat =
|
|
11229
|
+
stat = fs8.lstatSync(file);
|
|
11193
11230
|
} catch {
|
|
11194
11231
|
continue;
|
|
11195
11232
|
}
|
|
@@ -11200,11 +11237,11 @@ class TaskStore {
|
|
|
11200
11237
|
report.blobs.removed += 1;
|
|
11201
11238
|
report.blobs.removedBytes += stat.size;
|
|
11202
11239
|
if (!dryRun)
|
|
11203
|
-
|
|
11240
|
+
fs8.rmSync(file, { force: true });
|
|
11204
11241
|
}
|
|
11205
11242
|
return success(null, null, null);
|
|
11206
11243
|
};
|
|
11207
|
-
const swept = dryRun ? sweep() : this.withLock(
|
|
11244
|
+
const swept = dryRun ? sweep() : this.withLock(path7.join(dirs.dir, "gc.lock"), sweep);
|
|
11208
11245
|
if (!swept.ok)
|
|
11209
11246
|
return swept;
|
|
11210
11247
|
const temporaryDirs = [
|
|
@@ -11212,17 +11249,17 @@ class TaskStore {
|
|
|
11212
11249
|
dirs.tasks,
|
|
11213
11250
|
dirs.blobs,
|
|
11214
11251
|
dirs.checkout,
|
|
11215
|
-
...names.map((name) =>
|
|
11252
|
+
...names.map((name) => path7.join(dirs.tasks, name))
|
|
11216
11253
|
];
|
|
11217
11254
|
for (const directory of temporaryDirs) {
|
|
11218
|
-
if (!
|
|
11255
|
+
if (!fs8.existsSync(directory))
|
|
11219
11256
|
continue;
|
|
11220
|
-
for (const name of
|
|
11257
|
+
for (const name of fs8.readdirSync(directory)) {
|
|
11221
11258
|
if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
|
|
11222
11259
|
continue;
|
|
11223
|
-
const file =
|
|
11260
|
+
const file = path7.join(directory, name);
|
|
11224
11261
|
try {
|
|
11225
|
-
const stat =
|
|
11262
|
+
const stat = fs8.lstatSync(file);
|
|
11226
11263
|
if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
|
|
11227
11264
|
continue;
|
|
11228
11265
|
} catch {
|
|
@@ -11230,37 +11267,37 @@ class TaskStore {
|
|
|
11230
11267
|
}
|
|
11231
11268
|
report.temporary.removed += 1;
|
|
11232
11269
|
if (!dryRun)
|
|
11233
|
-
|
|
11270
|
+
fs8.rmSync(file, { force: true });
|
|
11234
11271
|
}
|
|
11235
11272
|
}
|
|
11236
11273
|
return success(null, null, report);
|
|
11237
11274
|
}
|
|
11238
11275
|
paths() {
|
|
11239
11276
|
const location = this.location();
|
|
11240
|
-
const dir = location.ok ? location.data.dir :
|
|
11241
|
-
const checkout =
|
|
11277
|
+
const dir = location.ok ? location.data.dir : path7.join(this.root, ".workit");
|
|
11278
|
+
const checkout = path7.join(dir, "checkouts", checkoutSlug(this.root));
|
|
11242
11279
|
return {
|
|
11243
11280
|
dir,
|
|
11244
11281
|
shared: location.ok ? location.data.shared : false,
|
|
11245
|
-
marker:
|
|
11282
|
+
marker: path7.join(dir, "store.json"),
|
|
11246
11283
|
checkout,
|
|
11247
|
-
workspace:
|
|
11248
|
-
checkoutLock:
|
|
11249
|
-
tasks:
|
|
11250
|
-
index:
|
|
11251
|
-
blobs:
|
|
11252
|
-
legacy:
|
|
11253
|
-
v2:
|
|
11284
|
+
workspace: path7.join(checkout, "workspace.json"),
|
|
11285
|
+
checkoutLock: path7.join(checkout, "metadata.lock"),
|
|
11286
|
+
tasks: path7.join(dir, "tasks"),
|
|
11287
|
+
index: path7.join(dir, "task-index.json"),
|
|
11288
|
+
blobs: path7.join(dir, "blobs", "candidates"),
|
|
11289
|
+
legacy: path7.join(dir, "legacy", checkoutSlug(this.root), "v2"),
|
|
11290
|
+
v2: path7.join(this.root, ".workit")
|
|
11254
11291
|
};
|
|
11255
11292
|
}
|
|
11256
11293
|
eventsPath(taskId) {
|
|
11257
|
-
return
|
|
11294
|
+
return path7.join(this.paths().tasks, taskId, "events.jsonl");
|
|
11258
11295
|
}
|
|
11259
11296
|
snapshotPath(taskId) {
|
|
11260
|
-
return
|
|
11297
|
+
return path7.join(this.paths().tasks, taskId, "snapshot.json");
|
|
11261
11298
|
}
|
|
11262
11299
|
taskLockPath(taskId) {
|
|
11263
|
-
return
|
|
11300
|
+
return path7.join(this.paths().tasks, taskId, "lock");
|
|
11264
11301
|
}
|
|
11265
11302
|
ready(write = false) {
|
|
11266
11303
|
const location = this.location();
|
|
@@ -11270,7 +11307,7 @@ class TaskStore {
|
|
|
11270
11307
|
return success(null, null, null);
|
|
11271
11308
|
const dirs = this.paths();
|
|
11272
11309
|
try {
|
|
11273
|
-
const marker = JSON.parse(
|
|
11310
|
+
const marker = JSON.parse(fs8.readFileSync(dirs.marker, "utf8"));
|
|
11274
11311
|
if (typeof marker.version === "number" && marker.version > STORE_FORMAT)
|
|
11275
11312
|
return failure2("recovery_required", `the workit store at ${dirs.dir} has format ${marker.version}; upgrade Workit to use it`, { path: dirs.marker });
|
|
11276
11313
|
} catch {}
|
|
@@ -11299,21 +11336,21 @@ class TaskStore {
|
|
|
11299
11336
|
return {
|
|
11300
11337
|
read: (digest) => {
|
|
11301
11338
|
try {
|
|
11302
|
-
return JSON.parse(
|
|
11339
|
+
return JSON.parse(fs8.readFileSync(path7.join(dir, `${digest}.json`), "utf8"));
|
|
11303
11340
|
} catch (error) {
|
|
11304
11341
|
throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
|
|
11305
11342
|
}
|
|
11306
11343
|
},
|
|
11307
11344
|
write: (digest, value) => {
|
|
11308
|
-
const file =
|
|
11309
|
-
if (
|
|
11345
|
+
const file = path7.join(dir, `${digest}.json`);
|
|
11346
|
+
if (fs8.existsSync(file)) {
|
|
11310
11347
|
const stamp = new Date;
|
|
11311
11348
|
try {
|
|
11312
|
-
|
|
11349
|
+
fs8.utimesSync(file, stamp, stamp);
|
|
11313
11350
|
return;
|
|
11314
11351
|
} catch {}
|
|
11315
11352
|
}
|
|
11316
|
-
|
|
11353
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
11317
11354
|
replaceFile(file, JSON.stringify(value), true);
|
|
11318
11355
|
}
|
|
11319
11356
|
};
|
|
@@ -11395,7 +11432,7 @@ class TaskStore {
|
|
|
11395
11432
|
}
|
|
11396
11433
|
readSnapshot(taskId) {
|
|
11397
11434
|
try {
|
|
11398
|
-
const value = JSON.parse(
|
|
11435
|
+
const value = JSON.parse(fs8.readFileSync(this.snapshotPath(taskId), "utf8"));
|
|
11399
11436
|
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))
|
|
11400
11437
|
return null;
|
|
11401
11438
|
return value;
|
|
@@ -11497,8 +11534,8 @@ class TaskStore {
|
|
|
11497
11534
|
}
|
|
11498
11535
|
}
|
|
11499
11536
|
openTask(record, type, key, legacy = null) {
|
|
11500
|
-
const dir =
|
|
11501
|
-
|
|
11537
|
+
const dir = path7.join(this.paths().tasks, record.id);
|
|
11538
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
11502
11539
|
const blobs = this.blobStore();
|
|
11503
11540
|
const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
|
|
11504
11541
|
const event = this.event(record.id, 1, type, {
|
|
@@ -11550,11 +11587,11 @@ class TaskStore {
|
|
|
11550
11587
|
} catch {}
|
|
11551
11588
|
if (!latest || latest.events[0]?.id !== last.id)
|
|
11552
11589
|
return failure2("busy", "the task log changed during compaction; retry", { taskId });
|
|
11553
|
-
const appended =
|
|
11590
|
+
const appended = fs8.readFileSync(file).subarray(log.end, latest.end);
|
|
11554
11591
|
const bytes = Buffer.concat([Buffer.from(prefix, "utf8"), appended]);
|
|
11555
11592
|
replaceFile(file, bytes.toString("utf8"), true);
|
|
11556
11593
|
try {
|
|
11557
|
-
|
|
11594
|
+
fs8.rmSync(this.snapshotPath(taskId), { force: true });
|
|
11558
11595
|
} catch {}
|
|
11559
11596
|
const reloaded = this.load(taskId);
|
|
11560
11597
|
if (reloaded.ok && reloaded.data)
|
|
@@ -11567,7 +11604,7 @@ class TaskStore {
|
|
|
11567
11604
|
}
|
|
11568
11605
|
countEvents(file) {
|
|
11569
11606
|
try {
|
|
11570
|
-
const bytes =
|
|
11607
|
+
const bytes = fs8.readFileSync(file);
|
|
11571
11608
|
let count = 0;
|
|
11572
11609
|
for (let index = bytes.indexOf(10);index >= 0; index = bytes.indexOf(10, index + 1))
|
|
11573
11610
|
count += 1;
|
|
@@ -11589,8 +11626,8 @@ class TaskStore {
|
|
|
11589
11626
|
return bound.ok ? success(null, null, bound.data[0] ?? null) : bound;
|
|
11590
11627
|
}
|
|
11591
11628
|
withKeyLock(key, operation) {
|
|
11592
|
-
const digest =
|
|
11593
|
-
return this.withLock(
|
|
11629
|
+
const digest = createHash5("sha256").update(key).digest("hex").slice(0, 32);
|
|
11630
|
+
return this.withLock(path7.join(this.paths().dir, "keys", `${digest}.lock`), operation);
|
|
11594
11631
|
}
|
|
11595
11632
|
bindLocked(taskId, key, timestamp) {
|
|
11596
11633
|
const current = this.readWorkspace();
|
|
@@ -11731,7 +11768,7 @@ class TaskStore {
|
|
|
11731
11768
|
writeWorkspace(record) {
|
|
11732
11769
|
const file = this.paths().workspace;
|
|
11733
11770
|
try {
|
|
11734
|
-
|
|
11771
|
+
fs8.mkdirSync(path7.dirname(file), { recursive: true });
|
|
11735
11772
|
replaceFile(file, `${canonicalJson(record)}
|
|
11736
11773
|
`, true);
|
|
11737
11774
|
return success(null, null, null);
|
|
@@ -11741,7 +11778,7 @@ class TaskStore {
|
|
|
11741
11778
|
}
|
|
11742
11779
|
readIndex() {
|
|
11743
11780
|
try {
|
|
11744
|
-
const value = JSON.parse(
|
|
11781
|
+
const value = JSON.parse(fs8.readFileSync(this.paths().index, "utf8"));
|
|
11745
11782
|
if (!isObject5(value) || value.version !== INDEX_VERSION || !isObject5(value.tasks))
|
|
11746
11783
|
return {};
|
|
11747
11784
|
const tasks = {};
|
|
@@ -11755,7 +11792,7 @@ class TaskStore {
|
|
|
11755
11792
|
}
|
|
11756
11793
|
writeIndex(entries) {
|
|
11757
11794
|
try {
|
|
11758
|
-
|
|
11795
|
+
fs8.mkdirSync(this.paths().tasks, { recursive: true });
|
|
11759
11796
|
replaceFile(this.paths().index, JSON.stringify({
|
|
11760
11797
|
version: INDEX_VERSION,
|
|
11761
11798
|
tasks: Object.fromEntries(entries.map((entry) => [entry.id, entry]))
|
|
@@ -11809,23 +11846,23 @@ class TaskStore {
|
|
|
11809
11846
|
return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
|
|
11810
11847
|
}
|
|
11811
11848
|
v2Dirs() {
|
|
11812
|
-
return [...new Set([this.root, this.origin])].map((dir) =>
|
|
11849
|
+
return [...new Set([this.root, this.origin])].map((dir) => path7.join(dir, ".workit"));
|
|
11813
11850
|
}
|
|
11814
11851
|
pendingMigrations() {
|
|
11815
11852
|
return this.v2Dirs().filter((dir) => {
|
|
11816
|
-
const raw = this.readV2Workspace(
|
|
11853
|
+
const raw = this.readV2Workspace(path7.join(dir, "workspace.json"));
|
|
11817
11854
|
return raw !== null && raw !== TOMBSTONE;
|
|
11818
11855
|
});
|
|
11819
11856
|
}
|
|
11820
11857
|
migrateV2(v2) {
|
|
11821
11858
|
const dirs = this.paths();
|
|
11822
|
-
const v2Workspace =
|
|
11823
|
-
const v2Tasks =
|
|
11824
|
-
const legacy =
|
|
11859
|
+
const v2Workspace = path7.join(v2, "workspace.json");
|
|
11860
|
+
const v2Tasks = path7.join(v2, "tasks");
|
|
11861
|
+
const legacy = path7.join(dirs.dir, "legacy", checkoutSlug(path7.dirname(v2)), "v2");
|
|
11825
11862
|
const raw = this.readV2Workspace(v2Workspace);
|
|
11826
11863
|
if (raw === null || raw === TOMBSTONE)
|
|
11827
11864
|
return success(null, null, null);
|
|
11828
|
-
return this.withLock(dirs.checkoutLock, () => this.withLock(
|
|
11865
|
+
return this.withLock(dirs.checkoutLock, () => this.withLock(path7.join(v2, "metadata.lock"), () => {
|
|
11829
11866
|
const workspace = this.readV2Workspace(v2Workspace);
|
|
11830
11867
|
if (workspace === null || workspace === TOMBSTONE)
|
|
11831
11868
|
return success(null, null, null);
|
|
@@ -11834,7 +11871,7 @@ class TaskStore {
|
|
|
11834
11871
|
return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
|
|
11835
11872
|
let files = [];
|
|
11836
11873
|
try {
|
|
11837
|
-
files =
|
|
11874
|
+
files = fs8.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path7.join(v2Tasks, name)));
|
|
11838
11875
|
} catch {}
|
|
11839
11876
|
const current = this.readWorkspace();
|
|
11840
11877
|
if (!current.ok)
|
|
@@ -11852,22 +11889,22 @@ class TaskStore {
|
|
|
11852
11889
|
let skipped = 0;
|
|
11853
11890
|
const done = new Map;
|
|
11854
11891
|
try {
|
|
11855
|
-
|
|
11892
|
+
fs8.mkdirSync(path7.join(legacy, "tasks"), { recursive: true });
|
|
11856
11893
|
for (const name of ["workspace.json", "index.json"])
|
|
11857
|
-
if (
|
|
11858
|
-
|
|
11894
|
+
if (fs8.existsSync(path7.join(v2, name)))
|
|
11895
|
+
fs8.copyFileSync(path7.join(v2, name), path7.join(legacy, name));
|
|
11859
11896
|
for (const name of files) {
|
|
11860
11897
|
const id = name.slice(0, -5);
|
|
11861
|
-
const file =
|
|
11862
|
-
const bytes =
|
|
11898
|
+
const file = path7.join(v2Tasks, name);
|
|
11899
|
+
const bytes = fs8.readFileSync(file, "utf8");
|
|
11863
11900
|
const digest = sha256(bytes);
|
|
11864
|
-
|
|
11901
|
+
fs8.writeFileSync(path7.join(legacy, "tasks", name), bytes);
|
|
11865
11902
|
const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
|
|
11866
11903
|
if (!parsed.ok || parsed.data.id !== id)
|
|
11867
11904
|
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 });
|
|
11868
11905
|
const record = { ...parsed.data, workspaceId: target.id };
|
|
11869
|
-
const origin = { path:
|
|
11870
|
-
if (!
|
|
11906
|
+
const origin = { path: path7.join(legacy, "tasks", name), digest };
|
|
11907
|
+
if (!fs8.existsSync(this.eventsPath(id))) {
|
|
11871
11908
|
this.openTask(record, "migrated.from_v2", null, origin);
|
|
11872
11909
|
migrated += 1;
|
|
11873
11910
|
} else {
|
|
@@ -11902,8 +11939,8 @@ class TaskStore {
|
|
|
11902
11939
|
try {
|
|
11903
11940
|
let complete = true;
|
|
11904
11941
|
for (const [name, digest] of done) {
|
|
11905
|
-
const file =
|
|
11906
|
-
if (sha256(
|
|
11942
|
+
const file = path7.join(v2Tasks, name);
|
|
11943
|
+
if (sha256(fs8.readFileSync(file, "utf8")) !== digest) {
|
|
11907
11944
|
complete = false;
|
|
11908
11945
|
continue;
|
|
11909
11946
|
}
|
|
@@ -11913,7 +11950,7 @@ class TaskStore {
|
|
|
11913
11950
|
return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
|
|
11914
11951
|
path: v2
|
|
11915
11952
|
});
|
|
11916
|
-
|
|
11953
|
+
fs8.rmSync(path7.join(v2, "index.json"), { force: true });
|
|
11917
11954
|
this.writeTombstone(v2, target.id);
|
|
11918
11955
|
} catch (error) {
|
|
11919
11956
|
return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
|
|
@@ -11933,45 +11970,45 @@ class TaskStore {
|
|
|
11933
11970
|
}
|
|
11934
11971
|
adoptLocalStore() {
|
|
11935
11972
|
const dirs = this.paths();
|
|
11936
|
-
const marker =
|
|
11937
|
-
if (!dirs.shared || !
|
|
11973
|
+
const marker = path7.join(dirs.v2, "store.json");
|
|
11974
|
+
if (!dirs.shared || !fs8.existsSync(marker))
|
|
11938
11975
|
return success(null, null, null);
|
|
11939
11976
|
return this.withLock(dirs.checkoutLock, () => {
|
|
11940
|
-
if (!
|
|
11977
|
+
if (!fs8.existsSync(marker))
|
|
11941
11978
|
return success(null, null, null);
|
|
11942
11979
|
let tasks = 0;
|
|
11943
11980
|
try {
|
|
11944
11981
|
const merge = (from, to) => {
|
|
11945
|
-
if (!
|
|
11946
|
-
|
|
11982
|
+
if (!fs8.existsSync(to)) {
|
|
11983
|
+
fs8.mkdirSync(path7.dirname(to), { recursive: true });
|
|
11947
11984
|
try {
|
|
11948
|
-
|
|
11985
|
+
fs8.renameSync(from, to);
|
|
11949
11986
|
} catch {
|
|
11950
|
-
|
|
11951
|
-
|
|
11987
|
+
fs8.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
|
|
11988
|
+
fs8.rmSync(from, { recursive: true, force: true });
|
|
11952
11989
|
}
|
|
11953
11990
|
return;
|
|
11954
11991
|
}
|
|
11955
|
-
if (
|
|
11956
|
-
for (const name of
|
|
11957
|
-
merge(
|
|
11992
|
+
if (fs8.lstatSync(from).isDirectory() && fs8.lstatSync(to).isDirectory())
|
|
11993
|
+
for (const name of fs8.readdirSync(from))
|
|
11994
|
+
merge(path7.join(from, name), path7.join(to, name));
|
|
11958
11995
|
};
|
|
11959
11996
|
for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
|
|
11960
|
-
const from =
|
|
11961
|
-
if (!
|
|
11997
|
+
const from = path7.join(dirs.v2, sub);
|
|
11998
|
+
if (!fs8.existsSync(from))
|
|
11962
11999
|
continue;
|
|
11963
12000
|
if (sub === "tasks") {
|
|
11964
|
-
for (const name of
|
|
11965
|
-
if (validId(name) && !
|
|
12001
|
+
for (const name of fs8.readdirSync(from))
|
|
12002
|
+
if (validId(name) && !fs8.existsSync(path7.join(dirs.dir, sub, name)))
|
|
11966
12003
|
tasks += 1;
|
|
11967
12004
|
}
|
|
11968
|
-
merge(from,
|
|
12005
|
+
merge(from, path7.join(dirs.dir, sub));
|
|
11969
12006
|
}
|
|
11970
|
-
const ledger =
|
|
11971
|
-
if (
|
|
11972
|
-
merge(ledger,
|
|
11973
|
-
|
|
11974
|
-
|
|
12007
|
+
const ledger = path7.join(dirs.v2, "ledger");
|
|
12008
|
+
if (fs8.existsSync(ledger) && !fs8.existsSync(path7.join(dirs.dir, "ledger")))
|
|
12009
|
+
merge(ledger, path7.join(dirs.dir, "ledger"));
|
|
12010
|
+
fs8.rmSync(path7.join(dirs.v2, "task-index.json"), { force: true });
|
|
12011
|
+
fs8.renameSync(marker, path7.join(dirs.v2, "store.moved.json"));
|
|
11975
12012
|
} catch (error) {
|
|
11976
12013
|
return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
|
|
11977
12014
|
path: dirs.v2
|
|
@@ -11991,7 +12028,7 @@ class TaskStore {
|
|
|
11991
12028
|
readV2Workspace(file) {
|
|
11992
12029
|
let bytes;
|
|
11993
12030
|
try {
|
|
11994
|
-
bytes =
|
|
12031
|
+
bytes = fs8.readFileSync(file, "utf8");
|
|
11995
12032
|
} catch {
|
|
11996
12033
|
return null;
|
|
11997
12034
|
}
|
|
@@ -12009,7 +12046,7 @@ class TaskStore {
|
|
|
12009
12046
|
schemaVersion: SCHEMA_VERSION,
|
|
12010
12047
|
id: workspaceId ?? newId(),
|
|
12011
12048
|
revision: newRevision(),
|
|
12012
|
-
root:
|
|
12049
|
+
root: path7.dirname(v2),
|
|
12013
12050
|
runtime: { createdWith: version, updatedWith: version },
|
|
12014
12051
|
writer: null,
|
|
12015
12052
|
store: {
|
|
@@ -12020,12 +12057,12 @@ class TaskStore {
|
|
|
12020
12057
|
},
|
|
12021
12058
|
critical: ["store"]
|
|
12022
12059
|
};
|
|
12023
|
-
|
|
12024
|
-
const ignore =
|
|
12025
|
-
if (!
|
|
12026
|
-
|
|
12060
|
+
fs8.mkdirSync(v2, { recursive: true });
|
|
12061
|
+
const ignore = path7.join(v2, ".gitignore");
|
|
12062
|
+
if (!fs8.existsSync(ignore))
|
|
12063
|
+
fs8.writeFileSync(ignore, `*
|
|
12027
12064
|
`);
|
|
12028
|
-
replaceFile(
|
|
12065
|
+
replaceFile(path7.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
|
|
12029
12066
|
`, true);
|
|
12030
12067
|
}
|
|
12031
12068
|
stubFor(taskId) {
|
|
@@ -12041,7 +12078,7 @@ class TaskStore {
|
|
|
12041
12078
|
}
|
|
12042
12079
|
isStub(file) {
|
|
12043
12080
|
try {
|
|
12044
|
-
const value = JSON.parse(
|
|
12081
|
+
const value = JSON.parse(fs8.readFileSync(file, "utf8"));
|
|
12045
12082
|
return isObject5(value) && isObject5(value.store) && value.store.format === "workit-store";
|
|
12046
12083
|
} catch {
|
|
12047
12084
|
return false;
|
|
@@ -12049,32 +12086,32 @@ class TaskStore {
|
|
|
12049
12086
|
}
|
|
12050
12087
|
writeTombstoneIfMissing() {
|
|
12051
12088
|
try {
|
|
12052
|
-
const v2 =
|
|
12053
|
-
if (
|
|
12089
|
+
const v2 = path7.join(this.root, ".workit");
|
|
12090
|
+
if (fs8.existsSync(path7.join(v2, "workspace.json")))
|
|
12054
12091
|
return;
|
|
12055
12092
|
let id = null;
|
|
12056
12093
|
try {
|
|
12057
|
-
id = JSON.parse(
|
|
12094
|
+
id = JSON.parse(fs8.readFileSync(this.paths().workspace, "utf8")).id ?? null;
|
|
12058
12095
|
} catch {}
|
|
12059
12096
|
this.writeTombstone(v2, id);
|
|
12060
12097
|
} catch {}
|
|
12061
12098
|
}
|
|
12062
12099
|
initializeStorage() {
|
|
12063
12100
|
const dirs = this.paths();
|
|
12064
|
-
retryTransient(() =>
|
|
12065
|
-
retryTransient(() =>
|
|
12066
|
-
if (!
|
|
12101
|
+
retryTransient(() => fs8.mkdirSync(dirs.checkout, { recursive: true }));
|
|
12102
|
+
retryTransient(() => fs8.mkdirSync(dirs.tasks, { recursive: true }));
|
|
12103
|
+
if (!fs8.existsSync(dirs.marker))
|
|
12067
12104
|
replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
|
|
12068
12105
|
`, false);
|
|
12069
12106
|
if (!dirs.shared) {
|
|
12070
|
-
const ignore =
|
|
12107
|
+
const ignore = path7.join(dirs.dir, ".gitignore");
|
|
12071
12108
|
let current = null;
|
|
12072
12109
|
try {
|
|
12073
|
-
current =
|
|
12110
|
+
current = fs8.readFileSync(ignore, "utf8");
|
|
12074
12111
|
} catch {}
|
|
12075
12112
|
if (current !== `*
|
|
12076
12113
|
`)
|
|
12077
|
-
retryTransient(() =>
|
|
12114
|
+
retryTransient(() => fs8.writeFileSync(ignore, `*
|
|
12078
12115
|
`));
|
|
12079
12116
|
}
|
|
12080
12117
|
this.writeTombstoneIfMissing();
|
|
@@ -12090,7 +12127,7 @@ class TaskStore {
|
|
|
12090
12127
|
shouldReclaim: ({ payload, nowMs }) => {
|
|
12091
12128
|
let ageMs = null;
|
|
12092
12129
|
try {
|
|
12093
|
-
ageMs = nowMs -
|
|
12130
|
+
ageMs = nowMs - fs8.lstatSync(lockPath).mtimeMs;
|
|
12094
12131
|
} catch {}
|
|
12095
12132
|
return classifyLockOwner(payload, ageMs, localLockHost(), TASK_STORE_LOCK).state === "stale";
|
|
12096
12133
|
},
|
|
@@ -12127,7 +12164,7 @@ class TaskStore {
|
|
|
12127
12164
|
withLock(lockPath, operation) {
|
|
12128
12165
|
try {
|
|
12129
12166
|
this.initializeStorage();
|
|
12130
|
-
|
|
12167
|
+
fs8.mkdirSync(path7.dirname(lockPath), { recursive: true });
|
|
12131
12168
|
} catch (error) {
|
|
12132
12169
|
return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
|
|
12133
12170
|
path: this.paths().dir
|
|
@@ -12167,18 +12204,18 @@ class TaskStore {
|
|
|
12167
12204
|
lockDirWritable(lockPath) {
|
|
12168
12205
|
const probe = `${lockPath}.${process.pid}.${randomUUID3()}.probe`;
|
|
12169
12206
|
try {
|
|
12170
|
-
|
|
12207
|
+
fs8.closeSync(fs8.openSync(probe, "wx", 384));
|
|
12171
12208
|
} catch {
|
|
12172
12209
|
return false;
|
|
12173
12210
|
}
|
|
12174
12211
|
try {
|
|
12175
|
-
|
|
12212
|
+
fs8.rmSync(probe, { force: true });
|
|
12176
12213
|
} catch {}
|
|
12177
12214
|
return true;
|
|
12178
12215
|
}
|
|
12179
12216
|
lockHolderPresent(lockPath) {
|
|
12180
12217
|
try {
|
|
12181
|
-
|
|
12218
|
+
fs8.lstatSync(lockPath);
|
|
12182
12219
|
return true;
|
|
12183
12220
|
} catch (error) {
|
|
12184
12221
|
return error?.code !== "ENOENT";
|
|
@@ -12190,12 +12227,12 @@ class TaskStore {
|
|
|
12190
12227
|
if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
|
|
12191
12228
|
return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
|
|
12192
12229
|
path: lockPath,
|
|
12193
|
-
guidance: `Check permissions and the read-only attribute on ${
|
|
12230
|
+
guidance: `Check permissions and the read-only attribute on ${path7.dirname(lockPath)}.`
|
|
12194
12231
|
});
|
|
12195
12232
|
if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
|
|
12196
12233
|
let lock = null;
|
|
12197
12234
|
try {
|
|
12198
|
-
lock = parseMetadataLockOrNull(
|
|
12235
|
+
lock = parseMetadataLockOrNull(fs8.readFileSync(lockPath, "utf8"));
|
|
12199
12236
|
} catch {}
|
|
12200
12237
|
return failure2("busy", `workit lock is held by another Workit call${lock ? ` (pid ${lock.pid} on ${lock.host})` : ""}; retry shortly`, {
|
|
12201
12238
|
outcome: "not_started",
|
|
@@ -12213,15 +12250,15 @@ var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
|
|
|
12213
12250
|
var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
|
|
12214
12251
|
|
|
12215
12252
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
12216
|
-
import { createHash as
|
|
12217
|
-
import * as
|
|
12253
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
12254
|
+
import * as fs13 from "node:fs";
|
|
12218
12255
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
12219
|
-
import
|
|
12256
|
+
import path15 from "node:path";
|
|
12220
12257
|
|
|
12221
12258
|
// packages/workit-core/src/check-config.ts
|
|
12222
12259
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
12223
|
-
import
|
|
12224
|
-
import
|
|
12260
|
+
import fs9 from "node:fs";
|
|
12261
|
+
import path8 from "node:path";
|
|
12225
12262
|
var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
|
|
12226
12263
|
var CHECKS_FILE = "workit.checks.json";
|
|
12227
12264
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -12274,10 +12311,10 @@ var git = (cwd, args) => {
|
|
|
12274
12311
|
return value || null;
|
|
12275
12312
|
};
|
|
12276
12313
|
function checkRoot(cwd) {
|
|
12277
|
-
return
|
|
12314
|
+
return path8.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
|
|
12278
12315
|
}
|
|
12279
12316
|
var packageManager = (root) => {
|
|
12280
|
-
const has = (file) =>
|
|
12317
|
+
const has = (file) => fs9.existsSync(path8.join(root, file));
|
|
12281
12318
|
if (has("bun.lock") || has("bun.lockb"))
|
|
12282
12319
|
return "bun";
|
|
12283
12320
|
if (has("pnpm-lock.yaml"))
|
|
@@ -12297,7 +12334,7 @@ var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
|
|
|
12297
12334
|
function detectedChecks(root) {
|
|
12298
12335
|
const read = (file) => {
|
|
12299
12336
|
try {
|
|
12300
|
-
return
|
|
12337
|
+
return fs9.readFileSync(path8.join(root, file), "utf8");
|
|
12301
12338
|
} catch {
|
|
12302
12339
|
return null;
|
|
12303
12340
|
}
|
|
@@ -12336,7 +12373,7 @@ function detectedChecks(root) {
|
|
|
12336
12373
|
var GATES = ["testing", "verification"];
|
|
12337
12374
|
function loadCheckConfig(cwd) {
|
|
12338
12375
|
const root = checkRoot(cwd);
|
|
12339
|
-
const file =
|
|
12376
|
+
const file = path8.join(root, CHECKS_FILE);
|
|
12340
12377
|
const none = (error = null) => ({
|
|
12341
12378
|
source: error ? CHECKS_FILE : "none",
|
|
12342
12379
|
root,
|
|
@@ -12346,7 +12383,7 @@ function loadCheckConfig(cwd) {
|
|
|
12346
12383
|
});
|
|
12347
12384
|
let text = null;
|
|
12348
12385
|
try {
|
|
12349
|
-
text =
|
|
12386
|
+
text = fs9.readFileSync(file, "utf8");
|
|
12350
12387
|
} catch (error) {
|
|
12351
12388
|
if (error.code !== "ENOENT")
|
|
12352
12389
|
return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
|
|
@@ -12409,10 +12446,10 @@ function workitAvailable(env = process.env) {
|
|
|
12409
12446
|
return workitOnPath;
|
|
12410
12447
|
const windows = process.platform === "win32";
|
|
12411
12448
|
const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
|
|
12412
|
-
const dirs = (env.PATH ?? env.Path ?? "").split(
|
|
12449
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(path8.delimiter).filter(Boolean);
|
|
12413
12450
|
workitOnPath = dirs.some((dir) => exts.some((ext) => {
|
|
12414
12451
|
try {
|
|
12415
|
-
return
|
|
12452
|
+
return fs9.statSync(path8.join(dir, `workit${ext}`)).isFile();
|
|
12416
12453
|
} catch {
|
|
12417
12454
|
return false;
|
|
12418
12455
|
}
|
|
@@ -12425,10 +12462,10 @@ function checkCommand(args, version, onPath = workitAvailable()) {
|
|
|
12425
12462
|
|
|
12426
12463
|
// packages/workit-core/src/git/rev.ts
|
|
12427
12464
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
12428
|
-
import { createHash as
|
|
12429
|
-
import
|
|
12465
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
12466
|
+
import fs10 from "node:fs";
|
|
12430
12467
|
import os from "node:os";
|
|
12431
|
-
import
|
|
12468
|
+
import path9 from "node:path";
|
|
12432
12469
|
var MAX_BUFFER = 256 * 1024 * 1024;
|
|
12433
12470
|
var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
|
|
12434
12471
|
var git2 = (cwd, args, options = {}) => {
|
|
@@ -12463,10 +12500,10 @@ function headSha(cwd) {
|
|
|
12463
12500
|
var emptyTree = (cwd) => line(git2(cwd, ["mktree"], { input: "" }));
|
|
12464
12501
|
var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
|
|
12465
12502
|
var seedIndex = (from, to) => {
|
|
12466
|
-
|
|
12467
|
-
const { atimeNs, mtimeNs } =
|
|
12503
|
+
fs10.copyFileSync(from, to);
|
|
12504
|
+
const { atimeNs, mtimeNs } = fs10.statSync(from, { bigint: true });
|
|
12468
12505
|
const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
|
|
12469
|
-
|
|
12506
|
+
fs10.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
|
|
12470
12507
|
};
|
|
12471
12508
|
function worktreeTree(cwd, options = {}) {
|
|
12472
12509
|
const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
|
|
@@ -12475,10 +12512,10 @@ function worktreeTree(cwd, options = {}) {
|
|
|
12475
12512
|
return null;
|
|
12476
12513
|
const cap = options.maxUntrackedBytes ?? MAX_UNTRACKED_BYTES;
|
|
12477
12514
|
const timeoutMs = options.timeoutMs ?? GIT_TIMEOUTS.worktree;
|
|
12478
|
-
const scratch =
|
|
12515
|
+
const scratch = fs10.mkdtempSync(path9.join(os.tmpdir(), "workit-tree-"));
|
|
12479
12516
|
try {
|
|
12480
|
-
const tempIndex =
|
|
12481
|
-
if (
|
|
12517
|
+
const tempIndex = path9.join(scratch, "index");
|
|
12518
|
+
if (fs10.existsSync(indexPath))
|
|
12482
12519
|
seedIndex(indexPath, tempIndex);
|
|
12483
12520
|
const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
|
|
12484
12521
|
const untracked = git2(top, ["ls-files", "--others", "--exclude-standard", "-z"], {
|
|
@@ -12490,7 +12527,7 @@ function worktreeTree(cwd, options = {}) {
|
|
|
12490
12527
|
const skipped = [];
|
|
12491
12528
|
for (const file of untracked.stdout.split("\x00").filter(Boolean)) {
|
|
12492
12529
|
try {
|
|
12493
|
-
const stat =
|
|
12530
|
+
const stat = fs10.statSync(path9.join(top, file));
|
|
12494
12531
|
if (stat.isFile() && stat.size > cap)
|
|
12495
12532
|
skipped.push({ path: file, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) });
|
|
12496
12533
|
} catch {}
|
|
@@ -12507,10 +12544,10 @@ function worktreeTree(cwd, options = {}) {
|
|
|
12507
12544
|
if (!tree)
|
|
12508
12545
|
return null;
|
|
12509
12546
|
const base = line(git2(top, ["rev-parse", "--verify", "-q", "HEAD^{tree}"])) ?? emptyTree(top);
|
|
12510
|
-
const key = skipped.length ? `sha256:${
|
|
12547
|
+
const key = skipped.length ? `sha256:${createHash6("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
|
|
12511
12548
|
return { tree, key, dirty: tree !== base || skipped.length > 0, skipped };
|
|
12512
12549
|
} finally {
|
|
12513
|
-
|
|
12550
|
+
fs10.rmSync(scratch, { recursive: true, force: true });
|
|
12514
12551
|
}
|
|
12515
12552
|
}
|
|
12516
12553
|
function worktreeSignal(cwd, options = {}) {
|
|
@@ -12530,16 +12567,16 @@ function worktreeSignal(cwd, options = {}) {
|
|
|
12530
12567
|
});
|
|
12531
12568
|
if (!status.ok)
|
|
12532
12569
|
return null;
|
|
12533
|
-
const hash =
|
|
12570
|
+
const hash = createHash6("sha256");
|
|
12534
12571
|
hash.update(`${headSha(top) ?? "unborn"}\x00`);
|
|
12535
12572
|
for (const entry of status.stdout.split("\x00")) {
|
|
12536
12573
|
if (entry.length < 4)
|
|
12537
12574
|
continue;
|
|
12538
12575
|
const file = entry.slice(3);
|
|
12539
|
-
const absolute =
|
|
12576
|
+
const absolute = path9.join(top, file);
|
|
12540
12577
|
let stat = "-";
|
|
12541
12578
|
try {
|
|
12542
|
-
const info =
|
|
12579
|
+
const info = fs10.lstatSync(absolute, { bigint: true });
|
|
12543
12580
|
stat = `${info.mode.toString(8)}:${info.size}:${info.mtimeNs}`;
|
|
12544
12581
|
if (info.isDirectory())
|
|
12545
12582
|
stat += `:${headSha(absolute) ?? "-"}`;
|
|
@@ -12606,17 +12643,17 @@ function pushRemoteName(cwd, branch) {
|
|
|
12606
12643
|
|
|
12607
12644
|
// packages/workit-core/src/core/policy/derive.ts
|
|
12608
12645
|
import { existsSync as existsSync4 } from "node:fs";
|
|
12609
|
-
import
|
|
12646
|
+
import path14 from "node:path";
|
|
12610
12647
|
|
|
12611
12648
|
// packages/workit-core/src/ledger.ts
|
|
12612
12649
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
12613
|
-
import { createHash as
|
|
12614
|
-
import
|
|
12615
|
-
import
|
|
12650
|
+
import { createHash as createHash9, randomBytes as randomBytes2 } from "node:crypto";
|
|
12651
|
+
import fs12 from "node:fs";
|
|
12652
|
+
import path13 from "node:path";
|
|
12616
12653
|
|
|
12617
12654
|
// packages/workit-core/src/core/vcs-config.ts
|
|
12618
|
-
import
|
|
12619
|
-
import
|
|
12655
|
+
import fs11 from "node:fs";
|
|
12656
|
+
import path12 from "node:path";
|
|
12620
12657
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
12621
12658
|
|
|
12622
12659
|
// packages/workit-core/src/core/config.ts
|
|
@@ -12630,7 +12667,7 @@ import {
|
|
|
12630
12667
|
writeFileSync as writeFileSync2
|
|
12631
12668
|
} from "node:fs";
|
|
12632
12669
|
import os2 from "node:os";
|
|
12633
|
-
import
|
|
12670
|
+
import path10 from "node:path";
|
|
12634
12671
|
|
|
12635
12672
|
// packages/workit-core/src/core/boundary.ts
|
|
12636
12673
|
var EVENT = {
|
|
@@ -12674,7 +12711,7 @@ var mergePreset = (preset, input = {}, current = {
|
|
|
12674
12711
|
protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
|
|
12675
12712
|
};
|
|
12676
12713
|
};
|
|
12677
|
-
var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ??
|
|
12714
|
+
var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path10.join(process.env.XDG_CONFIG_HOME || path10.join(os2.homedir(), ".config"), "workit");
|
|
12678
12715
|
var migratedDir = null;
|
|
12679
12716
|
var migrationFailed = false;
|
|
12680
12717
|
var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
@@ -12684,7 +12721,7 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
12684
12721
|
migratedDir = dir;
|
|
12685
12722
|
return dir;
|
|
12686
12723
|
}
|
|
12687
|
-
const legacy =
|
|
12724
|
+
const legacy = path10.join(process.env.XDG_CONFIG_HOME || path10.join(os2.homedir(), ".config"), "workflow-toolkit");
|
|
12688
12725
|
if (!existsSync3(legacy)) {
|
|
12689
12726
|
migratedDir = dir;
|
|
12690
12727
|
return dir;
|
|
@@ -12697,8 +12734,8 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
12697
12734
|
mkdirSync2(dir, { recursive: true });
|
|
12698
12735
|
diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
|
|
12699
12736
|
for (const entry of readdirSync2(legacy, { withFileTypes: true })) {
|
|
12700
|
-
const src =
|
|
12701
|
-
const dest =
|
|
12737
|
+
const src = path10.join(legacy, entry.name);
|
|
12738
|
+
const dest = path10.join(dir, entry.name);
|
|
12702
12739
|
if (existsSync3(dest))
|
|
12703
12740
|
continue;
|
|
12704
12741
|
try {
|
|
@@ -12770,7 +12807,7 @@ var parseConfigResult = (raw, file) => {
|
|
|
12770
12807
|
return { status: "valid", path: file, config };
|
|
12771
12808
|
};
|
|
12772
12809
|
var readConfigTyped = (dir) => {
|
|
12773
|
-
const file =
|
|
12810
|
+
const file = path10.join(dir ?? configDir(), "config.json");
|
|
12774
12811
|
return parseConfigResult(readSafe(file), file);
|
|
12775
12812
|
};
|
|
12776
12813
|
var readConfig = () => {
|
|
@@ -12779,7 +12816,7 @@ var readConfig = () => {
|
|
|
12779
12816
|
throw new Error(result.error);
|
|
12780
12817
|
return result.config ?? DEFAULTS;
|
|
12781
12818
|
};
|
|
12782
|
-
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ??
|
|
12819
|
+
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path10.join(configDir(), "vcs.json");
|
|
12783
12820
|
var branchGlobPattern = (glob) => new RegExp(`^${glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i");
|
|
12784
12821
|
var resolveBranchPolicy = (config, workspace, repoPreset) => {
|
|
12785
12822
|
const wp = workspace?.branchPolicy ?? {};
|
|
@@ -12821,9 +12858,9 @@ var resolveCommitPolicy = (config, workspace) => {
|
|
|
12821
12858
|
|
|
12822
12859
|
// packages/workit-core/src/core/workspaces.ts
|
|
12823
12860
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
12824
|
-
import { createHash as
|
|
12861
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
12825
12862
|
import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
12826
|
-
import
|
|
12863
|
+
import path11 from "node:path";
|
|
12827
12864
|
|
|
12828
12865
|
// packages/workit-core/src/core/branch-policy.ts
|
|
12829
12866
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
@@ -12948,7 +12985,7 @@ var releaseTrackSchema = object({
|
|
|
12948
12985
|
context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
|
|
12949
12986
|
}
|
|
12950
12987
|
}
|
|
12951
|
-
if (track.versionSource?.kind === "package-json" && (
|
|
12988
|
+
if (track.versionSource?.kind === "package-json" && (path11.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
|
|
12952
12989
|
context.addIssue({
|
|
12953
12990
|
code: "custom",
|
|
12954
12991
|
path: ["versionSource", "path"],
|
|
@@ -13008,7 +13045,7 @@ var workspacesFileSchema = object({ workspaces: array(workspaceConfigSchema).opt
|
|
|
13008
13045
|
names.add(workspace.name);
|
|
13009
13046
|
}
|
|
13010
13047
|
});
|
|
13011
|
-
var workspacesRevision = (contents) =>
|
|
13048
|
+
var workspacesRevision = (contents) => createHash7("sha256").update(contents ?? "<missing>").digest("hex");
|
|
13012
13049
|
var validateWorkspacesDocument = (parsed, file) => {
|
|
13013
13050
|
if (!isConfigObject(parsed)) {
|
|
13014
13051
|
return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
|
|
@@ -13046,7 +13083,7 @@ var validateWorkspacesDocument = (parsed, file) => {
|
|
|
13046
13083
|
};
|
|
13047
13084
|
};
|
|
13048
13085
|
var readWorkspacesResult = (dir = configDir()) => {
|
|
13049
|
-
const file =
|
|
13086
|
+
const file = path11.join(dir, "workspaces.json");
|
|
13050
13087
|
let raw;
|
|
13051
13088
|
try {
|
|
13052
13089
|
raw = readFileSync3(file, "utf8");
|
|
@@ -13157,9 +13194,9 @@ var mainCheckoutOf = (cwd) => {
|
|
|
13157
13194
|
let main = null;
|
|
13158
13195
|
const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
|
|
13159
13196
|
const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
|
|
13160
|
-
if (gitDir && common &&
|
|
13197
|
+
if (gitDir && common && path11.resolve(gitDir) !== path11.resolve(common)) {
|
|
13161
13198
|
const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
|
|
13162
|
-
main = listed?.trim() || (
|
|
13199
|
+
main = listed?.trim() || (path11.basename(common) === ".git" ? path11.dirname(common) : common);
|
|
13163
13200
|
}
|
|
13164
13201
|
mainCheckouts.set(cwd, main);
|
|
13165
13202
|
return main;
|
|
@@ -13250,7 +13287,7 @@ var runtimeWorkspaceIndexSchema = object({
|
|
|
13250
13287
|
workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
13251
13288
|
}).passthrough();
|
|
13252
13289
|
var matchRuntimeWorkspace = (cwd) => {
|
|
13253
|
-
const file =
|
|
13290
|
+
const file = path11.join(configDir(), "workspaces.json");
|
|
13254
13291
|
let raw;
|
|
13255
13292
|
try {
|
|
13256
13293
|
raw = readFileSync3(file, "utf8");
|
|
@@ -13325,7 +13362,7 @@ function workspaceDefaultTarget(cwd, globalDefault) {
|
|
|
13325
13362
|
}
|
|
13326
13363
|
|
|
13327
13364
|
// packages/workit-core/src/core/release-tracks.ts
|
|
13328
|
-
import { createHash as
|
|
13365
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
13329
13366
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
13330
13367
|
class ReleaseTrackError extends Error {
|
|
13331
13368
|
}
|
|
@@ -13708,7 +13745,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
13708
13745
|
]) ?? "",
|
|
13709
13746
|
git3(cwd, ["config", "--get", recordedBaseKey(branch)]) ?? ""
|
|
13710
13747
|
] : [];
|
|
13711
|
-
key =
|
|
13748
|
+
key = createHash8("sha256").update(JSON.stringify({ cwd, input, refs, head, own })).digest("hex");
|
|
13712
13749
|
const hit = memo2.get(key);
|
|
13713
13750
|
if (hit)
|
|
13714
13751
|
return hit;
|
|
@@ -13723,7 +13760,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
13723
13760
|
}
|
|
13724
13761
|
|
|
13725
13762
|
// packages/workit-core/src/core/vcs-config.ts
|
|
13726
|
-
var workspacesPath = () =>
|
|
13763
|
+
var workspacesPath = () => path12.join(configDir(), "workspaces.json");
|
|
13727
13764
|
var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
13728
13765
|
var remoteProvider = (cwd) => {
|
|
13729
13766
|
const r = spawnSync8("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
|
|
@@ -13813,7 +13850,7 @@ var readVcsConfig = () => {
|
|
|
13813
13850
|
const file = vcsConfigPath();
|
|
13814
13851
|
let raw;
|
|
13815
13852
|
try {
|
|
13816
|
-
raw =
|
|
13853
|
+
raw = fs11.readFileSync(file, "utf8");
|
|
13817
13854
|
} catch {
|
|
13818
13855
|
return { status: "missing", path: file, config: {} };
|
|
13819
13856
|
}
|
|
@@ -13898,7 +13935,7 @@ function vcsConfig(mode, cwd, options = {}) {
|
|
|
13898
13935
|
const prov = cfg[provider] ?? {};
|
|
13899
13936
|
const out = {
|
|
13900
13937
|
ok: true,
|
|
13901
|
-
configPath:
|
|
13938
|
+
configPath: path12.resolve(cfgPath),
|
|
13902
13939
|
provider,
|
|
13903
13940
|
defaultTargetBranch: workspaceDefault,
|
|
13904
13941
|
pr: cfg.pr ?? {},
|
|
@@ -14109,7 +14146,7 @@ function diffHash(cwd, base, head) {
|
|
|
14109
14146
|
], GIT_TIMEOUTS.worktree);
|
|
14110
14147
|
if (!diff.ok || !diff.stdout)
|
|
14111
14148
|
return null;
|
|
14112
|
-
const hash =
|
|
14149
|
+
const hash = createHash9("sha256");
|
|
14113
14150
|
for (const line of diff.stdout.split(`
|
|
14114
14151
|
`)) {
|
|
14115
14152
|
if (line.startsWith("index "))
|
|
@@ -14121,14 +14158,14 @@ function diffHash(cwd, base, head) {
|
|
|
14121
14158
|
return `sha256:${hash.digest("hex")}`;
|
|
14122
14159
|
}
|
|
14123
14160
|
function storeRoot(cwd) {
|
|
14124
|
-
const location = resolveStore(
|
|
14161
|
+
const location = resolveStore(path13.resolve(cwd));
|
|
14125
14162
|
if (location instanceof Error)
|
|
14126
14163
|
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");
|
|
14127
14164
|
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
14128
14165
|
}
|
|
14129
14166
|
function ledgerPath(cwd) {
|
|
14130
14167
|
const root = storeRoot(cwd);
|
|
14131
|
-
return root.ok ? { ok: true, value:
|
|
14168
|
+
return root.ok ? { ok: true, value: path13.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
14132
14169
|
}
|
|
14133
14170
|
var newId2 = () => `${Date.now().toString(36)}-${randomBytes2(6).toString("hex")}`;
|
|
14134
14171
|
var NETWORK_FS_TYPES = new Set([
|
|
@@ -14148,7 +14185,7 @@ var onNetworkFs = (dir) => {
|
|
|
14148
14185
|
if (process.platform !== "linux")
|
|
14149
14186
|
return false;
|
|
14150
14187
|
try {
|
|
14151
|
-
return isNetworkFsType(
|
|
14188
|
+
return isNetworkFsType(fs12.statfsSync(dir).type);
|
|
14152
14189
|
} catch {
|
|
14153
14190
|
return false;
|
|
14154
14191
|
}
|
|
@@ -14158,15 +14195,15 @@ var LOCK_STALE_MS = 30000;
|
|
|
14158
14195
|
var LOCK_OWNER = "owner";
|
|
14159
14196
|
var lockToken = (lock) => {
|
|
14160
14197
|
try {
|
|
14161
|
-
return
|
|
14198
|
+
return fs12.readFileSync(path13.join(lock, LOCK_OWNER), "utf8");
|
|
14162
14199
|
} catch {
|
|
14163
14200
|
return null;
|
|
14164
14201
|
}
|
|
14165
14202
|
};
|
|
14166
14203
|
var lockAgeMs = (lock) => {
|
|
14167
|
-
for (const target of [
|
|
14204
|
+
for (const target of [path13.join(lock, LOCK_OWNER), lock]) {
|
|
14168
14205
|
try {
|
|
14169
|
-
return Date.now() -
|
|
14206
|
+
return Date.now() - fs12.statSync(target).mtimeMs;
|
|
14170
14207
|
} catch {}
|
|
14171
14208
|
}
|
|
14172
14209
|
return null;
|
|
@@ -14174,14 +14211,14 @@ var lockAgeMs = (lock) => {
|
|
|
14174
14211
|
var ledgerLock = {
|
|
14175
14212
|
acquire(lock) {
|
|
14176
14213
|
try {
|
|
14177
|
-
|
|
14214
|
+
fs12.mkdirSync(lock);
|
|
14178
14215
|
} catch (error) {
|
|
14179
14216
|
if (error.code === "EEXIST")
|
|
14180
14217
|
return null;
|
|
14181
14218
|
throw error;
|
|
14182
14219
|
}
|
|
14183
14220
|
const token = `${process.pid}-${randomBytes2(8).toString("hex")}`;
|
|
14184
|
-
|
|
14221
|
+
fs12.writeFileSync(path13.join(lock, LOCK_OWNER), token);
|
|
14185
14222
|
return token;
|
|
14186
14223
|
},
|
|
14187
14224
|
release(lock, token) {
|
|
@@ -14189,35 +14226,35 @@ var ledgerLock = {
|
|
|
14189
14226
|
return false;
|
|
14190
14227
|
const tomb = `${lock}.release-${randomBytes2(6).toString("hex")}`;
|
|
14191
14228
|
try {
|
|
14192
|
-
|
|
14229
|
+
fs12.renameSync(lock, tomb);
|
|
14193
14230
|
} catch {
|
|
14194
14231
|
return false;
|
|
14195
14232
|
}
|
|
14196
14233
|
if (lockToken(tomb) !== token) {
|
|
14197
14234
|
try {
|
|
14198
|
-
|
|
14235
|
+
fs12.renameSync(tomb, lock);
|
|
14199
14236
|
} catch {}
|
|
14200
14237
|
return false;
|
|
14201
14238
|
}
|
|
14202
|
-
|
|
14239
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
14203
14240
|
return true;
|
|
14204
14241
|
},
|
|
14205
14242
|
token: lockToken,
|
|
14206
14243
|
reap(lock, observed) {
|
|
14207
14244
|
const tomb = `${lock}.reap-${randomBytes2(6).toString("hex")}`;
|
|
14208
14245
|
try {
|
|
14209
|
-
|
|
14246
|
+
fs12.renameSync(lock, tomb);
|
|
14210
14247
|
} catch {
|
|
14211
14248
|
return false;
|
|
14212
14249
|
}
|
|
14213
14250
|
if (lockToken(tomb) === observed) {
|
|
14214
|
-
|
|
14251
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
14215
14252
|
return true;
|
|
14216
14253
|
}
|
|
14217
14254
|
try {
|
|
14218
|
-
|
|
14255
|
+
fs12.renameSync(tomb, lock);
|
|
14219
14256
|
} catch {
|
|
14220
|
-
|
|
14257
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
14221
14258
|
}
|
|
14222
14259
|
return false;
|
|
14223
14260
|
}
|
|
@@ -14244,14 +14281,14 @@ function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
|
14244
14281
|
ledgerLock.release(lock, token);
|
|
14245
14282
|
}
|
|
14246
14283
|
}
|
|
14247
|
-
var sha2562 = (text) =>
|
|
14284
|
+
var sha2562 = (text) => createHash9("sha256").update(text).digest("hex");
|
|
14248
14285
|
var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
|
|
14249
14286
|
${body}`);
|
|
14250
14287
|
var TAIL_BYTES = 64 * 1024;
|
|
14251
14288
|
var lastLine = (fd, size) => {
|
|
14252
14289
|
for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
|
|
14253
14290
|
const buffer = Buffer.alloc(span);
|
|
14254
|
-
|
|
14291
|
+
fs12.readSync(fd, buffer, 0, span, size - span);
|
|
14255
14292
|
const end = buffer.lastIndexOf(10);
|
|
14256
14293
|
if (end < 0) {
|
|
14257
14294
|
if (span === size)
|
|
@@ -14282,13 +14319,13 @@ var lastLine = (fd, size) => {
|
|
|
14282
14319
|
};
|
|
14283
14320
|
function appendLine(file, build, options) {
|
|
14284
14321
|
const write = () => {
|
|
14285
|
-
const fd =
|
|
14322
|
+
const fd = fs12.openSync(file, "a+");
|
|
14286
14323
|
try {
|
|
14287
|
-
const size =
|
|
14324
|
+
const size = fs12.fstatSync(fd).size;
|
|
14288
14325
|
let fence = "";
|
|
14289
14326
|
if (size > 0) {
|
|
14290
14327
|
const last = Buffer.alloc(1);
|
|
14291
|
-
|
|
14328
|
+
fs12.readSync(fd, last, 0, 1, size - 1);
|
|
14292
14329
|
if (last[0] !== 10)
|
|
14293
14330
|
fence = `
|
|
14294
14331
|
`;
|
|
@@ -14297,17 +14334,17 @@ function appendLine(file, build, options) {
|
|
|
14297
14334
|
if (!line.ok)
|
|
14298
14335
|
return line;
|
|
14299
14336
|
const bytes = Buffer.from(fence + line.value);
|
|
14300
|
-
const written =
|
|
14337
|
+
const written = fs12.writeSync(fd, bytes);
|
|
14301
14338
|
if (written !== bytes.length)
|
|
14302
14339
|
return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
|
|
14303
14340
|
return { ok: true, value: undefined };
|
|
14304
14341
|
} finally {
|
|
14305
|
-
|
|
14342
|
+
fs12.closeSync(fd);
|
|
14306
14343
|
}
|
|
14307
14344
|
};
|
|
14308
14345
|
try {
|
|
14309
|
-
|
|
14310
|
-
if (!options.forceLock && !onNetworkFs(
|
|
14346
|
+
fs12.mkdirSync(path13.dirname(file), { recursive: true });
|
|
14347
|
+
if (!options.forceLock && !onNetworkFs(path13.dirname(file)))
|
|
14311
14348
|
return write();
|
|
14312
14349
|
const locked = withLedgerLock(file, write);
|
|
14313
14350
|
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
@@ -14379,7 +14416,7 @@ function readLedger(cwd, options = {}) {
|
|
|
14379
14416
|
return file;
|
|
14380
14417
|
let text = "";
|
|
14381
14418
|
try {
|
|
14382
|
-
text =
|
|
14419
|
+
text = fs12.readFileSync(file.value, "utf8");
|
|
14383
14420
|
} catch (error) {
|
|
14384
14421
|
if (error.code === "ENOENT")
|
|
14385
14422
|
return {
|
|
@@ -14573,11 +14610,20 @@ function authorSessions(cwd, rows, branch, range = { base: null, head: null }, s
|
|
|
14573
14610
|
sessions.add(session);
|
|
14574
14611
|
return sessions;
|
|
14575
14612
|
}
|
|
14613
|
+
var derivesFrom = (derived, bare) => !bare.includes(":") && derived.endsWith(`:${bare}`);
|
|
14614
|
+
var authoredBy = (authors, session) => {
|
|
14615
|
+
if (authors.has(session))
|
|
14616
|
+
return true;
|
|
14617
|
+
for (const author of authors)
|
|
14618
|
+
if (derivesFrom(author, session) || derivesFrom(session, author))
|
|
14619
|
+
return true;
|
|
14620
|
+
return false;
|
|
14621
|
+
};
|
|
14576
14622
|
var independenceReasons = (row, authors) => {
|
|
14577
14623
|
const reasons = [];
|
|
14578
14624
|
if (!row.actor.session)
|
|
14579
14625
|
reasons.push("no_session");
|
|
14580
|
-
else if (authors
|
|
14626
|
+
else if (authoredBy(authors, row.actor.session))
|
|
14581
14627
|
reasons.push("author_session");
|
|
14582
14628
|
if (row.self === true && !reasons.includes("no_session"))
|
|
14583
14629
|
reasons.push("self");
|
|
@@ -14861,7 +14907,7 @@ var productDecision = (task, ledger) => {
|
|
|
14861
14907
|
};
|
|
14862
14908
|
};
|
|
14863
14909
|
var planRecorded = (task, root) => {
|
|
14864
|
-
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(
|
|
14910
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path14.resolve(root, ref.path)));
|
|
14865
14911
|
if (cited)
|
|
14866
14912
|
return {
|
|
14867
14913
|
met: true,
|
|
@@ -14920,15 +14966,15 @@ function writeBlockers(task, root) {
|
|
|
14920
14966
|
}
|
|
14921
14967
|
|
|
14922
14968
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
14923
|
-
var digestBytes = (value) =>
|
|
14924
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
14925
|
-
var relative = (root, candidate) =>
|
|
14969
|
+
var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
|
|
14970
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path15.sep}`);
|
|
14971
|
+
var relative = (root, candidate) => path15.relative(root, candidate).split(path15.sep).join("/") || ".";
|
|
14926
14972
|
var canonicalPath = (value) => {
|
|
14927
14973
|
if (typeof value !== "string" || !value)
|
|
14928
14974
|
return null;
|
|
14929
14975
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
14930
14976
|
return null;
|
|
14931
|
-
const normalized =
|
|
14977
|
+
const normalized = path15.posix.normalize(value);
|
|
14932
14978
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
14933
14979
|
return null;
|
|
14934
14980
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -14976,21 +15022,21 @@ var gitPaths = (root) => {
|
|
|
14976
15022
|
git: !/not a git repository/i.test(stderr)
|
|
14977
15023
|
};
|
|
14978
15024
|
}
|
|
14979
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
15025
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path15.sep).join("/")) : [];
|
|
14980
15026
|
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
14981
15027
|
if (stagedDeleted.status !== 0) {
|
|
14982
15028
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
14983
15029
|
if (!/not a git repository/i.test(stderr))
|
|
14984
15030
|
return { paths, uncertain: true, git: true };
|
|
14985
15031
|
} else if (stagedDeleted.stdout) {
|
|
14986
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
15032
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path15.sep).join("/")));
|
|
14987
15033
|
}
|
|
14988
15034
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
14989
15035
|
};
|
|
14990
15036
|
var walk = (root, directory, output) => {
|
|
14991
15037
|
let names;
|
|
14992
15038
|
try {
|
|
14993
|
-
names =
|
|
15039
|
+
names = fs13.readdirSync(directory);
|
|
14994
15040
|
} catch {
|
|
14995
15041
|
return true;
|
|
14996
15042
|
}
|
|
@@ -14998,10 +15044,10 @@ var walk = (root, directory, output) => {
|
|
|
14998
15044
|
for (const name of names) {
|
|
14999
15045
|
if (name === ".git" || name === ".workit")
|
|
15000
15046
|
continue;
|
|
15001
|
-
const target =
|
|
15047
|
+
const target = path15.join(directory, name);
|
|
15002
15048
|
let stat;
|
|
15003
15049
|
try {
|
|
15004
|
-
stat =
|
|
15050
|
+
stat = fs13.lstatSync(target);
|
|
15005
15051
|
} catch {
|
|
15006
15052
|
uncertain = true;
|
|
15007
15053
|
continue;
|
|
@@ -15016,14 +15062,14 @@ var walk = (root, directory, output) => {
|
|
|
15016
15062
|
var scopeRoots = (root, scope) => {
|
|
15017
15063
|
const roots = [];
|
|
15018
15064
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
15019
|
-
const target =
|
|
15065
|
+
const target = path15.resolve(root, item);
|
|
15020
15066
|
if (!inside(root, target))
|
|
15021
15067
|
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` }] });
|
|
15022
15068
|
let ancestor = target;
|
|
15023
|
-
while (!
|
|
15024
|
-
ancestor =
|
|
15069
|
+
while (!fs13.existsSync(ancestor) && ancestor !== root)
|
|
15070
|
+
ancestor = path15.dirname(ancestor);
|
|
15025
15071
|
try {
|
|
15026
|
-
if (!inside(root,
|
|
15072
|
+
if (!inside(root, fs13.realpathSync(ancestor)))
|
|
15027
15073
|
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` }] });
|
|
15028
15074
|
} catch {
|
|
15029
15075
|
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
@@ -15035,7 +15081,7 @@ var scopeRoots = (root, scope) => {
|
|
|
15035
15081
|
function captureCandidate(root, scope, environment = []) {
|
|
15036
15082
|
let checkout;
|
|
15037
15083
|
try {
|
|
15038
|
-
checkout =
|
|
15084
|
+
checkout = fs13.realpathSync(root);
|
|
15039
15085
|
} catch {
|
|
15040
15086
|
return failure2("invalid_input", "candidate root does not exist");
|
|
15041
15087
|
}
|
|
@@ -15053,11 +15099,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
15053
15099
|
if (item === ".git" || item === ".workit")
|
|
15054
15100
|
continue;
|
|
15055
15101
|
names.add(item);
|
|
15056
|
-
if (!
|
|
15102
|
+
if (!fs13.existsSync(target))
|
|
15057
15103
|
continue;
|
|
15058
15104
|
let stat;
|
|
15059
15105
|
try {
|
|
15060
|
-
stat =
|
|
15106
|
+
stat = fs13.lstatSync(target);
|
|
15061
15107
|
} catch {
|
|
15062
15108
|
uncertain = true;
|
|
15063
15109
|
continue;
|
|
@@ -15073,17 +15119,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
15073
15119
|
for (const item of [...names].toSorted()) {
|
|
15074
15120
|
if (!scopeMatches(item, normalizedScope))
|
|
15075
15121
|
continue;
|
|
15076
|
-
const target =
|
|
15122
|
+
const target = path15.join(checkout, item);
|
|
15077
15123
|
try {
|
|
15078
|
-
const stat =
|
|
15124
|
+
const stat = fs13.lstatSync(target);
|
|
15079
15125
|
if (stat.isSymbolicLink()) {
|
|
15080
|
-
const link =
|
|
15126
|
+
const link = fs13.readlinkSync(target);
|
|
15081
15127
|
files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
|
|
15082
15128
|
} else if (stat.isFile()) {
|
|
15083
15129
|
files.push({
|
|
15084
15130
|
path: item,
|
|
15085
15131
|
kind: "file",
|
|
15086
|
-
digest: digestBytes(
|
|
15132
|
+
digest: digestBytes(fs13.readFileSync(target)),
|
|
15087
15133
|
executable: (stat.mode & 73) !== 0
|
|
15088
15134
|
});
|
|
15089
15135
|
} else if (!stat.isDirectory()) {
|
|
@@ -15248,7 +15294,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
|
|
|
15248
15294
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
15249
15295
|
let root;
|
|
15250
15296
|
try {
|
|
15251
|
-
root =
|
|
15297
|
+
root = fs13.realpathSync(checkoutRoot);
|
|
15252
15298
|
} catch {
|
|
15253
15299
|
return failure2("permission_denied", "checkout root is unavailable");
|
|
15254
15300
|
}
|
|
@@ -15257,23 +15303,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
15257
15303
|
continue;
|
|
15258
15304
|
if (!reference.digest)
|
|
15259
15305
|
return failure2("invalid_input", "document references require a byte digest");
|
|
15260
|
-
const target =
|
|
15261
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
15306
|
+
const target = path15.resolve(root, reference.path);
|
|
15307
|
+
if (target !== root && !target.startsWith(`${root}${path15.sep}`))
|
|
15262
15308
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
15263
15309
|
fields: [
|
|
15264
15310
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
15265
15311
|
]
|
|
15266
15312
|
});
|
|
15267
15313
|
try {
|
|
15268
|
-
const below =
|
|
15314
|
+
const below = path15.relative(root, target);
|
|
15269
15315
|
let current = root;
|
|
15270
|
-
for (const segment of below.split(
|
|
15316
|
+
for (const segment of below.split(path15.sep)) {
|
|
15271
15317
|
if (!segment)
|
|
15272
15318
|
continue;
|
|
15273
|
-
current =
|
|
15274
|
-
const stat =
|
|
15275
|
-
const real =
|
|
15276
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
15319
|
+
current = path15.join(current, segment);
|
|
15320
|
+
const stat = fs13.lstatSync(current);
|
|
15321
|
+
const real = fs13.realpathSync(current);
|
|
15322
|
+
if (real !== root && !real.startsWith(`${root}${path15.sep}`))
|
|
15277
15323
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
15278
15324
|
fields: [
|
|
15279
15325
|
{
|
|
@@ -15289,7 +15335,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
15289
15335
|
if (current !== target && !stat.isDirectory())
|
|
15290
15336
|
return failure2("invalid_input", "document reference ancestor is not a directory");
|
|
15291
15337
|
}
|
|
15292
|
-
if (digestBytes(
|
|
15338
|
+
if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
|
|
15293
15339
|
return failure2("permission_denied", "decision document bytes have changed");
|
|
15294
15340
|
} catch {
|
|
15295
15341
|
return failure2("invalid_input", "decision document is unavailable");
|
|
@@ -15658,7 +15704,8 @@ Autonomy contract:
|
|
|
15658
15704
|
satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
|
|
15659
15705
|
- Author is not verifier: your own verdict is \`--self\`, shown as
|
|
15660
15706
|
self-reviewed, never verified. High risk, \`verification: independent\` and
|
|
15661
|
-
merge need a verifier with its own session (
|
|
15707
|
+
merge need a verifier with its own host-derived session (a subagent's
|
|
15708
|
+
\`<lead>:<child>\`, set by the host); never hand-mint one such as \`<lead>-v1\`.
|
|
15662
15709
|
- A local-commit endpoint does not imply PR readiness, and a local commit alone
|
|
15663
15710
|
is not evidence of a requested remote push. Before reporting delivery,
|
|
15664
15711
|
reconcile every requested item and observe it (\`workit verify-delivery\`).
|
|
@@ -15815,7 +15862,7 @@ var exportDigest = (bundle) => sha256({
|
|
|
15815
15862
|
task: bundle.task
|
|
15816
15863
|
});
|
|
15817
15864
|
// packages/workit-core/src/core/branch.ts
|
|
15818
|
-
import { createHash as
|
|
15865
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
15819
15866
|
|
|
15820
15867
|
// packages/workit-core/src/core/git.ts
|
|
15821
15868
|
import { execFileSync } from "node:child_process";
|
|
@@ -15953,7 +16000,7 @@ var branchPolicySnapshotFor = (workspaceRoot) => {
|
|
|
15953
16000
|
const source = resolved.provenance.branchPolicy;
|
|
15954
16001
|
const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
|
|
15955
16002
|
const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
|
|
15956
|
-
const fingerprint =
|
|
16003
|
+
const fingerprint = createHash11("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
|
|
15957
16004
|
return {
|
|
15958
16005
|
branchPolicy,
|
|
15959
16006
|
namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
|
|
@@ -16209,7 +16256,7 @@ var judgeInputSummary = (raw) => {
|
|
|
16209
16256
|
|
|
16210
16257
|
// packages/workit-core/src/autonomy.ts
|
|
16211
16258
|
import os3 from "node:os";
|
|
16212
|
-
import
|
|
16259
|
+
import path16 from "node:path";
|
|
16213
16260
|
var accountHome = () => {
|
|
16214
16261
|
try {
|
|
16215
16262
|
return os3.userInfo().homedir;
|
|
@@ -16218,10 +16265,10 @@ var accountHome = () => {
|
|
|
16218
16265
|
}
|
|
16219
16266
|
};
|
|
16220
16267
|
var grantsHome = { resolve: accountHome };
|
|
16221
|
-
var grantsDir = () =>
|
|
16268
|
+
var grantsDir = () => path16.join(grantsHome.resolve(), ".config", "workit");
|
|
16222
16269
|
var grantsOverride = () => {
|
|
16223
|
-
const configured =
|
|
16224
|
-
return configured ===
|
|
16270
|
+
const configured = path16.resolve(resolveConfigDir());
|
|
16271
|
+
return configured === path16.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 ${path16.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
|
|
16225
16272
|
};
|
|
16226
16273
|
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
16227
16274
|
var DEFAULT_GRANTS = Object.freeze({
|
|
@@ -17993,7 +18040,7 @@ class WorkitCore {
|
|
|
17993
18040
|
// packages/workit-core/src/core/repo-context.ts
|
|
17994
18041
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
17995
18042
|
import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
|
|
17996
|
-
import
|
|
18043
|
+
import path17 from "node:path";
|
|
17997
18044
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
17998
18045
|
try {
|
|
17999
18046
|
gitRevisionParts(value);
|
|
@@ -18127,7 +18174,7 @@ var findPrTemplate = (cwd) => {
|
|
|
18127
18174
|
]) {
|
|
18128
18175
|
const parts = rel.split("/");
|
|
18129
18176
|
const base = parts[parts.length - 1];
|
|
18130
|
-
const dir = parts.length > 1 ?
|
|
18177
|
+
const dir = parts.length > 1 ? path17.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
18131
18178
|
const actual = probeCaseInsensitive(dir, base);
|
|
18132
18179
|
if (actual)
|
|
18133
18180
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -18158,7 +18205,7 @@ var diffStatForRange = (cwd, range) => {
|
|
|
18158
18205
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
18159
18206
|
};
|
|
18160
18207
|
var packageScripts = (cwd, keys) => {
|
|
18161
|
-
const pkgPath =
|
|
18208
|
+
const pkgPath = path17.join(cwd, "package.json");
|
|
18162
18209
|
if (!existsSync6(pkgPath))
|
|
18163
18210
|
return [];
|
|
18164
18211
|
try {
|
|
@@ -18184,9 +18231,9 @@ var documentationFiles = (cwd) => {
|
|
|
18184
18231
|
if (entry.isDirectory()) {
|
|
18185
18232
|
if (excluded.has(entry.name))
|
|
18186
18233
|
continue;
|
|
18187
|
-
walk(
|
|
18234
|
+
walk(path17.join(dir, entry.name), depth + 1);
|
|
18188
18235
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
18189
|
-
matches.push("./" +
|
|
18236
|
+
matches.push("./" + path17.relative(cwd, path17.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
18190
18237
|
}
|
|
18191
18238
|
}
|
|
18192
18239
|
};
|
|
@@ -18202,7 +18249,7 @@ var readTrimmed2 = (file, maxLines) => {
|
|
|
18202
18249
|
`);
|
|
18203
18250
|
};
|
|
18204
18251
|
function prReadyContext(root, range) {
|
|
18205
|
-
const cwd =
|
|
18252
|
+
const cwd = path17.resolve(repoRoot(root));
|
|
18206
18253
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
18207
18254
|
return invalidRange(cwd);
|
|
18208
18255
|
let stdout = "";
|
|
@@ -18260,7 +18307,7 @@ ${status.stderr}` : "");
|
|
|
18260
18307
|
stdout += `template_path: ${template}
|
|
18261
18308
|
|
|
18262
18309
|
`;
|
|
18263
|
-
stdout += readTrimmed2(
|
|
18310
|
+
stdout += readTrimmed2(path17.join(cwd, template), 220);
|
|
18264
18311
|
} else {
|
|
18265
18312
|
stdout += `template_path: none
|
|
18266
18313
|
|
|
@@ -18270,7 +18317,7 @@ ${status.stderr}` : "");
|
|
|
18270
18317
|
stdout += `
|
|
18271
18318
|
`;
|
|
18272
18319
|
stdout += printSection("Recent Validation Signals");
|
|
18273
|
-
if (existsSync6(
|
|
18320
|
+
if (existsSync6(path17.join(cwd, "package.json"))) {
|
|
18274
18321
|
stdout += `package.json detected. Common scripts:
|
|
18275
18322
|
`;
|
|
18276
18323
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -18279,7 +18326,7 @@ ${status.stderr}` : "");
|
|
|
18279
18326
|
`) + `
|
|
18280
18327
|
`;
|
|
18281
18328
|
}
|
|
18282
|
-
if (existsSync6(
|
|
18329
|
+
if (existsSync6(path17.join(cwd, "Cargo.toml")) || existsSync6(path17.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
18283
18330
|
stdout += `Rust project detected.
|
|
18284
18331
|
`;
|
|
18285
18332
|
}
|
|
@@ -18312,7 +18359,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
18312
18359
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
18313
18360
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
18314
18361
|
function changelogContext(root, range) {
|
|
18315
|
-
const cwd =
|
|
18362
|
+
const cwd = path17.resolve(repoRoot(root));
|
|
18316
18363
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
18317
18364
|
return invalidRange(cwd);
|
|
18318
18365
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -18330,7 +18377,7 @@ function changelogContext(root, range) {
|
|
|
18330
18377
|
stdout += CHANGELOG_RULES + `
|
|
18331
18378
|
`;
|
|
18332
18379
|
stdout += printSection("Existing CHANGELOG.md");
|
|
18333
|
-
const changelogPath =
|
|
18380
|
+
const changelogPath = path17.join(cwd, "CHANGELOG.md");
|
|
18334
18381
|
if (existsSync6(changelogPath)) {
|
|
18335
18382
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
18336
18383
|
`;
|
|
@@ -18350,7 +18397,7 @@ function changelogContext(root, range) {
|
|
|
18350
18397
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
18351
18398
|
}
|
|
18352
18399
|
function docsRefreshContext(root, range) {
|
|
18353
|
-
const cwd =
|
|
18400
|
+
const cwd = path17.resolve(repoRoot(root));
|
|
18354
18401
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
18355
18402
|
return invalidRange(cwd);
|
|
18356
18403
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -18372,7 +18419,7 @@ function docsRefreshContext(root, range) {
|
|
|
18372
18419
|
`) + `
|
|
18373
18420
|
`;
|
|
18374
18421
|
stdout += printSection("README Preview");
|
|
18375
|
-
const readme =
|
|
18422
|
+
const readme = path17.join(cwd, "README.md");
|
|
18376
18423
|
if (existsSync6(readme)) {
|
|
18377
18424
|
stdout += readTrimmed2(readme, 220) + `
|
|
18378
18425
|
`;
|
|
@@ -18381,7 +18428,7 @@ function docsRefreshContext(root, range) {
|
|
|
18381
18428
|
`;
|
|
18382
18429
|
}
|
|
18383
18430
|
stdout += printSection("Package Scripts");
|
|
18384
|
-
const pkgPath =
|
|
18431
|
+
const pkgPath = path17.join(cwd, "package.json");
|
|
18385
18432
|
if (existsSync6(pkgPath)) {
|
|
18386
18433
|
try {
|
|
18387
18434
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
@@ -18395,7 +18442,7 @@ function docsRefreshContext(root, range) {
|
|
|
18395
18442
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
18396
18443
|
}
|
|
18397
18444
|
function releaseNotesContext(root, rangeOrTag) {
|
|
18398
|
-
const cwd =
|
|
18445
|
+
const cwd = path17.resolve(repoRoot(root));
|
|
18399
18446
|
if (!rangeOrTag) {
|
|
18400
18447
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
18401
18448
|
`, exitCode: 1, cwd };
|
|
@@ -18433,7 +18480,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
18433
18480
|
`;
|
|
18434
18481
|
stdout += printSection("Existing Release Files");
|
|
18435
18482
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
18436
|
-
if (existsSync6(
|
|
18483
|
+
if (existsSync6(path17.join(cwd, rel)))
|
|
18437
18484
|
stdout += rel + `
|
|
18438
18485
|
`;
|
|
18439
18486
|
}
|
|
@@ -18464,8 +18511,8 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
18464
18511
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
18465
18512
|
|
|
18466
18513
|
// packages/workit-core/src/core/pr-create.ts
|
|
18467
|
-
import
|
|
18468
|
-
import
|
|
18514
|
+
import fs14 from "node:fs";
|
|
18515
|
+
import path18 from "node:path";
|
|
18469
18516
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
18470
18517
|
// packages/workit-core/src/forge/pr-body.ts
|
|
18471
18518
|
function parseGhIssue(value) {
|
|
@@ -18476,13 +18523,13 @@ function parseGhIssue(value) {
|
|
|
18476
18523
|
// packages/workit-core/src/core/pr-create.ts
|
|
18477
18524
|
function whichOnPath(tool) {
|
|
18478
18525
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
18479
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
18526
|
+
for (const dir of (process.env.PATH ?? "").split(path18.delimiter)) {
|
|
18480
18527
|
if (!dir)
|
|
18481
18528
|
continue;
|
|
18482
18529
|
for (const name of names) {
|
|
18483
|
-
const candidate =
|
|
18530
|
+
const candidate = path18.join(dir, name);
|
|
18484
18531
|
try {
|
|
18485
|
-
|
|
18532
|
+
fs14.accessSync(candidate, fs14.constants.X_OK);
|
|
18486
18533
|
return candidate;
|
|
18487
18534
|
} catch {}
|
|
18488
18535
|
}
|
|
@@ -18600,8 +18647,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
|
18600
18647
|
};
|
|
18601
18648
|
|
|
18602
18649
|
// packages/workit-core/src/core/youtrack.ts
|
|
18603
|
-
import
|
|
18604
|
-
import
|
|
18650
|
+
import fs15 from "node:fs";
|
|
18651
|
+
import path19 from "node:path";
|
|
18605
18652
|
|
|
18606
18653
|
// packages/workit-core/src/core/templates.ts
|
|
18607
18654
|
var repoRoot2 = assetRoot();
|
|
@@ -18614,22 +18661,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
18614
18661
|
// packages/workit-core/src/core/youtrack.ts
|
|
18615
18662
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
18616
18663
|
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
18617
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
18618
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
18664
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path19.join(configDir(), "youtrack.json");
|
|
18665
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path19.join(resolveConfigDir(), "youtrack.json");
|
|
18619
18666
|
var youTrackTokenModeOk = (p) => {
|
|
18620
18667
|
if (process.platform === "win32")
|
|
18621
18668
|
return true;
|
|
18622
|
-
const mode =
|
|
18669
|
+
const mode = fs15.statSync(p).mode & 511;
|
|
18623
18670
|
return mode === 384;
|
|
18624
18671
|
};
|
|
18625
18672
|
function readYouTrackConfig(required) {
|
|
18626
18673
|
const cfgPath = youTrackConfigPath();
|
|
18627
|
-
if (!
|
|
18674
|
+
if (!fs15.existsSync(cfgPath)) {
|
|
18628
18675
|
return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
|
|
18629
18676
|
}
|
|
18630
18677
|
let parsed;
|
|
18631
18678
|
try {
|
|
18632
|
-
parsed = JSON.parse(
|
|
18679
|
+
parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
|
|
18633
18680
|
} catch {
|
|
18634
18681
|
return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
|
|
18635
18682
|
}
|
|
@@ -18640,11 +18687,11 @@ function readYouTrackConfig(required) {
|
|
|
18640
18687
|
}
|
|
18641
18688
|
var readYouTrackContextConfig = () => {
|
|
18642
18689
|
const cfgPath = youTrackReadOnlyConfigPath();
|
|
18643
|
-
if (!
|
|
18690
|
+
if (!fs15.existsSync(cfgPath))
|
|
18644
18691
|
return { data: {} };
|
|
18645
18692
|
let parsed;
|
|
18646
18693
|
try {
|
|
18647
|
-
parsed = JSON.parse(
|
|
18694
|
+
parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
|
|
18648
18695
|
} catch {
|
|
18649
18696
|
return { error: "YouTrack configuration is unavailable" };
|
|
18650
18697
|
}
|
|
@@ -18683,12 +18730,12 @@ var youTrackToken = () => {
|
|
|
18683
18730
|
if ("error" in loaded)
|
|
18684
18731
|
return loaded;
|
|
18685
18732
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
18686
|
-
const tokenPath = tokenFile ?
|
|
18687
|
-
if (!tokenPath || !
|
|
18733
|
+
const tokenPath = tokenFile ? path19.isAbsolute(tokenFile) ? path19.resolve(tokenFile) : path19.resolve(process.cwd(), tokenFile) : "";
|
|
18734
|
+
if (!tokenPath || !fs15.existsSync(tokenPath))
|
|
18688
18735
|
return { error: "missing youtrack.token" };
|
|
18689
18736
|
if (!youTrackTokenModeOk(tokenPath))
|
|
18690
18737
|
return { error: "youtrack.token mode must be 0600" };
|
|
18691
|
-
const token =
|
|
18738
|
+
const token = fs15.readFileSync(tokenPath, "utf8").trim();
|
|
18692
18739
|
if (!token)
|
|
18693
18740
|
return { error: "empty token file" };
|
|
18694
18741
|
if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
|
|
@@ -18753,9 +18800,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
|
|
|
18753
18800
|
} catch {
|
|
18754
18801
|
continue;
|
|
18755
18802
|
}
|
|
18756
|
-
if (!
|
|
18803
|
+
if (!fs15.existsSync(full))
|
|
18757
18804
|
continue;
|
|
18758
|
-
const text =
|
|
18805
|
+
const text = fs15.readFileSync(full, "utf8");
|
|
18759
18806
|
const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
|
|
18760
18807
|
if (m)
|
|
18761
18808
|
return m[1];
|
|
@@ -19040,15 +19087,15 @@ function resolveGitRevision(root, value) {
|
|
|
19040
19087
|
}
|
|
19041
19088
|
function resolveInside(root, candidate) {
|
|
19042
19089
|
const base = realpathSync5(root);
|
|
19043
|
-
const target =
|
|
19044
|
-
if (target !== base && !target.startsWith(base +
|
|
19090
|
+
const target = path20.resolve(base, candidate);
|
|
19091
|
+
if (target !== base && !target.startsWith(base + path20.sep)) {
|
|
19045
19092
|
throw new Error("path must stay inside repository root");
|
|
19046
19093
|
}
|
|
19047
19094
|
let ancestor = target;
|
|
19048
19095
|
while (!existsSync7(ancestor))
|
|
19049
|
-
ancestor =
|
|
19096
|
+
ancestor = path20.dirname(ancestor);
|
|
19050
19097
|
const canonicalAncestor = realpathSync5(ancestor);
|
|
19051
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
19098
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path20.sep)) {
|
|
19052
19099
|
throw new Error("path must stay inside repository root");
|
|
19053
19100
|
}
|
|
19054
19101
|
return target;
|
|
@@ -19092,9 +19139,9 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
19092
19139
|
};
|
|
19093
19140
|
});
|
|
19094
19141
|
// packages/workit-core/src/hooks/context.ts
|
|
19095
|
-
import { createHash as
|
|
19096
|
-
import
|
|
19097
|
-
import
|
|
19142
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
19143
|
+
import fs16 from "node:fs";
|
|
19144
|
+
import path21 from "node:path";
|
|
19098
19145
|
var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
|
|
19099
19146
|
var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
|
|
19100
19147
|
var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
|
|
@@ -19179,7 +19226,7 @@ function entryCompactContext(store, entry, session, context) {
|
|
|
19179
19226
|
return null;
|
|
19180
19227
|
const freshness = { mode: "signal", current: () => recentSignal(store.root) };
|
|
19181
19228
|
const observed = task?.data.evidence.some(isObservedCheck) ?? false;
|
|
19182
|
-
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [
|
|
19229
|
+
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path21.resolve(store.root, ref.path)] : []));
|
|
19183
19230
|
const observedFiles = filesKey(files);
|
|
19184
19231
|
const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
|
|
19185
19232
|
if (!compact.ok || !compact.data)
|
|
@@ -19248,7 +19295,7 @@ var turnContextText = (input, descriptor) => {
|
|
|
19248
19295
|
}
|
|
19249
19296
|
};
|
|
19250
19297
|
var TURN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
19251
|
-
var digestOf = (text) =>
|
|
19298
|
+
var digestOf = (text) => createHash12("sha256").update(text).digest("hex");
|
|
19252
19299
|
var turnMarker = (input) => {
|
|
19253
19300
|
if (!input.session.id)
|
|
19254
19301
|
return null;
|
|
@@ -19257,32 +19304,32 @@ var turnMarker = (input) => {
|
|
|
19257
19304
|
if (location instanceof Error)
|
|
19258
19305
|
return null;
|
|
19259
19306
|
const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
|
|
19260
|
-
return
|
|
19307
|
+
return path21.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
|
|
19261
19308
|
} catch {
|
|
19262
19309
|
return null;
|
|
19263
19310
|
}
|
|
19264
19311
|
};
|
|
19265
19312
|
var writeTurnDigest = (file, digest) => {
|
|
19266
19313
|
try {
|
|
19267
|
-
|
|
19268
|
-
|
|
19314
|
+
fs16.mkdirSync(path21.dirname(file), { recursive: true });
|
|
19315
|
+
fs16.writeFileSync(file, `${JSON.stringify({ digest })}
|
|
19269
19316
|
`);
|
|
19270
19317
|
} catch {}
|
|
19271
19318
|
};
|
|
19272
19319
|
var clearTurnDigest = (file) => {
|
|
19273
19320
|
try {
|
|
19274
|
-
|
|
19321
|
+
fs16.rmSync(file, { force: true });
|
|
19275
19322
|
} catch {}
|
|
19276
19323
|
};
|
|
19277
19324
|
var pruneTurnDigests = (dir, now) => {
|
|
19278
19325
|
try {
|
|
19279
|
-
for (const name of
|
|
19326
|
+
for (const name of fs16.readdirSync(dir)) {
|
|
19280
19327
|
if (!name.startsWith("turn-"))
|
|
19281
19328
|
continue;
|
|
19282
|
-
const file =
|
|
19329
|
+
const file = path21.join(dir, name);
|
|
19283
19330
|
try {
|
|
19284
|
-
if (now -
|
|
19285
|
-
|
|
19331
|
+
if (now - fs16.statSync(file).mtimeMs > TURN_TTL_MS)
|
|
19332
|
+
fs16.rmSync(file, { force: true });
|
|
19286
19333
|
} catch {}
|
|
19287
19334
|
}
|
|
19288
19335
|
} catch {}
|
|
@@ -19298,7 +19345,7 @@ var changedTurnContext = (input, text) => {
|
|
|
19298
19345
|
return text;
|
|
19299
19346
|
const digest = digestOf(text);
|
|
19300
19347
|
try {
|
|
19301
|
-
const previous = JSON.parse(
|
|
19348
|
+
const previous = JSON.parse(fs16.readFileSync(file, "utf8"));
|
|
19302
19349
|
if (previous.digest === digest)
|
|
19303
19350
|
return null;
|
|
19304
19351
|
} catch {}
|
|
@@ -19309,7 +19356,7 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
|
|
|
19309
19356
|
const file = turnMarker(input);
|
|
19310
19357
|
if (!file)
|
|
19311
19358
|
return;
|
|
19312
|
-
pruneTurnDigests(
|
|
19359
|
+
pruneTurnDigests(path21.dirname(file), Date.now());
|
|
19313
19360
|
if (text)
|
|
19314
19361
|
writeTurnDigest(file, digestOf(text));
|
|
19315
19362
|
else
|
|
@@ -19322,7 +19369,7 @@ var shellPolicy = (cwd, command) => {
|
|
|
19322
19369
|
};
|
|
19323
19370
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
19324
19371
|
import { realpathSync as realpathSync6 } from "node:fs";
|
|
19325
|
-
import
|
|
19372
|
+
import path22 from "node:path";
|
|
19326
19373
|
|
|
19327
19374
|
// packages/workit-core/src/hooks/shell-words.ts
|
|
19328
19375
|
var closingQuote = (command, start, dialect) => {
|
|
@@ -19452,22 +19499,22 @@ var NONE2 = { kind: "none" };
|
|
|
19452
19499
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
19453
19500
|
var canonical2 = (file) => {
|
|
19454
19501
|
let rest = "";
|
|
19455
|
-
for (let dir =
|
|
19502
|
+
for (let dir = path22.resolve(file);; dir = path22.dirname(dir)) {
|
|
19456
19503
|
try {
|
|
19457
|
-
return
|
|
19504
|
+
return path22.join(realpathSync6(dir), rest);
|
|
19458
19505
|
} catch {
|
|
19459
|
-
if (
|
|
19460
|
-
return
|
|
19461
|
-
rest = rest ?
|
|
19506
|
+
if (path22.dirname(dir) === dir)
|
|
19507
|
+
return path22.resolve(file);
|
|
19508
|
+
rest = rest ? path22.join(path22.basename(dir), rest) : path22.basename(dir);
|
|
19462
19509
|
}
|
|
19463
19510
|
}
|
|
19464
19511
|
};
|
|
19465
19512
|
var exempt = (cwd, root, file, plans) => {
|
|
19466
19513
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
19467
|
-
const relative =
|
|
19468
|
-
if (relative.startsWith("..") ||
|
|
19514
|
+
const relative = path22.relative(fold(canonical2(root)), fold(canonical2(path22.resolve(cwd, file))));
|
|
19515
|
+
if (relative.startsWith("..") || path22.isAbsolute(relative))
|
|
19469
19516
|
return true;
|
|
19470
|
-
const posix = relative.split(
|
|
19517
|
+
const posix = relative.split(path22.sep).join("/");
|
|
19471
19518
|
const segments = posix.split("/");
|
|
19472
19519
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
19473
19520
|
};
|
|
@@ -19565,7 +19612,7 @@ var shellWrites = (command) => {
|
|
|
19565
19612
|
return { writes: true, targets: unknown ? null : targets };
|
|
19566
19613
|
};
|
|
19567
19614
|
var cache2 = new Map;
|
|
19568
|
-
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
19615
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path22.resolve(root, file)) ?? "-").join("\x00");
|
|
19569
19616
|
var gateFor = (cwd) => {
|
|
19570
19617
|
const store = new TaskStore(cwd);
|
|
19571
19618
|
const listed = store.listTaskIndex();
|
|
@@ -19625,15 +19672,15 @@ import { spawnSync as spawnSync16 } from "node:child_process";
|
|
|
19625
19672
|
|
|
19626
19673
|
// packages/workit-core/src/hooks/raw-git.ts
|
|
19627
19674
|
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
19628
|
-
import { createHash as
|
|
19629
|
-
import
|
|
19675
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
19676
|
+
import fs18 from "node:fs";
|
|
19630
19677
|
import os4 from "node:os";
|
|
19631
|
-
import
|
|
19678
|
+
import path24 from "node:path";
|
|
19632
19679
|
|
|
19633
19680
|
// packages/workit-core/src/hooks/session-marker.ts
|
|
19634
|
-
import { createHash as
|
|
19635
|
-
import
|
|
19636
|
-
import
|
|
19681
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
19682
|
+
import fs17 from "node:fs";
|
|
19683
|
+
import path23 from "node:path";
|
|
19637
19684
|
var sessionMarkerFile = (input, session, store) => {
|
|
19638
19685
|
let dir = store;
|
|
19639
19686
|
if (dir === undefined) {
|
|
@@ -19642,13 +19689,13 @@ var sessionMarkerFile = (input, session, store) => {
|
|
|
19642
19689
|
return null;
|
|
19643
19690
|
dir = location.dir;
|
|
19644
19691
|
}
|
|
19645
|
-
const name =
|
|
19646
|
-
return
|
|
19692
|
+
const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
19693
|
+
return path23.join(dir, "hooks", `skills-${name}.json`);
|
|
19647
19694
|
};
|
|
19648
19695
|
var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
19649
19696
|
var readSessionMarker = (file) => {
|
|
19650
19697
|
try {
|
|
19651
|
-
const value = JSON.parse(
|
|
19698
|
+
const value = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
19652
19699
|
return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
|
|
19653
19700
|
} catch {
|
|
19654
19701
|
return { nudged: [], loaded: [], raw: [] };
|
|
@@ -19656,8 +19703,8 @@ var readSessionMarker = (file) => {
|
|
|
19656
19703
|
};
|
|
19657
19704
|
var writeSessionMarker = (file, marker) => {
|
|
19658
19705
|
try {
|
|
19659
|
-
|
|
19660
|
-
|
|
19706
|
+
fs17.mkdirSync(path23.dirname(file), { recursive: true });
|
|
19707
|
+
fs17.writeFileSync(file, JSON.stringify(marker));
|
|
19661
19708
|
} catch {}
|
|
19662
19709
|
};
|
|
19663
19710
|
|
|
@@ -19678,12 +19725,12 @@ var GIT_VALUE_OPTIONS = new Set([
|
|
|
19678
19725
|
"--config-env",
|
|
19679
19726
|
"--super-prefix"
|
|
19680
19727
|
]);
|
|
19681
|
-
var isAbsolute = (value) =>
|
|
19728
|
+
var isAbsolute = (value) => path24.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
|
|
19682
19729
|
var joinDir = (base, next) => {
|
|
19683
19730
|
if (base === undefined || /[$`*?]/.test(next) || next === "-")
|
|
19684
19731
|
return;
|
|
19685
|
-
const expanded = next === "~" || next.startsWith("~/") ?
|
|
19686
|
-
return base === null || isAbsolute(expanded) ? expanded :
|
|
19732
|
+
const expanded = next === "~" || next.startsWith("~/") ? path24.join(os4.homedir(), next.slice(1)) : next;
|
|
19733
|
+
return base === null || isAbsolute(expanded) ? expanded : path24.join(base, expanded);
|
|
19687
19734
|
};
|
|
19688
19735
|
var cdTarget = (words) => {
|
|
19689
19736
|
const args = words.slice(1);
|
|
@@ -19936,7 +19983,7 @@ var repoAt = (dir) => {
|
|
|
19936
19983
|
return { location, dir, branch: resolveTaskKey(dir, location).branch };
|
|
19937
19984
|
};
|
|
19938
19985
|
var isWorkitWorkspace = (repo) => {
|
|
19939
|
-
if (
|
|
19986
|
+
if (fs18.existsSync(repo.location.dir))
|
|
19940
19987
|
return true;
|
|
19941
19988
|
try {
|
|
19942
19989
|
return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
|
|
@@ -19967,17 +20014,17 @@ var headSha2 = (repo) => {
|
|
|
19967
20014
|
if (!git || git.reftable)
|
|
19968
20015
|
return null;
|
|
19969
20016
|
try {
|
|
19970
|
-
const head =
|
|
20017
|
+
const head = fs18.readFileSync(path24.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
19971
20018
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
19972
20019
|
return head;
|
|
19973
20020
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
19974
20021
|
if (!ref)
|
|
19975
20022
|
return null;
|
|
19976
|
-
const common =
|
|
20023
|
+
const common = path24.dirname(repo.location.dir);
|
|
19977
20024
|
try {
|
|
19978
|
-
return
|
|
20025
|
+
return fs18.readFileSync(path24.join(common, ref), "utf8").trim() || null;
|
|
19979
20026
|
} catch {}
|
|
19980
|
-
const packed =
|
|
20027
|
+
const packed = fs18.readFileSync(path24.join(common, "packed-refs"), "utf8");
|
|
19981
20028
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
19982
20029
|
} catch {
|
|
19983
20030
|
return null;
|
|
@@ -20014,7 +20061,7 @@ var actionsIn = (cwd, command, dialect) => {
|
|
|
20014
20061
|
const action = rawAction(invocation);
|
|
20015
20062
|
if (!action)
|
|
20016
20063
|
continue;
|
|
20017
|
-
const dir =
|
|
20064
|
+
const dir = path24.resolve(cwd, invocation.dir ?? ".");
|
|
20018
20065
|
if (!repos.has(dir)) {
|
|
20019
20066
|
const repo = repoAt(dir);
|
|
20020
20067
|
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
@@ -20083,8 +20130,8 @@ function rawGitPre(input, command, options = {}) {
|
|
|
20083
20130
|
var deny = (reason) => ({ kind: "deny", reason, unblock: null });
|
|
20084
20131
|
var NEXT_COMMAND = "next";
|
|
20085
20132
|
var MARKER_TTL_SECONDS = 3600;
|
|
20086
|
-
var markerFile = (repo, session, key) =>
|
|
20087
|
-
var callKey = (toolUseId, command) => toolUseId ?? `cmd:${
|
|
20133
|
+
var markerFile = (repo, session, key) => path24.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
|
|
20134
|
+
var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`;
|
|
20088
20135
|
function noteRawCommit(input, command, key) {
|
|
20089
20136
|
try {
|
|
20090
20137
|
if (!input.session.id || !/\bcommit\b/.test(command))
|
|
@@ -20094,8 +20141,8 @@ function noteRawCommit(input, command, key) {
|
|
|
20094
20141
|
if (action.kind !== "commit")
|
|
20095
20142
|
continue;
|
|
20096
20143
|
const file = markerFile(repo, input.session.id, key);
|
|
20097
|
-
|
|
20098
|
-
|
|
20144
|
+
fs18.mkdirSync(path24.dirname(file), { recursive: true });
|
|
20145
|
+
fs18.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
20099
20146
|
`);
|
|
20100
20147
|
}
|
|
20101
20148
|
} catch {}
|
|
@@ -20118,11 +20165,11 @@ var headCommit = (dir) => {
|
|
|
20118
20165
|
var settle2 = (input, repo, file) => {
|
|
20119
20166
|
let marker;
|
|
20120
20167
|
try {
|
|
20121
|
-
marker = JSON.parse(
|
|
20168
|
+
marker = JSON.parse(fs18.readFileSync(file, "utf8"));
|
|
20122
20169
|
} catch {
|
|
20123
20170
|
return;
|
|
20124
20171
|
}
|
|
20125
|
-
|
|
20172
|
+
fs18.rmSync(file, { force: true });
|
|
20126
20173
|
const session = input.session.id;
|
|
20127
20174
|
const now = Date.now() / 1000;
|
|
20128
20175
|
if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
|
|
@@ -20171,7 +20218,7 @@ function settlePendingCommit(input) {
|
|
|
20171
20218
|
if (!repo)
|
|
20172
20219
|
return;
|
|
20173
20220
|
const file = markerFile(repo, input.session.id, NEXT_COMMAND);
|
|
20174
|
-
if (
|
|
20221
|
+
if (fs18.existsSync(file))
|
|
20175
20222
|
settle2(input, repo, file);
|
|
20176
20223
|
} catch {}
|
|
20177
20224
|
}
|
|
@@ -20629,6 +20676,8 @@ ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
|
|
|
20629
20676
|
if (role !== "judge" || !event.agentId)
|
|
20630
20677
|
return readOnly;
|
|
20631
20678
|
const own = subagentSession(session, event.agentId);
|
|
20679
|
+
if (session)
|
|
20680
|
+
recordIssuedSession(input.cwd || process.cwd(), session, own);
|
|
20632
20681
|
return `${readOnly} Its own Workit session is ${own}: record verdicts with \`workit ledger verdict <result> --session ${own} ...\` so the ledger tells it apart from the author.`;
|
|
20633
20682
|
};
|
|
20634
20683
|
function handleHook(input, deps) {
|
|
@@ -20990,12 +21039,12 @@ var skillPromptNudge = (prompt, ctx) => process.env.WORKIT_PI_WORKER_SESSION ? n
|
|
|
20990
21039
|
// packages/workit-pi/src/launches.ts
|
|
20991
21040
|
import { mkdirSync as mkdirSync3, readFileSync as readFileSync6, readdirSync as readdirSync5, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
20992
21041
|
import os5 from "node:os";
|
|
20993
|
-
import
|
|
21042
|
+
import path25 from "node:path";
|
|
20994
21043
|
var launchDir = (root) => {
|
|
20995
21044
|
const location = resolveStore(root);
|
|
20996
|
-
return location instanceof Error ? null :
|
|
21045
|
+
return location instanceof Error ? null : path25.join(location.dir, "pi-launches");
|
|
20997
21046
|
};
|
|
20998
|
-
var fileFor = (dir, workerId) =>
|
|
21047
|
+
var fileFor = (dir, workerId) => path25.join(dir, `${workerId.replace(/[^A-Za-z0-9-]/g, "_")}.json`);
|
|
20999
21048
|
var recordLaunch = (root, attempt) => {
|
|
21000
21049
|
const dir = launchDir(root);
|
|
21001
21050
|
if (!dir)
|
|
@@ -21041,7 +21090,7 @@ var abandonedLaunches = (root) => {
|
|
|
21041
21090
|
const found = [];
|
|
21042
21091
|
for (const name of names) {
|
|
21043
21092
|
try {
|
|
21044
|
-
const attempt = JSON.parse(readFileSync6(
|
|
21093
|
+
const attempt = JSON.parse(readFileSync6(path25.join(dir, name), "utf8"));
|
|
21045
21094
|
if (typeof attempt.workerId === "string" && typeof attempt.session === "string") {
|
|
21046
21095
|
if (!alive(attempt))
|
|
21047
21096
|
found.push(attempt);
|
|
@@ -21215,7 +21264,7 @@ function launchWorker(assignment, options) {
|
|
|
21215
21264
|
WORKIT_PI_WORKER_ID: workerId ?? "",
|
|
21216
21265
|
WORKIT_PI_WORKER_SESSION: sessionId,
|
|
21217
21266
|
WORKIT_HOST: "pi",
|
|
21218
|
-
WORKIT_SESSION_ID: sessionId,
|
|
21267
|
+
WORKIT_SESSION_ID: options.leadSession ? childSessionId(options.leadSession, sessionId) : sessionId,
|
|
21219
21268
|
WORKIT_PI_WORKER_ROLE: assignment.role,
|
|
21220
21269
|
WORKIT_PI_WORKER_SCOPE: JSON.stringify(assignment.scope)
|
|
21221
21270
|
};
|
|
@@ -21762,6 +21811,7 @@ function extension(pi) {
|
|
|
21762
21811
|
const handle = launchSupervisedWorker(worker.data.assignment, {
|
|
21763
21812
|
runtime: runtimeFor(ctx),
|
|
21764
21813
|
binding,
|
|
21814
|
+
leadSession: ctx.sessionManager.getSessionId(),
|
|
21765
21815
|
prompt: request.prompt,
|
|
21766
21816
|
onPrepare: (pending) => {
|
|
21767
21817
|
child = pending;
|