@davidbalzan/groundwork-console 0.1.12 → 0.1.14
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/bus.js +54 -3
- package/dist/events.js +1 -1
- package/dist/gitinfo.js +56 -2
- package/dist/seam.js +27 -10
- package/dist/seamwrite.js +5 -1
- package/dist/server.js +10 -10
- package/dist/tmux.js +1 -1
- package/package.json +8 -4
- package/public/assets/{abnfDiagram-N423BO3Z-C7_Zpec2.js → abnfDiagram-N423BO3Z-BSvB33qO.js} +1 -1
- package/public/assets/{arc-Bbq8pKhn.js → arc-zFmco7vQ.js} +1 -1
- package/public/assets/architecture-TIHT7OUA-BQFAhsKM.js +1 -0
- package/public/assets/{architectureDiagram-T3A2C74G-tEkFMAHO.js → architectureDiagram-T3A2C74G-Dq8B7QSd.js} +1 -1
- package/public/assets/{blockDiagram-VBNYF7ZC-CZNrU4_z.js → blockDiagram-VBNYF7ZC-Dyx-2MxL.js} +1 -1
- package/public/assets/{c4Diagram-5PPSVZJV-D1-T-b--.js → c4Diagram-5PPSVZJV-BcQgTpcV.js} +1 -1
- package/public/assets/channel-BBOBumsc.js +1 -0
- package/public/assets/{chunk-2GRJ4B5K-DMzvnmxY.js → chunk-2GRJ4B5K-0LJiHhVt.js} +1 -1
- package/public/assets/{chunk-4I5QYGJK-D03m5a1e.js → chunk-4I5QYGJK-DU9jcgy8.js} +1 -1
- package/public/assets/{chunk-5RXB4S5H-B5SxHfnz.js → chunk-5RXB4S5H-Dbb9fQEh.js} +1 -1
- package/public/assets/{chunk-6Q2QTUOP-BQYOf-YY.js → chunk-6Q2QTUOP-BEP7PF5q.js} +1 -1
- package/public/assets/{chunk-7Z6QIM7H-DGtHjZJL.js → chunk-7Z6QIM7H-BDULl8ao.js} +1 -1
- package/public/assets/{chunk-GF5L2VYU-6_YTDzGo.js → chunk-GF5L2VYU-CKmEMSg9.js} +1 -1
- package/public/assets/{chunk-I66GZJ75-CfWGFqqO.js → chunk-I66GZJ75-C8VZHfLH.js} +1 -1
- package/public/assets/{chunk-JQJVKLGR-DrCjH1AL.js → chunk-JQJVKLGR-DM2yoV15.js} +1 -1
- package/public/assets/{chunk-KBJHAD2P-DovMVsOt.js → chunk-KBJHAD2P-BRAKI2QY.js} +1 -1
- package/public/assets/{chunk-NSK5VX7P-qSMdOab2.js → chunk-NSK5VX7P-DV7uo6bZ.js} +1 -1
- package/public/assets/{chunk-QR6OTTB3-Iraem5u-.js → chunk-QR6OTTB3-CIKpM1Nr.js} +1 -1
- package/public/assets/{chunk-RYQCIY6F-8rrSNAgX.js → chunk-RYQCIY6F-DrSpjyPL.js} +1 -1
- package/public/assets/{chunk-UBXNYLIW-CyzK1RD5.js → chunk-UBXNYLIW-CKJQ1y-l.js} +1 -1
- package/public/assets/{chunk-W5SLKNZC-BVK4MUw1.js → chunk-W5SLKNZC-BrvtgZ7x.js} +1 -1
- package/public/assets/{chunk-WRU74C26-C_K_UiUW.js → chunk-WRU74C26-D5OxzQZ_.js} +1 -1
- package/public/assets/{chunk-XXDRQBXY-0nuSRCEm.js → chunk-XXDRQBXY-B7N0mEcW.js} +1 -1
- package/public/assets/classDiagram-JCYQIIEL-g9vVH4Bz.js +1 -0
- package/public/assets/classDiagram-v2-OCEON4UE-g9vVH4Bz.js +1 -0
- package/public/assets/{cose-bilkent-JH36ORCC-R9H6hDjY.js → cose-bilkent-JH36ORCC-B4AeOPAc.js} +1 -1
- package/public/assets/{cynefin-VYW2F7L2-DnWlTifu.js → cynefin-VYW2F7L2-DzOwlGVY.js} +1 -1
- package/public/assets/{cynefinDiagram-MW4NZA55-B-7owUyc.js → cynefinDiagram-MW4NZA55-SA4aXkpc.js} +1 -1
- package/public/assets/{dagre-VZM6K2ZE-B3ZoPe5S.js → dagre-VZM6K2ZE-C5ltS5m2.js} +1 -1
- package/public/assets/{diagram-7IWD3JNH-BI26fzwd.js → diagram-7IWD3JNH-TU7uqjDX.js} +1 -1
- package/public/assets/{diagram-B4RE2ZJO-BiSCmkOd.js → diagram-B4RE2ZJO-HCe8Z_RD.js} +1 -1
- package/public/assets/{diagram-LBJQPF4R-BQ35r2wn.js → diagram-LBJQPF4R-9kRxXqkG.js} +1 -1
- package/public/assets/{diagram-Q27KOJAE-258JzS88.js → diagram-Q27KOJAE-DfLFPFvB.js} +1 -1
- package/public/assets/{diagram-UB23O5K3-BQLuDWQ5.js → diagram-UB23O5K3-BUpWSI8L.js} +1 -1
- package/public/assets/{dist-BhEZ2d35.js → dist-By9Jxne0.js} +1 -1
- package/public/assets/{ebnfDiagram-BXEA7PRR-D6cGU6FA.js → ebnfDiagram-BXEA7PRR-Dz83w-0Y.js} +1 -1
- package/public/assets/{erDiagram-JOGREHBK-x-fYPfbp.js → erDiagram-JOGREHBK-DHZjXCza.js} +1 -1
- package/public/assets/eventmodeling-45OFAUF4-UQEvaa-G.js +1 -0
- package/public/assets/flowDiagram-UKHOOZJN-D3oCUrRO.js +1 -0
- package/public/assets/{ganttDiagram-PKOTCBZU-B44E_1OW.js → ganttDiagram-PKOTCBZU-B3tcBB-9.js} +1 -1
- package/public/assets/{gitGraph-TEB2WS4Q-B_J5fmci.js → gitGraph-TEB2WS4Q-BQKfiZNh.js} +1 -1
- package/public/assets/{gitGraphDiagram-DS77QQ5N-oi1EtSGf.js → gitGraphDiagram-DS77QQ5N-BM480xTb.js} +1 -1
- package/public/assets/{index-D4kIibeU.js → index-Crr1PM3T.js} +11 -11
- package/public/assets/{info-DKCQHKI2-CvqAVyoT.js → info-DKCQHKI2-DW33_uuk.js} +1 -1
- package/public/assets/{infoDiagram-6WML65LV-BroSi0D-.js → infoDiagram-6WML65LV-BeewbX8V.js} +1 -1
- package/public/assets/{ishikawaDiagram-WSZJBQD7-BRQ47rb2.js → ishikawaDiagram-WSZJBQD7-DcWSwP2B.js} +1 -1
- package/public/assets/{journeyDiagram-NVQOT4AX-DB6Xo-9Q.js → journeyDiagram-NVQOT4AX-BxT4Ea_S.js} +1 -1
- package/public/assets/{kanban-definition-27J2QSJJ-BBzsco6z.js → kanban-definition-27J2QSJJ-CEvqnKgz.js} +1 -1
- package/public/assets/{line-m55PflQc.js → line-DrJMZj-O.js} +1 -1
- package/public/assets/{linear-Dgtz6Nrl.js → linear-cUeO7N7d.js} +1 -1
- package/public/assets/{mermaid-parser.core-B98Rk-oq.js → mermaid-parser.core-CIHTTkEa.js} +3 -3
- package/public/assets/{mermaid.core-CqRCrVqJ.js → mermaid.core-Cc5OhPpa.js} +4 -4
- package/public/assets/{mindmap-definition-FAOFIHXS-BPP33G4r.js → mindmap-definition-FAOFIHXS-DsHkrrK_.js} +1 -1
- package/public/assets/{packet-7NZHBO7P-tO4S6ouz.js → packet-7NZHBO7P-C_T3uLvX.js} +1 -1
- package/public/assets/{pegDiagram-VL7TDLO6-Bw6mp9l7.js → pegDiagram-VL7TDLO6-CBdlvXSc.js} +1 -1
- package/public/assets/{pie-RZYD4A2V-C437Vvln.js → pie-RZYD4A2V-CpetmYJU.js} +1 -1
- package/public/assets/{pieDiagram-7S7Q4E2Y-CBxtUqZy.js → pieDiagram-7S7Q4E2Y-CT3wi9zO.js} +1 -1
- package/public/assets/{quadrantDiagram-CIZ2JOQS-Bqsvmpf9.js → quadrantDiagram-CIZ2JOQS-Vuywai3l.js} +1 -1
- package/public/assets/{radar-I7S5WNFK-C6dNsRn9.js → radar-I7S5WNFK-rNDTWylh.js} +1 -1
- package/public/assets/{railroad-3IZDKUUU-KaVebqGG.js → railroad-3IZDKUUU-B09wa8N5.js} +1 -1
- package/public/assets/railroad-abnf-AHOZXSZD-DPdRoDGL.js +1 -0
- package/public/assets/railroad-ebnf-EBAXGLYW-BWVVmeh0.js +1 -0
- package/public/assets/railroad-peg-LSFZ7HO6-CFlw0fDo.js +1 -0
- package/public/assets/{railroadDiagram-AXF67PYL-DKq4X6Uj.js → railroadDiagram-AXF67PYL-DxwqeTls.js} +1 -1
- package/public/assets/{requirementDiagram-LRYGKXZP-B9fsvyjq.js → requirementDiagram-LRYGKXZP-UGxNbVIj.js} +1 -1
- package/public/assets/{sankeyDiagram-W5VNT64P-BsfVDMI2.js → sankeyDiagram-W5VNT64P-DpR73q2C.js} +1 -1
- package/public/assets/{sequenceDiagram-SI44F4Z6-OgERYEKg.js → sequenceDiagram-SI44F4Z6-xvxxzFmu.js} +1 -1
- package/public/assets/{src-fnYA6irH.js → src-eh414mKr.js} +1 -1
- package/public/assets/{stateDiagram-OKZ733FA-CQbkYnuQ.js → stateDiagram-OKZ733FA-Cqt3c052.js} +1 -1
- package/public/assets/stateDiagram-v2-UEYNNEHI-toMIf4-z.js +1 -0
- package/public/assets/{swimlanes-SLNWSIFB-Bjko0riO.js → swimlanes-SLNWSIFB-BJknsAAA.js} +1 -1
- package/public/assets/swimlanesDiagram-ULZ7WXOC-BF4o5mrv.js +8 -0
- package/public/assets/{timeline-definition-Z64GVDOM-yK0hLw8V.js → timeline-definition-Z64GVDOM-Dkcqv4tL.js} +1 -1
- package/public/assets/{treeView-QDETBFTQ-BGpcfDZf.js → treeView-QDETBFTQ-C1uzwCFh.js} +1 -1
- package/public/assets/{treemap-6X3UGDF4-QT7YcofM.js → treemap-6X3UGDF4-C59GIx2E.js} +1 -1
- package/public/assets/{vennDiagram-T6HMQDX7-BuWvWEXb.js → vennDiagram-T6HMQDX7-Cd3Zvctp.js} +1 -1
- package/public/assets/{wardley-OPB4EBWU-hYpU_cIv.js → wardley-OPB4EBWU-D3P85e8V.js} +1 -1
- package/public/assets/{wardleyDiagram-T6FBY63Y-CgKHf2Kc.js → wardleyDiagram-T6FBY63Y-BLX8xtsj.js} +1 -1
- package/public/assets/{xychartDiagram-ELKLHX3M-CoNyQg1m.js → xychartDiagram-ELKLHX3M-CUfF0x4C.js} +1 -1
- package/public/index.html +1 -1
- package/public/assets/architecture-TIHT7OUA-CJFVglth.js +0 -1
- package/public/assets/channel-ClF6sQKu.js +0 -1
- package/public/assets/classDiagram-JCYQIIEL-z-TVRcqJ.js +0 -1
- package/public/assets/classDiagram-v2-OCEON4UE-z-TVRcqJ.js +0 -1
- package/public/assets/eventmodeling-45OFAUF4-CsZX8d8q.js +0 -1
- package/public/assets/flowDiagram-UKHOOZJN-B-BoRx9A.js +0 -1
- package/public/assets/railroad-abnf-AHOZXSZD-vLN4fXap.js +0 -1
- package/public/assets/railroad-ebnf-EBAXGLYW-B70X8CrM.js +0 -1
- package/public/assets/railroad-peg-LSFZ7HO6-DXKtxJxs.js +0 -1
- package/public/assets/stateDiagram-v2-UEYNNEHI-BDBAarNU.js +0 -1
- package/public/assets/swimlanesDiagram-ULZ7WXOC-BhW0icZR.js +0 -8
package/dist/bus.js
CHANGED
|
@@ -19,6 +19,54 @@ const tailJsonl = (p, limit) => {
|
|
|
19
19
|
return { malformed: true, raw: l };
|
|
20
20
|
} });
|
|
21
21
|
};
|
|
22
|
+
/* Thresholds are judgement, not measurement, and are stated here rather than buried: "producing"
|
|
23
|
+
is the working cadence, and >8h is the shape of an overnight stall. The 2h boundary exists
|
|
24
|
+
because the previous model put a coffee break and a seventeen-hour stall in one bucket. */
|
|
25
|
+
const EMITTING_MS = 2 * 3_600_000;
|
|
26
|
+
const SILENT_MS = 8 * 3_600_000;
|
|
27
|
+
const SCAN_LINES = 4000;
|
|
28
|
+
/** Newest emission per agent, from the room logs and the status stream. */
|
|
29
|
+
export function outputIndex(root, scanLines = SCAN_LINES) {
|
|
30
|
+
const out = new Map();
|
|
31
|
+
const note = (from, ts, basis) => {
|
|
32
|
+
if (typeof from !== "string" || typeof ts !== "number")
|
|
33
|
+
return;
|
|
34
|
+
const prev = out.get(from);
|
|
35
|
+
if (!prev || ts > prev.ts)
|
|
36
|
+
out.set(from, { ts, basis });
|
|
37
|
+
};
|
|
38
|
+
const roomDir = path.join(root, "rooms");
|
|
39
|
+
if (existsSync(roomDir))
|
|
40
|
+
for (const f of readdirSync(roomDir).filter((x) => x.endsWith(".jsonl"))) {
|
|
41
|
+
for (const m of tailJsonl(path.join(roomDir, f), scanLines))
|
|
42
|
+
note(m.from, m.ts, "room");
|
|
43
|
+
}
|
|
44
|
+
for (const m of tailJsonl(path.join(root, "status.jsonl"), scanLines))
|
|
45
|
+
note(m.agentId ?? m.from, m.ts, "status");
|
|
46
|
+
/* DMs count as output and are NOT in the room logs — they are rows in the RECIPIENT's inbox,
|
|
47
|
+
carrying the sender in `from`. Omitting them made an agent working entirely by DM read as
|
|
48
|
+
stalled; that was caught on the first live run, where this console's own worker showed
|
|
49
|
+
`stalled` while it had been reporting by DM all morning. Scanning inboxes attributes each
|
|
50
|
+
message to its sender, not its owner. */
|
|
51
|
+
const inboxDir = path.join(root, "inbox");
|
|
52
|
+
if (existsSync(inboxDir))
|
|
53
|
+
for (const f of readdirSync(inboxDir).filter((x) => x.endsWith(".jsonl"))) {
|
|
54
|
+
for (const m of tailJsonl(path.join(inboxDir, f), scanLines))
|
|
55
|
+
note(m.from, m.ts, "dm");
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
/* `unknown` is NOT "no output" — it is "none within the scanned window", and the window is reported
|
|
60
|
+
so a caller can tell the two apart. Collapsing them would be the same defect one level down. */
|
|
61
|
+
const progressOf = (hit, now, scanLines) => {
|
|
62
|
+
if (!hit)
|
|
63
|
+
return { lastOutputAt: null, ageMs: null, state: "unknown", basis: null, scannedLines: scanLines, measures: "bus-emissions" };
|
|
64
|
+
const ageMs = now - hit.ts;
|
|
65
|
+
return { lastOutputAt: hit.ts, ageMs, state: ageMs < EMITTING_MS ? "emitting" : ageMs < SILENT_MS ? "quiet-on-bus" : "silent-on-bus", basis: hit.basis, scannedLines: scanLines, measures: "bus-emissions" };
|
|
66
|
+
};
|
|
67
|
+
/* Reachability buckets. `idle` used to span 15 MINUTES to 24 HOURS, so a coffee break and an
|
|
68
|
+
overnight stall rendered identically; `dormant` splits that. */
|
|
69
|
+
export const reachOf = (age) => age < 15 * 60_000 ? "live" : age < 2 * 3_600_000 ? "idle" : age < 24 * 3_600_000 ? "dormant" : "dead";
|
|
22
70
|
export async function agentsAsync(root) {
|
|
23
71
|
const panes = await listPanes();
|
|
24
72
|
const base = agentsSync(root);
|
|
@@ -33,11 +81,14 @@ export async function agentsAsync(root) {
|
|
|
33
81
|
const paneActivity = ct === undefined || ct === null ? windowActivity : ct || null; // 0 = seeded, no change observed yet → treated as unknown, not as "now"
|
|
34
82
|
const lastSeen = Math.max(a.lastSeen ?? 0, paneActivity ?? 0);
|
|
35
83
|
const age = lastSeen ? Date.now() - lastSeen : Infinity;
|
|
36
|
-
|
|
84
|
+
/* paneActivity is another REACHABILITY input, not progress: a pane redraws when a message is pasted into
|
|
85
|
+
it. It is deliberately not folded into `progress`, which stays as agentsSync computed it. */
|
|
86
|
+
return { ...a, paneActivity, windowActivity, windowName: pane?.windowName ?? null, paneCommand: pane?.command ?? null, paneAlive: pane ? !pane.dead : null, lastSeen: lastSeen || null, heartbeatAgeMs: Number.isFinite(age) ? age : null, reachability: reachOf(age), active: paneActivity ? Date.now() - paneActivity < 60_000 : false };
|
|
37
87
|
}).sort((a, b) => (b.lastSeen ?? 0) - (a.lastSeen ?? 0));
|
|
38
88
|
}
|
|
39
89
|
export const agents = agentsSync;
|
|
40
|
-
export function agentsSync(root) {
|
|
90
|
+
export function agentsSync(root, scanLines = SCAN_LINES) {
|
|
91
|
+
const output = outputIndex(root, scanLines);
|
|
41
92
|
const reg = readJson(path.join(root, "agents.json"), {});
|
|
42
93
|
const transports = existsSync(path.join(root, "transports")) ? new Set(readdirSync(path.join(root, "transports")).map((f) => f.replace(/\.json$/, ""))) : new Set();
|
|
43
94
|
const now = Date.now();
|
|
@@ -48,7 +99,7 @@ export function agentsSync(root) {
|
|
|
48
99
|
const mtimes = [tPath, path.join(root, "inbox", `${a.agentId}.jsonl`), path.join(root, "cursors", `${a.agentId}.json`)].filter(existsSync).map((f) => statSync(f).mtimeMs);
|
|
49
100
|
const lastSeen = Math.max(a.lastHeartbeat ?? 0, ...mtimes);
|
|
50
101
|
const age = lastSeen ? now - lastSeen : Infinity;
|
|
51
|
-
return { ...a, transport: t?.transport ?? t?.kind ?? (t ? "tmux-push" : null), pane: t?.tmuxTarget ?? null,
|
|
102
|
+
return { ...a, transport: t?.transport ?? t?.kind ?? (t ? "tmux-push" : null), pane: t?.tmuxTarget ?? null, reachability: reachOf(age), heartbeatAgeMs: Number.isFinite(age) ? age : null, lastSeen: lastSeen || null, progress: progressOf(output.get(a.agentId), now, scanLines) };
|
|
52
103
|
}).sort((a, b) => (b.lastSeen ?? 0) - (a.lastSeen ?? 0));
|
|
53
104
|
}
|
|
54
105
|
export function rooms(root) {
|
package/dist/events.js
CHANGED
|
@@ -103,7 +103,7 @@ export class EventEngine {
|
|
|
103
103
|
}
|
|
104
104
|
/** The live coordinator agent for a project/room (role coordinator, project == room or name), else null. */
|
|
105
105
|
coordinatorFor(project, room) {
|
|
106
|
-
const a = agents(this.cfg.coordDir).find((x) => /coordinator/i.test(x.role ?? "") && (x.project === room || x.project === project) && x.
|
|
106
|
+
const a = agents(this.cfg.coordDir).find((x) => /coordinator/i.test(x.role ?? "") && (x.project === room || x.project === project) && x.reachability !== "dead");
|
|
107
107
|
return a?.agentId ?? null;
|
|
108
108
|
}
|
|
109
109
|
emitRaw(type, project, data) {
|
package/dist/gitinfo.js
CHANGED
|
@@ -3,6 +3,7 @@ import { execFile } from "node:child_process";
|
|
|
3
3
|
import { promisify } from "node:util";
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
|
+
import { refsIn, refsMatch } from "@davidbalzan/groundwork-seam";
|
|
6
7
|
const run = promisify(execFile);
|
|
7
8
|
async function git(cwd, args) { try {
|
|
8
9
|
const r = await run("git", args, { cwd, maxBuffer: 8 << 20 });
|
|
@@ -139,6 +140,59 @@ async function ghPrs(projectPath) {
|
|
|
139
140
|
return null;
|
|
140
141
|
}
|
|
141
142
|
}
|
|
143
|
+
/* Which PR a board row is ABOUT, from its `pr` CELL — never from a `#n` appearing anywhere in the
|
|
144
|
+
row. The openPrs extension declares fields ["repo","pr","owner","status","mergeGate"], so the
|
|
145
|
+
answer has a designated column and reading the others is a guess.
|
|
146
|
+
|
|
147
|
+
The old matcher was `Object.values(r).some(v => /#n/.test(v))`, which meant a Status cell reading
|
|
148
|
+
"blocked by #39" made THAT row answer for PR 39 — and `find` returns the first hit, so a mention
|
|
149
|
+
beat the real row. Measured on a two-row fixture: qaOf(39) returned "FAIL — held" from #57's row
|
|
150
|
+
while #39's own row said PASS. A passing PR shown as failing, in the alarming direction.
|
|
151
|
+
|
|
152
|
+
Same defect the kit found in queue-done-loop the same afternoon: a ref MENTIONED in a row is a
|
|
153
|
+
different fact from the ref that IDENTIFIES it. */
|
|
154
|
+
/* `owner/name` from a remote URL, ssh or https. null when there is no origin. */
|
|
155
|
+
export const slugFromRemoteUrl = (url) => {
|
|
156
|
+
const m = url ? /(?:[:/])([^/:]+\/[^/]+?)(?:\.git)?$/.exec(url.trim()) : null;
|
|
157
|
+
return m ? m[1] : null;
|
|
158
|
+
};
|
|
159
|
+
const originSlug = async (projectPath) => slugFromRemoteUrl(await git(projectPath, ["remote", "get-url", "origin"]));
|
|
160
|
+
/* The ref a board row is ABOUT: its `pr` CELL, qualified by its `repo` CELL when it has one.
|
|
161
|
+
Never a `#ref` occurring anywhere else in the row — a Status reading "blocked by #39" used to
|
|
162
|
+
make that row answer for PR 39, and `find` returns the first hit, so a mention beat the row that
|
|
163
|
+
actually was that PR.
|
|
164
|
+
|
|
165
|
+
Ref parsing and comparison now come from the seam (0.1.8) instead of the local pair this file
|
|
166
|
+
used to carry. That pair was a re-derivation of a primitive the seam did not yet export; it does
|
|
167
|
+
now, and keeping our own would be a fifth matcher of the class the kit spent a day removing.
|
|
168
|
+
|
|
169
|
+
ONE DELIBERATE BEHAVIOUR CHANGE, adopted rather than worked around: with NO origin remote
|
|
170
|
+
(`contextRepo` null) and a row that names a repo, the local version ACCEPTED — it could not
|
|
171
|
+
establish a difference, so it declined to assert one. `refsMatch` returns false: an unresolvable
|
|
172
|
+
tie is not a tie. The seam's direction is the conservative one for this caller too — showing no
|
|
173
|
+
merge gate is better than showing another repo's — so the console takes the primitive's
|
|
174
|
+
semantics rather than keeping a private exception. */
|
|
175
|
+
const rowRef = (row) => {
|
|
176
|
+
const cell = (row.pr ?? "").trim();
|
|
177
|
+
const repo = (row.repo ?? "").trim();
|
|
178
|
+
/* Qualify with the repo column only when the cell is not ALREADY qualified. The test is a `/`,
|
|
179
|
+
not a `#`: an earlier draft checked for `#` and every pr cell contains one, so qualification
|
|
180
|
+
silently never happened and a cross-repo collision resolved to the first row. Caught by the
|
|
181
|
+
two-repos fixture, which is why that fixture exists. */
|
|
182
|
+
const [ref] = refsIn(repo && !cell.includes("/") ? `${repo}#${cell.replace(/^#/, "")}` : cell);
|
|
183
|
+
return ref ?? null;
|
|
184
|
+
};
|
|
185
|
+
/* The gate a board reports for PR `n` in THIS repo, or undefined. Exported and used by
|
|
186
|
+
gitBranches directly, so the test exercises this function rather than a copy of it — a test that
|
|
187
|
+
re-implements the matcher pins the fixture, not the code. */
|
|
188
|
+
export const gateForPr = (rows, n, slug) => {
|
|
189
|
+
const [target] = refsIn(slug ? `${slug}#${n}` : `#${n}`);
|
|
190
|
+
if (!target)
|
|
191
|
+
return undefined;
|
|
192
|
+
const row = rows?.find((r) => { const ref = rowRef(r); return ref ? refsMatch(target, ref, { contextRepo: slug }) : false; });
|
|
193
|
+
const qa = row?.mergeGate ?? row?.status;
|
|
194
|
+
return qa ? String(qa) : undefined;
|
|
195
|
+
};
|
|
142
196
|
export function gitBranches(projectPath, hints = {}) {
|
|
143
197
|
return cached(`branches:${projectPath}:${(hints.agentIds ?? []).join(",")}`, async () => {
|
|
144
198
|
const defaultBranch = "main";
|
|
@@ -147,8 +201,8 @@ export function gitBranches(projectPath, hints = {}) {
|
|
|
147
201
|
const rows = splitRecords(await git(projectPath, ["for-each-ref", "--sort=-committerdate", "--format=%(refname)%1f%(objectname)%1f%(objectname:short)%1f%(subject)%1f%(committerdate:iso-strict)", "refs/heads", "refs/remotes"]))
|
|
148
202
|
.filter(([ref]) => !/^refs\/remotes\/[^/]+\/HEAD$/.test(ref));
|
|
149
203
|
const [{ worktrees }, prs] = await Promise.all([gitWorktrees(projectPath, hints), ghPrs(projectPath)]);
|
|
150
|
-
const
|
|
151
|
-
|
|
204
|
+
const slug = await originSlug(projectPath);
|
|
205
|
+
const qaOf = (n) => gateForPr(hints.openPrs, n, slug);
|
|
152
206
|
const now = Date.now();
|
|
153
207
|
const branches = await Promise.all(rows.map(async ([ref, sha, short, subject, date]) => {
|
|
154
208
|
const remote = ref.startsWith("refs/remotes/");
|
package/dist/seam.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* Read-only views over a project's seam files, parsed ONLY through @davidbalzan/groundwork-seam. */
|
|
2
2
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { parseWorkDoc, queueItemsOf, doneEntriesOf, listWorkBoardOf, workDocIssues, SCHEMA } from "@davidbalzan/groundwork-seam";
|
|
4
|
+
import { parseWorkDoc, queueItemsOf, doneEntriesOf, listWorkBoardOf, workDocIssues, boardSchemaOf, SCHEMA } from "@davidbalzan/groundwork-seam";
|
|
5
5
|
import { parseFactsDoc } from "@davidbalzan/groundwork-seam";
|
|
6
6
|
const read = (p) => (existsSync(p) ? readFileSync(p, "utf8") : null);
|
|
7
7
|
const FACT_STALE_DAYS = 14; // groundwork doctor rule
|
|
@@ -13,14 +13,22 @@ export function boardOf(projectPath) {
|
|
|
13
13
|
const f = read(path.join(docs, "FACTS.md"));
|
|
14
14
|
const wsDoc = ws ? parseWorkDoc(ws) : null;
|
|
15
15
|
const board = wsDoc ? listWorkBoardOf(wsDoc) : [];
|
|
16
|
-
/*
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
/* Schema comes from the seam's boardSchemaOf, not from a local re-derivation.
|
|
17
|
+
The console used to work this out itself, and console#35 fixed the first defect in that
|
|
18
|
+
copy: it read the schema off the first parsed ROW, so an empty-but-valid board yielded
|
|
19
|
+
null and rendered "unknown workstreams grammar" — the alarming reading of an empty one.
|
|
20
|
+
The seam now exports the answer, and its version is better than ours on two counts we
|
|
21
|
+
had wrong:
|
|
22
|
+
- it EXCLUDES extension tables by deriving their ids from WORKSTREAMS_EXT, so
|
|
23
|
+
registering a new extension cannot forget to exclude it. Ours allow-listed the two
|
|
24
|
+
board grammars we knew, which meant any FUTURE board schema read as unrecognised.
|
|
25
|
+
- it returns "unknown" only when a header was actually REFUSED, and null when there
|
|
26
|
+
is simply no board block. Ours collapsed those: `schema ?? (ws ? "unknown" : null)`
|
|
27
|
+
reported "unknown grammar" for a WORKSTREAMS.md that merely has no Active Streams
|
|
28
|
+
table — the same conflation console#35 fixed, one case over, still live until now.
|
|
29
|
+
So `null` here means "no board table", NOT "no file"; the caller must not render those
|
|
30
|
+
as the same thing. `files.workstreams` is what distinguishes them. */
|
|
31
|
+
const schema = wsDoc ? boardSchemaOf(wsDoc) : null;
|
|
24
32
|
const qDoc = q ? parseWorkDoc(q) : null;
|
|
25
33
|
const dDoc = d ? parseWorkDoc(d) : null;
|
|
26
34
|
const facts = f ? parseFactsDoc(f) : null;
|
|
@@ -31,9 +39,18 @@ export function boardOf(projectPath) {
|
|
|
31
39
|
return {
|
|
32
40
|
files: { workstreams: !!ws, queue: !!q, done: !!d, facts: !!f },
|
|
33
41
|
prd,
|
|
34
|
-
workstreams: { schema
|
|
42
|
+
workstreams: { schema, rows: board, issues: wsDoc ? workDocIssues(wsDoc) : [] },
|
|
35
43
|
queue: qDoc ? queueItemsOf(qDoc) : [],
|
|
36
44
|
done: dDoc ? doneEntriesOf(dDoc) : [],
|
|
45
|
+
/* QUEUE.md and DONE.md parse issues. Only WORKSTREAMS.md's were ever surfaced, so a malformed
|
|
46
|
+
queue line was parsed, kept, rendered — and reported NOWHERE. That gap is the console's half
|
|
47
|
+
of seam 0.1.6: the seam stopped DROPPING an out-of-range priority precisely so a consumer
|
|
48
|
+
could say something about it, and this consumer said nothing. `workDocIssues` already carries
|
|
49
|
+
the message (the seam builds it with invalidPriorityIssue), so nothing here re-derives it.
|
|
50
|
+
Siblings rather than a reshape of `queue`/`done`: those are arrays every caller maps over,
|
|
51
|
+
and the gap is a missing signal, not a wrong shape. */
|
|
52
|
+
queueIssues: qDoc ? workDocIssues(qDoc) : [],
|
|
53
|
+
doneIssues: dDoc ? workDocIssues(dDoc) : [],
|
|
37
54
|
facts: facts
|
|
38
55
|
? {
|
|
39
56
|
entries: facts.entries.map((e) => {
|
package/dist/seamwrite.js
CHANGED
|
@@ -65,7 +65,11 @@ export function addQueueItem(projectPath, item, log) {
|
|
|
65
65
|
log(`wrote docs/QUEUE.md (+1 item, ${out.length - src.length} bytes)`);
|
|
66
66
|
return { file: "docs/QUEUE.md", line: renderWorkDocLine(newItem), bytesDelta: out.length - src.length };
|
|
67
67
|
}
|
|
68
|
-
|
|
68
|
+
/* Display-only echo of the line we appended. `QueueItem.priority` is nullable since seam 0.1.6, and
|
|
69
|
+
interpolating a null renders the literal "(null)" — so fall back to the raw tag the seam kept. The
|
|
70
|
+
console never CREATES such an item (addQueueItem's input is narrowed to P1|P2|P3); this guards the
|
|
71
|
+
echo against a shape the type now permits. */
|
|
72
|
+
const renderWorkDocLine = (q) => `- [${q.done ? "x" : " "}] (${q.priority ?? q.priorityRaw ?? "?"}) ${q.text}`;
|
|
69
73
|
/** Re-verify (or add) a fact: seam upsertFact with a fresh timestamp. */
|
|
70
74
|
export function reverifyFact(projectPath, entry, log) {
|
|
71
75
|
const file = path.join(projectPath, "docs/FACTS.md");
|
package/dist/server.js
CHANGED
|
@@ -48,7 +48,7 @@ const clientFor = (id) => (id === "david" ? busDavid : bus);
|
|
|
48
48
|
/** Capabilities of the connected MCP (null until a client has connected and listed tools). */
|
|
49
49
|
const mcpCaps = () => { const cl = bus.connected ? bus : busDavid.connected ? busDavid : null; return { inReplyTo: cl ? cl.supports("send_message", "inReplyTo") : null, sendCommand: cl ? cl.sendCommands : null }; };
|
|
50
50
|
/* Live tmux-attached agents — the only valid send_command targets (the MCP hard-gates to tmux-push/-remote). */
|
|
51
|
-
const liveAttached = () => agents(cfg.coordDir).filter((a) => a.
|
|
51
|
+
const liveAttached = () => agents(cfg.coordDir).filter((a) => a.reachability === "live" && (a.transport === "tmux-push" || a.transport === "tmux-push-remote")).map((a) => a.agentId);
|
|
52
52
|
const PUBLIC_DIR = path.resolve(HERE, "../public");
|
|
53
53
|
const root = new Hono();
|
|
54
54
|
const app = new Hono().basePath("/api");
|
|
@@ -286,7 +286,7 @@ app.get("/projects", (c) => {
|
|
|
286
286
|
catch {
|
|
287
287
|
return null;
|
|
288
288
|
} })();
|
|
289
|
-
const liveAgents = agents(cfg.coordDir).filter((a) => a.
|
|
289
|
+
const liveAgents = agents(cfg.coordDir).filter((a) => a.reachability !== "dead");
|
|
290
290
|
return c.json(cfg.projects.map((p) => {
|
|
291
291
|
const b = boardOf(p.path);
|
|
292
292
|
const fleet = fleetStatus(liveAgents.filter((a) => a.project === p.room || a.project === p.name));
|
|
@@ -723,7 +723,7 @@ app.get("/projects/:name/fleet", async (c) => {
|
|
|
723
723
|
const p = project(c.req.param("name"));
|
|
724
724
|
if (!p)
|
|
725
725
|
return c.json({ error: "unknown project" }, 404);
|
|
726
|
-
const live = (await agentsAsync(cfg.coordDir)).filter((a) => a.
|
|
726
|
+
const live = (await agentsAsync(cfg.coordDir)).filter((a) => a.reachability !== "dead" && (a.project === p.room || a.project === p.name));
|
|
727
727
|
return c.json({ ...fleetStatus(live), templates: FLEET_TEMPLATES, window: `fleet-${p.name}` });
|
|
728
728
|
});
|
|
729
729
|
const roleOfAgent = (id) => agents(cfg.coordDir).find((a) => a.agentId === id)?.role ?? null;
|
|
@@ -788,7 +788,7 @@ app.post("/actions/prune", async (c) => {
|
|
|
788
788
|
});
|
|
789
789
|
app.post("/actions/unregister-dead", async (c) => {
|
|
790
790
|
const b = (await c.req.json().catch(() => ({})));
|
|
791
|
-
const dead = agents(cfg.coordDir).filter((a) => a.
|
|
791
|
+
const dead = agents(cfg.coordDir).filter((a) => a.reachability === "dead" && !["groundwork-console", "david"].includes(a.agentId)).map((a) => a.agentId);
|
|
792
792
|
const ids = (b.agentIds?.length ? b.agentIds.filter((id) => dead.includes(id)) : dead);
|
|
793
793
|
if (b.confirm !== "UNREGISTER")
|
|
794
794
|
return c.json({ error: "confirm must be UNREGISTER", wouldUnregister: ids }, 400);
|
|
@@ -810,7 +810,7 @@ app.post("/actions/unregister", async (c) => {
|
|
|
810
810
|
return c.json(r, r.status === "ok" ? 200 : 403);
|
|
811
811
|
});
|
|
812
812
|
/* ---- Roadmap actions: message the project's aide (plan) or coordinator (kick off) as david ---- */
|
|
813
|
-
const roleAgent = (projectName, room, roleRe) => agents(cfg.coordDir).find((a) => roleRe.test(a.role ?? "") && (a.project === room || a.project === projectName) && a.
|
|
813
|
+
const roleAgent = (projectName, room, roleRe) => agents(cfg.coordDir).find((a) => roleRe.test(a.role ?? "") && (a.project === room || a.project === projectName) && a.reachability !== "dead")?.agentId ?? null;
|
|
814
814
|
app.get("/projects/:name/agents", (c) => {
|
|
815
815
|
const p = project(c.req.param("name"));
|
|
816
816
|
if (!p)
|
|
@@ -855,7 +855,7 @@ app.post("/actions/task/:name/:phase/:line", async (c) => {
|
|
|
855
855
|
return c.json({ error: "no task at that line" }, 404);
|
|
856
856
|
if (task.done)
|
|
857
857
|
return c.json({ error: "already ticked" }, 409);
|
|
858
|
-
const live = agents(cfg.coordDir).filter((a) => a.
|
|
858
|
+
const live = agents(cfg.coordDir).filter((a) => a.reachability !== "dead");
|
|
859
859
|
const owner = b.owner && live.some((a) => a.agentId === b.owner) ? b.owner : roleAgent(p.name, p.room, /aide|liaison|curator/i);
|
|
860
860
|
if (!owner)
|
|
861
861
|
return c.json({ error: `no live owner or aide for ${p.name}` }, 409);
|
|
@@ -897,7 +897,7 @@ app.get("/projects/:name/doctor/fixers", async (c) => {
|
|
|
897
897
|
const p = project(c.req.param("name"));
|
|
898
898
|
if (!p)
|
|
899
899
|
return c.json({ error: "unknown project" }, 404);
|
|
900
|
-
const live = (await agentsAsync(cfg.coordDir)).filter((a) => a.
|
|
900
|
+
const live = (await agentsAsync(cfg.coordDir)).filter((a) => a.reachability !== "dead" && (a.project === p.room || a.project === p.name));
|
|
901
901
|
const out = {};
|
|
902
902
|
for (const [check, o] of Object.entries(DOCTOR_OWNER)) {
|
|
903
903
|
const a = live.find((x) => o.re.test(x.role ?? ""));
|
|
@@ -913,7 +913,7 @@ app.post("/actions/doctor-fix/:name", async (c) => {
|
|
|
913
913
|
const o = DOCTOR_OWNER[b.check];
|
|
914
914
|
if (!o)
|
|
915
915
|
return c.json({ error: "no fixer role for this check" }, 400);
|
|
916
|
-
const live = (await agentsAsync(cfg.coordDir)).filter((a) => a.
|
|
916
|
+
const live = (await agentsAsync(cfg.coordDir)).filter((a) => a.reachability !== "dead" && (a.project === p.room || a.project === p.name));
|
|
917
917
|
const target = live.find((x) => o.re.test(x.role ?? ""))?.agentId;
|
|
918
918
|
if (!target)
|
|
919
919
|
return c.json({ error: `no live ${o.role} for ${p.name}` }, 409);
|
|
@@ -1036,9 +1036,9 @@ app.get("/bus/panes", async (c) => {
|
|
|
1036
1036
|
const lines = Number(c.req.query("lines") ?? 40);
|
|
1037
1037
|
const p = proj ? project(proj) : undefined;
|
|
1038
1038
|
const all = await agentsAsync(cfg.coordDir);
|
|
1039
|
-
const scoped = all.filter((a) => a.pane && a.
|
|
1039
|
+
const scoped = all.filter((a) => a.pane && a.reachability !== "dead" && (!p || a.project === p.name || (p.room && a.project === p.room)));
|
|
1040
1040
|
const colors = c.req.query("colors") === "1";
|
|
1041
|
-
const out = await Promise.all(scoped.map(async (a) => ({ agentId: a.agentId, role: a.role ?? null, project: a.project ?? null, pane: a.pane, windowName: a.windowName ?? null, command: a.paneCommand, active: a.active, paneActivity: a.paneActivity,
|
|
1041
|
+
const out = await Promise.all(scoped.map(async (a) => ({ agentId: a.agentId, role: a.role ?? null, project: a.project ?? null, pane: a.pane, windowName: a.windowName ?? null, command: a.paneCommand, active: a.active, paneActivity: a.paneActivity, reachability: a.reachability, capture: await capturePane(a.pane, lines, colors) })));
|
|
1042
1042
|
return c.json(out);
|
|
1043
1043
|
});
|
|
1044
1044
|
/* Pane viewer: read-only capture of an agent's tmux pane (never sends keys). */
|
package/dist/tmux.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Read-only tmux views: pane activity (for
|
|
1
|
+
/* Read-only tmux views: pane activity (for reachability + activity indicators) and pane capture (for the pane viewer). Never sends keys. */
|
|
2
2
|
import { execFile } from "node:child_process";
|
|
3
3
|
import { promisify } from "node:util";
|
|
4
4
|
const run = promisify(execFile);
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@davidbalzan/groundwork-console",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.14",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@davidbalzan/groundwork-seam": "
|
|
7
|
+
"@davidbalzan/groundwork-seam": "0.1.8",
|
|
8
8
|
"@hono/node-server": "^2.1.1",
|
|
9
9
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
10
10
|
"hono": "^4.13.2",
|
|
@@ -48,8 +48,12 @@
|
|
|
48
48
|
"publish-check": "node scripts/publish-check.mjs",
|
|
49
49
|
"test:inject": "tsx test/inject.test.mts",
|
|
50
50
|
"test:skills": "tsx test/skills.test.mts",
|
|
51
|
-
"test": "pnpm run test:inject && pnpm run test:skills && pnpm run test:board && pnpm run test:panes",
|
|
51
|
+
"test": "pnpm run test:inject && pnpm run test:skills && pnpm run test:board && pnpm run test:panes && pnpm run test:seam-contract && pnpm run test:prref && pnpm run test:progress && pnpm run test:publish-check",
|
|
52
52
|
"test:board": "tsx test/board.test.mts",
|
|
53
|
-
"test:panes": "tsx test/panes.test.mts"
|
|
53
|
+
"test:panes": "tsx test/panes.test.mts",
|
|
54
|
+
"test:seam-contract": "tsx test/seam-contract.test.mts",
|
|
55
|
+
"test:prref": "tsx test/prref.test.mts",
|
|
56
|
+
"test:progress": "tsx test/progress.test.mts",
|
|
57
|
+
"test:publish-check": "tsx test/publish-check.test.mts"
|
|
54
58
|
}
|
|
55
59
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{m as e,t}from"./mermaid-parser.core-
|
|
1
|
+
import{m as e,t}from"./mermaid-parser.core-CIHTTkEa.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-eh414mKr.js";import"./chunk-I66GZJ75-C8VZHfLH.js";import{n as i,r as a,t as o}from"./chunk-6Q2QTUOP-BEP7PF5q.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";import"./mermaid.core-Cc5OhPpa.js";var c=e().RailroadAbnf.parser.LangiumParser,l=n(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=n(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=n(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=n(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=n(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=n(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=n(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:n(e=>{o.clear(),r.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new t(n);let i=n.value;r.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),r.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e,t}from"./path-fybaL0A-.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-
|
|
1
|
+
import{n as e,t}from"./path-fybaL0A-.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-By9Jxne0.js";function m(e){return e.innerRadius}function h(e){return e.outerRadius}function g(e){return e.startAngle}function _(e){return e.endAngle}function v(e){return e&&e.padAngle}function y(e,t,n,r,i,a,o,s){var c=n-e,l=r-t,u=o-i,d=s-a,f=d*c-u*l;if(!(f*f<1e-12))return f=(u*(t-a)-d*(e-i))/f,[e+f*c,t+f*l]}function b(e,t,n,r,i,a,o){var c=e-n,l=t-r,u=(o?a:-a)/d(c*c+l*l),f=u*l,p=-u*c,m=e+f,h=t+p,g=n+f,_=r+p,v=(m+g)/2,y=(h+_)/2,b=g-m,x=_-h,S=b*b+x*x,C=i-a,w=m*_-g*h,T=(x<0?-1:1)*d(s(0,C*C*S-w*w)),E=(w*x-b*T)/S,D=(-w*b-x*T)/S,O=(w*x+b*T)/S,k=(-w*b+x*T)/S,A=E-v,j=D-y,M=O-v,N=k-y;return A*A+j*j>M*M+N*N&&(E=O,D=k),{cx:E,cy:D,x01:-f,y01:-p,x11:E*(i/C-1),y11:D*(i/C-1)}}function x(){var s=m,x=h,S=e(0),C=null,w=g,T=_,E=v,D=null,O=t(k);function k(){var e,t,m=+s.apply(this,arguments),h=+x.apply(this,arguments),g=w.apply(this,arguments)-r,_=T.apply(this,arguments)-r,v=l(_-g),k=_>g;if(D||=e=O(),h<m&&(t=h,h=m,m=t),!(h>1e-12))D.moveTo(0,0);else if(v>c-1e-12)D.moveTo(h*u(g),h*a(g)),D.arc(0,0,h,g,_,!k),m>1e-12&&(D.moveTo(m*u(_),m*a(_)),D.arc(0,0,m,_,g,k));else{var A=g,j=_,M=g,N=_,P=v,F=v,I=E.apply(this,arguments)/2,L=I>1e-12&&(C?+C.apply(this,arguments):d(m*m+h*h)),R=p(l(h-m)/2,+S.apply(this,arguments)),z=R,B=R,V,H;if(L>1e-12){var U=o(L/m*a(I)),W=o(L/h*a(I));(P-=U*2)>1e-12?(U*=k?1:-1,M+=U,N-=U):(P=0,M=N=(g+_)/2),(F-=W*2)>1e-12?(W*=k?1:-1,A+=W,j-=W):(F=0,A=j=(g+_)/2)}var G=h*u(A),K=h*a(A),q=m*u(N),J=m*a(N);if(R>1e-12){var Y=h*u(j),X=h*a(j),Z=m*u(M),Q=m*a(M),$;if(v<i){if($=y(G,K,Z,Q,Y,X,q,J)){var ee=G-$[0],te=K-$[1],ne=Y-$[0],re=X-$[1],ie=1/a(f((ee*ne+te*re)/(d(ee*ee+te*te)*d(ne*ne+re*re)))/2),ae=d($[0]*$[0]+$[1]*$[1]);z=p(R,(m-ae)/(ie-1)),B=p(R,(h-ae)/(ie+1))}else z=B=0}}F>1e-12?B>1e-12?(V=b(Z,Q,G,K,h,B,k),H=b(Y,X,q,J,h,B,k),D.moveTo(V.cx+V.x01,V.cy+V.y01),B<R?D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,h,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),!k),D.arc(H.cx,H.cy,B,n(H.y11,H.x11),n(H.y01,H.x01),!k))):(D.moveTo(G,K),D.arc(0,0,h,A,j,!k)):D.moveTo(G,K),!(m>1e-12)||!(P>1e-12)?D.lineTo(q,J):z>1e-12?(V=b(q,J,Y,X,m,-z,k),H=b(G,K,Z,Q,m,-z,k),D.lineTo(V.cx+V.x01,V.cy+V.y01),z<R?D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,m,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),k),D.arc(H.cx,H.cy,z,n(H.y11,H.x11),n(H.y01,H.x01),!k))):D.arc(0,0,m,N,M,k)}if(D.closePath(),e)return D=null,e+``||null}return k.centroid=function(){var e=(+s.apply(this,arguments)+ +x.apply(this,arguments))/2,t=(+w.apply(this,arguments)+ +T.apply(this,arguments))/2-i/2;return[u(t)*e,a(t)*e]},k.innerRadius=function(t){return arguments.length?(s=typeof t==`function`?t:e(+t),k):s},k.outerRadius=function(t){return arguments.length?(x=typeof t==`function`?t:e(+t),k):x},k.cornerRadius=function(t){return arguments.length?(S=typeof t==`function`?t:e(+t),k):S},k.padRadius=function(t){return arguments.length?(C=t==null?null:typeof t==`function`?t:e(+t),k):C},k.startAngle=function(t){return arguments.length?(w=typeof t==`function`?t:e(+t),k):w},k.endAngle=function(t){return arguments.length?(T=typeof t==`function`?t:e(+t),k):T},k.padAngle=function(t){return arguments.length?(E=typeof t==`function`?t:e(+t),k):E},k.context=function(e){return arguments.length?(D=e??null,k):D},k}export{x as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./chunk-KEIR6QF5-BICK3FdT.js";import{M as e}from"./mermaid-parser.core-CIHTTkEa.js";export{e as createArchitectureServices};
|