@brainervirus/workit-cursor 8.9.0 → 8.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor-plugin/plugin.json +1 -1
- package/dist/cursor-session-start.js +585 -421
- package/dist/mcp-server.js +240 -105
- package/dist/workit-hook.js +594 -416
- package/package.json +3 -3
- package/rules/workit-contract.mdc +4 -1
- package/skills/workit-fanout/references/brief.md +1 -1
- package/skills/workit-fanout/references/orchestration.md +16 -6
- package/skills/workit-implement/SKILL.md +3 -2
- package/skills/workit-review/SKILL.md +4 -2
|
@@ -9832,37 +9832,6 @@ var init_vcs_config = __esm(() => {
|
|
|
9832
9832
|
init_release_tracks();
|
|
9833
9833
|
});
|
|
9834
9834
|
|
|
9835
|
-
// packages/workit-core/src/host-session.ts
|
|
9836
|
-
function hostSessionFromEnv(env) {
|
|
9837
|
-
const value = (key) => env[key]?.trim() || null;
|
|
9838
|
-
let inner = null;
|
|
9839
|
-
for (const [key, host] of HOST_SESSION_VARS) {
|
|
9840
|
-
const session = value(key);
|
|
9841
|
-
if (session) {
|
|
9842
|
-
inner = { host: host(env), session };
|
|
9843
|
-
break;
|
|
9844
|
-
}
|
|
9845
|
-
}
|
|
9846
|
-
const declared = value("WORKIT_HOST");
|
|
9847
|
-
if (env.WORKIT_SESSION_ID !== undefined) {
|
|
9848
|
-
const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
|
|
9849
|
-
if (!inherited)
|
|
9850
|
-
return { host: declared, session: value("WORKIT_SESSION_ID") };
|
|
9851
|
-
}
|
|
9852
|
-
return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
|
|
9853
|
-
}
|
|
9854
|
-
var HOST_SESSION_VARS, family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null;
|
|
9855
|
-
var init_host_session = __esm(() => {
|
|
9856
|
-
HOST_SESSION_VARS = [
|
|
9857
|
-
["OPENCODE_SESSION_ID", () => "opencode"],
|
|
9858
|
-
["PI_SESSION_ID", () => "pi"],
|
|
9859
|
-
[
|
|
9860
|
-
"CODEX_THREAD_ID",
|
|
9861
|
-
(env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
|
|
9862
|
-
]
|
|
9863
|
-
];
|
|
9864
|
-
});
|
|
9865
|
-
|
|
9866
9835
|
// packages/workit-core/src/store/paths.ts
|
|
9867
9836
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
9868
9837
|
import { createHash as createHash5 } from "node:crypto";
|
|
@@ -10139,11 +10108,69 @@ var init_paths = __esm(() => {
|
|
|
10139
10108
|
];
|
|
10140
10109
|
});
|
|
10141
10110
|
|
|
10142
|
-
// packages/workit-core/src/
|
|
10143
|
-
import {
|
|
10144
|
-
import { createHash as createHash6, randomBytes } from "node:crypto";
|
|
10111
|
+
// packages/workit-core/src/host-session.ts
|
|
10112
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
10145
10113
|
import fs4 from "node:fs";
|
|
10146
10114
|
import path6 from "node:path";
|
|
10115
|
+
function hostSessionFromEnv(env) {
|
|
10116
|
+
const value = (key) => env[key]?.trim() || null;
|
|
10117
|
+
let inner = null;
|
|
10118
|
+
for (const [key, host] of HOST_SESSION_VARS) {
|
|
10119
|
+
const session = value(key);
|
|
10120
|
+
if (session) {
|
|
10121
|
+
inner = { host: host(env), session };
|
|
10122
|
+
break;
|
|
10123
|
+
}
|
|
10124
|
+
}
|
|
10125
|
+
const declared = value("WORKIT_HOST");
|
|
10126
|
+
if (env.WORKIT_SESSION_ID !== undefined) {
|
|
10127
|
+
const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
|
|
10128
|
+
if (!inherited)
|
|
10129
|
+
return { host: declared, session: value("WORKIT_SESSION_ID") };
|
|
10130
|
+
}
|
|
10131
|
+
return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
|
|
10132
|
+
}
|
|
10133
|
+
function recordIssuedSession(cwd, lead, issued) {
|
|
10134
|
+
try {
|
|
10135
|
+
const dir = issuedDir(cwd, lead);
|
|
10136
|
+
if (!dir)
|
|
10137
|
+
return;
|
|
10138
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
10139
|
+
const own = hash(issued);
|
|
10140
|
+
fs4.writeFileSync(path6.join(dir, own), ISSUED_SESSIONS_MARKER);
|
|
10141
|
+
const entries = fs4.readdirSync(dir);
|
|
10142
|
+
if (entries.length <= ISSUED_PER_LEAD)
|
|
10143
|
+
return;
|
|
10144
|
+
const byAge = entries.filter((name) => name !== own).map((name) => ({ name, at: fs4.statSync(path6.join(dir, name)).mtimeMs })).toSorted((a, b) => a.at - b.at);
|
|
10145
|
+
for (const { name } of byAge.slice(0, entries.length - ISSUED_PER_LEAD))
|
|
10146
|
+
fs4.rmSync(path6.join(dir, name), { force: true });
|
|
10147
|
+
} catch {}
|
|
10148
|
+
}
|
|
10149
|
+
var HOST_SESSION_VARS, family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null, ISSUED_PER_LEAD = 64, ISSUED_SESSIONS_MARKER = "workit-issued-sessions/1", hash = (value) => createHash6("sha256").update(value).digest("hex").slice(0, 32), issuedRoot = (cwd) => {
|
|
10150
|
+
const location = resolveStore(cwd);
|
|
10151
|
+
return location instanceof Error ? null : path6.join(location.dir, "hooks", "issued");
|
|
10152
|
+
}, issuedDir = (cwd, lead) => {
|
|
10153
|
+
const root = issuedRoot(cwd);
|
|
10154
|
+
return root === null ? null : path6.join(root, hash(lead));
|
|
10155
|
+
}, OWN_CHILD_SHELL;
|
|
10156
|
+
var init_host_session = __esm(() => {
|
|
10157
|
+
init_paths();
|
|
10158
|
+
HOST_SESSION_VARS = [
|
|
10159
|
+
["OPENCODE_SESSION_ID", () => "opencode"],
|
|
10160
|
+
["PI_SESSION_ID", () => "pi"],
|
|
10161
|
+
[
|
|
10162
|
+
"CODEX_THREAD_ID",
|
|
10163
|
+
(env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
|
|
10164
|
+
]
|
|
10165
|
+
];
|
|
10166
|
+
OWN_CHILD_SHELL = new Set(["opencode", "pi"]);
|
|
10167
|
+
});
|
|
10168
|
+
|
|
10169
|
+
// packages/workit-core/src/ledger.ts
|
|
10170
|
+
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
10171
|
+
import { createHash as createHash7, randomBytes } from "node:crypto";
|
|
10172
|
+
import fs5 from "node:fs";
|
|
10173
|
+
import path7 from "node:path";
|
|
10147
10174
|
function resolveCommit(cwd, ref) {
|
|
10148
10175
|
if (!safeRef(ref))
|
|
10149
10176
|
return null;
|
|
@@ -10197,7 +10224,7 @@ function diffHash(cwd, base, head) {
|
|
|
10197
10224
|
], GIT_TIMEOUTS.worktree);
|
|
10198
10225
|
if (!diff.ok || !diff.stdout)
|
|
10199
10226
|
return null;
|
|
10200
|
-
const hash =
|
|
10227
|
+
const hash = createHash7("sha256");
|
|
10201
10228
|
for (const line of diff.stdout.split(`
|
|
10202
10229
|
`)) {
|
|
10203
10230
|
if (line.startsWith("index "))
|
|
@@ -10209,14 +10236,14 @@ function diffHash(cwd, base, head) {
|
|
|
10209
10236
|
return `sha256:${hash.digest("hex")}`;
|
|
10210
10237
|
}
|
|
10211
10238
|
function storeRoot(cwd) {
|
|
10212
|
-
const location = resolveStore(
|
|
10239
|
+
const location = resolveStore(path7.resolve(cwd));
|
|
10213
10240
|
if (location instanceof Error)
|
|
10214
10241
|
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");
|
|
10215
10242
|
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
10216
10243
|
}
|
|
10217
10244
|
function ledgerPath(cwd) {
|
|
10218
10245
|
const root = storeRoot(cwd);
|
|
10219
|
-
return root.ok ? { ok: true, value:
|
|
10246
|
+
return root.ok ? { ok: true, value: path7.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
10220
10247
|
}
|
|
10221
10248
|
function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
10222
10249
|
const lock = `${file}.lock`;
|
|
@@ -10242,13 +10269,13 @@ function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
|
10242
10269
|
}
|
|
10243
10270
|
function appendLine(file, build, options) {
|
|
10244
10271
|
const write = () => {
|
|
10245
|
-
const fd =
|
|
10272
|
+
const fd = fs5.openSync(file, "a+");
|
|
10246
10273
|
try {
|
|
10247
|
-
const size =
|
|
10274
|
+
const size = fs5.fstatSync(fd).size;
|
|
10248
10275
|
let fence = "";
|
|
10249
10276
|
if (size > 0) {
|
|
10250
10277
|
const last = Buffer.alloc(1);
|
|
10251
|
-
|
|
10278
|
+
fs5.readSync(fd, last, 0, 1, size - 1);
|
|
10252
10279
|
if (last[0] !== 10)
|
|
10253
10280
|
fence = `
|
|
10254
10281
|
`;
|
|
@@ -10257,17 +10284,17 @@ function appendLine(file, build, options) {
|
|
|
10257
10284
|
if (!line.ok)
|
|
10258
10285
|
return line;
|
|
10259
10286
|
const bytes = Buffer.from(fence + line.value);
|
|
10260
|
-
const written =
|
|
10287
|
+
const written = fs5.writeSync(fd, bytes);
|
|
10261
10288
|
if (written !== bytes.length)
|
|
10262
10289
|
return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
|
|
10263
10290
|
return { ok: true, value: undefined };
|
|
10264
10291
|
} finally {
|
|
10265
|
-
|
|
10292
|
+
fs5.closeSync(fd);
|
|
10266
10293
|
}
|
|
10267
10294
|
};
|
|
10268
10295
|
try {
|
|
10269
|
-
|
|
10270
|
-
if (!options.forceLock && !onNetworkFs(
|
|
10296
|
+
fs5.mkdirSync(path7.dirname(file), { recursive: true });
|
|
10297
|
+
if (!options.forceLock && !onNetworkFs(path7.dirname(file)))
|
|
10271
10298
|
return write();
|
|
10272
10299
|
const locked = withLedgerLock(file, write);
|
|
10273
10300
|
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
@@ -10321,7 +10348,7 @@ function readLedger(cwd, options = {}) {
|
|
|
10321
10348
|
return file;
|
|
10322
10349
|
let text = "";
|
|
10323
10350
|
try {
|
|
10324
|
-
text =
|
|
10351
|
+
text = fs5.readFileSync(file.value, "utf8");
|
|
10325
10352
|
} catch (error) {
|
|
10326
10353
|
if (error.code === "ENOENT")
|
|
10327
10354
|
return {
|
|
@@ -10676,28 +10703,28 @@ var LEDGER_VERSION = 1, MAX_LINE_BYTES = 4096, PASSING, FAILING, STRONG_RESULTS,
|
|
|
10676
10703
|
if (process.platform !== "linux")
|
|
10677
10704
|
return false;
|
|
10678
10705
|
try {
|
|
10679
|
-
return isNetworkFsType(
|
|
10706
|
+
return isNetworkFsType(fs5.statfsSync(dir).type);
|
|
10680
10707
|
} catch {
|
|
10681
10708
|
return false;
|
|
10682
10709
|
}
|
|
10683
10710
|
}, LOCK_WAIT_MS = 5000, LOCK_STALE_MS = 30000, LOCK_OWNER = "owner", lockToken = (lock) => {
|
|
10684
10711
|
try {
|
|
10685
|
-
return
|
|
10712
|
+
return fs5.readFileSync(path7.join(lock, LOCK_OWNER), "utf8");
|
|
10686
10713
|
} catch {
|
|
10687
10714
|
return null;
|
|
10688
10715
|
}
|
|
10689
10716
|
}, lockAgeMs = (lock) => {
|
|
10690
|
-
for (const target of [
|
|
10717
|
+
for (const target of [path7.join(lock, LOCK_OWNER), lock]) {
|
|
10691
10718
|
try {
|
|
10692
|
-
return Date.now() -
|
|
10719
|
+
return Date.now() - fs5.statSync(target).mtimeMs;
|
|
10693
10720
|
} catch {}
|
|
10694
10721
|
}
|
|
10695
10722
|
return null;
|
|
10696
|
-
}, ledgerLock, sha2562 = (text) =>
|
|
10723
|
+
}, ledgerLock, sha2562 = (text) => createHash7("sha256").update(text).digest("hex"), chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
|
|
10697
10724
|
${body}`), TAIL_BYTES, lastLine = (fd, size) => {
|
|
10698
10725
|
for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
|
|
10699
10726
|
const buffer = Buffer.alloc(span);
|
|
10700
|
-
|
|
10727
|
+
fs5.readSync(fd, buffer, 0, span, size - span);
|
|
10701
10728
|
const end = buffer.lastIndexOf(10);
|
|
10702
10729
|
if (end < 0) {
|
|
10703
10730
|
if (span === size)
|
|
@@ -10749,11 +10776,18 @@ ${body}`), TAIL_BYTES, lastLine = (fd, size) => {
|
|
|
10749
10776
|
return false;
|
|
10750
10777
|
return scopeOf(own)({ branch: theirs, at: target.at });
|
|
10751
10778
|
};
|
|
10779
|
+
}, derivesFrom = (derived, bare) => !bare.includes(":") && derived.endsWith(`:${bare}`), authoredBy = (authors, session) => {
|
|
10780
|
+
if (authors.has(session))
|
|
10781
|
+
return true;
|
|
10782
|
+
for (const author of authors)
|
|
10783
|
+
if (derivesFrom(author, session) || derivesFrom(session, author))
|
|
10784
|
+
return true;
|
|
10785
|
+
return false;
|
|
10752
10786
|
}, independenceReasons = (row, authors) => {
|
|
10753
10787
|
const reasons = [];
|
|
10754
10788
|
if (!row.actor.session)
|
|
10755
10789
|
reasons.push("no_session");
|
|
10756
|
-
else if (authors
|
|
10790
|
+
else if (authoredBy(authors, row.actor.session))
|
|
10757
10791
|
reasons.push("author_session");
|
|
10758
10792
|
if (row.self === true && !reasons.includes("no_session"))
|
|
10759
10793
|
reasons.push("self");
|
|
@@ -10783,14 +10817,14 @@ var init_ledger = __esm(() => {
|
|
|
10783
10817
|
ledgerLock = {
|
|
10784
10818
|
acquire(lock) {
|
|
10785
10819
|
try {
|
|
10786
|
-
|
|
10820
|
+
fs5.mkdirSync(lock);
|
|
10787
10821
|
} catch (error) {
|
|
10788
10822
|
if (error.code === "EEXIST")
|
|
10789
10823
|
return null;
|
|
10790
10824
|
throw error;
|
|
10791
10825
|
}
|
|
10792
10826
|
const token = `${process.pid}-${randomBytes(8).toString("hex")}`;
|
|
10793
|
-
|
|
10827
|
+
fs5.writeFileSync(path7.join(lock, LOCK_OWNER), token);
|
|
10794
10828
|
return token;
|
|
10795
10829
|
},
|
|
10796
10830
|
release(lock, token) {
|
|
@@ -10798,35 +10832,35 @@ var init_ledger = __esm(() => {
|
|
|
10798
10832
|
return false;
|
|
10799
10833
|
const tomb = `${lock}.release-${randomBytes(6).toString("hex")}`;
|
|
10800
10834
|
try {
|
|
10801
|
-
|
|
10835
|
+
fs5.renameSync(lock, tomb);
|
|
10802
10836
|
} catch {
|
|
10803
10837
|
return false;
|
|
10804
10838
|
}
|
|
10805
10839
|
if (lockToken(tomb) !== token) {
|
|
10806
10840
|
try {
|
|
10807
|
-
|
|
10841
|
+
fs5.renameSync(tomb, lock);
|
|
10808
10842
|
} catch {}
|
|
10809
10843
|
return false;
|
|
10810
10844
|
}
|
|
10811
|
-
|
|
10845
|
+
fs5.rmSync(tomb, { recursive: true, force: true });
|
|
10812
10846
|
return true;
|
|
10813
10847
|
},
|
|
10814
10848
|
token: lockToken,
|
|
10815
10849
|
reap(lock, observed) {
|
|
10816
10850
|
const tomb = `${lock}.reap-${randomBytes(6).toString("hex")}`;
|
|
10817
10851
|
try {
|
|
10818
|
-
|
|
10852
|
+
fs5.renameSync(lock, tomb);
|
|
10819
10853
|
} catch {
|
|
10820
10854
|
return false;
|
|
10821
10855
|
}
|
|
10822
10856
|
if (lockToken(tomb) === observed) {
|
|
10823
|
-
|
|
10857
|
+
fs5.rmSync(tomb, { recursive: true, force: true });
|
|
10824
10858
|
return true;
|
|
10825
10859
|
}
|
|
10826
10860
|
try {
|
|
10827
|
-
|
|
10861
|
+
fs5.renameSync(tomb, lock);
|
|
10828
10862
|
} catch {
|
|
10829
|
-
|
|
10863
|
+
fs5.rmSync(tomb, { recursive: true, force: true });
|
|
10830
10864
|
}
|
|
10831
10865
|
return false;
|
|
10832
10866
|
}
|
|
@@ -10843,7 +10877,7 @@ var init_ledger = __esm(() => {
|
|
|
10843
10877
|
|
|
10844
10878
|
// packages/workit-core/src/core/policy/derive.ts
|
|
10845
10879
|
import { existsSync as existsSync2 } from "node:fs";
|
|
10846
|
-
import
|
|
10880
|
+
import path8 from "node:path";
|
|
10847
10881
|
function deriveRequirements(judgment, scope, constraints, verification = "self") {
|
|
10848
10882
|
const out = [];
|
|
10849
10883
|
const base = { scope, dependentAction: null };
|
|
@@ -10995,7 +11029,7 @@ var RULES, derived2 = (spec) => ({
|
|
|
10995
11029
|
decisionIds: []
|
|
10996
11030
|
};
|
|
10997
11031
|
}, planRecorded = (task, root) => {
|
|
10998
|
-
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync2(
|
|
11032
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync2(path8.resolve(root, ref.path)));
|
|
10999
11033
|
if (cited)
|
|
11000
11034
|
return {
|
|
11001
11035
|
met: true,
|
|
@@ -11592,7 +11626,7 @@ var init_safe_write = () => {};
|
|
|
11592
11626
|
|
|
11593
11627
|
// packages/workit-core/src/autonomy.ts
|
|
11594
11628
|
import os3 from "node:os";
|
|
11595
|
-
import
|
|
11629
|
+
import path9 from "node:path";
|
|
11596
11630
|
function endpointOf(entry) {
|
|
11597
11631
|
const raw = entry?.defaultEndpoint;
|
|
11598
11632
|
if (raw === undefined)
|
|
@@ -11689,9 +11723,9 @@ var accountHome = () => {
|
|
|
11689
11723
|
} catch {
|
|
11690
11724
|
return os3.homedir();
|
|
11691
11725
|
}
|
|
11692
|
-
}, grantsHome, grantsDir = () =>
|
|
11693
|
-
const configured =
|
|
11694
|
-
return configured ===
|
|
11726
|
+
}, grantsHome, grantsDir = () => path9.join(grantsHome.resolve(), ".config", "workit"), grantsOverride = () => {
|
|
11727
|
+
const configured = path9.resolve(resolveConfigDir());
|
|
11728
|
+
return configured === path9.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 ${path9.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
|
|
11695
11729
|
}, GRANT_KINDS, DEFAULT_GRANTS, DEFAULT_ENDPOINTS, DEFAULT_ENDPOINT = "commit", isDefaultEndpoint = (value) => typeof value === "string" && DEFAULT_ENDPOINTS.includes(value), DEFAULT_VERIFICATION = "self", verificationOf = (entry) => entry?.verification === "independent" ? "independent" : "self", isGrantValue = (value) => value === true || value === false || value === "verified", normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value, fromAutoApprove = (value) => {
|
|
11696
11730
|
if (value === true)
|
|
11697
11731
|
return { push: true, pr: true, merge: "verified" };
|
|
@@ -11782,7 +11816,7 @@ function normalizeMaxBytes(value, options = {}) {
|
|
|
11782
11816
|
}
|
|
11783
11817
|
|
|
11784
11818
|
// node_modules/@openclaw/fs-safe/dist/bounded-read.js
|
|
11785
|
-
import
|
|
11819
|
+
import fs6 from "node:fs";
|
|
11786
11820
|
function createScratchBuffer(maxBytes) {
|
|
11787
11821
|
const initialReadBytes = Number.isFinite(maxBytes) ? Math.min(READ_CHUNK_BYTES, maxBytes + 1) : READ_CHUNK_BYTES;
|
|
11788
11822
|
return Buffer.allocUnsafe(Math.max(1, initialReadBytes));
|
|
@@ -11805,7 +11839,7 @@ function readFileDescriptorBoundedSync(fd, maxBytes) {
|
|
|
11805
11839
|
let total = 0;
|
|
11806
11840
|
while (true) {
|
|
11807
11841
|
const length = nextReadLength(total, maxBytes, scratch.length);
|
|
11808
|
-
const bytesRead =
|
|
11842
|
+
const bytesRead = fs6.readSync(fd, scratch, 0, length, null);
|
|
11809
11843
|
if (bytesRead === 0) {
|
|
11810
11844
|
return Buffer.concat(chunks, total);
|
|
11811
11845
|
}
|
|
@@ -11819,9 +11853,9 @@ var init_bounded_read = __esm(() => {
|
|
|
11819
11853
|
});
|
|
11820
11854
|
|
|
11821
11855
|
// node_modules/@openclaw/fs-safe/dist/read-open-flags.js
|
|
11822
|
-
import
|
|
11856
|
+
import fs7 from "node:fs";
|
|
11823
11857
|
function resolveReadOpenFlags(options) {
|
|
11824
|
-
const constants = options?.constants ??
|
|
11858
|
+
const constants = options?.constants ?? fs7.constants;
|
|
11825
11859
|
const noFollow = process.platform !== "win32" && options?.followSymlinks !== true && typeof constants.O_NOFOLLOW === "number" ? constants.O_NOFOLLOW : 0;
|
|
11826
11860
|
const nonBlocking = process.platform !== "win32" && typeof constants.O_NONBLOCK === "number" ? constants.O_NONBLOCK : 0;
|
|
11827
11861
|
return constants.O_RDONLY | noFollow | nonBlocking;
|
|
@@ -11831,7 +11865,7 @@ var init_read_open_flags = () => {};
|
|
|
11831
11865
|
// node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js
|
|
11832
11866
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
11833
11867
|
import fsSync from "node:fs";
|
|
11834
|
-
import
|
|
11868
|
+
import path10 from "node:path";
|
|
11835
11869
|
function parseSidecarLockSnapshot(snapshot, parser) {
|
|
11836
11870
|
return snapshot && { ...snapshot, payload: parseSidecarLockPayload(snapshot.raw, parser) };
|
|
11837
11871
|
}
|
|
@@ -11859,13 +11893,13 @@ ${ownershipToken}
|
|
|
11859
11893
|
return { raw, ownershipToken };
|
|
11860
11894
|
}
|
|
11861
11895
|
function relativeSidecarLockPath(lockRoot, lockPath) {
|
|
11862
|
-
const resolved =
|
|
11863
|
-
const lexicalRelative =
|
|
11864
|
-
const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${
|
|
11865
|
-
if (!relative || relative === ".." || relative.startsWith(`..${
|
|
11896
|
+
const resolved = path10.resolve(lockPath);
|
|
11897
|
+
const lexicalRelative = path10.relative(lockRoot.rootDir, resolved);
|
|
11898
|
+
const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path10.sep}`) && !path10.isAbsolute(lexicalRelative) ? lexicalRelative : path10.relative(lockRoot.rootReal, resolved);
|
|
11899
|
+
if (!relative || relative === ".." || relative.startsWith(`..${path10.sep}`) || path10.isAbsolute(relative)) {
|
|
11866
11900
|
throw new FsSafeError("outside-workspace", "sidecar lock path is outside lockRoot");
|
|
11867
11901
|
}
|
|
11868
|
-
return relative.split(
|
|
11902
|
+
return relative.split(path10.sep).join(path10.posix.sep);
|
|
11869
11903
|
}
|
|
11870
11904
|
function parseSidecarLockPayload(raw, parser) {
|
|
11871
11905
|
if (parser) {
|
|
@@ -12034,8 +12068,8 @@ function sleepSync(ms) {
|
|
|
12034
12068
|
}
|
|
12035
12069
|
|
|
12036
12070
|
// node_modules/@openclaw/fs-safe/dist/file-lock-sync.js
|
|
12037
|
-
import
|
|
12038
|
-
import
|
|
12071
|
+
import fs8 from "node:fs";
|
|
12072
|
+
import path11 from "node:path";
|
|
12039
12073
|
function getSyncHeldLocks() {
|
|
12040
12074
|
const globalWithState = globalThis;
|
|
12041
12075
|
if (!globalWithState[SYNC_HELD_LOCKS_KEY]) {
|
|
@@ -12052,7 +12086,7 @@ function releaseAllSyncHeldLocks() {
|
|
|
12052
12086
|
}
|
|
12053
12087
|
try {
|
|
12054
12088
|
if (held.fd !== undefined)
|
|
12055
|
-
|
|
12089
|
+
fs8.closeSync(held.fd);
|
|
12056
12090
|
} catch {}
|
|
12057
12091
|
try {
|
|
12058
12092
|
removeSidecarLockIfUnchangedSync(held.lockPath, held.snapshot);
|
|
@@ -12087,7 +12121,7 @@ function releaseSyncHeldLock(held) {
|
|
|
12087
12121
|
if (held.fd !== undefined) {
|
|
12088
12122
|
const fd = held.fd;
|
|
12089
12123
|
held.fd = undefined;
|
|
12090
|
-
|
|
12124
|
+
fs8.closeSync(fd);
|
|
12091
12125
|
}
|
|
12092
12126
|
removeSidecarLockIfUnchangedSync(held.lockPath, held.snapshot);
|
|
12093
12127
|
heldLocks.delete(held.normalizedTargetPath);
|
|
@@ -12110,29 +12144,29 @@ function createSyncHeldLockHandle(held) {
|
|
|
12110
12144
|
};
|
|
12111
12145
|
}
|
|
12112
12146
|
function canonicalLockParentSync(parent) {
|
|
12113
|
-
return process.platform === "win32" ?
|
|
12147
|
+
return process.platform === "win32" ? fs8.realpathSync.native(parent) : fs8.realpathSync(parent);
|
|
12114
12148
|
}
|
|
12115
12149
|
function normalizeTargetPath(targetPath) {
|
|
12116
|
-
const resolved =
|
|
12117
|
-
|
|
12150
|
+
const resolved = path11.resolve(targetPath);
|
|
12151
|
+
fs8.mkdirSync(path11.dirname(resolved), { recursive: true });
|
|
12118
12152
|
try {
|
|
12119
|
-
return
|
|
12153
|
+
return path11.join(canonicalLockParentSync(path11.dirname(resolved)), path11.basename(resolved));
|
|
12120
12154
|
} catch {
|
|
12121
12155
|
return resolved;
|
|
12122
12156
|
}
|
|
12123
12157
|
}
|
|
12124
12158
|
function boundedLockPath(lockPath, lockRoot) {
|
|
12125
|
-
const resolved =
|
|
12159
|
+
const resolved = path11.resolve(lockPath);
|
|
12126
12160
|
if (!lockRoot)
|
|
12127
12161
|
return resolved;
|
|
12128
12162
|
relativeSidecarLockPath(lockRoot, resolved);
|
|
12129
|
-
const parent =
|
|
12163
|
+
const parent = path11.dirname(resolved);
|
|
12130
12164
|
const parentReal = canonicalLockParentSync(parent);
|
|
12131
|
-
const parentRelative =
|
|
12132
|
-
if (parentRelative === ".." || parentRelative.startsWith(`..${
|
|
12165
|
+
const parentRelative = path11.relative(lockRoot.rootReal, parentReal);
|
|
12166
|
+
if (parentRelative === ".." || parentRelative.startsWith(`..${path11.sep}`) || path11.isAbsolute(parentRelative)) {
|
|
12133
12167
|
throw new FsSafeError("outside-workspace", "sidecar lock parent is outside lockRoot");
|
|
12134
12168
|
}
|
|
12135
|
-
return
|
|
12169
|
+
return path11.join(parentReal, path11.basename(resolved));
|
|
12136
12170
|
}
|
|
12137
12171
|
function defaultShouldReclaim(snapshot, staleMs, nowMs) {
|
|
12138
12172
|
const createdAtMs = sidecarLockPayloadCreatedAtMs(snapshot.payload);
|
|
@@ -12142,7 +12176,7 @@ function defaultShouldReclaim(snapshot, staleMs, nowMs) {
|
|
|
12142
12176
|
}
|
|
12143
12177
|
function reclaimGuardExists(reclaimGuardPath) {
|
|
12144
12178
|
try {
|
|
12145
|
-
|
|
12179
|
+
fs8.lstatSync(reclaimGuardPath);
|
|
12146
12180
|
return true;
|
|
12147
12181
|
} catch (error) {
|
|
12148
12182
|
if (error.code === "ENOENT")
|
|
@@ -12206,19 +12240,19 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
12206
12240
|
const { raw, ownershipToken } = serializeSidecarLockPayload(payload);
|
|
12207
12241
|
let lockFileCreateDenied = false;
|
|
12208
12242
|
try {
|
|
12209
|
-
const noFollow = process.platform !== "win32" && typeof
|
|
12243
|
+
const noFollow = process.platform !== "win32" && typeof fs8.constants.O_NOFOLLOW === "number" ? fs8.constants.O_NOFOLLOW : 0;
|
|
12210
12244
|
try {
|
|
12211
|
-
fd =
|
|
12245
|
+
fd = fs8.openSync(lockPath, fs8.constants.O_WRONLY | fs8.constants.O_CREAT | fs8.constants.O_EXCL | noFollow, 384);
|
|
12212
12246
|
} catch (error) {
|
|
12213
12247
|
lockFileCreateDenied = isTransientLockFileDenial(error, lockPath);
|
|
12214
12248
|
throw error;
|
|
12215
12249
|
}
|
|
12216
|
-
|
|
12217
|
-
|
|
12250
|
+
fs8.writeFileSync(fd, raw, "utf8");
|
|
12251
|
+
fs8.fsyncSync(fd);
|
|
12218
12252
|
const snapshot = {
|
|
12219
12253
|
raw,
|
|
12220
12254
|
payload,
|
|
12221
|
-
stat:
|
|
12255
|
+
stat: fs8.fstatSync(fd),
|
|
12222
12256
|
ownershipToken
|
|
12223
12257
|
};
|
|
12224
12258
|
const createdHeld = {
|
|
@@ -12253,8 +12287,8 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
12253
12287
|
return returnedHandle;
|
|
12254
12288
|
} catch (error) {
|
|
12255
12289
|
if (fd !== undefined) {
|
|
12256
|
-
const failed = { payload: null, stat:
|
|
12257
|
-
|
|
12290
|
+
const failed = { payload: null, stat: fs8.fstatSync(fd) };
|
|
12291
|
+
fs8.closeSync(fd);
|
|
12258
12292
|
fd = undefined;
|
|
12259
12293
|
removeSidecarLockIfUnchangedSync(lockPath, failed);
|
|
12260
12294
|
}
|
|
@@ -12303,7 +12337,7 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
12303
12337
|
})) {
|
|
12304
12338
|
let ownsReclaimGuard = false;
|
|
12305
12339
|
try {
|
|
12306
|
-
|
|
12340
|
+
fs8.mkdirSync(reclaimGuardPath);
|
|
12307
12341
|
ownsReclaimGuard = true;
|
|
12308
12342
|
if (removeSidecarLockIfUnchangedSync(lockPath, snapshot))
|
|
12309
12343
|
continue;
|
|
@@ -12316,7 +12350,7 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
12316
12350
|
} finally {
|
|
12317
12351
|
if (ownsReclaimGuard) {
|
|
12318
12352
|
try {
|
|
12319
|
-
|
|
12353
|
+
fs8.rmdirSync(reclaimGuardPath);
|
|
12320
12354
|
} catch {}
|
|
12321
12355
|
}
|
|
12322
12356
|
}
|
|
@@ -12349,13 +12383,13 @@ var init_file_lock = __esm(() => {
|
|
|
12349
12383
|
|
|
12350
12384
|
// packages/workit-core/src/core/package-root.ts
|
|
12351
12385
|
import { existsSync as existsSync3 } from "node:fs";
|
|
12352
|
-
import
|
|
12386
|
+
import path12 from "node:path";
|
|
12353
12387
|
import { fileURLToPath } from "node:url";
|
|
12354
12388
|
var packageRoot = () => {
|
|
12355
|
-
let dir =
|
|
12389
|
+
let dir = path12.dirname(fileURLToPath(import.meta.url));
|
|
12356
12390
|
while (true) {
|
|
12357
|
-
const parent =
|
|
12358
|
-
if (existsSync3(
|
|
12391
|
+
const parent = path12.dirname(dir);
|
|
12392
|
+
if (existsSync3(path12.join(dir, "package.json")) || parent === dir)
|
|
12359
12393
|
return dir;
|
|
12360
12394
|
dir = parent;
|
|
12361
12395
|
}
|
|
@@ -12364,7 +12398,7 @@ var init_package_root = () => {};
|
|
|
12364
12398
|
|
|
12365
12399
|
// packages/workit-core/src/core/store-lock.ts
|
|
12366
12400
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
12367
|
-
import
|
|
12401
|
+
import fs9 from "node:fs";
|
|
12368
12402
|
import { hostname } from "node:os";
|
|
12369
12403
|
var metadataLockSchema, FOREIGN_LOCK_TTL_MS, UNREADABLE_LOCK_TTL_MS = 30000, RECLAIM_GUARD_TTL_MS = 30000, defaultLockTimeoutMs = 250, defaultLockTimeout = () => defaultLockTimeoutMs, parseMetadataLock = (raw) => {
|
|
12370
12404
|
let value;
|
|
@@ -12391,7 +12425,7 @@ var metadataLockSchema, FOREIGN_LOCK_TTL_MS, UNREADABLE_LOCK_TTL_MS = 30000, REC
|
|
|
12391
12425
|
}, processStartOf = (pid) => {
|
|
12392
12426
|
if (process.platform === "linux") {
|
|
12393
12427
|
try {
|
|
12394
|
-
return parseProcStatStart(
|
|
12428
|
+
return parseProcStatStart(fs9.readFileSync(`/proc/${pid}/stat`, "utf8"));
|
|
12395
12429
|
} catch {
|
|
12396
12430
|
return null;
|
|
12397
12431
|
}
|
|
@@ -12411,7 +12445,7 @@ var metadataLockSchema, FOREIGN_LOCK_TTL_MS, UNREADABLE_LOCK_TTL_MS = 30000, REC
|
|
|
12411
12445
|
return null;
|
|
12412
12446
|
}, readTrimmed = (file) => {
|
|
12413
12447
|
try {
|
|
12414
|
-
return
|
|
12448
|
+
return fs9.readFileSync(file, "utf8").trim() || null;
|
|
12415
12449
|
} catch {
|
|
12416
12450
|
return null;
|
|
12417
12451
|
}
|
|
@@ -12420,7 +12454,7 @@ var metadataLockSchema, FOREIGN_LOCK_TTL_MS, UNREADABLE_LOCK_TTL_MS = 30000, REC
|
|
|
12420
12454
|
return cachedLockHost;
|
|
12421
12455
|
let pidns = null;
|
|
12422
12456
|
try {
|
|
12423
|
-
pidns = /\[(\d+)\]/.exec(
|
|
12457
|
+
pidns = /\[(\d+)\]/.exec(fs9.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
|
|
12424
12458
|
} catch {}
|
|
12425
12459
|
const boot = readTrimmed("/proc/sys/kernel/random/boot_id");
|
|
12426
12460
|
cachedLockHost = pidns || boot ? `${hostname()}#${pidns ?? "?"}:${boot ?? "?"}` : hostname();
|
|
@@ -12462,7 +12496,7 @@ var metadataLockSchema, FOREIGN_LOCK_TTL_MS, UNREADABLE_LOCK_TTL_MS = 30000, REC
|
|
|
12462
12496
|
return { state: "live", reason: `held by running pid ${pid}` };
|
|
12463
12497
|
}, ageOf = (file, nowMs) => {
|
|
12464
12498
|
try {
|
|
12465
|
-
return nowMs -
|
|
12499
|
+
return nowMs - fs9.lstatSync(file).mtimeMs;
|
|
12466
12500
|
} catch {
|
|
12467
12501
|
return null;
|
|
12468
12502
|
}
|
|
@@ -12472,7 +12506,7 @@ var metadataLockSchema, FOREIGN_LOCK_TTL_MS, UNREADABLE_LOCK_TTL_MS = 30000, REC
|
|
|
12472
12506
|
if (age === null || age <= RECLAIM_GUARD_TTL_MS)
|
|
12473
12507
|
return false;
|
|
12474
12508
|
try {
|
|
12475
|
-
|
|
12509
|
+
fs9.rmdirSync(guard);
|
|
12476
12510
|
return true;
|
|
12477
12511
|
} catch {
|
|
12478
12512
|
return false;
|
|
@@ -12495,12 +12529,12 @@ var init_store_lock = __esm(() => {
|
|
|
12495
12529
|
|
|
12496
12530
|
// packages/workit-core/src/store/event-log.ts
|
|
12497
12531
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
12498
|
-
import
|
|
12499
|
-
import
|
|
12532
|
+
import fs10 from "node:fs";
|
|
12533
|
+
import path13 from "node:path";
|
|
12500
12534
|
function readLog(file, from = 0, expectSeq = null) {
|
|
12501
12535
|
let fd;
|
|
12502
12536
|
try {
|
|
12503
|
-
fd =
|
|
12537
|
+
fd = fs10.openSync(file, "r");
|
|
12504
12538
|
} catch (error) {
|
|
12505
12539
|
if (error.code === "ENOENT")
|
|
12506
12540
|
return null;
|
|
@@ -12509,13 +12543,13 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
12509
12543
|
let bytes;
|
|
12510
12544
|
let size;
|
|
12511
12545
|
try {
|
|
12512
|
-
size =
|
|
12546
|
+
size = fs10.fstatSync(fd).size;
|
|
12513
12547
|
if (from > size)
|
|
12514
12548
|
throw new LogDamage(`${file}: shorter than its snapshot`);
|
|
12515
12549
|
bytes = Buffer.alloc(size - from);
|
|
12516
12550
|
let read = 0;
|
|
12517
12551
|
while (read < bytes.length) {
|
|
12518
|
-
const got =
|
|
12552
|
+
const got = fs10.readSync(fd, bytes, read, bytes.length - read, from + read);
|
|
12519
12553
|
if (got === 0)
|
|
12520
12554
|
break;
|
|
12521
12555
|
read += got;
|
|
@@ -12523,7 +12557,7 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
12523
12557
|
bytes = bytes.subarray(0, read);
|
|
12524
12558
|
size = from + read;
|
|
12525
12559
|
} finally {
|
|
12526
|
-
|
|
12560
|
+
fs10.closeSync(fd);
|
|
12527
12561
|
}
|
|
12528
12562
|
const events = [];
|
|
12529
12563
|
let start = 0;
|
|
@@ -12549,46 +12583,46 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
12549
12583
|
function appendEvent(file, event, validEnd) {
|
|
12550
12584
|
const line = Buffer.from(`${JSON.stringify(event)}
|
|
12551
12585
|
`, "utf8");
|
|
12552
|
-
const created = !
|
|
12553
|
-
if (!created &&
|
|
12554
|
-
const cut =
|
|
12586
|
+
const created = !fs10.existsSync(file);
|
|
12587
|
+
if (!created && fs10.statSync(file).size !== validEnd) {
|
|
12588
|
+
const cut = fs10.openSync(file, "r+");
|
|
12555
12589
|
try {
|
|
12556
|
-
|
|
12557
|
-
|
|
12590
|
+
fs10.ftruncateSync(cut, validEnd);
|
|
12591
|
+
fs10.fsyncSync(cut);
|
|
12558
12592
|
} finally {
|
|
12559
|
-
|
|
12593
|
+
fs10.closeSync(cut);
|
|
12560
12594
|
}
|
|
12561
12595
|
}
|
|
12562
|
-
const fd =
|
|
12596
|
+
const fd = fs10.openSync(file, "a", 384);
|
|
12563
12597
|
try {
|
|
12564
12598
|
let written = 0;
|
|
12565
12599
|
while (written < line.length)
|
|
12566
|
-
written +=
|
|
12567
|
-
|
|
12600
|
+
written += fs10.writeSync(fd, line, written, line.length - written);
|
|
12601
|
+
fs10.fsyncSync(fd);
|
|
12568
12602
|
} finally {
|
|
12569
|
-
|
|
12603
|
+
fs10.closeSync(fd);
|
|
12570
12604
|
}
|
|
12571
12605
|
if (created)
|
|
12572
|
-
fsyncDirectory(
|
|
12606
|
+
fsyncDirectory(path13.dirname(file));
|
|
12573
12607
|
return validEnd;
|
|
12574
12608
|
}
|
|
12575
12609
|
function replaceFile(file, bytes, durable) {
|
|
12576
12610
|
const temporary = `${file}.${process.pid}.${randomUUID2()}.tmp`;
|
|
12577
12611
|
try {
|
|
12578
|
-
const fd =
|
|
12612
|
+
const fd = fs10.openSync(temporary, "wx", 384);
|
|
12579
12613
|
try {
|
|
12580
|
-
|
|
12614
|
+
fs10.writeSync(fd, bytes);
|
|
12581
12615
|
if (durable)
|
|
12582
|
-
|
|
12616
|
+
fs10.fsyncSync(fd);
|
|
12583
12617
|
} finally {
|
|
12584
|
-
|
|
12618
|
+
fs10.closeSync(fd);
|
|
12585
12619
|
}
|
|
12586
12620
|
renameWithRetry(temporary, file);
|
|
12587
12621
|
if (durable)
|
|
12588
|
-
fsyncDirectory(
|
|
12622
|
+
fsyncDirectory(path13.dirname(file));
|
|
12589
12623
|
} catch (error) {
|
|
12590
12624
|
try {
|
|
12591
|
-
|
|
12625
|
+
fs10.unlinkSync(temporary);
|
|
12592
12626
|
} catch {}
|
|
12593
12627
|
throw error;
|
|
12594
12628
|
}
|
|
@@ -12609,11 +12643,11 @@ var EVENT_VERSION = 1, LogDamage, isObject4 = (value) => typeof value === "objec
|
|
|
12609
12643
|
return value;
|
|
12610
12644
|
}, fsyncDirectory = (dir) => {
|
|
12611
12645
|
try {
|
|
12612
|
-
const fd =
|
|
12646
|
+
const fd = fs10.openSync(dir, "r");
|
|
12613
12647
|
try {
|
|
12614
|
-
|
|
12648
|
+
fs10.fsyncSync(fd);
|
|
12615
12649
|
} finally {
|
|
12616
|
-
|
|
12650
|
+
fs10.closeSync(fd);
|
|
12617
12651
|
}
|
|
12618
12652
|
} catch (error) {
|
|
12619
12653
|
if (process.platform !== "win32")
|
|
@@ -12636,7 +12670,7 @@ var EVENT_VERSION = 1, LogDamage, isObject4 = (value) => typeof value === "objec
|
|
|
12636
12670
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 5 * (attempt + 1));
|
|
12637
12671
|
}
|
|
12638
12672
|
}
|
|
12639
|
-
}, renameWithRetry = (from, to) => retryTransient(() =>
|
|
12673
|
+
}, renameWithRetry = (from, to) => retryTransient(() => fs10.renameSync(from, to));
|
|
12640
12674
|
var init_event_log = __esm(() => {
|
|
12641
12675
|
LogDamage = class LogDamage extends Error {
|
|
12642
12676
|
upgrade;
|
|
@@ -12755,7 +12789,7 @@ var isPlainObject2 = (value) => typeof value === "object" && value !== null && !
|
|
|
12755
12789
|
});
|
|
12756
12790
|
|
|
12757
12791
|
// packages/workit-core/src/store/reduce.ts
|
|
12758
|
-
import { createHash as
|
|
12792
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
12759
12793
|
function reduce(state, event, blobs) {
|
|
12760
12794
|
const data = event.data;
|
|
12761
12795
|
const known = KNOWN_TYPES.has(event.type);
|
|
@@ -12822,7 +12856,7 @@ function eventType(before, after) {
|
|
|
12822
12856
|
return "task.noted";
|
|
12823
12857
|
return "task.updated";
|
|
12824
12858
|
}
|
|
12825
|
-
var OPENING_TYPES, KNOWN_TYPES, isObject5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), isKey = (value) => isObject5(value) && typeof value.key === "string" && (value.kind === "branch" || value.kind === "detached" || value.kind === "dir") && (value.branch === null || typeof value.branch === "string"), BLOB_REF = "$blob", blobRef = (value) => isObject5(value) && Object.keys(value).length === 1 && typeof value[BLOB_REF] === "string" && /^[0-9a-f]{64}$/.test(value[BLOB_REF]) ? value[BLOB_REF] : null, blobDigest = (value) =>
|
|
12859
|
+
var OPENING_TYPES, KNOWN_TYPES, isObject5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), isKey = (value) => isObject5(value) && typeof value.key === "string" && (value.kind === "branch" || value.kind === "detached" || value.kind === "dir") && (value.branch === null || typeof value.branch === "string"), BLOB_REF = "$blob", blobRef = (value) => isObject5(value) && Object.keys(value).length === 1 && typeof value[BLOB_REF] === "string" && /^[0-9a-f]{64}$/.test(value[BLOB_REF]) ? value[BLOB_REF] : null, blobDigest = (value) => createHash8("sha256").update(JSON.stringify(value)).digest("hex"), toBlob = (value, blobs) => {
|
|
12826
12860
|
if (!isObject5(value))
|
|
12827
12861
|
return value;
|
|
12828
12862
|
const digest = blobDigest(value);
|
|
@@ -12893,9 +12927,9 @@ var init_notes = __esm(() => {
|
|
|
12893
12927
|
});
|
|
12894
12928
|
|
|
12895
12929
|
// packages/workit-core/src/core/task-store.ts
|
|
12896
|
-
import { createHash as
|
|
12897
|
-
import * as
|
|
12898
|
-
import
|
|
12930
|
+
import { createHash as createHash9, randomUUID as randomUUID3 } from "node:crypto";
|
|
12931
|
+
import * as fs11 from "node:fs";
|
|
12932
|
+
import path14 from "node:path";
|
|
12899
12933
|
|
|
12900
12934
|
class TaskStore {
|
|
12901
12935
|
root;
|
|
@@ -12907,8 +12941,8 @@ class TaskStore {
|
|
|
12907
12941
|
migrationChecked = false;
|
|
12908
12942
|
compactDue = new Set;
|
|
12909
12943
|
constructor(root, options = {}) {
|
|
12910
|
-
const resolved =
|
|
12911
|
-
this.origin =
|
|
12944
|
+
const resolved = path14.resolve(root);
|
|
12945
|
+
this.origin = fs11.existsSync(resolved) ? fs11.realpathSync(resolved) : resolved;
|
|
12912
12946
|
this.located = resolveStore(this.origin);
|
|
12913
12947
|
this.root = checkoutRootOf(this.origin, this.located);
|
|
12914
12948
|
this.lockTimeoutMs = options.lockTimeoutMs ?? defaultLockTimeout();
|
|
@@ -12916,7 +12950,7 @@ class TaskStore {
|
|
|
12916
12950
|
this.migrateOnRead = options.migrateOnRead;
|
|
12917
12951
|
}
|
|
12918
12952
|
location() {
|
|
12919
|
-
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared &&
|
|
12953
|
+
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs11.existsSync(path14.join(this.root, ".git"))) {
|
|
12920
12954
|
this.located = undefined;
|
|
12921
12955
|
this.migrationChecked = false;
|
|
12922
12956
|
}
|
|
@@ -12980,7 +13014,7 @@ class TaskStore {
|
|
|
12980
13014
|
const tasksDir = this.paths().tasks;
|
|
12981
13015
|
let names;
|
|
12982
13016
|
try {
|
|
12983
|
-
names =
|
|
13017
|
+
names = fs11.existsSync(tasksDir) ? fs11.readdirSync(tasksDir).filter((name) => validId(name)) : [];
|
|
12984
13018
|
} catch (error) {
|
|
12985
13019
|
return failure2("storage_error", `unable to list tasks: ${String(error)}`, {
|
|
12986
13020
|
path: tasksDir
|
|
@@ -13021,7 +13055,7 @@ class TaskStore {
|
|
|
13021
13055
|
const file = this.paths().workspace;
|
|
13022
13056
|
let bytes;
|
|
13023
13057
|
try {
|
|
13024
|
-
bytes = retryTransient(() =>
|
|
13058
|
+
bytes = retryTransient(() => fs11.readFileSync(file, "utf8"));
|
|
13025
13059
|
} catch (error) {
|
|
13026
13060
|
if (error.code === "ENOENT")
|
|
13027
13061
|
return success(null, null, null);
|
|
@@ -13072,7 +13106,7 @@ class TaskStore {
|
|
|
13072
13106
|
const moved = this.bindLocked(again.data.id, key, input.now);
|
|
13073
13107
|
return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
|
|
13074
13108
|
}
|
|
13075
|
-
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" :
|
|
13109
|
+
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path14.basename(this.root) || this.root;
|
|
13076
13110
|
const intent = {
|
|
13077
13111
|
objective: input.objective?.trim() || `Work on ${label}`,
|
|
13078
13112
|
scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
|
|
@@ -13109,6 +13143,66 @@ class TaskStore {
|
|
|
13109
13143
|
return bound;
|
|
13110
13144
|
return success(null, null, { key: key.data, bound: bound.data });
|
|
13111
13145
|
}
|
|
13146
|
+
taskForWorker(workerId) {
|
|
13147
|
+
if (typeof workerId !== "string" || !validId(workerId))
|
|
13148
|
+
return failure2("invalid_input", "needs a worker id: `workit worker cancel|report <workerId>`");
|
|
13149
|
+
const listed = this.listTaskIndex();
|
|
13150
|
+
if (!listed.ok)
|
|
13151
|
+
return listed;
|
|
13152
|
+
for (const entry of listed.data) {
|
|
13153
|
+
if (entry.status === "closed")
|
|
13154
|
+
continue;
|
|
13155
|
+
const loaded = this.load(entry.id);
|
|
13156
|
+
if (!loaded.ok)
|
|
13157
|
+
return loaded;
|
|
13158
|
+
if (loaded.data?.record.workers.some((worker) => worker.id === workerId))
|
|
13159
|
+
return success(null, null, entry.id);
|
|
13160
|
+
}
|
|
13161
|
+
return success(null, null, null);
|
|
13162
|
+
}
|
|
13163
|
+
originTaskEntry() {
|
|
13164
|
+
const key = this.currentKey();
|
|
13165
|
+
const location = this.location();
|
|
13166
|
+
const branch = key.ok ? key.data.branch : null;
|
|
13167
|
+
if (!branch || !location.ok)
|
|
13168
|
+
return success(null, null, null);
|
|
13169
|
+
const common = path14.dirname(location.data.dir);
|
|
13170
|
+
const heads = path14.join(common, "logs", "refs", "heads");
|
|
13171
|
+
const headLog = location.data.git ? path14.join(location.data.git.gitDir, "logs", "HEAD") : "";
|
|
13172
|
+
const signatures = [
|
|
13173
|
+
path14.join(heads, branch),
|
|
13174
|
+
headLog,
|
|
13175
|
+
heads,
|
|
13176
|
+
path14.join(common, "packed-refs")
|
|
13177
|
+
].map((file) => fileSignature(file) ?? "-");
|
|
13178
|
+
const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
|
|
13179
|
+
let resolved = origins.get(slot);
|
|
13180
|
+
if (resolved?.signature !== signatures.join("|")) {
|
|
13181
|
+
const renames = branchRenames(location.data);
|
|
13182
|
+
const aliases = [branch];
|
|
13183
|
+
for (const rename of renames.toReversed())
|
|
13184
|
+
if (rename.to === aliases.at(-1))
|
|
13185
|
+
aliases.push(rename.from);
|
|
13186
|
+
const start = branchStart(location.data, branch, aliases);
|
|
13187
|
+
const slash = start?.indexOf("/") ?? -1;
|
|
13188
|
+
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path14.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
|
|
13189
|
+
const name = start && (remote ? start.slice(slash + 1) : start);
|
|
13190
|
+
resolved = {
|
|
13191
|
+
signature: signatures.join("|"),
|
|
13192
|
+
name: name && !aliases.includes(name) ? name : null,
|
|
13193
|
+
renames
|
|
13194
|
+
};
|
|
13195
|
+
origins.set(slot, resolved);
|
|
13196
|
+
}
|
|
13197
|
+
const entry = resolved.name ? this.openTasksForKey(resolved.name, resolved.renames) : null;
|
|
13198
|
+
if (entry && !entry.ok)
|
|
13199
|
+
return entry;
|
|
13200
|
+
return success(null, null, entry?.data[0] ? { origin: `branch ${resolved.name}`, entry: entry.data[0] } : null);
|
|
13201
|
+
}
|
|
13202
|
+
readStoreTask(taskId) {
|
|
13203
|
+
const loaded = this.load(taskId);
|
|
13204
|
+
return loaded.ok ? success(null, null, loaded.data?.record ?? null) : loaded;
|
|
13205
|
+
}
|
|
13112
13206
|
create(input, provenance, expectedWorkspaceRevision) {
|
|
13113
13207
|
const value = "intent" in input ? input : {
|
|
13114
13208
|
intent: input,
|
|
@@ -13329,13 +13423,13 @@ class TaskStore {
|
|
|
13329
13423
|
if (!ready.ok)
|
|
13330
13424
|
return ready;
|
|
13331
13425
|
const dirs = this.paths();
|
|
13332
|
-
const recovery =
|
|
13333
|
-
if (
|
|
13426
|
+
const recovery = path14.join(this.root, ".workit", "recovery");
|
|
13427
|
+
if (fs11.existsSync(recovery)) {
|
|
13334
13428
|
let files = 0;
|
|
13335
13429
|
let bytes = 0;
|
|
13336
13430
|
try {
|
|
13337
|
-
for (const name of
|
|
13338
|
-
const stat =
|
|
13431
|
+
for (const name of fs11.readdirSync(recovery)) {
|
|
13432
|
+
const stat = fs11.lstatSync(path14.join(recovery, name));
|
|
13339
13433
|
if (!stat.isFile())
|
|
13340
13434
|
continue;
|
|
13341
13435
|
files += 1;
|
|
@@ -13344,17 +13438,17 @@ class TaskStore {
|
|
|
13344
13438
|
} catch {}
|
|
13345
13439
|
const remove = options.pruneRecovery === true && !dryRun;
|
|
13346
13440
|
if (remove)
|
|
13347
|
-
|
|
13441
|
+
fs11.rmSync(recovery, { recursive: true, force: true });
|
|
13348
13442
|
report.legacyRecovery = { path: recovery, files, bytes, removed: remove };
|
|
13349
13443
|
}
|
|
13350
|
-
if (!
|
|
13444
|
+
if (!fs11.existsSync(dirs.tasks))
|
|
13351
13445
|
return success(null, null, report);
|
|
13352
|
-
const names =
|
|
13446
|
+
const names = fs11.readdirSync(dirs.tasks).filter((name) => validId(name));
|
|
13353
13447
|
for (const name of names) {
|
|
13354
13448
|
const file = this.eventsPath(name);
|
|
13355
13449
|
let size = 0;
|
|
13356
13450
|
try {
|
|
13357
|
-
size =
|
|
13451
|
+
size = fs11.statSync(file).size;
|
|
13358
13452
|
} catch {
|
|
13359
13453
|
continue;
|
|
13360
13454
|
}
|
|
@@ -13382,16 +13476,16 @@ class TaskStore {
|
|
|
13382
13476
|
const references = new Set;
|
|
13383
13477
|
for (const name of names)
|
|
13384
13478
|
try {
|
|
13385
|
-
for (const match of
|
|
13479
|
+
for (const match of fs11.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
|
|
13386
13480
|
references.add(match[1]);
|
|
13387
13481
|
} catch {}
|
|
13388
|
-
if (
|
|
13389
|
-
for (const name of
|
|
13482
|
+
if (fs11.existsSync(dirs.blobs))
|
|
13483
|
+
for (const name of fs11.readdirSync(dirs.blobs)) {
|
|
13390
13484
|
const digest = name.replace(/\.json$/, "");
|
|
13391
|
-
const file =
|
|
13485
|
+
const file = path14.join(dirs.blobs, name);
|
|
13392
13486
|
let stat;
|
|
13393
13487
|
try {
|
|
13394
|
-
stat =
|
|
13488
|
+
stat = fs11.lstatSync(file);
|
|
13395
13489
|
} catch {
|
|
13396
13490
|
continue;
|
|
13397
13491
|
}
|
|
@@ -13402,11 +13496,11 @@ class TaskStore {
|
|
|
13402
13496
|
report.blobs.removed += 1;
|
|
13403
13497
|
report.blobs.removedBytes += stat.size;
|
|
13404
13498
|
if (!dryRun)
|
|
13405
|
-
|
|
13499
|
+
fs11.rmSync(file, { force: true });
|
|
13406
13500
|
}
|
|
13407
13501
|
return success(null, null, null);
|
|
13408
13502
|
};
|
|
13409
|
-
const swept = dryRun ? sweep() : this.withLock(
|
|
13503
|
+
const swept = dryRun ? sweep() : this.withLock(path14.join(dirs.dir, "gc.lock"), sweep);
|
|
13410
13504
|
if (!swept.ok)
|
|
13411
13505
|
return swept;
|
|
13412
13506
|
const temporaryDirs = [
|
|
@@ -13414,17 +13508,17 @@ class TaskStore {
|
|
|
13414
13508
|
dirs.tasks,
|
|
13415
13509
|
dirs.blobs,
|
|
13416
13510
|
dirs.checkout,
|
|
13417
|
-
...names.map((name) =>
|
|
13511
|
+
...names.map((name) => path14.join(dirs.tasks, name))
|
|
13418
13512
|
];
|
|
13419
13513
|
for (const directory of temporaryDirs) {
|
|
13420
|
-
if (!
|
|
13514
|
+
if (!fs11.existsSync(directory))
|
|
13421
13515
|
continue;
|
|
13422
|
-
for (const name of
|
|
13516
|
+
for (const name of fs11.readdirSync(directory)) {
|
|
13423
13517
|
if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
|
|
13424
13518
|
continue;
|
|
13425
|
-
const file =
|
|
13519
|
+
const file = path14.join(directory, name);
|
|
13426
13520
|
try {
|
|
13427
|
-
const stat =
|
|
13521
|
+
const stat = fs11.lstatSync(file);
|
|
13428
13522
|
if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
|
|
13429
13523
|
continue;
|
|
13430
13524
|
} catch {
|
|
@@ -13432,37 +13526,37 @@ class TaskStore {
|
|
|
13432
13526
|
}
|
|
13433
13527
|
report.temporary.removed += 1;
|
|
13434
13528
|
if (!dryRun)
|
|
13435
|
-
|
|
13529
|
+
fs11.rmSync(file, { force: true });
|
|
13436
13530
|
}
|
|
13437
13531
|
}
|
|
13438
13532
|
return success(null, null, report);
|
|
13439
13533
|
}
|
|
13440
13534
|
paths() {
|
|
13441
13535
|
const location = this.location();
|
|
13442
|
-
const dir = location.ok ? location.data.dir :
|
|
13443
|
-
const checkout =
|
|
13536
|
+
const dir = location.ok ? location.data.dir : path14.join(this.root, ".workit");
|
|
13537
|
+
const checkout = path14.join(dir, "checkouts", checkoutSlug(this.root));
|
|
13444
13538
|
return {
|
|
13445
13539
|
dir,
|
|
13446
13540
|
shared: location.ok ? location.data.shared : false,
|
|
13447
|
-
marker:
|
|
13541
|
+
marker: path14.join(dir, "store.json"),
|
|
13448
13542
|
checkout,
|
|
13449
|
-
workspace:
|
|
13450
|
-
checkoutLock:
|
|
13451
|
-
tasks:
|
|
13452
|
-
index:
|
|
13453
|
-
blobs:
|
|
13454
|
-
legacy:
|
|
13455
|
-
v2:
|
|
13543
|
+
workspace: path14.join(checkout, "workspace.json"),
|
|
13544
|
+
checkoutLock: path14.join(checkout, "metadata.lock"),
|
|
13545
|
+
tasks: path14.join(dir, "tasks"),
|
|
13546
|
+
index: path14.join(dir, "task-index.json"),
|
|
13547
|
+
blobs: path14.join(dir, "blobs", "candidates"),
|
|
13548
|
+
legacy: path14.join(dir, "legacy", checkoutSlug(this.root), "v2"),
|
|
13549
|
+
v2: path14.join(this.root, ".workit")
|
|
13456
13550
|
};
|
|
13457
13551
|
}
|
|
13458
13552
|
eventsPath(taskId) {
|
|
13459
|
-
return
|
|
13553
|
+
return path14.join(this.paths().tasks, taskId, "events.jsonl");
|
|
13460
13554
|
}
|
|
13461
13555
|
snapshotPath(taskId) {
|
|
13462
|
-
return
|
|
13556
|
+
return path14.join(this.paths().tasks, taskId, "snapshot.json");
|
|
13463
13557
|
}
|
|
13464
13558
|
taskLockPath(taskId) {
|
|
13465
|
-
return
|
|
13559
|
+
return path14.join(this.paths().tasks, taskId, "lock");
|
|
13466
13560
|
}
|
|
13467
13561
|
ready(write = false) {
|
|
13468
13562
|
const location = this.location();
|
|
@@ -13472,7 +13566,7 @@ class TaskStore {
|
|
|
13472
13566
|
return success(null, null, null);
|
|
13473
13567
|
const dirs = this.paths();
|
|
13474
13568
|
try {
|
|
13475
|
-
const marker = JSON.parse(
|
|
13569
|
+
const marker = JSON.parse(fs11.readFileSync(dirs.marker, "utf8"));
|
|
13476
13570
|
if (typeof marker.version === "number" && marker.version > STORE_FORMAT)
|
|
13477
13571
|
return failure2("recovery_required", `the workit store at ${dirs.dir} has format ${marker.version}; upgrade Workit to use it`, { path: dirs.marker });
|
|
13478
13572
|
} catch {}
|
|
@@ -13501,21 +13595,21 @@ class TaskStore {
|
|
|
13501
13595
|
return {
|
|
13502
13596
|
read: (digest) => {
|
|
13503
13597
|
try {
|
|
13504
|
-
return JSON.parse(
|
|
13598
|
+
return JSON.parse(fs11.readFileSync(path14.join(dir, `${digest}.json`), "utf8"));
|
|
13505
13599
|
} catch (error) {
|
|
13506
13600
|
throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
|
|
13507
13601
|
}
|
|
13508
13602
|
},
|
|
13509
13603
|
write: (digest, value) => {
|
|
13510
|
-
const file =
|
|
13511
|
-
if (
|
|
13604
|
+
const file = path14.join(dir, `${digest}.json`);
|
|
13605
|
+
if (fs11.existsSync(file)) {
|
|
13512
13606
|
const stamp = new Date;
|
|
13513
13607
|
try {
|
|
13514
|
-
|
|
13608
|
+
fs11.utimesSync(file, stamp, stamp);
|
|
13515
13609
|
return;
|
|
13516
13610
|
} catch {}
|
|
13517
13611
|
}
|
|
13518
|
-
|
|
13612
|
+
fs11.mkdirSync(dir, { recursive: true });
|
|
13519
13613
|
replaceFile(file, JSON.stringify(value), true);
|
|
13520
13614
|
}
|
|
13521
13615
|
};
|
|
@@ -13597,7 +13691,7 @@ class TaskStore {
|
|
|
13597
13691
|
}
|
|
13598
13692
|
readSnapshot(taskId) {
|
|
13599
13693
|
try {
|
|
13600
|
-
const value = JSON.parse(
|
|
13694
|
+
const value = JSON.parse(fs11.readFileSync(this.snapshotPath(taskId), "utf8"));
|
|
13601
13695
|
if (!isObject6(value) || value.v !== 1 || value.task !== taskId || typeof value.seq !== "number" || typeof value.lastId !== "string" || typeof value.lastStart !== "number" || typeof value.end !== "number" || !isObject6(value.record))
|
|
13602
13696
|
return null;
|
|
13603
13697
|
return value;
|
|
@@ -13699,8 +13793,8 @@ class TaskStore {
|
|
|
13699
13793
|
}
|
|
13700
13794
|
}
|
|
13701
13795
|
openTask(record, type, key, legacy = null) {
|
|
13702
|
-
const dir =
|
|
13703
|
-
|
|
13796
|
+
const dir = path14.join(this.paths().tasks, record.id);
|
|
13797
|
+
fs11.mkdirSync(dir, { recursive: true });
|
|
13704
13798
|
const blobs = this.blobStore();
|
|
13705
13799
|
const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
|
|
13706
13800
|
const event = this.event(record.id, 1, type, {
|
|
@@ -13752,11 +13846,11 @@ class TaskStore {
|
|
|
13752
13846
|
} catch {}
|
|
13753
13847
|
if (!latest || latest.events[0]?.id !== last.id)
|
|
13754
13848
|
return failure2("busy", "the task log changed during compaction; retry", { taskId });
|
|
13755
|
-
const appended =
|
|
13849
|
+
const appended = fs11.readFileSync(file).subarray(log.end, latest.end);
|
|
13756
13850
|
const bytes = Buffer.concat([Buffer.from(prefix, "utf8"), appended]);
|
|
13757
13851
|
replaceFile(file, bytes.toString("utf8"), true);
|
|
13758
13852
|
try {
|
|
13759
|
-
|
|
13853
|
+
fs11.rmSync(this.snapshotPath(taskId), { force: true });
|
|
13760
13854
|
} catch {}
|
|
13761
13855
|
const reloaded = this.load(taskId);
|
|
13762
13856
|
if (reloaded.ok && reloaded.data)
|
|
@@ -13769,7 +13863,7 @@ class TaskStore {
|
|
|
13769
13863
|
}
|
|
13770
13864
|
countEvents(file) {
|
|
13771
13865
|
try {
|
|
13772
|
-
const bytes =
|
|
13866
|
+
const bytes = fs11.readFileSync(file);
|
|
13773
13867
|
let count = 0;
|
|
13774
13868
|
for (let index = bytes.indexOf(10);index >= 0; index = bytes.indexOf(10, index + 1))
|
|
13775
13869
|
count += 1;
|
|
@@ -13778,12 +13872,12 @@ class TaskStore {
|
|
|
13778
13872
|
return 0;
|
|
13779
13873
|
}
|
|
13780
13874
|
}
|
|
13781
|
-
openTasksForKey(key) {
|
|
13875
|
+
openTasksForKey(key, known) {
|
|
13782
13876
|
const all = this.listStoreIndex();
|
|
13783
13877
|
if (!all.ok)
|
|
13784
13878
|
return all;
|
|
13785
|
-
const location = this.location();
|
|
13786
|
-
const renames = location
|
|
13879
|
+
const location = known ? null : this.location();
|
|
13880
|
+
const renames = known ?? (location?.ok ? branchRenames(location.data) : []);
|
|
13787
13881
|
return success(null, null, all.data.filter((entry) => entry.key !== null && entry.status !== "closed").filter((entry) => (renames.length ? followRenames(entry.key, entry.boundAt ?? entry.createdAt, renames) : entry.key) === key).toSorted(oldestFirst));
|
|
13788
13882
|
}
|
|
13789
13883
|
openTaskForKey(key) {
|
|
@@ -13791,8 +13885,8 @@ class TaskStore {
|
|
|
13791
13885
|
return bound.ok ? success(null, null, bound.data[0] ?? null) : bound;
|
|
13792
13886
|
}
|
|
13793
13887
|
withKeyLock(key, operation) {
|
|
13794
|
-
const digest =
|
|
13795
|
-
return this.withLock(
|
|
13888
|
+
const digest = createHash9("sha256").update(key).digest("hex").slice(0, 32);
|
|
13889
|
+
return this.withLock(path14.join(this.paths().dir, "keys", `${digest}.lock`), operation);
|
|
13796
13890
|
}
|
|
13797
13891
|
bindLocked(taskId, key, timestamp) {
|
|
13798
13892
|
const current = this.readWorkspace();
|
|
@@ -13933,7 +14027,7 @@ class TaskStore {
|
|
|
13933
14027
|
writeWorkspace(record) {
|
|
13934
14028
|
const file = this.paths().workspace;
|
|
13935
14029
|
try {
|
|
13936
|
-
|
|
14030
|
+
fs11.mkdirSync(path14.dirname(file), { recursive: true });
|
|
13937
14031
|
replaceFile(file, `${canonicalJson(record)}
|
|
13938
14032
|
`, true);
|
|
13939
14033
|
return success(null, null, null);
|
|
@@ -13943,7 +14037,7 @@ class TaskStore {
|
|
|
13943
14037
|
}
|
|
13944
14038
|
readIndex() {
|
|
13945
14039
|
try {
|
|
13946
|
-
const value = JSON.parse(
|
|
14040
|
+
const value = JSON.parse(fs11.readFileSync(this.paths().index, "utf8"));
|
|
13947
14041
|
if (!isObject6(value) || value.version !== INDEX_VERSION || !isObject6(value.tasks))
|
|
13948
14042
|
return {};
|
|
13949
14043
|
const tasks = {};
|
|
@@ -13957,7 +14051,7 @@ class TaskStore {
|
|
|
13957
14051
|
}
|
|
13958
14052
|
writeIndex(entries) {
|
|
13959
14053
|
try {
|
|
13960
|
-
|
|
14054
|
+
fs11.mkdirSync(this.paths().tasks, { recursive: true });
|
|
13961
14055
|
replaceFile(this.paths().index, JSON.stringify({
|
|
13962
14056
|
version: INDEX_VERSION,
|
|
13963
14057
|
tasks: Object.fromEntries(entries.map((entry) => [entry.id, entry]))
|
|
@@ -14011,23 +14105,23 @@ class TaskStore {
|
|
|
14011
14105
|
return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
|
|
14012
14106
|
}
|
|
14013
14107
|
v2Dirs() {
|
|
14014
|
-
return [...new Set([this.root, this.origin])].map((dir) =>
|
|
14108
|
+
return [...new Set([this.root, this.origin])].map((dir) => path14.join(dir, ".workit"));
|
|
14015
14109
|
}
|
|
14016
14110
|
pendingMigrations() {
|
|
14017
14111
|
return this.v2Dirs().filter((dir) => {
|
|
14018
|
-
const raw = this.readV2Workspace(
|
|
14112
|
+
const raw = this.readV2Workspace(path14.join(dir, "workspace.json"));
|
|
14019
14113
|
return raw !== null && raw !== TOMBSTONE;
|
|
14020
14114
|
});
|
|
14021
14115
|
}
|
|
14022
14116
|
migrateV2(v2) {
|
|
14023
14117
|
const dirs = this.paths();
|
|
14024
|
-
const v2Workspace =
|
|
14025
|
-
const v2Tasks =
|
|
14026
|
-
const legacy =
|
|
14118
|
+
const v2Workspace = path14.join(v2, "workspace.json");
|
|
14119
|
+
const v2Tasks = path14.join(v2, "tasks");
|
|
14120
|
+
const legacy = path14.join(dirs.dir, "legacy", checkoutSlug(path14.dirname(v2)), "v2");
|
|
14027
14121
|
const raw = this.readV2Workspace(v2Workspace);
|
|
14028
14122
|
if (raw === null || raw === TOMBSTONE)
|
|
14029
14123
|
return success(null, null, null);
|
|
14030
|
-
return this.withLock(dirs.checkoutLock, () => this.withLock(
|
|
14124
|
+
return this.withLock(dirs.checkoutLock, () => this.withLock(path14.join(v2, "metadata.lock"), () => {
|
|
14031
14125
|
const workspace = this.readV2Workspace(v2Workspace);
|
|
14032
14126
|
if (workspace === null || workspace === TOMBSTONE)
|
|
14033
14127
|
return success(null, null, null);
|
|
@@ -14036,7 +14130,7 @@ class TaskStore {
|
|
|
14036
14130
|
return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
|
|
14037
14131
|
let files = [];
|
|
14038
14132
|
try {
|
|
14039
|
-
files =
|
|
14133
|
+
files = fs11.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path14.join(v2Tasks, name)));
|
|
14040
14134
|
} catch {}
|
|
14041
14135
|
const current = this.readWorkspace();
|
|
14042
14136
|
if (!current.ok)
|
|
@@ -14054,22 +14148,22 @@ class TaskStore {
|
|
|
14054
14148
|
let skipped = 0;
|
|
14055
14149
|
const done = new Map;
|
|
14056
14150
|
try {
|
|
14057
|
-
|
|
14151
|
+
fs11.mkdirSync(path14.join(legacy, "tasks"), { recursive: true });
|
|
14058
14152
|
for (const name of ["workspace.json", "index.json"])
|
|
14059
|
-
if (
|
|
14060
|
-
|
|
14153
|
+
if (fs11.existsSync(path14.join(v2, name)))
|
|
14154
|
+
fs11.copyFileSync(path14.join(v2, name), path14.join(legacy, name));
|
|
14061
14155
|
for (const name of files) {
|
|
14062
14156
|
const id = name.slice(0, -5);
|
|
14063
|
-
const file =
|
|
14064
|
-
const bytes =
|
|
14157
|
+
const file = path14.join(v2Tasks, name);
|
|
14158
|
+
const bytes = fs11.readFileSync(file, "utf8");
|
|
14065
14159
|
const digest = sha256(bytes);
|
|
14066
|
-
|
|
14160
|
+
fs11.writeFileSync(path14.join(legacy, "tasks", name), bytes);
|
|
14067
14161
|
const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
|
|
14068
14162
|
if (!parsed.ok || parsed.data.id !== id)
|
|
14069
14163
|
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 });
|
|
14070
14164
|
const record = { ...parsed.data, workspaceId: target.id };
|
|
14071
|
-
const origin = { path:
|
|
14072
|
-
if (!
|
|
14165
|
+
const origin = { path: path14.join(legacy, "tasks", name), digest };
|
|
14166
|
+
if (!fs11.existsSync(this.eventsPath(id))) {
|
|
14073
14167
|
this.openTask(record, "migrated.from_v2", null, origin);
|
|
14074
14168
|
migrated += 1;
|
|
14075
14169
|
} else {
|
|
@@ -14104,8 +14198,8 @@ class TaskStore {
|
|
|
14104
14198
|
try {
|
|
14105
14199
|
let complete = true;
|
|
14106
14200
|
for (const [name, digest] of done) {
|
|
14107
|
-
const file =
|
|
14108
|
-
if (sha256(
|
|
14201
|
+
const file = path14.join(v2Tasks, name);
|
|
14202
|
+
if (sha256(fs11.readFileSync(file, "utf8")) !== digest) {
|
|
14109
14203
|
complete = false;
|
|
14110
14204
|
continue;
|
|
14111
14205
|
}
|
|
@@ -14115,7 +14209,7 @@ class TaskStore {
|
|
|
14115
14209
|
return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
|
|
14116
14210
|
path: v2
|
|
14117
14211
|
});
|
|
14118
|
-
|
|
14212
|
+
fs11.rmSync(path14.join(v2, "index.json"), { force: true });
|
|
14119
14213
|
this.writeTombstone(v2, target.id);
|
|
14120
14214
|
} catch (error) {
|
|
14121
14215
|
return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
|
|
@@ -14135,45 +14229,45 @@ class TaskStore {
|
|
|
14135
14229
|
}
|
|
14136
14230
|
adoptLocalStore() {
|
|
14137
14231
|
const dirs = this.paths();
|
|
14138
|
-
const marker =
|
|
14139
|
-
if (!dirs.shared || !
|
|
14232
|
+
const marker = path14.join(dirs.v2, "store.json");
|
|
14233
|
+
if (!dirs.shared || !fs11.existsSync(marker))
|
|
14140
14234
|
return success(null, null, null);
|
|
14141
14235
|
return this.withLock(dirs.checkoutLock, () => {
|
|
14142
|
-
if (!
|
|
14236
|
+
if (!fs11.existsSync(marker))
|
|
14143
14237
|
return success(null, null, null);
|
|
14144
14238
|
let tasks = 0;
|
|
14145
14239
|
try {
|
|
14146
14240
|
const merge = (from, to) => {
|
|
14147
|
-
if (!
|
|
14148
|
-
|
|
14241
|
+
if (!fs11.existsSync(to)) {
|
|
14242
|
+
fs11.mkdirSync(path14.dirname(to), { recursive: true });
|
|
14149
14243
|
try {
|
|
14150
|
-
|
|
14244
|
+
fs11.renameSync(from, to);
|
|
14151
14245
|
} catch {
|
|
14152
|
-
|
|
14153
|
-
|
|
14246
|
+
fs11.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
|
|
14247
|
+
fs11.rmSync(from, { recursive: true, force: true });
|
|
14154
14248
|
}
|
|
14155
14249
|
return;
|
|
14156
14250
|
}
|
|
14157
|
-
if (
|
|
14158
|
-
for (const name of
|
|
14159
|
-
merge(
|
|
14251
|
+
if (fs11.lstatSync(from).isDirectory() && fs11.lstatSync(to).isDirectory())
|
|
14252
|
+
for (const name of fs11.readdirSync(from))
|
|
14253
|
+
merge(path14.join(from, name), path14.join(to, name));
|
|
14160
14254
|
};
|
|
14161
14255
|
for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
|
|
14162
|
-
const from =
|
|
14163
|
-
if (!
|
|
14256
|
+
const from = path14.join(dirs.v2, sub);
|
|
14257
|
+
if (!fs11.existsSync(from))
|
|
14164
14258
|
continue;
|
|
14165
14259
|
if (sub === "tasks") {
|
|
14166
|
-
for (const name of
|
|
14167
|
-
if (validId(name) && !
|
|
14260
|
+
for (const name of fs11.readdirSync(from))
|
|
14261
|
+
if (validId(name) && !fs11.existsSync(path14.join(dirs.dir, sub, name)))
|
|
14168
14262
|
tasks += 1;
|
|
14169
14263
|
}
|
|
14170
|
-
merge(from,
|
|
14264
|
+
merge(from, path14.join(dirs.dir, sub));
|
|
14171
14265
|
}
|
|
14172
|
-
const ledger =
|
|
14173
|
-
if (
|
|
14174
|
-
merge(ledger,
|
|
14175
|
-
|
|
14176
|
-
|
|
14266
|
+
const ledger = path14.join(dirs.v2, "ledger");
|
|
14267
|
+
if (fs11.existsSync(ledger) && !fs11.existsSync(path14.join(dirs.dir, "ledger")))
|
|
14268
|
+
merge(ledger, path14.join(dirs.dir, "ledger"));
|
|
14269
|
+
fs11.rmSync(path14.join(dirs.v2, "task-index.json"), { force: true });
|
|
14270
|
+
fs11.renameSync(marker, path14.join(dirs.v2, "store.moved.json"));
|
|
14177
14271
|
} catch (error) {
|
|
14178
14272
|
return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
|
|
14179
14273
|
path: dirs.v2
|
|
@@ -14193,7 +14287,7 @@ class TaskStore {
|
|
|
14193
14287
|
readV2Workspace(file) {
|
|
14194
14288
|
let bytes;
|
|
14195
14289
|
try {
|
|
14196
|
-
bytes =
|
|
14290
|
+
bytes = fs11.readFileSync(file, "utf8");
|
|
14197
14291
|
} catch {
|
|
14198
14292
|
return null;
|
|
14199
14293
|
}
|
|
@@ -14211,7 +14305,7 @@ class TaskStore {
|
|
|
14211
14305
|
schemaVersion: SCHEMA_VERSION,
|
|
14212
14306
|
id: workspaceId ?? newId(),
|
|
14213
14307
|
revision: newRevision(),
|
|
14214
|
-
root:
|
|
14308
|
+
root: path14.dirname(v2),
|
|
14215
14309
|
runtime: { createdWith: version, updatedWith: version },
|
|
14216
14310
|
writer: null,
|
|
14217
14311
|
store: {
|
|
@@ -14222,12 +14316,12 @@ class TaskStore {
|
|
|
14222
14316
|
},
|
|
14223
14317
|
critical: ["store"]
|
|
14224
14318
|
};
|
|
14225
|
-
|
|
14226
|
-
const ignore =
|
|
14227
|
-
if (!
|
|
14228
|
-
|
|
14319
|
+
fs11.mkdirSync(v2, { recursive: true });
|
|
14320
|
+
const ignore = path14.join(v2, ".gitignore");
|
|
14321
|
+
if (!fs11.existsSync(ignore))
|
|
14322
|
+
fs11.writeFileSync(ignore, `*
|
|
14229
14323
|
`);
|
|
14230
|
-
replaceFile(
|
|
14324
|
+
replaceFile(path14.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
|
|
14231
14325
|
`, true);
|
|
14232
14326
|
}
|
|
14233
14327
|
stubFor(taskId) {
|
|
@@ -14243,7 +14337,7 @@ class TaskStore {
|
|
|
14243
14337
|
}
|
|
14244
14338
|
isStub(file) {
|
|
14245
14339
|
try {
|
|
14246
|
-
const value = JSON.parse(
|
|
14340
|
+
const value = JSON.parse(fs11.readFileSync(file, "utf8"));
|
|
14247
14341
|
return isObject6(value) && isObject6(value.store) && value.store.format === "workit-store";
|
|
14248
14342
|
} catch {
|
|
14249
14343
|
return false;
|
|
@@ -14251,32 +14345,32 @@ class TaskStore {
|
|
|
14251
14345
|
}
|
|
14252
14346
|
writeTombstoneIfMissing() {
|
|
14253
14347
|
try {
|
|
14254
|
-
const v2 =
|
|
14255
|
-
if (
|
|
14348
|
+
const v2 = path14.join(this.root, ".workit");
|
|
14349
|
+
if (fs11.existsSync(path14.join(v2, "workspace.json")))
|
|
14256
14350
|
return;
|
|
14257
14351
|
let id = null;
|
|
14258
14352
|
try {
|
|
14259
|
-
id = JSON.parse(
|
|
14353
|
+
id = JSON.parse(fs11.readFileSync(this.paths().workspace, "utf8")).id ?? null;
|
|
14260
14354
|
} catch {}
|
|
14261
14355
|
this.writeTombstone(v2, id);
|
|
14262
14356
|
} catch {}
|
|
14263
14357
|
}
|
|
14264
14358
|
initializeStorage() {
|
|
14265
14359
|
const dirs = this.paths();
|
|
14266
|
-
retryTransient(() =>
|
|
14267
|
-
retryTransient(() =>
|
|
14268
|
-
if (!
|
|
14360
|
+
retryTransient(() => fs11.mkdirSync(dirs.checkout, { recursive: true }));
|
|
14361
|
+
retryTransient(() => fs11.mkdirSync(dirs.tasks, { recursive: true }));
|
|
14362
|
+
if (!fs11.existsSync(dirs.marker))
|
|
14269
14363
|
replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
|
|
14270
14364
|
`, false);
|
|
14271
14365
|
if (!dirs.shared) {
|
|
14272
|
-
const ignore =
|
|
14366
|
+
const ignore = path14.join(dirs.dir, ".gitignore");
|
|
14273
14367
|
let current = null;
|
|
14274
14368
|
try {
|
|
14275
|
-
current =
|
|
14369
|
+
current = fs11.readFileSync(ignore, "utf8");
|
|
14276
14370
|
} catch {}
|
|
14277
14371
|
if (current !== `*
|
|
14278
14372
|
`)
|
|
14279
|
-
retryTransient(() =>
|
|
14373
|
+
retryTransient(() => fs11.writeFileSync(ignore, `*
|
|
14280
14374
|
`));
|
|
14281
14375
|
}
|
|
14282
14376
|
this.writeTombstoneIfMissing();
|
|
@@ -14292,7 +14386,7 @@ class TaskStore {
|
|
|
14292
14386
|
shouldReclaim: ({ payload, nowMs }) => {
|
|
14293
14387
|
let ageMs = null;
|
|
14294
14388
|
try {
|
|
14295
|
-
ageMs = nowMs -
|
|
14389
|
+
ageMs = nowMs - fs11.lstatSync(lockPath).mtimeMs;
|
|
14296
14390
|
} catch {}
|
|
14297
14391
|
return classifyLockOwner(payload, ageMs, localLockHost(), TASK_STORE_LOCK).state === "stale";
|
|
14298
14392
|
},
|
|
@@ -14329,7 +14423,7 @@ class TaskStore {
|
|
|
14329
14423
|
withLock(lockPath, operation) {
|
|
14330
14424
|
try {
|
|
14331
14425
|
this.initializeStorage();
|
|
14332
|
-
|
|
14426
|
+
fs11.mkdirSync(path14.dirname(lockPath), { recursive: true });
|
|
14333
14427
|
} catch (error) {
|
|
14334
14428
|
return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
|
|
14335
14429
|
path: this.paths().dir
|
|
@@ -14369,18 +14463,18 @@ class TaskStore {
|
|
|
14369
14463
|
lockDirWritable(lockPath) {
|
|
14370
14464
|
const probe = `${lockPath}.${process.pid}.${randomUUID3()}.probe`;
|
|
14371
14465
|
try {
|
|
14372
|
-
|
|
14466
|
+
fs11.closeSync(fs11.openSync(probe, "wx", 384));
|
|
14373
14467
|
} catch {
|
|
14374
14468
|
return false;
|
|
14375
14469
|
}
|
|
14376
14470
|
try {
|
|
14377
|
-
|
|
14471
|
+
fs11.rmSync(probe, { force: true });
|
|
14378
14472
|
} catch {}
|
|
14379
14473
|
return true;
|
|
14380
14474
|
}
|
|
14381
14475
|
lockHolderPresent(lockPath) {
|
|
14382
14476
|
try {
|
|
14383
|
-
|
|
14477
|
+
fs11.lstatSync(lockPath);
|
|
14384
14478
|
return true;
|
|
14385
14479
|
} catch (error) {
|
|
14386
14480
|
return error?.code !== "ENOENT";
|
|
@@ -14392,12 +14486,12 @@ class TaskStore {
|
|
|
14392
14486
|
if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
|
|
14393
14487
|
return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
|
|
14394
14488
|
path: lockPath,
|
|
14395
|
-
guidance: `Check permissions and the read-only attribute on ${
|
|
14489
|
+
guidance: `Check permissions and the read-only attribute on ${path14.dirname(lockPath)}.`
|
|
14396
14490
|
});
|
|
14397
14491
|
if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
|
|
14398
14492
|
let lock = null;
|
|
14399
14493
|
try {
|
|
14400
|
-
lock = parseMetadataLockOrNull(
|
|
14494
|
+
lock = parseMetadataLockOrNull(fs11.readFileSync(lockPath, "utf8"));
|
|
14401
14495
|
} catch {}
|
|
14402
14496
|
return failure2("busy", `workit lock is held by another Workit call${lock ? ` (pid ${lock.pid} on ${lock.host})` : ""}; retry shortly`, {
|
|
14403
14497
|
outcome: "not_started",
|
|
@@ -14438,7 +14532,7 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
|
|
|
14438
14532
|
};
|
|
14439
14533
|
}, fileSignature = (file) => {
|
|
14440
14534
|
try {
|
|
14441
|
-
const stat =
|
|
14535
|
+
const stat = fs11.statSync(file, { bigint: true });
|
|
14442
14536
|
return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
|
|
14443
14537
|
} catch {
|
|
14444
14538
|
return null;
|
|
@@ -14450,7 +14544,7 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
|
|
|
14450
14544
|
}, now = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z"), oldestFirst = (left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id), cachedRuntimeVersion = null, runtimeVersion = () => {
|
|
14451
14545
|
if (cachedRuntimeVersion === null) {
|
|
14452
14546
|
try {
|
|
14453
|
-
const pkg = JSON.parse(
|
|
14547
|
+
const pkg = JSON.parse(fs11.readFileSync(path14.join(packageRoot(), "package.json"), "utf8"));
|
|
14454
14548
|
cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
|
|
14455
14549
|
} catch {
|
|
14456
14550
|
cachedRuntimeVersion = "0.0.0";
|
|
@@ -14470,20 +14564,47 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
|
|
|
14470
14564
|
return left > right;
|
|
14471
14565
|
}
|
|
14472
14566
|
return false;
|
|
14473
|
-
}, validId = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(value),
|
|
14474
|
-
|
|
14567
|
+
}, validId = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(value), readText = (file) => {
|
|
14568
|
+
try {
|
|
14569
|
+
return fs11.readFileSync(file, "utf8");
|
|
14570
|
+
} catch {
|
|
14571
|
+
return "";
|
|
14572
|
+
}
|
|
14573
|
+
}, REFLOG_LINE, branchStart = (location, branch, aliases) => {
|
|
14574
|
+
const first = readText(path14.join(path14.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
|
|
14575
|
+
`, 1)[0];
|
|
14576
|
+
const created = REFLOG_LINE.exec(first);
|
|
14577
|
+
const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
|
|
14578
|
+
if (!created || !start)
|
|
14579
|
+
return null;
|
|
14580
|
+
if (start !== "HEAD")
|
|
14581
|
+
return start;
|
|
14582
|
+
if (!location.git)
|
|
14583
|
+
return null;
|
|
14584
|
+
for (const line of readText(path14.join(location.git.gitDir, "logs", "HEAD")).split(`
|
|
14585
|
+
`)) {
|
|
14586
|
+
const entry = REFLOG_LINE.exec(line);
|
|
14587
|
+
const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
|
|
14588
|
+
const seconds = entry ? Number(entry[2]) - Number(created[2]) : -1;
|
|
14589
|
+
if (moved && entry[1] === created[1] && seconds >= 0 && seconds <= 1 && aliases.includes(moved[2]))
|
|
14590
|
+
return moved[1];
|
|
14591
|
+
}
|
|
14592
|
+
return null;
|
|
14593
|
+
}, refExists = (common, ref) => fs11.existsSync(path14.join(common, ref)) || readText(path14.join(common, "packed-refs")).includes(` ${ref}
|
|
14594
|
+
`), origins, sameDirectoryIdentity = (left, right) => {
|
|
14595
|
+
if (!path14.isAbsolute(left) || !path14.isAbsolute(right))
|
|
14475
14596
|
return false;
|
|
14476
|
-
const normalizedLeft =
|
|
14477
|
-
const normalizedRight =
|
|
14597
|
+
const normalizedLeft = path14.resolve(left);
|
|
14598
|
+
const normalizedRight = path14.resolve(right);
|
|
14478
14599
|
try {
|
|
14479
|
-
const a =
|
|
14480
|
-
const b =
|
|
14600
|
+
const a = fs11.statSync(normalizedLeft, { bigint: true });
|
|
14601
|
+
const b = fs11.statSync(normalizedRight, { bigint: true });
|
|
14481
14602
|
if (!a.isDirectory() || !b.isDirectory())
|
|
14482
14603
|
return false;
|
|
14483
14604
|
if (process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight)
|
|
14484
14605
|
return true;
|
|
14485
|
-
const canonicalLeft =
|
|
14486
|
-
const canonicalRight =
|
|
14606
|
+
const canonicalLeft = fs11.realpathSync(normalizedLeft);
|
|
14607
|
+
const canonicalRight = fs11.realpathSync(normalizedRight);
|
|
14487
14608
|
if (process.platform === "win32" ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase() : canonicalLeft === canonicalRight)
|
|
14488
14609
|
return true;
|
|
14489
14610
|
return a.dev === b.dev && a.ino !== 0n && a.ino === b.ino;
|
|
@@ -14518,6 +14639,8 @@ var init_task_store = __esm(() => {
|
|
|
14518
14639
|
AUTO_COMPACT_BYTES = 2 * 1024 * 1024;
|
|
14519
14640
|
TOMBSTONE = Symbol("tombstone");
|
|
14520
14641
|
STALE_TEMPORARY_MS = 60 * 60000;
|
|
14642
|
+
REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
|
|
14643
|
+
origins = new Map;
|
|
14521
14644
|
heldInProcess = new Map;
|
|
14522
14645
|
});
|
|
14523
14646
|
|
|
@@ -14529,8 +14652,8 @@ var init_workers = __esm(() => {
|
|
|
14529
14652
|
|
|
14530
14653
|
// packages/workit-core/src/check-config.ts
|
|
14531
14654
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
14532
|
-
import
|
|
14533
|
-
import
|
|
14655
|
+
import fs12 from "node:fs";
|
|
14656
|
+
import path15 from "node:path";
|
|
14534
14657
|
function splitCommand(command) {
|
|
14535
14658
|
const argv = [];
|
|
14536
14659
|
let current = "";
|
|
@@ -14568,12 +14691,12 @@ function splitCommand(command) {
|
|
|
14568
14691
|
return argv.length ? argv : new Error("empty command");
|
|
14569
14692
|
}
|
|
14570
14693
|
function checkRoot(cwd) {
|
|
14571
|
-
return
|
|
14694
|
+
return path15.resolve(git4(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
|
|
14572
14695
|
}
|
|
14573
14696
|
function detectedChecks(root) {
|
|
14574
14697
|
const read = (file) => {
|
|
14575
14698
|
try {
|
|
14576
|
-
return
|
|
14699
|
+
return fs12.readFileSync(path15.join(root, file), "utf8");
|
|
14577
14700
|
} catch {
|
|
14578
14701
|
return null;
|
|
14579
14702
|
}
|
|
@@ -14611,7 +14734,7 @@ function detectedChecks(root) {
|
|
|
14611
14734
|
}
|
|
14612
14735
|
function loadCheckConfig(cwd) {
|
|
14613
14736
|
const root = checkRoot(cwd);
|
|
14614
|
-
const file =
|
|
14737
|
+
const file = path15.join(root, CHECKS_FILE);
|
|
14615
14738
|
const none = (error = null) => ({
|
|
14616
14739
|
source: error ? CHECKS_FILE : "none",
|
|
14617
14740
|
root,
|
|
@@ -14621,7 +14744,7 @@ function loadCheckConfig(cwd) {
|
|
|
14621
14744
|
});
|
|
14622
14745
|
let text = null;
|
|
14623
14746
|
try {
|
|
14624
|
-
text =
|
|
14747
|
+
text = fs12.readFileSync(file, "utf8");
|
|
14625
14748
|
} catch (error) {
|
|
14626
14749
|
if (error.code !== "ENOENT")
|
|
14627
14750
|
return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
|
|
@@ -14682,10 +14805,10 @@ function workitAvailable(env = process.env) {
|
|
|
14682
14805
|
return workitOnPath;
|
|
14683
14806
|
const windows = process.platform === "win32";
|
|
14684
14807
|
const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
|
|
14685
|
-
const dirs = (env.PATH ?? env.Path ?? "").split(
|
|
14808
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(path15.delimiter).filter(Boolean);
|
|
14686
14809
|
workitOnPath = dirs.some((dir) => exts.some((ext) => {
|
|
14687
14810
|
try {
|
|
14688
|
-
return
|
|
14811
|
+
return fs12.statSync(path15.join(dir, `workit${ext}`)).isFile();
|
|
14689
14812
|
} catch {
|
|
14690
14813
|
return false;
|
|
14691
14814
|
}
|
|
@@ -14706,7 +14829,7 @@ var SCRIPT_CHECKS, CHECKS_FILE = "workit.checks.json", isRecord3 = (value) => ty
|
|
|
14706
14829
|
const value = run.status === 0 ? (run.stdout ?? "").trim() : "";
|
|
14707
14830
|
return value || null;
|
|
14708
14831
|
}, packageManager = (root) => {
|
|
14709
|
-
const has = (file) =>
|
|
14832
|
+
const has = (file) => fs12.existsSync(path15.join(root, file));
|
|
14710
14833
|
if (has("bun.lock") || has("bun.lockb"))
|
|
14711
14834
|
return "bun";
|
|
14712
14835
|
if (has("pnpm-lock.yaml"))
|
|
@@ -14729,14 +14852,14 @@ var init_check_config = __esm(() => {
|
|
|
14729
14852
|
});
|
|
14730
14853
|
|
|
14731
14854
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
14732
|
-
import { createHash as
|
|
14733
|
-
import * as
|
|
14855
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
14856
|
+
import * as fs13 from "node:fs";
|
|
14734
14857
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
14735
|
-
import
|
|
14858
|
+
import path16 from "node:path";
|
|
14736
14859
|
function captureCandidate(root, scope, environment = []) {
|
|
14737
14860
|
let checkout;
|
|
14738
14861
|
try {
|
|
14739
|
-
checkout =
|
|
14862
|
+
checkout = fs13.realpathSync(root);
|
|
14740
14863
|
} catch {
|
|
14741
14864
|
return failure2("invalid_input", "candidate root does not exist");
|
|
14742
14865
|
}
|
|
@@ -14754,11 +14877,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
14754
14877
|
if (item === ".git" || item === ".workit")
|
|
14755
14878
|
continue;
|
|
14756
14879
|
names.add(item);
|
|
14757
|
-
if (!
|
|
14880
|
+
if (!fs13.existsSync(target))
|
|
14758
14881
|
continue;
|
|
14759
14882
|
let stat;
|
|
14760
14883
|
try {
|
|
14761
|
-
stat =
|
|
14884
|
+
stat = fs13.lstatSync(target);
|
|
14762
14885
|
} catch {
|
|
14763
14886
|
uncertain = true;
|
|
14764
14887
|
continue;
|
|
@@ -14774,17 +14897,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
14774
14897
|
for (const item of [...names].toSorted()) {
|
|
14775
14898
|
if (!scopeMatches(item, normalizedScope))
|
|
14776
14899
|
continue;
|
|
14777
|
-
const target =
|
|
14900
|
+
const target = path16.join(checkout, item);
|
|
14778
14901
|
try {
|
|
14779
|
-
const stat =
|
|
14902
|
+
const stat = fs13.lstatSync(target);
|
|
14780
14903
|
if (stat.isSymbolicLink()) {
|
|
14781
|
-
const link =
|
|
14904
|
+
const link = fs13.readlinkSync(target);
|
|
14782
14905
|
files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
|
|
14783
14906
|
} else if (stat.isFile()) {
|
|
14784
14907
|
files.push({
|
|
14785
14908
|
path: item,
|
|
14786
14909
|
kind: "file",
|
|
14787
|
-
digest: digestBytes(
|
|
14910
|
+
digest: digestBytes(fs13.readFileSync(target)),
|
|
14788
14911
|
executable: (stat.mode & 73) !== 0
|
|
14789
14912
|
});
|
|
14790
14913
|
} else if (!stat.isDirectory()) {
|
|
@@ -15147,12 +15270,12 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
15147
15270
|
requirementIds: evaluations.map((item) => item.requirementId)
|
|
15148
15271
|
});
|
|
15149
15272
|
}
|
|
15150
|
-
var digestBytes = (value) =>
|
|
15273
|
+
var digestBytes = (value) => createHash10("sha256").update(value).digest("hex"), inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path16.sep}`), relative = (root, candidate) => path16.relative(root, candidate).split(path16.sep).join("/") || ".", canonicalPath = (value) => {
|
|
15151
15274
|
if (typeof value !== "string" || !value)
|
|
15152
15275
|
return null;
|
|
15153
15276
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
15154
15277
|
return null;
|
|
15155
|
-
const normalized =
|
|
15278
|
+
const normalized = path16.posix.normalize(value);
|
|
15156
15279
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
15157
15280
|
return null;
|
|
15158
15281
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -15194,20 +15317,20 @@ var digestBytes = (value) => createHash9("sha256").update(value).digest("hex"),
|
|
|
15194
15317
|
git: !/not a git repository/i.test(stderr)
|
|
15195
15318
|
};
|
|
15196
15319
|
}
|
|
15197
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
15320
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")) : [];
|
|
15198
15321
|
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
15199
15322
|
if (stagedDeleted.status !== 0) {
|
|
15200
15323
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
15201
15324
|
if (!/not a git repository/i.test(stderr))
|
|
15202
15325
|
return { paths, uncertain: true, git: true };
|
|
15203
15326
|
} else if (stagedDeleted.stdout) {
|
|
15204
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
15327
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")));
|
|
15205
15328
|
}
|
|
15206
15329
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
15207
15330
|
}, walk = (root, directory, output) => {
|
|
15208
15331
|
let names;
|
|
15209
15332
|
try {
|
|
15210
|
-
names =
|
|
15333
|
+
names = fs13.readdirSync(directory);
|
|
15211
15334
|
} catch {
|
|
15212
15335
|
return true;
|
|
15213
15336
|
}
|
|
@@ -15215,10 +15338,10 @@ var digestBytes = (value) => createHash9("sha256").update(value).digest("hex"),
|
|
|
15215
15338
|
for (const name of names) {
|
|
15216
15339
|
if (name === ".git" || name === ".workit")
|
|
15217
15340
|
continue;
|
|
15218
|
-
const target =
|
|
15341
|
+
const target = path16.join(directory, name);
|
|
15219
15342
|
let stat;
|
|
15220
15343
|
try {
|
|
15221
|
-
stat =
|
|
15344
|
+
stat = fs13.lstatSync(target);
|
|
15222
15345
|
} catch {
|
|
15223
15346
|
uncertain = true;
|
|
15224
15347
|
continue;
|
|
@@ -15232,14 +15355,14 @@ var digestBytes = (value) => createHash9("sha256").update(value).digest("hex"),
|
|
|
15232
15355
|
}, scopeRoots = (root, scope) => {
|
|
15233
15356
|
const roots = [];
|
|
15234
15357
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
15235
|
-
const target =
|
|
15358
|
+
const target = path16.resolve(root, item);
|
|
15236
15359
|
if (!inside(root, target))
|
|
15237
15360
|
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` }] });
|
|
15238
15361
|
let ancestor = target;
|
|
15239
|
-
while (!
|
|
15240
|
-
ancestor =
|
|
15362
|
+
while (!fs13.existsSync(ancestor) && ancestor !== root)
|
|
15363
|
+
ancestor = path16.dirname(ancestor);
|
|
15241
15364
|
try {
|
|
15242
|
-
if (!inside(root,
|
|
15365
|
+
if (!inside(root, fs13.realpathSync(ancestor)))
|
|
15243
15366
|
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` }] });
|
|
15244
15367
|
} catch {
|
|
15245
15368
|
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
@@ -15292,7 +15415,7 @@ var digestBytes = (value) => createHash9("sha256").update(value).digest("hex"),
|
|
|
15292
15415
|
}, findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId != null), verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
15293
15416
|
let root;
|
|
15294
15417
|
try {
|
|
15295
|
-
root =
|
|
15418
|
+
root = fs13.realpathSync(checkoutRoot);
|
|
15296
15419
|
} catch {
|
|
15297
15420
|
return failure2("permission_denied", "checkout root is unavailable");
|
|
15298
15421
|
}
|
|
@@ -15301,23 +15424,23 @@ var digestBytes = (value) => createHash9("sha256").update(value).digest("hex"),
|
|
|
15301
15424
|
continue;
|
|
15302
15425
|
if (!reference.digest)
|
|
15303
15426
|
return failure2("invalid_input", "document references require a byte digest");
|
|
15304
|
-
const target =
|
|
15305
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
15427
|
+
const target = path16.resolve(root, reference.path);
|
|
15428
|
+
if (target !== root && !target.startsWith(`${root}${path16.sep}`))
|
|
15306
15429
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
15307
15430
|
fields: [
|
|
15308
15431
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
15309
15432
|
]
|
|
15310
15433
|
});
|
|
15311
15434
|
try {
|
|
15312
|
-
const below =
|
|
15435
|
+
const below = path16.relative(root, target);
|
|
15313
15436
|
let current = root;
|
|
15314
|
-
for (const segment of below.split(
|
|
15437
|
+
for (const segment of below.split(path16.sep)) {
|
|
15315
15438
|
if (!segment)
|
|
15316
15439
|
continue;
|
|
15317
|
-
current =
|
|
15318
|
-
const stat =
|
|
15319
|
-
const real =
|
|
15320
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
15440
|
+
current = path16.join(current, segment);
|
|
15441
|
+
const stat = fs13.lstatSync(current);
|
|
15442
|
+
const real = fs13.realpathSync(current);
|
|
15443
|
+
if (real !== root && !real.startsWith(`${root}${path16.sep}`))
|
|
15321
15444
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
15322
15445
|
fields: [
|
|
15323
15446
|
{
|
|
@@ -15333,7 +15456,7 @@ var digestBytes = (value) => createHash9("sha256").update(value).digest("hex"),
|
|
|
15333
15456
|
if (current !== target && !stat.isDirectory())
|
|
15334
15457
|
return failure2("invalid_input", "document reference ancestor is not a directory");
|
|
15335
15458
|
}
|
|
15336
|
-
if (digestBytes(
|
|
15459
|
+
if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
|
|
15337
15460
|
return failure2("permission_denied", "decision document bytes have changed");
|
|
15338
15461
|
} catch {
|
|
15339
15462
|
return failure2("invalid_input", "decision document is unavailable");
|
|
@@ -15437,7 +15560,8 @@ Autonomy contract:
|
|
|
15437
15560
|
satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
|
|
15438
15561
|
- Author is not verifier: your own verdict is \`--self\`, shown as
|
|
15439
15562
|
self-reviewed, never verified. High risk, \`verification: independent\` and
|
|
15440
|
-
merge need a verifier with its own session (
|
|
15563
|
+
merge need a verifier with its own host-derived session (a subagent's
|
|
15564
|
+
\`<lead>:<child>\`, set by the host); never hand-mint one such as \`<lead>-v1\`.
|
|
15441
15565
|
- A local-commit endpoint does not imply PR readiness, and a local commit alone
|
|
15442
15566
|
is not evidence of a requested remote push. Before reporting delivery,
|
|
15443
15567
|
reconcile every requested item and observe it (\`workit verify-delivery\`).
|
|
@@ -15507,38 +15631,29 @@ function reconcileResume(view, observations = []) {
|
|
|
15507
15631
|
reassessmentRequired: view.task.policy?.policyVersion !== POLICY_VERSION
|
|
15508
15632
|
});
|
|
15509
15633
|
}
|
|
15510
|
-
function compactTaskContext(view) {
|
|
15511
|
-
const decisions =
|
|
15512
|
-
|
|
15513
|
-
|
|
15514
|
-
|
|
15515
|
-
|
|
15516
|
-
return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
|
|
15517
|
-
}).slice(0, COMPACT_MAX_ITEMS).map((entry) => ({
|
|
15518
|
-
id: entry.id,
|
|
15519
|
-
purpose: entry.data.purpose,
|
|
15520
|
-
status: entry.data.response,
|
|
15521
|
-
digest: entry.data.digest,
|
|
15522
|
-
choice: compactDecisionChoice(entry) || `task.inspect taskId=${view.task.id} view=full; decision=${entry.id}`,
|
|
15523
|
-
references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference)
|
|
15524
|
-
}));
|
|
15525
|
-
const methods = view.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
|
|
15634
|
+
function compactTaskContext(view, inherited = null) {
|
|
15635
|
+
const decisions = compactDecisions([
|
|
15636
|
+
view && { origin: null, taskId: view.task.id, decisions: view.task.decisions },
|
|
15637
|
+
inherited
|
|
15638
|
+
]);
|
|
15639
|
+
const methods = view?.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
|
|
15526
15640
|
id: method.id,
|
|
15527
15641
|
assurance: method.assurance,
|
|
15528
15642
|
reason: compactText(method.reason, COMPACT_TEXT_BYTES) ?? ""
|
|
15529
15643
|
})) : [];
|
|
15644
|
+
const stale = view?.evidence.filter((entry) => entry.status === "stale").length;
|
|
15530
15645
|
const gaps = Array.from(new Set([
|
|
15531
|
-
...
|
|
15532
|
-
...view
|
|
15533
|
-
...view
|
|
15646
|
+
...stale ? [`stale evidence: ${stale}; workit task inspect --task ${view.task.id} --view full`] : [],
|
|
15647
|
+
...(view?.requirements ?? []).filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
|
|
15648
|
+
...(view?.task.progress.blockers ?? []).map((entry) => entry.reason)
|
|
15534
15649
|
])).toSorted().slice(0, COMPACT_MAX_ITEMS).map((gap) => compactText(gap, COMPACT_TEXT_BYTES));
|
|
15535
15650
|
const context = {
|
|
15536
|
-
objective: compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "",
|
|
15537
|
-
status: view
|
|
15651
|
+
objective: view ? compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "" : "no task on this branch yet",
|
|
15652
|
+
status: view?.task.status ?? null,
|
|
15538
15653
|
decisions,
|
|
15539
15654
|
methods,
|
|
15540
15655
|
gaps,
|
|
15541
|
-
nextAction: compactText(view
|
|
15656
|
+
nextAction: compactText(view?.task.progress.nextAction ?? null, COMPACT_TEXT_BYTES)
|
|
15542
15657
|
};
|
|
15543
15658
|
let encoded = JSON.stringify(context);
|
|
15544
15659
|
while (compactBytes(encoded) > COMPACT_MAX_BYTES && (context.decisions.length || context.methods.length || context.gaps.length)) {
|
|
@@ -15581,7 +15696,21 @@ var COMPACT_MAX_BYTES = 4096, COMPACT_MAX_ITEMS = 8, COMPACT_TEXT_BYTES = 128, s
|
|
|
15581
15696
|
id: ref.id
|
|
15582
15697
|
};
|
|
15583
15698
|
return { kind: ref.kind };
|
|
15584
|
-
},
|
|
15699
|
+
}, newestDecisionFirst = (left, right) => {
|
|
15700
|
+
const leftAt = compactTimestamp(left.recordedAt);
|
|
15701
|
+
const rightAt = compactTimestamp(right.recordedAt);
|
|
15702
|
+
if (leftAt !== rightAt)
|
|
15703
|
+
return leftAt > rightAt ? -1 : 1;
|
|
15704
|
+
return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
|
|
15705
|
+
}, compactDecisions = (sources) => sources.flatMap((source) => source ? source.decisions.toSorted(newestDecisionFirst).map((entry) => ({
|
|
15706
|
+
id: entry.id,
|
|
15707
|
+
purpose: entry.data.purpose,
|
|
15708
|
+
status: entry.data.response,
|
|
15709
|
+
digest: entry.data.digest,
|
|
15710
|
+
choice: compactDecisionChoice(entry) || `task.inspect taskId=${source.taskId} view=full; decision=${entry.id}`,
|
|
15711
|
+
references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference),
|
|
15712
|
+
origin: source.origin ?? undefined
|
|
15713
|
+
})) : []).slice(0, COMPACT_MAX_ITEMS), exportDigest = (bundle) => sha256({
|
|
15585
15714
|
schemaVersion: bundle.schemaVersion,
|
|
15586
15715
|
exportedAt: bundle.exportedAt,
|
|
15587
15716
|
sourceWorkspaceId: bundle.sourceWorkspaceId,
|
|
@@ -15644,6 +15773,17 @@ class WorkitCore {
|
|
|
15644
15773
|
const root = this.contextRootError();
|
|
15645
15774
|
if (!root.ok)
|
|
15646
15775
|
return root;
|
|
15776
|
+
const addressed = WORKER_ADDRESSED_ACTIONS.has(action);
|
|
15777
|
+
const workerId = addressed ? value.workerId : this.context.workerId ?? null;
|
|
15778
|
+
if (workerId !== null) {
|
|
15779
|
+
const owner = this.store.taskForWorker(workerId);
|
|
15780
|
+
if (!owner.ok)
|
|
15781
|
+
return owner;
|
|
15782
|
+
if (owner.data)
|
|
15783
|
+
return success(null, null, { ...request, taskId: owner.data });
|
|
15784
|
+
if (addressed)
|
|
15785
|
+
return failure2("not_found", `worker ${workerId} is on no open task; find its task with \`workit task status --all\`, then run \`${workerCommand("<task-id>", request)}\``);
|
|
15786
|
+
}
|
|
15647
15787
|
const create = CREATING_ACTIONS.has(action) && (this.context.workerId ?? null) === null;
|
|
15648
15788
|
const found = this.store.implicitTask({
|
|
15649
15789
|
provenance: provenance(this.context),
|
|
@@ -16350,7 +16490,9 @@ class WorkitCore {
|
|
|
16350
16490
|
if (!resolved.ok)
|
|
16351
16491
|
return resolved;
|
|
16352
16492
|
const value = resolved.data;
|
|
16353
|
-
|
|
16493
|
+
const result = this.retryOmittedRevisions(value, () => this.workerOnce(value));
|
|
16494
|
+
const { action, taskId } = value ?? {};
|
|
16495
|
+
return result.ok || request?.taskId !== undefined || typeof taskId !== "string" || !WORKER_ADDRESSED_ACTIONS.has(`worker.${String(action)}`) ? result : { ...result, error: `${result.error}; run \`${workerCommand(taskId, value)}\`` };
|
|
16354
16496
|
}
|
|
16355
16497
|
workerOnce(request) {
|
|
16356
16498
|
const root = this.contextRootError();
|
|
@@ -16555,10 +16697,18 @@ class WorkitCore {
|
|
|
16555
16697
|
requirements
|
|
16556
16698
|
});
|
|
16557
16699
|
}
|
|
16558
|
-
compactContext(taskId, freshness) {
|
|
16700
|
+
compactContext(taskId, freshness, origin = null) {
|
|
16559
16701
|
const root = this.contextRootError();
|
|
16560
16702
|
if (!root.ok)
|
|
16561
16703
|
return root;
|
|
16704
|
+
const base = origin && this.store.readStoreTask(origin.taskId);
|
|
16705
|
+
const inherited = base?.ok && base.data ? {
|
|
16706
|
+
origin: origin.origin,
|
|
16707
|
+
taskId: base.data.id,
|
|
16708
|
+
decisions: base.data.decisions.filter((entry) => entry.data.revoked === null)
|
|
16709
|
+
} : null;
|
|
16710
|
+
if (taskId === null)
|
|
16711
|
+
return success(null, null, inherited?.decisions.length ? compactTaskContext(null, inherited) : "");
|
|
16562
16712
|
const task = this.store.readTask(taskId);
|
|
16563
16713
|
if (!task.ok)
|
|
16564
16714
|
return task;
|
|
@@ -16568,7 +16718,7 @@ class WorkitCore {
|
|
|
16568
16718
|
const view = this.view(task.data, false, freshness ?? signalFreshness(this.store.root));
|
|
16569
16719
|
if (!view.ok)
|
|
16570
16720
|
return view;
|
|
16571
|
-
return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
|
|
16721
|
+
return success(view.revision, view.workspaceRevision, compactTaskContext(view.data, inherited));
|
|
16572
16722
|
}
|
|
16573
16723
|
view(task, capture = true, freshness = treeFreshness(this.store.root)) {
|
|
16574
16724
|
const workspace = this.store.readWorkspace();
|
|
@@ -16979,7 +17129,12 @@ var MAX_JUDGED_REFS = 5, workspaceVerification = (root) => {
|
|
|
16979
17129
|
return changed;
|
|
16980
17130
|
const updated = changed.data.task.workers.find((candidate) => candidate.id === input.workerId);
|
|
16981
17131
|
return updated ? success(changed.data.task.revision, changed.data.workspace.revision, updated) : failure2("recovery_required", "worker disappeared during lifecycle observation");
|
|
16982
|
-
}, refsWithinScope = (refs, scope) => refs.every((ref) => ref.kind !== "file" || scopeCovers(scope, { description: "", paths: [ref.path], exclusions: [] })), TASK_SCOPED_ACTIONS,
|
|
17132
|
+
}, refsWithinScope = (refs, scope) => refs.every((ref) => ref.kind !== "file" || scopeCovers(scope, { description: "", paths: [ref.path], exclusions: [] })), TASK_SCOPED_ACTIONS, WORKER_ADDRESSED_ACTIONS, workerCommand = (taskId, request) => {
|
|
17133
|
+
const { action, workerId, reason, report } = request;
|
|
17134
|
+
const rest = JSON.stringify({ reason, report });
|
|
17135
|
+
const payload = rest === "{}" ? "" : ` --payload '${rest.replace(/'/gu, "'\\''")}'`;
|
|
17136
|
+
return `workit worker ${String(action)} ${String(workerId)} --task ${taskId}${payload}`;
|
|
17137
|
+
}, CREATING_ACTIONS, REVISION_RETRY_ATTEMPTS = 8, retryPause, filledRevisions = (request) => {
|
|
16983
17138
|
const value = typeof request === "object" && request !== null ? request : {};
|
|
16984
17139
|
return {
|
|
16985
17140
|
task: value.expectedRevision === undefined,
|
|
@@ -17201,6 +17356,7 @@ var init_task_engine = __esm(() => {
|
|
|
17201
17356
|
"worker.cancel",
|
|
17202
17357
|
"state.export"
|
|
17203
17358
|
]);
|
|
17359
|
+
WORKER_ADDRESSED_ACTIONS = new Set(["worker.cancel", "worker.report"]);
|
|
17204
17360
|
CREATING_ACTIONS = new Set([
|
|
17205
17361
|
"task.revise",
|
|
17206
17362
|
"task.progress",
|
|
@@ -17240,26 +17396,31 @@ var support = (descriptor, axis) => {
|
|
|
17240
17396
|
});
|
|
17241
17397
|
|
|
17242
17398
|
// packages/workit-core/src/hooks/context.ts
|
|
17243
|
-
import { createHash as
|
|
17244
|
-
import
|
|
17245
|
-
import
|
|
17399
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
17400
|
+
import fs14 from "node:fs";
|
|
17401
|
+
import path17 from "node:path";
|
|
17246
17402
|
function sessionCompactContext(store, session, context, selection = "session-bound") {
|
|
17247
17403
|
const listed = store.listTaskIndex();
|
|
17248
17404
|
if (!listed.ok)
|
|
17249
17405
|
return migrationPending(listed) ? MIGRATION_PENDING_NOTE : null;
|
|
17250
17406
|
const entry = currentTaskEntry(listed.data, session, selection) ?? implicitTaskEntry(store, listed.data, session);
|
|
17251
|
-
return
|
|
17407
|
+
return entryCompactContext(store, entry, session, context);
|
|
17252
17408
|
}
|
|
17253
17409
|
function entryCompactContext(store, entry, session, context) {
|
|
17410
|
+
const found = store.originTaskEntry();
|
|
17411
|
+
const origin = found.ok && found.data && found.data.entry.id !== entry?.id && !otherConversation(found.data.entry, session) ? found.data : null;
|
|
17412
|
+
if (!entry && !origin)
|
|
17413
|
+
return null;
|
|
17254
17414
|
const workspace = store.readWorkspace();
|
|
17255
|
-
if (!workspace.ok || !workspace.data)
|
|
17415
|
+
if (!workspace.ok || entry && !workspace.data)
|
|
17256
17416
|
return null;
|
|
17257
17417
|
const slot = `${store.root}\x00${session.host}\x00${session.handle}`;
|
|
17258
17418
|
const key = canonicalJson({
|
|
17259
|
-
task: entry
|
|
17260
|
-
revision: entry
|
|
17261
|
-
file: entry
|
|
17262
|
-
|
|
17419
|
+
task: entry?.id ?? null,
|
|
17420
|
+
revision: entry?.revision ?? null,
|
|
17421
|
+
file: entry?.file ?? null,
|
|
17422
|
+
origin: origin && origin.entry.file,
|
|
17423
|
+
workspace: workspace.data?.revision ?? null,
|
|
17263
17424
|
caller: context.caller,
|
|
17264
17425
|
workerId: context.workerId ?? null,
|
|
17265
17426
|
capabilities: context.capabilities
|
|
@@ -17267,15 +17428,15 @@ function entryCompactContext(store, entry, session, context) {
|
|
|
17267
17428
|
const hit = cache.get(slot);
|
|
17268
17429
|
if (hit && hit.key === key && hit.filesKey !== null && hit.filesKey === filesKey(hit.files) && (hit.signal === null || hit.signal === recentSignal(store.root)))
|
|
17269
17430
|
return hit.value;
|
|
17270
|
-
const task = store.readTask(entry.id);
|
|
17271
|
-
if (!task.ok)
|
|
17431
|
+
const task = entry ? store.readTask(entry.id) : null;
|
|
17432
|
+
if (task && !task.ok)
|
|
17272
17433
|
return null;
|
|
17273
17434
|
const freshness = { mode: "signal", current: () => recentSignal(store.root) };
|
|
17274
|
-
const observed = task
|
|
17275
|
-
const files = task
|
|
17435
|
+
const observed = task?.data.evidence.some(isObservedCheck) ?? false;
|
|
17436
|
+
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path17.resolve(store.root, ref.path)] : []));
|
|
17276
17437
|
const observedFiles = filesKey(files);
|
|
17277
|
-
const compact = new WorkitCore(store, context).compactContext(entry
|
|
17278
|
-
if (!compact.ok)
|
|
17438
|
+
const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
|
|
17439
|
+
if (!compact.ok || !compact.data)
|
|
17279
17440
|
return null;
|
|
17280
17441
|
cache.delete(slot);
|
|
17281
17442
|
cache.set(slot, {
|
|
@@ -17294,8 +17455,8 @@ var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task stat
|
|
|
17294
17455
|
return sessionTaskEntry(entries, session);
|
|
17295
17456
|
const active = entries.filter((entry) => entry.status === "active");
|
|
17296
17457
|
return active.length === 1 ? active[0] : null;
|
|
17297
|
-
}, implicitTaskEntry = (store, entries, session) => {
|
|
17298
|
-
const open = entries.filter((entry) => entry.key !== null && entry.status !== "closed" && !entry
|
|
17458
|
+
}, otherConversation = (entry, session) => entry.sessions.some((item) => item.host !== "workit_cli" && !(item.host === session.host && item.handle === session.handle)), implicitTaskEntry = (store, entries, session) => {
|
|
17459
|
+
const open = entries.filter((entry) => entry.key !== null && entry.status !== "closed" && !otherConversation(entry, session));
|
|
17299
17460
|
if (open.length === 0)
|
|
17300
17461
|
return null;
|
|
17301
17462
|
const key = store.currentKey();
|
|
@@ -17346,7 +17507,7 @@ ${options.addendum}` : ""}
|
|
|
17346
17507
|
if (!listed.ok)
|
|
17347
17508
|
throw new Error(listed.error);
|
|
17348
17509
|
const entry = currentTaskEntry(listed.data, session, descriptor.context.task) ?? implicitTaskEntry(store, listed.data, session);
|
|
17349
|
-
const text =
|
|
17510
|
+
const text = entryCompactContext(store, entry, session, hookOperationContext(input, descriptor));
|
|
17350
17511
|
if (text)
|
|
17351
17512
|
compact = `
|
|
17352
17513
|
<workit-task-context>${text}</workit-task-context>`;
|
|
@@ -17370,7 +17531,7 @@ ${options.addendum}` : ""}
|
|
|
17370
17531
|
} catch {
|
|
17371
17532
|
return null;
|
|
17372
17533
|
}
|
|
17373
|
-
}, TURN_TTL_MS, digestOf = (text) =>
|
|
17534
|
+
}, TURN_TTL_MS, digestOf = (text) => createHash11("sha256").update(text).digest("hex"), turnMarker = (input) => {
|
|
17374
17535
|
if (!input.session.id)
|
|
17375
17536
|
return null;
|
|
17376
17537
|
try {
|
|
@@ -17378,29 +17539,29 @@ ${options.addendum}` : ""}
|
|
|
17378
17539
|
if (location instanceof Error)
|
|
17379
17540
|
return null;
|
|
17380
17541
|
const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
|
|
17381
|
-
return
|
|
17542
|
+
return path17.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
|
|
17382
17543
|
} catch {
|
|
17383
17544
|
return null;
|
|
17384
17545
|
}
|
|
17385
17546
|
}, writeTurnDigest = (file, digest) => {
|
|
17386
17547
|
try {
|
|
17387
|
-
|
|
17388
|
-
|
|
17548
|
+
fs14.mkdirSync(path17.dirname(file), { recursive: true });
|
|
17549
|
+
fs14.writeFileSync(file, `${JSON.stringify({ digest })}
|
|
17389
17550
|
`);
|
|
17390
17551
|
} catch {}
|
|
17391
17552
|
}, clearTurnDigest = (file) => {
|
|
17392
17553
|
try {
|
|
17393
|
-
|
|
17554
|
+
fs14.rmSync(file, { force: true });
|
|
17394
17555
|
} catch {}
|
|
17395
17556
|
}, pruneTurnDigests = (dir, now) => {
|
|
17396
17557
|
try {
|
|
17397
|
-
for (const name of
|
|
17558
|
+
for (const name of fs14.readdirSync(dir)) {
|
|
17398
17559
|
if (!name.startsWith("turn-"))
|
|
17399
17560
|
continue;
|
|
17400
|
-
const file =
|
|
17561
|
+
const file = path17.join(dir, name);
|
|
17401
17562
|
try {
|
|
17402
|
-
if (now -
|
|
17403
|
-
|
|
17563
|
+
if (now - fs14.statSync(file).mtimeMs > TURN_TTL_MS)
|
|
17564
|
+
fs14.rmSync(file, { force: true });
|
|
17404
17565
|
} catch {}
|
|
17405
17566
|
}
|
|
17406
17567
|
} catch {}
|
|
@@ -17415,7 +17576,7 @@ ${options.addendum}` : ""}
|
|
|
17415
17576
|
return text;
|
|
17416
17577
|
const digest = digestOf(text);
|
|
17417
17578
|
try {
|
|
17418
|
-
const previous = JSON.parse(
|
|
17579
|
+
const previous = JSON.parse(fs14.readFileSync(file, "utf8"));
|
|
17419
17580
|
if (previous.digest === digest)
|
|
17420
17581
|
return null;
|
|
17421
17582
|
} catch {}
|
|
@@ -17425,7 +17586,7 @@ ${options.addendum}` : ""}
|
|
|
17425
17586
|
const file = turnMarker(input);
|
|
17426
17587
|
if (!file)
|
|
17427
17588
|
return;
|
|
17428
|
-
pruneTurnDigests(
|
|
17589
|
+
pruneTurnDigests(path17.dirname(file), Date.now());
|
|
17429
17590
|
if (text)
|
|
17430
17591
|
writeTurnDigest(file, digestOf(text));
|
|
17431
17592
|
else
|
|
@@ -17451,7 +17612,7 @@ var init_commit_flavors = () => {};
|
|
|
17451
17612
|
var init_git = () => {};
|
|
17452
17613
|
|
|
17453
17614
|
// packages/workit-core/src/core/branch.ts
|
|
17454
|
-
import { createHash as
|
|
17615
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
17455
17616
|
function effectiveWorkspacePolicy(workspaceRoot, kind, profileName) {
|
|
17456
17617
|
const selected = resolveRuntimeWorkspacePolicy(workspaceRoot, kind, profileName?.trim() || process.env.WORKFLOW_PROFILE?.trim() || undefined);
|
|
17457
17618
|
const config = readConfig();
|
|
@@ -17517,7 +17678,7 @@ var withReleaseTracks = (workspaceRoot, policy) => {
|
|
|
17517
17678
|
const source = resolved.provenance.branchPolicy;
|
|
17518
17679
|
const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
|
|
17519
17680
|
const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
|
|
17520
|
-
const fingerprint =
|
|
17681
|
+
const fingerprint = createHash12("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
|
|
17521
17682
|
return {
|
|
17522
17683
|
branchPolicy,
|
|
17523
17684
|
namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
|
|
@@ -17700,7 +17861,7 @@ var closingQuote = (command, start, dialect) => {
|
|
|
17700
17861
|
|
|
17701
17862
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
17702
17863
|
import { realpathSync as realpathSync5 } from "node:fs";
|
|
17703
|
-
import
|
|
17864
|
+
import path18 from "node:path";
|
|
17704
17865
|
function writeGate(cwd, paths) {
|
|
17705
17866
|
try {
|
|
17706
17867
|
const gate = gateFor(cwd);
|
|
@@ -17720,21 +17881,21 @@ function writeGate(cwd, paths) {
|
|
|
17720
17881
|
}
|
|
17721
17882
|
var NONE2, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
17722
17883
|
let rest = "";
|
|
17723
|
-
for (let dir =
|
|
17884
|
+
for (let dir = path18.resolve(file);; dir = path18.dirname(dir)) {
|
|
17724
17885
|
try {
|
|
17725
|
-
return
|
|
17886
|
+
return path18.join(realpathSync5(dir), rest);
|
|
17726
17887
|
} catch {
|
|
17727
|
-
if (
|
|
17728
|
-
return
|
|
17729
|
-
rest = rest ?
|
|
17888
|
+
if (path18.dirname(dir) === dir)
|
|
17889
|
+
return path18.resolve(file);
|
|
17890
|
+
rest = rest ? path18.join(path18.basename(dir), rest) : path18.basename(dir);
|
|
17730
17891
|
}
|
|
17731
17892
|
}
|
|
17732
17893
|
}, exempt = (cwd, root, file, plans) => {
|
|
17733
17894
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
17734
|
-
const relative =
|
|
17735
|
-
if (relative.startsWith("..") ||
|
|
17895
|
+
const relative = path18.relative(fold(canonical2(root)), fold(canonical2(path18.resolve(cwd, file))));
|
|
17896
|
+
if (relative.startsWith("..") || path18.isAbsolute(relative))
|
|
17736
17897
|
return true;
|
|
17737
|
-
const posix = relative.split(
|
|
17898
|
+
const posix = relative.split(path18.sep).join("/");
|
|
17738
17899
|
const segments = posix.split("/");
|
|
17739
17900
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
17740
17901
|
}, PREFIXES2, operands = (words) => words.filter((item) => !item.startsWith("-")), editsOf = (input) => {
|
|
@@ -17826,7 +17987,7 @@ var NONE2, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
17826
17987
|
if (!unknown && targets.length === 0)
|
|
17827
17988
|
return { writes: false, targets: null };
|
|
17828
17989
|
return { writes: true, targets: unknown ? null : targets };
|
|
17829
|
-
}, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
17990
|
+
}, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path18.resolve(root, file)) ?? "-").join("\x00"), gateFor = (cwd) => {
|
|
17830
17991
|
const store = new TaskStore(cwd);
|
|
17831
17992
|
const listed = store.listTaskIndex();
|
|
17832
17993
|
if (!listed.ok)
|
|
@@ -17875,9 +18036,9 @@ var init_write_gate = __esm(() => {
|
|
|
17875
18036
|
});
|
|
17876
18037
|
|
|
17877
18038
|
// packages/workit-core/src/hooks/session-marker.ts
|
|
17878
|
-
import { createHash as
|
|
17879
|
-
import
|
|
17880
|
-
import
|
|
18039
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
18040
|
+
import fs15 from "node:fs";
|
|
18041
|
+
import path19 from "node:path";
|
|
17881
18042
|
var sessionMarkerFile = (input, session, store) => {
|
|
17882
18043
|
let dir = store;
|
|
17883
18044
|
if (dir === undefined) {
|
|
@@ -17886,19 +18047,19 @@ var sessionMarkerFile = (input, session, store) => {
|
|
|
17886
18047
|
return null;
|
|
17887
18048
|
dir = location.dir;
|
|
17888
18049
|
}
|
|
17889
|
-
const name =
|
|
17890
|
-
return
|
|
18050
|
+
const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
18051
|
+
return path19.join(dir, "hooks", `skills-${name}.json`);
|
|
17891
18052
|
}, list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [], readSessionMarker = (file) => {
|
|
17892
18053
|
try {
|
|
17893
|
-
const value = JSON.parse(
|
|
18054
|
+
const value = JSON.parse(fs15.readFileSync(file, "utf8"));
|
|
17894
18055
|
return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
|
|
17895
18056
|
} catch {
|
|
17896
18057
|
return { nudged: [], loaded: [], raw: [] };
|
|
17897
18058
|
}
|
|
17898
18059
|
}, writeSessionMarker = (file, marker) => {
|
|
17899
18060
|
try {
|
|
17900
|
-
|
|
17901
|
-
|
|
18061
|
+
fs15.mkdirSync(path19.dirname(file), { recursive: true });
|
|
18062
|
+
fs15.writeFileSync(file, JSON.stringify(marker));
|
|
17902
18063
|
} catch {}
|
|
17903
18064
|
};
|
|
17904
18065
|
var init_session_marker = __esm(() => {
|
|
@@ -17907,10 +18068,10 @@ var init_session_marker = __esm(() => {
|
|
|
17907
18068
|
|
|
17908
18069
|
// packages/workit-core/src/hooks/raw-git.ts
|
|
17909
18070
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
17910
|
-
import { createHash as
|
|
17911
|
-
import
|
|
18071
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
18072
|
+
import fs16 from "node:fs";
|
|
17912
18073
|
import os4 from "node:os";
|
|
17913
|
-
import
|
|
18074
|
+
import path20 from "node:path";
|
|
17914
18075
|
function rawInvocations(command, dialect = "posix", depth = 0) {
|
|
17915
18076
|
const found = [];
|
|
17916
18077
|
let dir = null;
|
|
@@ -18058,8 +18219,8 @@ function noteRawCommit(input, command, key) {
|
|
|
18058
18219
|
if (action.kind !== "commit")
|
|
18059
18220
|
continue;
|
|
18060
18221
|
const file = markerFile(repo, input.session.id, key);
|
|
18061
|
-
|
|
18062
|
-
|
|
18222
|
+
fs16.mkdirSync(path20.dirname(file), { recursive: true });
|
|
18223
|
+
fs16.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
18063
18224
|
`);
|
|
18064
18225
|
}
|
|
18065
18226
|
} catch {}
|
|
@@ -18081,15 +18242,15 @@ function settlePendingCommit(input) {
|
|
|
18081
18242
|
if (!repo)
|
|
18082
18243
|
return;
|
|
18083
18244
|
const file = markerFile(repo, input.session.id, NEXT_COMMAND);
|
|
18084
|
-
if (
|
|
18245
|
+
if (fs16.existsSync(file))
|
|
18085
18246
|
settle2(input, repo, file);
|
|
18086
18247
|
} catch {}
|
|
18087
18248
|
}
|
|
18088
|
-
var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, isAbsolute = (value) =>
|
|
18249
|
+
var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, isAbsolute = (value) => path20.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\"), joinDir = (base, next) => {
|
|
18089
18250
|
if (base === undefined || /[$`*?]/.test(next) || next === "-")
|
|
18090
18251
|
return;
|
|
18091
|
-
const expanded = next === "~" || next.startsWith("~/") ?
|
|
18092
|
-
return base === null || isAbsolute(expanded) ? expanded :
|
|
18252
|
+
const expanded = next === "~" || next.startsWith("~/") ? path20.join(os4.homedir(), next.slice(1)) : next;
|
|
18253
|
+
return base === null || isAbsolute(expanded) ? expanded : path20.join(base, expanded);
|
|
18093
18254
|
}, cdTarget = (words) => {
|
|
18094
18255
|
const args = words.slice(1);
|
|
18095
18256
|
const flag = args.findIndex((word) => /^-(?:literal)?path$/i.test(word));
|
|
@@ -18187,7 +18348,7 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
|
|
|
18187
18348
|
return null;
|
|
18188
18349
|
return { location, dir, branch: resolveTaskKey(dir, location).branch };
|
|
18189
18350
|
}, isWorkitWorkspace = (repo) => {
|
|
18190
|
-
if (
|
|
18351
|
+
if (fs16.existsSync(repo.location.dir))
|
|
18191
18352
|
return true;
|
|
18192
18353
|
try {
|
|
18193
18354
|
return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
|
|
@@ -18215,17 +18376,17 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
|
|
|
18215
18376
|
if (!git || git.reftable)
|
|
18216
18377
|
return null;
|
|
18217
18378
|
try {
|
|
18218
|
-
const head =
|
|
18379
|
+
const head = fs16.readFileSync(path20.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
18219
18380
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
18220
18381
|
return head;
|
|
18221
18382
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
18222
18383
|
if (!ref)
|
|
18223
18384
|
return null;
|
|
18224
|
-
const common =
|
|
18385
|
+
const common = path20.dirname(repo.location.dir);
|
|
18225
18386
|
try {
|
|
18226
|
-
return
|
|
18387
|
+
return fs16.readFileSync(path20.join(common, ref), "utf8").trim() || null;
|
|
18227
18388
|
} catch {}
|
|
18228
|
-
const packed =
|
|
18389
|
+
const packed = fs16.readFileSync(path20.join(common, "packed-refs"), "utf8");
|
|
18229
18390
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
18230
18391
|
} catch {
|
|
18231
18392
|
return null;
|
|
@@ -18258,7 +18419,7 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
|
|
|
18258
18419
|
const action = rawAction(invocation);
|
|
18259
18420
|
if (!action)
|
|
18260
18421
|
continue;
|
|
18261
|
-
const dir =
|
|
18422
|
+
const dir = path20.resolve(cwd, invocation.dir ?? ".");
|
|
18262
18423
|
if (!repos.has(dir)) {
|
|
18263
18424
|
const repo = repoAt(dir);
|
|
18264
18425
|
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
@@ -18285,7 +18446,7 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
|
|
|
18285
18446
|
raw: [...marker.raw, ...fresh.map((kind) => agent + kind)]
|
|
18286
18447
|
});
|
|
18287
18448
|
return fresh.map((kind) => nudges.get(kind));
|
|
18288
|
-
}, deny = (reason) => ({ kind: "deny", reason, unblock: null }), NEXT_COMMAND = "next", MARKER_TTL_SECONDS = 3600, markerFile = (repo, session, key) =>
|
|
18449
|
+
}, deny = (reason) => ({ kind: "deny", reason, unblock: null }), NEXT_COMMAND = "next", MARKER_TTL_SECONDS = 3600, markerFile = (repo, session, key) => path20.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`), callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`, headCommit = (dir) => {
|
|
18289
18450
|
const run = spawnSync11("git", [
|
|
18290
18451
|
"log",
|
|
18291
18452
|
"-1",
|
|
@@ -18302,11 +18463,11 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
|
|
|
18302
18463
|
}, settle2 = (input, repo, file) => {
|
|
18303
18464
|
let marker;
|
|
18304
18465
|
try {
|
|
18305
|
-
marker = JSON.parse(
|
|
18466
|
+
marker = JSON.parse(fs16.readFileSync(file, "utf8"));
|
|
18306
18467
|
} catch {
|
|
18307
18468
|
return;
|
|
18308
18469
|
}
|
|
18309
|
-
|
|
18470
|
+
fs16.rmSync(file, { force: true });
|
|
18310
18471
|
const session = input.session.id;
|
|
18311
18472
|
const now = Date.now() / 1000;
|
|
18312
18473
|
if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
|
|
@@ -18781,9 +18942,9 @@ var init_skill_nudge = __esm(() => {
|
|
|
18781
18942
|
|
|
18782
18943
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
18783
18944
|
import { existsSync as existsSync6, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
18784
|
-
import
|
|
18945
|
+
import path21 from "node:path";
|
|
18785
18946
|
var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value), nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "", optionalText = (value) => typeof value === "string" ? value : null, existingDirectory = (value) => {
|
|
18786
|
-
if (!nonEmpty2(value) || !
|
|
18947
|
+
if (!nonEmpty2(value) || !path21.isAbsolute(value) || !existsSync6(value))
|
|
18787
18948
|
return null;
|
|
18788
18949
|
try {
|
|
18789
18950
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -18944,6 +19105,8 @@ ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
|
|
|
18944
19105
|
if (role !== "judge" || !event.agentId)
|
|
18945
19106
|
return readOnly;
|
|
18946
19107
|
const own = subagentSession(session, event.agentId);
|
|
19108
|
+
if (session)
|
|
19109
|
+
recordIssuedSession(input.cwd || process.cwd(), session, own);
|
|
18947
19110
|
return `${readOnly} Its own Workit session is ${own}: record verdicts with \`workit ledger verdict <result> --session ${own} ...\` so the ledger tells it apart from the author.`;
|
|
18948
19111
|
}, failureDecision = (descriptor, event, error) => {
|
|
18949
19112
|
if (event === "shell.pre" || event === "tool.pre" || event === "write.pre")
|
|
@@ -18975,6 +19138,7 @@ ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
|
|
|
18975
19138
|
};
|
|
18976
19139
|
var init_handle = __esm(() => {
|
|
18977
19140
|
init_context();
|
|
19141
|
+
init_host_session();
|
|
18978
19142
|
init_policy();
|
|
18979
19143
|
init_raw_git();
|
|
18980
19144
|
init_skill_nudge();
|
|
@@ -19512,13 +19676,13 @@ var init_codex = __esm(() => {
|
|
|
19512
19676
|
};
|
|
19513
19677
|
},
|
|
19514
19678
|
render: render2,
|
|
19515
|
-
addendum: (input) => `<workit-codex-mutations>Codex MCP is read-only: unattested callers cannot mutate. Run workit verbs with the workit CLI on the shell (node_modules/.bin/workit, or npx -y @brainervirus/workit-cli): check, git branch|commit|push, pr, ci, stack, ledger, handoff; task-family mutations take --json${input.session.id ? ` --actor ${input.session.id}` : ""}. workit acts as this thread (CODEX_THREAD_ID${input.session.id ? `=${input.session.id}` : ""}) unless WORKIT_SESSION_ID is set. A verifier or reviewer records workit ledger verdict under its own session, never the author's: a workit-verifier or workit-reviewer agent uses the --session its SubagentStart context names; any other
|
|
19679
|
+
addendum: (input) => `<workit-codex-mutations>Codex MCP is read-only: unattested callers cannot mutate. Run workit verbs with the workit CLI on the shell (node_modules/.bin/workit, or npx -y @brainervirus/workit-cli): check, git branch|commit|push, pr, ci, stack, ledger, handoff; task-family mutations take --json${input.session.id ? ` --actor ${input.session.id}` : ""}. workit acts as this thread (CODEX_THREAD_ID${input.session.id ? `=${input.session.id}` : ""}) unless WORKIT_SESSION_ID is set. A verifier or reviewer records workit ledger verdict under its own session, never the author's: a workit-verifier or workit-reviewer agent uses the --session its SubagentStart context names; any other passes --as verifier (an id derived from its own thread). A hand-minted id such as <lead>-v1 is refused as forged_session. Merge and release need a workspace grant (workit grant show); a question answer is not host permission.</workit-codex-mutations>`
|
|
19516
19680
|
};
|
|
19517
19681
|
});
|
|
19518
19682
|
|
|
19519
19683
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
19520
|
-
import
|
|
19521
|
-
import
|
|
19684
|
+
import fs17 from "node:fs";
|
|
19685
|
+
import path22 from "node:path";
|
|
19522
19686
|
var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (value) => {
|
|
19523
19687
|
if (!isRecord4(value) || !Object.hasOwn(EVENTS3, String(value.hook_event_name)))
|
|
19524
19688
|
return { ok: false, error: "hook_event_name is required" };
|
|
@@ -19599,17 +19763,17 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
|
|
|
19599
19763
|
};
|
|
19600
19764
|
}
|
|
19601
19765
|
}, TRANSCRIPT_TAIL_BYTES, lastAssistantText = (file) => {
|
|
19602
|
-
if (!file || !
|
|
19766
|
+
if (!file || !path22.isAbsolute(file))
|
|
19603
19767
|
return null;
|
|
19604
19768
|
try {
|
|
19605
|
-
const size =
|
|
19769
|
+
const size = fs17.statSync(file).size;
|
|
19606
19770
|
const start = Math.max(0, size - TRANSCRIPT_TAIL_BYTES);
|
|
19607
19771
|
const buffer = Buffer.alloc(size - start);
|
|
19608
|
-
const fd =
|
|
19772
|
+
const fd = fs17.openSync(file, "r");
|
|
19609
19773
|
try {
|
|
19610
|
-
|
|
19774
|
+
fs17.readSync(fd, buffer, 0, buffer.length, start);
|
|
19611
19775
|
} finally {
|
|
19612
|
-
|
|
19776
|
+
fs17.closeSync(fd);
|
|
19613
19777
|
}
|
|
19614
19778
|
const lines = buffer.toString("utf8").split(`
|
|
19615
19779
|
`);
|
|
@@ -19635,7 +19799,7 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
|
|
|
19635
19799
|
} catch {
|
|
19636
19800
|
return null;
|
|
19637
19801
|
}
|
|
19638
|
-
}, within = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
19802
|
+
}, within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path22.sep) ? root : `${root}${path22.sep}`), hookCwd = (input) => {
|
|
19639
19803
|
const cwd = existingDirectory(input.cwd);
|
|
19640
19804
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|
|
19641
19805
|
return cwd;
|