@brainervirus/workit-cli 8.11.0 → 8.12.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/dist/index.js +1653 -721
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -65,7 +65,7 @@ var package_default;
|
|
|
65
65
|
var init_package = __esm(() => {
|
|
66
66
|
package_default = {
|
|
67
67
|
name: "@brainervirus/workit-cli",
|
|
68
|
-
version: "8.
|
|
68
|
+
version: "8.12.1",
|
|
69
69
|
private: false,
|
|
70
70
|
description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
|
|
71
71
|
keywords: [
|
|
@@ -103,9 +103,9 @@ var init_package = __esm(() => {
|
|
|
103
103
|
},
|
|
104
104
|
dependencies: {
|
|
105
105
|
"@babel/parser": "7.29.9",
|
|
106
|
-
"@brainervirus/workit-core": "^8.
|
|
107
|
-
"@brainervirus/workit-cursor": "^8.
|
|
108
|
-
"@brainervirus/workit-opencode": "^8.
|
|
106
|
+
"@brainervirus/workit-core": "^8.12.1",
|
|
107
|
+
"@brainervirus/workit-cursor": "^8.12.1",
|
|
108
|
+
"@brainervirus/workit-opencode": "^8.12.1",
|
|
109
109
|
"@inkjs/ui": "2.0.0",
|
|
110
110
|
ink: "7.1.1",
|
|
111
111
|
react: "19.2.8"
|
|
@@ -298,7 +298,7 @@ function loadCheckConfig(cwd) {
|
|
|
298
298
|
gates[gate] = names;
|
|
299
299
|
}
|
|
300
300
|
}
|
|
301
|
-
return { source: CHECKS_FILE, root, checks, gates, error: null };
|
|
301
|
+
return { source: CHECKS_FILE, root, checks, gates, error: null, resources: parsed.resources };
|
|
302
302
|
}
|
|
303
303
|
function matchesNamedCheck(config, name, argv, cwd) {
|
|
304
304
|
if (config.error || name === null || cwd !== ".")
|
|
@@ -9939,11 +9939,224 @@ var init_file_lock = __esm(() => {
|
|
|
9939
9939
|
init_file_lock_sync();
|
|
9940
9940
|
});
|
|
9941
9941
|
|
|
9942
|
+
// packages/workit-core/src/core/logger.ts
|
|
9943
|
+
import { appendFileSync, mkdirSync, readdirSync, unlinkSync } from "node:fs";
|
|
9944
|
+
import os from "node:os";
|
|
9945
|
+
import path4 from "node:path";
|
|
9946
|
+
var REDACTED = "[REDACTED]", DEFAULT_MAX_RATE = 20, DEFAULT_RATE_WINDOW_MS = 1000, DEFAULT_MAX_FIELD_LENGTH = 200, DEFAULT_MAX_STACK_LINES = 30, DAY_FILE, RETAINED_DAYS = 7, SECRET_VALUE, KEY_EQ_VALUE, URL_QUERY, splitKey = (key) => key.split(/[_-]+|(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/).map((word) => word.toLowerCase()), SENSITIVE_WORDS, STACK_WORDS, escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), resolveStateDir = () => {
|
|
9947
|
+
const override = process.env.WORKFLOW_TOOLKIT_STATE;
|
|
9948
|
+
if (override)
|
|
9949
|
+
return override;
|
|
9950
|
+
const home = os.homedir();
|
|
9951
|
+
if (process.env.XDG_STATE_HOME)
|
|
9952
|
+
return path4.join(process.env.XDG_STATE_HOME, "workit");
|
|
9953
|
+
switch (os.platform()) {
|
|
9954
|
+
case "darwin":
|
|
9955
|
+
return path4.join(home, "Library", "Application Support", "workit");
|
|
9956
|
+
case "win32":
|
|
9957
|
+
return path4.join(process.env.LOCALAPPDATA ?? path4.join(home, "AppData", "Local"), "workit");
|
|
9958
|
+
default:
|
|
9959
|
+
return path4.join(process.env.HOME ?? home, ".local", "state", "workit");
|
|
9960
|
+
}
|
|
9961
|
+
}, homePattern, applyPatterns = (value) => {
|
|
9962
|
+
let out = value;
|
|
9963
|
+
out = out.replace(SECRET_VALUE, REDACTED);
|
|
9964
|
+
out = out.replace(KEY_EQ_VALUE, "$1$2[REDACTED]");
|
|
9965
|
+
out = out.replace(URL_QUERY, "$1?[REDACTED]");
|
|
9966
|
+
out = out.replace(homePattern, "~");
|
|
9967
|
+
if (process.platform === "win32")
|
|
9968
|
+
out = out.replace(/\\/g, "/");
|
|
9969
|
+
return out;
|
|
9970
|
+
}, redactString = (value, options) => {
|
|
9971
|
+
const out = applyPatterns(value);
|
|
9972
|
+
if (out.length > options.maxFieldLength) {
|
|
9973
|
+
return `${out.slice(0, options.maxFieldLength)}…`;
|
|
9974
|
+
}
|
|
9975
|
+
return out;
|
|
9976
|
+
}, boundStack = (value, options) => {
|
|
9977
|
+
if (typeof value !== "string")
|
|
9978
|
+
return redact(value, options);
|
|
9979
|
+
const lines = value.split(`
|
|
9980
|
+
`);
|
|
9981
|
+
const kept = lines.slice(0, options.maxStackLines).map(applyPatterns);
|
|
9982
|
+
const dropped = lines.length - kept.length;
|
|
9983
|
+
const body = dropped > 0 ? [...kept, ` ... ${dropped} more`] : kept;
|
|
9984
|
+
return body.join(`
|
|
9985
|
+
`);
|
|
9986
|
+
}, redact = (value, options = DEFAULT_OPTIONS) => {
|
|
9987
|
+
if (value === null || value === undefined)
|
|
9988
|
+
return null;
|
|
9989
|
+
if (typeof value === "boolean" || typeof value === "number")
|
|
9990
|
+
return value;
|
|
9991
|
+
if (typeof value === "string")
|
|
9992
|
+
return redactString(value, options);
|
|
9993
|
+
if (value instanceof Date)
|
|
9994
|
+
return value.toISOString();
|
|
9995
|
+
if (Array.isArray(value))
|
|
9996
|
+
return value.map((entry) => redact(entry, options));
|
|
9997
|
+
if (typeof value === "object") {
|
|
9998
|
+
const out = {};
|
|
9999
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
10000
|
+
const words = splitKey(key);
|
|
10001
|
+
if (words.some((word) => STACK_WORDS.has(word)))
|
|
10002
|
+
out[key] = boundStack(entry, options);
|
|
10003
|
+
else if (words.some((word) => SENSITIVE_WORDS.has(word)))
|
|
10004
|
+
out[key] = REDACTED;
|
|
10005
|
+
else
|
|
10006
|
+
out[key] = redact(entry, options);
|
|
10007
|
+
}
|
|
10008
|
+
return out;
|
|
10009
|
+
}
|
|
10010
|
+
return null;
|
|
10011
|
+
}, DEFAULT_OPTIONS, dailyFileName = (date) => {
|
|
10012
|
+
const y = date.getFullYear();
|
|
10013
|
+
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
10014
|
+
const d = String(date.getDate()).padStart(2, "0");
|
|
10015
|
+
return `workit-${y}-${m}-${d}.jsonl`;
|
|
10016
|
+
}, pruneOldFiles = (dir) => {
|
|
10017
|
+
try {
|
|
10018
|
+
const files = readdirSync(dir).filter((name) => DAY_FILE.test(name)).toSorted().toReversed();
|
|
10019
|
+
for (const file of files.slice(RETAINED_DAYS)) {
|
|
10020
|
+
try {
|
|
10021
|
+
unlinkSync(path4.join(dir, file));
|
|
10022
|
+
} catch {}
|
|
10023
|
+
}
|
|
10024
|
+
} catch {}
|
|
10025
|
+
}, createLogger = (options = {}) => {
|
|
10026
|
+
const stateDir = options.stateDir ?? resolveStateDir();
|
|
10027
|
+
const logDir = path4.join(stateDir, "logs");
|
|
10028
|
+
const now = options.now ?? (() => new Date);
|
|
10029
|
+
const maxRate = options.maxRate ?? DEFAULT_MAX_RATE;
|
|
10030
|
+
const rateWindowMs = options.rateWindowMs ?? DEFAULT_RATE_WINDOW_MS;
|
|
10031
|
+
const redactOptions = {
|
|
10032
|
+
maxFieldLength: options.maxFieldLength ?? DEFAULT_MAX_FIELD_LENGTH,
|
|
10033
|
+
maxStackLines: options.maxStackLines ?? DEFAULT_MAX_STACK_LINES
|
|
10034
|
+
};
|
|
10035
|
+
let windowStart = 0;
|
|
10036
|
+
const windowCounts = { debug: 0, info: 0, warn: 0, error: 0 };
|
|
10037
|
+
const emit = (level, message, context) => {
|
|
10038
|
+
const date = now();
|
|
10039
|
+
const elapsed = date.getTime() - windowStart;
|
|
10040
|
+
if (elapsed >= rateWindowMs || windowStart === 0) {
|
|
10041
|
+
windowStart = date.getTime();
|
|
10042
|
+
windowCounts.debug = 0;
|
|
10043
|
+
windowCounts.info = 0;
|
|
10044
|
+
windowCounts.warn = 0;
|
|
10045
|
+
windowCounts.error = 0;
|
|
10046
|
+
}
|
|
10047
|
+
windowCounts[level] += 1;
|
|
10048
|
+
if (windowCounts[level] > maxRate)
|
|
10049
|
+
return;
|
|
10050
|
+
let event = null;
|
|
10051
|
+
try {
|
|
10052
|
+
event = {
|
|
10053
|
+
level,
|
|
10054
|
+
time: date.toISOString(),
|
|
10055
|
+
message: redactString(message, redactOptions),
|
|
10056
|
+
context: redact(context ?? {}, redactOptions)
|
|
10057
|
+
};
|
|
10058
|
+
const line = `${JSON.stringify(event)}
|
|
10059
|
+
`;
|
|
10060
|
+
mkdirSync(logDir, { recursive: true, mode: 448 });
|
|
10061
|
+
appendFileSync(path4.join(logDir, dailyFileName(date)), line, { mode: 384 });
|
|
10062
|
+
pruneOldFiles(logDir);
|
|
10063
|
+
} catch {
|
|
10064
|
+
try {
|
|
10065
|
+
event = {
|
|
10066
|
+
level,
|
|
10067
|
+
time: date.toISOString(),
|
|
10068
|
+
message: redactString(message, redactOptions),
|
|
10069
|
+
context: { redaction_failed: true }
|
|
10070
|
+
};
|
|
10071
|
+
mkdirSync(logDir, { recursive: true, mode: 448 });
|
|
10072
|
+
appendFileSync(path4.join(logDir, dailyFileName(date)), `${JSON.stringify(event)}
|
|
10073
|
+
`, {
|
|
10074
|
+
mode: 384
|
|
10075
|
+
});
|
|
10076
|
+
} catch {}
|
|
10077
|
+
}
|
|
10078
|
+
for (const sink of [options.appLog, options.stderr]) {
|
|
10079
|
+
if (!sink)
|
|
10080
|
+
continue;
|
|
10081
|
+
if (!event)
|
|
10082
|
+
continue;
|
|
10083
|
+
try {
|
|
10084
|
+
sink(event);
|
|
10085
|
+
} catch {}
|
|
10086
|
+
}
|
|
10087
|
+
};
|
|
10088
|
+
return {
|
|
10089
|
+
debug: (message, context) => emit("debug", message, context),
|
|
10090
|
+
info: (message, context) => emit("info", message, context),
|
|
10091
|
+
warn: (message, context) => emit("warn", message, context),
|
|
10092
|
+
error: (message, context) => emit("error", message, context),
|
|
10093
|
+
guard: (name, fn) => {
|
|
10094
|
+
try {
|
|
10095
|
+
return fn();
|
|
10096
|
+
} catch (err) {
|
|
10097
|
+
emit("warn", "detector_failed", {
|
|
10098
|
+
detector: name,
|
|
10099
|
+
error: err instanceof Error ? err.message : String(err)
|
|
10100
|
+
});
|
|
10101
|
+
return;
|
|
10102
|
+
}
|
|
10103
|
+
}
|
|
10104
|
+
};
|
|
10105
|
+
};
|
|
10106
|
+
var init_logger = __esm(() => {
|
|
10107
|
+
DAY_FILE = /^workit-\d{4}-\d{2}-\d{2}\.jsonl$/;
|
|
10108
|
+
SECRET_VALUE = /\b(?:Bearer|Basic|Digest|Token)\s+\S+/gi;
|
|
10109
|
+
KEY_EQ_VALUE = /\b([A-Za-z0-9_-]*(?:token|secret|password|passwd|apikey|api[_-]?key|authorization|credential|bearer)[A-Za-z0-9_-]*)([:=]\s*).+/gi;
|
|
10110
|
+
URL_QUERY = /(https?:\/\/[^?#\s]+)\?[^#\s]*/g;
|
|
10111
|
+
SENSITIVE_WORDS = new Set([
|
|
10112
|
+
"token",
|
|
10113
|
+
"secret",
|
|
10114
|
+
"password",
|
|
10115
|
+
"passwd",
|
|
10116
|
+
"authorization",
|
|
10117
|
+
"credential",
|
|
10118
|
+
"credentials",
|
|
10119
|
+
"cookie",
|
|
10120
|
+
"api",
|
|
10121
|
+
"apikey",
|
|
10122
|
+
"apitoken",
|
|
10123
|
+
"accesstoken",
|
|
10124
|
+
"clientsecret",
|
|
10125
|
+
"refreshtoken",
|
|
10126
|
+
"key",
|
|
10127
|
+
"bearer",
|
|
10128
|
+
"prompt",
|
|
10129
|
+
"message",
|
|
10130
|
+
"messages",
|
|
10131
|
+
"content",
|
|
10132
|
+
"body",
|
|
10133
|
+
"args",
|
|
10134
|
+
"argument",
|
|
10135
|
+
"result",
|
|
10136
|
+
"results",
|
|
10137
|
+
"output",
|
|
10138
|
+
"issue",
|
|
10139
|
+
"description",
|
|
10140
|
+
"summary",
|
|
10141
|
+
"payload",
|
|
10142
|
+
"script",
|
|
10143
|
+
"command",
|
|
10144
|
+
"text",
|
|
10145
|
+
"query"
|
|
10146
|
+
]);
|
|
10147
|
+
STACK_WORDS = new Set(["stack", "stacktrace", "trace"]);
|
|
10148
|
+
homePattern = new RegExp(`${escapeRegExp(os.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
|
|
10149
|
+
DEFAULT_OPTIONS = {
|
|
10150
|
+
maxFieldLength: DEFAULT_MAX_FIELD_LENGTH,
|
|
10151
|
+
maxStackLines: DEFAULT_MAX_STACK_LINES
|
|
10152
|
+
};
|
|
10153
|
+
});
|
|
10154
|
+
|
|
9942
10155
|
// packages/workit-core/src/store/paths.ts
|
|
9943
10156
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
9944
10157
|
import { createHash as createHash2 } from "node:crypto";
|
|
9945
10158
|
import fs5 from "node:fs";
|
|
9946
|
-
import
|
|
10159
|
+
import path5 from "node:path";
|
|
9947
10160
|
function fastGit(root) {
|
|
9948
10161
|
if (DISCOVERY_ENV.some((name) => process.env[name]))
|
|
9949
10162
|
return null;
|
|
@@ -9959,7 +10172,7 @@ function fastGit(root) {
|
|
|
9959
10172
|
if (device !== null && stat.dev !== device)
|
|
9960
10173
|
return { kind: "none" };
|
|
9961
10174
|
device = stat.dev;
|
|
9962
|
-
const dotGit =
|
|
10175
|
+
const dotGit = path5.join(dir, ".git");
|
|
9963
10176
|
let entry = null;
|
|
9964
10177
|
try {
|
|
9965
10178
|
entry = fs5.statSync(dotGit);
|
|
@@ -9980,19 +10193,19 @@ function fastGit(root) {
|
|
|
9980
10193
|
const match = /^gitdir: (.+)$/mu.exec(text);
|
|
9981
10194
|
if (!match)
|
|
9982
10195
|
return null;
|
|
9983
|
-
gitDir =
|
|
10196
|
+
gitDir = path5.resolve(dir, match[1].trim());
|
|
9984
10197
|
} else
|
|
9985
10198
|
return null;
|
|
9986
|
-
if (!fs5.existsSync(
|
|
10199
|
+
if (!fs5.existsSync(path5.join(gitDir, "HEAD")))
|
|
9987
10200
|
return null;
|
|
9988
10201
|
} else if (isGitDir(dir))
|
|
9989
10202
|
gitDir = dir;
|
|
9990
10203
|
if (gitDir) {
|
|
9991
10204
|
let common = gitDir;
|
|
9992
10205
|
try {
|
|
9993
|
-
common =
|
|
10206
|
+
common = path5.resolve(gitDir, fs5.readFileSync(path5.join(gitDir, "commondir"), "utf8").trim());
|
|
9994
10207
|
} catch {}
|
|
9995
|
-
if (!fs5.existsSync(
|
|
10208
|
+
if (!fs5.existsSync(path5.join(common, "objects")))
|
|
9996
10209
|
return null;
|
|
9997
10210
|
return {
|
|
9998
10211
|
kind: "repo",
|
|
@@ -10002,26 +10215,26 @@ function fastGit(root) {
|
|
|
10002
10215
|
reftable: reftableRefs(common)
|
|
10003
10216
|
};
|
|
10004
10217
|
}
|
|
10005
|
-
const parent =
|
|
10218
|
+
const parent = path5.dirname(dir);
|
|
10006
10219
|
if (parent === dir)
|
|
10007
10220
|
return { kind: "none" };
|
|
10008
10221
|
dir = parent;
|
|
10009
10222
|
}
|
|
10010
10223
|
}
|
|
10011
10224
|
function resolveStore(root) {
|
|
10012
|
-
root =
|
|
10225
|
+
root = path5.resolve(root);
|
|
10013
10226
|
let directory = false;
|
|
10014
10227
|
try {
|
|
10015
10228
|
directory = fs5.statSync(root).isDirectory();
|
|
10016
10229
|
} catch {}
|
|
10017
10230
|
if (!directory)
|
|
10018
|
-
return { dir:
|
|
10231
|
+
return { dir: path5.join(root, ".workit"), shared: false, top: null };
|
|
10019
10232
|
const fast = fastGit(root);
|
|
10020
10233
|
if (fast?.kind === "none")
|
|
10021
|
-
return { dir:
|
|
10234
|
+
return { dir: path5.join(root, ".workit"), shared: false, top: null };
|
|
10022
10235
|
if (fast?.kind === "repo")
|
|
10023
10236
|
return {
|
|
10024
|
-
dir:
|
|
10237
|
+
dir: path5.join(fast.common, "workit"),
|
|
10025
10238
|
shared: true,
|
|
10026
10239
|
top: fast.top,
|
|
10027
10240
|
git: { gitDir: fast.gitDir, top: fast.top, reftable: fast.reftable }
|
|
@@ -10031,13 +10244,13 @@ function resolveStore(root) {
|
|
|
10031
10244
|
if (run.ok && common) {
|
|
10032
10245
|
const top = gitRun(root, ["rev-parse", "--path-format=absolute", "--show-toplevel"]);
|
|
10033
10246
|
return {
|
|
10034
|
-
dir:
|
|
10247
|
+
dir: path5.join(real(path5.resolve(root, common)), "workit"),
|
|
10035
10248
|
shared: true,
|
|
10036
10249
|
top: top.ok && top.stdout.trim() ? real(top.stdout.trim()) : null
|
|
10037
10250
|
};
|
|
10038
10251
|
}
|
|
10039
10252
|
if (run.missing || !run.ok && NOT_A_REPO.test(run.stderr))
|
|
10040
|
-
return { dir:
|
|
10253
|
+
return { dir: path5.join(root, ".workit"), shared: false, top: null };
|
|
10041
10254
|
return new Error(`cannot locate the workit store: git rev-parse failed (${run.stderr.trim().split(`
|
|
10042
10255
|
`)[0] || "no output"}); fix the repository (git status should work here)`);
|
|
10043
10256
|
}
|
|
@@ -10048,7 +10261,7 @@ function resolveTaskKey(root, location) {
|
|
|
10048
10261
|
if (location.git && !location.git.reftable) {
|
|
10049
10262
|
let head = "";
|
|
10050
10263
|
try {
|
|
10051
|
-
head = fs5.readFileSync(
|
|
10264
|
+
head = fs5.readFileSync(path5.join(location.git.gitDir, "HEAD"), "utf8").trim();
|
|
10052
10265
|
} catch {}
|
|
10053
10266
|
const ref = /^ref: refs\/heads\/(.+)$/u.exec(head);
|
|
10054
10267
|
if (ref && ref[1] !== ".invalid")
|
|
@@ -10079,7 +10292,7 @@ function resolveTaskKey(root, location) {
|
|
|
10079
10292
|
return { key: `detached-${shortHash(real(worktree))}`, kind: "detached", branch: null };
|
|
10080
10293
|
}
|
|
10081
10294
|
function checkoutRootOf(root, location) {
|
|
10082
|
-
const resolved =
|
|
10295
|
+
const resolved = path5.resolve(root);
|
|
10083
10296
|
let canonical = resolved;
|
|
10084
10297
|
try {
|
|
10085
10298
|
canonical = fs5.realpathSync(resolved);
|
|
@@ -10110,13 +10323,13 @@ function branchRenames(location) {
|
|
|
10110
10323
|
}
|
|
10111
10324
|
for (const entry of entries)
|
|
10112
10325
|
if (entry.isDirectory())
|
|
10113
|
-
walk(
|
|
10326
|
+
walk(path5.join(dir, entry.name));
|
|
10114
10327
|
else
|
|
10115
|
-
files.push(
|
|
10328
|
+
files.push(path5.join(dir, entry.name));
|
|
10116
10329
|
};
|
|
10117
|
-
walk(
|
|
10330
|
+
walk(path5.join(path5.dirname(location.dir), "logs", "refs", "heads"));
|
|
10118
10331
|
if (location.git)
|
|
10119
|
-
files.push(
|
|
10332
|
+
files.push(path5.join(location.git.gitDir, "logs", "HEAD"));
|
|
10120
10333
|
const seen = new Set;
|
|
10121
10334
|
const renames = [];
|
|
10122
10335
|
for (const file of files) {
|
|
@@ -10177,20 +10390,20 @@ var NOT_A_REPO, gitRun = (cwd, args) => {
|
|
|
10177
10390
|
}
|
|
10178
10391
|
}, DISCOVERY_ENV, isGitDir = (dir) => {
|
|
10179
10392
|
try {
|
|
10180
|
-
return fs5.statSync(
|
|
10393
|
+
return fs5.statSync(path5.join(dir, "HEAD")).isFile() && fs5.statSync(path5.join(dir, "objects")).isDirectory() && fs5.statSync(path5.join(dir, "refs")).isDirectory();
|
|
10181
10394
|
} catch {
|
|
10182
10395
|
return false;
|
|
10183
10396
|
}
|
|
10184
10397
|
}, reftableRefs = (common) => {
|
|
10185
10398
|
try {
|
|
10186
|
-
return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs5.readFileSync(
|
|
10399
|
+
return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs5.readFileSync(path5.join(common, "config"), "utf8"));
|
|
10187
10400
|
} catch {
|
|
10188
10401
|
return false;
|
|
10189
10402
|
}
|
|
10190
10403
|
}, rebasingBranch = (gitDir) => {
|
|
10191
10404
|
for (const state of ["rebase-merge", "rebase-apply"]) {
|
|
10192
10405
|
try {
|
|
10193
|
-
const ref = fs5.readFileSync(
|
|
10406
|
+
const ref = fs5.readFileSync(path5.join(gitDir, state, "head-name"), "utf8").trim();
|
|
10194
10407
|
const match = /^refs\/heads\/(.+)$/u.exec(ref);
|
|
10195
10408
|
if (match)
|
|
10196
10409
|
return match[1];
|
|
@@ -10201,7 +10414,7 @@ var NOT_A_REPO, gitRun = (cwd, args) => {
|
|
|
10201
10414
|
let canonical = real(root);
|
|
10202
10415
|
if (process.platform === "win32")
|
|
10203
10416
|
canonical = canonical.toLowerCase();
|
|
10204
|
-
const base =
|
|
10417
|
+
const base = path5.basename(canonical).replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 40) || "root";
|
|
10205
10418
|
return `${base}-${shortHash(canonical)}`;
|
|
10206
10419
|
};
|
|
10207
10420
|
var init_paths = __esm(() => {
|
|
@@ -10218,7 +10431,7 @@ var init_paths = __esm(() => {
|
|
|
10218
10431
|
// packages/workit-core/src/host-session.ts
|
|
10219
10432
|
import { createHash as createHash3 } from "node:crypto";
|
|
10220
10433
|
import fs6 from "node:fs";
|
|
10221
|
-
import
|
|
10434
|
+
import path6 from "node:path";
|
|
10222
10435
|
function hostSessionFromEnv(env) {
|
|
10223
10436
|
const value = (key) => env[key]?.trim() || null;
|
|
10224
10437
|
let inner = null;
|
|
@@ -10247,6 +10460,36 @@ function realHostSession(env) {
|
|
|
10247
10460
|
const hosted = declared.host !== null && declared.host !== "cli";
|
|
10248
10461
|
return { ...declared, kind: hosted ? "declared" : "none" };
|
|
10249
10462
|
}
|
|
10463
|
+
function migrateRepoIssued(cwd) {
|
|
10464
|
+
if (cwd === undefined)
|
|
10465
|
+
return;
|
|
10466
|
+
let old;
|
|
10467
|
+
try {
|
|
10468
|
+
const location = resolveStore(cwd);
|
|
10469
|
+
if (location instanceof Error)
|
|
10470
|
+
return;
|
|
10471
|
+
old = path6.join(location.dir, "hooks", "issued");
|
|
10472
|
+
if (!fs6.existsSync(old))
|
|
10473
|
+
return;
|
|
10474
|
+
} catch {
|
|
10475
|
+
return;
|
|
10476
|
+
}
|
|
10477
|
+
for (const lead of safeList(old)) {
|
|
10478
|
+
try {
|
|
10479
|
+
const from = path6.join(old, lead);
|
|
10480
|
+
if (!fs6.statSync(from).isDirectory())
|
|
10481
|
+
continue;
|
|
10482
|
+
const to = path6.join(userIssuedRoot(), lead);
|
|
10483
|
+
fs6.mkdirSync(to, { recursive: true, mode: 448 });
|
|
10484
|
+
for (const id of fs6.readdirSync(from)) {
|
|
10485
|
+
fs6.copyFileSync(path6.join(from, id), path6.join(to, id));
|
|
10486
|
+
fs6.rmSync(path6.join(from, id), { force: true });
|
|
10487
|
+
}
|
|
10488
|
+
removeIfEmpty(from);
|
|
10489
|
+
} catch {}
|
|
10490
|
+
}
|
|
10491
|
+
removeIfEmpty(old);
|
|
10492
|
+
}
|
|
10250
10493
|
function sessionClaimRefusal(env, claimed, cwd) {
|
|
10251
10494
|
const real = realHostSession(env);
|
|
10252
10495
|
const host = family(real.host);
|
|
@@ -10271,9 +10514,9 @@ function sessionClaimRefusal(env, claimed, cwd) {
|
|
|
10271
10514
|
issuedClaim = true;
|
|
10272
10515
|
}
|
|
10273
10516
|
const error = `forged_session: ${claimed} is neither this shell's session (${real.session}) nor an id its host issued to a child of it`;
|
|
10274
|
-
if (issuedClaim && noIssuedRecord(cwd) && host !== null && PLUGIN_UPDATE[host])
|
|
10517
|
+
if (issuedClaim && noIssuedRecord(cwd, leadOf(claimed)) && host !== null && PLUGIN_UPDATE[host])
|
|
10275
10518
|
return {
|
|
10276
|
-
error: `${error}; no SubagentStart hook
|
|
10519
|
+
error: `${error}; no SubagentStart hook has recorded an issued id for this lead`,
|
|
10277
10520
|
unblock: `the workit plugin hook is probably older than this CLI and does not record the ids it issues: update the plugin (${PLUGIN_UPDATE[host]}), then relaunch the verifier so its SubagentStart runs the new hook (workit doctor checks this as hook_issued_sessions)`
|
|
10278
10521
|
};
|
|
10279
10522
|
const ownChildShell = host !== null && OWN_CHILD_SHELL.has(host);
|
|
@@ -10282,32 +10525,35 @@ function sessionClaimRefusal(env, claimed, cwd) {
|
|
|
10282
10525
|
unblock: ownChildShell ? `record the verdict from the verifier's own ${host === "pi" ? "Pi worker" : "OpenCode child session"} shell, where WORKIT_SESSION_ID is already set; never set WORKIT_SESSION_ID or --session by hand` : "never name a session yourself: spawn a verifier or reviewer subagent and pass exactly the --session its SubagentStart context names, or pass --as verifier"
|
|
10283
10526
|
};
|
|
10284
10527
|
}
|
|
10285
|
-
var HOST_SESSION_VARS, family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null, hash = (value) => createHash3("sha256").update(value).digest("hex").slice(0, 32),
|
|
10286
|
-
|
|
10287
|
-
|
|
10288
|
-
}
|
|
10289
|
-
|
|
10290
|
-
|
|
10291
|
-
},
|
|
10528
|
+
var HOST_SESSION_VARS, family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null, hash = (value) => createHash3("sha256").update(value).digest("hex").slice(0, 32), userIssuedRoot = () => path6.join(resolveStateDir(), "issued"), safeList = (dir) => {
|
|
10529
|
+
try {
|
|
10530
|
+
return fs6.readdirSync(dir);
|
|
10531
|
+
} catch {
|
|
10532
|
+
return [];
|
|
10533
|
+
}
|
|
10534
|
+
}, removeIfEmpty = (dir) => {
|
|
10535
|
+
try {
|
|
10536
|
+
fs6.rmdirSync(dir);
|
|
10537
|
+
} catch {}
|
|
10538
|
+
}, noIssuedRecord = (cwd, lead) => {
|
|
10292
10539
|
if (cwd === undefined)
|
|
10293
10540
|
return false;
|
|
10294
10541
|
try {
|
|
10295
|
-
|
|
10296
|
-
return
|
|
10542
|
+
migrateRepoIssued(cwd);
|
|
10543
|
+
return !fs6.existsSync(path6.join(userIssuedRoot(), hash(lead)));
|
|
10297
10544
|
} catch {
|
|
10298
10545
|
return false;
|
|
10299
10546
|
}
|
|
10300
10547
|
}, PLUGIN_UPDATE, wasIssued = (cwd, lead, claimed) => {
|
|
10301
|
-
|
|
10302
|
-
return false;
|
|
10548
|
+
migrateRepoIssued(cwd);
|
|
10303
10549
|
try {
|
|
10304
|
-
|
|
10305
|
-
return dir !== null && fs6.existsSync(path5.join(dir, hash(claimed)));
|
|
10550
|
+
return fs6.existsSync(path6.join(userIssuedRoot(), hash(lead), hash(claimed)));
|
|
10306
10551
|
} catch {
|
|
10307
10552
|
return false;
|
|
10308
10553
|
}
|
|
10309
10554
|
}, leadOf = (session) => session.slice(0, session.lastIndexOf(":")), OWN_CHILD_SHELL, HOST_SET_VARIABLE;
|
|
10310
10555
|
var init_host_session = __esm(() => {
|
|
10556
|
+
init_logger();
|
|
10311
10557
|
init_paths();
|
|
10312
10558
|
HOST_SESSION_VARS = [
|
|
10313
10559
|
["OPENCODE_SESSION_ID", () => "opencode"],
|
|
@@ -10331,19 +10577,19 @@ var init_host_session = __esm(() => {
|
|
|
10331
10577
|
|
|
10332
10578
|
// packages/workit-core/src/core/package-root.ts
|
|
10333
10579
|
import { existsSync } from "node:fs";
|
|
10334
|
-
import
|
|
10580
|
+
import path7 from "node:path";
|
|
10335
10581
|
import { fileURLToPath } from "node:url";
|
|
10336
10582
|
var packageRoot = () => {
|
|
10337
|
-
let dir =
|
|
10583
|
+
let dir = path7.dirname(fileURLToPath(import.meta.url));
|
|
10338
10584
|
while (true) {
|
|
10339
|
-
const parent =
|
|
10340
|
-
if (existsSync(
|
|
10585
|
+
const parent = path7.dirname(dir);
|
|
10586
|
+
if (existsSync(path7.join(dir, "package.json")) || parent === dir)
|
|
10341
10587
|
return dir;
|
|
10342
10588
|
dir = parent;
|
|
10343
10589
|
}
|
|
10344
10590
|
}, assetRoot = () => {
|
|
10345
10591
|
const root = packageRoot();
|
|
10346
|
-
const assets =
|
|
10592
|
+
const assets = path7.join(root, "assets");
|
|
10347
10593
|
return existsSync(assets) ? assets : root;
|
|
10348
10594
|
};
|
|
10349
10595
|
var init_package_root = () => {};
|
|
@@ -10352,7 +10598,7 @@ var init_package_root = () => {};
|
|
|
10352
10598
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
10353
10599
|
import fs7 from "node:fs";
|
|
10354
10600
|
import { hostname } from "node:os";
|
|
10355
|
-
import
|
|
10601
|
+
import path8 from "node:path";
|
|
10356
10602
|
var metadataLockSchema, FOREIGN_LOCK_TTL_MS, UNREADABLE_LOCK_TTL_MS = 30000, RECLAIM_GUARD_TTL_MS = 30000, defaultLockTimeoutMs = 250, defaultLockTimeout = () => defaultLockTimeoutMs, setDefaultLockTimeout = (ms) => {
|
|
10357
10603
|
defaultLockTimeoutMs = ms;
|
|
10358
10604
|
}, parseMetadataLock = (raw) => {
|
|
@@ -10456,13 +10702,13 @@ var metadataLockSchema, FOREIGN_LOCK_TTL_MS, UNREADABLE_LOCK_TTL_MS = 30000, REC
|
|
|
10456
10702
|
return null;
|
|
10457
10703
|
}
|
|
10458
10704
|
}, lockPathFor = (root) => {
|
|
10459
|
-
let real =
|
|
10705
|
+
let real = path8.resolve(root);
|
|
10460
10706
|
try {
|
|
10461
10707
|
real = fs7.realpathSync(real);
|
|
10462
10708
|
} catch {}
|
|
10463
10709
|
const location = resolveStore(real);
|
|
10464
|
-
const dir = location instanceof Error ?
|
|
10465
|
-
return
|
|
10710
|
+
const dir = location instanceof Error ? path8.join(real, ".workit") : location.dir;
|
|
10711
|
+
return path8.join(dir, "checkouts", checkoutSlug(real), "metadata.lock");
|
|
10466
10712
|
}, clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
|
|
10467
10713
|
const guard = `${lockPath}.reclaim`;
|
|
10468
10714
|
const age = ageOf(guard, nowMs);
|
|
@@ -10549,7 +10795,7 @@ var init_store_lock = __esm(() => {
|
|
|
10549
10795
|
// packages/workit-core/src/store/event-log.ts
|
|
10550
10796
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
10551
10797
|
import fs8 from "node:fs";
|
|
10552
|
-
import
|
|
10798
|
+
import path9 from "node:path";
|
|
10553
10799
|
function readLog(file, from = 0, expectSeq = null) {
|
|
10554
10800
|
let fd;
|
|
10555
10801
|
try {
|
|
@@ -10622,7 +10868,7 @@ function appendEvent(file, event, validEnd) {
|
|
|
10622
10868
|
fs8.closeSync(fd);
|
|
10623
10869
|
}
|
|
10624
10870
|
if (created)
|
|
10625
|
-
fsyncDirectory(
|
|
10871
|
+
fsyncDirectory(path9.dirname(file));
|
|
10626
10872
|
return validEnd;
|
|
10627
10873
|
}
|
|
10628
10874
|
function replaceFile(file, bytes, durable) {
|
|
@@ -10638,7 +10884,7 @@ function replaceFile(file, bytes, durable) {
|
|
|
10638
10884
|
}
|
|
10639
10885
|
renameWithRetry(temporary, file);
|
|
10640
10886
|
if (durable)
|
|
10641
|
-
fsyncDirectory(
|
|
10887
|
+
fsyncDirectory(path9.dirname(file));
|
|
10642
10888
|
} catch (error) {
|
|
10643
10889
|
try {
|
|
10644
10890
|
fs8.unlinkSync(temporary);
|
|
@@ -10948,7 +11194,7 @@ var init_notes = __esm(() => {
|
|
|
10948
11194
|
// packages/workit-core/src/core/task-store.ts
|
|
10949
11195
|
import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
|
|
10950
11196
|
import * as fs9 from "node:fs";
|
|
10951
|
-
import
|
|
11197
|
+
import path10 from "node:path";
|
|
10952
11198
|
|
|
10953
11199
|
class TaskStore {
|
|
10954
11200
|
root;
|
|
@@ -10960,7 +11206,7 @@ class TaskStore {
|
|
|
10960
11206
|
migrationChecked = false;
|
|
10961
11207
|
compactDue = new Set;
|
|
10962
11208
|
constructor(root, options = {}) {
|
|
10963
|
-
const resolved =
|
|
11209
|
+
const resolved = path10.resolve(root);
|
|
10964
11210
|
this.origin = fs9.existsSync(resolved) ? fs9.realpathSync(resolved) : resolved;
|
|
10965
11211
|
this.located = resolveStore(this.origin);
|
|
10966
11212
|
this.root = checkoutRootOf(this.origin, this.located);
|
|
@@ -10969,7 +11215,7 @@ class TaskStore {
|
|
|
10969
11215
|
this.migrateOnRead = options.migrateOnRead;
|
|
10970
11216
|
}
|
|
10971
11217
|
location() {
|
|
10972
|
-
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs9.existsSync(
|
|
11218
|
+
if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs9.existsSync(path10.join(this.root, ".git"))) {
|
|
10973
11219
|
this.located = undefined;
|
|
10974
11220
|
this.migrationChecked = false;
|
|
10975
11221
|
}
|
|
@@ -11125,7 +11371,7 @@ class TaskStore {
|
|
|
11125
11371
|
const moved = this.bindLocked(again.data.id, key, input.now);
|
|
11126
11372
|
return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
|
|
11127
11373
|
}
|
|
11128
|
-
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" :
|
|
11374
|
+
const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path10.basename(this.root) || this.root;
|
|
11129
11375
|
const intent = {
|
|
11130
11376
|
objective: input.objective?.trim() || `Work on ${label}`,
|
|
11131
11377
|
scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
|
|
@@ -11185,14 +11431,14 @@ class TaskStore {
|
|
|
11185
11431
|
const branch = key.ok ? key.data.branch : null;
|
|
11186
11432
|
if (!branch || !location.ok)
|
|
11187
11433
|
return success(null, null, null);
|
|
11188
|
-
const common =
|
|
11189
|
-
const heads =
|
|
11190
|
-
const headLog = location.data.git ?
|
|
11434
|
+
const common = path10.dirname(location.data.dir);
|
|
11435
|
+
const heads = path10.join(common, "logs", "refs", "heads");
|
|
11436
|
+
const headLog = location.data.git ? path10.join(location.data.git.gitDir, "logs", "HEAD") : "";
|
|
11191
11437
|
const signatures = [
|
|
11192
|
-
|
|
11438
|
+
path10.join(heads, branch),
|
|
11193
11439
|
headLog,
|
|
11194
11440
|
heads,
|
|
11195
|
-
|
|
11441
|
+
path10.join(common, "packed-refs")
|
|
11196
11442
|
].map((file) => fileSignature(file) ?? "-");
|
|
11197
11443
|
const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
|
|
11198
11444
|
let resolved = origins.get(slot);
|
|
@@ -11204,7 +11450,7 @@ class TaskStore {
|
|
|
11204
11450
|
aliases.push(rename.from);
|
|
11205
11451
|
const start = branchStart(location.data, branch, aliases);
|
|
11206
11452
|
const slash = start?.indexOf("/") ?? -1;
|
|
11207
|
-
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(
|
|
11453
|
+
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path10.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
|
|
11208
11454
|
const name = start && (remote ? start.slice(slash + 1) : start);
|
|
11209
11455
|
resolved = {
|
|
11210
11456
|
signature: signatures.join("|"),
|
|
@@ -11442,13 +11688,13 @@ class TaskStore {
|
|
|
11442
11688
|
if (!ready.ok)
|
|
11443
11689
|
return ready;
|
|
11444
11690
|
const dirs = this.paths();
|
|
11445
|
-
const recovery =
|
|
11691
|
+
const recovery = path10.join(this.root, ".workit", "recovery");
|
|
11446
11692
|
if (fs9.existsSync(recovery)) {
|
|
11447
11693
|
let files = 0;
|
|
11448
11694
|
let bytes = 0;
|
|
11449
11695
|
try {
|
|
11450
11696
|
for (const name of fs9.readdirSync(recovery)) {
|
|
11451
|
-
const stat = fs9.lstatSync(
|
|
11697
|
+
const stat = fs9.lstatSync(path10.join(recovery, name));
|
|
11452
11698
|
if (!stat.isFile())
|
|
11453
11699
|
continue;
|
|
11454
11700
|
files += 1;
|
|
@@ -11501,7 +11747,7 @@ class TaskStore {
|
|
|
11501
11747
|
if (fs9.existsSync(dirs.blobs))
|
|
11502
11748
|
for (const name of fs9.readdirSync(dirs.blobs)) {
|
|
11503
11749
|
const digest = name.replace(/\.json$/, "");
|
|
11504
|
-
const file =
|
|
11750
|
+
const file = path10.join(dirs.blobs, name);
|
|
11505
11751
|
let stat;
|
|
11506
11752
|
try {
|
|
11507
11753
|
stat = fs9.lstatSync(file);
|
|
@@ -11519,7 +11765,7 @@ class TaskStore {
|
|
|
11519
11765
|
}
|
|
11520
11766
|
return success(null, null, null);
|
|
11521
11767
|
};
|
|
11522
|
-
const swept = dryRun ? sweep() : this.withLock(
|
|
11768
|
+
const swept = dryRun ? sweep() : this.withLock(path10.join(dirs.dir, "gc.lock"), sweep);
|
|
11523
11769
|
if (!swept.ok)
|
|
11524
11770
|
return swept;
|
|
11525
11771
|
const temporaryDirs = [
|
|
@@ -11527,7 +11773,7 @@ class TaskStore {
|
|
|
11527
11773
|
dirs.tasks,
|
|
11528
11774
|
dirs.blobs,
|
|
11529
11775
|
dirs.checkout,
|
|
11530
|
-
...names.map((name) =>
|
|
11776
|
+
...names.map((name) => path10.join(dirs.tasks, name))
|
|
11531
11777
|
];
|
|
11532
11778
|
for (const directory of temporaryDirs) {
|
|
11533
11779
|
if (!fs9.existsSync(directory))
|
|
@@ -11535,7 +11781,7 @@ class TaskStore {
|
|
|
11535
11781
|
for (const name of fs9.readdirSync(directory)) {
|
|
11536
11782
|
if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
|
|
11537
11783
|
continue;
|
|
11538
|
-
const file =
|
|
11784
|
+
const file = path10.join(directory, name);
|
|
11539
11785
|
try {
|
|
11540
11786
|
const stat = fs9.lstatSync(file);
|
|
11541
11787
|
if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
|
|
@@ -11552,30 +11798,30 @@ class TaskStore {
|
|
|
11552
11798
|
}
|
|
11553
11799
|
paths() {
|
|
11554
11800
|
const location = this.location();
|
|
11555
|
-
const dir = location.ok ? location.data.dir :
|
|
11556
|
-
const checkout =
|
|
11801
|
+
const dir = location.ok ? location.data.dir : path10.join(this.root, ".workit");
|
|
11802
|
+
const checkout = path10.join(dir, "checkouts", checkoutSlug(this.root));
|
|
11557
11803
|
return {
|
|
11558
11804
|
dir,
|
|
11559
11805
|
shared: location.ok ? location.data.shared : false,
|
|
11560
|
-
marker:
|
|
11806
|
+
marker: path10.join(dir, "store.json"),
|
|
11561
11807
|
checkout,
|
|
11562
|
-
workspace:
|
|
11563
|
-
checkoutLock:
|
|
11564
|
-
tasks:
|
|
11565
|
-
index:
|
|
11566
|
-
blobs:
|
|
11567
|
-
legacy:
|
|
11568
|
-
v2:
|
|
11808
|
+
workspace: path10.join(checkout, "workspace.json"),
|
|
11809
|
+
checkoutLock: path10.join(checkout, "metadata.lock"),
|
|
11810
|
+
tasks: path10.join(dir, "tasks"),
|
|
11811
|
+
index: path10.join(dir, "task-index.json"),
|
|
11812
|
+
blobs: path10.join(dir, "blobs", "candidates"),
|
|
11813
|
+
legacy: path10.join(dir, "legacy", checkoutSlug(this.root), "v2"),
|
|
11814
|
+
v2: path10.join(this.root, ".workit")
|
|
11569
11815
|
};
|
|
11570
11816
|
}
|
|
11571
11817
|
eventsPath(taskId) {
|
|
11572
|
-
return
|
|
11818
|
+
return path10.join(this.paths().tasks, taskId, "events.jsonl");
|
|
11573
11819
|
}
|
|
11574
11820
|
snapshotPath(taskId) {
|
|
11575
|
-
return
|
|
11821
|
+
return path10.join(this.paths().tasks, taskId, "snapshot.json");
|
|
11576
11822
|
}
|
|
11577
11823
|
taskLockPath(taskId) {
|
|
11578
|
-
return
|
|
11824
|
+
return path10.join(this.paths().tasks, taskId, "lock");
|
|
11579
11825
|
}
|
|
11580
11826
|
ready(write = false) {
|
|
11581
11827
|
const location = this.location();
|
|
@@ -11614,13 +11860,13 @@ class TaskStore {
|
|
|
11614
11860
|
return {
|
|
11615
11861
|
read: (digest) => {
|
|
11616
11862
|
try {
|
|
11617
|
-
return JSON.parse(fs9.readFileSync(
|
|
11863
|
+
return JSON.parse(fs9.readFileSync(path10.join(dir, `${digest}.json`), "utf8"));
|
|
11618
11864
|
} catch (error) {
|
|
11619
11865
|
throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
|
|
11620
11866
|
}
|
|
11621
11867
|
},
|
|
11622
11868
|
write: (digest, value) => {
|
|
11623
|
-
const file =
|
|
11869
|
+
const file = path10.join(dir, `${digest}.json`);
|
|
11624
11870
|
if (fs9.existsSync(file)) {
|
|
11625
11871
|
const stamp = new Date;
|
|
11626
11872
|
try {
|
|
@@ -11812,7 +12058,7 @@ class TaskStore {
|
|
|
11812
12058
|
}
|
|
11813
12059
|
}
|
|
11814
12060
|
openTask(record, type, key, legacy = null) {
|
|
11815
|
-
const dir =
|
|
12061
|
+
const dir = path10.join(this.paths().tasks, record.id);
|
|
11816
12062
|
fs9.mkdirSync(dir, { recursive: true });
|
|
11817
12063
|
const blobs = this.blobStore();
|
|
11818
12064
|
const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
|
|
@@ -11905,7 +12151,7 @@ class TaskStore {
|
|
|
11905
12151
|
}
|
|
11906
12152
|
withKeyLock(key, operation) {
|
|
11907
12153
|
const digest = createHash5("sha256").update(key).digest("hex").slice(0, 32);
|
|
11908
|
-
return this.withLock(
|
|
12154
|
+
return this.withLock(path10.join(this.paths().dir, "keys", `${digest}.lock`), operation);
|
|
11909
12155
|
}
|
|
11910
12156
|
bindLocked(taskId, key, timestamp) {
|
|
11911
12157
|
const current = this.readWorkspace();
|
|
@@ -12046,7 +12292,7 @@ class TaskStore {
|
|
|
12046
12292
|
writeWorkspace(record) {
|
|
12047
12293
|
const file = this.paths().workspace;
|
|
12048
12294
|
try {
|
|
12049
|
-
fs9.mkdirSync(
|
|
12295
|
+
fs9.mkdirSync(path10.dirname(file), { recursive: true });
|
|
12050
12296
|
replaceFile(file, `${canonicalJson(record)}
|
|
12051
12297
|
`, true);
|
|
12052
12298
|
return success(null, null, null);
|
|
@@ -12124,23 +12370,23 @@ class TaskStore {
|
|
|
12124
12370
|
return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
|
|
12125
12371
|
}
|
|
12126
12372
|
v2Dirs() {
|
|
12127
|
-
return [...new Set([this.root, this.origin])].map((dir) =>
|
|
12373
|
+
return [...new Set([this.root, this.origin])].map((dir) => path10.join(dir, ".workit"));
|
|
12128
12374
|
}
|
|
12129
12375
|
pendingMigrations() {
|
|
12130
12376
|
return this.v2Dirs().filter((dir) => {
|
|
12131
|
-
const raw = this.readV2Workspace(
|
|
12377
|
+
const raw = this.readV2Workspace(path10.join(dir, "workspace.json"));
|
|
12132
12378
|
return raw !== null && raw !== TOMBSTONE;
|
|
12133
12379
|
});
|
|
12134
12380
|
}
|
|
12135
12381
|
migrateV2(v2) {
|
|
12136
12382
|
const dirs = this.paths();
|
|
12137
|
-
const v2Workspace =
|
|
12138
|
-
const v2Tasks =
|
|
12139
|
-
const legacy =
|
|
12383
|
+
const v2Workspace = path10.join(v2, "workspace.json");
|
|
12384
|
+
const v2Tasks = path10.join(v2, "tasks");
|
|
12385
|
+
const legacy = path10.join(dirs.dir, "legacy", checkoutSlug(path10.dirname(v2)), "v2");
|
|
12140
12386
|
const raw = this.readV2Workspace(v2Workspace);
|
|
12141
12387
|
if (raw === null || raw === TOMBSTONE)
|
|
12142
12388
|
return success(null, null, null);
|
|
12143
|
-
return this.withLock(dirs.checkoutLock, () => this.withLock(
|
|
12389
|
+
return this.withLock(dirs.checkoutLock, () => this.withLock(path10.join(v2, "metadata.lock"), () => {
|
|
12144
12390
|
const workspace = this.readV2Workspace(v2Workspace);
|
|
12145
12391
|
if (workspace === null || workspace === TOMBSTONE)
|
|
12146
12392
|
return success(null, null, null);
|
|
@@ -12149,7 +12395,7 @@ class TaskStore {
|
|
|
12149
12395
|
return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
|
|
12150
12396
|
let files = [];
|
|
12151
12397
|
try {
|
|
12152
|
-
files = fs9.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(
|
|
12398
|
+
files = fs9.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path10.join(v2Tasks, name)));
|
|
12153
12399
|
} catch {}
|
|
12154
12400
|
const current = this.readWorkspace();
|
|
12155
12401
|
if (!current.ok)
|
|
@@ -12167,21 +12413,21 @@ class TaskStore {
|
|
|
12167
12413
|
let skipped = 0;
|
|
12168
12414
|
const done = new Map;
|
|
12169
12415
|
try {
|
|
12170
|
-
fs9.mkdirSync(
|
|
12416
|
+
fs9.mkdirSync(path10.join(legacy, "tasks"), { recursive: true });
|
|
12171
12417
|
for (const name of ["workspace.json", "index.json"])
|
|
12172
|
-
if (fs9.existsSync(
|
|
12173
|
-
fs9.copyFileSync(
|
|
12418
|
+
if (fs9.existsSync(path10.join(v2, name)))
|
|
12419
|
+
fs9.copyFileSync(path10.join(v2, name), path10.join(legacy, name));
|
|
12174
12420
|
for (const name of files) {
|
|
12175
12421
|
const id = name.slice(0, -5);
|
|
12176
|
-
const file =
|
|
12422
|
+
const file = path10.join(v2Tasks, name);
|
|
12177
12423
|
const bytes = fs9.readFileSync(file, "utf8");
|
|
12178
12424
|
const digest = sha256(bytes);
|
|
12179
|
-
fs9.writeFileSync(
|
|
12425
|
+
fs9.writeFileSync(path10.join(legacy, "tasks", name), bytes);
|
|
12180
12426
|
const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
|
|
12181
12427
|
if (!parsed.ok || parsed.data.id !== id)
|
|
12182
12428
|
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 });
|
|
12183
12429
|
const record = { ...parsed.data, workspaceId: target.id };
|
|
12184
|
-
const origin = { path:
|
|
12430
|
+
const origin = { path: path10.join(legacy, "tasks", name), digest };
|
|
12185
12431
|
if (!fs9.existsSync(this.eventsPath(id))) {
|
|
12186
12432
|
this.openTask(record, "migrated.from_v2", null, origin);
|
|
12187
12433
|
migrated += 1;
|
|
@@ -12217,7 +12463,7 @@ class TaskStore {
|
|
|
12217
12463
|
try {
|
|
12218
12464
|
let complete = true;
|
|
12219
12465
|
for (const [name, digest] of done) {
|
|
12220
|
-
const file =
|
|
12466
|
+
const file = path10.join(v2Tasks, name);
|
|
12221
12467
|
if (sha256(fs9.readFileSync(file, "utf8")) !== digest) {
|
|
12222
12468
|
complete = false;
|
|
12223
12469
|
continue;
|
|
@@ -12228,7 +12474,7 @@ class TaskStore {
|
|
|
12228
12474
|
return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
|
|
12229
12475
|
path: v2
|
|
12230
12476
|
});
|
|
12231
|
-
fs9.rmSync(
|
|
12477
|
+
fs9.rmSync(path10.join(v2, "index.json"), { force: true });
|
|
12232
12478
|
this.writeTombstone(v2, target.id);
|
|
12233
12479
|
} catch (error) {
|
|
12234
12480
|
return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
|
|
@@ -12248,7 +12494,7 @@ class TaskStore {
|
|
|
12248
12494
|
}
|
|
12249
12495
|
adoptLocalStore() {
|
|
12250
12496
|
const dirs = this.paths();
|
|
12251
|
-
const marker =
|
|
12497
|
+
const marker = path10.join(dirs.v2, "store.json");
|
|
12252
12498
|
if (!dirs.shared || !fs9.existsSync(marker))
|
|
12253
12499
|
return success(null, null, null);
|
|
12254
12500
|
return this.withLock(dirs.checkoutLock, () => {
|
|
@@ -12258,7 +12504,7 @@ class TaskStore {
|
|
|
12258
12504
|
try {
|
|
12259
12505
|
const merge = (from, to) => {
|
|
12260
12506
|
if (!fs9.existsSync(to)) {
|
|
12261
|
-
fs9.mkdirSync(
|
|
12507
|
+
fs9.mkdirSync(path10.dirname(to), { recursive: true });
|
|
12262
12508
|
try {
|
|
12263
12509
|
fs9.renameSync(from, to);
|
|
12264
12510
|
} catch {
|
|
@@ -12269,24 +12515,24 @@ class TaskStore {
|
|
|
12269
12515
|
}
|
|
12270
12516
|
if (fs9.lstatSync(from).isDirectory() && fs9.lstatSync(to).isDirectory())
|
|
12271
12517
|
for (const name of fs9.readdirSync(from))
|
|
12272
|
-
merge(
|
|
12518
|
+
merge(path10.join(from, name), path10.join(to, name));
|
|
12273
12519
|
};
|
|
12274
12520
|
for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
|
|
12275
|
-
const from =
|
|
12521
|
+
const from = path10.join(dirs.v2, sub);
|
|
12276
12522
|
if (!fs9.existsSync(from))
|
|
12277
12523
|
continue;
|
|
12278
12524
|
if (sub === "tasks") {
|
|
12279
12525
|
for (const name of fs9.readdirSync(from))
|
|
12280
|
-
if (validId(name) && !fs9.existsSync(
|
|
12526
|
+
if (validId(name) && !fs9.existsSync(path10.join(dirs.dir, sub, name)))
|
|
12281
12527
|
tasks += 1;
|
|
12282
12528
|
}
|
|
12283
|
-
merge(from,
|
|
12529
|
+
merge(from, path10.join(dirs.dir, sub));
|
|
12284
12530
|
}
|
|
12285
|
-
const ledger =
|
|
12286
|
-
if (fs9.existsSync(ledger) && !fs9.existsSync(
|
|
12287
|
-
merge(ledger,
|
|
12288
|
-
fs9.rmSync(
|
|
12289
|
-
fs9.renameSync(marker,
|
|
12531
|
+
const ledger = path10.join(dirs.v2, "ledger");
|
|
12532
|
+
if (fs9.existsSync(ledger) && !fs9.existsSync(path10.join(dirs.dir, "ledger")))
|
|
12533
|
+
merge(ledger, path10.join(dirs.dir, "ledger"));
|
|
12534
|
+
fs9.rmSync(path10.join(dirs.v2, "task-index.json"), { force: true });
|
|
12535
|
+
fs9.renameSync(marker, path10.join(dirs.v2, "store.moved.json"));
|
|
12290
12536
|
} catch (error) {
|
|
12291
12537
|
return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
|
|
12292
12538
|
path: dirs.v2
|
|
@@ -12324,7 +12570,7 @@ class TaskStore {
|
|
|
12324
12570
|
schemaVersion: SCHEMA_VERSION,
|
|
12325
12571
|
id: workspaceId ?? newId(),
|
|
12326
12572
|
revision: newRevision(),
|
|
12327
|
-
root:
|
|
12573
|
+
root: path10.dirname(v2),
|
|
12328
12574
|
runtime: { createdWith: version, updatedWith: version },
|
|
12329
12575
|
writer: null,
|
|
12330
12576
|
store: {
|
|
@@ -12336,11 +12582,11 @@ class TaskStore {
|
|
|
12336
12582
|
critical: ["store"]
|
|
12337
12583
|
};
|
|
12338
12584
|
fs9.mkdirSync(v2, { recursive: true });
|
|
12339
|
-
const ignore =
|
|
12585
|
+
const ignore = path10.join(v2, ".gitignore");
|
|
12340
12586
|
if (!fs9.existsSync(ignore))
|
|
12341
12587
|
fs9.writeFileSync(ignore, `*
|
|
12342
12588
|
`);
|
|
12343
|
-
replaceFile(
|
|
12589
|
+
replaceFile(path10.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
|
|
12344
12590
|
`, true);
|
|
12345
12591
|
}
|
|
12346
12592
|
stubFor(taskId) {
|
|
@@ -12364,8 +12610,8 @@ class TaskStore {
|
|
|
12364
12610
|
}
|
|
12365
12611
|
writeTombstoneIfMissing() {
|
|
12366
12612
|
try {
|
|
12367
|
-
const v2 =
|
|
12368
|
-
if (fs9.existsSync(
|
|
12613
|
+
const v2 = path10.join(this.root, ".workit");
|
|
12614
|
+
if (fs9.existsSync(path10.join(v2, "workspace.json")))
|
|
12369
12615
|
return;
|
|
12370
12616
|
let id = null;
|
|
12371
12617
|
try {
|
|
@@ -12382,7 +12628,7 @@ class TaskStore {
|
|
|
12382
12628
|
replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
|
|
12383
12629
|
`, false);
|
|
12384
12630
|
if (!dirs.shared) {
|
|
12385
|
-
const ignore =
|
|
12631
|
+
const ignore = path10.join(dirs.dir, ".gitignore");
|
|
12386
12632
|
let current = null;
|
|
12387
12633
|
try {
|
|
12388
12634
|
current = fs9.readFileSync(ignore, "utf8");
|
|
@@ -12442,7 +12688,7 @@ class TaskStore {
|
|
|
12442
12688
|
withLock(lockPath, operation) {
|
|
12443
12689
|
try {
|
|
12444
12690
|
this.initializeStorage();
|
|
12445
|
-
fs9.mkdirSync(
|
|
12691
|
+
fs9.mkdirSync(path10.dirname(lockPath), { recursive: true });
|
|
12446
12692
|
} catch (error) {
|
|
12447
12693
|
return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
|
|
12448
12694
|
path: this.paths().dir
|
|
@@ -12505,7 +12751,7 @@ class TaskStore {
|
|
|
12505
12751
|
if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
|
|
12506
12752
|
return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
|
|
12507
12753
|
path: lockPath,
|
|
12508
|
-
guidance: `Check permissions and the read-only attribute on ${
|
|
12754
|
+
guidance: `Check permissions and the read-only attribute on ${path10.dirname(lockPath)}.`
|
|
12509
12755
|
});
|
|
12510
12756
|
if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
|
|
12511
12757
|
let lock = null;
|
|
@@ -12563,7 +12809,7 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
|
|
|
12563
12809
|
}, now = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z"), oldestFirst = (left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id), cachedRuntimeVersion = null, runtimeVersion = () => {
|
|
12564
12810
|
if (cachedRuntimeVersion === null) {
|
|
12565
12811
|
try {
|
|
12566
|
-
const pkg = JSON.parse(fs9.readFileSync(
|
|
12812
|
+
const pkg = JSON.parse(fs9.readFileSync(path10.join(packageRoot(), "package.json"), "utf8"));
|
|
12567
12813
|
cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
|
|
12568
12814
|
} catch {
|
|
12569
12815
|
cachedRuntimeVersion = "0.0.0";
|
|
@@ -12590,7 +12836,7 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
|
|
|
12590
12836
|
return "";
|
|
12591
12837
|
}
|
|
12592
12838
|
}, REFLOG_LINE, branchStart = (location, branch, aliases) => {
|
|
12593
|
-
const first = readText(
|
|
12839
|
+
const first = readText(path10.join(path10.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
|
|
12594
12840
|
`, 1)[0];
|
|
12595
12841
|
const created = REFLOG_LINE.exec(first);
|
|
12596
12842
|
const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
|
|
@@ -12600,7 +12846,7 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
|
|
|
12600
12846
|
return start;
|
|
12601
12847
|
if (!location.git)
|
|
12602
12848
|
return null;
|
|
12603
|
-
for (const line of readText(
|
|
12849
|
+
for (const line of readText(path10.join(location.git.gitDir, "logs", "HEAD")).split(`
|
|
12604
12850
|
`)) {
|
|
12605
12851
|
const entry = REFLOG_LINE.exec(line);
|
|
12606
12852
|
const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
|
|
@@ -12609,12 +12855,12 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
|
|
|
12609
12855
|
return moved[1];
|
|
12610
12856
|
}
|
|
12611
12857
|
return null;
|
|
12612
|
-
}, refExists = (common, ref) => fs9.existsSync(
|
|
12858
|
+
}, refExists = (common, ref) => fs9.existsSync(path10.join(common, ref)) || readText(path10.join(common, "packed-refs")).includes(` ${ref}
|
|
12613
12859
|
`), origins, sameDirectoryIdentity = (left, right) => {
|
|
12614
|
-
if (!
|
|
12860
|
+
if (!path10.isAbsolute(left) || !path10.isAbsolute(right))
|
|
12615
12861
|
return false;
|
|
12616
|
-
const normalizedLeft =
|
|
12617
|
-
const normalizedRight =
|
|
12862
|
+
const normalizedLeft = path10.resolve(left);
|
|
12863
|
+
const normalizedRight = path10.resolve(right);
|
|
12618
12864
|
try {
|
|
12619
12865
|
const a = fs9.statSync(normalizedLeft, { bigint: true });
|
|
12620
12866
|
const b = fs9.statSync(normalizedRight, { bigint: true });
|
|
@@ -12673,8 +12919,8 @@ var init_workers = __esm(() => {
|
|
|
12673
12919
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
12674
12920
|
import { createHash as createHash6 } from "node:crypto";
|
|
12675
12921
|
import fs10 from "node:fs";
|
|
12676
|
-
import
|
|
12677
|
-
import
|
|
12922
|
+
import os2 from "node:os";
|
|
12923
|
+
import path11 from "node:path";
|
|
12678
12924
|
function networkGitInvocation(cwd, args, timeoutMs) {
|
|
12679
12925
|
const seconds = Math.max(1, Math.ceil(timeoutMs / 1000));
|
|
12680
12926
|
const env = {
|
|
@@ -12732,9 +12978,9 @@ function worktreeTree(cwd, options = {}) {
|
|
|
12732
12978
|
return null;
|
|
12733
12979
|
const cap = options.maxUntrackedBytes ?? MAX_UNTRACKED_BYTES;
|
|
12734
12980
|
const timeoutMs = options.timeoutMs ?? GIT_TIMEOUTS.worktree;
|
|
12735
|
-
const scratch = fs10.mkdtempSync(
|
|
12981
|
+
const scratch = fs10.mkdtempSync(path11.join(os2.tmpdir(), "workit-tree-"));
|
|
12736
12982
|
try {
|
|
12737
|
-
const tempIndex =
|
|
12983
|
+
const tempIndex = path11.join(scratch, "index");
|
|
12738
12984
|
if (fs10.existsSync(indexPath))
|
|
12739
12985
|
seedIndex(indexPath, tempIndex);
|
|
12740
12986
|
const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
|
|
@@ -12747,7 +12993,7 @@ function worktreeTree(cwd, options = {}) {
|
|
|
12747
12993
|
const skipped = [];
|
|
12748
12994
|
for (const file of untracked.stdout.split("\x00").filter(Boolean)) {
|
|
12749
12995
|
try {
|
|
12750
|
-
const stat = fs10.statSync(
|
|
12996
|
+
const stat = fs10.statSync(path11.join(top, file));
|
|
12751
12997
|
if (stat.isFile() && stat.size > cap)
|
|
12752
12998
|
skipped.push({ path: file, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) });
|
|
12753
12999
|
} catch {}
|
|
@@ -12793,7 +13039,7 @@ function worktreeSignal(cwd, options = {}) {
|
|
|
12793
13039
|
if (entry.length < 4)
|
|
12794
13040
|
continue;
|
|
12795
13041
|
const file = entry.slice(3);
|
|
12796
|
-
const absolute =
|
|
13042
|
+
const absolute = path11.join(top, file);
|
|
12797
13043
|
let stat = "-";
|
|
12798
13044
|
try {
|
|
12799
13045
|
const info = fs10.lstatSync(absolute, { bigint: true });
|
|
@@ -12972,15 +13218,15 @@ function parseRemoteUrl(raw) {
|
|
|
12972
13218
|
path: cleanPath(decodeURIComponent(url.pathname))
|
|
12973
13219
|
};
|
|
12974
13220
|
}
|
|
12975
|
-
function readSshConfig(home =
|
|
13221
|
+
function readSshConfig(home = os2.homedir()) {
|
|
12976
13222
|
try {
|
|
12977
|
-
return { text: fs10.readFileSync(
|
|
13223
|
+
return { text: fs10.readFileSync(path11.join(home, ".ssh", "config"), "utf8"), home };
|
|
12978
13224
|
} catch {
|
|
12979
13225
|
return { text: "", home };
|
|
12980
13226
|
}
|
|
12981
13227
|
}
|
|
12982
13228
|
function resolveSshHost(alias, sshConfig) {
|
|
12983
|
-
const source = typeof sshConfig === "string" ? { text: sshConfig, home:
|
|
13229
|
+
const source = typeof sshConfig === "string" ? { text: sshConfig, home: os2.homedir() } : sshConfig;
|
|
12984
13230
|
let hostname = null;
|
|
12985
13231
|
let port = null;
|
|
12986
13232
|
const evaluate = (text, initiallyActive, neverMatch, depth) => {
|
|
@@ -13176,15 +13422,15 @@ child.on("exit", (code) => {
|
|
|
13176
13422
|
return [];
|
|
13177
13423
|
return [{ key: match[1].toLowerCase(), args: tokens(match[2]) }];
|
|
13178
13424
|
}), includeFiles = (spec, home) => {
|
|
13179
|
-
const expanded = spec.startsWith("~") ?
|
|
13180
|
-
const absolute =
|
|
13181
|
-
const dir =
|
|
13182
|
-
const base =
|
|
13425
|
+
const expanded = spec.startsWith("~") ? path11.join(home, spec.slice(1)) : spec;
|
|
13426
|
+
const absolute = path11.isAbsolute(expanded) ? expanded : path11.join(home, ".ssh", expanded);
|
|
13427
|
+
const dir = path11.dirname(absolute);
|
|
13428
|
+
const base = path11.basename(absolute);
|
|
13183
13429
|
if (!/[*?]/u.test(base))
|
|
13184
13430
|
return [absolute];
|
|
13185
13431
|
try {
|
|
13186
13432
|
const pattern = globToRegExp(base);
|
|
13187
|
-
return fs10.readdirSync(dir).filter((name) => pattern.test(name)).toSorted().map((name) =>
|
|
13433
|
+
return fs10.readdirSync(dir).filter((name) => pattern.test(name)).toSorted().map((name) => path11.join(dir, name));
|
|
13188
13434
|
} catch {
|
|
13189
13435
|
return [];
|
|
13190
13436
|
}
|
|
@@ -13229,13 +13475,13 @@ import {
|
|
|
13229
13475
|
copyFileSync as copyFileSync2,
|
|
13230
13476
|
cpSync as cpSync2,
|
|
13231
13477
|
existsSync as existsSync3,
|
|
13232
|
-
mkdirSync as
|
|
13478
|
+
mkdirSync as mkdirSync3,
|
|
13233
13479
|
readFileSync as readFileSync2,
|
|
13234
|
-
readdirSync as
|
|
13480
|
+
readdirSync as readdirSync3,
|
|
13235
13481
|
writeFileSync as writeFileSync2
|
|
13236
13482
|
} from "node:fs";
|
|
13237
|
-
import
|
|
13238
|
-
import
|
|
13483
|
+
import os3 from "node:os";
|
|
13484
|
+
import path12 from "node:path";
|
|
13239
13485
|
var diagnosticLogger, setDiagnosticLogger = (logger) => {
|
|
13240
13486
|
diagnosticLogger = logger;
|
|
13241
13487
|
}, getDiagnosticLogger = () => diagnosticLogger, PRESETS, mergePreset = (preset, input = {}, current = {
|
|
@@ -13247,14 +13493,14 @@ var diagnosticLogger, setDiagnosticLogger = (logger) => {
|
|
|
13247
13493
|
allowed: preset === "custom" ? input.allowed ?? current.branchPolicy.allowed : [...defs.allowed],
|
|
13248
13494
|
protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
|
|
13249
13495
|
};
|
|
13250
|
-
}, resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ??
|
|
13496
|
+
}, resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path12.join(process.env.XDG_CONFIG_HOME || path12.join(os3.homedir(), ".config"), "workit"), migratedDir = null, migrationFailed = false, ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
13251
13497
|
if (migratedDir === dir)
|
|
13252
13498
|
return dir;
|
|
13253
13499
|
if (process.env.WORKFLOW_TOOLKIT_CONFIG || process.env.WORKFLOW_TOOLKIT_CONFIG_DIR) {
|
|
13254
13500
|
migratedDir = dir;
|
|
13255
13501
|
return dir;
|
|
13256
13502
|
}
|
|
13257
|
-
const legacy =
|
|
13503
|
+
const legacy = path12.join(process.env.XDG_CONFIG_HOME || path12.join(os3.homedir(), ".config"), "workflow-toolkit");
|
|
13258
13504
|
if (!existsSync3(legacy)) {
|
|
13259
13505
|
migratedDir = dir;
|
|
13260
13506
|
return dir;
|
|
@@ -13264,11 +13510,11 @@ var diagnosticLogger, setDiagnosticLogger = (logger) => {
|
|
|
13264
13510
|
return dir;
|
|
13265
13511
|
}
|
|
13266
13512
|
migrationFailed = false;
|
|
13267
|
-
|
|
13513
|
+
mkdirSync3(dir, { recursive: true });
|
|
13268
13514
|
diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
|
|
13269
|
-
for (const entry of
|
|
13270
|
-
const src =
|
|
13271
|
-
const dest =
|
|
13515
|
+
for (const entry of readdirSync3(legacy, { withFileTypes: true })) {
|
|
13516
|
+
const src = path12.join(legacy, entry.name);
|
|
13517
|
+
const dest = path12.join(dir, entry.name);
|
|
13272
13518
|
if (existsSync3(dest))
|
|
13273
13519
|
continue;
|
|
13274
13520
|
try {
|
|
@@ -13323,7 +13569,7 @@ var diagnosticLogger, setDiagnosticLogger = (logger) => {
|
|
|
13323
13569
|
unchosenBranchPolicy.add(config);
|
|
13324
13570
|
return { status: "valid", path: file, config };
|
|
13325
13571
|
}, readConfigTyped = (dir) => {
|
|
13326
|
-
const file =
|
|
13572
|
+
const file = path12.join(dir ?? configDir(), "config.json");
|
|
13327
13573
|
return parseConfigResult(readSafe(file), file);
|
|
13328
13574
|
}, readConfig = () => {
|
|
13329
13575
|
const result = readConfigTyped();
|
|
@@ -13335,7 +13581,7 @@ var diagnosticLogger, setDiagnosticLogger = (logger) => {
|
|
|
13335
13581
|
if (result.status === "malformed")
|
|
13336
13582
|
throw new Error(result.error);
|
|
13337
13583
|
return result.config ?? DEFAULTS;
|
|
13338
|
-
}, vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ??
|
|
13584
|
+
}, vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path12.join(configDir(), "vcs.json"), branchGlobPattern = (glob) => new RegExp(`^${glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i"), resolveBranchPolicy = (config, workspace, repoPreset) => {
|
|
13339
13585
|
const wp = workspace?.branchPolicy ?? {};
|
|
13340
13586
|
const workspacePreset = Object.hasOwn(PRESETS, wp.preset);
|
|
13341
13587
|
const detected = !workspacePreset && unchosenBranchPolicy.has(config) ? repoPreset?.() ?? null : null;
|
|
@@ -13466,7 +13712,7 @@ var init_branch_policy = __esm(() => {
|
|
|
13466
13712
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
13467
13713
|
import { createHash as createHash7 } from "node:crypto";
|
|
13468
13714
|
import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
13469
|
-
import
|
|
13715
|
+
import path13 from "node:path";
|
|
13470
13716
|
function workspaceDefaultTarget(cwd, globalDefault) {
|
|
13471
13717
|
const ws = resolveRuntimeWorkspaceVcs(cwd);
|
|
13472
13718
|
const wp = ws?.branchPolicy ?? {};
|
|
@@ -13511,7 +13757,7 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
13511
13757
|
document: parsed
|
|
13512
13758
|
};
|
|
13513
13759
|
}, readWorkspacesResult = (dir = configDir()) => {
|
|
13514
|
-
const file =
|
|
13760
|
+
const file = path13.join(dir, "workspaces.json");
|
|
13515
13761
|
let raw;
|
|
13516
13762
|
try {
|
|
13517
13763
|
raw = readFileSync3(file, "utf8");
|
|
@@ -13611,9 +13857,9 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
13611
13857
|
let main = null;
|
|
13612
13858
|
const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
|
|
13613
13859
|
const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
|
|
13614
|
-
if (gitDir && common &&
|
|
13860
|
+
if (gitDir && common && path13.resolve(gitDir) !== path13.resolve(common)) {
|
|
13615
13861
|
const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
|
|
13616
|
-
main = listed?.trim() || (
|
|
13862
|
+
main = listed?.trim() || (path13.basename(common) === ".git" ? path13.dirname(common) : common);
|
|
13617
13863
|
}
|
|
13618
13864
|
mainCheckouts.set(cwd, main);
|
|
13619
13865
|
return main;
|
|
@@ -13690,8 +13936,8 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
13690
13936
|
profiles: record(nonBlank, profileSchema).optional()
|
|
13691
13937
|
}).passthrough();
|
|
13692
13938
|
return entrySchema;
|
|
13693
|
-
}, resolveRuntimeWorkspaceVcs = (cwd) => resolveRuntimeWorkspaceCandidate(cwd, "vcs"), runtimeWorkspaceIndexSchema, matchRuntimeWorkspace = (cwd) => {
|
|
13694
|
-
const file =
|
|
13939
|
+
}, resolveRuntimeWorkspaceVcs = (cwd) => resolveRuntimeWorkspaceCandidate(cwd, "vcs"), runtimeWorkspaceIndexSchema, matchRuntimeWorkspace = (cwd, dir = configDir()) => {
|
|
13940
|
+
const file = path13.join(dir, "workspaces.json");
|
|
13695
13941
|
let raw;
|
|
13696
13942
|
try {
|
|
13697
13943
|
raw = readFileSync3(file, "utf8");
|
|
@@ -13720,10 +13966,27 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
13720
13966
|
const matches = matchingEntries(cwd, entries ?? []);
|
|
13721
13967
|
const workspaceName = process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined;
|
|
13722
13968
|
const selected = selectWorkspaceMatch(matches, cwd, workspaceName);
|
|
13723
|
-
return selected ? { file, selected } : null;
|
|
13969
|
+
return selected ? { file, selected, matches } : null;
|
|
13724
13970
|
}, resolveRuntimeReleaseTracks = (cwd) => {
|
|
13725
13971
|
const matched = matchRuntimeWorkspace(cwd);
|
|
13726
13972
|
return matched ? { workspace: matched.selected.name, releaseTracks: matched.selected.releaseTracks } : null;
|
|
13973
|
+
}, workspaceTrailers = (cwd, dir) => {
|
|
13974
|
+
let matched;
|
|
13975
|
+
try {
|
|
13976
|
+
matched = matchRuntimeWorkspace(cwd, dir);
|
|
13977
|
+
} catch {
|
|
13978
|
+
return { enabled: true, workspace: null, from: null };
|
|
13979
|
+
}
|
|
13980
|
+
if (!matched)
|
|
13981
|
+
return { enabled: true, workspace: null, from: null };
|
|
13982
|
+
const { selected, matches } = matched;
|
|
13983
|
+
const enclosing = matches.filter((entry) => entry !== selected).toSorted((a, b) => globSpecificity(b.glob) - globSpecificity(a.glob));
|
|
13984
|
+
const source = [selected, ...enclosing].find((entry) => typeof entry.trailers === "boolean");
|
|
13985
|
+
return {
|
|
13986
|
+
enabled: source?.trailers !== false,
|
|
13987
|
+
workspace: selected.name,
|
|
13988
|
+
from: source?.name ?? null
|
|
13989
|
+
};
|
|
13727
13990
|
}, resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
13728
13991
|
const matched = matchRuntimeWorkspace(cwd);
|
|
13729
13992
|
if (!matched)
|
|
@@ -13854,7 +14117,7 @@ var init_workspaces = __esm(() => {
|
|
|
13854
14117
|
context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
|
|
13855
14118
|
}
|
|
13856
14119
|
}
|
|
13857
|
-
if (track.versionSource?.kind === "package-json" && (
|
|
14120
|
+
if (track.versionSource?.kind === "package-json" && (path13.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
|
|
13858
14121
|
context.addIssue({
|
|
13859
14122
|
code: "custom",
|
|
13860
14123
|
path: ["versionSource", "path"],
|
|
@@ -13874,6 +14137,7 @@ var init_workspaces = __esm(() => {
|
|
|
13874
14137
|
autoApprove: union([boolean2(), array(nonBlank)]).optional(),
|
|
13875
14138
|
defaultEndpoint: unknown().optional(),
|
|
13876
14139
|
verification: _enum(["self", "independent"]).optional(),
|
|
14140
|
+
trailers: boolean2().optional(),
|
|
13877
14141
|
autonomy: object(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
|
|
13878
14142
|
kind,
|
|
13879
14143
|
union([boolean2(), literal("verified")]).optional()
|
|
@@ -14191,7 +14455,7 @@ function gitTrackProbe(cwd) {
|
|
|
14191
14455
|
const created = own.at(-1);
|
|
14192
14456
|
if (!created || created.subject !== "branch: Created from HEAD")
|
|
14193
14457
|
return null;
|
|
14194
|
-
const pattern = new RegExp(`^checkout: moving from (\\S+) to ${
|
|
14458
|
+
const pattern = new RegExp(`^checkout: moving from (\\S+) to ${escapeRegExp2(branch)}$`, "u");
|
|
14195
14459
|
const entry = reflog(cwd, "HEAD").findLast((item) => item.time >= created.time && item.sha === created.sha && pattern.test(item.subject));
|
|
14196
14460
|
return entry ? pattern.exec(entry.subject)?.[1] ?? null : null;
|
|
14197
14461
|
},
|
|
@@ -14339,7 +14603,7 @@ var ReleaseTrackError, KNOWN_FIELDS, BRANCH_FIELDS, PLACEHOLDER, HAS_PLACEHOLDER
|
|
|
14339
14603
|
return run.status === 0 ? (run.stdout ?? "").trim() : null;
|
|
14340
14604
|
}, recordedBaseKey = (branch) => `branch.${branch}.workitBase`, recordBranchBase = (cwd, branch, base) => {
|
|
14341
14605
|
git3(cwd, ["config", recordedBaseKey(branch), base]);
|
|
14342
|
-
},
|
|
14606
|
+
}, escapeRegExp2 = (text) => text.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&"), reflog = (cwd, ref) => (git3(cwd, ["log", "-g", "--date=unix", "--format=%H%x09%gd%x09%gs", ref, "--"]) ?? "").split(`
|
|
14343
14607
|
`).map((line) => {
|
|
14344
14608
|
const [sha = "", selector = "", ...subject] = line.split("\t");
|
|
14345
14609
|
return {
|
|
@@ -14389,7 +14653,7 @@ var init_release_tracks = __esm(() => {
|
|
|
14389
14653
|
|
|
14390
14654
|
// packages/workit-core/src/core/vcs-config.ts
|
|
14391
14655
|
import fs11 from "node:fs";
|
|
14392
|
-
import
|
|
14656
|
+
import path14 from "node:path";
|
|
14393
14657
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
14394
14658
|
function vcsConfig(mode, cwd, options = {}) {
|
|
14395
14659
|
const ws = resolveRuntimeWorkspaceVcs(vcsCwd(cwd));
|
|
@@ -14452,7 +14716,7 @@ function vcsConfig(mode, cwd, options = {}) {
|
|
|
14452
14716
|
const prov = cfg[provider] ?? {};
|
|
14453
14717
|
const out = {
|
|
14454
14718
|
ok: true,
|
|
14455
|
-
configPath:
|
|
14719
|
+
configPath: path14.resolve(cfgPath),
|
|
14456
14720
|
provider,
|
|
14457
14721
|
defaultTargetBranch: workspaceDefault,
|
|
14458
14722
|
pr: cfg.pr ?? {},
|
|
@@ -14472,7 +14736,7 @@ function vcsConfig(mode, cwd, options = {}) {
|
|
|
14472
14736
|
}
|
|
14473
14737
|
return out;
|
|
14474
14738
|
}
|
|
14475
|
-
var workspacesPath = () =>
|
|
14739
|
+
var workspacesPath = () => path14.join(configDir(), "workspaces.json"), vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd(), remoteProvider = (cwd) => {
|
|
14476
14740
|
const r = spawnSync8("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
|
|
14477
14741
|
if (r.status !== 0)
|
|
14478
14742
|
return null;
|
|
@@ -14519,7 +14783,7 @@ var init_vcs_config = __esm(() => {
|
|
|
14519
14783
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
14520
14784
|
import { createHash as createHash9, randomBytes as randomBytes2 } from "node:crypto";
|
|
14521
14785
|
import fs12 from "node:fs";
|
|
14522
|
-
import
|
|
14786
|
+
import path15 from "node:path";
|
|
14523
14787
|
function resolveCommit(cwd, ref) {
|
|
14524
14788
|
if (!safeRef(ref))
|
|
14525
14789
|
return null;
|
|
@@ -14585,14 +14849,14 @@ function diffHash(cwd, base, head) {
|
|
|
14585
14849
|
return `sha256:${hash.digest("hex")}`;
|
|
14586
14850
|
}
|
|
14587
14851
|
function storeRoot(cwd) {
|
|
14588
|
-
const location = resolveStore(
|
|
14852
|
+
const location = resolveStore(path15.resolve(cwd));
|
|
14589
14853
|
if (location instanceof Error)
|
|
14590
14854
|
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");
|
|
14591
14855
|
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
14592
14856
|
}
|
|
14593
14857
|
function ledgerPath(cwd) {
|
|
14594
14858
|
const root = storeRoot(cwd);
|
|
14595
|
-
return root.ok ? { ok: true, value:
|
|
14859
|
+
return root.ok ? { ok: true, value: path15.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
14596
14860
|
}
|
|
14597
14861
|
function actorFromEnv(env) {
|
|
14598
14862
|
const value = (key) => {
|
|
@@ -14654,8 +14918,8 @@ function appendLine(file, build, options) {
|
|
|
14654
14918
|
}
|
|
14655
14919
|
};
|
|
14656
14920
|
try {
|
|
14657
|
-
fs12.mkdirSync(
|
|
14658
|
-
if (!options.forceLock && !onNetworkFs(
|
|
14921
|
+
fs12.mkdirSync(path15.dirname(file), { recursive: true });
|
|
14922
|
+
if (!options.forceLock && !onNetworkFs(path15.dirname(file)))
|
|
14659
14923
|
return write();
|
|
14660
14924
|
const locked = withLedgerLock(file, write);
|
|
14661
14925
|
return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
|
|
@@ -15334,7 +15598,7 @@ function summarizeRow(row) {
|
|
|
15334
15598
|
};
|
|
15335
15599
|
}
|
|
15336
15600
|
function stackPosition(root, branch) {
|
|
15337
|
-
const dir =
|
|
15601
|
+
const dir = path15.join(root, "stacks");
|
|
15338
15602
|
let names = [];
|
|
15339
15603
|
try {
|
|
15340
15604
|
names = fs12.readdirSync(dir).filter((name) => name.endsWith(".json"));
|
|
@@ -15344,7 +15608,7 @@ function stackPosition(root, branch) {
|
|
|
15344
15608
|
for (const name of names.toSorted()) {
|
|
15345
15609
|
let parsed;
|
|
15346
15610
|
try {
|
|
15347
|
-
parsed = JSON.parse(fs12.readFileSync(
|
|
15611
|
+
parsed = JSON.parse(fs12.readFileSync(path15.join(dir, name), "utf8"));
|
|
15348
15612
|
} catch {
|
|
15349
15613
|
continue;
|
|
15350
15614
|
}
|
|
@@ -15489,12 +15753,12 @@ var LEDGER_VERSION = 1, MAX_LINE_BYTES = 4096, VERDICT_RESULTS, PASSING, FAILING
|
|
|
15489
15753
|
}
|
|
15490
15754
|
}, LOCK_WAIT_MS = 5000, LOCK_STALE_MS = 30000, LOCK_OWNER = "owner", lockToken = (lock) => {
|
|
15491
15755
|
try {
|
|
15492
|
-
return fs12.readFileSync(
|
|
15756
|
+
return fs12.readFileSync(path15.join(lock, LOCK_OWNER), "utf8");
|
|
15493
15757
|
} catch {
|
|
15494
15758
|
return null;
|
|
15495
15759
|
}
|
|
15496
15760
|
}, lockAgeMs = (lock) => {
|
|
15497
|
-
for (const target of [
|
|
15761
|
+
for (const target of [path15.join(lock, LOCK_OWNER), lock]) {
|
|
15498
15762
|
try {
|
|
15499
15763
|
return Date.now() - fs12.statSync(target).mtimeMs;
|
|
15500
15764
|
} catch {}
|
|
@@ -15664,7 +15928,7 @@ var init_ledger = __esm(() => {
|
|
|
15664
15928
|
throw error;
|
|
15665
15929
|
}
|
|
15666
15930
|
const token = `${process.pid}-${randomBytes2(8).toString("hex")}`;
|
|
15667
|
-
fs12.writeFileSync(
|
|
15931
|
+
fs12.writeFileSync(path15.join(lock, LOCK_OWNER), token);
|
|
15668
15932
|
return token;
|
|
15669
15933
|
},
|
|
15670
15934
|
release(lock, token) {
|
|
@@ -15717,7 +15981,7 @@ var init_ledger = __esm(() => {
|
|
|
15717
15981
|
|
|
15718
15982
|
// packages/workit-core/src/core/policy/derive.ts
|
|
15719
15983
|
import { existsSync as existsSync4 } from "node:fs";
|
|
15720
|
-
import
|
|
15984
|
+
import path16 from "node:path";
|
|
15721
15985
|
function deriveRequirements(judgment, scope, constraints, verification = "self") {
|
|
15722
15986
|
const out = [];
|
|
15723
15987
|
const base = { scope, dependentAction: null };
|
|
@@ -15858,7 +16122,7 @@ var RULES, derived2 = (spec) => ({
|
|
|
15858
16122
|
decisionIds: []
|
|
15859
16123
|
};
|
|
15860
16124
|
}, planRecorded = (task, root) => {
|
|
15861
|
-
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(
|
|
16125
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path16.resolve(root, ref.path)));
|
|
15862
16126
|
if (cited)
|
|
15863
16127
|
return {
|
|
15864
16128
|
met: true,
|
|
@@ -15907,7 +16171,7 @@ var init_derive = __esm(() => {
|
|
|
15907
16171
|
import { createHash as createHash10 } from "node:crypto";
|
|
15908
16172
|
import * as fs13 from "node:fs";
|
|
15909
16173
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
15910
|
-
import
|
|
16174
|
+
import path17 from "node:path";
|
|
15911
16175
|
function captureCandidate(root, scope, environment = []) {
|
|
15912
16176
|
let checkout;
|
|
15913
16177
|
try {
|
|
@@ -15949,7 +16213,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
15949
16213
|
for (const item of [...names].toSorted()) {
|
|
15950
16214
|
if (!scopeMatches(item, normalizedScope))
|
|
15951
16215
|
continue;
|
|
15952
|
-
const target =
|
|
16216
|
+
const target = path17.join(checkout, item);
|
|
15953
16217
|
try {
|
|
15954
16218
|
const stat = fs13.lstatSync(target);
|
|
15955
16219
|
if (stat.isSymbolicLink()) {
|
|
@@ -16322,12 +16586,12 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
16322
16586
|
requirementIds: evaluations.map((item) => item.requirementId)
|
|
16323
16587
|
});
|
|
16324
16588
|
}
|
|
16325
|
-
var digestBytes = (value) => createHash10("sha256").update(value).digest("hex"), inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
16589
|
+
var digestBytes = (value) => createHash10("sha256").update(value).digest("hex"), inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path17.sep}`), relative = (root, candidate) => path17.relative(root, candidate).split(path17.sep).join("/") || ".", canonicalPath = (value) => {
|
|
16326
16590
|
if (typeof value !== "string" || !value)
|
|
16327
16591
|
return null;
|
|
16328
16592
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
16329
16593
|
return null;
|
|
16330
|
-
const normalized =
|
|
16594
|
+
const normalized = path17.posix.normalize(value);
|
|
16331
16595
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
16332
16596
|
return null;
|
|
16333
16597
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -16369,14 +16633,14 @@ var digestBytes = (value) => createHash10("sha256").update(value).digest("hex"),
|
|
|
16369
16633
|
git: !/not a git repository/i.test(stderr)
|
|
16370
16634
|
};
|
|
16371
16635
|
}
|
|
16372
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
16636
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path17.sep).join("/")) : [];
|
|
16373
16637
|
const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
16374
16638
|
if (stagedDeleted.status !== 0) {
|
|
16375
16639
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
16376
16640
|
if (!/not a git repository/i.test(stderr))
|
|
16377
16641
|
return { paths, uncertain: true, git: true };
|
|
16378
16642
|
} else if (stagedDeleted.stdout) {
|
|
16379
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
16643
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path17.sep).join("/")));
|
|
16380
16644
|
}
|
|
16381
16645
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
16382
16646
|
}, walk = (root, directory, output) => {
|
|
@@ -16390,7 +16654,7 @@ var digestBytes = (value) => createHash10("sha256").update(value).digest("hex"),
|
|
|
16390
16654
|
for (const name of names) {
|
|
16391
16655
|
if (name === ".git" || name === ".workit")
|
|
16392
16656
|
continue;
|
|
16393
|
-
const target =
|
|
16657
|
+
const target = path17.join(directory, name);
|
|
16394
16658
|
let stat;
|
|
16395
16659
|
try {
|
|
16396
16660
|
stat = fs13.lstatSync(target);
|
|
@@ -16407,12 +16671,12 @@ var digestBytes = (value) => createHash10("sha256").update(value).digest("hex"),
|
|
|
16407
16671
|
}, scopeRoots = (root, scope) => {
|
|
16408
16672
|
const roots = [];
|
|
16409
16673
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
16410
|
-
const target =
|
|
16674
|
+
const target = path17.resolve(root, item);
|
|
16411
16675
|
if (!inside(root, target))
|
|
16412
16676
|
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` }] });
|
|
16413
16677
|
let ancestor = target;
|
|
16414
16678
|
while (!fs13.existsSync(ancestor) && ancestor !== root)
|
|
16415
|
-
ancestor =
|
|
16679
|
+
ancestor = path17.dirname(ancestor);
|
|
16416
16680
|
try {
|
|
16417
16681
|
if (!inside(root, fs13.realpathSync(ancestor)))
|
|
16418
16682
|
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` }] });
|
|
@@ -16476,23 +16740,23 @@ var digestBytes = (value) => createHash10("sha256").update(value).digest("hex"),
|
|
|
16476
16740
|
continue;
|
|
16477
16741
|
if (!reference.digest)
|
|
16478
16742
|
return failure2("invalid_input", "document references require a byte digest");
|
|
16479
|
-
const target =
|
|
16480
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
16743
|
+
const target = path17.resolve(root, reference.path);
|
|
16744
|
+
if (target !== root && !target.startsWith(`${root}${path17.sep}`))
|
|
16481
16745
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
16482
16746
|
fields: [
|
|
16483
16747
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
16484
16748
|
]
|
|
16485
16749
|
});
|
|
16486
16750
|
try {
|
|
16487
|
-
const below =
|
|
16751
|
+
const below = path17.relative(root, target);
|
|
16488
16752
|
let current = root;
|
|
16489
|
-
for (const segment of below.split(
|
|
16753
|
+
for (const segment of below.split(path17.sep)) {
|
|
16490
16754
|
if (!segment)
|
|
16491
16755
|
continue;
|
|
16492
|
-
current =
|
|
16756
|
+
current = path17.join(current, segment);
|
|
16493
16757
|
const stat = fs13.lstatSync(current);
|
|
16494
16758
|
const real = fs13.realpathSync(current);
|
|
16495
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
16759
|
+
if (real !== root && !real.startsWith(`${root}${path17.sep}`))
|
|
16496
16760
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
16497
16761
|
fields: [
|
|
16498
16762
|
{
|
|
@@ -16773,7 +17037,7 @@ var init_git = () => {};
|
|
|
16773
17037
|
// packages/workit-core/src/core/branch.ts
|
|
16774
17038
|
import { execFileSync } from "node:child_process";
|
|
16775
17039
|
import { createHash as createHash11 } from "node:crypto";
|
|
16776
|
-
import
|
|
17040
|
+
import path18 from "node:path";
|
|
16777
17041
|
function effectiveWorkspacePolicy(workspaceRoot, kind, profileName) {
|
|
16778
17042
|
const selected = resolveRuntimeWorkspacePolicy(workspaceRoot, kind, profileName?.trim() || process.env.WORKFLOW_PROFILE?.trim() || undefined);
|
|
16779
17043
|
const config = readConfig();
|
|
@@ -16972,7 +17236,7 @@ var withReleaseTracks = (workspaceRoot, policy) => {
|
|
|
16972
17236
|
let raw;
|
|
16973
17237
|
try {
|
|
16974
17238
|
raw = execFileSync("git", ["status", "--porcelain=v1"], {
|
|
16975
|
-
cwd:
|
|
17239
|
+
cwd: path18.resolve(workspaceRoot),
|
|
16976
17240
|
encoding: "utf8",
|
|
16977
17241
|
stdio: ["pipe", "pipe", "pipe"]
|
|
16978
17242
|
});
|
|
@@ -17210,13 +17474,13 @@ import {
|
|
|
17210
17474
|
fchmodSync,
|
|
17211
17475
|
fsyncSync,
|
|
17212
17476
|
lstatSync as lstatSync3,
|
|
17213
|
-
mkdirSync as
|
|
17477
|
+
mkdirSync as mkdirSync4,
|
|
17214
17478
|
openSync as openSync2,
|
|
17215
17479
|
renameSync as renameSync2,
|
|
17216
|
-
unlinkSync,
|
|
17480
|
+
unlinkSync as unlinkSync2,
|
|
17217
17481
|
writeFileSync as writeFileSync3
|
|
17218
17482
|
} from "node:fs";
|
|
17219
|
-
import
|
|
17483
|
+
import path19 from "node:path";
|
|
17220
17484
|
function writeFileExclusive(file, content, mode) {
|
|
17221
17485
|
try {
|
|
17222
17486
|
writeFileSync3(file, content, { encoding: "utf8", flag: "wx", mode });
|
|
@@ -17228,9 +17492,9 @@ function writeFileExclusive(file, content, mode) {
|
|
|
17228
17492
|
}
|
|
17229
17493
|
}
|
|
17230
17494
|
function writeFileAtomic(file, content, mode) {
|
|
17231
|
-
const absolute =
|
|
17232
|
-
let parent =
|
|
17233
|
-
const root =
|
|
17495
|
+
const absolute = path19.resolve(file);
|
|
17496
|
+
let parent = path19.dirname(absolute);
|
|
17497
|
+
const root = path19.parse(parent).root;
|
|
17234
17498
|
for (;; ) {
|
|
17235
17499
|
try {
|
|
17236
17500
|
if (lstatSync3(parent).isSymbolicLink())
|
|
@@ -17239,13 +17503,13 @@ function writeFileAtomic(file, content, mode) {
|
|
|
17239
17503
|
if (error.code !== "ENOENT")
|
|
17240
17504
|
throw error;
|
|
17241
17505
|
}
|
|
17242
|
-
const next =
|
|
17506
|
+
const next = path19.dirname(parent);
|
|
17243
17507
|
if (parent === root || next === parent)
|
|
17244
17508
|
break;
|
|
17245
17509
|
parent = next;
|
|
17246
17510
|
}
|
|
17247
|
-
const directory =
|
|
17248
|
-
|
|
17511
|
+
const directory = path19.dirname(absolute);
|
|
17512
|
+
mkdirSync4(directory, { recursive: true });
|
|
17249
17513
|
let existing = null;
|
|
17250
17514
|
try {
|
|
17251
17515
|
existing = lstatSync3(absolute);
|
|
@@ -17274,7 +17538,7 @@ function writeFileAtomic(file, content, mode) {
|
|
|
17274
17538
|
} catch {}
|
|
17275
17539
|
}
|
|
17276
17540
|
try {
|
|
17277
|
-
|
|
17541
|
+
unlinkSync2(temporary);
|
|
17278
17542
|
} catch {}
|
|
17279
17543
|
throw error;
|
|
17280
17544
|
}
|
|
@@ -17283,8 +17547,8 @@ var init_safe_write = () => {};
|
|
|
17283
17547
|
|
|
17284
17548
|
// packages/workit-core/src/autonomy.ts
|
|
17285
17549
|
import { copyFileSync as copyFileSync3, existsSync as existsSync6 } from "node:fs";
|
|
17286
|
-
import
|
|
17287
|
-
import
|
|
17550
|
+
import os4 from "node:os";
|
|
17551
|
+
import path20 from "node:path";
|
|
17288
17552
|
function endpointOf(entry) {
|
|
17289
17553
|
const raw = entry?.defaultEndpoint;
|
|
17290
17554
|
if (raw === undefined)
|
|
@@ -17523,13 +17787,13 @@ function listGrants(dir = grantsDir()) {
|
|
|
17523
17787
|
}
|
|
17524
17788
|
var accountHome = () => {
|
|
17525
17789
|
try {
|
|
17526
|
-
return
|
|
17790
|
+
return os4.userInfo().homedir;
|
|
17527
17791
|
} catch {
|
|
17528
|
-
return
|
|
17792
|
+
return os4.homedir();
|
|
17529
17793
|
}
|
|
17530
|
-
}, grantsHome, grantsDir = () =>
|
|
17531
|
-
const configured =
|
|
17532
|
-
return configured ===
|
|
17794
|
+
}, grantsHome, grantsDir = () => path20.join(grantsHome.resolve(), ".config", "workit"), grantsOverride = () => {
|
|
17795
|
+
const configured = path20.resolve(resolveConfigDir());
|
|
17796
|
+
return configured === path20.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 ${path20.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
|
|
17533
17797
|
}, GRANT_KINDS, DEFAULT_GRANTS, DEFAULT_ENDPOINTS, DEFAULT_ENDPOINT = "commit", isDefaultEndpoint = (value) => typeof value === "string" && DEFAULT_ENDPOINTS.includes(value), raisesEndpoint = (current, next) => DEFAULT_ENDPOINTS.indexOf(next) > DEFAULT_ENDPOINTS.indexOf(current), DEFAULT_VERIFICATION = "self", verificationOf = (entry) => entry?.verification === "independent" ? "independent" : "self", isGrantKind = (value) => typeof value === "string" && GRANT_KINDS.includes(value), isGrantValue = (value) => value === true || value === false || value === "verified", normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value, fromAutoApprove = (value) => {
|
|
17534
17798
|
if (value === true)
|
|
17535
17799
|
return { push: true, pr: true, merge: "verified" };
|
|
@@ -19273,12 +19537,12 @@ var init_pr_body = __esm(() => {
|
|
|
19273
19537
|
});
|
|
19274
19538
|
|
|
19275
19539
|
// packages/workit-core/src/core/pr-create.ts
|
|
19276
|
-
import
|
|
19540
|
+
import path21 from "node:path";
|
|
19277
19541
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
19278
19542
|
import { pathToFileURL } from "node:url";
|
|
19279
19543
|
var safePushUrl = (remote) => {
|
|
19280
19544
|
const raw = remote.trim();
|
|
19281
|
-
if (
|
|
19545
|
+
if (path21.isAbsolute(raw))
|
|
19282
19546
|
return pathToFileURL(raw).href;
|
|
19283
19547
|
if (!raw.includes("://")) {
|
|
19284
19548
|
const scp = /^(?:([^@\s]+)@)?([^:]+):(.+)$/u.exec(raw);
|
|
@@ -19339,7 +19603,7 @@ var init_scripts = __esm(() => {
|
|
|
19339
19603
|
|
|
19340
19604
|
// packages/workit-core/src/core/youtrack.ts
|
|
19341
19605
|
import fs14 from "node:fs";
|
|
19342
|
-
import
|
|
19606
|
+
import path22 from "node:path";
|
|
19343
19607
|
function readYouTrackConfig(required) {
|
|
19344
19608
|
const cfgPath = youTrackConfigPath();
|
|
19345
19609
|
if (!fs14.existsSync(cfgPath)) {
|
|
@@ -19363,7 +19627,7 @@ function youTrackConfigLoad() {
|
|
|
19363
19627
|
}
|
|
19364
19628
|
const cfgPath = loaded.path;
|
|
19365
19629
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
19366
|
-
const tokenPath = tokenFile ?
|
|
19630
|
+
const tokenPath = tokenFile ? path22.isAbsolute(tokenFile) ? path22.resolve(tokenFile) : path22.resolve(process.cwd(), tokenFile) : "";
|
|
19367
19631
|
if (!tokenPath || !fs14.existsSync(tokenPath)) {
|
|
19368
19632
|
return { error: "missing youtrack.token" };
|
|
19369
19633
|
}
|
|
@@ -19377,8 +19641,8 @@ function youTrackConfigLoad() {
|
|
|
19377
19641
|
const redacted = { ...loaded.config };
|
|
19378
19642
|
delete redacted.tokenFile;
|
|
19379
19643
|
redacted.tokenPresent = true;
|
|
19380
|
-
redacted.configPath =
|
|
19381
|
-
redacted.tokenPath =
|
|
19644
|
+
redacted.configPath = path22.resolve(cfgPath);
|
|
19645
|
+
redacted.tokenPath = path22.resolve(tokenPath);
|
|
19382
19646
|
return { data: redacted };
|
|
19383
19647
|
}
|
|
19384
19648
|
function tzParts(date, tz) {
|
|
@@ -19701,7 +19965,7 @@ async function postUpdate({
|
|
|
19701
19965
|
}
|
|
19702
19966
|
return { ok: true, issueId, postedComment: true };
|
|
19703
19967
|
}
|
|
19704
|
-
var ISSUE_RE, TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE", youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
19968
|
+
var ISSUE_RE, TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE", youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path22.join(configDir(), "youtrack.json"), youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path22.join(resolveConfigDir(), "youtrack.json"), youTrackTokenModeOk = (p) => {
|
|
19705
19969
|
if (process.platform === "win32")
|
|
19706
19970
|
return true;
|
|
19707
19971
|
const mode = fs14.statSync(p).mode & 511;
|
|
@@ -19737,7 +20001,7 @@ var ISSUE_RE, TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE", youTrackConfigPath = () =>
|
|
|
19737
20001
|
if ("error" in loaded)
|
|
19738
20002
|
return loaded;
|
|
19739
20003
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
19740
|
-
const tokenPath = tokenFile ?
|
|
20004
|
+
const tokenPath = tokenFile ? path22.isAbsolute(tokenFile) ? path22.resolve(tokenFile) : path22.resolve(process.cwd(), tokenFile) : "";
|
|
19741
20005
|
if (!tokenPath || !fs14.existsSync(tokenPath))
|
|
19742
20006
|
return { error: "missing youtrack.token" };
|
|
19743
20007
|
if (!youTrackTokenModeOk(tokenPath))
|
|
@@ -19842,18 +20106,18 @@ var init_context_read = __esm(() => {
|
|
|
19842
20106
|
|
|
19843
20107
|
// packages/workit-core/src/core.ts
|
|
19844
20108
|
import { existsSync as existsSync7, realpathSync as realpathSync5 } from "node:fs";
|
|
19845
|
-
import
|
|
20109
|
+
import path23 from "node:path";
|
|
19846
20110
|
function resolveInside(root, candidate) {
|
|
19847
20111
|
const base = realpathSync5(root);
|
|
19848
|
-
const target =
|
|
19849
|
-
if (target !== base && !target.startsWith(base +
|
|
20112
|
+
const target = path23.resolve(base, candidate);
|
|
20113
|
+
if (target !== base && !target.startsWith(base + path23.sep)) {
|
|
19850
20114
|
throw new Error("path must stay inside repository root");
|
|
19851
20115
|
}
|
|
19852
20116
|
let ancestor = target;
|
|
19853
20117
|
while (!existsSync7(ancestor))
|
|
19854
|
-
ancestor =
|
|
20118
|
+
ancestor = path23.dirname(ancestor);
|
|
19855
20119
|
const canonicalAncestor = realpathSync5(ancestor);
|
|
19856
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
20120
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path23.sep)) {
|
|
19857
20121
|
throw new Error("path must stay inside repository root");
|
|
19858
20122
|
}
|
|
19859
20123
|
return target;
|
|
@@ -19879,7 +20143,7 @@ var init_core3 = __esm(() => {
|
|
|
19879
20143
|
|
|
19880
20144
|
// packages/workit-cli/src/task.ts
|
|
19881
20145
|
import { readFileSync as readFileSync7 } from "node:fs";
|
|
19882
|
-
import
|
|
20146
|
+
import path24 from "node:path";
|
|
19883
20147
|
async function readInput(input) {
|
|
19884
20148
|
if (typeof input === "string")
|
|
19885
20149
|
return input;
|
|
@@ -20198,7 +20462,7 @@ var TASK_FAMILIES, workspaceRootFor = (deps = {}) => {
|
|
|
20198
20462
|
return explicit;
|
|
20199
20463
|
const cwd = deps.cwd ?? process.cwd();
|
|
20200
20464
|
const top = checkRoot(cwd);
|
|
20201
|
-
return sameDirectoryIdentity(top,
|
|
20465
|
+
return sameDirectoryIdentity(top, path24.resolve(cwd)) ? cwd : top;
|
|
20202
20466
|
}, TASK_ACTIONS, outOf = (deps) => deps.out ?? process.stdout, errOf = (deps) => deps.err ?? process.stderr, write = (stream, value) => stream.write(value.endsWith(`
|
|
20203
20467
|
`) ? value : `${value}
|
|
20204
20468
|
`), jsonResult = (stream, result) => write(stream, JSON.stringify(result)), usage = (message) => failure2("invalid_input", message), parseUsage = (message, json) => ({
|
|
@@ -21488,7 +21752,7 @@ var init_environment = __esm(() => {
|
|
|
21488
21752
|
|
|
21489
21753
|
// node_modules/ansi-escapes/base.js
|
|
21490
21754
|
import process2 from "node:process";
|
|
21491
|
-
import
|
|
21755
|
+
import os5 from "node:os";
|
|
21492
21756
|
var ESC = "\x1B[", SEP = ";", isTerminalApp, isWindows2, isTmux, cwdFunction, cursorTo = (x, y) => {
|
|
21493
21757
|
if (typeof x !== "number") {
|
|
21494
21758
|
throw new TypeError("The `x` argument is required");
|
|
@@ -21510,7 +21774,7 @@ var ESC = "\x1B[", SEP = ";", isTerminalApp, isWindows2, isTmux, cwdFunction, cu
|
|
|
21510
21774
|
if (isBrowser || !isWindows2) {
|
|
21511
21775
|
return false;
|
|
21512
21776
|
}
|
|
21513
|
-
const parts =
|
|
21777
|
+
const parts = os5.release().split(".");
|
|
21514
21778
|
const major = Number(parts[0]);
|
|
21515
21779
|
const build = Number(parts[2] ?? 0);
|
|
21516
21780
|
if (major < 10) {
|
|
@@ -57173,7 +57437,7 @@ var init_ansi_styles2 = __esm(() => {
|
|
|
57173
57437
|
|
|
57174
57438
|
// node_modules/chalk/source/vendor/supports-color/index.js
|
|
57175
57439
|
import process5 from "node:process";
|
|
57176
|
-
import
|
|
57440
|
+
import os6 from "node:os";
|
|
57177
57441
|
import tty2 from "node:tty";
|
|
57178
57442
|
function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process5.argv) {
|
|
57179
57443
|
const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
|
|
@@ -57231,7 +57495,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
|
|
|
57231
57495
|
return min;
|
|
57232
57496
|
}
|
|
57233
57497
|
if (process5.platform === "win32") {
|
|
57234
|
-
const osRelease =
|
|
57498
|
+
const osRelease = os6.release().split(".");
|
|
57235
57499
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
57236
57500
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
57237
57501
|
}
|
|
@@ -63060,7 +63324,7 @@ var init_gitignore = __esm(() => {
|
|
|
63060
63324
|
|
|
63061
63325
|
// packages/workit-core/src/core/changelog.ts
|
|
63062
63326
|
import fs17 from "node:fs";
|
|
63063
|
-
import
|
|
63327
|
+
import path25 from "node:path";
|
|
63064
63328
|
import { createHash as createHash13 } from "node:crypto";
|
|
63065
63329
|
function normalizeBullet(text) {
|
|
63066
63330
|
text = text.trim();
|
|
@@ -63220,7 +63484,7 @@ function applyChangelog(target, entries, normalizeOnly) {
|
|
|
63220
63484
|
const rendered = renderChangelog(source.text, entries, normalizeOnly);
|
|
63221
63485
|
if ("error" in rendered)
|
|
63222
63486
|
return { error: rendered.error };
|
|
63223
|
-
fs17.mkdirSync(
|
|
63487
|
+
fs17.mkdirSync(path25.dirname(target), { recursive: true });
|
|
63224
63488
|
fs17.writeFileSync(target, rendered.text, "utf8");
|
|
63225
63489
|
return { ...rendered.result, path: target };
|
|
63226
63490
|
}
|
|
@@ -63363,14 +63627,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
63363
63627
|
}, changelogTarget = (workspaceRoot, changelogPath) => {
|
|
63364
63628
|
try {
|
|
63365
63629
|
const cwd = fs17.realpathSync(resolveWorkspaceRoot(workspaceRoot));
|
|
63366
|
-
const target =
|
|
63367
|
-
if (target !== cwd && !target.startsWith(cwd +
|
|
63630
|
+
const target = path25.resolve(cwd, changelogPath || "CHANGELOG.md");
|
|
63631
|
+
if (target !== cwd && !target.startsWith(cwd + path25.sep))
|
|
63368
63632
|
return { error: "changelog path must be inside workspace_root" };
|
|
63369
63633
|
let ancestor = target;
|
|
63370
63634
|
while (!fs17.existsSync(ancestor))
|
|
63371
|
-
ancestor =
|
|
63635
|
+
ancestor = path25.dirname(ancestor);
|
|
63372
63636
|
const canonicalAncestor = fs17.realpathSync(ancestor);
|
|
63373
|
-
if (canonicalAncestor !== cwd && !canonicalAncestor.startsWith(cwd +
|
|
63637
|
+
if (canonicalAncestor !== cwd && !canonicalAncestor.startsWith(cwd + path25.sep))
|
|
63374
63638
|
return { error: "changelog path must be inside workspace_root" };
|
|
63375
63639
|
return { cwd, target: fs17.existsSync(target) ? fs17.realpathSync(target) : target };
|
|
63376
63640
|
} catch {
|
|
@@ -63389,22 +63653,22 @@ var init_changelog = __esm(() => {
|
|
|
63389
63653
|
|
|
63390
63654
|
// packages/workit-core/src/core/hygiene.ts
|
|
63391
63655
|
import { existsSync as existsSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "node:fs";
|
|
63392
|
-
import
|
|
63393
|
-
var repoRoot2, templatesDir = () =>
|
|
63394
|
-
if (!existsSync9(
|
|
63656
|
+
import path26 from "node:path";
|
|
63657
|
+
var repoRoot2, templatesDir = () => path26.join(repoRoot2, "templates", "hygiene"), packageJson = (root) => {
|
|
63658
|
+
if (!existsSync9(path26.join(root, "package.json")))
|
|
63395
63659
|
return null;
|
|
63396
63660
|
try {
|
|
63397
|
-
return JSON.parse(readFileSync9(
|
|
63661
|
+
return JSON.parse(readFileSync9(path26.join(root, "package.json"), "utf8"));
|
|
63398
63662
|
} catch {
|
|
63399
63663
|
return null;
|
|
63400
63664
|
}
|
|
63401
63665
|
}, isOpenSource = (root) => {
|
|
63402
|
-
if (existsSync9(
|
|
63666
|
+
if (existsSync9(path26.join(root, "LICENSE")))
|
|
63403
63667
|
return true;
|
|
63404
63668
|
const pkg = packageJson(root);
|
|
63405
63669
|
if (pkg && !pkg.private)
|
|
63406
63670
|
return true;
|
|
63407
|
-
return
|
|
63671
|
+
return path26.basename(root) === "workflow-toolkit";
|
|
63408
63672
|
}, licenseHolder = (root) => {
|
|
63409
63673
|
const pkg = packageJson(root);
|
|
63410
63674
|
if (pkg) {
|
|
@@ -63425,13 +63689,13 @@ var repoRoot2, templatesDir = () => path25.join(repoRoot2, "templates", "hygiene
|
|
|
63425
63689
|
files.push("LICENSE", "CONTRIBUTING.md");
|
|
63426
63690
|
const planned = [];
|
|
63427
63691
|
for (const file of files) {
|
|
63428
|
-
if (existsSync9(
|
|
63692
|
+
if (existsSync9(path26.join(root, file)))
|
|
63429
63693
|
continue;
|
|
63430
|
-
const tpl =
|
|
63694
|
+
const tpl = path26.join(templatesDir(), file);
|
|
63431
63695
|
if (!existsSync9(tpl))
|
|
63432
63696
|
continue;
|
|
63433
|
-
const content = readFileSync9(tpl, "utf8").replace(/<PROJECT>/g,
|
|
63434
|
-
planned.push({ path:
|
|
63697
|
+
const content = readFileSync9(tpl, "utf8").replace(/<PROJECT>/g, path26.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
|
|
63698
|
+
planned.push({ path: path26.join(root, file), content });
|
|
63435
63699
|
}
|
|
63436
63700
|
return planned;
|
|
63437
63701
|
};
|
|
@@ -63443,9 +63707,9 @@ var init_hygiene = __esm(() => {
|
|
|
63443
63707
|
|
|
63444
63708
|
// packages/workit-cli/src/admin/setup-state.ts
|
|
63445
63709
|
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "node:fs";
|
|
63446
|
-
import
|
|
63710
|
+
import path27 from "node:path";
|
|
63447
63711
|
var classifySetupFile = (dir, name) => {
|
|
63448
|
-
const file =
|
|
63712
|
+
const file = path27.join(dir, name);
|
|
63449
63713
|
let raw;
|
|
63450
63714
|
try {
|
|
63451
63715
|
raw = readFileSync10(file, "utf8");
|
|
@@ -63483,7 +63747,7 @@ var init_setup_state = __esm(() => {
|
|
|
63483
63747
|
});
|
|
63484
63748
|
|
|
63485
63749
|
// packages/workit-cli/src/admin/registration.ts
|
|
63486
|
-
import
|
|
63750
|
+
import path28 from "node:path";
|
|
63487
63751
|
function isWorkitPlugin(value) {
|
|
63488
63752
|
const s = String(value).replaceAll("\\", "/");
|
|
63489
63753
|
const url = s.startsWith("file://") || s.startsWith("git+file://");
|
|
@@ -63516,11 +63780,11 @@ function mergeCursorEnabledPlugins(enabled) {
|
|
|
63516
63780
|
}
|
|
63517
63781
|
function mergeCursorPluginDirs(pluginDirs, pluginDir) {
|
|
63518
63782
|
const strip = (p) => {
|
|
63519
|
-
const j =
|
|
63520
|
-
return
|
|
63783
|
+
const j = path28.join(p);
|
|
63784
|
+
return path28.dirname(j) === j ? j : j.replace(/[\\/]+$/, "");
|
|
63521
63785
|
};
|
|
63522
63786
|
const normalized = strip(pluginDir);
|
|
63523
|
-
const legacy = strip(
|
|
63787
|
+
const legacy = strip(path28.join(path28.dirname(pluginDir), "workflow-toolkit"));
|
|
63524
63788
|
const prev = Array.isArray(pluginDirs) ? pluginDirs.map(String) : [];
|
|
63525
63789
|
const kept = prev.filter((d) => strip(d) !== legacy);
|
|
63526
63790
|
const exists = kept.some((d) => strip(d) === normalized);
|
|
@@ -63608,7 +63872,7 @@ function cursorMcpServerEntry(_packageDir) {
|
|
|
63608
63872
|
function cursorHooksEntry(pluginDir) {
|
|
63609
63873
|
return { command: `${cursorHookLauncher(pluginDir)} workit-cursor-session-start`, args: [] };
|
|
63610
63874
|
}
|
|
63611
|
-
var OPENCODE_NPM_PIN = "@brainervirus/workit-opencode", isRecord4 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), named = (s, name) => s === name || s.startsWith(`${name}@`), CURSOR_RUNTIME_PACKAGE = "@brainervirus/workit-cursor@latest", cursorHookLauncher = (pluginDir) => pluginDir ? `node "${shellQuotedPath(
|
|
63875
|
+
var OPENCODE_NPM_PIN = "@brainervirus/workit-opencode", isRecord4 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), named = (s, name) => s === name || s.startsWith(`${name}@`), CURSOR_RUNTIME_PACKAGE = "@brainervirus/workit-cursor@latest", cursorHookLauncher = (pluginDir) => pluginDir ? `node "${shellQuotedPath(path28.join(pluginDir, "hooks", "launch.mjs"))}"` : 'node "${CURSOR_PLUGIN_ROOT}/hooks/launch.mjs"', shellQuotedPath = (file) => path28.sep === "\\" ? file : file.replace(/([$`"\\])/g, "\\$1"), CURSOR_HOOK_RUN_COMMAND, CURSOR_PRETOOLUSE_MATCHER = "Write|Edit|Delete|Shell|Remove|ApplyPatch|Apply_Patch|Patch|Rename|Mkdir|Mv|Cp|Touch", CURSOR_HOOK_EVENTS, canonicalHookEntry = (event, pluginDir) => {
|
|
63612
63876
|
const command = `${cursorHookLauncher(pluginDir)} workit-cursor-hook`;
|
|
63613
63877
|
if (event === "preToolUse")
|
|
63614
63878
|
return { command, matcher: CURSOR_PRETOOLUSE_MATCHER, failClosed: false };
|
|
@@ -63641,219 +63905,6 @@ var init_support_matrix = __esm(() => {
|
|
|
63641
63905
|
};
|
|
63642
63906
|
});
|
|
63643
63907
|
|
|
63644
|
-
// packages/workit-core/src/core/logger.ts
|
|
63645
|
-
import { appendFileSync, mkdirSync as mkdirSync4, readdirSync as readdirSync4, unlinkSync as unlinkSync2 } from "node:fs";
|
|
63646
|
-
import os6 from "node:os";
|
|
63647
|
-
import path28 from "node:path";
|
|
63648
|
-
var REDACTED = "[REDACTED]", DEFAULT_MAX_RATE = 20, DEFAULT_RATE_WINDOW_MS = 1000, DEFAULT_MAX_FIELD_LENGTH = 200, DEFAULT_MAX_STACK_LINES = 30, DAY_FILE, RETAINED_DAYS = 7, SECRET_VALUE, KEY_EQ_VALUE, URL_QUERY, splitKey = (key) => key.split(/[_-]+|(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/).map((word) => word.toLowerCase()), SENSITIVE_WORDS, STACK_WORDS, escapeRegExp3 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), resolveStateDir = () => {
|
|
63649
|
-
const override = process.env.WORKFLOW_TOOLKIT_STATE;
|
|
63650
|
-
if (override)
|
|
63651
|
-
return override;
|
|
63652
|
-
const home = os6.homedir();
|
|
63653
|
-
if (process.env.XDG_STATE_HOME)
|
|
63654
|
-
return path28.join(process.env.XDG_STATE_HOME, "workit");
|
|
63655
|
-
switch (os6.platform()) {
|
|
63656
|
-
case "darwin":
|
|
63657
|
-
return path28.join(home, "Library", "Application Support", "workit");
|
|
63658
|
-
case "win32":
|
|
63659
|
-
return path28.join(process.env.LOCALAPPDATA ?? path28.join(home, "AppData", "Local"), "workit");
|
|
63660
|
-
default:
|
|
63661
|
-
return path28.join(process.env.HOME ?? home, ".local", "state", "workit");
|
|
63662
|
-
}
|
|
63663
|
-
}, homePattern, applyPatterns = (value) => {
|
|
63664
|
-
let out = value;
|
|
63665
|
-
out = out.replace(SECRET_VALUE, REDACTED);
|
|
63666
|
-
out = out.replace(KEY_EQ_VALUE, "$1$2[REDACTED]");
|
|
63667
|
-
out = out.replace(URL_QUERY, "$1?[REDACTED]");
|
|
63668
|
-
out = out.replace(homePattern, "~");
|
|
63669
|
-
if (process.platform === "win32")
|
|
63670
|
-
out = out.replace(/\\/g, "/");
|
|
63671
|
-
return out;
|
|
63672
|
-
}, redactString = (value, options) => {
|
|
63673
|
-
const out = applyPatterns(value);
|
|
63674
|
-
if (out.length > options.maxFieldLength) {
|
|
63675
|
-
return `${out.slice(0, options.maxFieldLength)}…`;
|
|
63676
|
-
}
|
|
63677
|
-
return out;
|
|
63678
|
-
}, boundStack = (value, options) => {
|
|
63679
|
-
if (typeof value !== "string")
|
|
63680
|
-
return redact(value, options);
|
|
63681
|
-
const lines = value.split(`
|
|
63682
|
-
`);
|
|
63683
|
-
const kept = lines.slice(0, options.maxStackLines).map(applyPatterns);
|
|
63684
|
-
const dropped = lines.length - kept.length;
|
|
63685
|
-
const body = dropped > 0 ? [...kept, ` ... ${dropped} more`] : kept;
|
|
63686
|
-
return body.join(`
|
|
63687
|
-
`);
|
|
63688
|
-
}, redact = (value, options = DEFAULT_OPTIONS) => {
|
|
63689
|
-
if (value === null || value === undefined)
|
|
63690
|
-
return null;
|
|
63691
|
-
if (typeof value === "boolean" || typeof value === "number")
|
|
63692
|
-
return value;
|
|
63693
|
-
if (typeof value === "string")
|
|
63694
|
-
return redactString(value, options);
|
|
63695
|
-
if (value instanceof Date)
|
|
63696
|
-
return value.toISOString();
|
|
63697
|
-
if (Array.isArray(value))
|
|
63698
|
-
return value.map((entry) => redact(entry, options));
|
|
63699
|
-
if (typeof value === "object") {
|
|
63700
|
-
const out = {};
|
|
63701
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
63702
|
-
const words = splitKey(key);
|
|
63703
|
-
if (words.some((word) => STACK_WORDS.has(word)))
|
|
63704
|
-
out[key] = boundStack(entry, options);
|
|
63705
|
-
else if (words.some((word) => SENSITIVE_WORDS.has(word)))
|
|
63706
|
-
out[key] = REDACTED;
|
|
63707
|
-
else
|
|
63708
|
-
out[key] = redact(entry, options);
|
|
63709
|
-
}
|
|
63710
|
-
return out;
|
|
63711
|
-
}
|
|
63712
|
-
return null;
|
|
63713
|
-
}, DEFAULT_OPTIONS, dailyFileName = (date) => {
|
|
63714
|
-
const y = date.getFullYear();
|
|
63715
|
-
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
63716
|
-
const d = String(date.getDate()).padStart(2, "0");
|
|
63717
|
-
return `workit-${y}-${m}-${d}.jsonl`;
|
|
63718
|
-
}, pruneOldFiles = (dir) => {
|
|
63719
|
-
try {
|
|
63720
|
-
const files = readdirSync4(dir).filter((name) => DAY_FILE.test(name)).toSorted().toReversed();
|
|
63721
|
-
for (const file of files.slice(RETAINED_DAYS)) {
|
|
63722
|
-
try {
|
|
63723
|
-
unlinkSync2(path28.join(dir, file));
|
|
63724
|
-
} catch {}
|
|
63725
|
-
}
|
|
63726
|
-
} catch {}
|
|
63727
|
-
}, createLogger = (options = {}) => {
|
|
63728
|
-
const stateDir = options.stateDir ?? resolveStateDir();
|
|
63729
|
-
const logDir = path28.join(stateDir, "logs");
|
|
63730
|
-
const now = options.now ?? (() => new Date);
|
|
63731
|
-
const maxRate = options.maxRate ?? DEFAULT_MAX_RATE;
|
|
63732
|
-
const rateWindowMs = options.rateWindowMs ?? DEFAULT_RATE_WINDOW_MS;
|
|
63733
|
-
const redactOptions = {
|
|
63734
|
-
maxFieldLength: options.maxFieldLength ?? DEFAULT_MAX_FIELD_LENGTH,
|
|
63735
|
-
maxStackLines: options.maxStackLines ?? DEFAULT_MAX_STACK_LINES
|
|
63736
|
-
};
|
|
63737
|
-
let windowStart = 0;
|
|
63738
|
-
const windowCounts = { debug: 0, info: 0, warn: 0, error: 0 };
|
|
63739
|
-
const emit = (level, message, context) => {
|
|
63740
|
-
const date = now();
|
|
63741
|
-
const elapsed = date.getTime() - windowStart;
|
|
63742
|
-
if (elapsed >= rateWindowMs || windowStart === 0) {
|
|
63743
|
-
windowStart = date.getTime();
|
|
63744
|
-
windowCounts.debug = 0;
|
|
63745
|
-
windowCounts.info = 0;
|
|
63746
|
-
windowCounts.warn = 0;
|
|
63747
|
-
windowCounts.error = 0;
|
|
63748
|
-
}
|
|
63749
|
-
windowCounts[level] += 1;
|
|
63750
|
-
if (windowCounts[level] > maxRate)
|
|
63751
|
-
return;
|
|
63752
|
-
let event = null;
|
|
63753
|
-
try {
|
|
63754
|
-
event = {
|
|
63755
|
-
level,
|
|
63756
|
-
time: date.toISOString(),
|
|
63757
|
-
message: redactString(message, redactOptions),
|
|
63758
|
-
context: redact(context ?? {}, redactOptions)
|
|
63759
|
-
};
|
|
63760
|
-
const line = `${JSON.stringify(event)}
|
|
63761
|
-
`;
|
|
63762
|
-
mkdirSync4(logDir, { recursive: true, mode: 448 });
|
|
63763
|
-
appendFileSync(path28.join(logDir, dailyFileName(date)), line, { mode: 384 });
|
|
63764
|
-
pruneOldFiles(logDir);
|
|
63765
|
-
} catch {
|
|
63766
|
-
try {
|
|
63767
|
-
event = {
|
|
63768
|
-
level,
|
|
63769
|
-
time: date.toISOString(),
|
|
63770
|
-
message: redactString(message, redactOptions),
|
|
63771
|
-
context: { redaction_failed: true }
|
|
63772
|
-
};
|
|
63773
|
-
mkdirSync4(logDir, { recursive: true, mode: 448 });
|
|
63774
|
-
appendFileSync(path28.join(logDir, dailyFileName(date)), `${JSON.stringify(event)}
|
|
63775
|
-
`, {
|
|
63776
|
-
mode: 384
|
|
63777
|
-
});
|
|
63778
|
-
} catch {}
|
|
63779
|
-
}
|
|
63780
|
-
for (const sink of [options.appLog, options.stderr]) {
|
|
63781
|
-
if (!sink)
|
|
63782
|
-
continue;
|
|
63783
|
-
if (!event)
|
|
63784
|
-
continue;
|
|
63785
|
-
try {
|
|
63786
|
-
sink(event);
|
|
63787
|
-
} catch {}
|
|
63788
|
-
}
|
|
63789
|
-
};
|
|
63790
|
-
return {
|
|
63791
|
-
debug: (message, context) => emit("debug", message, context),
|
|
63792
|
-
info: (message, context) => emit("info", message, context),
|
|
63793
|
-
warn: (message, context) => emit("warn", message, context),
|
|
63794
|
-
error: (message, context) => emit("error", message, context),
|
|
63795
|
-
guard: (name, fn) => {
|
|
63796
|
-
try {
|
|
63797
|
-
return fn();
|
|
63798
|
-
} catch (err) {
|
|
63799
|
-
emit("warn", "detector_failed", {
|
|
63800
|
-
detector: name,
|
|
63801
|
-
error: err instanceof Error ? err.message : String(err)
|
|
63802
|
-
});
|
|
63803
|
-
return;
|
|
63804
|
-
}
|
|
63805
|
-
}
|
|
63806
|
-
};
|
|
63807
|
-
};
|
|
63808
|
-
var init_logger = __esm(() => {
|
|
63809
|
-
DAY_FILE = /^workit-\d{4}-\d{2}-\d{2}\.jsonl$/;
|
|
63810
|
-
SECRET_VALUE = /\b(?:Bearer|Basic|Digest|Token)\s+\S+/gi;
|
|
63811
|
-
KEY_EQ_VALUE = /\b([A-Za-z0-9_-]*(?:token|secret|password|passwd|apikey|api[_-]?key|authorization|credential|bearer)[A-Za-z0-9_-]*)([:=]\s*).+/gi;
|
|
63812
|
-
URL_QUERY = /(https?:\/\/[^?#\s]+)\?[^#\s]*/g;
|
|
63813
|
-
SENSITIVE_WORDS = new Set([
|
|
63814
|
-
"token",
|
|
63815
|
-
"secret",
|
|
63816
|
-
"password",
|
|
63817
|
-
"passwd",
|
|
63818
|
-
"authorization",
|
|
63819
|
-
"credential",
|
|
63820
|
-
"credentials",
|
|
63821
|
-
"cookie",
|
|
63822
|
-
"api",
|
|
63823
|
-
"apikey",
|
|
63824
|
-
"apitoken",
|
|
63825
|
-
"accesstoken",
|
|
63826
|
-
"clientsecret",
|
|
63827
|
-
"refreshtoken",
|
|
63828
|
-
"key",
|
|
63829
|
-
"bearer",
|
|
63830
|
-
"prompt",
|
|
63831
|
-
"message",
|
|
63832
|
-
"messages",
|
|
63833
|
-
"content",
|
|
63834
|
-
"body",
|
|
63835
|
-
"args",
|
|
63836
|
-
"argument",
|
|
63837
|
-
"result",
|
|
63838
|
-
"results",
|
|
63839
|
-
"output",
|
|
63840
|
-
"issue",
|
|
63841
|
-
"description",
|
|
63842
|
-
"summary",
|
|
63843
|
-
"payload",
|
|
63844
|
-
"script",
|
|
63845
|
-
"command",
|
|
63846
|
-
"text",
|
|
63847
|
-
"query"
|
|
63848
|
-
]);
|
|
63849
|
-
STACK_WORDS = new Set(["stack", "stacktrace", "trace"]);
|
|
63850
|
-
homePattern = new RegExp(`${escapeRegExp3(os6.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
|
|
63851
|
-
DEFAULT_OPTIONS = {
|
|
63852
|
-
maxFieldLength: DEFAULT_MAX_FIELD_LENGTH,
|
|
63853
|
-
maxStackLines: DEFAULT_MAX_STACK_LINES
|
|
63854
|
-
};
|
|
63855
|
-
});
|
|
63856
|
-
|
|
63857
63908
|
// packages/workit-cli/src/admin/host-install.ts
|
|
63858
63909
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
63859
63910
|
import {
|
|
@@ -64447,6 +64498,26 @@ function installSessionHook(cwd, env = process.env) {
|
|
|
64447
64498
|
detail: `${verb} the Workit ${SESSION_HOOK_NAME} hook at ${status.hookPath}${helper}`
|
|
64448
64499
|
};
|
|
64449
64500
|
}
|
|
64501
|
+
function removeSessionHook(cwd, env = process.env) {
|
|
64502
|
+
const status = inspectSessionHook(cwd, env);
|
|
64503
|
+
if (status.state === "not_git")
|
|
64504
|
+
return { action: "skipped", removed: [], status, detail: "not inside a git work tree" };
|
|
64505
|
+
const removed = [];
|
|
64506
|
+
if (workitHookIn(status)) {
|
|
64507
|
+
rmSync2(status.hookPath, { force: true });
|
|
64508
|
+
removed.push(status.hookPath);
|
|
64509
|
+
}
|
|
64510
|
+
if (helperIn(status)) {
|
|
64511
|
+
rmSync2(status.helperPath, { force: true });
|
|
64512
|
+
removed.push(status.helperPath);
|
|
64513
|
+
}
|
|
64514
|
+
return {
|
|
64515
|
+
action: removed.length ? "removed" : "unchanged",
|
|
64516
|
+
removed,
|
|
64517
|
+
status: inspectSessionHook(cwd, env),
|
|
64518
|
+
detail: removed.length ? `removed ${removed.join(" and ")}` : "no Workit session hook or helper to remove"
|
|
64519
|
+
};
|
|
64520
|
+
}
|
|
64450
64521
|
var SESSION_HOOK_NAME = "prepare-commit-msg", SESSION_HOOK_MARKER = "# workit-session-trailer-hook", HELPER, LEGACY_CHAINED = "commit-msg.workit-chained", SESSION_TRAILER_SCRIPT = `#!/bin/sh
|
|
64451
64522
|
# Workit (https://github.com/BrainerVirus/workit) wrote this file with
|
|
64452
64523
|
# \`workit doctor --fix\`. A prepare-commit-msg hook runs it with its own
|
|
@@ -64474,6 +64545,19 @@ fi
|
|
|
64474
64545
|
[ -n "$session" ] || exit 0
|
|
64475
64546
|
[ \${#session} -le 128 ] || exit 0
|
|
64476
64547
|
case "$session" in *[!A-Za-z0-9_.:@/+-]*) exit 0 ;; esac
|
|
64548
|
+
# A workspace with \`trailers: false\` keeps authorship in the ledger only.
|
|
64549
|
+
# \`workit git commit\` decides it already (WORKIT_TRAILERS); otherwise ask
|
|
64550
|
+
# workit, bounded, and add the trailer when it cannot answer.
|
|
64551
|
+
case "\${WORKIT_TRAILERS-}" in
|
|
64552
|
+
off) exit 0 ;;
|
|
64553
|
+
on) ;;
|
|
64554
|
+
*)
|
|
64555
|
+
if command -v workit >/dev/null 2>&1; then
|
|
64556
|
+
t=; ! command -v timeout >/dev/null 2>&1 || t="timeout 5"
|
|
64557
|
+
[ "$($t workit git trailers 2>/dev/null)" != off ] || exit 0
|
|
64558
|
+
fi
|
|
64559
|
+
;;
|
|
64560
|
+
esac
|
|
64477
64561
|
add_trailer() {
|
|
64478
64562
|
[ -f "$1" ] && [ -r "$1" ] || return 0
|
|
64479
64563
|
body=$(sed '/^# -\\{24\\} >8 -\\{24\\}$/,$d' "$1" | git stripspace --strip-comments)
|
|
@@ -64617,7 +64701,7 @@ exit 0
|
|
|
64617
64701
|
} finally {
|
|
64618
64702
|
rmSync2(staged, { force: true });
|
|
64619
64703
|
}
|
|
64620
|
-
};
|
|
64704
|
+
}, workitHookIn = (status) => status.hookPath !== null && (readText2(status.hookPath)?.includes(SESSION_HOOK_MARKER) ?? false), helperIn = (status) => status.helperPath !== null && exists(status.helperPath);
|
|
64621
64705
|
var init_session_hook = __esm(() => {
|
|
64622
64706
|
init_rev();
|
|
64623
64707
|
HELPER = ["workit", "session-trailer.sh"];
|
|
@@ -66052,12 +66136,34 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
66052
66136
|
};
|
|
66053
66137
|
}
|
|
66054
66138
|
}, checkSessionHook = (res) => {
|
|
66055
|
-
|
|
66139
|
+
const workspace = matchedWorkspace(res);
|
|
66140
|
+
if (!workspace)
|
|
66056
66141
|
return {
|
|
66057
66142
|
id: "session_hook",
|
|
66058
66143
|
status: "pass",
|
|
66059
66144
|
detail: `not in a Workit workspace — skipping the ${SESSION_HOOK_NAME} session hook`
|
|
66060
66145
|
};
|
|
66146
|
+
const trailers = workspaceTrailers(res.cwd, res.configDir);
|
|
66147
|
+
if (!trailers.enabled) {
|
|
66148
|
+
const setting = `workspace ${trailers.from ?? workspace.name} sets trailers: false`;
|
|
66149
|
+
const left = inspectSessionHook(res.cwd, res.env);
|
|
66150
|
+
const files = [
|
|
66151
|
+
...workitHookIn(left) ? [left.hookPath] : [],
|
|
66152
|
+
...helperIn(left) ? [left.helperPath] : []
|
|
66153
|
+
];
|
|
66154
|
+
if (files.length)
|
|
66155
|
+
return {
|
|
66156
|
+
id: "session_hook",
|
|
66157
|
+
status: "warn",
|
|
66158
|
+
detail: `${setting}, but Workit's ${files.join(" and ")} ${files.length > 1 ? "are" : "is"} still installed: plain git commits still get a Workit-Session trailer whenever the helper cannot ask workit (it fails open)`,
|
|
66159
|
+
fix: SESSION_HOOK_FIX
|
|
66160
|
+
};
|
|
66161
|
+
return {
|
|
66162
|
+
id: "session_hook",
|
|
66163
|
+
status: "pass",
|
|
66164
|
+
detail: `${setting} (authorship in the ledger only) — no ${SESSION_HOOK_NAME} session hook needed`
|
|
66165
|
+
};
|
|
66166
|
+
}
|
|
66061
66167
|
const hook = inspectSessionHook(res.cwd, res.env);
|
|
66062
66168
|
const finding = sessionHookFinding(hook);
|
|
66063
66169
|
if (!hook.leftover)
|
|
@@ -66077,6 +66183,14 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
66077
66183
|
detail: `${res.cwd} is not in a Workit workspace; no hook installed`,
|
|
66078
66184
|
workspace: null
|
|
66079
66185
|
};
|
|
66186
|
+
if (!workspaceTrailers(res.cwd, res.configDir).enabled) {
|
|
66187
|
+
const removal = removeSessionHook(res.cwd, res.env);
|
|
66188
|
+
return {
|
|
66189
|
+
...removal,
|
|
66190
|
+
detail: `trailers: false; no hook installed (${removal.detail})`,
|
|
66191
|
+
workspace: workspace.name
|
|
66192
|
+
};
|
|
66193
|
+
}
|
|
66080
66194
|
return { ...installSessionHook(res.cwd, res.env), workspace: workspace.name };
|
|
66081
66195
|
}, isPlaceholder = (p) => {
|
|
66082
66196
|
try {
|
|
@@ -66359,7 +66473,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
66359
66473
|
fix: repair,
|
|
66360
66474
|
registryProbed
|
|
66361
66475
|
};
|
|
66362
|
-
}, HOOK_ISSUED_MARKER = "workit-issued-sessions/
|
|
66476
|
+
}, HOOK_ISSUED_MARKER = "workit-issued-sessions/2", hookRecordsIssued = (root) => {
|
|
66363
66477
|
let files;
|
|
66364
66478
|
try {
|
|
66365
66479
|
files = readdirSync7(path32.join(root, "dist")).filter((f) => f.endsWith(".js"));
|
|
@@ -72515,7 +72629,10 @@ function runCheckCommand(argv, options) {
|
|
|
72515
72629
|
const buffer = new TailBuffer(MAX_LOG_BYTES);
|
|
72516
72630
|
const redactor = new StreamRedactor((text) => buffer.push(text));
|
|
72517
72631
|
const env = options.env ?? process.env;
|
|
72518
|
-
const
|
|
72632
|
+
const wrapped = Boolean(options.wrapper?.length) && platform !== "win32";
|
|
72633
|
+
const command = wrapped ? [...options.wrapper, ...options.shell ? ["/bin/sh", "-c", argv[0]] : argv] : argv;
|
|
72634
|
+
const shell = wrapped ? false : options.shell;
|
|
72635
|
+
const plan = planSpawn(command, { platform, env, cwd: options.cwd, shell });
|
|
72519
72636
|
return new Promise((resolve) => {
|
|
72520
72637
|
let settled = false;
|
|
72521
72638
|
let timedOut = false;
|
|
@@ -72578,6 +72695,8 @@ function runCheckCommand(argv, options) {
|
|
|
72578
72695
|
finish(null, null, error.code ?? error.message);
|
|
72579
72696
|
return;
|
|
72580
72697
|
}
|
|
72698
|
+
if (child.pid)
|
|
72699
|
+
options.onSpawn?.(child.pid);
|
|
72581
72700
|
if (!windows)
|
|
72582
72701
|
for (const name of FORWARDED_SIGNALS) {
|
|
72583
72702
|
const handler = () => {
|
|
@@ -72915,18 +73034,503 @@ var init_grant = __esm(() => {
|
|
|
72915
73034
|
};
|
|
72916
73035
|
});
|
|
72917
73036
|
|
|
73037
|
+
// packages/workit-core/src/core/resources.ts
|
|
73038
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
73039
|
+
import fs20 from "node:fs";
|
|
73040
|
+
import os14 from "node:os";
|
|
73041
|
+
import path40 from "node:path";
|
|
73042
|
+
function memoryValue(value, host) {
|
|
73043
|
+
const text = typeof value === "number" ? String(value) : typeof value === "string" ? value : "";
|
|
73044
|
+
const percent = PERCENT.exec(text.trim());
|
|
73045
|
+
if (percent) {
|
|
73046
|
+
const share = Number(percent[1]);
|
|
73047
|
+
if (share <= 0 || share > 100)
|
|
73048
|
+
return new Error(`memoryMax ${text} must be 1-100%`);
|
|
73049
|
+
return Math.floor(host.totalMemBytes * share / 100);
|
|
73050
|
+
}
|
|
73051
|
+
const size = SIZE.exec(text.trim());
|
|
73052
|
+
if (size && Number(size[1]) > 0)
|
|
73053
|
+
return Math.floor(Number(size[1]) * UNITS[size[2].toUpperCase()]);
|
|
73054
|
+
return new Error(`memoryMax must be a percent of RAM ("40%") or a size ("8G"), got ${text}`);
|
|
73055
|
+
}
|
|
73056
|
+
function cpuValue(value, host) {
|
|
73057
|
+
if (typeof value === "number" && value > 0)
|
|
73058
|
+
return Math.round(value * 100);
|
|
73059
|
+
const percent = typeof value === "string" ? PERCENT.exec(value.trim()) : null;
|
|
73060
|
+
if (percent) {
|
|
73061
|
+
const share = Number(percent[1]);
|
|
73062
|
+
if (share <= 0 || share > 100)
|
|
73063
|
+
return new Error(`cpuQuota ${percent[0]} must be 1-100%`);
|
|
73064
|
+
return Math.max(1, Math.round(host.cpus * share));
|
|
73065
|
+
}
|
|
73066
|
+
return new Error(`cpuQuota must be a percent of the machine ("70%") or a CPU count, got ${String(value)}`);
|
|
73067
|
+
}
|
|
73068
|
+
function resolveResourceSettings(sources, host = hostShape()) {
|
|
73069
|
+
let memory = Math.floor(host.totalMemBytes * DEFAULT_MEMORY_SHARE);
|
|
73070
|
+
let cpu = Math.max(1, Math.round(host.cpus * DEFAULT_CPU_SHARE * 100));
|
|
73071
|
+
const settings = {
|
|
73072
|
+
slots: DEFAULT_SLOTS,
|
|
73073
|
+
heavy: [...DEFAULT_HEAVY_CHECKS],
|
|
73074
|
+
memoryMax: "",
|
|
73075
|
+
cpuQuota: "",
|
|
73076
|
+
waitMs: DEFAULT_WAIT_S * 1000,
|
|
73077
|
+
scope: true
|
|
73078
|
+
};
|
|
73079
|
+
const warnings = [];
|
|
73080
|
+
const ordered = [
|
|
73081
|
+
...sources.filter((source) => source.trust === "machine"),
|
|
73082
|
+
...sources.filter((source) => source.trust === "repo")
|
|
73083
|
+
];
|
|
73084
|
+
for (const { label, trust, value } of ordered) {
|
|
73085
|
+
if (value === undefined || value === null)
|
|
73086
|
+
continue;
|
|
73087
|
+
if (!isRecord7(value)) {
|
|
73088
|
+
warnings.push(`${label}: resources must be an object`);
|
|
73089
|
+
continue;
|
|
73090
|
+
}
|
|
73091
|
+
const repo = trust === "repo";
|
|
73092
|
+
const warn = (field, message) => warnings.push(`${label}: ${field}: ${message}`);
|
|
73093
|
+
const machineOnly = (field) => warn(field, "is machine-wide; set it in your workspaces.json, not in the repo");
|
|
73094
|
+
if (value.slots !== undefined) {
|
|
73095
|
+
const slots = value.slots;
|
|
73096
|
+
if (repo)
|
|
73097
|
+
machineOnly("slots");
|
|
73098
|
+
else if (typeof slots === "number" && Number.isInteger(slots) && slots >= 1 && slots <= MAX_SLOTS)
|
|
73099
|
+
settings.slots = slots;
|
|
73100
|
+
else
|
|
73101
|
+
warn("slots", `must be an integer 1-${MAX_SLOTS}`);
|
|
73102
|
+
}
|
|
73103
|
+
if (value.scope !== undefined) {
|
|
73104
|
+
if (repo)
|
|
73105
|
+
machineOnly("scope");
|
|
73106
|
+
else if (typeof value.scope === "boolean")
|
|
73107
|
+
settings.scope = value.scope;
|
|
73108
|
+
else
|
|
73109
|
+
warn("scope", "must be true or false");
|
|
73110
|
+
}
|
|
73111
|
+
if (value.heavy !== undefined) {
|
|
73112
|
+
const heavy = isRecord7(value.heavy) ? Object.keys(value.heavy) : value.heavy;
|
|
73113
|
+
if (!Array.isArray(heavy) || !heavy.every((item) => typeof item === "string"))
|
|
73114
|
+
warn("heavy", "must be a list of check names");
|
|
73115
|
+
else
|
|
73116
|
+
settings.heavy = repo ? [...new Set([...settings.heavy, ...heavy])] : heavy;
|
|
73117
|
+
}
|
|
73118
|
+
if (value.memoryMax !== undefined) {
|
|
73119
|
+
const next = memoryValue(value.memoryMax, host);
|
|
73120
|
+
if (next instanceof Error)
|
|
73121
|
+
warn("memoryMax", next.message);
|
|
73122
|
+
else if (repo && next > memory)
|
|
73123
|
+
warn("memoryMax", "a repo can only lower it");
|
|
73124
|
+
else
|
|
73125
|
+
memory = next;
|
|
73126
|
+
}
|
|
73127
|
+
if (value.cpuQuota !== undefined) {
|
|
73128
|
+
const next = cpuValue(value.cpuQuota, host);
|
|
73129
|
+
if (next instanceof Error)
|
|
73130
|
+
warn("cpuQuota", next.message);
|
|
73131
|
+
else if (repo && next > cpu)
|
|
73132
|
+
warn("cpuQuota", "a repo can only lower it");
|
|
73133
|
+
else
|
|
73134
|
+
cpu = next;
|
|
73135
|
+
}
|
|
73136
|
+
if (value.wait !== undefined) {
|
|
73137
|
+
if (typeof value.wait === "number" && value.wait >= 0)
|
|
73138
|
+
settings.waitMs = value.wait * 1000;
|
|
73139
|
+
else
|
|
73140
|
+
warn("wait", "must be a number of seconds");
|
|
73141
|
+
}
|
|
73142
|
+
}
|
|
73143
|
+
settings.memoryMax = String(memory);
|
|
73144
|
+
settings.cpuQuota = `${cpu}%`;
|
|
73145
|
+
return { settings, warnings };
|
|
73146
|
+
}
|
|
73147
|
+
function stateDir(env, platform = process.platform) {
|
|
73148
|
+
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
73149
|
+
return env.WORKFLOW_TOOLKIT_STATE;
|
|
73150
|
+
if (env.XDG_STATE_HOME)
|
|
73151
|
+
return path40.join(env.XDG_STATE_HOME, "workit");
|
|
73152
|
+
const home = env.HOME || os14.homedir();
|
|
73153
|
+
if (platform === "darwin")
|
|
73154
|
+
return path40.join(home, "Library", "Application Support", "workit");
|
|
73155
|
+
if (platform === "win32")
|
|
73156
|
+
return path40.join(env.LOCALAPPDATA ?? path40.join(home, "AppData", "Local"), "workit");
|
|
73157
|
+
return path40.join(home, ".local", "state", "workit");
|
|
73158
|
+
}
|
|
73159
|
+
function scopeBusy(unit) {
|
|
73160
|
+
if (process.platform !== "linux" || !SCOPE_UNIT.test(unit))
|
|
73161
|
+
return false;
|
|
73162
|
+
const uid = process.getuid?.() ?? 0;
|
|
73163
|
+
const manager = `/sys/fs/cgroup/user.slice/user-${uid}.slice/user@${uid}.service`;
|
|
73164
|
+
return [`${manager}/app.slice/${unit}`, `${manager}/${unit}`].some((dir) => (readText3(`${dir}/cgroup.procs`) ?? "").trim() !== "");
|
|
73165
|
+
}
|
|
73166
|
+
function isAncestor(pid) {
|
|
73167
|
+
if (process.platform !== "linux")
|
|
73168
|
+
return true;
|
|
73169
|
+
let current = process.pid;
|
|
73170
|
+
for (let step = 0;step < 128 && current > 1; step += 1) {
|
|
73171
|
+
if (current === pid)
|
|
73172
|
+
return true;
|
|
73173
|
+
const ppid = Number(statFields(current)?.[1]);
|
|
73174
|
+
if (!Number.isInteger(ppid) || ppid <= 0)
|
|
73175
|
+
return false;
|
|
73176
|
+
current = ppid;
|
|
73177
|
+
}
|
|
73178
|
+
return current === pid;
|
|
73179
|
+
}
|
|
73180
|
+
function staleReason(lease, probe = {}) {
|
|
73181
|
+
const boot = probe.boot === undefined ? bootId() : probe.boot;
|
|
73182
|
+
if (lease.bootId && boot) {
|
|
73183
|
+
if (lease.bootId !== boot)
|
|
73184
|
+
return "taken before a reboot";
|
|
73185
|
+
} else if (typeof lease.bootTime === "number") {
|
|
73186
|
+
const now = probe.bootTime === undefined ? bootTime() : probe.bootTime;
|
|
73187
|
+
if (now !== null && Math.abs(now - lease.bootTime) > BOOT_SLACK_MS)
|
|
73188
|
+
return "taken before a reboot";
|
|
73189
|
+
}
|
|
73190
|
+
const start = lease.processStart ? (probe.start ?? processStart)(lease.pid) : null;
|
|
73191
|
+
const age = (probe.now ?? Date.now()) - Date.parse(lease.startedAt);
|
|
73192
|
+
if (!start && typeof lease.maxAgeMs === "number" && age > lease.maxAgeMs)
|
|
73193
|
+
return `older than its ${Math.round(lease.maxAgeMs / 1000)} s bound`;
|
|
73194
|
+
const holder = !(probe.alive ?? pidAlive2)(lease.pid) ? `holder pid ${lease.pid} is gone` : start && start !== lease.processStart ? `pid ${lease.pid} now belongs to another process` : null;
|
|
73195
|
+
if (!holder)
|
|
73196
|
+
return null;
|
|
73197
|
+
if (typeof lease.pgid === "number" && lease.pgid > 0 && (probe.group ?? groupAlive)(lease.pgid))
|
|
73198
|
+
return null;
|
|
73199
|
+
if (lease.scope && (probe.scope ?? scopeBusy)(lease.scope))
|
|
73200
|
+
return null;
|
|
73201
|
+
return holder;
|
|
73202
|
+
}
|
|
73203
|
+
function reclaim(file, stale) {
|
|
73204
|
+
const guard = `${file}.reclaim`;
|
|
73205
|
+
try {
|
|
73206
|
+
fs20.mkdirSync(guard);
|
|
73207
|
+
} catch {
|
|
73208
|
+
try {
|
|
73209
|
+
if (Date.now() - fs20.statSync(guard).mtimeMs > GUARD_TTL_MS)
|
|
73210
|
+
fs20.rmSync(guard, { force: true, recursive: true });
|
|
73211
|
+
} catch {}
|
|
73212
|
+
return false;
|
|
73213
|
+
}
|
|
73214
|
+
try {
|
|
73215
|
+
let ageMs = 0;
|
|
73216
|
+
try {
|
|
73217
|
+
ageMs = Date.now() - fs20.statSync(file).mtimeMs;
|
|
73218
|
+
} catch {
|
|
73219
|
+
return true;
|
|
73220
|
+
}
|
|
73221
|
+
if (!stale(parseLease(readText3(file)), ageMs))
|
|
73222
|
+
return false;
|
|
73223
|
+
fs20.rmSync(file, { force: true });
|
|
73224
|
+
return true;
|
|
73225
|
+
} finally {
|
|
73226
|
+
fs20.rmSync(guard, { force: true, recursive: true });
|
|
73227
|
+
}
|
|
73228
|
+
}
|
|
73229
|
+
function tryOnce(options, record, reclaimed) {
|
|
73230
|
+
const holders = [];
|
|
73231
|
+
const text = `${JSON.stringify(record)}
|
|
73232
|
+
`;
|
|
73233
|
+
for (let slot = 0;slot < options.slots; slot += 1) {
|
|
73234
|
+
const file = path40.join(options.dir, `slot-${slot}.json`);
|
|
73235
|
+
for (let attempt = 0;attempt < 2; attempt += 1) {
|
|
73236
|
+
try {
|
|
73237
|
+
const fd = fs20.openSync(file, "wx", 384);
|
|
73238
|
+
try {
|
|
73239
|
+
fs20.writeSync(fd, text);
|
|
73240
|
+
} finally {
|
|
73241
|
+
fs20.closeSync(fd);
|
|
73242
|
+
}
|
|
73243
|
+
return { slot, file };
|
|
73244
|
+
} catch (error) {
|
|
73245
|
+
if (error.code !== "EEXIST")
|
|
73246
|
+
throw error;
|
|
73247
|
+
}
|
|
73248
|
+
const lease = parseLease(readText3(file));
|
|
73249
|
+
let reason = null;
|
|
73250
|
+
const removed = reclaim(file, (current, ageMs) => {
|
|
73251
|
+
reason = current ? staleReason(current, options.probe) : ageMs > UNREADABLE_TTL_MS ? "unreadable" : null;
|
|
73252
|
+
return reason !== null;
|
|
73253
|
+
});
|
|
73254
|
+
if (removed) {
|
|
73255
|
+
if (lease && reason)
|
|
73256
|
+
reclaimed.push({ ...holderOf(lease), reason });
|
|
73257
|
+
continue;
|
|
73258
|
+
}
|
|
73259
|
+
if (lease)
|
|
73260
|
+
holders.push(holderOf(lease));
|
|
73261
|
+
break;
|
|
73262
|
+
}
|
|
73263
|
+
}
|
|
73264
|
+
return { holders };
|
|
73265
|
+
}
|
|
73266
|
+
function removeOwn(file, id) {
|
|
73267
|
+
if (parseLease(readText3(file))?.id === id)
|
|
73268
|
+
fs20.rmSync(file, { force: true });
|
|
73269
|
+
}
|
|
73270
|
+
async function acquireLease(options) {
|
|
73271
|
+
const pid = options.pid ?? process.pid;
|
|
73272
|
+
const boot = bootId();
|
|
73273
|
+
const record = {
|
|
73274
|
+
id: randomUUID5(),
|
|
73275
|
+
pid,
|
|
73276
|
+
processStart: processStart(pid),
|
|
73277
|
+
bootId: boot,
|
|
73278
|
+
bootTime: boot ? null : bootTime(),
|
|
73279
|
+
pgid: null,
|
|
73280
|
+
maxAgeMs: options.maxAgeMs ?? null,
|
|
73281
|
+
check: options.check,
|
|
73282
|
+
cwd: options.cwd,
|
|
73283
|
+
session: options.session,
|
|
73284
|
+
startedAt: new Date().toISOString(),
|
|
73285
|
+
scope: null
|
|
73286
|
+
};
|
|
73287
|
+
try {
|
|
73288
|
+
fs20.mkdirSync(options.dir, { recursive: true, mode: 448 });
|
|
73289
|
+
} catch (error) {
|
|
73290
|
+
return { ok: false, reason: "unavailable", error: error.message };
|
|
73291
|
+
}
|
|
73292
|
+
const started = Date.now();
|
|
73293
|
+
const reclaimed = [];
|
|
73294
|
+
let waitedOn = [];
|
|
73295
|
+
for (;; ) {
|
|
73296
|
+
let outcome;
|
|
73297
|
+
try {
|
|
73298
|
+
outcome = tryOnce(options, record, reclaimed);
|
|
73299
|
+
} catch (error) {
|
|
73300
|
+
return { ok: false, reason: "unavailable", error: error.message };
|
|
73301
|
+
}
|
|
73302
|
+
if ("slot" in outcome) {
|
|
73303
|
+
const { file, slot } = outcome;
|
|
73304
|
+
let released = false;
|
|
73305
|
+
const release = () => {
|
|
73306
|
+
if (released)
|
|
73307
|
+
return;
|
|
73308
|
+
released = true;
|
|
73309
|
+
process.off("exit", release);
|
|
73310
|
+
removeOwn(file, record.id);
|
|
73311
|
+
};
|
|
73312
|
+
process.on("exit", release);
|
|
73313
|
+
return {
|
|
73314
|
+
ok: true,
|
|
73315
|
+
lease: {
|
|
73316
|
+
record,
|
|
73317
|
+
slot,
|
|
73318
|
+
file,
|
|
73319
|
+
waitedOn,
|
|
73320
|
+
waitedMs: waitedOn.length ? Date.now() - started : 0,
|
|
73321
|
+
reclaimed,
|
|
73322
|
+
release
|
|
73323
|
+
}
|
|
73324
|
+
};
|
|
73325
|
+
}
|
|
73326
|
+
const waited = Date.now() - started;
|
|
73327
|
+
if (!waitedOn.length) {
|
|
73328
|
+
waitedOn = outcome.holders;
|
|
73329
|
+
options.onWait?.(outcome.holders);
|
|
73330
|
+
}
|
|
73331
|
+
if (waited >= options.waitMs)
|
|
73332
|
+
return { ok: false, reason: "timeout", holders: outcome.holders, waitedMs: waited };
|
|
73333
|
+
await sleep(Math.min(options.pollMs ?? 2000, Math.max(1, options.waitMs - waited)));
|
|
73334
|
+
}
|
|
73335
|
+
}
|
|
73336
|
+
function updateLease(lease, patch) {
|
|
73337
|
+
Object.assign(lease.record, patch);
|
|
73338
|
+
try {
|
|
73339
|
+
if (parseLease(readText3(lease.file))?.id !== lease.record.id)
|
|
73340
|
+
return;
|
|
73341
|
+
const temp = `${lease.file}.${process.pid}.tmp`;
|
|
73342
|
+
fs20.writeFileSync(temp, `${JSON.stringify(lease.record)}
|
|
73343
|
+
`, { mode: 384 });
|
|
73344
|
+
fs20.renameSync(temp, lease.file);
|
|
73345
|
+
} catch {}
|
|
73346
|
+
}
|
|
73347
|
+
function heldLease(dir, id, options = {}) {
|
|
73348
|
+
if (!id || !/^[0-9a-f-]{36}$/u.test(id))
|
|
73349
|
+
return null;
|
|
73350
|
+
let names;
|
|
73351
|
+
try {
|
|
73352
|
+
names = fs20.readdirSync(dir);
|
|
73353
|
+
} catch {
|
|
73354
|
+
return null;
|
|
73355
|
+
}
|
|
73356
|
+
for (const name of names) {
|
|
73357
|
+
if (!/^slot-\d+\.json$/u.test(name))
|
|
73358
|
+
continue;
|
|
73359
|
+
const lease = parseLease(readText3(path40.join(dir, name)));
|
|
73360
|
+
if (lease?.id === id && !staleReason(lease, options.probe) && (options.ancestor ?? isAncestor)(lease.pid))
|
|
73361
|
+
return lease;
|
|
73362
|
+
}
|
|
73363
|
+
return null;
|
|
73364
|
+
}
|
|
73365
|
+
function describeHolder(holder, now = Date.now()) {
|
|
73366
|
+
const seconds = Math.max(0, Math.round((now - Date.parse(holder.startedAt)) / 1000));
|
|
73367
|
+
const age = seconds >= 60 ? `${Math.floor(seconds / 60)}m ${seconds % 60}s` : `${seconds}s`;
|
|
73368
|
+
return `${holder.check}, pid ${holder.pid}${holder.session ? `, session ${holder.session}` : ""}, ${age}, ${holder.cwd}`;
|
|
73369
|
+
}
|
|
73370
|
+
function scopeSupport(env, platform = process.platform, read = readText3) {
|
|
73371
|
+
if (platform !== "linux")
|
|
73372
|
+
return { ok: false, reason: `no systemd scopes on ${platform}` };
|
|
73373
|
+
if (!onPath("systemd-run", env))
|
|
73374
|
+
return { ok: false, reason: "systemd-run is not on PATH" };
|
|
73375
|
+
const uid = process.getuid?.() ?? 0;
|
|
73376
|
+
const runtime = env.XDG_RUNTIME_DIR || `/run/user/${uid}`;
|
|
73377
|
+
if (!fs20.existsSync(path40.join(runtime, "systemd", "private")))
|
|
73378
|
+
return { ok: false, reason: "no user systemd manager is running" };
|
|
73379
|
+
const controllers = read(`/sys/fs/cgroup/user.slice/user-${uid}.slice/user@${uid}.service/cgroup.controllers`);
|
|
73380
|
+
if (controllers === null)
|
|
73381
|
+
return { ok: false, reason: "cgroup v2 is not mounted for the user manager" };
|
|
73382
|
+
const missing = ["memory", "cpu"].filter((name) => !controllers.split(/\s+/u).includes(name));
|
|
73383
|
+
if (missing.length)
|
|
73384
|
+
return { ok: false, reason: `the user manager is not delegated ${missing.join(" and ")}` };
|
|
73385
|
+
return { ok: true };
|
|
73386
|
+
}
|
|
73387
|
+
function scopePrefix(unit, settings) {
|
|
73388
|
+
return [
|
|
73389
|
+
"systemd-run",
|
|
73390
|
+
"--user",
|
|
73391
|
+
"--scope",
|
|
73392
|
+
"--quiet",
|
|
73393
|
+
"--collect",
|
|
73394
|
+
`--unit=${unit}`,
|
|
73395
|
+
"-p",
|
|
73396
|
+
`MemoryMax=${settings.memoryMax}`,
|
|
73397
|
+
"-p",
|
|
73398
|
+
`CPUQuota=${settings.cpuQuota}`,
|
|
73399
|
+
"--"
|
|
73400
|
+
];
|
|
73401
|
+
}
|
|
73402
|
+
async function runScoped(run, wrapper, sinks) {
|
|
73403
|
+
if (!wrapper)
|
|
73404
|
+
return { result: await run(null, sinks), fallback: null };
|
|
73405
|
+
let state = "deciding";
|
|
73406
|
+
let firstLine = "";
|
|
73407
|
+
const held = [];
|
|
73408
|
+
const flush = () => {
|
|
73409
|
+
state = "passing";
|
|
73410
|
+
for (const [sink, text] of held.splice(0))
|
|
73411
|
+
sinks[sink](text);
|
|
73412
|
+
};
|
|
73413
|
+
const result = await run(wrapper, {
|
|
73414
|
+
onStdout: (text) => {
|
|
73415
|
+
if (state === "passing")
|
|
73416
|
+
return sinks.onStdout(text);
|
|
73417
|
+
held.push(["onStdout", text]);
|
|
73418
|
+
if (state === "deciding")
|
|
73419
|
+
flush();
|
|
73420
|
+
},
|
|
73421
|
+
onStderr: (text) => {
|
|
73422
|
+
if (state === "passing")
|
|
73423
|
+
return sinks.onStderr(text);
|
|
73424
|
+
held.push(["onStderr", text]);
|
|
73425
|
+
if (state === "held")
|
|
73426
|
+
return;
|
|
73427
|
+
firstLine += text;
|
|
73428
|
+
const newline = firstLine.indexOf(`
|
|
73429
|
+
`);
|
|
73430
|
+
if (newline < 0 && firstLine.length < 4096)
|
|
73431
|
+
return;
|
|
73432
|
+
if (SCOPE_START_FAILURE.test(firstLine.slice(0, newline < 0 ? undefined : newline)))
|
|
73433
|
+
state = "held";
|
|
73434
|
+
else
|
|
73435
|
+
flush();
|
|
73436
|
+
}
|
|
73437
|
+
});
|
|
73438
|
+
const lines = tailLines(result.log, 2);
|
|
73439
|
+
if (state !== "passing" && result.exitCode !== 0 && lines.length === 1 && SCOPE_START_FAILURE.test(lines[0]))
|
|
73440
|
+
return { result: await run(null, sinks), fallback: lines[0] };
|
|
73441
|
+
flush();
|
|
73442
|
+
return { result, fallback: null };
|
|
73443
|
+
}
|
|
73444
|
+
function firstNotice(env, session, key, now = Date.now()) {
|
|
73445
|
+
const id = (session ?? `ppid-${process.ppid}`).replace(/[^A-Za-z0-9_.-]/gu, "_").slice(0, 120);
|
|
73446
|
+
const dir = path40.join(stateDir(env), "notices");
|
|
73447
|
+
try {
|
|
73448
|
+
fs20.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
73449
|
+
fs20.writeFileSync(path40.join(dir, `${key}-${id}`), `${new Date(now).toISOString()}
|
|
73450
|
+
`, {
|
|
73451
|
+
flag: "wx",
|
|
73452
|
+
mode: 384
|
|
73453
|
+
});
|
|
73454
|
+
} catch (error) {
|
|
73455
|
+
return error.code !== "EEXIST";
|
|
73456
|
+
}
|
|
73457
|
+
try {
|
|
73458
|
+
for (const name of fs20.readdirSync(dir)) {
|
|
73459
|
+
const file = path40.join(dir, name);
|
|
73460
|
+
if (now - fs20.statSync(file).mtimeMs > NOTICE_TTL_MS)
|
|
73461
|
+
fs20.rmSync(file, { force: true });
|
|
73462
|
+
}
|
|
73463
|
+
} catch {}
|
|
73464
|
+
return true;
|
|
73465
|
+
}
|
|
73466
|
+
var DEFAULT_HEAVY_CHECKS, DEFAULT_SLOTS = 1, DEFAULT_WAIT_S = 600, DEFAULT_MEMORY_SHARE = 0.4, DEFAULT_CPU_SHARE = 0.7, MAX_SLOTS = 64, UNREADABLE_TTL_MS = 30000, GUARD_TTL_MS = 30000, hostShape = () => ({
|
|
73467
|
+
totalMemBytes: os14.totalmem(),
|
|
73468
|
+
cpus: Math.max(1, os14.availableParallelism?.() ?? os14.cpus().length)
|
|
73469
|
+
}), isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), SIZE, PERCENT, UNITS, leaseDir = (env) => path40.join(stateDir(env), "leases"), readText3 = (file) => {
|
|
73470
|
+
try {
|
|
73471
|
+
return fs20.readFileSync(file, "utf8");
|
|
73472
|
+
} catch {
|
|
73473
|
+
return null;
|
|
73474
|
+
}
|
|
73475
|
+
}, bootId = () => readText3("/proc/sys/kernel/random/boot_id")?.trim() || null, bootTime = () => Math.round((Date.now() - os14.uptime() * 1000) / 1000) * 1000, BOOT_SLACK_MS = 120000, statFields = (pid) => {
|
|
73476
|
+
const stat = readText3(`/proc/${pid}/stat`);
|
|
73477
|
+
return stat ? stat.slice(stat.lastIndexOf(")") + 2).split(" ") : null;
|
|
73478
|
+
}, processStart = (pid) => statFields(pid)?.[19] ?? null, pidAlive2 = (pid) => {
|
|
73479
|
+
try {
|
|
73480
|
+
process.kill(pid, 0);
|
|
73481
|
+
return true;
|
|
73482
|
+
} catch (error) {
|
|
73483
|
+
return error.code === "EPERM";
|
|
73484
|
+
}
|
|
73485
|
+
}, groupAlive = (pgid) => process.platform === "win32" ? pidAlive2(pgid) : pidAlive2(-pgid), SCOPE_UNIT, parseLease = (text) => {
|
|
73486
|
+
if (!text)
|
|
73487
|
+
return null;
|
|
73488
|
+
try {
|
|
73489
|
+
const value = JSON.parse(text);
|
|
73490
|
+
return isRecord7(value) && typeof value.id === "string" && typeof value.pid === "number" ? value : null;
|
|
73491
|
+
} catch {
|
|
73492
|
+
return null;
|
|
73493
|
+
}
|
|
73494
|
+
}, holderOf = (lease) => ({
|
|
73495
|
+
pid: lease.pid,
|
|
73496
|
+
check: lease.check,
|
|
73497
|
+
cwd: lease.cwd,
|
|
73498
|
+
session: lease.session,
|
|
73499
|
+
startedAt: lease.startedAt
|
|
73500
|
+
}), sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), onPath = (command, env) => (env.PATH ?? "").split(path40.delimiter).filter(Boolean).some((dir) => {
|
|
73501
|
+
try {
|
|
73502
|
+
fs20.accessSync(path40.join(dir, command), fs20.constants.X_OK);
|
|
73503
|
+
return true;
|
|
73504
|
+
} catch {
|
|
73505
|
+
return false;
|
|
73506
|
+
}
|
|
73507
|
+
}), SCOPE_START_FAILURE, NOTICE_TTL_MS;
|
|
73508
|
+
var init_resources = __esm(() => {
|
|
73509
|
+
init_checks3();
|
|
73510
|
+
DEFAULT_HEAVY_CHECKS = ["test", "build", "typecheck"];
|
|
73511
|
+
SIZE = /^(\d+(?:\.\d+)?)([KMGT]?)$/iu;
|
|
73512
|
+
PERCENT = /^(\d+(?:\.\d+)?)%$/u;
|
|
73513
|
+
UNITS = { "": 1, K: 1024, M: 1024 ** 2, G: 1024 ** 3, T: 1024 ** 4 };
|
|
73514
|
+
SCOPE_UNIT = /^[\w.@-]+\.scope$/u;
|
|
73515
|
+
SCOPE_START_FAILURE = /^Failed to (?:connect to bus|create bus connection|start transient scope unit|parse \S+?=)/u;
|
|
73516
|
+
NOTICE_TTL_MS = 7 * 86400000;
|
|
73517
|
+
});
|
|
73518
|
+
|
|
72918
73519
|
// packages/workit-cli/src/verbs/check.ts
|
|
72919
73520
|
var exports_check = {};
|
|
72920
73521
|
__export(exports_check, {
|
|
72921
73522
|
run: () => run10
|
|
72922
73523
|
});
|
|
72923
|
-
import
|
|
73524
|
+
import os15 from "node:os";
|
|
73525
|
+
import path41 from "node:path";
|
|
72924
73526
|
function parse5(argv) {
|
|
72925
73527
|
const split = argv.indexOf("--");
|
|
72926
73528
|
const head = split >= 0 ? argv.slice(0, split) : [...argv];
|
|
72927
73529
|
const options = {
|
|
72928
73530
|
name: null,
|
|
72929
73531
|
shell: false,
|
|
73532
|
+
heavy: false,
|
|
73533
|
+
noLease: false,
|
|
72930
73534
|
timeoutMs: 0,
|
|
72931
73535
|
task: null,
|
|
72932
73536
|
base: null,
|
|
@@ -72946,6 +73550,13 @@ function parse5(argv) {
|
|
|
72946
73550
|
options.shell = true;
|
|
72947
73551
|
continue;
|
|
72948
73552
|
}
|
|
73553
|
+
if (flag === "--heavy" || flag === "--no-lease") {
|
|
73554
|
+
if (flag === "--heavy")
|
|
73555
|
+
options.heavy = true;
|
|
73556
|
+
else
|
|
73557
|
+
options.noLease = true;
|
|
73558
|
+
continue;
|
|
73559
|
+
}
|
|
72949
73560
|
if (flag === "--name" || flag === "--task" || flag === "--base" || flag === "--timeout") {
|
|
72950
73561
|
const got = value();
|
|
72951
73562
|
if (got instanceof Error)
|
|
@@ -73069,6 +73680,90 @@ function boundedArgv(argv) {
|
|
|
73069
73680
|
const truncated = cut.length !== argv.length || cut.some((item, index) => item !== argv[index]);
|
|
73070
73681
|
return { argv: cut, truncated };
|
|
73071
73682
|
}
|
|
73683
|
+
async function workspaceResources(io) {
|
|
73684
|
+
const env = io.env;
|
|
73685
|
+
const dir = env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path41.join(env.XDG_CONFIG_HOME || path41.join(env.HOME || os15.homedir(), ".config"), "workit");
|
|
73686
|
+
try {
|
|
73687
|
+
await Promise.resolve().then(() => init_workspaces());
|
|
73688
|
+
const workspace = resolveWorkspaceFrom(io.cwd, dir, env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined);
|
|
73689
|
+
return { value: workspace?.resources, warning: null };
|
|
73690
|
+
} catch (error) {
|
|
73691
|
+
return { value: undefined, warning: `workspaces.json: ${error.message}` };
|
|
73692
|
+
}
|
|
73693
|
+
}
|
|
73694
|
+
async function prepareHeavy(io, settings, label, runCwd, timeoutMs) {
|
|
73695
|
+
const session = hostSessionFromEnv(io.env).session;
|
|
73696
|
+
const dir = leaseDir(io.env);
|
|
73697
|
+
const outer = heldLease(dir, io.env.WORKIT_LEASE);
|
|
73698
|
+
if (outer)
|
|
73699
|
+
return {
|
|
73700
|
+
lease: null,
|
|
73701
|
+
leaseReport: { slot: null, waitedMs: 0, holder: null, reclaimed: [], inherited: true },
|
|
73702
|
+
scope: { unit: outer.scope, reason: "inherited from the outer heavy check" },
|
|
73703
|
+
wrapper: null,
|
|
73704
|
+
env: io.env
|
|
73705
|
+
};
|
|
73706
|
+
const acquired = await acquireLease({
|
|
73707
|
+
dir,
|
|
73708
|
+
slots: settings.slots,
|
|
73709
|
+
check: label,
|
|
73710
|
+
cwd: runCwd,
|
|
73711
|
+
session,
|
|
73712
|
+
waitMs: settings.waitMs,
|
|
73713
|
+
pollMs: 1000,
|
|
73714
|
+
maxAgeMs: (timeoutMs || MAX_UNVERIFIED_RUN_MS) + 60000,
|
|
73715
|
+
onWait: (holders) => io.stderr(`workit: waiting for the heavy-check lease (${settings.slots} slot${settings.slots === 1 ? "" : "s"}): held by ${holders.map((holder) => describeHolder(holder)).join("; ")}
|
|
73716
|
+
`)
|
|
73717
|
+
});
|
|
73718
|
+
if (!acquired.ok && acquired.reason === "timeout") {
|
|
73719
|
+
const holders = acquired.holders.map((holder) => describeHolder(holder)).join("; ");
|
|
73720
|
+
emit(io, fail("busy", `the heavy-check lease stayed busy for ${Math.round(acquired.waitedMs / 1000)} s`, {
|
|
73721
|
+
data: { lease: { waitedMs: acquired.waitedMs, holders: acquired.holders } },
|
|
73722
|
+
unblock: `wait for ${holders || "the holders"} to finish, then run it again`
|
|
73723
|
+
}));
|
|
73724
|
+
return 4;
|
|
73725
|
+
}
|
|
73726
|
+
let lease = null;
|
|
73727
|
+
if (acquired.ok)
|
|
73728
|
+
lease = acquired.lease;
|
|
73729
|
+
else
|
|
73730
|
+
io.stderr(`workit: running without the heavy-check lease: ${acquired.error}
|
|
73731
|
+
`);
|
|
73732
|
+
for (const stale of lease?.reclaimed ?? [])
|
|
73733
|
+
io.stderr(`workit: reclaimed a stale heavy-check lease (${stale.reason}): ${describeHolder(stale)}
|
|
73734
|
+
`);
|
|
73735
|
+
const leaseReport = lease ? {
|
|
73736
|
+
slot: lease.slot,
|
|
73737
|
+
waitedMs: lease.waitedMs,
|
|
73738
|
+
holder: lease.waitedOn[0] ?? null,
|
|
73739
|
+
reclaimed: lease.reclaimed,
|
|
73740
|
+
inherited: false
|
|
73741
|
+
} : null;
|
|
73742
|
+
const env = lease ? { ...io.env, WORKIT_LEASE: lease.record.id } : io.env;
|
|
73743
|
+
const support = settings.scope ? scopeSupport(io.env) : { ok: false, reason: "resources.scope is false" };
|
|
73744
|
+
if (!support.ok) {
|
|
73745
|
+
if (settings.scope && firstNotice(io.env, session, "no-scope"))
|
|
73746
|
+
io.stderr(`workit: heavy checks run without a memory/CPU limit here: ${support.reason} (said once per session)
|
|
73747
|
+
`);
|
|
73748
|
+
return {
|
|
73749
|
+
lease,
|
|
73750
|
+
leaseReport,
|
|
73751
|
+
scope: { unit: null, reason: support.reason },
|
|
73752
|
+
wrapper: null,
|
|
73753
|
+
env
|
|
73754
|
+
};
|
|
73755
|
+
}
|
|
73756
|
+
const unit = `workit-check-${(lease?.record.id ?? "nolease0").slice(0, 8)}-${process.pid}`;
|
|
73757
|
+
if (lease)
|
|
73758
|
+
updateLease(lease, { scope: `${unit}.scope` });
|
|
73759
|
+
return {
|
|
73760
|
+
lease,
|
|
73761
|
+
leaseReport,
|
|
73762
|
+
scope: { unit: `${unit}.scope`, memoryMax: settings.memoryMax, cpuQuota: settings.cpuQuota },
|
|
73763
|
+
wrapper: scopePrefix(unit, settings),
|
|
73764
|
+
env
|
|
73765
|
+
};
|
|
73766
|
+
}
|
|
73072
73767
|
async function run10(argv, io) {
|
|
73073
73768
|
const options = parse5(argv);
|
|
73074
73769
|
if (options instanceof Error)
|
|
@@ -73103,21 +73798,52 @@ async function run10(argv, io) {
|
|
|
73103
73798
|
if (options.shell && command.length !== 1)
|
|
73104
73799
|
return emit(io, fail("invalid_input", '--shell takes the command as one string: workit check --shell -- "<cmd>"'));
|
|
73105
73800
|
}
|
|
73106
|
-
const relCwd = posix(
|
|
73801
|
+
const relCwd = posix(path41.relative(top, runCwd));
|
|
73107
73802
|
const configured = !options.shell && matchesNamedCheck(config, name, command, relCwd);
|
|
73108
73803
|
const actor = actorFromEnv(io.env);
|
|
73109
73804
|
const branchKey = codeKey(io.cwd, { base: options.base });
|
|
73110
73805
|
const detached = branchKey.tree === null ? worktreeTree(io.cwd) : null;
|
|
73111
73806
|
const key = detached ? { ...branchKey, tree: detached.key, dirty: detached.dirty } : branchKey;
|
|
73112
73807
|
const signal = key.tree === null ? null : worktreeSignal(io.cwd);
|
|
73113
|
-
|
|
73114
|
-
|
|
73115
|
-
|
|
73116
|
-
|
|
73117
|
-
|
|
73118
|
-
|
|
73119
|
-
|
|
73120
|
-
|
|
73808
|
+
let heavy = null;
|
|
73809
|
+
if (!options.noLease && (options.heavy || options.command === null)) {
|
|
73810
|
+
const workspace = await workspaceResources(io);
|
|
73811
|
+
const { settings, warnings } = resolveResourceSettings([
|
|
73812
|
+
{ label: "workspaces.json", trust: "machine", value: workspace.value },
|
|
73813
|
+
{ label: CHECKS_FILE, trust: "repo", value: config.resources }
|
|
73814
|
+
]);
|
|
73815
|
+
for (const warning of [workspace.warning, ...warnings])
|
|
73816
|
+
if (warning)
|
|
73817
|
+
io.stderr(`workit: resources: ${warning}
|
|
73818
|
+
`);
|
|
73819
|
+
if (options.heavy || name !== null && settings.heavy.includes(name)) {
|
|
73820
|
+
const prepared = await prepareHeavy(io, settings, name ?? command.join(" "), runCwd, options.timeoutMs);
|
|
73821
|
+
if (typeof prepared === "number")
|
|
73822
|
+
return prepared;
|
|
73823
|
+
heavy = prepared;
|
|
73824
|
+
}
|
|
73825
|
+
}
|
|
73826
|
+
const lease = heavy?.lease ?? null;
|
|
73827
|
+
let result;
|
|
73828
|
+
try {
|
|
73829
|
+
const scoped = await runScoped((wrapper, sinks) => runCheckCommand(command, {
|
|
73830
|
+
cwd: runCwd,
|
|
73831
|
+
shell: options.shell,
|
|
73832
|
+
timeoutMs: options.timeoutMs,
|
|
73833
|
+
env: heavy?.env ?? io.env,
|
|
73834
|
+
...sinks,
|
|
73835
|
+
...wrapper ? { wrapper } : {},
|
|
73836
|
+
...lease ? { onSpawn: (pid) => updateLease(lease, { pgid: pid }) } : {}
|
|
73837
|
+
}), heavy?.wrapper ?? null, { onStdout: io.json ? io.stderr : io.stdout, onStderr: io.stderr });
|
|
73838
|
+
result = scoped.result;
|
|
73839
|
+
if (heavy && scoped.fallback) {
|
|
73840
|
+
io.stderr(`workit: the memory/CPU scope could not start (${scoped.fallback}); ran without it
|
|
73841
|
+
`);
|
|
73842
|
+
heavy.scope = { unit: null, reason: `systemd-run failed: ${scoped.fallback}` };
|
|
73843
|
+
}
|
|
73844
|
+
} finally {
|
|
73845
|
+
lease?.release();
|
|
73846
|
+
}
|
|
73121
73847
|
const treeAfter = key.tree === null ? null : worktreeTree(io.cwd)?.key ?? null;
|
|
73122
73848
|
const modifiedWorktree = key.tree !== null && treeAfter !== key.tree;
|
|
73123
73849
|
const store = storeRoot(io.cwd);
|
|
@@ -73198,6 +73924,8 @@ async function run10(argv, io) {
|
|
|
73198
73924
|
outputBytes: result.outputBytes,
|
|
73199
73925
|
logTruncated: result.truncated,
|
|
73200
73926
|
task: task.task,
|
|
73927
|
+
lease: heavy?.leaseReport ?? null,
|
|
73928
|
+
scope: heavy?.scope ?? null,
|
|
73201
73929
|
...task.note ? { taskNote: task.note } : {},
|
|
73202
73930
|
satisfies: task.satisfies,
|
|
73203
73931
|
stillUnsatisfied: task.stillUnsatisfied,
|
|
@@ -73227,7 +73955,7 @@ async function run10(argv, io) {
|
|
|
73227
73955
|
return recordError.code === "busy" ? 4 : recordError.code === "invalid_input" ? 2 : 5;
|
|
73228
73956
|
return 0;
|
|
73229
73957
|
}
|
|
73230
|
-
var USAGE3 = "workit check <name> | workit check [--name <n>] [--shell] [--timeout <s>] [--task <id>] [--json] -- <cmd…>", RECORD_TAIL_LINES = 20, RECORD_TAIL_CHARS = 200, posix = (value) => value.split(
|
|
73958
|
+
var USAGE3 = "workit check <name> [--no-lease] | workit check [--name <n>] [--shell] [--heavy] [--timeout <s>] [--task <id>] [--json] -- <cmd…>", MAX_UNVERIFIED_RUN_MS, RECORD_TAIL_LINES = 20, RECORD_TAIL_CHARS = 200, posix = (value) => value.split(path41.sep).join("/") || ".", ENGINE_CODES2, FINGERPRINT_VARS;
|
|
73231
73959
|
var init_check = __esm(() => {
|
|
73232
73960
|
init_check_config();
|
|
73233
73961
|
init_checks3();
|
|
@@ -73236,6 +73964,8 @@ var init_check = __esm(() => {
|
|
|
73236
73964
|
init_ledger();
|
|
73237
73965
|
init_output();
|
|
73238
73966
|
init_host_session();
|
|
73967
|
+
init_resources();
|
|
73968
|
+
MAX_UNVERIFIED_RUN_MS = 6 * 3600000;
|
|
73239
73969
|
ENGINE_CODES2 = {
|
|
73240
73970
|
busy: "busy",
|
|
73241
73971
|
not_found: "not_found",
|
|
@@ -73251,7 +73981,7 @@ var success2 = (data) => ({ ok: true, data }), failure3 = (code, error, unblock)
|
|
|
73251
73981
|
// packages/workit-core/src/git/ops.ts
|
|
73252
73982
|
import { spawnSync as spawnSync18 } from "node:child_process";
|
|
73253
73983
|
import { existsSync as existsSync19, readFileSync as readFileSync19 } from "node:fs";
|
|
73254
|
-
import
|
|
73984
|
+
import path42 from "node:path";
|
|
73255
73985
|
function operationInProgress(cwd) {
|
|
73256
73986
|
const markers = [
|
|
73257
73987
|
["rebase-merge", "rebase"],
|
|
@@ -73264,18 +73994,18 @@ function operationInProgress(cwd) {
|
|
|
73264
73994
|
if (!run.ok)
|
|
73265
73995
|
return null;
|
|
73266
73996
|
const found = run.stdout.split(`
|
|
73267
|
-
`).map((line) =>
|
|
73997
|
+
`).map((line) => path42.resolve(cwd, line.trim()));
|
|
73268
73998
|
const conflicted = () => git5(cwd, ["ls-files", "-u"]).stdout.trim() !== "";
|
|
73269
73999
|
for (const [index, [, operation]] of markers.entries()) {
|
|
73270
74000
|
const marker = found[index];
|
|
73271
74001
|
if (!marker || !existsSync19(marker))
|
|
73272
74002
|
continue;
|
|
73273
|
-
if (index === 1 && existsSync19(
|
|
74003
|
+
if (index === 1 && existsSync19(path42.join(marker, "applying")))
|
|
73274
74004
|
return { operation: "am", commitAllowed: false, branch: null };
|
|
73275
|
-
if (index === 0 && existsSync19(
|
|
74005
|
+
if (index === 0 && existsSync19(path42.join(marker, "amend")) && !conflicted()) {
|
|
73276
74006
|
let headName = "";
|
|
73277
74007
|
try {
|
|
73278
|
-
headName = readFileSync19(
|
|
74008
|
+
headName = readFileSync19(path42.join(marker, "head-name"), "utf8").trim();
|
|
73279
74009
|
} catch {}
|
|
73280
74010
|
const branch = headName.startsWith("refs/heads/") ? headName.slice("refs/heads/".length) : null;
|
|
73281
74011
|
return { operation, commitAllowed: branch !== null, branch };
|
|
@@ -73425,6 +74155,40 @@ function lintCommitMessage(cwd, message) {
|
|
|
73425
74155
|
correction: `use a ${detected} commit subject`
|
|
73426
74156
|
};
|
|
73427
74157
|
}
|
|
74158
|
+
function stripSessionTrailers(text, options) {
|
|
74159
|
+
const end = text.length - (/\s*$/u.exec(text)?.[0].length ?? 0);
|
|
74160
|
+
const lines = text.slice(0, end).split(`
|
|
74161
|
+
`);
|
|
74162
|
+
let start = lines.length;
|
|
74163
|
+
while (start > 0 && lines[start - 1]?.trim() !== "")
|
|
74164
|
+
start -= 1;
|
|
74165
|
+
if (start === 0 && options.subject)
|
|
74166
|
+
return text;
|
|
74167
|
+
const block = lines.slice(start);
|
|
74168
|
+
if (!block.length || !TRAILER_LINE.test(block[0] ?? ""))
|
|
74169
|
+
return text;
|
|
74170
|
+
if (!block.every((line) => TRAILER_LINE.test(line) || /^\s+\S/u.test(line)))
|
|
74171
|
+
return text;
|
|
74172
|
+
const kept = [];
|
|
74173
|
+
let dropping = false;
|
|
74174
|
+
for (const line of block) {
|
|
74175
|
+
if (TRAILER_LINE.test(line))
|
|
74176
|
+
dropping = SESSION_KEY.test(line);
|
|
74177
|
+
if (!dropping)
|
|
74178
|
+
kept.push(line);
|
|
74179
|
+
}
|
|
74180
|
+
if (kept.length === block.length)
|
|
74181
|
+
return text;
|
|
74182
|
+
let head = lines.slice(0, start);
|
|
74183
|
+
if (!kept.length) {
|
|
74184
|
+
let cut = head.length;
|
|
74185
|
+
while (cut > 0 && head[cut - 1]?.trim() === "")
|
|
74186
|
+
cut -= 1;
|
|
74187
|
+
head = head.slice(0, cut);
|
|
74188
|
+
}
|
|
74189
|
+
return [...head, ...kept].join(`
|
|
74190
|
+
`) + text.slice(end);
|
|
74191
|
+
}
|
|
73428
74192
|
function messageTrailers(cwd, message) {
|
|
73429
74193
|
const run = git5(cwd, [...TRAILER_CONFIG, "interpret-trailers", "--parse", "--no-divider"], {
|
|
73430
74194
|
input: `${message}
|
|
@@ -73484,6 +74248,12 @@ function gitCommit(cwd, input) {
|
|
|
73484
74248
|
const given = input.message?.trim() ?? "";
|
|
73485
74249
|
if (!given && !input.amend)
|
|
73486
74250
|
return failure3("invalid_input", "a commit message is required (-m <msg> or -F <file>)");
|
|
74251
|
+
const extra = (input.trailers ?? []).map((trailer) => trailer.trim());
|
|
74252
|
+
const malformed = extra.find((trailer) => !EXTRA_TRAILER.test(trailer));
|
|
74253
|
+
if (malformed !== undefined)
|
|
74254
|
+
return failure3("invalid_input", `--trailer ${JSON.stringify(malformed)} is not one "Key: value" line`, `workit git commit -m '<msg>' --trailer "Co-Authored-By: Name <email>"`);
|
|
74255
|
+
if (extra.some((trailer) => /^workit-session:/iu.test(trailer)))
|
|
74256
|
+
return failure3("invalid_input", "--trailer cannot set Workit-Session: workit writes it from the acting session");
|
|
73487
74257
|
const paths = input.paths ?? [];
|
|
73488
74258
|
if (input.all && paths.length)
|
|
73489
74259
|
return failure3("invalid_input", "pass --all or paths, not both");
|
|
@@ -73505,6 +74275,7 @@ function gitCommit(cwd, input) {
|
|
|
73505
74275
|
}
|
|
73506
74276
|
let message = given;
|
|
73507
74277
|
let carried = [];
|
|
74278
|
+
let previousTrailers = [];
|
|
73508
74279
|
if (given) {
|
|
73509
74280
|
const lint = lintCommitMessage(cwd, given);
|
|
73510
74281
|
if (!lint.ok)
|
|
@@ -73514,8 +74285,9 @@ function gitCommit(cwd, input) {
|
|
|
73514
74285
|
const kept = git5(cwd, ["log", "-1", "--format=%B", previous]);
|
|
73515
74286
|
if (!kept.ok)
|
|
73516
74287
|
return failure3("failed", `could not read the message of ${branch}: ${gitError(kept)}`);
|
|
74288
|
+
previousTrailers = messageTrailers(cwd, kept.stdout.trim());
|
|
73517
74289
|
if (given)
|
|
73518
|
-
carried =
|
|
74290
|
+
carried = previousTrailers;
|
|
73519
74291
|
else
|
|
73520
74292
|
message = kept.stdout.trim();
|
|
73521
74293
|
}
|
|
@@ -73529,12 +74301,31 @@ function gitCommit(cwd, input) {
|
|
|
73529
74301
|
args.push("--amend");
|
|
73530
74302
|
if (input.allowEmpty || emptyAmend)
|
|
73531
74303
|
args.push("--allow-empty");
|
|
73532
|
-
|
|
74304
|
+
const trailersOn = workspaceTrailers(cwd).enabled;
|
|
74305
|
+
const sessionTrailer = Boolean(session) && trailersOn;
|
|
74306
|
+
if (!trailersOn) {
|
|
74307
|
+
carried = carried.filter((trailer) => !SESSION_KEY.test(trailer));
|
|
74308
|
+
if (!given)
|
|
74309
|
+
message = stripSessionTrailers(message, { subject: true });
|
|
74310
|
+
}
|
|
74311
|
+
const finalMessage = withTrailers(cwd, message, [
|
|
74312
|
+
...carried,
|
|
74313
|
+
...extra,
|
|
74314
|
+
...sessionTrailer ? [`Workit-Session: ${session}`] : []
|
|
74315
|
+
]);
|
|
74316
|
+
const allowed = new Set([
|
|
74317
|
+
...trailersOn ? sessionValues(previousTrailers) : [],
|
|
74318
|
+
...sessionTrailer && session ? [session] : []
|
|
74319
|
+
]);
|
|
74320
|
+
const forged = sessionValues(messageTrailers(cwd, finalMessage)).find((value) => !allowed.has(value));
|
|
74321
|
+
if (forged !== undefined)
|
|
74322
|
+
return failure3("invalid_input", `forged_session_trailer: the message would carry "Workit-Session: ${forged}", which workit did not add; workit writes that trailer itself`, "drop the Workit-Session line (or the trailer.<alias>.key config that expands into it)");
|
|
74323
|
+
args.push("-m", finalMessage);
|
|
73533
74324
|
if (paths.length) {
|
|
73534
74325
|
if (paths.some((value) => value.startsWith("-") || !value.trim()))
|
|
73535
74326
|
return failure3("invalid_input", "paths must not be empty or start with -");
|
|
73536
74327
|
const tracked = new Set(nulList(git5(cwd, ["ls-files", "-z", "--", ...paths]).stdout));
|
|
73537
|
-
const addable = paths.filter((value) => existsSync19(
|
|
74328
|
+
const addable = paths.filter((value) => existsSync19(path42.resolve(cwd, value)) || [...tracked].some((file) => file === value || file.startsWith(`${value}/`)));
|
|
73538
74329
|
if (addable.length) {
|
|
73539
74330
|
const added = git5(cwd, ["add", "-A", "--", ...addable]);
|
|
73540
74331
|
if (!added.ok)
|
|
@@ -73560,7 +74351,7 @@ function gitCommit(cwd, input) {
|
|
|
73560
74351
|
}
|
|
73561
74352
|
const committed = git5(cwd, args, {
|
|
73562
74353
|
timeoutMs: COMMIT_TIMEOUT_MS,
|
|
73563
|
-
env: input.env
|
|
74354
|
+
env: { ...input.env ?? process.env, WORKIT_TRAILERS: trailersOn ? "on" : "off" }
|
|
73564
74355
|
});
|
|
73565
74356
|
if (!committed.ok)
|
|
73566
74357
|
return failure3("failed", `git commit failed (a hook or git refused it):
|
|
@@ -73604,6 +74395,7 @@ ${gitOutput(committed)}`);
|
|
|
73604
74395
|
files,
|
|
73605
74396
|
leftDirty,
|
|
73606
74397
|
session,
|
|
74398
|
+
sessionTrailer,
|
|
73607
74399
|
amended: previous,
|
|
73608
74400
|
notes,
|
|
73609
74401
|
recorded: row.ok ? { id: String(row.value.id) } : { error: row.error }
|
|
@@ -73967,18 +74759,22 @@ ${run.stdout ?? ""}`).split(/\r?\n/u).map((value) => value.trimEnd()).filter((va
|
|
|
73967
74759
|
...lines.slice(-half)
|
|
73968
74760
|
].join(`
|
|
73969
74761
|
`);
|
|
73970
|
-
}, validRefName = (cwd, name) => !name.startsWith("-") && git5(cwd, ["check-ref-format", "--branch", name]).ok, nulList = (text) => text.split("\x00").filter(Boolean), SESSION_SAFE, TRAILER_CONFIG, nothingToCommit = (what) => failure3("failed", `nothing_to_commit: ${what}`, "workit git commit --allow-empty -m '<msg>' # only to record an empty commit on purpose"), SHA, SHA_PREFIX, RELEASE_TAG;
|
|
74762
|
+
}, validRefName = (cwd, name) => !name.startsWith("-") && git5(cwd, ["check-ref-format", "--branch", name]).ok, nulList = (text) => text.split("\x00").filter(Boolean), SESSION_SAFE, EXTRA_TRAILER, SESSION_KEY, TRAILER_LINE, sessionValues = (trailers) => trailers.filter((trailer) => SESSION_KEY.test(trailer)).map((trailer) => trailer.slice(trailer.indexOf(":") + 1).trim()), TRAILER_CONFIG, nothingToCommit = (what) => failure3("failed", `nothing_to_commit: ${what}`, "workit git commit --allow-empty -m '<msg>' # only to record an empty commit on purpose"), SHA, SHA_PREFIX, RELEASE_TAG;
|
|
73971
74763
|
var init_ops = __esm(() => {
|
|
73972
74764
|
init_branch();
|
|
73973
74765
|
init_commit_flavors();
|
|
73974
74766
|
init_pr_create();
|
|
73975
74767
|
init_release_tracks();
|
|
73976
74768
|
init_vcs_config();
|
|
74769
|
+
init_workspaces();
|
|
73977
74770
|
init_redact();
|
|
73978
74771
|
init_ledger();
|
|
73979
74772
|
init_rev();
|
|
73980
74773
|
COMMIT_TIMEOUT_MS = 10 * 60000;
|
|
73981
74774
|
SESSION_SAFE = /^[\w.:@/+-]{1,128}$/u;
|
|
74775
|
+
EXTRA_TRAILER = /^[A-Za-z0-9][A-Za-z0-9-]*: *\S[^\n\r]*$/u;
|
|
74776
|
+
SESSION_KEY = /^workit-session\s*:/iu;
|
|
74777
|
+
TRAILER_LINE = /^[A-Za-z0-9][A-Za-z0-9-]*\s*:/u;
|
|
73982
74778
|
TRAILER_CONFIG = ["-c", "trailer.separators=:", "-c", "trailer.where=end"];
|
|
73983
74779
|
SHA = /^[0-9a-f]{40,64}$/u;
|
|
73984
74780
|
SHA_PREFIX = /^[0-9a-f]{7,39}$/u;
|
|
@@ -73986,11 +74782,11 @@ var init_ops = __esm(() => {
|
|
|
73986
74782
|
});
|
|
73987
74783
|
|
|
73988
74784
|
// packages/workit-core/src/forge/reruns.ts
|
|
73989
|
-
import
|
|
73990
|
-
import
|
|
74785
|
+
import fs21 from "node:fs";
|
|
74786
|
+
import path43 from "node:path";
|
|
73991
74787
|
function rerunLogPath(cwd) {
|
|
73992
74788
|
const common = gitCommonDir(cwd);
|
|
73993
|
-
return common ?
|
|
74789
|
+
return common ? path43.join(common, "workit", "ci-reruns.jsonl") : null;
|
|
73994
74790
|
}
|
|
73995
74791
|
function rerunCounts(cwd, key) {
|
|
73996
74792
|
const counts = new Map;
|
|
@@ -73999,7 +74795,7 @@ function rerunCounts(cwd, key) {
|
|
|
73999
74795
|
return counts;
|
|
74000
74796
|
let text;
|
|
74001
74797
|
try {
|
|
74002
|
-
text =
|
|
74798
|
+
text = fs21.readFileSync(file, "utf8");
|
|
74003
74799
|
} catch {
|
|
74004
74800
|
return counts;
|
|
74005
74801
|
}
|
|
@@ -74025,8 +74821,8 @@ function recordReruns(cwd, rows) {
|
|
|
74025
74821
|
if (!file)
|
|
74026
74822
|
return false;
|
|
74027
74823
|
try {
|
|
74028
|
-
|
|
74029
|
-
|
|
74824
|
+
fs21.mkdirSync(path43.dirname(file), { recursive: true });
|
|
74825
|
+
fs21.appendFileSync(file, rows.map((row) => `${JSON.stringify(row)}
|
|
74030
74826
|
`).join(""));
|
|
74031
74827
|
return true;
|
|
74032
74828
|
} catch {
|
|
@@ -74038,18 +74834,18 @@ function withRerunLock(cwd, fn) {
|
|
|
74038
74834
|
if (!file)
|
|
74039
74835
|
return fn();
|
|
74040
74836
|
const lock = `${file.slice(0, -".jsonl".length)}.lock`;
|
|
74041
|
-
|
|
74837
|
+
fs21.mkdirSync(path43.dirname(lock), { recursive: true });
|
|
74042
74838
|
const until = Date.now() + LOCK_WAIT_MS2;
|
|
74043
74839
|
for (;; ) {
|
|
74044
74840
|
try {
|
|
74045
|
-
|
|
74841
|
+
fs21.closeSync(fs21.openSync(lock, "wx"));
|
|
74046
74842
|
break;
|
|
74047
74843
|
} catch (error) {
|
|
74048
74844
|
if (error.code !== "EEXIST")
|
|
74049
74845
|
throw error;
|
|
74050
74846
|
try {
|
|
74051
|
-
if (Date.now() -
|
|
74052
|
-
|
|
74847
|
+
if (Date.now() - fs21.statSync(lock).mtimeMs > LOCK_STALE_MS2) {
|
|
74848
|
+
fs21.rmSync(lock, { force: true });
|
|
74053
74849
|
continue;
|
|
74054
74850
|
}
|
|
74055
74851
|
} catch {
|
|
@@ -74063,7 +74859,7 @@ function withRerunLock(cwd, fn) {
|
|
|
74063
74859
|
try {
|
|
74064
74860
|
return fn();
|
|
74065
74861
|
} finally {
|
|
74066
|
-
|
|
74862
|
+
fs21.rmSync(lock, { force: true });
|
|
74067
74863
|
}
|
|
74068
74864
|
}
|
|
74069
74865
|
var isRow = (value) => {
|
|
@@ -74647,6 +75443,56 @@ function nextHint(next, number) {
|
|
|
74647
75443
|
return null;
|
|
74648
75444
|
}
|
|
74649
75445
|
}
|
|
75446
|
+
function squashMessageFor(cwd, resolved, input, doc) {
|
|
75447
|
+
if (input.method !== "squash")
|
|
75448
|
+
return null;
|
|
75449
|
+
if (input.subject === undefined && input.body === undefined && workspaceTrailers(cwd).enabled)
|
|
75450
|
+
return null;
|
|
75451
|
+
let subject = input.subject?.trim() ?? "";
|
|
75452
|
+
if (!subject) {
|
|
75453
|
+
const meta = resolved.forge.prMeta(doc.number);
|
|
75454
|
+
if (!meta.ok)
|
|
75455
|
+
return meta;
|
|
75456
|
+
subject = resolved.forge.kind === "github" ? `${meta.data.title} (#${doc.number})` : meta.data.title;
|
|
75457
|
+
}
|
|
75458
|
+
const body = input.body === undefined ? squashCommitList(cwd, resolved, doc.base, doc.head.sha) : stripSessionTrailers(input.body, { subject: false });
|
|
75459
|
+
return success2({ subject, body });
|
|
75460
|
+
}
|
|
75461
|
+
function squashCommitList(cwd, resolved, base, head) {
|
|
75462
|
+
const from = resolveRef(cwd, `refs/remotes/${resolved.baseRemote}/${base}`) ?? resolveRef(cwd, `refs/heads/${base}`);
|
|
75463
|
+
if (!from)
|
|
75464
|
+
return "";
|
|
75465
|
+
const log = spawnSync19("git", ["log", "--reverse", "--no-merges", "--format=%B%x1e", `${from}..${head}`], {
|
|
75466
|
+
cwd,
|
|
75467
|
+
encoding: "utf8",
|
|
75468
|
+
timeout: GIT_TIMEOUTS.local,
|
|
75469
|
+
killSignal: "SIGKILL",
|
|
75470
|
+
maxBuffer: 64 * 1024 * 1024
|
|
75471
|
+
});
|
|
75472
|
+
if (log.status !== 0)
|
|
75473
|
+
return "";
|
|
75474
|
+
const entries = log.stdout.split("\x1E").map((message) => stripSessionTrailers(message.replace(/^\n+/u, ""), { subject: true })).filter((message) => message.trim()).map((message) => {
|
|
75475
|
+
const [subject = "", ...rest] = message.trimEnd().split(`
|
|
75476
|
+
`);
|
|
75477
|
+
const body = rest.join(`
|
|
75478
|
+
`).replace(/^\n+/u, "");
|
|
75479
|
+
return body ? `* ${subject}
|
|
75480
|
+
|
|
75481
|
+
${body}` : `* ${subject}`;
|
|
75482
|
+
});
|
|
75483
|
+
const listed = [];
|
|
75484
|
+
let size = 0;
|
|
75485
|
+
for (const entry of entries) {
|
|
75486
|
+
if (listed.length >= SQUASH_LIST_MAX_COMMITS || size + entry.length > SQUASH_LIST_MAX_CHARS)
|
|
75487
|
+
break;
|
|
75488
|
+
listed.push(entry);
|
|
75489
|
+
size += entry.length + 2;
|
|
75490
|
+
}
|
|
75491
|
+
const more = entries.length - listed.length;
|
|
75492
|
+
return [...listed, ...more ? [`… and ${more} more commit${more === 1 ? "" : "s"}`] : []].join(`
|
|
75493
|
+
|
|
75494
|
+
`);
|
|
75495
|
+
}
|
|
74650
75496
|
async function mergePullRequest(cwd, resolved, input, sleep) {
|
|
74651
75497
|
const grant = requireGrant(cwd, "merge");
|
|
74652
75498
|
if (!grant.allowed)
|
|
@@ -74723,7 +75569,14 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
|
|
|
74723
75569
|
return refuse(`head_mismatch: the verdict check reads ${branch} at ${check.head?.slice(0, 12) ?? "(missing)"} but ${label} is at ${head.slice(0, 12)}`, `git fetch && git switch ${branch} && git merge --ff-only @{u} # then re-verify that head`, { reason: "head_mismatch", verdict: summary });
|
|
74724
75570
|
else if (!covered)
|
|
74725
75571
|
return refuse(`NEEDS_VERDICT: ${label} has no accepted independent verdict for ${head.slice(0, 12)} (${check.accepted.reasons.join(", ")})`, `an independent session verifies the head and runs: workit ledger verdict verified --how "<what was exercised>" --branch ${branch}${grant.allowUnverified ? `; or, only if the user asks to merge unverified: workit pr merge --unverified --reason "<why>"` : ""}`, { reason: "needs_verdict", verdict: summary });
|
|
74726
|
-
const
|
|
75572
|
+
const squashMessage = squashMessageFor(cwd, resolved, input, doc);
|
|
75573
|
+
if (squashMessage && !squashMessage.ok)
|
|
75574
|
+
return squashMessage;
|
|
75575
|
+
const merged = resolved.forge.merge(doc.number, {
|
|
75576
|
+
sha: head,
|
|
75577
|
+
method: input.method,
|
|
75578
|
+
...squashMessage ? { squashMessage: squashMessage.data } : {}
|
|
75579
|
+
});
|
|
74727
75580
|
if (!merged.ok)
|
|
74728
75581
|
return merged;
|
|
74729
75582
|
let state = "open";
|
|
@@ -74813,11 +75666,12 @@ var VERIFY_DELAYS_MS, noun = (resolved) => resolved.forge.kind === "github" ? "p
|
|
|
74813
75666
|
error,
|
|
74814
75667
|
unblock,
|
|
74815
75668
|
refusal
|
|
74816
|
-
});
|
|
75669
|
+
}), SQUASH_LIST_MAX_COMMITS = 100, SQUASH_LIST_MAX_CHARS = 60000;
|
|
74817
75670
|
var init_pr_ops = __esm(() => {
|
|
74818
75671
|
init_autonomy();
|
|
74819
75672
|
init_branch();
|
|
74820
75673
|
init_vcs_config();
|
|
75674
|
+
init_workspaces();
|
|
74821
75675
|
init_release_tracks();
|
|
74822
75676
|
init_ops();
|
|
74823
75677
|
init_rev();
|
|
@@ -74836,8 +75690,8 @@ var init_pr_ops = __esm(() => {
|
|
|
74836
75690
|
// packages/workit-core/src/stack.ts
|
|
74837
75691
|
import { spawnSync as spawnSync20 } from "node:child_process";
|
|
74838
75692
|
import { createHash as createHash17, randomBytes as randomBytes3 } from "node:crypto";
|
|
74839
|
-
import
|
|
74840
|
-
import
|
|
75693
|
+
import fs22 from "node:fs";
|
|
75694
|
+
import path44 from "node:path";
|
|
74841
75695
|
function worktreeOf(cwd, branch) {
|
|
74842
75696
|
const list = gitRun3(cwd, ["worktree", "list", "--porcelain"]);
|
|
74843
75697
|
if (!list.ok)
|
|
@@ -74860,7 +75714,7 @@ function rebasingBranch2(dir) {
|
|
|
74860
75714
|
if (!found.ok)
|
|
74861
75715
|
continue;
|
|
74862
75716
|
try {
|
|
74863
|
-
return
|
|
75717
|
+
return fs22.readFileSync(found.stdout.trim(), "utf8").trim();
|
|
74864
75718
|
} catch {}
|
|
74865
75719
|
}
|
|
74866
75720
|
return null;
|
|
@@ -74868,7 +75722,7 @@ function rebasingBranch2(dir) {
|
|
|
74868
75722
|
function rebaseInProgress(dir) {
|
|
74869
75723
|
for (const name of ["rebase-merge", "rebase-apply"]) {
|
|
74870
75724
|
const found = gitRun3(dir, ["rev-parse", "--path-format=absolute", "--git-path", name]);
|
|
74871
|
-
if (found.ok &&
|
|
75725
|
+
if (found.ok && fs22.existsSync(found.stdout.trim()))
|
|
74872
75726
|
return true;
|
|
74873
75727
|
}
|
|
74874
75728
|
return false;
|
|
@@ -74880,8 +75734,8 @@ function gitRestack(cwd, input) {
|
|
|
74880
75734
|
const scratch = scratchDir(cwd);
|
|
74881
75735
|
if (!scratch)
|
|
74882
75736
|
return { ok: false, conflict: false, worktree: cwd, error: "no store root" };
|
|
74883
|
-
|
|
74884
|
-
dir =
|
|
75737
|
+
fs22.mkdirSync(scratch, { recursive: true });
|
|
75738
|
+
dir = path44.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes3(4).toString("hex")}`);
|
|
74885
75739
|
const added = gitRun3(cwd, ["worktree", "add", "-q", dir, input.branch], GIT_TIMEOUTS.worktree);
|
|
74886
75740
|
if (!added.ok)
|
|
74887
75741
|
return { ok: false, conflict: false, worktree: cwd, error: firstLine(added) };
|
|
@@ -74919,10 +75773,10 @@ function gitRestack(cwd, input) {
|
|
|
74919
75773
|
}
|
|
74920
75774
|
function pruneScratch(cwd) {
|
|
74921
75775
|
const scratch = scratchDir(cwd);
|
|
74922
|
-
if (!scratch || !
|
|
75776
|
+
if (!scratch || !fs22.existsSync(scratch))
|
|
74923
75777
|
return;
|
|
74924
|
-
for (const name of
|
|
74925
|
-
const dir =
|
|
75778
|
+
for (const name of fs22.readdirSync(scratch)) {
|
|
75779
|
+
const dir = path44.join(scratch, name);
|
|
74926
75780
|
if (!rebaseInProgress(dir) && !trackedDirt(dir))
|
|
74927
75781
|
gitRun3(cwd, ["worktree", "remove", "--force", dir]);
|
|
74928
75782
|
}
|
|
@@ -74932,10 +75786,10 @@ function stacksDir(cwd) {
|
|
|
74932
75786
|
const root = storeRoot(cwd);
|
|
74933
75787
|
if (!root.ok)
|
|
74934
75788
|
return stackFail(root.code, root.error, root.unblock);
|
|
74935
|
-
return { ok: true, data:
|
|
75789
|
+
return { ok: true, data: path44.join(root.value.root, "stacks") };
|
|
74936
75790
|
}
|
|
74937
75791
|
function parseStack(raw) {
|
|
74938
|
-
if (!
|
|
75792
|
+
if (!isRecord8(raw) || !Array.isArray(raw.branches))
|
|
74939
75793
|
return null;
|
|
74940
75794
|
const name = str3(raw.name);
|
|
74941
75795
|
const trunk = str3(raw.trunk);
|
|
@@ -74943,12 +75797,12 @@ function parseStack(raw) {
|
|
|
74943
75797
|
return null;
|
|
74944
75798
|
const branches = [];
|
|
74945
75799
|
for (const item of raw.branches) {
|
|
74946
|
-
if (!
|
|
75800
|
+
if (!isRecord8(item))
|
|
74947
75801
|
return null;
|
|
74948
75802
|
const branch = str3(item.branch);
|
|
74949
75803
|
if (!branch)
|
|
74950
75804
|
return null;
|
|
74951
|
-
const merged =
|
|
75805
|
+
const merged = isRecord8(item.merged) ? {
|
|
74952
75806
|
pr: typeof item.merged.pr === "number" ? item.merged.pr : null,
|
|
74953
75807
|
mergeSha: str3(item.merged.mergeSha),
|
|
74954
75808
|
at: str3(item.merged.at) ?? ""
|
|
@@ -74961,7 +75815,7 @@ function parseStack(raw) {
|
|
|
74961
75815
|
lastParentHead: str3(item.lastParentHead),
|
|
74962
75816
|
patchId: str3(item.patchId),
|
|
74963
75817
|
merged,
|
|
74964
|
-
...
|
|
75818
|
+
...isRecord8(item.pendingRestack) && str3(item.pendingRestack.from) && str3(item.pendingRestack.tip) && str3(item.pendingRestack.onto) ? {
|
|
74965
75819
|
pendingRestack: {
|
|
74966
75820
|
from: str3(item.pendingRestack.from),
|
|
74967
75821
|
tip: str3(item.pendingRestack.tip),
|
|
@@ -74984,10 +75838,10 @@ function readStack(cwd, name) {
|
|
|
74984
75838
|
const dir = stacksDir(cwd);
|
|
74985
75839
|
if (!dir.ok)
|
|
74986
75840
|
return dir;
|
|
74987
|
-
const file =
|
|
75841
|
+
const file = path44.join(dir.data, fileFor(name));
|
|
74988
75842
|
let text;
|
|
74989
75843
|
try {
|
|
74990
|
-
text =
|
|
75844
|
+
text = fs22.readFileSync(file, "utf8");
|
|
74991
75845
|
} catch (error) {
|
|
74992
75846
|
if (error.code === "ENOENT")
|
|
74993
75847
|
return { ok: true, data: null };
|
|
@@ -75005,17 +75859,17 @@ function writeStack(cwd, stack, now = new Date) {
|
|
|
75005
75859
|
const dir = stacksDir(cwd);
|
|
75006
75860
|
if (!dir.ok)
|
|
75007
75861
|
return dir;
|
|
75008
|
-
const file =
|
|
75862
|
+
const file = path44.join(dir.data, fileFor(stack.name));
|
|
75009
75863
|
const temp = `${file}.${process.pid}.tmp`;
|
|
75010
75864
|
try {
|
|
75011
|
-
|
|
75865
|
+
fs22.mkdirSync(dir.data, { recursive: true });
|
|
75012
75866
|
stack.updatedAt = now.toISOString();
|
|
75013
|
-
|
|
75867
|
+
fs22.writeFileSync(temp, `${JSON.stringify(stack, null, 2)}
|
|
75014
75868
|
`);
|
|
75015
|
-
|
|
75869
|
+
fs22.renameSync(temp, file);
|
|
75016
75870
|
return { ok: true, data: undefined };
|
|
75017
75871
|
} catch (error) {
|
|
75018
|
-
|
|
75872
|
+
fs22.rmSync(temp, { force: true });
|
|
75019
75873
|
return stackFail("unavailable", `cannot write ${file}: ${error.message}`);
|
|
75020
75874
|
}
|
|
75021
75875
|
}
|
|
@@ -75025,7 +75879,7 @@ function listStacks(cwd) {
|
|
|
75025
75879
|
return dir;
|
|
75026
75880
|
let names = [];
|
|
75027
75881
|
try {
|
|
75028
|
-
names =
|
|
75882
|
+
names = fs22.readdirSync(dir.data).filter((name) => name.endsWith(".json"));
|
|
75029
75883
|
} catch {
|
|
75030
75884
|
return { ok: true, data: [] };
|
|
75031
75885
|
}
|
|
@@ -75033,7 +75887,7 @@ function listStacks(cwd) {
|
|
|
75033
75887
|
for (const name of names) {
|
|
75034
75888
|
let parsed = null;
|
|
75035
75889
|
try {
|
|
75036
|
-
parsed = parseStack(JSON.parse(
|
|
75890
|
+
parsed = parseStack(JSON.parse(fs22.readFileSync(path44.join(dir.data, name), "utf8")));
|
|
75037
75891
|
} catch {
|
|
75038
75892
|
parsed = null;
|
|
75039
75893
|
}
|
|
@@ -75080,7 +75934,7 @@ function stackLockStale(raw, age) {
|
|
|
75080
75934
|
}
|
|
75081
75935
|
function takeLock(lock) {
|
|
75082
75936
|
try {
|
|
75083
|
-
|
|
75937
|
+
fs22.mkdirSync(lock);
|
|
75084
75938
|
} catch (error) {
|
|
75085
75939
|
if (error.code === "EEXIST")
|
|
75086
75940
|
return null;
|
|
@@ -75092,24 +75946,24 @@ function takeLock(lock) {
|
|
|
75092
75946
|
host: localLockHost(),
|
|
75093
75947
|
nonce: randomBytes3(8).toString("hex")
|
|
75094
75948
|
};
|
|
75095
|
-
|
|
75949
|
+
fs22.writeFileSync(path44.join(lock, OWNER), JSON.stringify(owner));
|
|
75096
75950
|
return owner;
|
|
75097
75951
|
}
|
|
75098
75952
|
function reapLock(lock, observed) {
|
|
75099
75953
|
const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
|
|
75100
75954
|
try {
|
|
75101
|
-
|
|
75955
|
+
fs22.renameSync(lock, tomb);
|
|
75102
75956
|
} catch {
|
|
75103
75957
|
return false;
|
|
75104
75958
|
}
|
|
75105
75959
|
if (readOwner(tomb).raw === observed) {
|
|
75106
|
-
|
|
75960
|
+
fs22.rmSync(tomb, { recursive: true, force: true });
|
|
75107
75961
|
return true;
|
|
75108
75962
|
}
|
|
75109
75963
|
try {
|
|
75110
|
-
|
|
75964
|
+
fs22.renameSync(tomb, lock);
|
|
75111
75965
|
} catch {
|
|
75112
|
-
|
|
75966
|
+
fs22.rmSync(tomb, { recursive: true, force: true });
|
|
75113
75967
|
}
|
|
75114
75968
|
return false;
|
|
75115
75969
|
}
|
|
@@ -75119,8 +75973,8 @@ function releaseLock(lock, owner) {
|
|
|
75119
75973
|
return;
|
|
75120
75974
|
const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
|
|
75121
75975
|
try {
|
|
75122
|
-
|
|
75123
|
-
|
|
75976
|
+
fs22.renameSync(lock, tomb);
|
|
75977
|
+
fs22.rmSync(tomb, { recursive: true, force: true });
|
|
75124
75978
|
} catch {}
|
|
75125
75979
|
}
|
|
75126
75980
|
async function withStackLock(cwd, name, fn, waitMs = LOCK_WAIT_MS3) {
|
|
@@ -75129,9 +75983,9 @@ async function withStackLock(cwd, name, fn, waitMs = LOCK_WAIT_MS3) {
|
|
|
75129
75983
|
return dir;
|
|
75130
75984
|
if (!validName(name))
|
|
75131
75985
|
return stackFail("invalid_input", "a stack name must be 1-200 printable characters");
|
|
75132
|
-
const lock =
|
|
75986
|
+
const lock = path44.join(dir.data, `${fileFor(name)}.lock`);
|
|
75133
75987
|
try {
|
|
75134
|
-
|
|
75988
|
+
fs22.mkdirSync(dir.data, { recursive: true });
|
|
75135
75989
|
} catch (error) {
|
|
75136
75990
|
return stackFail("unavailable", `cannot create ${dir.data}: ${error.message}`);
|
|
75137
75991
|
}
|
|
@@ -75159,7 +76013,7 @@ async function withStackLock(cwd, name, fn, waitMs = LOCK_WAIT_MS3) {
|
|
|
75159
76013
|
const touch = () => {
|
|
75160
76014
|
try {
|
|
75161
76015
|
const now = new Date;
|
|
75162
|
-
|
|
76016
|
+
fs22.utimesSync(path44.join(lock, OWNER), now, now);
|
|
75163
76017
|
} catch {}
|
|
75164
76018
|
};
|
|
75165
76019
|
const beat = setInterval(touch, HEARTBEAT_MS);
|
|
@@ -75192,7 +76046,7 @@ function deriveChain(cwd, trunk, base) {
|
|
|
75192
76046
|
const [name, sha] = line.trim().split(" ");
|
|
75193
76047
|
if (!name || !sha || name === branch || name === trunk)
|
|
75194
76048
|
continue;
|
|
75195
|
-
if (sha === head ||
|
|
76049
|
+
if (sha === head || isAncestor2(cwd, sha, base) || !isAncestor2(cwd, sha, head))
|
|
75196
76050
|
continue;
|
|
75197
76051
|
const count = gitRun3(cwd, ["rev-list", "--count", `${base}..${sha}`]);
|
|
75198
76052
|
below.push({ branch: name, depth: Number(count.stdout.trim()) || 0 });
|
|
@@ -75238,17 +76092,17 @@ function planStack(cwd, resolved, input, existing) {
|
|
|
75238
76092
|
const status = resolved.forge.prStatus(before.pr);
|
|
75239
76093
|
still = status.ok && status.data.state === "merged";
|
|
75240
76094
|
} else
|
|
75241
|
-
still = hasOwnCommits(cwd, tip, before.lastParentHead) &&
|
|
76095
|
+
still = hasOwnCommits(cwd, tip, before.lastParentHead) && isAncestor2(cwd, tip, trunkSha);
|
|
75242
76096
|
if (still) {
|
|
75243
76097
|
entries.push(before);
|
|
75244
76098
|
continue;
|
|
75245
76099
|
}
|
|
75246
76100
|
notes.push(`${branch} was recorded as merged but is not; it is back in the stack`);
|
|
75247
76101
|
}
|
|
75248
|
-
const onParent =
|
|
76102
|
+
const onParent = isAncestor2(cwd, parentSha, tip);
|
|
75249
76103
|
let builtOn = parentSha;
|
|
75250
76104
|
if (!onParent) {
|
|
75251
|
-
if (before?.lastParentHead &&
|
|
76105
|
+
if (before?.lastParentHead && isAncestor2(cwd, before.lastParentHead, tip))
|
|
75252
76106
|
builtOn = before.lastParentHead;
|
|
75253
76107
|
else {
|
|
75254
76108
|
const old = oldParentVersion(cwd, parent, parentSha, tip) ?? oldParentLookalike(cwd, parentSha, tip);
|
|
@@ -75323,7 +76177,7 @@ function planStack(cwd, resolved, input, existing) {
|
|
|
75323
76177
|
function hasOwnCommits(cwd, tip, base) {
|
|
75324
76178
|
if (!base)
|
|
75325
76179
|
return false;
|
|
75326
|
-
return tip !== base && !
|
|
76180
|
+
return tip !== base && !isAncestor2(cwd, tip, base);
|
|
75327
76181
|
}
|
|
75328
76182
|
function oldParentVersion(cwd, parent, parentSha, child) {
|
|
75329
76183
|
if (!safeRef2(parent))
|
|
@@ -75335,7 +76189,7 @@ function oldParentVersion(cwd, parent, parentSha, child) {
|
|
|
75335
76189
|
`).map((line) => line.trim())) {
|
|
75336
76190
|
if (!sha || sha === parentSha)
|
|
75337
76191
|
continue;
|
|
75338
|
-
if (
|
|
76192
|
+
if (isAncestor2(cwd, sha, child) && !isAncestor2(cwd, sha, parentSha))
|
|
75339
76193
|
return sha;
|
|
75340
76194
|
}
|
|
75341
76195
|
return null;
|
|
@@ -75469,7 +76323,7 @@ function stackStatus(ctx, stack) {
|
|
|
75469
76323
|
pr: entry.pr,
|
|
75470
76324
|
merged,
|
|
75471
76325
|
head,
|
|
75472
|
-
onParent: merged || !head || !parentSha ? null :
|
|
76326
|
+
onParent: merged || !head || !parentSha ? null : isAncestor2(ctx.cwd, parentSha, head),
|
|
75473
76327
|
prState: doc?.state ?? null,
|
|
75474
76328
|
prBase: doc?.base ?? null,
|
|
75475
76329
|
baseMatches: doc && !merged ? doc.base === parentName : null,
|
|
@@ -75532,7 +76386,7 @@ function detectMerged(ctx, cwd, stack, trunkSha, state) {
|
|
|
75532
76386
|
merged = read.data.state === "merged";
|
|
75533
76387
|
} else {
|
|
75534
76388
|
const tip = branchTip(cwd, entry.branch);
|
|
75535
|
-
merged = tip !== null && hasOwnCommits(cwd, tip, entry.lastParentHead) &&
|
|
76389
|
+
merged = tip !== null && hasOwnCommits(cwd, tip, entry.lastParentHead) && isAncestor2(cwd, tip, trunkSha);
|
|
75536
76390
|
}
|
|
75537
76391
|
if (merged) {
|
|
75538
76392
|
entry.merged = { pr: entry.pr, mergeSha: null, at: new Date().toISOString() };
|
|
@@ -75553,7 +76407,7 @@ function pushBranch(cwd, actor, branch, priorTip) {
|
|
|
75553
76407
|
const grant = requireGrant(cwd, "push", { forge: !plan.data.local });
|
|
75554
76408
|
if (!grant.allowed)
|
|
75555
76409
|
return stackFail("blocked", grant.error, grant.unblock, { reason: grant.reason });
|
|
75556
|
-
const force = remote.sha !== null && !
|
|
76410
|
+
const force = remote.sha !== null && !isAncestor2(cwd, remote.sha, plan.data.sha);
|
|
75557
76411
|
const expect = !force ? null : remote.sha === priorTip ? priorTip : recordedRemoteTip(cwd, plan.data) ?? null;
|
|
75558
76412
|
const pushed = executePush(cwd, plan.data, { forceWithLease: force, expect, actor });
|
|
75559
76413
|
if (!pushed.ok)
|
|
@@ -75630,9 +76484,9 @@ function syncStack(cwd, ctx, stack, options, actor) {
|
|
|
75630
76484
|
carried: null
|
|
75631
76485
|
};
|
|
75632
76486
|
newTip = null;
|
|
75633
|
-
} else if (!
|
|
76487
|
+
} else if (!isAncestor2(cwd, parentTip, tip)) {
|
|
75634
76488
|
const pending = entry.pendingRestack ?? null;
|
|
75635
|
-
const from = pending &&
|
|
76489
|
+
const from = pending && isAncestor2(cwd, pending.onto, tip) ? pending.onto : entry.lastParentHead && isAncestor2(cwd, entry.lastParentHead, tip) ? entry.lastParentHead : mergeBase(cwd, entry.lastParentHead ?? parentTip, tip) ?? parentTip;
|
|
75636
76490
|
if (options.dryRun) {
|
|
75637
76491
|
step.restack = { from, onto: parentTip, head: null, carried: null };
|
|
75638
76492
|
newTip = null;
|
|
@@ -75680,7 +76534,7 @@ function syncStack(cwd, ctx, stack, options, actor) {
|
|
|
75680
76534
|
}
|
|
75681
76535
|
}
|
|
75682
76536
|
if (!options.dryRun && newTip) {
|
|
75683
|
-
const check = entry.pendingRestack && parentTip &&
|
|
76537
|
+
const check = entry.pendingRestack && parentTip && isAncestor2(cwd, parentTip, newTip) ? entry.pendingRestack : null;
|
|
75684
76538
|
let forced = false;
|
|
75685
76539
|
if (check && parentTip) {
|
|
75686
76540
|
const before = {
|
|
@@ -75994,12 +76848,12 @@ var STACK_VERSION = 1, stackFail = (code, error, unblock, data) => ({
|
|
|
75994
76848
|
stderr: run.error ? String(run.error.message) : run.stderr ?? "",
|
|
75995
76849
|
timedOut: failure?.code === "ETIMEDOUT"
|
|
75996
76850
|
};
|
|
75997
|
-
}, safeRef2 = (value) => value.length > 0 && !value.startsWith("-"),
|
|
76851
|
+
}, safeRef2 = (value) => value.length > 0 && !value.startsWith("-"), isAncestor2 = (cwd, ancestor, descendant) => safeRef2(ancestor) && safeRef2(descendant) && gitRun3(cwd, ["merge-base", "--is-ancestor", ancestor, descendant]).ok, branchTip = (cwd, branch) => resolveRef(cwd, `refs/heads/${branch}`), trackedDirt = (dir) => {
|
|
75998
76852
|
const status = gitRun3(dir, ["status", "--porcelain", "--untracked-files=no"]);
|
|
75999
76853
|
return !status.ok || status.stdout.trim() !== "";
|
|
76000
76854
|
}, scratchDir = (cwd) => {
|
|
76001
76855
|
const dir = stacksDir(cwd);
|
|
76002
|
-
return dir.ok ?
|
|
76856
|
+
return dir.ok ? path44.join(dir.data, "worktrees") : null;
|
|
76003
76857
|
}, firstLine = (run) => (`${run.stderr}
|
|
76004
76858
|
${run.stdout}`.split(`
|
|
76005
76859
|
`).find((line) => line.trim()) ?? "git failed").trim().slice(0, 300), stackFileName = (name) => `${name.replace(/[^A-Za-z0-9._-]+/gu, "_").replace(/^[._]+/u, "").slice(0, 48).toLowerCase()}-${createHash17("sha256").update(name).digest("hex").slice(0, 12)}.json`, fileFor, validName = (name) => {
|
|
@@ -76011,17 +76865,17 @@ ${run.stdout}`.split(`
|
|
|
76011
76865
|
return false;
|
|
76012
76866
|
}
|
|
76013
76867
|
return true;
|
|
76014
|
-
},
|
|
76868
|
+
}, isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), str3 = (value) => typeof value === "string" && value ? value : null, LOCK_WAIT_MS3 = 2000, HEARTBEAT_MS = 20000, HEARTBEAT_STALE_MS, OWNERLESS_STALE_MS = 30000, OWNER = "owner", readOwner = (lock) => {
|
|
76015
76869
|
let raw = null;
|
|
76016
76870
|
try {
|
|
76017
|
-
raw =
|
|
76871
|
+
raw = fs22.readFileSync(path44.join(lock, OWNER), "utf8");
|
|
76018
76872
|
} catch {
|
|
76019
76873
|
raw = null;
|
|
76020
76874
|
}
|
|
76021
76875
|
let age = null;
|
|
76022
|
-
for (const target of [
|
|
76876
|
+
for (const target of [path44.join(lock, OWNER), lock])
|
|
76023
76877
|
try {
|
|
76024
|
-
age = Date.now() -
|
|
76878
|
+
age = Date.now() - fs22.statSync(target).mtimeMs;
|
|
76025
76879
|
break;
|
|
76026
76880
|
} catch {}
|
|
76027
76881
|
return { raw, age };
|
|
@@ -76238,16 +77092,16 @@ var init_pr_edit = __esm(() => {
|
|
|
76238
77092
|
|
|
76239
77093
|
// packages/workit-core/src/forge/exec.ts
|
|
76240
77094
|
import { spawnSync as spawnSync22 } from "node:child_process";
|
|
76241
|
-
import
|
|
76242
|
-
import
|
|
77095
|
+
import fs23 from "node:fs";
|
|
77096
|
+
import path45 from "node:path";
|
|
76243
77097
|
function findExecutable(bin, env) {
|
|
76244
|
-
const dirs = (env.PATH ?? env.Path ?? "").split(
|
|
77098
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(path45.delimiter).filter(Boolean);
|
|
76245
77099
|
const exts = process.platform === "win32" ? [".exe"] : [""];
|
|
76246
77100
|
for (const dir of dirs)
|
|
76247
77101
|
for (const ext of exts) {
|
|
76248
|
-
const candidate =
|
|
77102
|
+
const candidate = path45.join(dir, `${bin}${ext}`);
|
|
76249
77103
|
try {
|
|
76250
|
-
const stat =
|
|
77104
|
+
const stat = fs23.statSync(candidate);
|
|
76251
77105
|
if (stat.isFile())
|
|
76252
77106
|
return candidate;
|
|
76253
77107
|
} catch {}
|
|
@@ -76279,7 +77133,7 @@ function systemRunner(kind, apiHost, env = process.env) {
|
|
|
76279
77133
|
timeout: options.timeoutMs,
|
|
76280
77134
|
killSignal: "SIGKILL",
|
|
76281
77135
|
windowsHide: true,
|
|
76282
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
77136
|
+
...options.input === undefined ? { stdio: ["ignore", "pipe", "pipe"] } : { input: options.input, stdio: ["pipe", "pipe", "pipe"] }
|
|
76283
77137
|
});
|
|
76284
77138
|
const error = result.error;
|
|
76285
77139
|
return {
|
|
@@ -76355,8 +77209,8 @@ function apiText(runner, bin, apiHost, args, what, timeoutMs = FORGE_TIMEOUTS.lo
|
|
|
76355
77209
|
return cliFailure(bin, apiHost, run, what, timeoutMs);
|
|
76356
77210
|
return { ok: true, data: run.stdout };
|
|
76357
77211
|
}
|
|
76358
|
-
function apiWrite(runner, bin, apiHost, args, what, timeoutMs = FORGE_TIMEOUTS.api) {
|
|
76359
|
-
const run = runner(bin, args, { timeoutMs });
|
|
77212
|
+
function apiWrite(runner, bin, apiHost, args, what, timeoutMs = FORGE_TIMEOUTS.api, input) {
|
|
77213
|
+
const run = runner(bin, args, input === undefined ? { timeoutMs } : { timeoutMs, input });
|
|
76360
77214
|
if (run.status !== 0) {
|
|
76361
77215
|
const text = redactText(`${run.stderr}
|
|
76362
77216
|
${run.stdout}`).trim();
|
|
@@ -76658,16 +77512,19 @@ function createGitHubForge(options) {
|
|
|
76658
77512
|
});
|
|
76659
77513
|
},
|
|
76660
77514
|
merge(number, mergeOptions) {
|
|
77515
|
+
const squash = mergeOptions.method === "squash" ? mergeOptions.squashMessage : undefined;
|
|
76661
77516
|
const merged = apiWrite(runner, "gh", apiHost, [
|
|
76662
77517
|
"api",
|
|
76663
77518
|
"-X",
|
|
76664
77519
|
"PUT",
|
|
76665
77520
|
`repos/${repo}/pulls/${number}/merge`,
|
|
76666
|
-
"-f",
|
|
76667
|
-
|
|
76668
|
-
|
|
76669
|
-
|
|
76670
|
-
|
|
77521
|
+
...squash ? ["--input", "-"] : ["-f", `sha=${mergeOptions.sha}`, "-f", `merge_method=${mergeOptions.method}`]
|
|
77522
|
+
], `merge of pull request #${number}`, undefined, squash ? JSON.stringify({
|
|
77523
|
+
sha: mergeOptions.sha,
|
|
77524
|
+
merge_method: mergeOptions.method,
|
|
77525
|
+
commit_title: squash.subject,
|
|
77526
|
+
commit_message: squash.body
|
|
77527
|
+
}) : undefined);
|
|
76671
77528
|
if (!merged.ok)
|
|
76672
77529
|
return merged;
|
|
76673
77530
|
if (merged.data.merged === false)
|
|
@@ -77025,6 +77882,7 @@ function createGitLabForge(options) {
|
|
|
77025
77882
|
});
|
|
77026
77883
|
},
|
|
77027
77884
|
merge(iid, mergeOptions) {
|
|
77885
|
+
const squash = mergeOptions.method === "squash" ? mergeOptions.squashMessage : undefined;
|
|
77028
77886
|
if (mergeOptions.method === "rebase")
|
|
77029
77887
|
return failure3("invalid_input", "GitLab merges with the project's merge method; pass --method squash or merge");
|
|
77030
77888
|
const merged = apiWrite(runner, "glab", apiHost, [
|
|
@@ -77032,11 +77890,19 @@ function createGitLabForge(options) {
|
|
|
77032
77890
|
"-X",
|
|
77033
77891
|
"PUT",
|
|
77034
77892
|
`${project}/merge_requests/${iid}/merge`,
|
|
77035
|
-
"-
|
|
77036
|
-
|
|
77037
|
-
|
|
77038
|
-
|
|
77039
|
-
|
|
77893
|
+
...squash ? ["--input", "-"] : [
|
|
77894
|
+
"-f",
|
|
77895
|
+
`sha=${mergeOptions.sha}`,
|
|
77896
|
+
"-F",
|
|
77897
|
+
`squash=${mergeOptions.method === "squash" ? "true" : "false"}`
|
|
77898
|
+
]
|
|
77899
|
+
], `merge of merge request !${iid}`, undefined, squash ? JSON.stringify({
|
|
77900
|
+
sha: mergeOptions.sha,
|
|
77901
|
+
squash: true,
|
|
77902
|
+
squash_commit_message: squash.body ? `${squash.subject}
|
|
77903
|
+
|
|
77904
|
+
${squash.body}` : squash.subject
|
|
77905
|
+
}) : undefined);
|
|
77040
77906
|
if (!merged.ok)
|
|
77041
77907
|
return merged;
|
|
77042
77908
|
const value = merged.data;
|
|
@@ -77181,7 +78047,7 @@ var init_gitlab = __esm(() => {
|
|
|
77181
78047
|
});
|
|
77182
78048
|
|
|
77183
78049
|
// packages/workit-core/src/forge/resolve.ts
|
|
77184
|
-
import
|
|
78050
|
+
import fs24 from "node:fs";
|
|
77185
78051
|
function memo4(scope, key, compute, now = Date.now) {
|
|
77186
78052
|
let entries = memoScopes.get(scope.owner);
|
|
77187
78053
|
if (!entries)
|
|
@@ -77226,7 +78092,7 @@ function resolveCredential(workspace, derived, run, scope) {
|
|
|
77226
78092
|
return failure3("blocked", `token_host_unverified: push host ${derived.host} only looks like ${kind}; workspace ${workspace?.name ?? "?"} vcs.tokenFile is not sent to it`, `if ${apiHost} is your ${kind} server, set ${kind}.host in ~/.config/workit/vcs.json (or vcs.host for the workspace); otherwise fix the push remote`);
|
|
77227
78093
|
let token = "";
|
|
77228
78094
|
try {
|
|
77229
|
-
token =
|
|
78095
|
+
token = fs24.readFileSync(tokenFile.trim(), "utf8").trim();
|
|
77230
78096
|
} catch {
|
|
77231
78097
|
return failure3("blocked", `workspace ${workspace?.name ?? "?"} vcs.tokenFile cannot be read`, "fix vcs.tokenFile in ~/.config/workit/workspaces.json");
|
|
77232
78098
|
}
|
|
@@ -77424,16 +78290,16 @@ function parseDuration2(value) {
|
|
|
77424
78290
|
return null;
|
|
77425
78291
|
let total = 0;
|
|
77426
78292
|
for (const [, amount, unit] of parts) {
|
|
77427
|
-
const factor =
|
|
78293
|
+
const factor = UNITS2.find(([pattern]) => pattern.test(unit))?.[1];
|
|
77428
78294
|
if (factor === undefined)
|
|
77429
78295
|
return null;
|
|
77430
78296
|
total += Number(amount) * factor;
|
|
77431
78297
|
}
|
|
77432
78298
|
return Math.round(total);
|
|
77433
78299
|
}
|
|
77434
|
-
var
|
|
78300
|
+
var UNITS2;
|
|
77435
78301
|
var init_duration = __esm(() => {
|
|
77436
|
-
|
|
78302
|
+
UNITS2 = [
|
|
77437
78303
|
[/^(?:ms|milliseconds?)$/u, 1],
|
|
77438
78304
|
[/^(?:s|secs?|seconds?)$/u, 1000],
|
|
77439
78305
|
[/^(?:m|mins?|minutes?)$/u, 60000],
|
|
@@ -77598,11 +78464,11 @@ __export(exports_pr, {
|
|
|
77598
78464
|
run: () => run11
|
|
77599
78465
|
});
|
|
77600
78466
|
import { readFileSync as readFileSync21 } from "node:fs";
|
|
77601
|
-
import
|
|
78467
|
+
import path46 from "node:path";
|
|
77602
78468
|
function readBodyFile(io, file) {
|
|
77603
78469
|
try {
|
|
77604
78470
|
return {
|
|
77605
|
-
body: file === "-" ? forgeDeps.readStdin() : readFileSync21(
|
|
78471
|
+
body: file === "-" ? forgeDeps.readStdin() : readFileSync21(path46.resolve(io.cwd, file), "utf8")
|
|
77606
78472
|
};
|
|
77607
78473
|
} catch {
|
|
77608
78474
|
return `cannot read --body-file ${file}`;
|
|
@@ -77780,7 +78646,10 @@ async function merge3(argv, io) {
|
|
|
77780
78646
|
method: "value",
|
|
77781
78647
|
"delete-branch": "boolean",
|
|
77782
78648
|
unverified: "boolean",
|
|
77783
|
-
reason: "value"
|
|
78649
|
+
reason: "value",
|
|
78650
|
+
subject: "value",
|
|
78651
|
+
body: "value",
|
|
78652
|
+
"body-file": "value"
|
|
77784
78653
|
});
|
|
77785
78654
|
if (typeof flags === "string")
|
|
77786
78655
|
return usage3(io, flags, MERGE_USAGE);
|
|
@@ -77795,6 +78664,21 @@ async function merge3(argv, io) {
|
|
|
77795
78664
|
const method = flags.values.method ?? "squash";
|
|
77796
78665
|
if (!METHODS2.has(method))
|
|
77797
78666
|
return usage3(io, "--method must be squash, merge or rebase", MERGE_USAGE);
|
|
78667
|
+
const { subject } = flags.values;
|
|
78668
|
+
if (flags.values.body !== undefined && flags.values["body-file"] !== undefined)
|
|
78669
|
+
return usage3(io, "pass --body or --body-file, not both", MERGE_USAGE);
|
|
78670
|
+
let body = flags.values.body;
|
|
78671
|
+
if (flags.values["body-file"] !== undefined) {
|
|
78672
|
+
const read = readBodyFile(io, flags.values["body-file"]);
|
|
78673
|
+
if (typeof read === "string")
|
|
78674
|
+
return usage3(io, read, MERGE_USAGE);
|
|
78675
|
+
body = read.body;
|
|
78676
|
+
}
|
|
78677
|
+
if ((subject !== undefined || body !== undefined) && method !== "squash")
|
|
78678
|
+
return usage3(io, "--subject and --body set a squash message; use --method squash", MERGE_USAGE);
|
|
78679
|
+
if (subject !== undefined && (!subject.trim() || subject.includes(`
|
|
78680
|
+
`)))
|
|
78681
|
+
return usage3(io, "--subject must be one non-empty line", MERGE_USAGE);
|
|
77798
78682
|
const connected = connect(io);
|
|
77799
78683
|
if (!connected.ok)
|
|
77800
78684
|
return forgeFail(io, connected);
|
|
@@ -77803,7 +78687,9 @@ async function merge3(argv, io) {
|
|
|
77803
78687
|
method,
|
|
77804
78688
|
deleteBranch: flags.booleans.has("delete-branch"),
|
|
77805
78689
|
actor: actorFromEnv(io.env),
|
|
77806
|
-
...unverified ? { unverified } : {}
|
|
78690
|
+
...unverified ? { unverified } : {},
|
|
78691
|
+
...subject !== undefined ? { subject } : {},
|
|
78692
|
+
...body !== undefined ? { body } : {}
|
|
77807
78693
|
}, forgeDeps.sleep);
|
|
77808
78694
|
if (!result.ok)
|
|
77809
78695
|
return emit(io, fail(result.code, result.error, {
|
|
@@ -77967,7 +78853,7 @@ async function run11(argv, io) {
|
|
|
77967
78853
|
return reply(rest, io);
|
|
77968
78854
|
return usage3(io, sub && !sub.startsWith("-") ? `unknown pr subcommand "${sub}"` : "missing pr subcommand", USAGE4);
|
|
77969
78855
|
}
|
|
77970
|
-
var STATUS_USAGE = "workit pr status [--pr <n> | --branch <b>] [--log-lines 60] [--json]", CREATE_USAGE = "workit pr create [--base <b> | --track <t>] (--title <t> [--body <text> | --body-file <f|->] | --fill) [--label <l>]… [--reviewer <login>]… [--draft] [--json]", MERGE_USAGE = "workit pr merge [--pr <n>] [--method squash|merge|rebase] [--delete-branch] [--unverified --reason <why>] [--json]", READY_USAGE = "workit pr ready [--pr <n>] [--undo] [--json]", EDIT_USAGE = "workit pr edit [--pr <n>] [--title <t>] [--body-file <f|->] [--add-label <l>]… [--remove-label <l>]… [--add-reviewer <login>]… [--base <b>] [--json]", THREADS_USAGE = "workit pr threads [--pr <n>] [--json]", REPLY_USAGE = "workit pr reply [--pr <n>] --thread <id> [--body-file <f|->] [--resolve] [--json]", USAGE4 = "workit pr status|create|merge|ready|edit|threads|reply ... (workit help pr)", prLabel = (kind, number) => `${kind === "github" ? "PR #" : "MR !"}${number}`, mergedVerdictLine = (landed) => landed === "untracked" ? "merged outside workit (no merge record)" : landed.unverified ? `merged unverified at ${(landed.head ?? "?").slice(0, 12)} (recorded bypass)` : `merged with accepted verdict ${landed.verdictId ?? "?"} at ${(landed.head ?? "?").slice(0, 12)}`, METHODS2;
|
|
78856
|
+
var STATUS_USAGE = "workit pr status [--pr <n> | --branch <b>] [--log-lines 60] [--json]", CREATE_USAGE = "workit pr create [--base <b> | --track <t>] (--title <t> [--body <text> | --body-file <f|->] | --fill) [--label <l>]… [--reviewer <login>]… [--draft] [--json]", MERGE_USAGE = "workit pr merge [--pr <n>] [--method squash|merge|rebase] [--subject <s>] [--body <b> | --body-file <f|->] [--delete-branch] [--unverified --reason <why>] [--json]", READY_USAGE = "workit pr ready [--pr <n>] [--undo] [--json]", EDIT_USAGE = "workit pr edit [--pr <n>] [--title <t>] [--body-file <f|->] [--add-label <l>]… [--remove-label <l>]… [--add-reviewer <login>]… [--base <b>] [--json]", THREADS_USAGE = "workit pr threads [--pr <n>] [--json]", REPLY_USAGE = "workit pr reply [--pr <n>] --thread <id> [--body-file <f|->] [--resolve] [--json]", USAGE4 = "workit pr status|create|merge|ready|edit|threads|reply ... (workit help pr)", prLabel = (kind, number) => `${kind === "github" ? "PR #" : "MR !"}${number}`, mergedVerdictLine = (landed) => landed === "untracked" ? "merged outside workit (no merge record)" : landed.unverified ? `merged unverified at ${(landed.head ?? "?").slice(0, 12)} (recorded bypass)` : `merged with accepted verdict ${landed.verdictId ?? "?"} at ${(landed.head ?? "?").slice(0, 12)}`, METHODS2;
|
|
77971
78857
|
var init_pr = __esm(() => {
|
|
77972
78858
|
init_pr_body();
|
|
77973
78859
|
init_pr_edit();
|
|
@@ -78205,7 +79091,7 @@ __export(exports_git, {
|
|
|
78205
79091
|
run: () => run13
|
|
78206
79092
|
});
|
|
78207
79093
|
import { readFileSync as readFileSync22 } from "node:fs";
|
|
78208
|
-
import
|
|
79094
|
+
import path47 from "node:path";
|
|
78209
79095
|
function kindHint(cwd, kind) {
|
|
78210
79096
|
let kinds = [...BRANCH_KINDS];
|
|
78211
79097
|
try {
|
|
@@ -78256,6 +79142,7 @@ function parseCommit(argv) {
|
|
|
78256
79142
|
const out = {
|
|
78257
79143
|
messages: [],
|
|
78258
79144
|
files: [],
|
|
79145
|
+
trailers: [],
|
|
78259
79146
|
all: false,
|
|
78260
79147
|
amend: false,
|
|
78261
79148
|
noEdit: false,
|
|
@@ -78277,14 +79164,15 @@ function parseCommit(argv) {
|
|
|
78277
79164
|
}
|
|
78278
79165
|
const valued = [
|
|
78279
79166
|
["-m", "--message", out.messages],
|
|
78280
|
-
["-F", "--file", out.files]
|
|
78281
|
-
|
|
79167
|
+
["-F", "--file", out.files],
|
|
79168
|
+
[null, "--trailer", out.trailers]
|
|
79169
|
+
].find(([shortName, longName]) => arg === shortName || arg === longName || arg.startsWith(`${longName}=`) || shortName !== null && arg.startsWith(shortName) && arg.length > 2);
|
|
78282
79170
|
if (valued) {
|
|
78283
79171
|
const [shortName, longName, into] = valued;
|
|
78284
79172
|
if (arg === shortName || arg === longName) {
|
|
78285
79173
|
const value = argv[++index];
|
|
78286
79174
|
if (value === undefined)
|
|
78287
|
-
return `${arg} requires a ${shortName === "-m" ? "message" : "file"}`;
|
|
79175
|
+
return `${arg} requires a ${shortName === "-m" ? "message" : shortName === "-F" ? "file" : "Key: value trailer"}`;
|
|
78288
79176
|
into.push(value);
|
|
78289
79177
|
} else
|
|
78290
79178
|
into.push(arg.startsWith(`${longName}=`) ? arg.slice(longName.length + 1) : arg.slice(2));
|
|
@@ -78307,7 +79195,7 @@ function commitMessage(parsed, io) {
|
|
|
78307
79195
|
|
|
78308
79196
|
`);
|
|
78309
79197
|
try {
|
|
78310
|
-
return file === "-" ? readFileSync22(0, "utf8") : readFileSync22(
|
|
79198
|
+
return file === "-" ? readFileSync22(0, "utf8") : readFileSync22(path47.resolve(io.cwd, file), "utf8");
|
|
78311
79199
|
} catch (error) {
|
|
78312
79200
|
return {
|
|
78313
79201
|
error: `cannot read -F ${file}: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -78338,6 +79226,7 @@ async function commit(argv, io) {
|
|
|
78338
79226
|
paths: parsed.paths,
|
|
78339
79227
|
amend: parsed.amend,
|
|
78340
79228
|
allowEmpty: parsed.allowEmpty,
|
|
79229
|
+
trailers: parsed.trailers,
|
|
78341
79230
|
actor: actorFromEnv(io.env),
|
|
78342
79231
|
env: io.env
|
|
78343
79232
|
});
|
|
@@ -78450,6 +79339,13 @@ async function releasePush(names, io) {
|
|
|
78450
79339
|
..."error" in data.recorded ? [`ledger: not recorded (${data.recorded.error})`] : []
|
|
78451
79340
|
]);
|
|
78452
79341
|
}
|
|
79342
|
+
function trailers(argv, io) {
|
|
79343
|
+
const extra = argv.find((arg) => arg !== "--json");
|
|
79344
|
+
if (extra !== undefined)
|
|
79345
|
+
return usage3(io, `unexpected argument ${extra}`, "workit git trailers");
|
|
79346
|
+
const state = workspaceTrailers(io.cwd);
|
|
79347
|
+
return emit(io, ok(state), (data) => data.enabled ? "on" : "off");
|
|
79348
|
+
}
|
|
78453
79349
|
async function run13(argv, io) {
|
|
78454
79350
|
const [sub, ...rest] = argv;
|
|
78455
79351
|
if (sub === "branch")
|
|
@@ -78458,13 +79354,16 @@ async function run13(argv, io) {
|
|
|
78458
79354
|
return commit(rest, io);
|
|
78459
79355
|
if (sub === "push")
|
|
78460
79356
|
return push(rest, io);
|
|
79357
|
+
if (sub === "trailers")
|
|
79358
|
+
return trailers(rest, io);
|
|
78461
79359
|
return usage3(io, sub && !sub.startsWith("-") ? `unknown git subcommand "${sub}"` : "missing git subcommand", USAGE5);
|
|
78462
79360
|
}
|
|
78463
|
-
var BRANCH_USAGE = "workit git branch <name> | --kind feature|bugfix|release|hotfix --slug <s> [--base <b>] [--track <t>] [--carry] [--json]", COMMIT_USAGE = "workit git commit -m|--message <msg>… | -F|--file <file|-> [--amend [--no-edit]] [--allow-empty] [-a|--all | [--] <paths…>] [--json]", PUSH_USAGE = "workit git push [--set-upstream] [--force-with-lease [--expect <sha>] [--overwrite-unintegrated]] [--release <main|develop|v-tag>…] [--json]", USAGE5 = "workit git branch|commit|push ... (workit help git)", short2 = (sha) => sha ? sha.slice(0, 12) : "(none)", COMMIT_SWITCHES;
|
|
79361
|
+
var BRANCH_USAGE = "workit git branch <name> | --kind feature|bugfix|release|hotfix --slug <s> [--base <b>] [--track <t>] [--carry] [--json]", COMMIT_USAGE = "workit git commit -m|--message <msg>… | -F|--file <file|-> [--trailer <key: value>]… [--amend [--no-edit]] [--allow-empty] [-a|--all | [--] <paths…>] [--json]", PUSH_USAGE = "workit git push [--set-upstream] [--force-with-lease [--expect <sha>] [--overwrite-unintegrated]] [--release <main|develop|v-tag>…] [--json]", USAGE5 = "workit git branch|commit|push|trailers ... (workit help git)", short2 = (sha) => sha ? sha.slice(0, 12) : "(none)", COMMIT_SWITCHES;
|
|
78464
79362
|
var init_git2 = __esm(() => {
|
|
78465
79363
|
init_autonomy();
|
|
78466
79364
|
init_branch();
|
|
78467
79365
|
init_ops();
|
|
79366
|
+
init_workspaces();
|
|
78468
79367
|
init_ledger();
|
|
78469
79368
|
init_implicit_task();
|
|
78470
79369
|
init_output();
|
|
@@ -79015,8 +79914,8 @@ var init_stack2 = __esm(() => {
|
|
|
79015
79914
|
// packages/workit-core/src/fanout.ts
|
|
79016
79915
|
import { spawnSync as spawnSync24 } from "node:child_process";
|
|
79017
79916
|
import { createHash as createHash18 } from "node:crypto";
|
|
79018
|
-
import
|
|
79019
|
-
import
|
|
79917
|
+
import fs25 from "node:fs";
|
|
79918
|
+
import path48 from "node:path";
|
|
79020
79919
|
function expandBraces(pattern, limit = 64) {
|
|
79021
79920
|
for (let index = 0;index < pattern.length; index += 1) {
|
|
79022
79921
|
if (pattern[index] === "\\") {
|
|
@@ -79162,7 +80061,7 @@ function inScope(plan, slice, file) {
|
|
|
79162
80061
|
}
|
|
79163
80062
|
function parsePlan(raw, defaults, name) {
|
|
79164
80063
|
const problems = [];
|
|
79165
|
-
if (!
|
|
80064
|
+
if (!isRecord9(raw))
|
|
79166
80065
|
return { ok: false, problems: ["the plan must be a JSON object"] };
|
|
79167
80066
|
const planName = name ?? (typeof raw.name === "string" ? raw.name.trim() : "");
|
|
79168
80067
|
if (!planName || planName.length > 200 || /\p{Cc}/u.test(planName))
|
|
@@ -79185,8 +80084,8 @@ function parsePlan(raw, defaults, name) {
|
|
|
79185
80084
|
const seenIds = new Set;
|
|
79186
80085
|
const seenBranches = new Map;
|
|
79187
80086
|
raw.slices.forEach((item, index) => {
|
|
79188
|
-
const where =
|
|
79189
|
-
if (!
|
|
80087
|
+
const where = isRecord9(item) && typeof item.id === "string" ? `slice ${item.id}` : `slice #${index + 1}`;
|
|
80088
|
+
if (!isRecord9(item)) {
|
|
79190
80089
|
problems.push(`${where}: must be an object`);
|
|
79191
80090
|
return;
|
|
79192
80091
|
}
|
|
@@ -79250,7 +80149,7 @@ function parsePlan(raw, defaults, name) {
|
|
|
79250
80149
|
id,
|
|
79251
80150
|
branch,
|
|
79252
80151
|
base: optional("base") ?? "",
|
|
79253
|
-
worktree:
|
|
80152
|
+
worktree: path48.resolve(defaults.root, optional("worktree") ?? path48.join(defaults.worktreeRoot, id)),
|
|
79254
80153
|
tier,
|
|
79255
80154
|
dependsOn: [...new Set(dependsOn ?? [])],
|
|
79256
80155
|
scope,
|
|
@@ -79479,11 +80378,11 @@ function defaultTrunk(cwd) {
|
|
|
79479
80378
|
}
|
|
79480
80379
|
function mainCheckout(cwd) {
|
|
79481
80380
|
const common = gitCommonDir(cwd);
|
|
79482
|
-
return common &&
|
|
80381
|
+
return common && path48.basename(common) === ".git" ? path48.dirname(common) : path48.resolve(cwd);
|
|
79483
80382
|
}
|
|
79484
80383
|
function worktreeRoot(cwd) {
|
|
79485
80384
|
const main = mainCheckout(cwd);
|
|
79486
|
-
return
|
|
80385
|
+
return path48.join(path48.dirname(main), `${path48.basename(main)}-wt`);
|
|
79487
80386
|
}
|
|
79488
80387
|
function sliceHash(slice) {
|
|
79489
80388
|
const {
|
|
@@ -79501,10 +80400,10 @@ function fanoutsDir(cwd) {
|
|
|
79501
80400
|
const root = storeRoot(cwd);
|
|
79502
80401
|
if (!root.ok)
|
|
79503
80402
|
return fanoutFail(root.code, root.error, root.unblock);
|
|
79504
|
-
return { ok: true, data:
|
|
80403
|
+
return { ok: true, data: path48.join(root.value.root, "fanouts") };
|
|
79505
80404
|
}
|
|
79506
80405
|
function parseStored(raw) {
|
|
79507
|
-
if (!
|
|
80406
|
+
if (!isRecord9(raw) || typeof raw.name !== "string" || typeof raw.trunk !== "string")
|
|
79508
80407
|
return null;
|
|
79509
80408
|
if (!Array.isArray(raw.slices))
|
|
79510
80409
|
return null;
|
|
@@ -79521,7 +80420,7 @@ function parseStored(raw) {
|
|
|
79521
80420
|
});
|
|
79522
80421
|
const slices = [];
|
|
79523
80422
|
for (const item of raw.slices) {
|
|
79524
|
-
if (!
|
|
80423
|
+
if (!isRecord9(item) || typeof item.id !== "string" || typeof item.branch !== "string")
|
|
79525
80424
|
return null;
|
|
79526
80425
|
slices.push({
|
|
79527
80426
|
id: item.id,
|
|
@@ -79567,10 +80466,10 @@ function readFanout(cwd, name) {
|
|
|
79567
80466
|
const dir = fanoutsDir(cwd);
|
|
79568
80467
|
if (!dir.ok)
|
|
79569
80468
|
return dir;
|
|
79570
|
-
const file =
|
|
80469
|
+
const file = path48.join(dir.data, stackFileName(name));
|
|
79571
80470
|
let parsed = null;
|
|
79572
80471
|
try {
|
|
79573
|
-
parsed = parseStored(JSON.parse(
|
|
80472
|
+
parsed = parseStored(JSON.parse(fs25.readFileSync(file, "utf8")));
|
|
79574
80473
|
} catch (error) {
|
|
79575
80474
|
if (error.code === "ENOENT")
|
|
79576
80475
|
return { ok: true, data: null };
|
|
@@ -79584,7 +80483,7 @@ function listFanouts(cwd) {
|
|
|
79584
80483
|
return dir;
|
|
79585
80484
|
let names = [];
|
|
79586
80485
|
try {
|
|
79587
|
-
names =
|
|
80486
|
+
names = fs25.readdirSync(dir.data).filter((entry) => entry.endsWith(".json"));
|
|
79588
80487
|
} catch {
|
|
79589
80488
|
return { ok: true, data: [] };
|
|
79590
80489
|
}
|
|
@@ -79592,7 +80491,7 @@ function listFanouts(cwd) {
|
|
|
79592
80491
|
for (const entry of names) {
|
|
79593
80492
|
let parsed = null;
|
|
79594
80493
|
try {
|
|
79595
|
-
parsed = parseStored(JSON.parse(
|
|
80494
|
+
parsed = parseStored(JSON.parse(fs25.readFileSync(path48.join(dir.data, entry), "utf8")));
|
|
79596
80495
|
} catch {
|
|
79597
80496
|
parsed = null;
|
|
79598
80497
|
}
|
|
@@ -79622,16 +80521,16 @@ function writeFanout(cwd, plan) {
|
|
|
79622
80521
|
const dir = fanoutsDir(cwd);
|
|
79623
80522
|
if (!dir.ok)
|
|
79624
80523
|
return dir;
|
|
79625
|
-
const file =
|
|
80524
|
+
const file = path48.join(dir.data, stackFileName(plan.name));
|
|
79626
80525
|
const temp = `${file}.${process.pid}.tmp`;
|
|
79627
80526
|
try {
|
|
79628
|
-
|
|
79629
|
-
|
|
80527
|
+
fs25.mkdirSync(dir.data, { recursive: true });
|
|
80528
|
+
fs25.writeFileSync(temp, `${JSON.stringify(plan, null, 2)}
|
|
79630
80529
|
`);
|
|
79631
|
-
|
|
80530
|
+
fs25.renameSync(temp, file);
|
|
79632
80531
|
return { ok: true, data: file };
|
|
79633
80532
|
} catch (error) {
|
|
79634
|
-
|
|
80533
|
+
fs25.rmSync(temp, { force: true });
|
|
79635
80534
|
return fanoutFail("unavailable", `cannot write ${file}: ${error.message}`);
|
|
79636
80535
|
}
|
|
79637
80536
|
}
|
|
@@ -79644,7 +80543,7 @@ function planFanout(cwd, input, now = new Date) {
|
|
|
79644
80543
|
return fanoutFail("blocked", "--take-lead needs a session: WORKIT_SESSION_ID is not set", "export WORKIT_SESSION_ID=<your session id>, then plan again with --take-lead");
|
|
79645
80544
|
if (input.takeLead && isDelegateSession(session))
|
|
79646
80545
|
return fanoutFail("blocked", `session ${session} is a worker or verifier id; only a lead takes the lead`, "run --take-lead from the lead's own session");
|
|
79647
|
-
const parsed = parsePlan(input.trunk &&
|
|
80546
|
+
const parsed = parsePlan(input.trunk && isRecord9(input.raw) ? { ...input.raw, trunk: input.trunk } : input.raw, {
|
|
79648
80547
|
trunk: input.trunk ?? defaultTrunk(cwd),
|
|
79649
80548
|
root: mainCheckout(cwd),
|
|
79650
80549
|
worktreeRoot: worktreeRoot(cwd)
|
|
@@ -79767,7 +80666,7 @@ var FANOUT_VERSION = 1, TIERS, FAN_IN_MODES, fanoutFail = (code, error, unblock,
|
|
|
79767
80666
|
error,
|
|
79768
80667
|
...unblock ? { unblock } : {},
|
|
79769
80668
|
...data ? { data } : {}
|
|
79770
|
-
}), DEFAULT_SHARED, escapeRegExp4 = (text) => text.replace(/[.+^$()|[\]\\{}*?]/gu, "\\$&"), unescape2 = (pattern) => pattern.replace(/\\(.)/gu, "$1"), hasWildcard = (pattern) => /[*?{]/u.test(pattern.replace(/\\./gu, "")), compiled, matchesAny = (patterns, file) => patterns.some((pattern) => matchesScope(pattern, file)),
|
|
80669
|
+
}), DEFAULT_SHARED, escapeRegExp4 = (text) => text.replace(/[.+^$()|[\]\\{}*?]/gu, "\\$&"), unescape2 = (pattern) => pattern.replace(/\\(.)/gu, "$1"), hasWildcard = (pattern) => /[*?{]/u.test(pattern.replace(/\\./gu, "")), compiled, matchesAny = (patterns, file) => patterns.some((pattern) => matchesScope(pattern, file)), isRecord9 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), PLACEHOLDER3, filled = (text) => text.trim() !== "" && !PLACEHOLDER3.test(text.trim()), textList = (value) => {
|
|
79771
80670
|
if (typeof value === "string")
|
|
79772
80671
|
return [value];
|
|
79773
80672
|
if (Array.isArray(value) && value.every((item) => typeof item === "string"))
|
|
@@ -79825,8 +80724,8 @@ var init_fanout = __esm(() => {
|
|
|
79825
80724
|
|
|
79826
80725
|
// packages/workit-core/src/fanout-worktree.ts
|
|
79827
80726
|
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
79828
|
-
import
|
|
79829
|
-
import
|
|
80727
|
+
import fs26 from "node:fs";
|
|
80728
|
+
import path49 from "node:path";
|
|
79830
80729
|
function rowSample(lines) {
|
|
79831
80730
|
const out = [];
|
|
79832
80731
|
let bytes = 0;
|
|
@@ -79886,7 +80785,7 @@ function adminDir(worktree) {
|
|
|
79886
80785
|
const file = dir.stdout.trim();
|
|
79887
80786
|
let token = null;
|
|
79888
80787
|
try {
|
|
79889
|
-
token =
|
|
80788
|
+
token = fs26.readFileSync(path49.join(file, TOKEN_FILE), "utf8").trim() || null;
|
|
79890
80789
|
} catch {
|
|
79891
80790
|
token = null;
|
|
79892
80791
|
}
|
|
@@ -79894,7 +80793,7 @@ function adminDir(worktree) {
|
|
|
79894
80793
|
const stat = worktreeDeps.stat(file);
|
|
79895
80794
|
return {
|
|
79896
80795
|
path: file,
|
|
79897
|
-
id:
|
|
80796
|
+
id: path49.basename(file),
|
|
79898
80797
|
ino: stat.ino.toString(),
|
|
79899
80798
|
token,
|
|
79900
80799
|
birthMs: stat.birthtimeNs > 0n ? Number(stat.birthtimeMs) : null,
|
|
@@ -79910,7 +80809,7 @@ function markAdminDir(worktree) {
|
|
|
79910
80809
|
return null;
|
|
79911
80810
|
const token = randomBytes4(16).toString("hex");
|
|
79912
80811
|
try {
|
|
79913
|
-
|
|
80812
|
+
fs26.writeFileSync(path49.join(admin.path, TOKEN_FILE), `${token}
|
|
79914
80813
|
`);
|
|
79915
80814
|
} catch {
|
|
79916
80815
|
return null;
|
|
@@ -79925,25 +80824,25 @@ function excludeScratch(cwd) {
|
|
|
79925
80824
|
const common = gitCommonDir(cwd);
|
|
79926
80825
|
if (!common)
|
|
79927
80826
|
return;
|
|
79928
|
-
const file =
|
|
80827
|
+
const file = path49.join(common, "info", "exclude");
|
|
79929
80828
|
let text = "";
|
|
79930
80829
|
try {
|
|
79931
|
-
text =
|
|
80830
|
+
text = fs26.readFileSync(file, "utf8");
|
|
79932
80831
|
} catch {
|
|
79933
80832
|
text = "";
|
|
79934
80833
|
}
|
|
79935
80834
|
if (text.split(/\r?\n/u).includes(EXCLUDE_LINE))
|
|
79936
80835
|
return;
|
|
79937
|
-
|
|
79938
|
-
|
|
80836
|
+
fs26.mkdirSync(path49.dirname(file), { recursive: true });
|
|
80837
|
+
fs26.appendFileSync(file, `${text && !text.endsWith(`
|
|
79939
80838
|
`) ? `
|
|
79940
80839
|
` : ""}# workit fanout: per-worker scratch dirs
|
|
79941
80840
|
${EXCLUDE_LINE}
|
|
79942
80841
|
`);
|
|
79943
80842
|
}
|
|
79944
80843
|
function ensureScratch(worktree) {
|
|
79945
|
-
const scratch =
|
|
79946
|
-
|
|
80844
|
+
const scratch = path49.join(worktree, SCRATCH_DIR);
|
|
80845
|
+
fs26.mkdirSync(scratch, { recursive: true });
|
|
79947
80846
|
return scratch;
|
|
79948
80847
|
}
|
|
79949
80848
|
function createSliceWorktree(cwd, plan, input) {
|
|
@@ -79987,8 +80886,8 @@ function createSliceWorktree(cwd, plan, input) {
|
|
|
79987
80886
|
return fanoutFail("blocked", `${slice.branch} is checked out at ${elsewhere.path}: never two live workers on one branch`, `stop that worker, then workit fanout worktree release ${slice.id} (or git worktree remove ${elsewhere.path})`);
|
|
79988
80887
|
let dirExisted = false;
|
|
79989
80888
|
try {
|
|
79990
|
-
const stat =
|
|
79991
|
-
if (!stat.isDirectory() ||
|
|
80889
|
+
const stat = fs26.statSync(target);
|
|
80890
|
+
if (!stat.isDirectory() || fs26.readdirSync(target).length > 0)
|
|
79992
80891
|
return fanoutFail("blocked", `${target} exists and is ${stat.isDirectory() ? "not empty" : "not a directory"} (not a worktree of this repository)`, `move it away, or set slice ${slice.id}'s worktree to another path`);
|
|
79993
80892
|
dirExisted = true;
|
|
79994
80893
|
} catch (error) {
|
|
@@ -80070,7 +80969,7 @@ function releaseSliceWorktree(cwd, plan, input) {
|
|
|
80070
80969
|
notes: []
|
|
80071
80970
|
};
|
|
80072
80971
|
if (index < 0) {
|
|
80073
|
-
if (
|
|
80972
|
+
if (fs26.existsSync(target))
|
|
80074
80973
|
return fanoutFail("blocked", `${target} is not a worktree of this repository; nothing was removed`, "remove it yourself once you know what it holds");
|
|
80075
80974
|
out.notes.push(`no worktree at ${target}; nothing to release`);
|
|
80076
80975
|
return { ok: true, data: out };
|
|
@@ -80132,7 +81031,7 @@ function releaseSliceWorktree(cwd, plan, input) {
|
|
|
80132
81031
|
`)[0]}`, entry.locked ? `git worktree unlock ${entry.path}, then release again` : undefined, { ...out });
|
|
80133
81032
|
out.removed = true;
|
|
80134
81033
|
if (made.dirExisted === true)
|
|
80135
|
-
|
|
81034
|
+
fs26.mkdirSync(entry.path, { recursive: true });
|
|
80136
81035
|
if (out.unpushed)
|
|
80137
81036
|
out.notes.push(`${slice.branch} is kept with ${out.unpushed} commit(s) not on the remote`);
|
|
80138
81037
|
return { ok: true, data: out };
|
|
@@ -80160,32 +81059,32 @@ function unpushedCount(cwd, branch) {
|
|
|
80160
81059
|
return count.ok ? Number(count.stdout.trim()) : null;
|
|
80161
81060
|
}
|
|
80162
81061
|
function removeScratch(worktree) {
|
|
80163
|
-
const scratch =
|
|
81062
|
+
const scratch = path49.join(worktree, SCRATCH_DIR);
|
|
80164
81063
|
let stat;
|
|
80165
81064
|
try {
|
|
80166
|
-
stat =
|
|
81065
|
+
stat = fs26.lstatSync(scratch);
|
|
80167
81066
|
} catch {
|
|
80168
81067
|
return;
|
|
80169
81068
|
}
|
|
80170
81069
|
if (stat.isSymbolicLink() || !stat.isDirectory())
|
|
80171
|
-
|
|
81070
|
+
fs26.unlinkSync(scratch);
|
|
80172
81071
|
else
|
|
80173
|
-
|
|
81072
|
+
fs26.rmSync(scratch, { recursive: true, force: true, maxRetries: 2 });
|
|
80174
81073
|
}
|
|
80175
81074
|
var SLOW, SCRATCH_DIR = ".workit-scratch", EXCLUDE_LINE, DIRTY_ROW_BYTES = 2000, canonical2 = (target) => {
|
|
80176
81075
|
try {
|
|
80177
|
-
return
|
|
81076
|
+
return fs26.realpathSync.native(target);
|
|
80178
81077
|
} catch {
|
|
80179
|
-
return
|
|
81078
|
+
return path49.resolve(target);
|
|
80180
81079
|
}
|
|
80181
81080
|
}, samePath = (a, b) => {
|
|
80182
81081
|
const left = canonical2(a);
|
|
80183
81082
|
const right = canonical2(b);
|
|
80184
81083
|
return process.platform === "win32" || process.platform === "darwin" ? left.toLowerCase() === right.toLowerCase() : left === right;
|
|
80185
81084
|
}, inside3 = (child, parent) => {
|
|
80186
|
-
const relative =
|
|
80187
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
80188
|
-
}, slicePath = (cwd, slice) =>
|
|
81085
|
+
const relative = path49.relative(canonical2(parent), canonical2(child));
|
|
81086
|
+
return relative === "" || !relative.startsWith("..") && !path49.isAbsolute(relative);
|
|
81087
|
+
}, slicePath = (cwd, slice) => path49.isAbsolute(slice.worktree) ? slice.worktree : path49.resolve(mainCheckout(cwd), slice.worktree), CLOCK_SLACK_MS = 2000, TOKEN_FILE = "workit-fanout-token", worktreeDeps, worktreeFailed = (stderr) => fanoutFail("failed", `git worktree add failed: ${stderr.trim().split(`
|
|
80189
81088
|
`)[0] || "unknown error"}`);
|
|
80190
81089
|
var init_fanout_worktree = __esm(() => {
|
|
80191
81090
|
init_ops();
|
|
@@ -80195,7 +81094,7 @@ var init_fanout_worktree = __esm(() => {
|
|
|
80195
81094
|
SLOW = GIT_TIMEOUTS.worktree;
|
|
80196
81095
|
EXCLUDE_LINE = `/${SCRATCH_DIR}/`;
|
|
80197
81096
|
worktreeDeps = {
|
|
80198
|
-
stat: (file) =>
|
|
81097
|
+
stat: (file) => fs26.statSync(file, { bigint: true })
|
|
80199
81098
|
};
|
|
80200
81099
|
});
|
|
80201
81100
|
|
|
@@ -80236,7 +81135,9 @@ function renderBrief(cwd, plan, input) {
|
|
|
80236
81135
|
id: row.id,
|
|
80237
81136
|
what: String(row.what)
|
|
80238
81137
|
}));
|
|
80239
|
-
const
|
|
81138
|
+
const identity = identityOf(input.actor.host);
|
|
81139
|
+
const minted = workerSession(sessionSafe(input.actor.session), slice.id, input.attempt);
|
|
81140
|
+
const session = identity === "declared" ? minted : null;
|
|
80240
81141
|
const fanIn = plan.fanIn === "integration" ? [
|
|
80241
81142
|
`FAN-IN: integration branch ${plan.trunk}. Before you report, merge its tip into your branch`,
|
|
80242
81143
|
` (\`git merge --no-edit ${plan.trunk}\`), resolve conflicts inside SCOPE and re-run VERIFY, so the`,
|
|
@@ -80265,8 +81166,8 @@ function renderBrief(cwd, plan, input) {
|
|
|
80265
81166
|
...REPORT,
|
|
80266
81167
|
"STANDING:",
|
|
80267
81168
|
...standing.length ? standing.map((order) => ` - ${order.what}`) : [" - none recorded beyond this brief"],
|
|
80268
|
-
|
|
80269
|
-
...rules(mode, branch, slice.base)
|
|
81169
|
+
...identityLines(identity, minted),
|
|
81170
|
+
...rules(identity, mode, branch, slice.base)
|
|
80270
81171
|
];
|
|
80271
81172
|
return {
|
|
80272
81173
|
ok: true,
|
|
@@ -80287,13 +81188,38 @@ function renderBrief(cwd, plan, input) {
|
|
|
80287
81188
|
}
|
|
80288
81189
|
};
|
|
80289
81190
|
}
|
|
80290
|
-
var DEFAULT_TIMEBOX = "30 minutes", REPORT,
|
|
80291
|
-
"
|
|
80292
|
-
|
|
80293
|
-
|
|
80294
|
-
|
|
80295
|
-
|
|
80296
|
-
|
|
81191
|
+
var DEFAULT_TIMEBOX = "30 minutes", REPORT, identityOf = (host) => {
|
|
81192
|
+
const family = host?.replace(/_(?:cli|desktop)$/, "") ?? "";
|
|
81193
|
+
if (family === "claude_code")
|
|
81194
|
+
return "issued";
|
|
81195
|
+
if (family === "opencode" || family === "pi" || family === "codex")
|
|
81196
|
+
return "derived";
|
|
81197
|
+
return "declared";
|
|
81198
|
+
}, identityLines = (identity, session) => {
|
|
81199
|
+
if (identity === "issued")
|
|
81200
|
+
return [
|
|
81201
|
+
" Session: your env already has WORKIT_SESSION_ID (the lead's id; SubagentStart issues ids only to verifiers and reviewers). Use it as is, in your Workit-Session trailer and workit commands; never export or hand-mint one."
|
|
81202
|
+
];
|
|
81203
|
+
if (identity === "derived")
|
|
81204
|
+
return [
|
|
81205
|
+
" Session: your host already sets WORKIT_SESSION_ID in your shell; export nothing and never hand-mint one."
|
|
81206
|
+
];
|
|
81207
|
+
return [` export WORKIT_SESSION_ID=${session}`];
|
|
81208
|
+
}, rules = (identity, mode, branch, base) => {
|
|
81209
|
+
const plain = identity === "issued";
|
|
81210
|
+
let first = ` 1. First command: \`workit git branch ${branch} --base ${base}\` (your worktree may start on another name).`;
|
|
81211
|
+
if (mode === "resume")
|
|
81212
|
+
first = ` 1. First command: \`git switch ${branch}\`, then continue from its head.`;
|
|
81213
|
+
else if (plain)
|
|
81214
|
+
first = ` 1. First command: \`git fetch origin && git switch -c ${branch} origin/${base}\` (plain git: worktree isolation refuses every \`workit git\` form).`;
|
|
81215
|
+
return [
|
|
81216
|
+
"RULES:",
|
|
81217
|
+
first,
|
|
81218
|
+
' 2. Decide ambiguities yourself: `workit ledger ruling "<what>" --why "<why>" --cost-if-wrong "<cost>"`; stop only for an irreversible, security-sensitive or out-of-worktree action.',
|
|
81219
|
+
plain ? ' 3. Commit with plain `git commit -m <msg> --trailer "Workit-Session: <the WORKIT_SESSION_ID in your env>" --trailer "<your host attribution trailer>" -- <paths in SCOPE>`; push only if this brief says so.' : " 3. Commit with `workit git commit -m <msg> -- <paths in SCOPE>`; push only if this brief says so.",
|
|
81220
|
+
" 4. Never record a verdict on your own work."
|
|
81221
|
+
];
|
|
81222
|
+
}, sessionSafe = (text) => text.replace(/[^A-Za-z0-9_.:@/+-]/gu, "-"), SESSION_MAX = 128;
|
|
80297
81223
|
var init_fanout_brief = __esm(() => {
|
|
80298
81224
|
init_ledger();
|
|
80299
81225
|
init_fanout();
|
|
@@ -80349,9 +81275,9 @@ function hasOwnCommits2(cwd, branch, start, head) {
|
|
|
80349
81275
|
const onto = /^rebase(?: -i)? \(finish\): \S+ onto ([0-9a-f]+)/u.exec(subject)?.[1];
|
|
80350
81276
|
if (!made && !(onto && !sha.startsWith(onto) && !onto.startsWith(sha)))
|
|
80351
81277
|
continue;
|
|
80352
|
-
if (sha !== head && !
|
|
81278
|
+
if (sha !== head && !isAncestor3(cwd, sha, head))
|
|
80353
81279
|
continue;
|
|
80354
|
-
if (start && (sha === start ||
|
|
81280
|
+
if (start && (sha === start || isAncestor3(cwd, sha, start)))
|
|
80355
81281
|
continue;
|
|
80356
81282
|
return true;
|
|
80357
81283
|
}
|
|
@@ -80361,7 +81287,7 @@ function reverted(cwd, trunk, landedHead, start) {
|
|
|
80361
81287
|
const fork = mergeBase(cwd, landedHead, trunk);
|
|
80362
81288
|
if (fork && fork !== landedHead)
|
|
80363
81289
|
return unchangedSince(cwd, fork, landedHead, trunk);
|
|
80364
|
-
const before = start && start !== landedHead &&
|
|
81290
|
+
const before = start && start !== landedHead && isAncestor3(cwd, start, landedHead) ? start : gitRun4(cwd, ["rev-parse", "--verify", "-q", `${landedHead}^1`]).stdout.trim();
|
|
80365
81291
|
return before ? unchangedSince(cwd, before, landedHead, trunk) : false;
|
|
80366
81292
|
}
|
|
80367
81293
|
function unchangedSince(cwd, before, after, trunk) {
|
|
@@ -80469,9 +81395,9 @@ function detectLanding(ctx, slice, input) {
|
|
|
80469
81395
|
}
|
|
80470
81396
|
return merged ? landed("pr_merged", merged) : { ...out, landed: true, how: "pr_merged" };
|
|
80471
81397
|
}
|
|
80472
|
-
if (!merged || head === merged ||
|
|
81398
|
+
if (!merged || head === merged || isAncestor3(ctx.cwd, head, merged))
|
|
80473
81399
|
return landed("pr_merged", merged ?? head);
|
|
80474
|
-
out.note =
|
|
81400
|
+
out.note = isAncestor3(ctx.cwd, merged, head) ? `${label} merged at ${merged.slice(0, 12)}; ${slice.branch} is ahead of it with newer commits` : `${label} merged at ${merged.slice(0, 12)}; ${slice.branch} has diverged from it (rewritten after the merge)`;
|
|
80475
81401
|
return out;
|
|
80476
81402
|
}
|
|
80477
81403
|
}
|
|
@@ -80488,7 +81414,7 @@ function detectLanding(ctx, slice, input) {
|
|
|
80488
81414
|
if (trunkIds.size === 0)
|
|
80489
81415
|
return out;
|
|
80490
81416
|
const froms = [fork];
|
|
80491
|
-
if (input.parentHead && input.parentHead !== head &&
|
|
81417
|
+
if (input.parentHead && input.parentHead !== head && isAncestor3(ctx.cwd, input.parentHead, head))
|
|
80492
81418
|
froms.push(input.parentHead);
|
|
80493
81419
|
for (const from of froms) {
|
|
80494
81420
|
const id = patchId(ctx.cwd, from, head);
|
|
@@ -80504,7 +81430,7 @@ function sliceStart(cwd, rows, fanout, slice) {
|
|
|
80504
81430
|
const row = rows.findLast((candidate) => candidate.type === "fanout.worktree.created" && candidate.fanout === fanout && candidate.slice === slice.id && typeof candidate.baseSha === "string" && inSliceRun(candidate, slice));
|
|
80505
81431
|
return row?.baseSha ?? null;
|
|
80506
81432
|
}
|
|
80507
|
-
var
|
|
81433
|
+
var isAncestor3 = (cwd, a, b) => gitRun4(cwd, ["merge-base", "--is-ancestor", a, b]).ok, TRUNK_SCAN = 500;
|
|
80508
81434
|
var init_fanout_landed = __esm(() => {
|
|
80509
81435
|
init_rev();
|
|
80510
81436
|
init_fanout();
|
|
@@ -80561,7 +81487,7 @@ function checkFanout(cwd, plan, options = {}) {
|
|
|
80561
81487
|
const b = tips.get(second);
|
|
80562
81488
|
if (landings.get(first)?.landed || landings.get(second)?.landed)
|
|
80563
81489
|
continue;
|
|
80564
|
-
if (!a || !b ||
|
|
81490
|
+
if (!a || !b || isAncestor4(cwd, a, b) || isAncestor4(cwd, b, a))
|
|
80565
81491
|
continue;
|
|
80566
81492
|
const check = checks.get(second);
|
|
80567
81493
|
const theirs = new Map((changedBy.get(first) ?? []).map((file) => [file.toLowerCase(), file]));
|
|
@@ -80741,7 +81667,7 @@ function unblockFor(check, plan) {
|
|
|
80741
81667
|
return `unblock ${check.waitsFor.join(", ")} first`;
|
|
80742
81668
|
return check.reasons[0] ?? "workit fanout check";
|
|
80743
81669
|
}
|
|
80744
|
-
var SAFE = (value) => Boolean(value) && !value?.startsWith("-"),
|
|
81670
|
+
var SAFE = (value) => Boolean(value) && !value?.startsWith("-"), isAncestor4 = (cwd, a, b) => gitRun4(cwd, ["merge-base", "--is-ancestor", a, b]).ok, resolves = (cwd, ref) => gitRun4(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]).ok, blank2 = (slice, head) => ({
|
|
80745
81671
|
id: slice.id,
|
|
80746
81672
|
branch: slice.branch,
|
|
80747
81673
|
head,
|
|
@@ -80960,8 +81886,8 @@ var exports_fanout = {};
|
|
|
80960
81886
|
__export(exports_fanout, {
|
|
80961
81887
|
run: () => run16
|
|
80962
81888
|
});
|
|
80963
|
-
import
|
|
80964
|
-
import
|
|
81889
|
+
import fs27 from "node:fs";
|
|
81890
|
+
import path50 from "node:path";
|
|
80965
81891
|
function forgeFor(io, offline) {
|
|
80966
81892
|
if (offline)
|
|
80967
81893
|
return { forge: null, error: "--offline" };
|
|
@@ -81022,10 +81948,10 @@ function plan2(argv, io) {
|
|
|
81022
81948
|
return usage3(io, "pass --trunk or --track, not both (--track picks the trunk)", PLAN_USAGE2);
|
|
81023
81949
|
if (flags.positionals.length !== 1)
|
|
81024
81950
|
return usage3(io, flags.positionals.length ? `unexpected argument ${flags.positionals[1]}` : "missing <plan.json>", PLAN_USAGE2);
|
|
81025
|
-
const file =
|
|
81951
|
+
const file = path50.resolve(io.cwd, flags.positionals[0]);
|
|
81026
81952
|
let raw;
|
|
81027
81953
|
try {
|
|
81028
|
-
raw = JSON.parse(
|
|
81954
|
+
raw = JSON.parse(fs27.readFileSync(file, "utf8"));
|
|
81029
81955
|
} catch (error) {
|
|
81030
81956
|
return usage3(io, `cannot read ${file} as JSON: ${error.message}`, PLAN_USAGE2);
|
|
81031
81957
|
}
|
|
@@ -96510,7 +97436,7 @@ var init_ast = __esm(() => {
|
|
|
96510
97436
|
|
|
96511
97437
|
// packages/workit-cli/src/test-audit/rules.ts
|
|
96512
97438
|
import { builtinModules } from "node:module";
|
|
96513
|
-
import
|
|
97439
|
+
import path51 from "node:path";
|
|
96514
97440
|
function testCallee(call) {
|
|
96515
97441
|
let callee = call.callee;
|
|
96516
97442
|
if (callee.type === "CallExpression")
|
|
@@ -97003,12 +97929,12 @@ function checkConstantsOnly(ctx, emit) {
|
|
|
97003
97929
|
}
|
|
97004
97930
|
function checkOverMocking(ctx, emit) {
|
|
97005
97931
|
const { program } = ctx.parsed;
|
|
97006
|
-
const unit =
|
|
97932
|
+
const unit = path51.basename(ctx.file).replace(/\.(?:[cm]?[jt]sx?)$/, "").replace(/\.(?:test|spec)$/, "");
|
|
97007
97933
|
const mocked = find2(program, (node) => node.type === "CallExpression" && MODULE_MOCK.test(calleeName(node.callee) ?? "") && node.arguments[0]?.type === "StringLiteral");
|
|
97008
97934
|
const internal = mocked.filter((call) => /^(?:\.|@\/|~\/)/.test(call.arguments[0].value));
|
|
97009
97935
|
for (const call of mocked) {
|
|
97010
97936
|
const spec = call.arguments[0].value;
|
|
97011
|
-
const base =
|
|
97937
|
+
const base = path51.basename(spec.replace(/\/index(?:\.[cm]?[jt]sx?)?$/, "")).replace(/\.(?:[cm]?[jt]sx?)$/, "");
|
|
97012
97938
|
if (base === unit)
|
|
97013
97939
|
emit({
|
|
97014
97940
|
rule: "over-mocking",
|
|
@@ -97398,7 +98324,7 @@ var init_rules = __esm(() => {
|
|
|
97398
98324
|
// packages/workit-cli/src/test-audit/audit.ts
|
|
97399
98325
|
import { spawnSync as spawnSync26 } from "node:child_process";
|
|
97400
98326
|
import { existsSync as existsSync20, readdirSync as readdirSync9, readFileSync as readFileSync23, statSync as statSync8 } from "node:fs";
|
|
97401
|
-
import
|
|
98327
|
+
import path52 from "node:path";
|
|
97402
98328
|
function git7(cwd, args) {
|
|
97403
98329
|
const run = spawnSync26("git", ["-c", "core.quotepath=off", ...args], {
|
|
97404
98330
|
cwd,
|
|
@@ -97411,7 +98337,7 @@ function walkTests(dir, out) {
|
|
|
97411
98337
|
for (const entry of readdirSync9(dir, { withFileTypes: true })) {
|
|
97412
98338
|
if (entry.name.startsWith(".") && entry.name !== "." && entry.isDirectory())
|
|
97413
98339
|
continue;
|
|
97414
|
-
const full =
|
|
98340
|
+
const full = path52.join(dir, entry.name);
|
|
97415
98341
|
if (entry.isDirectory()) {
|
|
97416
98342
|
if (!SKIP_DIRS.has(entry.name))
|
|
97417
98343
|
walkTests(full, out);
|
|
@@ -97423,7 +98349,7 @@ function expandPaths(cwd, inputs) {
|
|
|
97423
98349
|
const files = [];
|
|
97424
98350
|
const missing = [];
|
|
97425
98351
|
for (const input of inputs.length > 0 ? inputs : ["."]) {
|
|
97426
|
-
const full =
|
|
98352
|
+
const full = path52.resolve(cwd, input);
|
|
97427
98353
|
if (!existsSync20(full))
|
|
97428
98354
|
missing.push(input);
|
|
97429
98355
|
else if (statSync8(full).isDirectory())
|
|
@@ -97460,11 +98386,11 @@ function changedLines(cwd, base) {
|
|
|
97460
98386
|
for (let offset = 0;offset < count; offset += 1)
|
|
97461
98387
|
lines.add(start + offset);
|
|
97462
98388
|
}
|
|
97463
|
-
changed.set(
|
|
98389
|
+
changed.set(path52.join(root, file), lines);
|
|
97464
98390
|
}
|
|
97465
98391
|
const untracked = git7(root, ["ls-files", "--others", "--exclude-standard", "-z"]);
|
|
97466
98392
|
for (const file of untracked.stdout.split("\x00").filter(Boolean))
|
|
97467
|
-
changed.set(
|
|
98393
|
+
changed.set(path52.join(root, file), "all");
|
|
97468
98394
|
return changed;
|
|
97469
98395
|
}
|
|
97470
98396
|
function auditFiles(files, display) {
|
|
@@ -97526,7 +98452,7 @@ function auditFiles(files, display) {
|
|
|
97526
98452
|
durationMs: Math.round(performance.now() - started)
|
|
97527
98453
|
};
|
|
97528
98454
|
}
|
|
97529
|
-
var TEST_FILE, CODE_FILE, SKIP_DIRS, toPosix = (file) => file.split(
|
|
98455
|
+
var TEST_FILE, CODE_FILE, SKIP_DIRS, toPosix = (file) => file.split(path52.sep).join("/"), SEVERITY_ORDER, ruleIds;
|
|
97530
98456
|
var init_audit = __esm(() => {
|
|
97531
98457
|
init_ast();
|
|
97532
98458
|
init_rules();
|
|
@@ -97555,8 +98481,8 @@ import {
|
|
|
97555
98481
|
writeFileSync as writeFileSync11,
|
|
97556
98482
|
copyFileSync as copyFileSync6
|
|
97557
98483
|
} from "node:fs";
|
|
97558
|
-
import
|
|
97559
|
-
import
|
|
98484
|
+
import os16 from "node:os";
|
|
98485
|
+
import path53 from "node:path";
|
|
97560
98486
|
function mutantsFor(source, file, lines) {
|
|
97561
98487
|
const parsed = parseSource(source, file);
|
|
97562
98488
|
const out = [];
|
|
@@ -97629,7 +98555,7 @@ function relatedTests(source, testFiles) {
|
|
|
97629
98555
|
for (const match of text.matchAll(SPECIFIER)) {
|
|
97630
98556
|
const spec = match[1];
|
|
97631
98557
|
if (spec.startsWith(".")) {
|
|
97632
|
-
if (stripExt(
|
|
98558
|
+
if (stripExt(path53.posix.join(path53.posix.dirname(file), spec)) === target)
|
|
97633
98559
|
return true;
|
|
97634
98560
|
} else if (spec.includes("/") && stripExt(spec).split("/").slice(-2).join("/") === tail)
|
|
97635
98561
|
return true;
|
|
@@ -97637,7 +98563,7 @@ function relatedTests(source, testFiles) {
|
|
|
97637
98563
|
return false;
|
|
97638
98564
|
}).map((entry) => entry.file);
|
|
97639
98565
|
}
|
|
97640
|
-
function removeStaleCopies(tmp =
|
|
98566
|
+
function removeStaleCopies(tmp = os16.tmpdir()) {
|
|
97641
98567
|
let removed = 0;
|
|
97642
98568
|
let names = [];
|
|
97643
98569
|
try {
|
|
@@ -97649,12 +98575,12 @@ function removeStaleCopies(tmp = os14.tmpdir()) {
|
|
|
97649
98575
|
for (const name of names) {
|
|
97650
98576
|
if (!name.startsWith(TEMP_PREFIX))
|
|
97651
98577
|
continue;
|
|
97652
|
-
const dir =
|
|
98578
|
+
const dir = path53.join(tmp, name);
|
|
97653
98579
|
try {
|
|
97654
98580
|
const stat = lstatSync8(dir);
|
|
97655
98581
|
if (!stat.isDirectory() || uid !== undefined && stat.uid !== uid)
|
|
97656
98582
|
continue;
|
|
97657
|
-
const pid = Number(readFileSync24(
|
|
98583
|
+
const pid = Number(readFileSync24(path53.join(dir, PID_FILE), "utf8"));
|
|
97658
98584
|
if (Number.isInteger(pid) && pid > 0 && alive(pid))
|
|
97659
98585
|
continue;
|
|
97660
98586
|
} catch (error) {
|
|
@@ -97671,8 +98597,8 @@ function removeStaleCopies(tmp = os14.tmpdir()) {
|
|
|
97671
98597
|
function linkNodeModules(origin, target, repo, copy) {
|
|
97672
98598
|
mkdirSync10(target, { recursive: true });
|
|
97673
98599
|
for (const entry of readdirSync10(origin, { withFileTypes: true })) {
|
|
97674
|
-
const from =
|
|
97675
|
-
const to =
|
|
98600
|
+
const from = path53.join(origin, entry.name);
|
|
98601
|
+
const to = path53.join(target, entry.name);
|
|
97676
98602
|
if (entry.name.startsWith("@") && entry.isDirectory()) {
|
|
97677
98603
|
linkNodeModules(from, to, repo, copy);
|
|
97678
98604
|
continue;
|
|
@@ -97688,9 +98614,9 @@ function linkNodeModules(origin, target, repo, copy) {
|
|
|
97688
98614
|
} catch {
|
|
97689
98615
|
continue;
|
|
97690
98616
|
}
|
|
97691
|
-
const relative =
|
|
97692
|
-
if (!relative.startsWith("..") && !
|
|
97693
|
-
symlinkSync(
|
|
98617
|
+
const relative = path53.relative(repo, real);
|
|
98618
|
+
if (!relative.startsWith("..") && !path53.isAbsolute(relative) && !relative.split(path53.sep).includes("node_modules")) {
|
|
98619
|
+
symlinkSync(path53.join(copy, relative), to, "junction");
|
|
97694
98620
|
continue;
|
|
97695
98621
|
}
|
|
97696
98622
|
}
|
|
@@ -97706,7 +98632,7 @@ function copySymlink(repo, tree, from, to) {
|
|
|
97706
98632
|
}
|
|
97707
98633
|
const realRepo = realpathSync9(repo);
|
|
97708
98634
|
if (real && inside4(realRepo, real)) {
|
|
97709
|
-
symlinkSync(
|
|
98635
|
+
symlinkSync(path53.relative(path53.dirname(to), path53.join(tree, path53.relative(realRepo, real))), to);
|
|
97710
98636
|
return;
|
|
97711
98637
|
}
|
|
97712
98638
|
if (real && statSync9(real).isFile())
|
|
@@ -97715,10 +98641,10 @@ function copySymlink(repo, tree, from, to) {
|
|
|
97715
98641
|
symlinkSync(readlinkSync3(from), to);
|
|
97716
98642
|
}
|
|
97717
98643
|
function writeInside(copy, file, content) {
|
|
97718
|
-
const target =
|
|
98644
|
+
const target = path53.join(copy, file);
|
|
97719
98645
|
try {
|
|
97720
98646
|
const stat = lstatSync8(target);
|
|
97721
|
-
if (!stat.isFile() || !inside4(realpathSync9(copy), realpathSync9(
|
|
98647
|
+
if (!stat.isFile() || !inside4(realpathSync9(copy), realpathSync9(path53.dirname(target))))
|
|
97722
98648
|
return false;
|
|
97723
98649
|
} catch {
|
|
97724
98650
|
return false;
|
|
@@ -97729,9 +98655,9 @@ function writeInside(copy, file, content) {
|
|
|
97729
98655
|
return true;
|
|
97730
98656
|
}
|
|
97731
98657
|
function copyWorkingTree(repo) {
|
|
97732
|
-
const dir = mkdtempSync4(
|
|
97733
|
-
writeFileSync11(
|
|
97734
|
-
const tree =
|
|
98658
|
+
const dir = mkdtempSync4(path53.join(os16.tmpdir(), TEMP_PREFIX));
|
|
98659
|
+
writeFileSync11(path53.join(dir, PID_FILE), String(process.pid));
|
|
98660
|
+
const tree = path53.join(dir, "tree");
|
|
97735
98661
|
const head = git7(repo, ["rev-parse", "--verify", "--quiet", "HEAD"]).stdout.trim();
|
|
97736
98662
|
if (head && git7(dir, ["clone", "-q", "--shared", "--no-checkout", repo, tree]).ok) {
|
|
97737
98663
|
const branch = git7(repo, ["symbolic-ref", "--quiet", "--short", "HEAD"]).stdout.trim();
|
|
@@ -97748,29 +98674,29 @@ function copyWorkingTree(repo) {
|
|
|
97748
98674
|
for (const relative of listed) {
|
|
97749
98675
|
if (!relative)
|
|
97750
98676
|
continue;
|
|
97751
|
-
const from =
|
|
98677
|
+
const from = path53.join(repo, relative);
|
|
97752
98678
|
let stat;
|
|
97753
98679
|
try {
|
|
97754
98680
|
stat = lstatSync8(from);
|
|
97755
98681
|
} catch {
|
|
97756
98682
|
continue;
|
|
97757
98683
|
}
|
|
97758
|
-
const to =
|
|
97759
|
-
mkdirSync10(
|
|
98684
|
+
const to = path53.join(tree, relative);
|
|
98685
|
+
mkdirSync10(path53.dirname(to), { recursive: true });
|
|
97760
98686
|
if (stat.isSymbolicLink())
|
|
97761
98687
|
copySymlink(repo, tree, from, to);
|
|
97762
98688
|
else if (stat.isFile())
|
|
97763
98689
|
copyFileSync6(from, to);
|
|
97764
|
-
let parent =
|
|
98690
|
+
let parent = path53.dirname(relative);
|
|
97765
98691
|
while (parent !== "." && !dirs.has(parent)) {
|
|
97766
98692
|
dirs.add(parent);
|
|
97767
|
-
parent =
|
|
98693
|
+
parent = path53.dirname(parent);
|
|
97768
98694
|
}
|
|
97769
98695
|
}
|
|
97770
98696
|
for (const parent of dirs) {
|
|
97771
|
-
const modules =
|
|
97772
|
-
if (existsSync21(modules) && !existsSync21(
|
|
97773
|
-
linkNodeModules(modules,
|
|
98697
|
+
const modules = path53.join(repo, parent, "node_modules");
|
|
98698
|
+
if (existsSync21(modules) && !existsSync21(path53.join(tree, parent, "node_modules")))
|
|
98699
|
+
linkNodeModules(modules, path53.join(tree, parent, "node_modules"), repo, tree);
|
|
97774
98700
|
}
|
|
97775
98701
|
return { dir, tree };
|
|
97776
98702
|
}
|
|
@@ -97809,14 +98735,14 @@ async function runMutation(options) {
|
|
|
97809
98735
|
const tracked = git7(repo, ["ls-files", "-co", "--exclude-standard", "-z"]).stdout.split("\x00").filter(Boolean);
|
|
97810
98736
|
const testFiles = tracked.filter((file) => TEST_FILE.test(file)).map((file) => {
|
|
97811
98737
|
try {
|
|
97812
|
-
return { file, text: readFileSync24(
|
|
98738
|
+
return { file, text: readFileSync24(path53.join(repo, file), "utf8") };
|
|
97813
98739
|
} catch {
|
|
97814
98740
|
return { file, text: "" };
|
|
97815
98741
|
}
|
|
97816
98742
|
});
|
|
97817
98743
|
const candidates = [];
|
|
97818
98744
|
for (const [absolute, lines] of [...options.changed].toSorted(([a], [b]) => a.localeCompare(b))) {
|
|
97819
|
-
const relative = toPosix(
|
|
98745
|
+
const relative = toPosix(path53.relative(repo, absolute));
|
|
97820
98746
|
if (!CODE_FILE.test(relative) || TEST_FILE.test(relative) || relative.endsWith(".d.ts"))
|
|
97821
98747
|
continue;
|
|
97822
98748
|
if (NOT_SOURCE.test(relative) || !existsSync21(absolute))
|
|
@@ -97830,9 +98756,9 @@ async function runMutation(options) {
|
|
|
97830
98756
|
for (const mutant of mutants)
|
|
97831
98757
|
if (!related.has(mutant.file))
|
|
97832
98758
|
related.set(mutant.file, relatedTests(mutant.file, testFiles));
|
|
97833
|
-
const cwdRelative =
|
|
98759
|
+
const cwdRelative = path53.relative(repo, options.cwd);
|
|
97834
98760
|
const asArg = (file) => {
|
|
97835
|
-
const relative = toPosix(
|
|
98761
|
+
const relative = toPosix(path53.relative(cwdRelative, file));
|
|
97836
98762
|
return shellQuote(relative.startsWith(".") ? relative : `./${relative}`);
|
|
97837
98763
|
};
|
|
97838
98764
|
const commandFor = (tests) => options.command.replaceAll("{files}", () => tests.map(asArg).join(" "));
|
|
@@ -97867,7 +98793,7 @@ async function runMutation(options) {
|
|
|
97867
98793
|
return finish2(options, 0, results, counts, started);
|
|
97868
98794
|
removeStaleCopies();
|
|
97869
98795
|
const { dir: copyDir, tree: copy } = copyWorkingTree(repo);
|
|
97870
|
-
const copyCwd =
|
|
98796
|
+
const copyCwd = path53.join(copy, cwdRelative);
|
|
97871
98797
|
let current = null;
|
|
97872
98798
|
const restore = () => {
|
|
97873
98799
|
if (current)
|
|
@@ -97902,7 +98828,7 @@ async function runMutation(options) {
|
|
|
97902
98828
|
record({ ...mutant, status: "skipped", durationMs: 0, tests, reason: "budget" });
|
|
97903
98829
|
continue;
|
|
97904
98830
|
}
|
|
97905
|
-
const original = readFileSync24(
|
|
98831
|
+
const original = readFileSync24(path53.join(copy, mutant.file), "utf8");
|
|
97906
98832
|
const mutated = original.slice(0, mutant.start) + mutant.replacement + original.slice(mutant.end);
|
|
97907
98833
|
current = { file: mutant.file, original };
|
|
97908
98834
|
if (!writeInside(copy, mutant.file, mutated)) {
|
|
@@ -97959,8 +98885,8 @@ var NOT_SOURCE, FLIPS, isStringy = (node) => node.type === "StringLiteral" || no
|
|
|
97959
98885
|
return error.code === "EPERM";
|
|
97960
98886
|
}
|
|
97961
98887
|
}, inside4 = (root, file) => {
|
|
97962
|
-
const relative =
|
|
97963
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
98888
|
+
const relative = path53.relative(root, file);
|
|
98889
|
+
return relative === "" || !relative.startsWith("..") && !path53.isAbsolute(relative);
|
|
97964
98890
|
}, active, killTree = (child) => {
|
|
97965
98891
|
try {
|
|
97966
98892
|
if (child.pid && process.platform !== "win32")
|
|
@@ -98009,7 +98935,7 @@ __export(exports_test_audit, {
|
|
|
98009
98935
|
run: () => run18
|
|
98010
98936
|
});
|
|
98011
98937
|
import { existsSync as existsSync22 } from "node:fs";
|
|
98012
|
-
import
|
|
98938
|
+
import path54 from "node:path";
|
|
98013
98939
|
function parse7(argv) {
|
|
98014
98940
|
const options = {
|
|
98015
98941
|
paths: [],
|
|
@@ -98113,8 +99039,8 @@ async function run18(argv, io) {
|
|
|
98113
99039
|
return emit(io, fail("invalid_input", `${options}
|
|
98114
99040
|
usage: ${USAGE10}`));
|
|
98115
99041
|
const top = git7(io.cwd, ["rev-parse", "--show-toplevel"]);
|
|
98116
|
-
const repo = top.ok ?
|
|
98117
|
-
const display = (file) => toPosix(
|
|
99042
|
+
const repo = top.ok ? path54.resolve(top.stdout.trim()) : null;
|
|
99043
|
+
const display = (file) => toPosix(path54.relative(io.cwd, file) || file);
|
|
98118
99044
|
let base = null;
|
|
98119
99045
|
let changed = null;
|
|
98120
99046
|
if (options.diff || options.mutate) {
|
|
@@ -98357,7 +99283,7 @@ __export(exports_youtrack, {
|
|
|
98357
99283
|
run: () => run20
|
|
98358
99284
|
});
|
|
98359
99285
|
import { readFileSync as readFileSync25 } from "node:fs";
|
|
98360
|
-
import
|
|
99286
|
+
import path55 from "node:path";
|
|
98361
99287
|
async function run20(argv, io) {
|
|
98362
99288
|
const [sub, ...rest] = argv;
|
|
98363
99289
|
if (sub !== "note" && sub !== "time" && sub !== "meeting")
|
|
@@ -98403,7 +99329,7 @@ async function run20(argv, io) {
|
|
|
98403
99329
|
let markdown = flags.values.markdown ?? "";
|
|
98404
99330
|
if (flags.values.file)
|
|
98405
99331
|
try {
|
|
98406
|
-
markdown = readFileSync25(
|
|
99332
|
+
markdown = readFileSync25(path55.resolve(io.cwd, flags.values.file), "utf8");
|
|
98407
99333
|
} catch (error) {
|
|
98408
99334
|
return usage3(io, `cannot read ${flags.values.file}: ${String(error)}`, USAGE12);
|
|
98409
99335
|
}
|
|
@@ -98472,7 +99398,7 @@ __export(exports_changelog, {
|
|
|
98472
99398
|
run: () => run21
|
|
98473
99399
|
});
|
|
98474
99400
|
import { readFileSync as readFileSync26 } from "node:fs";
|
|
98475
|
-
import
|
|
99401
|
+
import path56 from "node:path";
|
|
98476
99402
|
async function run21(argv, io) {
|
|
98477
99403
|
const [sub, ...rest] = argv;
|
|
98478
99404
|
if (sub !== "apply")
|
|
@@ -98518,7 +99444,7 @@ async function run21(argv, io) {
|
|
|
98518
99444
|
]);
|
|
98519
99445
|
}
|
|
98520
99446
|
var USAGE13 = "workit changelog apply (--entries <JSON|@file|-> | --normalize-only) [--path CHANGELOG.md] [--preview]", readEntries = (raw, cwd) => {
|
|
98521
|
-
const text = raw === "-" ? readFileSync26(0, "utf8") : raw.startsWith("@") ? readFileSync26(
|
|
99447
|
+
const text = raw === "-" ? readFileSync26(0, "utf8") : raw.startsWith("@") ? readFileSync26(path56.resolve(cwd, raw.slice(1)), "utf8") : raw;
|
|
98522
99448
|
return JSON.parse(text);
|
|
98523
99449
|
};
|
|
98524
99450
|
var init_changelog2 = __esm(() => {
|
|
@@ -98614,7 +99540,7 @@ var init_handoff = __esm(() => {
|
|
|
98614
99540
|
|
|
98615
99541
|
// packages/workit-cli/src/main.ts
|
|
98616
99542
|
init_package();
|
|
98617
|
-
import
|
|
99543
|
+
import path57 from "node:path";
|
|
98618
99544
|
|
|
98619
99545
|
// packages/workit-cli/src/help.ts
|
|
98620
99546
|
var HELP_FLAGS = new Set(["--help", "-h"]);
|
|
@@ -98879,7 +99805,7 @@ var VERBS = [
|
|
|
98879
99805
|
{
|
|
98880
99806
|
name: "check",
|
|
98881
99807
|
group: "delivery",
|
|
98882
|
-
usage: "workit check <name> [--task <id>] [--base <ref>] | workit check [--name <n>] [--shell] [--timeout <s>] [--task <id>] [--base <ref>] -- <cmd…>",
|
|
99808
|
+
usage: "workit check <name> [--task <id>] [--base <ref>] [--no-lease] | workit check [--name <n>] [--shell] [--timeout <s>] [--task <id>] [--base <ref>] [--heavy] [--no-lease] -- <cmd…>",
|
|
98883
99809
|
summary: "Run a configured or ad-hoc check and record CLI-observed evidence (exit = the command's)",
|
|
98884
99810
|
examples: ["workit check test", "workit check --name unit -- bun test test/unit"],
|
|
98885
99811
|
load: () => Promise.resolve().then(() => (init_check(), exports_check))
|
|
@@ -98921,8 +99847,8 @@ var VERBS = [
|
|
|
98921
99847
|
},
|
|
98922
99848
|
{
|
|
98923
99849
|
name: "merge",
|
|
98924
|
-
usage: "workit pr merge [--pr <n>] [--method squash|merge|rebase] [--delete-branch] [--unverified --reason <why>]",
|
|
98925
|
-
summary: "Merge when READY, verified by a non-author and granted"
|
|
99850
|
+
usage: "workit pr merge [--pr <n>] [--method squash|merge|rebase] [--subject <s>] [--body <b> | --body-file <f|->] [--delete-branch] [--unverified --reason <why>]",
|
|
99851
|
+
summary: "Merge when READY, verified by a non-author and granted; --subject/--body set the squash message"
|
|
98926
99852
|
}
|
|
98927
99853
|
],
|
|
98928
99854
|
examples: [
|
|
@@ -98962,18 +99888,24 @@ var VERBS = [
|
|
|
98962
99888
|
},
|
|
98963
99889
|
{
|
|
98964
99890
|
name: "commit",
|
|
98965
|
-
usage: "workit git commit -m|--message <msg>… | -F|--file <file|-> [--amend [--no-edit]] [--allow-empty] [-a|--all | [--] <paths…>]",
|
|
98966
|
-
summary: "Commit (or --amend) with a linted Conventional Commit message
|
|
99891
|
+
usage: "workit git commit -m|--message <msg>… | -F|--file <file|-> [--trailer <key: value>]… [--amend [--no-edit]] [--allow-empty] [-a|--all | [--] <paths…>]",
|
|
99892
|
+
summary: "Commit (or --amend) with a linted Conventional Commit message, the session trailer (unless the workspace sets trailers: false) and any --trailer"
|
|
98967
99893
|
},
|
|
98968
99894
|
{
|
|
98969
99895
|
name: "push",
|
|
98970
99896
|
usage: "workit git push [-u|--set-upstream] [--force-with-lease [--expect <sha>] [--overwrite-unintegrated]] [--release <main|develop|v-tag>…]",
|
|
98971
99897
|
summary: "Push the current branch and verify the remote tip (never a blind --force); --release finishes a git-flow release"
|
|
99898
|
+
},
|
|
99899
|
+
{
|
|
99900
|
+
name: "trailers",
|
|
99901
|
+
usage: "workit git trailers",
|
|
99902
|
+
summary: "Print on or off: whether commits in this workspace get the Workit-Session trailer"
|
|
98972
99903
|
}
|
|
98973
99904
|
],
|
|
98974
99905
|
examples: [
|
|
98975
99906
|
"workit git branch --kind feature --slug help-everywhere",
|
|
98976
99907
|
'workit git commit -m "fix(cli): answer help flags first" --all',
|
|
99908
|
+
'workit git commit -m "feat: x" --trailer "Co-Authored-By: Name <email>" --all',
|
|
98977
99909
|
"workit git commit --amend --no-edit -- src/forgotten.ts",
|
|
98978
99910
|
"workit git push --set-upstream"
|
|
98979
99911
|
],
|
|
@@ -99260,7 +100192,7 @@ var defaultIo = (json) => ({
|
|
|
99260
100192
|
});
|
|
99261
100193
|
async function main(argv, overrides = {}, options = {}) {
|
|
99262
100194
|
const parsed = parseGlobals(argv);
|
|
99263
|
-
const cwd =
|
|
100195
|
+
const cwd = path57.resolve(overrides.cwd ?? process.cwd(), parsed.cwd ?? ".");
|
|
99264
100196
|
const io = { ...defaultIo(parsed.json), ...overrides, json: parsed.json, cwd };
|
|
99265
100197
|
if (parsed.error)
|
|
99266
100198
|
return emit(io, fail("invalid_input", parsed.error));
|