@brainervirus/workit-cursor 8.13.0 → 8.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor-plugin/plugin.json +1 -1
- package/dist/cursor-session-start.js +2310 -1722
- package/dist/mcp-server.js +639 -369
- package/dist/workit-hook.js +1587 -963
- package/package.json +4 -4
- package/skills/workit-fanout/SKILL.md +4 -4
- package/skills/workit-fanout/references/orchestration.md +8 -5
- package/skills/workit-review/SKILL.md +1 -1
package/dist/mcp-server.js
CHANGED
|
@@ -6622,15 +6622,15 @@ var require_dist = __commonJS(function(exports, module) {
|
|
|
6622
6622
|
|
|
6623
6623
|
// packages/workit-cursor/mcp/run-server.ts
|
|
6624
6624
|
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
6625
|
-
import
|
|
6625
|
+
import path24 from "node:path";
|
|
6626
6626
|
|
|
6627
6627
|
// packages/workit-mcp/src/index.ts
|
|
6628
6628
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
6629
|
-
import
|
|
6629
|
+
import path23 from "node:path";
|
|
6630
6630
|
|
|
6631
6631
|
// packages/workit-mcp/src/server.ts
|
|
6632
6632
|
import { existsSync as existsSync8, readFileSync as readFileSync6, realpathSync as realpathSync6 } from "node:fs";
|
|
6633
|
-
import
|
|
6633
|
+
import path22 from "node:path";
|
|
6634
6634
|
|
|
6635
6635
|
// node_modules/zod/v4/core/util.js
|
|
6636
6636
|
function getEnumValues(entries) {
|
|
@@ -16582,7 +16582,7 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
|
16582
16582
|
// packages/workit-core/src/core.ts
|
|
16583
16583
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
16584
16584
|
import { existsSync as existsSync7, realpathSync as realpathSync5 } from "node:fs";
|
|
16585
|
-
import
|
|
16585
|
+
import path21 from "node:path";
|
|
16586
16586
|
|
|
16587
16587
|
// packages/workit-core/src/core/task-contract.ts
|
|
16588
16588
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -17837,9 +17837,9 @@ function parseAdvertisedOperation(family, input, host) {
|
|
|
17837
17837
|
});
|
|
17838
17838
|
}
|
|
17839
17839
|
// packages/workit-core/src/core/task-store.ts
|
|
17840
|
-
import { createHash as
|
|
17841
|
-
import * as
|
|
17842
|
-
import
|
|
17840
|
+
import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
|
|
17841
|
+
import * as fs8 from "node:fs";
|
|
17842
|
+
import path8 from "node:path";
|
|
17843
17843
|
|
|
17844
17844
|
// node_modules/@openclaw/fs-safe/dist/errors.js
|
|
17845
17845
|
var OPERATIONAL_CODES = new Set([
|
|
@@ -18442,6 +18442,11 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
18442
18442
|
}
|
|
18443
18443
|
}
|
|
18444
18444
|
}
|
|
18445
|
+
// packages/workit-core/src/host-session.ts
|
|
18446
|
+
import { createHash as createHash3, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
|
|
18447
|
+
import fs5 from "node:fs";
|
|
18448
|
+
import path5 from "node:path";
|
|
18449
|
+
|
|
18445
18450
|
// packages/workit-core/src/core/logger.ts
|
|
18446
18451
|
import os from "node:os";
|
|
18447
18452
|
import path3 from "node:path";
|
|
@@ -18824,30 +18829,113 @@ function hostSessionFromEnv(env) {
|
|
|
18824
18829
|
}
|
|
18825
18830
|
return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
|
|
18826
18831
|
}
|
|
18832
|
+
var hash = (value) => createHash3("sha256").update(value).digest("hex").slice(0, 32);
|
|
18833
|
+
var userIssuedRoot = () => path5.join(resolveStateDir(), "issued");
|
|
18834
|
+
var userTokenRoot = () => path5.join(resolveStateDir(), "issued-tokens");
|
|
18835
|
+
function migrateRepoIssued(cwd) {
|
|
18836
|
+
if (cwd === undefined)
|
|
18837
|
+
return;
|
|
18838
|
+
let old;
|
|
18839
|
+
try {
|
|
18840
|
+
const location = resolveStore(cwd);
|
|
18841
|
+
if (location instanceof Error)
|
|
18842
|
+
return;
|
|
18843
|
+
old = path5.join(location.dir, "hooks", "issued");
|
|
18844
|
+
if (!fs5.existsSync(old))
|
|
18845
|
+
return;
|
|
18846
|
+
} catch {
|
|
18847
|
+
return;
|
|
18848
|
+
}
|
|
18849
|
+
for (const lead of safeList(old)) {
|
|
18850
|
+
try {
|
|
18851
|
+
const from = path5.join(old, lead);
|
|
18852
|
+
if (!fs5.statSync(from).isDirectory())
|
|
18853
|
+
continue;
|
|
18854
|
+
const to = path5.join(userIssuedRoot(), lead);
|
|
18855
|
+
fs5.mkdirSync(to, { recursive: true, mode: 448 });
|
|
18856
|
+
for (const id of fs5.readdirSync(from)) {
|
|
18857
|
+
try {
|
|
18858
|
+
fs5.copyFileSync(path5.join(from, id), path5.join(to, id), fs5.constants.COPYFILE_EXCL);
|
|
18859
|
+
} catch (error) {
|
|
18860
|
+
if (error.code !== "EEXIST")
|
|
18861
|
+
throw error;
|
|
18862
|
+
}
|
|
18863
|
+
fs5.rmSync(path5.join(from, id), { force: true });
|
|
18864
|
+
}
|
|
18865
|
+
removeIfEmpty(from);
|
|
18866
|
+
} catch {}
|
|
18867
|
+
}
|
|
18868
|
+
removeIfEmpty(old);
|
|
18869
|
+
}
|
|
18870
|
+
var safeList = (dir) => {
|
|
18871
|
+
try {
|
|
18872
|
+
return fs5.readdirSync(dir);
|
|
18873
|
+
} catch {
|
|
18874
|
+
return [];
|
|
18875
|
+
}
|
|
18876
|
+
};
|
|
18877
|
+
var removeIfEmpty = (dir) => {
|
|
18878
|
+
try {
|
|
18879
|
+
fs5.rmdirSync(dir);
|
|
18880
|
+
} catch {}
|
|
18881
|
+
};
|
|
18882
|
+
var issuedRecord = (cwd, claimed) => {
|
|
18883
|
+
const cut = claimed.lastIndexOf(":");
|
|
18884
|
+
if (cut <= 0)
|
|
18885
|
+
return null;
|
|
18886
|
+
migrateRepoIssued(cwd);
|
|
18887
|
+
try {
|
|
18888
|
+
return fs5.readFileSync(path5.join(userIssuedRoot(), hash(claimed.slice(0, cut)), hash(claimed)), "utf8");
|
|
18889
|
+
} catch {
|
|
18890
|
+
return null;
|
|
18891
|
+
}
|
|
18892
|
+
};
|
|
18893
|
+
var TOKEN_HASH = /^([0-9a-f]{64})$/mu;
|
|
18894
|
+
var issuedToken = (claimed) => {
|
|
18895
|
+
const cut = claimed.lastIndexOf(":");
|
|
18896
|
+
if (cut <= 0)
|
|
18897
|
+
return { stored: null, leadBound: false };
|
|
18898
|
+
const dir = path5.join(userTokenRoot(), hash(claimed.slice(0, cut)));
|
|
18899
|
+
try {
|
|
18900
|
+
const text = fs5.readFileSync(path5.join(dir, hash(claimed)), "utf8");
|
|
18901
|
+
return { stored: TOKEN_HASH.exec(text)?.[1] ?? null, leadBound: true };
|
|
18902
|
+
} catch {
|
|
18903
|
+
return { stored: null, leadBound: safeList(dir).length > 0 };
|
|
18904
|
+
}
|
|
18905
|
+
};
|
|
18906
|
+
function issuedBinding(cwd, claimed) {
|
|
18907
|
+
const record = issuedRecord(cwd, claimed);
|
|
18908
|
+
const { stored, leadBound } = issuedToken(claimed);
|
|
18909
|
+
if (stored)
|
|
18910
|
+
return "token";
|
|
18911
|
+
if (record === null)
|
|
18912
|
+
return null;
|
|
18913
|
+
return leadBound ? "token" : "legacy";
|
|
18914
|
+
}
|
|
18827
18915
|
var OWN_CHILD_SHELL = new Set(["opencode", "pi"]);
|
|
18828
18916
|
|
|
18829
18917
|
// packages/workit-core/src/core/package-root.ts
|
|
18830
18918
|
import { existsSync } from "node:fs";
|
|
18831
|
-
import
|
|
18919
|
+
import path6 from "node:path";
|
|
18832
18920
|
import { fileURLToPath } from "node:url";
|
|
18833
18921
|
var packageRoot = () => {
|
|
18834
|
-
let dir =
|
|
18922
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
18835
18923
|
while (true) {
|
|
18836
|
-
const parent =
|
|
18837
|
-
if (existsSync(
|
|
18924
|
+
const parent = path6.dirname(dir);
|
|
18925
|
+
if (existsSync(path6.join(dir, "package.json")) || parent === dir)
|
|
18838
18926
|
return dir;
|
|
18839
18927
|
dir = parent;
|
|
18840
18928
|
}
|
|
18841
18929
|
};
|
|
18842
18930
|
var assetRoot = () => {
|
|
18843
18931
|
const root = packageRoot();
|
|
18844
|
-
const assets =
|
|
18932
|
+
const assets = path6.join(root, "assets");
|
|
18845
18933
|
return existsSync(assets) ? assets : root;
|
|
18846
18934
|
};
|
|
18847
18935
|
|
|
18848
18936
|
// packages/workit-core/src/core/store-lock.ts
|
|
18849
18937
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
18850
|
-
import
|
|
18938
|
+
import fs6 from "node:fs";
|
|
18851
18939
|
import { hostname } from "node:os";
|
|
18852
18940
|
var metadataLockSchema = object2({
|
|
18853
18941
|
pid: number2().int().nonnegative().safe(),
|
|
@@ -18859,8 +18947,10 @@ var metadataLockSchema = object2({
|
|
|
18859
18947
|
var FOREIGN_LOCK_TTL_MS = 10 * 60000;
|
|
18860
18948
|
var UNREADABLE_LOCK_TTL_MS = 30000;
|
|
18861
18949
|
var RECLAIM_GUARD_TTL_MS = 30000;
|
|
18862
|
-
var
|
|
18950
|
+
var IN_PROCESS_LOCK_TIMEOUT_MS = 250;
|
|
18951
|
+
var defaultLockTimeoutMs = IN_PROCESS_LOCK_TIMEOUT_MS;
|
|
18863
18952
|
var defaultLockTimeout = () => defaultLockTimeoutMs;
|
|
18953
|
+
var hostAwareLockTimeout = (cliMs) => defaultLockTimeoutMs > IN_PROCESS_LOCK_TIMEOUT_MS ? cliMs : IN_PROCESS_LOCK_TIMEOUT_MS;
|
|
18864
18954
|
var parseMetadataLock = (raw) => {
|
|
18865
18955
|
let value;
|
|
18866
18956
|
try {
|
|
@@ -18889,7 +18979,7 @@ var parseProcStatStart = (stat) => {
|
|
|
18889
18979
|
var processStartOf = (pid) => {
|
|
18890
18980
|
if (process.platform === "linux") {
|
|
18891
18981
|
try {
|
|
18892
|
-
return parseProcStatStart(
|
|
18982
|
+
return parseProcStatStart(fs6.readFileSync(`/proc/${pid}/stat`, "utf8"));
|
|
18893
18983
|
} catch {
|
|
18894
18984
|
return null;
|
|
18895
18985
|
}
|
|
@@ -18910,7 +19000,7 @@ var processStartOf = (pid) => {
|
|
|
18910
19000
|
};
|
|
18911
19001
|
var readTrimmed = (file) => {
|
|
18912
19002
|
try {
|
|
18913
|
-
return
|
|
19003
|
+
return fs6.readFileSync(file, "utf8").trim() || null;
|
|
18914
19004
|
} catch {
|
|
18915
19005
|
return null;
|
|
18916
19006
|
}
|
|
@@ -18921,7 +19011,7 @@ var localLockHost = () => {
|
|
|
18921
19011
|
return cachedLockHost;
|
|
18922
19012
|
let pidns = null;
|
|
18923
19013
|
try {
|
|
18924
|
-
pidns = /\[(\d+)\]/.exec(
|
|
19014
|
+
pidns = /\[(\d+)\]/.exec(fs6.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
|
|
18925
19015
|
} catch {}
|
|
18926
19016
|
const boot = readTrimmed("/proc/sys/kernel/random/boot_id");
|
|
18927
19017
|
cachedLockHost = pidns || boot ? `${hostname()}#${pidns ?? "?"}:${boot ?? "?"}` : hostname();
|
|
@@ -18967,7 +19057,7 @@ var classifyLockOwner = (payload, ageMs, localHost = localLockHost(), options =
|
|
|
18967
19057
|
};
|
|
18968
19058
|
var ageOf = (file, nowMs) => {
|
|
18969
19059
|
try {
|
|
18970
|
-
return nowMs -
|
|
19060
|
+
return nowMs - fs6.lstatSync(file).mtimeMs;
|
|
18971
19061
|
} catch {
|
|
18972
19062
|
return null;
|
|
18973
19063
|
}
|
|
@@ -18978,7 +19068,7 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
|
|
|
18978
19068
|
if (age === null || age <= RECLAIM_GUARD_TTL_MS)
|
|
18979
19069
|
return false;
|
|
18980
19070
|
try {
|
|
18981
|
-
|
|
19071
|
+
fs6.rmdirSync(guard);
|
|
18982
19072
|
return true;
|
|
18983
19073
|
} catch {
|
|
18984
19074
|
return false;
|
|
@@ -18987,8 +19077,8 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
|
|
|
18987
19077
|
|
|
18988
19078
|
// packages/workit-core/src/store/event-log.ts
|
|
18989
19079
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
18990
|
-
import
|
|
18991
|
-
import
|
|
19080
|
+
import fs7 from "node:fs";
|
|
19081
|
+
import path7 from "node:path";
|
|
18992
19082
|
var EVENT_VERSION = 1;
|
|
18993
19083
|
|
|
18994
19084
|
class LogDamage extends Error {
|
|
@@ -19017,7 +19107,7 @@ var parseEvent = (line, file, offset) => {
|
|
|
19017
19107
|
function readLog(file, from = 0, expectSeq = null) {
|
|
19018
19108
|
let fd;
|
|
19019
19109
|
try {
|
|
19020
|
-
fd =
|
|
19110
|
+
fd = fs7.openSync(file, "r");
|
|
19021
19111
|
} catch (error) {
|
|
19022
19112
|
if (error.code === "ENOENT")
|
|
19023
19113
|
return null;
|
|
@@ -19026,13 +19116,13 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
19026
19116
|
let bytes;
|
|
19027
19117
|
let size;
|
|
19028
19118
|
try {
|
|
19029
|
-
size =
|
|
19119
|
+
size = fs7.fstatSync(fd).size;
|
|
19030
19120
|
if (from > size)
|
|
19031
19121
|
throw new LogDamage(`${file}: shorter than its snapshot`);
|
|
19032
19122
|
bytes = Buffer.alloc(size - from);
|
|
19033
19123
|
let read = 0;
|
|
19034
19124
|
while (read < bytes.length) {
|
|
19035
|
-
const got =
|
|
19125
|
+
const got = fs7.readSync(fd, bytes, read, bytes.length - read, from + read);
|
|
19036
19126
|
if (got === 0)
|
|
19037
19127
|
break;
|
|
19038
19128
|
read += got;
|
|
@@ -19040,7 +19130,7 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
19040
19130
|
bytes = bytes.subarray(0, read);
|
|
19041
19131
|
size = from + read;
|
|
19042
19132
|
} finally {
|
|
19043
|
-
|
|
19133
|
+
fs7.closeSync(fd);
|
|
19044
19134
|
}
|
|
19045
19135
|
const events = [];
|
|
19046
19136
|
let start = 0;
|
|
@@ -19065,11 +19155,11 @@ function readLog(file, from = 0, expectSeq = null) {
|
|
|
19065
19155
|
}
|
|
19066
19156
|
var fsyncDirectory = (dir) => {
|
|
19067
19157
|
try {
|
|
19068
|
-
const fd =
|
|
19158
|
+
const fd = fs7.openSync(dir, "r");
|
|
19069
19159
|
try {
|
|
19070
|
-
|
|
19160
|
+
fs7.fsyncSync(fd);
|
|
19071
19161
|
} finally {
|
|
19072
|
-
|
|
19162
|
+
fs7.closeSync(fd);
|
|
19073
19163
|
}
|
|
19074
19164
|
} catch (error) {
|
|
19075
19165
|
if (process.platform !== "win32")
|
|
@@ -19079,46 +19169,46 @@ var fsyncDirectory = (dir) => {
|
|
|
19079
19169
|
function appendEvent(file, event, validEnd) {
|
|
19080
19170
|
const line = Buffer.from(`${JSON.stringify(event)}
|
|
19081
19171
|
`, "utf8");
|
|
19082
|
-
const created = !
|
|
19083
|
-
if (!created &&
|
|
19084
|
-
const cut =
|
|
19172
|
+
const created = !fs7.existsSync(file);
|
|
19173
|
+
if (!created && fs7.statSync(file).size !== validEnd) {
|
|
19174
|
+
const cut = fs7.openSync(file, "r+");
|
|
19085
19175
|
try {
|
|
19086
|
-
|
|
19087
|
-
|
|
19176
|
+
fs7.ftruncateSync(cut, validEnd);
|
|
19177
|
+
fs7.fsyncSync(cut);
|
|
19088
19178
|
} finally {
|
|
19089
|
-
|
|
19179
|
+
fs7.closeSync(cut);
|
|
19090
19180
|
}
|
|
19091
19181
|
}
|
|
19092
|
-
const fd =
|
|
19182
|
+
const fd = fs7.openSync(file, "a", 384);
|
|
19093
19183
|
try {
|
|
19094
19184
|
let written = 0;
|
|
19095
19185
|
while (written < line.length)
|
|
19096
|
-
written +=
|
|
19097
|
-
|
|
19186
|
+
written += fs7.writeSync(fd, line, written, line.length - written);
|
|
19187
|
+
fs7.fsyncSync(fd);
|
|
19098
19188
|
} finally {
|
|
19099
|
-
|
|
19189
|
+
fs7.closeSync(fd);
|
|
19100
19190
|
}
|
|
19101
19191
|
if (created)
|
|
19102
|
-
fsyncDirectory(
|
|
19192
|
+
fsyncDirectory(path7.dirname(file));
|
|
19103
19193
|
return validEnd;
|
|
19104
19194
|
}
|
|
19105
19195
|
function replaceFile(file, bytes, durable) {
|
|
19106
19196
|
const temporary = `${file}.${process.pid}.${randomUUID2()}.tmp`;
|
|
19107
19197
|
try {
|
|
19108
|
-
const fd =
|
|
19198
|
+
const fd = fs7.openSync(temporary, "wx", 384);
|
|
19109
19199
|
try {
|
|
19110
|
-
|
|
19200
|
+
fs7.writeSync(fd, bytes);
|
|
19111
19201
|
if (durable)
|
|
19112
|
-
|
|
19202
|
+
fs7.fsyncSync(fd);
|
|
19113
19203
|
} finally {
|
|
19114
|
-
|
|
19204
|
+
fs7.closeSync(fd);
|
|
19115
19205
|
}
|
|
19116
19206
|
renameWithRetry(temporary, file);
|
|
19117
19207
|
if (durable)
|
|
19118
|
-
fsyncDirectory(
|
|
19208
|
+
fsyncDirectory(path7.dirname(file));
|
|
19119
19209
|
} catch (error) {
|
|
19120
19210
|
try {
|
|
19121
|
-
|
|
19211
|
+
fs7.unlinkSync(temporary);
|
|
19122
19212
|
} catch {}
|
|
19123
19213
|
throw error;
|
|
19124
19214
|
}
|
|
@@ -19143,7 +19233,7 @@ var retryTransient = (run) => {
|
|
|
19143
19233
|
}
|
|
19144
19234
|
}
|
|
19145
19235
|
};
|
|
19146
|
-
var renameWithRetry = (from, to) => retryTransient(() =>
|
|
19236
|
+
var renameWithRetry = (from, to) => retryTransient(() => fs7.renameSync(from, to));
|
|
19147
19237
|
|
|
19148
19238
|
// packages/workit-core/src/store/patch.ts
|
|
19149
19239
|
var isPlainObject3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -19256,7 +19346,7 @@ var isPatch = (value) => Array.isArray(value) && value.every((op) => {
|
|
|
19256
19346
|
});
|
|
19257
19347
|
|
|
19258
19348
|
// packages/workit-core/src/store/reduce.ts
|
|
19259
|
-
import { createHash as
|
|
19349
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
19260
19350
|
var OPENING_TYPES = new Set([
|
|
19261
19351
|
"task.opened",
|
|
19262
19352
|
"task.imported",
|
|
@@ -19285,7 +19375,7 @@ var isObject4 = (value) => typeof value === "object" && value !== null && !Array
|
|
|
19285
19375
|
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");
|
|
19286
19376
|
var BLOB_REF = "$blob";
|
|
19287
19377
|
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;
|
|
19288
|
-
var blobDigest = (value) =>
|
|
19378
|
+
var blobDigest = (value) => createHash4("sha256").update(JSON.stringify(value)).digest("hex");
|
|
19289
19379
|
var toBlob = (value, blobs) => {
|
|
19290
19380
|
if (!isObject4(value))
|
|
19291
19381
|
return value;
|
|
@@ -19440,7 +19530,7 @@ var indexEntry = (task, key, legacy, file) => {
|
|
|
19440
19530
|
};
|
|
19441
19531
|
var fileSignature = (file) => {
|
|
19442
19532
|
try {
|
|
19443
|
-
const stat =
|
|
19533
|
+
const stat = fs8.statSync(file, { bigint: true });
|
|
19444
19534
|
return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
|
|
19445
19535
|
} catch {
|
|
19446
19536
|
return null;
|
|
@@ -19458,7 +19548,7 @@ var cachedRuntimeVersion = null;
|
|
|
19458
19548
|
var runtimeVersion = () => {
|
|
19459
19549
|
if (cachedRuntimeVersion === null) {
|
|
19460
19550
|
try {
|
|
19461
|
-
const pkg = JSON.parse(
|
|
19551
|
+
const pkg = JSON.parse(fs8.readFileSync(path8.join(packageRoot(), "package.json"), "utf8"));
|
|
19462
19552
|
cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
|
|
19463
19553
|
} catch {
|
|
19464
19554
|
cachedRuntimeVersion = "0.0.0";
|
|
@@ -19484,14 +19574,14 @@ var isNewerVersion = (candidate, current) => {
|
|
|
19484
19574
|
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);
|
|
19485
19575
|
var readText = (file) => {
|
|
19486
19576
|
try {
|
|
19487
|
-
return
|
|
19577
|
+
return fs8.readFileSync(file, "utf8");
|
|
19488
19578
|
} catch {
|
|
19489
19579
|
return "";
|
|
19490
19580
|
}
|
|
19491
19581
|
};
|
|
19492
19582
|
var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
|
|
19493
19583
|
var branchStart = (location, branch, aliases) => {
|
|
19494
|
-
const first = readText(
|
|
19584
|
+
const first = readText(path8.join(path8.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
|
|
19495
19585
|
`, 1)[0];
|
|
19496
19586
|
const created = REFLOG_LINE.exec(first);
|
|
19497
19587
|
const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
|
|
@@ -19501,7 +19591,7 @@ var branchStart = (location, branch, aliases) => {
|
|
|
19501
19591
|
return start;
|
|
19502
19592
|
if (!location.git)
|
|
19503
19593
|
return null;
|
|
19504
|
-
for (const line of readText(
|
|
19594
|
+
for (const line of readText(path8.join(location.git.gitDir, "logs", "HEAD")).split(`
|
|
19505
19595
|
`)) {
|
|
19506
19596
|
const entry = REFLOG_LINE.exec(line);
|
|
19507
19597
|
const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
|
|
@@ -19511,23 +19601,23 @@ var branchStart = (location, branch, aliases) => {
|
|
|
19511
19601
|
}
|
|
19512
19602
|
return null;
|
|
19513
19603
|
};
|
|
19514
|
-
var refExists = (common, ref) =>
|
|
19604
|
+
var refExists = (common, ref) => fs8.existsSync(path8.join(common, ref)) || readText(path8.join(common, "packed-refs")).includes(` ${ref}
|
|
19515
19605
|
`);
|
|
19516
19606
|
var origins = new Map;
|
|
19517
19607
|
var sameDirectoryIdentity = (left, right) => {
|
|
19518
|
-
if (!
|
|
19608
|
+
if (!path8.isAbsolute(left) || !path8.isAbsolute(right))
|
|
19519
19609
|
return false;
|
|
19520
|
-
const normalizedLeft =
|
|
19521
|
-
const normalizedRight =
|
|
19610
|
+
const normalizedLeft = path8.resolve(left);
|
|
19611
|
+
const normalizedRight = path8.resolve(right);
|
|
19522
19612
|
try {
|
|
19523
|
-
const a =
|
|
19524
|
-
const b =
|
|
19613
|
+
const a = fs8.statSync(normalizedLeft, { bigint: true });
|
|
19614
|
+
const b = fs8.statSync(normalizedRight, { bigint: true });
|
|
19525
19615
|
if (!a.isDirectory() || !b.isDirectory())
|
|
19526
19616
|
return false;
|
|
19527
19617
|
if (process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight)
|
|
19528
19618
|
return true;
|
|
19529
|
-
const canonicalLeft =
|
|
19530
|
-
const canonicalRight =
|
|
19619
|
+
const canonicalLeft = fs8.realpathSync(normalizedLeft);
|
|
19620
|
+
const canonicalRight = fs8.realpathSync(normalizedRight);
|
|
19531
19621
|
if (process.platform === "win32" ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase() : canonicalLeft === canonicalRight)
|
|
19532
19622
|
return true;
|
|
19533
19623
|
return a.dev === b.dev && a.ino !== 0n && a.ino === b.ino;
|
|
@@ -19564,8 +19654,8 @@ class TaskStore {
|
|
|
19564
19654
|
migrationChecked = false;
|
|
19565
19655
|
compactDue = new Set;
|
|
19566
19656
|
constructor(root, options = {}) {
|
|
19567
|
-
const resolved =
|
|
19568
|
-
this.origin =
|
|
19657
|
+
const resolved = path8.resolve(root);
|
|
19658
|
+
this.origin = fs8.existsSync(resolved) ? fs8.realpathSync(resolved) : resolved;
|
|
19569
19659
|
this.located = resolveStore(this.origin);
|
|
19570
19660
|
this.root = checkoutRootOf(this.origin, this.located);
|
|
19571
19661
|
this.lockTimeoutMs = options.lockTimeoutMs ?? defaultLockTimeout();
|
|
@@ -19573,7 +19663,7 @@ class TaskStore {
|
|
|
19573
19663
|
this.migrateOnRead = options.migrateOnRead;
|
|
19574
19664
|
}
|
|
19575
19665
|
location() {
|
|
19576
|
-
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared &&
|
|
19666
|
+
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs8.existsSync(path8.join(this.root, ".git"))) {
|
|
19577
19667
|
this.located = undefined;
|
|
19578
19668
|
this.migrationChecked = false;
|
|
19579
19669
|
}
|
|
@@ -19637,7 +19727,7 @@ class TaskStore {
|
|
|
19637
19727
|
const tasksDir = this.paths().tasks;
|
|
19638
19728
|
let names;
|
|
19639
19729
|
try {
|
|
19640
|
-
names =
|
|
19730
|
+
names = fs8.existsSync(tasksDir) ? fs8.readdirSync(tasksDir).filter((name) => validId(name)) : [];
|
|
19641
19731
|
} catch (error) {
|
|
19642
19732
|
return failure2("storage_error", `unable to list tasks: ${String(error)}`, {
|
|
19643
19733
|
path: tasksDir
|
|
@@ -19678,7 +19768,7 @@ class TaskStore {
|
|
|
19678
19768
|
const file = this.paths().workspace;
|
|
19679
19769
|
let bytes;
|
|
19680
19770
|
try {
|
|
19681
|
-
bytes = retryTransient(() =>
|
|
19771
|
+
bytes = retryTransient(() => fs8.readFileSync(file, "utf8"));
|
|
19682
19772
|
} catch (error) {
|
|
19683
19773
|
if (error.code === "ENOENT")
|
|
19684
19774
|
return success(null, null, null);
|
|
@@ -19729,7 +19819,7 @@ class TaskStore {
|
|
|
19729
19819
|
const moved = this.bindLocked(again.data.id, key, input.now);
|
|
19730
19820
|
return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
|
|
19731
19821
|
}
|
|
19732
|
-
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" :
|
|
19822
|
+
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path8.basename(this.root) || this.root;
|
|
19733
19823
|
const intent = {
|
|
19734
19824
|
objective: input.objective?.trim() || `Work on ${label}`,
|
|
19735
19825
|
scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
|
|
@@ -19789,14 +19879,14 @@ class TaskStore {
|
|
|
19789
19879
|
const branch = key.ok ? key.data.branch : null;
|
|
19790
19880
|
if (!branch || !location.ok)
|
|
19791
19881
|
return success(null, null, null);
|
|
19792
|
-
const common =
|
|
19793
|
-
const heads =
|
|
19794
|
-
const headLog = location.data.git ?
|
|
19882
|
+
const common = path8.dirname(location.data.dir);
|
|
19883
|
+
const heads = path8.join(common, "logs", "refs", "heads");
|
|
19884
|
+
const headLog = location.data.git ? path8.join(location.data.git.gitDir, "logs", "HEAD") : "";
|
|
19795
19885
|
const signatures = [
|
|
19796
|
-
|
|
19886
|
+
path8.join(heads, branch),
|
|
19797
19887
|
headLog,
|
|
19798
19888
|
heads,
|
|
19799
|
-
|
|
19889
|
+
path8.join(common, "packed-refs")
|
|
19800
19890
|
].map((file) => fileSignature(file) ?? "-");
|
|
19801
19891
|
const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
|
|
19802
19892
|
let resolved = origins.get(slot);
|
|
@@ -19808,7 +19898,7 @@ class TaskStore {
|
|
|
19808
19898
|
aliases.push(rename.from);
|
|
19809
19899
|
const start = branchStart(location.data, branch, aliases);
|
|
19810
19900
|
const slash = start?.indexOf("/") ?? -1;
|
|
19811
|
-
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(
|
|
19901
|
+
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)}"]`));
|
|
19812
19902
|
const name = start && (remote ? start.slice(slash + 1) : start);
|
|
19813
19903
|
resolved = {
|
|
19814
19904
|
signature: signatures.join("|"),
|
|
@@ -20046,13 +20136,13 @@ class TaskStore {
|
|
|
20046
20136
|
if (!ready.ok)
|
|
20047
20137
|
return ready;
|
|
20048
20138
|
const dirs = this.paths();
|
|
20049
|
-
const recovery =
|
|
20050
|
-
if (
|
|
20139
|
+
const recovery = path8.join(this.root, ".workit", "recovery");
|
|
20140
|
+
if (fs8.existsSync(recovery)) {
|
|
20051
20141
|
let files = 0;
|
|
20052
20142
|
let bytes = 0;
|
|
20053
20143
|
try {
|
|
20054
|
-
for (const name of
|
|
20055
|
-
const stat =
|
|
20144
|
+
for (const name of fs8.readdirSync(recovery)) {
|
|
20145
|
+
const stat = fs8.lstatSync(path8.join(recovery, name));
|
|
20056
20146
|
if (!stat.isFile())
|
|
20057
20147
|
continue;
|
|
20058
20148
|
files += 1;
|
|
@@ -20061,17 +20151,17 @@ class TaskStore {
|
|
|
20061
20151
|
} catch {}
|
|
20062
20152
|
const remove = options.pruneRecovery === true && !dryRun;
|
|
20063
20153
|
if (remove)
|
|
20064
|
-
|
|
20154
|
+
fs8.rmSync(recovery, { recursive: true, force: true });
|
|
20065
20155
|
report.legacyRecovery = { path: recovery, files, bytes, removed: remove };
|
|
20066
20156
|
}
|
|
20067
|
-
if (!
|
|
20157
|
+
if (!fs8.existsSync(dirs.tasks))
|
|
20068
20158
|
return success(null, null, report);
|
|
20069
|
-
const names =
|
|
20159
|
+
const names = fs8.readdirSync(dirs.tasks).filter((name) => validId(name));
|
|
20070
20160
|
for (const name of names) {
|
|
20071
20161
|
const file = this.eventsPath(name);
|
|
20072
20162
|
let size = 0;
|
|
20073
20163
|
try {
|
|
20074
|
-
size =
|
|
20164
|
+
size = fs8.statSync(file).size;
|
|
20075
20165
|
} catch {
|
|
20076
20166
|
continue;
|
|
20077
20167
|
}
|
|
@@ -20099,16 +20189,16 @@ class TaskStore {
|
|
|
20099
20189
|
const references = new Set;
|
|
20100
20190
|
for (const name of names)
|
|
20101
20191
|
try {
|
|
20102
|
-
for (const match of
|
|
20192
|
+
for (const match of fs8.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
|
|
20103
20193
|
references.add(match[1]);
|
|
20104
20194
|
} catch {}
|
|
20105
|
-
if (
|
|
20106
|
-
for (const name of
|
|
20195
|
+
if (fs8.existsSync(dirs.blobs))
|
|
20196
|
+
for (const name of fs8.readdirSync(dirs.blobs)) {
|
|
20107
20197
|
const digest = name.replace(/\.json$/, "");
|
|
20108
|
-
const file =
|
|
20198
|
+
const file = path8.join(dirs.blobs, name);
|
|
20109
20199
|
let stat;
|
|
20110
20200
|
try {
|
|
20111
|
-
stat =
|
|
20201
|
+
stat = fs8.lstatSync(file);
|
|
20112
20202
|
} catch {
|
|
20113
20203
|
continue;
|
|
20114
20204
|
}
|
|
@@ -20119,11 +20209,11 @@ class TaskStore {
|
|
|
20119
20209
|
report.blobs.removed += 1;
|
|
20120
20210
|
report.blobs.removedBytes += stat.size;
|
|
20121
20211
|
if (!dryRun)
|
|
20122
|
-
|
|
20212
|
+
fs8.rmSync(file, { force: true });
|
|
20123
20213
|
}
|
|
20124
20214
|
return success(null, null, null);
|
|
20125
20215
|
};
|
|
20126
|
-
const swept = dryRun ? sweep() : this.withLock(
|
|
20216
|
+
const swept = dryRun ? sweep() : this.withLock(path8.join(dirs.dir, "gc.lock"), sweep);
|
|
20127
20217
|
if (!swept.ok)
|
|
20128
20218
|
return swept;
|
|
20129
20219
|
const temporaryDirs = [
|
|
@@ -20131,17 +20221,17 @@ class TaskStore {
|
|
|
20131
20221
|
dirs.tasks,
|
|
20132
20222
|
dirs.blobs,
|
|
20133
20223
|
dirs.checkout,
|
|
20134
|
-
...names.map((name) =>
|
|
20224
|
+
...names.map((name) => path8.join(dirs.tasks, name))
|
|
20135
20225
|
];
|
|
20136
20226
|
for (const directory of temporaryDirs) {
|
|
20137
|
-
if (!
|
|
20227
|
+
if (!fs8.existsSync(directory))
|
|
20138
20228
|
continue;
|
|
20139
|
-
for (const name of
|
|
20229
|
+
for (const name of fs8.readdirSync(directory)) {
|
|
20140
20230
|
if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
|
|
20141
20231
|
continue;
|
|
20142
|
-
const file =
|
|
20232
|
+
const file = path8.join(directory, name);
|
|
20143
20233
|
try {
|
|
20144
|
-
const stat =
|
|
20234
|
+
const stat = fs8.lstatSync(file);
|
|
20145
20235
|
if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
|
|
20146
20236
|
continue;
|
|
20147
20237
|
} catch {
|
|
@@ -20149,37 +20239,37 @@ class TaskStore {
|
|
|
20149
20239
|
}
|
|
20150
20240
|
report.temporary.removed += 1;
|
|
20151
20241
|
if (!dryRun)
|
|
20152
|
-
|
|
20242
|
+
fs8.rmSync(file, { force: true });
|
|
20153
20243
|
}
|
|
20154
20244
|
}
|
|
20155
20245
|
return success(null, null, report);
|
|
20156
20246
|
}
|
|
20157
20247
|
paths() {
|
|
20158
20248
|
const location = this.location();
|
|
20159
|
-
const dir = location.ok ? location.data.dir :
|
|
20160
|
-
const checkout =
|
|
20249
|
+
const dir = location.ok ? location.data.dir : path8.join(this.root, ".workit");
|
|
20250
|
+
const checkout = path8.join(dir, "checkouts", checkoutSlug(this.root));
|
|
20161
20251
|
return {
|
|
20162
20252
|
dir,
|
|
20163
20253
|
shared: location.ok ? location.data.shared : false,
|
|
20164
|
-
marker:
|
|
20254
|
+
marker: path8.join(dir, "store.json"),
|
|
20165
20255
|
checkout,
|
|
20166
|
-
workspace:
|
|
20167
|
-
checkoutLock:
|
|
20168
|
-
tasks:
|
|
20169
|
-
index:
|
|
20170
|
-
blobs:
|
|
20171
|
-
legacy:
|
|
20172
|
-
v2:
|
|
20256
|
+
workspace: path8.join(checkout, "workspace.json"),
|
|
20257
|
+
checkoutLock: path8.join(checkout, "metadata.lock"),
|
|
20258
|
+
tasks: path8.join(dir, "tasks"),
|
|
20259
|
+
index: path8.join(dir, "task-index.json"),
|
|
20260
|
+
blobs: path8.join(dir, "blobs", "candidates"),
|
|
20261
|
+
legacy: path8.join(dir, "legacy", checkoutSlug(this.root), "v2"),
|
|
20262
|
+
v2: path8.join(this.root, ".workit")
|
|
20173
20263
|
};
|
|
20174
20264
|
}
|
|
20175
20265
|
eventsPath(taskId) {
|
|
20176
|
-
return
|
|
20266
|
+
return path8.join(this.paths().tasks, taskId, "events.jsonl");
|
|
20177
20267
|
}
|
|
20178
20268
|
snapshotPath(taskId) {
|
|
20179
|
-
return
|
|
20269
|
+
return path8.join(this.paths().tasks, taskId, "snapshot.json");
|
|
20180
20270
|
}
|
|
20181
20271
|
taskLockPath(taskId) {
|
|
20182
|
-
return
|
|
20272
|
+
return path8.join(this.paths().tasks, taskId, "lock");
|
|
20183
20273
|
}
|
|
20184
20274
|
ready(write = false) {
|
|
20185
20275
|
const location = this.location();
|
|
@@ -20189,7 +20279,7 @@ class TaskStore {
|
|
|
20189
20279
|
return success(null, null, null);
|
|
20190
20280
|
const dirs = this.paths();
|
|
20191
20281
|
try {
|
|
20192
|
-
const marker = JSON.parse(
|
|
20282
|
+
const marker = JSON.parse(fs8.readFileSync(dirs.marker, "utf8"));
|
|
20193
20283
|
if (typeof marker.version === "number" && marker.version > STORE_FORMAT)
|
|
20194
20284
|
return failure2("recovery_required", `the workit store at ${dirs.dir} has format ${marker.version}; upgrade Workit to use it`, { path: dirs.marker });
|
|
20195
20285
|
} catch {}
|
|
@@ -20218,21 +20308,21 @@ class TaskStore {
|
|
|
20218
20308
|
return {
|
|
20219
20309
|
read: (digest) => {
|
|
20220
20310
|
try {
|
|
20221
|
-
return JSON.parse(
|
|
20311
|
+
return JSON.parse(fs8.readFileSync(path8.join(dir, `${digest}.json`), "utf8"));
|
|
20222
20312
|
} catch (error) {
|
|
20223
20313
|
throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
|
|
20224
20314
|
}
|
|
20225
20315
|
},
|
|
20226
20316
|
write: (digest, value) => {
|
|
20227
|
-
const file =
|
|
20228
|
-
if (
|
|
20317
|
+
const file = path8.join(dir, `${digest}.json`);
|
|
20318
|
+
if (fs8.existsSync(file)) {
|
|
20229
20319
|
const stamp = new Date;
|
|
20230
20320
|
try {
|
|
20231
|
-
|
|
20321
|
+
fs8.utimesSync(file, stamp, stamp);
|
|
20232
20322
|
return;
|
|
20233
20323
|
} catch {}
|
|
20234
20324
|
}
|
|
20235
|
-
|
|
20325
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
20236
20326
|
replaceFile(file, JSON.stringify(value), true);
|
|
20237
20327
|
}
|
|
20238
20328
|
};
|
|
@@ -20314,7 +20404,7 @@ class TaskStore {
|
|
|
20314
20404
|
}
|
|
20315
20405
|
readSnapshot(taskId) {
|
|
20316
20406
|
try {
|
|
20317
|
-
const value = JSON.parse(
|
|
20407
|
+
const value = JSON.parse(fs8.readFileSync(this.snapshotPath(taskId), "utf8"));
|
|
20318
20408
|
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))
|
|
20319
20409
|
return null;
|
|
20320
20410
|
return value;
|
|
@@ -20416,8 +20506,8 @@ class TaskStore {
|
|
|
20416
20506
|
}
|
|
20417
20507
|
}
|
|
20418
20508
|
openTask(record, type, key, legacy = null) {
|
|
20419
|
-
const dir =
|
|
20420
|
-
|
|
20509
|
+
const dir = path8.join(this.paths().tasks, record.id);
|
|
20510
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
20421
20511
|
const blobs = this.blobStore();
|
|
20422
20512
|
const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
|
|
20423
20513
|
const event = this.event(record.id, 1, type, {
|
|
@@ -20469,11 +20559,11 @@ class TaskStore {
|
|
|
20469
20559
|
} catch {}
|
|
20470
20560
|
if (!latest || latest.events[0]?.id !== last.id)
|
|
20471
20561
|
return failure2("busy", "the task log changed during compaction; retry", { taskId });
|
|
20472
|
-
const appended =
|
|
20562
|
+
const appended = fs8.readFileSync(file).subarray(log.end, latest.end);
|
|
20473
20563
|
const bytes = Buffer.concat([Buffer.from(prefix, "utf8"), appended]);
|
|
20474
20564
|
replaceFile(file, bytes.toString("utf8"), true);
|
|
20475
20565
|
try {
|
|
20476
|
-
|
|
20566
|
+
fs8.rmSync(this.snapshotPath(taskId), { force: true });
|
|
20477
20567
|
} catch {}
|
|
20478
20568
|
const reloaded = this.load(taskId);
|
|
20479
20569
|
if (reloaded.ok && reloaded.data)
|
|
@@ -20486,7 +20576,7 @@ class TaskStore {
|
|
|
20486
20576
|
}
|
|
20487
20577
|
countEvents(file) {
|
|
20488
20578
|
try {
|
|
20489
|
-
const bytes =
|
|
20579
|
+
const bytes = fs8.readFileSync(file);
|
|
20490
20580
|
let count = 0;
|
|
20491
20581
|
for (let index = bytes.indexOf(10);index >= 0; index = bytes.indexOf(10, index + 1))
|
|
20492
20582
|
count += 1;
|
|
@@ -20508,8 +20598,8 @@ class TaskStore {
|
|
|
20508
20598
|
return bound.ok ? success(null, null, bound.data[0] ?? null) : bound;
|
|
20509
20599
|
}
|
|
20510
20600
|
withKeyLock(key, operation) {
|
|
20511
|
-
const digest =
|
|
20512
|
-
return this.withLock(
|
|
20601
|
+
const digest = createHash5("sha256").update(key).digest("hex").slice(0, 32);
|
|
20602
|
+
return this.withLock(path8.join(this.paths().dir, "keys", `${digest}.lock`), operation);
|
|
20513
20603
|
}
|
|
20514
20604
|
bindLocked(taskId, key, timestamp) {
|
|
20515
20605
|
const current = this.readWorkspace();
|
|
@@ -20650,7 +20740,7 @@ class TaskStore {
|
|
|
20650
20740
|
writeWorkspace(record) {
|
|
20651
20741
|
const file = this.paths().workspace;
|
|
20652
20742
|
try {
|
|
20653
|
-
|
|
20743
|
+
fs8.mkdirSync(path8.dirname(file), { recursive: true });
|
|
20654
20744
|
replaceFile(file, `${canonicalJson(record)}
|
|
20655
20745
|
`, true);
|
|
20656
20746
|
return success(null, null, null);
|
|
@@ -20660,7 +20750,7 @@ class TaskStore {
|
|
|
20660
20750
|
}
|
|
20661
20751
|
readIndex() {
|
|
20662
20752
|
try {
|
|
20663
|
-
const value = JSON.parse(
|
|
20753
|
+
const value = JSON.parse(fs8.readFileSync(this.paths().index, "utf8"));
|
|
20664
20754
|
if (!isObject5(value) || value.version !== INDEX_VERSION || !isObject5(value.tasks))
|
|
20665
20755
|
return {};
|
|
20666
20756
|
const tasks = {};
|
|
@@ -20674,7 +20764,7 @@ class TaskStore {
|
|
|
20674
20764
|
}
|
|
20675
20765
|
writeIndex(entries) {
|
|
20676
20766
|
try {
|
|
20677
|
-
|
|
20767
|
+
fs8.mkdirSync(this.paths().tasks, { recursive: true });
|
|
20678
20768
|
replaceFile(this.paths().index, JSON.stringify({
|
|
20679
20769
|
version: INDEX_VERSION,
|
|
20680
20770
|
tasks: Object.fromEntries(entries.map((entry) => [entry.id, entry]))
|
|
@@ -20728,23 +20818,23 @@ class TaskStore {
|
|
|
20728
20818
|
return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
|
|
20729
20819
|
}
|
|
20730
20820
|
v2Dirs() {
|
|
20731
|
-
return [...new Set([this.root, this.origin])].map((dir) =>
|
|
20821
|
+
return [...new Set([this.root, this.origin])].map((dir) => path8.join(dir, ".workit"));
|
|
20732
20822
|
}
|
|
20733
20823
|
pendingMigrations() {
|
|
20734
20824
|
return this.v2Dirs().filter((dir) => {
|
|
20735
|
-
const raw = this.readV2Workspace(
|
|
20825
|
+
const raw = this.readV2Workspace(path8.join(dir, "workspace.json"));
|
|
20736
20826
|
return raw !== null && raw !== TOMBSTONE;
|
|
20737
20827
|
});
|
|
20738
20828
|
}
|
|
20739
20829
|
migrateV2(v2) {
|
|
20740
20830
|
const dirs = this.paths();
|
|
20741
|
-
const v2Workspace =
|
|
20742
|
-
const v2Tasks =
|
|
20743
|
-
const legacy =
|
|
20831
|
+
const v2Workspace = path8.join(v2, "workspace.json");
|
|
20832
|
+
const v2Tasks = path8.join(v2, "tasks");
|
|
20833
|
+
const legacy = path8.join(dirs.dir, "legacy", checkoutSlug(path8.dirname(v2)), "v2");
|
|
20744
20834
|
const raw = this.readV2Workspace(v2Workspace);
|
|
20745
20835
|
if (raw === null || raw === TOMBSTONE)
|
|
20746
20836
|
return success(null, null, null);
|
|
20747
|
-
return this.withLock(dirs.checkoutLock, () => this.withLock(
|
|
20837
|
+
return this.withLock(dirs.checkoutLock, () => this.withLock(path8.join(v2, "metadata.lock"), () => {
|
|
20748
20838
|
const workspace = this.readV2Workspace(v2Workspace);
|
|
20749
20839
|
if (workspace === null || workspace === TOMBSTONE)
|
|
20750
20840
|
return success(null, null, null);
|
|
@@ -20753,7 +20843,7 @@ class TaskStore {
|
|
|
20753
20843
|
return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
|
|
20754
20844
|
let files = [];
|
|
20755
20845
|
try {
|
|
20756
|
-
files =
|
|
20846
|
+
files = fs8.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path8.join(v2Tasks, name)));
|
|
20757
20847
|
} catch {}
|
|
20758
20848
|
const current = this.readWorkspace();
|
|
20759
20849
|
if (!current.ok)
|
|
@@ -20771,22 +20861,22 @@ class TaskStore {
|
|
|
20771
20861
|
let skipped = 0;
|
|
20772
20862
|
const done = new Map;
|
|
20773
20863
|
try {
|
|
20774
|
-
|
|
20864
|
+
fs8.mkdirSync(path8.join(legacy, "tasks"), { recursive: true });
|
|
20775
20865
|
for (const name of ["workspace.json", "index.json"])
|
|
20776
|
-
if (
|
|
20777
|
-
|
|
20866
|
+
if (fs8.existsSync(path8.join(v2, name)))
|
|
20867
|
+
fs8.copyFileSync(path8.join(v2, name), path8.join(legacy, name));
|
|
20778
20868
|
for (const name of files) {
|
|
20779
20869
|
const id = name.slice(0, -5);
|
|
20780
|
-
const file =
|
|
20781
|
-
const bytes =
|
|
20870
|
+
const file = path8.join(v2Tasks, name);
|
|
20871
|
+
const bytes = fs8.readFileSync(file, "utf8");
|
|
20782
20872
|
const digest = sha256(bytes);
|
|
20783
|
-
|
|
20873
|
+
fs8.writeFileSync(path8.join(legacy, "tasks", name), bytes);
|
|
20784
20874
|
const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
|
|
20785
20875
|
if (!parsed.ok || parsed.data.id !== id)
|
|
20786
20876
|
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 });
|
|
20787
20877
|
const record = { ...parsed.data, workspaceId: target.id };
|
|
20788
|
-
const origin = { path:
|
|
20789
|
-
if (!
|
|
20878
|
+
const origin = { path: path8.join(legacy, "tasks", name), digest };
|
|
20879
|
+
if (!fs8.existsSync(this.eventsPath(id))) {
|
|
20790
20880
|
this.openTask(record, "migrated.from_v2", null, origin);
|
|
20791
20881
|
migrated += 1;
|
|
20792
20882
|
} else {
|
|
@@ -20821,8 +20911,8 @@ class TaskStore {
|
|
|
20821
20911
|
try {
|
|
20822
20912
|
let complete = true;
|
|
20823
20913
|
for (const [name, digest] of done) {
|
|
20824
|
-
const file =
|
|
20825
|
-
if (sha256(
|
|
20914
|
+
const file = path8.join(v2Tasks, name);
|
|
20915
|
+
if (sha256(fs8.readFileSync(file, "utf8")) !== digest) {
|
|
20826
20916
|
complete = false;
|
|
20827
20917
|
continue;
|
|
20828
20918
|
}
|
|
@@ -20832,7 +20922,7 @@ class TaskStore {
|
|
|
20832
20922
|
return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
|
|
20833
20923
|
path: v2
|
|
20834
20924
|
});
|
|
20835
|
-
|
|
20925
|
+
fs8.rmSync(path8.join(v2, "index.json"), { force: true });
|
|
20836
20926
|
this.writeTombstone(v2, target.id);
|
|
20837
20927
|
} catch (error) {
|
|
20838
20928
|
return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
|
|
@@ -20852,45 +20942,45 @@ class TaskStore {
|
|
|
20852
20942
|
}
|
|
20853
20943
|
adoptLocalStore() {
|
|
20854
20944
|
const dirs = this.paths();
|
|
20855
|
-
const marker =
|
|
20856
|
-
if (!dirs.shared || !
|
|
20945
|
+
const marker = path8.join(dirs.v2, "store.json");
|
|
20946
|
+
if (!dirs.shared || !fs8.existsSync(marker))
|
|
20857
20947
|
return success(null, null, null);
|
|
20858
20948
|
return this.withLock(dirs.checkoutLock, () => {
|
|
20859
|
-
if (!
|
|
20949
|
+
if (!fs8.existsSync(marker))
|
|
20860
20950
|
return success(null, null, null);
|
|
20861
20951
|
let tasks = 0;
|
|
20862
20952
|
try {
|
|
20863
20953
|
const merge = (from, to) => {
|
|
20864
|
-
if (!
|
|
20865
|
-
|
|
20954
|
+
if (!fs8.existsSync(to)) {
|
|
20955
|
+
fs8.mkdirSync(path8.dirname(to), { recursive: true });
|
|
20866
20956
|
try {
|
|
20867
|
-
|
|
20957
|
+
fs8.renameSync(from, to);
|
|
20868
20958
|
} catch {
|
|
20869
|
-
|
|
20870
|
-
|
|
20959
|
+
fs8.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
|
|
20960
|
+
fs8.rmSync(from, { recursive: true, force: true });
|
|
20871
20961
|
}
|
|
20872
20962
|
return;
|
|
20873
20963
|
}
|
|
20874
|
-
if (
|
|
20875
|
-
for (const name of
|
|
20876
|
-
merge(
|
|
20964
|
+
if (fs8.lstatSync(from).isDirectory() && fs8.lstatSync(to).isDirectory())
|
|
20965
|
+
for (const name of fs8.readdirSync(from))
|
|
20966
|
+
merge(path8.join(from, name), path8.join(to, name));
|
|
20877
20967
|
};
|
|
20878
20968
|
for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
|
|
20879
|
-
const from =
|
|
20880
|
-
if (!
|
|
20969
|
+
const from = path8.join(dirs.v2, sub);
|
|
20970
|
+
if (!fs8.existsSync(from))
|
|
20881
20971
|
continue;
|
|
20882
20972
|
if (sub === "tasks") {
|
|
20883
|
-
for (const name of
|
|
20884
|
-
if (validId(name) && !
|
|
20973
|
+
for (const name of fs8.readdirSync(from))
|
|
20974
|
+
if (validId(name) && !fs8.existsSync(path8.join(dirs.dir, sub, name)))
|
|
20885
20975
|
tasks += 1;
|
|
20886
20976
|
}
|
|
20887
|
-
merge(from,
|
|
20977
|
+
merge(from, path8.join(dirs.dir, sub));
|
|
20888
20978
|
}
|
|
20889
|
-
const ledger =
|
|
20890
|
-
if (
|
|
20891
|
-
merge(ledger,
|
|
20892
|
-
|
|
20893
|
-
|
|
20979
|
+
const ledger = path8.join(dirs.v2, "ledger");
|
|
20980
|
+
if (fs8.existsSync(ledger) && !fs8.existsSync(path8.join(dirs.dir, "ledger")))
|
|
20981
|
+
merge(ledger, path8.join(dirs.dir, "ledger"));
|
|
20982
|
+
fs8.rmSync(path8.join(dirs.v2, "task-index.json"), { force: true });
|
|
20983
|
+
fs8.renameSync(marker, path8.join(dirs.v2, "store.moved.json"));
|
|
20894
20984
|
} catch (error) {
|
|
20895
20985
|
return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
|
|
20896
20986
|
path: dirs.v2
|
|
@@ -20910,7 +21000,7 @@ class TaskStore {
|
|
|
20910
21000
|
readV2Workspace(file) {
|
|
20911
21001
|
let bytes;
|
|
20912
21002
|
try {
|
|
20913
|
-
bytes =
|
|
21003
|
+
bytes = fs8.readFileSync(file, "utf8");
|
|
20914
21004
|
} catch {
|
|
20915
21005
|
return null;
|
|
20916
21006
|
}
|
|
@@ -20928,7 +21018,7 @@ class TaskStore {
|
|
|
20928
21018
|
schemaVersion: SCHEMA_VERSION,
|
|
20929
21019
|
id: workspaceId ?? newId(),
|
|
20930
21020
|
revision: newRevision(),
|
|
20931
|
-
root:
|
|
21021
|
+
root: path8.dirname(v2),
|
|
20932
21022
|
runtime: { createdWith: version, updatedWith: version },
|
|
20933
21023
|
writer: null,
|
|
20934
21024
|
store: {
|
|
@@ -20939,12 +21029,12 @@ class TaskStore {
|
|
|
20939
21029
|
},
|
|
20940
21030
|
critical: ["store"]
|
|
20941
21031
|
};
|
|
20942
|
-
|
|
20943
|
-
const ignore =
|
|
20944
|
-
if (!
|
|
20945
|
-
|
|
21032
|
+
fs8.mkdirSync(v2, { recursive: true });
|
|
21033
|
+
const ignore = path8.join(v2, ".gitignore");
|
|
21034
|
+
if (!fs8.existsSync(ignore))
|
|
21035
|
+
fs8.writeFileSync(ignore, `*
|
|
20946
21036
|
`);
|
|
20947
|
-
replaceFile(
|
|
21037
|
+
replaceFile(path8.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
|
|
20948
21038
|
`, true);
|
|
20949
21039
|
}
|
|
20950
21040
|
stubFor(taskId) {
|
|
@@ -20960,7 +21050,7 @@ class TaskStore {
|
|
|
20960
21050
|
}
|
|
20961
21051
|
isStub(file) {
|
|
20962
21052
|
try {
|
|
20963
|
-
const value = JSON.parse(
|
|
21053
|
+
const value = JSON.parse(fs8.readFileSync(file, "utf8"));
|
|
20964
21054
|
return isObject5(value) && isObject5(value.store) && value.store.format === "workit-store";
|
|
20965
21055
|
} catch {
|
|
20966
21056
|
return false;
|
|
@@ -20968,32 +21058,32 @@ class TaskStore {
|
|
|
20968
21058
|
}
|
|
20969
21059
|
writeTombstoneIfMissing() {
|
|
20970
21060
|
try {
|
|
20971
|
-
const v2 =
|
|
20972
|
-
if (
|
|
21061
|
+
const v2 = path8.join(this.root, ".workit");
|
|
21062
|
+
if (fs8.existsSync(path8.join(v2, "workspace.json")))
|
|
20973
21063
|
return;
|
|
20974
21064
|
let id = null;
|
|
20975
21065
|
try {
|
|
20976
|
-
id = JSON.parse(
|
|
21066
|
+
id = JSON.parse(fs8.readFileSync(this.paths().workspace, "utf8")).id ?? null;
|
|
20977
21067
|
} catch {}
|
|
20978
21068
|
this.writeTombstone(v2, id);
|
|
20979
21069
|
} catch {}
|
|
20980
21070
|
}
|
|
20981
21071
|
initializeStorage() {
|
|
20982
21072
|
const dirs = this.paths();
|
|
20983
|
-
retryTransient(() =>
|
|
20984
|
-
retryTransient(() =>
|
|
20985
|
-
if (!
|
|
21073
|
+
retryTransient(() => fs8.mkdirSync(dirs.checkout, { recursive: true }));
|
|
21074
|
+
retryTransient(() => fs8.mkdirSync(dirs.tasks, { recursive: true }));
|
|
21075
|
+
if (!fs8.existsSync(dirs.marker))
|
|
20986
21076
|
replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
|
|
20987
21077
|
`, false);
|
|
20988
21078
|
if (!dirs.shared) {
|
|
20989
|
-
const ignore =
|
|
21079
|
+
const ignore = path8.join(dirs.dir, ".gitignore");
|
|
20990
21080
|
let current = null;
|
|
20991
21081
|
try {
|
|
20992
|
-
current =
|
|
21082
|
+
current = fs8.readFileSync(ignore, "utf8");
|
|
20993
21083
|
} catch {}
|
|
20994
21084
|
if (current !== `*
|
|
20995
21085
|
`)
|
|
20996
|
-
retryTransient(() =>
|
|
21086
|
+
retryTransient(() => fs8.writeFileSync(ignore, `*
|
|
20997
21087
|
`));
|
|
20998
21088
|
}
|
|
20999
21089
|
this.writeTombstoneIfMissing();
|
|
@@ -21009,7 +21099,7 @@ class TaskStore {
|
|
|
21009
21099
|
shouldReclaim: ({ payload, nowMs }) => {
|
|
21010
21100
|
let ageMs = null;
|
|
21011
21101
|
try {
|
|
21012
|
-
ageMs = nowMs -
|
|
21102
|
+
ageMs = nowMs - fs8.lstatSync(lockPath).mtimeMs;
|
|
21013
21103
|
} catch {}
|
|
21014
21104
|
return classifyLockOwner(payload, ageMs, localLockHost(), TASK_STORE_LOCK).state === "stale";
|
|
21015
21105
|
},
|
|
@@ -21046,7 +21136,7 @@ class TaskStore {
|
|
|
21046
21136
|
withLock(lockPath, operation) {
|
|
21047
21137
|
try {
|
|
21048
21138
|
this.initializeStorage();
|
|
21049
|
-
|
|
21139
|
+
fs8.mkdirSync(path8.dirname(lockPath), { recursive: true });
|
|
21050
21140
|
} catch (error) {
|
|
21051
21141
|
return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
|
|
21052
21142
|
path: this.paths().dir
|
|
@@ -21086,18 +21176,18 @@ class TaskStore {
|
|
|
21086
21176
|
lockDirWritable(lockPath) {
|
|
21087
21177
|
const probe = `${lockPath}.${process.pid}.${randomUUID3()}.probe`;
|
|
21088
21178
|
try {
|
|
21089
|
-
|
|
21179
|
+
fs8.closeSync(fs8.openSync(probe, "wx", 384));
|
|
21090
21180
|
} catch {
|
|
21091
21181
|
return false;
|
|
21092
21182
|
}
|
|
21093
21183
|
try {
|
|
21094
|
-
|
|
21184
|
+
fs8.rmSync(probe, { force: true });
|
|
21095
21185
|
} catch {}
|
|
21096
21186
|
return true;
|
|
21097
21187
|
}
|
|
21098
21188
|
lockHolderPresent(lockPath) {
|
|
21099
21189
|
try {
|
|
21100
|
-
|
|
21190
|
+
fs8.lstatSync(lockPath);
|
|
21101
21191
|
return true;
|
|
21102
21192
|
} catch (error) {
|
|
21103
21193
|
return error?.code !== "ENOENT";
|
|
@@ -21109,12 +21199,12 @@ class TaskStore {
|
|
|
21109
21199
|
if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
|
|
21110
21200
|
return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
|
|
21111
21201
|
path: lockPath,
|
|
21112
|
-
guidance: `Check permissions and the read-only attribute on ${
|
|
21202
|
+
guidance: `Check permissions and the read-only attribute on ${path8.dirname(lockPath)}.`
|
|
21113
21203
|
});
|
|
21114
21204
|
if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
|
|
21115
21205
|
let lock = null;
|
|
21116
21206
|
try {
|
|
21117
|
-
lock = parseMetadataLockOrNull(
|
|
21207
|
+
lock = parseMetadataLockOrNull(fs8.readFileSync(lockPath, "utf8"));
|
|
21118
21208
|
} catch {}
|
|
21119
21209
|
return failure2("busy", `workit lock is held by another Workit call${lock ? ` (pid ${lock.pid} on ${lock.host})` : ""}; retry shortly`, {
|
|
21120
21210
|
outcome: "not_started",
|
|
@@ -21132,15 +21222,15 @@ var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
|
|
|
21132
21222
|
var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
|
|
21133
21223
|
|
|
21134
21224
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
21135
|
-
import { createHash as
|
|
21136
|
-
import * as
|
|
21225
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
21226
|
+
import * as fs13 from "node:fs";
|
|
21137
21227
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
21138
|
-
import
|
|
21228
|
+
import path16 from "node:path";
|
|
21139
21229
|
|
|
21140
21230
|
// packages/workit-core/src/check-config.ts
|
|
21141
21231
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
21142
|
-
import
|
|
21143
|
-
import
|
|
21232
|
+
import fs9 from "node:fs";
|
|
21233
|
+
import path9 from "node:path";
|
|
21144
21234
|
var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
|
|
21145
21235
|
var CHECKS_FILE = "workit.checks.json";
|
|
21146
21236
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -21193,10 +21283,10 @@ var git = (cwd, args) => {
|
|
|
21193
21283
|
return value || null;
|
|
21194
21284
|
};
|
|
21195
21285
|
function checkRoot(cwd) {
|
|
21196
|
-
return
|
|
21286
|
+
return path9.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
|
|
21197
21287
|
}
|
|
21198
21288
|
var packageManager = (root) => {
|
|
21199
|
-
const has = (file) =>
|
|
21289
|
+
const has = (file) => fs9.existsSync(path9.join(root, file));
|
|
21200
21290
|
if (has("bun.lock") || has("bun.lockb"))
|
|
21201
21291
|
return "bun";
|
|
21202
21292
|
if (has("pnpm-lock.yaml"))
|
|
@@ -21216,7 +21306,7 @@ var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
|
|
|
21216
21306
|
function detectedChecks(root) {
|
|
21217
21307
|
const read = (file) => {
|
|
21218
21308
|
try {
|
|
21219
|
-
return
|
|
21309
|
+
return fs9.readFileSync(path9.join(root, file), "utf8");
|
|
21220
21310
|
} catch {
|
|
21221
21311
|
return null;
|
|
21222
21312
|
}
|
|
@@ -21255,7 +21345,7 @@ function detectedChecks(root) {
|
|
|
21255
21345
|
var GATES = ["testing", "verification"];
|
|
21256
21346
|
function loadCheckConfig(cwd) {
|
|
21257
21347
|
const root = checkRoot(cwd);
|
|
21258
|
-
const file =
|
|
21348
|
+
const file = path9.join(root, CHECKS_FILE);
|
|
21259
21349
|
const none = (error = null) => ({
|
|
21260
21350
|
source: error ? CHECKS_FILE : "none",
|
|
21261
21351
|
root,
|
|
@@ -21265,7 +21355,7 @@ function loadCheckConfig(cwd) {
|
|
|
21265
21355
|
});
|
|
21266
21356
|
let text = null;
|
|
21267
21357
|
try {
|
|
21268
|
-
text =
|
|
21358
|
+
text = fs9.readFileSync(file, "utf8");
|
|
21269
21359
|
} catch (error) {
|
|
21270
21360
|
if (error.code !== "ENOENT")
|
|
21271
21361
|
return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
|
|
@@ -21328,10 +21418,10 @@ function workitAvailable(env = process.env) {
|
|
|
21328
21418
|
return workitOnPath;
|
|
21329
21419
|
const windows = process.platform === "win32";
|
|
21330
21420
|
const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
|
|
21331
|
-
const dirs = (env.PATH ?? env.Path ?? "").split(
|
|
21421
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(path9.delimiter).filter(Boolean);
|
|
21332
21422
|
workitOnPath = dirs.some((dir) => exts.some((ext) => {
|
|
21333
21423
|
try {
|
|
21334
|
-
return
|
|
21424
|
+
return fs9.statSync(path9.join(dir, `workit${ext}`)).isFile();
|
|
21335
21425
|
} catch {
|
|
21336
21426
|
return false;
|
|
21337
21427
|
}
|
|
@@ -21344,10 +21434,10 @@ function checkCommand(args, version, onPath = workitAvailable()) {
|
|
|
21344
21434
|
|
|
21345
21435
|
// packages/workit-core/src/git/rev.ts
|
|
21346
21436
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
21347
|
-
import { createHash as
|
|
21348
|
-
import
|
|
21437
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
21438
|
+
import fs10 from "node:fs";
|
|
21349
21439
|
import os2 from "node:os";
|
|
21350
|
-
import
|
|
21440
|
+
import path10 from "node:path";
|
|
21351
21441
|
var MAX_BUFFER = 256 * 1024 * 1024;
|
|
21352
21442
|
var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
|
|
21353
21443
|
var git2 = (cwd, args, options = {}) => {
|
|
@@ -21382,10 +21472,10 @@ function headSha(cwd) {
|
|
|
21382
21472
|
var emptyTree = (cwd) => line(git2(cwd, ["mktree"], { input: "" }));
|
|
21383
21473
|
var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
|
|
21384
21474
|
var seedIndex = (from, to) => {
|
|
21385
|
-
|
|
21386
|
-
const { atimeNs, mtimeNs } =
|
|
21475
|
+
fs10.copyFileSync(from, to);
|
|
21476
|
+
const { atimeNs, mtimeNs } = fs10.statSync(from, { bigint: true });
|
|
21387
21477
|
const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
|
|
21388
|
-
|
|
21478
|
+
fs10.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
|
|
21389
21479
|
};
|
|
21390
21480
|
function worktreeTree(cwd, options = {}) {
|
|
21391
21481
|
const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
|
|
@@ -21394,10 +21484,10 @@ function worktreeTree(cwd, options = {}) {
|
|
|
21394
21484
|
return null;
|
|
21395
21485
|
const cap = options.maxUntrackedBytes ?? MAX_UNTRACKED_BYTES;
|
|
21396
21486
|
const timeoutMs = options.timeoutMs ?? GIT_TIMEOUTS.worktree;
|
|
21397
|
-
const scratch =
|
|
21487
|
+
const scratch = fs10.mkdtempSync(path10.join(os2.tmpdir(), "workit-tree-"));
|
|
21398
21488
|
try {
|
|
21399
|
-
const tempIndex =
|
|
21400
|
-
if (
|
|
21489
|
+
const tempIndex = path10.join(scratch, "index");
|
|
21490
|
+
if (fs10.existsSync(indexPath))
|
|
21401
21491
|
seedIndex(indexPath, tempIndex);
|
|
21402
21492
|
const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
|
|
21403
21493
|
const untracked = git2(top, ["ls-files", "--others", "--exclude-standard", "-z"], {
|
|
@@ -21409,7 +21499,7 @@ function worktreeTree(cwd, options = {}) {
|
|
|
21409
21499
|
const skipped = [];
|
|
21410
21500
|
for (const file of untracked.stdout.split("\x00").filter(Boolean)) {
|
|
21411
21501
|
try {
|
|
21412
|
-
const stat =
|
|
21502
|
+
const stat = fs10.statSync(path10.join(top, file));
|
|
21413
21503
|
if (stat.isFile() && stat.size > cap)
|
|
21414
21504
|
skipped.push({ path: file, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) });
|
|
21415
21505
|
} catch {}
|
|
@@ -21426,10 +21516,10 @@ function worktreeTree(cwd, options = {}) {
|
|
|
21426
21516
|
if (!tree)
|
|
21427
21517
|
return null;
|
|
21428
21518
|
const base = line(git2(top, ["rev-parse", "--verify", "-q", "HEAD^{tree}"])) ?? emptyTree(top);
|
|
21429
|
-
const key = skipped.length ? `sha256:${
|
|
21519
|
+
const key = skipped.length ? `sha256:${createHash6("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
|
|
21430
21520
|
return { tree, key, dirty: tree !== base || skipped.length > 0, skipped };
|
|
21431
21521
|
} finally {
|
|
21432
|
-
|
|
21522
|
+
fs10.rmSync(scratch, { recursive: true, force: true });
|
|
21433
21523
|
}
|
|
21434
21524
|
}
|
|
21435
21525
|
function worktreeSignal(cwd, options = {}) {
|
|
@@ -21449,16 +21539,16 @@ function worktreeSignal(cwd, options = {}) {
|
|
|
21449
21539
|
});
|
|
21450
21540
|
if (!status.ok)
|
|
21451
21541
|
return null;
|
|
21452
|
-
const hash =
|
|
21542
|
+
const hash = createHash6("sha256");
|
|
21453
21543
|
hash.update(`${headSha(top) ?? "unborn"}\x00`);
|
|
21454
21544
|
for (const entry of status.stdout.split("\x00")) {
|
|
21455
21545
|
if (entry.length < 4)
|
|
21456
21546
|
continue;
|
|
21457
21547
|
const file = entry.slice(3);
|
|
21458
|
-
const absolute =
|
|
21548
|
+
const absolute = path10.join(top, file);
|
|
21459
21549
|
let stat = "-";
|
|
21460
21550
|
try {
|
|
21461
|
-
const info =
|
|
21551
|
+
const info = fs10.lstatSync(absolute, { bigint: true });
|
|
21462
21552
|
stat = `${info.mode.toString(8)}:${info.size}:${info.mtimeNs}`;
|
|
21463
21553
|
if (info.isDirectory())
|
|
21464
21554
|
stat += `:${headSha(absolute) ?? "-"}`;
|
|
@@ -21525,17 +21615,17 @@ function pushRemoteName(cwd, branch) {
|
|
|
21525
21615
|
|
|
21526
21616
|
// packages/workit-core/src/core/policy/derive.ts
|
|
21527
21617
|
import { existsSync as existsSync4 } from "node:fs";
|
|
21528
|
-
import
|
|
21618
|
+
import path15 from "node:path";
|
|
21529
21619
|
|
|
21530
21620
|
// packages/workit-core/src/ledger.ts
|
|
21531
21621
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
21532
|
-
import { createHash as
|
|
21533
|
-
import
|
|
21534
|
-
import
|
|
21622
|
+
import { createHash as createHash9, randomBytes as randomBytes3 } from "node:crypto";
|
|
21623
|
+
import fs12 from "node:fs";
|
|
21624
|
+
import path14 from "node:path";
|
|
21535
21625
|
|
|
21536
21626
|
// packages/workit-core/src/core/vcs-config.ts
|
|
21537
|
-
import
|
|
21538
|
-
import
|
|
21627
|
+
import fs11 from "node:fs";
|
|
21628
|
+
import path13 from "node:path";
|
|
21539
21629
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
21540
21630
|
|
|
21541
21631
|
// packages/workit-core/src/core/config.ts
|
|
@@ -21549,7 +21639,7 @@ import {
|
|
|
21549
21639
|
writeFileSync as writeFileSync2
|
|
21550
21640
|
} from "node:fs";
|
|
21551
21641
|
import os3 from "node:os";
|
|
21552
|
-
import
|
|
21642
|
+
import path11 from "node:path";
|
|
21553
21643
|
|
|
21554
21644
|
// packages/workit-core/src/core/boundary.ts
|
|
21555
21645
|
import { statSync as statSync2 } from "node:fs";
|
|
@@ -21608,7 +21698,7 @@ var mergePreset = (preset, input = {}, current = {
|
|
|
21608
21698
|
protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
|
|
21609
21699
|
};
|
|
21610
21700
|
};
|
|
21611
|
-
var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ??
|
|
21701
|
+
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");
|
|
21612
21702
|
var migratedDir = null;
|
|
21613
21703
|
var migrationFailed = false;
|
|
21614
21704
|
var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
@@ -21618,7 +21708,7 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
21618
21708
|
migratedDir = dir;
|
|
21619
21709
|
return dir;
|
|
21620
21710
|
}
|
|
21621
|
-
const legacy =
|
|
21711
|
+
const legacy = path11.join(process.env.XDG_CONFIG_HOME || path11.join(os3.homedir(), ".config"), "workflow-toolkit");
|
|
21622
21712
|
if (!existsSync3(legacy)) {
|
|
21623
21713
|
migratedDir = dir;
|
|
21624
21714
|
return dir;
|
|
@@ -21631,8 +21721,8 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
21631
21721
|
mkdirSync2(dir, { recursive: true });
|
|
21632
21722
|
diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
|
|
21633
21723
|
for (const entry of readdirSync2(legacy, { withFileTypes: true })) {
|
|
21634
|
-
const src =
|
|
21635
|
-
const dest =
|
|
21724
|
+
const src = path11.join(legacy, entry.name);
|
|
21725
|
+
const dest = path11.join(dir, entry.name);
|
|
21636
21726
|
if (existsSync3(dest))
|
|
21637
21727
|
continue;
|
|
21638
21728
|
try {
|
|
@@ -21704,7 +21794,7 @@ var parseConfigResult = (raw, file) => {
|
|
|
21704
21794
|
return { status: "valid", path: file, config };
|
|
21705
21795
|
};
|
|
21706
21796
|
var readConfigTyped = (dir) => {
|
|
21707
|
-
const file =
|
|
21797
|
+
const file = path11.join(dir ?? configDir(), "config.json");
|
|
21708
21798
|
return parseConfigResult(readSafe(file), file);
|
|
21709
21799
|
};
|
|
21710
21800
|
var readConfig = () => {
|
|
@@ -21713,7 +21803,7 @@ var readConfig = () => {
|
|
|
21713
21803
|
throw new Error(result.error);
|
|
21714
21804
|
return result.config ?? DEFAULTS;
|
|
21715
21805
|
};
|
|
21716
|
-
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ??
|
|
21806
|
+
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path11.join(configDir(), "vcs.json");
|
|
21717
21807
|
var branchGlobPattern = (glob) => new RegExp(`^${glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i");
|
|
21718
21808
|
var resolveBranchPolicy = (config, workspace, repoPreset) => {
|
|
21719
21809
|
const wp = workspace?.branchPolicy ?? {};
|
|
@@ -21745,9 +21835,9 @@ var COMMIT_PRESETS = [
|
|
|
21745
21835
|
|
|
21746
21836
|
// packages/workit-core/src/core/workspaces.ts
|
|
21747
21837
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
21748
|
-
import { createHash as
|
|
21838
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
21749
21839
|
import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
21750
|
-
import
|
|
21840
|
+
import path12 from "node:path";
|
|
21751
21841
|
|
|
21752
21842
|
// packages/workit-core/src/core/branch-policy.ts
|
|
21753
21843
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
@@ -21872,7 +21962,7 @@ var releaseTrackSchema = object2({
|
|
|
21872
21962
|
context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
|
|
21873
21963
|
}
|
|
21874
21964
|
}
|
|
21875
|
-
if (track.versionSource?.kind === "package-json" && (
|
|
21965
|
+
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(".."))) {
|
|
21876
21966
|
context.addIssue({
|
|
21877
21967
|
code: "custom",
|
|
21878
21968
|
path: ["versionSource", "path"],
|
|
@@ -21893,6 +21983,8 @@ var workspaceConfigSchema = object2({
|
|
|
21893
21983
|
defaultEndpoint: unknown().optional(),
|
|
21894
21984
|
verification: _enum(["self", "independent"]).optional(),
|
|
21895
21985
|
trailers: boolean2().optional(),
|
|
21986
|
+
liveSessionGuard: boolean2().optional(),
|
|
21987
|
+
fanout: object2({ tierModels: unknown().optional() }).passthrough().optional(),
|
|
21896
21988
|
autonomy: object2(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
|
|
21897
21989
|
kind,
|
|
21898
21990
|
union([boolean2(), literal("verified")]).optional()
|
|
@@ -21933,7 +22025,7 @@ var workspacesFileSchema = object2({ workspaces: array(workspaceConfigSchema).op
|
|
|
21933
22025
|
names.add(workspace.name);
|
|
21934
22026
|
}
|
|
21935
22027
|
});
|
|
21936
|
-
var workspacesRevision = (contents) =>
|
|
22028
|
+
var workspacesRevision = (contents) => createHash7("sha256").update(contents ?? "<missing>").digest("hex");
|
|
21937
22029
|
var validateWorkspacesDocument = (parsed, file) => {
|
|
21938
22030
|
if (!isConfigObject(parsed)) {
|
|
21939
22031
|
return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
|
|
@@ -21971,7 +22063,7 @@ var validateWorkspacesDocument = (parsed, file) => {
|
|
|
21971
22063
|
};
|
|
21972
22064
|
};
|
|
21973
22065
|
var readWorkspacesResult = (dir = configDir()) => {
|
|
21974
|
-
const file =
|
|
22066
|
+
const file = path12.join(dir, "workspaces.json");
|
|
21975
22067
|
let raw;
|
|
21976
22068
|
try {
|
|
21977
22069
|
raw = readFileSync3(file, "utf8");
|
|
@@ -22082,9 +22174,9 @@ var mainCheckoutOf = (cwd) => {
|
|
|
22082
22174
|
let main = null;
|
|
22083
22175
|
const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
|
|
22084
22176
|
const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
|
|
22085
|
-
if (gitDir && common &&
|
|
22177
|
+
if (gitDir && common && path12.resolve(gitDir) !== path12.resolve(common)) {
|
|
22086
22178
|
const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
|
|
22087
|
-
main = listed?.trim() || (
|
|
22179
|
+
main = listed?.trim() || (path12.basename(common) === ".git" ? path12.dirname(common) : common);
|
|
22088
22180
|
}
|
|
22089
22181
|
mainCheckouts.set(cwd, main);
|
|
22090
22182
|
return main;
|
|
@@ -22175,7 +22267,7 @@ var runtimeWorkspaceIndexSchema = object2({
|
|
|
22175
22267
|
workspaces: array(object2({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
22176
22268
|
}).passthrough();
|
|
22177
22269
|
var matchRuntimeWorkspace = (cwd, dir = configDir()) => {
|
|
22178
|
-
const file =
|
|
22270
|
+
const file = path12.join(dir, "workspaces.json");
|
|
22179
22271
|
let raw;
|
|
22180
22272
|
try {
|
|
22181
22273
|
raw = readFileSync3(file, "utf8");
|
|
@@ -22210,6 +22302,17 @@ var resolveRuntimeReleaseTracks = (cwd) => {
|
|
|
22210
22302
|
const matched = matchRuntimeWorkspace(cwd);
|
|
22211
22303
|
return matched ? { workspace: matched.selected.name, releaseTracks: matched.selected.releaseTracks } : null;
|
|
22212
22304
|
};
|
|
22305
|
+
var TIER_KEYS = new Set(["mundane", "standard", "hard"]);
|
|
22306
|
+
var TIER_HOSTS = new Set([
|
|
22307
|
+
"claude_code",
|
|
22308
|
+
"opencode",
|
|
22309
|
+
"codex",
|
|
22310
|
+
"codex_cli",
|
|
22311
|
+
"codex_desktop",
|
|
22312
|
+
"cursor",
|
|
22313
|
+
"pi",
|
|
22314
|
+
"default"
|
|
22315
|
+
]);
|
|
22213
22316
|
var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
22214
22317
|
const matched = matchRuntimeWorkspace(cwd);
|
|
22215
22318
|
if (!matched)
|
|
@@ -22250,7 +22353,7 @@ function workspaceDefaultTarget(cwd, globalDefault) {
|
|
|
22250
22353
|
}
|
|
22251
22354
|
|
|
22252
22355
|
// packages/workit-core/src/core/release-tracks.ts
|
|
22253
|
-
import { createHash as
|
|
22356
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
22254
22357
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
22255
22358
|
var KNOWN_FIELDS = new Set([
|
|
22256
22359
|
"strategy",
|
|
@@ -22620,7 +22723,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
22620
22723
|
]) ?? "",
|
|
22621
22724
|
git3(cwd, ["config", "--get", recordedBaseKey(branch)]) ?? ""
|
|
22622
22725
|
] : [];
|
|
22623
|
-
key =
|
|
22726
|
+
key = createHash8("sha256").update(JSON.stringify({ cwd, input, refs, head, own })).digest("hex");
|
|
22624
22727
|
const hit = memo2.get(key);
|
|
22625
22728
|
if (hit)
|
|
22626
22729
|
return hit;
|
|
@@ -22635,7 +22738,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
22635
22738
|
}
|
|
22636
22739
|
|
|
22637
22740
|
// packages/workit-core/src/core/vcs-config.ts
|
|
22638
|
-
var workspacesPath = () =>
|
|
22741
|
+
var workspacesPath = () => path13.join(configDir(), "workspaces.json");
|
|
22639
22742
|
var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
22640
22743
|
var remoteProvider = (cwd) => {
|
|
22641
22744
|
const r = spawnSync8("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
|
|
@@ -22725,7 +22828,7 @@ var readVcsConfig = () => {
|
|
|
22725
22828
|
const file = vcsConfigPath();
|
|
22726
22829
|
let raw;
|
|
22727
22830
|
try {
|
|
22728
|
-
raw =
|
|
22831
|
+
raw = fs11.readFileSync(file, "utf8");
|
|
22729
22832
|
} catch {
|
|
22730
22833
|
return { status: "missing", path: file, config: {} };
|
|
22731
22834
|
}
|
|
@@ -22810,7 +22913,7 @@ function vcsConfig(mode, cwd, options = {}) {
|
|
|
22810
22913
|
const prov = cfg[provider] ?? {};
|
|
22811
22914
|
const out = {
|
|
22812
22915
|
ok: true,
|
|
22813
|
-
configPath:
|
|
22916
|
+
configPath: path13.resolve(cfgPath),
|
|
22814
22917
|
provider,
|
|
22815
22918
|
defaultTargetBranch: workspaceDefault,
|
|
22816
22919
|
pr: cfg.pr ?? {},
|
|
@@ -23021,7 +23124,7 @@ function diffHash(cwd, base, head) {
|
|
|
23021
23124
|
], GIT_TIMEOUTS.worktree);
|
|
23022
23125
|
if (!diff.ok || !diff.stdout)
|
|
23023
23126
|
return null;
|
|
23024
|
-
const hash =
|
|
23127
|
+
const hash = createHash9("sha256");
|
|
23025
23128
|
for (const line of diff.stdout.split(`
|
|
23026
23129
|
`)) {
|
|
23027
23130
|
if (line.startsWith("index "))
|
|
@@ -23033,16 +23136,16 @@ function diffHash(cwd, base, head) {
|
|
|
23033
23136
|
return `sha256:${hash.digest("hex")}`;
|
|
23034
23137
|
}
|
|
23035
23138
|
function storeRoot(cwd) {
|
|
23036
|
-
const location = resolveStore(
|
|
23139
|
+
const location = resolveStore(path14.resolve(cwd));
|
|
23037
23140
|
if (location instanceof Error)
|
|
23038
23141
|
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");
|
|
23039
23142
|
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
23040
23143
|
}
|
|
23041
23144
|
function ledgerPath(cwd) {
|
|
23042
23145
|
const root = storeRoot(cwd);
|
|
23043
|
-
return root.ok ? { ok: true, value:
|
|
23146
|
+
return root.ok ? { ok: true, value: path14.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
23044
23147
|
}
|
|
23045
|
-
var newId2 = () => `${Date.now().toString(36)}-${
|
|
23148
|
+
var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
|
|
23046
23149
|
var NETWORK_FS_TYPES = new Set([
|
|
23047
23150
|
26985,
|
|
23048
23151
|
20859,
|
|
@@ -23060,7 +23163,7 @@ var onNetworkFs = (dir) => {
|
|
|
23060
23163
|
if (process.platform !== "linux")
|
|
23061
23164
|
return false;
|
|
23062
23165
|
try {
|
|
23063
|
-
return isNetworkFsType(
|
|
23166
|
+
return isNetworkFsType(fs12.statfsSync(dir).type);
|
|
23064
23167
|
} catch {
|
|
23065
23168
|
return false;
|
|
23066
23169
|
}
|
|
@@ -23070,15 +23173,15 @@ var LOCK_STALE_MS = 30000;
|
|
|
23070
23173
|
var LOCK_OWNER = "owner";
|
|
23071
23174
|
var lockToken = (lock) => {
|
|
23072
23175
|
try {
|
|
23073
|
-
return
|
|
23176
|
+
return fs12.readFileSync(path14.join(lock, LOCK_OWNER), "utf8");
|
|
23074
23177
|
} catch {
|
|
23075
23178
|
return null;
|
|
23076
23179
|
}
|
|
23077
23180
|
};
|
|
23078
23181
|
var lockAgeMs = (lock) => {
|
|
23079
|
-
for (const target of [
|
|
23182
|
+
for (const target of [path14.join(lock, LOCK_OWNER), lock]) {
|
|
23080
23183
|
try {
|
|
23081
|
-
return Date.now() -
|
|
23184
|
+
return Date.now() - fs12.statSync(target).mtimeMs;
|
|
23082
23185
|
} catch {}
|
|
23083
23186
|
}
|
|
23084
23187
|
return null;
|
|
@@ -23086,55 +23189,55 @@ var lockAgeMs = (lock) => {
|
|
|
23086
23189
|
var ledgerLock = {
|
|
23087
23190
|
acquire(lock) {
|
|
23088
23191
|
try {
|
|
23089
|
-
|
|
23192
|
+
fs12.mkdirSync(lock);
|
|
23090
23193
|
} catch (error) {
|
|
23091
23194
|
if (error.code === "EEXIST")
|
|
23092
23195
|
return null;
|
|
23093
23196
|
throw error;
|
|
23094
23197
|
}
|
|
23095
|
-
const token = `${process.pid}-${
|
|
23096
|
-
|
|
23198
|
+
const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
|
|
23199
|
+
fs12.writeFileSync(path14.join(lock, LOCK_OWNER), token);
|
|
23097
23200
|
return token;
|
|
23098
23201
|
},
|
|
23099
23202
|
release(lock, token) {
|
|
23100
23203
|
if (lockToken(lock) !== token)
|
|
23101
23204
|
return false;
|
|
23102
|
-
const tomb = `${lock}.release-${
|
|
23205
|
+
const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
|
|
23103
23206
|
try {
|
|
23104
|
-
|
|
23207
|
+
fs12.renameSync(lock, tomb);
|
|
23105
23208
|
} catch {
|
|
23106
23209
|
return false;
|
|
23107
23210
|
}
|
|
23108
23211
|
if (lockToken(tomb) !== token) {
|
|
23109
23212
|
try {
|
|
23110
|
-
|
|
23213
|
+
fs12.renameSync(tomb, lock);
|
|
23111
23214
|
} catch {}
|
|
23112
23215
|
return false;
|
|
23113
23216
|
}
|
|
23114
|
-
|
|
23217
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
23115
23218
|
return true;
|
|
23116
23219
|
},
|
|
23117
23220
|
token: lockToken,
|
|
23118
23221
|
reap(lock, observed) {
|
|
23119
|
-
const tomb = `${lock}.reap-${
|
|
23222
|
+
const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
|
|
23120
23223
|
try {
|
|
23121
|
-
|
|
23224
|
+
fs12.renameSync(lock, tomb);
|
|
23122
23225
|
} catch {
|
|
23123
23226
|
return false;
|
|
23124
23227
|
}
|
|
23125
23228
|
if (lockToken(tomb) === observed) {
|
|
23126
|
-
|
|
23229
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
23127
23230
|
return true;
|
|
23128
23231
|
}
|
|
23129
23232
|
try {
|
|
23130
|
-
|
|
23233
|
+
fs12.renameSync(tomb, lock);
|
|
23131
23234
|
} catch {
|
|
23132
|
-
|
|
23235
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
23133
23236
|
}
|
|
23134
23237
|
return false;
|
|
23135
23238
|
}
|
|
23136
23239
|
};
|
|
23137
|
-
function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
23240
|
+
function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
|
|
23138
23241
|
const lock = `${file}.lock`;
|
|
23139
23242
|
const deadline = Date.now() + waitMs;
|
|
23140
23243
|
let token = null;
|
|
@@ -23156,14 +23259,14 @@ function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
|
23156
23259
|
ledgerLock.release(lock, token);
|
|
23157
23260
|
}
|
|
23158
23261
|
}
|
|
23159
|
-
var sha2562 = (text) =>
|
|
23262
|
+
var sha2562 = (text) => createHash9("sha256").update(text).digest("hex");
|
|
23160
23263
|
var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
|
|
23161
23264
|
${body}`);
|
|
23162
23265
|
var TAIL_BYTES = 64 * 1024;
|
|
23163
23266
|
var lastLine = (fd, size) => {
|
|
23164
23267
|
for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
|
|
23165
23268
|
const buffer = Buffer.alloc(span);
|
|
23166
|
-
|
|
23269
|
+
fs12.readSync(fd, buffer, 0, span, size - span);
|
|
23167
23270
|
const end = buffer.lastIndexOf(10);
|
|
23168
23271
|
if (end < 0) {
|
|
23169
23272
|
if (span === size)
|
|
@@ -23192,15 +23295,23 @@ var lastLine = (fd, size) => {
|
|
|
23192
23295
|
}
|
|
23193
23296
|
return null;
|
|
23194
23297
|
};
|
|
23298
|
+
var tighten = (fd) => {
|
|
23299
|
+
try {
|
|
23300
|
+
if ((fs12.fstatSync(fd).mode & 63) !== 0)
|
|
23301
|
+
fs12.fchmodSync(fd, 384);
|
|
23302
|
+
} catch {}
|
|
23303
|
+
};
|
|
23195
23304
|
function appendLine(file, build, options) {
|
|
23196
23305
|
const write = () => {
|
|
23197
|
-
const fd =
|
|
23306
|
+
const fd = fs12.openSync(file, "a+", 384);
|
|
23198
23307
|
try {
|
|
23199
|
-
|
|
23308
|
+
if (process.platform !== "win32")
|
|
23309
|
+
tighten(fd);
|
|
23310
|
+
const size = fs12.fstatSync(fd).size;
|
|
23200
23311
|
let fence = "";
|
|
23201
23312
|
if (size > 0) {
|
|
23202
23313
|
const last = Buffer.alloc(1);
|
|
23203
|
-
|
|
23314
|
+
fs12.readSync(fd, last, 0, 1, size - 1);
|
|
23204
23315
|
if (last[0] !== 10)
|
|
23205
23316
|
fence = `
|
|
23206
23317
|
`;
|
|
@@ -23209,17 +23320,17 @@ function appendLine(file, build, options) {
|
|
|
23209
23320
|
if (!line.ok)
|
|
23210
23321
|
return line;
|
|
23211
23322
|
const bytes = Buffer.from(fence + line.value);
|
|
23212
|
-
const written =
|
|
23323
|
+
const written = fs12.writeSync(fd, bytes);
|
|
23213
23324
|
if (written !== bytes.length)
|
|
23214
23325
|
return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
|
|
23215
23326
|
return { ok: true, value: undefined };
|
|
23216
23327
|
} finally {
|
|
23217
|
-
|
|
23328
|
+
fs12.closeSync(fd);
|
|
23218
23329
|
}
|
|
23219
23330
|
};
|
|
23220
23331
|
try {
|
|
23221
|
-
|
|
23222
|
-
if (!options.forceLock && !onNetworkFs(
|
|
23332
|
+
fs12.mkdirSync(path14.dirname(file), { recursive: true });
|
|
23333
|
+
if (!options.forceLock && !onNetworkFs(path14.dirname(file)))
|
|
23223
23334
|
return write();
|
|
23224
23335
|
const locked = withLedgerLock(file, write);
|
|
23225
23336
|
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
@@ -23287,8 +23398,10 @@ function readLedger(cwd, options = {}) {
|
|
|
23287
23398
|
if (!file.ok)
|
|
23288
23399
|
return file;
|
|
23289
23400
|
let text = "";
|
|
23401
|
+
let stat = null;
|
|
23290
23402
|
try {
|
|
23291
|
-
|
|
23403
|
+
stat = fs12.statSync(file.value);
|
|
23404
|
+
text = fs12.readFileSync(file.value, "utf8");
|
|
23292
23405
|
} catch (error) {
|
|
23293
23406
|
if (error.code === "ENOENT")
|
|
23294
23407
|
return {
|
|
@@ -23307,6 +23420,7 @@ function readLedger(cwd, options = {}) {
|
|
|
23307
23420
|
const integrity = { chained: 0, legacy: 0, unverified: [] };
|
|
23308
23421
|
const earlier = new Set([null]);
|
|
23309
23422
|
let chainStarted = false;
|
|
23423
|
+
const ids = new Set;
|
|
23310
23424
|
for (const raw of text.split(`
|
|
23311
23425
|
`)) {
|
|
23312
23426
|
const trimmed = raw.trim();
|
|
@@ -23340,6 +23454,10 @@ function readLedger(cwd, options = {}) {
|
|
|
23340
23454
|
reason = "unsigned";
|
|
23341
23455
|
else
|
|
23342
23456
|
rowIntegrity = "legacy";
|
|
23457
|
+
if (options.integrity && !reason && ids.has(parsed.id))
|
|
23458
|
+
reason = "duplicate_id";
|
|
23459
|
+
if (options.integrity)
|
|
23460
|
+
ids.add(parsed.id);
|
|
23343
23461
|
if (reason)
|
|
23344
23462
|
rowIntegrity = "unverified";
|
|
23345
23463
|
const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
|
|
@@ -23363,7 +23481,8 @@ function readLedger(cwd, options = {}) {
|
|
|
23363
23481
|
seq: rows.length + 1,
|
|
23364
23482
|
superseded: false,
|
|
23365
23483
|
supersedeIgnored: false,
|
|
23366
|
-
...rowIntegrity ? { integrity: rowIntegrity } : {}
|
|
23484
|
+
...rowIntegrity ? { integrity: rowIntegrity } : {},
|
|
23485
|
+
...reason ? { integrityReason: reason } : {}
|
|
23367
23486
|
};
|
|
23368
23487
|
rows.push(row);
|
|
23369
23488
|
if (!options.integrity)
|
|
@@ -23391,18 +23510,48 @@ function readLedger(cwd, options = {}) {
|
|
|
23391
23510
|
const byId = new Map;
|
|
23392
23511
|
for (const row of rows) {
|
|
23393
23512
|
const target = row.supersedes ? byId.get(row.supersedes) : undefined;
|
|
23394
|
-
if (target && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
23513
|
+
if (target && row.integrity !== "unverified" && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
23395
23514
|
target.superseded = true;
|
|
23396
23515
|
else if (row.supersedes)
|
|
23397
23516
|
row.supersedeIgnored = true;
|
|
23398
23517
|
if (!byId.has(row.id))
|
|
23399
23518
|
byId.set(row.id, row);
|
|
23400
23519
|
}
|
|
23520
|
+
if (options.integrity && stat && Buffer.byteLength(text) === stat.size)
|
|
23521
|
+
tamperCache.set(file.value, {
|
|
23522
|
+
size: stat.size,
|
|
23523
|
+
stamp: fileStamp(stat),
|
|
23524
|
+
breaks: integrity.unverified.filter((row) => TAMPER_REASONS.has(row.reason))
|
|
23525
|
+
});
|
|
23401
23526
|
return {
|
|
23402
23527
|
ok: true,
|
|
23403
23528
|
value: { path: file.value, rows, skipped, integrity: options.integrity ? integrity : null }
|
|
23404
23529
|
};
|
|
23405
23530
|
}
|
|
23531
|
+
var TAMPER_REASONS = new Set(["hash_mismatch", "broken_link"]);
|
|
23532
|
+
var tamperCache = new Map;
|
|
23533
|
+
var fileStamp = (stat) => `${stat.ino}:${stat.mtimeMs}:${stat.ctimeMs}`;
|
|
23534
|
+
function ledgerTampering(cwd) {
|
|
23535
|
+
const file = ledgerPath(cwd);
|
|
23536
|
+
if (!file.ok)
|
|
23537
|
+
return [];
|
|
23538
|
+
try {
|
|
23539
|
+
const stat = fs12.statSync(file.value);
|
|
23540
|
+
const cached = tamperCache.get(file.value);
|
|
23541
|
+
if (cached && cached.size === stat.size && cached.stamp === fileStamp(stat))
|
|
23542
|
+
return cached.breaks;
|
|
23543
|
+
} catch {
|
|
23544
|
+
return [];
|
|
23545
|
+
}
|
|
23546
|
+
const read = readLedger(cwd, { integrity: true });
|
|
23547
|
+
return read.ok ? (read.value.integrity?.unverified ?? []).filter((row) => TAMPER_REASONS.has(row.reason)) : [];
|
|
23548
|
+
}
|
|
23549
|
+
function tamperedUnblock(cwd) {
|
|
23550
|
+
const file = ledgerPath(cwd);
|
|
23551
|
+
const where = file.ok ? file.value : "the ledger file";
|
|
23552
|
+
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`;
|
|
23553
|
+
}
|
|
23554
|
+
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)}`;
|
|
23406
23555
|
function verdictBasis(verdict, current) {
|
|
23407
23556
|
if (verdict.head && verdict.head === current.head)
|
|
23408
23557
|
return "fresh";
|
|
@@ -23410,7 +23559,7 @@ function verdictBasis(verdict, current) {
|
|
|
23410
23559
|
return "carried";
|
|
23411
23560
|
return "stale";
|
|
23412
23561
|
}
|
|
23413
|
-
var isLiveVerdict = (row) => row.type === "verdict" && typeof row.result === "string" && !row.
|
|
23562
|
+
var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id);
|
|
23414
23563
|
function trailerSessions(cwd, base, head) {
|
|
23415
23564
|
if (!base || !head || !safeRef(base) || !safeRef(head))
|
|
23416
23565
|
return [];
|
|
@@ -23491,6 +23640,56 @@ var authoredBy = (authors, session) => {
|
|
|
23491
23640
|
return true;
|
|
23492
23641
|
return false;
|
|
23493
23642
|
};
|
|
23643
|
+
var TRUST_REASONS = new Set([
|
|
23644
|
+
"unsigned",
|
|
23645
|
+
"hash_mismatch",
|
|
23646
|
+
"broken_link",
|
|
23647
|
+
"duplicate_id",
|
|
23648
|
+
"unissued_session",
|
|
23649
|
+
"unbound_session"
|
|
23650
|
+
]);
|
|
23651
|
+
var SHELL_VOUCHED_HOSTS = new Set([
|
|
23652
|
+
"cli",
|
|
23653
|
+
"opencode",
|
|
23654
|
+
"pi",
|
|
23655
|
+
"codex_cli",
|
|
23656
|
+
"codex_desktop"
|
|
23657
|
+
]);
|
|
23658
|
+
function sessionTrust(cwd, row) {
|
|
23659
|
+
const session = row.actor.session;
|
|
23660
|
+
if (!session || row.self === true)
|
|
23661
|
+
return null;
|
|
23662
|
+
const source = typeof row.sessionSource === "string" ? row.sessionSource : "env";
|
|
23663
|
+
const host = row.actor.host;
|
|
23664
|
+
const issuedOnly = source === "flag" ? host !== "cli" : source === "env" && session.includes(":") && !SHELL_VOUCHED_HOSTS.has(host);
|
|
23665
|
+
if (!issuedOnly)
|
|
23666
|
+
return null;
|
|
23667
|
+
const binding = issuedBinding(cwd, session);
|
|
23668
|
+
if (binding === null)
|
|
23669
|
+
return "unissued_session";
|
|
23670
|
+
return binding === "token" && row.sessionBound !== true ? "unbound_session" : null;
|
|
23671
|
+
}
|
|
23672
|
+
function trustReasons(cwd, row) {
|
|
23673
|
+
if (row.integrity === "unverified")
|
|
23674
|
+
return [row.integrityReason ?? "unsigned"];
|
|
23675
|
+
if (row.integrity !== "verified")
|
|
23676
|
+
return ["unsigned"];
|
|
23677
|
+
const session = sessionTrust(cwd, row);
|
|
23678
|
+
return session ? [session] : [];
|
|
23679
|
+
}
|
|
23680
|
+
function withIntegrity(cwd, rows) {
|
|
23681
|
+
if (rows.every((row) => row.integrity !== undefined))
|
|
23682
|
+
return rows;
|
|
23683
|
+
const read = readLedger(cwd, { integrity: true });
|
|
23684
|
+
const bySeq = new Map(read.ok ? read.value.rows.map((row) => [row.seq, row]) : []);
|
|
23685
|
+
return rows.map((row) => {
|
|
23686
|
+
if (row.integrity !== undefined)
|
|
23687
|
+
return row;
|
|
23688
|
+
const fresh = bySeq.get(row.seq);
|
|
23689
|
+
return fresh && fresh.id === row.id && fresh.at === row.at ? fresh : row;
|
|
23690
|
+
});
|
|
23691
|
+
}
|
|
23692
|
+
var untrusted = (reasons) => reasons.some((reason) => TRUST_REASONS.has(reason));
|
|
23494
23693
|
var independenceReasons = (row, authors) => {
|
|
23495
23694
|
const reasons = [];
|
|
23496
23695
|
if (!row.actor.session)
|
|
@@ -23501,15 +23700,17 @@ var independenceReasons = (row, authors) => {
|
|
|
23501
23700
|
reasons.push("self");
|
|
23502
23701
|
return reasons;
|
|
23503
23702
|
};
|
|
23504
|
-
function effectiveOf(rows,
|
|
23703
|
+
function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
|
|
23505
23704
|
let effective = null;
|
|
23506
23705
|
for (const row of rows) {
|
|
23507
23706
|
if (!effective) {
|
|
23508
23707
|
effective = row;
|
|
23509
23708
|
continue;
|
|
23510
23709
|
}
|
|
23511
|
-
|
|
23512
|
-
|
|
23710
|
+
if (untrusted(reasonsOf(row)) && !untrusted(reasonsOf(effective)))
|
|
23711
|
+
continue;
|
|
23712
|
+
const rowIndependent = reasonsOf(row).length === 0;
|
|
23713
|
+
const effIndependent = reasonsOf(effective).length === 0;
|
|
23513
23714
|
if (effIndependent && !rowIndependent && isCurrent(effective))
|
|
23514
23715
|
continue;
|
|
23515
23716
|
if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session && sameCode(effective, row))
|
|
@@ -23523,7 +23724,7 @@ function restackCarries(rows, branch, from, to) {
|
|
|
23523
23724
|
return false;
|
|
23524
23725
|
const edges = new Map;
|
|
23525
23726
|
for (const row of rows)
|
|
23526
|
-
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
|
|
23727
|
+
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
|
|
23527
23728
|
edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
|
|
23528
23729
|
const seen = new Set([from]);
|
|
23529
23730
|
const queue = [from];
|
|
@@ -23539,7 +23740,8 @@ function restackCarries(rows, branch, from, to) {
|
|
|
23539
23740
|
}
|
|
23540
23741
|
return false;
|
|
23541
23742
|
}
|
|
23542
|
-
function checkVerdicts(cwd, branch,
|
|
23743
|
+
function checkVerdicts(cwd, branch, ledgerRows) {
|
|
23744
|
+
const rows = withIntegrity(cwd, ledgerRows);
|
|
23543
23745
|
const head = branchHead(cwd, branch);
|
|
23544
23746
|
const fallbackBase = defaultBase(cwd);
|
|
23545
23747
|
const scope = branchScope(cwd, branch);
|
|
@@ -23569,21 +23771,47 @@ function checkVerdicts(cwd, branch, rows) {
|
|
|
23569
23771
|
return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
|
|
23570
23772
|
};
|
|
23571
23773
|
const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
|
|
23774
|
+
const failureAuthorCache = new Map;
|
|
23775
|
+
const failureAuthors = (row) => {
|
|
23776
|
+
let set = failureAuthorCache.get(row.seq);
|
|
23777
|
+
if (!set) {
|
|
23778
|
+
set = authorSessions(cwd, rows.filter((other) => other.integrity === "verified" && other.seq < row.seq), branch, { base: null, head: null }, scope);
|
|
23779
|
+
failureAuthorCache.set(row.seq, set);
|
|
23780
|
+
}
|
|
23781
|
+
return set;
|
|
23782
|
+
};
|
|
23783
|
+
const reasonCache = new Map;
|
|
23784
|
+
const reasonsOf = (row) => {
|
|
23785
|
+
let reasons = reasonCache.get(row);
|
|
23786
|
+
if (!reasons) {
|
|
23787
|
+
const failing = FAILING.has(String(row.result));
|
|
23788
|
+
reasons = [
|
|
23789
|
+
...trustReasons(cwd, row),
|
|
23790
|
+
...independenceReasons(row, failing ? failureAuthors(row) : authors)
|
|
23791
|
+
];
|
|
23792
|
+
reasonCache.set(row, reasons);
|
|
23793
|
+
}
|
|
23794
|
+
return reasons;
|
|
23795
|
+
};
|
|
23796
|
+
const retired = new Set;
|
|
23797
|
+
for (const row of rows)
|
|
23798
|
+
if (row.type === "verdict" && row.supersedes && !row.supersedeIgnored && trustReasons(cwd, row).length === 0)
|
|
23799
|
+
retired.add(row.supersedes);
|
|
23572
23800
|
const byKind = new Map;
|
|
23573
23801
|
for (const row of rows)
|
|
23574
|
-
if (isLiveVerdict(row) && scope(row)) {
|
|
23802
|
+
if (isLiveVerdict(row, retired) && scope(row)) {
|
|
23575
23803
|
const kind = typeof row.kind === "string" ? row.kind : "review";
|
|
23576
23804
|
byKind.set(kind, [...byKind.get(kind) ?? [], row]);
|
|
23577
23805
|
}
|
|
23578
23806
|
const verdicts = [];
|
|
23579
23807
|
const warnings = [];
|
|
23580
23808
|
for (const [kind, list] of byKind) {
|
|
23581
|
-
const row = effectiveOf(list,
|
|
23809
|
+
const row = effectiveOf(list, reasonsOf, sameCode, (candidate) => basisOf(candidate) !== "stale");
|
|
23582
23810
|
if (!row)
|
|
23583
23811
|
continue;
|
|
23584
23812
|
const basis = basisOf(row);
|
|
23585
23813
|
const current = basis !== "stale";
|
|
23586
|
-
const independence =
|
|
23814
|
+
const independence = reasonsOf(row);
|
|
23587
23815
|
const reasons = [
|
|
23588
23816
|
...current ? [] : ["stale"],
|
|
23589
23817
|
...PASSING.has(String(row.result)) ? [] : ["not_passing"],
|
|
@@ -23600,23 +23828,31 @@ function checkVerdicts(cwd, branch, rows) {
|
|
|
23600
23828
|
});
|
|
23601
23829
|
if (reasons.length === 0) {
|
|
23602
23830
|
for (const later of list)
|
|
23603
|
-
if (later.seq > row.seq &&
|
|
23831
|
+
if (later.seq > row.seq && reasonsOf(later).length > 0 && !untrusted(reasonsOf(later)) && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
|
|
23604
23832
|
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`);
|
|
23605
23833
|
}
|
|
23606
23834
|
}
|
|
23607
23835
|
for (const row of rows)
|
|
23608
23836
|
if (row.integrity === "unverified" && scope(row))
|
|
23609
23837
|
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)`);
|
|
23838
|
+
const tampered = ledgerTampering(cwd);
|
|
23839
|
+
if (tampered.length) {
|
|
23840
|
+
warnings.push(tamperedWarning(cwd, tampered));
|
|
23841
|
+
for (const entry of verdicts) {
|
|
23842
|
+
entry.accepted = false;
|
|
23843
|
+
entry.reasons = [...entry.reasons, "ledger_tampered"];
|
|
23844
|
+
}
|
|
23845
|
+
}
|
|
23610
23846
|
verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
|
|
23611
23847
|
const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
|
|
23612
23848
|
const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
|
|
23613
23849
|
const newestAccepted = verdicts.findLast((entry) => entry.accepted && STRONG_RESULTS.has(String(entry.verdict.result))) ?? null;
|
|
23614
|
-
const accepted = newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
23850
|
+
const accepted = tampered.length ? { accepted: false, verdict: null, reasons: ["ledger_tampered"] } : newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
23615
23851
|
accepted: false,
|
|
23616
23852
|
verdict: failing?.verdict ?? null,
|
|
23617
23853
|
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
23618
23854
|
};
|
|
23619
|
-
const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
23855
|
+
const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
23620
23856
|
return {
|
|
23621
23857
|
branch,
|
|
23622
23858
|
head,
|
|
@@ -23779,7 +24015,7 @@ var productDecision = (task, ledger) => {
|
|
|
23779
24015
|
};
|
|
23780
24016
|
};
|
|
23781
24017
|
var planRecorded = (task, root) => {
|
|
23782
|
-
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(
|
|
24018
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path15.resolve(root, ref.path)));
|
|
23783
24019
|
if (cited)
|
|
23784
24020
|
return {
|
|
23785
24021
|
met: true,
|
|
@@ -23827,15 +24063,15 @@ function derivedRuleStatus(task, requirement, root, ledger) {
|
|
|
23827
24063
|
}
|
|
23828
24064
|
|
|
23829
24065
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
23830
|
-
var digestBytes = (value) =>
|
|
23831
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
23832
|
-
var relative = (root, candidate) =>
|
|
24066
|
+
var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
|
|
24067
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path16.sep}`);
|
|
24068
|
+
var relative = (root, candidate) => path16.relative(root, candidate).split(path16.sep).join("/") || ".";
|
|
23833
24069
|
var canonicalPath = (value) => {
|
|
23834
24070
|
if (typeof value !== "string" || !value)
|
|
23835
24071
|
return null;
|
|
23836
24072
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
23837
24073
|
return null;
|
|
23838
|
-
const normalized =
|
|
24074
|
+
const normalized = path16.posix.normalize(value);
|
|
23839
24075
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
23840
24076
|
return null;
|
|
23841
24077
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -23883,21 +24119,21 @@ var gitPaths = (root) => {
|
|
|
23883
24119
|
git: !/not a git repository/i.test(stderr)
|
|
23884
24120
|
};
|
|
23885
24121
|
}
|
|
23886
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
24122
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")) : [];
|
|
23887
24123
|
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
23888
24124
|
if (stagedDeleted.status !== 0) {
|
|
23889
24125
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
23890
24126
|
if (!/not a git repository/i.test(stderr))
|
|
23891
24127
|
return { paths, uncertain: true, git: true };
|
|
23892
24128
|
} else if (stagedDeleted.stdout) {
|
|
23893
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
24129
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")));
|
|
23894
24130
|
}
|
|
23895
24131
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
23896
24132
|
};
|
|
23897
24133
|
var walk = (root, directory, output) => {
|
|
23898
24134
|
let names;
|
|
23899
24135
|
try {
|
|
23900
|
-
names =
|
|
24136
|
+
names = fs13.readdirSync(directory);
|
|
23901
24137
|
} catch {
|
|
23902
24138
|
return true;
|
|
23903
24139
|
}
|
|
@@ -23905,10 +24141,10 @@ var walk = (root, directory, output) => {
|
|
|
23905
24141
|
for (const name of names) {
|
|
23906
24142
|
if (name === ".git" || name === ".workit")
|
|
23907
24143
|
continue;
|
|
23908
|
-
const target =
|
|
24144
|
+
const target = path16.join(directory, name);
|
|
23909
24145
|
let stat;
|
|
23910
24146
|
try {
|
|
23911
|
-
stat =
|
|
24147
|
+
stat = fs13.lstatSync(target);
|
|
23912
24148
|
} catch {
|
|
23913
24149
|
uncertain = true;
|
|
23914
24150
|
continue;
|
|
@@ -23923,14 +24159,14 @@ var walk = (root, directory, output) => {
|
|
|
23923
24159
|
var scopeRoots = (root, scope) => {
|
|
23924
24160
|
const roots = [];
|
|
23925
24161
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
23926
|
-
const target =
|
|
24162
|
+
const target = path16.resolve(root, item);
|
|
23927
24163
|
if (!inside(root, target))
|
|
23928
24164
|
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` }] });
|
|
23929
24165
|
let ancestor = target;
|
|
23930
|
-
while (!
|
|
23931
|
-
ancestor =
|
|
24166
|
+
while (!fs13.existsSync(ancestor) && ancestor !== root)
|
|
24167
|
+
ancestor = path16.dirname(ancestor);
|
|
23932
24168
|
try {
|
|
23933
|
-
if (!inside(root,
|
|
24169
|
+
if (!inside(root, fs13.realpathSync(ancestor)))
|
|
23934
24170
|
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` }] });
|
|
23935
24171
|
} catch {
|
|
23936
24172
|
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
@@ -23942,7 +24178,7 @@ var scopeRoots = (root, scope) => {
|
|
|
23942
24178
|
function captureCandidate(root, scope, environment = []) {
|
|
23943
24179
|
let checkout;
|
|
23944
24180
|
try {
|
|
23945
|
-
checkout =
|
|
24181
|
+
checkout = fs13.realpathSync(root);
|
|
23946
24182
|
} catch {
|
|
23947
24183
|
return failure2("invalid_input", "candidate root does not exist");
|
|
23948
24184
|
}
|
|
@@ -23960,11 +24196,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
23960
24196
|
if (item === ".git" || item === ".workit")
|
|
23961
24197
|
continue;
|
|
23962
24198
|
names.add(item);
|
|
23963
|
-
if (!
|
|
24199
|
+
if (!fs13.existsSync(target))
|
|
23964
24200
|
continue;
|
|
23965
24201
|
let stat;
|
|
23966
24202
|
try {
|
|
23967
|
-
stat =
|
|
24203
|
+
stat = fs13.lstatSync(target);
|
|
23968
24204
|
} catch {
|
|
23969
24205
|
uncertain = true;
|
|
23970
24206
|
continue;
|
|
@@ -23980,17 +24216,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
23980
24216
|
for (const item of [...names].toSorted()) {
|
|
23981
24217
|
if (!scopeMatches(item, normalizedScope))
|
|
23982
24218
|
continue;
|
|
23983
|
-
const target =
|
|
24219
|
+
const target = path16.join(checkout, item);
|
|
23984
24220
|
try {
|
|
23985
|
-
const stat =
|
|
24221
|
+
const stat = fs13.lstatSync(target);
|
|
23986
24222
|
if (stat.isSymbolicLink()) {
|
|
23987
|
-
const link =
|
|
24223
|
+
const link = fs13.readlinkSync(target);
|
|
23988
24224
|
files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
|
|
23989
24225
|
} else if (stat.isFile()) {
|
|
23990
24226
|
files.push({
|
|
23991
24227
|
path: item,
|
|
23992
24228
|
kind: "file",
|
|
23993
|
-
digest: digestBytes(
|
|
24229
|
+
digest: digestBytes(fs13.readFileSync(target)),
|
|
23994
24230
|
executable: (stat.mode & 73) !== 0
|
|
23995
24231
|
});
|
|
23996
24232
|
} else if (!stat.isDirectory()) {
|
|
@@ -24155,7 +24391,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
|
|
|
24155
24391
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
24156
24392
|
let root;
|
|
24157
24393
|
try {
|
|
24158
|
-
root =
|
|
24394
|
+
root = fs13.realpathSync(checkoutRoot);
|
|
24159
24395
|
} catch {
|
|
24160
24396
|
return failure2("permission_denied", "checkout root is unavailable");
|
|
24161
24397
|
}
|
|
@@ -24164,23 +24400,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
24164
24400
|
continue;
|
|
24165
24401
|
if (!reference.digest)
|
|
24166
24402
|
return failure2("invalid_input", "document references require a byte digest");
|
|
24167
|
-
const target =
|
|
24168
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
24403
|
+
const target = path16.resolve(root, reference.path);
|
|
24404
|
+
if (target !== root && !target.startsWith(`${root}${path16.sep}`))
|
|
24169
24405
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
24170
24406
|
fields: [
|
|
24171
24407
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
24172
24408
|
]
|
|
24173
24409
|
});
|
|
24174
24410
|
try {
|
|
24175
|
-
const below =
|
|
24411
|
+
const below = path16.relative(root, target);
|
|
24176
24412
|
let current = root;
|
|
24177
|
-
for (const segment of below.split(
|
|
24413
|
+
for (const segment of below.split(path16.sep)) {
|
|
24178
24414
|
if (!segment)
|
|
24179
24415
|
continue;
|
|
24180
|
-
current =
|
|
24181
|
-
const stat =
|
|
24182
|
-
const real =
|
|
24183
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
24416
|
+
current = path16.join(current, segment);
|
|
24417
|
+
const stat = fs13.lstatSync(current);
|
|
24418
|
+
const real = fs13.realpathSync(current);
|
|
24419
|
+
if (real !== root && !real.startsWith(`${root}${path16.sep}`))
|
|
24184
24420
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
24185
24421
|
fields: [
|
|
24186
24422
|
{
|
|
@@ -24196,7 +24432,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
24196
24432
|
if (current !== target && !stat.isDirectory())
|
|
24197
24433
|
return failure2("invalid_input", "document reference ancestor is not a directory");
|
|
24198
24434
|
}
|
|
24199
|
-
if (digestBytes(
|
|
24435
|
+
if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
|
|
24200
24436
|
return failure2("permission_denied", "decision document bytes have changed");
|
|
24201
24437
|
} catch {
|
|
24202
24438
|
return failure2("invalid_input", "decision document is unavailable");
|
|
@@ -25054,7 +25290,7 @@ var judgeInputSummary = (raw) => {
|
|
|
25054
25290
|
|
|
25055
25291
|
// packages/workit-core/src/autonomy.ts
|
|
25056
25292
|
import os4 from "node:os";
|
|
25057
|
-
import
|
|
25293
|
+
import path17 from "node:path";
|
|
25058
25294
|
var accountHome = () => {
|
|
25059
25295
|
try {
|
|
25060
25296
|
return os4.userInfo().homedir;
|
|
@@ -25063,10 +25299,10 @@ var accountHome = () => {
|
|
|
25063
25299
|
}
|
|
25064
25300
|
};
|
|
25065
25301
|
var grantsHome = { resolve: accountHome };
|
|
25066
|
-
var grantsDir = () =>
|
|
25302
|
+
var grantsDir = () => path17.join(grantsHome.resolve(), ".config", "workit");
|
|
25067
25303
|
var grantsOverride = () => {
|
|
25068
|
-
const configured =
|
|
25069
|
-
return configured ===
|
|
25304
|
+
const configured = path17.resolve(resolveConfigDir());
|
|
25305
|
+
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`;
|
|
25070
25306
|
};
|
|
25071
25307
|
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
25072
25308
|
var DEFAULT_GRANTS = Object.freeze({
|
|
@@ -26838,7 +27074,7 @@ class WorkitCore {
|
|
|
26838
27074
|
// packages/workit-core/src/core/repo-context.ts
|
|
26839
27075
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
26840
27076
|
import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
|
|
26841
|
-
import
|
|
27077
|
+
import path18 from "node:path";
|
|
26842
27078
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
26843
27079
|
try {
|
|
26844
27080
|
gitRevisionParts(value);
|
|
@@ -26972,7 +27208,7 @@ var findPrTemplate = (cwd) => {
|
|
|
26972
27208
|
]) {
|
|
26973
27209
|
const parts = rel.split("/");
|
|
26974
27210
|
const base = parts[parts.length - 1];
|
|
26975
|
-
const dir = parts.length > 1 ?
|
|
27211
|
+
const dir = parts.length > 1 ? path18.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
26976
27212
|
const actual = probeCaseInsensitive(dir, base);
|
|
26977
27213
|
if (actual)
|
|
26978
27214
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -27003,7 +27239,7 @@ var diffStatForRange = (cwd, range) => {
|
|
|
27003
27239
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
27004
27240
|
};
|
|
27005
27241
|
var packageScripts = (cwd, keys) => {
|
|
27006
|
-
const pkgPath =
|
|
27242
|
+
const pkgPath = path18.join(cwd, "package.json");
|
|
27007
27243
|
if (!existsSync6(pkgPath))
|
|
27008
27244
|
return [];
|
|
27009
27245
|
try {
|
|
@@ -27029,9 +27265,9 @@ var documentationFiles = (cwd) => {
|
|
|
27029
27265
|
if (entry.isDirectory()) {
|
|
27030
27266
|
if (excluded.has(entry.name))
|
|
27031
27267
|
continue;
|
|
27032
|
-
walk(
|
|
27268
|
+
walk(path18.join(dir, entry.name), depth + 1);
|
|
27033
27269
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
27034
|
-
matches.push("./" +
|
|
27270
|
+
matches.push("./" + path18.relative(cwd, path18.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
27035
27271
|
}
|
|
27036
27272
|
}
|
|
27037
27273
|
};
|
|
@@ -27047,7 +27283,7 @@ var readTrimmed2 = (file, maxLines) => {
|
|
|
27047
27283
|
`);
|
|
27048
27284
|
};
|
|
27049
27285
|
function prReadyContext(root, range) {
|
|
27050
|
-
const cwd =
|
|
27286
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
27051
27287
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
27052
27288
|
return invalidRange(cwd);
|
|
27053
27289
|
let stdout = "";
|
|
@@ -27105,7 +27341,7 @@ ${status.stderr}` : "");
|
|
|
27105
27341
|
stdout += `template_path: ${template}
|
|
27106
27342
|
|
|
27107
27343
|
`;
|
|
27108
|
-
stdout += readTrimmed2(
|
|
27344
|
+
stdout += readTrimmed2(path18.join(cwd, template), 220);
|
|
27109
27345
|
} else {
|
|
27110
27346
|
stdout += `template_path: none
|
|
27111
27347
|
|
|
@@ -27115,7 +27351,7 @@ ${status.stderr}` : "");
|
|
|
27115
27351
|
stdout += `
|
|
27116
27352
|
`;
|
|
27117
27353
|
stdout += printSection("Recent Validation Signals");
|
|
27118
|
-
if (existsSync6(
|
|
27354
|
+
if (existsSync6(path18.join(cwd, "package.json"))) {
|
|
27119
27355
|
stdout += `package.json detected. Common scripts:
|
|
27120
27356
|
`;
|
|
27121
27357
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -27124,7 +27360,7 @@ ${status.stderr}` : "");
|
|
|
27124
27360
|
`) + `
|
|
27125
27361
|
`;
|
|
27126
27362
|
}
|
|
27127
|
-
if (existsSync6(
|
|
27363
|
+
if (existsSync6(path18.join(cwd, "Cargo.toml")) || existsSync6(path18.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
27128
27364
|
stdout += `Rust project detected.
|
|
27129
27365
|
`;
|
|
27130
27366
|
}
|
|
@@ -27157,7 +27393,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
27157
27393
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
27158
27394
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
27159
27395
|
function changelogContext(root, range) {
|
|
27160
|
-
const cwd =
|
|
27396
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
27161
27397
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
27162
27398
|
return invalidRange(cwd);
|
|
27163
27399
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -27175,7 +27411,7 @@ function changelogContext(root, range) {
|
|
|
27175
27411
|
stdout += CHANGELOG_RULES + `
|
|
27176
27412
|
`;
|
|
27177
27413
|
stdout += printSection("Existing CHANGELOG.md");
|
|
27178
|
-
const changelogPath =
|
|
27414
|
+
const changelogPath = path18.join(cwd, "CHANGELOG.md");
|
|
27179
27415
|
if (existsSync6(changelogPath)) {
|
|
27180
27416
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
27181
27417
|
`;
|
|
@@ -27195,7 +27431,7 @@ function changelogContext(root, range) {
|
|
|
27195
27431
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
27196
27432
|
}
|
|
27197
27433
|
function docsRefreshContext(root, range) {
|
|
27198
|
-
const cwd =
|
|
27434
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
27199
27435
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
27200
27436
|
return invalidRange(cwd);
|
|
27201
27437
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -27217,7 +27453,7 @@ function docsRefreshContext(root, range) {
|
|
|
27217
27453
|
`) + `
|
|
27218
27454
|
`;
|
|
27219
27455
|
stdout += printSection("README Preview");
|
|
27220
|
-
const readme =
|
|
27456
|
+
const readme = path18.join(cwd, "README.md");
|
|
27221
27457
|
if (existsSync6(readme)) {
|
|
27222
27458
|
stdout += readTrimmed2(readme, 220) + `
|
|
27223
27459
|
`;
|
|
@@ -27226,7 +27462,7 @@ function docsRefreshContext(root, range) {
|
|
|
27226
27462
|
`;
|
|
27227
27463
|
}
|
|
27228
27464
|
stdout += printSection("Package Scripts");
|
|
27229
|
-
const pkgPath =
|
|
27465
|
+
const pkgPath = path18.join(cwd, "package.json");
|
|
27230
27466
|
if (existsSync6(pkgPath)) {
|
|
27231
27467
|
try {
|
|
27232
27468
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
@@ -27240,7 +27476,7 @@ function docsRefreshContext(root, range) {
|
|
|
27240
27476
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
27241
27477
|
}
|
|
27242
27478
|
function releaseNotesContext(root, rangeOrTag) {
|
|
27243
|
-
const cwd =
|
|
27479
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
27244
27480
|
if (!rangeOrTag) {
|
|
27245
27481
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
27246
27482
|
`, exitCode: 1, cwd };
|
|
@@ -27278,7 +27514,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
27278
27514
|
`;
|
|
27279
27515
|
stdout += printSection("Existing Release Files");
|
|
27280
27516
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
27281
|
-
if (existsSync6(
|
|
27517
|
+
if (existsSync6(path18.join(cwd, rel)))
|
|
27282
27518
|
stdout += rel + `
|
|
27283
27519
|
`;
|
|
27284
27520
|
}
|
|
@@ -27309,8 +27545,8 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
27309
27545
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
27310
27546
|
|
|
27311
27547
|
// packages/workit-core/src/core/pr-create.ts
|
|
27312
|
-
import
|
|
27313
|
-
import
|
|
27548
|
+
import fs14 from "node:fs";
|
|
27549
|
+
import path19 from "node:path";
|
|
27314
27550
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
27315
27551
|
// packages/workit-core/src/forge/pr-body.ts
|
|
27316
27552
|
function parseGhIssue(value) {
|
|
@@ -27321,13 +27557,13 @@ function parseGhIssue(value) {
|
|
|
27321
27557
|
// packages/workit-core/src/core/pr-create.ts
|
|
27322
27558
|
function whichOnPath(tool) {
|
|
27323
27559
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
27324
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
27560
|
+
for (const dir of (process.env.PATH ?? "").split(path19.delimiter)) {
|
|
27325
27561
|
if (!dir)
|
|
27326
27562
|
continue;
|
|
27327
27563
|
for (const name of names) {
|
|
27328
|
-
const candidate =
|
|
27564
|
+
const candidate = path19.join(dir, name);
|
|
27329
27565
|
try {
|
|
27330
|
-
|
|
27566
|
+
fs14.accessSync(candidate, fs14.constants.X_OK);
|
|
27331
27567
|
return candidate;
|
|
27332
27568
|
} catch {}
|
|
27333
27569
|
}
|
|
@@ -27445,8 +27681,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
|
27445
27681
|
};
|
|
27446
27682
|
|
|
27447
27683
|
// packages/workit-core/src/core/youtrack.ts
|
|
27448
|
-
import
|
|
27449
|
-
import
|
|
27684
|
+
import fs15 from "node:fs";
|
|
27685
|
+
import path20 from "node:path";
|
|
27450
27686
|
|
|
27451
27687
|
// packages/workit-core/src/core/templates.ts
|
|
27452
27688
|
var repoRoot2 = assetRoot();
|
|
@@ -27459,22 +27695,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
27459
27695
|
// packages/workit-core/src/core/youtrack.ts
|
|
27460
27696
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
27461
27697
|
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
27462
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
27463
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
27698
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path20.join(configDir(), "youtrack.json");
|
|
27699
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path20.join(resolveConfigDir(), "youtrack.json");
|
|
27464
27700
|
var youTrackTokenModeOk = (p) => {
|
|
27465
27701
|
if (process.platform === "win32")
|
|
27466
27702
|
return true;
|
|
27467
|
-
const mode =
|
|
27703
|
+
const mode = fs15.statSync(p).mode & 511;
|
|
27468
27704
|
return mode === 384;
|
|
27469
27705
|
};
|
|
27470
27706
|
function readYouTrackConfig(required) {
|
|
27471
27707
|
const cfgPath = youTrackConfigPath();
|
|
27472
|
-
if (!
|
|
27708
|
+
if (!fs15.existsSync(cfgPath)) {
|
|
27473
27709
|
return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
|
|
27474
27710
|
}
|
|
27475
27711
|
let parsed;
|
|
27476
27712
|
try {
|
|
27477
|
-
parsed = JSON.parse(
|
|
27713
|
+
parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
|
|
27478
27714
|
} catch {
|
|
27479
27715
|
return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
|
|
27480
27716
|
}
|
|
@@ -27485,11 +27721,11 @@ function readYouTrackConfig(required) {
|
|
|
27485
27721
|
}
|
|
27486
27722
|
var readYouTrackContextConfig = () => {
|
|
27487
27723
|
const cfgPath = youTrackReadOnlyConfigPath();
|
|
27488
|
-
if (!
|
|
27724
|
+
if (!fs15.existsSync(cfgPath))
|
|
27489
27725
|
return { data: {} };
|
|
27490
27726
|
let parsed;
|
|
27491
27727
|
try {
|
|
27492
|
-
parsed = JSON.parse(
|
|
27728
|
+
parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
|
|
27493
27729
|
} catch {
|
|
27494
27730
|
return { error: "YouTrack configuration is unavailable" };
|
|
27495
27731
|
}
|
|
@@ -27528,12 +27764,12 @@ var youTrackToken = () => {
|
|
|
27528
27764
|
if ("error" in loaded)
|
|
27529
27765
|
return loaded;
|
|
27530
27766
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
27531
|
-
const tokenPath = tokenFile ?
|
|
27532
|
-
if (!tokenPath || !
|
|
27767
|
+
const tokenPath = tokenFile ? path20.isAbsolute(tokenFile) ? path20.resolve(tokenFile) : path20.resolve(process.cwd(), tokenFile) : "";
|
|
27768
|
+
if (!tokenPath || !fs15.existsSync(tokenPath))
|
|
27533
27769
|
return { error: "missing youtrack.token" };
|
|
27534
27770
|
if (!youTrackTokenModeOk(tokenPath))
|
|
27535
27771
|
return { error: "youtrack.token mode must be 0600" };
|
|
27536
|
-
const token =
|
|
27772
|
+
const token = fs15.readFileSync(tokenPath, "utf8").trim();
|
|
27537
27773
|
if (!token)
|
|
27538
27774
|
return { error: "empty token file" };
|
|
27539
27775
|
if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
|
|
@@ -27598,9 +27834,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
|
|
|
27598
27834
|
} catch {
|
|
27599
27835
|
continue;
|
|
27600
27836
|
}
|
|
27601
|
-
if (!
|
|
27837
|
+
if (!fs15.existsSync(full))
|
|
27602
27838
|
continue;
|
|
27603
|
-
const text =
|
|
27839
|
+
const text = fs15.readFileSync(full, "utf8");
|
|
27604
27840
|
const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
|
|
27605
27841
|
if (m)
|
|
27606
27842
|
return m[1];
|
|
@@ -27870,15 +28106,15 @@ function resolveGitRevision(root, value) {
|
|
|
27870
28106
|
}
|
|
27871
28107
|
function resolveInside(root, candidate) {
|
|
27872
28108
|
const base = realpathSync5(root);
|
|
27873
|
-
const target =
|
|
27874
|
-
if (target !== base && !target.startsWith(base +
|
|
28109
|
+
const target = path21.resolve(base, candidate);
|
|
28110
|
+
if (target !== base && !target.startsWith(base + path21.sep)) {
|
|
27875
28111
|
throw new Error("path must stay inside repository root");
|
|
27876
28112
|
}
|
|
27877
28113
|
let ancestor = target;
|
|
27878
28114
|
while (!existsSync7(ancestor))
|
|
27879
|
-
ancestor =
|
|
28115
|
+
ancestor = path21.dirname(ancestor);
|
|
27880
28116
|
const canonicalAncestor = realpathSync5(ancestor);
|
|
27881
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
28117
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path21.sep)) {
|
|
27882
28118
|
throw new Error("path must stay inside repository root");
|
|
27883
28119
|
}
|
|
27884
28120
|
return target;
|
|
@@ -27936,13 +28172,13 @@ var rootVariants = (workspaceRoot) => {
|
|
|
27936
28172
|
if (!workspaceRoot)
|
|
27937
28173
|
return [];
|
|
27938
28174
|
const roots = new Set;
|
|
27939
|
-
for (const candidate of [workspaceRoot,
|
|
27940
|
-
if (
|
|
28175
|
+
for (const candidate of [workspaceRoot, path22.resolve(workspaceRoot)]) {
|
|
28176
|
+
if (path22.isAbsolute(candidate) && candidate !== path22.parse(candidate).root)
|
|
27941
28177
|
roots.add(candidate);
|
|
27942
28178
|
}
|
|
27943
28179
|
try {
|
|
27944
28180
|
const canonical = realpathSync6(workspaceRoot);
|
|
27945
|
-
if (canonical !==
|
|
28181
|
+
if (canonical !== path22.parse(canonical).root)
|
|
27946
28182
|
roots.add(canonical);
|
|
27947
28183
|
} catch {}
|
|
27948
28184
|
return [...roots].toSorted((left, right) => right.length - left.length);
|
|
@@ -28024,11 +28260,11 @@ var thrownResult = (tool, error, workspaceRoot) => {
|
|
|
28024
28260
|
};
|
|
28025
28261
|
function createMcpServer(host, contextProvider) {
|
|
28026
28262
|
assertMcpHost(host);
|
|
28027
|
-
const here =
|
|
28263
|
+
const here = path22.dirname(fileURLToPath2(import.meta.url));
|
|
28028
28264
|
const sourceMarker = markSourcesLoaded([
|
|
28029
|
-
|
|
28030
|
-
|
|
28031
|
-
|
|
28265
|
+
path22.join(here, "server.ts"),
|
|
28266
|
+
path22.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
|
|
28267
|
+
path22.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
|
|
28032
28268
|
].filter((file) => existsSync8(file)));
|
|
28033
28269
|
let staleWarned = false;
|
|
28034
28270
|
const server = new Server({ name: "workit", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
|
|
@@ -28208,8 +28444,8 @@ var main = async () => {
|
|
|
28208
28444
|
process.exitCode = 2;
|
|
28209
28445
|
}
|
|
28210
28446
|
};
|
|
28211
|
-
var entryPath = process.argv[1] ?
|
|
28212
|
-
if (entryPath ===
|
|
28447
|
+
var entryPath = process.argv[1] ? path23.resolve(process.argv[1]) : "";
|
|
28448
|
+
if (entryPath === path23.resolve(fileURLToPath3(import.meta.url)))
|
|
28213
28449
|
await main();
|
|
28214
28450
|
// packages/workit-core/src/hooks/descriptor.ts
|
|
28215
28451
|
var support = (descriptor, axis) => {
|
|
@@ -28239,10 +28475,44 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
28239
28475
|
var signals = new Map;
|
|
28240
28476
|
var cache = new Map;
|
|
28241
28477
|
var TURN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
28478
|
+
// packages/workit-core/src/core/live-session.ts
|
|
28479
|
+
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
28480
|
+
var LIVE_SESSION_TTL_MS = 15 * 60000;
|
|
28481
|
+
var liveProbe = {
|
|
28482
|
+
platform: process.platform,
|
|
28483
|
+
run: (args) => spawnSync15("ps", args, { encoding: "utf8", timeout: 2000 }).stdout ?? "",
|
|
28484
|
+
start: (pid) => processStartOf(pid)
|
|
28485
|
+
};
|
|
28486
|
+
var PIDS = new Map;
|
|
28487
|
+
|
|
28242
28488
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
28243
28489
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
28244
28490
|
var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
28245
28491
|
var cache2 = new Map;
|
|
28492
|
+
var MUTATING_GIT = new Set([
|
|
28493
|
+
"add",
|
|
28494
|
+
"am",
|
|
28495
|
+
"apply",
|
|
28496
|
+
"checkout",
|
|
28497
|
+
"cherry-pick",
|
|
28498
|
+
"clean",
|
|
28499
|
+
"commit",
|
|
28500
|
+
"merge",
|
|
28501
|
+
"mv",
|
|
28502
|
+
"pull",
|
|
28503
|
+
"rebase",
|
|
28504
|
+
"reset",
|
|
28505
|
+
"restore",
|
|
28506
|
+
"revert",
|
|
28507
|
+
"rm",
|
|
28508
|
+
"stash",
|
|
28509
|
+
"switch"
|
|
28510
|
+
]);
|
|
28511
|
+
var DRY_N = new Set(["add", "clean", "rm", "mv"]);
|
|
28512
|
+
var MUTATING_WORKIT_GIT = new Set(["commit", "branch"]);
|
|
28513
|
+
var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
28514
|
+
var PUSHD2 = new Set(["pushd", "push-location"]);
|
|
28515
|
+
var POPD2 = new Set(["popd", "pop-location"]);
|
|
28246
28516
|
// packages/workit-core/src/core/skill-manifests.ts
|
|
28247
28517
|
if (false) {}
|
|
28248
28518
|
|
|
@@ -28437,7 +28707,7 @@ var cursorContextProvider = (workspaceRoot = process.env.WORKFLOW_WORKSPACE_ROOT
|
|
|
28437
28707
|
var stampCursorSessionStart = (stateDir = resolveStateDir()) => {
|
|
28438
28708
|
try {
|
|
28439
28709
|
mkdirSync3(stateDir, { recursive: true });
|
|
28440
|
-
writeFileSync3(
|
|
28710
|
+
writeFileSync3(path24.join(stateDir, "cursor-session-last-start"), `${JSON.stringify({ at: new Date().toISOString() })}
|
|
28441
28711
|
`);
|
|
28442
28712
|
} catch {}
|
|
28443
28713
|
};
|