@brainervirus/workit-opencode 8.13.0 → 8.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/plugin.js
CHANGED
|
@@ -6,7 +6,7 @@ function define(plugin) {
|
|
|
6
6
|
// packages/workit-core/src/core.ts
|
|
7
7
|
import { spawnSync as spawnSync16 } from "node:child_process";
|
|
8
8
|
import { existsSync as existsSync8, realpathSync as realpathSync5 } from "node:fs";
|
|
9
|
-
import
|
|
9
|
+
import path24 from "node:path";
|
|
10
10
|
|
|
11
11
|
// packages/workit-core/src/core/task-contract.ts
|
|
12
12
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -8992,9 +8992,9 @@ function parseAdvertisedOperation(family, input, host) {
|
|
|
8992
8992
|
});
|
|
8993
8993
|
}
|
|
8994
8994
|
// packages/workit-core/src/core/task-store.ts
|
|
8995
|
-
import { createHash as
|
|
8996
|
-
import * as
|
|
8997
|
-
import
|
|
8995
|
+
import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
|
|
8996
|
+
import * as fs8 from "node:fs";
|
|
8997
|
+
import path8 from "node:path";
|
|
8998
8998
|
|
|
8999
8999
|
// node_modules/@openclaw/fs-safe/dist/errors.js
|
|
9000
9000
|
var OPERATIONAL_CODES = new Set([
|
|
@@ -9597,8 +9597,14 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
9597
9597
|
}
|
|
9598
9598
|
}
|
|
9599
9599
|
}
|
|
9600
|
+
// packages/workit-core/src/host-session.ts
|
|
9601
|
+
import { createHash as createHash3, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
|
|
9602
|
+
import fs5 from "node:fs";
|
|
9603
|
+
import path5 from "node:path";
|
|
9604
|
+
|
|
9600
9605
|
// packages/workit-core/src/core/logger.ts
|
|
9601
9606
|
import os from "node:os";
|
|
9607
|
+
import path3 from "node:path";
|
|
9602
9608
|
var SENSITIVE_WORDS = new Set([
|
|
9603
9609
|
"token",
|
|
9604
9610
|
"secret",
|
|
@@ -9637,13 +9643,29 @@ var SENSITIVE_WORDS = new Set([
|
|
|
9637
9643
|
]);
|
|
9638
9644
|
var STACK_WORDS = new Set(["stack", "stacktrace", "trace"]);
|
|
9639
9645
|
var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9646
|
+
var resolveStateDir = () => {
|
|
9647
|
+
const override = process.env.WORKFLOW_TOOLKIT_STATE;
|
|
9648
|
+
if (override)
|
|
9649
|
+
return override;
|
|
9650
|
+
const home = os.homedir();
|
|
9651
|
+
if (process.env.XDG_STATE_HOME)
|
|
9652
|
+
return path3.join(process.env.XDG_STATE_HOME, "workit");
|
|
9653
|
+
switch (os.platform()) {
|
|
9654
|
+
case "darwin":
|
|
9655
|
+
return path3.join(home, "Library", "Application Support", "workit");
|
|
9656
|
+
case "win32":
|
|
9657
|
+
return path3.join(process.env.LOCALAPPDATA ?? path3.join(home, "AppData", "Local"), "workit");
|
|
9658
|
+
default:
|
|
9659
|
+
return path3.join(process.env.HOME ?? home, ".local", "state", "workit");
|
|
9660
|
+
}
|
|
9661
|
+
};
|
|
9640
9662
|
var homePattern = new RegExp(`${escapeRegExp(os.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
|
|
9641
9663
|
|
|
9642
9664
|
// packages/workit-core/src/store/paths.ts
|
|
9643
9665
|
import { spawnSync } from "node:child_process";
|
|
9644
9666
|
import { createHash as createHash2 } from "node:crypto";
|
|
9645
9667
|
import fs4 from "node:fs";
|
|
9646
|
-
import
|
|
9668
|
+
import path4 from "node:path";
|
|
9647
9669
|
var NOT_A_REPO = /not a git repository \(or any (of the parent directories|parent up to)/u;
|
|
9648
9670
|
var gitRun = (cwd, args) => {
|
|
9649
9671
|
const run = spawnSync("git", args, {
|
|
@@ -9685,14 +9707,14 @@ var DISCOVERY_ENV = [
|
|
|
9685
9707
|
];
|
|
9686
9708
|
var isGitDir = (dir) => {
|
|
9687
9709
|
try {
|
|
9688
|
-
return fs4.statSync(
|
|
9710
|
+
return fs4.statSync(path4.join(dir, "HEAD")).isFile() && fs4.statSync(path4.join(dir, "objects")).isDirectory() && fs4.statSync(path4.join(dir, "refs")).isDirectory();
|
|
9689
9711
|
} catch {
|
|
9690
9712
|
return false;
|
|
9691
9713
|
}
|
|
9692
9714
|
};
|
|
9693
9715
|
var reftableRefs = (common) => {
|
|
9694
9716
|
try {
|
|
9695
|
-
return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs4.readFileSync(
|
|
9717
|
+
return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs4.readFileSync(path4.join(common, "config"), "utf8"));
|
|
9696
9718
|
} catch {
|
|
9697
9719
|
return false;
|
|
9698
9720
|
}
|
|
@@ -9712,7 +9734,7 @@ function fastGit(root) {
|
|
|
9712
9734
|
if (device !== null && stat.dev !== device)
|
|
9713
9735
|
return { kind: "none" };
|
|
9714
9736
|
device = stat.dev;
|
|
9715
|
-
const dotGit =
|
|
9737
|
+
const dotGit = path4.join(dir, ".git");
|
|
9716
9738
|
let entry = null;
|
|
9717
9739
|
try {
|
|
9718
9740
|
entry = fs4.statSync(dotGit);
|
|
@@ -9733,19 +9755,19 @@ function fastGit(root) {
|
|
|
9733
9755
|
const match = /^gitdir: (.+)$/mu.exec(text);
|
|
9734
9756
|
if (!match)
|
|
9735
9757
|
return null;
|
|
9736
|
-
gitDir =
|
|
9758
|
+
gitDir = path4.resolve(dir, match[1].trim());
|
|
9737
9759
|
} else
|
|
9738
9760
|
return null;
|
|
9739
|
-
if (!fs4.existsSync(
|
|
9761
|
+
if (!fs4.existsSync(path4.join(gitDir, "HEAD")))
|
|
9740
9762
|
return null;
|
|
9741
9763
|
} else if (isGitDir(dir))
|
|
9742
9764
|
gitDir = dir;
|
|
9743
9765
|
if (gitDir) {
|
|
9744
9766
|
let common = gitDir;
|
|
9745
9767
|
try {
|
|
9746
|
-
common =
|
|
9768
|
+
common = path4.resolve(gitDir, fs4.readFileSync(path4.join(gitDir, "commondir"), "utf8").trim());
|
|
9747
9769
|
} catch {}
|
|
9748
|
-
if (!fs4.existsSync(
|
|
9770
|
+
if (!fs4.existsSync(path4.join(common, "objects")))
|
|
9749
9771
|
return null;
|
|
9750
9772
|
return {
|
|
9751
9773
|
kind: "repo",
|
|
@@ -9755,26 +9777,26 @@ function fastGit(root) {
|
|
|
9755
9777
|
reftable: reftableRefs(common)
|
|
9756
9778
|
};
|
|
9757
9779
|
}
|
|
9758
|
-
const parent =
|
|
9780
|
+
const parent = path4.dirname(dir);
|
|
9759
9781
|
if (parent === dir)
|
|
9760
9782
|
return { kind: "none" };
|
|
9761
9783
|
dir = parent;
|
|
9762
9784
|
}
|
|
9763
9785
|
}
|
|
9764
9786
|
function resolveStore(root) {
|
|
9765
|
-
root =
|
|
9787
|
+
root = path4.resolve(root);
|
|
9766
9788
|
let directory = false;
|
|
9767
9789
|
try {
|
|
9768
9790
|
directory = fs4.statSync(root).isDirectory();
|
|
9769
9791
|
} catch {}
|
|
9770
9792
|
if (!directory)
|
|
9771
|
-
return { dir:
|
|
9793
|
+
return { dir: path4.join(root, ".workit"), shared: false, top: null };
|
|
9772
9794
|
const fast = fastGit(root);
|
|
9773
9795
|
if (fast?.kind === "none")
|
|
9774
|
-
return { dir:
|
|
9796
|
+
return { dir: path4.join(root, ".workit"), shared: false, top: null };
|
|
9775
9797
|
if (fast?.kind === "repo")
|
|
9776
9798
|
return {
|
|
9777
|
-
dir:
|
|
9799
|
+
dir: path4.join(fast.common, "workit"),
|
|
9778
9800
|
shared: true,
|
|
9779
9801
|
top: fast.top,
|
|
9780
9802
|
git: { gitDir: fast.gitDir, top: fast.top, reftable: fast.reftable }
|
|
@@ -9784,20 +9806,20 @@ function resolveStore(root) {
|
|
|
9784
9806
|
if (run.ok && common) {
|
|
9785
9807
|
const top = gitRun(root, ["rev-parse", "--path-format=absolute", "--show-toplevel"]);
|
|
9786
9808
|
return {
|
|
9787
|
-
dir:
|
|
9809
|
+
dir: path4.join(real(path4.resolve(root, common)), "workit"),
|
|
9788
9810
|
shared: true,
|
|
9789
9811
|
top: top.ok && top.stdout.trim() ? real(top.stdout.trim()) : null
|
|
9790
9812
|
};
|
|
9791
9813
|
}
|
|
9792
9814
|
if (run.missing || !run.ok && NOT_A_REPO.test(run.stderr))
|
|
9793
|
-
return { dir:
|
|
9815
|
+
return { dir: path4.join(root, ".workit"), shared: false, top: null };
|
|
9794
9816
|
return new Error(`cannot locate the workit store: git rev-parse failed (${run.stderr.trim().split(`
|
|
9795
9817
|
`)[0] || "no output"}); fix the repository (git status should work here)`);
|
|
9796
9818
|
}
|
|
9797
9819
|
var rebasingBranch = (gitDir) => {
|
|
9798
9820
|
for (const state of ["rebase-merge", "rebase-apply"]) {
|
|
9799
9821
|
try {
|
|
9800
|
-
const ref = fs4.readFileSync(
|
|
9822
|
+
const ref = fs4.readFileSync(path4.join(gitDir, state, "head-name"), "utf8").trim();
|
|
9801
9823
|
const match = /^refs\/heads\/(.+)$/u.exec(ref);
|
|
9802
9824
|
if (match)
|
|
9803
9825
|
return match[1];
|
|
@@ -9812,7 +9834,7 @@ function resolveTaskKey(root, location) {
|
|
|
9812
9834
|
if (location.git && !location.git.reftable) {
|
|
9813
9835
|
let head = "";
|
|
9814
9836
|
try {
|
|
9815
|
-
head = fs4.readFileSync(
|
|
9837
|
+
head = fs4.readFileSync(path4.join(location.git.gitDir, "HEAD"), "utf8").trim();
|
|
9816
9838
|
} catch {}
|
|
9817
9839
|
const ref = /^ref: refs\/heads\/(.+)$/u.exec(head);
|
|
9818
9840
|
if (ref && ref[1] !== ".invalid")
|
|
@@ -9843,7 +9865,7 @@ function resolveTaskKey(root, location) {
|
|
|
9843
9865
|
return { key: `detached-${shortHash(real(worktree))}`, kind: "detached", branch: null };
|
|
9844
9866
|
}
|
|
9845
9867
|
function checkoutRootOf(root, location) {
|
|
9846
|
-
const resolved =
|
|
9868
|
+
const resolved = path4.resolve(root);
|
|
9847
9869
|
let canonical = resolved;
|
|
9848
9870
|
try {
|
|
9849
9871
|
canonical = fs4.realpathSync(resolved);
|
|
@@ -9874,13 +9896,13 @@ function branchRenames(location) {
|
|
|
9874
9896
|
}
|
|
9875
9897
|
for (const entry of entries)
|
|
9876
9898
|
if (entry.isDirectory())
|
|
9877
|
-
walk(
|
|
9899
|
+
walk(path4.join(dir, entry.name));
|
|
9878
9900
|
else
|
|
9879
|
-
files.push(
|
|
9901
|
+
files.push(path4.join(dir, entry.name));
|
|
9880
9902
|
};
|
|
9881
|
-
walk(
|
|
9903
|
+
walk(path4.join(path4.dirname(location.dir), "logs", "refs", "heads"));
|
|
9882
9904
|
if (location.git)
|
|
9883
|
-
files.push(
|
|
9905
|
+
files.push(path4.join(location.git.gitDir, "logs", "HEAD"));
|
|
9884
9906
|
const seen = new Set;
|
|
9885
9907
|
const renames = [];
|
|
9886
9908
|
for (const file of files) {
|
|
@@ -9915,7 +9937,7 @@ var checkoutSlug = (root) => {
|
|
|
9915
9937
|
let canonical = real(root);
|
|
9916
9938
|
if (process.platform === "win32")
|
|
9917
9939
|
canonical = canonical.toLowerCase();
|
|
9918
|
-
const base =
|
|
9940
|
+
const base = path4.basename(canonical).replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 40) || "root";
|
|
9919
9941
|
return `${base}-${shortHash(canonical)}`;
|
|
9920
9942
|
};
|
|
9921
9943
|
|
|
@@ -9947,6 +9969,89 @@ function hostSessionFromEnv(env) {
|
|
|
9947
9969
|
}
|
|
9948
9970
|
return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
|
|
9949
9971
|
}
|
|
9972
|
+
var hash = (value) => createHash3("sha256").update(value).digest("hex").slice(0, 32);
|
|
9973
|
+
var userIssuedRoot = () => path5.join(resolveStateDir(), "issued");
|
|
9974
|
+
var userTokenRoot = () => path5.join(resolveStateDir(), "issued-tokens");
|
|
9975
|
+
function migrateRepoIssued(cwd) {
|
|
9976
|
+
if (cwd === undefined)
|
|
9977
|
+
return;
|
|
9978
|
+
let old;
|
|
9979
|
+
try {
|
|
9980
|
+
const location = resolveStore(cwd);
|
|
9981
|
+
if (location instanceof Error)
|
|
9982
|
+
return;
|
|
9983
|
+
old = path5.join(location.dir, "hooks", "issued");
|
|
9984
|
+
if (!fs5.existsSync(old))
|
|
9985
|
+
return;
|
|
9986
|
+
} catch {
|
|
9987
|
+
return;
|
|
9988
|
+
}
|
|
9989
|
+
for (const lead of safeList(old)) {
|
|
9990
|
+
try {
|
|
9991
|
+
const from = path5.join(old, lead);
|
|
9992
|
+
if (!fs5.statSync(from).isDirectory())
|
|
9993
|
+
continue;
|
|
9994
|
+
const to = path5.join(userIssuedRoot(), lead);
|
|
9995
|
+
fs5.mkdirSync(to, { recursive: true, mode: 448 });
|
|
9996
|
+
for (const id of fs5.readdirSync(from)) {
|
|
9997
|
+
try {
|
|
9998
|
+
fs5.copyFileSync(path5.join(from, id), path5.join(to, id), fs5.constants.COPYFILE_EXCL);
|
|
9999
|
+
} catch (error) {
|
|
10000
|
+
if (error.code !== "EEXIST")
|
|
10001
|
+
throw error;
|
|
10002
|
+
}
|
|
10003
|
+
fs5.rmSync(path5.join(from, id), { force: true });
|
|
10004
|
+
}
|
|
10005
|
+
removeIfEmpty(from);
|
|
10006
|
+
} catch {}
|
|
10007
|
+
}
|
|
10008
|
+
removeIfEmpty(old);
|
|
10009
|
+
}
|
|
10010
|
+
var safeList = (dir) => {
|
|
10011
|
+
try {
|
|
10012
|
+
return fs5.readdirSync(dir);
|
|
10013
|
+
} catch {
|
|
10014
|
+
return [];
|
|
10015
|
+
}
|
|
10016
|
+
};
|
|
10017
|
+
var removeIfEmpty = (dir) => {
|
|
10018
|
+
try {
|
|
10019
|
+
fs5.rmdirSync(dir);
|
|
10020
|
+
} catch {}
|
|
10021
|
+
};
|
|
10022
|
+
var issuedRecord = (cwd, claimed) => {
|
|
10023
|
+
const cut = claimed.lastIndexOf(":");
|
|
10024
|
+
if (cut <= 0)
|
|
10025
|
+
return null;
|
|
10026
|
+
migrateRepoIssued(cwd);
|
|
10027
|
+
try {
|
|
10028
|
+
return fs5.readFileSync(path5.join(userIssuedRoot(), hash(claimed.slice(0, cut)), hash(claimed)), "utf8");
|
|
10029
|
+
} catch {
|
|
10030
|
+
return null;
|
|
10031
|
+
}
|
|
10032
|
+
};
|
|
10033
|
+
var TOKEN_HASH = /^([0-9a-f]{64})$/mu;
|
|
10034
|
+
var issuedToken = (claimed) => {
|
|
10035
|
+
const cut = claimed.lastIndexOf(":");
|
|
10036
|
+
if (cut <= 0)
|
|
10037
|
+
return { stored: null, leadBound: false };
|
|
10038
|
+
const dir = path5.join(userTokenRoot(), hash(claimed.slice(0, cut)));
|
|
10039
|
+
try {
|
|
10040
|
+
const text = fs5.readFileSync(path5.join(dir, hash(claimed)), "utf8");
|
|
10041
|
+
return { stored: TOKEN_HASH.exec(text)?.[1] ?? null, leadBound: true };
|
|
10042
|
+
} catch {
|
|
10043
|
+
return { stored: null, leadBound: safeList(dir).length > 0 };
|
|
10044
|
+
}
|
|
10045
|
+
};
|
|
10046
|
+
function issuedBinding(cwd, claimed) {
|
|
10047
|
+
const record = issuedRecord(cwd, claimed);
|
|
10048
|
+
const { stored, leadBound } = issuedToken(claimed);
|
|
10049
|
+
if (stored)
|
|
10050
|
+
return "token";
|
|
10051
|
+
if (record === null)
|
|
10052
|
+
return null;
|
|
10053
|
+
return leadBound ? "token" : "legacy";
|
|
10054
|
+
}
|
|
9950
10055
|
var OWN_CHILD_SHELL = new Set(["opencode", "pi"]);
|
|
9951
10056
|
var SESSION_UNSAFE = /[^A-Za-z0-9_.:@/+-]/g;
|
|
9952
10057
|
function childSessionId(lead, child) {
|
|
@@ -9957,26 +10062,26 @@ function childSessionId(lead, child) {
|
|
|
9957
10062
|
|
|
9958
10063
|
// packages/workit-core/src/core/package-root.ts
|
|
9959
10064
|
import { existsSync } from "node:fs";
|
|
9960
|
-
import
|
|
10065
|
+
import path6 from "node:path";
|
|
9961
10066
|
import { fileURLToPath } from "node:url";
|
|
9962
10067
|
var packageRoot = () => {
|
|
9963
|
-
let dir =
|
|
10068
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
9964
10069
|
while (true) {
|
|
9965
|
-
const parent =
|
|
9966
|
-
if (existsSync(
|
|
10070
|
+
const parent = path6.dirname(dir);
|
|
10071
|
+
if (existsSync(path6.join(dir, "package.json")) || parent === dir)
|
|
9967
10072
|
return dir;
|
|
9968
10073
|
dir = parent;
|
|
9969
10074
|
}
|
|
9970
10075
|
};
|
|
9971
10076
|
var assetRoot = () => {
|
|
9972
10077
|
const root = packageRoot();
|
|
9973
|
-
const assets =
|
|
10078
|
+
const assets = path6.join(root, "assets");
|
|
9974
10079
|
return existsSync(assets) ? assets : root;
|
|
9975
10080
|
};
|
|
9976
10081
|
|
|
9977
10082
|
// packages/workit-core/src/core/store-lock.ts
|
|
9978
10083
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
9979
|
-
import
|
|
10084
|
+
import fs6 from "node:fs";
|
|
9980
10085
|
import { hostname } from "node:os";
|
|
9981
10086
|
var metadataLockSchema = object({
|
|
9982
10087
|
pid: number2().int().nonnegative().safe(),
|
|
@@ -9988,8 +10093,10 @@ var metadataLockSchema = object({
|
|
|
9988
10093
|
var FOREIGN_LOCK_TTL_MS = 10 * 60000;
|
|
9989
10094
|
var UNREADABLE_LOCK_TTL_MS = 30000;
|
|
9990
10095
|
var RECLAIM_GUARD_TTL_MS = 30000;
|
|
9991
|
-
var
|
|
10096
|
+
var IN_PROCESS_LOCK_TIMEOUT_MS = 250;
|
|
10097
|
+
var defaultLockTimeoutMs = IN_PROCESS_LOCK_TIMEOUT_MS;
|
|
9992
10098
|
var defaultLockTimeout = () => defaultLockTimeoutMs;
|
|
10099
|
+
var hostAwareLockTimeout = (cliMs) => defaultLockTimeoutMs > IN_PROCESS_LOCK_TIMEOUT_MS ? cliMs : IN_PROCESS_LOCK_TIMEOUT_MS;
|
|
9993
10100
|
var parseMetadataLock = (raw) => {
|
|
9994
10101
|
let value;
|
|
9995
10102
|
try {
|
|
@@ -10018,7 +10125,7 @@ var parseProcStatStart = (stat) => {
|
|
|
10018
10125
|
var processStartOf = (pid) => {
|
|
10019
10126
|
if (process.platform === "linux") {
|
|
10020
10127
|
try {
|
|
10021
|
-
return parseProcStatStart(
|
|
10128
|
+
return parseProcStatStart(fs6.readFileSync(`/proc/${pid}/stat`, "utf8"));
|
|
10022
10129
|
} catch {
|
|
10023
10130
|
return null;
|
|
10024
10131
|
}
|
|
@@ -10039,7 +10146,7 @@ var processStartOf = (pid) => {
|
|
|
10039
10146
|
};
|
|
10040
10147
|
var readTrimmed = (file) => {
|
|
10041
10148
|
try {
|
|
10042
|
-
return
|
|
10149
|
+
return fs6.readFileSync(file, "utf8").trim() || null;
|
|
10043
10150
|
} catch {
|
|
10044
10151
|
return null;
|
|
10045
10152
|
}
|
|
@@ -10050,7 +10157,7 @@ var localLockHost = () => {
|
|
|
10050
10157
|
return cachedLockHost;
|
|
10051
10158
|
let pidns = null;
|
|
10052
10159
|
try {
|
|
10053
|
-
pidns = /\[(\d+)\]/.exec(
|
|
10160
|
+
pidns = /\[(\d+)\]/.exec(fs6.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
|
|
10054
10161
|
} catch {}
|
|
10055
10162
|
const boot = readTrimmed("/proc/sys/kernel/random/boot_id");
|
|
10056
10163
|
cachedLockHost = pidns || boot ? `${hostname()}#${pidns ?? "?"}:${boot ?? "?"}` : hostname();
|
|
@@ -10096,7 +10203,7 @@ var classifyLockOwner = (payload, ageMs, localHost = localLockHost(), options =
|
|
|
10096
10203
|
};
|
|
10097
10204
|
var ageOf = (file, nowMs) => {
|
|
10098
10205
|
try {
|
|
10099
|
-
return nowMs -
|
|
10206
|
+
return nowMs - fs6.lstatSync(file).mtimeMs;
|
|
10100
10207
|
} catch {
|
|
10101
10208
|
return null;
|
|
10102
10209
|
}
|
|
@@ -10107,7 +10214,7 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
|
|
|
10107
10214
|
if (age === null || age <= RECLAIM_GUARD_TTL_MS)
|
|
10108
10215
|
return false;
|
|
10109
10216
|
try {
|
|
10110
|
-
|
|
10217
|
+
fs6.rmdirSync(guard);
|
|
10111
10218
|
return true;
|
|
10112
10219
|
} catch {
|
|
10113
10220
|
return false;
|
|
@@ -10116,8 +10223,8 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
|
|
|
10116
10223
|
|
|
10117
10224
|
// packages/workit-core/src/store/event-log.ts
|
|
10118
10225
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
10119
|
-
import
|
|
10120
|
-
import
|
|
10226
|
+
import fs7 from "node:fs";
|
|
10227
|
+
import path7 from "node:path";
|
|
10121
10228
|
var EVENT_VERSION = 1;
|
|
10122
10229
|
|
|
10123
10230
|
class LogDamage extends Error {
|
|
@@ -10146,7 +10253,7 @@ var parseEvent = (line, file, offset) => {
|
|
|
10146
10253
|
function readLog(file, from = 0, expectSeq = null) {
|
|
10147
10254
|
let fd;
|
|
10148
10255
|
try {
|
|
10149
|
-
fd =
|
|
10256
|
+
fd = fs7.openSync(file, "r");
|
|
10150
10257
|
} catch (error) {
|
|
10151
10258
|
if (error.code === "ENOENT")
|
|
10152
10259
|
return null;
|
|
@@ -10155,13 +10262,13 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
10155
10262
|
let bytes;
|
|
10156
10263
|
let size;
|
|
10157
10264
|
try {
|
|
10158
|
-
size =
|
|
10265
|
+
size = fs7.fstatSync(fd).size;
|
|
10159
10266
|
if (from > size)
|
|
10160
10267
|
throw new LogDamage(`${file}: shorter than its snapshot`);
|
|
10161
10268
|
bytes = Buffer.alloc(size - from);
|
|
10162
10269
|
let read = 0;
|
|
10163
10270
|
while (read < bytes.length) {
|
|
10164
|
-
const got =
|
|
10271
|
+
const got = fs7.readSync(fd, bytes, read, bytes.length - read, from + read);
|
|
10165
10272
|
if (got === 0)
|
|
10166
10273
|
break;
|
|
10167
10274
|
read += got;
|
|
@@ -10169,7 +10276,7 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
10169
10276
|
bytes = bytes.subarray(0, read);
|
|
10170
10277
|
size = from + read;
|
|
10171
10278
|
} finally {
|
|
10172
|
-
|
|
10279
|
+
fs7.closeSync(fd);
|
|
10173
10280
|
}
|
|
10174
10281
|
const events = [];
|
|
10175
10282
|
let start = 0;
|
|
@@ -10194,11 +10301,11 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
10194
10301
|
}
|
|
10195
10302
|
var fsyncDirectory = (dir) => {
|
|
10196
10303
|
try {
|
|
10197
|
-
const fd =
|
|
10304
|
+
const fd = fs7.openSync(dir, "r");
|
|
10198
10305
|
try {
|
|
10199
|
-
|
|
10306
|
+
fs7.fsyncSync(fd);
|
|
10200
10307
|
} finally {
|
|
10201
|
-
|
|
10308
|
+
fs7.closeSync(fd);
|
|
10202
10309
|
}
|
|
10203
10310
|
} catch (error) {
|
|
10204
10311
|
if (process.platform !== "win32")
|
|
@@ -10208,46 +10315,46 @@ var fsyncDirectory = (dir) => {
|
|
|
10208
10315
|
function appendEvent(file, event, validEnd) {
|
|
10209
10316
|
const line = Buffer.from(`${JSON.stringify(event)}
|
|
10210
10317
|
`, "utf8");
|
|
10211
|
-
const created = !
|
|
10212
|
-
if (!created &&
|
|
10213
|
-
const cut =
|
|
10318
|
+
const created = !fs7.existsSync(file);
|
|
10319
|
+
if (!created && fs7.statSync(file).size !== validEnd) {
|
|
10320
|
+
const cut = fs7.openSync(file, "r+");
|
|
10214
10321
|
try {
|
|
10215
|
-
|
|
10216
|
-
|
|
10322
|
+
fs7.ftruncateSync(cut, validEnd);
|
|
10323
|
+
fs7.fsyncSync(cut);
|
|
10217
10324
|
} finally {
|
|
10218
|
-
|
|
10325
|
+
fs7.closeSync(cut);
|
|
10219
10326
|
}
|
|
10220
10327
|
}
|
|
10221
|
-
const fd =
|
|
10328
|
+
const fd = fs7.openSync(file, "a", 384);
|
|
10222
10329
|
try {
|
|
10223
10330
|
let written = 0;
|
|
10224
10331
|
while (written < line.length)
|
|
10225
|
-
written +=
|
|
10226
|
-
|
|
10332
|
+
written += fs7.writeSync(fd, line, written, line.length - written);
|
|
10333
|
+
fs7.fsyncSync(fd);
|
|
10227
10334
|
} finally {
|
|
10228
|
-
|
|
10335
|
+
fs7.closeSync(fd);
|
|
10229
10336
|
}
|
|
10230
10337
|
if (created)
|
|
10231
|
-
fsyncDirectory(
|
|
10338
|
+
fsyncDirectory(path7.dirname(file));
|
|
10232
10339
|
return validEnd;
|
|
10233
10340
|
}
|
|
10234
10341
|
function replaceFile(file, bytes, durable) {
|
|
10235
10342
|
const temporary = `${file}.${process.pid}.${randomUUID2()}.tmp`;
|
|
10236
10343
|
try {
|
|
10237
|
-
const fd =
|
|
10344
|
+
const fd = fs7.openSync(temporary, "wx", 384);
|
|
10238
10345
|
try {
|
|
10239
|
-
|
|
10346
|
+
fs7.writeSync(fd, bytes);
|
|
10240
10347
|
if (durable)
|
|
10241
|
-
|
|
10348
|
+
fs7.fsyncSync(fd);
|
|
10242
10349
|
} finally {
|
|
10243
|
-
|
|
10350
|
+
fs7.closeSync(fd);
|
|
10244
10351
|
}
|
|
10245
10352
|
renameWithRetry(temporary, file);
|
|
10246
10353
|
if (durable)
|
|
10247
|
-
fsyncDirectory(
|
|
10354
|
+
fsyncDirectory(path7.dirname(file));
|
|
10248
10355
|
} catch (error) {
|
|
10249
10356
|
try {
|
|
10250
|
-
|
|
10357
|
+
fs7.unlinkSync(temporary);
|
|
10251
10358
|
} catch {}
|
|
10252
10359
|
throw error;
|
|
10253
10360
|
}
|
|
@@ -10272,7 +10379,7 @@ var retryTransient = (run) => {
|
|
|
10272
10379
|
}
|
|
10273
10380
|
}
|
|
10274
10381
|
};
|
|
10275
|
-
var renameWithRetry = (from, to) => retryTransient(() =>
|
|
10382
|
+
var renameWithRetry = (from, to) => retryTransient(() => fs7.renameSync(from, to));
|
|
10276
10383
|
|
|
10277
10384
|
// packages/workit-core/src/store/patch.ts
|
|
10278
10385
|
var isPlainObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -10385,7 +10492,7 @@ var isPatch = (value) => Array.isArray(value) && value.every((op) => {
|
|
|
10385
10492
|
});
|
|
10386
10493
|
|
|
10387
10494
|
// packages/workit-core/src/store/reduce.ts
|
|
10388
|
-
import { createHash as
|
|
10495
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
10389
10496
|
var OPENING_TYPES = new Set([
|
|
10390
10497
|
"task.opened",
|
|
10391
10498
|
"task.imported",
|
|
@@ -10414,7 +10521,7 @@ var isObject4 = (value) => typeof value === "object" && value !== null && !Array
|
|
|
10414
10521
|
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");
|
|
10415
10522
|
var BLOB_REF = "$blob";
|
|
10416
10523
|
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;
|
|
10417
|
-
var blobDigest = (value) =>
|
|
10524
|
+
var blobDigest = (value) => createHash4("sha256").update(JSON.stringify(value)).digest("hex");
|
|
10418
10525
|
var toBlob = (value, blobs) => {
|
|
10419
10526
|
if (!isObject4(value))
|
|
10420
10527
|
return value;
|
|
@@ -10569,7 +10676,7 @@ var indexEntry = (task, key, legacy, file) => {
|
|
|
10569
10676
|
};
|
|
10570
10677
|
var fileSignature = (file) => {
|
|
10571
10678
|
try {
|
|
10572
|
-
const stat =
|
|
10679
|
+
const stat = fs8.statSync(file, { bigint: true });
|
|
10573
10680
|
return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
|
|
10574
10681
|
} catch {
|
|
10575
10682
|
return null;
|
|
@@ -10587,7 +10694,7 @@ var cachedRuntimeVersion = null;
|
|
|
10587
10694
|
var runtimeVersion = () => {
|
|
10588
10695
|
if (cachedRuntimeVersion === null) {
|
|
10589
10696
|
try {
|
|
10590
|
-
const pkg = JSON.parse(
|
|
10697
|
+
const pkg = JSON.parse(fs8.readFileSync(path8.join(packageRoot(), "package.json"), "utf8"));
|
|
10591
10698
|
cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
|
|
10592
10699
|
} catch {
|
|
10593
10700
|
cachedRuntimeVersion = "0.0.0";
|
|
@@ -10613,14 +10720,14 @@ var isNewerVersion = (candidate, current) => {
|
|
|
10613
10720
|
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);
|
|
10614
10721
|
var readText = (file) => {
|
|
10615
10722
|
try {
|
|
10616
|
-
return
|
|
10723
|
+
return fs8.readFileSync(file, "utf8");
|
|
10617
10724
|
} catch {
|
|
10618
10725
|
return "";
|
|
10619
10726
|
}
|
|
10620
10727
|
};
|
|
10621
10728
|
var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
|
|
10622
10729
|
var branchStart = (location, branch, aliases) => {
|
|
10623
|
-
const first = readText(
|
|
10730
|
+
const first = readText(path8.join(path8.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
|
|
10624
10731
|
`, 1)[0];
|
|
10625
10732
|
const created = REFLOG_LINE.exec(first);
|
|
10626
10733
|
const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
|
|
@@ -10630,7 +10737,7 @@ var branchStart = (location, branch, aliases) => {
|
|
|
10630
10737
|
return start;
|
|
10631
10738
|
if (!location.git)
|
|
10632
10739
|
return null;
|
|
10633
|
-
for (const line of readText(
|
|
10740
|
+
for (const line of readText(path8.join(location.git.gitDir, "logs", "HEAD")).split(`
|
|
10634
10741
|
`)) {
|
|
10635
10742
|
const entry = REFLOG_LINE.exec(line);
|
|
10636
10743
|
const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
|
|
@@ -10640,23 +10747,23 @@ var branchStart = (location, branch, aliases) => {
|
|
|
10640
10747
|
}
|
|
10641
10748
|
return null;
|
|
10642
10749
|
};
|
|
10643
|
-
var refExists = (common, ref) =>
|
|
10750
|
+
var refExists = (common, ref) => fs8.existsSync(path8.join(common, ref)) || readText(path8.join(common, "packed-refs")).includes(` ${ref}
|
|
10644
10751
|
`);
|
|
10645
10752
|
var origins = new Map;
|
|
10646
10753
|
var sameDirectoryIdentity = (left, right) => {
|
|
10647
|
-
if (!
|
|
10754
|
+
if (!path8.isAbsolute(left) || !path8.isAbsolute(right))
|
|
10648
10755
|
return false;
|
|
10649
|
-
const normalizedLeft =
|
|
10650
|
-
const normalizedRight =
|
|
10756
|
+
const normalizedLeft = path8.resolve(left);
|
|
10757
|
+
const normalizedRight = path8.resolve(right);
|
|
10651
10758
|
try {
|
|
10652
|
-
const a =
|
|
10653
|
-
const b =
|
|
10759
|
+
const a = fs8.statSync(normalizedLeft, { bigint: true });
|
|
10760
|
+
const b = fs8.statSync(normalizedRight, { bigint: true });
|
|
10654
10761
|
if (!a.isDirectory() || !b.isDirectory())
|
|
10655
10762
|
return false;
|
|
10656
10763
|
if (process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight)
|
|
10657
10764
|
return true;
|
|
10658
|
-
const canonicalLeft =
|
|
10659
|
-
const canonicalRight =
|
|
10765
|
+
const canonicalLeft = fs8.realpathSync(normalizedLeft);
|
|
10766
|
+
const canonicalRight = fs8.realpathSync(normalizedRight);
|
|
10660
10767
|
if (process.platform === "win32" ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase() : canonicalLeft === canonicalRight)
|
|
10661
10768
|
return true;
|
|
10662
10769
|
return a.dev === b.dev && a.ino !== 0n && a.ino === b.ino;
|
|
@@ -10693,8 +10800,8 @@ class TaskStore {
|
|
|
10693
10800
|
migrationChecked = false;
|
|
10694
10801
|
compactDue = new Set;
|
|
10695
10802
|
constructor(root, options = {}) {
|
|
10696
|
-
const resolved =
|
|
10697
|
-
this.origin =
|
|
10803
|
+
const resolved = path8.resolve(root);
|
|
10804
|
+
this.origin = fs8.existsSync(resolved) ? fs8.realpathSync(resolved) : resolved;
|
|
10698
10805
|
this.located = resolveStore(this.origin);
|
|
10699
10806
|
this.root = checkoutRootOf(this.origin, this.located);
|
|
10700
10807
|
this.lockTimeoutMs = options.lockTimeoutMs ?? defaultLockTimeout();
|
|
@@ -10702,7 +10809,7 @@ class TaskStore {
|
|
|
10702
10809
|
this.migrateOnRead = options.migrateOnRead;
|
|
10703
10810
|
}
|
|
10704
10811
|
location() {
|
|
10705
|
-
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared &&
|
|
10812
|
+
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs8.existsSync(path8.join(this.root, ".git"))) {
|
|
10706
10813
|
this.located = undefined;
|
|
10707
10814
|
this.migrationChecked = false;
|
|
10708
10815
|
}
|
|
@@ -10766,7 +10873,7 @@ class TaskStore {
|
|
|
10766
10873
|
const tasksDir = this.paths().tasks;
|
|
10767
10874
|
let names;
|
|
10768
10875
|
try {
|
|
10769
|
-
names =
|
|
10876
|
+
names = fs8.existsSync(tasksDir) ? fs8.readdirSync(tasksDir).filter((name) => validId(name)) : [];
|
|
10770
10877
|
} catch (error) {
|
|
10771
10878
|
return failure2("storage_error", `unable to list tasks: ${String(error)}`, {
|
|
10772
10879
|
path: tasksDir
|
|
@@ -10807,7 +10914,7 @@ class TaskStore {
|
|
|
10807
10914
|
const file = this.paths().workspace;
|
|
10808
10915
|
let bytes;
|
|
10809
10916
|
try {
|
|
10810
|
-
bytes = retryTransient(() =>
|
|
10917
|
+
bytes = retryTransient(() => fs8.readFileSync(file, "utf8"));
|
|
10811
10918
|
} catch (error) {
|
|
10812
10919
|
if (error.code === "ENOENT")
|
|
10813
10920
|
return success(null, null, null);
|
|
@@ -10858,7 +10965,7 @@ class TaskStore {
|
|
|
10858
10965
|
const moved = this.bindLocked(again.data.id, key, input.now);
|
|
10859
10966
|
return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
|
|
10860
10967
|
}
|
|
10861
|
-
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" :
|
|
10968
|
+
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path8.basename(this.root) || this.root;
|
|
10862
10969
|
const intent = {
|
|
10863
10970
|
objective: input.objective?.trim() || `Work on ${label}`,
|
|
10864
10971
|
scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
|
|
@@ -10918,14 +11025,14 @@ class TaskStore {
|
|
|
10918
11025
|
const branch = key.ok ? key.data.branch : null;
|
|
10919
11026
|
if (!branch || !location.ok)
|
|
10920
11027
|
return success(null, null, null);
|
|
10921
|
-
const common =
|
|
10922
|
-
const heads =
|
|
10923
|
-
const headLog = location.data.git ?
|
|
11028
|
+
const common = path8.dirname(location.data.dir);
|
|
11029
|
+
const heads = path8.join(common, "logs", "refs", "heads");
|
|
11030
|
+
const headLog = location.data.git ? path8.join(location.data.git.gitDir, "logs", "HEAD") : "";
|
|
10924
11031
|
const signatures = [
|
|
10925
|
-
|
|
11032
|
+
path8.join(heads, branch),
|
|
10926
11033
|
headLog,
|
|
10927
11034
|
heads,
|
|
10928
|
-
|
|
11035
|
+
path8.join(common, "packed-refs")
|
|
10929
11036
|
].map((file) => fileSignature(file) ?? "-");
|
|
10930
11037
|
const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
|
|
10931
11038
|
let resolved = origins.get(slot);
|
|
@@ -10937,7 +11044,7 @@ class TaskStore {
|
|
|
10937
11044
|
aliases.push(rename.from);
|
|
10938
11045
|
const start = branchStart(location.data, branch, aliases);
|
|
10939
11046
|
const slash = start?.indexOf("/") ?? -1;
|
|
10940
|
-
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(
|
|
11047
|
+
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path8.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
|
|
10941
11048
|
const name = start && (remote ? start.slice(slash + 1) : start);
|
|
10942
11049
|
resolved = {
|
|
10943
11050
|
signature: signatures.join("|"),
|
|
@@ -11175,13 +11282,13 @@ class TaskStore {
|
|
|
11175
11282
|
if (!ready.ok)
|
|
11176
11283
|
return ready;
|
|
11177
11284
|
const dirs = this.paths();
|
|
11178
|
-
const recovery =
|
|
11179
|
-
if (
|
|
11285
|
+
const recovery = path8.join(this.root, ".workit", "recovery");
|
|
11286
|
+
if (fs8.existsSync(recovery)) {
|
|
11180
11287
|
let files = 0;
|
|
11181
11288
|
let bytes = 0;
|
|
11182
11289
|
try {
|
|
11183
|
-
for (const name of
|
|
11184
|
-
const stat =
|
|
11290
|
+
for (const name of fs8.readdirSync(recovery)) {
|
|
11291
|
+
const stat = fs8.lstatSync(path8.join(recovery, name));
|
|
11185
11292
|
if (!stat.isFile())
|
|
11186
11293
|
continue;
|
|
11187
11294
|
files += 1;
|
|
@@ -11190,17 +11297,17 @@ class TaskStore {
|
|
|
11190
11297
|
} catch {}
|
|
11191
11298
|
const remove = options.pruneRecovery === true && !dryRun;
|
|
11192
11299
|
if (remove)
|
|
11193
|
-
|
|
11300
|
+
fs8.rmSync(recovery, { recursive: true, force: true });
|
|
11194
11301
|
report.legacyRecovery = { path: recovery, files, bytes, removed: remove };
|
|
11195
11302
|
}
|
|
11196
|
-
if (!
|
|
11303
|
+
if (!fs8.existsSync(dirs.tasks))
|
|
11197
11304
|
return success(null, null, report);
|
|
11198
|
-
const names =
|
|
11305
|
+
const names = fs8.readdirSync(dirs.tasks).filter((name) => validId(name));
|
|
11199
11306
|
for (const name of names) {
|
|
11200
11307
|
const file = this.eventsPath(name);
|
|
11201
11308
|
let size = 0;
|
|
11202
11309
|
try {
|
|
11203
|
-
size =
|
|
11310
|
+
size = fs8.statSync(file).size;
|
|
11204
11311
|
} catch {
|
|
11205
11312
|
continue;
|
|
11206
11313
|
}
|
|
@@ -11228,16 +11335,16 @@ class TaskStore {
|
|
|
11228
11335
|
const references = new Set;
|
|
11229
11336
|
for (const name of names)
|
|
11230
11337
|
try {
|
|
11231
|
-
for (const match of
|
|
11338
|
+
for (const match of fs8.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
|
|
11232
11339
|
references.add(match[1]);
|
|
11233
11340
|
} catch {}
|
|
11234
|
-
if (
|
|
11235
|
-
for (const name of
|
|
11341
|
+
if (fs8.existsSync(dirs.blobs))
|
|
11342
|
+
for (const name of fs8.readdirSync(dirs.blobs)) {
|
|
11236
11343
|
const digest = name.replace(/\.json$/, "");
|
|
11237
|
-
const file =
|
|
11344
|
+
const file = path8.join(dirs.blobs, name);
|
|
11238
11345
|
let stat;
|
|
11239
11346
|
try {
|
|
11240
|
-
stat =
|
|
11347
|
+
stat = fs8.lstatSync(file);
|
|
11241
11348
|
} catch {
|
|
11242
11349
|
continue;
|
|
11243
11350
|
}
|
|
@@ -11248,11 +11355,11 @@ class TaskStore {
|
|
|
11248
11355
|
report.blobs.removed += 1;
|
|
11249
11356
|
report.blobs.removedBytes += stat.size;
|
|
11250
11357
|
if (!dryRun)
|
|
11251
|
-
|
|
11358
|
+
fs8.rmSync(file, { force: true });
|
|
11252
11359
|
}
|
|
11253
11360
|
return success(null, null, null);
|
|
11254
11361
|
};
|
|
11255
|
-
const swept = dryRun ? sweep() : this.withLock(
|
|
11362
|
+
const swept = dryRun ? sweep() : this.withLock(path8.join(dirs.dir, "gc.lock"), sweep);
|
|
11256
11363
|
if (!swept.ok)
|
|
11257
11364
|
return swept;
|
|
11258
11365
|
const temporaryDirs = [
|
|
@@ -11260,17 +11367,17 @@ class TaskStore {
|
|
|
11260
11367
|
dirs.tasks,
|
|
11261
11368
|
dirs.blobs,
|
|
11262
11369
|
dirs.checkout,
|
|
11263
|
-
...names.map((name) =>
|
|
11370
|
+
...names.map((name) => path8.join(dirs.tasks, name))
|
|
11264
11371
|
];
|
|
11265
11372
|
for (const directory of temporaryDirs) {
|
|
11266
|
-
if (!
|
|
11373
|
+
if (!fs8.existsSync(directory))
|
|
11267
11374
|
continue;
|
|
11268
|
-
for (const name of
|
|
11375
|
+
for (const name of fs8.readdirSync(directory)) {
|
|
11269
11376
|
if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
|
|
11270
11377
|
continue;
|
|
11271
|
-
const file =
|
|
11378
|
+
const file = path8.join(directory, name);
|
|
11272
11379
|
try {
|
|
11273
|
-
const stat =
|
|
11380
|
+
const stat = fs8.lstatSync(file);
|
|
11274
11381
|
if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
|
|
11275
11382
|
continue;
|
|
11276
11383
|
} catch {
|
|
@@ -11278,37 +11385,37 @@ class TaskStore {
|
|
|
11278
11385
|
}
|
|
11279
11386
|
report.temporary.removed += 1;
|
|
11280
11387
|
if (!dryRun)
|
|
11281
|
-
|
|
11388
|
+
fs8.rmSync(file, { force: true });
|
|
11282
11389
|
}
|
|
11283
11390
|
}
|
|
11284
11391
|
return success(null, null, report);
|
|
11285
11392
|
}
|
|
11286
11393
|
paths() {
|
|
11287
11394
|
const location = this.location();
|
|
11288
|
-
const dir = location.ok ? location.data.dir :
|
|
11289
|
-
const checkout =
|
|
11395
|
+
const dir = location.ok ? location.data.dir : path8.join(this.root, ".workit");
|
|
11396
|
+
const checkout = path8.join(dir, "checkouts", checkoutSlug(this.root));
|
|
11290
11397
|
return {
|
|
11291
11398
|
dir,
|
|
11292
11399
|
shared: location.ok ? location.data.shared : false,
|
|
11293
|
-
marker:
|
|
11400
|
+
marker: path8.join(dir, "store.json"),
|
|
11294
11401
|
checkout,
|
|
11295
|
-
workspace:
|
|
11296
|
-
checkoutLock:
|
|
11297
|
-
tasks:
|
|
11298
|
-
index:
|
|
11299
|
-
blobs:
|
|
11300
|
-
legacy:
|
|
11301
|
-
v2:
|
|
11402
|
+
workspace: path8.join(checkout, "workspace.json"),
|
|
11403
|
+
checkoutLock: path8.join(checkout, "metadata.lock"),
|
|
11404
|
+
tasks: path8.join(dir, "tasks"),
|
|
11405
|
+
index: path8.join(dir, "task-index.json"),
|
|
11406
|
+
blobs: path8.join(dir, "blobs", "candidates"),
|
|
11407
|
+
legacy: path8.join(dir, "legacy", checkoutSlug(this.root), "v2"),
|
|
11408
|
+
v2: path8.join(this.root, ".workit")
|
|
11302
11409
|
};
|
|
11303
11410
|
}
|
|
11304
11411
|
eventsPath(taskId) {
|
|
11305
|
-
return
|
|
11412
|
+
return path8.join(this.paths().tasks, taskId, "events.jsonl");
|
|
11306
11413
|
}
|
|
11307
11414
|
snapshotPath(taskId) {
|
|
11308
|
-
return
|
|
11415
|
+
return path8.join(this.paths().tasks, taskId, "snapshot.json");
|
|
11309
11416
|
}
|
|
11310
11417
|
taskLockPath(taskId) {
|
|
11311
|
-
return
|
|
11418
|
+
return path8.join(this.paths().tasks, taskId, "lock");
|
|
11312
11419
|
}
|
|
11313
11420
|
ready(write = false) {
|
|
11314
11421
|
const location = this.location();
|
|
@@ -11318,7 +11425,7 @@ class TaskStore {
|
|
|
11318
11425
|
return success(null, null, null);
|
|
11319
11426
|
const dirs = this.paths();
|
|
11320
11427
|
try {
|
|
11321
|
-
const marker = JSON.parse(
|
|
11428
|
+
const marker = JSON.parse(fs8.readFileSync(dirs.marker, "utf8"));
|
|
11322
11429
|
if (typeof marker.version === "number" && marker.version > STORE_FORMAT)
|
|
11323
11430
|
return failure2("recovery_required", `the workit store at ${dirs.dir} has format ${marker.version}; upgrade Workit to use it`, { path: dirs.marker });
|
|
11324
11431
|
} catch {}
|
|
@@ -11347,21 +11454,21 @@ class TaskStore {
|
|
|
11347
11454
|
return {
|
|
11348
11455
|
read: (digest) => {
|
|
11349
11456
|
try {
|
|
11350
|
-
return JSON.parse(
|
|
11457
|
+
return JSON.parse(fs8.readFileSync(path8.join(dir, `${digest}.json`), "utf8"));
|
|
11351
11458
|
} catch (error) {
|
|
11352
11459
|
throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
|
|
11353
11460
|
}
|
|
11354
11461
|
},
|
|
11355
11462
|
write: (digest, value) => {
|
|
11356
|
-
const file =
|
|
11357
|
-
if (
|
|
11463
|
+
const file = path8.join(dir, `${digest}.json`);
|
|
11464
|
+
if (fs8.existsSync(file)) {
|
|
11358
11465
|
const stamp = new Date;
|
|
11359
11466
|
try {
|
|
11360
|
-
|
|
11467
|
+
fs8.utimesSync(file, stamp, stamp);
|
|
11361
11468
|
return;
|
|
11362
11469
|
} catch {}
|
|
11363
11470
|
}
|
|
11364
|
-
|
|
11471
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
11365
11472
|
replaceFile(file, JSON.stringify(value), true);
|
|
11366
11473
|
}
|
|
11367
11474
|
};
|
|
@@ -11443,7 +11550,7 @@ class TaskStore {
|
|
|
11443
11550
|
}
|
|
11444
11551
|
readSnapshot(taskId) {
|
|
11445
11552
|
try {
|
|
11446
|
-
const value = JSON.parse(
|
|
11553
|
+
const value = JSON.parse(fs8.readFileSync(this.snapshotPath(taskId), "utf8"));
|
|
11447
11554
|
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))
|
|
11448
11555
|
return null;
|
|
11449
11556
|
return value;
|
|
@@ -11545,8 +11652,8 @@ class TaskStore {
|
|
|
11545
11652
|
}
|
|
11546
11653
|
}
|
|
11547
11654
|
openTask(record, type, key, legacy = null) {
|
|
11548
|
-
const dir =
|
|
11549
|
-
|
|
11655
|
+
const dir = path8.join(this.paths().tasks, record.id);
|
|
11656
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
11550
11657
|
const blobs = this.blobStore();
|
|
11551
11658
|
const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
|
|
11552
11659
|
const event = this.event(record.id, 1, type, {
|
|
@@ -11598,11 +11705,11 @@ class TaskStore {
|
|
|
11598
11705
|
} catch {}
|
|
11599
11706
|
if (!latest || latest.events[0]?.id !== last.id)
|
|
11600
11707
|
return failure2("busy", "the task log changed during compaction; retry", { taskId });
|
|
11601
|
-
const appended =
|
|
11708
|
+
const appended = fs8.readFileSync(file).subarray(log.end, latest.end);
|
|
11602
11709
|
const bytes = Buffer.concat([Buffer.from(prefix, "utf8"), appended]);
|
|
11603
11710
|
replaceFile(file, bytes.toString("utf8"), true);
|
|
11604
11711
|
try {
|
|
11605
|
-
|
|
11712
|
+
fs8.rmSync(this.snapshotPath(taskId), { force: true });
|
|
11606
11713
|
} catch {}
|
|
11607
11714
|
const reloaded = this.load(taskId);
|
|
11608
11715
|
if (reloaded.ok && reloaded.data)
|
|
@@ -11615,7 +11722,7 @@ class TaskStore {
|
|
|
11615
11722
|
}
|
|
11616
11723
|
countEvents(file) {
|
|
11617
11724
|
try {
|
|
11618
|
-
const bytes =
|
|
11725
|
+
const bytes = fs8.readFileSync(file);
|
|
11619
11726
|
let count = 0;
|
|
11620
11727
|
for (let index = bytes.indexOf(10);index >= 0; index = bytes.indexOf(10, index + 1))
|
|
11621
11728
|
count += 1;
|
|
@@ -11637,8 +11744,8 @@ class TaskStore {
|
|
|
11637
11744
|
return bound.ok ? success(null, null, bound.data[0] ?? null) : bound;
|
|
11638
11745
|
}
|
|
11639
11746
|
withKeyLock(key, operation) {
|
|
11640
|
-
const digest =
|
|
11641
|
-
return this.withLock(
|
|
11747
|
+
const digest = createHash5("sha256").update(key).digest("hex").slice(0, 32);
|
|
11748
|
+
return this.withLock(path8.join(this.paths().dir, "keys", `${digest}.lock`), operation);
|
|
11642
11749
|
}
|
|
11643
11750
|
bindLocked(taskId, key, timestamp) {
|
|
11644
11751
|
const current = this.readWorkspace();
|
|
@@ -11779,7 +11886,7 @@ class TaskStore {
|
|
|
11779
11886
|
writeWorkspace(record) {
|
|
11780
11887
|
const file = this.paths().workspace;
|
|
11781
11888
|
try {
|
|
11782
|
-
|
|
11889
|
+
fs8.mkdirSync(path8.dirname(file), { recursive: true });
|
|
11783
11890
|
replaceFile(file, `${canonicalJson(record)}
|
|
11784
11891
|
`, true);
|
|
11785
11892
|
return success(null, null, null);
|
|
@@ -11789,7 +11896,7 @@ class TaskStore {
|
|
|
11789
11896
|
}
|
|
11790
11897
|
readIndex() {
|
|
11791
11898
|
try {
|
|
11792
|
-
const value = JSON.parse(
|
|
11899
|
+
const value = JSON.parse(fs8.readFileSync(this.paths().index, "utf8"));
|
|
11793
11900
|
if (!isObject5(value) || value.version !== INDEX_VERSION || !isObject5(value.tasks))
|
|
11794
11901
|
return {};
|
|
11795
11902
|
const tasks = {};
|
|
@@ -11803,7 +11910,7 @@ class TaskStore {
|
|
|
11803
11910
|
}
|
|
11804
11911
|
writeIndex(entries) {
|
|
11805
11912
|
try {
|
|
11806
|
-
|
|
11913
|
+
fs8.mkdirSync(this.paths().tasks, { recursive: true });
|
|
11807
11914
|
replaceFile(this.paths().index, JSON.stringify({
|
|
11808
11915
|
version: INDEX_VERSION,
|
|
11809
11916
|
tasks: Object.fromEntries(entries.map((entry) => [entry.id, entry]))
|
|
@@ -11857,23 +11964,23 @@ class TaskStore {
|
|
|
11857
11964
|
return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
|
|
11858
11965
|
}
|
|
11859
11966
|
v2Dirs() {
|
|
11860
|
-
return [...new Set([this.root, this.origin])].map((dir) =>
|
|
11967
|
+
return [...new Set([this.root, this.origin])].map((dir) => path8.join(dir, ".workit"));
|
|
11861
11968
|
}
|
|
11862
11969
|
pendingMigrations() {
|
|
11863
11970
|
return this.v2Dirs().filter((dir) => {
|
|
11864
|
-
const raw = this.readV2Workspace(
|
|
11971
|
+
const raw = this.readV2Workspace(path8.join(dir, "workspace.json"));
|
|
11865
11972
|
return raw !== null && raw !== TOMBSTONE;
|
|
11866
11973
|
});
|
|
11867
11974
|
}
|
|
11868
11975
|
migrateV2(v2) {
|
|
11869
11976
|
const dirs = this.paths();
|
|
11870
|
-
const v2Workspace =
|
|
11871
|
-
const v2Tasks =
|
|
11872
|
-
const legacy =
|
|
11977
|
+
const v2Workspace = path8.join(v2, "workspace.json");
|
|
11978
|
+
const v2Tasks = path8.join(v2, "tasks");
|
|
11979
|
+
const legacy = path8.join(dirs.dir, "legacy", checkoutSlug(path8.dirname(v2)), "v2");
|
|
11873
11980
|
const raw = this.readV2Workspace(v2Workspace);
|
|
11874
11981
|
if (raw === null || raw === TOMBSTONE)
|
|
11875
11982
|
return success(null, null, null);
|
|
11876
|
-
return this.withLock(dirs.checkoutLock, () => this.withLock(
|
|
11983
|
+
return this.withLock(dirs.checkoutLock, () => this.withLock(path8.join(v2, "metadata.lock"), () => {
|
|
11877
11984
|
const workspace = this.readV2Workspace(v2Workspace);
|
|
11878
11985
|
if (workspace === null || workspace === TOMBSTONE)
|
|
11879
11986
|
return success(null, null, null);
|
|
@@ -11882,7 +11989,7 @@ class TaskStore {
|
|
|
11882
11989
|
return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
|
|
11883
11990
|
let files = [];
|
|
11884
11991
|
try {
|
|
11885
|
-
files =
|
|
11992
|
+
files = fs8.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path8.join(v2Tasks, name)));
|
|
11886
11993
|
} catch {}
|
|
11887
11994
|
const current = this.readWorkspace();
|
|
11888
11995
|
if (!current.ok)
|
|
@@ -11900,22 +12007,22 @@ class TaskStore {
|
|
|
11900
12007
|
let skipped = 0;
|
|
11901
12008
|
const done = new Map;
|
|
11902
12009
|
try {
|
|
11903
|
-
|
|
12010
|
+
fs8.mkdirSync(path8.join(legacy, "tasks"), { recursive: true });
|
|
11904
12011
|
for (const name of ["workspace.json", "index.json"])
|
|
11905
|
-
if (
|
|
11906
|
-
|
|
12012
|
+
if (fs8.existsSync(path8.join(v2, name)))
|
|
12013
|
+
fs8.copyFileSync(path8.join(v2, name), path8.join(legacy, name));
|
|
11907
12014
|
for (const name of files) {
|
|
11908
12015
|
const id = name.slice(0, -5);
|
|
11909
|
-
const file =
|
|
11910
|
-
const bytes =
|
|
12016
|
+
const file = path8.join(v2Tasks, name);
|
|
12017
|
+
const bytes = fs8.readFileSync(file, "utf8");
|
|
11911
12018
|
const digest = sha256(bytes);
|
|
11912
|
-
|
|
12019
|
+
fs8.writeFileSync(path8.join(legacy, "tasks", name), bytes);
|
|
11913
12020
|
const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
|
|
11914
12021
|
if (!parsed.ok || parsed.data.id !== id)
|
|
11915
12022
|
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 });
|
|
11916
12023
|
const record = { ...parsed.data, workspaceId: target.id };
|
|
11917
|
-
const origin = { path:
|
|
11918
|
-
if (!
|
|
12024
|
+
const origin = { path: path8.join(legacy, "tasks", name), digest };
|
|
12025
|
+
if (!fs8.existsSync(this.eventsPath(id))) {
|
|
11919
12026
|
this.openTask(record, "migrated.from_v2", null, origin);
|
|
11920
12027
|
migrated += 1;
|
|
11921
12028
|
} else {
|
|
@@ -11950,8 +12057,8 @@ class TaskStore {
|
|
|
11950
12057
|
try {
|
|
11951
12058
|
let complete = true;
|
|
11952
12059
|
for (const [name, digest] of done) {
|
|
11953
|
-
const file =
|
|
11954
|
-
if (sha256(
|
|
12060
|
+
const file = path8.join(v2Tasks, name);
|
|
12061
|
+
if (sha256(fs8.readFileSync(file, "utf8")) !== digest) {
|
|
11955
12062
|
complete = false;
|
|
11956
12063
|
continue;
|
|
11957
12064
|
}
|
|
@@ -11961,7 +12068,7 @@ class TaskStore {
|
|
|
11961
12068
|
return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
|
|
11962
12069
|
path: v2
|
|
11963
12070
|
});
|
|
11964
|
-
|
|
12071
|
+
fs8.rmSync(path8.join(v2, "index.json"), { force: true });
|
|
11965
12072
|
this.writeTombstone(v2, target.id);
|
|
11966
12073
|
} catch (error) {
|
|
11967
12074
|
return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
|
|
@@ -11981,45 +12088,45 @@ class TaskStore {
|
|
|
11981
12088
|
}
|
|
11982
12089
|
adoptLocalStore() {
|
|
11983
12090
|
const dirs = this.paths();
|
|
11984
|
-
const marker =
|
|
11985
|
-
if (!dirs.shared || !
|
|
12091
|
+
const marker = path8.join(dirs.v2, "store.json");
|
|
12092
|
+
if (!dirs.shared || !fs8.existsSync(marker))
|
|
11986
12093
|
return success(null, null, null);
|
|
11987
12094
|
return this.withLock(dirs.checkoutLock, () => {
|
|
11988
|
-
if (!
|
|
12095
|
+
if (!fs8.existsSync(marker))
|
|
11989
12096
|
return success(null, null, null);
|
|
11990
12097
|
let tasks = 0;
|
|
11991
12098
|
try {
|
|
11992
12099
|
const merge = (from, to) => {
|
|
11993
|
-
if (!
|
|
11994
|
-
|
|
12100
|
+
if (!fs8.existsSync(to)) {
|
|
12101
|
+
fs8.mkdirSync(path8.dirname(to), { recursive: true });
|
|
11995
12102
|
try {
|
|
11996
|
-
|
|
12103
|
+
fs8.renameSync(from, to);
|
|
11997
12104
|
} catch {
|
|
11998
|
-
|
|
11999
|
-
|
|
12105
|
+
fs8.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
|
|
12106
|
+
fs8.rmSync(from, { recursive: true, force: true });
|
|
12000
12107
|
}
|
|
12001
12108
|
return;
|
|
12002
12109
|
}
|
|
12003
|
-
if (
|
|
12004
|
-
for (const name of
|
|
12005
|
-
merge(
|
|
12110
|
+
if (fs8.lstatSync(from).isDirectory() && fs8.lstatSync(to).isDirectory())
|
|
12111
|
+
for (const name of fs8.readdirSync(from))
|
|
12112
|
+
merge(path8.join(from, name), path8.join(to, name));
|
|
12006
12113
|
};
|
|
12007
12114
|
for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
|
|
12008
|
-
const from =
|
|
12009
|
-
if (!
|
|
12115
|
+
const from = path8.join(dirs.v2, sub);
|
|
12116
|
+
if (!fs8.existsSync(from))
|
|
12010
12117
|
continue;
|
|
12011
12118
|
if (sub === "tasks") {
|
|
12012
|
-
for (const name of
|
|
12013
|
-
if (validId(name) && !
|
|
12119
|
+
for (const name of fs8.readdirSync(from))
|
|
12120
|
+
if (validId(name) && !fs8.existsSync(path8.join(dirs.dir, sub, name)))
|
|
12014
12121
|
tasks += 1;
|
|
12015
12122
|
}
|
|
12016
|
-
merge(from,
|
|
12123
|
+
merge(from, path8.join(dirs.dir, sub));
|
|
12017
12124
|
}
|
|
12018
|
-
const ledger =
|
|
12019
|
-
if (
|
|
12020
|
-
merge(ledger,
|
|
12021
|
-
|
|
12022
|
-
|
|
12125
|
+
const ledger = path8.join(dirs.v2, "ledger");
|
|
12126
|
+
if (fs8.existsSync(ledger) && !fs8.existsSync(path8.join(dirs.dir, "ledger")))
|
|
12127
|
+
merge(ledger, path8.join(dirs.dir, "ledger"));
|
|
12128
|
+
fs8.rmSync(path8.join(dirs.v2, "task-index.json"), { force: true });
|
|
12129
|
+
fs8.renameSync(marker, path8.join(dirs.v2, "store.moved.json"));
|
|
12023
12130
|
} catch (error) {
|
|
12024
12131
|
return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
|
|
12025
12132
|
path: dirs.v2
|
|
@@ -12039,7 +12146,7 @@ class TaskStore {
|
|
|
12039
12146
|
readV2Workspace(file) {
|
|
12040
12147
|
let bytes;
|
|
12041
12148
|
try {
|
|
12042
|
-
bytes =
|
|
12149
|
+
bytes = fs8.readFileSync(file, "utf8");
|
|
12043
12150
|
} catch {
|
|
12044
12151
|
return null;
|
|
12045
12152
|
}
|
|
@@ -12057,7 +12164,7 @@ class TaskStore {
|
|
|
12057
12164
|
schemaVersion: SCHEMA_VERSION,
|
|
12058
12165
|
id: workspaceId ?? newId(),
|
|
12059
12166
|
revision: newRevision(),
|
|
12060
|
-
root:
|
|
12167
|
+
root: path8.dirname(v2),
|
|
12061
12168
|
runtime: { createdWith: version, updatedWith: version },
|
|
12062
12169
|
writer: null,
|
|
12063
12170
|
store: {
|
|
@@ -12068,12 +12175,12 @@ class TaskStore {
|
|
|
12068
12175
|
},
|
|
12069
12176
|
critical: ["store"]
|
|
12070
12177
|
};
|
|
12071
|
-
|
|
12072
|
-
const ignore =
|
|
12073
|
-
if (!
|
|
12074
|
-
|
|
12178
|
+
fs8.mkdirSync(v2, { recursive: true });
|
|
12179
|
+
const ignore = path8.join(v2, ".gitignore");
|
|
12180
|
+
if (!fs8.existsSync(ignore))
|
|
12181
|
+
fs8.writeFileSync(ignore, `*
|
|
12075
12182
|
`);
|
|
12076
|
-
replaceFile(
|
|
12183
|
+
replaceFile(path8.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
|
|
12077
12184
|
`, true);
|
|
12078
12185
|
}
|
|
12079
12186
|
stubFor(taskId) {
|
|
@@ -12089,7 +12196,7 @@ class TaskStore {
|
|
|
12089
12196
|
}
|
|
12090
12197
|
isStub(file) {
|
|
12091
12198
|
try {
|
|
12092
|
-
const value = JSON.parse(
|
|
12199
|
+
const value = JSON.parse(fs8.readFileSync(file, "utf8"));
|
|
12093
12200
|
return isObject5(value) && isObject5(value.store) && value.store.format === "workit-store";
|
|
12094
12201
|
} catch {
|
|
12095
12202
|
return false;
|
|
@@ -12097,32 +12204,32 @@ class TaskStore {
|
|
|
12097
12204
|
}
|
|
12098
12205
|
writeTombstoneIfMissing() {
|
|
12099
12206
|
try {
|
|
12100
|
-
const v2 =
|
|
12101
|
-
if (
|
|
12207
|
+
const v2 = path8.join(this.root, ".workit");
|
|
12208
|
+
if (fs8.existsSync(path8.join(v2, "workspace.json")))
|
|
12102
12209
|
return;
|
|
12103
12210
|
let id = null;
|
|
12104
12211
|
try {
|
|
12105
|
-
id = JSON.parse(
|
|
12212
|
+
id = JSON.parse(fs8.readFileSync(this.paths().workspace, "utf8")).id ?? null;
|
|
12106
12213
|
} catch {}
|
|
12107
12214
|
this.writeTombstone(v2, id);
|
|
12108
12215
|
} catch {}
|
|
12109
12216
|
}
|
|
12110
12217
|
initializeStorage() {
|
|
12111
12218
|
const dirs = this.paths();
|
|
12112
|
-
retryTransient(() =>
|
|
12113
|
-
retryTransient(() =>
|
|
12114
|
-
if (!
|
|
12219
|
+
retryTransient(() => fs8.mkdirSync(dirs.checkout, { recursive: true }));
|
|
12220
|
+
retryTransient(() => fs8.mkdirSync(dirs.tasks, { recursive: true }));
|
|
12221
|
+
if (!fs8.existsSync(dirs.marker))
|
|
12115
12222
|
replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
|
|
12116
12223
|
`, false);
|
|
12117
12224
|
if (!dirs.shared) {
|
|
12118
|
-
const ignore =
|
|
12225
|
+
const ignore = path8.join(dirs.dir, ".gitignore");
|
|
12119
12226
|
let current = null;
|
|
12120
12227
|
try {
|
|
12121
|
-
current =
|
|
12228
|
+
current = fs8.readFileSync(ignore, "utf8");
|
|
12122
12229
|
} catch {}
|
|
12123
12230
|
if (current !== `*
|
|
12124
12231
|
`)
|
|
12125
|
-
retryTransient(() =>
|
|
12232
|
+
retryTransient(() => fs8.writeFileSync(ignore, `*
|
|
12126
12233
|
`));
|
|
12127
12234
|
}
|
|
12128
12235
|
this.writeTombstoneIfMissing();
|
|
@@ -12138,7 +12245,7 @@ class TaskStore {
|
|
|
12138
12245
|
shouldReclaim: ({ payload, nowMs }) => {
|
|
12139
12246
|
let ageMs = null;
|
|
12140
12247
|
try {
|
|
12141
|
-
ageMs = nowMs -
|
|
12248
|
+
ageMs = nowMs - fs8.lstatSync(lockPath).mtimeMs;
|
|
12142
12249
|
} catch {}
|
|
12143
12250
|
return classifyLockOwner(payload, ageMs, localLockHost(), TASK_STORE_LOCK).state === "stale";
|
|
12144
12251
|
},
|
|
@@ -12175,7 +12282,7 @@ class TaskStore {
|
|
|
12175
12282
|
withLock(lockPath, operation) {
|
|
12176
12283
|
try {
|
|
12177
12284
|
this.initializeStorage();
|
|
12178
|
-
|
|
12285
|
+
fs8.mkdirSync(path8.dirname(lockPath), { recursive: true });
|
|
12179
12286
|
} catch (error) {
|
|
12180
12287
|
return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
|
|
12181
12288
|
path: this.paths().dir
|
|
@@ -12215,18 +12322,18 @@ class TaskStore {
|
|
|
12215
12322
|
lockDirWritable(lockPath) {
|
|
12216
12323
|
const probe = `${lockPath}.${process.pid}.${randomUUID3()}.probe`;
|
|
12217
12324
|
try {
|
|
12218
|
-
|
|
12325
|
+
fs8.closeSync(fs8.openSync(probe, "wx", 384));
|
|
12219
12326
|
} catch {
|
|
12220
12327
|
return false;
|
|
12221
12328
|
}
|
|
12222
12329
|
try {
|
|
12223
|
-
|
|
12330
|
+
fs8.rmSync(probe, { force: true });
|
|
12224
12331
|
} catch {}
|
|
12225
12332
|
return true;
|
|
12226
12333
|
}
|
|
12227
12334
|
lockHolderPresent(lockPath) {
|
|
12228
12335
|
try {
|
|
12229
|
-
|
|
12336
|
+
fs8.lstatSync(lockPath);
|
|
12230
12337
|
return true;
|
|
12231
12338
|
} catch (error) {
|
|
12232
12339
|
return error?.code !== "ENOENT";
|
|
@@ -12238,12 +12345,12 @@ class TaskStore {
|
|
|
12238
12345
|
if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
|
|
12239
12346
|
return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
|
|
12240
12347
|
path: lockPath,
|
|
12241
|
-
guidance: `Check permissions and the read-only attribute on ${
|
|
12348
|
+
guidance: `Check permissions and the read-only attribute on ${path8.dirname(lockPath)}.`
|
|
12242
12349
|
});
|
|
12243
12350
|
if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
|
|
12244
12351
|
let lock = null;
|
|
12245
12352
|
try {
|
|
12246
|
-
lock = parseMetadataLockOrNull(
|
|
12353
|
+
lock = parseMetadataLockOrNull(fs8.readFileSync(lockPath, "utf8"));
|
|
12247
12354
|
} catch {}
|
|
12248
12355
|
return failure2("busy", `workit lock is held by another Workit call${lock ? ` (pid ${lock.pid} on ${lock.host})` : ""}; retry shortly`, {
|
|
12249
12356
|
outcome: "not_started",
|
|
@@ -12261,15 +12368,15 @@ var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
|
|
|
12261
12368
|
var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
|
|
12262
12369
|
|
|
12263
12370
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
12264
|
-
import { createHash as
|
|
12265
|
-
import * as
|
|
12371
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
12372
|
+
import * as fs13 from "node:fs";
|
|
12266
12373
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
12267
|
-
import
|
|
12374
|
+
import path16 from "node:path";
|
|
12268
12375
|
|
|
12269
12376
|
// packages/workit-core/src/check-config.ts
|
|
12270
12377
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
12271
|
-
import
|
|
12272
|
-
import
|
|
12378
|
+
import fs9 from "node:fs";
|
|
12379
|
+
import path9 from "node:path";
|
|
12273
12380
|
var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
|
|
12274
12381
|
var CHECKS_FILE = "workit.checks.json";
|
|
12275
12382
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -12322,10 +12429,10 @@ var git = (cwd, args) => {
|
|
|
12322
12429
|
return value || null;
|
|
12323
12430
|
};
|
|
12324
12431
|
function checkRoot(cwd) {
|
|
12325
|
-
return
|
|
12432
|
+
return path9.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
|
|
12326
12433
|
}
|
|
12327
12434
|
var packageManager = (root) => {
|
|
12328
|
-
const has = (file) =>
|
|
12435
|
+
const has = (file) => fs9.existsSync(path9.join(root, file));
|
|
12329
12436
|
if (has("bun.lock") || has("bun.lockb"))
|
|
12330
12437
|
return "bun";
|
|
12331
12438
|
if (has("pnpm-lock.yaml"))
|
|
@@ -12345,7 +12452,7 @@ var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
|
|
|
12345
12452
|
function detectedChecks(root) {
|
|
12346
12453
|
const read = (file) => {
|
|
12347
12454
|
try {
|
|
12348
|
-
return
|
|
12455
|
+
return fs9.readFileSync(path9.join(root, file), "utf8");
|
|
12349
12456
|
} catch {
|
|
12350
12457
|
return null;
|
|
12351
12458
|
}
|
|
@@ -12384,7 +12491,7 @@ function detectedChecks(root) {
|
|
|
12384
12491
|
var GATES = ["testing", "verification"];
|
|
12385
12492
|
function loadCheckConfig(cwd) {
|
|
12386
12493
|
const root = checkRoot(cwd);
|
|
12387
|
-
const file =
|
|
12494
|
+
const file = path9.join(root, CHECKS_FILE);
|
|
12388
12495
|
const none = (error = null) => ({
|
|
12389
12496
|
source: error ? CHECKS_FILE : "none",
|
|
12390
12497
|
root,
|
|
@@ -12394,7 +12501,7 @@ function loadCheckConfig(cwd) {
|
|
|
12394
12501
|
});
|
|
12395
12502
|
let text = null;
|
|
12396
12503
|
try {
|
|
12397
|
-
text =
|
|
12504
|
+
text = fs9.readFileSync(file, "utf8");
|
|
12398
12505
|
} catch (error) {
|
|
12399
12506
|
if (error.code !== "ENOENT")
|
|
12400
12507
|
return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
|
|
@@ -12457,10 +12564,10 @@ function workitAvailable(env = process.env) {
|
|
|
12457
12564
|
return workitOnPath;
|
|
12458
12565
|
const windows = process.platform === "win32";
|
|
12459
12566
|
const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
|
|
12460
|
-
const dirs = (env.PATH ?? env.Path ?? "").split(
|
|
12567
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(path9.delimiter).filter(Boolean);
|
|
12461
12568
|
workitOnPath = dirs.some((dir) => exts.some((ext) => {
|
|
12462
12569
|
try {
|
|
12463
|
-
return
|
|
12570
|
+
return fs9.statSync(path9.join(dir, `workit${ext}`)).isFile();
|
|
12464
12571
|
} catch {
|
|
12465
12572
|
return false;
|
|
12466
12573
|
}
|
|
@@ -12473,10 +12580,10 @@ function checkCommand(args, version, onPath = workitAvailable()) {
|
|
|
12473
12580
|
|
|
12474
12581
|
// packages/workit-core/src/git/rev.ts
|
|
12475
12582
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
12476
|
-
import { createHash as
|
|
12477
|
-
import
|
|
12583
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
12584
|
+
import fs10 from "node:fs";
|
|
12478
12585
|
import os2 from "node:os";
|
|
12479
|
-
import
|
|
12586
|
+
import path10 from "node:path";
|
|
12480
12587
|
var MAX_BUFFER = 256 * 1024 * 1024;
|
|
12481
12588
|
var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
|
|
12482
12589
|
var git2 = (cwd, args, options = {}) => {
|
|
@@ -12511,10 +12618,10 @@ function headSha(cwd) {
|
|
|
12511
12618
|
var emptyTree = (cwd) => line(git2(cwd, ["mktree"], { input: "" }));
|
|
12512
12619
|
var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
|
|
12513
12620
|
var seedIndex = (from, to) => {
|
|
12514
|
-
|
|
12515
|
-
const { atimeNs, mtimeNs } =
|
|
12621
|
+
fs10.copyFileSync(from, to);
|
|
12622
|
+
const { atimeNs, mtimeNs } = fs10.statSync(from, { bigint: true });
|
|
12516
12623
|
const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
|
|
12517
|
-
|
|
12624
|
+
fs10.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
|
|
12518
12625
|
};
|
|
12519
12626
|
function worktreeTree(cwd, options = {}) {
|
|
12520
12627
|
const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
|
|
@@ -12523,10 +12630,10 @@ function worktreeTree(cwd, options = {}) {
|
|
|
12523
12630
|
return null;
|
|
12524
12631
|
const cap = options.maxUntrackedBytes ?? MAX_UNTRACKED_BYTES;
|
|
12525
12632
|
const timeoutMs = options.timeoutMs ?? GIT_TIMEOUTS.worktree;
|
|
12526
|
-
const scratch =
|
|
12633
|
+
const scratch = fs10.mkdtempSync(path10.join(os2.tmpdir(), "workit-tree-"));
|
|
12527
12634
|
try {
|
|
12528
|
-
const tempIndex =
|
|
12529
|
-
if (
|
|
12635
|
+
const tempIndex = path10.join(scratch, "index");
|
|
12636
|
+
if (fs10.existsSync(indexPath))
|
|
12530
12637
|
seedIndex(indexPath, tempIndex);
|
|
12531
12638
|
const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
|
|
12532
12639
|
const untracked = git2(top, ["ls-files", "--others", "--exclude-standard", "-z"], {
|
|
@@ -12538,7 +12645,7 @@ function worktreeTree(cwd, options = {}) {
|
|
|
12538
12645
|
const skipped = [];
|
|
12539
12646
|
for (const file of untracked.stdout.split("\x00").filter(Boolean)) {
|
|
12540
12647
|
try {
|
|
12541
|
-
const stat =
|
|
12648
|
+
const stat = fs10.statSync(path10.join(top, file));
|
|
12542
12649
|
if (stat.isFile() && stat.size > cap)
|
|
12543
12650
|
skipped.push({ path: file, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) });
|
|
12544
12651
|
} catch {}
|
|
@@ -12555,10 +12662,10 @@ function worktreeTree(cwd, options = {}) {
|
|
|
12555
12662
|
if (!tree)
|
|
12556
12663
|
return null;
|
|
12557
12664
|
const base = line(git2(top, ["rev-parse", "--verify", "-q", "HEAD^{tree}"])) ?? emptyTree(top);
|
|
12558
|
-
const key = skipped.length ? `sha256:${
|
|
12665
|
+
const key = skipped.length ? `sha256:${createHash6("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
|
|
12559
12666
|
return { tree, key, dirty: tree !== base || skipped.length > 0, skipped };
|
|
12560
12667
|
} finally {
|
|
12561
|
-
|
|
12668
|
+
fs10.rmSync(scratch, { recursive: true, force: true });
|
|
12562
12669
|
}
|
|
12563
12670
|
}
|
|
12564
12671
|
function worktreeSignal(cwd, options = {}) {
|
|
@@ -12578,16 +12685,16 @@ function worktreeSignal(cwd, options = {}) {
|
|
|
12578
12685
|
});
|
|
12579
12686
|
if (!status.ok)
|
|
12580
12687
|
return null;
|
|
12581
|
-
const hash =
|
|
12688
|
+
const hash = createHash6("sha256");
|
|
12582
12689
|
hash.update(`${headSha(top) ?? "unborn"}\x00`);
|
|
12583
12690
|
for (const entry of status.stdout.split("\x00")) {
|
|
12584
12691
|
if (entry.length < 4)
|
|
12585
12692
|
continue;
|
|
12586
12693
|
const file = entry.slice(3);
|
|
12587
|
-
const absolute =
|
|
12694
|
+
const absolute = path10.join(top, file);
|
|
12588
12695
|
let stat = "-";
|
|
12589
12696
|
try {
|
|
12590
|
-
const info =
|
|
12697
|
+
const info = fs10.lstatSync(absolute, { bigint: true });
|
|
12591
12698
|
stat = `${info.mode.toString(8)}:${info.size}:${info.mtimeNs}`;
|
|
12592
12699
|
if (info.isDirectory())
|
|
12593
12700
|
stat += `:${headSha(absolute) ?? "-"}`;
|
|
@@ -12654,17 +12761,17 @@ function pushRemoteName(cwd, branch) {
|
|
|
12654
12761
|
|
|
12655
12762
|
// packages/workit-core/src/core/policy/derive.ts
|
|
12656
12763
|
import { existsSync as existsSync4 } from "node:fs";
|
|
12657
|
-
import
|
|
12764
|
+
import path15 from "node:path";
|
|
12658
12765
|
|
|
12659
12766
|
// packages/workit-core/src/ledger.ts
|
|
12660
12767
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
12661
|
-
import { createHash as
|
|
12662
|
-
import
|
|
12663
|
-
import
|
|
12768
|
+
import { createHash as createHash9, randomBytes as randomBytes3 } from "node:crypto";
|
|
12769
|
+
import fs12 from "node:fs";
|
|
12770
|
+
import path14 from "node:path";
|
|
12664
12771
|
|
|
12665
12772
|
// packages/workit-core/src/core/vcs-config.ts
|
|
12666
|
-
import
|
|
12667
|
-
import
|
|
12773
|
+
import fs11 from "node:fs";
|
|
12774
|
+
import path13 from "node:path";
|
|
12668
12775
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
12669
12776
|
|
|
12670
12777
|
// packages/workit-core/src/core/config.ts
|
|
@@ -12678,7 +12785,7 @@ import {
|
|
|
12678
12785
|
writeFileSync as writeFileSync2
|
|
12679
12786
|
} from "node:fs";
|
|
12680
12787
|
import os3 from "node:os";
|
|
12681
|
-
import
|
|
12788
|
+
import path11 from "node:path";
|
|
12682
12789
|
|
|
12683
12790
|
// packages/workit-core/src/core/boundary.ts
|
|
12684
12791
|
import { statSync as statSync2 } from "node:fs";
|
|
@@ -12737,7 +12844,7 @@ var mergePreset = (preset, input = {}, current = {
|
|
|
12737
12844
|
protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
|
|
12738
12845
|
};
|
|
12739
12846
|
};
|
|
12740
|
-
var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ??
|
|
12847
|
+
var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path11.join(process.env.XDG_CONFIG_HOME || path11.join(os3.homedir(), ".config"), "workit");
|
|
12741
12848
|
var migratedDir = null;
|
|
12742
12849
|
var migrationFailed = false;
|
|
12743
12850
|
var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
@@ -12747,7 +12854,7 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
12747
12854
|
migratedDir = dir;
|
|
12748
12855
|
return dir;
|
|
12749
12856
|
}
|
|
12750
|
-
const legacy =
|
|
12857
|
+
const legacy = path11.join(process.env.XDG_CONFIG_HOME || path11.join(os3.homedir(), ".config"), "workflow-toolkit");
|
|
12751
12858
|
if (!existsSync3(legacy)) {
|
|
12752
12859
|
migratedDir = dir;
|
|
12753
12860
|
return dir;
|
|
@@ -12760,8 +12867,8 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
12760
12867
|
mkdirSync2(dir, { recursive: true });
|
|
12761
12868
|
diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
|
|
12762
12869
|
for (const entry of readdirSync2(legacy, { withFileTypes: true })) {
|
|
12763
|
-
const src =
|
|
12764
|
-
const dest =
|
|
12870
|
+
const src = path11.join(legacy, entry.name);
|
|
12871
|
+
const dest = path11.join(dir, entry.name);
|
|
12765
12872
|
if (existsSync3(dest))
|
|
12766
12873
|
continue;
|
|
12767
12874
|
try {
|
|
@@ -12833,7 +12940,7 @@ var parseConfigResult = (raw, file) => {
|
|
|
12833
12940
|
return { status: "valid", path: file, config };
|
|
12834
12941
|
};
|
|
12835
12942
|
var readConfigTyped = (dir) => {
|
|
12836
|
-
const file =
|
|
12943
|
+
const file = path11.join(dir ?? configDir(), "config.json");
|
|
12837
12944
|
return parseConfigResult(readSafe(file), file);
|
|
12838
12945
|
};
|
|
12839
12946
|
var readConfig = () => {
|
|
@@ -12851,10 +12958,10 @@ var mergeConfigValues = (input, current) => ({
|
|
|
12851
12958
|
var writeConfig = (config) => {
|
|
12852
12959
|
const dir = configDir();
|
|
12853
12960
|
mkdirSync2(dir, { recursive: true });
|
|
12854
|
-
writeFileSync2(
|
|
12961
|
+
writeFileSync2(path11.join(dir, "config.json"), JSON.stringify(config, null, 2) + `
|
|
12855
12962
|
`, "utf8");
|
|
12856
12963
|
};
|
|
12857
|
-
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ??
|
|
12964
|
+
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path11.join(configDir(), "vcs.json");
|
|
12858
12965
|
var branchGlobPattern = (glob) => new RegExp(`^${glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i");
|
|
12859
12966
|
var resolveBranchPolicy = (config, workspace, repoPreset) => {
|
|
12860
12967
|
const wp = workspace?.branchPolicy ?? {};
|
|
@@ -12896,9 +13003,9 @@ var resolveCommitPolicy = (config, workspace) => {
|
|
|
12896
13003
|
|
|
12897
13004
|
// packages/workit-core/src/core/workspaces.ts
|
|
12898
13005
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
12899
|
-
import { createHash as
|
|
13006
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
12900
13007
|
import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
12901
|
-
import
|
|
13008
|
+
import path12 from "node:path";
|
|
12902
13009
|
|
|
12903
13010
|
// packages/workit-core/src/core/branch-policy.ts
|
|
12904
13011
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
@@ -13033,7 +13140,7 @@ var releaseTrackSchema = object({
|
|
|
13033
13140
|
context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
|
|
13034
13141
|
}
|
|
13035
13142
|
}
|
|
13036
|
-
if (track.versionSource?.kind === "package-json" && (
|
|
13143
|
+
if (track.versionSource?.kind === "package-json" && (path12.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
|
|
13037
13144
|
context.addIssue({
|
|
13038
13145
|
code: "custom",
|
|
13039
13146
|
path: ["versionSource", "path"],
|
|
@@ -13094,7 +13201,7 @@ var workspacesFileSchema = object({ workspaces: array(workspaceConfigSchema).opt
|
|
|
13094
13201
|
names.add(workspace.name);
|
|
13095
13202
|
}
|
|
13096
13203
|
});
|
|
13097
|
-
var workspacesRevision = (contents) =>
|
|
13204
|
+
var workspacesRevision = (contents) => createHash7("sha256").update(contents ?? "<missing>").digest("hex");
|
|
13098
13205
|
var validateWorkspacesDocument = (parsed, file) => {
|
|
13099
13206
|
if (!isConfigObject(parsed)) {
|
|
13100
13207
|
return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
|
|
@@ -13132,7 +13239,7 @@ var validateWorkspacesDocument = (parsed, file) => {
|
|
|
13132
13239
|
};
|
|
13133
13240
|
};
|
|
13134
13241
|
var readWorkspacesResult = (dir = configDir()) => {
|
|
13135
|
-
const file =
|
|
13242
|
+
const file = path12.join(dir, "workspaces.json");
|
|
13136
13243
|
let raw;
|
|
13137
13244
|
try {
|
|
13138
13245
|
raw = readFileSync3(file, "utf8");
|
|
@@ -13243,9 +13350,9 @@ var mainCheckoutOf = (cwd) => {
|
|
|
13243
13350
|
let main = null;
|
|
13244
13351
|
const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
|
|
13245
13352
|
const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
|
|
13246
|
-
if (gitDir && common &&
|
|
13353
|
+
if (gitDir && common && path12.resolve(gitDir) !== path12.resolve(common)) {
|
|
13247
13354
|
const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
|
|
13248
|
-
main = listed?.trim() || (
|
|
13355
|
+
main = listed?.trim() || (path12.basename(common) === ".git" ? path12.dirname(common) : common);
|
|
13249
13356
|
}
|
|
13250
13357
|
mainCheckouts.set(cwd, main);
|
|
13251
13358
|
return main;
|
|
@@ -13336,7 +13443,7 @@ var runtimeWorkspaceIndexSchema = object({
|
|
|
13336
13443
|
workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
13337
13444
|
}).passthrough();
|
|
13338
13445
|
var matchRuntimeWorkspace = (cwd, dir = configDir()) => {
|
|
13339
|
-
const file =
|
|
13446
|
+
const file = path12.join(dir, "workspaces.json");
|
|
13340
13447
|
let raw;
|
|
13341
13448
|
try {
|
|
13342
13449
|
raw = readFileSync3(file, "utf8");
|
|
@@ -13411,7 +13518,7 @@ function workspaceDefaultTarget(cwd, globalDefault) {
|
|
|
13411
13518
|
}
|
|
13412
13519
|
|
|
13413
13520
|
// packages/workit-core/src/core/release-tracks.ts
|
|
13414
|
-
import { createHash as
|
|
13521
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
13415
13522
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
13416
13523
|
class ReleaseTrackError extends Error {
|
|
13417
13524
|
}
|
|
@@ -13794,7 +13901,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
13794
13901
|
]) ?? "",
|
|
13795
13902
|
git3(cwd, ["config", "--get", recordedBaseKey(branch)]) ?? ""
|
|
13796
13903
|
] : [];
|
|
13797
|
-
key =
|
|
13904
|
+
key = createHash8("sha256").update(JSON.stringify({ cwd, input, refs, head, own })).digest("hex");
|
|
13798
13905
|
const hit = memo2.get(key);
|
|
13799
13906
|
if (hit)
|
|
13800
13907
|
return hit;
|
|
@@ -13809,7 +13916,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
13809
13916
|
}
|
|
13810
13917
|
|
|
13811
13918
|
// packages/workit-core/src/core/vcs-config.ts
|
|
13812
|
-
var workspacesPath = () =>
|
|
13919
|
+
var workspacesPath = () => path13.join(configDir(), "workspaces.json");
|
|
13813
13920
|
var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
13814
13921
|
var remoteProvider = (cwd) => {
|
|
13815
13922
|
const r = spawnSync8("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
|
|
@@ -13899,7 +14006,7 @@ var readVcsConfig = () => {
|
|
|
13899
14006
|
const file = vcsConfigPath();
|
|
13900
14007
|
let raw;
|
|
13901
14008
|
try {
|
|
13902
|
-
raw =
|
|
14009
|
+
raw = fs11.readFileSync(file, "utf8");
|
|
13903
14010
|
} catch {
|
|
13904
14011
|
return { status: "missing", path: file, config: {} };
|
|
13905
14012
|
}
|
|
@@ -13984,7 +14091,7 @@ function vcsConfig(mode, cwd, options = {}) {
|
|
|
13984
14091
|
const prov = cfg[provider] ?? {};
|
|
13985
14092
|
const out = {
|
|
13986
14093
|
ok: true,
|
|
13987
|
-
configPath:
|
|
14094
|
+
configPath: path13.resolve(cfgPath),
|
|
13988
14095
|
provider,
|
|
13989
14096
|
defaultTargetBranch: workspaceDefault,
|
|
13990
14097
|
pr: cfg.pr ?? {},
|
|
@@ -14195,7 +14302,7 @@ function diffHash(cwd, base, head) {
|
|
|
14195
14302
|
], GIT_TIMEOUTS.worktree);
|
|
14196
14303
|
if (!diff.ok || !diff.stdout)
|
|
14197
14304
|
return null;
|
|
14198
|
-
const hash =
|
|
14305
|
+
const hash = createHash9("sha256");
|
|
14199
14306
|
for (const line of diff.stdout.split(`
|
|
14200
14307
|
`)) {
|
|
14201
14308
|
if (line.startsWith("index "))
|
|
@@ -14207,16 +14314,16 @@ function diffHash(cwd, base, head) {
|
|
|
14207
14314
|
return `sha256:${hash.digest("hex")}`;
|
|
14208
14315
|
}
|
|
14209
14316
|
function storeRoot(cwd) {
|
|
14210
|
-
const location = resolveStore(
|
|
14317
|
+
const location = resolveStore(path14.resolve(cwd));
|
|
14211
14318
|
if (location instanceof Error)
|
|
14212
14319
|
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");
|
|
14213
14320
|
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
14214
14321
|
}
|
|
14215
14322
|
function ledgerPath(cwd) {
|
|
14216
14323
|
const root = storeRoot(cwd);
|
|
14217
|
-
return root.ok ? { ok: true, value:
|
|
14324
|
+
return root.ok ? { ok: true, value: path14.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
14218
14325
|
}
|
|
14219
|
-
var newId2 = () => `${Date.now().toString(36)}-${
|
|
14326
|
+
var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
|
|
14220
14327
|
var NETWORK_FS_TYPES = new Set([
|
|
14221
14328
|
26985,
|
|
14222
14329
|
20859,
|
|
@@ -14234,7 +14341,7 @@ var onNetworkFs = (dir) => {
|
|
|
14234
14341
|
if (process.platform !== "linux")
|
|
14235
14342
|
return false;
|
|
14236
14343
|
try {
|
|
14237
|
-
return isNetworkFsType(
|
|
14344
|
+
return isNetworkFsType(fs12.statfsSync(dir).type);
|
|
14238
14345
|
} catch {
|
|
14239
14346
|
return false;
|
|
14240
14347
|
}
|
|
@@ -14244,15 +14351,15 @@ var LOCK_STALE_MS = 30000;
|
|
|
14244
14351
|
var LOCK_OWNER = "owner";
|
|
14245
14352
|
var lockToken = (lock) => {
|
|
14246
14353
|
try {
|
|
14247
|
-
return
|
|
14354
|
+
return fs12.readFileSync(path14.join(lock, LOCK_OWNER), "utf8");
|
|
14248
14355
|
} catch {
|
|
14249
14356
|
return null;
|
|
14250
14357
|
}
|
|
14251
14358
|
};
|
|
14252
14359
|
var lockAgeMs = (lock) => {
|
|
14253
|
-
for (const target of [
|
|
14360
|
+
for (const target of [path14.join(lock, LOCK_OWNER), lock]) {
|
|
14254
14361
|
try {
|
|
14255
|
-
return Date.now() -
|
|
14362
|
+
return Date.now() - fs12.statSync(target).mtimeMs;
|
|
14256
14363
|
} catch {}
|
|
14257
14364
|
}
|
|
14258
14365
|
return null;
|
|
@@ -14260,55 +14367,55 @@ var lockAgeMs = (lock) => {
|
|
|
14260
14367
|
var ledgerLock = {
|
|
14261
14368
|
acquire(lock) {
|
|
14262
14369
|
try {
|
|
14263
|
-
|
|
14370
|
+
fs12.mkdirSync(lock);
|
|
14264
14371
|
} catch (error) {
|
|
14265
14372
|
if (error.code === "EEXIST")
|
|
14266
14373
|
return null;
|
|
14267
14374
|
throw error;
|
|
14268
14375
|
}
|
|
14269
|
-
const token = `${process.pid}-${
|
|
14270
|
-
|
|
14376
|
+
const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
|
|
14377
|
+
fs12.writeFileSync(path14.join(lock, LOCK_OWNER), token);
|
|
14271
14378
|
return token;
|
|
14272
14379
|
},
|
|
14273
14380
|
release(lock, token) {
|
|
14274
14381
|
if (lockToken(lock) !== token)
|
|
14275
14382
|
return false;
|
|
14276
|
-
const tomb = `${lock}.release-${
|
|
14383
|
+
const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
|
|
14277
14384
|
try {
|
|
14278
|
-
|
|
14385
|
+
fs12.renameSync(lock, tomb);
|
|
14279
14386
|
} catch {
|
|
14280
14387
|
return false;
|
|
14281
14388
|
}
|
|
14282
14389
|
if (lockToken(tomb) !== token) {
|
|
14283
14390
|
try {
|
|
14284
|
-
|
|
14391
|
+
fs12.renameSync(tomb, lock);
|
|
14285
14392
|
} catch {}
|
|
14286
14393
|
return false;
|
|
14287
14394
|
}
|
|
14288
|
-
|
|
14395
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
14289
14396
|
return true;
|
|
14290
14397
|
},
|
|
14291
14398
|
token: lockToken,
|
|
14292
14399
|
reap(lock, observed) {
|
|
14293
|
-
const tomb = `${lock}.reap-${
|
|
14400
|
+
const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
|
|
14294
14401
|
try {
|
|
14295
|
-
|
|
14402
|
+
fs12.renameSync(lock, tomb);
|
|
14296
14403
|
} catch {
|
|
14297
14404
|
return false;
|
|
14298
14405
|
}
|
|
14299
14406
|
if (lockToken(tomb) === observed) {
|
|
14300
|
-
|
|
14407
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
14301
14408
|
return true;
|
|
14302
14409
|
}
|
|
14303
14410
|
try {
|
|
14304
|
-
|
|
14411
|
+
fs12.renameSync(tomb, lock);
|
|
14305
14412
|
} catch {
|
|
14306
|
-
|
|
14413
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
14307
14414
|
}
|
|
14308
14415
|
return false;
|
|
14309
14416
|
}
|
|
14310
14417
|
};
|
|
14311
|
-
function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
14418
|
+
function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
|
|
14312
14419
|
const lock = `${file}.lock`;
|
|
14313
14420
|
const deadline = Date.now() + waitMs;
|
|
14314
14421
|
let token = null;
|
|
@@ -14330,14 +14437,14 @@ function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
|
14330
14437
|
ledgerLock.release(lock, token);
|
|
14331
14438
|
}
|
|
14332
14439
|
}
|
|
14333
|
-
var sha2562 = (text) =>
|
|
14440
|
+
var sha2562 = (text) => createHash9("sha256").update(text).digest("hex");
|
|
14334
14441
|
var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
|
|
14335
14442
|
${body}`);
|
|
14336
14443
|
var TAIL_BYTES = 64 * 1024;
|
|
14337
14444
|
var lastLine = (fd, size) => {
|
|
14338
14445
|
for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
|
|
14339
14446
|
const buffer = Buffer.alloc(span);
|
|
14340
|
-
|
|
14447
|
+
fs12.readSync(fd, buffer, 0, span, size - span);
|
|
14341
14448
|
const end = buffer.lastIndexOf(10);
|
|
14342
14449
|
if (end < 0) {
|
|
14343
14450
|
if (span === size)
|
|
@@ -14366,15 +14473,23 @@ var lastLine = (fd, size) => {
|
|
|
14366
14473
|
}
|
|
14367
14474
|
return null;
|
|
14368
14475
|
};
|
|
14476
|
+
var tighten = (fd) => {
|
|
14477
|
+
try {
|
|
14478
|
+
if ((fs12.fstatSync(fd).mode & 63) !== 0)
|
|
14479
|
+
fs12.fchmodSync(fd, 384);
|
|
14480
|
+
} catch {}
|
|
14481
|
+
};
|
|
14369
14482
|
function appendLine(file, build, options) {
|
|
14370
14483
|
const write = () => {
|
|
14371
|
-
const fd =
|
|
14484
|
+
const fd = fs12.openSync(file, "a+", 384);
|
|
14372
14485
|
try {
|
|
14373
|
-
|
|
14486
|
+
if (process.platform !== "win32")
|
|
14487
|
+
tighten(fd);
|
|
14488
|
+
const size = fs12.fstatSync(fd).size;
|
|
14374
14489
|
let fence = "";
|
|
14375
14490
|
if (size > 0) {
|
|
14376
14491
|
const last = Buffer.alloc(1);
|
|
14377
|
-
|
|
14492
|
+
fs12.readSync(fd, last, 0, 1, size - 1);
|
|
14378
14493
|
if (last[0] !== 10)
|
|
14379
14494
|
fence = `
|
|
14380
14495
|
`;
|
|
@@ -14383,17 +14498,17 @@ function appendLine(file, build, options) {
|
|
|
14383
14498
|
if (!line.ok)
|
|
14384
14499
|
return line;
|
|
14385
14500
|
const bytes = Buffer.from(fence + line.value);
|
|
14386
|
-
const written =
|
|
14501
|
+
const written = fs12.writeSync(fd, bytes);
|
|
14387
14502
|
if (written !== bytes.length)
|
|
14388
14503
|
return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
|
|
14389
14504
|
return { ok: true, value: undefined };
|
|
14390
14505
|
} finally {
|
|
14391
|
-
|
|
14506
|
+
fs12.closeSync(fd);
|
|
14392
14507
|
}
|
|
14393
14508
|
};
|
|
14394
14509
|
try {
|
|
14395
|
-
|
|
14396
|
-
if (!options.forceLock && !onNetworkFs(
|
|
14510
|
+
fs12.mkdirSync(path14.dirname(file), { recursive: true });
|
|
14511
|
+
if (!options.forceLock && !onNetworkFs(path14.dirname(file)))
|
|
14397
14512
|
return write();
|
|
14398
14513
|
const locked = withLedgerLock(file, write);
|
|
14399
14514
|
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
@@ -14464,8 +14579,10 @@ function readLedger(cwd, options = {}) {
|
|
|
14464
14579
|
if (!file.ok)
|
|
14465
14580
|
return file;
|
|
14466
14581
|
let text = "";
|
|
14582
|
+
let stat = null;
|
|
14467
14583
|
try {
|
|
14468
|
-
|
|
14584
|
+
stat = fs12.statSync(file.value);
|
|
14585
|
+
text = fs12.readFileSync(file.value, "utf8");
|
|
14469
14586
|
} catch (error) {
|
|
14470
14587
|
if (error.code === "ENOENT")
|
|
14471
14588
|
return {
|
|
@@ -14484,6 +14601,7 @@ function readLedger(cwd, options = {}) {
|
|
|
14484
14601
|
const integrity = { chained: 0, legacy: 0, unverified: [] };
|
|
14485
14602
|
const earlier = new Set([null]);
|
|
14486
14603
|
let chainStarted = false;
|
|
14604
|
+
const ids = new Set;
|
|
14487
14605
|
for (const raw of text.split(`
|
|
14488
14606
|
`)) {
|
|
14489
14607
|
const trimmed = raw.trim();
|
|
@@ -14517,6 +14635,10 @@ function readLedger(cwd, options = {}) {
|
|
|
14517
14635
|
reason = "unsigned";
|
|
14518
14636
|
else
|
|
14519
14637
|
rowIntegrity = "legacy";
|
|
14638
|
+
if (options.integrity && !reason && ids.has(parsed.id))
|
|
14639
|
+
reason = "duplicate_id";
|
|
14640
|
+
if (options.integrity)
|
|
14641
|
+
ids.add(parsed.id);
|
|
14520
14642
|
if (reason)
|
|
14521
14643
|
rowIntegrity = "unverified";
|
|
14522
14644
|
const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
|
|
@@ -14540,7 +14662,8 @@ function readLedger(cwd, options = {}) {
|
|
|
14540
14662
|
seq: rows.length + 1,
|
|
14541
14663
|
superseded: false,
|
|
14542
14664
|
supersedeIgnored: false,
|
|
14543
|
-
...rowIntegrity ? { integrity: rowIntegrity } : {}
|
|
14665
|
+
...rowIntegrity ? { integrity: rowIntegrity } : {},
|
|
14666
|
+
...reason ? { integrityReason: reason } : {}
|
|
14544
14667
|
};
|
|
14545
14668
|
rows.push(row);
|
|
14546
14669
|
if (!options.integrity)
|
|
@@ -14568,18 +14691,48 @@ function readLedger(cwd, options = {}) {
|
|
|
14568
14691
|
const byId = new Map;
|
|
14569
14692
|
for (const row of rows) {
|
|
14570
14693
|
const target = row.supersedes ? byId.get(row.supersedes) : undefined;
|
|
14571
|
-
if (target && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
14694
|
+
if (target && row.integrity !== "unverified" && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
14572
14695
|
target.superseded = true;
|
|
14573
14696
|
else if (row.supersedes)
|
|
14574
14697
|
row.supersedeIgnored = true;
|
|
14575
14698
|
if (!byId.has(row.id))
|
|
14576
14699
|
byId.set(row.id, row);
|
|
14577
14700
|
}
|
|
14701
|
+
if (options.integrity && stat && Buffer.byteLength(text) === stat.size)
|
|
14702
|
+
tamperCache.set(file.value, {
|
|
14703
|
+
size: stat.size,
|
|
14704
|
+
stamp: fileStamp(stat),
|
|
14705
|
+
breaks: integrity.unverified.filter((row) => TAMPER_REASONS.has(row.reason))
|
|
14706
|
+
});
|
|
14578
14707
|
return {
|
|
14579
14708
|
ok: true,
|
|
14580
14709
|
value: { path: file.value, rows, skipped, integrity: options.integrity ? integrity : null }
|
|
14581
14710
|
};
|
|
14582
14711
|
}
|
|
14712
|
+
var TAMPER_REASONS = new Set(["hash_mismatch", "broken_link"]);
|
|
14713
|
+
var tamperCache = new Map;
|
|
14714
|
+
var fileStamp = (stat) => `${stat.ino}:${stat.mtimeMs}:${stat.ctimeMs}`;
|
|
14715
|
+
function ledgerTampering(cwd) {
|
|
14716
|
+
const file = ledgerPath(cwd);
|
|
14717
|
+
if (!file.ok)
|
|
14718
|
+
return [];
|
|
14719
|
+
try {
|
|
14720
|
+
const stat = fs12.statSync(file.value);
|
|
14721
|
+
const cached = tamperCache.get(file.value);
|
|
14722
|
+
if (cached && cached.size === stat.size && cached.stamp === fileStamp(stat))
|
|
14723
|
+
return cached.breaks;
|
|
14724
|
+
} catch {
|
|
14725
|
+
return [];
|
|
14726
|
+
}
|
|
14727
|
+
const read = readLedger(cwd, { integrity: true });
|
|
14728
|
+
return read.ok ? (read.value.integrity?.unverified ?? []).filter((row) => TAMPER_REASONS.has(row.reason)) : [];
|
|
14729
|
+
}
|
|
14730
|
+
function tamperedUnblock(cwd) {
|
|
14731
|
+
const file = ledgerPath(cwd);
|
|
14732
|
+
const where = file.ok ? file.value : "the ledger file";
|
|
14733
|
+
return `workit ledger verify-integrity # lists the edited or missing rows; if the edit is understood, move ${where} aside (mv ${where} ${where}.tampered-<date>) and record the verdicts again`;
|
|
14734
|
+
}
|
|
14735
|
+
var tamperedWarning = (cwd, breaks) => `the ledger was edited, or a row was removed or moved (${breaks.slice(0, 3).map((row) => `${row.reason} at row ${row.seq}`).join(", ")}${breaks.length > 3 ? ", …" : ""}): no verdict is accepted until it is repaired. ${tamperedUnblock(cwd)}`;
|
|
14583
14736
|
function verdictBasis(verdict, current) {
|
|
14584
14737
|
if (verdict.head && verdict.head === current.head)
|
|
14585
14738
|
return "fresh";
|
|
@@ -14587,7 +14740,7 @@ function verdictBasis(verdict, current) {
|
|
|
14587
14740
|
return "carried";
|
|
14588
14741
|
return "stale";
|
|
14589
14742
|
}
|
|
14590
|
-
var isLiveVerdict = (row) => row.type === "verdict" && typeof row.result === "string" && !row.
|
|
14743
|
+
var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id);
|
|
14591
14744
|
function trailerSessions(cwd, base, head) {
|
|
14592
14745
|
if (!base || !head || !safeRef(base) || !safeRef(head))
|
|
14593
14746
|
return [];
|
|
@@ -14668,6 +14821,56 @@ var authoredBy = (authors, session) => {
|
|
|
14668
14821
|
return true;
|
|
14669
14822
|
return false;
|
|
14670
14823
|
};
|
|
14824
|
+
var TRUST_REASONS = new Set([
|
|
14825
|
+
"unsigned",
|
|
14826
|
+
"hash_mismatch",
|
|
14827
|
+
"broken_link",
|
|
14828
|
+
"duplicate_id",
|
|
14829
|
+
"unissued_session",
|
|
14830
|
+
"unbound_session"
|
|
14831
|
+
]);
|
|
14832
|
+
var SHELL_VOUCHED_HOSTS = new Set([
|
|
14833
|
+
"cli",
|
|
14834
|
+
"opencode",
|
|
14835
|
+
"pi",
|
|
14836
|
+
"codex_cli",
|
|
14837
|
+
"codex_desktop"
|
|
14838
|
+
]);
|
|
14839
|
+
function sessionTrust(cwd, row) {
|
|
14840
|
+
const session = row.actor.session;
|
|
14841
|
+
if (!session || row.self === true)
|
|
14842
|
+
return null;
|
|
14843
|
+
const source = typeof row.sessionSource === "string" ? row.sessionSource : "env";
|
|
14844
|
+
const host = row.actor.host;
|
|
14845
|
+
const issuedOnly = source === "flag" ? host !== "cli" : source === "env" && session.includes(":") && !SHELL_VOUCHED_HOSTS.has(host);
|
|
14846
|
+
if (!issuedOnly)
|
|
14847
|
+
return null;
|
|
14848
|
+
const binding = issuedBinding(cwd, session);
|
|
14849
|
+
if (binding === null)
|
|
14850
|
+
return "unissued_session";
|
|
14851
|
+
return binding === "token" && row.sessionBound !== true ? "unbound_session" : null;
|
|
14852
|
+
}
|
|
14853
|
+
function trustReasons(cwd, row) {
|
|
14854
|
+
if (row.integrity === "unverified")
|
|
14855
|
+
return [row.integrityReason ?? "unsigned"];
|
|
14856
|
+
if (row.integrity !== "verified")
|
|
14857
|
+
return ["unsigned"];
|
|
14858
|
+
const session = sessionTrust(cwd, row);
|
|
14859
|
+
return session ? [session] : [];
|
|
14860
|
+
}
|
|
14861
|
+
function withIntegrity(cwd, rows) {
|
|
14862
|
+
if (rows.every((row) => row.integrity !== undefined))
|
|
14863
|
+
return rows;
|
|
14864
|
+
const read = readLedger(cwd, { integrity: true });
|
|
14865
|
+
const bySeq = new Map(read.ok ? read.value.rows.map((row) => [row.seq, row]) : []);
|
|
14866
|
+
return rows.map((row) => {
|
|
14867
|
+
if (row.integrity !== undefined)
|
|
14868
|
+
return row;
|
|
14869
|
+
const fresh = bySeq.get(row.seq);
|
|
14870
|
+
return fresh && fresh.id === row.id && fresh.at === row.at ? fresh : row;
|
|
14871
|
+
});
|
|
14872
|
+
}
|
|
14873
|
+
var untrusted = (reasons) => reasons.some((reason) => TRUST_REASONS.has(reason));
|
|
14671
14874
|
var independenceReasons = (row, authors) => {
|
|
14672
14875
|
const reasons = [];
|
|
14673
14876
|
if (!row.actor.session)
|
|
@@ -14678,15 +14881,17 @@ var independenceReasons = (row, authors) => {
|
|
|
14678
14881
|
reasons.push("self");
|
|
14679
14882
|
return reasons;
|
|
14680
14883
|
};
|
|
14681
|
-
function effectiveOf(rows,
|
|
14884
|
+
function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
|
|
14682
14885
|
let effective = null;
|
|
14683
14886
|
for (const row of rows) {
|
|
14684
14887
|
if (!effective) {
|
|
14685
14888
|
effective = row;
|
|
14686
14889
|
continue;
|
|
14687
14890
|
}
|
|
14688
|
-
|
|
14689
|
-
|
|
14891
|
+
if (untrusted(reasonsOf(row)) && !untrusted(reasonsOf(effective)))
|
|
14892
|
+
continue;
|
|
14893
|
+
const rowIndependent = reasonsOf(row).length === 0;
|
|
14894
|
+
const effIndependent = reasonsOf(effective).length === 0;
|
|
14690
14895
|
if (effIndependent && !rowIndependent && isCurrent(effective))
|
|
14691
14896
|
continue;
|
|
14692
14897
|
if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session && sameCode(effective, row))
|
|
@@ -14700,7 +14905,7 @@ function restackCarries(rows, branch, from, to) {
|
|
|
14700
14905
|
return false;
|
|
14701
14906
|
const edges = new Map;
|
|
14702
14907
|
for (const row of rows)
|
|
14703
|
-
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
|
|
14908
|
+
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
|
|
14704
14909
|
edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
|
|
14705
14910
|
const seen = new Set([from]);
|
|
14706
14911
|
const queue = [from];
|
|
@@ -14716,7 +14921,8 @@ function restackCarries(rows, branch, from, to) {
|
|
|
14716
14921
|
}
|
|
14717
14922
|
return false;
|
|
14718
14923
|
}
|
|
14719
|
-
function checkVerdicts(cwd, branch,
|
|
14924
|
+
function checkVerdicts(cwd, branch, ledgerRows) {
|
|
14925
|
+
const rows = withIntegrity(cwd, ledgerRows);
|
|
14720
14926
|
const head = branchHead(cwd, branch);
|
|
14721
14927
|
const fallbackBase = defaultBase(cwd);
|
|
14722
14928
|
const scope = branchScope(cwd, branch);
|
|
@@ -14746,21 +14952,47 @@ function checkVerdicts(cwd, branch, rows) {
|
|
|
14746
14952
|
return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
|
|
14747
14953
|
};
|
|
14748
14954
|
const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
|
|
14955
|
+
const failureAuthorCache = new Map;
|
|
14956
|
+
const failureAuthors = (row) => {
|
|
14957
|
+
let set = failureAuthorCache.get(row.seq);
|
|
14958
|
+
if (!set) {
|
|
14959
|
+
set = authorSessions(cwd, rows.filter((other) => other.integrity === "verified" && other.seq < row.seq), branch, { base: null, head: null }, scope);
|
|
14960
|
+
failureAuthorCache.set(row.seq, set);
|
|
14961
|
+
}
|
|
14962
|
+
return set;
|
|
14963
|
+
};
|
|
14964
|
+
const reasonCache = new Map;
|
|
14965
|
+
const reasonsOf = (row) => {
|
|
14966
|
+
let reasons = reasonCache.get(row);
|
|
14967
|
+
if (!reasons) {
|
|
14968
|
+
const failing = FAILING.has(String(row.result));
|
|
14969
|
+
reasons = [
|
|
14970
|
+
...trustReasons(cwd, row),
|
|
14971
|
+
...independenceReasons(row, failing ? failureAuthors(row) : authors)
|
|
14972
|
+
];
|
|
14973
|
+
reasonCache.set(row, reasons);
|
|
14974
|
+
}
|
|
14975
|
+
return reasons;
|
|
14976
|
+
};
|
|
14977
|
+
const retired = new Set;
|
|
14978
|
+
for (const row of rows)
|
|
14979
|
+
if (row.type === "verdict" && row.supersedes && !row.supersedeIgnored && trustReasons(cwd, row).length === 0)
|
|
14980
|
+
retired.add(row.supersedes);
|
|
14749
14981
|
const byKind = new Map;
|
|
14750
14982
|
for (const row of rows)
|
|
14751
|
-
if (isLiveVerdict(row) && scope(row)) {
|
|
14983
|
+
if (isLiveVerdict(row, retired) && scope(row)) {
|
|
14752
14984
|
const kind = typeof row.kind === "string" ? row.kind : "review";
|
|
14753
14985
|
byKind.set(kind, [...byKind.get(kind) ?? [], row]);
|
|
14754
14986
|
}
|
|
14755
14987
|
const verdicts = [];
|
|
14756
14988
|
const warnings = [];
|
|
14757
14989
|
for (const [kind, list] of byKind) {
|
|
14758
|
-
const row = effectiveOf(list,
|
|
14990
|
+
const row = effectiveOf(list, reasonsOf, sameCode, (candidate) => basisOf(candidate) !== "stale");
|
|
14759
14991
|
if (!row)
|
|
14760
14992
|
continue;
|
|
14761
14993
|
const basis = basisOf(row);
|
|
14762
14994
|
const current = basis !== "stale";
|
|
14763
|
-
const independence =
|
|
14995
|
+
const independence = reasonsOf(row);
|
|
14764
14996
|
const reasons = [
|
|
14765
14997
|
...current ? [] : ["stale"],
|
|
14766
14998
|
...PASSING.has(String(row.result)) ? [] : ["not_passing"],
|
|
@@ -14777,23 +15009,31 @@ function checkVerdicts(cwd, branch, rows) {
|
|
|
14777
15009
|
});
|
|
14778
15010
|
if (reasons.length === 0) {
|
|
14779
15011
|
for (const later of list)
|
|
14780
|
-
if (later.seq > row.seq &&
|
|
15012
|
+
if (later.seq > row.seq && reasonsOf(later).length > 0 && !untrusted(reasonsOf(later)) && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
|
|
14781
15013
|
warnings.push(`the author's own (self) ${String(later.result)} ${kind} verdict ${later.id} by ${later.actor.session ?? "no session"} on the current code is newer than the accepted pass ${row.id}: read it before merging`);
|
|
14782
15014
|
}
|
|
14783
15015
|
}
|
|
14784
15016
|
for (const row of rows)
|
|
14785
15017
|
if (row.integrity === "unverified" && scope(row))
|
|
14786
15018
|
warnings.push(`ledger row ${row.id} (${row.type}) on ${row.branch ?? branch} is unverified: the workit CLI did not write it or it changed afterwards (workit ledger verify-integrity)`);
|
|
15019
|
+
const tampered = ledgerTampering(cwd);
|
|
15020
|
+
if (tampered.length) {
|
|
15021
|
+
warnings.push(tamperedWarning(cwd, tampered));
|
|
15022
|
+
for (const entry of verdicts) {
|
|
15023
|
+
entry.accepted = false;
|
|
15024
|
+
entry.reasons = [...entry.reasons, "ledger_tampered"];
|
|
15025
|
+
}
|
|
15026
|
+
}
|
|
14787
15027
|
verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
|
|
14788
15028
|
const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
|
|
14789
15029
|
const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
|
|
14790
15030
|
const newestAccepted = verdicts.findLast((entry) => entry.accepted && STRONG_RESULTS.has(String(entry.verdict.result))) ?? null;
|
|
14791
|
-
const accepted = newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
15031
|
+
const accepted = tampered.length ? { accepted: false, verdict: null, reasons: ["ledger_tampered"] } : newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
14792
15032
|
accepted: false,
|
|
14793
15033
|
verdict: failing?.verdict ?? null,
|
|
14794
15034
|
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
14795
15035
|
};
|
|
14796
|
-
const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
15036
|
+
const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
14797
15037
|
return {
|
|
14798
15038
|
branch,
|
|
14799
15039
|
head,
|
|
@@ -14956,7 +15196,7 @@ var productDecision = (task, ledger) => {
|
|
|
14956
15196
|
};
|
|
14957
15197
|
};
|
|
14958
15198
|
var planRecorded = (task, root) => {
|
|
14959
|
-
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(
|
|
15199
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path15.resolve(root, ref.path)));
|
|
14960
15200
|
if (cited)
|
|
14961
15201
|
return {
|
|
14962
15202
|
met: true,
|
|
@@ -15015,15 +15255,15 @@ function writeBlockers(task, root) {
|
|
|
15015
15255
|
}
|
|
15016
15256
|
|
|
15017
15257
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
15018
|
-
var digestBytes = (value) =>
|
|
15019
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
15020
|
-
var relative = (root, candidate) =>
|
|
15258
|
+
var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
|
|
15259
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path16.sep}`);
|
|
15260
|
+
var relative = (root, candidate) => path16.relative(root, candidate).split(path16.sep).join("/") || ".";
|
|
15021
15261
|
var canonicalPath = (value) => {
|
|
15022
15262
|
if (typeof value !== "string" || !value)
|
|
15023
15263
|
return null;
|
|
15024
15264
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
15025
15265
|
return null;
|
|
15026
|
-
const normalized =
|
|
15266
|
+
const normalized = path16.posix.normalize(value);
|
|
15027
15267
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
15028
15268
|
return null;
|
|
15029
15269
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -15071,21 +15311,21 @@ var gitPaths = (root) => {
|
|
|
15071
15311
|
git: !/not a git repository/i.test(stderr)
|
|
15072
15312
|
};
|
|
15073
15313
|
}
|
|
15074
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
15314
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")) : [];
|
|
15075
15315
|
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
15076
15316
|
if (stagedDeleted.status !== 0) {
|
|
15077
15317
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
15078
15318
|
if (!/not a git repository/i.test(stderr))
|
|
15079
15319
|
return { paths, uncertain: true, git: true };
|
|
15080
15320
|
} else if (stagedDeleted.stdout) {
|
|
15081
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
15321
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")));
|
|
15082
15322
|
}
|
|
15083
15323
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
15084
15324
|
};
|
|
15085
15325
|
var walk = (root, directory, output) => {
|
|
15086
15326
|
let names;
|
|
15087
15327
|
try {
|
|
15088
|
-
names =
|
|
15328
|
+
names = fs13.readdirSync(directory);
|
|
15089
15329
|
} catch {
|
|
15090
15330
|
return true;
|
|
15091
15331
|
}
|
|
@@ -15093,10 +15333,10 @@ var walk = (root, directory, output) => {
|
|
|
15093
15333
|
for (const name of names) {
|
|
15094
15334
|
if (name === ".git" || name === ".workit")
|
|
15095
15335
|
continue;
|
|
15096
|
-
const target =
|
|
15336
|
+
const target = path16.join(directory, name);
|
|
15097
15337
|
let stat;
|
|
15098
15338
|
try {
|
|
15099
|
-
stat =
|
|
15339
|
+
stat = fs13.lstatSync(target);
|
|
15100
15340
|
} catch {
|
|
15101
15341
|
uncertain = true;
|
|
15102
15342
|
continue;
|
|
@@ -15111,14 +15351,14 @@ var walk = (root, directory, output) => {
|
|
|
15111
15351
|
var scopeRoots = (root, scope) => {
|
|
15112
15352
|
const roots = [];
|
|
15113
15353
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
15114
|
-
const target =
|
|
15354
|
+
const target = path16.resolve(root, item);
|
|
15115
15355
|
if (!inside(root, target))
|
|
15116
15356
|
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` }] });
|
|
15117
15357
|
let ancestor = target;
|
|
15118
|
-
while (!
|
|
15119
|
-
ancestor =
|
|
15358
|
+
while (!fs13.existsSync(ancestor) && ancestor !== root)
|
|
15359
|
+
ancestor = path16.dirname(ancestor);
|
|
15120
15360
|
try {
|
|
15121
|
-
if (!inside(root,
|
|
15361
|
+
if (!inside(root, fs13.realpathSync(ancestor)))
|
|
15122
15362
|
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` }] });
|
|
15123
15363
|
} catch {
|
|
15124
15364
|
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
@@ -15130,7 +15370,7 @@ var scopeRoots = (root, scope) => {
|
|
|
15130
15370
|
function captureCandidate(root, scope, environment = []) {
|
|
15131
15371
|
let checkout;
|
|
15132
15372
|
try {
|
|
15133
|
-
checkout =
|
|
15373
|
+
checkout = fs13.realpathSync(root);
|
|
15134
15374
|
} catch {
|
|
15135
15375
|
return failure2("invalid_input", "candidate root does not exist");
|
|
15136
15376
|
}
|
|
@@ -15148,11 +15388,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
15148
15388
|
if (item === ".git" || item === ".workit")
|
|
15149
15389
|
continue;
|
|
15150
15390
|
names.add(item);
|
|
15151
|
-
if (!
|
|
15391
|
+
if (!fs13.existsSync(target))
|
|
15152
15392
|
continue;
|
|
15153
15393
|
let stat;
|
|
15154
15394
|
try {
|
|
15155
|
-
stat =
|
|
15395
|
+
stat = fs13.lstatSync(target);
|
|
15156
15396
|
} catch {
|
|
15157
15397
|
uncertain = true;
|
|
15158
15398
|
continue;
|
|
@@ -15168,17 +15408,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
15168
15408
|
for (const item of [...names].toSorted()) {
|
|
15169
15409
|
if (!scopeMatches(item, normalizedScope))
|
|
15170
15410
|
continue;
|
|
15171
|
-
const target =
|
|
15411
|
+
const target = path16.join(checkout, item);
|
|
15172
15412
|
try {
|
|
15173
|
-
const stat =
|
|
15413
|
+
const stat = fs13.lstatSync(target);
|
|
15174
15414
|
if (stat.isSymbolicLink()) {
|
|
15175
|
-
const link =
|
|
15415
|
+
const link = fs13.readlinkSync(target);
|
|
15176
15416
|
files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
|
|
15177
15417
|
} else if (stat.isFile()) {
|
|
15178
15418
|
files.push({
|
|
15179
15419
|
path: item,
|
|
15180
15420
|
kind: "file",
|
|
15181
|
-
digest: digestBytes(
|
|
15421
|
+
digest: digestBytes(fs13.readFileSync(target)),
|
|
15182
15422
|
executable: (stat.mode & 73) !== 0
|
|
15183
15423
|
});
|
|
15184
15424
|
} else if (!stat.isDirectory()) {
|
|
@@ -15343,7 +15583,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
|
|
|
15343
15583
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
15344
15584
|
let root;
|
|
15345
15585
|
try {
|
|
15346
|
-
root =
|
|
15586
|
+
root = fs13.realpathSync(checkoutRoot);
|
|
15347
15587
|
} catch {
|
|
15348
15588
|
return failure2("permission_denied", "checkout root is unavailable");
|
|
15349
15589
|
}
|
|
@@ -15352,23 +15592,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
15352
15592
|
continue;
|
|
15353
15593
|
if (!reference.digest)
|
|
15354
15594
|
return failure2("invalid_input", "document references require a byte digest");
|
|
15355
|
-
const target =
|
|
15356
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
15595
|
+
const target = path16.resolve(root, reference.path);
|
|
15596
|
+
if (target !== root && !target.startsWith(`${root}${path16.sep}`))
|
|
15357
15597
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
15358
15598
|
fields: [
|
|
15359
15599
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
15360
15600
|
]
|
|
15361
15601
|
});
|
|
15362
15602
|
try {
|
|
15363
|
-
const below =
|
|
15603
|
+
const below = path16.relative(root, target);
|
|
15364
15604
|
let current = root;
|
|
15365
|
-
for (const segment of below.split(
|
|
15605
|
+
for (const segment of below.split(path16.sep)) {
|
|
15366
15606
|
if (!segment)
|
|
15367
15607
|
continue;
|
|
15368
|
-
current =
|
|
15369
|
-
const stat =
|
|
15370
|
-
const real =
|
|
15371
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
15608
|
+
current = path16.join(current, segment);
|
|
15609
|
+
const stat = fs13.lstatSync(current);
|
|
15610
|
+
const real = fs13.realpathSync(current);
|
|
15611
|
+
if (real !== root && !real.startsWith(`${root}${path16.sep}`))
|
|
15372
15612
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
15373
15613
|
fields: [
|
|
15374
15614
|
{
|
|
@@ -15384,7 +15624,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
15384
15624
|
if (current !== target && !stat.isDirectory())
|
|
15385
15625
|
return failure2("invalid_input", "document reference ancestor is not a directory");
|
|
15386
15626
|
}
|
|
15387
|
-
if (digestBytes(
|
|
15627
|
+
if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
|
|
15388
15628
|
return failure2("permission_denied", "decision document bytes have changed");
|
|
15389
15629
|
} catch {
|
|
15390
15630
|
return failure2("invalid_input", "decision document is unavailable");
|
|
@@ -15916,17 +16156,17 @@ var exportDigest = (bundle) => sha256({
|
|
|
15916
16156
|
// packages/workit-core/src/core/route-intent.ts
|
|
15917
16157
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
15918
16158
|
import { existsSync as existsSync6 } from "node:fs";
|
|
15919
|
-
import
|
|
16159
|
+
import path19 from "node:path";
|
|
15920
16160
|
|
|
15921
16161
|
// packages/workit-core/src/hooks/raw-git.ts
|
|
15922
16162
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
15923
|
-
import { createHash as
|
|
15924
|
-
import
|
|
16163
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
16164
|
+
import fs15 from "node:fs";
|
|
15925
16165
|
import os4 from "node:os";
|
|
15926
|
-
import
|
|
16166
|
+
import path18 from "node:path";
|
|
15927
16167
|
|
|
15928
16168
|
// packages/workit-core/src/core/branch.ts
|
|
15929
|
-
import { createHash as
|
|
16169
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
15930
16170
|
|
|
15931
16171
|
// packages/workit-core/src/core/git.ts
|
|
15932
16172
|
import { execFileSync } from "node:child_process";
|
|
@@ -16120,7 +16360,7 @@ var branchPolicySnapshotFor = (workspaceRoot) => {
|
|
|
16120
16360
|
const source = resolved.provenance.branchPolicy;
|
|
16121
16361
|
const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
|
|
16122
16362
|
const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
|
|
16123
|
-
const fingerprint =
|
|
16363
|
+
const fingerprint = createHash11("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
|
|
16124
16364
|
return {
|
|
16125
16365
|
branchPolicy,
|
|
16126
16366
|
namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
|
|
@@ -16174,9 +16414,9 @@ var releaseBranchesOf = (cwd) => {
|
|
|
16174
16414
|
var releaseBranch = (cwd, branch) => releaseBranchesOf(cwd).branches.some((name) => name.toLowerCase() === branch.toLowerCase());
|
|
16175
16415
|
|
|
16176
16416
|
// packages/workit-core/src/hooks/session-marker.ts
|
|
16177
|
-
import { createHash as
|
|
16178
|
-
import
|
|
16179
|
-
import
|
|
16417
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
16418
|
+
import fs14 from "node:fs";
|
|
16419
|
+
import path17 from "node:path";
|
|
16180
16420
|
var sessionMarkerFile = (input, session, store) => {
|
|
16181
16421
|
let dir = store;
|
|
16182
16422
|
if (dir === undefined) {
|
|
@@ -16185,13 +16425,13 @@ var sessionMarkerFile = (input, session, store) => {
|
|
|
16185
16425
|
return null;
|
|
16186
16426
|
dir = location.dir;
|
|
16187
16427
|
}
|
|
16188
|
-
const name =
|
|
16189
|
-
return
|
|
16428
|
+
const name = createHash12("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
16429
|
+
return path17.join(dir, "hooks", `skills-${name}.json`);
|
|
16190
16430
|
};
|
|
16191
16431
|
var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
16192
16432
|
var readSessionMarker = (file) => {
|
|
16193
16433
|
try {
|
|
16194
|
-
const value = JSON.parse(
|
|
16434
|
+
const value = JSON.parse(fs14.readFileSync(file, "utf8"));
|
|
16195
16435
|
return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
|
|
16196
16436
|
} catch {
|
|
16197
16437
|
return { nudged: [], loaded: [], raw: [] };
|
|
@@ -16199,8 +16439,8 @@ var readSessionMarker = (file) => {
|
|
|
16199
16439
|
};
|
|
16200
16440
|
var writeSessionMarker = (file, marker) => {
|
|
16201
16441
|
try {
|
|
16202
|
-
|
|
16203
|
-
|
|
16442
|
+
fs14.mkdirSync(path17.dirname(file), { recursive: true });
|
|
16443
|
+
fs14.writeFileSync(file, JSON.stringify(marker));
|
|
16204
16444
|
} catch {}
|
|
16205
16445
|
};
|
|
16206
16446
|
|
|
@@ -16697,14 +16937,14 @@ var GIT_CONFIG_ENV = /^GIT_(?:CONFIG\w*|DIR|COMMON_DIR|WORK_TREE)$/;
|
|
|
16697
16937
|
var ASSIGNMENT = /^([A-Za-z_]\w*)=([\s\S]*)$/;
|
|
16698
16938
|
var MAX_DEPTH = 4;
|
|
16699
16939
|
var MAX_EXPANSIONS = 16;
|
|
16700
|
-
var isAbsolute = (value) =>
|
|
16940
|
+
var isAbsolute = (value) => path18.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
|
|
16701
16941
|
var HERE = /^(?:\$PWD|\$\{PWD\}|\$\(\s*pwd\s*\)|`pwd`|\$\(\s*git\s+rev-parse\s+--show-toplevel\s*\))(?=$|[\\/])/;
|
|
16702
16942
|
var joinDir = (base, raw) => {
|
|
16703
16943
|
const next = raw.replace(HERE, ".");
|
|
16704
16944
|
if (base === undefined || /[$`*?]/.test(next) || next === "-")
|
|
16705
16945
|
return;
|
|
16706
|
-
const expanded = next === "~" || next.startsWith("~/") ?
|
|
16707
|
-
return base === null || isAbsolute(expanded) ? expanded :
|
|
16946
|
+
const expanded = next === "~" || next.startsWith("~/") ? path18.join(os4.homedir(), next.slice(1)) : next;
|
|
16947
|
+
return base === null || isAbsolute(expanded) ? expanded : path18.join(base, expanded);
|
|
16708
16948
|
};
|
|
16709
16949
|
var rebase = (base, inner) => inner === null ? base : inner === undefined ? undefined : joinDir(base, inner);
|
|
16710
16950
|
var cdTarget = (words) => {
|
|
@@ -17455,7 +17695,7 @@ var repoAt = (dir) => {
|
|
|
17455
17695
|
return { location, dir, branch: resolveTaskKey(dir, location).branch };
|
|
17456
17696
|
};
|
|
17457
17697
|
var isWorkitWorkspace = (repo) => {
|
|
17458
|
-
if (
|
|
17698
|
+
if (fs15.existsSync(repo.location.dir))
|
|
17459
17699
|
return true;
|
|
17460
17700
|
try {
|
|
17461
17701
|
return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
|
|
@@ -17476,17 +17716,17 @@ var headSha2 = (repo) => {
|
|
|
17476
17716
|
if (!git || git.reftable)
|
|
17477
17717
|
return null;
|
|
17478
17718
|
try {
|
|
17479
|
-
const head =
|
|
17719
|
+
const head = fs15.readFileSync(path18.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
17480
17720
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
17481
17721
|
return head;
|
|
17482
17722
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
17483
17723
|
if (!ref)
|
|
17484
17724
|
return null;
|
|
17485
|
-
const common =
|
|
17725
|
+
const common = path18.dirname(repo.location.dir);
|
|
17486
17726
|
try {
|
|
17487
|
-
return
|
|
17727
|
+
return fs15.readFileSync(path18.join(common, ref), "utf8").trim() || null;
|
|
17488
17728
|
} catch {}
|
|
17489
|
-
const packed =
|
|
17729
|
+
const packed = fs15.readFileSync(path18.join(common, "packed-refs"), "utf8");
|
|
17490
17730
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
17491
17731
|
} catch {
|
|
17492
17732
|
return null;
|
|
@@ -17574,8 +17814,8 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
|
|
|
17574
17814
|
const sub = invocation.args[0];
|
|
17575
17815
|
if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
|
|
17576
17816
|
return [invocation];
|
|
17577
|
-
const dir =
|
|
17578
|
-
if (!
|
|
17817
|
+
const dir = path18.resolve(root, invocation.dir ?? ".");
|
|
17818
|
+
if (!fs15.existsSync(dir))
|
|
17579
17819
|
return [invocation];
|
|
17580
17820
|
const config = gitConfig(dir, invocation, cache);
|
|
17581
17821
|
const alias = configValue(config, `alias.${sub.toLowerCase()}`);
|
|
@@ -17597,11 +17837,11 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
|
|
|
17597
17837
|
var normalizedUrl = (url, dir) => {
|
|
17598
17838
|
const trimmed = url.trim();
|
|
17599
17839
|
if (/^file:\/\//i.test(trimmed))
|
|
17600
|
-
return
|
|
17840
|
+
return path18.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
|
|
17601
17841
|
const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
|
|
17602
17842
|
const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
|
|
17603
17843
|
if (!scheme && !scp)
|
|
17604
|
-
return
|
|
17844
|
+
return path18.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
|
|
17605
17845
|
const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
|
|
17606
17846
|
return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
|
|
17607
17847
|
};
|
|
@@ -17825,7 +18065,7 @@ var actionsIn = (cwd, command, dialect, cache = new Map) => {
|
|
|
17825
18065
|
const out = [];
|
|
17826
18066
|
const repos = new Map;
|
|
17827
18067
|
const repoFor = (relative) => {
|
|
17828
|
-
const dir =
|
|
18068
|
+
const dir = path18.resolve(cwd, relative ?? ".");
|
|
17829
18069
|
if (!repos.has(dir)) {
|
|
17830
18070
|
const repo = repoAt(dir);
|
|
17831
18071
|
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
@@ -17910,8 +18150,8 @@ var analysisDenial = (error) => ({
|
|
|
17910
18150
|
unblock: SPLIT_IT
|
|
17911
18151
|
});
|
|
17912
18152
|
var MARKER_TTL_SECONDS = 3600;
|
|
17913
|
-
var markerFile = (repo, session, key) =>
|
|
17914
|
-
var callKey = (toolUseId, command) => toolUseId ?? `cmd:${
|
|
18153
|
+
var markerFile = (repo, session, key) => path18.join(repo.location.dir, "hooks", `commit-${createHash13("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
|
|
18154
|
+
var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash13("sha256").update(command).digest("hex")}`;
|
|
17915
18155
|
function noteRawCommit(input, command, key) {
|
|
17916
18156
|
try {
|
|
17917
18157
|
if (!input.session.id || !/\bcommit\b/.test(command))
|
|
@@ -17921,8 +18161,8 @@ function noteRawCommit(input, command, key) {
|
|
|
17921
18161
|
if (action.kind !== "commit")
|
|
17922
18162
|
continue;
|
|
17923
18163
|
const file = markerFile(repo, input.session.id, key);
|
|
17924
|
-
|
|
17925
|
-
|
|
18164
|
+
fs15.mkdirSync(path18.dirname(file), { recursive: true });
|
|
18165
|
+
fs15.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
17926
18166
|
`);
|
|
17927
18167
|
}
|
|
17928
18168
|
} catch {}
|
|
@@ -17945,11 +18185,11 @@ var headCommit = (dir) => {
|
|
|
17945
18185
|
var settle2 = (input, repo, file) => {
|
|
17946
18186
|
let marker;
|
|
17947
18187
|
try {
|
|
17948
|
-
marker = JSON.parse(
|
|
18188
|
+
marker = JSON.parse(fs15.readFileSync(file, "utf8"));
|
|
17949
18189
|
} catch {
|
|
17950
18190
|
return;
|
|
17951
18191
|
}
|
|
17952
|
-
|
|
18192
|
+
fs15.rmSync(file, { force: true });
|
|
17953
18193
|
const session = input.session.id;
|
|
17954
18194
|
const now = Date.now() / 1000;
|
|
17955
18195
|
if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
|
|
@@ -18140,7 +18380,7 @@ var judgedDir = (root, dir) => {
|
|
|
18140
18380
|
return null;
|
|
18141
18381
|
if (dir === null)
|
|
18142
18382
|
return root;
|
|
18143
|
-
const resolved =
|
|
18383
|
+
const resolved = path19.resolve(root, dir);
|
|
18144
18384
|
return existsSync6(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
|
|
18145
18385
|
};
|
|
18146
18386
|
var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
|
|
@@ -18376,7 +18616,7 @@ var judgeInputSummary = (raw) => {
|
|
|
18376
18616
|
|
|
18377
18617
|
// packages/workit-core/src/autonomy.ts
|
|
18378
18618
|
import os5 from "node:os";
|
|
18379
|
-
import
|
|
18619
|
+
import path20 from "node:path";
|
|
18380
18620
|
|
|
18381
18621
|
// packages/workit-core/src/core/safe-write.ts
|
|
18382
18622
|
import {
|
|
@@ -18410,10 +18650,10 @@ var accountHome = () => {
|
|
|
18410
18650
|
}
|
|
18411
18651
|
};
|
|
18412
18652
|
var grantsHome = { resolve: accountHome };
|
|
18413
|
-
var grantsDir = () =>
|
|
18653
|
+
var grantsDir = () => path20.join(grantsHome.resolve(), ".config", "workit");
|
|
18414
18654
|
var grantsOverride = () => {
|
|
18415
|
-
const configured =
|
|
18416
|
-
return configured ===
|
|
18655
|
+
const configured = path20.resolve(resolveConfigDir());
|
|
18656
|
+
return configured === path20.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 ${path20.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
|
|
18417
18657
|
};
|
|
18418
18658
|
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
18419
18659
|
var DEFAULT_GRANTS = Object.freeze({
|
|
@@ -20185,7 +20425,7 @@ class WorkitCore {
|
|
|
20185
20425
|
// packages/workit-core/src/core/repo-context.ts
|
|
20186
20426
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
20187
20427
|
import { existsSync as existsSync7, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
|
|
20188
|
-
import
|
|
20428
|
+
import path21 from "node:path";
|
|
20189
20429
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
20190
20430
|
try {
|
|
20191
20431
|
gitRevisionParts(value);
|
|
@@ -20319,7 +20559,7 @@ var findPrTemplate = (cwd) => {
|
|
|
20319
20559
|
]) {
|
|
20320
20560
|
const parts = rel.split("/");
|
|
20321
20561
|
const base = parts[parts.length - 1];
|
|
20322
|
-
const dir = parts.length > 1 ?
|
|
20562
|
+
const dir = parts.length > 1 ? path21.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
20323
20563
|
const actual = probeCaseInsensitive(dir, base);
|
|
20324
20564
|
if (actual)
|
|
20325
20565
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -20350,7 +20590,7 @@ var diffStatForRange = (cwd, range) => {
|
|
|
20350
20590
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
20351
20591
|
};
|
|
20352
20592
|
var packageScripts = (cwd, keys) => {
|
|
20353
|
-
const pkgPath =
|
|
20593
|
+
const pkgPath = path21.join(cwd, "package.json");
|
|
20354
20594
|
if (!existsSync7(pkgPath))
|
|
20355
20595
|
return [];
|
|
20356
20596
|
try {
|
|
@@ -20376,9 +20616,9 @@ var documentationFiles = (cwd) => {
|
|
|
20376
20616
|
if (entry.isDirectory()) {
|
|
20377
20617
|
if (excluded.has(entry.name))
|
|
20378
20618
|
continue;
|
|
20379
|
-
walk(
|
|
20619
|
+
walk(path21.join(dir, entry.name), depth + 1);
|
|
20380
20620
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
20381
|
-
matches.push("./" +
|
|
20621
|
+
matches.push("./" + path21.relative(cwd, path21.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
20382
20622
|
}
|
|
20383
20623
|
}
|
|
20384
20624
|
};
|
|
@@ -20394,7 +20634,7 @@ var readTrimmed2 = (file, maxLines) => {
|
|
|
20394
20634
|
`);
|
|
20395
20635
|
};
|
|
20396
20636
|
function prReadyContext(root, range) {
|
|
20397
|
-
const cwd =
|
|
20637
|
+
const cwd = path21.resolve(repoRoot(root));
|
|
20398
20638
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
20399
20639
|
return invalidRange(cwd);
|
|
20400
20640
|
let stdout = "";
|
|
@@ -20452,7 +20692,7 @@ ${status.stderr}` : "");
|
|
|
20452
20692
|
stdout += `template_path: ${template}
|
|
20453
20693
|
|
|
20454
20694
|
`;
|
|
20455
|
-
stdout += readTrimmed2(
|
|
20695
|
+
stdout += readTrimmed2(path21.join(cwd, template), 220);
|
|
20456
20696
|
} else {
|
|
20457
20697
|
stdout += `template_path: none
|
|
20458
20698
|
|
|
@@ -20462,7 +20702,7 @@ ${status.stderr}` : "");
|
|
|
20462
20702
|
stdout += `
|
|
20463
20703
|
`;
|
|
20464
20704
|
stdout += printSection("Recent Validation Signals");
|
|
20465
|
-
if (existsSync7(
|
|
20705
|
+
if (existsSync7(path21.join(cwd, "package.json"))) {
|
|
20466
20706
|
stdout += `package.json detected. Common scripts:
|
|
20467
20707
|
`;
|
|
20468
20708
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -20471,7 +20711,7 @@ ${status.stderr}` : "");
|
|
|
20471
20711
|
`) + `
|
|
20472
20712
|
`;
|
|
20473
20713
|
}
|
|
20474
|
-
if (existsSync7(
|
|
20714
|
+
if (existsSync7(path21.join(cwd, "Cargo.toml")) || existsSync7(path21.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
20475
20715
|
stdout += `Rust project detected.
|
|
20476
20716
|
`;
|
|
20477
20717
|
}
|
|
@@ -20504,7 +20744,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
20504
20744
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
20505
20745
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
20506
20746
|
function changelogContext(root, range) {
|
|
20507
|
-
const cwd =
|
|
20747
|
+
const cwd = path21.resolve(repoRoot(root));
|
|
20508
20748
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
20509
20749
|
return invalidRange(cwd);
|
|
20510
20750
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -20522,7 +20762,7 @@ function changelogContext(root, range) {
|
|
|
20522
20762
|
stdout += CHANGELOG_RULES + `
|
|
20523
20763
|
`;
|
|
20524
20764
|
stdout += printSection("Existing CHANGELOG.md");
|
|
20525
|
-
const changelogPath =
|
|
20765
|
+
const changelogPath = path21.join(cwd, "CHANGELOG.md");
|
|
20526
20766
|
if (existsSync7(changelogPath)) {
|
|
20527
20767
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
20528
20768
|
`;
|
|
@@ -20542,7 +20782,7 @@ function changelogContext(root, range) {
|
|
|
20542
20782
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
20543
20783
|
}
|
|
20544
20784
|
function docsRefreshContext(root, range) {
|
|
20545
|
-
const cwd =
|
|
20785
|
+
const cwd = path21.resolve(repoRoot(root));
|
|
20546
20786
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
20547
20787
|
return invalidRange(cwd);
|
|
20548
20788
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -20564,7 +20804,7 @@ function docsRefreshContext(root, range) {
|
|
|
20564
20804
|
`) + `
|
|
20565
20805
|
`;
|
|
20566
20806
|
stdout += printSection("README Preview");
|
|
20567
|
-
const readme =
|
|
20807
|
+
const readme = path21.join(cwd, "README.md");
|
|
20568
20808
|
if (existsSync7(readme)) {
|
|
20569
20809
|
stdout += readTrimmed2(readme, 220) + `
|
|
20570
20810
|
`;
|
|
@@ -20573,7 +20813,7 @@ function docsRefreshContext(root, range) {
|
|
|
20573
20813
|
`;
|
|
20574
20814
|
}
|
|
20575
20815
|
stdout += printSection("Package Scripts");
|
|
20576
|
-
const pkgPath =
|
|
20816
|
+
const pkgPath = path21.join(cwd, "package.json");
|
|
20577
20817
|
if (existsSync7(pkgPath)) {
|
|
20578
20818
|
try {
|
|
20579
20819
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
@@ -20587,7 +20827,7 @@ function docsRefreshContext(root, range) {
|
|
|
20587
20827
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
20588
20828
|
}
|
|
20589
20829
|
function releaseNotesContext(root, rangeOrTag) {
|
|
20590
|
-
const cwd =
|
|
20830
|
+
const cwd = path21.resolve(repoRoot(root));
|
|
20591
20831
|
if (!rangeOrTag) {
|
|
20592
20832
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
20593
20833
|
`, exitCode: 1, cwd };
|
|
@@ -20625,7 +20865,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
20625
20865
|
`;
|
|
20626
20866
|
stdout += printSection("Existing Release Files");
|
|
20627
20867
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
20628
|
-
if (existsSync7(
|
|
20868
|
+
if (existsSync7(path21.join(cwd, rel)))
|
|
20629
20869
|
stdout += rel + `
|
|
20630
20870
|
`;
|
|
20631
20871
|
}
|
|
@@ -20656,8 +20896,8 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
20656
20896
|
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
20657
20897
|
|
|
20658
20898
|
// packages/workit-core/src/core/pr-create.ts
|
|
20659
|
-
import
|
|
20660
|
-
import
|
|
20899
|
+
import fs16 from "node:fs";
|
|
20900
|
+
import path22 from "node:path";
|
|
20661
20901
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
20662
20902
|
// packages/workit-core/src/forge/pr-body.ts
|
|
20663
20903
|
function parseGhIssue(value) {
|
|
@@ -20668,13 +20908,13 @@ function parseGhIssue(value) {
|
|
|
20668
20908
|
// packages/workit-core/src/core/pr-create.ts
|
|
20669
20909
|
function whichOnPath(tool) {
|
|
20670
20910
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
20671
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
20911
|
+
for (const dir of (process.env.PATH ?? "").split(path22.delimiter)) {
|
|
20672
20912
|
if (!dir)
|
|
20673
20913
|
continue;
|
|
20674
20914
|
for (const name of names) {
|
|
20675
|
-
const candidate =
|
|
20915
|
+
const candidate = path22.join(dir, name);
|
|
20676
20916
|
try {
|
|
20677
|
-
|
|
20917
|
+
fs16.accessSync(candidate, fs16.constants.X_OK);
|
|
20678
20918
|
return candidate;
|
|
20679
20919
|
} catch {}
|
|
20680
20920
|
}
|
|
@@ -20792,8 +21032,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
|
20792
21032
|
};
|
|
20793
21033
|
|
|
20794
21034
|
// packages/workit-core/src/core/youtrack.ts
|
|
20795
|
-
import
|
|
20796
|
-
import
|
|
21035
|
+
import fs17 from "node:fs";
|
|
21036
|
+
import path23 from "node:path";
|
|
20797
21037
|
|
|
20798
21038
|
// packages/workit-core/src/core/templates.ts
|
|
20799
21039
|
var repoRoot2 = assetRoot();
|
|
@@ -20806,22 +21046,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
20806
21046
|
// packages/workit-core/src/core/youtrack.ts
|
|
20807
21047
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
20808
21048
|
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
20809
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
20810
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
21049
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path23.join(configDir(), "youtrack.json");
|
|
21050
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path23.join(resolveConfigDir(), "youtrack.json");
|
|
20811
21051
|
var youTrackTokenModeOk = (p) => {
|
|
20812
21052
|
if (process.platform === "win32")
|
|
20813
21053
|
return true;
|
|
20814
|
-
const mode =
|
|
21054
|
+
const mode = fs17.statSync(p).mode & 511;
|
|
20815
21055
|
return mode === 384;
|
|
20816
21056
|
};
|
|
20817
21057
|
function readYouTrackConfig(required) {
|
|
20818
21058
|
const cfgPath = youTrackConfigPath();
|
|
20819
|
-
if (!
|
|
21059
|
+
if (!fs17.existsSync(cfgPath)) {
|
|
20820
21060
|
return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
|
|
20821
21061
|
}
|
|
20822
21062
|
let parsed;
|
|
20823
21063
|
try {
|
|
20824
|
-
parsed = JSON.parse(
|
|
21064
|
+
parsed = JSON.parse(fs17.readFileSync(cfgPath, "utf8"));
|
|
20825
21065
|
} catch {
|
|
20826
21066
|
return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
|
|
20827
21067
|
}
|
|
@@ -20832,11 +21072,11 @@ function readYouTrackConfig(required) {
|
|
|
20832
21072
|
}
|
|
20833
21073
|
var readYouTrackContextConfig = () => {
|
|
20834
21074
|
const cfgPath = youTrackReadOnlyConfigPath();
|
|
20835
|
-
if (!
|
|
21075
|
+
if (!fs17.existsSync(cfgPath))
|
|
20836
21076
|
return { data: {} };
|
|
20837
21077
|
let parsed;
|
|
20838
21078
|
try {
|
|
20839
|
-
parsed = JSON.parse(
|
|
21079
|
+
parsed = JSON.parse(fs17.readFileSync(cfgPath, "utf8"));
|
|
20840
21080
|
} catch {
|
|
20841
21081
|
return { error: "YouTrack configuration is unavailable" };
|
|
20842
21082
|
}
|
|
@@ -20875,12 +21115,12 @@ var youTrackToken = () => {
|
|
|
20875
21115
|
if ("error" in loaded)
|
|
20876
21116
|
return loaded;
|
|
20877
21117
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
20878
|
-
const tokenPath = tokenFile ?
|
|
20879
|
-
if (!tokenPath || !
|
|
21118
|
+
const tokenPath = tokenFile ? path23.isAbsolute(tokenFile) ? path23.resolve(tokenFile) : path23.resolve(process.cwd(), tokenFile) : "";
|
|
21119
|
+
if (!tokenPath || !fs17.existsSync(tokenPath))
|
|
20880
21120
|
return { error: "missing youtrack.token" };
|
|
20881
21121
|
if (!youTrackTokenModeOk(tokenPath))
|
|
20882
21122
|
return { error: "youtrack.token mode must be 0600" };
|
|
20883
|
-
const token =
|
|
21123
|
+
const token = fs17.readFileSync(tokenPath, "utf8").trim();
|
|
20884
21124
|
if (!token)
|
|
20885
21125
|
return { error: "empty token file" };
|
|
20886
21126
|
if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
|
|
@@ -20926,7 +21166,7 @@ function youTrackTokenCreateUrl() {
|
|
|
20926
21166
|
const desc = String(defaults.description ?? "OpenCode workit — /wk-issue-update and /wk-meetings");
|
|
20927
21167
|
const scopes = Array.isArray(defaults.scopes) ? defaults.scopes : ["YouTrack"];
|
|
20928
21168
|
const base = String(config.baseUrl ?? "").replace(/\/+$/, "");
|
|
20929
|
-
const tokenFile = String(config.tokenFile ??
|
|
21169
|
+
const tokenFile = String(config.tokenFile ?? path23.join(path23.dirname(loaded.path), "youtrack.token"));
|
|
20930
21170
|
const tab = String(defaults.profileTab ?? "account-security");
|
|
20931
21171
|
const createUrl = base ? `${base}/users/me?${new URLSearchParams({ tab })}` : null;
|
|
20932
21172
|
const docsUrl = "https://www.jetbrains.com/help/youtrack/cloud/manage-permanent-token.html";
|
|
@@ -20935,7 +21175,7 @@ function youTrackTokenCreateUrl() {
|
|
|
20935
21175
|
tokenName: name,
|
|
20936
21176
|
tokenDescription: desc,
|
|
20937
21177
|
scopes,
|
|
20938
|
-
tokenFile:
|
|
21178
|
+
tokenFile: path23.resolve(tokenFile),
|
|
20939
21179
|
createUrl,
|
|
20940
21180
|
docsUrl,
|
|
20941
21181
|
prefillSupported: false,
|
|
@@ -20981,9 +21221,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
|
|
|
20981
21221
|
} catch {
|
|
20982
21222
|
continue;
|
|
20983
21223
|
}
|
|
20984
|
-
if (!
|
|
21224
|
+
if (!fs17.existsSync(full))
|
|
20985
21225
|
continue;
|
|
20986
|
-
const text =
|
|
21226
|
+
const text = fs17.readFileSync(full, "utf8");
|
|
20987
21227
|
const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
|
|
20988
21228
|
if (m)
|
|
20989
21229
|
return m[1];
|
|
@@ -21275,15 +21515,15 @@ function resolveGitRevision(root, value) {
|
|
|
21275
21515
|
}
|
|
21276
21516
|
function resolveInside(root, candidate) {
|
|
21277
21517
|
const base = realpathSync5(root);
|
|
21278
|
-
const target =
|
|
21279
|
-
if (target !== base && !target.startsWith(base +
|
|
21518
|
+
const target = path24.resolve(base, candidate);
|
|
21519
|
+
if (target !== base && !target.startsWith(base + path24.sep)) {
|
|
21280
21520
|
throw new Error("path must stay inside repository root");
|
|
21281
21521
|
}
|
|
21282
21522
|
let ancestor = target;
|
|
21283
21523
|
while (!existsSync8(ancestor))
|
|
21284
|
-
ancestor =
|
|
21524
|
+
ancestor = path24.dirname(ancestor);
|
|
21285
21525
|
const canonicalAncestor = realpathSync5(ancestor);
|
|
21286
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
21526
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path24.sep)) {
|
|
21287
21527
|
throw new Error("path must stay inside repository root");
|
|
21288
21528
|
}
|
|
21289
21529
|
return target;
|
|
@@ -21327,7 +21567,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
21327
21567
|
};
|
|
21328
21568
|
});
|
|
21329
21569
|
// packages/workit-core/src/hooks/context.ts
|
|
21330
|
-
import
|
|
21570
|
+
import path25 from "node:path";
|
|
21331
21571
|
var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
|
|
21332
21572
|
var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
|
|
21333
21573
|
var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
|
|
@@ -21412,7 +21652,7 @@ function entryCompactContext(store, entry, session, context) {
|
|
|
21412
21652
|
return null;
|
|
21413
21653
|
const freshness = { mode: "signal", current: () => recentSignal(store.root) };
|
|
21414
21654
|
const observed = task?.data.evidence.some(isObservedCheck) ?? false;
|
|
21415
|
-
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [
|
|
21655
|
+
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path25.resolve(store.root, ref.path)] : []));
|
|
21416
21656
|
const observedFiles = filesKey(files);
|
|
21417
21657
|
const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
|
|
21418
21658
|
if (!compact.ok || !compact.data)
|
|
@@ -21444,27 +21684,27 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
|
|
|
21444
21684
|
};
|
|
21445
21685
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
21446
21686
|
import { realpathSync as realpathSync6 } from "node:fs";
|
|
21447
|
-
import
|
|
21687
|
+
import path26 from "node:path";
|
|
21448
21688
|
var NONE3 = { kind: "none" };
|
|
21449
21689
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
21450
21690
|
var canonical2 = (file) => {
|
|
21451
21691
|
let rest = "";
|
|
21452
|
-
for (let dir =
|
|
21692
|
+
for (let dir = path26.resolve(file);; dir = path26.dirname(dir)) {
|
|
21453
21693
|
try {
|
|
21454
|
-
return
|
|
21694
|
+
return path26.join(realpathSync6(dir), rest);
|
|
21455
21695
|
} catch {
|
|
21456
|
-
if (
|
|
21457
|
-
return
|
|
21458
|
-
rest = rest ?
|
|
21696
|
+
if (path26.dirname(dir) === dir)
|
|
21697
|
+
return path26.resolve(file);
|
|
21698
|
+
rest = rest ? path26.join(path26.basename(dir), rest) : path26.basename(dir);
|
|
21459
21699
|
}
|
|
21460
21700
|
}
|
|
21461
21701
|
};
|
|
21462
21702
|
var exempt = (cwd, root, file, plans) => {
|
|
21463
21703
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
21464
|
-
const relative =
|
|
21465
|
-
if (relative.startsWith("..") ||
|
|
21704
|
+
const relative = path26.relative(fold(canonical2(root)), fold(canonical2(path26.resolve(cwd, file))));
|
|
21705
|
+
if (relative.startsWith("..") || path26.isAbsolute(relative))
|
|
21466
21706
|
return true;
|
|
21467
|
-
const posix = relative.split(
|
|
21707
|
+
const posix = relative.split(path26.sep).join("/");
|
|
21468
21708
|
const segments = posix.split("/");
|
|
21469
21709
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
21470
21710
|
};
|
|
@@ -21562,7 +21802,7 @@ var shellWrites = (command) => {
|
|
|
21562
21802
|
return { writes: true, targets: unknown ? null : targets };
|
|
21563
21803
|
};
|
|
21564
21804
|
var cache2 = new Map;
|
|
21565
|
-
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
21805
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path26.resolve(root, file)) ?? "-").join("\x00");
|
|
21566
21806
|
var gateFor = (cwd) => {
|
|
21567
21807
|
const store = new TaskStore(cwd);
|
|
21568
21808
|
const listed = store.listTaskIndex();
|
|
@@ -21730,10 +21970,10 @@ function stopObligation(input, descriptor) {
|
|
|
21730
21970
|
const branch = currentBranch(input.cwd);
|
|
21731
21971
|
if (!branch || guardedBranch(input.cwd, branch))
|
|
21732
21972
|
return null;
|
|
21733
|
-
const ledger = readLedger(input.cwd);
|
|
21973
|
+
const ledger = readLedger(input.cwd, { integrity: true });
|
|
21734
21974
|
if (!ledger.ok)
|
|
21735
21975
|
return null;
|
|
21736
|
-
const all = ledger.value.rows;
|
|
21976
|
+
const all = ledger.value.rows.filter((row) => row.integrity !== "unverified");
|
|
21737
21977
|
const rows = all.filter((row) => row.branch === branch);
|
|
21738
21978
|
const mine = rows.filter((row) => sessionOf(row) === session);
|
|
21739
21979
|
const commits = new Set(mine.filter((row) => row.type === "commit.recorded").map((row) => typeof row.sha === "string" ? row.sha : row.head).filter((sha) => typeof sha === "string"));
|
|
@@ -22101,11 +22341,11 @@ var OPENCODE_DESCRIPTOR = {
|
|
|
22101
22341
|
]
|
|
22102
22342
|
};
|
|
22103
22343
|
// packages/workit-opencode/src/shared/init-apply.ts
|
|
22104
|
-
import
|
|
22344
|
+
import path31 from "node:path";
|
|
22105
22345
|
|
|
22106
22346
|
// packages/workit-core/src/core/gitignore.ts
|
|
22107
22347
|
import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
22108
|
-
import
|
|
22348
|
+
import path27 from "node:path";
|
|
22109
22349
|
var GITIGNORE_ENTRIES = [
|
|
22110
22350
|
"# workit: SDD working state (never commit)",
|
|
22111
22351
|
"docs/*/sdd/",
|
|
@@ -22125,7 +22365,7 @@ var GITIGNORE_ENTRIES = [
|
|
|
22125
22365
|
var ensureProjectGitignore = (workspaceRoot, confirmed) => {
|
|
22126
22366
|
if (!confirmed)
|
|
22127
22367
|
return { ok: false, error: "confirmed: true required" };
|
|
22128
|
-
const file =
|
|
22368
|
+
const file = path27.join(workspaceRoot, ".gitignore");
|
|
22129
22369
|
const existing = existsSync9(file) ? readFileSync6(file, "utf8") : "";
|
|
22130
22370
|
const existingLines = new Set(existing.split(`
|
|
22131
22371
|
`).map((l) => l.trim()).filter(Boolean));
|
|
@@ -22152,26 +22392,26 @@ var ensureProjectGitignore = (workspaceRoot, confirmed) => {
|
|
|
22152
22392
|
};
|
|
22153
22393
|
|
|
22154
22394
|
// packages/workit-core/src/core/hygiene.ts
|
|
22155
|
-
import { existsSync as existsSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
|
|
22156
|
-
import
|
|
22395
|
+
import { existsSync as existsSync10, readFileSync as readFileSync7, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
22396
|
+
import path28 from "node:path";
|
|
22157
22397
|
var repoRoot3 = assetRoot();
|
|
22158
|
-
var templatesDir = () =>
|
|
22398
|
+
var templatesDir = () => path28.join(repoRoot3, "templates", "hygiene");
|
|
22159
22399
|
var packageJson = (root) => {
|
|
22160
|
-
if (!existsSync10(
|
|
22400
|
+
if (!existsSync10(path28.join(root, "package.json")))
|
|
22161
22401
|
return null;
|
|
22162
22402
|
try {
|
|
22163
|
-
return JSON.parse(readFileSync7(
|
|
22403
|
+
return JSON.parse(readFileSync7(path28.join(root, "package.json"), "utf8"));
|
|
22164
22404
|
} catch {
|
|
22165
22405
|
return null;
|
|
22166
22406
|
}
|
|
22167
22407
|
};
|
|
22168
22408
|
var isOpenSource = (root) => {
|
|
22169
|
-
if (existsSync10(
|
|
22409
|
+
if (existsSync10(path28.join(root, "LICENSE")))
|
|
22170
22410
|
return true;
|
|
22171
22411
|
const pkg = packageJson(root);
|
|
22172
22412
|
if (pkg && !pkg.private)
|
|
22173
22413
|
return true;
|
|
22174
|
-
return
|
|
22414
|
+
return path28.basename(root) === "workflow-toolkit";
|
|
22175
22415
|
};
|
|
22176
22416
|
var licenseHolder = (root) => {
|
|
22177
22417
|
const pkg = packageJson(root);
|
|
@@ -22194,13 +22434,13 @@ var planHygieneFiles = (root, opts = {}) => {
|
|
|
22194
22434
|
files.push("LICENSE", "CONTRIBUTING.md");
|
|
22195
22435
|
const planned = [];
|
|
22196
22436
|
for (const file of files) {
|
|
22197
|
-
if (existsSync10(
|
|
22437
|
+
if (existsSync10(path28.join(root, file)))
|
|
22198
22438
|
continue;
|
|
22199
|
-
const tpl =
|
|
22439
|
+
const tpl = path28.join(templatesDir(), file);
|
|
22200
22440
|
if (!existsSync10(tpl))
|
|
22201
22441
|
continue;
|
|
22202
|
-
const content = readFileSync7(tpl, "utf8").replace(/<PROJECT>/g,
|
|
22203
|
-
planned.push({ path:
|
|
22442
|
+
const content = readFileSync7(tpl, "utf8").replace(/<PROJECT>/g, path28.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
|
|
22443
|
+
planned.push({ path: path28.join(root, file), content });
|
|
22204
22444
|
}
|
|
22205
22445
|
return planned;
|
|
22206
22446
|
};
|
|
@@ -22210,19 +22450,19 @@ var ensureHygieneFiles = (root, opts) => {
|
|
|
22210
22450
|
const created = [];
|
|
22211
22451
|
for (const planned of planHygieneFiles(root, opts)) {
|
|
22212
22452
|
writeFileSync5(planned.path, planned.content, "utf8");
|
|
22213
|
-
created.push(
|
|
22453
|
+
created.push(path28.basename(planned.path));
|
|
22214
22454
|
}
|
|
22215
22455
|
return { ok: true, created };
|
|
22216
22456
|
};
|
|
22217
22457
|
|
|
22218
22458
|
// packages/workit-core/src/core/init.ts
|
|
22219
|
-
import
|
|
22220
|
-
import
|
|
22459
|
+
import fs18 from "node:fs";
|
|
22460
|
+
import path30 from "node:path";
|
|
22221
22461
|
import { spawnSync as spawnSync18 } from "node:child_process";
|
|
22222
22462
|
|
|
22223
22463
|
// packages/workit-core/src/core/workspace-branch-policy.ts
|
|
22224
22464
|
import { mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
|
|
22225
|
-
import
|
|
22465
|
+
import path29 from "node:path";
|
|
22226
22466
|
import { isDeepStrictEqual } from "node:util";
|
|
22227
22467
|
var readFileSafe = (p) => {
|
|
22228
22468
|
try {
|
|
@@ -22233,7 +22473,7 @@ var readFileSafe = (p) => {
|
|
|
22233
22473
|
};
|
|
22234
22474
|
function applyWorkspaceBranchPolicy(opts) {
|
|
22235
22475
|
const { workspace_root, env = process.env } = opts;
|
|
22236
|
-
const dir =
|
|
22476
|
+
const dir = path29.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
|
|
22237
22477
|
const {
|
|
22238
22478
|
status,
|
|
22239
22479
|
path: wsPath,
|
|
@@ -22245,7 +22485,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
22245
22485
|
if (status === "malformed" || status === "invalid")
|
|
22246
22486
|
return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
|
|
22247
22487
|
const detection = detectBranchPolicy(workspace_root);
|
|
22248
|
-
const name = String(env.WORKFLOW_BP_NAME ??
|
|
22488
|
+
const name = String(env.WORKFLOW_BP_NAME ?? path29.basename(workspace_root));
|
|
22249
22489
|
const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
|
|
22250
22490
|
const policy = {
|
|
22251
22491
|
preset: detection.preset,
|
|
@@ -22294,7 +22534,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
22294
22534
|
ok: false,
|
|
22295
22535
|
error: `workspaces.json changed while applying the branch policy: ${wsPath}`
|
|
22296
22536
|
};
|
|
22297
|
-
mkdirSync4(
|
|
22537
|
+
mkdirSync4(path29.dirname(wsPath), { recursive: true });
|
|
22298
22538
|
const tmp = `${wsPath}.${process.pid}.tmp`;
|
|
22299
22539
|
try {
|
|
22300
22540
|
writeFileSync6(tmp, JSON.stringify({ ...document, workspaces: next }, null, 2) + `
|
|
@@ -22329,7 +22569,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
22329
22569
|
var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE";
|
|
22330
22570
|
var readJson = (p) => {
|
|
22331
22571
|
try {
|
|
22332
|
-
const parsed = JSON.parse(
|
|
22572
|
+
const parsed = JSON.parse(fs18.readFileSync(p, "utf8"));
|
|
22333
22573
|
return isConfigObject(parsed) ? parsed : null;
|
|
22334
22574
|
} catch {
|
|
22335
22575
|
return null;
|
|
@@ -22359,7 +22599,7 @@ var youtrackJsonContent = (dir) => {
|
|
|
22359
22599
|
const meetingIssue = process.env.WORKFLOW_YT_MEETING_ISSUE;
|
|
22360
22600
|
return {
|
|
22361
22601
|
baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://youtrack.example.com",
|
|
22362
|
-
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ??
|
|
22602
|
+
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path30.join(dir, "youtrack.token"),
|
|
22363
22603
|
...meetingIssue ? {
|
|
22364
22604
|
meetingIssue,
|
|
22365
22605
|
meetingIssues: { general: { issue: meetingIssue, label: "General meetings" } }
|
|
@@ -22389,19 +22629,19 @@ var vcsJsonContent = () => {
|
|
|
22389
22629
|
};
|
|
22390
22630
|
function initApplyData(action, env = process.env) {
|
|
22391
22631
|
const dir = String(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
|
|
22392
|
-
|
|
22632
|
+
fs18.mkdirSync(dir, { recursive: true });
|
|
22393
22633
|
switch (action) {
|
|
22394
22634
|
case "youtrack_json": {
|
|
22395
|
-
const out =
|
|
22396
|
-
|
|
22635
|
+
const out = path30.join(dir, "youtrack.json");
|
|
22636
|
+
fs18.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
|
|
22397
22637
|
`, "utf8");
|
|
22398
22638
|
return { action, ok: true, path: out };
|
|
22399
22639
|
}
|
|
22400
22640
|
case "youtrack_token_placeholder": {
|
|
22401
|
-
const p =
|
|
22641
|
+
const p = path30.join(dir, "youtrack.token");
|
|
22402
22642
|
const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER2 + `
|
|
22403
22643
|
`, 384) === "preserved";
|
|
22404
|
-
const abs =
|
|
22644
|
+
const abs = path30.resolve(p);
|
|
22405
22645
|
return {
|
|
22406
22646
|
action,
|
|
22407
22647
|
ok: true,
|
|
@@ -22413,14 +22653,14 @@ function initApplyData(action, env = process.env) {
|
|
|
22413
22653
|
};
|
|
22414
22654
|
}
|
|
22415
22655
|
case "youtrack_scaffold": {
|
|
22416
|
-
const jsonOut =
|
|
22417
|
-
const tokenOut =
|
|
22418
|
-
|
|
22656
|
+
const jsonOut = path30.join(dir, "youtrack.json");
|
|
22657
|
+
const tokenOut = path30.join(dir, "youtrack.token");
|
|
22658
|
+
fs18.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
|
|
22419
22659
|
`, "utf8");
|
|
22420
22660
|
const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER2 + `
|
|
22421
22661
|
`, 384) === "preserved";
|
|
22422
|
-
const configPath =
|
|
22423
|
-
const tokenPath =
|
|
22662
|
+
const configPath = path30.resolve(jsonOut);
|
|
22663
|
+
const tokenPath = path30.resolve(tokenOut);
|
|
22424
22664
|
const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
|
|
22425
22665
|
process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
|
|
22426
22666
|
let tokenCreate = {};
|
|
@@ -22472,10 +22712,10 @@ function initApplyData(action, env = process.env) {
|
|
|
22472
22712
|
}
|
|
22473
22713
|
}
|
|
22474
22714
|
case "vcs_scaffold": {
|
|
22475
|
-
const jsonOut =
|
|
22476
|
-
|
|
22715
|
+
const jsonOut = path30.join(dir, "vcs.json");
|
|
22716
|
+
fs18.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
|
|
22477
22717
|
`, "utf8");
|
|
22478
|
-
const configPath =
|
|
22718
|
+
const configPath = path30.resolve(jsonOut);
|
|
22479
22719
|
const prev = process.env.WORKFLOW_VCS_CONFIG;
|
|
22480
22720
|
process.env.WORKFLOW_VCS_CONFIG = configPath;
|
|
22481
22721
|
try {
|
|
@@ -22594,7 +22834,7 @@ var executeInitApply = (args, directory, runtime) => {
|
|
|
22594
22834
|
protectedNames: args.branch_policy_protected
|
|
22595
22835
|
}, current);
|
|
22596
22836
|
writeConfig(next);
|
|
22597
|
-
return output(ok({ action: "config", path:
|
|
22837
|
+
return output(ok({ action: "config", path: path31.join(configDir(), "config.json"), ...next }));
|
|
22598
22838
|
}
|
|
22599
22839
|
const env = Object.fromEntries(Object.entries({
|
|
22600
22840
|
WORKFLOW_YT_BASE_URL: args.base_url,
|
|
@@ -22611,14 +22851,14 @@ var executeInitApply = (args, directory, runtime) => {
|
|
|
22611
22851
|
|
|
22612
22852
|
// packages/workit-opencode/src/shared/session.ts
|
|
22613
22853
|
import { realpathSync as realpathSync7 } from "node:fs";
|
|
22614
|
-
import
|
|
22854
|
+
import path32 from "node:path";
|
|
22615
22855
|
var sameWorkspace = (expected, observed) => {
|
|
22616
22856
|
if (typeof observed !== "string" || !observed)
|
|
22617
22857
|
return false;
|
|
22618
22858
|
try {
|
|
22619
22859
|
return realpathSync7(expected) === realpathSync7(observed);
|
|
22620
22860
|
} catch {
|
|
22621
|
-
return
|
|
22861
|
+
return path32.resolve(expected) === path32.resolve(observed);
|
|
22622
22862
|
}
|
|
22623
22863
|
};
|
|
22624
22864
|
|
|
@@ -22690,7 +22930,7 @@ var workitFamilyOf = (toolName) => {
|
|
|
22690
22930
|
|
|
22691
22931
|
// packages/workit-opencode/src/tools/context.ts
|
|
22692
22932
|
import { realpathSync as realpathSync8 } from "node:fs";
|
|
22693
|
-
import
|
|
22933
|
+
import path33 from "node:path";
|
|
22694
22934
|
var createContextTool = () => ({
|
|
22695
22935
|
description: "Read Git, pull request, YouTrack, issue, changelog, release, or affected-file context.",
|
|
22696
22936
|
execute: async (args, context) => {
|
|
@@ -22700,7 +22940,7 @@ var createContextTool = () => ({
|
|
|
22700
22940
|
let root;
|
|
22701
22941
|
try {
|
|
22702
22942
|
const cwd = parsed.data.cwd;
|
|
22703
|
-
root = realpathSync8(cwd ?
|
|
22943
|
+
root = realpathSync8(cwd ? path33.resolve(context.directory, cwd) : context.directory);
|
|
22704
22944
|
} catch {
|
|
22705
22945
|
return JSON.stringify(failure2("invalid_input", "context target directory cannot be resolved"), null, 2);
|
|
22706
22946
|
}
|
|
@@ -23293,7 +23533,7 @@ var childTaskMissing = (store, input) => {
|
|
|
23293
23533
|
var childDenial = (identity) => `OpenCode child session has no validated Workit worker: as ${identity} it may only record evidence or findings (workit_evidence/workit_finding action "record") on an existing task; for anything else the lead assigns it a worker with workit_worker before launching it`;
|
|
23294
23534
|
|
|
23295
23535
|
// packages/workit-opencode/src/v2/shell.ts
|
|
23296
|
-
import
|
|
23536
|
+
import path34 from "node:path";
|
|
23297
23537
|
var commandOf = (input) => {
|
|
23298
23538
|
if (input === null || typeof input !== "object")
|
|
23299
23539
|
return null;
|
|
@@ -23302,7 +23542,7 @@ var commandOf = (input) => {
|
|
|
23302
23542
|
};
|
|
23303
23543
|
var hookInput = (root, sessionID, id, shell) => ({
|
|
23304
23544
|
host: "opencode",
|
|
23305
|
-
cwd: shell.workdir ?
|
|
23545
|
+
cwd: shell.workdir ? path34.resolve(root, shell.workdir) : root,
|
|
23306
23546
|
session: { id: sessionID, agentId: null, agentType: null, parentId: null },
|
|
23307
23547
|
permissionMode: null,
|
|
23308
23548
|
transcriptPath: null,
|
|
@@ -23487,37 +23727,37 @@ var createStopControl = (root, port) => {
|
|
|
23487
23727
|
|
|
23488
23728
|
// packages/workit-opencode/src/v2/registry.ts
|
|
23489
23729
|
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
|
|
23490
|
-
import
|
|
23730
|
+
import path36 from "node:path";
|
|
23491
23731
|
|
|
23492
23732
|
// packages/workit-opencode/src/shared/assets.ts
|
|
23493
23733
|
import { existsSync as existsSync11, realpathSync as realpathSync9 } from "node:fs";
|
|
23494
|
-
import
|
|
23734
|
+
import path35 from "node:path";
|
|
23495
23735
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
23496
23736
|
var moduleDir = () => {
|
|
23497
23737
|
try {
|
|
23498
|
-
return realpathSync9(
|
|
23738
|
+
return realpathSync9(path35.dirname(fileURLToPath2(import.meta.url)));
|
|
23499
23739
|
} catch {
|
|
23500
|
-
return
|
|
23740
|
+
return path35.dirname(fileURLToPath2(import.meta.url));
|
|
23501
23741
|
}
|
|
23502
23742
|
};
|
|
23503
23743
|
var packageRoot2 = () => {
|
|
23504
23744
|
const dir = moduleDir();
|
|
23505
23745
|
for (const candidate of [
|
|
23506
|
-
|
|
23507
|
-
|
|
23508
|
-
|
|
23746
|
+
path35.resolve(dir, ".."),
|
|
23747
|
+
path35.resolve(dir, "..", ".."),
|
|
23748
|
+
path35.resolve(dir, "..", "..", "..")
|
|
23509
23749
|
]) {
|
|
23510
|
-
if (existsSync11(
|
|
23750
|
+
if (existsSync11(path35.join(candidate, "package.json")))
|
|
23511
23751
|
return candidate;
|
|
23512
23752
|
}
|
|
23513
|
-
return
|
|
23753
|
+
return path35.resolve(dir, "..");
|
|
23514
23754
|
};
|
|
23515
23755
|
var skillsRoot = () => {
|
|
23516
23756
|
const root = packageRoot2();
|
|
23517
|
-
const canonical =
|
|
23518
|
-
if (existsSync11(
|
|
23757
|
+
const canonical = path35.resolve(root, "..", "workit-core", "skills");
|
|
23758
|
+
if (existsSync11(path35.join(root, "src")) && existsSync11(canonical))
|
|
23519
23759
|
return canonical;
|
|
23520
|
-
return
|
|
23760
|
+
return path35.join(root, "assets", "skills");
|
|
23521
23761
|
};
|
|
23522
23762
|
|
|
23523
23763
|
// packages/workit-opencode/src/v2/registry.ts
|
|
@@ -23544,11 +23784,11 @@ var registerSkills = async (ctx) => {
|
|
|
23544
23784
|
if (!existsSync12(skillsDir))
|
|
23545
23785
|
return new Set;
|
|
23546
23786
|
const listed = await ctx.skill.list().catch(() => ({ data: [] }));
|
|
23547
|
-
const own = (skill) => typeof skill.path === "string" &&
|
|
23787
|
+
const own = (skill) => typeof skill.path === "string" && path36.resolve(skill.path).startsWith(skillsDir + path36.sep);
|
|
23548
23788
|
const userSkills = new Set((listed?.data ?? []).filter((skill) => !own(skill)).map((skill) => skill.id));
|
|
23549
23789
|
const packaged = new Map;
|
|
23550
23790
|
for (const id of WORKIT_METHOD_SKILLS) {
|
|
23551
|
-
const file =
|
|
23791
|
+
const file = path36.join(skillsDir, id, "SKILL.md");
|
|
23552
23792
|
if (!userSkills.has(id) && existsSync12(file))
|
|
23553
23793
|
packaged.set(id, file);
|
|
23554
23794
|
}
|
|
@@ -23595,23 +23835,23 @@ var registerCommands = async (ctx, registeredSkills) => {
|
|
|
23595
23835
|
};
|
|
23596
23836
|
|
|
23597
23837
|
// packages/workit-opencode/src/stale-sources.ts
|
|
23598
|
-
import { existsSync as existsSync13, readdirSync as
|
|
23599
|
-
import
|
|
23838
|
+
import { existsSync as existsSync13, readdirSync as readdirSync6 } from "node:fs";
|
|
23839
|
+
import path37 from "node:path";
|
|
23600
23840
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
23601
23841
|
var typescriptFiles = (directory) => {
|
|
23602
23842
|
if (!existsSync13(directory))
|
|
23603
23843
|
return [];
|
|
23604
|
-
return
|
|
23605
|
-
const file =
|
|
23844
|
+
return readdirSync6(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
23845
|
+
const file = path37.join(directory, entry.name);
|
|
23606
23846
|
return entry.isDirectory() ? typescriptFiles(file) : entry.isFile() && file.endsWith(".ts") ? [file] : [];
|
|
23607
23847
|
});
|
|
23608
23848
|
};
|
|
23609
23849
|
var pluginSourceFiles = [
|
|
23610
23850
|
...new Set([
|
|
23611
23851
|
fileURLToPath3(import.meta.url),
|
|
23612
|
-
...typescriptFiles(
|
|
23613
|
-
|
|
23614
|
-
...typescriptFiles(
|
|
23852
|
+
...typescriptFiles(path37.join(packageRoot2(), "src")),
|
|
23853
|
+
path37.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
|
|
23854
|
+
...typescriptFiles(path37.join(packageRoot2(), "..", "workit-core", "src", "core"))
|
|
23615
23855
|
])
|
|
23616
23856
|
].filter((file) => existsSync13(file)).toSorted();
|
|
23617
23857
|
|