@kvell007/embed-labs-cli 0.1.0-alpha.98 → 0.1.0-alpha.99
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/README.md +196 -235
- package/dist/agent-runtime.d.ts +2137 -0
- package/dist/agent-runtime.js +21371 -0
- package/dist/agent-runtime.js.map +1 -0
- package/dist/boot-logo-local.d.ts +49 -0
- package/dist/boot-logo-local.js +364 -0
- package/dist/boot-logo-local.js.map +1 -0
- package/dist/embed-labs-mcp-bridge.mjs +1223 -1006
- package/dist/index.js +3541 -4676
- package/dist/index.js.map +1 -1
- package/dist/install-progress.js +1 -1
- package/dist/install-progress.js.map +1 -1
- package/dist/local-toolchain.d.ts +5 -14
- package/dist/local-toolchain.js +169 -45
- package/dist/local-toolchain.js.map +1 -1
- package/package.json +5 -6
|
@@ -9,15 +9,38 @@ const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
|
9
9
|
const REPO_ROOT = path.resolve(SCRIPT_DIR, "../../../../..");
|
|
10
10
|
const DEFAULT_TEMPLATE_ID = "taishanpi-1m-rk3566";
|
|
11
11
|
const DEFAULT_HOST = "198.19.77.2";
|
|
12
|
-
const DEFAULT_PORTS = "22
|
|
13
|
-
const
|
|
14
|
-
const TOOL_INTEGRITY_RELOGIN_MESSAGE = "工具完整性校验失败,请在当前电脑重新执行 embedlabs auth login --token <key>";
|
|
15
|
-
const MCP_SERVER_VERSION = "0.2.41";
|
|
12
|
+
const DEFAULT_PORTS = "22";
|
|
13
|
+
const MCP_SERVER_VERSION_FALLBACK = "0.0.0";
|
|
16
14
|
const MCP_ICON_FILES = [
|
|
17
15
|
{ fileName: "embed-labs-icon-dark.png", theme: "dark" },
|
|
18
16
|
{ fileName: "embed-labs-icon-light.png", theme: "light" }
|
|
19
17
|
];
|
|
20
18
|
|
|
19
|
+
function mcpServerVersion() {
|
|
20
|
+
const candidates = [
|
|
21
|
+
path.join(process.cwd(), "platform_plugins", "codex_plugin", "plugins", "embed-labs", ".codex-plugin", "plugin.json"),
|
|
22
|
+
path.join(process.cwd(), "platform_plugins", "opencode_plugin", "package.json"),
|
|
23
|
+
path.join(SCRIPT_DIR, "..", ".codex-plugin", "plugin.json"),
|
|
24
|
+
path.join(SCRIPT_DIR, "..", "package.json"),
|
|
25
|
+
path.join(SCRIPT_DIR, "..", "..", "package.json"),
|
|
26
|
+
path.join(REPO_ROOT, "platform_plugins", "codex_plugin", "plugins", "embed-labs", ".codex-plugin", "plugin.json"),
|
|
27
|
+
path.join(REPO_ROOT, "platform_plugins", "opencode_plugin", "package.json")
|
|
28
|
+
];
|
|
29
|
+
for (const candidate of candidates) {
|
|
30
|
+
if (!existsSync(candidate)) continue;
|
|
31
|
+
try {
|
|
32
|
+
const parsed = JSON.parse(readFileSync(candidate, "utf8"));
|
|
33
|
+
if (typeof parsed.version === "string" && parsed.version.trim()) {
|
|
34
|
+
return parsed.version.trim();
|
|
35
|
+
}
|
|
36
|
+
} catch {
|
|
37
|
+
// Keep server startup resilient when a packaged metadata file is missing
|
|
38
|
+
// or malformed; validation covers the normal release path.
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return MCP_SERVER_VERSION_FALLBACK;
|
|
42
|
+
}
|
|
43
|
+
|
|
21
44
|
function iconDataUri(fileName) {
|
|
22
45
|
const candidates = [
|
|
23
46
|
path.join(SCRIPT_DIR, "..", "assets", fileName),
|
|
@@ -40,9 +63,9 @@ function mcpServerInfo() {
|
|
|
40
63
|
.filter(Boolean);
|
|
41
64
|
return {
|
|
42
65
|
name: "embed-labs",
|
|
43
|
-
title: "
|
|
44
|
-
version:
|
|
45
|
-
description: "
|
|
66
|
+
title: "EmbedLabs",
|
|
67
|
+
version: mcpServerVersion(),
|
|
68
|
+
description: "EmbedLabs local Native Agent compatibility bridge for embedded development boards, BoardPack knowledge, toolchain metadata, Local Bridge hardware access, and redacted installation-identity reporting.",
|
|
46
69
|
websiteUrl: "https://embedboard.com/",
|
|
47
70
|
...(icons.length > 0 ? { icons } : {})
|
|
48
71
|
};
|
|
@@ -50,8 +73,8 @@ function mcpServerInfo() {
|
|
|
50
73
|
|
|
51
74
|
const tools = [
|
|
52
75
|
{
|
|
53
|
-
name: "
|
|
54
|
-
description: "
|
|
76
|
+
name: "dbt_agent_status",
|
|
77
|
+
description: "Report local EmbedLabs Agent readiness, local model setup, installation identity, local memory, Go Local Bridge access, and redacted public-experience report state. Model provider keys stay on this computer.",
|
|
55
78
|
inputSchema: {
|
|
56
79
|
type: "object",
|
|
57
80
|
properties: {}
|
|
@@ -59,7 +82,7 @@ const tools = [
|
|
|
59
82
|
},
|
|
60
83
|
{
|
|
61
84
|
name: "dbt_current_board_status",
|
|
62
|
-
description: "
|
|
85
|
+
description: "Compatibility-profile board inventory shortcut. Default clients should use embedlabs_action_run with action_id=detect_board, or embedlabs_chat for natural-language status requests. This is not a shell command. Do not report optional control ports or unrelated local serial paths unless the user explicitly asks.",
|
|
63
86
|
inputSchema: {
|
|
64
87
|
type: "object",
|
|
65
88
|
properties: {
|
|
@@ -71,9 +94,60 @@ const tools = [
|
|
|
71
94
|
}
|
|
72
95
|
}
|
|
73
96
|
},
|
|
97
|
+
{
|
|
98
|
+
name: "dbt_agent_action_schema",
|
|
99
|
+
description: "Return the local EmbedLabs Agent high-level action catalog or one action's machine-readable input_schema and safety contract. Use this when parameter, approval, target-selection, or must_not_infer rules are unclear. This discovery tool does not require login.",
|
|
100
|
+
inputSchema: {
|
|
101
|
+
type: "object",
|
|
102
|
+
properties: {
|
|
103
|
+
action_id: {
|
|
104
|
+
type: "string",
|
|
105
|
+
description: "Optional high-level Agent action id such as detect_board, manage_board_pack, query_board_knowledge, install_toolchain, build_project, flash_image, deploy_app, debug_board, capture_signal, submit_report, or create_fix_candidate. Omit to list all actions."
|
|
106
|
+
},
|
|
107
|
+
action: { type: "string", description: "Alias for action_id." },
|
|
108
|
+
include_raw: { type: "boolean" }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: "dbt_agent_action_run",
|
|
114
|
+
description: "Run one EmbedLabs Agent high-level action through the local CLI Agent orchestration layer. The Agent owns local installation identity checks, memory, Go Local Bridge routing, approval gates, and redacted report creation.",
|
|
115
|
+
inputSchema: {
|
|
116
|
+
type: "object",
|
|
117
|
+
required: ["action_id"],
|
|
118
|
+
properties: {
|
|
119
|
+
action_id: {
|
|
120
|
+
type: "string",
|
|
121
|
+
description: "High-level Agent action id such as detect_board, manage_board_pack, query_board_knowledge, install_toolchain, build_project, flash_image, deploy_app, debug_board, capture_signal, submit_report, or create_fix_candidate."
|
|
122
|
+
},
|
|
123
|
+
action: { type: "string", description: "Alias for action_id." },
|
|
124
|
+
request: { type: "string", description: "Optional user request. Used as query when query is not provided." },
|
|
125
|
+
arguments_json: { type: "string", description: "Optional JSON object with action arguments. Keys are converted to CLI flags, for example board_id -> --board-id and approved -> --approve." },
|
|
126
|
+
include_raw: { type: "boolean" },
|
|
127
|
+
timeout_ms: { type: "number" }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: "dbt_agent_chat",
|
|
133
|
+
description: "Run one natural-language EmbedLabs Native Agent chat turn through the local CLI Agent. IDE/plugin calls default to local-only deterministic Agent execution so the host IDE model is not duplicated by the user's local provider config. Set use_model=true only for explicit owner diagnostics that should invoke the local Native Agent model runtime.",
|
|
134
|
+
inputSchema: {
|
|
135
|
+
type: "object",
|
|
136
|
+
required: ["prompt"],
|
|
137
|
+
properties: {
|
|
138
|
+
prompt: { type: "string", description: "User request to pass to embedlabs agent chat." },
|
|
139
|
+
request: { type: "string", description: "Alias for prompt." },
|
|
140
|
+
session: { type: "string", description: "Optional local EmbedLabs chat session id to continue." },
|
|
141
|
+
new_session: { type: "boolean", description: "Create a new local chat session for this turn." },
|
|
142
|
+
use_model: { type: "boolean", description: "Opt in to the local Native Agent model runtime for this chat turn. Default false for IDE/plugin calls." },
|
|
143
|
+
include_raw: { type: "boolean" },
|
|
144
|
+
timeout_ms: { type: "number" }
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
},
|
|
74
148
|
{
|
|
75
149
|
name: "dbt_initial_image_flash",
|
|
76
|
-
description: "
|
|
150
|
+
description: "Compatibility-profile adapter for initialization-image flashing. Default clients must call embedlabs_action_run with action_id=flash_image and artifact_kind=initial_image or initial_firmware. This adapter maps historical requests to the Agent flash_image action, validates local SDK resources, calls Local Bridge flash plan/run, and returns a compact result.",
|
|
77
151
|
inputSchema: {
|
|
78
152
|
type: "object",
|
|
79
153
|
properties: {
|
|
@@ -93,7 +167,7 @@ const tools = [
|
|
|
93
167
|
},
|
|
94
168
|
{
|
|
95
169
|
name: "dbt_rp2350_monitor_status",
|
|
96
|
-
description: "Read RP2350-Monitor status through
|
|
170
|
+
description: "Read RP2350-Monitor status through EmbedLabs Local Bridge on 18083, including board status, logic-analyzer caps, probe caps, pins, channels, and the browser UI URL. RP2350 Monitor supports USB serial and LAN IP/TCP connection modes. Report ui_url/browser_url for the web interface; bridge_url/monitor_bridge_url is the internal upstream and must not be opened as /rp2350-monitor/.",
|
|
97
171
|
inputSchema: {
|
|
98
172
|
type: "object",
|
|
99
173
|
properties: {
|
|
@@ -113,12 +187,12 @@ const tools = [
|
|
|
113
187
|
},
|
|
114
188
|
{
|
|
115
189
|
name: "dbt_rp2350_capabilities",
|
|
116
|
-
description: "
|
|
190
|
+
description: "Full-profile diagnostics catalog for RP2350/Pico2 monitor operations, parameters, defaults, and firmware commands. Default clients should use embedlabs_action_schema/embedlabs_action_run with capture_signal, debug_board, or flash_image; this catalog is for engineering validation and fallback planning.",
|
|
117
191
|
inputSchema: { type: "object", properties: { include_raw: { type: "boolean" } } }
|
|
118
192
|
},
|
|
119
193
|
{
|
|
120
194
|
name: "dbt_rp2350_operation",
|
|
121
|
-
description: "
|
|
195
|
+
description: "Compatibility-profile engineering fallback for a supported RP2350 Monitor /api/operation action through the shared Local Bridge. Default clients should use embedlabs_action_run with action_id=capture_signal, debug_board, or another high-level Agent action first. Use this only after the high-level action cannot express an advanced catalog operation.",
|
|
122
196
|
inputSchema: {
|
|
123
197
|
type: "object",
|
|
124
198
|
required: ["action"],
|
|
@@ -150,7 +224,7 @@ const tools = [
|
|
|
150
224
|
},
|
|
151
225
|
{
|
|
152
226
|
name: "dbt_rp2350_uart_write",
|
|
153
|
-
description: "
|
|
227
|
+
description: "Compatibility-profile direct UART output fallback through the RP2350 Monitor hardware bridge. Default clients should normally call embedlabs_action_run with action_id=capture_signal and provide UART stimulus for closed-loop capture. Do not search the workspace, create Pico SDK/CMake projects, or write firmware source for UART test-data requests. Requires approved=true.",
|
|
154
228
|
inputSchema: { type: "object", properties: { id: { type: "number" }, instance: { type: "number" }, uart_id: { type: "string", description: "Alias such as uart0 or uart1; normalized to instance." }, tx: { type: "number" }, rx: { type: "number" }, baud: { type: "number" }, text: { type: "string" }, hex: { type: "string" }, data: { type: "string", description: "Text or hex payload depending on data_format." }, data_format: { type: "string", description: "hex, text, string, utf8, or ascii." }, data_hex: { type: "string" }, line_ending: { type: "string" }, approved: { type: "boolean" }, approve: { type: "boolean" }, transport: { type: "string" }, local_device_id: { type: "string" }, monitor_bridge_url: { type: "string" }, serial_path: { type: "string" }, tcp: { type: "string" }, tcp_endpoint: { type: "string" }, host: { type: "string" }, port: { type: "number" }, timeout_ms: { type: "number" } } }
|
|
155
229
|
},
|
|
156
230
|
{
|
|
@@ -160,7 +234,7 @@ const tools = [
|
|
|
160
234
|
},
|
|
161
235
|
{
|
|
162
236
|
name: "dbt_rp2350_logic_capture",
|
|
163
|
-
description: "
|
|
237
|
+
description: "Compatibility-profile adapter for RP2350 Monitor logic capture. Default clients should call embedlabs_action_run with action_id=capture_signal and pass analyzer pins plus optional UART/SPI/I2C stimulus. This adapter remains for compact/full engineering profiles. Do not search the repo, create Pico SDK/CMake projects, or write firmware source. Requires approved=true because it arms local hardware. UART/SPI/I2C/GPIO/logic functions may be combined when GPIO sets do not overlap. Example UART loopback: UART1 output GP8/GP9 wired to analyzer inputs GP16/GP17. GP16/GP17 are analyzer inputs in that example, not UART1 output pins.",
|
|
164
238
|
inputSchema: {
|
|
165
239
|
type: "object",
|
|
166
240
|
properties: {
|
|
@@ -210,7 +284,7 @@ const tools = [
|
|
|
210
284
|
},
|
|
211
285
|
{
|
|
212
286
|
name: "dbt_rp2350_logic_decode",
|
|
213
|
-
description: "Decode a local RP2350-Monitor logic JSONL capture without touching hardware. Supported decoders: summary, bursts, edges, uart, spi, and structure/auto/unknown. The result includes summary_for_user, decoded_preview, protocol_data, and analysis so the AI can report actual sampled data; use structure/unknown when the protocol is not known.",
|
|
287
|
+
description: "Decode a local RP2350-Monitor logic JSONL capture without touching hardware. Supported decoders: summary, bursts, edges, uart, spi, and structure/auto/unknown. The result includes summary_for_user, decoded_preview, protocol_data, and analysis so the AI can report actual sampled data; use structure/unknown when the protocol is not known. A 0-byte UART/SPI decode is a capture/wiring/decoder-parameter result, not firmware corruption; do not reflash, reboot, or enter BOOTSEL unless the user explicitly asks for firmware recovery.",
|
|
214
288
|
inputSchema: {
|
|
215
289
|
type: "object",
|
|
216
290
|
properties: {
|
|
@@ -237,7 +311,7 @@ const tools = [
|
|
|
237
311
|
},
|
|
238
312
|
{
|
|
239
313
|
name: "dbt_rp2350_spi_transfer",
|
|
240
|
-
description: "
|
|
314
|
+
description: "Compatibility-profile direct SPI output fallback through EmbedLabs Local Bridge. Default clients should normally use embedlabs_action_run with action_id=capture_signal and SPI stimulus for test-data capture. Pin choices must match the firmware pin map; mutating bus output requires approve=true.",
|
|
241
315
|
inputSchema: {
|
|
242
316
|
type: "object",
|
|
243
317
|
properties: {
|
|
@@ -280,7 +354,7 @@ const tools = [
|
|
|
280
354
|
},
|
|
281
355
|
{
|
|
282
356
|
name: "dbt_rp2350_monitor_command",
|
|
283
|
-
description: "Run an allowlisted RP2350-Monitor command through
|
|
357
|
+
description: "Run an allowlisted RP2350-Monitor command through EmbedLabs Local Bridge. Supports USB serial or board LAN IP/TCP transport. Mutating commands require approve=true.",
|
|
284
358
|
inputSchema: {
|
|
285
359
|
type: "object",
|
|
286
360
|
required: ["command"],
|
|
@@ -304,19 +378,20 @@ const tools = [
|
|
|
304
378
|
},
|
|
305
379
|
{
|
|
306
380
|
name: "dbt_cloud_status",
|
|
307
|
-
description: "
|
|
381
|
+
description: "Compatibility-profile board metadata adapter. It routes through local Agent board actions for supported boards, knowledge, and toolchain metadata. It does not expose model/service catalogs; default clients should use embedlabs_action_schema/embedlabs_action_run with manage_board_pack, query_board_knowledge, or install_toolchain.",
|
|
308
382
|
inputSchema: {
|
|
309
383
|
type: "object",
|
|
310
384
|
properties: {
|
|
311
385
|
template_id: { type: "string" },
|
|
312
386
|
template: { type: "string" },
|
|
387
|
+
query: { type: "string" },
|
|
313
388
|
include_raw: { type: "boolean" }
|
|
314
389
|
}
|
|
315
390
|
}
|
|
316
391
|
},
|
|
317
392
|
{
|
|
318
393
|
name: "dbt_board_knowledge_search",
|
|
319
|
-
description: "Search bounded
|
|
394
|
+
description: "Search bounded EmbedLabs server-side board knowledge snippets for pinout, UART/serial, GPIO, build, flash, SDK, and workflow questions. For supported boards, use this instead of web search; if no snippet matches, report the board-pack gap.",
|
|
320
395
|
inputSchema: {
|
|
321
396
|
type: "object",
|
|
322
397
|
required: ["query"],
|
|
@@ -330,66 +405,27 @@ const tools = [
|
|
|
330
405
|
},
|
|
331
406
|
{
|
|
332
407
|
name: "dbt_supported_boards",
|
|
333
|
-
description: "
|
|
408
|
+
description: "Compatibility-profile supported-board shortcut. Default clients should call embedlabs_action_run with action_id=manage_board_pack and operation=list for supported-board questions. This adapter returns BoardPack catalog summaries and local toolchain status hints.",
|
|
334
409
|
inputSchema: {
|
|
335
410
|
type: "object",
|
|
336
411
|
properties: {}
|
|
337
412
|
}
|
|
338
413
|
},
|
|
339
|
-
{
|
|
340
|
-
name: "dbt_task_status",
|
|
341
|
-
description: "Read an Embed Labs Cloud task status, including output_tail and artifact metadata.",
|
|
342
|
-
inputSchema: {
|
|
343
|
-
type: "object",
|
|
344
|
-
required: ["task_id"],
|
|
345
|
-
properties: {
|
|
346
|
-
task_id: { type: "string" }
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
},
|
|
350
|
-
{
|
|
351
|
-
name: "dbt_task_log_download",
|
|
352
|
-
description: "Download the first log artifact from a Cloud task.",
|
|
353
|
-
inputSchema: {
|
|
354
|
-
type: "object",
|
|
355
|
-
required: ["task_id"],
|
|
356
|
-
properties: {
|
|
357
|
-
task_id: { type: "string" },
|
|
358
|
-
output_path: { type: "string" }
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
},
|
|
362
|
-
{
|
|
363
|
-
name: "dbt_artifact_download",
|
|
364
|
-
description: "Download an Embed Labs Cloud artifact.",
|
|
365
|
-
inputSchema: {
|
|
366
|
-
type: "object",
|
|
367
|
-
required: ["artifact_id", "output_path"],
|
|
368
|
-
properties: {
|
|
369
|
-
artifact_id: { type: "string" },
|
|
370
|
-
output_path: { type: "string" }
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
},
|
|
374
414
|
{
|
|
375
415
|
name: "dbt_board_debug",
|
|
376
|
-
description: "
|
|
416
|
+
description: "High-level local board diagnostics/deploy handoff. Routes through Native Agent debug_board or deploy_app actions; source edits, compilation, and hardware access stay local. Do not use this for initialization image flashing; use embedlabs_action_run with action_id=flash_image and the appropriate artifact_kind instead.",
|
|
377
417
|
inputSchema: {
|
|
378
418
|
type: "object",
|
|
379
|
-
required: ["prompt"],
|
|
380
419
|
properties: {
|
|
381
|
-
account_id: { type: "string" },
|
|
382
|
-
workspace_id: { type: "string" },
|
|
383
420
|
prompt: { type: "string" },
|
|
384
|
-
provider: { type: "string" },
|
|
385
|
-
model: { type: "string" },
|
|
386
421
|
host: { type: "string" },
|
|
387
422
|
ports: { type: "string" },
|
|
388
423
|
artifact_path: { type: "string" },
|
|
389
|
-
artifact_id: { type: "string" },
|
|
390
|
-
artifact_task_id: { type: "string" },
|
|
391
|
-
artifact_output: { type: "string" },
|
|
392
424
|
remote_path: { type: "string" },
|
|
425
|
+
remote_dir: { type: "string" },
|
|
426
|
+
remote_file: { type: "string" },
|
|
427
|
+
wrapper_script: { type: "string" },
|
|
428
|
+
run_script: { type: "string" },
|
|
393
429
|
run_command: { type: "string" },
|
|
394
430
|
approve: { type: "boolean" },
|
|
395
431
|
run: { type: "boolean" }
|
|
@@ -398,7 +434,7 @@ const tools = [
|
|
|
398
434
|
},
|
|
399
435
|
{
|
|
400
436
|
name: "dbt_boot_logo_update",
|
|
401
|
-
description: "
|
|
437
|
+
description: "Compatibility-profile adapter for startup-logo work. Default clients must call embedlabs_action_run with action_id=flash_image and artifact_kind=boot_logo. This adapter validates the local SDK, converts/resizes the image locally, repacks the Rockchip resource image, patches boot.img, and returns a compact result.",
|
|
402
438
|
inputSchema: {
|
|
403
439
|
type: "object",
|
|
404
440
|
required: ["logo_path"],
|
|
@@ -423,33 +459,9 @@ const tools = [
|
|
|
423
459
|
}
|
|
424
460
|
}
|
|
425
461
|
},
|
|
426
|
-
{
|
|
427
|
-
name: "dbt_update_logo",
|
|
428
|
-
description: "Ask the server to generate a small free boot-logo package for later local image merge and approved flashing. This does not allocate a paid server workspace.",
|
|
429
|
-
inputSchema: {
|
|
430
|
-
type: "object",
|
|
431
|
-
required: ["logo_path"],
|
|
432
|
-
properties: {
|
|
433
|
-
account_id: { type: "string" },
|
|
434
|
-
account: { type: "string" },
|
|
435
|
-
project_id: { type: "string" },
|
|
436
|
-
project: { type: "string" },
|
|
437
|
-
board_id: { type: "string" },
|
|
438
|
-
board: { type: "string" },
|
|
439
|
-
variant_id: { type: "string" },
|
|
440
|
-
variant: { type: "string" },
|
|
441
|
-
logo_path: { type: "string" },
|
|
442
|
-
kernel_logo_path: { type: "string" },
|
|
443
|
-
output_path: { type: "string" },
|
|
444
|
-
output: { type: "string" },
|
|
445
|
-
rotate: { type: "string" },
|
|
446
|
-
scale: { type: "string" }
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
},
|
|
450
462
|
{
|
|
451
463
|
name: "dbt_compose_boot_logo",
|
|
452
|
-
description: "Compose a
|
|
464
|
+
description: "Compose a local boot-logo package with a local base image using the cross-platform Embed CLI local composer. The result reports whether it is ready for approved flashing.",
|
|
453
465
|
inputSchema: {
|
|
454
466
|
type: "object",
|
|
455
467
|
required: ["package_path", "base_image_path", "output_path"],
|
|
@@ -514,7 +526,7 @@ const tools = [
|
|
|
514
526
|
},
|
|
515
527
|
{
|
|
516
528
|
name: "dbt_local_toolchain_current",
|
|
517
|
-
description: "Show the currently selected local development-board environment in the
|
|
529
|
+
description: "Show the currently selected local development-board environment in the EmbedLabs CLI registry.",
|
|
518
530
|
inputSchema: {
|
|
519
531
|
type: "object",
|
|
520
532
|
properties: {
|
|
@@ -525,7 +537,7 @@ const tools = [
|
|
|
525
537
|
},
|
|
526
538
|
{
|
|
527
539
|
name: "dbt_local_toolchain_install",
|
|
528
|
-
description: "
|
|
540
|
+
description: "Compatibility-profile adapter for local SDK/toolchain install, update, or repair. Default clients should call embedlabs_action_run with action_id=install_toolchain and operation=install/update instead of selecting this tool directly.",
|
|
529
541
|
inputSchema: {
|
|
530
542
|
type: "object",
|
|
531
543
|
properties: {
|
|
@@ -541,7 +553,8 @@ const tools = [
|
|
|
541
553
|
install_root: { type: "string" },
|
|
542
554
|
installRoot: { type: "string" },
|
|
543
555
|
mode: { type: "string", enum: ["minimal", "runtime", "compile", "qt", "firmware", "full", "images"] },
|
|
544
|
-
force: { type: "boolean" }
|
|
556
|
+
force: { type: "boolean" },
|
|
557
|
+
include_raw: { type: "boolean" }
|
|
545
558
|
}
|
|
546
559
|
}
|
|
547
560
|
},
|
|
@@ -561,7 +574,7 @@ const tools = [
|
|
|
561
574
|
},
|
|
562
575
|
{
|
|
563
576
|
name: "dbt_local_toolchain_validate",
|
|
564
|
-
description: "
|
|
577
|
+
description: "Compatibility-profile validator for a local development-board environment release. Default clients should use embedlabs_action_run with action_id=install_toolchain and operation=list/install/update for repair decisions; this tool returns a compact validation summary and high-level repair command.",
|
|
565
578
|
inputSchema: {
|
|
566
579
|
type: "object",
|
|
567
580
|
properties: {
|
|
@@ -576,7 +589,7 @@ const tools = [
|
|
|
576
589
|
},
|
|
577
590
|
{
|
|
578
591
|
name: "dbt_taishanpi_initial_image_flash",
|
|
579
|
-
description: "
|
|
592
|
+
description: "Compatibility-profile TaishanPi initial-image adapter. Default clients must call embedlabs_action_run with action_id=flash_image and artifact_kind=initial_image. This adapter installs/repairs the images SDK, resolves images/current automatically, plans flashing through Local Bridge, and optionally runs it when approved=true.",
|
|
580
593
|
inputSchema: {
|
|
581
594
|
type: "object",
|
|
582
595
|
properties: {
|
|
@@ -598,7 +611,7 @@ const tools = [
|
|
|
598
611
|
},
|
|
599
612
|
{
|
|
600
613
|
name: "dbt_rp2350_monitor_firmware_flash",
|
|
601
|
-
description: "Download/install the optional RP2350 Monitor firmware package, locate rp2350_monitor.uf2, plan UF2 flashing through
|
|
614
|
+
description: "Download/install the optional RP2350 Monitor firmware package, locate rp2350_monitor.uf2, plan UF2 flashing through EmbedLabs Local Bridge, and optionally flash it when approved=true. Use this only when the user explicitly asks for RP2350 Monitor, logic-analyzer, hardware-control, or Monitor firmware. For 初始化镜像/initial firmware requests use embedlabs_action_run with action_id=flash_image and artifact_kind=initial_firmware instead. Initialized boards use the Local Bridge inventory and picotool runtime handoff when available.",
|
|
602
615
|
inputSchema: {
|
|
603
616
|
type: "object",
|
|
604
617
|
properties: {
|
|
@@ -619,7 +632,7 @@ const tools = [
|
|
|
619
632
|
},
|
|
620
633
|
{
|
|
621
634
|
name: "dbt_rp2350_initial_firmware_flash",
|
|
622
|
-
description: "
|
|
635
|
+
description: "Compatibility-profile RP2350 initial-firmware adapter. Default clients must call embedlabs_action_run with action_id=flash_image and artifact_kind=initial_firmware. This adapter installs the board initialization firmware package, locates initial.uf2, plans UF2 flashing through Local Bridge, and optionally flashes it when approved=true.",
|
|
623
636
|
inputSchema: {
|
|
624
637
|
type: "object",
|
|
625
638
|
properties: {
|
|
@@ -640,7 +653,7 @@ const tools = [
|
|
|
640
653
|
},
|
|
641
654
|
{
|
|
642
655
|
name: "dbt_plugin_update_check",
|
|
643
|
-
description: "Check the published
|
|
656
|
+
description: "Check the published EmbedLabs Codex/OpenCode/Trae plugin release and local plugin integrity, including repair_required, local_integrity, cache_stale, and release_package_stale diagnostics for same-version plugin repair.",
|
|
644
657
|
inputSchema: {
|
|
645
658
|
type: "object",
|
|
646
659
|
properties: {
|
|
@@ -658,12 +671,12 @@ const tools = [
|
|
|
658
671
|
},
|
|
659
672
|
{
|
|
660
673
|
name: "dbt_plugin_update",
|
|
661
|
-
description: "Update the local
|
|
674
|
+
description: "Update the local EmbedLabs Codex/OpenCode/Trae plugin installation from the published release. Omit target_plugin to update all retained IDE integrations. Restart the updated client after this finishes.",
|
|
662
675
|
inputSchema: {
|
|
663
676
|
type: "object",
|
|
664
677
|
properties: {
|
|
665
|
-
target_plugin: { type: "string", enum: ["codex", "opencode", "trae", "all"] },
|
|
666
|
-
plugin: { type: "string", enum: ["codex", "opencode", "trae", "all"] },
|
|
678
|
+
target_plugin: { type: "string", enum: ["codex", "opencode", "trae", "all"], description: "Optional target client. Defaults to all, which updates Codex, OpenCode, and Trae together." },
|
|
679
|
+
plugin: { type: "string", enum: ["codex", "opencode", "trae", "all"], description: "Alias for target_plugin. Defaults to all when omitted." },
|
|
667
680
|
release_url: { type: "string" },
|
|
668
681
|
releaseUrl: { type: "string" },
|
|
669
682
|
target: { type: "string" },
|
|
@@ -678,12 +691,11 @@ const tools = [
|
|
|
678
691
|
},
|
|
679
692
|
{
|
|
680
693
|
name: "dbt_local_compile",
|
|
681
|
-
description: "Compile a local TaishanPi source file with the local
|
|
694
|
+
description: "Compile a local TaishanPi source file with the local EmbedLabs LLVM toolchain. This runs on the user's computer through local toolchain metadata and the local installation identity.",
|
|
682
695
|
inputSchema: {
|
|
683
696
|
type: "object",
|
|
684
697
|
required: ["source_path", "output_path"],
|
|
685
698
|
properties: {
|
|
686
|
-
account_id: { type: "string" },
|
|
687
699
|
source_path: { type: "string" },
|
|
688
700
|
output_path: { type: "string" },
|
|
689
701
|
release_root: { type: "string" }
|
|
@@ -692,12 +704,11 @@ const tools = [
|
|
|
692
704
|
},
|
|
693
705
|
{
|
|
694
706
|
name: "dbt_local_qt_smoke",
|
|
695
|
-
description: "Build a local TaishanPi Qt/CMake application with the local
|
|
707
|
+
description: "Build a local TaishanPi Qt/CMake application with the local EmbedLabs LLVM/Qt toolchain. Defaults to the built-in smoke app, but for real demos pass source_path and target_name. Use official board-pack examples such as taishanpi/examples/modbus-loop-demo instead of ad hoc projects. This runs on the user's computer.",
|
|
696
708
|
inputSchema: {
|
|
697
709
|
type: "object",
|
|
698
710
|
required: ["build_dir"],
|
|
699
711
|
properties: {
|
|
700
|
-
account_id: { type: "string" },
|
|
701
712
|
source_path: { type: "string" },
|
|
702
713
|
build_dir: { type: "string" },
|
|
703
714
|
target_name: { type: "string" },
|
|
@@ -707,7 +718,7 @@ const tools = [
|
|
|
707
718
|
},
|
|
708
719
|
{
|
|
709
720
|
name: "dbt_taishanpi_qt_app_workflow",
|
|
710
|
-
description: "
|
|
721
|
+
description: "Compatibility-profile TaishanPi Qt application adapter. Default clients must call embedlabs_action_run with action_id=deploy_app for Qt/QtQuick app development, demos, cross compilation, deployment, and runtime checks. This adapter validates/repairs the Qt SDK, selects a BoardPack template when source_path is omitted, builds with the local LLVM/Qt toolchain, and can deploy/run after approval.",
|
|
711
722
|
inputSchema: {
|
|
712
723
|
type: "object",
|
|
713
724
|
properties: {
|
|
@@ -719,6 +730,10 @@ const tools = [
|
|
|
719
730
|
local_device_id: { type: "string" },
|
|
720
731
|
host: { type: "string" },
|
|
721
732
|
remote_path: { type: "string" },
|
|
733
|
+
remote_dir: { type: "string" },
|
|
734
|
+
remote_file: { type: "string" },
|
|
735
|
+
wrapper_script: { type: "string" },
|
|
736
|
+
run_script: { type: "string" },
|
|
722
737
|
deploy: { type: "boolean" },
|
|
723
738
|
run: { type: "boolean" },
|
|
724
739
|
approve: { type: "boolean" },
|
|
@@ -729,14 +744,14 @@ const tools = [
|
|
|
729
744
|
},
|
|
730
745
|
{
|
|
731
746
|
name: "dbttool",
|
|
732
|
-
description: "Generic
|
|
747
|
+
description: "Generic EmbedLabs compatibility router for engineering fallback. Default user requests must go through embedlabs_chat or embedlabs_action_run with high-level actions such as detect_board, query_board_knowledge, install_toolchain, build_project, flash_image, deploy_app, debug_board, capture_signal, manage_board_pack, submit_report, and create_fix_candidate. Use board/protocol-specific rp2350-* or taishanpi-* actions only in compact/full engineering profiles after the high-level Agent action cannot express the request or when validating a specific adapter. For closed-loop protocol validation, normally call the high-level capture_signal action; use rp2350-logic-capture/decode only as an engineering fallback. Do not scan the user's project to invent firmware, create Pico SDK/CMake projects, write main.c, scrape the monitor UI, or use shell serial/curl commands.",
|
|
733
748
|
inputSchema: {
|
|
734
749
|
type: "object",
|
|
735
750
|
required: ["action"],
|
|
736
751
|
properties: {
|
|
737
752
|
action: {
|
|
738
753
|
type: "string",
|
|
739
|
-
description: "Compact action id such as supported-boards, status, cloud-status, board-reboot, board-bootloader, image-flash, rp2350-capabilities, rp2350-spi-transfer, rp2350-logic-capture, boot-logo-update, local-toolchain-install, local-compile, local-qt-smoke, taishanpi-qt-workflow
|
|
754
|
+
description: "Compact action id such as supported-boards, status, cloud-status, board-reboot, board-bootloader, image-flash, rp2350-capabilities, rp2350-spi-transfer, rp2350-logic-capture, boot-logo-update, local-toolchain-install, local-compile, local-qt-smoke, or taishanpi-qt-workflow."
|
|
740
755
|
},
|
|
741
756
|
request: { type: "string", description: "Optional natural-language request for high-level workflows." },
|
|
742
757
|
arguments_json: { type: "string", description: "Optional JSON object string passed as the selected action arguments." }
|
|
@@ -745,12 +760,26 @@ const tools = [
|
|
|
745
760
|
}
|
|
746
761
|
];
|
|
747
762
|
|
|
748
|
-
const
|
|
749
|
-
"
|
|
763
|
+
const AGENT_MCP_TOOL_ALIASES = new Map([
|
|
764
|
+
["embedlabs_agent_status", "dbt_agent_status"],
|
|
765
|
+
["embedlabs_action_schema", "dbt_agent_action_schema"],
|
|
766
|
+
["embedlabs_action_run", "dbt_agent_action_run"],
|
|
767
|
+
["embedlabs_chat", "dbt_agent_chat"],
|
|
768
|
+
["embedlabs_plugin_update_check", "dbt_plugin_update_check"],
|
|
769
|
+
["embedlabs_plugin_update", "dbt_plugin_update"]
|
|
770
|
+
]);
|
|
771
|
+
|
|
772
|
+
const AGENT_EXPOSED_MCP_TOOL_NAMES = new Set(AGENT_MCP_TOOL_ALIASES.keys());
|
|
773
|
+
|
|
774
|
+
const COMPACT_EXPOSED_MCP_TOOL_NAMES = new Set([
|
|
775
|
+
"dbt_agent_status",
|
|
750
776
|
"dbt_current_board_status",
|
|
777
|
+
"dbt_agent_action_schema",
|
|
778
|
+
"dbt_agent_action_run",
|
|
779
|
+
"dbt_agent_chat",
|
|
751
780
|
"dbt_supported_boards",
|
|
752
|
-
"dbt_cloud_status",
|
|
753
781
|
"dbt_board_knowledge_search",
|
|
782
|
+
"dbt_cloud_status",
|
|
754
783
|
"dbt_initial_image_flash",
|
|
755
784
|
"dbt_board_debug",
|
|
756
785
|
"dbt_boot_logo_update",
|
|
@@ -765,16 +794,123 @@ const DEFAULT_EXPOSED_MCP_TOOL_NAMES = new Set([
|
|
|
765
794
|
"dbttool"
|
|
766
795
|
]);
|
|
767
796
|
|
|
797
|
+
const FULL_EXPOSED_MCP_TOOL_NAMES = new Set([
|
|
798
|
+
...COMPACT_EXPOSED_MCP_TOOL_NAMES,
|
|
799
|
+
"dbt_rp2350_monitor_status",
|
|
800
|
+
"dbt_rp2350_capabilities",
|
|
801
|
+
"dbt_rp2350_operation",
|
|
802
|
+
"dbt_rp2350_gpio_read",
|
|
803
|
+
"dbt_rp2350_gpio_write",
|
|
804
|
+
"dbt_rp2350_uart_write",
|
|
805
|
+
"dbt_rp2350_i2c_transfer",
|
|
806
|
+
"dbt_rp2350_logic_capture",
|
|
807
|
+
"dbt_rp2350_logic_decode",
|
|
808
|
+
"dbt_rp2350_spi_transfer",
|
|
809
|
+
"dbt_rp2350_wifi_manage",
|
|
810
|
+
"dbt_rp2350_probe_debug",
|
|
811
|
+
"dbt_rp2350_monitor_command",
|
|
812
|
+
"dbt_taishanpi_initial_image_flash",
|
|
813
|
+
"dbt_rp2350_monitor_firmware_flash",
|
|
814
|
+
"dbt_rp2350_initial_firmware_flash",
|
|
815
|
+
"dbt_local_compile",
|
|
816
|
+
"dbt_local_qt_smoke"
|
|
817
|
+
]);
|
|
818
|
+
|
|
768
819
|
function mcpToolProfile() {
|
|
769
820
|
const profile = asString(process.env.EMBEDLABS_MCP_TOOL_PROFILE).toLowerCase();
|
|
770
821
|
if (profile) return profile;
|
|
771
822
|
if (["1", "true", "yes", "on"].includes(asString(process.env.EMBEDLABS_MCP_FULL_TOOLS).toLowerCase())) return "full";
|
|
772
|
-
return "
|
|
823
|
+
return "agent";
|
|
773
824
|
}
|
|
774
825
|
|
|
775
826
|
function exposedMcpTools() {
|
|
776
|
-
|
|
777
|
-
return tools.filter((tool) =>
|
|
827
|
+
const profile = mcpToolProfile();
|
|
828
|
+
if (profile === "full") return tools.filter((tool) => FULL_EXPOSED_MCP_TOOL_NAMES.has(tool.name));
|
|
829
|
+
if (profile === "compact" || profile === "compat") return tools.filter((tool) => COMPACT_EXPOSED_MCP_TOOL_NAMES.has(tool.name));
|
|
830
|
+
return Array.from(AGENT_MCP_TOOL_ALIASES.entries()).map(([publicName, legacyName]) => {
|
|
831
|
+
const tool = tools.find((item) => item.name === legacyName);
|
|
832
|
+
return {
|
|
833
|
+
...tool,
|
|
834
|
+
name: publicName,
|
|
835
|
+
description: agentProfileToolDescription(tool.description),
|
|
836
|
+
inputSchema: sanitizeAgentProfileInputSchema(tool.inputSchema)
|
|
837
|
+
};
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
function isMcpToolExposed(name) {
|
|
842
|
+
return exposedMcpTools().some((tool) => tool.name === name);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
function canonicalMcpToolName(name) {
|
|
846
|
+
return AGENT_MCP_TOOL_ALIASES.get(name) || name;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function agentProfileToolDescription(description) {
|
|
850
|
+
return asString(description)
|
|
851
|
+
.replaceAll("dbt_agent_status", "embedlabs_agent_status")
|
|
852
|
+
.replaceAll("dbt_agent_action_schema", "embedlabs_action_schema")
|
|
853
|
+
.replaceAll("dbt_agent_action_run", "embedlabs_action_run")
|
|
854
|
+
.replaceAll("dbt_agent_chat", "embedlabs_chat")
|
|
855
|
+
.replaceAll("dbt_plugin_update_check", "embedlabs_plugin_update_check")
|
|
856
|
+
.replaceAll("dbt_plugin_update", "embedlabs_plugin_update")
|
|
857
|
+
.replaceAll("low-level MCP tools", "implementation-level tools");
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
function sanitizeAgentProfileInputSchema(schema) {
|
|
861
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema)) return schema;
|
|
862
|
+
const sanitized = { ...schema };
|
|
863
|
+
if (sanitized.properties && typeof sanitized.properties === "object" && !Array.isArray(sanitized.properties)) {
|
|
864
|
+
const { include_raw: _includeRaw, ...properties } = sanitized.properties;
|
|
865
|
+
sanitized.properties = properties;
|
|
866
|
+
}
|
|
867
|
+
return sanitized;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
function sanitizeAgentProfileArgs(args) {
|
|
871
|
+
if (!args || typeof args !== "object" || Array.isArray(args)) return {};
|
|
872
|
+
const { include_raw: _includeRaw, includeRaw: _includeRawCamel, ...rest } = args;
|
|
873
|
+
return rest;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
function sanitizeMcpPublicValue(value, key = "") {
|
|
877
|
+
if (typeof value === "string") {
|
|
878
|
+
if (isLocalPathLikePublicKey(key)) return "[local-path]";
|
|
879
|
+
return summarizePublicText(value);
|
|
880
|
+
}
|
|
881
|
+
if (Array.isArray(value)) {
|
|
882
|
+
return value.map((item) => sanitizeMcpPublicValue(item, key));
|
|
883
|
+
}
|
|
884
|
+
if (!value || typeof value !== "object") return value;
|
|
885
|
+
return Object.fromEntries(
|
|
886
|
+
Object.entries(value)
|
|
887
|
+
.map(([entryKey, entryValue]) => [entryKey, sanitizeMcpPublicValue(entryValue, entryKey)])
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function isLocalPathLikePublicKey(key) {
|
|
892
|
+
return [
|
|
893
|
+
"path",
|
|
894
|
+
"file",
|
|
895
|
+
"dir",
|
|
896
|
+
"directory",
|
|
897
|
+
"target",
|
|
898
|
+
"target_path",
|
|
899
|
+
"checked_path",
|
|
900
|
+
"source_path",
|
|
901
|
+
"package_path",
|
|
902
|
+
"marketplace_path",
|
|
903
|
+
"config_path",
|
|
904
|
+
"cache_path",
|
|
905
|
+
"report_path",
|
|
906
|
+
"reports_dir",
|
|
907
|
+
"session_path",
|
|
908
|
+
"memory_path",
|
|
909
|
+
"output_path",
|
|
910
|
+
"local_path",
|
|
911
|
+
"local_file",
|
|
912
|
+
"log_path"
|
|
913
|
+
].includes(asString(key).toLowerCase());
|
|
778
914
|
}
|
|
779
915
|
|
|
780
916
|
function asString(value) {
|
|
@@ -840,13 +976,167 @@ function pushOptional(args, name, value) {
|
|
|
840
976
|
if (text) args.push(`--${name}`, text);
|
|
841
977
|
}
|
|
842
978
|
|
|
979
|
+
function toCliFlagName(name) {
|
|
980
|
+
return asString(name).replace(/[A-Z]/g, (item) => `-${item.toLowerCase()}`).replaceAll("_", "-");
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
const AGENT_ACTION_CLI_FLAGS = new Set([
|
|
984
|
+
"board",
|
|
985
|
+
"board-id",
|
|
986
|
+
"template",
|
|
987
|
+
"template-id",
|
|
988
|
+
"query",
|
|
989
|
+
"title",
|
|
990
|
+
"description",
|
|
991
|
+
"optimization-item-id",
|
|
992
|
+
"item-id",
|
|
993
|
+
"branch",
|
|
994
|
+
"branch-name",
|
|
995
|
+
"test-scope",
|
|
996
|
+
"notes",
|
|
997
|
+
"category",
|
|
998
|
+
"severity",
|
|
999
|
+
"component",
|
|
1000
|
+
"device",
|
|
1001
|
+
"device-id",
|
|
1002
|
+
"channel",
|
|
1003
|
+
"metadata-root",
|
|
1004
|
+
"source-url",
|
|
1005
|
+
"source-release-root",
|
|
1006
|
+
"install-root",
|
|
1007
|
+
"operation",
|
|
1008
|
+
"op",
|
|
1009
|
+
"mode",
|
|
1010
|
+
"force",
|
|
1011
|
+
"update",
|
|
1012
|
+
"uninstall",
|
|
1013
|
+
"remove",
|
|
1014
|
+
"list",
|
|
1015
|
+
"source",
|
|
1016
|
+
"output",
|
|
1017
|
+
"artifact",
|
|
1018
|
+
"artifact-path",
|
|
1019
|
+
"image",
|
|
1020
|
+
"image-path",
|
|
1021
|
+
"image-dir",
|
|
1022
|
+
"artifact-kind",
|
|
1023
|
+
"logo",
|
|
1024
|
+
"logo-path",
|
|
1025
|
+
"kernel-logo",
|
|
1026
|
+
"kernel-logo-path",
|
|
1027
|
+
"base-image",
|
|
1028
|
+
"base-image-path",
|
|
1029
|
+
"output-image",
|
|
1030
|
+
"output-image-path",
|
|
1031
|
+
"output-resource",
|
|
1032
|
+
"output-resource-path",
|
|
1033
|
+
"target-volume",
|
|
1034
|
+
"local-device-id",
|
|
1035
|
+
"variant",
|
|
1036
|
+
"hardware-profile",
|
|
1037
|
+
"remote-path",
|
|
1038
|
+
"remote-dir",
|
|
1039
|
+
"remote-file",
|
|
1040
|
+
"wrapper-script",
|
|
1041
|
+
"run-script",
|
|
1042
|
+
"run-command",
|
|
1043
|
+
"run",
|
|
1044
|
+
"timeout",
|
|
1045
|
+
"user",
|
|
1046
|
+
"partition",
|
|
1047
|
+
"partitions",
|
|
1048
|
+
"release-root",
|
|
1049
|
+
"profile",
|
|
1050
|
+
"build-profile",
|
|
1051
|
+
"approve",
|
|
1052
|
+
"host",
|
|
1053
|
+
"port",
|
|
1054
|
+
"serial-path",
|
|
1055
|
+
"monitor-bridge-url",
|
|
1056
|
+
"pin-base",
|
|
1057
|
+
"pin-count",
|
|
1058
|
+
"sample-rate",
|
|
1059
|
+
"sample-rate-hz",
|
|
1060
|
+
"samples",
|
|
1061
|
+
"output-path",
|
|
1062
|
+
"pull",
|
|
1063
|
+
"wait-timeout-ms",
|
|
1064
|
+
"read-timeout-ms",
|
|
1065
|
+
"pre-samples",
|
|
1066
|
+
"post-samples",
|
|
1067
|
+
"search-samples",
|
|
1068
|
+
"trigger-pin",
|
|
1069
|
+
"trigger-mode",
|
|
1070
|
+
"trigger-level",
|
|
1071
|
+
"trigger-mask",
|
|
1072
|
+
"trigger-value",
|
|
1073
|
+
"channel-names",
|
|
1074
|
+
"protocol",
|
|
1075
|
+
"decoder",
|
|
1076
|
+
"stimulus",
|
|
1077
|
+
"stimulus-json",
|
|
1078
|
+
"input-path",
|
|
1079
|
+
"capture-id",
|
|
1080
|
+
"swclk",
|
|
1081
|
+
"swdio",
|
|
1082
|
+
"reset"
|
|
1083
|
+
]);
|
|
1084
|
+
|
|
1085
|
+
function appendAgentActionFlag(command, name, value) {
|
|
1086
|
+
const flag = toCliFlagName(name === "approved" ? "approve" : name);
|
|
1087
|
+
if (!flag) return;
|
|
1088
|
+
if (!AGENT_ACTION_CLI_FLAGS.has(flag)) return;
|
|
1089
|
+
if (value === undefined || value === null || value === "") return;
|
|
1090
|
+
if (typeof value === "boolean") {
|
|
1091
|
+
if (value) command.push(`--${flag}`);
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
if (Array.isArray(value)) {
|
|
1095
|
+
command.push(`--${flag}`, value.map((item) => asCliValue(item) || JSON.stringify(item)).join(","));
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
if (typeof value === "object") {
|
|
1099
|
+
command.push(`--${flag}`, JSON.stringify(value));
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
const text = asCliValue(value);
|
|
1103
|
+
if (text) command.push(`--${flag}`, text);
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
function agentActionPayload(args) {
|
|
1107
|
+
const payload = objectValue(args.arguments_json) || {};
|
|
1108
|
+
for (const [key, value] of Object.entries(args || {})) {
|
|
1109
|
+
if (["action_id", "action", "name", "arguments_json", "include_raw", "timeout_ms"].includes(key)) continue;
|
|
1110
|
+
if (payload[key] === undefined) payload[key] = value;
|
|
1111
|
+
}
|
|
1112
|
+
if (asString(args.request) && payload.query === undefined && payload.prompt === undefined) {
|
|
1113
|
+
payload.query = asString(args.request);
|
|
1114
|
+
}
|
|
1115
|
+
return payload;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
843
1118
|
async function runEmbed(args) {
|
|
844
1119
|
const command = resolveEmbedCommand();
|
|
845
1120
|
const fullArgs = command.args.concat(ensureJson(args));
|
|
1121
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
1122
|
+
try {
|
|
1123
|
+
return await runEmbedOnce(command, fullArgs);
|
|
1124
|
+
} catch (error) {
|
|
1125
|
+
if (attempt === 0 && isTransientEmbedCliPipeError(undefined, error?.message)) {
|
|
1126
|
+
await wait(100);
|
|
1127
|
+
continue;
|
|
1128
|
+
}
|
|
1129
|
+
throw error;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
throw new Error("EmbedLabs CLI failed after retry.");
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
async function runEmbedOnce(command, fullArgs) {
|
|
846
1136
|
return await new Promise((resolve, reject) => {
|
|
847
1137
|
const child = spawn(command.file, fullArgs, {
|
|
848
1138
|
cwd: process.cwd(),
|
|
849
|
-
env:
|
|
1139
|
+
env: embedCliEnv(),
|
|
850
1140
|
stdio: ["ignore", "pipe", "pipe"]
|
|
851
1141
|
});
|
|
852
1142
|
let stdout = "";
|
|
@@ -872,6 +1162,21 @@ async function runEmbed(args) {
|
|
|
872
1162
|
});
|
|
873
1163
|
}
|
|
874
1164
|
|
|
1165
|
+
function isTransientEmbedCliPipeError(stdout, stderr) {
|
|
1166
|
+
return !asString(stdout).trim() && /\bEPIPE\b/.test(asString(stderr));
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
function wait(ms) {
|
|
1170
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
function embedCliEnv() {
|
|
1174
|
+
return {
|
|
1175
|
+
...process.env,
|
|
1176
|
+
EMBEDLABS_ENABLE_INTERNAL_DIRECT_COMMANDS: "1"
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
|
|
875
1180
|
async function runProcess(command, args, options = {}) {
|
|
876
1181
|
return await new Promise((resolve) => {
|
|
877
1182
|
const child = spawn(command, args, {
|
|
@@ -900,19 +1205,19 @@ async function runProcess(command, args, options = {}) {
|
|
|
900
1205
|
|
|
901
1206
|
function parseEmbedJson(output) {
|
|
902
1207
|
if (!output) {
|
|
903
|
-
return { ok: false, error: { code: "empty_cli_output", message: "
|
|
1208
|
+
return { ok: false, error: { code: "empty_cli_output", message: "EmbedLabs CLI returned an empty response." } };
|
|
904
1209
|
}
|
|
905
1210
|
try {
|
|
906
1211
|
return JSON.parse(output);
|
|
907
1212
|
} catch {
|
|
908
|
-
return { ok: false, error: { code: "non_json_cli_output", message: "
|
|
1213
|
+
return { ok: false, error: { code: "non_json_cli_output", message: "EmbedLabs CLI returned non-JSON output." } };
|
|
909
1214
|
}
|
|
910
1215
|
}
|
|
911
1216
|
|
|
912
1217
|
function summarizeEmbedFailure(exitCode, parsed, stderr) {
|
|
913
1218
|
const body = parsed && typeof parsed === "object" ? parsed : {};
|
|
914
1219
|
const error = body.error && typeof body.error === "object" ? body.error : {};
|
|
915
|
-
const message = asString(error.message) || asString(error.code) || summarizePublicText(stderr) || "
|
|
1220
|
+
const message = asString(error.message) || asString(error.code) || summarizePublicText(stderr) || "EmbedLabs CLI command failed.";
|
|
916
1221
|
const code = asString(error.code) || "embed_cli_failed";
|
|
917
1222
|
const summarizedError = {
|
|
918
1223
|
code,
|
|
@@ -943,7 +1248,7 @@ function parseThrownEmbedFailure(error) {
|
|
|
943
1248
|
ok: false,
|
|
944
1249
|
error: {
|
|
945
1250
|
code: "embed_cli_failed",
|
|
946
|
-
message: summarizePublicText(text) || "
|
|
1251
|
+
message: summarizePublicText(text) || "EmbedLabs CLI command failed."
|
|
947
1252
|
}
|
|
948
1253
|
};
|
|
949
1254
|
}
|
|
@@ -952,7 +1257,7 @@ function toolFailureResult(error, context = {}) {
|
|
|
952
1257
|
const parsed = parseThrownEmbedFailure(error);
|
|
953
1258
|
const err = parsed?.error && typeof parsed.error === "object" ? parsed.error : {};
|
|
954
1259
|
const code = asString(err.code) || "embed_cli_failed";
|
|
955
|
-
const message = asString(err.message) || "
|
|
1260
|
+
const message = asString(err.message) || "EmbedLabs CLI command failed.";
|
|
956
1261
|
const failure = {
|
|
957
1262
|
ok: false,
|
|
958
1263
|
...(Number.isFinite(Number(parsed?.exit_code)) ? { exit_code: Number(parsed.exit_code) } : {}),
|
|
@@ -982,13 +1287,13 @@ function friendlyToolFailureSummary(code, message, context = {}) {
|
|
|
982
1287
|
return "RP2350 Monitor 没有可用连接通道。支持两种方式:USB 串口连接,或局域网 IP/TCP 连接。请确认 USB 设备未被虚拟机占用,或者提供设备 IP/tcp_endpoint 后重试。";
|
|
983
1288
|
}
|
|
984
1289
|
if (code === "rp2350_monitor_bridge_handshake_failed") {
|
|
985
|
-
return "RP2350 Monitor 固件控制通道没有响应。开发板已被识别,但 USB/TCP
|
|
1290
|
+
return "RP2350 Monitor 固件控制通道没有响应。开发板已被识别,但 USB/TCP 命令握手失败;请先执行 `embedlabs bridge restart` 后重试。如仍失败,再重新上电或重新刷写 Monitor 固件。";
|
|
986
1291
|
}
|
|
987
1292
|
if (code === "rp2350_monitor_unavailable") {
|
|
988
1293
|
return "RP2350 Monitor 控制通道尚未就绪。请确认设备已刷入 Monitor 固件且未被虚拟机占用;如果使用 Wi-Fi/以太网连接,请提供设备局域网 IP 或 tcp_endpoint。";
|
|
989
1294
|
}
|
|
990
1295
|
if (/No such file or directory/i.test(text) && /usbmodem|ttyACM|COM\d+/i.test(text)) {
|
|
991
|
-
return "RP2350 Monitor 的 USB
|
|
1296
|
+
return "RP2350 Monitor 的 USB 串口上游连接已经失效。请先执行 `embedlabs bridge restart`;如果开发板已联网,也可以提供局域网 IP/tcp_endpoint 走网络连接。";
|
|
992
1297
|
}
|
|
993
1298
|
return `RP2350 Monitor 操作失败:${text}`;
|
|
994
1299
|
}
|
|
@@ -996,17 +1301,14 @@ function friendlyToolFailureSummary(code, message, context = {}) {
|
|
|
996
1301
|
}
|
|
997
1302
|
|
|
998
1303
|
function isAuthSetupError(code) {
|
|
999
|
-
return code === "
|
|
1000
|
-
|| code === "
|
|
1001
|
-
|| code === "auth_not_ready"
|
|
1002
|
-
|| code === "tool_integrity_check_failed"
|
|
1003
|
-
|| code === "device_signature_required";
|
|
1304
|
+
return code === "tool_integrity_check_failed"
|
|
1305
|
+
|| code === "installation_identity_required";
|
|
1004
1306
|
}
|
|
1005
1307
|
|
|
1006
1308
|
function authSetupDetails(details) {
|
|
1007
1309
|
if (!details || typeof details !== "object" || Array.isArray(details)) return undefined;
|
|
1008
1310
|
const allowed = {};
|
|
1009
|
-
for (const key of ["
|
|
1311
|
+
for (const key of ["agent_status_command", "identity_init_command", "identity_status_command", "memory_home"]) {
|
|
1010
1312
|
const value = details[key];
|
|
1011
1313
|
if (typeof value === "string" && value.trim()) {
|
|
1012
1314
|
allowed[key] = value.slice(0, 500);
|
|
@@ -1019,9 +1321,17 @@ function summarizePublicText(value) {
|
|
|
1019
1321
|
const text = asString(value).trim();
|
|
1020
1322
|
if (!text) return undefined;
|
|
1021
1323
|
return text
|
|
1022
|
-
.replace(/(sk-[A-Za-z0-9_-]{8,})/g, "[redacted]")
|
|
1023
|
-
.replace(/(npm_[A-Za-z0-9_-]{8,})/g, "[redacted]")
|
|
1024
|
-
.replace(
|
|
1324
|
+
.replace(/(sk-[A-Za-z0-9_-]{8,})/g, "[redacted:api_key]")
|
|
1325
|
+
.replace(/(npm_[A-Za-z0-9_-]{8,})/g, "[redacted:api_key]")
|
|
1326
|
+
.replace(/\b(re_[A-Za-z0-9_-]{8,})\b/g, "[redacted:api_key]")
|
|
1327
|
+
.replace(/\b((?:OPENAI|ANTHROPIC|BAI|DASHSCOPE|GEMINI|GOOGLE|AWS|NPM)[A-Z0-9_]*_KEY)\s*[:=]\s*['"]?[^'"\s,;}]+/gi, "$1=[redacted:api_key]")
|
|
1328
|
+
.replace(/\b(password|passwd|pwd|secret|token|api[_-]?key)\s*[:=]\s*['"]?[^'"\s,;}]+/gi, "$1=[redacted:secret]")
|
|
1329
|
+
.replace(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g, "[redacted:email]")
|
|
1330
|
+
.replace(/\bhttps?:\/\/(?:localhost|127\.0\.0\.1|\[::1\]|0\.0\.0\.0)(?::\d+)?(?:\/[^\s,;)}\]]*)?/gi, "[redacted:local_url]")
|
|
1331
|
+
.replace(/\bfile:\/{2,3}(?:[A-Za-z]:\/|(?:Users|private|tmp|var\/folders|Volumes)\/)[^\s,;)}\]]+/gi, "[redacted:local_path]")
|
|
1332
|
+
.replace(/(?:\/Users|\/private|\/tmp|\/var\/folders|\/Volumes)\/[^\s,;)}\]]+/g, "[redacted:local_path]")
|
|
1333
|
+
.replace(/\b[A-Za-z]:\/Users\/[^\s,;)}\]]+/g, "[redacted:local_path]")
|
|
1334
|
+
.replace(/\b[A-Za-z]:\\[^\s,;)}\]]+/g, "[redacted:local_path]")
|
|
1025
1335
|
.slice(0, 500);
|
|
1026
1336
|
}
|
|
1027
1337
|
|
|
@@ -1031,32 +1341,49 @@ function responseData(response) {
|
|
|
1031
1341
|
return body;
|
|
1032
1342
|
}
|
|
1033
1343
|
|
|
1034
|
-
async function
|
|
1344
|
+
async function pluginAgentStatus() {
|
|
1035
1345
|
let response;
|
|
1036
1346
|
try {
|
|
1037
|
-
response = await runEmbed(["
|
|
1347
|
+
response = await runEmbed(["agent", "status"]);
|
|
1038
1348
|
} catch (error) {
|
|
1039
|
-
return authNotReadyResult({
|
|
1040
|
-
authenticated: false,
|
|
1041
|
-
source_error: error instanceof Error ? error.message : String(error)
|
|
1042
|
-
});
|
|
1043
|
-
}
|
|
1044
|
-
const status = responseData(response) || {};
|
|
1045
|
-
if (status.authenticated === true && status.device_id && status.device_integrity === "ok") {
|
|
1046
1349
|
return {
|
|
1047
|
-
ok:
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
message: "Embed Labs token and device binding are ready."
|
|
1350
|
+
ok: false,
|
|
1351
|
+
error: {
|
|
1352
|
+
code: "embedlabs_cli_unavailable",
|
|
1353
|
+
message: "EmbedLabs CLI is not available to this plugin.",
|
|
1354
|
+
remediation: "Install or update the embedlabs CLI, then restart Codex/OpenCode/Trae.",
|
|
1355
|
+
details: {
|
|
1356
|
+
source_error: error instanceof Error ? error.message : String(error)
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1057
1359
|
};
|
|
1058
1360
|
}
|
|
1059
|
-
|
|
1361
|
+
const status = responseData(response) || {};
|
|
1362
|
+
const statusNextCommands = Array.isArray(status.next_commands) ? status.next_commands : [];
|
|
1363
|
+
return {
|
|
1364
|
+
ok: true,
|
|
1365
|
+
local_agent_ready: status.runtime === "embedlabs-local-agent" || Boolean(status.installation),
|
|
1366
|
+
identity_scope: "local_installation",
|
|
1367
|
+
uses_local_installation_identity: true,
|
|
1368
|
+
runtime: status.runtime,
|
|
1369
|
+
client: status.client,
|
|
1370
|
+
installation: status.installation,
|
|
1371
|
+
model: status.model,
|
|
1372
|
+
memory: status.memory,
|
|
1373
|
+
bridge: status.bridge,
|
|
1374
|
+
closed_loop: status.closed_loop,
|
|
1375
|
+
upstream_adapter: status.upstream_adapter,
|
|
1376
|
+
public_experience: status.public_experience,
|
|
1377
|
+
auto_sync: status.auto_sync,
|
|
1378
|
+
message: "Local EmbedLabs Agent status was read from this computer. EmbedLabs uses local model configuration, local installation identity, BoardPack, SDK, Local Bridge, compile, flash, deploy, and debug workflows on this machine.",
|
|
1379
|
+
next_commands: statusNextCommands.length > 0 ? statusNextCommands : [
|
|
1380
|
+
{
|
|
1381
|
+
label: "check_local_agent",
|
|
1382
|
+
command: "embedlabs agent status --json",
|
|
1383
|
+
description: "Check local Agent identity, model config, memory, and Local Bridge status."
|
|
1384
|
+
}
|
|
1385
|
+
]
|
|
1386
|
+
};
|
|
1060
1387
|
}
|
|
1061
1388
|
|
|
1062
1389
|
function formatCurrencyCents(value, currency = "USD") {
|
|
@@ -1065,45 +1392,6 @@ function formatCurrencyCents(value, currency = "USD") {
|
|
|
1065
1392
|
return `${currency || "USD"} ${(amount / 100).toFixed(2)}`;
|
|
1066
1393
|
}
|
|
1067
1394
|
|
|
1068
|
-
function authNotReadyResult(status) {
|
|
1069
|
-
const authenticated = status?.authenticated === true;
|
|
1070
|
-
const integrity = asString(status?.device_integrity) || (status?.device_id ? "unknown" : "unbound");
|
|
1071
|
-
const code = !authenticated ? "auth_token_missing" : integrity === "failed" ? "tool_integrity_check_failed" : "auth_not_ready";
|
|
1072
|
-
const message = code === "tool_integrity_check_failed"
|
|
1073
|
-
? TOOL_INTEGRITY_RELOGIN_MESSAGE
|
|
1074
|
-
: !authenticated
|
|
1075
|
-
? "Embed Labs API Token is not configured for this plugin."
|
|
1076
|
-
: "Embed Labs API Token 已配置,但还没有完成本机设备绑定。";
|
|
1077
|
-
return {
|
|
1078
|
-
ok: false,
|
|
1079
|
-
error: {
|
|
1080
|
-
code,
|
|
1081
|
-
message,
|
|
1082
|
-
remediation: [
|
|
1083
|
-
"1. Open https://api.embedboard.com/dashboard and sign in or register.",
|
|
1084
|
-
"2. Create or copy your Embed Labs API Token.",
|
|
1085
|
-
"3. Update the local CLI, then run: embedlabs auth login --token <your_token>",
|
|
1086
|
-
"4. Verify device binding with: embedlabs auth device status",
|
|
1087
|
-
"5. Restart Codex/OpenCode/Trae so the plugin picks up the refreshed CLI and auth file."
|
|
1088
|
-
].join("\n"),
|
|
1089
|
-
details: {
|
|
1090
|
-
dashboard_url: "https://api.embedboard.com/dashboard",
|
|
1091
|
-
login_command: "embedlabs auth login --token <your_token>",
|
|
1092
|
-
auth_status_command: "embedlabs auth status",
|
|
1093
|
-
device_status_command: "embedlabs auth device status",
|
|
1094
|
-
auth_file: ".embed-labs/auth.json",
|
|
1095
|
-
current_status: {
|
|
1096
|
-
authenticated,
|
|
1097
|
-
source: status?.source,
|
|
1098
|
-
profile: status?.profile,
|
|
1099
|
-
device_id: status?.device_id,
|
|
1100
|
-
device_integrity: integrity
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
};
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
1395
|
function pickArtifact(response, preferredKind) {
|
|
1108
1396
|
const data = responseData(response);
|
|
1109
1397
|
const artifacts = Array.isArray(data?.artifacts) ? data.artifacts : [];
|
|
@@ -1120,73 +1408,72 @@ function pickArtifact(response, preferredKind) {
|
|
|
1120
1408
|
async function cloudStatus(args) {
|
|
1121
1409
|
const template = asString(args.template_id || args.template);
|
|
1122
1410
|
const includeRaw = boolValue(args.include_raw);
|
|
1123
|
-
const [modelDefault, serviceModes] = await Promise.all([
|
|
1124
|
-
runEmbed(["model", "default"]),
|
|
1125
|
-
runEmbed(["service", "modes"])
|
|
1126
|
-
]);
|
|
1127
1411
|
if (!template) {
|
|
1128
1412
|
const boards = await supportedBoards(args);
|
|
1129
1413
|
return {
|
|
1130
|
-
model_default: modelDefault,
|
|
1131
|
-
service_modes: serviceModes,
|
|
1132
1414
|
...boards,
|
|
1415
|
+
compatibility_adapter: "dbt_cloud_status_to_agent_board_metadata",
|
|
1416
|
+
agent_actions: ["install_toolchain", "manage_board_pack"],
|
|
1133
1417
|
selected_template_id: null,
|
|
1134
1418
|
board_methods: undefined,
|
|
1135
|
-
|
|
1419
|
+
model_service_catalog_retired: true,
|
|
1420
|
+
note: "No template_id was supplied, so this compatibility response lists supported boards only. Model/service catalogs are retired from the plugin surface. Normal clients should use embedlabs_action_run with action_id=manage_board_pack for supported-board/BoardPack catalog lists and action_id=install_toolchain only for SDK/toolchain install, update, uninstall, or package-status requests."
|
|
1136
1421
|
};
|
|
1137
1422
|
}
|
|
1138
|
-
const [
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1423
|
+
const [boards, knowledge] = await Promise.all([
|
|
1424
|
+
supportedBoards(args),
|
|
1425
|
+
boardKnowledgeSearch({
|
|
1426
|
+
template_id: template,
|
|
1427
|
+
query: asString(args.query) || "board capabilities toolchain install flash deploy debug pinout workflow",
|
|
1428
|
+
include_raw: includeRaw
|
|
1429
|
+
})
|
|
1142
1430
|
]);
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
const
|
|
1153
|
-
const
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
method_id:
|
|
1163
|
-
|
|
1164
|
-
tool: item.api?.mvp_tool || item.mvp_tool,
|
|
1165
|
-
summary: summarizePublicText(item.summary || item.description)
|
|
1166
|
-
}));
|
|
1431
|
+
return compactCloudStatusResult({ template, boards, knowledge, includeRaw });
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
function compactCloudStatusResult({ template, boards, knowledge, includeRaw }) {
|
|
1435
|
+
const table = Array.isArray(boards?.table) ? boards.table : [];
|
|
1436
|
+
const selectedBoard =
|
|
1437
|
+
table.find((item) => item.template_id === template || item.board_id === template) ||
|
|
1438
|
+
table.find((item) => String(item.template_id || "").includes(template)) ||
|
|
1439
|
+
null;
|
|
1440
|
+
const knowledgeData = knowledge?.board_knowledge || knowledge?.execution?.knowledge || {};
|
|
1441
|
+
const method_table = [
|
|
1442
|
+
{ method_id: "detect_board", tool: "embedlabs_action_run", summary: "Detect connected local hardware through Go Local Bridge inventory." },
|
|
1443
|
+
{ method_id: "query_board_knowledge", tool: "embedlabs_action_run", summary: "Answer pinout, SDK, flash, debug, and board workflow questions from the board pack." },
|
|
1444
|
+
{ method_id: "install_toolchain", tool: "embedlabs_action_run", summary: "List, install, update, or uninstall local board toolchain packages." },
|
|
1445
|
+
{ method_id: "build_project", tool: "embedlabs_action_run", summary: "Build a local project with the installed local SDK/toolchain." },
|
|
1446
|
+
{ method_id: "flash_image", tool: "embedlabs_action_run", summary: "Plan and run approved local flashing through Local Bridge." },
|
|
1447
|
+
{ method_id: "deploy_app", tool: "embedlabs_action_run", summary: "Deploy and optionally run a local app on a selected board." },
|
|
1448
|
+
{ method_id: "debug_board", tool: "embedlabs_action_run", summary: "Run local diagnostics for the selected connected board." },
|
|
1449
|
+
{ method_id: "capture_signal", tool: "embedlabs_action_run", summary: "Capture and decode hardware signals through RP2350 Monitor/Local Bridge." },
|
|
1450
|
+
{ method_id: "manage_board_pack", tool: "embedlabs_action_run", summary: "List or inspect BoardPack catalog/package metadata without adding visible MCP tools." }
|
|
1451
|
+
];
|
|
1167
1452
|
return compactObject({
|
|
1168
|
-
ok:
|
|
1453
|
+
ok: boards?.ok !== false && knowledge?.ok !== false,
|
|
1454
|
+
compatibility_adapter: "dbt_cloud_status_to_agent_board_metadata",
|
|
1169
1455
|
template_id: template,
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
display_name: model.display_name || model.name
|
|
1174
|
-
}),
|
|
1175
|
-
service_modes: compactObject({
|
|
1176
|
-
default_mode: service.default_mode || service.defaultMode,
|
|
1177
|
-
modes: Array.isArray(service.modes) ? service.modes.map((item) => item.id || item.name || item).slice(0, 10) : undefined
|
|
1178
|
-
}),
|
|
1456
|
+
selected_template_id: template,
|
|
1457
|
+
model_service_catalog_retired: true,
|
|
1458
|
+
agent_actions: ["install_toolchain", "query_board_knowledge", "manage_board_pack"],
|
|
1179
1459
|
board: compactObject({
|
|
1180
|
-
template_id:
|
|
1181
|
-
display_name:
|
|
1182
|
-
|
|
1183
|
-
|
|
1460
|
+
template_id: selectedBoard?.template_id || template,
|
|
1461
|
+
display_name: selectedBoard?.board,
|
|
1462
|
+
category: selectedBoard?.category,
|
|
1463
|
+
install_status: selectedBoard?.install_status,
|
|
1464
|
+
local_version: selectedBoard?.local_version,
|
|
1465
|
+
server_version: selectedBoard?.server_version,
|
|
1466
|
+
modes: selectedBoard?.modes
|
|
1184
1467
|
}),
|
|
1185
|
-
|
|
1468
|
+
board_table: table,
|
|
1469
|
+
board_methods: method_table,
|
|
1470
|
+
method_count: method_table.length,
|
|
1186
1471
|
method_table,
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1472
|
+
board_knowledge: knowledgeData,
|
|
1473
|
+
summary_for_user: knowledge?.summary_for_user || knowledge?.answer || "Board metadata is available through local Agent compatibility actions.",
|
|
1474
|
+
guidance: "This is a compatibility adapter. For normal use, call embedlabs_action_schema and embedlabs_action_run for the listed high-level actions: manage_board_pack for board/BoardPack catalog lists, install_toolchain for SDK/toolchain package status and install/update/uninstall, and query_board_knowledge for factual board questions. Compatibility tools such as dbt_supported_boards and dbt_board_knowledge_search are diagnostics only. Model/provider configuration stays local and is not part of this tool.",
|
|
1475
|
+
raw: includeRaw ? { supported_boards: boards, knowledge } : undefined,
|
|
1476
|
+
raw_omitted: !includeRaw
|
|
1190
1477
|
});
|
|
1191
1478
|
}
|
|
1192
1479
|
|
|
@@ -1196,103 +1483,252 @@ async function boardKnowledgeSearch(args) {
|
|
|
1196
1483
|
if (!query) {
|
|
1197
1484
|
throw new Error("query is required");
|
|
1198
1485
|
}
|
|
1199
|
-
const
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1486
|
+
const routed = await agentActionRun({
|
|
1487
|
+
action_id: "query_board_knowledge",
|
|
1488
|
+
arguments_json: JSON.stringify(compactObject({
|
|
1489
|
+
template_id: template,
|
|
1490
|
+
query,
|
|
1491
|
+
source: asString(args.source),
|
|
1492
|
+
limit: asString(args.limit)
|
|
1493
|
+
})),
|
|
1494
|
+
include_raw: args.include_raw
|
|
1495
|
+
});
|
|
1496
|
+
return {
|
|
1497
|
+
...routed,
|
|
1498
|
+
compatibility_adapter: "dbt_board_knowledge_search_to_agent_query_board_knowledge",
|
|
1499
|
+
board_knowledge: routed.execution?.knowledge,
|
|
1500
|
+
summary_for_user: routed.execution?.summary ?? routed.answer,
|
|
1501
|
+
guidance: "This compatibility tool now routes through the local EmbedLabs Agent query_board_knowledge action. Prefer embedlabs_chat for natural-language questions or embedlabs_action_run when the action contract is explicit."
|
|
1502
|
+
};
|
|
1203
1503
|
}
|
|
1204
1504
|
|
|
1205
|
-
async function supportedBoards() {
|
|
1206
|
-
const
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1505
|
+
async function supportedBoards(args = {}) {
|
|
1506
|
+
const routed = await agentActionRun({
|
|
1507
|
+
action_id: "manage_board_pack",
|
|
1508
|
+
arguments_json: JSON.stringify({ operation: "list" }),
|
|
1509
|
+
include_raw: args.include_raw
|
|
1510
|
+
});
|
|
1511
|
+
let toolchainRouted;
|
|
1512
|
+
try {
|
|
1513
|
+
toolchainRouted = await agentActionRun({
|
|
1514
|
+
action_id: "install_toolchain",
|
|
1515
|
+
arguments_json: JSON.stringify({ operation: "list" }),
|
|
1516
|
+
include_raw: args.include_raw
|
|
1517
|
+
});
|
|
1518
|
+
} catch (error) {
|
|
1519
|
+
toolchainRouted = {
|
|
1520
|
+
ok: false,
|
|
1521
|
+
error: { code: "toolchain_status_unavailable", message: error instanceof Error ? error.message : String(error) }
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
const boardPack = routed.execution?.board_pack || {};
|
|
1525
|
+
const profiles = Array.isArray(boardPack.profiles) ? boardPack.profiles : [];
|
|
1526
|
+
const operation = toolchainRouted?.execution?.toolchain_operation || {};
|
|
1527
|
+
const environments = Array.isArray(operation.environments) ? operation.environments : [];
|
|
1528
|
+
const environmentByBoard = new Map(environments.map((item) => [item.board_id || item.template_id, item]));
|
|
1529
|
+
const table = profiles.map((profile) => {
|
|
1530
|
+
const templateId = profile.template_id || profile.board_id;
|
|
1531
|
+
const item = environmentByBoard.get(templateId) || {};
|
|
1532
|
+
return ({
|
|
1533
|
+
board: profile.display_name || item.display_name || templateId,
|
|
1534
|
+
template_id: templateId,
|
|
1535
|
+
category: boardUxCategory(profile, []),
|
|
1536
|
+
support_level: profile.support_level || "",
|
|
1537
|
+
method_count: profile.method_count ?? "",
|
|
1538
|
+
knowledge_file_count: profile.knowledge_file_count ?? "",
|
|
1539
|
+
main_functions: Array.isArray(item.modes) ? item.modes.join(", ") : "",
|
|
1540
|
+
install_status: item.status || "unknown",
|
|
1541
|
+
local_version: item.local_version || "not installed",
|
|
1542
|
+
server_version: item.server_version || "unknown",
|
|
1543
|
+
modes: Array.isArray(item.modes) ? item.modes.join(",") : "",
|
|
1544
|
+
next_step: item.install_command || `embedlabs agent action run install_toolchain --operation install --board ${templateId} --json`,
|
|
1545
|
+
show: `embedlabs agent action run manage_board_pack --operation show --template-id ${templateId} --json`,
|
|
1546
|
+
install: item.install_command,
|
|
1547
|
+
update: item.update_command,
|
|
1548
|
+
uninstall: item.uninstall_command
|
|
1240
1549
|
});
|
|
1241
1550
|
});
|
|
1242
|
-
for (const env of environments) {
|
|
1243
|
-
const boardId = asString(env.board_id);
|
|
1244
|
-
if (!boardId || supported.some((item) => item.template_id === boardId)) continue;
|
|
1245
|
-
supported.push(compactObject({
|
|
1246
|
-
template_id: boardId,
|
|
1247
|
-
board_id: boardId,
|
|
1248
|
-
display_name: normalizeSupportedBoardDisplayName(env.display_name || boardId),
|
|
1249
|
-
category: boardUxCategory({ template_id: boardId, board_id: boardId }, []),
|
|
1250
|
-
support_level: "local_toolchain",
|
|
1251
|
-
cloud_method_count: 0,
|
|
1252
|
-
cloud_method_runtimes: [],
|
|
1253
|
-
capability_groups: localToolchainCapabilityGroups(env),
|
|
1254
|
-
recommended_next_step: recommendedBoardNextStep({ template_id: boardId, board_id: boardId }, env),
|
|
1255
|
-
local_toolchain_status: env.status,
|
|
1256
|
-
local_install_modes: Array.isArray(env.install_modes) ? env.install_modes : undefined,
|
|
1257
|
-
local_installed: env.installed,
|
|
1258
|
-
local_installed_version: env.installed?.version,
|
|
1259
|
-
server_latest_version: env.latest?.version,
|
|
1260
|
-
local_install_command: env.install_command,
|
|
1261
|
-
local_update_command: env.update_command,
|
|
1262
|
-
local_uninstall_command: `embedlabs local toolchain uninstall --board ${boardId}`
|
|
1263
|
-
}));
|
|
1264
|
-
}
|
|
1265
|
-
supported.sort((a, b) => asString(a.display_name).localeCompare(asString(b.display_name)));
|
|
1266
|
-
const table = supported.map((item) => ({
|
|
1267
|
-
board: item.display_name,
|
|
1268
|
-
template_id: item.template_id,
|
|
1269
|
-
category: item.category,
|
|
1270
|
-
main_functions: Array.isArray(item.capability_groups) ? item.capability_groups.join(", ") : "",
|
|
1271
|
-
support_level: item.support_level,
|
|
1272
|
-
install_status: item.local_toolchain_status || "unknown",
|
|
1273
|
-
local_version: item.local_installed_version || "not installed",
|
|
1274
|
-
server_version: item.server_latest_version || "unknown",
|
|
1275
|
-
modes: Array.isArray(item.local_install_modes) ? item.local_install_modes.join(",") : "",
|
|
1276
|
-
next_step: item.recommended_next_step,
|
|
1277
|
-
install: item.local_install_command,
|
|
1278
|
-
update: item.local_update_command,
|
|
1279
|
-
uninstall: item.local_uninstall_command
|
|
1280
|
-
}));
|
|
1281
1551
|
return {
|
|
1282
|
-
|
|
1552
|
+
ok: routed.ok,
|
|
1553
|
+
compatibility_adapter: "dbt_supported_boards_to_agent_manage_board_pack_list",
|
|
1554
|
+
agent_action: "manage_board_pack",
|
|
1555
|
+
companion_agent_action: "install_toolchain",
|
|
1556
|
+
supported_boards: profiles,
|
|
1557
|
+
board_packs: profiles,
|
|
1558
|
+
toolchain_environments: environments,
|
|
1283
1559
|
table,
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1560
|
+
execution: routed.execution,
|
|
1561
|
+
toolchain_execution: toolchainRouted?.execution,
|
|
1562
|
+
next_commands: [
|
|
1563
|
+
...(Array.isArray(routed.next_commands) ? routed.next_commands : []),
|
|
1564
|
+
...(Array.isArray(toolchainRouted?.next_commands) ? toolchainRouted.next_commands : [])
|
|
1565
|
+
],
|
|
1566
|
+
guidance: "When the user asks which boards are supported, show board, template_id, category, support_level, install_status, local_version, server_version, modes, and next_step as a Markdown table. Use embedlabs_action_run action_id=manage_board_pack with operation=list/show for BoardPack catalog questions, and action_id=install_toolchain with operation=list/install/update/uninstall only for SDK/toolchain package management. Do not call low-level toolchain tools unless the user explicitly asks for raw diagnostics, and never shell rm.",
|
|
1567
|
+
observed_at: new Date().toISOString()
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
async function agentActionSchema(args) {
|
|
1572
|
+
const actionId = asString(args.action_id || args.action || args.name);
|
|
1573
|
+
const command = actionId
|
|
1574
|
+
? ["agent", "action", "schema", actionId]
|
|
1575
|
+
: ["agent", "action", "schema"];
|
|
1576
|
+
const result = await runEmbed(command);
|
|
1577
|
+
const data = responseData(result) || result?.data || {};
|
|
1578
|
+
return {
|
|
1579
|
+
ok: true,
|
|
1580
|
+
schema_source: "embedlabs-agent-action-catalog",
|
|
1581
|
+
action_id: actionId || undefined,
|
|
1582
|
+
response: boolValue(args.include_raw) ? result : undefined,
|
|
1583
|
+
raw_omitted: !boolValue(args.include_raw),
|
|
1584
|
+
action: data.action,
|
|
1585
|
+
actions: data.actions,
|
|
1586
|
+
definition: data.definition,
|
|
1587
|
+
guidance: "Use input_schema and safety.must_not_infer before calling hardware or local-file actions. Do not infer pins, private paths, target boards, or approval from protocol words or previous memory."
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
async function agentActionRun(args) {
|
|
1592
|
+
const startedAt = Date.now();
|
|
1593
|
+
const actionId = asString(args.action_id || args.action || args.name);
|
|
1594
|
+
if (!actionId) {
|
|
1595
|
+
throw new Error("action_id is required");
|
|
1596
|
+
}
|
|
1597
|
+
const command = ["agent", "action", "run", actionId];
|
|
1598
|
+
const payload = agentActionPayload(args);
|
|
1599
|
+
const legacyCaptureArgumentIssue = captureSignalLegacyActionArgumentIssue(actionId, payload);
|
|
1600
|
+
if (legacyCaptureArgumentIssue) return legacyCaptureArgumentIssue;
|
|
1601
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
1602
|
+
appendAgentActionFlag(command, key, value);
|
|
1603
|
+
}
|
|
1604
|
+
const result = await runEmbed(command);
|
|
1605
|
+
const data = responseData(result) || result?.data || {};
|
|
1606
|
+
const executionOk = data.execution?.ok;
|
|
1607
|
+
const cliOk = result?.response?.ok !== false;
|
|
1608
|
+
const localDeviceId = localDeviceIdForTool(args) || inferLocalDeviceIdFromToolResponse(data);
|
|
1609
|
+
const summary = data.execution?.summary || data.answer || data.execution?.error?.message || `${actionId} completed.`;
|
|
1610
|
+
const logResult = await recordMcpToolEvent("embedlabs_action_run", {
|
|
1611
|
+
client: mcpClientName(),
|
|
1612
|
+
mode: "ide_plugin",
|
|
1613
|
+
success: cliOk && executionOk !== false,
|
|
1614
|
+
duration_ms: Date.now() - startedAt,
|
|
1615
|
+
local_device_id: localDeviceId,
|
|
1616
|
+
input_summary: `agent_action=${actionId} ${JSON.stringify(payload).slice(0, 180)}`,
|
|
1617
|
+
output_summary: asString(summary).slice(0, 240)
|
|
1618
|
+
});
|
|
1619
|
+
return {
|
|
1620
|
+
ok: cliOk,
|
|
1621
|
+
cli_ok: cliOk,
|
|
1622
|
+
execution_ok: executionOk,
|
|
1623
|
+
schema_hint: "Use embedlabs_action_schema before this tool when arguments are unclear.",
|
|
1624
|
+
action_id: actionId,
|
|
1625
|
+
response: boolValue(args.include_raw) ? result : undefined,
|
|
1626
|
+
raw_omitted: !boolValue(args.include_raw),
|
|
1627
|
+
execution: sanitizeMcpPublicValue(data.execution),
|
|
1628
|
+
report: sanitizeMcpPublicValue(data.report),
|
|
1629
|
+
secondary_reports: sanitizeMcpPublicValue(data.secondary_reports),
|
|
1630
|
+
auto_sync: sanitizeMcpPublicValue(data.auto_sync),
|
|
1631
|
+
next_commands: sanitizeMcpPublicValue(data.next_commands),
|
|
1632
|
+
answer: sanitizeMcpPublicValue(data.answer),
|
|
1633
|
+
public_experience: sanitizeMcpPublicValue(data.public_experience),
|
|
1634
|
+
usage: {
|
|
1635
|
+
mode: "ide_plugin",
|
|
1636
|
+
input_tokens: 0,
|
|
1637
|
+
output_tokens: 0,
|
|
1638
|
+
local_device_id: localDeviceId || undefined,
|
|
1639
|
+
mcp_service_log: logResult
|
|
1640
|
+
}
|
|
1641
|
+
};
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
function captureSignalLegacyActionArgumentIssue(actionId, payload) {
|
|
1645
|
+
if (actionId !== "capture_signal" || !payload || typeof payload !== "object") return undefined;
|
|
1646
|
+
const normalizedKeys = new Map(Object.keys(payload).map((key) => [toCliFlagName(key), key]));
|
|
1647
|
+
if (normalizedKeys.has("channels") || normalizedKeys.has("channel")) {
|
|
1648
|
+
const originalKey = normalizedKeys.get("channels") || normalizedKeys.get("channel") || "channels";
|
|
1649
|
+
return legacyCaptureArgumentFailure({
|
|
1650
|
+
actionId,
|
|
1651
|
+
key: originalKey,
|
|
1652
|
+
message: "`channels` is a low-level compatibility alias. For the high-level capture_signal action, pass explicit `pin_base` and `pin_count` or `pins`."
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
if (normalizedKeys.has("duration-ms")) {
|
|
1656
|
+
const originalKey = normalizedKeys.get("duration-ms") || "duration_ms";
|
|
1657
|
+
return legacyCaptureArgumentFailure({
|
|
1658
|
+
actionId,
|
|
1659
|
+
key: originalKey,
|
|
1660
|
+
message: "`duration_ms` is a low-level compatibility alias. For the high-level capture_signal action, pass explicit `samples`; compute samples = ceil(duration_ms * sample_rate / 1000)."
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1663
|
+
return undefined;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
function legacyCaptureArgumentFailure({ actionId, key, message }) {
|
|
1667
|
+
return {
|
|
1668
|
+
ok: false,
|
|
1669
|
+
cli_ok: false,
|
|
1670
|
+
execution_ok: false,
|
|
1671
|
+
action_id: actionId,
|
|
1672
|
+
error: {
|
|
1673
|
+
code: "invalid_args",
|
|
1674
|
+
message: `capture_signal received unsupported ${key}: ${message}`
|
|
1288
1675
|
},
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1676
|
+
summary_for_user: `capture_signal received unsupported ${key}: ${message}`,
|
|
1677
|
+
schema_hint: "Call embedlabs_action_schema with action_id=capture_signal and use the high-level input schema.",
|
|
1678
|
+
next_commands: [
|
|
1679
|
+
{
|
|
1680
|
+
label: "describe_capture_signal",
|
|
1681
|
+
command: "embedlabs agent action describe capture_signal --json",
|
|
1682
|
+
description: "Review the high-level capture_signal schema before retrying."
|
|
1683
|
+
}
|
|
1684
|
+
]
|
|
1685
|
+
};
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
async function agentChat(args) {
|
|
1689
|
+
const startedAt = Date.now();
|
|
1690
|
+
const prompt = asString(args.prompt || args.request || args.message);
|
|
1691
|
+
if (!prompt) {
|
|
1692
|
+
throw new Error("prompt is required");
|
|
1693
|
+
}
|
|
1694
|
+
const command = ["agent", "chat", "--prompt", prompt];
|
|
1695
|
+
if (!boolValue(args.use_model || args.useModel)) {
|
|
1696
|
+
command.push("--local-only");
|
|
1697
|
+
}
|
|
1698
|
+
const session = asString(args.session || args.chat_session_id);
|
|
1699
|
+
if (session) command.push("--session", session);
|
|
1700
|
+
else if (boolValue(args.new_session || args.newSession)) command.push("--new-session");
|
|
1701
|
+
const result = await runEmbed(command);
|
|
1702
|
+
const data = responseData(result) || result?.data || {};
|
|
1703
|
+
const turns = Array.isArray(data.turns) ? data.turns : [];
|
|
1704
|
+
const firstTurn = turns[0] || {};
|
|
1705
|
+
const logResult = await recordMcpToolEvent("embedlabs_chat", {
|
|
1706
|
+
client: mcpClientName(),
|
|
1707
|
+
mode: "ide_plugin",
|
|
1708
|
+
success: result?.response?.ok !== false && !firstTurn.report,
|
|
1709
|
+
duration_ms: Date.now() - startedAt,
|
|
1710
|
+
input_summary: `agent_chat ${prompt.slice(0, 180)}`,
|
|
1711
|
+
output_summary: asString(firstTurn.answer || firstTurn.report?.error?.message || data.answer || "agent chat completed").slice(0, 240)
|
|
1712
|
+
});
|
|
1713
|
+
return {
|
|
1714
|
+
ok: result?.response?.ok !== false,
|
|
1715
|
+
mode: data.mode,
|
|
1716
|
+
answer: sanitizeMcpPublicValue(firstTurn.answer),
|
|
1717
|
+
intent: firstTurn.intent,
|
|
1718
|
+
chat_session: sanitizeMcpPublicValue(data.chat_session),
|
|
1719
|
+
report: sanitizeMcpPublicValue(firstTurn.report),
|
|
1720
|
+
secondary_reports: sanitizeMcpPublicValue(firstTurn.secondary_reports),
|
|
1721
|
+
public_experience: sanitizeMcpPublicValue(firstTurn.public_experience || data.public_experience),
|
|
1722
|
+
auto_sync: sanitizeMcpPublicValue(firstTurn.auto_sync || data.auto_sync),
|
|
1723
|
+
next_commands: sanitizeMcpPublicValue(data.next_commands || firstTurn.next_commands),
|
|
1724
|
+
response: boolValue(args.include_raw) ? result : undefined,
|
|
1725
|
+
usage: {
|
|
1726
|
+
mode: "ide_plugin",
|
|
1727
|
+
input_tokens: 0,
|
|
1728
|
+
output_tokens: 0,
|
|
1729
|
+
mcp_service_log: logResult
|
|
1293
1730
|
},
|
|
1294
|
-
guidance: "
|
|
1295
|
-
observed_at: new Date().toISOString()
|
|
1731
|
+
guidance: "Continue with embedlabs_chat and the returned chat_session.chat_session_id when the user expects context. Use embedlabs_action_schema/embedlabs_action_run only when a specific high-level action contract is required."
|
|
1296
1732
|
};
|
|
1297
1733
|
}
|
|
1298
1734
|
|
|
@@ -1319,14 +1755,14 @@ function redactSensitive(value) {
|
|
|
1319
1755
|
return asString(value)
|
|
1320
1756
|
.replace(/(sk-[A-Za-z0-9_-]{8,})/g, "[redacted]")
|
|
1321
1757
|
.replace(/(npm_[A-Za-z0-9_-]{8,})/g, "[redacted]")
|
|
1322
|
-
.replace(
|
|
1758
|
+
.replace(/\b(re_[A-Za-z0-9_-]{8,})\b/g, "[redacted]")
|
|
1323
1759
|
.slice(0, 500);
|
|
1324
1760
|
}
|
|
1325
1761
|
|
|
1326
1762
|
function normalizeSupportedBoardDisplayName(value) {
|
|
1327
1763
|
const text = asString(value);
|
|
1328
1764
|
if (!text) return "";
|
|
1329
|
-
if (
|
|
1765
|
+
if (/taishanpi/i.test(text) && /rk3566/i.test(text) && /template/i.test(text)) return "TaishanPi 1M RK3566";
|
|
1330
1766
|
return text;
|
|
1331
1767
|
}
|
|
1332
1768
|
|
|
@@ -1381,65 +1817,81 @@ function recommendedBoardNextStep(profile, localEnv) {
|
|
|
1381
1817
|
}
|
|
1382
1818
|
|
|
1383
1819
|
async function boardDebug(args) {
|
|
1384
|
-
const
|
|
1385
|
-
const
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1820
|
+
const artifactPath = asString(args.artifact_path || args.artifact);
|
|
1821
|
+
const approved = boolValue(args.approve) || boolValue(args.approved);
|
|
1822
|
+
if (artifactPath && approved) {
|
|
1823
|
+
const routed = await agentActionRun({
|
|
1824
|
+
action_id: "deploy_app",
|
|
1825
|
+
arguments_json: JSON.stringify(compactObject({
|
|
1826
|
+
board_id: asString(args.board_id || args.board),
|
|
1827
|
+
local_device_id: asString(args.local_device_id || args.device_id || args.device),
|
|
1828
|
+
host: asString(args.host),
|
|
1829
|
+
user: asString(args.user),
|
|
1830
|
+
artifact_path: artifactPath,
|
|
1831
|
+
remote_path: asString(args.remote_path || args.remotePath),
|
|
1832
|
+
remote_dir: asString(args.remote_dir || args.remoteDir),
|
|
1833
|
+
remote_file: asString(args.remote_file || args.remoteFile),
|
|
1834
|
+
wrapper_script: asString(args.wrapper_script || args.run_script || args.wrapperScript || args.runScript),
|
|
1835
|
+
run_command: asString(args.run_command || args.runCommand),
|
|
1836
|
+
run: boolValue(args.run) || undefined,
|
|
1837
|
+
approve: true
|
|
1838
|
+
})),
|
|
1839
|
+
include_raw: args.include_raw,
|
|
1840
|
+
timeout_ms: args.timeout_ms
|
|
1841
|
+
});
|
|
1842
|
+
return {
|
|
1843
|
+
ok: routed.ok,
|
|
1844
|
+
compatibility_adapter: "dbt_board_debug_to_agent_deploy_app",
|
|
1845
|
+
agent_action: "deploy_app",
|
|
1846
|
+
execution: routed.execution,
|
|
1847
|
+
summary_for_user: routed.execution?.summary ?? routed.answer,
|
|
1848
|
+
response: boolValue(args.include_raw) ? routed.response : undefined
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1851
|
+
const routed = await agentActionRun({
|
|
1852
|
+
action_id: "debug_board",
|
|
1853
|
+
arguments_json: JSON.stringify(compactObject({
|
|
1854
|
+
board_id: asString(args.board_id || args.board),
|
|
1855
|
+
local_device_id: asString(args.local_device_id || args.device_id || args.device),
|
|
1856
|
+
host: asString(args.host),
|
|
1857
|
+
scope: asString(args.scope)
|
|
1858
|
+
})),
|
|
1859
|
+
include_raw: args.include_raw,
|
|
1860
|
+
timeout_ms: args.timeout_ms
|
|
1861
|
+
});
|
|
1862
|
+
return {
|
|
1863
|
+
ok: routed.ok,
|
|
1864
|
+
compatibility_adapter: "dbt_board_debug_to_agent_debug_board",
|
|
1865
|
+
agent_action: "debug_board",
|
|
1866
|
+
execution: routed.execution,
|
|
1867
|
+
tool_results: routed.execution?.tool_results,
|
|
1868
|
+
summary_for_user: routed.execution?.summary ?? routed.answer,
|
|
1869
|
+
response: boolValue(args.include_raw) ? routed.response : undefined
|
|
1870
|
+
};
|
|
1401
1871
|
}
|
|
1402
1872
|
|
|
1403
|
-
async function currentBoardStatus(args
|
|
1404
|
-
|
|
1405
|
-
const explicitHost = asString(args.host);
|
|
1406
|
-
const ports = parsePorts(args.ports || DEFAULT_STATUS_PORTS);
|
|
1873
|
+
async function currentBoardStatus(args) {
|
|
1874
|
+
const startedAt = Date.now();
|
|
1407
1875
|
const includeRaw = boolValue(args.include_raw);
|
|
1408
|
-
const requestedLocalDeviceId = localDeviceIdForTool(args
|
|
1409
|
-
const
|
|
1410
|
-
const
|
|
1411
|
-
const host = explicitHost || taishanHost;
|
|
1412
|
-
const probe = host
|
|
1413
|
-
? await runEmbed(["tool", "call", "device.probe", "--input-json", JSON.stringify({ host, ports })])
|
|
1414
|
-
: emptyBoardStatusProbe();
|
|
1415
|
-
const summary = summarizeBoardStatus(scan, probe, ports);
|
|
1876
|
+
const requestedLocalDeviceId = localDeviceIdForTool(args);
|
|
1877
|
+
const detected = await runEmbed(detectBoardAgentCommand(args));
|
|
1878
|
+
const summary = summarizeAgentDetectBoardStatus(detected);
|
|
1416
1879
|
const localDeviceId = requestedLocalDeviceId || (summary.local_device_ids?.length === 1 ? summary.local_device_ids[0] : "");
|
|
1417
|
-
if (includeRaw && authStatus?.ok) {
|
|
1418
|
-
summary.device_binding = {
|
|
1419
|
-
authenticated: true,
|
|
1420
|
-
account_id: authStatus.account_id,
|
|
1421
|
-
api_key_id: authStatus.api_key_id,
|
|
1422
|
-
device_id: authStatus.device_id,
|
|
1423
|
-
device_integrity: authStatus.device_integrity
|
|
1424
|
-
};
|
|
1425
|
-
}
|
|
1426
1880
|
if (localDeviceId) {
|
|
1427
1881
|
summary.local_device_id = localDeviceId;
|
|
1428
1882
|
}
|
|
1429
1883
|
const logResult = await recordMcpToolEvent("dbt_current_board_status", {
|
|
1430
1884
|
client: "codex",
|
|
1431
|
-
mode: "
|
|
1432
|
-
server_model_used: false,
|
|
1885
|
+
mode: "ide_plugin",
|
|
1433
1886
|
success: summary.query_ok === true,
|
|
1434
1887
|
duration_ms: Date.now() - startedAt,
|
|
1435
1888
|
local_device_id: localDeviceId,
|
|
1436
|
-
input_summary:
|
|
1889
|
+
input_summary: "agent_action=detect_board inventory_only",
|
|
1437
1890
|
output_summary: summary.status_text
|
|
1438
1891
|
});
|
|
1439
1892
|
if (includeRaw) {
|
|
1440
1893
|
summary.usage = {
|
|
1441
|
-
mode: "
|
|
1442
|
-
server_model_used: false,
|
|
1894
|
+
mode: "ide_plugin",
|
|
1443
1895
|
input_tokens: 0,
|
|
1444
1896
|
output_tokens: 0,
|
|
1445
1897
|
mcp_service_log: logResult
|
|
@@ -1450,6 +1902,64 @@ async function currentBoardStatus(args, authStatus = undefined) {
|
|
|
1450
1902
|
return summary;
|
|
1451
1903
|
}
|
|
1452
1904
|
|
|
1905
|
+
function detectBoardAgentCommand(args = {}) {
|
|
1906
|
+
const command = ["agent", "action", "run", "detect_board"];
|
|
1907
|
+
pushOptional(command, "board", args.board_id || args.board);
|
|
1908
|
+
pushOptional(command, "device", args.local_device_id || args.device_id || args.device);
|
|
1909
|
+
return command;
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
function summarizeAgentDetectBoardStatus(actionResult) {
|
|
1913
|
+
const data = responseData(actionResult) || {};
|
|
1914
|
+
const execution = objectValue(data.execution) || data;
|
|
1915
|
+
const bridge = objectValue(execution.bridge) || objectValue(data.bridge) || {};
|
|
1916
|
+
const inventory = objectValue(bridge.inventory) || {};
|
|
1917
|
+
const rawBoards = Array.isArray(execution.boards)
|
|
1918
|
+
? execution.boards
|
|
1919
|
+
: execution.board
|
|
1920
|
+
? [execution.board]
|
|
1921
|
+
: Array.isArray(inventory.boards)
|
|
1922
|
+
? inventory.boards
|
|
1923
|
+
: [];
|
|
1924
|
+
const boards = rawBoards.map((item) => summarizeBoardDevice(item, new Set()));
|
|
1925
|
+
const localDeviceIds = boards
|
|
1926
|
+
.map((item) => asString(item.local_device_id || item.device_id))
|
|
1927
|
+
.filter(Boolean);
|
|
1928
|
+
const statusText = asString(execution.summary)
|
|
1929
|
+
|| asString(data.answer)
|
|
1930
|
+
|| asString(inventory.summary_for_user)
|
|
1931
|
+
|| asString(inventory.status_text)
|
|
1932
|
+
|| (boards.length > 0
|
|
1933
|
+
? boards.map((board) => `${board.display_name || board.board_id || "Board"} is ${board.status || "detected"}.`).join(" ")
|
|
1934
|
+
: "No supported development board is currently detected by Local Bridge.");
|
|
1935
|
+
const result = {
|
|
1936
|
+
board_connected: boards.some((item) => item.connected === true || item.status === "connected"),
|
|
1937
|
+
boards,
|
|
1938
|
+
local_device_ids: localDeviceIds,
|
|
1939
|
+
tcp_ports: [],
|
|
1940
|
+
status_text: statusText,
|
|
1941
|
+
bridge_board_status: compactObject({
|
|
1942
|
+
status_text: statusText,
|
|
1943
|
+
runtime: bridge.runtime,
|
|
1944
|
+
ok: bridge.ok,
|
|
1945
|
+
inventory_seq: inventory.inventory_seq
|
|
1946
|
+
}),
|
|
1947
|
+
agent_action: compactObject({
|
|
1948
|
+
action_id: "detect_board",
|
|
1949
|
+
ok: execution.ok,
|
|
1950
|
+
high_level_action: true
|
|
1951
|
+
}),
|
|
1952
|
+
observed_at: inventory.observed_at || execution.observed_at || data.observed_at,
|
|
1953
|
+
raw_result_omitted: true,
|
|
1954
|
+
model_answer_omitted: true
|
|
1955
|
+
};
|
|
1956
|
+
Object.defineProperty(result, "query_ok", {
|
|
1957
|
+
value: actionResult?.ok === true && actionResult?.response?.ok !== false,
|
|
1958
|
+
enumerable: false
|
|
1959
|
+
});
|
|
1960
|
+
return result;
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1453
1963
|
function taishanStatusHostFromScan(scanResult) {
|
|
1454
1964
|
const scan = responseData(scanResult) || {};
|
|
1455
1965
|
const devices = Array.isArray(scan.development_boards) ? scan.development_boards : (Array.isArray(scan.devices) ? scan.devices : []);
|
|
@@ -1501,8 +2011,7 @@ async function rp2350MonitorTool(toolName, capabilityId, args) {
|
|
|
1501
2011
|
&& data?.result?.ok !== false;
|
|
1502
2012
|
const logResult = await recordMcpToolEvent(toolName, {
|
|
1503
2013
|
client: "codex",
|
|
1504
|
-
mode: "
|
|
1505
|
-
server_model_used: false,
|
|
2014
|
+
mode: "ide_plugin",
|
|
1506
2015
|
success,
|
|
1507
2016
|
duration_ms: Date.now() - startedAt,
|
|
1508
2017
|
local_device_id: localDeviceId,
|
|
@@ -1510,7 +2019,7 @@ async function rp2350MonitorTool(toolName, capabilityId, args) {
|
|
|
1510
2019
|
output_summary: asString(summary).slice(0, 240)
|
|
1511
2020
|
});
|
|
1512
2021
|
if (boolValue(args.include_raw)) {
|
|
1513
|
-
return { ...response, usage: { mode: "
|
|
2022
|
+
return { ...response, usage: { mode: "ide_plugin", input_tokens: 0, output_tokens: 0, local_device_id: localDeviceId || undefined, mcp_service_log: logResult } };
|
|
1514
2023
|
}
|
|
1515
2024
|
return response;
|
|
1516
2025
|
}
|
|
@@ -1540,8 +2049,7 @@ async function boardControl(args) {
|
|
|
1540
2049
|
&& data?.result?.ok !== false;
|
|
1541
2050
|
const logResult = await recordMcpToolEvent("dbttool", {
|
|
1542
2051
|
client: "codex",
|
|
1543
|
-
mode: "
|
|
1544
|
-
server_model_used: false,
|
|
2052
|
+
mode: "ide_plugin",
|
|
1545
2053
|
success,
|
|
1546
2054
|
duration_ms: Date.now() - startedAt,
|
|
1547
2055
|
local_device_id: localDeviceId,
|
|
@@ -1549,7 +2057,7 @@ async function boardControl(args) {
|
|
|
1549
2057
|
output_summary: asString(summary).slice(0, 240)
|
|
1550
2058
|
});
|
|
1551
2059
|
if (boolValue(args.include_raw)) {
|
|
1552
|
-
return { ...response, usage: { mode: "
|
|
2060
|
+
return { ...response, usage: { mode: "ide_plugin", input_tokens: 0, output_tokens: 0, local_device_id: localDeviceId || undefined, mcp_service_log: logResult } };
|
|
1553
2061
|
}
|
|
1554
2062
|
return response;
|
|
1555
2063
|
}
|
|
@@ -1660,8 +2168,10 @@ function compactRp2350CapabilitiesResult(response) {
|
|
|
1660
2168
|
return compactObject({
|
|
1661
2169
|
ok: response?.ok !== false && response?.response?.ok !== false,
|
|
1662
2170
|
capability_id: data.capability_id || "rp2350.monitor.capabilities",
|
|
1663
|
-
summary_for_user:
|
|
1664
|
-
preferred_mcp_tools:
|
|
2171
|
+
summary_for_user: "RP2350/Pico2 monitor capabilities are exposed as an engineering capability catalog. Default IDE profiles should use embedlabs_action_schema, embedlabs_action_run, or embedlabs_chat with high-level actions such as capture_signal, debug_board, or flash_image.",
|
|
2172
|
+
preferred_mcp_tools: ["embedlabs_action_schema", "embedlabs_action_run", "embedlabs_chat"],
|
|
2173
|
+
preferred_agent_actions: ["capture_signal", "debug_board", "flash_image"],
|
|
2174
|
+
engineering_fallback_tools: ["dbttool", "dbt_rp2350_* full-profile tools"],
|
|
1665
2175
|
operation_table,
|
|
1666
2176
|
transport_contract: result.transport_contract,
|
|
1667
2177
|
pin_model: compactObject({
|
|
@@ -1672,7 +2182,7 @@ function compactRp2350CapabilitiesResult(response) {
|
|
|
1672
2182
|
}),
|
|
1673
2183
|
example_loopback_profiles: Array.isArray(result.example_loopback_profiles) ? result.example_loopback_profiles : undefined,
|
|
1674
2184
|
unsupported: Array.isArray(result.unsupported) ? result.unsupported : undefined,
|
|
1675
|
-
guidance: "
|
|
2185
|
+
guidance: "Normal RP2350/Pico Monitor requests should use embedlabs_action_schema/embedlabs_action_run or embedlabs_chat with the high-level capture_signal, debug_board, or flash_image actions. In compact/full engineering profiles only, dbttool can call rp2350-* actions such as rp2350-spi-transfer, rp2350-logic-capture, or rp2350-logic-decode after the high-level Agent action cannot express the request or when validating a specific adapter. For closed-loop capture, prefer high-level capture_signal; lower-level logic-capture/decode is an engineering fallback. Do not search the workspace, create Pico SDK/CMake projects, write firmware source, search web pages, or scrape the Monitor UI for RP2350 Monitor hardware-control requests.",
|
|
1676
2186
|
raw_omitted: true
|
|
1677
2187
|
});
|
|
1678
2188
|
}
|
|
@@ -1698,7 +2208,53 @@ async function rp2350I2cTransfer(args) {
|
|
|
1698
2208
|
}
|
|
1699
2209
|
|
|
1700
2210
|
async function rp2350LogicCapture(args) {
|
|
1701
|
-
|
|
2211
|
+
const input = normalizeRp2350Input(args);
|
|
2212
|
+
const payload = compactObject({
|
|
2213
|
+
board_id: asString(args.board_id || args.board) || "pico2w-rp2350-monitor",
|
|
2214
|
+
local_device_id: asString(input.local_device_id || input.device_id || args.localDeviceId || args.deviceId || args.device),
|
|
2215
|
+
host: asString(input.host),
|
|
2216
|
+
port: input.port,
|
|
2217
|
+
monitor_bridge_url: asString(input.monitor_bridge_url || args.monitorBridgeUrl),
|
|
2218
|
+
serial_path: asString(input.serial_path || args.serialPath),
|
|
2219
|
+
pin_base: input.pin_base,
|
|
2220
|
+
pin_count: input.pin_count,
|
|
2221
|
+
sample_rate: input.sample_rate,
|
|
2222
|
+
samples: input.samples,
|
|
2223
|
+
output_path: asString(input.output_path || args.outputPath),
|
|
2224
|
+
pull: asString(input.pull),
|
|
2225
|
+
wait_timeout_ms: input.wait_timeout_ms,
|
|
2226
|
+
read_timeout_ms: input.read_timeout_ms,
|
|
2227
|
+
pre_samples: input.pre_samples,
|
|
2228
|
+
post_samples: input.post_samples,
|
|
2229
|
+
search_samples: input.search_samples,
|
|
2230
|
+
trigger_pin: input.trigger_pin,
|
|
2231
|
+
trigger_mode: asString(input.trigger_mode || args.triggerMode),
|
|
2232
|
+
trigger_level: input.trigger_level,
|
|
2233
|
+
trigger_mask: input.trigger_mask,
|
|
2234
|
+
trigger_value: input.trigger_value,
|
|
2235
|
+
channel_names: input.channel_names,
|
|
2236
|
+
protocol: asString(input.protocol),
|
|
2237
|
+
decoder: asString(input.decoder),
|
|
2238
|
+
stimulus: input.stimulus,
|
|
2239
|
+
approve: boolValue(args.approve) || boolValue(args.approved) || undefined
|
|
2240
|
+
});
|
|
2241
|
+
const routed = await agentActionRun({
|
|
2242
|
+
action_id: "capture_signal",
|
|
2243
|
+
arguments_json: JSON.stringify(payload),
|
|
2244
|
+
include_raw: args.include_raw,
|
|
2245
|
+
timeout_ms: args.timeout_ms
|
|
2246
|
+
});
|
|
2247
|
+
return {
|
|
2248
|
+
ok: routed.ok,
|
|
2249
|
+
compatibility_adapter: "dbt_rp2350_logic_capture_to_agent_capture_signal",
|
|
2250
|
+
agent_action: "capture_signal",
|
|
2251
|
+
execution: routed.execution,
|
|
2252
|
+
capture: routed.execution?.tool_results?.[0],
|
|
2253
|
+
decode: routed.execution?.tool_results?.[1],
|
|
2254
|
+
summary_for_user: routed.execution?.summary ?? routed.answer,
|
|
2255
|
+
response: boolValue(args.include_raw) ? routed.response : undefined,
|
|
2256
|
+
guidance: "This compatibility tool now routes through the Native Agent capture_signal action. Provide explicit analyzer pins, sample rate, sample count, and approved=true before arming local hardware."
|
|
2257
|
+
};
|
|
1702
2258
|
}
|
|
1703
2259
|
|
|
1704
2260
|
async function rp2350LogicDecode(args) {
|
|
@@ -1736,16 +2292,17 @@ async function recordMcpToolEvent(toolName, details) {
|
|
|
1736
2292
|
const command = [
|
|
1737
2293
|
"mcp", "log",
|
|
1738
2294
|
"--tool", toolName,
|
|
1739
|
-
"--client", details.client ||
|
|
1740
|
-
"--mode", details.mode || "
|
|
1741
|
-
"--server-model-used", details.server_model_used ? "true" : "false",
|
|
2295
|
+
"--client", details.client || mcpClientName(),
|
|
2296
|
+
"--mode", details.mode || "ide_plugin",
|
|
1742
2297
|
"--success", details.success ? "true" : "false",
|
|
1743
2298
|
"--request-id", requestId
|
|
1744
2299
|
];
|
|
1745
2300
|
if (Number.isFinite(details.duration_ms)) command.push("--duration-ms", String(Math.max(0, Math.round(details.duration_ms))));
|
|
1746
2301
|
if (asString(details.local_device_id)) command.push("--local-device-id", asString(details.local_device_id));
|
|
1747
|
-
|
|
1748
|
-
|
|
2302
|
+
const inputSummary = summarizePublicText(details.input_summary);
|
|
2303
|
+
const outputSummary = summarizePublicText(details.output_summary);
|
|
2304
|
+
if (inputSummary) command.push("--input-summary", inputSummary);
|
|
2305
|
+
if (outputSummary) command.push("--output-summary", outputSummary);
|
|
1749
2306
|
const response = await runEmbed(command);
|
|
1750
2307
|
return { recorded: response?.response?.ok === true, event_id: response?.response?.data?.event_id };
|
|
1751
2308
|
} catch (error) {
|
|
@@ -1753,6 +2310,11 @@ async function recordMcpToolEvent(toolName, details) {
|
|
|
1753
2310
|
}
|
|
1754
2311
|
}
|
|
1755
2312
|
|
|
2313
|
+
function mcpClientName() {
|
|
2314
|
+
const value = String(process.env.EMBED_MCP_CLIENT || "").trim().toLowerCase();
|
|
2315
|
+
return value || "codex";
|
|
2316
|
+
}
|
|
2317
|
+
|
|
1756
2318
|
function compactMcpLogError(error) {
|
|
1757
2319
|
const message = error instanceof Error ? error.message : String(error);
|
|
1758
2320
|
try {
|
|
@@ -1936,21 +2498,6 @@ function filteredRp2350Locators(locators) {
|
|
|
1936
2498
|
return filtered.length > 0 ? filtered : undefined;
|
|
1937
2499
|
}
|
|
1938
2500
|
|
|
1939
|
-
async function updateLogo(args) {
|
|
1940
|
-
const logoPath = asString(args.logo_path || args.logo);
|
|
1941
|
-
if (!logoPath) throw new Error("logo_path is required");
|
|
1942
|
-
const command = ["build", "image", "boot-logo", "--logo", logoPath];
|
|
1943
|
-
pushOptional(command, "account", args.account_id || args.account);
|
|
1944
|
-
pushOptional(command, "project", args.project_id || args.project);
|
|
1945
|
-
pushOptional(command, "board", args.board_id || args.board || "taishanpi");
|
|
1946
|
-
pushOptional(command, "variant", args.variant_id || args.variant);
|
|
1947
|
-
pushOptional(command, "kernel-logo", args.kernel_logo_path || args.kernel_logo);
|
|
1948
|
-
pushOptional(command, "output", args.output_path || args.output);
|
|
1949
|
-
pushOptional(command, "rotate", args.rotate);
|
|
1950
|
-
pushOptional(command, "scale", args.scale);
|
|
1951
|
-
return await runEmbed(command);
|
|
1952
|
-
}
|
|
1953
|
-
|
|
1954
2501
|
async function composeBootLogo(args) {
|
|
1955
2502
|
const packagePath = asString(args.package_path || args.package);
|
|
1956
2503
|
const baseImagePath = asString(args.base_image_path || args.base_image);
|
|
@@ -2416,75 +2963,15 @@ async function localBootLogoCompose(args, sdk, logoPath, outputDir, outputImageP
|
|
|
2416
2963
|
}
|
|
2417
2964
|
|
|
2418
2965
|
async function bootLogoUpdate(args) {
|
|
2419
|
-
const
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
message: `Logo image does not exist: ${logoPath}`
|
|
2428
|
-
}
|
|
2429
|
-
};
|
|
2430
|
-
}
|
|
2431
|
-
|
|
2432
|
-
const boardId = asString(args.board_id || args.board) || "taishanpi-1m-rk3566";
|
|
2433
|
-
const outputDir = defaultActionDir("boot-logo", args.output_dir || args.outputDir);
|
|
2434
|
-
const outputImagePath = asString(args.output_image_path || args.outputImage || args.output)
|
|
2435
|
-
|| path.join(outputDir, "boot-logo-boot.img");
|
|
2436
|
-
const manifestPath = asString(args.manifest_path || args.manifest) || path.join(outputDir, "boot-logo-compose-manifest.json");
|
|
2437
|
-
const sdk = await ensureLocalToolchain(args, boardId, "images");
|
|
2438
|
-
if (!sdk.releaseRoot) {
|
|
2439
|
-
return {
|
|
2440
|
-
ok: false,
|
|
2441
|
-
action: "boot_logo_update",
|
|
2442
|
-
error: {
|
|
2443
|
-
code: "local_sdk_missing",
|
|
2444
|
-
message: "本机没有可用的 TaishanPi images SDK,无法本地生成启动 Logo 镜像。"
|
|
2445
|
-
},
|
|
2446
|
-
sdk: compactSdkState(sdk.sdkCheck, sdk.sdkInstall)
|
|
2447
|
-
};
|
|
2448
|
-
}
|
|
2449
|
-
const composeSummary = await localBootLogoCompose(args, sdk, logoPath, outputDir, outputImagePath, manifestPath);
|
|
2450
|
-
if (!composeSummary.ok) {
|
|
2451
|
-
return {
|
|
2452
|
-
ok: false,
|
|
2453
|
-
action: "boot_logo_update",
|
|
2454
|
-
error: composeSummary.error,
|
|
2455
|
-
sdk: compactSdkState(sdk.sdkCheck, sdk.sdkInstall),
|
|
2456
|
-
output_dir: outputDir
|
|
2457
|
-
};
|
|
2458
|
-
}
|
|
2459
|
-
const approved = boolValue(args.approved) || boolValue(args.approve);
|
|
2460
|
-
const readyForFlash = composeSummary.ready_for_flash === true;
|
|
2461
|
-
const result = {
|
|
2462
|
-
ok: readyForFlash,
|
|
2463
|
-
action: "boot_logo_update",
|
|
2464
|
-
board: "TaishanPi 1M RK3566",
|
|
2465
|
-
status: readyForFlash ? (approved ? "composed_ready_for_flash" : "ready_for_approval") : "composed_not_flashable",
|
|
2466
|
-
approved,
|
|
2467
|
-
summary_for_user: readyForFlash
|
|
2468
|
-
? "启动 Logo 已在本机完成格式转换、resource.img 重打包和 boot.img 镜像生成。需要真实写入开发板时,应继续走受控的本地刷写入口。"
|
|
2469
|
-
: "启动 Logo 本地生成未完成,不能直接作为刷写产物。",
|
|
2470
|
-
sdk: compactSdkState(sdk.sdkCheck, sdk.sdkInstall),
|
|
2471
|
-
artifacts: compactObject({
|
|
2472
|
-
logo_path: logoPath,
|
|
2473
|
-
base_image_path: composeSummary.base_image_path,
|
|
2474
|
-
output_image_path: outputImagePath,
|
|
2475
|
-
output_resource_path: composeSummary.output_resource_path,
|
|
2476
|
-
manifest_path: manifestPath
|
|
2477
|
-
}),
|
|
2478
|
-
compose: composeSummary,
|
|
2479
|
-
next_step: readyForFlash
|
|
2480
|
-
? "如果用户明确批准写入开发板,后续应由同一高层工具调用 Local Bridge 的受控 boot 分区刷写能力,不能让模型手动拼命令。"
|
|
2481
|
-
: "需要修复本地 SDK 图像转换或 resource 重打包工具后再刷写。",
|
|
2482
|
-
raw_omitted: boolValue(args.include_raw) ? undefined : true
|
|
2966
|
+
const routed = await flashImageCompatibilityAction(args, {
|
|
2967
|
+
artifactKind: "boot_logo",
|
|
2968
|
+
defaultBoardId: "taishanpi-1m-rk3566",
|
|
2969
|
+
compatibilityAdapter: "dbt_boot_logo_update_to_agent_flash_image"
|
|
2970
|
+
});
|
|
2971
|
+
return {
|
|
2972
|
+
...routed,
|
|
2973
|
+
guidance: "This compatibility tool now routes through the Native Agent flash_image action with artifact_kind=boot_logo. The Agent owns local logo conversion, resource.img repack, boot.img patching, and approved Local Bridge flashing."
|
|
2483
2974
|
};
|
|
2484
|
-
if (boolValue(args.include_raw)) {
|
|
2485
|
-
result.raw = compactObject({ compose_result: composeSummary });
|
|
2486
|
-
}
|
|
2487
|
-
return result;
|
|
2488
2975
|
}
|
|
2489
2976
|
|
|
2490
2977
|
function shellQuote(value) {
|
|
@@ -2524,6 +3011,42 @@ function qtRuntimeCommand(remotePath) {
|
|
|
2524
3011
|
].join(" && ");
|
|
2525
3012
|
}
|
|
2526
3013
|
|
|
3014
|
+
function remotePathBase(remotePath) {
|
|
3015
|
+
const value = asString(remotePath).replace(/\/+$/, "");
|
|
3016
|
+
return value ? path.posix.basename(value) : "";
|
|
3017
|
+
}
|
|
3018
|
+
|
|
3019
|
+
function resolveTaishanQtDeployPaths(args, artifactPath) {
|
|
3020
|
+
const artifactName = path.basename(artifactPath);
|
|
3021
|
+
const remoteFile = asString(args.remote_file || args.remoteFile);
|
|
3022
|
+
if (remoteFile) {
|
|
3023
|
+
return { remoteFile, expectedRemotePath: remoteFile };
|
|
3024
|
+
}
|
|
3025
|
+
const remoteDir = asString(args.remote_dir || args.remoteDir);
|
|
3026
|
+
if (remoteDir) {
|
|
3027
|
+
return {
|
|
3028
|
+
remoteDir,
|
|
3029
|
+
expectedRemotePath: `${remoteDir.replace(/\/+$/, "")}/${artifactName}`
|
|
3030
|
+
};
|
|
3031
|
+
}
|
|
3032
|
+
const remotePath = asString(args.remote_path || args.remotePath);
|
|
3033
|
+
if (!remotePath) {
|
|
3034
|
+
const defaultRemoteDir = "/userdata/embed-labs/apps";
|
|
3035
|
+
return {
|
|
3036
|
+
remoteDir: defaultRemoteDir,
|
|
3037
|
+
expectedRemotePath: `${defaultRemoteDir}/${artifactName}`
|
|
3038
|
+
};
|
|
3039
|
+
}
|
|
3040
|
+
if (remotePath.endsWith("/") || remotePathBase(remotePath) !== artifactName) {
|
|
3041
|
+
const normalizedDir = remotePath.replace(/\/+$/, "");
|
|
3042
|
+
return {
|
|
3043
|
+
remoteDir: normalizedDir,
|
|
3044
|
+
expectedRemotePath: `${normalizedDir}/${artifactName}`
|
|
3045
|
+
};
|
|
3046
|
+
}
|
|
3047
|
+
return { remoteFile: remotePath, expectedRemotePath: remotePath };
|
|
3048
|
+
}
|
|
3049
|
+
|
|
2527
3050
|
function artifactPathFromBuild(buildResult) {
|
|
2528
3051
|
const data = responseData(buildResult) || {};
|
|
2529
3052
|
return asString(data.artifact_path || data.artifactPath || data.output_path || data.executable_path);
|
|
@@ -2541,109 +3064,40 @@ function taishanpiHostFromDeviceList(scanResult) {
|
|
|
2541
3064
|
}
|
|
2542
3065
|
|
|
2543
3066
|
async function taishanpiQtAppWorkflow(args) {
|
|
2544
|
-
const
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
}
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
let deployResult;
|
|
2578
|
-
const approved = boolValue(args.approved) || boolValue(args.approve);
|
|
2579
|
-
const shouldDeploy = boolValue(args.deploy) || boolValue(args.run);
|
|
2580
|
-
if (shouldDeploy && approved) {
|
|
2581
|
-
const scan = await runEmbed(["device", "list"]);
|
|
2582
|
-
const host = asString(args.host) || taishanpiHostFromDeviceList(scan);
|
|
2583
|
-
if (!host) {
|
|
2584
|
-
deployResult = {
|
|
2585
|
-
ok: false,
|
|
2586
|
-
error: {
|
|
2587
|
-
code: "runtime_host_not_found",
|
|
2588
|
-
message: "Qt 程序已完成交叉编译,但当前未检测到可通过 SSH 部署的泰山派运行态主机。",
|
|
2589
|
-
remediation: "确认泰山派已启动初始化系统并连接到当前电脑/网络后,再调用同一工具部署。"
|
|
2590
|
-
}
|
|
2591
|
-
};
|
|
2592
|
-
} else if (!artifactPath) {
|
|
2593
|
-
deployResult = {
|
|
2594
|
-
ok: false,
|
|
2595
|
-
error: {
|
|
2596
|
-
code: "artifact_not_found",
|
|
2597
|
-
message: "Qt 构建没有返回可部署的可执行文件路径。"
|
|
2598
|
-
}
|
|
2599
|
-
};
|
|
2600
|
-
} else {
|
|
2601
|
-
const remotePath = asString(args.remote_path || args.remotePath)
|
|
2602
|
-
|| `/userdata/embed-labs/apps/${path.basename(artifactPath)}`;
|
|
2603
|
-
const command = ["deploy", "taishanpi", "--host", host, "--artifact", artifactPath, "--remote-path", remotePath, "--approve"];
|
|
2604
|
-
if (boolValue(args.run)) {
|
|
2605
|
-
command.push("--run", "--run-command", qtRuntimeCommand(remotePath));
|
|
2606
|
-
}
|
|
2607
|
-
deployResult = await runEmbed(command);
|
|
2608
|
-
}
|
|
2609
|
-
}
|
|
2610
|
-
|
|
2611
|
-
const buildData = responseData(buildResult) || {};
|
|
2612
|
-
const deployData = responseData(deployResult) || {};
|
|
2613
|
-
const result = {
|
|
2614
|
-
ok: buildResult?.response?.ok === true && (!deployResult || deployResult?.response?.ok === true || deployResult?.ok === false),
|
|
2615
|
-
action: "taishanpi_qt_app_workflow",
|
|
2616
|
-
board: "TaishanPi 1M RK3566",
|
|
2617
|
-
status: deployResult ? (deployResult?.response?.ok === true ? "built_and_deployed" : "built_deploy_pending") : "built",
|
|
2618
|
-
template: selected.template,
|
|
2619
|
-
request: request || undefined,
|
|
2620
|
-
sdk: compactSdkState(sdk.sdkCheck, sdk.sdkInstall),
|
|
2621
|
-
source_path: sourcePath || "built-in Qt smoke source",
|
|
2622
|
-
build: compactObject({
|
|
2623
|
-
build_dir: buildDir,
|
|
2624
|
-
target_name: targetName,
|
|
2625
|
-
artifact_path: artifactPath,
|
|
2626
|
-
artifact_size_bytes: buildData.artifact_size_bytes,
|
|
2627
|
-
artifact_sha256: buildData.artifact_sha256,
|
|
2628
|
-
summary_for_user: buildData.summary_for_user
|
|
2629
|
-
}),
|
|
2630
|
-
deploy: deployResult ? compactObject({
|
|
2631
|
-
ok: deployResult?.response?.ok ?? deployResult?.ok,
|
|
2632
|
-
host: deployData.host,
|
|
2633
|
-
remote_path: deployData.remote_path,
|
|
2634
|
-
ran: deployData.ran,
|
|
2635
|
-
error: deployResult?.error || deployResult?.response?.error
|
|
2636
|
-
}) : undefined,
|
|
2637
|
-
summary_for_user: deployResult?.response?.ok === true
|
|
2638
|
-
? "Qt 程序已按泰山派 Qt 运行库路径完成构建、部署并启动。"
|
|
2639
|
-
: "Qt 程序已通过本机 LLVM/Qt 工具链完成交叉编译。",
|
|
2640
|
-
next_step: deployResult ? "查看部署/运行结果;如需协议采集,再调用 RP2350 Monitor 的对应高层工具。" : "如需上板运行,用户批准后用同一工具传入 deploy=true、run=true、approved=true。",
|
|
2641
|
-
raw_omitted: boolValue(args.include_raw) ? undefined : true
|
|
3067
|
+
const payload = compactObject({
|
|
3068
|
+
board_id: asString(args.board_id || args.board) || "taishanpi-1m-rk3566",
|
|
3069
|
+
local_device_id: asString(args.local_device_id || args.localDeviceId || args.device_id || args.deviceId || args.device),
|
|
3070
|
+
host: asString(args.host),
|
|
3071
|
+
user: asString(args.user),
|
|
3072
|
+
artifact_path: asString(args.artifact_path || args.artifact || args.output_path || args.output),
|
|
3073
|
+
remote_path: asString(args.remote_path || args.remotePath),
|
|
3074
|
+
remote_dir: asString(args.remote_dir || args.remoteDir),
|
|
3075
|
+
remote_file: asString(args.remote_file || args.remoteFile),
|
|
3076
|
+
wrapper_script: asString(args.wrapper_script || args.run_script || args.wrapperScript || args.runScript),
|
|
3077
|
+
run_script: asString(args.run_script || args.runScript),
|
|
3078
|
+
run_command: asString(args.run_command || args.runCommand),
|
|
3079
|
+
run: boolValue(args.run) || undefined,
|
|
3080
|
+
approve: boolValue(args.approve) || boolValue(args.approved) || undefined,
|
|
3081
|
+
release_root: asString(args.release_root || args.releaseRoot),
|
|
3082
|
+
profile: asString(args.profile || args.build_profile || args.buildProfile),
|
|
3083
|
+
query: asString(args.request || args.prompt || args.description)
|
|
3084
|
+
});
|
|
3085
|
+
const routed = await agentActionRun({
|
|
3086
|
+
action_id: "deploy_app",
|
|
3087
|
+
arguments_json: JSON.stringify(payload),
|
|
3088
|
+
include_raw: args.include_raw,
|
|
3089
|
+
timeout_ms: args.timeout_ms
|
|
3090
|
+
});
|
|
3091
|
+
return {
|
|
3092
|
+
ok: routed.ok,
|
|
3093
|
+
compatibility_adapter: "dbt_taishanpi_qt_app_workflow_to_agent_deploy_app",
|
|
3094
|
+
agent_action: "deploy_app",
|
|
3095
|
+
execution: routed.execution,
|
|
3096
|
+
deploy: routed.execution?.deploy,
|
|
3097
|
+
summary_for_user: routed.execution?.summary ?? routed.answer,
|
|
3098
|
+
response: boolValue(args.include_raw) ? routed.response : undefined,
|
|
3099
|
+
guidance: "This compatibility tool now routes through the Native Agent deploy_app action. Use build_project first when a local artifact has not been built yet; deploy_app owns target resolution, remote path semantics, wrapper execution, and approved Local Bridge deployment."
|
|
2642
3100
|
};
|
|
2643
|
-
if (boolValue(args.include_raw)) {
|
|
2644
|
-
result.raw = compactObject({ build_result: buildResult, deploy_result: deployResult });
|
|
2645
|
-
}
|
|
2646
|
-
return result;
|
|
2647
3101
|
}
|
|
2648
3102
|
|
|
2649
3103
|
async function localToolchainLatest(args) {
|
|
@@ -2678,25 +3132,98 @@ async function localToolchainCurrent(args) {
|
|
|
2678
3132
|
return await runEmbed(command);
|
|
2679
3133
|
}
|
|
2680
3134
|
|
|
3135
|
+
function compactLocalToolchainInstallResult(result) {
|
|
3136
|
+
const data = responseData(result) || {};
|
|
3137
|
+
const source = data.source && typeof data.source === "object" ? data.source : {};
|
|
3138
|
+
const validation = data.validation && typeof data.validation === "object" ? data.validation : {};
|
|
3139
|
+
const packages = Array.isArray(data.packages) ? data.packages : [];
|
|
3140
|
+
const sourceComponents = Array.isArray(source.components) ? source.components : [];
|
|
3141
|
+
return {
|
|
3142
|
+
ok: result?.response?.ok ?? result?.ok ?? true,
|
|
3143
|
+
board_id: data.board_id,
|
|
3144
|
+
version: data.version,
|
|
3145
|
+
channel: data.channel,
|
|
3146
|
+
mode: data.mode,
|
|
3147
|
+
install_root: data.install_root,
|
|
3148
|
+
release_root: data.release_root,
|
|
3149
|
+
source: compactObject({
|
|
3150
|
+
kind: source.kind,
|
|
3151
|
+
mirror_kind: source.mirror_kind,
|
|
3152
|
+
component_count: sourceComponents.length || undefined
|
|
3153
|
+
}),
|
|
3154
|
+
packages: packages.map((item) => compactObject({
|
|
3155
|
+
package_id: item.package_id || item.id,
|
|
3156
|
+
version: item.version,
|
|
3157
|
+
kind: item.kind
|
|
3158
|
+
})).slice(0, 8),
|
|
3159
|
+
installed_paths: Array.isArray(data.installed_paths) ? data.installed_paths.slice(0, 12) : undefined,
|
|
3160
|
+
removed_old_versions: data.removed_old_versions,
|
|
3161
|
+
validation: compactObject({
|
|
3162
|
+
ok: validation.ok,
|
|
3163
|
+
mode: validation.mode,
|
|
3164
|
+
missing_groups: validation.missing_groups,
|
|
3165
|
+
path_leaks: validation.path_leaks?.length ? validation.path_leaks.length : undefined,
|
|
3166
|
+
summary_for_user: validation.summary_for_user
|
|
3167
|
+
}),
|
|
3168
|
+
summary_for_user: validation.summary_for_user || `Local toolchain ${data.board_id || ""} ${data.version || ""} installed.`,
|
|
3169
|
+
raw_omitted: true
|
|
3170
|
+
};
|
|
3171
|
+
}
|
|
3172
|
+
|
|
2681
3173
|
async function localToolchainInstall(args) {
|
|
2682
|
-
const
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
3174
|
+
const payload = compactObject({
|
|
3175
|
+
operation: boolValue(args.update) ? "update" : "install",
|
|
3176
|
+
board_id: asString(args.board_id || args.board),
|
|
3177
|
+
channel: asString(args.channel),
|
|
3178
|
+
metadata_root: asString(args.metadata_root || args.metadataRoot),
|
|
3179
|
+
source_url: asString(args.source_url || args.sourceUrl),
|
|
3180
|
+
source_release_root: asString(args.source_release_root || args.sourceReleaseRoot),
|
|
3181
|
+
install_root: asString(args.install_root || args.installRoot),
|
|
3182
|
+
mode: asString(args.mode),
|
|
3183
|
+
force: boolValue(args.force) || undefined
|
|
3184
|
+
});
|
|
3185
|
+
const routed = await agentActionRun({
|
|
3186
|
+
action_id: "install_toolchain",
|
|
3187
|
+
arguments_json: JSON.stringify(payload),
|
|
3188
|
+
include_raw: args.include_raw
|
|
3189
|
+
});
|
|
3190
|
+
return {
|
|
3191
|
+
ok: routed.ok,
|
|
3192
|
+
compatibility_adapter: "dbt_local_toolchain_install_to_agent_install_toolchain",
|
|
3193
|
+
agent_action: "install_toolchain",
|
|
3194
|
+
operation: payload.operation,
|
|
3195
|
+
execution: routed.execution,
|
|
3196
|
+
toolchain: routed.execution?.toolchain,
|
|
3197
|
+
toolchain_operation: routed.execution?.toolchain_operation,
|
|
3198
|
+
summary_for_user: routed.execution?.summary ?? routed.answer,
|
|
3199
|
+
response: boolValue(args.include_raw) ? routed.response : undefined,
|
|
3200
|
+
guidance: "This compatibility tool now routes through the local EmbedLabs Agent install_toolchain action. Prefer embedlabs_chat for natural-language SDK requests or embedlabs_action_run for explicit action calls."
|
|
3201
|
+
};
|
|
2692
3202
|
}
|
|
2693
3203
|
|
|
2694
3204
|
async function localToolchainUninstall(args) {
|
|
2695
3205
|
const boardId = asString(args.board_id || args.board);
|
|
2696
3206
|
if (!boardId) throw new Error("board_id is required");
|
|
2697
|
-
const
|
|
2698
|
-
|
|
2699
|
-
|
|
3207
|
+
const routed = await agentActionRun({
|
|
3208
|
+
action_id: "install_toolchain",
|
|
3209
|
+
arguments_json: JSON.stringify(compactObject({
|
|
3210
|
+
operation: "uninstall",
|
|
3211
|
+
board_id: boardId,
|
|
3212
|
+
install_root: asString(args.install_root || args.installRoot)
|
|
3213
|
+
})),
|
|
3214
|
+
include_raw: args.include_raw
|
|
3215
|
+
});
|
|
3216
|
+
return {
|
|
3217
|
+
ok: routed.ok,
|
|
3218
|
+
compatibility_adapter: "dbt_local_toolchain_uninstall_to_agent_install_toolchain",
|
|
3219
|
+
agent_action: "install_toolchain",
|
|
3220
|
+
operation: "uninstall",
|
|
3221
|
+
execution: routed.execution,
|
|
3222
|
+
toolchain_operation: routed.execution?.toolchain_operation,
|
|
3223
|
+
summary_for_user: routed.execution?.summary ?? routed.answer,
|
|
3224
|
+
response: boolValue(args.include_raw) ? routed.response : undefined,
|
|
3225
|
+
guidance: "This compatibility tool now routes through the local EmbedLabs Agent install_toolchain action with operation=uninstall."
|
|
3226
|
+
};
|
|
2700
3227
|
}
|
|
2701
3228
|
|
|
2702
3229
|
async function localToolchainValidate(args) {
|
|
@@ -2715,21 +3242,33 @@ function compactLocalToolchainValidationResult(result) {
|
|
|
2715
3242
|
const missing = Array.isArray(data.missing_paths) ? data.missing_paths : [];
|
|
2716
3243
|
const pathLeaks = Array.isArray(data.path_leaks) ? data.path_leaks : [];
|
|
2717
3244
|
const notes = Array.isArray(data.notes) ? data.notes.map(summarizePublicText).filter(Boolean).slice(0, 10) : [];
|
|
3245
|
+
const repairCommand = data.ok === true
|
|
3246
|
+
? undefined
|
|
3247
|
+
: compactObject({
|
|
3248
|
+
action_id: "install_toolchain",
|
|
3249
|
+
operation: "install",
|
|
3250
|
+
board_id: data.board_id,
|
|
3251
|
+
mode: data.mode,
|
|
3252
|
+
command: `embedlabs agent action run install_toolchain --operation install${data.board_id ? ` --board-id ${data.board_id}` : ""}${data.mode ? ` --mode ${data.mode}` : ""} --json`
|
|
3253
|
+
});
|
|
2718
3254
|
return compactObject({
|
|
2719
3255
|
ok: data.ok === true,
|
|
2720
3256
|
board_id: data.board_id,
|
|
2721
3257
|
mode: data.mode,
|
|
3258
|
+
release_root: data.release_root,
|
|
2722
3259
|
host: data.host,
|
|
2723
3260
|
summary_for_user: data.summary_for_user,
|
|
2724
3261
|
missing_groups: Array.isArray(data.missing_groups) ? data.missing_groups : undefined,
|
|
2725
|
-
repair_command:
|
|
3262
|
+
repair_command: repairCommand?.command,
|
|
3263
|
+
repair_agent_action: repairCommand,
|
|
3264
|
+
original_repair_command_omitted: Boolean(data.repair_command),
|
|
2726
3265
|
checked_count: checked.length,
|
|
2727
3266
|
missing_count: missing.length,
|
|
2728
3267
|
path_leaks_count: pathLeaks.length,
|
|
2729
3268
|
notes,
|
|
2730
3269
|
guidance: data.ok === true
|
|
2731
3270
|
? "Summarize this as ready. Do not print checked_paths or internal registry/source paths unless the user asks for raw diagnostics."
|
|
2732
|
-
: "Read summary_for_user
|
|
3271
|
+
: "Read summary_for_user and missing_groups, then repair through embedlabs_action_run with action_id=install_toolchain and operation=install/update. dbt_local_toolchain_install is retained only for compact/full engineering validation.",
|
|
2733
3272
|
raw_omitted: true
|
|
2734
3273
|
});
|
|
2735
3274
|
}
|
|
@@ -2745,46 +3284,58 @@ function isRp2350InitialTarget(value) {
|
|
|
2745
3284
|
}
|
|
2746
3285
|
|
|
2747
3286
|
async function initialImageFlash(args) {
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
}
|
|
2752
|
-
|
|
2753
|
-
return await taishanpiInitialImageFlash(args);
|
|
2754
|
-
}
|
|
2755
|
-
|
|
2756
|
-
const scan = await runEmbed(["device", "list"]);
|
|
2757
|
-
const scanData = responseData(scan) || {};
|
|
2758
|
-
const devices = Array.isArray(scanData.development_boards) ? scanData.development_boards : [];
|
|
2759
|
-
const candidates = devices
|
|
2760
|
-
.filter((item) => item?.board_id === "taishanpi" || isRp2350Device(item))
|
|
2761
|
-
.map((item) => compactObject({
|
|
2762
|
-
local_device_id: item.local_device_id || item.device_id,
|
|
2763
|
-
board_id: item.board_id,
|
|
2764
|
-
variant_id: item.variant_id,
|
|
2765
|
-
display_name: item.display_name || item.name,
|
|
2766
|
-
status: item.status
|
|
2767
|
-
}));
|
|
2768
|
-
|
|
2769
|
-
if (candidates.length === 1) {
|
|
2770
|
-
const candidate = candidates[0];
|
|
2771
|
-
const nextArgs = { ...args, local_device_id: args.local_device_id || candidate.local_device_id };
|
|
2772
|
-
if (candidate.board_id === "taishanpi") return await taishanpiInitialImageFlash(nextArgs);
|
|
2773
|
-
return await rp2350InitialFirmwareFlash({ ...nextArgs, board_id: candidate.variant_id || candidate.board_id });
|
|
2774
|
-
}
|
|
3287
|
+
return await flashImageCompatibilityAction(args, {
|
|
3288
|
+
artifactKind: "initial",
|
|
3289
|
+
compatibilityAdapter: "dbt_initial_image_flash_to_agent_flash_image"
|
|
3290
|
+
});
|
|
3291
|
+
}
|
|
2775
3292
|
|
|
3293
|
+
function flashImageCompatibilityPayload(args, artifactKind, defaultBoardId) {
|
|
3294
|
+
return compactObject({
|
|
3295
|
+
board_id: asString(args.board_id || args.board) || defaultBoardId,
|
|
3296
|
+
local_device_id: asString(args.local_device_id || args.localDeviceId || args.device_id || args.deviceId || args.device),
|
|
3297
|
+
host: asString(args.host),
|
|
3298
|
+
user: asString(args.user),
|
|
3299
|
+
artifact_kind: artifactKind,
|
|
3300
|
+
artifact_path: asString(args.artifact_path || args.artifact),
|
|
3301
|
+
image_dir: asString(args.image_dir || args.imageDir),
|
|
3302
|
+
channel: asString(args.channel),
|
|
3303
|
+
metadata_root: asString(args.metadata_root || args.metadataRoot),
|
|
3304
|
+
source_url: asString(args.source_url || args.sourceUrl),
|
|
3305
|
+
source_release_root: asString(args.source_release_root || args.sourceReleaseRoot),
|
|
3306
|
+
install_root: asString(args.install_root || args.installRoot),
|
|
3307
|
+
logo_path: asString(args.logo_path || args.logo),
|
|
3308
|
+
kernel_logo_path: asString(args.kernel_logo_path || args.kernelLogo),
|
|
3309
|
+
base_image_path: asString(args.base_image_path || args.baseImage),
|
|
3310
|
+
output_image_path: asString(args.output_image_path || args.outputImage || args.output),
|
|
3311
|
+
output_resource_path: asString(args.output_resource_path || args.outputResource),
|
|
3312
|
+
target_volume: asString(args.target_volume || args.targetVolume || args.target_volume_path || args.targetVolumePath),
|
|
3313
|
+
partition: asString(args.partition),
|
|
3314
|
+
partitions: asString(args.partitions),
|
|
3315
|
+
profile: asString(args.profile),
|
|
3316
|
+
force: boolValue(args.force) || undefined,
|
|
3317
|
+
approve: boolValue(args.approve) || boolValue(args.approved) || undefined
|
|
3318
|
+
});
|
|
3319
|
+
}
|
|
3320
|
+
|
|
3321
|
+
async function flashImageCompatibilityAction(args, options) {
|
|
3322
|
+
const payload = flashImageCompatibilityPayload(args, options.artifactKind, options.defaultBoardId);
|
|
3323
|
+
const routed = await agentActionRun({
|
|
3324
|
+
action_id: "flash_image",
|
|
3325
|
+
arguments_json: JSON.stringify(payload),
|
|
3326
|
+
include_raw: args.include_raw,
|
|
3327
|
+
timeout_ms: args.timeout_ms
|
|
3328
|
+
});
|
|
2776
3329
|
return {
|
|
2777
|
-
ok:
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
: "请连接开发板,或让开发板进入 Loader/Maskrom/BOOTSEL 后重新查询开发板状态。"
|
|
2787
|
-
}
|
|
3330
|
+
ok: routed.ok,
|
|
3331
|
+
compatibility_adapter: options.compatibilityAdapter,
|
|
3332
|
+
agent_action: "flash_image",
|
|
3333
|
+
artifact_kind: payload.artifact_kind,
|
|
3334
|
+
execution: routed.execution,
|
|
3335
|
+
flash: routed.execution?.flash,
|
|
3336
|
+
summary_for_user: routed.execution?.summary ?? routed.answer,
|
|
3337
|
+
response: boolValue(args.include_raw) ? routed.response : undefined,
|
|
3338
|
+
guidance: "This compatibility tool now routes through the Native Agent flash_image action. Review the plan and re-run with approved=true only after explicit user approval."
|
|
2788
3339
|
};
|
|
2789
3340
|
}
|
|
2790
3341
|
|
|
@@ -2906,110 +3457,19 @@ function compactInitialFlashResult({ boardLabel, sdkCheck, sdkInstall, imageDir,
|
|
|
2906
3457
|
}
|
|
2907
3458
|
|
|
2908
3459
|
async function taishanpiInitialImageFlash(args) {
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
let releaseRoot = "";
|
|
2914
|
-
if (!explicitImageDir) {
|
|
2915
|
-
sdkCheck = await localToolchainValidate({ board_id: sdkBoardId, mode: "images" });
|
|
2916
|
-
const checkData = responseData(sdkCheck) || {};
|
|
2917
|
-
if (checkData.ok) {
|
|
2918
|
-
releaseRoot = asString(checkData.release_root);
|
|
2919
|
-
} else {
|
|
2920
|
-
const installArgs = {
|
|
2921
|
-
board_id: sdkBoardId,
|
|
2922
|
-
channel: args.channel,
|
|
2923
|
-
metadata_root: args.metadata_root || args.metadataRoot,
|
|
2924
|
-
install_root: args.install_root || args.installRoot,
|
|
2925
|
-
mode: "images",
|
|
2926
|
-
force: args.force
|
|
2927
|
-
};
|
|
2928
|
-
sdkInstall = await localToolchainInstall(installArgs);
|
|
2929
|
-
const installedData = responseData(sdkInstall) || {};
|
|
2930
|
-
releaseRoot = asString(installedData.release_root);
|
|
2931
|
-
}
|
|
2932
|
-
}
|
|
2933
|
-
const imageDir = explicitImageDir || (releaseRoot ? path.join(releaseRoot, "images", "current") : "");
|
|
2934
|
-
const localDeviceId = asString(args.local_device_id || args.localDeviceId || args.device_id || args.deviceId);
|
|
2935
|
-
const planCommand = ["flash", "plan", "--board", "taishanpi"];
|
|
2936
|
-
if (imageDir) pushOptional(planCommand, "image-dir", imageDir);
|
|
2937
|
-
if (localDeviceId) pushOptional(planCommand, "local-device-id", localDeviceId);
|
|
2938
|
-
const plan = await runEmbed(planCommand);
|
|
2939
|
-
const approved = boolValue(args.approved) || boolValue(args.approve);
|
|
2940
|
-
if (!approved) {
|
|
2941
|
-
return compactInitialFlashResult({
|
|
2942
|
-
boardLabel: "TaishanPi 1M RK3566",
|
|
2943
|
-
sdkCheck,
|
|
2944
|
-
sdkInstall,
|
|
2945
|
-
imageDir,
|
|
2946
|
-
plan,
|
|
2947
|
-
approved,
|
|
2948
|
-
includeRaw: boolValue(args.include_raw)
|
|
2949
|
-
});
|
|
2950
|
-
}
|
|
2951
|
-
const runCommand = ["flash", "run", "--board", "taishanpi", "--approve"];
|
|
2952
|
-
if (imageDir) pushOptional(runCommand, "image-dir", imageDir);
|
|
2953
|
-
if (localDeviceId) pushOptional(runCommand, "local-device-id", localDeviceId);
|
|
2954
|
-
const flashed = await runEmbed(runCommand);
|
|
2955
|
-
const wait = args.wait === undefined ? true : boolValue(args.wait);
|
|
2956
|
-
const jobId = localJobIdFromRun(flashed);
|
|
2957
|
-
const finalStatus = wait && jobId ? await waitForLocalJob(jobId, Number(args.timeout_ms) || 180000) : undefined;
|
|
2958
|
-
return compactInitialFlashResult({
|
|
2959
|
-
boardLabel: "TaishanPi 1M RK3566",
|
|
2960
|
-
sdkCheck,
|
|
2961
|
-
sdkInstall,
|
|
2962
|
-
imageDir,
|
|
2963
|
-
plan,
|
|
2964
|
-
runResult: flashed,
|
|
2965
|
-
finalStatus,
|
|
2966
|
-
approved,
|
|
2967
|
-
includeRaw: boolValue(args.include_raw)
|
|
3460
|
+
return await flashImageCompatibilityAction(args, {
|
|
3461
|
+
artifactKind: "initial_image",
|
|
3462
|
+
defaultBoardId: "taishanpi-1m-rk3566",
|
|
3463
|
+
compatibilityAdapter: "dbt_taishanpi_initial_image_flash_to_agent_flash_image"
|
|
2968
3464
|
});
|
|
2969
3465
|
}
|
|
2970
3466
|
|
|
2971
3467
|
async function rp2350MonitorFirmwareFlash(args) {
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
install_root: args.install_root || args.installRoot,
|
|
2978
|
-
mode: "firmware",
|
|
2979
|
-
force: args.force
|
|
2980
|
-
};
|
|
2981
|
-
const installed = await localToolchainInstall(installArgs);
|
|
2982
|
-
const installedData = responseData(installed) || {};
|
|
2983
|
-
const releaseRoot = asString(installedData.release_root);
|
|
2984
|
-
const artifactPath = asString(args.artifact_path || args.artifact)
|
|
2985
|
-
|| (releaseRoot ? path.join(releaseRoot, "toolkit-runtime", "rp2350-monitor", "firmware", "rp2350_monitor.uf2") : "");
|
|
2986
|
-
if (!artifactPath) {
|
|
2987
|
-
throw new Error("RP2350 Monitor firmware UF2 could not be resolved after local toolchain install.");
|
|
2988
|
-
}
|
|
2989
|
-
const plan = await runEmbed(["flash", "plan", "--board", "rp2350", "--artifact", artifactPath]);
|
|
2990
|
-
const approved = boolValue(args.approved) || boolValue(args.approve);
|
|
2991
|
-
if (!approved) {
|
|
2992
|
-
const planData = responseData(plan) || plan;
|
|
2993
|
-
const automaticBootsel = !!planData.automatic_bootsel
|
|
2994
|
-
|| (Array.isArray(planData.steps) && planData.steps.some((step) => step && step.kind === "picotool_load_force_usb"));
|
|
2995
|
-
return {
|
|
2996
|
-
ok: true,
|
|
2997
|
-
firmware_install: installed,
|
|
2998
|
-
artifact_path: artifactPath,
|
|
2999
|
-
flash_plan: plan,
|
|
3000
|
-
next_step: automaticBootsel
|
|
3001
|
-
? "Flash plan is ready. After user approval, call this tool again with approved=true."
|
|
3002
|
-
: "Flash plan needs a visible UF2 target volume before it can run."
|
|
3003
|
-
};
|
|
3004
|
-
}
|
|
3005
|
-
const flashed = await runEmbed(["flash", "run", "--board", "rp2350", "--artifact", artifactPath, "--approve"]);
|
|
3006
|
-
return {
|
|
3007
|
-
ok: true,
|
|
3008
|
-
firmware_install: installed,
|
|
3009
|
-
artifact_path: artifactPath,
|
|
3010
|
-
flash_plan: plan,
|
|
3011
|
-
flash_result: flashed
|
|
3012
|
-
};
|
|
3468
|
+
return await flashImageCompatibilityAction(args, {
|
|
3469
|
+
artifactKind: "monitor_firmware",
|
|
3470
|
+
defaultBoardId: "pico2w-rp2350-monitor",
|
|
3471
|
+
compatibilityAdapter: "dbt_rp2350_monitor_firmware_flash_to_agent_flash_image"
|
|
3472
|
+
});
|
|
3013
3473
|
}
|
|
3014
3474
|
|
|
3015
3475
|
function rp2350InitialFirmwarePath(boardId, releaseRoot) {
|
|
@@ -3019,46 +3479,11 @@ function rp2350InitialFirmwarePath(boardId, releaseRoot) {
|
|
|
3019
3479
|
}
|
|
3020
3480
|
|
|
3021
3481
|
async function rp2350InitialFirmwareFlash(args) {
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
install_root: args.install_root || args.installRoot,
|
|
3028
|
-
mode: "firmware",
|
|
3029
|
-
force: args.force
|
|
3030
|
-
};
|
|
3031
|
-
const installed = await localToolchainInstall(installArgs);
|
|
3032
|
-
const installedData = responseData(installed) || {};
|
|
3033
|
-
const releaseRoot = asString(installedData.release_root);
|
|
3034
|
-
const artifactPath = asString(args.artifact_path || args.artifact) || rp2350InitialFirmwarePath(boardId, releaseRoot);
|
|
3035
|
-
if (!artifactPath) {
|
|
3036
|
-
throw new Error("RP2350 initialization firmware UF2 could not be resolved after local toolchain install.");
|
|
3037
|
-
}
|
|
3038
|
-
const plan = await runEmbed(["flash", "plan", "--board", "rp2350", "--artifact", artifactPath]);
|
|
3039
|
-
const approved = boolValue(args.approved) || boolValue(args.approve);
|
|
3040
|
-
if (!approved) {
|
|
3041
|
-
const planData = responseData(plan) || plan;
|
|
3042
|
-
const automaticBootsel = !!planData.automatic_bootsel
|
|
3043
|
-
|| (Array.isArray(planData.steps) && planData.steps.some((step) => step && step.kind === "picotool_load_force_usb"));
|
|
3044
|
-
return {
|
|
3045
|
-
ok: true,
|
|
3046
|
-
firmware_install: installed,
|
|
3047
|
-
artifact_path: artifactPath,
|
|
3048
|
-
flash_plan: plan,
|
|
3049
|
-
next_step: automaticBootsel
|
|
3050
|
-
? "Initialization flash plan is ready. After user approval, call this tool again with approved=true."
|
|
3051
|
-
: "Initialization flash plan needs a visible UF2 target volume before it can run."
|
|
3052
|
-
};
|
|
3053
|
-
}
|
|
3054
|
-
const flashed = await runEmbed(["flash", "run", "--board", "rp2350", "--artifact", artifactPath, "--approve"]);
|
|
3055
|
-
return {
|
|
3056
|
-
ok: true,
|
|
3057
|
-
firmware_install: installed,
|
|
3058
|
-
artifact_path: artifactPath,
|
|
3059
|
-
flash_plan: plan,
|
|
3060
|
-
flash_result: flashed
|
|
3061
|
-
};
|
|
3482
|
+
return await flashImageCompatibilityAction(args, {
|
|
3483
|
+
artifactKind: "initial_firmware",
|
|
3484
|
+
defaultBoardId: "pico2w-rp2350-monitor",
|
|
3485
|
+
compatibilityAdapter: "dbt_rp2350_initial_firmware_flash_to_agent_flash_image"
|
|
3486
|
+
});
|
|
3062
3487
|
}
|
|
3063
3488
|
|
|
3064
3489
|
async function pluginUpdateCheck(args) {
|
|
@@ -3068,7 +3493,7 @@ async function pluginUpdateCheck(args) {
|
|
|
3068
3493
|
pushOptional(command, "codex-target", args.codex_target || args.codexTarget);
|
|
3069
3494
|
pushOptional(command, "opencode-target", args.opencode_target || args.opencodeTarget);
|
|
3070
3495
|
pushOptional(command, "trae-target", args.trae_target || args.traeTarget);
|
|
3071
|
-
return await runEmbed(command);
|
|
3496
|
+
return sanitizeMcpPublicValue(await runEmbed(command));
|
|
3072
3497
|
}
|
|
3073
3498
|
|
|
3074
3499
|
async function pluginUpdate(args) {
|
|
@@ -3082,7 +3507,7 @@ async function pluginUpdate(args) {
|
|
|
3082
3507
|
pushOptional(command, "codex-target", args.codex_target || args.codexTarget);
|
|
3083
3508
|
pushOptional(command, "opencode-target", args.opencode_target || args.opencodeTarget);
|
|
3084
3509
|
pushOptional(command, "trae-target", args.trae_target || args.traeTarget);
|
|
3085
|
-
return await runEmbed(command);
|
|
3510
|
+
return sanitizeMcpPublicValue(await runEmbed(command));
|
|
3086
3511
|
}
|
|
3087
3512
|
|
|
3088
3513
|
async function localCompile(args) {
|
|
@@ -3091,7 +3516,6 @@ async function localCompile(args) {
|
|
|
3091
3516
|
if (!sourcePath) throw new Error("source_path is required");
|
|
3092
3517
|
if (!outputPath) throw new Error("output_path is required");
|
|
3093
3518
|
const command = ["local", "compile", "taishanpi", "--source", sourcePath, "--output", outputPath];
|
|
3094
|
-
pushOptional(command, "account", args.account_id || args.account);
|
|
3095
3519
|
pushOptional(command, "release-root", args.release_root || args.releaseRoot);
|
|
3096
3520
|
return await runEmbed(command);
|
|
3097
3521
|
}
|
|
@@ -3100,240 +3524,30 @@ async function localQtSmoke(args) {
|
|
|
3100
3524
|
const buildDir = asString(args.build_dir || args.buildDir);
|
|
3101
3525
|
if (!buildDir) throw new Error("build_dir is required");
|
|
3102
3526
|
const command = ["local", "build", "qt-smoke", "--build-dir", buildDir];
|
|
3103
|
-
pushOptional(command, "account", args.account_id || args.account);
|
|
3104
3527
|
pushOptional(command, "source", args.source_path || args.source);
|
|
3105
3528
|
pushOptional(command, "target-name", args.target_name || args.targetName);
|
|
3106
3529
|
pushOptional(command, "release-root", args.release_root || args.releaseRoot);
|
|
3107
3530
|
return await runEmbed(command);
|
|
3108
3531
|
}
|
|
3109
3532
|
|
|
3110
|
-
function serverWorkspaceDisabledResult() {
|
|
3111
|
-
return {
|
|
3112
|
-
ok: false,
|
|
3113
|
-
error: {
|
|
3114
|
-
code: "server_workspaces_disabled",
|
|
3115
|
-
message: "Embed Labs now uses the user's local SDK/toolchain environment for source edits, builds, and board operation. Server-side user workspaces, Docker builds, and image generation are not exposed by the plugin.",
|
|
3116
|
-
remediation: "Use dbt_local_toolchain_install, dbt_local_toolchain_validate, dbt_local_compile, dbt_local_qt_smoke, and Local Bridge board tools. Cloud API remains available for account login, device binding, board knowledge, plugin releases, MCP service logs, and SDK downloads."
|
|
3117
|
-
}
|
|
3118
|
-
};
|
|
3119
|
-
}
|
|
3120
|
-
|
|
3121
|
-
function isServerWorkspaceToolName(name) {
|
|
3122
|
-
return [
|
|
3123
|
-
"dbt_workspace_provision",
|
|
3124
|
-
"dbt_application_generate",
|
|
3125
|
-
"dbt_source_list",
|
|
3126
|
-
"dbt_source_get",
|
|
3127
|
-
"dbt_source_search",
|
|
3128
|
-
"dbt_source_put",
|
|
3129
|
-
"dbt_source_patch",
|
|
3130
|
-
"dbt_application_compile",
|
|
3131
|
-
"dbt_image_generate",
|
|
3132
|
-
"dbt_workspace_release"
|
|
3133
|
-
].includes(name);
|
|
3134
|
-
}
|
|
3135
|
-
|
|
3136
|
-
function isServerWorkspaceAction(action) {
|
|
3137
|
-
return [
|
|
3138
|
-
"provision",
|
|
3139
|
-
"workspace-provision",
|
|
3140
|
-
"source-list",
|
|
3141
|
-
"list-source",
|
|
3142
|
-
"source-get",
|
|
3143
|
-
"source-read",
|
|
3144
|
-
"read-source",
|
|
3145
|
-
"source-search",
|
|
3146
|
-
"search-source",
|
|
3147
|
-
"source-put",
|
|
3148
|
-
"source-write",
|
|
3149
|
-
"write-source",
|
|
3150
|
-
"source-patch",
|
|
3151
|
-
"patch-source",
|
|
3152
|
-
"app",
|
|
3153
|
-
"application",
|
|
3154
|
-
"application-generate",
|
|
3155
|
-
"build-run",
|
|
3156
|
-
"compile",
|
|
3157
|
-
"application-compile",
|
|
3158
|
-
"image",
|
|
3159
|
-
"image-generate",
|
|
3160
|
-
"release",
|
|
3161
|
-
"workspace-release"
|
|
3162
|
-
].includes(action);
|
|
3163
|
-
}
|
|
3164
|
-
|
|
3165
|
-
async function workspaceProvision(args) {
|
|
3166
|
-
const account = asString(args.account_id || args.account);
|
|
3167
|
-
const project = asString(args.project_id || args.project);
|
|
3168
|
-
if (!account) throw new Error("account_id is required");
|
|
3169
|
-
if (!project) throw new Error("project_id is required");
|
|
3170
|
-
const command = [
|
|
3171
|
-
"build",
|
|
3172
|
-
"workspace",
|
|
3173
|
-
"provision",
|
|
3174
|
-
"--account",
|
|
3175
|
-
account,
|
|
3176
|
-
"--project",
|
|
3177
|
-
project,
|
|
3178
|
-
"--template",
|
|
3179
|
-
asString(args.template_id || args.template) || DEFAULT_TEMPLATE_ID,
|
|
3180
|
-
"--copy-mode",
|
|
3181
|
-
asString(args.copy_mode) || "skeleton"
|
|
3182
|
-
];
|
|
3183
|
-
if (boolValue(args.dry_run)) command.push("--dry-run");
|
|
3184
|
-
return await runEmbed(command);
|
|
3185
|
-
}
|
|
3186
|
-
|
|
3187
|
-
async function applicationGenerate(args) {
|
|
3188
|
-
const workspace = asString(args.workspace_id || args.workspace);
|
|
3189
|
-
const prompt = asString(args.prompt || args.request);
|
|
3190
|
-
if (!workspace) throw new Error("workspace_id is required");
|
|
3191
|
-
if (!prompt) throw new Error("prompt is required");
|
|
3192
|
-
const command = ["build", "application", "generate", "--workspace", workspace, "--prompt", prompt];
|
|
3193
|
-
pushOptional(command, "account", args.account_id || args.account);
|
|
3194
|
-
pushOptional(command, "provider", args.provider);
|
|
3195
|
-
pushOptional(command, "model", args.model);
|
|
3196
|
-
pushOptional(command, "target", args.target);
|
|
3197
|
-
const generated = await runEmbed(command);
|
|
3198
|
-
if (!asString(args.download_output)) return generated;
|
|
3199
|
-
const artifact = pickArtifact(generated, "firmware");
|
|
3200
|
-
if (!artifact?.artifact_id) throw new Error("No downloadable application artifact found");
|
|
3201
|
-
const downloaded = await runEmbed(["artifact", "download", artifact.artifact_id, "--output", args.download_output]);
|
|
3202
|
-
return { generated, selected_artifact: artifact, downloaded };
|
|
3203
|
-
}
|
|
3204
|
-
|
|
3205
|
-
async function sourceList(args) {
|
|
3206
|
-
const workspace = asString(args.workspace_id || args.workspace);
|
|
3207
|
-
if (!workspace) throw new Error("workspace_id is required");
|
|
3208
|
-
return await runEmbed(["build", "workspace", "source", "list", workspace]);
|
|
3209
|
-
}
|
|
3210
|
-
|
|
3211
|
-
async function sourceGet(args) {
|
|
3212
|
-
const workspace = asString(args.workspace_id || args.workspace);
|
|
3213
|
-
const sourcePath = asString(args.source_path || args.path);
|
|
3214
|
-
if (!workspace) throw new Error("workspace_id is required");
|
|
3215
|
-
if (!sourcePath) throw new Error("source_path is required");
|
|
3216
|
-
const command = ["build", "workspace", "source", "get", workspace, "--path", sourcePath];
|
|
3217
|
-
pushOptional(command, "output", args.output_path || args.output);
|
|
3218
|
-
return await runEmbed(command);
|
|
3219
|
-
}
|
|
3220
|
-
|
|
3221
|
-
async function sourceSearch(args) {
|
|
3222
|
-
const workspace = asString(args.workspace_id || args.workspace);
|
|
3223
|
-
if (!workspace) throw new Error("workspace_id is required");
|
|
3224
|
-
const command = ["build", "workspace", "source", "search", workspace];
|
|
3225
|
-
pushOptional(command, "query", args.query);
|
|
3226
|
-
pushOptional(command, "glob", args.glob);
|
|
3227
|
-
pushOptional(command, "limit", args.limit);
|
|
3228
|
-
return await runEmbed(command);
|
|
3229
|
-
}
|
|
3230
|
-
|
|
3231
|
-
async function sourcePut(args) {
|
|
3232
|
-
const workspace = asString(args.workspace_id || args.workspace);
|
|
3233
|
-
const localPath = asString(args.local_path || args.local);
|
|
3234
|
-
const sourcePath = asString(args.source_path || args.path);
|
|
3235
|
-
if (!workspace) throw new Error("workspace_id is required");
|
|
3236
|
-
if (!localPath) throw new Error("local_path is required");
|
|
3237
|
-
if (!sourcePath) throw new Error("source_path is required");
|
|
3238
|
-
const command = ["build", "workspace", "source", "put", workspace, "--file", `${localPath}:${sourcePath}`];
|
|
3239
|
-
pushOptional(command, "account", args.account_id || args.account);
|
|
3240
|
-
return await runEmbed(command);
|
|
3241
|
-
}
|
|
3242
|
-
|
|
3243
|
-
async function sourcePatch(args) {
|
|
3244
|
-
const workspace = asString(args.workspace_id || args.workspace);
|
|
3245
|
-
const patchPath = asString(args.patch_path || args.patch);
|
|
3246
|
-
if (!workspace) throw new Error("workspace_id is required");
|
|
3247
|
-
if (!patchPath) throw new Error("patch_path is required");
|
|
3248
|
-
const command = ["build", "workspace", "source", "patch", workspace, "--patch", patchPath];
|
|
3249
|
-
pushOptional(command, "account", args.account_id || args.account);
|
|
3250
|
-
return await runEmbed(command);
|
|
3251
|
-
}
|
|
3252
|
-
|
|
3253
|
-
async function applicationCompile(args) {
|
|
3254
|
-
const workspace = asString(args.workspace_id || args.workspace);
|
|
3255
|
-
if (!workspace) throw new Error("workspace_id is required");
|
|
3256
|
-
const command = ["build", "application", "compile", "--workspace", workspace];
|
|
3257
|
-
pushOptional(command, "account", args.account_id || args.account);
|
|
3258
|
-
pushOptional(command, "source", args.source_path || args.source);
|
|
3259
|
-
pushOptional(command, "output-name", args.output_name);
|
|
3260
|
-
pushOptional(command, "execution-mode", args.execution_mode);
|
|
3261
|
-
pushOptional(command, "compiler", args.compiler);
|
|
3262
|
-
return await runEmbed(command);
|
|
3263
|
-
}
|
|
3264
|
-
|
|
3265
|
-
async function taskStatus(args) {
|
|
3266
|
-
const task = asString(args.task_id || args.task);
|
|
3267
|
-
if (!task) throw new Error("task_id is required");
|
|
3268
|
-
return await runEmbed(["cloud", "task", "status", task]);
|
|
3269
|
-
}
|
|
3270
|
-
|
|
3271
|
-
function safeArtifactOutputFileName(name) {
|
|
3272
|
-
return asString(name).replace(/[^A-Za-z0-9._-]+/g, "_") || "task.log";
|
|
3273
|
-
}
|
|
3274
|
-
|
|
3275
|
-
async function taskLogDownload(args) {
|
|
3276
|
-
const task = asString(args.task_id || args.task);
|
|
3277
|
-
if (!task) throw new Error("task_id is required");
|
|
3278
|
-
const status = await taskStatus({ task_id: task });
|
|
3279
|
-
const data = responseData(status);
|
|
3280
|
-
const artifacts = Array.isArray(data?.artifacts) ? data.artifacts : [];
|
|
3281
|
-
const log = artifacts.find((item) => item.kind === "log" || asString(item.name).endsWith(".log"));
|
|
3282
|
-
if (!log?.artifact_id) throw new Error(`Task ${task} does not expose a log artifact`);
|
|
3283
|
-
const output = asString(args.output_path || args.output) || path.join(".embed-labs", "artifacts", `${task}-${safeArtifactOutputFileName(log.name)}`);
|
|
3284
|
-
const downloaded = await runEmbed(["artifact", "download", log.artifact_id, "--output", output]);
|
|
3285
|
-
return { status, selected_artifact: log, downloaded };
|
|
3286
|
-
}
|
|
3287
|
-
|
|
3288
|
-
async function imageGenerate(args) {
|
|
3289
|
-
const workspace = asString(args.workspace_id || args.workspace);
|
|
3290
|
-
const prompt = asString(args.prompt || args.request);
|
|
3291
|
-
if (!workspace) throw new Error("workspace_id is required");
|
|
3292
|
-
if (!prompt) throw new Error("prompt is required");
|
|
3293
|
-
const command = ["build", "image", "generate", "--workspace", workspace, "--prompt", prompt];
|
|
3294
|
-
pushOptional(command, "account", args.account_id || args.account);
|
|
3295
|
-
pushOptional(command, "provider", args.provider);
|
|
3296
|
-
pushOptional(command, "model", args.model);
|
|
3297
|
-
pushOptional(command, "image-profile", args.image_profile);
|
|
3298
|
-
pushOptional(command, "execution-mode", args.execution_mode);
|
|
3299
|
-
pushOptional(command, "worker-pool", args.worker_pool);
|
|
3300
|
-
const generated = await runEmbed(command);
|
|
3301
|
-
if (!asString(args.download_output)) return generated;
|
|
3302
|
-
const artifact = pickArtifact(generated, "image");
|
|
3303
|
-
if (!artifact?.artifact_id) throw new Error("No downloadable image artifact found");
|
|
3304
|
-
const downloaded = await runEmbed(["artifact", "download", artifact.artifact_id, "--output", args.download_output]);
|
|
3305
|
-
return { generated, selected_artifact: artifact, downloaded };
|
|
3306
|
-
}
|
|
3307
|
-
|
|
3308
|
-
async function artifactDownload(args) {
|
|
3309
|
-
const artifact = asString(args.artifact_id || args.artifact);
|
|
3310
|
-
const output = asString(args.output_path || args.output);
|
|
3311
|
-
if (!artifact) throw new Error("artifact_id is required");
|
|
3312
|
-
if (!output) throw new Error("output_path is required");
|
|
3313
|
-
return await runEmbed(["artifact", "download", artifact, "--output", output]);
|
|
3314
|
-
}
|
|
3315
|
-
|
|
3316
|
-
async function workspaceRelease(args) {
|
|
3317
|
-
const workspace = asString(args.workspace_id || args.workspace);
|
|
3318
|
-
if (!workspace) throw new Error("workspace_id is required");
|
|
3319
|
-
const command = ["build", "workspace", "release", workspace];
|
|
3320
|
-
if (boolValue(args.dry_run)) command.push("--dry-run");
|
|
3321
|
-
if (boolValue(args.no_settle_storage)) command.push("--no-settle-storage");
|
|
3322
|
-
return await runEmbed(command);
|
|
3323
|
-
}
|
|
3324
|
-
|
|
3325
3533
|
async function callTool(name, args = {}) {
|
|
3326
|
-
|
|
3534
|
+
const requestedName = name;
|
|
3535
|
+
if (mcpToolProfile() === "agent" && AGENT_MCP_TOOL_ALIASES.has(requestedName)) {
|
|
3536
|
+
args = sanitizeAgentProfileArgs(args);
|
|
3537
|
+
}
|
|
3538
|
+
name = canonicalMcpToolName(name);
|
|
3539
|
+
if (name === "dbt_agent_status") return await pluginAgentStatus();
|
|
3540
|
+
if (name === "dbt_agent_action_schema") return await agentActionSchema(args);
|
|
3541
|
+
if (name === "dbt_agent_action_run") return await agentActionRun(args);
|
|
3542
|
+
if (name === "dbt_agent_chat") return await agentChat(args);
|
|
3327
3543
|
if (name === "dbt_plugin_update_check") return await pluginUpdateCheck(args);
|
|
3328
3544
|
if (name === "dbt_plugin_update") return await pluginUpdate(args);
|
|
3545
|
+
if (name === "dbt_current_board_status") return await currentBoardStatus(args);
|
|
3329
3546
|
if (name === "dbt_supported_boards") return await supportedBoards(args);
|
|
3330
3547
|
if (name === "dbt_local_toolchain_list") return await localToolchainList(args);
|
|
3331
3548
|
if (name === "dbt_local_toolchain_installed") return await localToolchainInstalled(args);
|
|
3332
3549
|
if (name === "dbt_local_toolchain_latest") return await localToolchainLatest(args);
|
|
3333
3550
|
if (name === "dbt_local_toolchain_current") return await localToolchainCurrent(args);
|
|
3334
|
-
const authStatus = await pluginAuthStatus();
|
|
3335
|
-
if (!authStatus.ok) return authStatus;
|
|
3336
|
-
if (name === "dbt_current_board_status" || name === "dbtstatus") return await currentBoardStatus(args, authStatus);
|
|
3337
3551
|
if (name === "dbt_initial_image_flash") return await initialImageFlash(args);
|
|
3338
3552
|
if (name === "dbt_rp2350_monitor_status") return await rp2350MonitorStatus(args);
|
|
3339
3553
|
if (name === "dbt_rp2350_capabilities") return await rp2350Capabilities(args);
|
|
@@ -3350,13 +3564,8 @@ async function callTool(name, args = {}) {
|
|
|
3350
3564
|
if (name === "dbt_rp2350_monitor_command") return await rp2350MonitorCommand(args);
|
|
3351
3565
|
if (name === "dbt_cloud_status") return await cloudStatus(args);
|
|
3352
3566
|
if (name === "dbt_board_knowledge_search") return await boardKnowledgeSearch(args);
|
|
3353
|
-
if (isServerWorkspaceToolName(name)) return serverWorkspaceDisabledResult();
|
|
3354
|
-
if (name === "dbt_task_status") return await taskStatus(args);
|
|
3355
|
-
if (name === "dbt_task_log_download") return await taskLogDownload(args);
|
|
3356
|
-
if (name === "dbt_artifact_download") return await artifactDownload(args);
|
|
3357
3567
|
if (name === "dbt_board_debug") return await boardDebug(args);
|
|
3358
3568
|
if (name === "dbt_boot_logo_update") return await bootLogoUpdate(args);
|
|
3359
|
-
if (name === "dbt_update_logo") return await updateLogo(args);
|
|
3360
3569
|
if (name === "dbt_compose_boot_logo") return await composeBootLogo(args);
|
|
3361
3570
|
if (name === "dbt_local_toolchain_install") return await localToolchainInstall(args);
|
|
3362
3571
|
if (name === "dbt_local_toolchain_uninstall") return await localToolchainUninstall(args);
|
|
@@ -3374,8 +3583,10 @@ async function callTool(name, args = {}) {
|
|
|
3374
3583
|
let payload = {};
|
|
3375
3584
|
if (asString(args.arguments_json)) payload = JSON.parse(args.arguments_json);
|
|
3376
3585
|
if (asString(args.request) && !payload.prompt) payload.prompt = asString(args.request);
|
|
3586
|
+
if (["agent-action-schema", "action-schema", "agent-actions", "action-catalog"].includes(action)) return await agentActionSchema(payload);
|
|
3587
|
+
if (["agent-action-run", "action-run", "agent-run-action", "run-action"].includes(action)) return await agentActionRun(payload);
|
|
3377
3588
|
if (["supported-boards", "boards", "board-list", "registry-list"].includes(action)) return await supportedBoards(payload);
|
|
3378
|
-
if (["board-status", "status"].includes(action)) return await currentBoardStatus(payload
|
|
3589
|
+
if (["board-status", "status"].includes(action)) return await currentBoardStatus(payload);
|
|
3379
3590
|
if (["board-reboot", "reboot", "restart", "device-reboot", "board-restart"].includes(action)) return await boardControl({ ...payload, operation: "reboot" });
|
|
3380
3591
|
if (["board-bootloader", "bootloader", "loader", "enter-bootloader", "enter-loader", "bootsel"].includes(action)) return await boardControl({ ...payload, operation: "bootloader" });
|
|
3381
3592
|
if (["image-flash", "flash-image", "image-update", "update-image", "board-image-flash", "board-image-update"].includes(action)) return await boardControl({ ...payload, operation: "image.flash" });
|
|
@@ -3412,10 +3623,6 @@ async function callTool(name, args = {}) {
|
|
|
3412
3623
|
if (["local-compile", "compile-local"].includes(action)) return await localCompile(payload);
|
|
3413
3624
|
if (["local-qt-smoke", "qt-smoke", "local-qt-build"].includes(action)) return await localQtSmoke(payload);
|
|
3414
3625
|
if (["taishanpi-qt-app", "qt-app", "qt-workflow", "taishanpi-qt-workflow", "qt-develop"].includes(action)) return await taishanpiQtAppWorkflow(payload);
|
|
3415
|
-
if (isServerWorkspaceAction(action)) return serverWorkspaceDisabledResult();
|
|
3416
|
-
if (["task-status", "status-task"].includes(action)) return await taskStatus(payload);
|
|
3417
|
-
if (["task-log", "task-log-download", "log-download"].includes(action)) return await taskLogDownload(payload);
|
|
3418
|
-
if (["download", "artifact-download"].includes(action)) return await artifactDownload(payload);
|
|
3419
3626
|
return { ok: false, error_code: "unsupported_dbt_action" };
|
|
3420
3627
|
}
|
|
3421
3628
|
throw new Error(`Unknown tool: ${name}`);
|
|
@@ -3458,7 +3665,17 @@ async function handleRpc(message) {
|
|
|
3458
3665
|
let result;
|
|
3459
3666
|
let toolCallFailed = false;
|
|
3460
3667
|
try {
|
|
3461
|
-
|
|
3668
|
+
const toolName = asString(message.params?.name);
|
|
3669
|
+
if (!isMcpToolExposed(toolName)) {
|
|
3670
|
+
throw new Error(JSON.stringify({
|
|
3671
|
+
error: {
|
|
3672
|
+
code: "mcp_tool_not_exposed",
|
|
3673
|
+
message: `${toolName || "requested tool"} is not exposed in the current EmbedLabs MCP tool profile.`,
|
|
3674
|
+
remediation: "Use embedlabs_action_run/embedlabs_chat in the default Agent profile, or enable EMBEDLABS_MCP_TOOL_PROFILE=compact/full for compatibility testing."
|
|
3675
|
+
}
|
|
3676
|
+
}));
|
|
3677
|
+
}
|
|
3678
|
+
result = await callTool(toolName, message.params?.arguments || {});
|
|
3462
3679
|
toolCallFailed = result?.ok === false
|
|
3463
3680
|
|| result?.response?.ok === false
|
|
3464
3681
|
|| result?.response?.data?.result?.ok === false
|