@brainervirus/workit-codex 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/.codex-plugin/plugin.json +1 -1
- package/agents/workit-implementer.toml +5 -4
- package/agents/workit-verifier.toml +2 -2
- package/dist/launch-mcp.js +1650 -1493
- package/dist/{workit-hook-q465naq9.js → workit-hook-7a6v1719.js} +1734 -1463
- package/dist/workit-hook.js +1 -1
- 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/launch-mcp.js
CHANGED
|
@@ -15400,6 +15400,26 @@ function resolveRef(cwd, ref) {
|
|
|
15400
15400
|
return null;
|
|
15401
15401
|
return line(git(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
|
|
15402
15402
|
}
|
|
15403
|
+
function originHeadRef(cwd) {
|
|
15404
|
+
return line(git(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]));
|
|
15405
|
+
}
|
|
15406
|
+
function ownChangeDrift(cwd, range) {
|
|
15407
|
+
const before = diffRaw(cwd, range.fromFork, range.from);
|
|
15408
|
+
const after = diffRaw(cwd, range.toFork, range.to);
|
|
15409
|
+
if (!before || !after)
|
|
15410
|
+
return null;
|
|
15411
|
+
const judged = new Map(before.map((entry) => [entry.path, entry]));
|
|
15412
|
+
const current = new Map(after.map((entry) => [entry.path, entry]));
|
|
15413
|
+
const same = (a, b) => a !== undefined && b !== undefined && a.oldMode === b.oldMode && a.oldBlob === b.oldBlob && a.newMode === b.newMode && a.newBlob === b.newBlob;
|
|
15414
|
+
return [...new Set([...judged.keys(), ...current.keys()])].filter((file) => !same(judged.get(file), current.get(file))).toSorted();
|
|
15415
|
+
}
|
|
15416
|
+
function sameOwnChange(cwd, range) {
|
|
15417
|
+
const fromFork = mergeBase(cwd, range.fromBase, range.from);
|
|
15418
|
+
const toFork = mergeBase(cwd, range.toBase, range.to);
|
|
15419
|
+
if (!fromFork || !toFork)
|
|
15420
|
+
return false;
|
|
15421
|
+
return ownChangeDrift(cwd, { from: range.from, to: range.to, fromFork, toFork })?.length === 0;
|
|
15422
|
+
}
|
|
15403
15423
|
var LANE_DIFF = [
|
|
15404
15424
|
"-c",
|
|
15405
15425
|
"core.quotePath=false",
|
|
@@ -19026,9 +19046,9 @@ var workerBlocksTransition = (transition, state) => transition === "pause" ? fal
|
|
|
19026
19046
|
|
|
19027
19047
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
19028
19048
|
import { createHash as createHash10 } from "node:crypto";
|
|
19029
|
-
import * as
|
|
19030
|
-
import { spawnSync as
|
|
19031
|
-
import
|
|
19049
|
+
import * as fs13 from "node:fs";
|
|
19050
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
19051
|
+
import path17 from "node:path";
|
|
19032
19052
|
|
|
19033
19053
|
// packages/workit-core/src/check-config.ts
|
|
19034
19054
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
@@ -19249,68 +19269,20 @@ function checkCommand(args, version, onPath = workitAvailable()) {
|
|
|
19249
19269
|
|
|
19250
19270
|
// packages/workit-core/src/core/policy/derive.ts
|
|
19251
19271
|
import { existsSync as existsSync5 } from "node:fs";
|
|
19252
|
-
import
|
|
19272
|
+
import path16 from "node:path";
|
|
19253
19273
|
|
|
19254
19274
|
// packages/workit-core/src/ledger.ts
|
|
19255
19275
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
19256
19276
|
import { createHash as createHash9, randomBytes as randomBytes3 } from "node:crypto";
|
|
19257
19277
|
import fs12 from "node:fs";
|
|
19278
|
+
import path15 from "node:path";
|
|
19279
|
+
|
|
19280
|
+
// packages/workit-core/src/core/policy/lane.ts
|
|
19281
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
19258
19282
|
import path14 from "node:path";
|
|
19259
|
-
|
|
19260
|
-
|
|
19261
|
-
var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
|
|
19262
|
-
var FAILING = new Set(["failed", "blocked"]);
|
|
19263
|
-
var STRONG_RESULTS = new Set(["verified", "tests-verified"]);
|
|
19264
|
-
var PR_ROW_TYPES = new Set([
|
|
19265
|
-
"pr.created",
|
|
19266
|
-
"pr.status",
|
|
19267
|
-
"pr.merged",
|
|
19268
|
-
"pr.linked"
|
|
19269
|
-
]);
|
|
19270
|
-
var err = (code, error, unblock) => ({
|
|
19271
|
-
ok: false,
|
|
19272
|
-
code,
|
|
19273
|
-
error,
|
|
19274
|
-
...unblock ? { unblock } : {}
|
|
19275
|
-
});
|
|
19276
|
-
var gitRun2 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
|
|
19277
|
-
const run = spawnSync9("git", args, {
|
|
19278
|
-
cwd,
|
|
19279
|
-
encoding: "utf8",
|
|
19280
|
-
timeout: timeoutMs,
|
|
19281
|
-
killSignal: "SIGKILL",
|
|
19282
|
-
windowsHide: true,
|
|
19283
|
-
maxBuffer: 256 * 1024 * 1024,
|
|
19284
|
-
env: {
|
|
19285
|
-
...process.env,
|
|
19286
|
-
GIT_TERMINAL_PROMPT: "0",
|
|
19287
|
-
GIT_OPTIONAL_LOCKS: "0",
|
|
19288
|
-
LC_ALL: "C",
|
|
19289
|
-
LANGUAGE: "C"
|
|
19290
|
-
}
|
|
19291
|
-
});
|
|
19292
|
-
return {
|
|
19293
|
-
ok: run.status === 0,
|
|
19294
|
-
stdout: run.stdout ?? "",
|
|
19295
|
-
stderr: run.error ? String(run.error.message) : run.stderr ?? ""
|
|
19296
|
-
};
|
|
19297
|
-
};
|
|
19298
|
-
var git4 = (cwd, args) => {
|
|
19299
|
-
const run = gitRun2(cwd, args);
|
|
19300
|
-
const value = run.ok ? run.stdout.trim() : "";
|
|
19301
|
-
return value ? value : null;
|
|
19302
|
-
};
|
|
19283
|
+
|
|
19284
|
+
// packages/workit-core/src/git/default-base.ts
|
|
19303
19285
|
var safeRef = (value) => value.length > 0 && !value.startsWith("-");
|
|
19304
|
-
function resolveCommit(cwd, ref) {
|
|
19305
|
-
if (!safeRef(ref))
|
|
19306
|
-
return null;
|
|
19307
|
-
return git4(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]);
|
|
19308
|
-
}
|
|
19309
|
-
function branchHead(cwd, branch) {
|
|
19310
|
-
if (!safeRef(branch))
|
|
19311
|
-
return null;
|
|
19312
|
-
return resolveCommit(cwd, `refs/heads/${branch}`) ?? resolveCommit(cwd, `refs/remotes/origin/${branch}`);
|
|
19313
|
-
}
|
|
19314
19286
|
var trackTrunk = (cwd) => {
|
|
19315
19287
|
try {
|
|
19316
19288
|
const resolved = vcsConfig("resolve", cwd);
|
|
@@ -19326,11 +19298,11 @@ function defaultBase(cwd) {
|
|
|
19326
19298
|
const trunk = trackTrunk(cwd);
|
|
19327
19299
|
if (trunk) {
|
|
19328
19300
|
for (const candidate of [`origin/${trunk}`, trunk])
|
|
19329
|
-
if (
|
|
19301
|
+
if (resolveRef(cwd, candidate))
|
|
19330
19302
|
return candidate;
|
|
19331
19303
|
}
|
|
19332
|
-
const originHead =
|
|
19333
|
-
if (originHead &&
|
|
19304
|
+
const originHead = originHeadRef(cwd);
|
|
19305
|
+
if (originHead && resolveRef(cwd, originHead))
|
|
19334
19306
|
return originHead;
|
|
19335
19307
|
for (const candidate of [
|
|
19336
19308
|
"origin/main",
|
|
@@ -19340,1489 +19312,1611 @@ function defaultBase(cwd) {
|
|
|
19340
19312
|
"origin/develop",
|
|
19341
19313
|
"develop"
|
|
19342
19314
|
])
|
|
19343
|
-
if (
|
|
19315
|
+
if (resolveRef(cwd, candidate))
|
|
19344
19316
|
return candidate;
|
|
19345
19317
|
return null;
|
|
19346
19318
|
}
|
|
19347
|
-
|
|
19348
|
-
|
|
19349
|
-
|
|
19350
|
-
|
|
19351
|
-
"diff",
|
|
19352
|
-
"-U3",
|
|
19353
|
-
"--diff-algorithm=myers",
|
|
19354
|
-
"--indent-heuristic",
|
|
19355
|
-
"--no-color",
|
|
19356
|
-
"--no-ext-diff",
|
|
19357
|
-
"--no-textconv",
|
|
19358
|
-
"--no-renames",
|
|
19359
|
-
"--full-index",
|
|
19360
|
-
"--binary",
|
|
19361
|
-
"--src-prefix=a/",
|
|
19362
|
-
"--dst-prefix=b/",
|
|
19363
|
-
`${base}...${head}`,
|
|
19364
|
-
"--"
|
|
19365
|
-
], GIT_TIMEOUTS.worktree);
|
|
19366
|
-
if (!diff.ok || !diff.stdout)
|
|
19319
|
+
|
|
19320
|
+
// packages/workit-core/src/core/policy/attributes.ts
|
|
19321
|
+
function patternRegex(dir, pattern) {
|
|
19322
|
+
if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
|
|
19367
19323
|
return null;
|
|
19368
|
-
const
|
|
19369
|
-
|
|
19370
|
-
|
|
19371
|
-
|
|
19372
|
-
|
|
19373
|
-
|
|
19374
|
-
|
|
19375
|
-
|
|
19376
|
-
|
|
19377
|
-
|
|
19378
|
-
|
|
19379
|
-
|
|
19380
|
-
|
|
19381
|
-
|
|
19382
|
-
|
|
19383
|
-
|
|
19384
|
-
|
|
19385
|
-
|
|
19386
|
-
|
|
19387
|
-
|
|
19388
|
-
}
|
|
19389
|
-
|
|
19390
|
-
|
|
19391
|
-
|
|
19392
|
-
|
|
19393
|
-
4266872130,
|
|
19394
|
-
4283649346,
|
|
19395
|
-
1702057286,
|
|
19396
|
-
1397113167,
|
|
19397
|
-
12805120,
|
|
19398
|
-
16914839,
|
|
19399
|
-
1196443219,
|
|
19400
|
-
198183888
|
|
19401
|
-
]);
|
|
19402
|
-
var isNetworkFsType = (type) => NETWORK_FS_TYPES.has(type >>> 0);
|
|
19403
|
-
var onNetworkFs = (dir) => {
|
|
19404
|
-
if (process.platform !== "linux")
|
|
19405
|
-
return false;
|
|
19406
|
-
try {
|
|
19407
|
-
return isNetworkFsType(fs12.statfsSync(dir).type);
|
|
19408
|
-
} catch {
|
|
19409
|
-
return false;
|
|
19324
|
+
const anchored = pattern.includes("/");
|
|
19325
|
+
const body = pattern.replace(/^\//u, "");
|
|
19326
|
+
let source = "";
|
|
19327
|
+
for (let index = 0;index < body.length; index += 1) {
|
|
19328
|
+
const char = body[index];
|
|
19329
|
+
if (char === "*" && body[index + 1] === "*") {
|
|
19330
|
+
const slash = body[index + 2] === "/";
|
|
19331
|
+
source += slash ? "(?:.*/)?" : ".*";
|
|
19332
|
+
index += slash ? 2 : 1;
|
|
19333
|
+
} else if (char === "*")
|
|
19334
|
+
source += "[^/]*";
|
|
19335
|
+
else if (char === "?")
|
|
19336
|
+
source += "[^/]";
|
|
19337
|
+
else if (char === "[") {
|
|
19338
|
+
const end = body.indexOf("]", index + 2);
|
|
19339
|
+
if (end < 0)
|
|
19340
|
+
return null;
|
|
19341
|
+
const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
|
|
19342
|
+
source += `[${set}]`;
|
|
19343
|
+
index = end;
|
|
19344
|
+
} else if (char === "\\" && index + 1 < body.length) {
|
|
19345
|
+
source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
19346
|
+
index += 1;
|
|
19347
|
+
} else
|
|
19348
|
+
source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
19410
19349
|
}
|
|
19411
|
-
};
|
|
19412
|
-
var LOCK_WAIT_MS = 5000;
|
|
19413
|
-
var LOCK_STALE_MS = 30000;
|
|
19414
|
-
var LOCK_OWNER = "owner";
|
|
19415
|
-
var lockToken = (lock) => {
|
|
19350
|
+
const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
|
|
19416
19351
|
try {
|
|
19417
|
-
return
|
|
19352
|
+
return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
|
|
19418
19353
|
} catch {
|
|
19419
19354
|
return null;
|
|
19420
19355
|
}
|
|
19421
|
-
}
|
|
19422
|
-
|
|
19423
|
-
|
|
19424
|
-
|
|
19425
|
-
|
|
19426
|
-
|
|
19427
|
-
|
|
19428
|
-
|
|
19429
|
-
|
|
19430
|
-
|
|
19431
|
-
|
|
19432
|
-
|
|
19433
|
-
|
|
19434
|
-
|
|
19435
|
-
|
|
19436
|
-
return null;
|
|
19437
|
-
throw error;
|
|
19438
|
-
}
|
|
19439
|
-
const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
|
|
19440
|
-
fs12.writeFileSync(path14.join(lock, LOCK_OWNER), token);
|
|
19441
|
-
return token;
|
|
19442
|
-
},
|
|
19443
|
-
release(lock, token) {
|
|
19444
|
-
if (lockToken(lock) !== token)
|
|
19445
|
-
return false;
|
|
19446
|
-
const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
|
|
19447
|
-
try {
|
|
19448
|
-
fs12.renameSync(lock, tomb);
|
|
19449
|
-
} catch {
|
|
19450
|
-
return false;
|
|
19451
|
-
}
|
|
19452
|
-
if (lockToken(tomb) !== token) {
|
|
19453
|
-
try {
|
|
19454
|
-
fs12.renameSync(tomb, lock);
|
|
19455
|
-
} catch {}
|
|
19456
|
-
return false;
|
|
19457
|
-
}
|
|
19458
|
-
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
19459
|
-
return true;
|
|
19460
|
-
},
|
|
19461
|
-
token: lockToken,
|
|
19462
|
-
reap(lock, observed) {
|
|
19463
|
-
const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
|
|
19464
|
-
try {
|
|
19465
|
-
fs12.renameSync(lock, tomb);
|
|
19466
|
-
} catch {
|
|
19467
|
-
return false;
|
|
19468
|
-
}
|
|
19469
|
-
if (lockToken(tomb) === observed) {
|
|
19470
|
-
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
19471
|
-
return true;
|
|
19472
|
-
}
|
|
19473
|
-
try {
|
|
19474
|
-
fs12.renameSync(tomb, lock);
|
|
19475
|
-
} catch {
|
|
19476
|
-
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
19356
|
+
}
|
|
19357
|
+
function rulesOf(dir, text) {
|
|
19358
|
+
const out = [];
|
|
19359
|
+
for (const raw of text.split(`
|
|
19360
|
+
`)) {
|
|
19361
|
+
const line = raw.trim();
|
|
19362
|
+
if (!line || line.startsWith("#") || line.startsWith("[attr]"))
|
|
19363
|
+
continue;
|
|
19364
|
+
const [pattern, ...attributes] = line.split(/\s+/u);
|
|
19365
|
+
let generated = null;
|
|
19366
|
+
for (const attribute of attributes) {
|
|
19367
|
+
if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
|
|
19368
|
+
generated = true;
|
|
19369
|
+
else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
|
|
19370
|
+
generated = false;
|
|
19477
19371
|
}
|
|
19478
|
-
|
|
19479
|
-
}
|
|
19480
|
-
};
|
|
19481
|
-
function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
|
|
19482
|
-
const lock = `${file}.lock`;
|
|
19483
|
-
const deadline = Date.now() + waitMs;
|
|
19484
|
-
let token = null;
|
|
19485
|
-
for (;; ) {
|
|
19486
|
-
token = ledgerLock.acquire(lock);
|
|
19487
|
-
if (token)
|
|
19488
|
-
break;
|
|
19489
|
-
const observed = lockToken(lock);
|
|
19490
|
-
const age = lockAgeMs(lock);
|
|
19491
|
-
if (age !== null && age > LOCK_STALE_MS && ledgerLock.reap(lock, observed))
|
|
19372
|
+
if (generated === null)
|
|
19492
19373
|
continue;
|
|
19493
|
-
|
|
19494
|
-
|
|
19495
|
-
|
|
19374
|
+
const regex = patternRegex(dir, pattern);
|
|
19375
|
+
if (regex)
|
|
19376
|
+
out.push({ regex, generated });
|
|
19496
19377
|
}
|
|
19497
|
-
|
|
19498
|
-
|
|
19499
|
-
|
|
19500
|
-
|
|
19378
|
+
return out;
|
|
19379
|
+
}
|
|
19380
|
+
function generatedByAttributes(files, paths) {
|
|
19381
|
+
const rules = [...files].map(([file, text]) => {
|
|
19382
|
+
const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "";
|
|
19383
|
+
return {
|
|
19384
|
+
depth: dir ? dir.split("/").length : 0,
|
|
19385
|
+
rules: rulesOf(dir, text)
|
|
19386
|
+
};
|
|
19387
|
+
}).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
|
|
19388
|
+
const out = new Set;
|
|
19389
|
+
for (const file of paths) {
|
|
19390
|
+
const last = rules.findLast((rule) => rule.regex.test(file));
|
|
19391
|
+
if (last?.generated)
|
|
19392
|
+
out.add(file);
|
|
19501
19393
|
}
|
|
19394
|
+
return out;
|
|
19502
19395
|
}
|
|
19503
|
-
|
|
19504
|
-
|
|
19505
|
-
|
|
19506
|
-
var
|
|
19507
|
-
var
|
|
19508
|
-
|
|
19509
|
-
|
|
19510
|
-
|
|
19511
|
-
|
|
19512
|
-
|
|
19513
|
-
|
|
19514
|
-
|
|
19515
|
-
|
|
19396
|
+
|
|
19397
|
+
// packages/workit-core/src/core/policy/substance.ts
|
|
19398
|
+
var SLASH_BLOCK = [["/*", "*/"]];
|
|
19399
|
+
var C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
|
|
19400
|
+
var SYNTAXES = Object.fromEntries([
|
|
19401
|
+
...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
|
|
19402
|
+
...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
|
|
19403
|
+
ext,
|
|
19404
|
+
{ ...C_LIKE, triple: true }
|
|
19405
|
+
]),
|
|
19406
|
+
["go", { ...C_LIKE, rawBacktick: true }],
|
|
19407
|
+
...["css", "scss", "less"].map((ext) => [
|
|
19408
|
+
ext,
|
|
19409
|
+
{ line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
|
|
19410
|
+
]),
|
|
19411
|
+
...["py", "pyi"].map((ext) => [
|
|
19412
|
+
ext,
|
|
19413
|
+
{ line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
|
|
19414
|
+
]),
|
|
19415
|
+
["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
|
|
19416
|
+
...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
|
|
19417
|
+
ext,
|
|
19418
|
+
{ line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
|
|
19419
|
+
])
|
|
19420
|
+
]);
|
|
19421
|
+
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;
|
|
19422
|
+
var extensionOf = (file) => {
|
|
19423
|
+
const name = file.slice(file.lastIndexOf("/") + 1);
|
|
19424
|
+
const dot = name.lastIndexOf(".");
|
|
19425
|
+
return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
|
|
19426
|
+
};
|
|
19427
|
+
var CI_PATH = /^\.github\/|(?:^|\/)(?:\.gitlab-ci\.ya?ml|Jenkinsfile|azure-pipelines\.ya?ml|\.pre-commit-config\.ya?ml|\.circleci\/.*)$/u;
|
|
19428
|
+
var syntaxOf = (file) => CI_PATH.test(file) ? null : SYNTAXES[extensionOf(file)] ?? null;
|
|
19429
|
+
var SPACE = /[ \t\r\f\v]/u;
|
|
19430
|
+
var WORD = /[\w$]/u;
|
|
19431
|
+
var NO_SPACE_AFTER = "([{,;";
|
|
19432
|
+
var NO_SPACE_BEFORE = ")]},;";
|
|
19433
|
+
var REGEX_AFTER = "(,=:[!&|?{};+-*%<>~^";
|
|
19434
|
+
var REGEX_KEYWORDS = new Set([
|
|
19435
|
+
"return",
|
|
19436
|
+
"typeof",
|
|
19437
|
+
"instanceof",
|
|
19438
|
+
"case",
|
|
19439
|
+
"do",
|
|
19440
|
+
"else",
|
|
19441
|
+
"in",
|
|
19442
|
+
"of",
|
|
19443
|
+
"new",
|
|
19444
|
+
"delete",
|
|
19445
|
+
"void",
|
|
19446
|
+
"throw",
|
|
19447
|
+
"yield",
|
|
19448
|
+
"await"
|
|
19449
|
+
]);
|
|
19450
|
+
function substanceOf(file, text) {
|
|
19451
|
+
const syntax = syntaxOf(file);
|
|
19452
|
+
if (!syntax)
|
|
19453
|
+
return plainSubstance(text);
|
|
19454
|
+
const markup = syntax.block.some(([open]) => open === "<!--");
|
|
19455
|
+
if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
|
|
19456
|
+
return plainSubstance(text);
|
|
19457
|
+
return lex(syntax, text);
|
|
19458
|
+
}
|
|
19459
|
+
function plainSubstance(text) {
|
|
19460
|
+
return text.split(`
|
|
19461
|
+
`).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
|
|
19462
|
+
`);
|
|
19463
|
+
}
|
|
19464
|
+
function lex(syntax, text) {
|
|
19465
|
+
const collapse = !syntax.indented;
|
|
19466
|
+
let out = "";
|
|
19467
|
+
let lineStart = 0;
|
|
19468
|
+
let kept = 0;
|
|
19469
|
+
let pendingSpace = false;
|
|
19470
|
+
const frames = [{ kind: "code", braces: 0 }];
|
|
19471
|
+
let index = 0;
|
|
19472
|
+
if (text.startsWith("#!")) {
|
|
19473
|
+
const end = text.indexOf(`
|
|
19474
|
+
`);
|
|
19475
|
+
out = end < 0 ? text : text.slice(0, end);
|
|
19476
|
+
kept = out.length;
|
|
19477
|
+
index = end < 0 ? text.length : end;
|
|
19478
|
+
}
|
|
19479
|
+
const emitCode = (chunk) => {
|
|
19480
|
+
if (collapse && pendingSpace && out.length > lineStart) {
|
|
19481
|
+
const before = out[out.length - 1];
|
|
19482
|
+
if (!NO_SPACE_AFTER.includes(before) && !NO_SPACE_BEFORE.includes(chunk[0]))
|
|
19483
|
+
out += " ";
|
|
19516
19484
|
}
|
|
19517
|
-
|
|
19518
|
-
|
|
19519
|
-
|
|
19520
|
-
|
|
19521
|
-
|
|
19522
|
-
|
|
19523
|
-
|
|
19524
|
-
|
|
19525
|
-
|
|
19526
|
-
|
|
19527
|
-
|
|
19528
|
-
|
|
19529
|
-
|
|
19530
|
-
break;
|
|
19531
|
-
}
|
|
19532
|
-
stop = start;
|
|
19485
|
+
pendingSpace = false;
|
|
19486
|
+
out += chunk;
|
|
19487
|
+
};
|
|
19488
|
+
const emitVerbatim = (chunk) => {
|
|
19489
|
+
emitCode(chunk);
|
|
19490
|
+
kept = out.length;
|
|
19491
|
+
};
|
|
19492
|
+
const newline = () => {
|
|
19493
|
+
if (!collapse) {
|
|
19494
|
+
let end = out.length;
|
|
19495
|
+
while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
|
|
19496
|
+
end -= 1;
|
|
19497
|
+
out = out.slice(0, end);
|
|
19533
19498
|
}
|
|
19534
|
-
|
|
19535
|
-
|
|
19536
|
-
|
|
19537
|
-
return null;
|
|
19538
|
-
};
|
|
19539
|
-
var tighten = (fd) => {
|
|
19540
|
-
try {
|
|
19541
|
-
if ((fs12.fstatSync(fd).mode & 63) !== 0)
|
|
19542
|
-
fs12.fchmodSync(fd, 384);
|
|
19543
|
-
} catch {}
|
|
19544
|
-
};
|
|
19545
|
-
function appendLine(file, build, options) {
|
|
19546
|
-
const write = () => {
|
|
19547
|
-
const fd = fs12.openSync(file, "a+", 384);
|
|
19548
|
-
try {
|
|
19549
|
-
if (process.platform !== "win32")
|
|
19550
|
-
tighten(fd);
|
|
19551
|
-
const size = fs12.fstatSync(fd).size;
|
|
19552
|
-
let fence = "";
|
|
19553
|
-
if (size > 0) {
|
|
19554
|
-
const last = Buffer.alloc(1);
|
|
19555
|
-
fs12.readSync(fd, last, 0, 1, size - 1);
|
|
19556
|
-
if (last[0] !== 10)
|
|
19557
|
-
fence = `
|
|
19499
|
+
pendingSpace = false;
|
|
19500
|
+
if (out.length > lineStart) {
|
|
19501
|
+
out += `
|
|
19558
19502
|
`;
|
|
19559
|
-
|
|
19560
|
-
const line = build(lastLine(fd, size));
|
|
19561
|
-
if (!line.ok)
|
|
19562
|
-
return line;
|
|
19563
|
-
const bytes = Buffer.from(fence + line.value);
|
|
19564
|
-
const written = fs12.writeSync(fd, bytes);
|
|
19565
|
-
if (written !== bytes.length)
|
|
19566
|
-
return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
|
|
19567
|
-
return { ok: true, value: undefined };
|
|
19568
|
-
} finally {
|
|
19569
|
-
fs12.closeSync(fd);
|
|
19503
|
+
lineStart = out.length;
|
|
19570
19504
|
}
|
|
19571
19505
|
};
|
|
19572
|
-
|
|
19573
|
-
|
|
19574
|
-
|
|
19575
|
-
|
|
19576
|
-
|
|
19577
|
-
|
|
19578
|
-
|
|
19579
|
-
|
|
19580
|
-
|
|
19581
|
-
|
|
19582
|
-
var sealed = (row, id, at, previous) => {
|
|
19583
|
-
const { prevHash: _prev, rowHash: _row, ...fields } = row;
|
|
19584
|
-
const prevHash = previous === null ? null : sha2562(previous);
|
|
19585
|
-
const body = { v: LEDGER_VERSION, id, at, ...fields, prevHash };
|
|
19586
|
-
const full = { ...body, rowHash: chainHash(prevHash, JSON.stringify(body)) };
|
|
19587
|
-
return { full, line: `${JSON.stringify(full)}
|
|
19588
|
-
` };
|
|
19589
|
-
};
|
|
19590
|
-
function appendRaw(cwd, row, options) {
|
|
19591
|
-
const file = ledgerPath(cwd);
|
|
19592
|
-
if (!file.ok)
|
|
19593
|
-
return file;
|
|
19594
|
-
const id = newId2();
|
|
19595
|
-
const at = (options.now ?? new Date).toISOString();
|
|
19596
|
-
let full = null;
|
|
19597
|
-
const written = appendLine(file.value, (previous) => {
|
|
19598
|
-
const next = sealed(row, id, at, previous);
|
|
19599
|
-
const bytes = Buffer.byteLength(next.line);
|
|
19600
|
-
if (bytes > MAX_LINE_BYTES)
|
|
19601
|
-
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>`);
|
|
19602
|
-
full = next.full;
|
|
19603
|
-
return { ok: true, value: next.line };
|
|
19604
|
-
}, options);
|
|
19605
|
-
return written.ok ? { ok: true, value: full } : written;
|
|
19606
|
-
}
|
|
19607
|
-
function observedRowBytes(row) {
|
|
19608
|
-
const sample = sealed({ ...row, observer: "workit_cli" }, newId2(), new Date().toISOString(), "");
|
|
19609
|
-
return Buffer.byteLength(sample.line);
|
|
19610
|
-
}
|
|
19611
|
-
function appendObserved(cwd, row, options = {}) {
|
|
19612
|
-
return appendRaw(cwd, { ...row, observer: "workit_cli" }, options);
|
|
19613
|
-
}
|
|
19614
|
-
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19615
|
-
var str2 = (value) => typeof value === "string" ? value : null;
|
|
19616
|
-
var normalizeActor = (value) => {
|
|
19617
|
-
const actor = isRecord3(value) ? value : {};
|
|
19618
|
-
return {
|
|
19619
|
-
host: str2(actor.host) ?? "unknown",
|
|
19620
|
-
session: str2(actor.session),
|
|
19621
|
-
agentId: str2(actor.agentId)
|
|
19506
|
+
const regexAllowed = () => {
|
|
19507
|
+
let end = out.length;
|
|
19508
|
+
while (end > 0 && /\s/u.test(out[end - 1]))
|
|
19509
|
+
end -= 1;
|
|
19510
|
+
if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
|
|
19511
|
+
return true;
|
|
19512
|
+
let start = end;
|
|
19513
|
+
while (start > 0 && WORD.test(out[start - 1]))
|
|
19514
|
+
start -= 1;
|
|
19515
|
+
return REGEX_KEYWORDS.has(out.slice(start, end));
|
|
19622
19516
|
};
|
|
19623
|
-
|
|
19624
|
-
|
|
19625
|
-
|
|
19626
|
-
|
|
19627
|
-
|
|
19628
|
-
|
|
19629
|
-
|
|
19630
|
-
|
|
19631
|
-
|
|
19632
|
-
|
|
19633
|
-
|
|
19634
|
-
|
|
19635
|
-
|
|
19636
|
-
|
|
19637
|
-
|
|
19638
|
-
|
|
19639
|
-
|
|
19640
|
-
|
|
19641
|
-
|
|
19642
|
-
|
|
19643
|
-
|
|
19644
|
-
|
|
19645
|
-
|
|
19646
|
-
}
|
|
19647
|
-
|
|
19648
|
-
|
|
19649
|
-
|
|
19650
|
-
|
|
19651
|
-
|
|
19652
|
-
|
|
19653
|
-
|
|
19654
|
-
|
|
19655
|
-
|
|
19656
|
-
|
|
19657
|
-
|
|
19658
|
-
|
|
19659
|
-
|
|
19660
|
-
|
|
19661
|
-
|
|
19662
|
-
|
|
19663
|
-
|
|
19664
|
-
|
|
19665
|
-
|
|
19666
|
-
|
|
19667
|
-
const trimmed = raw.trim();
|
|
19668
|
-
if (!trimmed)
|
|
19517
|
+
const scanString = (quote, escapes, multiline) => {
|
|
19518
|
+
const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
|
|
19519
|
+
const fstring = syntax.fstring === true && /f/iu.test(prefix);
|
|
19520
|
+
let end = index + quote.length;
|
|
19521
|
+
while (end < text.length) {
|
|
19522
|
+
const char = text[end];
|
|
19523
|
+
if (escapes && char === "\\") {
|
|
19524
|
+
end += 2;
|
|
19525
|
+
continue;
|
|
19526
|
+
}
|
|
19527
|
+
if (text.startsWith(quote, end)) {
|
|
19528
|
+
if (fstring && unbalanced(text.slice(index + quote.length, end)))
|
|
19529
|
+
return false;
|
|
19530
|
+
emitVerbatim(text.slice(index, end + quote.length));
|
|
19531
|
+
index = end + quote.length;
|
|
19532
|
+
return true;
|
|
19533
|
+
}
|
|
19534
|
+
if (char === `
|
|
19535
|
+
` && !multiline)
|
|
19536
|
+
return false;
|
|
19537
|
+
end += 1;
|
|
19538
|
+
}
|
|
19539
|
+
return false;
|
|
19540
|
+
};
|
|
19541
|
+
while (index < text.length) {
|
|
19542
|
+
const frame = frames[frames.length - 1];
|
|
19543
|
+
const char = text[index];
|
|
19544
|
+
if (frame.kind === "template") {
|
|
19545
|
+
if (char === "\\") {
|
|
19546
|
+
emitVerbatim(text.slice(index, index + 2));
|
|
19547
|
+
index += 2;
|
|
19548
|
+
} else if (char === "`") {
|
|
19549
|
+
emitVerbatim(char);
|
|
19550
|
+
frames.pop();
|
|
19551
|
+
index += 1;
|
|
19552
|
+
} else if (text.startsWith("${", index)) {
|
|
19553
|
+
emitVerbatim("${");
|
|
19554
|
+
frames.push({ kind: "code", braces: 0 });
|
|
19555
|
+
index += 2;
|
|
19556
|
+
} else {
|
|
19557
|
+
out += char;
|
|
19558
|
+
kept = out.length;
|
|
19559
|
+
index += 1;
|
|
19560
|
+
}
|
|
19669
19561
|
continue;
|
|
19670
|
-
|
|
19671
|
-
if (
|
|
19672
|
-
|
|
19673
|
-
|
|
19674
|
-
|
|
19675
|
-
parsed = JSON.parse(trimmed);
|
|
19676
|
-
} catch {
|
|
19677
|
-
skipped += 1;
|
|
19562
|
+
}
|
|
19563
|
+
if (char === `
|
|
19564
|
+
`) {
|
|
19565
|
+
newline();
|
|
19566
|
+
index += 1;
|
|
19678
19567
|
continue;
|
|
19679
19568
|
}
|
|
19680
|
-
if (
|
|
19681
|
-
|
|
19569
|
+
if (SPACE.test(char)) {
|
|
19570
|
+
if (collapse)
|
|
19571
|
+
pendingSpace = true;
|
|
19572
|
+
else
|
|
19573
|
+
out += char;
|
|
19574
|
+
index += 1;
|
|
19682
19575
|
continue;
|
|
19683
19576
|
}
|
|
19684
|
-
|
|
19685
|
-
|
|
19686
|
-
|
|
19687
|
-
|
|
19688
|
-
const
|
|
19689
|
-
|
|
19690
|
-
|
|
19691
|
-
|
|
19692
|
-
|
|
19693
|
-
|
|
19694
|
-
} else if (chainStarted)
|
|
19695
|
-
reason = "unsigned";
|
|
19696
|
-
else
|
|
19697
|
-
rowIntegrity = "legacy";
|
|
19698
|
-
if (options.integrity && !reason && ids.has(parsed.id))
|
|
19699
|
-
reason = "duplicate_id";
|
|
19700
|
-
if (options.integrity)
|
|
19701
|
-
ids.add(parsed.id);
|
|
19702
|
-
if (reason)
|
|
19703
|
-
rowIntegrity = "unverified";
|
|
19704
|
-
const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
|
|
19705
|
-
const row = {
|
|
19706
|
-
...parsed,
|
|
19707
|
-
v: typeof parsed.v === "number" ? parsed.v : LEDGER_VERSION,
|
|
19708
|
-
id: parsed.id,
|
|
19709
|
-
at: parsed.at,
|
|
19710
|
-
type: parsed.type,
|
|
19711
|
-
actor: normalizeActor(parsed.actor),
|
|
19712
|
-
branch: str2(parsed.branch),
|
|
19713
|
-
head: str2(parsed.head),
|
|
19714
|
-
base: str2(parsed.base),
|
|
19715
|
-
baseSha: str2(parsed.baseSha),
|
|
19716
|
-
patchId: str2(parsed.patchId),
|
|
19717
|
-
diffHash: str2(parsed.diffHash),
|
|
19718
|
-
tree: str2(parsed.tree),
|
|
19719
|
-
dirty: typeof parsed.dirty === "boolean" ? parsed.dirty : null,
|
|
19720
|
-
...pr === undefined ? {} : { pr },
|
|
19721
|
-
...str2(parsed.supersedes) ? { supersedes: parsed.supersedes } : {},
|
|
19722
|
-
seq: rows.length + 1,
|
|
19723
|
-
superseded: false,
|
|
19724
|
-
supersedeIgnored: false,
|
|
19725
|
-
...rowIntegrity ? { integrity: rowIntegrity } : {},
|
|
19726
|
-
...reason ? { integrityReason: reason } : {}
|
|
19727
|
-
};
|
|
19728
|
-
rows.push(row);
|
|
19729
|
-
if (!options.integrity)
|
|
19577
|
+
const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
|
|
19578
|
+
if (lineOpener) {
|
|
19579
|
+
const end = text.indexOf(`
|
|
19580
|
+
`, index);
|
|
19581
|
+
const comment = text.slice(index, end < 0 ? text.length : end);
|
|
19582
|
+
if (DIRECTIVE.test(comment.slice(lineOpener.length)))
|
|
19583
|
+
emitVerbatim(comment);
|
|
19584
|
+
else
|
|
19585
|
+
pendingSpace = true;
|
|
19586
|
+
index = end < 0 ? text.length : end;
|
|
19730
19587
|
continue;
|
|
19731
|
-
|
|
19732
|
-
|
|
19733
|
-
|
|
19734
|
-
|
|
19735
|
-
|
|
19736
|
-
|
|
19737
|
-
|
|
19738
|
-
|
|
19739
|
-
|
|
19740
|
-
|
|
19741
|
-
|
|
19742
|
-
|
|
19588
|
+
}
|
|
19589
|
+
const block = syntax.block.find(([open]) => text.startsWith(open, index));
|
|
19590
|
+
if (block) {
|
|
19591
|
+
const end = text.indexOf(block[1], index + block[0].length);
|
|
19592
|
+
if (end < 0)
|
|
19593
|
+
return null;
|
|
19594
|
+
const comment = text.slice(index, end + block[1].length);
|
|
19595
|
+
if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
|
|
19596
|
+
emitVerbatim(comment);
|
|
19597
|
+
else
|
|
19598
|
+
pendingSpace = true;
|
|
19599
|
+
if (comment.includes(`
|
|
19600
|
+
`))
|
|
19601
|
+
newline();
|
|
19602
|
+
index = end + block[1].length;
|
|
19603
|
+
continue;
|
|
19604
|
+
}
|
|
19605
|
+
if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
|
|
19606
|
+
if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
|
|
19607
|
+
return null;
|
|
19608
|
+
continue;
|
|
19609
|
+
}
|
|
19610
|
+
if (syntax.quotes.includes(char)) {
|
|
19611
|
+
if (!scanString(char, true, Boolean(syntax.multiline)))
|
|
19612
|
+
return null;
|
|
19613
|
+
continue;
|
|
19614
|
+
}
|
|
19615
|
+
if (syntax.literal?.includes(char)) {
|
|
19616
|
+
if (!scanString(char, false, false))
|
|
19617
|
+
return null;
|
|
19618
|
+
continue;
|
|
19619
|
+
}
|
|
19620
|
+
if (syntax.rawBacktick && char === "`") {
|
|
19621
|
+
if (!scanString("`", false, true))
|
|
19622
|
+
return null;
|
|
19623
|
+
continue;
|
|
19624
|
+
}
|
|
19625
|
+
if (syntax.js && char === "`") {
|
|
19626
|
+
emitVerbatim(char);
|
|
19627
|
+
frames.push({ kind: "template" });
|
|
19628
|
+
index += 1;
|
|
19629
|
+
continue;
|
|
19630
|
+
}
|
|
19631
|
+
if (syntax.cssUrl && /^url\(\s*[^"')\s]/iu.test(text.slice(index, index + 64)))
|
|
19632
|
+
return null;
|
|
19633
|
+
if (syntax.js && char === "/" && !regexAllowed() && out.trimEnd().endsWith(")")) {
|
|
19634
|
+
const lineEnd = text.indexOf(`
|
|
19635
|
+
`, index);
|
|
19636
|
+
if (text.slice(index + 1, lineEnd < 0 ? text.length : lineEnd).includes("/"))
|
|
19637
|
+
return null;
|
|
19638
|
+
}
|
|
19639
|
+
if (syntax.js && char === "/" && regexAllowed()) {
|
|
19640
|
+
let end = index + 1;
|
|
19641
|
+
let inClass = false;
|
|
19642
|
+
while (end < text.length && (inClass || text[end] !== "/")) {
|
|
19643
|
+
if (text[end] === `
|
|
19644
|
+
`)
|
|
19645
|
+
return null;
|
|
19646
|
+
if (text[end] === "\\")
|
|
19647
|
+
end += 1;
|
|
19648
|
+
else if (text[end] === "[")
|
|
19649
|
+
inClass = true;
|
|
19650
|
+
else if (text[end] === "]")
|
|
19651
|
+
inClass = false;
|
|
19652
|
+
end += 1;
|
|
19653
|
+
}
|
|
19654
|
+
if (end >= text.length)
|
|
19655
|
+
return null;
|
|
19656
|
+
emitVerbatim(text.slice(index, end + 1));
|
|
19657
|
+
index = end + 1;
|
|
19658
|
+
continue;
|
|
19659
|
+
}
|
|
19660
|
+
if (frames.length > 1 && frame.kind === "code") {
|
|
19661
|
+
if (char === "{")
|
|
19662
|
+
frame.braces += 1;
|
|
19663
|
+
else if (char === "}") {
|
|
19664
|
+
if (frame.braces === 0) {
|
|
19665
|
+
emitVerbatim(char);
|
|
19666
|
+
frames.pop();
|
|
19667
|
+
index += 1;
|
|
19668
|
+
continue;
|
|
19669
|
+
}
|
|
19670
|
+
frame.braces -= 1;
|
|
19671
|
+
}
|
|
19672
|
+
}
|
|
19673
|
+
emitCode(char);
|
|
19674
|
+
index += 1;
|
|
19743
19675
|
}
|
|
19744
|
-
|
|
19745
|
-
|
|
19746
|
-
|
|
19747
|
-
|
|
19748
|
-
|
|
19749
|
-
|
|
19750
|
-
|
|
19751
|
-
const
|
|
19752
|
-
|
|
19753
|
-
|
|
19754
|
-
if (
|
|
19755
|
-
|
|
19756
|
-
else if (row.supersedes)
|
|
19757
|
-
row.supersedeIgnored = true;
|
|
19758
|
-
if (!byId.has(row.id))
|
|
19759
|
-
byId.set(row.id, row);
|
|
19676
|
+
if (frames.length !== 1)
|
|
19677
|
+
return null;
|
|
19678
|
+
newline();
|
|
19679
|
+
return out;
|
|
19680
|
+
}
|
|
19681
|
+
function unbalanced(body) {
|
|
19682
|
+
let depth = 0;
|
|
19683
|
+
for (const part of body.replaceAll("{{", "").replaceAll("}}", "")) {
|
|
19684
|
+
if (part === "{")
|
|
19685
|
+
depth += 1;
|
|
19686
|
+
else if (part === "}")
|
|
19687
|
+
depth = Math.max(0, depth - 1);
|
|
19760
19688
|
}
|
|
19761
|
-
|
|
19762
|
-
tamperCache.set(file.value, {
|
|
19763
|
-
size: stat.size,
|
|
19764
|
-
stamp: fileStamp(stat),
|
|
19765
|
-
breaks: integrity.unverified.filter((row) => TAMPER_REASONS.has(row.reason))
|
|
19766
|
-
});
|
|
19767
|
-
return {
|
|
19768
|
-
ok: true,
|
|
19769
|
-
value: { path: file.value, rows, skipped, integrity: options.integrity ? integrity : null }
|
|
19770
|
-
};
|
|
19689
|
+
return depth > 0;
|
|
19771
19690
|
}
|
|
19772
|
-
|
|
19773
|
-
|
|
19774
|
-
|
|
19775
|
-
|
|
19776
|
-
|
|
19777
|
-
if (
|
|
19778
|
-
return
|
|
19691
|
+
function hasOpaqueLineBreak(text) {
|
|
19692
|
+
return /\r(?!\n)|[\u2028\u2029]/u.test(text);
|
|
19693
|
+
}
|
|
19694
|
+
var decoder = new TextDecoder("utf-8", { fatal: true });
|
|
19695
|
+
function textOf(bytes) {
|
|
19696
|
+
if (bytes.includes(0))
|
|
19697
|
+
return null;
|
|
19779
19698
|
try {
|
|
19780
|
-
|
|
19781
|
-
const cached = tamperCache.get(file.value);
|
|
19782
|
-
if (cached && cached.size === stat.size && cached.stamp === fileStamp(stat))
|
|
19783
|
-
return cached.breaks;
|
|
19699
|
+
return decoder.decode(bytes);
|
|
19784
19700
|
} catch {
|
|
19785
|
-
return
|
|
19701
|
+
return null;
|
|
19786
19702
|
}
|
|
19787
|
-
const read = readLedger(cwd, { integrity: true });
|
|
19788
|
-
return read.ok ? (read.value.integrity?.unverified ?? []).filter((row) => TAMPER_REASONS.has(row.reason)) : [];
|
|
19789
|
-
}
|
|
19790
|
-
function tamperedUnblock(cwd) {
|
|
19791
|
-
const file = ledgerPath(cwd);
|
|
19792
|
-
const where = file.ok ? file.value : "the ledger file";
|
|
19793
|
-
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`;
|
|
19794
19703
|
}
|
|
19795
|
-
|
|
19796
|
-
|
|
19797
|
-
|
|
19798
|
-
|
|
19799
|
-
|
|
19800
|
-
|
|
19801
|
-
|
|
19704
|
+
function cosmeticChange(file, before, after) {
|
|
19705
|
+
const left = textOf(before);
|
|
19706
|
+
const right = textOf(after);
|
|
19707
|
+
if (left === null || right === null)
|
|
19708
|
+
return false;
|
|
19709
|
+
if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
|
|
19710
|
+
return false;
|
|
19711
|
+
const a = substanceOf(file, left);
|
|
19712
|
+
return a !== null && a === substanceOf(file, right);
|
|
19802
19713
|
}
|
|
19803
|
-
|
|
19804
|
-
|
|
19805
|
-
|
|
19806
|
-
|
|
19807
|
-
|
|
19808
|
-
|
|
19809
|
-
|
|
19810
|
-
|
|
19811
|
-
|
|
19812
|
-
|
|
19714
|
+
|
|
19715
|
+
// packages/workit-core/src/core/policy/lane.ts
|
|
19716
|
+
var LANE_THRESHOLDS = {
|
|
19717
|
+
selfMaxLines: 100,
|
|
19718
|
+
selfMaxFiles: 5,
|
|
19719
|
+
fullMinLines: 500
|
|
19720
|
+
};
|
|
19721
|
+
var RISK_TOKENS = new Set([
|
|
19722
|
+
"auth",
|
|
19723
|
+
"authn",
|
|
19724
|
+
"authz",
|
|
19725
|
+
"oauth",
|
|
19726
|
+
"security",
|
|
19727
|
+
"permission",
|
|
19728
|
+
"permissions",
|
|
19729
|
+
"secret",
|
|
19730
|
+
"secrets",
|
|
19731
|
+
"crypto",
|
|
19732
|
+
"gate",
|
|
19733
|
+
"gates",
|
|
19734
|
+
"policy",
|
|
19735
|
+
"policies",
|
|
19736
|
+
"hook",
|
|
19737
|
+
"hooks",
|
|
19738
|
+
"husky",
|
|
19739
|
+
"lefthook",
|
|
19740
|
+
"release",
|
|
19741
|
+
"releases",
|
|
19742
|
+
"migration",
|
|
19743
|
+
"migrations",
|
|
19744
|
+
"migrate",
|
|
19745
|
+
"workflows"
|
|
19746
|
+
]);
|
|
19747
|
+
var RISK_FILES = new Set([
|
|
19748
|
+
".gitlab-ci.yml",
|
|
19749
|
+
"Jenkinsfile",
|
|
19750
|
+
"azure-pipelines.yml",
|
|
19751
|
+
".pre-commit-config.yaml",
|
|
19752
|
+
".workit/risk-paths"
|
|
19753
|
+
]);
|
|
19754
|
+
var RISK_NAMES = new Set([".gitattributes"]);
|
|
19755
|
+
var RISK_PATHS_FILE = ".workit/risk-paths";
|
|
19756
|
+
var LOCKFILES = new Set([
|
|
19757
|
+
"bun.lock",
|
|
19758
|
+
"bun.lockb",
|
|
19759
|
+
"package-lock.json",
|
|
19760
|
+
"npm-shrinkwrap.json",
|
|
19761
|
+
"yarn.lock",
|
|
19762
|
+
"pnpm-lock.yaml",
|
|
19763
|
+
"Cargo.lock",
|
|
19764
|
+
"go.sum",
|
|
19765
|
+
"poetry.lock",
|
|
19766
|
+
"uv.lock",
|
|
19767
|
+
"Pipfile.lock",
|
|
19768
|
+
"Gemfile.lock",
|
|
19769
|
+
"composer.lock"
|
|
19770
|
+
]);
|
|
19771
|
+
var DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc)$/iu;
|
|
19772
|
+
var DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
|
|
19773
|
+
var INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
|
|
19774
|
+
var TEST_PATH = /(?:^|\/)(?:test|tests|__tests__)\/|\.(?:test|spec)\.[^/]+$|_test\.[^/]+$/iu;
|
|
19775
|
+
var basename = (file) => file.slice(file.lastIndexOf("/") + 1);
|
|
19776
|
+
var isDoc = (file) => !INSTRUCTIONS.test(file) && (DOC_EXT.test(file) || DOC_NAMES.test(basename(file)));
|
|
19777
|
+
function kindOf(file) {
|
|
19778
|
+
if (file.binary)
|
|
19779
|
+
return "behavior";
|
|
19780
|
+
if (file.generated)
|
|
19781
|
+
return "generated";
|
|
19782
|
+
if (LOCKFILES.has(basename(file.path)))
|
|
19783
|
+
return "lockfile";
|
|
19784
|
+
if (isDoc(file.path))
|
|
19785
|
+
return "doc";
|
|
19786
|
+
if (file.cosmetic)
|
|
19787
|
+
return "cosmetic";
|
|
19788
|
+
return TEST_PATH.test(file.path) ? "test" : "behavior";
|
|
19813
19789
|
}
|
|
19814
|
-
var
|
|
19815
|
-
function
|
|
19816
|
-
|
|
19817
|
-
|
|
19818
|
-
|
|
19819
|
-
|
|
19820
|
-
|
|
19821
|
-
"
|
|
19822
|
-
|
|
19823
|
-
|
|
19824
|
-
|
|
19825
|
-
|
|
19826
|
-
|
|
19827
|
-
|
|
19828
|
-
|
|
19829
|
-
|
|
19830
|
-
const seconds = Number(/@\{(\d+)\}$/u.exec(selector)?.[1]);
|
|
19831
|
-
if (!Number.isFinite(seconds))
|
|
19832
|
-
continue;
|
|
19833
|
-
since = seconds * 1000;
|
|
19834
|
-
const match = RENAMED.exec(subject);
|
|
19835
|
-
if (!match || match[1] === branch)
|
|
19836
|
-
continue;
|
|
19837
|
-
const until = (seconds + 1) * 1000;
|
|
19838
|
-
renamedAt.set(match[1], Math.max(renamedAt.get(match[1]) ?? 0, until));
|
|
19790
|
+
var globCache = new Map;
|
|
19791
|
+
function globMatch(pattern, file) {
|
|
19792
|
+
let regex = globCache.get(pattern);
|
|
19793
|
+
if (!regex) {
|
|
19794
|
+
let source = "";
|
|
19795
|
+
for (let index = 0;index < pattern.length; index += 1) {
|
|
19796
|
+
const char = pattern[index];
|
|
19797
|
+
if (char === "*" && pattern[index + 1] === "*") {
|
|
19798
|
+
source += pattern[index + 2] === "/" ? "(?:[^/]*/)*" : ".*";
|
|
19799
|
+
index += pattern[index + 2] === "/" ? 2 : 1;
|
|
19800
|
+
} else if (char === "*")
|
|
19801
|
+
source += "[^/]*";
|
|
19802
|
+
else if (char === "?")
|
|
19803
|
+
source += "[^/]";
|
|
19804
|
+
else
|
|
19805
|
+
source += char.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
|
|
19839
19806
|
}
|
|
19807
|
+
regex = new RegExp(`^${source}$`, "u");
|
|
19808
|
+
globCache.set(pattern, regex);
|
|
19840
19809
|
}
|
|
19841
|
-
return (
|
|
19842
|
-
if (row.branch === branch)
|
|
19843
|
-
return true;
|
|
19844
|
-
const until = row.branch === null ? undefined : renamedAt.get(row.branch);
|
|
19845
|
-
if (until === undefined)
|
|
19846
|
-
return false;
|
|
19847
|
-
const at = Date.parse(row.at);
|
|
19848
|
-
return at >= since && at <= until;
|
|
19849
|
-
};
|
|
19850
|
-
}
|
|
19851
|
-
var renameAwareBranch = (scopeOf, branch) => {
|
|
19852
|
-
return (target) => {
|
|
19853
|
-
const own = typeof branch === "string" ? branch : null;
|
|
19854
|
-
const theirs = typeof target.branch === "string" ? target.branch : null;
|
|
19855
|
-
if (own === theirs)
|
|
19856
|
-
return true;
|
|
19857
|
-
if (own === null || theirs === null || typeof target.at !== "string")
|
|
19858
|
-
return false;
|
|
19859
|
-
return scopeOf(own)({ branch: theirs, at: target.at });
|
|
19860
|
-
};
|
|
19861
|
-
};
|
|
19862
|
-
function authorSessions(cwd, rows, branch, range = { base: null, head: null }, scope = branchScope(cwd, branch)) {
|
|
19863
|
-
const sessions = new Set;
|
|
19864
|
-
for (const row of rows) {
|
|
19865
|
-
if (!scope(row) || row.type !== "commit.recorded" && row.type !== "task.opened")
|
|
19866
|
-
continue;
|
|
19867
|
-
const session = str2(row.session) ?? row.actor.session;
|
|
19868
|
-
if (session)
|
|
19869
|
-
sessions.add(session);
|
|
19870
|
-
}
|
|
19871
|
-
for (const session of trailerSessions(cwd, range.base, range.head))
|
|
19872
|
-
sessions.add(session);
|
|
19873
|
-
return sessions;
|
|
19810
|
+
return regex.test(file) || file.startsWith(`${pattern.replace(/\/$/u, "")}/`);
|
|
19874
19811
|
}
|
|
19875
|
-
|
|
19876
|
-
|
|
19877
|
-
if (authors.has(session))
|
|
19812
|
+
function isRiskPath(file, extra = []) {
|
|
19813
|
+
if (extra.some((pattern) => globMatch(pattern, file)))
|
|
19878
19814
|
return true;
|
|
19879
|
-
|
|
19880
|
-
|
|
19881
|
-
|
|
19882
|
-
|
|
19883
|
-
|
|
19884
|
-
var TRUST_REASONS = new Set([
|
|
19885
|
-
"unsigned",
|
|
19886
|
-
"hash_mismatch",
|
|
19887
|
-
"broken_link",
|
|
19888
|
-
"duplicate_id",
|
|
19889
|
-
"unissued_session",
|
|
19890
|
-
"unbound_session"
|
|
19891
|
-
]);
|
|
19892
|
-
var SHELL_VOUCHED_HOSTS = new Set([
|
|
19893
|
-
"cli",
|
|
19894
|
-
"opencode",
|
|
19895
|
-
"pi",
|
|
19896
|
-
"codex_cli",
|
|
19897
|
-
"codex_desktop"
|
|
19898
|
-
]);
|
|
19899
|
-
function sessionTrust(cwd, row) {
|
|
19900
|
-
const session = row.actor.session;
|
|
19901
|
-
if (!session || row.self === true)
|
|
19902
|
-
return null;
|
|
19903
|
-
const source = typeof row.sessionSource === "string" ? row.sessionSource : "env";
|
|
19904
|
-
const host = row.actor.host;
|
|
19905
|
-
const issuedOnly = source === "flag" ? host !== "cli" : source === "env" && session.includes(":") && !SHELL_VOUCHED_HOSTS.has(host);
|
|
19906
|
-
if (!issuedOnly)
|
|
19907
|
-
return null;
|
|
19908
|
-
const binding = issuedBinding(cwd, session);
|
|
19909
|
-
if (binding === null)
|
|
19910
|
-
return "unissued_session";
|
|
19911
|
-
return binding === "token" && row.sessionBound !== true ? "unbound_session" : null;
|
|
19912
|
-
}
|
|
19913
|
-
function trustReasons(cwd, row) {
|
|
19914
|
-
if (row.integrity === "unverified")
|
|
19915
|
-
return [row.integrityReason ?? "unsigned"];
|
|
19916
|
-
if (row.integrity !== "verified")
|
|
19917
|
-
return ["unsigned"];
|
|
19918
|
-
const session = sessionTrust(cwd, row);
|
|
19919
|
-
return session ? [session] : [];
|
|
19815
|
+
if (RISK_FILES.has(file) || RISK_NAMES.has(basename(file)))
|
|
19816
|
+
return true;
|
|
19817
|
+
if (TEST_PATH.test(file) || isDoc(file))
|
|
19818
|
+
return false;
|
|
19819
|
+
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)));
|
|
19920
19820
|
}
|
|
19921
|
-
|
|
19922
|
-
|
|
19923
|
-
|
|
19924
|
-
const
|
|
19925
|
-
const
|
|
19926
|
-
|
|
19927
|
-
|
|
19928
|
-
|
|
19929
|
-
|
|
19930
|
-
|
|
19821
|
+
var parseRiskPaths = (text) => text.split(`
|
|
19822
|
+
`).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.replace(/^\//u, ""));
|
|
19823
|
+
function computeLane(input) {
|
|
19824
|
+
const riskFiles = input.files.filter((file) => isRiskPath(file.path, input.riskPaths)).map((file) => file.path);
|
|
19825
|
+
const risky = new Set(riskFiles);
|
|
19826
|
+
const kinds = input.files.map((file) => ({
|
|
19827
|
+
file,
|
|
19828
|
+
kind: risky.has(file.path) ? "behavior" : kindOf(file)
|
|
19829
|
+
}));
|
|
19830
|
+
const behavior = kinds.filter((entry) => entry.kind === "behavior" || entry.kind === "test" || entry.kind === "lockfile");
|
|
19831
|
+
const lines = behavior.filter((entry) => entry.kind !== "lockfile").reduce((sum, entry) => sum + entry.file.added + entry.file.deleted, 0);
|
|
19832
|
+
const opaque = behavior.filter((entry) => entry.kind === "lockfile" || entry.file.binary);
|
|
19833
|
+
const result = (lane, reasons) => ({
|
|
19834
|
+
lane,
|
|
19835
|
+
reasons,
|
|
19836
|
+
lines,
|
|
19837
|
+
files: behavior.length,
|
|
19838
|
+
riskFiles
|
|
19931
19839
|
});
|
|
19840
|
+
const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
|
|
19841
|
+
if (input.riskTier === "high")
|
|
19842
|
+
return result("full", ["the task judgment records high risk"]);
|
|
19843
|
+
if (riskFiles.length)
|
|
19844
|
+
return result("full", [
|
|
19845
|
+
`touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
|
|
19846
|
+
]);
|
|
19847
|
+
if (!behavior.length)
|
|
19848
|
+
return result("none", [
|
|
19849
|
+
input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
|
|
19850
|
+
]);
|
|
19851
|
+
if (lines >= fullMinLines)
|
|
19852
|
+
return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
|
|
19853
|
+
if (lines > selfMaxLines)
|
|
19854
|
+
return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
|
|
19855
|
+
if (behavior.length > selfMaxFiles)
|
|
19856
|
+
return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
|
|
19857
|
+
if (opaque.length)
|
|
19858
|
+
return result("focused", [
|
|
19859
|
+
`a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
|
|
19860
|
+
]);
|
|
19861
|
+
return result("self", [
|
|
19862
|
+
`small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
|
|
19863
|
+
]);
|
|
19932
19864
|
}
|
|
19933
|
-
|
|
19934
|
-
|
|
19935
|
-
|
|
19936
|
-
|
|
19937
|
-
|
|
19938
|
-
|
|
19939
|
-
|
|
19940
|
-
|
|
19941
|
-
|
|
19942
|
-
|
|
19943
|
-
|
|
19944
|
-
|
|
19945
|
-
|
|
19946
|
-
|
|
19947
|
-
if (!effective) {
|
|
19948
|
-
effective = row;
|
|
19949
|
-
continue;
|
|
19950
|
-
}
|
|
19951
|
-
if (untrusted(reasonsOf(row)) && !untrusted(reasonsOf(effective)))
|
|
19952
|
-
continue;
|
|
19953
|
-
const rowIndependent = reasonsOf(row).length === 0;
|
|
19954
|
-
const effIndependent = reasonsOf(effective).length === 0;
|
|
19955
|
-
if (effIndependent && !rowIndependent && isCurrent(effective))
|
|
19956
|
-
continue;
|
|
19957
|
-
if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session && sameCode(effective, row))
|
|
19958
|
-
continue;
|
|
19959
|
-
effective = row;
|
|
19865
|
+
function untrackedStat(cwd, file) {
|
|
19866
|
+
try {
|
|
19867
|
+
const bytes = readFileSync4(path14.join(cwd, file));
|
|
19868
|
+
if (bytes.includes(0))
|
|
19869
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
19870
|
+
const text = bytes.toString("utf8");
|
|
19871
|
+
if (hasOpaqueLineBreak(text))
|
|
19872
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
19873
|
+
const lines = text.split(`
|
|
19874
|
+
`).length - (text.endsWith(`
|
|
19875
|
+
`) ? 1 : 0);
|
|
19876
|
+
return { path: file, added: lines, deleted: 0 };
|
|
19877
|
+
} catch {
|
|
19878
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
19960
19879
|
}
|
|
19961
|
-
return effective;
|
|
19962
19880
|
}
|
|
19963
|
-
|
|
19964
|
-
|
|
19965
|
-
|
|
19966
|
-
const
|
|
19967
|
-
|
|
19968
|
-
|
|
19969
|
-
|
|
19970
|
-
const
|
|
19971
|
-
|
|
19972
|
-
|
|
19973
|
-
|
|
19974
|
-
|
|
19975
|
-
|
|
19976
|
-
|
|
19977
|
-
|
|
19978
|
-
|
|
19979
|
-
|
|
19881
|
+
var REGULAR = new Set(["100644", "100755"]);
|
|
19882
|
+
var ABSENT = /^0+$/u;
|
|
19883
|
+
function cosmeticFiles(cwd, fork, head) {
|
|
19884
|
+
const raw = diffRaw(cwd, fork, head);
|
|
19885
|
+
if (!raw)
|
|
19886
|
+
return null;
|
|
19887
|
+
const candidates = raw.filter((entry) => REGULAR.has(entry.oldMode) && entry.oldMode === entry.newMode && !ABSENT.test(entry.oldBlob) && (head === null || !ABSENT.test(entry.newBlob)));
|
|
19888
|
+
const blobs = readBlobs(cwd, candidates.flatMap((entry) => [entry.oldBlob, entry.newBlob]));
|
|
19889
|
+
if (!blobs)
|
|
19890
|
+
return null;
|
|
19891
|
+
const out = new Set;
|
|
19892
|
+
const opaque = new Set;
|
|
19893
|
+
for (const entry of candidates) {
|
|
19894
|
+
const before = blobs.get(entry.oldBlob);
|
|
19895
|
+
let after = blobs.get(entry.newBlob);
|
|
19896
|
+
if (!after && head === null && ABSENT.test(entry.newBlob))
|
|
19897
|
+
try {
|
|
19898
|
+
after = readFileSync4(path14.join(cwd, entry.path));
|
|
19899
|
+
} catch {
|
|
19900
|
+
after = undefined;
|
|
19980
19901
|
}
|
|
19902
|
+
if (before && after && cosmeticChange(entry.path, before, after))
|
|
19903
|
+
out.add(entry.path);
|
|
19904
|
+
else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
|
|
19905
|
+
opaque.add(entry.path);
|
|
19981
19906
|
}
|
|
19982
|
-
return
|
|
19907
|
+
return { cosmetic: out, opaque };
|
|
19983
19908
|
}
|
|
19984
|
-
function
|
|
19985
|
-
const
|
|
19986
|
-
const
|
|
19987
|
-
|
|
19988
|
-
|
|
19989
|
-
const
|
|
19990
|
-
const
|
|
19991
|
-
|
|
19992
|
-
|
|
19993
|
-
|
|
19994
|
-
|
|
19995
|
-
|
|
19996
|
-
|
|
19997
|
-
|
|
19998
|
-
|
|
19999
|
-
|
|
20000
|
-
|
|
20001
|
-
|
|
20002
|
-
|
|
20003
|
-
|
|
20004
|
-
|
|
20005
|
-
|
|
20006
|
-
|
|
20007
|
-
|
|
20008
|
-
|
|
20009
|
-
|
|
20010
|
-
|
|
20011
|
-
|
|
20012
|
-
|
|
20013
|
-
|
|
20014
|
-
|
|
20015
|
-
|
|
20016
|
-
|
|
20017
|
-
|
|
20018
|
-
|
|
20019
|
-
|
|
20020
|
-
|
|
20021
|
-
|
|
20022
|
-
|
|
20023
|
-
|
|
20024
|
-
const
|
|
20025
|
-
const
|
|
20026
|
-
|
|
20027
|
-
|
|
20028
|
-
|
|
20029
|
-
|
|
20030
|
-
|
|
20031
|
-
|
|
20032
|
-
|
|
20033
|
-
|
|
20034
|
-
|
|
20035
|
-
|
|
20036
|
-
};
|
|
20037
|
-
const
|
|
20038
|
-
|
|
20039
|
-
|
|
20040
|
-
|
|
20041
|
-
const
|
|
20042
|
-
|
|
20043
|
-
|
|
20044
|
-
|
|
20045
|
-
|
|
20046
|
-
|
|
20047
|
-
|
|
20048
|
-
|
|
20049
|
-
|
|
20050
|
-
|
|
20051
|
-
|
|
20052
|
-
|
|
20053
|
-
|
|
20054
|
-
|
|
20055
|
-
|
|
20056
|
-
|
|
20057
|
-
|
|
20058
|
-
|
|
20059
|
-
|
|
20060
|
-
|
|
20061
|
-
|
|
20062
|
-
|
|
20063
|
-
|
|
20064
|
-
|
|
20065
|
-
|
|
20066
|
-
accepted: reasons.length === 0,
|
|
20067
|
-
reasons,
|
|
20068
|
-
verdict: row
|
|
20069
|
-
});
|
|
20070
|
-
if (reasons.length === 0) {
|
|
20071
|
-
for (const later of list)
|
|
20072
|
-
if (later.seq > row.seq && reasonsOf(later).length > 0 && !untrusted(reasonsOf(later)) && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
|
|
20073
|
-
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`);
|
|
20074
|
-
}
|
|
20075
|
-
}
|
|
20076
|
-
for (const row of rows)
|
|
20077
|
-
if (row.integrity === "unverified" && scope(row))
|
|
20078
|
-
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)`);
|
|
20079
|
-
const tampered = ledgerTampering(cwd);
|
|
20080
|
-
if (tampered.length) {
|
|
20081
|
-
warnings.push(tamperedWarning(cwd, tampered));
|
|
20082
|
-
for (const entry of verdicts) {
|
|
20083
|
-
entry.accepted = false;
|
|
20084
|
-
entry.reasons = [...entry.reasons, "ledger_tampered"];
|
|
20085
|
-
}
|
|
20086
|
-
}
|
|
20087
|
-
verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
|
|
20088
|
-
const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
|
|
20089
|
-
const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
|
|
20090
|
-
const newestAccepted = verdicts.findLast((entry) => entry.accepted && STRONG_RESULTS.has(String(entry.verdict.result))) ?? null;
|
|
20091
|
-
const accepted = tampered.length ? { accepted: false, verdict: null, reasons: ["ledger_tampered"] } : newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
20092
|
-
accepted: false,
|
|
20093
|
-
verdict: failing?.verdict ?? null,
|
|
20094
|
-
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
20095
|
-
};
|
|
20096
|
-
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))));
|
|
20097
|
-
const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
|
|
20098
|
-
return {
|
|
20099
|
-
branch,
|
|
20100
|
-
head,
|
|
20101
|
-
review: accepted.accepted ? "verified" : selfEntry ? "self-reviewed" : "unreviewed",
|
|
20102
|
-
selfVerdict: accepted.accepted ? null : selfEntry?.verdict ?? null,
|
|
20103
|
-
base: newestCurrent?.verdict.base ?? fallbackBase,
|
|
20104
|
-
patchId: keyFor(newestCurrent?.verdict.base ?? fallbackBase).patchId,
|
|
20105
|
-
current: { basis: newestCurrent?.basis ?? "none", verdict: newestCurrent?.verdict ?? null },
|
|
20106
|
-
accepted,
|
|
20107
|
-
verdicts,
|
|
20108
|
-
authors: [...authors].toSorted(),
|
|
20109
|
-
warnings
|
|
19909
|
+
function generatedAt(cwd, fork, paths) {
|
|
19910
|
+
const files = treeFilesNamed(cwd, fork, ".gitattributes");
|
|
19911
|
+
const blobs = files?.size ? readBlobs(cwd, [...files.values()]) : null;
|
|
19912
|
+
if (!files || !blobs)
|
|
19913
|
+
return new Set;
|
|
19914
|
+
const texts = new Map;
|
|
19915
|
+
for (const [file, id] of files) {
|
|
19916
|
+
const bytes = blobs.get(id);
|
|
19917
|
+
if (bytes)
|
|
19918
|
+
texts.set(file, bytes.toString("utf8"));
|
|
19919
|
+
}
|
|
19920
|
+
return generatedByAttributes(texts, paths);
|
|
19921
|
+
}
|
|
19922
|
+
var forkOf = (cwd, base, head) => mergeBase(cwd, base, head ?? "HEAD");
|
|
19923
|
+
function laneFiles(cwd, fork, head, attributesAt = fork) {
|
|
19924
|
+
const stats = diffNumstat(cwd, fork, head);
|
|
19925
|
+
const cosmetic = stats ? cosmeticFiles(cwd, fork, head) : null;
|
|
19926
|
+
if (!stats || !cosmetic)
|
|
19927
|
+
return null;
|
|
19928
|
+
const files = [
|
|
19929
|
+
...stats.map((entry) => ({
|
|
19930
|
+
...entry,
|
|
19931
|
+
...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
|
|
19932
|
+
cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
|
|
19933
|
+
})),
|
|
19934
|
+
...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
|
|
19935
|
+
];
|
|
19936
|
+
const generated = generatedAt(cwd, attributesAt, files.map((entry) => entry.path));
|
|
19937
|
+
return files.map((entry) => ({
|
|
19938
|
+
...entry,
|
|
19939
|
+
generated: generated.has(entry.path)
|
|
19940
|
+
}));
|
|
19941
|
+
}
|
|
19942
|
+
function driftFiles(cwd, range) {
|
|
19943
|
+
if (range.fromFork === range.toFork)
|
|
19944
|
+
return laneFiles(cwd, range.from, range.to, range.toFork);
|
|
19945
|
+
const drifted = ownChangeDrift(cwd, range);
|
|
19946
|
+
const stats = drifted ? diffNumstat(cwd, range.from, range.to) : null;
|
|
19947
|
+
if (!drifted || !stats)
|
|
19948
|
+
return null;
|
|
19949
|
+
const byPath = new Map(stats.map((entry) => [entry.path, entry]));
|
|
19950
|
+
const generated = generatedAt(cwd, range.toFork, drifted);
|
|
19951
|
+
return drifted.map((file) => ({
|
|
19952
|
+
path: file,
|
|
19953
|
+
added: byPath.get(file)?.added ?? 0,
|
|
19954
|
+
deleted: byPath.get(file)?.deleted ?? 0,
|
|
19955
|
+
...byPath.get(file)?.binary ? { binary: true } : {},
|
|
19956
|
+
cosmetic: false,
|
|
19957
|
+
generated: generated.has(file)
|
|
19958
|
+
}));
|
|
19959
|
+
}
|
|
19960
|
+
function branchLane(cwd, options) {
|
|
19961
|
+
const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
|
|
19962
|
+
const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
|
|
19963
|
+
const head = options.workingTree ? null : options.head;
|
|
19964
|
+
const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
|
|
19965
|
+
const files = fork ? laneFiles(cwd, fork, head) : null;
|
|
19966
|
+
const riskTier = options.judgment?.riskTier ?? null;
|
|
19967
|
+
if (!fork || !files) {
|
|
19968
|
+
const high = riskTier === "high";
|
|
19969
|
+
return {
|
|
19970
|
+
lane: high ? "full" : "focused",
|
|
19971
|
+
reasons: [
|
|
19972
|
+
`the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
|
|
19973
|
+
...high ? ["the task judgment records high risk"] : []
|
|
19974
|
+
],
|
|
19975
|
+
lines: 0,
|
|
19976
|
+
files: 0,
|
|
19977
|
+
riskFiles: [],
|
|
19978
|
+
base,
|
|
19979
|
+
head: options.head
|
|
19980
|
+
};
|
|
19981
|
+
}
|
|
19982
|
+
const repoRisk = parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? "");
|
|
19983
|
+
return {
|
|
19984
|
+
...computeLane({
|
|
19985
|
+
files,
|
|
19986
|
+
riskTier,
|
|
19987
|
+
riskPaths: [...repoRisk, ...options.riskPaths ?? []]
|
|
19988
|
+
}),
|
|
19989
|
+
base,
|
|
19990
|
+
head: options.head
|
|
20110
19991
|
};
|
|
20111
19992
|
}
|
|
20112
|
-
var
|
|
20113
|
-
"check",
|
|
20114
|
-
"merge.unverified",
|
|
20115
|
-
"policy.judged",
|
|
20116
|
-
...PR_ROW_TYPES
|
|
20117
|
-
]);
|
|
20118
|
-
|
|
20119
|
-
// packages/workit-core/src/core/policy/lane.ts
|
|
20120
|
-
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
20121
|
-
import path16 from "node:path";
|
|
19993
|
+
var laneNeed = (lane) => lane === "none" ? "none" : lane === "self" ? "self" : "non-author";
|
|
20122
19994
|
|
|
20123
|
-
// packages/workit-core/src/core/
|
|
20124
|
-
|
|
20125
|
-
|
|
20126
|
-
|
|
20127
|
-
|
|
20128
|
-
|
|
20129
|
-
|
|
20130
|
-
|
|
19995
|
+
// packages/workit-core/src/core/policy/carry.ts
|
|
19996
|
+
var TEST_ONLY_KINDS = new Set(["doc", "cosmetic", "generated", "test"]);
|
|
19997
|
+
function verdictDrift(cwd, input) {
|
|
19998
|
+
const since = input.from;
|
|
19999
|
+
const stale = (reason, files = null, focused = false) => ({
|
|
20000
|
+
carries: false,
|
|
20001
|
+
since,
|
|
20002
|
+
files,
|
|
20003
|
+
focused,
|
|
20004
|
+
reason
|
|
20005
|
+
});
|
|
20006
|
+
const fromFork = mergeBase(cwd, input.fromBase, input.from);
|
|
20007
|
+
const toFork = mergeBase(cwd, input.toBase, input.to);
|
|
20008
|
+
const files = fromFork && toFork ? driftFiles(cwd, { from: input.from, to: input.to, fromFork, toFork }) : null;
|
|
20009
|
+
if (!fromFork || !toFork || !files)
|
|
20010
|
+
return stale(`the change since ${since.slice(0, 12)} cannot be read`);
|
|
20011
|
+
const riskPaths = [fromFork, toFork].flatMap((fork) => parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? ""));
|
|
20012
|
+
const lane = computeLane({ files, riskPaths });
|
|
20013
|
+
const paths = files.map((file) => file.path);
|
|
20014
|
+
if (lane.lane === "none")
|
|
20015
|
+
return { carries: true, rule: "lane-none", since, files: paths };
|
|
20016
|
+
if (input.kind === "unit" && lane.lane === "self" && files.every((file) => TEST_ONLY_KINDS.has(kindOf(file))))
|
|
20017
|
+
return { carries: true, rule: "tests-only", since, files: paths };
|
|
20018
|
+
return stale(lane.reasons[0] ?? `lane ${lane.lane}`, paths, lane.lane === "self");
|
|
20131
20019
|
}
|
|
20132
20020
|
|
|
20133
|
-
// packages/workit-core/src/
|
|
20134
|
-
|
|
20135
|
-
|
|
20136
|
-
|
|
20137
|
-
|
|
20138
|
-
|
|
20139
|
-
|
|
20140
|
-
|
|
20141
|
-
|
|
20142
|
-
|
|
20143
|
-
|
|
20144
|
-
|
|
20145
|
-
|
|
20146
|
-
|
|
20147
|
-
|
|
20148
|
-
|
|
20149
|
-
|
|
20150
|
-
|
|
20151
|
-
|
|
20021
|
+
// packages/workit-core/src/ledger.ts
|
|
20022
|
+
var LEDGER_VERSION = 1;
|
|
20023
|
+
var MAX_LINE_BYTES = 4096;
|
|
20024
|
+
var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
|
|
20025
|
+
var FAILING = new Set(["failed", "blocked"]);
|
|
20026
|
+
var STRONG_RESULTS = new Set(["verified", "tests-verified"]);
|
|
20027
|
+
var PR_ROW_TYPES = new Set([
|
|
20028
|
+
"pr.created",
|
|
20029
|
+
"pr.status",
|
|
20030
|
+
"pr.merged",
|
|
20031
|
+
"pr.linked"
|
|
20032
|
+
]);
|
|
20033
|
+
var err = (code, error, unblock) => ({
|
|
20034
|
+
ok: false,
|
|
20035
|
+
code,
|
|
20036
|
+
error,
|
|
20037
|
+
...unblock ? { unblock } : {}
|
|
20038
|
+
});
|
|
20039
|
+
var gitRun2 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
|
|
20040
|
+
const run = spawnSync9("git", args, {
|
|
20152
20041
|
cwd,
|
|
20153
|
-
encoding: "utf8"
|
|
20042
|
+
encoding: "utf8",
|
|
20043
|
+
timeout: timeoutMs,
|
|
20044
|
+
killSignal: "SIGKILL",
|
|
20045
|
+
windowsHide: true,
|
|
20046
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
20047
|
+
env: {
|
|
20048
|
+
...process.env,
|
|
20049
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
20050
|
+
GIT_OPTIONAL_LOCKS: "0",
|
|
20051
|
+
LC_ALL: "C",
|
|
20052
|
+
LANGUAGE: "C"
|
|
20053
|
+
}
|
|
20154
20054
|
});
|
|
20155
|
-
|
|
20156
|
-
|
|
20157
|
-
|
|
20158
|
-
|
|
20055
|
+
return {
|
|
20056
|
+
ok: run.status === 0,
|
|
20057
|
+
stdout: run.stdout ?? "",
|
|
20058
|
+
stderr: run.error ? String(run.error.message) : run.stderr ?? ""
|
|
20059
|
+
};
|
|
20159
20060
|
};
|
|
20160
|
-
|
|
20161
|
-
|
|
20162
|
-
|
|
20163
|
-
|
|
20164
|
-
|
|
20165
|
-
var
|
|
20166
|
-
|
|
20167
|
-
|
|
20168
|
-
var COMMIT_TIMEOUT_MS = 10 * 60000;
|
|
20169
|
-
|
|
20170
|
-
// packages/workit-core/src/core/policy/attributes.ts
|
|
20171
|
-
function patternRegex(dir, pattern) {
|
|
20172
|
-
if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
|
|
20061
|
+
var git4 = (cwd, args) => {
|
|
20062
|
+
const run = gitRun2(cwd, args);
|
|
20063
|
+
const value = run.ok ? run.stdout.trim() : "";
|
|
20064
|
+
return value ? value : null;
|
|
20065
|
+
};
|
|
20066
|
+
var safeRef2 = (value) => value.length > 0 && !value.startsWith("-");
|
|
20067
|
+
function resolveCommit(cwd, ref) {
|
|
20068
|
+
if (!safeRef2(ref))
|
|
20173
20069
|
return null;
|
|
20174
|
-
|
|
20175
|
-
|
|
20176
|
-
|
|
20177
|
-
|
|
20178
|
-
const char = body[index];
|
|
20179
|
-
if (char === "*" && body[index + 1] === "*") {
|
|
20180
|
-
const slash = body[index + 2] === "/";
|
|
20181
|
-
source += slash ? "(?:.*/)?" : ".*";
|
|
20182
|
-
index += slash ? 2 : 1;
|
|
20183
|
-
} else if (char === "*")
|
|
20184
|
-
source += "[^/]*";
|
|
20185
|
-
else if (char === "?")
|
|
20186
|
-
source += "[^/]";
|
|
20187
|
-
else if (char === "[") {
|
|
20188
|
-
const end = body.indexOf("]", index + 2);
|
|
20189
|
-
if (end < 0)
|
|
20190
|
-
return null;
|
|
20191
|
-
const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
|
|
20192
|
-
source += `[${set}]`;
|
|
20193
|
-
index = end;
|
|
20194
|
-
} else if (char === "\\" && index + 1 < body.length) {
|
|
20195
|
-
source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
20196
|
-
index += 1;
|
|
20197
|
-
} else
|
|
20198
|
-
source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
20199
|
-
}
|
|
20200
|
-
const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
|
|
20201
|
-
try {
|
|
20202
|
-
return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
|
|
20203
|
-
} catch {
|
|
20070
|
+
return git4(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]);
|
|
20071
|
+
}
|
|
20072
|
+
function branchHead(cwd, branch) {
|
|
20073
|
+
if (!safeRef2(branch))
|
|
20204
20074
|
return null;
|
|
20205
|
-
}
|
|
20075
|
+
return resolveCommit(cwd, `refs/heads/${branch}`) ?? resolveCommit(cwd, `refs/remotes/origin/${branch}`);
|
|
20206
20076
|
}
|
|
20207
|
-
function
|
|
20208
|
-
|
|
20209
|
-
|
|
20077
|
+
function diffHash(cwd, base, head) {
|
|
20078
|
+
if (!safeRef2(base) || !safeRef2(head))
|
|
20079
|
+
return null;
|
|
20080
|
+
const diff = gitRun2(cwd, [
|
|
20081
|
+
"diff",
|
|
20082
|
+
"-U3",
|
|
20083
|
+
"--diff-algorithm=myers",
|
|
20084
|
+
"--indent-heuristic",
|
|
20085
|
+
"--no-color",
|
|
20086
|
+
"--no-ext-diff",
|
|
20087
|
+
"--no-textconv",
|
|
20088
|
+
"--no-renames",
|
|
20089
|
+
"--full-index",
|
|
20090
|
+
"--binary",
|
|
20091
|
+
"--src-prefix=a/",
|
|
20092
|
+
"--dst-prefix=b/",
|
|
20093
|
+
`${base}...${head}`,
|
|
20094
|
+
"--"
|
|
20095
|
+
], GIT_TIMEOUTS.worktree);
|
|
20096
|
+
if (!diff.ok || !diff.stdout)
|
|
20097
|
+
return null;
|
|
20098
|
+
const hash = createHash9("sha256");
|
|
20099
|
+
for (const line of diff.stdout.split(`
|
|
20210
20100
|
`)) {
|
|
20211
|
-
|
|
20212
|
-
if (!line || line.startsWith("#") || line.startsWith("[attr]"))
|
|
20101
|
+
if (line.startsWith("index "))
|
|
20213
20102
|
continue;
|
|
20214
|
-
|
|
20215
|
-
|
|
20216
|
-
|
|
20217
|
-
if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
|
|
20218
|
-
generated = true;
|
|
20219
|
-
else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
|
|
20220
|
-
generated = false;
|
|
20221
|
-
}
|
|
20222
|
-
if (generated === null)
|
|
20223
|
-
continue;
|
|
20224
|
-
const regex = patternRegex(dir, pattern);
|
|
20225
|
-
if (regex)
|
|
20226
|
-
out.push({ regex, generated });
|
|
20103
|
+
hash.update(line.startsWith("@@ ") ? "@@" : line);
|
|
20104
|
+
hash.update(`
|
|
20105
|
+
`);
|
|
20227
20106
|
}
|
|
20228
|
-
return
|
|
20107
|
+
return `sha256:${hash.digest("hex")}`;
|
|
20229
20108
|
}
|
|
20230
|
-
function
|
|
20231
|
-
const
|
|
20232
|
-
|
|
20233
|
-
return
|
|
20234
|
-
|
|
20235
|
-
rules: rulesOf(dir, text)
|
|
20236
|
-
};
|
|
20237
|
-
}).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
|
|
20238
|
-
const out = new Set;
|
|
20239
|
-
for (const file of paths) {
|
|
20240
|
-
const last = rules.findLast((rule) => rule.regex.test(file));
|
|
20241
|
-
if (last?.generated)
|
|
20242
|
-
out.add(file);
|
|
20243
|
-
}
|
|
20244
|
-
return out;
|
|
20109
|
+
function storeRoot(cwd) {
|
|
20110
|
+
const location = resolveStore(path15.resolve(cwd));
|
|
20111
|
+
if (location instanceof Error)
|
|
20112
|
+
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");
|
|
20113
|
+
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
20245
20114
|
}
|
|
20246
|
-
|
|
20247
|
-
|
|
20248
|
-
|
|
20249
|
-
|
|
20250
|
-
var
|
|
20251
|
-
|
|
20252
|
-
|
|
20253
|
-
|
|
20254
|
-
|
|
20255
|
-
|
|
20256
|
-
|
|
20257
|
-
|
|
20258
|
-
|
|
20259
|
-
|
|
20260
|
-
|
|
20261
|
-
|
|
20262
|
-
ext,
|
|
20263
|
-
{ line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
|
|
20264
|
-
]),
|
|
20265
|
-
["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
|
|
20266
|
-
...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
|
|
20267
|
-
ext,
|
|
20268
|
-
{ line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
|
|
20269
|
-
])
|
|
20115
|
+
function ledgerPath(cwd) {
|
|
20116
|
+
const root = storeRoot(cwd);
|
|
20117
|
+
return root.ok ? { ok: true, value: path15.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
20118
|
+
}
|
|
20119
|
+
var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
|
|
20120
|
+
var NETWORK_FS_TYPES = new Set([
|
|
20121
|
+
26985,
|
|
20122
|
+
20859,
|
|
20123
|
+
4266872130,
|
|
20124
|
+
4283649346,
|
|
20125
|
+
1702057286,
|
|
20126
|
+
1397113167,
|
|
20127
|
+
12805120,
|
|
20128
|
+
16914839,
|
|
20129
|
+
1196443219,
|
|
20130
|
+
198183888
|
|
20270
20131
|
]);
|
|
20271
|
-
var
|
|
20272
|
-
var
|
|
20273
|
-
|
|
20274
|
-
|
|
20275
|
-
|
|
20132
|
+
var isNetworkFsType = (type) => NETWORK_FS_TYPES.has(type >>> 0);
|
|
20133
|
+
var onNetworkFs = (dir) => {
|
|
20134
|
+
if (process.platform !== "linux")
|
|
20135
|
+
return false;
|
|
20136
|
+
try {
|
|
20137
|
+
return isNetworkFsType(fs12.statfsSync(dir).type);
|
|
20138
|
+
} catch {
|
|
20139
|
+
return false;
|
|
20140
|
+
}
|
|
20276
20141
|
};
|
|
20277
|
-
var
|
|
20278
|
-
var
|
|
20279
|
-
var
|
|
20280
|
-
var
|
|
20281
|
-
|
|
20282
|
-
|
|
20283
|
-
|
|
20284
|
-
|
|
20285
|
-
"return",
|
|
20286
|
-
"typeof",
|
|
20287
|
-
"instanceof",
|
|
20288
|
-
"case",
|
|
20289
|
-
"do",
|
|
20290
|
-
"else",
|
|
20291
|
-
"in",
|
|
20292
|
-
"of",
|
|
20293
|
-
"new",
|
|
20294
|
-
"delete",
|
|
20295
|
-
"void",
|
|
20296
|
-
"throw",
|
|
20297
|
-
"yield",
|
|
20298
|
-
"await"
|
|
20299
|
-
]);
|
|
20300
|
-
function substanceOf(file, text) {
|
|
20301
|
-
const syntax = syntaxOf(file);
|
|
20302
|
-
if (!syntax)
|
|
20303
|
-
return plainSubstance(text);
|
|
20304
|
-
const markup = syntax.block.some(([open]) => open === "<!--");
|
|
20305
|
-
if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
|
|
20306
|
-
return plainSubstance(text);
|
|
20307
|
-
return lex(syntax, text);
|
|
20308
|
-
}
|
|
20309
|
-
function plainSubstance(text) {
|
|
20310
|
-
return text.split(`
|
|
20311
|
-
`).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
|
|
20312
|
-
`);
|
|
20313
|
-
}
|
|
20314
|
-
function lex(syntax, text) {
|
|
20315
|
-
const collapse = !syntax.indented;
|
|
20316
|
-
let out = "";
|
|
20317
|
-
let lineStart = 0;
|
|
20318
|
-
let kept = 0;
|
|
20319
|
-
let pendingSpace = false;
|
|
20320
|
-
const frames = [{ kind: "code", braces: 0 }];
|
|
20321
|
-
let index = 0;
|
|
20322
|
-
if (text.startsWith("#!")) {
|
|
20323
|
-
const end = text.indexOf(`
|
|
20324
|
-
`);
|
|
20325
|
-
out = end < 0 ? text : text.slice(0, end);
|
|
20326
|
-
kept = out.length;
|
|
20327
|
-
index = end < 0 ? text.length : end;
|
|
20142
|
+
var LOCK_WAIT_MS = 5000;
|
|
20143
|
+
var LOCK_STALE_MS = 30000;
|
|
20144
|
+
var LOCK_OWNER = "owner";
|
|
20145
|
+
var lockToken = (lock) => {
|
|
20146
|
+
try {
|
|
20147
|
+
return fs12.readFileSync(path15.join(lock, LOCK_OWNER), "utf8");
|
|
20148
|
+
} catch {
|
|
20149
|
+
return null;
|
|
20328
20150
|
}
|
|
20329
|
-
|
|
20330
|
-
|
|
20331
|
-
|
|
20332
|
-
|
|
20333
|
-
|
|
20334
|
-
}
|
|
20335
|
-
|
|
20336
|
-
|
|
20337
|
-
|
|
20338
|
-
|
|
20339
|
-
|
|
20340
|
-
|
|
20341
|
-
|
|
20342
|
-
|
|
20343
|
-
|
|
20344
|
-
let end = out.length;
|
|
20345
|
-
while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
|
|
20346
|
-
end -= 1;
|
|
20347
|
-
out = out.slice(0, end);
|
|
20348
|
-
}
|
|
20349
|
-
pendingSpace = false;
|
|
20350
|
-
if (out.length > lineStart) {
|
|
20351
|
-
out += `
|
|
20352
|
-
`;
|
|
20353
|
-
lineStart = out.length;
|
|
20354
|
-
}
|
|
20355
|
-
};
|
|
20356
|
-
const regexAllowed = () => {
|
|
20357
|
-
let end = out.length;
|
|
20358
|
-
while (end > 0 && /\s/u.test(out[end - 1]))
|
|
20359
|
-
end -= 1;
|
|
20360
|
-
if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
|
|
20361
|
-
return true;
|
|
20362
|
-
let start = end;
|
|
20363
|
-
while (start > 0 && WORD.test(out[start - 1]))
|
|
20364
|
-
start -= 1;
|
|
20365
|
-
return REGEX_KEYWORDS.has(out.slice(start, end));
|
|
20366
|
-
};
|
|
20367
|
-
const scanString = (quote, escapes, multiline) => {
|
|
20368
|
-
const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
|
|
20369
|
-
const fstring = syntax.fstring === true && /f/iu.test(prefix);
|
|
20370
|
-
let end = index + quote.length;
|
|
20371
|
-
while (end < text.length) {
|
|
20372
|
-
const char = text[end];
|
|
20373
|
-
if (escapes && char === "\\") {
|
|
20374
|
-
end += 2;
|
|
20375
|
-
continue;
|
|
20376
|
-
}
|
|
20377
|
-
if (text.startsWith(quote, end)) {
|
|
20378
|
-
if (fstring && unbalanced(text.slice(index + quote.length, end)))
|
|
20379
|
-
return false;
|
|
20380
|
-
emitVerbatim(text.slice(index, end + quote.length));
|
|
20381
|
-
index = end + quote.length;
|
|
20382
|
-
return true;
|
|
20383
|
-
}
|
|
20384
|
-
if (char === `
|
|
20385
|
-
` && !multiline)
|
|
20386
|
-
return false;
|
|
20387
|
-
end += 1;
|
|
20388
|
-
}
|
|
20389
|
-
return false;
|
|
20390
|
-
};
|
|
20391
|
-
while (index < text.length) {
|
|
20392
|
-
const frame = frames[frames.length - 1];
|
|
20393
|
-
const char = text[index];
|
|
20394
|
-
if (frame.kind === "template") {
|
|
20395
|
-
if (char === "\\") {
|
|
20396
|
-
emitVerbatim(text.slice(index, index + 2));
|
|
20397
|
-
index += 2;
|
|
20398
|
-
} else if (char === "`") {
|
|
20399
|
-
emitVerbatim(char);
|
|
20400
|
-
frames.pop();
|
|
20401
|
-
index += 1;
|
|
20402
|
-
} else if (text.startsWith("${", index)) {
|
|
20403
|
-
emitVerbatim("${");
|
|
20404
|
-
frames.push({ kind: "code", braces: 0 });
|
|
20405
|
-
index += 2;
|
|
20406
|
-
} else {
|
|
20407
|
-
out += char;
|
|
20408
|
-
kept = out.length;
|
|
20409
|
-
index += 1;
|
|
20410
|
-
}
|
|
20411
|
-
continue;
|
|
20412
|
-
}
|
|
20413
|
-
if (char === `
|
|
20414
|
-
`) {
|
|
20415
|
-
newline();
|
|
20416
|
-
index += 1;
|
|
20417
|
-
continue;
|
|
20418
|
-
}
|
|
20419
|
-
if (SPACE.test(char)) {
|
|
20420
|
-
if (collapse)
|
|
20421
|
-
pendingSpace = true;
|
|
20422
|
-
else
|
|
20423
|
-
out += char;
|
|
20424
|
-
index += 1;
|
|
20425
|
-
continue;
|
|
20426
|
-
}
|
|
20427
|
-
const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
|
|
20428
|
-
if (lineOpener) {
|
|
20429
|
-
const end = text.indexOf(`
|
|
20430
|
-
`, index);
|
|
20431
|
-
const comment = text.slice(index, end < 0 ? text.length : end);
|
|
20432
|
-
if (DIRECTIVE.test(comment.slice(lineOpener.length)))
|
|
20433
|
-
emitVerbatim(comment);
|
|
20434
|
-
else
|
|
20435
|
-
pendingSpace = true;
|
|
20436
|
-
index = end < 0 ? text.length : end;
|
|
20437
|
-
continue;
|
|
20438
|
-
}
|
|
20439
|
-
const block = syntax.block.find(([open]) => text.startsWith(open, index));
|
|
20440
|
-
if (block) {
|
|
20441
|
-
const end = text.indexOf(block[1], index + block[0].length);
|
|
20442
|
-
if (end < 0)
|
|
20443
|
-
return null;
|
|
20444
|
-
const comment = text.slice(index, end + block[1].length);
|
|
20445
|
-
if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
|
|
20446
|
-
emitVerbatim(comment);
|
|
20447
|
-
else
|
|
20448
|
-
pendingSpace = true;
|
|
20449
|
-
if (comment.includes(`
|
|
20450
|
-
`))
|
|
20451
|
-
newline();
|
|
20452
|
-
index = end + block[1].length;
|
|
20453
|
-
continue;
|
|
20454
|
-
}
|
|
20455
|
-
if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
|
|
20456
|
-
if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
|
|
20151
|
+
};
|
|
20152
|
+
var lockAgeMs = (lock) => {
|
|
20153
|
+
for (const target of [path15.join(lock, LOCK_OWNER), lock]) {
|
|
20154
|
+
try {
|
|
20155
|
+
return Date.now() - fs12.statSync(target).mtimeMs;
|
|
20156
|
+
} catch {}
|
|
20157
|
+
}
|
|
20158
|
+
return null;
|
|
20159
|
+
};
|
|
20160
|
+
var ledgerLock = {
|
|
20161
|
+
acquire(lock) {
|
|
20162
|
+
try {
|
|
20163
|
+
fs12.mkdirSync(lock);
|
|
20164
|
+
} catch (error) {
|
|
20165
|
+
if (error.code === "EEXIST")
|
|
20457
20166
|
return null;
|
|
20458
|
-
|
|
20167
|
+
throw error;
|
|
20459
20168
|
}
|
|
20460
|
-
|
|
20461
|
-
|
|
20462
|
-
|
|
20463
|
-
|
|
20169
|
+
const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
|
|
20170
|
+
fs12.writeFileSync(path15.join(lock, LOCK_OWNER), token);
|
|
20171
|
+
return token;
|
|
20172
|
+
},
|
|
20173
|
+
release(lock, token) {
|
|
20174
|
+
if (lockToken(lock) !== token)
|
|
20175
|
+
return false;
|
|
20176
|
+
const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
|
|
20177
|
+
try {
|
|
20178
|
+
fs12.renameSync(lock, tomb);
|
|
20179
|
+
} catch {
|
|
20180
|
+
return false;
|
|
20464
20181
|
}
|
|
20465
|
-
if (
|
|
20466
|
-
|
|
20467
|
-
|
|
20468
|
-
|
|
20182
|
+
if (lockToken(tomb) !== token) {
|
|
20183
|
+
try {
|
|
20184
|
+
fs12.renameSync(tomb, lock);
|
|
20185
|
+
} catch {}
|
|
20186
|
+
return false;
|
|
20469
20187
|
}
|
|
20470
|
-
|
|
20471
|
-
|
|
20472
|
-
|
|
20473
|
-
|
|
20188
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
20189
|
+
return true;
|
|
20190
|
+
},
|
|
20191
|
+
token: lockToken,
|
|
20192
|
+
reap(lock, observed) {
|
|
20193
|
+
const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
|
|
20194
|
+
try {
|
|
20195
|
+
fs12.renameSync(lock, tomb);
|
|
20196
|
+
} catch {
|
|
20197
|
+
return false;
|
|
20474
20198
|
}
|
|
20475
|
-
if (
|
|
20476
|
-
|
|
20477
|
-
|
|
20478
|
-
index += 1;
|
|
20479
|
-
continue;
|
|
20199
|
+
if (lockToken(tomb) === observed) {
|
|
20200
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
20201
|
+
return true;
|
|
20480
20202
|
}
|
|
20481
|
-
|
|
20203
|
+
try {
|
|
20204
|
+
fs12.renameSync(tomb, lock);
|
|
20205
|
+
} catch {
|
|
20206
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
20207
|
+
}
|
|
20208
|
+
return false;
|
|
20209
|
+
}
|
|
20210
|
+
};
|
|
20211
|
+
function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
|
|
20212
|
+
const lock = `${file}.lock`;
|
|
20213
|
+
const deadline = Date.now() + waitMs;
|
|
20214
|
+
let token = null;
|
|
20215
|
+
for (;; ) {
|
|
20216
|
+
token = ledgerLock.acquire(lock);
|
|
20217
|
+
if (token)
|
|
20218
|
+
break;
|
|
20219
|
+
const observed = lockToken(lock);
|
|
20220
|
+
const age = lockAgeMs(lock);
|
|
20221
|
+
if (age !== null && age > LOCK_STALE_MS && ledgerLock.reap(lock, observed))
|
|
20222
|
+
continue;
|
|
20223
|
+
if (Date.now() >= deadline)
|
|
20482
20224
|
return null;
|
|
20483
|
-
|
|
20484
|
-
|
|
20485
|
-
|
|
20486
|
-
|
|
20225
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
|
|
20226
|
+
}
|
|
20227
|
+
try {
|
|
20228
|
+
return fn();
|
|
20229
|
+
} finally {
|
|
20230
|
+
ledgerLock.release(lock, token);
|
|
20231
|
+
}
|
|
20232
|
+
}
|
|
20233
|
+
var sha2562 = (text) => createHash9("sha256").update(text).digest("hex");
|
|
20234
|
+
var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
|
|
20235
|
+
${body}`);
|
|
20236
|
+
var TAIL_BYTES = 64 * 1024;
|
|
20237
|
+
var lastLine = (fd, size) => {
|
|
20238
|
+
for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
|
|
20239
|
+
const buffer = Buffer.alloc(span);
|
|
20240
|
+
fs12.readSync(fd, buffer, 0, span, size - span);
|
|
20241
|
+
const end = buffer.lastIndexOf(10);
|
|
20242
|
+
if (end < 0) {
|
|
20243
|
+
if (span === size)
|
|
20487
20244
|
return null;
|
|
20245
|
+
continue;
|
|
20488
20246
|
}
|
|
20489
|
-
|
|
20490
|
-
|
|
20491
|
-
|
|
20492
|
-
|
|
20493
|
-
|
|
20494
|
-
|
|
20247
|
+
let stop = end;
|
|
20248
|
+
let text = "";
|
|
20249
|
+
while (stop >= 0) {
|
|
20250
|
+
const start = buffer.lastIndexOf(10, stop - 1);
|
|
20251
|
+
text = buffer.toString("utf8", start + 1, stop).trim();
|
|
20252
|
+
if (text) {
|
|
20253
|
+
if (start >= 0 || span === size)
|
|
20254
|
+
return text;
|
|
20255
|
+
break;
|
|
20256
|
+
}
|
|
20257
|
+
if (start < 0) {
|
|
20258
|
+
if (span === size)
|
|
20495
20259
|
return null;
|
|
20496
|
-
|
|
20497
|
-
end += 1;
|
|
20498
|
-
else if (text[end] === "[")
|
|
20499
|
-
inClass = true;
|
|
20500
|
-
else if (text[end] === "]")
|
|
20501
|
-
inClass = false;
|
|
20502
|
-
end += 1;
|
|
20260
|
+
break;
|
|
20503
20261
|
}
|
|
20504
|
-
|
|
20505
|
-
|
|
20506
|
-
|
|
20507
|
-
|
|
20262
|
+
stop = start;
|
|
20263
|
+
}
|
|
20264
|
+
if (span === size)
|
|
20265
|
+
return text || null;
|
|
20266
|
+
}
|
|
20267
|
+
return null;
|
|
20268
|
+
};
|
|
20269
|
+
var tighten = (fd) => {
|
|
20270
|
+
try {
|
|
20271
|
+
if ((fs12.fstatSync(fd).mode & 63) !== 0)
|
|
20272
|
+
fs12.fchmodSync(fd, 384);
|
|
20273
|
+
} catch {}
|
|
20274
|
+
};
|
|
20275
|
+
function appendLine(file, build, options) {
|
|
20276
|
+
const write = () => {
|
|
20277
|
+
const fd = fs12.openSync(file, "a+", 384);
|
|
20278
|
+
try {
|
|
20279
|
+
if (process.platform !== "win32")
|
|
20280
|
+
tighten(fd);
|
|
20281
|
+
const size = fs12.fstatSync(fd).size;
|
|
20282
|
+
let fence = "";
|
|
20283
|
+
if (size > 0) {
|
|
20284
|
+
const last = Buffer.alloc(1);
|
|
20285
|
+
fs12.readSync(fd, last, 0, 1, size - 1);
|
|
20286
|
+
if (last[0] !== 10)
|
|
20287
|
+
fence = `
|
|
20288
|
+
`;
|
|
20289
|
+
}
|
|
20290
|
+
const line = build(lastLine(fd, size));
|
|
20291
|
+
if (!line.ok)
|
|
20292
|
+
return line;
|
|
20293
|
+
const bytes = Buffer.from(fence + line.value);
|
|
20294
|
+
const written = fs12.writeSync(fd, bytes);
|
|
20295
|
+
if (written !== bytes.length)
|
|
20296
|
+
return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
|
|
20297
|
+
return { ok: true, value: undefined };
|
|
20298
|
+
} finally {
|
|
20299
|
+
fs12.closeSync(fd);
|
|
20300
|
+
}
|
|
20301
|
+
};
|
|
20302
|
+
try {
|
|
20303
|
+
fs12.mkdirSync(path15.dirname(file), { recursive: true });
|
|
20304
|
+
if (!options.forceLock && !onNetworkFs(path15.dirname(file)))
|
|
20305
|
+
return write();
|
|
20306
|
+
const locked = withLedgerLock(file, write);
|
|
20307
|
+
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
20308
|
+
} catch (error) {
|
|
20309
|
+
return err("unavailable", `cannot append to ${file}: ${error.message}`);
|
|
20310
|
+
}
|
|
20311
|
+
}
|
|
20312
|
+
var sealed = (row, id, at, previous) => {
|
|
20313
|
+
const { prevHash: _prev, rowHash: _row, ...fields } = row;
|
|
20314
|
+
const prevHash = previous === null ? null : sha2562(previous);
|
|
20315
|
+
const body = { v: LEDGER_VERSION, id, at, ...fields, prevHash };
|
|
20316
|
+
const full = { ...body, rowHash: chainHash(prevHash, JSON.stringify(body)) };
|
|
20317
|
+
return { full, line: `${JSON.stringify(full)}
|
|
20318
|
+
` };
|
|
20319
|
+
};
|
|
20320
|
+
function appendRaw(cwd, row, options) {
|
|
20321
|
+
const file = ledgerPath(cwd);
|
|
20322
|
+
if (!file.ok)
|
|
20323
|
+
return file;
|
|
20324
|
+
const id = newId2();
|
|
20325
|
+
const at = (options.now ?? new Date).toISOString();
|
|
20326
|
+
let full = null;
|
|
20327
|
+
const written = appendLine(file.value, (previous) => {
|
|
20328
|
+
const next = sealed(row, id, at, previous);
|
|
20329
|
+
const bytes = Buffer.byteLength(next.line);
|
|
20330
|
+
if (bytes > MAX_LINE_BYTES)
|
|
20331
|
+
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>`);
|
|
20332
|
+
full = next.full;
|
|
20333
|
+
return { ok: true, value: next.line };
|
|
20334
|
+
}, options);
|
|
20335
|
+
return written.ok ? { ok: true, value: full } : written;
|
|
20336
|
+
}
|
|
20337
|
+
function observedRowBytes(row) {
|
|
20338
|
+
const sample = sealed({ ...row, observer: "workit_cli" }, newId2(), new Date().toISOString(), "");
|
|
20339
|
+
return Buffer.byteLength(sample.line);
|
|
20340
|
+
}
|
|
20341
|
+
function appendObserved(cwd, row, options = {}) {
|
|
20342
|
+
return appendRaw(cwd, { ...row, observer: "workit_cli" }, options);
|
|
20343
|
+
}
|
|
20344
|
+
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20345
|
+
var str2 = (value) => typeof value === "string" ? value : null;
|
|
20346
|
+
var normalizeActor = (value) => {
|
|
20347
|
+
const actor = isRecord3(value) ? value : {};
|
|
20348
|
+
return {
|
|
20349
|
+
host: str2(actor.host) ?? "unknown",
|
|
20350
|
+
session: str2(actor.session),
|
|
20351
|
+
agentId: str2(actor.agentId)
|
|
20352
|
+
};
|
|
20353
|
+
};
|
|
20354
|
+
function supersedeAllowed(by, target, onBranch = (row) => (by.branch ?? null) === (row.branch ?? null)) {
|
|
20355
|
+
if (by.type !== target.type)
|
|
20356
|
+
return `a ${by.type} row cannot supersede a ${target.type} row`;
|
|
20357
|
+
if (!by.actor.session || by.actor.session !== target.actor.session)
|
|
20358
|
+
return "only the session that wrote a row can supersede it";
|
|
20359
|
+
if (!onBranch(target))
|
|
20360
|
+
return `the row is on another branch (${String(target.branch ?? "none")}); a row supersedes only one on its own branch`;
|
|
20361
|
+
if (by.type === "verdict" && (by.kind ?? "review") !== (target.kind ?? "review"))
|
|
20362
|
+
return `a ${String(by.kind ?? "review")} verdict cannot supersede a ${String(target.kind ?? "review")} verdict; record it with --kind ${String(target.kind ?? "review")}`;
|
|
20363
|
+
if (by.type === "verdict" && by.self === true && target.self !== true)
|
|
20364
|
+
return "a self verdict cannot replace an independent verdict";
|
|
20365
|
+
return null;
|
|
20366
|
+
}
|
|
20367
|
+
function readLedger(cwd, options = {}) {
|
|
20368
|
+
const file = ledgerPath(cwd);
|
|
20369
|
+
if (!file.ok)
|
|
20370
|
+
return file;
|
|
20371
|
+
let text = "";
|
|
20372
|
+
let stat = null;
|
|
20373
|
+
try {
|
|
20374
|
+
stat = fs12.statSync(file.value);
|
|
20375
|
+
text = fs12.readFileSync(file.value, "utf8");
|
|
20376
|
+
} catch (error) {
|
|
20377
|
+
if (error.code === "ENOENT")
|
|
20378
|
+
return {
|
|
20379
|
+
ok: true,
|
|
20380
|
+
value: {
|
|
20381
|
+
path: file.value,
|
|
20382
|
+
rows: [],
|
|
20383
|
+
skipped: 0,
|
|
20384
|
+
integrity: options.integrity ? { chained: 0, legacy: 0, unverified: [] } : null
|
|
20385
|
+
}
|
|
20386
|
+
};
|
|
20387
|
+
return err("unavailable", `cannot read ${file.value}: ${error.message}`);
|
|
20388
|
+
}
|
|
20389
|
+
const rows = [];
|
|
20390
|
+
let skipped = 0;
|
|
20391
|
+
const integrity = { chained: 0, legacy: 0, unverified: [] };
|
|
20392
|
+
const earlier = new Set([null]);
|
|
20393
|
+
let chainStarted = false;
|
|
20394
|
+
const ids = new Set;
|
|
20395
|
+
for (const raw of text.split(`
|
|
20396
|
+
`)) {
|
|
20397
|
+
const trimmed = raw.trim();
|
|
20398
|
+
if (!trimmed)
|
|
20399
|
+
continue;
|
|
20400
|
+
const linked = (prevHash) => earlier.has(prevHash);
|
|
20401
|
+
if (options.integrity)
|
|
20402
|
+
earlier.add(sha2562(trimmed));
|
|
20403
|
+
let parsed;
|
|
20404
|
+
try {
|
|
20405
|
+
parsed = JSON.parse(trimmed);
|
|
20406
|
+
} catch {
|
|
20407
|
+
skipped += 1;
|
|
20508
20408
|
continue;
|
|
20509
20409
|
}
|
|
20510
|
-
if (
|
|
20511
|
-
|
|
20512
|
-
|
|
20513
|
-
else if (char === "}") {
|
|
20514
|
-
if (frame.braces === 0) {
|
|
20515
|
-
emitVerbatim(char);
|
|
20516
|
-
frames.pop();
|
|
20517
|
-
index += 1;
|
|
20518
|
-
continue;
|
|
20519
|
-
}
|
|
20520
|
-
frame.braces -= 1;
|
|
20521
|
-
}
|
|
20410
|
+
if (!isRecord3(parsed) || !str2(parsed.id) || !str2(parsed.type) || !str2(parsed.at)) {
|
|
20411
|
+
skipped += 1;
|
|
20412
|
+
continue;
|
|
20522
20413
|
}
|
|
20523
|
-
|
|
20524
|
-
|
|
20414
|
+
let rowIntegrity = options.integrity ? "verified" : undefined;
|
|
20415
|
+
let reason = null;
|
|
20416
|
+
if (!options.integrity) {} else if (typeof parsed.rowHash === "string") {
|
|
20417
|
+
chainStarted = true;
|
|
20418
|
+
const { rowHash, ...body } = parsed;
|
|
20419
|
+
const prevHash = str2(parsed.prevHash);
|
|
20420
|
+
if (chainHash(prevHash, JSON.stringify(body)) !== rowHash)
|
|
20421
|
+
reason = "hash_mismatch";
|
|
20422
|
+
else if (!linked(prevHash))
|
|
20423
|
+
reason = "broken_link";
|
|
20424
|
+
} else if (chainStarted)
|
|
20425
|
+
reason = "unsigned";
|
|
20426
|
+
else
|
|
20427
|
+
rowIntegrity = "legacy";
|
|
20428
|
+
if (options.integrity && !reason && ids.has(parsed.id))
|
|
20429
|
+
reason = "duplicate_id";
|
|
20430
|
+
if (options.integrity)
|
|
20431
|
+
ids.add(parsed.id);
|
|
20432
|
+
if (reason)
|
|
20433
|
+
rowIntegrity = "unverified";
|
|
20434
|
+
const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
|
|
20435
|
+
const row = {
|
|
20436
|
+
...parsed,
|
|
20437
|
+
v: typeof parsed.v === "number" ? parsed.v : LEDGER_VERSION,
|
|
20438
|
+
id: parsed.id,
|
|
20439
|
+
at: parsed.at,
|
|
20440
|
+
type: parsed.type,
|
|
20441
|
+
actor: normalizeActor(parsed.actor),
|
|
20442
|
+
branch: str2(parsed.branch),
|
|
20443
|
+
head: str2(parsed.head),
|
|
20444
|
+
base: str2(parsed.base),
|
|
20445
|
+
baseSha: str2(parsed.baseSha),
|
|
20446
|
+
patchId: str2(parsed.patchId),
|
|
20447
|
+
diffHash: str2(parsed.diffHash),
|
|
20448
|
+
tree: str2(parsed.tree),
|
|
20449
|
+
dirty: typeof parsed.dirty === "boolean" ? parsed.dirty : null,
|
|
20450
|
+
...pr === undefined ? {} : { pr },
|
|
20451
|
+
...str2(parsed.supersedes) ? { supersedes: parsed.supersedes } : {},
|
|
20452
|
+
seq: rows.length + 1,
|
|
20453
|
+
superseded: false,
|
|
20454
|
+
supersedeIgnored: false,
|
|
20455
|
+
...rowIntegrity ? { integrity: rowIntegrity } : {},
|
|
20456
|
+
...reason ? { integrityReason: reason } : {}
|
|
20457
|
+
};
|
|
20458
|
+
rows.push(row);
|
|
20459
|
+
if (!options.integrity)
|
|
20460
|
+
continue;
|
|
20461
|
+
if (rowIntegrity === "verified")
|
|
20462
|
+
integrity.chained += 1;
|
|
20463
|
+
else if (rowIntegrity === "legacy")
|
|
20464
|
+
integrity.legacy += 1;
|
|
20465
|
+
else if (reason)
|
|
20466
|
+
integrity.unverified.push({
|
|
20467
|
+
seq: row.seq,
|
|
20468
|
+
id: row.id,
|
|
20469
|
+
type: row.type,
|
|
20470
|
+
branch: row.branch,
|
|
20471
|
+
reason
|
|
20472
|
+
});
|
|
20525
20473
|
}
|
|
20526
|
-
|
|
20527
|
-
|
|
20528
|
-
|
|
20529
|
-
|
|
20530
|
-
|
|
20531
|
-
|
|
20532
|
-
|
|
20533
|
-
|
|
20534
|
-
|
|
20535
|
-
|
|
20536
|
-
|
|
20537
|
-
|
|
20474
|
+
const scopes = new Map;
|
|
20475
|
+
const scopeOf = (branch) => {
|
|
20476
|
+
let scope = scopes.get(branch);
|
|
20477
|
+
if (!scope)
|
|
20478
|
+
scopes.set(branch, scope = branchScope(cwd, branch));
|
|
20479
|
+
return scope;
|
|
20480
|
+
};
|
|
20481
|
+
const byId = new Map;
|
|
20482
|
+
for (const row of rows) {
|
|
20483
|
+
const target = row.supersedes ? byId.get(row.supersedes) : undefined;
|
|
20484
|
+
if (target && row.integrity !== "unverified" && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
20485
|
+
target.superseded = true;
|
|
20486
|
+
else if (row.supersedes)
|
|
20487
|
+
row.supersedeIgnored = true;
|
|
20488
|
+
if (!byId.has(row.id))
|
|
20489
|
+
byId.set(row.id, row);
|
|
20538
20490
|
}
|
|
20539
|
-
|
|
20540
|
-
|
|
20541
|
-
|
|
20542
|
-
|
|
20491
|
+
if (options.integrity && stat && Buffer.byteLength(text) === stat.size)
|
|
20492
|
+
tamperCache.set(file.value, {
|
|
20493
|
+
size: stat.size,
|
|
20494
|
+
stamp: fileStamp(stat),
|
|
20495
|
+
breaks: integrity.unverified.filter((row) => TAMPER_REASONS.has(row.reason))
|
|
20496
|
+
});
|
|
20497
|
+
return {
|
|
20498
|
+
ok: true,
|
|
20499
|
+
value: { path: file.value, rows, skipped, integrity: options.integrity ? integrity : null }
|
|
20500
|
+
};
|
|
20543
20501
|
}
|
|
20544
|
-
var
|
|
20545
|
-
|
|
20546
|
-
|
|
20547
|
-
|
|
20502
|
+
var TAMPER_REASONS = new Set(["hash_mismatch", "broken_link"]);
|
|
20503
|
+
var tamperCache = new Map;
|
|
20504
|
+
var fileStamp = (stat) => `${stat.ino}:${stat.mtimeMs}:${stat.ctimeMs}`;
|
|
20505
|
+
function ledgerTampering(cwd) {
|
|
20506
|
+
const file = ledgerPath(cwd);
|
|
20507
|
+
if (!file.ok)
|
|
20508
|
+
return [];
|
|
20548
20509
|
try {
|
|
20549
|
-
|
|
20510
|
+
const stat = fs12.statSync(file.value);
|
|
20511
|
+
const cached = tamperCache.get(file.value);
|
|
20512
|
+
if (cached && cached.size === stat.size && cached.stamp === fileStamp(stat))
|
|
20513
|
+
return cached.breaks;
|
|
20550
20514
|
} catch {
|
|
20551
|
-
return
|
|
20515
|
+
return [];
|
|
20552
20516
|
}
|
|
20517
|
+
const read = readLedger(cwd, { integrity: true });
|
|
20518
|
+
return read.ok ? (read.value.integrity?.unverified ?? []).filter((row) => TAMPER_REASONS.has(row.reason)) : [];
|
|
20553
20519
|
}
|
|
20554
|
-
function
|
|
20555
|
-
const
|
|
20556
|
-
const
|
|
20557
|
-
if (
|
|
20558
|
-
return false;
|
|
20559
|
-
if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
|
|
20560
|
-
return false;
|
|
20561
|
-
const a = substanceOf(file, left);
|
|
20562
|
-
return a !== null && a === substanceOf(file, right);
|
|
20520
|
+
function tamperedUnblock(cwd) {
|
|
20521
|
+
const file = ledgerPath(cwd);
|
|
20522
|
+
const where = file.ok ? file.value : "the ledger file";
|
|
20523
|
+
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`;
|
|
20563
20524
|
}
|
|
20564
|
-
|
|
20565
|
-
|
|
20566
|
-
|
|
20567
|
-
|
|
20568
|
-
|
|
20569
|
-
|
|
20570
|
-
|
|
20571
|
-
|
|
20572
|
-
|
|
20573
|
-
|
|
20574
|
-
|
|
20575
|
-
|
|
20576
|
-
|
|
20577
|
-
|
|
20578
|
-
|
|
20579
|
-
|
|
20580
|
-
|
|
20581
|
-
"
|
|
20582
|
-
"gate",
|
|
20583
|
-
"gates",
|
|
20584
|
-
"policy",
|
|
20585
|
-
"policies",
|
|
20586
|
-
"hook",
|
|
20587
|
-
"hooks",
|
|
20588
|
-
"husky",
|
|
20589
|
-
"lefthook",
|
|
20590
|
-
"release",
|
|
20591
|
-
"releases",
|
|
20592
|
-
"migration",
|
|
20593
|
-
"migrations",
|
|
20594
|
-
"migrate",
|
|
20595
|
-
"workflows"
|
|
20596
|
-
]);
|
|
20597
|
-
var RISK_FILES = new Set([
|
|
20598
|
-
".gitlab-ci.yml",
|
|
20599
|
-
"Jenkinsfile",
|
|
20600
|
-
"azure-pipelines.yml",
|
|
20601
|
-
".pre-commit-config.yaml",
|
|
20602
|
-
".workit/risk-paths"
|
|
20603
|
-
]);
|
|
20604
|
-
var RISK_NAMES = new Set([".gitattributes"]);
|
|
20605
|
-
var RISK_PATHS_FILE = ".workit/risk-paths";
|
|
20606
|
-
var LOCKFILES = new Set([
|
|
20607
|
-
"bun.lock",
|
|
20608
|
-
"bun.lockb",
|
|
20609
|
-
"package-lock.json",
|
|
20610
|
-
"npm-shrinkwrap.json",
|
|
20611
|
-
"yarn.lock",
|
|
20612
|
-
"pnpm-lock.yaml",
|
|
20613
|
-
"Cargo.lock",
|
|
20614
|
-
"go.sum",
|
|
20615
|
-
"poetry.lock",
|
|
20616
|
-
"uv.lock",
|
|
20617
|
-
"Pipfile.lock",
|
|
20618
|
-
"Gemfile.lock",
|
|
20619
|
-
"composer.lock"
|
|
20620
|
-
]);
|
|
20621
|
-
var DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc|txt)$/iu;
|
|
20622
|
-
var DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
|
|
20623
|
-
var INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
|
|
20624
|
-
var TEST_PATH = /(?:^|\/)(?:test|tests|__tests__|spec|specs|fixtures)\/|\.(?:test|spec)\.[^/]+$/iu;
|
|
20625
|
-
var basename = (file) => file.slice(file.lastIndexOf("/") + 1);
|
|
20626
|
-
var isDoc = (file) => !INSTRUCTIONS.test(file) && (DOC_EXT.test(file) || DOC_NAMES.test(basename(file)));
|
|
20627
|
-
function kindOf(file) {
|
|
20628
|
-
if (file.binary)
|
|
20629
|
-
return "behavior";
|
|
20630
|
-
if (file.generated)
|
|
20631
|
-
return "generated";
|
|
20632
|
-
if (LOCKFILES.has(basename(file.path)))
|
|
20633
|
-
return "lockfile";
|
|
20634
|
-
if (isDoc(file.path))
|
|
20635
|
-
return "doc";
|
|
20636
|
-
if (file.cosmetic)
|
|
20637
|
-
return "cosmetic";
|
|
20638
|
-
return TEST_PATH.test(file.path) ? "test" : "behavior";
|
|
20525
|
+
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)}`;
|
|
20526
|
+
function verdictBasis(verdict, current) {
|
|
20527
|
+
if (verdict.head && verdict.head === current.head)
|
|
20528
|
+
return "fresh";
|
|
20529
|
+
if (verdict.patchId && verdict.patchId === current.patchId && verdict.diffHash && verdict.diffHash === current.diffHash)
|
|
20530
|
+
return "carried";
|
|
20531
|
+
return "stale";
|
|
20532
|
+
}
|
|
20533
|
+
var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id);
|
|
20534
|
+
function trailerSessions(cwd, base, head) {
|
|
20535
|
+
if (!base || !head || !safeRef2(base) || !safeRef2(head))
|
|
20536
|
+
return [];
|
|
20537
|
+
const out = git4(cwd, [
|
|
20538
|
+
"log",
|
|
20539
|
+
"--format=%(trailers:key=Workit-Session,valueonly,separator=%x00)",
|
|
20540
|
+
`${base}..${head}`
|
|
20541
|
+
]);
|
|
20542
|
+
return (out ?? "").split(/[\0\n]/u).map((value) => value.trim()).filter(Boolean);
|
|
20639
20543
|
}
|
|
20640
|
-
var
|
|
20641
|
-
function
|
|
20642
|
-
|
|
20643
|
-
|
|
20644
|
-
|
|
20645
|
-
|
|
20646
|
-
|
|
20647
|
-
|
|
20648
|
-
|
|
20649
|
-
|
|
20650
|
-
}
|
|
20651
|
-
|
|
20652
|
-
|
|
20653
|
-
|
|
20654
|
-
|
|
20655
|
-
|
|
20544
|
+
var RENAMED = /^branch: renamed refs\/heads\/(.+) to refs\/heads\/(.+)$/iu;
|
|
20545
|
+
function branchScope(cwd, branch) {
|
|
20546
|
+
const renamedAt = new Map;
|
|
20547
|
+
let since = Number.NEGATIVE_INFINITY;
|
|
20548
|
+
if (safeRef2(branch)) {
|
|
20549
|
+
const log = git4(cwd, [
|
|
20550
|
+
"log",
|
|
20551
|
+
"-g",
|
|
20552
|
+
"--date=unix",
|
|
20553
|
+
"--format=%gd%x09%gs",
|
|
20554
|
+
`refs/heads/${branch}`,
|
|
20555
|
+
"--"
|
|
20556
|
+
]);
|
|
20557
|
+
for (const line of (log ?? "").split(`
|
|
20558
|
+
`)) {
|
|
20559
|
+
const [selector = "", subject = ""] = line.split("\t");
|
|
20560
|
+
const seconds = Number(/@\{(\d+)\}$/u.exec(selector)?.[1]);
|
|
20561
|
+
if (!Number.isFinite(seconds))
|
|
20562
|
+
continue;
|
|
20563
|
+
since = seconds * 1000;
|
|
20564
|
+
const match = RENAMED.exec(subject);
|
|
20565
|
+
if (!match || match[1] === branch)
|
|
20566
|
+
continue;
|
|
20567
|
+
const until = (seconds + 1) * 1000;
|
|
20568
|
+
renamedAt.set(match[1], Math.max(renamedAt.get(match[1]) ?? 0, until));
|
|
20656
20569
|
}
|
|
20657
|
-
regex = new RegExp(`^${source}$`, "u");
|
|
20658
|
-
globCache.set(pattern, regex);
|
|
20659
20570
|
}
|
|
20660
|
-
return
|
|
20571
|
+
return (row) => {
|
|
20572
|
+
if (row.branch === branch)
|
|
20573
|
+
return true;
|
|
20574
|
+
const until = row.branch === null ? undefined : renamedAt.get(row.branch);
|
|
20575
|
+
if (until === undefined)
|
|
20576
|
+
return false;
|
|
20577
|
+
const at = Date.parse(row.at);
|
|
20578
|
+
return at >= since && at <= until;
|
|
20579
|
+
};
|
|
20661
20580
|
}
|
|
20662
|
-
|
|
20663
|
-
|
|
20664
|
-
|
|
20665
|
-
|
|
20581
|
+
var renameAwareBranch = (scopeOf, branch) => {
|
|
20582
|
+
return (target) => {
|
|
20583
|
+
const own = typeof branch === "string" ? branch : null;
|
|
20584
|
+
const theirs = typeof target.branch === "string" ? target.branch : null;
|
|
20585
|
+
if (own === theirs)
|
|
20586
|
+
return true;
|
|
20587
|
+
if (own === null || theirs === null || typeof target.at !== "string")
|
|
20588
|
+
return false;
|
|
20589
|
+
return scopeOf(own)({ branch: theirs, at: target.at });
|
|
20590
|
+
};
|
|
20591
|
+
};
|
|
20592
|
+
function authorSessions(cwd, rows, branch, range = { base: null, head: null }, scope = branchScope(cwd, branch)) {
|
|
20593
|
+
const sessions = new Set;
|
|
20594
|
+
for (const row of rows) {
|
|
20595
|
+
if (!scope(row) || row.type !== "commit.recorded" && row.type !== "task.opened")
|
|
20596
|
+
continue;
|
|
20597
|
+
const session = str2(row.session) ?? row.actor.session;
|
|
20598
|
+
if (session)
|
|
20599
|
+
sessions.add(session);
|
|
20600
|
+
}
|
|
20601
|
+
for (const session of trailerSessions(cwd, range.base, range.head))
|
|
20602
|
+
sessions.add(session);
|
|
20603
|
+
return sessions;
|
|
20604
|
+
}
|
|
20605
|
+
var derivesFrom = (derived, bare) => !bare.includes(":") && derived.endsWith(`:${bare}`);
|
|
20606
|
+
var authoredBy = (authors, session) => {
|
|
20607
|
+
if (authors.has(session))
|
|
20666
20608
|
return true;
|
|
20667
|
-
|
|
20668
|
-
|
|
20669
|
-
|
|
20609
|
+
for (const author of authors)
|
|
20610
|
+
if (derivesFrom(author, session) || derivesFrom(session, author))
|
|
20611
|
+
return true;
|
|
20612
|
+
return false;
|
|
20613
|
+
};
|
|
20614
|
+
var TRUST_REASONS = new Set([
|
|
20615
|
+
"unsigned",
|
|
20616
|
+
"hash_mismatch",
|
|
20617
|
+
"broken_link",
|
|
20618
|
+
"duplicate_id",
|
|
20619
|
+
"unissued_session",
|
|
20620
|
+
"unbound_session"
|
|
20621
|
+
]);
|
|
20622
|
+
var SHELL_VOUCHED_HOSTS = new Set([
|
|
20623
|
+
"cli",
|
|
20624
|
+
"opencode",
|
|
20625
|
+
"pi",
|
|
20626
|
+
"codex_cli",
|
|
20627
|
+
"codex_desktop"
|
|
20628
|
+
]);
|
|
20629
|
+
function sessionTrust(cwd, row) {
|
|
20630
|
+
const session = row.actor.session;
|
|
20631
|
+
if (!session || row.self === true)
|
|
20632
|
+
return null;
|
|
20633
|
+
const source = typeof row.sessionSource === "string" ? row.sessionSource : "env";
|
|
20634
|
+
const host = row.actor.host;
|
|
20635
|
+
const issuedOnly = source === "flag" ? host !== "cli" : source === "env" && session.includes(":") && !SHELL_VOUCHED_HOSTS.has(host);
|
|
20636
|
+
if (!issuedOnly)
|
|
20637
|
+
return null;
|
|
20638
|
+
const binding = issuedBinding(cwd, session);
|
|
20639
|
+
if (binding === null)
|
|
20640
|
+
return "unissued_session";
|
|
20641
|
+
return binding === "token" && row.sessionBound !== true ? "unbound_session" : null;
|
|
20670
20642
|
}
|
|
20671
|
-
|
|
20672
|
-
|
|
20673
|
-
|
|
20674
|
-
|
|
20675
|
-
|
|
20676
|
-
const
|
|
20677
|
-
|
|
20678
|
-
|
|
20679
|
-
|
|
20680
|
-
|
|
20681
|
-
|
|
20682
|
-
const
|
|
20683
|
-
const
|
|
20684
|
-
|
|
20685
|
-
|
|
20686
|
-
|
|
20687
|
-
|
|
20688
|
-
|
|
20643
|
+
function trustReasons(cwd, row) {
|
|
20644
|
+
if (row.integrity === "unverified")
|
|
20645
|
+
return [row.integrityReason ?? "unsigned"];
|
|
20646
|
+
if (row.integrity !== "verified")
|
|
20647
|
+
return ["unsigned"];
|
|
20648
|
+
const session = sessionTrust(cwd, row);
|
|
20649
|
+
return session ? [session] : [];
|
|
20650
|
+
}
|
|
20651
|
+
function withIntegrity(cwd, rows) {
|
|
20652
|
+
if (rows.every((row) => row.integrity !== undefined))
|
|
20653
|
+
return rows;
|
|
20654
|
+
const read = readLedger(cwd, { integrity: true });
|
|
20655
|
+
const bySeq = new Map(read.ok ? read.value.rows.map((row) => [row.seq, row]) : []);
|
|
20656
|
+
return rows.map((row) => {
|
|
20657
|
+
if (row.integrity !== undefined)
|
|
20658
|
+
return row;
|
|
20659
|
+
const fresh = bySeq.get(row.seq);
|
|
20660
|
+
return fresh && fresh.id === row.id && fresh.at === row.at ? fresh : row;
|
|
20689
20661
|
});
|
|
20690
|
-
const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
|
|
20691
|
-
if (input.riskTier === "high")
|
|
20692
|
-
return result("full", ["the task judgment records high risk"]);
|
|
20693
|
-
if (riskFiles.length)
|
|
20694
|
-
return result("full", [
|
|
20695
|
-
`touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
|
|
20696
|
-
]);
|
|
20697
|
-
if (!behavior.length)
|
|
20698
|
-
return result("none", [
|
|
20699
|
-
input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
|
|
20700
|
-
]);
|
|
20701
|
-
if (lines >= fullMinLines)
|
|
20702
|
-
return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
|
|
20703
|
-
if (lines > selfMaxLines)
|
|
20704
|
-
return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
|
|
20705
|
-
if (behavior.length > selfMaxFiles)
|
|
20706
|
-
return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
|
|
20707
|
-
if (opaque.length)
|
|
20708
|
-
return result("focused", [
|
|
20709
|
-
`a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
|
|
20710
|
-
]);
|
|
20711
|
-
return result("self", [
|
|
20712
|
-
`small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
|
|
20713
|
-
]);
|
|
20714
20662
|
}
|
|
20715
|
-
|
|
20716
|
-
|
|
20717
|
-
|
|
20718
|
-
|
|
20719
|
-
|
|
20720
|
-
|
|
20721
|
-
|
|
20722
|
-
|
|
20723
|
-
|
|
20724
|
-
|
|
20725
|
-
|
|
20726
|
-
|
|
20727
|
-
|
|
20728
|
-
|
|
20663
|
+
var untrusted = (reasons) => reasons.some((reason) => TRUST_REASONS.has(reason));
|
|
20664
|
+
var independenceReasons = (row, authors) => {
|
|
20665
|
+
const reasons = [];
|
|
20666
|
+
if (!row.actor.session)
|
|
20667
|
+
reasons.push("no_session");
|
|
20668
|
+
else if (authoredBy(authors, row.actor.session))
|
|
20669
|
+
reasons.push("author_session");
|
|
20670
|
+
if (row.self === true && !reasons.includes("no_session"))
|
|
20671
|
+
reasons.push("self");
|
|
20672
|
+
return reasons;
|
|
20673
|
+
};
|
|
20674
|
+
function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
|
|
20675
|
+
let effective = null;
|
|
20676
|
+
for (const row of rows) {
|
|
20677
|
+
if (!effective) {
|
|
20678
|
+
effective = row;
|
|
20679
|
+
continue;
|
|
20680
|
+
}
|
|
20681
|
+
if (untrusted(reasonsOf(row)) && !untrusted(reasonsOf(effective)))
|
|
20682
|
+
continue;
|
|
20683
|
+
const rowIndependent = reasonsOf(row).length === 0;
|
|
20684
|
+
const effIndependent = reasonsOf(effective).length === 0;
|
|
20685
|
+
if (effIndependent && !rowIndependent && isCurrent(effective))
|
|
20686
|
+
continue;
|
|
20687
|
+
if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session && sameCode(effective, row))
|
|
20688
|
+
continue;
|
|
20689
|
+
effective = row;
|
|
20729
20690
|
}
|
|
20691
|
+
return effective;
|
|
20730
20692
|
}
|
|
20731
|
-
var
|
|
20732
|
-
|
|
20733
|
-
|
|
20734
|
-
|
|
20735
|
-
|
|
20736
|
-
|
|
20737
|
-
|
|
20738
|
-
|
|
20739
|
-
|
|
20740
|
-
|
|
20741
|
-
|
|
20742
|
-
|
|
20743
|
-
|
|
20744
|
-
|
|
20745
|
-
|
|
20746
|
-
|
|
20747
|
-
|
|
20748
|
-
|
|
20749
|
-
|
|
20750
|
-
|
|
20693
|
+
var restackExact = new WeakMap;
|
|
20694
|
+
function restackCarries(exactIn, rows, branch, from, to) {
|
|
20695
|
+
if (!from || !to)
|
|
20696
|
+
return false;
|
|
20697
|
+
const exact = (row, fromHead, head) => {
|
|
20698
|
+
if (exactIn === null)
|
|
20699
|
+
return true;
|
|
20700
|
+
const cwd = exactIn;
|
|
20701
|
+
let known = restackExact.get(row);
|
|
20702
|
+
if (known === undefined) {
|
|
20703
|
+
known = typeof row.fromBase === "string" && typeof row.toBase === "string" && sameOwnChange(cwd, {
|
|
20704
|
+
from: fromHead,
|
|
20705
|
+
to: head,
|
|
20706
|
+
fromBase: row.fromBase,
|
|
20707
|
+
toBase: row.toBase
|
|
20708
|
+
});
|
|
20709
|
+
restackExact.set(row, known);
|
|
20710
|
+
}
|
|
20711
|
+
return known;
|
|
20712
|
+
};
|
|
20713
|
+
const edges = new Map;
|
|
20714
|
+
for (const row of rows)
|
|
20715
|
+
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))
|
|
20716
|
+
edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
|
|
20717
|
+
const seen = new Set([from]);
|
|
20718
|
+
const queue = [from];
|
|
20719
|
+
while (queue.length) {
|
|
20720
|
+
const next = queue.shift();
|
|
20721
|
+
if (next === to)
|
|
20722
|
+
return true;
|
|
20723
|
+
for (const target of edges.get(next) ?? [])
|
|
20724
|
+
if (!seen.has(target)) {
|
|
20725
|
+
seen.add(target);
|
|
20726
|
+
queue.push(target);
|
|
20751
20727
|
}
|
|
20752
|
-
if (before && after && cosmeticChange(entry.path, before, after))
|
|
20753
|
-
out.add(entry.path);
|
|
20754
|
-
else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
|
|
20755
|
-
opaque.add(entry.path);
|
|
20756
20728
|
}
|
|
20757
|
-
return
|
|
20729
|
+
return false;
|
|
20758
20730
|
}
|
|
20759
|
-
function
|
|
20760
|
-
const
|
|
20761
|
-
const
|
|
20762
|
-
|
|
20763
|
-
|
|
20764
|
-
const
|
|
20765
|
-
|
|
20766
|
-
|
|
20767
|
-
|
|
20768
|
-
|
|
20731
|
+
function checkVerdicts(cwd, branch, ledgerRows) {
|
|
20732
|
+
const rows = withIntegrity(cwd, ledgerRows);
|
|
20733
|
+
const head = branchHead(cwd, branch);
|
|
20734
|
+
const fallbackBase = defaultBase(cwd);
|
|
20735
|
+
const scope = branchScope(cwd, branch);
|
|
20736
|
+
const keyCache = new Map;
|
|
20737
|
+
const keysAt = (base, at) => {
|
|
20738
|
+
if (!base || !at)
|
|
20739
|
+
return { patchId: null, diffHash: null };
|
|
20740
|
+
const cacheKey = `${base}\x00${at}`;
|
|
20741
|
+
let key = keyCache.get(cacheKey);
|
|
20742
|
+
if (!key) {
|
|
20743
|
+
key = { patchId: patchId(cwd, base, at), diffHash: diffHash(cwd, base, at) };
|
|
20744
|
+
keyCache.set(cacheKey, key);
|
|
20745
|
+
}
|
|
20746
|
+
return key;
|
|
20747
|
+
};
|
|
20748
|
+
const keyFor = (base) => keysAt(base, head);
|
|
20749
|
+
const driftCache = new Map;
|
|
20750
|
+
const driftOf = (row) => {
|
|
20751
|
+
if (driftCache.has(row))
|
|
20752
|
+
return driftCache.get(row) ?? null;
|
|
20753
|
+
let drift = null;
|
|
20754
|
+
const toBase = row.base ?? fallbackBase;
|
|
20755
|
+
const toBaseSha = toBase ? resolveCommit(cwd, toBase) : null;
|
|
20756
|
+
const fromBase = row.baseSha ?? toBaseSha;
|
|
20757
|
+
if (row.head && head && row.head !== head && toBaseSha && fromBase && !untrusted(reasonsOf(row)))
|
|
20758
|
+
drift = verdictDrift(cwd, {
|
|
20759
|
+
from: row.head,
|
|
20760
|
+
to: head,
|
|
20761
|
+
fromBase,
|
|
20762
|
+
toBase: toBaseSha,
|
|
20763
|
+
kind: typeof row.kind === "string" ? row.kind : "review"
|
|
20764
|
+
});
|
|
20765
|
+
driftCache.set(row, drift);
|
|
20766
|
+
return drift;
|
|
20767
|
+
};
|
|
20768
|
+
const exactCache = new Map;
|
|
20769
|
+
const exactCarry = (row) => {
|
|
20770
|
+
if (FAILING.has(String(row.result)))
|
|
20771
|
+
return true;
|
|
20772
|
+
let exact = exactCache.get(row);
|
|
20773
|
+
if (exact === undefined) {
|
|
20774
|
+
const toBase = row.base ?? fallbackBase;
|
|
20775
|
+
const toBaseSha = toBase ? resolveCommit(cwd, toBase) : null;
|
|
20776
|
+
const fromBase = row.baseSha ?? toBaseSha;
|
|
20777
|
+
exact = Boolean(row.head && head && toBaseSha && fromBase && sameOwnChange(cwd, { from: row.head, to: head, fromBase, toBase: toBaseSha }));
|
|
20778
|
+
exactCache.set(row, exact);
|
|
20779
|
+
}
|
|
20780
|
+
return exact;
|
|
20781
|
+
};
|
|
20782
|
+
const keyBasis = (row) => {
|
|
20783
|
+
const basis = verdictBasis(row, { head, ...keyFor(row.base ?? fallbackBase) });
|
|
20784
|
+
return basis === "carried" && !exactCarry(row) ? "stale" : basis;
|
|
20785
|
+
};
|
|
20786
|
+
const restacked = (row) => restackCarries(FAILING.has(String(row.result)) ? null : cwd, rows, branch, row.head, head);
|
|
20787
|
+
const carryOf = (row) => {
|
|
20788
|
+
if (keyBasis(row) !== "stale")
|
|
20789
|
+
return null;
|
|
20790
|
+
if (restacked(row))
|
|
20791
|
+
return null;
|
|
20792
|
+
const drift = driftOf(row);
|
|
20793
|
+
return drift?.carries ? { rule: drift.rule, since: drift.since, files: drift.files } : null;
|
|
20794
|
+
};
|
|
20795
|
+
const basisOf = (row) => {
|
|
20796
|
+
const basis = keyBasis(row);
|
|
20797
|
+
if (basis !== "stale")
|
|
20798
|
+
return basis;
|
|
20799
|
+
return restacked(row) || carryOf(row) ? "carried" : basis;
|
|
20800
|
+
};
|
|
20801
|
+
const sameCode = (failed, row) => {
|
|
20802
|
+
if (failed.head && failed.head === row.head)
|
|
20803
|
+
return true;
|
|
20804
|
+
const base = failed.base ?? fallbackBase;
|
|
20805
|
+
const stored = (row.base ?? fallbackBase) === base && row.baseSha !== null && row.baseSha === failed.baseSha;
|
|
20806
|
+
const keys = stored ? { patchId: row.patchId, diffHash: row.diffHash } : keysAt(base, row.head);
|
|
20807
|
+
return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(null, rows, branch, failed.head, row.head);
|
|
20808
|
+
};
|
|
20809
|
+
const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
|
|
20810
|
+
const failureAuthorCache = new Map;
|
|
20811
|
+
const failureAuthors = (row) => {
|
|
20812
|
+
let set = failureAuthorCache.get(row.seq);
|
|
20813
|
+
if (!set) {
|
|
20814
|
+
set = authorSessions(cwd, rows.filter((other) => other.integrity === "verified" && other.seq < row.seq), branch, { base: null, head: null }, scope);
|
|
20815
|
+
failureAuthorCache.set(row.seq, set);
|
|
20816
|
+
}
|
|
20817
|
+
return set;
|
|
20818
|
+
};
|
|
20819
|
+
const reasonCache = new Map;
|
|
20820
|
+
const reasonsOf = (row) => {
|
|
20821
|
+
let reasons = reasonCache.get(row);
|
|
20822
|
+
if (!reasons) {
|
|
20823
|
+
const failing = FAILING.has(String(row.result));
|
|
20824
|
+
reasons = [
|
|
20825
|
+
...trustReasons(cwd, row),
|
|
20826
|
+
...independenceReasons(row, failing ? failureAuthors(row) : authors)
|
|
20827
|
+
];
|
|
20828
|
+
reasonCache.set(row, reasons);
|
|
20829
|
+
}
|
|
20830
|
+
return reasons;
|
|
20831
|
+
};
|
|
20832
|
+
const retired = new Set;
|
|
20833
|
+
for (const row of rows)
|
|
20834
|
+
if (row.type === "verdict" && row.supersedes && !row.supersedeIgnored && trustReasons(cwd, row).length === 0)
|
|
20835
|
+
retired.add(row.supersedes);
|
|
20836
|
+
const byKind = new Map;
|
|
20837
|
+
for (const row of rows)
|
|
20838
|
+
if (isLiveVerdict(row, retired) && scope(row)) {
|
|
20839
|
+
const kind = typeof row.kind === "string" ? row.kind : "review";
|
|
20840
|
+
byKind.set(kind, [...byKind.get(kind) ?? [], row]);
|
|
20841
|
+
}
|
|
20842
|
+
const verdicts = [];
|
|
20843
|
+
const warnings = [];
|
|
20844
|
+
for (const [kind, list] of byKind) {
|
|
20845
|
+
const row = effectiveOf(list, reasonsOf, sameCode, (candidate) => basisOf(candidate) !== "stale");
|
|
20846
|
+
if (!row)
|
|
20847
|
+
continue;
|
|
20848
|
+
const basis = basisOf(row);
|
|
20849
|
+
const current = basis !== "stale";
|
|
20850
|
+
const independence = reasonsOf(row);
|
|
20851
|
+
const reasons = [
|
|
20852
|
+
...current ? [] : ["stale"],
|
|
20853
|
+
...PASSING.has(String(row.result)) ? [] : ["not_passing"],
|
|
20854
|
+
...independence
|
|
20855
|
+
];
|
|
20856
|
+
verdicts.push({
|
|
20857
|
+
kind,
|
|
20858
|
+
basis,
|
|
20859
|
+
current,
|
|
20860
|
+
independent: independence.length === 0,
|
|
20861
|
+
accepted: reasons.length === 0,
|
|
20862
|
+
reasons,
|
|
20863
|
+
carry: basis === "carried" ? carryOf(row) : null,
|
|
20864
|
+
verdict: row
|
|
20865
|
+
});
|
|
20866
|
+
if (reasons.length === 0) {
|
|
20867
|
+
for (const later of list)
|
|
20868
|
+
if (later.seq > row.seq && reasonsOf(later).length > 0 && !untrusted(reasonsOf(later)) && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
|
|
20869
|
+
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`);
|
|
20870
|
+
}
|
|
20769
20871
|
}
|
|
20770
|
-
|
|
20771
|
-
|
|
20772
|
-
|
|
20773
|
-
|
|
20774
|
-
|
|
20775
|
-
|
|
20776
|
-
|
|
20777
|
-
|
|
20778
|
-
|
|
20779
|
-
|
|
20780
|
-
...entry,
|
|
20781
|
-
...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
|
|
20782
|
-
cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
|
|
20783
|
-
})),
|
|
20784
|
-
...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
|
|
20785
|
-
];
|
|
20786
|
-
const generated = generatedAt(cwd, fork, files.map((entry) => entry.path));
|
|
20787
|
-
return files.map((entry) => ({
|
|
20788
|
-
...entry,
|
|
20789
|
-
generated: generated.has(entry.path)
|
|
20790
|
-
}));
|
|
20791
|
-
}
|
|
20792
|
-
function branchLane(cwd, options) {
|
|
20793
|
-
const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
|
|
20794
|
-
const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
|
|
20795
|
-
const head = options.workingTree ? null : options.head;
|
|
20796
|
-
const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
|
|
20797
|
-
const files = fork ? laneFiles(cwd, fork, head) : null;
|
|
20798
|
-
const riskTier = options.judgment?.riskTier ?? null;
|
|
20799
|
-
if (!fork || !files) {
|
|
20800
|
-
const high = riskTier === "high";
|
|
20801
|
-
return {
|
|
20802
|
-
lane: high ? "full" : "focused",
|
|
20803
|
-
reasons: [
|
|
20804
|
-
`the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
|
|
20805
|
-
...high ? ["the task judgment records high risk"] : []
|
|
20806
|
-
],
|
|
20807
|
-
lines: 0,
|
|
20808
|
-
files: 0,
|
|
20809
|
-
riskFiles: [],
|
|
20810
|
-
base,
|
|
20811
|
-
head: options.head
|
|
20812
|
-
};
|
|
20872
|
+
for (const row of rows)
|
|
20873
|
+
if (row.integrity === "unverified" && scope(row))
|
|
20874
|
+
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)`);
|
|
20875
|
+
const tampered = ledgerTampering(cwd);
|
|
20876
|
+
if (tampered.length) {
|
|
20877
|
+
warnings.push(tamperedWarning(cwd, tampered));
|
|
20878
|
+
for (const entry of verdicts) {
|
|
20879
|
+
entry.accepted = false;
|
|
20880
|
+
entry.reasons = [...entry.reasons, "ledger_tampered"];
|
|
20881
|
+
}
|
|
20813
20882
|
}
|
|
20814
|
-
|
|
20883
|
+
verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
|
|
20884
|
+
const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
|
|
20885
|
+
const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
|
|
20886
|
+
const newestAccepted = verdicts.findLast((entry) => entry.accepted && STRONG_RESULTS.has(String(entry.verdict.result))) ?? null;
|
|
20887
|
+
const accepted = tampered.length ? { accepted: false, verdict: null, reasons: ["ledger_tampered"] } : newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
20888
|
+
accepted: false,
|
|
20889
|
+
verdict: failing?.verdict ?? null,
|
|
20890
|
+
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
20891
|
+
};
|
|
20892
|
+
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))));
|
|
20893
|
+
const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
|
|
20894
|
+
const carryEntry = verdicts.find((entry) => entry.verdict === accepted.verdict && entry.accepted) ?? newestCurrent;
|
|
20895
|
+
const lastPass = accepted.accepted || failing || tampered.length ? undefined : verdicts.findLast((entry) => !entry.current && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
20896
|
+
const drift = lastPass ? driftOf(lastPass.verdict) : null;
|
|
20897
|
+
const reverify = drift && !drift.carries && drift.focused && drift.files ? { scope: "focused", since: drift.since, files: drift.files } : null;
|
|
20815
20898
|
return {
|
|
20816
|
-
|
|
20817
|
-
|
|
20818
|
-
|
|
20819
|
-
|
|
20820
|
-
|
|
20821
|
-
base,
|
|
20822
|
-
|
|
20899
|
+
branch,
|
|
20900
|
+
head,
|
|
20901
|
+
review: accepted.accepted ? "verified" : selfEntry ? "self-reviewed" : "unreviewed",
|
|
20902
|
+
selfVerdict: accepted.accepted ? null : selfEntry?.verdict ?? null,
|
|
20903
|
+
base: newestCurrent?.verdict.base ?? fallbackBase,
|
|
20904
|
+
patchId: keyFor(newestCurrent?.verdict.base ?? fallbackBase).patchId,
|
|
20905
|
+
current: { basis: newestCurrent?.basis ?? "none", verdict: newestCurrent?.verdict ?? null },
|
|
20906
|
+
carry: carryEntry?.carry ?? null,
|
|
20907
|
+
reverify,
|
|
20908
|
+
accepted,
|
|
20909
|
+
verdicts,
|
|
20910
|
+
authors: [...authors].toSorted(),
|
|
20911
|
+
warnings
|
|
20823
20912
|
};
|
|
20824
20913
|
}
|
|
20825
|
-
var
|
|
20914
|
+
var OBSERVED_ONLY = new Set([
|
|
20915
|
+
"check",
|
|
20916
|
+
"merge.unverified",
|
|
20917
|
+
"policy.judged",
|
|
20918
|
+
...PR_ROW_TYPES
|
|
20919
|
+
]);
|
|
20826
20920
|
|
|
20827
20921
|
// packages/workit-core/src/core/policy/derive.ts
|
|
20828
20922
|
var RULES = {
|
|
@@ -20977,7 +21071,7 @@ var productDecision = (task, ledger) => {
|
|
|
20977
21071
|
};
|
|
20978
21072
|
};
|
|
20979
21073
|
var planRecorded = (task, root) => {
|
|
20980
|
-
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync5(
|
|
21074
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync5(path16.resolve(root, ref.path)));
|
|
20981
21075
|
if (cited)
|
|
20982
21076
|
return {
|
|
20983
21077
|
met: true,
|
|
@@ -21044,14 +21138,14 @@ function derivedRuleStatus(task, requirement, root, ledger) {
|
|
|
21044
21138
|
|
|
21045
21139
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
21046
21140
|
var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
|
|
21047
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
21048
|
-
var relative = (root, candidate) =>
|
|
21141
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path17.sep}`);
|
|
21142
|
+
var relative = (root, candidate) => path17.relative(root, candidate).split(path17.sep).join("/") || ".";
|
|
21049
21143
|
var canonicalPath = (value) => {
|
|
21050
21144
|
if (typeof value !== "string" || !value)
|
|
21051
21145
|
return null;
|
|
21052
21146
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
21053
21147
|
return null;
|
|
21054
|
-
const normalized =
|
|
21148
|
+
const normalized = path17.posix.normalize(value);
|
|
21055
21149
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
21056
21150
|
return null;
|
|
21057
21151
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -21087,7 +21181,7 @@ var compareCodeUnits2 = (left, right) => {
|
|
|
21087
21181
|
return left.length - right.length;
|
|
21088
21182
|
};
|
|
21089
21183
|
var gitPaths = (root) => {
|
|
21090
|
-
const result =
|
|
21184
|
+
const result = spawnSync10("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
21091
21185
|
cwd: root,
|
|
21092
21186
|
encoding: "buffer"
|
|
21093
21187
|
});
|
|
@@ -21099,21 +21193,21 @@ var gitPaths = (root) => {
|
|
|
21099
21193
|
git: !/not a git repository/i.test(stderr)
|
|
21100
21194
|
};
|
|
21101
21195
|
}
|
|
21102
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
21103
|
-
const stagedDeleted =
|
|
21196
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path17.sep).join("/")) : [];
|
|
21197
|
+
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
21104
21198
|
if (stagedDeleted.status !== 0) {
|
|
21105
21199
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
21106
21200
|
if (!/not a git repository/i.test(stderr))
|
|
21107
21201
|
return { paths, uncertain: true, git: true };
|
|
21108
21202
|
} else if (stagedDeleted.stdout) {
|
|
21109
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
21203
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path17.sep).join("/")));
|
|
21110
21204
|
}
|
|
21111
21205
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
21112
21206
|
};
|
|
21113
21207
|
var walk = (root, directory, output) => {
|
|
21114
21208
|
let names;
|
|
21115
21209
|
try {
|
|
21116
|
-
names =
|
|
21210
|
+
names = fs13.readdirSync(directory);
|
|
21117
21211
|
} catch {
|
|
21118
21212
|
return true;
|
|
21119
21213
|
}
|
|
@@ -21121,10 +21215,10 @@ var walk = (root, directory, output) => {
|
|
|
21121
21215
|
for (const name of names) {
|
|
21122
21216
|
if (name === ".git" || name === ".workit")
|
|
21123
21217
|
continue;
|
|
21124
|
-
const target =
|
|
21218
|
+
const target = path17.join(directory, name);
|
|
21125
21219
|
let stat;
|
|
21126
21220
|
try {
|
|
21127
|
-
stat =
|
|
21221
|
+
stat = fs13.lstatSync(target);
|
|
21128
21222
|
} catch {
|
|
21129
21223
|
uncertain = true;
|
|
21130
21224
|
continue;
|
|
@@ -21139,14 +21233,14 @@ var walk = (root, directory, output) => {
|
|
|
21139
21233
|
var scopeRoots = (root, scope) => {
|
|
21140
21234
|
const roots = [];
|
|
21141
21235
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
21142
|
-
const target =
|
|
21236
|
+
const target = path17.resolve(root, item);
|
|
21143
21237
|
if (!inside(root, target))
|
|
21144
21238
|
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` }] });
|
|
21145
21239
|
let ancestor = target;
|
|
21146
|
-
while (!
|
|
21147
|
-
ancestor =
|
|
21240
|
+
while (!fs13.existsSync(ancestor) && ancestor !== root)
|
|
21241
|
+
ancestor = path17.dirname(ancestor);
|
|
21148
21242
|
try {
|
|
21149
|
-
if (!inside(root,
|
|
21243
|
+
if (!inside(root, fs13.realpathSync(ancestor)))
|
|
21150
21244
|
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` }] });
|
|
21151
21245
|
} catch {
|
|
21152
21246
|
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
@@ -21158,7 +21252,7 @@ var scopeRoots = (root, scope) => {
|
|
|
21158
21252
|
function captureCandidate(root, scope, environment = []) {
|
|
21159
21253
|
let checkout;
|
|
21160
21254
|
try {
|
|
21161
|
-
checkout =
|
|
21255
|
+
checkout = fs13.realpathSync(root);
|
|
21162
21256
|
} catch {
|
|
21163
21257
|
return failure2("invalid_input", "candidate root does not exist");
|
|
21164
21258
|
}
|
|
@@ -21176,11 +21270,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
21176
21270
|
if (item === ".git" || item === ".workit")
|
|
21177
21271
|
continue;
|
|
21178
21272
|
names.add(item);
|
|
21179
|
-
if (!
|
|
21273
|
+
if (!fs13.existsSync(target))
|
|
21180
21274
|
continue;
|
|
21181
21275
|
let stat;
|
|
21182
21276
|
try {
|
|
21183
|
-
stat =
|
|
21277
|
+
stat = fs13.lstatSync(target);
|
|
21184
21278
|
} catch {
|
|
21185
21279
|
uncertain = true;
|
|
21186
21280
|
continue;
|
|
@@ -21196,17 +21290,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
21196
21290
|
for (const item of [...names].toSorted()) {
|
|
21197
21291
|
if (!scopeMatches(item, normalizedScope))
|
|
21198
21292
|
continue;
|
|
21199
|
-
const target =
|
|
21293
|
+
const target = path17.join(checkout, item);
|
|
21200
21294
|
try {
|
|
21201
|
-
const stat =
|
|
21295
|
+
const stat = fs13.lstatSync(target);
|
|
21202
21296
|
if (stat.isSymbolicLink()) {
|
|
21203
|
-
const link =
|
|
21297
|
+
const link = fs13.readlinkSync(target);
|
|
21204
21298
|
files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
|
|
21205
21299
|
} else if (stat.isFile()) {
|
|
21206
21300
|
files.push({
|
|
21207
21301
|
path: item,
|
|
21208
21302
|
kind: "file",
|
|
21209
|
-
digest: digestBytes(
|
|
21303
|
+
digest: digestBytes(fs13.readFileSync(target)),
|
|
21210
21304
|
executable: (stat.mode & 73) !== 0
|
|
21211
21305
|
});
|
|
21212
21306
|
} else if (!stat.isDirectory()) {
|
|
@@ -21231,7 +21325,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
21231
21325
|
const values = supplied.toSorted((left, right) => compareCodeUnits2(left.name, right.name)).map(({ name, value }) => ({ name, value, refs: [] }));
|
|
21232
21326
|
if (values.some(({ value }) => value === null))
|
|
21233
21327
|
completeness = "uncertain";
|
|
21234
|
-
const headResult =
|
|
21328
|
+
const headResult = spawnSync10("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
|
|
21235
21329
|
if (headResult.status !== 0 && !/not a git repository/i.test(headResult.stderr ?? ""))
|
|
21236
21330
|
completeness = "uncertain";
|
|
21237
21331
|
const head = headResult.status === 0 ? headResult.stdout.trim() : null;
|
|
@@ -21371,7 +21465,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
|
|
|
21371
21465
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
21372
21466
|
let root;
|
|
21373
21467
|
try {
|
|
21374
|
-
root =
|
|
21468
|
+
root = fs13.realpathSync(checkoutRoot);
|
|
21375
21469
|
} catch {
|
|
21376
21470
|
return failure2("permission_denied", "checkout root is unavailable");
|
|
21377
21471
|
}
|
|
@@ -21380,23 +21474,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
21380
21474
|
continue;
|
|
21381
21475
|
if (!reference.digest)
|
|
21382
21476
|
return failure2("invalid_input", "document references require a byte digest");
|
|
21383
|
-
const target =
|
|
21384
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
21477
|
+
const target = path17.resolve(root, reference.path);
|
|
21478
|
+
if (target !== root && !target.startsWith(`${root}${path17.sep}`))
|
|
21385
21479
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
21386
21480
|
fields: [
|
|
21387
21481
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
21388
21482
|
]
|
|
21389
21483
|
});
|
|
21390
21484
|
try {
|
|
21391
|
-
const below =
|
|
21485
|
+
const below = path17.relative(root, target);
|
|
21392
21486
|
let current = root;
|
|
21393
|
-
for (const segment of below.split(
|
|
21487
|
+
for (const segment of below.split(path17.sep)) {
|
|
21394
21488
|
if (!segment)
|
|
21395
21489
|
continue;
|
|
21396
|
-
current =
|
|
21397
|
-
const stat =
|
|
21398
|
-
const real =
|
|
21399
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
21490
|
+
current = path17.join(current, segment);
|
|
21491
|
+
const stat = fs13.lstatSync(current);
|
|
21492
|
+
const real = fs13.realpathSync(current);
|
|
21493
|
+
if (real !== root && !real.startsWith(`${root}${path17.sep}`))
|
|
21400
21494
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
21401
21495
|
fields: [
|
|
21402
21496
|
{
|
|
@@ -21412,7 +21506,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
21412
21506
|
if (current !== target && !stat.isDirectory())
|
|
21413
21507
|
return failure2("invalid_input", "document reference ancestor is not a directory");
|
|
21414
21508
|
}
|
|
21415
|
-
if (digestBytes(
|
|
21509
|
+
if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
|
|
21416
21510
|
return failure2("permission_denied", "decision document bytes have changed");
|
|
21417
21511
|
} catch {
|
|
21418
21512
|
return failure2("invalid_input", "decision document is unavailable");
|
|
@@ -22255,7 +22349,7 @@ var judgeInputSummary = (raw) => {
|
|
|
22255
22349
|
|
|
22256
22350
|
// packages/workit-core/src/autonomy.ts
|
|
22257
22351
|
import os4 from "node:os";
|
|
22258
|
-
import
|
|
22352
|
+
import path18 from "node:path";
|
|
22259
22353
|
var accountHome = () => {
|
|
22260
22354
|
try {
|
|
22261
22355
|
return os4.userInfo().homedir;
|
|
@@ -22264,10 +22358,10 @@ var accountHome = () => {
|
|
|
22264
22358
|
}
|
|
22265
22359
|
};
|
|
22266
22360
|
var grantsHome = { resolve: accountHome };
|
|
22267
|
-
var grantsDir = () =>
|
|
22361
|
+
var grantsDir = () => path18.join(grantsHome.resolve(), ".config", "workit");
|
|
22268
22362
|
var grantsOverride = () => {
|
|
22269
|
-
const configured =
|
|
22270
|
-
return configured ===
|
|
22363
|
+
const configured = path18.resolve(resolveConfigDir());
|
|
22364
|
+
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`;
|
|
22271
22365
|
};
|
|
22272
22366
|
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
22273
22367
|
var DEFAULT_GRANTS = Object.freeze({
|
|
@@ -24076,9 +24170,9 @@ class WorkitCore {
|
|
|
24076
24170
|
}
|
|
24077
24171
|
}
|
|
24078
24172
|
// packages/workit-core/src/core/repo-context.ts
|
|
24079
|
-
import { spawnSync as
|
|
24173
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
24080
24174
|
import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "node:fs";
|
|
24081
|
-
import
|
|
24175
|
+
import path19 from "node:path";
|
|
24082
24176
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
24083
24177
|
try {
|
|
24084
24178
|
gitRevisionParts(value);
|
|
@@ -24105,7 +24199,7 @@ var invalidRange = (cwd) => ({
|
|
|
24105
24199
|
cwd
|
|
24106
24200
|
});
|
|
24107
24201
|
var runGit = (cwd, args) => {
|
|
24108
|
-
const result =
|
|
24202
|
+
const result = spawnSync11("git", args, { cwd, encoding: "utf8" });
|
|
24109
24203
|
return {
|
|
24110
24204
|
stdout: (result.stdout ?? "").trimEnd(),
|
|
24111
24205
|
stderr: (result.stderr ?? "").trimEnd(),
|
|
@@ -24212,7 +24306,7 @@ var findPrTemplate = (cwd) => {
|
|
|
24212
24306
|
]) {
|
|
24213
24307
|
const parts = rel.split("/");
|
|
24214
24308
|
const base = parts[parts.length - 1];
|
|
24215
|
-
const dir = parts.length > 1 ?
|
|
24309
|
+
const dir = parts.length > 1 ? path19.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
24216
24310
|
const actual = probeCaseInsensitive(dir, base);
|
|
24217
24311
|
if (actual)
|
|
24218
24312
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -24243,7 +24337,7 @@ var diffStatForRange = (cwd, range) => {
|
|
|
24243
24337
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
24244
24338
|
};
|
|
24245
24339
|
var packageScripts = (cwd, keys) => {
|
|
24246
|
-
const pkgPath =
|
|
24340
|
+
const pkgPath = path19.join(cwd, "package.json");
|
|
24247
24341
|
if (!existsSync7(pkgPath))
|
|
24248
24342
|
return [];
|
|
24249
24343
|
try {
|
|
@@ -24269,9 +24363,9 @@ var documentationFiles = (cwd) => {
|
|
|
24269
24363
|
if (entry.isDirectory()) {
|
|
24270
24364
|
if (excluded.has(entry.name))
|
|
24271
24365
|
continue;
|
|
24272
|
-
walk(
|
|
24366
|
+
walk(path19.join(dir, entry.name), depth + 1);
|
|
24273
24367
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
24274
|
-
matches.push("./" +
|
|
24368
|
+
matches.push("./" + path19.relative(cwd, path19.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
24275
24369
|
}
|
|
24276
24370
|
}
|
|
24277
24371
|
};
|
|
@@ -24287,7 +24381,7 @@ var readTrimmed2 = (file, maxLines) => {
|
|
|
24287
24381
|
`);
|
|
24288
24382
|
};
|
|
24289
24383
|
function prReadyContext(root, range) {
|
|
24290
|
-
const cwd =
|
|
24384
|
+
const cwd = path19.resolve(repoRoot(root));
|
|
24291
24385
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
24292
24386
|
return invalidRange(cwd);
|
|
24293
24387
|
let stdout = "";
|
|
@@ -24345,7 +24439,7 @@ ${status.stderr}` : "");
|
|
|
24345
24439
|
stdout += `template_path: ${template}
|
|
24346
24440
|
|
|
24347
24441
|
`;
|
|
24348
|
-
stdout += readTrimmed2(
|
|
24442
|
+
stdout += readTrimmed2(path19.join(cwd, template), 220);
|
|
24349
24443
|
} else {
|
|
24350
24444
|
stdout += `template_path: none
|
|
24351
24445
|
|
|
@@ -24355,7 +24449,7 @@ ${status.stderr}` : "");
|
|
|
24355
24449
|
stdout += `
|
|
24356
24450
|
`;
|
|
24357
24451
|
stdout += printSection("Recent Validation Signals");
|
|
24358
|
-
if (existsSync7(
|
|
24452
|
+
if (existsSync7(path19.join(cwd, "package.json"))) {
|
|
24359
24453
|
stdout += `package.json detected. Common scripts:
|
|
24360
24454
|
`;
|
|
24361
24455
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -24364,7 +24458,7 @@ ${status.stderr}` : "");
|
|
|
24364
24458
|
`) + `
|
|
24365
24459
|
`;
|
|
24366
24460
|
}
|
|
24367
|
-
if (existsSync7(
|
|
24461
|
+
if (existsSync7(path19.join(cwd, "Cargo.toml")) || existsSync7(path19.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
24368
24462
|
stdout += `Rust project detected.
|
|
24369
24463
|
`;
|
|
24370
24464
|
}
|
|
@@ -24397,7 +24491,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
24397
24491
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
24398
24492
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
24399
24493
|
function changelogContext(root, range) {
|
|
24400
|
-
const cwd =
|
|
24494
|
+
const cwd = path19.resolve(repoRoot(root));
|
|
24401
24495
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
24402
24496
|
return invalidRange(cwd);
|
|
24403
24497
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -24415,7 +24509,7 @@ function changelogContext(root, range) {
|
|
|
24415
24509
|
stdout += CHANGELOG_RULES + `
|
|
24416
24510
|
`;
|
|
24417
24511
|
stdout += printSection("Existing CHANGELOG.md");
|
|
24418
|
-
const changelogPath =
|
|
24512
|
+
const changelogPath = path19.join(cwd, "CHANGELOG.md");
|
|
24419
24513
|
if (existsSync7(changelogPath)) {
|
|
24420
24514
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
24421
24515
|
`;
|
|
@@ -24435,7 +24529,7 @@ function changelogContext(root, range) {
|
|
|
24435
24529
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
24436
24530
|
}
|
|
24437
24531
|
function docsRefreshContext(root, range) {
|
|
24438
|
-
const cwd =
|
|
24532
|
+
const cwd = path19.resolve(repoRoot(root));
|
|
24439
24533
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
24440
24534
|
return invalidRange(cwd);
|
|
24441
24535
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -24457,7 +24551,7 @@ function docsRefreshContext(root, range) {
|
|
|
24457
24551
|
`) + `
|
|
24458
24552
|
`;
|
|
24459
24553
|
stdout += printSection("README Preview");
|
|
24460
|
-
const readme =
|
|
24554
|
+
const readme = path19.join(cwd, "README.md");
|
|
24461
24555
|
if (existsSync7(readme)) {
|
|
24462
24556
|
stdout += readTrimmed2(readme, 220) + `
|
|
24463
24557
|
`;
|
|
@@ -24466,7 +24560,7 @@ function docsRefreshContext(root, range) {
|
|
|
24466
24560
|
`;
|
|
24467
24561
|
}
|
|
24468
24562
|
stdout += printSection("Package Scripts");
|
|
24469
|
-
const pkgPath =
|
|
24563
|
+
const pkgPath = path19.join(cwd, "package.json");
|
|
24470
24564
|
if (existsSync7(pkgPath)) {
|
|
24471
24565
|
try {
|
|
24472
24566
|
const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
|
|
@@ -24480,7 +24574,7 @@ function docsRefreshContext(root, range) {
|
|
|
24480
24574
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
24481
24575
|
}
|
|
24482
24576
|
function releaseNotesContext(root, rangeOrTag) {
|
|
24483
|
-
const cwd =
|
|
24577
|
+
const cwd = path19.resolve(repoRoot(root));
|
|
24484
24578
|
if (!rangeOrTag) {
|
|
24485
24579
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
24486
24580
|
`, exitCode: 1, cwd };
|
|
@@ -24518,7 +24612,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
24518
24612
|
`;
|
|
24519
24613
|
stdout += printSection("Existing Release Files");
|
|
24520
24614
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
24521
|
-
if (existsSync7(
|
|
24615
|
+
if (existsSync7(path19.join(cwd, rel)))
|
|
24522
24616
|
stdout += rel + `
|
|
24523
24617
|
`;
|
|
24524
24618
|
}
|
|
@@ -24547,6 +24641,46 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
24547
24641
|
|
|
24548
24642
|
// packages/workit-core/src/core/tracker-issues.ts
|
|
24549
24643
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
24644
|
+
|
|
24645
|
+
// packages/workit-core/src/core/pr-create.ts
|
|
24646
|
+
import fs14 from "node:fs";
|
|
24647
|
+
import path20 from "node:path";
|
|
24648
|
+
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
24649
|
+
// packages/workit-core/src/forge/pr-body.ts
|
|
24650
|
+
function parseGhIssue(value) {
|
|
24651
|
+
const m = /issues\/(\d+)/.exec(value);
|
|
24652
|
+
return m ? m[1] : String(value).trim().replace(/^#/, "");
|
|
24653
|
+
}
|
|
24654
|
+
|
|
24655
|
+
// packages/workit-core/src/core/pr-create.ts
|
|
24656
|
+
function whichOnPath(tool) {
|
|
24657
|
+
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
24658
|
+
for (const dir of (process.env.PATH ?? "").split(path20.delimiter)) {
|
|
24659
|
+
if (!dir)
|
|
24660
|
+
continue;
|
|
24661
|
+
for (const name of names) {
|
|
24662
|
+
const candidate = path20.join(dir, name);
|
|
24663
|
+
try {
|
|
24664
|
+
fs14.accessSync(candidate, fs14.constants.X_OK);
|
|
24665
|
+
return candidate;
|
|
24666
|
+
} catch {}
|
|
24667
|
+
}
|
|
24668
|
+
}
|
|
24669
|
+
return null;
|
|
24670
|
+
}
|
|
24671
|
+
var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
|
|
24672
|
+
var pushRemote = (cwd) => {
|
|
24673
|
+
const result = spawnSync12("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
24674
|
+
cwd,
|
|
24675
|
+
encoding: "utf8"
|
|
24676
|
+
});
|
|
24677
|
+
if (result.status !== 0)
|
|
24678
|
+
return null;
|
|
24679
|
+
const urls = (result.stdout ?? "").split(/\r?\n/u).map((url) => url.trim()).filter(Boolean);
|
|
24680
|
+
return urls.length === 1 ? urls[0] : null;
|
|
24681
|
+
};
|
|
24682
|
+
|
|
24683
|
+
// packages/workit-core/src/core/tracker-issues.ts
|
|
24550
24684
|
var repoPathFromRemote = (remote) => {
|
|
24551
24685
|
let rest = (remote || "").trim().replace(/\/+$/, "");
|
|
24552
24686
|
if (!rest)
|
|
@@ -27810,6 +27944,25 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
27810
27944
|
var signals = new Map;
|
|
27811
27945
|
var cache = new Map;
|
|
27812
27946
|
var TURN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
27947
|
+
// packages/workit-core/src/hooks/delivery-verbs.ts
|
|
27948
|
+
var WORKIT_ALLOWED_VERBS = [
|
|
27949
|
+
"git",
|
|
27950
|
+
"pr",
|
|
27951
|
+
"ci",
|
|
27952
|
+
"stack",
|
|
27953
|
+
"release",
|
|
27954
|
+
"task",
|
|
27955
|
+
"ledger",
|
|
27956
|
+
"evidence",
|
|
27957
|
+
"finding",
|
|
27958
|
+
"decision",
|
|
27959
|
+
"worker",
|
|
27960
|
+
"verify-delivery",
|
|
27961
|
+
"policy",
|
|
27962
|
+
"doctor",
|
|
27963
|
+
"help"
|
|
27964
|
+
];
|
|
27965
|
+
var ALLOWED = new Set(WORKIT_ALLOWED_VERBS);
|
|
27813
27966
|
// packages/workit-core/src/core/heavy.ts
|
|
27814
27967
|
var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
|
|
27815
27968
|
var sub = (...names) => new Set(names);
|
|
@@ -28018,6 +28171,10 @@ var WRITE_TOOLS = new Set([
|
|
|
28018
28171
|
// packages/workit-core/src/hooks/standing.ts
|
|
28019
28172
|
var sinceStart = new Map;
|
|
28020
28173
|
// packages/workit-core/src/hooks/cwd-normalize.ts
|
|
28174
|
+
var DIR = String.raw`[A-Za-z0-9_./@%+:,=-]+|'[^'$${"`"}\\\n]*'|"[^"$${"`"}\\!\n]*"`;
|
|
28175
|
+
var COMPOUND = new RegExp(String.raw`^\s*cd\s+(${DIR})\s*&&\s*(workit(?:\s[\s\S]*)?)$`, "u");
|
|
28176
|
+
var GLOBALS_FIRST = new RegExp(String.raw`^(\s*workit)((?:\s+(?:--json|--cwd(?:=|\s+)(?:${DIR})))+)\s+([a-z][a-z-]*)(?=\s|$)`, "u");
|
|
28177
|
+
var VERBS = new Set(WORKIT_ALLOWED_VERBS);
|
|
28021
28178
|
var FILTERS = new Set(["tail", "head", "grep"]);
|
|
28022
28179
|
|
|
28023
28180
|
// packages/workit-core/src/hooks/worktree-confine.ts
|