crosspad-mcp-server 8.1.2 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/.claude-plugin/marketplace.json +13 -0
  2. package/.claude-plugin/plugin.json +14 -0
  3. package/.mcp.json +9 -0
  4. package/README.md +95 -0
  5. package/dist/config.d.ts +3 -0
  6. package/dist/config.js +8 -0
  7. package/dist/config.js.map +1 -1
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.js +369 -49
  10. package/dist/index.js.map +1 -1
  11. package/dist/tools/idf-flash.js +2 -2
  12. package/dist/tools/idf-flash.js.map +1 -1
  13. package/dist/tools/idf-monitor.d.ts +3 -1
  14. package/dist/tools/idf-monitor.js +19 -3
  15. package/dist/tools/idf-monitor.js.map +1 -1
  16. package/dist/tools/midi.js +20 -16
  17. package/dist/tools/midi.js.map +1 -1
  18. package/dist/tools/symbols.d.ts +3 -1
  19. package/dist/tools/symbols.js +31 -1
  20. package/dist/tools/symbols.js.map +1 -1
  21. package/dist/tools/trace-buffer.d.ts +40 -0
  22. package/dist/tools/trace-buffer.js +74 -0
  23. package/dist/tools/trace-buffer.js.map +1 -0
  24. package/dist/tools/trace-device.d.ts +10 -0
  25. package/dist/tools/trace-device.js +26 -0
  26. package/dist/tools/trace-device.js.map +1 -0
  27. package/dist/tools/trace-doctor.d.ts +43 -0
  28. package/dist/tools/trace-doctor.js +150 -0
  29. package/dist/tools/trace-doctor.js.map +1 -0
  30. package/dist/tools/trace-export.d.ts +4 -0
  31. package/dist/tools/trace-export.js +14 -0
  32. package/dist/tools/trace-export.js.map +1 -0
  33. package/dist/tools/trace-session.d.ts +118 -0
  34. package/dist/tools/trace-session.js +346 -0
  35. package/dist/tools/trace-session.js.map +1 -0
  36. package/dist/tools/trace-symbols.d.ts +24 -0
  37. package/dist/tools/trace-symbols.js +44 -0
  38. package/dist/tools/trace-symbols.js.map +1 -0
  39. package/dist/tools/trace-webui.d.ts +53 -0
  40. package/dist/tools/trace-webui.js +222 -0
  41. package/dist/tools/trace-webui.js.map +1 -0
  42. package/dist/utils/device.d.ts +5 -0
  43. package/dist/utils/device.js +43 -15
  44. package/dist/utils/device.js.map +1 -1
  45. package/dist/utils/exec.js +26 -0
  46. package/dist/utils/exec.js.map +1 -1
  47. package/dist/utils/userConfig.d.ts +13 -0
  48. package/dist/utils/userConfig.js +43 -0
  49. package/dist/utils/userConfig.js.map +1 -0
  50. package/package.json +12 -4
  51. package/skills/crosspad/SKILL.md +58 -0
  52. package/skills/crosspad/reference/faq.md +40 -0
  53. package/skills/crosspad/reference/install.md +84 -0
  54. package/skills/crosspad/reference/repos.md +29 -0
  55. package/skills/crosspad/reference/role-contributor.md +64 -0
  56. package/skills/crosspad/reference/role-fw-dev.md +44 -0
  57. package/skills/crosspad/reference/role-user.md +49 -0
  58. package/skills/crosspad/reference/tools.md +68 -0
  59. package/skills/crosspad/scripts/doctor.sh +65 -0
  60. package/skills/crosspad/scripts/setup.sh +53 -0
  61. package/skills/swd-tracer/SKILL.md +135 -0
  62. package/skills/swd-tracer/reference/signals.md +42 -0
  63. package/skills/swd-tracer/scripts/detect-env.sh +61 -0
  64. package/skills/swd-tracer/scripts/install-udev-rules.sh +25 -0
  65. package/skills/swd-tracer/scripts/setup-venv.sh +26 -0
  66. package/tracer/PROTOCOL.md +260 -0
  67. package/tracer/README.md +327 -0
  68. package/tracer/swd_tracer.py +1066 -0
  69. package/tracer/ui/index.html +834 -0
