crosspad-mcp-server 9.1.0 → 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/.claude-plugin/plugin.json +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/config.js +15 -0
- package/dist/config.js.map +1 -1
- package/dist/index.js +129 -29
- 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 +460 -15
- 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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crosspad",
|
|
3
3
|
"description": "CrossPad development toolkit: the crosspad-mcp server plus two Claude Code skills — `crosspad` (onboarding/help: ecosystem map, install & config, per-role guides, tool cheat-sheet, FAQ — use this first) and `swd-tracer` (real-time SWD variable tracing for CrossPad r20 / STM32G0B1 firmware over ST-Link).",
|
|
4
|
-
"version": "9.1.
|
|
4
|
+
"version": "9.1.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mateusz Czarnecki"
|
|
7
7
|
},
|
package/dist/config.d.ts
CHANGED
|
@@ -5,6 +5,13 @@ export declare const CROSSPAD_IDF_ROOT: string;
|
|
|
5
5
|
export declare const CROSSPAD_STM_ROOT: string;
|
|
6
6
|
export declare const STM_ELF_DEFAULT: string;
|
|
7
7
|
export declare const TRACE_DIR_DEFAULT: string;
|
|
8
|
+
export declare const STM_PROJECT_NAME = "CrossPad_STM32_r20";
|
|
9
|
+
export declare const STM_FLASH_ADDR = "0x08000000";
|
|
10
|
+
export type StmPreset = "Debug" | "Release";
|
|
11
|
+
/** Build directory for a preset: <repo>/build/<preset>. */
|
|
12
|
+
export declare function stmBuildDir(preset: StmPreset): string;
|
|
13
|
+
/** Artifact path for a preset, e.g. build/Debug/CrossPad_STM32_r20.elf. */
|
|
14
|
+
export declare function stmArtifact(preset: StmPreset, ext: "elf" | "bin" | "hex"): string;
|
|
8
15
|
export declare const IDF_PATH: string;
|
|
9
16
|
export declare const VCVARSALL: string;
|
|
10
17
|
export declare const VCPKG_TOOLCHAIN: string;
|
package/dist/config.js
CHANGED
|
@@ -23,6 +23,21 @@ export const CROSSPAD_STM_ROOT = process.env.CROSSPAD_STM_ROOT || path.join(GIT_
|
|
|
23
23
|
// Trace defaults (overridable later via user config at call sites).
|
|
24
24
|
export const STM_ELF_DEFAULT = path.join(CROSSPAD_STM_ROOT, "build", "Debug", "CrossPad_STM32_r20.elf");
|
|
25
25
|
export const TRACE_DIR_DEFAULT = path.join(CROSSPAD_STM_ROOT, "traces");
|
|
26
|
+
// ─── STM32 build / flash ─────────────────────────────────────────────────
|
|
27
|
+
// CMake project name (artifact basename) and the CMakePresets.json preset
|
|
28
|
+
// names. binaryDir is build/<preset> per the preset's `binaryDir` template.
|
|
29
|
+
export const STM_PROJECT_NAME = "CrossPad_STM32_r20";
|
|
30
|
+
// Flash base address — STM32G0 internal flash origin (matches the *.ld linker
|
|
31
|
+
// script ORIGIN and the STM32_Programmer_CLI invocations in CLAUDE.md).
|
|
32
|
+
export const STM_FLASH_ADDR = "0x08000000";
|
|
33
|
+
/** Build directory for a preset: <repo>/build/<preset>. */
|
|
34
|
+
export function stmBuildDir(preset) {
|
|
35
|
+
return path.join(CROSSPAD_STM_ROOT, "build", preset);
|
|
36
|
+
}
|
|
37
|
+
/** Artifact path for a preset, e.g. build/Debug/CrossPad_STM32_r20.elf. */
|
|
38
|
+
export function stmArtifact(preset, ext) {
|
|
39
|
+
return path.join(stmBuildDir(preset), `${STM_PROJECT_NAME}.${ext}`);
|
|
40
|
+
}
|
|
26
41
|
// ═══════════════════════════════════════════════════════════════════════
|
|
27
42
|
// ESP-IDF SDK PATH — fallback chain
|
|
28
43
|
// ═══════════════════════════════════════════════════════════════════════
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;AACvD,MAAM,CAAC,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC;AAEpD,0EAA0E;AAC1E,uEAAuE;AACvE,0EAA0E;AAE1E,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;AAE/E,0EAA0E;AAC1E,wDAAwD;AACxD,8DAA8D;AAC9D,0EAA0E;AAE1E,MAAM,CAAC,MAAM,gBAAgB,GAC3B,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;AAEpE,MAAM,CAAC,MAAM,iBAAiB,GAC5B,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;AAEtE,MAAM,qBAAqB,GACzB,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAEtE,MAAM,kBAAkB,GACtB,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;AAExE,MAAM,iBAAiB,GACrB,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;AAEtE,0EAA0E;AAC1E,+EAA+E;AAC/E,6EAA6E;AAC7E,MAAM,CAAC,MAAM,iBAAiB,GAC5B,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;AAE5E,oEAAoE;AACpE,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,OAAO,EAAE,OAAO,EAAE,wBAAwB,CAAC,CAAC;AACxG,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,8 @@ import { BIN_EXE as _BIN_EXE } from "./config.js";
|
|
|
11
11
|
import { crosspadLog } from "./tools/log.js";
|
|
12
12
|
import { crosspadIdfBuild } from "./tools/idf-build.js";
|
|
13
13
|
import { crosspadIdfFlash, crosspadIdfOta } from "./tools/idf-flash.js";
|
|
14
|
+
import { crosspadStmBuild } from "./tools/stm-build.js";
|
|
15
|
+
import { crosspadStmFlash } from "./tools/stm-flash.js";
|
|
14
16
|
import { crosspadIdfMonitor } from "./tools/idf-monitor.js";
|
|
15
17
|
import { listDevices } from "./utils/device.js";
|
|
16
18
|
import { crosspadTest } from "./tools/test.js";
|
|
@@ -26,10 +28,10 @@ import { crosspadSettingsGet, crosspadSettingsSet } from "./tools/settings.js";
|
|
|
26
28
|
import { crosspadMidiSend } from "./tools/midi.js";
|
|
27
29
|
import { crosspadAppList, crosspadAppInstall, crosspadAppRemove, crosspadAppUpdate, crosspadAppSync, } from "./tools/app-manager.js";
|
|
28
30
|
import { runDoctor, realProbe } from "./tools/trace-doctor.js";
|
|
29
|
-
import { setConfigValue } from "./utils/userConfig.js";
|
|
31
|
+
import { setConfigValue, resolveConfigValue } from "./utils/userConfig.js";
|
|
30
32
|
import { listSymbols } from "./tools/trace-symbols.js";
|
|
31
33
|
import { getDeviceState } from "./tools/trace-device.js";
|
|
32
|
-
import { TraceSession, getActiveSession, setActiveSession } from "./tools/trace-session.js";
|
|
34
|
+
import { TraceSession, getActiveSession, setActiveSession, traceWrite, traceCall } from "./tools/trace-session.js";
|
|
33
35
|
import { getDashboard, openInBrowser, buildUiUrl } from "./tools/trace-webui.js";
|
|
34
36
|
import { writeCsv } from "./tools/trace-export.js";
|
|
35
37
|
// Server instructions — MCP clients prepend these to the LLM system prompt.
|
|
@@ -42,14 +44,15 @@ You have access to the CrossPad MCP server, which exposes purpose-built tools fo
|
|
|
42
44
|
|
|
43
45
|
NEW TO A CROSSPAD REPO OR SETTING UP? Use the \`crosspad\` skill first — it maps the ecosystem (repos, MCP tools, roles), walks install/config, and routes to per-role guides + an FAQ. Run \`bash scripts/doctor.sh\` from that skill to check your environment.
|
|
44
46
|
|
|
45
|
-
TOOL TAGS — a tool description starting with a bracket tag has a hardware
|
|
47
|
+
TOOL TAGS — a tool description starting with a bracket tag has a platform/hardware precondition. Atomic tokens: \`[PC]\` = the crosspad-pc repo + host toolchain (build/test/inspect — no running sim); \`[PC sim]\` = a RUNNING simulator instance (launch it with crosspad_run first); \`[ESP HW]\` = a connected ESP32-S3 device; \`[STM HW]\` = an ST-Link + STM32 board. Tokens combine: \`+\` = both contexts at once (e.g. \`[PC + ESP]\`); \`|\` = either, depending on a param (e.g. \`[PC | ESP HW]\`). Untagged tools (code search, repo/git, apps registry) need no hardware.
|
|
46
48
|
|
|
47
49
|
WHEN TO USE THESE TOOLS — in any conversation that touches a CrossPad repo, prefer the crosspad_* tools over raw shell equivalents:
|
|
48
50
|
|
|
49
51
|
- Inspecting code → crosspad_search_symbols (NOT \`grep -r\`); crosspad_list_interfaces; crosspad_interface_implementations.
|
|
50
52
|
- Repo state → crosspad_repo_status (NOT \`git status\` across N repos); crosspad_repo_diff for submodule drift.
|
|
51
53
|
- Building PC sim → crosspad_check platform=pc → crosspad_build platform=pc (NOT raw cmake/ninja). Then crosspad_run; crosspad_kill when done.
|
|
52
|
-
- Building
|
|
54
|
+
- Building ESP fw → crosspad_build platform=idf (NOT raw \`idf.py build\`); crosspad_flash target=esp transport=uart|ota.
|
|
55
|
+
- Building STM fw → crosspad_build platform=stm (NOT raw cmake/STM32_Programmer_CLI for CrossPad_STM32_r20); crosspad_flash target=stm method=swd|dfu.
|
|
53
56
|
- Tests → crosspad_test_run (NOT raw catch2 binary).
|
|
54
57
|
- Sim interaction → crosspad_screenshot, crosspad_input, crosspad_midi, crosspad_stats, crosspad_settings_get/set.
|
|
55
58
|
- Apps (registry) → crosspad_apps_list / install / remove / update / sync (NOT manual submodule git ops).
|
|
@@ -212,8 +215,12 @@ const O_BuildCheck = {
|
|
|
212
215
|
};
|
|
213
216
|
const O_Flash = {
|
|
214
217
|
success: z.boolean(),
|
|
215
|
-
method: z.enum(["uart", "ota"]),
|
|
216
|
-
port
|
|
218
|
+
method: z.enum(["uart", "ota", "swd", "dfu"]),
|
|
219
|
+
// ESP transports report the serial port; STM methods report the flasher
|
|
220
|
+
// binary + firmware path instead. Both kept optional so each path validates.
|
|
221
|
+
port: z.string().optional(),
|
|
222
|
+
programmer: z.string().optional(),
|
|
223
|
+
firmware_path: z.string().optional(),
|
|
217
224
|
duration_seconds: z.number(),
|
|
218
225
|
output_tail: z.array(z.string()),
|
|
219
226
|
...ErrorField,
|
|
@@ -386,26 +393,27 @@ const O_AppAction = {
|
|
|
386
393
|
// BUILD — unified across platforms (pc, idf)
|
|
387
394
|
// `platform` arg disambiguates. Modes are validated per-platform at runtime.
|
|
388
395
|
// ═══════════════════════════════════════════════════════════════════════
|
|
389
|
-
const BuildPlatform = z.enum(["pc", "idf"]).describe("Target platform: 'pc' = host simulator, 'idf' = ESP32-S3 firmware.");
|
|
396
|
+
const BuildPlatform = z.enum(["pc", "idf", "stm"]).describe("Target platform: 'pc' = host simulator, 'idf' = ESP32-S3 firmware, 'stm' = STM32G0 firmware (CrossPad r20).");
|
|
390
397
|
const PlatformPcOnly = z.enum(["pc"]).default("pc").describe("Platform — currently only 'pc' is supported here.");
|
|
391
398
|
server.registerTool("crosspad_build", {
|
|
392
|
-
description: "[PC
|
|
399
|
+
description: "[PC | ESP | STM HW] Build CrossPad for the given platform.\n" +
|
|
393
400
|
" • platform='pc' → CMake + Ninja host simulator. PREFER THIS over `cmake --build build` (picks right MSVC env on Windows, parses errors/warnings, streams progress).\n" +
|
|
394
401
|
" • platform='idf' → idf.py build for ESP32-S3 firmware. PREFER THIS over raw `idf.py build` (sources IDF env, auto-fullcleans when new apps detected, parses errors/warnings).\n" +
|
|
402
|
+
" • platform='stm' → CMake + Ninja + arm-none-eabi for STM32G0 firmware (CrossPad r20). Uses CMakePresets (Debug/Release); output is build/<preset>/CrossPad_STM32_r20.elf.\n" +
|
|
395
403
|
"Mode×platform compatibility:\n" +
|
|
396
|
-
" • incremental →
|
|
397
|
-
" • clean →
|
|
398
|
-
" • reconfigure → PC
|
|
404
|
+
" • incremental → all (default)\n" +
|
|
405
|
+
" • clean → all (wipes build dir, then builds)\n" +
|
|
406
|
+
" • reconfigure → PC & STM (re-runs cmake without wiping cache)\n" +
|
|
399
407
|
" • fullclean → IDF only (runs idf.py fullclean, then builds)",
|
|
400
408
|
inputSchema: {
|
|
401
409
|
platform: BuildPlatform,
|
|
402
410
|
mode: z.enum(["incremental", "clean", "fullclean", "reconfigure"])
|
|
403
411
|
.default("incremental")
|
|
404
|
-
.describe("Build mode. Compatibility: incremental & clean =
|
|
405
|
-
"Pick incremental for normal iteration; clean if you suspect stale artifacts; fullclean (IDF) after adding new apps; reconfigure (PC) after editing CMakeLists."),
|
|
412
|
+
.describe("Build mode. Compatibility: incremental & clean = all platforms; reconfigure = PC & STM; fullclean = IDF only. " +
|
|
413
|
+
"Pick incremental for normal iteration; clean if you suspect stale artifacts; fullclean (IDF) after adding new apps; reconfigure (PC/STM) after editing CMakeLists/presets."),
|
|
406
414
|
build_type: z.enum(["Debug", "Release", "RelWithDebInfo"])
|
|
407
415
|
.default("Debug")
|
|
408
|
-
.describe("CMake build type — PC
|
|
416
|
+
.describe("CMake build type — PC & STM (ignored for IDF; ESP32 build type comes from sdkconfig). STM maps to the Debug/Release preset (RelWithDebInfo→Release). Only honored on mode=clean|reconfigure (incremental keeps existing cache)."),
|
|
409
417
|
},
|
|
410
418
|
outputSchema: O_Build,
|
|
411
419
|
annotations: ANN_DESTRUCTIVE,
|
|
@@ -416,15 +424,21 @@ server.registerTool("crosspad_build", {
|
|
|
416
424
|
const onLine = makeProgressLogger("build-pc", extra);
|
|
417
425
|
return jsonResponse(await crosspadBuild(mode, onLine, build_type, extra.signal));
|
|
418
426
|
}
|
|
427
|
+
if (platform === "stm") {
|
|
428
|
+
if (mode === "fullclean")
|
|
429
|
+
return err("mode='fullclean' is IDF-only. STM supports: incremental, clean, reconfigure.");
|
|
430
|
+
const onLine = makeProgressLogger("build-stm", extra);
|
|
431
|
+
return jsonResponse(await crosspadStmBuild(mode, onLine, build_type, extra.signal));
|
|
432
|
+
}
|
|
419
433
|
// idf
|
|
420
434
|
if (mode === "reconfigure")
|
|
421
|
-
return err("mode='reconfigure' is PC-only. IDF supports: incremental, clean, fullclean.");
|
|
435
|
+
return err("mode='reconfigure' is PC/STM-only. IDF supports: incremental, clean, fullclean.");
|
|
422
436
|
const idfMode = mode === "incremental" ? "build" : mode;
|
|
423
437
|
const onLine = makeProgressLogger("build-idf", extra);
|
|
424
438
|
return jsonResponse(await crosspadIdfBuild(idfMode, onLine, extra.signal));
|
|
425
439
|
});
|
|
426
440
|
server.registerTool("crosspad_run", {
|
|
427
|
-
description: "[PC
|
|
441
|
+
description: "[PC] Launch the built simulator binary in the background. Returns pid + exe_path. Refuses to spawn a duplicate if one is already responding on the TCP control port (use force=true to override). Fails if binary not built — call crosspad_build first. Currently PC-only (IDF firmware doesn't run on the host).",
|
|
428
442
|
inputSchema: {
|
|
429
443
|
platform: PlatformPcOnly,
|
|
430
444
|
force: z.boolean().default(false)
|
|
@@ -454,7 +468,7 @@ server.registerTool("crosspad_kill", {
|
|
|
454
468
|
annotations: ANN_DESTRUCTIVE,
|
|
455
469
|
}, async () => jsonResponse(await crosspadKill()));
|
|
456
470
|
server.registerTool("crosspad_check", {
|
|
457
|
-
description: "[PC
|
|
471
|
+
description: "[PC] Health check for a build — detects stale exe, new sources missing from build system, dirty submodules. Use before crosspad_build to decide if rebuild needed. Currently PC-only.",
|
|
458
472
|
inputSchema: {
|
|
459
473
|
platform: PlatformPcOnly,
|
|
460
474
|
},
|
|
@@ -465,16 +479,45 @@ server.registerTool("crosspad_check", {
|
|
|
465
479
|
// FLASH — unified UART/OTA into one tool with `transport` axis
|
|
466
480
|
// ═══════════════════════════════════════════════════════════════════════
|
|
467
481
|
server.registerTool("crosspad_flash", {
|
|
468
|
-
description: "[ESP HW] Flash
|
|
482
|
+
description: "[ESP HW | STM HW] Flash firmware to a connected CrossPad device.\n" +
|
|
483
|
+
"target='esp' (default) → ESP32-S3 firmware (requires prior crosspad_build platform=idf):\n" +
|
|
484
|
+
" • transport='uart' uses idf.py flash (device must be in bootloader mode).\n" +
|
|
485
|
+
" • transport='ota' uses platform-idf/tools/ota_flash.py over USB CDC (no bootloader mode required).\n" +
|
|
486
|
+
" Works on both CrossPad generations: rev <2.0 (ESP native USB) and rev 2.0 (port is the STM32 CDC bridge — STM emulates the esptool DTR/RTS auto-reset and forwards the flash to the ESP over LPUART2; rev-2.0 STM must be in passthrough mode, i.e. NOT booted with pad-4 held).\n" +
|
|
487
|
+
"target='stm' → STM32G0 firmware via STM32_Programmer_CLI (requires prior crosspad_build platform=stm):\n" +
|
|
488
|
+
" • method='swd' flashes over ST-Link (SWD).\n" +
|
|
489
|
+
" • method='dfu' flashes the USB DFU bootloader (board in ST system memory — hold pad 1 at boot or trigger boot_request_dfu).\n" +
|
|
490
|
+
" Flasher resolved from config (stm_programmer_cli) → $STM32_PROG → PATH.",
|
|
469
491
|
inputSchema: {
|
|
470
|
-
|
|
492
|
+
target: z.enum(["esp", "stm"]).default("esp").describe("'esp' = ESP32-S3 (transport uart/ota); 'stm' = STM32G0 firmware via STM32_Programmer_CLI (method swd/dfu)."),
|
|
493
|
+
transport: z.enum(["uart", "ota"]).optional().describe("ESP only. 'uart' = bootloader-mode flash via idf.py; 'ota' = USB-CDC OTA flash via ota_flash.py."),
|
|
494
|
+
method: z.enum(["swd", "dfu"]).optional().describe("STM only. 'swd' = ST-Link/SWD; 'dfu' = USB DFU system bootloader."),
|
|
471
495
|
port: Port.optional(),
|
|
496
|
+
build_type: z.enum(["Debug", "Release", "RelWithDebInfo"]).optional()
|
|
497
|
+
.describe("STM only. Selects the build/<preset> dir for the default firmware binary. Defaults to Debug."),
|
|
472
498
|
firmware_path: z.string().optional()
|
|
473
|
-
.describe("Custom firmware binary path
|
|
499
|
+
.describe("Custom firmware binary path. ESP: OTA only, defaults to <idf-root>/build/CrossPad.bin. STM: defaults to <stm-root>/build/<preset>/CrossPad_STM32_r20.bin."),
|
|
474
500
|
},
|
|
475
501
|
outputSchema: O_Flash,
|
|
476
502
|
annotations: ANN_DESTRUCTIVE,
|
|
477
|
-
}, async ({ transport, port, firmware_path }, extra) => {
|
|
503
|
+
}, async ({ target, transport, method, port, build_type, firmware_path }, extra) => {
|
|
504
|
+
if (target === "stm") {
|
|
505
|
+
if (!method)
|
|
506
|
+
return err("target='stm' requires 'method' (swd or dfu).");
|
|
507
|
+
if (transport)
|
|
508
|
+
return err("Field 'transport' is ESP-only. For STM use 'method' (swd|dfu).");
|
|
509
|
+
if (port)
|
|
510
|
+
return err("Field 'port' is ESP-only — STM flash addresses the ST-Link/DFU device, not a serial port.");
|
|
511
|
+
const onLine = makeProgressLogger(`flash-stm-${method}`, extra);
|
|
512
|
+
return jsonResponse(await crosspadStmFlash(method, build_type ?? "Debug", firmware_path, onLine, extra.signal));
|
|
513
|
+
}
|
|
514
|
+
// esp
|
|
515
|
+
if (method)
|
|
516
|
+
return err("Field 'method' is STM-only. For ESP use 'transport' (uart|ota).");
|
|
517
|
+
if (build_type)
|
|
518
|
+
return err("Field 'build_type' is STM-only (ESP build type comes from sdkconfig).");
|
|
519
|
+
if (!transport)
|
|
520
|
+
return err("target='esp' requires 'transport' (uart or ota).");
|
|
478
521
|
if (transport === "uart") {
|
|
479
522
|
if (firmware_path)
|
|
480
523
|
return err("Field 'firmware_path' is OTA-only — UART flash always uses the active build dir.");
|
|
@@ -486,7 +529,7 @@ server.registerTool("crosspad_flash", {
|
|
|
486
529
|
return jsonResponse(await crosspadIdfOta(port, firmware_path, onLine, extra.signal));
|
|
487
530
|
});
|
|
488
531
|
server.registerTool("crosspad_log", {
|
|
489
|
-
description: "[PC
|
|
532
|
+
description: "[PC | ESP HW] Capture logs (consolidated; replaces crosspad_log_pc and crosspad_log_idf in v6).\n" +
|
|
490
533
|
" • target='pc' → spawn the built sim binary, capture stdout/stderr, then kill it. " +
|
|
491
534
|
"Fields used: timeout_seconds (default 5), max_lines (default 200). `port` and `filter` MUST be omitted.\n" +
|
|
492
535
|
" • target='idf' → read serial from a connected ESP32-S3 via pyserial (no TTY needed). " +
|
|
@@ -537,6 +580,7 @@ export function setTraceBrowserOpener(fn) { traceBrowserOpener = fn; }
|
|
|
537
580
|
const TraceAction = z.enum([
|
|
538
581
|
"doctor", "config_set", "symbols", "start", "stop",
|
|
539
582
|
"add", "remove", "status", "read", "save", "device_state", "ui",
|
|
583
|
+
"write", "call",
|
|
540
584
|
]);
|
|
541
585
|
server.registerTool("crosspad_trace", {
|
|
542
586
|
description: "[STM HW] Real-time SWD tracer for the STM32G0B1 firmware (ST-Link). Non-halting RAM polling of firmware variables resolved from the Debug ELF (like ST-Studio/CubeMonitor). Pick an `action`:\n" +
|
|
@@ -555,28 +599,35 @@ server.registerTool("crosspad_trace", {
|
|
|
555
599
|
inputSchema: {
|
|
556
600
|
action: TraceAction.describe("Required params per action — doctor/stop/status/device_state/ui: (none); " +
|
|
557
601
|
"config_set: key,value; symbols: query?; start: signals[],rate_hz?; " +
|
|
558
|
-
"add/remove: signals[]; read: window_from?,window_to?,max_points?; save: format
|
|
559
|
-
|
|
602
|
+
"add/remove: signals[]; read: window_from?,window_to?,max_points?; save: format?; " +
|
|
603
|
+
"write: writes[]; call: func,args?,confirm,ret_type?,timeout?."),
|
|
604
|
+
signals: z.array(z.string()).optional().describe("start: variable names from `symbols` (e.g. ['s_vbat_mv','s_inputs[0]']). Also accepts raw @address specs that bypass DWARF — '@0x40021000' (u32), '@0x40021000:u16' (u8|u16|u32|i8|i16|i32|f32), '@0x20000000:u8[16]' (16-element block) — for peripheral registers / arbitrary RAM."),
|
|
560
605
|
rate_hz: z.number().int().min(0).max(2000).optional().describe("start: target sample rate (0 = as fast as the probe allows). Actual Fs is reported."),
|
|
561
606
|
swo: z.array(z.string()).optional().describe("start (EXPERIMENTAL): map ITM stimulus ports to signal names, e.g. ['0:phase','1:isr_us']. Requires firmware that emits ITM on the SWO pin (NOT present in current CrossPad firmware — UNTESTED against real ITM). Omit for plain RAM polling. Fails soft: if SWV init fails, polling continues normally."),
|
|
562
607
|
query: z.string().optional().describe("symbols: case-insensitive substring filter."),
|
|
563
|
-
key: z.string().optional().describe("config_set: one of stm_elf_path|pyocd_python|probe_serial|trace_dir."),
|
|
608
|
+
key: z.string().optional().describe("config_set: one of stm_elf_path|pyocd_python|probe_serial|trace_dir|ui_open|stm_programmer_cli. stm_programmer_cli = path to STM32_Programmer_CLI for crosspad_flash target=stm. ui_open ∈ vscode(default: reply with the link → user clicks → opens in the VS Code Simple Browser; system-browser fallback after 30s if unopened)|browser(open system browser immediately)|none(never auto-open)."),
|
|
564
609
|
value: z.string().optional().describe("config_set: the value to persist."),
|
|
565
610
|
window_from: z.number().optional().describe("read: start time (s) of the window."),
|
|
566
611
|
window_to: z.number().optional().describe("read: end time (s) of the window."),
|
|
567
612
|
max_points: z.number().int().min(1).max(5000).optional().describe("read: max points per signal (default 200)."),
|
|
568
613
|
format: z.enum(["csv"]).optional().describe("save: export format (csv)."),
|
|
614
|
+
writes: z.array(z.string()).optional().describe("write: list of 'target=value' specs. target = @0xADDR[:type] (u8|u16|u32|i8|i16|i32|f32, default u32) or a DWARF symbol; value = hex 0x.. or decimal (float for f32). e.g. ['@0x50000414:u16=0xFFFF','s_vbat_mv=4200']. Allowlist: SRAM/peripheral/PPB only — Code/flash region is blocked."),
|
|
615
|
+
func: z.string().optional().describe("call: firmware function symbol to invoke (AAPCS)."),
|
|
616
|
+
args: z.array(z.number().int()).max(4).optional().describe("call: up to 4 integer args → r0-r3."),
|
|
617
|
+
confirm: z.boolean().optional().describe("call: must be true — acknowledges the core is halted for the call."),
|
|
618
|
+
ret_type: z.enum(["u32", "i32", "u16", "i16", "u8", "i8", "f32"]).optional().describe("call: decode r0 as this type (default u32; raw r0 always returned)."),
|
|
619
|
+
timeout: z.number().min(0.1).max(30).optional().describe("call: max seconds to wait for the function to return (default 2)."),
|
|
569
620
|
},
|
|
570
621
|
outputSchema: O_Trace,
|
|
571
622
|
annotations: ANN_SIDE_EFFECT,
|
|
572
|
-
}, async ({ action, signals, rate_hz, swo, query, key, value, window_from, window_to, max_points, format }, extra) => {
|
|
623
|
+
}, async ({ action, signals, rate_hz, swo, query, key, value, window_from, window_to, max_points, format, writes, func, args, confirm, ret_type, timeout }, extra) => {
|
|
573
624
|
switch (action) {
|
|
574
625
|
case "doctor": {
|
|
575
626
|
const r = await runDoctor(realProbe());
|
|
576
627
|
return ok({ action, ok: r.ok, issues: r.issues, device_state: r.probe ? "connected" : "no_probe" });
|
|
577
628
|
}
|
|
578
629
|
case "config_set": {
|
|
579
|
-
const allowed = ["stm_elf_path", "pyocd_python", "probe_serial", "trace_dir"];
|
|
630
|
+
const allowed = ["stm_elf_path", "pyocd_python", "probe_serial", "trace_dir", "ui_open", "stm_programmer_cli"];
|
|
580
631
|
if (!key || !allowed.includes(key))
|
|
581
632
|
return err(`config_set requires key in ${allowed.join("|")}`);
|
|
582
633
|
if (value === undefined)
|
|
@@ -615,9 +666,32 @@ server.registerTool("crosspad_trace", {
|
|
|
615
666
|
let uiUrl;
|
|
616
667
|
try {
|
|
617
668
|
uiUrl = await dashboard.ensureStarted();
|
|
618
|
-
if (!dashboard.hasClients())
|
|
619
|
-
traceBrowserOpener(uiUrl);
|
|
620
669
|
dashboard.bind(sess);
|
|
670
|
+
// §12.6 open behavior, by ui_open config (default "vscode"):
|
|
671
|
+
// vscode → DON'T pop anything now. The agent always replies with the
|
|
672
|
+
// dashboard link; the user clicks it → it opens in the VS Code
|
|
673
|
+
// Simple Browser via their workbench.externalUriOpeners. If
|
|
674
|
+
// nobody opens it within the fallback window, fall back to the
|
|
675
|
+
// system browser so a first-timer is never left staring at nothing.
|
|
676
|
+
// browser → open the system browser immediately.
|
|
677
|
+
// none → never auto-open (rely on an already-open persistent tab).
|
|
678
|
+
if (uiUrl && !dashboard.hasClients()) {
|
|
679
|
+
const mode = resolveConfigValue("ui_open", "CROSSPAD_TRACE_UI_OPEN", process.env.CROSSPAD_TRACE_UI_OPEN, "vscode");
|
|
680
|
+
if (mode === "browser") {
|
|
681
|
+
traceBrowserOpener(uiUrl);
|
|
682
|
+
}
|
|
683
|
+
else if (mode === "vscode") {
|
|
684
|
+
const ms = Number(process.env.CROSSPAD_TRACE_OPEN_FALLBACK_MS) || 30000;
|
|
685
|
+
const url = uiUrl;
|
|
686
|
+
setTimeout(() => {
|
|
687
|
+
// Only fall back if THIS trace is still the active one and nobody
|
|
688
|
+
// ever opened the in-editor link.
|
|
689
|
+
if (getActiveSession() === sess && sess.isRunning() && !getDashboard().hasClients()) {
|
|
690
|
+
traceBrowserOpener(url);
|
|
691
|
+
}
|
|
692
|
+
}, ms);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
621
695
|
}
|
|
622
696
|
catch { /* UI optional — never block a trace on the dashboard */ }
|
|
623
697
|
// §11.6: don't lie. Wait for the first real frame and report what the
|
|
@@ -733,6 +807,32 @@ server.registerTool("crosspad_trace", {
|
|
|
733
807
|
const reconciled = action === "add" ? await s.addSignals(signals) : await s.removeSignals(signals);
|
|
734
808
|
return ok({ action, signals: reconciled });
|
|
735
809
|
}
|
|
810
|
+
case "write": {
|
|
811
|
+
if (!writes || writes.length === 0)
|
|
812
|
+
return err("write requires `writes` (['target=value', ...]).");
|
|
813
|
+
try {
|
|
814
|
+
const r = await traceWrite(writes);
|
|
815
|
+
return ok({ action, ok: r.ok, results: r.results, error: r.error });
|
|
816
|
+
}
|
|
817
|
+
catch (e) {
|
|
818
|
+
return err(`write failed: ${String(e)}`, { action });
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
case "call": {
|
|
822
|
+
if (!func)
|
|
823
|
+
return err("call requires `func` (function symbol).");
|
|
824
|
+
if (!confirm)
|
|
825
|
+
return err("call requires confirm:true (the core is halted for the call).");
|
|
826
|
+
if (args && args.length > 4)
|
|
827
|
+
return err("call accepts at most 4 args (r0-r3).");
|
|
828
|
+
try {
|
|
829
|
+
const r = await traceCall(func, args ?? [], true, ret_type ?? "u32", timeout ?? 2);
|
|
830
|
+
return ok({ action, ...r });
|
|
831
|
+
}
|
|
832
|
+
catch (e) {
|
|
833
|
+
return err(`call failed: ${String(e)}`, { action });
|
|
834
|
+
}
|
|
835
|
+
}
|
|
736
836
|
}
|
|
737
837
|
});
|
|
738
838
|
// ═══════════════════════════════════════════════════════════════════════
|