@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.
- package/dist/index.js +199 -16
- 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;
|
|
6069
|
-
this.
|
|
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
|
-
|
|
10210
|
-
|
|
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
|
-
|
|
10226
|
-
|
|
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
|
-
|
|
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();
|