@brainervirus/workit-cursor 8.24.0 → 8.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor-plugin/plugin.json +1 -1
- package/dist/cursor-session-start.js +2198 -1951
- package/dist/mcp-server.js +1650 -1493
- package/dist/workit-hook.js +2218 -1949
- package/package.json +3 -3
- package/skills/workit-fanout/SKILL.md +1 -1
- package/skills/workit-fanout/references/brief.md +4 -4
- package/skills/workit-review/SKILL.md +2 -0
- package/skills/workit-ship/SKILL.md +1 -1
package/dist/mcp-server.js
CHANGED
|
@@ -17781,6 +17781,26 @@ function resolveRef(cwd, ref) {
|
|
|
17781
17781
|
return null;
|
|
17782
17782
|
return line(git(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
|
|
17783
17783
|
}
|
|
17784
|
+
function originHeadRef(cwd) {
|
|
17785
|
+
return line(git(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]));
|
|
17786
|
+
}
|
|
17787
|
+
function ownChangeDrift(cwd, range) {
|
|
17788
|
+
const before = diffRaw(cwd, range.fromFork, range.from);
|
|
17789
|
+
const after = diffRaw(cwd, range.toFork, range.to);
|
|
17790
|
+
if (!before || !after)
|
|
17791
|
+
return null;
|
|
17792
|
+
const judged = new Map(before.map((entry) => [entry.path, entry]));
|
|
17793
|
+
const current = new Map(after.map((entry) => [entry.path, entry]));
|
|
17794
|
+
const same = (a, b) => a !== undefined && b !== undefined && a.oldMode === b.oldMode && a.oldBlob === b.oldBlob && a.newMode === b.newMode && a.newBlob === b.newBlob;
|
|
17795
|
+
return [...new Set([...judged.keys(), ...current.keys()])].filter((file) => !same(judged.get(file), current.get(file))).toSorted();
|
|
17796
|
+
}
|
|
17797
|
+
function sameOwnChange(cwd, range) {
|
|
17798
|
+
const fromFork = mergeBase(cwd, range.fromBase, range.from);
|
|
17799
|
+
const toFork = mergeBase(cwd, range.toBase, range.to);
|
|
17800
|
+
if (!fromFork || !toFork)
|
|
17801
|
+
return false;
|
|
17802
|
+
return ownChangeDrift(cwd, { from: range.from, to: range.to, fromFork, toFork })?.length === 0;
|
|
17803
|
+
}
|
|
17784
17804
|
var LANE_DIFF = [
|
|
17785
17805
|
"-c",
|
|
17786
17806
|
"core.quotePath=false",
|
|
@@ -21407,9 +21427,9 @@ var workerBlocksTransition = (transition, state) => transition === "pause" ? fal
|
|
|
21407
21427
|
|
|
21408
21428
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
21409
21429
|
import { createHash as createHash10 } from "node:crypto";
|
|
21410
|
-
import * as
|
|
21411
|
-
import { spawnSync as
|
|
21412
|
-
import
|
|
21430
|
+
import * as fs13 from "node:fs";
|
|
21431
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
21432
|
+
import path17 from "node:path";
|
|
21413
21433
|
|
|
21414
21434
|
// packages/workit-core/src/check-config.ts
|
|
21415
21435
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
@@ -21630,68 +21650,20 @@ function checkCommand(args, version, onPath = workitAvailable()) {
|
|
|
21630
21650
|
|
|
21631
21651
|
// packages/workit-core/src/core/policy/derive.ts
|
|
21632
21652
|
import { existsSync as existsSync5 } from "node:fs";
|
|
21633
|
-
import
|
|
21653
|
+
import path16 from "node:path";
|
|
21634
21654
|
|
|
21635
21655
|
// packages/workit-core/src/ledger.ts
|
|
21636
21656
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
21637
21657
|
import { createHash as createHash9, randomBytes as randomBytes3 } from "node:crypto";
|
|
21638
21658
|
import fs12 from "node:fs";
|
|
21659
|
+
import path15 from "node:path";
|
|
21660
|
+
|
|
21661
|
+
// packages/workit-core/src/core/policy/lane.ts
|
|
21662
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
21639
21663
|
import path14 from "node:path";
|
|
21640
|
-
|
|
21641
|
-
|
|
21642
|
-
var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
|
|
21643
|
-
var FAILING = new Set(["failed", "blocked"]);
|
|
21644
|
-
var STRONG_RESULTS = new Set(["verified", "tests-verified"]);
|
|
21645
|
-
var PR_ROW_TYPES = new Set([
|
|
21646
|
-
"pr.created",
|
|
21647
|
-
"pr.status",
|
|
21648
|
-
"pr.merged",
|
|
21649
|
-
"pr.linked"
|
|
21650
|
-
]);
|
|
21651
|
-
var err = (code, error, unblock) => ({
|
|
21652
|
-
ok: false,
|
|
21653
|
-
code,
|
|
21654
|
-
error,
|
|
21655
|
-
...unblock ? { unblock } : {}
|
|
21656
|
-
});
|
|
21657
|
-
var gitRun2 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
|
|
21658
|
-
const run = spawnSync9("git", args, {
|
|
21659
|
-
cwd,
|
|
21660
|
-
encoding: "utf8",
|
|
21661
|
-
timeout: timeoutMs,
|
|
21662
|
-
killSignal: "SIGKILL",
|
|
21663
|
-
windowsHide: true,
|
|
21664
|
-
maxBuffer: 256 * 1024 * 1024,
|
|
21665
|
-
env: {
|
|
21666
|
-
...process.env,
|
|
21667
|
-
GIT_TERMINAL_PROMPT: "0",
|
|
21668
|
-
GIT_OPTIONAL_LOCKS: "0",
|
|
21669
|
-
LC_ALL: "C",
|
|
21670
|
-
LANGUAGE: "C"
|
|
21671
|
-
}
|
|
21672
|
-
});
|
|
21673
|
-
return {
|
|
21674
|
-
ok: run.status === 0,
|
|
21675
|
-
stdout: run.stdout ?? "",
|
|
21676
|
-
stderr: run.error ? String(run.error.message) : run.stderr ?? ""
|
|
21677
|
-
};
|
|
21678
|
-
};
|
|
21679
|
-
var git4 = (cwd, args) => {
|
|
21680
|
-
const run = gitRun2(cwd, args);
|
|
21681
|
-
const value = run.ok ? run.stdout.trim() : "";
|
|
21682
|
-
return value ? value : null;
|
|
21683
|
-
};
|
|
21664
|
+
|
|
21665
|
+
// packages/workit-core/src/git/default-base.ts
|
|
21684
21666
|
var safeRef = (value) => value.length > 0 && !value.startsWith("-");
|
|
21685
|
-
function resolveCommit(cwd, ref) {
|
|
21686
|
-
if (!safeRef(ref))
|
|
21687
|
-
return null;
|
|
21688
|
-
return git4(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]);
|
|
21689
|
-
}
|
|
21690
|
-
function branchHead(cwd, branch) {
|
|
21691
|
-
if (!safeRef(branch))
|
|
21692
|
-
return null;
|
|
21693
|
-
return resolveCommit(cwd, `refs/heads/${branch}`) ?? resolveCommit(cwd, `refs/remotes/origin/${branch}`);
|
|
21694
|
-
}
|
|
21695
21667
|
var trackTrunk = (cwd) => {
|
|
21696
21668
|
try {
|
|
21697
21669
|
const resolved = vcsConfig("resolve", cwd);
|
|
@@ -21707,11 +21679,11 @@ function defaultBase(cwd) {
|
|
|
21707
21679
|
const trunk = trackTrunk(cwd);
|
|
21708
21680
|
if (trunk) {
|
|
21709
21681
|
for (const candidate of [`origin/${trunk}`, trunk])
|
|
21710
|
-
if (
|
|
21682
|
+
if (resolveRef(cwd, candidate))
|
|
21711
21683
|
return candidate;
|
|
21712
21684
|
}
|
|
21713
|
-
const originHead =
|
|
21714
|
-
if (originHead &&
|
|
21685
|
+
const originHead = originHeadRef(cwd);
|
|
21686
|
+
if (originHead && resolveRef(cwd, originHead))
|
|
21715
21687
|
return originHead;
|
|
21716
21688
|
for (const candidate of [
|
|
21717
21689
|
"origin/main",
|
|
@@ -21721,1489 +21693,1611 @@ function defaultBase(cwd) {
|
|
|
21721
21693
|
"origin/develop",
|
|
21722
21694
|
"develop"
|
|
21723
21695
|
])
|
|
21724
|
-
if (
|
|
21696
|
+
if (resolveRef(cwd, candidate))
|
|
21725
21697
|
return candidate;
|
|
21726
21698
|
return null;
|
|
21727
21699
|
}
|
|
21728
|
-
|
|
21729
|
-
|
|
21730
|
-
|
|
21731
|
-
|
|
21732
|
-
"diff",
|
|
21733
|
-
"-U3",
|
|
21734
|
-
"--diff-algorithm=myers",
|
|
21735
|
-
"--indent-heuristic",
|
|
21736
|
-
"--no-color",
|
|
21737
|
-
"--no-ext-diff",
|
|
21738
|
-
"--no-textconv",
|
|
21739
|
-
"--no-renames",
|
|
21740
|
-
"--full-index",
|
|
21741
|
-
"--binary",
|
|
21742
|
-
"--src-prefix=a/",
|
|
21743
|
-
"--dst-prefix=b/",
|
|
21744
|
-
`${base}...${head}`,
|
|
21745
|
-
"--"
|
|
21746
|
-
], GIT_TIMEOUTS.worktree);
|
|
21747
|
-
if (!diff.ok || !diff.stdout)
|
|
21700
|
+
|
|
21701
|
+
// packages/workit-core/src/core/policy/attributes.ts
|
|
21702
|
+
function patternRegex(dir, pattern) {
|
|
21703
|
+
if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
|
|
21748
21704
|
return null;
|
|
21749
|
-
const
|
|
21750
|
-
|
|
21751
|
-
|
|
21752
|
-
|
|
21753
|
-
|
|
21754
|
-
|
|
21755
|
-
|
|
21756
|
-
|
|
21757
|
-
|
|
21758
|
-
|
|
21759
|
-
|
|
21760
|
-
|
|
21761
|
-
|
|
21762
|
-
|
|
21763
|
-
|
|
21764
|
-
|
|
21765
|
-
|
|
21766
|
-
|
|
21767
|
-
|
|
21768
|
-
|
|
21769
|
-
}
|
|
21770
|
-
|
|
21771
|
-
|
|
21772
|
-
|
|
21773
|
-
|
|
21774
|
-
4266872130,
|
|
21775
|
-
4283649346,
|
|
21776
|
-
1702057286,
|
|
21777
|
-
1397113167,
|
|
21778
|
-
12805120,
|
|
21779
|
-
16914839,
|
|
21780
|
-
1196443219,
|
|
21781
|
-
198183888
|
|
21782
|
-
]);
|
|
21783
|
-
var isNetworkFsType = (type) => NETWORK_FS_TYPES.has(type >>> 0);
|
|
21784
|
-
var onNetworkFs = (dir) => {
|
|
21785
|
-
if (process.platform !== "linux")
|
|
21786
|
-
return false;
|
|
21787
|
-
try {
|
|
21788
|
-
return isNetworkFsType(fs12.statfsSync(dir).type);
|
|
21789
|
-
} catch {
|
|
21790
|
-
return false;
|
|
21705
|
+
const anchored = pattern.includes("/");
|
|
21706
|
+
const body = pattern.replace(/^\//u, "");
|
|
21707
|
+
let source = "";
|
|
21708
|
+
for (let index = 0;index < body.length; index += 1) {
|
|
21709
|
+
const char = body[index];
|
|
21710
|
+
if (char === "*" && body[index + 1] === "*") {
|
|
21711
|
+
const slash = body[index + 2] === "/";
|
|
21712
|
+
source += slash ? "(?:.*/)?" : ".*";
|
|
21713
|
+
index += slash ? 2 : 1;
|
|
21714
|
+
} else if (char === "*")
|
|
21715
|
+
source += "[^/]*";
|
|
21716
|
+
else if (char === "?")
|
|
21717
|
+
source += "[^/]";
|
|
21718
|
+
else if (char === "[") {
|
|
21719
|
+
const end = body.indexOf("]", index + 2);
|
|
21720
|
+
if (end < 0)
|
|
21721
|
+
return null;
|
|
21722
|
+
const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
|
|
21723
|
+
source += `[${set}]`;
|
|
21724
|
+
index = end;
|
|
21725
|
+
} else if (char === "\\" && index + 1 < body.length) {
|
|
21726
|
+
source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
21727
|
+
index += 1;
|
|
21728
|
+
} else
|
|
21729
|
+
source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
21791
21730
|
}
|
|
21792
|
-
};
|
|
21793
|
-
var LOCK_WAIT_MS = 5000;
|
|
21794
|
-
var LOCK_STALE_MS = 30000;
|
|
21795
|
-
var LOCK_OWNER = "owner";
|
|
21796
|
-
var lockToken = (lock) => {
|
|
21731
|
+
const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
|
|
21797
21732
|
try {
|
|
21798
|
-
return
|
|
21733
|
+
return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
|
|
21799
21734
|
} catch {
|
|
21800
21735
|
return null;
|
|
21801
21736
|
}
|
|
21802
|
-
}
|
|
21803
|
-
|
|
21804
|
-
|
|
21805
|
-
|
|
21806
|
-
|
|
21807
|
-
|
|
21808
|
-
|
|
21809
|
-
|
|
21810
|
-
|
|
21811
|
-
|
|
21812
|
-
|
|
21813
|
-
|
|
21814
|
-
|
|
21815
|
-
|
|
21816
|
-
|
|
21817
|
-
return null;
|
|
21818
|
-
throw error;
|
|
21819
|
-
}
|
|
21820
|
-
const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
|
|
21821
|
-
fs12.writeFileSync(path14.join(lock, LOCK_OWNER), token);
|
|
21822
|
-
return token;
|
|
21823
|
-
},
|
|
21824
|
-
release(lock, token) {
|
|
21825
|
-
if (lockToken(lock) !== token)
|
|
21826
|
-
return false;
|
|
21827
|
-
const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
|
|
21828
|
-
try {
|
|
21829
|
-
fs12.renameSync(lock, tomb);
|
|
21830
|
-
} catch {
|
|
21831
|
-
return false;
|
|
21832
|
-
}
|
|
21833
|
-
if (lockToken(tomb) !== token) {
|
|
21834
|
-
try {
|
|
21835
|
-
fs12.renameSync(tomb, lock);
|
|
21836
|
-
} catch {}
|
|
21837
|
-
return false;
|
|
21838
|
-
}
|
|
21839
|
-
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
21840
|
-
return true;
|
|
21841
|
-
},
|
|
21842
|
-
token: lockToken,
|
|
21843
|
-
reap(lock, observed) {
|
|
21844
|
-
const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
|
|
21845
|
-
try {
|
|
21846
|
-
fs12.renameSync(lock, tomb);
|
|
21847
|
-
} catch {
|
|
21848
|
-
return false;
|
|
21849
|
-
}
|
|
21850
|
-
if (lockToken(tomb) === observed) {
|
|
21851
|
-
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
21852
|
-
return true;
|
|
21853
|
-
}
|
|
21854
|
-
try {
|
|
21855
|
-
fs12.renameSync(tomb, lock);
|
|
21856
|
-
} catch {
|
|
21857
|
-
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
21737
|
+
}
|
|
21738
|
+
function rulesOf(dir, text) {
|
|
21739
|
+
const out = [];
|
|
21740
|
+
for (const raw of text.split(`
|
|
21741
|
+
`)) {
|
|
21742
|
+
const line = raw.trim();
|
|
21743
|
+
if (!line || line.startsWith("#") || line.startsWith("[attr]"))
|
|
21744
|
+
continue;
|
|
21745
|
+
const [pattern, ...attributes] = line.split(/\s+/u);
|
|
21746
|
+
let generated = null;
|
|
21747
|
+
for (const attribute of attributes) {
|
|
21748
|
+
if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
|
|
21749
|
+
generated = true;
|
|
21750
|
+
else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
|
|
21751
|
+
generated = false;
|
|
21858
21752
|
}
|
|
21859
|
-
|
|
21860
|
-
}
|
|
21861
|
-
};
|
|
21862
|
-
function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
|
|
21863
|
-
const lock = `${file}.lock`;
|
|
21864
|
-
const deadline = Date.now() + waitMs;
|
|
21865
|
-
let token = null;
|
|
21866
|
-
for (;; ) {
|
|
21867
|
-
token = ledgerLock.acquire(lock);
|
|
21868
|
-
if (token)
|
|
21869
|
-
break;
|
|
21870
|
-
const observed = lockToken(lock);
|
|
21871
|
-
const age = lockAgeMs(lock);
|
|
21872
|
-
if (age !== null && age > LOCK_STALE_MS && ledgerLock.reap(lock, observed))
|
|
21753
|
+
if (generated === null)
|
|
21873
21754
|
continue;
|
|
21874
|
-
|
|
21875
|
-
|
|
21876
|
-
|
|
21755
|
+
const regex = patternRegex(dir, pattern);
|
|
21756
|
+
if (regex)
|
|
21757
|
+
out.push({ regex, generated });
|
|
21877
21758
|
}
|
|
21878
|
-
|
|
21879
|
-
|
|
21880
|
-
|
|
21881
|
-
|
|
21759
|
+
return out;
|
|
21760
|
+
}
|
|
21761
|
+
function generatedByAttributes(files, paths) {
|
|
21762
|
+
const rules = [...files].map(([file, text]) => {
|
|
21763
|
+
const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "";
|
|
21764
|
+
return {
|
|
21765
|
+
depth: dir ? dir.split("/").length : 0,
|
|
21766
|
+
rules: rulesOf(dir, text)
|
|
21767
|
+
};
|
|
21768
|
+
}).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
|
|
21769
|
+
const out = new Set;
|
|
21770
|
+
for (const file of paths) {
|
|
21771
|
+
const last = rules.findLast((rule) => rule.regex.test(file));
|
|
21772
|
+
if (last?.generated)
|
|
21773
|
+
out.add(file);
|
|
21882
21774
|
}
|
|
21775
|
+
return out;
|
|
21883
21776
|
}
|
|
21884
|
-
|
|
21885
|
-
|
|
21886
|
-
|
|
21887
|
-
var
|
|
21888
|
-
var
|
|
21889
|
-
|
|
21890
|
-
|
|
21891
|
-
|
|
21892
|
-
|
|
21893
|
-
|
|
21894
|
-
|
|
21895
|
-
|
|
21896
|
-
|
|
21777
|
+
|
|
21778
|
+
// packages/workit-core/src/core/policy/substance.ts
|
|
21779
|
+
var SLASH_BLOCK = [["/*", "*/"]];
|
|
21780
|
+
var C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
|
|
21781
|
+
var SYNTAXES = Object.fromEntries([
|
|
21782
|
+
...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
|
|
21783
|
+
...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
|
|
21784
|
+
ext,
|
|
21785
|
+
{ ...C_LIKE, triple: true }
|
|
21786
|
+
]),
|
|
21787
|
+
["go", { ...C_LIKE, rawBacktick: true }],
|
|
21788
|
+
...["css", "scss", "less"].map((ext) => [
|
|
21789
|
+
ext,
|
|
21790
|
+
{ line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
|
|
21791
|
+
]),
|
|
21792
|
+
...["py", "pyi"].map((ext) => [
|
|
21793
|
+
ext,
|
|
21794
|
+
{ line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
|
|
21795
|
+
]),
|
|
21796
|
+
["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
|
|
21797
|
+
...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
|
|
21798
|
+
ext,
|
|
21799
|
+
{ line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
|
|
21800
|
+
])
|
|
21801
|
+
]);
|
|
21802
|
+
var DIRECTIVE = /@ts-|eslint|prettier|biome|oxlint|istanbul|\bc8\b|\bv8\b|@jsx|@flow|@vite|webpack|__PURE__|__NO_SIDE_EFFECTS__|source(?:Mapping)?URL|^\/\s*<|go:|\+build|nolint|noqa|type:|pyright|mypy|pylint|pragma|coding[:=]|fmt:|NOSONAR|@license|@preserve|@refresh|environment|language=|cython|ruff|isort|deno-|@vitest|@jest|swiftlint|ktlint|checkstyle|suppress|nullable|#(?:end)?region/iu;
|
|
21803
|
+
var extensionOf = (file) => {
|
|
21804
|
+
const name = file.slice(file.lastIndexOf("/") + 1);
|
|
21805
|
+
const dot = name.lastIndexOf(".");
|
|
21806
|
+
return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
|
|
21807
|
+
};
|
|
21808
|
+
var CI_PATH = /^\.github\/|(?:^|\/)(?:\.gitlab-ci\.ya?ml|Jenkinsfile|azure-pipelines\.ya?ml|\.pre-commit-config\.ya?ml|\.circleci\/.*)$/u;
|
|
21809
|
+
var syntaxOf = (file) => CI_PATH.test(file) ? null : SYNTAXES[extensionOf(file)] ?? null;
|
|
21810
|
+
var SPACE = /[ \t\r\f\v]/u;
|
|
21811
|
+
var WORD = /[\w$]/u;
|
|
21812
|
+
var NO_SPACE_AFTER = "([{,;";
|
|
21813
|
+
var NO_SPACE_BEFORE = ")]},;";
|
|
21814
|
+
var REGEX_AFTER = "(,=:[!&|?{};+-*%<>~^";
|
|
21815
|
+
var REGEX_KEYWORDS = new Set([
|
|
21816
|
+
"return",
|
|
21817
|
+
"typeof",
|
|
21818
|
+
"instanceof",
|
|
21819
|
+
"case",
|
|
21820
|
+
"do",
|
|
21821
|
+
"else",
|
|
21822
|
+
"in",
|
|
21823
|
+
"of",
|
|
21824
|
+
"new",
|
|
21825
|
+
"delete",
|
|
21826
|
+
"void",
|
|
21827
|
+
"throw",
|
|
21828
|
+
"yield",
|
|
21829
|
+
"await"
|
|
21830
|
+
]);
|
|
21831
|
+
function substanceOf(file, text) {
|
|
21832
|
+
const syntax = syntaxOf(file);
|
|
21833
|
+
if (!syntax)
|
|
21834
|
+
return plainSubstance(text);
|
|
21835
|
+
const markup = syntax.block.some(([open]) => open === "<!--");
|
|
21836
|
+
if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
|
|
21837
|
+
return plainSubstance(text);
|
|
21838
|
+
return lex(syntax, text);
|
|
21839
|
+
}
|
|
21840
|
+
function plainSubstance(text) {
|
|
21841
|
+
return text.split(`
|
|
21842
|
+
`).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
|
|
21843
|
+
`);
|
|
21844
|
+
}
|
|
21845
|
+
function lex(syntax, text) {
|
|
21846
|
+
const collapse = !syntax.indented;
|
|
21847
|
+
let out = "";
|
|
21848
|
+
let lineStart = 0;
|
|
21849
|
+
let kept = 0;
|
|
21850
|
+
let pendingSpace = false;
|
|
21851
|
+
const frames = [{ kind: "code", braces: 0 }];
|
|
21852
|
+
let index = 0;
|
|
21853
|
+
if (text.startsWith("#!")) {
|
|
21854
|
+
const end = text.indexOf(`
|
|
21855
|
+
`);
|
|
21856
|
+
out = end < 0 ? text : text.slice(0, end);
|
|
21857
|
+
kept = out.length;
|
|
21858
|
+
index = end < 0 ? text.length : end;
|
|
21859
|
+
}
|
|
21860
|
+
const emitCode = (chunk) => {
|
|
21861
|
+
if (collapse && pendingSpace && out.length > lineStart) {
|
|
21862
|
+
const before = out[out.length - 1];
|
|
21863
|
+
if (!NO_SPACE_AFTER.includes(before) && !NO_SPACE_BEFORE.includes(chunk[0]))
|
|
21864
|
+
out += " ";
|
|
21897
21865
|
}
|
|
21898
|
-
|
|
21899
|
-
|
|
21900
|
-
|
|
21901
|
-
|
|
21902
|
-
|
|
21903
|
-
|
|
21904
|
-
|
|
21905
|
-
|
|
21906
|
-
|
|
21907
|
-
|
|
21908
|
-
|
|
21909
|
-
|
|
21910
|
-
|
|
21911
|
-
break;
|
|
21912
|
-
}
|
|
21913
|
-
stop = start;
|
|
21866
|
+
pendingSpace = false;
|
|
21867
|
+
out += chunk;
|
|
21868
|
+
};
|
|
21869
|
+
const emitVerbatim = (chunk) => {
|
|
21870
|
+
emitCode(chunk);
|
|
21871
|
+
kept = out.length;
|
|
21872
|
+
};
|
|
21873
|
+
const newline = () => {
|
|
21874
|
+
if (!collapse) {
|
|
21875
|
+
let end = out.length;
|
|
21876
|
+
while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
|
|
21877
|
+
end -= 1;
|
|
21878
|
+
out = out.slice(0, end);
|
|
21914
21879
|
}
|
|
21915
|
-
|
|
21916
|
-
|
|
21917
|
-
|
|
21918
|
-
return null;
|
|
21919
|
-
};
|
|
21920
|
-
var tighten = (fd) => {
|
|
21921
|
-
try {
|
|
21922
|
-
if ((fs12.fstatSync(fd).mode & 63) !== 0)
|
|
21923
|
-
fs12.fchmodSync(fd, 384);
|
|
21924
|
-
} catch {}
|
|
21925
|
-
};
|
|
21926
|
-
function appendLine(file, build, options) {
|
|
21927
|
-
const write = () => {
|
|
21928
|
-
const fd = fs12.openSync(file, "a+", 384);
|
|
21929
|
-
try {
|
|
21930
|
-
if (process.platform !== "win32")
|
|
21931
|
-
tighten(fd);
|
|
21932
|
-
const size = fs12.fstatSync(fd).size;
|
|
21933
|
-
let fence = "";
|
|
21934
|
-
if (size > 0) {
|
|
21935
|
-
const last = Buffer.alloc(1);
|
|
21936
|
-
fs12.readSync(fd, last, 0, 1, size - 1);
|
|
21937
|
-
if (last[0] !== 10)
|
|
21938
|
-
fence = `
|
|
21880
|
+
pendingSpace = false;
|
|
21881
|
+
if (out.length > lineStart) {
|
|
21882
|
+
out += `
|
|
21939
21883
|
`;
|
|
21940
|
-
|
|
21941
|
-
const line = build(lastLine(fd, size));
|
|
21942
|
-
if (!line.ok)
|
|
21943
|
-
return line;
|
|
21944
|
-
const bytes = Buffer.from(fence + line.value);
|
|
21945
|
-
const written = fs12.writeSync(fd, bytes);
|
|
21946
|
-
if (written !== bytes.length)
|
|
21947
|
-
return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
|
|
21948
|
-
return { ok: true, value: undefined };
|
|
21949
|
-
} finally {
|
|
21950
|
-
fs12.closeSync(fd);
|
|
21884
|
+
lineStart = out.length;
|
|
21951
21885
|
}
|
|
21952
21886
|
};
|
|
21953
|
-
|
|
21954
|
-
|
|
21955
|
-
|
|
21956
|
-
|
|
21957
|
-
|
|
21958
|
-
|
|
21959
|
-
|
|
21960
|
-
|
|
21961
|
-
|
|
21962
|
-
|
|
21963
|
-
var sealed = (row, id, at, previous) => {
|
|
21964
|
-
const { prevHash: _prev, rowHash: _row, ...fields } = row;
|
|
21965
|
-
const prevHash = previous === null ? null : sha2562(previous);
|
|
21966
|
-
const body = { v: LEDGER_VERSION, id, at, ...fields, prevHash };
|
|
21967
|
-
const full = { ...body, rowHash: chainHash(prevHash, JSON.stringify(body)) };
|
|
21968
|
-
return { full, line: `${JSON.stringify(full)}
|
|
21969
|
-
` };
|
|
21970
|
-
};
|
|
21971
|
-
function appendRaw(cwd, row, options) {
|
|
21972
|
-
const file = ledgerPath(cwd);
|
|
21973
|
-
if (!file.ok)
|
|
21974
|
-
return file;
|
|
21975
|
-
const id = newId2();
|
|
21976
|
-
const at = (options.now ?? new Date).toISOString();
|
|
21977
|
-
let full = null;
|
|
21978
|
-
const written = appendLine(file.value, (previous) => {
|
|
21979
|
-
const next = sealed(row, id, at, previous);
|
|
21980
|
-
const bytes = Buffer.byteLength(next.line);
|
|
21981
|
-
if (bytes > MAX_LINE_BYTES)
|
|
21982
|
-
return err("invalid_input", `ledger row is ${bytes} bytes; the limit is ${MAX_LINE_BYTES}. Shorten the text and pass details by --ref <path|url>`);
|
|
21983
|
-
full = next.full;
|
|
21984
|
-
return { ok: true, value: next.line };
|
|
21985
|
-
}, options);
|
|
21986
|
-
return written.ok ? { ok: true, value: full } : written;
|
|
21987
|
-
}
|
|
21988
|
-
function observedRowBytes(row) {
|
|
21989
|
-
const sample = sealed({ ...row, observer: "workit_cli" }, newId2(), new Date().toISOString(), "");
|
|
21990
|
-
return Buffer.byteLength(sample.line);
|
|
21991
|
-
}
|
|
21992
|
-
function appendObserved(cwd, row, options = {}) {
|
|
21993
|
-
return appendRaw(cwd, { ...row, observer: "workit_cli" }, options);
|
|
21994
|
-
}
|
|
21995
|
-
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21996
|
-
var str2 = (value) => typeof value === "string" ? value : null;
|
|
21997
|
-
var normalizeActor = (value) => {
|
|
21998
|
-
const actor = isRecord3(value) ? value : {};
|
|
21999
|
-
return {
|
|
22000
|
-
host: str2(actor.host) ?? "unknown",
|
|
22001
|
-
session: str2(actor.session),
|
|
22002
|
-
agentId: str2(actor.agentId)
|
|
21887
|
+
const regexAllowed = () => {
|
|
21888
|
+
let end = out.length;
|
|
21889
|
+
while (end > 0 && /\s/u.test(out[end - 1]))
|
|
21890
|
+
end -= 1;
|
|
21891
|
+
if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
|
|
21892
|
+
return true;
|
|
21893
|
+
let start = end;
|
|
21894
|
+
while (start > 0 && WORD.test(out[start - 1]))
|
|
21895
|
+
start -= 1;
|
|
21896
|
+
return REGEX_KEYWORDS.has(out.slice(start, end));
|
|
22003
21897
|
};
|
|
22004
|
-
|
|
22005
|
-
|
|
22006
|
-
|
|
22007
|
-
|
|
22008
|
-
|
|
22009
|
-
|
|
22010
|
-
|
|
22011
|
-
|
|
22012
|
-
|
|
22013
|
-
|
|
22014
|
-
|
|
22015
|
-
|
|
22016
|
-
|
|
22017
|
-
|
|
22018
|
-
|
|
22019
|
-
|
|
22020
|
-
|
|
22021
|
-
|
|
22022
|
-
|
|
22023
|
-
|
|
22024
|
-
|
|
22025
|
-
|
|
22026
|
-
|
|
22027
|
-
}
|
|
22028
|
-
|
|
22029
|
-
|
|
22030
|
-
|
|
22031
|
-
|
|
22032
|
-
|
|
22033
|
-
|
|
22034
|
-
|
|
22035
|
-
|
|
22036
|
-
|
|
22037
|
-
|
|
22038
|
-
|
|
22039
|
-
|
|
22040
|
-
|
|
22041
|
-
|
|
22042
|
-
|
|
22043
|
-
|
|
22044
|
-
|
|
22045
|
-
|
|
22046
|
-
|
|
22047
|
-
|
|
22048
|
-
const trimmed = raw.trim();
|
|
22049
|
-
if (!trimmed)
|
|
21898
|
+
const scanString = (quote, escapes, multiline) => {
|
|
21899
|
+
const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
|
|
21900
|
+
const fstring = syntax.fstring === true && /f/iu.test(prefix);
|
|
21901
|
+
let end = index + quote.length;
|
|
21902
|
+
while (end < text.length) {
|
|
21903
|
+
const char = text[end];
|
|
21904
|
+
if (escapes && char === "\\") {
|
|
21905
|
+
end += 2;
|
|
21906
|
+
continue;
|
|
21907
|
+
}
|
|
21908
|
+
if (text.startsWith(quote, end)) {
|
|
21909
|
+
if (fstring && unbalanced(text.slice(index + quote.length, end)))
|
|
21910
|
+
return false;
|
|
21911
|
+
emitVerbatim(text.slice(index, end + quote.length));
|
|
21912
|
+
index = end + quote.length;
|
|
21913
|
+
return true;
|
|
21914
|
+
}
|
|
21915
|
+
if (char === `
|
|
21916
|
+
` && !multiline)
|
|
21917
|
+
return false;
|
|
21918
|
+
end += 1;
|
|
21919
|
+
}
|
|
21920
|
+
return false;
|
|
21921
|
+
};
|
|
21922
|
+
while (index < text.length) {
|
|
21923
|
+
const frame = frames[frames.length - 1];
|
|
21924
|
+
const char = text[index];
|
|
21925
|
+
if (frame.kind === "template") {
|
|
21926
|
+
if (char === "\\") {
|
|
21927
|
+
emitVerbatim(text.slice(index, index + 2));
|
|
21928
|
+
index += 2;
|
|
21929
|
+
} else if (char === "`") {
|
|
21930
|
+
emitVerbatim(char);
|
|
21931
|
+
frames.pop();
|
|
21932
|
+
index += 1;
|
|
21933
|
+
} else if (text.startsWith("${", index)) {
|
|
21934
|
+
emitVerbatim("${");
|
|
21935
|
+
frames.push({ kind: "code", braces: 0 });
|
|
21936
|
+
index += 2;
|
|
21937
|
+
} else {
|
|
21938
|
+
out += char;
|
|
21939
|
+
kept = out.length;
|
|
21940
|
+
index += 1;
|
|
21941
|
+
}
|
|
22050
21942
|
continue;
|
|
22051
|
-
|
|
22052
|
-
if (
|
|
22053
|
-
|
|
22054
|
-
|
|
22055
|
-
|
|
22056
|
-
parsed = JSON.parse(trimmed);
|
|
22057
|
-
} catch {
|
|
22058
|
-
skipped += 1;
|
|
21943
|
+
}
|
|
21944
|
+
if (char === `
|
|
21945
|
+
`) {
|
|
21946
|
+
newline();
|
|
21947
|
+
index += 1;
|
|
22059
21948
|
continue;
|
|
22060
21949
|
}
|
|
22061
|
-
if (
|
|
22062
|
-
|
|
21950
|
+
if (SPACE.test(char)) {
|
|
21951
|
+
if (collapse)
|
|
21952
|
+
pendingSpace = true;
|
|
21953
|
+
else
|
|
21954
|
+
out += char;
|
|
21955
|
+
index += 1;
|
|
22063
21956
|
continue;
|
|
22064
21957
|
}
|
|
22065
|
-
|
|
22066
|
-
|
|
22067
|
-
|
|
22068
|
-
|
|
22069
|
-
const
|
|
22070
|
-
|
|
22071
|
-
|
|
22072
|
-
|
|
22073
|
-
|
|
22074
|
-
|
|
22075
|
-
} else if (chainStarted)
|
|
22076
|
-
reason = "unsigned";
|
|
22077
|
-
else
|
|
22078
|
-
rowIntegrity = "legacy";
|
|
22079
|
-
if (options.integrity && !reason && ids.has(parsed.id))
|
|
22080
|
-
reason = "duplicate_id";
|
|
22081
|
-
if (options.integrity)
|
|
22082
|
-
ids.add(parsed.id);
|
|
22083
|
-
if (reason)
|
|
22084
|
-
rowIntegrity = "unverified";
|
|
22085
|
-
const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
|
|
22086
|
-
const row = {
|
|
22087
|
-
...parsed,
|
|
22088
|
-
v: typeof parsed.v === "number" ? parsed.v : LEDGER_VERSION,
|
|
22089
|
-
id: parsed.id,
|
|
22090
|
-
at: parsed.at,
|
|
22091
|
-
type: parsed.type,
|
|
22092
|
-
actor: normalizeActor(parsed.actor),
|
|
22093
|
-
branch: str2(parsed.branch),
|
|
22094
|
-
head: str2(parsed.head),
|
|
22095
|
-
base: str2(parsed.base),
|
|
22096
|
-
baseSha: str2(parsed.baseSha),
|
|
22097
|
-
patchId: str2(parsed.patchId),
|
|
22098
|
-
diffHash: str2(parsed.diffHash),
|
|
22099
|
-
tree: str2(parsed.tree),
|
|
22100
|
-
dirty: typeof parsed.dirty === "boolean" ? parsed.dirty : null,
|
|
22101
|
-
...pr === undefined ? {} : { pr },
|
|
22102
|
-
...str2(parsed.supersedes) ? { supersedes: parsed.supersedes } : {},
|
|
22103
|
-
seq: rows.length + 1,
|
|
22104
|
-
superseded: false,
|
|
22105
|
-
supersedeIgnored: false,
|
|
22106
|
-
...rowIntegrity ? { integrity: rowIntegrity } : {},
|
|
22107
|
-
...reason ? { integrityReason: reason } : {}
|
|
22108
|
-
};
|
|
22109
|
-
rows.push(row);
|
|
22110
|
-
if (!options.integrity)
|
|
21958
|
+
const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
|
|
21959
|
+
if (lineOpener) {
|
|
21960
|
+
const end = text.indexOf(`
|
|
21961
|
+
`, index);
|
|
21962
|
+
const comment = text.slice(index, end < 0 ? text.length : end);
|
|
21963
|
+
if (DIRECTIVE.test(comment.slice(lineOpener.length)))
|
|
21964
|
+
emitVerbatim(comment);
|
|
21965
|
+
else
|
|
21966
|
+
pendingSpace = true;
|
|
21967
|
+
index = end < 0 ? text.length : end;
|
|
22111
21968
|
continue;
|
|
22112
|
-
|
|
22113
|
-
|
|
22114
|
-
|
|
22115
|
-
|
|
22116
|
-
|
|
22117
|
-
|
|
22118
|
-
|
|
22119
|
-
|
|
22120
|
-
|
|
22121
|
-
|
|
22122
|
-
|
|
22123
|
-
|
|
21969
|
+
}
|
|
21970
|
+
const block = syntax.block.find(([open]) => text.startsWith(open, index));
|
|
21971
|
+
if (block) {
|
|
21972
|
+
const end = text.indexOf(block[1], index + block[0].length);
|
|
21973
|
+
if (end < 0)
|
|
21974
|
+
return null;
|
|
21975
|
+
const comment = text.slice(index, end + block[1].length);
|
|
21976
|
+
if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
|
|
21977
|
+
emitVerbatim(comment);
|
|
21978
|
+
else
|
|
21979
|
+
pendingSpace = true;
|
|
21980
|
+
if (comment.includes(`
|
|
21981
|
+
`))
|
|
21982
|
+
newline();
|
|
21983
|
+
index = end + block[1].length;
|
|
21984
|
+
continue;
|
|
21985
|
+
}
|
|
21986
|
+
if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
|
|
21987
|
+
if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
|
|
21988
|
+
return null;
|
|
21989
|
+
continue;
|
|
21990
|
+
}
|
|
21991
|
+
if (syntax.quotes.includes(char)) {
|
|
21992
|
+
if (!scanString(char, true, Boolean(syntax.multiline)))
|
|
21993
|
+
return null;
|
|
21994
|
+
continue;
|
|
21995
|
+
}
|
|
21996
|
+
if (syntax.literal?.includes(char)) {
|
|
21997
|
+
if (!scanString(char, false, false))
|
|
21998
|
+
return null;
|
|
21999
|
+
continue;
|
|
22000
|
+
}
|
|
22001
|
+
if (syntax.rawBacktick && char === "`") {
|
|
22002
|
+
if (!scanString("`", false, true))
|
|
22003
|
+
return null;
|
|
22004
|
+
continue;
|
|
22005
|
+
}
|
|
22006
|
+
if (syntax.js && char === "`") {
|
|
22007
|
+
emitVerbatim(char);
|
|
22008
|
+
frames.push({ kind: "template" });
|
|
22009
|
+
index += 1;
|
|
22010
|
+
continue;
|
|
22011
|
+
}
|
|
22012
|
+
if (syntax.cssUrl && /^url\(\s*[^"')\s]/iu.test(text.slice(index, index + 64)))
|
|
22013
|
+
return null;
|
|
22014
|
+
if (syntax.js && char === "/" && !regexAllowed() && out.trimEnd().endsWith(")")) {
|
|
22015
|
+
const lineEnd = text.indexOf(`
|
|
22016
|
+
`, index);
|
|
22017
|
+
if (text.slice(index + 1, lineEnd < 0 ? text.length : lineEnd).includes("/"))
|
|
22018
|
+
return null;
|
|
22019
|
+
}
|
|
22020
|
+
if (syntax.js && char === "/" && regexAllowed()) {
|
|
22021
|
+
let end = index + 1;
|
|
22022
|
+
let inClass = false;
|
|
22023
|
+
while (end < text.length && (inClass || text[end] !== "/")) {
|
|
22024
|
+
if (text[end] === `
|
|
22025
|
+
`)
|
|
22026
|
+
return null;
|
|
22027
|
+
if (text[end] === "\\")
|
|
22028
|
+
end += 1;
|
|
22029
|
+
else if (text[end] === "[")
|
|
22030
|
+
inClass = true;
|
|
22031
|
+
else if (text[end] === "]")
|
|
22032
|
+
inClass = false;
|
|
22033
|
+
end += 1;
|
|
22034
|
+
}
|
|
22035
|
+
if (end >= text.length)
|
|
22036
|
+
return null;
|
|
22037
|
+
emitVerbatim(text.slice(index, end + 1));
|
|
22038
|
+
index = end + 1;
|
|
22039
|
+
continue;
|
|
22040
|
+
}
|
|
22041
|
+
if (frames.length > 1 && frame.kind === "code") {
|
|
22042
|
+
if (char === "{")
|
|
22043
|
+
frame.braces += 1;
|
|
22044
|
+
else if (char === "}") {
|
|
22045
|
+
if (frame.braces === 0) {
|
|
22046
|
+
emitVerbatim(char);
|
|
22047
|
+
frames.pop();
|
|
22048
|
+
index += 1;
|
|
22049
|
+
continue;
|
|
22050
|
+
}
|
|
22051
|
+
frame.braces -= 1;
|
|
22052
|
+
}
|
|
22053
|
+
}
|
|
22054
|
+
emitCode(char);
|
|
22055
|
+
index += 1;
|
|
22124
22056
|
}
|
|
22125
|
-
|
|
22126
|
-
|
|
22127
|
-
|
|
22128
|
-
|
|
22129
|
-
|
|
22130
|
-
|
|
22131
|
-
|
|
22132
|
-
const
|
|
22133
|
-
|
|
22134
|
-
|
|
22135
|
-
if (
|
|
22136
|
-
|
|
22137
|
-
else if (row.supersedes)
|
|
22138
|
-
row.supersedeIgnored = true;
|
|
22139
|
-
if (!byId.has(row.id))
|
|
22140
|
-
byId.set(row.id, row);
|
|
22057
|
+
if (frames.length !== 1)
|
|
22058
|
+
return null;
|
|
22059
|
+
newline();
|
|
22060
|
+
return out;
|
|
22061
|
+
}
|
|
22062
|
+
function unbalanced(body) {
|
|
22063
|
+
let depth = 0;
|
|
22064
|
+
for (const part of body.replaceAll("{{", "").replaceAll("}}", "")) {
|
|
22065
|
+
if (part === "{")
|
|
22066
|
+
depth += 1;
|
|
22067
|
+
else if (part === "}")
|
|
22068
|
+
depth = Math.max(0, depth - 1);
|
|
22141
22069
|
}
|
|
22142
|
-
|
|
22143
|
-
tamperCache.set(file.value, {
|
|
22144
|
-
size: stat.size,
|
|
22145
|
-
stamp: fileStamp(stat),
|
|
22146
|
-
breaks: integrity.unverified.filter((row) => TAMPER_REASONS.has(row.reason))
|
|
22147
|
-
});
|
|
22148
|
-
return {
|
|
22149
|
-
ok: true,
|
|
22150
|
-
value: { path: file.value, rows, skipped, integrity: options.integrity ? integrity : null }
|
|
22151
|
-
};
|
|
22070
|
+
return depth > 0;
|
|
22152
22071
|
}
|
|
22153
|
-
|
|
22154
|
-
|
|
22155
|
-
|
|
22156
|
-
|
|
22157
|
-
|
|
22158
|
-
if (
|
|
22159
|
-
return
|
|
22072
|
+
function hasOpaqueLineBreak(text) {
|
|
22073
|
+
return /\r(?!\n)|[\u2028\u2029]/u.test(text);
|
|
22074
|
+
}
|
|
22075
|
+
var decoder = new TextDecoder("utf-8", { fatal: true });
|
|
22076
|
+
function textOf(bytes) {
|
|
22077
|
+
if (bytes.includes(0))
|
|
22078
|
+
return null;
|
|
22160
22079
|
try {
|
|
22161
|
-
|
|
22162
|
-
const cached = tamperCache.get(file.value);
|
|
22163
|
-
if (cached && cached.size === stat.size && cached.stamp === fileStamp(stat))
|
|
22164
|
-
return cached.breaks;
|
|
22080
|
+
return decoder.decode(bytes);
|
|
22165
22081
|
} catch {
|
|
22166
|
-
return
|
|
22082
|
+
return null;
|
|
22167
22083
|
}
|
|
22168
|
-
const read = readLedger(cwd, { integrity: true });
|
|
22169
|
-
return read.ok ? (read.value.integrity?.unverified ?? []).filter((row) => TAMPER_REASONS.has(row.reason)) : [];
|
|
22170
|
-
}
|
|
22171
|
-
function tamperedUnblock(cwd) {
|
|
22172
|
-
const file = ledgerPath(cwd);
|
|
22173
|
-
const where = file.ok ? file.value : "the ledger file";
|
|
22174
|
-
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`;
|
|
22175
22084
|
}
|
|
22176
|
-
|
|
22177
|
-
|
|
22178
|
-
|
|
22179
|
-
|
|
22180
|
-
|
|
22181
|
-
|
|
22182
|
-
|
|
22085
|
+
function cosmeticChange(file, before, after) {
|
|
22086
|
+
const left = textOf(before);
|
|
22087
|
+
const right = textOf(after);
|
|
22088
|
+
if (left === null || right === null)
|
|
22089
|
+
return false;
|
|
22090
|
+
if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
|
|
22091
|
+
return false;
|
|
22092
|
+
const a = substanceOf(file, left);
|
|
22093
|
+
return a !== null && a === substanceOf(file, right);
|
|
22183
22094
|
}
|
|
22184
|
-
|
|
22185
|
-
|
|
22186
|
-
|
|
22187
|
-
|
|
22188
|
-
|
|
22189
|
-
|
|
22190
|
-
|
|
22191
|
-
|
|
22192
|
-
|
|
22193
|
-
|
|
22095
|
+
|
|
22096
|
+
// packages/workit-core/src/core/policy/lane.ts
|
|
22097
|
+
var LANE_THRESHOLDS = {
|
|
22098
|
+
selfMaxLines: 100,
|
|
22099
|
+
selfMaxFiles: 5,
|
|
22100
|
+
fullMinLines: 500
|
|
22101
|
+
};
|
|
22102
|
+
var RISK_TOKENS = new Set([
|
|
22103
|
+
"auth",
|
|
22104
|
+
"authn",
|
|
22105
|
+
"authz",
|
|
22106
|
+
"oauth",
|
|
22107
|
+
"security",
|
|
22108
|
+
"permission",
|
|
22109
|
+
"permissions",
|
|
22110
|
+
"secret",
|
|
22111
|
+
"secrets",
|
|
22112
|
+
"crypto",
|
|
22113
|
+
"gate",
|
|
22114
|
+
"gates",
|
|
22115
|
+
"policy",
|
|
22116
|
+
"policies",
|
|
22117
|
+
"hook",
|
|
22118
|
+
"hooks",
|
|
22119
|
+
"husky",
|
|
22120
|
+
"lefthook",
|
|
22121
|
+
"release",
|
|
22122
|
+
"releases",
|
|
22123
|
+
"migration",
|
|
22124
|
+
"migrations",
|
|
22125
|
+
"migrate",
|
|
22126
|
+
"workflows"
|
|
22127
|
+
]);
|
|
22128
|
+
var RISK_FILES = new Set([
|
|
22129
|
+
".gitlab-ci.yml",
|
|
22130
|
+
"Jenkinsfile",
|
|
22131
|
+
"azure-pipelines.yml",
|
|
22132
|
+
".pre-commit-config.yaml",
|
|
22133
|
+
".workit/risk-paths"
|
|
22134
|
+
]);
|
|
22135
|
+
var RISK_NAMES = new Set([".gitattributes"]);
|
|
22136
|
+
var RISK_PATHS_FILE = ".workit/risk-paths";
|
|
22137
|
+
var LOCKFILES = new Set([
|
|
22138
|
+
"bun.lock",
|
|
22139
|
+
"bun.lockb",
|
|
22140
|
+
"package-lock.json",
|
|
22141
|
+
"npm-shrinkwrap.json",
|
|
22142
|
+
"yarn.lock",
|
|
22143
|
+
"pnpm-lock.yaml",
|
|
22144
|
+
"Cargo.lock",
|
|
22145
|
+
"go.sum",
|
|
22146
|
+
"poetry.lock",
|
|
22147
|
+
"uv.lock",
|
|
22148
|
+
"Pipfile.lock",
|
|
22149
|
+
"Gemfile.lock",
|
|
22150
|
+
"composer.lock"
|
|
22151
|
+
]);
|
|
22152
|
+
var DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc)$/iu;
|
|
22153
|
+
var DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
|
|
22154
|
+
var INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
|
|
22155
|
+
var TEST_PATH = /(?:^|\/)(?:test|tests|__tests__)\/|\.(?:test|spec)\.[^/]+$|_test\.[^/]+$/iu;
|
|
22156
|
+
var basename = (file) => file.slice(file.lastIndexOf("/") + 1);
|
|
22157
|
+
var isDoc = (file) => !INSTRUCTIONS.test(file) && (DOC_EXT.test(file) || DOC_NAMES.test(basename(file)));
|
|
22158
|
+
function kindOf(file) {
|
|
22159
|
+
if (file.binary)
|
|
22160
|
+
return "behavior";
|
|
22161
|
+
if (file.generated)
|
|
22162
|
+
return "generated";
|
|
22163
|
+
if (LOCKFILES.has(basename(file.path)))
|
|
22164
|
+
return "lockfile";
|
|
22165
|
+
if (isDoc(file.path))
|
|
22166
|
+
return "doc";
|
|
22167
|
+
if (file.cosmetic)
|
|
22168
|
+
return "cosmetic";
|
|
22169
|
+
return TEST_PATH.test(file.path) ? "test" : "behavior";
|
|
22194
22170
|
}
|
|
22195
|
-
var
|
|
22196
|
-
function
|
|
22197
|
-
|
|
22198
|
-
|
|
22199
|
-
|
|
22200
|
-
|
|
22201
|
-
|
|
22202
|
-
"
|
|
22203
|
-
|
|
22204
|
-
|
|
22205
|
-
|
|
22206
|
-
|
|
22207
|
-
|
|
22208
|
-
|
|
22209
|
-
|
|
22210
|
-
|
|
22211
|
-
const seconds = Number(/@\{(\d+)\}$/u.exec(selector)?.[1]);
|
|
22212
|
-
if (!Number.isFinite(seconds))
|
|
22213
|
-
continue;
|
|
22214
|
-
since = seconds * 1000;
|
|
22215
|
-
const match = RENAMED.exec(subject);
|
|
22216
|
-
if (!match || match[1] === branch)
|
|
22217
|
-
continue;
|
|
22218
|
-
const until = (seconds + 1) * 1000;
|
|
22219
|
-
renamedAt.set(match[1], Math.max(renamedAt.get(match[1]) ?? 0, until));
|
|
22171
|
+
var globCache = new Map;
|
|
22172
|
+
function globMatch(pattern, file) {
|
|
22173
|
+
let regex = globCache.get(pattern);
|
|
22174
|
+
if (!regex) {
|
|
22175
|
+
let source = "";
|
|
22176
|
+
for (let index = 0;index < pattern.length; index += 1) {
|
|
22177
|
+
const char = pattern[index];
|
|
22178
|
+
if (char === "*" && pattern[index + 1] === "*") {
|
|
22179
|
+
source += pattern[index + 2] === "/" ? "(?:[^/]*/)*" : ".*";
|
|
22180
|
+
index += pattern[index + 2] === "/" ? 2 : 1;
|
|
22181
|
+
} else if (char === "*")
|
|
22182
|
+
source += "[^/]*";
|
|
22183
|
+
else if (char === "?")
|
|
22184
|
+
source += "[^/]";
|
|
22185
|
+
else
|
|
22186
|
+
source += char.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
|
|
22220
22187
|
}
|
|
22188
|
+
regex = new RegExp(`^${source}$`, "u");
|
|
22189
|
+
globCache.set(pattern, regex);
|
|
22221
22190
|
}
|
|
22222
|
-
return (
|
|
22223
|
-
if (row.branch === branch)
|
|
22224
|
-
return true;
|
|
22225
|
-
const until = row.branch === null ? undefined : renamedAt.get(row.branch);
|
|
22226
|
-
if (until === undefined)
|
|
22227
|
-
return false;
|
|
22228
|
-
const at = Date.parse(row.at);
|
|
22229
|
-
return at >= since && at <= until;
|
|
22230
|
-
};
|
|
22231
|
-
}
|
|
22232
|
-
var renameAwareBranch = (scopeOf, branch) => {
|
|
22233
|
-
return (target) => {
|
|
22234
|
-
const own = typeof branch === "string" ? branch : null;
|
|
22235
|
-
const theirs = typeof target.branch === "string" ? target.branch : null;
|
|
22236
|
-
if (own === theirs)
|
|
22237
|
-
return true;
|
|
22238
|
-
if (own === null || theirs === null || typeof target.at !== "string")
|
|
22239
|
-
return false;
|
|
22240
|
-
return scopeOf(own)({ branch: theirs, at: target.at });
|
|
22241
|
-
};
|
|
22242
|
-
};
|
|
22243
|
-
function authorSessions(cwd, rows, branch, range = { base: null, head: null }, scope = branchScope(cwd, branch)) {
|
|
22244
|
-
const sessions = new Set;
|
|
22245
|
-
for (const row of rows) {
|
|
22246
|
-
if (!scope(row) || row.type !== "commit.recorded" && row.type !== "task.opened")
|
|
22247
|
-
continue;
|
|
22248
|
-
const session = str2(row.session) ?? row.actor.session;
|
|
22249
|
-
if (session)
|
|
22250
|
-
sessions.add(session);
|
|
22251
|
-
}
|
|
22252
|
-
for (const session of trailerSessions(cwd, range.base, range.head))
|
|
22253
|
-
sessions.add(session);
|
|
22254
|
-
return sessions;
|
|
22191
|
+
return regex.test(file) || file.startsWith(`${pattern.replace(/\/$/u, "")}/`);
|
|
22255
22192
|
}
|
|
22256
|
-
|
|
22257
|
-
|
|
22258
|
-
if (authors.has(session))
|
|
22193
|
+
function isRiskPath(file, extra = []) {
|
|
22194
|
+
if (extra.some((pattern) => globMatch(pattern, file)))
|
|
22259
22195
|
return true;
|
|
22260
|
-
|
|
22261
|
-
|
|
22262
|
-
|
|
22263
|
-
|
|
22264
|
-
|
|
22265
|
-
var TRUST_REASONS = new Set([
|
|
22266
|
-
"unsigned",
|
|
22267
|
-
"hash_mismatch",
|
|
22268
|
-
"broken_link",
|
|
22269
|
-
"duplicate_id",
|
|
22270
|
-
"unissued_session",
|
|
22271
|
-
"unbound_session"
|
|
22272
|
-
]);
|
|
22273
|
-
var SHELL_VOUCHED_HOSTS = new Set([
|
|
22274
|
-
"cli",
|
|
22275
|
-
"opencode",
|
|
22276
|
-
"pi",
|
|
22277
|
-
"codex_cli",
|
|
22278
|
-
"codex_desktop"
|
|
22279
|
-
]);
|
|
22280
|
-
function sessionTrust(cwd, row) {
|
|
22281
|
-
const session = row.actor.session;
|
|
22282
|
-
if (!session || row.self === true)
|
|
22283
|
-
return null;
|
|
22284
|
-
const source = typeof row.sessionSource === "string" ? row.sessionSource : "env";
|
|
22285
|
-
const host = row.actor.host;
|
|
22286
|
-
const issuedOnly = source === "flag" ? host !== "cli" : source === "env" && session.includes(":") && !SHELL_VOUCHED_HOSTS.has(host);
|
|
22287
|
-
if (!issuedOnly)
|
|
22288
|
-
return null;
|
|
22289
|
-
const binding = issuedBinding(cwd, session);
|
|
22290
|
-
if (binding === null)
|
|
22291
|
-
return "unissued_session";
|
|
22292
|
-
return binding === "token" && row.sessionBound !== true ? "unbound_session" : null;
|
|
22293
|
-
}
|
|
22294
|
-
function trustReasons(cwd, row) {
|
|
22295
|
-
if (row.integrity === "unverified")
|
|
22296
|
-
return [row.integrityReason ?? "unsigned"];
|
|
22297
|
-
if (row.integrity !== "verified")
|
|
22298
|
-
return ["unsigned"];
|
|
22299
|
-
const session = sessionTrust(cwd, row);
|
|
22300
|
-
return session ? [session] : [];
|
|
22196
|
+
if (RISK_FILES.has(file) || RISK_NAMES.has(basename(file)))
|
|
22197
|
+
return true;
|
|
22198
|
+
if (TEST_PATH.test(file) || isDoc(file))
|
|
22199
|
+
return false;
|
|
22200
|
+
return file.split("/").map((segment, index, all) => index === all.length - 1 ? segment.replace(/\.[^.]+$/u, "") : segment).some((segment) => segment.toLowerCase().split(/[-_.]/u).some((token) => RISK_TOKENS.has(token)));
|
|
22301
22201
|
}
|
|
22302
|
-
|
|
22303
|
-
|
|
22304
|
-
|
|
22305
|
-
const
|
|
22306
|
-
const
|
|
22307
|
-
|
|
22308
|
-
|
|
22309
|
-
|
|
22310
|
-
|
|
22311
|
-
|
|
22202
|
+
var parseRiskPaths = (text) => text.split(`
|
|
22203
|
+
`).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.replace(/^\//u, ""));
|
|
22204
|
+
function computeLane(input) {
|
|
22205
|
+
const riskFiles = input.files.filter((file) => isRiskPath(file.path, input.riskPaths)).map((file) => file.path);
|
|
22206
|
+
const risky = new Set(riskFiles);
|
|
22207
|
+
const kinds = input.files.map((file) => ({
|
|
22208
|
+
file,
|
|
22209
|
+
kind: risky.has(file.path) ? "behavior" : kindOf(file)
|
|
22210
|
+
}));
|
|
22211
|
+
const behavior = kinds.filter((entry) => entry.kind === "behavior" || entry.kind === "test" || entry.kind === "lockfile");
|
|
22212
|
+
const lines = behavior.filter((entry) => entry.kind !== "lockfile").reduce((sum, entry) => sum + entry.file.added + entry.file.deleted, 0);
|
|
22213
|
+
const opaque = behavior.filter((entry) => entry.kind === "lockfile" || entry.file.binary);
|
|
22214
|
+
const result = (lane, reasons) => ({
|
|
22215
|
+
lane,
|
|
22216
|
+
reasons,
|
|
22217
|
+
lines,
|
|
22218
|
+
files: behavior.length,
|
|
22219
|
+
riskFiles
|
|
22312
22220
|
});
|
|
22221
|
+
const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
|
|
22222
|
+
if (input.riskTier === "high")
|
|
22223
|
+
return result("full", ["the task judgment records high risk"]);
|
|
22224
|
+
if (riskFiles.length)
|
|
22225
|
+
return result("full", [
|
|
22226
|
+
`touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
|
|
22227
|
+
]);
|
|
22228
|
+
if (!behavior.length)
|
|
22229
|
+
return result("none", [
|
|
22230
|
+
input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
|
|
22231
|
+
]);
|
|
22232
|
+
if (lines >= fullMinLines)
|
|
22233
|
+
return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
|
|
22234
|
+
if (lines > selfMaxLines)
|
|
22235
|
+
return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
|
|
22236
|
+
if (behavior.length > selfMaxFiles)
|
|
22237
|
+
return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
|
|
22238
|
+
if (opaque.length)
|
|
22239
|
+
return result("focused", [
|
|
22240
|
+
`a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
|
|
22241
|
+
]);
|
|
22242
|
+
return result("self", [
|
|
22243
|
+
`small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
|
|
22244
|
+
]);
|
|
22313
22245
|
}
|
|
22314
|
-
|
|
22315
|
-
|
|
22316
|
-
|
|
22317
|
-
|
|
22318
|
-
|
|
22319
|
-
|
|
22320
|
-
|
|
22321
|
-
|
|
22322
|
-
|
|
22323
|
-
|
|
22324
|
-
|
|
22325
|
-
|
|
22326
|
-
|
|
22327
|
-
|
|
22328
|
-
if (!effective) {
|
|
22329
|
-
effective = row;
|
|
22330
|
-
continue;
|
|
22331
|
-
}
|
|
22332
|
-
if (untrusted(reasonsOf(row)) && !untrusted(reasonsOf(effective)))
|
|
22333
|
-
continue;
|
|
22334
|
-
const rowIndependent = reasonsOf(row).length === 0;
|
|
22335
|
-
const effIndependent = reasonsOf(effective).length === 0;
|
|
22336
|
-
if (effIndependent && !rowIndependent && isCurrent(effective))
|
|
22337
|
-
continue;
|
|
22338
|
-
if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session && sameCode(effective, row))
|
|
22339
|
-
continue;
|
|
22340
|
-
effective = row;
|
|
22246
|
+
function untrackedStat(cwd, file) {
|
|
22247
|
+
try {
|
|
22248
|
+
const bytes = readFileSync4(path14.join(cwd, file));
|
|
22249
|
+
if (bytes.includes(0))
|
|
22250
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
22251
|
+
const text = bytes.toString("utf8");
|
|
22252
|
+
if (hasOpaqueLineBreak(text))
|
|
22253
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
22254
|
+
const lines = text.split(`
|
|
22255
|
+
`).length - (text.endsWith(`
|
|
22256
|
+
`) ? 1 : 0);
|
|
22257
|
+
return { path: file, added: lines, deleted: 0 };
|
|
22258
|
+
} catch {
|
|
22259
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
22341
22260
|
}
|
|
22342
|
-
return effective;
|
|
22343
22261
|
}
|
|
22344
|
-
|
|
22345
|
-
|
|
22346
|
-
|
|
22347
|
-
const
|
|
22348
|
-
|
|
22349
|
-
|
|
22350
|
-
|
|
22351
|
-
const
|
|
22352
|
-
|
|
22353
|
-
|
|
22354
|
-
|
|
22355
|
-
|
|
22356
|
-
|
|
22357
|
-
|
|
22358
|
-
|
|
22359
|
-
|
|
22360
|
-
|
|
22262
|
+
var REGULAR = new Set(["100644", "100755"]);
|
|
22263
|
+
var ABSENT = /^0+$/u;
|
|
22264
|
+
function cosmeticFiles(cwd, fork, head) {
|
|
22265
|
+
const raw = diffRaw(cwd, fork, head);
|
|
22266
|
+
if (!raw)
|
|
22267
|
+
return null;
|
|
22268
|
+
const candidates = raw.filter((entry) => REGULAR.has(entry.oldMode) && entry.oldMode === entry.newMode && !ABSENT.test(entry.oldBlob) && (head === null || !ABSENT.test(entry.newBlob)));
|
|
22269
|
+
const blobs = readBlobs(cwd, candidates.flatMap((entry) => [entry.oldBlob, entry.newBlob]));
|
|
22270
|
+
if (!blobs)
|
|
22271
|
+
return null;
|
|
22272
|
+
const out = new Set;
|
|
22273
|
+
const opaque = new Set;
|
|
22274
|
+
for (const entry of candidates) {
|
|
22275
|
+
const before = blobs.get(entry.oldBlob);
|
|
22276
|
+
let after = blobs.get(entry.newBlob);
|
|
22277
|
+
if (!after && head === null && ABSENT.test(entry.newBlob))
|
|
22278
|
+
try {
|
|
22279
|
+
after = readFileSync4(path14.join(cwd, entry.path));
|
|
22280
|
+
} catch {
|
|
22281
|
+
after = undefined;
|
|
22361
22282
|
}
|
|
22283
|
+
if (before && after && cosmeticChange(entry.path, before, after))
|
|
22284
|
+
out.add(entry.path);
|
|
22285
|
+
else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
|
|
22286
|
+
opaque.add(entry.path);
|
|
22362
22287
|
}
|
|
22363
|
-
return
|
|
22288
|
+
return { cosmetic: out, opaque };
|
|
22364
22289
|
}
|
|
22365
|
-
function
|
|
22366
|
-
const
|
|
22367
|
-
const
|
|
22368
|
-
|
|
22369
|
-
|
|
22370
|
-
const
|
|
22371
|
-
const
|
|
22372
|
-
|
|
22373
|
-
|
|
22374
|
-
|
|
22375
|
-
|
|
22376
|
-
|
|
22377
|
-
|
|
22378
|
-
|
|
22379
|
-
|
|
22380
|
-
|
|
22381
|
-
|
|
22382
|
-
|
|
22383
|
-
|
|
22384
|
-
|
|
22385
|
-
|
|
22386
|
-
|
|
22387
|
-
|
|
22388
|
-
|
|
22389
|
-
|
|
22390
|
-
|
|
22391
|
-
|
|
22392
|
-
|
|
22393
|
-
|
|
22394
|
-
|
|
22395
|
-
|
|
22396
|
-
|
|
22397
|
-
|
|
22398
|
-
|
|
22399
|
-
|
|
22400
|
-
|
|
22401
|
-
|
|
22402
|
-
|
|
22403
|
-
|
|
22404
|
-
|
|
22405
|
-
const
|
|
22406
|
-
const
|
|
22407
|
-
|
|
22408
|
-
|
|
22409
|
-
|
|
22410
|
-
|
|
22411
|
-
|
|
22412
|
-
|
|
22413
|
-
|
|
22414
|
-
|
|
22415
|
-
|
|
22416
|
-
|
|
22417
|
-
};
|
|
22418
|
-
const
|
|
22419
|
-
|
|
22420
|
-
|
|
22421
|
-
|
|
22422
|
-
const
|
|
22423
|
-
|
|
22424
|
-
|
|
22425
|
-
|
|
22426
|
-
|
|
22427
|
-
|
|
22428
|
-
|
|
22429
|
-
|
|
22430
|
-
|
|
22431
|
-
|
|
22432
|
-
|
|
22433
|
-
|
|
22434
|
-
|
|
22435
|
-
|
|
22436
|
-
|
|
22437
|
-
|
|
22438
|
-
|
|
22439
|
-
|
|
22440
|
-
|
|
22441
|
-
|
|
22442
|
-
|
|
22443
|
-
|
|
22444
|
-
|
|
22445
|
-
|
|
22446
|
-
|
|
22447
|
-
accepted: reasons.length === 0,
|
|
22448
|
-
reasons,
|
|
22449
|
-
verdict: row
|
|
22450
|
-
});
|
|
22451
|
-
if (reasons.length === 0) {
|
|
22452
|
-
for (const later of list)
|
|
22453
|
-
if (later.seq > row.seq && reasonsOf(later).length > 0 && !untrusted(reasonsOf(later)) && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
|
|
22454
|
-
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`);
|
|
22455
|
-
}
|
|
22456
|
-
}
|
|
22457
|
-
for (const row of rows)
|
|
22458
|
-
if (row.integrity === "unverified" && scope(row))
|
|
22459
|
-
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)`);
|
|
22460
|
-
const tampered = ledgerTampering(cwd);
|
|
22461
|
-
if (tampered.length) {
|
|
22462
|
-
warnings.push(tamperedWarning(cwd, tampered));
|
|
22463
|
-
for (const entry of verdicts) {
|
|
22464
|
-
entry.accepted = false;
|
|
22465
|
-
entry.reasons = [...entry.reasons, "ledger_tampered"];
|
|
22466
|
-
}
|
|
22467
|
-
}
|
|
22468
|
-
verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
|
|
22469
|
-
const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
|
|
22470
|
-
const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
|
|
22471
|
-
const newestAccepted = verdicts.findLast((entry) => entry.accepted && STRONG_RESULTS.has(String(entry.verdict.result))) ?? null;
|
|
22472
|
-
const accepted = tampered.length ? { accepted: false, verdict: null, reasons: ["ledger_tampered"] } : newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
22473
|
-
accepted: false,
|
|
22474
|
-
verdict: failing?.verdict ?? null,
|
|
22475
|
-
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
22476
|
-
};
|
|
22477
|
-
const latestSelf = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && (STRONG_RESULTS.has(String(entry.verdict.result)) || FAILING.has(String(entry.verdict.result))));
|
|
22478
|
-
const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
|
|
22479
|
-
return {
|
|
22480
|
-
branch,
|
|
22481
|
-
head,
|
|
22482
|
-
review: accepted.accepted ? "verified" : selfEntry ? "self-reviewed" : "unreviewed",
|
|
22483
|
-
selfVerdict: accepted.accepted ? null : selfEntry?.verdict ?? null,
|
|
22484
|
-
base: newestCurrent?.verdict.base ?? fallbackBase,
|
|
22485
|
-
patchId: keyFor(newestCurrent?.verdict.base ?? fallbackBase).patchId,
|
|
22486
|
-
current: { basis: newestCurrent?.basis ?? "none", verdict: newestCurrent?.verdict ?? null },
|
|
22487
|
-
accepted,
|
|
22488
|
-
verdicts,
|
|
22489
|
-
authors: [...authors].toSorted(),
|
|
22490
|
-
warnings
|
|
22290
|
+
function generatedAt(cwd, fork, paths) {
|
|
22291
|
+
const files = treeFilesNamed(cwd, fork, ".gitattributes");
|
|
22292
|
+
const blobs = files?.size ? readBlobs(cwd, [...files.values()]) : null;
|
|
22293
|
+
if (!files || !blobs)
|
|
22294
|
+
return new Set;
|
|
22295
|
+
const texts = new Map;
|
|
22296
|
+
for (const [file, id] of files) {
|
|
22297
|
+
const bytes = blobs.get(id);
|
|
22298
|
+
if (bytes)
|
|
22299
|
+
texts.set(file, bytes.toString("utf8"));
|
|
22300
|
+
}
|
|
22301
|
+
return generatedByAttributes(texts, paths);
|
|
22302
|
+
}
|
|
22303
|
+
var forkOf = (cwd, base, head) => mergeBase(cwd, base, head ?? "HEAD");
|
|
22304
|
+
function laneFiles(cwd, fork, head, attributesAt = fork) {
|
|
22305
|
+
const stats = diffNumstat(cwd, fork, head);
|
|
22306
|
+
const cosmetic = stats ? cosmeticFiles(cwd, fork, head) : null;
|
|
22307
|
+
if (!stats || !cosmetic)
|
|
22308
|
+
return null;
|
|
22309
|
+
const files = [
|
|
22310
|
+
...stats.map((entry) => ({
|
|
22311
|
+
...entry,
|
|
22312
|
+
...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
|
|
22313
|
+
cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
|
|
22314
|
+
})),
|
|
22315
|
+
...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
|
|
22316
|
+
];
|
|
22317
|
+
const generated = generatedAt(cwd, attributesAt, files.map((entry) => entry.path));
|
|
22318
|
+
return files.map((entry) => ({
|
|
22319
|
+
...entry,
|
|
22320
|
+
generated: generated.has(entry.path)
|
|
22321
|
+
}));
|
|
22322
|
+
}
|
|
22323
|
+
function driftFiles(cwd, range) {
|
|
22324
|
+
if (range.fromFork === range.toFork)
|
|
22325
|
+
return laneFiles(cwd, range.from, range.to, range.toFork);
|
|
22326
|
+
const drifted = ownChangeDrift(cwd, range);
|
|
22327
|
+
const stats = drifted ? diffNumstat(cwd, range.from, range.to) : null;
|
|
22328
|
+
if (!drifted || !stats)
|
|
22329
|
+
return null;
|
|
22330
|
+
const byPath = new Map(stats.map((entry) => [entry.path, entry]));
|
|
22331
|
+
const generated = generatedAt(cwd, range.toFork, drifted);
|
|
22332
|
+
return drifted.map((file) => ({
|
|
22333
|
+
path: file,
|
|
22334
|
+
added: byPath.get(file)?.added ?? 0,
|
|
22335
|
+
deleted: byPath.get(file)?.deleted ?? 0,
|
|
22336
|
+
...byPath.get(file)?.binary ? { binary: true } : {},
|
|
22337
|
+
cosmetic: false,
|
|
22338
|
+
generated: generated.has(file)
|
|
22339
|
+
}));
|
|
22340
|
+
}
|
|
22341
|
+
function branchLane(cwd, options) {
|
|
22342
|
+
const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
|
|
22343
|
+
const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
|
|
22344
|
+
const head = options.workingTree ? null : options.head;
|
|
22345
|
+
const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
|
|
22346
|
+
const files = fork ? laneFiles(cwd, fork, head) : null;
|
|
22347
|
+
const riskTier = options.judgment?.riskTier ?? null;
|
|
22348
|
+
if (!fork || !files) {
|
|
22349
|
+
const high = riskTier === "high";
|
|
22350
|
+
return {
|
|
22351
|
+
lane: high ? "full" : "focused",
|
|
22352
|
+
reasons: [
|
|
22353
|
+
`the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
|
|
22354
|
+
...high ? ["the task judgment records high risk"] : []
|
|
22355
|
+
],
|
|
22356
|
+
lines: 0,
|
|
22357
|
+
files: 0,
|
|
22358
|
+
riskFiles: [],
|
|
22359
|
+
base,
|
|
22360
|
+
head: options.head
|
|
22361
|
+
};
|
|
22362
|
+
}
|
|
22363
|
+
const repoRisk = parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? "");
|
|
22364
|
+
return {
|
|
22365
|
+
...computeLane({
|
|
22366
|
+
files,
|
|
22367
|
+
riskTier,
|
|
22368
|
+
riskPaths: [...repoRisk, ...options.riskPaths ?? []]
|
|
22369
|
+
}),
|
|
22370
|
+
base,
|
|
22371
|
+
head: options.head
|
|
22491
22372
|
};
|
|
22492
22373
|
}
|
|
22493
|
-
var
|
|
22494
|
-
"check",
|
|
22495
|
-
"merge.unverified",
|
|
22496
|
-
"policy.judged",
|
|
22497
|
-
...PR_ROW_TYPES
|
|
22498
|
-
]);
|
|
22499
|
-
|
|
22500
|
-
// packages/workit-core/src/core/policy/lane.ts
|
|
22501
|
-
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
22502
|
-
import path16 from "node:path";
|
|
22374
|
+
var laneNeed = (lane) => lane === "none" ? "none" : lane === "self" ? "self" : "non-author";
|
|
22503
22375
|
|
|
22504
|
-
// packages/workit-core/src/core/
|
|
22505
|
-
|
|
22506
|
-
|
|
22507
|
-
|
|
22508
|
-
|
|
22509
|
-
|
|
22510
|
-
|
|
22511
|
-
|
|
22376
|
+
// packages/workit-core/src/core/policy/carry.ts
|
|
22377
|
+
var TEST_ONLY_KINDS = new Set(["doc", "cosmetic", "generated", "test"]);
|
|
22378
|
+
function verdictDrift(cwd, input) {
|
|
22379
|
+
const since = input.from;
|
|
22380
|
+
const stale = (reason, files = null, focused = false) => ({
|
|
22381
|
+
carries: false,
|
|
22382
|
+
since,
|
|
22383
|
+
files,
|
|
22384
|
+
focused,
|
|
22385
|
+
reason
|
|
22386
|
+
});
|
|
22387
|
+
const fromFork = mergeBase(cwd, input.fromBase, input.from);
|
|
22388
|
+
const toFork = mergeBase(cwd, input.toBase, input.to);
|
|
22389
|
+
const files = fromFork && toFork ? driftFiles(cwd, { from: input.from, to: input.to, fromFork, toFork }) : null;
|
|
22390
|
+
if (!fromFork || !toFork || !files)
|
|
22391
|
+
return stale(`the change since ${since.slice(0, 12)} cannot be read`);
|
|
22392
|
+
const riskPaths = [fromFork, toFork].flatMap((fork) => parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? ""));
|
|
22393
|
+
const lane = computeLane({ files, riskPaths });
|
|
22394
|
+
const paths = files.map((file) => file.path);
|
|
22395
|
+
if (lane.lane === "none")
|
|
22396
|
+
return { carries: true, rule: "lane-none", since, files: paths };
|
|
22397
|
+
if (input.kind === "unit" && lane.lane === "self" && files.every((file) => TEST_ONLY_KINDS.has(kindOf(file))))
|
|
22398
|
+
return { carries: true, rule: "tests-only", since, files: paths };
|
|
22399
|
+
return stale(lane.reasons[0] ?? `lane ${lane.lane}`, paths, lane.lane === "self");
|
|
22512
22400
|
}
|
|
22513
22401
|
|
|
22514
|
-
// packages/workit-core/src/
|
|
22515
|
-
|
|
22516
|
-
|
|
22517
|
-
|
|
22518
|
-
|
|
22519
|
-
|
|
22520
|
-
|
|
22521
|
-
|
|
22522
|
-
|
|
22523
|
-
|
|
22524
|
-
|
|
22525
|
-
|
|
22526
|
-
|
|
22527
|
-
|
|
22528
|
-
|
|
22529
|
-
|
|
22530
|
-
|
|
22531
|
-
|
|
22532
|
-
|
|
22402
|
+
// packages/workit-core/src/ledger.ts
|
|
22403
|
+
var LEDGER_VERSION = 1;
|
|
22404
|
+
var MAX_LINE_BYTES = 4096;
|
|
22405
|
+
var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
|
|
22406
|
+
var FAILING = new Set(["failed", "blocked"]);
|
|
22407
|
+
var STRONG_RESULTS = new Set(["verified", "tests-verified"]);
|
|
22408
|
+
var PR_ROW_TYPES = new Set([
|
|
22409
|
+
"pr.created",
|
|
22410
|
+
"pr.status",
|
|
22411
|
+
"pr.merged",
|
|
22412
|
+
"pr.linked"
|
|
22413
|
+
]);
|
|
22414
|
+
var err = (code, error, unblock) => ({
|
|
22415
|
+
ok: false,
|
|
22416
|
+
code,
|
|
22417
|
+
error,
|
|
22418
|
+
...unblock ? { unblock } : {}
|
|
22419
|
+
});
|
|
22420
|
+
var gitRun2 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
|
|
22421
|
+
const run = spawnSync9("git", args, {
|
|
22533
22422
|
cwd,
|
|
22534
|
-
encoding: "utf8"
|
|
22423
|
+
encoding: "utf8",
|
|
22424
|
+
timeout: timeoutMs,
|
|
22425
|
+
killSignal: "SIGKILL",
|
|
22426
|
+
windowsHide: true,
|
|
22427
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
22428
|
+
env: {
|
|
22429
|
+
...process.env,
|
|
22430
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
22431
|
+
GIT_OPTIONAL_LOCKS: "0",
|
|
22432
|
+
LC_ALL: "C",
|
|
22433
|
+
LANGUAGE: "C"
|
|
22434
|
+
}
|
|
22535
22435
|
});
|
|
22536
|
-
|
|
22537
|
-
|
|
22538
|
-
|
|
22539
|
-
|
|
22436
|
+
return {
|
|
22437
|
+
ok: run.status === 0,
|
|
22438
|
+
stdout: run.stdout ?? "",
|
|
22439
|
+
stderr: run.error ? String(run.error.message) : run.stderr ?? ""
|
|
22440
|
+
};
|
|
22540
22441
|
};
|
|
22541
|
-
|
|
22542
|
-
|
|
22543
|
-
|
|
22544
|
-
|
|
22545
|
-
|
|
22546
|
-
var
|
|
22547
|
-
|
|
22548
|
-
|
|
22549
|
-
var COMMIT_TIMEOUT_MS = 10 * 60000;
|
|
22550
|
-
|
|
22551
|
-
// packages/workit-core/src/core/policy/attributes.ts
|
|
22552
|
-
function patternRegex(dir, pattern) {
|
|
22553
|
-
if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
|
|
22442
|
+
var git4 = (cwd, args) => {
|
|
22443
|
+
const run = gitRun2(cwd, args);
|
|
22444
|
+
const value = run.ok ? run.stdout.trim() : "";
|
|
22445
|
+
return value ? value : null;
|
|
22446
|
+
};
|
|
22447
|
+
var safeRef2 = (value) => value.length > 0 && !value.startsWith("-");
|
|
22448
|
+
function resolveCommit(cwd, ref) {
|
|
22449
|
+
if (!safeRef2(ref))
|
|
22554
22450
|
return null;
|
|
22555
|
-
|
|
22556
|
-
|
|
22557
|
-
|
|
22558
|
-
|
|
22559
|
-
const char = body[index];
|
|
22560
|
-
if (char === "*" && body[index + 1] === "*") {
|
|
22561
|
-
const slash = body[index + 2] === "/";
|
|
22562
|
-
source += slash ? "(?:.*/)?" : ".*";
|
|
22563
|
-
index += slash ? 2 : 1;
|
|
22564
|
-
} else if (char === "*")
|
|
22565
|
-
source += "[^/]*";
|
|
22566
|
-
else if (char === "?")
|
|
22567
|
-
source += "[^/]";
|
|
22568
|
-
else if (char === "[") {
|
|
22569
|
-
const end = body.indexOf("]", index + 2);
|
|
22570
|
-
if (end < 0)
|
|
22571
|
-
return null;
|
|
22572
|
-
const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
|
|
22573
|
-
source += `[${set}]`;
|
|
22574
|
-
index = end;
|
|
22575
|
-
} else if (char === "\\" && index + 1 < body.length) {
|
|
22576
|
-
source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
22577
|
-
index += 1;
|
|
22578
|
-
} else
|
|
22579
|
-
source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
22580
|
-
}
|
|
22581
|
-
const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
|
|
22582
|
-
try {
|
|
22583
|
-
return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
|
|
22584
|
-
} catch {
|
|
22451
|
+
return git4(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]);
|
|
22452
|
+
}
|
|
22453
|
+
function branchHead(cwd, branch) {
|
|
22454
|
+
if (!safeRef2(branch))
|
|
22585
22455
|
return null;
|
|
22586
|
-
}
|
|
22456
|
+
return resolveCommit(cwd, `refs/heads/${branch}`) ?? resolveCommit(cwd, `refs/remotes/origin/${branch}`);
|
|
22587
22457
|
}
|
|
22588
|
-
function
|
|
22589
|
-
|
|
22590
|
-
|
|
22458
|
+
function diffHash(cwd, base, head) {
|
|
22459
|
+
if (!safeRef2(base) || !safeRef2(head))
|
|
22460
|
+
return null;
|
|
22461
|
+
const diff = gitRun2(cwd, [
|
|
22462
|
+
"diff",
|
|
22463
|
+
"-U3",
|
|
22464
|
+
"--diff-algorithm=myers",
|
|
22465
|
+
"--indent-heuristic",
|
|
22466
|
+
"--no-color",
|
|
22467
|
+
"--no-ext-diff",
|
|
22468
|
+
"--no-textconv",
|
|
22469
|
+
"--no-renames",
|
|
22470
|
+
"--full-index",
|
|
22471
|
+
"--binary",
|
|
22472
|
+
"--src-prefix=a/",
|
|
22473
|
+
"--dst-prefix=b/",
|
|
22474
|
+
`${base}...${head}`,
|
|
22475
|
+
"--"
|
|
22476
|
+
], GIT_TIMEOUTS.worktree);
|
|
22477
|
+
if (!diff.ok || !diff.stdout)
|
|
22478
|
+
return null;
|
|
22479
|
+
const hash = createHash9("sha256");
|
|
22480
|
+
for (const line of diff.stdout.split(`
|
|
22591
22481
|
`)) {
|
|
22592
|
-
|
|
22593
|
-
if (!line || line.startsWith("#") || line.startsWith("[attr]"))
|
|
22482
|
+
if (line.startsWith("index "))
|
|
22594
22483
|
continue;
|
|
22595
|
-
|
|
22596
|
-
|
|
22597
|
-
|
|
22598
|
-
if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
|
|
22599
|
-
generated = true;
|
|
22600
|
-
else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
|
|
22601
|
-
generated = false;
|
|
22602
|
-
}
|
|
22603
|
-
if (generated === null)
|
|
22604
|
-
continue;
|
|
22605
|
-
const regex = patternRegex(dir, pattern);
|
|
22606
|
-
if (regex)
|
|
22607
|
-
out.push({ regex, generated });
|
|
22484
|
+
hash.update(line.startsWith("@@ ") ? "@@" : line);
|
|
22485
|
+
hash.update(`
|
|
22486
|
+
`);
|
|
22608
22487
|
}
|
|
22609
|
-
return
|
|
22488
|
+
return `sha256:${hash.digest("hex")}`;
|
|
22610
22489
|
}
|
|
22611
|
-
function
|
|
22612
|
-
const
|
|
22613
|
-
|
|
22614
|
-
return
|
|
22615
|
-
|
|
22616
|
-
rules: rulesOf(dir, text)
|
|
22617
|
-
};
|
|
22618
|
-
}).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
|
|
22619
|
-
const out = new Set;
|
|
22620
|
-
for (const file of paths) {
|
|
22621
|
-
const last = rules.findLast((rule) => rule.regex.test(file));
|
|
22622
|
-
if (last?.generated)
|
|
22623
|
-
out.add(file);
|
|
22624
|
-
}
|
|
22625
|
-
return out;
|
|
22490
|
+
function storeRoot(cwd) {
|
|
22491
|
+
const location = resolveStore(path15.resolve(cwd));
|
|
22492
|
+
if (location instanceof Error)
|
|
22493
|
+
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");
|
|
22494
|
+
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
22626
22495
|
}
|
|
22627
|
-
|
|
22628
|
-
|
|
22629
|
-
|
|
22630
|
-
|
|
22631
|
-
var
|
|
22632
|
-
|
|
22633
|
-
|
|
22634
|
-
|
|
22635
|
-
|
|
22636
|
-
|
|
22637
|
-
|
|
22638
|
-
|
|
22639
|
-
|
|
22640
|
-
|
|
22641
|
-
|
|
22642
|
-
|
|
22643
|
-
ext,
|
|
22644
|
-
{ line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
|
|
22645
|
-
]),
|
|
22646
|
-
["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
|
|
22647
|
-
...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
|
|
22648
|
-
ext,
|
|
22649
|
-
{ line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
|
|
22650
|
-
])
|
|
22496
|
+
function ledgerPath(cwd) {
|
|
22497
|
+
const root = storeRoot(cwd);
|
|
22498
|
+
return root.ok ? { ok: true, value: path15.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
22499
|
+
}
|
|
22500
|
+
var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
|
|
22501
|
+
var NETWORK_FS_TYPES = new Set([
|
|
22502
|
+
26985,
|
|
22503
|
+
20859,
|
|
22504
|
+
4266872130,
|
|
22505
|
+
4283649346,
|
|
22506
|
+
1702057286,
|
|
22507
|
+
1397113167,
|
|
22508
|
+
12805120,
|
|
22509
|
+
16914839,
|
|
22510
|
+
1196443219,
|
|
22511
|
+
198183888
|
|
22651
22512
|
]);
|
|
22652
|
-
var
|
|
22653
|
-
var
|
|
22654
|
-
|
|
22655
|
-
|
|
22656
|
-
|
|
22513
|
+
var isNetworkFsType = (type) => NETWORK_FS_TYPES.has(type >>> 0);
|
|
22514
|
+
var onNetworkFs = (dir) => {
|
|
22515
|
+
if (process.platform !== "linux")
|
|
22516
|
+
return false;
|
|
22517
|
+
try {
|
|
22518
|
+
return isNetworkFsType(fs12.statfsSync(dir).type);
|
|
22519
|
+
} catch {
|
|
22520
|
+
return false;
|
|
22521
|
+
}
|
|
22657
22522
|
};
|
|
22658
|
-
var
|
|
22659
|
-
var
|
|
22660
|
-
var
|
|
22661
|
-
var
|
|
22662
|
-
|
|
22663
|
-
|
|
22664
|
-
|
|
22665
|
-
|
|
22666
|
-
"return",
|
|
22667
|
-
"typeof",
|
|
22668
|
-
"instanceof",
|
|
22669
|
-
"case",
|
|
22670
|
-
"do",
|
|
22671
|
-
"else",
|
|
22672
|
-
"in",
|
|
22673
|
-
"of",
|
|
22674
|
-
"new",
|
|
22675
|
-
"delete",
|
|
22676
|
-
"void",
|
|
22677
|
-
"throw",
|
|
22678
|
-
"yield",
|
|
22679
|
-
"await"
|
|
22680
|
-
]);
|
|
22681
|
-
function substanceOf(file, text) {
|
|
22682
|
-
const syntax = syntaxOf(file);
|
|
22683
|
-
if (!syntax)
|
|
22684
|
-
return plainSubstance(text);
|
|
22685
|
-
const markup = syntax.block.some(([open]) => open === "<!--");
|
|
22686
|
-
if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
|
|
22687
|
-
return plainSubstance(text);
|
|
22688
|
-
return lex(syntax, text);
|
|
22689
|
-
}
|
|
22690
|
-
function plainSubstance(text) {
|
|
22691
|
-
return text.split(`
|
|
22692
|
-
`).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
|
|
22693
|
-
`);
|
|
22694
|
-
}
|
|
22695
|
-
function lex(syntax, text) {
|
|
22696
|
-
const collapse = !syntax.indented;
|
|
22697
|
-
let out = "";
|
|
22698
|
-
let lineStart = 0;
|
|
22699
|
-
let kept = 0;
|
|
22700
|
-
let pendingSpace = false;
|
|
22701
|
-
const frames = [{ kind: "code", braces: 0 }];
|
|
22702
|
-
let index = 0;
|
|
22703
|
-
if (text.startsWith("#!")) {
|
|
22704
|
-
const end = text.indexOf(`
|
|
22705
|
-
`);
|
|
22706
|
-
out = end < 0 ? text : text.slice(0, end);
|
|
22707
|
-
kept = out.length;
|
|
22708
|
-
index = end < 0 ? text.length : end;
|
|
22523
|
+
var LOCK_WAIT_MS = 5000;
|
|
22524
|
+
var LOCK_STALE_MS = 30000;
|
|
22525
|
+
var LOCK_OWNER = "owner";
|
|
22526
|
+
var lockToken = (lock) => {
|
|
22527
|
+
try {
|
|
22528
|
+
return fs12.readFileSync(path15.join(lock, LOCK_OWNER), "utf8");
|
|
22529
|
+
} catch {
|
|
22530
|
+
return null;
|
|
22709
22531
|
}
|
|
22710
|
-
|
|
22711
|
-
|
|
22712
|
-
|
|
22713
|
-
|
|
22714
|
-
|
|
22715
|
-
}
|
|
22716
|
-
|
|
22717
|
-
|
|
22718
|
-
|
|
22719
|
-
|
|
22720
|
-
|
|
22721
|
-
|
|
22722
|
-
|
|
22723
|
-
|
|
22724
|
-
|
|
22725
|
-
let end = out.length;
|
|
22726
|
-
while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
|
|
22727
|
-
end -= 1;
|
|
22728
|
-
out = out.slice(0, end);
|
|
22729
|
-
}
|
|
22730
|
-
pendingSpace = false;
|
|
22731
|
-
if (out.length > lineStart) {
|
|
22732
|
-
out += `
|
|
22733
|
-
`;
|
|
22734
|
-
lineStart = out.length;
|
|
22735
|
-
}
|
|
22736
|
-
};
|
|
22737
|
-
const regexAllowed = () => {
|
|
22738
|
-
let end = out.length;
|
|
22739
|
-
while (end > 0 && /\s/u.test(out[end - 1]))
|
|
22740
|
-
end -= 1;
|
|
22741
|
-
if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
|
|
22742
|
-
return true;
|
|
22743
|
-
let start = end;
|
|
22744
|
-
while (start > 0 && WORD.test(out[start - 1]))
|
|
22745
|
-
start -= 1;
|
|
22746
|
-
return REGEX_KEYWORDS.has(out.slice(start, end));
|
|
22747
|
-
};
|
|
22748
|
-
const scanString = (quote, escapes, multiline) => {
|
|
22749
|
-
const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
|
|
22750
|
-
const fstring = syntax.fstring === true && /f/iu.test(prefix);
|
|
22751
|
-
let end = index + quote.length;
|
|
22752
|
-
while (end < text.length) {
|
|
22753
|
-
const char = text[end];
|
|
22754
|
-
if (escapes && char === "\\") {
|
|
22755
|
-
end += 2;
|
|
22756
|
-
continue;
|
|
22757
|
-
}
|
|
22758
|
-
if (text.startsWith(quote, end)) {
|
|
22759
|
-
if (fstring && unbalanced(text.slice(index + quote.length, end)))
|
|
22760
|
-
return false;
|
|
22761
|
-
emitVerbatim(text.slice(index, end + quote.length));
|
|
22762
|
-
index = end + quote.length;
|
|
22763
|
-
return true;
|
|
22764
|
-
}
|
|
22765
|
-
if (char === `
|
|
22766
|
-
` && !multiline)
|
|
22767
|
-
return false;
|
|
22768
|
-
end += 1;
|
|
22769
|
-
}
|
|
22770
|
-
return false;
|
|
22771
|
-
};
|
|
22772
|
-
while (index < text.length) {
|
|
22773
|
-
const frame = frames[frames.length - 1];
|
|
22774
|
-
const char = text[index];
|
|
22775
|
-
if (frame.kind === "template") {
|
|
22776
|
-
if (char === "\\") {
|
|
22777
|
-
emitVerbatim(text.slice(index, index + 2));
|
|
22778
|
-
index += 2;
|
|
22779
|
-
} else if (char === "`") {
|
|
22780
|
-
emitVerbatim(char);
|
|
22781
|
-
frames.pop();
|
|
22782
|
-
index += 1;
|
|
22783
|
-
} else if (text.startsWith("${", index)) {
|
|
22784
|
-
emitVerbatim("${");
|
|
22785
|
-
frames.push({ kind: "code", braces: 0 });
|
|
22786
|
-
index += 2;
|
|
22787
|
-
} else {
|
|
22788
|
-
out += char;
|
|
22789
|
-
kept = out.length;
|
|
22790
|
-
index += 1;
|
|
22791
|
-
}
|
|
22792
|
-
continue;
|
|
22793
|
-
}
|
|
22794
|
-
if (char === `
|
|
22795
|
-
`) {
|
|
22796
|
-
newline();
|
|
22797
|
-
index += 1;
|
|
22798
|
-
continue;
|
|
22799
|
-
}
|
|
22800
|
-
if (SPACE.test(char)) {
|
|
22801
|
-
if (collapse)
|
|
22802
|
-
pendingSpace = true;
|
|
22803
|
-
else
|
|
22804
|
-
out += char;
|
|
22805
|
-
index += 1;
|
|
22806
|
-
continue;
|
|
22807
|
-
}
|
|
22808
|
-
const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
|
|
22809
|
-
if (lineOpener) {
|
|
22810
|
-
const end = text.indexOf(`
|
|
22811
|
-
`, index);
|
|
22812
|
-
const comment = text.slice(index, end < 0 ? text.length : end);
|
|
22813
|
-
if (DIRECTIVE.test(comment.slice(lineOpener.length)))
|
|
22814
|
-
emitVerbatim(comment);
|
|
22815
|
-
else
|
|
22816
|
-
pendingSpace = true;
|
|
22817
|
-
index = end < 0 ? text.length : end;
|
|
22818
|
-
continue;
|
|
22819
|
-
}
|
|
22820
|
-
const block = syntax.block.find(([open]) => text.startsWith(open, index));
|
|
22821
|
-
if (block) {
|
|
22822
|
-
const end = text.indexOf(block[1], index + block[0].length);
|
|
22823
|
-
if (end < 0)
|
|
22824
|
-
return null;
|
|
22825
|
-
const comment = text.slice(index, end + block[1].length);
|
|
22826
|
-
if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
|
|
22827
|
-
emitVerbatim(comment);
|
|
22828
|
-
else
|
|
22829
|
-
pendingSpace = true;
|
|
22830
|
-
if (comment.includes(`
|
|
22831
|
-
`))
|
|
22832
|
-
newline();
|
|
22833
|
-
index = end + block[1].length;
|
|
22834
|
-
continue;
|
|
22835
|
-
}
|
|
22836
|
-
if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
|
|
22837
|
-
if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
|
|
22532
|
+
};
|
|
22533
|
+
var lockAgeMs = (lock) => {
|
|
22534
|
+
for (const target of [path15.join(lock, LOCK_OWNER), lock]) {
|
|
22535
|
+
try {
|
|
22536
|
+
return Date.now() - fs12.statSync(target).mtimeMs;
|
|
22537
|
+
} catch {}
|
|
22538
|
+
}
|
|
22539
|
+
return null;
|
|
22540
|
+
};
|
|
22541
|
+
var ledgerLock = {
|
|
22542
|
+
acquire(lock) {
|
|
22543
|
+
try {
|
|
22544
|
+
fs12.mkdirSync(lock);
|
|
22545
|
+
} catch (error) {
|
|
22546
|
+
if (error.code === "EEXIST")
|
|
22838
22547
|
return null;
|
|
22839
|
-
|
|
22548
|
+
throw error;
|
|
22840
22549
|
}
|
|
22841
|
-
|
|
22842
|
-
|
|
22843
|
-
|
|
22844
|
-
|
|
22550
|
+
const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
|
|
22551
|
+
fs12.writeFileSync(path15.join(lock, LOCK_OWNER), token);
|
|
22552
|
+
return token;
|
|
22553
|
+
},
|
|
22554
|
+
release(lock, token) {
|
|
22555
|
+
if (lockToken(lock) !== token)
|
|
22556
|
+
return false;
|
|
22557
|
+
const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
|
|
22558
|
+
try {
|
|
22559
|
+
fs12.renameSync(lock, tomb);
|
|
22560
|
+
} catch {
|
|
22561
|
+
return false;
|
|
22845
22562
|
}
|
|
22846
|
-
if (
|
|
22847
|
-
|
|
22848
|
-
|
|
22849
|
-
|
|
22563
|
+
if (lockToken(tomb) !== token) {
|
|
22564
|
+
try {
|
|
22565
|
+
fs12.renameSync(tomb, lock);
|
|
22566
|
+
} catch {}
|
|
22567
|
+
return false;
|
|
22850
22568
|
}
|
|
22851
|
-
|
|
22852
|
-
|
|
22853
|
-
|
|
22854
|
-
|
|
22569
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
22570
|
+
return true;
|
|
22571
|
+
},
|
|
22572
|
+
token: lockToken,
|
|
22573
|
+
reap(lock, observed) {
|
|
22574
|
+
const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
|
|
22575
|
+
try {
|
|
22576
|
+
fs12.renameSync(lock, tomb);
|
|
22577
|
+
} catch {
|
|
22578
|
+
return false;
|
|
22855
22579
|
}
|
|
22856
|
-
if (
|
|
22857
|
-
|
|
22858
|
-
|
|
22859
|
-
index += 1;
|
|
22860
|
-
continue;
|
|
22580
|
+
if (lockToken(tomb) === observed) {
|
|
22581
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
22582
|
+
return true;
|
|
22861
22583
|
}
|
|
22862
|
-
|
|
22584
|
+
try {
|
|
22585
|
+
fs12.renameSync(tomb, lock);
|
|
22586
|
+
} catch {
|
|
22587
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
22588
|
+
}
|
|
22589
|
+
return false;
|
|
22590
|
+
}
|
|
22591
|
+
};
|
|
22592
|
+
function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
|
|
22593
|
+
const lock = `${file}.lock`;
|
|
22594
|
+
const deadline = Date.now() + waitMs;
|
|
22595
|
+
let token = null;
|
|
22596
|
+
for (;; ) {
|
|
22597
|
+
token = ledgerLock.acquire(lock);
|
|
22598
|
+
if (token)
|
|
22599
|
+
break;
|
|
22600
|
+
const observed = lockToken(lock);
|
|
22601
|
+
const age = lockAgeMs(lock);
|
|
22602
|
+
if (age !== null && age > LOCK_STALE_MS && ledgerLock.reap(lock, observed))
|
|
22603
|
+
continue;
|
|
22604
|
+
if (Date.now() >= deadline)
|
|
22863
22605
|
return null;
|
|
22864
|
-
|
|
22865
|
-
|
|
22866
|
-
|
|
22867
|
-
|
|
22606
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
|
|
22607
|
+
}
|
|
22608
|
+
try {
|
|
22609
|
+
return fn();
|
|
22610
|
+
} finally {
|
|
22611
|
+
ledgerLock.release(lock, token);
|
|
22612
|
+
}
|
|
22613
|
+
}
|
|
22614
|
+
var sha2562 = (text) => createHash9("sha256").update(text).digest("hex");
|
|
22615
|
+
var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
|
|
22616
|
+
${body}`);
|
|
22617
|
+
var TAIL_BYTES = 64 * 1024;
|
|
22618
|
+
var lastLine = (fd, size) => {
|
|
22619
|
+
for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
|
|
22620
|
+
const buffer = Buffer.alloc(span);
|
|
22621
|
+
fs12.readSync(fd, buffer, 0, span, size - span);
|
|
22622
|
+
const end = buffer.lastIndexOf(10);
|
|
22623
|
+
if (end < 0) {
|
|
22624
|
+
if (span === size)
|
|
22868
22625
|
return null;
|
|
22626
|
+
continue;
|
|
22869
22627
|
}
|
|
22870
|
-
|
|
22871
|
-
|
|
22872
|
-
|
|
22873
|
-
|
|
22874
|
-
|
|
22875
|
-
|
|
22628
|
+
let stop = end;
|
|
22629
|
+
let text = "";
|
|
22630
|
+
while (stop >= 0) {
|
|
22631
|
+
const start = buffer.lastIndexOf(10, stop - 1);
|
|
22632
|
+
text = buffer.toString("utf8", start + 1, stop).trim();
|
|
22633
|
+
if (text) {
|
|
22634
|
+
if (start >= 0 || span === size)
|
|
22635
|
+
return text;
|
|
22636
|
+
break;
|
|
22637
|
+
}
|
|
22638
|
+
if (start < 0) {
|
|
22639
|
+
if (span === size)
|
|
22876
22640
|
return null;
|
|
22877
|
-
|
|
22878
|
-
end += 1;
|
|
22879
|
-
else if (text[end] === "[")
|
|
22880
|
-
inClass = true;
|
|
22881
|
-
else if (text[end] === "]")
|
|
22882
|
-
inClass = false;
|
|
22883
|
-
end += 1;
|
|
22641
|
+
break;
|
|
22884
22642
|
}
|
|
22885
|
-
|
|
22886
|
-
|
|
22887
|
-
|
|
22888
|
-
|
|
22643
|
+
stop = start;
|
|
22644
|
+
}
|
|
22645
|
+
if (span === size)
|
|
22646
|
+
return text || null;
|
|
22647
|
+
}
|
|
22648
|
+
return null;
|
|
22649
|
+
};
|
|
22650
|
+
var tighten = (fd) => {
|
|
22651
|
+
try {
|
|
22652
|
+
if ((fs12.fstatSync(fd).mode & 63) !== 0)
|
|
22653
|
+
fs12.fchmodSync(fd, 384);
|
|
22654
|
+
} catch {}
|
|
22655
|
+
};
|
|
22656
|
+
function appendLine(file, build, options) {
|
|
22657
|
+
const write = () => {
|
|
22658
|
+
const fd = fs12.openSync(file, "a+", 384);
|
|
22659
|
+
try {
|
|
22660
|
+
if (process.platform !== "win32")
|
|
22661
|
+
tighten(fd);
|
|
22662
|
+
const size = fs12.fstatSync(fd).size;
|
|
22663
|
+
let fence = "";
|
|
22664
|
+
if (size > 0) {
|
|
22665
|
+
const last = Buffer.alloc(1);
|
|
22666
|
+
fs12.readSync(fd, last, 0, 1, size - 1);
|
|
22667
|
+
if (last[0] !== 10)
|
|
22668
|
+
fence = `
|
|
22669
|
+
`;
|
|
22670
|
+
}
|
|
22671
|
+
const line = build(lastLine(fd, size));
|
|
22672
|
+
if (!line.ok)
|
|
22673
|
+
return line;
|
|
22674
|
+
const bytes = Buffer.from(fence + line.value);
|
|
22675
|
+
const written = fs12.writeSync(fd, bytes);
|
|
22676
|
+
if (written !== bytes.length)
|
|
22677
|
+
return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
|
|
22678
|
+
return { ok: true, value: undefined };
|
|
22679
|
+
} finally {
|
|
22680
|
+
fs12.closeSync(fd);
|
|
22681
|
+
}
|
|
22682
|
+
};
|
|
22683
|
+
try {
|
|
22684
|
+
fs12.mkdirSync(path15.dirname(file), { recursive: true });
|
|
22685
|
+
if (!options.forceLock && !onNetworkFs(path15.dirname(file)))
|
|
22686
|
+
return write();
|
|
22687
|
+
const locked = withLedgerLock(file, write);
|
|
22688
|
+
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
22689
|
+
} catch (error) {
|
|
22690
|
+
return err("unavailable", `cannot append to ${file}: ${error.message}`);
|
|
22691
|
+
}
|
|
22692
|
+
}
|
|
22693
|
+
var sealed = (row, id, at, previous) => {
|
|
22694
|
+
const { prevHash: _prev, rowHash: _row, ...fields } = row;
|
|
22695
|
+
const prevHash = previous === null ? null : sha2562(previous);
|
|
22696
|
+
const body = { v: LEDGER_VERSION, id, at, ...fields, prevHash };
|
|
22697
|
+
const full = { ...body, rowHash: chainHash(prevHash, JSON.stringify(body)) };
|
|
22698
|
+
return { full, line: `${JSON.stringify(full)}
|
|
22699
|
+
` };
|
|
22700
|
+
};
|
|
22701
|
+
function appendRaw(cwd, row, options) {
|
|
22702
|
+
const file = ledgerPath(cwd);
|
|
22703
|
+
if (!file.ok)
|
|
22704
|
+
return file;
|
|
22705
|
+
const id = newId2();
|
|
22706
|
+
const at = (options.now ?? new Date).toISOString();
|
|
22707
|
+
let full = null;
|
|
22708
|
+
const written = appendLine(file.value, (previous) => {
|
|
22709
|
+
const next = sealed(row, id, at, previous);
|
|
22710
|
+
const bytes = Buffer.byteLength(next.line);
|
|
22711
|
+
if (bytes > MAX_LINE_BYTES)
|
|
22712
|
+
return err("invalid_input", `ledger row is ${bytes} bytes; the limit is ${MAX_LINE_BYTES}. Shorten the text and pass details by --ref <path|url>`);
|
|
22713
|
+
full = next.full;
|
|
22714
|
+
return { ok: true, value: next.line };
|
|
22715
|
+
}, options);
|
|
22716
|
+
return written.ok ? { ok: true, value: full } : written;
|
|
22717
|
+
}
|
|
22718
|
+
function observedRowBytes(row) {
|
|
22719
|
+
const sample = sealed({ ...row, observer: "workit_cli" }, newId2(), new Date().toISOString(), "");
|
|
22720
|
+
return Buffer.byteLength(sample.line);
|
|
22721
|
+
}
|
|
22722
|
+
function appendObserved(cwd, row, options = {}) {
|
|
22723
|
+
return appendRaw(cwd, { ...row, observer: "workit_cli" }, options);
|
|
22724
|
+
}
|
|
22725
|
+
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22726
|
+
var str2 = (value) => typeof value === "string" ? value : null;
|
|
22727
|
+
var normalizeActor = (value) => {
|
|
22728
|
+
const actor = isRecord3(value) ? value : {};
|
|
22729
|
+
return {
|
|
22730
|
+
host: str2(actor.host) ?? "unknown",
|
|
22731
|
+
session: str2(actor.session),
|
|
22732
|
+
agentId: str2(actor.agentId)
|
|
22733
|
+
};
|
|
22734
|
+
};
|
|
22735
|
+
function supersedeAllowed(by, target, onBranch = (row) => (by.branch ?? null) === (row.branch ?? null)) {
|
|
22736
|
+
if (by.type !== target.type)
|
|
22737
|
+
return `a ${by.type} row cannot supersede a ${target.type} row`;
|
|
22738
|
+
if (!by.actor.session || by.actor.session !== target.actor.session)
|
|
22739
|
+
return "only the session that wrote a row can supersede it";
|
|
22740
|
+
if (!onBranch(target))
|
|
22741
|
+
return `the row is on another branch (${String(target.branch ?? "none")}); a row supersedes only one on its own branch`;
|
|
22742
|
+
if (by.type === "verdict" && (by.kind ?? "review") !== (target.kind ?? "review"))
|
|
22743
|
+
return `a ${String(by.kind ?? "review")} verdict cannot supersede a ${String(target.kind ?? "review")} verdict; record it with --kind ${String(target.kind ?? "review")}`;
|
|
22744
|
+
if (by.type === "verdict" && by.self === true && target.self !== true)
|
|
22745
|
+
return "a self verdict cannot replace an independent verdict";
|
|
22746
|
+
return null;
|
|
22747
|
+
}
|
|
22748
|
+
function readLedger(cwd, options = {}) {
|
|
22749
|
+
const file = ledgerPath(cwd);
|
|
22750
|
+
if (!file.ok)
|
|
22751
|
+
return file;
|
|
22752
|
+
let text = "";
|
|
22753
|
+
let stat = null;
|
|
22754
|
+
try {
|
|
22755
|
+
stat = fs12.statSync(file.value);
|
|
22756
|
+
text = fs12.readFileSync(file.value, "utf8");
|
|
22757
|
+
} catch (error) {
|
|
22758
|
+
if (error.code === "ENOENT")
|
|
22759
|
+
return {
|
|
22760
|
+
ok: true,
|
|
22761
|
+
value: {
|
|
22762
|
+
path: file.value,
|
|
22763
|
+
rows: [],
|
|
22764
|
+
skipped: 0,
|
|
22765
|
+
integrity: options.integrity ? { chained: 0, legacy: 0, unverified: [] } : null
|
|
22766
|
+
}
|
|
22767
|
+
};
|
|
22768
|
+
return err("unavailable", `cannot read ${file.value}: ${error.message}`);
|
|
22769
|
+
}
|
|
22770
|
+
const rows = [];
|
|
22771
|
+
let skipped = 0;
|
|
22772
|
+
const integrity = { chained: 0, legacy: 0, unverified: [] };
|
|
22773
|
+
const earlier = new Set([null]);
|
|
22774
|
+
let chainStarted = false;
|
|
22775
|
+
const ids = new Set;
|
|
22776
|
+
for (const raw of text.split(`
|
|
22777
|
+
`)) {
|
|
22778
|
+
const trimmed = raw.trim();
|
|
22779
|
+
if (!trimmed)
|
|
22780
|
+
continue;
|
|
22781
|
+
const linked = (prevHash) => earlier.has(prevHash);
|
|
22782
|
+
if (options.integrity)
|
|
22783
|
+
earlier.add(sha2562(trimmed));
|
|
22784
|
+
let parsed;
|
|
22785
|
+
try {
|
|
22786
|
+
parsed = JSON.parse(trimmed);
|
|
22787
|
+
} catch {
|
|
22788
|
+
skipped += 1;
|
|
22889
22789
|
continue;
|
|
22890
22790
|
}
|
|
22891
|
-
if (
|
|
22892
|
-
|
|
22893
|
-
|
|
22894
|
-
else if (char === "}") {
|
|
22895
|
-
if (frame.braces === 0) {
|
|
22896
|
-
emitVerbatim(char);
|
|
22897
|
-
frames.pop();
|
|
22898
|
-
index += 1;
|
|
22899
|
-
continue;
|
|
22900
|
-
}
|
|
22901
|
-
frame.braces -= 1;
|
|
22902
|
-
}
|
|
22791
|
+
if (!isRecord3(parsed) || !str2(parsed.id) || !str2(parsed.type) || !str2(parsed.at)) {
|
|
22792
|
+
skipped += 1;
|
|
22793
|
+
continue;
|
|
22903
22794
|
}
|
|
22904
|
-
|
|
22905
|
-
|
|
22795
|
+
let rowIntegrity = options.integrity ? "verified" : undefined;
|
|
22796
|
+
let reason = null;
|
|
22797
|
+
if (!options.integrity) {} else if (typeof parsed.rowHash === "string") {
|
|
22798
|
+
chainStarted = true;
|
|
22799
|
+
const { rowHash, ...body } = parsed;
|
|
22800
|
+
const prevHash = str2(parsed.prevHash);
|
|
22801
|
+
if (chainHash(prevHash, JSON.stringify(body)) !== rowHash)
|
|
22802
|
+
reason = "hash_mismatch";
|
|
22803
|
+
else if (!linked(prevHash))
|
|
22804
|
+
reason = "broken_link";
|
|
22805
|
+
} else if (chainStarted)
|
|
22806
|
+
reason = "unsigned";
|
|
22807
|
+
else
|
|
22808
|
+
rowIntegrity = "legacy";
|
|
22809
|
+
if (options.integrity && !reason && ids.has(parsed.id))
|
|
22810
|
+
reason = "duplicate_id";
|
|
22811
|
+
if (options.integrity)
|
|
22812
|
+
ids.add(parsed.id);
|
|
22813
|
+
if (reason)
|
|
22814
|
+
rowIntegrity = "unverified";
|
|
22815
|
+
const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
|
|
22816
|
+
const row = {
|
|
22817
|
+
...parsed,
|
|
22818
|
+
v: typeof parsed.v === "number" ? parsed.v : LEDGER_VERSION,
|
|
22819
|
+
id: parsed.id,
|
|
22820
|
+
at: parsed.at,
|
|
22821
|
+
type: parsed.type,
|
|
22822
|
+
actor: normalizeActor(parsed.actor),
|
|
22823
|
+
branch: str2(parsed.branch),
|
|
22824
|
+
head: str2(parsed.head),
|
|
22825
|
+
base: str2(parsed.base),
|
|
22826
|
+
baseSha: str2(parsed.baseSha),
|
|
22827
|
+
patchId: str2(parsed.patchId),
|
|
22828
|
+
diffHash: str2(parsed.diffHash),
|
|
22829
|
+
tree: str2(parsed.tree),
|
|
22830
|
+
dirty: typeof parsed.dirty === "boolean" ? parsed.dirty : null,
|
|
22831
|
+
...pr === undefined ? {} : { pr },
|
|
22832
|
+
...str2(parsed.supersedes) ? { supersedes: parsed.supersedes } : {},
|
|
22833
|
+
seq: rows.length + 1,
|
|
22834
|
+
superseded: false,
|
|
22835
|
+
supersedeIgnored: false,
|
|
22836
|
+
...rowIntegrity ? { integrity: rowIntegrity } : {},
|
|
22837
|
+
...reason ? { integrityReason: reason } : {}
|
|
22838
|
+
};
|
|
22839
|
+
rows.push(row);
|
|
22840
|
+
if (!options.integrity)
|
|
22841
|
+
continue;
|
|
22842
|
+
if (rowIntegrity === "verified")
|
|
22843
|
+
integrity.chained += 1;
|
|
22844
|
+
else if (rowIntegrity === "legacy")
|
|
22845
|
+
integrity.legacy += 1;
|
|
22846
|
+
else if (reason)
|
|
22847
|
+
integrity.unverified.push({
|
|
22848
|
+
seq: row.seq,
|
|
22849
|
+
id: row.id,
|
|
22850
|
+
type: row.type,
|
|
22851
|
+
branch: row.branch,
|
|
22852
|
+
reason
|
|
22853
|
+
});
|
|
22906
22854
|
}
|
|
22907
|
-
|
|
22908
|
-
|
|
22909
|
-
|
|
22910
|
-
|
|
22911
|
-
|
|
22912
|
-
|
|
22913
|
-
|
|
22914
|
-
|
|
22915
|
-
|
|
22916
|
-
|
|
22917
|
-
|
|
22918
|
-
|
|
22855
|
+
const scopes = new Map;
|
|
22856
|
+
const scopeOf = (branch) => {
|
|
22857
|
+
let scope = scopes.get(branch);
|
|
22858
|
+
if (!scope)
|
|
22859
|
+
scopes.set(branch, scope = branchScope(cwd, branch));
|
|
22860
|
+
return scope;
|
|
22861
|
+
};
|
|
22862
|
+
const byId = new Map;
|
|
22863
|
+
for (const row of rows) {
|
|
22864
|
+
const target = row.supersedes ? byId.get(row.supersedes) : undefined;
|
|
22865
|
+
if (target && row.integrity !== "unverified" && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
22866
|
+
target.superseded = true;
|
|
22867
|
+
else if (row.supersedes)
|
|
22868
|
+
row.supersedeIgnored = true;
|
|
22869
|
+
if (!byId.has(row.id))
|
|
22870
|
+
byId.set(row.id, row);
|
|
22919
22871
|
}
|
|
22920
|
-
|
|
22921
|
-
|
|
22922
|
-
|
|
22923
|
-
|
|
22872
|
+
if (options.integrity && stat && Buffer.byteLength(text) === stat.size)
|
|
22873
|
+
tamperCache.set(file.value, {
|
|
22874
|
+
size: stat.size,
|
|
22875
|
+
stamp: fileStamp(stat),
|
|
22876
|
+
breaks: integrity.unverified.filter((row) => TAMPER_REASONS.has(row.reason))
|
|
22877
|
+
});
|
|
22878
|
+
return {
|
|
22879
|
+
ok: true,
|
|
22880
|
+
value: { path: file.value, rows, skipped, integrity: options.integrity ? integrity : null }
|
|
22881
|
+
};
|
|
22924
22882
|
}
|
|
22925
|
-
var
|
|
22926
|
-
|
|
22927
|
-
|
|
22928
|
-
|
|
22883
|
+
var TAMPER_REASONS = new Set(["hash_mismatch", "broken_link"]);
|
|
22884
|
+
var tamperCache = new Map;
|
|
22885
|
+
var fileStamp = (stat) => `${stat.ino}:${stat.mtimeMs}:${stat.ctimeMs}`;
|
|
22886
|
+
function ledgerTampering(cwd) {
|
|
22887
|
+
const file = ledgerPath(cwd);
|
|
22888
|
+
if (!file.ok)
|
|
22889
|
+
return [];
|
|
22929
22890
|
try {
|
|
22930
|
-
|
|
22891
|
+
const stat = fs12.statSync(file.value);
|
|
22892
|
+
const cached = tamperCache.get(file.value);
|
|
22893
|
+
if (cached && cached.size === stat.size && cached.stamp === fileStamp(stat))
|
|
22894
|
+
return cached.breaks;
|
|
22931
22895
|
} catch {
|
|
22932
|
-
return
|
|
22896
|
+
return [];
|
|
22933
22897
|
}
|
|
22898
|
+
const read = readLedger(cwd, { integrity: true });
|
|
22899
|
+
return read.ok ? (read.value.integrity?.unverified ?? []).filter((row) => TAMPER_REASONS.has(row.reason)) : [];
|
|
22934
22900
|
}
|
|
22935
|
-
function
|
|
22936
|
-
const
|
|
22937
|
-
const
|
|
22938
|
-
if (
|
|
22939
|
-
return false;
|
|
22940
|
-
if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
|
|
22941
|
-
return false;
|
|
22942
|
-
const a = substanceOf(file, left);
|
|
22943
|
-
return a !== null && a === substanceOf(file, right);
|
|
22901
|
+
function tamperedUnblock(cwd) {
|
|
22902
|
+
const file = ledgerPath(cwd);
|
|
22903
|
+
const where = file.ok ? file.value : "the ledger file";
|
|
22904
|
+
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`;
|
|
22944
22905
|
}
|
|
22945
|
-
|
|
22946
|
-
|
|
22947
|
-
|
|
22948
|
-
|
|
22949
|
-
|
|
22950
|
-
|
|
22951
|
-
|
|
22952
|
-
|
|
22953
|
-
|
|
22954
|
-
|
|
22955
|
-
|
|
22956
|
-
|
|
22957
|
-
|
|
22958
|
-
|
|
22959
|
-
|
|
22960
|
-
|
|
22961
|
-
|
|
22962
|
-
"
|
|
22963
|
-
"gate",
|
|
22964
|
-
"gates",
|
|
22965
|
-
"policy",
|
|
22966
|
-
"policies",
|
|
22967
|
-
"hook",
|
|
22968
|
-
"hooks",
|
|
22969
|
-
"husky",
|
|
22970
|
-
"lefthook",
|
|
22971
|
-
"release",
|
|
22972
|
-
"releases",
|
|
22973
|
-
"migration",
|
|
22974
|
-
"migrations",
|
|
22975
|
-
"migrate",
|
|
22976
|
-
"workflows"
|
|
22977
|
-
]);
|
|
22978
|
-
var RISK_FILES = new Set([
|
|
22979
|
-
".gitlab-ci.yml",
|
|
22980
|
-
"Jenkinsfile",
|
|
22981
|
-
"azure-pipelines.yml",
|
|
22982
|
-
".pre-commit-config.yaml",
|
|
22983
|
-
".workit/risk-paths"
|
|
22984
|
-
]);
|
|
22985
|
-
var RISK_NAMES = new Set([".gitattributes"]);
|
|
22986
|
-
var RISK_PATHS_FILE = ".workit/risk-paths";
|
|
22987
|
-
var LOCKFILES = new Set([
|
|
22988
|
-
"bun.lock",
|
|
22989
|
-
"bun.lockb",
|
|
22990
|
-
"package-lock.json",
|
|
22991
|
-
"npm-shrinkwrap.json",
|
|
22992
|
-
"yarn.lock",
|
|
22993
|
-
"pnpm-lock.yaml",
|
|
22994
|
-
"Cargo.lock",
|
|
22995
|
-
"go.sum",
|
|
22996
|
-
"poetry.lock",
|
|
22997
|
-
"uv.lock",
|
|
22998
|
-
"Pipfile.lock",
|
|
22999
|
-
"Gemfile.lock",
|
|
23000
|
-
"composer.lock"
|
|
23001
|
-
]);
|
|
23002
|
-
var DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc|txt)$/iu;
|
|
23003
|
-
var DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
|
|
23004
|
-
var INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
|
|
23005
|
-
var TEST_PATH = /(?:^|\/)(?:test|tests|__tests__|spec|specs|fixtures)\/|\.(?:test|spec)\.[^/]+$/iu;
|
|
23006
|
-
var basename = (file) => file.slice(file.lastIndexOf("/") + 1);
|
|
23007
|
-
var isDoc = (file) => !INSTRUCTIONS.test(file) && (DOC_EXT.test(file) || DOC_NAMES.test(basename(file)));
|
|
23008
|
-
function kindOf(file) {
|
|
23009
|
-
if (file.binary)
|
|
23010
|
-
return "behavior";
|
|
23011
|
-
if (file.generated)
|
|
23012
|
-
return "generated";
|
|
23013
|
-
if (LOCKFILES.has(basename(file.path)))
|
|
23014
|
-
return "lockfile";
|
|
23015
|
-
if (isDoc(file.path))
|
|
23016
|
-
return "doc";
|
|
23017
|
-
if (file.cosmetic)
|
|
23018
|
-
return "cosmetic";
|
|
23019
|
-
return TEST_PATH.test(file.path) ? "test" : "behavior";
|
|
22906
|
+
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)}`;
|
|
22907
|
+
function verdictBasis(verdict, current) {
|
|
22908
|
+
if (verdict.head && verdict.head === current.head)
|
|
22909
|
+
return "fresh";
|
|
22910
|
+
if (verdict.patchId && verdict.patchId === current.patchId && verdict.diffHash && verdict.diffHash === current.diffHash)
|
|
22911
|
+
return "carried";
|
|
22912
|
+
return "stale";
|
|
22913
|
+
}
|
|
22914
|
+
var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id);
|
|
22915
|
+
function trailerSessions(cwd, base, head) {
|
|
22916
|
+
if (!base || !head || !safeRef2(base) || !safeRef2(head))
|
|
22917
|
+
return [];
|
|
22918
|
+
const out = git4(cwd, [
|
|
22919
|
+
"log",
|
|
22920
|
+
"--format=%(trailers:key=Workit-Session,valueonly,separator=%x00)",
|
|
22921
|
+
`${base}..${head}`
|
|
22922
|
+
]);
|
|
22923
|
+
return (out ?? "").split(/[\0\n]/u).map((value) => value.trim()).filter(Boolean);
|
|
23020
22924
|
}
|
|
23021
|
-
var
|
|
23022
|
-
function
|
|
23023
|
-
|
|
23024
|
-
|
|
23025
|
-
|
|
23026
|
-
|
|
23027
|
-
|
|
23028
|
-
|
|
23029
|
-
|
|
23030
|
-
|
|
23031
|
-
}
|
|
23032
|
-
|
|
23033
|
-
|
|
23034
|
-
|
|
23035
|
-
|
|
23036
|
-
|
|
22925
|
+
var RENAMED = /^branch: renamed refs\/heads\/(.+) to refs\/heads\/(.+)$/iu;
|
|
22926
|
+
function branchScope(cwd, branch) {
|
|
22927
|
+
const renamedAt = new Map;
|
|
22928
|
+
let since = Number.NEGATIVE_INFINITY;
|
|
22929
|
+
if (safeRef2(branch)) {
|
|
22930
|
+
const log = git4(cwd, [
|
|
22931
|
+
"log",
|
|
22932
|
+
"-g",
|
|
22933
|
+
"--date=unix",
|
|
22934
|
+
"--format=%gd%x09%gs",
|
|
22935
|
+
`refs/heads/${branch}`,
|
|
22936
|
+
"--"
|
|
22937
|
+
]);
|
|
22938
|
+
for (const line of (log ?? "").split(`
|
|
22939
|
+
`)) {
|
|
22940
|
+
const [selector = "", subject = ""] = line.split("\t");
|
|
22941
|
+
const seconds = Number(/@\{(\d+)\}$/u.exec(selector)?.[1]);
|
|
22942
|
+
if (!Number.isFinite(seconds))
|
|
22943
|
+
continue;
|
|
22944
|
+
since = seconds * 1000;
|
|
22945
|
+
const match = RENAMED.exec(subject);
|
|
22946
|
+
if (!match || match[1] === branch)
|
|
22947
|
+
continue;
|
|
22948
|
+
const until = (seconds + 1) * 1000;
|
|
22949
|
+
renamedAt.set(match[1], Math.max(renamedAt.get(match[1]) ?? 0, until));
|
|
23037
22950
|
}
|
|
23038
|
-
regex = new RegExp(`^${source}$`, "u");
|
|
23039
|
-
globCache.set(pattern, regex);
|
|
23040
22951
|
}
|
|
23041
|
-
return
|
|
22952
|
+
return (row) => {
|
|
22953
|
+
if (row.branch === branch)
|
|
22954
|
+
return true;
|
|
22955
|
+
const until = row.branch === null ? undefined : renamedAt.get(row.branch);
|
|
22956
|
+
if (until === undefined)
|
|
22957
|
+
return false;
|
|
22958
|
+
const at = Date.parse(row.at);
|
|
22959
|
+
return at >= since && at <= until;
|
|
22960
|
+
};
|
|
23042
22961
|
}
|
|
23043
|
-
|
|
23044
|
-
|
|
23045
|
-
|
|
23046
|
-
|
|
22962
|
+
var renameAwareBranch = (scopeOf, branch) => {
|
|
22963
|
+
return (target) => {
|
|
22964
|
+
const own = typeof branch === "string" ? branch : null;
|
|
22965
|
+
const theirs = typeof target.branch === "string" ? target.branch : null;
|
|
22966
|
+
if (own === theirs)
|
|
22967
|
+
return true;
|
|
22968
|
+
if (own === null || theirs === null || typeof target.at !== "string")
|
|
22969
|
+
return false;
|
|
22970
|
+
return scopeOf(own)({ branch: theirs, at: target.at });
|
|
22971
|
+
};
|
|
22972
|
+
};
|
|
22973
|
+
function authorSessions(cwd, rows, branch, range = { base: null, head: null }, scope = branchScope(cwd, branch)) {
|
|
22974
|
+
const sessions = new Set;
|
|
22975
|
+
for (const row of rows) {
|
|
22976
|
+
if (!scope(row) || row.type !== "commit.recorded" && row.type !== "task.opened")
|
|
22977
|
+
continue;
|
|
22978
|
+
const session = str2(row.session) ?? row.actor.session;
|
|
22979
|
+
if (session)
|
|
22980
|
+
sessions.add(session);
|
|
22981
|
+
}
|
|
22982
|
+
for (const session of trailerSessions(cwd, range.base, range.head))
|
|
22983
|
+
sessions.add(session);
|
|
22984
|
+
return sessions;
|
|
22985
|
+
}
|
|
22986
|
+
var derivesFrom = (derived, bare) => !bare.includes(":") && derived.endsWith(`:${bare}`);
|
|
22987
|
+
var authoredBy = (authors, session) => {
|
|
22988
|
+
if (authors.has(session))
|
|
23047
22989
|
return true;
|
|
23048
|
-
|
|
23049
|
-
|
|
23050
|
-
|
|
22990
|
+
for (const author of authors)
|
|
22991
|
+
if (derivesFrom(author, session) || derivesFrom(session, author))
|
|
22992
|
+
return true;
|
|
22993
|
+
return false;
|
|
22994
|
+
};
|
|
22995
|
+
var TRUST_REASONS = new Set([
|
|
22996
|
+
"unsigned",
|
|
22997
|
+
"hash_mismatch",
|
|
22998
|
+
"broken_link",
|
|
22999
|
+
"duplicate_id",
|
|
23000
|
+
"unissued_session",
|
|
23001
|
+
"unbound_session"
|
|
23002
|
+
]);
|
|
23003
|
+
var SHELL_VOUCHED_HOSTS = new Set([
|
|
23004
|
+
"cli",
|
|
23005
|
+
"opencode",
|
|
23006
|
+
"pi",
|
|
23007
|
+
"codex_cli",
|
|
23008
|
+
"codex_desktop"
|
|
23009
|
+
]);
|
|
23010
|
+
function sessionTrust(cwd, row) {
|
|
23011
|
+
const session = row.actor.session;
|
|
23012
|
+
if (!session || row.self === true)
|
|
23013
|
+
return null;
|
|
23014
|
+
const source = typeof row.sessionSource === "string" ? row.sessionSource : "env";
|
|
23015
|
+
const host = row.actor.host;
|
|
23016
|
+
const issuedOnly = source === "flag" ? host !== "cli" : source === "env" && session.includes(":") && !SHELL_VOUCHED_HOSTS.has(host);
|
|
23017
|
+
if (!issuedOnly)
|
|
23018
|
+
return null;
|
|
23019
|
+
const binding = issuedBinding(cwd, session);
|
|
23020
|
+
if (binding === null)
|
|
23021
|
+
return "unissued_session";
|
|
23022
|
+
return binding === "token" && row.sessionBound !== true ? "unbound_session" : null;
|
|
23051
23023
|
}
|
|
23052
|
-
|
|
23053
|
-
|
|
23054
|
-
|
|
23055
|
-
|
|
23056
|
-
|
|
23057
|
-
const
|
|
23058
|
-
|
|
23059
|
-
|
|
23060
|
-
|
|
23061
|
-
|
|
23062
|
-
|
|
23063
|
-
const
|
|
23064
|
-
const
|
|
23065
|
-
|
|
23066
|
-
|
|
23067
|
-
|
|
23068
|
-
|
|
23069
|
-
|
|
23024
|
+
function trustReasons(cwd, row) {
|
|
23025
|
+
if (row.integrity === "unverified")
|
|
23026
|
+
return [row.integrityReason ?? "unsigned"];
|
|
23027
|
+
if (row.integrity !== "verified")
|
|
23028
|
+
return ["unsigned"];
|
|
23029
|
+
const session = sessionTrust(cwd, row);
|
|
23030
|
+
return session ? [session] : [];
|
|
23031
|
+
}
|
|
23032
|
+
function withIntegrity(cwd, rows) {
|
|
23033
|
+
if (rows.every((row) => row.integrity !== undefined))
|
|
23034
|
+
return rows;
|
|
23035
|
+
const read = readLedger(cwd, { integrity: true });
|
|
23036
|
+
const bySeq = new Map(read.ok ? read.value.rows.map((row) => [row.seq, row]) : []);
|
|
23037
|
+
return rows.map((row) => {
|
|
23038
|
+
if (row.integrity !== undefined)
|
|
23039
|
+
return row;
|
|
23040
|
+
const fresh = bySeq.get(row.seq);
|
|
23041
|
+
return fresh && fresh.id === row.id && fresh.at === row.at ? fresh : row;
|
|
23070
23042
|
});
|
|
23071
|
-
const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
|
|
23072
|
-
if (input.riskTier === "high")
|
|
23073
|
-
return result("full", ["the task judgment records high risk"]);
|
|
23074
|
-
if (riskFiles.length)
|
|
23075
|
-
return result("full", [
|
|
23076
|
-
`touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
|
|
23077
|
-
]);
|
|
23078
|
-
if (!behavior.length)
|
|
23079
|
-
return result("none", [
|
|
23080
|
-
input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
|
|
23081
|
-
]);
|
|
23082
|
-
if (lines >= fullMinLines)
|
|
23083
|
-
return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
|
|
23084
|
-
if (lines > selfMaxLines)
|
|
23085
|
-
return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
|
|
23086
|
-
if (behavior.length > selfMaxFiles)
|
|
23087
|
-
return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
|
|
23088
|
-
if (opaque.length)
|
|
23089
|
-
return result("focused", [
|
|
23090
|
-
`a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
|
|
23091
|
-
]);
|
|
23092
|
-
return result("self", [
|
|
23093
|
-
`small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
|
|
23094
|
-
]);
|
|
23095
23043
|
}
|
|
23096
|
-
|
|
23097
|
-
|
|
23098
|
-
|
|
23099
|
-
|
|
23100
|
-
|
|
23101
|
-
|
|
23102
|
-
|
|
23103
|
-
|
|
23104
|
-
|
|
23105
|
-
|
|
23106
|
-
|
|
23107
|
-
|
|
23108
|
-
|
|
23109
|
-
|
|
23044
|
+
var untrusted = (reasons) => reasons.some((reason) => TRUST_REASONS.has(reason));
|
|
23045
|
+
var independenceReasons = (row, authors) => {
|
|
23046
|
+
const reasons = [];
|
|
23047
|
+
if (!row.actor.session)
|
|
23048
|
+
reasons.push("no_session");
|
|
23049
|
+
else if (authoredBy(authors, row.actor.session))
|
|
23050
|
+
reasons.push("author_session");
|
|
23051
|
+
if (row.self === true && !reasons.includes("no_session"))
|
|
23052
|
+
reasons.push("self");
|
|
23053
|
+
return reasons;
|
|
23054
|
+
};
|
|
23055
|
+
function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
|
|
23056
|
+
let effective = null;
|
|
23057
|
+
for (const row of rows) {
|
|
23058
|
+
if (!effective) {
|
|
23059
|
+
effective = row;
|
|
23060
|
+
continue;
|
|
23061
|
+
}
|
|
23062
|
+
if (untrusted(reasonsOf(row)) && !untrusted(reasonsOf(effective)))
|
|
23063
|
+
continue;
|
|
23064
|
+
const rowIndependent = reasonsOf(row).length === 0;
|
|
23065
|
+
const effIndependent = reasonsOf(effective).length === 0;
|
|
23066
|
+
if (effIndependent && !rowIndependent && isCurrent(effective))
|
|
23067
|
+
continue;
|
|
23068
|
+
if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session && sameCode(effective, row))
|
|
23069
|
+
continue;
|
|
23070
|
+
effective = row;
|
|
23110
23071
|
}
|
|
23072
|
+
return effective;
|
|
23111
23073
|
}
|
|
23112
|
-
var
|
|
23113
|
-
|
|
23114
|
-
|
|
23115
|
-
|
|
23116
|
-
|
|
23117
|
-
|
|
23118
|
-
|
|
23119
|
-
|
|
23120
|
-
|
|
23121
|
-
|
|
23122
|
-
|
|
23123
|
-
|
|
23124
|
-
|
|
23125
|
-
|
|
23126
|
-
|
|
23127
|
-
|
|
23128
|
-
|
|
23129
|
-
|
|
23130
|
-
|
|
23131
|
-
|
|
23074
|
+
var restackExact = new WeakMap;
|
|
23075
|
+
function restackCarries(exactIn, rows, branch, from, to) {
|
|
23076
|
+
if (!from || !to)
|
|
23077
|
+
return false;
|
|
23078
|
+
const exact = (row, fromHead, head) => {
|
|
23079
|
+
if (exactIn === null)
|
|
23080
|
+
return true;
|
|
23081
|
+
const cwd = exactIn;
|
|
23082
|
+
let known = restackExact.get(row);
|
|
23083
|
+
if (known === undefined) {
|
|
23084
|
+
known = typeof row.fromBase === "string" && typeof row.toBase === "string" && sameOwnChange(cwd, {
|
|
23085
|
+
from: fromHead,
|
|
23086
|
+
to: head,
|
|
23087
|
+
fromBase: row.fromBase,
|
|
23088
|
+
toBase: row.toBase
|
|
23089
|
+
});
|
|
23090
|
+
restackExact.set(row, known);
|
|
23091
|
+
}
|
|
23092
|
+
return known;
|
|
23093
|
+
};
|
|
23094
|
+
const edges = new Map;
|
|
23095
|
+
for (const row of rows)
|
|
23096
|
+
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head && exact(row, row.fromHead, row.head))
|
|
23097
|
+
edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
|
|
23098
|
+
const seen = new Set([from]);
|
|
23099
|
+
const queue = [from];
|
|
23100
|
+
while (queue.length) {
|
|
23101
|
+
const next = queue.shift();
|
|
23102
|
+
if (next === to)
|
|
23103
|
+
return true;
|
|
23104
|
+
for (const target of edges.get(next) ?? [])
|
|
23105
|
+
if (!seen.has(target)) {
|
|
23106
|
+
seen.add(target);
|
|
23107
|
+
queue.push(target);
|
|
23132
23108
|
}
|
|
23133
|
-
if (before && after && cosmeticChange(entry.path, before, after))
|
|
23134
|
-
out.add(entry.path);
|
|
23135
|
-
else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
|
|
23136
|
-
opaque.add(entry.path);
|
|
23137
23109
|
}
|
|
23138
|
-
return
|
|
23110
|
+
return false;
|
|
23139
23111
|
}
|
|
23140
|
-
function
|
|
23141
|
-
const
|
|
23142
|
-
const
|
|
23143
|
-
|
|
23144
|
-
|
|
23145
|
-
const
|
|
23146
|
-
|
|
23147
|
-
|
|
23148
|
-
|
|
23149
|
-
|
|
23112
|
+
function checkVerdicts(cwd, branch, ledgerRows) {
|
|
23113
|
+
const rows = withIntegrity(cwd, ledgerRows);
|
|
23114
|
+
const head = branchHead(cwd, branch);
|
|
23115
|
+
const fallbackBase = defaultBase(cwd);
|
|
23116
|
+
const scope = branchScope(cwd, branch);
|
|
23117
|
+
const keyCache = new Map;
|
|
23118
|
+
const keysAt = (base, at) => {
|
|
23119
|
+
if (!base || !at)
|
|
23120
|
+
return { patchId: null, diffHash: null };
|
|
23121
|
+
const cacheKey = `${base}\x00${at}`;
|
|
23122
|
+
let key = keyCache.get(cacheKey);
|
|
23123
|
+
if (!key) {
|
|
23124
|
+
key = { patchId: patchId(cwd, base, at), diffHash: diffHash(cwd, base, at) };
|
|
23125
|
+
keyCache.set(cacheKey, key);
|
|
23126
|
+
}
|
|
23127
|
+
return key;
|
|
23128
|
+
};
|
|
23129
|
+
const keyFor = (base) => keysAt(base, head);
|
|
23130
|
+
const driftCache = new Map;
|
|
23131
|
+
const driftOf = (row) => {
|
|
23132
|
+
if (driftCache.has(row))
|
|
23133
|
+
return driftCache.get(row) ?? null;
|
|
23134
|
+
let drift = null;
|
|
23135
|
+
const toBase = row.base ?? fallbackBase;
|
|
23136
|
+
const toBaseSha = toBase ? resolveCommit(cwd, toBase) : null;
|
|
23137
|
+
const fromBase = row.baseSha ?? toBaseSha;
|
|
23138
|
+
if (row.head && head && row.head !== head && toBaseSha && fromBase && !untrusted(reasonsOf(row)))
|
|
23139
|
+
drift = verdictDrift(cwd, {
|
|
23140
|
+
from: row.head,
|
|
23141
|
+
to: head,
|
|
23142
|
+
fromBase,
|
|
23143
|
+
toBase: toBaseSha,
|
|
23144
|
+
kind: typeof row.kind === "string" ? row.kind : "review"
|
|
23145
|
+
});
|
|
23146
|
+
driftCache.set(row, drift);
|
|
23147
|
+
return drift;
|
|
23148
|
+
};
|
|
23149
|
+
const exactCache = new Map;
|
|
23150
|
+
const exactCarry = (row) => {
|
|
23151
|
+
if (FAILING.has(String(row.result)))
|
|
23152
|
+
return true;
|
|
23153
|
+
let exact = exactCache.get(row);
|
|
23154
|
+
if (exact === undefined) {
|
|
23155
|
+
const toBase = row.base ?? fallbackBase;
|
|
23156
|
+
const toBaseSha = toBase ? resolveCommit(cwd, toBase) : null;
|
|
23157
|
+
const fromBase = row.baseSha ?? toBaseSha;
|
|
23158
|
+
exact = Boolean(row.head && head && toBaseSha && fromBase && sameOwnChange(cwd, { from: row.head, to: head, fromBase, toBase: toBaseSha }));
|
|
23159
|
+
exactCache.set(row, exact);
|
|
23160
|
+
}
|
|
23161
|
+
return exact;
|
|
23162
|
+
};
|
|
23163
|
+
const keyBasis = (row) => {
|
|
23164
|
+
const basis = verdictBasis(row, { head, ...keyFor(row.base ?? fallbackBase) });
|
|
23165
|
+
return basis === "carried" && !exactCarry(row) ? "stale" : basis;
|
|
23166
|
+
};
|
|
23167
|
+
const restacked = (row) => restackCarries(FAILING.has(String(row.result)) ? null : cwd, rows, branch, row.head, head);
|
|
23168
|
+
const carryOf = (row) => {
|
|
23169
|
+
if (keyBasis(row) !== "stale")
|
|
23170
|
+
return null;
|
|
23171
|
+
if (restacked(row))
|
|
23172
|
+
return null;
|
|
23173
|
+
const drift = driftOf(row);
|
|
23174
|
+
return drift?.carries ? { rule: drift.rule, since: drift.since, files: drift.files } : null;
|
|
23175
|
+
};
|
|
23176
|
+
const basisOf = (row) => {
|
|
23177
|
+
const basis = keyBasis(row);
|
|
23178
|
+
if (basis !== "stale")
|
|
23179
|
+
return basis;
|
|
23180
|
+
return restacked(row) || carryOf(row) ? "carried" : basis;
|
|
23181
|
+
};
|
|
23182
|
+
const sameCode = (failed, row) => {
|
|
23183
|
+
if (failed.head && failed.head === row.head)
|
|
23184
|
+
return true;
|
|
23185
|
+
const base = failed.base ?? fallbackBase;
|
|
23186
|
+
const stored = (row.base ?? fallbackBase) === base && row.baseSha !== null && row.baseSha === failed.baseSha;
|
|
23187
|
+
const keys = stored ? { patchId: row.patchId, diffHash: row.diffHash } : keysAt(base, row.head);
|
|
23188
|
+
return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(null, rows, branch, failed.head, row.head);
|
|
23189
|
+
};
|
|
23190
|
+
const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
|
|
23191
|
+
const failureAuthorCache = new Map;
|
|
23192
|
+
const failureAuthors = (row) => {
|
|
23193
|
+
let set = failureAuthorCache.get(row.seq);
|
|
23194
|
+
if (!set) {
|
|
23195
|
+
set = authorSessions(cwd, rows.filter((other) => other.integrity === "verified" && other.seq < row.seq), branch, { base: null, head: null }, scope);
|
|
23196
|
+
failureAuthorCache.set(row.seq, set);
|
|
23197
|
+
}
|
|
23198
|
+
return set;
|
|
23199
|
+
};
|
|
23200
|
+
const reasonCache = new Map;
|
|
23201
|
+
const reasonsOf = (row) => {
|
|
23202
|
+
let reasons = reasonCache.get(row);
|
|
23203
|
+
if (!reasons) {
|
|
23204
|
+
const failing = FAILING.has(String(row.result));
|
|
23205
|
+
reasons = [
|
|
23206
|
+
...trustReasons(cwd, row),
|
|
23207
|
+
...independenceReasons(row, failing ? failureAuthors(row) : authors)
|
|
23208
|
+
];
|
|
23209
|
+
reasonCache.set(row, reasons);
|
|
23210
|
+
}
|
|
23211
|
+
return reasons;
|
|
23212
|
+
};
|
|
23213
|
+
const retired = new Set;
|
|
23214
|
+
for (const row of rows)
|
|
23215
|
+
if (row.type === "verdict" && row.supersedes && !row.supersedeIgnored && trustReasons(cwd, row).length === 0)
|
|
23216
|
+
retired.add(row.supersedes);
|
|
23217
|
+
const byKind = new Map;
|
|
23218
|
+
for (const row of rows)
|
|
23219
|
+
if (isLiveVerdict(row, retired) && scope(row)) {
|
|
23220
|
+
const kind = typeof row.kind === "string" ? row.kind : "review";
|
|
23221
|
+
byKind.set(kind, [...byKind.get(kind) ?? [], row]);
|
|
23222
|
+
}
|
|
23223
|
+
const verdicts = [];
|
|
23224
|
+
const warnings = [];
|
|
23225
|
+
for (const [kind, list] of byKind) {
|
|
23226
|
+
const row = effectiveOf(list, reasonsOf, sameCode, (candidate) => basisOf(candidate) !== "stale");
|
|
23227
|
+
if (!row)
|
|
23228
|
+
continue;
|
|
23229
|
+
const basis = basisOf(row);
|
|
23230
|
+
const current = basis !== "stale";
|
|
23231
|
+
const independence = reasonsOf(row);
|
|
23232
|
+
const reasons = [
|
|
23233
|
+
...current ? [] : ["stale"],
|
|
23234
|
+
...PASSING.has(String(row.result)) ? [] : ["not_passing"],
|
|
23235
|
+
...independence
|
|
23236
|
+
];
|
|
23237
|
+
verdicts.push({
|
|
23238
|
+
kind,
|
|
23239
|
+
basis,
|
|
23240
|
+
current,
|
|
23241
|
+
independent: independence.length === 0,
|
|
23242
|
+
accepted: reasons.length === 0,
|
|
23243
|
+
reasons,
|
|
23244
|
+
carry: basis === "carried" ? carryOf(row) : null,
|
|
23245
|
+
verdict: row
|
|
23246
|
+
});
|
|
23247
|
+
if (reasons.length === 0) {
|
|
23248
|
+
for (const later of list)
|
|
23249
|
+
if (later.seq > row.seq && reasonsOf(later).length > 0 && !untrusted(reasonsOf(later)) && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
|
|
23250
|
+
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`);
|
|
23251
|
+
}
|
|
23150
23252
|
}
|
|
23151
|
-
|
|
23152
|
-
|
|
23153
|
-
|
|
23154
|
-
|
|
23155
|
-
|
|
23156
|
-
|
|
23157
|
-
|
|
23158
|
-
|
|
23159
|
-
|
|
23160
|
-
|
|
23161
|
-
...entry,
|
|
23162
|
-
...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
|
|
23163
|
-
cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
|
|
23164
|
-
})),
|
|
23165
|
-
...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
|
|
23166
|
-
];
|
|
23167
|
-
const generated = generatedAt(cwd, fork, files.map((entry) => entry.path));
|
|
23168
|
-
return files.map((entry) => ({
|
|
23169
|
-
...entry,
|
|
23170
|
-
generated: generated.has(entry.path)
|
|
23171
|
-
}));
|
|
23172
|
-
}
|
|
23173
|
-
function branchLane(cwd, options) {
|
|
23174
|
-
const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
|
|
23175
|
-
const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
|
|
23176
|
-
const head = options.workingTree ? null : options.head;
|
|
23177
|
-
const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
|
|
23178
|
-
const files = fork ? laneFiles(cwd, fork, head) : null;
|
|
23179
|
-
const riskTier = options.judgment?.riskTier ?? null;
|
|
23180
|
-
if (!fork || !files) {
|
|
23181
|
-
const high = riskTier === "high";
|
|
23182
|
-
return {
|
|
23183
|
-
lane: high ? "full" : "focused",
|
|
23184
|
-
reasons: [
|
|
23185
|
-
`the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
|
|
23186
|
-
...high ? ["the task judgment records high risk"] : []
|
|
23187
|
-
],
|
|
23188
|
-
lines: 0,
|
|
23189
|
-
files: 0,
|
|
23190
|
-
riskFiles: [],
|
|
23191
|
-
base,
|
|
23192
|
-
head: options.head
|
|
23193
|
-
};
|
|
23253
|
+
for (const row of rows)
|
|
23254
|
+
if (row.integrity === "unverified" && scope(row))
|
|
23255
|
+
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)`);
|
|
23256
|
+
const tampered = ledgerTampering(cwd);
|
|
23257
|
+
if (tampered.length) {
|
|
23258
|
+
warnings.push(tamperedWarning(cwd, tampered));
|
|
23259
|
+
for (const entry of verdicts) {
|
|
23260
|
+
entry.accepted = false;
|
|
23261
|
+
entry.reasons = [...entry.reasons, "ledger_tampered"];
|
|
23262
|
+
}
|
|
23194
23263
|
}
|
|
23195
|
-
|
|
23264
|
+
verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
|
|
23265
|
+
const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
|
|
23266
|
+
const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
|
|
23267
|
+
const newestAccepted = verdicts.findLast((entry) => entry.accepted && STRONG_RESULTS.has(String(entry.verdict.result))) ?? null;
|
|
23268
|
+
const accepted = tampered.length ? { accepted: false, verdict: null, reasons: ["ledger_tampered"] } : newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
23269
|
+
accepted: false,
|
|
23270
|
+
verdict: failing?.verdict ?? null,
|
|
23271
|
+
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
23272
|
+
};
|
|
23273
|
+
const latestSelf = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && (STRONG_RESULTS.has(String(entry.verdict.result)) || FAILING.has(String(entry.verdict.result))));
|
|
23274
|
+
const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
|
|
23275
|
+
const carryEntry = verdicts.find((entry) => entry.verdict === accepted.verdict && entry.accepted) ?? newestCurrent;
|
|
23276
|
+
const lastPass = accepted.accepted || failing || tampered.length ? undefined : verdicts.findLast((entry) => !entry.current && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
23277
|
+
const drift = lastPass ? driftOf(lastPass.verdict) : null;
|
|
23278
|
+
const reverify = drift && !drift.carries && drift.focused && drift.files ? { scope: "focused", since: drift.since, files: drift.files } : null;
|
|
23196
23279
|
return {
|
|
23197
|
-
|
|
23198
|
-
|
|
23199
|
-
|
|
23200
|
-
|
|
23201
|
-
|
|
23202
|
-
base,
|
|
23203
|
-
|
|
23280
|
+
branch,
|
|
23281
|
+
head,
|
|
23282
|
+
review: accepted.accepted ? "verified" : selfEntry ? "self-reviewed" : "unreviewed",
|
|
23283
|
+
selfVerdict: accepted.accepted ? null : selfEntry?.verdict ?? null,
|
|
23284
|
+
base: newestCurrent?.verdict.base ?? fallbackBase,
|
|
23285
|
+
patchId: keyFor(newestCurrent?.verdict.base ?? fallbackBase).patchId,
|
|
23286
|
+
current: { basis: newestCurrent?.basis ?? "none", verdict: newestCurrent?.verdict ?? null },
|
|
23287
|
+
carry: carryEntry?.carry ?? null,
|
|
23288
|
+
reverify,
|
|
23289
|
+
accepted,
|
|
23290
|
+
verdicts,
|
|
23291
|
+
authors: [...authors].toSorted(),
|
|
23292
|
+
warnings
|
|
23204
23293
|
};
|
|
23205
23294
|
}
|
|
23206
|
-
var
|
|
23295
|
+
var OBSERVED_ONLY = new Set([
|
|
23296
|
+
"check",
|
|
23297
|
+
"merge.unverified",
|
|
23298
|
+
"policy.judged",
|
|
23299
|
+
...PR_ROW_TYPES
|
|
23300
|
+
]);
|
|
23207
23301
|
|
|
23208
23302
|
// packages/workit-core/src/core/policy/derive.ts
|
|
23209
23303
|
var RULES = {
|
|
@@ -23358,7 +23452,7 @@ var productDecision = (task, ledger) => {
|
|
|
23358
23452
|
};
|
|
23359
23453
|
};
|
|
23360
23454
|
var planRecorded = (task, root) => {
|
|
23361
|
-
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync5(
|
|
23455
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync5(path16.resolve(root, ref.path)));
|
|
23362
23456
|
if (cited)
|
|
23363
23457
|
return {
|
|
23364
23458
|
met: true,
|
|
@@ -23425,14 +23519,14 @@ function derivedRuleStatus(task, requirement, root, ledger) {
|
|
|
23425
23519
|
|
|
23426
23520
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
23427
23521
|
var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
|
|
23428
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
23429
|
-
var relative = (root, candidate) =>
|
|
23522
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path17.sep}`);
|
|
23523
|
+
var relative = (root, candidate) => path17.relative(root, candidate).split(path17.sep).join("/") || ".";
|
|
23430
23524
|
var canonicalPath = (value) => {
|
|
23431
23525
|
if (typeof value !== "string" || !value)
|
|
23432
23526
|
return null;
|
|
23433
23527
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
23434
23528
|
return null;
|
|
23435
|
-
const normalized =
|
|
23529
|
+
const normalized = path17.posix.normalize(value);
|
|
23436
23530
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
23437
23531
|
return null;
|
|
23438
23532
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -23468,7 +23562,7 @@ var compareCodeUnits2 = (left, right) => {
|
|
|
23468
23562
|
return left.length - right.length;
|
|
23469
23563
|
};
|
|
23470
23564
|
var gitPaths = (root) => {
|
|
23471
|
-
const result =
|
|
23565
|
+
const result = spawnSync10("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
23472
23566
|
cwd: root,
|
|
23473
23567
|
encoding: "buffer"
|
|
23474
23568
|
});
|
|
@@ -23480,21 +23574,21 @@ var gitPaths = (root) => {
|
|
|
23480
23574
|
git: !/not a git repository/i.test(stderr)
|
|
23481
23575
|
};
|
|
23482
23576
|
}
|
|
23483
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
23484
|
-
const stagedDeleted =
|
|
23577
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path17.sep).join("/")) : [];
|
|
23578
|
+
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
23485
23579
|
if (stagedDeleted.status !== 0) {
|
|
23486
23580
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
23487
23581
|
if (!/not a git repository/i.test(stderr))
|
|
23488
23582
|
return { paths, uncertain: true, git: true };
|
|
23489
23583
|
} else if (stagedDeleted.stdout) {
|
|
23490
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
23584
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path17.sep).join("/")));
|
|
23491
23585
|
}
|
|
23492
23586
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
23493
23587
|
};
|
|
23494
23588
|
var walk = (root, directory, output) => {
|
|
23495
23589
|
let names;
|
|
23496
23590
|
try {
|
|
23497
|
-
names =
|
|
23591
|
+
names = fs13.readdirSync(directory);
|
|
23498
23592
|
} catch {
|
|
23499
23593
|
return true;
|
|
23500
23594
|
}
|
|
@@ -23502,10 +23596,10 @@ var walk = (root, directory, output) => {
|
|
|
23502
23596
|
for (const name of names) {
|
|
23503
23597
|
if (name === ".git" || name === ".workit")
|
|
23504
23598
|
continue;
|
|
23505
|
-
const target =
|
|
23599
|
+
const target = path17.join(directory, name);
|
|
23506
23600
|
let stat;
|
|
23507
23601
|
try {
|
|
23508
|
-
stat =
|
|
23602
|
+
stat = fs13.lstatSync(target);
|
|
23509
23603
|
} catch {
|
|
23510
23604
|
uncertain = true;
|
|
23511
23605
|
continue;
|
|
@@ -23520,14 +23614,14 @@ var walk = (root, directory, output) => {
|
|
|
23520
23614
|
var scopeRoots = (root, scope) => {
|
|
23521
23615
|
const roots = [];
|
|
23522
23616
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
23523
|
-
const target =
|
|
23617
|
+
const target = path17.resolve(root, item);
|
|
23524
23618
|
if (!inside(root, target))
|
|
23525
23619
|
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` }] });
|
|
23526
23620
|
let ancestor = target;
|
|
23527
|
-
while (!
|
|
23528
|
-
ancestor =
|
|
23621
|
+
while (!fs13.existsSync(ancestor) && ancestor !== root)
|
|
23622
|
+
ancestor = path17.dirname(ancestor);
|
|
23529
23623
|
try {
|
|
23530
|
-
if (!inside(root,
|
|
23624
|
+
if (!inside(root, fs13.realpathSync(ancestor)))
|
|
23531
23625
|
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` }] });
|
|
23532
23626
|
} catch {
|
|
23533
23627
|
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
@@ -23539,7 +23633,7 @@ var scopeRoots = (root, scope) => {
|
|
|
23539
23633
|
function captureCandidate(root, scope, environment = []) {
|
|
23540
23634
|
let checkout;
|
|
23541
23635
|
try {
|
|
23542
|
-
checkout =
|
|
23636
|
+
checkout = fs13.realpathSync(root);
|
|
23543
23637
|
} catch {
|
|
23544
23638
|
return failure2("invalid_input", "candidate root does not exist");
|
|
23545
23639
|
}
|
|
@@ -23557,11 +23651,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
23557
23651
|
if (item === ".git" || item === ".workit")
|
|
23558
23652
|
continue;
|
|
23559
23653
|
names.add(item);
|
|
23560
|
-
if (!
|
|
23654
|
+
if (!fs13.existsSync(target))
|
|
23561
23655
|
continue;
|
|
23562
23656
|
let stat;
|
|
23563
23657
|
try {
|
|
23564
|
-
stat =
|
|
23658
|
+
stat = fs13.lstatSync(target);
|
|
23565
23659
|
} catch {
|
|
23566
23660
|
uncertain = true;
|
|
23567
23661
|
continue;
|
|
@@ -23577,17 +23671,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
23577
23671
|
for (const item of [...names].toSorted()) {
|
|
23578
23672
|
if (!scopeMatches(item, normalizedScope))
|
|
23579
23673
|
continue;
|
|
23580
|
-
const target =
|
|
23674
|
+
const target = path17.join(checkout, item);
|
|
23581
23675
|
try {
|
|
23582
|
-
const stat =
|
|
23676
|
+
const stat = fs13.lstatSync(target);
|
|
23583
23677
|
if (stat.isSymbolicLink()) {
|
|
23584
|
-
const link =
|
|
23678
|
+
const link = fs13.readlinkSync(target);
|
|
23585
23679
|
files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
|
|
23586
23680
|
} else if (stat.isFile()) {
|
|
23587
23681
|
files.push({
|
|
23588
23682
|
path: item,
|
|
23589
23683
|
kind: "file",
|
|
23590
|
-
digest: digestBytes(
|
|
23684
|
+
digest: digestBytes(fs13.readFileSync(target)),
|
|
23591
23685
|
executable: (stat.mode & 73) !== 0
|
|
23592
23686
|
});
|
|
23593
23687
|
} else if (!stat.isDirectory()) {
|
|
@@ -23612,7 +23706,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
23612
23706
|
const values = supplied.toSorted((left, right) => compareCodeUnits2(left.name, right.name)).map(({ name, value }) => ({ name, value, refs: [] }));
|
|
23613
23707
|
if (values.some(({ value }) => value === null))
|
|
23614
23708
|
completeness = "uncertain";
|
|
23615
|
-
const headResult =
|
|
23709
|
+
const headResult = spawnSync10("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
|
|
23616
23710
|
if (headResult.status !== 0 && !/not a git repository/i.test(headResult.stderr ?? ""))
|
|
23617
23711
|
completeness = "uncertain";
|
|
23618
23712
|
const head = headResult.status === 0 ? headResult.stdout.trim() : null;
|
|
@@ -23752,7 +23846,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
|
|
|
23752
23846
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
23753
23847
|
let root;
|
|
23754
23848
|
try {
|
|
23755
|
-
root =
|
|
23849
|
+
root = fs13.realpathSync(checkoutRoot);
|
|
23756
23850
|
} catch {
|
|
23757
23851
|
return failure2("permission_denied", "checkout root is unavailable");
|
|
23758
23852
|
}
|
|
@@ -23761,23 +23855,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
23761
23855
|
continue;
|
|
23762
23856
|
if (!reference.digest)
|
|
23763
23857
|
return failure2("invalid_input", "document references require a byte digest");
|
|
23764
|
-
const target =
|
|
23765
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
23858
|
+
const target = path17.resolve(root, reference.path);
|
|
23859
|
+
if (target !== root && !target.startsWith(`${root}${path17.sep}`))
|
|
23766
23860
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
23767
23861
|
fields: [
|
|
23768
23862
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
23769
23863
|
]
|
|
23770
23864
|
});
|
|
23771
23865
|
try {
|
|
23772
|
-
const below =
|
|
23866
|
+
const below = path17.relative(root, target);
|
|
23773
23867
|
let current = root;
|
|
23774
|
-
for (const segment of below.split(
|
|
23868
|
+
for (const segment of below.split(path17.sep)) {
|
|
23775
23869
|
if (!segment)
|
|
23776
23870
|
continue;
|
|
23777
|
-
current =
|
|
23778
|
-
const stat =
|
|
23779
|
-
const real =
|
|
23780
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
23871
|
+
current = path17.join(current, segment);
|
|
23872
|
+
const stat = fs13.lstatSync(current);
|
|
23873
|
+
const real = fs13.realpathSync(current);
|
|
23874
|
+
if (real !== root && !real.startsWith(`${root}${path17.sep}`))
|
|
23781
23875
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
23782
23876
|
fields: [
|
|
23783
23877
|
{
|
|
@@ -23793,7 +23887,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
23793
23887
|
if (current !== target && !stat.isDirectory())
|
|
23794
23888
|
return failure2("invalid_input", "document reference ancestor is not a directory");
|
|
23795
23889
|
}
|
|
23796
|
-
if (digestBytes(
|
|
23890
|
+
if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
|
|
23797
23891
|
return failure2("permission_denied", "decision document bytes have changed");
|
|
23798
23892
|
} catch {
|
|
23799
23893
|
return failure2("invalid_input", "decision document is unavailable");
|
|
@@ -24636,7 +24730,7 @@ var judgeInputSummary = (raw) => {
|
|
|
24636
24730
|
|
|
24637
24731
|
// packages/workit-core/src/autonomy.ts
|
|
24638
24732
|
import os4 from "node:os";
|
|
24639
|
-
import
|
|
24733
|
+
import path18 from "node:path";
|
|
24640
24734
|
var accountHome = () => {
|
|
24641
24735
|
try {
|
|
24642
24736
|
return os4.userInfo().homedir;
|
|
@@ -24645,10 +24739,10 @@ var accountHome = () => {
|
|
|
24645
24739
|
}
|
|
24646
24740
|
};
|
|
24647
24741
|
var grantsHome = { resolve: accountHome };
|
|
24648
|
-
var grantsDir = () =>
|
|
24742
|
+
var grantsDir = () => path18.join(grantsHome.resolve(), ".config", "workit");
|
|
24649
24743
|
var grantsOverride = () => {
|
|
24650
|
-
const configured =
|
|
24651
|
-
return configured ===
|
|
24744
|
+
const configured = path18.resolve(resolveConfigDir());
|
|
24745
|
+
return configured === path18.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 ${path18.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
|
|
24652
24746
|
};
|
|
24653
24747
|
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
24654
24748
|
var DEFAULT_GRANTS = Object.freeze({
|
|
@@ -26457,9 +26551,9 @@ class WorkitCore {
|
|
|
26457
26551
|
}
|
|
26458
26552
|
}
|
|
26459
26553
|
// packages/workit-core/src/core/repo-context.ts
|
|
26460
|
-
import { spawnSync as
|
|
26554
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
26461
26555
|
import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "node:fs";
|
|
26462
|
-
import
|
|
26556
|
+
import path19 from "node:path";
|
|
26463
26557
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
26464
26558
|
try {
|
|
26465
26559
|
gitRevisionParts(value);
|
|
@@ -26486,7 +26580,7 @@ var invalidRange = (cwd) => ({
|
|
|
26486
26580
|
cwd
|
|
26487
26581
|
});
|
|
26488
26582
|
var runGit = (cwd, args) => {
|
|
26489
|
-
const result =
|
|
26583
|
+
const result = spawnSync11("git", args, { cwd, encoding: "utf8" });
|
|
26490
26584
|
return {
|
|
26491
26585
|
stdout: (result.stdout ?? "").trimEnd(),
|
|
26492
26586
|
stderr: (result.stderr ?? "").trimEnd(),
|
|
@@ -26593,7 +26687,7 @@ var findPrTemplate = (cwd) => {
|
|
|
26593
26687
|
]) {
|
|
26594
26688
|
const parts = rel.split("/");
|
|
26595
26689
|
const base = parts[parts.length - 1];
|
|
26596
|
-
const dir = parts.length > 1 ?
|
|
26690
|
+
const dir = parts.length > 1 ? path19.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
26597
26691
|
const actual = probeCaseInsensitive(dir, base);
|
|
26598
26692
|
if (actual)
|
|
26599
26693
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -26624,7 +26718,7 @@ var diffStatForRange = (cwd, range) => {
|
|
|
26624
26718
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
26625
26719
|
};
|
|
26626
26720
|
var packageScripts = (cwd, keys) => {
|
|
26627
|
-
const pkgPath =
|
|
26721
|
+
const pkgPath = path19.join(cwd, "package.json");
|
|
26628
26722
|
if (!existsSync7(pkgPath))
|
|
26629
26723
|
return [];
|
|
26630
26724
|
try {
|
|
@@ -26650,9 +26744,9 @@ var documentationFiles = (cwd) => {
|
|
|
26650
26744
|
if (entry.isDirectory()) {
|
|
26651
26745
|
if (excluded.has(entry.name))
|
|
26652
26746
|
continue;
|
|
26653
|
-
walk(
|
|
26747
|
+
walk(path19.join(dir, entry.name), depth + 1);
|
|
26654
26748
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
26655
|
-
matches.push("./" +
|
|
26749
|
+
matches.push("./" + path19.relative(cwd, path19.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
26656
26750
|
}
|
|
26657
26751
|
}
|
|
26658
26752
|
};
|
|
@@ -26668,7 +26762,7 @@ var readTrimmed2 = (file, maxLines) => {
|
|
|
26668
26762
|
`);
|
|
26669
26763
|
};
|
|
26670
26764
|
function prReadyContext(root, range) {
|
|
26671
|
-
const cwd =
|
|
26765
|
+
const cwd = path19.resolve(repoRoot(root));
|
|
26672
26766
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
26673
26767
|
return invalidRange(cwd);
|
|
26674
26768
|
let stdout = "";
|
|
@@ -26726,7 +26820,7 @@ ${status.stderr}` : "");
|
|
|
26726
26820
|
stdout += `template_path: ${template}
|
|
26727
26821
|
|
|
26728
26822
|
`;
|
|
26729
|
-
stdout += readTrimmed2(
|
|
26823
|
+
stdout += readTrimmed2(path19.join(cwd, template), 220);
|
|
26730
26824
|
} else {
|
|
26731
26825
|
stdout += `template_path: none
|
|
26732
26826
|
|
|
@@ -26736,7 +26830,7 @@ ${status.stderr}` : "");
|
|
|
26736
26830
|
stdout += `
|
|
26737
26831
|
`;
|
|
26738
26832
|
stdout += printSection("Recent Validation Signals");
|
|
26739
|
-
if (existsSync7(
|
|
26833
|
+
if (existsSync7(path19.join(cwd, "package.json"))) {
|
|
26740
26834
|
stdout += `package.json detected. Common scripts:
|
|
26741
26835
|
`;
|
|
26742
26836
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -26745,7 +26839,7 @@ ${status.stderr}` : "");
|
|
|
26745
26839
|
`) + `
|
|
26746
26840
|
`;
|
|
26747
26841
|
}
|
|
26748
|
-
if (existsSync7(
|
|
26842
|
+
if (existsSync7(path19.join(cwd, "Cargo.toml")) || existsSync7(path19.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
26749
26843
|
stdout += `Rust project detected.
|
|
26750
26844
|
`;
|
|
26751
26845
|
}
|
|
@@ -26778,7 +26872,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
26778
26872
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
26779
26873
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
26780
26874
|
function changelogContext(root, range) {
|
|
26781
|
-
const cwd =
|
|
26875
|
+
const cwd = path19.resolve(repoRoot(root));
|
|
26782
26876
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
26783
26877
|
return invalidRange(cwd);
|
|
26784
26878
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -26796,7 +26890,7 @@ function changelogContext(root, range) {
|
|
|
26796
26890
|
stdout += CHANGELOG_RULES + `
|
|
26797
26891
|
`;
|
|
26798
26892
|
stdout += printSection("Existing CHANGELOG.md");
|
|
26799
|
-
const changelogPath =
|
|
26893
|
+
const changelogPath = path19.join(cwd, "CHANGELOG.md");
|
|
26800
26894
|
if (existsSync7(changelogPath)) {
|
|
26801
26895
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
26802
26896
|
`;
|
|
@@ -26816,7 +26910,7 @@ function changelogContext(root, range) {
|
|
|
26816
26910
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
26817
26911
|
}
|
|
26818
26912
|
function docsRefreshContext(root, range) {
|
|
26819
|
-
const cwd =
|
|
26913
|
+
const cwd = path19.resolve(repoRoot(root));
|
|
26820
26914
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
26821
26915
|
return invalidRange(cwd);
|
|
26822
26916
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -26838,7 +26932,7 @@ function docsRefreshContext(root, range) {
|
|
|
26838
26932
|
`) + `
|
|
26839
26933
|
`;
|
|
26840
26934
|
stdout += printSection("README Preview");
|
|
26841
|
-
const readme =
|
|
26935
|
+
const readme = path19.join(cwd, "README.md");
|
|
26842
26936
|
if (existsSync7(readme)) {
|
|
26843
26937
|
stdout += readTrimmed2(readme, 220) + `
|
|
26844
26938
|
`;
|
|
@@ -26847,7 +26941,7 @@ function docsRefreshContext(root, range) {
|
|
|
26847
26941
|
`;
|
|
26848
26942
|
}
|
|
26849
26943
|
stdout += printSection("Package Scripts");
|
|
26850
|
-
const pkgPath =
|
|
26944
|
+
const pkgPath = path19.join(cwd, "package.json");
|
|
26851
26945
|
if (existsSync7(pkgPath)) {
|
|
26852
26946
|
try {
|
|
26853
26947
|
const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
|
|
@@ -26861,7 +26955,7 @@ function docsRefreshContext(root, range) {
|
|
|
26861
26955
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
26862
26956
|
}
|
|
26863
26957
|
function releaseNotesContext(root, rangeOrTag) {
|
|
26864
|
-
const cwd =
|
|
26958
|
+
const cwd = path19.resolve(repoRoot(root));
|
|
26865
26959
|
if (!rangeOrTag) {
|
|
26866
26960
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
26867
26961
|
`, exitCode: 1, cwd };
|
|
@@ -26899,7 +26993,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
26899
26993
|
`;
|
|
26900
26994
|
stdout += printSection("Existing Release Files");
|
|
26901
26995
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
26902
|
-
if (existsSync7(
|
|
26996
|
+
if (existsSync7(path19.join(cwd, rel)))
|
|
26903
26997
|
stdout += rel + `
|
|
26904
26998
|
`;
|
|
26905
26999
|
}
|
|
@@ -26928,6 +27022,46 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
26928
27022
|
|
|
26929
27023
|
// packages/workit-core/src/core/tracker-issues.ts
|
|
26930
27024
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
27025
|
+
|
|
27026
|
+
// packages/workit-core/src/core/pr-create.ts
|
|
27027
|
+
import fs14 from "node:fs";
|
|
27028
|
+
import path20 from "node:path";
|
|
27029
|
+
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
27030
|
+
// packages/workit-core/src/forge/pr-body.ts
|
|
27031
|
+
function parseGhIssue(value) {
|
|
27032
|
+
const m = /issues\/(\d+)/.exec(value);
|
|
27033
|
+
return m ? m[1] : String(value).trim().replace(/^#/, "");
|
|
27034
|
+
}
|
|
27035
|
+
|
|
27036
|
+
// packages/workit-core/src/core/pr-create.ts
|
|
27037
|
+
function whichOnPath(tool) {
|
|
27038
|
+
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
27039
|
+
for (const dir of (process.env.PATH ?? "").split(path20.delimiter)) {
|
|
27040
|
+
if (!dir)
|
|
27041
|
+
continue;
|
|
27042
|
+
for (const name of names) {
|
|
27043
|
+
const candidate = path20.join(dir, name);
|
|
27044
|
+
try {
|
|
27045
|
+
fs14.accessSync(candidate, fs14.constants.X_OK);
|
|
27046
|
+
return candidate;
|
|
27047
|
+
} catch {}
|
|
27048
|
+
}
|
|
27049
|
+
}
|
|
27050
|
+
return null;
|
|
27051
|
+
}
|
|
27052
|
+
var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
|
|
27053
|
+
var pushRemote = (cwd) => {
|
|
27054
|
+
const result = spawnSync12("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
27055
|
+
cwd,
|
|
27056
|
+
encoding: "utf8"
|
|
27057
|
+
});
|
|
27058
|
+
if (result.status !== 0)
|
|
27059
|
+
return null;
|
|
27060
|
+
const urls = (result.stdout ?? "").split(/\r?\n/u).map((url) => url.trim()).filter(Boolean);
|
|
27061
|
+
return urls.length === 1 ? urls[0] : null;
|
|
27062
|
+
};
|
|
27063
|
+
|
|
27064
|
+
// packages/workit-core/src/core/tracker-issues.ts
|
|
26931
27065
|
var repoPathFromRemote = (remote) => {
|
|
26932
27066
|
let rest = (remote || "").trim().replace(/\/+$/, "");
|
|
26933
27067
|
if (!rest)
|
|
@@ -27821,6 +27955,25 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
27821
27955
|
var signals = new Map;
|
|
27822
27956
|
var cache = new Map;
|
|
27823
27957
|
var TURN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
27958
|
+
// packages/workit-core/src/hooks/delivery-verbs.ts
|
|
27959
|
+
var WORKIT_ALLOWED_VERBS = [
|
|
27960
|
+
"git",
|
|
27961
|
+
"pr",
|
|
27962
|
+
"ci",
|
|
27963
|
+
"stack",
|
|
27964
|
+
"release",
|
|
27965
|
+
"task",
|
|
27966
|
+
"ledger",
|
|
27967
|
+
"evidence",
|
|
27968
|
+
"finding",
|
|
27969
|
+
"decision",
|
|
27970
|
+
"worker",
|
|
27971
|
+
"verify-delivery",
|
|
27972
|
+
"policy",
|
|
27973
|
+
"doctor",
|
|
27974
|
+
"help"
|
|
27975
|
+
];
|
|
27976
|
+
var ALLOWED = new Set(WORKIT_ALLOWED_VERBS);
|
|
27824
27977
|
// packages/workit-core/src/core/heavy.ts
|
|
27825
27978
|
var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
|
|
27826
27979
|
var sub = (...names) => new Set(names);
|
|
@@ -28029,6 +28182,10 @@ var WRITE_TOOLS = new Set([
|
|
|
28029
28182
|
// packages/workit-core/src/hooks/standing.ts
|
|
28030
28183
|
var sinceStart = new Map;
|
|
28031
28184
|
// packages/workit-core/src/hooks/cwd-normalize.ts
|
|
28185
|
+
var DIR = String.raw`[A-Za-z0-9_./@%+:,=-]+|'[^'$${"`"}\\\n]*'|"[^"$${"`"}\\!\n]*"`;
|
|
28186
|
+
var COMPOUND = new RegExp(String.raw`^\s*cd\s+(${DIR})\s*&&\s*(workit(?:\s[\s\S]*)?)$`, "u");
|
|
28187
|
+
var GLOBALS_FIRST = new RegExp(String.raw`^(\s*workit)((?:\s+(?:--json|--cwd(?:=|\s+)(?:${DIR})))+)\s+([a-z][a-z-]*)(?=\s|$)`, "u");
|
|
28188
|
+
var VERBS = new Set(WORKIT_ALLOWED_VERBS);
|
|
28032
28189
|
var FILTERS = new Set(["tail", "head", "grep"]);
|
|
28033
28190
|
|
|
28034
28191
|
// packages/workit-core/src/hooks/worktree-confine.ts
|