@brainervirus/workit-opencode 8.12.3 → 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";
|
|
@@ -12965,6 +13072,15 @@ var repoBranchPreset = (workspaceRoot) => {
|
|
|
12965
13072
|
const detected = detectBranchPolicy(workspaceRoot);
|
|
12966
13073
|
return detected.developBranch || detected.allowed.length ? detected.preset : null;
|
|
12967
13074
|
};
|
|
13075
|
+
var releaseTagPrefixOf = (workspaceRoot, policyPrefix) => {
|
|
13076
|
+
if (typeof policyPrefix === "string")
|
|
13077
|
+
return policyPrefix.trim();
|
|
13078
|
+
const r = spawnSync5("git", ["config", "--get", "gitflow.prefix.versiontag"], {
|
|
13079
|
+
cwd: workspaceRoot,
|
|
13080
|
+
encoding: "utf8"
|
|
13081
|
+
});
|
|
13082
|
+
return r.status === 0 ? (r.stdout ?? "").trim() : "v";
|
|
13083
|
+
};
|
|
12968
13084
|
|
|
12969
13085
|
// packages/workit-core/src/core/workspaces.ts
|
|
12970
13086
|
var nonBlank = string2().refine((value) => value.trim().length > 0, "must not be blank");
|
|
@@ -13024,7 +13140,7 @@ var releaseTrackSchema = object({
|
|
|
13024
13140
|
context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
|
|
13025
13141
|
}
|
|
13026
13142
|
}
|
|
13027
|
-
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(".."))) {
|
|
13028
13144
|
context.addIssue({
|
|
13029
13145
|
code: "custom",
|
|
13030
13146
|
path: ["versionSource", "path"],
|
|
@@ -13085,7 +13201,7 @@ var workspacesFileSchema = object({ workspaces: array(workspaceConfigSchema).opt
|
|
|
13085
13201
|
names.add(workspace.name);
|
|
13086
13202
|
}
|
|
13087
13203
|
});
|
|
13088
|
-
var workspacesRevision = (contents) =>
|
|
13204
|
+
var workspacesRevision = (contents) => createHash7("sha256").update(contents ?? "<missing>").digest("hex");
|
|
13089
13205
|
var validateWorkspacesDocument = (parsed, file) => {
|
|
13090
13206
|
if (!isConfigObject(parsed)) {
|
|
13091
13207
|
return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
|
|
@@ -13123,7 +13239,7 @@ var validateWorkspacesDocument = (parsed, file) => {
|
|
|
13123
13239
|
};
|
|
13124
13240
|
};
|
|
13125
13241
|
var readWorkspacesResult = (dir = configDir()) => {
|
|
13126
|
-
const file =
|
|
13242
|
+
const file = path12.join(dir, "workspaces.json");
|
|
13127
13243
|
let raw;
|
|
13128
13244
|
try {
|
|
13129
13245
|
raw = readFileSync3(file, "utf8");
|
|
@@ -13234,9 +13350,9 @@ var mainCheckoutOf = (cwd) => {
|
|
|
13234
13350
|
let main = null;
|
|
13235
13351
|
const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
|
|
13236
13352
|
const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
|
|
13237
|
-
if (gitDir && common &&
|
|
13353
|
+
if (gitDir && common && path12.resolve(gitDir) !== path12.resolve(common)) {
|
|
13238
13354
|
const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
|
|
13239
|
-
main = listed?.trim() || (
|
|
13355
|
+
main = listed?.trim() || (path12.basename(common) === ".git" ? path12.dirname(common) : common);
|
|
13240
13356
|
}
|
|
13241
13357
|
mainCheckouts.set(cwd, main);
|
|
13242
13358
|
return main;
|
|
@@ -13327,7 +13443,7 @@ var runtimeWorkspaceIndexSchema = object({
|
|
|
13327
13443
|
workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
13328
13444
|
}).passthrough();
|
|
13329
13445
|
var matchRuntimeWorkspace = (cwd, dir = configDir()) => {
|
|
13330
|
-
const file =
|
|
13446
|
+
const file = path12.join(dir, "workspaces.json");
|
|
13331
13447
|
let raw;
|
|
13332
13448
|
try {
|
|
13333
13449
|
raw = readFileSync3(file, "utf8");
|
|
@@ -13402,7 +13518,7 @@ function workspaceDefaultTarget(cwd, globalDefault) {
|
|
|
13402
13518
|
}
|
|
13403
13519
|
|
|
13404
13520
|
// packages/workit-core/src/core/release-tracks.ts
|
|
13405
|
-
import { createHash as
|
|
13521
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
13406
13522
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
13407
13523
|
class ReleaseTrackError extends Error {
|
|
13408
13524
|
}
|
|
@@ -13785,7 +13901,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
13785
13901
|
]) ?? "",
|
|
13786
13902
|
git3(cwd, ["config", "--get", recordedBaseKey(branch)]) ?? ""
|
|
13787
13903
|
] : [];
|
|
13788
|
-
key =
|
|
13904
|
+
key = createHash8("sha256").update(JSON.stringify({ cwd, input, refs, head, own })).digest("hex");
|
|
13789
13905
|
const hit = memo2.get(key);
|
|
13790
13906
|
if (hit)
|
|
13791
13907
|
return hit;
|
|
@@ -13800,7 +13916,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
13800
13916
|
}
|
|
13801
13917
|
|
|
13802
13918
|
// packages/workit-core/src/core/vcs-config.ts
|
|
13803
|
-
var workspacesPath = () =>
|
|
13919
|
+
var workspacesPath = () => path13.join(configDir(), "workspaces.json");
|
|
13804
13920
|
var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
13805
13921
|
var remoteProvider = (cwd) => {
|
|
13806
13922
|
const r = spawnSync8("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
|
|
@@ -13890,7 +14006,7 @@ var readVcsConfig = () => {
|
|
|
13890
14006
|
const file = vcsConfigPath();
|
|
13891
14007
|
let raw;
|
|
13892
14008
|
try {
|
|
13893
|
-
raw =
|
|
14009
|
+
raw = fs11.readFileSync(file, "utf8");
|
|
13894
14010
|
} catch {
|
|
13895
14011
|
return { status: "missing", path: file, config: {} };
|
|
13896
14012
|
}
|
|
@@ -13975,7 +14091,7 @@ function vcsConfig(mode, cwd, options = {}) {
|
|
|
13975
14091
|
const prov = cfg[provider] ?? {};
|
|
13976
14092
|
const out = {
|
|
13977
14093
|
ok: true,
|
|
13978
|
-
configPath:
|
|
14094
|
+
configPath: path13.resolve(cfgPath),
|
|
13979
14095
|
provider,
|
|
13980
14096
|
defaultTargetBranch: workspaceDefault,
|
|
13981
14097
|
pr: cfg.pr ?? {},
|
|
@@ -14186,7 +14302,7 @@ function diffHash(cwd, base, head) {
|
|
|
14186
14302
|
], GIT_TIMEOUTS.worktree);
|
|
14187
14303
|
if (!diff.ok || !diff.stdout)
|
|
14188
14304
|
return null;
|
|
14189
|
-
const hash =
|
|
14305
|
+
const hash = createHash9("sha256");
|
|
14190
14306
|
for (const line of diff.stdout.split(`
|
|
14191
14307
|
`)) {
|
|
14192
14308
|
if (line.startsWith("index "))
|
|
@@ -14198,16 +14314,16 @@ function diffHash(cwd, base, head) {
|
|
|
14198
14314
|
return `sha256:${hash.digest("hex")}`;
|
|
14199
14315
|
}
|
|
14200
14316
|
function storeRoot(cwd) {
|
|
14201
|
-
const location = resolveStore(
|
|
14317
|
+
const location = resolveStore(path14.resolve(cwd));
|
|
14202
14318
|
if (location instanceof Error)
|
|
14203
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");
|
|
14204
14320
|
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
14205
14321
|
}
|
|
14206
14322
|
function ledgerPath(cwd) {
|
|
14207
14323
|
const root = storeRoot(cwd);
|
|
14208
|
-
return root.ok ? { ok: true, value:
|
|
14324
|
+
return root.ok ? { ok: true, value: path14.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
14209
14325
|
}
|
|
14210
|
-
var newId2 = () => `${Date.now().toString(36)}-${
|
|
14326
|
+
var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
|
|
14211
14327
|
var NETWORK_FS_TYPES = new Set([
|
|
14212
14328
|
26985,
|
|
14213
14329
|
20859,
|
|
@@ -14225,7 +14341,7 @@ var onNetworkFs = (dir) => {
|
|
|
14225
14341
|
if (process.platform !== "linux")
|
|
14226
14342
|
return false;
|
|
14227
14343
|
try {
|
|
14228
|
-
return isNetworkFsType(
|
|
14344
|
+
return isNetworkFsType(fs12.statfsSync(dir).type);
|
|
14229
14345
|
} catch {
|
|
14230
14346
|
return false;
|
|
14231
14347
|
}
|
|
@@ -14235,15 +14351,15 @@ var LOCK_STALE_MS = 30000;
|
|
|
14235
14351
|
var LOCK_OWNER = "owner";
|
|
14236
14352
|
var lockToken = (lock) => {
|
|
14237
14353
|
try {
|
|
14238
|
-
return
|
|
14354
|
+
return fs12.readFileSync(path14.join(lock, LOCK_OWNER), "utf8");
|
|
14239
14355
|
} catch {
|
|
14240
14356
|
return null;
|
|
14241
14357
|
}
|
|
14242
14358
|
};
|
|
14243
14359
|
var lockAgeMs = (lock) => {
|
|
14244
|
-
for (const target of [
|
|
14360
|
+
for (const target of [path14.join(lock, LOCK_OWNER), lock]) {
|
|
14245
14361
|
try {
|
|
14246
|
-
return Date.now() -
|
|
14362
|
+
return Date.now() - fs12.statSync(target).mtimeMs;
|
|
14247
14363
|
} catch {}
|
|
14248
14364
|
}
|
|
14249
14365
|
return null;
|
|
@@ -14251,55 +14367,55 @@ var lockAgeMs = (lock) => {
|
|
|
14251
14367
|
var ledgerLock = {
|
|
14252
14368
|
acquire(lock) {
|
|
14253
14369
|
try {
|
|
14254
|
-
|
|
14370
|
+
fs12.mkdirSync(lock);
|
|
14255
14371
|
} catch (error) {
|
|
14256
14372
|
if (error.code === "EEXIST")
|
|
14257
14373
|
return null;
|
|
14258
14374
|
throw error;
|
|
14259
14375
|
}
|
|
14260
|
-
const token = `${process.pid}-${
|
|
14261
|
-
|
|
14376
|
+
const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
|
|
14377
|
+
fs12.writeFileSync(path14.join(lock, LOCK_OWNER), token);
|
|
14262
14378
|
return token;
|
|
14263
14379
|
},
|
|
14264
14380
|
release(lock, token) {
|
|
14265
14381
|
if (lockToken(lock) !== token)
|
|
14266
14382
|
return false;
|
|
14267
|
-
const tomb = `${lock}.release-${
|
|
14383
|
+
const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
|
|
14268
14384
|
try {
|
|
14269
|
-
|
|
14385
|
+
fs12.renameSync(lock, tomb);
|
|
14270
14386
|
} catch {
|
|
14271
14387
|
return false;
|
|
14272
14388
|
}
|
|
14273
14389
|
if (lockToken(tomb) !== token) {
|
|
14274
14390
|
try {
|
|
14275
|
-
|
|
14391
|
+
fs12.renameSync(tomb, lock);
|
|
14276
14392
|
} catch {}
|
|
14277
14393
|
return false;
|
|
14278
14394
|
}
|
|
14279
|
-
|
|
14395
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
14280
14396
|
return true;
|
|
14281
14397
|
},
|
|
14282
14398
|
token: lockToken,
|
|
14283
14399
|
reap(lock, observed) {
|
|
14284
|
-
const tomb = `${lock}.reap-${
|
|
14400
|
+
const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
|
|
14285
14401
|
try {
|
|
14286
|
-
|
|
14402
|
+
fs12.renameSync(lock, tomb);
|
|
14287
14403
|
} catch {
|
|
14288
14404
|
return false;
|
|
14289
14405
|
}
|
|
14290
14406
|
if (lockToken(tomb) === observed) {
|
|
14291
|
-
|
|
14407
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
14292
14408
|
return true;
|
|
14293
14409
|
}
|
|
14294
14410
|
try {
|
|
14295
|
-
|
|
14411
|
+
fs12.renameSync(tomb, lock);
|
|
14296
14412
|
} catch {
|
|
14297
|
-
|
|
14413
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
14298
14414
|
}
|
|
14299
14415
|
return false;
|
|
14300
14416
|
}
|
|
14301
14417
|
};
|
|
14302
|
-
function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
14418
|
+
function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
|
|
14303
14419
|
const lock = `${file}.lock`;
|
|
14304
14420
|
const deadline = Date.now() + waitMs;
|
|
14305
14421
|
let token = null;
|
|
@@ -14321,14 +14437,14 @@ function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
|
14321
14437
|
ledgerLock.release(lock, token);
|
|
14322
14438
|
}
|
|
14323
14439
|
}
|
|
14324
|
-
var sha2562 = (text) =>
|
|
14440
|
+
var sha2562 = (text) => createHash9("sha256").update(text).digest("hex");
|
|
14325
14441
|
var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
|
|
14326
14442
|
${body}`);
|
|
14327
14443
|
var TAIL_BYTES = 64 * 1024;
|
|
14328
14444
|
var lastLine = (fd, size) => {
|
|
14329
14445
|
for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
|
|
14330
14446
|
const buffer = Buffer.alloc(span);
|
|
14331
|
-
|
|
14447
|
+
fs12.readSync(fd, buffer, 0, span, size - span);
|
|
14332
14448
|
const end = buffer.lastIndexOf(10);
|
|
14333
14449
|
if (end < 0) {
|
|
14334
14450
|
if (span === size)
|
|
@@ -14357,15 +14473,23 @@ var lastLine = (fd, size) => {
|
|
|
14357
14473
|
}
|
|
14358
14474
|
return null;
|
|
14359
14475
|
};
|
|
14476
|
+
var tighten = (fd) => {
|
|
14477
|
+
try {
|
|
14478
|
+
if ((fs12.fstatSync(fd).mode & 63) !== 0)
|
|
14479
|
+
fs12.fchmodSync(fd, 384);
|
|
14480
|
+
} catch {}
|
|
14481
|
+
};
|
|
14360
14482
|
function appendLine(file, build, options) {
|
|
14361
14483
|
const write = () => {
|
|
14362
|
-
const fd =
|
|
14484
|
+
const fd = fs12.openSync(file, "a+", 384);
|
|
14363
14485
|
try {
|
|
14364
|
-
|
|
14486
|
+
if (process.platform !== "win32")
|
|
14487
|
+
tighten(fd);
|
|
14488
|
+
const size = fs12.fstatSync(fd).size;
|
|
14365
14489
|
let fence = "";
|
|
14366
14490
|
if (size > 0) {
|
|
14367
14491
|
const last = Buffer.alloc(1);
|
|
14368
|
-
|
|
14492
|
+
fs12.readSync(fd, last, 0, 1, size - 1);
|
|
14369
14493
|
if (last[0] !== 10)
|
|
14370
14494
|
fence = `
|
|
14371
14495
|
`;
|
|
@@ -14374,17 +14498,17 @@ function appendLine(file, build, options) {
|
|
|
14374
14498
|
if (!line.ok)
|
|
14375
14499
|
return line;
|
|
14376
14500
|
const bytes = Buffer.from(fence + line.value);
|
|
14377
|
-
const written =
|
|
14501
|
+
const written = fs12.writeSync(fd, bytes);
|
|
14378
14502
|
if (written !== bytes.length)
|
|
14379
14503
|
return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
|
|
14380
14504
|
return { ok: true, value: undefined };
|
|
14381
14505
|
} finally {
|
|
14382
|
-
|
|
14506
|
+
fs12.closeSync(fd);
|
|
14383
14507
|
}
|
|
14384
14508
|
};
|
|
14385
14509
|
try {
|
|
14386
|
-
|
|
14387
|
-
if (!options.forceLock && !onNetworkFs(
|
|
14510
|
+
fs12.mkdirSync(path14.dirname(file), { recursive: true });
|
|
14511
|
+
if (!options.forceLock && !onNetworkFs(path14.dirname(file)))
|
|
14388
14512
|
return write();
|
|
14389
14513
|
const locked = withLedgerLock(file, write);
|
|
14390
14514
|
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
@@ -14455,8 +14579,10 @@ function readLedger(cwd, options = {}) {
|
|
|
14455
14579
|
if (!file.ok)
|
|
14456
14580
|
return file;
|
|
14457
14581
|
let text = "";
|
|
14582
|
+
let stat = null;
|
|
14458
14583
|
try {
|
|
14459
|
-
|
|
14584
|
+
stat = fs12.statSync(file.value);
|
|
14585
|
+
text = fs12.readFileSync(file.value, "utf8");
|
|
14460
14586
|
} catch (error) {
|
|
14461
14587
|
if (error.code === "ENOENT")
|
|
14462
14588
|
return {
|
|
@@ -14475,6 +14601,7 @@ function readLedger(cwd, options = {}) {
|
|
|
14475
14601
|
const integrity = { chained: 0, legacy: 0, unverified: [] };
|
|
14476
14602
|
const earlier = new Set([null]);
|
|
14477
14603
|
let chainStarted = false;
|
|
14604
|
+
const ids = new Set;
|
|
14478
14605
|
for (const raw of text.split(`
|
|
14479
14606
|
`)) {
|
|
14480
14607
|
const trimmed = raw.trim();
|
|
@@ -14508,6 +14635,10 @@ function readLedger(cwd, options = {}) {
|
|
|
14508
14635
|
reason = "unsigned";
|
|
14509
14636
|
else
|
|
14510
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);
|
|
14511
14642
|
if (reason)
|
|
14512
14643
|
rowIntegrity = "unverified";
|
|
14513
14644
|
const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
|
|
@@ -14531,7 +14662,8 @@ function readLedger(cwd, options = {}) {
|
|
|
14531
14662
|
seq: rows.length + 1,
|
|
14532
14663
|
superseded: false,
|
|
14533
14664
|
supersedeIgnored: false,
|
|
14534
|
-
...rowIntegrity ? { integrity: rowIntegrity } : {}
|
|
14665
|
+
...rowIntegrity ? { integrity: rowIntegrity } : {},
|
|
14666
|
+
...reason ? { integrityReason: reason } : {}
|
|
14535
14667
|
};
|
|
14536
14668
|
rows.push(row);
|
|
14537
14669
|
if (!options.integrity)
|
|
@@ -14559,18 +14691,48 @@ function readLedger(cwd, options = {}) {
|
|
|
14559
14691
|
const byId = new Map;
|
|
14560
14692
|
for (const row of rows) {
|
|
14561
14693
|
const target = row.supersedes ? byId.get(row.supersedes) : undefined;
|
|
14562
|
-
if (target && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
14694
|
+
if (target && row.integrity !== "unverified" && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
14563
14695
|
target.superseded = true;
|
|
14564
14696
|
else if (row.supersedes)
|
|
14565
14697
|
row.supersedeIgnored = true;
|
|
14566
14698
|
if (!byId.has(row.id))
|
|
14567
14699
|
byId.set(row.id, row);
|
|
14568
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
|
+
});
|
|
14569
14707
|
return {
|
|
14570
14708
|
ok: true,
|
|
14571
14709
|
value: { path: file.value, rows, skipped, integrity: options.integrity ? integrity : null }
|
|
14572
14710
|
};
|
|
14573
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)}`;
|
|
14574
14736
|
function verdictBasis(verdict, current) {
|
|
14575
14737
|
if (verdict.head && verdict.head === current.head)
|
|
14576
14738
|
return "fresh";
|
|
@@ -14578,7 +14740,7 @@ function verdictBasis(verdict, current) {
|
|
|
14578
14740
|
return "carried";
|
|
14579
14741
|
return "stale";
|
|
14580
14742
|
}
|
|
14581
|
-
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);
|
|
14582
14744
|
function trailerSessions(cwd, base, head) {
|
|
14583
14745
|
if (!base || !head || !safeRef(base) || !safeRef(head))
|
|
14584
14746
|
return [];
|
|
@@ -14659,6 +14821,56 @@ var authoredBy = (authors, session) => {
|
|
|
14659
14821
|
return true;
|
|
14660
14822
|
return false;
|
|
14661
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));
|
|
14662
14874
|
var independenceReasons = (row, authors) => {
|
|
14663
14875
|
const reasons = [];
|
|
14664
14876
|
if (!row.actor.session)
|
|
@@ -14669,15 +14881,17 @@ var independenceReasons = (row, authors) => {
|
|
|
14669
14881
|
reasons.push("self");
|
|
14670
14882
|
return reasons;
|
|
14671
14883
|
};
|
|
14672
|
-
function effectiveOf(rows,
|
|
14884
|
+
function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
|
|
14673
14885
|
let effective = null;
|
|
14674
14886
|
for (const row of rows) {
|
|
14675
14887
|
if (!effective) {
|
|
14676
14888
|
effective = row;
|
|
14677
14889
|
continue;
|
|
14678
14890
|
}
|
|
14679
|
-
|
|
14680
|
-
|
|
14891
|
+
if (untrusted(reasonsOf(row)) && !untrusted(reasonsOf(effective)))
|
|
14892
|
+
continue;
|
|
14893
|
+
const rowIndependent = reasonsOf(row).length === 0;
|
|
14894
|
+
const effIndependent = reasonsOf(effective).length === 0;
|
|
14681
14895
|
if (effIndependent && !rowIndependent && isCurrent(effective))
|
|
14682
14896
|
continue;
|
|
14683
14897
|
if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session && sameCode(effective, row))
|
|
@@ -14691,7 +14905,7 @@ function restackCarries(rows, branch, from, to) {
|
|
|
14691
14905
|
return false;
|
|
14692
14906
|
const edges = new Map;
|
|
14693
14907
|
for (const row of rows)
|
|
14694
|
-
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)
|
|
14695
14909
|
edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
|
|
14696
14910
|
const seen = new Set([from]);
|
|
14697
14911
|
const queue = [from];
|
|
@@ -14707,7 +14921,8 @@ function restackCarries(rows, branch, from, to) {
|
|
|
14707
14921
|
}
|
|
14708
14922
|
return false;
|
|
14709
14923
|
}
|
|
14710
|
-
function checkVerdicts(cwd, branch,
|
|
14924
|
+
function checkVerdicts(cwd, branch, ledgerRows) {
|
|
14925
|
+
const rows = withIntegrity(cwd, ledgerRows);
|
|
14711
14926
|
const head = branchHead(cwd, branch);
|
|
14712
14927
|
const fallbackBase = defaultBase(cwd);
|
|
14713
14928
|
const scope = branchScope(cwd, branch);
|
|
@@ -14737,21 +14952,47 @@ function checkVerdicts(cwd, branch, rows) {
|
|
|
14737
14952
|
return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
|
|
14738
14953
|
};
|
|
14739
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);
|
|
14740
14981
|
const byKind = new Map;
|
|
14741
14982
|
for (const row of rows)
|
|
14742
|
-
if (isLiveVerdict(row) && scope(row)) {
|
|
14983
|
+
if (isLiveVerdict(row, retired) && scope(row)) {
|
|
14743
14984
|
const kind = typeof row.kind === "string" ? row.kind : "review";
|
|
14744
14985
|
byKind.set(kind, [...byKind.get(kind) ?? [], row]);
|
|
14745
14986
|
}
|
|
14746
14987
|
const verdicts = [];
|
|
14747
14988
|
const warnings = [];
|
|
14748
14989
|
for (const [kind, list] of byKind) {
|
|
14749
|
-
const row = effectiveOf(list,
|
|
14990
|
+
const row = effectiveOf(list, reasonsOf, sameCode, (candidate) => basisOf(candidate) !== "stale");
|
|
14750
14991
|
if (!row)
|
|
14751
14992
|
continue;
|
|
14752
14993
|
const basis = basisOf(row);
|
|
14753
14994
|
const current = basis !== "stale";
|
|
14754
|
-
const independence =
|
|
14995
|
+
const independence = reasonsOf(row);
|
|
14755
14996
|
const reasons = [
|
|
14756
14997
|
...current ? [] : ["stale"],
|
|
14757
14998
|
...PASSING.has(String(row.result)) ? [] : ["not_passing"],
|
|
@@ -14768,23 +15009,31 @@ function checkVerdicts(cwd, branch, rows) {
|
|
|
14768
15009
|
});
|
|
14769
15010
|
if (reasons.length === 0) {
|
|
14770
15011
|
for (const later of list)
|
|
14771
|
-
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")
|
|
14772
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`);
|
|
14773
15014
|
}
|
|
14774
15015
|
}
|
|
14775
15016
|
for (const row of rows)
|
|
14776
15017
|
if (row.integrity === "unverified" && scope(row))
|
|
14777
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
|
+
}
|
|
14778
15027
|
verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
|
|
14779
15028
|
const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
|
|
14780
15029
|
const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
|
|
14781
15030
|
const newestAccepted = verdicts.findLast((entry) => entry.accepted && STRONG_RESULTS.has(String(entry.verdict.result))) ?? null;
|
|
14782
|
-
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: [] } : {
|
|
14783
15032
|
accepted: false,
|
|
14784
15033
|
verdict: failing?.verdict ?? null,
|
|
14785
15034
|
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
14786
15035
|
};
|
|
14787
|
-
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)));
|
|
14788
15037
|
return {
|
|
14789
15038
|
branch,
|
|
14790
15039
|
head,
|
|
@@ -14947,7 +15196,7 @@ var productDecision = (task, ledger) => {
|
|
|
14947
15196
|
};
|
|
14948
15197
|
};
|
|
14949
15198
|
var planRecorded = (task, root) => {
|
|
14950
|
-
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)));
|
|
14951
15200
|
if (cited)
|
|
14952
15201
|
return {
|
|
14953
15202
|
met: true,
|
|
@@ -15006,15 +15255,15 @@ function writeBlockers(task, root) {
|
|
|
15006
15255
|
}
|
|
15007
15256
|
|
|
15008
15257
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
15009
|
-
var digestBytes = (value) =>
|
|
15010
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
15011
|
-
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("/") || ".";
|
|
15012
15261
|
var canonicalPath = (value) => {
|
|
15013
15262
|
if (typeof value !== "string" || !value)
|
|
15014
15263
|
return null;
|
|
15015
15264
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
15016
15265
|
return null;
|
|
15017
|
-
const normalized =
|
|
15266
|
+
const normalized = path16.posix.normalize(value);
|
|
15018
15267
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
15019
15268
|
return null;
|
|
15020
15269
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -15062,21 +15311,21 @@ var gitPaths = (root) => {
|
|
|
15062
15311
|
git: !/not a git repository/i.test(stderr)
|
|
15063
15312
|
};
|
|
15064
15313
|
}
|
|
15065
|
-
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("/")) : [];
|
|
15066
15315
|
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
15067
15316
|
if (stagedDeleted.status !== 0) {
|
|
15068
15317
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
15069
15318
|
if (!/not a git repository/i.test(stderr))
|
|
15070
15319
|
return { paths, uncertain: true, git: true };
|
|
15071
15320
|
} else if (stagedDeleted.stdout) {
|
|
15072
|
-
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("/")));
|
|
15073
15322
|
}
|
|
15074
15323
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
15075
15324
|
};
|
|
15076
15325
|
var walk = (root, directory, output) => {
|
|
15077
15326
|
let names;
|
|
15078
15327
|
try {
|
|
15079
|
-
names =
|
|
15328
|
+
names = fs13.readdirSync(directory);
|
|
15080
15329
|
} catch {
|
|
15081
15330
|
return true;
|
|
15082
15331
|
}
|
|
@@ -15084,10 +15333,10 @@ var walk = (root, directory, output) => {
|
|
|
15084
15333
|
for (const name of names) {
|
|
15085
15334
|
if (name === ".git" || name === ".workit")
|
|
15086
15335
|
continue;
|
|
15087
|
-
const target =
|
|
15336
|
+
const target = path16.join(directory, name);
|
|
15088
15337
|
let stat;
|
|
15089
15338
|
try {
|
|
15090
|
-
stat =
|
|
15339
|
+
stat = fs13.lstatSync(target);
|
|
15091
15340
|
} catch {
|
|
15092
15341
|
uncertain = true;
|
|
15093
15342
|
continue;
|
|
@@ -15102,14 +15351,14 @@ var walk = (root, directory, output) => {
|
|
|
15102
15351
|
var scopeRoots = (root, scope) => {
|
|
15103
15352
|
const roots = [];
|
|
15104
15353
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
15105
|
-
const target =
|
|
15354
|
+
const target = path16.resolve(root, item);
|
|
15106
15355
|
if (!inside(root, target))
|
|
15107
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` }] });
|
|
15108
15357
|
let ancestor = target;
|
|
15109
|
-
while (!
|
|
15110
|
-
ancestor =
|
|
15358
|
+
while (!fs13.existsSync(ancestor) && ancestor !== root)
|
|
15359
|
+
ancestor = path16.dirname(ancestor);
|
|
15111
15360
|
try {
|
|
15112
|
-
if (!inside(root,
|
|
15361
|
+
if (!inside(root, fs13.realpathSync(ancestor)))
|
|
15113
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` }] });
|
|
15114
15363
|
} catch {
|
|
15115
15364
|
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
@@ -15121,7 +15370,7 @@ var scopeRoots = (root, scope) => {
|
|
|
15121
15370
|
function captureCandidate(root, scope, environment = []) {
|
|
15122
15371
|
let checkout;
|
|
15123
15372
|
try {
|
|
15124
|
-
checkout =
|
|
15373
|
+
checkout = fs13.realpathSync(root);
|
|
15125
15374
|
} catch {
|
|
15126
15375
|
return failure2("invalid_input", "candidate root does not exist");
|
|
15127
15376
|
}
|
|
@@ -15139,11 +15388,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
15139
15388
|
if (item === ".git" || item === ".workit")
|
|
15140
15389
|
continue;
|
|
15141
15390
|
names.add(item);
|
|
15142
|
-
if (!
|
|
15391
|
+
if (!fs13.existsSync(target))
|
|
15143
15392
|
continue;
|
|
15144
15393
|
let stat;
|
|
15145
15394
|
try {
|
|
15146
|
-
stat =
|
|
15395
|
+
stat = fs13.lstatSync(target);
|
|
15147
15396
|
} catch {
|
|
15148
15397
|
uncertain = true;
|
|
15149
15398
|
continue;
|
|
@@ -15159,17 +15408,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
15159
15408
|
for (const item of [...names].toSorted()) {
|
|
15160
15409
|
if (!scopeMatches(item, normalizedScope))
|
|
15161
15410
|
continue;
|
|
15162
|
-
const target =
|
|
15411
|
+
const target = path16.join(checkout, item);
|
|
15163
15412
|
try {
|
|
15164
|
-
const stat =
|
|
15413
|
+
const stat = fs13.lstatSync(target);
|
|
15165
15414
|
if (stat.isSymbolicLink()) {
|
|
15166
|
-
const link =
|
|
15415
|
+
const link = fs13.readlinkSync(target);
|
|
15167
15416
|
files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
|
|
15168
15417
|
} else if (stat.isFile()) {
|
|
15169
15418
|
files.push({
|
|
15170
15419
|
path: item,
|
|
15171
15420
|
kind: "file",
|
|
15172
|
-
digest: digestBytes(
|
|
15421
|
+
digest: digestBytes(fs13.readFileSync(target)),
|
|
15173
15422
|
executable: (stat.mode & 73) !== 0
|
|
15174
15423
|
});
|
|
15175
15424
|
} else if (!stat.isDirectory()) {
|
|
@@ -15334,7 +15583,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
|
|
|
15334
15583
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
15335
15584
|
let root;
|
|
15336
15585
|
try {
|
|
15337
|
-
root =
|
|
15586
|
+
root = fs13.realpathSync(checkoutRoot);
|
|
15338
15587
|
} catch {
|
|
15339
15588
|
return failure2("permission_denied", "checkout root is unavailable");
|
|
15340
15589
|
}
|
|
@@ -15343,23 +15592,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
15343
15592
|
continue;
|
|
15344
15593
|
if (!reference.digest)
|
|
15345
15594
|
return failure2("invalid_input", "document references require a byte digest");
|
|
15346
|
-
const target =
|
|
15347
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
15595
|
+
const target = path16.resolve(root, reference.path);
|
|
15596
|
+
if (target !== root && !target.startsWith(`${root}${path16.sep}`))
|
|
15348
15597
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
15349
15598
|
fields: [
|
|
15350
15599
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
15351
15600
|
]
|
|
15352
15601
|
});
|
|
15353
15602
|
try {
|
|
15354
|
-
const below =
|
|
15603
|
+
const below = path16.relative(root, target);
|
|
15355
15604
|
let current = root;
|
|
15356
|
-
for (const segment of below.split(
|
|
15605
|
+
for (const segment of below.split(path16.sep)) {
|
|
15357
15606
|
if (!segment)
|
|
15358
15607
|
continue;
|
|
15359
|
-
current =
|
|
15360
|
-
const stat =
|
|
15361
|
-
const real =
|
|
15362
|
-
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}`))
|
|
15363
15612
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
15364
15613
|
fields: [
|
|
15365
15614
|
{
|
|
@@ -15375,7 +15624,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
15375
15624
|
if (current !== target && !stat.isDirectory())
|
|
15376
15625
|
return failure2("invalid_input", "document reference ancestor is not a directory");
|
|
15377
15626
|
}
|
|
15378
|
-
if (digestBytes(
|
|
15627
|
+
if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
|
|
15379
15628
|
return failure2("permission_denied", "decision document bytes have changed");
|
|
15380
15629
|
} catch {
|
|
15381
15630
|
return failure2("invalid_input", "decision document is unavailable");
|
|
@@ -15722,11 +15971,11 @@ what and whether, the CLI does how and records what it observed.
|
|
|
15722
15971
|
Native host allow/ask/deny, sandbox and org rules stay authoritative. Never
|
|
15723
15972
|
switch paths to evade a denial, never claim enforcement a host cannot provide,
|
|
15724
15973
|
and never fabricate an approval or verdict. A question answer is not
|
|
15725
|
-
host permission, and imported decisions or handoffs
|
|
15974
|
+
host permission, and imported decisions or handoffs grant no authority.
|
|
15726
15975
|
|
|
15727
15976
|
Autonomy contract:
|
|
15728
15977
|
- Continue to the requested endpoint (commit, push, PR, green CI, verified,
|
|
15729
|
-
merged) without asking "continue?", running checks and repairs
|
|
15978
|
+
merged) without asking "continue?", running checks and repairs.
|
|
15730
15979
|
Stop only for a new consequential choice, a host denial, a conflicting edit
|
|
15731
15980
|
or an unresolved blocker; report open gaps and uncertain workers.
|
|
15732
15981
|
- No endpoint named: stop at a local commit on a policy-compliant branch and
|
|
@@ -15736,7 +15985,8 @@ Autonomy contract:
|
|
|
15736
15985
|
never merging; \`merged\` also lands it with \`workit pr merge\`.
|
|
15737
15986
|
Merge and release need a workspace grant only the user raises from their
|
|
15738
15987
|
own terminal (a blocked verb names how); never edit workspaces.json or route
|
|
15739
|
-
around
|
|
15988
|
+
around grant_required. Release: \`workit release start <v>\`, commits,
|
|
15989
|
+
\`finish\`; \`publish\` if asked.
|
|
15740
15990
|
- Ask only for a product or preference choice, or for authority you lack, with
|
|
15741
15991
|
your recommended answer. Facts are yours: read, run or prototype.
|
|
15742
15992
|
- Label claims measured (run and seen this session), inferred, or guess.
|
|
@@ -15751,22 +16001,22 @@ Autonomy contract:
|
|
|
15751
16001
|
reconcile every requested item and observe it (\`workit verify-delivery\`).
|
|
15752
16002
|
|
|
15753
16003
|
CLI first: if a step has one right answer, use the \`workit\` verb instead of
|
|
15754
|
-
hand-running git, gh or glab (\`workit git|pr|ci|check\`;
|
|
15755
|
-
|
|
16004
|
+
hand-running git, gh or glab (\`workit git|pr|ci|check|help\`;
|
|
16005
|
+
else \`npx -y @brainervirus/workit-cli\`).
|
|
15756
16006
|
Add Co-Authored-By via \`workit git commit --trailer "K: v" -- <paths>\`, never
|
|
15757
16007
|
an amend.
|
|
15758
16008
|
The task families (task, policy, evidence, finding, decision, worker, state)
|
|
15759
16009
|
are optional continuity for tracked work; each branch has one implicit task,
|
|
15760
|
-
and a solo edit needs no task. A decision
|
|
15761
|
-
|
|
16010
|
+
and a solo edit needs no task. A decision records a user choice; it never
|
|
16011
|
+
authorizes an effect. Judge work once with
|
|
15762
16012
|
\`workit policy assess --judge risk= behavior= product-choice= plan=\`.
|
|
15763
|
-
Across repositories, bind
|
|
15764
|
-
|
|
15765
|
-
external effect before
|
|
16013
|
+
Across repositories, bind items to checkout, branch and endpoint, resolve
|
|
16014
|
+
competing targets before a mutation, and reconcile an uncertain
|
|
16015
|
+
external effect before a retry.
|
|
15766
16016
|
|
|
15767
16017
|
Mid-task: never silently drop or resume an objective (workit-continue).
|
|
15768
16018
|
|
|
15769
|
-
Skills: load the skill, never act from memory
|
|
16019
|
+
Skills: load the skill, never act from memory (aliases /wk-<name>).
|
|
15770
16020
|
- brainstorm, plan, spec, grill, should we: workit-shape
|
|
15771
16021
|
- implement, build, add a feature: workit-implement
|
|
15772
16022
|
- review, blast radius: workit-review
|
|
@@ -15906,17 +16156,17 @@ var exportDigest = (bundle) => sha256({
|
|
|
15906
16156
|
// packages/workit-core/src/core/route-intent.ts
|
|
15907
16157
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
15908
16158
|
import { existsSync as existsSync6 } from "node:fs";
|
|
15909
|
-
import
|
|
16159
|
+
import path19 from "node:path";
|
|
15910
16160
|
|
|
15911
16161
|
// packages/workit-core/src/hooks/raw-git.ts
|
|
15912
16162
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
15913
|
-
import { createHash as
|
|
15914
|
-
import
|
|
16163
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
16164
|
+
import fs15 from "node:fs";
|
|
15915
16165
|
import os4 from "node:os";
|
|
15916
|
-
import
|
|
16166
|
+
import path18 from "node:path";
|
|
15917
16167
|
|
|
15918
16168
|
// packages/workit-core/src/core/branch.ts
|
|
15919
|
-
import { createHash as
|
|
16169
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
15920
16170
|
|
|
15921
16171
|
// packages/workit-core/src/core/git.ts
|
|
15922
16172
|
import { execFileSync } from "node:child_process";
|
|
@@ -16110,7 +16360,7 @@ var branchPolicySnapshotFor = (workspaceRoot) => {
|
|
|
16110
16360
|
const source = resolved.provenance.branchPolicy;
|
|
16111
16361
|
const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
|
|
16112
16362
|
const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
|
|
16113
|
-
const fingerprint =
|
|
16363
|
+
const fingerprint = createHash11("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
|
|
16114
16364
|
return {
|
|
16115
16365
|
branchPolicy,
|
|
16116
16366
|
namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
|
|
@@ -16164,9 +16414,9 @@ var releaseBranchesOf = (cwd) => {
|
|
|
16164
16414
|
var releaseBranch = (cwd, branch) => releaseBranchesOf(cwd).branches.some((name) => name.toLowerCase() === branch.toLowerCase());
|
|
16165
16415
|
|
|
16166
16416
|
// packages/workit-core/src/hooks/session-marker.ts
|
|
16167
|
-
import { createHash as
|
|
16168
|
-
import
|
|
16169
|
-
import
|
|
16417
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
16418
|
+
import fs14 from "node:fs";
|
|
16419
|
+
import path17 from "node:path";
|
|
16170
16420
|
var sessionMarkerFile = (input, session, store) => {
|
|
16171
16421
|
let dir = store;
|
|
16172
16422
|
if (dir === undefined) {
|
|
@@ -16175,13 +16425,13 @@ var sessionMarkerFile = (input, session, store) => {
|
|
|
16175
16425
|
return null;
|
|
16176
16426
|
dir = location.dir;
|
|
16177
16427
|
}
|
|
16178
|
-
const name =
|
|
16179
|
-
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`);
|
|
16180
16430
|
};
|
|
16181
16431
|
var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
16182
16432
|
var readSessionMarker = (file) => {
|
|
16183
16433
|
try {
|
|
16184
|
-
const value = JSON.parse(
|
|
16434
|
+
const value = JSON.parse(fs14.readFileSync(file, "utf8"));
|
|
16185
16435
|
return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
|
|
16186
16436
|
} catch {
|
|
16187
16437
|
return { nudged: [], loaded: [], raw: [] };
|
|
@@ -16189,8 +16439,8 @@ var readSessionMarker = (file) => {
|
|
|
16189
16439
|
};
|
|
16190
16440
|
var writeSessionMarker = (file, marker) => {
|
|
16191
16441
|
try {
|
|
16192
|
-
|
|
16193
|
-
|
|
16442
|
+
fs14.mkdirSync(path17.dirname(file), { recursive: true });
|
|
16443
|
+
fs14.writeFileSync(file, JSON.stringify(marker));
|
|
16194
16444
|
} catch {}
|
|
16195
16445
|
};
|
|
16196
16446
|
|
|
@@ -16687,14 +16937,14 @@ var GIT_CONFIG_ENV = /^GIT_(?:CONFIG\w*|DIR|COMMON_DIR|WORK_TREE)$/;
|
|
|
16687
16937
|
var ASSIGNMENT = /^([A-Za-z_]\w*)=([\s\S]*)$/;
|
|
16688
16938
|
var MAX_DEPTH = 4;
|
|
16689
16939
|
var MAX_EXPANSIONS = 16;
|
|
16690
|
-
var isAbsolute = (value) =>
|
|
16940
|
+
var isAbsolute = (value) => path18.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
|
|
16691
16941
|
var HERE = /^(?:\$PWD|\$\{PWD\}|\$\(\s*pwd\s*\)|`pwd`|\$\(\s*git\s+rev-parse\s+--show-toplevel\s*\))(?=$|[\\/])/;
|
|
16692
16942
|
var joinDir = (base, raw) => {
|
|
16693
16943
|
const next = raw.replace(HERE, ".");
|
|
16694
16944
|
if (base === undefined || /[$`*?]/.test(next) || next === "-")
|
|
16695
16945
|
return;
|
|
16696
|
-
const expanded = next === "~" || next.startsWith("~/") ?
|
|
16697
|
-
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);
|
|
16698
16948
|
};
|
|
16699
16949
|
var rebase = (base, inner) => inner === null ? base : inner === undefined ? undefined : joinDir(base, inner);
|
|
16700
16950
|
var cdTarget = (words) => {
|
|
@@ -17445,7 +17695,7 @@ var repoAt = (dir) => {
|
|
|
17445
17695
|
return { location, dir, branch: resolveTaskKey(dir, location).branch };
|
|
17446
17696
|
};
|
|
17447
17697
|
var isWorkitWorkspace = (repo) => {
|
|
17448
|
-
if (
|
|
17698
|
+
if (fs15.existsSync(repo.location.dir))
|
|
17449
17699
|
return true;
|
|
17450
17700
|
try {
|
|
17451
17701
|
return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
|
|
@@ -17466,17 +17716,17 @@ var headSha2 = (repo) => {
|
|
|
17466
17716
|
if (!git || git.reftable)
|
|
17467
17717
|
return null;
|
|
17468
17718
|
try {
|
|
17469
|
-
const head =
|
|
17719
|
+
const head = fs15.readFileSync(path18.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
17470
17720
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
17471
17721
|
return head;
|
|
17472
17722
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
17473
17723
|
if (!ref)
|
|
17474
17724
|
return null;
|
|
17475
|
-
const common =
|
|
17725
|
+
const common = path18.dirname(repo.location.dir);
|
|
17476
17726
|
try {
|
|
17477
|
-
return
|
|
17727
|
+
return fs15.readFileSync(path18.join(common, ref), "utf8").trim() || null;
|
|
17478
17728
|
} catch {}
|
|
17479
|
-
const packed =
|
|
17729
|
+
const packed = fs15.readFileSync(path18.join(common, "packed-refs"), "utf8");
|
|
17480
17730
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
17481
17731
|
} catch {
|
|
17482
17732
|
return null;
|
|
@@ -17564,8 +17814,8 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
|
|
|
17564
17814
|
const sub = invocation.args[0];
|
|
17565
17815
|
if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
|
|
17566
17816
|
return [invocation];
|
|
17567
|
-
const dir =
|
|
17568
|
-
if (!
|
|
17817
|
+
const dir = path18.resolve(root, invocation.dir ?? ".");
|
|
17818
|
+
if (!fs15.existsSync(dir))
|
|
17569
17819
|
return [invocation];
|
|
17570
17820
|
const config = gitConfig(dir, invocation, cache);
|
|
17571
17821
|
const alias = configValue(config, `alias.${sub.toLowerCase()}`);
|
|
@@ -17587,11 +17837,11 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
|
|
|
17587
17837
|
var normalizedUrl = (url, dir) => {
|
|
17588
17838
|
const trimmed = url.trim();
|
|
17589
17839
|
if (/^file:\/\//i.test(trimmed))
|
|
17590
|
-
return
|
|
17840
|
+
return path18.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
|
|
17591
17841
|
const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
|
|
17592
17842
|
const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
|
|
17593
17843
|
if (!scheme && !scp)
|
|
17594
|
-
return
|
|
17844
|
+
return path18.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
|
|
17595
17845
|
const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
|
|
17596
17846
|
return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
|
|
17597
17847
|
};
|
|
@@ -17647,7 +17897,7 @@ var refusal = (action, branch, dir) => {
|
|
|
17647
17897
|
return `workit: raw ${quote(action.via === "api" ? `${action.forge} api` : action.forge === "gh" ? "gh pr merge" : "glab mr merge")}${action.via === "api" ? " merge" : ""} bypasses the merge grant and the verdict gate. Run ${quote("workit pr merge")} instead (it checks both; ${quote("workit help pr")}).`;
|
|
17648
17898
|
const plainPush = action.kind === "push" && !action.blindForce && !action.deletes;
|
|
17649
17899
|
if (dir && branch && plainPush && releaseBranch(dir, branch))
|
|
17650
|
-
return `workit: raw ${quote("git push")} onto ${branch}, a protected release branch, bypasses workit's protected-branch check. To finish a release or hotfix, push it with ${quote(`workit git push --release ${branch} [--release
|
|
17900
|
+
return `workit: raw ${quote("git push")} onto ${branch}, a protected release branch, bypasses workit's protected-branch check. To finish a release or hotfix, push it with ${quote(`workit git push --release ${branch} [--release ${releaseTagPrefixOf(dir)}<version>]`)}: it pushes ${branch} (fast-forward only) and the release tag, verifies the remote and records it, under the ${quote("release")} grant (if refused, ask the user to run ${quote("workit grant set <workspace> release=true")}). Feature work goes through ${quote("workit git push")} and ${quote("workit pr merge")}.`;
|
|
17651
17901
|
return `workit: raw ${quote("git push")}${action.kind === "push" && action.blindForce ? " --force" : ""} onto ${branch}, a protected branch or the default target, bypasses workit's protected-branch check. Push your feature branch with ${quote("workit git push")} (${quote("workit git push --force-with-lease")} to rewrite it) and land it with ${quote("workit pr merge")}.`;
|
|
17652
17902
|
};
|
|
17653
17903
|
var refFacts = (repo, remote) => {
|
|
@@ -17734,15 +17984,16 @@ var guardedTarget = (dir, target) => {
|
|
|
17734
17984
|
return null;
|
|
17735
17985
|
};
|
|
17736
17986
|
var isTag = (facts, name) => facts.tags.has(name) && !facts.heads.has(name);
|
|
17737
|
-
var tagRefusal = (action, facts) => {
|
|
17987
|
+
var tagRefusal = (action, facts, dir) => {
|
|
17738
17988
|
const tags = [
|
|
17739
17989
|
...action.tags ?? [],
|
|
17740
17990
|
...action.targets.filter((name) => name !== null && isTag(facts, name))
|
|
17741
17991
|
];
|
|
17742
17992
|
if (!tags.length && !action.allTags)
|
|
17743
17993
|
return null;
|
|
17744
|
-
const
|
|
17745
|
-
|
|
17994
|
+
const shape = `${releaseTagPrefixOf(dir)}<version>`;
|
|
17995
|
+
const named = tags.length ? tags.join(" --release ") : shape;
|
|
17996
|
+
return `workit: raw ${quote(`git push${action.allTags ? " --tags" : ""}`)} of ${tags.length ? `tag ${tags.join(", ")}` : "tags"} bypasses the release grant. Push a release tag with ${quote(`workit git push --release ${named}`)} (${shape} tags whose commit the production branch already has; a tag never moves), under the ${quote("release")} grant (if refused, ask the user to run ${quote("workit grant set <workspace> release=true")}).`;
|
|
17746
17997
|
};
|
|
17747
17998
|
var remoteHeads = (repo, remote) => {
|
|
17748
17999
|
const listed = spawnSync11("git", ["ls-remote", "--heads", remote], {
|
|
@@ -17808,13 +18059,13 @@ var pushRefusal = (action, repo, config) => {
|
|
|
17808
18059
|
if (guarded)
|
|
17809
18060
|
return refusal(action, guarded, repo.dir);
|
|
17810
18061
|
}
|
|
17811
|
-
return tagRefusal(pushed, facts) ?? (action.deletes ? null : offPolicyPush(pushed, repo, facts, remote));
|
|
18062
|
+
return tagRefusal(pushed, facts, repo.dir) ?? (action.deletes ? null : offPolicyPush(pushed, repo, facts, remote));
|
|
17812
18063
|
};
|
|
17813
18064
|
var actionsIn = (cwd, command, dialect, cache = new Map) => {
|
|
17814
18065
|
const out = [];
|
|
17815
18066
|
const repos = new Map;
|
|
17816
18067
|
const repoFor = (relative) => {
|
|
17817
|
-
const dir =
|
|
18068
|
+
const dir = path18.resolve(cwd, relative ?? ".");
|
|
17818
18069
|
if (!repos.has(dir)) {
|
|
17819
18070
|
const repo = repoAt(dir);
|
|
17820
18071
|
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
@@ -17899,8 +18150,8 @@ var analysisDenial = (error) => ({
|
|
|
17899
18150
|
unblock: SPLIT_IT
|
|
17900
18151
|
});
|
|
17901
18152
|
var MARKER_TTL_SECONDS = 3600;
|
|
17902
|
-
var markerFile = (repo, session, key) =>
|
|
17903
|
-
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")}`;
|
|
17904
18155
|
function noteRawCommit(input, command, key) {
|
|
17905
18156
|
try {
|
|
17906
18157
|
if (!input.session.id || !/\bcommit\b/.test(command))
|
|
@@ -17910,8 +18161,8 @@ function noteRawCommit(input, command, key) {
|
|
|
17910
18161
|
if (action.kind !== "commit")
|
|
17911
18162
|
continue;
|
|
17912
18163
|
const file = markerFile(repo, input.session.id, key);
|
|
17913
|
-
|
|
17914
|
-
|
|
18164
|
+
fs15.mkdirSync(path18.dirname(file), { recursive: true });
|
|
18165
|
+
fs15.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
17915
18166
|
`);
|
|
17916
18167
|
}
|
|
17917
18168
|
} catch {}
|
|
@@ -17934,11 +18185,11 @@ var headCommit = (dir) => {
|
|
|
17934
18185
|
var settle2 = (input, repo, file) => {
|
|
17935
18186
|
let marker;
|
|
17936
18187
|
try {
|
|
17937
|
-
marker = JSON.parse(
|
|
18188
|
+
marker = JSON.parse(fs15.readFileSync(file, "utf8"));
|
|
17938
18189
|
} catch {
|
|
17939
18190
|
return;
|
|
17940
18191
|
}
|
|
17941
|
-
|
|
18192
|
+
fs15.rmSync(file, { force: true });
|
|
17942
18193
|
const session = input.session.id;
|
|
17943
18194
|
const now = Date.now() / 1000;
|
|
17944
18195
|
if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
|
|
@@ -18129,7 +18380,7 @@ var judgedDir = (root, dir) => {
|
|
|
18129
18380
|
return null;
|
|
18130
18381
|
if (dir === null)
|
|
18131
18382
|
return root;
|
|
18132
|
-
const resolved =
|
|
18383
|
+
const resolved = path19.resolve(root, dir);
|
|
18133
18384
|
return existsSync6(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
|
|
18134
18385
|
};
|
|
18135
18386
|
var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
|
|
@@ -18365,7 +18616,7 @@ var judgeInputSummary = (raw) => {
|
|
|
18365
18616
|
|
|
18366
18617
|
// packages/workit-core/src/autonomy.ts
|
|
18367
18618
|
import os5 from "node:os";
|
|
18368
|
-
import
|
|
18619
|
+
import path20 from "node:path";
|
|
18369
18620
|
|
|
18370
18621
|
// packages/workit-core/src/core/safe-write.ts
|
|
18371
18622
|
import {
|
|
@@ -18399,10 +18650,10 @@ var accountHome = () => {
|
|
|
18399
18650
|
}
|
|
18400
18651
|
};
|
|
18401
18652
|
var grantsHome = { resolve: accountHome };
|
|
18402
|
-
var grantsDir = () =>
|
|
18653
|
+
var grantsDir = () => path20.join(grantsHome.resolve(), ".config", "workit");
|
|
18403
18654
|
var grantsOverride = () => {
|
|
18404
|
-
const configured =
|
|
18405
|
-
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`;
|
|
18406
18657
|
};
|
|
18407
18658
|
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
18408
18659
|
var DEFAULT_GRANTS = Object.freeze({
|
|
@@ -20174,7 +20425,7 @@ class WorkitCore {
|
|
|
20174
20425
|
// packages/workit-core/src/core/repo-context.ts
|
|
20175
20426
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
20176
20427
|
import { existsSync as existsSync7, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
|
|
20177
|
-
import
|
|
20428
|
+
import path21 from "node:path";
|
|
20178
20429
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
20179
20430
|
try {
|
|
20180
20431
|
gitRevisionParts(value);
|
|
@@ -20308,7 +20559,7 @@ var findPrTemplate = (cwd) => {
|
|
|
20308
20559
|
]) {
|
|
20309
20560
|
const parts = rel.split("/");
|
|
20310
20561
|
const base = parts[parts.length - 1];
|
|
20311
|
-
const dir = parts.length > 1 ?
|
|
20562
|
+
const dir = parts.length > 1 ? path21.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
20312
20563
|
const actual = probeCaseInsensitive(dir, base);
|
|
20313
20564
|
if (actual)
|
|
20314
20565
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -20339,7 +20590,7 @@ var diffStatForRange = (cwd, range) => {
|
|
|
20339
20590
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
20340
20591
|
};
|
|
20341
20592
|
var packageScripts = (cwd, keys) => {
|
|
20342
|
-
const pkgPath =
|
|
20593
|
+
const pkgPath = path21.join(cwd, "package.json");
|
|
20343
20594
|
if (!existsSync7(pkgPath))
|
|
20344
20595
|
return [];
|
|
20345
20596
|
try {
|
|
@@ -20365,9 +20616,9 @@ var documentationFiles = (cwd) => {
|
|
|
20365
20616
|
if (entry.isDirectory()) {
|
|
20366
20617
|
if (excluded.has(entry.name))
|
|
20367
20618
|
continue;
|
|
20368
|
-
walk(
|
|
20619
|
+
walk(path21.join(dir, entry.name), depth + 1);
|
|
20369
20620
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
20370
|
-
matches.push("./" +
|
|
20621
|
+
matches.push("./" + path21.relative(cwd, path21.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
20371
20622
|
}
|
|
20372
20623
|
}
|
|
20373
20624
|
};
|
|
@@ -20383,7 +20634,7 @@ var readTrimmed2 = (file, maxLines) => {
|
|
|
20383
20634
|
`);
|
|
20384
20635
|
};
|
|
20385
20636
|
function prReadyContext(root, range) {
|
|
20386
|
-
const cwd =
|
|
20637
|
+
const cwd = path21.resolve(repoRoot(root));
|
|
20387
20638
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
20388
20639
|
return invalidRange(cwd);
|
|
20389
20640
|
let stdout = "";
|
|
@@ -20441,7 +20692,7 @@ ${status.stderr}` : "");
|
|
|
20441
20692
|
stdout += `template_path: ${template}
|
|
20442
20693
|
|
|
20443
20694
|
`;
|
|
20444
|
-
stdout += readTrimmed2(
|
|
20695
|
+
stdout += readTrimmed2(path21.join(cwd, template), 220);
|
|
20445
20696
|
} else {
|
|
20446
20697
|
stdout += `template_path: none
|
|
20447
20698
|
|
|
@@ -20451,7 +20702,7 @@ ${status.stderr}` : "");
|
|
|
20451
20702
|
stdout += `
|
|
20452
20703
|
`;
|
|
20453
20704
|
stdout += printSection("Recent Validation Signals");
|
|
20454
|
-
if (existsSync7(
|
|
20705
|
+
if (existsSync7(path21.join(cwd, "package.json"))) {
|
|
20455
20706
|
stdout += `package.json detected. Common scripts:
|
|
20456
20707
|
`;
|
|
20457
20708
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -20460,7 +20711,7 @@ ${status.stderr}` : "");
|
|
|
20460
20711
|
`) + `
|
|
20461
20712
|
`;
|
|
20462
20713
|
}
|
|
20463
|
-
if (existsSync7(
|
|
20714
|
+
if (existsSync7(path21.join(cwd, "Cargo.toml")) || existsSync7(path21.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
20464
20715
|
stdout += `Rust project detected.
|
|
20465
20716
|
`;
|
|
20466
20717
|
}
|
|
@@ -20493,7 +20744,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
20493
20744
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
20494
20745
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
20495
20746
|
function changelogContext(root, range) {
|
|
20496
|
-
const cwd =
|
|
20747
|
+
const cwd = path21.resolve(repoRoot(root));
|
|
20497
20748
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
20498
20749
|
return invalidRange(cwd);
|
|
20499
20750
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -20511,7 +20762,7 @@ function changelogContext(root, range) {
|
|
|
20511
20762
|
stdout += CHANGELOG_RULES + `
|
|
20512
20763
|
`;
|
|
20513
20764
|
stdout += printSection("Existing CHANGELOG.md");
|
|
20514
|
-
const changelogPath =
|
|
20765
|
+
const changelogPath = path21.join(cwd, "CHANGELOG.md");
|
|
20515
20766
|
if (existsSync7(changelogPath)) {
|
|
20516
20767
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
20517
20768
|
`;
|
|
@@ -20531,7 +20782,7 @@ function changelogContext(root, range) {
|
|
|
20531
20782
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
20532
20783
|
}
|
|
20533
20784
|
function docsRefreshContext(root, range) {
|
|
20534
|
-
const cwd =
|
|
20785
|
+
const cwd = path21.resolve(repoRoot(root));
|
|
20535
20786
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
20536
20787
|
return invalidRange(cwd);
|
|
20537
20788
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -20553,7 +20804,7 @@ function docsRefreshContext(root, range) {
|
|
|
20553
20804
|
`) + `
|
|
20554
20805
|
`;
|
|
20555
20806
|
stdout += printSection("README Preview");
|
|
20556
|
-
const readme =
|
|
20807
|
+
const readme = path21.join(cwd, "README.md");
|
|
20557
20808
|
if (existsSync7(readme)) {
|
|
20558
20809
|
stdout += readTrimmed2(readme, 220) + `
|
|
20559
20810
|
`;
|
|
@@ -20562,7 +20813,7 @@ function docsRefreshContext(root, range) {
|
|
|
20562
20813
|
`;
|
|
20563
20814
|
}
|
|
20564
20815
|
stdout += printSection("Package Scripts");
|
|
20565
|
-
const pkgPath =
|
|
20816
|
+
const pkgPath = path21.join(cwd, "package.json");
|
|
20566
20817
|
if (existsSync7(pkgPath)) {
|
|
20567
20818
|
try {
|
|
20568
20819
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
@@ -20576,7 +20827,7 @@ function docsRefreshContext(root, range) {
|
|
|
20576
20827
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
20577
20828
|
}
|
|
20578
20829
|
function releaseNotesContext(root, rangeOrTag) {
|
|
20579
|
-
const cwd =
|
|
20830
|
+
const cwd = path21.resolve(repoRoot(root));
|
|
20580
20831
|
if (!rangeOrTag) {
|
|
20581
20832
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
20582
20833
|
`, exitCode: 1, cwd };
|
|
@@ -20614,7 +20865,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
20614
20865
|
`;
|
|
20615
20866
|
stdout += printSection("Existing Release Files");
|
|
20616
20867
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
20617
|
-
if (existsSync7(
|
|
20868
|
+
if (existsSync7(path21.join(cwd, rel)))
|
|
20618
20869
|
stdout += rel + `
|
|
20619
20870
|
`;
|
|
20620
20871
|
}
|
|
@@ -20645,8 +20896,8 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
20645
20896
|
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
20646
20897
|
|
|
20647
20898
|
// packages/workit-core/src/core/pr-create.ts
|
|
20648
|
-
import
|
|
20649
|
-
import
|
|
20899
|
+
import fs16 from "node:fs";
|
|
20900
|
+
import path22 from "node:path";
|
|
20650
20901
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
20651
20902
|
// packages/workit-core/src/forge/pr-body.ts
|
|
20652
20903
|
function parseGhIssue(value) {
|
|
@@ -20657,13 +20908,13 @@ function parseGhIssue(value) {
|
|
|
20657
20908
|
// packages/workit-core/src/core/pr-create.ts
|
|
20658
20909
|
function whichOnPath(tool) {
|
|
20659
20910
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
20660
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
20911
|
+
for (const dir of (process.env.PATH ?? "").split(path22.delimiter)) {
|
|
20661
20912
|
if (!dir)
|
|
20662
20913
|
continue;
|
|
20663
20914
|
for (const name of names) {
|
|
20664
|
-
const candidate =
|
|
20915
|
+
const candidate = path22.join(dir, name);
|
|
20665
20916
|
try {
|
|
20666
|
-
|
|
20917
|
+
fs16.accessSync(candidate, fs16.constants.X_OK);
|
|
20667
20918
|
return candidate;
|
|
20668
20919
|
} catch {}
|
|
20669
20920
|
}
|
|
@@ -20781,8 +21032,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
|
20781
21032
|
};
|
|
20782
21033
|
|
|
20783
21034
|
// packages/workit-core/src/core/youtrack.ts
|
|
20784
|
-
import
|
|
20785
|
-
import
|
|
21035
|
+
import fs17 from "node:fs";
|
|
21036
|
+
import path23 from "node:path";
|
|
20786
21037
|
|
|
20787
21038
|
// packages/workit-core/src/core/templates.ts
|
|
20788
21039
|
var repoRoot2 = assetRoot();
|
|
@@ -20795,22 +21046,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
20795
21046
|
// packages/workit-core/src/core/youtrack.ts
|
|
20796
21047
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
20797
21048
|
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
20798
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
20799
|
-
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");
|
|
20800
21051
|
var youTrackTokenModeOk = (p) => {
|
|
20801
21052
|
if (process.platform === "win32")
|
|
20802
21053
|
return true;
|
|
20803
|
-
const mode =
|
|
21054
|
+
const mode = fs17.statSync(p).mode & 511;
|
|
20804
21055
|
return mode === 384;
|
|
20805
21056
|
};
|
|
20806
21057
|
function readYouTrackConfig(required) {
|
|
20807
21058
|
const cfgPath = youTrackConfigPath();
|
|
20808
|
-
if (!
|
|
21059
|
+
if (!fs17.existsSync(cfgPath)) {
|
|
20809
21060
|
return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
|
|
20810
21061
|
}
|
|
20811
21062
|
let parsed;
|
|
20812
21063
|
try {
|
|
20813
|
-
parsed = JSON.parse(
|
|
21064
|
+
parsed = JSON.parse(fs17.readFileSync(cfgPath, "utf8"));
|
|
20814
21065
|
} catch {
|
|
20815
21066
|
return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
|
|
20816
21067
|
}
|
|
@@ -20821,11 +21072,11 @@ function readYouTrackConfig(required) {
|
|
|
20821
21072
|
}
|
|
20822
21073
|
var readYouTrackContextConfig = () => {
|
|
20823
21074
|
const cfgPath = youTrackReadOnlyConfigPath();
|
|
20824
|
-
if (!
|
|
21075
|
+
if (!fs17.existsSync(cfgPath))
|
|
20825
21076
|
return { data: {} };
|
|
20826
21077
|
let parsed;
|
|
20827
21078
|
try {
|
|
20828
|
-
parsed = JSON.parse(
|
|
21079
|
+
parsed = JSON.parse(fs17.readFileSync(cfgPath, "utf8"));
|
|
20829
21080
|
} catch {
|
|
20830
21081
|
return { error: "YouTrack configuration is unavailable" };
|
|
20831
21082
|
}
|
|
@@ -20864,12 +21115,12 @@ var youTrackToken = () => {
|
|
|
20864
21115
|
if ("error" in loaded)
|
|
20865
21116
|
return loaded;
|
|
20866
21117
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
20867
|
-
const tokenPath = tokenFile ?
|
|
20868
|
-
if (!tokenPath || !
|
|
21118
|
+
const tokenPath = tokenFile ? path23.isAbsolute(tokenFile) ? path23.resolve(tokenFile) : path23.resolve(process.cwd(), tokenFile) : "";
|
|
21119
|
+
if (!tokenPath || !fs17.existsSync(tokenPath))
|
|
20869
21120
|
return { error: "missing youtrack.token" };
|
|
20870
21121
|
if (!youTrackTokenModeOk(tokenPath))
|
|
20871
21122
|
return { error: "youtrack.token mode must be 0600" };
|
|
20872
|
-
const token =
|
|
21123
|
+
const token = fs17.readFileSync(tokenPath, "utf8").trim();
|
|
20873
21124
|
if (!token)
|
|
20874
21125
|
return { error: "empty token file" };
|
|
20875
21126
|
if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
|
|
@@ -20915,7 +21166,7 @@ function youTrackTokenCreateUrl() {
|
|
|
20915
21166
|
const desc = String(defaults.description ?? "OpenCode workit — /wk-issue-update and /wk-meetings");
|
|
20916
21167
|
const scopes = Array.isArray(defaults.scopes) ? defaults.scopes : ["YouTrack"];
|
|
20917
21168
|
const base = String(config.baseUrl ?? "").replace(/\/+$/, "");
|
|
20918
|
-
const tokenFile = String(config.tokenFile ??
|
|
21169
|
+
const tokenFile = String(config.tokenFile ?? path23.join(path23.dirname(loaded.path), "youtrack.token"));
|
|
20919
21170
|
const tab = String(defaults.profileTab ?? "account-security");
|
|
20920
21171
|
const createUrl = base ? `${base}/users/me?${new URLSearchParams({ tab })}` : null;
|
|
20921
21172
|
const docsUrl = "https://www.jetbrains.com/help/youtrack/cloud/manage-permanent-token.html";
|
|
@@ -20924,7 +21175,7 @@ function youTrackTokenCreateUrl() {
|
|
|
20924
21175
|
tokenName: name,
|
|
20925
21176
|
tokenDescription: desc,
|
|
20926
21177
|
scopes,
|
|
20927
|
-
tokenFile:
|
|
21178
|
+
tokenFile: path23.resolve(tokenFile),
|
|
20928
21179
|
createUrl,
|
|
20929
21180
|
docsUrl,
|
|
20930
21181
|
prefillSupported: false,
|
|
@@ -20970,9 +21221,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
|
|
|
20970
21221
|
} catch {
|
|
20971
21222
|
continue;
|
|
20972
21223
|
}
|
|
20973
|
-
if (!
|
|
21224
|
+
if (!fs17.existsSync(full))
|
|
20974
21225
|
continue;
|
|
20975
|
-
const text =
|
|
21226
|
+
const text = fs17.readFileSync(full, "utf8");
|
|
20976
21227
|
const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
|
|
20977
21228
|
if (m)
|
|
20978
21229
|
return m[1];
|
|
@@ -21264,15 +21515,15 @@ function resolveGitRevision(root, value) {
|
|
|
21264
21515
|
}
|
|
21265
21516
|
function resolveInside(root, candidate) {
|
|
21266
21517
|
const base = realpathSync5(root);
|
|
21267
|
-
const target =
|
|
21268
|
-
if (target !== base && !target.startsWith(base +
|
|
21518
|
+
const target = path24.resolve(base, candidate);
|
|
21519
|
+
if (target !== base && !target.startsWith(base + path24.sep)) {
|
|
21269
21520
|
throw new Error("path must stay inside repository root");
|
|
21270
21521
|
}
|
|
21271
21522
|
let ancestor = target;
|
|
21272
21523
|
while (!existsSync8(ancestor))
|
|
21273
|
-
ancestor =
|
|
21524
|
+
ancestor = path24.dirname(ancestor);
|
|
21274
21525
|
const canonicalAncestor = realpathSync5(ancestor);
|
|
21275
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
21526
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path24.sep)) {
|
|
21276
21527
|
throw new Error("path must stay inside repository root");
|
|
21277
21528
|
}
|
|
21278
21529
|
return target;
|
|
@@ -21316,7 +21567,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
21316
21567
|
};
|
|
21317
21568
|
});
|
|
21318
21569
|
// packages/workit-core/src/hooks/context.ts
|
|
21319
|
-
import
|
|
21570
|
+
import path25 from "node:path";
|
|
21320
21571
|
var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
|
|
21321
21572
|
var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
|
|
21322
21573
|
var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
|
|
@@ -21401,7 +21652,7 @@ function entryCompactContext(store, entry, session, context) {
|
|
|
21401
21652
|
return null;
|
|
21402
21653
|
const freshness = { mode: "signal", current: () => recentSignal(store.root) };
|
|
21403
21654
|
const observed = task?.data.evidence.some(isObservedCheck) ?? false;
|
|
21404
|
-
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)] : []));
|
|
21405
21656
|
const observedFiles = filesKey(files);
|
|
21406
21657
|
const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
|
|
21407
21658
|
if (!compact.ok || !compact.data)
|
|
@@ -21433,27 +21684,27 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
|
|
|
21433
21684
|
};
|
|
21434
21685
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
21435
21686
|
import { realpathSync as realpathSync6 } from "node:fs";
|
|
21436
|
-
import
|
|
21687
|
+
import path26 from "node:path";
|
|
21437
21688
|
var NONE3 = { kind: "none" };
|
|
21438
21689
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
21439
21690
|
var canonical2 = (file) => {
|
|
21440
21691
|
let rest = "";
|
|
21441
|
-
for (let dir =
|
|
21692
|
+
for (let dir = path26.resolve(file);; dir = path26.dirname(dir)) {
|
|
21442
21693
|
try {
|
|
21443
|
-
return
|
|
21694
|
+
return path26.join(realpathSync6(dir), rest);
|
|
21444
21695
|
} catch {
|
|
21445
|
-
if (
|
|
21446
|
-
return
|
|
21447
|
-
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);
|
|
21448
21699
|
}
|
|
21449
21700
|
}
|
|
21450
21701
|
};
|
|
21451
21702
|
var exempt = (cwd, root, file, plans) => {
|
|
21452
21703
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
21453
|
-
const relative =
|
|
21454
|
-
if (relative.startsWith("..") ||
|
|
21704
|
+
const relative = path26.relative(fold(canonical2(root)), fold(canonical2(path26.resolve(cwd, file))));
|
|
21705
|
+
if (relative.startsWith("..") || path26.isAbsolute(relative))
|
|
21455
21706
|
return true;
|
|
21456
|
-
const posix = relative.split(
|
|
21707
|
+
const posix = relative.split(path26.sep).join("/");
|
|
21457
21708
|
const segments = posix.split("/");
|
|
21458
21709
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
21459
21710
|
};
|
|
@@ -21551,7 +21802,7 @@ var shellWrites = (command) => {
|
|
|
21551
21802
|
return { writes: true, targets: unknown ? null : targets };
|
|
21552
21803
|
};
|
|
21553
21804
|
var cache2 = new Map;
|
|
21554
|
-
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
21805
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path26.resolve(root, file)) ?? "-").join("\x00");
|
|
21555
21806
|
var gateFor = (cwd) => {
|
|
21556
21807
|
const store = new TaskStore(cwd);
|
|
21557
21808
|
const listed = store.listTaskIndex();
|
|
@@ -21719,10 +21970,10 @@ function stopObligation(input, descriptor) {
|
|
|
21719
21970
|
const branch = currentBranch(input.cwd);
|
|
21720
21971
|
if (!branch || guardedBranch(input.cwd, branch))
|
|
21721
21972
|
return null;
|
|
21722
|
-
const ledger = readLedger(input.cwd);
|
|
21973
|
+
const ledger = readLedger(input.cwd, { integrity: true });
|
|
21723
21974
|
if (!ledger.ok)
|
|
21724
21975
|
return null;
|
|
21725
|
-
const all = ledger.value.rows;
|
|
21976
|
+
const all = ledger.value.rows.filter((row) => row.integrity !== "unverified");
|
|
21726
21977
|
const rows = all.filter((row) => row.branch === branch);
|
|
21727
21978
|
const mine = rows.filter((row) => sessionOf(row) === session);
|
|
21728
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"));
|
|
@@ -22090,11 +22341,11 @@ var OPENCODE_DESCRIPTOR = {
|
|
|
22090
22341
|
]
|
|
22091
22342
|
};
|
|
22092
22343
|
// packages/workit-opencode/src/shared/init-apply.ts
|
|
22093
|
-
import
|
|
22344
|
+
import path31 from "node:path";
|
|
22094
22345
|
|
|
22095
22346
|
// packages/workit-core/src/core/gitignore.ts
|
|
22096
22347
|
import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
22097
|
-
import
|
|
22348
|
+
import path27 from "node:path";
|
|
22098
22349
|
var GITIGNORE_ENTRIES = [
|
|
22099
22350
|
"# workit: SDD working state (never commit)",
|
|
22100
22351
|
"docs/*/sdd/",
|
|
@@ -22114,7 +22365,7 @@ var GITIGNORE_ENTRIES = [
|
|
|
22114
22365
|
var ensureProjectGitignore = (workspaceRoot, confirmed) => {
|
|
22115
22366
|
if (!confirmed)
|
|
22116
22367
|
return { ok: false, error: "confirmed: true required" };
|
|
22117
|
-
const file =
|
|
22368
|
+
const file = path27.join(workspaceRoot, ".gitignore");
|
|
22118
22369
|
const existing = existsSync9(file) ? readFileSync6(file, "utf8") : "";
|
|
22119
22370
|
const existingLines = new Set(existing.split(`
|
|
22120
22371
|
`).map((l) => l.trim()).filter(Boolean));
|
|
@@ -22141,26 +22392,26 @@ var ensureProjectGitignore = (workspaceRoot, confirmed) => {
|
|
|
22141
22392
|
};
|
|
22142
22393
|
|
|
22143
22394
|
// packages/workit-core/src/core/hygiene.ts
|
|
22144
|
-
import { existsSync as existsSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
|
|
22145
|
-
import
|
|
22395
|
+
import { existsSync as existsSync10, readFileSync as readFileSync7, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
22396
|
+
import path28 from "node:path";
|
|
22146
22397
|
var repoRoot3 = assetRoot();
|
|
22147
|
-
var templatesDir = () =>
|
|
22398
|
+
var templatesDir = () => path28.join(repoRoot3, "templates", "hygiene");
|
|
22148
22399
|
var packageJson = (root) => {
|
|
22149
|
-
if (!existsSync10(
|
|
22400
|
+
if (!existsSync10(path28.join(root, "package.json")))
|
|
22150
22401
|
return null;
|
|
22151
22402
|
try {
|
|
22152
|
-
return JSON.parse(readFileSync7(
|
|
22403
|
+
return JSON.parse(readFileSync7(path28.join(root, "package.json"), "utf8"));
|
|
22153
22404
|
} catch {
|
|
22154
22405
|
return null;
|
|
22155
22406
|
}
|
|
22156
22407
|
};
|
|
22157
22408
|
var isOpenSource = (root) => {
|
|
22158
|
-
if (existsSync10(
|
|
22409
|
+
if (existsSync10(path28.join(root, "LICENSE")))
|
|
22159
22410
|
return true;
|
|
22160
22411
|
const pkg = packageJson(root);
|
|
22161
22412
|
if (pkg && !pkg.private)
|
|
22162
22413
|
return true;
|
|
22163
|
-
return
|
|
22414
|
+
return path28.basename(root) === "workflow-toolkit";
|
|
22164
22415
|
};
|
|
22165
22416
|
var licenseHolder = (root) => {
|
|
22166
22417
|
const pkg = packageJson(root);
|
|
@@ -22183,13 +22434,13 @@ var planHygieneFiles = (root, opts = {}) => {
|
|
|
22183
22434
|
files.push("LICENSE", "CONTRIBUTING.md");
|
|
22184
22435
|
const planned = [];
|
|
22185
22436
|
for (const file of files) {
|
|
22186
|
-
if (existsSync10(
|
|
22437
|
+
if (existsSync10(path28.join(root, file)))
|
|
22187
22438
|
continue;
|
|
22188
|
-
const tpl =
|
|
22439
|
+
const tpl = path28.join(templatesDir(), file);
|
|
22189
22440
|
if (!existsSync10(tpl))
|
|
22190
22441
|
continue;
|
|
22191
|
-
const content = readFileSync7(tpl, "utf8").replace(/<PROJECT>/g,
|
|
22192
|
-
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 });
|
|
22193
22444
|
}
|
|
22194
22445
|
return planned;
|
|
22195
22446
|
};
|
|
@@ -22199,19 +22450,19 @@ var ensureHygieneFiles = (root, opts) => {
|
|
|
22199
22450
|
const created = [];
|
|
22200
22451
|
for (const planned of planHygieneFiles(root, opts)) {
|
|
22201
22452
|
writeFileSync5(planned.path, planned.content, "utf8");
|
|
22202
|
-
created.push(
|
|
22453
|
+
created.push(path28.basename(planned.path));
|
|
22203
22454
|
}
|
|
22204
22455
|
return { ok: true, created };
|
|
22205
22456
|
};
|
|
22206
22457
|
|
|
22207
22458
|
// packages/workit-core/src/core/init.ts
|
|
22208
|
-
import
|
|
22209
|
-
import
|
|
22459
|
+
import fs18 from "node:fs";
|
|
22460
|
+
import path30 from "node:path";
|
|
22210
22461
|
import { spawnSync as spawnSync18 } from "node:child_process";
|
|
22211
22462
|
|
|
22212
22463
|
// packages/workit-core/src/core/workspace-branch-policy.ts
|
|
22213
22464
|
import { mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
|
|
22214
|
-
import
|
|
22465
|
+
import path29 from "node:path";
|
|
22215
22466
|
import { isDeepStrictEqual } from "node:util";
|
|
22216
22467
|
var readFileSafe = (p) => {
|
|
22217
22468
|
try {
|
|
@@ -22222,7 +22473,7 @@ var readFileSafe = (p) => {
|
|
|
22222
22473
|
};
|
|
22223
22474
|
function applyWorkspaceBranchPolicy(opts) {
|
|
22224
22475
|
const { workspace_root, env = process.env } = opts;
|
|
22225
|
-
const dir =
|
|
22476
|
+
const dir = path29.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
|
|
22226
22477
|
const {
|
|
22227
22478
|
status,
|
|
22228
22479
|
path: wsPath,
|
|
@@ -22234,7 +22485,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
22234
22485
|
if (status === "malformed" || status === "invalid")
|
|
22235
22486
|
return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
|
|
22236
22487
|
const detection = detectBranchPolicy(workspace_root);
|
|
22237
|
-
const name = String(env.WORKFLOW_BP_NAME ??
|
|
22488
|
+
const name = String(env.WORKFLOW_BP_NAME ?? path29.basename(workspace_root));
|
|
22238
22489
|
const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
|
|
22239
22490
|
const policy = {
|
|
22240
22491
|
preset: detection.preset,
|
|
@@ -22283,7 +22534,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
22283
22534
|
ok: false,
|
|
22284
22535
|
error: `workspaces.json changed while applying the branch policy: ${wsPath}`
|
|
22285
22536
|
};
|
|
22286
|
-
mkdirSync4(
|
|
22537
|
+
mkdirSync4(path29.dirname(wsPath), { recursive: true });
|
|
22287
22538
|
const tmp = `${wsPath}.${process.pid}.tmp`;
|
|
22288
22539
|
try {
|
|
22289
22540
|
writeFileSync6(tmp, JSON.stringify({ ...document, workspaces: next }, null, 2) + `
|
|
@@ -22318,7 +22569,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
22318
22569
|
var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE";
|
|
22319
22570
|
var readJson = (p) => {
|
|
22320
22571
|
try {
|
|
22321
|
-
const parsed = JSON.parse(
|
|
22572
|
+
const parsed = JSON.parse(fs18.readFileSync(p, "utf8"));
|
|
22322
22573
|
return isConfigObject(parsed) ? parsed : null;
|
|
22323
22574
|
} catch {
|
|
22324
22575
|
return null;
|
|
@@ -22348,7 +22599,7 @@ var youtrackJsonContent = (dir) => {
|
|
|
22348
22599
|
const meetingIssue = process.env.WORKFLOW_YT_MEETING_ISSUE;
|
|
22349
22600
|
return {
|
|
22350
22601
|
baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://youtrack.example.com",
|
|
22351
|
-
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ??
|
|
22602
|
+
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path30.join(dir, "youtrack.token"),
|
|
22352
22603
|
...meetingIssue ? {
|
|
22353
22604
|
meetingIssue,
|
|
22354
22605
|
meetingIssues: { general: { issue: meetingIssue, label: "General meetings" } }
|
|
@@ -22378,19 +22629,19 @@ var vcsJsonContent = () => {
|
|
|
22378
22629
|
};
|
|
22379
22630
|
function initApplyData(action, env = process.env) {
|
|
22380
22631
|
const dir = String(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
|
|
22381
|
-
|
|
22632
|
+
fs18.mkdirSync(dir, { recursive: true });
|
|
22382
22633
|
switch (action) {
|
|
22383
22634
|
case "youtrack_json": {
|
|
22384
|
-
const out =
|
|
22385
|
-
|
|
22635
|
+
const out = path30.join(dir, "youtrack.json");
|
|
22636
|
+
fs18.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
|
|
22386
22637
|
`, "utf8");
|
|
22387
22638
|
return { action, ok: true, path: out };
|
|
22388
22639
|
}
|
|
22389
22640
|
case "youtrack_token_placeholder": {
|
|
22390
|
-
const p =
|
|
22641
|
+
const p = path30.join(dir, "youtrack.token");
|
|
22391
22642
|
const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER2 + `
|
|
22392
22643
|
`, 384) === "preserved";
|
|
22393
|
-
const abs =
|
|
22644
|
+
const abs = path30.resolve(p);
|
|
22394
22645
|
return {
|
|
22395
22646
|
action,
|
|
22396
22647
|
ok: true,
|
|
@@ -22402,14 +22653,14 @@ function initApplyData(action, env = process.env) {
|
|
|
22402
22653
|
};
|
|
22403
22654
|
}
|
|
22404
22655
|
case "youtrack_scaffold": {
|
|
22405
|
-
const jsonOut =
|
|
22406
|
-
const tokenOut =
|
|
22407
|
-
|
|
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) + `
|
|
22408
22659
|
`, "utf8");
|
|
22409
22660
|
const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER2 + `
|
|
22410
22661
|
`, 384) === "preserved";
|
|
22411
|
-
const configPath =
|
|
22412
|
-
const tokenPath =
|
|
22662
|
+
const configPath = path30.resolve(jsonOut);
|
|
22663
|
+
const tokenPath = path30.resolve(tokenOut);
|
|
22413
22664
|
const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
|
|
22414
22665
|
process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
|
|
22415
22666
|
let tokenCreate = {};
|
|
@@ -22461,10 +22712,10 @@ function initApplyData(action, env = process.env) {
|
|
|
22461
22712
|
}
|
|
22462
22713
|
}
|
|
22463
22714
|
case "vcs_scaffold": {
|
|
22464
|
-
const jsonOut =
|
|
22465
|
-
|
|
22715
|
+
const jsonOut = path30.join(dir, "vcs.json");
|
|
22716
|
+
fs18.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
|
|
22466
22717
|
`, "utf8");
|
|
22467
|
-
const configPath =
|
|
22718
|
+
const configPath = path30.resolve(jsonOut);
|
|
22468
22719
|
const prev = process.env.WORKFLOW_VCS_CONFIG;
|
|
22469
22720
|
process.env.WORKFLOW_VCS_CONFIG = configPath;
|
|
22470
22721
|
try {
|
|
@@ -22583,7 +22834,7 @@ var executeInitApply = (args, directory, runtime) => {
|
|
|
22583
22834
|
protectedNames: args.branch_policy_protected
|
|
22584
22835
|
}, current);
|
|
22585
22836
|
writeConfig(next);
|
|
22586
|
-
return output(ok({ action: "config", path:
|
|
22837
|
+
return output(ok({ action: "config", path: path31.join(configDir(), "config.json"), ...next }));
|
|
22587
22838
|
}
|
|
22588
22839
|
const env = Object.fromEntries(Object.entries({
|
|
22589
22840
|
WORKFLOW_YT_BASE_URL: args.base_url,
|
|
@@ -22600,14 +22851,14 @@ var executeInitApply = (args, directory, runtime) => {
|
|
|
22600
22851
|
|
|
22601
22852
|
// packages/workit-opencode/src/shared/session.ts
|
|
22602
22853
|
import { realpathSync as realpathSync7 } from "node:fs";
|
|
22603
|
-
import
|
|
22854
|
+
import path32 from "node:path";
|
|
22604
22855
|
var sameWorkspace = (expected, observed) => {
|
|
22605
22856
|
if (typeof observed !== "string" || !observed)
|
|
22606
22857
|
return false;
|
|
22607
22858
|
try {
|
|
22608
22859
|
return realpathSync7(expected) === realpathSync7(observed);
|
|
22609
22860
|
} catch {
|
|
22610
|
-
return
|
|
22861
|
+
return path32.resolve(expected) === path32.resolve(observed);
|
|
22611
22862
|
}
|
|
22612
22863
|
};
|
|
22613
22864
|
|
|
@@ -22679,7 +22930,7 @@ var workitFamilyOf = (toolName) => {
|
|
|
22679
22930
|
|
|
22680
22931
|
// packages/workit-opencode/src/tools/context.ts
|
|
22681
22932
|
import { realpathSync as realpathSync8 } from "node:fs";
|
|
22682
|
-
import
|
|
22933
|
+
import path33 from "node:path";
|
|
22683
22934
|
var createContextTool = () => ({
|
|
22684
22935
|
description: "Read Git, pull request, YouTrack, issue, changelog, release, or affected-file context.",
|
|
22685
22936
|
execute: async (args, context) => {
|
|
@@ -22689,7 +22940,7 @@ var createContextTool = () => ({
|
|
|
22689
22940
|
let root;
|
|
22690
22941
|
try {
|
|
22691
22942
|
const cwd = parsed.data.cwd;
|
|
22692
|
-
root = realpathSync8(cwd ?
|
|
22943
|
+
root = realpathSync8(cwd ? path33.resolve(context.directory, cwd) : context.directory);
|
|
22693
22944
|
} catch {
|
|
22694
22945
|
return JSON.stringify(failure2("invalid_input", "context target directory cannot be resolved"), null, 2);
|
|
22695
22946
|
}
|
|
@@ -23282,7 +23533,7 @@ var childTaskMissing = (store, input) => {
|
|
|
23282
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`;
|
|
23283
23534
|
|
|
23284
23535
|
// packages/workit-opencode/src/v2/shell.ts
|
|
23285
|
-
import
|
|
23536
|
+
import path34 from "node:path";
|
|
23286
23537
|
var commandOf = (input) => {
|
|
23287
23538
|
if (input === null || typeof input !== "object")
|
|
23288
23539
|
return null;
|
|
@@ -23291,7 +23542,7 @@ var commandOf = (input) => {
|
|
|
23291
23542
|
};
|
|
23292
23543
|
var hookInput = (root, sessionID, id, shell) => ({
|
|
23293
23544
|
host: "opencode",
|
|
23294
|
-
cwd: shell.workdir ?
|
|
23545
|
+
cwd: shell.workdir ? path34.resolve(root, shell.workdir) : root,
|
|
23295
23546
|
session: { id: sessionID, agentId: null, agentType: null, parentId: null },
|
|
23296
23547
|
permissionMode: null,
|
|
23297
23548
|
transcriptPath: null,
|
|
@@ -23476,37 +23727,37 @@ var createStopControl = (root, port) => {
|
|
|
23476
23727
|
|
|
23477
23728
|
// packages/workit-opencode/src/v2/registry.ts
|
|
23478
23729
|
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
|
|
23479
|
-
import
|
|
23730
|
+
import path36 from "node:path";
|
|
23480
23731
|
|
|
23481
23732
|
// packages/workit-opencode/src/shared/assets.ts
|
|
23482
23733
|
import { existsSync as existsSync11, realpathSync as realpathSync9 } from "node:fs";
|
|
23483
|
-
import
|
|
23734
|
+
import path35 from "node:path";
|
|
23484
23735
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
23485
23736
|
var moduleDir = () => {
|
|
23486
23737
|
try {
|
|
23487
|
-
return realpathSync9(
|
|
23738
|
+
return realpathSync9(path35.dirname(fileURLToPath2(import.meta.url)));
|
|
23488
23739
|
} catch {
|
|
23489
|
-
return
|
|
23740
|
+
return path35.dirname(fileURLToPath2(import.meta.url));
|
|
23490
23741
|
}
|
|
23491
23742
|
};
|
|
23492
23743
|
var packageRoot2 = () => {
|
|
23493
23744
|
const dir = moduleDir();
|
|
23494
23745
|
for (const candidate of [
|
|
23495
|
-
|
|
23496
|
-
|
|
23497
|
-
|
|
23746
|
+
path35.resolve(dir, ".."),
|
|
23747
|
+
path35.resolve(dir, "..", ".."),
|
|
23748
|
+
path35.resolve(dir, "..", "..", "..")
|
|
23498
23749
|
]) {
|
|
23499
|
-
if (existsSync11(
|
|
23750
|
+
if (existsSync11(path35.join(candidate, "package.json")))
|
|
23500
23751
|
return candidate;
|
|
23501
23752
|
}
|
|
23502
|
-
return
|
|
23753
|
+
return path35.resolve(dir, "..");
|
|
23503
23754
|
};
|
|
23504
23755
|
var skillsRoot = () => {
|
|
23505
23756
|
const root = packageRoot2();
|
|
23506
|
-
const canonical =
|
|
23507
|
-
if (existsSync11(
|
|
23757
|
+
const canonical = path35.resolve(root, "..", "workit-core", "skills");
|
|
23758
|
+
if (existsSync11(path35.join(root, "src")) && existsSync11(canonical))
|
|
23508
23759
|
return canonical;
|
|
23509
|
-
return
|
|
23760
|
+
return path35.join(root, "assets", "skills");
|
|
23510
23761
|
};
|
|
23511
23762
|
|
|
23512
23763
|
// packages/workit-opencode/src/v2/registry.ts
|
|
@@ -23533,11 +23784,11 @@ var registerSkills = async (ctx) => {
|
|
|
23533
23784
|
if (!existsSync12(skillsDir))
|
|
23534
23785
|
return new Set;
|
|
23535
23786
|
const listed = await ctx.skill.list().catch(() => ({ data: [] }));
|
|
23536
|
-
const own = (skill) => typeof skill.path === "string" &&
|
|
23787
|
+
const own = (skill) => typeof skill.path === "string" && path36.resolve(skill.path).startsWith(skillsDir + path36.sep);
|
|
23537
23788
|
const userSkills = new Set((listed?.data ?? []).filter((skill) => !own(skill)).map((skill) => skill.id));
|
|
23538
23789
|
const packaged = new Map;
|
|
23539
23790
|
for (const id of WORKIT_METHOD_SKILLS) {
|
|
23540
|
-
const file =
|
|
23791
|
+
const file = path36.join(skillsDir, id, "SKILL.md");
|
|
23541
23792
|
if (!userSkills.has(id) && existsSync12(file))
|
|
23542
23793
|
packaged.set(id, file);
|
|
23543
23794
|
}
|
|
@@ -23584,23 +23835,23 @@ var registerCommands = async (ctx, registeredSkills) => {
|
|
|
23584
23835
|
};
|
|
23585
23836
|
|
|
23586
23837
|
// packages/workit-opencode/src/stale-sources.ts
|
|
23587
|
-
import { existsSync as existsSync13, readdirSync as
|
|
23588
|
-
import
|
|
23838
|
+
import { existsSync as existsSync13, readdirSync as readdirSync6 } from "node:fs";
|
|
23839
|
+
import path37 from "node:path";
|
|
23589
23840
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
23590
23841
|
var typescriptFiles = (directory) => {
|
|
23591
23842
|
if (!existsSync13(directory))
|
|
23592
23843
|
return [];
|
|
23593
|
-
return
|
|
23594
|
-
const file =
|
|
23844
|
+
return readdirSync6(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
23845
|
+
const file = path37.join(directory, entry.name);
|
|
23595
23846
|
return entry.isDirectory() ? typescriptFiles(file) : entry.isFile() && file.endsWith(".ts") ? [file] : [];
|
|
23596
23847
|
});
|
|
23597
23848
|
};
|
|
23598
23849
|
var pluginSourceFiles = [
|
|
23599
23850
|
...new Set([
|
|
23600
23851
|
fileURLToPath3(import.meta.url),
|
|
23601
|
-
...typescriptFiles(
|
|
23602
|
-
|
|
23603
|
-
...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"))
|
|
23604
23855
|
])
|
|
23605
23856
|
].filter((file) => existsSync13(file)).toSorted();
|
|
23606
23857
|
|