@brainervirus/workit-codex 8.11.0 → 8.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +1 -1
- package/dist/launch-mcp.js +72 -73
- package/dist/{workit-hook-gtdf2ra8.js → workit-hook-n81rx47z.js} +315 -199
- package/dist/workit-hook.js +1 -1
- package/package.json +3 -3
- package/skills/workit-fanout/references/brief.md +23 -8
- package/skills/workit-fanout/references/orchestration.md +9 -4
|
@@ -26,7 +26,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
26
26
|
// packages/workit-core/src/hooks/context.ts
|
|
27
27
|
import { createHash as createHash11 } from "node:crypto";
|
|
28
28
|
import fs14 from "node:fs";
|
|
29
|
-
import
|
|
29
|
+
import path18 from "node:path";
|
|
30
30
|
|
|
31
31
|
// packages/workit-core/src/core/methods.ts
|
|
32
32
|
var METHODS = {
|
|
@@ -91,9 +91,9 @@ Autonomy contract:
|
|
|
91
91
|
ship), go to the effective endpoint in \`workit grant show\`: \`pr\` opens
|
|
92
92
|
the PR; \`green\` babysits it without asking (workit-ship) to merge-ready,
|
|
93
93
|
never merging; \`merged\` also lands it with \`workit pr merge\`.
|
|
94
|
-
Merge and release need a workspace grant
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
Merge and release need a workspace grant only the user raises from their
|
|
95
|
+
own terminal (a blocked verb names how); never edit workspaces.json or route
|
|
96
|
+
around a grant_required.
|
|
97
97
|
- Ask only for a product or preference choice, or for authority you lack, with
|
|
98
98
|
your recommended answer. Facts are yours: read, run or prototype.
|
|
99
99
|
- Label claims measured (run and seen this session), inferred, or guess.
|
|
@@ -102,17 +102,19 @@ Autonomy contract:
|
|
|
102
102
|
- Author is not verifier: your own verdict is \`--self\`, shown as
|
|
103
103
|
self-reviewed, never verified. High risk, \`verification: independent\` and
|
|
104
104
|
merge need a verifier with its own host-derived session (a subagent's
|
|
105
|
-
\`<lead>:<child
|
|
105
|
+
\`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
|
|
106
106
|
- A local-commit endpoint does not imply PR readiness, and a local commit alone
|
|
107
107
|
is not evidence of a requested remote push. Before reporting delivery,
|
|
108
108
|
reconcile every requested item and observe it (\`workit verify-delivery\`).
|
|
109
109
|
|
|
110
110
|
CLI first: if a step has one right answer, use the \`workit\` verb instead of
|
|
111
111
|
hand-running git, gh or glab (\`workit git|pr|ci|check\`; \`workit help\` lists
|
|
112
|
-
the rest;
|
|
112
|
+
the rest; lacking \`workit\`, \`npx -y @brainervirus/workit-cli\`).
|
|
113
|
+
Add Co-Authored-By via \`workit git commit --trailer "K: v" -- <paths>\`, never
|
|
114
|
+
an amend.
|
|
113
115
|
The task families (task, policy, evidence, finding, decision, worker, state)
|
|
114
116
|
are optional continuity for tracked work; each branch has one implicit task,
|
|
115
|
-
|
|
117
|
+
and a solo edit needs no task. A decision
|
|
116
118
|
records a user choice; it never authorizes an effect. Judge work once with
|
|
117
119
|
\`workit policy assess --judge risk= behavior= product-choice= plan=\`.
|
|
118
120
|
Across repositories, bind each item to its checkout, branch and endpoint,
|
|
@@ -8791,7 +8793,7 @@ function requirementId(input) {
|
|
|
8791
8793
|
import { createHash as createHash10 } from "node:crypto";
|
|
8792
8794
|
import * as fs13 from "node:fs";
|
|
8793
8795
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
8794
|
-
import
|
|
8796
|
+
import path16 from "node:path";
|
|
8795
8797
|
|
|
8796
8798
|
// packages/workit-core/src/check-config.ts
|
|
8797
8799
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
@@ -8960,7 +8962,7 @@ function loadCheckConfig(cwd) {
|
|
|
8960
8962
|
gates[gate] = names;
|
|
8961
8963
|
}
|
|
8962
8964
|
}
|
|
8963
|
-
return { source: CHECKS_FILE, root, checks, gates, error: null };
|
|
8965
|
+
return { source: CHECKS_FILE, root, checks, gates, error: null, resources: parsed.resources };
|
|
8964
8966
|
}
|
|
8965
8967
|
var sameArgv = (left, right) => left.length === right.length && left.every((item, index) => item === right[index]);
|
|
8966
8968
|
function matchesNamedCheck(config, name, argv, cwd) {
|
|
@@ -9182,7 +9184,7 @@ function pushRemoteName(cwd, branch) {
|
|
|
9182
9184
|
// packages/workit-core/src/core/task-store.ts
|
|
9183
9185
|
import { createHash as createHash6, randomUUID as randomUUID3 } from "node:crypto";
|
|
9184
9186
|
import * as fs10 from "node:fs";
|
|
9185
|
-
import
|
|
9187
|
+
import path10 from "node:path";
|
|
9186
9188
|
|
|
9187
9189
|
// node_modules/@openclaw/fs-safe/dist/errors.js
|
|
9188
9190
|
var OPERATIONAL_CODES = new Set([
|
|
@@ -9788,7 +9790,68 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
9788
9790
|
// packages/workit-core/src/host-session.ts
|
|
9789
9791
|
import { createHash as createHash4 } from "node:crypto";
|
|
9790
9792
|
import fs7 from "node:fs";
|
|
9793
|
+
import path7 from "node:path";
|
|
9794
|
+
|
|
9795
|
+
// packages/workit-core/src/core/logger.ts
|
|
9796
|
+
import os2 from "node:os";
|
|
9791
9797
|
import path6 from "node:path";
|
|
9798
|
+
var SENSITIVE_WORDS = new Set([
|
|
9799
|
+
"token",
|
|
9800
|
+
"secret",
|
|
9801
|
+
"password",
|
|
9802
|
+
"passwd",
|
|
9803
|
+
"authorization",
|
|
9804
|
+
"credential",
|
|
9805
|
+
"credentials",
|
|
9806
|
+
"cookie",
|
|
9807
|
+
"api",
|
|
9808
|
+
"apikey",
|
|
9809
|
+
"apitoken",
|
|
9810
|
+
"accesstoken",
|
|
9811
|
+
"clientsecret",
|
|
9812
|
+
"refreshtoken",
|
|
9813
|
+
"key",
|
|
9814
|
+
"bearer",
|
|
9815
|
+
"prompt",
|
|
9816
|
+
"message",
|
|
9817
|
+
"messages",
|
|
9818
|
+
"content",
|
|
9819
|
+
"body",
|
|
9820
|
+
"args",
|
|
9821
|
+
"argument",
|
|
9822
|
+
"result",
|
|
9823
|
+
"results",
|
|
9824
|
+
"output",
|
|
9825
|
+
"issue",
|
|
9826
|
+
"description",
|
|
9827
|
+
"summary",
|
|
9828
|
+
"payload",
|
|
9829
|
+
"script",
|
|
9830
|
+
"command",
|
|
9831
|
+
"text",
|
|
9832
|
+
"query"
|
|
9833
|
+
]);
|
|
9834
|
+
var STACK_WORDS = new Set(["stack", "stacktrace", "trace"]);
|
|
9835
|
+
var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9836
|
+
var resolveStateDir = () => {
|
|
9837
|
+
const override = process.env.WORKFLOW_TOOLKIT_STATE;
|
|
9838
|
+
if (override)
|
|
9839
|
+
return override;
|
|
9840
|
+
const home = os2.homedir();
|
|
9841
|
+
if (process.env.XDG_STATE_HOME)
|
|
9842
|
+
return path6.join(process.env.XDG_STATE_HOME, "workit");
|
|
9843
|
+
switch (os2.platform()) {
|
|
9844
|
+
case "darwin":
|
|
9845
|
+
return path6.join(home, "Library", "Application Support", "workit");
|
|
9846
|
+
case "win32":
|
|
9847
|
+
return path6.join(process.env.LOCALAPPDATA ?? path6.join(home, "AppData", "Local"), "workit");
|
|
9848
|
+
default:
|
|
9849
|
+
return path6.join(process.env.HOME ?? home, ".local", "state", "workit");
|
|
9850
|
+
}
|
|
9851
|
+
};
|
|
9852
|
+
var homePattern = new RegExp(`${escapeRegExp(os2.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
|
|
9853
|
+
|
|
9854
|
+
// packages/workit-core/src/host-session.ts
|
|
9792
9855
|
var HOST_SESSION_VARS = [
|
|
9793
9856
|
["OPENCODE_SESSION_ID", () => "opencode"],
|
|
9794
9857
|
["PI_SESSION_ID", () => "pi"],
|
|
@@ -9817,43 +9880,77 @@ function hostSessionFromEnv(env) {
|
|
|
9817
9880
|
return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
|
|
9818
9881
|
}
|
|
9819
9882
|
var ISSUED_PER_LEAD = 64;
|
|
9820
|
-
var ISSUED_SESSIONS_MARKER = "workit-issued-sessions/
|
|
9883
|
+
var ISSUED_SESSIONS_MARKER = "workit-issued-sessions/2";
|
|
9821
9884
|
var hash = (value) => createHash4("sha256").update(value).digest("hex").slice(0, 32);
|
|
9822
|
-
var
|
|
9823
|
-
|
|
9824
|
-
|
|
9885
|
+
var userIssuedRoot = () => path7.join(resolveStateDir(), "issued");
|
|
9886
|
+
function migrateRepoIssued(cwd) {
|
|
9887
|
+
if (cwd === undefined)
|
|
9888
|
+
return;
|
|
9889
|
+
let old;
|
|
9890
|
+
try {
|
|
9891
|
+
const location = resolveStore(cwd);
|
|
9892
|
+
if (location instanceof Error)
|
|
9893
|
+
return;
|
|
9894
|
+
old = path7.join(location.dir, "hooks", "issued");
|
|
9895
|
+
if (!fs7.existsSync(old))
|
|
9896
|
+
return;
|
|
9897
|
+
} catch {
|
|
9898
|
+
return;
|
|
9899
|
+
}
|
|
9900
|
+
for (const lead of safeList(old)) {
|
|
9901
|
+
try {
|
|
9902
|
+
const from = path7.join(old, lead);
|
|
9903
|
+
if (!fs7.statSync(from).isDirectory())
|
|
9904
|
+
continue;
|
|
9905
|
+
const to = path7.join(userIssuedRoot(), lead);
|
|
9906
|
+
fs7.mkdirSync(to, { recursive: true, mode: 448 });
|
|
9907
|
+
for (const id of fs7.readdirSync(from)) {
|
|
9908
|
+
fs7.copyFileSync(path7.join(from, id), path7.join(to, id));
|
|
9909
|
+
fs7.rmSync(path7.join(from, id), { force: true });
|
|
9910
|
+
}
|
|
9911
|
+
removeIfEmpty(from);
|
|
9912
|
+
} catch {}
|
|
9913
|
+
}
|
|
9914
|
+
removeIfEmpty(old);
|
|
9915
|
+
}
|
|
9916
|
+
var safeList = (dir) => {
|
|
9917
|
+
try {
|
|
9918
|
+
return fs7.readdirSync(dir);
|
|
9919
|
+
} catch {
|
|
9920
|
+
return [];
|
|
9921
|
+
}
|
|
9825
9922
|
};
|
|
9826
|
-
var
|
|
9827
|
-
|
|
9828
|
-
|
|
9923
|
+
var removeIfEmpty = (dir) => {
|
|
9924
|
+
try {
|
|
9925
|
+
fs7.rmdirSync(dir);
|
|
9926
|
+
} catch {}
|
|
9829
9927
|
};
|
|
9830
9928
|
function recordIssuedSession(cwd, lead, issued) {
|
|
9831
9929
|
try {
|
|
9832
|
-
|
|
9833
|
-
|
|
9834
|
-
|
|
9835
|
-
fs7.mkdirSync(dir, { recursive: true });
|
|
9930
|
+
migrateRepoIssued(cwd);
|
|
9931
|
+
const dir = path7.join(userIssuedRoot(), hash(lead));
|
|
9932
|
+
fs7.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
9836
9933
|
const own = hash(issued);
|
|
9837
|
-
fs7.writeFileSync(
|
|
9934
|
+
fs7.writeFileSync(path7.join(dir, own), ISSUED_SESSIONS_MARKER);
|
|
9838
9935
|
const entries = fs7.readdirSync(dir);
|
|
9839
9936
|
if (entries.length <= ISSUED_PER_LEAD)
|
|
9840
9937
|
return;
|
|
9841
|
-
const byAge = entries.filter((name) => name !== own).map((name) => ({ name, at: fs7.statSync(
|
|
9938
|
+
const byAge = entries.filter((name) => name !== own).map((name) => ({ name, at: fs7.statSync(path7.join(dir, name)).mtimeMs })).toSorted((a, b) => a.at - b.at);
|
|
9842
9939
|
for (const { name } of byAge.slice(0, entries.length - ISSUED_PER_LEAD))
|
|
9843
|
-
fs7.rmSync(
|
|
9940
|
+
fs7.rmSync(path7.join(dir, name), { force: true });
|
|
9844
9941
|
} catch {}
|
|
9845
9942
|
}
|
|
9846
9943
|
var OWN_CHILD_SHELL = new Set(["opencode", "pi"]);
|
|
9847
9944
|
|
|
9848
9945
|
// packages/workit-core/src/core/package-root.ts
|
|
9849
9946
|
import { existsSync } from "node:fs";
|
|
9850
|
-
import
|
|
9947
|
+
import path8 from "node:path";
|
|
9851
9948
|
import { fileURLToPath } from "node:url";
|
|
9852
9949
|
var packageRoot = () => {
|
|
9853
|
-
let dir =
|
|
9950
|
+
let dir = path8.dirname(fileURLToPath(import.meta.url));
|
|
9854
9951
|
while (true) {
|
|
9855
|
-
const parent =
|
|
9856
|
-
if (existsSync(
|
|
9952
|
+
const parent = path8.dirname(dir);
|
|
9953
|
+
if (existsSync(path8.join(dir, "package.json")) || parent === dir)
|
|
9857
9954
|
return dir;
|
|
9858
9955
|
dir = parent;
|
|
9859
9956
|
}
|
|
@@ -10002,7 +10099,7 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
|
|
|
10002
10099
|
// packages/workit-core/src/store/event-log.ts
|
|
10003
10100
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
10004
10101
|
import fs9 from "node:fs";
|
|
10005
|
-
import
|
|
10102
|
+
import path9 from "node:path";
|
|
10006
10103
|
var EVENT_VERSION = 1;
|
|
10007
10104
|
|
|
10008
10105
|
class LogDamage extends Error {
|
|
@@ -10113,7 +10210,7 @@ function appendEvent(file, event, validEnd) {
|
|
|
10113
10210
|
fs9.closeSync(fd);
|
|
10114
10211
|
}
|
|
10115
10212
|
if (created)
|
|
10116
|
-
fsyncDirectory(
|
|
10213
|
+
fsyncDirectory(path9.dirname(file));
|
|
10117
10214
|
return validEnd;
|
|
10118
10215
|
}
|
|
10119
10216
|
function replaceFile(file, bytes, durable) {
|
|
@@ -10129,7 +10226,7 @@ function replaceFile(file, bytes, durable) {
|
|
|
10129
10226
|
}
|
|
10130
10227
|
renameWithRetry(temporary, file);
|
|
10131
10228
|
if (durable)
|
|
10132
|
-
fsyncDirectory(
|
|
10229
|
+
fsyncDirectory(path9.dirname(file));
|
|
10133
10230
|
} catch (error) {
|
|
10134
10231
|
try {
|
|
10135
10232
|
fs9.unlinkSync(temporary);
|
|
@@ -10472,7 +10569,7 @@ var cachedRuntimeVersion = null;
|
|
|
10472
10569
|
var runtimeVersion = () => {
|
|
10473
10570
|
if (cachedRuntimeVersion === null) {
|
|
10474
10571
|
try {
|
|
10475
|
-
const pkg = JSON.parse(fs10.readFileSync(
|
|
10572
|
+
const pkg = JSON.parse(fs10.readFileSync(path10.join(packageRoot(), "package.json"), "utf8"));
|
|
10476
10573
|
cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
|
|
10477
10574
|
} catch {
|
|
10478
10575
|
cachedRuntimeVersion = "0.0.0";
|
|
@@ -10505,7 +10602,7 @@ var readText = (file) => {
|
|
|
10505
10602
|
};
|
|
10506
10603
|
var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
|
|
10507
10604
|
var branchStart = (location, branch, aliases) => {
|
|
10508
|
-
const first = readText(
|
|
10605
|
+
const first = readText(path10.join(path10.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
|
|
10509
10606
|
`, 1)[0];
|
|
10510
10607
|
const created = REFLOG_LINE.exec(first);
|
|
10511
10608
|
const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
|
|
@@ -10515,7 +10612,7 @@ var branchStart = (location, branch, aliases) => {
|
|
|
10515
10612
|
return start;
|
|
10516
10613
|
if (!location.git)
|
|
10517
10614
|
return null;
|
|
10518
|
-
for (const line of readText(
|
|
10615
|
+
for (const line of readText(path10.join(location.git.gitDir, "logs", "HEAD")).split(`
|
|
10519
10616
|
`)) {
|
|
10520
10617
|
const entry = REFLOG_LINE.exec(line);
|
|
10521
10618
|
const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
|
|
@@ -10525,14 +10622,14 @@ var branchStart = (location, branch, aliases) => {
|
|
|
10525
10622
|
}
|
|
10526
10623
|
return null;
|
|
10527
10624
|
};
|
|
10528
|
-
var refExists = (common, ref) => fs10.existsSync(
|
|
10625
|
+
var refExists = (common, ref) => fs10.existsSync(path10.join(common, ref)) || readText(path10.join(common, "packed-refs")).includes(` ${ref}
|
|
10529
10626
|
`);
|
|
10530
10627
|
var origins = new Map;
|
|
10531
10628
|
var sameDirectoryIdentity = (left, right) => {
|
|
10532
|
-
if (!
|
|
10629
|
+
if (!path10.isAbsolute(left) || !path10.isAbsolute(right))
|
|
10533
10630
|
return false;
|
|
10534
|
-
const normalizedLeft =
|
|
10535
|
-
const normalizedRight =
|
|
10631
|
+
const normalizedLeft = path10.resolve(left);
|
|
10632
|
+
const normalizedRight = path10.resolve(right);
|
|
10536
10633
|
try {
|
|
10537
10634
|
const a = fs10.statSync(normalizedLeft, { bigint: true });
|
|
10538
10635
|
const b = fs10.statSync(normalizedRight, { bigint: true });
|
|
@@ -10578,7 +10675,7 @@ class TaskStore {
|
|
|
10578
10675
|
migrationChecked = false;
|
|
10579
10676
|
compactDue = new Set;
|
|
10580
10677
|
constructor(root, options = {}) {
|
|
10581
|
-
const resolved =
|
|
10678
|
+
const resolved = path10.resolve(root);
|
|
10582
10679
|
this.origin = fs10.existsSync(resolved) ? fs10.realpathSync(resolved) : resolved;
|
|
10583
10680
|
this.located = resolveStore(this.origin);
|
|
10584
10681
|
this.root = checkoutRootOf(this.origin, this.located);
|
|
@@ -10587,7 +10684,7 @@ class TaskStore {
|
|
|
10587
10684
|
this.migrateOnRead = options.migrateOnRead;
|
|
10588
10685
|
}
|
|
10589
10686
|
location() {
|
|
10590
|
-
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs10.existsSync(
|
|
10687
|
+
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs10.existsSync(path10.join(this.root, ".git"))) {
|
|
10591
10688
|
this.located = undefined;
|
|
10592
10689
|
this.migrationChecked = false;
|
|
10593
10690
|
}
|
|
@@ -10743,7 +10840,7 @@ class TaskStore {
|
|
|
10743
10840
|
const moved = this.bindLocked(again.data.id, key, input.now);
|
|
10744
10841
|
return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
|
|
10745
10842
|
}
|
|
10746
|
-
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" :
|
|
10843
|
+
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path10.basename(this.root) || this.root;
|
|
10747
10844
|
const intent = {
|
|
10748
10845
|
objective: input.objective?.trim() || `Work on ${label}`,
|
|
10749
10846
|
scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
|
|
@@ -10803,14 +10900,14 @@ class TaskStore {
|
|
|
10803
10900
|
const branch = key.ok ? key.data.branch : null;
|
|
10804
10901
|
if (!branch || !location.ok)
|
|
10805
10902
|
return success(null, null, null);
|
|
10806
|
-
const common =
|
|
10807
|
-
const heads =
|
|
10808
|
-
const headLog = location.data.git ?
|
|
10903
|
+
const common = path10.dirname(location.data.dir);
|
|
10904
|
+
const heads = path10.join(common, "logs", "refs", "heads");
|
|
10905
|
+
const headLog = location.data.git ? path10.join(location.data.git.gitDir, "logs", "HEAD") : "";
|
|
10809
10906
|
const signatures = [
|
|
10810
|
-
|
|
10907
|
+
path10.join(heads, branch),
|
|
10811
10908
|
headLog,
|
|
10812
10909
|
heads,
|
|
10813
|
-
|
|
10910
|
+
path10.join(common, "packed-refs")
|
|
10814
10911
|
].map((file) => fileSignature(file) ?? "-");
|
|
10815
10912
|
const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
|
|
10816
10913
|
let resolved = origins.get(slot);
|
|
@@ -10822,7 +10919,7 @@ class TaskStore {
|
|
|
10822
10919
|
aliases.push(rename.from);
|
|
10823
10920
|
const start = branchStart(location.data, branch, aliases);
|
|
10824
10921
|
const slash = start?.indexOf("/") ?? -1;
|
|
10825
|
-
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(
|
|
10922
|
+
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path10.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
|
|
10826
10923
|
const name = start && (remote ? start.slice(slash + 1) : start);
|
|
10827
10924
|
resolved = {
|
|
10828
10925
|
signature: signatures.join("|"),
|
|
@@ -11060,13 +11157,13 @@ class TaskStore {
|
|
|
11060
11157
|
if (!ready.ok)
|
|
11061
11158
|
return ready;
|
|
11062
11159
|
const dirs = this.paths();
|
|
11063
|
-
const recovery =
|
|
11160
|
+
const recovery = path10.join(this.root, ".workit", "recovery");
|
|
11064
11161
|
if (fs10.existsSync(recovery)) {
|
|
11065
11162
|
let files = 0;
|
|
11066
11163
|
let bytes = 0;
|
|
11067
11164
|
try {
|
|
11068
11165
|
for (const name of fs10.readdirSync(recovery)) {
|
|
11069
|
-
const stat = fs10.lstatSync(
|
|
11166
|
+
const stat = fs10.lstatSync(path10.join(recovery, name));
|
|
11070
11167
|
if (!stat.isFile())
|
|
11071
11168
|
continue;
|
|
11072
11169
|
files += 1;
|
|
@@ -11119,7 +11216,7 @@ class TaskStore {
|
|
|
11119
11216
|
if (fs10.existsSync(dirs.blobs))
|
|
11120
11217
|
for (const name of fs10.readdirSync(dirs.blobs)) {
|
|
11121
11218
|
const digest = name.replace(/\.json$/, "");
|
|
11122
|
-
const file =
|
|
11219
|
+
const file = path10.join(dirs.blobs, name);
|
|
11123
11220
|
let stat;
|
|
11124
11221
|
try {
|
|
11125
11222
|
stat = fs10.lstatSync(file);
|
|
@@ -11137,7 +11234,7 @@ class TaskStore {
|
|
|
11137
11234
|
}
|
|
11138
11235
|
return success(null, null, null);
|
|
11139
11236
|
};
|
|
11140
|
-
const swept = dryRun ? sweep() : this.withLock(
|
|
11237
|
+
const swept = dryRun ? sweep() : this.withLock(path10.join(dirs.dir, "gc.lock"), sweep);
|
|
11141
11238
|
if (!swept.ok)
|
|
11142
11239
|
return swept;
|
|
11143
11240
|
const temporaryDirs = [
|
|
@@ -11145,7 +11242,7 @@ class TaskStore {
|
|
|
11145
11242
|
dirs.tasks,
|
|
11146
11243
|
dirs.blobs,
|
|
11147
11244
|
dirs.checkout,
|
|
11148
|
-
...names.map((name) =>
|
|
11245
|
+
...names.map((name) => path10.join(dirs.tasks, name))
|
|
11149
11246
|
];
|
|
11150
11247
|
for (const directory of temporaryDirs) {
|
|
11151
11248
|
if (!fs10.existsSync(directory))
|
|
@@ -11153,7 +11250,7 @@ class TaskStore {
|
|
|
11153
11250
|
for (const name of fs10.readdirSync(directory)) {
|
|
11154
11251
|
if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
|
|
11155
11252
|
continue;
|
|
11156
|
-
const file =
|
|
11253
|
+
const file = path10.join(directory, name);
|
|
11157
11254
|
try {
|
|
11158
11255
|
const stat = fs10.lstatSync(file);
|
|
11159
11256
|
if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
|
|
@@ -11170,30 +11267,30 @@ class TaskStore {
|
|
|
11170
11267
|
}
|
|
11171
11268
|
paths() {
|
|
11172
11269
|
const location = this.location();
|
|
11173
|
-
const dir = location.ok ? location.data.dir :
|
|
11174
|
-
const checkout =
|
|
11270
|
+
const dir = location.ok ? location.data.dir : path10.join(this.root, ".workit");
|
|
11271
|
+
const checkout = path10.join(dir, "checkouts", checkoutSlug(this.root));
|
|
11175
11272
|
return {
|
|
11176
11273
|
dir,
|
|
11177
11274
|
shared: location.ok ? location.data.shared : false,
|
|
11178
|
-
marker:
|
|
11275
|
+
marker: path10.join(dir, "store.json"),
|
|
11179
11276
|
checkout,
|
|
11180
|
-
workspace:
|
|
11181
|
-
checkoutLock:
|
|
11182
|
-
tasks:
|
|
11183
|
-
index:
|
|
11184
|
-
blobs:
|
|
11185
|
-
legacy:
|
|
11186
|
-
v2:
|
|
11277
|
+
workspace: path10.join(checkout, "workspace.json"),
|
|
11278
|
+
checkoutLock: path10.join(checkout, "metadata.lock"),
|
|
11279
|
+
tasks: path10.join(dir, "tasks"),
|
|
11280
|
+
index: path10.join(dir, "task-index.json"),
|
|
11281
|
+
blobs: path10.join(dir, "blobs", "candidates"),
|
|
11282
|
+
legacy: path10.join(dir, "legacy", checkoutSlug(this.root), "v2"),
|
|
11283
|
+
v2: path10.join(this.root, ".workit")
|
|
11187
11284
|
};
|
|
11188
11285
|
}
|
|
11189
11286
|
eventsPath(taskId) {
|
|
11190
|
-
return
|
|
11287
|
+
return path10.join(this.paths().tasks, taskId, "events.jsonl");
|
|
11191
11288
|
}
|
|
11192
11289
|
snapshotPath(taskId) {
|
|
11193
|
-
return
|
|
11290
|
+
return path10.join(this.paths().tasks, taskId, "snapshot.json");
|
|
11194
11291
|
}
|
|
11195
11292
|
taskLockPath(taskId) {
|
|
11196
|
-
return
|
|
11293
|
+
return path10.join(this.paths().tasks, taskId, "lock");
|
|
11197
11294
|
}
|
|
11198
11295
|
ready(write = false) {
|
|
11199
11296
|
const location = this.location();
|
|
@@ -11232,13 +11329,13 @@ class TaskStore {
|
|
|
11232
11329
|
return {
|
|
11233
11330
|
read: (digest) => {
|
|
11234
11331
|
try {
|
|
11235
|
-
return JSON.parse(fs10.readFileSync(
|
|
11332
|
+
return JSON.parse(fs10.readFileSync(path10.join(dir, `${digest}.json`), "utf8"));
|
|
11236
11333
|
} catch (error) {
|
|
11237
11334
|
throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
|
|
11238
11335
|
}
|
|
11239
11336
|
},
|
|
11240
11337
|
write: (digest, value) => {
|
|
11241
|
-
const file =
|
|
11338
|
+
const file = path10.join(dir, `${digest}.json`);
|
|
11242
11339
|
if (fs10.existsSync(file)) {
|
|
11243
11340
|
const stamp = new Date;
|
|
11244
11341
|
try {
|
|
@@ -11430,7 +11527,7 @@ class TaskStore {
|
|
|
11430
11527
|
}
|
|
11431
11528
|
}
|
|
11432
11529
|
openTask(record, type, key, legacy = null) {
|
|
11433
|
-
const dir =
|
|
11530
|
+
const dir = path10.join(this.paths().tasks, record.id);
|
|
11434
11531
|
fs10.mkdirSync(dir, { recursive: true });
|
|
11435
11532
|
const blobs = this.blobStore();
|
|
11436
11533
|
const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
|
|
@@ -11523,7 +11620,7 @@ class TaskStore {
|
|
|
11523
11620
|
}
|
|
11524
11621
|
withKeyLock(key, operation) {
|
|
11525
11622
|
const digest = createHash6("sha256").update(key).digest("hex").slice(0, 32);
|
|
11526
|
-
return this.withLock(
|
|
11623
|
+
return this.withLock(path10.join(this.paths().dir, "keys", `${digest}.lock`), operation);
|
|
11527
11624
|
}
|
|
11528
11625
|
bindLocked(taskId, key, timestamp) {
|
|
11529
11626
|
const current = this.readWorkspace();
|
|
@@ -11664,7 +11761,7 @@ class TaskStore {
|
|
|
11664
11761
|
writeWorkspace(record) {
|
|
11665
11762
|
const file = this.paths().workspace;
|
|
11666
11763
|
try {
|
|
11667
|
-
fs10.mkdirSync(
|
|
11764
|
+
fs10.mkdirSync(path10.dirname(file), { recursive: true });
|
|
11668
11765
|
replaceFile(file, `${canonicalJson(record)}
|
|
11669
11766
|
`, true);
|
|
11670
11767
|
return success(null, null, null);
|
|
@@ -11742,23 +11839,23 @@ class TaskStore {
|
|
|
11742
11839
|
return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
|
|
11743
11840
|
}
|
|
11744
11841
|
v2Dirs() {
|
|
11745
|
-
return [...new Set([this.root, this.origin])].map((dir) =>
|
|
11842
|
+
return [...new Set([this.root, this.origin])].map((dir) => path10.join(dir, ".workit"));
|
|
11746
11843
|
}
|
|
11747
11844
|
pendingMigrations() {
|
|
11748
11845
|
return this.v2Dirs().filter((dir) => {
|
|
11749
|
-
const raw = this.readV2Workspace(
|
|
11846
|
+
const raw = this.readV2Workspace(path10.join(dir, "workspace.json"));
|
|
11750
11847
|
return raw !== null && raw !== TOMBSTONE;
|
|
11751
11848
|
});
|
|
11752
11849
|
}
|
|
11753
11850
|
migrateV2(v2) {
|
|
11754
11851
|
const dirs = this.paths();
|
|
11755
|
-
const v2Workspace =
|
|
11756
|
-
const v2Tasks =
|
|
11757
|
-
const legacy =
|
|
11852
|
+
const v2Workspace = path10.join(v2, "workspace.json");
|
|
11853
|
+
const v2Tasks = path10.join(v2, "tasks");
|
|
11854
|
+
const legacy = path10.join(dirs.dir, "legacy", checkoutSlug(path10.dirname(v2)), "v2");
|
|
11758
11855
|
const raw = this.readV2Workspace(v2Workspace);
|
|
11759
11856
|
if (raw === null || raw === TOMBSTONE)
|
|
11760
11857
|
return success(null, null, null);
|
|
11761
|
-
return this.withLock(dirs.checkoutLock, () => this.withLock(
|
|
11858
|
+
return this.withLock(dirs.checkoutLock, () => this.withLock(path10.join(v2, "metadata.lock"), () => {
|
|
11762
11859
|
const workspace = this.readV2Workspace(v2Workspace);
|
|
11763
11860
|
if (workspace === null || workspace === TOMBSTONE)
|
|
11764
11861
|
return success(null, null, null);
|
|
@@ -11767,7 +11864,7 @@ class TaskStore {
|
|
|
11767
11864
|
return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
|
|
11768
11865
|
let files = [];
|
|
11769
11866
|
try {
|
|
11770
|
-
files = fs10.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(
|
|
11867
|
+
files = fs10.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path10.join(v2Tasks, name)));
|
|
11771
11868
|
} catch {}
|
|
11772
11869
|
const current = this.readWorkspace();
|
|
11773
11870
|
if (!current.ok)
|
|
@@ -11785,21 +11882,21 @@ class TaskStore {
|
|
|
11785
11882
|
let skipped = 0;
|
|
11786
11883
|
const done = new Map;
|
|
11787
11884
|
try {
|
|
11788
|
-
fs10.mkdirSync(
|
|
11885
|
+
fs10.mkdirSync(path10.join(legacy, "tasks"), { recursive: true });
|
|
11789
11886
|
for (const name of ["workspace.json", "index.json"])
|
|
11790
|
-
if (fs10.existsSync(
|
|
11791
|
-
fs10.copyFileSync(
|
|
11887
|
+
if (fs10.existsSync(path10.join(v2, name)))
|
|
11888
|
+
fs10.copyFileSync(path10.join(v2, name), path10.join(legacy, name));
|
|
11792
11889
|
for (const name of files) {
|
|
11793
11890
|
const id = name.slice(0, -5);
|
|
11794
|
-
const file =
|
|
11891
|
+
const file = path10.join(v2Tasks, name);
|
|
11795
11892
|
const bytes = fs10.readFileSync(file, "utf8");
|
|
11796
11893
|
const digest = sha256(bytes);
|
|
11797
|
-
fs10.writeFileSync(
|
|
11894
|
+
fs10.writeFileSync(path10.join(legacy, "tasks", name), bytes);
|
|
11798
11895
|
const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
|
|
11799
11896
|
if (!parsed.ok || parsed.data.id !== id)
|
|
11800
11897
|
return failure2("recovery_required", `cannot migrate 2.x task ${id}: ${parsed.ok ? "file name and record ID differ" : parsed.error}; move ${file} aside to continue`, { path: file });
|
|
11801
11898
|
const record = { ...parsed.data, workspaceId: target.id };
|
|
11802
|
-
const origin = { path:
|
|
11899
|
+
const origin = { path: path10.join(legacy, "tasks", name), digest };
|
|
11803
11900
|
if (!fs10.existsSync(this.eventsPath(id))) {
|
|
11804
11901
|
this.openTask(record, "migrated.from_v2", null, origin);
|
|
11805
11902
|
migrated += 1;
|
|
@@ -11835,7 +11932,7 @@ class TaskStore {
|
|
|
11835
11932
|
try {
|
|
11836
11933
|
let complete = true;
|
|
11837
11934
|
for (const [name, digest] of done) {
|
|
11838
|
-
const file =
|
|
11935
|
+
const file = path10.join(v2Tasks, name);
|
|
11839
11936
|
if (sha256(fs10.readFileSync(file, "utf8")) !== digest) {
|
|
11840
11937
|
complete = false;
|
|
11841
11938
|
continue;
|
|
@@ -11846,7 +11943,7 @@ class TaskStore {
|
|
|
11846
11943
|
return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
|
|
11847
11944
|
path: v2
|
|
11848
11945
|
});
|
|
11849
|
-
fs10.rmSync(
|
|
11946
|
+
fs10.rmSync(path10.join(v2, "index.json"), { force: true });
|
|
11850
11947
|
this.writeTombstone(v2, target.id);
|
|
11851
11948
|
} catch (error) {
|
|
11852
11949
|
return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
|
|
@@ -11866,7 +11963,7 @@ class TaskStore {
|
|
|
11866
11963
|
}
|
|
11867
11964
|
adoptLocalStore() {
|
|
11868
11965
|
const dirs = this.paths();
|
|
11869
|
-
const marker =
|
|
11966
|
+
const marker = path10.join(dirs.v2, "store.json");
|
|
11870
11967
|
if (!dirs.shared || !fs10.existsSync(marker))
|
|
11871
11968
|
return success(null, null, null);
|
|
11872
11969
|
return this.withLock(dirs.checkoutLock, () => {
|
|
@@ -11876,7 +11973,7 @@ class TaskStore {
|
|
|
11876
11973
|
try {
|
|
11877
11974
|
const merge = (from, to) => {
|
|
11878
11975
|
if (!fs10.existsSync(to)) {
|
|
11879
|
-
fs10.mkdirSync(
|
|
11976
|
+
fs10.mkdirSync(path10.dirname(to), { recursive: true });
|
|
11880
11977
|
try {
|
|
11881
11978
|
fs10.renameSync(from, to);
|
|
11882
11979
|
} catch {
|
|
@@ -11887,24 +11984,24 @@ class TaskStore {
|
|
|
11887
11984
|
}
|
|
11888
11985
|
if (fs10.lstatSync(from).isDirectory() && fs10.lstatSync(to).isDirectory())
|
|
11889
11986
|
for (const name of fs10.readdirSync(from))
|
|
11890
|
-
merge(
|
|
11987
|
+
merge(path10.join(from, name), path10.join(to, name));
|
|
11891
11988
|
};
|
|
11892
11989
|
for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
|
|
11893
|
-
const from =
|
|
11990
|
+
const from = path10.join(dirs.v2, sub);
|
|
11894
11991
|
if (!fs10.existsSync(from))
|
|
11895
11992
|
continue;
|
|
11896
11993
|
if (sub === "tasks") {
|
|
11897
11994
|
for (const name of fs10.readdirSync(from))
|
|
11898
|
-
if (validId(name) && !fs10.existsSync(
|
|
11995
|
+
if (validId(name) && !fs10.existsSync(path10.join(dirs.dir, sub, name)))
|
|
11899
11996
|
tasks += 1;
|
|
11900
11997
|
}
|
|
11901
|
-
merge(from,
|
|
11998
|
+
merge(from, path10.join(dirs.dir, sub));
|
|
11902
11999
|
}
|
|
11903
|
-
const ledger =
|
|
11904
|
-
if (fs10.existsSync(ledger) && !fs10.existsSync(
|
|
11905
|
-
merge(ledger,
|
|
11906
|
-
fs10.rmSync(
|
|
11907
|
-
fs10.renameSync(marker,
|
|
12000
|
+
const ledger = path10.join(dirs.v2, "ledger");
|
|
12001
|
+
if (fs10.existsSync(ledger) && !fs10.existsSync(path10.join(dirs.dir, "ledger")))
|
|
12002
|
+
merge(ledger, path10.join(dirs.dir, "ledger"));
|
|
12003
|
+
fs10.rmSync(path10.join(dirs.v2, "task-index.json"), { force: true });
|
|
12004
|
+
fs10.renameSync(marker, path10.join(dirs.v2, "store.moved.json"));
|
|
11908
12005
|
} catch (error) {
|
|
11909
12006
|
return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
|
|
11910
12007
|
path: dirs.v2
|
|
@@ -11942,7 +12039,7 @@ class TaskStore {
|
|
|
11942
12039
|
schemaVersion: SCHEMA_VERSION,
|
|
11943
12040
|
id: workspaceId ?? newId(),
|
|
11944
12041
|
revision: newRevision(),
|
|
11945
|
-
root:
|
|
12042
|
+
root: path10.dirname(v2),
|
|
11946
12043
|
runtime: { createdWith: version, updatedWith: version },
|
|
11947
12044
|
writer: null,
|
|
11948
12045
|
store: {
|
|
@@ -11954,11 +12051,11 @@ class TaskStore {
|
|
|
11954
12051
|
critical: ["store"]
|
|
11955
12052
|
};
|
|
11956
12053
|
fs10.mkdirSync(v2, { recursive: true });
|
|
11957
|
-
const ignore =
|
|
12054
|
+
const ignore = path10.join(v2, ".gitignore");
|
|
11958
12055
|
if (!fs10.existsSync(ignore))
|
|
11959
12056
|
fs10.writeFileSync(ignore, `*
|
|
11960
12057
|
`);
|
|
11961
|
-
replaceFile(
|
|
12058
|
+
replaceFile(path10.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
|
|
11962
12059
|
`, true);
|
|
11963
12060
|
}
|
|
11964
12061
|
stubFor(taskId) {
|
|
@@ -11982,8 +12079,8 @@ class TaskStore {
|
|
|
11982
12079
|
}
|
|
11983
12080
|
writeTombstoneIfMissing() {
|
|
11984
12081
|
try {
|
|
11985
|
-
const v2 =
|
|
11986
|
-
if (fs10.existsSync(
|
|
12082
|
+
const v2 = path10.join(this.root, ".workit");
|
|
12083
|
+
if (fs10.existsSync(path10.join(v2, "workspace.json")))
|
|
11987
12084
|
return;
|
|
11988
12085
|
let id = null;
|
|
11989
12086
|
try {
|
|
@@ -12000,7 +12097,7 @@ class TaskStore {
|
|
|
12000
12097
|
replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
|
|
12001
12098
|
`, false);
|
|
12002
12099
|
if (!dirs.shared) {
|
|
12003
|
-
const ignore =
|
|
12100
|
+
const ignore = path10.join(dirs.dir, ".gitignore");
|
|
12004
12101
|
let current = null;
|
|
12005
12102
|
try {
|
|
12006
12103
|
current = fs10.readFileSync(ignore, "utf8");
|
|
@@ -12060,7 +12157,7 @@ class TaskStore {
|
|
|
12060
12157
|
withLock(lockPath, operation) {
|
|
12061
12158
|
try {
|
|
12062
12159
|
this.initializeStorage();
|
|
12063
|
-
fs10.mkdirSync(
|
|
12160
|
+
fs10.mkdirSync(path10.dirname(lockPath), { recursive: true });
|
|
12064
12161
|
} catch (error) {
|
|
12065
12162
|
return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
|
|
12066
12163
|
path: this.paths().dir
|
|
@@ -12123,7 +12220,7 @@ class TaskStore {
|
|
|
12123
12220
|
if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
|
|
12124
12221
|
return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
|
|
12125
12222
|
path: lockPath,
|
|
12126
|
-
guidance: `Check permissions and the read-only attribute on ${
|
|
12223
|
+
guidance: `Check permissions and the read-only attribute on ${path10.dirname(lockPath)}.`
|
|
12127
12224
|
});
|
|
12128
12225
|
if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
|
|
12129
12226
|
let lock = null;
|
|
@@ -12143,17 +12240,17 @@ class TaskStore {
|
|
|
12143
12240
|
|
|
12144
12241
|
// packages/workit-core/src/core/policy/derive.ts
|
|
12145
12242
|
import { existsSync as existsSync4 } from "node:fs";
|
|
12146
|
-
import
|
|
12243
|
+
import path15 from "node:path";
|
|
12147
12244
|
|
|
12148
12245
|
// packages/workit-core/src/ledger.ts
|
|
12149
12246
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
12150
12247
|
import { createHash as createHash9, randomBytes as randomBytes2 } from "node:crypto";
|
|
12151
12248
|
import fs12 from "node:fs";
|
|
12152
|
-
import
|
|
12249
|
+
import path14 from "node:path";
|
|
12153
12250
|
|
|
12154
12251
|
// packages/workit-core/src/core/vcs-config.ts
|
|
12155
12252
|
import fs11 from "node:fs";
|
|
12156
|
-
import
|
|
12253
|
+
import path13 from "node:path";
|
|
12157
12254
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
12158
12255
|
|
|
12159
12256
|
// packages/workit-core/src/core/config.ts
|
|
@@ -12166,8 +12263,8 @@ import {
|
|
|
12166
12263
|
readdirSync as readdirSync2,
|
|
12167
12264
|
writeFileSync as writeFileSync2
|
|
12168
12265
|
} from "node:fs";
|
|
12169
|
-
import
|
|
12170
|
-
import
|
|
12266
|
+
import os3 from "node:os";
|
|
12267
|
+
import path11 from "node:path";
|
|
12171
12268
|
|
|
12172
12269
|
// packages/workit-core/src/core/boundary.ts
|
|
12173
12270
|
var EVENT = {
|
|
@@ -12211,7 +12308,7 @@ var mergePreset = (preset, input = {}, current = {
|
|
|
12211
12308
|
protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
|
|
12212
12309
|
};
|
|
12213
12310
|
};
|
|
12214
|
-
var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ??
|
|
12311
|
+
var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path11.join(process.env.XDG_CONFIG_HOME || path11.join(os3.homedir(), ".config"), "workit");
|
|
12215
12312
|
var migratedDir = null;
|
|
12216
12313
|
var migrationFailed = false;
|
|
12217
12314
|
var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
@@ -12221,7 +12318,7 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
12221
12318
|
migratedDir = dir;
|
|
12222
12319
|
return dir;
|
|
12223
12320
|
}
|
|
12224
|
-
const legacy =
|
|
12321
|
+
const legacy = path11.join(process.env.XDG_CONFIG_HOME || path11.join(os3.homedir(), ".config"), "workflow-toolkit");
|
|
12225
12322
|
if (!existsSync3(legacy)) {
|
|
12226
12323
|
migratedDir = dir;
|
|
12227
12324
|
return dir;
|
|
@@ -12234,8 +12331,8 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
12234
12331
|
mkdirSync2(dir, { recursive: true });
|
|
12235
12332
|
diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
|
|
12236
12333
|
for (const entry of readdirSync2(legacy, { withFileTypes: true })) {
|
|
12237
|
-
const src =
|
|
12238
|
-
const dest =
|
|
12334
|
+
const src = path11.join(legacy, entry.name);
|
|
12335
|
+
const dest = path11.join(dir, entry.name);
|
|
12239
12336
|
if (existsSync3(dest))
|
|
12240
12337
|
continue;
|
|
12241
12338
|
try {
|
|
@@ -12307,7 +12404,7 @@ var parseConfigResult = (raw, file) => {
|
|
|
12307
12404
|
return { status: "valid", path: file, config };
|
|
12308
12405
|
};
|
|
12309
12406
|
var readConfigTyped = (dir) => {
|
|
12310
|
-
const file =
|
|
12407
|
+
const file = path11.join(dir ?? configDir(), "config.json");
|
|
12311
12408
|
return parseConfigResult(readSafe(file), file);
|
|
12312
12409
|
};
|
|
12313
12410
|
var readConfig = () => {
|
|
@@ -12316,7 +12413,7 @@ var readConfig = () => {
|
|
|
12316
12413
|
throw new Error(result.error);
|
|
12317
12414
|
return result.config ?? DEFAULTS;
|
|
12318
12415
|
};
|
|
12319
|
-
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ??
|
|
12416
|
+
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path11.join(configDir(), "vcs.json");
|
|
12320
12417
|
var branchGlobPattern = (glob) => new RegExp(`^${glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i");
|
|
12321
12418
|
var resolveBranchPolicy = (config, workspace, repoPreset) => {
|
|
12322
12419
|
const wp = workspace?.branchPolicy ?? {};
|
|
@@ -12360,7 +12457,7 @@ var resolveCommitPolicy = (config, workspace) => {
|
|
|
12360
12457
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
12361
12458
|
import { createHash as createHash7 } from "node:crypto";
|
|
12362
12459
|
import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
12363
|
-
import
|
|
12460
|
+
import path12 from "node:path";
|
|
12364
12461
|
|
|
12365
12462
|
// packages/workit-core/src/core/branch-policy.ts
|
|
12366
12463
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
@@ -12486,7 +12583,7 @@ var releaseTrackSchema = object({
|
|
|
12486
12583
|
context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
|
|
12487
12584
|
}
|
|
12488
12585
|
}
|
|
12489
|
-
if (track.versionSource?.kind === "package-json" && (
|
|
12586
|
+
if (track.versionSource?.kind === "package-json" && (path12.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
|
|
12490
12587
|
context.addIssue({
|
|
12491
12588
|
code: "custom",
|
|
12492
12589
|
path: ["versionSource", "path"],
|
|
@@ -12506,6 +12603,7 @@ var workspaceConfigSchema = object({
|
|
|
12506
12603
|
autoApprove: union([boolean2(), array(nonBlank)]).optional(),
|
|
12507
12604
|
defaultEndpoint: unknown().optional(),
|
|
12508
12605
|
verification: _enum(["self", "independent"]).optional(),
|
|
12606
|
+
trailers: boolean2().optional(),
|
|
12509
12607
|
autonomy: object(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
|
|
12510
12608
|
kind,
|
|
12511
12609
|
union([boolean2(), literal("verified")]).optional()
|
|
@@ -12584,7 +12682,7 @@ var validateWorkspacesDocument = (parsed, file) => {
|
|
|
12584
12682
|
};
|
|
12585
12683
|
};
|
|
12586
12684
|
var readWorkspacesResult = (dir = configDir()) => {
|
|
12587
|
-
const file =
|
|
12685
|
+
const file = path12.join(dir, "workspaces.json");
|
|
12588
12686
|
let raw;
|
|
12589
12687
|
try {
|
|
12590
12688
|
raw = readFileSync3(file, "utf8");
|
|
@@ -12695,9 +12793,9 @@ var mainCheckoutOf = (cwd) => {
|
|
|
12695
12793
|
let main = null;
|
|
12696
12794
|
const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
|
|
12697
12795
|
const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
|
|
12698
|
-
if (gitDir && common &&
|
|
12796
|
+
if (gitDir && common && path12.resolve(gitDir) !== path12.resolve(common)) {
|
|
12699
12797
|
const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
|
|
12700
|
-
main = listed?.trim() || (
|
|
12798
|
+
main = listed?.trim() || (path12.basename(common) === ".git" ? path12.dirname(common) : common);
|
|
12701
12799
|
}
|
|
12702
12800
|
mainCheckouts.set(cwd, main);
|
|
12703
12801
|
return main;
|
|
@@ -12787,8 +12885,8 @@ var resolveRuntimeWorkspaceVcs = (cwd) => resolveRuntimeWorkspaceCandidate(cwd,
|
|
|
12787
12885
|
var runtimeWorkspaceIndexSchema = object({
|
|
12788
12886
|
workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
12789
12887
|
}).passthrough();
|
|
12790
|
-
var matchRuntimeWorkspace = (cwd) => {
|
|
12791
|
-
const file =
|
|
12888
|
+
var matchRuntimeWorkspace = (cwd, dir = configDir()) => {
|
|
12889
|
+
const file = path12.join(dir, "workspaces.json");
|
|
12792
12890
|
let raw;
|
|
12793
12891
|
try {
|
|
12794
12892
|
raw = readFileSync3(file, "utf8");
|
|
@@ -12817,12 +12915,30 @@ var matchRuntimeWorkspace = (cwd) => {
|
|
|
12817
12915
|
const matches = matchingEntries(cwd, entries ?? []);
|
|
12818
12916
|
const workspaceName = process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined;
|
|
12819
12917
|
const selected = selectWorkspaceMatch(matches, cwd, workspaceName);
|
|
12820
|
-
return selected ? { file, selected } : null;
|
|
12918
|
+
return selected ? { file, selected, matches } : null;
|
|
12821
12919
|
};
|
|
12822
12920
|
var resolveRuntimeReleaseTracks = (cwd) => {
|
|
12823
12921
|
const matched = matchRuntimeWorkspace(cwd);
|
|
12824
12922
|
return matched ? { workspace: matched.selected.name, releaseTracks: matched.selected.releaseTracks } : null;
|
|
12825
12923
|
};
|
|
12924
|
+
var workspaceTrailers = (cwd, dir) => {
|
|
12925
|
+
let matched;
|
|
12926
|
+
try {
|
|
12927
|
+
matched = matchRuntimeWorkspace(cwd, dir);
|
|
12928
|
+
} catch {
|
|
12929
|
+
return { enabled: true, workspace: null, from: null };
|
|
12930
|
+
}
|
|
12931
|
+
if (!matched)
|
|
12932
|
+
return { enabled: true, workspace: null, from: null };
|
|
12933
|
+
const { selected, matches } = matched;
|
|
12934
|
+
const enclosing = matches.filter((entry) => entry !== selected).toSorted((a, b) => globSpecificity(b.glob) - globSpecificity(a.glob));
|
|
12935
|
+
const source = [selected, ...enclosing].find((entry) => typeof entry.trailers === "boolean");
|
|
12936
|
+
return {
|
|
12937
|
+
enabled: source?.trailers !== false,
|
|
12938
|
+
workspace: selected.name,
|
|
12939
|
+
from: source?.name ?? null
|
|
12940
|
+
};
|
|
12941
|
+
};
|
|
12826
12942
|
var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
12827
12943
|
const matched = matchRuntimeWorkspace(cwd);
|
|
12828
12944
|
if (!matched)
|
|
@@ -13159,7 +13275,7 @@ var git3 = (cwd, args) => {
|
|
|
13159
13275
|
return run.status === 0 ? (run.stdout ?? "").trim() : null;
|
|
13160
13276
|
};
|
|
13161
13277
|
var recordedBaseKey = (branch) => `branch.${branch}.workitBase`;
|
|
13162
|
-
var
|
|
13278
|
+
var escapeRegExp2 = (text) => text.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
13163
13279
|
var reflog = (cwd, ref) => (git3(cwd, ["log", "-g", "--date=unix", "--format=%H%x09%gd%x09%gs", ref, "--"]) ?? "").split(`
|
|
13164
13280
|
`).map((line) => {
|
|
13165
13281
|
const [sha = "", selector = "", ...subject] = line.split("\t");
|
|
@@ -13191,7 +13307,7 @@ function gitTrackProbe(cwd) {
|
|
|
13191
13307
|
const created = own.at(-1);
|
|
13192
13308
|
if (!created || created.subject !== "branch: Created from HEAD")
|
|
13193
13309
|
return null;
|
|
13194
|
-
const pattern = new RegExp(`^checkout: moving from (\\S+) to ${
|
|
13310
|
+
const pattern = new RegExp(`^checkout: moving from (\\S+) to ${escapeRegExp2(branch)}$`, "u");
|
|
13195
13311
|
const entry = reflog(cwd, "HEAD").findLast((item) => item.time >= created.time && item.sha === created.sha && pattern.test(item.subject));
|
|
13196
13312
|
return entry ? pattern.exec(entry.subject)?.[1] ?? null : null;
|
|
13197
13313
|
},
|
|
@@ -13261,7 +13377,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
13261
13377
|
}
|
|
13262
13378
|
|
|
13263
13379
|
// packages/workit-core/src/core/vcs-config.ts
|
|
13264
|
-
var workspacesPath = () =>
|
|
13380
|
+
var workspacesPath = () => path13.join(configDir(), "workspaces.json");
|
|
13265
13381
|
var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
13266
13382
|
var remoteProvider = (cwd) => {
|
|
13267
13383
|
const r = spawnSync8("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
|
|
@@ -13363,7 +13479,7 @@ function vcsConfig(mode, cwd, options = {}) {
|
|
|
13363
13479
|
const prov = cfg[provider] ?? {};
|
|
13364
13480
|
const out = {
|
|
13365
13481
|
ok: true,
|
|
13366
|
-
configPath:
|
|
13482
|
+
configPath: path13.resolve(cfgPath),
|
|
13367
13483
|
provider,
|
|
13368
13484
|
defaultTargetBranch: workspaceDefault,
|
|
13369
13485
|
pr: cfg.pr ?? {},
|
|
@@ -13501,14 +13617,14 @@ function diffHash(cwd, base, head) {
|
|
|
13501
13617
|
return `sha256:${hash.digest("hex")}`;
|
|
13502
13618
|
}
|
|
13503
13619
|
function storeRoot(cwd) {
|
|
13504
|
-
const location = resolveStore(
|
|
13620
|
+
const location = resolveStore(path14.resolve(cwd));
|
|
13505
13621
|
if (location instanceof Error)
|
|
13506
13622
|
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");
|
|
13507
13623
|
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
13508
13624
|
}
|
|
13509
13625
|
function ledgerPath(cwd) {
|
|
13510
13626
|
const root = storeRoot(cwd);
|
|
13511
|
-
return root.ok ? { ok: true, value:
|
|
13627
|
+
return root.ok ? { ok: true, value: path14.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
13512
13628
|
}
|
|
13513
13629
|
var newId2 = () => `${Date.now().toString(36)}-${randomBytes2(6).toString("hex")}`;
|
|
13514
13630
|
var NETWORK_FS_TYPES = new Set([
|
|
@@ -13538,13 +13654,13 @@ var LOCK_STALE_MS = 30000;
|
|
|
13538
13654
|
var LOCK_OWNER = "owner";
|
|
13539
13655
|
var lockToken = (lock) => {
|
|
13540
13656
|
try {
|
|
13541
|
-
return fs12.readFileSync(
|
|
13657
|
+
return fs12.readFileSync(path14.join(lock, LOCK_OWNER), "utf8");
|
|
13542
13658
|
} catch {
|
|
13543
13659
|
return null;
|
|
13544
13660
|
}
|
|
13545
13661
|
};
|
|
13546
13662
|
var lockAgeMs = (lock) => {
|
|
13547
|
-
for (const target of [
|
|
13663
|
+
for (const target of [path14.join(lock, LOCK_OWNER), lock]) {
|
|
13548
13664
|
try {
|
|
13549
13665
|
return Date.now() - fs12.statSync(target).mtimeMs;
|
|
13550
13666
|
} catch {}
|
|
@@ -13561,7 +13677,7 @@ var ledgerLock = {
|
|
|
13561
13677
|
throw error;
|
|
13562
13678
|
}
|
|
13563
13679
|
const token = `${process.pid}-${randomBytes2(8).toString("hex")}`;
|
|
13564
|
-
fs12.writeFileSync(
|
|
13680
|
+
fs12.writeFileSync(path14.join(lock, LOCK_OWNER), token);
|
|
13565
13681
|
return token;
|
|
13566
13682
|
},
|
|
13567
13683
|
release(lock, token) {
|
|
@@ -13686,8 +13802,8 @@ function appendLine(file, build, options) {
|
|
|
13686
13802
|
}
|
|
13687
13803
|
};
|
|
13688
13804
|
try {
|
|
13689
|
-
fs12.mkdirSync(
|
|
13690
|
-
if (!options.forceLock && !onNetworkFs(
|
|
13805
|
+
fs12.mkdirSync(path14.dirname(file), { recursive: true });
|
|
13806
|
+
if (!options.forceLock && !onNetworkFs(path14.dirname(file)))
|
|
13691
13807
|
return write();
|
|
13692
13808
|
const locked = withLedgerLock(file, write);
|
|
13693
13809
|
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
@@ -14250,7 +14366,7 @@ var productDecision = (task, ledger) => {
|
|
|
14250
14366
|
};
|
|
14251
14367
|
};
|
|
14252
14368
|
var planRecorded = (task, root) => {
|
|
14253
|
-
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(
|
|
14369
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path15.resolve(root, ref.path)));
|
|
14254
14370
|
if (cited)
|
|
14255
14371
|
return {
|
|
14256
14372
|
met: true,
|
|
@@ -14310,14 +14426,14 @@ function writeBlockers(task, root) {
|
|
|
14310
14426
|
|
|
14311
14427
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
14312
14428
|
var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
|
|
14313
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
14314
|
-
var relative = (root, candidate) =>
|
|
14429
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path16.sep}`);
|
|
14430
|
+
var relative = (root, candidate) => path16.relative(root, candidate).split(path16.sep).join("/") || ".";
|
|
14315
14431
|
var canonicalPath = (value) => {
|
|
14316
14432
|
if (typeof value !== "string" || !value)
|
|
14317
14433
|
return null;
|
|
14318
14434
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
14319
14435
|
return null;
|
|
14320
|
-
const normalized =
|
|
14436
|
+
const normalized = path16.posix.normalize(value);
|
|
14321
14437
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
14322
14438
|
return null;
|
|
14323
14439
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -14365,14 +14481,14 @@ var gitPaths = (root) => {
|
|
|
14365
14481
|
git: !/not a git repository/i.test(stderr)
|
|
14366
14482
|
};
|
|
14367
14483
|
}
|
|
14368
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
14484
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")) : [];
|
|
14369
14485
|
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
14370
14486
|
if (stagedDeleted.status !== 0) {
|
|
14371
14487
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
14372
14488
|
if (!/not a git repository/i.test(stderr))
|
|
14373
14489
|
return { paths, uncertain: true, git: true };
|
|
14374
14490
|
} else if (stagedDeleted.stdout) {
|
|
14375
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
14491
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")));
|
|
14376
14492
|
}
|
|
14377
14493
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
14378
14494
|
};
|
|
@@ -14387,7 +14503,7 @@ var walk = (root, directory, output) => {
|
|
|
14387
14503
|
for (const name of names) {
|
|
14388
14504
|
if (name === ".git" || name === ".workit")
|
|
14389
14505
|
continue;
|
|
14390
|
-
const target =
|
|
14506
|
+
const target = path16.join(directory, name);
|
|
14391
14507
|
let stat;
|
|
14392
14508
|
try {
|
|
14393
14509
|
stat = fs13.lstatSync(target);
|
|
@@ -14405,12 +14521,12 @@ var walk = (root, directory, output) => {
|
|
|
14405
14521
|
var scopeRoots = (root, scope) => {
|
|
14406
14522
|
const roots = [];
|
|
14407
14523
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
14408
|
-
const target =
|
|
14524
|
+
const target = path16.resolve(root, item);
|
|
14409
14525
|
if (!inside(root, target))
|
|
14410
14526
|
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` }] });
|
|
14411
14527
|
let ancestor = target;
|
|
14412
14528
|
while (!fs13.existsSync(ancestor) && ancestor !== root)
|
|
14413
|
-
ancestor =
|
|
14529
|
+
ancestor = path16.dirname(ancestor);
|
|
14414
14530
|
try {
|
|
14415
14531
|
if (!inside(root, fs13.realpathSync(ancestor)))
|
|
14416
14532
|
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` }] });
|
|
@@ -14462,7 +14578,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
14462
14578
|
for (const item of [...names].toSorted()) {
|
|
14463
14579
|
if (!scopeMatches(item, normalizedScope))
|
|
14464
14580
|
continue;
|
|
14465
|
-
const target =
|
|
14581
|
+
const target = path16.join(checkout, item);
|
|
14466
14582
|
try {
|
|
14467
14583
|
const stat = fs13.lstatSync(target);
|
|
14468
14584
|
if (stat.isSymbolicLink()) {
|
|
@@ -14646,23 +14762,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
14646
14762
|
continue;
|
|
14647
14763
|
if (!reference.digest)
|
|
14648
14764
|
return failure2("invalid_input", "document references require a byte digest");
|
|
14649
|
-
const target =
|
|
14650
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
14765
|
+
const target = path16.resolve(root, reference.path);
|
|
14766
|
+
if (target !== root && !target.startsWith(`${root}${path16.sep}`))
|
|
14651
14767
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
14652
14768
|
fields: [
|
|
14653
14769
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
14654
14770
|
]
|
|
14655
14771
|
});
|
|
14656
14772
|
try {
|
|
14657
|
-
const below =
|
|
14773
|
+
const below = path16.relative(root, target);
|
|
14658
14774
|
let current = root;
|
|
14659
|
-
for (const segment of below.split(
|
|
14775
|
+
for (const segment of below.split(path16.sep)) {
|
|
14660
14776
|
if (!segment)
|
|
14661
14777
|
continue;
|
|
14662
|
-
current =
|
|
14778
|
+
current = path16.join(current, segment);
|
|
14663
14779
|
const stat = fs13.lstatSync(current);
|
|
14664
14780
|
const real = fs13.realpathSync(current);
|
|
14665
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
14781
|
+
if (real !== root && !real.startsWith(`${root}${path16.sep}`))
|
|
14666
14782
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
14667
14783
|
fields: [
|
|
14668
14784
|
{
|
|
@@ -15534,20 +15650,20 @@ var judgeInputSummary = (raw) => {
|
|
|
15534
15650
|
};
|
|
15535
15651
|
|
|
15536
15652
|
// packages/workit-core/src/autonomy.ts
|
|
15537
|
-
import
|
|
15538
|
-
import
|
|
15653
|
+
import os4 from "node:os";
|
|
15654
|
+
import path17 from "node:path";
|
|
15539
15655
|
var accountHome = () => {
|
|
15540
15656
|
try {
|
|
15541
|
-
return
|
|
15657
|
+
return os4.userInfo().homedir;
|
|
15542
15658
|
} catch {
|
|
15543
|
-
return
|
|
15659
|
+
return os4.homedir();
|
|
15544
15660
|
}
|
|
15545
15661
|
};
|
|
15546
15662
|
var grantsHome = { resolve: accountHome };
|
|
15547
|
-
var grantsDir = () =>
|
|
15663
|
+
var grantsDir = () => path17.join(grantsHome.resolve(), ".config", "workit");
|
|
15548
15664
|
var grantsOverride = () => {
|
|
15549
|
-
const configured =
|
|
15550
|
-
return configured ===
|
|
15665
|
+
const configured = path17.resolve(resolveConfigDir());
|
|
15666
|
+
return configured === path17.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 ${path17.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
|
|
15551
15667
|
};
|
|
15552
15668
|
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
15553
15669
|
var DEFAULT_GRANTS = Object.freeze({
|
|
@@ -17529,7 +17645,7 @@ function entryCompactContext(store, entry, session, context) {
|
|
|
17529
17645
|
return null;
|
|
17530
17646
|
const freshness = { mode: "signal", current: () => recentSignal(store.root) };
|
|
17531
17647
|
const observed = task?.data.evidence.some(isObservedCheck) ?? false;
|
|
17532
|
-
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [
|
|
17648
|
+
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path18.resolve(store.root, ref.path)] : []));
|
|
17533
17649
|
const observedFiles = filesKey(files);
|
|
17534
17650
|
const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
|
|
17535
17651
|
if (!compact.ok || !compact.data)
|
|
@@ -17607,14 +17723,14 @@ var turnMarker = (input) => {
|
|
|
17607
17723
|
if (location instanceof Error)
|
|
17608
17724
|
return null;
|
|
17609
17725
|
const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
|
|
17610
|
-
return
|
|
17726
|
+
return path18.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
|
|
17611
17727
|
} catch {
|
|
17612
17728
|
return null;
|
|
17613
17729
|
}
|
|
17614
17730
|
};
|
|
17615
17731
|
var writeTurnDigest = (file, digest) => {
|
|
17616
17732
|
try {
|
|
17617
|
-
fs14.mkdirSync(
|
|
17733
|
+
fs14.mkdirSync(path18.dirname(file), { recursive: true });
|
|
17618
17734
|
fs14.writeFileSync(file, `${JSON.stringify({ digest })}
|
|
17619
17735
|
`);
|
|
17620
17736
|
} catch {}
|
|
@@ -17629,7 +17745,7 @@ var pruneTurnDigests = (dir, now) => {
|
|
|
17629
17745
|
for (const name of fs14.readdirSync(dir)) {
|
|
17630
17746
|
if (!name.startsWith("turn-"))
|
|
17631
17747
|
continue;
|
|
17632
|
-
const file =
|
|
17748
|
+
const file = path18.join(dir, name);
|
|
17633
17749
|
try {
|
|
17634
17750
|
if (now - fs14.statSync(file).mtimeMs > TURN_TTL_MS)
|
|
17635
17751
|
fs14.rmSync(file, { force: true });
|
|
@@ -17659,7 +17775,7 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
|
|
|
17659
17775
|
const file = turnMarker(input);
|
|
17660
17776
|
if (!file)
|
|
17661
17777
|
return;
|
|
17662
|
-
pruneTurnDigests(
|
|
17778
|
+
pruneTurnDigests(path18.dirname(file), Date.now());
|
|
17663
17779
|
if (text)
|
|
17664
17780
|
writeTurnDigest(file, digestOf(text));
|
|
17665
17781
|
else
|
|
@@ -17668,14 +17784,14 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
|
|
|
17668
17784
|
// packages/workit-core/src/core/route-intent.ts
|
|
17669
17785
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
17670
17786
|
import { existsSync as existsSync6 } from "node:fs";
|
|
17671
|
-
import
|
|
17787
|
+
import path21 from "node:path";
|
|
17672
17788
|
|
|
17673
17789
|
// packages/workit-core/src/hooks/raw-git.ts
|
|
17674
17790
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
17675
17791
|
import { createHash as createHash14 } from "node:crypto";
|
|
17676
17792
|
import fs16 from "node:fs";
|
|
17677
|
-
import
|
|
17678
|
-
import
|
|
17793
|
+
import os5 from "node:os";
|
|
17794
|
+
import path20 from "node:path";
|
|
17679
17795
|
|
|
17680
17796
|
// packages/workit-core/src/core/branch.ts
|
|
17681
17797
|
import { createHash as createHash12 } from "node:crypto";
|
|
@@ -17888,7 +18004,7 @@ var globOf = (pattern) => pattern.source.replace(/^\^|\$$/gu, "").replaceAll(".*
|
|
|
17888
18004
|
// packages/workit-core/src/hooks/session-marker.ts
|
|
17889
18005
|
import { createHash as createHash13 } from "node:crypto";
|
|
17890
18006
|
import fs15 from "node:fs";
|
|
17891
|
-
import
|
|
18007
|
+
import path19 from "node:path";
|
|
17892
18008
|
var sessionMarkerFile = (input, session, store) => {
|
|
17893
18009
|
let dir = store;
|
|
17894
18010
|
if (dir === undefined) {
|
|
@@ -17898,7 +18014,7 @@ var sessionMarkerFile = (input, session, store) => {
|
|
|
17898
18014
|
dir = location.dir;
|
|
17899
18015
|
}
|
|
17900
18016
|
const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
17901
|
-
return
|
|
18017
|
+
return path19.join(dir, "hooks", `skills-${name}.json`);
|
|
17902
18018
|
};
|
|
17903
18019
|
var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
17904
18020
|
var readSessionMarker = (file) => {
|
|
@@ -17911,7 +18027,7 @@ var readSessionMarker = (file) => {
|
|
|
17911
18027
|
};
|
|
17912
18028
|
var writeSessionMarker = (file, marker) => {
|
|
17913
18029
|
try {
|
|
17914
|
-
fs15.mkdirSync(
|
|
18030
|
+
fs15.mkdirSync(path19.dirname(file), { recursive: true });
|
|
17915
18031
|
fs15.writeFileSync(file, JSON.stringify(marker));
|
|
17916
18032
|
} catch {}
|
|
17917
18033
|
};
|
|
@@ -18056,12 +18172,12 @@ var GIT_VALUE_OPTIONS = new Set([
|
|
|
18056
18172
|
"--config-env",
|
|
18057
18173
|
"--super-prefix"
|
|
18058
18174
|
]);
|
|
18059
|
-
var isAbsolute = (value) =>
|
|
18175
|
+
var isAbsolute = (value) => path20.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
|
|
18060
18176
|
var joinDir = (base, next) => {
|
|
18061
18177
|
if (base === undefined || /[$`*?]/.test(next) || next === "-")
|
|
18062
18178
|
return;
|
|
18063
|
-
const expanded = next === "~" || next.startsWith("~/") ?
|
|
18064
|
-
return base === null || isAbsolute(expanded) ? expanded :
|
|
18179
|
+
const expanded = next === "~" || next.startsWith("~/") ? path20.join(os5.homedir(), next.slice(1)) : next;
|
|
18180
|
+
return base === null || isAbsolute(expanded) ? expanded : path20.join(base, expanded);
|
|
18065
18181
|
};
|
|
18066
18182
|
var cdTarget = (words) => {
|
|
18067
18183
|
const args = words.slice(1);
|
|
@@ -18112,7 +18228,7 @@ function rawInvocations(command, dialect = "posix", depth = 0) {
|
|
|
18112
18228
|
const name = commandName(words[0]);
|
|
18113
18229
|
if (CD.has(name)) {
|
|
18114
18230
|
const target = cdTarget(words);
|
|
18115
|
-
dir = target === null ?
|
|
18231
|
+
dir = target === null ? os5.homedir() : joinDir(dir, target);
|
|
18116
18232
|
continue;
|
|
18117
18233
|
}
|
|
18118
18234
|
if (PUSHD.has(name)) {
|
|
@@ -18370,17 +18486,17 @@ var headSha2 = (repo) => {
|
|
|
18370
18486
|
if (!git || git.reftable)
|
|
18371
18487
|
return null;
|
|
18372
18488
|
try {
|
|
18373
|
-
const head = fs16.readFileSync(
|
|
18489
|
+
const head = fs16.readFileSync(path20.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
18374
18490
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
18375
18491
|
return head;
|
|
18376
18492
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
18377
18493
|
if (!ref)
|
|
18378
18494
|
return null;
|
|
18379
|
-
const common =
|
|
18495
|
+
const common = path20.dirname(repo.location.dir);
|
|
18380
18496
|
try {
|
|
18381
|
-
return fs16.readFileSync(
|
|
18497
|
+
return fs16.readFileSync(path20.join(common, ref), "utf8").trim() || null;
|
|
18382
18498
|
} catch {}
|
|
18383
|
-
const packed = fs16.readFileSync(
|
|
18499
|
+
const packed = fs16.readFileSync(path20.join(common, "packed-refs"), "utf8");
|
|
18384
18500
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
18385
18501
|
} catch {
|
|
18386
18502
|
return null;
|
|
@@ -18469,7 +18585,7 @@ var actionsIn = (cwd, command, dialect) => {
|
|
|
18469
18585
|
const action = rawAction(invocation);
|
|
18470
18586
|
if (!action)
|
|
18471
18587
|
continue;
|
|
18472
|
-
const dir =
|
|
18588
|
+
const dir = path20.resolve(cwd, invocation.dir ?? ".");
|
|
18473
18589
|
if (!repos.has(dir)) {
|
|
18474
18590
|
const repo = repoAt(dir);
|
|
18475
18591
|
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
@@ -18549,7 +18665,7 @@ function rawGitPre(input, command, options = {}) {
|
|
|
18549
18665
|
var deny = (reason) => ({ kind: "deny", reason, unblock: null });
|
|
18550
18666
|
var NEXT_COMMAND = "next";
|
|
18551
18667
|
var MARKER_TTL_SECONDS = 3600;
|
|
18552
|
-
var markerFile = (repo, session, key) =>
|
|
18668
|
+
var markerFile = (repo, session, key) => path20.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
|
|
18553
18669
|
var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`;
|
|
18554
18670
|
function noteRawCommit(input, command, key) {
|
|
18555
18671
|
try {
|
|
@@ -18560,7 +18676,7 @@ function noteRawCommit(input, command, key) {
|
|
|
18560
18676
|
if (action.kind !== "commit")
|
|
18561
18677
|
continue;
|
|
18562
18678
|
const file = markerFile(repo, input.session.id, key);
|
|
18563
|
-
fs16.mkdirSync(
|
|
18679
|
+
fs16.mkdirSync(path20.dirname(file), { recursive: true });
|
|
18564
18680
|
fs16.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
18565
18681
|
`);
|
|
18566
18682
|
}
|
|
@@ -18733,7 +18849,7 @@ var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
|
|
|
18733
18849
|
for (const target of shellBranchTargets(command, dialect)) {
|
|
18734
18850
|
if (target.dir === undefined)
|
|
18735
18851
|
continue;
|
|
18736
|
-
const dir = target.dir === null ? root :
|
|
18852
|
+
const dir = target.dir === null ? root : path21.resolve(root, target.dir);
|
|
18737
18853
|
if (target.dir !== null && (!existsSync6(dir) || !inWorkitWorkspace(dir)))
|
|
18738
18854
|
continue;
|
|
18739
18855
|
if (localBranchExists(dir, target.branch))
|
|
@@ -18753,27 +18869,27 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
|
|
|
18753
18869
|
};
|
|
18754
18870
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
18755
18871
|
import { realpathSync as realpathSync5 } from "node:fs";
|
|
18756
|
-
import
|
|
18872
|
+
import path22 from "node:path";
|
|
18757
18873
|
var NONE3 = { kind: "none" };
|
|
18758
18874
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
18759
18875
|
var canonical2 = (file) => {
|
|
18760
18876
|
let rest = "";
|
|
18761
|
-
for (let dir =
|
|
18877
|
+
for (let dir = path22.resolve(file);; dir = path22.dirname(dir)) {
|
|
18762
18878
|
try {
|
|
18763
|
-
return
|
|
18879
|
+
return path22.join(realpathSync5(dir), rest);
|
|
18764
18880
|
} catch {
|
|
18765
|
-
if (
|
|
18766
|
-
return
|
|
18767
|
-
rest = rest ?
|
|
18881
|
+
if (path22.dirname(dir) === dir)
|
|
18882
|
+
return path22.resolve(file);
|
|
18883
|
+
rest = rest ? path22.join(path22.basename(dir), rest) : path22.basename(dir);
|
|
18768
18884
|
}
|
|
18769
18885
|
}
|
|
18770
18886
|
};
|
|
18771
18887
|
var exempt = (cwd, root, file, plans) => {
|
|
18772
18888
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
18773
|
-
const relative =
|
|
18774
|
-
if (relative.startsWith("..") ||
|
|
18889
|
+
const relative = path22.relative(fold(canonical2(root)), fold(canonical2(path22.resolve(cwd, file))));
|
|
18890
|
+
if (relative.startsWith("..") || path22.isAbsolute(relative))
|
|
18775
18891
|
return true;
|
|
18776
|
-
const posix = relative.split(
|
|
18892
|
+
const posix = relative.split(path22.sep).join("/");
|
|
18777
18893
|
const segments = posix.split("/");
|
|
18778
18894
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
18779
18895
|
};
|
|
@@ -18871,7 +18987,7 @@ var shellWrites = (command) => {
|
|
|
18871
18987
|
return { writes: true, targets: unknown ? null : targets };
|
|
18872
18988
|
};
|
|
18873
18989
|
var cache2 = new Map;
|
|
18874
|
-
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
18990
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path22.resolve(root, file)) ?? "-").join("\x00");
|
|
18875
18991
|
var gateFor = (cwd) => {
|
|
18876
18992
|
const store = new TaskStore(cwd);
|
|
18877
18993
|
const listed = store.listTaskIndex();
|
|
@@ -19312,12 +19428,12 @@ ${line}` };
|
|
|
19312
19428
|
};
|
|
19313
19429
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
19314
19430
|
import { existsSync as existsSync7, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
19315
|
-
import
|
|
19431
|
+
import path23 from "node:path";
|
|
19316
19432
|
var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
19317
19433
|
var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
|
|
19318
19434
|
var optionalText = (value) => typeof value === "string" ? value : null;
|
|
19319
19435
|
var existingDirectory = (value) => {
|
|
19320
|
-
if (!nonEmpty2(value) || !
|
|
19436
|
+
if (!nonEmpty2(value) || !path23.isAbsolute(value) || !existsSync7(value))
|
|
19321
19437
|
return null;
|
|
19322
19438
|
try {
|
|
19323
19439
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -19400,7 +19516,7 @@ var workitRole = (descriptor, agentType) => {
|
|
|
19400
19516
|
};
|
|
19401
19517
|
var subagentSession = (session, agentId) => [session, agentId].filter((part) => Boolean(part)).join(":").replace(/[^A-Za-z0-9_.:@/+-]/g, "-").slice(0, 128);
|
|
19402
19518
|
var CLAUDE_WORKTREE = /[\\/]\.claude[\\/]worktrees[\\/]/;
|
|
19403
|
-
var
|
|
19519
|
+
var isolatedGit = (cwd) => `Run git as plain, separate commands inside your worktree (\`git switch -c <branch> <base>\`, ${workspaceTrailers(cwd).enabled ? '`git commit --trailer "Workit-Session=<your session>"`, `git push`; `workit doctor --fix` can install a prepare-commit-msg hook that adds the trailer' : "`git commit` (this workspace sets trailers: false: add no Workit-Session trailer), `git push`"}; add Co-Authored-By with \`git commit --trailer\` in the same commit, never by amending), never \`workit git …\`: Claude Code's worktree isolation refuses a command that wraps git and cannot be turned off (https://code.claude.com/docs/en/worktrees#how-claude-code-enforces-isolation). Never dodge that check by renaming or re-wrapping git. Use workit for the non-git verbs (\`workit check\`, \`workit ledger\`, \`workit pr\`).`;
|
|
19404
19520
|
var subagentStartText = (input, descriptor, event) => {
|
|
19405
19521
|
const session = input.session.id ?? null;
|
|
19406
19522
|
const role = workitRole(descriptor, event.agentType);
|
|
@@ -19408,9 +19524,9 @@ var subagentStartText = (input, descriptor, event) => {
|
|
|
19408
19524
|
const isolated = descriptor.subagents.worktreeIsolation === "native" && (role === "implementer" || CLAUDE_WORKTREE.test(input.cwd));
|
|
19409
19525
|
const guide = role === "implementer" ? branchGuidance(input.cwd) : null;
|
|
19410
19526
|
if (role === "implementer" && isolated)
|
|
19411
|
-
return `Workit observed ${who} working in its own git worktree: it may edit and commit there, within its brief's scope. ${
|
|
19527
|
+
return `Workit observed ${who} working in its own git worktree: it may edit and commit there, within its brief's scope. ${isolatedGit(input.cwd)} Plain git is expected here, and the branch name is still policy-checked: a raw \`git switch -c\`, \`git checkout -b\`, \`git branch\` or \`git worktree add -b\` onto a protected or non-compliant name is denied, so use a policy-compliant branch.${guide ? ` ${guide}` : " (e.g. feature/<slug>)"} Never record a verdict on your own work. Never push, open a PR, or merge unless the brief asks for it.`;
|
|
19412
19528
|
if (isolated)
|
|
19413
|
-
return `${
|
|
19529
|
+
return `${isolatedGit(input.cwd)}
|
|
19414
19530
|
${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
|
|
19415
19531
|
if (role === "implementer")
|
|
19416
19532
|
return `Workit observed ${who} as an implementer: it edits and commits only in its own git worktree (the lead makes one with \`workit fanout worktree create <slice>\`), never in the lead's checkout, within its brief's scope. Before the first commit, switch to a policy-compliant branch with \`workit git branch <branch> --base <base>\` (or \`--kind <kind> --slug <slug>\`, which picks the base); branch policy hooks still deny protected or non-compliant branches, raw git included.${guide ? ` ${guide}` : " (e.g. feature/<slug>)"} Never record a verdict on your own work. Never push, open a PR, or merge unless the brief asks for it.`;
|
|
@@ -20056,7 +20172,7 @@ var codexAdapter = {
|
|
|
20056
20172
|
|
|
20057
20173
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
20058
20174
|
import fs17 from "node:fs";
|
|
20059
|
-
import
|
|
20175
|
+
import path24 from "node:path";
|
|
20060
20176
|
var undocumented = { support: "undocumented", native: null };
|
|
20061
20177
|
var none = { support: "none", native: null };
|
|
20062
20178
|
var CURSOR_DESCRIPTOR = {
|
|
@@ -20253,7 +20369,7 @@ var protocolEvent2 = (input) => {
|
|
|
20253
20369
|
};
|
|
20254
20370
|
var TRANSCRIPT_TAIL_BYTES = 256 * 1024;
|
|
20255
20371
|
var lastAssistantText = (file) => {
|
|
20256
|
-
if (!file || !
|
|
20372
|
+
if (!file || !path24.isAbsolute(file))
|
|
20257
20373
|
return null;
|
|
20258
20374
|
try {
|
|
20259
20375
|
const size = fs17.statSync(file).size;
|
|
@@ -20290,7 +20406,7 @@ var lastAssistantText = (file) => {
|
|
|
20290
20406
|
return null;
|
|
20291
20407
|
}
|
|
20292
20408
|
};
|
|
20293
|
-
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
20409
|
+
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path24.sep) ? root : `${root}${path24.sep}`);
|
|
20294
20410
|
var hookCwd = (input) => {
|
|
20295
20411
|
const cwd = existingDirectory(input.cwd);
|
|
20296
20412
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|