@brainervirus/workit-cursor 8.12.3 → 8.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor-plugin/plugin.json +1 -1
- package/dist/cursor-session-start.js +1855 -1661
- package/dist/mcp-server.js +592 -369
- package/dist/workit-hook.js +1241 -1037
- package/package.json +4 -4
- package/skills/workit-fanout/references/orchestration.md +4 -3
- package/skills/workit-review/SKILL.md +1 -1
- package/skills/workit-ship/SKILL.md +20 -20
- package/skills/workit-ship/references/release.md +26 -0
package/dist/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"],
|
|
@@ -21933,7 +22023,7 @@ var workspacesFileSchema = object2({ workspaces: array(workspaceConfigSchema).op
|
|
|
21933
22023
|
names.add(workspace.name);
|
|
21934
22024
|
}
|
|
21935
22025
|
});
|
|
21936
|
-
var workspacesRevision = (contents) =>
|
|
22026
|
+
var workspacesRevision = (contents) => createHash7("sha256").update(contents ?? "<missing>").digest("hex");
|
|
21937
22027
|
var validateWorkspacesDocument = (parsed, file) => {
|
|
21938
22028
|
if (!isConfigObject(parsed)) {
|
|
21939
22029
|
return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
|
|
@@ -21971,7 +22061,7 @@ var validateWorkspacesDocument = (parsed, file) => {
|
|
|
21971
22061
|
};
|
|
21972
22062
|
};
|
|
21973
22063
|
var readWorkspacesResult = (dir = configDir()) => {
|
|
21974
|
-
const file =
|
|
22064
|
+
const file = path12.join(dir, "workspaces.json");
|
|
21975
22065
|
let raw;
|
|
21976
22066
|
try {
|
|
21977
22067
|
raw = readFileSync3(file, "utf8");
|
|
@@ -22082,9 +22172,9 @@ var mainCheckoutOf = (cwd) => {
|
|
|
22082
22172
|
let main = null;
|
|
22083
22173
|
const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
|
|
22084
22174
|
const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
|
|
22085
|
-
if (gitDir && common &&
|
|
22175
|
+
if (gitDir && common && path12.resolve(gitDir) !== path12.resolve(common)) {
|
|
22086
22176
|
const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
|
|
22087
|
-
main = listed?.trim() || (
|
|
22177
|
+
main = listed?.trim() || (path12.basename(common) === ".git" ? path12.dirname(common) : common);
|
|
22088
22178
|
}
|
|
22089
22179
|
mainCheckouts.set(cwd, main);
|
|
22090
22180
|
return main;
|
|
@@ -22175,7 +22265,7 @@ var runtimeWorkspaceIndexSchema = object2({
|
|
|
22175
22265
|
workspaces: array(object2({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
22176
22266
|
}).passthrough();
|
|
22177
22267
|
var matchRuntimeWorkspace = (cwd, dir = configDir()) => {
|
|
22178
|
-
const file =
|
|
22268
|
+
const file = path12.join(dir, "workspaces.json");
|
|
22179
22269
|
let raw;
|
|
22180
22270
|
try {
|
|
22181
22271
|
raw = readFileSync3(file, "utf8");
|
|
@@ -22250,7 +22340,7 @@ function workspaceDefaultTarget(cwd, globalDefault) {
|
|
|
22250
22340
|
}
|
|
22251
22341
|
|
|
22252
22342
|
// packages/workit-core/src/core/release-tracks.ts
|
|
22253
|
-
import { createHash as
|
|
22343
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
22254
22344
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
22255
22345
|
var KNOWN_FIELDS = new Set([
|
|
22256
22346
|
"strategy",
|
|
@@ -22620,7 +22710,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
22620
22710
|
]) ?? "",
|
|
22621
22711
|
git3(cwd, ["config", "--get", recordedBaseKey(branch)]) ?? ""
|
|
22622
22712
|
] : [];
|
|
22623
|
-
key =
|
|
22713
|
+
key = createHash8("sha256").update(JSON.stringify({ cwd, input, refs, head, own })).digest("hex");
|
|
22624
22714
|
const hit = memo2.get(key);
|
|
22625
22715
|
if (hit)
|
|
22626
22716
|
return hit;
|
|
@@ -22635,7 +22725,7 @@ function resolveTrackFor(cwd, options) {
|
|
|
22635
22725
|
}
|
|
22636
22726
|
|
|
22637
22727
|
// packages/workit-core/src/core/vcs-config.ts
|
|
22638
|
-
var workspacesPath = () =>
|
|
22728
|
+
var workspacesPath = () => path13.join(configDir(), "workspaces.json");
|
|
22639
22729
|
var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
22640
22730
|
var remoteProvider = (cwd) => {
|
|
22641
22731
|
const r = spawnSync8("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
|
|
@@ -22725,7 +22815,7 @@ var readVcsConfig = () => {
|
|
|
22725
22815
|
const file = vcsConfigPath();
|
|
22726
22816
|
let raw;
|
|
22727
22817
|
try {
|
|
22728
|
-
raw =
|
|
22818
|
+
raw = fs11.readFileSync(file, "utf8");
|
|
22729
22819
|
} catch {
|
|
22730
22820
|
return { status: "missing", path: file, config: {} };
|
|
22731
22821
|
}
|
|
@@ -22810,7 +22900,7 @@ function vcsConfig(mode, cwd, options = {}) {
|
|
|
22810
22900
|
const prov = cfg[provider] ?? {};
|
|
22811
22901
|
const out = {
|
|
22812
22902
|
ok: true,
|
|
22813
|
-
configPath:
|
|
22903
|
+
configPath: path13.resolve(cfgPath),
|
|
22814
22904
|
provider,
|
|
22815
22905
|
defaultTargetBranch: workspaceDefault,
|
|
22816
22906
|
pr: cfg.pr ?? {},
|
|
@@ -23021,7 +23111,7 @@ function diffHash(cwd, base, head) {
|
|
|
23021
23111
|
], GIT_TIMEOUTS.worktree);
|
|
23022
23112
|
if (!diff.ok || !diff.stdout)
|
|
23023
23113
|
return null;
|
|
23024
|
-
const hash =
|
|
23114
|
+
const hash = createHash9("sha256");
|
|
23025
23115
|
for (const line of diff.stdout.split(`
|
|
23026
23116
|
`)) {
|
|
23027
23117
|
if (line.startsWith("index "))
|
|
@@ -23033,16 +23123,16 @@ function diffHash(cwd, base, head) {
|
|
|
23033
23123
|
return `sha256:${hash.digest("hex")}`;
|
|
23034
23124
|
}
|
|
23035
23125
|
function storeRoot(cwd) {
|
|
23036
|
-
const location = resolveStore(
|
|
23126
|
+
const location = resolveStore(path14.resolve(cwd));
|
|
23037
23127
|
if (location instanceof Error)
|
|
23038
23128
|
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
23129
|
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
23040
23130
|
}
|
|
23041
23131
|
function ledgerPath(cwd) {
|
|
23042
23132
|
const root = storeRoot(cwd);
|
|
23043
|
-
return root.ok ? { ok: true, value:
|
|
23133
|
+
return root.ok ? { ok: true, value: path14.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
23044
23134
|
}
|
|
23045
|
-
var newId2 = () => `${Date.now().toString(36)}-${
|
|
23135
|
+
var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
|
|
23046
23136
|
var NETWORK_FS_TYPES = new Set([
|
|
23047
23137
|
26985,
|
|
23048
23138
|
20859,
|
|
@@ -23060,7 +23150,7 @@ var onNetworkFs = (dir) => {
|
|
|
23060
23150
|
if (process.platform !== "linux")
|
|
23061
23151
|
return false;
|
|
23062
23152
|
try {
|
|
23063
|
-
return isNetworkFsType(
|
|
23153
|
+
return isNetworkFsType(fs12.statfsSync(dir).type);
|
|
23064
23154
|
} catch {
|
|
23065
23155
|
return false;
|
|
23066
23156
|
}
|
|
@@ -23070,15 +23160,15 @@ var LOCK_STALE_MS = 30000;
|
|
|
23070
23160
|
var LOCK_OWNER = "owner";
|
|
23071
23161
|
var lockToken = (lock) => {
|
|
23072
23162
|
try {
|
|
23073
|
-
return
|
|
23163
|
+
return fs12.readFileSync(path14.join(lock, LOCK_OWNER), "utf8");
|
|
23074
23164
|
} catch {
|
|
23075
23165
|
return null;
|
|
23076
23166
|
}
|
|
23077
23167
|
};
|
|
23078
23168
|
var lockAgeMs = (lock) => {
|
|
23079
|
-
for (const target of [
|
|
23169
|
+
for (const target of [path14.join(lock, LOCK_OWNER), lock]) {
|
|
23080
23170
|
try {
|
|
23081
|
-
return Date.now() -
|
|
23171
|
+
return Date.now() - fs12.statSync(target).mtimeMs;
|
|
23082
23172
|
} catch {}
|
|
23083
23173
|
}
|
|
23084
23174
|
return null;
|
|
@@ -23086,55 +23176,55 @@ var lockAgeMs = (lock) => {
|
|
|
23086
23176
|
var ledgerLock = {
|
|
23087
23177
|
acquire(lock) {
|
|
23088
23178
|
try {
|
|
23089
|
-
|
|
23179
|
+
fs12.mkdirSync(lock);
|
|
23090
23180
|
} catch (error) {
|
|
23091
23181
|
if (error.code === "EEXIST")
|
|
23092
23182
|
return null;
|
|
23093
23183
|
throw error;
|
|
23094
23184
|
}
|
|
23095
|
-
const token = `${process.pid}-${
|
|
23096
|
-
|
|
23185
|
+
const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
|
|
23186
|
+
fs12.writeFileSync(path14.join(lock, LOCK_OWNER), token);
|
|
23097
23187
|
return token;
|
|
23098
23188
|
},
|
|
23099
23189
|
release(lock, token) {
|
|
23100
23190
|
if (lockToken(lock) !== token)
|
|
23101
23191
|
return false;
|
|
23102
|
-
const tomb = `${lock}.release-${
|
|
23192
|
+
const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
|
|
23103
23193
|
try {
|
|
23104
|
-
|
|
23194
|
+
fs12.renameSync(lock, tomb);
|
|
23105
23195
|
} catch {
|
|
23106
23196
|
return false;
|
|
23107
23197
|
}
|
|
23108
23198
|
if (lockToken(tomb) !== token) {
|
|
23109
23199
|
try {
|
|
23110
|
-
|
|
23200
|
+
fs12.renameSync(tomb, lock);
|
|
23111
23201
|
} catch {}
|
|
23112
23202
|
return false;
|
|
23113
23203
|
}
|
|
23114
|
-
|
|
23204
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
23115
23205
|
return true;
|
|
23116
23206
|
},
|
|
23117
23207
|
token: lockToken,
|
|
23118
23208
|
reap(lock, observed) {
|
|
23119
|
-
const tomb = `${lock}.reap-${
|
|
23209
|
+
const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
|
|
23120
23210
|
try {
|
|
23121
|
-
|
|
23211
|
+
fs12.renameSync(lock, tomb);
|
|
23122
23212
|
} catch {
|
|
23123
23213
|
return false;
|
|
23124
23214
|
}
|
|
23125
23215
|
if (lockToken(tomb) === observed) {
|
|
23126
|
-
|
|
23216
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
23127
23217
|
return true;
|
|
23128
23218
|
}
|
|
23129
23219
|
try {
|
|
23130
|
-
|
|
23220
|
+
fs12.renameSync(tomb, lock);
|
|
23131
23221
|
} catch {
|
|
23132
|
-
|
|
23222
|
+
fs12.rmSync(tomb, { recursive: true, force: true });
|
|
23133
23223
|
}
|
|
23134
23224
|
return false;
|
|
23135
23225
|
}
|
|
23136
23226
|
};
|
|
23137
|
-
function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
23227
|
+
function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
|
|
23138
23228
|
const lock = `${file}.lock`;
|
|
23139
23229
|
const deadline = Date.now() + waitMs;
|
|
23140
23230
|
let token = null;
|
|
@@ -23156,14 +23246,14 @@ function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
|
23156
23246
|
ledgerLock.release(lock, token);
|
|
23157
23247
|
}
|
|
23158
23248
|
}
|
|
23159
|
-
var sha2562 = (text) =>
|
|
23249
|
+
var sha2562 = (text) => createHash9("sha256").update(text).digest("hex");
|
|
23160
23250
|
var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
|
|
23161
23251
|
${body}`);
|
|
23162
23252
|
var TAIL_BYTES = 64 * 1024;
|
|
23163
23253
|
var lastLine = (fd, size) => {
|
|
23164
23254
|
for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
|
|
23165
23255
|
const buffer = Buffer.alloc(span);
|
|
23166
|
-
|
|
23256
|
+
fs12.readSync(fd, buffer, 0, span, size - span);
|
|
23167
23257
|
const end = buffer.lastIndexOf(10);
|
|
23168
23258
|
if (end < 0) {
|
|
23169
23259
|
if (span === size)
|
|
@@ -23192,15 +23282,23 @@ var lastLine = (fd, size) => {
|
|
|
23192
23282
|
}
|
|
23193
23283
|
return null;
|
|
23194
23284
|
};
|
|
23285
|
+
var tighten = (fd) => {
|
|
23286
|
+
try {
|
|
23287
|
+
if ((fs12.fstatSync(fd).mode & 63) !== 0)
|
|
23288
|
+
fs12.fchmodSync(fd, 384);
|
|
23289
|
+
} catch {}
|
|
23290
|
+
};
|
|
23195
23291
|
function appendLine(file, build, options) {
|
|
23196
23292
|
const write = () => {
|
|
23197
|
-
const fd =
|
|
23293
|
+
const fd = fs12.openSync(file, "a+", 384);
|
|
23198
23294
|
try {
|
|
23199
|
-
|
|
23295
|
+
if (process.platform !== "win32")
|
|
23296
|
+
tighten(fd);
|
|
23297
|
+
const size = fs12.fstatSync(fd).size;
|
|
23200
23298
|
let fence = "";
|
|
23201
23299
|
if (size > 0) {
|
|
23202
23300
|
const last = Buffer.alloc(1);
|
|
23203
|
-
|
|
23301
|
+
fs12.readSync(fd, last, 0, 1, size - 1);
|
|
23204
23302
|
if (last[0] !== 10)
|
|
23205
23303
|
fence = `
|
|
23206
23304
|
`;
|
|
@@ -23209,17 +23307,17 @@ function appendLine(file, build, options) {
|
|
|
23209
23307
|
if (!line.ok)
|
|
23210
23308
|
return line;
|
|
23211
23309
|
const bytes = Buffer.from(fence + line.value);
|
|
23212
|
-
const written =
|
|
23310
|
+
const written = fs12.writeSync(fd, bytes);
|
|
23213
23311
|
if (written !== bytes.length)
|
|
23214
23312
|
return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
|
|
23215
23313
|
return { ok: true, value: undefined };
|
|
23216
23314
|
} finally {
|
|
23217
|
-
|
|
23315
|
+
fs12.closeSync(fd);
|
|
23218
23316
|
}
|
|
23219
23317
|
};
|
|
23220
23318
|
try {
|
|
23221
|
-
|
|
23222
|
-
if (!options.forceLock && !onNetworkFs(
|
|
23319
|
+
fs12.mkdirSync(path14.dirname(file), { recursive: true });
|
|
23320
|
+
if (!options.forceLock && !onNetworkFs(path14.dirname(file)))
|
|
23223
23321
|
return write();
|
|
23224
23322
|
const locked = withLedgerLock(file, write);
|
|
23225
23323
|
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
@@ -23287,8 +23385,10 @@ function readLedger(cwd, options = {}) {
|
|
|
23287
23385
|
if (!file.ok)
|
|
23288
23386
|
return file;
|
|
23289
23387
|
let text = "";
|
|
23388
|
+
let stat = null;
|
|
23290
23389
|
try {
|
|
23291
|
-
|
|
23390
|
+
stat = fs12.statSync(file.value);
|
|
23391
|
+
text = fs12.readFileSync(file.value, "utf8");
|
|
23292
23392
|
} catch (error) {
|
|
23293
23393
|
if (error.code === "ENOENT")
|
|
23294
23394
|
return {
|
|
@@ -23307,6 +23407,7 @@ function readLedger(cwd, options = {}) {
|
|
|
23307
23407
|
const integrity = { chained: 0, legacy: 0, unverified: [] };
|
|
23308
23408
|
const earlier = new Set([null]);
|
|
23309
23409
|
let chainStarted = false;
|
|
23410
|
+
const ids = new Set;
|
|
23310
23411
|
for (const raw of text.split(`
|
|
23311
23412
|
`)) {
|
|
23312
23413
|
const trimmed = raw.trim();
|
|
@@ -23340,6 +23441,10 @@ function readLedger(cwd, options = {}) {
|
|
|
23340
23441
|
reason = "unsigned";
|
|
23341
23442
|
else
|
|
23342
23443
|
rowIntegrity = "legacy";
|
|
23444
|
+
if (options.integrity && !reason && ids.has(parsed.id))
|
|
23445
|
+
reason = "duplicate_id";
|
|
23446
|
+
if (options.integrity)
|
|
23447
|
+
ids.add(parsed.id);
|
|
23343
23448
|
if (reason)
|
|
23344
23449
|
rowIntegrity = "unverified";
|
|
23345
23450
|
const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
|
|
@@ -23363,7 +23468,8 @@ function readLedger(cwd, options = {}) {
|
|
|
23363
23468
|
seq: rows.length + 1,
|
|
23364
23469
|
superseded: false,
|
|
23365
23470
|
supersedeIgnored: false,
|
|
23366
|
-
...rowIntegrity ? { integrity: rowIntegrity } : {}
|
|
23471
|
+
...rowIntegrity ? { integrity: rowIntegrity } : {},
|
|
23472
|
+
...reason ? { integrityReason: reason } : {}
|
|
23367
23473
|
};
|
|
23368
23474
|
rows.push(row);
|
|
23369
23475
|
if (!options.integrity)
|
|
@@ -23391,18 +23497,48 @@ function readLedger(cwd, options = {}) {
|
|
|
23391
23497
|
const byId = new Map;
|
|
23392
23498
|
for (const row of rows) {
|
|
23393
23499
|
const target = row.supersedes ? byId.get(row.supersedes) : undefined;
|
|
23394
|
-
if (target && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
23500
|
+
if (target && row.integrity !== "unverified" && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
23395
23501
|
target.superseded = true;
|
|
23396
23502
|
else if (row.supersedes)
|
|
23397
23503
|
row.supersedeIgnored = true;
|
|
23398
23504
|
if (!byId.has(row.id))
|
|
23399
23505
|
byId.set(row.id, row);
|
|
23400
23506
|
}
|
|
23507
|
+
if (options.integrity && stat && Buffer.byteLength(text) === stat.size)
|
|
23508
|
+
tamperCache.set(file.value, {
|
|
23509
|
+
size: stat.size,
|
|
23510
|
+
stamp: fileStamp(stat),
|
|
23511
|
+
breaks: integrity.unverified.filter((row) => TAMPER_REASONS.has(row.reason))
|
|
23512
|
+
});
|
|
23401
23513
|
return {
|
|
23402
23514
|
ok: true,
|
|
23403
23515
|
value: { path: file.value, rows, skipped, integrity: options.integrity ? integrity : null }
|
|
23404
23516
|
};
|
|
23405
23517
|
}
|
|
23518
|
+
var TAMPER_REASONS = new Set(["hash_mismatch", "broken_link"]);
|
|
23519
|
+
var tamperCache = new Map;
|
|
23520
|
+
var fileStamp = (stat) => `${stat.ino}:${stat.mtimeMs}:${stat.ctimeMs}`;
|
|
23521
|
+
function ledgerTampering(cwd) {
|
|
23522
|
+
const file = ledgerPath(cwd);
|
|
23523
|
+
if (!file.ok)
|
|
23524
|
+
return [];
|
|
23525
|
+
try {
|
|
23526
|
+
const stat = fs12.statSync(file.value);
|
|
23527
|
+
const cached = tamperCache.get(file.value);
|
|
23528
|
+
if (cached && cached.size === stat.size && cached.stamp === fileStamp(stat))
|
|
23529
|
+
return cached.breaks;
|
|
23530
|
+
} catch {
|
|
23531
|
+
return [];
|
|
23532
|
+
}
|
|
23533
|
+
const read = readLedger(cwd, { integrity: true });
|
|
23534
|
+
return read.ok ? (read.value.integrity?.unverified ?? []).filter((row) => TAMPER_REASONS.has(row.reason)) : [];
|
|
23535
|
+
}
|
|
23536
|
+
function tamperedUnblock(cwd) {
|
|
23537
|
+
const file = ledgerPath(cwd);
|
|
23538
|
+
const where = file.ok ? file.value : "the ledger file";
|
|
23539
|
+
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`;
|
|
23540
|
+
}
|
|
23541
|
+
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
23542
|
function verdictBasis(verdict, current) {
|
|
23407
23543
|
if (verdict.head && verdict.head === current.head)
|
|
23408
23544
|
return "fresh";
|
|
@@ -23410,7 +23546,7 @@ function verdictBasis(verdict, current) {
|
|
|
23410
23546
|
return "carried";
|
|
23411
23547
|
return "stale";
|
|
23412
23548
|
}
|
|
23413
|
-
var isLiveVerdict = (row) => row.type === "verdict" && typeof row.result === "string" && !row.
|
|
23549
|
+
var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id);
|
|
23414
23550
|
function trailerSessions(cwd, base, head) {
|
|
23415
23551
|
if (!base || !head || !safeRef(base) || !safeRef(head))
|
|
23416
23552
|
return [];
|
|
@@ -23491,6 +23627,56 @@ var authoredBy = (authors, session) => {
|
|
|
23491
23627
|
return true;
|
|
23492
23628
|
return false;
|
|
23493
23629
|
};
|
|
23630
|
+
var TRUST_REASONS = new Set([
|
|
23631
|
+
"unsigned",
|
|
23632
|
+
"hash_mismatch",
|
|
23633
|
+
"broken_link",
|
|
23634
|
+
"duplicate_id",
|
|
23635
|
+
"unissued_session",
|
|
23636
|
+
"unbound_session"
|
|
23637
|
+
]);
|
|
23638
|
+
var SHELL_VOUCHED_HOSTS = new Set([
|
|
23639
|
+
"cli",
|
|
23640
|
+
"opencode",
|
|
23641
|
+
"pi",
|
|
23642
|
+
"codex_cli",
|
|
23643
|
+
"codex_desktop"
|
|
23644
|
+
]);
|
|
23645
|
+
function sessionTrust(cwd, row) {
|
|
23646
|
+
const session = row.actor.session;
|
|
23647
|
+
if (!session || row.self === true)
|
|
23648
|
+
return null;
|
|
23649
|
+
const source = typeof row.sessionSource === "string" ? row.sessionSource : "env";
|
|
23650
|
+
const host = row.actor.host;
|
|
23651
|
+
const issuedOnly = source === "flag" ? host !== "cli" : source === "env" && session.includes(":") && !SHELL_VOUCHED_HOSTS.has(host);
|
|
23652
|
+
if (!issuedOnly)
|
|
23653
|
+
return null;
|
|
23654
|
+
const binding = issuedBinding(cwd, session);
|
|
23655
|
+
if (binding === null)
|
|
23656
|
+
return "unissued_session";
|
|
23657
|
+
return binding === "token" && row.sessionBound !== true ? "unbound_session" : null;
|
|
23658
|
+
}
|
|
23659
|
+
function trustReasons(cwd, row) {
|
|
23660
|
+
if (row.integrity === "unverified")
|
|
23661
|
+
return [row.integrityReason ?? "unsigned"];
|
|
23662
|
+
if (row.integrity !== "verified")
|
|
23663
|
+
return ["unsigned"];
|
|
23664
|
+
const session = sessionTrust(cwd, row);
|
|
23665
|
+
return session ? [session] : [];
|
|
23666
|
+
}
|
|
23667
|
+
function withIntegrity(cwd, rows) {
|
|
23668
|
+
if (rows.every((row) => row.integrity !== undefined))
|
|
23669
|
+
return rows;
|
|
23670
|
+
const read = readLedger(cwd, { integrity: true });
|
|
23671
|
+
const bySeq = new Map(read.ok ? read.value.rows.map((row) => [row.seq, row]) : []);
|
|
23672
|
+
return rows.map((row) => {
|
|
23673
|
+
if (row.integrity !== undefined)
|
|
23674
|
+
return row;
|
|
23675
|
+
const fresh = bySeq.get(row.seq);
|
|
23676
|
+
return fresh && fresh.id === row.id && fresh.at === row.at ? fresh : row;
|
|
23677
|
+
});
|
|
23678
|
+
}
|
|
23679
|
+
var untrusted = (reasons) => reasons.some((reason) => TRUST_REASONS.has(reason));
|
|
23494
23680
|
var independenceReasons = (row, authors) => {
|
|
23495
23681
|
const reasons = [];
|
|
23496
23682
|
if (!row.actor.session)
|
|
@@ -23501,15 +23687,17 @@ var independenceReasons = (row, authors) => {
|
|
|
23501
23687
|
reasons.push("self");
|
|
23502
23688
|
return reasons;
|
|
23503
23689
|
};
|
|
23504
|
-
function effectiveOf(rows,
|
|
23690
|
+
function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
|
|
23505
23691
|
let effective = null;
|
|
23506
23692
|
for (const row of rows) {
|
|
23507
23693
|
if (!effective) {
|
|
23508
23694
|
effective = row;
|
|
23509
23695
|
continue;
|
|
23510
23696
|
}
|
|
23511
|
-
|
|
23512
|
-
|
|
23697
|
+
if (untrusted(reasonsOf(row)) && !untrusted(reasonsOf(effective)))
|
|
23698
|
+
continue;
|
|
23699
|
+
const rowIndependent = reasonsOf(row).length === 0;
|
|
23700
|
+
const effIndependent = reasonsOf(effective).length === 0;
|
|
23513
23701
|
if (effIndependent && !rowIndependent && isCurrent(effective))
|
|
23514
23702
|
continue;
|
|
23515
23703
|
if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session && sameCode(effective, row))
|
|
@@ -23523,7 +23711,7 @@ function restackCarries(rows, branch, from, to) {
|
|
|
23523
23711
|
return false;
|
|
23524
23712
|
const edges = new Map;
|
|
23525
23713
|
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)
|
|
23714
|
+
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
23715
|
edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
|
|
23528
23716
|
const seen = new Set([from]);
|
|
23529
23717
|
const queue = [from];
|
|
@@ -23539,7 +23727,8 @@ function restackCarries(rows, branch, from, to) {
|
|
|
23539
23727
|
}
|
|
23540
23728
|
return false;
|
|
23541
23729
|
}
|
|
23542
|
-
function checkVerdicts(cwd, branch,
|
|
23730
|
+
function checkVerdicts(cwd, branch, ledgerRows) {
|
|
23731
|
+
const rows = withIntegrity(cwd, ledgerRows);
|
|
23543
23732
|
const head = branchHead(cwd, branch);
|
|
23544
23733
|
const fallbackBase = defaultBase(cwd);
|
|
23545
23734
|
const scope = branchScope(cwd, branch);
|
|
@@ -23569,21 +23758,47 @@ function checkVerdicts(cwd, branch, rows) {
|
|
|
23569
23758
|
return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
|
|
23570
23759
|
};
|
|
23571
23760
|
const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
|
|
23761
|
+
const failureAuthorCache = new Map;
|
|
23762
|
+
const failureAuthors = (row) => {
|
|
23763
|
+
let set = failureAuthorCache.get(row.seq);
|
|
23764
|
+
if (!set) {
|
|
23765
|
+
set = authorSessions(cwd, rows.filter((other) => other.integrity === "verified" && other.seq < row.seq), branch, { base: null, head: null }, scope);
|
|
23766
|
+
failureAuthorCache.set(row.seq, set);
|
|
23767
|
+
}
|
|
23768
|
+
return set;
|
|
23769
|
+
};
|
|
23770
|
+
const reasonCache = new Map;
|
|
23771
|
+
const reasonsOf = (row) => {
|
|
23772
|
+
let reasons = reasonCache.get(row);
|
|
23773
|
+
if (!reasons) {
|
|
23774
|
+
const failing = FAILING.has(String(row.result));
|
|
23775
|
+
reasons = [
|
|
23776
|
+
...trustReasons(cwd, row),
|
|
23777
|
+
...independenceReasons(row, failing ? failureAuthors(row) : authors)
|
|
23778
|
+
];
|
|
23779
|
+
reasonCache.set(row, reasons);
|
|
23780
|
+
}
|
|
23781
|
+
return reasons;
|
|
23782
|
+
};
|
|
23783
|
+
const retired = new Set;
|
|
23784
|
+
for (const row of rows)
|
|
23785
|
+
if (row.type === "verdict" && row.supersedes && !row.supersedeIgnored && trustReasons(cwd, row).length === 0)
|
|
23786
|
+
retired.add(row.supersedes);
|
|
23572
23787
|
const byKind = new Map;
|
|
23573
23788
|
for (const row of rows)
|
|
23574
|
-
if (isLiveVerdict(row) && scope(row)) {
|
|
23789
|
+
if (isLiveVerdict(row, retired) && scope(row)) {
|
|
23575
23790
|
const kind = typeof row.kind === "string" ? row.kind : "review";
|
|
23576
23791
|
byKind.set(kind, [...byKind.get(kind) ?? [], row]);
|
|
23577
23792
|
}
|
|
23578
23793
|
const verdicts = [];
|
|
23579
23794
|
const warnings = [];
|
|
23580
23795
|
for (const [kind, list] of byKind) {
|
|
23581
|
-
const row = effectiveOf(list,
|
|
23796
|
+
const row = effectiveOf(list, reasonsOf, sameCode, (candidate) => basisOf(candidate) !== "stale");
|
|
23582
23797
|
if (!row)
|
|
23583
23798
|
continue;
|
|
23584
23799
|
const basis = basisOf(row);
|
|
23585
23800
|
const current = basis !== "stale";
|
|
23586
|
-
const independence =
|
|
23801
|
+
const independence = reasonsOf(row);
|
|
23587
23802
|
const reasons = [
|
|
23588
23803
|
...current ? [] : ["stale"],
|
|
23589
23804
|
...PASSING.has(String(row.result)) ? [] : ["not_passing"],
|
|
@@ -23600,23 +23815,31 @@ function checkVerdicts(cwd, branch, rows) {
|
|
|
23600
23815
|
});
|
|
23601
23816
|
if (reasons.length === 0) {
|
|
23602
23817
|
for (const later of list)
|
|
23603
|
-
if (later.seq > row.seq &&
|
|
23818
|
+
if (later.seq > row.seq && reasonsOf(later).length > 0 && !untrusted(reasonsOf(later)) && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
|
|
23604
23819
|
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
23820
|
}
|
|
23606
23821
|
}
|
|
23607
23822
|
for (const row of rows)
|
|
23608
23823
|
if (row.integrity === "unverified" && scope(row))
|
|
23609
23824
|
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)`);
|
|
23825
|
+
const tampered = ledgerTampering(cwd);
|
|
23826
|
+
if (tampered.length) {
|
|
23827
|
+
warnings.push(tamperedWarning(cwd, tampered));
|
|
23828
|
+
for (const entry of verdicts) {
|
|
23829
|
+
entry.accepted = false;
|
|
23830
|
+
entry.reasons = [...entry.reasons, "ledger_tampered"];
|
|
23831
|
+
}
|
|
23832
|
+
}
|
|
23610
23833
|
verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
|
|
23611
23834
|
const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
|
|
23612
23835
|
const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
|
|
23613
23836
|
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: [] } : {
|
|
23837
|
+
const accepted = tampered.length ? { accepted: false, verdict: null, reasons: ["ledger_tampered"] } : newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
23615
23838
|
accepted: false,
|
|
23616
23839
|
verdict: failing?.verdict ?? null,
|
|
23617
23840
|
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
23618
23841
|
};
|
|
23619
|
-
const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
23842
|
+
const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
23620
23843
|
return {
|
|
23621
23844
|
branch,
|
|
23622
23845
|
head,
|
|
@@ -23779,7 +24002,7 @@ var productDecision = (task, ledger) => {
|
|
|
23779
24002
|
};
|
|
23780
24003
|
};
|
|
23781
24004
|
var planRecorded = (task, root) => {
|
|
23782
|
-
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(
|
|
24005
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path15.resolve(root, ref.path)));
|
|
23783
24006
|
if (cited)
|
|
23784
24007
|
return {
|
|
23785
24008
|
met: true,
|
|
@@ -23827,15 +24050,15 @@ function derivedRuleStatus(task, requirement, root, ledger) {
|
|
|
23827
24050
|
}
|
|
23828
24051
|
|
|
23829
24052
|
// 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) =>
|
|
24053
|
+
var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
|
|
24054
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path16.sep}`);
|
|
24055
|
+
var relative = (root, candidate) => path16.relative(root, candidate).split(path16.sep).join("/") || ".";
|
|
23833
24056
|
var canonicalPath = (value) => {
|
|
23834
24057
|
if (typeof value !== "string" || !value)
|
|
23835
24058
|
return null;
|
|
23836
24059
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
23837
24060
|
return null;
|
|
23838
|
-
const normalized =
|
|
24061
|
+
const normalized = path16.posix.normalize(value);
|
|
23839
24062
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
23840
24063
|
return null;
|
|
23841
24064
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -23883,21 +24106,21 @@ var gitPaths = (root) => {
|
|
|
23883
24106
|
git: !/not a git repository/i.test(stderr)
|
|
23884
24107
|
};
|
|
23885
24108
|
}
|
|
23886
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
24109
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")) : [];
|
|
23887
24110
|
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
23888
24111
|
if (stagedDeleted.status !== 0) {
|
|
23889
24112
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
23890
24113
|
if (!/not a git repository/i.test(stderr))
|
|
23891
24114
|
return { paths, uncertain: true, git: true };
|
|
23892
24115
|
} else if (stagedDeleted.stdout) {
|
|
23893
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
24116
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")));
|
|
23894
24117
|
}
|
|
23895
24118
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
23896
24119
|
};
|
|
23897
24120
|
var walk = (root, directory, output) => {
|
|
23898
24121
|
let names;
|
|
23899
24122
|
try {
|
|
23900
|
-
names =
|
|
24123
|
+
names = fs13.readdirSync(directory);
|
|
23901
24124
|
} catch {
|
|
23902
24125
|
return true;
|
|
23903
24126
|
}
|
|
@@ -23905,10 +24128,10 @@ var walk = (root, directory, output) => {
|
|
|
23905
24128
|
for (const name of names) {
|
|
23906
24129
|
if (name === ".git" || name === ".workit")
|
|
23907
24130
|
continue;
|
|
23908
|
-
const target =
|
|
24131
|
+
const target = path16.join(directory, name);
|
|
23909
24132
|
let stat;
|
|
23910
24133
|
try {
|
|
23911
|
-
stat =
|
|
24134
|
+
stat = fs13.lstatSync(target);
|
|
23912
24135
|
} catch {
|
|
23913
24136
|
uncertain = true;
|
|
23914
24137
|
continue;
|
|
@@ -23923,14 +24146,14 @@ var walk = (root, directory, output) => {
|
|
|
23923
24146
|
var scopeRoots = (root, scope) => {
|
|
23924
24147
|
const roots = [];
|
|
23925
24148
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
23926
|
-
const target =
|
|
24149
|
+
const target = path16.resolve(root, item);
|
|
23927
24150
|
if (!inside(root, target))
|
|
23928
24151
|
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
24152
|
let ancestor = target;
|
|
23930
|
-
while (!
|
|
23931
|
-
ancestor =
|
|
24153
|
+
while (!fs13.existsSync(ancestor) && ancestor !== root)
|
|
24154
|
+
ancestor = path16.dirname(ancestor);
|
|
23932
24155
|
try {
|
|
23933
|
-
if (!inside(root,
|
|
24156
|
+
if (!inside(root, fs13.realpathSync(ancestor)))
|
|
23934
24157
|
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
24158
|
} catch {
|
|
23936
24159
|
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
@@ -23942,7 +24165,7 @@ var scopeRoots = (root, scope) => {
|
|
|
23942
24165
|
function captureCandidate(root, scope, environment = []) {
|
|
23943
24166
|
let checkout;
|
|
23944
24167
|
try {
|
|
23945
|
-
checkout =
|
|
24168
|
+
checkout = fs13.realpathSync(root);
|
|
23946
24169
|
} catch {
|
|
23947
24170
|
return failure2("invalid_input", "candidate root does not exist");
|
|
23948
24171
|
}
|
|
@@ -23960,11 +24183,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
23960
24183
|
if (item === ".git" || item === ".workit")
|
|
23961
24184
|
continue;
|
|
23962
24185
|
names.add(item);
|
|
23963
|
-
if (!
|
|
24186
|
+
if (!fs13.existsSync(target))
|
|
23964
24187
|
continue;
|
|
23965
24188
|
let stat;
|
|
23966
24189
|
try {
|
|
23967
|
-
stat =
|
|
24190
|
+
stat = fs13.lstatSync(target);
|
|
23968
24191
|
} catch {
|
|
23969
24192
|
uncertain = true;
|
|
23970
24193
|
continue;
|
|
@@ -23980,17 +24203,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
23980
24203
|
for (const item of [...names].toSorted()) {
|
|
23981
24204
|
if (!scopeMatches(item, normalizedScope))
|
|
23982
24205
|
continue;
|
|
23983
|
-
const target =
|
|
24206
|
+
const target = path16.join(checkout, item);
|
|
23984
24207
|
try {
|
|
23985
|
-
const stat =
|
|
24208
|
+
const stat = fs13.lstatSync(target);
|
|
23986
24209
|
if (stat.isSymbolicLink()) {
|
|
23987
|
-
const link =
|
|
24210
|
+
const link = fs13.readlinkSync(target);
|
|
23988
24211
|
files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
|
|
23989
24212
|
} else if (stat.isFile()) {
|
|
23990
24213
|
files.push({
|
|
23991
24214
|
path: item,
|
|
23992
24215
|
kind: "file",
|
|
23993
|
-
digest: digestBytes(
|
|
24216
|
+
digest: digestBytes(fs13.readFileSync(target)),
|
|
23994
24217
|
executable: (stat.mode & 73) !== 0
|
|
23995
24218
|
});
|
|
23996
24219
|
} else if (!stat.isDirectory()) {
|
|
@@ -24155,7 +24378,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
|
|
|
24155
24378
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
24156
24379
|
let root;
|
|
24157
24380
|
try {
|
|
24158
|
-
root =
|
|
24381
|
+
root = fs13.realpathSync(checkoutRoot);
|
|
24159
24382
|
} catch {
|
|
24160
24383
|
return failure2("permission_denied", "checkout root is unavailable");
|
|
24161
24384
|
}
|
|
@@ -24164,23 +24387,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
24164
24387
|
continue;
|
|
24165
24388
|
if (!reference.digest)
|
|
24166
24389
|
return failure2("invalid_input", "document references require a byte digest");
|
|
24167
|
-
const target =
|
|
24168
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
24390
|
+
const target = path16.resolve(root, reference.path);
|
|
24391
|
+
if (target !== root && !target.startsWith(`${root}${path16.sep}`))
|
|
24169
24392
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
24170
24393
|
fields: [
|
|
24171
24394
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
24172
24395
|
]
|
|
24173
24396
|
});
|
|
24174
24397
|
try {
|
|
24175
|
-
const below =
|
|
24398
|
+
const below = path16.relative(root, target);
|
|
24176
24399
|
let current = root;
|
|
24177
|
-
for (const segment of below.split(
|
|
24400
|
+
for (const segment of below.split(path16.sep)) {
|
|
24178
24401
|
if (!segment)
|
|
24179
24402
|
continue;
|
|
24180
|
-
current =
|
|
24181
|
-
const stat =
|
|
24182
|
-
const real =
|
|
24183
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
24403
|
+
current = path16.join(current, segment);
|
|
24404
|
+
const stat = fs13.lstatSync(current);
|
|
24405
|
+
const real = fs13.realpathSync(current);
|
|
24406
|
+
if (real !== root && !real.startsWith(`${root}${path16.sep}`))
|
|
24184
24407
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
24185
24408
|
fields: [
|
|
24186
24409
|
{
|
|
@@ -24196,7 +24419,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
24196
24419
|
if (current !== target && !stat.isDirectory())
|
|
24197
24420
|
return failure2("invalid_input", "document reference ancestor is not a directory");
|
|
24198
24421
|
}
|
|
24199
|
-
if (digestBytes(
|
|
24422
|
+
if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
|
|
24200
24423
|
return failure2("permission_denied", "decision document bytes have changed");
|
|
24201
24424
|
} catch {
|
|
24202
24425
|
return failure2("invalid_input", "decision document is unavailable");
|
|
@@ -25054,7 +25277,7 @@ var judgeInputSummary = (raw) => {
|
|
|
25054
25277
|
|
|
25055
25278
|
// packages/workit-core/src/autonomy.ts
|
|
25056
25279
|
import os4 from "node:os";
|
|
25057
|
-
import
|
|
25280
|
+
import path17 from "node:path";
|
|
25058
25281
|
var accountHome = () => {
|
|
25059
25282
|
try {
|
|
25060
25283
|
return os4.userInfo().homedir;
|
|
@@ -25063,10 +25286,10 @@ var accountHome = () => {
|
|
|
25063
25286
|
}
|
|
25064
25287
|
};
|
|
25065
25288
|
var grantsHome = { resolve: accountHome };
|
|
25066
|
-
var grantsDir = () =>
|
|
25289
|
+
var grantsDir = () => path17.join(grantsHome.resolve(), ".config", "workit");
|
|
25067
25290
|
var grantsOverride = () => {
|
|
25068
|
-
const configured =
|
|
25069
|
-
return configured ===
|
|
25291
|
+
const configured = path17.resolve(resolveConfigDir());
|
|
25292
|
+
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
25293
|
};
|
|
25071
25294
|
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
25072
25295
|
var DEFAULT_GRANTS = Object.freeze({
|
|
@@ -26838,7 +27061,7 @@ class WorkitCore {
|
|
|
26838
27061
|
// packages/workit-core/src/core/repo-context.ts
|
|
26839
27062
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
26840
27063
|
import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
|
|
26841
|
-
import
|
|
27064
|
+
import path18 from "node:path";
|
|
26842
27065
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
26843
27066
|
try {
|
|
26844
27067
|
gitRevisionParts(value);
|
|
@@ -26972,7 +27195,7 @@ var findPrTemplate = (cwd) => {
|
|
|
26972
27195
|
]) {
|
|
26973
27196
|
const parts = rel.split("/");
|
|
26974
27197
|
const base = parts[parts.length - 1];
|
|
26975
|
-
const dir = parts.length > 1 ?
|
|
27198
|
+
const dir = parts.length > 1 ? path18.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
26976
27199
|
const actual = probeCaseInsensitive(dir, base);
|
|
26977
27200
|
if (actual)
|
|
26978
27201
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -27003,7 +27226,7 @@ var diffStatForRange = (cwd, range) => {
|
|
|
27003
27226
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
27004
27227
|
};
|
|
27005
27228
|
var packageScripts = (cwd, keys) => {
|
|
27006
|
-
const pkgPath =
|
|
27229
|
+
const pkgPath = path18.join(cwd, "package.json");
|
|
27007
27230
|
if (!existsSync6(pkgPath))
|
|
27008
27231
|
return [];
|
|
27009
27232
|
try {
|
|
@@ -27029,9 +27252,9 @@ var documentationFiles = (cwd) => {
|
|
|
27029
27252
|
if (entry.isDirectory()) {
|
|
27030
27253
|
if (excluded.has(entry.name))
|
|
27031
27254
|
continue;
|
|
27032
|
-
walk(
|
|
27255
|
+
walk(path18.join(dir, entry.name), depth + 1);
|
|
27033
27256
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
27034
|
-
matches.push("./" +
|
|
27257
|
+
matches.push("./" + path18.relative(cwd, path18.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
27035
27258
|
}
|
|
27036
27259
|
}
|
|
27037
27260
|
};
|
|
@@ -27047,7 +27270,7 @@ var readTrimmed2 = (file, maxLines) => {
|
|
|
27047
27270
|
`);
|
|
27048
27271
|
};
|
|
27049
27272
|
function prReadyContext(root, range) {
|
|
27050
|
-
const cwd =
|
|
27273
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
27051
27274
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
27052
27275
|
return invalidRange(cwd);
|
|
27053
27276
|
let stdout = "";
|
|
@@ -27105,7 +27328,7 @@ ${status.stderr}` : "");
|
|
|
27105
27328
|
stdout += `template_path: ${template}
|
|
27106
27329
|
|
|
27107
27330
|
`;
|
|
27108
|
-
stdout += readTrimmed2(
|
|
27331
|
+
stdout += readTrimmed2(path18.join(cwd, template), 220);
|
|
27109
27332
|
} else {
|
|
27110
27333
|
stdout += `template_path: none
|
|
27111
27334
|
|
|
@@ -27115,7 +27338,7 @@ ${status.stderr}` : "");
|
|
|
27115
27338
|
stdout += `
|
|
27116
27339
|
`;
|
|
27117
27340
|
stdout += printSection("Recent Validation Signals");
|
|
27118
|
-
if (existsSync6(
|
|
27341
|
+
if (existsSync6(path18.join(cwd, "package.json"))) {
|
|
27119
27342
|
stdout += `package.json detected. Common scripts:
|
|
27120
27343
|
`;
|
|
27121
27344
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -27124,7 +27347,7 @@ ${status.stderr}` : "");
|
|
|
27124
27347
|
`) + `
|
|
27125
27348
|
`;
|
|
27126
27349
|
}
|
|
27127
|
-
if (existsSync6(
|
|
27350
|
+
if (existsSync6(path18.join(cwd, "Cargo.toml")) || existsSync6(path18.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
27128
27351
|
stdout += `Rust project detected.
|
|
27129
27352
|
`;
|
|
27130
27353
|
}
|
|
@@ -27157,7 +27380,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
27157
27380
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
27158
27381
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
27159
27382
|
function changelogContext(root, range) {
|
|
27160
|
-
const cwd =
|
|
27383
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
27161
27384
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
27162
27385
|
return invalidRange(cwd);
|
|
27163
27386
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -27175,7 +27398,7 @@ function changelogContext(root, range) {
|
|
|
27175
27398
|
stdout += CHANGELOG_RULES + `
|
|
27176
27399
|
`;
|
|
27177
27400
|
stdout += printSection("Existing CHANGELOG.md");
|
|
27178
|
-
const changelogPath =
|
|
27401
|
+
const changelogPath = path18.join(cwd, "CHANGELOG.md");
|
|
27179
27402
|
if (existsSync6(changelogPath)) {
|
|
27180
27403
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
27181
27404
|
`;
|
|
@@ -27195,7 +27418,7 @@ function changelogContext(root, range) {
|
|
|
27195
27418
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
27196
27419
|
}
|
|
27197
27420
|
function docsRefreshContext(root, range) {
|
|
27198
|
-
const cwd =
|
|
27421
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
27199
27422
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
27200
27423
|
return invalidRange(cwd);
|
|
27201
27424
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -27217,7 +27440,7 @@ function docsRefreshContext(root, range) {
|
|
|
27217
27440
|
`) + `
|
|
27218
27441
|
`;
|
|
27219
27442
|
stdout += printSection("README Preview");
|
|
27220
|
-
const readme =
|
|
27443
|
+
const readme = path18.join(cwd, "README.md");
|
|
27221
27444
|
if (existsSync6(readme)) {
|
|
27222
27445
|
stdout += readTrimmed2(readme, 220) + `
|
|
27223
27446
|
`;
|
|
@@ -27226,7 +27449,7 @@ function docsRefreshContext(root, range) {
|
|
|
27226
27449
|
`;
|
|
27227
27450
|
}
|
|
27228
27451
|
stdout += printSection("Package Scripts");
|
|
27229
|
-
const pkgPath =
|
|
27452
|
+
const pkgPath = path18.join(cwd, "package.json");
|
|
27230
27453
|
if (existsSync6(pkgPath)) {
|
|
27231
27454
|
try {
|
|
27232
27455
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
@@ -27240,7 +27463,7 @@ function docsRefreshContext(root, range) {
|
|
|
27240
27463
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
27241
27464
|
}
|
|
27242
27465
|
function releaseNotesContext(root, rangeOrTag) {
|
|
27243
|
-
const cwd =
|
|
27466
|
+
const cwd = path18.resolve(repoRoot(root));
|
|
27244
27467
|
if (!rangeOrTag) {
|
|
27245
27468
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
27246
27469
|
`, exitCode: 1, cwd };
|
|
@@ -27278,7 +27501,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
27278
27501
|
`;
|
|
27279
27502
|
stdout += printSection("Existing Release Files");
|
|
27280
27503
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
27281
|
-
if (existsSync6(
|
|
27504
|
+
if (existsSync6(path18.join(cwd, rel)))
|
|
27282
27505
|
stdout += rel + `
|
|
27283
27506
|
`;
|
|
27284
27507
|
}
|
|
@@ -27309,8 +27532,8 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
27309
27532
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
27310
27533
|
|
|
27311
27534
|
// packages/workit-core/src/core/pr-create.ts
|
|
27312
|
-
import
|
|
27313
|
-
import
|
|
27535
|
+
import fs14 from "node:fs";
|
|
27536
|
+
import path19 from "node:path";
|
|
27314
27537
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
27315
27538
|
// packages/workit-core/src/forge/pr-body.ts
|
|
27316
27539
|
function parseGhIssue(value) {
|
|
@@ -27321,13 +27544,13 @@ function parseGhIssue(value) {
|
|
|
27321
27544
|
// packages/workit-core/src/core/pr-create.ts
|
|
27322
27545
|
function whichOnPath(tool) {
|
|
27323
27546
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
27324
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
27547
|
+
for (const dir of (process.env.PATH ?? "").split(path19.delimiter)) {
|
|
27325
27548
|
if (!dir)
|
|
27326
27549
|
continue;
|
|
27327
27550
|
for (const name of names) {
|
|
27328
|
-
const candidate =
|
|
27551
|
+
const candidate = path19.join(dir, name);
|
|
27329
27552
|
try {
|
|
27330
|
-
|
|
27553
|
+
fs14.accessSync(candidate, fs14.constants.X_OK);
|
|
27331
27554
|
return candidate;
|
|
27332
27555
|
} catch {}
|
|
27333
27556
|
}
|
|
@@ -27445,8 +27668,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
|
27445
27668
|
};
|
|
27446
27669
|
|
|
27447
27670
|
// packages/workit-core/src/core/youtrack.ts
|
|
27448
|
-
import
|
|
27449
|
-
import
|
|
27671
|
+
import fs15 from "node:fs";
|
|
27672
|
+
import path20 from "node:path";
|
|
27450
27673
|
|
|
27451
27674
|
// packages/workit-core/src/core/templates.ts
|
|
27452
27675
|
var repoRoot2 = assetRoot();
|
|
@@ -27459,22 +27682,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
27459
27682
|
// packages/workit-core/src/core/youtrack.ts
|
|
27460
27683
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
27461
27684
|
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
27462
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
27463
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
27685
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path20.join(configDir(), "youtrack.json");
|
|
27686
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path20.join(resolveConfigDir(), "youtrack.json");
|
|
27464
27687
|
var youTrackTokenModeOk = (p) => {
|
|
27465
27688
|
if (process.platform === "win32")
|
|
27466
27689
|
return true;
|
|
27467
|
-
const mode =
|
|
27690
|
+
const mode = fs15.statSync(p).mode & 511;
|
|
27468
27691
|
return mode === 384;
|
|
27469
27692
|
};
|
|
27470
27693
|
function readYouTrackConfig(required) {
|
|
27471
27694
|
const cfgPath = youTrackConfigPath();
|
|
27472
|
-
if (!
|
|
27695
|
+
if (!fs15.existsSync(cfgPath)) {
|
|
27473
27696
|
return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
|
|
27474
27697
|
}
|
|
27475
27698
|
let parsed;
|
|
27476
27699
|
try {
|
|
27477
|
-
parsed = JSON.parse(
|
|
27700
|
+
parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
|
|
27478
27701
|
} catch {
|
|
27479
27702
|
return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
|
|
27480
27703
|
}
|
|
@@ -27485,11 +27708,11 @@ function readYouTrackConfig(required) {
|
|
|
27485
27708
|
}
|
|
27486
27709
|
var readYouTrackContextConfig = () => {
|
|
27487
27710
|
const cfgPath = youTrackReadOnlyConfigPath();
|
|
27488
|
-
if (!
|
|
27711
|
+
if (!fs15.existsSync(cfgPath))
|
|
27489
27712
|
return { data: {} };
|
|
27490
27713
|
let parsed;
|
|
27491
27714
|
try {
|
|
27492
|
-
parsed = JSON.parse(
|
|
27715
|
+
parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
|
|
27493
27716
|
} catch {
|
|
27494
27717
|
return { error: "YouTrack configuration is unavailable" };
|
|
27495
27718
|
}
|
|
@@ -27528,12 +27751,12 @@ var youTrackToken = () => {
|
|
|
27528
27751
|
if ("error" in loaded)
|
|
27529
27752
|
return loaded;
|
|
27530
27753
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
27531
|
-
const tokenPath = tokenFile ?
|
|
27532
|
-
if (!tokenPath || !
|
|
27754
|
+
const tokenPath = tokenFile ? path20.isAbsolute(tokenFile) ? path20.resolve(tokenFile) : path20.resolve(process.cwd(), tokenFile) : "";
|
|
27755
|
+
if (!tokenPath || !fs15.existsSync(tokenPath))
|
|
27533
27756
|
return { error: "missing youtrack.token" };
|
|
27534
27757
|
if (!youTrackTokenModeOk(tokenPath))
|
|
27535
27758
|
return { error: "youtrack.token mode must be 0600" };
|
|
27536
|
-
const token =
|
|
27759
|
+
const token = fs15.readFileSync(tokenPath, "utf8").trim();
|
|
27537
27760
|
if (!token)
|
|
27538
27761
|
return { error: "empty token file" };
|
|
27539
27762
|
if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
|
|
@@ -27598,9 +27821,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
|
|
|
27598
27821
|
} catch {
|
|
27599
27822
|
continue;
|
|
27600
27823
|
}
|
|
27601
|
-
if (!
|
|
27824
|
+
if (!fs15.existsSync(full))
|
|
27602
27825
|
continue;
|
|
27603
|
-
const text =
|
|
27826
|
+
const text = fs15.readFileSync(full, "utf8");
|
|
27604
27827
|
const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
|
|
27605
27828
|
if (m)
|
|
27606
27829
|
return m[1];
|
|
@@ -27870,15 +28093,15 @@ function resolveGitRevision(root, value) {
|
|
|
27870
28093
|
}
|
|
27871
28094
|
function resolveInside(root, candidate) {
|
|
27872
28095
|
const base = realpathSync5(root);
|
|
27873
|
-
const target =
|
|
27874
|
-
if (target !== base && !target.startsWith(base +
|
|
28096
|
+
const target = path21.resolve(base, candidate);
|
|
28097
|
+
if (target !== base && !target.startsWith(base + path21.sep)) {
|
|
27875
28098
|
throw new Error("path must stay inside repository root");
|
|
27876
28099
|
}
|
|
27877
28100
|
let ancestor = target;
|
|
27878
28101
|
while (!existsSync7(ancestor))
|
|
27879
|
-
ancestor =
|
|
28102
|
+
ancestor = path21.dirname(ancestor);
|
|
27880
28103
|
const canonicalAncestor = realpathSync5(ancestor);
|
|
27881
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
28104
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path21.sep)) {
|
|
27882
28105
|
throw new Error("path must stay inside repository root");
|
|
27883
28106
|
}
|
|
27884
28107
|
return target;
|
|
@@ -27936,13 +28159,13 @@ var rootVariants = (workspaceRoot) => {
|
|
|
27936
28159
|
if (!workspaceRoot)
|
|
27937
28160
|
return [];
|
|
27938
28161
|
const roots = new Set;
|
|
27939
|
-
for (const candidate of [workspaceRoot,
|
|
27940
|
-
if (
|
|
28162
|
+
for (const candidate of [workspaceRoot, path22.resolve(workspaceRoot)]) {
|
|
28163
|
+
if (path22.isAbsolute(candidate) && candidate !== path22.parse(candidate).root)
|
|
27941
28164
|
roots.add(candidate);
|
|
27942
28165
|
}
|
|
27943
28166
|
try {
|
|
27944
28167
|
const canonical = realpathSync6(workspaceRoot);
|
|
27945
|
-
if (canonical !==
|
|
28168
|
+
if (canonical !== path22.parse(canonical).root)
|
|
27946
28169
|
roots.add(canonical);
|
|
27947
28170
|
} catch {}
|
|
27948
28171
|
return [...roots].toSorted((left, right) => right.length - left.length);
|
|
@@ -28024,11 +28247,11 @@ var thrownResult = (tool, error, workspaceRoot) => {
|
|
|
28024
28247
|
};
|
|
28025
28248
|
function createMcpServer(host, contextProvider) {
|
|
28026
28249
|
assertMcpHost(host);
|
|
28027
|
-
const here =
|
|
28250
|
+
const here = path22.dirname(fileURLToPath2(import.meta.url));
|
|
28028
28251
|
const sourceMarker = markSourcesLoaded([
|
|
28029
|
-
|
|
28030
|
-
|
|
28031
|
-
|
|
28252
|
+
path22.join(here, "server.ts"),
|
|
28253
|
+
path22.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
|
|
28254
|
+
path22.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
|
|
28032
28255
|
].filter((file) => existsSync8(file)));
|
|
28033
28256
|
let staleWarned = false;
|
|
28034
28257
|
const server = new Server({ name: "workit", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
|
|
@@ -28208,8 +28431,8 @@ var main = async () => {
|
|
|
28208
28431
|
process.exitCode = 2;
|
|
28209
28432
|
}
|
|
28210
28433
|
};
|
|
28211
|
-
var entryPath = process.argv[1] ?
|
|
28212
|
-
if (entryPath ===
|
|
28434
|
+
var entryPath = process.argv[1] ? path23.resolve(process.argv[1]) : "";
|
|
28435
|
+
if (entryPath === path23.resolve(fileURLToPath3(import.meta.url)))
|
|
28213
28436
|
await main();
|
|
28214
28437
|
// packages/workit-core/src/hooks/descriptor.ts
|
|
28215
28438
|
var support = (descriptor, axis) => {
|
|
@@ -28437,7 +28660,7 @@ var cursorContextProvider = (workspaceRoot = process.env.WORKFLOW_WORKSPACE_ROOT
|
|
|
28437
28660
|
var stampCursorSessionStart = (stateDir = resolveStateDir()) => {
|
|
28438
28661
|
try {
|
|
28439
28662
|
mkdirSync3(stateDir, { recursive: true });
|
|
28440
|
-
writeFileSync3(
|
|
28663
|
+
writeFileSync3(path24.join(stateDir, "cursor-session-last-start"), `${JSON.stringify({ at: new Date().toISOString() })}
|
|
28441
28664
|
`);
|
|
28442
28665
|
} catch {}
|
|
28443
28666
|
};
|