crosspad-mcp-server 9.1.1 → 9.2.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/dist/config.d.ts +7 -0
- package/dist/config.js +15 -0
- package/dist/config.js.map +1 -1
- package/dist/index.js +125 -25
- package/dist/index.js.map +1 -1
- package/dist/tools/stm-build.d.ts +23 -0
- package/dist/tools/stm-build.js +78 -0
- package/dist/tools/stm-build.js.map +1 -0
- package/dist/tools/stm-flash.d.ts +36 -0
- package/dist/tools/stm-flash.js +112 -0
- package/dist/tools/stm-flash.js.map +1 -0
- package/dist/tools/trace-session.d.ts +12 -0
- package/dist/tools/trace-session.js +69 -0
- package/dist/tools/trace-session.js.map +1 -1
- package/dist/tools/trace-webui.d.ts +14 -7
- package/dist/tools/trace-webui.js +77 -33
- package/dist/tools/trace-webui.js.map +1 -1
- package/dist/tools/trace-write.d.ts +9 -0
- package/dist/tools/trace-write.js +32 -0
- package/dist/tools/trace-write.js.map +1 -0
- package/dist/utils/userConfig.d.ts +9 -0
- package/dist/utils/userConfig.js.map +1 -1
- package/package.json +2 -1
- package/skills/swd-tracer/SKILL.md +48 -0
- package/tracer/PROTOCOL.md +69 -0
- package/tracer/swd_tracer.py +449 -12
- package/tracer/ui/config.js +40 -0
- package/tracer/ui/connection.js +170 -0
- package/tracer/ui/index.html +50 -798
- package/tracer/ui/main.js +30 -0
- package/tracer/ui/plot.js +337 -0
- package/tracer/ui/signals.js +39 -0
- package/tracer/ui/state.js +57 -0
- package/tracer/ui/stats.js +52 -0
- package/tracer/ui/style.css +97 -0
- package/tracer/ui/symbols.js +80 -0
- package/tracer/ui/toolbar.js +95 -0
- package/tracer/ui/util.js +62 -0
- package/tracer/ui/watchlist.js +179 -0
- package/vscode-extension/README.md +32 -0
- package/vscode-extension/extension.js +56 -0
- package/vscode-extension/package.json +14 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/* WebSocket link + connection/trace state machine + inbound frame dispatch +
|
|
2
|
+
outbound commands (PROTOCOL §12.5).
|
|
3
|
+
|
|
4
|
+
The Node UI server is a PERSISTENT singleton: it stays up across trace
|
|
5
|
+
start/stop and only the bound session comes and goes. So we treat the socket
|
|
6
|
+
and the trace as two INDEPENDENT lifecycles:
|
|
7
|
+
|
|
8
|
+
connState — the WebSocket link: "connected" / "reconnecting" / "disconnected".
|
|
9
|
+
traceState — whether a trace is producing samples: "active" / "idle" / "ended".
|
|
10
|
+
|
|
11
|
+
The connection dot reflects connState; the banner reflects traceState. The
|
|
12
|
+
reconnect loop uses exponential backoff and retries forever; a successful open
|
|
13
|
+
resets it. The manual Reconnect button reuses the same loop. */
|
|
14
|
+
|
|
15
|
+
import { state, sigs, fsState, fsNoteSample } from "./state.js";
|
|
16
|
+
import { toast } from "./util.js";
|
|
17
|
+
import { reconcile, push, toDesc } from "./signals.js";
|
|
18
|
+
import { loadSymbols } from "./symbols.js";
|
|
19
|
+
|
|
20
|
+
let ws=null;
|
|
21
|
+
let connState="reconnecting"; // flips to "connected" on open
|
|
22
|
+
let traceState="idle"; // until hello.active/trace_start says otherwise
|
|
23
|
+
let reconnectTimer=null;
|
|
24
|
+
let reconnectDelay=0;
|
|
25
|
+
let manualClose=false;
|
|
26
|
+
const RECONNECT_MIN=500;
|
|
27
|
+
const RECONNECT_MAX=5000;
|
|
28
|
+
|
|
29
|
+
export function getConnState(){ return connState; }
|
|
30
|
+
export function hasSocket(){ return !!ws; }
|
|
31
|
+
|
|
32
|
+
/* Open a fresh socket. Never throws; failure falls through to onclose →
|
|
33
|
+
scheduleReconnect, so the loop is self-healing. */
|
|
34
|
+
export function connect(){
|
|
35
|
+
if(reconnectTimer){clearTimeout(reconnectTimer);reconnectTimer=null;}
|
|
36
|
+
let sock;
|
|
37
|
+
try{ sock=new WebSocket("ws://"+location.host); }
|
|
38
|
+
catch(_){ scheduleReconnect(); return; }
|
|
39
|
+
ws=sock;
|
|
40
|
+
sock.onopen=()=>{
|
|
41
|
+
if(ws!==sock)return;
|
|
42
|
+
reconnectDelay=0;
|
|
43
|
+
setConn("connected");
|
|
44
|
+
setBanner("syncing","sync"); // real trace state arrives via the fresh hello
|
|
45
|
+
loadSymbols(); // refresh autocomplete on every (re)connect
|
|
46
|
+
};
|
|
47
|
+
sock.onclose=()=>{
|
|
48
|
+
if(ws!==sock)return;
|
|
49
|
+
ws=null;
|
|
50
|
+
if(manualClose){manualClose=false;}
|
|
51
|
+
scheduleReconnect();
|
|
52
|
+
};
|
|
53
|
+
sock.onerror=()=>{};
|
|
54
|
+
sock.onmessage=(e)=>{ try{ handleFrame(e.data); }catch(_){} };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function scheduleReconnect(){
|
|
58
|
+
setConn("reconnecting");
|
|
59
|
+
if(reconnectTimer)return;
|
|
60
|
+
reconnectDelay=reconnectDelay?Math.min(reconnectDelay*2,RECONNECT_MAX):RECONNECT_MIN;
|
|
61
|
+
reconnectTimer=setTimeout(()=>{reconnectTimer=null;connect();},reconnectDelay);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* Drop the current socket and retry now at the minimum delay. */
|
|
65
|
+
export function forceReconnect(){
|
|
66
|
+
reconnectDelay=0;
|
|
67
|
+
if(reconnectTimer){clearTimeout(reconnectTimer);reconnectTimer=null;}
|
|
68
|
+
const old=ws; ws=null;
|
|
69
|
+
if(old){
|
|
70
|
+
manualClose=true;
|
|
71
|
+
try{old.onopen=old.onclose=old.onerror=old.onmessage=null;}catch(_){}
|
|
72
|
+
try{old.close();}catch(_){}
|
|
73
|
+
}
|
|
74
|
+
setConn("reconnecting");
|
|
75
|
+
connect();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Parse + dispatch one inbound frame. Defensive: tolerates junk/unknown/missing;
|
|
79
|
+
never throws. Note: samples are ALWAYS ingested — pause only freezes the view
|
|
80
|
+
(see toolbar.togglePause), so no data is lost across a pause. */
|
|
81
|
+
function handleFrame(data){
|
|
82
|
+
let m; try{m=JSON.parse(data);}catch(_){return;}
|
|
83
|
+
if(!m||typeof m!=="object")return;
|
|
84
|
+
switch(m.type){
|
|
85
|
+
case "hello":
|
|
86
|
+
if(Array.isArray(m.signals)) reconcile(m.signals.map(toDesc), null);
|
|
87
|
+
setTrace(m.active?"active":"idle");
|
|
88
|
+
break;
|
|
89
|
+
case "trace_start":
|
|
90
|
+
resetPlot();
|
|
91
|
+
if(Array.isArray(m.signals)) reconcile(m.signals.map(toDesc), null);
|
|
92
|
+
setTrace("active");
|
|
93
|
+
break;
|
|
94
|
+
case "trace_end":
|
|
95
|
+
setTrace("ended");
|
|
96
|
+
break;
|
|
97
|
+
case "signals":
|
|
98
|
+
if(Array.isArray(m.signals)) reconcile(m.signals, m.unresolved);
|
|
99
|
+
break;
|
|
100
|
+
case "sample":
|
|
101
|
+
if(m.values && typeof m.values==="object"){
|
|
102
|
+
if(traceState!=="active") setTrace("active");
|
|
103
|
+
const t=typeof m.t==="number"?m.t:performance.now()/1000;
|
|
104
|
+
fsNoteSample(t);
|
|
105
|
+
for(const k in m.values){const v=m.values[k]; if(typeof v==="number") push(k,t,v);}
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
case "status":
|
|
109
|
+
setStat("device: "+(m.device_state||"?"));
|
|
110
|
+
{const f=(typeof m.actual_fs==="number")?m.actual_fs:(typeof m.fs==="number"?m.fs:null);
|
|
111
|
+
if(f!=null&&isFinite(f)&&f>0) fsState.reported=f;}
|
|
112
|
+
break;
|
|
113
|
+
case "error":
|
|
114
|
+
toast("error: "+(m.error||"unknown"));
|
|
115
|
+
break;
|
|
116
|
+
default: break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* ---------- presentation of the two state machines ---------- */
|
|
121
|
+
function setStat(s){const el=document.getElementById("stat"); if(el)el.textContent=s;}
|
|
122
|
+
|
|
123
|
+
export function setConn(stateName){
|
|
124
|
+
connState=stateName;
|
|
125
|
+
const dot=document.getElementById("connDot");
|
|
126
|
+
const txt=document.getElementById("connTxt");
|
|
127
|
+
if(!dot||!txt)return;
|
|
128
|
+
dot.className="cdot";
|
|
129
|
+
if(stateName==="connected"){
|
|
130
|
+
dot.classList.add(traceState==="active"?"ok":"idle");
|
|
131
|
+
dot.textContent="●"; txt.textContent="connected";
|
|
132
|
+
}else if(stateName==="reconnecting"){
|
|
133
|
+
dot.classList.add("retry"); dot.textContent="○"; txt.textContent="reconnecting…";
|
|
134
|
+
}else{
|
|
135
|
+
dot.classList.add("down"); dot.textContent="×"; txt.textContent="disconnected";
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function setTrace(stateName){
|
|
140
|
+
traceState=(stateName==="active")?"active":(stateName==="ended"?"ended":"idle");
|
|
141
|
+
if(traceState==="active") setBanner("","live");
|
|
142
|
+
else if(traceState==="ended") setBanner("trace ended — waiting for next trace…","ended");
|
|
143
|
+
else setBanner("waiting for trace…","idle");
|
|
144
|
+
if(connState==="connected") setConn("connected");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function setBanner(text,kind){
|
|
148
|
+
const b=document.getElementById("banner");
|
|
149
|
+
if(!b)return;
|
|
150
|
+
if(kind==="live"){ b.style.display="none"; b.className="banner"; return; }
|
|
151
|
+
b.className="banner"+(kind==="ended"?" ended":"");
|
|
152
|
+
b.textContent=text;
|
|
153
|
+
b.style.display="block";
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* Clear plotted data + reset view for a fresh trace. */
|
|
157
|
+
function resetPlot(){
|
|
158
|
+
for(const[,s]of sigs) s.data=[];
|
|
159
|
+
state.viewT0=null; state.viewT1=null;
|
|
160
|
+
fsState.stamps=[]; fsState.reported=null; fsState.lastShown="";
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* ---------- outbound commands ---------- */
|
|
164
|
+
export function wsSend(o){ if(ws&&ws.readyState===1) ws.send(JSON.stringify(o)); else toast("not connected — cannot send"); }
|
|
165
|
+
export function addSpecs(specs){
|
|
166
|
+
const list=specs.map(s=>s.trim()).filter(Boolean);
|
|
167
|
+
if(!list.length)return;
|
|
168
|
+
wsSend({cmd:"add",signals:list});
|
|
169
|
+
}
|
|
170
|
+
export function removeSignal(name){ wsSend({cmd:"remove",signals:[name]}); }
|