lobstah 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +528 -337
- package/dist/runner.js +6 -2
- package/docs/configuration.md +1 -0
- package/docs/man.md +5 -2
- package/docs/pickup.md +7 -1
- package/docs/vocabulary.md +5 -5
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -1621,6 +1621,10 @@ function expandHome(p) {
|
|
|
1621
1621
|
function loadConfig() {
|
|
1622
1622
|
const file2 = configPath();
|
|
1623
1623
|
const raw = fs7.existsSync(file2) ? parse(fs7.readFileSync(file2, "utf8")) : {};
|
|
1624
|
+
const watch2 = { ...DEFAULT_WATCH, ...raw.watch ?? {} };
|
|
1625
|
+
if (typeof watch2.repairCooldownSecs !== "number" || !Number.isFinite(watch2.repairCooldownSecs) || watch2.repairCooldownSecs < 0) {
|
|
1626
|
+
throw new Error(`[watch].repairCooldownSecs must be a non-negative number in ${configPath()}`);
|
|
1627
|
+
}
|
|
1624
1628
|
const readySettleSecs = raw.readySettleSecs ?? 600;
|
|
1625
1629
|
if (typeof readySettleSecs !== "number" || !Number.isFinite(readySettleSecs) || readySettleSecs < 0) {
|
|
1626
1630
|
throw new Error(`readySettleSecs must be a non-negative number in ${configPath()}`);
|
|
@@ -1656,7 +1660,7 @@ function loadConfig() {
|
|
|
1656
1660
|
soak: parseSoak(raw.soak),
|
|
1657
1661
|
helm: { ...DEFAULT_HELM, ...raw.helm ?? {} },
|
|
1658
1662
|
glass: { ...DEFAULT_GLASS, ...raw.glass ?? {} },
|
|
1659
|
-
watch:
|
|
1663
|
+
watch: watch2,
|
|
1660
1664
|
grounds,
|
|
1661
1665
|
notifyCommand: raw.notifyCommand ? String(raw.notifyCommand) : void 0,
|
|
1662
1666
|
notifyVerbs: Array.isArray(raw.notifyVerbs) ? raw.notifyVerbs.map(String) : void 0,
|
|
@@ -1706,7 +1710,7 @@ var init_config = __esm({
|
|
|
1706
1710
|
armGraceSecs: 5
|
|
1707
1711
|
};
|
|
1708
1712
|
DEFAULT_GLASS = { port: 4949 };
|
|
1709
|
-
DEFAULT_WATCH = { maxForksPerCycle: 3, autoRepair: true, conflicts: true, checks: true, maxRepairsPerPr: 2, maxRepairsWithoutProgress: 2, repairSettleSecs: 600, repairTrapWaitSecs: 600 };
|
|
1713
|
+
DEFAULT_WATCH = { maxForksPerCycle: 3, autoRepair: true, conflicts: true, checks: true, maxRepairsPerPr: 2, maxRepairsWithoutProgress: 2, repairSettleSecs: 600, repairCooldownSecs: 0, repairTrapWaitSecs: 600 };
|
|
1710
1714
|
DEFAULT_LIMITS = {
|
|
1711
1715
|
maxConcurrent: 2,
|
|
1712
1716
|
choreConcurrent: 1,
|
|
@@ -2210,6 +2214,7 @@ function upsertPr(pr, dispatchId) {
|
|
|
2210
2214
|
key: ref.key,
|
|
2211
2215
|
repo: `${ref.owner}/${ref.repo}`,
|
|
2212
2216
|
dispatches,
|
|
2217
|
+
observedHeadShas: [...new Set([...before?.observedHeadShas ?? [], before?.headSha, pr.headSha].filter((s) => !!s))],
|
|
2213
2218
|
standingSince,
|
|
2214
2219
|
observations: (before?.observations ?? 0) + 1,
|
|
2215
2220
|
firstSeenAt,
|
|
@@ -2249,16 +2254,18 @@ function removePr(key) {
|
|
|
2249
2254
|
}
|
|
2250
2255
|
function resetRepairStreaks(key) {
|
|
2251
2256
|
const reset = [];
|
|
2257
|
+
const lastRepair = (pr) => pr.repairCooldown?.dispatchId ?? pr.repairStreak?.lastRepairId ?? pr.repair?.dispatchId;
|
|
2252
2258
|
for (const pr of key === void 0 ? readPrs() : [readPr(key)].filter((p) => p !== void 0)) {
|
|
2253
|
-
if (!pr.repairStreak)
|
|
2259
|
+
if (!pr.repairStreak && (!lastRepair(pr) || pr.repairCooldown?.released))
|
|
2254
2260
|
continue;
|
|
2255
2261
|
withPrLock(pr.key, () => {
|
|
2256
2262
|
const current = readPr(pr.key);
|
|
2257
|
-
if (!current
|
|
2263
|
+
if (!current || !current.repairStreak && (!lastRepair(current) || current.repairCooldown?.released))
|
|
2258
2264
|
return;
|
|
2259
|
-
const stopped = current.repair?.status === "gave-up" && current.repairStreak
|
|
2265
|
+
const stopped = current.repair?.status === "gave-up" && current.repairStreak?.stoppedHead !== void 0;
|
|
2266
|
+
const cooldown = current.repairCooldown ?? (lastRepair(current) ? { dispatchId: lastRepair(current) } : void 0);
|
|
2260
2267
|
const { repairStreak: _streak, ...rest } = current;
|
|
2261
|
-
writePr({ ...rest, ...stopped ? { repair: void 0 } : {} });
|
|
2268
|
+
writePr({ ...rest, ...cooldown ? { repairCooldown: { ...cooldown, released: true } } : {}, ...stopped ? { repair: void 0 } : {} });
|
|
2262
2269
|
reset.push(pr.key);
|
|
2263
2270
|
});
|
|
2264
2271
|
}
|
|
@@ -2317,7 +2324,7 @@ function derivePrStacks(records, opts = {}) {
|
|
|
2317
2324
|
const order = members.map((p) => `#${p.number}`).join(" \u2192 ");
|
|
2318
2325
|
const waiting = members.filter((p) => !isReady(p)).map((p) => `#${p.number} (${p.discoveryPending ? "awaiting watch check" : prStandingKinds(p).includes("pr:ready") ? "settling" : prBadge(p).text})`).join(", ");
|
|
2319
2326
|
out.push({
|
|
2320
|
-
id: parsePrRef(
|
|
2327
|
+
id: parsePrRef(bottom.url).key,
|
|
2321
2328
|
repo: repo(bottom),
|
|
2322
2329
|
members,
|
|
2323
2330
|
ready,
|
|
@@ -2338,54 +2345,80 @@ function readStackEpochs() {
|
|
|
2338
2345
|
return {};
|
|
2339
2346
|
}
|
|
2340
2347
|
}
|
|
2341
|
-
function
|
|
2342
|
-
const
|
|
2343
|
-
return
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2348
|
+
function identifyPrStacks(stacks, state = readStackEpochs()) {
|
|
2349
|
+
const used = /* @__PURE__ */ new Set();
|
|
2350
|
+
return stacks.map((s) => {
|
|
2351
|
+
const keys = Object.keys(headsOf(s));
|
|
2352
|
+
const matches = Object.entries(state).filter(([id2]) => !used.has(id2)).map(([id2, old]) => ({
|
|
2353
|
+
id: id2,
|
|
2354
|
+
old,
|
|
2355
|
+
overlap: keys.filter((key) => key in old.heads).length
|
|
2356
|
+
})).filter((x) => x.overlap > 0).sort((a, b) => b.overlap - a.overlap || a.old.at.localeCompare(b.old.at) || a.id.localeCompare(b.id));
|
|
2357
|
+
const id = matches[0]?.old.version === 2 ? matches[0].id : s.id;
|
|
2358
|
+
used.add(id);
|
|
2359
|
+
return { ...s, id, legacy: matches.some((m) => m.old.version !== 2) };
|
|
2349
2360
|
});
|
|
2350
2361
|
}
|
|
2351
2362
|
function syncStackReadiness(cfg = loadConfig(), now2 = Date.now()) {
|
|
2352
2363
|
const records = readPrs();
|
|
2353
|
-
const stacks = derivePrStacks(records, { trunk: (r) => prStackTrunk(cfg, r), readySettleSecs: cfg.readySettleSecs, now: now2 });
|
|
2354
2364
|
withPrLock("pr:lobstah/stack-state#0", () => {
|
|
2355
2365
|
const state = readStackEpochs();
|
|
2366
|
+
const stacks = identifyPrStacks(derivePrStacks(records, { trunk: (r) => prStackTrunk(cfg, r), readySettleSecs: cfg.readySettleSecs, now: now2 }), state);
|
|
2367
|
+
const notices = listNotices(Number.MAX_SAFE_INTEGER).filter((n) => n.kind === "stack-ready");
|
|
2368
|
+
const kept = /* @__PURE__ */ new Set();
|
|
2356
2369
|
for (const s of stacks) {
|
|
2357
|
-
const old = state[s.id];
|
|
2358
2370
|
const heads = headsOf(s);
|
|
2371
|
+
const aliases = Object.keys(state).filter((id) => Object.keys(heads).some((key) => key in state[id].heads));
|
|
2372
|
+
const old = state[s.id] ?? aliases.map((id) => state[id]).sort((a, b) => b.at.localeCompare(a.at))[0];
|
|
2373
|
+
const previous = notices.filter((n) => n.refId === s.id || aliases.includes(n.refId ?? "") || n.refId in heads);
|
|
2374
|
+
const migrating = !!old && old.version !== 2 || !old && previous.length > 0;
|
|
2359
2375
|
const removed = old ? Object.keys(old.heads).filter((key) => !(key in heads)) : [];
|
|
2360
2376
|
const partialMerge = !!old && Object.entries(heads).every(([key, sha3]) => old.heads[key] === sha3) && removed.every((key) => records.find((p) => p.key === key)?.state === "MERGED");
|
|
2361
2377
|
const changed = !!old && !partialMerge && JSON.stringify(heads) !== JSON.stringify(old.heads);
|
|
2362
|
-
const
|
|
2378
|
+
const settlingOnly = !!old?.notified && !changed && s.members.every((p) => prStandingKinds(p).includes("pr:ready"));
|
|
2379
|
+
const epoch = (old?.epoch ?? 0) + Number(changed || !!old?.ready && !s.allReady && !settlingOnly);
|
|
2363
2380
|
const next = {
|
|
2381
|
+
version: 2,
|
|
2364
2382
|
heads,
|
|
2365
2383
|
ready: s.allReady,
|
|
2366
2384
|
epoch,
|
|
2367
2385
|
at: !old || changed || old.ready !== s.allReady ? new Date(now2).toISOString() : old.at,
|
|
2368
|
-
notified: changed || !s.allReady ? false : old?.notified
|
|
2386
|
+
notified: migrating ? s.allReady : changed || !s.allReady && !settlingOnly ? false : old?.notified,
|
|
2387
|
+
silent: migrating || !changed && (s.allReady || settlingOnly) && !!old?.silent
|
|
2369
2388
|
};
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2389
|
+
const wake = s.allReady && !next.notified && !migrating;
|
|
2390
|
+
const current = previous.at(-1);
|
|
2391
|
+
const fields = {
|
|
2392
|
+
kind: "stack-ready",
|
|
2393
|
+
text: s.text,
|
|
2394
|
+
refId: s.id,
|
|
2395
|
+
url: s.members.at(-1).url,
|
|
2396
|
+
stateHash: stackStateHash(s, epoch),
|
|
2397
|
+
repo: repoKey(cfg, s.repo),
|
|
2398
|
+
quiet: migrating || !s.allReady || !stackReadyEnabled(cfg) || !wake && (!current || !!current.quiet)
|
|
2399
|
+
};
|
|
2400
|
+
const notice = wake || !current ? postNotice(fields) : { ...current, ...fields };
|
|
2401
|
+
if (current && !wake) {
|
|
2402
|
+
const file3 = path7.join(noticesDir(), `${notice.seq}.json`), tmp2 = `${file3}.tmp-${process.pid}`;
|
|
2403
|
+
fs9.writeFileSync(tmp2, JSON.stringify(notice));
|
|
2404
|
+
fs9.renameSync(tmp2, file3);
|
|
2405
|
+
}
|
|
2406
|
+
kept.add(notice.seq);
|
|
2407
|
+
if (s.allReady)
|
|
2380
2408
|
next.notified = true;
|
|
2381
|
-
|
|
2409
|
+
for (const alias of aliases)
|
|
2410
|
+
if (alias !== s.id)
|
|
2411
|
+
delete state[alias];
|
|
2382
2412
|
state[s.id] = next;
|
|
2383
2413
|
}
|
|
2414
|
+
for (const n of notices)
|
|
2415
|
+
if (!kept.has(n.seq))
|
|
2416
|
+
fs9.rmSync(path7.join(noticesDir(), `${n.seq}.json`), { force: true });
|
|
2384
2417
|
for (const [id, old] of Object.entries(state)) {
|
|
2385
2418
|
if (stacks.some((s) => s.id === id))
|
|
2386
2419
|
continue;
|
|
2387
2420
|
const surviving = records.filter((p) => p.state === "OPEN" && p.key in old.heads);
|
|
2388
|
-
if (old.ready && (surviving.length === 0 || records.some((p) => p.key in old.heads && p.state === "CLOSED") || surviving.some((p) => p.headSha !== old.heads[p.key] || !prStandingKinds(p, { readySettleSecs: cfg.readySettleSecs, now: now2 }).includes("pr:ready")))) {
|
|
2421
|
+
if (old.ready && (surviving.length === 0 || surviving.length >= 2 || records.some((p) => p.key in old.heads && p.state === "CLOSED") || surviving.some((p) => p.headSha !== old.heads[p.key] || !prStandingKinds(p, { readySettleSecs: cfg.readySettleSecs, now: now2 }).includes("pr:ready")))) {
|
|
2389
2422
|
old.ready = false;
|
|
2390
2423
|
old.notified = false;
|
|
2391
2424
|
old.epoch++;
|
|
@@ -2416,7 +2449,7 @@ var init_pr_stacks = __esm({
|
|
|
2416
2449
|
});
|
|
2417
2450
|
|
|
2418
2451
|
// packages/core/dist/notices.js
|
|
2419
|
-
import
|
|
2452
|
+
import fs10 from "node:fs";
|
|
2420
2453
|
import * as path8 from "node:path";
|
|
2421
2454
|
function noticesDir() {
|
|
2422
2455
|
return path8.join(lobstahHome(), "notices");
|
|
@@ -2435,13 +2468,14 @@ function postNotice(n) {
|
|
|
2435
2468
|
return void 0;
|
|
2436
2469
|
}
|
|
2437
2470
|
}
|
|
2438
|
-
const seq = `${String(Date.now()).padStart(15, "0")}-${process.pid}-${counter++}`;
|
|
2471
|
+
const seq = `${String(Date.now()).padStart(15, "0")}-${process.pid}-${String(counter++).padStart(8, "0")}`;
|
|
2439
2472
|
const notice = {
|
|
2440
2473
|
seq,
|
|
2441
2474
|
kind: n.kind,
|
|
2442
2475
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2443
2476
|
text: n.text,
|
|
2444
2477
|
refId: n.refId,
|
|
2478
|
+
...n.url ? { url: n.url } : {},
|
|
2445
2479
|
repo: n.repo,
|
|
2446
2480
|
by: n.by,
|
|
2447
2481
|
...n.quiet ? { quiet: true } : {},
|
|
@@ -2461,7 +2495,15 @@ function listNotices(limit = 20) {
|
|
|
2461
2495
|
} catch {
|
|
2462
2496
|
return [];
|
|
2463
2497
|
}
|
|
2464
|
-
return files.sort().slice(-limit).
|
|
2498
|
+
return files.sort().slice(-limit).flatMap((f) => {
|
|
2499
|
+
try {
|
|
2500
|
+
return [JSON.parse(fs10.readFileSync(path8.join(dir, f), "utf8"))];
|
|
2501
|
+
} catch (error) {
|
|
2502
|
+
if (error.code === "ENOENT")
|
|
2503
|
+
return [];
|
|
2504
|
+
throw error;
|
|
2505
|
+
}
|
|
2506
|
+
});
|
|
2465
2507
|
}
|
|
2466
2508
|
function unseenNotices(consume, filter, wakes) {
|
|
2467
2509
|
let seen = "";
|
|
@@ -7987,14 +8029,14 @@ __export(stats_exports, {
|
|
|
7987
8029
|
readStatsPage: () => readStatsPage,
|
|
7988
8030
|
statsOutput: () => statsOutput
|
|
7989
8031
|
});
|
|
7990
|
-
import * as
|
|
8032
|
+
import * as fs69 from "node:fs";
|
|
7991
8033
|
import * as path59 from "node:path";
|
|
7992
8034
|
function trapNames() {
|
|
7993
8035
|
const dir = path59.join(lobstahHome(), "trap-names");
|
|
7994
8036
|
const names = /* @__PURE__ */ new Map();
|
|
7995
8037
|
for (const file2 of readDirIfPresent(dir).filter((f) => f.endsWith(".json")).sort()) {
|
|
7996
8038
|
try {
|
|
7997
|
-
const reg = JSON.parse(
|
|
8039
|
+
const reg = JSON.parse(fs69.readFileSync(path59.join(dir, file2), "utf8"));
|
|
7998
8040
|
if (typeof reg.trapId === "string") names.set(`wt:${reg.trapId}`, file2.slice(0, -5));
|
|
7999
8041
|
} catch (err) {
|
|
8000
8042
|
if (!(err instanceof SyntaxError) && err.code !== "ENOENT") throw err;
|
|
@@ -8326,25 +8368,25 @@ var init_claude = __esm({
|
|
|
8326
8368
|
});
|
|
8327
8369
|
|
|
8328
8370
|
// packages/adapters/dist/codex.js
|
|
8329
|
-
import * as
|
|
8371
|
+
import * as fs83 from "node:fs";
|
|
8330
8372
|
import * as os12 from "node:os";
|
|
8331
8373
|
import * as path68 from "node:path";
|
|
8332
8374
|
import { spawn as spawn6 } from "node:child_process";
|
|
8333
8375
|
function isolatedCodexHome() {
|
|
8334
8376
|
const dir = path68.join(lobstahHome(), "codex-home");
|
|
8335
|
-
|
|
8377
|
+
fs83.mkdirSync(dir, { recursive: true });
|
|
8336
8378
|
const auth = path68.join(dir, "auth.json");
|
|
8337
8379
|
const realAuth = path68.join(os12.homedir(), ".codex", "auth.json");
|
|
8338
|
-
if (!
|
|
8380
|
+
if (!fs83.existsSync(auth) && fs83.existsSync(realAuth)) {
|
|
8339
8381
|
try {
|
|
8340
|
-
|
|
8382
|
+
fs83.symlinkSync(realAuth, auth);
|
|
8341
8383
|
} catch {
|
|
8342
|
-
|
|
8384
|
+
fs83.copyFileSync(realAuth, auth);
|
|
8343
8385
|
}
|
|
8344
8386
|
}
|
|
8345
8387
|
const cfg = path68.join(dir, "config.toml");
|
|
8346
|
-
if (!
|
|
8347
|
-
|
|
8388
|
+
if (!fs83.existsSync(cfg)) {
|
|
8389
|
+
fs83.writeFileSync(cfg, "# lobstah-managed: auth inherited from ~/.codex, interactive config not\n");
|
|
8348
8390
|
}
|
|
8349
8391
|
return dir;
|
|
8350
8392
|
}
|
|
@@ -8970,7 +9012,7 @@ var init_wallclock = __esm({
|
|
|
8970
9012
|
import { execFile as execFile5 } from "node:child_process";
|
|
8971
9013
|
import { promisify as promisify5 } from "node:util";
|
|
8972
9014
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
8973
|
-
import * as
|
|
9015
|
+
import * as fs84 from "node:fs";
|
|
8974
9016
|
import * as path69 from "node:path";
|
|
8975
9017
|
function firstLine2(err) {
|
|
8976
9018
|
const stderr = err && typeof err === "object" && "stderr" in err ? String(err.stderr).trim() : "";
|
|
@@ -9007,7 +9049,7 @@ function keepRemote(opts) {
|
|
|
9007
9049
|
const root = path69.resolve(path69.dirname(fileURLToPath4(import.meta.url)), "..", "..", "..");
|
|
9008
9050
|
const localCli = path69.join(root, "apps", "cli", "dist", "main.js");
|
|
9009
9051
|
const args = ["watch", "add", url, "--for", id];
|
|
9010
|
-
if (
|
|
9052
|
+
if (fs84.existsSync(localCli))
|
|
9011
9053
|
await command(process.execPath, [localCli, ...args], 15e3);
|
|
9012
9054
|
else
|
|
9013
9055
|
await command("lobstah", args, 15e3);
|
|
@@ -9169,7 +9211,7 @@ var init_remote = __esm({
|
|
|
9169
9211
|
});
|
|
9170
9212
|
|
|
9171
9213
|
// packages/runner/dist/run.js
|
|
9172
|
-
import * as
|
|
9214
|
+
import * as fs85 from "node:fs";
|
|
9173
9215
|
import * as path70 from "node:path";
|
|
9174
9216
|
import { execFileSync as execFileSync9 } from "node:child_process";
|
|
9175
9217
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
@@ -9208,13 +9250,13 @@ async function main(activeDir, lane, seams = {}) {
|
|
|
9208
9250
|
throw new Error(`refusing headless runner for trap catch ${id}`);
|
|
9209
9251
|
const status = (verb, note) => appendStatus(id, lane, verb, note);
|
|
9210
9252
|
const cfg = loadConfig();
|
|
9211
|
-
const descriptor = JSON.parse(
|
|
9253
|
+
const descriptor = JSON.parse(fs85.readFileSync(path70.join(activeDir, "descriptor.json"), "utf8"));
|
|
9212
9254
|
const resolved = resolveDispatch(descriptor, cfg);
|
|
9213
9255
|
const repo = cfg.repos[descriptor.repo];
|
|
9214
9256
|
const briefFile = path70.join(activeDir, "brief.md");
|
|
9215
|
-
if (!
|
|
9216
|
-
|
|
9217
|
-
const brief =
|
|
9257
|
+
if (!fs85.existsSync(briefFile))
|
|
9258
|
+
fs85.writeFileSync(briefFile, descriptor.brief);
|
|
9259
|
+
const brief = fs85.readFileSync(briefFile, "utf8");
|
|
9218
9260
|
const attempts = Number(process.env.LOBSTAH_ATTEMPTS ?? "1");
|
|
9219
9261
|
const wake = process.env.LOBSTAH_WAKE;
|
|
9220
9262
|
const runnerInfo = {
|
|
@@ -9222,7 +9264,7 @@ async function main(activeDir, lane, seams = {}) {
|
|
|
9222
9264
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9223
9265
|
attempts
|
|
9224
9266
|
};
|
|
9225
|
-
|
|
9267
|
+
fs85.writeFileSync(path70.join(activeDir, "runner.json"), JSON.stringify(runnerInfo, null, 2));
|
|
9226
9268
|
if (descriptor.systemRepair && descriptor.pr?.url) {
|
|
9227
9269
|
const state = deps.prState(descriptor.pr.url) ?? readPr(parsePrRef(descriptor.pr.url)?.key ?? "")?.state;
|
|
9228
9270
|
if (state === "MERGED" || state === "CLOSED") {
|
|
@@ -9243,11 +9285,11 @@ async function main(activeDir, lane, seams = {}) {
|
|
|
9243
9285
|
let recorded;
|
|
9244
9286
|
let recovered = false;
|
|
9245
9287
|
let choice3;
|
|
9246
|
-
if (
|
|
9247
|
-
recorded = JSON.parse(
|
|
9248
|
-
if (!
|
|
9288
|
+
if (fs85.existsSync(wtFile)) {
|
|
9289
|
+
recorded = JSON.parse(fs85.readFileSync(wtFile, "utf8")).path;
|
|
9290
|
+
if (!fs85.existsSync(recorded))
|
|
9249
9291
|
throw new Error(`recorded worktree ${recorded} is gone`);
|
|
9250
|
-
} else if (
|
|
9292
|
+
} else if (fs85.existsSync(worktreePath(id))) {
|
|
9251
9293
|
recorded = await recoverWorktree(repo, id);
|
|
9252
9294
|
recovered = true;
|
|
9253
9295
|
} else if (descriptor.followUp && cfg.limits.reuseWorktree !== false) {
|
|
@@ -9277,7 +9319,7 @@ async function main(activeDir, lane, seams = {}) {
|
|
|
9277
9319
|
throw new Error(`worktree at ${cwd} is in use by ${held.id} \u2014 wait for that dispatch to release it before retrying`);
|
|
9278
9320
|
if (recovered) {
|
|
9279
9321
|
await deps.prepareReuse(repo, cwd);
|
|
9280
|
-
|
|
9322
|
+
fs85.writeFileSync(wtFile, JSON.stringify({ path: cwd }, null, 2));
|
|
9281
9323
|
}
|
|
9282
9324
|
} else if (choice3?.reuse) {
|
|
9283
9325
|
cwd = choice3.path;
|
|
@@ -9289,13 +9331,13 @@ async function main(activeDir, lane, seams = {}) {
|
|
|
9289
9331
|
type: "runner",
|
|
9290
9332
|
data: { worktree: cwd, worktreeOf: choice3.owner, setupRan }
|
|
9291
9333
|
});
|
|
9292
|
-
|
|
9334
|
+
fs85.writeFileSync(wtFile, JSON.stringify({ path: cwd, of: choice3.owner }, null, 2));
|
|
9293
9335
|
} else {
|
|
9294
9336
|
cwd = await deps.allocate(repo, id, descriptor.systemRepair ? descriptor.pr?.headRefName : void 0);
|
|
9295
9337
|
acquireWorktreeLock(cwd, id, lane);
|
|
9296
|
-
|
|
9338
|
+
fs85.writeFileSync(wtFile, JSON.stringify({ path: cwd }, null, 2));
|
|
9297
9339
|
}
|
|
9298
|
-
const worktreeOf = JSON.parse(
|
|
9340
|
+
const worktreeOf = JSON.parse(fs85.readFileSync(wtFile, "utf8")).of;
|
|
9299
9341
|
mergeEvidence(id, lane, { worktree: cwd, worktreeOf });
|
|
9300
9342
|
const existingPr = boundPr(descriptor, lane);
|
|
9301
9343
|
if (existingPr)
|
|
@@ -9330,13 +9372,13 @@ async function main(activeDir, lane, seams = {}) {
|
|
|
9330
9372
|
const planNudge = plan.cold && !envNudge ? coldNote(plan.cold, cwd, repo.trunk) : void 0;
|
|
9331
9373
|
const promptWith = (nudge) => buildPrompt(brief, { id, nudge, attachments: descriptor.attachments, existingPr });
|
|
9332
9374
|
const binDir = path70.resolve(path70.dirname(fileURLToPath5(import.meta.url)), "..", "..", "..", "bin");
|
|
9333
|
-
const workerPath =
|
|
9375
|
+
const workerPath = fs85.existsSync(path70.join(binDir, "lobstah")) ? `${binDir}${path70.delimiter}${process.env.PATH ?? ""}` : void 0;
|
|
9334
9376
|
let wallClockHit = false;
|
|
9335
9377
|
let current;
|
|
9336
9378
|
const wallFile = path70.join(activeDir, "wallclock.json");
|
|
9337
9379
|
const wallState = (() => {
|
|
9338
9380
|
try {
|
|
9339
|
-
return JSON.parse(
|
|
9381
|
+
return JSON.parse(fs85.readFileSync(wallFile, "utf8"));
|
|
9340
9382
|
} catch {
|
|
9341
9383
|
return { elapsedMs: 0, windowMs: resolved.limits.wallClockSecs * 1e3 };
|
|
9342
9384
|
}
|
|
@@ -9352,8 +9394,8 @@ async function main(activeDir, lane, seams = {}) {
|
|
|
9352
9394
|
const maxWallMs = Math.max(resolved.limits.wallClockSecs ?? 0, cfg.limits.maxWallClockSecs ?? (resolved.limits.wallClockSecs ?? 0) * 4) * 1e3;
|
|
9353
9395
|
const persistWall = (elapsedMs, windowMs) => {
|
|
9354
9396
|
const tmp = `${wallFile}.tmp-${process.pid}`;
|
|
9355
|
-
|
|
9356
|
-
|
|
9397
|
+
fs85.writeFileSync(tmp, JSON.stringify({ ...wallState, elapsedMs, windowMs }));
|
|
9398
|
+
fs85.renameSync(tmp, wallFile);
|
|
9357
9399
|
};
|
|
9358
9400
|
const madeProgress = () => {
|
|
9359
9401
|
const activity = readActivity(id, lane);
|
|
@@ -9647,7 +9689,7 @@ __export(dist_exports, {
|
|
|
9647
9689
|
startedBy: () => startedBy,
|
|
9648
9690
|
stopPids: () => stopPids
|
|
9649
9691
|
});
|
|
9650
|
-
import * as
|
|
9692
|
+
import * as fs86 from "node:fs";
|
|
9651
9693
|
import * as path71 from "node:path";
|
|
9652
9694
|
function runRunner(activeDirArg2, laneArg2) {
|
|
9653
9695
|
const lane = laneArg2 === "chore" ? "chore" : "work";
|
|
@@ -9657,7 +9699,7 @@ function runRunner(activeDirArg2, laneArg2) {
|
|
|
9657
9699
|
if (!isFinished(id, lane)) {
|
|
9658
9700
|
appendStatus(id, lane, "failed", String(err instanceof Error ? err.message : err).slice(0, 500));
|
|
9659
9701
|
}
|
|
9660
|
-
|
|
9702
|
+
fs86.renameSync(activeDirArg2, path71.join(laneDirs(lane).done, id));
|
|
9661
9703
|
} catch {
|
|
9662
9704
|
}
|
|
9663
9705
|
process.exitCode = 1;
|
|
@@ -9685,7 +9727,7 @@ var init_dist5 = __esm({
|
|
|
9685
9727
|
// apps/cli/src/main.ts
|
|
9686
9728
|
init_dist2();
|
|
9687
9729
|
init_dist3();
|
|
9688
|
-
import * as
|
|
9730
|
+
import * as fs87 from "node:fs";
|
|
9689
9731
|
import * as os13 from "node:os";
|
|
9690
9732
|
import * as path72 from "node:path";
|
|
9691
9733
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
@@ -11363,16 +11405,23 @@ function rebaseBrief(pr, id, trunk) {
|
|
|
11363
11405
|
conflictUpdate(pr.baseRef, pr.headRef, standalone),
|
|
11364
11406
|
pushRule(pr.headRef, id, standalone ? "merge" : "rebase"),
|
|
11365
11407
|
`Do not merge the PR. Do not change anything beyond conflict resolution.`,
|
|
11366
|
-
|
|
11367
|
-
pr.reviews.length > 0 ? rerequestReview({ url: pr.url, number: pr.number }, id, [...new Set(pr.reviews.map((r) => r.author))]) : `When pushed, report status done.`
|
|
11408
|
+
`When pushed, report status done.`
|
|
11368
11409
|
].join(" ");
|
|
11369
11410
|
}
|
|
11370
|
-
function choreVerdict(uuid) {
|
|
11411
|
+
function choreVerdict(uuid, pr) {
|
|
11412
|
+
const last = readStatusLog(uuid, "chore").at(-1);
|
|
11413
|
+
const pausedOnReview = last?.verb === "paused" && last.waitingOn === "review";
|
|
11414
|
+
if (last?.verb === "done" || pausedOnReview) {
|
|
11415
|
+
const startedHead = storedDescriptor(uuid, "chore")?.pr?.headSha;
|
|
11416
|
+
const unchangedConflict = pr.mergeableState === "dirty" && startedHead === pr.headSha;
|
|
11417
|
+
return { state: unchangedConflict ? "failed" : "done", pausedOnReview };
|
|
11418
|
+
}
|
|
11419
|
+
if (last?.verb === "paused")
|
|
11420
|
+
return { state: "active" };
|
|
11371
11421
|
const d = laneDirs("chore");
|
|
11372
11422
|
if (fs46.existsSync(path41.join(d.active, uuid)) || fs46.existsSync(path41.join(d.queue, `${uuid}.json`)))
|
|
11373
|
-
return "active";
|
|
11374
|
-
|
|
11375
|
-
return last === "done" ? "done" : "failed";
|
|
11423
|
+
return { state: "active" };
|
|
11424
|
+
return { state: "failed" };
|
|
11376
11425
|
}
|
|
11377
11426
|
async function mergeLoop(ms, policy, state, log = () => {
|
|
11378
11427
|
}) {
|
|
@@ -11416,12 +11465,12 @@ async function mergeLoop(ms, policy, state, log = () => {
|
|
|
11416
11465
|
observe(candidate, "rebase-failed");
|
|
11417
11466
|
continue;
|
|
11418
11467
|
}
|
|
11419
|
-
const verdict = choreVerdict(rebase.uuid);
|
|
11420
|
-
if (verdict === "active") {
|
|
11468
|
+
const verdict = choreVerdict(rebase.uuid, candidate);
|
|
11469
|
+
if (verdict.state === "active") {
|
|
11421
11470
|
observe(candidate, `conflict-chore:${rebase.uuid}`);
|
|
11422
11471
|
continue;
|
|
11423
11472
|
}
|
|
11424
|
-
if (verdict === "failed") {
|
|
11473
|
+
if (verdict.state === "failed") {
|
|
11425
11474
|
await ms.comment(candidate.number, `Automated rebase failed (dispatch ${rebase.uuid}) \u2014 needs a human.`);
|
|
11426
11475
|
await ms.addLabel(candidate.number, "needs-human");
|
|
11427
11476
|
state.failRebase(prKey);
|
|
@@ -11429,6 +11478,8 @@ async function mergeLoop(ms, policy, state, log = () => {
|
|
|
11429
11478
|
observe(candidate, "rebase-failed");
|
|
11430
11479
|
continue;
|
|
11431
11480
|
}
|
|
11481
|
+
if (verdict.pausedOnReview)
|
|
11482
|
+
log(`${prKey}: rebase chore ${rebase.uuid} paused on review \u2014 treating as finished; the merge gate owns the review wait`);
|
|
11432
11483
|
state.clearRebase(prKey);
|
|
11433
11484
|
}
|
|
11434
11485
|
const provisional = qualifiedApproval(policy, candidate);
|
|
@@ -11950,7 +12001,10 @@ function pruneStaleAcks(standing) {
|
|
|
11950
12001
|
return pruned;
|
|
11951
12002
|
}
|
|
11952
12003
|
function ackItemExists(key, culling = /* @__PURE__ */ new Set()) {
|
|
11953
|
-
if (key.startsWith("stack:"))
|
|
12004
|
+
if (key.startsWith("stack:")) {
|
|
12005
|
+
const stack = readStackEpochs()[key.slice("stack:".length)];
|
|
12006
|
+
return stack ? Object.keys(stack.heads).some((pr) => readPr(pr)?.state === "OPEN") : readPr(key.slice("stack:".length))?.state === "OPEN";
|
|
12007
|
+
}
|
|
11954
12008
|
if (key.startsWith("watch:")) return readWatch(key.slice("watch:".length)) !== void 0;
|
|
11955
12009
|
if (key.startsWith("decision:")) return readDecision(key) !== void 0 && readDecisionAnswer(key) === void 0;
|
|
11956
12010
|
if (key.startsWith("report:")) {
|
|
@@ -12047,6 +12101,15 @@ function reportAck(r) {
|
|
|
12047
12101
|
const ack = currentAck(r.key, r.stateHash);
|
|
12048
12102
|
return ack ? { at: ack.at, by: ack.by } : void 0;
|
|
12049
12103
|
}
|
|
12104
|
+
function viewReport(key, now2 = /* @__PURE__ */ new Date()) {
|
|
12105
|
+
const r = readReport(key);
|
|
12106
|
+
if (!r) return void 0;
|
|
12107
|
+
const held = currentAck(r.key, r.stateHash);
|
|
12108
|
+
if (held) return { key: r.key, viewedAt: held.at, by: held.by, first: false };
|
|
12109
|
+
const ack = { key: r.key, kind: "report", stateHash: r.stateHash, at: now2.toISOString(), by: "glass" };
|
|
12110
|
+
writeAck(ack);
|
|
12111
|
+
return { key: r.key, viewedAt: ack.at, by: ack.by, first: true };
|
|
12112
|
+
}
|
|
12050
12113
|
function reportRows() {
|
|
12051
12114
|
return listReports().map((r) => ({
|
|
12052
12115
|
key: r.key,
|
|
@@ -12322,7 +12385,7 @@ function deriveGlassPrs(dispatches, watches = [], records = [], stackOptions = {
|
|
|
12322
12385
|
for (const d of dispatches) if (d.pr && !recordByUrl.has(d.pr.url) && (!evidenceByUrl.has(d.pr.url) || evidenceByUrl.get(d.pr.url).observedAt < d.pr.observedAt)) {
|
|
12323
12386
|
evidenceByUrl.set(d.pr.url, d.pr);
|
|
12324
12387
|
}
|
|
12325
|
-
const readiness = derivePrStacks([...evidenceByUrl.values()], stackOptions);
|
|
12388
|
+
const readiness = identifyPrStacks(derivePrStacks([...evidenceByUrl.values()], stackOptions));
|
|
12326
12389
|
for (const [id, members] of groupedStacks) {
|
|
12327
12390
|
const root = members.find((p) => p.key === id);
|
|
12328
12391
|
const ordered = [];
|
|
@@ -12355,6 +12418,8 @@ function deriveGlassPrs(dispatches, watches = [], records = [], stackOptions = {
|
|
|
12355
12418
|
});
|
|
12356
12419
|
const status = readiness.find((s) => s.members.some((p) => members.some((m) => p.url === m.url)));
|
|
12357
12420
|
if (status) stacks.at(-1).readiness = {
|
|
12421
|
+
id: status.id,
|
|
12422
|
+
url: status.members.at(-1).url,
|
|
12358
12423
|
ready: status.ready,
|
|
12359
12424
|
total: status.members.length,
|
|
12360
12425
|
allReady: status.allReady,
|
|
@@ -12714,6 +12779,15 @@ function progressStreak(pr) {
|
|
|
12714
12779
|
if (!own && s.startHead !== void 0 && s.startHead !== pr.headSha) return { count: 0 };
|
|
12715
12780
|
return { count: s.count + 1 };
|
|
12716
12781
|
}
|
|
12782
|
+
function completedRepair(pr, now2) {
|
|
12783
|
+
const id = pr.repairCooldown?.dispatchId ?? pr.repairStreak?.lastRepairId ?? pr.repair?.dispatchId;
|
|
12784
|
+
if (!id || pr.repairCooldown?.endedAt || !isTerminal2(id)) return pr;
|
|
12785
|
+
const last = readStatusLog(id, laneOf(id) ?? "chore").at(-1);
|
|
12786
|
+
const at = last && ["done", "failed"].includes(last.verb) ? Date.parse(last.at) : NaN;
|
|
12787
|
+
const after = { ...pr, repairCooldown: { ...pr.repairCooldown, dispatchId: id, endedAt: new Date(Number.isFinite(at) ? at : now2).toISOString() } };
|
|
12788
|
+
writePr(after);
|
|
12789
|
+
return after;
|
|
12790
|
+
}
|
|
12717
12791
|
function stackedAbove(pr, records, workers) {
|
|
12718
12792
|
const out = [];
|
|
12719
12793
|
for (const p of prsAbove(records, pr.key)) {
|
|
@@ -12750,8 +12824,9 @@ function deliverPrRepairs(log, cap = 3, opts = {}) {
|
|
|
12750
12824
|
}
|
|
12751
12825
|
const watch2 = w;
|
|
12752
12826
|
withPrLock(watch2.key, () => {
|
|
12753
|
-
|
|
12827
|
+
let pr = readPr(watch2.key);
|
|
12754
12828
|
if (!pr || pr.state !== "OPEN" || pr.dispatches.length === 0 || (pr.observations ?? 0) <= 1) return;
|
|
12829
|
+
pr = completedRepair(pr, now2);
|
|
12755
12830
|
const owner = watch2.owner.slice("dispatch:".length);
|
|
12756
12831
|
const origin = storedDescriptor(owner, "work")?.followUp;
|
|
12757
12832
|
const ancestorPr = origin && chainPr(origin, "work");
|
|
@@ -12897,9 +12972,16 @@ function deliverPrRepairs(log, cap = 3, opts = {}) {
|
|
|
12897
12972
|
log(`repair ${watch2.key}: stopped \u2014 ${reason}`);
|
|
12898
12973
|
return;
|
|
12899
12974
|
}
|
|
12975
|
+
const end = Date.parse(pr.repairCooldown?.endedAt ?? "");
|
|
12976
|
+
const cooldownUntil = end + cfg.repairCooldownSecs * 1e3;
|
|
12977
|
+
if (cfg.repairCooldownSecs > 0 && !pr.repairCooldown?.released && now2 < cooldownUntil) {
|
|
12978
|
+
const until2 = new Date(cooldownUntil).toISOString();
|
|
12979
|
+
return wait({ heldBy: "cooldown", reason: `cooldown until ${until2}` }, until2);
|
|
12980
|
+
}
|
|
12900
12981
|
const id = randomUUID6();
|
|
12901
12982
|
writePr({
|
|
12902
12983
|
...pr,
|
|
12984
|
+
repairCooldown: { dispatchId: id },
|
|
12903
12985
|
repairStreak: { count: streak.count, lastRepairId: id, startHead: pr.headSha },
|
|
12904
12986
|
repair: {
|
|
12905
12987
|
headSha: pr.headSha,
|
|
@@ -13136,36 +13218,12 @@ function prAttention(now2, observed2 = observedPrs(), cfg = loadConfig()) {
|
|
|
13136
13218
|
Object.values(readPickupMap()).filter((e) => e.kind === "review").map((e) => e.uuid)
|
|
13137
13219
|
);
|
|
13138
13220
|
const out = [];
|
|
13139
|
-
const stacks = derivePrStacks(observed2.map((x) => x.pr), {
|
|
13221
|
+
const stacks = identifyPrStacks(derivePrStacks(observed2.map((x) => x.pr), {
|
|
13140
13222
|
trunk: (repo) => prStackTrunk(cfg, repo),
|
|
13141
13223
|
readySettleSecs: cfg.readySettleSecs,
|
|
13142
13224
|
now: now2
|
|
13143
|
-
});
|
|
13225
|
+
}));
|
|
13144
13226
|
const epochs = readStackEpochs();
|
|
13145
|
-
const quietReady = /* @__PURE__ */ new Set([
|
|
13146
|
-
...stacks.flatMap((s) => s.members.map((p) => p.url)),
|
|
13147
|
-
...quietStackTailUrls(observed2.map((x) => x.pr), epochs)
|
|
13148
|
-
]);
|
|
13149
|
-
for (const s of stacks.filter((s2) => s2.allReady)) {
|
|
13150
|
-
const top = s.members.at(-1), ref = parsePrRef(top.url);
|
|
13151
|
-
const epoch = epochs[s.id];
|
|
13152
|
-
const at = epoch?.ready ? epoch.at : new Date(now2).toISOString();
|
|
13153
|
-
const source = observed2.find((x) => x.pr.url === top.url);
|
|
13154
|
-
out.push({
|
|
13155
|
-
kind: "stack-ready",
|
|
13156
|
-
key: `stack:${s.id}`,
|
|
13157
|
-
stateHash: stackStateHash(s, epoch?.epoch),
|
|
13158
|
-
id: source.id,
|
|
13159
|
-
lane: source.lane,
|
|
13160
|
-
verb: "stack-ready",
|
|
13161
|
-
at,
|
|
13162
|
-
standingSince: at,
|
|
13163
|
-
ageSecs: Math.max(0, Math.round((now2 - Date.parse(at)) / 1e3)),
|
|
13164
|
-
note: s.text,
|
|
13165
|
-
repo: source.dispatch ? repoOf(source.id, source.lane) : repoKey(cfg, s.repo),
|
|
13166
|
-
prUrl: ref.url
|
|
13167
|
-
});
|
|
13168
|
-
}
|
|
13169
13227
|
for (const { id, lane, pr, dispatch } of observed2) {
|
|
13170
13228
|
const kinds = prKinds(pr, { readySettleSecs: cfg.readySettleSecs, now: now2 });
|
|
13171
13229
|
if (kinds.length === 0) continue;
|
|
@@ -13174,6 +13232,10 @@ function prAttention(now2, observed2 = observedPrs(), cfg = loadConfig()) {
|
|
|
13174
13232
|
const watchFollowUp = prWatch?.lastFollowUpId;
|
|
13175
13233
|
const chain = dispatch ? chainOf(id) : [];
|
|
13176
13234
|
for (const kind of kinds) {
|
|
13235
|
+
if (kind === "pr:checks" && stacks.some((s) => s.members.some((p) => p.url === pr.url))) {
|
|
13236
|
+
const gates = ["Approval Gate", ...humanGatesFor(readPr(pr.url), ref ? repoKey(cfg, `${ref.owner}/${ref.repo}`) : void 0)];
|
|
13237
|
+
if (pr.failingChecks?.length === pr.checks.failed && pr.failingChecks.every((c) => matchesGate(c.name, gates))) continue;
|
|
13238
|
+
}
|
|
13177
13239
|
const human = humanPrAttention(pr, kind, cfg, !!prWatch && !prWatch.done, now2);
|
|
13178
13240
|
if (!human.show) continue;
|
|
13179
13241
|
const repairCannotAct = human.reason !== void 0 && !["auto-repair is off", "not owned by lobstah", "no active PR watch"].includes(human.reason);
|
|
@@ -13182,7 +13244,6 @@ function prAttention(now2, observed2 = observedPrs(), cfg = loadConfig()) {
|
|
|
13182
13244
|
const standingSince = kind === "pr:ready" ? new Date(Date.parse(conditionSince) + cfg.readySettleSecs * 1e3).toISOString() : conditionSince;
|
|
13183
13245
|
out.push({
|
|
13184
13246
|
kind,
|
|
13185
|
-
...kind === "pr:ready" && quietReady.has(pr.url) ? { quiet: true } : {},
|
|
13186
13247
|
key: ref?.key ?? pr.url,
|
|
13187
13248
|
stateHash: prStateHash(pr),
|
|
13188
13249
|
id,
|
|
@@ -13205,6 +13266,38 @@ function prAttention(now2, observed2 = observedPrs(), cfg = loadConfig()) {
|
|
|
13205
13266
|
});
|
|
13206
13267
|
}
|
|
13207
13268
|
}
|
|
13269
|
+
for (const s of stacks) {
|
|
13270
|
+
const members = s.members.map((pr) => {
|
|
13271
|
+
const items = out.filter((a) => a.prUrl === pr.url);
|
|
13272
|
+
return {
|
|
13273
|
+
key: parsePrRef(pr.url).key,
|
|
13274
|
+
url: pr.url,
|
|
13275
|
+
number: pr.number,
|
|
13276
|
+
note: items.map((a) => a.note).join("; ") || `#${pr.number} ${prBadge(pr).text}`,
|
|
13277
|
+
kinds: items.map((a) => a.kind)
|
|
13278
|
+
};
|
|
13279
|
+
});
|
|
13280
|
+
const human = out.filter((a) => s.members.some((p) => p.url === a.prUrl) && a.kind !== "pr:ready" && a.kind !== "pr:draft" && cfg.attentionKinds.includes(a.kind));
|
|
13281
|
+
for (let i = out.length - 1; i >= 0; i--) if (s.members.some((p) => p.url === out[i].prUrl)) out.splice(i, 1);
|
|
13282
|
+
const top = s.members.at(-1), source = observed2.find((x) => x.pr.url === top.url);
|
|
13283
|
+
const at = epochs[s.id]?.at ?? s.members[0].observedAt;
|
|
13284
|
+
out.push({
|
|
13285
|
+
kind: "stack-ready",
|
|
13286
|
+
key: `stack:${s.id}`,
|
|
13287
|
+
stateHash: human.length ? statusStateHash(stackStateHash(s, epochs[s.id]?.epoch), JSON.stringify(human.map((a) => [a.key, a.kind, a.stateHash]))) : stackStateHash(s, epochs[s.id]?.epoch),
|
|
13288
|
+
id: source.id,
|
|
13289
|
+
lane: source.lane,
|
|
13290
|
+
verb: "stack-ready",
|
|
13291
|
+
at,
|
|
13292
|
+
standingSince: at,
|
|
13293
|
+
ageSecs: Math.max(0, Math.round((now2 - Date.parse(at)) / 1e3)),
|
|
13294
|
+
note: s.text,
|
|
13295
|
+
quiet: !s.allReady && human.length === 0 || !!epochs[s.id]?.silent || !!s.legacy,
|
|
13296
|
+
stack: { ready: s.ready, total: members.length, allReady: s.allReady, members },
|
|
13297
|
+
repo: repoKey(cfg, s.repo),
|
|
13298
|
+
prUrl: top.url
|
|
13299
|
+
});
|
|
13300
|
+
}
|
|
13208
13301
|
return out;
|
|
13209
13302
|
}
|
|
13210
13303
|
function landedAttention(cfg, now2) {
|
|
@@ -13536,8 +13629,10 @@ function buildTendReport(now2 = Date.now()) {
|
|
|
13536
13629
|
{ readySettleSecs: cfg.readySettleSecs, now: now2, trunk: (r) => prStackTrunk(cfg, r) }
|
|
13537
13630
|
).stacks.filter((s) => s.open);
|
|
13538
13631
|
attention.push(...landedAttention(cfg, now2), ...prAttention(now2, observed2, cfg), ...reportAttention(now2), ...decisionAttention(now2));
|
|
13632
|
+
const stackKeys = new Set(attention.flatMap((a) => a.stack?.members.map((m) => `watch:${m.key}`) ?? []));
|
|
13633
|
+
for (let i = attention.length - 1; i >= 0; i--) if (attention[i].kind === "watch" && stackKeys.has(attention[i].key)) attention.splice(i, 1);
|
|
13539
13634
|
const enabled = new Set(cfg.attentionKinds);
|
|
13540
|
-
const walking = attention.filter((a) => a.kind === "watch" || (a.kind === "stack-ready" ? stackReadyEnabled(cfg) : enabled.has(a.kind)));
|
|
13635
|
+
const walking = attention.filter((a) => a.kind === "watch" || (a.kind === "stack-ready" ? stackReadyEnabled(cfg) || a.stack?.members.some((m) => m.kinds.some((kind) => enabled.has(kind))) : enabled.has(a.kind)));
|
|
13541
13636
|
const shown = enabled.has("decision") ? hideFramedQuestions(walking) : walking;
|
|
13542
13637
|
attention.length = 0;
|
|
13543
13638
|
attention.push(
|
|
@@ -13580,7 +13675,8 @@ function buildTendReport(now2 = Date.now()) {
|
|
|
13580
13675
|
const notices = listNotices(5).map((n) => ({
|
|
13581
13676
|
kind: n.kind,
|
|
13582
13677
|
ageMins: Math.max(0, Math.round((now2 - (Date.parse(n.at) || 0)) / 6e4)),
|
|
13583
|
-
text: n.text
|
|
13678
|
+
text: n.text,
|
|
13679
|
+
...n.url ? { url: n.url } : {}
|
|
13584
13680
|
}));
|
|
13585
13681
|
const helms = listHelms().map((h) => ({
|
|
13586
13682
|
grounds: h.grounds,
|
|
@@ -13653,7 +13749,9 @@ function renderTend(r) {
|
|
|
13653
13749
|
if (r.queueWait) lines.push(r.queueWait);
|
|
13654
13750
|
for (const stack of r.stacks) {
|
|
13655
13751
|
lines.push(`stack ${stack.numbers.map((n) => `#${n}`).join(" \u2192 ")}: next ${stack.nextNumber ? `#${stack.nextNumber}` : "none"}`);
|
|
13656
|
-
if (stack.readiness) lines.push(stack.readiness.text);
|
|
13752
|
+
if (stack.readiness) lines.push(`${stack.readiness.text} ${stack.readiness.url}`);
|
|
13753
|
+
const item = r.attention.find((a) => a.key === `stack:${stack.readiness?.id}`);
|
|
13754
|
+
if (item?.stack) for (const member of item.stack.members) lines.push(` ${member.note} ${member.url}`);
|
|
13657
13755
|
}
|
|
13658
13756
|
if (r.attention.length > 0) {
|
|
13659
13757
|
lines.push("");
|
|
@@ -13665,8 +13763,9 @@ function renderTend(r) {
|
|
|
13665
13763
|
verb: a.kind === "question" || a.kind === "watch" ? a.verb : a.kind === "landed" ? `landed (${a.verb})` : a.kind,
|
|
13666
13764
|
waitingMins: Math.round(a.ageSecs / 60),
|
|
13667
13765
|
held: a.held ? "yes" : "",
|
|
13668
|
-
// Whether the human has opened it in the glass's decision modal
|
|
13669
|
-
|
|
13766
|
+
// Whether the human has opened it in the glass's decision modal,
|
|
13767
|
+
// or acked a report (opening its page acks it).
|
|
13768
|
+
viewed: a.kind === "decision" || a.kind === "question" ? a.viewedAt ? `yes ${a.viewedAt}` : "no" : a.kind === "report" ? a.acked ? `yes ${a.acked.at}` : "no" : "",
|
|
13670
13769
|
note: named(a.prUrl ? `${a.note ?? ""} ${a.prUrl}`.trim() : a.note ?? "")
|
|
13671
13770
|
})),
|
|
13672
13771
|
["id", "verb", "waitingMins", "held", "viewed", "note"]
|
|
@@ -13782,7 +13881,7 @@ function renderTend(r) {
|
|
|
13782
13881
|
lines.push(
|
|
13783
13882
|
toonTable(
|
|
13784
13883
|
"notices (recent)",
|
|
13785
|
-
r.notices.map((n) => ({ kind: n.kind, ageMins: n.ageMins, text: n.text })),
|
|
13884
|
+
r.notices.map((n) => ({ kind: n.kind, ageMins: n.ageMins, text: `${n.text}${n.url ? ` ${n.url}` : ""}` })),
|
|
13786
13885
|
["kind", "ageMins", "text"]
|
|
13787
13886
|
)
|
|
13788
13887
|
);
|
|
@@ -13869,7 +13968,7 @@ function buildDigest(opts = {}) {
|
|
|
13869
13968
|
landed,
|
|
13870
13969
|
arisen,
|
|
13871
13970
|
standing,
|
|
13872
|
-
stacks: tend.stacks.filter((s) => inGrounds(repoKey(loadConfig(), s.repo)) && s.readiness).map((s) => s.readiness.text),
|
|
13971
|
+
stacks: tend.stacks.filter((s) => inGrounds(repoKey(loadConfig(), s.repo)) && s.readiness).map((s) => `${s.readiness.text} ${s.readiness.url}`),
|
|
13873
13972
|
traps,
|
|
13874
13973
|
verdict: tend.verdict,
|
|
13875
13974
|
counts: tend.counts
|
|
@@ -14381,13 +14480,82 @@ ${help}`;
|
|
|
14381
14480
|
|
|
14382
14481
|
// apps/cli/src/cull.ts
|
|
14383
14482
|
init_dist2();
|
|
14384
|
-
import * as
|
|
14483
|
+
import * as fs60 from "node:fs";
|
|
14385
14484
|
import * as path52 from "node:path";
|
|
14485
|
+
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
14486
|
+
|
|
14487
|
+
// apps/cli/src/merged-worktree-safety.ts
|
|
14488
|
+
init_dist2();
|
|
14489
|
+
init_dist3();
|
|
14386
14490
|
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
14491
|
+
import * as fs59 from "node:fs";
|
|
14492
|
+
var git7 = (dir, ...args) => {
|
|
14493
|
+
try {
|
|
14494
|
+
return execFileSync7("git", args, { cwd: dir, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
14495
|
+
} catch {
|
|
14496
|
+
return void 0;
|
|
14497
|
+
}
|
|
14498
|
+
};
|
|
14499
|
+
function mergedWorktreeUnsafe(dir, heads, trunk, fetched) {
|
|
14500
|
+
const status = git7(dir, "status", "--porcelain", "--untracked-files=all");
|
|
14501
|
+
if (status === void 0) return "unknown (not a readable git checkout; retry next pass)";
|
|
14502
|
+
if (status) return `dirty (${status.split("\n").length} files)`;
|
|
14503
|
+
const head = git7(dir, "rev-parse", "HEAD");
|
|
14504
|
+
const recorded = [...new Set(heads.filter((s) => /^[a-f0-9]{40,64}$/i.test(s)))];
|
|
14505
|
+
if (head && recorded.includes(head)) return void 0;
|
|
14506
|
+
let available = recorded.filter((s) => git7(dir, "cat-file", "-t", s) === "commit");
|
|
14507
|
+
const count = () => {
|
|
14508
|
+
const base = git7(dir, "rev-parse", "--verify", `refs/remotes/origin/${trunk}^{commit}`);
|
|
14509
|
+
const exclusions = [...available, ...base ? [base] : []];
|
|
14510
|
+
if (!exclusions.length) return void 0;
|
|
14511
|
+
const n2 = git7(dir, "rev-list", "--count", "HEAD", "--not", ...exclusions);
|
|
14512
|
+
return n2 === void 0 ? void 0 : Number(n2);
|
|
14513
|
+
};
|
|
14514
|
+
if (count() === 0) return void 0;
|
|
14515
|
+
const common = git7(dir, "rev-parse", "--path-format=absolute", "--git-common-dir") ?? dir;
|
|
14516
|
+
if (!fetched.has(common)) fetched.set(common, withRepoLockSync(dir, () => git7(dir, "fetch", "--quiet", "origin")) !== void 0);
|
|
14517
|
+
available = recorded.filter((s) => git7(dir, "cat-file", "-t", s) === "commit");
|
|
14518
|
+
if (count() === 0) return void 0;
|
|
14519
|
+
if (!fetched.get(common)) return "unknown (fetch failed, remote state unknown; retry next pass)";
|
|
14520
|
+
const n = count();
|
|
14521
|
+
if (n !== void 0 && recorded.length > 0 && available.length === recorded.length) return `ahead of PR head (${n} commits)`;
|
|
14522
|
+
return "unknown (recorded PR heads unavailable; retry next pass)";
|
|
14523
|
+
}
|
|
14524
|
+
function mergedCullProofs() {
|
|
14525
|
+
const proofs = /* @__PURE__ */ new Map();
|
|
14526
|
+
const records = readPrs();
|
|
14527
|
+
const config = loadConfig();
|
|
14528
|
+
for (const lane of ["work", "chore"]) {
|
|
14529
|
+
const done = laneDirs(lane).done;
|
|
14530
|
+
for (const id of fs59.readdirSync(done).filter((s) => !s.startsWith("."))) {
|
|
14531
|
+
const owner = dispatchWorktree(id, lane).owner;
|
|
14532
|
+
const ev = readEvidence(id, lane);
|
|
14533
|
+
const lineage = /* @__PURE__ */ new Set([id, ...followUpAncestors(id, lane)]);
|
|
14534
|
+
const prs = records.filter((r) => r.dispatches?.some((d) => lineage.has(d)) && (r.dispatches.includes(id) || ev.branch === r.headRefName));
|
|
14535
|
+
for (const url of dispatchPrUrls(ev)) {
|
|
14536
|
+
const ref = parsePrRef(url);
|
|
14537
|
+
if (!ref) continue;
|
|
14538
|
+
const r = readPr(ref.key) ?? (ev.pr?.url === url ? { ...ev.pr, key: ref.key, observedHeadShas: [] } : void 0);
|
|
14539
|
+
if (r && !prs.some((p) => p.key === r.key)) prs.push(r);
|
|
14540
|
+
}
|
|
14541
|
+
const proof = proofs.get(owner) ?? { heads: [], trunk: config.repos[storedDescriptor(id, lane)?.repo ?? ""]?.trunk ?? "main", keys: /* @__PURE__ */ new Set(), users: /* @__PURE__ */ new Set() };
|
|
14542
|
+
for (const r of prs) {
|
|
14543
|
+
if (r.state !== "MERGED") continue;
|
|
14544
|
+
proof.heads.push(...r.observedHeadShas ?? [], r.headSha);
|
|
14545
|
+
proof.keys.add(r.key);
|
|
14546
|
+
}
|
|
14547
|
+
proof.users.add(id);
|
|
14548
|
+
proofs.set(owner, proof);
|
|
14549
|
+
}
|
|
14550
|
+
}
|
|
14551
|
+
return new Map([...proofs].filter(([, p]) => p.keys.size > 0));
|
|
14552
|
+
}
|
|
14553
|
+
|
|
14554
|
+
// apps/cli/src/cull.ts
|
|
14387
14555
|
var DAY = 864e5;
|
|
14388
14556
|
function walkBytes(target) {
|
|
14389
|
-
const stat =
|
|
14390
|
-
if (stat.isDirectory()) return
|
|
14557
|
+
const stat = fs60.lstatSync(target);
|
|
14558
|
+
if (stat.isDirectory()) return fs60.readdirSync(target).reduce((sum, name) => sum + walkBytes(path52.join(target, name)), 0);
|
|
14391
14559
|
return stat.isFile() ? stat.size : 0;
|
|
14392
14560
|
}
|
|
14393
14561
|
var sizing = {
|
|
@@ -14402,7 +14570,7 @@ var sizing = {
|
|
|
14402
14570
|
worktree(target, platform = process.platform) {
|
|
14403
14571
|
if (platform !== "win32") {
|
|
14404
14572
|
try {
|
|
14405
|
-
const out =
|
|
14573
|
+
const out = execFileSync8("du", ["-sk", target], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
14406
14574
|
const kb = Number(out.trim().split(/\s+/)[0]);
|
|
14407
14575
|
if (Number.isFinite(kb)) return kb * 1024;
|
|
14408
14576
|
} catch {
|
|
@@ -14413,7 +14581,7 @@ var sizing = {
|
|
|
14413
14581
|
};
|
|
14414
14582
|
function idsIn(dir) {
|
|
14415
14583
|
try {
|
|
14416
|
-
return new Set(
|
|
14584
|
+
return new Set(fs60.readdirSync(dir).filter((f) => !f.startsWith(".")).map((f) => f.replace(/\.json$/, "")));
|
|
14417
14585
|
} catch {
|
|
14418
14586
|
return /* @__PURE__ */ new Set();
|
|
14419
14587
|
}
|
|
@@ -14449,7 +14617,7 @@ function planCull(olderThanDays, now2 = Date.now(), opts = {}) {
|
|
|
14449
14617
|
retainReferences(id, lane);
|
|
14450
14618
|
continue;
|
|
14451
14619
|
}
|
|
14452
|
-
const m =
|
|
14620
|
+
const m = fs60.statSync(p).mtimeMs;
|
|
14453
14621
|
doneMtimes.set(id, m);
|
|
14454
14622
|
if (m >= cutoff) retainReferences(id, lane);
|
|
14455
14623
|
if (m < cutoff) items.push({ kind: "done", id, target: p, ageDays: Math.floor((now2 - m) / DAY), bytes: size(p), ageFrom: m });
|
|
@@ -14458,28 +14626,45 @@ function planCull(olderThanDays, now2 = Date.now(), opts = {}) {
|
|
|
14458
14626
|
const wtRoot = path52.join(lobstahHome(), "worktrees");
|
|
14459
14627
|
const trapped = trapWorktreeIds(wtRoot);
|
|
14460
14628
|
const usage = worktreeUsage(opts.keepOpenPrs ? openPr : void 0);
|
|
14629
|
+
const proofs = mergedCullProofs();
|
|
14630
|
+
const fetched = /* @__PURE__ */ new Map();
|
|
14631
|
+
const keptUsers = /* @__PURE__ */ new Set();
|
|
14632
|
+
const keptPrs = /* @__PURE__ */ new Set();
|
|
14633
|
+
for (const [owner, proof] of proofs) if (fs60.existsSync(path52.join(wtRoot, owner))) for (const key of proof.keys) keptPrs.add(key);
|
|
14461
14634
|
for (const id of idsIn(wtRoot)) {
|
|
14462
14635
|
if (live.has(id) || trapped.has(id) || usage.live.has(id)) continue;
|
|
14463
14636
|
const doneAt = usage.newest.get(id) ?? doneMtimes.get(id);
|
|
14464
14637
|
if (doneAt !== void 0 && doneAt >= cutoff) continue;
|
|
14465
14638
|
const p = path52.join(wtRoot, id);
|
|
14639
|
+
const proof = proofs.get(id);
|
|
14640
|
+
if (proof && mergedWorktreeUnsafe(p, proof.heads, proof.trunk, fetched)) {
|
|
14641
|
+
for (const user of proof.users) {
|
|
14642
|
+
live.add(user);
|
|
14643
|
+
keptUsers.add(user);
|
|
14644
|
+
retainReferences(user, "work");
|
|
14645
|
+
retainReferences(user, "chore");
|
|
14646
|
+
}
|
|
14647
|
+
for (const key of proof.keys) keptPrs.add(key);
|
|
14648
|
+
continue;
|
|
14649
|
+
}
|
|
14466
14650
|
if (readTrapAnchor(p)?.createdBy === "soak" && inspectTrapWorktree(p).reason) continue;
|
|
14467
|
-
const from = doneAt ??
|
|
14651
|
+
const from = doneAt ?? fs60.statSync(p).mtimeMs;
|
|
14468
14652
|
items.push({ kind: "worktree", id, target: p, ageDays: Math.floor((now2 - from) / DAY), bytes: measure ? sizing.worktree(p) : 0, ageFrom: from });
|
|
14469
14653
|
}
|
|
14654
|
+
for (let i = items.length - 1; i >= 0; i--) if (items[i].kind === "done" && keptUsers.has(items[i].id)) items.splice(i, 1);
|
|
14470
14655
|
for (const lane of ["work", "chore"]) {
|
|
14471
14656
|
const d = laneDirs(lane);
|
|
14472
14657
|
const groups = /* @__PURE__ */ new Map();
|
|
14473
|
-
for (const f of
|
|
14658
|
+
for (const f of fs60.readdirSync(d.state)) {
|
|
14474
14659
|
const target = path52.join(d.state, f);
|
|
14475
|
-
const directory =
|
|
14476
|
-
if (directory && !
|
|
14660
|
+
const directory = fs60.statSync(target).isDirectory();
|
|
14661
|
+
if (directory && !fs60.existsSync(path52.join(target, "attachments")) && !fs60.existsSync(path52.join(target, "report.json"))) continue;
|
|
14477
14662
|
const id = directory ? f : f.replace(/\.(status|events|evidence|attn|notified|runner\.log)$/, "");
|
|
14478
14663
|
if (id === f && !directory) continue;
|
|
14479
14664
|
if (live.has(id) || (doneMtimes.get(id) ?? 0) >= cutoff || referencedAttachmentState.has(path52.join(d.state, id))) continue;
|
|
14480
14665
|
const previous = groups.get(id);
|
|
14481
14666
|
groups.set(id, {
|
|
14482
|
-
mtime: Math.max(previous?.mtime ?? 0,
|
|
14667
|
+
mtime: Math.max(previous?.mtime ?? 0, fs60.statSync(target).mtimeMs),
|
|
14483
14668
|
bytes: (previous?.bytes ?? 0) + size(target)
|
|
14484
14669
|
});
|
|
14485
14670
|
}
|
|
@@ -14490,7 +14675,7 @@ function planCull(olderThanDays, now2 = Date.now(), opts = {}) {
|
|
|
14490
14675
|
}
|
|
14491
14676
|
}
|
|
14492
14677
|
for (const r of readPrs()) {
|
|
14493
|
-
if (r.state === "OPEN") continue;
|
|
14678
|
+
if (r.state === "OPEN" || keptPrs.has(r.key)) continue;
|
|
14494
14679
|
const seen = Date.parse(r.observedAt) || now2;
|
|
14495
14680
|
if (seen < cutoff) items.push({ kind: "pr", id: r.key, target: r.key, ageDays: Math.floor((now2 - seen) / DAY), bytes: size(prRecordFile(r.key)), ageFrom: seen });
|
|
14496
14681
|
}
|
|
@@ -14550,7 +14735,7 @@ function worktreeUsage(openPr) {
|
|
|
14550
14735
|
if (openPr?.has(id)) live.add(owner);
|
|
14551
14736
|
let m;
|
|
14552
14737
|
try {
|
|
14553
|
-
m =
|
|
14738
|
+
m = fs60.statSync(path52.join(d.done, id)).mtimeMs;
|
|
14554
14739
|
} catch {
|
|
14555
14740
|
continue;
|
|
14556
14741
|
}
|
|
@@ -14564,7 +14749,7 @@ function trapWorktreeIds(wtRoot) {
|
|
|
14564
14749
|
const real = (p) => {
|
|
14565
14750
|
let r;
|
|
14566
14751
|
try {
|
|
14567
|
-
r =
|
|
14752
|
+
r = fs60.realpathSync.native(p);
|
|
14568
14753
|
} catch {
|
|
14569
14754
|
r = path52.resolve(p);
|
|
14570
14755
|
}
|
|
@@ -14593,13 +14778,15 @@ function limitBatch(items, maxGroups) {
|
|
|
14593
14778
|
return { batch: items.filter((i) => keep.has(groupOf(i))), deferred: order.length - keep.size };
|
|
14594
14779
|
}
|
|
14595
14780
|
function planPressureCull(now2 = Date.now()) {
|
|
14781
|
+
const proofs = mergedCullProofs();
|
|
14782
|
+
const fetched = /* @__PURE__ */ new Map();
|
|
14596
14783
|
const live = /* @__PURE__ */ new Set();
|
|
14597
14784
|
const doneMtimes = /* @__PURE__ */ new Map();
|
|
14598
14785
|
for (const lane of ["work", "chore"]) {
|
|
14599
14786
|
const d = laneDirs(lane);
|
|
14600
14787
|
for (const id of idsIn(d.queue)) live.add(id);
|
|
14601
14788
|
for (const id of idsIn(d.active)) live.add(id);
|
|
14602
|
-
for (const id of idsIn(d.done)) doneMtimes.set(id,
|
|
14789
|
+
for (const id of idsIn(d.done)) doneMtimes.set(id, fs60.statSync(path52.join(d.done, id)).mtimeMs);
|
|
14603
14790
|
}
|
|
14604
14791
|
const openPr = openPrDispatches();
|
|
14605
14792
|
for (const id of openPr) live.add(id);
|
|
@@ -14611,8 +14798,10 @@ function planPressureCull(now2 = Date.now()) {
|
|
|
14611
14798
|
for (const id of idsIn(wtRoot)) {
|
|
14612
14799
|
if (live.has(id)) continue;
|
|
14613
14800
|
const p = path52.join(wtRoot, id);
|
|
14801
|
+
const proof = proofs.get(id);
|
|
14802
|
+
if (proof && mergedWorktreeUnsafe(p, proof.heads, proof.trunk, fetched)) continue;
|
|
14614
14803
|
if (readTrapAnchor(p)?.createdBy === "soak" && inspectTrapWorktree(p).reason) continue;
|
|
14615
|
-
const from = usage.newest.get(id) ?? doneMtimes.get(id) ??
|
|
14804
|
+
const from = usage.newest.get(id) ?? doneMtimes.get(id) ?? fs60.statSync(p).mtimeMs;
|
|
14616
14805
|
items.push({ kind: "worktree", id, target: p, ageDays: Math.floor((now2 - from) / DAY), bytes: 0, ageFrom: from });
|
|
14617
14806
|
}
|
|
14618
14807
|
return items.sort((a, b) => a.ageFrom - b.ageFrom || a.id.localeCompare(b.id));
|
|
@@ -14626,28 +14815,28 @@ function removeWorktree(id, dir) {
|
|
|
14626
14815
|
for (const lane of ["work", "chore"]) {
|
|
14627
14816
|
const descFile = path52.join(laneDirs(lane).done, id, "descriptor.json");
|
|
14628
14817
|
try {
|
|
14629
|
-
const desc = JSON.parse(
|
|
14818
|
+
const desc = JSON.parse(fs60.readFileSync(descFile, "utf8"));
|
|
14630
14819
|
const repo = loadConfig().repos[desc.repo];
|
|
14631
14820
|
if (repo) {
|
|
14632
|
-
|
|
14821
|
+
execFileSync8("git", ["worktree", "remove", "--force", dir], { cwd: repo.path, stdio: "ignore" });
|
|
14633
14822
|
return;
|
|
14634
14823
|
}
|
|
14635
14824
|
} catch {
|
|
14636
14825
|
}
|
|
14637
14826
|
}
|
|
14638
14827
|
try {
|
|
14639
|
-
const common =
|
|
14828
|
+
const common = execFileSync8("git", ["rev-parse", "--path-format=absolute", "--git-common-dir"], {
|
|
14640
14829
|
cwd: dir,
|
|
14641
14830
|
encoding: "utf8",
|
|
14642
14831
|
stdio: ["ignore", "pipe", "ignore"]
|
|
14643
14832
|
}).trim();
|
|
14644
14833
|
if (common) {
|
|
14645
|
-
|
|
14834
|
+
execFileSync8("git", ["--git-dir", common, "worktree", "remove", "--force", dir], { stdio: "ignore" });
|
|
14646
14835
|
return;
|
|
14647
14836
|
}
|
|
14648
14837
|
} catch {
|
|
14649
14838
|
}
|
|
14650
|
-
|
|
14839
|
+
fs60.rmSync(dir, { recursive: true, force: true });
|
|
14651
14840
|
}
|
|
14652
14841
|
function culledState(items) {
|
|
14653
14842
|
const lanes = new Map(["work", "chore"].map((lane) => [path52.resolve(laneDirs(lane).state), lane]));
|
|
@@ -14659,7 +14848,7 @@ function culledState(items) {
|
|
|
14659
14848
|
function applyCull(items) {
|
|
14660
14849
|
foldCatches(culledState(items));
|
|
14661
14850
|
for (const item of items.filter((i) => i.kind === "worktree")) removeWorktree(item.id, item.target);
|
|
14662
|
-
for (const item of items.filter((i) => i.kind === "done"))
|
|
14851
|
+
for (const item of items.filter((i) => i.kind === "done")) fs60.rmSync(item.target, { recursive: true, force: true });
|
|
14663
14852
|
for (const item of items.filter((i) => i.kind === "ack")) removeAck(item.target);
|
|
14664
14853
|
for (const item of items.filter((i) => i.kind === "pr")) removePr(item.target);
|
|
14665
14854
|
for (const item of items.filter((i) => i.kind === "report")) removeHelmReport(item.id);
|
|
@@ -14667,8 +14856,8 @@ function applyCull(items) {
|
|
|
14667
14856
|
for (const item of items.filter((i) => i.kind === "release")) removeRelease(item.id);
|
|
14668
14857
|
for (const item of items.filter((i) => i.kind === "state")) {
|
|
14669
14858
|
const dir = path52.dirname(item.target);
|
|
14670
|
-
for (const f of
|
|
14671
|
-
if (f === item.id || f.startsWith(`${item.id}.`))
|
|
14859
|
+
for (const f of fs60.readdirSync(dir)) {
|
|
14860
|
+
if (f === item.id || f.startsWith(`${item.id}.`)) fs60.rmSync(path52.join(dir, f), { recursive: true, force: true });
|
|
14672
14861
|
}
|
|
14673
14862
|
}
|
|
14674
14863
|
}
|
|
@@ -14737,13 +14926,11 @@ ${toonKV(statusFields(s))}${still}`;
|
|
|
14737
14926
|
|
|
14738
14927
|
// apps/cli/src/release.ts
|
|
14739
14928
|
init_dist2();
|
|
14740
|
-
|
|
14741
|
-
import * as fs60 from "node:fs";
|
|
14742
|
-
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
14929
|
+
import * as fs61 from "node:fs";
|
|
14743
14930
|
var DAY2 = 864e5;
|
|
14744
14931
|
function listIds(dir) {
|
|
14745
14932
|
try {
|
|
14746
|
-
return
|
|
14933
|
+
return fs61.readdirSync(dir).filter((f) => !f.startsWith(".")).map((f) => f.replace(/\.json$/, ""));
|
|
14747
14934
|
} catch {
|
|
14748
14935
|
return [];
|
|
14749
14936
|
}
|
|
@@ -14794,15 +14981,16 @@ function prUrlOf(ev) {
|
|
|
14794
14981
|
}
|
|
14795
14982
|
function mergedPrs(all) {
|
|
14796
14983
|
const merged = /* @__PURE__ */ new Map();
|
|
14797
|
-
const add2 = (url, key, headRef, id) => {
|
|
14798
|
-
const m = merged.get(key) ?? { url, key, headRef, owners: /* @__PURE__ */ new Set() };
|
|
14984
|
+
const add2 = (url, key, headRef, id, heads) => {
|
|
14985
|
+
const m = merged.get(key) ?? { url, key, headRef, owners: /* @__PURE__ */ new Set(), heads: [] };
|
|
14986
|
+
m.heads = [.../* @__PURE__ */ new Set([...m.heads, ...heads])];
|
|
14799
14987
|
m.headRef ??= headRef;
|
|
14800
14988
|
m.owners.add(id);
|
|
14801
14989
|
merged.set(key, m);
|
|
14802
14990
|
};
|
|
14803
14991
|
for (const r of readPrs()) {
|
|
14804
14992
|
if (r.state !== "MERGED") continue;
|
|
14805
|
-
for (const id of r.dispatches ?? []) if (all.has(id)) add2(r.url, r.key, r.headRefName, id);
|
|
14993
|
+
for (const id of r.dispatches ?? []) if (all.has(id)) add2(r.url, r.key, r.headRefName, id, [...r.observedHeadShas ?? [], r.headSha]);
|
|
14806
14994
|
}
|
|
14807
14995
|
for (const k of all.values()) {
|
|
14808
14996
|
if (k.bucket !== "done") continue;
|
|
@@ -14812,28 +15000,10 @@ function mergedPrs(all) {
|
|
|
14812
15000
|
if (!ref) continue;
|
|
14813
15001
|
const record = readPr(ref.key);
|
|
14814
15002
|
const state = record?.state ?? ev.pr?.state;
|
|
14815
|
-
if (state === "MERGED") add2(ref.url, ref.key, record?.headRefName ?? ev.pr?.headRefName, k.id);
|
|
15003
|
+
if (state === "MERGED") add2(ref.url, ref.key, record?.headRefName ?? ev.pr?.headRefName, k.id, record ? [...record.observedHeadShas ?? [], record.headSha] : ev.pr?.headSha ? [ev.pr.headSha] : []);
|
|
14816
15004
|
}
|
|
14817
15005
|
return merged;
|
|
14818
15006
|
}
|
|
14819
|
-
function git7(cwd, ...args) {
|
|
14820
|
-
try {
|
|
14821
|
-
return execFileSync8("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
14822
|
-
} catch {
|
|
14823
|
-
return void 0;
|
|
14824
|
-
}
|
|
14825
|
-
}
|
|
14826
|
-
function unsafe(dir, fetched) {
|
|
14827
|
-
const status = git7(dir, "status", "--porcelain");
|
|
14828
|
-
if (status === void 0) return "unpushed work: not a readable git checkout";
|
|
14829
|
-
if (status !== "") return "unpushed work: uncommitted changes";
|
|
14830
|
-
const common = git7(dir, "rev-parse", "--path-format=absolute", "--git-common-dir") ?? dir;
|
|
14831
|
-
if (!fetched.has(common)) fetched.set(common, withRepoLockSync(dir, () => git7(dir, "fetch", "--quiet", "origin")) !== void 0);
|
|
14832
|
-
if (!fetched.get(common)) return "unpushed work: fetch failed, remote state unknown";
|
|
14833
|
-
const remote = git7(dir, "branch", "-r", "--contains", "HEAD");
|
|
14834
|
-
if (remote === void 0 || remote === "") return "unpushed work: HEAD is not on the remote";
|
|
14835
|
-
return void 0;
|
|
14836
|
-
}
|
|
14837
15007
|
function planMergeRelease(now2 = Date.now(), batch = Infinity) {
|
|
14838
15008
|
const plan = { items: [], users: /* @__PURE__ */ new Map(), kept: [] };
|
|
14839
15009
|
if (!loadConfig().limits.releaseOnMerge) return plan;
|
|
@@ -14860,7 +15030,11 @@ function planMergeRelease(now2 = Date.now(), batch = Infinity) {
|
|
|
14860
15030
|
const chain = chains.get(id) ?? /* @__PURE__ */ new Set([id]);
|
|
14861
15031
|
const home = owner(id);
|
|
14862
15032
|
const add2 = (o) => {
|
|
14863
|
-
|
|
15033
|
+
const previous = candidates.get(o);
|
|
15034
|
+
if (previous) {
|
|
15035
|
+
previous.pr.heads = [.../* @__PURE__ */ new Set([...previous.pr.heads, ...pr.heads])];
|
|
15036
|
+
previous.chain = /* @__PURE__ */ new Set([...previous.chain, ...chain]);
|
|
15037
|
+
} else candidates.set(o, { pr: { ...pr, heads: [...pr.heads] }, chain });
|
|
14864
15038
|
};
|
|
14865
15039
|
add2(home);
|
|
14866
15040
|
for (const m of chain) {
|
|
@@ -14874,7 +15048,7 @@ function planMergeRelease(now2 = Date.now(), batch = Infinity) {
|
|
|
14874
15048
|
}
|
|
14875
15049
|
}
|
|
14876
15050
|
}
|
|
14877
|
-
const ordered = [...candidates.entries()].filter(([o]) =>
|
|
15051
|
+
const ordered = [...candidates.entries()].filter(([o]) => fs61.existsSync(worktreePath(o)) && !trapped.has(o)).map(([o, c]) => ({ o, c, from: usage.newest.get(o) ?? now2 })).sort((a, b) => a.from - b.from || a.o.localeCompare(b.o));
|
|
14878
15052
|
const fetched = /* @__PURE__ */ new Map();
|
|
14879
15053
|
let checked = 0;
|
|
14880
15054
|
for (const { o, c, from } of ordered) {
|
|
@@ -14884,7 +15058,8 @@ function planMergeRelease(now2 = Date.now(), batch = Infinity) {
|
|
|
14884
15058
|
if (![...c.chain].every((id) => finished(all.get(id))) || !users.every((k) => finished(k))) continue;
|
|
14885
15059
|
checked++;
|
|
14886
15060
|
const dir = worktreePath(o);
|
|
14887
|
-
const
|
|
15061
|
+
const repoKey2 = users.map((k) => storedDescriptor(k.id, k.lane)?.repo).find(Boolean);
|
|
15062
|
+
const why = mergedWorktreeUnsafe(dir, c.pr.heads, loadConfig().repos[repoKey2 ?? ""]?.trunk ?? "main", fetched);
|
|
14888
15063
|
if (why) {
|
|
14889
15064
|
plan.kept.push({ id: o, reason: why, pr: c.pr.key, at: new Date(now2).toISOString() });
|
|
14890
15065
|
continue;
|
|
@@ -14900,7 +15075,7 @@ function runMergeRelease(now2 = Date.now(), batch = Infinity) {
|
|
|
14900
15075
|
const released = [];
|
|
14901
15076
|
for (const item of plan.items) {
|
|
14902
15077
|
applyCull([item]);
|
|
14903
|
-
if (
|
|
15078
|
+
if (fs61.existsSync(item.target)) continue;
|
|
14904
15079
|
released.push(item.id);
|
|
14905
15080
|
for (const k of plan.users.get(item.id) ?? []) mergeEvidence(k.id, k.lane, { worktreeReleased: at });
|
|
14906
15081
|
}
|
|
@@ -15041,7 +15216,7 @@ full pattern and trade-offs: docs/man.md \xB7 config: docs/configuration.md`;
|
|
|
15041
15216
|
|
|
15042
15217
|
// apps/cli/src/decision-answer.ts
|
|
15043
15218
|
init_dist2();
|
|
15044
|
-
import * as
|
|
15219
|
+
import * as fs62 from "node:fs";
|
|
15045
15220
|
function standingQuestion2(key) {
|
|
15046
15221
|
return buildTendReport().attention.find((a) => a.kind === "question" && a.key === key);
|
|
15047
15222
|
}
|
|
@@ -15068,7 +15243,7 @@ function answerKey(key, req) {
|
|
|
15068
15243
|
try {
|
|
15069
15244
|
return { decision: meta, answer: answerDecision({ key: meta.key, ...req, maxBytes }) };
|
|
15070
15245
|
} catch (err) {
|
|
15071
|
-
|
|
15246
|
+
fs62.rmSync(decisionDir(meta.key), { recursive: true, force: true });
|
|
15072
15247
|
throw err;
|
|
15073
15248
|
}
|
|
15074
15249
|
}
|
|
@@ -15082,7 +15257,7 @@ function viewKey(key, now2 = /* @__PURE__ */ new Date()) {
|
|
|
15082
15257
|
// apps/cli/src/doctor.ts
|
|
15083
15258
|
init_dist();
|
|
15084
15259
|
init_dist2();
|
|
15085
|
-
import * as
|
|
15260
|
+
import * as fs68 from "node:fs";
|
|
15086
15261
|
import * as path58 from "node:path";
|
|
15087
15262
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
15088
15263
|
|
|
@@ -15099,7 +15274,7 @@ function parkedSummary(parked = parkedDispatches()) {
|
|
|
15099
15274
|
|
|
15100
15275
|
// apps/cli/src/plugin-version.ts
|
|
15101
15276
|
init_dist();
|
|
15102
|
-
import * as
|
|
15277
|
+
import * as fs63 from "node:fs";
|
|
15103
15278
|
import * as os7 from "node:os";
|
|
15104
15279
|
import * as path53 from "node:path";
|
|
15105
15280
|
var UPDATE_COMMAND = {
|
|
@@ -15108,7 +15283,7 @@ var UPDATE_COMMAND = {
|
|
|
15108
15283
|
};
|
|
15109
15284
|
var readJson2 = (file2) => {
|
|
15110
15285
|
try {
|
|
15111
|
-
return JSON.parse(
|
|
15286
|
+
return JSON.parse(fs63.readFileSync(file2, "utf8"));
|
|
15112
15287
|
} catch {
|
|
15113
15288
|
return void 0;
|
|
15114
15289
|
}
|
|
@@ -15139,7 +15314,7 @@ function installedCodexPlugin(opts = {}) {
|
|
|
15139
15314
|
const codexDir = env.CODEX_HOME ?? path53.join(opts.home ?? os7.homedir(), ".codex");
|
|
15140
15315
|
let cfg;
|
|
15141
15316
|
try {
|
|
15142
|
-
cfg = parse(
|
|
15317
|
+
cfg = parse(fs63.readFileSync(path53.join(codexDir, "config.toml"), "utf8"));
|
|
15143
15318
|
} catch {
|
|
15144
15319
|
return void 0;
|
|
15145
15320
|
}
|
|
@@ -15148,7 +15323,7 @@ function installedCodexPlugin(opts = {}) {
|
|
|
15148
15323
|
const cache = path53.join(codexDir, "plugins", "cache", "lobstah", "lobstah");
|
|
15149
15324
|
let dirs;
|
|
15150
15325
|
try {
|
|
15151
|
-
dirs =
|
|
15326
|
+
dirs = fs63.readdirSync(cache);
|
|
15152
15327
|
} catch {
|
|
15153
15328
|
return void 0;
|
|
15154
15329
|
}
|
|
@@ -15206,7 +15381,7 @@ function pluginBehindLine(cliVersion, opts = {}) {
|
|
|
15206
15381
|
|
|
15207
15382
|
// apps/cli/src/service.ts
|
|
15208
15383
|
init_dist2();
|
|
15209
|
-
import * as
|
|
15384
|
+
import * as fs64 from "node:fs";
|
|
15210
15385
|
import * as os8 from "node:os";
|
|
15211
15386
|
import * as path54 from "node:path";
|
|
15212
15387
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
@@ -15271,7 +15446,7 @@ function serviceFile(kind) {
|
|
|
15271
15446
|
return process.platform === "darwin" ? path54.join(os8.homedir(), "Library", "LaunchAgents", `lobstah.${kind}.plist`) : path54.join(os8.homedir(), ".config", "systemd", "user", `lobstah-${kind}.service`);
|
|
15272
15447
|
}
|
|
15273
15448
|
function serviceInstalled(kind) {
|
|
15274
|
-
return
|
|
15449
|
+
return fs64.existsSync(serviceFile(kind));
|
|
15275
15450
|
}
|
|
15276
15451
|
function restartCommand(kind, platform = process.platform, uid = process.getuid?.() ?? 0) {
|
|
15277
15452
|
return platform === "darwin" ? ["launchctl", "kickstart", "-k", `gui/${uid}/lobstah.${kind}`] : ["systemctl", "--user", "restart", `lobstah-${kind}.service`];
|
|
@@ -15290,7 +15465,7 @@ function installService(kind, port) {
|
|
|
15290
15465
|
if (process.platform === "win32") {
|
|
15291
15466
|
throw new Error(`no service manager support on Windows \u2014 run \`lobstah ${kind}\` under your process manager of choice`);
|
|
15292
15467
|
}
|
|
15293
|
-
const entry = COMPILED_BINARY ? "" :
|
|
15468
|
+
const entry = COMPILED_BINARY ? "" : fs64.realpathSync(process.argv[1]);
|
|
15294
15469
|
const spec = {
|
|
15295
15470
|
kind,
|
|
15296
15471
|
nodePath: process.execPath,
|
|
@@ -15303,16 +15478,16 @@ function installService(kind, port) {
|
|
|
15303
15478
|
if (kind !== "glass" && !onPath("git", spec.pathEnv)) {
|
|
15304
15479
|
throw new Error(`git is not reachable on the service PATH (${spec.pathEnv}) \u2014 the daemon cannot allocate worktrees`);
|
|
15305
15480
|
}
|
|
15306
|
-
|
|
15481
|
+
fs64.mkdirSync(spec.logDir, { recursive: true });
|
|
15307
15482
|
const file2 = serviceFile(kind);
|
|
15308
|
-
|
|
15483
|
+
fs64.mkdirSync(path54.dirname(file2), { recursive: true });
|
|
15309
15484
|
if (process.platform === "darwin") {
|
|
15310
|
-
if (
|
|
15311
|
-
|
|
15485
|
+
if (fs64.existsSync(file2)) run2("launchctl", ["unload", file2]);
|
|
15486
|
+
fs64.writeFileSync(file2, renderLaunchdPlist(spec));
|
|
15312
15487
|
const load = run2("launchctl", ["load", file2]);
|
|
15313
15488
|
return { file: file2, loaded: load.ok, detail: load.ok ? `label lobstah.${kind}` : load.out };
|
|
15314
15489
|
}
|
|
15315
|
-
|
|
15490
|
+
fs64.writeFileSync(file2, renderSystemdUnit(spec));
|
|
15316
15491
|
run2("systemctl", ["--user", "daemon-reload"]);
|
|
15317
15492
|
const enable = run2("systemctl", ["--user", "enable", "--now", `lobstah-${kind}.service`]);
|
|
15318
15493
|
return { file: file2, loaded: enable.ok, detail: enable.ok ? `unit lobstah-${kind}.service` : enable.out };
|
|
@@ -15321,19 +15496,19 @@ function uninstallService(kind) {
|
|
|
15321
15496
|
if (process.platform === "win32") throw new Error(`no service manager support on Windows`);
|
|
15322
15497
|
const file2 = serviceFile(kind);
|
|
15323
15498
|
if (process.platform === "darwin") {
|
|
15324
|
-
if (
|
|
15499
|
+
if (fs64.existsSync(file2)) run2("launchctl", ["unload", file2]);
|
|
15325
15500
|
} else {
|
|
15326
15501
|
run2("systemctl", ["--user", "disable", "--now", `lobstah-${kind}.service`]);
|
|
15327
15502
|
}
|
|
15328
|
-
const removed =
|
|
15329
|
-
if (removed)
|
|
15503
|
+
const removed = fs64.existsSync(file2);
|
|
15504
|
+
if (removed) fs64.unlinkSync(file2);
|
|
15330
15505
|
if (process.platform !== "darwin") run2("systemctl", ["--user", "daemon-reload"]);
|
|
15331
15506
|
return { file: file2, removed };
|
|
15332
15507
|
}
|
|
15333
15508
|
|
|
15334
15509
|
// apps/cli/src/pet.ts
|
|
15335
15510
|
init_dist2();
|
|
15336
|
-
import * as
|
|
15511
|
+
import * as fs65 from "node:fs";
|
|
15337
15512
|
import * as os9 from "node:os";
|
|
15338
15513
|
import * as path55 from "node:path";
|
|
15339
15514
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
@@ -15385,7 +15560,7 @@ function installPet(binaryArg) {
|
|
|
15385
15560
|
path55.join(process.cwd(), ".build", "debug", "LobstahPet"),
|
|
15386
15561
|
petBinaryHome()
|
|
15387
15562
|
].filter((c) => c !== void 0);
|
|
15388
|
-
const source = candidates.find((c) =>
|
|
15563
|
+
const source = candidates.find((c) => fs65.existsSync(c));
|
|
15389
15564
|
if (!source) {
|
|
15390
15565
|
throw new Error(
|
|
15391
15566
|
"no pet binary found \u2014 build it first (`cd apps/pet && swift build -c release`) and run install from the repo root, or pass --binary <path>"
|
|
@@ -15393,33 +15568,33 @@ function installPet(binaryArg) {
|
|
|
15393
15568
|
}
|
|
15394
15569
|
const binary = petBinaryHome();
|
|
15395
15570
|
const file0 = petPlistFile();
|
|
15396
|
-
if (
|
|
15571
|
+
if (fs65.existsSync(file0)) run3("launchctl", ["unload", file0]);
|
|
15397
15572
|
run3("pkill", ["-f", "LobstahPet"]);
|
|
15398
15573
|
if (path55.resolve(source) !== path55.resolve(binary)) {
|
|
15399
|
-
|
|
15400
|
-
|
|
15401
|
-
|
|
15402
|
-
|
|
15574
|
+
fs65.mkdirSync(path55.dirname(binary), { recursive: true });
|
|
15575
|
+
fs65.rmSync(binary, { force: true });
|
|
15576
|
+
fs65.copyFileSync(source, binary);
|
|
15577
|
+
fs65.chmodSync(binary, 493);
|
|
15403
15578
|
const bundle = path55.join(path55.dirname(source), "LobstahPet_LobstahPet.bundle");
|
|
15404
|
-
if (
|
|
15579
|
+
if (fs65.existsSync(bundle)) {
|
|
15405
15580
|
const dest = path55.join(path55.dirname(binary), "LobstahPet_LobstahPet.bundle");
|
|
15406
|
-
|
|
15407
|
-
|
|
15581
|
+
fs65.rmSync(dest, { recursive: true, force: true });
|
|
15582
|
+
fs65.cpSync(bundle, dest, { recursive: true });
|
|
15408
15583
|
}
|
|
15409
15584
|
}
|
|
15410
15585
|
const logDir = path55.join(lobstahHome(), "logs");
|
|
15411
|
-
|
|
15586
|
+
fs65.mkdirSync(logDir, { recursive: true });
|
|
15412
15587
|
const file2 = petPlistFile();
|
|
15413
|
-
|
|
15414
|
-
|
|
15588
|
+
fs65.mkdirSync(path55.dirname(file2), { recursive: true });
|
|
15589
|
+
fs65.writeFileSync(file2, renderPetPlist(binary, servicePathEnv(process.execPath), lobstahHome(), logDir));
|
|
15415
15590
|
const load = run3("launchctl", ["load", file2]);
|
|
15416
15591
|
return { file: file2, binary, loaded: load.ok, detail: load.ok ? "label lobstah.pet \u2014 starts at login" : load.out };
|
|
15417
15592
|
}
|
|
15418
15593
|
function uninstallPet() {
|
|
15419
15594
|
const file2 = petPlistFile();
|
|
15420
|
-
if (process.platform === "darwin" &&
|
|
15421
|
-
const removed =
|
|
15422
|
-
if (removed)
|
|
15595
|
+
if (process.platform === "darwin" && fs65.existsSync(file2)) run3("launchctl", ["unload", file2]);
|
|
15596
|
+
const removed = fs65.existsSync(file2);
|
|
15597
|
+
if (removed) fs65.unlinkSync(file2);
|
|
15423
15598
|
run3("pkill", ["-f", "LobstahPet"]);
|
|
15424
15599
|
return { file: file2, removed };
|
|
15425
15600
|
}
|
|
@@ -15428,7 +15603,7 @@ function petStateFile() {
|
|
|
15428
15603
|
}
|
|
15429
15604
|
function readPetState() {
|
|
15430
15605
|
try {
|
|
15431
|
-
const s = JSON.parse(
|
|
15606
|
+
const s = JSON.parse(fs65.readFileSync(petStateFile(), "utf8"));
|
|
15432
15607
|
return typeof s.pid === "number" && typeof s.at === "string" ? s : void 0;
|
|
15433
15608
|
} catch {
|
|
15434
15609
|
return void 0;
|
|
@@ -15450,8 +15625,8 @@ function processAlive(pid) {
|
|
|
15450
15625
|
function petRow(opts = {}) {
|
|
15451
15626
|
const now2 = opts.now ?? Date.now();
|
|
15452
15627
|
const alive2 = opts.alive ?? processAlive;
|
|
15453
|
-
const agent =
|
|
15454
|
-
const binary =
|
|
15628
|
+
const agent = fs65.existsSync(opts.plist ?? petPlistFile());
|
|
15629
|
+
const binary = fs65.existsSync(opts.binary ?? petBinaryHome());
|
|
15455
15630
|
const installed = agent ? "installed" : binary ? "binary installed, no login agent" : "not installed";
|
|
15456
15631
|
const state = readPetState();
|
|
15457
15632
|
if (!state) {
|
|
@@ -15474,13 +15649,13 @@ function petRow(opts = {}) {
|
|
|
15474
15649
|
init_dist();
|
|
15475
15650
|
init_dist2();
|
|
15476
15651
|
import { createHash as createHash9 } from "node:crypto";
|
|
15477
|
-
import * as
|
|
15652
|
+
import * as fs67 from "node:fs";
|
|
15478
15653
|
import * as os10 from "node:os";
|
|
15479
15654
|
import * as path57 from "node:path";
|
|
15480
15655
|
|
|
15481
15656
|
// apps/cli/src/watchers.ts
|
|
15482
15657
|
init_dist2();
|
|
15483
|
-
import * as
|
|
15658
|
+
import * as fs66 from "node:fs";
|
|
15484
15659
|
import * as path56 from "node:path";
|
|
15485
15660
|
var STALE_MS = 5e3;
|
|
15486
15661
|
var HEARTBEAT_MS = 1500;
|
|
@@ -15488,7 +15663,7 @@ var watcherDir = () => path56.join(lobstahHome(), "watchers");
|
|
|
15488
15663
|
var watcherFile = (sessionId) => path56.join(watcherDir(), `${encodeURIComponent(sessionId)}.json`);
|
|
15489
15664
|
function liveRegistration(sessionId, now2 = Date.now()) {
|
|
15490
15665
|
try {
|
|
15491
|
-
const w = JSON.parse(
|
|
15666
|
+
const w = JSON.parse(fs66.readFileSync(watcherFile(sessionId), "utf8"));
|
|
15492
15667
|
const beat = Date.parse(w.heartbeatAt);
|
|
15493
15668
|
if (w.sessionId !== sessionId || !Number.isInteger(w.pid) || w.pid <= 0 || !Number.isFinite(beat) || now2 - beat > STALE_MS) return void 0;
|
|
15494
15669
|
return w;
|
|
@@ -15536,7 +15711,7 @@ async function stopWatcher(sessionId, kind, graceMs = 3e3, pollMs = 50) {
|
|
|
15536
15711
|
const file2 = watcherFile(sessionId);
|
|
15537
15712
|
const claimed = () => {
|
|
15538
15713
|
try {
|
|
15539
|
-
return JSON.parse(
|
|
15714
|
+
return JSON.parse(fs66.readFileSync(file2, "utf8")).pid === w.pid;
|
|
15540
15715
|
} catch {
|
|
15541
15716
|
return false;
|
|
15542
15717
|
}
|
|
@@ -15544,7 +15719,7 @@ async function stopWatcher(sessionId, kind, graceMs = 3e3, pollMs = 50) {
|
|
|
15544
15719
|
for (; ; ) {
|
|
15545
15720
|
if (!claimed()) return { watcher: w, exited: true };
|
|
15546
15721
|
if (!pidAlive4(w.pid)) {
|
|
15547
|
-
if (claimed())
|
|
15722
|
+
if (claimed()) fs66.rmSync(file2, { force: true });
|
|
15548
15723
|
return { watcher: w, exited: true };
|
|
15549
15724
|
}
|
|
15550
15725
|
if (Date.now() >= deadline) return { watcher: w, exited: false };
|
|
@@ -15552,13 +15727,13 @@ async function stopWatcher(sessionId, kind, graceMs = 3e3, pollMs = 50) {
|
|
|
15552
15727
|
}
|
|
15553
15728
|
}
|
|
15554
15729
|
function armWatcher(sessionId, kind, trapId) {
|
|
15555
|
-
|
|
15730
|
+
fs66.mkdirSync(watcherDir(), { recursive: true });
|
|
15556
15731
|
const file2 = watcherFile(sessionId);
|
|
15557
15732
|
const own = () => ({ sessionId, kind, pid: process.pid, heartbeatAt: (/* @__PURE__ */ new Date()).toISOString(), ...trapId ? { trapId } : {} });
|
|
15558
15733
|
let claimed = false;
|
|
15559
15734
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
15560
15735
|
try {
|
|
15561
|
-
|
|
15736
|
+
fs66.writeFileSync(file2, `${JSON.stringify(own())}
|
|
15562
15737
|
`, { flag: "wx" });
|
|
15563
15738
|
claimed = true;
|
|
15564
15739
|
break;
|
|
@@ -15567,7 +15742,7 @@ function armWatcher(sessionId, kind, trapId) {
|
|
|
15567
15742
|
const live = liveRegistration(sessionId);
|
|
15568
15743
|
if (live) throw new Error(alreadyArmed(live));
|
|
15569
15744
|
try {
|
|
15570
|
-
|
|
15745
|
+
fs66.rmSync(file2);
|
|
15571
15746
|
} catch {
|
|
15572
15747
|
}
|
|
15573
15748
|
}
|
|
@@ -15575,7 +15750,7 @@ function armWatcher(sessionId, kind, trapId) {
|
|
|
15575
15750
|
if (!claimed) throw new Error(`could not arm watcher for session ${sessionId}`);
|
|
15576
15751
|
const ownsFile = () => {
|
|
15577
15752
|
try {
|
|
15578
|
-
return JSON.parse(
|
|
15753
|
+
return JSON.parse(fs66.readFileSync(file2, "utf8")).pid === process.pid;
|
|
15579
15754
|
} catch {
|
|
15580
15755
|
return false;
|
|
15581
15756
|
}
|
|
@@ -15584,12 +15759,12 @@ function armWatcher(sessionId, kind, trapId) {
|
|
|
15584
15759
|
if (!ownsFile()) return;
|
|
15585
15760
|
const tmp = `${file2}.tmp-${process.pid}`;
|
|
15586
15761
|
try {
|
|
15587
|
-
|
|
15762
|
+
fs66.writeFileSync(tmp, `${JSON.stringify(own())}
|
|
15588
15763
|
`);
|
|
15589
|
-
|
|
15764
|
+
fs66.renameSync(tmp, file2);
|
|
15590
15765
|
} catch {
|
|
15591
15766
|
try {
|
|
15592
|
-
|
|
15767
|
+
fs66.rmSync(tmp, { force: true });
|
|
15593
15768
|
} catch {
|
|
15594
15769
|
}
|
|
15595
15770
|
}
|
|
@@ -15601,7 +15776,7 @@ function armWatcher(sessionId, kind, trapId) {
|
|
|
15601
15776
|
if (stopped) return;
|
|
15602
15777
|
stopped = true;
|
|
15603
15778
|
clearInterval(timer);
|
|
15604
|
-
if (ownsFile())
|
|
15779
|
+
if (ownsFile()) fs66.rmSync(file2, { force: true });
|
|
15605
15780
|
process.off("exit", stop);
|
|
15606
15781
|
process.off("SIGINT", interrupt);
|
|
15607
15782
|
process.off("SIGTERM", terminate2);
|
|
@@ -15623,7 +15798,7 @@ function armWatcher(sessionId, kind, trapId) {
|
|
|
15623
15798
|
// apps/cli/src/hook-readiness.ts
|
|
15624
15799
|
var readJson3 = (file2) => {
|
|
15625
15800
|
try {
|
|
15626
|
-
return JSON.parse(
|
|
15801
|
+
return JSON.parse(fs67.readFileSync(file2, "utf8"));
|
|
15627
15802
|
} catch {
|
|
15628
15803
|
return void 0;
|
|
15629
15804
|
}
|
|
@@ -15660,7 +15835,7 @@ function codexHooks(opts = {}) {
|
|
|
15660
15835
|
const codexDir = env.CODEX_HOME ?? path57.join(opts.home ?? os10.homedir(), ".codex");
|
|
15661
15836
|
let cfg = {};
|
|
15662
15837
|
try {
|
|
15663
|
-
cfg = parse(
|
|
15838
|
+
cfg = parse(fs67.readFileSync(path57.join(codexDir, "config.toml"), "utf8"));
|
|
15664
15839
|
} catch {
|
|
15665
15840
|
}
|
|
15666
15841
|
const state = (cfg.hooks ?? {}).state ?? {};
|
|
@@ -15894,7 +16069,7 @@ var PROBE_PERMISSION = { "pull requests": "Pull requests", contents: "Contents",
|
|
|
15894
16069
|
function githubRepos(repos) {
|
|
15895
16070
|
const out = [];
|
|
15896
16071
|
for (const [key, r] of Object.entries(repos)) {
|
|
15897
|
-
const origin = r.origin ?? (
|
|
16072
|
+
const origin = r.origin ?? (fs68.existsSync(r.path) ? git8(r.path, "remote", "get-url", "origin").out : "");
|
|
15898
16073
|
const forgeRepo = origin ? githubRepoFromOrigin(origin) : void 0;
|
|
15899
16074
|
if (forgeRepo) out.push({ key, forgeRepo, trunk: r.trunk });
|
|
15900
16075
|
}
|
|
@@ -15904,12 +16079,12 @@ function diskRow(cfg, freeBytes = statfsFreeBytes, now2 = Date.now()) {
|
|
|
15904
16079
|
const dir = worktreesDir();
|
|
15905
16080
|
let free;
|
|
15906
16081
|
try {
|
|
15907
|
-
free = freeBytes(
|
|
16082
|
+
free = freeBytes(fs68.existsSync(dir) ? dir : lobstahHome());
|
|
15908
16083
|
} catch {
|
|
15909
16084
|
free = void 0;
|
|
15910
16085
|
}
|
|
15911
16086
|
const { minFreeGB, retentionDays } = cfg.limits;
|
|
15912
|
-
const cullable =
|
|
16087
|
+
const cullable = fs68.existsSync(dir) ? planPressureCull(now2) : [];
|
|
15913
16088
|
const oldest = cullable.reduce((max, i) => Math.max(max, i.ageDays), 0);
|
|
15914
16089
|
const parts = [
|
|
15915
16090
|
free === void 0 ? "free space unreadable" : `${formatGB(free)} free on ${dir}`,
|
|
@@ -15921,7 +16096,7 @@ function diskRow(cfg, freeBytes = statfsFreeBytes, now2 = Date.now()) {
|
|
|
15921
16096
|
const kept = readKeptWorktrees();
|
|
15922
16097
|
if (kept.length > 0) {
|
|
15923
16098
|
parts.push(
|
|
15924
|
-
`kept:
|
|
16099
|
+
`kept: ${kept.length} worktree(s) of merged PRs: ${kept.map((k) => `${k.id.slice(0, 8)} ${k.reason}`).join(", ")}`
|
|
15925
16100
|
);
|
|
15926
16101
|
}
|
|
15927
16102
|
const hold = readHold();
|
|
@@ -15947,7 +16122,7 @@ async function runDoctor(now2 = Date.now()) {
|
|
|
15947
16122
|
try {
|
|
15948
16123
|
const port = glassPort();
|
|
15949
16124
|
const info = await probeGlass(port);
|
|
15950
|
-
const installed = process.platform === "win32" ? false :
|
|
16125
|
+
const installed = process.platform === "win32" ? false : fs68.existsSync(serviceFile("glass"));
|
|
15951
16126
|
push(
|
|
15952
16127
|
"glass",
|
|
15953
16128
|
info ? "ok" : "warn",
|
|
@@ -15959,13 +16134,13 @@ async function runDoctor(now2 = Date.now()) {
|
|
|
15959
16134
|
const pet = petRow({ now: now2 });
|
|
15960
16135
|
push(pet.check, pet.status, pet.detail);
|
|
15961
16136
|
const cfgFile = configPath();
|
|
15962
|
-
if (!
|
|
16137
|
+
if (!fs68.existsSync(cfgFile)) {
|
|
15963
16138
|
push("config", "fail", `${cfgFile} missing \u2014 run \`lobstah init\``);
|
|
15964
16139
|
return rows;
|
|
15965
16140
|
}
|
|
15966
16141
|
let parsed;
|
|
15967
16142
|
try {
|
|
15968
|
-
parsed = parse(
|
|
16143
|
+
parsed = parse(fs68.readFileSync(cfgFile, "utf8"));
|
|
15969
16144
|
push("config", "ok", cfgFile);
|
|
15970
16145
|
} catch (err) {
|
|
15971
16146
|
push("config", "fail", `${cfgFile}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -15976,7 +16151,7 @@ async function runDoctor(now2 = Date.now()) {
|
|
|
15976
16151
|
if (repoKeys.length === 0) push("repos", "warn", "none configured \u2014 add one with `lobstah repos add <path>`");
|
|
15977
16152
|
for (const [key, repo] of Object.entries(cfg.repos)) {
|
|
15978
16153
|
const label = `repo ${key}`;
|
|
15979
|
-
if (!
|
|
16154
|
+
if (!fs68.existsSync(repo.path)) {
|
|
15980
16155
|
push(label, "fail", `path ${repo.path} does not exist`);
|
|
15981
16156
|
continue;
|
|
15982
16157
|
}
|
|
@@ -16007,11 +16182,11 @@ async function runDoctor(now2 = Date.now()) {
|
|
|
16007
16182
|
const trapNames2 = listTraps().map(trapLabel);
|
|
16008
16183
|
const parked = parkedSummary();
|
|
16009
16184
|
const slots = `headless: ${workSlots.headless} of ${cfg.limits.maxConcurrent} work, ${choreSlots.headless} of ${cfg.limits.choreConcurrent} chore; traps: ${workSlots.traps + choreSlots.traps}${trapNames2.length ? ` (${trapNames2.join(", ")})` : ""}${parked ? `; ${parked}` : ""}`;
|
|
16010
|
-
if (!
|
|
16185
|
+
if (!fs68.existsSync(hb)) {
|
|
16011
16186
|
push("daemon", "warn", `no heartbeat \u2014 daemon not running (\`lobstah daemon install\`); ${slots}`);
|
|
16012
16187
|
} else {
|
|
16013
16188
|
try {
|
|
16014
|
-
const payload = JSON.parse(
|
|
16189
|
+
const payload = JSON.parse(fs68.readFileSync(hb, "utf8"));
|
|
16015
16190
|
const age2 = now2 - new Date(payload.heartbeat ?? 0).getTime();
|
|
16016
16191
|
push(
|
|
16017
16192
|
"daemon",
|
|
@@ -16044,9 +16219,9 @@ async function runDoctor(now2 = Date.now()) {
|
|
|
16044
16219
|
push(disk.check, disk.status, disk.detail);
|
|
16045
16220
|
try {
|
|
16046
16221
|
const soaking = path58.join(lobstahHome(), "soaking");
|
|
16047
|
-
const stale =
|
|
16222
|
+
const stale = fs68.readdirSync(soaking).filter((f) => f.endsWith(".json")).filter((f) => {
|
|
16048
16223
|
try {
|
|
16049
|
-
return typeof JSON.parse(
|
|
16224
|
+
return typeof JSON.parse(fs68.readFileSync(path58.join(soaking, f), "utf8")).trapId !== "string";
|
|
16050
16225
|
} catch {
|
|
16051
16226
|
return true;
|
|
16052
16227
|
}
|
|
@@ -16074,7 +16249,7 @@ async function runDoctor(now2 = Date.now()) {
|
|
|
16074
16249
|
// apps/cli/src/glass.ts
|
|
16075
16250
|
init_stats2();
|
|
16076
16251
|
init_dist2();
|
|
16077
|
-
import * as
|
|
16252
|
+
import * as fs73 from "node:fs";
|
|
16078
16253
|
import * as os11 from "node:os";
|
|
16079
16254
|
import * as path62 from "node:path";
|
|
16080
16255
|
import * as http3 from "node:http";
|
|
@@ -16082,17 +16257,17 @@ import { randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual3 } from
|
|
|
16082
16257
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
16083
16258
|
|
|
16084
16259
|
// apps/cli/src/glass-page.generated.ts
|
|
16085
|
-
var GLASS_PAGE = '<!doctype html>\n<html>\n <head>\n <meta charset="utf-8" />\n <meta name="viewport" content="width=device-width,initial-scale=1" />\n <title>spyglass</title>\n <link rel="icon" type="image/png" href="/icon.png" />\n <style>:root{--bg: #0e1116;--card: #161b22;--line: #2b3240;--line-hover: #3a455a;--row-hover: #1c2330;--inset: #10151c;--seg-on: #26436b;--scrim: rgba(4, 7, 11, 0.72);--fg: #dbe2ea;--dim: #8b96a5;--ok: #4fc17c;--warn: #e2b93d;--bad: #e26d5c;--link: #6cb2e2;--on-fill: #fff;--pr-merged: #8957e5;--pr-merged-fg: #ab7df8;--pr-open: #238636;--pr-draft: #6e7681;--pr-closed: #da3633;--lobstah: #eb6644;--heat-0: var(--inset);--heat-1: color-mix(in srgb, var(--lobstah) 30%, var(--card));--heat-2: color-mix(in srgb, var(--lobstah) 55%, var(--card));--heat-3: color-mix(in srgb, var(--lobstah) 78%, var(--card));--heat-4: var(--lobstah);--heat-cell: 11px;--heat-gap: 3px;--font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;--font-size: 13px;--space-1: 4px;--space-2: 8px;--space-3: 12px;--space-4: 16px;--page-pad-block: 14px;--radius: 6px;--card-radius: 8px;--card-pad: 12px 14px;--card-min: 300px;--deck-card-min: 240px;--badge-radius: 5px;--badge-pad: 1px 7px;--badge-font: 11px;--modal-radius: 10px;--modal-width: 780px;--layer-modal: 10;--layer-decision-modal: 15;--layer-alert: calc(var(--layer-modal) - 1);--unread-bg: color-mix(in srgb, var(--bad) 16%, var(--card))}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--fg);font:var(--font-size)/1.45 var(--font-mono);padding-block:var(--page-pad-block);padding-inline:var(--space-4);display:flex;flex-direction:column;min-height:100vh}h1{font-size:15px;margin:0 0 10px}h1 .dim{color:var(--dim);font-weight:normal}h2{font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:0.08em;margin:20px 0 6px}.chips,.controls{display:flex;gap:var(--space-2);flex-wrap:wrap;align-items:center}.controls{margin:0 0 14px}.chips{margin-bottom:8px}.chip{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:3px 9px}.controls select,.newtrap select,.controls input[type=search]{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);color:var(--fg);font:inherit;padding:4px 8px}.controls input[type=search]{flex:1 1 140px;min-width:120px;max-width:340px}#chain-control{display:inline-flex;align-items:center;gap:7px;white-space:nowrap}#f-chain{margin:0;width:14px;height:14px;accent-color:var(--link)}.tabs{display:flex;gap:var(--space-1);border-bottom:1px solid var(--line);margin:8px 0 10px}.tabs a{color:var(--dim);padding:6px 11px;border-bottom:2px solid transparent}.tabs a.on{color:var(--fg);border-color:var(--link)}.tabpage{display:none}.tabpage.on{display:block;flex:1 0 auto}.headerline{display:flex;align-items:center;gap:var(--space-2)}.headerline h1{flex:1}#settings-slot{min-width:34px;text-align:right;color:var(--dim)}#settings-slot #gearbtn{margin-left:0}.deckgrid{display:flex;flex-direction:column;gap:var(--space-3)}.deckgrid h2{margin:5px 0}.deckgrid section{width:100%;min-width:0}.deckline{padding:2px 0;border-top:1px solid var(--line);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.deckline.acked,.card.acked,.rowhead.acked,.badge.acked{opacity:0.55}.deckmore{color:var(--dim);font-size:11px;display:inline-block;margin-top:4px}.deckline.click{cursor:pointer}.deckline.click:hover{background:var(--row-hover)}.deckgrid h2 a{color:inherit}.deckgrid h2 a:hover{color:var(--fg)}.deckgrid .cards{grid-template-columns:repeat(auto-fill,minmax(min(var(--deck-card-min),100%),1fr));gap:10px}.deckstack{margin-bottom:9px}.deckstack>.dim{font-size:11px;margin:3px 0 5px}.seg{display:inline-flex;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}.seg button{background:var(--card);border:none;color:var(--dim);font:inherit;padding:4px 11px;cursor:pointer}.seg button.on{background:var(--seg-on);color:var(--fg)}.wrap{overflow-x:auto}table{border-collapse:collapse;width:100%;background:var(--card);border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}th,td{text-align:left;padding:4px 9px;border-top:1px solid var(--line);vertical-align:top;white-space:nowrap}td.grow{white-space:normal;word-break:break-word;min-width:140px}th{color:var(--dim);font-weight:normal;border-top:none;font-size:11px}tr.rowhead{cursor:pointer}tr.rowhead:hover{background:var(--row-hover)}.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(var(--card-min),100%),1fr));gap:var(--space-3)}.card{background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad);cursor:pointer;min-width:0;overflow:hidden;overflow-wrap:anywhere}.card:hover{border-color:var(--line-hover)}.worker-harness{cursor:help}.worker-harness:focus-visible{outline:2px solid var(--ok);outline-offset:3px}.worker-tooltip{position:fixed;z-index:20;max-width:264px;padding:6px 9px;background:var(--card);color:var(--fg);border:1px solid var(--line-hover);border-radius:var(--badge-radius);box-shadow:0 3px 12px var(--scrim);font-size:12px;white-space:normal;pointer-events:none}.worker-details dl{display:grid;grid-template-columns:max-content 1fr;gap:4px 14px;font-size:12px}.worker-details dt{color:var(--dim)}.worker-details dd{margin:0;overflow-wrap:anywhere}.card .top{display:flex;justify-content:space-between;gap:var(--space-3);align-items:center;min-height:22px}.card .top>b,.card .top>.prname{flex:1 1000000 auto;min-width:min(10ch,100%);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card .top .badge{flex:0 0 auto}.card .top .badge.long{flex:0 1 auto;min-width:min(12ch,100%);max-width:min(26ch,100%);overflow:hidden;text-overflow:ellipsis}.card .meta{color:var(--dim);font-size:12px;margin-top:2px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.card .note{margin-top:6px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.activity{margin-top:4px;font-size:12px;color:var(--fg);opacity:0.85}.waiting{margin-top:4px;font-size:12px;color:var(--dim)}.activity.stale{color:var(--dim);opacity:1}.card .foot{margin-top:8px;display:flex;gap:10px;flex-wrap:wrap;align-items:center;color:var(--dim);font-size:12px}.card .foot>*{min-width:0}.card .foot .footact{margin-left:auto}.catchn{white-space:nowrap}.btn{background:var(--card);border:1px solid var(--line);border-radius:5px;color:var(--dim);font:inherit;font-size:11px;padding:1px 8px;cursor:pointer;white-space:nowrap}.btn:hover{color:var(--fg);border-color:var(--line-hover)}.badge{border-radius:var(--badge-radius);padding:var(--badge-pad);font-size:var(--badge-font);line-height:1.5;border:1px solid var(--line);white-space:nowrap;display:inline-block}.badge.pr-merged{background:var(--pr-merged);border-color:var(--pr-merged-fg);color:var(--on-fill)}.badge.pr-open{background:var(--pr-open);border-color:var(--pr-open);color:var(--on-fill)}.badge.pr-draft{background:var(--pr-draft);border-color:var(--pr-draft);color:var(--on-fill)}.badge.pr-closed{background:var(--pr-closed);border-color:var(--pr-closed);color:var(--on-fill)}.badge.pr-conflicts{background:var(--pr-closed);border-color:var(--pr-closed);color:var(--on-fill)}.badge.pr-behind{background:var(--pr-draft);border-color:var(--pr-draft);color:var(--on-fill)}.badge.unreported{font-weight:normal;font-size:10px;padding:0 5px;vertical-align:1px}.v-done{color:var(--ok)}.v-working,.v-queued{color:var(--fg)}.v-needs-decision,.v-blocked{color:var(--bad);font-weight:bold}.v-failed{color:var(--bad)}.v-paused,.v-unknown{color:var(--dim)}.dim{color:var(--dim)}.ok{color:var(--ok)}.warn{color:var(--warn)}.bad{color:var(--bad)}.detail{display:none}.detail.open{display:table-row}.detail td{background:var(--inset);white-space:pre-wrap;word-break:break-word;font-size:12px;color:var(--dim)}.detail b{color:var(--fg)}.empty{color:var(--dim);padding:6px 2px}.older{padding:8px 2px}#stale{display:none;color:var(--bad)}a{color:var(--link);text-decoration:none}.dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:5px;vertical-align:middle}.dot.ok{background:var(--ok)}.dot.warn{background:var(--warn)}.dot.bad{background:var(--bad)}.dot.dim{background:var(--dim)}#overlay{position:fixed;inset:0;background:var(--scrim);display:none;z-index:var(--layer-modal);padding:28px 16px;overflow:auto}#overlay.open{display:block}.modal{max-width:var(--modal-width);margin:0 auto;background:var(--card);border:1px solid var(--line);border-radius:var(--modal-radius);padding:16px 18px;position:relative}.modal .x{position:absolute;top:8px;right:12px;cursor:pointer;color:var(--dim);font-size:18px;line-height:1}.modal .x:hover{color:var(--fg)}.modal h3{margin:0 24px 2px 0;font-size:14px}.modal .sub{color:var(--dim);font-size:12px;margin-bottom:10px}.modal .sec{color:var(--dim);text-transform:uppercase;letter-spacing:0.08em;font-size:11px;margin:14px 0 4px}.modal pre{white-space:pre-wrap;word-break:break-word;margin:0;font:inherit;color:var(--fg)}.msg{border-left:3px solid var(--line);padding:3px 10px;margin:8px 0;white-space:pre-wrap;word-break:break-word}.msg.from-helm{border-color:var(--link)}.msg .hdr{color:var(--dim);font-size:11px}.catch{border:1px solid var(--line);border-radius:var(--radius);padding:6px 10px;margin:8px 0}.catch.click{cursor:pointer}.catch .hdr{display:flex;gap:10px;flex-wrap:wrap;align-items:baseline}.loglines{color:var(--dim);font-size:12px;white-space:pre-wrap;word-break:break-word;margin-top:4px}.cmd{display:flex;gap:var(--space-2);align-items:center;margin:5px 0;flex-wrap:wrap}.cmd code{background:var(--inset);border:1px solid var(--line);border-radius:5px;padding:3px 8px;word-break:break-all;cursor:pointer}.cmd code:hover{border-color:var(--line-hover)}.cmd code.copied{border-color:var(--ok)}.cmd button{background:var(--card);border:1px solid var(--line);border-radius:5px;color:var(--dim);font:inherit;font-size:11px;padding:2px 8px;cursor:pointer}.cmd button:hover{color:var(--fg)}.chip.click{cursor:pointer}.chip.click:hover{border-color:var(--line-hover)}footer{margin-top:26px;padding-top:10px;border-top:1px solid var(--line);color:var(--dim);font-size:12px;display:flex;gap:var(--space-2);flex-wrap:wrap}.lob{position:fixed;bottom:6px;left:0;z-index:5;cursor:pointer;font-size:34px;line-height:1;user-select:none;animation:crawl 18s linear infinite}a.lob{color:inherit}.lob .bub .badge{display:inline-block;margin:0 0 3px}.pet-art{display:block;position:relative;width:72px;height:56px}.pet-art .sprite{width:72px;height:56px;background-position:0 0;background-repeat:no-repeat;background-size:400% 100%;image-rendering:pixelated;animation:step 0.5s steps(4) infinite}.lob .bub{display:none;position:absolute;bottom:58px;right:-8px;z-index:2;background:var(--card);border:1px solid var(--line);border-radius:9px;padding:6px 9px 5px;font-size:11px;line-height:1.35;color:var(--fg);width:max-content;max-width:150px;box-shadow:0 2px 8px rgba(0,0,0,0.4)}.lob:hover .bub{display:block}.lob .bub span{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;white-space:normal;word-break:break-word}.pet-art .star{position:absolute;left:56px;top:-9px;height:14px;image-rendering:pixelated;z-index:3}.lob:hover{animation-play-state:paused}@keyframes crawl{0%{transform:translateX(-90px)}100%{transform:translateX(100vw)}}@keyframes step{to{background-position-x:-288px}}#gearbtn{background:none;border:1px solid var(--line);border-radius:var(--radius);color:var(--dim);font:inherit;font-size:13px;padding:1px 7px;margin-left:8px;cursor:pointer;vertical-align:1px}#gearbtn:hover,#gearbtn.on{color:var(--fg);border-color:var(--line-hover)}.settings .row{display:flex;justify-content:space-between;align-items:center;gap:14px;margin:12px 0}.settings .lbl{color:var(--fg);font-size:12px}.settings .hint{display:block;color:var(--dim);font-size:11px;margin-top:2px}.dlist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.drow{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--space-2);background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:8px 12px}.drow.unread{background:var(--unread-bg);border-color:var(--bad)}.drow.answered{opacity:0.7}.drow.focus{animation:dflash 1.6s ease-out 1}.drow .dopen{flex:1 1 280px;display:flex;align-items:baseline;gap:var(--space-2);min-width:0;background:none;border:none;padding:0;color:var(--fg);font:inherit;text-align:left;cursor:pointer}.drow .dopen:hover .dtitle{text-decoration:underline}.drow .dtitle{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.drow .dmeta,.drow .dage{font-size:11px}.drow .dlink,.dmodal .dlink{color:var(--link)}.dstate{font-size:var(--badge-font);border-radius:var(--badge-radius);padding:var(--badge-pad);border:1px solid var(--line);color:var(--dim)}.dstate.unread{border-color:var(--bad);color:var(--bad)}.dstate.answered{color:var(--ok)}.dbadge{display:inline-block;margin-left:6px;min-width:1.6em;text-align:center;border-radius:999px;padding:0 6px;font-size:11px;font-weight:bold;background:var(--bad);color:var(--on-fill)}#doverlay{position:fixed;inset:0;background:var(--scrim);z-index:var(--layer-decision-modal);padding:28px 16px;overflow:auto}.dmodal:focus{outline:none}.dmodal .x{background:none;border:none;font:inherit;font-size:18px;color:var(--dim)}.dnav{display:flex;align-items:center;gap:var(--space-2);margin:0 28px var(--space-3) 0}.dnav .dpos{color:var(--dim);font-size:11px}.dmodal .dtop{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--space-2);margin-bottom:var(--space-2)}.dmodal .dtitle{white-space:pre-wrap;overflow-wrap:anywhere;font-size:14px}.dmodal .dmeta{font-size:11px}.ddone{padding:28px 8px;text-align:center;cursor:pointer}.ddone .ok{font-size:15px;margin-bottom:var(--space-2)}.dalert-live{position:fixed;right:var(--space-4);bottom:var(--space-4);z-index:var(--layer-alert)}.dalert{display:flex;align-items:center;gap:4px;background:var(--card);border:1px solid var(--bad);border-radius:var(--card-radius);padding:8px 10px;box-shadow:0 6px 24px var(--scrim)}.dalert .dalert-open{display:flex;align-items:center;gap:var(--space-2);background:none;border:none;color:var(--fg);font:inherit;cursor:pointer}.dalert .dx{background:none;border:none;color:var(--dim);font:inherit;font-size:15px;cursor:pointer}.dcount{display:inline-block;min-width:1.8em;text-align:center;border-radius:999px;padding:1px 7px;font-weight:bold;background:var(--bad);color:var(--on-fill);animation:dglow 1.8s ease-in-out infinite}@keyframes dglow{0%,100%{box-shadow:0 0 0 0 transparent}50%{box-shadow:0 0 10px 3px var(--bad)}}@media(prefers-reduced-motion:reduce){.dcount,.drow.focus{animation:none}}.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}@keyframes dflash{0%,35%{border-color:var(--link);box-shadow:0 0 0 3px var(--link)}100%{border-color:var(--line);box-shadow:0 0 0 0 transparent}}.dfiles{display:flex;flex-wrap:wrap;gap:var(--space-2);margin:6px 0}.dfiles .dimg img{max-height:160px;max-width:320px;border:1px solid var(--line);border-radius:var(--radius);display:block}.dfiles .dfile{width:100%;font-size:11px}.doptions{display:flex;flex-wrap:wrap;gap:6px;margin:8px 0 6px}.btn.dopt{font-size:12px;padding:3px 12px;color:var(--fg)}.btn.dopt.on{background:var(--seg-on);border-color:var(--link)}.danswer{display:block;width:100%;min-height:3.2em;max-height:50vh;margin:6px 0;padding:6px 8px;resize:vertical;overflow-y:auto;field-sizing:content;background:var(--inset);color:var(--fg);border:1px solid var(--line);border-radius:var(--radius);font:inherit}.dkey,.dlegend kbd{display:inline-block;min-width:1.5em;text-align:center;font:inherit;font-size:10px;line-height:1.4;padding:0 4px;border:1px solid var(--line-hover);border-radius:4px;color:var(--dim);background:var(--inset)}.btn.dopt .dkey{margin-right:6px}.btn.dopt.on .dkey{border-color:var(--link);color:var(--fg)}.danswer-wrap{position:relative}.danswer-wrap .dkey-text{position:absolute;top:6px;right:8px;pointer-events:none}.dlegend{margin-top:var(--space-3);padding-top:var(--space-2);border-top:1px solid var(--line);font-size:11px;line-height:1.9}.danswer:focus{outline:none;border-color:var(--line-hover)}.dfoot{display:flex;flex-wrap:wrap;align-items:center;gap:6px}.dfoot .dattach input{display:none}.dfoot .dchip{font-size:11px;border:1px solid var(--line);border-radius:var(--badge-radius);padding:0 0 0 6px}.dfoot .dx{background:none;border:none;color:var(--dim);cursor:pointer;font:inherit}.dfoot .derr{font-size:11px}.btn.dsend{margin-left:auto;color:var(--fg);padding:2px 14px}.btn:disabled{cursor:default;opacity:0.6}.danswered{margin-top:6px}.thumb,.dimg,.mdimg{background:none;border:none;padding:0;cursor:zoom-in;font:inherit}.thumb{display:block;margin:4px 0}.thumb img{max-height:120px;max-width:240px;border:1px solid var(--line);border-radius:var(--radius);display:block}.lightbox{position:fixed;inset:0;z-index:20;background:rgba(0,0,0,0.85);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-2)}.lightbox .lbimg{max-width:90vw;max-height:90vh;object-fit:contain;border-radius:var(--radius);box-shadow:0 8px 40px rgba(0,0,0,0.6)}.lightbox .lbclose{position:absolute;top:12px;right:16px;background:none;border:none;color:var(--fg);font-size:28px;line-height:1;cursor:pointer}.lightbox .lboriginal{color:var(--link);font-size:12px}.mdpage{background:var(--inset);border:1px solid var(--line);border-radius:6px;padding:8px 14px;margin:6px 0;line-height:1.5;overflow-wrap:anywhere}.mdpage h1,.mdpage h2,.mdpage h3,.mdpage h4{margin:12px 0 6px;line-height:1.25}.mdpage h1{font-size:18px}.mdpage h2{font-size:15px}.mdpage h3,.mdpage h4{font-size:13px}.mdpage p,.mdpage ul,.mdpage ol,.mdpage blockquote{margin:6px 0}.mdpage ul,.mdpage ol{padding-left:22px}.mdpage a{color:var(--link)}.mdpage code{background:var(--card);border-radius:3px;padding:0 3px}.mdpage pre.mdcode{background:var(--card);border:1px solid var(--line);border-radius:4px;padding:8px;max-width:100%;overflow-x:auto;white-space:pre;overflow-wrap:normal}.mdpage pre.mdcode code{padding:0;background:none}.mdpage blockquote{border-left:3px solid var(--line);padding-left:10px;color:var(--dim)}.mdpage img{max-width:100%;height:auto;border-radius:4px;display:block;margin:6px 0}.mdpage hr{border:0;border-top:1px solid var(--line)}.mdpage .mdtable{max-width:100%;overflow-x:auto}.mdpage table{border-collapse:collapse;margin:6px 0}.mdpage th,.mdpage td{border:1px solid var(--line);padding:3px 8px;text-align:left;white-space:normal;overflow-wrap:anywhere;min-width:12ch}.tabhead{margin:0 0 8px}.newtrap{display:inline-flex;flex-wrap:wrap;gap:6px;align-items:center;font-size:12px;font-weight:normal;text-transform:none;letter-spacing:normal}.startcmds{margin:6px 0;font-size:11px;cursor:default}.reportview{width:100%;max-width:1100px;margin:0 auto;padding:20px 16px 48px;overflow-wrap:anywhere}.reportview h1{font-size:18px;margin:0 0 4px}.reportview .sub{color:var(--dim);font-size:12px;margin-bottom:10px}.reportview .sec{color:var(--dim);text-transform:uppercase;letter-spacing:0.08em;font-size:11px;margin:14px 0 4px}.reportview .mdpage{padding:12px 20px}@media(max-width:600px){.reportview{padding:12px 0 32px}.reportview .mdpage{padding:8px 12px}}.stats{display:flex;flex-direction:column;gap:var(--space-4);max-width:100%}.stats h2{margin:0 0 var(--space-2)}.stat-tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(100px,1fr));gap:var(--space-2)}.stat-tile{background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad)}.stat-tile .n{font-size:22px;font-weight:bold;color:var(--fg)}.stats-empty{display:flex;align-items:center;gap:var(--space-4);background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--space-4) var(--space-4) var(--space-3)}.stats-empty .pet-art{flex:none;margin-top:6px}.heat{background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad);min-width:0}.heat-scroll{overflow-x:auto;padding:2px 0 4px}.heatmap{border-collapse:separate;border-spacing:var(--heat-gap);width:auto;margin:0;background:none;border:0;border-radius:0;overflow:visible}.heatmap td{padding:0;border:0;vertical-align:middle;font-size:10px;line-height:1;color:var(--dim)}.heatmap .month{position:relative;height:14px}.heatmap .month span{position:absolute;left:0;bottom:3px;white-space:nowrap}.heatmap .wd{position:sticky;left:0;z-index:1;background:var(--card);padding-right:var(--space-1);white-space:nowrap}.heatmap .day,.heatmap .pad,.heat-legend .swatch{width:var(--heat-cell);min-width:var(--heat-cell);height:var(--heat-cell);border-radius:2px}.heatmap .day{position:relative;cursor:default;outline:1px solid rgba(255,255,255,0.04);outline-offset:-1px}.heatmap .day:focus-visible{outline:2px solid var(--link);outline-offset:0}.l0{background:var(--heat-0)}.l1{background:var(--heat-1)}.l2{background:var(--heat-2)}.l3{background:var(--heat-3)}.l4{background:var(--heat-4)}.heatmap .day:hover::after,.heatmap .day:focus::after{content:attr(data-tip);position:absolute;bottom:calc(100% + 6px);left:50%;transform:translateX(-50%);z-index:4;background:var(--bg);border:1px solid var(--line-hover);border-radius:var(--badge-radius);color:var(--fg);font-size:11px;padding:3px 7px;white-space:nowrap;pointer-events:none}.heatmap .day.below:hover::after,.heatmap .day.below:focus::after{bottom:auto;top:calc(100% + 6px)}.heatmap .day.tl:hover::after,.heatmap .day.tl:focus::after{left:0;transform:none}.heatmap .day.tr:hover::after,.heatmap .day.tr:focus::after{left:auto;right:0;transform:none}.heat-foot{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:var(--space-2);font-size:11px;margin-top:var(--space-1)}.heat-legend{display:inline-flex;align-items:center;gap:var(--heat-gap);color:var(--dim);margin-left:auto}.heat-legend .swatch{display:inline-block}.heat-legend .swatch:first-of-type{margin-left:var(--space-1)}.heat-legend .swatch:last-of-type{margin-right:var(--space-1)}.top-traps{background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad)}.top-traps ol{list-style:none;margin:0;padding:0;display:grid;gap:6px}.top-traps li{display:grid;grid-template-columns:minmax(0,14em) minmax(40px,32em) auto;justify-content:start;align-items:center;gap:var(--space-3)}.top-traps .name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.top-traps .bar{height:6px;background:var(--inset);border-radius:3px;overflow:hidden}.top-traps .bar span{display:block;height:100%;background:var(--lobstah)}.top-traps .n{color:var(--dim);white-space:nowrap}@media(max-width:600px){.tabs{flex-wrap:wrap}.tabs a{white-space:nowrap}}</style>\n </head>\n <body>\n <!-- The page is rendered by Preact (src/components/app.ts): header, tabs, controls, the five tab pages, footer, lobs, modal overlay. -->\n <script>"use strict";(()=>{var qe,y,ys,qr,F,$s,_s,ws,kt,Ne,$e,Ss,_t,bt,yt,Kr,Ue={},Fe=[],Vr=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,Ke=Array.isArray;function L(e,t){for(var s in t)e[s]=t[s];return e}function wt(e){e&&e.parentNode&&e.parentNode.removeChild(e)}function Z(e,t,s){var n,r,o,a={};for(o in t)o=="key"?n=t[o]:o=="ref"?r=t[o]:a[o]=t[o];if(arguments.length>2&&(a.children=arguments.length>3?qe.call(arguments,2):s),typeof e=="function"&&e.defaultProps!=null)for(o in e.defaultProps)a[o]===void 0&&(a[o]=e.defaultProps[o]);return je(e,a,n,r,null)}function je(e,t,s,n,r){var o={type:e,props:t,key:s,ref:n,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:r??++ys,__i:-1,__u:0};return r==null&&y.vnode!=null&&y.vnode(o),o}function ee(e){return e.children}function Be(e,t){this.props=e,this.context=t}function V(e,t){if(t==null)return e.__?V(e.__,e.__i+1):null;for(var s;t<e.__k.length;t++)if((s=e.__k[t])!=null&&s.__e!=null)return s.__e;return typeof e.type=="function"?V(e):null}function zr(e){if(e.__P&&e.__d){var t=e.__v,s=t.__e,n=[],r=[],o=L({},t);o.__v=t.__v+1,y.vnode&&y.vnode(o),St(e.__P,o,t,e.__n,e.__P.namespaceURI,32&t.__u?[s]:null,n,s??V(t),!!(32&t.__u),r),o.__v=t.__v,o.__.__k[o.__i]=o,Ts(n,o,r),t.__e=t.__=null,o.__e!=s&&Ds(o)}}function Ds(e){if((e=e.__)!=null&&e.__c!=null)return e.__e=e.__c.base=null,e.__k.some(function(t){if(t!=null&&t.__e!=null)return e.__e=e.__c.base=t.__e}),Ds(e)}function vs(e){(!e.__d&&(e.__d=!0)&&F.push(e)&&!We.__r++||$s!=y.debounceRendering)&&(($s=y.debounceRendering)||_s)(We)}function We(){try{for(var e,t=1;F.length;)F.length>t&&F.sort(ws),e=F.shift(),t=F.length,zr(e)}finally{F.length=We.__r=0}}function Gs(e,t,s,n,r,o,a,l,u,c,d){var h,p,m,f,$,_,G=n&&n.__k||Fe,b=t.length;for(u=Jr(s,t,G,u,b),h=0;h<b;h++)(m=s.__k[h])!=null&&(p=m.__i!=-1&&G[m.__i]||Ue,m.__i=h,_=St(e,m,p,r,o,a,l,u,c,d),f=m.__e,m.ref&&p.ref!=m.ref&&(p.ref&&Dt(p.ref,null,m),d.push(m.ref,m.__c||f,m)),$==null&&f!=null&&($=f),4&m.__u?(u=xs(m,u,e),p.__e&&(p.__e=null)):typeof m.type=="function"&&_!==void 0?u=_:f&&(u=f.nextSibling),m.__u&=-7);return s.__e=$,u}function Jr(e,t,s,n,r){var o,a,l,u,c,d=s.length,h=d,p=0;for(e.__k=new Array(r),o=0;o<r;o++)(a=t[o])!=null&&typeof a!="boolean"&&typeof a!="function"?(typeof a=="string"||typeof a=="number"||typeof a=="bigint"||a.constructor==String?a=e.__k[o]=je(null,a,null,null,null):Ke(a)?a=e.__k[o]=je(ee,{children:a},null,null,null):a.constructor===void 0&&a.__b>0?a=e.__k[o]=je(a.type,a.props,a.key,a.ref?a.ref:null,a.__v):e.__k[o]=a,u=o+p,a.__=e,a.__b=e.__b+1,l=null,(c=a.__i=Yr(a,s,u,h))!=-1&&(h--,(l=s[c])&&(l.__u|=2)),l==null||l.__v==null?(c==-1&&(r>d?p--:r<d&&p++),typeof a.type!="function"&&(a.__u|=4)):c!=u&&(c==u-1?p--:c==u+1?p++:(c>u?p--:p++,a.__u|=4))):e.__k[o]=null;if(h)for(o=0;o<d;o++)(l=s[o])!=null&&(2&l.__u)==0&&(l.__e==n&&(n=V(l)),Rs(l,l));return n}function xs(e,t,s){var n,r;if(typeof e.type=="function"){for(n=e.__k,r=0;n&&r<n.length;r++)n[r]&&(n[r].__=e,t=xs(n[r],t,s));return t}e.__e!=t&&(t&&e.type&&!t.parentNode&&(t=V(e)),t=s.insertBefore(e.__e,t||null));do t=t&&t.nextSibling;while(t!=null&&t.nodeType==8);return t}function Yr(e,t,s,n){var r,o,a,l=e.key,u=e.type,c=t[s],d=c!=null&&(2&c.__u)==0;if(c===null&&l==null||d&&l==c.key&&u==c.type)return s;if(n>(d?1:0)){for(r=s-1,o=s+1;r>=0||o<t.length;)if((c=t[a=r>=0?r--:o++])!=null&&(2&c.__u)==0&&l==c.key&&u==c.type)return a}return-1}function ks(e,t,s){t[0]=="-"?e.setProperty(t,s??""):e[t]=s==null?"":typeof s!="number"||Vr.test(t)?s:s+"px"}function He(e,t,s,n,r){var o,a;e:if(t=="style")if(typeof s=="string")e.style.cssText=s;else{if(typeof n=="string"&&(e.style.cssText=n=""),n)for(t in n)s&&t in s||ks(e.style,t,"");if(s)for(t in s)n&&s[t]==n[t]||ks(e.style,t,s[t])}else if(t[0]=="o"&&t[1]=="n")o=t!=(t=t.replace(Ss,"$1")),a=t.toLowerCase(),t=a in e||t=="onFocusOut"||t=="onFocusIn"?a.slice(2):t.slice(2),e.l||(e.l={}),e.l[t+o]=s,s?n?s[$e]=n[$e]:(s[$e]=_t,e.addEventListener(t,o?yt:bt,o)):e.removeEventListener(t,o?yt:bt,o);else{if(r=="http://www.w3.org/2000/svg")t=t.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if(t!="width"&&t!="height"&&t!="href"&&t!="list"&&t!="form"&&t!="tabIndex"&&t!="download"&&t!="rowSpan"&&t!="colSpan"&&t!="role"&&t!="popover"&&t in e)try{e[t]=s??"";break e}catch{}typeof s=="function"||(s==null||s===!1&&t[4]!="-"?e.removeAttribute(t):e.setAttribute(t,t=="popover"&&s==1?"":s))}}function bs(e){return function(t){if(this.l){var s=this.l[t.type+e];if(t[Ne]==null)t[Ne]=_t++;else if(t[Ne]<s[$e])return;return s(y.event?y.event(t):t)}}}function St(e,t,s,n,r,o,a,l,u,c){var d,h,p,m,f,$,_,G,b,P,he,K,ge,gs,Le,vt,I=t.type;if(t.constructor!==void 0)return null;128&s.__u&&(u=!!(32&s.__u),o=[l=t.__e=s.__e]),(d=y.__b)&&d(t);e:if(typeof I=="function"){h=a.length;try{if(b=t.props,P=I.prototype&&I.prototype.render,he=(d=I.contextType)&&n[d.__c],K=d?he?he.props.value:d.__:n,s.__c?G=(p=t.__c=s.__c).__=p.__E:(P?t.__c=p=new I(b,K):(t.__c=p=new Be(b,K),p.constructor=I,p.render=Xr),he&&he.sub(p),p.state||(p.state={}),p.__n=n,m=p.__d=!0,p.__h=[],p._sb=[]),P&&p.__s==null&&(p.__s=p.state),P&&I.getDerivedStateFromProps!=null&&(p.__s==p.state&&(p.__s=L({},p.__s)),L(p.__s,I.getDerivedStateFromProps(b,p.__s))),f=p.props,$=p.state,p.__v=t,m)P&&I.getDerivedStateFromProps==null&&p.componentWillMount!=null&&p.componentWillMount(),P&&p.componentDidMount!=null&&p.__h.push(p.componentDidMount);else{if(P&&I.getDerivedStateFromProps==null&&b!==f&&p.componentWillReceiveProps!=null&&p.componentWillReceiveProps(b,K),t.__v==s.__v||!p.__e&&p.shouldComponentUpdate!=null&&p.shouldComponentUpdate(b,p.__s,K)===!1){t.__v!=s.__v&&(p.props=b,p.state=p.__s,p.__d=!1),t.__e=s.__e,t.__k=s.__k,t.__k.some(function(X){X&&(X.__=t)}),Fe.push.apply(p.__h,p._sb),p._sb=[],p.__h.length&&a.push(p),l=V(s);break e}p.componentWillUpdate!=null&&p.componentWillUpdate(b,p.__s,K),P&&p.componentDidUpdate!=null&&p.__h.push(function(){p.componentDidUpdate(f,$,_)})}if(p.context=K,p.props=b,p.__P=e,p.__e=!1,ge=y.__r,gs=0,P)p.state=p.__s,p.__d=!1,ge&&ge(t),d=p.render(p.props,p.state,p.context),Fe.push.apply(p.__h,p._sb),p._sb=[];else do p.__d=!1,ge&&ge(t),d=p.render(p.props,p.state,p.context),p.state=p.__s;while(p.__d&&++gs<25);p.state=p.__s,p.getChildContext!=null&&(n=L(L({},n),p.getChildContext())),P&&!m&&p.getSnapshotBeforeUpdate!=null&&(_=p.getSnapshotBeforeUpdate(f,$)),Le=d!=null&&d.type===ee&&d.key==null?As(d.props.children):d,l=Gs(e,Ke(Le)?Le:[Le],t,s,n,r,o,a,l,u,c),p.base=t.__e,t.__u&=-161,p.__h.length&&a.push(p),G&&(p.__E=p.__=null)}catch(X){if(a.length=h,t.__v=null,u||o!=null){if(X.then){for(t.__u|=u?160:128;l&&l.nodeType==8&&l.nextSibling;)l=l.nextSibling;o!=null&&(o[o.indexOf(l)]=null),t.__e=l}else if(o!=null)for(vt=o.length;vt--;)wt(o[vt])}else t.__e=s.__e;t.__k==null&&(t.__k=s.__k||[]),X.then||Cs(t),y.__e(X,t,s)}}else o==null&&t.__v==s.__v?(t.__k=s.__k,t.__e=s.__e):l=t.__e=Qr(s.__e,t,s,n,r,o,a,u,c);return(d=y.diffed)&&d(t),128&t.__u?void 0:l}function Cs(e){e&&(e.__c&&(e.__c.__e=!0),e.__k&&e.__k.some(Cs))}function Ts(e,t,s){for(var n=0;n<s.length;n++)Dt(s[n],s[++n],s[++n]);y.__c&&y.__c(t,e),e.some(function(r){try{e=r.__h,r.__h=[],e.some(function(o){o.call(r)})}catch(o){y.__e(o,r.__v)}})}function As(e){return typeof e!="object"||e==null||e.__b>0?e:Ke(e)?e.map(As):e.constructor!==void 0?null:L({},e)}function Qr(e,t,s,n,r,o,a,l,u){var c,d,h,p,m,f,$,_=s.props||Ue,G=t.props,b=t.type;if(b=="svg"?r="http://www.w3.org/2000/svg":b=="math"?r="http://www.w3.org/1998/Math/MathML":r||(r="http://www.w3.org/1999/xhtml"),o!=null){for(c=0;c<o.length;c++)if((m=o[c])&&"setAttribute"in m==!!b&&(b?m.localName==b:m.nodeType==3)){e=m,o[c]=null;break}}if(e==null){if(b==null)return document.createTextNode(G);e=document.createElementNS(r,b,G.is&&G),l&&(y.__m&&y.__m(t,o),l=!1),o=null}if(b==null)_===G||l&&e.data==G||(e.data=G);else{if(o=b=="textarea"&&G.defaultValue!=null?null:o&&qe.call(e.childNodes),!l&&o!=null)for(_={},c=0;c<e.attributes.length;c++)_[(m=e.attributes[c]).name]=m.value;for(c in _)m=_[c],c=="dangerouslySetInnerHTML"?h=m:c=="children"||c in G||c=="value"&&"defaultValue"in G||c=="checked"&&"defaultChecked"in G||He(e,c,null,m,r);for(c in G)m=G[c],c=="children"?p=m:c=="dangerouslySetInnerHTML"?d=m:c=="value"?f=m:c=="checked"?$=m:l&&typeof m!="function"||_[c]===m||He(e,c,m,_[c],r);if(d)l||h&&(d.__html==h.__html||d.__html==e.innerHTML)||(e.innerHTML=d.__html),t.__k=[];else if(h&&(e.innerHTML=""),Gs(t.type=="template"?e.content:e,Ke(p)?p:[p],t,s,n,b=="foreignObject"?"http://www.w3.org/1999/xhtml":r,o,a,o?o[0]:s.__k&&V(s,0),l,u),o!=null)for(c=o.length;c--;)wt(o[c]);l&&b!="textarea"||(c="value",b=="progress"&&f==null?e.removeAttribute("value"):f!=null&&(f!==e[c]||b=="progress"&&!f||b=="option"&&f!=_[c])&&He(e,c,f,_[c],r),c="checked",$!=null&&$!=e[c]&&He(e,c,$,_[c],r))}return e}function Dt(e,t,s){try{if(typeof e=="function"){var n=typeof e.__u=="function";n&&e.__u(),n&&t==null||(e.__u=e(t))}else e.current=t}catch(r){y.__e(r,s)}}function Rs(e,t,s){var n,r;if(y.unmount&&y.unmount(e),(n=e.ref)&&(n.current&&n.current!=e.__e||Dt(n,null,t)),(n=e.__c)!=null){if(n.componentWillUnmount)try{n.componentWillUnmount()}catch(o){y.__e(o,t)}n.base=n.__P=n.__n=null}if(n=e.__k)for(r=0;r<n.length;r++)n[r]&&Rs(n[r],t,s||typeof e.type!="function");s||wt(e.__e),e.__c=e.__=e.__e=void 0}function Xr(e,t,s){return this.constructor(e,s)}function Gt(e,t,s){var n,r,o,a;t==document&&(t=document.documentElement),y.__&&y.__(e,t),r=(n=typeof s=="function")?null:s&&s.__k||t.__k,o=[],a=[],St(t,e=(!n&&s||t).__k=Z(ee,null,[e]),r||Ue,Ue,t.namespaceURI,!n&&s?[s]:r?null:t.firstChild?qe.call(t.childNodes):null,o,!n&&s?s:r?r.__e:t.firstChild,n,a),Ts(o,e,a),e.props.children=null}qe=Fe.slice,y={__e:function(e,t,s,n){for(var r,o,a;t=t.__;)if((r=t.__c)&&!r.__)try{if((o=r.constructor)&&o.getDerivedStateFromError!=null&&(r.setState(o.getDerivedStateFromError(e)),a=r.__d),r.componentDidCatch!=null&&(r.componentDidCatch(e,n||{}),a=r.__d),a)return r.__E=r}catch(l){e=l}throw e}},ys=0,qr=function(e){return e!=null&&e.constructor===void 0},Be.prototype.setState=function(e,t){var s;s=this.__s!=null&&this.__s!=this.state?this.__s:this.__s=L({},this.state),typeof e=="function"&&(e=e(L({},s),this.props)),e&&L(s,e),e!=null&&this.__v&&(t&&this._sb.push(t),vs(this))},Be.prototype.forceUpdate=function(e){this.__v&&(this.__e=!0,e&&this.__h.push(e),vs(this))},Be.prototype.render=ee,F=[],_s=typeof Promise=="function"?Promise.prototype.then.bind(Promise.resolve()):setTimeout,ws=function(e,t){return e.__v.__b-t.__v.__b},We.__r=0,kt=Math.random().toString(8),Ne="__d"+kt,$e="__a"+kt,Ss=/(PointerCapture)$|Capture$/i,_t=0,bt=bs(!1),yt=bs(!0),Kr=0;var te=e=>e>999?"999+":String(e);function Zr(e){return{id:e,lane:"work",bucket:"done",repo:"",title:"",verb:"unknown",sort:0}}var Ms=new WeakMap;function xt(e,t){let s=e.dispatches||[],n=Ms.get(s);return n||Ms.set(s,n=new Map(s.map(r=>[r.id,r]))),{...t,catches:(t.catches||[]).map(r=>n.get(r)??Zr(r))}}function E(e){if(!e)return"dim";let t=e.state||"open";return t==="open"&&e.merge?"pr-"+e.merge:t==="open"&&e.tone&&e.tone!=="ok"?e.tone:"pr-"+t}var se=["deck","dispatches","traps","prs","reports","notices","stats"];function Os(e){let t=String(e||"").replace(/^#/,"");return se.includes(t)?t:"deck"}var Ct=e=>`#decision/${encodeURIComponent(e)}`;function ne(e){let t=/^#?decision\\/(.+)$/.exec(String(e||""));if(!t)return null;try{return decodeURIComponent(t[1])}catch{return null}}var Tt=(e,t,s)=>`/attachment/dispatch/${encodeURIComponent(e)}/${encodeURIComponent(t)}/${encodeURIComponent(s)}`,Ls=(e,t)=>`/attachment/trap/${encodeURIComponent(e)}/${encodeURIComponent(t)}`,Hs=e=>/\\.(png|jpe?g|gif|webp)$/i.test(e),At=(e,t)=>`/decision/${encodeURIComponent(e)}/files/${encodeURIComponent(t)}`;function Ve(e,t){let s=new Map(t.map(r=>[r.key,r])),n=[];for(let r of e)if(r.kind==="decision"){let o=s.get(r.key);if(!o)continue;n.push({kind:"decision",key:o.key,title:o.title,detail:o.detail,options:o.options,attachments:o.attachments,...o.dispatch?{dispatch:o.dispatch,lane:o.lane??"work"}:{},...o.repo?{repo:o.repo}:{},at:o.askedAt,...o.viewedAt??r.viewedAt?{viewedAt:o.viewedAt??r.viewedAt}:{}})}else r.kind==="question"&&n.push({kind:"question",key:r.key,verb:r.verb,note:r.note||r.verb,dispatch:r.id,lane:r.lane,...r.repo?{repo:r.repo}:{},at:r.at??"",...r.viewedAt?{viewedAt:r.viewedAt}:{}});return n.sort((r,o)=>o.at.localeCompare(r.at)||r.key.localeCompare(o.key))}function Ns(e){let t=(e.text??"").split(`\n`).map(s=>s.trim()).find(Boolean)??"";return[e.option,t&&(t.length>80?t.slice(0,79)+"\\u2026":t),e.files?`${e.files} file${e.files===1?"":"s"}`:""].filter(Boolean).join(" \\xB7 ")}function js(e,t){return(e.reports||[]).find(s=>s.key===`report:${t.lane}:${t.id}`)}function ve(e){return e.author==="helm"?"":e.trap?e.trap:e.author==="headless"?e.dispatch?e.dispatch.slice(0,8):"":e.author}var Ps=(e,t)=>+!!e.acked-+!!t.acked||t.filedAt.localeCompare(e.filedAt),x=e=>`/report/${encodeURIComponent(e)}`;function ke(e){let t=/^#?report\\/(.+)$/.exec(String(e||""));if(!t)return null;try{return decodeURIComponent(t[1])}catch{return null}}function Bs(e){return typeof e!="string"?!1:e===""?!0:e.length>1024||!e.startsWith("#")||/[\\s\\x00-\\x1f\\x7f]/.test(e)?!1:se.includes(e.slice(1))||ne(e)!==null||ke(e)!==null}function Us(e){let t=/^\\/report\\/([^/?#]+)\\/?$/.exec(e);if(!t)return null;try{return decodeURIComponent(t[1])}catch{return null}}var Fs=e=>`/report/${encodeURIComponent(e)}/md`,Rt=(e,t)=>`/report/${encodeURIComponent(e)}/files/${encodeURIComponent(t)}`,Mt={dispatches:[],notices:[],prs:[],stacks:[]};function Ws(e,t){if(t.kind==="dispatches"){let r=new Set(e.dispatches.map(o=>o.lane+":"+o.id));return{...e,dispatches:[...e.dispatches,...t.items.filter(o=>!r.has(o.lane+":"+o.id))]}}if(t.kind==="notices"){let r=new Set(e.notices.map(o=>o.seq));return{...e,notices:[...e.notices,...t.items.filter(o=>!r.has(o.seq))]}}let s=new Set(e.prs.map(r=>r.key)),n=new Set(e.stacks.map(r=>r.id));return{...e,prs:[...e.prs,...t.items.filter(r=>!s.has(r.key))],stacks:[...e.stacks,...t.stacks.filter(r=>!n.has(r.id))]}}function qs(e,t){if(t===Mt||!t.dispatches.length&&!t.notices.length&&!t.prs.length)return e;let s=new Set(e.dispatches.map(a=>a.lane+":"+a.id)),n=new Set(e.notices.map(a=>a.seq)),r=new Set(e.prs.map(a=>a.key)),o=new Set(e.stacks.map(a=>a.id));return{...e,dispatches:[...e.dispatches,...t.dispatches.filter(a=>!s.has(a.lane+":"+a.id))],notices:[...e.notices,...t.notices.filter(a=>!n.has(a.seq))],prs:[...e.prs,...t.prs.filter(a=>!r.has(a.key))],stacks:[...e.stacks,...t.stacks.filter(a=>!o.has(a.id))]}}function Ks(e,t,s){return Math.max(0,(e.older?.[s]??0)-t[s].length)}function Vs(e,t){return{...e,now:t.now,daemon:e.daemon&&t.daemon!==void 0?{...e.daemon,heartbeat:t.daemon}:e.daemon,helms:e.helms.map(s=>t.helms[s.grounds]!==void 0?{...s,heartbeatAt:t.helms[s.grounds]}:s),traps:e.traps.map(s=>t.traps[s.trapId]?{...s,...t.traps[s.trapId]}:s)}}function Is(e,t,s){return!!e&&s-Date.parse(e)>t}function Es(e,t){if(t.lane&&e.lane!==t.lane||t.repo&&e.repo!==t.repo||t.verb&&e.verb!==t.verb)return!1;if(t.q){let s=t.q.toLowerCase();if(!(e.id+" "+(e.note||"")+" "+(e.title||"")+" "+(e.repo||"")+" "+(e.for||"")).toLowerCase().includes(s))return!1}return!0}function re(e,t,s){if(!t)return null;if(t.type==="helm")return e.helms.find(r=>r.grounds===t.key)||null;if(t.type==="dispatch"){let r=e.dispatches.find(a=>a.lane+":"+a.id===t.key),o=s&&s.key===t.key?s.data:void 0;return r&&o?{...r,...o}:r??o??null}if(t.type==="pr")return(e.prs||[]).find(r=>r.key===t.key)||null;if(t.type==="settings")return{attentionKinds:e.attentionKinds||[],attentionError:e.attentionError};let n=e.traps.find(r=>r.trapId===t.key);return n?xt(e,n):null}function zs(e,t,s){let n=t.st,r=String(n.q||"").toLowerCase(),o=(...f)=>!r||f.join(" ").toLowerCase().includes(r),a=f=>({x:f,stale:Is(f.heartbeatAt,18e5,s)}),l=re(e,t.modal,t.detail),u=(f,$)=>!!f&&s-Date.parse(f)<=$,c=f=>Math.max(0,...(f.notices||[]).filter($=>$.kind==="trap-stowed"||$.kind==="trap-ghosted").map($=>Date.parse($.at)||0)),d=f=>f.live||!!f.starting||!!f.requested,h=[...e.traps||[]].sort((f,$)=>Number(d($))-Number(d(f))||Number($.live)-Number(f.live)||(f.live?(Date.parse(f.signedOnAt||"")||0)-(Date.parse($.signedOnAt||"")||0):c($)-c(f))||(f.name??f.trapId).localeCompare($.name??$.trapId)),p=h.filter(f=>(d(f)||(f.notices||[]).some($=>($.kind==="trap-stowed"||$.kind==="trap-ghosted")&&u($.at,36e5)))&&o(f.name,f.trapId,f.repo,f.worktree)),m=({ageSecs:f,...$})=>$;return{chips:{daemon:e.daemon,daemonStale:!!e.daemon&&Is(e.daemon.heartbeat,9e4,s),helms:e.helms.map(a),stats:e.stats},deck:{view:n.view,decisions:Ve(e.attention||[],e.decisions||[]).filter(f=>f.kind==="decision"?o(f.kind,f.repo,f.title,f.dispatch,f.detail):o(f.kind,f.repo,f.note,f.dispatch)),prAttention:(e.attention||[]).filter(f=>f.kind&&f.kind.startsWith("pr:")).map(m),landed:(e.landed||[]).filter(f=>u(f.at,864e5)&&o(f.repo,f.note,f.id)).sort((f,$)=>Date.parse($.at)-Date.parse(f.at)).slice(0,8),reports:(e.reports||[]).filter(f=>o(f.title,f.author,f.dispatch,f.repo)).sort(Ps),inflight:e.dispatches.filter(f=>f.bucket!=="done"&&Es(f,{...n,lane:"",repo:"",verb:""})),traps:p.map(f=>a(xt(e,f))),stacks:(e.stacks||[]).filter(f=>f.open&&o(f.repo,f.numbers.join(" "))),prs:(e.prs||[]).filter(f=>f.state==="OPEN"),error:e.attentionError},dispatches:{view:n.view,chain:n.chain,list:e.dispatches.filter(f=>Es(f,n))},traps:{view:n.view,list:h.filter(f=>(!n.repo||f.repo===n.repo)&&o(f.name,f.trapId,f.repo,f.worktree,f.harness)).map(f=>a(xt(e,f)))},prs:{view:n.view,stacks:(e.stacks||[]).filter(f=>!n.repo||f.repo===n.repo),prs:(e.prs||[]).filter(f=>(!n.repo||f.repo===n.repo)&&o(f.number,f.title,f.url,f.state,f.baseRefName,f.headRefName)),watches:(e.watches||[]).filter(f=>!String(f.key).startsWith("pr:"))},reports:{view:n.view,list:(e.reports||[]).filter(f=>(!n.repo||f.repo===n.repo)&&o(f.title,f.author,f.dispatch,f.repo)).sort(Ps)},notices:{list:(e.notices||[]).filter(f=>(!n.repo||!f.repo||f.repo===n.repo)&&(!n.noticeKind||f.kind===n.noticeKind)&&o(f.kind,f.text,f.repo))},foot:{version:e.version,repoUrl:e.repoUrl},modal:{modal:t.modal,item:l&&a(l),prefs:t.modal&&t.modal.type==="settings"?{view:n.view,lobs:n.lobs}:void 0}}}function Js(e,t){let s=(e.prs||[]).find(l=>l.key===t);if(!s)return null;let n=(e.stacks||[]).find(l=>l.id===s.stackId),r=new Map((e.dispatches||[]).map(l=>[l.id,l])),o=(s.dispatchIds||[]).map(l=>{let u=r.get(l);return u?{id:l,verb:u.verb,modalKey:u.lane+":"+u.id}:{id:l,culled:!0}}),a=s.watch;return{pr:s,stack:n?{numbers:n.numbers,titles:n.numbers.map(l=>(e.prs||[]).find(u=>u.stackId===n.id&&u.number===l)?.title||""),position:s.position+1,size:n.numbers.length,floor:n.floor,nextNumber:n.nextNumber,nextMergeable:!!s.nextMergeable,blockedBy:s.blockedBy}:null,chain:o,watch:a?{key:a.key,owner:a.owner||"",lastCheckedAt:a.lastCheckedAt||null,lastError:a.lastError||null,cursor:a.cursor}:null}}function Ys(e){return e?{text:e.lastError?"failing":"watching",at:e.lastCheckedAt||null}:{text:"no watch",at:null}}function ze(e,t){return e.filter(s=>!t(s.key)).sort((s,n)=>s.at.localeCompare(n.at)||s.key.localeCompare(n.key))}function Qs(e,t,s){return ze(e,t).filter(n=>!n.viewedAt&&!s(n.key))}function Xs(){let e={view:"table",lane:"",repo:"",verb:"",q:"",lobs:!0,chain:!1,noticeKind:""};try{Object.assign(e,JSON.parse(localStorage.getItem("spyglass")||"{}"))}catch{}return e.view!=="table"&&e.view!=="cards"&&(e.view="table"),e.lobs=e.lobs!==!1,e}function Zs(e){try{localStorage.setItem("spyglass",JSON.stringify(e))}catch{}}function en(){try{return JSON.parse(localStorage.getItem("spyglass-lob-hidden")||"{}")||{}}catch{return{}}}function tn(e){try{localStorage.setItem("spyglass-lob-hidden",JSON.stringify(e))}catch{}}var be,Je;function oe(e){if(e.snapshot)return(Je?.snapshot!==e.snapshot||Je.older!==e.older)&&(Je={snapshot:e.snapshot,older:e.older,view:qs(e.snapshot,e.older)}),Je.view}var sn=new Set,g=()=>be;function Pt(e){be=e}function k(e){be={...be,...e};for(let t of sn)t(be)}var It=e=>{sn.add(e)};function eo(e,t){let s=oe(e);return s&&re(s,t,e.detail)?!0:t.type==="dispatch"&&e.detail?.key===t.key&&!e.detail.error}function Ye(e){let{modal:t,drafts:s,older:n,detail:r,decisionModal:o}=g(),a=Ve(e.attention||[],e.decisions||[]),l=new Set(a.map(c=>c.key)),u=Object.fromEntries(Object.entries(s).filter(([c])=>l.has(c)));k({snapshot:e,stale:!1,modal:t&&eo({snapshot:e,older:n,detail:r},t)?t:null,...Object.keys(u).length!==Object.keys(s).length?{drafts:u}:{},...o?.key&&!l.has(o.key)&&!s[o.key]?.sent?{decisionModal:nn(a,u,o.key)}:{}})}function nn(e,t,s){let n=ze(e,o=>!!t[o]?.sent||o===s);if(!n.length)return null;let r=e.find(o=>o.key===s)?.at??"";return{key:(n.find(o=>o.at>r||o.at===r&&o.key>(s??""))??n[0]).key}}function Qe(){let e=oe(g());return e?Ve(e.attention||[],e.decisions||[]):[]}function ye(){let{drafts:e}=g();return ze(Qe(),t=>!!e[t]?.sent)}function Xe(){let{drafts:e,viewedHere:t}=g();return Qs(Qe(),s=>!!e[s]?.sent,s=>!!t[s])}function ae(e){let t=e??Xe()[0]?.key??ye()[0]?.key;t&&k({decisionModal:{key:t}})}function _e(e){let t=g().decisionModal;if(!t?.key)return;let s=ye(),n=s.findIndex(o=>o.key===t.key),r=s[n+e];n>=0&&r&&k({decisionModal:{key:r.key}})}var ie=()=>k({decisionModal:null}),we=9;function to(){let e=g().decisionModal?.key;return e?ye().find(t=>t.key===e):void 0}function rn(e){let t=to();if(!t||e<1||e>we)return!1;let s=g().drafts[t.key];if(s?.sending||s?.sent)return!1;let n=t.kind==="decision"?t.options:[];return e<=n.length?(A(t.key,{option:n[e-1],error:void 0}),document.querySelectorAll(".dmodal .dopt")[e-1]?.focus(),!0):e===n.length+1?(document.querySelector(".dmodal textarea.danswer")?.focus(),!0):!1}async function on(e){let t=g();if(t.viewedHere[e]||(k({viewedHere:{...t.viewedHere,[e]:!0}}),Qe().find(n=>n.key===e)?.viewedAt))return;let s=t.snapshot?.focusToken;if(s)try{await fetch("/requests",{method:"POST",headers:{"content-type":"application/json","x-lobstah-token":s},body:JSON.stringify({kind:"decision-viewed",payload:{key:e}})})}catch{}}function an(){k({alertDismissed:Xe().map(e=>e.key)})}function ln(e){let{snapshot:t}=g();k({stale:!1,...t?{snapshot:Vs(t,e)}:{}})}async function Ot(){let{modal:e}=g();if(e?.type!=="dispatch")return;let t=e.key,s=t.slice(t.indexOf(":")+1),n;try{let o=await fetch(`/data/dispatch/${encodeURIComponent(s)}`,{cache:"no-store"});n=o.ok?{data:await o.json()}:{error:o.status===404?"this dispatch is gone":`the detail answered ${o.status}`}}catch{n={error:"the detail could not be read"}}let r=g();r.modal?.type!=="dispatch"||r.modal.key!==t||n.error&&r.detail?.key===t&&r.detail.data||k({detail:{key:t,...n}})}var Et=!1;async function Se(){if(!Et){Et=!0;try{let e=await fetch("/data/stats",{cache:"no-store"});if(!e.ok)throw new Error(`/data/stats answered ${e.status}`);k({stats:await e.json(),statsError:null})}catch{k({statsError:"stats could not be read"})}finally{Et=!1}}}var Lt=e=>{g().statsFocus!==e&&k({statsFocus:e})};async function dn(e){let{olderLoading:t,older:s}=g();if(!t){k({olderLoading:e,olderError:null});try{let n=await fetch(`/data/older?kind=${e}&offset=${s[e].length}&limit=50`,{cache:"no-store"});if(!n.ok)throw new Error(`/data/older answered ${n.status}`);let r=await n.json();k({older:Ws(g().older,r),olderLoading:null})}catch{k({olderLoading:null,olderError:{kind:e,text:"older records could not be read"}})}}}var cn=()=>({text:"",files:[]});function A(e,t){let s=g().drafts;k({drafts:{...s,[e]:{...s[e]??cn(),...t}}})}function pn(e,t){let s=g().drafts[e]?.option;A(e,{option:s===t?void 0:t,error:void 0})}var so=e=>new Promise((t,s)=>{let n=new FileReader;n.onload=()=>t(String(n.result).replace(/^data:[^,]*,/,"")),n.onerror=()=>s(n.error),n.readAsDataURL(e)});async function Ht(e,t){let s=g().snapshot?.answerLimits,n=[...t??[]],r=g().drafts[e]?.files??[],o=[],a=[];for(let l of n){let u=(/\\.[^.]+$/.exec(l.name)?.[0]??"").toLowerCase();if(s&&!s.extensions.includes(u))o.push(`${l.name}: type not accepted`);else if(s&&l.size>s.maxBytes)o.push(`${l.name}: larger than ${s.maxBytes} bytes`);else if(s&&r.length+a.length>=s.maxFiles)o.push(`${l.name}: at most ${s.maxFiles} files`);else try{a.push({name:l.name,bytes:l.size,data:await so(l)})}catch{o.push(`${l.name}: could not be read`)}}A(e,{files:[...g().drafts[e]?.files??[],...a],error:o.length?o.join("; "):void 0})}var no={"image/png":".png","image/jpeg":".jpg","image/gif":".gif","image/webp":".webp"};async function un(e,t){let s=[...t.clipboardData?.items??[]],n=new Date().toISOString().replace(/[:.]/g,"-").replace(/-\\d{3}Z$/,"Z"),r=s.filter(o=>o.kind==="file"&&o.type.startsWith("image/")).map(o=>o.getAsFile()).filter(o=>o!==null).map((o,a,l)=>{let u=`pasted-${n}${l.length>1?`-${a+1}`:""}${no[o.type]??".png"}`;return new File([o],u,{type:o.type})});r.length&&await Ht(e,r)}function fn(e,t){let s=(g().drafts[e]?.files??[]).filter((n,r)=>r!==t);A(e,{files:s})}async function Ze(e){let t=g().snapshot?.focusToken,s=g().drafts[e]??cn();if(!(s.sending||s.sent)){if(!s.option&&!s.text.trim()&&s.files.length===0){A(e,{error:"Choose an option, write an answer, or attach a file."});return}if(!t){A(e,{error:"This page has no answer token; reload it."});return}A(e,{sending:!0,error:void 0});try{let n=await fetch("/requests",{method:"POST",headers:{"content-type":"application/json","x-lobstah-token":t},body:JSON.stringify({kind:"decision-answer",payload:{key:e,...s.option?{option:s.option}:{},...s.text.trim()?{text:s.text}:{},files:s.files.map(a=>({name:a.name,data:a.data}))}})}),r=await n.json();if(!n.ok||!r.ok){A(e,{sending:!1,error:r.reason??`The answer was refused (${n.status}).`});return}A(e,{sending:!1,sent:Ns({option:s.option,text:s.text,files:s.files.length})}),g().decisionModal?.key===e&&k({decisionModal:nn(Qe(),g().drafts,e)??{key:null}})}catch{A(e,{sending:!1,error:"The answer could not be sent."})}}}var mn=()=>k({stale:!0}),hn=(e,t)=>k({lightbox:{src:e,name:t}}),le=()=>k({lightbox:null});function O(e,t){let s=g(),n={type:e,key:t};if(e==="dispatch"){k({modal:n,detail:s.detail?.key===t?s.detail:{key:t}}),Ot();return}let r=oe(s);k({modal:!r||re(r,n)?n:null,detail:null})}function de(){k({modal:null,detail:null})}async function gn(e){let t=g().snapshot?.focusToken;if(t)try{let n=await(await fetch(`/api/focus/${encodeURIComponent(e)}`,{method:"POST",headers:{"x-lobstah-focus-token":t}})).json();k({focusResults:{...g().focusResults,[e]:n.message??n.reason??"Window focus failed."}})}catch{k({focusResults:{...g().focusResults,[e]:"Window focus failed."}})}}async function $n(e,t){let s=g().snapshot?.focusToken;if(!s)return"The glass has no page token yet.";try{let r=await(await fetch("/requests",{method:"POST",headers:{"content-type":"application/json","x-lobstah-token":s},body:JSON.stringify({kind:"trap-request",payload:{repo:e,harness:t}})})).json();if(!r.ok)return r.reason??"The request was refused."}catch{return"The request could not be sent."}try{Ye(await(await fetch("/data")).json())}catch{}}function H(e){let t={...g().prefs,...e};Zs(t),k({prefs:t})}function vn(e){e!=="table"&&e!=="cards"||H({view:e})}var kn=e=>H({lobs:e==="on"});function bn(e,t){let s={...g().lobHidden,[e]:t};tn(s),setTimeout(()=>k({lobHidden:s}),0)}async function yn(e){try{return await navigator.clipboard.writeText(e),!0}catch{return!1}}var wn=function(e,t,s,n){var r;t[0]=0;for(var o=1;o<t.length;o++){var a=t[o++],l=t[o]?(t[0]|=a?1:2,s[t[o++]]):t[++o];a===3?n[0]=l:a===4?n[1]=Object.assign(n[1]||{},l):a===5?(n[1]=n[1]||{})[t[++o]]=l:a===6?n[1][t[++o]]+=l+"":a?(r=e.apply(l,wn(e,l,s,["",null])),n.push(r),l[0]?t[0]|=2:(t[o-2]=0,t[o]=r)):n.push(l)}return n},_n=new Map;function Sn(e){var t=_n.get(this);return t||(t=new Map,_n.set(this,t)),(t=wn(this,t.get(e)||(t.set(e,t=(function(s){for(var n,r,o=1,a="",l="",u=[0],c=function(p){o===1&&(p||(a=a.replace(/^\\s*\\n\\s*|\\s*\\n\\s*$/g,"")))?u.push(0,p,a):o===3&&(p||a)?(u.push(3,p,a),o=2):o===2&&a==="..."&&p?u.push(4,p,0):o===2&&a&&!p?u.push(5,0,!0,a):o>=5&&((a||!p&&o===5)&&(u.push(o,0,a,r),o=6),p&&(u.push(o,p,0,r),o=6)),a=""},d=0;d<s.length;d++){d&&(o===1&&c(),c(d));for(var h=0;h<s[d].length;h++)n=s[d][h],o===1?n==="<"?(c(),u=[u],o=3):a+=n:o===4?a==="--"&&n===">"?(o=1,a=""):a=n+a[0]:l?n===l?l="":a+=n:n===\'"\'||n==="\'"?l=n:n===">"?(c(),o=1):o&&(n==="="?(o=5,r=a,a=""):n==="/"&&(o<5||s[d][h+1]===">")?(c(),o===3&&(u=u[0]),o=u,(u=u[0]).push(2,0,o),o=0):n===" "||n===" "||n===`\n`||n==="\\r"?(c(),o=2):a+=n),o===3&&a==="!--"&&(o=4,u=u[0])}return c(),u})(e)),t),arguments,[])).length>1?t:t[0]}var Dn=e=>Array.isArray(e)?e.map(Dn):e===""?null:e,ro=(e,t,...s)=>Z(e,t,...s.map(Dn)),i=Sn.bind(ro);var De,w,Nt,Gn,tt=0,In=[],D=y,xn=D.__b,Cn=D.__r,Tn=D.diffed,An=D.__c,Rn=D.unmount,Mn=D.__;function Bt(e,t){D.__h&&D.__h(w,e,tt||t),tt=0;var s=w.__H||(w.__H={__:[],__h:[]});return e>=s.__.length&&s.__.push({}),s.__[e]}function z(e){return tt=1,oo(On,e)}function oo(e,t,s){var n=Bt(De++,2);if(n.t=e,!n.__c&&(n.__=[s?s(t):On(void 0,t),function(l){var u=n.__N?n.__N[0]:n.__[0],c=n.t(u,l);u!==c&&(n.__N=[c,n.__[1]],n.__c.setState({}))}],n.__c=w,!w.__f)){var r=function(l,u,c){if(!n.__c.__H)return!0;var d=!1,h=n.__c.props!==l;if(n.__c.__H.__.some(function(m){if(m.__N){d=!0;var f=m.__[0];m.__=m.__N,m.__N=void 0,f!==m.__[0]&&(h=!0)}}),o){var p=o.call(this,l,u,c);return d?p||h:p}return!d||h};w.__f=!0;var o=w.shouldComponentUpdate,a=w.componentWillUpdate;w.componentWillUpdate=function(l,u,c){if(this.__e){var d=o;o=void 0,r(l,u,c),o=d}a&&a.call(this,l,u,c)},w.shouldComponentUpdate=r}return n.__N||n.__}function Ut(e,t){var s=Bt(De++,3);!D.__s&&En(s.__H,t)&&(s.__=e,s.u=t,w.__H.__h.push(s))}function Ft(e){return tt=5,Wt(function(){return{current:e}},[])}function Wt(e,t){var s=Bt(De++,7);return En(s.__H,t)&&(s.__=e(),s.__H=t,s.__h=e),s.__}function ao(){for(var e;e=In.shift();){var t=e.__H;if(e.__P&&t)try{t.__h.some(et),t.__h.some(jt),t.__h=[]}catch(s){t.__h=[],D.__e(s,e.__v)}}}D.__b=function(e){w=null,xn&&xn(e)},D.__=function(e,t){e&&t.__k&&t.__k.__m&&(e.__m=t.__k.__m),Mn&&Mn(e,t)},D.__r=function(e){Cn&&Cn(e),De=0;var t=(w=e.__c).__H;t&&(Nt===w?(t.__h=[],w.__h=[],t.__.some(function(s){s.__N&&(s.__=s.__N),s.u=s.__N=void 0})):(t.__h.some(et),t.__h.some(jt),t.__h=[],De=0)),Nt=w},D.diffed=function(e){Tn&&Tn(e);var t=e.__c;t&&t.__H&&(t.__H.__h.length&&(In.push(t)!==1&&Gn===D.requestAnimationFrame||((Gn=D.requestAnimationFrame)||io)(ao)),t.__H.__.some(function(s){s.u&&(s.__H=s.u,s.u=void 0)})),Nt=w=null},D.__c=function(e,t){t.some(function(s){try{s.__h.some(et),s.__h=s.__h.filter(function(n){return!n.__||jt(n)})}catch(n){t.some(function(r){r.__h&&(r.__h=[])}),t=[],D.__e(n,s.__v)}}),An&&An(e,t)},D.unmount=function(e){Rn&&Rn(e);var t,s=e.__c;s&&s.__H&&(s.__H.__.some(function(n){try{et(n)}catch(r){t=r}}),s.__H=void 0,t&&D.__e(t,s.__v))};var Pn=typeof requestAnimationFrame=="function";function io(e){var t,s=function(){clearTimeout(n),Pn&&cancelAnimationFrame(t),setTimeout(e)},n=setTimeout(s,35);Pn&&(t=requestAnimationFrame(s))}function et(e){var t=w,s=e.__c;typeof s=="function"&&(e.__c=void 0,s()),w=t}function jt(e){var t=w;e.__c=e.__(),w=t}function En(e,t){return!e||e.length!==t.length||t.some(function(s,n){return s!==e[n]})}function On(e,t){return typeof t=="function"?t(e):t}var lo=["none","minimal","low","medium","high","xhigh","max","ultra"],co=["default","acceptEdits","plan","dontAsk","bypassPermissions","auto"];function Ln(e){return typeof e=="string"&&/^[a-zA-Z0-9][a-zA-Z0-9._:/-]{0,127}$/.test(e)?e:null}function Hn(e,t){return typeof e=="string"&&t.includes(e)?e:null}function qt(e={}){return{harness:Ln(e.harness),model:Ln(e.model),config:{effort:Hn(e.config?.effort,lo),permissionMode:Hn(e.config?.permissionMode,co)},...typeof e.observedAt=="string"&&/^\\d{4}-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d\\.\\d{3}Z$/.test(e.observedAt)&&Number.isFinite(Date.parse(e.observedAt))?{observedAt:e.observedAt}:{}}}var J=e=>{if(!e)return"";let t=Math.max(0,(Date.now()-Date.parse(e))/1e3);return t<90?Math.round(t)+"s":t<5400?Math.round(t/60)+"m":t<172800?(t/3600).toFixed(1)+"h":Math.round(t/86400)+"d"},v=e=>i`<span data-age=${e??""}>${J(e)}</span>`;function N({worker:e,label:t,cls:s="badge"}){let n=qt(e),r=[n.model??"model unknown",n.config.effort].filter(Boolean).join(" \\xB7 "),[o,a]=z(null),l=u=>{let c=u.currentTarget.getBoundingClientRect();a({left:Math.max(8,Math.min(c.left,window.innerWidth-280)),top:c.bottom+6})};return i`<span class=${s+" worker-harness"} tabindex="0" aria-label=${r}\n onMouseEnter=${l} onFocus=${l} onBlur=${()=>a(null)}\n onMouseLeave=${u=>{document.activeElement!==u.currentTarget&&a(null)}}\n onKeyDown=${u=>{u.key==="Escape"&&a(null)}}>\n ${t??n.harness??"unknown harness"}${o&&!g().modal&&i`<span class="worker-tooltip" role="tooltip" aria-hidden="true" style=${o}>${r}</span>`}\n </span>`}var Kt=e=>e.worker&&i`<${N} worker=${e.worker} />`;function nt({worker:e,headless:t=!1}){let s=qt(e);return i`<section class="worker-details"><div class="sec">${t?"headless worker \\xB7 launch / observation":"worker observation"}</div><dl>\n <dt>harness</dt><dd>${s.harness??"unknown"}</dd>\n <dt>model</dt><dd>${s.model??"model unknown"}</dd>\n <dt>effort</dt><dd>${s.config.effort??"unknown"}</dd>\n <dt>permission mode</dt><dd>${s.config.permissionMode??"unknown"}</dd>\n <dt>observed</dt><dd>${s.observedAt??"unknown"}</dd>\n </dl></section>`}var rt=e=>e.activity&&i`<div class=${"activity"+(e.activity.stale?" stale":"")} title=${e.activity.stale?"no activity past the wedge threshold":e.activity.kind}>${e.activity.stale?"stale \\xB7 ":""}${e.activity.summary} \xB7 ${v(e.activity.at)} ago</div>`,po=e=>e&&/^https?:\\/\\//i.test(e)?e:void 0,ot=e=>{let t=e.waiting;if(!t)return;let s=po(t.link),n=s;try{s&&(n=new URL(s).host+new URL(s).pathname.replace(/\\/$/,""))}catch{n=s}return i`<div class="waiting">${e.verb}: waiting on ${t.on} \xB7 ${v(t.since)}${s&&[" \\xB7 ",i`<a href=${s} target="_blank" rel="noopener noreferrer" onClick=${C}>${n}</a>`]}</div>`},uo=12,Nn=e=>e!==void 0&&e.length>uo,j=e=>Nn(e)?e:void 0,B=e=>Nn(e)?" long":"",S=(e,t)=>()=>O(e,t),C=e=>e.stopPropagation(),fo=e=>e.sessionId?(e.harness==="codex"?"codex resume ":"claude --resume ")+e.sessionId:void 0;function Y(e){if(e.requested)return i`<span class="dim">Requested \u2014 no window yet</span>`;if(e.starting)return i`<span class="dim">${e.starting.failedAt?"Did not start":"Starting \\u2014 no window yet"}</span>`;if(!e.live){let n=fo(e);return n?i`<span class="dim">Resume: <code onClick=${C}>${n}</code></span>`:i`<span class="dim">Resume command unavailable</span>`}let t=g();return!t.snapshot?.focusSupported&&!e.link?i`<span class="dim">Window focus is not supported here</span>`:i`<span class="winaction"><button class="btn open" title="open this trap\'s window" onClick=${n=>{C(n),gn(e.trapId)}}>\u2197 open</button>${e.link&&[" ",i`<a href=${e.link} onClick=${C}>Session link</a>`]}${t.focusResults[e.trapId]&&[" ",i`<span class="dim">${t.focusResults[e.trapId]}</span>`]}</span>`}function M(e,t,s){return t.length?i`<div class="wrap"><table><tbody><tr>${e.map(n=>i`<th>${n}</th>`)}</tr>${t}</tbody></table></div>`:i`<div class="empty">${s}</div>`}function pe({kind:e,more:t}){return!t.left&&!t.error?null:i`<div class="older">${t.left>0&&i`<button class="btn" disabled=${t.loading} onClick=${()=>{dn(e)}}>${t.loading?"loading\\u2026":`show ${Math.min(t.left,50)} older (of ${t.left})`}</button>`}${t.error&&i` <span class="bad">${t.error}</span>`}</div>`}function mo({text:e}){let[t,s]=z(null),n=r=>async()=>{await yn(e)&&(s(r),setTimeout(()=>s(null),1e3))};return i`<div class="cmd"><code class=${t==="code"?"copied":void 0} title="click to copy" onClick=${n("code")}>${e}</code><button title="copy" onClick=${n("button")}>${t==="button"?"\\u2713":"\\u29C9"}</button></div>`}var T=(e,t)=>i`<${mo} key=${t} text=${e} />`,Ge=(e,t,s="thumb")=>i`<button\n type="button"\n class=${s}\n title=${"view "+t}\n onClick=${n=>{C(n),hn(e,t)}}\n >\n <img src=${e} alt=${t} loading="lazy" />\n </button>`,ce=(e,t)=>e.map(s=>[i`<div class="sub">${s.name} \xB7 ${s.type} \xB7 ${s.bytes} bytes</div>`,t&&Hs(s.name)&&Ge(t(s.name),s.name),T(s.path)]),jn=/\\bwt:([a-z0-9][a-z0-9-]*)/g,Vt=e=>g().snapshot?.trapNames?.[e],U=e=>e.replace(jn,(t,s,n)=>{let r=Vt(s);return r&&!Bn(e,n,t.length,r)?r:t}),Bn=(e,t,s,n)=>e.slice(Math.max(0,t-n.length-2),t)===`${n} (`&&e[t+s]===")";function at(e){let t="wt:"+e,s=Vt(e)??t;return g().snapshot?.traps.some(r=>r.trapId===e)?i`<a class="trapname" href="#traps" title=${t} onClick=${r=>{r.preventDefault(),C(r),O("trap",e)}}>${s}</a>`:i`<span class="trapname" title=${t}>${s}</span>`}var st=e=>{let t=/^wt:([a-z0-9][a-z0-9-]*)$/.exec(e);return t?at(t[1]):e};function R(e,t=1/0){let s=[],n=0;for(let l of e.matchAll(jn)){l.index>n&&s.push({text:e.slice(n,l.index)});let u=Vt(l[1]);s.push(u&&Bn(e,l.index,l[0].length,u)?{text:l[0]}:{text:u??l[0],trapId:l[1]}),n=l.index+l[0].length}n<e.length&&s.push({text:e.slice(n)});let r=s.reduce((l,u)=>l+u.text.length,0),o=r>t?t-1:1/0,a=[];for(let l of s){if(o<=0)break;l.text.length<=o?a.push(l.trapId?at(l.trapId):l.text):a.push(l.text.slice(0,o)),o-=l.text.length}return r>t&&a.push("\\u2026"),a}var ho=e=>e.log.flatMap((t,s)=>[s?`\n`:"",t.at+" "+t.verb,...t.note?[" ",...R(t.note)]:[]]),go=e=>"brief"in e;function Un(e,t){let s=[e.elapsed&&`elapsed: ${e.elapsed}`,e.attempt&&`attempt: ${e.attempt}`,e.branch&&`branch: ${e.branch}`,e.lastCommit&&`last commit: ${e.lastCommit}`,e.aheadTrunk&&`commits: ${e.aheadTrunk}`,e.draftPr&&`draft PR: ${e.draftPr}`,e.updated&&`updated: ${e.updated}`].filter(Boolean).join(`\n`),n=i`${e.waiting&&[i`<div class="sec">waiting</div>`,ot(e)]}${e.activity&&[i`<div class="sec">activity</div>`,rt(e)]}${s&&[i`<div class="sec">progress</div>`,i`<pre>${s}</pre>`]}`,r=e.evidence&&{...e.evidence};return r?.worker&&(delete r.worker,delete r.harness),go(e)?i`${n}<div class="sec">brief</div><pre>${e.brief}</pre>${e.attachments.length>0&&[i`<div class="sec">attachments (${e.attachments.length})</div>`,ce(e.attachments,o=>Tt(e.lane,e.id,o))]}${e.messageAttachments.length>0&&[i`<div class="sec">message attachments (${e.messageAttachments.length})</div>`,ce(e.messageAttachments,o=>Tt(e.lane,e.id,o))]}${e.followUp&&[i`<div class="sec">forks</div>`,i`<pre>${e.followUp}</pre>`]}<div class="sec">log</div><div class="loglines">${e.log.length?ho(e):"no entries yet"}</div>${e.inbox.length>0&&[i`<div class="sec">inbox</div>`,i`<div class="loglines">${e.inbox.join(`\n---\n`)}</div>`]}${e.awaitingReply&&[i`<div class="sec">awaiting reply</div>`,i`<div>${e.awaitingReply.line} \xB7 from ${e.awaitingReply.from} \xB7 ${v(e.awaitingReply.sentAt)} ago</div>`]}${r&&[i`<div class="sec">evidence</div>`,i`<div class="loglines">${JSON.stringify(r)}</div>`]}`:i`${n}<div class="sec">brief</div><div class=${t?.error?"bad":"dim"}>${t?.error??"loading\\u2026"}</div>${e.followUp&&[i`<div class="sec">forks</div>`,i`<pre>${e.followUp}</pre>`]}${e.note&&[i`<div class="sec">last note</div>`,i`<div class="loglines">${R(e.note)}</div>`]}`}function xe(e){return e.for?[st(e.for),e.evidence&&e.evidence.deliveredTo?[" ",i`<span class="ok">\u2713delivered</span>`]:[" ",i`<span class="warn">waiting</span>`]]:e.claimedBy?st(e.claimedBy):""}function ue(e){if(e.prList&&e.prList.length>1)return[e.prList.map((r,o)=>[o?" \\u2192 ":"",i`<a href=${r.url} target="_blank" rel="noopener" onClick=${C}>#${r.number}</a>`,r.badge&&[" ",i`<span class=${"badge "+E(r.badge)}>${r.badge.text}</span>`]]),e.prGate&&[" ",i`<span class="badge dim" title="merge gate (pick)">${e.prGate}</span>`]];let t=e.evidence&&(e.evidence.prUrl||e.evidence.pr&&e.evidence.pr.url);if(!t)return"";let s=e.prBadge,n=e.evidence&&e.evidence.prUrls&&e.evidence.prUrls.length>1?e.evidence.prUrls:void 0;return[n?n.map((r,o)=>[o?" ":"",i`<a href=${r} target="_blank" rel="noopener" onClick=${C}>#${r.split("/").pop()}</a>`]):i`<a href=${t} target="_blank" rel="noopener" onClick=${C}>PR</a>`,s&&[" ",i`<span class=${"badge "+E(s)} title=${"observed "+s.observedAt}>${s.text}</span>`],e.prGate&&[" ",i`<span class="badge dim" title="merge gate (pick)">${e.prGate}</span>`]]}var $o={"pr:draft":"draft","pr:review":"review","pr:checks":"checks","pr:conflict":"conflicts","pr:ready":"ready","stack-ready":"stack ready",landed:"landed",watch:"watch"},zt={"pr:review":"bad","pr:checks":"bad","pr:conflict":"pr-conflicts","pr:ready":"ok","stack-ready":"ok","pr:draft":"dim",watch:"warn"},Fn=e=>$o[e]||"";var it=e=>i`<a href=${e.url} target="_blank" rel="noopener" onClick=${C}>#${e.number}</a>`,lt=e=>i`<span class="prname" title=${e.title||void 0}><b>#${e.number}</b>${e.title&&[" ",e.title]}</span>`;function fe(e,t,s){return e.flatMap((n,r)=>{let o=t.get(n)||void 0,a=n===s?i`<b title=${o}>#${n}</b>`:i`<span title=${o}>#${n}</span>`;return r?[" \\u2192 ",a]:[a]})}function Ce(e){let t=e.checks;return t.passed+"/"+t.total+" passed"+(t.failed?" \\xB7 "+t.failed+" failed":"")+(t.pending?" \\xB7 "+t.pending+" pending":"")}function dt(e){let t=e.review||{};return(e.reviewDecision||"")+(t.unresolvedThreads?" \\xB7 "+t.unresolvedThreads+" unresolved":"")+(t.changesRequested?" \\xB7 changes requested":"")}function Te(e){return[e.mergeStateStatus,e.nextMergeable?[" \\xB7 ",i`<span class="ok">next mergeable</span>`]:e.blockedBy?" \\xB7 blocked by #"+e.blockedBy:""]}function Jt(e){let t=Ys(e);return e?[t.text," \\xB7 ",t.at?[v(t.at)," ago"]:"never checked"]:i`<span class="dim">${t.text}</span>`}function Ae(e){if(e.requested)return{stale:!1,listen:[i`<span class="dot"></span>`,i`<span class="dim">requested</span>`],hb:i`<span class="dim">\u2014</span>`};if(e.starting)return{stale:!1,listen:e.starting.failedAt?[i`<span class="dot bad"></span>`,i`<span class="bad">start failed</span>`]:[i`<span class="dot warn"></span>`,"starting"],hb:i`<span class="dim">\u2014</span>`};if(!e.live)return{stale:!1,listen:[i`<span class="dot"></span>`,i`<span class="dim">signed off</span>`],hb:i`<span class="dim">\u2014</span>`};let t=Date.now()-Date.parse(e.heartbeatAt)>18e5,s=e.listening??(!!e.firstParkedAt&&!t);return{stale:t,listen:s?[i`<span class="dot ok"></span>`,"listening"]:[i`<span class="dot warn"></span>`,"not listening"],hb:i`<span class=${t?"bad":"ok"}>${t&&"stale "}${v(e.heartbeatAt)} ago</span>`}}function Yt(e){if(e.requested)return{kind:"requested",helmOn:!!g().snapshot?.helmOn};if(e.starting)return e.starting.failedAt?{kind:"start failed",reason:e.starting.reason??"no session signed on in time"}:{kind:"starting",deadline:e.starting.deadline};if(!e.live)return{kind:"signed off"};let t=e.claimed?e.catches.find(s=>s.id===e.claimed&&s.bucket==="active"):void 0;return t?t.waiting||t.verb==="paused"?{kind:"parked",current:t}:{kind:"working",current:t}:{kind:"idle",listening:e.listening??(!!e.firstParkedAt&&Date.now()-Date.parse(e.heartbeatAt??"")<=18e5)}}function vo(e){let t=Yt(e);return t.kind==="working"||t.kind==="idle"&&t.listening?"ok":t.kind==="start failed"?"bad":t.kind==="parked"||t.kind==="starting"?"warn":"dim"}var Wn=e=>e.waiting?`waiting on ${e.waiting.on}`:e.note||"waiting",qn=e=>e.title.slice(0,40)||"(no title)";function Re(e){let t=Yt(e),s=i`<span class=${"dot trapdot "+vo(e)}></span>`;if(t.kind==="requested")return[s,i`<span class="dim">${Kn(e,t.helmOn)}</span>`];if(t.kind==="starting")return[s,"starting \\xB7 waiting for its session to sign on"];if(t.kind==="start failed")return[s,i`<span class="bad">start failed</span>`," \\xB7 ",t.reason];if(t.kind==="signed off")return[s,i`<span class="dim">signed off</span>`];if(t.kind==="idle")return[s,"idle \\xB7 ",t.listening?"listening":"not listening"];let n=t.current;return t.kind==="parked"?[s,"parked \\xB7 ",Wn(n)]:[s,"working \\xB7 ",i`<a href="#dispatches" onClick=${o=>{o.preventDefault(),C(o),O("dispatch",`${n.lane}:${n.id}`)}}>${n.id.slice(0,8)}</a>`," \\xB7 ",qn(n),n.activity&&[" \\xB7 ",n.activity.summary," ",v(n.activity.at)," ago"]]}var Kn=(e,t)=>`requested \\xB7 ${e.repo??""} \\xB7 ${e.harness??""} \\xB7 ${t?"waiting for the helm":"waiting for a helm"}`;function Q(e){let t=e.starting?.commands;return t?.length?i`<div class="startcmds" onClick=${C}><div class="dim">start it: run one in a terminal</div>${t.map(s=>T(s.command,s.harness))}</div>`:null}function ct(){let t=g().snapshot?.repoKeys??[],[s,n]=z(!1),[r,o]=z(""),[a,l]=z("claude"),[u,c]=z(null),d=r||t[0]||"",h=async p=>{p.preventDefault();let m=await $n(d,a);c(m??null),m||n(!1)};return s?i`<form class="newtrap" onClick=${C} onSubmit=${h}>\n <label>repo <select name="repo" value=${d} onChange=${p=>o(p.target.value)}>${t.map(p=>i`<option value=${p}>${p}</option>`)}</select></label>\n <label>harness <select name="harness" value=${a} onChange=${p=>l(p.target.value)}><option value="claude">claude</option><option value="codex">codex</option></select></label>\n <button class="btn" type="submit" disabled=${!d}>Request</button>\n <button class="btn" type="button" onClick=${()=>n(!1)}>Cancel</button>\n ${u&&i`<span class="bad">${u}</span>`}\n </form>`:i`<span class="newtrap"><button class="btn" onClick=${p=>(C(p),n(!0),c(null))}>+ New trap</button></span>`}function pt(e){let t=Yt(e);if(t.kind==="requested")return Kn(e,t.helmOn);if(t.kind==="starting")return`starting \\xB7 sign-on due by ${t.deadline}`;if(t.kind==="start failed")return`start failed \\xB7 ${t.reason}`;if(t.kind==="signed off")return"signed off";if(t.kind==="idle")return`idle \\xB7 ${t.listening?"listening":"not listening"}`;if(t.kind==="parked")return`parked \\xB7 ${Wn(t.current)}`;let s=t.current;return[`working \\xB7 ${s.id.slice(0,8)} \\xB7 ${qn(s)}`,s.activity&&`${s.activity.summary} ${J(s.activity.at)} ago`].filter(Boolean).join(" \\xB7 ")}function Qt(e){let t=e.messages.filter(s=>s.state==="pending").length;return e.messages.length?["\\u2709 "+e.messages.length,t>0&&[" ",i`<span class="warn">(${t} pending)</span>`]]:null}var ko=e=>()=>{window.open(x(e.key),"_blank","noopener")};function Xt(e){let t=ve(e);return{nodes:[t&&t+" \\xB7 ",v(e.filedAt)," ago",e.acked?" \\xB7 acked":""],text:[t,`${J(e.filedAt)} ago`,e.acked?"acked":""].filter(Boolean).join(" \\xB7 ")}}function bo(e){let t=Xt(e);return i`<a key=${e.key} class=${"card"+(e.acked?" acked":"")} href=${x(e.key)} target="_blank" rel="noopener"><div class="top"><b title=${e.title}>${e.title}</b></div><div class="meta" title=${t.text}>${t.nodes}</div></a>`}function yo(e){return i`<tr key=${e.key} class=${"rowhead"+(e.acked?" acked":"")} onClick=${ko(e)}><td class="grow"><a href=${x(e.key)} target="_blank" rel="noopener" onClick=${t=>t.stopPropagation()}>${e.title}</a></td><td>${ve(e)}</td><td>${v(e.filedAt)}</td><td>${e.acked?"acked":""}</td></tr>`}function Vn({inp:e}){return e.view==="cards"?e.list.length?i`<div class="cards">${e.list.map(bo)}</div>`:i`<div class="empty">no reports match</div>`:M(["title","from","filed","acked"],e.list.map(yo),"no reports match")}function Yn(e){let t=e.trim();return/^(https?:\\/\\/|mailto:|#)/i.test(t)?t:void 0}function Qn(e){let t=e.trim();return/^[^/\\\\:?#]+$/.test(t)&&t!=="."&&t!==".."?t:void 0}function zn(e,t,s,n){let r=0;for(let o=t;o<e.length;o++){if(e[o]==="\\\\"){o++;continue}if(e[o]===s)r++;else if(e[o]===n){if(r===0)return o;r--}}return-1}function Jn(e,t){let s=zn(e,t+1,"[","]");if(s<0||e[s+1]!=="(")return;let n=zn(e,s+2,"(",")");if(n<0)return;let r=e.slice(s+2,n).trim().replace(/\\s+"[^"]*"$/,"").replace(/^<(.*)>$/,"$1");return{text:e.slice(t+1,s),target:r,end:n+1}}function W(e){let t=[],s="",n=()=>{s&&t.push({t:"text",v:s}),s=""};for(let r=0;r<e.length;){let o=e[r];if(o==="\\\\"&&r+1<e.length&&/[\\\\`*_{}[\\]()#+\\-.!|<>~]/.test(e[r+1])){s+=e[r+1],r+=2;continue}if(o==="`"){let a=/^`+/.exec(e.slice(r))[0],l=e.indexOf(a,r+a.length);if(l>0){n(),t.push({t:"code",v:e.slice(r+a.length,l).replace(/^ (.*) $/,"$1")}),r=l+a.length;continue}}if(o==="!"&&e[r+1]==="["){let a=Jn(e,r+1);if(a){n(),t.push({t:"img",alt:a.text,src:a.target}),r=a.end;continue}}if(o==="["){let a=Jn(e,r);if(a){n(),t.push({t:"a",href:a.target,c:W(a.text)}),r=a.end;continue}}if((o==="*"||o==="_")&&e[r+1]===o){let a=e.indexOf(o+o,r+2);if(a>r+2){n(),t.push({t:"b",c:W(e.slice(r+2,a))}),r=a+2;continue}}if(o==="*"||o==="_"&&(r===0||!/\\w/.test(e[r-1]))){let a=r+1;for(;(a=e.indexOf(o,a))>0&&(e[a+1]===o||o==="_"&&/\\w/.test(e[a+1]??""));)a+=2;if(a>r+1&&e[r+1]!==" "){n(),t.push({t:"i",c:W(e.slice(r+1,a))}),r=a+1;continue}}if(o===`\n`){/ {2,}$/.test(s)||s.endsWith("\\\\")?(s=s.replace(/( {2,}|\\\\)$/,""),n(),t.push({t:"br"})):s+=" ",r++;continue}s+=o,r++}return n(),t}var Xn=/^ {0,3}(`{3,}|~{3,})\\s*([^`\\s]*)/,Zn=/^ {0,3}(#{1,6})\\s+(.*?)\\s*#*\\s*$/,er=/^ {0,3}([-*_])(\\s*\\1){2,}\\s*$/,ts=/^( {0,3})([-*+])\\s+(.*)$/,ss=/^( {0,3})(\\d{1,9})[.)]\\s+(.*)$/,ut=/^ {0,3}>\\s?(.*)$/,_o=/^\\s*\\|?\\s*:?-+:?\\s*(\\|\\s*:?-+:?\\s*)*\\|?\\s*$/;function Zt(e){let t=e.trim();t.startsWith("|")&&(t=t.slice(1)),t.endsWith("|")&&!t.endsWith("\\\\|")&&(t=t.slice(0,-1));let s=[],n="";for(let r=0;r<t.length;r++)t[r]==="\\\\"&&t[r+1]==="|"?(n+="|",r++):t[r]==="|"?(s.push(n.trim()),n=""):n+=t[r];return s.push(n.trim()),s}var es=e=>Xn.test(e)||Zn.test(e)||er.test(e)||ts.test(e)||ss.test(e)||ut.test(e);function ft(e){let t=e.replace(/\\r\\n?/g,`\n`).replace(/\\t/g," ").split(`\n`),s=[],n=0;for(;n<t.length;){let r=t[n];if(!r.trim()){n++;continue}let o=Xn.exec(r);if(o){let d=o[1],h=[];for(n++;n<t.length&&!new RegExp(`^ {0,3}${d[0]}{${d.length},}\\\\s*$`).test(t[n]);)h.push(t[n++]);n++,s.push({t:"code",lang:o[2]??"",v:h.join(`\n`)});continue}let a=Zn.exec(r);if(a){s.push({t:"h",level:a[1].length,c:W(a[2])}),n++;continue}if(er.test(r)){s.push({t:"hr"}),n++;continue}if(ut.test(r)){let d=[];for(;n<t.length&&t[n].trim()&&(ut.test(t[n])||!es(t[n]));)d.push(ut.exec(t[n])?.[1]??t[n]),n++;s.push({t:"quote",c:ft(d.join(`\n`))});continue}let l=ts.exec(r),u=ss.exec(r);if(l||u){let d=!l,h=d?ss:ts,p=[];for(;n<t.length;){let m=t[n],f=h.exec(m);if(f&&f[1].length<=3){p.push([f[3]]),n++;continue}if(!m.trim()){if(n+1<t.length&&/^ {2,}\\S/.test(t[n+1])){p.at(-1).push(""),n++;continue}break}if(/^ {2,}\\S/.test(m)){p.at(-1).push(m.replace(/^ {2,4}/,"")),n++;continue}if(es(m))break;p.at(-1).push(m.trim()),n++}s.push({t:"list",ordered:d,start:d?Number(u[2]):1,items:p.map(m=>ft(m.join(`\n`)))});continue}if(r.includes("|")&&n+1<t.length&&_o.test(t[n+1])&&t[n+1].includes("-")){let d=Zt(r),h=Zt(t[n+1]).map(m=>m.startsWith(":")&&m.endsWith(":")?"center":m.endsWith(":")?"right":m.startsWith(":")?"left":"");n+=2;let p=[];for(;n<t.length&&t[n].trim()&&t[n].includes("|");){let m=Zt(t[n++]);p.push(d.map((f,$)=>W(m[$]??"")))}s.push({t:"table",align:d.map((m,f)=>h[f]??""),head:d.map(W),rows:p});continue}let c=[];for(;n<t.length&&t[n].trim()&&!es(t[n])&&!(c.length&&/^ {0,3}(=+|-+)\\s*$/.test(t[n]));)c.push(t[n++].replace(/^ +/,""));if(c.length===0){s.push({t:"p",c:W(r.trim())}),n++;continue}s.push({t:"p",c:W(c.join(`\n`))})}return s}function q(e,t){return t.map(s=>{switch(s.t){case"text":return s.v;case"code":return i`<code>${s.v}</code>`;case"b":return i`<strong>${q(e,s.c)}</strong>`;case"i":return i`<em>${q(e,s.c)}</em>`;case"br":return i`<br />`;case"a":{let n=Yn(s.href);return n?i`<a href=${n} target="_blank" rel="noopener noreferrer">${q(e,s.c)}</a>`:q(e,s.c)}case"img":{let n=Qn(s.src);return n?Ge(e(n),s.alt||n,"mdimg"):i`<span class="dim">[image not shown: ${s.alt||s.src}]</span>`}}})}function mt(e,t){return t.map(s=>{switch(s.t){case"h":{let n=q(e,s.c);return s.level===1?i`<h1>${n}</h1>`:s.level===2?i`<h2>${n}</h2>`:s.level===3?i`<h3>${n}</h3>`:i`<h4>${n}</h4>`}case"p":return i`<p>${q(e,s.c)}</p>`;case"code":return i`<pre class="mdcode"><code>${s.v}</code></pre>`;case"hr":return i`<hr />`;case"quote":return i`<blockquote>${mt(e,s.c)}</blockquote>`;case"list":{let n=s.items.map(r=>i`<li>${mt(e,r)}</li>`);return s.ordered?i`<ol start=${s.start}>${n}</ol>`:i`<ul>${n}</ul>`}case"table":return i`<div class="mdtable"><table><thead><tr>${s.head.map((n,r)=>i`<th style=${s.align[r]?"text-align:"+s.align[r]:void 0}>${q(e,n)}</th>`)}</tr></thead><tbody>${s.rows.map(n=>i`<tr>${n.map((r,o)=>i`<td style=${s.align[o]?"text-align:"+s.align[o]:void 0}>${q(e,r)}</td>`)}</tr>`)}</tbody></table></div>`}})}var Me=new Map;function tr(e){let t=Me.get(e);return t||(t=ft(e),Me.set(e,t),Me.size>8&&Me.delete(Me.keys().next().value)),t}function wo({r:e,text:t}){let s=t?.text!==void 0?mt(n=>Rt(e.key,n),tr(t.text)):t?.error?i`<div class="bad">${t.error}</div>`:i`<div class="dim">loading\u2026</div>`;return i`<div class="mdpage" data-report=${e.key}>${s}</div>`}function sr({text:e,fileUrl:t}){return i`<div class="mdpage">${mt(t,tr(e))}</div>`}function ns(e){let t=ve(e);return[t&&t+" \\xB7 ",v(e.filedAt)," ago",e.acked&&[" \\xB7 ",i`<span class="ok">acked</span>`]]}function nr({r:e,text:t,heading:s}){return[s&&i`<div class="sec">report \xB7 ${e.title}</div>`,i`<div class="sub">${ns(e)}</div>`,i`<${wo} r=${e} text=${t} />`,e.attachments.length>0&&[i`<div class="sec">report attachments (${e.attachments.length})</div>`,ce(e.attachments,n=>Rt(e.key,n))],!e.acked&&[i`<div class="sec">ack</div>`,T("lobstah attention ack "+e.key)]]}var So=/\\.(png|jpe?g|gif|webp)$/i,Do=e=>{e.style.height="auto",e.style.height=e.scrollHeight+"px"};function Go(e,t){return t.length?i`<div class="dfiles">${t.map(s=>So.test(s.name)?i`<span key=${s.name}>${Ge(At(e,s.name),s.name,"dimg")}</span>`:i`<div key=${s.name} class="dfile"><span>${s.name}</span> <span class="dim">\xB7 ${s.type} \xB7 ${s.bytes} bytes \xB7 </span><code>${s.path}</code></div>`)}</div>`:null}function xo(e,t,s){let n=e.key,r=e.kind==="decision"?e.options:[],o=t?.text??"",a=t?.files??[],l=!!t?.sending,u=d=>d+1<=we?String(d+1):void 0,c=u(r.length);return[r.length>0&&i`<div class="doptions">${r.map((d,h)=>{let p=u(h);return i`<button key=${d} type="button" class=${"btn dopt"+(t?.option===d?" on":"")} aria-pressed=${t?.option===d?"true":"false"} aria-keyshortcuts=${p} disabled=${l} onClick=${()=>pn(n,d)}>${p&&i`<kbd class="dkey" aria-hidden="true">${p}</kbd>`}<span class="dlabel">${d}</span></button>`})}</div>`,i`<div class="danswer-wrap">${c&&i`<kbd class="dkey dkey-text" aria-hidden="true">${c}</kbd>`}<textarea\n class="danswer"\n rows="2"\n aria-keyshortcuts=${c}\n aria-label=${r.length?"Your answer, in your own words":"Your answer"}\n placeholder=${r.length?"Add to your answer, or answer in your own words":"Your answer"}\n value=${o}\n disabled=${l}\n onInput=${d=>{let h=d.currentTarget;Do(h),A(n,{text:h.value,error:void 0})}}\n onPaste=${d=>{un(n,d)}}\n ></textarea></div>`,i`<div class="dfoot">\n <label class="btn dattach" title="attach images or files">attach<input type="file" multiple accept=${s} disabled=${l} onChange=${d=>{let h=d.currentTarget;Ht(n,h.files).then(()=>{h.value=""})}} /></label>\n ${a.map((d,h)=>i`<span key=${d.name+h} class="dchip">${d.name}<button type="button" class="dx" title="remove" disabled=${l} onClick=${()=>fn(n,h)}>\xD7</button></span>`)}\n ${t?.error&&i`<span class="bad derr">${t.error}</span>`}\n <button type="button" class="btn dsend" disabled=${l} onClick=${()=>{Ze(n)}}>${l?"Sending\\u2026":"Send"}</button>\n </div>`]}var rr=e=>e.kind==="decision"?e.title:R(e.note);function Co(e,t,s){return t?.sent?"answered":e.viewedAt||s?"read":"unread"}function rs(e){let t=e.dispatch,s=e.lane??"work";return[t&&i`<a class="dlink" href="#" onClick=${r=>{r.preventDefault(),r.stopPropagation(),S("dispatch",s+":"+t)()}}>${t.slice(0,8)}</a>`,e.repo].filter(Boolean).flatMap((r,o)=>o?[" \\xB7 ",r]:[r])}function To(e,t,s,n){let r=Co(e,t,s),o=()=>ae(e.key);return i`<li key=${e.key} class=${"drow "+r+(n?" focus":"")} data-decision=${e.key}>\n <button type="button" class="dopen" onClick=${o} aria-label=${`${r}: ${e.kind==="decision"?e.title:e.note}`}>\n ${e.kind==="question"&&i`<span class=${"badge "+(e.verb==="blocked"?"bad":"warn")}>${e.verb}</span>`}\n <b class="dtitle">${rr(e)}</b>\n </button>\n <span class="dmeta dim">${rs(e)}</span>\n <span class="dage dim">${v(e.at)} ago</span>\n <span class=${"dstate "+r}>${r==="answered"?`answered \\xB7 ${t.sent}`:r}</span>\n </li>`}function or({cards:e,drafts:t,focus:s,viewedHere:n,unread:r}){let o=e.length?i`<ul class="dlist">${e.map(a=>To(a,t[a.key],!!n[a.key],a.key===s))}</ul>`:i`<div class="empty">none</div>`;return i`<section class="decisions"><h2>decisions${r>0&&i`<span class="dbadge" aria-label=${`${r} new`}>${r}</span>`}</h2>${o}</section>`}function Ao(e){let t=Math.min(e,we),s=e+1<=we?String(e+1):void 0,n=[t>0&&[i`<kbd>1</kbd>`,t>1&&["\\u2013",i`<kbd>${t}</kbd>`]," choose"],s&&[i`<kbd>${s}</kbd>`," write"],t>0&&[i`<kbd>Enter</kbd>`," on a chosen option sends"],[i`<kbd>\u2190</kbd>`," ",i`<kbd>\u2192</kbd>`," previous / next"],[i`<kbd>Esc</kbd>`," leaves the text box, then closes"],[i`<kbd>d</kbd>`," opens decisions anywhere"]].filter(Boolean);return i`<div class="dlegend dim">${n.flatMap((r,o)=>o?[" \\xB7 ",r]:[r])}</div>`}var Ro=\'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])\';function ar({open:e,order:t,drafts:s,extensions:n}){let r=Ft(null),o=Ft(null),a=!!e;Ut(()=>{if(a)return o.current=document.activeElement,r.current?.focus(),()=>{let m=o.current;m&&typeof m.focus=="function"&&document.contains(m)&&m.focus()}},[a]);let l=e?.key??null;if(Ut(()=>{l&&on(l)},[l]),!e)return null;let u=m=>{if(m.key!=="Tab"||!r.current)return;let f=[...r.current.querySelectorAll(Ro)];if(!f.length)return void m.preventDefault();let $=f[0],_=f[f.length-1];m.shiftKey&&(document.activeElement===$||document.activeElement===r.current)?(m.preventDefault(),_.focus()):!m.shiftKey&&document.activeElement===_&&(m.preventDefault(),$.focus())},c=l?t.find(m=>m.key===l):void 0,d=c?t.indexOf(c):-1,h=(m,f,$="")=>i`<div id="doverlay" class="open" onClick=${_=>_.target===_.currentTarget&&ie()}>\n <div class="modal dmodal" role="dialog" aria-modal="true" aria-labelledby="dmodal-title" tabindex="-1" ref=${r} onKeyDown=${u} data-decision=${$}>\n <button type="button" class="x" aria-label="close" aria-keyshortcuts="Escape" onClick=${ie}>\xD7</button>\n <h3 id="dmodal-title" class="sr-only">${m}</h3>\n ${f}\n </div>\n </div>`;if(!c)return h("All decisions answered",i`<div class="ddone" onClick=${ie}><div class="ok">\u2713 All decisions answered.</div><div class="dim">Click anywhere or press Escape to close.</div></div>`);let p=s[c.key];return h(c.kind==="decision"?c.title:c.note,i`<div class="dnav">\n <button type="button" class="btn dprev" aria-label="previous decision" aria-keyshortcuts="ArrowLeft" disabled=${d<=0} onClick=${()=>_e(-1)}>\u2039 prev</button>\n <span class="dpos" aria-live="polite">${d+1} of ${t.length}</span>\n <button type="button" class="btn dnext" aria-label="next decision" aria-keyshortcuts="ArrowRight" disabled=${d>=t.length-1} onClick=${()=>_e(1)}>next \u203A</button>\n </div>\n <div class="dtop">\n ${c.kind==="question"&&i`<span class=${"badge "+(c.verb==="blocked"?"bad":"warn")}>${c.verb}</span>`}\n <b class="dtitle">${rr(c)}</b>\n <span class="dmeta dim">${rs(c)}${rs(c).length?" \\xB7 ":""}${v(c.at)} ago</span>\n </div>\n ${c.kind==="decision"&&c.detail.trim()&&i`<${sr} text=${c.detail} fileUrl=${m=>At(c.key,m)} />`}\n ${c.kind==="decision"&&Go(c.key,c.attachments)}\n ${p?.sent?i`<div class="danswered ok">answered \xB7 ${p.sent}</div>`:xo(c,p,n.join(","))}\n ${Ao(c.kind==="decision"?c.options.length:0)}`,c.key)}function ir({unread:e,dismissed:t}){let s=e.some(a=>!t.includes(a.key)),n=e.length>0&&s,r=e.length,o=`${r} new decision${r===1?"":"s"}`;return i`<div class="dalert-live" role="status" aria-live="polite">${n&&i`<div class="dalert">\n <button type="button" class="dalert-open" aria-keyshortcuts="d" onClick=${()=>ae()}><span class="dcount" aria-hidden="true">${r}</span> <span>${o}</span></button>\n <button type="button" class="dx" aria-label="dismiss" title="dismiss" onClick=${an}>\xD7</button>\n </div>`}</div>`}function Eo(e,t){let s=e.badge&&(e.badge.worker?i`<${N} worker=${e.badge.worker} label=${e.badge.text} cls=${"badge "+(e.badge.tone||"dim")} />`:i`<span class=${"badge "+(e.badge.tone||"dim")+B(e.badge.text)} title=${j(e.badge.text)}>${e.badge.text}</span>`),n=e.worker&&i`<${N} worker=${e.worker} />`;if(e.href){let r=(t==="cards"?"card":"deckline click")+(e.acked?" acked":"");return t==="cards"?i`<a key=${e.key} class=${r} href=${e.href} target="_blank" rel="noopener"><div class="top"><b>${e.title}</b>${s}</div>${e.meta&&i`<div class="meta" title=${e.metaText}>${e.meta}</div>`}</a>`:i`<a key=${e.key} class=${r} href=${e.href} target="_blank" rel="noopener">${s&&[s," "]}<b>${e.title}</b>${e.meta&&[" ",i`<span class="dim">\xB7 ${e.meta}</span>`]}</a>`}return t==="cards"?i`<div key=${e.key} class=${"card"+(e.acked?" acked":"")} onClick=${e.open} style=${e.open?void 0:"cursor:default"}><div class="top"><b>${e.title}</b>${n}${s}</div>${e.meta&&i`<div class="meta" title=${e.metaText}>${e.meta}</div>`}${e.extra}${e.action&&i`<div class="foot"><span class="footact">${e.action}</span></div>`}</div>`:i`<div key=${e.key} class=${"deckline"+(e.open?" click":"")+(e.acked?" acked":"")} onClick=${e.open}>${s&&[s," "]}<b>${e.title}</b>${n&&[" ",n]}${e.meta&&[" ",i`<span class="dim">\xB7 ${e.meta}</span>`]}${e.action&&[" ",i`<span class="dim">\xB7 </span>`,e.action]}${e.extra}</div>`}var lr=(e,t)=>e>0&&i`<a class="deckmore" href=${"#"+t}>+${e} more \u2192</a>`;function ht(e,t,s,n,r,o){let a=t.slice(0,n),l=a.map(c=>Eo(c,r)),u=a.length?r==="cards"?i`<div class="cards">${l}</div>`:l:i`<div class="empty">none</div>`;return i`<section><h2><a href=${"#"+s}>${e} \u2192</a></h2>${o&&i`<div class="tabhead">${o}</div>`}${u}${lr(t.length-a.length,s)}</section>`}function Oo(e,t,s,n){let r=t.find(c=>c.number===e.nextNumber)||t[0],o=fe(e.numbers,new Map(t.map(c=>[c.number,c.title]))),a=r?"next #"+r.number:"nothing mergeable";if(n==="cards"){let c=d=>{let h=s.get(d.key)||[],p=h.length>0&&h.every($=>$.acked),m=h.length?h.map($=>i`<span class=${"badge "+(zt[$.kind]||"dim")}>${Fn($.kind)}</span>`):i`<span class=${"badge "+E(d.badge)+B(d.badge.text)} title=${j(d.badge.text)}>${d.badge.text}</span>`,f=`${d.repo} \\xB7 ${d.badge.text}${p?" \\xB7 acked":""}`;return i`<div key=${d.key} class=${"card"+(p?" acked":"")} onClick=${S("pr",d.key)}><div class="top">${lt(d)}${m}</div><div class="meta" title=${f}>${f}</div></div>`};return i`<div key=${e.id} class="deckstack"><div class="dim">${o} \xB7 ${a}</div>${e.readiness&&i`<div class=${e.readiness.allReady?"ok":"dim"}>${e.readiness.text}</div>`}<div class="cards">${t.map(c)}</div></div>`}let l=t.flatMap(c=>(s.get(c.key)||[]).map(d=>i`<span class=${"badge "+(zt[d.kind]||E(c.badge))+(d.acked?" acked":"")}>#${c.number} ${c.badge.text}</span>`)),u=l.flatMap((c,d)=>d?[" ",c]:[c]);return i`<div key=${e.id} class=${"deckline"+(r?" click":"")} onClick=${r&&S("pr",r.key)}><b>${o}</b> <span class="dim">\xB7 ${a}</span>${l.length>0&&[" \\xB7 ",u]}${e.readiness&&i`<div class=${e.readiness.allReady?"ok":"dim"}>${e.readiness.text}</div>`}</div>`}function Lo(e,t){let s=e.stacks.slice(0,3),n=new Map;for(let o of e.prAttention){let a=n.get(o.key)||[];a.push(o),n.set(o.key,a)}let r=s.length?s.map(o=>Oo(o,e.prs.filter(a=>a.stackId===o.id).sort((a,l)=>a.position-l.position),n,t)):i`<div class="empty">none</div>`;return i`<section><h2><a href="#prs">PRs \u2192</a></h2>${r}${lr(e.stacks.length-s.length,"prs")}</section>`}function dr({inp:e,drafts:t,focus:s,viewedHere:n,unread:r}){let o=e.view,a=e.inflight.map(d=>({key:d.lane+":"+d.id,title:[d.id.slice(0,8)," ",d.repo||""],worker:d.worker,badge:{text:d.verb,tone:d.verb==="needs-decision"||d.verb==="blocked"?"bad":"dim"},meta:[R(d.note||"",90),d.verbAt&&[" \\xB7 ",v(d.verbAt)," ago"],(d.for||d.claimedBy)&&[" \\xB7 ",st(d.for||d.claimedBy)]],metaText:[U(d.note||""),d.verbAt&&`${J(d.verbAt)} ago`,U(d.for||d.claimedBy||"")].filter(Boolean).join(" \\xB7 "),open:S("dispatch",d.lane+":"+d.id)})),l=e.landed.map(d=>({key:d.key,title:[d.id.slice(0,8)," ",d.repo||"",d.unreported&&[" ",i`<span class="badge warn unreported">unreported</span>`]],badge:{text:d.verb,tone:d.verb==="failed"?"bad":"ok"},meta:[R(d.note||"",90)," \\xB7 ",v(d.at)," ago"],metaText:`${U(d.note||"")} \\xB7 ${J(d.at)} ago`,open:S("dispatch",d.lane+":"+d.id)})),u=e.reports.map(d=>{let h=Xt(d);return{key:d.key,title:d.title,meta:h.nodes,metaText:h.text,href:x(d.key),acked:!!d.acked}}),c=e.traps.map(({x:d})=>({key:d.trapId,title:"\\u{1FAA4} "+(d.label??`wt:${d.trapId}`),badge:d.requested?{text:"requested",tone:"dim"}:d.starting?{text:d.starting.failedAt?"start failed":"starting",tone:d.starting.failedAt?"bad":"warn"}:{text:d.live?d.harness||"live":"signed off",tone:d.live?"ok":"dim",worker:d.live?d:void 0},meta:[d.repo||""," \\xB7 ",i`<span class="catchn" title="catches">\u{1F99E} ${te(d.totalCatches??0)}</span>`," \\xB7 ",Re(d)],metaText:`${d.repo||""} \\xB7 \\u{1F99E} ${te(d.totalCatches??0)} \\xB7 ${pt(d)}`,action:Y(d),extra:Q(d),open:S("trap",d.trapId)}));return i`<div class="deckgrid"><${or} cards=${e.decisions} drafts=${t} focus=${s} viewedHere=${n} unread=${r} />${ht("in flight",a,"dispatches",4,o)}${ht("Landed \\xB7 24h",l,"dispatches",8,o)}${ht("reports",u,"reports",8,o)}${ht("traps",c,"traps",8,o,i`<${ct} />`)}${Lo(e,o)}</div>`}function Ho(e,t){if(!t)return e;let s=new Map(e.map(o=>[o.id,o])),n=o=>{let a=o,l=new Set;for(;a.followUp&&s.has(a.followUp)&&!l.has(a.id);)l.add(a.id),a=s.get(a.followUp);return a.id},r=new Map;for(let o of e){let a=n(o),l=r.get(a)||[];l.push(o),r.set(a,l)}return[...r.values()].sort((o,a)=>a[0].sort-o[0].sort).flatMap(o=>o.sort((a,l)=>a.id===n(a)?-1:l.id===n(l)?1:a.sort-l.sort))}function No(e,t){let s=e.lane+":"+e.id;return i`<tr key=${s} class="rowhead" onClick=${S("dispatch",s)}><td>${t&&e.followUp&&i`<span class="dim">\u21B3 </span>`}${e.id.slice(0,8)}</td><td>${e.lane} / ${e.bucket}</td><td>${e.repo}</td><td class=${"v-"+e.verb}>${e.verb}</td><td class="grow">${R(e.note??"",90)}${ot(e)}${Kt(e)}${rt(e)}</td><td>${v(e.verbAt)}</td><td>${xe(e)}</td><td>${ue(e)}</td></tr>`}function jo(e){let t=e.lane+":"+e.id;return i`<div key=${t} class="card" onClick=${S("dispatch",t)}><div class="top"><b>${e.id.slice(0,8)}</b>${Kt(e)}<span class=${"badge v-"+e.verb+B(e.verb)} title=${j(e.verb)}>${e.verb}</span></div><div class="meta" title=${`${e.repo} \\xB7 ${e.lane} ${e.bucket}`}>${e.repo} \xB7 ${e.lane} ${e.bucket}${e.verbAt&&[" \\xB7 ",v(e.verbAt)]}</div>${e.note&&i`<div class="note" title=${U(e.note)}>${R(e.note)}</div>`}${ot(e)}${rt(e)}<div class="foot">${xe(e)} ${ue(e)}</div></div>`}function cr({inp:e,more:t}){let s=e.list,n=t&&i`<${pe} kind="dispatches" more=${t} />`;return e.view==="cards"?[s.length?i`<div class="cards">${s.map(jo)}</div>`:i`<div class="empty">no dispatches match</div>`,n]:[M(["id","lane / bucket","repo","verb","note","age","addressed","pr"],Ho(s,e.chain).map(r=>No(r,e.chain)),"no dispatches match"),n]}var Bo={deck:"On deck",dispatches:"Dispatches",traps:"Traps",prs:"PRs",reports:"Reports",notices:"Notices",stats:"Stats"},Uo=["working","needs-decision","blocked","paused","done","failed","unknown"],pr=(e,t)=>i`<span role="img" aria-label=${t} title=${t}>${e}</span>`;function Fo(e,t){let s=t.daemonStale,n=e.daemon?[i`<span class=${s?"bad":"ok"}>${s&&"stale "}${v(e.daemon.heartbeat)} ago</span>`," ",i`<span class="dim">v${e.daemon.version}</span>`]:i`<span class="bad">down</span>`;return[i`<span class="chip">daemon ${n}</span>`,t.stats&&i`<span class="chip" title="catches today">\u{1F99E} ${t.stats.catchesToday} today</span>`,e.slots&&i`<span class="chip">active ${pr("\\u{1F3A3}","headless")} ${e.slots.headless} of ${e.slots.limit}; ${pr("\\u{1FAA4}","traps")} ${e.slots.traps}${e.slots.parked?`; parked: ${e.slots.parked} (no slot)`:""}</span>`,t.helms.map(({x:r,stale:o})=>i`<span key=${r.grounds} class="chip click" onClick=${S("helm",r.grounds)}>\u26F5 <${N} worker=${r} label=${i`<b>${r.man}</b>`} cls="" /> <span class="dim">helm ${r.grounds}</span> <span class=${o?"warn":"ok"}>${o&&"stale "}${v(r.heartbeatAt)} ago</span></span>`)]}var os=[];function Wo(e){let t=[...new Set([...e.dispatches.map(s=>s.repo),...e.notices.map(s=>s.repo),...e.prs.map(s=>s.repo)].filter(Boolean))].sort();return t.length!==os.length&&(os=t),os}var as=[];function qo(e){let t=[...new Set(e.notices.map(s=>s.kind))].sort();return t.length!==as.length&&(as=t),as}var Pe=e=>e.currentTarget.value;function Ko(e,t,s){let n=a=>t===a?"":"display:none",r=e?Wo(e):[],o=e?qo(e):[];return i`<div class="controls" style=${t==="stats"?"display:none":void 0}>\n <select id="f-lane" style=${e&&n("dispatches")} value=${s.lane} onChange=${a=>H({lane:Pe(a)})}>\n <option value="">all lanes</option>\n <option value="work">work</option>\n <option value="chore">chore</option>\n </select>\n <select id="f-repo" value=${s.repo} onChange=${a=>H({repo:Pe(a)})}>\n <option value="">all repos</option>\n ${r.map(a=>i`<option key=${a}>${a}</option>`)}\n </select>\n <select id="f-verb" style=${e&&n("dispatches")} value=${s.verb} onChange=${a=>H({verb:Pe(a)})}>\n <option value="">all verbs</option>\n ${Uo.map(a=>i`<option key=${a}>${a}</option>`)}\n </select>\n <label id="chain-control" style=${e&&n("dispatches")}><input id="f-chain" type="checkbox" checked=${!!s.chain} onChange=${a=>H({chain:a.currentTarget.checked})} /> group by chain</label>\n <select id="f-kind" style=${e&&n("notices")} value=${s.noticeKind} onChange=${a=>H({noticeKind:Pe(a)})}>\n <option value="">all notice kinds</option>\n ${o.map(a=>i`<option key=${a}>${a}</option>`)}\n </select>\n <input id="f-q" type="search" placeholder="search id \xB7 note \xB7 brief" value=${s.q} onInput=${a=>H({q:Pe(a)})} />\n </div>`}function ur({d:e,inp:t,tab:s,st:n,stale:r,unread:o=0}){return[i`<div class="headerline"><h1>\u{1F99E}\u2728 spyglass<span id="stale" style=${e||r?r?"display:inline":"display:none":void 0}> \xB7 STALE FEED</span></h1><span id="settings-slot"><button id="gearbtn" title="settings" aria-label="settings" onClick=${()=>O("settings","browser")}>\u2699</button></span></div>`,i`<div class="chips"><span id="chips" style="display:contents">${e&&t&&Fo(e,t.chips)}</span><span class="chip dim" id="clock">${e&&new Date(e.now).toLocaleTimeString("en-GB")}</span></div>`,i`<nav class="tabs" id="tabs" aria-label="Spyglass views">${se.map(a=>i`<a href=${"#"+a} data-tab=${a} class=${e?a===s?"on":"":void 0}>${Bo[a]}${a==="deck"&&o>0&&i`<span class="dbadge" aria-label=${`${o} new decision${o===1?"":"s"}`}>${o}</span>`}</a>`)}</nav>`,Ko(e,s,n)]}var fr=({d:e})=>e&&["\\u{1F99E}\\u2728 lobstah v"+e.version+" \\xB7 ",i`<a href=${e.repoUrl} target="_blank">${e.repoUrl.replace("https://github.com/","")}</a>`];function mr(e,t){if(!t.lobs)return[];let s={"pr:draft":"draft","pr:review":"review","pr:checks":"checks","pr:conflict":"conflicts","pr:ready":"ready","stack-ready":"stack ready",landed:"landed",watch:"watch",report:"report"},n=t.hidden??{},r=e.filter(a=>!a.quiet&&!a.acked&&!(a.key!==void 0&&n[a.key]===a.stateHash)).map(a=>{let l=s[a.kind??""]??"",u=a.key!==void 0?{hideKey:a.key,hideHash:a.stateHash??""}:{};return a.kind==="decision"&&a.key?{key:"decision:"+a.key,text:a.note||a.verb,label:l,hash:Ct(a.key),...u}:(a.kind??"question")==="question"&&a.key?{key:"question:"+a.key,text:a.note||a.verb,label:l,hash:Ct(a.key),...u}:a.kind==="report"&&a.key?{key:"report:"+a.key,text:a.note||a.verb,label:l,href:x(a.key),...u}:typeof a.kind=="string"&&(a.kind.startsWith("pr:")||a.kind==="stack-ready")&&a.prUrl?{key:a.kind+":"+(a.key??a.prUrl),text:a.note||a.kind,href:a.prUrl,label:l,...u}:{key:(a.kind??"question")+":"+(a.key??a.lane+":"+a.id),text:a.note||a.verb,label:l,...a.kind==="watch"?{}:{open:{type:"dispatch",key:a.lane+":"+a.id}},...u}});!r.length&&t.preview&&(r=[{key:"preview",text:"attention questions crawl in here",...t.previewHelm?{open:{type:"helm",key:t.previewHelm}}:{}}]);let o=r.length>4?r.length-4:0;return r=r.slice(0,4),o&&(r[3]={...r[3],text:"\\u2026and "+o+" more \\u2014 see attention",label:""}),r}var hr="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAA4CAYAAABdcSXCAAAVF0lEQVR4nO2d34sc15XHb/fUaGRpsIwky0qbRCiyIZh1NjEKxDixTUge8up9ccKSZSHZf8BP0R/gffI/kCwsLEvWL/FrHmKM88PEYOHsJiYIFGWwQjq2rBk8xpI1mp7p5XOrvj2n71R1V1VXz3RP3yOaaXV1Vd/7rXO/95xzzz3lXJS5kVe++3w/732UKGUk6k8EqDFFeu07l/uRhOIAqyNRfyJAE0uczeIAm0Si/kSAGlWmaA3FAVZXov6UAOjlr/1Df5GUoKpSRCUajU3Un9nUn/Y8DLCXfvFma+Xs2YUMvFbByB1hifpTT6L+jCGgqrO8W0Api9NRtoKi/tSXqD8RoANVIrdgEgfY/OrPgblg49yHrdu3x3b+qLsZZfo+DiNwXDSJ+jO/+jMTBIRceee9UgAtspQh4EWNlUX9mU/9mRkCqmLhLOIAq9L/i71PF3IFKerP7OnPXBGQZNEJZtJBtpasNvqbX3rouCPzel6IKOrPbOnPXBHQIsd5DlsgmHCLBzGBy50T/gURzfrgjvozXzJzBHRYMm9L2E23FfL51qUz7tnLj+871t1quePLieucOdnkTx4pWXT9OXIENE2AipQF33dWbkyZVZ8myQfr5rGLHffnte6+lZDOSt/d2+65Sw8uu2dO9ecCo6g/B6c/Ve5B+Fl7kQBSQJVBFA4kuRqQ0DzEOlitaJJ8sHy2Nzdyj2H9WDKa9aX+qD8Hpz95gt4wtsIxxnvGF27+TBPQNAACFAbO97/8iJ/pu+t3huIFDDTkqS+cGryfddHNDS26qnEQ63adP5mSja4HHq1225PQZ1v33bxI1J+D0x87xuTK/8tXPrfvWprk9d2ZJqCmAYJU/u3bT7qVlcTd7f7NXfv43uCYwGCQnVxq+VjHrLoZwoKZhNfbLz7n3zPb8F7fKWvFoSySfq/n8eEzlEVEvO36rtdO40DIPASjkag/09ef0FpmjDHBf3rrlnvjxvrQcf6iQ1rQQPZs6xlTGuUieHOt96knBQDqrqdxGgaIks/GCZ3XLL995zOXrCwPWTm8Z6a/t73tj2EFbN340M2SqM+y4n7eXfVKw81+7OLe9x7L/v766l48q4iwrZLJ/Vo+ddp1r1/3mIB5u7fter1UcZAHVo455+7O7GpT1J+D0x8rcuOR3Y8YO/tPYWIjligd4vrJrAGE/FNnyb170/kBAEBWAOvZDCAGEKb2OIA0y99fv+UJppUkQ7/J7zDz95ecS3b7rseImyHRjPXst58cfPbMna53I63yWEEZnrp1y717c9PjNI6ssX7ABYzAAxI+0UqDzwgWENhgDc2iRP05PP0ZmuCZyI4tO7fVG7ovjMEvri4N9IkJf627MxsumADCfMN3XD13zn8OQP31j/yg4AWz0kFedBgmHxc05pjiGhJmeURuxhceTNzS/e2h70ziZjTlntiguY3TyFrBlBUeeoEXOPE6tuTc04+f8Te/qE1grOtBQsiF08c9+Vi3C/JBeZZzZrbDlqg/h6c/oUiHmNAxDjTG0CkrH9zp+WO1tInGNGGCW3aUpQMANtiZmmt70t/dHbwnONpLltz7G/fcW5utXEuIG/CvX3vUEwwDill+a6vnmZ2ZHvLpbaWuF4OMa97Z6buf/eHDkS6erC/aLytMfrCySSfBSNhAsiJkBCVJejtjzxdO6nMeRgoYohxyTRFvBbZbg/cI5AMZ/eXTHe/bl3V/i/oW9We+9Od319dz22R1SOQjfWNyE9FpIpMl/dv3P/Fx2PY49rQBYGXKNrFUbQES+RAcpuOQjl6hQBB68V2Ihc6Pyk+BcGT6KdAKs3Me5OOByf6WbTuKgrWG1WY/47rMNpPkFAkbri3l0QxVRnmscLOFUdgmSARSkkUokZLovSwhMJRSlelD1J+joT9Jb8e3izFWdtyTroGb9Y0LD/r/a/ztu35RB3yA6ispOL++en1g5tIQFLaz1nWvnK03k1mARDwoeJ61A9HkiY4Rk0iyAdZdX983uzK7QDjbvT2CkbvlB1o260NAewGy/PbqeohMWqL9YIVVcPmJ0/7a3Gj5uHUFqw3hWr69x5YrKY8Imj7KbcLiW9vM/z795vsDRcksQns9MqKZtcZZP1F/jpr+OH8upJInGBCMYY0lJnm9v5fFghBZ1XZMJ0UAySrBF0Rxr924PQSQj6ts1gMoj3ys2acGFpGPhO8v852sY2pnKBDm0v0PXS8AYojlsxkewT2TaBZSIPut7BAZw2DEKhERf5nOnsTabfe+93H3liHLCjMMQfhBnGqr5060UtLsG8UowkfErL+tZMnd2LjnLpxeypII97eJvkDgS/f33FsIuWfIiV/H+skjn6g/i6A/x93uTkoqVuALSNQlexO5yAfR2KZfHOP/29nPokttWTYAxIv3EAQXhWgEKgChiAxeOkMQqY4AEG6XyM1m2VoQbKwnPBbGgG58klo0RQxNX3aYAVAUM6vb6yC0hWvZWR4sMJXT5cqO+/7FFY+Rgrd+j9RKf8jPpY0cr5oMJzPc+uzkJYWELBe0CKuB/54suf9852/u66/+yv1sbcstn3xgX3AdaxE3DB9fFiAv+qIXmLy9tule+OXVXPKJ+rMY+rOykuQuzsAF/KYIJi+cob7xuzc/6Q3y8NoAhDkIOCyT4WLxI2Jx2Az2wyT0jcouXjYWEAIEyVmALBj2/5aprdjPLUD8zRtg4YwkJvZ9ywiJuYHrcswmT+URGealsjkxSz2xrSz73+Z83hOoBeCq7qnuhSTcGoH8Zu1j99Pff+D/Cg/Ezmp6EQi88s57kD5t8bMibQ+3UtBO2vvff9rw12URgD7w+o//SxPK8sjHtjnqz2LoTyfYkEwbLZlqscJO9Bgt+hyCtosYQzYYXxJA+iHlzPg4iusPVkLqAiTXjmX10ATkZvzXX3seIBoqsglZOgTISh5AtJUZnnMU41HMR32kb1e7d/2sY90M/o+5LAw4bzdZdt+8+JAnQIvRic6jHvg65KzVBCvy2WUOszr3wi+v+j7TJ35/YCobrPgewsTxynef999/7TuX/Wfcv3CriWbOf34iNdshIpREilJ2xSvqz9HXn25O28LxhR7YiZ4YJASbt4LatgAhOomdz2HQCqDu9lu1zEMLEK6XzScRCPirMC0dtjNJaBFZgGBnhPYUBdk4BkiwPucCxiBItt3zRIaLkbfMKBfFA3nqtFce8iOUkAfwejHDaXmxyjK1LDaRs/IxbP99P1aSQX8tadMvCLv98CP+JpNCQD/B4wefT9ztH3/PW7Zck2uFW020R4cBIAIvSzxRfxZLf/KEyUvjC1KzLjzWD30Fo7z0jQSAWFo/f/KWW+086i0TxLsl2fo/F9d2BdyZqjkKClICkF8ODFabrCUEQNaq4RgdU1Y0ZElHiPIDEH+JKQGuKyAgfO7OpTOeOAHXWgDyRUfd8HR3dZqjAQbHzpwbrA5y/rfMtdMZMN9dGSWKiyGjVit8UtjZ5/3KiZR6LdlyL7z6pp+tFLj0md1Krlz/yK82aFXDz2JZ1Tu74x0FKwo0j8Im6s9i6M92ThY8+sPYFDfAFwifyVoSf+Rh5G0//PtUETf9hRSjKQKoztK7ACLhrSjwfHKpPQSQLCMIj9UtZhIBBMhpHlB63rZPgmoVAgR5XTi94a69k25ErdoPwINI/dJo9++DvWr0i6zhxy4+7lMTtMpRRbjW6jm7j6ZghaLX81bfBZO2w/eZpZ558Tn3RrYCCCkrKcyKksoQPYVEqRWoLLOU3aRbVqL+LIj+bKUB9pWzq0N77vCWWmcedu7m5kB/GLNbJSb31qiNfAjKyaoX5hmEVCdDkxlS9WZEQKOWAMP/Y9ZZS0xtCoNospZU01Zp4CRDYWrqGpNk8Ya5Lsoixq1jNvnJ63+sbD6rdIEUogw2eYISKPBYNAuGWIIN1w2Dg3Uk6s/R1p/frH08mLzhCIwVVtmQuvozZIpYYhFAiN8DlO2/uvb6H6tc31/nqU4abCtDPmE0HoFsCNo9e+Zh7yZgGo4CU6s82uIBqJrdmyAfCVYh7L9EYH7jnnMb3cFjTcqSdNrWz+0ljRUoi83NKBKw0Q3N+64vq3HnM/8e8gFTVrwIvjeBTdSfo60/0pvL53bd8WUm9TTsoUm/jv60xwEkJmQAE3+pukkzNQ9Tl07kU5Tjk7csr884B3YvAkaWEkIbf/TV8/7mkujIjI/71iT5SBjABA5hf/ASaZeRMK0dos3rfxMCNvd3nCcbBLcUxWHFq67yjJOoP0dHf8IVZoLVjCtWrCfZtza2HIdmyDoCQMyslM/QCgxSRCCjpAyorKIpZoVJyA2tE++pIgxgER6DHDM6rakyvhYP7YQYcW2xRvLcT/v/KhLmdyQ9536X+ei016/MeOKpHvCsIlF/5lt/wmuhP4PH9jSgP4UnK0BpV8CIjAOQVqJGDWwLENcBoGmJjf+IcCTTLJylPmJp4d5gYWDtCR+kKF1BxaBQHiTEp8jSG5VGnyf6Pm40yj4NSydPov6Ml5ej/hRbQCoPYLfaE8NxLtsHZlZSRg2wbjbAqkiZgJn9LhKmwB+UgIWPrZA/lSwPrRyKYGS6ioxttqpdrhy3L8fOYvxmldo8SrI8KIn6c/T0ZxqSlAEI8lGColZQQoDk7tjSjLhdOoZ5KR+1iGTGmYmjiKlOcuSkwu+xD4aaQsS3fvv+5j5sbILYW//79yE/Gnw4t7ueJleyHBtKnvIgZMzyTxtrpUzh97TClZLzdN2tPIn6c5T0573G9addBiByR8gc1moJKftYRRYgSMcm+Km2DwApPqMOqpOjCMduwdAr3JrBi+X1ultDmhCID9dLbQAb9tT5HcJZGkO4OueD6VlWqfJySOLKi5H5ejzJ0kBRBttHsu9oP5vwsDv9+Q4xmEmX1+tK1J/x0l1w/UnKAASJaAcrAJHFfCcjFGJDqmIo9gYgZTqznV8AEeeQ6SfysdmVtjF5rJ3HzhBb1ezdpgTC8y5ottk13WrS37cBFjx049n5bBVEq4xg982c2E5Yo5p8kVZvx89KKGoodhOgAsDWMrSlMxBV5ZsWflF/iuWlqD+jCUh+fOeT1I2yD6nTABtK2c7E7/DNBpYdYE8T5c9JcILFlXnJubhqDDCIL698hh1g73bv7kXlC2rTTHOAIcr+1C7nkGA+y3YCgx9FvS6tDJ/PeVyD/qj2DjWOwtKovH/7+ron8tTvP+kD4JynkrKSdOPfxmChQLhoW4o17f+81vfF5ZrGKepPObm2wPoz1gKiAUrRxvpJ0/nTmRd/FLJgdmeTHQDZAvC+WuFufwAQuURaMbI/7CuoZQDd3LiX+bXFAO1l7hYDhIuookwA9NpDl31KQNNEpN9W9jiKgj8ua1FWG8RqM06VMq8aPGCHtfL04w84/xycnMJpEDXX/cbursce07jzxOm0DEJWQIxrgVd3/e4QCYRPRbCCIr1RMcG0rET9GS0vLbj+jF1qogEoER3E/Lf5Nlb8ZtCsOBEi4Ng9rzo9CkbbWsMDcHz1tcSDgk8sgLgZtsgRN4LPFHS2AFFlUbVpIB+llYeP9mlaaANWGLMT/bDF2kTOVuS/q0aK9fV9WYPMr0fAAhFW4EjiGn3iPUvr9joQM4qo5EIVzw/rxKjgHCkDpP+HZSSaxCbqz3iM1hZUf9pVAMKCoXMCCGvH1lW2ZqOtB4KFI4B0jVAUC1IxNAHEBlgBpNIZdke+fbY5AjAEwH2N2t6O/wtA08r2tRhBmEqAlLlMu1md8DV1sxXBMJYlUvYlZTNLkTiZLZwmXFUYjn7JZEdhMJn5q+JhmllV/JwqB5yjoljs4VGJhLx8LhWUbwqbqD9Rf/KkdM62Ld05ONkUes/LSVDtExGOrBoNMLFzeA0GIQOl7ACDfEQ6DHYVuIcMYPi8ARY+7rkJgeBon6xDAs4qziTS0TGRqmYmSAHXk/5wDcxgjvsnk2bfEUlzDeVlYKbzf/1umIoQVrLkeuAuogyx0dMswqJlk0rUn/FyZQH1p9KTUWVxKN8HghnM0tl3OPbUTt8dC6wilQL40noK0PkvP+JnYdwpSCoEUatmlCvAChI4lAIIAbKlJ2WyfpDtzs0DyPvbWZZ30yKzlWtDpop54WN3suAhZPrF1VR5bAAeLPUYE4jT1zk6+YBPAKVP3PwTy4lXGo4PbRDsnPAWlx5drWPKzNZvI1yH+FmG2OAZZz5+dqrvLn3+fJr7kVPgfxKJ+jNeriyY/tRyScRwAKQsaRERHbPbEwQQrEnDcdeoHgfAKiRmAeI43w0Lh6ng0aDWSPZAN9wLLB8J59MW5SxZsW0uqtDWlNjSFEoeY9kc/xtRSU7iaaElCRbqq9pLsB/FQ2yhJ0R4I0r4VGW9H/7juX3PZFJA3z/na6s1VNBf6RNNlOcokqg/4+WVBdGfRmq/0Ek1QKadYj5KQBRIcssgIQuQSEzni2TCshoiMwAWieXdAD4TOAJIz4VPTAmBaScv2rQAESAKoH5TTF/Pzeam8znvCd7bXCJJuLn27Ref6+Maaxc1/STorydTQtx6zLIemTLuIYycj6tdVIi+CYn6Ux6no6w/rWkDRENkSqq0Ku/1SGVYUpaO9UEF0O0ff69v98MQ+MZqEkC+Vm1mgiqmZAnJPniwDkBNSvg4nLCsgp0tyn5WdB8kKJAsSxVEFz55yqSHDhQ96rppifozv/qjhaFJ9KdSDChPwviKN2ySVWrMDnX8jZIAhclMZ//9f1qjAJJoMx8SunM22u4B2jAlBQ5Qpjmgw/ug9507xN7SOAAiXOwDGTW7Ienjl+8OakZPW6L+zK/+QD3HJ9SfZFqdODCAsqc2uq3tXMtHAW7tZjjoAVZHRs1UVbACJ/n7BC01Ww2sHrNQoAQ0BScPY+Nq1J/mg9m8qupRWf3xVlG2wlZXf6ZTOi0QATDqwfZVBh0ACST/ULScQveha6HgmJKsDmPj6kGL8m9wcxWABAcfRLSZ5Un6wLi856IdtlTRizJ6hkT9mR39ORACmjZAsC4A6QmTei4RAJFvZAGadfKZ1PoJhb6qMBz9BwfImc3FYMZiAPEwiHzWsWlajqL+1CHkw9SfRl2wuibhJINOCYnsV1MQ3Ep3PV229DGfOVKePKlLSl9/9VctG6PTo42QMOg/S1JHL6q6HkdBf65M2WKdpv60Dkuhit5Pcv3waZ+zPsCijJa6elH3vKg/UaJEieIWR/4fY2sZ1Lh7NyEAAAAASUVORK5CYII=";var gr="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAA2UlEQVR4nGNgwAPeHo35D8K45JnwaRQS/YXPbAacmkEa/3z/BaZx2c6Ey1YGIgAjLsV8PF/g7E9feMC0sPUSRhTNuPwGcjI28AlqENzZIIUw/O41G8O58z/BkiycbGDFl278R3GRENQyJpBTkE1DtgFkyIMnn+EGwQDIApA+uB9AzgeZCtIEMhlmu54GI1gMFgafvvDA/Y4SADADYABkG7LfPyFpxIgq9IBD1ojsbAZ0zYTiFlvoY00ksFAGYWw2wtWhC4BD/gsiQUBcxAbxEiI4MAE+p2PLYQAGj4EmRoa2cwAAAABJRU5ErkJggg==";var gt=()=>i`<span class="pet-art" aria-hidden="true"><div class="sprite" style=${{backgroundImage:`url(${hr})`}}></div><img class="star" src=${gr} alt="" /></span>`;function Jo(e,t){let s="animation-duration:"+((innerWidth+180)/(100+t*12)).toFixed(1)+"s;animation-delay:-"+t*9%14+"s",n=()=>{e.hideKey&&bn(e.hideKey,e.hideHash??"")},r=[i`<div class="bub">${e.label&&[i`<span class="badge dim">${e.label}</span>`," "]}<span>${e.text.length>48?e.text.slice(0,47)+"\\u2026":e.text}</span></div>`,i`<${gt} />`],o=e.open;return e.hash?i`<a key=${e.key} class="lob" style=${s} title="open the decision" href=${e.hash} onClick=${n}>${r}</a>`:e.href?i`<a key=${e.key} class="lob" style=${s} title=${e.label==="report"?"open the report":"open the PR"} href=${e.href} target="_blank" rel="noopener" onClick=${n}>${r}</a>`:i`<div\n key=${e.key}\n class="lob"\n style=${s}\n title="click to open"\n onClick=${()=>{n(),o&&O(o.type,o.key)}}\n >\n ${r}\n </div>`}function $r({state:e}){let t=e.snapshot,s=mr(t?.attention||[],{lobs:e.prefs.lobs,hidden:e.lobHidden,preview:e.preview,previewHelm:t&&t.helms.length?t.helms[0].grounds:void 0}),n=s.map(r=>r.key).join("|");return Wt(()=>s.map((r,o)=>Jo(r,o)),[n])}function $t({box:e}){if(!e)return null;let t=s=>{s.target===s.currentTarget&&le()};return i`<div id="lightbox" class="lightbox" role="dialog" aria-label=${e.name} onClick=${t}>\n <button type="button" class="lbclose" title="close" onClick=${le}>\xD7</button>\n <img class="lbimg" src=${e.src} alt=${e.name} />\n <a class="lboriginal" href=${e.src} target="_blank" rel="noopener">open original \xB7 ${e.name}</a>\n </div>`}var me=i`<span class="x" onClick=${de}>\xD7</span>`,vr=(e,t,s)=>i`<span class="seg">${t.map(([n,r])=>i`<button class=${n===s?"on":""} onClick=${()=>e(n)}>${r}</button>`)}</span>`;function Yo(e,t){let s=vr(vn,[["table","table"],["cards","cards"]],t.view),n=vr(kn,[["on","on"],["off","off"]],t.lobs?"on":"off"),r=e.attentionError?e.attentionError:(e.attentionKinds||[]).join(" \\xB7 ");return[me,i`<h3>\u2699 settings <span class="dim" style="font-weight:normal">\xB7 this browser only</span></h3>`,i`<div class="settings"><div class="row"><span class="lbl">view<span class="hint">cards or table (notices stay a table)</span></span>${s}</div><div class="row"><span class="lbl">lobs<span class="hint">crawling lobsters in this page</span></span>${n}</div><div class="row"><span class="lbl">attention<span class="hint">kinds shown \u2014 attentionKinds in config.toml (read-only here)</span></span><span class="dim" style="font-size:11px;text-align:right;max-width:260px">${r}</span></div></div>`]}function Qo(e,t){let s=fe(t.numbers,new Map(t.numbers.map((r,o)=>[r,t.titles[o]])),e.number),n=t.nextMergeable?[" \\xB7 ",i`<span class="ok">next mergeable</span>`]:t.blockedBy?" \\xB7 blocked by #"+t.blockedBy:t.nextNumber?" \\xB7 next is #"+t.nextNumber:"";return[s," \\xB7 ",t.position," of ",t.size," \\xB7 floor ",t.floor,n]}var Xo=e=>t=>{t.preventDefault(),O("dispatch",e)};function Zo(e,t){let s=Js(e,t),n=s.pr,r=s.stack,o=s.watch,a=dt(n),l=s.chain.length?s.chain.flatMap((c,d)=>[d?" \\u2192 ":"",c.culled?i`<span class="dim">${c.id.slice(0,8)} (culled)</span>`:[i`<a href="#" onClick=${Xo(c.modalKey)}>${c.id.slice(0,8)}</a>`," ",i`<span class="dim">${c.verb}</span>`]]):i`<span class="dim">none</span>`,u=o?[i`<div>${o.key} \xB7 owner ${o.owner||"?"} \xB7 ${o.lastCheckedAt?["checked ",v(o.lastCheckedAt)," ago"]:"never checked"}</div>`,o.lastError&&i`<div class="bad">${o.lastError}</div>`,i`<div class="dim" style="font-size:11px;margin-top:4px">cursor</div>`,T(o.cursor)]:i`<div class="dim">no watch \u2014 <code>lobstah watch add ${n.url}</code> registers one</div>`;return[me,i`<h3>${it(n)} ${n.title||""} <span class=${"badge "+E(n.badge)}>${n.badge.text}</span>${n.draft&&n.badge.text!=="draft"&&[" ",i`<span class="badge pr-draft">draft</span>`]}</h3>`,i`<div class="sub">${n.repo} \xB7 ${n.state} \xB7 observed ${v(n.observedAt)} ago${n.gate&&" \\xB7 gate "+n.gate}</div>`,i`<div class="sec">checks</div><div>${Ce(n)}</div><div class="sec">review</div><div>${a||i`<span class="dim">no review yet</span>`}</div><div class="sec">merge</div><div>${Te(n)}</div>${n.repairWait&&i`<div class="sec">repair waits</div><div>${n.repairWait.heldBy}: ${n.repairWait.reason}${n.repairWait.until?" \\xB7 until "+n.repairWait.until:""}</div>`}<div class="sec">refs</div><div>${n.headRefName||"?"} \u2192 ${n.baseRefName||"?"}</div><div class="sec">stack</div><div>${r?Qo(n,r):i`<span class="dim">not stacked</span>`}</div><div class="sec">dispatch chain</div><div>${l}</div><div class="sec">watch</div>`,u]}var kr=(e,t)=>(e==="codex"?"codex resume ":"claude --resume ")+t;function ea(e){let t=Date.now()-Date.parse(e.heartbeatAt)>18e5;return[me,i`<h3>\u26F5 ${e.man}</h3><div class="sub">helm of <b>${e.grounds}</b> (${(e.repos||[]).join(", ")})</div><div class="sub">${e.cwd??"?"}${e.host&&" \\xB7 "+e.host}</div><div class="sub">session ${e.sessionId??""} \xB7 signed on ${v(e.signedOnAt)} ago \xB7 heartbeat <span class=${t?"warn":"ok"}>${v(e.heartbeatAt)} ago</span></div>`,i`<${nt} worker=${e} />`,e.sessionId&&[i`<div class="sec">open this session</div>`,T(kr(e.harness,e.sessionId))],e.transcript&&[i`<div class="sec">transcript</div>`,T(e.transcript)]]}function ta(e){return[i`<div class="sec">report \xB7 ${e.title}</div>`,i`<div class="sub">${ns(e)} \xB7 <a href=${x(e.key)} target="_blank" rel="noopener">open the report \u2197</a></div>`]}function sa(e,t,s){let n=e.claimedBy&&e.claimedBy.startsWith("wt:")?[i`<div class="sec">worked by trap</div>`,i`<div class="claimedby">claimed by ${at(e.claimedBy.slice(3))}</div>`,T(U(e.claimedBy)),i`<div class="dim" style="font-size:11px">an opted-in interactive session mans this seat \u2014 attach would resume someone\'s live thread. Message it instead: lobstah send ${U(e.claimedBy)} "\u2026"</div>`]:[i`<div class="sec">open this session</div>`,T("lobstah attach "+e.id)];return[me,i`<h3>${e.id.slice(0,8)} <span class=${"badge v-"+e.verb}>${e.verb}</span></h3>`,i`<div class="sub">${e.repo} \xB7 ${e.lane} ${e.bucket} \xB7 ${v(e.verbAt)}${e.for&&[" \\xB7 ",xe(e)]} ${ue(e)}</div>`,n,e.worker&&i`<${nt} worker=${e.worker} headless=${!e.claimedBy?.startsWith("wt:")} />`,e.worktree&&[i`<div class="sec">worktree${e.worktreeOf?" (reused from "+e.worktreeOf.slice(0,8)+")":""}</div>`,T(e.worktree),e.worktreeKept&&i`<div class="dim" style="font-size:11px">kept: ${e.worktreeKept}</div>`],e.transcript&&[i`<div class="sec">transcript</div>`,T(e.transcript)],t&&ta(t),Un(e,s)]}function na(e){let t=Ae(e),s=e.requested?[e.repo??""," \\xB7 ",e.harness??""," \\xB7 requested ",v(e.requested.at)," ago from the glass; the helm starts it with man throw --new."]:e.starting?e.starting.failedAt?[e.repo??""," \\xB7 reserved ",v(e.starting.reservedAt)," ago \\xB7 start failed: ",e.starting.reason??"",". Its addressed work stays queued; a session can still redeem the ticket, or `lobstah stow --wt ",e.name??e.trapId,"` withdraws it."]:[e.repo??""," \\xB7 reserved ",v(e.starting.reservedAt)," ago \\xB7 starting: waiting for a session to redeem the ticket (due by ",e.starting.deadline,")"]:e.live?[e.repo??"addressed bait only"," \\xB7 session ",e.sessionId??""," \\xB7 signed on ",v(e.signedOnAt)," ago \\xB7 ",t.listen," \\xB7 heartbeat ",t.hb]:"signed off \\u2014 registration gone; the lifecycle, messages, and catches are the surviving record. Re-soaking the same worktree restores this address.",n=e.notices.length?e.notices.map(a=>i`<div key=${a.seq} class="loglines">${v(a.at)} ago \xB7 <b>${a.kind}</b> \u2014 ${a.text}</div>`):i`<div class="empty">none recorded</div>`,r=e.messages.length?e.messages.map(a=>i`<div key=${a.file} class=${"msg"+(a.from==="helm"?" from-helm":"")}><div class="hdr">from ${a.from} \xB7 ${a.at&&[v(a.at)," ago"]} \xB7 ${a.state==="pending"?i`<span class="warn">pending</span>`:i`<span class="ok">delivered</span>`}</div>${a.text}${a.attachments?.length?ce(a.attachments,l=>Ls(e.trapId,l)):""}</div>`):i`<div class="empty">none</div>`,o=e.catches.length?e.catches.map(a=>i`<div key=${a.lane+":"+a.id} class="catch click" onClick=${S("dispatch",a.lane+":"+a.id)}><div class="hdr"><b>${a.id.slice(0,8)}</b><span class=${"badge v-"+a.verb}>${a.verb}</span><span class="dim">${v(a.verbAt)}</span>${ue(a)}</div>${a.note&&i`<div class="loglines">${R(a.note)}</div>`}</div>`):i`<div class="empty">none yet</div>`;return[me,i`<h3>\u{1FAA4} ${e.label??`wt:${e.trapId}`} <span class="badge">${e.requested?"requested":e.starting?e.starting.failedAt?"start failed":"starting":e.live?"live":"signed off"}</span></h3>`,i`<${nt} worker=${e} />`,e.worktree&&i`<div class="sub">${e.worktree}</div>`,i`<div class="sub">${s}</div>`,i`<div class="sec">window</div>`,Y(e),Q(e),e.live&&e.sessionId&&[i`<div class="sec">open this session</div>`,T(kr(e.harness,e.sessionId)),i`<div class="dim" style="font-size:11px">as registered at sign-on \u2014 a hookless enlistment may hold a made-up id</div>`],i`<div class="sec">lifecycle (${e.notices.length})</div>`,n,i`<div class="sec">messages (${e.messages.length})</div>`,r,i`<div class="sec">catches (${e.catches.length})</div>`,o]}function br({snapshot:e,modal:t,prefs:s,detail:n=null}){if(!e||!t)return null;let r=re(e,t,n);if(!r&&t.type==="dispatch"&&n?.key===t.key)return[me,i`<h3>${t.key.slice(t.key.indexOf(":")+1,t.key.indexOf(":")+9)}</h3>`,i`<div class=${n.error?"bad":"dim"}>${n.error??"loading\\u2026"}</div>`];if(!r)return null;if(t.type==="settings")return Yo(r,s);if(t.type==="pr")return Zo(e,t.key);if(t.type==="helm")return ea(r);if(t.type==="dispatch"){let o=js(e,r);return sa(r,o,n)}return na(r)}var ra=e=>i`<tr key=${e.seq}><td class="dim">${v(e.at)}</td><td>${e.kind}</td><td class="grow">${e.text}</td><td class="dim">${e.repo??""}</td></tr>`,yr=({inp:e,more:t})=>[M(["at","kind","text","repo"],e.list.map(ra),"no notices"),t&&i`<${pe} kind="notices" more=${t} />`];function _r(e){let t=new Map;for(let s of e.prs){let n=t.get(s.stackId)||[];n.push(s),t.set(s.stackId,n)}return e.stacks.map(s=>({s,prs:t.get(s.id)||[]})).filter(s=>s.prs.length)}var wr=(e,t)=>fe(e.numbers,new Map(t.map(s=>[s.number,s.title]))),is=e=>e.state+(e.draft?" \\xB7 draft":"")+(e.state==="MERGED"&&e.mergedAt?" \\xB7 "+e.mergedAt:"");function Sr(e){let t=n=>{let r=String(n??"");return r.length>24?r.slice(0,23)+"\\u2026":r},s=e.watches.map(n=>i`<tr key=${n.key}><td>${n.key}</td><td>${n.owner}</td><td title=${n.cursor}>${t(n.cursor)}</td><td>${n.lastCheckedAt&&v(n.lastCheckedAt)}</td><td>${n.lastError||""}</td></tr>`);return[i`<h2>other watches</h2>`,M(["key","owner","cursor","last check","error"],s,"no other watches")]}function oa(e,t){let s=_r(e).flatMap(({s:n,prs:r})=>[i`<tr key=${"stack:"+n.id}><th colspan="8">${wr(n,r)} \xB7 floor ${n.floor}${n.open?" \\xB7 open":" \\xB7 history"}${n.readiness&&i`<div class=${n.readiness.allReady?"ok":"dim"}>${n.readiness.text}</div>`}</th></tr>`,...r.map(o=>i`<tr key=${o.key} class="rowhead" onClick=${S("pr",o.key)}><td>${it(o)}</td><td class="grow">${o.title||""}</td><td>${is(o)}</td><td>${Ce(o)}</td><td>${dt(o)}</td><td>${Te(o)}</td><td>${Jt(o.watch)}</td><td>${o.gate||""}</td></tr>`)]);return[M(["PR","title","state","checks","review","merge","watch","gate"],s,"no PR evidence"),t,Sr(e)]}function aa(e,t){let s=_r(e),n=o=>i`<div key=${o.key} class="card" onClick=${S("pr",o.key)}><div class="top">${lt(o)}<span class=${"badge "+E(o.badge)+B(o.badge.text)} title=${j(o.badge.text)}>${o.badge.text}</span></div><div class="meta" title=${`${o.repo} \\xB7 ${is(o)}`}>${o.repo} \xB7 ${is(o)} \xB7 ${Te(o)}</div><div class="foot"><span>${Ce(o)}</span><span>${Jt(o.watch)}</span>${o.gate&&i`<span>gate ${o.gate}</span>`}</div></div>`;return[s.length?s.map(({s:o,prs:a})=>i`<${ee} key=${"stack:"+o.id}><h2>${wr(o,a)} \xB7 floor ${o.floor}${o.open?"":" \\xB7 history"}</h2>${o.readiness&&i`<div class=${o.readiness.allReady?"ok":"dim"}>${o.readiness.text}</div>`}<div class="cards">${a.map(n)}</div><//>`):i`<div class="empty">no PR evidence</div>`,t,Sr(e)]}function Dr({inp:e,more:t}){let s=t&&i`<${pe} kind="prs" more=${t} />`;return e.view==="cards"?aa(e,s):oa(e,s)}var ia=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],la=5,ls=(e,t,s=t+"es")=>`${e} ${e===1?t:s}`,da=e=>e.count?`${ls(e.count,"catch")} on ${e.date}`:`No catches on ${e.date}`,Gr=new WeakSet,ca=e=>{!e||Gr.has(e)||(Gr.add(e),e.scrollLeft=e.scrollWidth)},xr={ArrowUp:-1,ArrowDown:1,ArrowLeft:-7,ArrowRight:7};function pa({page:e,focus:t}){let s=e.weeks.flat(),n=t&&s.some(u=>u.date===t)?t:e.today,r=u=>{let c=s.findIndex(p=>p.date===n),d;if(u.key in xr)d=c+xr[u.key];else if(u.key==="Home")d=0;else if(u.key==="End")d=s.length-1;else return;u.preventDefault();let h=s[Math.max(0,Math.min(s.length-1,d))];Lt(h.date),u.currentTarget.querySelector(`[data-date="${h.date}"]`)?.focus()},o=e.weeks.length-1,a=(u,c,d)=>{let h=u[d];if(!h)return i`<td key=${c} class="pad"></td>`;let p=da(h),m=c<4?" tl":c>o-4?" tr":"";return i`<td\n key=${c}\n role="gridcell"\n class=${`day l${h.level}${d<2?" below":""}${m}`}\n data-date=${h.date}\n data-tip=${p}\n aria-label=${p}\n tabindex=${h.date===n?0:-1}\n onFocus=${()=>Lt(h.date)}\n ></td>`},l=e.months[0]?.week??e.weeks.length;return i`<div class="heat-scroll" ref=${ca}>\n <table class="heatmap" role="grid" aria-label=${`Catches per day, last ${e.weeks.length} weeks`} onKeyDown=${r}>\n <thead aria-hidden="true">\n <tr>\n <td class="wd"></td>\n ${l>0&&i`<td colspan=${l}></td>`}\n ${e.months.map((u,c)=>i`<td key=${u.week} class="month" colspan=${(e.months[c+1]?.week??e.weeks.length)-u.week}><span>${u.label}</span></td>`)}\n </tr>\n </thead>\n <tbody>\n ${ia.map((u,c)=>i`<tr key=${u} role="row">\n <td class="wd" aria-hidden="true">${c%2?u:""}</td>\n ${e.weeks.map((d,h)=>a(d,h,c))}\n </tr>`)}\n </tbody>\n </table>\n </div>`}var ua=()=>i`<div class="heat-legend" aria-hidden="true">\n Less ${[0,1,2,3,4].map(e=>i`<span key=${e} class=${"swatch l"+e}></span>`)} More\n </div>`;function fa({page:e}){let t=[["today",e.catchesToday],["this week",e.catchesThisWeek],["total",e.totalCatches]];return i`<div class="stat-tiles">\n ${t.map(([s,n])=>i`<div key=${s} class="stat-tile"><div class="n">${n}</div><div class="dim">${s}</div></div>`)}\n </div>`}function ma({page:e}){let t=e.perTrap.slice(0,la);if(!t.length)return null;let s=t[0].catches||1;return i`<section class="top-traps">\n <h2>Top traps <span class="dim">\xB7 all time</span></h2>\n <ol>\n ${t.map(n=>i`<li key=${n.name}><span class="name" title=${n.name}>${n.name}</span><span class="bar"><span style=${{width:`${n.catches/s*100}%`}}></span></span><span class="n">${ls(n.catches,"catch")}</span></li>`)}\n </ol>\n </section>`}function Cr({page:e,error:t,focus:s}){return e?i`<div class="stats">\n <${fa} page=${e} />\n ${e.totalCatches===0&&i`<div class="stats-empty"><${gt} /><div><b>No catches yet.</b> <span class="dim">A dispatch that finishes done is a catch; each one lands on its day here.</span></div></div>`}\n <section class="heat">\n <h2>Catches per day</h2>\n <${pa} page=${e} focus=${s} />\n <div class="heat-foot">\n <span class="dim"\n >${e.undated>0?`${ls(e.undated,"earlier catch")} ${e.undated===1?"has":"have"} no recorded day${e.historyFrom?`; daily history starts ${e.historyFrom}`:""}.`:""}</span\n >\n <${ua} />\n </div>\n </section>\n <${ma} page=${e} />\n ${t&&i`<div class="dim">${t}; showing the last numbers read.</div>`}\n </div>`:i`<div class="empty">${t??"loading stats\\u2026"}</div>`}var ds=e=>e.requested?"requested":e.starting?e.starting.failedAt?"start failed":"starting":e.harness??(e.live?"":"signed off"),Tr=e=>e.live||e.starting?"":" dim";function ha(e){let t=Ae(e);return i`<tr key=${e.trapId} class=${"rowhead"+Tr(e)} onClick=${S("trap",e.trapId)}><td><b>${e.label??`wt:${e.trapId}`}</b></td><td>${e.repo??"\\u2014"}</td><td class="grow">${e.worktree??""}</td><td>${e.harness&&i`<${N} worker=${e} cls="" />`}</td><td>${(e.sessionId??"").slice(0,8)}</td><td>${Re(e)}${Q(e)}</td><td>${t.listen}</td><td>${t.hb}</td><td>${Qt(e)}</td><td class="catchn" title="catches">\u{1F99E} ${te(e.totalCatches??0)}</td><td>${Y(e)}</td></tr>`}function ga(e){let t=Ae(e),s=Qt(e),n=`${e.repo??(e.live?"addressed bait only":"history")}${e.sessionId?" \\xB7 session "+e.sessionId.slice(0,8):""}`;return i`<div key=${e.trapId} class=${"card"+Tr(e)} onClick=${S("trap",e.trapId)}><div class="top"><b>\u{1FAA4} ${e.label??`wt:${e.trapId}`}</b>${!e.requested&&!e.starting&&e.harness?i`<${N} worker=${e} />`:i`<span class=${"badge"+B(ds(e))} title=${j(ds(e))}>${ds(e)}</span>`}</div><div class="meta" title=${n}>${n}</div>${e.worktree&&i`<div class="note" title=${e.worktree}>${e.worktree}</div>`}<div class="note" title=${pt(e)}>${Re(e)}</div>${Q(e)}<div class="foot"><span>${t.listen}${e.live&&[" \\xB7 heartbeat ",t.hb]} \xB7 <span class="catchn" title="catches">\u{1F99E} ${te(e.totalCatches??0)}</span>${s&&[" \\xB7 ",s]}</span><span class="footact">${Y(e)}</span></div></div>`}function Ar({inp:e}){let t=e.list.map(n=>n.x),s=i`<div class="tabhead"><${ct} /></div>`;return e.view==="cards"?[s,t.length?i`<div class="cards">${t.map(ga)}</div>`:i`<div class="empty">no traps soaking</div>`]:[s,M(["address","repo","worktree","harness","session","now","listening","heartbeat","mail","catches","window"],t.map(ha),"no traps soaking")]}function Rr({state:e}){let t=oe(e),s=e.route,n=t?Xe():[],r=t&&zs(t,{st:e.prefs,modal:e.modal,detail:e.detail},Date.now()),o=u=>({left:e.snapshot?Ks(e.snapshot,e.older,u):0,loading:e.olderLoading===u,error:e.olderError?.kind===u?e.olderError.text:void 0}),a=u=>!r||u!==s?null:u==="deck"?i`<${dr} inp=${r.deck} drafts=${e.drafts} focus=${e.focusDecision} viewedHere=${e.viewedHere} unread=${n.length} />`:u==="dispatches"?i`<${cr} inp=${r.dispatches} more=${o("dispatches")} />`:u==="traps"?i`<${Ar} inp=${r.traps} />`:u==="prs"?i`<${Dr} inp=${r.prs} more=${o("prs")} />`:u==="reports"?i`<${Vn} inp=${r.reports} />`:u==="stats"?i`<${Cr} page=${e.stats} error=${e.statsError} focus=${e.statsFocus} />`:i`<${yr} inp=${r.notices} more=${o("notices")} />`,l=u=>{u.target===u.currentTarget&&de()};return[i`<${ur} d=${t} inp=${r} tab=${s} st=${e.prefs} stale=${e.stale} unread=${n.length} />`,se.map(u=>i`<main key=${u} id=${"page-"+u} class=${t&&u===s?"tabpage on":"tabpage"}><div id=${u}>${a(u)}</div></main>`),i`<footer id="foot"><${fr} d=${t} /></footer>`,i`<div id="lobs"><${$r} state=${e} /></div>`,i`<${$t} box=${e.lightbox} />`,i`<div id="overlay" class=${e.modal&&t?"open":t?"":void 0} onClick=${l}><div class="modal" id="modalbox"><${br} snapshot=${t} modal=${e.modal} prefs=${e.prefs} detail=${e.detail} /></div></div>`,t&&i`<${ar} open=${e.decisionModal} order=${ye()} drafts=${e.drafts} extensions=${t.answerLimits?.extensions??[]} />`,t&&i`<${ir} unread=${n} dismissed=${e.alertDismissed} />`]}function $a({view:e}){if(e.state==="loading")return i`<main class="reportview"><div class="dim">loading\u2026</div></main>`;if(e.state==="error")return i`<main class="reportview"><div class="bad">${e.error}</div></main>`;let t=e.report;return i`<main class="reportview">\n <h1>${t.title}</h1>\n <${nr} r=${t} text=${e.text!==void 0?{text:e.text}:{error:e.error}} />\n </main>`}function Mr({view:e,box:t}){return[i`<${$a} view=${e} />`,i`<${$t} box=${t} />`]}async function Pr(e){let t=encodeURIComponent(e),s;try{let n=await fetch(`/report/${t}/meta`);if(!n.ok)return{state:"error",error:`report not found (${n.status})`};s=JSON.parse(await n.text())}catch{return{state:"error",error:"report could not be read"}}try{let n=await fetch(Fs(e));return n.ok?{state:"ready",report:s,text:await n.text()}:{state:"ready",report:s,error:`report not found (${n.status})`}}catch{return{state:"ready",report:s,error:"report could not be read"}}}var va=3e4,Ir="lobstah-glass-page",Or="lobstah-glass-shown",ka=20;function ba(){try{let t=sessionStorage.getItem(Ir);if(t&&/^[A-Za-z0-9_-]{8,64}$/.test(t))return t}catch{}let e="";try{e=Array.from(crypto.getRandomValues(new Uint8Array(12)),t=>t.toString(16).padStart(2,"0")).join("")}catch{for(;e.length<24;)e+=Math.random().toString(36).slice(2);e=e.slice(0,24)}try{sessionStorage.setItem(Ir,e)}catch{}return e}var Er=null,Lr=()=>Er??(Er=ba()),ps=[];function Hr(){try{let e=JSON.parse(sessionStorage.getItem(Or)??"[]");if(Array.isArray(e))return e.filter(t=>typeof t=="string")}catch{}return ps}function ya(e){ps=[...Hr(),e].slice(-ka);try{sessionStorage.setItem(Or,JSON.stringify(ps))}catch{}}function _a(e){if(!e||typeof e!="object")return!1;let{id:t,hash:s}=e;if(typeof t!="string"||!t||!Bs(s)||Hr().includes(t))return!1;if(ya(t),s==="")return!0;let n=ke(s);return n!==null?location.pathname!==x(n)&&location.assign(x(n)):location.pathname!=="/"?location.assign(`/${s}`):location.hash!==s&&(location.hash=s),!0}var cs=!1;async function Ie(){if(!cs){cs=!0;try{let e=document.hidden?"hidden":"visible",t=await fetch(`/api/presence?page=${Lr()}&vis=${e}`,{method:"POST",cache:"no-store"});if(!t.ok)return;_a((await t.json())?.show)}catch{}finally{cs=!1}}}function us(e){let t=null,s=0,n=()=>{let r=document.hidden?va:e??0;r!==s&&(t&&clearInterval(t),t=r?setInterval(()=>{Ie()},r):null,s=r)};document.addEventListener("visibilitychange",()=>{n(),Ie()}),window.addEventListener("pagehide",()=>{try{navigator.sendBeacon?.(`/api/presence?page=${Lr()}&vis=gone`)}catch{}}),n(),Ie()}var fs=!1,Ee=null,Oe=null;function wa(e){let t=e.headers?.get("x-lobstah-beats");if(t)try{return JSON.parse(decodeURIComponent(t))}catch{return}}async function ms(){if(!fs){fs=!0,Ie();try{let e=await fetch("/data",{cache:"no-store",headers:Oe?{"if-none-match":Oe}:{}});if(e.status===304){let t=wa(e);t?ln(t):Ye(await Sa())}else{if(!e.ok)throw new Error(`/data answered ${e.status}`);Oe=e.headers?.get("etag")??null,Ye(await e.json())}Ot(),g().route==="stats"&&Se()}catch{mn()}finally{fs=!1}}}async function Sa(){Oe=null;let e=await fetch("/data",{cache:"no-store"});if(!e.ok)throw new Error(`/data answered ${e.status}`);return Oe=e.headers?.get("etag")??null,await e.json()}var Nr=()=>{Ee||(Ee=setInterval(ms,2e3))},Da=()=>{Ee&&clearInterval(Ee),Ee=null};function jr(){document.addEventListener("visibilitychange",()=>{document.hidden?Da():(ms(),Nr())}),ms(),document.hidden||Nr()}var hs=()=>Os(location.hash),Br=e=>window.addEventListener("hashchange",()=>e(hs()));function Fr(){let e=ke(location.hash);return e===null?!1:(location.replace(x(e)),!0)}var Wr=()=>({snapshot:void 0,route:hs(),prefs:Xs(),modal:null,detail:null,older:Mt,olderLoading:null,olderError:null,stale:!1,lobHidden:en(),focusResults:{},preview:new URLSearchParams(location.search).has("lob"),drafts:{},focusDecision:ne(location.hash),decisionModal:ne(location.hash)?{key:ne(location.hash)}:null,viewedHere:{},alertDismissed:[],lightbox:null,stats:null,statsError:null,statsFocus:null}),Ur=Us(location.pathname);Ur!==null?Ga(Ur):Fr()||xa();function Ga(e){Pt(Wr());let t={state:"loading"},s=()=>Gt(Z(Mr,{view:t,box:g().lightbox}),document.body);document.title="report \\xB7 lobstah glass",It(s),s(),us(2e3),Pr(e).then(n=>{t=n,n.state==="ready"&&(document.title=`${n.report.title} \\xB7 lobstah glass`),s()}),document.addEventListener("keydown",n=>{n.key==="Escape"&&g().lightbox&&le()})}function xa(){Pt(Wr());let e,t=!1,s=()=>{let a=g(),l=a.route!==e||!t&&!!a.snapshot;Gt(Z(Rr,{state:a}),document.body),e=a.route,t||(t=!!a.snapshot),l&&window.scrollTo(0,0),a.focusDecision||(n=null),r(a.focusDecision)},n=null;function r(a){if(!a||a===n)return;let l=[...document.querySelectorAll("[data-decision]")].find(u=>u.getAttribute("data-decision")===a);l&&(n=a,l.scrollIntoView?.({block:"center"}))}It(s),s(),Br(a=>{if(Fr())return;let l=ne(location.hash);l!==g().focusDecision&&(n=null),k({route:a,focusDecision:l}),l&&ae(l),a==="stats"&&Se()});let o=a=>!!a&&(!!a.closest?.("input, textarea, select, [contenteditable]")||a.isContentEditable===!0);document.addEventListener("keydown",a=>{let l=g(),u=a.target;if(a.key==="Escape"){l.lightbox?le():l.decisionModal?o(u)&&u.closest?.(".dmodal")?(u.blur(),document.querySelector(".dmodal")?.focus()):ie():de();return}if(!(a.metaKey||a.ctrlKey||a.altKey||a.isComposing||a.keyCode===229||o(u)||l.lightbox)){if(l.decisionModal){a.key==="ArrowLeft"||a.key==="ArrowRight"?(a.preventDefault(),_e(a.key==="ArrowLeft"?-1:1)):/^[1-9]$/.test(a.key)?rn(Number(a.key))&&a.preventDefault():a.key==="Enter"&&u?.matches?.(".dmodal .dopt.on")&&l.decisionModal.key&&(a.preventDefault(),Ze(l.decisionModal.key));return}a.key==="d"&&!a.shiftKey&&!l.modal&&(a.preventDefault(),ae())}}),us(),jr(),g().route==="stats"&&Se()}})();</script>\n </body>\n</html>\n';
|
|
16260
|
+
var GLASS_PAGE = '<!doctype html>\n<html>\n <head>\n <meta charset="utf-8" />\n <meta name="viewport" content="width=device-width,initial-scale=1" />\n <title>spyglass</title>\n <link rel="icon" type="image/png" href="/icon.png" />\n <style>:root{--bg: #0e1116;--card: #161b22;--line: #2b3240;--line-hover: #3a455a;--row-hover: #1c2330;--inset: #10151c;--seg-on: #26436b;--scrim: rgba(4, 7, 11, 0.72);--fg: #dbe2ea;--dim: #8b96a5;--ok: #4fc17c;--warn: #e2b93d;--bad: #e26d5c;--link: #6cb2e2;--on-fill: #fff;--pr-merged: #8957e5;--pr-merged-fg: #ab7df8;--pr-open: #238636;--pr-draft: #6e7681;--pr-closed: #da3633;--lobstah: #eb6644;--heat-0: var(--inset);--heat-1: color-mix(in srgb, var(--lobstah) 30%, var(--card));--heat-2: color-mix(in srgb, var(--lobstah) 55%, var(--card));--heat-3: color-mix(in srgb, var(--lobstah) 78%, var(--card));--heat-4: var(--lobstah);--heat-cell: 11px;--heat-gap: 3px;--font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;--font-size: 13px;--space-1: 4px;--space-2: 8px;--space-3: 12px;--space-4: 16px;--page-pad-block: 14px;--radius: 6px;--card-radius: 8px;--card-pad: 12px 14px;--card-min: 300px;--deck-card-min: 240px;--badge-radius: 5px;--badge-pad: 1px 7px;--badge-font: 11px;--modal-radius: 10px;--modal-width: 780px;--layer-modal: 10;--layer-decision-modal: 15;--layer-alert: calc(var(--layer-modal) - 1);--unread-bg: color-mix(in srgb, var(--bad) 16%, var(--card))}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--fg);font:var(--font-size)/1.45 var(--font-mono);padding-block:var(--page-pad-block);padding-inline:var(--space-4);display:flex;flex-direction:column;min-height:100vh}h1{font-size:15px;margin:0 0 10px}h1 .dim{color:var(--dim);font-weight:normal}h2{font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:0.08em;margin:20px 0 6px}.chips,.controls{display:flex;gap:var(--space-2);flex-wrap:wrap;align-items:center}.controls{margin:0 0 14px}.chips{margin-bottom:8px}.chip{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:3px 9px}.controls select,.newtrap select,.controls input[type=search]{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);color:var(--fg);font:inherit;padding:4px 8px}.controls input[type=search]{flex:1 1 140px;min-width:120px;max-width:340px}#chain-control{display:inline-flex;align-items:center;gap:7px;white-space:nowrap}#f-chain{margin:0;width:14px;height:14px;accent-color:var(--link)}.tabs{display:flex;gap:var(--space-1);border-bottom:1px solid var(--line);margin:8px 0 10px}.tabs a{color:var(--dim);padding:6px 11px;border-bottom:2px solid transparent}.tabs a.on{color:var(--fg);border-color:var(--link)}.tabpage{display:none}.tabpage.on{display:block;flex:1 0 auto}.headerline{display:flex;align-items:center;gap:var(--space-2)}.headerline h1{flex:1}#settings-slot{min-width:34px;text-align:right;color:var(--dim)}#settings-slot #gearbtn{margin-left:0}.deckgrid{display:flex;flex-direction:column;gap:var(--space-3)}.deckgrid h2{margin:5px 0}.deckgrid section{width:100%;min-width:0}.deckline{padding:2px 0;border-top:1px solid var(--line);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.deckline.acked,.card.acked,.rowhead.acked,.badge.acked{opacity:0.55}.deckmore{color:var(--dim);font-size:11px;display:inline-block;margin-top:4px}.deckline.click{cursor:pointer}.deckline.click:hover{background:var(--row-hover)}.deckgrid h2 a{color:inherit}.deckgrid h2 a:hover{color:var(--fg)}.deckgrid .cards{grid-template-columns:repeat(auto-fill,minmax(min(var(--deck-card-min),100%),1fr));gap:10px}.deckstack{margin-bottom:9px}.deckstack>.dim{font-size:11px;margin:3px 0 5px}.seg{display:inline-flex;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}.seg button{background:var(--card);border:none;color:var(--dim);font:inherit;padding:4px 11px;cursor:pointer}.seg button.on{background:var(--seg-on);color:var(--fg)}.wrap{overflow-x:auto}table{border-collapse:collapse;width:100%;background:var(--card);border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}th,td{text-align:left;padding:4px 9px;border-top:1px solid var(--line);vertical-align:top;white-space:nowrap}td.grow{white-space:normal;word-break:break-word;min-width:140px}th{color:var(--dim);font-weight:normal;border-top:none;font-size:11px}tr.rowhead{cursor:pointer}tr.rowhead:hover{background:var(--row-hover)}.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(var(--card-min),100%),1fr));gap:var(--space-3)}.card{background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad);cursor:pointer;min-width:0;overflow:hidden;overflow-wrap:anywhere}.card:hover{border-color:var(--line-hover)}.worker-harness{cursor:help}.worker-harness:focus-visible{outline:2px solid var(--ok);outline-offset:3px}.worker-tooltip{position:fixed;z-index:20;max-width:264px;padding:6px 9px;background:var(--card);color:var(--fg);border:1px solid var(--line-hover);border-radius:var(--badge-radius);box-shadow:0 3px 12px var(--scrim);font-size:12px;white-space:normal;pointer-events:none}.worker-details dl{display:grid;grid-template-columns:max-content 1fr;gap:4px 14px;font-size:12px}.worker-details dt{color:var(--dim)}.worker-details dd{margin:0;overflow-wrap:anywhere}.card .top{display:flex;justify-content:space-between;gap:var(--space-3);align-items:center;min-height:22px}.card .top>b,.card .top>.prname{flex:1 1000000 auto;min-width:min(10ch,100%);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card .top .badge{flex:0 0 auto}.card .top .badge.long{flex:0 1 auto;min-width:min(12ch,100%);max-width:min(26ch,100%);overflow:hidden;text-overflow:ellipsis}.card .meta{color:var(--dim);font-size:12px;margin-top:2px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.card .note{margin-top:6px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.activity{margin-top:4px;font-size:12px;color:var(--fg);opacity:0.85}.waiting{margin-top:4px;font-size:12px;color:var(--dim)}.activity.stale{color:var(--dim);opacity:1}.card .foot{margin-top:8px;display:flex;gap:10px;flex-wrap:wrap;align-items:center;color:var(--dim);font-size:12px}.card .foot>*{min-width:0}.card .foot .footact{margin-left:auto}.catchn{white-space:nowrap}.btn{background:var(--card);border:1px solid var(--line);border-radius:5px;color:var(--dim);font:inherit;font-size:11px;padding:1px 8px;cursor:pointer;white-space:nowrap}.btn:hover{color:var(--fg);border-color:var(--line-hover)}.badge{border-radius:var(--badge-radius);padding:var(--badge-pad);font-size:var(--badge-font);line-height:1.5;border:1px solid var(--line);white-space:nowrap;display:inline-block}.badge.pr-merged{background:var(--pr-merged);border-color:var(--pr-merged-fg);color:var(--on-fill)}.badge.pr-open{background:var(--pr-open);border-color:var(--pr-open);color:var(--on-fill)}.badge.pr-draft{background:var(--pr-draft);border-color:var(--pr-draft);color:var(--on-fill)}.badge.pr-closed{background:var(--pr-closed);border-color:var(--pr-closed);color:var(--on-fill)}.badge.pr-conflicts{background:var(--pr-closed);border-color:var(--pr-closed);color:var(--on-fill)}.badge.pr-behind{background:var(--pr-draft);border-color:var(--pr-draft);color:var(--on-fill)}.badge.unreported{font-weight:normal;font-size:10px;padding:0 5px;vertical-align:1px}.v-done{color:var(--ok)}.v-working,.v-queued{color:var(--fg)}.v-needs-decision,.v-blocked{color:var(--bad);font-weight:bold}.v-failed{color:var(--bad)}.v-paused,.v-unknown{color:var(--dim)}.dim{color:var(--dim)}.ok{color:var(--ok)}.warn{color:var(--warn)}.bad{color:var(--bad)}.detail{display:none}.detail.open{display:table-row}.detail td{background:var(--inset);white-space:pre-wrap;word-break:break-word;font-size:12px;color:var(--dim)}.detail b{color:var(--fg)}.empty{color:var(--dim);padding:6px 2px}.older{padding:8px 2px}#stale{display:none;color:var(--bad)}a{color:var(--link);text-decoration:none}.dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:5px;vertical-align:middle}.dot.ok{background:var(--ok)}.dot.warn{background:var(--warn)}.dot.bad{background:var(--bad)}.dot.dim{background:var(--dim)}#overlay{position:fixed;inset:0;background:var(--scrim);display:none;z-index:var(--layer-modal);padding:28px 16px;overflow:auto}#overlay.open{display:block}.modal{max-width:var(--modal-width);margin:0 auto;background:var(--card);border:1px solid var(--line);border-radius:var(--modal-radius);padding:16px 18px;position:relative}.modal .x{position:absolute;top:8px;right:12px;cursor:pointer;color:var(--dim);font-size:18px;line-height:1}.modal .x:hover{color:var(--fg)}.modal h3{margin:0 24px 2px 0;font-size:14px}.modal .sub{color:var(--dim);font-size:12px;margin-bottom:10px}.modal .sec{color:var(--dim);text-transform:uppercase;letter-spacing:0.08em;font-size:11px;margin:14px 0 4px}.modal pre{white-space:pre-wrap;word-break:break-word;margin:0;font:inherit;color:var(--fg)}.msg{border-left:3px solid var(--line);padding:3px 10px;margin:8px 0;white-space:pre-wrap;word-break:break-word}.msg.from-helm{border-color:var(--link)}.msg .hdr{color:var(--dim);font-size:11px}.catch{border:1px solid var(--line);border-radius:var(--radius);padding:6px 10px;margin:8px 0}.catch.click{cursor:pointer}.catch .hdr{display:flex;gap:10px;flex-wrap:wrap;align-items:baseline}.loglines{color:var(--dim);font-size:12px;white-space:pre-wrap;word-break:break-word;margin-top:4px}.cmd{display:flex;gap:var(--space-2);align-items:center;margin:5px 0;flex-wrap:wrap}.cmd code{background:var(--inset);border:1px solid var(--line);border-radius:5px;padding:3px 8px;word-break:break-all;cursor:pointer}.cmd code:hover{border-color:var(--line-hover)}.cmd code.copied{border-color:var(--ok)}.cmd button{background:var(--card);border:1px solid var(--line);border-radius:5px;color:var(--dim);font:inherit;font-size:11px;padding:2px 8px;cursor:pointer}.cmd button:hover{color:var(--fg)}.chip.click{cursor:pointer}.chip.click:hover{border-color:var(--line-hover)}footer{margin-top:26px;padding-top:10px;border-top:1px solid var(--line);color:var(--dim);font-size:12px;display:flex;gap:var(--space-2);flex-wrap:wrap}.lob{position:fixed;bottom:6px;left:0;z-index:5;cursor:pointer;font-size:34px;line-height:1;user-select:none;animation:crawl 18s linear infinite}a.lob{color:inherit}.lob .bub .badge{display:inline-block;margin:0 0 3px}.pet-art{display:block;position:relative;width:72px;height:56px}.pet-art .sprite{width:72px;height:56px;background-position:0 0;background-repeat:no-repeat;background-size:400% 100%;image-rendering:pixelated;animation:step 0.5s steps(4) infinite}.lob .bub{display:none;position:absolute;bottom:58px;right:-8px;z-index:2;background:var(--card);border:1px solid var(--line);border-radius:9px;padding:6px 9px 5px;font-size:11px;line-height:1.35;color:var(--fg);width:max-content;max-width:150px;box-shadow:0 2px 8px rgba(0,0,0,0.4)}.lob:hover .bub{display:block}.lob .bub span{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;white-space:normal;word-break:break-word}.pet-art .star{position:absolute;left:56px;top:-9px;height:14px;image-rendering:pixelated;z-index:3}.lob:hover{animation-play-state:paused}@keyframes crawl{0%{transform:translateX(-90px)}100%{transform:translateX(100vw)}}@keyframes step{to{background-position-x:-288px}}#gearbtn{background:none;border:1px solid var(--line);border-radius:var(--radius);color:var(--dim);font:inherit;font-size:13px;padding:1px 7px;margin-left:8px;cursor:pointer;vertical-align:1px}#gearbtn:hover,#gearbtn.on{color:var(--fg);border-color:var(--line-hover)}.settings .row{display:flex;justify-content:space-between;align-items:center;gap:14px;margin:12px 0}.settings .lbl{color:var(--fg);font-size:12px}.settings .hint{display:block;color:var(--dim);font-size:11px;margin-top:2px}.dlist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.drow{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--space-2);background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:8px 12px}.drow.unread{background:var(--unread-bg);border-color:var(--bad)}.drow.answered{opacity:0.7}.drow.focus{animation:dflash 1.6s ease-out 1}.drow .dopen{flex:1 1 280px;display:flex;align-items:baseline;gap:var(--space-2);min-width:0;background:none;border:none;padding:0;color:var(--fg);font:inherit;text-align:left;cursor:pointer}.drow .dopen:hover .dtitle{text-decoration:underline}.drow .dtitle{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.drow .dmeta,.drow .dage{font-size:11px}.drow .dlink,.dmodal .dlink{color:var(--link)}.dstate{font-size:var(--badge-font);border-radius:var(--badge-radius);padding:var(--badge-pad);border:1px solid var(--line);color:var(--dim)}.dstate.unread{border-color:var(--bad);color:var(--bad)}.dstate.answered{color:var(--ok)}.dbadge{display:inline-block;margin-left:6px;min-width:1.6em;text-align:center;border-radius:999px;padding:0 6px;font-size:11px;font-weight:bold;background:var(--bad);color:var(--on-fill)}#doverlay{position:fixed;inset:0;background:var(--scrim);z-index:var(--layer-decision-modal);padding:28px 16px;overflow:auto}.dmodal:focus{outline:none}.dmodal .x{background:none;border:none;font:inherit;font-size:18px;color:var(--dim)}.dnav{display:flex;align-items:center;gap:var(--space-2);margin:0 28px var(--space-3) 0}.dnav .dpos{color:var(--dim);font-size:11px}.dmodal .dtop{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--space-2);margin-bottom:var(--space-2)}.dmodal .dtitle{white-space:pre-wrap;overflow-wrap:anywhere;font-size:14px}.dmodal .dmeta{font-size:11px}.ddone{padding:28px 8px;text-align:center;cursor:pointer}.ddone .ok{font-size:15px;margin-bottom:var(--space-2)}.dalert-live{position:fixed;right:var(--space-4);bottom:var(--space-4);z-index:var(--layer-alert)}.dalert{display:flex;align-items:center;gap:4px;background:var(--card);border:1px solid var(--bad);border-radius:var(--card-radius);padding:8px 10px;box-shadow:0 6px 24px var(--scrim)}.dalert .dalert-open{display:flex;align-items:center;gap:var(--space-2);background:none;border:none;color:var(--fg);font:inherit;cursor:pointer}.dalert .dx{background:none;border:none;color:var(--dim);font:inherit;font-size:15px;cursor:pointer}.dcount{display:inline-block;min-width:1.8em;text-align:center;border-radius:999px;padding:1px 7px;font-weight:bold;background:var(--bad);color:var(--on-fill);animation:dglow 1.8s ease-in-out infinite}@keyframes dglow{0%,100%{box-shadow:0 0 0 0 transparent}50%{box-shadow:0 0 10px 3px var(--bad)}}@media(prefers-reduced-motion:reduce){.dcount,.drow.focus{animation:none}}.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}@keyframes dflash{0%,35%{border-color:var(--link);box-shadow:0 0 0 3px var(--link)}100%{border-color:var(--line);box-shadow:0 0 0 0 transparent}}.dfiles{display:flex;flex-wrap:wrap;gap:var(--space-2);margin:6px 0}.dfiles .dimg img{max-height:160px;max-width:320px;border:1px solid var(--line);border-radius:var(--radius);display:block}.dfiles .dfile{width:100%;font-size:11px}.doptions{display:flex;flex-wrap:wrap;gap:6px;margin:8px 0 6px}.btn.dopt{font-size:12px;padding:3px 12px;color:var(--fg)}.btn.dopt.on{background:var(--seg-on);border-color:var(--link)}.danswer{display:block;width:100%;min-height:3.2em;max-height:50vh;margin:6px 0;padding:6px 8px;resize:vertical;overflow-y:auto;field-sizing:content;background:var(--inset);color:var(--fg);border:1px solid var(--line);border-radius:var(--radius);font:inherit}.dkey,.dlegend kbd{display:inline-block;min-width:1.5em;text-align:center;font:inherit;font-size:10px;line-height:1.4;padding:0 4px;border:1px solid var(--line-hover);border-radius:4px;color:var(--dim);background:var(--inset)}.btn.dopt .dkey{margin-right:6px}.btn.dopt.on .dkey{border-color:var(--link);color:var(--fg)}.danswer-wrap{position:relative}.danswer-wrap .dkey-text{position:absolute;top:6px;right:8px;pointer-events:none}.dlegend{margin-top:var(--space-3);padding-top:var(--space-2);border-top:1px solid var(--line);font-size:11px;line-height:1.9}.danswer:focus{outline:none;border-color:var(--line-hover)}.dfoot{display:flex;flex-wrap:wrap;align-items:center;gap:6px}.dfoot .dattach input{display:none}.dfoot .dchip{font-size:11px;border:1px solid var(--line);border-radius:var(--badge-radius);padding:0 0 0 6px}.dfoot .dx{background:none;border:none;color:var(--dim);cursor:pointer;font:inherit}.dfoot .derr{font-size:11px}.btn.dsend{margin-left:auto;color:var(--fg);padding:2px 14px}.btn:disabled{cursor:default;opacity:0.6}.danswered{margin-top:6px}.thumb,.dimg,.mdimg{background:none;border:none;padding:0;cursor:zoom-in;font:inherit}.thumb{display:block;margin:4px 0}.thumb img{max-height:120px;max-width:240px;border:1px solid var(--line);border-radius:var(--radius);display:block}.lightbox{position:fixed;inset:0;z-index:20;background:rgba(0,0,0,0.85);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-2)}.lightbox .lbimg{max-width:90vw;max-height:90vh;object-fit:contain;border-radius:var(--radius);box-shadow:0 8px 40px rgba(0,0,0,0.6)}.lightbox .lbclose{position:absolute;top:12px;right:16px;background:none;border:none;color:var(--fg);font-size:28px;line-height:1;cursor:pointer}.lightbox .lboriginal{color:var(--link);font-size:12px}.mdpage{background:var(--inset);border:1px solid var(--line);border-radius:6px;padding:8px 14px;margin:6px 0;line-height:1.5;overflow-wrap:anywhere}.mdpage h1,.mdpage h2,.mdpage h3,.mdpage h4{margin:12px 0 6px;line-height:1.25}.mdpage h1{font-size:18px}.mdpage h2{font-size:15px}.mdpage h3,.mdpage h4{font-size:13px}.mdpage p,.mdpage ul,.mdpage ol,.mdpage blockquote{margin:6px 0}.mdpage ul,.mdpage ol{padding-left:22px}.mdpage a{color:var(--link)}.mdpage code{background:var(--card);border-radius:3px;padding:0 3px}.mdpage pre.mdcode{background:var(--card);border:1px solid var(--line);border-radius:4px;padding:8px;max-width:100%;overflow-x:auto;white-space:pre;overflow-wrap:normal}.mdpage pre.mdcode code{padding:0;background:none}.mdpage blockquote{border-left:3px solid var(--line);padding-left:10px;color:var(--dim)}.mdpage img{max-width:100%;height:auto;border-radius:4px;display:block;margin:6px 0}.mdpage hr{border:0;border-top:1px solid var(--line)}.mdpage .mdtable{max-width:100%;overflow-x:auto}.mdpage table{border-collapse:collapse;margin:6px 0}.mdpage th,.mdpage td{border:1px solid var(--line);padding:3px 8px;text-align:left;white-space:normal;overflow-wrap:anywhere;min-width:12ch}.tabhead{margin:0 0 8px}.newtrap{display:inline-flex;flex-wrap:wrap;gap:6px;align-items:center;font-size:12px;font-weight:normal;text-transform:none;letter-spacing:normal}.startcmds{margin:6px 0;font-size:11px;cursor:default}.reportview{width:100%;max-width:1100px;margin:0 auto;padding:20px 16px 48px;overflow-wrap:anywhere}.reportview h1{font-size:18px;margin:0 0 4px}.reportview .sub{color:var(--dim);font-size:12px;margin-bottom:10px}.reportview .sec{color:var(--dim);text-transform:uppercase;letter-spacing:0.08em;font-size:11px;margin:14px 0 4px}.reportview .mdpage{padding:12px 20px}@media(max-width:600px){.reportview{padding:12px 0 32px}.reportview .mdpage{padding:8px 12px}}.stats{display:flex;flex-direction:column;gap:var(--space-4);max-width:100%}.stats h2{margin:0 0 var(--space-2)}.stat-tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(100px,1fr));gap:var(--space-2)}.stat-tile{background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad)}.stat-tile .n{font-size:22px;font-weight:bold;color:var(--fg)}.stats-empty{display:flex;align-items:center;gap:var(--space-4);background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--space-4) var(--space-4) var(--space-3)}.stats-empty .pet-art{flex:none;margin-top:6px}.heat{background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad);min-width:0}.heat-scroll{overflow-x:auto;padding:2px 0 4px}.heatmap{border-collapse:separate;border-spacing:var(--heat-gap);width:auto;margin:0;background:none;border:0;border-radius:0;overflow:visible}.heatmap td{padding:0;border:0;vertical-align:middle;font-size:10px;line-height:1;color:var(--dim)}.heatmap .month{position:relative;height:14px}.heatmap .month span{position:absolute;left:0;bottom:3px;white-space:nowrap}.heatmap .wd{position:sticky;left:0;z-index:1;background:var(--card);padding-right:var(--space-1);white-space:nowrap}.heatmap .day,.heatmap .pad,.heat-legend .swatch{width:var(--heat-cell);min-width:var(--heat-cell);height:var(--heat-cell);border-radius:2px}.heatmap .day{position:relative;cursor:default;outline:1px solid rgba(255,255,255,0.04);outline-offset:-1px}.heatmap .day:focus-visible{outline:2px solid var(--link);outline-offset:0}.l0{background:var(--heat-0)}.l1{background:var(--heat-1)}.l2{background:var(--heat-2)}.l3{background:var(--heat-3)}.l4{background:var(--heat-4)}.heatmap .day:hover::after,.heatmap .day:focus::after{content:attr(data-tip);position:absolute;bottom:calc(100% + 6px);left:50%;transform:translateX(-50%);z-index:4;background:var(--bg);border:1px solid var(--line-hover);border-radius:var(--badge-radius);color:var(--fg);font-size:11px;padding:3px 7px;white-space:nowrap;pointer-events:none}.heatmap .day.below:hover::after,.heatmap .day.below:focus::after{bottom:auto;top:calc(100% + 6px)}.heatmap .day.tl:hover::after,.heatmap .day.tl:focus::after{left:0;transform:none}.heatmap .day.tr:hover::after,.heatmap .day.tr:focus::after{left:auto;right:0;transform:none}.heat-foot{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:var(--space-2);font-size:11px;margin-top:var(--space-1)}.heat-legend{display:inline-flex;align-items:center;gap:var(--heat-gap);color:var(--dim);margin-left:auto}.heat-legend .swatch{display:inline-block}.heat-legend .swatch:first-of-type{margin-left:var(--space-1)}.heat-legend .swatch:last-of-type{margin-right:var(--space-1)}.top-traps{background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad)}.top-traps ol{list-style:none;margin:0;padding:0;display:grid;gap:6px}.top-traps li{display:grid;grid-template-columns:minmax(0,14em) minmax(40px,32em) auto;justify-content:start;align-items:center;gap:var(--space-3)}.top-traps .name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.top-traps .bar{height:6px;background:var(--inset);border-radius:3px;overflow:hidden}.top-traps .bar span{display:block;height:100%;background:var(--lobstah)}.top-traps .n{color:var(--dim);white-space:nowrap}@media(max-width:600px){.tabs{flex-wrap:wrap}.tabs a{white-space:nowrap}}</style>\n </head>\n <body>\n <!-- The page is rendered by Preact (src/components/app.ts): header, tabs, controls, the five tab pages, footer, lobs, modal overlay. -->\n <script>"use strict";(()=>{var We,y,ys,Kr,F,$s,_s,ws,kt,He,$e,Ss,_t,bt,yt,Vr,Ue={},Fe=[],zr=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,Ke=Array.isArray;function L(e,t){for(var s in t)e[s]=t[s];return e}function wt(e){e&&e.parentNode&&e.parentNode.removeChild(e)}function Z(e,t,s){var n,r,o,a={};for(o in t)o=="key"?n=t[o]:o=="ref"?r=t[o]:a[o]=t[o];if(arguments.length>2&&(a.children=arguments.length>3?We.call(arguments,2):s),typeof e=="function"&&e.defaultProps!=null)for(o in e.defaultProps)a[o]===void 0&&(a[o]=e.defaultProps[o]);return je(e,a,n,r,null)}function je(e,t,s,n,r){var o={type:e,props:t,key:s,ref:n,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:r??++ys,__i:-1,__u:0};return r==null&&y.vnode!=null&&y.vnode(o),o}function ee(e){return e.children}function Be(e,t){this.props=e,this.context=t}function V(e,t){if(t==null)return e.__?V(e.__,e.__i+1):null;for(var s;t<e.__k.length;t++)if((s=e.__k[t])!=null&&s.__e!=null)return s.__e;return typeof e.type=="function"?V(e):null}function Jr(e){if(e.__P&&e.__d){var t=e.__v,s=t.__e,n=[],r=[],o=L({},t);o.__v=t.__v+1,y.vnode&&y.vnode(o),St(e.__P,o,t,e.__n,e.__P.namespaceURI,32&t.__u?[s]:null,n,s??V(t),!!(32&t.__u),r),o.__v=t.__v,o.__.__k[o.__i]=o,Ts(n,o,r),t.__e=t.__=null,o.__e!=s&&Ds(o)}}function Ds(e){if((e=e.__)!=null&&e.__c!=null)return e.__e=e.__c.base=null,e.__k.some(function(t){if(t!=null&&t.__e!=null)return e.__e=e.__c.base=t.__e}),Ds(e)}function vs(e){(!e.__d&&(e.__d=!0)&&F.push(e)&&!qe.__r++||$s!=y.debounceRendering)&&(($s=y.debounceRendering)||_s)(qe)}function qe(){try{for(var e,t=1;F.length;)F.length>t&&F.sort(ws),e=F.shift(),t=F.length,Jr(e)}finally{F.length=qe.__r=0}}function xs(e,t,s,n,r,o,a,l,u,c,d){var h,p,m,f,$,_,x=n&&n.__k||Fe,b=t.length;for(u=Yr(s,t,x,u,b),h=0;h<b;h++)(m=s.__k[h])!=null&&(p=m.__i!=-1&&x[m.__i]||Ue,m.__i=h,_=St(e,m,p,r,o,a,l,u,c,d),f=m.__e,m.ref&&p.ref!=m.ref&&(p.ref&&Dt(p.ref,null,m),d.push(m.ref,m.__c||f,m)),$==null&&f!=null&&($=f),4&m.__u?(u=Gs(m,u,e),p.__e&&(p.__e=null)):typeof m.type=="function"&&_!==void 0?u=_:f&&(u=f.nextSibling),m.__u&=-7);return s.__e=$,u}function Yr(e,t,s,n,r){var o,a,l,u,c,d=s.length,h=d,p=0;for(e.__k=new Array(r),o=0;o<r;o++)(a=t[o])!=null&&typeof a!="boolean"&&typeof a!="function"?(typeof a=="string"||typeof a=="number"||typeof a=="bigint"||a.constructor==String?a=e.__k[o]=je(null,a,null,null,null):Ke(a)?a=e.__k[o]=je(ee,{children:a},null,null,null):a.constructor===void 0&&a.__b>0?a=e.__k[o]=je(a.type,a.props,a.key,a.ref?a.ref:null,a.__v):e.__k[o]=a,u=o+p,a.__=e,a.__b=e.__b+1,l=null,(c=a.__i=Qr(a,s,u,h))!=-1&&(h--,(l=s[c])&&(l.__u|=2)),l==null||l.__v==null?(c==-1&&(r>d?p--:r<d&&p++),typeof a.type!="function"&&(a.__u|=4)):c!=u&&(c==u-1?p--:c==u+1?p++:(c>u?p--:p++,a.__u|=4))):e.__k[o]=null;if(h)for(o=0;o<d;o++)(l=s[o])!=null&&(2&l.__u)==0&&(l.__e==n&&(n=V(l)),Rs(l,l));return n}function Gs(e,t,s){var n,r;if(typeof e.type=="function"){for(n=e.__k,r=0;n&&r<n.length;r++)n[r]&&(n[r].__=e,t=Gs(n[r],t,s));return t}e.__e!=t&&(t&&e.type&&!t.parentNode&&(t=V(e)),t=s.insertBefore(e.__e,t||null));do t=t&&t.nextSibling;while(t!=null&&t.nodeType==8);return t}function Qr(e,t,s,n){var r,o,a,l=e.key,u=e.type,c=t[s],d=c!=null&&(2&c.__u)==0;if(c===null&&l==null||d&&l==c.key&&u==c.type)return s;if(n>(d?1:0)){for(r=s-1,o=s+1;r>=0||o<t.length;)if((c=t[a=r>=0?r--:o++])!=null&&(2&c.__u)==0&&l==c.key&&u==c.type)return a}return-1}function ks(e,t,s){t[0]=="-"?e.setProperty(t,s??""):e[t]=s==null?"":typeof s!="number"||zr.test(t)?s:s+"px"}function Ne(e,t,s,n,r){var o,a;e:if(t=="style")if(typeof s=="string")e.style.cssText=s;else{if(typeof n=="string"&&(e.style.cssText=n=""),n)for(t in n)s&&t in s||ks(e.style,t,"");if(s)for(t in s)n&&s[t]==n[t]||ks(e.style,t,s[t])}else if(t[0]=="o"&&t[1]=="n")o=t!=(t=t.replace(Ss,"$1")),a=t.toLowerCase(),t=a in e||t=="onFocusOut"||t=="onFocusIn"?a.slice(2):t.slice(2),e.l||(e.l={}),e.l[t+o]=s,s?n?s[$e]=n[$e]:(s[$e]=_t,e.addEventListener(t,o?yt:bt,o)):e.removeEventListener(t,o?yt:bt,o);else{if(r=="http://www.w3.org/2000/svg")t=t.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if(t!="width"&&t!="height"&&t!="href"&&t!="list"&&t!="form"&&t!="tabIndex"&&t!="download"&&t!="rowSpan"&&t!="colSpan"&&t!="role"&&t!="popover"&&t in e)try{e[t]=s??"";break e}catch{}typeof s=="function"||(s==null||s===!1&&t[4]!="-"?e.removeAttribute(t):e.setAttribute(t,t=="popover"&&s==1?"":s))}}function bs(e){return function(t){if(this.l){var s=this.l[t.type+e];if(t[He]==null)t[He]=_t++;else if(t[He]<s[$e])return;return s(y.event?y.event(t):t)}}}function St(e,t,s,n,r,o,a,l,u,c){var d,h,p,m,f,$,_,x,b,P,he,K,ge,gs,Le,vt,I=t.type;if(t.constructor!==void 0)return null;128&s.__u&&(u=!!(32&s.__u),o=[l=t.__e=s.__e]),(d=y.__b)&&d(t);e:if(typeof I=="function"){h=a.length;try{if(b=t.props,P=I.prototype&&I.prototype.render,he=(d=I.contextType)&&n[d.__c],K=d?he?he.props.value:d.__:n,s.__c?x=(p=t.__c=s.__c).__=p.__E:(P?t.__c=p=new I(b,K):(t.__c=p=new Be(b,K),p.constructor=I,p.render=Zr),he&&he.sub(p),p.state||(p.state={}),p.__n=n,m=p.__d=!0,p.__h=[],p._sb=[]),P&&p.__s==null&&(p.__s=p.state),P&&I.getDerivedStateFromProps!=null&&(p.__s==p.state&&(p.__s=L({},p.__s)),L(p.__s,I.getDerivedStateFromProps(b,p.__s))),f=p.props,$=p.state,p.__v=t,m)P&&I.getDerivedStateFromProps==null&&p.componentWillMount!=null&&p.componentWillMount(),P&&p.componentDidMount!=null&&p.__h.push(p.componentDidMount);else{if(P&&I.getDerivedStateFromProps==null&&b!==f&&p.componentWillReceiveProps!=null&&p.componentWillReceiveProps(b,K),t.__v==s.__v||!p.__e&&p.shouldComponentUpdate!=null&&p.shouldComponentUpdate(b,p.__s,K)===!1){t.__v!=s.__v&&(p.props=b,p.state=p.__s,p.__d=!1),t.__e=s.__e,t.__k=s.__k,t.__k.some(function(X){X&&(X.__=t)}),Fe.push.apply(p.__h,p._sb),p._sb=[],p.__h.length&&a.push(p),l=V(s);break e}p.componentWillUpdate!=null&&p.componentWillUpdate(b,p.__s,K),P&&p.componentDidUpdate!=null&&p.__h.push(function(){p.componentDidUpdate(f,$,_)})}if(p.context=K,p.props=b,p.__P=e,p.__e=!1,ge=y.__r,gs=0,P)p.state=p.__s,p.__d=!1,ge&&ge(t),d=p.render(p.props,p.state,p.context),Fe.push.apply(p.__h,p._sb),p._sb=[];else do p.__d=!1,ge&&ge(t),d=p.render(p.props,p.state,p.context),p.state=p.__s;while(p.__d&&++gs<25);p.state=p.__s,p.getChildContext!=null&&(n=L(L({},n),p.getChildContext())),P&&!m&&p.getSnapshotBeforeUpdate!=null&&(_=p.getSnapshotBeforeUpdate(f,$)),Le=d!=null&&d.type===ee&&d.key==null?As(d.props.children):d,l=xs(e,Ke(Le)?Le:[Le],t,s,n,r,o,a,l,u,c),p.base=t.__e,t.__u&=-161,p.__h.length&&a.push(p),x&&(p.__E=p.__=null)}catch(X){if(a.length=h,t.__v=null,u||o!=null){if(X.then){for(t.__u|=u?160:128;l&&l.nodeType==8&&l.nextSibling;)l=l.nextSibling;o!=null&&(o[o.indexOf(l)]=null),t.__e=l}else if(o!=null)for(vt=o.length;vt--;)wt(o[vt])}else t.__e=s.__e;t.__k==null&&(t.__k=s.__k||[]),X.then||Cs(t),y.__e(X,t,s)}}else o==null&&t.__v==s.__v?(t.__k=s.__k,t.__e=s.__e):l=t.__e=Xr(s.__e,t,s,n,r,o,a,u,c);return(d=y.diffed)&&d(t),128&t.__u?void 0:l}function Cs(e){e&&(e.__c&&(e.__c.__e=!0),e.__k&&e.__k.some(Cs))}function Ts(e,t,s){for(var n=0;n<s.length;n++)Dt(s[n],s[++n],s[++n]);y.__c&&y.__c(t,e),e.some(function(r){try{e=r.__h,r.__h=[],e.some(function(o){o.call(r)})}catch(o){y.__e(o,r.__v)}})}function As(e){return typeof e!="object"||e==null||e.__b>0?e:Ke(e)?e.map(As):e.constructor!==void 0?null:L({},e)}function Xr(e,t,s,n,r,o,a,l,u){var c,d,h,p,m,f,$,_=s.props||Ue,x=t.props,b=t.type;if(b=="svg"?r="http://www.w3.org/2000/svg":b=="math"?r="http://www.w3.org/1998/Math/MathML":r||(r="http://www.w3.org/1999/xhtml"),o!=null){for(c=0;c<o.length;c++)if((m=o[c])&&"setAttribute"in m==!!b&&(b?m.localName==b:m.nodeType==3)){e=m,o[c]=null;break}}if(e==null){if(b==null)return document.createTextNode(x);e=document.createElementNS(r,b,x.is&&x),l&&(y.__m&&y.__m(t,o),l=!1),o=null}if(b==null)_===x||l&&e.data==x||(e.data=x);else{if(o=b=="textarea"&&x.defaultValue!=null?null:o&&We.call(e.childNodes),!l&&o!=null)for(_={},c=0;c<e.attributes.length;c++)_[(m=e.attributes[c]).name]=m.value;for(c in _)m=_[c],c=="dangerouslySetInnerHTML"?h=m:c=="children"||c in x||c=="value"&&"defaultValue"in x||c=="checked"&&"defaultChecked"in x||Ne(e,c,null,m,r);for(c in x)m=x[c],c=="children"?p=m:c=="dangerouslySetInnerHTML"?d=m:c=="value"?f=m:c=="checked"?$=m:l&&typeof m!="function"||_[c]===m||Ne(e,c,m,_[c],r);if(d)l||h&&(d.__html==h.__html||d.__html==e.innerHTML)||(e.innerHTML=d.__html),t.__k=[];else if(h&&(e.innerHTML=""),xs(t.type=="template"?e.content:e,Ke(p)?p:[p],t,s,n,b=="foreignObject"?"http://www.w3.org/1999/xhtml":r,o,a,o?o[0]:s.__k&&V(s,0),l,u),o!=null)for(c=o.length;c--;)wt(o[c]);l&&b!="textarea"||(c="value",b=="progress"&&f==null?e.removeAttribute("value"):f!=null&&(f!==e[c]||b=="progress"&&!f||b=="option"&&f!=_[c])&&Ne(e,c,f,_[c],r),c="checked",$!=null&&$!=e[c]&&Ne(e,c,$,_[c],r))}return e}function Dt(e,t,s){try{if(typeof e=="function"){var n=typeof e.__u=="function";n&&e.__u(),n&&t==null||(e.__u=e(t))}else e.current=t}catch(r){y.__e(r,s)}}function Rs(e,t,s){var n,r;if(y.unmount&&y.unmount(e),(n=e.ref)&&(n.current&&n.current!=e.__e||Dt(n,null,t)),(n=e.__c)!=null){if(n.componentWillUnmount)try{n.componentWillUnmount()}catch(o){y.__e(o,t)}n.base=n.__P=n.__n=null}if(n=e.__k)for(r=0;r<n.length;r++)n[r]&&Rs(n[r],t,s||typeof e.type!="function");s||wt(e.__e),e.__c=e.__=e.__e=void 0}function Zr(e,t,s){return this.constructor(e,s)}function xt(e,t,s){var n,r,o,a;t==document&&(t=document.documentElement),y.__&&y.__(e,t),r=(n=typeof s=="function")?null:s&&s.__k||t.__k,o=[],a=[],St(t,e=(!n&&s||t).__k=Z(ee,null,[e]),r||Ue,Ue,t.namespaceURI,!n&&s?[s]:r?null:t.firstChild?We.call(t.childNodes):null,o,!n&&s?s:r?r.__e:t.firstChild,n,a),Ts(o,e,a),e.props.children=null}We=Fe.slice,y={__e:function(e,t,s,n){for(var r,o,a;t=t.__;)if((r=t.__c)&&!r.__)try{if((o=r.constructor)&&o.getDerivedStateFromError!=null&&(r.setState(o.getDerivedStateFromError(e)),a=r.__d),r.componentDidCatch!=null&&(r.componentDidCatch(e,n||{}),a=r.__d),a)return r.__E=r}catch(l){e=l}throw e}},ys=0,Kr=function(e){return e!=null&&e.constructor===void 0},Be.prototype.setState=function(e,t){var s;s=this.__s!=null&&this.__s!=this.state?this.__s:this.__s=L({},this.state),typeof e=="function"&&(e=e(L({},s),this.props)),e&&L(s,e),e!=null&&this.__v&&(t&&this._sb.push(t),vs(this))},Be.prototype.forceUpdate=function(e){this.__v&&(this.__e=!0,e&&this.__h.push(e),vs(this))},Be.prototype.render=ee,F=[],_s=typeof Promise=="function"?Promise.prototype.then.bind(Promise.resolve()):setTimeout,ws=function(e,t){return e.__v.__b-t.__v.__b},qe.__r=0,kt=Math.random().toString(8),He="__d"+kt,$e="__a"+kt,Ss=/(PointerCapture)$|Capture$/i,_t=0,bt=bs(!1),yt=bs(!0),Vr=0;var te=e=>e>999?"999+":String(e);function eo(e){return{id:e,lane:"work",bucket:"done",repo:"",title:"",verb:"unknown",sort:0}}var Ms=new WeakMap;function Gt(e,t){let s=e.dispatches||[],n=Ms.get(s);return n||Ms.set(s,n=new Map(s.map(r=>[r.id,r]))),{...t,catches:(t.catches||[]).map(r=>n.get(r)??eo(r))}}function E(e){if(!e)return"dim";let t=e.state||"open";return t==="open"&&e.merge?"pr-"+e.merge:t==="open"&&e.tone&&e.tone!=="ok"?e.tone:"pr-"+t}var se=["deck","dispatches","traps","prs","reports","notices","stats"];function Os(e){let t=String(e||"").replace(/^#/,"");return se.includes(t)?t:"deck"}var Ct=e=>`#decision/${encodeURIComponent(e)}`;function ne(e){let t=/^#?decision\\/(.+)$/.exec(String(e||""));if(!t)return null;try{return decodeURIComponent(t[1])}catch{return null}}var Tt=(e,t,s)=>`/attachment/dispatch/${encodeURIComponent(e)}/${encodeURIComponent(t)}/${encodeURIComponent(s)}`,Ls=(e,t)=>`/attachment/trap/${encodeURIComponent(e)}/${encodeURIComponent(t)}`,Ns=e=>/\\.(png|jpe?g|gif|webp)$/i.test(e),At=(e,t)=>`/decision/${encodeURIComponent(e)}/files/${encodeURIComponent(t)}`;function Ve(e,t){let s=new Map(t.map(r=>[r.key,r])),n=[];for(let r of e)if(r.kind==="decision"){let o=s.get(r.key);if(!o)continue;n.push({kind:"decision",key:o.key,title:o.title,detail:o.detail,options:o.options,attachments:o.attachments,...o.dispatch?{dispatch:o.dispatch,lane:o.lane??"work"}:{},...o.repo?{repo:o.repo}:{},at:o.askedAt,...o.viewedAt??r.viewedAt?{viewedAt:o.viewedAt??r.viewedAt}:{}})}else r.kind==="question"&&n.push({kind:"question",key:r.key,verb:r.verb,note:r.note||r.verb,dispatch:r.id,lane:r.lane,...r.repo?{repo:r.repo}:{},at:r.at??"",...r.viewedAt?{viewedAt:r.viewedAt}:{}});return n.sort((r,o)=>o.at.localeCompare(r.at)||r.key.localeCompare(o.key))}function Hs(e){let t=(e.text??"").split(`\n`).map(s=>s.trim()).find(Boolean)??"";return[e.option,t&&(t.length>80?t.slice(0,79)+"\\u2026":t),e.files?`${e.files} file${e.files===1?"":"s"}`:""].filter(Boolean).join(" \\xB7 ")}function js(e,t){return(e.reports||[]).find(s=>s.key===`report:${t.lane}:${t.id}`)}function ve(e){return e.author==="helm"?"":e.trap?e.trap:e.author==="headless"?e.dispatch?e.dispatch.slice(0,8):"":e.author}var Ps=(e,t)=>+!!e.acked-+!!t.acked||t.filedAt.localeCompare(e.filedAt),G=e=>`/report/${encodeURIComponent(e)}`;function ke(e){let t=/^#?report\\/(.+)$/.exec(String(e||""));if(!t)return null;try{return decodeURIComponent(t[1])}catch{return null}}function Bs(e){return typeof e!="string"?!1:e===""?!0:e.length>1024||!e.startsWith("#")||/[\\s\\x00-\\x1f\\x7f]/.test(e)?!1:se.includes(e.slice(1))||ne(e)!==null||ke(e)!==null}function Us(e){let t=/^\\/report\\/([^/?#]+)\\/?$/.exec(e);if(!t)return null;try{return decodeURIComponent(t[1])}catch{return null}}var Fs=e=>`/report/${encodeURIComponent(e)}/md`,Rt=(e,t)=>`/report/${encodeURIComponent(e)}/files/${encodeURIComponent(t)}`,Mt={dispatches:[],notices:[],prs:[],stacks:[]};function qs(e,t){if(t.kind==="dispatches"){let r=new Set(e.dispatches.map(o=>o.lane+":"+o.id));return{...e,dispatches:[...e.dispatches,...t.items.filter(o=>!r.has(o.lane+":"+o.id))]}}if(t.kind==="notices"){let r=new Set(e.notices.map(o=>o.seq));return{...e,notices:[...e.notices,...t.items.filter(o=>!r.has(o.seq))]}}let s=new Set(e.prs.map(r=>r.key)),n=new Set(e.stacks.map(r=>r.id));return{...e,prs:[...e.prs,...t.items.filter(r=>!s.has(r.key))],stacks:[...e.stacks,...t.stacks.filter(r=>!n.has(r.id))]}}function Ws(e,t){if(t===Mt||!t.dispatches.length&&!t.notices.length&&!t.prs.length)return e;let s=new Set(e.dispatches.map(a=>a.lane+":"+a.id)),n=new Set(e.notices.map(a=>a.seq)),r=new Set(e.prs.map(a=>a.key)),o=new Set(e.stacks.map(a=>a.id));return{...e,dispatches:[...e.dispatches,...t.dispatches.filter(a=>!s.has(a.lane+":"+a.id))],notices:[...e.notices,...t.notices.filter(a=>!n.has(a.seq))],prs:[...e.prs,...t.prs.filter(a=>!r.has(a.key))],stacks:[...e.stacks,...t.stacks.filter(a=>!o.has(a.id))]}}function Ks(e,t,s){return Math.max(0,(e.older?.[s]??0)-t[s].length)}function Vs(e,t){return{...e,now:t.now,daemon:e.daemon&&t.daemon!==void 0?{...e.daemon,heartbeat:t.daemon}:e.daemon,helms:e.helms.map(s=>t.helms[s.grounds]!==void 0?{...s,heartbeatAt:t.helms[s.grounds]}:s),traps:e.traps.map(s=>t.traps[s.trapId]?{...s,...t.traps[s.trapId]}:s)}}function Is(e,t,s){return!!e&&s-Date.parse(e)>t}function Es(e,t){if(t.lane&&e.lane!==t.lane||t.repo&&e.repo!==t.repo||t.verb&&e.verb!==t.verb)return!1;if(t.q){let s=t.q.toLowerCase();if(!(e.id+" "+(e.note||"")+" "+(e.title||"")+" "+(e.repo||"")+" "+(e.for||"")).toLowerCase().includes(s))return!1}return!0}function re(e,t,s){if(!t)return null;if(t.type==="helm")return e.helms.find(r=>r.grounds===t.key)||null;if(t.type==="dispatch"){let r=e.dispatches.find(a=>a.lane+":"+a.id===t.key),o=s&&s.key===t.key?s.data:void 0;return r&&o?{...r,...o}:r??o??null}if(t.type==="pr")return(e.prs||[]).find(r=>r.key===t.key)||null;if(t.type==="settings")return{attentionKinds:e.attentionKinds||[],attentionError:e.attentionError};let n=e.traps.find(r=>r.trapId===t.key);return n?Gt(e,n):null}function zs(e,t,s){let n=t.st,r=String(n.q||"").toLowerCase(),o=(...f)=>!r||f.join(" ").toLowerCase().includes(r),a=f=>({x:f,stale:Is(f.heartbeatAt,18e5,s)}),l=re(e,t.modal,t.detail),u=(f,$)=>!!f&&s-Date.parse(f)<=$,c=f=>Math.max(0,...(f.notices||[]).filter($=>$.kind==="trap-stowed"||$.kind==="trap-ghosted").map($=>Date.parse($.at)||0)),d=f=>f.live||!!f.starting||!!f.requested,h=[...e.traps||[]].sort((f,$)=>Number(d($))-Number(d(f))||Number($.live)-Number(f.live)||(f.live?(Date.parse(f.signedOnAt||"")||0)-(Date.parse($.signedOnAt||"")||0):c($)-c(f))||(f.name??f.trapId).localeCompare($.name??$.trapId)),p=h.filter(f=>(d(f)||(f.notices||[]).some($=>($.kind==="trap-stowed"||$.kind==="trap-ghosted")&&u($.at,36e5)))&&o(f.name,f.trapId,f.repo,f.worktree)),m=({ageSecs:f,...$})=>$;return{chips:{daemon:e.daemon,daemonStale:!!e.daemon&&Is(e.daemon.heartbeat,9e4,s),helms:e.helms.map(a),stats:e.stats},deck:{view:n.view,decisions:Ve(e.attention||[],e.decisions||[]).filter(f=>f.kind==="decision"?o(f.kind,f.repo,f.title,f.dispatch,f.detail):o(f.kind,f.repo,f.note,f.dispatch)),prAttention:(e.attention||[]).filter(f=>f.kind&&f.kind.startsWith("pr:")).map(m),landed:(e.landed||[]).filter(f=>u(f.at,864e5)&&o(f.repo,f.note,f.id)).sort((f,$)=>Date.parse($.at)-Date.parse(f.at)).slice(0,8),reports:(e.reports||[]).filter(f=>o(f.title,f.author,f.dispatch,f.repo)).sort(Ps),inflight:e.dispatches.filter(f=>f.bucket!=="done"&&Es(f,{...n,lane:"",repo:"",verb:""})),traps:p.map(f=>a(Gt(e,f))),stacks:(e.stacks||[]).filter(f=>f.open&&o(f.repo,f.numbers.join(" "))),prs:(e.prs||[]).filter(f=>f.state==="OPEN"),error:e.attentionError},dispatches:{view:n.view,chain:n.chain,list:e.dispatches.filter(f=>Es(f,n))},traps:{view:n.view,list:h.filter(f=>(!n.repo||f.repo===n.repo)&&o(f.name,f.trapId,f.repo,f.worktree,f.harness)).map(f=>a(Gt(e,f)))},prs:{view:n.view,stacks:(e.stacks||[]).filter(f=>!n.repo||f.repo===n.repo),prs:(e.prs||[]).filter(f=>(!n.repo||f.repo===n.repo)&&o(f.number,f.title,f.url,f.state,f.baseRefName,f.headRefName)),watches:(e.watches||[]).filter(f=>!String(f.key).startsWith("pr:"))},reports:{view:n.view,list:(e.reports||[]).filter(f=>(!n.repo||f.repo===n.repo)&&o(f.title,f.author,f.dispatch,f.repo)).sort(Ps)},notices:{list:(e.notices||[]).filter(f=>(!n.repo||!f.repo||f.repo===n.repo)&&(!n.noticeKind||f.kind===n.noticeKind)&&o(f.kind,f.text,f.repo))},foot:{version:e.version,repoUrl:e.repoUrl},modal:{modal:t.modal,item:l&&a(l),prefs:t.modal&&t.modal.type==="settings"?{view:n.view,lobs:n.lobs}:void 0}}}function Js(e,t){let s=(e.prs||[]).find(l=>l.key===t);if(!s)return null;let n=(e.stacks||[]).find(l=>l.id===s.stackId),r=new Map((e.dispatches||[]).map(l=>[l.id,l])),o=(s.dispatchIds||[]).map(l=>{let u=r.get(l);return u?{id:l,verb:u.verb,modalKey:u.lane+":"+u.id}:{id:l,culled:!0}}),a=s.watch;return{pr:s,stack:n?{numbers:n.numbers,titles:n.numbers.map(l=>(e.prs||[]).find(u=>u.stackId===n.id&&u.number===l)?.title||""),position:s.position+1,size:n.numbers.length,floor:n.floor,nextNumber:n.nextNumber,nextMergeable:!!s.nextMergeable,blockedBy:s.blockedBy}:null,chain:o,watch:a?{key:a.key,owner:a.owner||"",lastCheckedAt:a.lastCheckedAt||null,lastError:a.lastError||null,cursor:a.cursor}:null}}function Ys(e){return e?{text:e.lastError?"failing":"watching",at:e.lastCheckedAt||null}:{text:"no watch",at:null}}function ze(e,t){return e.filter(s=>!t(s.key)).sort((s,n)=>s.at.localeCompare(n.at)||s.key.localeCompare(n.key))}function Qs(e,t,s){return ze(e,t).filter(n=>!n.viewedAt&&!s(n.key))}function Xs(){let e={view:"table",lane:"",repo:"",verb:"",q:"",lobs:!0,chain:!1,noticeKind:""};try{Object.assign(e,JSON.parse(localStorage.getItem("spyglass")||"{}"))}catch{}return e.view!=="table"&&e.view!=="cards"&&(e.view="table"),e.lobs=e.lobs!==!1,e}function Zs(e){try{localStorage.setItem("spyglass",JSON.stringify(e))}catch{}}function en(){try{return JSON.parse(localStorage.getItem("spyglass-lob-hidden")||"{}")||{}}catch{return{}}}function tn(e){try{localStorage.setItem("spyglass-lob-hidden",JSON.stringify(e))}catch{}}var be,Je;function oe(e){if(e.snapshot)return(Je?.snapshot!==e.snapshot||Je.older!==e.older)&&(Je={snapshot:e.snapshot,older:e.older,view:Ws(e.snapshot,e.older)}),Je.view}var sn=new Set,g=()=>be;function Pt(e){be=e}function k(e){be={...be,...e};for(let t of sn)t(be)}var It=e=>{sn.add(e)};function to(e,t){let s=oe(e);return s&&re(s,t,e.detail)?!0:t.type==="dispatch"&&e.detail?.key===t.key&&!e.detail.error}function Ye(e){let{modal:t,drafts:s,older:n,detail:r,decisionModal:o}=g(),a=Ve(e.attention||[],e.decisions||[]),l=new Set(a.map(c=>c.key)),u=Object.fromEntries(Object.entries(s).filter(([c])=>l.has(c)));k({snapshot:e,stale:!1,modal:t&&to({snapshot:e,older:n,detail:r},t)?t:null,...Object.keys(u).length!==Object.keys(s).length?{drafts:u}:{},...o?.key&&!l.has(o.key)&&!s[o.key]?.sent?{decisionModal:nn(a,u,o.key)}:{}})}function nn(e,t,s){let n=ze(e,o=>!!t[o]?.sent||o===s);if(!n.length)return null;let r=e.find(o=>o.key===s)?.at??"";return{key:(n.find(o=>o.at>r||o.at===r&&o.key>(s??""))??n[0]).key}}function Qe(){let e=oe(g());return e?Ve(e.attention||[],e.decisions||[]):[]}function ye(){let{drafts:e}=g();return ze(Qe(),t=>!!e[t]?.sent)}function Xe(){let{drafts:e,viewedHere:t}=g();return Qs(Qe(),s=>!!e[s]?.sent,s=>!!t[s])}function ae(e){let t=e??Xe()[0]?.key??ye()[0]?.key;t&&k({decisionModal:{key:t}})}function _e(e){let t=g().decisionModal;if(!t?.key)return;let s=ye(),n=s.findIndex(o=>o.key===t.key),r=s[n+e];n>=0&&r&&k({decisionModal:{key:r.key}})}var ie=()=>k({decisionModal:null}),we=9;function so(){let e=g().decisionModal?.key;return e?ye().find(t=>t.key===e):void 0}function rn(e){let t=so();if(!t||e<1||e>we)return!1;let s=g().drafts[t.key];if(s?.sending||s?.sent)return!1;let n=t.kind==="decision"?t.options:[];return e<=n.length?(A(t.key,{option:n[e-1],error:void 0}),document.querySelectorAll(".dmodal .dopt")[e-1]?.focus(),!0):e===n.length+1?(document.querySelector(".dmodal textarea.danswer")?.focus(),!0):!1}async function on(e){let t=g();if(t.viewedHere[e]||(k({viewedHere:{...t.viewedHere,[e]:!0}}),Qe().find(n=>n.key===e)?.viewedAt))return;let s=t.snapshot?.focusToken;if(s)try{await fetch("/requests",{method:"POST",headers:{"content-type":"application/json","x-lobstah-token":s},body:JSON.stringify({kind:"decision-viewed",payload:{key:e}})})}catch{}}function an(){k({alertDismissed:Xe().map(e=>e.key)})}function ln(e){let{snapshot:t}=g();k({stale:!1,...t?{snapshot:Vs(t,e)}:{}})}async function Ot(){let{modal:e}=g();if(e?.type!=="dispatch")return;let t=e.key,s=t.slice(t.indexOf(":")+1),n;try{let o=await fetch(`/data/dispatch/${encodeURIComponent(s)}`,{cache:"no-store"});n=o.ok?{data:await o.json()}:{error:o.status===404?"this dispatch is gone":`the detail answered ${o.status}`}}catch{n={error:"the detail could not be read"}}let r=g();r.modal?.type!=="dispatch"||r.modal.key!==t||n.error&&r.detail?.key===t&&r.detail.data||k({detail:{key:t,...n}})}var Et=!1;async function Se(){if(!Et){Et=!0;try{let e=await fetch("/data/stats",{cache:"no-store"});if(!e.ok)throw new Error(`/data/stats answered ${e.status}`);k({stats:await e.json(),statsError:null})}catch{k({statsError:"stats could not be read"})}finally{Et=!1}}}var Lt=e=>{g().statsFocus!==e&&k({statsFocus:e})};async function dn(e){let{olderLoading:t,older:s}=g();if(!t){k({olderLoading:e,olderError:null});try{let n=await fetch(`/data/older?kind=${e}&offset=${s[e].length}&limit=50`,{cache:"no-store"});if(!n.ok)throw new Error(`/data/older answered ${n.status}`);let r=await n.json();k({older:qs(g().older,r),olderLoading:null})}catch{k({olderLoading:null,olderError:{kind:e,text:"older records could not be read"}})}}}var cn=()=>({text:"",files:[]});function A(e,t){let s=g().drafts;k({drafts:{...s,[e]:{...s[e]??cn(),...t}}})}function pn(e,t){let s=g().drafts[e]?.option;A(e,{option:s===t?void 0:t,error:void 0})}var no=e=>new Promise((t,s)=>{let n=new FileReader;n.onload=()=>t(String(n.result).replace(/^data:[^,]*,/,"")),n.onerror=()=>s(n.error),n.readAsDataURL(e)});async function Nt(e,t){let s=g().snapshot?.answerLimits,n=[...t??[]],r=g().drafts[e]?.files??[],o=[],a=[];for(let l of n){let u=(/\\.[^.]+$/.exec(l.name)?.[0]??"").toLowerCase();if(s&&!s.extensions.includes(u))o.push(`${l.name}: type not accepted`);else if(s&&l.size>s.maxBytes)o.push(`${l.name}: larger than ${s.maxBytes} bytes`);else if(s&&r.length+a.length>=s.maxFiles)o.push(`${l.name}: at most ${s.maxFiles} files`);else try{a.push({name:l.name,bytes:l.size,data:await no(l)})}catch{o.push(`${l.name}: could not be read`)}}A(e,{files:[...g().drafts[e]?.files??[],...a],error:o.length?o.join("; "):void 0})}var ro={"image/png":".png","image/jpeg":".jpg","image/gif":".gif","image/webp":".webp"};async function un(e,t){let s=[...t.clipboardData?.items??[]],n=new Date().toISOString().replace(/[:.]/g,"-").replace(/-\\d{3}Z$/,"Z"),r=s.filter(o=>o.kind==="file"&&o.type.startsWith("image/")).map(o=>o.getAsFile()).filter(o=>o!==null).map((o,a,l)=>{let u=`pasted-${n}${l.length>1?`-${a+1}`:""}${ro[o.type]??".png"}`;return new File([o],u,{type:o.type})});r.length&&await Nt(e,r)}function fn(e,t){let s=(g().drafts[e]?.files??[]).filter((n,r)=>r!==t);A(e,{files:s})}async function Ze(e){let t=g().snapshot?.focusToken,s=g().drafts[e]??cn();if(!(s.sending||s.sent)){if(!s.option&&!s.text.trim()&&s.files.length===0){A(e,{error:"Choose an option, write an answer, or attach a file."});return}if(!t){A(e,{error:"This page has no answer token; reload it."});return}A(e,{sending:!0,error:void 0});try{let n=await fetch("/requests",{method:"POST",headers:{"content-type":"application/json","x-lobstah-token":t},body:JSON.stringify({kind:"decision-answer",payload:{key:e,...s.option?{option:s.option}:{},...s.text.trim()?{text:s.text}:{},files:s.files.map(a=>({name:a.name,data:a.data}))}})}),r=await n.json();if(!n.ok||!r.ok){A(e,{sending:!1,error:r.reason??`The answer was refused (${n.status}).`});return}A(e,{sending:!1,sent:Hs({option:s.option,text:s.text,files:s.files.length})}),g().decisionModal?.key===e&&k({decisionModal:nn(Qe(),g().drafts,e)??{key:null}})}catch{A(e,{sending:!1,error:"The answer could not be sent."})}}}var mn=()=>k({stale:!0}),hn=(e,t)=>k({lightbox:{src:e,name:t}}),le=()=>k({lightbox:null});function O(e,t){let s=g(),n={type:e,key:t};if(e==="dispatch"){k({modal:n,detail:s.detail?.key===t?s.detail:{key:t}}),Ot();return}let r=oe(s);k({modal:!r||re(r,n)?n:null,detail:null})}function de(){k({modal:null,detail:null})}async function gn(e){let t=g().snapshot?.focusToken;if(t)try{let n=await(await fetch(`/api/focus/${encodeURIComponent(e)}`,{method:"POST",headers:{"x-lobstah-focus-token":t}})).json();k({focusResults:{...g().focusResults,[e]:n.message??n.reason??"Window focus failed."}})}catch{k({focusResults:{...g().focusResults,[e]:"Window focus failed."}})}}async function $n(e,t){let s=g().snapshot?.focusToken;if(!s)return"The glass has no page token yet.";try{let r=await(await fetch("/requests",{method:"POST",headers:{"content-type":"application/json","x-lobstah-token":s},body:JSON.stringify({kind:"trap-request",payload:{repo:e,harness:t}})})).json();if(!r.ok)return r.reason??"The request was refused."}catch{return"The request could not be sent."}try{Ye(await(await fetch("/data")).json())}catch{}}function N(e){let t={...g().prefs,...e};Zs(t),k({prefs:t})}function vn(e){e!=="table"&&e!=="cards"||N({view:e})}var kn=e=>N({lobs:e==="on"});function bn(e,t){let s={...g().lobHidden,[e]:t};tn(s),setTimeout(()=>k({lobHidden:s}),0)}async function yn(e){try{return await navigator.clipboard.writeText(e),!0}catch{return!1}}var wn=function(e,t,s,n){var r;t[0]=0;for(var o=1;o<t.length;o++){var a=t[o++],l=t[o]?(t[0]|=a?1:2,s[t[o++]]):t[++o];a===3?n[0]=l:a===4?n[1]=Object.assign(n[1]||{},l):a===5?(n[1]=n[1]||{})[t[++o]]=l:a===6?n[1][t[++o]]+=l+"":a?(r=e.apply(l,wn(e,l,s,["",null])),n.push(r),l[0]?t[0]|=2:(t[o-2]=0,t[o]=r)):n.push(l)}return n},_n=new Map;function Sn(e){var t=_n.get(this);return t||(t=new Map,_n.set(this,t)),(t=wn(this,t.get(e)||(t.set(e,t=(function(s){for(var n,r,o=1,a="",l="",u=[0],c=function(p){o===1&&(p||(a=a.replace(/^\\s*\\n\\s*|\\s*\\n\\s*$/g,"")))?u.push(0,p,a):o===3&&(p||a)?(u.push(3,p,a),o=2):o===2&&a==="..."&&p?u.push(4,p,0):o===2&&a&&!p?u.push(5,0,!0,a):o>=5&&((a||!p&&o===5)&&(u.push(o,0,a,r),o=6),p&&(u.push(o,p,0,r),o=6)),a=""},d=0;d<s.length;d++){d&&(o===1&&c(),c(d));for(var h=0;h<s[d].length;h++)n=s[d][h],o===1?n==="<"?(c(),u=[u],o=3):a+=n:o===4?a==="--"&&n===">"?(o=1,a=""):a=n+a[0]:l?n===l?l="":a+=n:n===\'"\'||n==="\'"?l=n:n===">"?(c(),o=1):o&&(n==="="?(o=5,r=a,a=""):n==="/"&&(o<5||s[d][h+1]===">")?(c(),o===3&&(u=u[0]),o=u,(u=u[0]).push(2,0,o),o=0):n===" "||n===" "||n===`\n`||n==="\\r"?(c(),o=2):a+=n),o===3&&a==="!--"&&(o=4,u=u[0])}return c(),u})(e)),t),arguments,[])).length>1?t:t[0]}var Dn=e=>Array.isArray(e)?e.map(Dn):e===""?null:e,oo=(e,t,...s)=>Z(e,t,...s.map(Dn)),i=Sn.bind(oo);var De,w,Ht,xn,tt=0,In=[],D=y,Gn=D.__b,Cn=D.__r,Tn=D.diffed,An=D.__c,Rn=D.unmount,Mn=D.__;function Bt(e,t){D.__h&&D.__h(w,e,tt||t),tt=0;var s=w.__H||(w.__H={__:[],__h:[]});return e>=s.__.length&&s.__.push({}),s.__[e]}function z(e){return tt=1,ao(On,e)}function ao(e,t,s){var n=Bt(De++,2);if(n.t=e,!n.__c&&(n.__=[s?s(t):On(void 0,t),function(l){var u=n.__N?n.__N[0]:n.__[0],c=n.t(u,l);u!==c&&(n.__N=[c,n.__[1]],n.__c.setState({}))}],n.__c=w,!w.__f)){var r=function(l,u,c){if(!n.__c.__H)return!0;var d=!1,h=n.__c.props!==l;if(n.__c.__H.__.some(function(m){if(m.__N){d=!0;var f=m.__[0];m.__=m.__N,m.__N=void 0,f!==m.__[0]&&(h=!0)}}),o){var p=o.call(this,l,u,c);return d?p||h:p}return!d||h};w.__f=!0;var o=w.shouldComponentUpdate,a=w.componentWillUpdate;w.componentWillUpdate=function(l,u,c){if(this.__e){var d=o;o=void 0,r(l,u,c),o=d}a&&a.call(this,l,u,c)},w.shouldComponentUpdate=r}return n.__N||n.__}function Ut(e,t){var s=Bt(De++,3);!D.__s&&En(s.__H,t)&&(s.__=e,s.u=t,w.__H.__h.push(s))}function Ft(e){return tt=5,qt(function(){return{current:e}},[])}function qt(e,t){var s=Bt(De++,7);return En(s.__H,t)&&(s.__=e(),s.__H=t,s.__h=e),s.__}function io(){for(var e;e=In.shift();){var t=e.__H;if(e.__P&&t)try{t.__h.some(et),t.__h.some(jt),t.__h=[]}catch(s){t.__h=[],D.__e(s,e.__v)}}}D.__b=function(e){w=null,Gn&&Gn(e)},D.__=function(e,t){e&&t.__k&&t.__k.__m&&(e.__m=t.__k.__m),Mn&&Mn(e,t)},D.__r=function(e){Cn&&Cn(e),De=0;var t=(w=e.__c).__H;t&&(Ht===w?(t.__h=[],w.__h=[],t.__.some(function(s){s.__N&&(s.__=s.__N),s.u=s.__N=void 0})):(t.__h.some(et),t.__h.some(jt),t.__h=[],De=0)),Ht=w},D.diffed=function(e){Tn&&Tn(e);var t=e.__c;t&&t.__H&&(t.__H.__h.length&&(In.push(t)!==1&&xn===D.requestAnimationFrame||((xn=D.requestAnimationFrame)||lo)(io)),t.__H.__.some(function(s){s.u&&(s.__H=s.u,s.u=void 0)})),Ht=w=null},D.__c=function(e,t){t.some(function(s){try{s.__h.some(et),s.__h=s.__h.filter(function(n){return!n.__||jt(n)})}catch(n){t.some(function(r){r.__h&&(r.__h=[])}),t=[],D.__e(n,s.__v)}}),An&&An(e,t)},D.unmount=function(e){Rn&&Rn(e);var t,s=e.__c;s&&s.__H&&(s.__H.__.some(function(n){try{et(n)}catch(r){t=r}}),s.__H=void 0,t&&D.__e(t,s.__v))};var Pn=typeof requestAnimationFrame=="function";function lo(e){var t,s=function(){clearTimeout(n),Pn&&cancelAnimationFrame(t),setTimeout(e)},n=setTimeout(s,35);Pn&&(t=requestAnimationFrame(s))}function et(e){var t=w,s=e.__c;typeof s=="function"&&(e.__c=void 0,s()),w=t}function jt(e){var t=w;e.__c=e.__(),w=t}function En(e,t){return!e||e.length!==t.length||t.some(function(s,n){return s!==e[n]})}function On(e,t){return typeof t=="function"?t(e):t}var co=["none","minimal","low","medium","high","xhigh","max","ultra"],po=["default","acceptEdits","plan","dontAsk","bypassPermissions","auto"];function Ln(e){return typeof e=="string"&&/^[a-zA-Z0-9][a-zA-Z0-9._:/-]{0,127}$/.test(e)?e:null}function Nn(e,t){return typeof e=="string"&&t.includes(e)?e:null}function Wt(e={}){return{harness:Ln(e.harness),model:Ln(e.model),config:{effort:Nn(e.config?.effort,co),permissionMode:Nn(e.config?.permissionMode,po)},...typeof e.observedAt=="string"&&/^\\d{4}-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d\\.\\d{3}Z$/.test(e.observedAt)&&Number.isFinite(Date.parse(e.observedAt))?{observedAt:e.observedAt}:{}}}var J=e=>{if(!e)return"";let t=Math.max(0,(Date.now()-Date.parse(e))/1e3);return t<90?Math.round(t)+"s":t<5400?Math.round(t/60)+"m":t<172800?(t/3600).toFixed(1)+"h":Math.round(t/86400)+"d"},v=e=>i`<span data-age=${e??""}>${J(e)}</span>`;function H({worker:e,label:t,cls:s="badge"}){let n=Wt(e),r=[n.model??"model unknown",n.config.effort].filter(Boolean).join(" \\xB7 "),[o,a]=z(null),l=u=>{let c=u.currentTarget.getBoundingClientRect();a({left:Math.max(8,Math.min(c.left,window.innerWidth-280)),top:c.bottom+6})};return i`<span class=${s+" worker-harness"} tabindex="0" aria-label=${r}\n onMouseEnter=${l} onFocus=${l} onBlur=${()=>a(null)}\n onMouseLeave=${u=>{document.activeElement!==u.currentTarget&&a(null)}}\n onKeyDown=${u=>{u.key==="Escape"&&a(null)}}>\n ${t??n.harness??"unknown harness"}${o&&!g().modal&&i`<span class="worker-tooltip" role="tooltip" aria-hidden="true" style=${o}>${r}</span>`}\n </span>`}var Kt=e=>e.worker&&i`<${H} worker=${e.worker} />`;function nt({worker:e,headless:t=!1}){let s=Wt(e);return i`<section class="worker-details"><div class="sec">${t?"headless worker \\xB7 launch / observation":"worker observation"}</div><dl>\n <dt>harness</dt><dd>${s.harness??"unknown"}</dd>\n <dt>model</dt><dd>${s.model??"model unknown"}</dd>\n <dt>effort</dt><dd>${s.config.effort??"unknown"}</dd>\n <dt>permission mode</dt><dd>${s.config.permissionMode??"unknown"}</dd>\n <dt>observed</dt><dd>${s.observedAt??"unknown"}</dd>\n </dl></section>`}var rt=e=>e.activity&&i`<div class=${"activity"+(e.activity.stale?" stale":"")} title=${e.activity.stale?"no activity past the wedge threshold":e.activity.kind}>${e.activity.stale?"stale \\xB7 ":""}${e.activity.summary} \xB7 ${v(e.activity.at)} ago</div>`,uo=e=>e&&/^https?:\\/\\//i.test(e)?e:void 0,ot=e=>{let t=e.waiting;if(!t)return;let s=uo(t.link),n=s;try{s&&(n=new URL(s).host+new URL(s).pathname.replace(/\\/$/,""))}catch{n=s}return i`<div class="waiting">${e.verb}: waiting on ${t.on} \xB7 ${v(t.since)}${s&&[" \\xB7 ",i`<a href=${s} target="_blank" rel="noopener noreferrer" onClick=${C}>${n}</a>`]}</div>`},fo=12,Hn=e=>e!==void 0&&e.length>fo,j=e=>Hn(e)?e:void 0,B=e=>Hn(e)?" long":"",S=(e,t)=>()=>O(e,t),C=e=>e.stopPropagation(),mo=e=>e.sessionId?(e.harness==="codex"?"codex resume ":"claude --resume ")+e.sessionId:void 0;function Y(e){if(e.requested)return i`<span class="dim">Requested \u2014 no window yet</span>`;if(e.starting)return i`<span class="dim">${e.starting.failedAt?"Did not start":"Starting \\u2014 no window yet"}</span>`;if(!e.live){let n=mo(e);return n?i`<span class="dim">Resume: <code onClick=${C}>${n}</code></span>`:i`<span class="dim">Resume command unavailable</span>`}let t=g();return!t.snapshot?.focusSupported&&!e.link?i`<span class="dim">Window focus is not supported here</span>`:i`<span class="winaction"><button class="btn open" title="open this trap\'s window" onClick=${n=>{C(n),gn(e.trapId)}}>\u2197 open</button>${e.link&&[" ",i`<a href=${e.link} onClick=${C}>Session link</a>`]}${t.focusResults[e.trapId]&&[" ",i`<span class="dim">${t.focusResults[e.trapId]}</span>`]}</span>`}function M(e,t,s){return t.length?i`<div class="wrap"><table><tbody><tr>${e.map(n=>i`<th>${n}</th>`)}</tr>${t}</tbody></table></div>`:i`<div class="empty">${s}</div>`}function pe({kind:e,more:t}){return!t.left&&!t.error?null:i`<div class="older">${t.left>0&&i`<button class="btn" disabled=${t.loading} onClick=${()=>{dn(e)}}>${t.loading?"loading\\u2026":`show ${Math.min(t.left,50)} older (of ${t.left})`}</button>`}${t.error&&i` <span class="bad">${t.error}</span>`}</div>`}function ho({text:e}){let[t,s]=z(null),n=r=>async()=>{await yn(e)&&(s(r),setTimeout(()=>s(null),1e3))};return i`<div class="cmd"><code class=${t==="code"?"copied":void 0} title="click to copy" onClick=${n("code")}>${e}</code><button title="copy" onClick=${n("button")}>${t==="button"?"\\u2713":"\\u29C9"}</button></div>`}var T=(e,t)=>i`<${ho} key=${t} text=${e} />`,xe=(e,t,s="thumb")=>i`<button\n type="button"\n class=${s}\n title=${"view "+t}\n onClick=${n=>{C(n),hn(e,t)}}\n >\n <img src=${e} alt=${t} loading="lazy" />\n </button>`,ce=(e,t)=>e.map(s=>[i`<div class="sub">${s.name} \xB7 ${s.type} \xB7 ${s.bytes} bytes</div>`,t&&Ns(s.name)&&xe(t(s.name),s.name),T(s.path)]),jn=/\\bwt:([a-z0-9][a-z0-9-]*)/g,Vt=e=>g().snapshot?.trapNames?.[e],U=e=>e.replace(jn,(t,s,n)=>{let r=Vt(s);return r&&!Bn(e,n,t.length,r)?r:t}),Bn=(e,t,s,n)=>e.slice(Math.max(0,t-n.length-2),t)===`${n} (`&&e[t+s]===")";function at(e){let t="wt:"+e,s=Vt(e)??t;return g().snapshot?.traps.some(r=>r.trapId===e)?i`<a class="trapname" href="#traps" title=${t} onClick=${r=>{r.preventDefault(),C(r),O("trap",e)}}>${s}</a>`:i`<span class="trapname" title=${t}>${s}</span>`}var st=e=>{let t=/^wt:([a-z0-9][a-z0-9-]*)$/.exec(e);return t?at(t[1]):e};function R(e,t=1/0){let s=[],n=0;for(let l of e.matchAll(jn)){l.index>n&&s.push({text:e.slice(n,l.index)});let u=Vt(l[1]);s.push(u&&Bn(e,l.index,l[0].length,u)?{text:l[0]}:{text:u??l[0],trapId:l[1]}),n=l.index+l[0].length}n<e.length&&s.push({text:e.slice(n)});let r=s.reduce((l,u)=>l+u.text.length,0),o=r>t?t-1:1/0,a=[];for(let l of s){if(o<=0)break;l.text.length<=o?a.push(l.trapId?at(l.trapId):l.text):a.push(l.text.slice(0,o)),o-=l.text.length}return r>t&&a.push("\\u2026"),a}var go=e=>e.log.flatMap((t,s)=>[s?`\n`:"",t.at+" "+t.verb,...t.note?[" ",...R(t.note)]:[]]),$o=e=>"brief"in e;function Un(e,t){let s=[e.elapsed&&`elapsed: ${e.elapsed}`,e.attempt&&`attempt: ${e.attempt}`,e.branch&&`branch: ${e.branch}`,e.lastCommit&&`last commit: ${e.lastCommit}`,e.aheadTrunk&&`commits: ${e.aheadTrunk}`,e.draftPr&&`draft PR: ${e.draftPr}`,e.updated&&`updated: ${e.updated}`].filter(Boolean).join(`\n`),n=i`${e.waiting&&[i`<div class="sec">waiting</div>`,ot(e)]}${e.activity&&[i`<div class="sec">activity</div>`,rt(e)]}${s&&[i`<div class="sec">progress</div>`,i`<pre>${s}</pre>`]}`,r=e.evidence&&{...e.evidence};return r?.worker&&(delete r.worker,delete r.harness),$o(e)?i`${n}<div class="sec">brief</div><pre>${e.brief}</pre>${e.attachments.length>0&&[i`<div class="sec">attachments (${e.attachments.length})</div>`,ce(e.attachments,o=>Tt(e.lane,e.id,o))]}${e.messageAttachments.length>0&&[i`<div class="sec">message attachments (${e.messageAttachments.length})</div>`,ce(e.messageAttachments,o=>Tt(e.lane,e.id,o))]}${e.followUp&&[i`<div class="sec">forks</div>`,i`<pre>${e.followUp}</pre>`]}<div class="sec">log</div><div class="loglines">${e.log.length?go(e):"no entries yet"}</div>${e.inbox.length>0&&[i`<div class="sec">inbox</div>`,i`<div class="loglines">${e.inbox.join(`\n---\n`)}</div>`]}${e.awaitingReply&&[i`<div class="sec">awaiting reply</div>`,i`<div>${e.awaitingReply.line} \xB7 from ${e.awaitingReply.from} \xB7 ${v(e.awaitingReply.sentAt)} ago</div>`]}${r&&[i`<div class="sec">evidence</div>`,i`<div class="loglines">${JSON.stringify(r)}</div>`]}`:i`${n}<div class="sec">brief</div><div class=${t?.error?"bad":"dim"}>${t?.error??"loading\\u2026"}</div>${e.followUp&&[i`<div class="sec">forks</div>`,i`<pre>${e.followUp}</pre>`]}${e.note&&[i`<div class="sec">last note</div>`,i`<div class="loglines">${R(e.note)}</div>`]}`}function Ge(e){return e.for?[st(e.for),e.evidence&&e.evidence.deliveredTo?[" ",i`<span class="ok">\u2713delivered</span>`]:[" ",i`<span class="warn">waiting</span>`]]:e.claimedBy?st(e.claimedBy):""}function ue(e){if(e.prList&&e.prList.length>1)return[e.prList.map((r,o)=>[o?" \\u2192 ":"",i`<a href=${r.url} target="_blank" rel="noopener" onClick=${C}>#${r.number}</a>`,r.badge&&[" ",i`<span class=${"badge "+E(r.badge)}>${r.badge.text}</span>`]]),e.prGate&&[" ",i`<span class="badge dim" title="merge gate (pick)">${e.prGate}</span>`]];let t=e.evidence&&(e.evidence.prUrl||e.evidence.pr&&e.evidence.pr.url);if(!t)return"";let s=e.prBadge,n=e.evidence&&e.evidence.prUrls&&e.evidence.prUrls.length>1?e.evidence.prUrls:void 0;return[n?n.map((r,o)=>[o?" ":"",i`<a href=${r} target="_blank" rel="noopener" onClick=${C}>#${r.split("/").pop()}</a>`]):i`<a href=${t} target="_blank" rel="noopener" onClick=${C}>PR</a>`,s&&[" ",i`<span class=${"badge "+E(s)} title=${"observed "+s.observedAt}>${s.text}</span>`],e.prGate&&[" ",i`<span class="badge dim" title="merge gate (pick)">${e.prGate}</span>`]]}var vo={"pr:draft":"draft","pr:review":"review","pr:checks":"checks","pr:conflict":"conflicts","pr:ready":"ready","stack-ready":"stack ready",landed:"landed",watch:"watch"},zt={"pr:review":"bad","pr:checks":"bad","pr:conflict":"pr-conflicts","pr:ready":"ok","stack-ready":"ok","pr:draft":"dim",watch:"warn"},Fn=e=>vo[e]||"";var it=e=>i`<a href=${e.url} target="_blank" rel="noopener" onClick=${C}>#${e.number}</a>`,lt=e=>i`<span class="prname" title=${e.title||void 0}><b>#${e.number}</b>${e.title&&[" ",e.title]}</span>`;function fe(e,t,s){return e.flatMap((n,r)=>{let o=t.get(n)||void 0,a=n===s?i`<b title=${o}>#${n}</b>`:i`<span title=${o}>#${n}</span>`;return r?[" \\u2192 ",a]:[a]})}function Ce(e){let t=e.checks;return t.passed+"/"+t.total+" passed"+(t.failed?" \\xB7 "+t.failed+" failed":"")+(t.pending?" \\xB7 "+t.pending+" pending":"")}function dt(e){let t=e.review||{};return(e.reviewDecision||"")+(t.unresolvedThreads?" \\xB7 "+t.unresolvedThreads+" unresolved":"")+(t.changesRequested?" \\xB7 changes requested":"")}function Te(e){return[e.mergeStateStatus,e.nextMergeable?[" \\xB7 ",i`<span class="ok">next mergeable</span>`]:e.blockedBy?" \\xB7 blocked by #"+e.blockedBy:""]}function Jt(e){let t=Ys(e);return e?[t.text," \\xB7 ",t.at?[v(t.at)," ago"]:"never checked"]:i`<span class="dim">${t.text}</span>`}function Ae(e){if(e.requested)return{stale:!1,listen:[i`<span class="dot"></span>`,i`<span class="dim">requested</span>`],hb:i`<span class="dim">\u2014</span>`};if(e.starting)return{stale:!1,listen:e.starting.failedAt?[i`<span class="dot bad"></span>`,i`<span class="bad">start failed</span>`]:[i`<span class="dot warn"></span>`,"starting"],hb:i`<span class="dim">\u2014</span>`};if(!e.live)return{stale:!1,listen:[i`<span class="dot"></span>`,i`<span class="dim">signed off</span>`],hb:i`<span class="dim">\u2014</span>`};let t=Date.now()-Date.parse(e.heartbeatAt)>18e5,s=e.listening??(!!e.firstParkedAt&&!t);return{stale:t,listen:s?[i`<span class="dot ok"></span>`,"listening"]:[i`<span class="dot warn"></span>`,"not listening"],hb:i`<span class=${t?"bad":"ok"}>${t&&"stale "}${v(e.heartbeatAt)} ago</span>`}}function Yt(e){if(e.requested)return{kind:"requested",helmOn:!!g().snapshot?.helmOn};if(e.starting)return e.starting.failedAt?{kind:"start failed",reason:e.starting.reason??"no session signed on in time"}:{kind:"starting",deadline:e.starting.deadline};if(!e.live)return{kind:"signed off"};let t=e.claimed?e.catches.find(s=>s.id===e.claimed&&s.bucket==="active"):void 0;return t?t.waiting||t.verb==="paused"?{kind:"parked",current:t}:{kind:"working",current:t}:{kind:"idle",listening:e.listening??(!!e.firstParkedAt&&Date.now()-Date.parse(e.heartbeatAt??"")<=18e5)}}function ko(e){let t=Yt(e);return t.kind==="working"||t.kind==="idle"&&t.listening?"ok":t.kind==="start failed"?"bad":t.kind==="parked"||t.kind==="starting"?"warn":"dim"}var qn=e=>e.waiting?`waiting on ${e.waiting.on}`:e.note||"waiting",Wn=e=>e.title.slice(0,40)||"(no title)";function Re(e){let t=Yt(e),s=i`<span class=${"dot trapdot "+ko(e)}></span>`;if(t.kind==="requested")return[s,i`<span class="dim">${Kn(e,t.helmOn)}</span>`];if(t.kind==="starting")return[s,"starting \\xB7 waiting for its session to sign on"];if(t.kind==="start failed")return[s,i`<span class="bad">start failed</span>`," \\xB7 ",t.reason];if(t.kind==="signed off")return[s,i`<span class="dim">signed off</span>`];if(t.kind==="idle")return[s,"idle \\xB7 ",t.listening?"listening":"not listening"];let n=t.current;return t.kind==="parked"?[s,"parked \\xB7 ",qn(n)]:[s,"working \\xB7 ",i`<a href="#dispatches" onClick=${o=>{o.preventDefault(),C(o),O("dispatch",`${n.lane}:${n.id}`)}}>${n.id.slice(0,8)}</a>`," \\xB7 ",Wn(n),n.activity&&[" \\xB7 ",n.activity.summary," ",v(n.activity.at)," ago"]]}var Kn=(e,t)=>`requested \\xB7 ${e.repo??""} \\xB7 ${e.harness??""} \\xB7 ${t?"waiting for the helm":"waiting for a helm"}`;function Q(e){let t=e.starting?.commands;return t?.length?i`<div class="startcmds" onClick=${C}><div class="dim">start it: run one in a terminal</div>${t.map(s=>T(s.command,s.harness))}</div>`:null}function ct(){let t=g().snapshot?.repoKeys??[],[s,n]=z(!1),[r,o]=z(""),[a,l]=z("claude"),[u,c]=z(null),d=r||t[0]||"",h=async p=>{p.preventDefault();let m=await $n(d,a);c(m??null),m||n(!1)};return s?i`<form class="newtrap" onClick=${C} onSubmit=${h}>\n <label>repo <select name="repo" value=${d} onChange=${p=>o(p.target.value)}>${t.map(p=>i`<option value=${p}>${p}</option>`)}</select></label>\n <label>harness <select name="harness" value=${a} onChange=${p=>l(p.target.value)}><option value="claude">claude</option><option value="codex">codex</option></select></label>\n <button class="btn" type="submit" disabled=${!d}>Request</button>\n <button class="btn" type="button" onClick=${()=>n(!1)}>Cancel</button>\n ${u&&i`<span class="bad">${u}</span>`}\n </form>`:i`<span class="newtrap"><button class="btn" onClick=${p=>(C(p),n(!0),c(null))}>+ New trap</button></span>`}function pt(e){let t=Yt(e);if(t.kind==="requested")return Kn(e,t.helmOn);if(t.kind==="starting")return`starting \\xB7 sign-on due by ${t.deadline}`;if(t.kind==="start failed")return`start failed \\xB7 ${t.reason}`;if(t.kind==="signed off")return"signed off";if(t.kind==="idle")return`idle \\xB7 ${t.listening?"listening":"not listening"}`;if(t.kind==="parked")return`parked \\xB7 ${qn(t.current)}`;let s=t.current;return[`working \\xB7 ${s.id.slice(0,8)} \\xB7 ${Wn(s)}`,s.activity&&`${s.activity.summary} ${J(s.activity.at)} ago`].filter(Boolean).join(" \\xB7 ")}function Qt(e){let t=e.messages.filter(s=>s.state==="pending").length;return e.messages.length?["\\u2709 "+e.messages.length,t>0&&[" ",i`<span class="warn">(${t} pending)</span>`]]:null}var bo=e=>()=>{window.open(G(e.key),"_blank","noopener")};function Xt(e){let t=ve(e);return{nodes:[t&&t+" \\xB7 ",v(e.filedAt)," ago",e.acked?" \\xB7 acked":""],text:[t,`${J(e.filedAt)} ago`,e.acked?"acked":""].filter(Boolean).join(" \\xB7 ")}}function yo(e){let t=Xt(e);return i`<a key=${e.key} class=${"card"+(e.acked?" acked":"")} href=${G(e.key)} target="_blank" rel="noopener"><div class="top"><b title=${e.title}>${e.title}</b></div><div class="meta" title=${t.text}>${t.nodes}</div></a>`}function _o(e){return i`<tr key=${e.key} class=${"rowhead"+(e.acked?" acked":"")} onClick=${bo(e)}><td class="grow"><a href=${G(e.key)} target="_blank" rel="noopener" onClick=${t=>t.stopPropagation()}>${e.title}</a></td><td>${ve(e)}</td><td>${v(e.filedAt)}</td><td>${e.acked?"acked":""}</td></tr>`}function Vn({inp:e}){return e.view==="cards"?e.list.length?i`<div class="cards">${e.list.map(yo)}</div>`:i`<div class="empty">no reports match</div>`:M(["title","from","filed","acked"],e.list.map(_o),"no reports match")}function Yn(e){let t=e.trim();return/^(https?:\\/\\/|mailto:|#)/i.test(t)?t:void 0}function Qn(e){let t=e.trim();return/^[^/\\\\:?#]+$/.test(t)&&t!=="."&&t!==".."?t:void 0}function zn(e,t,s,n){let r=0;for(let o=t;o<e.length;o++){if(e[o]==="\\\\"){o++;continue}if(e[o]===s)r++;else if(e[o]===n){if(r===0)return o;r--}}return-1}function Jn(e,t){let s=zn(e,t+1,"[","]");if(s<0||e[s+1]!=="(")return;let n=zn(e,s+2,"(",")");if(n<0)return;let r=e.slice(s+2,n).trim().replace(/\\s+"[^"]*"$/,"").replace(/^<(.*)>$/,"$1");return{text:e.slice(t+1,s),target:r,end:n+1}}function q(e){let t=[],s="",n=()=>{s&&t.push({t:"text",v:s}),s=""};for(let r=0;r<e.length;){let o=e[r];if(o==="\\\\"&&r+1<e.length&&/[\\\\`*_{}[\\]()#+\\-.!|<>~]/.test(e[r+1])){s+=e[r+1],r+=2;continue}if(o==="`"){let a=/^`+/.exec(e.slice(r))[0],l=e.indexOf(a,r+a.length);if(l>0){n(),t.push({t:"code",v:e.slice(r+a.length,l).replace(/^ (.*) $/,"$1")}),r=l+a.length;continue}}if(o==="!"&&e[r+1]==="["){let a=Jn(e,r+1);if(a){n(),t.push({t:"img",alt:a.text,src:a.target}),r=a.end;continue}}if(o==="["){let a=Jn(e,r);if(a){n(),t.push({t:"a",href:a.target,c:q(a.text)}),r=a.end;continue}}if((o==="*"||o==="_")&&e[r+1]===o){let a=e.indexOf(o+o,r+2);if(a>r+2){n(),t.push({t:"b",c:q(e.slice(r+2,a))}),r=a+2;continue}}if(o==="*"||o==="_"&&(r===0||!/\\w/.test(e[r-1]))){let a=r+1;for(;(a=e.indexOf(o,a))>0&&(e[a+1]===o||o==="_"&&/\\w/.test(e[a+1]??""));)a+=2;if(a>r+1&&e[r+1]!==" "){n(),t.push({t:"i",c:q(e.slice(r+1,a))}),r=a+1;continue}}if(o===`\n`){/ {2,}$/.test(s)||s.endsWith("\\\\")?(s=s.replace(/( {2,}|\\\\)$/,""),n(),t.push({t:"br"})):s+=" ",r++;continue}s+=o,r++}return n(),t}var Xn=/^ {0,3}(`{3,}|~{3,})\\s*([^`\\s]*)/,Zn=/^ {0,3}(#{1,6})\\s+(.*?)\\s*#*\\s*$/,er=/^ {0,3}([-*_])(\\s*\\1){2,}\\s*$/,ts=/^( {0,3})([-*+])\\s+(.*)$/,ss=/^( {0,3})(\\d{1,9})[.)]\\s+(.*)$/,ut=/^ {0,3}>\\s?(.*)$/,wo=/^\\s*\\|?\\s*:?-+:?\\s*(\\|\\s*:?-+:?\\s*)*\\|?\\s*$/;function Zt(e){let t=e.trim();t.startsWith("|")&&(t=t.slice(1)),t.endsWith("|")&&!t.endsWith("\\\\|")&&(t=t.slice(0,-1));let s=[],n="";for(let r=0;r<t.length;r++)t[r]==="\\\\"&&t[r+1]==="|"?(n+="|",r++):t[r]==="|"?(s.push(n.trim()),n=""):n+=t[r];return s.push(n.trim()),s}var es=e=>Xn.test(e)||Zn.test(e)||er.test(e)||ts.test(e)||ss.test(e)||ut.test(e);function ft(e){let t=e.replace(/\\r\\n?/g,`\n`).replace(/\\t/g," ").split(`\n`),s=[],n=0;for(;n<t.length;){let r=t[n];if(!r.trim()){n++;continue}let o=Xn.exec(r);if(o){let d=o[1],h=[];for(n++;n<t.length&&!new RegExp(`^ {0,3}${d[0]}{${d.length},}\\\\s*$`).test(t[n]);)h.push(t[n++]);n++,s.push({t:"code",lang:o[2]??"",v:h.join(`\n`)});continue}let a=Zn.exec(r);if(a){s.push({t:"h",level:a[1].length,c:q(a[2])}),n++;continue}if(er.test(r)){s.push({t:"hr"}),n++;continue}if(ut.test(r)){let d=[];for(;n<t.length&&t[n].trim()&&(ut.test(t[n])||!es(t[n]));)d.push(ut.exec(t[n])?.[1]??t[n]),n++;s.push({t:"quote",c:ft(d.join(`\n`))});continue}let l=ts.exec(r),u=ss.exec(r);if(l||u){let d=!l,h=d?ss:ts,p=[];for(;n<t.length;){let m=t[n],f=h.exec(m);if(f&&f[1].length<=3){p.push([f[3]]),n++;continue}if(!m.trim()){if(n+1<t.length&&/^ {2,}\\S/.test(t[n+1])){p.at(-1).push(""),n++;continue}break}if(/^ {2,}\\S/.test(m)){p.at(-1).push(m.replace(/^ {2,4}/,"")),n++;continue}if(es(m))break;p.at(-1).push(m.trim()),n++}s.push({t:"list",ordered:d,start:d?Number(u[2]):1,items:p.map(m=>ft(m.join(`\n`)))});continue}if(r.includes("|")&&n+1<t.length&&wo.test(t[n+1])&&t[n+1].includes("-")){let d=Zt(r),h=Zt(t[n+1]).map(m=>m.startsWith(":")&&m.endsWith(":")?"center":m.endsWith(":")?"right":m.startsWith(":")?"left":"");n+=2;let p=[];for(;n<t.length&&t[n].trim()&&t[n].includes("|");){let m=Zt(t[n++]);p.push(d.map((f,$)=>q(m[$]??"")))}s.push({t:"table",align:d.map((m,f)=>h[f]??""),head:d.map(q),rows:p});continue}let c=[];for(;n<t.length&&t[n].trim()&&!es(t[n])&&!(c.length&&/^ {0,3}(=+|-+)\\s*$/.test(t[n]));)c.push(t[n++].replace(/^ +/,""));if(c.length===0){s.push({t:"p",c:q(r.trim())}),n++;continue}s.push({t:"p",c:q(c.join(`\n`))})}return s}function W(e,t){return t.map(s=>{switch(s.t){case"text":return s.v;case"code":return i`<code>${s.v}</code>`;case"b":return i`<strong>${W(e,s.c)}</strong>`;case"i":return i`<em>${W(e,s.c)}</em>`;case"br":return i`<br />`;case"a":{let n=Yn(s.href);return n?i`<a href=${n} target="_blank" rel="noopener noreferrer">${W(e,s.c)}</a>`:W(e,s.c)}case"img":{let n=Qn(s.src);return n?xe(e(n),s.alt||n,"mdimg"):i`<span class="dim">[image not shown: ${s.alt||s.src}]</span>`}}})}function mt(e,t){return t.map(s=>{switch(s.t){case"h":{let n=W(e,s.c);return s.level===1?i`<h1>${n}</h1>`:s.level===2?i`<h2>${n}</h2>`:s.level===3?i`<h3>${n}</h3>`:i`<h4>${n}</h4>`}case"p":return i`<p>${W(e,s.c)}</p>`;case"code":return i`<pre class="mdcode"><code>${s.v}</code></pre>`;case"hr":return i`<hr />`;case"quote":return i`<blockquote>${mt(e,s.c)}</blockquote>`;case"list":{let n=s.items.map(r=>i`<li>${mt(e,r)}</li>`);return s.ordered?i`<ol start=${s.start}>${n}</ol>`:i`<ul>${n}</ul>`}case"table":return i`<div class="mdtable"><table><thead><tr>${s.head.map((n,r)=>i`<th style=${s.align[r]?"text-align:"+s.align[r]:void 0}>${W(e,n)}</th>`)}</tr></thead><tbody>${s.rows.map(n=>i`<tr>${n.map((r,o)=>i`<td style=${s.align[o]?"text-align:"+s.align[o]:void 0}>${W(e,r)}</td>`)}</tr>`)}</tbody></table></div>`}})}var Me=new Map;function tr(e){let t=Me.get(e);return t||(t=ft(e),Me.set(e,t),Me.size>8&&Me.delete(Me.keys().next().value)),t}function So({r:e,text:t}){let s=t?.text!==void 0?mt(n=>Rt(e.key,n),tr(t.text)):t?.error?i`<div class="bad">${t.error}</div>`:i`<div class="dim">loading\u2026</div>`;return i`<div class="mdpage" data-report=${e.key}>${s}</div>`}function sr({text:e,fileUrl:t}){return i`<div class="mdpage">${mt(t,tr(e))}</div>`}function ns(e){let t=ve(e);return[t&&t+" \\xB7 ",v(e.filedAt)," ago",e.acked&&[" \\xB7 ",i`<span class="ok">acked</span>`]]}function nr({r:e,text:t,heading:s}){return[s&&i`<div class="sec">report \xB7 ${e.title}</div>`,i`<div class="sub">${ns(e)}</div>`,i`<${So} r=${e} text=${t} />`,e.attachments.length>0&&[i`<div class="sec">report attachments (${e.attachments.length})</div>`,ce(e.attachments,n=>Rt(e.key,n))],!e.acked&&[i`<div class="sec">ack</div>`,T("lobstah attention ack "+e.key)]]}var Do=/\\.(png|jpe?g|gif|webp)$/i,xo=e=>{e.style.height="auto",e.style.height=e.scrollHeight+"px"};function Go(e,t){return t.length?i`<div class="dfiles">${t.map(s=>Do.test(s.name)?i`<span key=${s.name}>${xe(At(e,s.name),s.name,"dimg")}</span>`:i`<div key=${s.name} class="dfile"><span>${s.name}</span> <span class="dim">\xB7 ${s.type} \xB7 ${s.bytes} bytes \xB7 </span><code>${s.path}</code></div>`)}</div>`:null}function Co(e,t,s){let n=e.key,r=e.kind==="decision"?e.options:[],o=t?.text??"",a=t?.files??[],l=!!t?.sending,u=d=>d+1<=we?String(d+1):void 0,c=u(r.length);return[r.length>0&&i`<div class="doptions">${r.map((d,h)=>{let p=u(h);return i`<button key=${d} type="button" class=${"btn dopt"+(t?.option===d?" on":"")} aria-pressed=${t?.option===d?"true":"false"} aria-keyshortcuts=${p} disabled=${l} onClick=${()=>pn(n,d)}>${p&&i`<kbd class="dkey" aria-hidden="true">${p}</kbd>`}<span class="dlabel">${d}</span></button>`})}</div>`,i`<div class="danswer-wrap">${c&&i`<kbd class="dkey dkey-text" aria-hidden="true">${c}</kbd>`}<textarea\n class="danswer"\n rows="2"\n aria-keyshortcuts=${c}\n aria-label=${r.length?"Your answer, in your own words":"Your answer"}\n placeholder=${r.length?"Add to your answer, or answer in your own words":"Your answer"}\n value=${o}\n disabled=${l}\n onInput=${d=>{let h=d.currentTarget;xo(h),A(n,{text:h.value,error:void 0})}}\n onPaste=${d=>{un(n,d)}}\n ></textarea></div>`,i`<div class="dfoot">\n <label class="btn dattach" title="attach images or files">attach<input type="file" multiple accept=${s} disabled=${l} onChange=${d=>{let h=d.currentTarget;Nt(n,h.files).then(()=>{h.value=""})}} /></label>\n ${a.map((d,h)=>i`<span key=${d.name+h} class="dchip">${d.name}<button type="button" class="dx" title="remove" disabled=${l} onClick=${()=>fn(n,h)}>\xD7</button></span>`)}\n ${t?.error&&i`<span class="bad derr">${t.error}</span>`}\n <button type="button" class="btn dsend" disabled=${l} onClick=${()=>{Ze(n)}}>${l?"Sending\\u2026":"Send"}</button>\n </div>`]}var rr=e=>e.kind==="decision"?e.title:R(e.note);function To(e,t,s){return t?.sent?"answered":e.viewedAt||s?"read":"unread"}function rs(e){let t=e.dispatch,s=e.lane??"work";return[t&&i`<a class="dlink" href="#" onClick=${r=>{r.preventDefault(),r.stopPropagation(),S("dispatch",s+":"+t)()}}>${t.slice(0,8)}</a>`,e.repo].filter(Boolean).flatMap((r,o)=>o?[" \\xB7 ",r]:[r])}function Ao(e,t,s,n){let r=To(e,t,s),o=()=>ae(e.key);return i`<li key=${e.key} class=${"drow "+r+(n?" focus":"")} data-decision=${e.key}>\n <button type="button" class="dopen" onClick=${o} aria-label=${`${r}: ${e.kind==="decision"?e.title:e.note}`}>\n ${e.kind==="question"&&i`<span class=${"badge "+(e.verb==="blocked"?"bad":"warn")}>${e.verb}</span>`}\n <b class="dtitle">${rr(e)}</b>\n </button>\n <span class="dmeta dim">${rs(e)}</span>\n <span class="dage dim">${v(e.at)} ago</span>\n <span class=${"dstate "+r}>${r==="answered"?`answered \\xB7 ${t.sent}`:r}</span>\n </li>`}function or({cards:e,drafts:t,focus:s,viewedHere:n,unread:r}){let o=e.length?i`<ul class="dlist">${e.map(a=>Ao(a,t[a.key],!!n[a.key],a.key===s))}</ul>`:i`<div class="empty">none</div>`;return i`<section class="decisions"><h2>decisions${r>0&&i`<span class="dbadge" aria-label=${`${r} new`}>${r}</span>`}</h2>${o}</section>`}function Ro(e){let t=Math.min(e,we),s=e+1<=we?String(e+1):void 0,n=[t>0&&[i`<kbd>1</kbd>`,t>1&&["\\u2013",i`<kbd>${t}</kbd>`]," choose"],s&&[i`<kbd>${s}</kbd>`," write"],t>0&&[i`<kbd>Enter</kbd>`," on a chosen option sends"],[i`<kbd>\u2190</kbd>`," ",i`<kbd>\u2192</kbd>`," previous / next"],[i`<kbd>Esc</kbd>`," leaves the text box, then closes"],[i`<kbd>d</kbd>`," opens decisions anywhere"]].filter(Boolean);return i`<div class="dlegend dim">${n.flatMap((r,o)=>o?[" \\xB7 ",r]:[r])}</div>`}var Mo=\'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])\';function ar({open:e,order:t,drafts:s,extensions:n}){let r=Ft(null),o=Ft(null),a=!!e;Ut(()=>{if(a)return o.current=document.activeElement,r.current?.focus(),()=>{let m=o.current;m&&typeof m.focus=="function"&&document.contains(m)&&m.focus()}},[a]);let l=e?.key??null;if(Ut(()=>{l&&on(l)},[l]),!e)return null;let u=m=>{if(m.key!=="Tab"||!r.current)return;let f=[...r.current.querySelectorAll(Mo)];if(!f.length)return void m.preventDefault();let $=f[0],_=f[f.length-1];m.shiftKey&&(document.activeElement===$||document.activeElement===r.current)?(m.preventDefault(),_.focus()):!m.shiftKey&&document.activeElement===_&&(m.preventDefault(),$.focus())},c=l?t.find(m=>m.key===l):void 0,d=c?t.indexOf(c):-1,h=(m,f,$="")=>i`<div id="doverlay" class="open" onClick=${_=>_.target===_.currentTarget&&ie()}>\n <div class="modal dmodal" role="dialog" aria-modal="true" aria-labelledby="dmodal-title" tabindex="-1" ref=${r} onKeyDown=${u} data-decision=${$}>\n <button type="button" class="x" aria-label="close" aria-keyshortcuts="Escape" onClick=${ie}>\xD7</button>\n <h3 id="dmodal-title" class="sr-only">${m}</h3>\n ${f}\n </div>\n </div>`;if(!c)return h("All decisions answered",i`<div class="ddone" onClick=${ie}><div class="ok">\u2713 All decisions answered.</div><div class="dim">Click anywhere or press Escape to close.</div></div>`);let p=s[c.key];return h(c.kind==="decision"?c.title:c.note,i`<div class="dnav">\n <button type="button" class="btn dprev" aria-label="previous decision" aria-keyshortcuts="ArrowLeft" disabled=${d<=0} onClick=${()=>_e(-1)}>\u2039 prev</button>\n <span class="dpos" aria-live="polite">${d+1} of ${t.length}</span>\n <button type="button" class="btn dnext" aria-label="next decision" aria-keyshortcuts="ArrowRight" disabled=${d>=t.length-1} onClick=${()=>_e(1)}>next \u203A</button>\n </div>\n <div class="dtop">\n ${c.kind==="question"&&i`<span class=${"badge "+(c.verb==="blocked"?"bad":"warn")}>${c.verb}</span>`}\n <b class="dtitle">${rr(c)}</b>\n <span class="dmeta dim">${rs(c)}${rs(c).length?" \\xB7 ":""}${v(c.at)} ago</span>\n </div>\n ${c.kind==="decision"&&c.detail.trim()&&i`<${sr} text=${c.detail} fileUrl=${m=>At(c.key,m)} />`}\n ${c.kind==="decision"&&Go(c.key,c.attachments)}\n ${p?.sent?i`<div class="danswered ok">answered \xB7 ${p.sent}</div>`:Co(c,p,n.join(","))}\n ${Ro(c.kind==="decision"?c.options.length:0)}`,c.key)}function ir({unread:e,dismissed:t}){let s=e.some(a=>!t.includes(a.key)),n=e.length>0&&s,r=e.length,o=`${r} new decision${r===1?"":"s"}`;return i`<div class="dalert-live" role="status" aria-live="polite">${n&&i`<div class="dalert">\n <button type="button" class="dalert-open" aria-keyshortcuts="d" onClick=${()=>ae()}><span class="dcount" aria-hidden="true">${r}</span> <span>${o}</span></button>\n <button type="button" class="dx" aria-label="dismiss" title="dismiss" onClick=${an}>\xD7</button>\n </div>`}</div>`}function Oo(e,t){let s=e.badge&&(e.badge.worker?i`<${H} worker=${e.badge.worker} label=${e.badge.text} cls=${"badge "+(e.badge.tone||"dim")} />`:i`<span class=${"badge "+(e.badge.tone||"dim")+B(e.badge.text)} title=${j(e.badge.text)}>${e.badge.text}</span>`),n=e.worker&&i`<${H} worker=${e.worker} />`;if(e.href){let r=(t==="cards"?"card":"deckline click")+(e.acked?" acked":"");return t==="cards"?i`<a key=${e.key} class=${r} href=${e.href} target="_blank" rel="noopener"><div class="top"><b>${e.title}</b>${s}</div>${e.meta&&i`<div class="meta" title=${e.metaText}>${e.meta}</div>`}</a>`:i`<a key=${e.key} class=${r} href=${e.href} target="_blank" rel="noopener">${s&&[s," "]}<b>${e.title}</b>${e.meta&&[" ",i`<span class="dim">\xB7 ${e.meta}</span>`]}</a>`}return t==="cards"?i`<div key=${e.key} class=${"card"+(e.acked?" acked":"")} onClick=${e.open} style=${e.open?void 0:"cursor:default"}><div class="top"><b>${e.title}</b>${n}${s}</div>${e.meta&&i`<div class="meta" title=${e.metaText}>${e.meta}</div>`}${e.extra}${e.action&&i`<div class="foot"><span class="footact">${e.action}</span></div>`}</div>`:i`<div key=${e.key} class=${"deckline"+(e.open?" click":"")+(e.acked?" acked":"")} onClick=${e.open}>${s&&[s," "]}<b>${e.title}</b>${n&&[" ",n]}${e.meta&&[" ",i`<span class="dim">\xB7 ${e.meta}</span>`]}${e.action&&[" ",i`<span class="dim">\xB7 </span>`,e.action]}${e.extra}</div>`}var lr=(e,t)=>e>0&&i`<a class="deckmore" href=${"#"+t}>+${e} more \u2192</a>`;function ht(e,t,s,n,r,o){let a=t.slice(0,n),l=a.map(c=>Oo(c,r)),u=a.length?r==="cards"?i`<div class="cards">${l}</div>`:l:i`<div class="empty">none</div>`;return i`<section><h2><a href=${"#"+s}>${e} \u2192</a></h2>${o&&i`<div class="tabhead">${o}</div>`}${u}${lr(t.length-a.length,s)}</section>`}function Lo(e,t,s,n){let r=t.find(c=>c.number===e.nextNumber)||t[0],o=fe(e.numbers,new Map(t.map(c=>[c.number,c.title]))),a=r?"next #"+r.number:"nothing mergeable";if(n==="cards"){let c=d=>{let h=s.get(d.key)||[],p=h.length>0&&h.every($=>$.acked),m=h.length?h.map($=>i`<span class=${"badge "+(zt[$.kind]||"dim")}>${Fn($.kind)}</span>`):i`<span class=${"badge "+E(d.badge)+B(d.badge.text)} title=${j(d.badge.text)}>${d.badge.text}</span>`,f=`${d.repo} \\xB7 ${d.badge.text}${p?" \\xB7 acked":""}`;return i`<div key=${d.key} class=${"card"+(p?" acked":"")} onClick=${S("pr",d.key)}><div class="top">${lt(d)}${m}</div><div class="meta" title=${f}>${f}</div></div>`};return i`<div key=${e.id} class="deckstack"><div class="dim">${o} \xB7 ${a}</div>${e.readiness&&i`<div class=${e.readiness.allReady?"ok":"dim"}>${e.readiness.text}</div>`}<div class="cards">${t.map(c)}</div></div>`}let l=t.flatMap(c=>(s.get(c.key)||[]).map(d=>i`<span class=${"badge "+(zt[d.kind]||E(c.badge))+(d.acked?" acked":"")}>#${c.number} ${c.badge.text}</span>`)),u=l.flatMap((c,d)=>d?[" ",c]:[c]);return i`<div key=${e.id} class=${"deckline"+(r?" click":"")} onClick=${r&&S("pr",r.key)}><b>${o}</b> <span class="dim">\xB7 ${a}</span>${l.length>0&&[" \\xB7 ",u]}${e.readiness&&i`<div class=${e.readiness.allReady?"ok":"dim"}>${e.readiness.text}</div>`}</div>`}function No(e,t){let s=e.stacks.slice(0,3),n=new Map;for(let o of e.prAttention){let a=n.get(o.key)||[];a.push(o),n.set(o.key,a)}let r=s.length?s.map(o=>Lo(o,e.prs.filter(a=>a.stackId===o.id).sort((a,l)=>a.position-l.position),n,t)):i`<div class="empty">none</div>`;return i`<section><h2><a href="#prs">PRs \u2192</a></h2>${r}${lr(e.stacks.length-s.length,"prs")}</section>`}function dr({inp:e,drafts:t,focus:s,viewedHere:n,unread:r}){let o=e.view,a=e.inflight.map(d=>({key:d.lane+":"+d.id,title:[d.id.slice(0,8)," ",d.repo||""],worker:d.worker,badge:{text:d.verb,tone:d.verb==="needs-decision"||d.verb==="blocked"?"bad":"dim"},meta:[R(d.note||"",90),d.verbAt&&[" \\xB7 ",v(d.verbAt)," ago"],(d.for||d.claimedBy)&&[" \\xB7 ",st(d.for||d.claimedBy)]],metaText:[U(d.note||""),d.verbAt&&`${J(d.verbAt)} ago`,U(d.for||d.claimedBy||"")].filter(Boolean).join(" \\xB7 "),open:S("dispatch",d.lane+":"+d.id)})),l=e.landed.map(d=>({key:d.key,title:[d.id.slice(0,8)," ",d.repo||"",d.unreported&&[" ",i`<span class="badge warn unreported">unreported</span>`]],badge:{text:d.verb,tone:d.verb==="failed"?"bad":"ok"},meta:[R(d.note||"",90)," \\xB7 ",v(d.at)," ago"],metaText:`${U(d.note||"")} \\xB7 ${J(d.at)} ago`,open:S("dispatch",d.lane+":"+d.id)})),u=e.reports.map(d=>{let h=Xt(d);return{key:d.key,title:d.title,meta:h.nodes,metaText:h.text,href:G(d.key),acked:!!d.acked}}),c=e.traps.map(({x:d})=>({key:d.trapId,title:"\\u{1FAA4} "+(d.label??`wt:${d.trapId}`),badge:d.requested?{text:"requested",tone:"dim"}:d.starting?{text:d.starting.failedAt?"start failed":"starting",tone:d.starting.failedAt?"bad":"warn"}:{text:d.live?d.harness||"live":"signed off",tone:d.live?"ok":"dim",worker:d.live?d:void 0},meta:[d.repo||""," \\xB7 ",i`<span class="catchn" title="catches">\u{1F99E} ${te(d.totalCatches??0)}</span>`," \\xB7 ",Re(d)],metaText:`${d.repo||""} \\xB7 \\u{1F99E} ${te(d.totalCatches??0)} \\xB7 ${pt(d)}`,action:Y(d),extra:Q(d),open:S("trap",d.trapId)}));return i`<div class="deckgrid"><${or} cards=${e.decisions} drafts=${t} focus=${s} viewedHere=${n} unread=${r} />${ht("in flight",a,"dispatches",4,o)}${ht("Landed \\xB7 24h",l,"dispatches",8,o)}${ht("reports",u,"reports",8,o)}${ht("traps",c,"traps",8,o,i`<${ct} />`)}${No(e,o)}</div>`}function Ho(e,t){if(!t)return e;let s=new Map(e.map(o=>[o.id,o])),n=o=>{let a=o,l=new Set;for(;a.followUp&&s.has(a.followUp)&&!l.has(a.id);)l.add(a.id),a=s.get(a.followUp);return a.id},r=new Map;for(let o of e){let a=n(o),l=r.get(a)||[];l.push(o),r.set(a,l)}return[...r.values()].sort((o,a)=>a[0].sort-o[0].sort).flatMap(o=>o.sort((a,l)=>a.id===n(a)?-1:l.id===n(l)?1:a.sort-l.sort))}function jo(e,t){let s=e.lane+":"+e.id;return i`<tr key=${s} class="rowhead" onClick=${S("dispatch",s)}><td>${t&&e.followUp&&i`<span class="dim">\u21B3 </span>`}${e.id.slice(0,8)}</td><td>${e.lane} / ${e.bucket}</td><td>${e.repo}</td><td class=${"v-"+e.verb}>${e.verb}</td><td class="grow">${R(e.note??"",90)}${ot(e)}${Kt(e)}${rt(e)}</td><td>${v(e.verbAt)}</td><td>${Ge(e)}</td><td>${ue(e)}</td></tr>`}function Bo(e){let t=e.lane+":"+e.id;return i`<div key=${t} class="card" onClick=${S("dispatch",t)}><div class="top"><b>${e.id.slice(0,8)}</b>${Kt(e)}<span class=${"badge v-"+e.verb+B(e.verb)} title=${j(e.verb)}>${e.verb}</span></div><div class="meta" title=${`${e.repo} \\xB7 ${e.lane} ${e.bucket}`}>${e.repo} \xB7 ${e.lane} ${e.bucket}${e.verbAt&&[" \\xB7 ",v(e.verbAt)]}</div>${e.note&&i`<div class="note" title=${U(e.note)}>${R(e.note)}</div>`}${ot(e)}${rt(e)}<div class="foot">${Ge(e)} ${ue(e)}</div></div>`}function cr({inp:e,more:t}){let s=e.list,n=t&&i`<${pe} kind="dispatches" more=${t} />`;return e.view==="cards"?[s.length?i`<div class="cards">${s.map(Bo)}</div>`:i`<div class="empty">no dispatches match</div>`,n]:[M(["id","lane / bucket","repo","verb","note","age","addressed","pr"],Ho(s,e.chain).map(r=>jo(r,e.chain)),"no dispatches match"),n]}var Uo={deck:"On deck",dispatches:"Dispatches",traps:"Traps",prs:"PRs",reports:"Reports",notices:"Notices",stats:"Stats"},Fo=["working","needs-decision","blocked","paused","done","failed","unknown"],pr=(e,t)=>i`<span role="img" aria-label=${t} title=${t}>${e}</span>`;function qo(e,t){let s=t.daemonStale,n=e.daemon?[i`<span class=${s?"bad":"ok"}>${s&&"stale "}${v(e.daemon.heartbeat)} ago</span>`," ",i`<span class="dim">v${e.daemon.version}</span>`]:i`<span class="bad">down</span>`;return[i`<span class="chip">daemon ${n}</span>`,t.stats&&i`<span class="chip" title="catches today">\u{1F99E} ${t.stats.catchesToday} today</span>`,e.slots&&i`<span class="chip">active ${pr("\\u{1F3A3}","headless")} ${e.slots.headless} of ${e.slots.limit}; ${pr("\\u{1FAA4}","traps")} ${e.slots.traps}${e.slots.parked?`; parked: ${e.slots.parked} (no slot)`:""}</span>`,t.helms.map(({x:r,stale:o})=>i`<span key=${r.grounds} class="chip click" onClick=${S("helm",r.grounds)}>\u26F5 <${H} worker=${r} label=${i`<b>${r.man}</b>`} cls="" /> <span class="dim">helm ${r.grounds}</span> <span class=${o?"warn":"ok"}>${o&&"stale "}${v(r.heartbeatAt)} ago</span></span>`)]}var os=[];function Wo(e){let t=[...new Set([...e.dispatches.map(s=>s.repo),...e.notices.map(s=>s.repo),...e.prs.map(s=>s.repo)].filter(Boolean))].sort();return t.length!==os.length&&(os=t),os}var as=[];function Ko(e){let t=[...new Set(e.notices.map(s=>s.kind))].sort();return t.length!==as.length&&(as=t),as}var Pe=e=>e.currentTarget.value;function Vo(e,t,s){let n=a=>t===a?"":"display:none",r=e?Wo(e):[],o=e?Ko(e):[];return i`<div class="controls" style=${t==="stats"?"display:none":void 0}>\n <select id="f-lane" style=${e&&n("dispatches")} value=${s.lane} onChange=${a=>N({lane:Pe(a)})}>\n <option value="">all lanes</option>\n <option value="work">work</option>\n <option value="chore">chore</option>\n </select>\n <select id="f-repo" value=${s.repo} onChange=${a=>N({repo:Pe(a)})}>\n <option value="">all repos</option>\n ${r.map(a=>i`<option key=${a}>${a}</option>`)}\n </select>\n <select id="f-verb" style=${e&&n("dispatches")} value=${s.verb} onChange=${a=>N({verb:Pe(a)})}>\n <option value="">all verbs</option>\n ${Fo.map(a=>i`<option key=${a}>${a}</option>`)}\n </select>\n <label id="chain-control" style=${e&&n("dispatches")}><input id="f-chain" type="checkbox" checked=${!!s.chain} onChange=${a=>N({chain:a.currentTarget.checked})} /> group by chain</label>\n <select id="f-kind" style=${e&&n("notices")} value=${s.noticeKind} onChange=${a=>N({noticeKind:Pe(a)})}>\n <option value="">all notice kinds</option>\n ${o.map(a=>i`<option key=${a}>${a}</option>`)}\n </select>\n <input id="f-q" type="search" placeholder="search id \xB7 note \xB7 brief" value=${s.q} onInput=${a=>N({q:Pe(a)})} />\n </div>`}function ur({d:e,inp:t,tab:s,st:n,stale:r,unread:o=0}){return[i`<div class="headerline"><h1>\u{1F99E}\u2728 spyglass<span id="stale" style=${e||r?r?"display:inline":"display:none":void 0}> \xB7 STALE FEED</span></h1><span id="settings-slot"><button id="gearbtn" title="settings" aria-label="settings" onClick=${()=>O("settings","browser")}>\u2699</button></span></div>`,i`<div class="chips"><span id="chips" style="display:contents">${e&&t&&qo(e,t.chips)}</span><span class="chip dim" id="clock">${e&&new Date(e.now).toLocaleTimeString("en-GB")}</span></div>`,i`<nav class="tabs" id="tabs" aria-label="Spyglass views">${se.map(a=>i`<a href=${"#"+a} data-tab=${a} class=${e?a===s?"on":"":void 0}>${Uo[a]}${a==="deck"&&o>0&&i`<span class="dbadge" aria-label=${`${o} new decision${o===1?"":"s"}`}>${o}</span>`}</a>`)}</nav>`,Vo(e,s,n)]}var fr=({d:e})=>e&&["\\u{1F99E}\\u2728 lobstah v"+e.version+" \\xB7 ",i`<a href=${e.repoUrl} target="_blank">${e.repoUrl.replace("https://github.com/","")}</a>`];function mr(e,t){if(!t.lobs)return[];let s={"pr:draft":"draft","pr:review":"review","pr:checks":"checks","pr:conflict":"conflicts","pr:ready":"ready","stack-ready":"stack ready",landed:"landed",watch:"watch",report:"report"},n=t.hidden??{},r=e.filter(a=>!a.quiet&&!a.acked&&!(a.key!==void 0&&n[a.key]===a.stateHash)).map(a=>{let l=s[a.kind??""]??"",u=a.key!==void 0?{hideKey:a.key,hideHash:a.stateHash??""}:{};return a.kind==="decision"&&a.key?{key:"decision:"+a.key,text:a.note||a.verb,label:l,hash:Ct(a.key),...u}:(a.kind??"question")==="question"&&a.key?{key:"question:"+a.key,text:a.note||a.verb,label:l,hash:Ct(a.key),...u}:a.kind==="report"&&a.key?{key:"report:"+a.key,text:a.note||a.verb,label:l,href:G(a.key),...u}:typeof a.kind=="string"&&(a.kind.startsWith("pr:")||a.kind==="stack-ready")&&a.prUrl?{key:a.kind+":"+(a.key??a.prUrl),text:a.note||a.kind,href:a.prUrl,label:l,...u}:{key:(a.kind??"question")+":"+(a.key??a.lane+":"+a.id),text:a.note||a.verb,label:l,...a.kind==="watch"?{}:{open:{type:"dispatch",key:a.lane+":"+a.id}},...u}});!r.length&&t.preview&&(r=[{key:"preview",text:"attention questions crawl in here",...t.previewHelm?{open:{type:"helm",key:t.previewHelm}}:{}}]);let o=r.length>4?r.length-4:0;return r=r.slice(0,4),o&&(r[3]={...r[3],text:"\\u2026and "+o+" more \\u2014 see attention",label:""}),r}var hr="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAA4CAYAAABdcSXCAAAVF0lEQVR4nO2d34sc15XHb/fUaGRpsIwky0qbRCiyIZh1NjEKxDixTUge8up9ccKSZSHZf8BP0R/gffI/kCwsLEvWL/FrHmKM88PEYOHsJiYIFGWwQjq2rBk8xpI1mp7p5XOrvj2n71R1V1VXz3RP3yOaaXV1Vd/7rXO/95xzzz3lXJS5kVe++3w/732UKGUk6k8EqDFFeu07l/uRhOIAqyNRfyJAE0uczeIAm0Si/kSAGlWmaA3FAVZXov6UAOjlr/1Df5GUoKpSRCUajU3Un9nUn/Y8DLCXfvFma+Xs2YUMvFbByB1hifpTT6L+jCGgqrO8W0Api9NRtoKi/tSXqD8RoANVIrdgEgfY/OrPgblg49yHrdu3x3b+qLsZZfo+DiNwXDSJ+jO/+jMTBIRceee9UgAtspQh4EWNlUX9mU/9mRkCqmLhLOIAq9L/i71PF3IFKerP7OnPXBGQZNEJZtJBtpasNvqbX3rouCPzel6IKOrPbOnPXBHQIsd5DlsgmHCLBzGBy50T/gURzfrgjvozXzJzBHRYMm9L2E23FfL51qUz7tnLj+871t1quePLieucOdnkTx4pWXT9OXIENE2AipQF33dWbkyZVZ8myQfr5rGLHffnte6+lZDOSt/d2+65Sw8uu2dO9ecCo6g/B6c/Ve5B+Fl7kQBSQJVBFA4kuRqQ0DzEOlitaJJ8sHy2Nzdyj2H9WDKa9aX+qD8Hpz95gt4wtsIxxnvGF27+TBPQNAACFAbO97/8iJ/pu+t3huIFDDTkqS+cGryfddHNDS26qnEQ63adP5mSja4HHq1225PQZ1v33bxI1J+D0x87xuTK/8tXPrfvWprk9d2ZJqCmAYJU/u3bT7qVlcTd7f7NXfv43uCYwGCQnVxq+VjHrLoZwoKZhNfbLz7n3zPb8F7fKWvFoSySfq/n8eEzlEVEvO36rtdO40DIPASjkag/09ef0FpmjDHBf3rrlnvjxvrQcf6iQ1rQQPZs6xlTGuUieHOt96knBQDqrqdxGgaIks/GCZ3XLL995zOXrCwPWTm8Z6a/t73tj2EFbN340M2SqM+y4n7eXfVKw81+7OLe9x7L/v766l48q4iwrZLJ/Vo+ddp1r1/3mIB5u7fter1UcZAHVo455+7O7GpT1J+D0x8rcuOR3Y8YO/tPYWIjligd4vrJrAGE/FNnyb170/kBAEBWAOvZDCAGEKb2OIA0y99fv+UJppUkQ7/J7zDz95ecS3b7rseImyHRjPXst58cfPbMna53I63yWEEZnrp1y717c9PjNI6ssX7ABYzAAxI+0UqDzwgWENhgDc2iRP05PP0ZmuCZyI4tO7fVG7ovjMEvri4N9IkJf627MxsumADCfMN3XD13zn8OQP31j/yg4AWz0kFedBgmHxc05pjiGhJmeURuxhceTNzS/e2h70ziZjTlntiguY3TyFrBlBUeeoEXOPE6tuTc04+f8Te/qE1grOtBQsiF08c9+Vi3C/JBeZZzZrbDlqg/h6c/oUiHmNAxDjTG0CkrH9zp+WO1tInGNGGCW3aUpQMANtiZmmt70t/dHbwnONpLltz7G/fcW5utXEuIG/CvX3vUEwwDill+a6vnmZ2ZHvLpbaWuF4OMa97Z6buf/eHDkS6erC/aLytMfrCySSfBSNhAsiJkBCVJejtjzxdO6nMeRgoYohxyTRFvBbZbg/cI5AMZ/eXTHe/bl3V/i/oW9We+9Od319dz22R1SOQjfWNyE9FpIpMl/dv3P/Fx2PY49rQBYGXKNrFUbQES+RAcpuOQjl6hQBB68V2Ihc6Pyk+BcGT6KdAKs3Me5OOByf6WbTuKgrWG1WY/47rMNpPkFAkbri3l0QxVRnmscLOFUdgmSARSkkUokZLovSwhMJRSlelD1J+joT9Jb8e3izFWdtyTroGb9Y0LD/r/a/ztu35RB3yA6ispOL++en1g5tIQFLaz1nWvnK03k1mARDwoeJ61A9HkiY4Rk0iyAdZdX983uzK7QDjbvT2CkbvlB1o260NAewGy/PbqeohMWqL9YIVVcPmJ0/7a3Gj5uHUFqw3hWr69x5YrKY8Imj7KbcLiW9vM/z795vsDRcksQns9MqKZtcZZP1F/jpr+OH8upJInGBCMYY0lJnm9v5fFghBZ1XZMJ0UAySrBF0Rxr924PQSQj6ts1gMoj3ys2acGFpGPhO8v852sY2pnKBDm0v0PXS8AYojlsxkewT2TaBZSIPut7BAZw2DEKhERf5nOnsTabfe+93H3liHLCjMMQfhBnGqr5060UtLsG8UowkfErL+tZMnd2LjnLpxeypII97eJvkDgS/f33FsIuWfIiV/H+skjn6g/i6A/x93uTkoqVuALSNQlexO5yAfR2KZfHOP/29nPokttWTYAxIv3EAQXhWgEKgChiAxeOkMQqY4AEG6XyM1m2VoQbKwnPBbGgG58klo0RQxNX3aYAVAUM6vb6yC0hWvZWR4sMJXT5cqO+/7FFY+Rgrd+j9RKf8jPpY0cr5oMJzPc+uzkJYWELBe0CKuB/54suf9852/u66/+yv1sbcstn3xgX3AdaxE3DB9fFiAv+qIXmLy9tule+OXVXPKJ+rMY+rOykuQuzsAF/KYIJi+cob7xuzc/6Q3y8NoAhDkIOCyT4WLxI2Jx2Az2wyT0jcouXjYWEAIEyVmALBj2/5aprdjPLUD8zRtg4YwkJvZ9ywiJuYHrcswmT+URGealsjkxSz2xrSz73+Z83hOoBeCq7qnuhSTcGoH8Zu1j99Pff+D/Cg/Ezmp6EQi88s57kD5t8bMibQ+3UtBO2vvff9rw12URgD7w+o//SxPK8sjHtjnqz2LoTyfYkEwbLZlqscJO9Bgt+hyCtosYQzYYXxJA+iHlzPg4iusPVkLqAiTXjmX10ATkZvzXX3seIBoqsglZOgTISh5AtJUZnnMU41HMR32kb1e7d/2sY90M/o+5LAw4bzdZdt+8+JAnQIvRic6jHvg65KzVBCvy2WUOszr3wi+v+j7TJ35/YCobrPgewsTxynef999/7TuX/Wfcv3CriWbOf34iNdshIpREilJ2xSvqz9HXn25O28LxhR7YiZ4YJASbt4LatgAhOomdz2HQCqDu9lu1zEMLEK6XzScRCPirMC0dtjNJaBFZgGBnhPYUBdk4BkiwPucCxiBItt3zRIaLkbfMKBfFA3nqtFce8iOUkAfwejHDaXmxyjK1LDaRs/IxbP99P1aSQX8tadMvCLv98CP+JpNCQD/B4wefT9ztH3/PW7Zck2uFW020R4cBIAIvSzxRfxZLf/KEyUvjC1KzLjzWD30Fo7z0jQSAWFo/f/KWW+086i0TxLsl2fo/F9d2BdyZqjkKClICkF8ODFabrCUEQNaq4RgdU1Y0ZElHiPIDEH+JKQGuKyAgfO7OpTOeOAHXWgDyRUfd8HR3dZqjAQbHzpwbrA5y/rfMtdMZMN9dGSWKiyGjVit8UtjZ5/3KiZR6LdlyL7z6pp+tFLj0md1Krlz/yK82aFXDz2JZ1Tu74x0FKwo0j8Im6s9i6M92ThY8+sPYFDfAFwifyVoSf+Rh5G0//PtUETf9hRSjKQKoztK7ACLhrSjwfHKpPQSQLCMIj9UtZhIBBMhpHlB63rZPgmoVAgR5XTi94a69k25ErdoPwINI/dJo9++DvWr0i6zhxy4+7lMTtMpRRbjW6jm7j6ZghaLX81bfBZO2w/eZpZ558Tn3RrYCCCkrKcyKksoQPYVEqRWoLLOU3aRbVqL+LIj+bKUB9pWzq0N77vCWWmcedu7m5kB/GLNbJSb31qiNfAjKyaoX5hmEVCdDkxlS9WZEQKOWAMP/Y9ZZS0xtCoNospZU01Zp4CRDYWrqGpNk8Ya5Lsoixq1jNvnJ63+sbD6rdIEUogw2eYISKPBYNAuGWIIN1w2Dg3Uk6s/R1p/frH08mLzhCIwVVtmQuvozZIpYYhFAiN8DlO2/uvb6H6tc31/nqU4abCtDPmE0HoFsCNo9e+Zh7yZgGo4CU6s82uIBqJrdmyAfCVYh7L9EYH7jnnMb3cFjTcqSdNrWz+0ljRUoi83NKBKw0Q3N+64vq3HnM/8e8gFTVrwIvjeBTdSfo60/0pvL53bd8WUm9TTsoUm/jv60xwEkJmQAE3+pukkzNQ9Tl07kU5Tjk7csr884B3YvAkaWEkIbf/TV8/7mkujIjI/71iT5SBjABA5hf/ASaZeRMK0dos3rfxMCNvd3nCcbBLcUxWHFq67yjJOoP0dHf8IVZoLVjCtWrCfZtza2HIdmyDoCQMyslM/QCgxSRCCjpAyorKIpZoVJyA2tE++pIgxgER6DHDM6rakyvhYP7YQYcW2xRvLcT/v/KhLmdyQ9536X+ei016/MeOKpHvCsIlF/5lt/wmuhP4PH9jSgP4UnK0BpV8CIjAOQVqJGDWwLENcBoGmJjf+IcCTTLJylPmJp4d5gYWDtCR+kKF1BxaBQHiTEp8jSG5VGnyf6Pm40yj4NSydPov6Ml5ej/hRbQCoPYLfaE8NxLtsHZlZSRg2wbjbAqkiZgJn9LhKmwB+UgIWPrZA/lSwPrRyKYGS6ioxttqpdrhy3L8fOYvxmldo8SrI8KIn6c/T0ZxqSlAEI8lGColZQQoDk7tjSjLhdOoZ5KR+1iGTGmYmjiKlOcuSkwu+xD4aaQsS3fvv+5j5sbILYW//79yE/Gnw4t7ueJleyHBtKnvIgZMzyTxtrpUzh97TClZLzdN2tPIn6c5T0573G9addBiByR8gc1moJKftYRRYgSMcm+Km2DwApPqMOqpOjCMduwdAr3JrBi+X1ultDmhCID9dLbQAb9tT5HcJZGkO4OueD6VlWqfJySOLKi5H5ejzJ0kBRBttHsu9oP5vwsDv9+Q4xmEmX1+tK1J/x0l1w/UnKAASJaAcrAJHFfCcjFGJDqmIo9gYgZTqznV8AEeeQ6SfysdmVtjF5rJ3HzhBb1ezdpgTC8y5ottk13WrS37cBFjx049n5bBVEq4xg982c2E5Yo5p8kVZvx89KKGoodhOgAsDWMrSlMxBV5ZsWflF/iuWlqD+jCUh+fOeT1I2yD6nTABtK2c7E7/DNBpYdYE8T5c9JcILFlXnJubhqDDCIL698hh1g73bv7kXlC2rTTHOAIcr+1C7nkGA+y3YCgx9FvS6tDJ/PeVyD/qj2DjWOwtKovH/7+ron8tTvP+kD4JynkrKSdOPfxmChQLhoW4o17f+81vfF5ZrGKepPObm2wPoz1gKiAUrRxvpJ0/nTmRd/FLJgdmeTHQDZAvC+WuFufwAQuURaMbI/7CuoZQDd3LiX+bXFAO1l7hYDhIuookwA9NpDl31KQNNEpN9W9jiKgj8ua1FWG8RqM06VMq8aPGCHtfL04w84/xycnMJpEDXX/cbursce07jzxOm0DEJWQIxrgVd3/e4QCYRPRbCCIr1RMcG0rET9GS0vLbj+jF1qogEoER3E/Lf5Nlb8ZtCsOBEi4Ng9rzo9CkbbWsMDcHz1tcSDgk8sgLgZtsgRN4LPFHS2AFFlUbVpIB+llYeP9mlaaANWGLMT/bDF2kTOVuS/q0aK9fV9WYPMr0fAAhFW4EjiGn3iPUvr9joQM4qo5EIVzw/rxKjgHCkDpP+HZSSaxCbqz3iM1hZUf9pVAMKCoXMCCGvH1lW2ZqOtB4KFI4B0jVAUC1IxNAHEBlgBpNIZdke+fbY5AjAEwH2N2t6O/wtA08r2tRhBmEqAlLlMu1md8DV1sxXBMJYlUvYlZTNLkTiZLZwmXFUYjn7JZEdhMJn5q+JhmllV/JwqB5yjoljs4VGJhLx8LhWUbwqbqD9Rf/KkdM62Ld05ONkUes/LSVDtExGOrBoNMLFzeA0GIQOl7ACDfEQ6DHYVuIcMYPi8ARY+7rkJgeBon6xDAs4qziTS0TGRqmYmSAHXk/5wDcxgjvsnk2bfEUlzDeVlYKbzf/1umIoQVrLkeuAuogyx0dMswqJlk0rUn/FyZQH1p9KTUWVxKN8HghnM0tl3OPbUTt8dC6wilQL40noK0PkvP+JnYdwpSCoEUatmlCvAChI4lAIIAbKlJ2WyfpDtzs0DyPvbWZZ30yKzlWtDpop54WN3suAhZPrF1VR5bAAeLPUYE4jT1zk6+YBPAKVP3PwTy4lXGo4PbRDsnPAWlx5drWPKzNZvI1yH+FmG2OAZZz5+dqrvLn3+fJr7kVPgfxKJ+jNeriyY/tRyScRwAKQsaRERHbPbEwQQrEnDcdeoHgfAKiRmAeI43w0Lh6ng0aDWSPZAN9wLLB8J59MW5SxZsW0uqtDWlNjSFEoeY9kc/xtRSU7iaaElCRbqq9pLsB/FQ2yhJ0R4I0r4VGW9H/7juX3PZFJA3z/na6s1VNBf6RNNlOcokqg/4+WVBdGfRmq/0Ek1QKadYj5KQBRIcssgIQuQSEzni2TCshoiMwAWieXdAD4TOAJIz4VPTAmBaScv2rQAESAKoH5TTF/Pzeam8znvCd7bXCJJuLn27Ref6+Maaxc1/STorydTQtx6zLIemTLuIYycj6tdVIi+CYn6Ux6no6w/rWkDRENkSqq0Ku/1SGVYUpaO9UEF0O0ff69v98MQ+MZqEkC+Vm1mgiqmZAnJPniwDkBNSvg4nLCsgp0tyn5WdB8kKJAsSxVEFz55yqSHDhQ96rppifozv/qjhaFJ9KdSDChPwviKN2ySVWrMDnX8jZIAhclMZ//9f1qjAJJoMx8SunM22u4B2jAlBQ5Qpjmgw/ug9507xN7SOAAiXOwDGTW7Ienjl+8OakZPW6L+zK/+QD3HJ9SfZFqdODCAsqc2uq3tXMtHAW7tZjjoAVZHRs1UVbACJ/n7BC01Ww2sHrNQoAQ0BScPY+Nq1J/mg9m8qupRWf3xVlG2wlZXf6ZTOi0QATDqwfZVBh0ACST/ULScQveha6HgmJKsDmPj6kGL8m9wcxWABAcfRLSZ5Un6wLi856IdtlTRizJ6hkT9mR39ORACmjZAsC4A6QmTei4RAJFvZAGadfKZ1PoJhb6qMBz9BwfImc3FYMZiAPEwiHzWsWlajqL+1CHkw9SfRl2wuibhJINOCYnsV1MQ3Ep3PV229DGfOVKePKlLSl9/9VctG6PTo42QMOg/S1JHL6q6HkdBf65M2WKdpv60Dkuhit5Pcv3waZ+zPsCijJa6elH3vKg/UaJEieIWR/4fY2sZ1Lh7NyEAAAAASUVORK5CYII=";var gr="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAA2UlEQVR4nGNgwAPeHo35D8K45JnwaRQS/YXPbAacmkEa/3z/BaZx2c6Ey1YGIgAjLsV8PF/g7E9feMC0sPUSRhTNuPwGcjI28AlqENzZIIUw/O41G8O58z/BkiycbGDFl278R3GRENQyJpBTkE1DtgFkyIMnn+EGwQDIApA+uB9AzgeZCtIEMhlmu54GI1gMFgafvvDA/Y4SADADYABkG7LfPyFpxIgq9IBD1ojsbAZ0zYTiFlvoY00ksFAGYWw2wtWhC4BD/gsiQUBcxAbxEiI4MAE+p2PLYQAGj4EmRoa2cwAAAABJRU5ErkJggg==";var gt=()=>i`<span class="pet-art" aria-hidden="true"><div class="sprite" style=${{backgroundImage:`url(${hr})`}}></div><img class="star" src=${gr} alt="" /></span>`;function Yo(e,t){let s="animation-duration:"+((innerWidth+180)/(100+t*12)).toFixed(1)+"s;animation-delay:-"+t*9%14+"s",n=()=>{e.hideKey&&bn(e.hideKey,e.hideHash??"")},r=[i`<div class="bub">${e.label&&[i`<span class="badge dim">${e.label}</span>`," "]}<span>${e.text.length>48?e.text.slice(0,47)+"\\u2026":e.text}</span></div>`,i`<${gt} />`],o=e.open;return e.hash?i`<a key=${e.key} class="lob" style=${s} title="open the decision" href=${e.hash} onClick=${n}>${r}</a>`:e.href?i`<a key=${e.key} class="lob" style=${s} title=${e.label==="report"?"open the report":"open the PR"} href=${e.href} target="_blank" rel="noopener" onClick=${n}>${r}</a>`:i`<div\n key=${e.key}\n class="lob"\n style=${s}\n title="click to open"\n onClick=${()=>{n(),o&&O(o.type,o.key)}}\n >\n ${r}\n </div>`}function $r({state:e}){let t=e.snapshot,s=mr(t?.attention||[],{lobs:e.prefs.lobs,hidden:e.lobHidden,preview:e.preview,previewHelm:t&&t.helms.length?t.helms[0].grounds:void 0}),n=s.map(r=>r.key).join("|");return qt(()=>s.map((r,o)=>Yo(r,o)),[n])}function $t({box:e}){if(!e)return null;let t=s=>{s.target===s.currentTarget&&le()};return i`<div id="lightbox" class="lightbox" role="dialog" aria-label=${e.name} onClick=${t}>\n <button type="button" class="lbclose" title="close" onClick=${le}>\xD7</button>\n <img class="lbimg" src=${e.src} alt=${e.name} />\n <a class="lboriginal" href=${e.src} target="_blank" rel="noopener">open original \xB7 ${e.name}</a>\n </div>`}var me=i`<span class="x" onClick=${de}>\xD7</span>`,vr=(e,t,s)=>i`<span class="seg">${t.map(([n,r])=>i`<button class=${n===s?"on":""} onClick=${()=>e(n)}>${r}</button>`)}</span>`;function Qo(e,t){let s=vr(vn,[["table","table"],["cards","cards"]],t.view),n=vr(kn,[["on","on"],["off","off"]],t.lobs?"on":"off"),r=e.attentionError?e.attentionError:(e.attentionKinds||[]).join(" \\xB7 ");return[me,i`<h3>\u2699 settings <span class="dim" style="font-weight:normal">\xB7 this browser only</span></h3>`,i`<div class="settings"><div class="row"><span class="lbl">view<span class="hint">cards or table (notices stay a table)</span></span>${s}</div><div class="row"><span class="lbl">lobs<span class="hint">crawling lobsters in this page</span></span>${n}</div><div class="row"><span class="lbl">attention<span class="hint">kinds shown \u2014 attentionKinds in config.toml (read-only here)</span></span><span class="dim" style="font-size:11px;text-align:right;max-width:260px">${r}</span></div></div>`]}function Xo(e,t){let s=fe(t.numbers,new Map(t.numbers.map((r,o)=>[r,t.titles[o]])),e.number),n=t.nextMergeable?[" \\xB7 ",i`<span class="ok">next mergeable</span>`]:t.blockedBy?" \\xB7 blocked by #"+t.blockedBy:t.nextNumber?" \\xB7 next is #"+t.nextNumber:"";return[s," \\xB7 ",t.position," of ",t.size," \\xB7 floor ",t.floor,n]}var Zo=e=>t=>{t.preventDefault(),O("dispatch",e)};function ea(e,t){let s=Js(e,t),n=s.pr,r=s.stack,o=s.watch,a=dt(n),l=s.chain.length?s.chain.flatMap((c,d)=>[d?" \\u2192 ":"",c.culled?i`<span class="dim">${c.id.slice(0,8)} (culled)</span>`:[i`<a href="#" onClick=${Zo(c.modalKey)}>${c.id.slice(0,8)}</a>`," ",i`<span class="dim">${c.verb}</span>`]]):i`<span class="dim">none</span>`,u=o?[i`<div>${o.key} \xB7 owner ${o.owner||"?"} \xB7 ${o.lastCheckedAt?["checked ",v(o.lastCheckedAt)," ago"]:"never checked"}</div>`,o.lastError&&i`<div class="bad">${o.lastError}</div>`,i`<div class="dim" style="font-size:11px;margin-top:4px">cursor</div>`,T(o.cursor)]:i`<div class="dim">no watch \u2014 <code>lobstah watch add ${n.url}</code> registers one</div>`;return[me,i`<h3>${it(n)} ${n.title||""} <span class=${"badge "+E(n.badge)}>${n.badge.text}</span>${n.draft&&n.badge.text!=="draft"&&[" ",i`<span class="badge pr-draft">draft</span>`]}</h3>`,i`<div class="sub">${n.repo} \xB7 ${n.state} \xB7 observed ${v(n.observedAt)} ago${n.gate&&" \\xB7 gate "+n.gate}</div>`,i`<div class="sec">checks</div><div>${Ce(n)}</div><div class="sec">review</div><div>${a||i`<span class="dim">no review yet</span>`}</div><div class="sec">merge</div><div>${Te(n)}</div>${n.repairWait&&i`<div class="sec">repair waits</div><div>${n.repairWait.heldBy}: ${n.repairWait.reason}${n.repairWait.until?" \\xB7 until "+n.repairWait.until:""}</div>`}<div class="sec">refs</div><div>${n.headRefName||"?"} \u2192 ${n.baseRefName||"?"}</div><div class="sec">stack</div><div>${r?Xo(n,r):i`<span class="dim">not stacked</span>`}</div><div class="sec">dispatch chain</div><div>${l}</div><div class="sec">watch</div>`,u]}var kr=(e,t)=>(e==="codex"?"codex resume ":"claude --resume ")+t;function ta(e){let t=Date.now()-Date.parse(e.heartbeatAt)>18e5;return[me,i`<h3>\u26F5 ${e.man}</h3><div class="sub">helm of <b>${e.grounds}</b> (${(e.repos||[]).join(", ")})</div><div class="sub">${e.cwd??"?"}${e.host&&" \\xB7 "+e.host}</div><div class="sub">session ${e.sessionId??""} \xB7 signed on ${v(e.signedOnAt)} ago \xB7 heartbeat <span class=${t?"warn":"ok"}>${v(e.heartbeatAt)} ago</span></div>`,i`<${nt} worker=${e} />`,e.sessionId&&[i`<div class="sec">open this session</div>`,T(kr(e.harness,e.sessionId))],e.transcript&&[i`<div class="sec">transcript</div>`,T(e.transcript)]]}function sa(e){return[i`<div class="sec">report \xB7 ${e.title}</div>`,i`<div class="sub">${ns(e)} \xB7 <a href=${G(e.key)} target="_blank" rel="noopener">open the report \u2197</a></div>`]}function na(e,t,s){let n=e.claimedBy&&e.claimedBy.startsWith("wt:")?[i`<div class="sec">worked by trap</div>`,i`<div class="claimedby">claimed by ${at(e.claimedBy.slice(3))}</div>`,T(U(e.claimedBy)),i`<div class="dim" style="font-size:11px">an opted-in interactive session mans this seat \u2014 attach would resume someone\'s live thread. Message it instead: lobstah send ${U(e.claimedBy)} "\u2026"</div>`]:[i`<div class="sec">open this session</div>`,T("lobstah attach "+e.id)];return[me,i`<h3>${e.id.slice(0,8)} <span class=${"badge v-"+e.verb}>${e.verb}</span></h3>`,i`<div class="sub">${e.repo} \xB7 ${e.lane} ${e.bucket} \xB7 ${v(e.verbAt)}${e.for&&[" \\xB7 ",Ge(e)]} ${ue(e)}</div>`,n,e.worker&&i`<${nt} worker=${e.worker} headless=${!e.claimedBy?.startsWith("wt:")} />`,e.worktree&&[i`<div class="sec">worktree${e.worktreeOf?" (reused from "+e.worktreeOf.slice(0,8)+")":""}</div>`,T(e.worktree),e.worktreeKept&&i`<div class="dim" style="font-size:11px">kept: ${e.worktreeKept}</div>`],e.transcript&&[i`<div class="sec">transcript</div>`,T(e.transcript)],t&&sa(t),Un(e,s)]}function ra(e){let t=Ae(e),s=e.requested?[e.repo??""," \\xB7 ",e.harness??""," \\xB7 requested ",v(e.requested.at)," ago from the glass; the helm starts it with man throw --new."]:e.starting?e.starting.failedAt?[e.repo??""," \\xB7 reserved ",v(e.starting.reservedAt)," ago \\xB7 start failed: ",e.starting.reason??"",". Its addressed work stays queued; a session can still redeem the ticket, or `lobstah stow --wt ",e.name??e.trapId,"` withdraws it."]:[e.repo??""," \\xB7 reserved ",v(e.starting.reservedAt)," ago \\xB7 starting: waiting for a session to redeem the ticket (due by ",e.starting.deadline,")"]:e.live?[e.repo??"addressed bait only"," \\xB7 session ",e.sessionId??""," \\xB7 signed on ",v(e.signedOnAt)," ago \\xB7 ",t.listen," \\xB7 heartbeat ",t.hb]:"signed off \\u2014 registration gone; the lifecycle, messages, and catches are the surviving record. Re-soaking the same worktree restores this address.",n=e.notices.length?e.notices.map(a=>i`<div key=${a.seq} class="loglines">${v(a.at)} ago \xB7 <b>${a.kind}</b> \u2014 ${a.text}</div>`):i`<div class="empty">none recorded</div>`,r=e.messages.length?e.messages.map(a=>i`<div key=${a.file} class=${"msg"+(a.from==="helm"?" from-helm":"")}><div class="hdr">from ${a.from} \xB7 ${a.at&&[v(a.at)," ago"]} \xB7 ${a.state==="pending"?i`<span class="warn">pending</span>`:i`<span class="ok">delivered</span>`}</div>${a.text}${a.attachments?.length?ce(a.attachments,l=>Ls(e.trapId,l)):""}</div>`):i`<div class="empty">none</div>`,o=e.catches.length?e.catches.map(a=>i`<div key=${a.lane+":"+a.id} class="catch click" onClick=${S("dispatch",a.lane+":"+a.id)}><div class="hdr"><b>${a.id.slice(0,8)}</b><span class=${"badge v-"+a.verb}>${a.verb}</span><span class="dim">${v(a.verbAt)}</span>${ue(a)}</div>${a.note&&i`<div class="loglines">${R(a.note)}</div>`}</div>`):i`<div class="empty">none yet</div>`;return[me,i`<h3>\u{1FAA4} ${e.label??`wt:${e.trapId}`} <span class="badge">${e.requested?"requested":e.starting?e.starting.failedAt?"start failed":"starting":e.live?"live":"signed off"}</span></h3>`,i`<${nt} worker=${e} />`,e.worktree&&i`<div class="sub">${e.worktree}</div>`,i`<div class="sub">${s}</div>`,i`<div class="sec">window</div>`,Y(e),Q(e),e.live&&e.sessionId&&[i`<div class="sec">open this session</div>`,T(kr(e.harness,e.sessionId)),i`<div class="dim" style="font-size:11px">as registered at sign-on \u2014 a hookless enlistment may hold a made-up id</div>`],i`<div class="sec">lifecycle (${e.notices.length})</div>`,n,i`<div class="sec">messages (${e.messages.length})</div>`,r,i`<div class="sec">catches (${e.catches.length})</div>`,o]}function br({snapshot:e,modal:t,prefs:s,detail:n=null}){if(!e||!t)return null;let r=re(e,t,n);if(!r&&t.type==="dispatch"&&n?.key===t.key)return[me,i`<h3>${t.key.slice(t.key.indexOf(":")+1,t.key.indexOf(":")+9)}</h3>`,i`<div class=${n.error?"bad":"dim"}>${n.error??"loading\\u2026"}</div>`];if(!r)return null;if(t.type==="settings")return Qo(r,s);if(t.type==="pr")return ea(e,t.key);if(t.type==="helm")return ta(r);if(t.type==="dispatch"){let o=js(e,r);return na(r,o,n)}return ra(r)}var oa=e=>i`<tr key=${e.seq}><td class="dim">${v(e.at)}</td><td>${e.kind}</td><td class="grow">${e.text}</td><td class="dim">${e.repo??""}</td></tr>`,yr=({inp:e,more:t})=>[M(["at","kind","text","repo"],e.list.map(oa),"no notices"),t&&i`<${pe} kind="notices" more=${t} />`];function _r(e){let t=new Map;for(let s of e.prs){let n=t.get(s.stackId)||[];n.push(s),t.set(s.stackId,n)}return e.stacks.map(s=>({s,prs:t.get(s.id)||[]})).filter(s=>s.prs.length)}var wr=(e,t)=>fe(e.numbers,new Map(t.map(s=>[s.number,s.title]))),is=e=>e.state+(e.draft?" \\xB7 draft":"")+(e.state==="MERGED"&&e.mergedAt?" \\xB7 "+e.mergedAt:"");function Sr(e){let t=n=>{let r=String(n??"");return r.length>24?r.slice(0,23)+"\\u2026":r},s=e.watches.map(n=>i`<tr key=${n.key}><td>${n.key}</td><td>${n.owner}</td><td title=${n.cursor}>${t(n.cursor)}</td><td>${n.lastCheckedAt&&v(n.lastCheckedAt)}</td><td>${n.lastError||""}</td></tr>`);return[i`<h2>other watches</h2>`,M(["key","owner","cursor","last check","error"],s,"no other watches")]}function aa(e,t){let s=_r(e).flatMap(({s:n,prs:r})=>[i`<tr key=${"stack:"+n.id}><th colspan="8">${wr(n,r)} \xB7 floor ${n.floor}${n.open?" \\xB7 open":" \\xB7 history"}${n.readiness&&i`<div class=${n.readiness.allReady?"ok":"dim"}>${n.readiness.text}</div>`}</th></tr>`,...r.map(o=>i`<tr key=${o.key} class="rowhead" onClick=${S("pr",o.key)}><td>${it(o)}</td><td class="grow">${o.title||""}</td><td>${is(o)}</td><td>${Ce(o)}</td><td>${dt(o)}</td><td>${Te(o)}</td><td>${Jt(o.watch)}</td><td>${o.gate||""}</td></tr>`)]);return[M(["PR","title","state","checks","review","merge","watch","gate"],s,"no PR evidence"),t,Sr(e)]}function ia(e,t){let s=_r(e),n=o=>i`<div key=${o.key} class="card" onClick=${S("pr",o.key)}><div class="top">${lt(o)}<span class=${"badge "+E(o.badge)+B(o.badge.text)} title=${j(o.badge.text)}>${o.badge.text}</span></div><div class="meta" title=${`${o.repo} \\xB7 ${is(o)}`}>${o.repo} \xB7 ${is(o)} \xB7 ${Te(o)}</div><div class="foot"><span>${Ce(o)}</span><span>${Jt(o.watch)}</span>${o.gate&&i`<span>gate ${o.gate}</span>`}</div></div>`;return[s.length?s.map(({s:o,prs:a})=>i`<${ee} key=${"stack:"+o.id}><h2>${wr(o,a)} \xB7 floor ${o.floor}${o.open?"":" \\xB7 history"}</h2>${o.readiness&&i`<div class=${o.readiness.allReady?"ok":"dim"}>${o.readiness.text}</div>`}<div class="cards">${a.map(n)}</div><//>`):i`<div class="empty">no PR evidence</div>`,t,Sr(e)]}function Dr({inp:e,more:t}){let s=t&&i`<${pe} kind="prs" more=${t} />`;return e.view==="cards"?ia(e,s):aa(e,s)}var la=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],da=5,ls=(e,t,s=t+"es")=>`${e} ${e===1?t:s}`,ca=e=>e.count?`${ls(e.count,"catch")} on ${e.date}`:`No catches on ${e.date}`,xr=new WeakSet,pa=e=>{!e||xr.has(e)||(xr.add(e),e.scrollLeft=e.scrollWidth)},Gr={ArrowUp:-1,ArrowDown:1,ArrowLeft:-7,ArrowRight:7};function ua({page:e,focus:t}){let s=e.weeks.flat(),n=t&&s.some(u=>u.date===t)?t:e.today,r=u=>{let c=s.findIndex(p=>p.date===n),d;if(u.key in Gr)d=c+Gr[u.key];else if(u.key==="Home")d=0;else if(u.key==="End")d=s.length-1;else return;u.preventDefault();let h=s[Math.max(0,Math.min(s.length-1,d))];Lt(h.date),u.currentTarget.querySelector(`[data-date="${h.date}"]`)?.focus()},o=e.weeks.length-1,a=(u,c,d)=>{let h=u[d];if(!h)return i`<td key=${c} class="pad"></td>`;let p=ca(h),m=c<4?" tl":c>o-4?" tr":"";return i`<td\n key=${c}\n role="gridcell"\n class=${`day l${h.level}${d<2?" below":""}${m}`}\n data-date=${h.date}\n data-tip=${p}\n aria-label=${p}\n tabindex=${h.date===n?0:-1}\n onFocus=${()=>Lt(h.date)}\n ></td>`},l=e.months[0]?.week??e.weeks.length;return i`<div class="heat-scroll" ref=${pa}>\n <table class="heatmap" role="grid" aria-label=${`Catches per day, last ${e.weeks.length} weeks`} onKeyDown=${r}>\n <thead aria-hidden="true">\n <tr>\n <td class="wd"></td>\n ${l>0&&i`<td colspan=${l}></td>`}\n ${e.months.map((u,c)=>i`<td key=${u.week} class="month" colspan=${(e.months[c+1]?.week??e.weeks.length)-u.week}><span>${u.label}</span></td>`)}\n </tr>\n </thead>\n <tbody>\n ${la.map((u,c)=>i`<tr key=${u} role="row">\n <td class="wd" aria-hidden="true">${c%2?u:""}</td>\n ${e.weeks.map((d,h)=>a(d,h,c))}\n </tr>`)}\n </tbody>\n </table>\n </div>`}var fa=()=>i`<div class="heat-legend" aria-hidden="true">\n Less ${[0,1,2,3,4].map(e=>i`<span key=${e} class=${"swatch l"+e}></span>`)} More\n </div>`;function ma({page:e}){let t=[["today",e.catchesToday],["this week",e.catchesThisWeek],["total",e.totalCatches]];return i`<div class="stat-tiles">\n ${t.map(([s,n])=>i`<div key=${s} class="stat-tile"><div class="n">${n}</div><div class="dim">${s}</div></div>`)}\n </div>`}function ha({page:e}){let t=e.perTrap.slice(0,da);if(!t.length)return null;let s=t[0].catches||1;return i`<section class="top-traps">\n <h2>Top traps <span class="dim">\xB7 all time</span></h2>\n <ol>\n ${t.map(n=>i`<li key=${n.name}><span class="name" title=${n.name}>${n.name}</span><span class="bar"><span style=${{width:`${n.catches/s*100}%`}}></span></span><span class="n">${ls(n.catches,"catch")}</span></li>`)}\n </ol>\n </section>`}function Cr({page:e,error:t,focus:s}){return e?i`<div class="stats">\n <${ma} page=${e} />\n ${e.totalCatches===0&&i`<div class="stats-empty"><${gt} /><div><b>No catches yet.</b> <span class="dim">A dispatch that finishes done is a catch; each one lands on its day here.</span></div></div>`}\n <section class="heat">\n <h2>Catches per day</h2>\n <${ua} page=${e} focus=${s} />\n <div class="heat-foot">\n <span class="dim"\n >${e.undated>0?`${ls(e.undated,"earlier catch")} ${e.undated===1?"has":"have"} no recorded day${e.historyFrom?`; daily history starts ${e.historyFrom}`:""}.`:""}</span\n >\n <${fa} />\n </div>\n </section>\n <${ha} page=${e} />\n ${t&&i`<div class="dim">${t}; showing the last numbers read.</div>`}\n </div>`:i`<div class="empty">${t??"loading stats\\u2026"}</div>`}var ds=e=>e.requested?"requested":e.starting?e.starting.failedAt?"start failed":"starting":e.harness??(e.live?"":"signed off"),Tr=e=>e.live||e.starting?"":" dim";function ga(e){let t=Ae(e);return i`<tr key=${e.trapId} class=${"rowhead"+Tr(e)} onClick=${S("trap",e.trapId)}><td><b>${e.label??`wt:${e.trapId}`}</b></td><td>${e.repo??"\\u2014"}</td><td class="grow">${e.worktree??""}</td><td>${e.harness&&i`<${H} worker=${e} cls="" />`}</td><td>${(e.sessionId??"").slice(0,8)}</td><td>${Re(e)}${Q(e)}</td><td>${t.listen}</td><td>${t.hb}</td><td>${Qt(e)}</td><td class="catchn" title="catches">\u{1F99E} ${te(e.totalCatches??0)}</td><td>${Y(e)}</td></tr>`}function $a(e){let t=Ae(e),s=Qt(e),n=`${e.repo??(e.live?"addressed bait only":"history")}${e.sessionId?" \\xB7 session "+e.sessionId.slice(0,8):""}`;return i`<div key=${e.trapId} class=${"card"+Tr(e)} onClick=${S("trap",e.trapId)}><div class="top"><b>\u{1FAA4} ${e.label??`wt:${e.trapId}`}</b>${!e.requested&&!e.starting&&e.harness?i`<${H} worker=${e} />`:i`<span class=${"badge"+B(ds(e))} title=${j(ds(e))}>${ds(e)}</span>`}</div><div class="meta" title=${n}>${n}</div>${e.worktree&&i`<div class="note" title=${e.worktree}>${e.worktree}</div>`}<div class="note" title=${pt(e)}>${Re(e)}</div>${Q(e)}<div class="foot"><span>${t.listen}${e.live&&[" \\xB7 heartbeat ",t.hb]} \xB7 <span class="catchn" title="catches">\u{1F99E} ${te(e.totalCatches??0)}</span>${s&&[" \\xB7 ",s]}</span><span class="footact">${Y(e)}</span></div></div>`}function Ar({inp:e}){let t=e.list.map(n=>n.x),s=i`<div class="tabhead"><${ct} /></div>`;return e.view==="cards"?[s,t.length?i`<div class="cards">${t.map($a)}</div>`:i`<div class="empty">no traps soaking</div>`]:[s,M(["address","repo","worktree","harness","session","now","listening","heartbeat","mail","catches","window"],t.map(ga),"no traps soaking")]}function Rr({state:e}){let t=oe(e),s=e.route,n=t?Xe():[],r=t&&zs(t,{st:e.prefs,modal:e.modal,detail:e.detail},Date.now()),o=u=>({left:e.snapshot?Ks(e.snapshot,e.older,u):0,loading:e.olderLoading===u,error:e.olderError?.kind===u?e.olderError.text:void 0}),a=u=>!r||u!==s?null:u==="deck"?i`<${dr} inp=${r.deck} drafts=${e.drafts} focus=${e.focusDecision} viewedHere=${e.viewedHere} unread=${n.length} />`:u==="dispatches"?i`<${cr} inp=${r.dispatches} more=${o("dispatches")} />`:u==="traps"?i`<${Ar} inp=${r.traps} />`:u==="prs"?i`<${Dr} inp=${r.prs} more=${o("prs")} />`:u==="reports"?i`<${Vn} inp=${r.reports} />`:u==="stats"?i`<${Cr} page=${e.stats} error=${e.statsError} focus=${e.statsFocus} />`:i`<${yr} inp=${r.notices} more=${o("notices")} />`,l=u=>{u.target===u.currentTarget&&de()};return[i`<${ur} d=${t} inp=${r} tab=${s} st=${e.prefs} stale=${e.stale} unread=${n.length} />`,se.map(u=>i`<main key=${u} id=${"page-"+u} class=${t&&u===s?"tabpage on":"tabpage"}><div id=${u}>${a(u)}</div></main>`),i`<footer id="foot"><${fr} d=${t} /></footer>`,i`<div id="lobs"><${$r} state=${e} /></div>`,i`<${$t} box=${e.lightbox} />`,i`<div id="overlay" class=${e.modal&&t?"open":t?"":void 0} onClick=${l}><div class="modal" id="modalbox"><${br} snapshot=${t} modal=${e.modal} prefs=${e.prefs} detail=${e.detail} /></div></div>`,t&&i`<${ar} open=${e.decisionModal} order=${ye()} drafts=${e.drafts} extensions=${t.answerLimits?.extensions??[]} />`,t&&i`<${ir} unread=${n} dismissed=${e.alertDismissed} />`]}function va({view:e}){if(e.state==="loading")return i`<main class="reportview"><div class="dim">loading\u2026</div></main>`;if(e.state==="error")return i`<main class="reportview"><div class="bad">${e.error}</div></main>`;let t=e.report;return i`<main class="reportview">\n <h1>${t.title}</h1>\n <${nr} r=${t} text=${e.text!==void 0?{text:e.text}:{error:e.error}} />\n </main>`}function Mr({view:e,box:t}){return[i`<${va} view=${e} />`,i`<${$t} box=${t} />`]}async function Pr(e){let t=encodeURIComponent(e),s,n;try{let r=await fetch(`/report/${t}/meta`);if(!r.ok)return{state:"error",error:`report not found (${r.status})`};n=r.headers?.get("x-lobstah-token")??void 0,s=JSON.parse(await r.text())}catch{return{state:"error",error:"report could not be read"}}try{let r=await fetch(Fs(e)),o=r.ok?{text:await r.text()}:{error:`report not found (${r.status})`};return{state:"ready",report:s,...o,...n?{token:n}:{}}}catch{return{state:"ready",report:s,error:"report could not be read"}}}async function Ir(e){if(!(e.state!=="ready"||e.text===void 0||e.report.acked||!e.token))try{await fetch("/requests",{method:"POST",headers:{"content-type":"application/json","x-lobstah-token":e.token},body:JSON.stringify({kind:"report-viewed",payload:{key:e.report.key}})})}catch{}}var ka=3e4,Er="lobstah-glass-page",Lr="lobstah-glass-shown",ba=20;function ya(){try{let t=sessionStorage.getItem(Er);if(t&&/^[A-Za-z0-9_-]{8,64}$/.test(t))return t}catch{}let e="";try{e=Array.from(crypto.getRandomValues(new Uint8Array(12)),t=>t.toString(16).padStart(2,"0")).join("")}catch{for(;e.length<24;)e+=Math.random().toString(36).slice(2);e=e.slice(0,24)}try{sessionStorage.setItem(Er,e)}catch{}return e}var Or=null,Nr=()=>Or??(Or=ya()),ps=[];function Hr(){try{let e=JSON.parse(sessionStorage.getItem(Lr)??"[]");if(Array.isArray(e))return e.filter(t=>typeof t=="string")}catch{}return ps}function _a(e){ps=[...Hr(),e].slice(-ba);try{sessionStorage.setItem(Lr,JSON.stringify(ps))}catch{}}function wa(e){if(!e||typeof e!="object")return!1;let{id:t,hash:s}=e;if(typeof t!="string"||!t||!Bs(s)||Hr().includes(t))return!1;if(_a(t),s==="")return!0;let n=ke(s);return n!==null?location.pathname!==G(n)&&location.assign(G(n)):location.pathname!=="/"?location.assign(`/${s}`):location.hash!==s&&(location.hash=s),!0}var cs=!1;async function Ie(){if(!cs){cs=!0;try{let e=document.hidden?"hidden":"visible",t=await fetch(`/api/presence?page=${Nr()}&vis=${e}`,{method:"POST",cache:"no-store"});if(!t.ok)return;wa((await t.json())?.show)}catch{}finally{cs=!1}}}function us(e){let t=null,s=0,n=()=>{let r=document.hidden?ka:e??0;r!==s&&(t&&clearInterval(t),t=r?setInterval(()=>{Ie()},r):null,s=r)};document.addEventListener("visibilitychange",()=>{n(),Ie()}),window.addEventListener("pagehide",()=>{try{navigator.sendBeacon?.(`/api/presence?page=${Nr()}&vis=gone`)}catch{}}),n(),Ie()}var fs=!1,Ee=null,Oe=null;function Sa(e){let t=e.headers?.get("x-lobstah-beats");if(t)try{return JSON.parse(decodeURIComponent(t))}catch{return}}async function ms(){if(!fs){fs=!0,Ie();try{let e=await fetch("/data",{cache:"no-store",headers:Oe?{"if-none-match":Oe}:{}});if(e.status===304){let t=Sa(e);t?ln(t):Ye(await Da())}else{if(!e.ok)throw new Error(`/data answered ${e.status}`);Oe=e.headers?.get("etag")??null,Ye(await e.json())}Ot(),g().route==="stats"&&Se()}catch{mn()}finally{fs=!1}}}async function Da(){Oe=null;let e=await fetch("/data",{cache:"no-store"});if(!e.ok)throw new Error(`/data answered ${e.status}`);return Oe=e.headers?.get("etag")??null,await e.json()}var jr=()=>{Ee||(Ee=setInterval(ms,2e3))},xa=()=>{Ee&&clearInterval(Ee),Ee=null};function Br(){document.addEventListener("visibilitychange",()=>{document.hidden?xa():(ms(),jr())}),ms(),document.hidden||jr()}var hs=()=>Os(location.hash),Ur=e=>window.addEventListener("hashchange",()=>e(hs()));function qr(){let e=ke(location.hash);return e===null?!1:(location.replace(G(e)),!0)}var Wr=()=>({snapshot:void 0,route:hs(),prefs:Xs(),modal:null,detail:null,older:Mt,olderLoading:null,olderError:null,stale:!1,lobHidden:en(),focusResults:{},preview:new URLSearchParams(location.search).has("lob"),drafts:{},focusDecision:ne(location.hash),decisionModal:ne(location.hash)?{key:ne(location.hash)}:null,viewedHere:{},alertDismissed:[],lightbox:null,stats:null,statsError:null,statsFocus:null}),Fr=Us(location.pathname);Fr!==null?Ga(Fr):qr()||Ca();function Ga(e){Pt(Wr());let t={state:"loading"},s=()=>xt(Z(Mr,{view:t,box:g().lightbox}),document.body);document.title="report \\xB7 lobstah glass",It(s),s(),us(2e3),Pr(e).then(n=>{t=n,n.state==="ready"&&(document.title=`${n.report.title} \\xB7 lobstah glass`),s(),Ir(n)}),document.addEventListener("keydown",n=>{n.key==="Escape"&&g().lightbox&&le()})}function Ca(){Pt(Wr());let e,t=!1,s=()=>{let a=g(),l=a.route!==e||!t&&!!a.snapshot;xt(Z(Rr,{state:a}),document.body),e=a.route,t||(t=!!a.snapshot),l&&window.scrollTo(0,0),a.focusDecision||(n=null),r(a.focusDecision)},n=null;function r(a){if(!a||a===n)return;let l=[...document.querySelectorAll("[data-decision]")].find(u=>u.getAttribute("data-decision")===a);l&&(n=a,l.scrollIntoView?.({block:"center"}))}It(s),s(),Ur(a=>{if(qr())return;let l=ne(location.hash);l!==g().focusDecision&&(n=null),k({route:a,focusDecision:l}),l&&ae(l),a==="stats"&&Se()});let o=a=>!!a&&(!!a.closest?.("input, textarea, select, [contenteditable]")||a.isContentEditable===!0);document.addEventListener("keydown",a=>{let l=g(),u=a.target;if(a.key==="Escape"){l.lightbox?le():l.decisionModal?o(u)&&u.closest?.(".dmodal")?(u.blur(),document.querySelector(".dmodal")?.focus()):ie():de();return}if(!(a.metaKey||a.ctrlKey||a.altKey||a.isComposing||a.keyCode===229||o(u)||l.lightbox)){if(l.decisionModal){a.key==="ArrowLeft"||a.key==="ArrowRight"?(a.preventDefault(),_e(a.key==="ArrowLeft"?-1:1)):/^[1-9]$/.test(a.key)?rn(Number(a.key))&&a.preventDefault():a.key==="Enter"&&u?.matches?.(".dmodal .dopt.on")&&l.decisionModal.key&&(a.preventDefault(),Ze(l.decisionModal.key));return}a.key==="d"&&!a.shiftKey&&!l.modal&&(a.preventDefault(),ae())}}),us(),Br(),g().route==="stats"&&Se()}})();</script>\n </body>\n</html>\n';
|
|
16086
16261
|
|
|
16087
16262
|
// apps/cli/src/glass-snapshot-thread.ts
|
|
16088
16263
|
init_dist2();
|
|
16089
|
-
import * as
|
|
16264
|
+
import * as fs70 from "node:fs";
|
|
16090
16265
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
16091
16266
|
import { Worker } from "node:worker_threads";
|
|
16092
16267
|
function workerEntry() {
|
|
16093
16268
|
if (COMPILED_BINARY) return "./glass-snapshot-worker.ts";
|
|
16094
16269
|
const url = new URL("./glass-snapshot-worker.js", import.meta.url);
|
|
16095
|
-
return
|
|
16270
|
+
return fs70.existsSync(fileURLToPath2(url)) ? url : void 0;
|
|
16096
16271
|
}
|
|
16097
16272
|
function startSnapshotThread() {
|
|
16098
16273
|
const entry = workerEntry();
|
|
@@ -16279,7 +16454,7 @@ function pollBody(full, now2) {
|
|
|
16279
16454
|
|
|
16280
16455
|
// apps/cli/src/glass-presence.ts
|
|
16281
16456
|
init_dist2();
|
|
16282
|
-
import * as
|
|
16457
|
+
import * as fs71 from "node:fs";
|
|
16283
16458
|
import * as http2 from "node:http";
|
|
16284
16459
|
import * as path60 from "node:path";
|
|
16285
16460
|
import { randomBytes as randomBytes5, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
@@ -16362,8 +16537,8 @@ var SECRET_RE = /^[0-9a-f]{64}$/;
|
|
|
16362
16537
|
function readShowSecret() {
|
|
16363
16538
|
try {
|
|
16364
16539
|
const file2 = showSecretPath();
|
|
16365
|
-
if (process.platform !== "win32" && (
|
|
16366
|
-
const secret =
|
|
16540
|
+
if (process.platform !== "win32" && (fs71.statSync(file2).mode & 63) !== 0) return void 0;
|
|
16541
|
+
const secret = fs71.readFileSync(file2, "utf8").trim();
|
|
16367
16542
|
return SECRET_RE.test(secret) ? secret : void 0;
|
|
16368
16543
|
} catch {
|
|
16369
16544
|
return void 0;
|
|
@@ -16373,17 +16548,17 @@ function ensureShowSecret() {
|
|
|
16373
16548
|
const existing = readShowSecret();
|
|
16374
16549
|
if (existing) return existing;
|
|
16375
16550
|
const file2 = showSecretPath();
|
|
16376
|
-
|
|
16551
|
+
fs71.mkdirSync(path60.dirname(file2), { recursive: true });
|
|
16377
16552
|
const secret = randomBytes5(32).toString("hex");
|
|
16378
16553
|
try {
|
|
16379
|
-
|
|
16554
|
+
fs71.writeFileSync(file2, secret, { mode: 384, flag: "wx" });
|
|
16380
16555
|
} catch (e) {
|
|
16381
16556
|
if (e.code !== "EEXIST") throw e;
|
|
16382
16557
|
const raced = readShowSecret();
|
|
16383
16558
|
if (raced) return raced;
|
|
16384
|
-
|
|
16559
|
+
fs71.writeFileSync(file2, secret, { mode: 384 });
|
|
16385
16560
|
}
|
|
16386
|
-
|
|
16561
|
+
fs71.chmodSync(file2, 384);
|
|
16387
16562
|
return secret;
|
|
16388
16563
|
}
|
|
16389
16564
|
function showSecretMatches(supplied) {
|
|
@@ -16427,7 +16602,7 @@ function requestShow(port, hash, timeoutMs = 2e3) {
|
|
|
16427
16602
|
|
|
16428
16603
|
// apps/cli/src/focus.ts
|
|
16429
16604
|
init_dist2();
|
|
16430
|
-
import * as
|
|
16605
|
+
import * as fs72 from "node:fs";
|
|
16431
16606
|
import * as path61 from "node:path";
|
|
16432
16607
|
import { execFile as execFile2 } from "node:child_process";
|
|
16433
16608
|
import { promisify as promisify2 } from "node:util";
|
|
@@ -16480,7 +16655,7 @@ function safeWorktree(reg) {
|
|
|
16480
16655
|
const folder = reg.worktree;
|
|
16481
16656
|
if (!path61.isAbsolute(folder) || /[\x00-\x1f\x7f]/.test(folder) || path61.normalize(folder) !== folder) return false;
|
|
16482
16657
|
try {
|
|
16483
|
-
return
|
|
16658
|
+
return fs72.statSync(folder).isDirectory() && trapIdAt(folder) === reg.trapId;
|
|
16484
16659
|
} catch {
|
|
16485
16660
|
return false;
|
|
16486
16661
|
}
|
|
@@ -16565,25 +16740,26 @@ function requestBodyCap() {
|
|
|
16565
16740
|
}
|
|
16566
16741
|
var REQUEST_KIND_MAX_BYTES = /* @__PURE__ */ new Map([
|
|
16567
16742
|
["trap-request", 4096],
|
|
16568
|
-
["decision-viewed", 1024]
|
|
16743
|
+
["decision-viewed", 1024],
|
|
16744
|
+
["report-viewed", 1024]
|
|
16569
16745
|
]);
|
|
16570
16746
|
var readJson4 = (f) => {
|
|
16571
16747
|
try {
|
|
16572
|
-
return JSON.parse(
|
|
16748
|
+
return JSON.parse(fs73.readFileSync(f, "utf8"));
|
|
16573
16749
|
} catch {
|
|
16574
16750
|
return void 0;
|
|
16575
16751
|
}
|
|
16576
16752
|
};
|
|
16577
16753
|
var listDir = (d) => {
|
|
16578
16754
|
try {
|
|
16579
|
-
return
|
|
16755
|
+
return fs73.readdirSync(d);
|
|
16580
16756
|
} catch {
|
|
16581
16757
|
return [];
|
|
16582
16758
|
}
|
|
16583
16759
|
};
|
|
16584
16760
|
var mtime = (f) => {
|
|
16585
16761
|
try {
|
|
16586
|
-
return
|
|
16762
|
+
return fs73.statSync(f).mtimeMs;
|
|
16587
16763
|
} catch {
|
|
16588
16764
|
return 0;
|
|
16589
16765
|
}
|
|
@@ -16592,7 +16768,7 @@ function assetPath(name) {
|
|
|
16592
16768
|
for (const rel of [`../docs/assets/${name}`, `../../../docs/assets/${name}`]) {
|
|
16593
16769
|
try {
|
|
16594
16770
|
const p = fileURLToPath3(new URL(rel, import.meta.url));
|
|
16595
|
-
if (
|
|
16771
|
+
if (fs73.existsSync(p)) return p;
|
|
16596
16772
|
} catch {
|
|
16597
16773
|
}
|
|
16598
16774
|
}
|
|
@@ -16601,7 +16777,7 @@ function assetPath(name) {
|
|
|
16601
16777
|
function transcriptPath(harness, cwd, sessionId) {
|
|
16602
16778
|
if (harness !== "claude" || !cwd || !sessionId) return void 0;
|
|
16603
16779
|
const p = path62.join(os11.homedir(), ".claude", "projects", cwd.replace(/[^A-Za-z0-9-]/g, "-"), `${sessionId}.jsonl`);
|
|
16604
|
-
return
|
|
16780
|
+
return fs73.existsSync(p) ? p : void 0;
|
|
16605
16781
|
}
|
|
16606
16782
|
function messageAttachments(dir) {
|
|
16607
16783
|
return [dir, path62.join(dir, "handled")].flatMap(
|
|
@@ -16613,7 +16789,7 @@ function trapMessages(trapId) {
|
|
|
16613
16789
|
const parse2 = (file2, state) => {
|
|
16614
16790
|
let raw;
|
|
16615
16791
|
try {
|
|
16616
|
-
raw =
|
|
16792
|
+
raw = fs73.readFileSync(file2, "utf8");
|
|
16617
16793
|
} catch {
|
|
16618
16794
|
return void 0;
|
|
16619
16795
|
}
|
|
@@ -16703,7 +16879,7 @@ function dispatchRow(r, hold, staleSecs) {
|
|
|
16703
16879
|
claimedBy: claim?.by,
|
|
16704
16880
|
worker: evidence.worker,
|
|
16705
16881
|
log,
|
|
16706
|
-
inbox: listDir(inboxDir2).filter((f) => f.endsWith(".msg")).sort().map((f) =>
|
|
16882
|
+
inbox: listDir(inboxDir2).filter((f) => f.endsWith(".msg")).sort().map((f) => fs73.readFileSync(path62.join(inboxDir2, f), "utf8").trim()),
|
|
16707
16883
|
...awaitingOf(id),
|
|
16708
16884
|
evidence: Object.keys(evidence).length > 0 ? evidence : void 0,
|
|
16709
16885
|
...r.bucket === "queued" ? {} : worktreeView(id, r.lane),
|
|
@@ -16796,7 +16972,7 @@ function serveReport(url, res) {
|
|
|
16796
16972
|
const type = file2 && REPORT_IMAGE_TYPES[path62.extname(file2).toLowerCase()];
|
|
16797
16973
|
if (!file2 || !type) return notFound(), true;
|
|
16798
16974
|
res.writeHead(200, { ...headers, "content-type": type });
|
|
16799
|
-
res.end(
|
|
16975
|
+
res.end(fs73.readFileSync(file2));
|
|
16800
16976
|
return true;
|
|
16801
16977
|
}
|
|
16802
16978
|
function trapNamesShown(traps, dispatches, attention, names) {
|
|
@@ -16839,7 +17015,7 @@ function serveAttachment(url, res) {
|
|
|
16839
17015
|
const type = file2 && REPORT_IMAGE_TYPES[path62.extname(file2).toLowerCase()];
|
|
16840
17016
|
let data;
|
|
16841
17017
|
try {
|
|
16842
|
-
if (file2 && type &&
|
|
17018
|
+
if (file2 && type && fs73.lstatSync(file2, { throwIfNoEntry: false })?.isFile()) data = fs73.readFileSync(file2);
|
|
16843
17019
|
} catch {
|
|
16844
17020
|
}
|
|
16845
17021
|
if (!data || !type) {
|
|
@@ -17110,6 +17286,16 @@ function serveGlass(port, options = {}) {
|
|
|
17110
17286
|
return reply(500, { ok: false, reason: "The view could not be stored." });
|
|
17111
17287
|
}
|
|
17112
17288
|
}
|
|
17289
|
+
case "report-viewed": {
|
|
17290
|
+
const key = body.payload?.key;
|
|
17291
|
+
if (typeof key !== "string" || !key.startsWith("report:") || key.length > 200) return reply(400, { ok: false, reason: "A report key is required." });
|
|
17292
|
+
try {
|
|
17293
|
+
const viewed = viewReport(key);
|
|
17294
|
+
return viewed ? reply(200, { ok: true, ...viewed }) : reply(404, { ok: false, reason: `No report ${key}.` });
|
|
17295
|
+
} catch {
|
|
17296
|
+
return reply(500, { ok: false, reason: "The view could not be stored." });
|
|
17297
|
+
}
|
|
17298
|
+
}
|
|
17113
17299
|
case "trap-request": {
|
|
17114
17300
|
const error = trapRequestError(body.payload, Object.keys(loadConfig().repos));
|
|
17115
17301
|
if (error) return reply(400, { ok: false, reason: `Invalid trap request: ${error}.` });
|
|
@@ -17206,17 +17392,17 @@ function serveGlass(port, options = {}) {
|
|
|
17206
17392
|
}
|
|
17207
17393
|
if (req.url === "/lob.png" && lob) {
|
|
17208
17394
|
res.writeHead(200, { "content-type": "image/png", "cache-control": "no-cache" });
|
|
17209
|
-
res.end(
|
|
17395
|
+
res.end(fs73.readFileSync(lob));
|
|
17210
17396
|
} else if (req.url === "/star.png" && star) {
|
|
17211
17397
|
res.writeHead(200, { "content-type": "image/png", "cache-control": "no-cache" });
|
|
17212
|
-
res.end(
|
|
17398
|
+
res.end(fs73.readFileSync(star));
|
|
17213
17399
|
} else if (req.url === "/lob-sprite.png" && sprite) {
|
|
17214
17400
|
res.writeHead(200, { "content-type": "image/png", "cache-control": "no-cache" });
|
|
17215
|
-
res.end(
|
|
17401
|
+
res.end(fs73.readFileSync(sprite));
|
|
17216
17402
|
} else if (req.url === "/icon.png") {
|
|
17217
17403
|
if (icon) {
|
|
17218
17404
|
res.writeHead(200, { "content-type": "image/png", "cache-control": "no-cache" });
|
|
17219
|
-
res.end(
|
|
17405
|
+
res.end(fs73.readFileSync(icon));
|
|
17220
17406
|
} else {
|
|
17221
17407
|
res.writeHead(200, { "content-type": "image/svg+xml", "cache-control": "no-cache" });
|
|
17222
17408
|
res.end(fallbackIcon);
|
|
@@ -17236,7 +17422,12 @@ function serveGlass(port, options = {}) {
|
|
|
17236
17422
|
}
|
|
17237
17423
|
const row = key ? reportRow(key) : void 0;
|
|
17238
17424
|
if (m[2] === "/meta") {
|
|
17239
|
-
res.writeHead(row ? 200 : 404, {
|
|
17425
|
+
res.writeHead(row ? 200 : 404, {
|
|
17426
|
+
"content-type": "application/json",
|
|
17427
|
+
"cache-control": "no-store",
|
|
17428
|
+
// The page token, so the report page can record its view.
|
|
17429
|
+
...row ? { "x-lobstah-token": focusToken } : {}
|
|
17430
|
+
});
|
|
17240
17431
|
res.end(JSON.stringify(row ?? { error: "report not found" }));
|
|
17241
17432
|
return;
|
|
17242
17433
|
}
|
|
@@ -17340,12 +17531,12 @@ function serveGlass(port, options = {}) {
|
|
|
17340
17531
|
|
|
17341
17532
|
// apps/cli/src/park-renewal.ts
|
|
17342
17533
|
init_dist2();
|
|
17343
|
-
import * as
|
|
17534
|
+
import * as fs75 from "node:fs";
|
|
17344
17535
|
import * as path63 from "node:path";
|
|
17345
17536
|
|
|
17346
17537
|
// apps/cli/src/restart.ts
|
|
17347
17538
|
init_dist2();
|
|
17348
|
-
import * as
|
|
17539
|
+
import * as fs74 from "node:fs";
|
|
17349
17540
|
var RESTART_WAIT_MS = 1e4;
|
|
17350
17541
|
var HEARTBEAT_STALE_MS3 = 9e4;
|
|
17351
17542
|
var INSTALL_COMMAND = {
|
|
@@ -17369,7 +17560,7 @@ function restartRefusal(opts) {
|
|
|
17369
17560
|
}
|
|
17370
17561
|
function readHeartbeat() {
|
|
17371
17562
|
try {
|
|
17372
|
-
const hb = JSON.parse(
|
|
17563
|
+
const hb = JSON.parse(fs74.readFileSync(executorPath(), "utf8"));
|
|
17373
17564
|
return typeof hb.heartbeat === "string" ? hb : void 0;
|
|
17374
17565
|
} catch {
|
|
17375
17566
|
return void 0;
|
|
@@ -17430,7 +17621,7 @@ var RENEW_REASON = "lobstah: this trap is still signed on and its park timed out
|
|
|
17430
17621
|
var statePath = (trapId) => path63.join(lobstahHome(), "park-renewals", `${trapId}.json`);
|
|
17431
17622
|
function readState(trapId) {
|
|
17432
17623
|
try {
|
|
17433
|
-
const s = JSON.parse(
|
|
17624
|
+
const s = JSON.parse(fs75.readFileSync(statePath(trapId), "utf8"));
|
|
17434
17625
|
return { instant: Number.isFinite(s.instant) ? s.instant : 0, at: s.at };
|
|
17435
17626
|
} catch {
|
|
17436
17627
|
return { instant: 0, at: "" };
|
|
@@ -17438,11 +17629,11 @@ function readState(trapId) {
|
|
|
17438
17629
|
}
|
|
17439
17630
|
function writeState(trapId, state) {
|
|
17440
17631
|
const file2 = statePath(trapId);
|
|
17441
|
-
|
|
17442
|
-
|
|
17632
|
+
fs75.mkdirSync(path63.dirname(file2), { recursive: true });
|
|
17633
|
+
fs75.writeFileSync(file2, JSON.stringify(state));
|
|
17443
17634
|
}
|
|
17444
17635
|
function clearParkRenewal(trapId) {
|
|
17445
|
-
|
|
17636
|
+
fs75.rmSync(statePath(trapId), { force: true });
|
|
17446
17637
|
}
|
|
17447
17638
|
function daemonReachable(now2 = Date.now()) {
|
|
17448
17639
|
const at = Date.parse(readHeartbeat()?.heartbeat ?? "");
|
|
@@ -17480,7 +17671,7 @@ function parkRenewal(trapId, parkedMs, now2 = Date.now(), daemonUp = daemonReach
|
|
|
17480
17671
|
// apps/cli/src/repos.ts
|
|
17481
17672
|
init_dist();
|
|
17482
17673
|
init_dist2();
|
|
17483
|
-
import * as
|
|
17674
|
+
import * as fs76 from "node:fs";
|
|
17484
17675
|
import * as path64 from "node:path";
|
|
17485
17676
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
17486
17677
|
function git9(dir, ...args) {
|
|
@@ -17501,27 +17692,27 @@ function detectRepo(dir) {
|
|
|
17501
17692
|
if (git9(abs, "rev-parse", "--is-inside-work-tree") !== "true") return void 0;
|
|
17502
17693
|
const top = git9(abs, "rev-parse", "--show-toplevel");
|
|
17503
17694
|
const canon2 = (p) => {
|
|
17504
|
-
const real = path64.resolve(
|
|
17695
|
+
const real = path64.resolve(fs76.realpathSync.native(p));
|
|
17505
17696
|
return process.platform === "win32" ? real.toLowerCase() : real;
|
|
17506
17697
|
};
|
|
17507
17698
|
if (!top || canon2(top) !== canon2(abs)) return void 0;
|
|
17508
17699
|
const origin = git9(abs, "remote", "get-url", "origin");
|
|
17509
17700
|
const headRef = git9(abs, "symbolic-ref", "--short", "refs/remotes/origin/HEAD");
|
|
17510
17701
|
const trunk = headRef?.replace(/^origin\//, "") ?? git9(abs, "branch", "--show-current") ?? "main";
|
|
17511
|
-
const setup = LOCKFILE_SETUP.find(([f]) =>
|
|
17702
|
+
const setup = LOCKFILE_SETUP.find(([f]) => fs76.existsSync(path64.join(abs, f)))?.[1];
|
|
17512
17703
|
return { key: path64.basename(abs), path: abs, trunk, origin, setup };
|
|
17513
17704
|
}
|
|
17514
17705
|
function scanForRepos(roots) {
|
|
17515
17706
|
const found = [];
|
|
17516
17707
|
for (const root of roots) {
|
|
17517
17708
|
const abs = path64.resolve(root);
|
|
17518
|
-
if (!
|
|
17709
|
+
if (!fs76.existsSync(abs)) continue;
|
|
17519
17710
|
const self = detectRepo(abs);
|
|
17520
17711
|
if (self) {
|
|
17521
17712
|
found.push(self);
|
|
17522
17713
|
continue;
|
|
17523
17714
|
}
|
|
17524
|
-
for (const entry of
|
|
17715
|
+
for (const entry of fs76.readdirSync(abs, { withFileTypes: true })) {
|
|
17525
17716
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
17526
17717
|
const child = detectRepo(path64.join(abs, entry.name));
|
|
17527
17718
|
if (child) found.push(child);
|
|
@@ -17530,8 +17721,8 @@ function scanForRepos(roots) {
|
|
|
17530
17721
|
return found;
|
|
17531
17722
|
}
|
|
17532
17723
|
function configuredRepoKeys(file2 = configPath()) {
|
|
17533
|
-
if (!
|
|
17534
|
-
const raw = parse(
|
|
17724
|
+
if (!fs76.existsSync(file2)) return /* @__PURE__ */ new Set();
|
|
17725
|
+
const raw = parse(fs76.readFileSync(file2, "utf8"));
|
|
17535
17726
|
return new Set(Object.keys(raw.repos ?? {}));
|
|
17536
17727
|
}
|
|
17537
17728
|
function appendRepoBlock(repo, opts = {}, file2 = configPath()) {
|
|
@@ -17539,19 +17730,19 @@ function appendRepoBlock(repo, opts = {}, file2 = configPath()) {
|
|
|
17539
17730
|
if (repo.origin) lines.push(`origin = ${JSON.stringify(repo.origin)}`);
|
|
17540
17731
|
if (repo.setup) lines.push(`setup = [${repo.setup.map((s) => JSON.stringify(s)).join(", ")}]`);
|
|
17541
17732
|
if (opts.pickup) lines.push(`pickup = true`);
|
|
17542
|
-
|
|
17733
|
+
fs76.appendFileSync(file2, `${lines.join("\n")}
|
|
17543
17734
|
`);
|
|
17544
17735
|
}
|
|
17545
17736
|
|
|
17546
17737
|
// apps/cli/src/pr-watch.ts
|
|
17547
17738
|
init_dist();
|
|
17548
17739
|
init_dist2();
|
|
17549
|
-
import * as
|
|
17740
|
+
import * as fs78 from "node:fs";
|
|
17550
17741
|
|
|
17551
17742
|
// apps/cli/src/pr-stack-watch.ts
|
|
17552
17743
|
init_dist2();
|
|
17553
17744
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
17554
|
-
import * as
|
|
17745
|
+
import * as fs77 from "node:fs";
|
|
17555
17746
|
import * as path65 from "node:path";
|
|
17556
17747
|
var ghStackLink = (ref, branch, direction) => {
|
|
17557
17748
|
const res = spawnSync11(
|
|
@@ -17581,7 +17772,7 @@ function discoverPrStack(ref, opts = {}) {
|
|
|
17581
17772
|
const file2 = path65.join(lobstahHome(), "pr-stack-links.json");
|
|
17582
17773
|
let checked;
|
|
17583
17774
|
try {
|
|
17584
|
-
checked = JSON.parse(
|
|
17775
|
+
checked = JSON.parse(fs77.readFileSync(file2, "utf8"));
|
|
17585
17776
|
} catch {
|
|
17586
17777
|
checked = {};
|
|
17587
17778
|
}
|
|
@@ -17618,15 +17809,15 @@ function discoverPrStack(ref, opts = {}) {
|
|
|
17618
17809
|
}
|
|
17619
17810
|
}
|
|
17620
17811
|
const tmp = `${file2}.tmp-${process.pid}`;
|
|
17621
|
-
|
|
17622
|
-
|
|
17812
|
+
fs77.writeFileSync(tmp, JSON.stringify(checked));
|
|
17813
|
+
fs77.renameSync(tmp, file2);
|
|
17623
17814
|
}
|
|
17624
17815
|
|
|
17625
17816
|
// apps/cli/src/pr-watch.ts
|
|
17626
17817
|
var shq = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
|
|
17627
17818
|
function selfCommand() {
|
|
17628
17819
|
if (COMPILED_BINARY) return shq(process.execPath);
|
|
17629
|
-
const entry = process.argv[1] ?
|
|
17820
|
+
const entry = process.argv[1] ? fs78.realpathSync(process.argv[1]) : "lobstah";
|
|
17630
17821
|
return `${shq(process.execPath)} ${shq(entry)}`;
|
|
17631
17822
|
}
|
|
17632
17823
|
function prCheckCommand(ref, forId) {
|
|
@@ -17676,7 +17867,7 @@ function backfillPrWatches(opts = {}) {
|
|
|
17676
17867
|
for (const bucket of ["active", "done"]) {
|
|
17677
17868
|
let ids;
|
|
17678
17869
|
try {
|
|
17679
|
-
ids =
|
|
17870
|
+
ids = fs78.readdirSync(dirs[bucket]).filter((id) => !id.startsWith("."));
|
|
17680
17871
|
} catch {
|
|
17681
17872
|
continue;
|
|
17682
17873
|
}
|
|
@@ -17688,7 +17879,7 @@ function backfillPrWatches(opts = {}) {
|
|
|
17688
17879
|
}
|
|
17689
17880
|
let files;
|
|
17690
17881
|
try {
|
|
17691
|
-
files =
|
|
17882
|
+
files = fs78.readdirSync(dirs.state).filter((f) => f.endsWith(".evidence"));
|
|
17692
17883
|
} catch {
|
|
17693
17884
|
continue;
|
|
17694
17885
|
}
|
|
@@ -17750,7 +17941,7 @@ function backfillPrWatches(opts = {}) {
|
|
|
17750
17941
|
return rows;
|
|
17751
17942
|
}
|
|
17752
17943
|
function laneOf4(id) {
|
|
17753
|
-
return ["work", "chore"].find((l) =>
|
|
17944
|
+
return ["work", "chore"].find((l) => fs78.existsSync(evidencePath(id, l)) || readStatusLog(id, l).length > 0);
|
|
17754
17945
|
}
|
|
17755
17946
|
var FIRST_SIGHT_NOTICE_MS = 24 * 60 * 60 * 1e3;
|
|
17756
17947
|
function observePr(ref, view, opts = {}) {
|
|
@@ -17794,7 +17985,7 @@ function cancelQueuedRepairs(key) {
|
|
|
17794
17985
|
}
|
|
17795
17986
|
function rawConfig() {
|
|
17796
17987
|
try {
|
|
17797
|
-
return parse(
|
|
17988
|
+
return parse(fs78.readFileSync(configPath(), "utf8"));
|
|
17798
17989
|
} catch {
|
|
17799
17990
|
return {};
|
|
17800
17991
|
}
|
|
@@ -17969,7 +18160,7 @@ function finishResolvedWaits(log = () => {
|
|
|
17969
18160
|
// apps/cli/src/pr-stack.ts
|
|
17970
18161
|
init_dist2();
|
|
17971
18162
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
17972
|
-
import * as
|
|
18163
|
+
import * as fs79 from "node:fs";
|
|
17973
18164
|
import * as path66 from "node:path";
|
|
17974
18165
|
var defaultRun = (cmd, args, cwd) => {
|
|
17975
18166
|
const res = spawnSync12(cmd, args, { cwd, encoding: "utf8", timeout: cmd === "gh" ? 1e4 : 3e3, windowsHide: true });
|
|
@@ -17982,7 +18173,7 @@ function ghStackPrs(cwd, keys, run4) {
|
|
|
17982
18173
|
if (res.status !== 0 || !res.stdout.trim()) continue;
|
|
17983
18174
|
let state;
|
|
17984
18175
|
try {
|
|
17985
|
-
state = JSON.parse(
|
|
18176
|
+
state = JSON.parse(fs79.readFileSync(path66.join(path66.resolve(cwd, res.stdout.trim()), "gh-stack"), "utf8"));
|
|
17986
18177
|
} catch {
|
|
17987
18178
|
continue;
|
|
17988
18179
|
}
|
|
@@ -18343,7 +18534,7 @@ function batchText(results) {
|
|
|
18343
18534
|
// apps/cli/src/soak-worktree.ts
|
|
18344
18535
|
init_dist2();
|
|
18345
18536
|
init_dist3();
|
|
18346
|
-
import * as
|
|
18537
|
+
import * as fs80 from "node:fs";
|
|
18347
18538
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
18348
18539
|
function branchExists(repo, branch) {
|
|
18349
18540
|
return spawnSync13("git", ["-C", repo.path, "rev-parse", "--verify", "-q", `refs/heads/${branch}`], { stdio: "ignore" }).status === 0;
|
|
@@ -18363,7 +18554,7 @@ async function createSoakWorktree(opts) {
|
|
|
18363
18554
|
);
|
|
18364
18555
|
}
|
|
18365
18556
|
}
|
|
18366
|
-
const taken = (id2) => branchExists(opts.repo, `lobstah/soak-${id2}`) ||
|
|
18557
|
+
const taken = (id2) => branchExists(opts.repo, `lobstah/soak-${id2}`) || fs80.existsSync(worktreePath(`soak-${id2}`));
|
|
18367
18558
|
let trapId = opts.trapId ?? newTrapId();
|
|
18368
18559
|
if (opts.trapId !== void 0 && taken(trapId)) {
|
|
18369
18560
|
throw new Error(`could not create a worktree for repo ${opts.repoKey}: soak-${trapId} or its branch already exists`);
|
|
@@ -18388,7 +18579,7 @@ async function discardSoakWorktree(repo, made) {
|
|
|
18388
18579
|
// apps/cli/src/forget.ts
|
|
18389
18580
|
init_dist2();
|
|
18390
18581
|
init_dist3();
|
|
18391
|
-
import * as
|
|
18582
|
+
import * as fs81 from "node:fs";
|
|
18392
18583
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
18393
18584
|
var ForgetRefusedError = class extends Error {
|
|
18394
18585
|
};
|
|
@@ -18409,7 +18600,7 @@ async function forgetTrap(address, cfg, opts = {}) {
|
|
|
18409
18600
|
if (readReservation(entry.trapId)) throw new ForgetRefusedError(`${label} is starting (reserved) \u2014 withdraw it with \`lobstah stow --wt ${entry.name}\` first`);
|
|
18410
18601
|
const repo = entry.repo ? cfg.repos[entry.repo] : void 0;
|
|
18411
18602
|
const gitDir = entry.gitDir ?? (repo ? gitCommonDir(repo.path) : void 0);
|
|
18412
|
-
const present =
|
|
18603
|
+
const present = fs81.existsSync(entry.worktree);
|
|
18413
18604
|
const anchored = present && readTrapAnchor(entry.worktree)?.trapId === entry.trapId;
|
|
18414
18605
|
const unsaved = anchored ? unsavedFiles(entry.worktree) : [];
|
|
18415
18606
|
if (unsaved === void 0 && !opts.force) throw new ForgetRefusedError(`cannot read git status in ${entry.worktree} \u2014 inspect it, or pass --force`);
|
|
@@ -18456,7 +18647,7 @@ Push or merge it, or pass --force to forget the trap anyway.`);
|
|
|
18456
18647
|
throw new Error(`could not remove ${entry.worktree}: ${removal.reason} \u2014 the trap is not forgotten`);
|
|
18457
18648
|
}
|
|
18458
18649
|
} else {
|
|
18459
|
-
if (anchored)
|
|
18650
|
+
if (anchored) fs81.rmSync(`${entry.worktree}/${TRAP_ANCHOR_FILE}`, { force: true });
|
|
18460
18651
|
result.worktreeNote = "soak did not create it: left in place, its trap anchor removed";
|
|
18461
18652
|
}
|
|
18462
18653
|
}
|
|
@@ -18532,7 +18723,7 @@ async function setTerminalTitle(win, title, options = {}) {
|
|
|
18532
18723
|
|
|
18533
18724
|
// apps/cli/src/beat.ts
|
|
18534
18725
|
init_dist2();
|
|
18535
|
-
import * as
|
|
18726
|
+
import * as fs82 from "node:fs";
|
|
18536
18727
|
import * as path67 from "node:path";
|
|
18537
18728
|
|
|
18538
18729
|
// apps/cli/src/beat-pr.ts
|
|
@@ -18584,9 +18775,9 @@ function probeTrapPr(reg, opts = {}) {
|
|
|
18584
18775
|
function logError(err) {
|
|
18585
18776
|
try {
|
|
18586
18777
|
const dir = path67.join(lobstahHome(), "logs");
|
|
18587
|
-
|
|
18778
|
+
fs82.mkdirSync(dir, { recursive: true });
|
|
18588
18779
|
const msg = err instanceof Error ? err.message : String(err);
|
|
18589
|
-
|
|
18780
|
+
fs82.appendFileSync(path67.join(dir, "beat.log"), `${(/* @__PURE__ */ new Date()).toISOString()} ${msg.replace(/\s+/g, " ").slice(0, 300)}
|
|
18590
18781
|
`);
|
|
18591
18782
|
} catch {
|
|
18592
18783
|
}
|
|
@@ -19414,7 +19605,7 @@ function emitNotices(notices, sessionId) {
|
|
|
19414
19605
|
for (const n of notices) {
|
|
19415
19606
|
const answer = n.kind === "decision-answer" && n.refId ? readRequest(n.refId) : void 0;
|
|
19416
19607
|
if (answer) console.log(toonKV(decisionEventFields(answer)));
|
|
19417
|
-
else console.log(toonKV(trapRequestFields(n) ?? { notice: n.kind, ...n.refId ? { ref: n.refId } : {}, text: n.text }));
|
|
19608
|
+
else console.log(toonKV(trapRequestFields(n) ?? { notice: n.kind, ...n.refId ? { ref: n.refId } : {}, ...n.url ? { url: n.url } : {}, text: n.text }));
|
|
19418
19609
|
}
|
|
19419
19610
|
console.log(
|
|
19420
19611
|
`next: each notice names its own decision or remedy \u2014 act on it (or note it and move on), then re-arm a background \`lobstah man wait${sessionId ? ` --session ${sessionId}` : ""}\`. \`lobstah man tend\` keeps the recent tail visible either way.`
|
|
@@ -19432,7 +19623,7 @@ function noticeLine(n, prefix = "notice ") {
|
|
|
19432
19623
|
const answer = n.kind === "decision-answer" && n.refId ? readRequest(n.refId) : void 0;
|
|
19433
19624
|
if (answer) return decisionLine(answer);
|
|
19434
19625
|
const r = trapRequestFields(n);
|
|
19435
|
-
return r ? `- trap-request ${r.id} \u2014 repo ${r.repo}, harness ${r.harness}: start it with \`lobstah man throw --new --request ${r.id}\`` : `- ${prefix}${n.kind}${n.refId ? ` ${n.refId}` : ""} \u2014 ${n.text}`;
|
|
19626
|
+
return r ? `- trap-request ${r.id} \u2014 repo ${r.repo}, harness ${r.harness}: start it with \`lobstah man throw --new --request ${r.id}\`` : `- ${prefix}${n.kind}${n.refId ? ` ${n.refId}` : ""} \u2014 ${n.text}${n.url ? ` ${n.url}` : ""}`;
|
|
19436
19627
|
}
|
|
19437
19628
|
function replyLine(r) {
|
|
19438
19629
|
return `- reply ${r.id} (${r.entry.verb})${r.entry.note ? ` \u2014 ${r.entry.note}` : ""} \xB7 sent: ${r.sent}`;
|
|
@@ -19454,7 +19645,7 @@ function emitWatchAttention(attns, sessionId) {
|
|
|
19454
19645
|
function findLane(id) {
|
|
19455
19646
|
for (const lane of ["work", "chore"]) {
|
|
19456
19647
|
const d = laneDirs(lane);
|
|
19457
|
-
if (
|
|
19648
|
+
if (fs87.existsSync(path72.join(d.active, id)) || fs87.existsSync(path72.join(d.queue, `${id}.json`)) || fs87.existsSync(path72.join(d.done, id)) || fs87.existsSync(path72.join(d.state, `${id}.status`))) {
|
|
19458
19649
|
return lane;
|
|
19459
19650
|
}
|
|
19460
19651
|
}
|
|
@@ -19463,7 +19654,7 @@ function findLane(id) {
|
|
|
19463
19654
|
function anythingInFlight() {
|
|
19464
19655
|
const dispatches = ["work", "chore"].some((lane) => {
|
|
19465
19656
|
const dirs = laneDirs(lane);
|
|
19466
|
-
return [dirs.queue, dirs.active].some((dir) =>
|
|
19657
|
+
return [dirs.queue, dirs.active].some((dir) => fs87.readdirSync(dir).some((file2) => !file2.startsWith(".")));
|
|
19467
19658
|
});
|
|
19468
19659
|
return dispatches || listWatches().some((watch2) => watch2.owner === "man");
|
|
19469
19660
|
}
|
|
@@ -19480,7 +19671,7 @@ function dispatchChain(target) {
|
|
|
19480
19671
|
for (const lane of ["work", "chore"]) {
|
|
19481
19672
|
const dirs = laneDirs(lane);
|
|
19482
19673
|
for (const bucket of ["queue", "active", "done"]) {
|
|
19483
|
-
for (const entry of
|
|
19674
|
+
for (const entry of fs87.readdirSync(dirs[bucket])) {
|
|
19484
19675
|
if (entry.startsWith(".")) continue;
|
|
19485
19676
|
const id = bucket === "queue" ? entry.replace(/\.json$/, "") : entry;
|
|
19486
19677
|
const descriptor = storedDescriptor(id, lane);
|
|
@@ -19510,7 +19701,7 @@ function liveChainMember(member) {
|
|
|
19510
19701
|
}
|
|
19511
19702
|
if (isParked(member.id, member.lane)) return true;
|
|
19512
19703
|
try {
|
|
19513
|
-
const runner = JSON.parse(
|
|
19704
|
+
const runner = JSON.parse(fs87.readFileSync(path72.join(dir, "runner.json"), "utf8"));
|
|
19514
19705
|
return pidAlive2(runner.pid, runner.processStartTime);
|
|
19515
19706
|
} catch {
|
|
19516
19707
|
return false;
|
|
@@ -19519,7 +19710,7 @@ function liveChainMember(member) {
|
|
|
19519
19710
|
function rowsFor(lane, bucket) {
|
|
19520
19711
|
const dir = laneDirs(lane)[bucket];
|
|
19521
19712
|
const staleSecs = wedgeSecs2();
|
|
19522
|
-
const entries =
|
|
19713
|
+
const entries = fs87.readdirSync(dir).filter((f) => !f.startsWith(".")).map((f) => ({ f, m: fs87.statSync(path72.join(dir, f)).mtimeMs })).sort((a, b) => b.m - a.m);
|
|
19523
19714
|
const sliced = bucket === "done" ? entries.slice(0, 10) : entries;
|
|
19524
19715
|
return sliced.map(({ f, m }) => {
|
|
19525
19716
|
const id = f.replace(/\.json$/, "");
|
|
@@ -19544,7 +19735,7 @@ function rowsFor(lane, bucket) {
|
|
|
19544
19735
|
function dispatchTrap(id, lane, names) {
|
|
19545
19736
|
const claimedBy = (bucket) => {
|
|
19546
19737
|
try {
|
|
19547
|
-
return JSON.parse(
|
|
19738
|
+
return JSON.parse(fs87.readFileSync(path72.join(laneDirs(lane)[bucket], id, "claim.json"), "utf8")).by;
|
|
19548
19739
|
} catch {
|
|
19549
19740
|
return void 0;
|
|
19550
19741
|
}
|
|
@@ -19696,7 +19887,7 @@ ${usageFor(cmd)}`);
|
|
|
19696
19887
|
const d = {
|
|
19697
19888
|
id: opt("--id") ?? randomUUID8(),
|
|
19698
19889
|
repo,
|
|
19699
|
-
brief: briefText ??
|
|
19890
|
+
brief: briefText ?? fs87.readFileSync(briefFile, "utf8"),
|
|
19700
19891
|
harness: opt("--harness"),
|
|
19701
19892
|
model: opt("--model"),
|
|
19702
19893
|
effort: opt("--effort"),
|
|
@@ -19753,7 +19944,7 @@ ${usageFor(cmd)}`);
|
|
|
19753
19944
|
const since = queuedAt(id, lane);
|
|
19754
19945
|
const claimedAt = claim?.at;
|
|
19755
19946
|
const state = displayState({ log, lastEventAt: lastEventAt(id, lane), queued: since !== void 0, claimedAt });
|
|
19756
|
-
const live =
|
|
19947
|
+
const live = fs87.existsSync(path72.join(laneDirs(lane).active, id));
|
|
19757
19948
|
const activity = live ? activityView(readActivity(id, lane), wedgeSecs2()) : void 0;
|
|
19758
19949
|
const waitingNow = live ? waitingView(log.at(-1)) : void 0;
|
|
19759
19950
|
console.log(
|
|
@@ -19793,8 +19984,8 @@ ${usageFor(cmd)}`);
|
|
|
19793
19984
|
if (!id) throw new Error("logs requires a dispatch id");
|
|
19794
19985
|
const lane = findLane(id);
|
|
19795
19986
|
const file2 = eventsPath(id, lane);
|
|
19796
|
-
if (
|
|
19797
|
-
const raw =
|
|
19987
|
+
if (fs87.existsSync(file2)) {
|
|
19988
|
+
const raw = fs87.readFileSync(file2, "utf8");
|
|
19798
19989
|
const lines = raw.split("\n").filter((l) => l.length > 0);
|
|
19799
19990
|
const LIMIT = 50;
|
|
19800
19991
|
if (!has("--full") && lines.length > LIMIT) {
|
|
@@ -19805,15 +19996,15 @@ ${usageFor(cmd)}`);
|
|
|
19805
19996
|
}
|
|
19806
19997
|
}
|
|
19807
19998
|
if (has("--follow")) {
|
|
19808
|
-
let size =
|
|
19999
|
+
let size = fs87.existsSync(file2) ? fs87.statSync(file2).size : 0;
|
|
19809
20000
|
setInterval(() => {
|
|
19810
|
-
if (!
|
|
19811
|
-
const now2 =
|
|
20001
|
+
if (!fs87.existsSync(file2)) return;
|
|
20002
|
+
const now2 = fs87.statSync(file2).size;
|
|
19812
20003
|
if (now2 > size) {
|
|
19813
|
-
const fd =
|
|
20004
|
+
const fd = fs87.openSync(file2, "r");
|
|
19814
20005
|
const buf = Buffer.alloc(now2 - size);
|
|
19815
|
-
|
|
19816
|
-
|
|
20006
|
+
fs87.readSync(fd, buf, 0, buf.length, size);
|
|
20007
|
+
fs87.closeSync(fd);
|
|
19817
20008
|
process.stdout.write(buf.toString("utf8"));
|
|
19818
20009
|
size = now2;
|
|
19819
20010
|
}
|
|
@@ -19911,7 +20102,7 @@ ${[text, block].filter(Boolean).join("\n\n")}`,
|
|
|
19911
20102
|
const claimFile = path72.join(laneDirs(origin.lane)[origin.bucket], origin.id, "claim.json");
|
|
19912
20103
|
let claimedBy;
|
|
19913
20104
|
try {
|
|
19914
|
-
claimedBy = JSON.parse(
|
|
20105
|
+
claimedBy = JSON.parse(fs87.readFileSync(claimFile, "utf8")).by;
|
|
19915
20106
|
} catch {
|
|
19916
20107
|
claimedBy = readEvidence(origin.id, origin.lane).deliveredTo;
|
|
19917
20108
|
}
|
|
@@ -19995,7 +20186,7 @@ ${usageFor("report")}`);
|
|
|
19995
20186
|
if (verb === "done") {
|
|
19996
20187
|
const claim = readSessionClaim(id, lane);
|
|
19997
20188
|
let ownCheckout = false;
|
|
19998
|
-
if (claim?.by.startsWith("wt:") && claim.worktree &&
|
|
20189
|
+
if (claim?.by.startsWith("wt:") && claim.worktree && fs87.existsSync(claim.worktree)) {
|
|
19999
20190
|
const rel = path72.relative(canon(claim.worktree), canon(process.cwd()));
|
|
20000
20191
|
const inside = rel === "" || !rel.startsWith("..") && !path72.isAbsolute(rel);
|
|
20001
20192
|
const who = callerSession(opt("--session"))?.id;
|
|
@@ -20017,7 +20208,7 @@ ${usageFor("report")}`);
|
|
|
20017
20208
|
const branches = [.../* @__PURE__ */ new Set([ev.branch, ...(ev.pushes ?? []).map((p) => p.branch)])].filter(
|
|
20018
20209
|
(b) => !!b && b !== "HEAD"
|
|
20019
20210
|
);
|
|
20020
|
-
reportPrs = stackPrUrls(reportedPrs, { cwd: worktree &&
|
|
20211
|
+
reportPrs = stackPrUrls(reportedPrs, { cwd: worktree && fs87.existsSync(worktree) ? worktree : process.cwd(), branches });
|
|
20021
20212
|
const all = dispatchPrUrls({ prUrl, prUrls: [...reportPrs, ...dispatchPrUrls(ev)] });
|
|
20022
20213
|
mergeEvidence(id, lane, { prUrl, ...all.length > 1 ? { prUrls: all } : {} });
|
|
20023
20214
|
}
|
|
@@ -20083,7 +20274,7 @@ ${usageFor("report")}`);
|
|
|
20083
20274
|
);
|
|
20084
20275
|
}
|
|
20085
20276
|
const worktree = dispatchWorktree(id, lane).path;
|
|
20086
|
-
const cwd =
|
|
20277
|
+
const cwd = fs87.existsSync(worktree) ? worktree : process.cwd();
|
|
20087
20278
|
const invocation = harness === "codex" ? codexInvocation(["resume", sessionId]) : { file: "claude", argv: ["--resume", sessionId] };
|
|
20088
20279
|
if (!invocation) throw new Error("no codex CLI found \u2014 neither on PATH nor vendored by @openai/codex-sdk");
|
|
20089
20280
|
if (has("--print")) {
|
|
@@ -20102,9 +20293,9 @@ ${usageFor("report")}`);
|
|
|
20102
20293
|
}
|
|
20103
20294
|
const lane = findLane(id);
|
|
20104
20295
|
const activeDir = path72.join(laneDirs(lane).active, id);
|
|
20105
|
-
if (!
|
|
20296
|
+
if (!fs87.existsSync(activeDir)) throw new Error(`${id} is not active \u2014 swap only applies to in-flight dispatches`);
|
|
20106
20297
|
const descFile = path72.join(activeDir, "descriptor.json");
|
|
20107
|
-
const descriptor = JSON.parse(
|
|
20298
|
+
const descriptor = JSON.parse(fs87.readFileSync(descFile, "utf8"));
|
|
20108
20299
|
const fromHarness = resolveSessionHarness(id, loadConfig(), lane).harness ?? descriptor.harness ?? "default";
|
|
20109
20300
|
for (const key of ["harness", "model", "effort"]) {
|
|
20110
20301
|
const v = opt(`--${key}`);
|
|
@@ -20112,14 +20303,14 @@ ${usageFor("report")}`);
|
|
|
20112
20303
|
}
|
|
20113
20304
|
if (opt("--harness")) descriptor.harnessExplicit = true;
|
|
20114
20305
|
if (opt("--model")) descriptor.modelExplicit = true;
|
|
20115
|
-
|
|
20306
|
+
fs87.writeFileSync(descFile, JSON.stringify(descriptor, null, 2));
|
|
20116
20307
|
const worktree = dispatchWorktree(id, lane).path;
|
|
20117
|
-
|
|
20308
|
+
fs87.writeFileSync(path72.join(activeDir, "handoff"), handoffNote(fromHarness, worktreeProgress(worktree)));
|
|
20118
20309
|
const runnerFile = path72.join(activeDir, "runner.json");
|
|
20119
|
-
if (
|
|
20120
|
-
const runner = JSON.parse(
|
|
20310
|
+
if (fs87.existsSync(runnerFile)) {
|
|
20311
|
+
const runner = JSON.parse(fs87.readFileSync(runnerFile, "utf8"));
|
|
20121
20312
|
if (pidAlive2(runner.pid, runner.processStartTime)) killGroup(runner.pid, "SIGKILL");
|
|
20122
|
-
|
|
20313
|
+
fs87.unlinkSync(runnerFile);
|
|
20123
20314
|
}
|
|
20124
20315
|
appendStatus(id, lane, "working", `swapped to ${descriptor.harness ?? "default"} \u2014 awaiting respawn`);
|
|
20125
20316
|
console.log(toonKV({ id, harness: descriptor.harness ?? "default", model: descriptor.model, swapped: true }));
|
|
@@ -20180,7 +20371,7 @@ ${usageFor("report")}`);
|
|
|
20180
20371
|
console.log(`commits[${ev.commits.length}]:`);
|
|
20181
20372
|
for (const c of ev.commits) console.log(` ${c}`);
|
|
20182
20373
|
}
|
|
20183
|
-
if (
|
|
20374
|
+
if (fs87.existsSync(path72.join(laneDirs(lane).done, id))) {
|
|
20184
20375
|
console.log(toonHelp([`lobstah send ${id} "<instruction>" (wakes it as a follow-up in its worktree)`]));
|
|
20185
20376
|
}
|
|
20186
20377
|
const attachments = storedDescriptor(id, lane)?.attachments ?? [];
|
|
@@ -20720,7 +20911,7 @@ ${errs.map((e) => `- ${e}`).join("\n")}`);
|
|
|
20720
20911
|
const keys = holdCancelledRepair(id);
|
|
20721
20912
|
return keys.length > 0 ? { repairHeld: `${keys.join(", ")} (lobstah watch release <key> frees it)` } : {};
|
|
20722
20913
|
};
|
|
20723
|
-
if (
|
|
20914
|
+
if (fs87.existsSync(path72.join(laneDirs(lane).active, id))) {
|
|
20724
20915
|
requestCancel(id, lane);
|
|
20725
20916
|
console.log(toonKV({ id, cancel: "requested", note: "the claimant (daemon or trap) winds it down at its next check", ...repairHeld() }));
|
|
20726
20917
|
break;
|
|
@@ -20729,7 +20920,7 @@ ${errs.map((e) => `- ${e}`).join("\n")}`);
|
|
|
20729
20920
|
console.log(toonKV({ id, cancel: "finalized", note: "cancelled before claim \u2014 recorded as failed in done/", ...repairHeld() }));
|
|
20730
20921
|
break;
|
|
20731
20922
|
}
|
|
20732
|
-
if (
|
|
20923
|
+
if (fs87.existsSync(path72.join(laneDirs(lane).active, id))) {
|
|
20733
20924
|
requestCancel(id, lane);
|
|
20734
20925
|
console.log(toonKV({ id, cancel: "requested", note: "claimed while cancelling \u2014 the claimant winds it down", ...repairHeld() }));
|
|
20735
20926
|
break;
|
|
@@ -20856,17 +21047,17 @@ ${usageFor("man:wait")}`);
|
|
|
20856
21047
|
const file2 = has("--global") ? path72.join(os13.homedir(), ".claude", "settings.json") : path72.join(".claude", has("--shared") ? "settings.json" : "settings.local.json");
|
|
20857
21048
|
let existing;
|
|
20858
21049
|
try {
|
|
20859
|
-
existing = JSON.parse(
|
|
21050
|
+
existing = JSON.parse(fs87.readFileSync(file2, "utf8"));
|
|
20860
21051
|
} catch {
|
|
20861
21052
|
existing = void 0;
|
|
20862
21053
|
}
|
|
20863
21054
|
const { settings, changed } = mergeHaulHook(existing);
|
|
20864
21055
|
if (changed) {
|
|
20865
|
-
|
|
20866
|
-
|
|
21056
|
+
fs87.mkdirSync(path72.dirname(file2), { recursive: true });
|
|
21057
|
+
fs87.writeFileSync(file2, `${JSON.stringify(settings, null, 2)}
|
|
20867
21058
|
`);
|
|
20868
21059
|
}
|
|
20869
|
-
if (has("--marker"))
|
|
21060
|
+
if (has("--marker")) fs87.writeFileSync(".lobstah-man", "");
|
|
20870
21061
|
console.log(
|
|
20871
21062
|
toonKV({
|
|
20872
21063
|
hook: "lobstah man haul",
|
|
@@ -20924,7 +21115,7 @@ ${usageFor("man:wait")}`);
|
|
|
20924
21115
|
if (helm) releaseHeldQuestions(helm);
|
|
20925
21116
|
const cfgHaul = loadConfig();
|
|
20926
21117
|
if (!helm && liveHelms(cfgHaul.helm.ttlSecs * 1e3).length > 0) break;
|
|
20927
|
-
if (!helm && process.env.LOBSTAH_MAN !== "1" && !
|
|
21118
|
+
if (!helm && process.env.LOBSTAH_MAN !== "1" && !fs87.existsSync(".lobstah-man")) break;
|
|
20928
21119
|
const dueDigest = () => helm ? dueHelmDigest(helm, cfgHaul.helm.reportSecs) : void 0;
|
|
20929
21120
|
const blockDigest = (d) => {
|
|
20930
21121
|
advanceCursor(helm.grounds, d.now);
|
|
@@ -21134,7 +21325,7 @@ ${behind}` : "");
|
|
|
21134
21325
|
}
|
|
21135
21326
|
repoKey2 = reservation.repo;
|
|
21136
21327
|
if (reservation.worktree !== void 0) {
|
|
21137
|
-
if (!
|
|
21328
|
+
if (!fs87.existsSync(reservation.worktree) || trapIdAt(reservation.worktree) !== reservation.trapId) {
|
|
21138
21329
|
throw new Error(`the thrown trap ${trapLabel(reservation)} has no checkout anchoring it at ${reservation.worktree} \u2014 withdraw it with \`lobstah stow --wt ${reservation.name}\` and throw it again`);
|
|
21139
21330
|
}
|
|
21140
21331
|
worktree = canon(reservation.worktree);
|
|
@@ -21173,7 +21364,7 @@ ${behind}` : "");
|
|
|
21173
21364
|
`this session already mans trap wt:${mine.trapId} (repo ${mine.repo ?? "none"}) at ${mine.worktree}. Sign it off with \`lobstah stow\` before you soak for repo ${repoFlag}.`
|
|
21174
21365
|
);
|
|
21175
21366
|
}
|
|
21176
|
-
if (!
|
|
21367
|
+
if (!fs87.existsSync(mine.worktree)) {
|
|
21177
21368
|
throw new Error(
|
|
21178
21369
|
`this session's trap wt:${mine.trapId} is anchored at ${mine.worktree}, which no longer exists. Run \`lobstah stow\`, then soak again.`
|
|
21179
21370
|
);
|
|
@@ -21357,7 +21548,7 @@ ${usageFor("stow")}`);
|
|
|
21357
21548
|
const anchor = wtDir !== void 0 ? readTrapAnchor(wtDir) : void 0;
|
|
21358
21549
|
const created = reg?.createdWorktree === true || anchor?.createdBy === "soak";
|
|
21359
21550
|
let worktreeOut = {};
|
|
21360
|
-
if (wtDir !== void 0 &&
|
|
21551
|
+
if (wtDir !== void 0 && fs87.existsSync(wtDir)) {
|
|
21361
21552
|
const keepReason = !has("--remove") ? "stow keeps the worktree; --remove removes it" : sessionEnd ? "the session ended; the SessionEnd hook keeps the worktree" : !created ? "soak did not create this worktree; stow leaves it in place" : void 0;
|
|
21362
21553
|
if (keepReason) {
|
|
21363
21554
|
worktreeOut = { worktree: "kept", path: wtDir, reason: keepReason };
|
|
@@ -21604,7 +21795,7 @@ ${usageFor("pet")}`);
|
|
|
21604
21795
|
path: r.path,
|
|
21605
21796
|
trunk: r.trunk,
|
|
21606
21797
|
pickup: r.pickup ?? false,
|
|
21607
|
-
exists:
|
|
21798
|
+
exists: fs87.existsSync(r.path)
|
|
21608
21799
|
}));
|
|
21609
21800
|
console.log(toonTable("repos", rows, ["key", "path", "trunk", "pickup", "exists"]));
|
|
21610
21801
|
break;
|
|
@@ -21722,7 +21913,7 @@ ${usageFor("pet")}`);
|
|
|
21722
21913
|
break;
|
|
21723
21914
|
}
|
|
21724
21915
|
case "init": {
|
|
21725
|
-
if (!
|
|
21916
|
+
if (!fs87.existsSync(configPath())) {
|
|
21726
21917
|
const exampleRepo = has("--scan") ? "" : `[repos.example]
|
|
21727
21918
|
path = "~/src/example"
|
|
21728
21919
|
trunk = "main"
|
|
@@ -21731,7 +21922,7 @@ trunk = "main"
|
|
|
21731
21922
|
# pickup = true # opt into [pickup.github] multi-repo tracker pickup
|
|
21732
21923
|
|
|
21733
21924
|
`;
|
|
21734
|
-
|
|
21925
|
+
fs87.writeFileSync(
|
|
21735
21926
|
configPath(),
|
|
21736
21927
|
`# lobstah workspace definitions \u2014 the descriptor's repo key resolves here.
|
|
21737
21928
|
# Top-level keys must come before any [section].
|
|
@@ -21769,7 +21960,7 @@ wallClockSecs = 3600
|
|
|
21769
21960
|
let reference = "https://github.com/aequitas-labs/lobstah/blob/main/docs/configuration.md";
|
|
21770
21961
|
try {
|
|
21771
21962
|
const local = fileURLToPath6(new URL("../docs/configuration.md", import.meta.url));
|
|
21772
|
-
if (
|
|
21963
|
+
if (fs87.existsSync(local)) reference = local;
|
|
21773
21964
|
} catch {
|
|
21774
21965
|
}
|
|
21775
21966
|
console.log(
|