@p697/clawket 0.6.0 → 0.6.2

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.
Files changed (2) hide show
  1. package/dist/index.js +199 -16
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1447,6 +1447,7 @@ var HermesLocalBridge = class {
1447
1447
  hermesChild = null;
1448
1448
  modelStateCache = null;
1449
1449
  contextWindowCache = /* @__PURE__ */ new Map();
1450
+ bridgeRequestSeq = 0;
1450
1451
  constructor(options = {}) {
1451
1452
  this.options = options;
1452
1453
  this.host = normalizeHost(options.host);
@@ -1491,6 +1492,12 @@ var HermesLocalBridge = class {
1491
1492
  if (this.httpServer) {
1492
1493
  return;
1493
1494
  }
1495
+ this.logPerf("bridge_start_begin", {
1496
+ apiBaseUrl: this.apiBaseUrl,
1497
+ host: this.host,
1498
+ port: this.port
1499
+ });
1500
+ const startStartedAt = Date.now();
1494
1501
  const hermesReady = await this.ensureHermesApiReady();
1495
1502
  this.httpServer = createServer((req, res) => {
1496
1503
  void this.handleHttpRequest(req, res);
@@ -1534,6 +1541,11 @@ var HermesLocalBridge = class {
1534
1541
  prewarmComplete: true,
1535
1542
  lastError: hermesReady ? null : this.snapshot.lastError
1536
1543
  });
1544
+ this.logPerf("bridge_start_ready", {
1545
+ elapsedMs: Date.now() - startStartedAt,
1546
+ hermesReady,
1547
+ hermesApiReachable: this.snapshot.hermesApiReachable
1548
+ });
1537
1549
  this.log(hermesReady ? `hermes bridge listening on ${this.snapshot.bridgeUrl}` : `hermes bridge listening on ${this.snapshot.bridgeUrl} (degraded: Hermes API not ready yet)`);
1538
1550
  }
1539
1551
  async stop() {
@@ -1575,21 +1587,39 @@ var HermesLocalBridge = class {
1575
1587
  });
1576
1588
  }
1577
1589
  async ensureHermesApiReady() {
1590
+ const startedAt = Date.now();
1578
1591
  if (await probeHermesApi(this.apiBaseUrl, this.apiKey)) {
1579
1592
  this.updateSnapshot({ hermesApiReachable: true, lastError: null });
1593
+ this.logPerf("hermes_api_probe", {
1594
+ result: "warm",
1595
+ elapsedMs: Date.now() - startedAt
1596
+ });
1580
1597
  this.log(`reusing Hermes API already running at ${this.apiBaseUrl}`);
1581
1598
  return true;
1582
1599
  }
1583
1600
  if (this.options.startHermesIfNeeded === false) {
1584
1601
  const error = `Hermes API is not reachable at ${this.apiBaseUrl}. Start Hermes gateway with API server enabled and retry.`;
1585
1602
  this.updateSnapshot({ hermesApiReachable: false, lastError: error });
1603
+ this.logPerf("hermes_api_probe", {
1604
+ result: "unreachable_no_autostart",
1605
+ elapsedMs: Date.now() - startedAt
1606
+ });
1586
1607
  this.log(error);
1587
1608
  return false;
1588
1609
  }
1610
+ this.logPerf("hermes_api_probe", {
1611
+ result: "cold_start_required",
1612
+ elapsedMs: Date.now() - startedAt
1613
+ });
1589
1614
  return this.startHermesGatewayProcess();
1590
1615
  }
1591
1616
  async startHermesGatewayProcess() {
1592
1617
  const command = this.options.hermesCommand?.trim() || "hermes";
1618
+ const startedAt = Date.now();
1619
+ this.logPerf("hermes_api_cold_start_begin", {
1620
+ command,
1621
+ apiBaseUrl: this.apiBaseUrl
1622
+ });
1593
1623
  this.log(`starting hermes gateway via ${command}`);
1594
1624
  const verboseHermesStdio = process.env.CLAWKET_HERMES_VERBOSE === "1";
1595
1625
  const hermesChildEnv = {
@@ -1624,12 +1654,19 @@ var HermesLocalBridge = class {
1624
1654
  while (Date.now() - startMs < HERMES_BOOT_TIMEOUT_MS) {
1625
1655
  if (await probeHermesApi(this.apiBaseUrl, this.apiKey)) {
1626
1656
  this.updateSnapshot({ hermesApiReachable: true, lastError: null });
1657
+ this.logPerf("hermes_api_cold_start_ready", {
1658
+ elapsedMs: Date.now() - startedAt
1659
+ });
1627
1660
  return true;
1628
1661
  }
1629
1662
  await delay2(500);
1630
1663
  }
1631
1664
  const error = `Hermes API did not become ready within ${HERMES_BOOT_TIMEOUT_MS}ms at ${this.apiBaseUrl}.`;
1632
1665
  this.updateSnapshot({ hermesApiReachable: false, lastError: error });
1666
+ this.logPerf("hermes_api_cold_start_timeout", {
1667
+ elapsedMs: Date.now() - startedAt,
1668
+ timeoutMs: HERMES_BOOT_TIMEOUT_MS
1669
+ });
1633
1670
  this.log(error);
1634
1671
  return false;
1635
1672
  }
@@ -1647,6 +1684,8 @@ var HermesLocalBridge = class {
1647
1684
  });
1648
1685
  }
1649
1686
  async prewarmBridgeState() {
1687
+ const startedAt = Date.now();
1688
+ this.logPerf("bridge_prewarm_begin");
1650
1689
  const tasks = [
1651
1690
  () => {
1652
1691
  this.listHermesSessions(24);
@@ -1665,6 +1704,9 @@ var HermesLocalBridge = class {
1665
1704
  this.log(`bridge prewarm skipped: ${formatError2(error)}`);
1666
1705
  }
1667
1706
  }));
1707
+ this.logPerf("bridge_prewarm_done", {
1708
+ elapsedMs: Date.now() - startedAt
1709
+ });
1668
1710
  }
1669
1711
  async handleHttpRequest(req, res) {
1670
1712
  const pathname = readRequestPathname(req.url);
@@ -1739,23 +1781,34 @@ var HermesLocalBridge = class {
1739
1781
  }
1740
1782
  async dispatchRequest(method, params) {
1741
1783
  const payload = isRecord(params) ? params : {};
1784
+ const shouldTracePerf = method === "health" || method === "last-heartbeat" || method === "sessions.list" || method === "chat.history" || method === "chat.send" || method === "models.list" || method === "model.current" || method === "model.get";
1785
+ const requestStartedAt = shouldTracePerf ? Date.now() : 0;
1786
+ const requestSeq = shouldTracePerf ? ++this.bridgeRequestSeq : 0;
1787
+ if (shouldTracePerf) {
1788
+ this.logPerf("bridge_request_begin", {
1789
+ requestSeq,
1790
+ method,
1791
+ sessionKey: readString(payload.sessionKey) || void 0,
1792
+ limit: readPositiveInt(payload.limit, 0) || void 0
1793
+ });
1794
+ }
1742
1795
  switch (method) {
1743
1796
  case "health":
1744
1797
  case "last-heartbeat":
1745
- return {
1798
+ return this.traceBridgeRequest(method, requestStartedAt, requestSeq, {
1746
1799
  status: this.snapshot.hermesApiReachable ? "ok" : "degraded",
1747
1800
  ts: Date.now(),
1748
1801
  hermesApiReachable: this.snapshot.hermesApiReachable
1749
- };
1802
+ });
1750
1803
  case "sessions.list":
1751
- return {
1804
+ return this.traceBridgeRequest(method, requestStartedAt, requestSeq, {
1752
1805
  defaults: this.getHermesSessionListDefaults(),
1753
1806
  sessions: this.listHermesSessions(readPositiveInt(payload.limit, 100))
1754
- };
1807
+ });
1755
1808
  case "chat.history":
1756
- return this.getHermesSessionHistory(readString(payload.sessionKey) || DEFAULT_SESSION_ID, readPositiveInt(payload.limit, 50));
1809
+ return this.traceBridgeRequest(method, requestStartedAt, requestSeq, this.getHermesSessionHistory(readString(payload.sessionKey) || DEFAULT_SESSION_ID, readPositiveInt(payload.limit, 50)));
1757
1810
  case "chat.send":
1758
- return this.handleChatSend(payload);
1811
+ return this.traceBridgeRequest(method, requestStartedAt, requestSeq, this.handleChatSend(payload));
1759
1812
  case "sessions.reset":
1760
1813
  this.cancelActiveRunsForSession(readString(payload.key) || DEFAULT_SESSION_ID);
1761
1814
  this.sessionStore.resetSession(readString(payload.key) || DEFAULT_SESSION_ID);
@@ -1875,6 +1928,17 @@ var HermesLocalBridge = class {
1875
1928
  throw new Error(`Unsupported Hermes bridge method: ${method}`);
1876
1929
  }
1877
1930
  }
1931
+ async traceBridgeRequest(method, startedAt, requestSeq, value) {
1932
+ const result = await value;
1933
+ if (startedAt > 0) {
1934
+ this.logPerf("bridge_request", {
1935
+ requestSeq,
1936
+ method,
1937
+ elapsedMs: Date.now() - startedAt
1938
+ });
1939
+ }
1940
+ return result;
1941
+ }
1878
1942
  async handleChatSend(payload) {
1879
1943
  const sessionKey = readString(payload.sessionKey) || DEFAULT_SESSION_ID;
1880
1944
  const text = readString(payload.message);
@@ -4659,6 +4723,10 @@ var HermesLocalBridge = class {
4659
4723
  log(line) {
4660
4724
  this.options.onLog?.(line);
4661
4725
  }
4726
+ logPerf(event, fields) {
4727
+ const payload = fields ? Object.entries(fields).filter(([, value]) => value !== void 0).map(([key, value]) => `${key}=${String(value)}`).join(" ") : "";
4728
+ this.log(`[perf] ${event}${payload ? ` ${payload}` : ""}`);
4729
+ }
4662
4730
  async hydrateToolOutputsFromHermesState(params) {
4663
4731
  const toolOutputs = this.readHermesToolOutputsFromLocalState(params.sessionId, params.runStartedAtMs);
4664
4732
  if (toolOutputs.length === 0 || params.completedTools.length === 0) {
@@ -5943,10 +6011,29 @@ var HermesRelayRuntime = class {
5943
6011
  if (text == null)
5944
6012
  return;
5945
6013
  if (text.startsWith(RELAY_CONTROL_PREFIX)) {
6014
+ this.handleRelayControl(text);
5946
6015
  return;
5947
6016
  }
5948
6017
  this.forwardOrQueueBridgeMessage({ text });
5949
6018
  }
6019
+ handleRelayControl(text) {
6020
+ try {
6021
+ const parsed = JSON.parse(text.slice(RELAY_CONTROL_PREFIX.length));
6022
+ if (parsed?.event !== "gateway_ping") {
6023
+ return;
6024
+ }
6025
+ const relay = this.relaySocket;
6026
+ if (!relay || relay.readyState !== WebSocket2.OPEN) {
6027
+ return;
6028
+ }
6029
+ relay.send(`${RELAY_CONTROL_PREFIX}${JSON.stringify({
6030
+ type: "control",
6031
+ event: "gateway_pong",
6032
+ ts: typeof parsed.ts === "number" ? parsed.ts : Date.now()
6033
+ })}`);
6034
+ } catch {
6035
+ }
6036
+ }
5950
6037
  handleBridgeMessage(data, isBinary) {
5951
6038
  const relay = this.relaySocket;
5952
6039
  if (isBinary) {
@@ -6065,9 +6152,8 @@ var HermesRelayRuntime = class {
6065
6152
  } else {
6066
6153
  const payload = await response.json();
6067
6154
  if (!payload?.hasBridge) {
6068
- this.log("bridge status probe reported hasBridge=false; restarting relay socket");
6069
- this.relaySocket?.close();
6070
- this.bridgeSocket?.close();
6155
+ this.log("bridge status probe reported hasBridge=false; recycling relay socket");
6156
+ this.recycleRelaySocket("bridge status probe reported hasBridge=false");
6071
6157
  return;
6072
6158
  }
6073
6159
  }
@@ -6157,6 +6243,26 @@ var HermesRelayRuntime = class {
6157
6243
  clearTimeout(this.pendingBridgeHealthProbe.timeout);
6158
6244
  this.pendingBridgeHealthProbe = null;
6159
6245
  }
6246
+ recycleRelaySocket(reason) {
6247
+ const relay = this.relaySocket;
6248
+ if (!relay)
6249
+ return;
6250
+ this.relaySocket = null;
6251
+ this.clearBridgeStatusProbe();
6252
+ this.clearPendingBridgeHealthProbe();
6253
+ this.updateSnapshot({
6254
+ relayConnected: false,
6255
+ bridgeConnected: this.bridgeSocket?.readyState === WebSocket2.OPEN,
6256
+ lastError: reason
6257
+ });
6258
+ try {
6259
+ relay.close();
6260
+ } catch {
6261
+ }
6262
+ if (!this.stopped) {
6263
+ this.scheduleRelayReconnect();
6264
+ }
6265
+ }
6160
6266
  isRelayOpen() {
6161
6267
  return this.relaySocket?.readyState === WebSocket2.OPEN;
6162
6268
  }
@@ -9162,6 +9268,7 @@ function decidePairServiceAction(paired, service) {
9162
9268
  }
9163
9269
 
9164
9270
  // apps/bridge-cli/src/index.ts
9271
+ var HERMES_SERVICE_WATCHDOG_INTERVAL_MS = 3e4;
9165
9272
  async function main() {
9166
9273
  const [, , command = "help", ...args] = process.argv;
9167
9274
  const isServiceMode = hasFlag(args, "--service");
@@ -9339,6 +9446,9 @@ async function main() {
9339
9446
  instanceId: config.instanceId,
9340
9447
  serviceMode: isServiceMode
9341
9448
  });
9449
+ const hermesServiceWatchdog = isServiceMode ? startHermesServiceWatchdog((line) => {
9450
+ emitRuntimeLine(`[hermes-service] ${line}`);
9451
+ }) : null;
9342
9452
  if (isServiceMode) {
9343
9453
  await restoreHermesServiceRuntime((line) => {
9344
9454
  emitRuntimeLine(`[hermes-service] ${line}`);
@@ -9348,6 +9458,9 @@ async function main() {
9348
9458
  process.off("SIGINT", shutdown);
9349
9459
  process.off("SIGTERM", shutdown);
9350
9460
  await runtime.stop();
9461
+ if (hermesServiceWatchdog) {
9462
+ clearInterval(hermesServiceWatchdog);
9463
+ }
9351
9464
  unregisterRuntimeProcess(process.pid);
9352
9465
  if (isServiceMode) {
9353
9466
  clearServiceState(process.pid);
@@ -9820,6 +9933,8 @@ async function performHermesLocalPairing(args) {
9820
9933
  };
9821
9934
  }
9822
9935
  async function performHermesRelayPairing(args) {
9936
+ const pairingStartedAt = Date.now();
9937
+ logHermesPerf("pair_relay_begin");
9823
9938
  const server = resolvePairServer(args, "hermes");
9824
9939
  const name = readFlag(args, "--name") ?? readFlag(args, "-n") ?? "Hermes";
9825
9940
  const qrFile = readFlag(args, "--qr-file");
@@ -9827,6 +9942,10 @@ async function performHermesRelayPairing(args) {
9827
9942
  serverUrl: server,
9828
9943
  displayName: name
9829
9944
  });
9945
+ logHermesPerf("pair_relay_registered", {
9946
+ elapsedMs: Date.now() - pairingStartedAt,
9947
+ action: paired.action
9948
+ });
9830
9949
  const qrImagePath = await writeRawQrPng(paired.qrPayload, "clawket-hermes-relay-pair", qrFile);
9831
9950
  if (paired.action === "registered") {
9832
9951
  const stalePids = listHermesRelayRuntimePids2();
@@ -9835,6 +9954,9 @@ async function performHermesRelayPairing(args) {
9835
9954
  }
9836
9955
  }
9837
9956
  const runtimeMessage = await ensureHermesRelayBackgroundRuntime(args);
9957
+ logHermesPerf("pair_relay_ready", {
9958
+ elapsedMs: Date.now() - pairingStartedAt
9959
+ });
9838
9960
  return {
9839
9961
  backend: "hermes",
9840
9962
  transport: "relay",
@@ -10192,6 +10314,16 @@ async function keepHermesBridgeAlive(bridge) {
10192
10314
  });
10193
10315
  }
10194
10316
  async function restoreHermesServiceRuntime(log) {
10317
+ await restoreHermesServiceRuntimeInternal(log, { silentIfHealthy: false });
10318
+ }
10319
+ function startHermesServiceWatchdog(log) {
10320
+ const timer = setInterval(() => {
10321
+ void restoreHermesServiceRuntimeInternal(log, { silentIfHealthy: true });
10322
+ }, HERMES_SERVICE_WATCHDOG_INTERVAL_MS);
10323
+ timer.unref?.();
10324
+ return timer;
10325
+ }
10326
+ async function restoreHermesServiceRuntimeInternal(log, options) {
10195
10327
  const bridgeConfig = readHermesBridgeCliConfig();
10196
10328
  const relayConfig = readHermesRelayConfig();
10197
10329
  if (!bridgeConfig && !relayConfig) {
@@ -10206,9 +10338,11 @@ async function restoreHermesServiceRuntime(log) {
10206
10338
  config: bridgeConfig,
10207
10339
  replaceExisting: false
10208
10340
  });
10209
- log(
10210
- bridgePid == null ? "Hermes bridge runtime already running." : `Started Hermes bridge runtime (pid ${bridgePid}).`
10211
- );
10341
+ if (bridgePid != null || !options.silentIfHealthy) {
10342
+ log(
10343
+ bridgePid == null ? "Hermes bridge runtime already running." : `Started Hermes bridge runtime (pid ${bridgePid}).`
10344
+ );
10345
+ }
10212
10346
  } catch (error) {
10213
10347
  log(`Hermes bridge restore failed: ${formatError3(error)}`);
10214
10348
  return;
@@ -10222,9 +10356,11 @@ async function restoreHermesServiceRuntime(log) {
10222
10356
  config: bridgeConfig,
10223
10357
  replaceExisting: false
10224
10358
  });
10225
- log(
10226
- relayPid == null ? "Hermes relay runtime already running." : `Started Hermes relay runtime (pid ${relayPid}).`
10227
- );
10359
+ if (relayPid != null || !options.silentIfHealthy) {
10360
+ log(
10361
+ relayPid == null ? "Hermes relay runtime already running." : `Started Hermes relay runtime (pid ${relayPid}).`
10362
+ );
10363
+ }
10228
10364
  } catch (error) {
10229
10365
  log(`Hermes relay restore failed: ${formatError3(error)}`);
10230
10366
  }
@@ -10265,6 +10401,8 @@ async function keepHermesRelayRuntimeAlive(runtime) {
10265
10401
  });
10266
10402
  }
10267
10403
  async function ensureHermesRelayBackgroundRuntime(args) {
10404
+ const startedAt = Date.now();
10405
+ logHermesPerf("relay_runtime_ensure_begin");
10268
10406
  const relayConfig = readHermesRelayConfig();
10269
10407
  if (!relayConfig) {
10270
10408
  throw new Error("Hermes relay is not paired.");
@@ -10272,6 +10410,10 @@ async function ensureHermesRelayBackgroundRuntime(args) {
10272
10410
  const relayPids = listHermesRelayRuntimePids2();
10273
10411
  if (relayPids.length === 1) {
10274
10412
  await waitForHermesRelayCloudBridgeReady(relayConfig, 2e4);
10413
+ logHermesPerf("relay_runtime_ensure_reused", {
10414
+ elapsedMs: Date.now() - startedAt,
10415
+ pid: relayPids[0]
10416
+ });
10275
10417
  return `Hermes relay runtime already running (pid ${relayPids[0]}) and confirmed by relay.`;
10276
10418
  }
10277
10419
  if (relayPids.length > 1) {
@@ -10290,8 +10432,15 @@ async function ensureHermesRelayBackgroundRuntime(args) {
10290
10432
  await waitForHermesRelayCloudBridgeReady(relayConfig, 2e4);
10291
10433
  const startedPids = listHermesRelayRuntimePids2();
10292
10434
  if (startedPids.length === 0) {
10435
+ logHermesPerf("relay_runtime_ensure_requested_no_pid", {
10436
+ elapsedMs: Date.now() - startedAt
10437
+ });
10293
10438
  return "Hermes relay runtime launch was requested. Run `clawket hermes relay run` manually if it did not stay up.";
10294
10439
  }
10440
+ logHermesPerf("relay_runtime_ensure_started", {
10441
+ elapsedMs: Date.now() - startedAt,
10442
+ pid: startedPids[0]
10443
+ });
10295
10444
  return `Auto-started Hermes relay runtime (pid ${startedPids[0]}) and confirmed cloud bridge attachment.`;
10296
10445
  }
10297
10446
  async function ensureHermesBridgeBackgroundRuntime(input) {
@@ -10506,6 +10655,8 @@ function resolveRequestedPairBackend(args) {
10506
10655
  throw new Error(`Unsupported local pairing backend "${backend}". Use --backend openclaw or --backend hermes.`);
10507
10656
  }
10508
10657
  async function ensureHermesPairingRuntimeReady(args) {
10658
+ const startedAt = Date.now();
10659
+ logHermesPerf("pairing_runtime_ready_begin");
10509
10660
  const saved = readHermesBridgeCliConfig();
10510
10661
  const host = readFlag(args, "--host") ?? saved?.host ?? "0.0.0.0";
10511
10662
  const port = Number(readFlag(args, "--port") ?? saved?.port ?? "4319");
@@ -10513,7 +10664,12 @@ async function ensureHermesPairingRuntimeReady(args) {
10513
10664
  const token = readFlag(args, "--token") ?? process.env.CLAWKET_HERMES_BRIDGE_TOKEN ?? saved?.token ?? randomUUID5();
10514
10665
  const existingBridgePids = listHermesBridgeRuntimePids2();
10515
10666
  if (existingBridgePids.length > 0) {
10516
- return await resolveExistingHermesPairingRuntime(saved);
10667
+ const resolved = await resolveExistingHermesPairingRuntime(saved);
10668
+ logHermesPerf("pairing_runtime_ready_reused", {
10669
+ elapsedMs: Date.now() - startedAt,
10670
+ port: resolved.port
10671
+ });
10672
+ return resolved;
10517
10673
  }
10518
10674
  if (!existsSync7(resolveDefaultHermesSourcePath())) {
10519
10675
  throw new Error(
@@ -10528,6 +10684,10 @@ async function ensureHermesPairingRuntimeReady(args) {
10528
10684
  restartHermes: hasFlag(args, "--restart-hermes")
10529
10685
  });
10530
10686
  await waitForHermesBridgeHealth(port);
10687
+ logHermesPerf("pairing_runtime_ready_started", {
10688
+ elapsedMs: Date.now() - startedAt,
10689
+ port
10690
+ });
10531
10691
  return { host, port, apiBaseUrl, token };
10532
10692
  }
10533
10693
  async function resolveExistingHermesPairingRuntime(saved) {
@@ -10602,17 +10762,27 @@ function startDetachedHermesRelayRuntime(input) {
10602
10762
  child.unref();
10603
10763
  }
10604
10764
  async function waitForHermesBridgeHealth(port, timeoutMs = 15e3) {
10765
+ const startedAt = Date.now();
10605
10766
  const deadline = Date.now() + timeoutMs;
10606
10767
  while (Date.now() < deadline) {
10607
10768
  try {
10608
10769
  const health = await readHermesBridgeHealth2(port);
10609
10770
  if (health.ok && health.running) {
10771
+ logHermesPerf("bridge_health_ready", {
10772
+ elapsedMs: Date.now() - startedAt,
10773
+ port
10774
+ });
10610
10775
  return;
10611
10776
  }
10612
10777
  } catch {
10613
10778
  }
10614
10779
  await new Promise((resolve4) => setTimeout(resolve4, 250));
10615
10780
  }
10781
+ logHermesPerf("bridge_health_timeout", {
10782
+ elapsedMs: Date.now() - startedAt,
10783
+ port,
10784
+ timeoutMs
10785
+ });
10616
10786
  throw new Error(`Hermes bridge did not become ready at http://127.0.0.1:${port}/health within ${timeoutMs}ms.`);
10617
10787
  }
10618
10788
  async function readHermesBridgeHealth2(port) {
@@ -10664,6 +10834,7 @@ async function waitForHermesRelayRuntimeReady(startedAtMs, timeoutMs) {
10664
10834
  }
10665
10835
  }
10666
10836
  async function waitForHermesRelayCloudBridgeReady(relayConfig, timeoutMs) {
10837
+ const startedAt = Date.now();
10667
10838
  const statusUrl = buildHermesRelayBridgeStatusUrl2(relayConfig.relayUrl, relayConfig.bridgeId);
10668
10839
  const deadline = Date.now() + timeoutMs;
10669
10840
  let lastError = null;
@@ -10678,6 +10849,9 @@ async function waitForHermesRelayCloudBridgeReady(relayConfig, timeoutMs) {
10678
10849
  if (response.ok) {
10679
10850
  const payload = await response.json();
10680
10851
  if (payload?.hasBridge) {
10852
+ logHermesPerf("relay_cloud_bridge_ready", {
10853
+ elapsedMs: Date.now() - startedAt
10854
+ });
10681
10855
  return;
10682
10856
  }
10683
10857
  } else {
@@ -10688,6 +10862,10 @@ async function waitForHermesRelayCloudBridgeReady(relayConfig, timeoutMs) {
10688
10862
  }
10689
10863
  await sleep(500);
10690
10864
  }
10865
+ logHermesPerf("relay_cloud_bridge_timeout", {
10866
+ elapsedMs: Date.now() - startedAt,
10867
+ timeoutMs
10868
+ });
10691
10869
  throw new Error(
10692
10870
  `Hermes relay did not observe the local bridge for ${relayConfig.bridgeId} within ${timeoutMs}ms` + (lastError ? ` (${lastError}).` : ".")
10693
10871
  );
@@ -10768,6 +10946,11 @@ function formatLocalTime(iso) {
10768
10946
  function formatError3(error) {
10769
10947
  return error instanceof Error ? error.message : String(error);
10770
10948
  }
10949
+ function logHermesPerf(event, fields) {
10950
+ const payload = fields ? Object.entries(fields).filter(([, value]) => value !== void 0).map(([key, value]) => `${key}=${String(value)}`).join(" ") : "";
10951
+ void event;
10952
+ void payload;
10953
+ }
10771
10954
  async function followCliLogs(input) {
10772
10955
  const sources = getCliLogSourcePaths(input.includeErrorLog);
10773
10956
  const state = /* @__PURE__ */ new Map();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@p697/clawket",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",