@@ -0,0 +1,346 @@
1
+ import { spawn } from "child_process";
2
+ import path from "path";
3
+ import fs from "fs";
4
+ import { TraceBuffer } from "./trace-buffer.js";
5
+ import { daemonPath, resolvedPython, resolvedElf } from "./trace-symbols.js";
6
+ import { resolveConfigValue } from "../utils/userConfig.js";
7
+ import { TRACE_DIR_DEFAULT } from "../config.js";
8
+ /** Pure: parse one NDJSON line from the daemon into a Frame, or null. */
9
+ export function parseFrame(line) {
10
+ const s = line.trim();
11
+ if (!s.startsWith("{"))
12
+ return null;
13
+ try {
14
+ const o = JSON.parse(s);
15
+ if (o.type === "sample" && o.values)
16
+ return o;
17
+ if (o.type === "status" && o.device_state)
18
+ return o;
19
+ if (o.type === "error" && o.error)
20
+ return o;
21
+ if (o.type === "signals" && Array.isArray(o.signals)) {
22
+ if (!Array.isArray(o.unresolved))
23
+ o.unresolved = [];
24
+ return o;
25
+ }
26
+ return null;
27
+ }
28
+ catch {
29
+ return null;
30
+ }
31
+ }
32
+ export class TraceSession {
33
+ opts;
34
+ proc = null;
35
+ buffer;
36
+ deviceState = "starting";
37
+ startedAt = 0;
38
+ filePath = null;
39
+ stdoutBuf = "";
40
+ onFrameExtra = null;
41
+ // Pending add/remove resolvers, fired (FIFO) on the next "signals" frame so
42
+ // the MCP/UI sees the POST-reconcile set (PROTOCOL §4). Each carries a timeout
43
+ // handle so a missing frame can't hang the caller forever.
44
+ pendingReconcile = [];
45
+ // ── §11.5 guaranteed teardown ──────────────────────────────────────────
46
+ // stop() escalates {cmd:stop} → SIGTERM → SIGKILL. These hold the pending
47
+ // escalation timers so a clean exit can cancel them (no stray kill) and so a
48
+ // second stop() is idempotent.
49
+ termTimer = null;
50
+ killTimer = null;
51
+ // ── §11.6 daemon stderr ring + start truthfulness ──────────────────────
52
+ // Ring of the last STDERR_RING lines of daemon stderr (human logs + any
53
+ // libusb noise) so a non-zero exit can surface *why* via stderrTail().
54
+ static STDERR_RING = 30;
55
+ stderrLines = [];
56
+ stderrBuf = "";
57
+ // Did we ever ingest an `error` frame? a non-zero exit with no error frame
58
+ // means the daemon died unexpectedly → synthesize device_state from stderr.
59
+ sawErrorFrame = false;
60
+ // Resolvers for waitForFirstFrame(), fired on the first ingested frame.
61
+ firstFrameSeen = false;
62
+ firstFrameWaiters = [];
63
+ constructor(opts) {
64
+ this.opts = opts;
65
+ this.buffer = new TraceBuffer(opts.signals, opts.capacity ?? 200_000);
66
+ }
67
+ onFrame(cb) { this.onFrameExtra = cb; }
68
+ /** Detach the frame sink so late `sample`/`status` frames during a shutdown
69
+ * race aren't rebroadcast after `trace_end`, and the consumer (Dashboard)
70
+ * doesn't hold this session longer than necessary. */
71
+ offFrame() { this.onFrameExtra = null; }
72
+ // Fired once when the daemon process is truly gone (clean exit, kill, or
73
+ // spawn error). Lets callers defer teardown (dashboard unbind, clearing the
74
+ // active session) until the probe is actually free — see §11.5.
75
+ stoppedCbs = [];
76
+ /** Register a callback for when the daemon process has fully exited. If the
77
+ * process is already gone, fires synchronously. */
78
+ onStopped(cb) {
79
+ if (this.proc === null) {
80
+ cb();
81
+ return;
82
+ }
83
+ this.stoppedCbs.push(cb);
84
+ }
85
+ fireStopped() {
86
+ const cbs = this.stoppedCbs;
87
+ this.stoppedCbs = [];
88
+ for (const cb of cbs) {
89
+ try {
90
+ cb();
91
+ }
92
+ catch { /* */ }
93
+ }
94
+ }
95
+ /** The ELF this session traces against (explicit opt or the configured default).
96
+ * Used by the web UI's /symbols endpoint so autocomplete matches the trace. */
97
+ elfPath() { return this.opts.elf ?? resolvedElf(); }
98
+ start() {
99
+ const traceDir = resolveConfigValue("trace_dir", "CROSSPAD_TRACE_DIR", process.env.CROSSPAD_TRACE_DIR, TRACE_DIR_DEFAULT);
100
+ fs.mkdirSync(traceDir, { recursive: true });
101
+ this.filePath = this.opts.outFile ?? path.join(traceDir, `trace-${process.pid}.cptrace`);
102
+ const argv = [daemonPath(), "trace", "--elf", this.opts.elf ?? resolvedElf(),
103
+ "--signals", this.opts.signals.join(","), "--rate", String(this.opts.rateHz), "--out", this.filePath];
104
+ const probe = resolveConfigValue("probe_serial", "CROSSPAD_PROBE_SERIAL", process.env.CROSSPAD_PROBE_SERIAL, "");
105
+ if (probe)
106
+ argv.push("--probe", probe);
107
+ // EXPERIMENTAL: SWO/ITM channel decode (opt-in; fail-soft in daemon).
108
+ if (this.opts.swo && this.opts.swo.length > 0) {
109
+ argv.push("--swo", this.opts.swo.join(","));
110
+ }
111
+ this.proc = spawn(resolvedPython(), argv, { stdio: ["pipe", "pipe", "pipe"] });
112
+ this.startedAt = performance.now();
113
+ // §11.4: honest pre-first-frame state. start() upgrades this to "running"
114
+ // only once a real signals/sample frame lands (see waitForFirstFrame).
115
+ this.deviceState = "connecting";
116
+ this.proc.stdout?.on("data", (c) => this.ingest(c.toString()));
117
+ // §11.6: capture daemon stderr into a bounded ring so a non-zero exit can
118
+ // explain itself (the daemon's human logs + any libusb noise go here).
119
+ this.proc.stderr?.on("data", (c) => this.ingestStderr(c.toString()));
120
+ this.proc.on("exit", (code) => this.onExit(code));
121
+ // A bad interpreter path (race after doctor) emits 'error' — handle it so it
122
+ // never bubbles up as an uncaught exception that crashes the MCP server.
123
+ this.proc.on("error", (e) => {
124
+ this.deviceState = "spawn_failed: " + e.message;
125
+ this.proc = null;
126
+ this.clearKillTimers();
127
+ this.resolveFirstFrame(null);
128
+ this.fireStopped();
129
+ });
130
+ }
131
+ /** §11.6: fold daemon stderr into a bounded ring (last STDERR_RING lines). */
132
+ ingestStderr(text) {
133
+ this.stderrBuf += text;
134
+ const parts = this.stderrBuf.split("\n");
135
+ this.stderrBuf = parts.pop() ?? "";
136
+ for (const line of parts) {
137
+ const s = line.replace(/\r$/, "");
138
+ if (s.length === 0)
139
+ continue;
140
+ this.stderrLines.push(s);
141
+ }
142
+ if (this.stderrLines.length > TraceSession.STDERR_RING) {
143
+ this.stderrLines.splice(0, this.stderrLines.length - TraceSession.STDERR_RING);
144
+ }
145
+ }
146
+ /** §11.6: the last `n` captured daemon stderr lines, newest last. */
147
+ stderrTail(n = TraceSession.STDERR_RING) {
148
+ return this.stderrLines.slice(-n).join("\n");
149
+ }
150
+ /** Daemon exited. §11.6: if it died non-zero WITHOUT emitting an error frame,
151
+ * synthesize an `error: <stderr tail>` device_state so status/start never
152
+ * reports a stale "running" for a process that's actually dead. */
153
+ onExit(code) {
154
+ if (code && code !== 0 && !this.sawErrorFrame) {
155
+ const last = this.stderrLines[this.stderrLines.length - 1];
156
+ this.deviceState = "error: " + (last && last.length > 0 ? last : `daemon exited code ${code}`);
157
+ }
158
+ else if (this.deviceState === "connecting" || this.deviceState === "running") {
159
+ // Clean (or already-error) exit with no lingering fault → mark exited.
160
+ this.deviceState = "exited";
161
+ }
162
+ this.proc = null;
163
+ this.clearKillTimers();
164
+ // Unblock any start() still waiting — the process is gone.
165
+ this.resolveFirstFrame(null);
166
+ this.fireStopped();
167
+ }
168
+ ingest(text) {
169
+ this.stdoutBuf += text;
170
+ const parts = this.stdoutBuf.split("\n");
171
+ this.stdoutBuf = parts.pop() ?? "";
172
+ for (const line of parts) {
173
+ const f = parseFrame(line);
174
+ if (!f)
175
+ continue;
176
+ // §11.6: the first machine frame (signals|sample|error) unblocks start().
177
+ this.resolveFirstFrame(f);
178
+ if (f.type === "sample") {
179
+ this.buffer.push({ t: f.t, values: f.values });
180
+ this.deviceState = "running";
181
+ }
182
+ else if (f.type === "status")
183
+ this.deviceState = f.device_state;
184
+ else if (f.type === "error") {
185
+ this.deviceState = "error: " + f.error;
186
+ this.sawErrorFrame = true;
187
+ }
188
+ else if (f.type === "signals") {
189
+ this.reconcileSignals(f.signals.map((s) => s.name));
190
+ // Resolve the oldest pending add/remove with the now-reconciled set.
191
+ const pend = this.pendingReconcile.shift();
192
+ if (pend) {
193
+ clearTimeout(pend.timer);
194
+ pend.resolve(this.buffer.signalNames());
195
+ }
196
+ }
197
+ this.onFrameExtra?.(f);
198
+ }
199
+ }
200
+ /** Reconcile the buffer's watched-signal set to match the daemon's authoritative
201
+ * list (from a "signals" frame): add any new names, drop any no longer present. */
202
+ reconcileSignals(names) {
203
+ const want = new Set(names);
204
+ for (const have of this.buffer.signalNames()) {
205
+ if (!want.has(have))
206
+ this.buffer.removeSignal(have);
207
+ }
208
+ for (const n of names)
209
+ this.buffer.addSignal(n);
210
+ }
211
+ /** Add signals to the live poll set (NDJSON `add` to daemon stdin).
212
+ * Resolves with the POST-reconcile signal-name set once the daemon's next
213
+ * "signals" frame is ingested (PROTOCOL §4); ~2 s timeout → resolves with the
214
+ * current `buffer.signalNames()` so a missing frame never hangs the caller. */
215
+ addSignals(specs) {
216
+ if (!this.proc?.stdin)
217
+ throw new Error("No running trace to add signals to.");
218
+ this.proc.stdin.write(JSON.stringify({ cmd: "add", signals: specs }) + "\n");
219
+ return this.awaitReconcile();
220
+ }
221
+ /** Remove signals from the live poll set (NDJSON `remove` to daemon stdin).
222
+ * Same post-reconcile / timeout contract as addSignals. */
223
+ removeSignals(specs) {
224
+ if (!this.proc?.stdin)
225
+ throw new Error("No running trace to remove signals from.");
226
+ this.proc.stdin.write(JSON.stringify({ cmd: "remove", signals: specs }) + "\n");
227
+ return this.awaitReconcile();
228
+ }
229
+ /** Queue a resolver fired by the next "signals" frame; falls back to the
230
+ * current signal set after ~2 s so callers can't hang on a silent daemon. */
231
+ awaitReconcile(timeoutMs = 2000) {
232
+ return new Promise((resolve) => {
233
+ const entry = {
234
+ resolve,
235
+ timer: setTimeout(() => {
236
+ const i = this.pendingReconcile.indexOf(entry);
237
+ if (i >= 0)
238
+ this.pendingReconcile.splice(i, 1);
239
+ resolve(this.buffer.signalNames());
240
+ }, timeoutMs),
241
+ };
242
+ this.pendingReconcile.push(entry);
243
+ });
244
+ }
245
+ /** §11.6: resolve on the FIRST ingested frame (signals|sample|error), or
246
+ * `null` on timeout / if the daemon exits first. Lets MCP `start` report a
247
+ * truthful device_state instead of an optimistic "running". Idempotent:
248
+ * if a frame was already seen before the call, resolves immediately. */
249
+ waitForFirstFrame(timeoutMs = 3000) {
250
+ // Already exited with nothing, or a frame already arrived → settle now.
251
+ if (this.firstFrameSeen || this.proc === null) {
252
+ return Promise.resolve(this.lastFirstFrame);
253
+ }
254
+ return new Promise((resolve) => {
255
+ const entry = {
256
+ resolve,
257
+ timer: setTimeout(() => {
258
+ const i = this.firstFrameWaiters.indexOf(entry);
259
+ if (i >= 0)
260
+ this.firstFrameWaiters.splice(i, 1);
261
+ resolve(null);
262
+ }, timeoutMs),
263
+ };
264
+ this.firstFrameWaiters.push(entry);
265
+ });
266
+ }
267
+ // Remember the first frame so a waitForFirstFrame() called *after* it arrived
268
+ // still gets it (start() awaits right after spawn, but be robust to ordering).
269
+ lastFirstFrame = null;
270
+ /** Fire all pending first-frame waiters once. `f` is the triggering frame
271
+ * (or null when the daemon exited / spawn-errored before any frame). */
272
+ resolveFirstFrame(f) {
273
+ if (f && !this.firstFrameSeen) {
274
+ this.firstFrameSeen = true;
275
+ this.lastFirstFrame = f;
276
+ }
277
+ if (this.firstFrameWaiters.length === 0)
278
+ return;
279
+ const waiters = this.firstFrameWaiters;
280
+ this.firstFrameWaiters = [];
281
+ for (const w of waiters) {
282
+ clearTimeout(w.timer);
283
+ w.resolve(f ?? this.lastFirstFrame);
284
+ }
285
+ }
286
+ /** Cancel any pending SIGTERM/SIGKILL escalation timers (clean exit). */
287
+ clearKillTimers() {
288
+ if (this.termTimer) {
289
+ clearTimeout(this.termTimer);
290
+ this.termTimer = null;
291
+ }
292
+ if (this.killTimer) {
293
+ clearTimeout(this.killTimer);
294
+ this.killTimer = null;
295
+ }
296
+ }
297
+ /** §11.5 guaranteed teardown — escalate {cmd:stop} → SIGTERM → SIGKILL.
298
+ * Idempotent and safe to call when `proc` is already null (the daemon may
299
+ * have self-exited on a connect failure / crash / target reset). A wedged
300
+ * daemon blocked in uninterruptible libusb ignores SIGTERM, so the SIGKILL
301
+ * fallback is the ONLY thing that guarantees no `pkill -9` is ever needed.
302
+ * The escalation timers are cleared on real exit (see onExit) so a clean
303
+ * shutdown never fires a stray kill at a recycled PID.
304
+ *
305
+ * §12.1: stop() ONLY ends the daemon now. The web UI is owned by the
306
+ * persistent Dashboard singleton (see trace-webui.ts), which OUTLIVES the
307
+ * session — the MCP `stop` handler calls dashboard.unbind() so the server
308
+ * keeps listening and the dashboard tab survives across traces. */
309
+ stop() {
310
+ const p = this.proc;
311
+ if (!p) {
312
+ this.clearKillTimers();
313
+ return;
314
+ } // already gone → nothing to kill
315
+ if (this.termTimer || this.killTimer)
316
+ return; // a stop() is already in flight (idempotent)
317
+ try {
318
+ p.stdin?.write(JSON.stringify({ cmd: "stop" }) + "\n");
319
+ }
320
+ catch { /* */ }
321
+ // 1) polite SIGTERM after ~1.5 s if {cmd:stop} didn't already end it.
322
+ this.termTimer = setTimeout(() => {
323
+ this.termTimer = null;
324
+ try {
325
+ p.kill("SIGTERM");
326
+ }
327
+ catch { /* */ }
328
+ }, 1500);
329
+ // 2) hard SIGKILL ~3 s later if the process is STILL alive (wedged libusb).
330
+ this.killTimer = setTimeout(() => {
331
+ this.killTimer = null;
332
+ if (this.proc) {
333
+ try {
334
+ p.kill("SIGKILL");
335
+ }
336
+ catch { /* */ }
337
+ }
338
+ }, 4500);
339
+ }
340
+ isRunning() { return this.proc !== null; }
341
+ }
342
+ // Module-level singleton — one active trace session per server.
343
+ let active = null;
344
+ export function getActiveSession() { return active; }
345
+ export function setActiveSession(s) { active = s; }
346
+ //# sourceMappingURL=trace-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace-session.js","sourceRoot":"","sources":["../../src/tools/trace-session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export interface TraceSymbol {
2
+ name: string;
3
+ address: number;
4
+ encoding: string;
5
+ size: number;
6
+ kind?: "scalar" | "array" | "struct" | "union" | "other";
7
+ dims?: number[];
8
+ count?: number;
9
+ elem_size?: number;
10
+ elem_encoding?: string;
11
+ members?: string[];
12
+ }
13
+ export interface SymbolsResult {
14
+ success: boolean;
15
+ symbols: TraceSymbol[];
16
+ error?: string;
17
+ }
18
+ /** tracer/swd_tracer.py — resolved relative to the dist/ or src/ tree → repo root /tracer. */
19
+ export declare function daemonPath(): string;
20
+ export declare function resolvedPython(): string;
21
+ export declare function resolvedElf(): string;
22
+ /** Pure: extract the symbols JSON from mixed daemon output. */
23
+ export declare function parseSymbolsOutput(out: string): SymbolsResult;
24
+ export declare function listSymbols(query?: string, elf?: string, signal?: AbortSignal): Promise<SymbolsResult>;
@@ -0,0 +1,44 @@
1
+ // src/tools/trace-symbols.ts
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ import { runArgvStream } from "../utils/exec.js";
5
+ import { resolveConfigValue } from "../utils/userConfig.js";
6
+ import { STM_ELF_DEFAULT } from "../config.js";
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+ /** tracer/swd_tracer.py — resolved relative to the dist/ or src/ tree → repo root /tracer. */
9
+ export function daemonPath() {
10
+ // dist/tools/trace-symbols.js → ../../tracer ; src/tools/*.ts → ../../tracer
11
+ return path.resolve(__dirname, "..", "..", "tracer", "swd_tracer.py");
12
+ }
13
+ export function resolvedPython() {
14
+ return resolveConfigValue("pyocd_python", "CROSSPAD_TRACE_PYTHON", process.env.CROSSPAD_TRACE_PYTHON, "python3");
15
+ }
16
+ export function resolvedElf() {
17
+ return resolveConfigValue("stm_elf_path", "CROSSPAD_STM_ELF", process.env.CROSSPAD_STM_ELF, STM_ELF_DEFAULT);
18
+ }
19
+ /** Pure: extract the symbols JSON from mixed daemon output. */
20
+ export function parseSymbolsOutput(out) {
21
+ const lines = out.split("\n").map((l) => l.trim()).filter(Boolean);
22
+ for (let i = lines.length - 1; i >= 0; i--) {
23
+ const l = lines[i];
24
+ if (l.startsWith("{") && l.includes('"symbols"')) {
25
+ try {
26
+ const obj = JSON.parse(l);
27
+ if (Array.isArray(obj.symbols))
28
+ return { success: true, symbols: obj.symbols };
29
+ }
30
+ catch { /* keep scanning */ }
31
+ }
32
+ }
33
+ return { success: false, symbols: [], error: out.split("\n").filter(Boolean).slice(-3).join(" | ") || "no output" };
34
+ }
35
+ export async function listSymbols(query, elf, signal) {
36
+ const argv = ["symbols", "--elf", elf ?? resolvedElf()];
37
+ if (query)
38
+ argv.push("--query", query);
39
+ let out = "";
40
+ await runArgvStream(resolvedPython(), [daemonPath(), ...argv], process.cwd(), (s, line) => { if (s === "stdout")
41
+ out += line + "\n"; }, 30_000, signal);
42
+ return parseSymbolsOutput(out);
43
+ }
44
+ //# sourceMappingURL=trace-symbols.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace-symbols.js","sourceRoot":"","sources":["../../src/tools/trace-symbols.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { TraceSession } from "./trace-session.js";
2
+ export declare function buildUiUrl(port: number): string;
3
+ /** Allow a WS upgrade only from a loopback Origin, or when no Origin is sent
4
+ * (native/non-browser clients). Defends against cross-site WebSocket hijacking. */
5
+ export declare function originIsLoopback(info: {
6
+ origin?: string;
7
+ }): boolean;
8
+ /** §12.4 best-effort browser opener. Spawns the platform opener detached so the
9
+ * MCP server never owns the browser process, swallows every failure (a missing
10
+ * opener / headless box must never throw), and SKIPS when:
11
+ * - CROSSPAD_TRACE_NO_BROWSER is set (explicit opt-out), or
12
+ * - on linux when neither DISPLAY nor WAYLAND_DISPLAY is set (headless — no GUI
13
+ * to pop, so xdg-open would just error).
14
+ * Returns true if an opener was actually spawned, false if skipped. */
15
+ export declare function openInBrowser(url: string): boolean;
16
+ /**
17
+ * §12.1 Persistent dashboard singleton. ONE http+ws server that OUTLIVES trace
18
+ * sessions for the MCP server's whole lifetime. `currentSession` is mutable:
19
+ * `bind` attaches a new trace (subscribes to its frames + broadcasts a
20
+ * `trace_start`); `unbind` detaches it (broadcasts `trace_end`) WITHOUT tearing
21
+ * the server down, so a browser / VS Code Simple Browser tab stays connected
22
+ * across start→stop→start cycles. The port (7373) is therefore stable.
23
+ */
24
+ export declare class Dashboard {
25
+ private server;
26
+ private wss;
27
+ private clients;
28
+ private url;
29
+ private currentSession;
30
+ port: number;
31
+ /** §12.1 start listening once; idempotent — a second call reuses the running
32
+ * server and resolves with the same url. The server keeps listening across
33
+ * every trace, so the dashboard tab never goes dead. */
34
+ ensureStarted(preferredPort?: number): Promise<string>;
35
+ /** §12.1 attach a new trace: become its frame sink, broadcast trace_start. */
36
+ bind(session: TraceSession): void;
37
+ /** §12.1 detach the current trace: broadcast trace_end, go idle. The server
38
+ * KEEPS listening (clients stay connected, ready for the next trace). */
39
+ unbind(): void;
40
+ /** §12.4: any WS client connected right now? `start` uses this to auto-open
41
+ * the browser ONLY when the dashboard isn't already open in some tab. */
42
+ hasClients(): boolean;
43
+ /** §9/§12.3 GET /symbols handler: resolve symbols against the active session's
44
+ * ELF, or — when idle — the configured DEFAULT ELF so autocomplete keeps
45
+ * working between traces. Failures reply 500 rather than crashing the server. */
46
+ private serveSymbols;
47
+ /** Parse a browser → server WS message and forward `add`/`remove` to the bound
48
+ * session. Ignored when idle (no currentSession) or when malformed (bad JSON,
49
+ * wrong shape) — silently, per §5. */
50
+ private handleInbound;
51
+ private broadcast;
52
+ }
53
+ export declare function getDashboard(): Dashboard;