lobstah 0.6.9 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -1
- package/dist/main.js +7657 -5262
- package/dist/runner.js +1765 -1522
- package/docs/configuration.md +3 -1
- package/docs/man.md +163 -48
- package/docs/vocabulary.md +20 -10
- package/package.json +1 -1
package/dist/runner.js
CHANGED
|
@@ -9,8 +9,8 @@ var WAITING_ON = ["review", "pr", "deploy", "person", "external"];
|
|
|
9
9
|
var WAITING_ON_VERBS = ["paused", "needs-decision", "blocked"];
|
|
10
10
|
|
|
11
11
|
// packages/core/dist/stats.js
|
|
12
|
-
import * as
|
|
13
|
-
import * as
|
|
12
|
+
import * as fs12 from "node:fs";
|
|
13
|
+
import * as path10 from "node:path";
|
|
14
14
|
|
|
15
15
|
// packages/core/dist/paths.js
|
|
16
16
|
import * as os from "node:os";
|
|
@@ -58,1472 +58,1481 @@ function evidencePath(id, lane) {
|
|
|
58
58
|
return path.join(laneDirs(lane).state, `${id}.evidence`);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
// packages/core/dist/
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
// packages/core/dist/worker-metadata.js
|
|
62
|
+
var WORKER_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"];
|
|
63
|
+
var WORKER_PERMISSIONS = ["default", "acceptEdits", "plan", "dontAsk", "bypassPermissions", "auto"];
|
|
64
|
+
function localModel(value) {
|
|
65
|
+
return typeof value === "string" && /^[a-zA-Z0-9][a-zA-Z0-9._:/-]{0,127}$/.test(value) ? value : null;
|
|
64
66
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
67
|
+
function choice(value, values) {
|
|
68
|
+
return typeof value === "string" && values.includes(value) ? value : null;
|
|
68
69
|
}
|
|
69
|
-
function
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
last = JSON.parse(line);
|
|
77
|
-
} catch {
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
} catch {
|
|
81
|
-
return { done: false };
|
|
82
|
-
}
|
|
83
|
-
if (last?.verb !== "done")
|
|
84
|
-
return { done: false };
|
|
85
|
-
let trap;
|
|
86
|
-
try {
|
|
87
|
-
const to = JSON.parse(fs2.readFileSync(path2.join(stateDir, `${id}.evidence`), "utf8")).deliveredTo;
|
|
88
|
-
if (typeof to === "string" && to.startsWith("wt:"))
|
|
89
|
-
trap = to;
|
|
90
|
-
} catch {
|
|
91
|
-
}
|
|
92
|
-
return { done: true, ...typeof last.at === "string" ? { at: last.at } : {}, ...trap ? { trap } : {} };
|
|
70
|
+
function workerMetadata(input = {}) {
|
|
71
|
+
return {
|
|
72
|
+
harness: localModel(input.harness),
|
|
73
|
+
model: localModel(input.model),
|
|
74
|
+
config: { effort: choice(input.config?.effort, WORKER_EFFORTS), permissionMode: choice(input.config?.permissionMode, WORKER_PERMISSIONS) },
|
|
75
|
+
...typeof input.observedAt === "string" && /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{3}Z$/.test(input.observedAt) && Number.isFinite(Date.parse(input.observedAt)) ? { observedAt: input.observedAt } : {}
|
|
76
|
+
};
|
|
93
77
|
}
|
|
94
|
-
|
|
95
|
-
|
|
78
|
+
|
|
79
|
+
// packages/core/dist/worker-profile.js
|
|
80
|
+
var WORKER_HARNESSES = ["claude", "codex", "other"];
|
|
81
|
+
var WORKER_MODEL_RE = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
82
|
+
var WORKER_MODELS = [
|
|
83
|
+
"other",
|
|
84
|
+
"opus",
|
|
85
|
+
"sonnet",
|
|
86
|
+
"haiku",
|
|
87
|
+
"fable",
|
|
88
|
+
"opusplan",
|
|
89
|
+
"claude-opus-4",
|
|
90
|
+
"claude-opus-4-1",
|
|
91
|
+
"claude-opus-4-5",
|
|
92
|
+
"claude-opus-4-6",
|
|
93
|
+
"claude-opus-5",
|
|
94
|
+
"claude-opus-5-5",
|
|
95
|
+
"claude-sonnet-4",
|
|
96
|
+
"claude-sonnet-4-5",
|
|
97
|
+
"claude-sonnet-4-6",
|
|
98
|
+
"claude-sonnet-5",
|
|
99
|
+
"claude-haiku-4-5",
|
|
100
|
+
"claude-opus-4-20250514",
|
|
101
|
+
"claude-sonnet-4-20250514",
|
|
102
|
+
"claude-sonnet-4-5-20250929",
|
|
103
|
+
"claude-haiku-4-5-20251001",
|
|
104
|
+
"claude-opus-4-1-20250805",
|
|
105
|
+
"claude-opus-4-5-20251101",
|
|
106
|
+
"gpt-5",
|
|
107
|
+
"gpt-5-codex",
|
|
108
|
+
"gpt-5.1",
|
|
109
|
+
"gpt-5.1-codex",
|
|
110
|
+
"gpt-5.1-codex-mini",
|
|
111
|
+
"gpt-5.1-codex-max",
|
|
112
|
+
"gpt-5.2",
|
|
113
|
+
"gpt-5.2-codex",
|
|
114
|
+
"gpt-5.3-codex",
|
|
115
|
+
"gpt-5.4",
|
|
116
|
+
"gpt-5.4-mini",
|
|
117
|
+
"gpt-5.5",
|
|
118
|
+
"gpt-5.6-sol",
|
|
119
|
+
"gpt-5.6-terra",
|
|
120
|
+
"gpt-5.6-luna",
|
|
121
|
+
"gpt-6",
|
|
122
|
+
"gpt-6-sol",
|
|
123
|
+
"gpt-6-astra",
|
|
124
|
+
"gpt-6-luna",
|
|
125
|
+
"gpt-6.1-sol",
|
|
126
|
+
"codex-mini-latest",
|
|
127
|
+
"o1",
|
|
128
|
+
"o3",
|
|
129
|
+
"o3-mini",
|
|
130
|
+
"o4-mini"
|
|
131
|
+
];
|
|
132
|
+
function choice2(value, choices) {
|
|
133
|
+
return typeof value === "string" && choices.includes(value) ? value : null;
|
|
96
134
|
}
|
|
97
|
-
function
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
135
|
+
function safeModel(value) {
|
|
136
|
+
if (value === void 0 || value === null || value === "")
|
|
137
|
+
return null;
|
|
138
|
+
return typeof value === "string" && WORKER_MODEL_RE.test(value) && WORKER_MODELS.includes(value) ? value : "other";
|
|
139
|
+
}
|
|
140
|
+
function workerProfile(input = {}) {
|
|
141
|
+
return {
|
|
142
|
+
harness: input.harness === void 0 || input.harness === null ? null : choice2(input.harness, WORKER_HARNESSES) ?? "other",
|
|
143
|
+
model: safeModel(input.model),
|
|
144
|
+
config: { effort: choice2(input.effort, WORKER_EFFORTS), permissionMode: choice2(input.permissionMode, WORKER_PERMISSIONS) }
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function sanitizeWorker(value) {
|
|
148
|
+
return workerProfile({ harness: value?.harness, model: value?.model, effort: value?.config?.effort, permissionMode: value?.config?.permissionMode });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// packages/core/dist/soak.js
|
|
152
|
+
import * as fs11 from "node:fs";
|
|
153
|
+
import * as path9 from "node:path";
|
|
154
|
+
|
|
155
|
+
// packages/core/dist/queue.js
|
|
156
|
+
import * as fs4 from "node:fs";
|
|
157
|
+
import * as path3 from "node:path";
|
|
158
|
+
|
|
159
|
+
// packages/core/dist/status.js
|
|
160
|
+
import * as fs3 from "node:fs";
|
|
161
|
+
|
|
162
|
+
// packages/core/dist/activity.js
|
|
163
|
+
import * as fs2 from "node:fs";
|
|
164
|
+
import * as path2 from "node:path";
|
|
165
|
+
var ACTIVITY_KINDS = ["tool", "message", "thinking", "waiting"];
|
|
166
|
+
var ACTIVITY_SUMMARY_MAX = 80;
|
|
167
|
+
var SECRET_PATTERNS = [
|
|
168
|
+
/\b(?:sk|pk|rk)[-_][A-Za-z0-9_-]{8,}/i,
|
|
169
|
+
/\b(?:gh[posur]_|github_pat_|glpat-|xox[abprs]-|npm_|hf_)[A-Za-z0-9_-]{16,}/,
|
|
170
|
+
/\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/,
|
|
171
|
+
/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{4,}/,
|
|
172
|
+
/\bbearer\b/i,
|
|
173
|
+
/(?:token|secret|passw(?:or)?d|pwd|api[-_]?key|auth|credential|private[-_]?key)[^\s]*[=:]/i
|
|
174
|
+
];
|
|
175
|
+
function looksSecret(word) {
|
|
176
|
+
if (SECRET_PATTERNS.some((re) => re.test(word)))
|
|
177
|
+
return true;
|
|
178
|
+
for (const run2 of word.match(/[A-Za-z0-9+/_=-]{24,}/g) ?? []) {
|
|
179
|
+
if (/[0-9]/.test(run2) && /[A-Za-z]/.test(run2))
|
|
180
|
+
return true;
|
|
105
181
|
}
|
|
106
|
-
|
|
107
|
-
if ((at ? localDay(at) : today) === today)
|
|
108
|
-
store.catchesToday++;
|
|
182
|
+
return false;
|
|
109
183
|
}
|
|
110
|
-
function
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
return [...live, ...backups];
|
|
184
|
+
function redactSummary(s, max = ACTIVITY_SUMMARY_MAX) {
|
|
185
|
+
const clean = s.replace(/[\r\n\t]+/g, " ").split(" ").filter(Boolean).map((w) => looksSecret(w) ? "[redacted]" : w).join(" ");
|
|
186
|
+
return clean.length > max ? `${clean.slice(0, max - 1)}\u2026` : clean;
|
|
114
187
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
continue;
|
|
125
|
-
seen.add(id);
|
|
126
|
-
const facts = readCatchFacts(dir, id);
|
|
127
|
-
if (!facts.done)
|
|
128
|
-
continue;
|
|
129
|
-
add(store, facts, now2);
|
|
130
|
-
if (live)
|
|
131
|
-
store.counted.push(id);
|
|
132
|
-
}
|
|
188
|
+
var SHELLS = /* @__PURE__ */ new Set(["bash", "sh", "zsh", "dash", "fish", "pwsh", "powershell", "cmd"]);
|
|
189
|
+
function commandWord(command) {
|
|
190
|
+
let words2 = command.trim().split(/\s+/).filter(Boolean);
|
|
191
|
+
while (words2.length > 0 && /^[A-Za-z_][A-Za-z0-9_]*=/.test(words2[0]))
|
|
192
|
+
words2 = words2.slice(1);
|
|
193
|
+
if (words2[0] === "env") {
|
|
194
|
+
words2 = words2.slice(1);
|
|
195
|
+
while (words2.length > 0 && (/^[A-Za-z_][A-Za-z0-9_]*=/.test(words2[0]) || words2[0].startsWith("-")))
|
|
196
|
+
words2 = words2.slice(1);
|
|
133
197
|
}
|
|
134
|
-
|
|
198
|
+
const first = words2[0];
|
|
199
|
+
if (!first)
|
|
200
|
+
return void 0;
|
|
201
|
+
const name = baseName(first.replace(/^["']|["']$/g, "")).replace(/\.(exe|cmd|bat)$/i, "");
|
|
202
|
+
if (SHELLS.has(name.toLowerCase()) && words2[1] && /^(-\w*c|\/c|-command)$/i.test(words2[1])) {
|
|
203
|
+
return commandWord(words2.slice(2).join(" ").replace(/^["']/, "")) ?? name;
|
|
204
|
+
}
|
|
205
|
+
return name || void 0;
|
|
135
206
|
}
|
|
136
|
-
function
|
|
207
|
+
function baseName(p) {
|
|
208
|
+
const parts = p.split(/[\\/]/).filter(Boolean);
|
|
209
|
+
return parts.at(-1) ?? p;
|
|
210
|
+
}
|
|
211
|
+
function isWindowsPath(p) {
|
|
212
|
+
return /^[A-Za-z]:[\\/]/.test(p) || p.startsWith("\\\\");
|
|
213
|
+
}
|
|
214
|
+
function relativeTarget(p, root) {
|
|
215
|
+
if (!root)
|
|
216
|
+
return baseName(p);
|
|
217
|
+
const win = isWindowsPath(p) || isWindowsPath(root);
|
|
218
|
+
const lib = win ? path2.win32 : path2.posix;
|
|
219
|
+
if (!lib.isAbsolute(p))
|
|
220
|
+
return p.replace(/\\/g, "/");
|
|
221
|
+
const rel = lib.relative(win ? root : root.replace(/\\/g, "/"), p);
|
|
222
|
+
if (!rel || rel.startsWith("..") || lib.isAbsolute(rel))
|
|
223
|
+
return `\u2026/${baseName(p)}`;
|
|
224
|
+
return rel.replace(/\\/g, "/");
|
|
225
|
+
}
|
|
226
|
+
function urlHost(u) {
|
|
137
227
|
try {
|
|
138
|
-
|
|
139
|
-
if (s.version !== 1 || typeof s.totalCatches !== "number" || typeof s.catchesToday !== "number" || typeof s.day !== "string")
|
|
140
|
-
return void 0;
|
|
141
|
-
return { version: 1, totalCatches: s.totalCatches, perTrap: s.perTrap ?? {}, day: s.day, catchesToday: s.catchesToday, counted: s.counted ?? [] };
|
|
228
|
+
return new URL(u).host || void 0;
|
|
142
229
|
} catch {
|
|
143
230
|
return void 0;
|
|
144
231
|
}
|
|
145
232
|
}
|
|
146
|
-
function
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
233
|
+
function toolTarget(input) {
|
|
234
|
+
if (!input || typeof input !== "object")
|
|
235
|
+
return void 0;
|
|
236
|
+
const i = input;
|
|
237
|
+
const str = (k) => typeof i[k] === "string" && i[k].length > 0 ? i[k] : void 0;
|
|
238
|
+
const file = str("file_path") ?? str("notebook_path") ?? str("path");
|
|
239
|
+
if (file)
|
|
240
|
+
return file;
|
|
241
|
+
const command = str("command") ?? (Array.isArray(i.command) ? i.command.map(String).join(" ") : void 0);
|
|
242
|
+
if (command)
|
|
243
|
+
return commandWord(command);
|
|
244
|
+
const url = str("url");
|
|
245
|
+
if (url)
|
|
246
|
+
return urlHost(url);
|
|
247
|
+
const changes = i.changes;
|
|
248
|
+
if (Array.isArray(changes) && changes.length > 0) {
|
|
249
|
+
const first = changes[0];
|
|
250
|
+
if (typeof first?.path === "string")
|
|
251
|
+
return first.path;
|
|
252
|
+
}
|
|
253
|
+
return str("subagent_type") ?? str("skill");
|
|
152
254
|
}
|
|
153
|
-
function
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
if (Date.now() - fs2.statSync(lock).mtimeMs > 12e4)
|
|
166
|
-
fs2.rmdirSync(lock);
|
|
167
|
-
} catch {
|
|
168
|
-
}
|
|
169
|
-
if (Date.now() >= deadline)
|
|
170
|
-
throw new Error("stats store locked");
|
|
171
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
|
|
255
|
+
function toolSummary(name, target, root) {
|
|
256
|
+
const looksPath = target !== void 0 && (/[\\/]/.test(target) || isWindowsPath(target));
|
|
257
|
+
const shown = target === void 0 ? "" : looksPath ? relativeTarget(target, root) : target;
|
|
258
|
+
return redactSummary(shown ? `${name} ${shown}` : name);
|
|
259
|
+
}
|
|
260
|
+
function activityFromEvent(ev, root) {
|
|
261
|
+
const data = ev.data ?? {};
|
|
262
|
+
switch (ev.type) {
|
|
263
|
+
case "tool-start": {
|
|
264
|
+
const name = typeof data.name === "string" && data.name ? data.name : "tool";
|
|
265
|
+
const target = typeof data.target === "string" ? data.target : void 0;
|
|
266
|
+
return { kind: "tool", summary: toolSummary(name, target, root) };
|
|
172
267
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
268
|
+
case "text":
|
|
269
|
+
return { kind: "message", summary: "writing a message" };
|
|
270
|
+
case "thinking":
|
|
271
|
+
return { kind: "thinking", summary: "thinking" };
|
|
272
|
+
case "runner": {
|
|
273
|
+
if (typeof data.on === "string")
|
|
274
|
+
return { kind: "waiting", summary: redactSummary(`waiting on ${data.on}`) };
|
|
275
|
+
if (typeof data.waiting === "string")
|
|
276
|
+
return { kind: "waiting", summary: redactSummary(`waiting for an answer (${data.waiting})`) };
|
|
277
|
+
if (data.holding === "background")
|
|
278
|
+
return { kind: "waiting", summary: "waiting on background work" };
|
|
279
|
+
return void 0;
|
|
280
|
+
}
|
|
281
|
+
default:
|
|
282
|
+
return void 0;
|
|
178
283
|
}
|
|
179
284
|
}
|
|
180
|
-
function
|
|
181
|
-
|
|
285
|
+
function atomicWrite(file, content) {
|
|
286
|
+
const tmp = `${file}.tmp-${process.pid}`;
|
|
287
|
+
fs2.writeFileSync(tmp, content);
|
|
288
|
+
fs2.renameSync(tmp, file);
|
|
289
|
+
}
|
|
290
|
+
function writeActivity(id, lane, a) {
|
|
291
|
+
const safe = { at: a.at, kind: a.kind, summary: redactSummary(a.summary) };
|
|
292
|
+
atomicWrite(activityPath(id, lane), JSON.stringify(safe));
|
|
293
|
+
}
|
|
294
|
+
function readActivity(id, lane) {
|
|
182
295
|
try {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
296
|
+
const parsed = JSON.parse(fs2.readFileSync(activityPath(id, lane), "utf8"));
|
|
297
|
+
if (typeof parsed.at !== "string" || typeof parsed.summary !== "string")
|
|
298
|
+
return void 0;
|
|
299
|
+
if (!ACTIVITY_KINDS.includes(parsed.kind))
|
|
300
|
+
return void 0;
|
|
301
|
+
return { ...parsed, summary: redactSummary(parsed.summary) };
|
|
302
|
+
} catch {
|
|
303
|
+
return void 0;
|
|
187
304
|
}
|
|
188
|
-
const saved = text === void 0 ? void 0 : parseStore(text);
|
|
189
|
-
if (saved)
|
|
190
|
-
return saved;
|
|
191
|
-
if (text !== void 0)
|
|
192
|
-
fs2.renameSync(statsPath(), `${statsPath()}.unreadable-${now2}`);
|
|
193
|
-
const store = backfillStats(now2);
|
|
194
|
-
writeStore(store);
|
|
195
|
-
return store;
|
|
196
305
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
// packages/core/dist/attachments.js
|
|
212
|
-
function attachmentBlock(attachments) {
|
|
213
|
-
if (attachments.length === 0)
|
|
214
|
-
return "";
|
|
215
|
-
return [
|
|
216
|
-
"--- ATTACHMENTS ---",
|
|
217
|
-
...attachments.map((a) => `${a.name} (${a.type}, ${a.bytes} bytes) at ${a.path}`),
|
|
218
|
-
"Images and PDFs can be read directly with your file tools."
|
|
219
|
-
].join("\n");
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// packages/core/dist/decisions.js
|
|
223
|
-
var DECISION_DETAIL_MAX = 64 * 1024;
|
|
224
|
-
var ANSWER_TYPES = {
|
|
225
|
-
".png": "image/png",
|
|
226
|
-
".jpg": "image/jpeg",
|
|
227
|
-
".jpeg": "image/jpeg",
|
|
228
|
-
".gif": "image/gif",
|
|
229
|
-
".webp": "image/webp",
|
|
230
|
-
".pdf": "application/pdf",
|
|
231
|
-
".txt": "text/plain",
|
|
232
|
-
".md": "text/markdown",
|
|
233
|
-
".csv": "text/csv",
|
|
234
|
-
".json": "application/json",
|
|
235
|
-
".log": "text/plain",
|
|
236
|
-
".diff": "text/plain",
|
|
237
|
-
".patch": "text/plain",
|
|
238
|
-
".yaml": "text/plain",
|
|
239
|
-
".yml": "text/plain",
|
|
240
|
-
".toml": "text/plain",
|
|
241
|
-
".zip": "application/zip"
|
|
242
|
-
};
|
|
243
|
-
var ANSWER_EXTENSIONS = Object.keys(ANSWER_TYPES);
|
|
244
|
-
|
|
245
|
-
// packages/core/dist/config.js
|
|
246
|
-
import * as fs3 from "node:fs";
|
|
247
|
-
import * as os2 from "node:os";
|
|
248
|
-
import * as path3 from "node:path";
|
|
249
|
-
|
|
250
|
-
// node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/date.js
|
|
251
|
-
var DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}(?::\d{2}(?:\.\d+)?)?)?(Z|[-+]\d{2}:\d{2})?$/i;
|
|
252
|
-
var TomlDate = class _TomlDate extends Date {
|
|
253
|
-
#hasDate = false;
|
|
254
|
-
#hasTime = false;
|
|
255
|
-
#offset = null;
|
|
256
|
-
constructor(date) {
|
|
257
|
-
let hasDate = true;
|
|
258
|
-
let hasTime = true;
|
|
259
|
-
let offset = "Z";
|
|
260
|
-
if (typeof date === "string") {
|
|
261
|
-
let match = date.match(DATE_TIME_RE);
|
|
262
|
-
if (match) {
|
|
263
|
-
if (!match[1]) {
|
|
264
|
-
hasDate = false;
|
|
265
|
-
date = `0000-01-01T${date}`;
|
|
266
|
-
}
|
|
267
|
-
hasTime = !!match[2];
|
|
268
|
-
hasTime && date[10] === " " && (date = date.replace(" ", "T"));
|
|
269
|
-
if (match[2] && +match[2] > 23) {
|
|
270
|
-
date = "";
|
|
271
|
-
} else {
|
|
272
|
-
offset = match[3] || null;
|
|
273
|
-
date = date.toUpperCase();
|
|
274
|
-
if (!offset && hasTime)
|
|
275
|
-
date += "Z";
|
|
276
|
-
}
|
|
277
|
-
} else {
|
|
278
|
-
date = "";
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
super(date);
|
|
282
|
-
if (!isNaN(this.getTime())) {
|
|
283
|
-
this.#hasDate = hasDate;
|
|
284
|
-
this.#hasTime = hasTime;
|
|
285
|
-
this.#offset = offset;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
isDateTime() {
|
|
289
|
-
return this.#hasDate && this.#hasTime;
|
|
290
|
-
}
|
|
291
|
-
isLocal() {
|
|
292
|
-
return !this.#hasDate || !this.#hasTime || !this.#offset;
|
|
293
|
-
}
|
|
294
|
-
isDate() {
|
|
295
|
-
return this.#hasDate && !this.#hasTime;
|
|
296
|
-
}
|
|
297
|
-
isTime() {
|
|
298
|
-
return this.#hasTime && !this.#hasDate;
|
|
299
|
-
}
|
|
300
|
-
isValid() {
|
|
301
|
-
return this.#hasDate || this.#hasTime;
|
|
306
|
+
var ActivityTracker = class {
|
|
307
|
+
write;
|
|
308
|
+
opts;
|
|
309
|
+
lastWriteAt = -Infinity;
|
|
310
|
+
lastKind;
|
|
311
|
+
pending;
|
|
312
|
+
timer;
|
|
313
|
+
throttleMs;
|
|
314
|
+
now;
|
|
315
|
+
constructor(write, opts = {}) {
|
|
316
|
+
this.write = write;
|
|
317
|
+
this.opts = opts;
|
|
318
|
+
this.throttleMs = opts.throttleMs ?? 1e4;
|
|
319
|
+
this.now = opts.now ?? Date.now;
|
|
302
320
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
if (
|
|
306
|
-
|
|
307
|
-
if (this.isTime())
|
|
308
|
-
return iso.slice(11, 23);
|
|
309
|
-
if (this.#offset === null)
|
|
310
|
-
return iso.slice(0, -1);
|
|
311
|
-
if (this.#offset === "Z")
|
|
312
|
-
return iso;
|
|
313
|
-
let offset = +this.#offset.slice(1, 3) * 60 + +this.#offset.slice(4, 6);
|
|
314
|
-
offset = this.#offset[0] === "-" ? offset : -offset;
|
|
315
|
-
let offsetDate = new Date(this.getTime() - offset * 6e4);
|
|
316
|
-
return offsetDate.toISOString().slice(0, -1) + this.#offset;
|
|
321
|
+
observe(ev) {
|
|
322
|
+
const derived = activityFromEvent(ev, this.opts.root);
|
|
323
|
+
if (derived)
|
|
324
|
+
this.offer({ at: new Date(this.now()).toISOString(), ...derived });
|
|
317
325
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
326
|
+
offer(a) {
|
|
327
|
+
const t = this.now();
|
|
328
|
+
if (a.kind !== this.lastKind || t - this.lastWriteAt >= this.throttleMs) {
|
|
329
|
+
this.emit(a, t);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
this.pending = a;
|
|
333
|
+
if (!this.timer) {
|
|
334
|
+
this.timer = setTimeout(() => {
|
|
335
|
+
this.timer = void 0;
|
|
336
|
+
this.flush();
|
|
337
|
+
}, Math.max(0, this.throttleMs - (t - this.lastWriteAt)));
|
|
338
|
+
this.timer.unref?.();
|
|
339
|
+
}
|
|
322
340
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
341
|
+
/** Write a held record now. */
|
|
342
|
+
flush() {
|
|
343
|
+
if (!this.pending)
|
|
344
|
+
return;
|
|
345
|
+
const a = this.pending;
|
|
346
|
+
this.emit(a, this.now());
|
|
327
347
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
348
|
+
stop() {
|
|
349
|
+
if (this.timer)
|
|
350
|
+
clearTimeout(this.timer);
|
|
351
|
+
this.timer = void 0;
|
|
352
|
+
this.pending = void 0;
|
|
333
353
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
354
|
+
emit(a, t) {
|
|
355
|
+
this.pending = void 0;
|
|
356
|
+
if (this.timer) {
|
|
357
|
+
clearTimeout(this.timer);
|
|
358
|
+
this.timer = void 0;
|
|
359
|
+
}
|
|
360
|
+
this.lastWriteAt = t;
|
|
361
|
+
this.lastKind = a.kind;
|
|
362
|
+
try {
|
|
363
|
+
this.write(a);
|
|
364
|
+
} catch {
|
|
365
|
+
}
|
|
339
366
|
}
|
|
340
367
|
};
|
|
341
368
|
|
|
342
|
-
//
|
|
343
|
-
function
|
|
344
|
-
|
|
345
|
-
return [lines.length, lines.pop().length + 1];
|
|
369
|
+
// packages/core/dist/status.js
|
|
370
|
+
function isVerb(v) {
|
|
371
|
+
return VERBS.includes(v);
|
|
346
372
|
}
|
|
347
|
-
function
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
373
|
+
function isWaitingOn(v) {
|
|
374
|
+
return WAITING_ON.includes(v);
|
|
375
|
+
}
|
|
376
|
+
var DURATION = /^(\d+(?:\.\d+)?)\s*(s|m|h|d)$/i;
|
|
377
|
+
var UNIT_MS = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
|
|
378
|
+
function parseUntil(v, now2 = Date.now()) {
|
|
379
|
+
const d = DURATION.exec(v.trim());
|
|
380
|
+
if (d)
|
|
381
|
+
return new Date(now2 + Number(d[1]) * UNIT_MS[d[2].toLowerCase()]).toISOString();
|
|
382
|
+
const t = Date.parse(v);
|
|
383
|
+
if (Number.isNaN(t))
|
|
384
|
+
throw new Error(`invalid --until "${v}" \u2014 an ISO time (2026-10-01T09:00Z) or a duration (30m, 4h, 2d)`);
|
|
385
|
+
return new Date(t).toISOString();
|
|
386
|
+
}
|
|
387
|
+
function waitingFields(verb, w, now2 = Date.now()) {
|
|
388
|
+
const out = {};
|
|
389
|
+
if ((w.waitingOn !== void 0 || w.link !== void 0) && !WAITING_ON_VERBS.includes(verb)) {
|
|
390
|
+
throw new Error(`--waiting-on and --link are valid only with ${WAITING_ON_VERBS.join(", ")} \u2014 not ${verb}`);
|
|
391
|
+
}
|
|
392
|
+
if (w.until !== void 0 && verb !== "paused")
|
|
393
|
+
throw new Error(`--until is valid only with paused \u2014 not ${verb}`);
|
|
394
|
+
if (w.waitingOn !== void 0) {
|
|
395
|
+
if (!isWaitingOn(w.waitingOn)) {
|
|
396
|
+
throw new Error(`invalid --waiting-on "${w.waitingOn}" \u2014 must be one of: ${WAITING_ON.join(", ")}`);
|
|
362
397
|
}
|
|
398
|
+
out.waitingOn = w.waitingOn;
|
|
363
399
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
${codeblock}`, options);
|
|
376
|
-
this.line = line;
|
|
377
|
-
this.column = column;
|
|
378
|
-
this.codeblock = codeblock;
|
|
400
|
+
if (w.link !== void 0) {
|
|
401
|
+
let url;
|
|
402
|
+
try {
|
|
403
|
+
url = new URL(w.link);
|
|
404
|
+
} catch {
|
|
405
|
+
url = void 0;
|
|
406
|
+
}
|
|
407
|
+
if (!url || url.protocol !== "http:" && url.protocol !== "https:") {
|
|
408
|
+
throw new Error(`invalid --link "${w.link}" \u2014 must be an http or https URL`);
|
|
409
|
+
}
|
|
410
|
+
out.link = url.href;
|
|
379
411
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
function indexOfNewline(str, start = 0) {
|
|
384
|
-
let idx = str.indexOf("\n", start);
|
|
385
|
-
if (str.charCodeAt(idx - 1) === 13)
|
|
386
|
-
idx--;
|
|
387
|
-
return idx;
|
|
412
|
+
if (w.until !== void 0)
|
|
413
|
+
out.until = parseUntil(w.until, now2);
|
|
414
|
+
return out;
|
|
388
415
|
}
|
|
389
|
-
function
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
416
|
+
function appendStatus(id, lane, verb, note, at, waiting, reported = false) {
|
|
417
|
+
if (!isVerb(verb)) {
|
|
418
|
+
throw new Error(`invalid status verb "${verb}" \u2014 must be one of: ${VERBS.join(", ")}`);
|
|
419
|
+
}
|
|
420
|
+
const extra = waiting ? waitingFields(verb, waiting) : {};
|
|
421
|
+
const entry = {
|
|
422
|
+
at: at ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
423
|
+
verb,
|
|
424
|
+
...note ? { note } : {},
|
|
425
|
+
...extra,
|
|
426
|
+
...reported ? { reported: true } : {}
|
|
427
|
+
};
|
|
428
|
+
fs3.appendFileSync(statusPath(id, lane), `${JSON.stringify(entry)}
|
|
429
|
+
`);
|
|
430
|
+
if (verb === "done") {
|
|
431
|
+
try {
|
|
432
|
+
recordCatch(id, lane);
|
|
433
|
+
} catch {
|
|
403
434
|
}
|
|
404
435
|
}
|
|
436
|
+
return entry;
|
|
405
437
|
}
|
|
406
|
-
function
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
438
|
+
function readStatusLog(id, lane) {
|
|
439
|
+
const file = statusPath(id, lane);
|
|
440
|
+
if (!fs3.existsSync(file))
|
|
441
|
+
return [];
|
|
442
|
+
return fs3.readFileSync(file, "utf8").split("\n").filter(Boolean).flatMap((line) => {
|
|
443
|
+
try {
|
|
444
|
+
const parsed = JSON.parse(line);
|
|
445
|
+
return isVerb(parsed.verb) ? [parsed] : [];
|
|
446
|
+
} catch {
|
|
447
|
+
return [];
|
|
448
|
+
}
|
|
449
|
+
});
|
|
415
450
|
}
|
|
416
|
-
function
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
for (
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
451
|
+
function pausedWaiting(entry) {
|
|
452
|
+
return entry?.verb === "paused" && entry.waitingOn !== void 0;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// packages/core/dist/queue.js
|
|
456
|
+
function storedDescriptor(id, lane) {
|
|
457
|
+
const dirs = laneDirs(lane);
|
|
458
|
+
for (const file of [
|
|
459
|
+
path3.join(dirs.queue, `${id}.json`),
|
|
460
|
+
path3.join(dirs.active, id, "descriptor.json"),
|
|
461
|
+
path3.join(dirs.done, id, "descriptor.json")
|
|
462
|
+
]) {
|
|
463
|
+
try {
|
|
464
|
+
return JSON.parse(fs4.readFileSync(file, "utf8"));
|
|
465
|
+
} catch {
|
|
429
466
|
}
|
|
430
467
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
468
|
+
return void 0;
|
|
469
|
+
}
|
|
470
|
+
function activeIds(lane) {
|
|
471
|
+
return readDirIfPresent(laneDirs(lane).active).filter((f) => !f.startsWith("."));
|
|
472
|
+
}
|
|
473
|
+
function complete(id, lane) {
|
|
474
|
+
const dirs = laneDirs(lane);
|
|
475
|
+
fs4.renameSync(path3.join(dirs.active, id), path3.join(dirs.done, id));
|
|
476
|
+
}
|
|
477
|
+
function cancelRequested(id, lane) {
|
|
478
|
+
return fs4.existsSync(path3.join(laneDirs(lane).active, id, "cancel"));
|
|
435
479
|
}
|
|
436
480
|
|
|
437
|
-
//
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
let first = c;
|
|
445
|
-
let isLiteral = c === 39;
|
|
446
|
-
let isMultiline = c === ctx.s.charCodeAt(ctx.p) && c === ctx.s.charCodeAt(ctx.p + 1);
|
|
447
|
-
if (isMultiline) {
|
|
448
|
-
if ((c = ctx.s.charCodeAt(ctx.p += 2)) === 10)
|
|
449
|
-
ctx.p++;
|
|
450
|
-
else if (c === 13 && ctx.s.charCodeAt(ctx.p + 1) === 10)
|
|
451
|
-
ctx.p += 2;
|
|
481
|
+
// packages/core/dist/evidence.js
|
|
482
|
+
import * as fs5 from "node:fs";
|
|
483
|
+
function readEvidence(id, lane) {
|
|
484
|
+
try {
|
|
485
|
+
return JSON.parse(fs5.readFileSync(evidencePath(id, lane), "utf8"));
|
|
486
|
+
} catch {
|
|
487
|
+
return {};
|
|
452
488
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
state = 0;
|
|
507
|
-
} else if (c === 32 || c === 9) {
|
|
508
|
-
state = 2;
|
|
509
|
-
} else {
|
|
510
|
-
if (c === 98)
|
|
511
|
-
parsed += "\b";
|
|
512
|
-
else if (c === 116)
|
|
513
|
-
parsed += " ";
|
|
514
|
-
else if (c === 110)
|
|
515
|
-
parsed += "\n";
|
|
516
|
-
else if (c === 102)
|
|
517
|
-
parsed += "\f";
|
|
518
|
-
else if (c === 114)
|
|
519
|
-
parsed += "\r";
|
|
520
|
-
else if (c === 101)
|
|
521
|
-
parsed += "\x1B";
|
|
522
|
-
else if (c === 34)
|
|
523
|
-
parsed += '"';
|
|
524
|
-
else if (c === 92)
|
|
525
|
-
parsed += "\\";
|
|
526
|
-
else
|
|
527
|
-
throw new TomlError("unrecognized escape sequence", { toml: ctx.s, ptr: ctx.p });
|
|
528
|
-
sliceStart = ctx.p + 1;
|
|
529
|
-
state = 0;
|
|
530
|
-
}
|
|
531
|
-
} else if (c !== 32 && c !== 9) {
|
|
532
|
-
if (state === 2) {
|
|
533
|
-
throw new TomlError("invalid escape: only line-ending whitespace may be escaped", {
|
|
534
|
-
toml: ctx.s,
|
|
535
|
-
ptr: sliceStart
|
|
536
|
-
});
|
|
489
|
+
}
|
|
490
|
+
function mergeEvidence(id, lane, patch) {
|
|
491
|
+
const file = evidencePath(id, lane);
|
|
492
|
+
const tmp = `${file}.tmp-${process.pid}`;
|
|
493
|
+
fs5.writeFileSync(tmp, JSON.stringify({ ...readEvidence(id, lane), ...patch }, null, 2));
|
|
494
|
+
fs5.renameSync(tmp, file);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// packages/core/dist/pushes.js
|
|
498
|
+
import * as fs6 from "node:fs";
|
|
499
|
+
import * as path4 from "node:path";
|
|
500
|
+
var MAX_PUSHES = 20;
|
|
501
|
+
var PUSH_HEAD = "HEAD";
|
|
502
|
+
var VALUE_OPTIONS = /* @__PURE__ */ new Set(["-o", "--push-option", "--repo", "--receive-pack", "--exec"]);
|
|
503
|
+
function words(segment) {
|
|
504
|
+
const out = [];
|
|
505
|
+
const re = /"((?:[^"\\]|\\.)*)"|'([^']*)'|(\S+)/g;
|
|
506
|
+
for (let m = re.exec(segment); m; m = re.exec(segment))
|
|
507
|
+
out.push(m[1] ?? m[2] ?? m[3] ?? "");
|
|
508
|
+
return out;
|
|
509
|
+
}
|
|
510
|
+
function branchOf(ref) {
|
|
511
|
+
const name = ref.replace(/^\+/, "").replace(/^refs\/heads\//, "");
|
|
512
|
+
return name === "@" || name === "" ? PUSH_HEAD : name;
|
|
513
|
+
}
|
|
514
|
+
function gitPushTargets(command) {
|
|
515
|
+
if (command === void 0)
|
|
516
|
+
return void 0;
|
|
517
|
+
const text = typeof command === "string" ? command : command.join(" ");
|
|
518
|
+
if (!/\bgit\b[^\n]*\bpush\b/.test(text))
|
|
519
|
+
return void 0;
|
|
520
|
+
const targets = [];
|
|
521
|
+
let found = false;
|
|
522
|
+
for (const segment of text.split(/&&|\|\||[;|\n]/)) {
|
|
523
|
+
const w = words(segment);
|
|
524
|
+
let i = w.findIndex((x) => x === "git" || /[\\/]git(?:\.exe)?$/.test(x));
|
|
525
|
+
if (i < 0)
|
|
526
|
+
continue;
|
|
527
|
+
for (i++; i < w.length && w[i].startsWith("-"); i++)
|
|
528
|
+
if (w[i] === "-C" || w[i] === "-c")
|
|
529
|
+
i++;
|
|
530
|
+
if (w[i] !== "push")
|
|
531
|
+
continue;
|
|
532
|
+
found = true;
|
|
533
|
+
const positional = [];
|
|
534
|
+
for (let j = i + 1; j < w.length; j++) {
|
|
535
|
+
const arg = w[j];
|
|
536
|
+
if (arg === "--")
|
|
537
|
+
continue;
|
|
538
|
+
if (arg.startsWith("-")) {
|
|
539
|
+
if (VALUE_OPTIONS.has(arg))
|
|
540
|
+
j++;
|
|
541
|
+
continue;
|
|
537
542
|
}
|
|
538
|
-
|
|
539
|
-
sliceStart = ctx.p;
|
|
543
|
+
positional.push(arg);
|
|
540
544
|
}
|
|
545
|
+
const refspecs = positional.slice(1);
|
|
546
|
+
if (refspecs.length === 0)
|
|
547
|
+
targets.push(PUSH_HEAD);
|
|
548
|
+
for (const spec of refspecs)
|
|
549
|
+
targets.push(branchOf(spec.includes(":") ? spec.slice(spec.indexOf(":") + 1) : spec));
|
|
541
550
|
}
|
|
542
|
-
|
|
551
|
+
return found ? [...new Set(targets)] : void 0;
|
|
543
552
|
}
|
|
544
|
-
function
|
|
545
|
-
let
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
553
|
+
function gitDirAt(dir) {
|
|
554
|
+
let at = path4.resolve(dir);
|
|
555
|
+
for (; ; ) {
|
|
556
|
+
const dotGit = path4.join(at, ".git");
|
|
557
|
+
try {
|
|
558
|
+
const stat = fs6.statSync(dotGit);
|
|
559
|
+
if (stat.isDirectory())
|
|
560
|
+
return dotGit;
|
|
561
|
+
const m = /^gitdir:\s*(.+)$/m.exec(fs6.readFileSync(dotGit, "utf8"));
|
|
562
|
+
return m ? path4.resolve(at, m[1].trim()) : void 0;
|
|
563
|
+
} catch {
|
|
564
|
+
}
|
|
565
|
+
const up = path4.dirname(at);
|
|
566
|
+
if (up === at)
|
|
567
|
+
return void 0;
|
|
568
|
+
at = up;
|
|
550
569
|
}
|
|
551
|
-
return value.trimEnd();
|
|
552
570
|
}
|
|
553
|
-
function
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
return
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
if (
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
571
|
+
function currentBranchAt(dir) {
|
|
572
|
+
const gitDir = gitDirAt(dir);
|
|
573
|
+
if (!gitDir)
|
|
574
|
+
return void 0;
|
|
575
|
+
try {
|
|
576
|
+
const m = /^ref:\s*refs\/heads\/(.+)$/m.exec(fs6.readFileSync(path4.join(gitDir, "HEAD"), "utf8"));
|
|
577
|
+
return m ? m[1].trim() : void 0;
|
|
578
|
+
} catch {
|
|
579
|
+
return void 0;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
function resolvePushTargets(targets, cwd) {
|
|
583
|
+
const head = cwd ? currentBranchAt(cwd) : void 0;
|
|
584
|
+
return [...new Set(targets.flatMap((t) => t === PUSH_HEAD ? head ? [head] : [] : [t]))];
|
|
585
|
+
}
|
|
586
|
+
function recordPush(id, lane, branches, at = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
587
|
+
if (branches.length === 0)
|
|
588
|
+
return;
|
|
589
|
+
const kept = (readEvidence(id, lane).pushes ?? []).filter((p) => !branches.includes(p.branch));
|
|
590
|
+
const pushes = [...kept, ...branches.map((branch) => ({ branch, at }))].slice(-MAX_PUSHES);
|
|
591
|
+
mergeEvidence(id, lane, { pushes });
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// packages/core/dist/config.js
|
|
595
|
+
import * as fs7 from "node:fs";
|
|
596
|
+
import * as os2 from "node:os";
|
|
597
|
+
import * as path5 from "node:path";
|
|
598
|
+
|
|
599
|
+
// node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/date.js
|
|
600
|
+
var DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}(?::\d{2}(?:\.\d+)?)?)?(Z|[-+]\d{2}:\d{2})?$/i;
|
|
601
|
+
var TomlDate = class _TomlDate extends Date {
|
|
602
|
+
#hasDate = false;
|
|
603
|
+
#hasTime = false;
|
|
604
|
+
#offset = null;
|
|
605
|
+
constructor(date) {
|
|
606
|
+
let hasDate = true;
|
|
607
|
+
let hasTime = true;
|
|
608
|
+
let offset = "Z";
|
|
609
|
+
if (typeof date === "string") {
|
|
610
|
+
let match = date.match(DATE_TIME_RE);
|
|
611
|
+
if (match) {
|
|
612
|
+
if (!match[1]) {
|
|
613
|
+
hasDate = false;
|
|
614
|
+
date = `0000-01-01T${date}`;
|
|
615
|
+
}
|
|
616
|
+
hasTime = !!match[2];
|
|
617
|
+
hasTime && date[10] === " " && (date = date.replace(" ", "T"));
|
|
618
|
+
if (match[2] && +match[2] > 23) {
|
|
619
|
+
date = "";
|
|
620
|
+
} else {
|
|
621
|
+
offset = match[3] || null;
|
|
622
|
+
date = date.toUpperCase();
|
|
623
|
+
if (!offset && hasTime)
|
|
624
|
+
date += "Z";
|
|
625
|
+
}
|
|
626
|
+
} else {
|
|
627
|
+
date = "";
|
|
581
628
|
}
|
|
582
|
-
if (isInt || integersAsBigInt === true)
|
|
583
|
-
numeric = BigInt(value);
|
|
584
629
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
return date;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
// node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/extract.js
|
|
594
|
-
function extractValue(ctx, end, integersAsBigInt) {
|
|
595
|
-
let ptr = ctx.p;
|
|
596
|
-
let c = ctx.s.charCodeAt(ptr);
|
|
597
|
-
if (c === 91 || c === 123) {
|
|
598
|
-
if (!ctx.d--) {
|
|
599
|
-
throw new TomlError("document contains excessively nested structures. aborting.", {
|
|
600
|
-
toml: ctx.s,
|
|
601
|
-
ptr
|
|
602
|
-
});
|
|
630
|
+
super(date);
|
|
631
|
+
if (!isNaN(this.getTime())) {
|
|
632
|
+
this.#hasDate = hasDate;
|
|
633
|
+
this.#hasTime = hasTime;
|
|
634
|
+
this.#offset = offset;
|
|
603
635
|
}
|
|
604
|
-
let value = c === 91 ? parseArray(ctx, integersAsBigInt) : parseInlineTable(ctx, integersAsBigInt);
|
|
605
|
-
ctx.d++;
|
|
606
|
-
return value;
|
|
607
636
|
}
|
|
608
|
-
|
|
609
|
-
return
|
|
637
|
+
isDateTime() {
|
|
638
|
+
return this.#hasDate && this.#hasTime;
|
|
610
639
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
throw new TomlError("invalid value", { toml: ctx.s, ptr });
|
|
614
|
-
ctx.p++;
|
|
615
|
-
return true;
|
|
640
|
+
isLocal() {
|
|
641
|
+
return !this.#hasDate || !this.#hasTime || !this.#offset;
|
|
616
642
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
throw new TomlError("invalid value", { toml: ctx.s, ptr });
|
|
620
|
-
ctx.p++;
|
|
621
|
-
return false;
|
|
643
|
+
isDate() {
|
|
644
|
+
return this.#hasDate && !this.#hasTime;
|
|
622
645
|
}
|
|
623
|
-
|
|
646
|
+
isTime() {
|
|
647
|
+
return this.#hasTime && !this.#hasDate;
|
|
648
|
+
}
|
|
649
|
+
isValid() {
|
|
650
|
+
return this.#hasDate || this.#hasTime;
|
|
651
|
+
}
|
|
652
|
+
toISOString() {
|
|
653
|
+
let iso = super.toISOString();
|
|
654
|
+
if (this.isDate())
|
|
655
|
+
return iso.slice(0, 10);
|
|
656
|
+
if (this.isTime())
|
|
657
|
+
return iso.slice(11, 23);
|
|
658
|
+
if (this.#offset === null)
|
|
659
|
+
return iso.slice(0, -1);
|
|
660
|
+
if (this.#offset === "Z")
|
|
661
|
+
return iso;
|
|
662
|
+
let offset = +this.#offset.slice(1, 3) * 60 + +this.#offset.slice(4, 6);
|
|
663
|
+
offset = this.#offset[0] === "-" ? offset : -offset;
|
|
664
|
+
let offsetDate = new Date(this.getTime() - offset * 6e4);
|
|
665
|
+
return offsetDate.toISOString().slice(0, -1) + this.#offset;
|
|
666
|
+
}
|
|
667
|
+
static wrapAsOffsetDateTime(jsDate, offset = "Z") {
|
|
668
|
+
let date = new _TomlDate(jsDate);
|
|
669
|
+
date.#offset = offset;
|
|
670
|
+
return date;
|
|
671
|
+
}
|
|
672
|
+
static wrapAsLocalDateTime(jsDate) {
|
|
673
|
+
let date = new _TomlDate(jsDate);
|
|
674
|
+
date.#offset = null;
|
|
675
|
+
return date;
|
|
676
|
+
}
|
|
677
|
+
static wrapAsLocalDate(jsDate) {
|
|
678
|
+
let date = new _TomlDate(jsDate);
|
|
679
|
+
date.#hasTime = false;
|
|
680
|
+
date.#offset = null;
|
|
681
|
+
return date;
|
|
682
|
+
}
|
|
683
|
+
static wrapAsLocalTime(jsDate) {
|
|
684
|
+
let date = new _TomlDate(jsDate);
|
|
685
|
+
date.#hasDate = false;
|
|
686
|
+
date.#offset = null;
|
|
687
|
+
return date;
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
// node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/error.js
|
|
692
|
+
function getLineColFromPtr(string, ptr) {
|
|
693
|
+
let lines = string.slice(0, ptr).split(/\r\n|\n|\r/g);
|
|
694
|
+
return [lines.length, lines.pop().length + 1];
|
|
695
|
+
}
|
|
696
|
+
function makeCodeBlock(string, line, column) {
|
|
697
|
+
let lines = string.split(/\r\n|\n|\r/g);
|
|
698
|
+
let codeblock = "";
|
|
699
|
+
let numberLen = (Math.log10(line + 1) | 0) + 1;
|
|
700
|
+
for (let i = line - 1; i <= line + 1; i++) {
|
|
701
|
+
let l = lines[i - 1];
|
|
702
|
+
if (!l)
|
|
703
|
+
continue;
|
|
704
|
+
codeblock += i.toString().padEnd(numberLen, " ");
|
|
705
|
+
codeblock += ": ";
|
|
706
|
+
codeblock += l;
|
|
707
|
+
codeblock += "\n";
|
|
708
|
+
if (i === line) {
|
|
709
|
+
codeblock += " ".repeat(numberLen + column + 2);
|
|
710
|
+
codeblock += "^\n";
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
return codeblock;
|
|
624
714
|
}
|
|
715
|
+
var TomlError = class extends Error {
|
|
716
|
+
line;
|
|
717
|
+
column;
|
|
718
|
+
codeblock;
|
|
719
|
+
constructor(message, options) {
|
|
720
|
+
const [line, column] = getLineColFromPtr(options.toml, options.ptr);
|
|
721
|
+
const codeblock = makeCodeBlock(options.toml, line, column);
|
|
722
|
+
super(`Invalid TOML document: ${message}
|
|
625
723
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
let dot = start - 1;
|
|
631
|
-
let parsed = [];
|
|
632
|
-
let endPtr = ctx.s.indexOf(end, start);
|
|
633
|
-
if (endPtr < 0) {
|
|
634
|
-
throw new TomlError("incomplete key-value: cannot find end of key", {
|
|
635
|
-
toml: ctx.s,
|
|
636
|
-
ptr: start
|
|
637
|
-
});
|
|
724
|
+
${codeblock}`, options);
|
|
725
|
+
this.line = line;
|
|
726
|
+
this.column = column;
|
|
727
|
+
this.codeblock = codeblock;
|
|
638
728
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
let part = parseString(ctx);
|
|
650
|
-
dot = ctx.s.indexOf(".", ctx.p);
|
|
651
|
-
let strEnd = ctx.s.slice(ctx.p, dot < 0 || dot > endPtr ? endPtr : dot);
|
|
652
|
-
let newLine = indexOfNewline(strEnd);
|
|
653
|
-
if (newLine > -1) {
|
|
654
|
-
throw new TomlError("newlines are not allowed in keys", {
|
|
655
|
-
toml: ctx.s,
|
|
656
|
-
ptr: newLine
|
|
657
|
-
});
|
|
658
|
-
}
|
|
659
|
-
if (strEnd.trimStart()) {
|
|
660
|
-
throw new TomlError("found extra tokens after the string part", {
|
|
661
|
-
toml: ctx.s,
|
|
662
|
-
ptr: ctx.p
|
|
663
|
-
});
|
|
664
|
-
}
|
|
665
|
-
if (endPtr < ctx.p) {
|
|
666
|
-
endPtr = ctx.s.indexOf(end, ctx.p);
|
|
667
|
-
if (endPtr < 0) {
|
|
668
|
-
throw new TomlError("incomplete key-value: cannot find end of key", {
|
|
669
|
-
toml: ctx.s,
|
|
670
|
-
ptr: start
|
|
671
|
-
});
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
parsed.push(part);
|
|
675
|
-
} else {
|
|
676
|
-
dot = ctx.s.indexOf(".", ctx.p);
|
|
677
|
-
let part = ctx.s.slice(ctx.p, dot < 0 || dot > endPtr ? endPtr : dot);
|
|
678
|
-
if (!KEY_PART_RE.test(part)) {
|
|
679
|
-
throw new TomlError("only letter, numbers, dashes and underscores are allowed in keys", {
|
|
680
|
-
toml: ctx.s,
|
|
681
|
-
ptr: ctx.p
|
|
682
|
-
});
|
|
683
|
-
}
|
|
684
|
-
parsed.push(part.trimEnd());
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
} while (dot + 1 && dot < endPtr);
|
|
688
|
-
ctx.p = endPtr + 1;
|
|
689
|
-
skipVoid(ctx, true, true);
|
|
690
|
-
return parsed;
|
|
729
|
+
};
|
|
730
|
+
|
|
731
|
+
// node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/util.js
|
|
732
|
+
function indexOfNewline(str, start = 0) {
|
|
733
|
+
let idx = str.indexOf("\n", start);
|
|
734
|
+
if (str.charCodeAt(idx - 1) === 13)
|
|
735
|
+
idx--;
|
|
736
|
+
return idx;
|
|
691
737
|
}
|
|
692
|
-
function
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
skipVoid(ctx);
|
|
699
|
-
if ((c = ctx.s.charCodeAt(ctx.p)) === 125) {
|
|
738
|
+
function skipComment(ctx) {
|
|
739
|
+
for (; ctx.p < ctx.s.length; ctx.p++) {
|
|
740
|
+
let c = ctx.s.charCodeAt(ctx.p);
|
|
741
|
+
if (c === 10)
|
|
742
|
+
break;
|
|
743
|
+
if (c === 13 && ctx.s.charCodeAt(ctx.p + 1) === 10) {
|
|
700
744
|
ctx.p++;
|
|
701
|
-
|
|
702
|
-
}
|
|
703
|
-
let k;
|
|
704
|
-
let t = res;
|
|
705
|
-
let hasOwn = false;
|
|
706
|
-
let p = ctx.p;
|
|
707
|
-
let key = parseKey(ctx);
|
|
708
|
-
for (let i = 0; i < key.length; i++) {
|
|
709
|
-
if (i)
|
|
710
|
-
t = hasOwn ? t[k] : t[k] = {};
|
|
711
|
-
k = key[i];
|
|
712
|
-
if ((hasOwn = Object.hasOwn(t, k)) && (typeof t[k] !== "object" || seen.has(t[k]))) {
|
|
713
|
-
throw new TomlError("trying to redefine an already defined value", {
|
|
714
|
-
toml: ctx.s,
|
|
715
|
-
ptr: p
|
|
716
|
-
});
|
|
717
|
-
}
|
|
718
|
-
if (!hasOwn && k === "__proto__") {
|
|
719
|
-
Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
|
|
720
|
-
}
|
|
745
|
+
break;
|
|
721
746
|
}
|
|
722
|
-
if (
|
|
723
|
-
throw new TomlError("
|
|
747
|
+
if (c < 32 && c !== 9 || c === 127) {
|
|
748
|
+
throw new TomlError("control characters are not allowed in comments", {
|
|
724
749
|
toml: ctx.s,
|
|
725
750
|
ptr: ctx.p
|
|
726
751
|
});
|
|
727
752
|
}
|
|
728
|
-
let value = extractValue(ctx, 125, integersAsBigInt);
|
|
729
|
-
seen.add(t[k] = value);
|
|
730
|
-
skipVoid(ctx);
|
|
731
|
-
if ((c = ctx.s.charCodeAt(ctx.p++)) === 125) {
|
|
732
|
-
return res;
|
|
733
|
-
}
|
|
734
|
-
if (c !== 44) {
|
|
735
|
-
throw new TomlError("expected comma or end of structure", { toml: ctx.s, ptr: ctx.p - 1 });
|
|
736
|
-
}
|
|
737
753
|
}
|
|
738
|
-
throw new TomlError("unfinished table encountered", {
|
|
739
|
-
toml: ctx.s,
|
|
740
|
-
ptr: ctx.p
|
|
741
|
-
});
|
|
742
754
|
}
|
|
743
|
-
function
|
|
744
|
-
let res = [];
|
|
755
|
+
function skipVoid(ctx, banNewLines, banComments) {
|
|
745
756
|
let c;
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
skipVoid(ctx);
|
|
749
|
-
if ((c = ctx.s.charCodeAt(ctx.p)) === 93) {
|
|
757
|
+
while (1) {
|
|
758
|
+
while ((c = ctx.s.charCodeAt(ctx.p)) === 32 || c === 9 || !banNewLines && (c === 10 || c === 13 && ctx.s.charCodeAt(ctx.p + 1) === 10))
|
|
750
759
|
ctx.p++;
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
+
if (banComments || c !== 35)
|
|
761
|
+
break;
|
|
762
|
+
skipComment(ctx);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
function skipUntil(ctx, sep, end) {
|
|
766
|
+
let ptr = ctx.p;
|
|
767
|
+
if (!end) {
|
|
768
|
+
ptr = indexOfNewline(ctx.s, ptr);
|
|
769
|
+
ctx.p = ptr < 0 ? ctx.s.length : ptr;
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
for (; ctx.p < ctx.s.length; ctx.p++) {
|
|
773
|
+
let c = ctx.s.charCodeAt(ctx.p);
|
|
774
|
+
if (c === 35) {
|
|
775
|
+
skipComment(ctx);
|
|
776
|
+
} else if (c === end || c === sep) {
|
|
777
|
+
return;
|
|
760
778
|
}
|
|
761
779
|
}
|
|
762
|
-
throw new TomlError("
|
|
780
|
+
throw new TomlError("cannot find end of structure", {
|
|
763
781
|
toml: ctx.s,
|
|
764
|
-
ptr
|
|
782
|
+
ptr
|
|
765
783
|
});
|
|
766
784
|
}
|
|
767
785
|
|
|
768
|
-
// node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
let
|
|
774
|
-
let
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
+
// node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/primitive.js
|
|
787
|
+
var INT_REGEX = /^((0x[0-9a-fA-F](_?[0-9a-fA-F])*)|(([+-]|0[ob])?\d(_?\d)*))$/;
|
|
788
|
+
var FLOAT_REGEX = /^[+-]?\d(_?\d)*(\.\d(_?\d)*)?([eE][+-]?\d(_?\d)*)?$/;
|
|
789
|
+
var LEADING_ZERO = /^[+-]?0[0-9_]/;
|
|
790
|
+
function parseString(ctx) {
|
|
791
|
+
let start = ctx.p;
|
|
792
|
+
let c = ctx.s.charCodeAt(ctx.p++);
|
|
793
|
+
let first = c;
|
|
794
|
+
let isLiteral = c === 39;
|
|
795
|
+
let isMultiline = c === ctx.s.charCodeAt(ctx.p) && c === ctx.s.charCodeAt(ctx.p + 1);
|
|
796
|
+
if (isMultiline) {
|
|
797
|
+
if ((c = ctx.s.charCodeAt(ctx.p += 2)) === 10)
|
|
798
|
+
ctx.p++;
|
|
799
|
+
else if (c === 13 && ctx.s.charCodeAt(ctx.p + 1) === 10)
|
|
800
|
+
ctx.p += 2;
|
|
801
|
+
}
|
|
802
|
+
let parsed = "";
|
|
803
|
+
let sliceStart = ctx.p;
|
|
804
|
+
let state = 0;
|
|
805
|
+
for (; ctx.p < ctx.s.length; ctx.p++) {
|
|
806
|
+
c = ctx.s.charCodeAt(ctx.p);
|
|
807
|
+
if (isMultiline && (c === 10 || c === 13 && ctx.s.charCodeAt(ctx.p + 1) === 10)) {
|
|
808
|
+
state = state && 3;
|
|
809
|
+
} else if (c < 32 && c !== 9 || c === 127) {
|
|
810
|
+
throw new TomlError("control characters are not allowed in strings", {
|
|
811
|
+
toml: ctx.s,
|
|
812
|
+
ptr: ctx.p
|
|
813
|
+
});
|
|
814
|
+
} else if ((!state || state === 3) && c === first && (!isMultiline || ctx.s.charCodeAt(ctx.p + 1) === first && ctx.s.charCodeAt(ctx.p + 2) === first)) {
|
|
815
|
+
if (isMultiline) {
|
|
816
|
+
if (ctx.s.charCodeAt(ctx.p + 3) === first)
|
|
817
|
+
ctx.p++;
|
|
818
|
+
if (ctx.s.charCodeAt(ctx.p + 3) === first)
|
|
819
|
+
ctx.p++;
|
|
786
820
|
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
return
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
Object.defineProperty(m, k, { enumerable: true, configurable: true, writable: true });
|
|
821
|
+
if (!state)
|
|
822
|
+
parsed += ctx.s.slice(sliceStart, ctx.p);
|
|
823
|
+
ctx.p += isMultiline ? 3 : 1;
|
|
824
|
+
return parsed;
|
|
825
|
+
} else if (!state) {
|
|
826
|
+
if (!isLiteral && c === 92) {
|
|
827
|
+
parsed += ctx.s.slice(sliceStart, sliceStart = ctx.p);
|
|
828
|
+
state = 1;
|
|
796
829
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
}
|
|
817
|
-
if (state.d) {
|
|
818
|
-
return null;
|
|
819
|
-
}
|
|
820
|
-
state.d = true;
|
|
821
|
-
if (type === 1) {
|
|
822
|
-
t = hasOwn ? t[k] : t[k] = {};
|
|
823
|
-
} else if (type === 0 && hasOwn) {
|
|
824
|
-
return null;
|
|
825
|
-
}
|
|
826
|
-
return [k, t, state.c];
|
|
827
|
-
}
|
|
828
|
-
function parse(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
|
|
829
|
-
let ctx = { s: toml, p: 0, d: maxDepth };
|
|
830
|
-
let res = {};
|
|
831
|
-
let meta = {};
|
|
832
|
-
let tmp;
|
|
833
|
-
let tbl = res;
|
|
834
|
-
let m = meta;
|
|
835
|
-
skipVoid(ctx);
|
|
836
|
-
while (ctx.p < toml.length) {
|
|
837
|
-
if (toml.charCodeAt(ctx.p) === 91) {
|
|
838
|
-
let isTableArray = toml.charCodeAt(++ctx.p) === 91;
|
|
839
|
-
tmp = ctx.p += +isTableArray;
|
|
840
|
-
let k = parseKey(ctx, "]");
|
|
841
|
-
if (isTableArray) {
|
|
842
|
-
if (toml.charCodeAt(ctx.p - 1) !== 93) {
|
|
843
|
-
throw new TomlError("expected end of table declaration", {
|
|
844
|
-
toml,
|
|
845
|
-
ptr: ctx.p - 1
|
|
846
|
-
});
|
|
830
|
+
} else if (state === 1) {
|
|
831
|
+
if (c === 120 || c === 117 || c === 85) {
|
|
832
|
+
let value = 0;
|
|
833
|
+
let len = c === 120 ? 2 : c === 117 ? 4 : 8;
|
|
834
|
+
for (let j = 0; j < len; j++, ctx.p++) {
|
|
835
|
+
let hex = ctx.s.charCodeAt(ctx.p + 1);
|
|
836
|
+
let digit = (
|
|
837
|
+
/* 0-9 */
|
|
838
|
+
hex >= 48 && hex <= 57 ? hex - 48 : (
|
|
839
|
+
/* A-F */
|
|
840
|
+
hex >= 65 && hex <= 70 ? hex - 65 + 10 : (
|
|
841
|
+
/* a-f */
|
|
842
|
+
hex >= 97 && hex <= 102 ? hex - 97 + 10 : -1
|
|
843
|
+
)
|
|
844
|
+
)
|
|
845
|
+
);
|
|
846
|
+
if (digit < 0)
|
|
847
|
+
throw new TomlError("invalid non-hex character in unicode escape", { toml: ctx.s, ptr: ctx.p + 1 });
|
|
848
|
+
value = value << 4 | digit;
|
|
847
849
|
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
850
|
+
if (value < 0 || value > 1114111 || value >= 55296 && value <= 57343) {
|
|
851
|
+
throw new TomlError("invalid unicode escape", { toml: ctx.s, ptr: ctx.p });
|
|
852
|
+
}
|
|
853
|
+
parsed += String.fromCodePoint(value);
|
|
854
|
+
sliceStart = ctx.p + 1;
|
|
855
|
+
state = 0;
|
|
856
|
+
} else if (c === 32 || c === 9) {
|
|
857
|
+
state = 2;
|
|
858
|
+
} else {
|
|
859
|
+
if (c === 98)
|
|
860
|
+
parsed += "\b";
|
|
861
|
+
else if (c === 116)
|
|
862
|
+
parsed += " ";
|
|
863
|
+
else if (c === 110)
|
|
864
|
+
parsed += "\n";
|
|
865
|
+
else if (c === 102)
|
|
866
|
+
parsed += "\f";
|
|
867
|
+
else if (c === 114)
|
|
868
|
+
parsed += "\r";
|
|
869
|
+
else if (c === 101)
|
|
870
|
+
parsed += "\x1B";
|
|
871
|
+
else if (c === 34)
|
|
872
|
+
parsed += '"';
|
|
873
|
+
else if (c === 92)
|
|
874
|
+
parsed += "\\";
|
|
875
|
+
else
|
|
876
|
+
throw new TomlError("unrecognized escape sequence", { toml: ctx.s, ptr: ctx.p });
|
|
877
|
+
sliceStart = ctx.p + 1;
|
|
878
|
+
state = 0;
|
|
862
879
|
}
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
let p = peekTable(
|
|
869
|
-
k,
|
|
870
|
-
tbl,
|
|
871
|
-
m,
|
|
872
|
-
0
|
|
873
|
-
/* Type.DOTTED */
|
|
874
|
-
);
|
|
875
|
-
if (!p) {
|
|
876
|
-
throw new TomlError("trying to redefine an already defined table or value", {
|
|
877
|
-
toml,
|
|
878
|
-
ptr: tmp
|
|
880
|
+
} else if (c !== 32 && c !== 9) {
|
|
881
|
+
if (state === 2) {
|
|
882
|
+
throw new TomlError("invalid escape: only line-ending whitespace may be escaped", {
|
|
883
|
+
toml: ctx.s,
|
|
884
|
+
ptr: sliceStart
|
|
879
885
|
});
|
|
880
886
|
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
skipVoid(ctx, true);
|
|
884
|
-
if (ctx.p < toml.length && (tmp = toml.charCodeAt(ctx.p)) !== 10 && tmp !== 13) {
|
|
885
|
-
throw new TomlError("each key-value declaration must be followed by an end-of-line", {
|
|
886
|
-
toml,
|
|
887
|
-
ptr: ctx.p
|
|
888
|
-
});
|
|
887
|
+
state = !isLiteral && c === 92 ? 1 : 0;
|
|
888
|
+
sliceStart = ctx.p;
|
|
889
889
|
}
|
|
890
|
-
skipVoid(ctx);
|
|
891
890
|
}
|
|
892
|
-
|
|
891
|
+
throw new TomlError("unfinished string", { toml: ctx.s, ptr: start });
|
|
893
892
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
const out = {};
|
|
901
|
-
for (const kind of [...BRIEF_KINDS, "all"]) {
|
|
902
|
-
const text = raw[kind];
|
|
903
|
-
if (typeof text === "string" && text.trim())
|
|
904
|
-
out[kind] = text.trim();
|
|
893
|
+
function sliceAndTrimEndOf(ctx, start, end) {
|
|
894
|
+
let value = ctx.s.slice(start, end);
|
|
895
|
+
let commentIdx = value.indexOf("#");
|
|
896
|
+
if (commentIdx > 0) {
|
|
897
|
+
skipComment({ s: value, p: commentIdx, d: 0 });
|
|
898
|
+
value = value.slice(0, commentIdx);
|
|
905
899
|
}
|
|
906
|
-
return
|
|
900
|
+
return value.trimEnd();
|
|
907
901
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
if (!
|
|
914
|
-
throw new
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
902
|
+
function parseValue(ctx, integersAsBigInt, end) {
|
|
903
|
+
let ptr = ctx.p;
|
|
904
|
+
let err = { toml: ctx.s, ptr };
|
|
905
|
+
skipUntil(ctx, 44, end);
|
|
906
|
+
let value = sliceAndTrimEndOf(ctx, ptr, ctx.p);
|
|
907
|
+
if (!value)
|
|
908
|
+
throw new TomlError("incomplete declaration: value expected", err);
|
|
909
|
+
if (value === "-inf")
|
|
910
|
+
return -Infinity;
|
|
911
|
+
if (value === "inf" || value === "+inf")
|
|
912
|
+
return Infinity;
|
|
913
|
+
if (value === "nan" || value === "+nan" || value === "-nan")
|
|
914
|
+
return NaN;
|
|
915
|
+
if (value === "-0")
|
|
916
|
+
return integersAsBigInt ? 0n : 0;
|
|
917
|
+
let isInt = INT_REGEX.test(value);
|
|
918
|
+
if (isInt || FLOAT_REGEX.test(value)) {
|
|
919
|
+
if (LEADING_ZERO.test(value)) {
|
|
920
|
+
throw new TomlError("leading zeroes are not allowed", err);
|
|
919
921
|
}
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
deferSecs: 90,
|
|
925
|
-
claimIdleNoticeSecs: 180,
|
|
926
|
-
ttlSecs: 1800,
|
|
927
|
-
beat: true,
|
|
928
|
-
pausedTtlSecs: 86400,
|
|
929
|
-
signOffGraceSecs: 600
|
|
930
|
-
};
|
|
931
|
-
var DEFAULT_HELM = {
|
|
932
|
-
ttlSecs: 1800,
|
|
933
|
-
reportSecs: 900,
|
|
934
|
-
armGraceSecs: 5
|
|
935
|
-
};
|
|
936
|
-
var DEFAULT_GLASS = { port: 4949 };
|
|
937
|
-
var DEFAULT_WATCH = { maxForksPerCycle: 3, autoRepair: true, conflicts: true, checks: true, maxRepairsPerPr: 2, maxRepairsWithoutProgress: 2, repairSettleSecs: 600, repairTrapWaitSecs: 600 };
|
|
938
|
-
var DEFAULT_LIMITS = {
|
|
939
|
-
maxConcurrent: 2,
|
|
940
|
-
choreConcurrent: 1,
|
|
941
|
-
wedgeThresholdSecs: 600,
|
|
942
|
-
maxRestartAttempts: 2,
|
|
943
|
-
wallClockSecs: 3600,
|
|
944
|
-
pushEarly: true,
|
|
945
|
-
draftPr: true,
|
|
946
|
-
checkpointOnStop: true,
|
|
947
|
-
backgroundWaitSecs: 1800,
|
|
948
|
-
exitGraceSecs: 30,
|
|
949
|
-
choreRetentionDays: 7,
|
|
950
|
-
attachmentMaxBytes: 25 * 1024 * 1024,
|
|
951
|
-
retentionDays: 0,
|
|
952
|
-
minFreeGB: 0,
|
|
953
|
-
reuseWorktree: true,
|
|
954
|
-
releaseOnMerge: false
|
|
955
|
-
};
|
|
956
|
-
function configPath() {
|
|
957
|
-
return path3.join(lobstahHome(), "config.toml");
|
|
958
|
-
}
|
|
959
|
-
function expandHome(p) {
|
|
960
|
-
return p.startsWith("~/") ? path3.join(os2.homedir(), p.slice(2)) : p;
|
|
961
|
-
}
|
|
962
|
-
function loadConfig() {
|
|
963
|
-
const file = configPath();
|
|
964
|
-
const raw = fs3.existsSync(file) ? parse(fs3.readFileSync(file, "utf8")) : {};
|
|
965
|
-
const reposRaw = raw.repos ?? {};
|
|
966
|
-
const repos = {};
|
|
967
|
-
for (const [key, r] of Object.entries(reposRaw)) {
|
|
968
|
-
repos[key] = {
|
|
969
|
-
path: expandHome(String(r.path ?? "")),
|
|
970
|
-
origin: r.origin ? String(r.origin) : void 0,
|
|
971
|
-
trunk: String(r.trunk ?? "main"),
|
|
972
|
-
setup: Array.isArray(r.setup) ? r.setup.map(String) : void 0,
|
|
973
|
-
env: r.env ?? void 0,
|
|
974
|
-
harness: r.harness ?? void 0,
|
|
975
|
-
pickup: r.pickup === void 0 ? void 0 : Boolean(r.pickup),
|
|
976
|
-
scratch: Array.isArray(r.scratch) ? r.scratch.map(String) : void 0,
|
|
977
|
-
pushEarly: r.pushEarly === void 0 ? void 0 : Boolean(r.pushEarly),
|
|
978
|
-
draftPr: r.draftPr === void 0 ? void 0 : Boolean(r.draftPr),
|
|
979
|
-
checkpointOnStop: r.checkpointOnStop === void 0 ? void 0 : Boolean(r.checkpointOnStop),
|
|
980
|
-
humanGateChecks: Array.isArray(r.humanGateChecks) ? r.humanGateChecks.map(String).filter(Boolean) : void 0,
|
|
981
|
-
briefHooks: parseBriefHooks(r.briefHooks)
|
|
982
|
-
};
|
|
983
|
-
}
|
|
984
|
-
const groundsRaw = raw.grounds ?? {};
|
|
985
|
-
const grounds = {};
|
|
986
|
-
for (const [key, g] of Object.entries(groundsRaw)) {
|
|
987
|
-
grounds[key] = { repos: Array.isArray(g.repos) ? g.repos.map(String) : [] };
|
|
988
|
-
}
|
|
989
|
-
return {
|
|
990
|
-
repos,
|
|
991
|
-
harness: raw.harness ?? {},
|
|
992
|
-
limits: { ...DEFAULT_LIMITS, ...raw.limits ?? {} },
|
|
993
|
-
soak: { ...DEFAULT_SOAK, ...raw.soak ?? {} },
|
|
994
|
-
helm: { ...DEFAULT_HELM, ...raw.helm ?? {} },
|
|
995
|
-
glass: { ...DEFAULT_GLASS, ...raw.glass ?? {} },
|
|
996
|
-
watch: { ...DEFAULT_WATCH, ...raw.watch ?? {} },
|
|
997
|
-
grounds,
|
|
998
|
-
notifyCommand: raw.notifyCommand ? String(raw.notifyCommand) : void 0,
|
|
999
|
-
notifyVerbs: Array.isArray(raw.notifyVerbs) ? raw.notifyVerbs.map(String) : void 0,
|
|
1000
|
-
remindSecs: raw.remindSecs !== void 0 ? Number(raw.remindSecs) : void 0,
|
|
1001
|
-
attentionKinds: parseAttentionKinds(raw.attentionKinds)
|
|
1002
|
-
};
|
|
1003
|
-
}
|
|
1004
|
-
function resolveDispatch(d, cfg) {
|
|
1005
|
-
const repo = cfg.repos[d.repo];
|
|
1006
|
-
if (!repo)
|
|
1007
|
-
throw new Error(`unknown repo key "${d.repo}" \u2014 add it to ${configPath()}`);
|
|
1008
|
-
const rh = repo.harness ?? {};
|
|
1009
|
-
const gh = cfg.harness;
|
|
1010
|
-
return {
|
|
1011
|
-
harness: d.harness ?? rh.default ?? gh.default ?? "claude",
|
|
1012
|
-
model: d.model ?? rh.model ?? gh.model,
|
|
1013
|
-
effort: d.effort ?? rh.effort ?? gh.effort,
|
|
1014
|
-
limits: {
|
|
1015
|
-
wallClockSecs: cfg.limits.wallClockSecs,
|
|
1016
|
-
...d.limits ?? {}
|
|
1017
|
-
},
|
|
1018
|
-
env: { ...repo.env ?? {}, ...d.env ?? {} },
|
|
1019
|
-
flags: d.flags ?? []
|
|
1020
|
-
};
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
// packages/core/dist/disk.js
|
|
1024
|
-
var GB = 1024 ** 3;
|
|
1025
|
-
|
|
1026
|
-
// packages/core/dist/watch.js
|
|
1027
|
-
import * as fs4 from "node:fs";
|
|
1028
|
-
import * as path4 from "node:path";
|
|
1029
|
-
|
|
1030
|
-
// packages/core/dist/gh-errors.js
|
|
1031
|
-
function firstMeaningfulLine(...outputs) {
|
|
1032
|
-
for (const out of outputs) {
|
|
1033
|
-
for (const raw of (out ?? "").split("\n")) {
|
|
1034
|
-
let line = raw.trim();
|
|
1035
|
-
if (!line || /^(warning:|hint:|\(node:|\{|\})/i.test(line))
|
|
1036
|
-
continue;
|
|
1037
|
-
const m = /^error:\s*(.*)$/.exec(line);
|
|
1038
|
-
if (m)
|
|
1039
|
-
line = m[1].replace(/^"(.*)"$/, "$1").trim();
|
|
1040
|
-
line = line.replace(/^(gh|GraphQL):\s*/i, "");
|
|
1041
|
-
if (line)
|
|
1042
|
-
return line;
|
|
922
|
+
value = value.replace(/_/g, "");
|
|
923
|
+
let numeric = +value;
|
|
924
|
+
if (isNaN(numeric)) {
|
|
925
|
+
throw new TomlError("invalid number", err);
|
|
1043
926
|
}
|
|
927
|
+
if (isInt) {
|
|
928
|
+
if ((isInt = !Number.isSafeInteger(numeric)) && !integersAsBigInt) {
|
|
929
|
+
throw new TomlError("integer value cannot be represented losslessly", err);
|
|
930
|
+
}
|
|
931
|
+
if (isInt || integersAsBigInt === true)
|
|
932
|
+
numeric = BigInt(value);
|
|
933
|
+
}
|
|
934
|
+
return numeric;
|
|
1044
935
|
}
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
function watchesDir() {
|
|
1050
|
-
return path4.join(lobstahHome(), "watches");
|
|
1051
|
-
}
|
|
1052
|
-
function slug(key) {
|
|
1053
|
-
return key.replace(/[^A-Za-z0-9._-]+/g, "-");
|
|
1054
|
-
}
|
|
1055
|
-
function watchPath(key) {
|
|
1056
|
-
return path4.join(watchesDir(), `${slug(key)}.json`);
|
|
1057
|
-
}
|
|
1058
|
-
function readWatch(key) {
|
|
1059
|
-
try {
|
|
1060
|
-
return JSON.parse(fs4.readFileSync(watchPath(key), "utf8"));
|
|
1061
|
-
} catch {
|
|
1062
|
-
return void 0;
|
|
1063
|
-
}
|
|
936
|
+
const date = new TomlDate(value);
|
|
937
|
+
if (!date.isValid())
|
|
938
|
+
throw new TomlError("invalid value", err);
|
|
939
|
+
return date;
|
|
1064
940
|
}
|
|
1065
941
|
|
|
1066
|
-
//
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
/\b(?:gh[posur]_|github_pat_|glpat-|xox[abprs]-|npm_|hf_)[A-Za-z0-9_-]{16,}/,
|
|
1081
|
-
/\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/,
|
|
1082
|
-
/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{4,}/,
|
|
1083
|
-
/\bbearer\b/i,
|
|
1084
|
-
/(?:token|secret|passw(?:or)?d|pwd|api[-_]?key|auth|credential|private[-_]?key)[^\s]*[=:]/i
|
|
1085
|
-
];
|
|
1086
|
-
function looksSecret(word) {
|
|
1087
|
-
if (SECRET_PATTERNS.some((re) => re.test(word)))
|
|
1088
|
-
return true;
|
|
1089
|
-
for (const run2 of word.match(/[A-Za-z0-9+/_=-]{24,}/g) ?? []) {
|
|
1090
|
-
if (/[0-9]/.test(run2) && /[A-Za-z]/.test(run2))
|
|
1091
|
-
return true;
|
|
942
|
+
// node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/extract.js
|
|
943
|
+
function extractValue(ctx, end, integersAsBigInt) {
|
|
944
|
+
let ptr = ctx.p;
|
|
945
|
+
let c = ctx.s.charCodeAt(ptr);
|
|
946
|
+
if (c === 91 || c === 123) {
|
|
947
|
+
if (!ctx.d--) {
|
|
948
|
+
throw new TomlError("document contains excessively nested structures. aborting.", {
|
|
949
|
+
toml: ctx.s,
|
|
950
|
+
ptr
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
let value = c === 91 ? parseArray(ctx, integersAsBigInt) : parseInlineTable(ctx, integersAsBigInt);
|
|
954
|
+
ctx.d++;
|
|
955
|
+
return value;
|
|
1092
956
|
}
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
function redactSummary(s, max = ACTIVITY_SUMMARY_MAX) {
|
|
1096
|
-
const clean = s.replace(/[\r\n\t]+/g, " ").split(" ").filter(Boolean).map((w) => looksSecret(w) ? "[redacted]" : w).join(" ");
|
|
1097
|
-
return clean.length > max ? `${clean.slice(0, max - 1)}\u2026` : clean;
|
|
1098
|
-
}
|
|
1099
|
-
var SHELLS = /* @__PURE__ */ new Set(["bash", "sh", "zsh", "dash", "fish", "pwsh", "powershell", "cmd"]);
|
|
1100
|
-
function commandWord(command) {
|
|
1101
|
-
let words2 = command.trim().split(/\s+/).filter(Boolean);
|
|
1102
|
-
while (words2.length > 0 && /^[A-Za-z_][A-Za-z0-9_]*=/.test(words2[0]))
|
|
1103
|
-
words2 = words2.slice(1);
|
|
1104
|
-
if (words2[0] === "env") {
|
|
1105
|
-
words2 = words2.slice(1);
|
|
1106
|
-
while (words2.length > 0 && (/^[A-Za-z_][A-Za-z0-9_]*=/.test(words2[0]) || words2[0].startsWith("-")))
|
|
1107
|
-
words2 = words2.slice(1);
|
|
957
|
+
if (c === 34 || c === 39) {
|
|
958
|
+
return parseString(ctx);
|
|
1108
959
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
return commandWord(words2.slice(2).join(" ").replace(/^["']/, "")) ?? name;
|
|
960
|
+
if (c === 116) {
|
|
961
|
+
if (ctx.s.charCodeAt(++ctx.p) !== 114 || ctx.s.charCodeAt(++ctx.p) !== 117 || ctx.s.charCodeAt(++ctx.p) !== 101)
|
|
962
|
+
throw new TomlError("invalid value", { toml: ctx.s, ptr });
|
|
963
|
+
ctx.p++;
|
|
964
|
+
return true;
|
|
1115
965
|
}
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
}
|
|
1122
|
-
function isWindowsPath(p) {
|
|
1123
|
-
return /^[A-Za-z]:[\\/]/.test(p) || p.startsWith("\\\\");
|
|
1124
|
-
}
|
|
1125
|
-
function relativeTarget(p, root) {
|
|
1126
|
-
if (!root)
|
|
1127
|
-
return baseName(p);
|
|
1128
|
-
const win = isWindowsPath(p) || isWindowsPath(root);
|
|
1129
|
-
const lib = win ? path5.win32 : path5.posix;
|
|
1130
|
-
if (!lib.isAbsolute(p))
|
|
1131
|
-
return p.replace(/\\/g, "/");
|
|
1132
|
-
const rel = lib.relative(win ? root : root.replace(/\\/g, "/"), p);
|
|
1133
|
-
if (!rel || rel.startsWith("..") || lib.isAbsolute(rel))
|
|
1134
|
-
return `\u2026/${baseName(p)}`;
|
|
1135
|
-
return rel.replace(/\\/g, "/");
|
|
1136
|
-
}
|
|
1137
|
-
function urlHost(u) {
|
|
1138
|
-
try {
|
|
1139
|
-
return new URL(u).host || void 0;
|
|
1140
|
-
} catch {
|
|
1141
|
-
return void 0;
|
|
966
|
+
if (c === 102) {
|
|
967
|
+
if (ctx.s.charCodeAt(++ctx.p) !== 97 || ctx.s.charCodeAt(++ctx.p) !== 108 || ctx.s.charCodeAt(++ctx.p) !== 115 || ctx.s.charCodeAt(++ctx.p) !== 101)
|
|
968
|
+
throw new TomlError("invalid value", { toml: ctx.s, ptr });
|
|
969
|
+
ctx.p++;
|
|
970
|
+
return false;
|
|
1142
971
|
}
|
|
972
|
+
return parseValue(ctx, integersAsBigInt, end);
|
|
1143
973
|
}
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
return urlHost(url);
|
|
1158
|
-
const changes = i.changes;
|
|
1159
|
-
if (Array.isArray(changes) && changes.length > 0) {
|
|
1160
|
-
const first = changes[0];
|
|
1161
|
-
if (typeof first?.path === "string")
|
|
1162
|
-
return first.path;
|
|
974
|
+
|
|
975
|
+
// node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/struct.js
|
|
976
|
+
var KEY_PART_RE = /^[a-zA-Z0-9-_]+[ \t]*$/;
|
|
977
|
+
function parseKey(ctx, end = "=") {
|
|
978
|
+
let start = ctx.p;
|
|
979
|
+
let dot = start - 1;
|
|
980
|
+
let parsed = [];
|
|
981
|
+
let endPtr = ctx.s.indexOf(end, start);
|
|
982
|
+
if (endPtr < 0) {
|
|
983
|
+
throw new TomlError("incomplete key-value: cannot find end of key", {
|
|
984
|
+
toml: ctx.s,
|
|
985
|
+
ptr: start
|
|
986
|
+
});
|
|
1163
987
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
988
|
+
do {
|
|
989
|
+
let c = ctx.s.charCodeAt(ctx.p = ++dot);
|
|
990
|
+
if (c !== 32 && c !== 9) {
|
|
991
|
+
if (c === 34 || c === 39) {
|
|
992
|
+
if (c === ctx.s.charCodeAt(ctx.p + 1) && c === ctx.s.charCodeAt(ctx.p + 2)) {
|
|
993
|
+
throw new TomlError("multiline strings are not allowed in keys", {
|
|
994
|
+
toml: ctx.s,
|
|
995
|
+
ptr: ctx.p
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
let part = parseString(ctx);
|
|
999
|
+
dot = ctx.s.indexOf(".", ctx.p);
|
|
1000
|
+
let strEnd = ctx.s.slice(ctx.p, dot < 0 || dot > endPtr ? endPtr : dot);
|
|
1001
|
+
let newLine = indexOfNewline(strEnd);
|
|
1002
|
+
if (newLine > -1) {
|
|
1003
|
+
throw new TomlError("newlines are not allowed in keys", {
|
|
1004
|
+
toml: ctx.s,
|
|
1005
|
+
ptr: newLine
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
if (strEnd.trimStart()) {
|
|
1009
|
+
throw new TomlError("found extra tokens after the string part", {
|
|
1010
|
+
toml: ctx.s,
|
|
1011
|
+
ptr: ctx.p
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
if (endPtr < ctx.p) {
|
|
1015
|
+
endPtr = ctx.s.indexOf(end, ctx.p);
|
|
1016
|
+
if (endPtr < 0) {
|
|
1017
|
+
throw new TomlError("incomplete key-value: cannot find end of key", {
|
|
1018
|
+
toml: ctx.s,
|
|
1019
|
+
ptr: start
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
parsed.push(part);
|
|
1024
|
+
} else {
|
|
1025
|
+
dot = ctx.s.indexOf(".", ctx.p);
|
|
1026
|
+
let part = ctx.s.slice(ctx.p, dot < 0 || dot > endPtr ? endPtr : dot);
|
|
1027
|
+
if (!KEY_PART_RE.test(part)) {
|
|
1028
|
+
throw new TomlError("only letter, numbers, dashes and underscores are allowed in keys", {
|
|
1029
|
+
toml: ctx.s,
|
|
1030
|
+
ptr: ctx.p
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
parsed.push(part.trimEnd());
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
} while (dot + 1 && dot < endPtr);
|
|
1037
|
+
ctx.p = endPtr + 1;
|
|
1038
|
+
skipVoid(ctx, true, true);
|
|
1039
|
+
return parsed;
|
|
1170
1040
|
}
|
|
1171
|
-
function
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1041
|
+
function parseInlineTable(ctx, integersAsBigInt) {
|
|
1042
|
+
let res = {};
|
|
1043
|
+
let seen = /* @__PURE__ */ new Set();
|
|
1044
|
+
let c;
|
|
1045
|
+
ctx.p++;
|
|
1046
|
+
while (ctx.p < ctx.s.length) {
|
|
1047
|
+
skipVoid(ctx);
|
|
1048
|
+
if ((c = ctx.s.charCodeAt(ctx.p)) === 125) {
|
|
1049
|
+
ctx.p++;
|
|
1050
|
+
return res;
|
|
1178
1051
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
if (
|
|
1189
|
-
|
|
1190
|
-
|
|
1052
|
+
let k;
|
|
1053
|
+
let t = res;
|
|
1054
|
+
let hasOwn = false;
|
|
1055
|
+
let p = ctx.p;
|
|
1056
|
+
let key = parseKey(ctx);
|
|
1057
|
+
for (let i = 0; i < key.length; i++) {
|
|
1058
|
+
if (i)
|
|
1059
|
+
t = hasOwn ? t[k] : t[k] = {};
|
|
1060
|
+
k = key[i];
|
|
1061
|
+
if ((hasOwn = Object.hasOwn(t, k)) && (typeof t[k] !== "object" || seen.has(t[k]))) {
|
|
1062
|
+
throw new TomlError("trying to redefine an already defined value", {
|
|
1063
|
+
toml: ctx.s,
|
|
1064
|
+
ptr: p
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
if (!hasOwn && k === "__proto__") {
|
|
1068
|
+
Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
if (hasOwn) {
|
|
1072
|
+
throw new TomlError("trying to redefine an already defined value", {
|
|
1073
|
+
toml: ctx.s,
|
|
1074
|
+
ptr: ctx.p
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
let value = extractValue(ctx, 125, integersAsBigInt);
|
|
1078
|
+
seen.add(t[k] = value);
|
|
1079
|
+
skipVoid(ctx);
|
|
1080
|
+
if ((c = ctx.s.charCodeAt(ctx.p++)) === 125) {
|
|
1081
|
+
return res;
|
|
1082
|
+
}
|
|
1083
|
+
if (c !== 44) {
|
|
1084
|
+
throw new TomlError("expected comma or end of structure", { toml: ctx.s, ptr: ctx.p - 1 });
|
|
1191
1085
|
}
|
|
1192
|
-
default:
|
|
1193
|
-
return void 0;
|
|
1194
1086
|
}
|
|
1087
|
+
throw new TomlError("unfinished table encountered", {
|
|
1088
|
+
toml: ctx.s,
|
|
1089
|
+
ptr: ctx.p
|
|
1090
|
+
});
|
|
1195
1091
|
}
|
|
1196
|
-
function
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
if (
|
|
1209
|
-
return
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
return void 0;
|
|
1092
|
+
function parseArray(ctx, integersAsBigInt) {
|
|
1093
|
+
let res = [];
|
|
1094
|
+
let c;
|
|
1095
|
+
ctx.p++;
|
|
1096
|
+
while (ctx.p < ctx.s.length) {
|
|
1097
|
+
skipVoid(ctx);
|
|
1098
|
+
if ((c = ctx.s.charCodeAt(ctx.p)) === 93) {
|
|
1099
|
+
ctx.p++;
|
|
1100
|
+
return res;
|
|
1101
|
+
}
|
|
1102
|
+
res.push(extractValue(ctx, 93, integersAsBigInt));
|
|
1103
|
+
skipVoid(ctx);
|
|
1104
|
+
if ((c = ctx.s.charCodeAt(ctx.p++)) === 93) {
|
|
1105
|
+
return res;
|
|
1106
|
+
}
|
|
1107
|
+
if (c !== 44) {
|
|
1108
|
+
throw new TomlError("expected comma or end of structure", { toml: ctx.s, ptr: ctx.p - 1 });
|
|
1109
|
+
}
|
|
1215
1110
|
}
|
|
1111
|
+
throw new TomlError("unfinished array encountered", {
|
|
1112
|
+
toml: ctx.s,
|
|
1113
|
+
ptr: ctx.p
|
|
1114
|
+
});
|
|
1216
1115
|
}
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1116
|
+
|
|
1117
|
+
// node_modules/.pnpm/smol-toml@1.8.0/node_modules/smol-toml/dist/parse.js
|
|
1118
|
+
function peekTable(key, table, meta, type) {
|
|
1119
|
+
let t = table;
|
|
1120
|
+
let m = meta;
|
|
1121
|
+
let k;
|
|
1122
|
+
let hasOwn = false;
|
|
1123
|
+
let state;
|
|
1124
|
+
for (let i = 0; i < key.length; i++) {
|
|
1125
|
+
if (i) {
|
|
1126
|
+
t = hasOwn ? t[k] : t[k] = {};
|
|
1127
|
+
m = (state = m[k]).c;
|
|
1128
|
+
if (type === 0 && (state.t === 1 || state.t === 2)) {
|
|
1129
|
+
return null;
|
|
1130
|
+
}
|
|
1131
|
+
if (state.t === 2) {
|
|
1132
|
+
let l = t.length - 1;
|
|
1133
|
+
t = t[l];
|
|
1134
|
+
m = m[l].c;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
k = key[i];
|
|
1138
|
+
if ((hasOwn = Object.hasOwn(t, k)) && m[k]?.t === 0 && m[k]?.d) {
|
|
1139
|
+
return null;
|
|
1140
|
+
}
|
|
1141
|
+
if (!hasOwn) {
|
|
1142
|
+
if (k === "__proto__") {
|
|
1143
|
+
Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
|
|
1144
|
+
Object.defineProperty(m, k, { enumerable: true, configurable: true, writable: true });
|
|
1145
|
+
}
|
|
1146
|
+
m[k] = {
|
|
1147
|
+
t: i < key.length - 1 && type === 2 ? 3 : type,
|
|
1148
|
+
d: false,
|
|
1149
|
+
i: 0,
|
|
1150
|
+
c: {}
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1231
1153
|
}
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
this.offer({ at: new Date(this.now()).toISOString(), ...derived });
|
|
1154
|
+
state = m[k];
|
|
1155
|
+
if (state.t !== type && !(type === 1 && state.t === 3)) {
|
|
1156
|
+
return null;
|
|
1236
1157
|
}
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
return;
|
|
1242
|
-
}
|
|
1243
|
-
this.pending = a;
|
|
1244
|
-
if (!this.timer) {
|
|
1245
|
-
this.timer = setTimeout(() => {
|
|
1246
|
-
this.timer = void 0;
|
|
1247
|
-
this.flush();
|
|
1248
|
-
}, Math.max(0, this.throttleMs - (t - this.lastWriteAt)));
|
|
1249
|
-
this.timer.unref?.();
|
|
1158
|
+
if (type === 2) {
|
|
1159
|
+
if (!state.d) {
|
|
1160
|
+
state.d = true;
|
|
1161
|
+
t[k] = [];
|
|
1250
1162
|
}
|
|
1163
|
+
t[k].push(t = {});
|
|
1164
|
+
state.c[state.i++] = state = { t: 1, d: false, i: 0, c: {} };
|
|
1251
1165
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
if (!this.pending)
|
|
1255
|
-
return;
|
|
1256
|
-
const a = this.pending;
|
|
1257
|
-
this.emit(a, this.now());
|
|
1166
|
+
if (state.d) {
|
|
1167
|
+
return null;
|
|
1258
1168
|
}
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1169
|
+
state.d = true;
|
|
1170
|
+
if (type === 1) {
|
|
1171
|
+
t = hasOwn ? t[k] : t[k] = {};
|
|
1172
|
+
} else if (type === 0 && hasOwn) {
|
|
1173
|
+
return null;
|
|
1264
1174
|
}
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1175
|
+
return [k, t, state.c];
|
|
1176
|
+
}
|
|
1177
|
+
function parse(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
|
|
1178
|
+
let ctx = { s: toml, p: 0, d: maxDepth };
|
|
1179
|
+
let res = {};
|
|
1180
|
+
let meta = {};
|
|
1181
|
+
let tmp;
|
|
1182
|
+
let tbl = res;
|
|
1183
|
+
let m = meta;
|
|
1184
|
+
skipVoid(ctx);
|
|
1185
|
+
while (ctx.p < toml.length) {
|
|
1186
|
+
if (toml.charCodeAt(ctx.p) === 91) {
|
|
1187
|
+
let isTableArray = toml.charCodeAt(++ctx.p) === 91;
|
|
1188
|
+
tmp = ctx.p += +isTableArray;
|
|
1189
|
+
let k = parseKey(ctx, "]");
|
|
1190
|
+
if (isTableArray) {
|
|
1191
|
+
if (toml.charCodeAt(ctx.p - 1) !== 93) {
|
|
1192
|
+
throw new TomlError("expected end of table declaration", {
|
|
1193
|
+
toml,
|
|
1194
|
+
ptr: ctx.p - 1
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1197
|
+
ctx.p++;
|
|
1198
|
+
}
|
|
1199
|
+
let p = peekTable(
|
|
1200
|
+
k,
|
|
1201
|
+
res,
|
|
1202
|
+
meta,
|
|
1203
|
+
isTableArray ? 2 : 1
|
|
1204
|
+
/* Type.EXPLICIT */
|
|
1205
|
+
);
|
|
1206
|
+
if (!p) {
|
|
1207
|
+
throw new TomlError("trying to redefine an already defined table or value", {
|
|
1208
|
+
toml,
|
|
1209
|
+
ptr: tmp
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
m = p[2];
|
|
1213
|
+
tbl = p[1];
|
|
1214
|
+
} else {
|
|
1215
|
+
tmp = ctx.p;
|
|
1216
|
+
let k = parseKey(ctx);
|
|
1217
|
+
let p = peekTable(
|
|
1218
|
+
k,
|
|
1219
|
+
tbl,
|
|
1220
|
+
m,
|
|
1221
|
+
0
|
|
1222
|
+
/* Type.DOTTED */
|
|
1223
|
+
);
|
|
1224
|
+
if (!p) {
|
|
1225
|
+
throw new TomlError("trying to redefine an already defined table or value", {
|
|
1226
|
+
toml,
|
|
1227
|
+
ptr: tmp
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
p[1][p[0]] = extractValue(ctx, void 0, integersAsBigInt);
|
|
1270
1231
|
}
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1232
|
+
skipVoid(ctx, true);
|
|
1233
|
+
if (ctx.p < toml.length && (tmp = toml.charCodeAt(ctx.p)) !== 10 && tmp !== 13) {
|
|
1234
|
+
throw new TomlError("each key-value declaration must be followed by an end-of-line", {
|
|
1235
|
+
toml,
|
|
1236
|
+
ptr: ctx.p
|
|
1237
|
+
});
|
|
1276
1238
|
}
|
|
1239
|
+
skipVoid(ctx);
|
|
1277
1240
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
// packages/core/dist/status.js
|
|
1281
|
-
function isVerb(v) {
|
|
1282
|
-
return VERBS.includes(v);
|
|
1283
|
-
}
|
|
1284
|
-
function isWaitingOn(v) {
|
|
1285
|
-
return WAITING_ON.includes(v);
|
|
1286
|
-
}
|
|
1287
|
-
var DURATION = /^(\d+(?:\.\d+)?)\s*(s|m|h|d)$/i;
|
|
1288
|
-
var UNIT_MS = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
|
|
1289
|
-
function parseUntil(v, now2 = Date.now()) {
|
|
1290
|
-
const d = DURATION.exec(v.trim());
|
|
1291
|
-
if (d)
|
|
1292
|
-
return new Date(now2 + Number(d[1]) * UNIT_MS[d[2].toLowerCase()]).toISOString();
|
|
1293
|
-
const t = Date.parse(v);
|
|
1294
|
-
if (Number.isNaN(t))
|
|
1295
|
-
throw new Error(`invalid --until "${v}" \u2014 an ISO time (2026-10-01T09:00Z) or a duration (30m, 4h, 2d)`);
|
|
1296
|
-
return new Date(t).toISOString();
|
|
1241
|
+
return res;
|
|
1297
1242
|
}
|
|
1298
|
-
|
|
1243
|
+
|
|
1244
|
+
// packages/core/dist/config.js
|
|
1245
|
+
var BRIEF_KINDS = ["conflict", "checks", "review", "ciFix", "rebase"];
|
|
1246
|
+
function parseBriefHooks(raw) {
|
|
1247
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
1248
|
+
return void 0;
|
|
1299
1249
|
const out = {};
|
|
1300
|
-
|
|
1301
|
-
|
|
1250
|
+
for (const kind of [...BRIEF_KINDS, "all"]) {
|
|
1251
|
+
const text = raw[kind];
|
|
1252
|
+
if (typeof text === "string" && text.trim())
|
|
1253
|
+
out[kind] = text.trim();
|
|
1302
1254
|
}
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1255
|
+
return Object.keys(out).length ? out : void 0;
|
|
1256
|
+
}
|
|
1257
|
+
var ATTENTION_KINDS = ["question", "decision", "landed", "pr:draft", "pr:review", "pr:checks", "pr:conflict", "pr:ready", "report", "stack-ready"];
|
|
1258
|
+
var DEFAULT_ATTENTION_KINDS = ["question", "decision", "pr:ready", "pr:review", "pr:conflict", "pr:checks"];
|
|
1259
|
+
function parseAttentionKinds(raw) {
|
|
1260
|
+
if (raw === void 0)
|
|
1261
|
+
return [...DEFAULT_ATTENTION_KINDS];
|
|
1262
|
+
if (!Array.isArray(raw)) {
|
|
1263
|
+
throw new Error(`attentionKinds must be an array of kinds (${ATTENTION_KINDS.join(", ")}) in ${configPath()}`);
|
|
1264
|
+
}
|
|
1265
|
+
for (const k of raw) {
|
|
1266
|
+
if (!ATTENTION_KINDS.includes(k)) {
|
|
1267
|
+
throw new Error(`attentionKinds: unknown kind "${String(k)}" in ${configPath()} \u2014 valid kinds: ${ATTENTION_KINDS.join(", ")}`);
|
|
1308
1268
|
}
|
|
1309
|
-
out.waitingOn = w.waitingOn;
|
|
1310
1269
|
}
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1270
|
+
return [...new Set(raw)];
|
|
1271
|
+
}
|
|
1272
|
+
function parseSoak(raw) {
|
|
1273
|
+
const soak = { ...DEFAULT_SOAK, ...raw ?? {} };
|
|
1274
|
+
if (soak.terminal !== void 0 && soak.terminal !== "terminal" && soak.terminal !== "iterm") {
|
|
1275
|
+
throw new Error(`soak.terminal: unknown terminal "${String(soak.terminal)}" in ${configPath()} \u2014 use "terminal" or "iterm"`);
|
|
1276
|
+
}
|
|
1277
|
+
return soak;
|
|
1278
|
+
}
|
|
1279
|
+
var DEFAULT_SOAK = {
|
|
1280
|
+
deferSecs: 90,
|
|
1281
|
+
claimIdleNoticeSecs: 180,
|
|
1282
|
+
ttlSecs: 1800,
|
|
1283
|
+
beat: true,
|
|
1284
|
+
pausedTtlSecs: 86400,
|
|
1285
|
+
signOffGraceSecs: 600
|
|
1286
|
+
};
|
|
1287
|
+
var DEFAULT_HELM = {
|
|
1288
|
+
ttlSecs: 1800,
|
|
1289
|
+
reportSecs: 900,
|
|
1290
|
+
armGraceSecs: 5
|
|
1291
|
+
};
|
|
1292
|
+
var DEFAULT_GLASS = { port: 4949 };
|
|
1293
|
+
var DEFAULT_WATCH = { maxForksPerCycle: 3, autoRepair: true, conflicts: true, checks: true, maxRepairsPerPr: 2, maxRepairsWithoutProgress: 2, repairSettleSecs: 600, repairTrapWaitSecs: 600 };
|
|
1294
|
+
var DEFAULT_LIMITS = {
|
|
1295
|
+
maxConcurrent: 2,
|
|
1296
|
+
choreConcurrent: 1,
|
|
1297
|
+
wedgeThresholdSecs: 600,
|
|
1298
|
+
maxRestartAttempts: 2,
|
|
1299
|
+
wallClockSecs: 3600,
|
|
1300
|
+
pushEarly: true,
|
|
1301
|
+
draftPr: true,
|
|
1302
|
+
checkpointOnStop: true,
|
|
1303
|
+
backgroundWaitSecs: 1800,
|
|
1304
|
+
exitGraceSecs: 30,
|
|
1305
|
+
choreRetentionDays: 7,
|
|
1306
|
+
attachmentMaxBytes: 25 * 1024 * 1024,
|
|
1307
|
+
retentionDays: 0,
|
|
1308
|
+
minFreeGB: 0,
|
|
1309
|
+
reuseWorktree: true,
|
|
1310
|
+
releaseOnMerge: false
|
|
1311
|
+
};
|
|
1312
|
+
function configPath() {
|
|
1313
|
+
return path5.join(lobstahHome(), "config.toml");
|
|
1314
|
+
}
|
|
1315
|
+
function expandHome(p) {
|
|
1316
|
+
return p.startsWith("~/") ? path5.join(os2.homedir(), p.slice(2)) : p;
|
|
1317
|
+
}
|
|
1318
|
+
function loadConfig() {
|
|
1319
|
+
const file = configPath();
|
|
1320
|
+
const raw = fs7.existsSync(file) ? parse(fs7.readFileSync(file, "utf8")) : {};
|
|
1321
|
+
const readySettleSecs = raw.readySettleSecs ?? 600;
|
|
1322
|
+
if (typeof readySettleSecs !== "number" || !Number.isFinite(readySettleSecs) || readySettleSecs < 0) {
|
|
1323
|
+
throw new Error(`readySettleSecs must be a non-negative number in ${configPath()}`);
|
|
1324
|
+
}
|
|
1325
|
+
const reposRaw = raw.repos ?? {};
|
|
1326
|
+
const repos = {};
|
|
1327
|
+
for (const [key, r] of Object.entries(reposRaw)) {
|
|
1328
|
+
repos[key] = {
|
|
1329
|
+
path: expandHome(String(r.path ?? "")),
|
|
1330
|
+
origin: r.origin ? String(r.origin) : void 0,
|
|
1331
|
+
trunk: String(r.trunk ?? "main"),
|
|
1332
|
+
setup: Array.isArray(r.setup) ? r.setup.map(String) : void 0,
|
|
1333
|
+
env: r.env ?? void 0,
|
|
1334
|
+
harness: r.harness ?? void 0,
|
|
1335
|
+
pickup: r.pickup === void 0 ? void 0 : Boolean(r.pickup),
|
|
1336
|
+
scratch: Array.isArray(r.scratch) ? r.scratch.map(String) : void 0,
|
|
1337
|
+
pushEarly: r.pushEarly === void 0 ? void 0 : Boolean(r.pushEarly),
|
|
1338
|
+
draftPr: r.draftPr === void 0 ? void 0 : Boolean(r.draftPr),
|
|
1339
|
+
checkpointOnStop: r.checkpointOnStop === void 0 ? void 0 : Boolean(r.checkpointOnStop),
|
|
1340
|
+
humanGateChecks: Array.isArray(r.humanGateChecks) ? r.humanGateChecks.map(String).filter(Boolean) : void 0,
|
|
1341
|
+
briefHooks: parseBriefHooks(r.briefHooks)
|
|
1342
|
+
};
|
|
1322
1343
|
}
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
}
|
|
1327
|
-
function appendStatus(id, lane, verb, note, at, waiting, reported = false) {
|
|
1328
|
-
if (!isVerb(verb)) {
|
|
1329
|
-
throw new Error(`invalid status verb "${verb}" \u2014 must be one of: ${VERBS.join(", ")}`);
|
|
1344
|
+
const groundsRaw = raw.grounds ?? {};
|
|
1345
|
+
const grounds = {};
|
|
1346
|
+
for (const [key, g] of Object.entries(groundsRaw)) {
|
|
1347
|
+
grounds[key] = { repos: Array.isArray(g.repos) ? g.repos.map(String) : [] };
|
|
1330
1348
|
}
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
...
|
|
1337
|
-
|
|
1349
|
+
return {
|
|
1350
|
+
repos,
|
|
1351
|
+
harness: raw.harness ?? {},
|
|
1352
|
+
limits: { ...DEFAULT_LIMITS, ...raw.limits ?? {} },
|
|
1353
|
+
soak: parseSoak(raw.soak),
|
|
1354
|
+
helm: { ...DEFAULT_HELM, ...raw.helm ?? {} },
|
|
1355
|
+
glass: { ...DEFAULT_GLASS, ...raw.glass ?? {} },
|
|
1356
|
+
watch: { ...DEFAULT_WATCH, ...raw.watch ?? {} },
|
|
1357
|
+
grounds,
|
|
1358
|
+
notifyCommand: raw.notifyCommand ? String(raw.notifyCommand) : void 0,
|
|
1359
|
+
notifyVerbs: Array.isArray(raw.notifyVerbs) ? raw.notifyVerbs.map(String) : void 0,
|
|
1360
|
+
remindSecs: raw.remindSecs !== void 0 ? Number(raw.remindSecs) : void 0,
|
|
1361
|
+
readySettleSecs,
|
|
1362
|
+
attentionKinds: parseAttentionKinds(raw.attentionKinds)
|
|
1338
1363
|
};
|
|
1339
|
-
fs6.appendFileSync(statusPath(id, lane), `${JSON.stringify(entry)}
|
|
1340
|
-
`);
|
|
1341
|
-
if (verb === "done") {
|
|
1342
|
-
try {
|
|
1343
|
-
recordCatch(id, lane);
|
|
1344
|
-
} catch {
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
return entry;
|
|
1348
|
-
}
|
|
1349
|
-
function readStatusLog(id, lane) {
|
|
1350
|
-
const file = statusPath(id, lane);
|
|
1351
|
-
if (!fs6.existsSync(file))
|
|
1352
|
-
return [];
|
|
1353
|
-
return fs6.readFileSync(file, "utf8").split("\n").filter(Boolean).flatMap((line) => {
|
|
1354
|
-
try {
|
|
1355
|
-
const parsed = JSON.parse(line);
|
|
1356
|
-
return isVerb(parsed.verb) ? [parsed] : [];
|
|
1357
|
-
} catch {
|
|
1358
|
-
return [];
|
|
1359
|
-
}
|
|
1360
|
-
});
|
|
1361
1364
|
}
|
|
1362
|
-
function
|
|
1363
|
-
|
|
1365
|
+
function resolveDispatch(d, cfg) {
|
|
1366
|
+
const repo = cfg.repos[d.repo];
|
|
1367
|
+
if (!repo)
|
|
1368
|
+
throw new Error(`unknown repo key "${d.repo}" \u2014 add it to ${configPath()}`);
|
|
1369
|
+
const rh = repo.harness ?? {};
|
|
1370
|
+
const gh = cfg.harness;
|
|
1371
|
+
return {
|
|
1372
|
+
harness: d.harness ?? rh.default ?? gh.default ?? "claude",
|
|
1373
|
+
model: d.model ?? rh.model ?? gh.model,
|
|
1374
|
+
effort: d.effort ?? rh.effort ?? gh.effort,
|
|
1375
|
+
limits: {
|
|
1376
|
+
wallClockSecs: cfg.limits.wallClockSecs,
|
|
1377
|
+
...d.limits ?? {}
|
|
1378
|
+
},
|
|
1379
|
+
env: { ...repo.env ?? {}, ...d.env ?? {} },
|
|
1380
|
+
flags: d.flags ?? []
|
|
1381
|
+
};
|
|
1364
1382
|
}
|
|
1365
1383
|
|
|
1366
|
-
// packages/core/dist/
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1384
|
+
// packages/core/dist/pr.js
|
|
1385
|
+
import { spawnSync } from "node:child_process";
|
|
1386
|
+
|
|
1387
|
+
// packages/core/dist/gh-errors.js
|
|
1388
|
+
function firstMeaningfulLine(...outputs) {
|
|
1389
|
+
for (const out of outputs) {
|
|
1390
|
+
for (const raw of (out ?? "").split("\n")) {
|
|
1391
|
+
let line = raw.trim();
|
|
1392
|
+
if (!line || /^(warning:|hint:|\(node:|\{|\})/i.test(line))
|
|
1393
|
+
continue;
|
|
1394
|
+
const m = /^error:\s*(.*)$/.exec(line);
|
|
1395
|
+
if (m)
|
|
1396
|
+
line = m[1].replace(/^"(.*)"$/, "$1").trim();
|
|
1397
|
+
line = line.replace(/^(gh|GraphQL):\s*/i, "");
|
|
1398
|
+
if (line)
|
|
1399
|
+
return line;
|
|
1377
1400
|
}
|
|
1378
1401
|
}
|
|
1379
1402
|
return void 0;
|
|
1380
1403
|
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1404
|
+
|
|
1405
|
+
// packages/core/dist/pr.js
|
|
1406
|
+
function parsePrRef(s) {
|
|
1407
|
+
const m = /^(?:pr:)?([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)#(\d+)$/.exec(s.trim()) ?? /^https?:\/\/github\.com\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)\/pull\/(\d+)(?:[/?#].*)?$/.exec(s.trim());
|
|
1408
|
+
if (!m)
|
|
1409
|
+
return void 0;
|
|
1410
|
+
const [, owner, repo, n] = m;
|
|
1411
|
+
const number = Number(n);
|
|
1412
|
+
return { owner, repo, number, key: `pr:${owner}/${repo}#${number}`, url: `https://github.com/${owner}/${repo}/pull/${number}` };
|
|
1383
1413
|
}
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1414
|
+
var PR_VIEW_FIELDS = "title,state,isDraft,isCrossRepository,headRefOid,baseRefName,baseRefOid,headRefName,mergeStateStatus,reviewDecision,statusCheckRollup,mergedAt,closedAt,updatedAt,reviews";
|
|
1415
|
+
var PR_VIEW_FIELDS_NO_CHECKS = PR_VIEW_FIELDS.split(",").filter((f) => f !== "statusCheckRollup").join(",");
|
|
1416
|
+
var FORBIDDEN = /resource not accessible by (integration|personal access token)/i;
|
|
1417
|
+
function ghPrView(ref) {
|
|
1418
|
+
const view1 = (fields2) => spawnSync("gh", ["pr", "view", String(ref.number), "--repo", `${ref.owner}/${ref.repo}`, "--json", fields2], {
|
|
1419
|
+
encoding: "utf8",
|
|
1420
|
+
timeout: 6e4
|
|
1421
|
+
});
|
|
1422
|
+
const reason = (r) => firstMeaningfulLine(r.stderr) ?? `gh exited ${r.status}`;
|
|
1423
|
+
const unknownBase = (r) => r.status !== 0 && /unknown json field.*baseRefOid/i.test(r.stderr ?? "");
|
|
1424
|
+
const withoutBase = (fields2) => fields2.split(",").filter((f) => f !== "baseRefOid").join(",");
|
|
1425
|
+
let fields = PR_VIEW_FIELDS;
|
|
1426
|
+
let res = view1(fields);
|
|
1427
|
+
if (unknownBase(res))
|
|
1428
|
+
res = view1(fields = withoutBase(fields));
|
|
1429
|
+
if (res.error)
|
|
1430
|
+
throw new Error(`gh: ${res.error.message}`);
|
|
1431
|
+
let checksError;
|
|
1432
|
+
if (res.status !== 0 && FORBIDDEN.test(res.stderr ?? "")) {
|
|
1433
|
+
checksError = reason(res);
|
|
1434
|
+
const retry = view1(fields === PR_VIEW_FIELDS ? PR_VIEW_FIELDS_NO_CHECKS : withoutBase(PR_VIEW_FIELDS_NO_CHECKS));
|
|
1435
|
+
if (retry.error)
|
|
1436
|
+
throw new Error(`gh: ${retry.error.message}`);
|
|
1437
|
+
if (retry.status !== 0) {
|
|
1438
|
+
throw new Error(FORBIDDEN.test(retry.stderr ?? "") ? `${reason(retry)} (fails even without check results)` : reason(retry));
|
|
1439
|
+
}
|
|
1440
|
+
res = retry;
|
|
1441
|
+
}
|
|
1442
|
+
if (res.status !== 0)
|
|
1443
|
+
throw new Error(reason(res));
|
|
1444
|
+
const view = JSON.parse(res.stdout);
|
|
1445
|
+
if (checksError)
|
|
1446
|
+
view.checksError = checksError;
|
|
1447
|
+
if (view.state === "OPEN") {
|
|
1448
|
+
const threads = ghUnresolvedThreads(ref);
|
|
1449
|
+
if (threads !== void 0)
|
|
1450
|
+
view.unresolvedThreads = threads;
|
|
1451
|
+
}
|
|
1452
|
+
return view;
|
|
1387
1453
|
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1454
|
+
var THREADS_QUERY = "query($owner:String!,$repo:String!,$n:Int!){repository(owner:$owner,name:$repo){pullRequest(number:$n){reviewThreads(first:100){nodes{isResolved}}}}}";
|
|
1455
|
+
function ghUnresolvedThreads(ref) {
|
|
1456
|
+
const res = spawnSync("gh", ["api", "graphql", "-f", `query=${THREADS_QUERY}`, "-f", `owner=${ref.owner}`, "-f", `repo=${ref.repo}`, "-F", `n=${ref.number}`], { encoding: "utf8", timeout: 6e4 });
|
|
1457
|
+
if (res.error || res.status !== 0)
|
|
1458
|
+
return void 0;
|
|
1459
|
+
return parseUnresolvedThreads(res.stdout);
|
|
1390
1460
|
}
|
|
1391
|
-
|
|
1392
|
-
// packages/core/dist/soak.js
|
|
1393
|
-
import * as fs11 from "node:fs";
|
|
1394
|
-
import * as path9 from "node:path";
|
|
1395
|
-
|
|
1396
|
-
// packages/core/dist/evidence.js
|
|
1397
|
-
import * as fs8 from "node:fs";
|
|
1398
|
-
function readEvidence(id, lane) {
|
|
1461
|
+
function parseUnresolvedThreads(stdout) {
|
|
1399
1462
|
try {
|
|
1400
|
-
|
|
1463
|
+
const nodes = JSON.parse(stdout).data?.repository?.pullRequest?.reviewThreads?.nodes;
|
|
1464
|
+
return Array.isArray(nodes) ? nodes.filter((t) => t.isResolved === false).length : void 0;
|
|
1401
1465
|
} catch {
|
|
1402
|
-
return {};
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
function mergeEvidence(id, lane, patch) {
|
|
1406
|
-
const file = evidencePath(id, lane);
|
|
1407
|
-
const tmp = `${file}.tmp-${process.pid}`;
|
|
1408
|
-
fs8.writeFileSync(tmp, JSON.stringify({ ...readEvidence(id, lane), ...patch }, null, 2));
|
|
1409
|
-
fs8.renameSync(tmp, file);
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
// packages/core/dist/pushes.js
|
|
1413
|
-
import * as fs9 from "node:fs";
|
|
1414
|
-
import * as path7 from "node:path";
|
|
1415
|
-
var MAX_PUSHES = 20;
|
|
1416
|
-
var PUSH_HEAD = "HEAD";
|
|
1417
|
-
var VALUE_OPTIONS = /* @__PURE__ */ new Set(["-o", "--push-option", "--repo", "--receive-pack", "--exec"]);
|
|
1418
|
-
function words(segment) {
|
|
1419
|
-
const out = [];
|
|
1420
|
-
const re = /"((?:[^"\\]|\\.)*)"|'([^']*)'|(\S+)/g;
|
|
1421
|
-
for (let m = re.exec(segment); m; m = re.exec(segment))
|
|
1422
|
-
out.push(m[1] ?? m[2] ?? m[3] ?? "");
|
|
1423
|
-
return out;
|
|
1424
|
-
}
|
|
1425
|
-
function branchOf(ref) {
|
|
1426
|
-
const name = ref.replace(/^\+/, "").replace(/^refs\/heads\//, "");
|
|
1427
|
-
return name === "@" || name === "" ? PUSH_HEAD : name;
|
|
1428
|
-
}
|
|
1429
|
-
function gitPushTargets(command) {
|
|
1430
|
-
if (command === void 0)
|
|
1431
|
-
return void 0;
|
|
1432
|
-
const text = typeof command === "string" ? command : command.join(" ");
|
|
1433
|
-
if (!/\bgit\b[^\n]*\bpush\b/.test(text))
|
|
1434
1466
|
return void 0;
|
|
1435
|
-
const targets = [];
|
|
1436
|
-
let found = false;
|
|
1437
|
-
for (const segment of text.split(/&&|\|\||[;|\n]/)) {
|
|
1438
|
-
const w = words(segment);
|
|
1439
|
-
let i = w.findIndex((x) => x === "git" || /[\\/]git(?:\.exe)?$/.test(x));
|
|
1440
|
-
if (i < 0)
|
|
1441
|
-
continue;
|
|
1442
|
-
for (i++; i < w.length && w[i].startsWith("-"); i++)
|
|
1443
|
-
if (w[i] === "-C" || w[i] === "-c")
|
|
1444
|
-
i++;
|
|
1445
|
-
if (w[i] !== "push")
|
|
1446
|
-
continue;
|
|
1447
|
-
found = true;
|
|
1448
|
-
const positional = [];
|
|
1449
|
-
for (let j = i + 1; j < w.length; j++) {
|
|
1450
|
-
const arg = w[j];
|
|
1451
|
-
if (arg === "--")
|
|
1452
|
-
continue;
|
|
1453
|
-
if (arg.startsWith("-")) {
|
|
1454
|
-
if (VALUE_OPTIONS.has(arg))
|
|
1455
|
-
j++;
|
|
1456
|
-
continue;
|
|
1457
|
-
}
|
|
1458
|
-
positional.push(arg);
|
|
1459
|
-
}
|
|
1460
|
-
const refspecs = positional.slice(1);
|
|
1461
|
-
if (refspecs.length === 0)
|
|
1462
|
-
targets.push(PUSH_HEAD);
|
|
1463
|
-
for (const spec of refspecs)
|
|
1464
|
-
targets.push(branchOf(spec.includes(":") ? spec.slice(spec.indexOf(":") + 1) : spec));
|
|
1465
1467
|
}
|
|
1466
|
-
return found ? [...new Set(targets)] : void 0;
|
|
1467
1468
|
}
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
if (stat.isDirectory())
|
|
1475
|
-
return dotGit;
|
|
1476
|
-
const m = /^gitdir:\s*(.+)$/m.exec(fs9.readFileSync(dotGit, "utf8"));
|
|
1477
|
-
return m ? path7.resolve(at, m[1].trim()) : void 0;
|
|
1478
|
-
} catch {
|
|
1479
|
-
}
|
|
1480
|
-
const up = path7.dirname(at);
|
|
1481
|
-
if (up === at)
|
|
1482
|
-
return void 0;
|
|
1483
|
-
at = up;
|
|
1484
|
-
}
|
|
1469
|
+
|
|
1470
|
+
// packages/core/dist/prs.js
|
|
1471
|
+
import * as fs8 from "node:fs";
|
|
1472
|
+
import * as path6 from "node:path";
|
|
1473
|
+
function prsDir() {
|
|
1474
|
+
return path6.join(lobstahHome(), "prs");
|
|
1485
1475
|
}
|
|
1486
|
-
function
|
|
1487
|
-
const
|
|
1488
|
-
if (!
|
|
1489
|
-
|
|
1476
|
+
function prRecordFile(key) {
|
|
1477
|
+
const ref = parsePrRef(key);
|
|
1478
|
+
if (!ref)
|
|
1479
|
+
throw new Error(`not a PR key: ${key}`);
|
|
1480
|
+
const safe = (s) => s.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
1481
|
+
return path6.join(prsDir(), `${safe(ref.owner)}__${safe(ref.repo)}__${ref.number}.json`);
|
|
1482
|
+
}
|
|
1483
|
+
function readPr(key) {
|
|
1490
1484
|
try {
|
|
1491
|
-
|
|
1492
|
-
return m ? m[1].trim() : void 0;
|
|
1485
|
+
return JSON.parse(fs8.readFileSync(prRecordFile(key), "utf8"));
|
|
1493
1486
|
} catch {
|
|
1494
1487
|
return void 0;
|
|
1495
1488
|
}
|
|
1496
1489
|
}
|
|
1497
|
-
function
|
|
1498
|
-
|
|
1499
|
-
|
|
1490
|
+
function readPrs() {
|
|
1491
|
+
let files;
|
|
1492
|
+
try {
|
|
1493
|
+
files = fs8.readdirSync(prsDir()).filter((f) => f.endsWith(".json"));
|
|
1494
|
+
} catch {
|
|
1495
|
+
return [];
|
|
1496
|
+
}
|
|
1497
|
+
return files.flatMap((f) => {
|
|
1498
|
+
try {
|
|
1499
|
+
const r = JSON.parse(fs8.readFileSync(path6.join(prsDir(), f), "utf8"));
|
|
1500
|
+
return typeof r.key === "string" && typeof r.url === "string" ? [r] : [];
|
|
1501
|
+
} catch {
|
|
1502
|
+
return [];
|
|
1503
|
+
}
|
|
1504
|
+
});
|
|
1500
1505
|
}
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1506
|
+
|
|
1507
|
+
// packages/core/dist/attachments.js
|
|
1508
|
+
function attachmentBlock(attachments) {
|
|
1509
|
+
if (attachments.length === 0)
|
|
1510
|
+
return "";
|
|
1511
|
+
return [
|
|
1512
|
+
"--- ATTACHMENTS ---",
|
|
1513
|
+
...attachments.map((a) => `${a.name} (${a.type}, ${a.bytes} bytes) at ${a.path}`),
|
|
1514
|
+
"Images and PDFs can be read directly with your file tools."
|
|
1515
|
+
].join("\n");
|
|
1507
1516
|
}
|
|
1508
1517
|
|
|
1509
1518
|
// packages/core/dist/worktrees.js
|
|
1510
|
-
import * as
|
|
1511
|
-
import * as
|
|
1519
|
+
import * as fs9 from "node:fs";
|
|
1520
|
+
import * as path7 from "node:path";
|
|
1512
1521
|
var LANES = ["work", "chore"];
|
|
1513
1522
|
function worktreePath(id) {
|
|
1514
|
-
return
|
|
1523
|
+
return path7.join(lobstahHome(), "worktrees", id);
|
|
1515
1524
|
}
|
|
1516
1525
|
function readRecord(file) {
|
|
1517
1526
|
try {
|
|
1518
|
-
const r = JSON.parse(
|
|
1527
|
+
const r = JSON.parse(fs9.readFileSync(file, "utf8"));
|
|
1519
1528
|
return typeof r.path === "string" ? r : void 0;
|
|
1520
1529
|
} catch {
|
|
1521
1530
|
return void 0;
|
|
1522
1531
|
}
|
|
1523
1532
|
}
|
|
1524
1533
|
function ownerOfPath(p) {
|
|
1525
|
-
const rel =
|
|
1526
|
-
if (!rel || rel.startsWith("..") ||
|
|
1534
|
+
const rel = path7.relative(path7.join(lobstahHome(), "worktrees"), p);
|
|
1535
|
+
if (!rel || rel.startsWith("..") || path7.isAbsolute(rel))
|
|
1527
1536
|
return void 0;
|
|
1528
1537
|
return rel.split(/[\\/]/)[0];
|
|
1529
1538
|
}
|
|
@@ -1536,7 +1545,7 @@ function dispatchWorktree(id, lane) {
|
|
|
1536
1545
|
}
|
|
1537
1546
|
const dirs = laneDirs(l);
|
|
1538
1547
|
for (const dir of [dirs.active, dirs.done]) {
|
|
1539
|
-
const rec = readRecord(
|
|
1548
|
+
const rec = readRecord(path7.join(dir, id, "worktree.json"));
|
|
1540
1549
|
if (rec) {
|
|
1541
1550
|
const owner = rec.of ?? ownerOfPath(rec.path) ?? id;
|
|
1542
1551
|
return { path: rec.path, owner, reused: owner !== id };
|
|
@@ -1548,7 +1557,7 @@ function dispatchWorktree(id, lane) {
|
|
|
1548
1557
|
function laneOf(id) {
|
|
1549
1558
|
for (const l of LANES) {
|
|
1550
1559
|
const d = laneDirs(l);
|
|
1551
|
-
if (
|
|
1560
|
+
if (fs9.existsSync(path7.join(d.queue, `${id}.json`)) || fs9.existsSync(path7.join(d.active, id)) || fs9.existsSync(path7.join(d.done, id))) {
|
|
1552
1561
|
return l;
|
|
1553
1562
|
}
|
|
1554
1563
|
}
|
|
@@ -1568,174 +1577,478 @@ function followUpAncestors(id, lane) {
|
|
|
1568
1577
|
function chainWorktree(followUp) {
|
|
1569
1578
|
for (const id of [followUp, ...followUpAncestors(followUp)]) {
|
|
1570
1579
|
const wt = dispatchWorktree(id, laneOf(id));
|
|
1571
|
-
if (
|
|
1580
|
+
if (fs9.existsSync(wt.path))
|
|
1572
1581
|
return { ...wt, from: id };
|
|
1573
1582
|
}
|
|
1574
1583
|
return void 0;
|
|
1575
1584
|
}
|
|
1576
1585
|
function worktreeGitDir(wt) {
|
|
1577
|
-
const dotGit =
|
|
1586
|
+
const dotGit = path7.join(wt, ".git");
|
|
1578
1587
|
try {
|
|
1579
|
-
const st =
|
|
1588
|
+
const st = fs9.statSync(dotGit);
|
|
1580
1589
|
if (st.isDirectory())
|
|
1581
1590
|
return dotGit;
|
|
1582
|
-
const m = /^gitdir:\s*(.+?)\s*$/m.exec(
|
|
1583
|
-
return m ?
|
|
1591
|
+
const m = /^gitdir:\s*(.+?)\s*$/m.exec(fs9.readFileSync(dotGit, "utf8"));
|
|
1592
|
+
return m ? path7.resolve(wt, m[1]) : void 0;
|
|
1584
1593
|
} catch {
|
|
1585
1594
|
return void 0;
|
|
1586
1595
|
}
|
|
1587
1596
|
}
|
|
1588
1597
|
function worktreeLockFile(wt) {
|
|
1589
1598
|
const gitDir = worktreeGitDir(wt);
|
|
1590
|
-
return gitDir ?
|
|
1599
|
+
return gitDir ? path7.join(gitDir, "lobstah.lock") : void 0;
|
|
1591
1600
|
}
|
|
1592
1601
|
function readWorktreeLock(wt) {
|
|
1593
1602
|
const file = worktreeLockFile(wt);
|
|
1594
1603
|
if (!file)
|
|
1595
1604
|
return void 0;
|
|
1596
1605
|
try {
|
|
1597
|
-
return JSON.parse(
|
|
1606
|
+
return JSON.parse(fs9.readFileSync(file, "utf8"));
|
|
1607
|
+
} catch {
|
|
1608
|
+
return void 0;
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
function lockIsLive(lock) {
|
|
1612
|
+
return fs9.existsSync(path7.join(laneDirs(lock.lane).active, lock.id));
|
|
1613
|
+
}
|
|
1614
|
+
function worktreeHolder(wt) {
|
|
1615
|
+
const lock = readWorktreeLock(wt);
|
|
1616
|
+
return lock && lockIsLive(lock) ? lock : void 0;
|
|
1617
|
+
}
|
|
1618
|
+
function acquireWorktreeLock(wt, id, lane) {
|
|
1619
|
+
const file = worktreeLockFile(wt);
|
|
1620
|
+
const mine = { id, lane, pid: process.pid, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1621
|
+
if (!file)
|
|
1622
|
+
return { id: "(no git dir)", lane, pid: 0, at: mine.at };
|
|
1623
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
1624
|
+
try {
|
|
1625
|
+
fs9.writeFileSync(file, JSON.stringify(mine, null, 2), { flag: "wx" });
|
|
1626
|
+
return void 0;
|
|
1627
|
+
} catch (err) {
|
|
1628
|
+
if (err.code !== "EEXIST")
|
|
1629
|
+
throw err;
|
|
1630
|
+
}
|
|
1631
|
+
let raw;
|
|
1632
|
+
try {
|
|
1633
|
+
raw = fs9.readFileSync(file, "utf8");
|
|
1634
|
+
} catch {
|
|
1635
|
+
continue;
|
|
1636
|
+
}
|
|
1637
|
+
let held2;
|
|
1638
|
+
try {
|
|
1639
|
+
held2 = JSON.parse(raw);
|
|
1640
|
+
} catch {
|
|
1641
|
+
held2 = void 0;
|
|
1642
|
+
}
|
|
1643
|
+
if (held2?.id === id) {
|
|
1644
|
+
fs9.writeFileSync(file, JSON.stringify(mine, null, 2));
|
|
1645
|
+
return void 0;
|
|
1646
|
+
}
|
|
1647
|
+
if (held2 && lockIsLive(held2))
|
|
1648
|
+
return held2;
|
|
1649
|
+
try {
|
|
1650
|
+
if (fs9.readFileSync(file, "utf8") === raw)
|
|
1651
|
+
fs9.rmSync(file, { force: true });
|
|
1652
|
+
} catch {
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
const held = readWorktreeLock(wt);
|
|
1656
|
+
return held ?? { id: "(contended)", lane, pid: 0, at: mine.at };
|
|
1657
|
+
}
|
|
1658
|
+
function releaseWorktreeLock(wt, id) {
|
|
1659
|
+
const file = worktreeLockFile(wt);
|
|
1660
|
+
if (!file)
|
|
1661
|
+
return;
|
|
1662
|
+
if (readWorktreeLock(wt)?.id === id)
|
|
1663
|
+
fs9.rmSync(file, { force: true });
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
// packages/core/dist/disk.js
|
|
1667
|
+
var GB = 1024 ** 3;
|
|
1668
|
+
|
|
1669
|
+
// packages/core/dist/watch.js
|
|
1670
|
+
import * as fs10 from "node:fs";
|
|
1671
|
+
import * as path8 from "node:path";
|
|
1672
|
+
function watchesDir() {
|
|
1673
|
+
return path8.join(lobstahHome(), "watches");
|
|
1674
|
+
}
|
|
1675
|
+
function slug(key) {
|
|
1676
|
+
return key.replace(/[^A-Za-z0-9._-]+/g, "-");
|
|
1677
|
+
}
|
|
1678
|
+
function watchPath(key) {
|
|
1679
|
+
return path8.join(watchesDir(), `${slug(key)}.json`);
|
|
1680
|
+
}
|
|
1681
|
+
function readWatch(key) {
|
|
1682
|
+
try {
|
|
1683
|
+
return JSON.parse(fs10.readFileSync(watchPath(key), "utf8"));
|
|
1684
|
+
} catch {
|
|
1685
|
+
return void 0;
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
// packages/core/dist/soak.js
|
|
1690
|
+
function regPath(trapId) {
|
|
1691
|
+
return path9.join(soakingDir(), `${trapId}.json`);
|
|
1692
|
+
}
|
|
1693
|
+
function readTrap(trapId) {
|
|
1694
|
+
try {
|
|
1695
|
+
return JSON.parse(fs11.readFileSync(regPath(trapId), "utf8"));
|
|
1696
|
+
} catch {
|
|
1697
|
+
return void 0;
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
function listTraps() {
|
|
1701
|
+
try {
|
|
1702
|
+
return fs11.readdirSync(soakingDir()).filter((f) => f.endsWith(".json")).map((f) => JSON.parse(fs11.readFileSync(path9.join(soakingDir(), f), "utf8"))).filter((r) => typeof r.trapId === "string");
|
|
1703
|
+
} catch {
|
|
1704
|
+
return [];
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
function claimPath(id, lane) {
|
|
1708
|
+
return path9.join(laneDirs(lane).active, id, "claim.json");
|
|
1709
|
+
}
|
|
1710
|
+
function readSessionClaim(id, lane) {
|
|
1711
|
+
try {
|
|
1712
|
+
return JSON.parse(fs11.readFileSync(claimPath(id, lane), "utf8"));
|
|
1713
|
+
} catch {
|
|
1714
|
+
return void 0;
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
// packages/core/dist/stats.js
|
|
1719
|
+
var DAILY_RETENTION_DAYS = 400;
|
|
1720
|
+
function statsPath() {
|
|
1721
|
+
return path10.join(lobstahHome(), "stats.json");
|
|
1722
|
+
}
|
|
1723
|
+
function shiftDay(day, days) {
|
|
1724
|
+
const [y, m, d] = day.split("-").map(Number);
|
|
1725
|
+
return localDay(new Date(y, m - 1, d - days));
|
|
1726
|
+
}
|
|
1727
|
+
function localDay(at = Date.now()) {
|
|
1728
|
+
const d = new Date(at);
|
|
1729
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
1730
|
+
}
|
|
1731
|
+
function readCatchFacts(stateDir, id) {
|
|
1732
|
+
let last;
|
|
1733
|
+
try {
|
|
1734
|
+
for (const line of fs12.readFileSync(path10.join(stateDir, `${id}.status`), "utf8").split("\n")) {
|
|
1735
|
+
if (!line.trim())
|
|
1736
|
+
continue;
|
|
1737
|
+
try {
|
|
1738
|
+
last = JSON.parse(line);
|
|
1739
|
+
} catch {
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
} catch {
|
|
1743
|
+
return { done: false };
|
|
1744
|
+
}
|
|
1745
|
+
if (last?.verb !== "done")
|
|
1746
|
+
return { done: false };
|
|
1747
|
+
let trap;
|
|
1748
|
+
let worker = workerProfile();
|
|
1749
|
+
try {
|
|
1750
|
+
const evidence = JSON.parse(fs12.readFileSync(path10.join(stateDir, `${id}.evidence`), "utf8"));
|
|
1751
|
+
const to = evidence.deliveredTo;
|
|
1752
|
+
if (typeof to === "string" && to.startsWith("wt:"))
|
|
1753
|
+
trap = to;
|
|
1754
|
+
const reg = trap ? readTrap(trap.slice("wt:".length)) : void 0;
|
|
1755
|
+
worker = sanitizeWorker(reg ?? evidence.worker ?? workerProfile({ harness: evidence.harness }));
|
|
1756
|
+
} catch {
|
|
1757
|
+
}
|
|
1758
|
+
return { done: true, worker, ...typeof last.at === "string" ? { at: last.at } : {}, ...trap ? { trap } : {} };
|
|
1759
|
+
}
|
|
1760
|
+
function emptyStore(now2) {
|
|
1761
|
+
return {
|
|
1762
|
+
version: 1,
|
|
1763
|
+
totalCatches: 0,
|
|
1764
|
+
perTrap: {},
|
|
1765
|
+
day: localDay(now2),
|
|
1766
|
+
catchesToday: 0,
|
|
1767
|
+
counted: [],
|
|
1768
|
+
daily: {},
|
|
1769
|
+
utc: { date: new Date(now2).toISOString().slice(0, 10), catches: 0, perTrap: {}, trapWorkers: {}, byWorker: [] }
|
|
1770
|
+
};
|
|
1771
|
+
}
|
|
1772
|
+
function catchDay(facts, now2) {
|
|
1773
|
+
return facts.at && Number.isFinite(Date.parse(facts.at)) ? localDay(facts.at) : localDay(now2);
|
|
1774
|
+
}
|
|
1775
|
+
function addDaily(store, day, now2) {
|
|
1776
|
+
if (day < shiftDay(localDay(now2), DAILY_RETENTION_DAYS - 1))
|
|
1777
|
+
return;
|
|
1778
|
+
store.daily[day] = (store.daily[day] ?? 0) + 1;
|
|
1779
|
+
}
|
|
1780
|
+
function pruneDaily(store, now2) {
|
|
1781
|
+
const oldest = shiftDay(localDay(now2), DAILY_RETENTION_DAYS - 1);
|
|
1782
|
+
for (const day of Object.keys(store.daily))
|
|
1783
|
+
if (day < oldest)
|
|
1784
|
+
delete store.daily[day];
|
|
1785
|
+
}
|
|
1786
|
+
function add(store, facts, now2) {
|
|
1787
|
+
store.totalCatches++;
|
|
1788
|
+
if (facts.trap)
|
|
1789
|
+
store.perTrap[facts.trap] = (store.perTrap[facts.trap] ?? 0) + 1;
|
|
1790
|
+
const today = localDay(now2);
|
|
1791
|
+
if (store.day !== today) {
|
|
1792
|
+
store.day = today;
|
|
1793
|
+
store.catchesToday = 0;
|
|
1794
|
+
}
|
|
1795
|
+
const day = catchDay(facts, now2);
|
|
1796
|
+
if (day === today)
|
|
1797
|
+
store.catchesToday++;
|
|
1798
|
+
addDaily(store, day, now2);
|
|
1799
|
+
pruneDaily(store, now2);
|
|
1800
|
+
const at = facts.at && Number.isFinite(Date.parse(facts.at)) ? facts.at : void 0;
|
|
1801
|
+
const utcDay = new Date(now2).toISOString().slice(0, 10);
|
|
1802
|
+
if (store.utc?.date !== utcDay)
|
|
1803
|
+
store.utc = { date: utcDay, catches: 0, perTrap: {}, trapWorkers: {}, byWorker: [] };
|
|
1804
|
+
if ((at ? new Date(at).toISOString().slice(0, 10) : utcDay) === utcDay) {
|
|
1805
|
+
store.utc.catches++;
|
|
1806
|
+
const worker = sanitizeWorker(facts.worker);
|
|
1807
|
+
if (facts.trap) {
|
|
1808
|
+
store.utc.perTrap[facts.trap] = (store.utc.perTrap[facts.trap] ?? 0) + 1;
|
|
1809
|
+
(store.utc.trapWorkers ??= {})[facts.trap] = worker;
|
|
1810
|
+
} else {
|
|
1811
|
+
const rows = store.utc.byWorker ??= [];
|
|
1812
|
+
const key = JSON.stringify(worker);
|
|
1813
|
+
const row = rows.find((r) => JSON.stringify(sanitizeWorker(r)) === key);
|
|
1814
|
+
if (row)
|
|
1815
|
+
row.today++;
|
|
1816
|
+
else
|
|
1817
|
+
rows.push({ ...worker, today: 1 });
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
function backfillSources() {
|
|
1822
|
+
const live = ["work", "chore"].map((lane) => ({ dir: laneDirs(lane).state, live: true }));
|
|
1823
|
+
const backups = readDirIfPresent(lobstahHome()).filter((name) => name.startsWith("state-backup-")).sort().map((name) => ({ dir: path10.join(lobstahHome(), name), live: false })).filter((s) => fs12.statSync(s.dir).isDirectory());
|
|
1824
|
+
return [...live, ...backups];
|
|
1825
|
+
}
|
|
1826
|
+
function backfillStats(now2 = Date.now(), alreadyCounted) {
|
|
1827
|
+
const store = emptyStore(now2);
|
|
1828
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1829
|
+
for (const { dir, live } of backfillSources()) {
|
|
1830
|
+
for (const file of readDirIfPresent(dir).sort()) {
|
|
1831
|
+
if (!file.endsWith(".status"))
|
|
1832
|
+
continue;
|
|
1833
|
+
const id = file.slice(0, -".status".length);
|
|
1834
|
+
if (seen.has(id))
|
|
1835
|
+
continue;
|
|
1836
|
+
seen.add(id);
|
|
1837
|
+
if (live && alreadyCounted && !alreadyCounted.has(id))
|
|
1838
|
+
continue;
|
|
1839
|
+
const facts = readCatchFacts(dir, id);
|
|
1840
|
+
if (!facts.done)
|
|
1841
|
+
continue;
|
|
1842
|
+
add(store, facts, now2);
|
|
1843
|
+
if (live)
|
|
1844
|
+
store.counted.push(id);
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
return store;
|
|
1848
|
+
}
|
|
1849
|
+
function backfillDaily(store, now2 = Date.now()) {
|
|
1850
|
+
const daily = {};
|
|
1851
|
+
const counted = new Set(store.counted);
|
|
1852
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1853
|
+
const into = { ...store, daily };
|
|
1854
|
+
for (const { dir, live } of backfillSources()) {
|
|
1855
|
+
for (const file of readDirIfPresent(dir).sort()) {
|
|
1856
|
+
if (!file.endsWith(".status"))
|
|
1857
|
+
continue;
|
|
1858
|
+
const id = file.slice(0, -".status".length);
|
|
1859
|
+
if (seen.has(id))
|
|
1860
|
+
continue;
|
|
1861
|
+
seen.add(id);
|
|
1862
|
+
if (live && !counted.has(id))
|
|
1863
|
+
continue;
|
|
1864
|
+
const facts = readCatchFacts(dir, id);
|
|
1865
|
+
if (facts.done)
|
|
1866
|
+
addDaily(into, catchDay(facts, now2), now2);
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
pruneDaily(into, now2);
|
|
1870
|
+
return into;
|
|
1871
|
+
}
|
|
1872
|
+
var isDaily = (v) => !!v && typeof v === "object" && !Array.isArray(v) && Object.values(v).every((n) => typeof n === "number");
|
|
1873
|
+
function parseStore(text) {
|
|
1874
|
+
try {
|
|
1875
|
+
const s = JSON.parse(text);
|
|
1876
|
+
if (s.version !== 1 || typeof s.totalCatches !== "number" || typeof s.catchesToday !== "number" || typeof s.day !== "string")
|
|
1877
|
+
return void 0;
|
|
1878
|
+
return {
|
|
1879
|
+
version: 1,
|
|
1880
|
+
totalCatches: s.totalCatches,
|
|
1881
|
+
perTrap: s.perTrap ?? {},
|
|
1882
|
+
day: s.day,
|
|
1883
|
+
catchesToday: s.catchesToday,
|
|
1884
|
+
counted: s.counted ?? [],
|
|
1885
|
+
...isDaily(s.daily) ? { daily: s.daily } : {},
|
|
1886
|
+
...s.utc ? { utc: s.utc } : {}
|
|
1887
|
+
};
|
|
1598
1888
|
} catch {
|
|
1599
1889
|
return void 0;
|
|
1600
1890
|
}
|
|
1601
1891
|
}
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
const
|
|
1607
|
-
|
|
1892
|
+
var complete2 = (s) => !!s && s.daily !== void 0;
|
|
1893
|
+
function writeStore(store) {
|
|
1894
|
+
const file = statsPath();
|
|
1895
|
+
fs12.mkdirSync(path10.dirname(file), { recursive: true });
|
|
1896
|
+
const tmp = `${file}.tmp-${process.pid}`;
|
|
1897
|
+
fs12.writeFileSync(tmp, JSON.stringify(store));
|
|
1898
|
+
fs12.renameSync(tmp, file);
|
|
1608
1899
|
}
|
|
1609
|
-
function
|
|
1610
|
-
|
|
1611
|
-
const
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
for (let attempt = 0; attempt < 3; attempt++) {
|
|
1900
|
+
function withStatsLock(action) {
|
|
1901
|
+
fs12.mkdirSync(lobstahHome(), { recursive: true });
|
|
1902
|
+
const lock = `${statsPath()}.lock`;
|
|
1903
|
+
const deadline = Date.now() + 1e4;
|
|
1904
|
+
for (; ; ) {
|
|
1615
1905
|
try {
|
|
1616
|
-
|
|
1617
|
-
|
|
1906
|
+
fs12.mkdirSync(lock);
|
|
1907
|
+
break;
|
|
1618
1908
|
} catch (err) {
|
|
1619
1909
|
if (err.code !== "EEXIST")
|
|
1620
1910
|
throw err;
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
try {
|
|
1630
|
-
held2 = JSON.parse(raw);
|
|
1631
|
-
} catch {
|
|
1632
|
-
held2 = void 0;
|
|
1633
|
-
}
|
|
1634
|
-
if (held2?.id === id) {
|
|
1635
|
-
fs10.writeFileSync(file, JSON.stringify(mine, null, 2));
|
|
1636
|
-
return void 0;
|
|
1637
|
-
}
|
|
1638
|
-
if (held2 && lockIsLive(held2))
|
|
1639
|
-
return held2;
|
|
1640
|
-
try {
|
|
1641
|
-
if (fs10.readFileSync(file, "utf8") === raw)
|
|
1642
|
-
fs10.rmSync(file, { force: true });
|
|
1643
|
-
} catch {
|
|
1911
|
+
try {
|
|
1912
|
+
if (Date.now() - fs12.statSync(lock).mtimeMs > 12e4)
|
|
1913
|
+
fs12.rmdirSync(lock);
|
|
1914
|
+
} catch {
|
|
1915
|
+
}
|
|
1916
|
+
if (Date.now() >= deadline)
|
|
1917
|
+
throw new Error("stats store locked");
|
|
1918
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
|
|
1644
1919
|
}
|
|
1645
1920
|
}
|
|
1646
|
-
const held = readWorktreeLock(wt);
|
|
1647
|
-
return held ?? { id: "(contended)", lane, pid: 0, at: mine.at };
|
|
1648
|
-
}
|
|
1649
|
-
function releaseWorktreeLock(wt, id) {
|
|
1650
|
-
const file = worktreeLockFile(wt);
|
|
1651
|
-
if (!file)
|
|
1652
|
-
return;
|
|
1653
|
-
if (readWorktreeLock(wt)?.id === id)
|
|
1654
|
-
fs10.rmSync(file, { force: true });
|
|
1655
|
-
}
|
|
1656
|
-
|
|
1657
|
-
// packages/core/dist/soak.js
|
|
1658
|
-
function regPath(trapId) {
|
|
1659
|
-
return path9.join(soakingDir(), `${trapId}.json`);
|
|
1660
|
-
}
|
|
1661
|
-
function readTrap(trapId) {
|
|
1662
1921
|
try {
|
|
1663
|
-
return
|
|
1664
|
-
}
|
|
1665
|
-
|
|
1922
|
+
return action();
|
|
1923
|
+
} finally {
|
|
1924
|
+
fs12.rmdirSync(lock);
|
|
1666
1925
|
}
|
|
1667
1926
|
}
|
|
1668
|
-
function
|
|
1927
|
+
function loadOrBackfill(now2) {
|
|
1928
|
+
let text;
|
|
1669
1929
|
try {
|
|
1670
|
-
|
|
1671
|
-
} catch {
|
|
1672
|
-
|
|
1930
|
+
text = fs12.readFileSync(statsPath(), "utf8");
|
|
1931
|
+
} catch (err) {
|
|
1932
|
+
if (err.code !== "ENOENT")
|
|
1933
|
+
throw err;
|
|
1673
1934
|
}
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1935
|
+
const saved = text === void 0 ? void 0 : parseStore(text);
|
|
1936
|
+
if (complete2(saved) && saved.utc?.trapWorkers !== void 0 && saved.utc.byWorker !== void 0)
|
|
1937
|
+
return saved;
|
|
1938
|
+
if (saved) {
|
|
1939
|
+
const store2 = complete2(saved) ? saved : backfillDaily({ ...saved, daily: {} }, now2);
|
|
1940
|
+
if (!store2.utc) {
|
|
1941
|
+
store2.utc = backfillStats(now2, new Set(store2.counted)).utc;
|
|
1942
|
+
}
|
|
1943
|
+
if (store2.utc.trapWorkers === void 0 || store2.utc.byWorker === void 0) {
|
|
1944
|
+
const history = backfillStats(now2, new Set(store2.counted)).utc;
|
|
1945
|
+
if (history?.date === store2.utc.date) {
|
|
1946
|
+
store2.utc.trapWorkers ??= history.trapWorkers ?? {};
|
|
1947
|
+
store2.utc.byWorker ??= history.byWorker ?? [];
|
|
1948
|
+
} else {
|
|
1949
|
+
store2.utc.trapWorkers ??= {};
|
|
1950
|
+
store2.utc.byWorker ??= [];
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
writeStore(store2);
|
|
1954
|
+
return store2;
|
|
1683
1955
|
}
|
|
1956
|
+
if (text !== void 0)
|
|
1957
|
+
fs12.renameSync(statsPath(), `${statsPath()}.unreadable-${now2}`);
|
|
1958
|
+
const store = backfillStats(now2);
|
|
1959
|
+
writeStore(store);
|
|
1960
|
+
return store;
|
|
1961
|
+
}
|
|
1962
|
+
function recordCatch(id, lane, now2 = Date.now()) {
|
|
1963
|
+
withStatsLock(() => {
|
|
1964
|
+
const store = loadOrBackfill(now2);
|
|
1965
|
+
if (store.counted.includes(id))
|
|
1966
|
+
return;
|
|
1967
|
+
const facts = readCatchFacts(laneDirs(lane).state, id);
|
|
1968
|
+
if (!facts.done)
|
|
1969
|
+
return;
|
|
1970
|
+
add(store, facts, now2);
|
|
1971
|
+
store.counted.push(id);
|
|
1972
|
+
writeStore(store);
|
|
1973
|
+
});
|
|
1684
1974
|
}
|
|
1685
1975
|
|
|
1976
|
+
// packages/core/dist/decisions.js
|
|
1977
|
+
var DECISION_DETAIL_MAX = 64 * 1024;
|
|
1978
|
+
var ANSWER_TYPES = {
|
|
1979
|
+
".png": "image/png",
|
|
1980
|
+
".jpg": "image/jpeg",
|
|
1981
|
+
".jpeg": "image/jpeg",
|
|
1982
|
+
".gif": "image/gif",
|
|
1983
|
+
".webp": "image/webp",
|
|
1984
|
+
".pdf": "application/pdf",
|
|
1985
|
+
".txt": "text/plain",
|
|
1986
|
+
".md": "text/markdown",
|
|
1987
|
+
".csv": "text/csv",
|
|
1988
|
+
".json": "application/json",
|
|
1989
|
+
".log": "text/plain",
|
|
1990
|
+
".diff": "text/plain",
|
|
1991
|
+
".patch": "text/plain",
|
|
1992
|
+
".yaml": "text/plain",
|
|
1993
|
+
".yml": "text/plain",
|
|
1994
|
+
".toml": "text/plain",
|
|
1995
|
+
".zip": "application/zip"
|
|
1996
|
+
};
|
|
1997
|
+
var ANSWER_EXTENSIONS = Object.keys(ANSWER_TYPES);
|
|
1998
|
+
|
|
1686
1999
|
// packages/core/dist/inbox.js
|
|
1687
|
-
import * as
|
|
1688
|
-
import * as
|
|
2000
|
+
import * as fs13 from "node:fs";
|
|
2001
|
+
import * as path11 from "node:path";
|
|
1689
2002
|
var metaName = (file) => file.replace(/\.msg$/, ".meta.json");
|
|
1690
2003
|
function inboxDir(id, lane) {
|
|
1691
|
-
return
|
|
2004
|
+
return path11.join(laneDirs(lane).inbox, id);
|
|
1692
2005
|
}
|
|
1693
2006
|
function unhandled(id, lane) {
|
|
1694
2007
|
const dir = inboxDir(id, lane);
|
|
1695
|
-
return readDirIfPresent(dir).filter((f) => f.endsWith(".msg")).sort().map((f) => ({ file: f, text:
|
|
2008
|
+
return readDirIfPresent(dir).filter((f) => f.endsWith(".msg")).sort().map((f) => ({ file: f, text: fs13.readFileSync(path11.join(dir, f), "utf8") }));
|
|
1696
2009
|
}
|
|
1697
2010
|
function acknowledge(id, lane, file) {
|
|
1698
2011
|
const dir = inboxDir(id, lane);
|
|
1699
|
-
|
|
1700
|
-
|
|
2012
|
+
fs13.mkdirSync(path11.join(dir, "handled"), { recursive: true });
|
|
2013
|
+
fs13.renameSync(path11.join(dir, file), path11.join(dir, "handled", file));
|
|
1701
2014
|
try {
|
|
1702
|
-
|
|
2015
|
+
fs13.renameSync(path11.join(dir, metaName(file)), path11.join(dir, "handled", metaName(file)));
|
|
1703
2016
|
} catch {
|
|
1704
2017
|
}
|
|
1705
2018
|
}
|
|
1706
2019
|
|
|
1707
2020
|
// packages/core/dist/events.js
|
|
1708
|
-
import * as
|
|
2021
|
+
import * as fs14 from "node:fs";
|
|
1709
2022
|
function appendEvent(id, lane, ev) {
|
|
1710
|
-
|
|
2023
|
+
fs14.appendFileSync(eventsPath(id, lane), `${JSON.stringify(ev)}
|
|
1711
2024
|
`);
|
|
1712
2025
|
}
|
|
1713
2026
|
function touchEvents(id, lane, at = /* @__PURE__ */ new Date()) {
|
|
1714
2027
|
const file = eventsPath(id, lane);
|
|
1715
2028
|
try {
|
|
1716
|
-
|
|
2029
|
+
fs14.utimesSync(file, at, at);
|
|
1717
2030
|
} catch {
|
|
1718
|
-
|
|
1719
|
-
|
|
2031
|
+
fs14.appendFileSync(file, "");
|
|
2032
|
+
fs14.utimesSync(file, at, at);
|
|
1720
2033
|
}
|
|
1721
2034
|
}
|
|
1722
2035
|
|
|
1723
2036
|
// packages/core/dist/harness.js
|
|
1724
|
-
import * as
|
|
1725
|
-
import * as
|
|
2037
|
+
import * as fs15 from "node:fs";
|
|
2038
|
+
import * as path12 from "node:path";
|
|
1726
2039
|
import { createRequire } from "node:module";
|
|
1727
2040
|
function resolveOnPath(bin, pathVar = process.env.PATH) {
|
|
1728
2041
|
if (!pathVar)
|
|
1729
2042
|
return void 0;
|
|
1730
2043
|
const names = process.platform === "win32" ? [bin, `${bin}.exe`, `${bin}.cmd`, `${bin}.bat`] : [bin];
|
|
1731
|
-
for (const dir of pathVar.split(
|
|
2044
|
+
for (const dir of pathVar.split(path12.delimiter)) {
|
|
1732
2045
|
if (!dir)
|
|
1733
2046
|
continue;
|
|
1734
2047
|
for (const name of names) {
|
|
1735
|
-
const candidate =
|
|
2048
|
+
const candidate = path12.join(dir, name);
|
|
1736
2049
|
try {
|
|
1737
|
-
|
|
1738
|
-
if (
|
|
2050
|
+
fs15.accessSync(candidate, fs15.constants.X_OK);
|
|
2051
|
+
if (fs15.statSync(candidate).isFile())
|
|
1739
2052
|
return candidate;
|
|
1740
2053
|
} catch {
|
|
1741
2054
|
}
|
|
@@ -1756,8 +2069,8 @@ function codexInvocation(argv) {
|
|
|
1756
2069
|
} catch {
|
|
1757
2070
|
}
|
|
1758
2071
|
for (const dir of require_.resolve.paths("@openai/codex") ?? []) {
|
|
1759
|
-
const cli =
|
|
1760
|
-
if (
|
|
2072
|
+
const cli = path12.join(dir, "@openai", "codex", "bin", "codex.js");
|
|
2073
|
+
if (fs15.existsSync(cli))
|
|
1761
2074
|
return { file: process.execPath, argv: [cli, ...argv] };
|
|
1762
2075
|
}
|
|
1763
2076
|
return void 0;
|
|
@@ -1769,109 +2082,6 @@ var COMPILED_BINARY = (() => {
|
|
|
1769
2082
|
return bun !== void 0 && String(bun.main ?? "").includes("$bunfs");
|
|
1770
2083
|
})();
|
|
1771
2084
|
|
|
1772
|
-
// packages/core/dist/pr.js
|
|
1773
|
-
import { spawnSync } from "node:child_process";
|
|
1774
|
-
function parsePrRef(s) {
|
|
1775
|
-
const m = /^(?:pr:)?([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)#(\d+)$/.exec(s.trim()) ?? /^https?:\/\/github\.com\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)\/pull\/(\d+)(?:[/?#].*)?$/.exec(s.trim());
|
|
1776
|
-
if (!m)
|
|
1777
|
-
return void 0;
|
|
1778
|
-
const [, owner, repo, n] = m;
|
|
1779
|
-
const number = Number(n);
|
|
1780
|
-
return { owner, repo, number, key: `pr:${owner}/${repo}#${number}`, url: `https://github.com/${owner}/${repo}/pull/${number}` };
|
|
1781
|
-
}
|
|
1782
|
-
var PR_VIEW_FIELDS = "title,state,isDraft,headRefOid,baseRefName,baseRefOid,headRefName,mergeStateStatus,reviewDecision,statusCheckRollup,mergedAt,closedAt,updatedAt,reviews";
|
|
1783
|
-
var PR_VIEW_FIELDS_NO_CHECKS = PR_VIEW_FIELDS.split(",").filter((f) => f !== "statusCheckRollup").join(",");
|
|
1784
|
-
var FORBIDDEN = /resource not accessible by (integration|personal access token)/i;
|
|
1785
|
-
function ghPrView(ref) {
|
|
1786
|
-
const view1 = (fields2) => spawnSync("gh", ["pr", "view", String(ref.number), "--repo", `${ref.owner}/${ref.repo}`, "--json", fields2], {
|
|
1787
|
-
encoding: "utf8",
|
|
1788
|
-
timeout: 6e4
|
|
1789
|
-
});
|
|
1790
|
-
const reason = (r) => firstMeaningfulLine(r.stderr) ?? `gh exited ${r.status}`;
|
|
1791
|
-
const unknownBase = (r) => r.status !== 0 && /unknown json field.*baseRefOid/i.test(r.stderr ?? "");
|
|
1792
|
-
const withoutBase = (fields2) => fields2.split(",").filter((f) => f !== "baseRefOid").join(",");
|
|
1793
|
-
let fields = PR_VIEW_FIELDS;
|
|
1794
|
-
let res = view1(fields);
|
|
1795
|
-
if (unknownBase(res))
|
|
1796
|
-
res = view1(fields = withoutBase(fields));
|
|
1797
|
-
if (res.error)
|
|
1798
|
-
throw new Error(`gh: ${res.error.message}`);
|
|
1799
|
-
let checksError;
|
|
1800
|
-
if (res.status !== 0 && FORBIDDEN.test(res.stderr ?? "")) {
|
|
1801
|
-
checksError = reason(res);
|
|
1802
|
-
const retry = view1(fields === PR_VIEW_FIELDS ? PR_VIEW_FIELDS_NO_CHECKS : withoutBase(PR_VIEW_FIELDS_NO_CHECKS));
|
|
1803
|
-
if (retry.error)
|
|
1804
|
-
throw new Error(`gh: ${retry.error.message}`);
|
|
1805
|
-
if (retry.status !== 0) {
|
|
1806
|
-
throw new Error(FORBIDDEN.test(retry.stderr ?? "") ? `${reason(retry)} (fails even without check results)` : reason(retry));
|
|
1807
|
-
}
|
|
1808
|
-
res = retry;
|
|
1809
|
-
}
|
|
1810
|
-
if (res.status !== 0)
|
|
1811
|
-
throw new Error(reason(res));
|
|
1812
|
-
const view = JSON.parse(res.stdout);
|
|
1813
|
-
if (checksError)
|
|
1814
|
-
view.checksError = checksError;
|
|
1815
|
-
if (view.state === "OPEN") {
|
|
1816
|
-
const threads = ghUnresolvedThreads(ref);
|
|
1817
|
-
if (threads !== void 0)
|
|
1818
|
-
view.unresolvedThreads = threads;
|
|
1819
|
-
}
|
|
1820
|
-
return view;
|
|
1821
|
-
}
|
|
1822
|
-
var THREADS_QUERY = "query($owner:String!,$repo:String!,$n:Int!){repository(owner:$owner,name:$repo){pullRequest(number:$n){reviewThreads(first:100){nodes{isResolved}}}}}";
|
|
1823
|
-
function ghUnresolvedThreads(ref) {
|
|
1824
|
-
const res = spawnSync("gh", ["api", "graphql", "-f", `query=${THREADS_QUERY}`, "-f", `owner=${ref.owner}`, "-f", `repo=${ref.repo}`, "-F", `n=${ref.number}`], { encoding: "utf8", timeout: 6e4 });
|
|
1825
|
-
if (res.error || res.status !== 0)
|
|
1826
|
-
return void 0;
|
|
1827
|
-
return parseUnresolvedThreads(res.stdout);
|
|
1828
|
-
}
|
|
1829
|
-
function parseUnresolvedThreads(stdout) {
|
|
1830
|
-
try {
|
|
1831
|
-
const nodes = JSON.parse(stdout).data?.repository?.pullRequest?.reviewThreads?.nodes;
|
|
1832
|
-
return Array.isArray(nodes) ? nodes.filter((t) => t.isResolved === false).length : void 0;
|
|
1833
|
-
} catch {
|
|
1834
|
-
return void 0;
|
|
1835
|
-
}
|
|
1836
|
-
}
|
|
1837
|
-
|
|
1838
|
-
// packages/core/dist/prs.js
|
|
1839
|
-
import * as fs15 from "node:fs";
|
|
1840
|
-
import * as path12 from "node:path";
|
|
1841
|
-
function prsDir() {
|
|
1842
|
-
return path12.join(lobstahHome(), "prs");
|
|
1843
|
-
}
|
|
1844
|
-
function prRecordFile(key) {
|
|
1845
|
-
const ref = parsePrRef(key);
|
|
1846
|
-
if (!ref)
|
|
1847
|
-
throw new Error(`not a PR key: ${key}`);
|
|
1848
|
-
const safe = (s) => s.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
1849
|
-
return path12.join(prsDir(), `${safe(ref.owner)}__${safe(ref.repo)}__${ref.number}.json`);
|
|
1850
|
-
}
|
|
1851
|
-
function readPr(key) {
|
|
1852
|
-
try {
|
|
1853
|
-
return JSON.parse(fs15.readFileSync(prRecordFile(key), "utf8"));
|
|
1854
|
-
} catch {
|
|
1855
|
-
return void 0;
|
|
1856
|
-
}
|
|
1857
|
-
}
|
|
1858
|
-
function readPrs() {
|
|
1859
|
-
let files;
|
|
1860
|
-
try {
|
|
1861
|
-
files = fs15.readdirSync(prsDir()).filter((f) => f.endsWith(".json"));
|
|
1862
|
-
} catch {
|
|
1863
|
-
return [];
|
|
1864
|
-
}
|
|
1865
|
-
return files.flatMap((f) => {
|
|
1866
|
-
try {
|
|
1867
|
-
const r = JSON.parse(fs15.readFileSync(path12.join(prsDir(), f), "utf8"));
|
|
1868
|
-
return typeof r.key === "string" && typeof r.url === "string" ? [r] : [];
|
|
1869
|
-
} catch {
|
|
1870
|
-
return [];
|
|
1871
|
-
}
|
|
1872
|
-
});
|
|
1873
|
-
}
|
|
1874
|
-
|
|
1875
2085
|
// packages/core/dist/chain-pr.js
|
|
1876
2086
|
function chainPr(id, lane) {
|
|
1877
2087
|
const ancestry = [];
|
|
@@ -2177,7 +2387,7 @@ function pumpClaudeMessage(msg, push, onSession) {
|
|
|
2177
2387
|
if (msg.type === "system" && msg.subtype === "init") {
|
|
2178
2388
|
if (msg.session_id)
|
|
2179
2389
|
onSession(msg.session_id);
|
|
2180
|
-
push({ at, type: "session", data: { sessionId: msg.session_id } });
|
|
2390
|
+
push({ at, type: "session", data: { sessionId: msg.session_id, ...msg.model === void 0 ? {} : { model: localModel(msg.model) } } });
|
|
2181
2391
|
} else if (msg.type === "system" && msg.subtype === "background_tasks_changed") {
|
|
2182
2392
|
const live = (msg.tasks ?? []).filter((t) => !t.ambient).length;
|
|
2183
2393
|
push({ at, type: "background", data: { live } });
|
|
@@ -2788,6 +2998,21 @@ async function allocate(repo, id, fromRemoteBranch = repo.trunk) {
|
|
|
2788
2998
|
await runSetup(repo, dir);
|
|
2789
2999
|
return dir;
|
|
2790
3000
|
}
|
|
3001
|
+
async function recoverWorktree(repo, id) {
|
|
3002
|
+
const dir = worktreePath(id);
|
|
3003
|
+
try {
|
|
3004
|
+
if (fs18.statSync(path15.join(dir, ".git")).isFile()) {
|
|
3005
|
+
const branch = await git(dir, "symbolic-ref", "--quiet", "HEAD");
|
|
3006
|
+
const root = await git(dir, "rev-parse", "--show-toplevel");
|
|
3007
|
+
const common = await git(dir, "rev-parse", "--path-format=absolute", "--git-common-dir");
|
|
3008
|
+
const mine = await git(repo.path, "rev-parse", "--path-format=absolute", "--git-common-dir");
|
|
3009
|
+
if (branch === `refs/heads/lobstah/${id}` && realpath(root) === realpath(dir) && realpath(common) === realpath(mine))
|
|
3010
|
+
return dir;
|
|
3011
|
+
}
|
|
3012
|
+
} catch {
|
|
3013
|
+
}
|
|
3014
|
+
throw new Error(`unrecorded worktree at ${dir} for ${id} \u2014 ownership not proven (expected lobstah/${id} in ${repo.path}); inspect and preserve it, then relocate the conflicting checkout before retrying`);
|
|
3015
|
+
}
|
|
2791
3016
|
async function runSetup(repo, dir) {
|
|
2792
3017
|
for (const cmd of repo.setup ?? []) {
|
|
2793
3018
|
await shell(cmd, { cwd: dir, env: { ...process.env, ...repo.env ?? {} } });
|
|
@@ -3116,7 +3341,11 @@ async function drive(run2, opts) {
|
|
|
3116
3341
|
recordPush(id, lane, resolvePushTargets(ev.data.pushes.map(String), opts.cwd), ev.at);
|
|
3117
3342
|
}
|
|
3118
3343
|
if (ev.type === "session" && ev.data?.sessionId) {
|
|
3119
|
-
|
|
3344
|
+
const prior = readEvidence(id, lane).worker;
|
|
3345
|
+
mergeEvidence(id, lane, {
|
|
3346
|
+
sessionId: String(ev.data.sessionId),
|
|
3347
|
+
...ev.data.model === void 0 ? {} : { worker: workerMetadata({ ...prior, model: ev.data.model, observedAt: ev.at }) }
|
|
3348
|
+
});
|
|
3120
3349
|
}
|
|
3121
3350
|
if (ev.type !== "turn-end" || cancelled || stopped() || final || parked) continue;
|
|
3122
3351
|
const lastVerb = readStatusLog(id, lane).at(-1)?.verb;
|
|
@@ -3545,16 +3774,18 @@ async function main(activeDir, lane, seams = {}) {
|
|
|
3545
3774
|
});
|
|
3546
3775
|
const wtFile = path17.join(activeDir, "worktree.json");
|
|
3547
3776
|
let recorded;
|
|
3548
|
-
let
|
|
3777
|
+
let recovered = false;
|
|
3778
|
+
let choice3;
|
|
3549
3779
|
if (fs20.existsSync(wtFile)) {
|
|
3550
3780
|
recorded = JSON.parse(fs20.readFileSync(wtFile, "utf8")).path;
|
|
3551
3781
|
if (!fs20.existsSync(recorded)) throw new Error(`recorded worktree ${recorded} is gone`);
|
|
3552
3782
|
} else if (fs20.existsSync(worktreePath(id))) {
|
|
3553
|
-
|
|
3783
|
+
recorded = await recoverWorktree(repo, id);
|
|
3784
|
+
recovered = true;
|
|
3554
3785
|
} else if (descriptor.followUp && cfg.limits.reuseWorktree !== false) {
|
|
3555
|
-
|
|
3786
|
+
choice3 = await deps.chooseWorktree({ id, lane, repoKey: descriptor.repo, repo, followUp: descriptor.followUp });
|
|
3556
3787
|
}
|
|
3557
|
-
const worktreeNote =
|
|
3788
|
+
const worktreeNote = choice3 ? choice3.reuse ? `reusing worktree of ${short3(choice3.from)}` : `fresh worktree (${choice3.reason})` : recovered ? "recovered own unrecorded worktree" : void 0;
|
|
3558
3789
|
const firstModel = modelForHarness(plan.harness, resolved.model);
|
|
3559
3790
|
const firstNote = [
|
|
3560
3791
|
wake ? `woke from pause: ${wake}` : attempts > 1 ? `attempt ${attempts}` : void 0,
|
|
@@ -3573,17 +3804,22 @@ async function main(activeDir, lane, seams = {}) {
|
|
|
3573
3804
|
let cwd;
|
|
3574
3805
|
if (recorded) {
|
|
3575
3806
|
cwd = recorded;
|
|
3576
|
-
acquireWorktreeLock(cwd, id, lane);
|
|
3577
|
-
|
|
3578
|
-
|
|
3807
|
+
const held = acquireWorktreeLock(cwd, id, lane);
|
|
3808
|
+
if (held) throw new Error(`worktree at ${cwd} is in use by ${held.id} \u2014 wait for that dispatch to release it before retrying`);
|
|
3809
|
+
if (recovered) {
|
|
3810
|
+
await deps.prepareReuse(repo, cwd);
|
|
3811
|
+
fs20.writeFileSync(wtFile, JSON.stringify({ path: cwd }, null, 2));
|
|
3812
|
+
}
|
|
3813
|
+
} else if (choice3?.reuse) {
|
|
3814
|
+
cwd = choice3.path;
|
|
3579
3815
|
const { setupRan } = await deps.prepareReuse(repo, cwd);
|
|
3580
3816
|
if (setupRan) console.log(`[runner] lockfile changed since setup last ran in ${cwd}: setup re-run`);
|
|
3581
3817
|
appendEvent(id, lane, {
|
|
3582
3818
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3583
3819
|
type: "runner",
|
|
3584
|
-
data: { worktree: cwd, worktreeOf:
|
|
3820
|
+
data: { worktree: cwd, worktreeOf: choice3.owner, setupRan }
|
|
3585
3821
|
});
|
|
3586
|
-
fs20.writeFileSync(wtFile, JSON.stringify({ path: cwd, of:
|
|
3822
|
+
fs20.writeFileSync(wtFile, JSON.stringify({ path: cwd, of: choice3.owner }, null, 2));
|
|
3587
3823
|
} else {
|
|
3588
3824
|
cwd = await deps.allocate(repo, id, descriptor.systemRepair ? descriptor.pr?.headRefName : void 0);
|
|
3589
3825
|
acquireWorktreeLock(cwd, id, lane);
|
|
@@ -3692,6 +3928,13 @@ async function main(activeDir, lane, seams = {}) {
|
|
|
3692
3928
|
return;
|
|
3693
3929
|
}
|
|
3694
3930
|
const runOnce = async (harness, prompt, resumeSession) => {
|
|
3931
|
+
const explicit = resolved.flags.length === 0;
|
|
3932
|
+
mergeEvidence(id, lane, { worker: workerMetadata({
|
|
3933
|
+
harness,
|
|
3934
|
+
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3935
|
+
model: explicit ? modelForHarness(harness, resolved.model).model : void 0,
|
|
3936
|
+
config: { effort: explicit ? resolved.effort : null, permissionMode: explicit && harness === "claude" ? "bypassPermissions" : null }
|
|
3937
|
+
}) });
|
|
3695
3938
|
const run2 = await deps.loadAdapter(harness).start({
|
|
3696
3939
|
id,
|
|
3697
3940
|
cwd,
|