@brainervirus/workit-pi 8.9.0 → 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";
|
|
@@ -10563,20 +10600,52 @@ var isNewerVersion = (candidate, current) => {
|
|
|
10563
10600
|
return false;
|
|
10564
10601
|
};
|
|
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);
|
|
10603
|
+
var readText = (file) => {
|
|
10604
|
+
try {
|
|
10605
|
+
return fs8.readFileSync(file, "utf8");
|
|
10606
|
+
} catch {
|
|
10607
|
+
return "";
|
|
10608
|
+
}
|
|
10609
|
+
};
|
|
10610
|
+
var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
|
|
10611
|
+
var branchStart = (location, branch, aliases) => {
|
|
10612
|
+
const first = readText(path7.join(path7.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
|
|
10613
|
+
`, 1)[0];
|
|
10614
|
+
const created = REFLOG_LINE.exec(first);
|
|
10615
|
+
const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
|
|
10616
|
+
if (!created || !start)
|
|
10617
|
+
return null;
|
|
10618
|
+
if (start !== "HEAD")
|
|
10619
|
+
return start;
|
|
10620
|
+
if (!location.git)
|
|
10621
|
+
return null;
|
|
10622
|
+
for (const line of readText(path7.join(location.git.gitDir, "logs", "HEAD")).split(`
|
|
10623
|
+
`)) {
|
|
10624
|
+
const entry = REFLOG_LINE.exec(line);
|
|
10625
|
+
const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
|
|
10626
|
+
const seconds = entry ? Number(entry[2]) - Number(created[2]) : -1;
|
|
10627
|
+
if (moved && entry[1] === created[1] && seconds >= 0 && seconds <= 1 && aliases.includes(moved[2]))
|
|
10628
|
+
return moved[1];
|
|
10629
|
+
}
|
|
10630
|
+
return null;
|
|
10631
|
+
};
|
|
10632
|
+
var refExists = (common, ref) => fs8.existsSync(path7.join(common, ref)) || readText(path7.join(common, "packed-refs")).includes(` ${ref}
|
|
10633
|
+
`);
|
|
10634
|
+
var origins = new Map;
|
|
10566
10635
|
var sameDirectoryIdentity = (left, right) => {
|
|
10567
|
-
if (!
|
|
10636
|
+
if (!path7.isAbsolute(left) || !path7.isAbsolute(right))
|
|
10568
10637
|
return false;
|
|
10569
|
-
const normalizedLeft =
|
|
10570
|
-
const normalizedRight =
|
|
10638
|
+
const normalizedLeft = path7.resolve(left);
|
|
10639
|
+
const normalizedRight = path7.resolve(right);
|
|
10571
10640
|
try {
|
|
10572
|
-
const a =
|
|
10573
|
-
const b =
|
|
10641
|
+
const a = fs8.statSync(normalizedLeft, { bigint: true });
|
|
10642
|
+
const b = fs8.statSync(normalizedRight, { bigint: true });
|
|
10574
10643
|
if (!a.isDirectory() || !b.isDirectory())
|
|
10575
10644
|
return false;
|
|
10576
10645
|
if (process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight)
|
|
10577
10646
|
return true;
|
|
10578
|
-
const canonicalLeft =
|
|
10579
|
-
const canonicalRight =
|
|
10647
|
+
const canonicalLeft = fs8.realpathSync(normalizedLeft);
|
|
10648
|
+
const canonicalRight = fs8.realpathSync(normalizedRight);
|
|
10580
10649
|
if (process.platform === "win32" ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase() : canonicalLeft === canonicalRight)
|
|
10581
10650
|
return true;
|
|
10582
10651
|
return a.dev === b.dev && a.ino !== 0n && a.ino === b.ino;
|
|
@@ -10613,8 +10682,8 @@ class TaskStore {
|
|
|
10613
10682
|
migrationChecked = false;
|
|
10614
10683
|
compactDue = new Set;
|
|
10615
10684
|
constructor(root, options = {}) {
|
|
10616
|
-
const resolved =
|
|
10617
|
-
this.origin =
|
|
10685
|
+
const resolved = path7.resolve(root);
|
|
10686
|
+
this.origin = fs8.existsSync(resolved) ? fs8.realpathSync(resolved) : resolved;
|
|
10618
10687
|
this.located = resolveStore(this.origin);
|
|
10619
10688
|
this.root = checkoutRootOf(this.origin, this.located);
|
|
10620
10689
|
this.lockTimeoutMs = options.lockTimeoutMs ?? defaultLockTimeout();
|
|
@@ -10622,7 +10691,7 @@ class TaskStore {
|
|
|
10622
10691
|
this.migrateOnRead = options.migrateOnRead;
|
|
10623
10692
|
}
|
|
10624
10693
|
location() {
|
|
10625
|
-
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"))) {
|
|
10626
10695
|
this.located = undefined;
|
|
10627
10696
|
this.migrationChecked = false;
|
|
10628
10697
|
}
|
|
@@ -10686,7 +10755,7 @@ class TaskStore {
|
|
|
10686
10755
|
const tasksDir = this.paths().tasks;
|
|
10687
10756
|
let names;
|
|
10688
10757
|
try {
|
|
10689
|
-
names =
|
|
10758
|
+
names = fs8.existsSync(tasksDir) ? fs8.readdirSync(tasksDir).filter((name) => validId(name)) : [];
|
|
10690
10759
|
} catch (error) {
|
|
10691
10760
|
return failure2("storage_error", `unable to list tasks: ${String(error)}`, {
|
|
10692
10761
|
path: tasksDir
|
|
@@ -10727,7 +10796,7 @@ class TaskStore {
|
|
|
10727
10796
|
const file = this.paths().workspace;
|
|
10728
10797
|
let bytes;
|
|
10729
10798
|
try {
|
|
10730
|
-
bytes = retryTransient(() =>
|
|
10799
|
+
bytes = retryTransient(() => fs8.readFileSync(file, "utf8"));
|
|
10731
10800
|
} catch (error) {
|
|
10732
10801
|
if (error.code === "ENOENT")
|
|
10733
10802
|
return success(null, null, null);
|
|
@@ -10778,7 +10847,7 @@ class TaskStore {
|
|
|
10778
10847
|
const moved = this.bindLocked(again.data.id, key, input.now);
|
|
10779
10848
|
return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
|
|
10780
10849
|
}
|
|
10781
|
-
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;
|
|
10782
10851
|
const intent = {
|
|
10783
10852
|
objective: input.objective?.trim() || `Work on ${label}`,
|
|
10784
10853
|
scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
|
|
@@ -10815,6 +10884,66 @@ class TaskStore {
|
|
|
10815
10884
|
return bound;
|
|
10816
10885
|
return success(null, null, { key: key.data, bound: bound.data });
|
|
10817
10886
|
}
|
|
10887
|
+
taskForWorker(workerId) {
|
|
10888
|
+
if (typeof workerId !== "string" || !validId(workerId))
|
|
10889
|
+
return failure2("invalid_input", "needs a worker id: `workit worker cancel|report <workerId>`");
|
|
10890
|
+
const listed = this.listTaskIndex();
|
|
10891
|
+
if (!listed.ok)
|
|
10892
|
+
return listed;
|
|
10893
|
+
for (const entry of listed.data) {
|
|
10894
|
+
if (entry.status === "closed")
|
|
10895
|
+
continue;
|
|
10896
|
+
const loaded = this.load(entry.id);
|
|
10897
|
+
if (!loaded.ok)
|
|
10898
|
+
return loaded;
|
|
10899
|
+
if (loaded.data?.record.workers.some((worker) => worker.id === workerId))
|
|
10900
|
+
return success(null, null, entry.id);
|
|
10901
|
+
}
|
|
10902
|
+
return success(null, null, null);
|
|
10903
|
+
}
|
|
10904
|
+
originTaskEntry() {
|
|
10905
|
+
const key = this.currentKey();
|
|
10906
|
+
const location = this.location();
|
|
10907
|
+
const branch = key.ok ? key.data.branch : null;
|
|
10908
|
+
if (!branch || !location.ok)
|
|
10909
|
+
return success(null, null, null);
|
|
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") : "";
|
|
10913
|
+
const signatures = [
|
|
10914
|
+
path7.join(heads, branch),
|
|
10915
|
+
headLog,
|
|
10916
|
+
heads,
|
|
10917
|
+
path7.join(common, "packed-refs")
|
|
10918
|
+
].map((file) => fileSignature(file) ?? "-");
|
|
10919
|
+
const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
|
|
10920
|
+
let resolved = origins.get(slot);
|
|
10921
|
+
if (resolved?.signature !== signatures.join("|")) {
|
|
10922
|
+
const renames = branchRenames(location.data);
|
|
10923
|
+
const aliases = [branch];
|
|
10924
|
+
for (const rename of renames.toReversed())
|
|
10925
|
+
if (rename.to === aliases.at(-1))
|
|
10926
|
+
aliases.push(rename.from);
|
|
10927
|
+
const start = branchStart(location.data, branch, aliases);
|
|
10928
|
+
const slash = start?.indexOf("/") ?? -1;
|
|
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)}"]`));
|
|
10930
|
+
const name = start && (remote ? start.slice(slash + 1) : start);
|
|
10931
|
+
resolved = {
|
|
10932
|
+
signature: signatures.join("|"),
|
|
10933
|
+
name: name && !aliases.includes(name) ? name : null,
|
|
10934
|
+
renames
|
|
10935
|
+
};
|
|
10936
|
+
origins.set(slot, resolved);
|
|
10937
|
+
}
|
|
10938
|
+
const entry = resolved.name ? this.openTasksForKey(resolved.name, resolved.renames) : null;
|
|
10939
|
+
if (entry && !entry.ok)
|
|
10940
|
+
return entry;
|
|
10941
|
+
return success(null, null, entry?.data[0] ? { origin: `branch ${resolved.name}`, entry: entry.data[0] } : null);
|
|
10942
|
+
}
|
|
10943
|
+
readStoreTask(taskId) {
|
|
10944
|
+
const loaded = this.load(taskId);
|
|
10945
|
+
return loaded.ok ? success(null, null, loaded.data?.record ?? null) : loaded;
|
|
10946
|
+
}
|
|
10818
10947
|
create(input, provenance, expectedWorkspaceRevision) {
|
|
10819
10948
|
const value = "intent" in input ? input : {
|
|
10820
10949
|
intent: input,
|
|
@@ -11035,13 +11164,13 @@ class TaskStore {
|
|
|
11035
11164
|
if (!ready.ok)
|
|
11036
11165
|
return ready;
|
|
11037
11166
|
const dirs = this.paths();
|
|
11038
|
-
const recovery =
|
|
11039
|
-
if (
|
|
11167
|
+
const recovery = path7.join(this.root, ".workit", "recovery");
|
|
11168
|
+
if (fs8.existsSync(recovery)) {
|
|
11040
11169
|
let files = 0;
|
|
11041
11170
|
let bytes = 0;
|
|
11042
11171
|
try {
|
|
11043
|
-
for (const name of
|
|
11044
|
-
const stat =
|
|
11172
|
+
for (const name of fs8.readdirSync(recovery)) {
|
|
11173
|
+
const stat = fs8.lstatSync(path7.join(recovery, name));
|
|
11045
11174
|
if (!stat.isFile())
|
|
11046
11175
|
continue;
|
|
11047
11176
|
files += 1;
|
|
@@ -11050,17 +11179,17 @@ class TaskStore {
|
|
|
11050
11179
|
} catch {}
|
|
11051
11180
|
const remove = options.pruneRecovery === true && !dryRun;
|
|
11052
11181
|
if (remove)
|
|
11053
|
-
|
|
11182
|
+
fs8.rmSync(recovery, { recursive: true, force: true });
|
|
11054
11183
|
report.legacyRecovery = { path: recovery, files, bytes, removed: remove };
|
|
11055
11184
|
}
|
|
11056
|
-
if (!
|
|
11185
|
+
if (!fs8.existsSync(dirs.tasks))
|
|
11057
11186
|
return success(null, null, report);
|
|
11058
|
-
const names =
|
|
11187
|
+
const names = fs8.readdirSync(dirs.tasks).filter((name) => validId(name));
|
|
11059
11188
|
for (const name of names) {
|
|
11060
11189
|
const file = this.eventsPath(name);
|
|
11061
11190
|
let size = 0;
|
|
11062
11191
|
try {
|
|
11063
|
-
size =
|
|
11192
|
+
size = fs8.statSync(file).size;
|
|
11064
11193
|
} catch {
|
|
11065
11194
|
continue;
|
|
11066
11195
|
}
|
|
@@ -11088,16 +11217,16 @@ class TaskStore {
|
|
|
11088
11217
|
const references = new Set;
|
|
11089
11218
|
for (const name of names)
|
|
11090
11219
|
try {
|
|
11091
|
-
for (const match of
|
|
11220
|
+
for (const match of fs8.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
|
|
11092
11221
|
references.add(match[1]);
|
|
11093
11222
|
} catch {}
|
|
11094
|
-
if (
|
|
11095
|
-
for (const name of
|
|
11223
|
+
if (fs8.existsSync(dirs.blobs))
|
|
11224
|
+
for (const name of fs8.readdirSync(dirs.blobs)) {
|
|
11096
11225
|
const digest = name.replace(/\.json$/, "");
|
|
11097
|
-
const file =
|
|
11226
|
+
const file = path7.join(dirs.blobs, name);
|
|
11098
11227
|
let stat;
|
|
11099
11228
|
try {
|
|
11100
|
-
stat =
|
|
11229
|
+
stat = fs8.lstatSync(file);
|
|
11101
11230
|
} catch {
|
|
11102
11231
|
continue;
|
|
11103
11232
|
}
|
|
@@ -11108,11 +11237,11 @@ class TaskStore {
|
|
|
11108
11237
|
report.blobs.removed += 1;
|
|
11109
11238
|
report.blobs.removedBytes += stat.size;
|
|
11110
11239
|
if (!dryRun)
|
|
11111
|
-
|
|
11240
|
+
fs8.rmSync(file, { force: true });
|
|
11112
11241
|
}
|
|
11113
11242
|
return success(null, null, null);
|
|
11114
11243
|
};
|
|
11115
|
-
const swept = dryRun ? sweep() : this.withLock(
|
|
11244
|
+
const swept = dryRun ? sweep() : this.withLock(path7.join(dirs.dir, "gc.lock"), sweep);
|
|
11116
11245
|
if (!swept.ok)
|
|
11117
11246
|
return swept;
|
|
11118
11247
|
const temporaryDirs = [
|
|
@@ -11120,17 +11249,17 @@ class TaskStore {
|
|
|
11120
11249
|
dirs.tasks,
|
|
11121
11250
|
dirs.blobs,
|
|
11122
11251
|
dirs.checkout,
|
|
11123
|
-
...names.map((name) =>
|
|
11252
|
+
...names.map((name) => path7.join(dirs.tasks, name))
|
|
11124
11253
|
];
|
|
11125
11254
|
for (const directory of temporaryDirs) {
|
|
11126
|
-
if (!
|
|
11255
|
+
if (!fs8.existsSync(directory))
|
|
11127
11256
|
continue;
|
|
11128
|
-
for (const name of
|
|
11257
|
+
for (const name of fs8.readdirSync(directory)) {
|
|
11129
11258
|
if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
|
|
11130
11259
|
continue;
|
|
11131
|
-
const file =
|
|
11260
|
+
const file = path7.join(directory, name);
|
|
11132
11261
|
try {
|
|
11133
|
-
const stat =
|
|
11262
|
+
const stat = fs8.lstatSync(file);
|
|
11134
11263
|
if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
|
|
11135
11264
|
continue;
|
|
11136
11265
|
} catch {
|
|
@@ -11138,37 +11267,37 @@ class TaskStore {
|
|
|
11138
11267
|
}
|
|
11139
11268
|
report.temporary.removed += 1;
|
|
11140
11269
|
if (!dryRun)
|
|
11141
|
-
|
|
11270
|
+
fs8.rmSync(file, { force: true });
|
|
11142
11271
|
}
|
|
11143
11272
|
}
|
|
11144
11273
|
return success(null, null, report);
|
|
11145
11274
|
}
|
|
11146
11275
|
paths() {
|
|
11147
11276
|
const location = this.location();
|
|
11148
|
-
const dir = location.ok ? location.data.dir :
|
|
11149
|
-
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));
|
|
11150
11279
|
return {
|
|
11151
11280
|
dir,
|
|
11152
11281
|
shared: location.ok ? location.data.shared : false,
|
|
11153
|
-
marker:
|
|
11282
|
+
marker: path7.join(dir, "store.json"),
|
|
11154
11283
|
checkout,
|
|
11155
|
-
workspace:
|
|
11156
|
-
checkoutLock:
|
|
11157
|
-
tasks:
|
|
11158
|
-
index:
|
|
11159
|
-
blobs:
|
|
11160
|
-
legacy:
|
|
11161
|
-
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")
|
|
11162
11291
|
};
|
|
11163
11292
|
}
|
|
11164
11293
|
eventsPath(taskId) {
|
|
11165
|
-
return
|
|
11294
|
+
return path7.join(this.paths().tasks, taskId, "events.jsonl");
|
|
11166
11295
|
}
|
|
11167
11296
|
snapshotPath(taskId) {
|
|
11168
|
-
return
|
|
11297
|
+
return path7.join(this.paths().tasks, taskId, "snapshot.json");
|
|
11169
11298
|
}
|
|
11170
11299
|
taskLockPath(taskId) {
|
|
11171
|
-
return
|
|
11300
|
+
return path7.join(this.paths().tasks, taskId, "lock");
|
|
11172
11301
|
}
|
|
11173
11302
|
ready(write = false) {
|
|
11174
11303
|
const location = this.location();
|
|
@@ -11178,7 +11307,7 @@ class TaskStore {
|
|
|
11178
11307
|
return success(null, null, null);
|
|
11179
11308
|
const dirs = this.paths();
|
|
11180
11309
|
try {
|
|
11181
|
-
const marker = JSON.parse(
|
|
11310
|
+
const marker = JSON.parse(fs8.readFileSync(dirs.marker, "utf8"));
|
|
11182
11311
|
if (typeof marker.version === "number" && marker.version > STORE_FORMAT)
|
|
11183
11312
|
return failure2("recovery_required", `the workit store at ${dirs.dir} has format ${marker.version}; upgrade Workit to use it`, { path: dirs.marker });
|
|
11184
11313
|
} catch {}
|
|
@@ -11207,21 +11336,21 @@ class TaskStore {
|
|
|
11207
11336
|
return {
|
|
11208
11337
|
read: (digest) => {
|
|
11209
11338
|
try {
|
|
11210
|
-
return JSON.parse(
|
|
11339
|
+
return JSON.parse(fs8.readFileSync(path7.join(dir, `${digest}.json`), "utf8"));
|
|
11211
11340
|
} catch (error) {
|
|
11212
11341
|
throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
|
|
11213
11342
|
}
|
|
11214
11343
|
},
|
|
11215
11344
|
write: (digest, value) => {
|
|
11216
|
-
const file =
|
|
11217
|
-
if (
|
|
11345
|
+
const file = path7.join(dir, `${digest}.json`);
|
|
11346
|
+
if (fs8.existsSync(file)) {
|
|
11218
11347
|
const stamp = new Date;
|
|
11219
11348
|
try {
|
|
11220
|
-
|
|
11349
|
+
fs8.utimesSync(file, stamp, stamp);
|
|
11221
11350
|
return;
|
|
11222
11351
|
} catch {}
|
|
11223
11352
|
}
|
|
11224
|
-
|
|
11353
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
11225
11354
|
replaceFile(file, JSON.stringify(value), true);
|
|
11226
11355
|
}
|
|
11227
11356
|
};
|
|
@@ -11303,7 +11432,7 @@ class TaskStore {
|
|
|
11303
11432
|
}
|
|
11304
11433
|
readSnapshot(taskId) {
|
|
11305
11434
|
try {
|
|
11306
|
-
const value = JSON.parse(
|
|
11435
|
+
const value = JSON.parse(fs8.readFileSync(this.snapshotPath(taskId), "utf8"));
|
|
11307
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))
|
|
11308
11437
|
return null;
|
|
11309
11438
|
return value;
|
|
@@ -11405,8 +11534,8 @@ class TaskStore {
|
|
|
11405
11534
|
}
|
|
11406
11535
|
}
|
|
11407
11536
|
openTask(record, type, key, legacy = null) {
|
|
11408
|
-
const dir =
|
|
11409
|
-
|
|
11537
|
+
const dir = path7.join(this.paths().tasks, record.id);
|
|
11538
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
11410
11539
|
const blobs = this.blobStore();
|
|
11411
11540
|
const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
|
|
11412
11541
|
const event = this.event(record.id, 1, type, {
|
|
@@ -11458,11 +11587,11 @@ class TaskStore {
|
|
|
11458
11587
|
} catch {}
|
|
11459
11588
|
if (!latest || latest.events[0]?.id !== last.id)
|
|
11460
11589
|
return failure2("busy", "the task log changed during compaction; retry", { taskId });
|
|
11461
|
-
const appended =
|
|
11590
|
+
const appended = fs8.readFileSync(file).subarray(log.end, latest.end);
|
|
11462
11591
|
const bytes = Buffer.concat([Buffer.from(prefix, "utf8"), appended]);
|
|
11463
11592
|
replaceFile(file, bytes.toString("utf8"), true);
|
|
11464
11593
|
try {
|
|
11465
|
-
|
|
11594
|
+
fs8.rmSync(this.snapshotPath(taskId), { force: true });
|
|
11466
11595
|
} catch {}
|
|
11467
11596
|
const reloaded = this.load(taskId);
|
|
11468
11597
|
if (reloaded.ok && reloaded.data)
|
|
@@ -11475,7 +11604,7 @@ class TaskStore {
|
|
|
11475
11604
|
}
|
|
11476
11605
|
countEvents(file) {
|
|
11477
11606
|
try {
|
|
11478
|
-
const bytes =
|
|
11607
|
+
const bytes = fs8.readFileSync(file);
|
|
11479
11608
|
let count = 0;
|
|
11480
11609
|
for (let index = bytes.indexOf(10);index >= 0; index = bytes.indexOf(10, index + 1))
|
|
11481
11610
|
count += 1;
|
|
@@ -11484,12 +11613,12 @@ class TaskStore {
|
|
|
11484
11613
|
return 0;
|
|
11485
11614
|
}
|
|
11486
11615
|
}
|
|
11487
|
-
openTasksForKey(key) {
|
|
11616
|
+
openTasksForKey(key, known) {
|
|
11488
11617
|
const all = this.listStoreIndex();
|
|
11489
11618
|
if (!all.ok)
|
|
11490
11619
|
return all;
|
|
11491
|
-
const location = this.location();
|
|
11492
|
-
const renames = location
|
|
11620
|
+
const location = known ? null : this.location();
|
|
11621
|
+
const renames = known ?? (location?.ok ? branchRenames(location.data) : []);
|
|
11493
11622
|
return success(null, null, all.data.filter((entry) => entry.key !== null && entry.status !== "closed").filter((entry) => (renames.length ? followRenames(entry.key, entry.boundAt ?? entry.createdAt, renames) : entry.key) === key).toSorted(oldestFirst));
|
|
11494
11623
|
}
|
|
11495
11624
|
openTaskForKey(key) {
|
|
@@ -11497,8 +11626,8 @@ class TaskStore {
|
|
|
11497
11626
|
return bound.ok ? success(null, null, bound.data[0] ?? null) : bound;
|
|
11498
11627
|
}
|
|
11499
11628
|
withKeyLock(key, operation) {
|
|
11500
|
-
const digest =
|
|
11501
|
-
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);
|
|
11502
11631
|
}
|
|
11503
11632
|
bindLocked(taskId, key, timestamp) {
|
|
11504
11633
|
const current = this.readWorkspace();
|
|
@@ -11639,7 +11768,7 @@ class TaskStore {
|
|
|
11639
11768
|
writeWorkspace(record) {
|
|
11640
11769
|
const file = this.paths().workspace;
|
|
11641
11770
|
try {
|
|
11642
|
-
|
|
11771
|
+
fs8.mkdirSync(path7.dirname(file), { recursive: true });
|
|
11643
11772
|
replaceFile(file, `${canonicalJson(record)}
|
|
11644
11773
|
`, true);
|
|
11645
11774
|
return success(null, null, null);
|
|
@@ -11649,7 +11778,7 @@ class TaskStore {
|
|
|
11649
11778
|
}
|
|
11650
11779
|
readIndex() {
|
|
11651
11780
|
try {
|
|
11652
|
-
const value = JSON.parse(
|
|
11781
|
+
const value = JSON.parse(fs8.readFileSync(this.paths().index, "utf8"));
|
|
11653
11782
|
if (!isObject5(value) || value.version !== INDEX_VERSION || !isObject5(value.tasks))
|
|
11654
11783
|
return {};
|
|
11655
11784
|
const tasks = {};
|
|
@@ -11663,7 +11792,7 @@ class TaskStore {
|
|
|
11663
11792
|
}
|
|
11664
11793
|
writeIndex(entries) {
|
|
11665
11794
|
try {
|
|
11666
|
-
|
|
11795
|
+
fs8.mkdirSync(this.paths().tasks, { recursive: true });
|
|
11667
11796
|
replaceFile(this.paths().index, JSON.stringify({
|
|
11668
11797
|
version: INDEX_VERSION,
|
|
11669
11798
|
tasks: Object.fromEntries(entries.map((entry) => [entry.id, entry]))
|
|
@@ -11717,23 +11846,23 @@ class TaskStore {
|
|
|
11717
11846
|
return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
|
|
11718
11847
|
}
|
|
11719
11848
|
v2Dirs() {
|
|
11720
|
-
return [...new Set([this.root, this.origin])].map((dir) =>
|
|
11849
|
+
return [...new Set([this.root, this.origin])].map((dir) => path7.join(dir, ".workit"));
|
|
11721
11850
|
}
|
|
11722
11851
|
pendingMigrations() {
|
|
11723
11852
|
return this.v2Dirs().filter((dir) => {
|
|
11724
|
-
const raw = this.readV2Workspace(
|
|
11853
|
+
const raw = this.readV2Workspace(path7.join(dir, "workspace.json"));
|
|
11725
11854
|
return raw !== null && raw !== TOMBSTONE;
|
|
11726
11855
|
});
|
|
11727
11856
|
}
|
|
11728
11857
|
migrateV2(v2) {
|
|
11729
11858
|
const dirs = this.paths();
|
|
11730
|
-
const v2Workspace =
|
|
11731
|
-
const v2Tasks =
|
|
11732
|
-
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");
|
|
11733
11862
|
const raw = this.readV2Workspace(v2Workspace);
|
|
11734
11863
|
if (raw === null || raw === TOMBSTONE)
|
|
11735
11864
|
return success(null, null, null);
|
|
11736
|
-
return this.withLock(dirs.checkoutLock, () => this.withLock(
|
|
11865
|
+
return this.withLock(dirs.checkoutLock, () => this.withLock(path7.join(v2, "metadata.lock"), () => {
|
|
11737
11866
|
const workspace = this.readV2Workspace(v2Workspace);
|
|
11738
11867
|
if (workspace === null || workspace === TOMBSTONE)
|
|
11739
11868
|
return success(null, null, null);
|
|
@@ -11742,7 +11871,7 @@ class TaskStore {
|
|
|
11742
11871
|
return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
|
|
11743
11872
|
let files = [];
|
|
11744
11873
|
try {
|
|
11745
|
-
files =
|
|
11874
|
+
files = fs8.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path7.join(v2Tasks, name)));
|
|
11746
11875
|
} catch {}
|
|
11747
11876
|
const current = this.readWorkspace();
|
|
11748
11877
|
if (!current.ok)
|
|
@@ -11760,22 +11889,22 @@ class TaskStore {
|
|
|
11760
11889
|
let skipped = 0;
|
|
11761
11890
|
const done = new Map;
|
|
11762
11891
|
try {
|
|
11763
|
-
|
|
11892
|
+
fs8.mkdirSync(path7.join(legacy, "tasks"), { recursive: true });
|
|
11764
11893
|
for (const name of ["workspace.json", "index.json"])
|
|
11765
|
-
if (
|
|
11766
|
-
|
|
11894
|
+
if (fs8.existsSync(path7.join(v2, name)))
|
|
11895
|
+
fs8.copyFileSync(path7.join(v2, name), path7.join(legacy, name));
|
|
11767
11896
|
for (const name of files) {
|
|
11768
11897
|
const id = name.slice(0, -5);
|
|
11769
|
-
const file =
|
|
11770
|
-
const bytes =
|
|
11898
|
+
const file = path7.join(v2Tasks, name);
|
|
11899
|
+
const bytes = fs8.readFileSync(file, "utf8");
|
|
11771
11900
|
const digest = sha256(bytes);
|
|
11772
|
-
|
|
11901
|
+
fs8.writeFileSync(path7.join(legacy, "tasks", name), bytes);
|
|
11773
11902
|
const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
|
|
11774
11903
|
if (!parsed.ok || parsed.data.id !== id)
|
|
11775
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 });
|
|
11776
11905
|
const record = { ...parsed.data, workspaceId: target.id };
|
|
11777
|
-
const origin = { path:
|
|
11778
|
-
if (!
|
|
11906
|
+
const origin = { path: path7.join(legacy, "tasks", name), digest };
|
|
11907
|
+
if (!fs8.existsSync(this.eventsPath(id))) {
|
|
11779
11908
|
this.openTask(record, "migrated.from_v2", null, origin);
|
|
11780
11909
|
migrated += 1;
|
|
11781
11910
|
} else {
|
|
@@ -11810,8 +11939,8 @@ class TaskStore {
|
|
|
11810
11939
|
try {
|
|
11811
11940
|
let complete = true;
|
|
11812
11941
|
for (const [name, digest] of done) {
|
|
11813
|
-
const file =
|
|
11814
|
-
if (sha256(
|
|
11942
|
+
const file = path7.join(v2Tasks, name);
|
|
11943
|
+
if (sha256(fs8.readFileSync(file, "utf8")) !== digest) {
|
|
11815
11944
|
complete = false;
|
|
11816
11945
|
continue;
|
|
11817
11946
|
}
|
|
@@ -11821,7 +11950,7 @@ class TaskStore {
|
|
|
11821
11950
|
return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
|
|
11822
11951
|
path: v2
|
|
11823
11952
|
});
|
|
11824
|
-
|
|
11953
|
+
fs8.rmSync(path7.join(v2, "index.json"), { force: true });
|
|
11825
11954
|
this.writeTombstone(v2, target.id);
|
|
11826
11955
|
} catch (error) {
|
|
11827
11956
|
return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
|
|
@@ -11841,45 +11970,45 @@ class TaskStore {
|
|
|
11841
11970
|
}
|
|
11842
11971
|
adoptLocalStore() {
|
|
11843
11972
|
const dirs = this.paths();
|
|
11844
|
-
const marker =
|
|
11845
|
-
if (!dirs.shared || !
|
|
11973
|
+
const marker = path7.join(dirs.v2, "store.json");
|
|
11974
|
+
if (!dirs.shared || !fs8.existsSync(marker))
|
|
11846
11975
|
return success(null, null, null);
|
|
11847
11976
|
return this.withLock(dirs.checkoutLock, () => {
|
|
11848
|
-
if (!
|
|
11977
|
+
if (!fs8.existsSync(marker))
|
|
11849
11978
|
return success(null, null, null);
|
|
11850
11979
|
let tasks = 0;
|
|
11851
11980
|
try {
|
|
11852
11981
|
const merge = (from, to) => {
|
|
11853
|
-
if (!
|
|
11854
|
-
|
|
11982
|
+
if (!fs8.existsSync(to)) {
|
|
11983
|
+
fs8.mkdirSync(path7.dirname(to), { recursive: true });
|
|
11855
11984
|
try {
|
|
11856
|
-
|
|
11985
|
+
fs8.renameSync(from, to);
|
|
11857
11986
|
} catch {
|
|
11858
|
-
|
|
11859
|
-
|
|
11987
|
+
fs8.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
|
|
11988
|
+
fs8.rmSync(from, { recursive: true, force: true });
|
|
11860
11989
|
}
|
|
11861
11990
|
return;
|
|
11862
11991
|
}
|
|
11863
|
-
if (
|
|
11864
|
-
for (const name of
|
|
11865
|
-
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));
|
|
11866
11995
|
};
|
|
11867
11996
|
for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
|
|
11868
|
-
const from =
|
|
11869
|
-
if (!
|
|
11997
|
+
const from = path7.join(dirs.v2, sub);
|
|
11998
|
+
if (!fs8.existsSync(from))
|
|
11870
11999
|
continue;
|
|
11871
12000
|
if (sub === "tasks") {
|
|
11872
|
-
for (const name of
|
|
11873
|
-
if (validId(name) && !
|
|
12001
|
+
for (const name of fs8.readdirSync(from))
|
|
12002
|
+
if (validId(name) && !fs8.existsSync(path7.join(dirs.dir, sub, name)))
|
|
11874
12003
|
tasks += 1;
|
|
11875
12004
|
}
|
|
11876
|
-
merge(from,
|
|
12005
|
+
merge(from, path7.join(dirs.dir, sub));
|
|
11877
12006
|
}
|
|
11878
|
-
const ledger =
|
|
11879
|
-
if (
|
|
11880
|
-
merge(ledger,
|
|
11881
|
-
|
|
11882
|
-
|
|
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"));
|
|
11883
12012
|
} catch (error) {
|
|
11884
12013
|
return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
|
|
11885
12014
|
path: dirs.v2
|
|
@@ -11899,7 +12028,7 @@ class TaskStore {
|
|
|
11899
12028
|
readV2Workspace(file) {
|
|
11900
12029
|
let bytes;
|
|
11901
12030
|
try {
|
|
11902
|
-
bytes =
|
|
12031
|
+
bytes = fs8.readFileSync(file, "utf8");
|
|
11903
12032
|
} catch {
|
|
11904
12033
|
return null;
|
|
11905
12034
|
}
|
|
@@ -11917,7 +12046,7 @@ class TaskStore {
|
|
|
11917
12046
|
schemaVersion: SCHEMA_VERSION,
|
|
11918
12047
|
id: workspaceId ?? newId(),
|
|
11919
12048
|
revision: newRevision(),
|
|
11920
|
-
root:
|
|
12049
|
+
root: path7.dirname(v2),
|
|
11921
12050
|
runtime: { createdWith: version, updatedWith: version },
|
|
11922
12051
|
writer: null,
|
|
11923
12052
|
store: {
|
|
@@ -11928,12 +12057,12 @@ class TaskStore {
|
|
|
11928
12057
|
},
|
|
11929
12058
|
critical: ["store"]
|
|
11930
12059
|
};
|
|
11931
|
-
|
|
11932
|
-
const ignore =
|
|
11933
|
-
if (!
|
|
11934
|
-
|
|
12060
|
+
fs8.mkdirSync(v2, { recursive: true });
|
|
12061
|
+
const ignore = path7.join(v2, ".gitignore");
|
|
12062
|
+
if (!fs8.existsSync(ignore))
|
|
12063
|
+
fs8.writeFileSync(ignore, `*
|
|
11935
12064
|
`);
|
|
11936
|
-
replaceFile(
|
|
12065
|
+
replaceFile(path7.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
|
|
11937
12066
|
`, true);
|
|
11938
12067
|
}
|
|
11939
12068
|
stubFor(taskId) {
|
|
@@ -11949,7 +12078,7 @@ class TaskStore {
|
|
|
11949
12078
|
}
|
|
11950
12079
|
isStub(file) {
|
|
11951
12080
|
try {
|
|
11952
|
-
const value = JSON.parse(
|
|
12081
|
+
const value = JSON.parse(fs8.readFileSync(file, "utf8"));
|
|
11953
12082
|
return isObject5(value) && isObject5(value.store) && value.store.format === "workit-store";
|
|
11954
12083
|
} catch {
|
|
11955
12084
|
return false;
|
|
@@ -11957,32 +12086,32 @@ class TaskStore {
|
|
|
11957
12086
|
}
|
|
11958
12087
|
writeTombstoneIfMissing() {
|
|
11959
12088
|
try {
|
|
11960
|
-
const v2 =
|
|
11961
|
-
if (
|
|
12089
|
+
const v2 = path7.join(this.root, ".workit");
|
|
12090
|
+
if (fs8.existsSync(path7.join(v2, "workspace.json")))
|
|
11962
12091
|
return;
|
|
11963
12092
|
let id = null;
|
|
11964
12093
|
try {
|
|
11965
|
-
id = JSON.parse(
|
|
12094
|
+
id = JSON.parse(fs8.readFileSync(this.paths().workspace, "utf8")).id ?? null;
|
|
11966
12095
|
} catch {}
|
|
11967
12096
|
this.writeTombstone(v2, id);
|
|
11968
12097
|
} catch {}
|
|
11969
12098
|
}
|
|
11970
12099
|
initializeStorage() {
|
|
11971
12100
|
const dirs = this.paths();
|
|
11972
|
-
retryTransient(() =>
|
|
11973
|
-
retryTransient(() =>
|
|
11974
|
-
if (!
|
|
12101
|
+
retryTransient(() => fs8.mkdirSync(dirs.checkout, { recursive: true }));
|
|
12102
|
+
retryTransient(() => fs8.mkdirSync(dirs.tasks, { recursive: true }));
|
|
12103
|
+
if (!fs8.existsSync(dirs.marker))
|
|
11975
12104
|
replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
|
|
11976
12105
|
`, false);
|
|
11977
12106
|
if (!dirs.shared) {
|
|
11978
|
-
const ignore =
|
|
12107
|
+
const ignore = path7.join(dirs.dir, ".gitignore");
|
|
11979
12108
|
let current = null;
|
|
11980
12109
|
try {
|
|
11981
|
-
current =
|
|
12110
|
+
current = fs8.readFileSync(ignore, "utf8");
|
|
11982
12111
|
} catch {}
|
|
11983
12112
|
if (current !== `*
|
|
11984
12113
|
`)
|
|
11985
|
-
retryTransient(() =>
|
|
12114
|
+
retryTransient(() => fs8.writeFileSync(ignore, `*
|
|
11986
12115
|
`));
|
|
11987
12116
|
}
|
|
11988
12117
|
this.writeTombstoneIfMissing();
|
|
@@ -11998,7 +12127,7 @@ class TaskStore {
|
|
|
11998
12127
|
shouldReclaim: ({ payload, nowMs }) => {
|
|
11999
12128
|
let ageMs = null;
|
|
12000
12129
|
try {
|
|
12001
|
-
ageMs = nowMs -
|
|
12130
|
+
ageMs = nowMs - fs8.lstatSync(lockPath).mtimeMs;
|
|
12002
12131
|
} catch {}
|
|
12003
12132
|
return classifyLockOwner(payload, ageMs, localLockHost(), TASK_STORE_LOCK).state === "stale";
|
|
12004
12133
|
},
|
|
@@ -12035,7 +12164,7 @@ class TaskStore {
|
|
|
12035
12164
|
withLock(lockPath, operation) {
|
|
12036
12165
|
try {
|
|
12037
12166
|
this.initializeStorage();
|
|
12038
|
-
|
|
12167
|
+
fs8.mkdirSync(path7.dirname(lockPath), { recursive: true });
|
|
12039
12168
|
} catch (error) {
|
|
12040
12169
|
return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
|
|
12041
12170
|
path: this.paths().dir
|
|
@@ -12075,18 +12204,18 @@ class TaskStore {
|
|
|
12075
12204
|
lockDirWritable(lockPath) {
|
|
12076
12205
|
const probe = `${lockPath}.${process.pid}.${randomUUID3()}.probe`;
|
|
12077
12206
|
try {
|
|
12078
|
-
|
|
12207
|
+
fs8.closeSync(fs8.openSync(probe, "wx", 384));
|
|
12079
12208
|
} catch {
|
|
12080
12209
|
return false;
|
|
12081
12210
|
}
|
|
12082
12211
|
try {
|
|
12083
|
-
|
|
12212
|
+
fs8.rmSync(probe, { force: true });
|
|
12084
12213
|
} catch {}
|
|
12085
12214
|
return true;
|
|
12086
12215
|
}
|
|
12087
12216
|
lockHolderPresent(lockPath) {
|
|
12088
12217
|
try {
|
|
12089
|
-
|
|
12218
|
+
fs8.lstatSync(lockPath);
|
|
12090
12219
|
return true;
|
|
12091
12220
|
} catch (error) {
|
|
12092
12221
|
return error?.code !== "ENOENT";
|
|
@@ -12098,12 +12227,12 @@ class TaskStore {
|
|
|
12098
12227
|
if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
|
|
12099
12228
|
return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
|
|
12100
12229
|
path: lockPath,
|
|
12101
|
-
guidance: `Check permissions and the read-only attribute on ${
|
|
12230
|
+
guidance: `Check permissions and the read-only attribute on ${path7.dirname(lockPath)}.`
|
|
12102
12231
|
});
|
|
12103
12232
|
if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
|
|
12104
12233
|
let lock = null;
|
|
12105
12234
|
try {
|
|
12106
|
-
lock = parseMetadataLockOrNull(
|
|
12235
|
+
lock = parseMetadataLockOrNull(fs8.readFileSync(lockPath, "utf8"));
|
|
12107
12236
|
} catch {}
|
|
12108
12237
|
return failure2("busy", `workit lock is held by another Workit call${lock ? ` (pid ${lock.pid} on ${lock.host})` : ""}; retry shortly`, {
|
|
12109
12238
|
outcome: "not_started",
|
|
@@ -12121,15 +12250,15 @@ var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
|
|
|
12121
12250
|
var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
|
|
12122
12251
|
|
|
12123
12252
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
12124
|
-
import { createHash as
|
|
12125
|
-
import * as
|
|
12253
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
12254
|
+
import * as fs13 from "node:fs";
|
|
12126
12255
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
12127
|
-
import
|
|
12256
|
+
import path15 from "node:path";
|
|
12128
12257
|
|
|
12129
12258
|
// packages/workit-core/src/check-config.ts
|
|
12130
12259
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
12131
|
-
import
|
|
12132
|
-
import
|
|
12260
|
+
import fs9 from "node:fs";
|
|
12261
|
+
import path8 from "node:path";
|
|
12133
12262
|
var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
|
|
12134
12263
|
var CHECKS_FILE = "workit.checks.json";
|
|
12135
12264
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -12182,10 +12311,10 @@ var git = (cwd, args) => {
|
|
|
12182
12311
|
return value || null;
|
|
12183
12312
|
};
|
|
12184
12313
|
function checkRoot(cwd) {
|
|
12185
|
-
return
|
|
12314
|
+
return path8.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
|
|
12186
12315
|
}
|
|
12187
12316
|
var packageManager = (root) => {
|
|
12188
|
-
const has = (file) =>
|
|
12317
|
+
const has = (file) => fs9.existsSync(path8.join(root, file));
|
|
12189
12318
|
if (has("bun.lock") || has("bun.lockb"))
|
|
12190
12319
|
return "bun";
|
|
12191
12320
|
if (has("pnpm-lock.yaml"))
|
|
@@ -12205,7 +12334,7 @@ var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
|
|
|
12205
12334
|
function detectedChecks(root) {
|
|
12206
12335
|
const read = (file) => {
|
|
12207
12336
|
try {
|
|
12208
|
-
return
|
|
12337
|
+
return fs9.readFileSync(path8.join(root, file), "utf8");
|
|
12209
12338
|
} catch {
|
|
12210
12339
|
return null;
|
|
12211
12340
|
}
|
|
@@ -12244,7 +12373,7 @@ function detectedChecks(root) {
|
|
|
12244
12373
|
var GATES = ["testing", "verification"];
|
|
12245
12374
|
function loadCheckConfig(cwd) {
|
|
12246
12375
|
const root = checkRoot(cwd);
|
|
12247
|
-
const file =
|
|
12376
|
+
const file = path8.join(root, CHECKS_FILE);
|
|
12248
12377
|
const none = (error = null) => ({
|
|
12249
12378
|
source: error ? CHECKS_FILE : "none",
|
|
12250
12379
|
root,
|
|
@@ -12254,7 +12383,7 @@ function loadCheckConfig(cwd) {
|
|
|
12254
12383
|
});
|
|
12255
12384
|
let text = null;
|
|
12256
12385
|
try {
|
|
12257
|
-
text =
|
|
12386
|
+
text = fs9.readFileSync(file, "utf8");
|
|
12258
12387
|
} catch (error) {
|
|
12259
12388
|
if (error.code !== "ENOENT")
|
|
12260
12389
|
return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
|
|
@@ -12317,10 +12446,10 @@ function workitAvailable(env = process.env) {
|
|
|
12317
12446
|
return workitOnPath;
|
|
12318
12447
|
const windows = process.platform === "win32";
|
|
12319
12448
|
const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
|
|
12320
|
-
const dirs = (env.PATH ?? env.Path ?? "").split(
|
|
12449
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(path8.delimiter).filter(Boolean);
|
|
12321
12450
|
workitOnPath = dirs.some((dir) => exts.some((ext) => {
|
|
12322
12451
|
try {
|
|
12323
|
-
return
|
|
12452
|
+
return fs9.statSync(path8.join(dir, `workit${ext}`)).isFile();
|
|
12324
12453
|
} catch {
|
|
12325
12454
|
return false;
|
|
12326
12455
|
}
|
|
@@ -12333,10 +12462,10 @@ function checkCommand(args, version, onPath = workitAvailable()) {
|
|
|
12333
12462
|
|
|
12334
12463
|
// packages/workit-core/src/git/rev.ts
|
|
12335
12464
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
12336
|
-
import { createHash as
|
|
12337
|
-
import
|
|
12465
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
12466
|
+
import fs10 from "node:fs";
|
|
12338
12467
|
import os from "node:os";
|
|
12339
|
-
import
|
|
12468
|
+
import path9 from "node:path";
|
|
12340
12469
|
var MAX_BUFFER = 256 * 1024 * 1024;
|
|
12341
12470
|
var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
|
|
12342
12471
|
var git2 = (cwd, args, options = {}) => {
|
|
@@ -12371,10 +12500,10 @@ function headSha(cwd) {
|
|
|
12371
12500
|
var emptyTree = (cwd) => line(git2(cwd, ["mktree"], { input: "" }));
|
|
12372
12501
|
var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
|
|
12373
12502
|
var seedIndex = (from, to) => {
|
|
12374
|
-
|
|
12375
|
-
const { atimeNs, mtimeNs } =
|
|
12503
|
+
fs10.copyFileSync(from, to);
|
|
12504
|
+
const { atimeNs, mtimeNs } = fs10.statSync(from, { bigint: true });
|
|
12376
12505
|
const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
|
|
12377
|
-
|
|
12506
|
+
fs10.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
|
|
12378
12507
|
};
|
|
12379
12508
|
function worktreeTree(cwd, options = {}) {
|
|
12380
12509
|
const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
|
|
@@ -12383,10 +12512,10 @@ function worktreeTree(cwd, options = {}) {
|
|
|
12383
12512
|
return null;
|
|
12384
12513
|
const cap = options.maxUntrackedBytes ?? MAX_UNTRACKED_BYTES;
|
|
12385
12514
|
const timeoutMs = options.timeoutMs ?? GIT_TIMEOUTS.worktree;
|
|
12386
|
-
const scratch =
|
|
12515
|
+
const scratch = fs10.mkdtempSync(path9.join(os.tmpdir(), "workit-tree-"));
|
|
12387
12516
|
try {
|
|
12388
|
-
const tempIndex =
|
|
12389
|
-
if (
|
|
12517
|
+
const tempIndex = path9.join(scratch, "index");
|
|
12518
|
+
if (fs10.existsSync(indexPath))
|
|
12390
12519
|
seedIndex(indexPath, tempIndex);
|
|
12391
12520
|
const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
|
|
12392
12521
|
const untracked = git2(top, ["ls-files", "--others", "--exclude-standard", "-z"], {
|
|
@@ -12398,7 +12527,7 @@ function worktreeTree(cwd, options = {}) {
|
|
|
12398
12527
|
const skipped = [];
|
|
12399
12528
|
for (const file of untracked.stdout.split("\x00").filter(Boolean)) {
|
|
12400
12529
|
try {
|
|
12401
|
-
const stat =
|
|
12530
|
+
const stat = fs10.statSync(path9.join(top, file));
|
|
12402
12531
|
if (stat.isFile() && stat.size > cap)
|
|
12403
12532
|
skipped.push({ path: file, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) });
|
|
12404
12533
|
} catch {}
|
|
@@ -12415,10 +12544,10 @@ function worktreeTree(cwd, options = {}) {
|
|
|
12415
12544
|
if (!tree)
|
|
12416
12545
|
return null;
|
|
12417
12546
|
const base = line(git2(top, ["rev-parse", "--verify", "-q", "HEAD^{tree}"])) ?? emptyTree(top);
|
|
12418
|
-
const key = skipped.length ? `sha256:${
|
|
12547
|
+
const key = skipped.length ? `sha256:${createHash6("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
|
|
12419
12548
|
return { tree, key, dirty: tree !== base || skipped.length > 0, skipped };
|
|
12420
12549
|
} finally {
|
|
12421
|
-
|
|
12550
|
+
fs10.rmSync(scratch, { recursive: true, force: true });
|
|
12422
12551
|
}
|
|
12423
12552
|
}
|
|
12424
12553
|
function worktreeSignal(cwd, options = {}) {
|
|
@@ -12438,16 +12567,16 @@ function worktreeSignal(cwd, options = {}) {
|
|
|
12438
12567
|
});
|
|
12439
12568
|
if (!status.ok)
|
|
12440
12569
|
return null;
|
|
12441
|
-
const hash =
|
|
12570
|
+
const hash = createHash6("sha256");
|
|
12442
12571
|
hash.update(`${headSha(top) ?? "unborn"}\x00`);
|
|
12443
12572
|
for (const entry of status.stdout.split("\x00")) {
|
|
12444
12573
|
if (entry.length < 4)
|
|
12445
12574
|
continue;
|
|
12446
12575
|
const file = entry.slice(3);
|
|
12447
|
-
const absolute =
|
|
12576
|
+
const absolute = path9.join(top, file);
|
|
12448
12577
|
let stat = "-";
|
|
12449
12578
|
try {
|
|
12450
|
-
const info =
|
|
12579
|
+
const info = fs10.lstatSync(absolute, { bigint: true });
|
|
12451
12580
|
stat = `${info.mode.toString(8)}:${info.size}:${info.mtimeNs}`;
|
|
12452
12581
|
if (info.isDirectory())
|
|
12453
12582
|
stat += `:${headSha(absolute) ?? "-"}`;
|
|
@@ -12514,17 +12643,17 @@ function pushRemoteName(cwd, branch) {
|
|
|
12514
12643
|
|
|
12515
12644
|
// packages/workit-core/src/core/policy/derive.ts
|
|
12516
12645
|
import { existsSync as existsSync4 } from "node:fs";
|
|
12517
|
-
import
|
|
12646
|
+
import path14 from "node:path";
|
|
12518
12647
|
|
|
12519
12648
|
// packages/workit-core/src/ledger.ts
|
|
12520
12649
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
12521
|
-
import { createHash as
|
|
12522
|
-
import
|
|
12523
|
-
import
|
|
12650
|
+
import { createHash as createHash9, randomBytes as randomBytes2 } from "node:crypto";
|
|
12651
|
+
import fs12 from "node:fs";
|
|
12652
|
+
import path13 from "node:path";
|
|
12524
12653
|
|
|
12525
12654
|
// packages/workit-core/src/core/vcs-config.ts
|
|
12526
|
-
import
|
|
12527
|
-
import
|
|
12655
|
+
import fs11 from "node:fs";
|
|
12656
|
+
import path12 from "node:path";
|
|
12528
12657
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
12529
12658
|
|
|
12530
12659
|
// packages/workit-core/src/core/config.ts
|
|
@@ -12538,7 +12667,7 @@ import {
|
|
|
12538
12667
|
writeFileSync as writeFileSync2
|
|
12539
12668
|
} from "node:fs";
|
|
12540
12669
|
import os2 from "node:os";
|
|
12541
|
-
import
|
|
12670
|
+
import path10 from "node:path";
|
|
12542
12671
|
|
|
12543
12672
|
// packages/workit-core/src/core/boundary.ts
|
|
12544
12673
|
var EVENT = {
|
|
@@ -12582,7 +12711,7 @@ var mergePreset = (preset, input = {}, current = {
|
|
|
12582
12711
|
protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
|
|
12583
12712
|
};
|
|
12584
12713
|
};
|
|
12585
|
-
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");
|
|
12586
12715
|
var migratedDir = null;
|
|
12587
12716
|
var migrationFailed = false;
|
|
12588
12717
|
var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
@@ -12592,7 +12721,7 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
12592
12721
|
migratedDir = dir;
|
|
12593
12722
|
return dir;
|
|
12594
12723
|
}
|
|
12595
|
-
const legacy =
|
|
12724
|
+
const legacy = path10.join(process.env.XDG_CONFIG_HOME || path10.join(os2.homedir(), ".config"), "workflow-toolkit");
|
|
12596
12725
|
if (!existsSync3(legacy)) {
|
|
12597
12726
|
migratedDir = dir;
|
|
12598
12727
|
return dir;
|
|
@@ -12605,8 +12734,8 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
12605
12734
|
mkdirSync2(dir, { recursive: true });
|
|
12606
12735
|
diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
|
|
12607
12736
|
for (const entry of readdirSync2(legacy, { withFileTypes: true })) {
|
|
12608
|
-
const src =
|
|
12609
|
-
const dest =
|
|
12737
|
+
const src = path10.join(legacy, entry.name);
|
|
12738
|
+
const dest = path10.join(dir, entry.name);
|
|
12610
12739
|
if (existsSync3(dest))
|
|
12611
12740
|
continue;
|
|
12612
12741
|
try {
|
|
@@ -12678,7 +12807,7 @@ var parseConfigResult = (raw, file) => {
|
|
|
12678
12807
|
return { status: "valid", path: file, config };
|
|
12679
12808
|
};
|
|
12680
12809
|
var readConfigTyped = (dir) => {
|
|
12681
|
-
const file =
|
|
12810
|
+
const file = path10.join(dir ?? configDir(), "config.json");
|
|
12682
12811
|
return parseConfigResult(readSafe(file), file);
|
|
12683
12812
|
};
|
|
12684
12813
|
var readConfig = () => {
|
|
@@ -12687,7 +12816,7 @@ var readConfig = () => {
|
|
|
12687
12816
|
throw new Error(result.error);
|
|
12688
12817
|
return result.config ?? DEFAULTS;
|
|
12689
12818
|
};
|
|
12690
|
-
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ??
|
|
12819
|
+
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path10.join(configDir(), "vcs.json");
|
|
12691
12820
|
var branchGlobPattern = (glob) => new RegExp(`^${glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i");
|
|
12692
12821
|
var resolveBranchPolicy = (config, workspace, repoPreset) => {
|
|
12693
12822
|
const wp = workspace?.branchPolicy ?? {};
|
|
@@ -12729,9 +12858,9 @@ var resolveCommitPolicy = (config, workspace) => {
|
|
|
12729
12858
|
|
|
12730
12859
|
// packages/workit-core/src/core/workspaces.ts
|
|
12731
12860
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
12732
|
-
import { createHash as
|
|
12861
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
12733
12862
|
import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
12734
|
-
import
|
|
12863
|
+
import path11 from "node:path";
|
|
12735
12864
|
|
|
12736
12865
|
// packages/workit-core/src/core/branch-policy.ts
|
|
12737
12866
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
@@ -12856,7 +12985,7 @@ var releaseTrackSchema = object({
|
|
|
12856
12985
|
context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
|
|
12857
12986
|
}
|
|
12858
12987
|
}
|
|
12859
|
-
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(".."))) {
|
|
12860
12989
|
context.addIssue({
|
|
12861
12990
|
code: "custom",
|
|
12862
12991
|
path: ["versionSource", "path"],
|
|
@@ -12916,7 +13045,7 @@ var workspacesFileSchema = object({ workspaces: array(workspaceConfigSchema).opt
|
|
|
12916
13045
|
names.add(workspace.name);
|
|
12917
13046
|
}
|
|
12918
13047
|
});
|
|
12919
|
-
var workspacesRevision = (contents) =>
|
|
13048
|
+
var workspacesRevision = (contents) => createHash7("sha256").update(contents ?? "<missing>").digest("hex");
|
|
12920
13049
|
var validateWorkspacesDocument = (parsed, file) => {
|
|
12921
13050
|
if (!isConfigObject(parsed)) {
|
|
12922
13051
|
return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
|
|
@@ -12954,7 +13083,7 @@ var validateWorkspacesDocument = (parsed, file) => {
|
|
|
12954
13083
|
};
|
|
12955
13084
|
};
|
|
12956
13085
|
var readWorkspacesResult = (dir = configDir()) => {
|
|
12957
|
-
const file =
|
|
13086
|
+
const file = path11.join(dir, "workspaces.json");
|
|
12958
13087
|
let raw;
|
|
12959
13088
|
try {
|
|
12960
13089
|
raw = readFileSync3(file, "utf8");
|
|
@@ -13065,9 +13194,9 @@ var mainCheckoutOf = (cwd) => {
|
|
|
13065
13194
|
let main = null;
|
|
13066
13195
|
const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
|
|
13067
13196
|
const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
|
|
13068
|
-
if (gitDir && common &&
|
|
13197
|
+
if (gitDir && common && path11.resolve(gitDir) !== path11.resolve(common)) {
|
|
13069
13198
|
const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
|
|
13070
|
-
main = listed?.trim() || (
|
|
13199
|
+
main = listed?.trim() || (path11.basename(common) === ".git" ? path11.dirname(common) : common);
|
|
13071
13200
|
}
|
|
13072
13201
|
mainCheckouts.set(cwd, main);
|
|
13073
13202
|
return main;
|
|
@@ -13158,7 +13287,7 @@ var runtimeWorkspaceIndexSchema = object({
|
|
|
13158
13287
|
workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
13159
13288
|
}).passthrough();
|
|
13160
13289
|
var matchRuntimeWorkspace = (cwd) => {
|
|
13161
|
-
const file =
|
|
13290
|
+
const file = path11.join(configDir(), "workspaces.json");
|
|
13162
13291
|
let raw;
|
|
13163
13292
|
try {
|
|
13164
13293
|
raw = readFileSync3(file, "utf8");
|
|
@@ -13233,7 +13362,7 @@ function workspaceDefaultTarget(cwd, globalDefault) {
|
|
|
13233
13362
|
}
|
|
13234
13363
|
|
|
13235
13364
|
// packages/workit-core/src/core/release-tracks.ts
|
|
13236
|
-
import { createHash as
|
|
13365
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
13237
13366
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
13238
13367
|
class ReleaseTrackError extends Error {
|
|
13239
13368
|
}
|
|
@@ -13616,7 +13745,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
13616
13745
|
]) ?? "",
|
|
13617
13746
|
git3(cwd, ["config", "--get", recordedBaseKey(branch)]) ?? ""
|
|
13618
13747
|
] : [];
|
|
13619
|
-
key =
|
|
13748
|
+
key = createHash8("sha256").update(JSON.stringify({ cwd, input, refs, head, own })).digest("hex");
|
|
13620
13749
|
const hit = memo2.get(key);
|
|
13621
13750
|
if (hit)
|
|
13622
13751
|
return hit;
|
|
@@ -13631,7 +13760,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
13631
13760
|
}
|
|
13632
13761
|
|
|
13633
13762
|
// packages/workit-core/src/core/vcs-config.ts
|
|
13634
|
-
var workspacesPath = () =>
|
|
13763
|
+
var workspacesPath = () => path12.join(configDir(), "workspaces.json");
|
|
13635
13764
|
var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
13636
13765
|
var remoteProvider = (cwd) => {
|
|
13637
13766
|
const r = spawnSync8("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
|
|
@@ -13721,7 +13850,7 @@ var readVcsConfig = () => {
|
|
|
13721
13850
|
const file = vcsConfigPath();
|
|
13722
13851
|
let raw;
|
|
13723
13852
|
try {
|
|
13724
|
-
raw =
|
|
13853
|
+
raw = fs11.readFileSync(file, "utf8");
|
|
13725
13854
|
} catch {
|
|
13726
13855
|
return { status: "missing", path: file, config: {} };
|
|
13727
13856
|
}
|
|
@@ -13806,7 +13935,7 @@ function vcsConfig(mode, cwd, options = {}) {
|
|
|
13806
13935
|
const prov = cfg[provider] ?? {};
|
|
13807
13936
|
const out = {
|
|
13808
13937
|
ok: true,
|
|
13809
|
-
configPath:
|
|
13938
|
+
configPath: path12.resolve(cfgPath),
|
|
13810
13939
|
provider,
|
|
13811
13940
|
defaultTargetBranch: workspaceDefault,
|
|
13812
13941
|
pr: cfg.pr ?? {},
|
|
@@ -14017,7 +14146,7 @@ function diffHash(cwd, base, head) {
|
|
|
14017
14146
|
], GIT_TIMEOUTS.worktree);
|
|
14018
14147
|
if (!diff.ok || !diff.stdout)
|
|
14019
14148
|
return null;
|
|
14020
|
-
const hash =
|
|
14149
|
+
const hash = createHash9("sha256");
|
|
14021
14150
|
for (const line of diff.stdout.split(`
|
|
14022
14151
|
`)) {
|
|
14023
14152
|
if (line.startsWith("index "))
|
|
@@ -14029,14 +14158,14 @@ function diffHash(cwd, base, head) {
|
|
|
14029
14158
|
return `sha256:${hash.digest("hex")}`;
|
|
14030
14159
|
}
|
|
14031
14160
|
function storeRoot(cwd) {
|
|
14032
|
-
const location = resolveStore(
|
|
14161
|
+
const location = resolveStore(path13.resolve(cwd));
|
|
14033
14162
|
if (location instanceof Error)
|
|
14034
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");
|
|
14035
14164
|
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
14036
14165
|
}
|
|
14037
14166
|
function ledgerPath(cwd) {
|
|
14038
14167
|
const root = storeRoot(cwd);
|
|
14039
|
-
return root.ok ? { ok: true, value:
|
|
14168
|
+
return root.ok ? { ok: true, value: path13.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
14040
14169
|
}
|
|
14041
14170
|
var newId2 = () => `${Date.now().toString(36)}-${randomBytes2(6).toString("hex")}`;
|
|
14042
14171
|
var NETWORK_FS_TYPES = new Set([
|
|
@@ -14056,7 +14185,7 @@ var onNetworkFs = (dir) => {
|
|
|
14056
14185
|
if (process.platform !== "linux")
|
|
14057
14186
|
return false;
|
|
14058
14187
|
try {
|
|
14059
|
-
return isNetworkFsType(
|
|
14188
|
+
return isNetworkFsType(fs12.statfsSync(dir).type);
|
|
14060
14189
|
} catch {
|
|
14061
14190
|
return false;
|
|
14062
14191
|
}
|
|
@@ -14066,15 +14195,15 @@ var LOCK_STALE_MS = 30000;
|
|
|
14066
14195
|
var LOCK_OWNER = "owner";
|
|
14067
14196
|
var lockToken = (lock) => {
|
|
14068
14197
|
try {
|
|
14069
|
-
return
|
|
14198
|
+
return fs12.readFileSync(path13.join(lock, LOCK_OWNER), "utf8");
|
|
14070
14199
|
} catch {
|
|
14071
14200
|
return null;
|
|
14072
14201
|
}
|
|
14073
14202
|
};
|
|
14074
14203
|
var lockAgeMs = (lock) => {
|
|
14075
|
-
for (const target of [
|
|
14204
|
+
for (const target of [path13.join(lock, LOCK_OWNER), lock]) {
|
|
14076
14205
|
try {
|
|
14077
|
-
return Date.now() -
|
|
14206
|
+
return Date.now() - fs12.statSync(target).mtimeMs;
|
|
14078
14207
|
} catch {}
|
|
14079
14208
|
}
|
|
14080
14209
|
return null;
|
|
@@ -14082,14 +14211,14 @@ var lockAgeMs = (lock) => {
|
|
|
14082
14211
|
var ledgerLock = {
|
|
14083
14212
|
acquire(lock) {
|
|
14084
14213
|
try {
|
|
14085
|
-
|
|
14214
|
+
fs12.mkdirSync(lock);
|
|
14086
14215
|
} catch (error) {
|
|
14087
14216
|
if (error.code === "EEXIST")
|
|
14088
14217
|
return null;
|
|
14089
14218
|
throw error;
|
|
14090
14219
|
}
|
|
14091
14220
|
const token = `${process.pid}-${randomBytes2(8).toString("hex")}`;
|
|
14092
|
-
|
|
14221
|
+
fs12.writeFileSync(path13.join(lock, LOCK_OWNER), token);
|
|
14093
14222
|
return token;
|
|
14094
14223
|
},
|
|
14095
14224
|
release(lock, token) {
|
|
@@ -14097,35 +14226,35 @@ var ledgerLock = {
|
|
|
14097
14226
|
return false;
|
|
14098
14227
|
const tomb = `${lock}.release-${randomBytes2(6).toString("hex")}`;
|
|
14099
14228
|
try {
|
|
14100
|
-
|
|
14229
|
+
fs12.renameSync(lock, tomb);
|
|
14101
14230
|
} catch {
|
|
14102
14231
|
return false;
|
|
14103
14232
|
}
|
|
14104
14233
|
if (lockToken(tomb) !== token) {
|
|
14105
14234
|
try {
|
|
14106
|
-
|
|
14235
|
+
fs12.renameSync(tomb, lock);
|
|
14107
14236
|
} catch {}
|
|
14108
14237
|
return false;
|
|
14109
14238
|
}
|
|
14110
|
-
|
|
14239
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
14111
14240
|
return true;
|
|
14112
14241
|
},
|
|
14113
14242
|
token: lockToken,
|
|
14114
14243
|
reap(lock, observed) {
|
|
14115
14244
|
const tomb = `${lock}.reap-${randomBytes2(6).toString("hex")}`;
|
|
14116
14245
|
try {
|
|
14117
|
-
|
|
14246
|
+
fs12.renameSync(lock, tomb);
|
|
14118
14247
|
} catch {
|
|
14119
14248
|
return false;
|
|
14120
14249
|
}
|
|
14121
14250
|
if (lockToken(tomb) === observed) {
|
|
14122
|
-
|
|
14251
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
14123
14252
|
return true;
|
|
14124
14253
|
}
|
|
14125
14254
|
try {
|
|
14126
|
-
|
|
14255
|
+
fs12.renameSync(tomb, lock);
|
|
14127
14256
|
} catch {
|
|
14128
|
-
|
|
14257
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
14129
14258
|
}
|
|
14130
14259
|
return false;
|
|
14131
14260
|
}
|
|
@@ -14152,14 +14281,14 @@ function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
|
14152
14281
|
ledgerLock.release(lock, token);
|
|
14153
14282
|
}
|
|
14154
14283
|
}
|
|
14155
|
-
var sha2562 = (text) =>
|
|
14284
|
+
var sha2562 = (text) => createHash9("sha256").update(text).digest("hex");
|
|
14156
14285
|
var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
|
|
14157
14286
|
${body}`);
|
|
14158
14287
|
var TAIL_BYTES = 64 * 1024;
|
|
14159
14288
|
var lastLine = (fd, size) => {
|
|
14160
14289
|
for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
|
|
14161
14290
|
const buffer = Buffer.alloc(span);
|
|
14162
|
-
|
|
14291
|
+
fs12.readSync(fd, buffer, 0, span, size - span);
|
|
14163
14292
|
const end = buffer.lastIndexOf(10);
|
|
14164
14293
|
if (end < 0) {
|
|
14165
14294
|
if (span === size)
|
|
@@ -14190,13 +14319,13 @@ var lastLine = (fd, size) => {
|
|
|
14190
14319
|
};
|
|
14191
14320
|
function appendLine(file, build, options) {
|
|
14192
14321
|
const write = () => {
|
|
14193
|
-
const fd =
|
|
14322
|
+
const fd = fs12.openSync(file, "a+");
|
|
14194
14323
|
try {
|
|
14195
|
-
const size =
|
|
14324
|
+
const size = fs12.fstatSync(fd).size;
|
|
14196
14325
|
let fence = "";
|
|
14197
14326
|
if (size > 0) {
|
|
14198
14327
|
const last = Buffer.alloc(1);
|
|
14199
|
-
|
|
14328
|
+
fs12.readSync(fd, last, 0, 1, size - 1);
|
|
14200
14329
|
if (last[0] !== 10)
|
|
14201
14330
|
fence = `
|
|
14202
14331
|
`;
|
|
@@ -14205,17 +14334,17 @@ function appendLine(file, build, options) {
|
|
|
14205
14334
|
if (!line.ok)
|
|
14206
14335
|
return line;
|
|
14207
14336
|
const bytes = Buffer.from(fence + line.value);
|
|
14208
|
-
const written =
|
|
14337
|
+
const written = fs12.writeSync(fd, bytes);
|
|
14209
14338
|
if (written !== bytes.length)
|
|
14210
14339
|
return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
|
|
14211
14340
|
return { ok: true, value: undefined };
|
|
14212
14341
|
} finally {
|
|
14213
|
-
|
|
14342
|
+
fs12.closeSync(fd);
|
|
14214
14343
|
}
|
|
14215
14344
|
};
|
|
14216
14345
|
try {
|
|
14217
|
-
|
|
14218
|
-
if (!options.forceLock && !onNetworkFs(
|
|
14346
|
+
fs12.mkdirSync(path13.dirname(file), { recursive: true });
|
|
14347
|
+
if (!options.forceLock && !onNetworkFs(path13.dirname(file)))
|
|
14219
14348
|
return write();
|
|
14220
14349
|
const locked = withLedgerLock(file, write);
|
|
14221
14350
|
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
@@ -14287,7 +14416,7 @@ function readLedger(cwd, options = {}) {
|
|
|
14287
14416
|
return file;
|
|
14288
14417
|
let text = "";
|
|
14289
14418
|
try {
|
|
14290
|
-
text =
|
|
14419
|
+
text = fs12.readFileSync(file.value, "utf8");
|
|
14291
14420
|
} catch (error) {
|
|
14292
14421
|
if (error.code === "ENOENT")
|
|
14293
14422
|
return {
|
|
@@ -14481,11 +14610,20 @@ function authorSessions(cwd, rows, branch, range = { base: null, head: null }, s
|
|
|
14481
14610
|
sessions.add(session);
|
|
14482
14611
|
return sessions;
|
|
14483
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
|
+
};
|
|
14484
14622
|
var independenceReasons = (row, authors) => {
|
|
14485
14623
|
const reasons = [];
|
|
14486
14624
|
if (!row.actor.session)
|
|
14487
14625
|
reasons.push("no_session");
|
|
14488
|
-
else if (authors
|
|
14626
|
+
else if (authoredBy(authors, row.actor.session))
|
|
14489
14627
|
reasons.push("author_session");
|
|
14490
14628
|
if (row.self === true && !reasons.includes("no_session"))
|
|
14491
14629
|
reasons.push("self");
|
|
@@ -14769,7 +14907,7 @@ var productDecision = (task, ledger) => {
|
|
|
14769
14907
|
};
|
|
14770
14908
|
};
|
|
14771
14909
|
var planRecorded = (task, root) => {
|
|
14772
|
-
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)));
|
|
14773
14911
|
if (cited)
|
|
14774
14912
|
return {
|
|
14775
14913
|
met: true,
|
|
@@ -14828,15 +14966,15 @@ function writeBlockers(task, root) {
|
|
|
14828
14966
|
}
|
|
14829
14967
|
|
|
14830
14968
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
14831
|
-
var digestBytes = (value) =>
|
|
14832
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
14833
|
-
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("/") || ".";
|
|
14834
14972
|
var canonicalPath = (value) => {
|
|
14835
14973
|
if (typeof value !== "string" || !value)
|
|
14836
14974
|
return null;
|
|
14837
14975
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
14838
14976
|
return null;
|
|
14839
|
-
const normalized =
|
|
14977
|
+
const normalized = path15.posix.normalize(value);
|
|
14840
14978
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
14841
14979
|
return null;
|
|
14842
14980
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -14884,21 +15022,21 @@ var gitPaths = (root) => {
|
|
|
14884
15022
|
git: !/not a git repository/i.test(stderr)
|
|
14885
15023
|
};
|
|
14886
15024
|
}
|
|
14887
|
-
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("/")) : [];
|
|
14888
15026
|
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
14889
15027
|
if (stagedDeleted.status !== 0) {
|
|
14890
15028
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
14891
15029
|
if (!/not a git repository/i.test(stderr))
|
|
14892
15030
|
return { paths, uncertain: true, git: true };
|
|
14893
15031
|
} else if (stagedDeleted.stdout) {
|
|
14894
|
-
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("/")));
|
|
14895
15033
|
}
|
|
14896
15034
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
14897
15035
|
};
|
|
14898
15036
|
var walk = (root, directory, output) => {
|
|
14899
15037
|
let names;
|
|
14900
15038
|
try {
|
|
14901
|
-
names =
|
|
15039
|
+
names = fs13.readdirSync(directory);
|
|
14902
15040
|
} catch {
|
|
14903
15041
|
return true;
|
|
14904
15042
|
}
|
|
@@ -14906,10 +15044,10 @@ var walk = (root, directory, output) => {
|
|
|
14906
15044
|
for (const name of names) {
|
|
14907
15045
|
if (name === ".git" || name === ".workit")
|
|
14908
15046
|
continue;
|
|
14909
|
-
const target =
|
|
15047
|
+
const target = path15.join(directory, name);
|
|
14910
15048
|
let stat;
|
|
14911
15049
|
try {
|
|
14912
|
-
stat =
|
|
15050
|
+
stat = fs13.lstatSync(target);
|
|
14913
15051
|
} catch {
|
|
14914
15052
|
uncertain = true;
|
|
14915
15053
|
continue;
|
|
@@ -14924,14 +15062,14 @@ var walk = (root, directory, output) => {
|
|
|
14924
15062
|
var scopeRoots = (root, scope) => {
|
|
14925
15063
|
const roots = [];
|
|
14926
15064
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
14927
|
-
const target =
|
|
15065
|
+
const target = path15.resolve(root, item);
|
|
14928
15066
|
if (!inside(root, target))
|
|
14929
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` }] });
|
|
14930
15068
|
let ancestor = target;
|
|
14931
|
-
while (!
|
|
14932
|
-
ancestor =
|
|
15069
|
+
while (!fs13.existsSync(ancestor) && ancestor !== root)
|
|
15070
|
+
ancestor = path15.dirname(ancestor);
|
|
14933
15071
|
try {
|
|
14934
|
-
if (!inside(root,
|
|
15072
|
+
if (!inside(root, fs13.realpathSync(ancestor)))
|
|
14935
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` }] });
|
|
14936
15074
|
} catch {
|
|
14937
15075
|
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
@@ -14943,7 +15081,7 @@ var scopeRoots = (root, scope) => {
|
|
|
14943
15081
|
function captureCandidate(root, scope, environment = []) {
|
|
14944
15082
|
let checkout;
|
|
14945
15083
|
try {
|
|
14946
|
-
checkout =
|
|
15084
|
+
checkout = fs13.realpathSync(root);
|
|
14947
15085
|
} catch {
|
|
14948
15086
|
return failure2("invalid_input", "candidate root does not exist");
|
|
14949
15087
|
}
|
|
@@ -14961,11 +15099,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
14961
15099
|
if (item === ".git" || item === ".workit")
|
|
14962
15100
|
continue;
|
|
14963
15101
|
names.add(item);
|
|
14964
|
-
if (!
|
|
15102
|
+
if (!fs13.existsSync(target))
|
|
14965
15103
|
continue;
|
|
14966
15104
|
let stat;
|
|
14967
15105
|
try {
|
|
14968
|
-
stat =
|
|
15106
|
+
stat = fs13.lstatSync(target);
|
|
14969
15107
|
} catch {
|
|
14970
15108
|
uncertain = true;
|
|
14971
15109
|
continue;
|
|
@@ -14981,17 +15119,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
14981
15119
|
for (const item of [...names].toSorted()) {
|
|
14982
15120
|
if (!scopeMatches(item, normalizedScope))
|
|
14983
15121
|
continue;
|
|
14984
|
-
const target =
|
|
15122
|
+
const target = path15.join(checkout, item);
|
|
14985
15123
|
try {
|
|
14986
|
-
const stat =
|
|
15124
|
+
const stat = fs13.lstatSync(target);
|
|
14987
15125
|
if (stat.isSymbolicLink()) {
|
|
14988
|
-
const link =
|
|
15126
|
+
const link = fs13.readlinkSync(target);
|
|
14989
15127
|
files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
|
|
14990
15128
|
} else if (stat.isFile()) {
|
|
14991
15129
|
files.push({
|
|
14992
15130
|
path: item,
|
|
14993
15131
|
kind: "file",
|
|
14994
|
-
digest: digestBytes(
|
|
15132
|
+
digest: digestBytes(fs13.readFileSync(target)),
|
|
14995
15133
|
executable: (stat.mode & 73) !== 0
|
|
14996
15134
|
});
|
|
14997
15135
|
} else if (!stat.isDirectory()) {
|
|
@@ -15156,7 +15294,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
|
|
|
15156
15294
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
15157
15295
|
let root;
|
|
15158
15296
|
try {
|
|
15159
|
-
root =
|
|
15297
|
+
root = fs13.realpathSync(checkoutRoot);
|
|
15160
15298
|
} catch {
|
|
15161
15299
|
return failure2("permission_denied", "checkout root is unavailable");
|
|
15162
15300
|
}
|
|
@@ -15165,23 +15303,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
15165
15303
|
continue;
|
|
15166
15304
|
if (!reference.digest)
|
|
15167
15305
|
return failure2("invalid_input", "document references require a byte digest");
|
|
15168
|
-
const target =
|
|
15169
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
15306
|
+
const target = path15.resolve(root, reference.path);
|
|
15307
|
+
if (target !== root && !target.startsWith(`${root}${path15.sep}`))
|
|
15170
15308
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
15171
15309
|
fields: [
|
|
15172
15310
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
15173
15311
|
]
|
|
15174
15312
|
});
|
|
15175
15313
|
try {
|
|
15176
|
-
const below =
|
|
15314
|
+
const below = path15.relative(root, target);
|
|
15177
15315
|
let current = root;
|
|
15178
|
-
for (const segment of below.split(
|
|
15316
|
+
for (const segment of below.split(path15.sep)) {
|
|
15179
15317
|
if (!segment)
|
|
15180
15318
|
continue;
|
|
15181
|
-
current =
|
|
15182
|
-
const stat =
|
|
15183
|
-
const real =
|
|
15184
|
-
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}`))
|
|
15185
15323
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
15186
15324
|
fields: [
|
|
15187
15325
|
{
|
|
@@ -15197,7 +15335,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
15197
15335
|
if (current !== target && !stat.isDirectory())
|
|
15198
15336
|
return failure2("invalid_input", "document reference ancestor is not a directory");
|
|
15199
15337
|
}
|
|
15200
|
-
if (digestBytes(
|
|
15338
|
+
if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
|
|
15201
15339
|
return failure2("permission_denied", "decision document bytes have changed");
|
|
15202
15340
|
} catch {
|
|
15203
15341
|
return failure2("invalid_input", "decision document is unavailable");
|
|
@@ -15566,7 +15704,8 @@ Autonomy contract:
|
|
|
15566
15704
|
satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
|
|
15567
15705
|
- Author is not verifier: your own verdict is \`--self\`, shown as
|
|
15568
15706
|
self-reviewed, never verified. High risk, \`verification: independent\` and
|
|
15569
|
-
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\`.
|
|
15570
15709
|
- A local-commit endpoint does not imply PR readiness, and a local commit alone
|
|
15571
15710
|
is not evidence of a requested remote push. Before reporting delivery,
|
|
15572
15711
|
reconcile every requested item and observe it (\`workit verify-delivery\`).
|
|
@@ -15659,38 +15798,45 @@ var compactReference = (ref) => {
|
|
|
15659
15798
|
};
|
|
15660
15799
|
return { kind: ref.kind };
|
|
15661
15800
|
};
|
|
15662
|
-
|
|
15663
|
-
const
|
|
15664
|
-
|
|
15665
|
-
|
|
15666
|
-
|
|
15667
|
-
|
|
15668
|
-
|
|
15669
|
-
|
|
15670
|
-
|
|
15671
|
-
|
|
15672
|
-
|
|
15673
|
-
|
|
15674
|
-
|
|
15675
|
-
|
|
15676
|
-
|
|
15677
|
-
|
|
15801
|
+
var newestDecisionFirst = (left, right) => {
|
|
15802
|
+
const leftAt = compactTimestamp(left.recordedAt);
|
|
15803
|
+
const rightAt = compactTimestamp(right.recordedAt);
|
|
15804
|
+
if (leftAt !== rightAt)
|
|
15805
|
+
return leftAt > rightAt ? -1 : 1;
|
|
15806
|
+
return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
|
|
15807
|
+
};
|
|
15808
|
+
var compactDecisions = (sources) => sources.flatMap((source) => source ? source.decisions.toSorted(newestDecisionFirst).map((entry) => ({
|
|
15809
|
+
id: entry.id,
|
|
15810
|
+
purpose: entry.data.purpose,
|
|
15811
|
+
status: entry.data.response,
|
|
15812
|
+
digest: entry.data.digest,
|
|
15813
|
+
choice: compactDecisionChoice(entry) || `task.inspect taskId=${source.taskId} view=full; decision=${entry.id}`,
|
|
15814
|
+
references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference),
|
|
15815
|
+
origin: source.origin ?? undefined
|
|
15816
|
+
})) : []).slice(0, COMPACT_MAX_ITEMS);
|
|
15817
|
+
function compactTaskContext(view, inherited = null) {
|
|
15818
|
+
const decisions = compactDecisions([
|
|
15819
|
+
view && { origin: null, taskId: view.task.id, decisions: view.task.decisions },
|
|
15820
|
+
inherited
|
|
15821
|
+
]);
|
|
15822
|
+
const methods = view?.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
|
|
15678
15823
|
id: method.id,
|
|
15679
15824
|
assurance: method.assurance,
|
|
15680
15825
|
reason: compactText(method.reason, COMPACT_TEXT_BYTES) ?? ""
|
|
15681
15826
|
})) : [];
|
|
15827
|
+
const stale = view?.evidence.filter((entry) => entry.status === "stale").length;
|
|
15682
15828
|
const gaps = Array.from(new Set([
|
|
15683
|
-
...
|
|
15684
|
-
...view
|
|
15685
|
-
...view
|
|
15829
|
+
...stale ? [`stale evidence: ${stale}; workit task inspect --task ${view.task.id} --view full`] : [],
|
|
15830
|
+
...(view?.requirements ?? []).filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
|
|
15831
|
+
...(view?.task.progress.blockers ?? []).map((entry) => entry.reason)
|
|
15686
15832
|
])).toSorted().slice(0, COMPACT_MAX_ITEMS).map((gap) => compactText(gap, COMPACT_TEXT_BYTES));
|
|
15687
15833
|
const context = {
|
|
15688
|
-
objective: compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "",
|
|
15689
|
-
status: view
|
|
15834
|
+
objective: view ? compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "" : "no task on this branch yet",
|
|
15835
|
+
status: view?.task.status ?? null,
|
|
15690
15836
|
decisions,
|
|
15691
15837
|
methods,
|
|
15692
15838
|
gaps,
|
|
15693
|
-
nextAction: compactText(view
|
|
15839
|
+
nextAction: compactText(view?.task.progress.nextAction ?? null, COMPACT_TEXT_BYTES)
|
|
15694
15840
|
};
|
|
15695
15841
|
let encoded = JSON.stringify(context);
|
|
15696
15842
|
while (compactBytes(encoded) > COMPACT_MAX_BYTES && (context.decisions.length || context.methods.length || context.gaps.length)) {
|
|
@@ -15716,7 +15862,7 @@ var exportDigest = (bundle) => sha256({
|
|
|
15716
15862
|
task: bundle.task
|
|
15717
15863
|
});
|
|
15718
15864
|
// packages/workit-core/src/core/branch.ts
|
|
15719
|
-
import { createHash as
|
|
15865
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
15720
15866
|
|
|
15721
15867
|
// packages/workit-core/src/core/git.ts
|
|
15722
15868
|
import { execFileSync } from "node:child_process";
|
|
@@ -15854,7 +16000,7 @@ var branchPolicySnapshotFor = (workspaceRoot) => {
|
|
|
15854
16000
|
const source = resolved.provenance.branchPolicy;
|
|
15855
16001
|
const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
|
|
15856
16002
|
const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
|
|
15857
|
-
const fingerprint =
|
|
16003
|
+
const fingerprint = createHash11("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
|
|
15858
16004
|
return {
|
|
15859
16005
|
branchPolicy,
|
|
15860
16006
|
namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
|
|
@@ -16110,7 +16256,7 @@ var judgeInputSummary = (raw) => {
|
|
|
16110
16256
|
|
|
16111
16257
|
// packages/workit-core/src/autonomy.ts
|
|
16112
16258
|
import os3 from "node:os";
|
|
16113
|
-
import
|
|
16259
|
+
import path16 from "node:path";
|
|
16114
16260
|
var accountHome = () => {
|
|
16115
16261
|
try {
|
|
16116
16262
|
return os3.userInfo().homedir;
|
|
@@ -16119,10 +16265,10 @@ var accountHome = () => {
|
|
|
16119
16265
|
}
|
|
16120
16266
|
};
|
|
16121
16267
|
var grantsHome = { resolve: accountHome };
|
|
16122
|
-
var grantsDir = () =>
|
|
16268
|
+
var grantsDir = () => path16.join(grantsHome.resolve(), ".config", "workit");
|
|
16123
16269
|
var grantsOverride = () => {
|
|
16124
|
-
const configured =
|
|
16125
|
-
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`;
|
|
16126
16272
|
};
|
|
16127
16273
|
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
16128
16274
|
var DEFAULT_GRANTS = Object.freeze({
|
|
@@ -16408,6 +16554,13 @@ var TASK_SCOPED_ACTIONS = new Set([
|
|
|
16408
16554
|
"worker.cancel",
|
|
16409
16555
|
"state.export"
|
|
16410
16556
|
]);
|
|
16557
|
+
var WORKER_ADDRESSED_ACTIONS = new Set(["worker.cancel", "worker.report"]);
|
|
16558
|
+
var workerCommand = (taskId, request) => {
|
|
16559
|
+
const { action, workerId, reason, report } = request;
|
|
16560
|
+
const rest = JSON.stringify({ reason, report });
|
|
16561
|
+
const payload = rest === "{}" ? "" : ` --payload '${rest.replace(/'/gu, "'\\''")}'`;
|
|
16562
|
+
return `workit worker ${String(action)} ${String(workerId)} --task ${taskId}${payload}`;
|
|
16563
|
+
};
|
|
16411
16564
|
var CREATING_ACTIONS = new Set([
|
|
16412
16565
|
"task.revise",
|
|
16413
16566
|
"task.progress",
|
|
@@ -16658,6 +16811,17 @@ class WorkitCore {
|
|
|
16658
16811
|
const root = this.contextRootError();
|
|
16659
16812
|
if (!root.ok)
|
|
16660
16813
|
return root;
|
|
16814
|
+
const addressed = WORKER_ADDRESSED_ACTIONS.has(action);
|
|
16815
|
+
const workerId = addressed ? value.workerId : this.context.workerId ?? null;
|
|
16816
|
+
if (workerId !== null) {
|
|
16817
|
+
const owner = this.store.taskForWorker(workerId);
|
|
16818
|
+
if (!owner.ok)
|
|
16819
|
+
return owner;
|
|
16820
|
+
if (owner.data)
|
|
16821
|
+
return success(null, null, { ...request, taskId: owner.data });
|
|
16822
|
+
if (addressed)
|
|
16823
|
+
return failure2("not_found", `worker ${workerId} is on no open task; find its task with \`workit task status --all\`, then run \`${workerCommand("<task-id>", request)}\``);
|
|
16824
|
+
}
|
|
16661
16825
|
const create = CREATING_ACTIONS.has(action) && (this.context.workerId ?? null) === null;
|
|
16662
16826
|
const found = this.store.implicitTask({
|
|
16663
16827
|
provenance: provenance(this.context),
|
|
@@ -17364,7 +17528,9 @@ class WorkitCore {
|
|
|
17364
17528
|
if (!resolved.ok)
|
|
17365
17529
|
return resolved;
|
|
17366
17530
|
const value = resolved.data;
|
|
17367
|
-
|
|
17531
|
+
const result = this.retryOmittedRevisions(value, () => this.workerOnce(value));
|
|
17532
|
+
const { action, taskId } = value ?? {};
|
|
17533
|
+
return result.ok || request?.taskId !== undefined || typeof taskId !== "string" || !WORKER_ADDRESSED_ACTIONS.has(`worker.${String(action)}`) ? result : { ...result, error: `${result.error}; run \`${workerCommand(taskId, value)}\`` };
|
|
17368
17534
|
}
|
|
17369
17535
|
workerOnce(request) {
|
|
17370
17536
|
const root = this.contextRootError();
|
|
@@ -17569,10 +17735,18 @@ class WorkitCore {
|
|
|
17569
17735
|
requirements
|
|
17570
17736
|
});
|
|
17571
17737
|
}
|
|
17572
|
-
compactContext(taskId, freshness) {
|
|
17738
|
+
compactContext(taskId, freshness, origin = null) {
|
|
17573
17739
|
const root = this.contextRootError();
|
|
17574
17740
|
if (!root.ok)
|
|
17575
17741
|
return root;
|
|
17742
|
+
const base = origin && this.store.readStoreTask(origin.taskId);
|
|
17743
|
+
const inherited = base?.ok && base.data ? {
|
|
17744
|
+
origin: origin.origin,
|
|
17745
|
+
taskId: base.data.id,
|
|
17746
|
+
decisions: base.data.decisions.filter((entry) => entry.data.revoked === null)
|
|
17747
|
+
} : null;
|
|
17748
|
+
if (taskId === null)
|
|
17749
|
+
return success(null, null, inherited?.decisions.length ? compactTaskContext(null, inherited) : "");
|
|
17576
17750
|
const task = this.store.readTask(taskId);
|
|
17577
17751
|
if (!task.ok)
|
|
17578
17752
|
return task;
|
|
@@ -17582,7 +17756,7 @@ class WorkitCore {
|
|
|
17582
17756
|
const view = this.view(task.data, false, freshness ?? signalFreshness(this.store.root));
|
|
17583
17757
|
if (!view.ok)
|
|
17584
17758
|
return view;
|
|
17585
|
-
return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
|
|
17759
|
+
return success(view.revision, view.workspaceRevision, compactTaskContext(view.data, inherited));
|
|
17586
17760
|
}
|
|
17587
17761
|
view(task, capture = true, freshness = treeFreshness(this.store.root)) {
|
|
17588
17762
|
const workspace = this.store.readWorkspace();
|
|
@@ -17866,7 +18040,7 @@ class WorkitCore {
|
|
|
17866
18040
|
// packages/workit-core/src/core/repo-context.ts
|
|
17867
18041
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
17868
18042
|
import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
|
|
17869
|
-
import
|
|
18043
|
+
import path17 from "node:path";
|
|
17870
18044
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
17871
18045
|
try {
|
|
17872
18046
|
gitRevisionParts(value);
|
|
@@ -18000,7 +18174,7 @@ var findPrTemplate = (cwd) => {
|
|
|
18000
18174
|
]) {
|
|
18001
18175
|
const parts = rel.split("/");
|
|
18002
18176
|
const base = parts[parts.length - 1];
|
|
18003
|
-
const dir = parts.length > 1 ?
|
|
18177
|
+
const dir = parts.length > 1 ? path17.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
18004
18178
|
const actual = probeCaseInsensitive(dir, base);
|
|
18005
18179
|
if (actual)
|
|
18006
18180
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -18031,7 +18205,7 @@ var diffStatForRange = (cwd, range) => {
|
|
|
18031
18205
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
18032
18206
|
};
|
|
18033
18207
|
var packageScripts = (cwd, keys) => {
|
|
18034
|
-
const pkgPath =
|
|
18208
|
+
const pkgPath = path17.join(cwd, "package.json");
|
|
18035
18209
|
if (!existsSync6(pkgPath))
|
|
18036
18210
|
return [];
|
|
18037
18211
|
try {
|
|
@@ -18057,9 +18231,9 @@ var documentationFiles = (cwd) => {
|
|
|
18057
18231
|
if (entry.isDirectory()) {
|
|
18058
18232
|
if (excluded.has(entry.name))
|
|
18059
18233
|
continue;
|
|
18060
|
-
walk(
|
|
18234
|
+
walk(path17.join(dir, entry.name), depth + 1);
|
|
18061
18235
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
18062
|
-
matches.push("./" +
|
|
18236
|
+
matches.push("./" + path17.relative(cwd, path17.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
18063
18237
|
}
|
|
18064
18238
|
}
|
|
18065
18239
|
};
|
|
@@ -18075,7 +18249,7 @@ var readTrimmed2 = (file, maxLines) => {
|
|
|
18075
18249
|
`);
|
|
18076
18250
|
};
|
|
18077
18251
|
function prReadyContext(root, range) {
|
|
18078
|
-
const cwd =
|
|
18252
|
+
const cwd = path17.resolve(repoRoot(root));
|
|
18079
18253
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
18080
18254
|
return invalidRange(cwd);
|
|
18081
18255
|
let stdout = "";
|
|
@@ -18133,7 +18307,7 @@ ${status.stderr}` : "");
|
|
|
18133
18307
|
stdout += `template_path: ${template}
|
|
18134
18308
|
|
|
18135
18309
|
`;
|
|
18136
|
-
stdout += readTrimmed2(
|
|
18310
|
+
stdout += readTrimmed2(path17.join(cwd, template), 220);
|
|
18137
18311
|
} else {
|
|
18138
18312
|
stdout += `template_path: none
|
|
18139
18313
|
|
|
@@ -18143,7 +18317,7 @@ ${status.stderr}` : "");
|
|
|
18143
18317
|
stdout += `
|
|
18144
18318
|
`;
|
|
18145
18319
|
stdout += printSection("Recent Validation Signals");
|
|
18146
|
-
if (existsSync6(
|
|
18320
|
+
if (existsSync6(path17.join(cwd, "package.json"))) {
|
|
18147
18321
|
stdout += `package.json detected. Common scripts:
|
|
18148
18322
|
`;
|
|
18149
18323
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -18152,7 +18326,7 @@ ${status.stderr}` : "");
|
|
|
18152
18326
|
`) + `
|
|
18153
18327
|
`;
|
|
18154
18328
|
}
|
|
18155
|
-
if (existsSync6(
|
|
18329
|
+
if (existsSync6(path17.join(cwd, "Cargo.toml")) || existsSync6(path17.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
18156
18330
|
stdout += `Rust project detected.
|
|
18157
18331
|
`;
|
|
18158
18332
|
}
|
|
@@ -18185,7 +18359,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
18185
18359
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
18186
18360
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
18187
18361
|
function changelogContext(root, range) {
|
|
18188
|
-
const cwd =
|
|
18362
|
+
const cwd = path17.resolve(repoRoot(root));
|
|
18189
18363
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
18190
18364
|
return invalidRange(cwd);
|
|
18191
18365
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -18203,7 +18377,7 @@ function changelogContext(root, range) {
|
|
|
18203
18377
|
stdout += CHANGELOG_RULES + `
|
|
18204
18378
|
`;
|
|
18205
18379
|
stdout += printSection("Existing CHANGELOG.md");
|
|
18206
|
-
const changelogPath =
|
|
18380
|
+
const changelogPath = path17.join(cwd, "CHANGELOG.md");
|
|
18207
18381
|
if (existsSync6(changelogPath)) {
|
|
18208
18382
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
18209
18383
|
`;
|
|
@@ -18223,7 +18397,7 @@ function changelogContext(root, range) {
|
|
|
18223
18397
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
18224
18398
|
}
|
|
18225
18399
|
function docsRefreshContext(root, range) {
|
|
18226
|
-
const cwd =
|
|
18400
|
+
const cwd = path17.resolve(repoRoot(root));
|
|
18227
18401
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
18228
18402
|
return invalidRange(cwd);
|
|
18229
18403
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -18245,7 +18419,7 @@ function docsRefreshContext(root, range) {
|
|
|
18245
18419
|
`) + `
|
|
18246
18420
|
`;
|
|
18247
18421
|
stdout += printSection("README Preview");
|
|
18248
|
-
const readme =
|
|
18422
|
+
const readme = path17.join(cwd, "README.md");
|
|
18249
18423
|
if (existsSync6(readme)) {
|
|
18250
18424
|
stdout += readTrimmed2(readme, 220) + `
|
|
18251
18425
|
`;
|
|
@@ -18254,7 +18428,7 @@ function docsRefreshContext(root, range) {
|
|
|
18254
18428
|
`;
|
|
18255
18429
|
}
|
|
18256
18430
|
stdout += printSection("Package Scripts");
|
|
18257
|
-
const pkgPath =
|
|
18431
|
+
const pkgPath = path17.join(cwd, "package.json");
|
|
18258
18432
|
if (existsSync6(pkgPath)) {
|
|
18259
18433
|
try {
|
|
18260
18434
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
@@ -18268,7 +18442,7 @@ function docsRefreshContext(root, range) {
|
|
|
18268
18442
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
18269
18443
|
}
|
|
18270
18444
|
function releaseNotesContext(root, rangeOrTag) {
|
|
18271
|
-
const cwd =
|
|
18445
|
+
const cwd = path17.resolve(repoRoot(root));
|
|
18272
18446
|
if (!rangeOrTag) {
|
|
18273
18447
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
18274
18448
|
`, exitCode: 1, cwd };
|
|
@@ -18306,7 +18480,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
18306
18480
|
`;
|
|
18307
18481
|
stdout += printSection("Existing Release Files");
|
|
18308
18482
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
18309
|
-
if (existsSync6(
|
|
18483
|
+
if (existsSync6(path17.join(cwd, rel)))
|
|
18310
18484
|
stdout += rel + `
|
|
18311
18485
|
`;
|
|
18312
18486
|
}
|
|
@@ -18337,8 +18511,8 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
18337
18511
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
18338
18512
|
|
|
18339
18513
|
// packages/workit-core/src/core/pr-create.ts
|
|
18340
|
-
import
|
|
18341
|
-
import
|
|
18514
|
+
import fs14 from "node:fs";
|
|
18515
|
+
import path18 from "node:path";
|
|
18342
18516
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
18343
18517
|
// packages/workit-core/src/forge/pr-body.ts
|
|
18344
18518
|
function parseGhIssue(value) {
|
|
@@ -18349,13 +18523,13 @@ function parseGhIssue(value) {
|
|
|
18349
18523
|
// packages/workit-core/src/core/pr-create.ts
|
|
18350
18524
|
function whichOnPath(tool) {
|
|
18351
18525
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
18352
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
18526
|
+
for (const dir of (process.env.PATH ?? "").split(path18.delimiter)) {
|
|
18353
18527
|
if (!dir)
|
|
18354
18528
|
continue;
|
|
18355
18529
|
for (const name of names) {
|
|
18356
|
-
const candidate =
|
|
18530
|
+
const candidate = path18.join(dir, name);
|
|
18357
18531
|
try {
|
|
18358
|
-
|
|
18532
|
+
fs14.accessSync(candidate, fs14.constants.X_OK);
|
|
18359
18533
|
return candidate;
|
|
18360
18534
|
} catch {}
|
|
18361
18535
|
}
|
|
@@ -18473,8 +18647,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
|
18473
18647
|
};
|
|
18474
18648
|
|
|
18475
18649
|
// packages/workit-core/src/core/youtrack.ts
|
|
18476
|
-
import
|
|
18477
|
-
import
|
|
18650
|
+
import fs15 from "node:fs";
|
|
18651
|
+
import path19 from "node:path";
|
|
18478
18652
|
|
|
18479
18653
|
// packages/workit-core/src/core/templates.ts
|
|
18480
18654
|
var repoRoot2 = assetRoot();
|
|
@@ -18487,22 +18661,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
18487
18661
|
// packages/workit-core/src/core/youtrack.ts
|
|
18488
18662
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
18489
18663
|
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
18490
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
18491
|
-
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");
|
|
18492
18666
|
var youTrackTokenModeOk = (p) => {
|
|
18493
18667
|
if (process.platform === "win32")
|
|
18494
18668
|
return true;
|
|
18495
|
-
const mode =
|
|
18669
|
+
const mode = fs15.statSync(p).mode & 511;
|
|
18496
18670
|
return mode === 384;
|
|
18497
18671
|
};
|
|
18498
18672
|
function readYouTrackConfig(required) {
|
|
18499
18673
|
const cfgPath = youTrackConfigPath();
|
|
18500
|
-
if (!
|
|
18674
|
+
if (!fs15.existsSync(cfgPath)) {
|
|
18501
18675
|
return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
|
|
18502
18676
|
}
|
|
18503
18677
|
let parsed;
|
|
18504
18678
|
try {
|
|
18505
|
-
parsed = JSON.parse(
|
|
18679
|
+
parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
|
|
18506
18680
|
} catch {
|
|
18507
18681
|
return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
|
|
18508
18682
|
}
|
|
@@ -18513,11 +18687,11 @@ function readYouTrackConfig(required) {
|
|
|
18513
18687
|
}
|
|
18514
18688
|
var readYouTrackContextConfig = () => {
|
|
18515
18689
|
const cfgPath = youTrackReadOnlyConfigPath();
|
|
18516
|
-
if (!
|
|
18690
|
+
if (!fs15.existsSync(cfgPath))
|
|
18517
18691
|
return { data: {} };
|
|
18518
18692
|
let parsed;
|
|
18519
18693
|
try {
|
|
18520
|
-
parsed = JSON.parse(
|
|
18694
|
+
parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
|
|
18521
18695
|
} catch {
|
|
18522
18696
|
return { error: "YouTrack configuration is unavailable" };
|
|
18523
18697
|
}
|
|
@@ -18556,12 +18730,12 @@ var youTrackToken = () => {
|
|
|
18556
18730
|
if ("error" in loaded)
|
|
18557
18731
|
return loaded;
|
|
18558
18732
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
18559
|
-
const tokenPath = tokenFile ?
|
|
18560
|
-
if (!tokenPath || !
|
|
18733
|
+
const tokenPath = tokenFile ? path19.isAbsolute(tokenFile) ? path19.resolve(tokenFile) : path19.resolve(process.cwd(), tokenFile) : "";
|
|
18734
|
+
if (!tokenPath || !fs15.existsSync(tokenPath))
|
|
18561
18735
|
return { error: "missing youtrack.token" };
|
|
18562
18736
|
if (!youTrackTokenModeOk(tokenPath))
|
|
18563
18737
|
return { error: "youtrack.token mode must be 0600" };
|
|
18564
|
-
const token =
|
|
18738
|
+
const token = fs15.readFileSync(tokenPath, "utf8").trim();
|
|
18565
18739
|
if (!token)
|
|
18566
18740
|
return { error: "empty token file" };
|
|
18567
18741
|
if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
|
|
@@ -18626,9 +18800,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
|
|
|
18626
18800
|
} catch {
|
|
18627
18801
|
continue;
|
|
18628
18802
|
}
|
|
18629
|
-
if (!
|
|
18803
|
+
if (!fs15.existsSync(full))
|
|
18630
18804
|
continue;
|
|
18631
|
-
const text =
|
|
18805
|
+
const text = fs15.readFileSync(full, "utf8");
|
|
18632
18806
|
const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
|
|
18633
18807
|
if (m)
|
|
18634
18808
|
return m[1];
|
|
@@ -18913,15 +19087,15 @@ function resolveGitRevision(root, value) {
|
|
|
18913
19087
|
}
|
|
18914
19088
|
function resolveInside(root, candidate) {
|
|
18915
19089
|
const base = realpathSync5(root);
|
|
18916
|
-
const target =
|
|
18917
|
-
if (target !== base && !target.startsWith(base +
|
|
19090
|
+
const target = path20.resolve(base, candidate);
|
|
19091
|
+
if (target !== base && !target.startsWith(base + path20.sep)) {
|
|
18918
19092
|
throw new Error("path must stay inside repository root");
|
|
18919
19093
|
}
|
|
18920
19094
|
let ancestor = target;
|
|
18921
19095
|
while (!existsSync7(ancestor))
|
|
18922
|
-
ancestor =
|
|
19096
|
+
ancestor = path20.dirname(ancestor);
|
|
18923
19097
|
const canonicalAncestor = realpathSync5(ancestor);
|
|
18924
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
19098
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path20.sep)) {
|
|
18925
19099
|
throw new Error("path must stay inside repository root");
|
|
18926
19100
|
}
|
|
18927
19101
|
return target;
|
|
@@ -18965,9 +19139,9 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
18965
19139
|
};
|
|
18966
19140
|
});
|
|
18967
19141
|
// packages/workit-core/src/hooks/context.ts
|
|
18968
|
-
import { createHash as
|
|
18969
|
-
import
|
|
18970
|
-
import
|
|
19142
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
19143
|
+
import fs16 from "node:fs";
|
|
19144
|
+
import path21 from "node:path";
|
|
18971
19145
|
var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
|
|
18972
19146
|
var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
|
|
18973
19147
|
var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
|
|
@@ -18980,8 +19154,9 @@ var currentTaskEntry = (entries, session, selection) => {
|
|
|
18980
19154
|
const active = entries.filter((entry) => entry.status === "active");
|
|
18981
19155
|
return active.length === 1 ? active[0] : null;
|
|
18982
19156
|
};
|
|
19157
|
+
var otherConversation = (entry, session) => entry.sessions.some((item) => item.host !== "workit_cli" && !(item.host === session.host && item.handle === session.handle));
|
|
18983
19158
|
var implicitTaskEntry = (store, entries, session) => {
|
|
18984
|
-
const open = entries.filter((entry) => entry.key !== null && entry.status !== "closed" && !entry
|
|
19159
|
+
const open = entries.filter((entry) => entry.key !== null && entry.status !== "closed" && !otherConversation(entry, session));
|
|
18985
19160
|
if (open.length === 0)
|
|
18986
19161
|
return null;
|
|
18987
19162
|
const key = store.currentKey();
|
|
@@ -19022,18 +19197,23 @@ function sessionCompactContext(store, session, context, selection = "session-bou
|
|
|
19022
19197
|
if (!listed.ok)
|
|
19023
19198
|
return migrationPending(listed) ? MIGRATION_PENDING_NOTE : null;
|
|
19024
19199
|
const entry = currentTaskEntry(listed.data, session, selection) ?? implicitTaskEntry(store, listed.data, session);
|
|
19025
|
-
return
|
|
19200
|
+
return entryCompactContext(store, entry, session, context);
|
|
19026
19201
|
}
|
|
19027
19202
|
function entryCompactContext(store, entry, session, context) {
|
|
19203
|
+
const found = store.originTaskEntry();
|
|
19204
|
+
const origin = found.ok && found.data && found.data.entry.id !== entry?.id && !otherConversation(found.data.entry, session) ? found.data : null;
|
|
19205
|
+
if (!entry && !origin)
|
|
19206
|
+
return null;
|
|
19028
19207
|
const workspace = store.readWorkspace();
|
|
19029
|
-
if (!workspace.ok || !workspace.data)
|
|
19208
|
+
if (!workspace.ok || entry && !workspace.data)
|
|
19030
19209
|
return null;
|
|
19031
19210
|
const slot = `${store.root}\x00${session.host}\x00${session.handle}`;
|
|
19032
19211
|
const key = canonicalJson({
|
|
19033
|
-
task: entry
|
|
19034
|
-
revision: entry
|
|
19035
|
-
file: entry
|
|
19036
|
-
|
|
19212
|
+
task: entry?.id ?? null,
|
|
19213
|
+
revision: entry?.revision ?? null,
|
|
19214
|
+
file: entry?.file ?? null,
|
|
19215
|
+
origin: origin && origin.entry.file,
|
|
19216
|
+
workspace: workspace.data?.revision ?? null,
|
|
19037
19217
|
caller: context.caller,
|
|
19038
19218
|
workerId: context.workerId ?? null,
|
|
19039
19219
|
capabilities: context.capabilities
|
|
@@ -19041,15 +19221,15 @@ function entryCompactContext(store, entry, session, context) {
|
|
|
19041
19221
|
const hit = cache.get(slot);
|
|
19042
19222
|
if (hit && hit.key === key && hit.filesKey !== null && hit.filesKey === filesKey(hit.files) && (hit.signal === null || hit.signal === recentSignal(store.root)))
|
|
19043
19223
|
return hit.value;
|
|
19044
|
-
const task = store.readTask(entry.id);
|
|
19045
|
-
if (!task.ok)
|
|
19224
|
+
const task = entry ? store.readTask(entry.id) : null;
|
|
19225
|
+
if (task && !task.ok)
|
|
19046
19226
|
return null;
|
|
19047
19227
|
const freshness = { mode: "signal", current: () => recentSignal(store.root) };
|
|
19048
|
-
const observed = task
|
|
19049
|
-
const files = task
|
|
19228
|
+
const observed = task?.data.evidence.some(isObservedCheck) ?? false;
|
|
19229
|
+
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path21.resolve(store.root, ref.path)] : []));
|
|
19050
19230
|
const observedFiles = filesKey(files);
|
|
19051
|
-
const compact = new WorkitCore(store, context).compactContext(entry
|
|
19052
|
-
if (!compact.ok)
|
|
19231
|
+
const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
|
|
19232
|
+
if (!compact.ok || !compact.data)
|
|
19053
19233
|
return null;
|
|
19054
19234
|
cache.delete(slot);
|
|
19055
19235
|
cache.set(slot, {
|
|
@@ -19088,7 +19268,7 @@ ${options.addendum}` : ""}
|
|
|
19088
19268
|
if (!listed.ok)
|
|
19089
19269
|
throw new Error(listed.error);
|
|
19090
19270
|
const entry = currentTaskEntry(listed.data, session, descriptor.context.task) ?? implicitTaskEntry(store, listed.data, session);
|
|
19091
|
-
const text =
|
|
19271
|
+
const text = entryCompactContext(store, entry, session, hookOperationContext(input, descriptor));
|
|
19092
19272
|
if (text)
|
|
19093
19273
|
compact = `
|
|
19094
19274
|
<workit-task-context>${text}</workit-task-context>`;
|
|
@@ -19115,7 +19295,7 @@ var turnContextText = (input, descriptor) => {
|
|
|
19115
19295
|
}
|
|
19116
19296
|
};
|
|
19117
19297
|
var TURN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
19118
|
-
var digestOf = (text) =>
|
|
19298
|
+
var digestOf = (text) => createHash12("sha256").update(text).digest("hex");
|
|
19119
19299
|
var turnMarker = (input) => {
|
|
19120
19300
|
if (!input.session.id)
|
|
19121
19301
|
return null;
|
|
@@ -19124,32 +19304,32 @@ var turnMarker = (input) => {
|
|
|
19124
19304
|
if (location instanceof Error)
|
|
19125
19305
|
return null;
|
|
19126
19306
|
const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
|
|
19127
|
-
return
|
|
19307
|
+
return path21.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
|
|
19128
19308
|
} catch {
|
|
19129
19309
|
return null;
|
|
19130
19310
|
}
|
|
19131
19311
|
};
|
|
19132
19312
|
var writeTurnDigest = (file, digest) => {
|
|
19133
19313
|
try {
|
|
19134
|
-
|
|
19135
|
-
|
|
19314
|
+
fs16.mkdirSync(path21.dirname(file), { recursive: true });
|
|
19315
|
+
fs16.writeFileSync(file, `${JSON.stringify({ digest })}
|
|
19136
19316
|
`);
|
|
19137
19317
|
} catch {}
|
|
19138
19318
|
};
|
|
19139
19319
|
var clearTurnDigest = (file) => {
|
|
19140
19320
|
try {
|
|
19141
|
-
|
|
19321
|
+
fs16.rmSync(file, { force: true });
|
|
19142
19322
|
} catch {}
|
|
19143
19323
|
};
|
|
19144
19324
|
var pruneTurnDigests = (dir, now) => {
|
|
19145
19325
|
try {
|
|
19146
|
-
for (const name of
|
|
19326
|
+
for (const name of fs16.readdirSync(dir)) {
|
|
19147
19327
|
if (!name.startsWith("turn-"))
|
|
19148
19328
|
continue;
|
|
19149
|
-
const file =
|
|
19329
|
+
const file = path21.join(dir, name);
|
|
19150
19330
|
try {
|
|
19151
|
-
if (now -
|
|
19152
|
-
|
|
19331
|
+
if (now - fs16.statSync(file).mtimeMs > TURN_TTL_MS)
|
|
19332
|
+
fs16.rmSync(file, { force: true });
|
|
19153
19333
|
} catch {}
|
|
19154
19334
|
}
|
|
19155
19335
|
} catch {}
|
|
@@ -19165,7 +19345,7 @@ var changedTurnContext = (input, text) => {
|
|
|
19165
19345
|
return text;
|
|
19166
19346
|
const digest = digestOf(text);
|
|
19167
19347
|
try {
|
|
19168
|
-
const previous = JSON.parse(
|
|
19348
|
+
const previous = JSON.parse(fs16.readFileSync(file, "utf8"));
|
|
19169
19349
|
if (previous.digest === digest)
|
|
19170
19350
|
return null;
|
|
19171
19351
|
} catch {}
|
|
@@ -19176,7 +19356,7 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
|
|
|
19176
19356
|
const file = turnMarker(input);
|
|
19177
19357
|
if (!file)
|
|
19178
19358
|
return;
|
|
19179
|
-
pruneTurnDigests(
|
|
19359
|
+
pruneTurnDigests(path21.dirname(file), Date.now());
|
|
19180
19360
|
if (text)
|
|
19181
19361
|
writeTurnDigest(file, digestOf(text));
|
|
19182
19362
|
else
|
|
@@ -19189,7 +19369,7 @@ var shellPolicy = (cwd, command) => {
|
|
|
19189
19369
|
};
|
|
19190
19370
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
19191
19371
|
import { realpathSync as realpathSync6 } from "node:fs";
|
|
19192
|
-
import
|
|
19372
|
+
import path22 from "node:path";
|
|
19193
19373
|
|
|
19194
19374
|
// packages/workit-core/src/hooks/shell-words.ts
|
|
19195
19375
|
var closingQuote = (command, start, dialect) => {
|
|
@@ -19319,22 +19499,22 @@ var NONE2 = { kind: "none" };
|
|
|
19319
19499
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
19320
19500
|
var canonical2 = (file) => {
|
|
19321
19501
|
let rest = "";
|
|
19322
|
-
for (let dir =
|
|
19502
|
+
for (let dir = path22.resolve(file);; dir = path22.dirname(dir)) {
|
|
19323
19503
|
try {
|
|
19324
|
-
return
|
|
19504
|
+
return path22.join(realpathSync6(dir), rest);
|
|
19325
19505
|
} catch {
|
|
19326
|
-
if (
|
|
19327
|
-
return
|
|
19328
|
-
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);
|
|
19329
19509
|
}
|
|
19330
19510
|
}
|
|
19331
19511
|
};
|
|
19332
19512
|
var exempt = (cwd, root, file, plans) => {
|
|
19333
19513
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
19334
|
-
const relative =
|
|
19335
|
-
if (relative.startsWith("..") ||
|
|
19514
|
+
const relative = path22.relative(fold(canonical2(root)), fold(canonical2(path22.resolve(cwd, file))));
|
|
19515
|
+
if (relative.startsWith("..") || path22.isAbsolute(relative))
|
|
19336
19516
|
return true;
|
|
19337
|
-
const posix = relative.split(
|
|
19517
|
+
const posix = relative.split(path22.sep).join("/");
|
|
19338
19518
|
const segments = posix.split("/");
|
|
19339
19519
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
19340
19520
|
};
|
|
@@ -19432,7 +19612,7 @@ var shellWrites = (command) => {
|
|
|
19432
19612
|
return { writes: true, targets: unknown ? null : targets };
|
|
19433
19613
|
};
|
|
19434
19614
|
var cache2 = new Map;
|
|
19435
|
-
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
19615
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path22.resolve(root, file)) ?? "-").join("\x00");
|
|
19436
19616
|
var gateFor = (cwd) => {
|
|
19437
19617
|
const store = new TaskStore(cwd);
|
|
19438
19618
|
const listed = store.listTaskIndex();
|
|
@@ -19492,15 +19672,15 @@ import { spawnSync as spawnSync16 } from "node:child_process";
|
|
|
19492
19672
|
|
|
19493
19673
|
// packages/workit-core/src/hooks/raw-git.ts
|
|
19494
19674
|
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
19495
|
-
import { createHash as
|
|
19496
|
-
import
|
|
19675
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
19676
|
+
import fs18 from "node:fs";
|
|
19497
19677
|
import os4 from "node:os";
|
|
19498
|
-
import
|
|
19678
|
+
import path24 from "node:path";
|
|
19499
19679
|
|
|
19500
19680
|
// packages/workit-core/src/hooks/session-marker.ts
|
|
19501
|
-
import { createHash as
|
|
19502
|
-
import
|
|
19503
|
-
import
|
|
19681
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
19682
|
+
import fs17 from "node:fs";
|
|
19683
|
+
import path23 from "node:path";
|
|
19504
19684
|
var sessionMarkerFile = (input, session, store) => {
|
|
19505
19685
|
let dir = store;
|
|
19506
19686
|
if (dir === undefined) {
|
|
@@ -19509,13 +19689,13 @@ var sessionMarkerFile = (input, session, store) => {
|
|
|
19509
19689
|
return null;
|
|
19510
19690
|
dir = location.dir;
|
|
19511
19691
|
}
|
|
19512
|
-
const name =
|
|
19513
|
-
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`);
|
|
19514
19694
|
};
|
|
19515
19695
|
var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
19516
19696
|
var readSessionMarker = (file) => {
|
|
19517
19697
|
try {
|
|
19518
|
-
const value = JSON.parse(
|
|
19698
|
+
const value = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
19519
19699
|
return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
|
|
19520
19700
|
} catch {
|
|
19521
19701
|
return { nudged: [], loaded: [], raw: [] };
|
|
@@ -19523,8 +19703,8 @@ var readSessionMarker = (file) => {
|
|
|
19523
19703
|
};
|
|
19524
19704
|
var writeSessionMarker = (file, marker) => {
|
|
19525
19705
|
try {
|
|
19526
|
-
|
|
19527
|
-
|
|
19706
|
+
fs17.mkdirSync(path23.dirname(file), { recursive: true });
|
|
19707
|
+
fs17.writeFileSync(file, JSON.stringify(marker));
|
|
19528
19708
|
} catch {}
|
|
19529
19709
|
};
|
|
19530
19710
|
|
|
@@ -19545,12 +19725,12 @@ var GIT_VALUE_OPTIONS = new Set([
|
|
|
19545
19725
|
"--config-env",
|
|
19546
19726
|
"--super-prefix"
|
|
19547
19727
|
]);
|
|
19548
|
-
var isAbsolute = (value) =>
|
|
19728
|
+
var isAbsolute = (value) => path24.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
|
|
19549
19729
|
var joinDir = (base, next) => {
|
|
19550
19730
|
if (base === undefined || /[$`*?]/.test(next) || next === "-")
|
|
19551
19731
|
return;
|
|
19552
|
-
const expanded = next === "~" || next.startsWith("~/") ?
|
|
19553
|
-
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);
|
|
19554
19734
|
};
|
|
19555
19735
|
var cdTarget = (words) => {
|
|
19556
19736
|
const args = words.slice(1);
|
|
@@ -19803,7 +19983,7 @@ var repoAt = (dir) => {
|
|
|
19803
19983
|
return { location, dir, branch: resolveTaskKey(dir, location).branch };
|
|
19804
19984
|
};
|
|
19805
19985
|
var isWorkitWorkspace = (repo) => {
|
|
19806
|
-
if (
|
|
19986
|
+
if (fs18.existsSync(repo.location.dir))
|
|
19807
19987
|
return true;
|
|
19808
19988
|
try {
|
|
19809
19989
|
return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
|
|
@@ -19834,17 +20014,17 @@ var headSha2 = (repo) => {
|
|
|
19834
20014
|
if (!git || git.reftable)
|
|
19835
20015
|
return null;
|
|
19836
20016
|
try {
|
|
19837
|
-
const head =
|
|
20017
|
+
const head = fs18.readFileSync(path24.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
19838
20018
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
19839
20019
|
return head;
|
|
19840
20020
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
19841
20021
|
if (!ref)
|
|
19842
20022
|
return null;
|
|
19843
|
-
const common =
|
|
20023
|
+
const common = path24.dirname(repo.location.dir);
|
|
19844
20024
|
try {
|
|
19845
|
-
return
|
|
20025
|
+
return fs18.readFileSync(path24.join(common, ref), "utf8").trim() || null;
|
|
19846
20026
|
} catch {}
|
|
19847
|
-
const packed =
|
|
20027
|
+
const packed = fs18.readFileSync(path24.join(common, "packed-refs"), "utf8");
|
|
19848
20028
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
19849
20029
|
} catch {
|
|
19850
20030
|
return null;
|
|
@@ -19881,7 +20061,7 @@ var actionsIn = (cwd, command, dialect) => {
|
|
|
19881
20061
|
const action = rawAction(invocation);
|
|
19882
20062
|
if (!action)
|
|
19883
20063
|
continue;
|
|
19884
|
-
const dir =
|
|
20064
|
+
const dir = path24.resolve(cwd, invocation.dir ?? ".");
|
|
19885
20065
|
if (!repos.has(dir)) {
|
|
19886
20066
|
const repo = repoAt(dir);
|
|
19887
20067
|
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
@@ -19950,8 +20130,8 @@ function rawGitPre(input, command, options = {}) {
|
|
|
19950
20130
|
var deny = (reason) => ({ kind: "deny", reason, unblock: null });
|
|
19951
20131
|
var NEXT_COMMAND = "next";
|
|
19952
20132
|
var MARKER_TTL_SECONDS = 3600;
|
|
19953
|
-
var markerFile = (repo, session, key) =>
|
|
19954
|
-
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")}`;
|
|
19955
20135
|
function noteRawCommit(input, command, key) {
|
|
19956
20136
|
try {
|
|
19957
20137
|
if (!input.session.id || !/\bcommit\b/.test(command))
|
|
@@ -19961,8 +20141,8 @@ function noteRawCommit(input, command, key) {
|
|
|
19961
20141
|
if (action.kind !== "commit")
|
|
19962
20142
|
continue;
|
|
19963
20143
|
const file = markerFile(repo, input.session.id, key);
|
|
19964
|
-
|
|
19965
|
-
|
|
20144
|
+
fs18.mkdirSync(path24.dirname(file), { recursive: true });
|
|
20145
|
+
fs18.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
19966
20146
|
`);
|
|
19967
20147
|
}
|
|
19968
20148
|
} catch {}
|
|
@@ -19985,11 +20165,11 @@ var headCommit = (dir) => {
|
|
|
19985
20165
|
var settle2 = (input, repo, file) => {
|
|
19986
20166
|
let marker;
|
|
19987
20167
|
try {
|
|
19988
|
-
marker = JSON.parse(
|
|
20168
|
+
marker = JSON.parse(fs18.readFileSync(file, "utf8"));
|
|
19989
20169
|
} catch {
|
|
19990
20170
|
return;
|
|
19991
20171
|
}
|
|
19992
|
-
|
|
20172
|
+
fs18.rmSync(file, { force: true });
|
|
19993
20173
|
const session = input.session.id;
|
|
19994
20174
|
const now = Date.now() / 1000;
|
|
19995
20175
|
if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
|
|
@@ -20038,7 +20218,7 @@ function settlePendingCommit(input) {
|
|
|
20038
20218
|
if (!repo)
|
|
20039
20219
|
return;
|
|
20040
20220
|
const file = markerFile(repo, input.session.id, NEXT_COMMAND);
|
|
20041
|
-
if (
|
|
20221
|
+
if (fs18.existsSync(file))
|
|
20042
20222
|
settle2(input, repo, file);
|
|
20043
20223
|
} catch {}
|
|
20044
20224
|
}
|
|
@@ -20496,6 +20676,8 @@ ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
|
|
|
20496
20676
|
if (role !== "judge" || !event.agentId)
|
|
20497
20677
|
return readOnly;
|
|
20498
20678
|
const own = subagentSession(session, event.agentId);
|
|
20679
|
+
if (session)
|
|
20680
|
+
recordIssuedSession(input.cwd || process.cwd(), session, own);
|
|
20499
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.`;
|
|
20500
20682
|
};
|
|
20501
20683
|
function handleHook(input, deps) {
|
|
@@ -20857,12 +21039,12 @@ var skillPromptNudge = (prompt, ctx) => process.env.WORKIT_PI_WORKER_SESSION ? n
|
|
|
20857
21039
|
// packages/workit-pi/src/launches.ts
|
|
20858
21040
|
import { mkdirSync as mkdirSync3, readFileSync as readFileSync6, readdirSync as readdirSync5, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
20859
21041
|
import os5 from "node:os";
|
|
20860
|
-
import
|
|
21042
|
+
import path25 from "node:path";
|
|
20861
21043
|
var launchDir = (root) => {
|
|
20862
21044
|
const location = resolveStore(root);
|
|
20863
|
-
return location instanceof Error ? null :
|
|
21045
|
+
return location instanceof Error ? null : path25.join(location.dir, "pi-launches");
|
|
20864
21046
|
};
|
|
20865
|
-
var fileFor = (dir, workerId) =>
|
|
21047
|
+
var fileFor = (dir, workerId) => path25.join(dir, `${workerId.replace(/[^A-Za-z0-9-]/g, "_")}.json`);
|
|
20866
21048
|
var recordLaunch = (root, attempt) => {
|
|
20867
21049
|
const dir = launchDir(root);
|
|
20868
21050
|
if (!dir)
|
|
@@ -20908,7 +21090,7 @@ var abandonedLaunches = (root) => {
|
|
|
20908
21090
|
const found = [];
|
|
20909
21091
|
for (const name of names) {
|
|
20910
21092
|
try {
|
|
20911
|
-
const attempt = JSON.parse(readFileSync6(
|
|
21093
|
+
const attempt = JSON.parse(readFileSync6(path25.join(dir, name), "utf8"));
|
|
20912
21094
|
if (typeof attempt.workerId === "string" && typeof attempt.session === "string") {
|
|
20913
21095
|
if (!alive(attempt))
|
|
20914
21096
|
found.push(attempt);
|
|
@@ -20994,7 +21176,7 @@ var roleTools = (role) => {
|
|
|
20994
21176
|
return [...readOnly, "write", "edit", ...familyTools];
|
|
20995
21177
|
return [...readOnly, ...familyTools];
|
|
20996
21178
|
};
|
|
20997
|
-
function
|
|
21179
|
+
function workerCommand2(runtime, assignment) {
|
|
20998
21180
|
return {
|
|
20999
21181
|
command: runtime.node,
|
|
21000
21182
|
args: [
|
|
@@ -21046,7 +21228,7 @@ function launchWorker(assignment, options) {
|
|
|
21046
21228
|
const taskId = options.taskId ?? null;
|
|
21047
21229
|
const workerId = options.workerId ?? null;
|
|
21048
21230
|
const sessionId = options.sessionId ?? "pi-worker-" + randomUUID4();
|
|
21049
|
-
const spec =
|
|
21231
|
+
const spec = workerCommand2({ ...options.runtime, sessionId }, assignment);
|
|
21050
21232
|
const handle = {
|
|
21051
21233
|
id: randomUUID4(),
|
|
21052
21234
|
taskId,
|
|
@@ -21082,7 +21264,7 @@ function launchWorker(assignment, options) {
|
|
|
21082
21264
|
WORKIT_PI_WORKER_ID: workerId ?? "",
|
|
21083
21265
|
WORKIT_PI_WORKER_SESSION: sessionId,
|
|
21084
21266
|
WORKIT_HOST: "pi",
|
|
21085
|
-
WORKIT_SESSION_ID: sessionId,
|
|
21267
|
+
WORKIT_SESSION_ID: options.leadSession ? childSessionId(options.leadSession, sessionId) : sessionId,
|
|
21086
21268
|
WORKIT_PI_WORKER_ROLE: assignment.role,
|
|
21087
21269
|
WORKIT_PI_WORKER_SCOPE: JSON.stringify(assignment.scope)
|
|
21088
21270
|
};
|
|
@@ -21629,6 +21811,7 @@ function extension(pi) {
|
|
|
21629
21811
|
const handle = launchSupervisedWorker(worker.data.assignment, {
|
|
21630
21812
|
runtime: runtimeFor(ctx),
|
|
21631
21813
|
binding,
|
|
21814
|
+
leadSession: ctx.sessionManager.getSessionId(),
|
|
21632
21815
|
prompt: request.prompt,
|
|
21633
21816
|
onPrepare: (pending) => {
|
|
21634
21817
|
child = pending;
|