@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
package/dist/workit-hook.js
CHANGED
|
@@ -8377,7 +8377,7 @@ import { realpathSync as realpathSync4 } from "node:fs";
|
|
|
8377
8377
|
|
|
8378
8378
|
// packages/workit-core/src/core/policy/derive.ts
|
|
8379
8379
|
import { existsSync as existsSync2 } from "node:fs";
|
|
8380
|
-
import
|
|
8380
|
+
import path8 from "node:path";
|
|
8381
8381
|
|
|
8382
8382
|
// packages/workit-core/src/git/rev.ts
|
|
8383
8383
|
import { spawnSync } from "node:child_process";
|
|
@@ -8562,9 +8562,9 @@ function pushRemoteName(cwd, branch) {
|
|
|
8562
8562
|
|
|
8563
8563
|
// packages/workit-core/src/ledger.ts
|
|
8564
8564
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
8565
|
-
import { createHash as
|
|
8566
|
-
import
|
|
8567
|
-
import
|
|
8565
|
+
import { createHash as createHash7, randomBytes } from "node:crypto";
|
|
8566
|
+
import fs5 from "node:fs";
|
|
8567
|
+
import path7 from "node:path";
|
|
8568
8568
|
|
|
8569
8569
|
// packages/workit-core/src/core/vcs-config.ts
|
|
8570
8570
|
import fs2 from "node:fs";
|
|
@@ -9799,33 +9799,9 @@ function vcsConfig(mode, cwd, options = {}) {
|
|
|
9799
9799
|
}
|
|
9800
9800
|
|
|
9801
9801
|
// packages/workit-core/src/host-session.ts
|
|
9802
|
-
|
|
9803
|
-
|
|
9804
|
-
|
|
9805
|
-
[
|
|
9806
|
-
"CODEX_THREAD_ID",
|
|
9807
|
-
(env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
|
|
9808
|
-
]
|
|
9809
|
-
];
|
|
9810
|
-
var family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null;
|
|
9811
|
-
function hostSessionFromEnv(env) {
|
|
9812
|
-
const value = (key) => env[key]?.trim() || null;
|
|
9813
|
-
let inner = null;
|
|
9814
|
-
for (const [key, host] of HOST_SESSION_VARS) {
|
|
9815
|
-
const session = value(key);
|
|
9816
|
-
if (session) {
|
|
9817
|
-
inner = { host: host(env), session };
|
|
9818
|
-
break;
|
|
9819
|
-
}
|
|
9820
|
-
}
|
|
9821
|
-
const declared = value("WORKIT_HOST");
|
|
9822
|
-
if (env.WORKIT_SESSION_ID !== undefined) {
|
|
9823
|
-
const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
|
|
9824
|
-
if (!inherited)
|
|
9825
|
-
return { host: declared, session: value("WORKIT_SESSION_ID") };
|
|
9826
|
-
}
|
|
9827
|
-
return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
|
|
9828
|
-
}
|
|
9802
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
9803
|
+
import fs4 from "node:fs";
|
|
9804
|
+
import path6 from "node:path";
|
|
9829
9805
|
|
|
9830
9806
|
// packages/workit-core/src/store/paths.ts
|
|
9831
9807
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
@@ -10107,6 +10083,63 @@ var checkoutSlug = (root) => {
|
|
|
10107
10083
|
return `${base}-${shortHash(canonical)}`;
|
|
10108
10084
|
};
|
|
10109
10085
|
|
|
10086
|
+
// packages/workit-core/src/host-session.ts
|
|
10087
|
+
var HOST_SESSION_VARS = [
|
|
10088
|
+
["OPENCODE_SESSION_ID", () => "opencode"],
|
|
10089
|
+
["PI_SESSION_ID", () => "pi"],
|
|
10090
|
+
[
|
|
10091
|
+
"CODEX_THREAD_ID",
|
|
10092
|
+
(env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
|
|
10093
|
+
]
|
|
10094
|
+
];
|
|
10095
|
+
var family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null;
|
|
10096
|
+
function hostSessionFromEnv(env) {
|
|
10097
|
+
const value = (key) => env[key]?.trim() || null;
|
|
10098
|
+
let inner = null;
|
|
10099
|
+
for (const [key, host] of HOST_SESSION_VARS) {
|
|
10100
|
+
const session = value(key);
|
|
10101
|
+
if (session) {
|
|
10102
|
+
inner = { host: host(env), session };
|
|
10103
|
+
break;
|
|
10104
|
+
}
|
|
10105
|
+
}
|
|
10106
|
+
const declared = value("WORKIT_HOST");
|
|
10107
|
+
if (env.WORKIT_SESSION_ID !== undefined) {
|
|
10108
|
+
const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
|
|
10109
|
+
if (!inherited)
|
|
10110
|
+
return { host: declared, session: value("WORKIT_SESSION_ID") };
|
|
10111
|
+
}
|
|
10112
|
+
return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
|
|
10113
|
+
}
|
|
10114
|
+
var ISSUED_PER_LEAD = 64;
|
|
10115
|
+
var ISSUED_SESSIONS_MARKER = "workit-issued-sessions/1";
|
|
10116
|
+
var hash = (value) => createHash6("sha256").update(value).digest("hex").slice(0, 32);
|
|
10117
|
+
var issuedRoot = (cwd) => {
|
|
10118
|
+
const location = resolveStore(cwd);
|
|
10119
|
+
return location instanceof Error ? null : path6.join(location.dir, "hooks", "issued");
|
|
10120
|
+
};
|
|
10121
|
+
var issuedDir = (cwd, lead) => {
|
|
10122
|
+
const root = issuedRoot(cwd);
|
|
10123
|
+
return root === null ? null : path6.join(root, hash(lead));
|
|
10124
|
+
};
|
|
10125
|
+
function recordIssuedSession(cwd, lead, issued) {
|
|
10126
|
+
try {
|
|
10127
|
+
const dir = issuedDir(cwd, lead);
|
|
10128
|
+
if (!dir)
|
|
10129
|
+
return;
|
|
10130
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
10131
|
+
const own = hash(issued);
|
|
10132
|
+
fs4.writeFileSync(path6.join(dir, own), ISSUED_SESSIONS_MARKER);
|
|
10133
|
+
const entries = fs4.readdirSync(dir);
|
|
10134
|
+
if (entries.length <= ISSUED_PER_LEAD)
|
|
10135
|
+
return;
|
|
10136
|
+
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);
|
|
10137
|
+
for (const { name } of byAge.slice(0, entries.length - ISSUED_PER_LEAD))
|
|
10138
|
+
fs4.rmSync(path6.join(dir, name), { force: true });
|
|
10139
|
+
} catch {}
|
|
10140
|
+
}
|
|
10141
|
+
var OWN_CHILD_SHELL = new Set(["opencode", "pi"]);
|
|
10142
|
+
|
|
10110
10143
|
// packages/workit-core/src/ledger.ts
|
|
10111
10144
|
var LEDGER_VERSION = 1;
|
|
10112
10145
|
var MAX_LINE_BYTES = 4096;
|
|
@@ -10212,7 +10245,7 @@ function diffHash(cwd, base, head) {
|
|
|
10212
10245
|
], GIT_TIMEOUTS.worktree);
|
|
10213
10246
|
if (!diff.ok || !diff.stdout)
|
|
10214
10247
|
return null;
|
|
10215
|
-
const hash =
|
|
10248
|
+
const hash = createHash7("sha256");
|
|
10216
10249
|
for (const line of diff.stdout.split(`
|
|
10217
10250
|
`)) {
|
|
10218
10251
|
if (line.startsWith("index "))
|
|
@@ -10224,14 +10257,14 @@ function diffHash(cwd, base, head) {
|
|
|
10224
10257
|
return `sha256:${hash.digest("hex")}`;
|
|
10225
10258
|
}
|
|
10226
10259
|
function storeRoot(cwd) {
|
|
10227
|
-
const location = resolveStore(
|
|
10260
|
+
const location = resolveStore(path7.resolve(cwd));
|
|
10228
10261
|
if (location instanceof Error)
|
|
10229
10262
|
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");
|
|
10230
10263
|
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
10231
10264
|
}
|
|
10232
10265
|
function ledgerPath(cwd) {
|
|
10233
10266
|
const root = storeRoot(cwd);
|
|
10234
|
-
return root.ok ? { ok: true, value:
|
|
10267
|
+
return root.ok ? { ok: true, value: path7.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
10235
10268
|
}
|
|
10236
10269
|
var newId2 = () => `${Date.now().toString(36)}-${randomBytes(6).toString("hex")}`;
|
|
10237
10270
|
var NETWORK_FS_TYPES = new Set([
|
|
@@ -10251,7 +10284,7 @@ var onNetworkFs = (dir) => {
|
|
|
10251
10284
|
if (process.platform !== "linux")
|
|
10252
10285
|
return false;
|
|
10253
10286
|
try {
|
|
10254
|
-
return isNetworkFsType(
|
|
10287
|
+
return isNetworkFsType(fs5.statfsSync(dir).type);
|
|
10255
10288
|
} catch {
|
|
10256
10289
|
return false;
|
|
10257
10290
|
}
|
|
@@ -10261,15 +10294,15 @@ var LOCK_STALE_MS = 30000;
|
|
|
10261
10294
|
var LOCK_OWNER = "owner";
|
|
10262
10295
|
var lockToken = (lock) => {
|
|
10263
10296
|
try {
|
|
10264
|
-
return
|
|
10297
|
+
return fs5.readFileSync(path7.join(lock, LOCK_OWNER), "utf8");
|
|
10265
10298
|
} catch {
|
|
10266
10299
|
return null;
|
|
10267
10300
|
}
|
|
10268
10301
|
};
|
|
10269
10302
|
var lockAgeMs = (lock) => {
|
|
10270
|
-
for (const target of [
|
|
10303
|
+
for (const target of [path7.join(lock, LOCK_OWNER), lock]) {
|
|
10271
10304
|
try {
|
|
10272
|
-
return Date.now() -
|
|
10305
|
+
return Date.now() - fs5.statSync(target).mtimeMs;
|
|
10273
10306
|
} catch {}
|
|
10274
10307
|
}
|
|
10275
10308
|
return null;
|
|
@@ -10277,14 +10310,14 @@ var lockAgeMs = (lock) => {
|
|
|
10277
10310
|
var ledgerLock = {
|
|
10278
10311
|
acquire(lock) {
|
|
10279
10312
|
try {
|
|
10280
|
-
|
|
10313
|
+
fs5.mkdirSync(lock);
|
|
10281
10314
|
} catch (error) {
|
|
10282
10315
|
if (error.code === "EEXIST")
|
|
10283
10316
|
return null;
|
|
10284
10317
|
throw error;
|
|
10285
10318
|
}
|
|
10286
10319
|
const token = `${process.pid}-${randomBytes(8).toString("hex")}`;
|
|
10287
|
-
|
|
10320
|
+
fs5.writeFileSync(path7.join(lock, LOCK_OWNER), token);
|
|
10288
10321
|
return token;
|
|
10289
10322
|
},
|
|
10290
10323
|
release(lock, token) {
|
|
@@ -10292,35 +10325,35 @@ var ledgerLock = {
|
|
|
10292
10325
|
return false;
|
|
10293
10326
|
const tomb = `${lock}.release-${randomBytes(6).toString("hex")}`;
|
|
10294
10327
|
try {
|
|
10295
|
-
|
|
10328
|
+
fs5.renameSync(lock, tomb);
|
|
10296
10329
|
} catch {
|
|
10297
10330
|
return false;
|
|
10298
10331
|
}
|
|
10299
10332
|
if (lockToken(tomb) !== token) {
|
|
10300
10333
|
try {
|
|
10301
|
-
|
|
10334
|
+
fs5.renameSync(tomb, lock);
|
|
10302
10335
|
} catch {}
|
|
10303
10336
|
return false;
|
|
10304
10337
|
}
|
|
10305
|
-
|
|
10338
|
+
fs5.rmSync(tomb, { recursive: true, force: true });
|
|
10306
10339
|
return true;
|
|
10307
10340
|
},
|
|
10308
10341
|
token: lockToken,
|
|
10309
10342
|
reap(lock, observed) {
|
|
10310
10343
|
const tomb = `${lock}.reap-${randomBytes(6).toString("hex")}`;
|
|
10311
10344
|
try {
|
|
10312
|
-
|
|
10345
|
+
fs5.renameSync(lock, tomb);
|
|
10313
10346
|
} catch {
|
|
10314
10347
|
return false;
|
|
10315
10348
|
}
|
|
10316
10349
|
if (lockToken(tomb) === observed) {
|
|
10317
|
-
|
|
10350
|
+
fs5.rmSync(tomb, { recursive: true, force: true });
|
|
10318
10351
|
return true;
|
|
10319
10352
|
}
|
|
10320
10353
|
try {
|
|
10321
|
-
|
|
10354
|
+
fs5.renameSync(tomb, lock);
|
|
10322
10355
|
} catch {
|
|
10323
|
-
|
|
10356
|
+
fs5.rmSync(tomb, { recursive: true, force: true });
|
|
10324
10357
|
}
|
|
10325
10358
|
return false;
|
|
10326
10359
|
}
|
|
@@ -10347,14 +10380,14 @@ function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
|
10347
10380
|
ledgerLock.release(lock, token);
|
|
10348
10381
|
}
|
|
10349
10382
|
}
|
|
10350
|
-
var sha2562 = (text) =>
|
|
10383
|
+
var sha2562 = (text) => createHash7("sha256").update(text).digest("hex");
|
|
10351
10384
|
var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
|
|
10352
10385
|
${body}`);
|
|
10353
10386
|
var TAIL_BYTES = 64 * 1024;
|
|
10354
10387
|
var lastLine = (fd, size) => {
|
|
10355
10388
|
for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
|
|
10356
10389
|
const buffer = Buffer.alloc(span);
|
|
10357
|
-
|
|
10390
|
+
fs5.readSync(fd, buffer, 0, span, size - span);
|
|
10358
10391
|
const end = buffer.lastIndexOf(10);
|
|
10359
10392
|
if (end < 0) {
|
|
10360
10393
|
if (span === size)
|
|
@@ -10385,13 +10418,13 @@ var lastLine = (fd, size) => {
|
|
|
10385
10418
|
};
|
|
10386
10419
|
function appendLine(file, build, options) {
|
|
10387
10420
|
const write = () => {
|
|
10388
|
-
const fd =
|
|
10421
|
+
const fd = fs5.openSync(file, "a+");
|
|
10389
10422
|
try {
|
|
10390
|
-
const size =
|
|
10423
|
+
const size = fs5.fstatSync(fd).size;
|
|
10391
10424
|
let fence = "";
|
|
10392
10425
|
if (size > 0) {
|
|
10393
10426
|
const last = Buffer.alloc(1);
|
|
10394
|
-
|
|
10427
|
+
fs5.readSync(fd, last, 0, 1, size - 1);
|
|
10395
10428
|
if (last[0] !== 10)
|
|
10396
10429
|
fence = `
|
|
10397
10430
|
`;
|
|
@@ -10400,17 +10433,17 @@ function appendLine(file, build, options) {
|
|
|
10400
10433
|
if (!line.ok)
|
|
10401
10434
|
return line;
|
|
10402
10435
|
const bytes = Buffer.from(fence + line.value);
|
|
10403
|
-
const written =
|
|
10436
|
+
const written = fs5.writeSync(fd, bytes);
|
|
10404
10437
|
if (written !== bytes.length)
|
|
10405
10438
|
return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
|
|
10406
10439
|
return { ok: true, value: undefined };
|
|
10407
10440
|
} finally {
|
|
10408
|
-
|
|
10441
|
+
fs5.closeSync(fd);
|
|
10409
10442
|
}
|
|
10410
10443
|
};
|
|
10411
10444
|
try {
|
|
10412
|
-
|
|
10413
|
-
if (!options.forceLock && !onNetworkFs(
|
|
10445
|
+
fs5.mkdirSync(path7.dirname(file), { recursive: true });
|
|
10446
|
+
if (!options.forceLock && !onNetworkFs(path7.dirname(file)))
|
|
10414
10447
|
return write();
|
|
10415
10448
|
const locked = withLedgerLock(file, write);
|
|
10416
10449
|
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
@@ -10482,7 +10515,7 @@ function readLedger(cwd, options = {}) {
|
|
|
10482
10515
|
return file;
|
|
10483
10516
|
let text = "";
|
|
10484
10517
|
try {
|
|
10485
|
-
text =
|
|
10518
|
+
text = fs5.readFileSync(file.value, "utf8");
|
|
10486
10519
|
} catch (error) {
|
|
10487
10520
|
if (error.code === "ENOENT")
|
|
10488
10521
|
return {
|
|
@@ -10676,11 +10709,20 @@ function authorSessions(cwd, rows, branch, range = { base: null, head: null }, s
|
|
|
10676
10709
|
sessions.add(session);
|
|
10677
10710
|
return sessions;
|
|
10678
10711
|
}
|
|
10712
|
+
var derivesFrom = (derived, bare) => !bare.includes(":") && derived.endsWith(`:${bare}`);
|
|
10713
|
+
var authoredBy = (authors, session) => {
|
|
10714
|
+
if (authors.has(session))
|
|
10715
|
+
return true;
|
|
10716
|
+
for (const author of authors)
|
|
10717
|
+
if (derivesFrom(author, session) || derivesFrom(session, author))
|
|
10718
|
+
return true;
|
|
10719
|
+
return false;
|
|
10720
|
+
};
|
|
10679
10721
|
var independenceReasons = (row, authors) => {
|
|
10680
10722
|
const reasons = [];
|
|
10681
10723
|
if (!row.actor.session)
|
|
10682
10724
|
reasons.push("no_session");
|
|
10683
|
-
else if (authors
|
|
10725
|
+
else if (authoredBy(authors, row.actor.session))
|
|
10684
10726
|
reasons.push("author_session");
|
|
10685
10727
|
if (row.self === true && !reasons.includes("no_session"))
|
|
10686
10728
|
reasons.push("self");
|
|
@@ -10964,7 +11006,7 @@ var productDecision = (task, ledger) => {
|
|
|
10964
11006
|
};
|
|
10965
11007
|
};
|
|
10966
11008
|
var planRecorded = (task, root) => {
|
|
10967
|
-
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync2(
|
|
11009
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync2(path8.resolve(root, ref.path)));
|
|
10968
11010
|
if (cited)
|
|
10969
11011
|
return {
|
|
10970
11012
|
met: true,
|
|
@@ -11580,7 +11622,7 @@ var judgeInputSummary = (raw) => {
|
|
|
11580
11622
|
|
|
11581
11623
|
// packages/workit-core/src/autonomy.ts
|
|
11582
11624
|
import os3 from "node:os";
|
|
11583
|
-
import
|
|
11625
|
+
import path9 from "node:path";
|
|
11584
11626
|
var accountHome = () => {
|
|
11585
11627
|
try {
|
|
11586
11628
|
return os3.userInfo().homedir;
|
|
@@ -11589,10 +11631,10 @@ var accountHome = () => {
|
|
|
11589
11631
|
}
|
|
11590
11632
|
};
|
|
11591
11633
|
var grantsHome = { resolve: accountHome };
|
|
11592
|
-
var grantsDir = () =>
|
|
11634
|
+
var grantsDir = () => path9.join(grantsHome.resolve(), ".config", "workit");
|
|
11593
11635
|
var grantsOverride = () => {
|
|
11594
|
-
const configured =
|
|
11595
|
-
return configured ===
|
|
11636
|
+
const configured = path9.resolve(resolveConfigDir());
|
|
11637
|
+
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`;
|
|
11596
11638
|
};
|
|
11597
11639
|
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
11598
11640
|
var DEFAULT_GRANTS = Object.freeze({
|
|
@@ -11716,9 +11758,9 @@ var suggested = (kind) => kind === "merge" ? "verified" : "true";
|
|
|
11716
11758
|
var grantHint = (workspace, kind) => `ask the user to run, in their own terminal: workit grant set ${workspace ?? "<workspace>"} ${kind}=${suggested(kind)}`;
|
|
11717
11759
|
|
|
11718
11760
|
// packages/workit-core/src/core/task-store.ts
|
|
11719
|
-
import { createHash as
|
|
11720
|
-
import * as
|
|
11721
|
-
import
|
|
11761
|
+
import { createHash as createHash9, randomUUID as randomUUID3 } from "node:crypto";
|
|
11762
|
+
import * as fs11 from "node:fs";
|
|
11763
|
+
import path14 from "node:path";
|
|
11722
11764
|
|
|
11723
11765
|
// node_modules/@openclaw/fs-safe/dist/errors.js
|
|
11724
11766
|
var OPERATIONAL_CODES = new Set([
|
|
@@ -11769,10 +11811,10 @@ function sameFileIdentity(left, right, platform = process.platform) {
|
|
|
11769
11811
|
// node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js
|
|
11770
11812
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
11771
11813
|
import fsSync from "node:fs";
|
|
11772
|
-
import
|
|
11814
|
+
import path10 from "node:path";
|
|
11773
11815
|
|
|
11774
11816
|
// node_modules/@openclaw/fs-safe/dist/bounded-read.js
|
|
11775
|
-
import
|
|
11817
|
+
import fs6 from "node:fs";
|
|
11776
11818
|
|
|
11777
11819
|
// node_modules/@openclaw/fs-safe/dist/byte-budget.js
|
|
11778
11820
|
function normalizeMaxBytes(value, options = {}) {
|
|
@@ -11809,7 +11851,7 @@ function readFileDescriptorBoundedSync(fd, maxBytes) {
|
|
|
11809
11851
|
let total = 0;
|
|
11810
11852
|
while (true) {
|
|
11811
11853
|
const length = nextReadLength(total, maxBytes, scratch.length);
|
|
11812
|
-
const bytesRead =
|
|
11854
|
+
const bytesRead = fs6.readSync(fd, scratch, 0, length, null);
|
|
11813
11855
|
if (bytesRead === 0) {
|
|
11814
11856
|
return Buffer.concat(chunks, total);
|
|
11815
11857
|
}
|
|
@@ -11818,9 +11860,9 @@ function readFileDescriptorBoundedSync(fd, maxBytes) {
|
|
|
11818
11860
|
}
|
|
11819
11861
|
|
|
11820
11862
|
// node_modules/@openclaw/fs-safe/dist/read-open-flags.js
|
|
11821
|
-
import
|
|
11863
|
+
import fs7 from "node:fs";
|
|
11822
11864
|
function resolveReadOpenFlags(options) {
|
|
11823
|
-
const constants = options?.constants ??
|
|
11865
|
+
const constants = options?.constants ?? fs7.constants;
|
|
11824
11866
|
const noFollow = process.platform !== "win32" && options?.followSymlinks !== true && typeof constants.O_NOFOLLOW === "number" ? constants.O_NOFOLLOW : 0;
|
|
11825
11867
|
const nonBlocking = process.platform !== "win32" && typeof constants.O_NONBLOCK === "number" ? constants.O_NONBLOCK : 0;
|
|
11826
11868
|
return constants.O_RDONLY | noFollow | nonBlocking;
|
|
@@ -11859,13 +11901,13 @@ ${ownershipToken}
|
|
|
11859
11901
|
return { raw, ownershipToken };
|
|
11860
11902
|
}
|
|
11861
11903
|
function relativeSidecarLockPath(lockRoot, lockPath) {
|
|
11862
|
-
const resolved =
|
|
11863
|
-
const lexicalRelative =
|
|
11864
|
-
const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${
|
|
11865
|
-
if (!relative || relative === ".." || relative.startsWith(`..${
|
|
11904
|
+
const resolved = path10.resolve(lockPath);
|
|
11905
|
+
const lexicalRelative = path10.relative(lockRoot.rootDir, resolved);
|
|
11906
|
+
const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path10.sep}`) && !path10.isAbsolute(lexicalRelative) ? lexicalRelative : path10.relative(lockRoot.rootReal, resolved);
|
|
11907
|
+
if (!relative || relative === ".." || relative.startsWith(`..${path10.sep}`) || path10.isAbsolute(relative)) {
|
|
11866
11908
|
throw new FsSafeError("outside-workspace", "sidecar lock path is outside lockRoot");
|
|
11867
11909
|
}
|
|
11868
|
-
return relative.split(
|
|
11910
|
+
return relative.split(path10.sep).join(path10.posix.sep);
|
|
11869
11911
|
}
|
|
11870
11912
|
function parseSidecarLockPayload(raw, parser) {
|
|
11871
11913
|
if (parser) {
|
|
@@ -12011,8 +12053,8 @@ function getFsSafeLockConfig() {
|
|
|
12011
12053
|
}
|
|
12012
12054
|
|
|
12013
12055
|
// node_modules/@openclaw/fs-safe/dist/file-lock-sync.js
|
|
12014
|
-
import
|
|
12015
|
-
import
|
|
12056
|
+
import fs8 from "node:fs";
|
|
12057
|
+
import path11 from "node:path";
|
|
12016
12058
|
|
|
12017
12059
|
// node_modules/@openclaw/fs-safe/dist/timing.js
|
|
12018
12060
|
function sleepSync(ms) {
|
|
@@ -12042,7 +12084,7 @@ function releaseAllSyncHeldLocks() {
|
|
|
12042
12084
|
}
|
|
12043
12085
|
try {
|
|
12044
12086
|
if (held.fd !== undefined)
|
|
12045
|
-
|
|
12087
|
+
fs8.closeSync(held.fd);
|
|
12046
12088
|
} catch {}
|
|
12047
12089
|
try {
|
|
12048
12090
|
removeSidecarLockIfUnchangedSync(held.lockPath, held.snapshot);
|
|
@@ -12077,7 +12119,7 @@ function releaseSyncHeldLock(held) {
|
|
|
12077
12119
|
if (held.fd !== undefined) {
|
|
12078
12120
|
const fd = held.fd;
|
|
12079
12121
|
held.fd = undefined;
|
|
12080
|
-
|
|
12122
|
+
fs8.closeSync(fd);
|
|
12081
12123
|
}
|
|
12082
12124
|
removeSidecarLockIfUnchangedSync(held.lockPath, held.snapshot);
|
|
12083
12125
|
heldLocks.delete(held.normalizedTargetPath);
|
|
@@ -12100,29 +12142,29 @@ function createSyncHeldLockHandle(held) {
|
|
|
12100
12142
|
};
|
|
12101
12143
|
}
|
|
12102
12144
|
function canonicalLockParentSync(parent) {
|
|
12103
|
-
return process.platform === "win32" ?
|
|
12145
|
+
return process.platform === "win32" ? fs8.realpathSync.native(parent) : fs8.realpathSync(parent);
|
|
12104
12146
|
}
|
|
12105
12147
|
function normalizeTargetPath(targetPath) {
|
|
12106
|
-
const resolved =
|
|
12107
|
-
|
|
12148
|
+
const resolved = path11.resolve(targetPath);
|
|
12149
|
+
fs8.mkdirSync(path11.dirname(resolved), { recursive: true });
|
|
12108
12150
|
try {
|
|
12109
|
-
return
|
|
12151
|
+
return path11.join(canonicalLockParentSync(path11.dirname(resolved)), path11.basename(resolved));
|
|
12110
12152
|
} catch {
|
|
12111
12153
|
return resolved;
|
|
12112
12154
|
}
|
|
12113
12155
|
}
|
|
12114
12156
|
function boundedLockPath(lockPath, lockRoot) {
|
|
12115
|
-
const resolved =
|
|
12157
|
+
const resolved = path11.resolve(lockPath);
|
|
12116
12158
|
if (!lockRoot)
|
|
12117
12159
|
return resolved;
|
|
12118
12160
|
relativeSidecarLockPath(lockRoot, resolved);
|
|
12119
|
-
const parent =
|
|
12161
|
+
const parent = path11.dirname(resolved);
|
|
12120
12162
|
const parentReal = canonicalLockParentSync(parent);
|
|
12121
|
-
const parentRelative =
|
|
12122
|
-
if (parentRelative === ".." || parentRelative.startsWith(`..${
|
|
12163
|
+
const parentRelative = path11.relative(lockRoot.rootReal, parentReal);
|
|
12164
|
+
if (parentRelative === ".." || parentRelative.startsWith(`..${path11.sep}`) || path11.isAbsolute(parentRelative)) {
|
|
12123
12165
|
throw new FsSafeError("outside-workspace", "sidecar lock parent is outside lockRoot");
|
|
12124
12166
|
}
|
|
12125
|
-
return
|
|
12167
|
+
return path11.join(parentReal, path11.basename(resolved));
|
|
12126
12168
|
}
|
|
12127
12169
|
function defaultShouldReclaim(snapshot, staleMs, nowMs) {
|
|
12128
12170
|
const createdAtMs = sidecarLockPayloadCreatedAtMs(snapshot.payload);
|
|
@@ -12132,7 +12174,7 @@ function defaultShouldReclaim(snapshot, staleMs, nowMs) {
|
|
|
12132
12174
|
}
|
|
12133
12175
|
function reclaimGuardExists(reclaimGuardPath) {
|
|
12134
12176
|
try {
|
|
12135
|
-
|
|
12177
|
+
fs8.lstatSync(reclaimGuardPath);
|
|
12136
12178
|
return true;
|
|
12137
12179
|
} catch (error) {
|
|
12138
12180
|
if (error.code === "ENOENT")
|
|
@@ -12196,19 +12238,19 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
12196
12238
|
const { raw, ownershipToken } = serializeSidecarLockPayload(payload);
|
|
12197
12239
|
let lockFileCreateDenied = false;
|
|
12198
12240
|
try {
|
|
12199
|
-
const noFollow = process.platform !== "win32" && typeof
|
|
12241
|
+
const noFollow = process.platform !== "win32" && typeof fs8.constants.O_NOFOLLOW === "number" ? fs8.constants.O_NOFOLLOW : 0;
|
|
12200
12242
|
try {
|
|
12201
|
-
fd =
|
|
12243
|
+
fd = fs8.openSync(lockPath, fs8.constants.O_WRONLY | fs8.constants.O_CREAT | fs8.constants.O_EXCL | noFollow, 384);
|
|
12202
12244
|
} catch (error) {
|
|
12203
12245
|
lockFileCreateDenied = isTransientLockFileDenial(error, lockPath);
|
|
12204
12246
|
throw error;
|
|
12205
12247
|
}
|
|
12206
|
-
|
|
12207
|
-
|
|
12248
|
+
fs8.writeFileSync(fd, raw, "utf8");
|
|
12249
|
+
fs8.fsyncSync(fd);
|
|
12208
12250
|
const snapshot = {
|
|
12209
12251
|
raw,
|
|
12210
12252
|
payload,
|
|
12211
|
-
stat:
|
|
12253
|
+
stat: fs8.fstatSync(fd),
|
|
12212
12254
|
ownershipToken
|
|
12213
12255
|
};
|
|
12214
12256
|
const createdHeld = {
|
|
@@ -12243,8 +12285,8 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
12243
12285
|
return returnedHandle;
|
|
12244
12286
|
} catch (error) {
|
|
12245
12287
|
if (fd !== undefined) {
|
|
12246
|
-
const failed = { payload: null, stat:
|
|
12247
|
-
|
|
12288
|
+
const failed = { payload: null, stat: fs8.fstatSync(fd) };
|
|
12289
|
+
fs8.closeSync(fd);
|
|
12248
12290
|
fd = undefined;
|
|
12249
12291
|
removeSidecarLockIfUnchangedSync(lockPath, failed);
|
|
12250
12292
|
}
|
|
@@ -12293,7 +12335,7 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
12293
12335
|
})) {
|
|
12294
12336
|
let ownsReclaimGuard = false;
|
|
12295
12337
|
try {
|
|
12296
|
-
|
|
12338
|
+
fs8.mkdirSync(reclaimGuardPath);
|
|
12297
12339
|
ownsReclaimGuard = true;
|
|
12298
12340
|
if (removeSidecarLockIfUnchangedSync(lockPath, snapshot))
|
|
12299
12341
|
continue;
|
|
@@ -12306,7 +12348,7 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
12306
12348
|
} finally {
|
|
12307
12349
|
if (ownsReclaimGuard) {
|
|
12308
12350
|
try {
|
|
12309
|
-
|
|
12351
|
+
fs8.rmdirSync(reclaimGuardPath);
|
|
12310
12352
|
} catch {}
|
|
12311
12353
|
}
|
|
12312
12354
|
}
|
|
@@ -12323,13 +12365,13 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
12323
12365
|
}
|
|
12324
12366
|
// packages/workit-core/src/core/package-root.ts
|
|
12325
12367
|
import { existsSync as existsSync3 } from "node:fs";
|
|
12326
|
-
import
|
|
12368
|
+
import path12 from "node:path";
|
|
12327
12369
|
import { fileURLToPath } from "node:url";
|
|
12328
12370
|
var packageRoot = () => {
|
|
12329
|
-
let dir =
|
|
12371
|
+
let dir = path12.dirname(fileURLToPath(import.meta.url));
|
|
12330
12372
|
while (true) {
|
|
12331
|
-
const parent =
|
|
12332
|
-
if (existsSync3(
|
|
12373
|
+
const parent = path12.dirname(dir);
|
|
12374
|
+
if (existsSync3(path12.join(dir, "package.json")) || parent === dir)
|
|
12333
12375
|
return dir;
|
|
12334
12376
|
dir = parent;
|
|
12335
12377
|
}
|
|
@@ -12337,7 +12379,7 @@ var packageRoot = () => {
|
|
|
12337
12379
|
|
|
12338
12380
|
// packages/workit-core/src/core/store-lock.ts
|
|
12339
12381
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
12340
|
-
import
|
|
12382
|
+
import fs9 from "node:fs";
|
|
12341
12383
|
import { hostname } from "node:os";
|
|
12342
12384
|
var metadataLockSchema = object({
|
|
12343
12385
|
pid: number2().int().nonnegative().safe(),
|
|
@@ -12379,7 +12421,7 @@ var parseProcStatStart = (stat) => {
|
|
|
12379
12421
|
var processStartOf = (pid) => {
|
|
12380
12422
|
if (process.platform === "linux") {
|
|
12381
12423
|
try {
|
|
12382
|
-
return parseProcStatStart(
|
|
12424
|
+
return parseProcStatStart(fs9.readFileSync(`/proc/${pid}/stat`, "utf8"));
|
|
12383
12425
|
} catch {
|
|
12384
12426
|
return null;
|
|
12385
12427
|
}
|
|
@@ -12400,7 +12442,7 @@ var processStartOf = (pid) => {
|
|
|
12400
12442
|
};
|
|
12401
12443
|
var readTrimmed = (file) => {
|
|
12402
12444
|
try {
|
|
12403
|
-
return
|
|
12445
|
+
return fs9.readFileSync(file, "utf8").trim() || null;
|
|
12404
12446
|
} catch {
|
|
12405
12447
|
return null;
|
|
12406
12448
|
}
|
|
@@ -12411,7 +12453,7 @@ var localLockHost = () => {
|
|
|
12411
12453
|
return cachedLockHost;
|
|
12412
12454
|
let pidns = null;
|
|
12413
12455
|
try {
|
|
12414
|
-
pidns = /\[(\d+)\]/.exec(
|
|
12456
|
+
pidns = /\[(\d+)\]/.exec(fs9.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
|
|
12415
12457
|
} catch {}
|
|
12416
12458
|
const boot = readTrimmed("/proc/sys/kernel/random/boot_id");
|
|
12417
12459
|
cachedLockHost = pidns || boot ? `${hostname()}#${pidns ?? "?"}:${boot ?? "?"}` : hostname();
|
|
@@ -12457,7 +12499,7 @@ var classifyLockOwner = (payload, ageMs, localHost = localLockHost(), options =
|
|
|
12457
12499
|
};
|
|
12458
12500
|
var ageOf = (file, nowMs) => {
|
|
12459
12501
|
try {
|
|
12460
|
-
return nowMs -
|
|
12502
|
+
return nowMs - fs9.lstatSync(file).mtimeMs;
|
|
12461
12503
|
} catch {
|
|
12462
12504
|
return null;
|
|
12463
12505
|
}
|
|
@@ -12468,7 +12510,7 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
|
|
|
12468
12510
|
if (age === null || age <= RECLAIM_GUARD_TTL_MS)
|
|
12469
12511
|
return false;
|
|
12470
12512
|
try {
|
|
12471
|
-
|
|
12513
|
+
fs9.rmdirSync(guard);
|
|
12472
12514
|
return true;
|
|
12473
12515
|
} catch {
|
|
12474
12516
|
return false;
|
|
@@ -12477,8 +12519,8 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
|
|
|
12477
12519
|
|
|
12478
12520
|
// packages/workit-core/src/store/event-log.ts
|
|
12479
12521
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
12480
|
-
import
|
|
12481
|
-
import
|
|
12522
|
+
import fs10 from "node:fs";
|
|
12523
|
+
import path13 from "node:path";
|
|
12482
12524
|
var EVENT_VERSION = 1;
|
|
12483
12525
|
|
|
12484
12526
|
class LogDamage extends Error {
|
|
@@ -12507,7 +12549,7 @@ var parseEvent = (line, file, offset) => {
|
|
|
12507
12549
|
function readLog(file, from = 0, expectSeq = null) {
|
|
12508
12550
|
let fd;
|
|
12509
12551
|
try {
|
|
12510
|
-
fd =
|
|
12552
|
+
fd = fs10.openSync(file, "r");
|
|
12511
12553
|
} catch (error) {
|
|
12512
12554
|
if (error.code === "ENOENT")
|
|
12513
12555
|
return null;
|
|
@@ -12516,13 +12558,13 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
12516
12558
|
let bytes;
|
|
12517
12559
|
let size;
|
|
12518
12560
|
try {
|
|
12519
|
-
size =
|
|
12561
|
+
size = fs10.fstatSync(fd).size;
|
|
12520
12562
|
if (from > size)
|
|
12521
12563
|
throw new LogDamage(`${file}: shorter than its snapshot`);
|
|
12522
12564
|
bytes = Buffer.alloc(size - from);
|
|
12523
12565
|
let read = 0;
|
|
12524
12566
|
while (read < bytes.length) {
|
|
12525
|
-
const got =
|
|
12567
|
+
const got = fs10.readSync(fd, bytes, read, bytes.length - read, from + read);
|
|
12526
12568
|
if (got === 0)
|
|
12527
12569
|
break;
|
|
12528
12570
|
read += got;
|
|
@@ -12530,7 +12572,7 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
12530
12572
|
bytes = bytes.subarray(0, read);
|
|
12531
12573
|
size = from + read;
|
|
12532
12574
|
} finally {
|
|
12533
|
-
|
|
12575
|
+
fs10.closeSync(fd);
|
|
12534
12576
|
}
|
|
12535
12577
|
const events = [];
|
|
12536
12578
|
let start = 0;
|
|
@@ -12555,11 +12597,11 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
12555
12597
|
}
|
|
12556
12598
|
var fsyncDirectory = (dir) => {
|
|
12557
12599
|
try {
|
|
12558
|
-
const fd =
|
|
12600
|
+
const fd = fs10.openSync(dir, "r");
|
|
12559
12601
|
try {
|
|
12560
|
-
|
|
12602
|
+
fs10.fsyncSync(fd);
|
|
12561
12603
|
} finally {
|
|
12562
|
-
|
|
12604
|
+
fs10.closeSync(fd);
|
|
12563
12605
|
}
|
|
12564
12606
|
} catch (error) {
|
|
12565
12607
|
if (process.platform !== "win32")
|
|
@@ -12569,46 +12611,46 @@ var fsyncDirectory = (dir) => {
|
|
|
12569
12611
|
function appendEvent(file, event, validEnd) {
|
|
12570
12612
|
const line = Buffer.from(`${JSON.stringify(event)}
|
|
12571
12613
|
`, "utf8");
|
|
12572
|
-
const created = !
|
|
12573
|
-
if (!created &&
|
|
12574
|
-
const cut =
|
|
12614
|
+
const created = !fs10.existsSync(file);
|
|
12615
|
+
if (!created && fs10.statSync(file).size !== validEnd) {
|
|
12616
|
+
const cut = fs10.openSync(file, "r+");
|
|
12575
12617
|
try {
|
|
12576
|
-
|
|
12577
|
-
|
|
12618
|
+
fs10.ftruncateSync(cut, validEnd);
|
|
12619
|
+
fs10.fsyncSync(cut);
|
|
12578
12620
|
} finally {
|
|
12579
|
-
|
|
12621
|
+
fs10.closeSync(cut);
|
|
12580
12622
|
}
|
|
12581
12623
|
}
|
|
12582
|
-
const fd =
|
|
12624
|
+
const fd = fs10.openSync(file, "a", 384);
|
|
12583
12625
|
try {
|
|
12584
12626
|
let written = 0;
|
|
12585
12627
|
while (written < line.length)
|
|
12586
|
-
written +=
|
|
12587
|
-
|
|
12628
|
+
written += fs10.writeSync(fd, line, written, line.length - written);
|
|
12629
|
+
fs10.fsyncSync(fd);
|
|
12588
12630
|
} finally {
|
|
12589
|
-
|
|
12631
|
+
fs10.closeSync(fd);
|
|
12590
12632
|
}
|
|
12591
12633
|
if (created)
|
|
12592
|
-
fsyncDirectory(
|
|
12634
|
+
fsyncDirectory(path13.dirname(file));
|
|
12593
12635
|
return validEnd;
|
|
12594
12636
|
}
|
|
12595
12637
|
function replaceFile(file, bytes, durable) {
|
|
12596
12638
|
const temporary = `${file}.${process.pid}.${randomUUID2()}.tmp`;
|
|
12597
12639
|
try {
|
|
12598
|
-
const fd =
|
|
12640
|
+
const fd = fs10.openSync(temporary, "wx", 384);
|
|
12599
12641
|
try {
|
|
12600
|
-
|
|
12642
|
+
fs10.writeSync(fd, bytes);
|
|
12601
12643
|
if (durable)
|
|
12602
|
-
|
|
12644
|
+
fs10.fsyncSync(fd);
|
|
12603
12645
|
} finally {
|
|
12604
|
-
|
|
12646
|
+
fs10.closeSync(fd);
|
|
12605
12647
|
}
|
|
12606
12648
|
renameWithRetry(temporary, file);
|
|
12607
12649
|
if (durable)
|
|
12608
|
-
fsyncDirectory(
|
|
12650
|
+
fsyncDirectory(path13.dirname(file));
|
|
12609
12651
|
} catch (error) {
|
|
12610
12652
|
try {
|
|
12611
|
-
|
|
12653
|
+
fs10.unlinkSync(temporary);
|
|
12612
12654
|
} catch {}
|
|
12613
12655
|
throw error;
|
|
12614
12656
|
}
|
|
@@ -12633,7 +12675,7 @@ var retryTransient = (run) => {
|
|
|
12633
12675
|
}
|
|
12634
12676
|
}
|
|
12635
12677
|
};
|
|
12636
|
-
var renameWithRetry = (from, to) => retryTransient(() =>
|
|
12678
|
+
var renameWithRetry = (from, to) => retryTransient(() => fs10.renameSync(from, to));
|
|
12637
12679
|
|
|
12638
12680
|
// packages/workit-core/src/store/patch.ts
|
|
12639
12681
|
var isPlainObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -12746,7 +12788,7 @@ var isPatch = (value) => Array.isArray(value) && value.every((op) => {
|
|
|
12746
12788
|
});
|
|
12747
12789
|
|
|
12748
12790
|
// packages/workit-core/src/store/reduce.ts
|
|
12749
|
-
import { createHash as
|
|
12791
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
12750
12792
|
var OPENING_TYPES = new Set([
|
|
12751
12793
|
"task.opened",
|
|
12752
12794
|
"task.imported",
|
|
@@ -12775,7 +12817,7 @@ var isObject5 = (value) => typeof value === "object" && value !== null && !Array
|
|
|
12775
12817
|
var isKey = (value) => isObject5(value) && typeof value.key === "string" && (value.kind === "branch" || value.kind === "detached" || value.kind === "dir") && (value.branch === null || typeof value.branch === "string");
|
|
12776
12818
|
var BLOB_REF = "$blob";
|
|
12777
12819
|
var 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;
|
|
12778
|
-
var blobDigest = (value) =>
|
|
12820
|
+
var blobDigest = (value) => createHash8("sha256").update(JSON.stringify(value)).digest("hex");
|
|
12779
12821
|
var toBlob = (value, blobs) => {
|
|
12780
12822
|
if (!isObject5(value))
|
|
12781
12823
|
return value;
|
|
@@ -12930,7 +12972,7 @@ var indexEntry = (task, key, legacy, file) => {
|
|
|
12930
12972
|
};
|
|
12931
12973
|
var fileSignature = (file) => {
|
|
12932
12974
|
try {
|
|
12933
|
-
const stat =
|
|
12975
|
+
const stat = fs11.statSync(file, { bigint: true });
|
|
12934
12976
|
return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
|
|
12935
12977
|
} catch {
|
|
12936
12978
|
return null;
|
|
@@ -12948,7 +12990,7 @@ var cachedRuntimeVersion = null;
|
|
|
12948
12990
|
var runtimeVersion = () => {
|
|
12949
12991
|
if (cachedRuntimeVersion === null) {
|
|
12950
12992
|
try {
|
|
12951
|
-
const pkg = JSON.parse(
|
|
12993
|
+
const pkg = JSON.parse(fs11.readFileSync(path14.join(packageRoot(), "package.json"), "utf8"));
|
|
12952
12994
|
cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
|
|
12953
12995
|
} catch {
|
|
12954
12996
|
cachedRuntimeVersion = "0.0.0";
|
|
@@ -12972,20 +13014,52 @@ var isNewerVersion = (candidate, current) => {
|
|
|
12972
13014
|
return false;
|
|
12973
13015
|
};
|
|
12974
13016
|
var validId = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(value);
|
|
13017
|
+
var readText = (file) => {
|
|
13018
|
+
try {
|
|
13019
|
+
return fs11.readFileSync(file, "utf8");
|
|
13020
|
+
} catch {
|
|
13021
|
+
return "";
|
|
13022
|
+
}
|
|
13023
|
+
};
|
|
13024
|
+
var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
|
|
13025
|
+
var branchStart = (location, branch, aliases) => {
|
|
13026
|
+
const first = readText(path14.join(path14.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
|
|
13027
|
+
`, 1)[0];
|
|
13028
|
+
const created = REFLOG_LINE.exec(first);
|
|
13029
|
+
const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
|
|
13030
|
+
if (!created || !start)
|
|
13031
|
+
return null;
|
|
13032
|
+
if (start !== "HEAD")
|
|
13033
|
+
return start;
|
|
13034
|
+
if (!location.git)
|
|
13035
|
+
return null;
|
|
13036
|
+
for (const line of readText(path14.join(location.git.gitDir, "logs", "HEAD")).split(`
|
|
13037
|
+
`)) {
|
|
13038
|
+
const entry = REFLOG_LINE.exec(line);
|
|
13039
|
+
const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
|
|
13040
|
+
const seconds = entry ? Number(entry[2]) - Number(created[2]) : -1;
|
|
13041
|
+
if (moved && entry[1] === created[1] && seconds >= 0 && seconds <= 1 && aliases.includes(moved[2]))
|
|
13042
|
+
return moved[1];
|
|
13043
|
+
}
|
|
13044
|
+
return null;
|
|
13045
|
+
};
|
|
13046
|
+
var refExists = (common, ref) => fs11.existsSync(path14.join(common, ref)) || readText(path14.join(common, "packed-refs")).includes(` ${ref}
|
|
13047
|
+
`);
|
|
13048
|
+
var origins = new Map;
|
|
12975
13049
|
var sameDirectoryIdentity = (left, right) => {
|
|
12976
|
-
if (!
|
|
13050
|
+
if (!path14.isAbsolute(left) || !path14.isAbsolute(right))
|
|
12977
13051
|
return false;
|
|
12978
|
-
const normalizedLeft =
|
|
12979
|
-
const normalizedRight =
|
|
13052
|
+
const normalizedLeft = path14.resolve(left);
|
|
13053
|
+
const normalizedRight = path14.resolve(right);
|
|
12980
13054
|
try {
|
|
12981
|
-
const a =
|
|
12982
|
-
const b =
|
|
13055
|
+
const a = fs11.statSync(normalizedLeft, { bigint: true });
|
|
13056
|
+
const b = fs11.statSync(normalizedRight, { bigint: true });
|
|
12983
13057
|
if (!a.isDirectory() || !b.isDirectory())
|
|
12984
13058
|
return false;
|
|
12985
13059
|
if (process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight)
|
|
12986
13060
|
return true;
|
|
12987
|
-
const canonicalLeft =
|
|
12988
|
-
const canonicalRight =
|
|
13061
|
+
const canonicalLeft = fs11.realpathSync(normalizedLeft);
|
|
13062
|
+
const canonicalRight = fs11.realpathSync(normalizedRight);
|
|
12989
13063
|
if (process.platform === "win32" ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase() : canonicalLeft === canonicalRight)
|
|
12990
13064
|
return true;
|
|
12991
13065
|
return a.dev === b.dev && a.ino !== 0n && a.ino === b.ino;
|
|
@@ -13022,8 +13096,8 @@ class TaskStore {
|
|
|
13022
13096
|
migrationChecked = false;
|
|
13023
13097
|
compactDue = new Set;
|
|
13024
13098
|
constructor(root, options = {}) {
|
|
13025
|
-
const resolved =
|
|
13026
|
-
this.origin =
|
|
13099
|
+
const resolved = path14.resolve(root);
|
|
13100
|
+
this.origin = fs11.existsSync(resolved) ? fs11.realpathSync(resolved) : resolved;
|
|
13027
13101
|
this.located = resolveStore(this.origin);
|
|
13028
13102
|
this.root = checkoutRootOf(this.origin, this.located);
|
|
13029
13103
|
this.lockTimeoutMs = options.lockTimeoutMs ?? defaultLockTimeout();
|
|
@@ -13031,7 +13105,7 @@ class TaskStore {
|
|
|
13031
13105
|
this.migrateOnRead = options.migrateOnRead;
|
|
13032
13106
|
}
|
|
13033
13107
|
location() {
|
|
13034
|
-
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared &&
|
|
13108
|
+
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs11.existsSync(path14.join(this.root, ".git"))) {
|
|
13035
13109
|
this.located = undefined;
|
|
13036
13110
|
this.migrationChecked = false;
|
|
13037
13111
|
}
|
|
@@ -13095,7 +13169,7 @@ class TaskStore {
|
|
|
13095
13169
|
const tasksDir = this.paths().tasks;
|
|
13096
13170
|
let names;
|
|
13097
13171
|
try {
|
|
13098
|
-
names =
|
|
13172
|
+
names = fs11.existsSync(tasksDir) ? fs11.readdirSync(tasksDir).filter((name) => validId(name)) : [];
|
|
13099
13173
|
} catch (error) {
|
|
13100
13174
|
return failure2("storage_error", `unable to list tasks: ${String(error)}`, {
|
|
13101
13175
|
path: tasksDir
|
|
@@ -13136,7 +13210,7 @@ class TaskStore {
|
|
|
13136
13210
|
const file = this.paths().workspace;
|
|
13137
13211
|
let bytes;
|
|
13138
13212
|
try {
|
|
13139
|
-
bytes = retryTransient(() =>
|
|
13213
|
+
bytes = retryTransient(() => fs11.readFileSync(file, "utf8"));
|
|
13140
13214
|
} catch (error) {
|
|
13141
13215
|
if (error.code === "ENOENT")
|
|
13142
13216
|
return success(null, null, null);
|
|
@@ -13187,7 +13261,7 @@ class TaskStore {
|
|
|
13187
13261
|
const moved = this.bindLocked(again.data.id, key, input.now);
|
|
13188
13262
|
return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
|
|
13189
13263
|
}
|
|
13190
|
-
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" :
|
|
13264
|
+
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path14.basename(this.root) || this.root;
|
|
13191
13265
|
const intent = {
|
|
13192
13266
|
objective: input.objective?.trim() || `Work on ${label}`,
|
|
13193
13267
|
scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
|
|
@@ -13224,6 +13298,66 @@ class TaskStore {
|
|
|
13224
13298
|
return bound;
|
|
13225
13299
|
return success(null, null, { key: key.data, bound: bound.data });
|
|
13226
13300
|
}
|
|
13301
|
+
taskForWorker(workerId) {
|
|
13302
|
+
if (typeof workerId !== "string" || !validId(workerId))
|
|
13303
|
+
return failure2("invalid_input", "needs a worker id: `workit worker cancel|report <workerId>`");
|
|
13304
|
+
const listed = this.listTaskIndex();
|
|
13305
|
+
if (!listed.ok)
|
|
13306
|
+
return listed;
|
|
13307
|
+
for (const entry of listed.data) {
|
|
13308
|
+
if (entry.status === "closed")
|
|
13309
|
+
continue;
|
|
13310
|
+
const loaded = this.load(entry.id);
|
|
13311
|
+
if (!loaded.ok)
|
|
13312
|
+
return loaded;
|
|
13313
|
+
if (loaded.data?.record.workers.some((worker) => worker.id === workerId))
|
|
13314
|
+
return success(null, null, entry.id);
|
|
13315
|
+
}
|
|
13316
|
+
return success(null, null, null);
|
|
13317
|
+
}
|
|
13318
|
+
originTaskEntry() {
|
|
13319
|
+
const key = this.currentKey();
|
|
13320
|
+
const location = this.location();
|
|
13321
|
+
const branch = key.ok ? key.data.branch : null;
|
|
13322
|
+
if (!branch || !location.ok)
|
|
13323
|
+
return success(null, null, null);
|
|
13324
|
+
const common = path14.dirname(location.data.dir);
|
|
13325
|
+
const heads = path14.join(common, "logs", "refs", "heads");
|
|
13326
|
+
const headLog = location.data.git ? path14.join(location.data.git.gitDir, "logs", "HEAD") : "";
|
|
13327
|
+
const signatures = [
|
|
13328
|
+
path14.join(heads, branch),
|
|
13329
|
+
headLog,
|
|
13330
|
+
heads,
|
|
13331
|
+
path14.join(common, "packed-refs")
|
|
13332
|
+
].map((file) => fileSignature(file) ?? "-");
|
|
13333
|
+
const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
|
|
13334
|
+
let resolved = origins.get(slot);
|
|
13335
|
+
if (resolved?.signature !== signatures.join("|")) {
|
|
13336
|
+
const renames = branchRenames(location.data);
|
|
13337
|
+
const aliases = [branch];
|
|
13338
|
+
for (const rename of renames.toReversed())
|
|
13339
|
+
if (rename.to === aliases.at(-1))
|
|
13340
|
+
aliases.push(rename.from);
|
|
13341
|
+
const start = branchStart(location.data, branch, aliases);
|
|
13342
|
+
const slash = start?.indexOf("/") ?? -1;
|
|
13343
|
+
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)}"]`));
|
|
13344
|
+
const name = start && (remote ? start.slice(slash + 1) : start);
|
|
13345
|
+
resolved = {
|
|
13346
|
+
signature: signatures.join("|"),
|
|
13347
|
+
name: name && !aliases.includes(name) ? name : null,
|
|
13348
|
+
renames
|
|
13349
|
+
};
|
|
13350
|
+
origins.set(slot, resolved);
|
|
13351
|
+
}
|
|
13352
|
+
const entry = resolved.name ? this.openTasksForKey(resolved.name, resolved.renames) : null;
|
|
13353
|
+
if (entry && !entry.ok)
|
|
13354
|
+
return entry;
|
|
13355
|
+
return success(null, null, entry?.data[0] ? { origin: `branch ${resolved.name}`, entry: entry.data[0] } : null);
|
|
13356
|
+
}
|
|
13357
|
+
readStoreTask(taskId) {
|
|
13358
|
+
const loaded = this.load(taskId);
|
|
13359
|
+
return loaded.ok ? success(null, null, loaded.data?.record ?? null) : loaded;
|
|
13360
|
+
}
|
|
13227
13361
|
create(input, provenance, expectedWorkspaceRevision) {
|
|
13228
13362
|
const value = "intent" in input ? input : {
|
|
13229
13363
|
intent: input,
|
|
@@ -13444,13 +13578,13 @@ class TaskStore {
|
|
|
13444
13578
|
if (!ready.ok)
|
|
13445
13579
|
return ready;
|
|
13446
13580
|
const dirs = this.paths();
|
|
13447
|
-
const recovery =
|
|
13448
|
-
if (
|
|
13581
|
+
const recovery = path14.join(this.root, ".workit", "recovery");
|
|
13582
|
+
if (fs11.existsSync(recovery)) {
|
|
13449
13583
|
let files = 0;
|
|
13450
13584
|
let bytes = 0;
|
|
13451
13585
|
try {
|
|
13452
|
-
for (const name of
|
|
13453
|
-
const stat =
|
|
13586
|
+
for (const name of fs11.readdirSync(recovery)) {
|
|
13587
|
+
const stat = fs11.lstatSync(path14.join(recovery, name));
|
|
13454
13588
|
if (!stat.isFile())
|
|
13455
13589
|
continue;
|
|
13456
13590
|
files += 1;
|
|
@@ -13459,17 +13593,17 @@ class TaskStore {
|
|
|
13459
13593
|
} catch {}
|
|
13460
13594
|
const remove = options.pruneRecovery === true && !dryRun;
|
|
13461
13595
|
if (remove)
|
|
13462
|
-
|
|
13596
|
+
fs11.rmSync(recovery, { recursive: true, force: true });
|
|
13463
13597
|
report.legacyRecovery = { path: recovery, files, bytes, removed: remove };
|
|
13464
13598
|
}
|
|
13465
|
-
if (!
|
|
13599
|
+
if (!fs11.existsSync(dirs.tasks))
|
|
13466
13600
|
return success(null, null, report);
|
|
13467
|
-
const names =
|
|
13601
|
+
const names = fs11.readdirSync(dirs.tasks).filter((name) => validId(name));
|
|
13468
13602
|
for (const name of names) {
|
|
13469
13603
|
const file = this.eventsPath(name);
|
|
13470
13604
|
let size = 0;
|
|
13471
13605
|
try {
|
|
13472
|
-
size =
|
|
13606
|
+
size = fs11.statSync(file).size;
|
|
13473
13607
|
} catch {
|
|
13474
13608
|
continue;
|
|
13475
13609
|
}
|
|
@@ -13497,16 +13631,16 @@ class TaskStore {
|
|
|
13497
13631
|
const references = new Set;
|
|
13498
13632
|
for (const name of names)
|
|
13499
13633
|
try {
|
|
13500
|
-
for (const match of
|
|
13634
|
+
for (const match of fs11.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
|
|
13501
13635
|
references.add(match[1]);
|
|
13502
13636
|
} catch {}
|
|
13503
|
-
if (
|
|
13504
|
-
for (const name of
|
|
13637
|
+
if (fs11.existsSync(dirs.blobs))
|
|
13638
|
+
for (const name of fs11.readdirSync(dirs.blobs)) {
|
|
13505
13639
|
const digest = name.replace(/\.json$/, "");
|
|
13506
|
-
const file =
|
|
13640
|
+
const file = path14.join(dirs.blobs, name);
|
|
13507
13641
|
let stat;
|
|
13508
13642
|
try {
|
|
13509
|
-
stat =
|
|
13643
|
+
stat = fs11.lstatSync(file);
|
|
13510
13644
|
} catch {
|
|
13511
13645
|
continue;
|
|
13512
13646
|
}
|
|
@@ -13517,11 +13651,11 @@ class TaskStore {
|
|
|
13517
13651
|
report.blobs.removed += 1;
|
|
13518
13652
|
report.blobs.removedBytes += stat.size;
|
|
13519
13653
|
if (!dryRun)
|
|
13520
|
-
|
|
13654
|
+
fs11.rmSync(file, { force: true });
|
|
13521
13655
|
}
|
|
13522
13656
|
return success(null, null, null);
|
|
13523
13657
|
};
|
|
13524
|
-
const swept = dryRun ? sweep() : this.withLock(
|
|
13658
|
+
const swept = dryRun ? sweep() : this.withLock(path14.join(dirs.dir, "gc.lock"), sweep);
|
|
13525
13659
|
if (!swept.ok)
|
|
13526
13660
|
return swept;
|
|
13527
13661
|
const temporaryDirs = [
|
|
@@ -13529,17 +13663,17 @@ class TaskStore {
|
|
|
13529
13663
|
dirs.tasks,
|
|
13530
13664
|
dirs.blobs,
|
|
13531
13665
|
dirs.checkout,
|
|
13532
|
-
...names.map((name) =>
|
|
13666
|
+
...names.map((name) => path14.join(dirs.tasks, name))
|
|
13533
13667
|
];
|
|
13534
13668
|
for (const directory of temporaryDirs) {
|
|
13535
|
-
if (!
|
|
13669
|
+
if (!fs11.existsSync(directory))
|
|
13536
13670
|
continue;
|
|
13537
|
-
for (const name of
|
|
13671
|
+
for (const name of fs11.readdirSync(directory)) {
|
|
13538
13672
|
if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
|
|
13539
13673
|
continue;
|
|
13540
|
-
const file =
|
|
13674
|
+
const file = path14.join(directory, name);
|
|
13541
13675
|
try {
|
|
13542
|
-
const stat =
|
|
13676
|
+
const stat = fs11.lstatSync(file);
|
|
13543
13677
|
if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
|
|
13544
13678
|
continue;
|
|
13545
13679
|
} catch {
|
|
@@ -13547,37 +13681,37 @@ class TaskStore {
|
|
|
13547
13681
|
}
|
|
13548
13682
|
report.temporary.removed += 1;
|
|
13549
13683
|
if (!dryRun)
|
|
13550
|
-
|
|
13684
|
+
fs11.rmSync(file, { force: true });
|
|
13551
13685
|
}
|
|
13552
13686
|
}
|
|
13553
13687
|
return success(null, null, report);
|
|
13554
13688
|
}
|
|
13555
13689
|
paths() {
|
|
13556
13690
|
const location = this.location();
|
|
13557
|
-
const dir = location.ok ? location.data.dir :
|
|
13558
|
-
const checkout =
|
|
13691
|
+
const dir = location.ok ? location.data.dir : path14.join(this.root, ".workit");
|
|
13692
|
+
const checkout = path14.join(dir, "checkouts", checkoutSlug(this.root));
|
|
13559
13693
|
return {
|
|
13560
13694
|
dir,
|
|
13561
13695
|
shared: location.ok ? location.data.shared : false,
|
|
13562
|
-
marker:
|
|
13696
|
+
marker: path14.join(dir, "store.json"),
|
|
13563
13697
|
checkout,
|
|
13564
|
-
workspace:
|
|
13565
|
-
checkoutLock:
|
|
13566
|
-
tasks:
|
|
13567
|
-
index:
|
|
13568
|
-
blobs:
|
|
13569
|
-
legacy:
|
|
13570
|
-
v2:
|
|
13698
|
+
workspace: path14.join(checkout, "workspace.json"),
|
|
13699
|
+
checkoutLock: path14.join(checkout, "metadata.lock"),
|
|
13700
|
+
tasks: path14.join(dir, "tasks"),
|
|
13701
|
+
index: path14.join(dir, "task-index.json"),
|
|
13702
|
+
blobs: path14.join(dir, "blobs", "candidates"),
|
|
13703
|
+
legacy: path14.join(dir, "legacy", checkoutSlug(this.root), "v2"),
|
|
13704
|
+
v2: path14.join(this.root, ".workit")
|
|
13571
13705
|
};
|
|
13572
13706
|
}
|
|
13573
13707
|
eventsPath(taskId) {
|
|
13574
|
-
return
|
|
13708
|
+
return path14.join(this.paths().tasks, taskId, "events.jsonl");
|
|
13575
13709
|
}
|
|
13576
13710
|
snapshotPath(taskId) {
|
|
13577
|
-
return
|
|
13711
|
+
return path14.join(this.paths().tasks, taskId, "snapshot.json");
|
|
13578
13712
|
}
|
|
13579
13713
|
taskLockPath(taskId) {
|
|
13580
|
-
return
|
|
13714
|
+
return path14.join(this.paths().tasks, taskId, "lock");
|
|
13581
13715
|
}
|
|
13582
13716
|
ready(write = false) {
|
|
13583
13717
|
const location = this.location();
|
|
@@ -13587,7 +13721,7 @@ class TaskStore {
|
|
|
13587
13721
|
return success(null, null, null);
|
|
13588
13722
|
const dirs = this.paths();
|
|
13589
13723
|
try {
|
|
13590
|
-
const marker = JSON.parse(
|
|
13724
|
+
const marker = JSON.parse(fs11.readFileSync(dirs.marker, "utf8"));
|
|
13591
13725
|
if (typeof marker.version === "number" && marker.version > STORE_FORMAT)
|
|
13592
13726
|
return failure2("recovery_required", `the workit store at ${dirs.dir} has format ${marker.version}; upgrade Workit to use it`, { path: dirs.marker });
|
|
13593
13727
|
} catch {}
|
|
@@ -13616,21 +13750,21 @@ class TaskStore {
|
|
|
13616
13750
|
return {
|
|
13617
13751
|
read: (digest) => {
|
|
13618
13752
|
try {
|
|
13619
|
-
return JSON.parse(
|
|
13753
|
+
return JSON.parse(fs11.readFileSync(path14.join(dir, `${digest}.json`), "utf8"));
|
|
13620
13754
|
} catch (error) {
|
|
13621
13755
|
throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
|
|
13622
13756
|
}
|
|
13623
13757
|
},
|
|
13624
13758
|
write: (digest, value) => {
|
|
13625
|
-
const file =
|
|
13626
|
-
if (
|
|
13759
|
+
const file = path14.join(dir, `${digest}.json`);
|
|
13760
|
+
if (fs11.existsSync(file)) {
|
|
13627
13761
|
const stamp = new Date;
|
|
13628
13762
|
try {
|
|
13629
|
-
|
|
13763
|
+
fs11.utimesSync(file, stamp, stamp);
|
|
13630
13764
|
return;
|
|
13631
13765
|
} catch {}
|
|
13632
13766
|
}
|
|
13633
|
-
|
|
13767
|
+
fs11.mkdirSync(dir, { recursive: true });
|
|
13634
13768
|
replaceFile(file, JSON.stringify(value), true);
|
|
13635
13769
|
}
|
|
13636
13770
|
};
|
|
@@ -13712,7 +13846,7 @@ class TaskStore {
|
|
|
13712
13846
|
}
|
|
13713
13847
|
readSnapshot(taskId) {
|
|
13714
13848
|
try {
|
|
13715
|
-
const value = JSON.parse(
|
|
13849
|
+
const value = JSON.parse(fs11.readFileSync(this.snapshotPath(taskId), "utf8"));
|
|
13716
13850
|
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))
|
|
13717
13851
|
return null;
|
|
13718
13852
|
return value;
|
|
@@ -13814,8 +13948,8 @@ class TaskStore {
|
|
|
13814
13948
|
}
|
|
13815
13949
|
}
|
|
13816
13950
|
openTask(record, type, key, legacy = null) {
|
|
13817
|
-
const dir =
|
|
13818
|
-
|
|
13951
|
+
const dir = path14.join(this.paths().tasks, record.id);
|
|
13952
|
+
fs11.mkdirSync(dir, { recursive: true });
|
|
13819
13953
|
const blobs = this.blobStore();
|
|
13820
13954
|
const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
|
|
13821
13955
|
const event = this.event(record.id, 1, type, {
|
|
@@ -13867,11 +14001,11 @@ class TaskStore {
|
|
|
13867
14001
|
} catch {}
|
|
13868
14002
|
if (!latest || latest.events[0]?.id !== last.id)
|
|
13869
14003
|
return failure2("busy", "the task log changed during compaction; retry", { taskId });
|
|
13870
|
-
const appended =
|
|
14004
|
+
const appended = fs11.readFileSync(file).subarray(log.end, latest.end);
|
|
13871
14005
|
const bytes = Buffer.concat([Buffer.from(prefix, "utf8"), appended]);
|
|
13872
14006
|
replaceFile(file, bytes.toString("utf8"), true);
|
|
13873
14007
|
try {
|
|
13874
|
-
|
|
14008
|
+
fs11.rmSync(this.snapshotPath(taskId), { force: true });
|
|
13875
14009
|
} catch {}
|
|
13876
14010
|
const reloaded = this.load(taskId);
|
|
13877
14011
|
if (reloaded.ok && reloaded.data)
|
|
@@ -13884,7 +14018,7 @@ class TaskStore {
|
|
|
13884
14018
|
}
|
|
13885
14019
|
countEvents(file) {
|
|
13886
14020
|
try {
|
|
13887
|
-
const bytes =
|
|
14021
|
+
const bytes = fs11.readFileSync(file);
|
|
13888
14022
|
let count = 0;
|
|
13889
14023
|
for (let index = bytes.indexOf(10);index >= 0; index = bytes.indexOf(10, index + 1))
|
|
13890
14024
|
count += 1;
|
|
@@ -13893,12 +14027,12 @@ class TaskStore {
|
|
|
13893
14027
|
return 0;
|
|
13894
14028
|
}
|
|
13895
14029
|
}
|
|
13896
|
-
openTasksForKey(key) {
|
|
14030
|
+
openTasksForKey(key, known) {
|
|
13897
14031
|
const all = this.listStoreIndex();
|
|
13898
14032
|
if (!all.ok)
|
|
13899
14033
|
return all;
|
|
13900
|
-
const location = this.location();
|
|
13901
|
-
const renames = location
|
|
14034
|
+
const location = known ? null : this.location();
|
|
14035
|
+
const renames = known ?? (location?.ok ? branchRenames(location.data) : []);
|
|
13902
14036
|
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));
|
|
13903
14037
|
}
|
|
13904
14038
|
openTaskForKey(key) {
|
|
@@ -13906,8 +14040,8 @@ class TaskStore {
|
|
|
13906
14040
|
return bound.ok ? success(null, null, bound.data[0] ?? null) : bound;
|
|
13907
14041
|
}
|
|
13908
14042
|
withKeyLock(key, operation) {
|
|
13909
|
-
const digest =
|
|
13910
|
-
return this.withLock(
|
|
14043
|
+
const digest = createHash9("sha256").update(key).digest("hex").slice(0, 32);
|
|
14044
|
+
return this.withLock(path14.join(this.paths().dir, "keys", `${digest}.lock`), operation);
|
|
13911
14045
|
}
|
|
13912
14046
|
bindLocked(taskId, key, timestamp) {
|
|
13913
14047
|
const current = this.readWorkspace();
|
|
@@ -14048,7 +14182,7 @@ class TaskStore {
|
|
|
14048
14182
|
writeWorkspace(record) {
|
|
14049
14183
|
const file = this.paths().workspace;
|
|
14050
14184
|
try {
|
|
14051
|
-
|
|
14185
|
+
fs11.mkdirSync(path14.dirname(file), { recursive: true });
|
|
14052
14186
|
replaceFile(file, `${canonicalJson(record)}
|
|
14053
14187
|
`, true);
|
|
14054
14188
|
return success(null, null, null);
|
|
@@ -14058,7 +14192,7 @@ class TaskStore {
|
|
|
14058
14192
|
}
|
|
14059
14193
|
readIndex() {
|
|
14060
14194
|
try {
|
|
14061
|
-
const value = JSON.parse(
|
|
14195
|
+
const value = JSON.parse(fs11.readFileSync(this.paths().index, "utf8"));
|
|
14062
14196
|
if (!isObject6(value) || value.version !== INDEX_VERSION || !isObject6(value.tasks))
|
|
14063
14197
|
return {};
|
|
14064
14198
|
const tasks = {};
|
|
@@ -14072,7 +14206,7 @@ class TaskStore {
|
|
|
14072
14206
|
}
|
|
14073
14207
|
writeIndex(entries) {
|
|
14074
14208
|
try {
|
|
14075
|
-
|
|
14209
|
+
fs11.mkdirSync(this.paths().tasks, { recursive: true });
|
|
14076
14210
|
replaceFile(this.paths().index, JSON.stringify({
|
|
14077
14211
|
version: INDEX_VERSION,
|
|
14078
14212
|
tasks: Object.fromEntries(entries.map((entry) => [entry.id, entry]))
|
|
@@ -14126,23 +14260,23 @@ class TaskStore {
|
|
|
14126
14260
|
return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
|
|
14127
14261
|
}
|
|
14128
14262
|
v2Dirs() {
|
|
14129
|
-
return [...new Set([this.root, this.origin])].map((dir) =>
|
|
14263
|
+
return [...new Set([this.root, this.origin])].map((dir) => path14.join(dir, ".workit"));
|
|
14130
14264
|
}
|
|
14131
14265
|
pendingMigrations() {
|
|
14132
14266
|
return this.v2Dirs().filter((dir) => {
|
|
14133
|
-
const raw = this.readV2Workspace(
|
|
14267
|
+
const raw = this.readV2Workspace(path14.join(dir, "workspace.json"));
|
|
14134
14268
|
return raw !== null && raw !== TOMBSTONE;
|
|
14135
14269
|
});
|
|
14136
14270
|
}
|
|
14137
14271
|
migrateV2(v2) {
|
|
14138
14272
|
const dirs = this.paths();
|
|
14139
|
-
const v2Workspace =
|
|
14140
|
-
const v2Tasks =
|
|
14141
|
-
const legacy =
|
|
14273
|
+
const v2Workspace = path14.join(v2, "workspace.json");
|
|
14274
|
+
const v2Tasks = path14.join(v2, "tasks");
|
|
14275
|
+
const legacy = path14.join(dirs.dir, "legacy", checkoutSlug(path14.dirname(v2)), "v2");
|
|
14142
14276
|
const raw = this.readV2Workspace(v2Workspace);
|
|
14143
14277
|
if (raw === null || raw === TOMBSTONE)
|
|
14144
14278
|
return success(null, null, null);
|
|
14145
|
-
return this.withLock(dirs.checkoutLock, () => this.withLock(
|
|
14279
|
+
return this.withLock(dirs.checkoutLock, () => this.withLock(path14.join(v2, "metadata.lock"), () => {
|
|
14146
14280
|
const workspace = this.readV2Workspace(v2Workspace);
|
|
14147
14281
|
if (workspace === null || workspace === TOMBSTONE)
|
|
14148
14282
|
return success(null, null, null);
|
|
@@ -14151,7 +14285,7 @@ class TaskStore {
|
|
|
14151
14285
|
return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
|
|
14152
14286
|
let files = [];
|
|
14153
14287
|
try {
|
|
14154
|
-
files =
|
|
14288
|
+
files = fs11.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path14.join(v2Tasks, name)));
|
|
14155
14289
|
} catch {}
|
|
14156
14290
|
const current = this.readWorkspace();
|
|
14157
14291
|
if (!current.ok)
|
|
@@ -14169,22 +14303,22 @@ class TaskStore {
|
|
|
14169
14303
|
let skipped = 0;
|
|
14170
14304
|
const done = new Map;
|
|
14171
14305
|
try {
|
|
14172
|
-
|
|
14306
|
+
fs11.mkdirSync(path14.join(legacy, "tasks"), { recursive: true });
|
|
14173
14307
|
for (const name of ["workspace.json", "index.json"])
|
|
14174
|
-
if (
|
|
14175
|
-
|
|
14308
|
+
if (fs11.existsSync(path14.join(v2, name)))
|
|
14309
|
+
fs11.copyFileSync(path14.join(v2, name), path14.join(legacy, name));
|
|
14176
14310
|
for (const name of files) {
|
|
14177
14311
|
const id = name.slice(0, -5);
|
|
14178
|
-
const file =
|
|
14179
|
-
const bytes =
|
|
14312
|
+
const file = path14.join(v2Tasks, name);
|
|
14313
|
+
const bytes = fs11.readFileSync(file, "utf8");
|
|
14180
14314
|
const digest = sha256(bytes);
|
|
14181
|
-
|
|
14315
|
+
fs11.writeFileSync(path14.join(legacy, "tasks", name), bytes);
|
|
14182
14316
|
const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
|
|
14183
14317
|
if (!parsed.ok || parsed.data.id !== id)
|
|
14184
14318
|
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 });
|
|
14185
14319
|
const record = { ...parsed.data, workspaceId: target.id };
|
|
14186
|
-
const origin = { path:
|
|
14187
|
-
if (!
|
|
14320
|
+
const origin = { path: path14.join(legacy, "tasks", name), digest };
|
|
14321
|
+
if (!fs11.existsSync(this.eventsPath(id))) {
|
|
14188
14322
|
this.openTask(record, "migrated.from_v2", null, origin);
|
|
14189
14323
|
migrated += 1;
|
|
14190
14324
|
} else {
|
|
@@ -14219,8 +14353,8 @@ class TaskStore {
|
|
|
14219
14353
|
try {
|
|
14220
14354
|
let complete = true;
|
|
14221
14355
|
for (const [name, digest] of done) {
|
|
14222
|
-
const file =
|
|
14223
|
-
if (sha256(
|
|
14356
|
+
const file = path14.join(v2Tasks, name);
|
|
14357
|
+
if (sha256(fs11.readFileSync(file, "utf8")) !== digest) {
|
|
14224
14358
|
complete = false;
|
|
14225
14359
|
continue;
|
|
14226
14360
|
}
|
|
@@ -14230,7 +14364,7 @@ class TaskStore {
|
|
|
14230
14364
|
return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
|
|
14231
14365
|
path: v2
|
|
14232
14366
|
});
|
|
14233
|
-
|
|
14367
|
+
fs11.rmSync(path14.join(v2, "index.json"), { force: true });
|
|
14234
14368
|
this.writeTombstone(v2, target.id);
|
|
14235
14369
|
} catch (error) {
|
|
14236
14370
|
return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
|
|
@@ -14250,45 +14384,45 @@ class TaskStore {
|
|
|
14250
14384
|
}
|
|
14251
14385
|
adoptLocalStore() {
|
|
14252
14386
|
const dirs = this.paths();
|
|
14253
|
-
const marker =
|
|
14254
|
-
if (!dirs.shared || !
|
|
14387
|
+
const marker = path14.join(dirs.v2, "store.json");
|
|
14388
|
+
if (!dirs.shared || !fs11.existsSync(marker))
|
|
14255
14389
|
return success(null, null, null);
|
|
14256
14390
|
return this.withLock(dirs.checkoutLock, () => {
|
|
14257
|
-
if (!
|
|
14391
|
+
if (!fs11.existsSync(marker))
|
|
14258
14392
|
return success(null, null, null);
|
|
14259
14393
|
let tasks = 0;
|
|
14260
14394
|
try {
|
|
14261
14395
|
const merge = (from, to) => {
|
|
14262
|
-
if (!
|
|
14263
|
-
|
|
14396
|
+
if (!fs11.existsSync(to)) {
|
|
14397
|
+
fs11.mkdirSync(path14.dirname(to), { recursive: true });
|
|
14264
14398
|
try {
|
|
14265
|
-
|
|
14399
|
+
fs11.renameSync(from, to);
|
|
14266
14400
|
} catch {
|
|
14267
|
-
|
|
14268
|
-
|
|
14401
|
+
fs11.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
|
|
14402
|
+
fs11.rmSync(from, { recursive: true, force: true });
|
|
14269
14403
|
}
|
|
14270
14404
|
return;
|
|
14271
14405
|
}
|
|
14272
|
-
if (
|
|
14273
|
-
for (const name of
|
|
14274
|
-
merge(
|
|
14406
|
+
if (fs11.lstatSync(from).isDirectory() && fs11.lstatSync(to).isDirectory())
|
|
14407
|
+
for (const name of fs11.readdirSync(from))
|
|
14408
|
+
merge(path14.join(from, name), path14.join(to, name));
|
|
14275
14409
|
};
|
|
14276
14410
|
for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
|
|
14277
|
-
const from =
|
|
14278
|
-
if (!
|
|
14411
|
+
const from = path14.join(dirs.v2, sub);
|
|
14412
|
+
if (!fs11.existsSync(from))
|
|
14279
14413
|
continue;
|
|
14280
14414
|
if (sub === "tasks") {
|
|
14281
|
-
for (const name of
|
|
14282
|
-
if (validId(name) && !
|
|
14415
|
+
for (const name of fs11.readdirSync(from))
|
|
14416
|
+
if (validId(name) && !fs11.existsSync(path14.join(dirs.dir, sub, name)))
|
|
14283
14417
|
tasks += 1;
|
|
14284
14418
|
}
|
|
14285
|
-
merge(from,
|
|
14419
|
+
merge(from, path14.join(dirs.dir, sub));
|
|
14286
14420
|
}
|
|
14287
|
-
const ledger =
|
|
14288
|
-
if (
|
|
14289
|
-
merge(ledger,
|
|
14290
|
-
|
|
14291
|
-
|
|
14421
|
+
const ledger = path14.join(dirs.v2, "ledger");
|
|
14422
|
+
if (fs11.existsSync(ledger) && !fs11.existsSync(path14.join(dirs.dir, "ledger")))
|
|
14423
|
+
merge(ledger, path14.join(dirs.dir, "ledger"));
|
|
14424
|
+
fs11.rmSync(path14.join(dirs.v2, "task-index.json"), { force: true });
|
|
14425
|
+
fs11.renameSync(marker, path14.join(dirs.v2, "store.moved.json"));
|
|
14292
14426
|
} catch (error) {
|
|
14293
14427
|
return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
|
|
14294
14428
|
path: dirs.v2
|
|
@@ -14308,7 +14442,7 @@ class TaskStore {
|
|
|
14308
14442
|
readV2Workspace(file) {
|
|
14309
14443
|
let bytes;
|
|
14310
14444
|
try {
|
|
14311
|
-
bytes =
|
|
14445
|
+
bytes = fs11.readFileSync(file, "utf8");
|
|
14312
14446
|
} catch {
|
|
14313
14447
|
return null;
|
|
14314
14448
|
}
|
|
@@ -14326,7 +14460,7 @@ class TaskStore {
|
|
|
14326
14460
|
schemaVersion: SCHEMA_VERSION,
|
|
14327
14461
|
id: workspaceId ?? newId(),
|
|
14328
14462
|
revision: newRevision(),
|
|
14329
|
-
root:
|
|
14463
|
+
root: path14.dirname(v2),
|
|
14330
14464
|
runtime: { createdWith: version, updatedWith: version },
|
|
14331
14465
|
writer: null,
|
|
14332
14466
|
store: {
|
|
@@ -14337,12 +14471,12 @@ class TaskStore {
|
|
|
14337
14471
|
},
|
|
14338
14472
|
critical: ["store"]
|
|
14339
14473
|
};
|
|
14340
|
-
|
|
14341
|
-
const ignore =
|
|
14342
|
-
if (!
|
|
14343
|
-
|
|
14474
|
+
fs11.mkdirSync(v2, { recursive: true });
|
|
14475
|
+
const ignore = path14.join(v2, ".gitignore");
|
|
14476
|
+
if (!fs11.existsSync(ignore))
|
|
14477
|
+
fs11.writeFileSync(ignore, `*
|
|
14344
14478
|
`);
|
|
14345
|
-
replaceFile(
|
|
14479
|
+
replaceFile(path14.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
|
|
14346
14480
|
`, true);
|
|
14347
14481
|
}
|
|
14348
14482
|
stubFor(taskId) {
|
|
@@ -14358,7 +14492,7 @@ class TaskStore {
|
|
|
14358
14492
|
}
|
|
14359
14493
|
isStub(file) {
|
|
14360
14494
|
try {
|
|
14361
|
-
const value = JSON.parse(
|
|
14495
|
+
const value = JSON.parse(fs11.readFileSync(file, "utf8"));
|
|
14362
14496
|
return isObject6(value) && isObject6(value.store) && value.store.format === "workit-store";
|
|
14363
14497
|
} catch {
|
|
14364
14498
|
return false;
|
|
@@ -14366,32 +14500,32 @@ class TaskStore {
|
|
|
14366
14500
|
}
|
|
14367
14501
|
writeTombstoneIfMissing() {
|
|
14368
14502
|
try {
|
|
14369
|
-
const v2 =
|
|
14370
|
-
if (
|
|
14503
|
+
const v2 = path14.join(this.root, ".workit");
|
|
14504
|
+
if (fs11.existsSync(path14.join(v2, "workspace.json")))
|
|
14371
14505
|
return;
|
|
14372
14506
|
let id = null;
|
|
14373
14507
|
try {
|
|
14374
|
-
id = JSON.parse(
|
|
14508
|
+
id = JSON.parse(fs11.readFileSync(this.paths().workspace, "utf8")).id ?? null;
|
|
14375
14509
|
} catch {}
|
|
14376
14510
|
this.writeTombstone(v2, id);
|
|
14377
14511
|
} catch {}
|
|
14378
14512
|
}
|
|
14379
14513
|
initializeStorage() {
|
|
14380
14514
|
const dirs = this.paths();
|
|
14381
|
-
retryTransient(() =>
|
|
14382
|
-
retryTransient(() =>
|
|
14383
|
-
if (!
|
|
14515
|
+
retryTransient(() => fs11.mkdirSync(dirs.checkout, { recursive: true }));
|
|
14516
|
+
retryTransient(() => fs11.mkdirSync(dirs.tasks, { recursive: true }));
|
|
14517
|
+
if (!fs11.existsSync(dirs.marker))
|
|
14384
14518
|
replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
|
|
14385
14519
|
`, false);
|
|
14386
14520
|
if (!dirs.shared) {
|
|
14387
|
-
const ignore =
|
|
14521
|
+
const ignore = path14.join(dirs.dir, ".gitignore");
|
|
14388
14522
|
let current = null;
|
|
14389
14523
|
try {
|
|
14390
|
-
current =
|
|
14524
|
+
current = fs11.readFileSync(ignore, "utf8");
|
|
14391
14525
|
} catch {}
|
|
14392
14526
|
if (current !== `*
|
|
14393
14527
|
`)
|
|
14394
|
-
retryTransient(() =>
|
|
14528
|
+
retryTransient(() => fs11.writeFileSync(ignore, `*
|
|
14395
14529
|
`));
|
|
14396
14530
|
}
|
|
14397
14531
|
this.writeTombstoneIfMissing();
|
|
@@ -14407,7 +14541,7 @@ class TaskStore {
|
|
|
14407
14541
|
shouldReclaim: ({ payload, nowMs }) => {
|
|
14408
14542
|
let ageMs = null;
|
|
14409
14543
|
try {
|
|
14410
|
-
ageMs = nowMs -
|
|
14544
|
+
ageMs = nowMs - fs11.lstatSync(lockPath).mtimeMs;
|
|
14411
14545
|
} catch {}
|
|
14412
14546
|
return classifyLockOwner(payload, ageMs, localLockHost(), TASK_STORE_LOCK).state === "stale";
|
|
14413
14547
|
},
|
|
@@ -14444,7 +14578,7 @@ class TaskStore {
|
|
|
14444
14578
|
withLock(lockPath, operation) {
|
|
14445
14579
|
try {
|
|
14446
14580
|
this.initializeStorage();
|
|
14447
|
-
|
|
14581
|
+
fs11.mkdirSync(path14.dirname(lockPath), { recursive: true });
|
|
14448
14582
|
} catch (error) {
|
|
14449
14583
|
return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
|
|
14450
14584
|
path: this.paths().dir
|
|
@@ -14484,18 +14618,18 @@ class TaskStore {
|
|
|
14484
14618
|
lockDirWritable(lockPath) {
|
|
14485
14619
|
const probe = `${lockPath}.${process.pid}.${randomUUID3()}.probe`;
|
|
14486
14620
|
try {
|
|
14487
|
-
|
|
14621
|
+
fs11.closeSync(fs11.openSync(probe, "wx", 384));
|
|
14488
14622
|
} catch {
|
|
14489
14623
|
return false;
|
|
14490
14624
|
}
|
|
14491
14625
|
try {
|
|
14492
|
-
|
|
14626
|
+
fs11.rmSync(probe, { force: true });
|
|
14493
14627
|
} catch {}
|
|
14494
14628
|
return true;
|
|
14495
14629
|
}
|
|
14496
14630
|
lockHolderPresent(lockPath) {
|
|
14497
14631
|
try {
|
|
14498
|
-
|
|
14632
|
+
fs11.lstatSync(lockPath);
|
|
14499
14633
|
return true;
|
|
14500
14634
|
} catch (error) {
|
|
14501
14635
|
return error?.code !== "ENOENT";
|
|
@@ -14507,12 +14641,12 @@ class TaskStore {
|
|
|
14507
14641
|
if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
|
|
14508
14642
|
return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
|
|
14509
14643
|
path: lockPath,
|
|
14510
|
-
guidance: `Check permissions and the read-only attribute on ${
|
|
14644
|
+
guidance: `Check permissions and the read-only attribute on ${path14.dirname(lockPath)}.`
|
|
14511
14645
|
});
|
|
14512
14646
|
if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
|
|
14513
14647
|
let lock = null;
|
|
14514
14648
|
try {
|
|
14515
|
-
lock = parseMetadataLockOrNull(
|
|
14649
|
+
lock = parseMetadataLockOrNull(fs11.readFileSync(lockPath, "utf8"));
|
|
14516
14650
|
} catch {}
|
|
14517
14651
|
return failure2("busy", `workit lock is held by another Workit call${lock ? ` (pid ${lock.pid} on ${lock.host})` : ""}; retry shortly`, {
|
|
14518
14652
|
outcome: "not_started",
|
|
@@ -14531,15 +14665,15 @@ var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
|
|
|
14531
14665
|
var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
|
|
14532
14666
|
|
|
14533
14667
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
14534
|
-
import { createHash as
|
|
14535
|
-
import * as
|
|
14668
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
14669
|
+
import * as fs13 from "node:fs";
|
|
14536
14670
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
14537
|
-
import
|
|
14671
|
+
import path16 from "node:path";
|
|
14538
14672
|
|
|
14539
14673
|
// packages/workit-core/src/check-config.ts
|
|
14540
14674
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
14541
|
-
import
|
|
14542
|
-
import
|
|
14675
|
+
import fs12 from "node:fs";
|
|
14676
|
+
import path15 from "node:path";
|
|
14543
14677
|
var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
|
|
14544
14678
|
var CHECKS_FILE = "workit.checks.json";
|
|
14545
14679
|
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -14592,10 +14726,10 @@ var git4 = (cwd, args) => {
|
|
|
14592
14726
|
return value || null;
|
|
14593
14727
|
};
|
|
14594
14728
|
function checkRoot(cwd) {
|
|
14595
|
-
return
|
|
14729
|
+
return path15.resolve(git4(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
|
|
14596
14730
|
}
|
|
14597
14731
|
var packageManager = (root) => {
|
|
14598
|
-
const has = (file) =>
|
|
14732
|
+
const has = (file) => fs12.existsSync(path15.join(root, file));
|
|
14599
14733
|
if (has("bun.lock") || has("bun.lockb"))
|
|
14600
14734
|
return "bun";
|
|
14601
14735
|
if (has("pnpm-lock.yaml"))
|
|
@@ -14615,7 +14749,7 @@ var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
|
|
|
14615
14749
|
function detectedChecks(root) {
|
|
14616
14750
|
const read = (file) => {
|
|
14617
14751
|
try {
|
|
14618
|
-
return
|
|
14752
|
+
return fs12.readFileSync(path15.join(root, file), "utf8");
|
|
14619
14753
|
} catch {
|
|
14620
14754
|
return null;
|
|
14621
14755
|
}
|
|
@@ -14654,7 +14788,7 @@ function detectedChecks(root) {
|
|
|
14654
14788
|
var GATES = ["testing", "verification"];
|
|
14655
14789
|
function loadCheckConfig(cwd) {
|
|
14656
14790
|
const root = checkRoot(cwd);
|
|
14657
|
-
const file =
|
|
14791
|
+
const file = path15.join(root, CHECKS_FILE);
|
|
14658
14792
|
const none = (error = null) => ({
|
|
14659
14793
|
source: error ? CHECKS_FILE : "none",
|
|
14660
14794
|
root,
|
|
@@ -14664,7 +14798,7 @@ function loadCheckConfig(cwd) {
|
|
|
14664
14798
|
});
|
|
14665
14799
|
let text = null;
|
|
14666
14800
|
try {
|
|
14667
|
-
text =
|
|
14801
|
+
text = fs12.readFileSync(file, "utf8");
|
|
14668
14802
|
} catch (error) {
|
|
14669
14803
|
if (error.code !== "ENOENT")
|
|
14670
14804
|
return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
|
|
@@ -14727,10 +14861,10 @@ function workitAvailable(env = process.env) {
|
|
|
14727
14861
|
return workitOnPath;
|
|
14728
14862
|
const windows = process.platform === "win32";
|
|
14729
14863
|
const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
|
|
14730
|
-
const dirs = (env.PATH ?? env.Path ?? "").split(
|
|
14864
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(path15.delimiter).filter(Boolean);
|
|
14731
14865
|
workitOnPath = dirs.some((dir) => exts.some((ext) => {
|
|
14732
14866
|
try {
|
|
14733
|
-
return
|
|
14867
|
+
return fs12.statSync(path15.join(dir, `workit${ext}`)).isFile();
|
|
14734
14868
|
} catch {
|
|
14735
14869
|
return false;
|
|
14736
14870
|
}
|
|
@@ -14742,15 +14876,15 @@ function checkCommand(args, version, onPath = workitAvailable()) {
|
|
|
14742
14876
|
}
|
|
14743
14877
|
|
|
14744
14878
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
14745
|
-
var digestBytes = (value) =>
|
|
14746
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
14747
|
-
var relative = (root, candidate) =>
|
|
14879
|
+
var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
|
|
14880
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path16.sep}`);
|
|
14881
|
+
var relative = (root, candidate) => path16.relative(root, candidate).split(path16.sep).join("/") || ".";
|
|
14748
14882
|
var canonicalPath = (value) => {
|
|
14749
14883
|
if (typeof value !== "string" || !value)
|
|
14750
14884
|
return null;
|
|
14751
14885
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
14752
14886
|
return null;
|
|
14753
|
-
const normalized =
|
|
14887
|
+
const normalized = path16.posix.normalize(value);
|
|
14754
14888
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
14755
14889
|
return null;
|
|
14756
14890
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -14798,21 +14932,21 @@ var gitPaths = (root) => {
|
|
|
14798
14932
|
git: !/not a git repository/i.test(stderr)
|
|
14799
14933
|
};
|
|
14800
14934
|
}
|
|
14801
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
14935
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")) : [];
|
|
14802
14936
|
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
14803
14937
|
if (stagedDeleted.status !== 0) {
|
|
14804
14938
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
14805
14939
|
if (!/not a git repository/i.test(stderr))
|
|
14806
14940
|
return { paths, uncertain: true, git: true };
|
|
14807
14941
|
} else if (stagedDeleted.stdout) {
|
|
14808
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
14942
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")));
|
|
14809
14943
|
}
|
|
14810
14944
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
14811
14945
|
};
|
|
14812
14946
|
var walk = (root, directory, output) => {
|
|
14813
14947
|
let names;
|
|
14814
14948
|
try {
|
|
14815
|
-
names =
|
|
14949
|
+
names = fs13.readdirSync(directory);
|
|
14816
14950
|
} catch {
|
|
14817
14951
|
return true;
|
|
14818
14952
|
}
|
|
@@ -14820,10 +14954,10 @@ var walk = (root, directory, output) => {
|
|
|
14820
14954
|
for (const name of names) {
|
|
14821
14955
|
if (name === ".git" || name === ".workit")
|
|
14822
14956
|
continue;
|
|
14823
|
-
const target =
|
|
14957
|
+
const target = path16.join(directory, name);
|
|
14824
14958
|
let stat;
|
|
14825
14959
|
try {
|
|
14826
|
-
stat =
|
|
14960
|
+
stat = fs13.lstatSync(target);
|
|
14827
14961
|
} catch {
|
|
14828
14962
|
uncertain = true;
|
|
14829
14963
|
continue;
|
|
@@ -14838,14 +14972,14 @@ var walk = (root, directory, output) => {
|
|
|
14838
14972
|
var scopeRoots = (root, scope) => {
|
|
14839
14973
|
const roots = [];
|
|
14840
14974
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
14841
|
-
const target =
|
|
14975
|
+
const target = path16.resolve(root, item);
|
|
14842
14976
|
if (!inside(root, target))
|
|
14843
14977
|
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` }] });
|
|
14844
14978
|
let ancestor = target;
|
|
14845
|
-
while (!
|
|
14846
|
-
ancestor =
|
|
14979
|
+
while (!fs13.existsSync(ancestor) && ancestor !== root)
|
|
14980
|
+
ancestor = path16.dirname(ancestor);
|
|
14847
14981
|
try {
|
|
14848
|
-
if (!inside(root,
|
|
14982
|
+
if (!inside(root, fs13.realpathSync(ancestor)))
|
|
14849
14983
|
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` }] });
|
|
14850
14984
|
} catch {
|
|
14851
14985
|
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
@@ -14857,7 +14991,7 @@ var scopeRoots = (root, scope) => {
|
|
|
14857
14991
|
function captureCandidate(root, scope, environment = []) {
|
|
14858
14992
|
let checkout;
|
|
14859
14993
|
try {
|
|
14860
|
-
checkout =
|
|
14994
|
+
checkout = fs13.realpathSync(root);
|
|
14861
14995
|
} catch {
|
|
14862
14996
|
return failure2("invalid_input", "candidate root does not exist");
|
|
14863
14997
|
}
|
|
@@ -14875,11 +15009,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
14875
15009
|
if (item === ".git" || item === ".workit")
|
|
14876
15010
|
continue;
|
|
14877
15011
|
names.add(item);
|
|
14878
|
-
if (!
|
|
15012
|
+
if (!fs13.existsSync(target))
|
|
14879
15013
|
continue;
|
|
14880
15014
|
let stat;
|
|
14881
15015
|
try {
|
|
14882
|
-
stat =
|
|
15016
|
+
stat = fs13.lstatSync(target);
|
|
14883
15017
|
} catch {
|
|
14884
15018
|
uncertain = true;
|
|
14885
15019
|
continue;
|
|
@@ -14895,17 +15029,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
14895
15029
|
for (const item of [...names].toSorted()) {
|
|
14896
15030
|
if (!scopeMatches(item, normalizedScope))
|
|
14897
15031
|
continue;
|
|
14898
|
-
const target =
|
|
15032
|
+
const target = path16.join(checkout, item);
|
|
14899
15033
|
try {
|
|
14900
|
-
const stat =
|
|
15034
|
+
const stat = fs13.lstatSync(target);
|
|
14901
15035
|
if (stat.isSymbolicLink()) {
|
|
14902
|
-
const link =
|
|
15036
|
+
const link = fs13.readlinkSync(target);
|
|
14903
15037
|
files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
|
|
14904
15038
|
} else if (stat.isFile()) {
|
|
14905
15039
|
files.push({
|
|
14906
15040
|
path: item,
|
|
14907
15041
|
kind: "file",
|
|
14908
|
-
digest: digestBytes(
|
|
15042
|
+
digest: digestBytes(fs13.readFileSync(target)),
|
|
14909
15043
|
executable: (stat.mode & 73) !== 0
|
|
14910
15044
|
});
|
|
14911
15045
|
} else if (!stat.isDirectory()) {
|
|
@@ -15070,7 +15204,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
|
|
|
15070
15204
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
15071
15205
|
let root;
|
|
15072
15206
|
try {
|
|
15073
|
-
root =
|
|
15207
|
+
root = fs13.realpathSync(checkoutRoot);
|
|
15074
15208
|
} catch {
|
|
15075
15209
|
return failure2("permission_denied", "checkout root is unavailable");
|
|
15076
15210
|
}
|
|
@@ -15079,23 +15213,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
15079
15213
|
continue;
|
|
15080
15214
|
if (!reference.digest)
|
|
15081
15215
|
return failure2("invalid_input", "document references require a byte digest");
|
|
15082
|
-
const target =
|
|
15083
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
15216
|
+
const target = path16.resolve(root, reference.path);
|
|
15217
|
+
if (target !== root && !target.startsWith(`${root}${path16.sep}`))
|
|
15084
15218
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
15085
15219
|
fields: [
|
|
15086
15220
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
15087
15221
|
]
|
|
15088
15222
|
});
|
|
15089
15223
|
try {
|
|
15090
|
-
const below =
|
|
15224
|
+
const below = path16.relative(root, target);
|
|
15091
15225
|
let current = root;
|
|
15092
|
-
for (const segment of below.split(
|
|
15226
|
+
for (const segment of below.split(path16.sep)) {
|
|
15093
15227
|
if (!segment)
|
|
15094
15228
|
continue;
|
|
15095
|
-
current =
|
|
15096
|
-
const stat =
|
|
15097
|
-
const real =
|
|
15098
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
15229
|
+
current = path16.join(current, segment);
|
|
15230
|
+
const stat = fs13.lstatSync(current);
|
|
15231
|
+
const real = fs13.realpathSync(current);
|
|
15232
|
+
if (real !== root && !real.startsWith(`${root}${path16.sep}`))
|
|
15099
15233
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
15100
15234
|
fields: [
|
|
15101
15235
|
{
|
|
@@ -15111,7 +15245,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
15111
15245
|
if (current !== target && !stat.isDirectory())
|
|
15112
15246
|
return failure2("invalid_input", "document reference ancestor is not a directory");
|
|
15113
15247
|
}
|
|
15114
|
-
if (digestBytes(
|
|
15248
|
+
if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
|
|
15115
15249
|
return failure2("permission_denied", "decision document bytes have changed");
|
|
15116
15250
|
} catch {
|
|
15117
15251
|
return failure2("invalid_input", "decision document is unavailable");
|
|
@@ -15480,7 +15614,8 @@ Autonomy contract:
|
|
|
15480
15614
|
satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
|
|
15481
15615
|
- Author is not verifier: your own verdict is \`--self\`, shown as
|
|
15482
15616
|
self-reviewed, never verified. High risk, \`verification: independent\` and
|
|
15483
|
-
merge need a verifier with its own session (
|
|
15617
|
+
merge need a verifier with its own host-derived session (a subagent's
|
|
15618
|
+
\`<lead>:<child>\`, set by the host); never hand-mint one such as \`<lead>-v1\`.
|
|
15484
15619
|
- A local-commit endpoint does not imply PR readiness, and a local commit alone
|
|
15485
15620
|
is not evidence of a requested remote push. Before reporting delivery,
|
|
15486
15621
|
reconcile every requested item and observe it (\`workit verify-delivery\`).
|
|
@@ -15573,38 +15708,45 @@ var compactReference = (ref) => {
|
|
|
15573
15708
|
};
|
|
15574
15709
|
return { kind: ref.kind };
|
|
15575
15710
|
};
|
|
15576
|
-
|
|
15577
|
-
const
|
|
15578
|
-
|
|
15579
|
-
|
|
15580
|
-
|
|
15581
|
-
|
|
15582
|
-
|
|
15583
|
-
|
|
15584
|
-
|
|
15585
|
-
|
|
15586
|
-
|
|
15587
|
-
|
|
15588
|
-
|
|
15589
|
-
|
|
15590
|
-
|
|
15591
|
-
|
|
15711
|
+
var newestDecisionFirst = (left, right) => {
|
|
15712
|
+
const leftAt = compactTimestamp(left.recordedAt);
|
|
15713
|
+
const rightAt = compactTimestamp(right.recordedAt);
|
|
15714
|
+
if (leftAt !== rightAt)
|
|
15715
|
+
return leftAt > rightAt ? -1 : 1;
|
|
15716
|
+
return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
|
|
15717
|
+
};
|
|
15718
|
+
var compactDecisions = (sources) => sources.flatMap((source) => source ? source.decisions.toSorted(newestDecisionFirst).map((entry) => ({
|
|
15719
|
+
id: entry.id,
|
|
15720
|
+
purpose: entry.data.purpose,
|
|
15721
|
+
status: entry.data.response,
|
|
15722
|
+
digest: entry.data.digest,
|
|
15723
|
+
choice: compactDecisionChoice(entry) || `task.inspect taskId=${source.taskId} view=full; decision=${entry.id}`,
|
|
15724
|
+
references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference),
|
|
15725
|
+
origin: source.origin ?? undefined
|
|
15726
|
+
})) : []).slice(0, COMPACT_MAX_ITEMS);
|
|
15727
|
+
function compactTaskContext(view, inherited = null) {
|
|
15728
|
+
const decisions = compactDecisions([
|
|
15729
|
+
view && { origin: null, taskId: view.task.id, decisions: view.task.decisions },
|
|
15730
|
+
inherited
|
|
15731
|
+
]);
|
|
15732
|
+
const methods = view?.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
|
|
15592
15733
|
id: method.id,
|
|
15593
15734
|
assurance: method.assurance,
|
|
15594
15735
|
reason: compactText(method.reason, COMPACT_TEXT_BYTES) ?? ""
|
|
15595
15736
|
})) : [];
|
|
15737
|
+
const stale = view?.evidence.filter((entry) => entry.status === "stale").length;
|
|
15596
15738
|
const gaps = Array.from(new Set([
|
|
15597
|
-
...
|
|
15598
|
-
...view
|
|
15599
|
-
...view
|
|
15739
|
+
...stale ? [`stale evidence: ${stale}; workit task inspect --task ${view.task.id} --view full`] : [],
|
|
15740
|
+
...(view?.requirements ?? []).filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
|
|
15741
|
+
...(view?.task.progress.blockers ?? []).map((entry) => entry.reason)
|
|
15600
15742
|
])).toSorted().slice(0, COMPACT_MAX_ITEMS).map((gap) => compactText(gap, COMPACT_TEXT_BYTES));
|
|
15601
15743
|
const context = {
|
|
15602
|
-
objective: compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "",
|
|
15603
|
-
status: view
|
|
15744
|
+
objective: view ? compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "" : "no task on this branch yet",
|
|
15745
|
+
status: view?.task.status ?? null,
|
|
15604
15746
|
decisions,
|
|
15605
15747
|
methods,
|
|
15606
15748
|
gaps,
|
|
15607
|
-
nextAction: compactText(view
|
|
15749
|
+
nextAction: compactText(view?.task.progress.nextAction ?? null, COMPACT_TEXT_BYTES)
|
|
15608
15750
|
};
|
|
15609
15751
|
let encoded = JSON.stringify(context);
|
|
15610
15752
|
while (compactBytes(encoded) > COMPACT_MAX_BYTES && (context.decisions.length || context.methods.length || context.gaps.length)) {
|
|
@@ -15793,6 +15935,13 @@ var TASK_SCOPED_ACTIONS = new Set([
|
|
|
15793
15935
|
"worker.cancel",
|
|
15794
15936
|
"state.export"
|
|
15795
15937
|
]);
|
|
15938
|
+
var WORKER_ADDRESSED_ACTIONS = new Set(["worker.cancel", "worker.report"]);
|
|
15939
|
+
var workerCommand = (taskId, request) => {
|
|
15940
|
+
const { action, workerId, reason, report } = request;
|
|
15941
|
+
const rest = JSON.stringify({ reason, report });
|
|
15942
|
+
const payload = rest === "{}" ? "" : ` --payload '${rest.replace(/'/gu, "'\\''")}'`;
|
|
15943
|
+
return `workit worker ${String(action)} ${String(workerId)} --task ${taskId}${payload}`;
|
|
15944
|
+
};
|
|
15796
15945
|
var CREATING_ACTIONS = new Set([
|
|
15797
15946
|
"task.revise",
|
|
15798
15947
|
"task.progress",
|
|
@@ -16043,6 +16192,17 @@ class WorkitCore {
|
|
|
16043
16192
|
const root = this.contextRootError();
|
|
16044
16193
|
if (!root.ok)
|
|
16045
16194
|
return root;
|
|
16195
|
+
const addressed = WORKER_ADDRESSED_ACTIONS.has(action);
|
|
16196
|
+
const workerId = addressed ? value.workerId : this.context.workerId ?? null;
|
|
16197
|
+
if (workerId !== null) {
|
|
16198
|
+
const owner = this.store.taskForWorker(workerId);
|
|
16199
|
+
if (!owner.ok)
|
|
16200
|
+
return owner;
|
|
16201
|
+
if (owner.data)
|
|
16202
|
+
return success(null, null, { ...request, taskId: owner.data });
|
|
16203
|
+
if (addressed)
|
|
16204
|
+
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)}\``);
|
|
16205
|
+
}
|
|
16046
16206
|
const create = CREATING_ACTIONS.has(action) && (this.context.workerId ?? null) === null;
|
|
16047
16207
|
const found = this.store.implicitTask({
|
|
16048
16208
|
provenance: provenance(this.context),
|
|
@@ -16749,7 +16909,9 @@ class WorkitCore {
|
|
|
16749
16909
|
if (!resolved.ok)
|
|
16750
16910
|
return resolved;
|
|
16751
16911
|
const value = resolved.data;
|
|
16752
|
-
|
|
16912
|
+
const result = this.retryOmittedRevisions(value, () => this.workerOnce(value));
|
|
16913
|
+
const { action, taskId } = value ?? {};
|
|
16914
|
+
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)}\`` };
|
|
16753
16915
|
}
|
|
16754
16916
|
workerOnce(request) {
|
|
16755
16917
|
const root = this.contextRootError();
|
|
@@ -16954,10 +17116,18 @@ class WorkitCore {
|
|
|
16954
17116
|
requirements
|
|
16955
17117
|
});
|
|
16956
17118
|
}
|
|
16957
|
-
compactContext(taskId, freshness) {
|
|
17119
|
+
compactContext(taskId, freshness, origin = null) {
|
|
16958
17120
|
const root = this.contextRootError();
|
|
16959
17121
|
if (!root.ok)
|
|
16960
17122
|
return root;
|
|
17123
|
+
const base = origin && this.store.readStoreTask(origin.taskId);
|
|
17124
|
+
const inherited = base?.ok && base.data ? {
|
|
17125
|
+
origin: origin.origin,
|
|
17126
|
+
taskId: base.data.id,
|
|
17127
|
+
decisions: base.data.decisions.filter((entry) => entry.data.revoked === null)
|
|
17128
|
+
} : null;
|
|
17129
|
+
if (taskId === null)
|
|
17130
|
+
return success(null, null, inherited?.decisions.length ? compactTaskContext(null, inherited) : "");
|
|
16961
17131
|
const task = this.store.readTask(taskId);
|
|
16962
17132
|
if (!task.ok)
|
|
16963
17133
|
return task;
|
|
@@ -16967,7 +17137,7 @@ class WorkitCore {
|
|
|
16967
17137
|
const view = this.view(task.data, false, freshness ?? signalFreshness(this.store.root));
|
|
16968
17138
|
if (!view.ok)
|
|
16969
17139
|
return view;
|
|
16970
|
-
return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
|
|
17140
|
+
return success(view.revision, view.workspaceRevision, compactTaskContext(view.data, inherited));
|
|
16971
17141
|
}
|
|
16972
17142
|
view(task, capture = true, freshness = treeFreshness(this.store.root)) {
|
|
16973
17143
|
const workspace = this.store.readWorkspace();
|
|
@@ -17273,9 +17443,9 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
17273
17443
|
};
|
|
17274
17444
|
});
|
|
17275
17445
|
// packages/workit-core/src/hooks/context.ts
|
|
17276
|
-
import { createHash as
|
|
17277
|
-
import
|
|
17278
|
-
import
|
|
17446
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
17447
|
+
import fs14 from "node:fs";
|
|
17448
|
+
import path17 from "node:path";
|
|
17279
17449
|
var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
|
|
17280
17450
|
var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
|
|
17281
17451
|
var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
|
|
@@ -17288,8 +17458,9 @@ var currentTaskEntry = (entries, session, selection) => {
|
|
|
17288
17458
|
const active = entries.filter((entry) => entry.status === "active");
|
|
17289
17459
|
return active.length === 1 ? active[0] : null;
|
|
17290
17460
|
};
|
|
17461
|
+
var otherConversation = (entry, session) => entry.sessions.some((item) => item.host !== "workit_cli" && !(item.host === session.host && item.handle === session.handle));
|
|
17291
17462
|
var implicitTaskEntry = (store, entries, session) => {
|
|
17292
|
-
const open = entries.filter((entry) => entry.key !== null && entry.status !== "closed" && !entry
|
|
17463
|
+
const open = entries.filter((entry) => entry.key !== null && entry.status !== "closed" && !otherConversation(entry, session));
|
|
17293
17464
|
if (open.length === 0)
|
|
17294
17465
|
return null;
|
|
17295
17466
|
const key = store.currentKey();
|
|
@@ -17330,18 +17501,23 @@ function sessionCompactContext(store, session, context, selection = "session-bou
|
|
|
17330
17501
|
if (!listed.ok)
|
|
17331
17502
|
return migrationPending(listed) ? MIGRATION_PENDING_NOTE : null;
|
|
17332
17503
|
const entry = currentTaskEntry(listed.data, session, selection) ?? implicitTaskEntry(store, listed.data, session);
|
|
17333
|
-
return
|
|
17504
|
+
return entryCompactContext(store, entry, session, context);
|
|
17334
17505
|
}
|
|
17335
17506
|
function entryCompactContext(store, entry, session, context) {
|
|
17507
|
+
const found = store.originTaskEntry();
|
|
17508
|
+
const origin = found.ok && found.data && found.data.entry.id !== entry?.id && !otherConversation(found.data.entry, session) ? found.data : null;
|
|
17509
|
+
if (!entry && !origin)
|
|
17510
|
+
return null;
|
|
17336
17511
|
const workspace = store.readWorkspace();
|
|
17337
|
-
if (!workspace.ok || !workspace.data)
|
|
17512
|
+
if (!workspace.ok || entry && !workspace.data)
|
|
17338
17513
|
return null;
|
|
17339
17514
|
const slot = `${store.root}\x00${session.host}\x00${session.handle}`;
|
|
17340
17515
|
const key = canonicalJson({
|
|
17341
|
-
task: entry
|
|
17342
|
-
revision: entry
|
|
17343
|
-
file: entry
|
|
17344
|
-
|
|
17516
|
+
task: entry?.id ?? null,
|
|
17517
|
+
revision: entry?.revision ?? null,
|
|
17518
|
+
file: entry?.file ?? null,
|
|
17519
|
+
origin: origin && origin.entry.file,
|
|
17520
|
+
workspace: workspace.data?.revision ?? null,
|
|
17345
17521
|
caller: context.caller,
|
|
17346
17522
|
workerId: context.workerId ?? null,
|
|
17347
17523
|
capabilities: context.capabilities
|
|
@@ -17349,15 +17525,15 @@ function entryCompactContext(store, entry, session, context) {
|
|
|
17349
17525
|
const hit = cache.get(slot);
|
|
17350
17526
|
if (hit && hit.key === key && hit.filesKey !== null && hit.filesKey === filesKey(hit.files) && (hit.signal === null || hit.signal === recentSignal(store.root)))
|
|
17351
17527
|
return hit.value;
|
|
17352
|
-
const task = store.readTask(entry.id);
|
|
17353
|
-
if (!task.ok)
|
|
17528
|
+
const task = entry ? store.readTask(entry.id) : null;
|
|
17529
|
+
if (task && !task.ok)
|
|
17354
17530
|
return null;
|
|
17355
17531
|
const freshness = { mode: "signal", current: () => recentSignal(store.root) };
|
|
17356
|
-
const observed = task
|
|
17357
|
-
const files = task
|
|
17532
|
+
const observed = task?.data.evidence.some(isObservedCheck) ?? false;
|
|
17533
|
+
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path17.resolve(store.root, ref.path)] : []));
|
|
17358
17534
|
const observedFiles = filesKey(files);
|
|
17359
|
-
const compact = new WorkitCore(store, context).compactContext(entry
|
|
17360
|
-
if (!compact.ok)
|
|
17535
|
+
const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
|
|
17536
|
+
if (!compact.ok || !compact.data)
|
|
17361
17537
|
return null;
|
|
17362
17538
|
cache.delete(slot);
|
|
17363
17539
|
cache.set(slot, {
|
|
@@ -17396,7 +17572,7 @@ ${options.addendum}` : ""}
|
|
|
17396
17572
|
if (!listed.ok)
|
|
17397
17573
|
throw new Error(listed.error);
|
|
17398
17574
|
const entry = currentTaskEntry(listed.data, session, descriptor.context.task) ?? implicitTaskEntry(store, listed.data, session);
|
|
17399
|
-
const text =
|
|
17575
|
+
const text = entryCompactContext(store, entry, session, hookOperationContext(input, descriptor));
|
|
17400
17576
|
if (text)
|
|
17401
17577
|
compact = `
|
|
17402
17578
|
<workit-task-context>${text}</workit-task-context>`;
|
|
@@ -17423,7 +17599,7 @@ var turnContextText = (input, descriptor) => {
|
|
|
17423
17599
|
}
|
|
17424
17600
|
};
|
|
17425
17601
|
var TURN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
17426
|
-
var digestOf = (text) =>
|
|
17602
|
+
var digestOf = (text) => createHash11("sha256").update(text).digest("hex");
|
|
17427
17603
|
var turnMarker = (input) => {
|
|
17428
17604
|
if (!input.session.id)
|
|
17429
17605
|
return null;
|
|
@@ -17432,32 +17608,32 @@ var turnMarker = (input) => {
|
|
|
17432
17608
|
if (location instanceof Error)
|
|
17433
17609
|
return null;
|
|
17434
17610
|
const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
|
|
17435
|
-
return
|
|
17611
|
+
return path17.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
|
|
17436
17612
|
} catch {
|
|
17437
17613
|
return null;
|
|
17438
17614
|
}
|
|
17439
17615
|
};
|
|
17440
17616
|
var writeTurnDigest = (file, digest) => {
|
|
17441
17617
|
try {
|
|
17442
|
-
|
|
17443
|
-
|
|
17618
|
+
fs14.mkdirSync(path17.dirname(file), { recursive: true });
|
|
17619
|
+
fs14.writeFileSync(file, `${JSON.stringify({ digest })}
|
|
17444
17620
|
`);
|
|
17445
17621
|
} catch {}
|
|
17446
17622
|
};
|
|
17447
17623
|
var clearTurnDigest = (file) => {
|
|
17448
17624
|
try {
|
|
17449
|
-
|
|
17625
|
+
fs14.rmSync(file, { force: true });
|
|
17450
17626
|
} catch {}
|
|
17451
17627
|
};
|
|
17452
17628
|
var pruneTurnDigests = (dir, now) => {
|
|
17453
17629
|
try {
|
|
17454
|
-
for (const name of
|
|
17630
|
+
for (const name of fs14.readdirSync(dir)) {
|
|
17455
17631
|
if (!name.startsWith("turn-"))
|
|
17456
17632
|
continue;
|
|
17457
|
-
const file =
|
|
17633
|
+
const file = path17.join(dir, name);
|
|
17458
17634
|
try {
|
|
17459
|
-
if (now -
|
|
17460
|
-
|
|
17635
|
+
if (now - fs14.statSync(file).mtimeMs > TURN_TTL_MS)
|
|
17636
|
+
fs14.rmSync(file, { force: true });
|
|
17461
17637
|
} catch {}
|
|
17462
17638
|
}
|
|
17463
17639
|
} catch {}
|
|
@@ -17473,7 +17649,7 @@ var changedTurnContext = (input, text) => {
|
|
|
17473
17649
|
return text;
|
|
17474
17650
|
const digest = digestOf(text);
|
|
17475
17651
|
try {
|
|
17476
|
-
const previous = JSON.parse(
|
|
17652
|
+
const previous = JSON.parse(fs14.readFileSync(file, "utf8"));
|
|
17477
17653
|
if (previous.digest === digest)
|
|
17478
17654
|
return null;
|
|
17479
17655
|
} catch {}
|
|
@@ -17484,14 +17660,14 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
|
|
|
17484
17660
|
const file = turnMarker(input);
|
|
17485
17661
|
if (!file)
|
|
17486
17662
|
return;
|
|
17487
|
-
pruneTurnDigests(
|
|
17663
|
+
pruneTurnDigests(path17.dirname(file), Date.now());
|
|
17488
17664
|
if (text)
|
|
17489
17665
|
writeTurnDigest(file, digestOf(text));
|
|
17490
17666
|
else
|
|
17491
17667
|
clearTurnDigest(file);
|
|
17492
17668
|
};
|
|
17493
17669
|
// packages/workit-core/src/core/branch.ts
|
|
17494
|
-
import { createHash as
|
|
17670
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
17495
17671
|
var withReleaseTracks = (workspaceRoot, policy) => {
|
|
17496
17672
|
const read = workspaceReleaseTracks(workspaceRoot);
|
|
17497
17673
|
if (read.error)
|
|
@@ -17559,7 +17735,7 @@ var branchPolicySnapshotFor = (workspaceRoot) => {
|
|
|
17559
17735
|
const source = resolved.provenance.branchPolicy;
|
|
17560
17736
|
const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
|
|
17561
17737
|
const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
|
|
17562
|
-
const fingerprint =
|
|
17738
|
+
const fingerprint = createHash12("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
|
|
17563
17739
|
return {
|
|
17564
17740
|
branchPolicy,
|
|
17565
17741
|
namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
|
|
@@ -17608,7 +17784,7 @@ var shellPolicy = (cwd, command) => {
|
|
|
17608
17784
|
};
|
|
17609
17785
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
17610
17786
|
import { realpathSync as realpathSync5 } from "node:fs";
|
|
17611
|
-
import
|
|
17787
|
+
import path18 from "node:path";
|
|
17612
17788
|
|
|
17613
17789
|
// packages/workit-core/src/hooks/shell-words.ts
|
|
17614
17790
|
var closingQuote = (command, start, dialect) => {
|
|
@@ -17738,22 +17914,22 @@ var NONE2 = { kind: "none" };
|
|
|
17738
17914
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
17739
17915
|
var canonical2 = (file) => {
|
|
17740
17916
|
let rest = "";
|
|
17741
|
-
for (let dir =
|
|
17917
|
+
for (let dir = path18.resolve(file);; dir = path18.dirname(dir)) {
|
|
17742
17918
|
try {
|
|
17743
|
-
return
|
|
17919
|
+
return path18.join(realpathSync5(dir), rest);
|
|
17744
17920
|
} catch {
|
|
17745
|
-
if (
|
|
17746
|
-
return
|
|
17747
|
-
rest = rest ?
|
|
17921
|
+
if (path18.dirname(dir) === dir)
|
|
17922
|
+
return path18.resolve(file);
|
|
17923
|
+
rest = rest ? path18.join(path18.basename(dir), rest) : path18.basename(dir);
|
|
17748
17924
|
}
|
|
17749
17925
|
}
|
|
17750
17926
|
};
|
|
17751
17927
|
var exempt = (cwd, root, file, plans) => {
|
|
17752
17928
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
17753
|
-
const relative =
|
|
17754
|
-
if (relative.startsWith("..") ||
|
|
17929
|
+
const relative = path18.relative(fold(canonical2(root)), fold(canonical2(path18.resolve(cwd, file))));
|
|
17930
|
+
if (relative.startsWith("..") || path18.isAbsolute(relative))
|
|
17755
17931
|
return true;
|
|
17756
|
-
const posix = relative.split(
|
|
17932
|
+
const posix = relative.split(path18.sep).join("/");
|
|
17757
17933
|
const segments = posix.split("/");
|
|
17758
17934
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
17759
17935
|
};
|
|
@@ -17851,7 +18027,7 @@ var shellWrites = (command) => {
|
|
|
17851
18027
|
return { writes: true, targets: unknown ? null : targets };
|
|
17852
18028
|
};
|
|
17853
18029
|
var cache2 = new Map;
|
|
17854
|
-
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
18030
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path18.resolve(root, file)) ?? "-").join("\x00");
|
|
17855
18031
|
var gateFor = (cwd) => {
|
|
17856
18032
|
const store = new TaskStore(cwd);
|
|
17857
18033
|
const listed = store.listTaskIndex();
|
|
@@ -17911,15 +18087,15 @@ import { spawnSync as spawnSync12 } from "node:child_process";
|
|
|
17911
18087
|
|
|
17912
18088
|
// packages/workit-core/src/hooks/raw-git.ts
|
|
17913
18089
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
17914
|
-
import { createHash as
|
|
17915
|
-
import
|
|
18090
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
18091
|
+
import fs16 from "node:fs";
|
|
17916
18092
|
import os4 from "node:os";
|
|
17917
|
-
import
|
|
18093
|
+
import path20 from "node:path";
|
|
17918
18094
|
|
|
17919
18095
|
// packages/workit-core/src/hooks/session-marker.ts
|
|
17920
|
-
import { createHash as
|
|
17921
|
-
import
|
|
17922
|
-
import
|
|
18096
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
18097
|
+
import fs15 from "node:fs";
|
|
18098
|
+
import path19 from "node:path";
|
|
17923
18099
|
var sessionMarkerFile = (input, session, store) => {
|
|
17924
18100
|
let dir = store;
|
|
17925
18101
|
if (dir === undefined) {
|
|
@@ -17928,13 +18104,13 @@ var sessionMarkerFile = (input, session, store) => {
|
|
|
17928
18104
|
return null;
|
|
17929
18105
|
dir = location.dir;
|
|
17930
18106
|
}
|
|
17931
|
-
const name =
|
|
17932
|
-
return
|
|
18107
|
+
const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
18108
|
+
return path19.join(dir, "hooks", `skills-${name}.json`);
|
|
17933
18109
|
};
|
|
17934
18110
|
var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
17935
18111
|
var readSessionMarker = (file) => {
|
|
17936
18112
|
try {
|
|
17937
|
-
const value = JSON.parse(
|
|
18113
|
+
const value = JSON.parse(fs15.readFileSync(file, "utf8"));
|
|
17938
18114
|
return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
|
|
17939
18115
|
} catch {
|
|
17940
18116
|
return { nudged: [], loaded: [], raw: [] };
|
|
@@ -17942,8 +18118,8 @@ var readSessionMarker = (file) => {
|
|
|
17942
18118
|
};
|
|
17943
18119
|
var writeSessionMarker = (file, marker) => {
|
|
17944
18120
|
try {
|
|
17945
|
-
|
|
17946
|
-
|
|
18121
|
+
fs15.mkdirSync(path19.dirname(file), { recursive: true });
|
|
18122
|
+
fs15.writeFileSync(file, JSON.stringify(marker));
|
|
17947
18123
|
} catch {}
|
|
17948
18124
|
};
|
|
17949
18125
|
|
|
@@ -17964,12 +18140,12 @@ var GIT_VALUE_OPTIONS = new Set([
|
|
|
17964
18140
|
"--config-env",
|
|
17965
18141
|
"--super-prefix"
|
|
17966
18142
|
]);
|
|
17967
|
-
var isAbsolute = (value) =>
|
|
18143
|
+
var isAbsolute = (value) => path20.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
|
|
17968
18144
|
var joinDir = (base, next) => {
|
|
17969
18145
|
if (base === undefined || /[$`*?]/.test(next) || next === "-")
|
|
17970
18146
|
return;
|
|
17971
|
-
const expanded = next === "~" || next.startsWith("~/") ?
|
|
17972
|
-
return base === null || isAbsolute(expanded) ? expanded :
|
|
18147
|
+
const expanded = next === "~" || next.startsWith("~/") ? path20.join(os4.homedir(), next.slice(1)) : next;
|
|
18148
|
+
return base === null || isAbsolute(expanded) ? expanded : path20.join(base, expanded);
|
|
17973
18149
|
};
|
|
17974
18150
|
var cdTarget = (words) => {
|
|
17975
18151
|
const args = words.slice(1);
|
|
@@ -18222,7 +18398,7 @@ var repoAt = (dir) => {
|
|
|
18222
18398
|
return { location, dir, branch: resolveTaskKey(dir, location).branch };
|
|
18223
18399
|
};
|
|
18224
18400
|
var isWorkitWorkspace = (repo) => {
|
|
18225
|
-
if (
|
|
18401
|
+
if (fs16.existsSync(repo.location.dir))
|
|
18226
18402
|
return true;
|
|
18227
18403
|
try {
|
|
18228
18404
|
return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
|
|
@@ -18253,17 +18429,17 @@ var headSha2 = (repo) => {
|
|
|
18253
18429
|
if (!git || git.reftable)
|
|
18254
18430
|
return null;
|
|
18255
18431
|
try {
|
|
18256
|
-
const head =
|
|
18432
|
+
const head = fs16.readFileSync(path20.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
18257
18433
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
18258
18434
|
return head;
|
|
18259
18435
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
18260
18436
|
if (!ref)
|
|
18261
18437
|
return null;
|
|
18262
|
-
const common =
|
|
18438
|
+
const common = path20.dirname(repo.location.dir);
|
|
18263
18439
|
try {
|
|
18264
|
-
return
|
|
18440
|
+
return fs16.readFileSync(path20.join(common, ref), "utf8").trim() || null;
|
|
18265
18441
|
} catch {}
|
|
18266
|
-
const packed =
|
|
18442
|
+
const packed = fs16.readFileSync(path20.join(common, "packed-refs"), "utf8");
|
|
18267
18443
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
18268
18444
|
} catch {
|
|
18269
18445
|
return null;
|
|
@@ -18300,7 +18476,7 @@ var actionsIn = (cwd, command, dialect) => {
|
|
|
18300
18476
|
const action = rawAction(invocation);
|
|
18301
18477
|
if (!action)
|
|
18302
18478
|
continue;
|
|
18303
|
-
const dir =
|
|
18479
|
+
const dir = path20.resolve(cwd, invocation.dir ?? ".");
|
|
18304
18480
|
if (!repos.has(dir)) {
|
|
18305
18481
|
const repo = repoAt(dir);
|
|
18306
18482
|
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
@@ -18369,8 +18545,8 @@ function rawGitPre(input, command, options = {}) {
|
|
|
18369
18545
|
var deny = (reason) => ({ kind: "deny", reason, unblock: null });
|
|
18370
18546
|
var NEXT_COMMAND = "next";
|
|
18371
18547
|
var MARKER_TTL_SECONDS = 3600;
|
|
18372
|
-
var markerFile = (repo, session, key) =>
|
|
18373
|
-
var callKey = (toolUseId, command) => toolUseId ?? `cmd:${
|
|
18548
|
+
var markerFile = (repo, session, key) => path20.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
|
|
18549
|
+
var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`;
|
|
18374
18550
|
function noteRawCommit(input, command, key) {
|
|
18375
18551
|
try {
|
|
18376
18552
|
if (!input.session.id || !/\bcommit\b/.test(command))
|
|
@@ -18380,8 +18556,8 @@ function noteRawCommit(input, command, key) {
|
|
|
18380
18556
|
if (action.kind !== "commit")
|
|
18381
18557
|
continue;
|
|
18382
18558
|
const file = markerFile(repo, input.session.id, key);
|
|
18383
|
-
|
|
18384
|
-
|
|
18559
|
+
fs16.mkdirSync(path20.dirname(file), { recursive: true });
|
|
18560
|
+
fs16.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
18385
18561
|
`);
|
|
18386
18562
|
}
|
|
18387
18563
|
} catch {}
|
|
@@ -18404,11 +18580,11 @@ var headCommit = (dir) => {
|
|
|
18404
18580
|
var settle2 = (input, repo, file) => {
|
|
18405
18581
|
let marker;
|
|
18406
18582
|
try {
|
|
18407
|
-
marker = JSON.parse(
|
|
18583
|
+
marker = JSON.parse(fs16.readFileSync(file, "utf8"));
|
|
18408
18584
|
} catch {
|
|
18409
18585
|
return;
|
|
18410
18586
|
}
|
|
18411
|
-
|
|
18587
|
+
fs16.rmSync(file, { force: true });
|
|
18412
18588
|
const session = input.session.id;
|
|
18413
18589
|
const now = Date.now() / 1000;
|
|
18414
18590
|
if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
|
|
@@ -18457,7 +18633,7 @@ function settlePendingCommit(input) {
|
|
|
18457
18633
|
if (!repo)
|
|
18458
18634
|
return;
|
|
18459
18635
|
const file = markerFile(repo, input.session.id, NEXT_COMMAND);
|
|
18460
|
-
if (
|
|
18636
|
+
if (fs16.existsSync(file))
|
|
18461
18637
|
settle2(input, repo, file);
|
|
18462
18638
|
} catch {}
|
|
18463
18639
|
}
|
|
@@ -18847,12 +19023,12 @@ ${line}` };
|
|
|
18847
19023
|
};
|
|
18848
19024
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
18849
19025
|
import { existsSync as existsSync6, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
18850
|
-
import
|
|
19026
|
+
import path21 from "node:path";
|
|
18851
19027
|
var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
18852
19028
|
var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
|
|
18853
19029
|
var optionalText = (value) => typeof value === "string" ? value : null;
|
|
18854
19030
|
var existingDirectory = (value) => {
|
|
18855
|
-
if (!nonEmpty2(value) || !
|
|
19031
|
+
if (!nonEmpty2(value) || !path21.isAbsolute(value) || !existsSync6(value))
|
|
18856
19032
|
return null;
|
|
18857
19033
|
try {
|
|
18858
19034
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -18952,6 +19128,8 @@ ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
|
|
|
18952
19128
|
if (role !== "judge" || !event.agentId)
|
|
18953
19129
|
return readOnly;
|
|
18954
19130
|
const own = subagentSession(session, event.agentId);
|
|
19131
|
+
if (session)
|
|
19132
|
+
recordIssuedSession(input.cwd || process.cwd(), session, own);
|
|
18955
19133
|
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.`;
|
|
18956
19134
|
};
|
|
18957
19135
|
function handleHook(input, deps) {
|
|
@@ -19583,12 +19761,12 @@ var codexAdapter = {
|
|
|
19583
19761
|
};
|
|
19584
19762
|
},
|
|
19585
19763
|
render: render2,
|
|
19586
|
-
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
|
|
19764
|
+
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>`
|
|
19587
19765
|
};
|
|
19588
19766
|
|
|
19589
19767
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
19590
|
-
import
|
|
19591
|
-
import
|
|
19768
|
+
import fs17 from "node:fs";
|
|
19769
|
+
import path22 from "node:path";
|
|
19592
19770
|
var undocumented = { support: "undocumented", native: null };
|
|
19593
19771
|
var none = { support: "none", native: null };
|
|
19594
19772
|
var CURSOR_DESCRIPTOR = {
|
|
@@ -19785,17 +19963,17 @@ var protocolEvent2 = (input) => {
|
|
|
19785
19963
|
};
|
|
19786
19964
|
var TRANSCRIPT_TAIL_BYTES = 256 * 1024;
|
|
19787
19965
|
var lastAssistantText = (file) => {
|
|
19788
|
-
if (!file || !
|
|
19966
|
+
if (!file || !path22.isAbsolute(file))
|
|
19789
19967
|
return null;
|
|
19790
19968
|
try {
|
|
19791
|
-
const size =
|
|
19969
|
+
const size = fs17.statSync(file).size;
|
|
19792
19970
|
const start = Math.max(0, size - TRANSCRIPT_TAIL_BYTES);
|
|
19793
19971
|
const buffer = Buffer.alloc(size - start);
|
|
19794
|
-
const fd =
|
|
19972
|
+
const fd = fs17.openSync(file, "r");
|
|
19795
19973
|
try {
|
|
19796
|
-
|
|
19974
|
+
fs17.readSync(fd, buffer, 0, buffer.length, start);
|
|
19797
19975
|
} finally {
|
|
19798
|
-
|
|
19976
|
+
fs17.closeSync(fd);
|
|
19799
19977
|
}
|
|
19800
19978
|
const lines = buffer.toString("utf8").split(`
|
|
19801
19979
|
`);
|
|
@@ -19822,7 +20000,7 @@ var lastAssistantText = (file) => {
|
|
|
19822
20000
|
return null;
|
|
19823
20001
|
}
|
|
19824
20002
|
};
|
|
19825
|
-
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
20003
|
+
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path22.sep) ? root : `${root}${path22.sep}`);
|
|
19826
20004
|
var hookCwd = (input) => {
|
|
19827
20005
|
const cwd = existingDirectory(input.cwd);
|
|
19828
20006
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|