lobstah 0.7.1 → 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 +510 -328
- package/dist/runner.js +6 -2
- package/docs/configuration.md +1 -0
- package/docs/man.md +5 -2
- 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";
|
|
@@ -11959,7 +12001,10 @@ function pruneStaleAcks(standing) {
|
|
|
11959
12001
|
return pruned;
|
|
11960
12002
|
}
|
|
11961
12003
|
function ackItemExists(key, culling = /* @__PURE__ */ new Set()) {
|
|
11962
|
-
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
|
+
}
|
|
11963
12008
|
if (key.startsWith("watch:")) return readWatch(key.slice("watch:".length)) !== void 0;
|
|
11964
12009
|
if (key.startsWith("decision:")) return readDecision(key) !== void 0 && readDecisionAnswer(key) === void 0;
|
|
11965
12010
|
if (key.startsWith("report:")) {
|
|
@@ -12056,6 +12101,15 @@ function reportAck(r) {
|
|
|
12056
12101
|
const ack = currentAck(r.key, r.stateHash);
|
|
12057
12102
|
return ack ? { at: ack.at, by: ack.by } : void 0;
|
|
12058
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
|
+
}
|
|
12059
12113
|
function reportRows() {
|
|
12060
12114
|
return listReports().map((r) => ({
|
|
12061
12115
|
key: r.key,
|
|
@@ -12331,7 +12385,7 @@ function deriveGlassPrs(dispatches, watches = [], records = [], stackOptions = {
|
|
|
12331
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)) {
|
|
12332
12386
|
evidenceByUrl.set(d.pr.url, d.pr);
|
|
12333
12387
|
}
|
|
12334
|
-
const readiness = derivePrStacks([...evidenceByUrl.values()], stackOptions);
|
|
12388
|
+
const readiness = identifyPrStacks(derivePrStacks([...evidenceByUrl.values()], stackOptions));
|
|
12335
12389
|
for (const [id, members] of groupedStacks) {
|
|
12336
12390
|
const root = members.find((p) => p.key === id);
|
|
12337
12391
|
const ordered = [];
|
|
@@ -12364,6 +12418,8 @@ function deriveGlassPrs(dispatches, watches = [], records = [], stackOptions = {
|
|
|
12364
12418
|
});
|
|
12365
12419
|
const status = readiness.find((s) => s.members.some((p) => members.some((m) => p.url === m.url)));
|
|
12366
12420
|
if (status) stacks.at(-1).readiness = {
|
|
12421
|
+
id: status.id,
|
|
12422
|
+
url: status.members.at(-1).url,
|
|
12367
12423
|
ready: status.ready,
|
|
12368
12424
|
total: status.members.length,
|
|
12369
12425
|
allReady: status.allReady,
|
|
@@ -12723,6 +12779,15 @@ function progressStreak(pr) {
|
|
|
12723
12779
|
if (!own && s.startHead !== void 0 && s.startHead !== pr.headSha) return { count: 0 };
|
|
12724
12780
|
return { count: s.count + 1 };
|
|
12725
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
|
+
}
|
|
12726
12791
|
function stackedAbove(pr, records, workers) {
|
|
12727
12792
|
const out = [];
|
|
12728
12793
|
for (const p of prsAbove(records, pr.key)) {
|
|
@@ -12759,8 +12824,9 @@ function deliverPrRepairs(log, cap = 3, opts = {}) {
|
|
|
12759
12824
|
}
|
|
12760
12825
|
const watch2 = w;
|
|
12761
12826
|
withPrLock(watch2.key, () => {
|
|
12762
|
-
|
|
12827
|
+
let pr = readPr(watch2.key);
|
|
12763
12828
|
if (!pr || pr.state !== "OPEN" || pr.dispatches.length === 0 || (pr.observations ?? 0) <= 1) return;
|
|
12829
|
+
pr = completedRepair(pr, now2);
|
|
12764
12830
|
const owner = watch2.owner.slice("dispatch:".length);
|
|
12765
12831
|
const origin = storedDescriptor(owner, "work")?.followUp;
|
|
12766
12832
|
const ancestorPr = origin && chainPr(origin, "work");
|
|
@@ -12906,9 +12972,16 @@ function deliverPrRepairs(log, cap = 3, opts = {}) {
|
|
|
12906
12972
|
log(`repair ${watch2.key}: stopped \u2014 ${reason}`);
|
|
12907
12973
|
return;
|
|
12908
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
|
+
}
|
|
12909
12981
|
const id = randomUUID6();
|
|
12910
12982
|
writePr({
|
|
12911
12983
|
...pr,
|
|
12984
|
+
repairCooldown: { dispatchId: id },
|
|
12912
12985
|
repairStreak: { count: streak.count, lastRepairId: id, startHead: pr.headSha },
|
|
12913
12986
|
repair: {
|
|
12914
12987
|
headSha: pr.headSha,
|
|
@@ -13145,36 +13218,12 @@ function prAttention(now2, observed2 = observedPrs(), cfg = loadConfig()) {
|
|
|
13145
13218
|
Object.values(readPickupMap()).filter((e) => e.kind === "review").map((e) => e.uuid)
|
|
13146
13219
|
);
|
|
13147
13220
|
const out = [];
|
|
13148
|
-
const stacks = derivePrStacks(observed2.map((x) => x.pr), {
|
|
13221
|
+
const stacks = identifyPrStacks(derivePrStacks(observed2.map((x) => x.pr), {
|
|
13149
13222
|
trunk: (repo) => prStackTrunk(cfg, repo),
|
|
13150
13223
|
readySettleSecs: cfg.readySettleSecs,
|
|
13151
13224
|
now: now2
|
|
13152
|
-
});
|
|
13225
|
+
}));
|
|
13153
13226
|
const epochs = readStackEpochs();
|
|
13154
|
-
const quietReady = /* @__PURE__ */ new Set([
|
|
13155
|
-
...stacks.flatMap((s) => s.members.map((p) => p.url)),
|
|
13156
|
-
...quietStackTailUrls(observed2.map((x) => x.pr), epochs)
|
|
13157
|
-
]);
|
|
13158
|
-
for (const s of stacks.filter((s2) => s2.allReady)) {
|
|
13159
|
-
const top = s.members.at(-1), ref = parsePrRef(top.url);
|
|
13160
|
-
const epoch = epochs[s.id];
|
|
13161
|
-
const at = epoch?.ready ? epoch.at : new Date(now2).toISOString();
|
|
13162
|
-
const source = observed2.find((x) => x.pr.url === top.url);
|
|
13163
|
-
out.push({
|
|
13164
|
-
kind: "stack-ready",
|
|
13165
|
-
key: `stack:${s.id}`,
|
|
13166
|
-
stateHash: stackStateHash(s, epoch?.epoch),
|
|
13167
|
-
id: source.id,
|
|
13168
|
-
lane: source.lane,
|
|
13169
|
-
verb: "stack-ready",
|
|
13170
|
-
at,
|
|
13171
|
-
standingSince: at,
|
|
13172
|
-
ageSecs: Math.max(0, Math.round((now2 - Date.parse(at)) / 1e3)),
|
|
13173
|
-
note: s.text,
|
|
13174
|
-
repo: source.dispatch ? repoOf(source.id, source.lane) : repoKey(cfg, s.repo),
|
|
13175
|
-
prUrl: ref.url
|
|
13176
|
-
});
|
|
13177
|
-
}
|
|
13178
13227
|
for (const { id, lane, pr, dispatch } of observed2) {
|
|
13179
13228
|
const kinds = prKinds(pr, { readySettleSecs: cfg.readySettleSecs, now: now2 });
|
|
13180
13229
|
if (kinds.length === 0) continue;
|
|
@@ -13183,6 +13232,10 @@ function prAttention(now2, observed2 = observedPrs(), cfg = loadConfig()) {
|
|
|
13183
13232
|
const watchFollowUp = prWatch?.lastFollowUpId;
|
|
13184
13233
|
const chain = dispatch ? chainOf(id) : [];
|
|
13185
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
|
+
}
|
|
13186
13239
|
const human = humanPrAttention(pr, kind, cfg, !!prWatch && !prWatch.done, now2);
|
|
13187
13240
|
if (!human.show) continue;
|
|
13188
13241
|
const repairCannotAct = human.reason !== void 0 && !["auto-repair is off", "not owned by lobstah", "no active PR watch"].includes(human.reason);
|
|
@@ -13191,7 +13244,6 @@ function prAttention(now2, observed2 = observedPrs(), cfg = loadConfig()) {
|
|
|
13191
13244
|
const standingSince = kind === "pr:ready" ? new Date(Date.parse(conditionSince) + cfg.readySettleSecs * 1e3).toISOString() : conditionSince;
|
|
13192
13245
|
out.push({
|
|
13193
13246
|
kind,
|
|
13194
|
-
...kind === "pr:ready" && quietReady.has(pr.url) ? { quiet: true } : {},
|
|
13195
13247
|
key: ref?.key ?? pr.url,
|
|
13196
13248
|
stateHash: prStateHash(pr),
|
|
13197
13249
|
id,
|
|
@@ -13214,6 +13266,38 @@ function prAttention(now2, observed2 = observedPrs(), cfg = loadConfig()) {
|
|
|
13214
13266
|
});
|
|
13215
13267
|
}
|
|
13216
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
|
+
}
|
|
13217
13301
|
return out;
|
|
13218
13302
|
}
|
|
13219
13303
|
function landedAttention(cfg, now2) {
|
|
@@ -13545,8 +13629,10 @@ function buildTendReport(now2 = Date.now()) {
|
|
|
13545
13629
|
{ readySettleSecs: cfg.readySettleSecs, now: now2, trunk: (r) => prStackTrunk(cfg, r) }
|
|
13546
13630
|
).stacks.filter((s) => s.open);
|
|
13547
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);
|
|
13548
13634
|
const enabled = new Set(cfg.attentionKinds);
|
|
13549
|
-
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)));
|
|
13550
13636
|
const shown = enabled.has("decision") ? hideFramedQuestions(walking) : walking;
|
|
13551
13637
|
attention.length = 0;
|
|
13552
13638
|
attention.push(
|
|
@@ -13589,7 +13675,8 @@ function buildTendReport(now2 = Date.now()) {
|
|
|
13589
13675
|
const notices = listNotices(5).map((n) => ({
|
|
13590
13676
|
kind: n.kind,
|
|
13591
13677
|
ageMins: Math.max(0, Math.round((now2 - (Date.parse(n.at) || 0)) / 6e4)),
|
|
13592
|
-
text: n.text
|
|
13678
|
+
text: n.text,
|
|
13679
|
+
...n.url ? { url: n.url } : {}
|
|
13593
13680
|
}));
|
|
13594
13681
|
const helms = listHelms().map((h) => ({
|
|
13595
13682
|
grounds: h.grounds,
|
|
@@ -13662,7 +13749,9 @@ function renderTend(r) {
|
|
|
13662
13749
|
if (r.queueWait) lines.push(r.queueWait);
|
|
13663
13750
|
for (const stack of r.stacks) {
|
|
13664
13751
|
lines.push(`stack ${stack.numbers.map((n) => `#${n}`).join(" \u2192 ")}: next ${stack.nextNumber ? `#${stack.nextNumber}` : "none"}`);
|
|
13665
|
-
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}`);
|
|
13666
13755
|
}
|
|
13667
13756
|
if (r.attention.length > 0) {
|
|
13668
13757
|
lines.push("");
|
|
@@ -13674,8 +13763,9 @@ function renderTend(r) {
|
|
|
13674
13763
|
verb: a.kind === "question" || a.kind === "watch" ? a.verb : a.kind === "landed" ? `landed (${a.verb})` : a.kind,
|
|
13675
13764
|
waitingMins: Math.round(a.ageSecs / 60),
|
|
13676
13765
|
held: a.held ? "yes" : "",
|
|
13677
|
-
// Whether the human has opened it in the glass's decision modal
|
|
13678
|
-
|
|
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" : "",
|
|
13679
13769
|
note: named(a.prUrl ? `${a.note ?? ""} ${a.prUrl}`.trim() : a.note ?? "")
|
|
13680
13770
|
})),
|
|
13681
13771
|
["id", "verb", "waitingMins", "held", "viewed", "note"]
|
|
@@ -13791,7 +13881,7 @@ function renderTend(r) {
|
|
|
13791
13881
|
lines.push(
|
|
13792
13882
|
toonTable(
|
|
13793
13883
|
"notices (recent)",
|
|
13794
|
-
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}` : ""}` })),
|
|
13795
13885
|
["kind", "ageMins", "text"]
|
|
13796
13886
|
)
|
|
13797
13887
|
);
|
|
@@ -13878,7 +13968,7 @@ function buildDigest(opts = {}) {
|
|
|
13878
13968
|
landed,
|
|
13879
13969
|
arisen,
|
|
13880
13970
|
standing,
|
|
13881
|
-
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}`),
|
|
13882
13972
|
traps,
|
|
13883
13973
|
verdict: tend.verdict,
|
|
13884
13974
|
counts: tend.counts
|
|
@@ -14390,13 +14480,82 @@ ${help}`;
|
|
|
14390
14480
|
|
|
14391
14481
|
// apps/cli/src/cull.ts
|
|
14392
14482
|
init_dist2();
|
|
14393
|
-
import * as
|
|
14483
|
+
import * as fs60 from "node:fs";
|
|
14394
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();
|
|
14395
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
|
|
14396
14555
|
var DAY = 864e5;
|
|
14397
14556
|
function walkBytes(target) {
|
|
14398
|
-
const stat =
|
|
14399
|
-
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);
|
|
14400
14559
|
return stat.isFile() ? stat.size : 0;
|
|
14401
14560
|
}
|
|
14402
14561
|
var sizing = {
|
|
@@ -14411,7 +14570,7 @@ var sizing = {
|
|
|
14411
14570
|
worktree(target, platform = process.platform) {
|
|
14412
14571
|
if (platform !== "win32") {
|
|
14413
14572
|
try {
|
|
14414
|
-
const out =
|
|
14573
|
+
const out = execFileSync8("du", ["-sk", target], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
14415
14574
|
const kb = Number(out.trim().split(/\s+/)[0]);
|
|
14416
14575
|
if (Number.isFinite(kb)) return kb * 1024;
|
|
14417
14576
|
} catch {
|
|
@@ -14422,7 +14581,7 @@ var sizing = {
|
|
|
14422
14581
|
};
|
|
14423
14582
|
function idsIn(dir) {
|
|
14424
14583
|
try {
|
|
14425
|
-
return new Set(
|
|
14584
|
+
return new Set(fs60.readdirSync(dir).filter((f) => !f.startsWith(".")).map((f) => f.replace(/\.json$/, "")));
|
|
14426
14585
|
} catch {
|
|
14427
14586
|
return /* @__PURE__ */ new Set();
|
|
14428
14587
|
}
|
|
@@ -14458,7 +14617,7 @@ function planCull(olderThanDays, now2 = Date.now(), opts = {}) {
|
|
|
14458
14617
|
retainReferences(id, lane);
|
|
14459
14618
|
continue;
|
|
14460
14619
|
}
|
|
14461
|
-
const m =
|
|
14620
|
+
const m = fs60.statSync(p).mtimeMs;
|
|
14462
14621
|
doneMtimes.set(id, m);
|
|
14463
14622
|
if (m >= cutoff) retainReferences(id, lane);
|
|
14464
14623
|
if (m < cutoff) items.push({ kind: "done", id, target: p, ageDays: Math.floor((now2 - m) / DAY), bytes: size(p), ageFrom: m });
|
|
@@ -14467,28 +14626,45 @@ function planCull(olderThanDays, now2 = Date.now(), opts = {}) {
|
|
|
14467
14626
|
const wtRoot = path52.join(lobstahHome(), "worktrees");
|
|
14468
14627
|
const trapped = trapWorktreeIds(wtRoot);
|
|
14469
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);
|
|
14470
14634
|
for (const id of idsIn(wtRoot)) {
|
|
14471
14635
|
if (live.has(id) || trapped.has(id) || usage.live.has(id)) continue;
|
|
14472
14636
|
const doneAt = usage.newest.get(id) ?? doneMtimes.get(id);
|
|
14473
14637
|
if (doneAt !== void 0 && doneAt >= cutoff) continue;
|
|
14474
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
|
+
}
|
|
14475
14650
|
if (readTrapAnchor(p)?.createdBy === "soak" && inspectTrapWorktree(p).reason) continue;
|
|
14476
|
-
const from = doneAt ??
|
|
14651
|
+
const from = doneAt ?? fs60.statSync(p).mtimeMs;
|
|
14477
14652
|
items.push({ kind: "worktree", id, target: p, ageDays: Math.floor((now2 - from) / DAY), bytes: measure ? sizing.worktree(p) : 0, ageFrom: from });
|
|
14478
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);
|
|
14479
14655
|
for (const lane of ["work", "chore"]) {
|
|
14480
14656
|
const d = laneDirs(lane);
|
|
14481
14657
|
const groups = /* @__PURE__ */ new Map();
|
|
14482
|
-
for (const f of
|
|
14658
|
+
for (const f of fs60.readdirSync(d.state)) {
|
|
14483
14659
|
const target = path52.join(d.state, f);
|
|
14484
|
-
const directory =
|
|
14485
|
-
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;
|
|
14486
14662
|
const id = directory ? f : f.replace(/\.(status|events|evidence|attn|notified|runner\.log)$/, "");
|
|
14487
14663
|
if (id === f && !directory) continue;
|
|
14488
14664
|
if (live.has(id) || (doneMtimes.get(id) ?? 0) >= cutoff || referencedAttachmentState.has(path52.join(d.state, id))) continue;
|
|
14489
14665
|
const previous = groups.get(id);
|
|
14490
14666
|
groups.set(id, {
|
|
14491
|
-
mtime: Math.max(previous?.mtime ?? 0,
|
|
14667
|
+
mtime: Math.max(previous?.mtime ?? 0, fs60.statSync(target).mtimeMs),
|
|
14492
14668
|
bytes: (previous?.bytes ?? 0) + size(target)
|
|
14493
14669
|
});
|
|
14494
14670
|
}
|
|
@@ -14499,7 +14675,7 @@ function planCull(olderThanDays, now2 = Date.now(), opts = {}) {
|
|
|
14499
14675
|
}
|
|
14500
14676
|
}
|
|
14501
14677
|
for (const r of readPrs()) {
|
|
14502
|
-
if (r.state === "OPEN") continue;
|
|
14678
|
+
if (r.state === "OPEN" || keptPrs.has(r.key)) continue;
|
|
14503
14679
|
const seen = Date.parse(r.observedAt) || now2;
|
|
14504
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 });
|
|
14505
14681
|
}
|
|
@@ -14559,7 +14735,7 @@ function worktreeUsage(openPr) {
|
|
|
14559
14735
|
if (openPr?.has(id)) live.add(owner);
|
|
14560
14736
|
let m;
|
|
14561
14737
|
try {
|
|
14562
|
-
m =
|
|
14738
|
+
m = fs60.statSync(path52.join(d.done, id)).mtimeMs;
|
|
14563
14739
|
} catch {
|
|
14564
14740
|
continue;
|
|
14565
14741
|
}
|
|
@@ -14573,7 +14749,7 @@ function trapWorktreeIds(wtRoot) {
|
|
|
14573
14749
|
const real = (p) => {
|
|
14574
14750
|
let r;
|
|
14575
14751
|
try {
|
|
14576
|
-
r =
|
|
14752
|
+
r = fs60.realpathSync.native(p);
|
|
14577
14753
|
} catch {
|
|
14578
14754
|
r = path52.resolve(p);
|
|
14579
14755
|
}
|
|
@@ -14602,13 +14778,15 @@ function limitBatch(items, maxGroups) {
|
|
|
14602
14778
|
return { batch: items.filter((i) => keep.has(groupOf(i))), deferred: order.length - keep.size };
|
|
14603
14779
|
}
|
|
14604
14780
|
function planPressureCull(now2 = Date.now()) {
|
|
14781
|
+
const proofs = mergedCullProofs();
|
|
14782
|
+
const fetched = /* @__PURE__ */ new Map();
|
|
14605
14783
|
const live = /* @__PURE__ */ new Set();
|
|
14606
14784
|
const doneMtimes = /* @__PURE__ */ new Map();
|
|
14607
14785
|
for (const lane of ["work", "chore"]) {
|
|
14608
14786
|
const d = laneDirs(lane);
|
|
14609
14787
|
for (const id of idsIn(d.queue)) live.add(id);
|
|
14610
14788
|
for (const id of idsIn(d.active)) live.add(id);
|
|
14611
|
-
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);
|
|
14612
14790
|
}
|
|
14613
14791
|
const openPr = openPrDispatches();
|
|
14614
14792
|
for (const id of openPr) live.add(id);
|
|
@@ -14620,8 +14798,10 @@ function planPressureCull(now2 = Date.now()) {
|
|
|
14620
14798
|
for (const id of idsIn(wtRoot)) {
|
|
14621
14799
|
if (live.has(id)) continue;
|
|
14622
14800
|
const p = path52.join(wtRoot, id);
|
|
14801
|
+
const proof = proofs.get(id);
|
|
14802
|
+
if (proof && mergedWorktreeUnsafe(p, proof.heads, proof.trunk, fetched)) continue;
|
|
14623
14803
|
if (readTrapAnchor(p)?.createdBy === "soak" && inspectTrapWorktree(p).reason) continue;
|
|
14624
|
-
const from = usage.newest.get(id) ?? doneMtimes.get(id) ??
|
|
14804
|
+
const from = usage.newest.get(id) ?? doneMtimes.get(id) ?? fs60.statSync(p).mtimeMs;
|
|
14625
14805
|
items.push({ kind: "worktree", id, target: p, ageDays: Math.floor((now2 - from) / DAY), bytes: 0, ageFrom: from });
|
|
14626
14806
|
}
|
|
14627
14807
|
return items.sort((a, b) => a.ageFrom - b.ageFrom || a.id.localeCompare(b.id));
|
|
@@ -14635,28 +14815,28 @@ function removeWorktree(id, dir) {
|
|
|
14635
14815
|
for (const lane of ["work", "chore"]) {
|
|
14636
14816
|
const descFile = path52.join(laneDirs(lane).done, id, "descriptor.json");
|
|
14637
14817
|
try {
|
|
14638
|
-
const desc = JSON.parse(
|
|
14818
|
+
const desc = JSON.parse(fs60.readFileSync(descFile, "utf8"));
|
|
14639
14819
|
const repo = loadConfig().repos[desc.repo];
|
|
14640
14820
|
if (repo) {
|
|
14641
|
-
|
|
14821
|
+
execFileSync8("git", ["worktree", "remove", "--force", dir], { cwd: repo.path, stdio: "ignore" });
|
|
14642
14822
|
return;
|
|
14643
14823
|
}
|
|
14644
14824
|
} catch {
|
|
14645
14825
|
}
|
|
14646
14826
|
}
|
|
14647
14827
|
try {
|
|
14648
|
-
const common =
|
|
14828
|
+
const common = execFileSync8("git", ["rev-parse", "--path-format=absolute", "--git-common-dir"], {
|
|
14649
14829
|
cwd: dir,
|
|
14650
14830
|
encoding: "utf8",
|
|
14651
14831
|
stdio: ["ignore", "pipe", "ignore"]
|
|
14652
14832
|
}).trim();
|
|
14653
14833
|
if (common) {
|
|
14654
|
-
|
|
14834
|
+
execFileSync8("git", ["--git-dir", common, "worktree", "remove", "--force", dir], { stdio: "ignore" });
|
|
14655
14835
|
return;
|
|
14656
14836
|
}
|
|
14657
14837
|
} catch {
|
|
14658
14838
|
}
|
|
14659
|
-
|
|
14839
|
+
fs60.rmSync(dir, { recursive: true, force: true });
|
|
14660
14840
|
}
|
|
14661
14841
|
function culledState(items) {
|
|
14662
14842
|
const lanes = new Map(["work", "chore"].map((lane) => [path52.resolve(laneDirs(lane).state), lane]));
|
|
@@ -14668,7 +14848,7 @@ function culledState(items) {
|
|
|
14668
14848
|
function applyCull(items) {
|
|
14669
14849
|
foldCatches(culledState(items));
|
|
14670
14850
|
for (const item of items.filter((i) => i.kind === "worktree")) removeWorktree(item.id, item.target);
|
|
14671
|
-
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 });
|
|
14672
14852
|
for (const item of items.filter((i) => i.kind === "ack")) removeAck(item.target);
|
|
14673
14853
|
for (const item of items.filter((i) => i.kind === "pr")) removePr(item.target);
|
|
14674
14854
|
for (const item of items.filter((i) => i.kind === "report")) removeHelmReport(item.id);
|
|
@@ -14676,8 +14856,8 @@ function applyCull(items) {
|
|
|
14676
14856
|
for (const item of items.filter((i) => i.kind === "release")) removeRelease(item.id);
|
|
14677
14857
|
for (const item of items.filter((i) => i.kind === "state")) {
|
|
14678
14858
|
const dir = path52.dirname(item.target);
|
|
14679
|
-
for (const f of
|
|
14680
|
-
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 });
|
|
14681
14861
|
}
|
|
14682
14862
|
}
|
|
14683
14863
|
}
|
|
@@ -14746,13 +14926,11 @@ ${toonKV(statusFields(s))}${still}`;
|
|
|
14746
14926
|
|
|
14747
14927
|
// apps/cli/src/release.ts
|
|
14748
14928
|
init_dist2();
|
|
14749
|
-
|
|
14750
|
-
import * as fs60 from "node:fs";
|
|
14751
|
-
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
14929
|
+
import * as fs61 from "node:fs";
|
|
14752
14930
|
var DAY2 = 864e5;
|
|
14753
14931
|
function listIds(dir) {
|
|
14754
14932
|
try {
|
|
14755
|
-
return
|
|
14933
|
+
return fs61.readdirSync(dir).filter((f) => !f.startsWith(".")).map((f) => f.replace(/\.json$/, ""));
|
|
14756
14934
|
} catch {
|
|
14757
14935
|
return [];
|
|
14758
14936
|
}
|
|
@@ -14803,15 +14981,16 @@ function prUrlOf(ev) {
|
|
|
14803
14981
|
}
|
|
14804
14982
|
function mergedPrs(all) {
|
|
14805
14983
|
const merged = /* @__PURE__ */ new Map();
|
|
14806
|
-
const add2 = (url, key, headRef, id) => {
|
|
14807
|
-
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])];
|
|
14808
14987
|
m.headRef ??= headRef;
|
|
14809
14988
|
m.owners.add(id);
|
|
14810
14989
|
merged.set(key, m);
|
|
14811
14990
|
};
|
|
14812
14991
|
for (const r of readPrs()) {
|
|
14813
14992
|
if (r.state !== "MERGED") continue;
|
|
14814
|
-
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]);
|
|
14815
14994
|
}
|
|
14816
14995
|
for (const k of all.values()) {
|
|
14817
14996
|
if (k.bucket !== "done") continue;
|
|
@@ -14821,28 +15000,10 @@ function mergedPrs(all) {
|
|
|
14821
15000
|
if (!ref) continue;
|
|
14822
15001
|
const record = readPr(ref.key);
|
|
14823
15002
|
const state = record?.state ?? ev.pr?.state;
|
|
14824
|
-
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] : []);
|
|
14825
15004
|
}
|
|
14826
15005
|
return merged;
|
|
14827
15006
|
}
|
|
14828
|
-
function git7(cwd, ...args) {
|
|
14829
|
-
try {
|
|
14830
|
-
return execFileSync8("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
14831
|
-
} catch {
|
|
14832
|
-
return void 0;
|
|
14833
|
-
}
|
|
14834
|
-
}
|
|
14835
|
-
function unsafe(dir, fetched) {
|
|
14836
|
-
const status = git7(dir, "status", "--porcelain");
|
|
14837
|
-
if (status === void 0) return "unpushed work: not a readable git checkout";
|
|
14838
|
-
if (status !== "") return "unpushed work: uncommitted changes";
|
|
14839
|
-
const common = git7(dir, "rev-parse", "--path-format=absolute", "--git-common-dir") ?? dir;
|
|
14840
|
-
if (!fetched.has(common)) fetched.set(common, withRepoLockSync(dir, () => git7(dir, "fetch", "--quiet", "origin")) !== void 0);
|
|
14841
|
-
if (!fetched.get(common)) return "unpushed work: fetch failed, remote state unknown";
|
|
14842
|
-
const remote = git7(dir, "branch", "-r", "--contains", "HEAD");
|
|
14843
|
-
if (remote === void 0 || remote === "") return "unpushed work: HEAD is not on the remote";
|
|
14844
|
-
return void 0;
|
|
14845
|
-
}
|
|
14846
15007
|
function planMergeRelease(now2 = Date.now(), batch = Infinity) {
|
|
14847
15008
|
const plan = { items: [], users: /* @__PURE__ */ new Map(), kept: [] };
|
|
14848
15009
|
if (!loadConfig().limits.releaseOnMerge) return plan;
|
|
@@ -14869,7 +15030,11 @@ function planMergeRelease(now2 = Date.now(), batch = Infinity) {
|
|
|
14869
15030
|
const chain = chains.get(id) ?? /* @__PURE__ */ new Set([id]);
|
|
14870
15031
|
const home = owner(id);
|
|
14871
15032
|
const add2 = (o) => {
|
|
14872
|
-
|
|
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 });
|
|
14873
15038
|
};
|
|
14874
15039
|
add2(home);
|
|
14875
15040
|
for (const m of chain) {
|
|
@@ -14883,7 +15048,7 @@ function planMergeRelease(now2 = Date.now(), batch = Infinity) {
|
|
|
14883
15048
|
}
|
|
14884
15049
|
}
|
|
14885
15050
|
}
|
|
14886
|
-
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));
|
|
14887
15052
|
const fetched = /* @__PURE__ */ new Map();
|
|
14888
15053
|
let checked = 0;
|
|
14889
15054
|
for (const { o, c, from } of ordered) {
|
|
@@ -14893,7 +15058,8 @@ function planMergeRelease(now2 = Date.now(), batch = Infinity) {
|
|
|
14893
15058
|
if (![...c.chain].every((id) => finished(all.get(id))) || !users.every((k) => finished(k))) continue;
|
|
14894
15059
|
checked++;
|
|
14895
15060
|
const dir = worktreePath(o);
|
|
14896
|
-
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);
|
|
14897
15063
|
if (why) {
|
|
14898
15064
|
plan.kept.push({ id: o, reason: why, pr: c.pr.key, at: new Date(now2).toISOString() });
|
|
14899
15065
|
continue;
|
|
@@ -14909,7 +15075,7 @@ function runMergeRelease(now2 = Date.now(), batch = Infinity) {
|
|
|
14909
15075
|
const released = [];
|
|
14910
15076
|
for (const item of plan.items) {
|
|
14911
15077
|
applyCull([item]);
|
|
14912
|
-
if (
|
|
15078
|
+
if (fs61.existsSync(item.target)) continue;
|
|
14913
15079
|
released.push(item.id);
|
|
14914
15080
|
for (const k of plan.users.get(item.id) ?? []) mergeEvidence(k.id, k.lane, { worktreeReleased: at });
|
|
14915
15081
|
}
|
|
@@ -15050,7 +15216,7 @@ full pattern and trade-offs: docs/man.md \xB7 config: docs/configuration.md`;
|
|
|
15050
15216
|
|
|
15051
15217
|
// apps/cli/src/decision-answer.ts
|
|
15052
15218
|
init_dist2();
|
|
15053
|
-
import * as
|
|
15219
|
+
import * as fs62 from "node:fs";
|
|
15054
15220
|
function standingQuestion2(key) {
|
|
15055
15221
|
return buildTendReport().attention.find((a) => a.kind === "question" && a.key === key);
|
|
15056
15222
|
}
|
|
@@ -15077,7 +15243,7 @@ function answerKey(key, req) {
|
|
|
15077
15243
|
try {
|
|
15078
15244
|
return { decision: meta, answer: answerDecision({ key: meta.key, ...req, maxBytes }) };
|
|
15079
15245
|
} catch (err) {
|
|
15080
|
-
|
|
15246
|
+
fs62.rmSync(decisionDir(meta.key), { recursive: true, force: true });
|
|
15081
15247
|
throw err;
|
|
15082
15248
|
}
|
|
15083
15249
|
}
|
|
@@ -15091,7 +15257,7 @@ function viewKey(key, now2 = /* @__PURE__ */ new Date()) {
|
|
|
15091
15257
|
// apps/cli/src/doctor.ts
|
|
15092
15258
|
init_dist();
|
|
15093
15259
|
init_dist2();
|
|
15094
|
-
import * as
|
|
15260
|
+
import * as fs68 from "node:fs";
|
|
15095
15261
|
import * as path58 from "node:path";
|
|
15096
15262
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
15097
15263
|
|
|
@@ -15108,7 +15274,7 @@ function parkedSummary(parked = parkedDispatches()) {
|
|
|
15108
15274
|
|
|
15109
15275
|
// apps/cli/src/plugin-version.ts
|
|
15110
15276
|
init_dist();
|
|
15111
|
-
import * as
|
|
15277
|
+
import * as fs63 from "node:fs";
|
|
15112
15278
|
import * as os7 from "node:os";
|
|
15113
15279
|
import * as path53 from "node:path";
|
|
15114
15280
|
var UPDATE_COMMAND = {
|
|
@@ -15117,7 +15283,7 @@ var UPDATE_COMMAND = {
|
|
|
15117
15283
|
};
|
|
15118
15284
|
var readJson2 = (file2) => {
|
|
15119
15285
|
try {
|
|
15120
|
-
return JSON.parse(
|
|
15286
|
+
return JSON.parse(fs63.readFileSync(file2, "utf8"));
|
|
15121
15287
|
} catch {
|
|
15122
15288
|
return void 0;
|
|
15123
15289
|
}
|
|
@@ -15148,7 +15314,7 @@ function installedCodexPlugin(opts = {}) {
|
|
|
15148
15314
|
const codexDir = env.CODEX_HOME ?? path53.join(opts.home ?? os7.homedir(), ".codex");
|
|
15149
15315
|
let cfg;
|
|
15150
15316
|
try {
|
|
15151
|
-
cfg = parse(
|
|
15317
|
+
cfg = parse(fs63.readFileSync(path53.join(codexDir, "config.toml"), "utf8"));
|
|
15152
15318
|
} catch {
|
|
15153
15319
|
return void 0;
|
|
15154
15320
|
}
|
|
@@ -15157,7 +15323,7 @@ function installedCodexPlugin(opts = {}) {
|
|
|
15157
15323
|
const cache = path53.join(codexDir, "plugins", "cache", "lobstah", "lobstah");
|
|
15158
15324
|
let dirs;
|
|
15159
15325
|
try {
|
|
15160
|
-
dirs =
|
|
15326
|
+
dirs = fs63.readdirSync(cache);
|
|
15161
15327
|
} catch {
|
|
15162
15328
|
return void 0;
|
|
15163
15329
|
}
|
|
@@ -15215,7 +15381,7 @@ function pluginBehindLine(cliVersion, opts = {}) {
|
|
|
15215
15381
|
|
|
15216
15382
|
// apps/cli/src/service.ts
|
|
15217
15383
|
init_dist2();
|
|
15218
|
-
import * as
|
|
15384
|
+
import * as fs64 from "node:fs";
|
|
15219
15385
|
import * as os8 from "node:os";
|
|
15220
15386
|
import * as path54 from "node:path";
|
|
15221
15387
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
@@ -15280,7 +15446,7 @@ function serviceFile(kind) {
|
|
|
15280
15446
|
return process.platform === "darwin" ? path54.join(os8.homedir(), "Library", "LaunchAgents", `lobstah.${kind}.plist`) : path54.join(os8.homedir(), ".config", "systemd", "user", `lobstah-${kind}.service`);
|
|
15281
15447
|
}
|
|
15282
15448
|
function serviceInstalled(kind) {
|
|
15283
|
-
return
|
|
15449
|
+
return fs64.existsSync(serviceFile(kind));
|
|
15284
15450
|
}
|
|
15285
15451
|
function restartCommand(kind, platform = process.platform, uid = process.getuid?.() ?? 0) {
|
|
15286
15452
|
return platform === "darwin" ? ["launchctl", "kickstart", "-k", `gui/${uid}/lobstah.${kind}`] : ["systemctl", "--user", "restart", `lobstah-${kind}.service`];
|
|
@@ -15299,7 +15465,7 @@ function installService(kind, port) {
|
|
|
15299
15465
|
if (process.platform === "win32") {
|
|
15300
15466
|
throw new Error(`no service manager support on Windows \u2014 run \`lobstah ${kind}\` under your process manager of choice`);
|
|
15301
15467
|
}
|
|
15302
|
-
const entry = COMPILED_BINARY ? "" :
|
|
15468
|
+
const entry = COMPILED_BINARY ? "" : fs64.realpathSync(process.argv[1]);
|
|
15303
15469
|
const spec = {
|
|
15304
15470
|
kind,
|
|
15305
15471
|
nodePath: process.execPath,
|
|
@@ -15312,16 +15478,16 @@ function installService(kind, port) {
|
|
|
15312
15478
|
if (kind !== "glass" && !onPath("git", spec.pathEnv)) {
|
|
15313
15479
|
throw new Error(`git is not reachable on the service PATH (${spec.pathEnv}) \u2014 the daemon cannot allocate worktrees`);
|
|
15314
15480
|
}
|
|
15315
|
-
|
|
15481
|
+
fs64.mkdirSync(spec.logDir, { recursive: true });
|
|
15316
15482
|
const file2 = serviceFile(kind);
|
|
15317
|
-
|
|
15483
|
+
fs64.mkdirSync(path54.dirname(file2), { recursive: true });
|
|
15318
15484
|
if (process.platform === "darwin") {
|
|
15319
|
-
if (
|
|
15320
|
-
|
|
15485
|
+
if (fs64.existsSync(file2)) run2("launchctl", ["unload", file2]);
|
|
15486
|
+
fs64.writeFileSync(file2, renderLaunchdPlist(spec));
|
|
15321
15487
|
const load = run2("launchctl", ["load", file2]);
|
|
15322
15488
|
return { file: file2, loaded: load.ok, detail: load.ok ? `label lobstah.${kind}` : load.out };
|
|
15323
15489
|
}
|
|
15324
|
-
|
|
15490
|
+
fs64.writeFileSync(file2, renderSystemdUnit(spec));
|
|
15325
15491
|
run2("systemctl", ["--user", "daemon-reload"]);
|
|
15326
15492
|
const enable = run2("systemctl", ["--user", "enable", "--now", `lobstah-${kind}.service`]);
|
|
15327
15493
|
return { file: file2, loaded: enable.ok, detail: enable.ok ? `unit lobstah-${kind}.service` : enable.out };
|
|
@@ -15330,19 +15496,19 @@ function uninstallService(kind) {
|
|
|
15330
15496
|
if (process.platform === "win32") throw new Error(`no service manager support on Windows`);
|
|
15331
15497
|
const file2 = serviceFile(kind);
|
|
15332
15498
|
if (process.platform === "darwin") {
|
|
15333
|
-
if (
|
|
15499
|
+
if (fs64.existsSync(file2)) run2("launchctl", ["unload", file2]);
|
|
15334
15500
|
} else {
|
|
15335
15501
|
run2("systemctl", ["--user", "disable", "--now", `lobstah-${kind}.service`]);
|
|
15336
15502
|
}
|
|
15337
|
-
const removed =
|
|
15338
|
-
if (removed)
|
|
15503
|
+
const removed = fs64.existsSync(file2);
|
|
15504
|
+
if (removed) fs64.unlinkSync(file2);
|
|
15339
15505
|
if (process.platform !== "darwin") run2("systemctl", ["--user", "daemon-reload"]);
|
|
15340
15506
|
return { file: file2, removed };
|
|
15341
15507
|
}
|
|
15342
15508
|
|
|
15343
15509
|
// apps/cli/src/pet.ts
|
|
15344
15510
|
init_dist2();
|
|
15345
|
-
import * as
|
|
15511
|
+
import * as fs65 from "node:fs";
|
|
15346
15512
|
import * as os9 from "node:os";
|
|
15347
15513
|
import * as path55 from "node:path";
|
|
15348
15514
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
@@ -15394,7 +15560,7 @@ function installPet(binaryArg) {
|
|
|
15394
15560
|
path55.join(process.cwd(), ".build", "debug", "LobstahPet"),
|
|
15395
15561
|
petBinaryHome()
|
|
15396
15562
|
].filter((c) => c !== void 0);
|
|
15397
|
-
const source = candidates.find((c) =>
|
|
15563
|
+
const source = candidates.find((c) => fs65.existsSync(c));
|
|
15398
15564
|
if (!source) {
|
|
15399
15565
|
throw new Error(
|
|
15400
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>"
|
|
@@ -15402,33 +15568,33 @@ function installPet(binaryArg) {
|
|
|
15402
15568
|
}
|
|
15403
15569
|
const binary = petBinaryHome();
|
|
15404
15570
|
const file0 = petPlistFile();
|
|
15405
|
-
if (
|
|
15571
|
+
if (fs65.existsSync(file0)) run3("launchctl", ["unload", file0]);
|
|
15406
15572
|
run3("pkill", ["-f", "LobstahPet"]);
|
|
15407
15573
|
if (path55.resolve(source) !== path55.resolve(binary)) {
|
|
15408
|
-
|
|
15409
|
-
|
|
15410
|
-
|
|
15411
|
-
|
|
15574
|
+
fs65.mkdirSync(path55.dirname(binary), { recursive: true });
|
|
15575
|
+
fs65.rmSync(binary, { force: true });
|
|
15576
|
+
fs65.copyFileSync(source, binary);
|
|
15577
|
+
fs65.chmodSync(binary, 493);
|
|
15412
15578
|
const bundle = path55.join(path55.dirname(source), "LobstahPet_LobstahPet.bundle");
|
|
15413
|
-
if (
|
|
15579
|
+
if (fs65.existsSync(bundle)) {
|
|
15414
15580
|
const dest = path55.join(path55.dirname(binary), "LobstahPet_LobstahPet.bundle");
|
|
15415
|
-
|
|
15416
|
-
|
|
15581
|
+
fs65.rmSync(dest, { recursive: true, force: true });
|
|
15582
|
+
fs65.cpSync(bundle, dest, { recursive: true });
|
|
15417
15583
|
}
|
|
15418
15584
|
}
|
|
15419
15585
|
const logDir = path55.join(lobstahHome(), "logs");
|
|
15420
|
-
|
|
15586
|
+
fs65.mkdirSync(logDir, { recursive: true });
|
|
15421
15587
|
const file2 = petPlistFile();
|
|
15422
|
-
|
|
15423
|
-
|
|
15588
|
+
fs65.mkdirSync(path55.dirname(file2), { recursive: true });
|
|
15589
|
+
fs65.writeFileSync(file2, renderPetPlist(binary, servicePathEnv(process.execPath), lobstahHome(), logDir));
|
|
15424
15590
|
const load = run3("launchctl", ["load", file2]);
|
|
15425
15591
|
return { file: file2, binary, loaded: load.ok, detail: load.ok ? "label lobstah.pet \u2014 starts at login" : load.out };
|
|
15426
15592
|
}
|
|
15427
15593
|
function uninstallPet() {
|
|
15428
15594
|
const file2 = petPlistFile();
|
|
15429
|
-
if (process.platform === "darwin" &&
|
|
15430
|
-
const removed =
|
|
15431
|
-
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);
|
|
15432
15598
|
run3("pkill", ["-f", "LobstahPet"]);
|
|
15433
15599
|
return { file: file2, removed };
|
|
15434
15600
|
}
|
|
@@ -15437,7 +15603,7 @@ function petStateFile() {
|
|
|
15437
15603
|
}
|
|
15438
15604
|
function readPetState() {
|
|
15439
15605
|
try {
|
|
15440
|
-
const s = JSON.parse(
|
|
15606
|
+
const s = JSON.parse(fs65.readFileSync(petStateFile(), "utf8"));
|
|
15441
15607
|
return typeof s.pid === "number" && typeof s.at === "string" ? s : void 0;
|
|
15442
15608
|
} catch {
|
|
15443
15609
|
return void 0;
|
|
@@ -15459,8 +15625,8 @@ function processAlive(pid) {
|
|
|
15459
15625
|
function petRow(opts = {}) {
|
|
15460
15626
|
const now2 = opts.now ?? Date.now();
|
|
15461
15627
|
const alive2 = opts.alive ?? processAlive;
|
|
15462
|
-
const agent =
|
|
15463
|
-
const binary =
|
|
15628
|
+
const agent = fs65.existsSync(opts.plist ?? petPlistFile());
|
|
15629
|
+
const binary = fs65.existsSync(opts.binary ?? petBinaryHome());
|
|
15464
15630
|
const installed = agent ? "installed" : binary ? "binary installed, no login agent" : "not installed";
|
|
15465
15631
|
const state = readPetState();
|
|
15466
15632
|
if (!state) {
|
|
@@ -15483,13 +15649,13 @@ function petRow(opts = {}) {
|
|
|
15483
15649
|
init_dist();
|
|
15484
15650
|
init_dist2();
|
|
15485
15651
|
import { createHash as createHash9 } from "node:crypto";
|
|
15486
|
-
import * as
|
|
15652
|
+
import * as fs67 from "node:fs";
|
|
15487
15653
|
import * as os10 from "node:os";
|
|
15488
15654
|
import * as path57 from "node:path";
|
|
15489
15655
|
|
|
15490
15656
|
// apps/cli/src/watchers.ts
|
|
15491
15657
|
init_dist2();
|
|
15492
|
-
import * as
|
|
15658
|
+
import * as fs66 from "node:fs";
|
|
15493
15659
|
import * as path56 from "node:path";
|
|
15494
15660
|
var STALE_MS = 5e3;
|
|
15495
15661
|
var HEARTBEAT_MS = 1500;
|
|
@@ -15497,7 +15663,7 @@ var watcherDir = () => path56.join(lobstahHome(), "watchers");
|
|
|
15497
15663
|
var watcherFile = (sessionId) => path56.join(watcherDir(), `${encodeURIComponent(sessionId)}.json`);
|
|
15498
15664
|
function liveRegistration(sessionId, now2 = Date.now()) {
|
|
15499
15665
|
try {
|
|
15500
|
-
const w = JSON.parse(
|
|
15666
|
+
const w = JSON.parse(fs66.readFileSync(watcherFile(sessionId), "utf8"));
|
|
15501
15667
|
const beat = Date.parse(w.heartbeatAt);
|
|
15502
15668
|
if (w.sessionId !== sessionId || !Number.isInteger(w.pid) || w.pid <= 0 || !Number.isFinite(beat) || now2 - beat > STALE_MS) return void 0;
|
|
15503
15669
|
return w;
|
|
@@ -15545,7 +15711,7 @@ async function stopWatcher(sessionId, kind, graceMs = 3e3, pollMs = 50) {
|
|
|
15545
15711
|
const file2 = watcherFile(sessionId);
|
|
15546
15712
|
const claimed = () => {
|
|
15547
15713
|
try {
|
|
15548
|
-
return JSON.parse(
|
|
15714
|
+
return JSON.parse(fs66.readFileSync(file2, "utf8")).pid === w.pid;
|
|
15549
15715
|
} catch {
|
|
15550
15716
|
return false;
|
|
15551
15717
|
}
|
|
@@ -15553,7 +15719,7 @@ async function stopWatcher(sessionId, kind, graceMs = 3e3, pollMs = 50) {
|
|
|
15553
15719
|
for (; ; ) {
|
|
15554
15720
|
if (!claimed()) return { watcher: w, exited: true };
|
|
15555
15721
|
if (!pidAlive4(w.pid)) {
|
|
15556
|
-
if (claimed())
|
|
15722
|
+
if (claimed()) fs66.rmSync(file2, { force: true });
|
|
15557
15723
|
return { watcher: w, exited: true };
|
|
15558
15724
|
}
|
|
15559
15725
|
if (Date.now() >= deadline) return { watcher: w, exited: false };
|
|
@@ -15561,13 +15727,13 @@ async function stopWatcher(sessionId, kind, graceMs = 3e3, pollMs = 50) {
|
|
|
15561
15727
|
}
|
|
15562
15728
|
}
|
|
15563
15729
|
function armWatcher(sessionId, kind, trapId) {
|
|
15564
|
-
|
|
15730
|
+
fs66.mkdirSync(watcherDir(), { recursive: true });
|
|
15565
15731
|
const file2 = watcherFile(sessionId);
|
|
15566
15732
|
const own = () => ({ sessionId, kind, pid: process.pid, heartbeatAt: (/* @__PURE__ */ new Date()).toISOString(), ...trapId ? { trapId } : {} });
|
|
15567
15733
|
let claimed = false;
|
|
15568
15734
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
15569
15735
|
try {
|
|
15570
|
-
|
|
15736
|
+
fs66.writeFileSync(file2, `${JSON.stringify(own())}
|
|
15571
15737
|
`, { flag: "wx" });
|
|
15572
15738
|
claimed = true;
|
|
15573
15739
|
break;
|
|
@@ -15576,7 +15742,7 @@ function armWatcher(sessionId, kind, trapId) {
|
|
|
15576
15742
|
const live = liveRegistration(sessionId);
|
|
15577
15743
|
if (live) throw new Error(alreadyArmed(live));
|
|
15578
15744
|
try {
|
|
15579
|
-
|
|
15745
|
+
fs66.rmSync(file2);
|
|
15580
15746
|
} catch {
|
|
15581
15747
|
}
|
|
15582
15748
|
}
|
|
@@ -15584,7 +15750,7 @@ function armWatcher(sessionId, kind, trapId) {
|
|
|
15584
15750
|
if (!claimed) throw new Error(`could not arm watcher for session ${sessionId}`);
|
|
15585
15751
|
const ownsFile = () => {
|
|
15586
15752
|
try {
|
|
15587
|
-
return JSON.parse(
|
|
15753
|
+
return JSON.parse(fs66.readFileSync(file2, "utf8")).pid === process.pid;
|
|
15588
15754
|
} catch {
|
|
15589
15755
|
return false;
|
|
15590
15756
|
}
|
|
@@ -15593,12 +15759,12 @@ function armWatcher(sessionId, kind, trapId) {
|
|
|
15593
15759
|
if (!ownsFile()) return;
|
|
15594
15760
|
const tmp = `${file2}.tmp-${process.pid}`;
|
|
15595
15761
|
try {
|
|
15596
|
-
|
|
15762
|
+
fs66.writeFileSync(tmp, `${JSON.stringify(own())}
|
|
15597
15763
|
`);
|
|
15598
|
-
|
|
15764
|
+
fs66.renameSync(tmp, file2);
|
|
15599
15765
|
} catch {
|
|
15600
15766
|
try {
|
|
15601
|
-
|
|
15767
|
+
fs66.rmSync(tmp, { force: true });
|
|
15602
15768
|
} catch {
|
|
15603
15769
|
}
|
|
15604
15770
|
}
|
|
@@ -15610,7 +15776,7 @@ function armWatcher(sessionId, kind, trapId) {
|
|
|
15610
15776
|
if (stopped) return;
|
|
15611
15777
|
stopped = true;
|
|
15612
15778
|
clearInterval(timer);
|
|
15613
|
-
if (ownsFile())
|
|
15779
|
+
if (ownsFile()) fs66.rmSync(file2, { force: true });
|
|
15614
15780
|
process.off("exit", stop);
|
|
15615
15781
|
process.off("SIGINT", interrupt);
|
|
15616
15782
|
process.off("SIGTERM", terminate2);
|
|
@@ -15632,7 +15798,7 @@ function armWatcher(sessionId, kind, trapId) {
|
|
|
15632
15798
|
// apps/cli/src/hook-readiness.ts
|
|
15633
15799
|
var readJson3 = (file2) => {
|
|
15634
15800
|
try {
|
|
15635
|
-
return JSON.parse(
|
|
15801
|
+
return JSON.parse(fs67.readFileSync(file2, "utf8"));
|
|
15636
15802
|
} catch {
|
|
15637
15803
|
return void 0;
|
|
15638
15804
|
}
|
|
@@ -15669,7 +15835,7 @@ function codexHooks(opts = {}) {
|
|
|
15669
15835
|
const codexDir = env.CODEX_HOME ?? path57.join(opts.home ?? os10.homedir(), ".codex");
|
|
15670
15836
|
let cfg = {};
|
|
15671
15837
|
try {
|
|
15672
|
-
cfg = parse(
|
|
15838
|
+
cfg = parse(fs67.readFileSync(path57.join(codexDir, "config.toml"), "utf8"));
|
|
15673
15839
|
} catch {
|
|
15674
15840
|
}
|
|
15675
15841
|
const state = (cfg.hooks ?? {}).state ?? {};
|
|
@@ -15903,7 +16069,7 @@ var PROBE_PERMISSION = { "pull requests": "Pull requests", contents: "Contents",
|
|
|
15903
16069
|
function githubRepos(repos) {
|
|
15904
16070
|
const out = [];
|
|
15905
16071
|
for (const [key, r] of Object.entries(repos)) {
|
|
15906
|
-
const origin = r.origin ?? (
|
|
16072
|
+
const origin = r.origin ?? (fs68.existsSync(r.path) ? git8(r.path, "remote", "get-url", "origin").out : "");
|
|
15907
16073
|
const forgeRepo = origin ? githubRepoFromOrigin(origin) : void 0;
|
|
15908
16074
|
if (forgeRepo) out.push({ key, forgeRepo, trunk: r.trunk });
|
|
15909
16075
|
}
|
|
@@ -15913,12 +16079,12 @@ function diskRow(cfg, freeBytes = statfsFreeBytes, now2 = Date.now()) {
|
|
|
15913
16079
|
const dir = worktreesDir();
|
|
15914
16080
|
let free;
|
|
15915
16081
|
try {
|
|
15916
|
-
free = freeBytes(
|
|
16082
|
+
free = freeBytes(fs68.existsSync(dir) ? dir : lobstahHome());
|
|
15917
16083
|
} catch {
|
|
15918
16084
|
free = void 0;
|
|
15919
16085
|
}
|
|
15920
16086
|
const { minFreeGB, retentionDays } = cfg.limits;
|
|
15921
|
-
const cullable =
|
|
16087
|
+
const cullable = fs68.existsSync(dir) ? planPressureCull(now2) : [];
|
|
15922
16088
|
const oldest = cullable.reduce((max, i) => Math.max(max, i.ageDays), 0);
|
|
15923
16089
|
const parts = [
|
|
15924
16090
|
free === void 0 ? "free space unreadable" : `${formatGB(free)} free on ${dir}`,
|
|
@@ -15930,7 +16096,7 @@ function diskRow(cfg, freeBytes = statfsFreeBytes, now2 = Date.now()) {
|
|
|
15930
16096
|
const kept = readKeptWorktrees();
|
|
15931
16097
|
if (kept.length > 0) {
|
|
15932
16098
|
parts.push(
|
|
15933
|
-
`kept:
|
|
16099
|
+
`kept: ${kept.length} worktree(s) of merged PRs: ${kept.map((k) => `${k.id.slice(0, 8)} ${k.reason}`).join(", ")}`
|
|
15934
16100
|
);
|
|
15935
16101
|
}
|
|
15936
16102
|
const hold = readHold();
|
|
@@ -15956,7 +16122,7 @@ async function runDoctor(now2 = Date.now()) {
|
|
|
15956
16122
|
try {
|
|
15957
16123
|
const port = glassPort();
|
|
15958
16124
|
const info = await probeGlass(port);
|
|
15959
|
-
const installed = process.platform === "win32" ? false :
|
|
16125
|
+
const installed = process.platform === "win32" ? false : fs68.existsSync(serviceFile("glass"));
|
|
15960
16126
|
push(
|
|
15961
16127
|
"glass",
|
|
15962
16128
|
info ? "ok" : "warn",
|
|
@@ -15968,13 +16134,13 @@ async function runDoctor(now2 = Date.now()) {
|
|
|
15968
16134
|
const pet = petRow({ now: now2 });
|
|
15969
16135
|
push(pet.check, pet.status, pet.detail);
|
|
15970
16136
|
const cfgFile = configPath();
|
|
15971
|
-
if (!
|
|
16137
|
+
if (!fs68.existsSync(cfgFile)) {
|
|
15972
16138
|
push("config", "fail", `${cfgFile} missing \u2014 run \`lobstah init\``);
|
|
15973
16139
|
return rows;
|
|
15974
16140
|
}
|
|
15975
16141
|
let parsed;
|
|
15976
16142
|
try {
|
|
15977
|
-
parsed = parse(
|
|
16143
|
+
parsed = parse(fs68.readFileSync(cfgFile, "utf8"));
|
|
15978
16144
|
push("config", "ok", cfgFile);
|
|
15979
16145
|
} catch (err) {
|
|
15980
16146
|
push("config", "fail", `${cfgFile}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -15985,7 +16151,7 @@ async function runDoctor(now2 = Date.now()) {
|
|
|
15985
16151
|
if (repoKeys.length === 0) push("repos", "warn", "none configured \u2014 add one with `lobstah repos add <path>`");
|
|
15986
16152
|
for (const [key, repo] of Object.entries(cfg.repos)) {
|
|
15987
16153
|
const label = `repo ${key}`;
|
|
15988
|
-
if (!
|
|
16154
|
+
if (!fs68.existsSync(repo.path)) {
|
|
15989
16155
|
push(label, "fail", `path ${repo.path} does not exist`);
|
|
15990
16156
|
continue;
|
|
15991
16157
|
}
|
|
@@ -16016,11 +16182,11 @@ async function runDoctor(now2 = Date.now()) {
|
|
|
16016
16182
|
const trapNames2 = listTraps().map(trapLabel);
|
|
16017
16183
|
const parked = parkedSummary();
|
|
16018
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}` : ""}`;
|
|
16019
|
-
if (!
|
|
16185
|
+
if (!fs68.existsSync(hb)) {
|
|
16020
16186
|
push("daemon", "warn", `no heartbeat \u2014 daemon not running (\`lobstah daemon install\`); ${slots}`);
|
|
16021
16187
|
} else {
|
|
16022
16188
|
try {
|
|
16023
|
-
const payload = JSON.parse(
|
|
16189
|
+
const payload = JSON.parse(fs68.readFileSync(hb, "utf8"));
|
|
16024
16190
|
const age2 = now2 - new Date(payload.heartbeat ?? 0).getTime();
|
|
16025
16191
|
push(
|
|
16026
16192
|
"daemon",
|
|
@@ -16053,9 +16219,9 @@ async function runDoctor(now2 = Date.now()) {
|
|
|
16053
16219
|
push(disk.check, disk.status, disk.detail);
|
|
16054
16220
|
try {
|
|
16055
16221
|
const soaking = path58.join(lobstahHome(), "soaking");
|
|
16056
|
-
const stale =
|
|
16222
|
+
const stale = fs68.readdirSync(soaking).filter((f) => f.endsWith(".json")).filter((f) => {
|
|
16057
16223
|
try {
|
|
16058
|
-
return typeof JSON.parse(
|
|
16224
|
+
return typeof JSON.parse(fs68.readFileSync(path58.join(soaking, f), "utf8")).trapId !== "string";
|
|
16059
16225
|
} catch {
|
|
16060
16226
|
return true;
|
|
16061
16227
|
}
|
|
@@ -16083,7 +16249,7 @@ async function runDoctor(now2 = Date.now()) {
|
|
|
16083
16249
|
// apps/cli/src/glass.ts
|
|
16084
16250
|
init_stats2();
|
|
16085
16251
|
init_dist2();
|
|
16086
|
-
import * as
|
|
16252
|
+
import * as fs73 from "node:fs";
|
|
16087
16253
|
import * as os11 from "node:os";
|
|
16088
16254
|
import * as path62 from "node:path";
|
|
16089
16255
|
import * as http3 from "node:http";
|
|
@@ -16091,17 +16257,17 @@ import { randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual3 } from
|
|
|
16091
16257
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
16092
16258
|
|
|
16093
16259
|
// apps/cli/src/glass-page.generated.ts
|
|
16094
|
-
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';
|
|
16095
16261
|
|
|
16096
16262
|
// apps/cli/src/glass-snapshot-thread.ts
|
|
16097
16263
|
init_dist2();
|
|
16098
|
-
import * as
|
|
16264
|
+
import * as fs70 from "node:fs";
|
|
16099
16265
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
16100
16266
|
import { Worker } from "node:worker_threads";
|
|
16101
16267
|
function workerEntry() {
|
|
16102
16268
|
if (COMPILED_BINARY) return "./glass-snapshot-worker.ts";
|
|
16103
16269
|
const url = new URL("./glass-snapshot-worker.js", import.meta.url);
|
|
16104
|
-
return
|
|
16270
|
+
return fs70.existsSync(fileURLToPath2(url)) ? url : void 0;
|
|
16105
16271
|
}
|
|
16106
16272
|
function startSnapshotThread() {
|
|
16107
16273
|
const entry = workerEntry();
|
|
@@ -16288,7 +16454,7 @@ function pollBody(full, now2) {
|
|
|
16288
16454
|
|
|
16289
16455
|
// apps/cli/src/glass-presence.ts
|
|
16290
16456
|
init_dist2();
|
|
16291
|
-
import * as
|
|
16457
|
+
import * as fs71 from "node:fs";
|
|
16292
16458
|
import * as http2 from "node:http";
|
|
16293
16459
|
import * as path60 from "node:path";
|
|
16294
16460
|
import { randomBytes as randomBytes5, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
@@ -16371,8 +16537,8 @@ var SECRET_RE = /^[0-9a-f]{64}$/;
|
|
|
16371
16537
|
function readShowSecret() {
|
|
16372
16538
|
try {
|
|
16373
16539
|
const file2 = showSecretPath();
|
|
16374
|
-
if (process.platform !== "win32" && (
|
|
16375
|
-
const secret =
|
|
16540
|
+
if (process.platform !== "win32" && (fs71.statSync(file2).mode & 63) !== 0) return void 0;
|
|
16541
|
+
const secret = fs71.readFileSync(file2, "utf8").trim();
|
|
16376
16542
|
return SECRET_RE.test(secret) ? secret : void 0;
|
|
16377
16543
|
} catch {
|
|
16378
16544
|
return void 0;
|
|
@@ -16382,17 +16548,17 @@ function ensureShowSecret() {
|
|
|
16382
16548
|
const existing = readShowSecret();
|
|
16383
16549
|
if (existing) return existing;
|
|
16384
16550
|
const file2 = showSecretPath();
|
|
16385
|
-
|
|
16551
|
+
fs71.mkdirSync(path60.dirname(file2), { recursive: true });
|
|
16386
16552
|
const secret = randomBytes5(32).toString("hex");
|
|
16387
16553
|
try {
|
|
16388
|
-
|
|
16554
|
+
fs71.writeFileSync(file2, secret, { mode: 384, flag: "wx" });
|
|
16389
16555
|
} catch (e) {
|
|
16390
16556
|
if (e.code !== "EEXIST") throw e;
|
|
16391
16557
|
const raced = readShowSecret();
|
|
16392
16558
|
if (raced) return raced;
|
|
16393
|
-
|
|
16559
|
+
fs71.writeFileSync(file2, secret, { mode: 384 });
|
|
16394
16560
|
}
|
|
16395
|
-
|
|
16561
|
+
fs71.chmodSync(file2, 384);
|
|
16396
16562
|
return secret;
|
|
16397
16563
|
}
|
|
16398
16564
|
function showSecretMatches(supplied) {
|
|
@@ -16436,7 +16602,7 @@ function requestShow(port, hash, timeoutMs = 2e3) {
|
|
|
16436
16602
|
|
|
16437
16603
|
// apps/cli/src/focus.ts
|
|
16438
16604
|
init_dist2();
|
|
16439
|
-
import * as
|
|
16605
|
+
import * as fs72 from "node:fs";
|
|
16440
16606
|
import * as path61 from "node:path";
|
|
16441
16607
|
import { execFile as execFile2 } from "node:child_process";
|
|
16442
16608
|
import { promisify as promisify2 } from "node:util";
|
|
@@ -16489,7 +16655,7 @@ function safeWorktree(reg) {
|
|
|
16489
16655
|
const folder = reg.worktree;
|
|
16490
16656
|
if (!path61.isAbsolute(folder) || /[\x00-\x1f\x7f]/.test(folder) || path61.normalize(folder) !== folder) return false;
|
|
16491
16657
|
try {
|
|
16492
|
-
return
|
|
16658
|
+
return fs72.statSync(folder).isDirectory() && trapIdAt(folder) === reg.trapId;
|
|
16493
16659
|
} catch {
|
|
16494
16660
|
return false;
|
|
16495
16661
|
}
|
|
@@ -16574,25 +16740,26 @@ function requestBodyCap() {
|
|
|
16574
16740
|
}
|
|
16575
16741
|
var REQUEST_KIND_MAX_BYTES = /* @__PURE__ */ new Map([
|
|
16576
16742
|
["trap-request", 4096],
|
|
16577
|
-
["decision-viewed", 1024]
|
|
16743
|
+
["decision-viewed", 1024],
|
|
16744
|
+
["report-viewed", 1024]
|
|
16578
16745
|
]);
|
|
16579
16746
|
var readJson4 = (f) => {
|
|
16580
16747
|
try {
|
|
16581
|
-
return JSON.parse(
|
|
16748
|
+
return JSON.parse(fs73.readFileSync(f, "utf8"));
|
|
16582
16749
|
} catch {
|
|
16583
16750
|
return void 0;
|
|
16584
16751
|
}
|
|
16585
16752
|
};
|
|
16586
16753
|
var listDir = (d) => {
|
|
16587
16754
|
try {
|
|
16588
|
-
return
|
|
16755
|
+
return fs73.readdirSync(d);
|
|
16589
16756
|
} catch {
|
|
16590
16757
|
return [];
|
|
16591
16758
|
}
|
|
16592
16759
|
};
|
|
16593
16760
|
var mtime = (f) => {
|
|
16594
16761
|
try {
|
|
16595
|
-
return
|
|
16762
|
+
return fs73.statSync(f).mtimeMs;
|
|
16596
16763
|
} catch {
|
|
16597
16764
|
return 0;
|
|
16598
16765
|
}
|
|
@@ -16601,7 +16768,7 @@ function assetPath(name) {
|
|
|
16601
16768
|
for (const rel of [`../docs/assets/${name}`, `../../../docs/assets/${name}`]) {
|
|
16602
16769
|
try {
|
|
16603
16770
|
const p = fileURLToPath3(new URL(rel, import.meta.url));
|
|
16604
|
-
if (
|
|
16771
|
+
if (fs73.existsSync(p)) return p;
|
|
16605
16772
|
} catch {
|
|
16606
16773
|
}
|
|
16607
16774
|
}
|
|
@@ -16610,7 +16777,7 @@ function assetPath(name) {
|
|
|
16610
16777
|
function transcriptPath(harness, cwd, sessionId) {
|
|
16611
16778
|
if (harness !== "claude" || !cwd || !sessionId) return void 0;
|
|
16612
16779
|
const p = path62.join(os11.homedir(), ".claude", "projects", cwd.replace(/[^A-Za-z0-9-]/g, "-"), `${sessionId}.jsonl`);
|
|
16613
|
-
return
|
|
16780
|
+
return fs73.existsSync(p) ? p : void 0;
|
|
16614
16781
|
}
|
|
16615
16782
|
function messageAttachments(dir) {
|
|
16616
16783
|
return [dir, path62.join(dir, "handled")].flatMap(
|
|
@@ -16622,7 +16789,7 @@ function trapMessages(trapId) {
|
|
|
16622
16789
|
const parse2 = (file2, state) => {
|
|
16623
16790
|
let raw;
|
|
16624
16791
|
try {
|
|
16625
|
-
raw =
|
|
16792
|
+
raw = fs73.readFileSync(file2, "utf8");
|
|
16626
16793
|
} catch {
|
|
16627
16794
|
return void 0;
|
|
16628
16795
|
}
|
|
@@ -16712,7 +16879,7 @@ function dispatchRow(r, hold, staleSecs) {
|
|
|
16712
16879
|
claimedBy: claim?.by,
|
|
16713
16880
|
worker: evidence.worker,
|
|
16714
16881
|
log,
|
|
16715
|
-
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()),
|
|
16716
16883
|
...awaitingOf(id),
|
|
16717
16884
|
evidence: Object.keys(evidence).length > 0 ? evidence : void 0,
|
|
16718
16885
|
...r.bucket === "queued" ? {} : worktreeView(id, r.lane),
|
|
@@ -16805,7 +16972,7 @@ function serveReport(url, res) {
|
|
|
16805
16972
|
const type = file2 && REPORT_IMAGE_TYPES[path62.extname(file2).toLowerCase()];
|
|
16806
16973
|
if (!file2 || !type) return notFound(), true;
|
|
16807
16974
|
res.writeHead(200, { ...headers, "content-type": type });
|
|
16808
|
-
res.end(
|
|
16975
|
+
res.end(fs73.readFileSync(file2));
|
|
16809
16976
|
return true;
|
|
16810
16977
|
}
|
|
16811
16978
|
function trapNamesShown(traps, dispatches, attention, names) {
|
|
@@ -16848,7 +17015,7 @@ function serveAttachment(url, res) {
|
|
|
16848
17015
|
const type = file2 && REPORT_IMAGE_TYPES[path62.extname(file2).toLowerCase()];
|
|
16849
17016
|
let data;
|
|
16850
17017
|
try {
|
|
16851
|
-
if (file2 && type &&
|
|
17018
|
+
if (file2 && type && fs73.lstatSync(file2, { throwIfNoEntry: false })?.isFile()) data = fs73.readFileSync(file2);
|
|
16852
17019
|
} catch {
|
|
16853
17020
|
}
|
|
16854
17021
|
if (!data || !type) {
|
|
@@ -17119,6 +17286,16 @@ function serveGlass(port, options = {}) {
|
|
|
17119
17286
|
return reply(500, { ok: false, reason: "The view could not be stored." });
|
|
17120
17287
|
}
|
|
17121
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
|
+
}
|
|
17122
17299
|
case "trap-request": {
|
|
17123
17300
|
const error = trapRequestError(body.payload, Object.keys(loadConfig().repos));
|
|
17124
17301
|
if (error) return reply(400, { ok: false, reason: `Invalid trap request: ${error}.` });
|
|
@@ -17215,17 +17392,17 @@ function serveGlass(port, options = {}) {
|
|
|
17215
17392
|
}
|
|
17216
17393
|
if (req.url === "/lob.png" && lob) {
|
|
17217
17394
|
res.writeHead(200, { "content-type": "image/png", "cache-control": "no-cache" });
|
|
17218
|
-
res.end(
|
|
17395
|
+
res.end(fs73.readFileSync(lob));
|
|
17219
17396
|
} else if (req.url === "/star.png" && star) {
|
|
17220
17397
|
res.writeHead(200, { "content-type": "image/png", "cache-control": "no-cache" });
|
|
17221
|
-
res.end(
|
|
17398
|
+
res.end(fs73.readFileSync(star));
|
|
17222
17399
|
} else if (req.url === "/lob-sprite.png" && sprite) {
|
|
17223
17400
|
res.writeHead(200, { "content-type": "image/png", "cache-control": "no-cache" });
|
|
17224
|
-
res.end(
|
|
17401
|
+
res.end(fs73.readFileSync(sprite));
|
|
17225
17402
|
} else if (req.url === "/icon.png") {
|
|
17226
17403
|
if (icon) {
|
|
17227
17404
|
res.writeHead(200, { "content-type": "image/png", "cache-control": "no-cache" });
|
|
17228
|
-
res.end(
|
|
17405
|
+
res.end(fs73.readFileSync(icon));
|
|
17229
17406
|
} else {
|
|
17230
17407
|
res.writeHead(200, { "content-type": "image/svg+xml", "cache-control": "no-cache" });
|
|
17231
17408
|
res.end(fallbackIcon);
|
|
@@ -17245,7 +17422,12 @@ function serveGlass(port, options = {}) {
|
|
|
17245
17422
|
}
|
|
17246
17423
|
const row = key ? reportRow(key) : void 0;
|
|
17247
17424
|
if (m[2] === "/meta") {
|
|
17248
|
-
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
|
+
});
|
|
17249
17431
|
res.end(JSON.stringify(row ?? { error: "report not found" }));
|
|
17250
17432
|
return;
|
|
17251
17433
|
}
|
|
@@ -17349,12 +17531,12 @@ function serveGlass(port, options = {}) {
|
|
|
17349
17531
|
|
|
17350
17532
|
// apps/cli/src/park-renewal.ts
|
|
17351
17533
|
init_dist2();
|
|
17352
|
-
import * as
|
|
17534
|
+
import * as fs75 from "node:fs";
|
|
17353
17535
|
import * as path63 from "node:path";
|
|
17354
17536
|
|
|
17355
17537
|
// apps/cli/src/restart.ts
|
|
17356
17538
|
init_dist2();
|
|
17357
|
-
import * as
|
|
17539
|
+
import * as fs74 from "node:fs";
|
|
17358
17540
|
var RESTART_WAIT_MS = 1e4;
|
|
17359
17541
|
var HEARTBEAT_STALE_MS3 = 9e4;
|
|
17360
17542
|
var INSTALL_COMMAND = {
|
|
@@ -17378,7 +17560,7 @@ function restartRefusal(opts) {
|
|
|
17378
17560
|
}
|
|
17379
17561
|
function readHeartbeat() {
|
|
17380
17562
|
try {
|
|
17381
|
-
const hb = JSON.parse(
|
|
17563
|
+
const hb = JSON.parse(fs74.readFileSync(executorPath(), "utf8"));
|
|
17382
17564
|
return typeof hb.heartbeat === "string" ? hb : void 0;
|
|
17383
17565
|
} catch {
|
|
17384
17566
|
return void 0;
|
|
@@ -17439,7 +17621,7 @@ var RENEW_REASON = "lobstah: this trap is still signed on and its park timed out
|
|
|
17439
17621
|
var statePath = (trapId) => path63.join(lobstahHome(), "park-renewals", `${trapId}.json`);
|
|
17440
17622
|
function readState(trapId) {
|
|
17441
17623
|
try {
|
|
17442
|
-
const s = JSON.parse(
|
|
17624
|
+
const s = JSON.parse(fs75.readFileSync(statePath(trapId), "utf8"));
|
|
17443
17625
|
return { instant: Number.isFinite(s.instant) ? s.instant : 0, at: s.at };
|
|
17444
17626
|
} catch {
|
|
17445
17627
|
return { instant: 0, at: "" };
|
|
@@ -17447,11 +17629,11 @@ function readState(trapId) {
|
|
|
17447
17629
|
}
|
|
17448
17630
|
function writeState(trapId, state) {
|
|
17449
17631
|
const file2 = statePath(trapId);
|
|
17450
|
-
|
|
17451
|
-
|
|
17632
|
+
fs75.mkdirSync(path63.dirname(file2), { recursive: true });
|
|
17633
|
+
fs75.writeFileSync(file2, JSON.stringify(state));
|
|
17452
17634
|
}
|
|
17453
17635
|
function clearParkRenewal(trapId) {
|
|
17454
|
-
|
|
17636
|
+
fs75.rmSync(statePath(trapId), { force: true });
|
|
17455
17637
|
}
|
|
17456
17638
|
function daemonReachable(now2 = Date.now()) {
|
|
17457
17639
|
const at = Date.parse(readHeartbeat()?.heartbeat ?? "");
|
|
@@ -17489,7 +17671,7 @@ function parkRenewal(trapId, parkedMs, now2 = Date.now(), daemonUp = daemonReach
|
|
|
17489
17671
|
// apps/cli/src/repos.ts
|
|
17490
17672
|
init_dist();
|
|
17491
17673
|
init_dist2();
|
|
17492
|
-
import * as
|
|
17674
|
+
import * as fs76 from "node:fs";
|
|
17493
17675
|
import * as path64 from "node:path";
|
|
17494
17676
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
17495
17677
|
function git9(dir, ...args) {
|
|
@@ -17510,27 +17692,27 @@ function detectRepo(dir) {
|
|
|
17510
17692
|
if (git9(abs, "rev-parse", "--is-inside-work-tree") !== "true") return void 0;
|
|
17511
17693
|
const top = git9(abs, "rev-parse", "--show-toplevel");
|
|
17512
17694
|
const canon2 = (p) => {
|
|
17513
|
-
const real = path64.resolve(
|
|
17695
|
+
const real = path64.resolve(fs76.realpathSync.native(p));
|
|
17514
17696
|
return process.platform === "win32" ? real.toLowerCase() : real;
|
|
17515
17697
|
};
|
|
17516
17698
|
if (!top || canon2(top) !== canon2(abs)) return void 0;
|
|
17517
17699
|
const origin = git9(abs, "remote", "get-url", "origin");
|
|
17518
17700
|
const headRef = git9(abs, "symbolic-ref", "--short", "refs/remotes/origin/HEAD");
|
|
17519
17701
|
const trunk = headRef?.replace(/^origin\//, "") ?? git9(abs, "branch", "--show-current") ?? "main";
|
|
17520
|
-
const setup = LOCKFILE_SETUP.find(([f]) =>
|
|
17702
|
+
const setup = LOCKFILE_SETUP.find(([f]) => fs76.existsSync(path64.join(abs, f)))?.[1];
|
|
17521
17703
|
return { key: path64.basename(abs), path: abs, trunk, origin, setup };
|
|
17522
17704
|
}
|
|
17523
17705
|
function scanForRepos(roots) {
|
|
17524
17706
|
const found = [];
|
|
17525
17707
|
for (const root of roots) {
|
|
17526
17708
|
const abs = path64.resolve(root);
|
|
17527
|
-
if (!
|
|
17709
|
+
if (!fs76.existsSync(abs)) continue;
|
|
17528
17710
|
const self = detectRepo(abs);
|
|
17529
17711
|
if (self) {
|
|
17530
17712
|
found.push(self);
|
|
17531
17713
|
continue;
|
|
17532
17714
|
}
|
|
17533
|
-
for (const entry of
|
|
17715
|
+
for (const entry of fs76.readdirSync(abs, { withFileTypes: true })) {
|
|
17534
17716
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
17535
17717
|
const child = detectRepo(path64.join(abs, entry.name));
|
|
17536
17718
|
if (child) found.push(child);
|
|
@@ -17539,8 +17721,8 @@ function scanForRepos(roots) {
|
|
|
17539
17721
|
return found;
|
|
17540
17722
|
}
|
|
17541
17723
|
function configuredRepoKeys(file2 = configPath()) {
|
|
17542
|
-
if (!
|
|
17543
|
-
const raw = parse(
|
|
17724
|
+
if (!fs76.existsSync(file2)) return /* @__PURE__ */ new Set();
|
|
17725
|
+
const raw = parse(fs76.readFileSync(file2, "utf8"));
|
|
17544
17726
|
return new Set(Object.keys(raw.repos ?? {}));
|
|
17545
17727
|
}
|
|
17546
17728
|
function appendRepoBlock(repo, opts = {}, file2 = configPath()) {
|
|
@@ -17548,19 +17730,19 @@ function appendRepoBlock(repo, opts = {}, file2 = configPath()) {
|
|
|
17548
17730
|
if (repo.origin) lines.push(`origin = ${JSON.stringify(repo.origin)}`);
|
|
17549
17731
|
if (repo.setup) lines.push(`setup = [${repo.setup.map((s) => JSON.stringify(s)).join(", ")}]`);
|
|
17550
17732
|
if (opts.pickup) lines.push(`pickup = true`);
|
|
17551
|
-
|
|
17733
|
+
fs76.appendFileSync(file2, `${lines.join("\n")}
|
|
17552
17734
|
`);
|
|
17553
17735
|
}
|
|
17554
17736
|
|
|
17555
17737
|
// apps/cli/src/pr-watch.ts
|
|
17556
17738
|
init_dist();
|
|
17557
17739
|
init_dist2();
|
|
17558
|
-
import * as
|
|
17740
|
+
import * as fs78 from "node:fs";
|
|
17559
17741
|
|
|
17560
17742
|
// apps/cli/src/pr-stack-watch.ts
|
|
17561
17743
|
init_dist2();
|
|
17562
17744
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
17563
|
-
import * as
|
|
17745
|
+
import * as fs77 from "node:fs";
|
|
17564
17746
|
import * as path65 from "node:path";
|
|
17565
17747
|
var ghStackLink = (ref, branch, direction) => {
|
|
17566
17748
|
const res = spawnSync11(
|
|
@@ -17590,7 +17772,7 @@ function discoverPrStack(ref, opts = {}) {
|
|
|
17590
17772
|
const file2 = path65.join(lobstahHome(), "pr-stack-links.json");
|
|
17591
17773
|
let checked;
|
|
17592
17774
|
try {
|
|
17593
|
-
checked = JSON.parse(
|
|
17775
|
+
checked = JSON.parse(fs77.readFileSync(file2, "utf8"));
|
|
17594
17776
|
} catch {
|
|
17595
17777
|
checked = {};
|
|
17596
17778
|
}
|
|
@@ -17627,15 +17809,15 @@ function discoverPrStack(ref, opts = {}) {
|
|
|
17627
17809
|
}
|
|
17628
17810
|
}
|
|
17629
17811
|
const tmp = `${file2}.tmp-${process.pid}`;
|
|
17630
|
-
|
|
17631
|
-
|
|
17812
|
+
fs77.writeFileSync(tmp, JSON.stringify(checked));
|
|
17813
|
+
fs77.renameSync(tmp, file2);
|
|
17632
17814
|
}
|
|
17633
17815
|
|
|
17634
17816
|
// apps/cli/src/pr-watch.ts
|
|
17635
17817
|
var shq = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
|
|
17636
17818
|
function selfCommand() {
|
|
17637
17819
|
if (COMPILED_BINARY) return shq(process.execPath);
|
|
17638
|
-
const entry = process.argv[1] ?
|
|
17820
|
+
const entry = process.argv[1] ? fs78.realpathSync(process.argv[1]) : "lobstah";
|
|
17639
17821
|
return `${shq(process.execPath)} ${shq(entry)}`;
|
|
17640
17822
|
}
|
|
17641
17823
|
function prCheckCommand(ref, forId) {
|
|
@@ -17685,7 +17867,7 @@ function backfillPrWatches(opts = {}) {
|
|
|
17685
17867
|
for (const bucket of ["active", "done"]) {
|
|
17686
17868
|
let ids;
|
|
17687
17869
|
try {
|
|
17688
|
-
ids =
|
|
17870
|
+
ids = fs78.readdirSync(dirs[bucket]).filter((id) => !id.startsWith("."));
|
|
17689
17871
|
} catch {
|
|
17690
17872
|
continue;
|
|
17691
17873
|
}
|
|
@@ -17697,7 +17879,7 @@ function backfillPrWatches(opts = {}) {
|
|
|
17697
17879
|
}
|
|
17698
17880
|
let files;
|
|
17699
17881
|
try {
|
|
17700
|
-
files =
|
|
17882
|
+
files = fs78.readdirSync(dirs.state).filter((f) => f.endsWith(".evidence"));
|
|
17701
17883
|
} catch {
|
|
17702
17884
|
continue;
|
|
17703
17885
|
}
|
|
@@ -17759,7 +17941,7 @@ function backfillPrWatches(opts = {}) {
|
|
|
17759
17941
|
return rows;
|
|
17760
17942
|
}
|
|
17761
17943
|
function laneOf4(id) {
|
|
17762
|
-
return ["work", "chore"].find((l) =>
|
|
17944
|
+
return ["work", "chore"].find((l) => fs78.existsSync(evidencePath(id, l)) || readStatusLog(id, l).length > 0);
|
|
17763
17945
|
}
|
|
17764
17946
|
var FIRST_SIGHT_NOTICE_MS = 24 * 60 * 60 * 1e3;
|
|
17765
17947
|
function observePr(ref, view, opts = {}) {
|
|
@@ -17803,7 +17985,7 @@ function cancelQueuedRepairs(key) {
|
|
|
17803
17985
|
}
|
|
17804
17986
|
function rawConfig() {
|
|
17805
17987
|
try {
|
|
17806
|
-
return parse(
|
|
17988
|
+
return parse(fs78.readFileSync(configPath(), "utf8"));
|
|
17807
17989
|
} catch {
|
|
17808
17990
|
return {};
|
|
17809
17991
|
}
|
|
@@ -17978,7 +18160,7 @@ function finishResolvedWaits(log = () => {
|
|
|
17978
18160
|
// apps/cli/src/pr-stack.ts
|
|
17979
18161
|
init_dist2();
|
|
17980
18162
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
17981
|
-
import * as
|
|
18163
|
+
import * as fs79 from "node:fs";
|
|
17982
18164
|
import * as path66 from "node:path";
|
|
17983
18165
|
var defaultRun = (cmd, args, cwd) => {
|
|
17984
18166
|
const res = spawnSync12(cmd, args, { cwd, encoding: "utf8", timeout: cmd === "gh" ? 1e4 : 3e3, windowsHide: true });
|
|
@@ -17991,7 +18173,7 @@ function ghStackPrs(cwd, keys, run4) {
|
|
|
17991
18173
|
if (res.status !== 0 || !res.stdout.trim()) continue;
|
|
17992
18174
|
let state;
|
|
17993
18175
|
try {
|
|
17994
|
-
state = JSON.parse(
|
|
18176
|
+
state = JSON.parse(fs79.readFileSync(path66.join(path66.resolve(cwd, res.stdout.trim()), "gh-stack"), "utf8"));
|
|
17995
18177
|
} catch {
|
|
17996
18178
|
continue;
|
|
17997
18179
|
}
|
|
@@ -18352,7 +18534,7 @@ function batchText(results) {
|
|
|
18352
18534
|
// apps/cli/src/soak-worktree.ts
|
|
18353
18535
|
init_dist2();
|
|
18354
18536
|
init_dist3();
|
|
18355
|
-
import * as
|
|
18537
|
+
import * as fs80 from "node:fs";
|
|
18356
18538
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
18357
18539
|
function branchExists(repo, branch) {
|
|
18358
18540
|
return spawnSync13("git", ["-C", repo.path, "rev-parse", "--verify", "-q", `refs/heads/${branch}`], { stdio: "ignore" }).status === 0;
|
|
@@ -18372,7 +18554,7 @@ async function createSoakWorktree(opts) {
|
|
|
18372
18554
|
);
|
|
18373
18555
|
}
|
|
18374
18556
|
}
|
|
18375
|
-
const taken = (id2) => branchExists(opts.repo, `lobstah/soak-${id2}`) ||
|
|
18557
|
+
const taken = (id2) => branchExists(opts.repo, `lobstah/soak-${id2}`) || fs80.existsSync(worktreePath(`soak-${id2}`));
|
|
18376
18558
|
let trapId = opts.trapId ?? newTrapId();
|
|
18377
18559
|
if (opts.trapId !== void 0 && taken(trapId)) {
|
|
18378
18560
|
throw new Error(`could not create a worktree for repo ${opts.repoKey}: soak-${trapId} or its branch already exists`);
|
|
@@ -18397,7 +18579,7 @@ async function discardSoakWorktree(repo, made) {
|
|
|
18397
18579
|
// apps/cli/src/forget.ts
|
|
18398
18580
|
init_dist2();
|
|
18399
18581
|
init_dist3();
|
|
18400
|
-
import * as
|
|
18582
|
+
import * as fs81 from "node:fs";
|
|
18401
18583
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
18402
18584
|
var ForgetRefusedError = class extends Error {
|
|
18403
18585
|
};
|
|
@@ -18418,7 +18600,7 @@ async function forgetTrap(address, cfg, opts = {}) {
|
|
|
18418
18600
|
if (readReservation(entry.trapId)) throw new ForgetRefusedError(`${label} is starting (reserved) \u2014 withdraw it with \`lobstah stow --wt ${entry.name}\` first`);
|
|
18419
18601
|
const repo = entry.repo ? cfg.repos[entry.repo] : void 0;
|
|
18420
18602
|
const gitDir = entry.gitDir ?? (repo ? gitCommonDir(repo.path) : void 0);
|
|
18421
|
-
const present =
|
|
18603
|
+
const present = fs81.existsSync(entry.worktree);
|
|
18422
18604
|
const anchored = present && readTrapAnchor(entry.worktree)?.trapId === entry.trapId;
|
|
18423
18605
|
const unsaved = anchored ? unsavedFiles(entry.worktree) : [];
|
|
18424
18606
|
if (unsaved === void 0 && !opts.force) throw new ForgetRefusedError(`cannot read git status in ${entry.worktree} \u2014 inspect it, or pass --force`);
|
|
@@ -18465,7 +18647,7 @@ Push or merge it, or pass --force to forget the trap anyway.`);
|
|
|
18465
18647
|
throw new Error(`could not remove ${entry.worktree}: ${removal.reason} \u2014 the trap is not forgotten`);
|
|
18466
18648
|
}
|
|
18467
18649
|
} else {
|
|
18468
|
-
if (anchored)
|
|
18650
|
+
if (anchored) fs81.rmSync(`${entry.worktree}/${TRAP_ANCHOR_FILE}`, { force: true });
|
|
18469
18651
|
result.worktreeNote = "soak did not create it: left in place, its trap anchor removed";
|
|
18470
18652
|
}
|
|
18471
18653
|
}
|
|
@@ -18541,7 +18723,7 @@ async function setTerminalTitle(win, title, options = {}) {
|
|
|
18541
18723
|
|
|
18542
18724
|
// apps/cli/src/beat.ts
|
|
18543
18725
|
init_dist2();
|
|
18544
|
-
import * as
|
|
18726
|
+
import * as fs82 from "node:fs";
|
|
18545
18727
|
import * as path67 from "node:path";
|
|
18546
18728
|
|
|
18547
18729
|
// apps/cli/src/beat-pr.ts
|
|
@@ -18593,9 +18775,9 @@ function probeTrapPr(reg, opts = {}) {
|
|
|
18593
18775
|
function logError(err) {
|
|
18594
18776
|
try {
|
|
18595
18777
|
const dir = path67.join(lobstahHome(), "logs");
|
|
18596
|
-
|
|
18778
|
+
fs82.mkdirSync(dir, { recursive: true });
|
|
18597
18779
|
const msg = err instanceof Error ? err.message : String(err);
|
|
18598
|
-
|
|
18780
|
+
fs82.appendFileSync(path67.join(dir, "beat.log"), `${(/* @__PURE__ */ new Date()).toISOString()} ${msg.replace(/\s+/g, " ").slice(0, 300)}
|
|
18599
18781
|
`);
|
|
18600
18782
|
} catch {
|
|
18601
18783
|
}
|
|
@@ -19423,7 +19605,7 @@ function emitNotices(notices, sessionId) {
|
|
|
19423
19605
|
for (const n of notices) {
|
|
19424
19606
|
const answer = n.kind === "decision-answer" && n.refId ? readRequest(n.refId) : void 0;
|
|
19425
19607
|
if (answer) console.log(toonKV(decisionEventFields(answer)));
|
|
19426
|
-
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 }));
|
|
19427
19609
|
}
|
|
19428
19610
|
console.log(
|
|
19429
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.`
|
|
@@ -19441,7 +19623,7 @@ function noticeLine(n, prefix = "notice ") {
|
|
|
19441
19623
|
const answer = n.kind === "decision-answer" && n.refId ? readRequest(n.refId) : void 0;
|
|
19442
19624
|
if (answer) return decisionLine(answer);
|
|
19443
19625
|
const r = trapRequestFields(n);
|
|
19444
|
-
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}` : ""}`;
|
|
19445
19627
|
}
|
|
19446
19628
|
function replyLine(r) {
|
|
19447
19629
|
return `- reply ${r.id} (${r.entry.verb})${r.entry.note ? ` \u2014 ${r.entry.note}` : ""} \xB7 sent: ${r.sent}`;
|
|
@@ -19463,7 +19645,7 @@ function emitWatchAttention(attns, sessionId) {
|
|
|
19463
19645
|
function findLane(id) {
|
|
19464
19646
|
for (const lane of ["work", "chore"]) {
|
|
19465
19647
|
const d = laneDirs(lane);
|
|
19466
|
-
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`))) {
|
|
19467
19649
|
return lane;
|
|
19468
19650
|
}
|
|
19469
19651
|
}
|
|
@@ -19472,7 +19654,7 @@ function findLane(id) {
|
|
|
19472
19654
|
function anythingInFlight() {
|
|
19473
19655
|
const dispatches = ["work", "chore"].some((lane) => {
|
|
19474
19656
|
const dirs = laneDirs(lane);
|
|
19475
|
-
return [dirs.queue, dirs.active].some((dir) =>
|
|
19657
|
+
return [dirs.queue, dirs.active].some((dir) => fs87.readdirSync(dir).some((file2) => !file2.startsWith(".")));
|
|
19476
19658
|
});
|
|
19477
19659
|
return dispatches || listWatches().some((watch2) => watch2.owner === "man");
|
|
19478
19660
|
}
|
|
@@ -19489,7 +19671,7 @@ function dispatchChain(target) {
|
|
|
19489
19671
|
for (const lane of ["work", "chore"]) {
|
|
19490
19672
|
const dirs = laneDirs(lane);
|
|
19491
19673
|
for (const bucket of ["queue", "active", "done"]) {
|
|
19492
|
-
for (const entry of
|
|
19674
|
+
for (const entry of fs87.readdirSync(dirs[bucket])) {
|
|
19493
19675
|
if (entry.startsWith(".")) continue;
|
|
19494
19676
|
const id = bucket === "queue" ? entry.replace(/\.json$/, "") : entry;
|
|
19495
19677
|
const descriptor = storedDescriptor(id, lane);
|
|
@@ -19519,7 +19701,7 @@ function liveChainMember(member) {
|
|
|
19519
19701
|
}
|
|
19520
19702
|
if (isParked(member.id, member.lane)) return true;
|
|
19521
19703
|
try {
|
|
19522
|
-
const runner = JSON.parse(
|
|
19704
|
+
const runner = JSON.parse(fs87.readFileSync(path72.join(dir, "runner.json"), "utf8"));
|
|
19523
19705
|
return pidAlive2(runner.pid, runner.processStartTime);
|
|
19524
19706
|
} catch {
|
|
19525
19707
|
return false;
|
|
@@ -19528,7 +19710,7 @@ function liveChainMember(member) {
|
|
|
19528
19710
|
function rowsFor(lane, bucket) {
|
|
19529
19711
|
const dir = laneDirs(lane)[bucket];
|
|
19530
19712
|
const staleSecs = wedgeSecs2();
|
|
19531
|
-
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);
|
|
19532
19714
|
const sliced = bucket === "done" ? entries.slice(0, 10) : entries;
|
|
19533
19715
|
return sliced.map(({ f, m }) => {
|
|
19534
19716
|
const id = f.replace(/\.json$/, "");
|
|
@@ -19553,7 +19735,7 @@ function rowsFor(lane, bucket) {
|
|
|
19553
19735
|
function dispatchTrap(id, lane, names) {
|
|
19554
19736
|
const claimedBy = (bucket) => {
|
|
19555
19737
|
try {
|
|
19556
|
-
return JSON.parse(
|
|
19738
|
+
return JSON.parse(fs87.readFileSync(path72.join(laneDirs(lane)[bucket], id, "claim.json"), "utf8")).by;
|
|
19557
19739
|
} catch {
|
|
19558
19740
|
return void 0;
|
|
19559
19741
|
}
|
|
@@ -19705,7 +19887,7 @@ ${usageFor(cmd)}`);
|
|
|
19705
19887
|
const d = {
|
|
19706
19888
|
id: opt("--id") ?? randomUUID8(),
|
|
19707
19889
|
repo,
|
|
19708
|
-
brief: briefText ??
|
|
19890
|
+
brief: briefText ?? fs87.readFileSync(briefFile, "utf8"),
|
|
19709
19891
|
harness: opt("--harness"),
|
|
19710
19892
|
model: opt("--model"),
|
|
19711
19893
|
effort: opt("--effort"),
|
|
@@ -19762,7 +19944,7 @@ ${usageFor(cmd)}`);
|
|
|
19762
19944
|
const since = queuedAt(id, lane);
|
|
19763
19945
|
const claimedAt = claim?.at;
|
|
19764
19946
|
const state = displayState({ log, lastEventAt: lastEventAt(id, lane), queued: since !== void 0, claimedAt });
|
|
19765
|
-
const live =
|
|
19947
|
+
const live = fs87.existsSync(path72.join(laneDirs(lane).active, id));
|
|
19766
19948
|
const activity = live ? activityView(readActivity(id, lane), wedgeSecs2()) : void 0;
|
|
19767
19949
|
const waitingNow = live ? waitingView(log.at(-1)) : void 0;
|
|
19768
19950
|
console.log(
|
|
@@ -19802,8 +19984,8 @@ ${usageFor(cmd)}`);
|
|
|
19802
19984
|
if (!id) throw new Error("logs requires a dispatch id");
|
|
19803
19985
|
const lane = findLane(id);
|
|
19804
19986
|
const file2 = eventsPath(id, lane);
|
|
19805
|
-
if (
|
|
19806
|
-
const raw =
|
|
19987
|
+
if (fs87.existsSync(file2)) {
|
|
19988
|
+
const raw = fs87.readFileSync(file2, "utf8");
|
|
19807
19989
|
const lines = raw.split("\n").filter((l) => l.length > 0);
|
|
19808
19990
|
const LIMIT = 50;
|
|
19809
19991
|
if (!has("--full") && lines.length > LIMIT) {
|
|
@@ -19814,15 +19996,15 @@ ${usageFor(cmd)}`);
|
|
|
19814
19996
|
}
|
|
19815
19997
|
}
|
|
19816
19998
|
if (has("--follow")) {
|
|
19817
|
-
let size =
|
|
19999
|
+
let size = fs87.existsSync(file2) ? fs87.statSync(file2).size : 0;
|
|
19818
20000
|
setInterval(() => {
|
|
19819
|
-
if (!
|
|
19820
|
-
const now2 =
|
|
20001
|
+
if (!fs87.existsSync(file2)) return;
|
|
20002
|
+
const now2 = fs87.statSync(file2).size;
|
|
19821
20003
|
if (now2 > size) {
|
|
19822
|
-
const fd =
|
|
20004
|
+
const fd = fs87.openSync(file2, "r");
|
|
19823
20005
|
const buf = Buffer.alloc(now2 - size);
|
|
19824
|
-
|
|
19825
|
-
|
|
20006
|
+
fs87.readSync(fd, buf, 0, buf.length, size);
|
|
20007
|
+
fs87.closeSync(fd);
|
|
19826
20008
|
process.stdout.write(buf.toString("utf8"));
|
|
19827
20009
|
size = now2;
|
|
19828
20010
|
}
|
|
@@ -19920,7 +20102,7 @@ ${[text, block].filter(Boolean).join("\n\n")}`,
|
|
|
19920
20102
|
const claimFile = path72.join(laneDirs(origin.lane)[origin.bucket], origin.id, "claim.json");
|
|
19921
20103
|
let claimedBy;
|
|
19922
20104
|
try {
|
|
19923
|
-
claimedBy = JSON.parse(
|
|
20105
|
+
claimedBy = JSON.parse(fs87.readFileSync(claimFile, "utf8")).by;
|
|
19924
20106
|
} catch {
|
|
19925
20107
|
claimedBy = readEvidence(origin.id, origin.lane).deliveredTo;
|
|
19926
20108
|
}
|
|
@@ -20004,7 +20186,7 @@ ${usageFor("report")}`);
|
|
|
20004
20186
|
if (verb === "done") {
|
|
20005
20187
|
const claim = readSessionClaim(id, lane);
|
|
20006
20188
|
let ownCheckout = false;
|
|
20007
|
-
if (claim?.by.startsWith("wt:") && claim.worktree &&
|
|
20189
|
+
if (claim?.by.startsWith("wt:") && claim.worktree && fs87.existsSync(claim.worktree)) {
|
|
20008
20190
|
const rel = path72.relative(canon(claim.worktree), canon(process.cwd()));
|
|
20009
20191
|
const inside = rel === "" || !rel.startsWith("..") && !path72.isAbsolute(rel);
|
|
20010
20192
|
const who = callerSession(opt("--session"))?.id;
|
|
@@ -20026,7 +20208,7 @@ ${usageFor("report")}`);
|
|
|
20026
20208
|
const branches = [.../* @__PURE__ */ new Set([ev.branch, ...(ev.pushes ?? []).map((p) => p.branch)])].filter(
|
|
20027
20209
|
(b) => !!b && b !== "HEAD"
|
|
20028
20210
|
);
|
|
20029
|
-
reportPrs = stackPrUrls(reportedPrs, { cwd: worktree &&
|
|
20211
|
+
reportPrs = stackPrUrls(reportedPrs, { cwd: worktree && fs87.existsSync(worktree) ? worktree : process.cwd(), branches });
|
|
20030
20212
|
const all = dispatchPrUrls({ prUrl, prUrls: [...reportPrs, ...dispatchPrUrls(ev)] });
|
|
20031
20213
|
mergeEvidence(id, lane, { prUrl, ...all.length > 1 ? { prUrls: all } : {} });
|
|
20032
20214
|
}
|
|
@@ -20092,7 +20274,7 @@ ${usageFor("report")}`);
|
|
|
20092
20274
|
);
|
|
20093
20275
|
}
|
|
20094
20276
|
const worktree = dispatchWorktree(id, lane).path;
|
|
20095
|
-
const cwd =
|
|
20277
|
+
const cwd = fs87.existsSync(worktree) ? worktree : process.cwd();
|
|
20096
20278
|
const invocation = harness === "codex" ? codexInvocation(["resume", sessionId]) : { file: "claude", argv: ["--resume", sessionId] };
|
|
20097
20279
|
if (!invocation) throw new Error("no codex CLI found \u2014 neither on PATH nor vendored by @openai/codex-sdk");
|
|
20098
20280
|
if (has("--print")) {
|
|
@@ -20111,9 +20293,9 @@ ${usageFor("report")}`);
|
|
|
20111
20293
|
}
|
|
20112
20294
|
const lane = findLane(id);
|
|
20113
20295
|
const activeDir = path72.join(laneDirs(lane).active, id);
|
|
20114
|
-
if (!
|
|
20296
|
+
if (!fs87.existsSync(activeDir)) throw new Error(`${id} is not active \u2014 swap only applies to in-flight dispatches`);
|
|
20115
20297
|
const descFile = path72.join(activeDir, "descriptor.json");
|
|
20116
|
-
const descriptor = JSON.parse(
|
|
20298
|
+
const descriptor = JSON.parse(fs87.readFileSync(descFile, "utf8"));
|
|
20117
20299
|
const fromHarness = resolveSessionHarness(id, loadConfig(), lane).harness ?? descriptor.harness ?? "default";
|
|
20118
20300
|
for (const key of ["harness", "model", "effort"]) {
|
|
20119
20301
|
const v = opt(`--${key}`);
|
|
@@ -20121,14 +20303,14 @@ ${usageFor("report")}`);
|
|
|
20121
20303
|
}
|
|
20122
20304
|
if (opt("--harness")) descriptor.harnessExplicit = true;
|
|
20123
20305
|
if (opt("--model")) descriptor.modelExplicit = true;
|
|
20124
|
-
|
|
20306
|
+
fs87.writeFileSync(descFile, JSON.stringify(descriptor, null, 2));
|
|
20125
20307
|
const worktree = dispatchWorktree(id, lane).path;
|
|
20126
|
-
|
|
20308
|
+
fs87.writeFileSync(path72.join(activeDir, "handoff"), handoffNote(fromHarness, worktreeProgress(worktree)));
|
|
20127
20309
|
const runnerFile = path72.join(activeDir, "runner.json");
|
|
20128
|
-
if (
|
|
20129
|
-
const runner = JSON.parse(
|
|
20310
|
+
if (fs87.existsSync(runnerFile)) {
|
|
20311
|
+
const runner = JSON.parse(fs87.readFileSync(runnerFile, "utf8"));
|
|
20130
20312
|
if (pidAlive2(runner.pid, runner.processStartTime)) killGroup(runner.pid, "SIGKILL");
|
|
20131
|
-
|
|
20313
|
+
fs87.unlinkSync(runnerFile);
|
|
20132
20314
|
}
|
|
20133
20315
|
appendStatus(id, lane, "working", `swapped to ${descriptor.harness ?? "default"} \u2014 awaiting respawn`);
|
|
20134
20316
|
console.log(toonKV({ id, harness: descriptor.harness ?? "default", model: descriptor.model, swapped: true }));
|
|
@@ -20189,7 +20371,7 @@ ${usageFor("report")}`);
|
|
|
20189
20371
|
console.log(`commits[${ev.commits.length}]:`);
|
|
20190
20372
|
for (const c of ev.commits) console.log(` ${c}`);
|
|
20191
20373
|
}
|
|
20192
|
-
if (
|
|
20374
|
+
if (fs87.existsSync(path72.join(laneDirs(lane).done, id))) {
|
|
20193
20375
|
console.log(toonHelp([`lobstah send ${id} "<instruction>" (wakes it as a follow-up in its worktree)`]));
|
|
20194
20376
|
}
|
|
20195
20377
|
const attachments = storedDescriptor(id, lane)?.attachments ?? [];
|
|
@@ -20729,7 +20911,7 @@ ${errs.map((e) => `- ${e}`).join("\n")}`);
|
|
|
20729
20911
|
const keys = holdCancelledRepair(id);
|
|
20730
20912
|
return keys.length > 0 ? { repairHeld: `${keys.join(", ")} (lobstah watch release <key> frees it)` } : {};
|
|
20731
20913
|
};
|
|
20732
|
-
if (
|
|
20914
|
+
if (fs87.existsSync(path72.join(laneDirs(lane).active, id))) {
|
|
20733
20915
|
requestCancel(id, lane);
|
|
20734
20916
|
console.log(toonKV({ id, cancel: "requested", note: "the claimant (daemon or trap) winds it down at its next check", ...repairHeld() }));
|
|
20735
20917
|
break;
|
|
@@ -20738,7 +20920,7 @@ ${errs.map((e) => `- ${e}`).join("\n")}`);
|
|
|
20738
20920
|
console.log(toonKV({ id, cancel: "finalized", note: "cancelled before claim \u2014 recorded as failed in done/", ...repairHeld() }));
|
|
20739
20921
|
break;
|
|
20740
20922
|
}
|
|
20741
|
-
if (
|
|
20923
|
+
if (fs87.existsSync(path72.join(laneDirs(lane).active, id))) {
|
|
20742
20924
|
requestCancel(id, lane);
|
|
20743
20925
|
console.log(toonKV({ id, cancel: "requested", note: "claimed while cancelling \u2014 the claimant winds it down", ...repairHeld() }));
|
|
20744
20926
|
break;
|
|
@@ -20865,17 +21047,17 @@ ${usageFor("man:wait")}`);
|
|
|
20865
21047
|
const file2 = has("--global") ? path72.join(os13.homedir(), ".claude", "settings.json") : path72.join(".claude", has("--shared") ? "settings.json" : "settings.local.json");
|
|
20866
21048
|
let existing;
|
|
20867
21049
|
try {
|
|
20868
|
-
existing = JSON.parse(
|
|
21050
|
+
existing = JSON.parse(fs87.readFileSync(file2, "utf8"));
|
|
20869
21051
|
} catch {
|
|
20870
21052
|
existing = void 0;
|
|
20871
21053
|
}
|
|
20872
21054
|
const { settings, changed } = mergeHaulHook(existing);
|
|
20873
21055
|
if (changed) {
|
|
20874
|
-
|
|
20875
|
-
|
|
21056
|
+
fs87.mkdirSync(path72.dirname(file2), { recursive: true });
|
|
21057
|
+
fs87.writeFileSync(file2, `${JSON.stringify(settings, null, 2)}
|
|
20876
21058
|
`);
|
|
20877
21059
|
}
|
|
20878
|
-
if (has("--marker"))
|
|
21060
|
+
if (has("--marker")) fs87.writeFileSync(".lobstah-man", "");
|
|
20879
21061
|
console.log(
|
|
20880
21062
|
toonKV({
|
|
20881
21063
|
hook: "lobstah man haul",
|
|
@@ -20933,7 +21115,7 @@ ${usageFor("man:wait")}`);
|
|
|
20933
21115
|
if (helm) releaseHeldQuestions(helm);
|
|
20934
21116
|
const cfgHaul = loadConfig();
|
|
20935
21117
|
if (!helm && liveHelms(cfgHaul.helm.ttlSecs * 1e3).length > 0) break;
|
|
20936
|
-
if (!helm && process.env.LOBSTAH_MAN !== "1" && !
|
|
21118
|
+
if (!helm && process.env.LOBSTAH_MAN !== "1" && !fs87.existsSync(".lobstah-man")) break;
|
|
20937
21119
|
const dueDigest = () => helm ? dueHelmDigest(helm, cfgHaul.helm.reportSecs) : void 0;
|
|
20938
21120
|
const blockDigest = (d) => {
|
|
20939
21121
|
advanceCursor(helm.grounds, d.now);
|
|
@@ -21143,7 +21325,7 @@ ${behind}` : "");
|
|
|
21143
21325
|
}
|
|
21144
21326
|
repoKey2 = reservation.repo;
|
|
21145
21327
|
if (reservation.worktree !== void 0) {
|
|
21146
|
-
if (!
|
|
21328
|
+
if (!fs87.existsSync(reservation.worktree) || trapIdAt(reservation.worktree) !== reservation.trapId) {
|
|
21147
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`);
|
|
21148
21330
|
}
|
|
21149
21331
|
worktree = canon(reservation.worktree);
|
|
@@ -21182,7 +21364,7 @@ ${behind}` : "");
|
|
|
21182
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}.`
|
|
21183
21365
|
);
|
|
21184
21366
|
}
|
|
21185
|
-
if (!
|
|
21367
|
+
if (!fs87.existsSync(mine.worktree)) {
|
|
21186
21368
|
throw new Error(
|
|
21187
21369
|
`this session's trap wt:${mine.trapId} is anchored at ${mine.worktree}, which no longer exists. Run \`lobstah stow\`, then soak again.`
|
|
21188
21370
|
);
|
|
@@ -21366,7 +21548,7 @@ ${usageFor("stow")}`);
|
|
|
21366
21548
|
const anchor = wtDir !== void 0 ? readTrapAnchor(wtDir) : void 0;
|
|
21367
21549
|
const created = reg?.createdWorktree === true || anchor?.createdBy === "soak";
|
|
21368
21550
|
let worktreeOut = {};
|
|
21369
|
-
if (wtDir !== void 0 &&
|
|
21551
|
+
if (wtDir !== void 0 && fs87.existsSync(wtDir)) {
|
|
21370
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;
|
|
21371
21553
|
if (keepReason) {
|
|
21372
21554
|
worktreeOut = { worktree: "kept", path: wtDir, reason: keepReason };
|
|
@@ -21613,7 +21795,7 @@ ${usageFor("pet")}`);
|
|
|
21613
21795
|
path: r.path,
|
|
21614
21796
|
trunk: r.trunk,
|
|
21615
21797
|
pickup: r.pickup ?? false,
|
|
21616
|
-
exists:
|
|
21798
|
+
exists: fs87.existsSync(r.path)
|
|
21617
21799
|
}));
|
|
21618
21800
|
console.log(toonTable("repos", rows, ["key", "path", "trunk", "pickup", "exists"]));
|
|
21619
21801
|
break;
|
|
@@ -21731,7 +21913,7 @@ ${usageFor("pet")}`);
|
|
|
21731
21913
|
break;
|
|
21732
21914
|
}
|
|
21733
21915
|
case "init": {
|
|
21734
|
-
if (!
|
|
21916
|
+
if (!fs87.existsSync(configPath())) {
|
|
21735
21917
|
const exampleRepo = has("--scan") ? "" : `[repos.example]
|
|
21736
21918
|
path = "~/src/example"
|
|
21737
21919
|
trunk = "main"
|
|
@@ -21740,7 +21922,7 @@ trunk = "main"
|
|
|
21740
21922
|
# pickup = true # opt into [pickup.github] multi-repo tracker pickup
|
|
21741
21923
|
|
|
21742
21924
|
`;
|
|
21743
|
-
|
|
21925
|
+
fs87.writeFileSync(
|
|
21744
21926
|
configPath(),
|
|
21745
21927
|
`# lobstah workspace definitions \u2014 the descriptor's repo key resolves here.
|
|
21746
21928
|
# Top-level keys must come before any [section].
|
|
@@ -21778,7 +21960,7 @@ wallClockSecs = 3600
|
|
|
21778
21960
|
let reference = "https://github.com/aequitas-labs/lobstah/blob/main/docs/configuration.md";
|
|
21779
21961
|
try {
|
|
21780
21962
|
const local = fileURLToPath6(new URL("../docs/configuration.md", import.meta.url));
|
|
21781
|
-
if (
|
|
21963
|
+
if (fs87.existsSync(local)) reference = local;
|
|
21782
21964
|
} catch {
|
|
21783
21965
|
}
|
|
21784
21966
|
console.log(
|