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.
- package/.claude-plugin/marketplace.json +13 -0
- package/.claude-plugin/plugin.json +14 -0
- package/.mcp.json +9 -0
- package/README.md +95 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +369 -49
- package/dist/index.js.map +1 -1
- package/dist/tools/idf-flash.js +2 -2
- package/dist/tools/idf-flash.js.map +1 -1
- package/dist/tools/idf-monitor.d.ts +3 -1
- package/dist/tools/idf-monitor.js +19 -3
- package/dist/tools/idf-monitor.js.map +1 -1
- package/dist/tools/midi.js +20 -16
- package/dist/tools/midi.js.map +1 -1
- package/dist/tools/symbols.d.ts +3 -1
- package/dist/tools/symbols.js +31 -1
- package/dist/tools/symbols.js.map +1 -1
- package/dist/tools/trace-buffer.d.ts +40 -0
- package/dist/tools/trace-buffer.js +74 -0
- package/dist/tools/trace-buffer.js.map +1 -0
- package/dist/tools/trace-device.d.ts +10 -0
- package/dist/tools/trace-device.js +26 -0
- package/dist/tools/trace-device.js.map +1 -0
- package/dist/tools/trace-doctor.d.ts +43 -0
- package/dist/tools/trace-doctor.js +150 -0
- package/dist/tools/trace-doctor.js.map +1 -0
- package/dist/tools/trace-export.d.ts +4 -0
- package/dist/tools/trace-export.js +14 -0
- package/dist/tools/trace-export.js.map +1 -0
- package/dist/tools/trace-session.d.ts +118 -0
- package/dist/tools/trace-session.js +346 -0
- package/dist/tools/trace-session.js.map +1 -0
- package/dist/tools/trace-symbols.d.ts +24 -0
- package/dist/tools/trace-symbols.js +44 -0
- package/dist/tools/trace-symbols.js.map +1 -0
- package/dist/tools/trace-webui.d.ts +53 -0
- package/dist/tools/trace-webui.js +222 -0
- package/dist/tools/trace-webui.js.map +1 -0
- package/dist/utils/device.d.ts +5 -0
- package/dist/utils/device.js +43 -15
- package/dist/utils/device.js.map +1 -1
- package/dist/utils/exec.js +26 -0
- package/dist/utils/exec.js.map +1 -1
- package/dist/utils/userConfig.d.ts +13 -0
- package/dist/utils/userConfig.js +43 -0
- package/dist/utils/userConfig.js.map +1 -0
- package/package.json +12 -4
- package/skills/crosspad/SKILL.md +58 -0
- package/skills/crosspad/reference/faq.md +40 -0
- package/skills/crosspad/reference/install.md +84 -0
- package/skills/crosspad/reference/repos.md +29 -0
- package/skills/crosspad/reference/role-contributor.md +64 -0
- package/skills/crosspad/reference/role-fw-dev.md +44 -0
- package/skills/crosspad/reference/role-user.md +49 -0
- package/skills/crosspad/reference/tools.md +68 -0
- package/skills/crosspad/scripts/doctor.sh +65 -0
- package/skills/crosspad/scripts/setup.sh +53 -0
- package/skills/swd-tracer/SKILL.md +135 -0
- package/skills/swd-tracer/reference/signals.md +42 -0
- package/skills/swd-tracer/scripts/detect-env.sh +61 -0
- package/skills/swd-tracer/scripts/install-udev-rules.sh +25 -0
- package/skills/swd-tracer/scripts/setup-venv.sh +26 -0
- package/tracer/PROTOCOL.md +260 -0
- package/tracer/README.md +327 -0
- package/tracer/swd_tracer.py +1066 -0
- 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;
|