@brainervirus/workit-codex 8.12.3 → 8.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +1 -1
- package/agents/workit-reviewer.toml +2 -2
- package/agents/workit-verifier.toml +2 -2
- package/dist/launch-mcp.js +641 -401
- package/dist/{workit-hook-2d3wce88.js → workit-hook-j1zd509p.js} +251 -49
- package/dist/workit-hook.js +1 -1
- package/package.json +4 -4
- package/skills/workit-fanout/references/orchestration.md +4 -3
- package/skills/workit-review/SKILL.md +1 -1
- package/skills/workit-ship/SKILL.md +20 -20
- package/skills/workit-ship/references/release.md +26 -0
package/dist/launch-mcp.js
CHANGED
|
@@ -6622,13 +6622,13 @@ var require_dist = __commonJS(function(exports, module) {
|
|
|
6622
6622
|
|
|
6623
6623
|
// packages/workit-codex/scripts/launch-mcp.ts
|
|
6624
6624
|
import { existsSync as existsSync10, realpathSync as realpathSync7, statSync as statSync3 } from "node:fs";
|
|
6625
|
-
import
|
|
6625
|
+
import path24 from "node:path";
|
|
6626
6626
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
6627
6627
|
|
|
6628
6628
|
// packages/workit-core/src/core.ts
|
|
6629
6629
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
6630
6630
|
import { existsSync as existsSync7, realpathSync as realpathSync5 } from "node:fs";
|
|
6631
|
-
import
|
|
6631
|
+
import path21 from "node:path";
|
|
6632
6632
|
|
|
6633
6633
|
// packages/workit-core/src/core/task-contract.ts
|
|
6634
6634
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -15456,9 +15456,9 @@ function parseAdvertisedOperation(family, input, host) {
|
|
|
15456
15456
|
});
|
|
15457
15457
|
}
|
|
15458
15458
|
// packages/workit-core/src/core/task-store.ts
|
|
15459
|
-
import { createHash as
|
|
15460
|
-
import * as
|
|
15461
|
-
import
|
|
15459
|
+
import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
|
|
15460
|
+
import * as fs8 from "node:fs";
|
|
15461
|
+
import path8 from "node:path";
|
|
15462
15462
|
|
|
15463
15463
|
// node_modules/@openclaw/fs-safe/dist/errors.js
|
|
15464
15464
|
var OPERATIONAL_CODES = new Set([
|
|
@@ -16061,8 +16061,14 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
16061
16061
|
}
|
|
16062
16062
|
}
|
|
16063
16063
|
}
|
|
16064
|
+
// packages/workit-core/src/host-session.ts
|
|
16065
|
+
import { createHash as createHash3, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
|
|
16066
|
+
import fs5 from "node:fs";
|
|
16067
|
+
import path5 from "node:path";
|
|
16068
|
+
|
|
16064
16069
|
// packages/workit-core/src/core/logger.ts
|
|
16065
16070
|
import os from "node:os";
|
|
16071
|
+
import path3 from "node:path";
|
|
16066
16072
|
var REDACTED = "[REDACTED]";
|
|
16067
16073
|
var SECRET_VALUE = /\b(?:Bearer|Basic|Digest|Token)\s+\S+/gi;
|
|
16068
16074
|
var KEY_EQ_VALUE = /\b([A-Za-z0-9_-]*(?:token|secret|password|passwd|apikey|api[_-]?key|authorization|credential|bearer)[A-Za-z0-9_-]*)([:=]\s*).+/gi;
|
|
@@ -16105,6 +16111,22 @@ var SENSITIVE_WORDS = new Set([
|
|
|
16105
16111
|
]);
|
|
16106
16112
|
var STACK_WORDS = new Set(["stack", "stacktrace", "trace"]);
|
|
16107
16113
|
var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
16114
|
+
var resolveStateDir = () => {
|
|
16115
|
+
const override = process.env.WORKFLOW_TOOLKIT_STATE;
|
|
16116
|
+
if (override)
|
|
16117
|
+
return override;
|
|
16118
|
+
const home = os.homedir();
|
|
16119
|
+
if (process.env.XDG_STATE_HOME)
|
|
16120
|
+
return path3.join(process.env.XDG_STATE_HOME, "workit");
|
|
16121
|
+
switch (os.platform()) {
|
|
16122
|
+
case "darwin":
|
|
16123
|
+
return path3.join(home, "Library", "Application Support", "workit");
|
|
16124
|
+
case "win32":
|
|
16125
|
+
return path3.join(process.env.LOCALAPPDATA ?? path3.join(home, "AppData", "Local"), "workit");
|
|
16126
|
+
default:
|
|
16127
|
+
return path3.join(process.env.HOME ?? home, ".local", "state", "workit");
|
|
16128
|
+
}
|
|
16129
|
+
};
|
|
16108
16130
|
var homePattern = new RegExp(`${escapeRegExp(os.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
|
|
16109
16131
|
var applyPatterns = (value) => {
|
|
16110
16132
|
let out = value;
|
|
@@ -16122,7 +16144,7 @@ var redactSecrets = (value) => applyPatterns(value);
|
|
|
16122
16144
|
import { spawnSync } from "node:child_process";
|
|
16123
16145
|
import { createHash as createHash2 } from "node:crypto";
|
|
16124
16146
|
import fs4 from "node:fs";
|
|
16125
|
-
import
|
|
16147
|
+
import path4 from "node:path";
|
|
16126
16148
|
var NOT_A_REPO = /not a git repository \(or any (of the parent directories|parent up to)/u;
|
|
16127
16149
|
var gitRun = (cwd, args) => {
|
|
16128
16150
|
const run = spawnSync("git", args, {
|
|
@@ -16164,14 +16186,14 @@ var DISCOVERY_ENV = [
|
|
|
16164
16186
|
];
|
|
16165
16187
|
var isGitDir = (dir) => {
|
|
16166
16188
|
try {
|
|
16167
|
-
return fs4.statSync(
|
|
16189
|
+
return fs4.statSync(path4.join(dir, "HEAD")).isFile() && fs4.statSync(path4.join(dir, "objects")).isDirectory() && fs4.statSync(path4.join(dir, "refs")).isDirectory();
|
|
16168
16190
|
} catch {
|
|
16169
16191
|
return false;
|
|
16170
16192
|
}
|
|
16171
16193
|
};
|
|
16172
16194
|
var reftableRefs = (common) => {
|
|
16173
16195
|
try {
|
|
16174
|
-
return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs4.readFileSync(
|
|
16196
|
+
return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs4.readFileSync(path4.join(common, "config"), "utf8"));
|
|
16175
16197
|
} catch {
|
|
16176
16198
|
return false;
|
|
16177
16199
|
}
|
|
@@ -16191,7 +16213,7 @@ function fastGit(root) {
|
|
|
16191
16213
|
if (device !== null && stat.dev !== device)
|
|
16192
16214
|
return { kind: "none" };
|
|
16193
16215
|
device = stat.dev;
|
|
16194
|
-
const dotGit =
|
|
16216
|
+
const dotGit = path4.join(dir, ".git");
|
|
16195
16217
|
let entry = null;
|
|
16196
16218
|
try {
|
|
16197
16219
|
entry = fs4.statSync(dotGit);
|
|
@@ -16212,19 +16234,19 @@ function fastGit(root) {
|
|
|
16212
16234
|
const match = /^gitdir: (.+)$/mu.exec(text);
|
|
16213
16235
|
if (!match)
|
|
16214
16236
|
return null;
|
|
16215
|
-
gitDir =
|
|
16237
|
+
gitDir = path4.resolve(dir, match[1].trim());
|
|
16216
16238
|
} else
|
|
16217
16239
|
return null;
|
|
16218
|
-
if (!fs4.existsSync(
|
|
16240
|
+
if (!fs4.existsSync(path4.join(gitDir, "HEAD")))
|
|
16219
16241
|
return null;
|
|
16220
16242
|
} else if (isGitDir(dir))
|
|
16221
16243
|
gitDir = dir;
|
|
16222
16244
|
if (gitDir) {
|
|
16223
16245
|
let common = gitDir;
|
|
16224
16246
|
try {
|
|
16225
|
-
common =
|
|
16247
|
+
common = path4.resolve(gitDir, fs4.readFileSync(path4.join(gitDir, "commondir"), "utf8").trim());
|
|
16226
16248
|
} catch {}
|
|
16227
|
-
if (!fs4.existsSync(
|
|
16249
|
+
if (!fs4.existsSync(path4.join(common, "objects")))
|
|
16228
16250
|
return null;
|
|
16229
16251
|
return {
|
|
16230
16252
|
kind: "repo",
|
|
@@ -16234,26 +16256,26 @@ function fastGit(root) {
|
|
|
16234
16256
|
reftable: reftableRefs(common)
|
|
16235
16257
|
};
|
|
16236
16258
|
}
|
|
16237
|
-
const parent =
|
|
16259
|
+
const parent = path4.dirname(dir);
|
|
16238
16260
|
if (parent === dir)
|
|
16239
16261
|
return { kind: "none" };
|
|
16240
16262
|
dir = parent;
|
|
16241
16263
|
}
|
|
16242
16264
|
}
|
|
16243
16265
|
function resolveStore(root) {
|
|
16244
|
-
root =
|
|
16266
|
+
root = path4.resolve(root);
|
|
16245
16267
|
let directory = false;
|
|
16246
16268
|
try {
|
|
16247
16269
|
directory = fs4.statSync(root).isDirectory();
|
|
16248
16270
|
} catch {}
|
|
16249
16271
|
if (!directory)
|
|
16250
|
-
return { dir:
|
|
16272
|
+
return { dir: path4.join(root, ".workit"), shared: false, top: null };
|
|
16251
16273
|
const fast = fastGit(root);
|
|
16252
16274
|
if (fast?.kind === "none")
|
|
16253
|
-
return { dir:
|
|
16275
|
+
return { dir: path4.join(root, ".workit"), shared: false, top: null };
|
|
16254
16276
|
if (fast?.kind === "repo")
|
|
16255
16277
|
return {
|
|
16256
|
-
dir:
|
|
16278
|
+
dir: path4.join(fast.common, "workit"),
|
|
16257
16279
|
shared: true,
|
|
16258
16280
|
top: fast.top,
|
|
16259
16281
|
git: { gitDir: fast.gitDir, top: fast.top, reftable: fast.reftable }
|
|
@@ -16263,20 +16285,20 @@ function resolveStore(root) {
|
|
|
16263
16285
|
if (run.ok && common) {
|
|
16264
16286
|
const top = gitRun(root, ["rev-parse", "--path-format=absolute", "--show-toplevel"]);
|
|
16265
16287
|
return {
|
|
16266
|
-
dir:
|
|
16288
|
+
dir: path4.join(real(path4.resolve(root, common)), "workit"),
|
|
16267
16289
|
shared: true,
|
|
16268
16290
|
top: top.ok && top.stdout.trim() ? real(top.stdout.trim()) : null
|
|
16269
16291
|
};
|
|
16270
16292
|
}
|
|
16271
16293
|
if (run.missing || !run.ok && NOT_A_REPO.test(run.stderr))
|
|
16272
|
-
return { dir:
|
|
16294
|
+
return { dir: path4.join(root, ".workit"), shared: false, top: null };
|
|
16273
16295
|
return new Error(`cannot locate the workit store: git rev-parse failed (${run.stderr.trim().split(`
|
|
16274
16296
|
`)[0] || "no output"}); fix the repository (git status should work here)`);
|
|
16275
16297
|
}
|
|
16276
16298
|
var rebasingBranch = (gitDir) => {
|
|
16277
16299
|
for (const state of ["rebase-merge", "rebase-apply"]) {
|
|
16278
16300
|
try {
|
|
16279
|
-
const ref = fs4.readFileSync(
|
|
16301
|
+
const ref = fs4.readFileSync(path4.join(gitDir, state, "head-name"), "utf8").trim();
|
|
16280
16302
|
const match = /^refs\/heads\/(.+)$/u.exec(ref);
|
|
16281
16303
|
if (match)
|
|
16282
16304
|
return match[1];
|
|
@@ -16291,7 +16313,7 @@ function resolveTaskKey(root, location) {
|
|
|
16291
16313
|
if (location.git && !location.git.reftable) {
|
|
16292
16314
|
let head = "";
|
|
16293
16315
|
try {
|
|
16294
|
-
head = fs4.readFileSync(
|
|
16316
|
+
head = fs4.readFileSync(path4.join(location.git.gitDir, "HEAD"), "utf8").trim();
|
|
16295
16317
|
} catch {}
|
|
16296
16318
|
const ref = /^ref: refs\/heads\/(.+)$/u.exec(head);
|
|
16297
16319
|
if (ref && ref[1] !== ".invalid")
|
|
@@ -16322,7 +16344,7 @@ function resolveTaskKey(root, location) {
|
|
|
16322
16344
|
return { key: `detached-${shortHash(real(worktree))}`, kind: "detached", branch: null };
|
|
16323
16345
|
}
|
|
16324
16346
|
function checkoutRootOf(root, location) {
|
|
16325
|
-
const resolved =
|
|
16347
|
+
const resolved = path4.resolve(root);
|
|
16326
16348
|
let canonical = resolved;
|
|
16327
16349
|
try {
|
|
16328
16350
|
canonical = fs4.realpathSync(resolved);
|
|
@@ -16353,13 +16375,13 @@ function branchRenames(location) {
|
|
|
16353
16375
|
}
|
|
16354
16376
|
for (const entry of entries)
|
|
16355
16377
|
if (entry.isDirectory())
|
|
16356
|
-
walk(
|
|
16378
|
+
walk(path4.join(dir, entry.name));
|
|
16357
16379
|
else
|
|
16358
|
-
files.push(
|
|
16380
|
+
files.push(path4.join(dir, entry.name));
|
|
16359
16381
|
};
|
|
16360
|
-
walk(
|
|
16382
|
+
walk(path4.join(path4.dirname(location.dir), "logs", "refs", "heads"));
|
|
16361
16383
|
if (location.git)
|
|
16362
|
-
files.push(
|
|
16384
|
+
files.push(path4.join(location.git.gitDir, "logs", "HEAD"));
|
|
16363
16385
|
const seen = new Set;
|
|
16364
16386
|
const renames = [];
|
|
16365
16387
|
for (const file of files) {
|
|
@@ -16394,7 +16416,7 @@ var checkoutSlug = (root) => {
|
|
|
16394
16416
|
let canonical = real(root);
|
|
16395
16417
|
if (process.platform === "win32")
|
|
16396
16418
|
canonical = canonical.toLowerCase();
|
|
16397
|
-
const base =
|
|
16419
|
+
const base = path4.basename(canonical).replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 40) || "root";
|
|
16398
16420
|
return `${base}-${shortHash(canonical)}`;
|
|
16399
16421
|
};
|
|
16400
16422
|
|
|
@@ -16426,30 +16448,113 @@ function hostSessionFromEnv(env) {
|
|
|
16426
16448
|
}
|
|
16427
16449
|
return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
|
|
16428
16450
|
}
|
|
16451
|
+
var hash = (value) => createHash3("sha256").update(value).digest("hex").slice(0, 32);
|
|
16452
|
+
var userIssuedRoot = () => path5.join(resolveStateDir(), "issued");
|
|
16453
|
+
var userTokenRoot = () => path5.join(resolveStateDir(), "issued-tokens");
|
|
16454
|
+
function migrateRepoIssued(cwd) {
|
|
16455
|
+
if (cwd === undefined)
|
|
16456
|
+
return;
|
|
16457
|
+
let old;
|
|
16458
|
+
try {
|
|
16459
|
+
const location = resolveStore(cwd);
|
|
16460
|
+
if (location instanceof Error)
|
|
16461
|
+
return;
|
|
16462
|
+
old = path5.join(location.dir, "hooks", "issued");
|
|
16463
|
+
if (!fs5.existsSync(old))
|
|
16464
|
+
return;
|
|
16465
|
+
} catch {
|
|
16466
|
+
return;
|
|
16467
|
+
}
|
|
16468
|
+
for (const lead of safeList(old)) {
|
|
16469
|
+
try {
|
|
16470
|
+
const from = path5.join(old, lead);
|
|
16471
|
+
if (!fs5.statSync(from).isDirectory())
|
|
16472
|
+
continue;
|
|
16473
|
+
const to = path5.join(userIssuedRoot(), lead);
|
|
16474
|
+
fs5.mkdirSync(to, { recursive: true, mode: 448 });
|
|
16475
|
+
for (const id of fs5.readdirSync(from)) {
|
|
16476
|
+
try {
|
|
16477
|
+
fs5.copyFileSync(path5.join(from, id), path5.join(to, id), fs5.constants.COPYFILE_EXCL);
|
|
16478
|
+
} catch (error) {
|
|
16479
|
+
if (error.code !== "EEXIST")
|
|
16480
|
+
throw error;
|
|
16481
|
+
}
|
|
16482
|
+
fs5.rmSync(path5.join(from, id), { force: true });
|
|
16483
|
+
}
|
|
16484
|
+
removeIfEmpty(from);
|
|
16485
|
+
} catch {}
|
|
16486
|
+
}
|
|
16487
|
+
removeIfEmpty(old);
|
|
16488
|
+
}
|
|
16489
|
+
var safeList = (dir) => {
|
|
16490
|
+
try {
|
|
16491
|
+
return fs5.readdirSync(dir);
|
|
16492
|
+
} catch {
|
|
16493
|
+
return [];
|
|
16494
|
+
}
|
|
16495
|
+
};
|
|
16496
|
+
var removeIfEmpty = (dir) => {
|
|
16497
|
+
try {
|
|
16498
|
+
fs5.rmdirSync(dir);
|
|
16499
|
+
} catch {}
|
|
16500
|
+
};
|
|
16501
|
+
var issuedRecord = (cwd, claimed) => {
|
|
16502
|
+
const cut = claimed.lastIndexOf(":");
|
|
16503
|
+
if (cut <= 0)
|
|
16504
|
+
return null;
|
|
16505
|
+
migrateRepoIssued(cwd);
|
|
16506
|
+
try {
|
|
16507
|
+
return fs5.readFileSync(path5.join(userIssuedRoot(), hash(claimed.slice(0, cut)), hash(claimed)), "utf8");
|
|
16508
|
+
} catch {
|
|
16509
|
+
return null;
|
|
16510
|
+
}
|
|
16511
|
+
};
|
|
16512
|
+
var TOKEN_HASH = /^([0-9a-f]{64})$/mu;
|
|
16513
|
+
var issuedToken = (claimed) => {
|
|
16514
|
+
const cut = claimed.lastIndexOf(":");
|
|
16515
|
+
if (cut <= 0)
|
|
16516
|
+
return { stored: null, leadBound: false };
|
|
16517
|
+
const dir = path5.join(userTokenRoot(), hash(claimed.slice(0, cut)));
|
|
16518
|
+
try {
|
|
16519
|
+
const text = fs5.readFileSync(path5.join(dir, hash(claimed)), "utf8");
|
|
16520
|
+
return { stored: TOKEN_HASH.exec(text)?.[1] ?? null, leadBound: true };
|
|
16521
|
+
} catch {
|
|
16522
|
+
return { stored: null, leadBound: safeList(dir).length > 0 };
|
|
16523
|
+
}
|
|
16524
|
+
};
|
|
16525
|
+
function issuedBinding(cwd, claimed) {
|
|
16526
|
+
const record = issuedRecord(cwd, claimed);
|
|
16527
|
+
const { stored, leadBound } = issuedToken(claimed);
|
|
16528
|
+
if (stored)
|
|
16529
|
+
return "token";
|
|
16530
|
+
if (record === null)
|
|
16531
|
+
return null;
|
|
16532
|
+
return leadBound ? "token" : "legacy";
|
|
16533
|
+
}
|
|
16429
16534
|
var OWN_CHILD_SHELL = new Set(["opencode", "pi"]);
|
|
16430
16535
|
|
|
16431
16536
|
// packages/workit-core/src/core/package-root.ts
|
|
16432
16537
|
import { existsSync } from "node:fs";
|
|
16433
|
-
import
|
|
16538
|
+
import path6 from "node:path";
|
|
16434
16539
|
import { fileURLToPath } from "node:url";
|
|
16435
16540
|
var packageRoot = () => {
|
|
16436
|
-
let dir =
|
|
16541
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
16437
16542
|
while (true) {
|
|
16438
|
-
const parent =
|
|
16439
|
-
if (existsSync(
|
|
16543
|
+
const parent = path6.dirname(dir);
|
|
16544
|
+
if (existsSync(path6.join(dir, "package.json")) || parent === dir)
|
|
16440
16545
|
return dir;
|
|
16441
16546
|
dir = parent;
|
|
16442
16547
|
}
|
|
16443
16548
|
};
|
|
16444
16549
|
var assetRoot = () => {
|
|
16445
16550
|
const root = packageRoot();
|
|
16446
|
-
const assets =
|
|
16551
|
+
const assets = path6.join(root, "assets");
|
|
16447
16552
|
return existsSync(assets) ? assets : root;
|
|
16448
16553
|
};
|
|
16449
16554
|
|
|
16450
16555
|
// packages/workit-core/src/core/store-lock.ts
|
|
16451
16556
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
16452
|
-
import
|
|
16557
|
+
import fs6 from "node:fs";
|
|
16453
16558
|
import { hostname } from "node:os";
|
|
16454
16559
|
var metadataLockSchema = object({
|
|
16455
16560
|
pid: number2().int().nonnegative().safe(),
|
|
@@ -16461,8 +16566,10 @@ var metadataLockSchema = object({
|
|
|
16461
16566
|
var FOREIGN_LOCK_TTL_MS = 10 * 60000;
|
|
16462
16567
|
var UNREADABLE_LOCK_TTL_MS = 30000;
|
|
16463
16568
|
var RECLAIM_GUARD_TTL_MS = 30000;
|
|
16464
|
-
var
|
|
16569
|
+
var IN_PROCESS_LOCK_TIMEOUT_MS = 250;
|
|
16570
|
+
var defaultLockTimeoutMs = IN_PROCESS_LOCK_TIMEOUT_MS;
|
|
16465
16571
|
var defaultLockTimeout = () => defaultLockTimeoutMs;
|
|
16572
|
+
var hostAwareLockTimeout = (cliMs) => defaultLockTimeoutMs > IN_PROCESS_LOCK_TIMEOUT_MS ? cliMs : IN_PROCESS_LOCK_TIMEOUT_MS;
|
|
16466
16573
|
var parseMetadataLock = (raw) => {
|
|
16467
16574
|
let value;
|
|
16468
16575
|
try {
|
|
@@ -16491,7 +16598,7 @@ var parseProcStatStart = (stat) => {
|
|
|
16491
16598
|
var processStartOf = (pid) => {
|
|
16492
16599
|
if (process.platform === "linux") {
|
|
16493
16600
|
try {
|
|
16494
|
-
return parseProcStatStart(
|
|
16601
|
+
return parseProcStatStart(fs6.readFileSync(`/proc/${pid}/stat`, "utf8"));
|
|
16495
16602
|
} catch {
|
|
16496
16603
|
return null;
|
|
16497
16604
|
}
|
|
@@ -16512,7 +16619,7 @@ var processStartOf = (pid) => {
|
|
|
16512
16619
|
};
|
|
16513
16620
|
var readTrimmed = (file) => {
|
|
16514
16621
|
try {
|
|
16515
|
-
return
|
|
16622
|
+
return fs6.readFileSync(file, "utf8").trim() || null;
|
|
16516
16623
|
} catch {
|
|
16517
16624
|
return null;
|
|
16518
16625
|
}
|
|
@@ -16523,7 +16630,7 @@ var localLockHost = () => {
|
|
|
16523
16630
|
return cachedLockHost;
|
|
16524
16631
|
let pidns = null;
|
|
16525
16632
|
try {
|
|
16526
|
-
pidns = /\[(\d+)\]/.exec(
|
|
16633
|
+
pidns = /\[(\d+)\]/.exec(fs6.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
|
|
16527
16634
|
} catch {}
|
|
16528
16635
|
const boot = readTrimmed("/proc/sys/kernel/random/boot_id");
|
|
16529
16636
|
cachedLockHost = pidns || boot ? `${hostname()}#${pidns ?? "?"}:${boot ?? "?"}` : hostname();
|
|
@@ -16569,7 +16676,7 @@ var classifyLockOwner = (payload, ageMs, localHost = localLockHost(), options =
|
|
|
16569
16676
|
};
|
|
16570
16677
|
var ageOf = (file, nowMs) => {
|
|
16571
16678
|
try {
|
|
16572
|
-
return nowMs -
|
|
16679
|
+
return nowMs - fs6.lstatSync(file).mtimeMs;
|
|
16573
16680
|
} catch {
|
|
16574
16681
|
return null;
|
|
16575
16682
|
}
|
|
@@ -16580,7 +16687,7 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
|
|
|
16580
16687
|
if (age === null || age <= RECLAIM_GUARD_TTL_MS)
|
|
16581
16688
|
return false;
|
|
16582
16689
|
try {
|
|
16583
|
-
|
|
16690
|
+
fs6.rmdirSync(guard);
|
|
16584
16691
|
return true;
|
|
16585
16692
|
} catch {
|
|
16586
16693
|
return false;
|
|
@@ -16589,8 +16696,8 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
|
|
|
16589
16696
|
|
|
16590
16697
|
// packages/workit-core/src/store/event-log.ts
|
|
16591
16698
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
16592
|
-
import
|
|
16593
|
-
import
|
|
16699
|
+
import fs7 from "node:fs";
|
|
16700
|
+
import path7 from "node:path";
|
|
16594
16701
|
var EVENT_VERSION = 1;
|
|
16595
16702
|
|
|
16596
16703
|
class LogDamage extends Error {
|
|
@@ -16619,7 +16726,7 @@ var parseEvent = (line, file, offset) => {
|
|
|
16619
16726
|
function readLog(file, from = 0, expectSeq = null) {
|
|
16620
16727
|
let fd;
|
|
16621
16728
|
try {
|
|
16622
|
-
fd =
|
|
16729
|
+
fd = fs7.openSync(file, "r");
|
|
16623
16730
|
} catch (error) {
|
|
16624
16731
|
if (error.code === "ENOENT")
|
|
16625
16732
|
return null;
|
|
@@ -16628,13 +16735,13 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
16628
16735
|
let bytes;
|
|
16629
16736
|
let size;
|
|
16630
16737
|
try {
|
|
16631
|
-
size =
|
|
16738
|
+
size = fs7.fstatSync(fd).size;
|
|
16632
16739
|
if (from > size)
|
|
16633
16740
|
throw new LogDamage(`${file}: shorter than its snapshot`);
|
|
16634
16741
|
bytes = Buffer.alloc(size - from);
|
|
16635
16742
|
let read = 0;
|
|
16636
16743
|
while (read < bytes.length) {
|
|
16637
|
-
const got =
|
|
16744
|
+
const got = fs7.readSync(fd, bytes, read, bytes.length - read, from + read);
|
|
16638
16745
|
if (got === 0)
|
|
16639
16746
|
break;
|
|
16640
16747
|
read += got;
|
|
@@ -16642,7 +16749,7 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
16642
16749
|
bytes = bytes.subarray(0, read);
|
|
16643
16750
|
size = from + read;
|
|
16644
16751
|
} finally {
|
|
16645
|
-
|
|
16752
|
+
fs7.closeSync(fd);
|
|
16646
16753
|
}
|
|
16647
16754
|
const events = [];
|
|
16648
16755
|
let start = 0;
|
|
@@ -16667,11 +16774,11 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
16667
16774
|
}
|
|
16668
16775
|
var fsyncDirectory = (dir) => {
|
|
16669
16776
|
try {
|
|
16670
|
-
const fd =
|
|
16777
|
+
const fd = fs7.openSync(dir, "r");
|
|
16671
16778
|
try {
|
|
16672
|
-
|
|
16779
|
+
fs7.fsyncSync(fd);
|
|
16673
16780
|
} finally {
|
|
16674
|
-
|
|
16781
|
+
fs7.closeSync(fd);
|
|
16675
16782
|
}
|
|
16676
16783
|
} catch (error) {
|
|
16677
16784
|
if (process.platform !== "win32")
|
|
@@ -16681,46 +16788,46 @@ var fsyncDirectory = (dir) => {
|
|
|
16681
16788
|
function appendEvent(file, event, validEnd) {
|
|
16682
16789
|
const line = Buffer.from(`${JSON.stringify(event)}
|
|
16683
16790
|
`, "utf8");
|
|
16684
|
-
const created = !
|
|
16685
|
-
if (!created &&
|
|
16686
|
-
const cut =
|
|
16791
|
+
const created = !fs7.existsSync(file);
|
|
16792
|
+
if (!created && fs7.statSync(file).size !== validEnd) {
|
|
16793
|
+
const cut = fs7.openSync(file, "r+");
|
|
16687
16794
|
try {
|
|
16688
|
-
|
|
16689
|
-
|
|
16795
|
+
fs7.ftruncateSync(cut, validEnd);
|
|
16796
|
+
fs7.fsyncSync(cut);
|
|
16690
16797
|
} finally {
|
|
16691
|
-
|
|
16798
|
+
fs7.closeSync(cut);
|
|
16692
16799
|
}
|
|
16693
16800
|
}
|
|
16694
|
-
const fd =
|
|
16801
|
+
const fd = fs7.openSync(file, "a", 384);
|
|
16695
16802
|
try {
|
|
16696
16803
|
let written = 0;
|
|
16697
16804
|
while (written < line.length)
|
|
16698
|
-
written +=
|
|
16699
|
-
|
|
16805
|
+
written += fs7.writeSync(fd, line, written, line.length - written);
|
|
16806
|
+
fs7.fsyncSync(fd);
|
|
16700
16807
|
} finally {
|
|
16701
|
-
|
|
16808
|
+
fs7.closeSync(fd);
|
|
16702
16809
|
}
|
|
16703
16810
|
if (created)
|
|
16704
|
-
fsyncDirectory(
|
|
16811
|
+
fsyncDirectory(path7.dirname(file));
|
|
16705
16812
|
return validEnd;
|
|
16706
16813
|
}
|
|
16707
16814
|
function replaceFile(file, bytes, durable) {
|
|
16708
16815
|
const temporary = `${file}.${process.pid}.${randomUUID2()}.tmp`;
|
|
16709
16816
|
try {
|
|
16710
|
-
const fd =
|
|
16817
|
+
const fd = fs7.openSync(temporary, "wx", 384);
|
|
16711
16818
|
try {
|
|
16712
|
-
|
|
16819
|
+
fs7.writeSync(fd, bytes);
|
|
16713
16820
|
if (durable)
|
|
16714
|
-
|
|
16821
|
+
fs7.fsyncSync(fd);
|
|
16715
16822
|
} finally {
|
|
16716
|
-
|
|
16823
|
+
fs7.closeSync(fd);
|
|
16717
16824
|
}
|
|
16718
16825
|
renameWithRetry(temporary, file);
|
|
16719
16826
|
if (durable)
|
|
16720
|
-
fsyncDirectory(
|
|
16827
|
+
fsyncDirectory(path7.dirname(file));
|
|
16721
16828
|
} catch (error) {
|
|
16722
16829
|
try {
|
|
16723
|
-
|
|
16830
|
+
fs7.unlinkSync(temporary);
|
|
16724
16831
|
} catch {}
|
|
16725
16832
|
throw error;
|
|
16726
16833
|
}
|
|
@@ -16745,7 +16852,7 @@ var retryTransient = (run) => {
|
|
|
16745
16852
|
}
|
|
16746
16853
|
}
|
|
16747
16854
|
};
|
|
16748
|
-
var renameWithRetry = (from, to) => retryTransient(() =>
|
|
16855
|
+
var renameWithRetry = (from, to) => retryTransient(() => fs7.renameSync(from, to));
|
|
16749
16856
|
|
|
16750
16857
|
// packages/workit-core/src/store/patch.ts
|
|
16751
16858
|
var isPlainObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -16858,7 +16965,7 @@ var isPatch = (value) => Array.isArray(value) && value.every((op) => {
|
|
|
16858
16965
|
});
|
|
16859
16966
|
|
|
16860
16967
|
// packages/workit-core/src/store/reduce.ts
|
|
16861
|
-
import { createHash as
|
|
16968
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
16862
16969
|
var OPENING_TYPES = new Set([
|
|
16863
16970
|
"task.opened",
|
|
16864
16971
|
"task.imported",
|
|
@@ -16887,7 +16994,7 @@ var isObject4 = (value) => typeof value === "object" && value !== null && !Array
|
|
|
16887
16994
|
var isKey = (value) => isObject4(value) && typeof value.key === "string" && (value.kind === "branch" || value.kind === "detached" || value.kind === "dir") && (value.branch === null || typeof value.branch === "string");
|
|
16888
16995
|
var BLOB_REF = "$blob";
|
|
16889
16996
|
var blobRef = (value) => isObject4(value) && Object.keys(value).length === 1 && typeof value[BLOB_REF] === "string" && /^[0-9a-f]{64}$/.test(value[BLOB_REF]) ? value[BLOB_REF] : null;
|
|
16890
|
-
var blobDigest = (value) =>
|
|
16997
|
+
var blobDigest = (value) => createHash4("sha256").update(JSON.stringify(value)).digest("hex");
|
|
16891
16998
|
var toBlob = (value, blobs) => {
|
|
16892
16999
|
if (!isObject4(value))
|
|
16893
17000
|
return value;
|
|
@@ -17042,7 +17149,7 @@ var indexEntry = (task, key, legacy, file) => {
|
|
|
17042
17149
|
};
|
|
17043
17150
|
var fileSignature = (file) => {
|
|
17044
17151
|
try {
|
|
17045
|
-
const stat =
|
|
17152
|
+
const stat = fs8.statSync(file, { bigint: true });
|
|
17046
17153
|
return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
|
|
17047
17154
|
} catch {
|
|
17048
17155
|
return null;
|
|
@@ -17060,7 +17167,7 @@ var cachedRuntimeVersion = null;
|
|
|
17060
17167
|
var runtimeVersion = () => {
|
|
17061
17168
|
if (cachedRuntimeVersion === null) {
|
|
17062
17169
|
try {
|
|
17063
|
-
const pkg = JSON.parse(
|
|
17170
|
+
const pkg = JSON.parse(fs8.readFileSync(path8.join(packageRoot(), "package.json"), "utf8"));
|
|
17064
17171
|
cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
|
|
17065
17172
|
} catch {
|
|
17066
17173
|
cachedRuntimeVersion = "0.0.0";
|
|
@@ -17086,14 +17193,14 @@ var isNewerVersion = (candidate, current) => {
|
|
|
17086
17193
|
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);
|
|
17087
17194
|
var readText = (file) => {
|
|
17088
17195
|
try {
|
|
17089
|
-
return
|
|
17196
|
+
return fs8.readFileSync(file, "utf8");
|
|
17090
17197
|
} catch {
|
|
17091
17198
|
return "";
|
|
17092
17199
|
}
|
|
17093
17200
|
};
|
|
17094
17201
|
var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
|
|
17095
17202
|
var branchStart = (location, branch, aliases) => {
|
|
17096
|
-
const first = readText(
|
|
17203
|
+
const first = readText(path8.join(path8.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
|
|
17097
17204
|
`, 1)[0];
|
|
17098
17205
|
const created = REFLOG_LINE.exec(first);
|
|
17099
17206
|
const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
|
|
@@ -17103,7 +17210,7 @@ var branchStart = (location, branch, aliases) => {
|
|
|
17103
17210
|
return start;
|
|
17104
17211
|
if (!location.git)
|
|
17105
17212
|
return null;
|
|
17106
|
-
for (const line of readText(
|
|
17213
|
+
for (const line of readText(path8.join(location.git.gitDir, "logs", "HEAD")).split(`
|
|
17107
17214
|
`)) {
|
|
17108
17215
|
const entry = REFLOG_LINE.exec(line);
|
|
17109
17216
|
const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
|
|
@@ -17113,23 +17220,23 @@ var branchStart = (location, branch, aliases) => {
|
|
|
17113
17220
|
}
|
|
17114
17221
|
return null;
|
|
17115
17222
|
};
|
|
17116
|
-
var refExists = (common, ref) =>
|
|
17223
|
+
var refExists = (common, ref) => fs8.existsSync(path8.join(common, ref)) || readText(path8.join(common, "packed-refs")).includes(` ${ref}
|
|
17117
17224
|
`);
|
|
17118
17225
|
var origins = new Map;
|
|
17119
17226
|
var sameDirectoryIdentity = (left, right) => {
|
|
17120
|
-
if (!
|
|
17227
|
+
if (!path8.isAbsolute(left) || !path8.isAbsolute(right))
|
|
17121
17228
|
return false;
|
|
17122
|
-
const normalizedLeft =
|
|
17123
|
-
const normalizedRight =
|
|
17229
|
+
const normalizedLeft = path8.resolve(left);
|
|
17230
|
+
const normalizedRight = path8.resolve(right);
|
|
17124
17231
|
try {
|
|
17125
|
-
const a =
|
|
17126
|
-
const b =
|
|
17232
|
+
const a = fs8.statSync(normalizedLeft, { bigint: true });
|
|
17233
|
+
const b = fs8.statSync(normalizedRight, { bigint: true });
|
|
17127
17234
|
if (!a.isDirectory() || !b.isDirectory())
|
|
17128
17235
|
return false;
|
|
17129
17236
|
if (process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight)
|
|
17130
17237
|
return true;
|
|
17131
|
-
const canonicalLeft =
|
|
17132
|
-
const canonicalRight =
|
|
17238
|
+
const canonicalLeft = fs8.realpathSync(normalizedLeft);
|
|
17239
|
+
const canonicalRight = fs8.realpathSync(normalizedRight);
|
|
17133
17240
|
if (process.platform === "win32" ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase() : canonicalLeft === canonicalRight)
|
|
17134
17241
|
return true;
|
|
17135
17242
|
return a.dev === b.dev && a.ino !== 0n && a.ino === b.ino;
|
|
@@ -17166,8 +17273,8 @@ class TaskStore {
|
|
|
17166
17273
|
migrationChecked = false;
|
|
17167
17274
|
compactDue = new Set;
|
|
17168
17275
|
constructor(root, options = {}) {
|
|
17169
|
-
const resolved =
|
|
17170
|
-
this.origin =
|
|
17276
|
+
const resolved = path8.resolve(root);
|
|
17277
|
+
this.origin = fs8.existsSync(resolved) ? fs8.realpathSync(resolved) : resolved;
|
|
17171
17278
|
this.located = resolveStore(this.origin);
|
|
17172
17279
|
this.root = checkoutRootOf(this.origin, this.located);
|
|
17173
17280
|
this.lockTimeoutMs = options.lockTimeoutMs ?? defaultLockTimeout();
|
|
@@ -17175,7 +17282,7 @@ class TaskStore {
|
|
|
17175
17282
|
this.migrateOnRead = options.migrateOnRead;
|
|
17176
17283
|
}
|
|
17177
17284
|
location() {
|
|
17178
|
-
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared &&
|
|
17285
|
+
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs8.existsSync(path8.join(this.root, ".git"))) {
|
|
17179
17286
|
this.located = undefined;
|
|
17180
17287
|
this.migrationChecked = false;
|
|
17181
17288
|
}
|
|
@@ -17239,7 +17346,7 @@ class TaskStore {
|
|
|
17239
17346
|
const tasksDir = this.paths().tasks;
|
|
17240
17347
|
let names;
|
|
17241
17348
|
try {
|
|
17242
|
-
names =
|
|
17349
|
+
names = fs8.existsSync(tasksDir) ? fs8.readdirSync(tasksDir).filter((name) => validId(name)) : [];
|
|
17243
17350
|
} catch (error) {
|
|
17244
17351
|
return failure2("storage_error", `unable to list tasks: ${String(error)}`, {
|
|
17245
17352
|
path: tasksDir
|
|
@@ -17280,7 +17387,7 @@ class TaskStore {
|
|
|
17280
17387
|
const file = this.paths().workspace;
|
|
17281
17388
|
let bytes;
|
|
17282
17389
|
try {
|
|
17283
|
-
bytes = retryTransient(() =>
|
|
17390
|
+
bytes = retryTransient(() => fs8.readFileSync(file, "utf8"));
|
|
17284
17391
|
} catch (error) {
|
|
17285
17392
|
if (error.code === "ENOENT")
|
|
17286
17393
|
return success(null, null, null);
|
|
@@ -17331,7 +17438,7 @@ class TaskStore {
|
|
|
17331
17438
|
const moved = this.bindLocked(again.data.id, key, input.now);
|
|
17332
17439
|
return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
|
|
17333
17440
|
}
|
|
17334
|
-
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" :
|
|
17441
|
+
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path8.basename(this.root) || this.root;
|
|
17335
17442
|
const intent = {
|
|
17336
17443
|
objective: input.objective?.trim() || `Work on ${label}`,
|
|
17337
17444
|
scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
|
|
@@ -17391,14 +17498,14 @@ class TaskStore {
|
|
|
17391
17498
|
const branch = key.ok ? key.data.branch : null;
|
|
17392
17499
|
if (!branch || !location.ok)
|
|
17393
17500
|
return success(null, null, null);
|
|
17394
|
-
const common =
|
|
17395
|
-
const heads =
|
|
17396
|
-
const headLog = location.data.git ?
|
|
17501
|
+
const common = path8.dirname(location.data.dir);
|
|
17502
|
+
const heads = path8.join(common, "logs", "refs", "heads");
|
|
17503
|
+
const headLog = location.data.git ? path8.join(location.data.git.gitDir, "logs", "HEAD") : "";
|
|
17397
17504
|
const signatures = [
|
|
17398
|
-
|
|
17505
|
+
path8.join(heads, branch),
|
|
17399
17506
|
headLog,
|
|
17400
17507
|
heads,
|
|
17401
|
-
|
|
17508
|
+
path8.join(common, "packed-refs")
|
|
17402
17509
|
].map((file) => fileSignature(file) ?? "-");
|
|
17403
17510
|
const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
|
|
17404
17511
|
let resolved = origins.get(slot);
|
|
@@ -17410,7 +17517,7 @@ class TaskStore {
|
|
|
17410
17517
|
aliases.push(rename.from);
|
|
17411
17518
|
const start = branchStart(location.data, branch, aliases);
|
|
17412
17519
|
const slash = start?.indexOf("/") ?? -1;
|
|
17413
|
-
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(
|
|
17520
|
+
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path8.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
|
|
17414
17521
|
const name = start && (remote ? start.slice(slash + 1) : start);
|
|
17415
17522
|
resolved = {
|
|
17416
17523
|
signature: signatures.join("|"),
|
|
@@ -17648,13 +17755,13 @@ class TaskStore {
|
|
|
17648
17755
|
if (!ready.ok)
|
|
17649
17756
|
return ready;
|
|
17650
17757
|
const dirs = this.paths();
|
|
17651
|
-
const recovery =
|
|
17652
|
-
if (
|
|
17758
|
+
const recovery = path8.join(this.root, ".workit", "recovery");
|
|
17759
|
+
if (fs8.existsSync(recovery)) {
|
|
17653
17760
|
let files = 0;
|
|
17654
17761
|
let bytes = 0;
|
|
17655
17762
|
try {
|
|
17656
|
-
for (const name of
|
|
17657
|
-
const stat =
|
|
17763
|
+
for (const name of fs8.readdirSync(recovery)) {
|
|
17764
|
+
const stat = fs8.lstatSync(path8.join(recovery, name));
|
|
17658
17765
|
if (!stat.isFile())
|
|
17659
17766
|
continue;
|
|
17660
17767
|
files += 1;
|
|
@@ -17663,17 +17770,17 @@ class TaskStore {
|
|
|
17663
17770
|
} catch {}
|
|
17664
17771
|
const remove = options.pruneRecovery === true && !dryRun;
|
|
17665
17772
|
if (remove)
|
|
17666
|
-
|
|
17773
|
+
fs8.rmSync(recovery, { recursive: true, force: true });
|
|
17667
17774
|
report.legacyRecovery = { path: recovery, files, bytes, removed: remove };
|
|
17668
17775
|
}
|
|
17669
|
-
if (!
|
|
17776
|
+
if (!fs8.existsSync(dirs.tasks))
|
|
17670
17777
|
return success(null, null, report);
|
|
17671
|
-
const names =
|
|
17778
|
+
const names = fs8.readdirSync(dirs.tasks).filter((name) => validId(name));
|
|
17672
17779
|
for (const name of names) {
|
|
17673
17780
|
const file = this.eventsPath(name);
|
|
17674
17781
|
let size = 0;
|
|
17675
17782
|
try {
|
|
17676
|
-
size =
|
|
17783
|
+
size = fs8.statSync(file).size;
|
|
17677
17784
|
} catch {
|
|
17678
17785
|
continue;
|
|
17679
17786
|
}
|
|
@@ -17701,16 +17808,16 @@ class TaskStore {
|
|
|
17701
17808
|
const references = new Set;
|
|
17702
17809
|
for (const name of names)
|
|
17703
17810
|
try {
|
|
17704
|
-
for (const match of
|
|
17811
|
+
for (const match of fs8.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
|
|
17705
17812
|
references.add(match[1]);
|
|
17706
17813
|
} catch {}
|
|
17707
|
-
if (
|
|
17708
|
-
for (const name of
|
|
17814
|
+
if (fs8.existsSync(dirs.blobs))
|
|
17815
|
+
for (const name of fs8.readdirSync(dirs.blobs)) {
|
|
17709
17816
|
const digest = name.replace(/\.json$/, "");
|
|
17710
|
-
const file =
|
|
17817
|
+
const file = path8.join(dirs.blobs, name);
|
|
17711
17818
|
let stat;
|
|
17712
17819
|
try {
|
|
17713
|
-
stat =
|
|
17820
|
+
stat = fs8.lstatSync(file);
|
|
17714
17821
|
} catch {
|
|
17715
17822
|
continue;
|
|
17716
17823
|
}
|
|
@@ -17721,11 +17828,11 @@ class TaskStore {
|
|
|
17721
17828
|
report.blobs.removed += 1;
|
|
17722
17829
|
report.blobs.removedBytes += stat.size;
|
|
17723
17830
|
if (!dryRun)
|
|
17724
|
-
|
|
17831
|
+
fs8.rmSync(file, { force: true });
|
|
17725
17832
|
}
|
|
17726
17833
|
return success(null, null, null);
|
|
17727
17834
|
};
|
|
17728
|
-
const swept = dryRun ? sweep() : this.withLock(
|
|
17835
|
+
const swept = dryRun ? sweep() : this.withLock(path8.join(dirs.dir, "gc.lock"), sweep);
|
|
17729
17836
|
if (!swept.ok)
|
|
17730
17837
|
return swept;
|
|
17731
17838
|
const temporaryDirs = [
|
|
@@ -17733,17 +17840,17 @@ class TaskStore {
|
|
|
17733
17840
|
dirs.tasks,
|
|
17734
17841
|
dirs.blobs,
|
|
17735
17842
|
dirs.checkout,
|
|
17736
|
-
...names.map((name) =>
|
|
17843
|
+
...names.map((name) => path8.join(dirs.tasks, name))
|
|
17737
17844
|
];
|
|
17738
17845
|
for (const directory of temporaryDirs) {
|
|
17739
|
-
if (!
|
|
17846
|
+
if (!fs8.existsSync(directory))
|
|
17740
17847
|
continue;
|
|
17741
|
-
for (const name of
|
|
17848
|
+
for (const name of fs8.readdirSync(directory)) {
|
|
17742
17849
|
if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
|
|
17743
17850
|
continue;
|
|
17744
|
-
const file =
|
|
17851
|
+
const file = path8.join(directory, name);
|
|
17745
17852
|
try {
|
|
17746
|
-
const stat =
|
|
17853
|
+
const stat = fs8.lstatSync(file);
|
|
17747
17854
|
if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
|
|
17748
17855
|
continue;
|
|
17749
17856
|
} catch {
|
|
@@ -17751,37 +17858,37 @@ class TaskStore {
|
|
|
17751
17858
|
}
|
|
17752
17859
|
report.temporary.removed += 1;
|
|
17753
17860
|
if (!dryRun)
|
|
17754
|
-
|
|
17861
|
+
fs8.rmSync(file, { force: true });
|
|
17755
17862
|
}
|
|
17756
17863
|
}
|
|
17757
17864
|
return success(null, null, report);
|
|
17758
17865
|
}
|
|
17759
17866
|
paths() {
|
|
17760
17867
|
const location = this.location();
|
|
17761
|
-
const dir = location.ok ? location.data.dir :
|
|
17762
|
-
const checkout =
|
|
17868
|
+
const dir = location.ok ? location.data.dir : path8.join(this.root, ".workit");
|
|
17869
|
+
const checkout = path8.join(dir, "checkouts", checkoutSlug(this.root));
|
|
17763
17870
|
return {
|
|
17764
17871
|
dir,
|
|
17765
17872
|
shared: location.ok ? location.data.shared : false,
|
|
17766
|
-
marker:
|
|
17873
|
+
marker: path8.join(dir, "store.json"),
|
|
17767
17874
|
checkout,
|
|
17768
|
-
workspace:
|
|
17769
|
-
checkoutLock:
|
|
17770
|
-
tasks:
|
|
17771
|
-
index:
|
|
17772
|
-
blobs:
|
|
17773
|
-
legacy:
|
|
17774
|
-
v2:
|
|
17875
|
+
workspace: path8.join(checkout, "workspace.json"),
|
|
17876
|
+
checkoutLock: path8.join(checkout, "metadata.lock"),
|
|
17877
|
+
tasks: path8.join(dir, "tasks"),
|
|
17878
|
+
index: path8.join(dir, "task-index.json"),
|
|
17879
|
+
blobs: path8.join(dir, "blobs", "candidates"),
|
|
17880
|
+
legacy: path8.join(dir, "legacy", checkoutSlug(this.root), "v2"),
|
|
17881
|
+
v2: path8.join(this.root, ".workit")
|
|
17775
17882
|
};
|
|
17776
17883
|
}
|
|
17777
17884
|
eventsPath(taskId) {
|
|
17778
|
-
return
|
|
17885
|
+
return path8.join(this.paths().tasks, taskId, "events.jsonl");
|
|
17779
17886
|
}
|
|
17780
17887
|
snapshotPath(taskId) {
|
|
17781
|
-
return
|
|
17888
|
+
return path8.join(this.paths().tasks, taskId, "snapshot.json");
|
|
17782
17889
|
}
|
|
17783
17890
|
taskLockPath(taskId) {
|
|
17784
|
-
return
|
|
17891
|
+
return path8.join(this.paths().tasks, taskId, "lock");
|
|
17785
17892
|
}
|
|
17786
17893
|
ready(write = false) {
|
|
17787
17894
|
const location = this.location();
|
|
@@ -17791,7 +17898,7 @@ class TaskStore {
|
|
|
17791
17898
|
return success(null, null, null);
|
|
17792
17899
|
const dirs = this.paths();
|
|
17793
17900
|
try {
|
|
17794
|
-
const marker = JSON.parse(
|
|
17901
|
+
const marker = JSON.parse(fs8.readFileSync(dirs.marker, "utf8"));
|
|
17795
17902
|
if (typeof marker.version === "number" && marker.version > STORE_FORMAT)
|
|
17796
17903
|
return failure2("recovery_required", `the workit store at ${dirs.dir} has format ${marker.version}; upgrade Workit to use it`, { path: dirs.marker });
|
|
17797
17904
|
} catch {}
|
|
@@ -17820,21 +17927,21 @@ class TaskStore {
|
|
|
17820
17927
|
return {
|
|
17821
17928
|
read: (digest) => {
|
|
17822
17929
|
try {
|
|
17823
|
-
return JSON.parse(
|
|
17930
|
+
return JSON.parse(fs8.readFileSync(path8.join(dir, `${digest}.json`), "utf8"));
|
|
17824
17931
|
} catch (error) {
|
|
17825
17932
|
throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
|
|
17826
17933
|
}
|
|
17827
17934
|
},
|
|
17828
17935
|
write: (digest, value) => {
|
|
17829
|
-
const file =
|
|
17830
|
-
if (
|
|
17936
|
+
const file = path8.join(dir, `${digest}.json`);
|
|
17937
|
+
if (fs8.existsSync(file)) {
|
|
17831
17938
|
const stamp = new Date;
|
|
17832
17939
|
try {
|
|
17833
|
-
|
|
17940
|
+
fs8.utimesSync(file, stamp, stamp);
|
|
17834
17941
|
return;
|
|
17835
17942
|
} catch {}
|
|
17836
17943
|
}
|
|
17837
|
-
|
|
17944
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
17838
17945
|
replaceFile(file, JSON.stringify(value), true);
|
|
17839
17946
|
}
|
|
17840
17947
|
};
|
|
@@ -17916,7 +18023,7 @@ class TaskStore {
|
|
|
17916
18023
|
}
|
|
17917
18024
|
readSnapshot(taskId) {
|
|
17918
18025
|
try {
|
|
17919
|
-
const value = JSON.parse(
|
|
18026
|
+
const value = JSON.parse(fs8.readFileSync(this.snapshotPath(taskId), "utf8"));
|
|
17920
18027
|
if (!isObject5(value) || value.v !== 1 || value.task !== taskId || typeof value.seq !== "number" || typeof value.lastId !== "string" || typeof value.lastStart !== "number" || typeof value.end !== "number" || !isObject5(value.record))
|
|
17921
18028
|
return null;
|
|
17922
18029
|
return value;
|
|
@@ -18018,8 +18125,8 @@ class TaskStore {
|
|
|
18018
18125
|
}
|
|
18019
18126
|
}
|
|
18020
18127
|
openTask(record, type, key, legacy = null) {
|
|
18021
|
-
const dir =
|
|
18022
|
-
|
|
18128
|
+
const dir = path8.join(this.paths().tasks, record.id);
|
|
18129
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
18023
18130
|
const blobs = this.blobStore();
|
|
18024
18131
|
const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
|
|
18025
18132
|
const event = this.event(record.id, 1, type, {
|
|
@@ -18071,11 +18178,11 @@ class TaskStore {
|
|
|
18071
18178
|
} catch {}
|
|
18072
18179
|
if (!latest || latest.events[0]?.id !== last.id)
|
|
18073
18180
|
return failure2("busy", "the task log changed during compaction; retry", { taskId });
|
|
18074
|
-
const appended =
|
|
18181
|
+
const appended = fs8.readFileSync(file).subarray(log.end, latest.end);
|
|
18075
18182
|
const bytes = Buffer.concat([Buffer.from(prefix, "utf8"), appended]);
|
|
18076
18183
|
replaceFile(file, bytes.toString("utf8"), true);
|
|
18077
18184
|
try {
|
|
18078
|
-
|
|
18185
|
+
fs8.rmSync(this.snapshotPath(taskId), { force: true });
|
|
18079
18186
|
} catch {}
|
|
18080
18187
|
const reloaded = this.load(taskId);
|
|
18081
18188
|
if (reloaded.ok && reloaded.data)
|
|
@@ -18088,7 +18195,7 @@ class TaskStore {
|
|
|
18088
18195
|
}
|
|
18089
18196
|
countEvents(file) {
|
|
18090
18197
|
try {
|
|
18091
|
-
const bytes =
|
|
18198
|
+
const bytes = fs8.readFileSync(file);
|
|
18092
18199
|
let count = 0;
|
|
18093
18200
|
for (let index = bytes.indexOf(10);index >= 0; index = bytes.indexOf(10, index + 1))
|
|
18094
18201
|
count += 1;
|
|
@@ -18110,8 +18217,8 @@ class TaskStore {
|
|
|
18110
18217
|
return bound.ok ? success(null, null, bound.data[0] ?? null) : bound;
|
|
18111
18218
|
}
|
|
18112
18219
|
withKeyLock(key, operation) {
|
|
18113
|
-
const digest =
|
|
18114
|
-
return this.withLock(
|
|
18220
|
+
const digest = createHash5("sha256").update(key).digest("hex").slice(0, 32);
|
|
18221
|
+
return this.withLock(path8.join(this.paths().dir, "keys", `${digest}.lock`), operation);
|
|
18115
18222
|
}
|
|
18116
18223
|
bindLocked(taskId, key, timestamp) {
|
|
18117
18224
|
const current = this.readWorkspace();
|
|
@@ -18252,7 +18359,7 @@ class TaskStore {
|
|
|
18252
18359
|
writeWorkspace(record) {
|
|
18253
18360
|
const file = this.paths().workspace;
|
|
18254
18361
|
try {
|
|
18255
|
-
|
|
18362
|
+
fs8.mkdirSync(path8.dirname(file), { recursive: true });
|
|
18256
18363
|
replaceFile(file, `${canonicalJson(record)}
|
|
18257
18364
|
`, true);
|
|
18258
18365
|
return success(null, null, null);
|
|
@@ -18262,7 +18369,7 @@ class TaskStore {
|
|
|
18262
18369
|
}
|
|
18263
18370
|
readIndex() {
|
|
18264
18371
|
try {
|
|
18265
|
-
const value = JSON.parse(
|
|
18372
|
+
const value = JSON.parse(fs8.readFileSync(this.paths().index, "utf8"));
|
|
18266
18373
|
if (!isObject5(value) || value.version !== INDEX_VERSION || !isObject5(value.tasks))
|
|
18267
18374
|
return {};
|
|
18268
18375
|
const tasks = {};
|
|
@@ -18276,7 +18383,7 @@ class TaskStore {
|
|
|
18276
18383
|
}
|
|
18277
18384
|
writeIndex(entries) {
|
|
18278
18385
|
try {
|
|
18279
|
-
|
|
18386
|
+
fs8.mkdirSync(this.paths().tasks, { recursive: true });
|
|
18280
18387
|
replaceFile(this.paths().index, JSON.stringify({
|
|
18281
18388
|
version: INDEX_VERSION,
|
|
18282
18389
|
tasks: Object.fromEntries(entries.map((entry) => [entry.id, entry]))
|
|
@@ -18330,23 +18437,23 @@ class TaskStore {
|
|
|
18330
18437
|
return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
|
|
18331
18438
|
}
|
|
18332
18439
|
v2Dirs() {
|
|
18333
|
-
return [...new Set([this.root, this.origin])].map((dir) =>
|
|
18440
|
+
return [...new Set([this.root, this.origin])].map((dir) => path8.join(dir, ".workit"));
|
|
18334
18441
|
}
|
|
18335
18442
|
pendingMigrations() {
|
|
18336
18443
|
return this.v2Dirs().filter((dir) => {
|
|
18337
|
-
const raw = this.readV2Workspace(
|
|
18444
|
+
const raw = this.readV2Workspace(path8.join(dir, "workspace.json"));
|
|
18338
18445
|
return raw !== null && raw !== TOMBSTONE;
|
|
18339
18446
|
});
|
|
18340
18447
|
}
|
|
18341
18448
|
migrateV2(v2) {
|
|
18342
18449
|
const dirs = this.paths();
|
|
18343
|
-
const v2Workspace =
|
|
18344
|
-
const v2Tasks =
|
|
18345
|
-
const legacy =
|
|
18450
|
+
const v2Workspace = path8.join(v2, "workspace.json");
|
|
18451
|
+
const v2Tasks = path8.join(v2, "tasks");
|
|
18452
|
+
const legacy = path8.join(dirs.dir, "legacy", checkoutSlug(path8.dirname(v2)), "v2");
|
|
18346
18453
|
const raw = this.readV2Workspace(v2Workspace);
|
|
18347
18454
|
if (raw === null || raw === TOMBSTONE)
|
|
18348
18455
|
return success(null, null, null);
|
|
18349
|
-
return this.withLock(dirs.checkoutLock, () => this.withLock(
|
|
18456
|
+
return this.withLock(dirs.checkoutLock, () => this.withLock(path8.join(v2, "metadata.lock"), () => {
|
|
18350
18457
|
const workspace = this.readV2Workspace(v2Workspace);
|
|
18351
18458
|
if (workspace === null || workspace === TOMBSTONE)
|
|
18352
18459
|
return success(null, null, null);
|
|
@@ -18355,7 +18462,7 @@ class TaskStore {
|
|
|
18355
18462
|
return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
|
|
18356
18463
|
let files = [];
|
|
18357
18464
|
try {
|
|
18358
|
-
files =
|
|
18465
|
+
files = fs8.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path8.join(v2Tasks, name)));
|
|
18359
18466
|
} catch {}
|
|
18360
18467
|
const current = this.readWorkspace();
|
|
18361
18468
|
if (!current.ok)
|
|
@@ -18373,22 +18480,22 @@ class TaskStore {
|
|
|
18373
18480
|
let skipped = 0;
|
|
18374
18481
|
const done = new Map;
|
|
18375
18482
|
try {
|
|
18376
|
-
|
|
18483
|
+
fs8.mkdirSync(path8.join(legacy, "tasks"), { recursive: true });
|
|
18377
18484
|
for (const name of ["workspace.json", "index.json"])
|
|
18378
|
-
if (
|
|
18379
|
-
|
|
18485
|
+
if (fs8.existsSync(path8.join(v2, name)))
|
|
18486
|
+
fs8.copyFileSync(path8.join(v2, name), path8.join(legacy, name));
|
|
18380
18487
|
for (const name of files) {
|
|
18381
18488
|
const id = name.slice(0, -5);
|
|
18382
|
-
const file =
|
|
18383
|
-
const bytes =
|
|
18489
|
+
const file = path8.join(v2Tasks, name);
|
|
18490
|
+
const bytes = fs8.readFileSync(file, "utf8");
|
|
18384
18491
|
const digest = sha256(bytes);
|
|
18385
|
-
|
|
18492
|
+
fs8.writeFileSync(path8.join(legacy, "tasks", name), bytes);
|
|
18386
18493
|
const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
|
|
18387
18494
|
if (!parsed.ok || parsed.data.id !== id)
|
|
18388
18495
|
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 });
|
|
18389
18496
|
const record = { ...parsed.data, workspaceId: target.id };
|
|
18390
|
-
const origin = { path:
|
|
18391
|
-
if (!
|
|
18497
|
+
const origin = { path: path8.join(legacy, "tasks", name), digest };
|
|
18498
|
+
if (!fs8.existsSync(this.eventsPath(id))) {
|
|
18392
18499
|
this.openTask(record, "migrated.from_v2", null, origin);
|
|
18393
18500
|
migrated += 1;
|
|
18394
18501
|
} else {
|
|
@@ -18423,8 +18530,8 @@ class TaskStore {
|
|
|
18423
18530
|
try {
|
|
18424
18531
|
let complete = true;
|
|
18425
18532
|
for (const [name, digest] of done) {
|
|
18426
|
-
const file =
|
|
18427
|
-
if (sha256(
|
|
18533
|
+
const file = path8.join(v2Tasks, name);
|
|
18534
|
+
if (sha256(fs8.readFileSync(file, "utf8")) !== digest) {
|
|
18428
18535
|
complete = false;
|
|
18429
18536
|
continue;
|
|
18430
18537
|
}
|
|
@@ -18434,7 +18541,7 @@ class TaskStore {
|
|
|
18434
18541
|
return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
|
|
18435
18542
|
path: v2
|
|
18436
18543
|
});
|
|
18437
|
-
|
|
18544
|
+
fs8.rmSync(path8.join(v2, "index.json"), { force: true });
|
|
18438
18545
|
this.writeTombstone(v2, target.id);
|
|
18439
18546
|
} catch (error) {
|
|
18440
18547
|
return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
|
|
@@ -18454,45 +18561,45 @@ class TaskStore {
|
|
|
18454
18561
|
}
|
|
18455
18562
|
adoptLocalStore() {
|
|
18456
18563
|
const dirs = this.paths();
|
|
18457
|
-
const marker =
|
|
18458
|
-
if (!dirs.shared || !
|
|
18564
|
+
const marker = path8.join(dirs.v2, "store.json");
|
|
18565
|
+
if (!dirs.shared || !fs8.existsSync(marker))
|
|
18459
18566
|
return success(null, null, null);
|
|
18460
18567
|
return this.withLock(dirs.checkoutLock, () => {
|
|
18461
|
-
if (!
|
|
18568
|
+
if (!fs8.existsSync(marker))
|
|
18462
18569
|
return success(null, null, null);
|
|
18463
18570
|
let tasks = 0;
|
|
18464
18571
|
try {
|
|
18465
18572
|
const merge = (from, to) => {
|
|
18466
|
-
if (!
|
|
18467
|
-
|
|
18573
|
+
if (!fs8.existsSync(to)) {
|
|
18574
|
+
fs8.mkdirSync(path8.dirname(to), { recursive: true });
|
|
18468
18575
|
try {
|
|
18469
|
-
|
|
18576
|
+
fs8.renameSync(from, to);
|
|
18470
18577
|
} catch {
|
|
18471
|
-
|
|
18472
|
-
|
|
18578
|
+
fs8.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
|
|
18579
|
+
fs8.rmSync(from, { recursive: true, force: true });
|
|
18473
18580
|
}
|
|
18474
18581
|
return;
|
|
18475
18582
|
}
|
|
18476
|
-
if (
|
|
18477
|
-
for (const name of
|
|
18478
|
-
merge(
|
|
18583
|
+
if (fs8.lstatSync(from).isDirectory() && fs8.lstatSync(to).isDirectory())
|
|
18584
|
+
for (const name of fs8.readdirSync(from))
|
|
18585
|
+
merge(path8.join(from, name), path8.join(to, name));
|
|
18479
18586
|
};
|
|
18480
18587
|
for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
|
|
18481
|
-
const from =
|
|
18482
|
-
if (!
|
|
18588
|
+
const from = path8.join(dirs.v2, sub);
|
|
18589
|
+
if (!fs8.existsSync(from))
|
|
18483
18590
|
continue;
|
|
18484
18591
|
if (sub === "tasks") {
|
|
18485
|
-
for (const name of
|
|
18486
|
-
if (validId(name) && !
|
|
18592
|
+
for (const name of fs8.readdirSync(from))
|
|
18593
|
+
if (validId(name) && !fs8.existsSync(path8.join(dirs.dir, sub, name)))
|
|
18487
18594
|
tasks += 1;
|
|
18488
18595
|
}
|
|
18489
|
-
merge(from,
|
|
18596
|
+
merge(from, path8.join(dirs.dir, sub));
|
|
18490
18597
|
}
|
|
18491
|
-
const ledger =
|
|
18492
|
-
if (
|
|
18493
|
-
merge(ledger,
|
|
18494
|
-
|
|
18495
|
-
|
|
18598
|
+
const ledger = path8.join(dirs.v2, "ledger");
|
|
18599
|
+
if (fs8.existsSync(ledger) && !fs8.existsSync(path8.join(dirs.dir, "ledger")))
|
|
18600
|
+
merge(ledger, path8.join(dirs.dir, "ledger"));
|
|
18601
|
+
fs8.rmSync(path8.join(dirs.v2, "task-index.json"), { force: true });
|
|
18602
|
+
fs8.renameSync(marker, path8.join(dirs.v2, "store.moved.json"));
|
|
18496
18603
|
} catch (error) {
|
|
18497
18604
|
return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
|
|
18498
18605
|
path: dirs.v2
|
|
@@ -18512,7 +18619,7 @@ class TaskStore {
|
|
|
18512
18619
|
readV2Workspace(file) {
|
|
18513
18620
|
let bytes;
|
|
18514
18621
|
try {
|
|
18515
|
-
bytes =
|
|
18622
|
+
bytes = fs8.readFileSync(file, "utf8");
|
|
18516
18623
|
} catch {
|
|
18517
18624
|
return null;
|
|
18518
18625
|
}
|
|
@@ -18530,7 +18637,7 @@ class TaskStore {
|
|
|
18530
18637
|
schemaVersion: SCHEMA_VERSION,
|
|
18531
18638
|
id: workspaceId ?? newId(),
|
|
18532
18639
|
revision: newRevision(),
|
|
18533
|
-
root:
|
|
18640
|
+
root: path8.dirname(v2),
|
|
18534
18641
|
runtime: { createdWith: version, updatedWith: version },
|
|
18535
18642
|
writer: null,
|
|
18536
18643
|
store: {
|
|
@@ -18541,12 +18648,12 @@ class TaskStore {
|
|
|
18541
18648
|
},
|
|
18542
18649
|
critical: ["store"]
|
|
18543
18650
|
};
|
|
18544
|
-
|
|
18545
|
-
const ignore =
|
|
18546
|
-
if (!
|
|
18547
|
-
|
|
18651
|
+
fs8.mkdirSync(v2, { recursive: true });
|
|
18652
|
+
const ignore = path8.join(v2, ".gitignore");
|
|
18653
|
+
if (!fs8.existsSync(ignore))
|
|
18654
|
+
fs8.writeFileSync(ignore, `*
|
|
18548
18655
|
`);
|
|
18549
|
-
replaceFile(
|
|
18656
|
+
replaceFile(path8.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
|
|
18550
18657
|
`, true);
|
|
18551
18658
|
}
|
|
18552
18659
|
stubFor(taskId) {
|
|
@@ -18562,7 +18669,7 @@ class TaskStore {
|
|
|
18562
18669
|
}
|
|
18563
18670
|
isStub(file) {
|
|
18564
18671
|
try {
|
|
18565
|
-
const value = JSON.parse(
|
|
18672
|
+
const value = JSON.parse(fs8.readFileSync(file, "utf8"));
|
|
18566
18673
|
return isObject5(value) && isObject5(value.store) && value.store.format === "workit-store";
|
|
18567
18674
|
} catch {
|
|
18568
18675
|
return false;
|
|
@@ -18570,32 +18677,32 @@ class TaskStore {
|
|
|
18570
18677
|
}
|
|
18571
18678
|
writeTombstoneIfMissing() {
|
|
18572
18679
|
try {
|
|
18573
|
-
const v2 =
|
|
18574
|
-
if (
|
|
18680
|
+
const v2 = path8.join(this.root, ".workit");
|
|
18681
|
+
if (fs8.existsSync(path8.join(v2, "workspace.json")))
|
|
18575
18682
|
return;
|
|
18576
18683
|
let id = null;
|
|
18577
18684
|
try {
|
|
18578
|
-
id = JSON.parse(
|
|
18685
|
+
id = JSON.parse(fs8.readFileSync(this.paths().workspace, "utf8")).id ?? null;
|
|
18579
18686
|
} catch {}
|
|
18580
18687
|
this.writeTombstone(v2, id);
|
|
18581
18688
|
} catch {}
|
|
18582
18689
|
}
|
|
18583
18690
|
initializeStorage() {
|
|
18584
18691
|
const dirs = this.paths();
|
|
18585
|
-
retryTransient(() =>
|
|
18586
|
-
retryTransient(() =>
|
|
18587
|
-
if (!
|
|
18692
|
+
retryTransient(() => fs8.mkdirSync(dirs.checkout, { recursive: true }));
|
|
18693
|
+
retryTransient(() => fs8.mkdirSync(dirs.tasks, { recursive: true }));
|
|
18694
|
+
if (!fs8.existsSync(dirs.marker))
|
|
18588
18695
|
replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
|
|
18589
18696
|
`, false);
|
|
18590
18697
|
if (!dirs.shared) {
|
|
18591
|
-
const ignore =
|
|
18698
|
+
const ignore = path8.join(dirs.dir, ".gitignore");
|
|
18592
18699
|
let current = null;
|
|
18593
18700
|
try {
|
|
18594
|
-
current =
|
|
18701
|
+
current = fs8.readFileSync(ignore, "utf8");
|
|
18595
18702
|
} catch {}
|
|
18596
18703
|
if (current !== `*
|
|
18597
18704
|
`)
|
|
18598
|
-
retryTransient(() =>
|
|
18705
|
+
retryTransient(() => fs8.writeFileSync(ignore, `*
|
|
18599
18706
|
`));
|
|
18600
18707
|
}
|
|
18601
18708
|
this.writeTombstoneIfMissing();
|
|
@@ -18611,7 +18718,7 @@ class TaskStore {
|
|
|
18611
18718
|
shouldReclaim: ({ payload, nowMs }) => {
|
|
18612
18719
|
let ageMs = null;
|
|
18613
18720
|
try {
|
|
18614
|
-
ageMs = nowMs -
|
|
18721
|
+
ageMs = nowMs - fs8.lstatSync(lockPath).mtimeMs;
|
|
18615
18722
|
} catch {}
|
|
18616
18723
|
return classifyLockOwner(payload, ageMs, localLockHost(), TASK_STORE_LOCK).state === "stale";
|
|
18617
18724
|
},
|
|
@@ -18648,7 +18755,7 @@ class TaskStore {
|
|
|
18648
18755
|
withLock(lockPath, operation) {
|
|
18649
18756
|
try {
|
|
18650
18757
|
this.initializeStorage();
|
|
18651
|
-
|
|
18758
|
+
fs8.mkdirSync(path8.dirname(lockPath), { recursive: true });
|
|
18652
18759
|
} catch (error) {
|
|
18653
18760
|
return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
|
|
18654
18761
|
path: this.paths().dir
|
|
@@ -18688,18 +18795,18 @@ class TaskStore {
|
|
|
18688
18795
|
lockDirWritable(lockPath) {
|
|
18689
18796
|
const probe = `${lockPath}.${process.pid}.${randomUUID3()}.probe`;
|
|
18690
18797
|
try {
|
|
18691
|
-
|
|
18798
|
+
fs8.closeSync(fs8.openSync(probe, "wx", 384));
|
|
18692
18799
|
} catch {
|
|
18693
18800
|
return false;
|
|
18694
18801
|
}
|
|
18695
18802
|
try {
|
|
18696
|
-
|
|
18803
|
+
fs8.rmSync(probe, { force: true });
|
|
18697
18804
|
} catch {}
|
|
18698
18805
|
return true;
|
|
18699
18806
|
}
|
|
18700
18807
|
lockHolderPresent(lockPath) {
|
|
18701
18808
|
try {
|
|
18702
|
-
|
|
18809
|
+
fs8.lstatSync(lockPath);
|
|
18703
18810
|
return true;
|
|
18704
18811
|
} catch (error) {
|
|
18705
18812
|
return error?.code !== "ENOENT";
|
|
@@ -18711,12 +18818,12 @@ class TaskStore {
|
|
|
18711
18818
|
if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
|
|
18712
18819
|
return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
|
|
18713
18820
|
path: lockPath,
|
|
18714
|
-
guidance: `Check permissions and the read-only attribute on ${
|
|
18821
|
+
guidance: `Check permissions and the read-only attribute on ${path8.dirname(lockPath)}.`
|
|
18715
18822
|
});
|
|
18716
18823
|
if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
|
|
18717
18824
|
let lock = null;
|
|
18718
18825
|
try {
|
|
18719
|
-
lock = parseMetadataLockOrNull(
|
|
18826
|
+
lock = parseMetadataLockOrNull(fs8.readFileSync(lockPath, "utf8"));
|
|
18720
18827
|
} catch {}
|
|
18721
18828
|
return failure2("busy", `workit lock is held by another Workit call${lock ? ` (pid ${lock.pid} on ${lock.host})` : ""}; retry shortly`, {
|
|
18722
18829
|
outcome: "not_started",
|
|
@@ -18734,15 +18841,15 @@ var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
|
|
|
18734
18841
|
var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
|
|
18735
18842
|
|
|
18736
18843
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
18737
|
-
import { createHash as
|
|
18738
|
-
import * as
|
|
18844
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
18845
|
+
import * as fs13 from "node:fs";
|
|
18739
18846
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
18740
|
-
import
|
|
18847
|
+
import path16 from "node:path";
|
|
18741
18848
|
|
|
18742
18849
|
// packages/workit-core/src/check-config.ts
|
|
18743
18850
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
18744
|
-
import
|
|
18745
|
-
import
|
|
18851
|
+
import fs9 from "node:fs";
|
|
18852
|
+
import path9 from "node:path";
|
|
18746
18853
|
var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
|
|
18747
18854
|
var CHECKS_FILE = "workit.checks.json";
|
|
18748
18855
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -18795,10 +18902,10 @@ var git = (cwd, args) => {
|
|
|
18795
18902
|
return value || null;
|
|
18796
18903
|
};
|
|
18797
18904
|
function checkRoot(cwd) {
|
|
18798
|
-
return
|
|
18905
|
+
return path9.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
|
|
18799
18906
|
}
|
|
18800
18907
|
var packageManager = (root) => {
|
|
18801
|
-
const has = (file) =>
|
|
18908
|
+
const has = (file) => fs9.existsSync(path9.join(root, file));
|
|
18802
18909
|
if (has("bun.lock") || has("bun.lockb"))
|
|
18803
18910
|
return "bun";
|
|
18804
18911
|
if (has("pnpm-lock.yaml"))
|
|
@@ -18818,7 +18925,7 @@ var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
|
|
|
18818
18925
|
function detectedChecks(root) {
|
|
18819
18926
|
const read = (file) => {
|
|
18820
18927
|
try {
|
|
18821
|
-
return
|
|
18928
|
+
return fs9.readFileSync(path9.join(root, file), "utf8");
|
|
18822
18929
|
} catch {
|
|
18823
18930
|
return null;
|
|
18824
18931
|
}
|
|
@@ -18857,7 +18964,7 @@ function detectedChecks(root) {
|
|
|
18857
18964
|
var GATES = ["testing", "verification"];
|
|
18858
18965
|
function loadCheckConfig(cwd) {
|
|
18859
18966
|
const root = checkRoot(cwd);
|
|
18860
|
-
const file =
|
|
18967
|
+
const file = path9.join(root, CHECKS_FILE);
|
|
18861
18968
|
const none = (error = null) => ({
|
|
18862
18969
|
source: error ? CHECKS_FILE : "none",
|
|
18863
18970
|
root,
|
|
@@ -18867,7 +18974,7 @@ function loadCheckConfig(cwd) {
|
|
|
18867
18974
|
});
|
|
18868
18975
|
let text = null;
|
|
18869
18976
|
try {
|
|
18870
|
-
text =
|
|
18977
|
+
text = fs9.readFileSync(file, "utf8");
|
|
18871
18978
|
} catch (error) {
|
|
18872
18979
|
if (error.code !== "ENOENT")
|
|
18873
18980
|
return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
|
|
@@ -18930,10 +19037,10 @@ function workitAvailable(env = process.env) {
|
|
|
18930
19037
|
return workitOnPath;
|
|
18931
19038
|
const windows = process.platform === "win32";
|
|
18932
19039
|
const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
|
|
18933
|
-
const dirs = (env.PATH ?? env.Path ?? "").split(
|
|
19040
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(path9.delimiter).filter(Boolean);
|
|
18934
19041
|
workitOnPath = dirs.some((dir) => exts.some((ext) => {
|
|
18935
19042
|
try {
|
|
18936
|
-
return
|
|
19043
|
+
return fs9.statSync(path9.join(dir, `workit${ext}`)).isFile();
|
|
18937
19044
|
} catch {
|
|
18938
19045
|
return false;
|
|
18939
19046
|
}
|
|
@@ -18946,10 +19053,10 @@ function checkCommand(args, version, onPath = workitAvailable()) {
|
|
|
18946
19053
|
|
|
18947
19054
|
// packages/workit-core/src/git/rev.ts
|
|
18948
19055
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
18949
|
-
import { createHash as
|
|
18950
|
-
import
|
|
19056
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
19057
|
+
import fs10 from "node:fs";
|
|
18951
19058
|
import os2 from "node:os";
|
|
18952
|
-
import
|
|
19059
|
+
import path10 from "node:path";
|
|
18953
19060
|
var MAX_BUFFER = 256 * 1024 * 1024;
|
|
18954
19061
|
var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
|
|
18955
19062
|
var git2 = (cwd, args, options = {}) => {
|
|
@@ -18984,10 +19091,10 @@ function headSha(cwd) {
|
|
|
18984
19091
|
var emptyTree = (cwd) => line(git2(cwd, ["mktree"], { input: "" }));
|
|
18985
19092
|
var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
|
|
18986
19093
|
var seedIndex = (from, to) => {
|
|
18987
|
-
|
|
18988
|
-
const { atimeNs, mtimeNs } =
|
|
19094
|
+
fs10.copyFileSync(from, to);
|
|
19095
|
+
const { atimeNs, mtimeNs } = fs10.statSync(from, { bigint: true });
|
|
18989
19096
|
const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
|
|
18990
|
-
|
|
19097
|
+
fs10.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
|
|
18991
19098
|
};
|
|
18992
19099
|
function worktreeTree(cwd, options = {}) {
|
|
18993
19100
|
const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
|
|
@@ -18996,10 +19103,10 @@ function worktreeTree(cwd, options = {}) {
|
|
|
18996
19103
|
return null;
|
|
18997
19104
|
const cap = options.maxUntrackedBytes ?? MAX_UNTRACKED_BYTES;
|
|
18998
19105
|
const timeoutMs = options.timeoutMs ?? GIT_TIMEOUTS.worktree;
|
|
18999
|
-
const scratch =
|
|
19106
|
+
const scratch = fs10.mkdtempSync(path10.join(os2.tmpdir(), "workit-tree-"));
|
|
19000
19107
|
try {
|
|
19001
|
-
const tempIndex =
|
|
19002
|
-
if (
|
|
19108
|
+
const tempIndex = path10.join(scratch, "index");
|
|
19109
|
+
if (fs10.existsSync(indexPath))
|
|
19003
19110
|
seedIndex(indexPath, tempIndex);
|
|
19004
19111
|
const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
|
|
19005
19112
|
const untracked = git2(top, ["ls-files", "--others", "--exclude-standard", "-z"], {
|
|
@@ -19011,7 +19118,7 @@ function worktreeTree(cwd, options = {}) {
|
|
|
19011
19118
|
const skipped = [];
|
|
19012
19119
|
for (const file of untracked.stdout.split("\x00").filter(Boolean)) {
|
|
19013
19120
|
try {
|
|
19014
|
-
const stat =
|
|
19121
|
+
const stat = fs10.statSync(path10.join(top, file));
|
|
19015
19122
|
if (stat.isFile() && stat.size > cap)
|
|
19016
19123
|
skipped.push({ path: file, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) });
|
|
19017
19124
|
} catch {}
|
|
@@ -19028,10 +19135,10 @@ function worktreeTree(cwd, options = {}) {
|
|
|
19028
19135
|
if (!tree)
|
|
19029
19136
|
return null;
|
|
19030
19137
|
const base = line(git2(top, ["rev-parse", "--verify", "-q", "HEAD^{tree}"])) ?? emptyTree(top);
|
|
19031
|
-
const key = skipped.length ? `sha256:${
|
|
19138
|
+
const key = skipped.length ? `sha256:${createHash6("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
|
|
19032
19139
|
return { tree, key, dirty: tree !== base || skipped.length > 0, skipped };
|
|
19033
19140
|
} finally {
|
|
19034
|
-
|
|
19141
|
+
fs10.rmSync(scratch, { recursive: true, force: true });
|
|
19035
19142
|
}
|
|
19036
19143
|
}
|
|
19037
19144
|
function worktreeSignal(cwd, options = {}) {
|
|
@@ -19051,16 +19158,16 @@ function worktreeSignal(cwd, options = {}) {
|
|
|
19051
19158
|
});
|
|
19052
19159
|
if (!status.ok)
|
|
19053
19160
|
return null;
|
|
19054
|
-
const hash =
|
|
19161
|
+
const hash = createHash6("sha256");
|
|
19055
19162
|
hash.update(`${headSha(top) ?? "unborn"}\x00`);
|
|
19056
19163
|
for (const entry of status.stdout.split("\x00")) {
|
|
19057
19164
|
if (entry.length < 4)
|
|
19058
19165
|
continue;
|
|
19059
19166
|
const file = entry.slice(3);
|
|
19060
|
-
const absolute =
|
|
19167
|
+
const absolute = path10.join(top, file);
|
|
19061
19168
|
let stat = "-";
|
|
19062
19169
|
try {
|
|
19063
|
-
const info =
|
|
19170
|
+
const info = fs10.lstatSync(absolute, { bigint: true });
|
|
19064
19171
|
stat = `${info.mode.toString(8)}:${info.size}:${info.mtimeNs}`;
|
|
19065
19172
|
if (info.isDirectory())
|
|
19066
19173
|
stat += `:${headSha(absolute) ?? "-"}`;
|
|
@@ -19127,17 +19234,17 @@ function pushRemoteName(cwd, branch) {
|
|
|
19127
19234
|
|
|
19128
19235
|
// packages/workit-core/src/core/policy/derive.ts
|
|
19129
19236
|
import { existsSync as existsSync4 } from "node:fs";
|
|
19130
|
-
import
|
|
19237
|
+
import path15 from "node:path";
|
|
19131
19238
|
|
|
19132
19239
|
// packages/workit-core/src/ledger.ts
|
|
19133
19240
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
19134
|
-
import { createHash as
|
|
19135
|
-
import
|
|
19136
|
-
import
|
|
19241
|
+
import { createHash as createHash9, randomBytes as randomBytes3 } from "node:crypto";
|
|
19242
|
+
import fs12 from "node:fs";
|
|
19243
|
+
import path14 from "node:path";
|
|
19137
19244
|
|
|
19138
19245
|
// packages/workit-core/src/core/vcs-config.ts
|
|
19139
|
-
import
|
|
19140
|
-
import
|
|
19246
|
+
import fs11 from "node:fs";
|
|
19247
|
+
import path13 from "node:path";
|
|
19141
19248
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
19142
19249
|
|
|
19143
19250
|
// packages/workit-core/src/core/config.ts
|
|
@@ -19151,7 +19258,7 @@ import {
|
|
|
19151
19258
|
writeFileSync as writeFileSync2
|
|
19152
19259
|
} from "node:fs";
|
|
19153
19260
|
import os3 from "node:os";
|
|
19154
|
-
import
|
|
19261
|
+
import path11 from "node:path";
|
|
19155
19262
|
|
|
19156
19263
|
// packages/workit-core/src/core/boundary.ts
|
|
19157
19264
|
import { statSync as statSync2 } from "node:fs";
|
|
@@ -19210,7 +19317,7 @@ var mergePreset = (preset, input = {}, current = {
|
|
|
19210
19317
|
protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
|
|
19211
19318
|
};
|
|
19212
19319
|
};
|
|
19213
|
-
var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ??
|
|
19320
|
+
var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path11.join(process.env.XDG_CONFIG_HOME || path11.join(os3.homedir(), ".config"), "workit");
|
|
19214
19321
|
var migratedDir = null;
|
|
19215
19322
|
var migrationFailed = false;
|
|
19216
19323
|
var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
@@ -19220,7 +19327,7 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
19220
19327
|
migratedDir = dir;
|
|
19221
19328
|
return dir;
|
|
19222
19329
|
}
|
|
19223
|
-
const legacy =
|
|
19330
|
+
const legacy = path11.join(process.env.XDG_CONFIG_HOME || path11.join(os3.homedir(), ".config"), "workflow-toolkit");
|
|
19224
19331
|
if (!existsSync3(legacy)) {
|
|
19225
19332
|
migratedDir = dir;
|
|
19226
19333
|
return dir;
|
|
@@ -19233,8 +19340,8 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
19233
19340
|
mkdirSync2(dir, { recursive: true });
|
|
19234
19341
|
diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
|
|
19235
19342
|
for (const entry of readdirSync2(legacy, { withFileTypes: true })) {
|
|
19236
|
-
const src =
|
|
19237
|
-
const dest =
|
|
19343
|
+
const src = path11.join(legacy, entry.name);
|
|
19344
|
+
const dest = path11.join(dir, entry.name);
|
|
19238
19345
|
if (existsSync3(dest))
|
|
19239
19346
|
continue;
|
|
19240
19347
|
try {
|
|
@@ -19306,7 +19413,7 @@ var parseConfigResult = (raw, file) => {
|
|
|
19306
19413
|
return { status: "valid", path: file, config };
|
|
19307
19414
|
};
|
|
19308
19415
|
var readConfigTyped = (dir) => {
|
|
19309
|
-
const file =
|
|
19416
|
+
const file = path11.join(dir ?? configDir(), "config.json");
|
|
19310
19417
|
return parseConfigResult(readSafe(file), file);
|
|
19311
19418
|
};
|
|
19312
19419
|
var readConfig = () => {
|
|
@@ -19315,7 +19422,7 @@ var readConfig = () => {
|
|
|
19315
19422
|
throw new Error(result.error);
|
|
19316
19423
|
return result.config ?? DEFAULTS;
|
|
19317
19424
|
};
|
|
19318
|
-
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ??
|
|
19425
|
+
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path11.join(configDir(), "vcs.json");
|
|
19319
19426
|
var branchGlobPattern = (glob) => new RegExp(`^${glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i");
|
|
19320
19427
|
var resolveBranchPolicy = (config, workspace, repoPreset) => {
|
|
19321
19428
|
const wp = workspace?.branchPolicy ?? {};
|
|
@@ -19347,9 +19454,9 @@ var COMMIT_PRESETS = [
|
|
|
19347
19454
|
|
|
19348
19455
|
// packages/workit-core/src/core/workspaces.ts
|
|
19349
19456
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
19350
|
-
import { createHash as
|
|
19457
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
19351
19458
|
import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
19352
|
-
import
|
|
19459
|
+
import path12 from "node:path";
|
|
19353
19460
|
|
|
19354
19461
|
// packages/workit-core/src/core/branch-policy.ts
|
|
19355
19462
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
@@ -19474,7 +19581,7 @@ var releaseTrackSchema = object({
|
|
|
19474
19581
|
context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
|
|
19475
19582
|
}
|
|
19476
19583
|
}
|
|
19477
|
-
if (track.versionSource?.kind === "package-json" && (
|
|
19584
|
+
if (track.versionSource?.kind === "package-json" && (path12.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
|
|
19478
19585
|
context.addIssue({
|
|
19479
19586
|
code: "custom",
|
|
19480
19587
|
path: ["versionSource", "path"],
|
|
@@ -19535,7 +19642,7 @@ var workspacesFileSchema = object({ workspaces: array(workspaceConfigSchema).opt
|
|
|
19535
19642
|
names.add(workspace.name);
|
|
19536
19643
|
}
|
|
19537
19644
|
});
|
|
19538
|
-
var workspacesRevision = (contents) =>
|
|
19645
|
+
var workspacesRevision = (contents) => createHash7("sha256").update(contents ?? "<missing>").digest("hex");
|
|
19539
19646
|
var validateWorkspacesDocument = (parsed, file) => {
|
|
19540
19647
|
if (!isConfigObject(parsed)) {
|
|
19541
19648
|
return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
|
|
@@ -19573,7 +19680,7 @@ var validateWorkspacesDocument = (parsed, file) => {
|
|
|
19573
19680
|
};
|
|
19574
19681
|
};
|
|
19575
19682
|
var readWorkspacesResult = (dir = configDir()) => {
|
|
19576
|
-
const file =
|
|
19683
|
+
const file = path12.join(dir, "workspaces.json");
|
|
19577
19684
|
let raw;
|
|
19578
19685
|
try {
|
|
19579
19686
|
raw = readFileSync3(file, "utf8");
|
|
@@ -19684,9 +19791,9 @@ var mainCheckoutOf = (cwd) => {
|
|
|
19684
19791
|
let main = null;
|
|
19685
19792
|
const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
|
|
19686
19793
|
const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
|
|
19687
|
-
if (gitDir && common &&
|
|
19794
|
+
if (gitDir && common && path12.resolve(gitDir) !== path12.resolve(common)) {
|
|
19688
19795
|
const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
|
|
19689
|
-
main = listed?.trim() || (
|
|
19796
|
+
main = listed?.trim() || (path12.basename(common) === ".git" ? path12.dirname(common) : common);
|
|
19690
19797
|
}
|
|
19691
19798
|
mainCheckouts.set(cwd, main);
|
|
19692
19799
|
return main;
|
|
@@ -19777,7 +19884,7 @@ var runtimeWorkspaceIndexSchema = object({
|
|
|
19777
19884
|
workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
19778
19885
|
}).passthrough();
|
|
19779
19886
|
var matchRuntimeWorkspace = (cwd, dir = configDir()) => {
|
|
19780
|
-
const file =
|
|
19887
|
+
const file = path12.join(dir, "workspaces.json");
|
|
19781
19888
|
let raw;
|
|
19782
19889
|
try {
|
|
19783
19890
|
raw = readFileSync3(file, "utf8");
|
|
@@ -19852,7 +19959,7 @@ function workspaceDefaultTarget(cwd, globalDefault) {
|
|
|
19852
19959
|
}
|
|
19853
19960
|
|
|
19854
19961
|
// packages/workit-core/src/core/release-tracks.ts
|
|
19855
|
-
import { createHash as
|
|
19962
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
19856
19963
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
19857
19964
|
var KNOWN_FIELDS = new Set([
|
|
19858
19965
|
"strategy",
|
|
@@ -20222,7 +20329,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
20222
20329
|
]) ?? "",
|
|
20223
20330
|
git3(cwd, ["config", "--get", recordedBaseKey(branch)]) ?? ""
|
|
20224
20331
|
] : [];
|
|
20225
|
-
key =
|
|
20332
|
+
key = createHash8("sha256").update(JSON.stringify({ cwd, input, refs, head, own })).digest("hex");
|
|
20226
20333
|
const hit = memo2.get(key);
|
|
20227
20334
|
if (hit)
|
|
20228
20335
|
return hit;
|
|
@@ -20237,7 +20344,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
20237
20344
|
}
|
|
20238
20345
|
|
|
20239
20346
|
// packages/workit-core/src/core/vcs-config.ts
|
|
20240
|
-
var workspacesPath = () =>
|
|
20347
|
+
var workspacesPath = () => path13.join(configDir(), "workspaces.json");
|
|
20241
20348
|
var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
20242
20349
|
var remoteProvider = (cwd) => {
|
|
20243
20350
|
const r = spawnSync8("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
|
|
@@ -20327,7 +20434,7 @@ var readVcsConfig = () => {
|
|
|
20327
20434
|
const file = vcsConfigPath();
|
|
20328
20435
|
let raw;
|
|
20329
20436
|
try {
|
|
20330
|
-
raw =
|
|
20437
|
+
raw = fs11.readFileSync(file, "utf8");
|
|
20331
20438
|
} catch {
|
|
20332
20439
|
return { status: "missing", path: file, config: {} };
|
|
20333
20440
|
}
|
|
@@ -20412,7 +20519,7 @@ function vcsConfig(mode, cwd, options = {}) {
|
|
|
20412
20519
|
const prov = cfg[provider] ?? {};
|
|
20413
20520
|
const out = {
|
|
20414
20521
|
ok: true,
|
|
20415
|
-
configPath:
|
|
20522
|
+
configPath: path13.resolve(cfgPath),
|
|
20416
20523
|
provider,
|
|
20417
20524
|
defaultTargetBranch: workspaceDefault,
|
|
20418
20525
|
pr: cfg.pr ?? {},
|
|
@@ -20623,7 +20730,7 @@ function diffHash(cwd, base, head) {
|
|
|
20623
20730
|
], GIT_TIMEOUTS.worktree);
|
|
20624
20731
|
if (!diff.ok || !diff.stdout)
|
|
20625
20732
|
return null;
|
|
20626
|
-
const hash =
|
|
20733
|
+
const hash = createHash9("sha256");
|
|
20627
20734
|
for (const line of diff.stdout.split(`
|
|
20628
20735
|
`)) {
|
|
20629
20736
|
if (line.startsWith("index "))
|
|
@@ -20635,16 +20742,16 @@ function diffHash(cwd, base, head) {
|
|
|
20635
20742
|
return `sha256:${hash.digest("hex")}`;
|
|
20636
20743
|
}
|
|
20637
20744
|
function storeRoot(cwd) {
|
|
20638
|
-
const location = resolveStore(
|
|
20745
|
+
const location = resolveStore(path14.resolve(cwd));
|
|
20639
20746
|
if (location instanceof Error)
|
|
20640
20747
|
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");
|
|
20641
20748
|
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
20642
20749
|
}
|
|
20643
20750
|
function ledgerPath(cwd) {
|
|
20644
20751
|
const root = storeRoot(cwd);
|
|
20645
|
-
return root.ok ? { ok: true, value:
|
|
20752
|
+
return root.ok ? { ok: true, value: path14.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
20646
20753
|
}
|
|
20647
|
-
var newId2 = () => `${Date.now().toString(36)}-${
|
|
20754
|
+
var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
|
|
20648
20755
|
var NETWORK_FS_TYPES = new Set([
|
|
20649
20756
|
26985,
|
|
20650
20757
|
20859,
|
|
@@ -20662,7 +20769,7 @@ var onNetworkFs = (dir) => {
|
|
|
20662
20769
|
if (process.platform !== "linux")
|
|
20663
20770
|
return false;
|
|
20664
20771
|
try {
|
|
20665
|
-
return isNetworkFsType(
|
|
20772
|
+
return isNetworkFsType(fs12.statfsSync(dir).type);
|
|
20666
20773
|
} catch {
|
|
20667
20774
|
return false;
|
|
20668
20775
|
}
|
|
@@ -20672,15 +20779,15 @@ var LOCK_STALE_MS = 30000;
|
|
|
20672
20779
|
var LOCK_OWNER = "owner";
|
|
20673
20780
|
var lockToken = (lock) => {
|
|
20674
20781
|
try {
|
|
20675
|
-
return
|
|
20782
|
+
return fs12.readFileSync(path14.join(lock, LOCK_OWNER), "utf8");
|
|
20676
20783
|
} catch {
|
|
20677
20784
|
return null;
|
|
20678
20785
|
}
|
|
20679
20786
|
};
|
|
20680
20787
|
var lockAgeMs = (lock) => {
|
|
20681
|
-
for (const target of [
|
|
20788
|
+
for (const target of [path14.join(lock, LOCK_OWNER), lock]) {
|
|
20682
20789
|
try {
|
|
20683
|
-
return Date.now() -
|
|
20790
|
+
return Date.now() - fs12.statSync(target).mtimeMs;
|
|
20684
20791
|
} catch {}
|
|
20685
20792
|
}
|
|
20686
20793
|
return null;
|
|
@@ -20688,55 +20795,55 @@ var lockAgeMs = (lock) => {
|
|
|
20688
20795
|
var ledgerLock = {
|
|
20689
20796
|
acquire(lock) {
|
|
20690
20797
|
try {
|
|
20691
|
-
|
|
20798
|
+
fs12.mkdirSync(lock);
|
|
20692
20799
|
} catch (error) {
|
|
20693
20800
|
if (error.code === "EEXIST")
|
|
20694
20801
|
return null;
|
|
20695
20802
|
throw error;
|
|
20696
20803
|
}
|
|
20697
|
-
const token = `${process.pid}-${
|
|
20698
|
-
|
|
20804
|
+
const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
|
|
20805
|
+
fs12.writeFileSync(path14.join(lock, LOCK_OWNER), token);
|
|
20699
20806
|
return token;
|
|
20700
20807
|
},
|
|
20701
20808
|
release(lock, token) {
|
|
20702
20809
|
if (lockToken(lock) !== token)
|
|
20703
20810
|
return false;
|
|
20704
|
-
const tomb = `${lock}.release-${
|
|
20811
|
+
const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
|
|
20705
20812
|
try {
|
|
20706
|
-
|
|
20813
|
+
fs12.renameSync(lock, tomb);
|
|
20707
20814
|
} catch {
|
|
20708
20815
|
return false;
|
|
20709
20816
|
}
|
|
20710
20817
|
if (lockToken(tomb) !== token) {
|
|
20711
20818
|
try {
|
|
20712
|
-
|
|
20819
|
+
fs12.renameSync(tomb, lock);
|
|
20713
20820
|
} catch {}
|
|
20714
20821
|
return false;
|
|
20715
20822
|
}
|
|
20716
|
-
|
|
20823
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
20717
20824
|
return true;
|
|
20718
20825
|
},
|
|
20719
20826
|
token: lockToken,
|
|
20720
20827
|
reap(lock, observed) {
|
|
20721
|
-
const tomb = `${lock}.reap-${
|
|
20828
|
+
const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
|
|
20722
20829
|
try {
|
|
20723
|
-
|
|
20830
|
+
fs12.renameSync(lock, tomb);
|
|
20724
20831
|
} catch {
|
|
20725
20832
|
return false;
|
|
20726
20833
|
}
|
|
20727
20834
|
if (lockToken(tomb) === observed) {
|
|
20728
|
-
|
|
20835
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
20729
20836
|
return true;
|
|
20730
20837
|
}
|
|
20731
20838
|
try {
|
|
20732
|
-
|
|
20839
|
+
fs12.renameSync(tomb, lock);
|
|
20733
20840
|
} catch {
|
|
20734
|
-
|
|
20841
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
20735
20842
|
}
|
|
20736
20843
|
return false;
|
|
20737
20844
|
}
|
|
20738
20845
|
};
|
|
20739
|
-
function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
20846
|
+
function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
|
|
20740
20847
|
const lock = `${file}.lock`;
|
|
20741
20848
|
const deadline = Date.now() + waitMs;
|
|
20742
20849
|
let token = null;
|
|
@@ -20758,14 +20865,14 @@ function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
|
20758
20865
|
ledgerLock.release(lock, token);
|
|
20759
20866
|
}
|
|
20760
20867
|
}
|
|
20761
|
-
var sha2562 = (text) =>
|
|
20868
|
+
var sha2562 = (text) => createHash9("sha256").update(text).digest("hex");
|
|
20762
20869
|
var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
|
|
20763
20870
|
${body}`);
|
|
20764
20871
|
var TAIL_BYTES = 64 * 1024;
|
|
20765
20872
|
var lastLine = (fd, size) => {
|
|
20766
20873
|
for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
|
|
20767
20874
|
const buffer = Buffer.alloc(span);
|
|
20768
|
-
|
|
20875
|
+
fs12.readSync(fd, buffer, 0, span, size - span);
|
|
20769
20876
|
const end = buffer.lastIndexOf(10);
|
|
20770
20877
|
if (end < 0) {
|
|
20771
20878
|
if (span === size)
|
|
@@ -20794,15 +20901,23 @@ var lastLine = (fd, size) => {
|
|
|
20794
20901
|
}
|
|
20795
20902
|
return null;
|
|
20796
20903
|
};
|
|
20904
|
+
var tighten = (fd) => {
|
|
20905
|
+
try {
|
|
20906
|
+
if ((fs12.fstatSync(fd).mode & 63) !== 0)
|
|
20907
|
+
fs12.fchmodSync(fd, 384);
|
|
20908
|
+
} catch {}
|
|
20909
|
+
};
|
|
20797
20910
|
function appendLine(file, build, options) {
|
|
20798
20911
|
const write = () => {
|
|
20799
|
-
const fd =
|
|
20912
|
+
const fd = fs12.openSync(file, "a+", 384);
|
|
20800
20913
|
try {
|
|
20801
|
-
|
|
20914
|
+
if (process.platform !== "win32")
|
|
20915
|
+
tighten(fd);
|
|
20916
|
+
const size = fs12.fstatSync(fd).size;
|
|
20802
20917
|
let fence = "";
|
|
20803
20918
|
if (size > 0) {
|
|
20804
20919
|
const last = Buffer.alloc(1);
|
|
20805
|
-
|
|
20920
|
+
fs12.readSync(fd, last, 0, 1, size - 1);
|
|
20806
20921
|
if (last[0] !== 10)
|
|
20807
20922
|
fence = `
|
|
20808
20923
|
`;
|
|
@@ -20811,17 +20926,17 @@ function appendLine(file, build, options) {
|
|
|
20811
20926
|
if (!line.ok)
|
|
20812
20927
|
return line;
|
|
20813
20928
|
const bytes = Buffer.from(fence + line.value);
|
|
20814
|
-
const written =
|
|
20929
|
+
const written = fs12.writeSync(fd, bytes);
|
|
20815
20930
|
if (written !== bytes.length)
|
|
20816
20931
|
return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
|
|
20817
20932
|
return { ok: true, value: undefined };
|
|
20818
20933
|
} finally {
|
|
20819
|
-
|
|
20934
|
+
fs12.closeSync(fd);
|
|
20820
20935
|
}
|
|
20821
20936
|
};
|
|
20822
20937
|
try {
|
|
20823
|
-
|
|
20824
|
-
if (!options.forceLock && !onNetworkFs(
|
|
20938
|
+
fs12.mkdirSync(path14.dirname(file), { recursive: true });
|
|
20939
|
+
if (!options.forceLock && !onNetworkFs(path14.dirname(file)))
|
|
20825
20940
|
return write();
|
|
20826
20941
|
const locked = withLedgerLock(file, write);
|
|
20827
20942
|
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
@@ -20889,8 +21004,10 @@ function readLedger(cwd, options = {}) {
|
|
|
20889
21004
|
if (!file.ok)
|
|
20890
21005
|
return file;
|
|
20891
21006
|
let text = "";
|
|
21007
|
+
let stat = null;
|
|
20892
21008
|
try {
|
|
20893
|
-
|
|
21009
|
+
stat = fs12.statSync(file.value);
|
|
21010
|
+
text = fs12.readFileSync(file.value, "utf8");
|
|
20894
21011
|
} catch (error) {
|
|
20895
21012
|
if (error.code === "ENOENT")
|
|
20896
21013
|
return {
|
|
@@ -20909,6 +21026,7 @@ function readLedger(cwd, options = {}) {
|
|
|
20909
21026
|
const integrity = { chained: 0, legacy: 0, unverified: [] };
|
|
20910
21027
|
const earlier = new Set([null]);
|
|
20911
21028
|
let chainStarted = false;
|
|
21029
|
+
const ids = new Set;
|
|
20912
21030
|
for (const raw of text.split(`
|
|
20913
21031
|
`)) {
|
|
20914
21032
|
const trimmed = raw.trim();
|
|
@@ -20942,6 +21060,10 @@ function readLedger(cwd, options = {}) {
|
|
|
20942
21060
|
reason = "unsigned";
|
|
20943
21061
|
else
|
|
20944
21062
|
rowIntegrity = "legacy";
|
|
21063
|
+
if (options.integrity && !reason && ids.has(parsed.id))
|
|
21064
|
+
reason = "duplicate_id";
|
|
21065
|
+
if (options.integrity)
|
|
21066
|
+
ids.add(parsed.id);
|
|
20945
21067
|
if (reason)
|
|
20946
21068
|
rowIntegrity = "unverified";
|
|
20947
21069
|
const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
|
|
@@ -20965,7 +21087,8 @@ function readLedger(cwd, options = {}) {
|
|
|
20965
21087
|
seq: rows.length + 1,
|
|
20966
21088
|
superseded: false,
|
|
20967
21089
|
supersedeIgnored: false,
|
|
20968
|
-
...rowIntegrity ? { integrity: rowIntegrity } : {}
|
|
21090
|
+
...rowIntegrity ? { integrity: rowIntegrity } : {},
|
|
21091
|
+
...reason ? { integrityReason: reason } : {}
|
|
20969
21092
|
};
|
|
20970
21093
|
rows.push(row);
|
|
20971
21094
|
if (!options.integrity)
|
|
@@ -20993,18 +21116,48 @@ function readLedger(cwd, options = {}) {
|
|
|
20993
21116
|
const byId = new Map;
|
|
20994
21117
|
for (const row of rows) {
|
|
20995
21118
|
const target = row.supersedes ? byId.get(row.supersedes) : undefined;
|
|
20996
|
-
if (target && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
21119
|
+
if (target && row.integrity !== "unverified" && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
20997
21120
|
target.superseded = true;
|
|
20998
21121
|
else if (row.supersedes)
|
|
20999
21122
|
row.supersedeIgnored = true;
|
|
21000
21123
|
if (!byId.has(row.id))
|
|
21001
21124
|
byId.set(row.id, row);
|
|
21002
21125
|
}
|
|
21126
|
+
if (options.integrity && stat && Buffer.byteLength(text) === stat.size)
|
|
21127
|
+
tamperCache.set(file.value, {
|
|
21128
|
+
size: stat.size,
|
|
21129
|
+
stamp: fileStamp(stat),
|
|
21130
|
+
breaks: integrity.unverified.filter((row) => TAMPER_REASONS.has(row.reason))
|
|
21131
|
+
});
|
|
21003
21132
|
return {
|
|
21004
21133
|
ok: true,
|
|
21005
21134
|
value: { path: file.value, rows, skipped, integrity: options.integrity ? integrity : null }
|
|
21006
21135
|
};
|
|
21007
21136
|
}
|
|
21137
|
+
var TAMPER_REASONS = new Set(["hash_mismatch", "broken_link"]);
|
|
21138
|
+
var tamperCache = new Map;
|
|
21139
|
+
var fileStamp = (stat) => `${stat.ino}:${stat.mtimeMs}:${stat.ctimeMs}`;
|
|
21140
|
+
function ledgerTampering(cwd) {
|
|
21141
|
+
const file = ledgerPath(cwd);
|
|
21142
|
+
if (!file.ok)
|
|
21143
|
+
return [];
|
|
21144
|
+
try {
|
|
21145
|
+
const stat = fs12.statSync(file.value);
|
|
21146
|
+
const cached = tamperCache.get(file.value);
|
|
21147
|
+
if (cached && cached.size === stat.size && cached.stamp === fileStamp(stat))
|
|
21148
|
+
return cached.breaks;
|
|
21149
|
+
} catch {
|
|
21150
|
+
return [];
|
|
21151
|
+
}
|
|
21152
|
+
const read = readLedger(cwd, { integrity: true });
|
|
21153
|
+
return read.ok ? (read.value.integrity?.unverified ?? []).filter((row) => TAMPER_REASONS.has(row.reason)) : [];
|
|
21154
|
+
}
|
|
21155
|
+
function tamperedUnblock(cwd) {
|
|
21156
|
+
const file = ledgerPath(cwd);
|
|
21157
|
+
const where = file.ok ? file.value : "the ledger file";
|
|
21158
|
+
return `workit ledger verify-integrity # lists the edited or missing rows; if the edit is understood, move ${where} aside (mv ${where} ${where}.tampered-<date>) and record the verdicts again`;
|
|
21159
|
+
}
|
|
21160
|
+
var tamperedWarning = (cwd, breaks) => `the ledger was edited, or a row was removed or moved (${breaks.slice(0, 3).map((row) => `${row.reason} at row ${row.seq}`).join(", ")}${breaks.length > 3 ? ", …" : ""}): no verdict is accepted until it is repaired. ${tamperedUnblock(cwd)}`;
|
|
21008
21161
|
function verdictBasis(verdict, current) {
|
|
21009
21162
|
if (verdict.head && verdict.head === current.head)
|
|
21010
21163
|
return "fresh";
|
|
@@ -21012,7 +21165,7 @@ function verdictBasis(verdict, current) {
|
|
|
21012
21165
|
return "carried";
|
|
21013
21166
|
return "stale";
|
|
21014
21167
|
}
|
|
21015
|
-
var isLiveVerdict = (row) => row.type === "verdict" && typeof row.result === "string" && !row.
|
|
21168
|
+
var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id);
|
|
21016
21169
|
function trailerSessions(cwd, base, head) {
|
|
21017
21170
|
if (!base || !head || !safeRef(base) || !safeRef(head))
|
|
21018
21171
|
return [];
|
|
@@ -21093,6 +21246,56 @@ var authoredBy = (authors, session) => {
|
|
|
21093
21246
|
return true;
|
|
21094
21247
|
return false;
|
|
21095
21248
|
};
|
|
21249
|
+
var TRUST_REASONS = new Set([
|
|
21250
|
+
"unsigned",
|
|
21251
|
+
"hash_mismatch",
|
|
21252
|
+
"broken_link",
|
|
21253
|
+
"duplicate_id",
|
|
21254
|
+
"unissued_session",
|
|
21255
|
+
"unbound_session"
|
|
21256
|
+
]);
|
|
21257
|
+
var SHELL_VOUCHED_HOSTS = new Set([
|
|
21258
|
+
"cli",
|
|
21259
|
+
"opencode",
|
|
21260
|
+
"pi",
|
|
21261
|
+
"codex_cli",
|
|
21262
|
+
"codex_desktop"
|
|
21263
|
+
]);
|
|
21264
|
+
function sessionTrust(cwd, row) {
|
|
21265
|
+
const session = row.actor.session;
|
|
21266
|
+
if (!session || row.self === true)
|
|
21267
|
+
return null;
|
|
21268
|
+
const source = typeof row.sessionSource === "string" ? row.sessionSource : "env";
|
|
21269
|
+
const host = row.actor.host;
|
|
21270
|
+
const issuedOnly = source === "flag" ? host !== "cli" : source === "env" && session.includes(":") && !SHELL_VOUCHED_HOSTS.has(host);
|
|
21271
|
+
if (!issuedOnly)
|
|
21272
|
+
return null;
|
|
21273
|
+
const binding = issuedBinding(cwd, session);
|
|
21274
|
+
if (binding === null)
|
|
21275
|
+
return "unissued_session";
|
|
21276
|
+
return binding === "token" && row.sessionBound !== true ? "unbound_session" : null;
|
|
21277
|
+
}
|
|
21278
|
+
function trustReasons(cwd, row) {
|
|
21279
|
+
if (row.integrity === "unverified")
|
|
21280
|
+
return [row.integrityReason ?? "unsigned"];
|
|
21281
|
+
if (row.integrity !== "verified")
|
|
21282
|
+
return ["unsigned"];
|
|
21283
|
+
const session = sessionTrust(cwd, row);
|
|
21284
|
+
return session ? [session] : [];
|
|
21285
|
+
}
|
|
21286
|
+
function withIntegrity(cwd, rows) {
|
|
21287
|
+
if (rows.every((row) => row.integrity !== undefined))
|
|
21288
|
+
return rows;
|
|
21289
|
+
const read = readLedger(cwd, { integrity: true });
|
|
21290
|
+
const bySeq = new Map(read.ok ? read.value.rows.map((row) => [row.seq, row]) : []);
|
|
21291
|
+
return rows.map((row) => {
|
|
21292
|
+
if (row.integrity !== undefined)
|
|
21293
|
+
return row;
|
|
21294
|
+
const fresh = bySeq.get(row.seq);
|
|
21295
|
+
return fresh && fresh.id === row.id && fresh.at === row.at ? fresh : row;
|
|
21296
|
+
});
|
|
21297
|
+
}
|
|
21298
|
+
var untrusted = (reasons) => reasons.some((reason) => TRUST_REASONS.has(reason));
|
|
21096
21299
|
var independenceReasons = (row, authors) => {
|
|
21097
21300
|
const reasons = [];
|
|
21098
21301
|
if (!row.actor.session)
|
|
@@ -21103,15 +21306,17 @@ var independenceReasons = (row, authors) => {
|
|
|
21103
21306
|
reasons.push("self");
|
|
21104
21307
|
return reasons;
|
|
21105
21308
|
};
|
|
21106
|
-
function effectiveOf(rows,
|
|
21309
|
+
function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
|
|
21107
21310
|
let effective = null;
|
|
21108
21311
|
for (const row of rows) {
|
|
21109
21312
|
if (!effective) {
|
|
21110
21313
|
effective = row;
|
|
21111
21314
|
continue;
|
|
21112
21315
|
}
|
|
21113
|
-
|
|
21114
|
-
|
|
21316
|
+
if (untrusted(reasonsOf(row)) && !untrusted(reasonsOf(effective)))
|
|
21317
|
+
continue;
|
|
21318
|
+
const rowIndependent = reasonsOf(row).length === 0;
|
|
21319
|
+
const effIndependent = reasonsOf(effective).length === 0;
|
|
21115
21320
|
if (effIndependent && !rowIndependent && isCurrent(effective))
|
|
21116
21321
|
continue;
|
|
21117
21322
|
if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session && sameCode(effective, row))
|
|
@@ -21125,7 +21330,7 @@ function restackCarries(rows, branch, from, to) {
|
|
|
21125
21330
|
return false;
|
|
21126
21331
|
const edges = new Map;
|
|
21127
21332
|
for (const row of rows)
|
|
21128
|
-
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
|
|
21333
|
+
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
|
|
21129
21334
|
edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
|
|
21130
21335
|
const seen = new Set([from]);
|
|
21131
21336
|
const queue = [from];
|
|
@@ -21141,7 +21346,8 @@ function restackCarries(rows, branch, from, to) {
|
|
|
21141
21346
|
}
|
|
21142
21347
|
return false;
|
|
21143
21348
|
}
|
|
21144
|
-
function checkVerdicts(cwd, branch,
|
|
21349
|
+
function checkVerdicts(cwd, branch, ledgerRows) {
|
|
21350
|
+
const rows = withIntegrity(cwd, ledgerRows);
|
|
21145
21351
|
const head = branchHead(cwd, branch);
|
|
21146
21352
|
const fallbackBase = defaultBase(cwd);
|
|
21147
21353
|
const scope = branchScope(cwd, branch);
|
|
@@ -21171,21 +21377,47 @@ function checkVerdicts(cwd, branch, rows) {
|
|
|
21171
21377
|
return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
|
|
21172
21378
|
};
|
|
21173
21379
|
const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
|
|
21380
|
+
const failureAuthorCache = new Map;
|
|
21381
|
+
const failureAuthors = (row) => {
|
|
21382
|
+
let set = failureAuthorCache.get(row.seq);
|
|
21383
|
+
if (!set) {
|
|
21384
|
+
set = authorSessions(cwd, rows.filter((other) => other.integrity === "verified" && other.seq < row.seq), branch, { base: null, head: null }, scope);
|
|
21385
|
+
failureAuthorCache.set(row.seq, set);
|
|
21386
|
+
}
|
|
21387
|
+
return set;
|
|
21388
|
+
};
|
|
21389
|
+
const reasonCache = new Map;
|
|
21390
|
+
const reasonsOf = (row) => {
|
|
21391
|
+
let reasons = reasonCache.get(row);
|
|
21392
|
+
if (!reasons) {
|
|
21393
|
+
const failing = FAILING.has(String(row.result));
|
|
21394
|
+
reasons = [
|
|
21395
|
+
...trustReasons(cwd, row),
|
|
21396
|
+
...independenceReasons(row, failing ? failureAuthors(row) : authors)
|
|
21397
|
+
];
|
|
21398
|
+
reasonCache.set(row, reasons);
|
|
21399
|
+
}
|
|
21400
|
+
return reasons;
|
|
21401
|
+
};
|
|
21402
|
+
const retired = new Set;
|
|
21403
|
+
for (const row of rows)
|
|
21404
|
+
if (row.type === "verdict" && row.supersedes && !row.supersedeIgnored && trustReasons(cwd, row).length === 0)
|
|
21405
|
+
retired.add(row.supersedes);
|
|
21174
21406
|
const byKind = new Map;
|
|
21175
21407
|
for (const row of rows)
|
|
21176
|
-
if (isLiveVerdict(row) && scope(row)) {
|
|
21408
|
+
if (isLiveVerdict(row, retired) && scope(row)) {
|
|
21177
21409
|
const kind = typeof row.kind === "string" ? row.kind : "review";
|
|
21178
21410
|
byKind.set(kind, [...byKind.get(kind) ?? [], row]);
|
|
21179
21411
|
}
|
|
21180
21412
|
const verdicts = [];
|
|
21181
21413
|
const warnings = [];
|
|
21182
21414
|
for (const [kind, list] of byKind) {
|
|
21183
|
-
const row = effectiveOf(list,
|
|
21415
|
+
const row = effectiveOf(list, reasonsOf, sameCode, (candidate) => basisOf(candidate) !== "stale");
|
|
21184
21416
|
if (!row)
|
|
21185
21417
|
continue;
|
|
21186
21418
|
const basis = basisOf(row);
|
|
21187
21419
|
const current = basis !== "stale";
|
|
21188
|
-
const independence =
|
|
21420
|
+
const independence = reasonsOf(row);
|
|
21189
21421
|
const reasons = [
|
|
21190
21422
|
...current ? [] : ["stale"],
|
|
21191
21423
|
...PASSING.has(String(row.result)) ? [] : ["not_passing"],
|
|
@@ -21202,23 +21434,31 @@ function checkVerdicts(cwd, branch, rows) {
|
|
|
21202
21434
|
});
|
|
21203
21435
|
if (reasons.length === 0) {
|
|
21204
21436
|
for (const later of list)
|
|
21205
|
-
if (later.seq > row.seq &&
|
|
21437
|
+
if (later.seq > row.seq && reasonsOf(later).length > 0 && !untrusted(reasonsOf(later)) && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
|
|
21206
21438
|
warnings.push(`the author's own (self) ${String(later.result)} ${kind} verdict ${later.id} by ${later.actor.session ?? "no session"} on the current code is newer than the accepted pass ${row.id}: read it before merging`);
|
|
21207
21439
|
}
|
|
21208
21440
|
}
|
|
21209
21441
|
for (const row of rows)
|
|
21210
21442
|
if (row.integrity === "unverified" && scope(row))
|
|
21211
21443
|
warnings.push(`ledger row ${row.id} (${row.type}) on ${row.branch ?? branch} is unverified: the workit CLI did not write it or it changed afterwards (workit ledger verify-integrity)`);
|
|
21444
|
+
const tampered = ledgerTampering(cwd);
|
|
21445
|
+
if (tampered.length) {
|
|
21446
|
+
warnings.push(tamperedWarning(cwd, tampered));
|
|
21447
|
+
for (const entry of verdicts) {
|
|
21448
|
+
entry.accepted = false;
|
|
21449
|
+
entry.reasons = [...entry.reasons, "ledger_tampered"];
|
|
21450
|
+
}
|
|
21451
|
+
}
|
|
21212
21452
|
verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
|
|
21213
21453
|
const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
|
|
21214
21454
|
const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
|
|
21215
21455
|
const newestAccepted = verdicts.findLast((entry) => entry.accepted && STRONG_RESULTS.has(String(entry.verdict.result))) ?? null;
|
|
21216
|
-
const accepted = newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
21456
|
+
const accepted = tampered.length ? { accepted: false, verdict: null, reasons: ["ledger_tampered"] } : newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
21217
21457
|
accepted: false,
|
|
21218
21458
|
verdict: failing?.verdict ?? null,
|
|
21219
21459
|
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
21220
21460
|
};
|
|
21221
|
-
const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
21461
|
+
const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
21222
21462
|
return {
|
|
21223
21463
|
branch,
|
|
21224
21464
|
head,
|
|
@@ -21381,7 +21621,7 @@ var productDecision = (task, ledger) => {
|
|
|
21381
21621
|
};
|
|
21382
21622
|
};
|
|
21383
21623
|
var planRecorded = (task, root) => {
|
|
21384
|
-
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(
|
|
21624
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path15.resolve(root, ref.path)));
|
|
21385
21625
|
if (cited)
|
|
21386
21626
|
return {
|
|
21387
21627
|
met: true,
|
|
@@ -21429,15 +21669,15 @@ function derivedRuleStatus(task, requirement, root, ledger) {
|
|
|
21429
21669
|
}
|
|
21430
21670
|
|
|
21431
21671
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
21432
|
-
var digestBytes = (value) =>
|
|
21433
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
21434
|
-
var relative = (root, candidate) =>
|
|
21672
|
+
var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
|
|
21673
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path16.sep}`);
|
|
21674
|
+
var relative = (root, candidate) => path16.relative(root, candidate).split(path16.sep).join("/") || ".";
|
|
21435
21675
|
var canonicalPath = (value) => {
|
|
21436
21676
|
if (typeof value !== "string" || !value)
|
|
21437
21677
|
return null;
|
|
21438
21678
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
21439
21679
|
return null;
|
|
21440
|
-
const normalized =
|
|
21680
|
+
const normalized = path16.posix.normalize(value);
|
|
21441
21681
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
21442
21682
|
return null;
|
|
21443
21683
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -21485,21 +21725,21 @@ var gitPaths = (root) => {
|
|
|
21485
21725
|
git: !/not a git repository/i.test(stderr)
|
|
21486
21726
|
};
|
|
21487
21727
|
}
|
|
21488
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
21728
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")) : [];
|
|
21489
21729
|
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
21490
21730
|
if (stagedDeleted.status !== 0) {
|
|
21491
21731
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
21492
21732
|
if (!/not a git repository/i.test(stderr))
|
|
21493
21733
|
return { paths, uncertain: true, git: true };
|
|
21494
21734
|
} else if (stagedDeleted.stdout) {
|
|
21495
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
21735
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")));
|
|
21496
21736
|
}
|
|
21497
21737
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
21498
21738
|
};
|
|
21499
21739
|
var walk = (root, directory, output) => {
|
|
21500
21740
|
let names;
|
|
21501
21741
|
try {
|
|
21502
|
-
names =
|
|
21742
|
+
names = fs13.readdirSync(directory);
|
|
21503
21743
|
} catch {
|
|
21504
21744
|
return true;
|
|
21505
21745
|
}
|
|
@@ -21507,10 +21747,10 @@ var walk = (root, directory, output) => {
|
|
|
21507
21747
|
for (const name of names) {
|
|
21508
21748
|
if (name === ".git" || name === ".workit")
|
|
21509
21749
|
continue;
|
|
21510
|
-
const target =
|
|
21750
|
+
const target = path16.join(directory, name);
|
|
21511
21751
|
let stat;
|
|
21512
21752
|
try {
|
|
21513
|
-
stat =
|
|
21753
|
+
stat = fs13.lstatSync(target);
|
|
21514
21754
|
} catch {
|
|
21515
21755
|
uncertain = true;
|
|
21516
21756
|
continue;
|
|
@@ -21525,14 +21765,14 @@ var walk = (root, directory, output) => {
|
|
|
21525
21765
|
var scopeRoots = (root, scope) => {
|
|
21526
21766
|
const roots = [];
|
|
21527
21767
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
21528
|
-
const target =
|
|
21768
|
+
const target = path16.resolve(root, item);
|
|
21529
21769
|
if (!inside(root, target))
|
|
21530
21770
|
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` }] });
|
|
21531
21771
|
let ancestor = target;
|
|
21532
|
-
while (!
|
|
21533
|
-
ancestor =
|
|
21772
|
+
while (!fs13.existsSync(ancestor) && ancestor !== root)
|
|
21773
|
+
ancestor = path16.dirname(ancestor);
|
|
21534
21774
|
try {
|
|
21535
|
-
if (!inside(root,
|
|
21775
|
+
if (!inside(root, fs13.realpathSync(ancestor)))
|
|
21536
21776
|
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` }] });
|
|
21537
21777
|
} catch {
|
|
21538
21778
|
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
@@ -21544,7 +21784,7 @@ var scopeRoots = (root, scope) => {
|
|
|
21544
21784
|
function captureCandidate(root, scope, environment = []) {
|
|
21545
21785
|
let checkout;
|
|
21546
21786
|
try {
|
|
21547
|
-
checkout =
|
|
21787
|
+
checkout = fs13.realpathSync(root);
|
|
21548
21788
|
} catch {
|
|
21549
21789
|
return failure2("invalid_input", "candidate root does not exist");
|
|
21550
21790
|
}
|
|
@@ -21562,11 +21802,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
21562
21802
|
if (item === ".git" || item === ".workit")
|
|
21563
21803
|
continue;
|
|
21564
21804
|
names.add(item);
|
|
21565
|
-
if (!
|
|
21805
|
+
if (!fs13.existsSync(target))
|
|
21566
21806
|
continue;
|
|
21567
21807
|
let stat;
|
|
21568
21808
|
try {
|
|
21569
|
-
stat =
|
|
21809
|
+
stat = fs13.lstatSync(target);
|
|
21570
21810
|
} catch {
|
|
21571
21811
|
uncertain = true;
|
|
21572
21812
|
continue;
|
|
@@ -21582,17 +21822,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
21582
21822
|
for (const item of [...names].toSorted()) {
|
|
21583
21823
|
if (!scopeMatches(item, normalizedScope))
|
|
21584
21824
|
continue;
|
|
21585
|
-
const target =
|
|
21825
|
+
const target = path16.join(checkout, item);
|
|
21586
21826
|
try {
|
|
21587
|
-
const stat =
|
|
21827
|
+
const stat = fs13.lstatSync(target);
|
|
21588
21828
|
if (stat.isSymbolicLink()) {
|
|
21589
|
-
const link =
|
|
21829
|
+
const link = fs13.readlinkSync(target);
|
|
21590
21830
|
files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
|
|
21591
21831
|
} else if (stat.isFile()) {
|
|
21592
21832
|
files.push({
|
|
21593
21833
|
path: item,
|
|
21594
21834
|
kind: "file",
|
|
21595
|
-
digest: digestBytes(
|
|
21835
|
+
digest: digestBytes(fs13.readFileSync(target)),
|
|
21596
21836
|
executable: (stat.mode & 73) !== 0
|
|
21597
21837
|
});
|
|
21598
21838
|
} else if (!stat.isDirectory()) {
|
|
@@ -21757,7 +21997,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
|
|
|
21757
21997
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
21758
21998
|
let root;
|
|
21759
21999
|
try {
|
|
21760
|
-
root =
|
|
22000
|
+
root = fs13.realpathSync(checkoutRoot);
|
|
21761
22001
|
} catch {
|
|
21762
22002
|
return failure2("permission_denied", "checkout root is unavailable");
|
|
21763
22003
|
}
|
|
@@ -21766,23 +22006,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
21766
22006
|
continue;
|
|
21767
22007
|
if (!reference.digest)
|
|
21768
22008
|
return failure2("invalid_input", "document references require a byte digest");
|
|
21769
|
-
const target =
|
|
21770
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
22009
|
+
const target = path16.resolve(root, reference.path);
|
|
22010
|
+
if (target !== root && !target.startsWith(`${root}${path16.sep}`))
|
|
21771
22011
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
21772
22012
|
fields: [
|
|
21773
22013
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
21774
22014
|
]
|
|
21775
22015
|
});
|
|
21776
22016
|
try {
|
|
21777
|
-
const below =
|
|
22017
|
+
const below = path16.relative(root, target);
|
|
21778
22018
|
let current = root;
|
|
21779
|
-
for (const segment of below.split(
|
|
22019
|
+
for (const segment of below.split(path16.sep)) {
|
|
21780
22020
|
if (!segment)
|
|
21781
22021
|
continue;
|
|
21782
|
-
current =
|
|
21783
|
-
const stat =
|
|
21784
|
-
const real =
|
|
21785
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
22022
|
+
current = path16.join(current, segment);
|
|
22023
|
+
const stat = fs13.lstatSync(current);
|
|
22024
|
+
const real = fs13.realpathSync(current);
|
|
22025
|
+
if (real !== root && !real.startsWith(`${root}${path16.sep}`))
|
|
21786
22026
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
21787
22027
|
fields: [
|
|
21788
22028
|
{
|
|
@@ -21798,7 +22038,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
21798
22038
|
if (current !== target && !stat.isDirectory())
|
|
21799
22039
|
return failure2("invalid_input", "document reference ancestor is not a directory");
|
|
21800
22040
|
}
|
|
21801
|
-
if (digestBytes(
|
|
22041
|
+
if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
|
|
21802
22042
|
return failure2("permission_denied", "decision document bytes have changed");
|
|
21803
22043
|
} catch {
|
|
21804
22044
|
return failure2("invalid_input", "decision document is unavailable");
|
|
@@ -22656,7 +22896,7 @@ var judgeInputSummary = (raw) => {
|
|
|
22656
22896
|
|
|
22657
22897
|
// packages/workit-core/src/autonomy.ts
|
|
22658
22898
|
import os4 from "node:os";
|
|
22659
|
-
import
|
|
22899
|
+
import path17 from "node:path";
|
|
22660
22900
|
var accountHome = () => {
|
|
22661
22901
|
try {
|
|
22662
22902
|
return os4.userInfo().homedir;
|
|
@@ -22665,10 +22905,10 @@ var accountHome = () => {
|
|
|
22665
22905
|
}
|
|
22666
22906
|
};
|
|
22667
22907
|
var grantsHome = { resolve: accountHome };
|
|
22668
|
-
var grantsDir = () =>
|
|
22908
|
+
var grantsDir = () => path17.join(grantsHome.resolve(), ".config", "workit");
|
|
22669
22909
|
var grantsOverride = () => {
|
|
22670
|
-
const configured =
|
|
22671
|
-
return configured ===
|
|
22910
|
+
const configured = path17.resolve(resolveConfigDir());
|
|
22911
|
+
return configured === path17.resolve(grantsDir()) ? null : `the config directory is redirected to ${configured} (WORKFLOW_TOOLKIT_CONFIG, WORKFLOW_TOOLKIT_CONFIG_DIR or XDG_CONFIG_HOME); grants are read only from ${path17.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
|
|
22672
22912
|
};
|
|
22673
22913
|
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
22674
22914
|
var DEFAULT_GRANTS = Object.freeze({
|
|
@@ -24440,7 +24680,7 @@ class WorkitCore {
|
|
|
24440
24680
|
// packages/workit-core/src/core/repo-context.ts
|
|
24441
24681
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
24442
24682
|
import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
|
|
24443
|
-
import
|
|
24683
|
+
import path18 from "node:path";
|
|
24444
24684
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
24445
24685
|
try {
|
|
24446
24686
|
gitRevisionParts(value);
|
|
@@ -24574,7 +24814,7 @@ var findPrTemplate = (cwd) => {
|
|
|
24574
24814
|
]) {
|
|
24575
24815
|
const parts = rel.split("/");
|
|
24576
24816
|
const base = parts[parts.length - 1];
|
|
24577
|
-
const dir = parts.length > 1 ?
|
|
24817
|
+
const dir = parts.length > 1 ? path18.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
24578
24818
|
const actual = probeCaseInsensitive(dir, base);
|
|
24579
24819
|
if (actual)
|
|
24580
24820
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -24605,7 +24845,7 @@ var diffStatForRange = (cwd, range) => {
|
|
|
24605
24845
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
24606
24846
|
};
|
|
24607
24847
|
var packageScripts = (cwd, keys) => {
|
|
24608
|
-
const pkgPath =
|
|
24848
|
+
const pkgPath = path18.join(cwd, "package.json");
|
|
24609
24849
|
if (!existsSync6(pkgPath))
|
|
24610
24850
|
return [];
|
|
24611
24851
|
try {
|
|
@@ -24631,9 +24871,9 @@ var documentationFiles = (cwd) => {
|
|
|
24631
24871
|
if (entry.isDirectory()) {
|
|
24632
24872
|
if (excluded.has(entry.name))
|
|
24633
24873
|
continue;
|
|
24634
|
-
walk(
|
|
24874
|
+
walk(path18.join(dir, entry.name), depth + 1);
|
|
24635
24875
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
24636
|
-
matches.push("./" +
|
|
24876
|
+
matches.push("./" + path18.relative(cwd, path18.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
24637
24877
|
}
|
|
24638
24878
|
}
|
|
24639
24879
|
};
|
|
@@ -24649,7 +24889,7 @@ var readTrimmed2 = (file, maxLines) => {
|
|
|
24649
24889
|
`);
|
|
24650
24890
|
};
|
|
24651
24891
|
function prReadyContext(root, range) {
|
|
24652
|
-
const cwd =
|
|
24892
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
24653
24893
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
24654
24894
|
return invalidRange(cwd);
|
|
24655
24895
|
let stdout = "";
|
|
@@ -24707,7 +24947,7 @@ ${status.stderr}` : "");
|
|
|
24707
24947
|
stdout += `template_path: ${template}
|
|
24708
24948
|
|
|
24709
24949
|
`;
|
|
24710
|
-
stdout += readTrimmed2(
|
|
24950
|
+
stdout += readTrimmed2(path18.join(cwd, template), 220);
|
|
24711
24951
|
} else {
|
|
24712
24952
|
stdout += `template_path: none
|
|
24713
24953
|
|
|
@@ -24717,7 +24957,7 @@ ${status.stderr}` : "");
|
|
|
24717
24957
|
stdout += `
|
|
24718
24958
|
`;
|
|
24719
24959
|
stdout += printSection("Recent Validation Signals");
|
|
24720
|
-
if (existsSync6(
|
|
24960
|
+
if (existsSync6(path18.join(cwd, "package.json"))) {
|
|
24721
24961
|
stdout += `package.json detected. Common scripts:
|
|
24722
24962
|
`;
|
|
24723
24963
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -24726,7 +24966,7 @@ ${status.stderr}` : "");
|
|
|
24726
24966
|
`) + `
|
|
24727
24967
|
`;
|
|
24728
24968
|
}
|
|
24729
|
-
if (existsSync6(
|
|
24969
|
+
if (existsSync6(path18.join(cwd, "Cargo.toml")) || existsSync6(path18.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
24730
24970
|
stdout += `Rust project detected.
|
|
24731
24971
|
`;
|
|
24732
24972
|
}
|
|
@@ -24759,7 +24999,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
24759
24999
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
24760
25000
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
24761
25001
|
function changelogContext(root, range) {
|
|
24762
|
-
const cwd =
|
|
25002
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
24763
25003
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
24764
25004
|
return invalidRange(cwd);
|
|
24765
25005
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -24777,7 +25017,7 @@ function changelogContext(root, range) {
|
|
|
24777
25017
|
stdout += CHANGELOG_RULES + `
|
|
24778
25018
|
`;
|
|
24779
25019
|
stdout += printSection("Existing CHANGELOG.md");
|
|
24780
|
-
const changelogPath =
|
|
25020
|
+
const changelogPath = path18.join(cwd, "CHANGELOG.md");
|
|
24781
25021
|
if (existsSync6(changelogPath)) {
|
|
24782
25022
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
24783
25023
|
`;
|
|
@@ -24797,7 +25037,7 @@ function changelogContext(root, range) {
|
|
|
24797
25037
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
24798
25038
|
}
|
|
24799
25039
|
function docsRefreshContext(root, range) {
|
|
24800
|
-
const cwd =
|
|
25040
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
24801
25041
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
24802
25042
|
return invalidRange(cwd);
|
|
24803
25043
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -24819,7 +25059,7 @@ function docsRefreshContext(root, range) {
|
|
|
24819
25059
|
`) + `
|
|
24820
25060
|
`;
|
|
24821
25061
|
stdout += printSection("README Preview");
|
|
24822
|
-
const readme =
|
|
25062
|
+
const readme = path18.join(cwd, "README.md");
|
|
24823
25063
|
if (existsSync6(readme)) {
|
|
24824
25064
|
stdout += readTrimmed2(readme, 220) + `
|
|
24825
25065
|
`;
|
|
@@ -24828,7 +25068,7 @@ function docsRefreshContext(root, range) {
|
|
|
24828
25068
|
`;
|
|
24829
25069
|
}
|
|
24830
25070
|
stdout += printSection("Package Scripts");
|
|
24831
|
-
const pkgPath =
|
|
25071
|
+
const pkgPath = path18.join(cwd, "package.json");
|
|
24832
25072
|
if (existsSync6(pkgPath)) {
|
|
24833
25073
|
try {
|
|
24834
25074
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
@@ -24842,7 +25082,7 @@ function docsRefreshContext(root, range) {
|
|
|
24842
25082
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
24843
25083
|
}
|
|
24844
25084
|
function releaseNotesContext(root, rangeOrTag) {
|
|
24845
|
-
const cwd =
|
|
25085
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
24846
25086
|
if (!rangeOrTag) {
|
|
24847
25087
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
24848
25088
|
`, exitCode: 1, cwd };
|
|
@@ -24880,7 +25120,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
24880
25120
|
`;
|
|
24881
25121
|
stdout += printSection("Existing Release Files");
|
|
24882
25122
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
24883
|
-
if (existsSync6(
|
|
25123
|
+
if (existsSync6(path18.join(cwd, rel)))
|
|
24884
25124
|
stdout += rel + `
|
|
24885
25125
|
`;
|
|
24886
25126
|
}
|
|
@@ -24911,8 +25151,8 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
24911
25151
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
24912
25152
|
|
|
24913
25153
|
// packages/workit-core/src/core/pr-create.ts
|
|
24914
|
-
import
|
|
24915
|
-
import
|
|
25154
|
+
import fs14 from "node:fs";
|
|
25155
|
+
import path19 from "node:path";
|
|
24916
25156
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
24917
25157
|
// packages/workit-core/src/forge/pr-body.ts
|
|
24918
25158
|
function parseGhIssue(value) {
|
|
@@ -24923,13 +25163,13 @@ function parseGhIssue(value) {
|
|
|
24923
25163
|
// packages/workit-core/src/core/pr-create.ts
|
|
24924
25164
|
function whichOnPath(tool) {
|
|
24925
25165
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
24926
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
25166
|
+
for (const dir of (process.env.PATH ?? "").split(path19.delimiter)) {
|
|
24927
25167
|
if (!dir)
|
|
24928
25168
|
continue;
|
|
24929
25169
|
for (const name of names) {
|
|
24930
|
-
const candidate =
|
|
25170
|
+
const candidate = path19.join(dir, name);
|
|
24931
25171
|
try {
|
|
24932
|
-
|
|
25172
|
+
fs14.accessSync(candidate, fs14.constants.X_OK);
|
|
24933
25173
|
return candidate;
|
|
24934
25174
|
} catch {}
|
|
24935
25175
|
}
|
|
@@ -25047,8 +25287,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
|
25047
25287
|
};
|
|
25048
25288
|
|
|
25049
25289
|
// packages/workit-core/src/core/youtrack.ts
|
|
25050
|
-
import
|
|
25051
|
-
import
|
|
25290
|
+
import fs15 from "node:fs";
|
|
25291
|
+
import path20 from "node:path";
|
|
25052
25292
|
|
|
25053
25293
|
// packages/workit-core/src/core/templates.ts
|
|
25054
25294
|
var repoRoot2 = assetRoot();
|
|
@@ -25061,22 +25301,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
25061
25301
|
// packages/workit-core/src/core/youtrack.ts
|
|
25062
25302
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
25063
25303
|
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
25064
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
25065
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
25304
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path20.join(configDir(), "youtrack.json");
|
|
25305
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path20.join(resolveConfigDir(), "youtrack.json");
|
|
25066
25306
|
var youTrackTokenModeOk = (p) => {
|
|
25067
25307
|
if (process.platform === "win32")
|
|
25068
25308
|
return true;
|
|
25069
|
-
const mode =
|
|
25309
|
+
const mode = fs15.statSync(p).mode & 511;
|
|
25070
25310
|
return mode === 384;
|
|
25071
25311
|
};
|
|
25072
25312
|
function readYouTrackConfig(required) {
|
|
25073
25313
|
const cfgPath = youTrackConfigPath();
|
|
25074
|
-
if (!
|
|
25314
|
+
if (!fs15.existsSync(cfgPath)) {
|
|
25075
25315
|
return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
|
|
25076
25316
|
}
|
|
25077
25317
|
let parsed;
|
|
25078
25318
|
try {
|
|
25079
|
-
parsed = JSON.parse(
|
|
25319
|
+
parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
|
|
25080
25320
|
} catch {
|
|
25081
25321
|
return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
|
|
25082
25322
|
}
|
|
@@ -25087,11 +25327,11 @@ function readYouTrackConfig(required) {
|
|
|
25087
25327
|
}
|
|
25088
25328
|
var readYouTrackContextConfig = () => {
|
|
25089
25329
|
const cfgPath = youTrackReadOnlyConfigPath();
|
|
25090
|
-
if (!
|
|
25330
|
+
if (!fs15.existsSync(cfgPath))
|
|
25091
25331
|
return { data: {} };
|
|
25092
25332
|
let parsed;
|
|
25093
25333
|
try {
|
|
25094
|
-
parsed = JSON.parse(
|
|
25334
|
+
parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
|
|
25095
25335
|
} catch {
|
|
25096
25336
|
return { error: "YouTrack configuration is unavailable" };
|
|
25097
25337
|
}
|
|
@@ -25130,12 +25370,12 @@ var youTrackToken = () => {
|
|
|
25130
25370
|
if ("error" in loaded)
|
|
25131
25371
|
return loaded;
|
|
25132
25372
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
25133
|
-
const tokenPath = tokenFile ?
|
|
25134
|
-
if (!tokenPath || !
|
|
25373
|
+
const tokenPath = tokenFile ? path20.isAbsolute(tokenFile) ? path20.resolve(tokenFile) : path20.resolve(process.cwd(), tokenFile) : "";
|
|
25374
|
+
if (!tokenPath || !fs15.existsSync(tokenPath))
|
|
25135
25375
|
return { error: "missing youtrack.token" };
|
|
25136
25376
|
if (!youTrackTokenModeOk(tokenPath))
|
|
25137
25377
|
return { error: "youtrack.token mode must be 0600" };
|
|
25138
|
-
const token =
|
|
25378
|
+
const token = fs15.readFileSync(tokenPath, "utf8").trim();
|
|
25139
25379
|
if (!token)
|
|
25140
25380
|
return { error: "empty token file" };
|
|
25141
25381
|
if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
|
|
@@ -25200,9 +25440,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
|
|
|
25200
25440
|
} catch {
|
|
25201
25441
|
continue;
|
|
25202
25442
|
}
|
|
25203
|
-
if (!
|
|
25443
|
+
if (!fs15.existsSync(full))
|
|
25204
25444
|
continue;
|
|
25205
|
-
const text =
|
|
25445
|
+
const text = fs15.readFileSync(full, "utf8");
|
|
25206
25446
|
const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
|
|
25207
25447
|
if (m)
|
|
25208
25448
|
return m[1];
|
|
@@ -25472,15 +25712,15 @@ function resolveGitRevision(root, value) {
|
|
|
25472
25712
|
}
|
|
25473
25713
|
function resolveInside(root, candidate) {
|
|
25474
25714
|
const base = realpathSync5(root);
|
|
25475
|
-
const target =
|
|
25476
|
-
if (target !== base && !target.startsWith(base +
|
|
25715
|
+
const target = path21.resolve(base, candidate);
|
|
25716
|
+
if (target !== base && !target.startsWith(base + path21.sep)) {
|
|
25477
25717
|
throw new Error("path must stay inside repository root");
|
|
25478
25718
|
}
|
|
25479
25719
|
let ancestor = target;
|
|
25480
25720
|
while (!existsSync7(ancestor))
|
|
25481
|
-
ancestor =
|
|
25721
|
+
ancestor = path21.dirname(ancestor);
|
|
25482
25722
|
const canonicalAncestor = realpathSync5(ancestor);
|
|
25483
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
25723
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path21.sep)) {
|
|
25484
25724
|
throw new Error("path must stay inside repository root");
|
|
25485
25725
|
}
|
|
25486
25726
|
return target;
|
|
@@ -25512,7 +25752,7 @@ var SUPPORT_MATRIX = {
|
|
|
25512
25752
|
|
|
25513
25753
|
// packages/workit-mcp/src/server.ts
|
|
25514
25754
|
import { existsSync as existsSync8, readFileSync as readFileSync6, realpathSync as realpathSync6 } from "node:fs";
|
|
25515
|
-
import
|
|
25755
|
+
import path22 from "node:path";
|
|
25516
25756
|
// node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
|
|
25517
25757
|
function isZ4Schema(s) {
|
|
25518
25758
|
const schema = s;
|
|
@@ -27926,13 +28166,13 @@ var rootVariants = (workspaceRoot) => {
|
|
|
27926
28166
|
if (!workspaceRoot)
|
|
27927
28167
|
return [];
|
|
27928
28168
|
const roots = new Set;
|
|
27929
|
-
for (const candidate of [workspaceRoot,
|
|
27930
|
-
if (
|
|
28169
|
+
for (const candidate of [workspaceRoot, path22.resolve(workspaceRoot)]) {
|
|
28170
|
+
if (path22.isAbsolute(candidate) && candidate !== path22.parse(candidate).root)
|
|
27931
28171
|
roots.add(candidate);
|
|
27932
28172
|
}
|
|
27933
28173
|
try {
|
|
27934
28174
|
const canonical = realpathSync6(workspaceRoot);
|
|
27935
|
-
if (canonical !==
|
|
28175
|
+
if (canonical !== path22.parse(canonical).root)
|
|
27936
28176
|
roots.add(canonical);
|
|
27937
28177
|
} catch {}
|
|
27938
28178
|
return [...roots].toSorted((left, right) => right.length - left.length);
|
|
@@ -28014,11 +28254,11 @@ var thrownResult = (tool, error, workspaceRoot) => {
|
|
|
28014
28254
|
};
|
|
28015
28255
|
function createMcpServer(host, contextProvider) {
|
|
28016
28256
|
assertMcpHost(host);
|
|
28017
|
-
const here =
|
|
28257
|
+
const here = path22.dirname(fileURLToPath2(import.meta.url));
|
|
28018
28258
|
const sourceMarker = markSourcesLoaded([
|
|
28019
|
-
|
|
28020
|
-
|
|
28021
|
-
|
|
28259
|
+
path22.join(here, "server.ts"),
|
|
28260
|
+
path22.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
|
|
28261
|
+
path22.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
|
|
28022
28262
|
].filter((file) => existsSync8(file)));
|
|
28023
28263
|
let staleWarned = false;
|
|
28024
28264
|
const server = new Server({ name: "workit", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
|
|
@@ -28401,14 +28641,14 @@ import {
|
|
|
28401
28641
|
writeFileSync as writeFileSync3
|
|
28402
28642
|
} from "node:fs";
|
|
28403
28643
|
import os5 from "node:os";
|
|
28404
|
-
import
|
|
28644
|
+
import path23 from "node:path";
|
|
28405
28645
|
var CODEX_AGENT_MARKER = "# Generated by Workit";
|
|
28406
28646
|
var codexHomeOfInstall = (pluginRoot) => {
|
|
28407
|
-
const parts =
|
|
28647
|
+
const parts = path23.resolve(pluginRoot).split(path23.sep);
|
|
28408
28648
|
const cache = parts.length - 4;
|
|
28409
|
-
return cache >= 2 && parts[cache] === "cache" && parts[cache - 1] === "plugins" ? parts.slice(0, cache - 1).join(
|
|
28649
|
+
return cache >= 2 && parts[cache] === "cache" && parts[cache - 1] === "plugins" ? parts.slice(0, cache - 1).join(path23.sep) || path23.sep : null;
|
|
28410
28650
|
};
|
|
28411
|
-
var codexHomeFor = (pluginRoot, env) => codexHomeOfInstall(pluginRoot) ?? (env.CODEX_HOME ||
|
|
28651
|
+
var codexHomeFor = (pluginRoot, env) => codexHomeOfInstall(pluginRoot) ?? (env.CODEX_HOME || path23.join(env.HOME || os5.homedir(), ".codex"));
|
|
28412
28652
|
var MARKER_VERSION = /^# Generated by Workit (\d+\.\d+\.\d+)\b/;
|
|
28413
28653
|
var agentMarkerVersion = (text) => MARKER_VERSION.exec(text)?.[1] ?? null;
|
|
28414
28654
|
var stampCodexAgent = (content, version) => version && content.startsWith(CODEX_AGENT_MARKER) && !agentMarkerVersion(content) ? `${CODEX_AGENT_MARKER} ${version}${content.slice(CODEX_AGENT_MARKER.length)}` : content;
|
|
@@ -28422,7 +28662,7 @@ var versionAtLeast = (version, min) => {
|
|
|
28422
28662
|
};
|
|
28423
28663
|
var pluginVersion = (pluginRoot) => {
|
|
28424
28664
|
try {
|
|
28425
|
-
const version = JSON.parse(readFileSync7(
|
|
28665
|
+
const version = JSON.parse(readFileSync7(path23.join(pluginRoot, "package.json"), "utf8")).version;
|
|
28426
28666
|
return typeof version === "string" && /^\d+\.\d+\.\d+/.test(version) ? version.match(/^\d+\.\d+\.\d+/)[0] : null;
|
|
28427
28667
|
} catch {
|
|
28428
28668
|
return null;
|
|
@@ -28436,15 +28676,15 @@ var lstatOrNull = (file) => {
|
|
|
28436
28676
|
}
|
|
28437
28677
|
};
|
|
28438
28678
|
var installCodexAgents = (pluginRoot, codexHome) => {
|
|
28439
|
-
const source =
|
|
28679
|
+
const source = path23.join(pluginRoot, "agents");
|
|
28440
28680
|
if (!existsSync9(source))
|
|
28441
28681
|
return [];
|
|
28442
28682
|
const version = pluginVersion(pluginRoot);
|
|
28443
|
-
const target =
|
|
28683
|
+
const target = path23.join(codexHome, "agents");
|
|
28444
28684
|
const results = [];
|
|
28445
28685
|
for (const file of readdirSync5(source).filter((name) => name.endsWith(".toml")).toSorted()) {
|
|
28446
|
-
const content = stampCodexAgent(readFileSync7(
|
|
28447
|
-
const dest =
|
|
28686
|
+
const content = stampCodexAgent(readFileSync7(path23.join(source, file), "utf8"), version);
|
|
28687
|
+
const dest = path23.join(target, file);
|
|
28448
28688
|
const stat = lstatOrNull(dest);
|
|
28449
28689
|
if (stat?.isSymbolicLink()) {
|
|
28450
28690
|
results.push({ file: dest, action: "skipped-symlink" });
|
|
@@ -28480,7 +28720,7 @@ var installCodexAgents = (pluginRoot, codexHome) => {
|
|
|
28480
28720
|
results.push({ file: dest, action: "kept-newer-version" });
|
|
28481
28721
|
continue;
|
|
28482
28722
|
}
|
|
28483
|
-
const temp =
|
|
28723
|
+
const temp = path23.join(target, `.${file}.${process.pid}.${Date.now()}.tmp`);
|
|
28484
28724
|
writeFileSync3(temp, content, { flag: "wx" });
|
|
28485
28725
|
try {
|
|
28486
28726
|
renameSync2(temp, dest);
|
|
@@ -28502,8 +28742,8 @@ var codexQualification = (host) => host === "codex_cli" ? { surface: host, cli:
|
|
|
28502
28742
|
var resolveCodexWorkspaceRoot = (pluginRoot, env) => {
|
|
28503
28743
|
const explicit = env.WORKFLOW_WORKSPACE_ROOT;
|
|
28504
28744
|
const inherited = env.PWD;
|
|
28505
|
-
const candidate = explicit ?? (inherited &&
|
|
28506
|
-
if (!candidate || !
|
|
28745
|
+
const candidate = explicit ?? (inherited && path24.resolve(inherited) !== path24.resolve(pluginRoot) ? inherited : undefined);
|
|
28746
|
+
if (!candidate || !path24.isAbsolute(candidate) || !existsSync10(candidate))
|
|
28507
28747
|
return null;
|
|
28508
28748
|
try {
|
|
28509
28749
|
if (!statSync3(candidate).isDirectory())
|
|
@@ -28542,7 +28782,7 @@ var codexContextProvider = (host = detectCodexSurface(process.env), root) => ({
|
|
|
28542
28782
|
};
|
|
28543
28783
|
}
|
|
28544
28784
|
});
|
|
28545
|
-
var pluginRoot = () =>
|
|
28785
|
+
var pluginRoot = () => path24.resolve(path24.dirname(fileURLToPath3(import.meta.url)), "..");
|
|
28546
28786
|
var syncCodexAgents = (root) => {
|
|
28547
28787
|
const codexHome = codexHomeOfInstall(root);
|
|
28548
28788
|
if (!codexHome)
|