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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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
  }