@kvell007/embed-labs-cli 0.1.0-alpha.22 → 0.1.0-alpha.24

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 CHANGED
@@ -118,6 +118,7 @@ For local hardware access:
118
118
  embed bridge start
119
119
  embed device list
120
120
  embed tool call wifi.scan --input-json '{"host":"198.19.77.2","user":"root"}'
121
+ embed tool call rp2350.monitor.spi.transfer --input-json '{"hex":"a55a3cc3"}' --approve
121
122
  embed tool call chip.temperature --input-json '{"host":"198.19.77.2","user":"root"}'
122
123
  embed flash plan --board <rp2350|taishanpi> --artifact ./artifact.bin
123
124
  ```
@@ -198,6 +199,7 @@ embed tool call chip.cpu.frequency --input-json '{"host":"198.19.77.2","user":"r
198
199
  embed tool call chip.temperature --input-json '{"host":"198.19.77.2","user":"root"}' [--json]
199
200
  embed tool call qml.runtime.status --input-json '{"host":"198.19.77.2","user":"root","port":18130}' [--json]
200
201
  embed tool call qml.runtime.start --input-json '{"host":"198.19.77.2","user":"root","port":18130}' [--json]
202
+ embed tool call rp2350.monitor.spi.transfer --input-json '{"hex":"a55a3cc3"}' --approve [--json]
201
203
 
202
204
  embed serial list [--json]
203
205
  embed serial capture --path <port> [--baud 115200] [--duration 5] [--json]
package/dist/index.js CHANGED
@@ -1392,19 +1392,34 @@ function isApiResponse(value) {
1392
1392
  return isJsonObject(error) && typeof error.code === "string" && typeof error.message === "string";
1393
1393
  }
1394
1394
  async function bridgeGet(path) {
1395
- const response = await fetch(`${DEFAULT_BRIDGE_URL}${path}`, {
1396
- headers: bridgeHeaders("GET", path, "")
1397
- });
1398
- return await response.json();
1395
+ return await bridgeRequest("GET", path);
1399
1396
  }
1400
1397
  async function bridgePost(path, body) {
1401
- const bodyText = JSON.stringify(body);
1402
- const response = await fetch(`${DEFAULT_BRIDGE_URL}${path}`, {
1403
- method: "POST",
1404
- headers: bridgeHeaders("POST", path, bodyText, { "content-type": "application/json" }),
1405
- body: bodyText
1406
- });
1407
- return await response.json();
1398
+ return await bridgeRequest("POST", path, body);
1399
+ }
1400
+ async function bridgeRequest(method, path, body) {
1401
+ const bodyText = body === undefined ? "" : JSON.stringify(body);
1402
+ const makeRequest = async () => {
1403
+ const response = await fetch(`${DEFAULT_BRIDGE_URL}${path}`, {
1404
+ method,
1405
+ headers: bridgeHeaders(method, path, method === "POST" ? bodyText : "", method === "POST" ? { "content-type": "application/json" } : {}),
1406
+ body: method === "POST" ? bodyText : undefined
1407
+ });
1408
+ return await response.json();
1409
+ };
1410
+ try {
1411
+ return await makeRequest();
1412
+ }
1413
+ catch (error) {
1414
+ if (!isBridgeConnectionFailure(error)) {
1415
+ throw error;
1416
+ }
1417
+ const started = await ensureBridgeStartedForRequest();
1418
+ if (!started.ok) {
1419
+ return started;
1420
+ }
1421
+ return await makeRequest();
1422
+ }
1408
1423
  }
1409
1424
  function bridgeHeaders(method, path, bodyText, base = {}) {
1410
1425
  const token = process.env.EMBED_BRIDGE_TOKEN?.trim();
@@ -1433,6 +1448,87 @@ function addBridgeRequestSignature(headers, method, pathWithQuery, bodyText, tok
1433
1448
  headers["x-embed-body-sha256"] = bodySha256;
1434
1449
  headers["x-embed-signature"] = createHmac("sha256", token).update(canonical).digest("hex");
1435
1450
  }
1451
+ function isBridgeConnectionFailure(error) {
1452
+ const message = error instanceof Error ? error.message : String(error);
1453
+ return message.includes("fetch failed") ||
1454
+ message.includes("ECONNREFUSED") ||
1455
+ message.includes("ECONNRESET") ||
1456
+ message.includes("UND_ERR_SOCKET");
1457
+ }
1458
+ async function ensureBridgeStartedForRequest() {
1459
+ if (process.env.EMBED_BRIDGE_AUTO_START === "0") {
1460
+ return fail("bridge_unavailable", `embed-local-bridge is not running at ${DEFAULT_BRIDGE_URL}.`, {
1461
+ remediation: `Start it with: embed bridge start`
1462
+ });
1463
+ }
1464
+ let bridgeURL;
1465
+ try {
1466
+ bridgeURL = new URL(DEFAULT_BRIDGE_URL);
1467
+ }
1468
+ catch {
1469
+ return fail("bridge_url_invalid", `EMBED_BRIDGE_URL is not a valid URL: ${DEFAULT_BRIDGE_URL}`);
1470
+ }
1471
+ if (!isLocalBridgeURL(bridgeURL)) {
1472
+ return fail("bridge_unavailable", `embed-local-bridge is not reachable at ${DEFAULT_BRIDGE_URL}.`, {
1473
+ remediation: `Start the bridge for that host, or set EMBED_BRIDGE_URL to a local bridge URL.`
1474
+ });
1475
+ }
1476
+ const launcher = await resolveBridgeLauncher();
1477
+ const host = bridgeURL.hostname === "::1" ? "::1" : bridgeURL.hostname || "127.0.0.1";
1478
+ const port = bridgeURL.port || "18083";
1479
+ const env = {
1480
+ ...process.env,
1481
+ EMBED_BRIDGE_HOST: host,
1482
+ EMBED_BRIDGE_PORT: port
1483
+ };
1484
+ const child = spawn(launcher.command, [...launcher.args, "--host", host, "--port", port], {
1485
+ cwd: process.cwd(),
1486
+ detached: true,
1487
+ stdio: "ignore",
1488
+ env
1489
+ });
1490
+ child.unref();
1491
+ const ready = await waitForBridgeHealth(bridgeURL, 8000);
1492
+ if (!ready.ok) {
1493
+ return ready;
1494
+ }
1495
+ return ok({
1496
+ started: true,
1497
+ bridge_url: DEFAULT_BRIDGE_URL,
1498
+ command: launcher.command
1499
+ });
1500
+ }
1501
+ function isLocalBridgeURL(url) {
1502
+ const host = url.hostname.toLowerCase();
1503
+ return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
1504
+ }
1505
+ async function waitForBridgeHealth(bridgeURL, timeoutMs) {
1506
+ const deadline = Date.now() + timeoutMs;
1507
+ let lastError = "";
1508
+ while (Date.now() < deadline) {
1509
+ try {
1510
+ const response = await fetch(new URL("/healthz", bridgeURL), {
1511
+ headers: bridgeHeaders("GET", "/healthz", "")
1512
+ });
1513
+ const parsed = await response.json();
1514
+ if (parsed.ok) {
1515
+ return parsed;
1516
+ }
1517
+ lastError = parsed.error?.message ?? `HTTP ${response.status}`;
1518
+ }
1519
+ catch (error) {
1520
+ lastError = error instanceof Error ? error.message : String(error);
1521
+ }
1522
+ await delay(100);
1523
+ }
1524
+ return fail("bridge_start_failed", `embed-local-bridge did not become healthy at ${DEFAULT_BRIDGE_URL}.`, {
1525
+ remediation: `Run embed bridge start in a separate terminal and retry.`,
1526
+ details: { last_error: lastError }
1527
+ });
1528
+ }
1529
+ function delay(ms) {
1530
+ return new Promise((resolveDelay) => setTimeout(resolveDelay, ms));
1531
+ }
1436
1532
  async function cloudGet(path) {
1437
1533
  return await cloudRequest("GET", path);
1438
1534
  }
@@ -7557,6 +7653,7 @@ Local hardware:
7557
7653
  embed tool list
7558
7654
  embed tool call device.probe --input-json '{"host":"198.19.77.2","ports":[22,15301]}'
7559
7655
  embed tool call wifi.scan --input-json '{"host":"198.19.77.2","user":"root"}'
7656
+ embed tool call rp2350.monitor.spi.transfer --input-json '{"hex":"a55a3cc3"}' --approve
7560
7657
  embed tool call chip.temperature --input-json '{"host":"198.19.77.2","user":"root"}'
7561
7658
  embed tool call qml.runtime.status --input-json '{"host":"198.19.77.2","user":"root","port":18130}'
7562
7659
  embed device list
@@ -7781,6 +7878,7 @@ Usage:
7781
7878
  embed tool call qml.runtime.start --input-json '{"host":"198.19.77.2","user":"root","port":18130}' [--json]
7782
7879
  embed tool call rp2350.monitor.status [--json]
7783
7880
  embed tool call rp2350.monitor.logic.capture --input-json '{"pin_base":16,"pin_count":4,"sample_rate":1000000,"samples":4096}' [--json]
7881
+ embed tool call rp2350.monitor.spi.transfer --input-json '{"hex":"a55a3cc3"}' --approve [--json]
7784
7882
  embed tool call rp2350.monitor.logic.decode --input-json '{"input_path":".embed-labs/rp2350-monitor/captures/logic.jsonl","decoder":"summary"}' [--json]
7785
7883
  embed deploy taishanpi --host <ip> --artifact <local_file> --approve [--remote-path /userdata/embed-labs/apps/app] [--run] [--json]
7786
7884
  embed board deploy taishanpi --host <ip> --artifact <local_file> --approve [--remote-path /userdata/embed-labs/apps/app] [--run] [--json]