clawdentity 0.0.3 → 0.0.4

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/bin.js CHANGED
@@ -18495,9 +18495,9 @@ var createConfigCommand = () => {
18495
18495
 
18496
18496
  // src/commands/connector.ts
18497
18497
  import { execFile as execFileCallback } from "child_process";
18498
- import { mkdir as mkdir4, readFile as readFile3, rm, writeFile as writeFile4 } from "fs/promises";
18498
+ import { mkdir as mkdir4, readFile as readFile4, rm, writeFile as writeFile4 } from "fs/promises";
18499
18499
  import { homedir as homedir2 } from "os";
18500
- import { dirname as dirname3, join as join5 } from "path";
18500
+ import { dirname as dirname3, join as join6 } from "path";
18501
18501
  import { fileURLToPath } from "url";
18502
18502
  import { promisify } from "util";
18503
18503
 
@@ -19151,15 +19151,271 @@ import { mkdir as mkdir3, rename as rename2, writeFile as writeFile3 } from "fs/
19151
19151
  import {
19152
19152
  createServer
19153
19153
  } from "http";
19154
- import { dirname as dirname2, join as join4 } from "path";
19154
+ import { dirname as dirname2, join as join5 } from "path";
19155
19155
  import { WebSocket as NodeWebSocket } from "ws";
19156
+
19157
+ // ../../packages/connector/src/relay-echo.ts
19158
+ import { readFile as readFile3 } from "fs/promises";
19159
+ import { join as join4 } from "path";
19160
+ var OPENCLAW_RELAY_RUNTIME_CONFIG_FILENAME = "openclaw-relay.json";
19161
+ var PEERS_CONFIG_FILENAME = "peers.json";
19162
+ var ECHO_EMPTY_MESSAGE_FALLBACK = "(no message content)";
19163
+ var MIN_ECHO_MAX_LENGTH = 50;
19164
+ var MAX_ECHO_MAX_LENGTH = 2e3;
19165
+ var DEFAULT_RELAY_ECHO_ENABLED = true;
19166
+ var DEFAULT_RELAY_ECHO_CHANNEL = "last";
19167
+ var DEFAULT_RELAY_ECHO_MAX_LENGTH = 500;
19168
+ function isRecord4(value) {
19169
+ return typeof value === "object" && value !== null;
19170
+ }
19171
+ function parseAbsoluteHttpUrl(value, field) {
19172
+ if (typeof value !== "string" || value.trim().length === 0) {
19173
+ throw new Error(`${field} must be a non-empty string`);
19174
+ }
19175
+ let parsed;
19176
+ try {
19177
+ parsed = new URL(value.trim());
19178
+ } catch {
19179
+ throw new Error(`${field} must be a valid absolute URL`);
19180
+ }
19181
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
19182
+ throw new Error(`${field} must use http or https`);
19183
+ }
19184
+ if (parsed.pathname === "/" && parsed.search.length === 0 && parsed.hash.length === 0) {
19185
+ return parsed.origin;
19186
+ }
19187
+ return parsed.toString();
19188
+ }
19189
+ function parseBoolean(value, field) {
19190
+ if (typeof value !== "boolean") {
19191
+ throw new Error(`${field} must be a boolean`);
19192
+ }
19193
+ return value;
19194
+ }
19195
+ function parsePositiveInt(value, field, min, max) {
19196
+ const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : Number.NaN;
19197
+ if (!Number.isInteger(parsed) || parsed < min || parsed > max) {
19198
+ throw new Error(`${field} must be an integer between ${min} and ${max}`);
19199
+ }
19200
+ return parsed;
19201
+ }
19202
+ function parseOptionalString(value, field) {
19203
+ if (value === void 0) {
19204
+ return void 0;
19205
+ }
19206
+ if (typeof value !== "string") {
19207
+ throw new Error(`${field} must be a string`);
19208
+ }
19209
+ const trimmed = value.trim();
19210
+ if (trimmed.length === 0) {
19211
+ return void 0;
19212
+ }
19213
+ return trimmed;
19214
+ }
19215
+ function parseEchoConfig(value) {
19216
+ if (value === void 0) {
19217
+ return {
19218
+ enabled: DEFAULT_RELAY_ECHO_ENABLED,
19219
+ channel: DEFAULT_RELAY_ECHO_CHANNEL,
19220
+ maxLength: DEFAULT_RELAY_ECHO_MAX_LENGTH
19221
+ };
19222
+ }
19223
+ if (!isRecord4(value)) {
19224
+ throw new Error("echo config must be an object");
19225
+ }
19226
+ return {
19227
+ enabled: value.enabled === void 0 ? DEFAULT_RELAY_ECHO_ENABLED : parseBoolean(value.enabled, "echo.enabled"),
19228
+ channel: parseOptionalString(value.channel, "echo.channel") ?? DEFAULT_RELAY_ECHO_CHANNEL,
19229
+ to: parseOptionalString(value.to, "echo.to"),
19230
+ maxLength: value.maxLength === void 0 ? DEFAULT_RELAY_ECHO_MAX_LENGTH : parsePositiveInt(
19231
+ value.maxLength,
19232
+ "echo.maxLength",
19233
+ MIN_ECHO_MAX_LENGTH,
19234
+ MAX_ECHO_MAX_LENGTH
19235
+ )
19236
+ };
19237
+ }
19238
+ async function loadRelayRuntimeConfigFile(input) {
19239
+ const relayRuntimeConfigPath = join4(
19240
+ input.configDir,
19241
+ OPENCLAW_RELAY_RUNTIME_CONFIG_FILENAME
19242
+ );
19243
+ let raw;
19244
+ try {
19245
+ raw = await readFile3(relayRuntimeConfigPath, "utf8");
19246
+ } catch (error48) {
19247
+ if (typeof error48 === "object" && error48 !== null && "code" in error48 && error48.code === "ENOENT") {
19248
+ return void 0;
19249
+ }
19250
+ throw new Error(
19251
+ `Unable to read relay runtime config at ${relayRuntimeConfigPath}`
19252
+ );
19253
+ }
19254
+ let parsed;
19255
+ try {
19256
+ parsed = JSON.parse(raw);
19257
+ } catch {
19258
+ throw new Error(
19259
+ `Unable to parse relay runtime config at ${relayRuntimeConfigPath}`
19260
+ );
19261
+ }
19262
+ if (!isRecord4(parsed)) {
19263
+ throw new Error("Relay runtime config root must be a JSON object");
19264
+ }
19265
+ const config2 = parsed;
19266
+ const openclawBaseUrl = config2.openclawBaseUrl === void 0 ? void 0 : parseAbsoluteHttpUrl(config2.openclawBaseUrl, "openclawBaseUrl");
19267
+ return {
19268
+ openclawBaseUrl,
19269
+ echo: parseEchoConfig(config2.echo)
19270
+ };
19271
+ }
19272
+ function compactString(value) {
19273
+ return value.replaceAll(/\s+/g, " ").trim();
19274
+ }
19275
+ function toCompactJson(value) {
19276
+ try {
19277
+ const raw = JSON.stringify(value);
19278
+ if (typeof raw !== "string" || raw.length === 0) {
19279
+ return ECHO_EMPTY_MESSAGE_FALLBACK;
19280
+ }
19281
+ return raw;
19282
+ } catch {
19283
+ return ECHO_EMPTY_MESSAGE_FALLBACK;
19284
+ }
19285
+ }
19286
+ function truncateMessage(value, maxLength) {
19287
+ if (value.length <= maxLength) {
19288
+ return value;
19289
+ }
19290
+ return `${value.slice(0, maxLength)}...`;
19291
+ }
19292
+ function extractRelayMessageContent(payload) {
19293
+ if (typeof payload === "string") {
19294
+ const compact = compactString(payload);
19295
+ return compact.length > 0 ? compact : ECHO_EMPTY_MESSAGE_FALLBACK;
19296
+ }
19297
+ if (isRecord4(payload)) {
19298
+ for (const candidateKey of ["message", "text", "content"]) {
19299
+ const candidateValue = payload[candidateKey];
19300
+ if (typeof candidateValue === "string") {
19301
+ const compact = compactString(candidateValue);
19302
+ if (compact.length > 0) {
19303
+ return compact;
19304
+ }
19305
+ }
19306
+ }
19307
+ }
19308
+ return toCompactJson(payload);
19309
+ }
19310
+ function formatInboundRelayEcho(input) {
19311
+ const content = truncateMessage(
19312
+ extractRelayMessageContent(input.payload),
19313
+ input.maxLength
19314
+ );
19315
+ return `\u{1F517} [${input.fromLabel}]: ${content}`;
19316
+ }
19317
+ function formatOutboundRelayEcho(input) {
19318
+ const content = truncateMessage(
19319
+ extractRelayMessageContent(input.payload),
19320
+ input.maxLength
19321
+ );
19322
+ return `\u21A9\uFE0F Reply to [${input.toLabel}]: ${content}`;
19323
+ }
19324
+ async function loadPeerLabelsByDid(input) {
19325
+ const peersPath = join4(input.configDir, PEERS_CONFIG_FILENAME);
19326
+ let raw;
19327
+ try {
19328
+ raw = await readFile3(peersPath, "utf8");
19329
+ } catch (error48) {
19330
+ if (typeof error48 === "object" && error48 !== null && "code" in error48 && error48.code === "ENOENT") {
19331
+ return /* @__PURE__ */ new Map();
19332
+ }
19333
+ throw new Error(`Unable to read peers config at ${peersPath}`);
19334
+ }
19335
+ let parsed;
19336
+ try {
19337
+ parsed = JSON.parse(raw);
19338
+ } catch {
19339
+ throw new Error(`Unable to parse peers config at ${peersPath}`);
19340
+ }
19341
+ if (!isRecord4(parsed) || !isRecord4(parsed.peers)) {
19342
+ return /* @__PURE__ */ new Map();
19343
+ }
19344
+ const labels = /* @__PURE__ */ new Map();
19345
+ for (const [alias, entry] of Object.entries(parsed.peers)) {
19346
+ if (!isRecord4(entry) || typeof entry.did !== "string") {
19347
+ continue;
19348
+ }
19349
+ const did = entry.did.trim();
19350
+ if (did.length === 0) {
19351
+ continue;
19352
+ }
19353
+ const name = typeof entry.name === "string" && entry.name.trim().length > 0 ? entry.name.trim() : alias.trim();
19354
+ if (name.length === 0) {
19355
+ continue;
19356
+ }
19357
+ labels.set(did, name);
19358
+ }
19359
+ return labels;
19360
+ }
19361
+ function resolvePeerLabel(input) {
19362
+ if (input.peerDid !== void 0) {
19363
+ const byDid = input.peerLabelsByDid.get(input.peerDid);
19364
+ if (typeof byDid === "string" && byDid.length > 0) {
19365
+ return byDid;
19366
+ }
19367
+ }
19368
+ const fallback = input.fallbackLabel.trim();
19369
+ if (fallback.length > 0) {
19370
+ return fallback;
19371
+ }
19372
+ return input.peerDid?.trim() || "Unknown peer";
19373
+ }
19374
+ async function sendRelayEchoToOpenclaw(input) {
19375
+ if (!input.echoConfig.enabled) {
19376
+ return;
19377
+ }
19378
+ const payload = {
19379
+ message: input.message,
19380
+ name: "Clawdentity Relay",
19381
+ wakeMode: "now",
19382
+ deliver: true,
19383
+ channel: input.echoConfig.channel
19384
+ };
19385
+ if (input.echoConfig.to !== void 0) {
19386
+ payload.to = input.echoConfig.to;
19387
+ }
19388
+ const headers = {
19389
+ "content-type": "application/json"
19390
+ };
19391
+ if (input.requestId !== void 0 && input.requestId.trim().length > 0) {
19392
+ headers["x-request-id"] = input.requestId.trim();
19393
+ }
19394
+ if (input.hookToken !== void 0 && input.hookToken.trim().length > 0) {
19395
+ headers["x-openclaw-token"] = input.hookToken.trim();
19396
+ }
19397
+ const response = await input.fetchImpl(input.endpoint, {
19398
+ method: "POST",
19399
+ headers,
19400
+ body: JSON.stringify(payload)
19401
+ });
19402
+ if (!response.ok) {
19403
+ input.logger.warn("connector.relay_echo.rejected", {
19404
+ status: response.status,
19405
+ requestId: input.requestId
19406
+ });
19407
+ throw new Error(`Relay echo rejected with status ${response.status}`);
19408
+ }
19409
+ }
19410
+
19411
+ // ../../packages/connector/src/runtime.ts
19156
19412
  var REGISTRY_AUTH_FILENAME = "registry-auth.json";
19157
19413
  var AGENTS_DIR_NAME2 = "agents";
19158
19414
  var REFRESH_SINGLE_FLIGHT_PREFIX = "connector-runtime";
19159
19415
  var NONCE_SIZE = 16;
19160
19416
  var MAX_OUTBOUND_BODY_BYTES = 1024 * 1024;
19161
19417
  var ACCESS_TOKEN_REFRESH_SKEW_MS = 3e4;
19162
- function isRecord4(value) {
19418
+ function isRecord5(value) {
19163
19419
  return typeof value === "object" && value !== null;
19164
19420
  }
19165
19421
  function toPathWithQuery2(url2) {
@@ -19209,9 +19465,38 @@ function normalizeWebSocketUrl(urlInput) {
19209
19465
  }
19210
19466
  return parsed.toString();
19211
19467
  }
19212
- function resolveOpenclawBaseUrl(input) {
19213
- const value = input?.trim() || process.env.OPENCLAW_BASE_URL?.trim() || DEFAULT_OPENCLAW_BASE_URL;
19214
- return value;
19468
+ function parseOpenclawBaseUrl(value) {
19469
+ let parsed;
19470
+ try {
19471
+ parsed = new URL(value);
19472
+ } catch {
19473
+ throw new Error("OpenClaw base URL is invalid");
19474
+ }
19475
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
19476
+ throw new Error("OpenClaw base URL is invalid");
19477
+ }
19478
+ if (parsed.pathname === "/" && parsed.search.length === 0 && parsed.hash.length === 0) {
19479
+ return parsed.origin;
19480
+ }
19481
+ return parsed.toString();
19482
+ }
19483
+ async function resolveOpenclawRuntimeSettings(input) {
19484
+ const fileConfig = await loadRelayRuntimeConfigFile({
19485
+ configDir: input.configDir
19486
+ });
19487
+ const baseUrlFromOption = input.openclawBaseUrlOption?.trim();
19488
+ const baseUrlFromEnv = process.env.OPENCLAW_BASE_URL?.trim();
19489
+ const openclawBaseUrl = parseOpenclawBaseUrl(
19490
+ baseUrlFromOption || baseUrlFromEnv || fileConfig?.openclawBaseUrl || DEFAULT_OPENCLAW_BASE_URL
19491
+ );
19492
+ return {
19493
+ openclawBaseUrl,
19494
+ echoConfig: fileConfig?.echo ?? {
19495
+ enabled: DEFAULT_RELAY_ECHO_ENABLED,
19496
+ channel: DEFAULT_RELAY_ECHO_CHANNEL,
19497
+ maxLength: DEFAULT_RELAY_ECHO_MAX_LENGTH
19498
+ }
19499
+ };
19215
19500
  }
19216
19501
  function resolveOpenclawHookPath(input) {
19217
19502
  const value = input?.trim() || process.env.OPENCLAW_HOOK_PATH?.trim() || DEFAULT_OPENCLAW_HOOK_PATH;
@@ -19251,7 +19536,7 @@ function shouldRefreshAccessToken(auth, nowMs) {
19251
19536
  return expiresAtMs <= nowMs + ACCESS_TOKEN_REFRESH_SKEW_MS;
19252
19537
  }
19253
19538
  function parseOutboundRelayRequest(payload) {
19254
- if (!isRecord4(payload)) {
19539
+ if (!isRecord5(payload)) {
19255
19540
  throw new AppError({
19256
19541
  code: "CONNECTOR_OUTBOUND_INVALID_REQUEST",
19257
19542
  message: "Outbound relay request must be an object",
@@ -19309,7 +19594,7 @@ function createWebSocketFactory() {
19309
19594
  };
19310
19595
  }
19311
19596
  async function writeRegistryAuthAtomic(input) {
19312
- const targetPath = join4(
19597
+ const targetPath = join5(
19313
19598
  input.configDir,
19314
19599
  AGENTS_DIR_NAME2,
19315
19600
  input.agentName,
@@ -19406,14 +19691,63 @@ async function startConnectorRuntime(input) {
19406
19691
  accessToken: currentAuth.accessToken,
19407
19692
  secretKey
19408
19693
  });
19694
+ const openclawRuntimeSettings = await resolveOpenclawRuntimeSettings({
19695
+ configDir: input.configDir,
19696
+ openclawBaseUrlOption: input.openclawBaseUrl
19697
+ });
19698
+ const peerLabelsByDid = await loadPeerLabelsByDid({
19699
+ configDir: input.configDir
19700
+ });
19701
+ const openclawHookPath = resolveOpenclawHookPath(input.openclawHookPath);
19702
+ const openclawHookToken = resolveOpenclawHookToken(input.openclawHookToken);
19703
+ const openclawHookUrl = new URL(
19704
+ openclawHookPath,
19705
+ openclawRuntimeSettings.openclawBaseUrl
19706
+ ).toString();
19707
+ const queueRelayEcho = (echoInput) => {
19708
+ void sendRelayEchoToOpenclaw({
19709
+ endpoint: openclawHookUrl,
19710
+ echoConfig: openclawRuntimeSettings.echoConfig,
19711
+ fetchImpl,
19712
+ hookToken: openclawHookToken,
19713
+ logger: logger12,
19714
+ message: echoInput.message,
19715
+ requestId: echoInput.requestId
19716
+ }).catch((error48) => {
19717
+ logger12.warn("connector.relay_echo.failed", {
19718
+ direction: echoInput.direction,
19719
+ errorName: error48 instanceof Error ? error48.name : "unknown",
19720
+ requestId: echoInput.requestId
19721
+ });
19722
+ });
19723
+ };
19409
19724
  const connectorClient = new ConnectorClient({
19410
19725
  connectorUrl: wsParsed.toString(),
19411
19726
  connectionHeaders: upgradeHeaders,
19412
- openclawBaseUrl: resolveOpenclawBaseUrl(input.openclawBaseUrl),
19413
- openclawHookPath: resolveOpenclawHookPath(input.openclawHookPath),
19414
- openclawHookToken: resolveOpenclawHookToken(input.openclawHookToken),
19727
+ openclawBaseUrl: openclawRuntimeSettings.openclawBaseUrl,
19728
+ openclawHookPath,
19729
+ openclawHookToken,
19415
19730
  fetchImpl,
19416
19731
  logger: logger12,
19732
+ hooks: {
19733
+ onDeliverSucceeded: (frame) => {
19734
+ const fromLabel = resolvePeerLabel({
19735
+ peerLabelsByDid,
19736
+ peerDid: frame.fromAgentDid,
19737
+ fallbackLabel: frame.fromAgentDid
19738
+ });
19739
+ const echoMessage = formatInboundRelayEcho({
19740
+ fromLabel,
19741
+ payload: frame.payload,
19742
+ maxLength: openclawRuntimeSettings.echoConfig.maxLength
19743
+ });
19744
+ queueRelayEcho({
19745
+ direction: "inbound",
19746
+ message: echoMessage,
19747
+ requestId: frame.id
19748
+ });
19749
+ }
19750
+ },
19417
19751
  webSocketFactory: createWebSocketFactory()
19418
19752
  });
19419
19753
  const outboundBaseUrl = normalizeOutboundBaseUrl(input.outboundBaseUrl);
@@ -19499,6 +19833,22 @@ async function startConnectorRuntime(input) {
19499
19833
  const requestBody = await readRequestJson(req);
19500
19834
  const relayRequest = parseOutboundRelayRequest(requestBody);
19501
19835
  await relayToPeer(relayRequest);
19836
+ const toLabel = resolvePeerLabel({
19837
+ peerLabelsByDid,
19838
+ peerDid: relayRequest.peerDid,
19839
+ fallbackLabel: relayRequest.peer
19840
+ });
19841
+ const requestId = typeof req.headers["x-request-id"] === "string" ? req.headers["x-request-id"] : void 0;
19842
+ const echoMessage = formatOutboundRelayEcho({
19843
+ toLabel,
19844
+ payload: relayRequest.payload,
19845
+ maxLength: openclawRuntimeSettings.echoConfig.maxLength
19846
+ });
19847
+ queueRelayEcho({
19848
+ direction: "outbound",
19849
+ message: echoMessage,
19850
+ requestId
19851
+ });
19502
19852
  writeJson(res, 202, { accepted: true, peer: relayRequest.peer });
19503
19853
  } catch (error48) {
19504
19854
  if (error48 instanceof AppError) {
@@ -19580,11 +19930,11 @@ var REGISTRY_AUTH_FILE_NAME2 = "registry-auth.json";
19580
19930
  var SERVICE_LOG_DIR_NAME = "logs";
19581
19931
  var DEFAULT_CONNECTOR_BASE_URL2 = "http://127.0.0.1:19400";
19582
19932
  var DEFAULT_CONNECTOR_OUTBOUND_PATH2 = "/v1/outbound";
19583
- function isRecord5(value) {
19933
+ function isRecord6(value) {
19584
19934
  return typeof value === "object" && value !== null;
19585
19935
  }
19586
19936
  function getErrorCode(error48) {
19587
- if (!isRecord5(error48)) {
19937
+ if (!isRecord6(error48)) {
19588
19938
  return void 0;
19589
19939
  }
19590
19940
  return typeof error48.code === "string" ? error48.code : void 0;
@@ -19708,7 +20058,7 @@ function parseJsonRecord(value, code, message2) {
19708
20058
  } catch {
19709
20059
  throw createCliError3(code, message2);
19710
20060
  }
19711
- if (!isRecord5(parsed)) {
20061
+ if (!isRecord6(parsed)) {
19712
20062
  throw createCliError3(code, message2);
19713
20063
  }
19714
20064
  return parsed;
@@ -19748,7 +20098,7 @@ async function loadDefaultConnectorModule() {
19748
20098
  };
19749
20099
  }
19750
20100
  function resolveWaitPromise(runtime) {
19751
- if (!runtime || !isRecord5(runtime)) {
20101
+ if (!runtime || !isRecord6(runtime)) {
19752
20102
  return void 0;
19753
20103
  }
19754
20104
  if (typeof runtime.waitUntilStopped === "function") {
@@ -19813,7 +20163,7 @@ function buildConnectorStartArgs(agentName, commandOptions) {
19813
20163
  }
19814
20164
  function resolveCliEntryPath(resolveCurrentModulePathImpl) {
19815
20165
  const modulePath = resolveCurrentModulePathImpl?.() ?? fileURLToPath(import.meta.url);
19816
- return join5(dirname3(modulePath), "..", "bin.js");
20166
+ return join6(dirname3(modulePath), "..", "bin.js");
19817
20167
  }
19818
20168
  function escapeXml(value) {
19819
20169
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
@@ -19911,7 +20261,7 @@ async function installConnectorServiceForAgent(agentName, commandOptions = {}, d
19911
20261
  );
19912
20262
  const configDir = serviceDependencies.getConfigDirImpl();
19913
20263
  const homeDir = serviceDependencies.getHomeDirImpl();
19914
- const logsDir = join5(configDir, SERVICE_LOG_DIR_NAME);
20264
+ const logsDir = join6(configDir, SERVICE_LOG_DIR_NAME);
19915
20265
  const serviceName = sanitizeServiceSegment(
19916
20266
  `clawdentity-connector-${agentName}`
19917
20267
  );
@@ -19921,12 +20271,12 @@ async function installConnectorServiceForAgent(agentName, commandOptions = {}, d
19921
20271
  resolveCliEntryPath(serviceDependencies.resolveCurrentModulePathImpl),
19922
20272
  ...startArgs
19923
20273
  ];
19924
- const outputLogPath = join5(logsDir, `${serviceName}.out.log`);
19925
- const errorLogPath = join5(logsDir, `${serviceName}.err.log`);
20274
+ const outputLogPath = join6(logsDir, `${serviceName}.out.log`);
20275
+ const errorLogPath = join6(logsDir, `${serviceName}.err.log`);
19926
20276
  await serviceDependencies.mkdirImpl(logsDir, { recursive: true });
19927
20277
  if (platform === "systemd") {
19928
- const serviceDir = join5(homeDir, ".config", "systemd", "user");
19929
- const serviceFilePath2 = join5(serviceDir, `${serviceName}.service`);
20278
+ const serviceDir = join6(homeDir, ".config", "systemd", "user");
20279
+ const serviceFilePath2 = join6(serviceDir, `${serviceName}.service`);
19930
20280
  await serviceDependencies.mkdirImpl(serviceDir, { recursive: true });
19931
20281
  await serviceDependencies.writeFileImpl(
19932
20282
  serviceFilePath2,
@@ -19965,9 +20315,9 @@ async function installConnectorServiceForAgent(agentName, commandOptions = {}, d
19965
20315
  serviceFilePath: serviceFilePath2
19966
20316
  };
19967
20317
  }
19968
- const launchAgentsDir = join5(homeDir, "Library", "LaunchAgents");
20318
+ const launchAgentsDir = join6(homeDir, "Library", "LaunchAgents");
19969
20319
  const serviceNameWithDomain = `com.clawdentity.${serviceName}`;
19970
- const serviceFilePath = join5(
20320
+ const serviceFilePath = join6(
19971
20321
  launchAgentsDir,
19972
20322
  `${serviceNameWithDomain}.plist`
19973
20323
  );
@@ -20026,7 +20376,7 @@ async function uninstallConnectorServiceForAgent(agentName, commandOptions = {},
20026
20376
  `clawdentity-connector-${agentName}`
20027
20377
  );
20028
20378
  if (platform === "systemd") {
20029
- const serviceFilePath2 = join5(
20379
+ const serviceFilePath2 = join6(
20030
20380
  homeDir,
20031
20381
  ".config",
20032
20382
  "systemd",
@@ -20057,7 +20407,7 @@ async function uninstallConnectorServiceForAgent(agentName, commandOptions = {},
20057
20407
  };
20058
20408
  }
20059
20409
  const serviceNameWithDomain = `com.clawdentity.${serviceName}`;
20060
- const serviceFilePath = join5(
20410
+ const serviceFilePath = join6(
20061
20411
  homeDir,
20062
20412
  "Library",
20063
20413
  "LaunchAgents",
@@ -20081,10 +20431,10 @@ async function uninstallConnectorServiceForAgent(agentName, commandOptions = {},
20081
20431
  async function startConnectorForAgent(agentName, commandOptions = {}, dependencies = {}) {
20082
20432
  const resolveConfigImpl = dependencies.resolveConfigImpl ?? resolveConfig;
20083
20433
  const getConfigDirImpl = dependencies.getConfigDirImpl ?? getConfigDir;
20084
- const readFileImpl = dependencies.readFileImpl ?? ((path, encoding) => readFile3(path, encoding));
20434
+ const readFileImpl = dependencies.readFileImpl ?? ((path, encoding) => readFile4(path, encoding));
20085
20435
  const loadConnectorModule = dependencies.loadConnectorModule ?? loadDefaultConnectorModule;
20086
20436
  const configDir = getConfigDirImpl();
20087
- const agentDirectory = join5(configDir, AGENTS_DIR_NAME3, agentName);
20437
+ const agentDirectory = join6(configDir, AGENTS_DIR_NAME3, agentName);
20088
20438
  const [
20089
20439
  rawAit,
20090
20440
  rawSecretKey,
@@ -20094,22 +20444,22 @@ async function startConnectorForAgent(agentName, commandOptions = {}, dependenci
20094
20444
  connectorModule
20095
20445
  ] = await Promise.all([
20096
20446
  readRequiredTrimmedFile(
20097
- join5(agentDirectory, AIT_FILE_NAME2),
20447
+ join6(agentDirectory, AIT_FILE_NAME2),
20098
20448
  AIT_FILE_NAME2,
20099
20449
  readFileImpl
20100
20450
  ),
20101
20451
  readRequiredTrimmedFile(
20102
- join5(agentDirectory, SECRET_KEY_FILE_NAME),
20452
+ join6(agentDirectory, SECRET_KEY_FILE_NAME),
20103
20453
  SECRET_KEY_FILE_NAME,
20104
20454
  readFileImpl
20105
20455
  ),
20106
20456
  readRequiredTrimmedFile(
20107
- join5(agentDirectory, IDENTITY_FILE_NAME2),
20457
+ join6(agentDirectory, IDENTITY_FILE_NAME2),
20108
20458
  IDENTITY_FILE_NAME2,
20109
20459
  readFileImpl
20110
20460
  ),
20111
20461
  readRequiredTrimmedFile(
20112
- join5(agentDirectory, REGISTRY_AUTH_FILE_NAME2),
20462
+ join6(agentDirectory, REGISTRY_AUTH_FILE_NAME2),
20113
20463
  REGISTRY_AUTH_FILE_NAME2,
20114
20464
  readFileImpl
20115
20465
  ),
@@ -20147,8 +20497,8 @@ async function startConnectorForAgent(agentName, commandOptions = {}, dependenci
20147
20497
  tokenType: registryAuth.tokenType
20148
20498
  }
20149
20499
  });
20150
- const outboundUrl = runtime && isRecord5(runtime) && typeof runtime.outboundUrl === "string" ? runtime.outboundUrl : resolveOutboundUrl(outboundBaseUrl, outboundPath);
20151
- const proxyWebsocketUrl = runtime && isRecord5(runtime) ? typeof runtime.websocketUrl === "string" ? runtime.websocketUrl : typeof runtime.proxyWebsocketUrl === "string" ? runtime.proxyWebsocketUrl : void 0 : void 0;
20500
+ const outboundUrl = runtime && isRecord6(runtime) && typeof runtime.outboundUrl === "string" ? runtime.outboundUrl : resolveOutboundUrl(outboundBaseUrl, outboundPath);
20501
+ const proxyWebsocketUrl = runtime && isRecord6(runtime) ? typeof runtime.websocketUrl === "string" ? runtime.websocketUrl : typeof runtime.proxyWebsocketUrl === "string" ? runtime.proxyWebsocketUrl : void 0 : void 0;
20152
20502
  return {
20153
20503
  outboundUrl,
20154
20504
  proxyWebsocketUrl,
@@ -20279,7 +20629,7 @@ function createConnectorCommand(dependencies = {}) {
20279
20629
  // src/commands/invite.ts
20280
20630
  import { Command as Command6 } from "commander";
20281
20631
  var logger7 = createLogger({ service: "cli", module: "invite" });
20282
- var isRecord6 = (value) => {
20632
+ var isRecord7 = (value) => {
20283
20633
  return typeof value === "object" && value !== null;
20284
20634
  };
20285
20635
  function parseNonEmptyString6(value) {
@@ -20320,7 +20670,7 @@ function toRegistryRequestUrl(registryUrl, path) {
20320
20670
  return new URL(path.slice(1), normalizedBaseUrl).toString();
20321
20671
  }
20322
20672
  function extractRegistryErrorCode(payload) {
20323
- if (!isRecord6(payload)) {
20673
+ if (!isRecord7(payload)) {
20324
20674
  return void 0;
20325
20675
  }
20326
20676
  const envelope = payload;
@@ -20331,7 +20681,7 @@ function extractRegistryErrorCode(payload) {
20331
20681
  return trimmed.length > 0 ? trimmed : void 0;
20332
20682
  }
20333
20683
  function extractRegistryErrorMessage3(payload) {
20334
- if (!isRecord6(payload)) {
20684
+ if (!isRecord7(payload)) {
20335
20685
  return void 0;
20336
20686
  }
20337
20687
  const envelope = payload;
@@ -20405,13 +20755,13 @@ function mapRedeemInviteError(status, payload) {
20405
20755
  return `Invite redeem failed (${status})`;
20406
20756
  }
20407
20757
  function parseInviteRecord(payload) {
20408
- if (!isRecord6(payload)) {
20758
+ if (!isRecord7(payload)) {
20409
20759
  throw createCliError4(
20410
20760
  "CLI_INVITE_CREATE_INVALID_RESPONSE",
20411
20761
  "Invite response is invalid"
20412
20762
  );
20413
20763
  }
20414
- const source = isRecord6(payload.invite) ? payload.invite : payload;
20764
+ const source = isRecord7(payload.invite) ? payload.invite : payload;
20415
20765
  const code = parseNonEmptyString6(source.code);
20416
20766
  if (code.length === 0) {
20417
20767
  throw createCliError4(
@@ -20436,15 +20786,15 @@ function parseInviteRecord(payload) {
20436
20786
  return invite;
20437
20787
  }
20438
20788
  function parseInviteRedeemResponse(payload) {
20439
- if (!isRecord6(payload)) {
20789
+ if (!isRecord7(payload)) {
20440
20790
  throw createCliError4(
20441
20791
  "CLI_INVITE_REDEEM_INVALID_RESPONSE",
20442
20792
  "Invite redeem response is invalid"
20443
20793
  );
20444
20794
  }
20445
- const apiKeySource = isRecord6(payload.apiKey) ? payload.apiKey : payload;
20795
+ const apiKeySource = isRecord7(payload.apiKey) ? payload.apiKey : payload;
20446
20796
  const apiKeyToken = parseNonEmptyString6(
20447
- isRecord6(payload.apiKey) ? payload.apiKey.token : payload.token
20797
+ isRecord7(payload.apiKey) ? payload.apiKey.token : payload.token
20448
20798
  );
20449
20799
  if (apiKeyToken.length === 0) {
20450
20800
  throw createCliError4(
@@ -20602,9 +20952,9 @@ var createInviteCommand = (dependencies = {}) => {
20602
20952
  };
20603
20953
 
20604
20954
  // src/commands/openclaw.ts
20605
- import { chmod as chmod3, copyFile, mkdir as mkdir5, readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
20955
+ import { chmod as chmod3, copyFile, mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
20606
20956
  import { homedir as homedir3 } from "os";
20607
- import { dirname as dirname4, join as join6 } from "path";
20957
+ import { dirname as dirname4, join as join7 } from "path";
20608
20958
  import { Command as Command7 } from "commander";
20609
20959
  var logger8 = createLogger({ service: "cli", module: "openclaw" });
20610
20960
  var CLAWDENTITY_DIR_NAME = ".clawdentity";
@@ -20622,12 +20972,17 @@ var HOOK_MAPPING_ID = "clawdentity-send-to-peer";
20622
20972
  var HOOK_PATH_SEND_TO_PEER = "send-to-peer";
20623
20973
  var OPENCLAW_SEND_TO_PEER_HOOK_PATH = "hooks/send-to-peer";
20624
20974
  var DEFAULT_OPENCLAW_BASE_URL2 = "http://127.0.0.1:18789";
20975
+ var DEFAULT_RELAY_ECHO_ENABLED2 = true;
20976
+ var DEFAULT_RELAY_ECHO_CHANNEL2 = "last";
20977
+ var DEFAULT_RELAY_ECHO_MAX_LENGTH2 = 500;
20978
+ var MIN_RELAY_ECHO_MAX_LENGTH = 50;
20979
+ var MAX_RELAY_ECHO_MAX_LENGTH = 2e3;
20625
20980
  var INVITE_CODE_PREFIX = "clawd1_";
20626
20981
  var PEER_ALIAS_PATTERN = /^[a-zA-Z0-9._-]+$/;
20627
20982
  var FILE_MODE3 = 384;
20628
20983
  var textEncoder2 = new TextEncoder();
20629
20984
  var textDecoder = new TextDecoder();
20630
- function isRecord7(value) {
20985
+ function isRecord8(value) {
20631
20986
  return typeof value === "object" && value !== null;
20632
20987
  }
20633
20988
  function createCliError5(code, message2, details) {
@@ -20639,7 +20994,7 @@ function createCliError5(code, message2, details) {
20639
20994
  });
20640
20995
  }
20641
20996
  function getErrorCode2(error48) {
20642
- if (!isRecord7(error48)) {
20997
+ if (!isRecord8(error48)) {
20643
20998
  return void 0;
20644
20999
  }
20645
21000
  return typeof error48.code === "string" ? error48.code : void 0;
@@ -20709,13 +21064,82 @@ function parseHttpUrl(value, input) {
20709
21064
  }
20710
21065
  return parsedUrl.toString();
20711
21066
  }
20712
- function parseOpenclawBaseUrl(value) {
21067
+ function parseOpenclawBaseUrl2(value) {
20713
21068
  return parseHttpUrl(value, {
20714
21069
  label: "OpenClaw base URL",
20715
21070
  code: "CLI_OPENCLAW_INVALID_OPENCLAW_BASE_URL",
20716
21071
  message: "OpenClaw base URL must be a valid URL"
20717
21072
  });
20718
21073
  }
21074
+ function parseBooleanValue(value, label) {
21075
+ if (typeof value === "boolean") {
21076
+ return value;
21077
+ }
21078
+ if (typeof value === "string") {
21079
+ const normalized = value.trim().toLowerCase();
21080
+ if (normalized === "true" || normalized === "1" || normalized === "yes" || normalized === "on") {
21081
+ return true;
21082
+ }
21083
+ if (normalized === "false" || normalized === "0" || normalized === "no" || normalized === "off") {
21084
+ return false;
21085
+ }
21086
+ }
21087
+ throw createCliError5(
21088
+ "CLI_OPENCLAW_INVALID_INPUT",
21089
+ `${label} must be true or false`,
21090
+ { label }
21091
+ );
21092
+ }
21093
+ function parseRelayEchoChannel(value) {
21094
+ return parseNonEmptyString7(value, "relay echo channel");
21095
+ }
21096
+ function parseRelayEchoMaxLength(value) {
21097
+ const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : Number.NaN;
21098
+ if (!Number.isInteger(parsed) || parsed < MIN_RELAY_ECHO_MAX_LENGTH || parsed > MAX_RELAY_ECHO_MAX_LENGTH) {
21099
+ throw createCliError5(
21100
+ "CLI_OPENCLAW_INVALID_ECHO_MAX_LENGTH",
21101
+ `echo max length must be an integer between ${MIN_RELAY_ECHO_MAX_LENGTH} and ${MAX_RELAY_ECHO_MAX_LENGTH}`
21102
+ );
21103
+ }
21104
+ return parsed;
21105
+ }
21106
+ function parseRelayEchoEnabled(value) {
21107
+ return parseBooleanValue(value, "relay echo enabled");
21108
+ }
21109
+ function parseRelayEchoTo(value) {
21110
+ if (value === void 0) {
21111
+ return void 0;
21112
+ }
21113
+ if (typeof value !== "string") {
21114
+ throw createCliError5(
21115
+ "CLI_OPENCLAW_INVALID_INPUT",
21116
+ "relay echo target must be a string",
21117
+ { label: "relay echo target" }
21118
+ );
21119
+ }
21120
+ const trimmed = value.trim();
21121
+ if (trimmed.length === 0) {
21122
+ return void 0;
21123
+ }
21124
+ return trimmed;
21125
+ }
21126
+ function defaultRelayEchoConfig() {
21127
+ return {
21128
+ enabled: DEFAULT_RELAY_ECHO_ENABLED2,
21129
+ channel: DEFAULT_RELAY_ECHO_CHANNEL2,
21130
+ maxLength: DEFAULT_RELAY_ECHO_MAX_LENGTH2
21131
+ };
21132
+ }
21133
+ function resolveSetupRelayEchoConfig(input) {
21134
+ const current = input.existingConfig?.echo ?? defaultRelayEchoConfig();
21135
+ const options = input.options;
21136
+ return {
21137
+ enabled: options.echoEnabled === void 0 ? current.enabled : parseRelayEchoEnabled(options.echoEnabled),
21138
+ channel: options.echoChannel === void 0 ? current.channel : parseRelayEchoChannel(options.echoChannel),
21139
+ to: options.echoTo === void 0 ? current.to : parseRelayEchoTo(options.echoTo),
21140
+ maxLength: options.echoMaxLength === void 0 ? current.maxLength : parseRelayEchoMaxLength(options.echoMaxLength)
21141
+ };
21142
+ }
20719
21143
  function parseAgentDid2(value, label) {
20720
21144
  const did = parseNonEmptyString7(value, label);
20721
21145
  try {
@@ -20734,7 +21158,7 @@ function parseAgentDid2(value, label) {
20734
21158
  return did;
20735
21159
  }
20736
21160
  function parseInvitePayload(value) {
20737
- if (!isRecord7(value)) {
21161
+ if (!isRecord8(value)) {
20738
21162
  throw createCliError5(
20739
21163
  "CLI_OPENCLAW_INVALID_INVITE",
20740
21164
  "invite payload must be an object"
@@ -20787,19 +21211,19 @@ function resolveOpenclawDir(openclawDir, homeDir) {
20787
21211
  if (typeof openclawDir === "string" && openclawDir.trim().length > 0) {
20788
21212
  return openclawDir.trim();
20789
21213
  }
20790
- return join6(homeDir, OPENCLAW_DIR_NAME);
21214
+ return join7(homeDir, OPENCLAW_DIR_NAME);
20791
21215
  }
20792
21216
  function resolveAgentDirectory(homeDir, agentName) {
20793
- return join6(homeDir, CLAWDENTITY_DIR_NAME, AGENTS_DIR_NAME4, agentName);
21217
+ return join7(homeDir, CLAWDENTITY_DIR_NAME, AGENTS_DIR_NAME4, agentName);
20794
21218
  }
20795
21219
  function resolvePeersPath(homeDir) {
20796
- return join6(homeDir, CLAWDENTITY_DIR_NAME, PEERS_FILE_NAME);
21220
+ return join7(homeDir, CLAWDENTITY_DIR_NAME, PEERS_FILE_NAME);
20797
21221
  }
20798
21222
  function resolveOpenclawConfigPath(openclawDir) {
20799
- return join6(openclawDir, OPENCLAW_CONFIG_FILE_NAME);
21223
+ return join7(openclawDir, OPENCLAW_CONFIG_FILE_NAME);
20800
21224
  }
20801
21225
  function resolveDefaultTransformSource(openclawDir) {
20802
- return join6(
21226
+ return join7(
20803
21227
  openclawDir,
20804
21228
  "workspace",
20805
21229
  "skills",
@@ -20808,16 +21232,16 @@ function resolveDefaultTransformSource(openclawDir) {
20808
21232
  );
20809
21233
  }
20810
21234
  function resolveTransformTargetPath(openclawDir) {
20811
- return join6(openclawDir, "hooks", "transforms", RELAY_MODULE_FILE_NAME);
21235
+ return join7(openclawDir, "hooks", "transforms", RELAY_MODULE_FILE_NAME);
20812
21236
  }
20813
21237
  function resolveOpenclawAgentNamePath(homeDir) {
20814
- return join6(homeDir, CLAWDENTITY_DIR_NAME, OPENCLAW_AGENT_FILE_NAME);
21238
+ return join7(homeDir, CLAWDENTITY_DIR_NAME, OPENCLAW_AGENT_FILE_NAME);
20815
21239
  }
20816
21240
  function resolveRelayRuntimeConfigPath(homeDir) {
20817
- return join6(homeDir, CLAWDENTITY_DIR_NAME, OPENCLAW_RELAY_RUNTIME_FILE_NAME);
21241
+ return join7(homeDir, CLAWDENTITY_DIR_NAME, OPENCLAW_RELAY_RUNTIME_FILE_NAME);
20818
21242
  }
20819
21243
  async function readJsonFile(filePath) {
20820
- const raw = await readFile4(filePath, "utf8");
21244
+ const raw = await readFile5(filePath, "utf8");
20821
21245
  try {
20822
21246
  return JSON.parse(raw);
20823
21247
  } catch {
@@ -20829,13 +21253,13 @@ async function readJsonFile(filePath) {
20829
21253
  async function ensureLocalAgentCredentials(homeDir, agentName) {
20830
21254
  const agentDir = resolveAgentDirectory(homeDir, agentName);
20831
21255
  const requiredFiles = [
20832
- join6(agentDir, SECRET_KEY_FILE_NAME2),
20833
- join6(agentDir, AIT_FILE_NAME3)
21256
+ join7(agentDir, SECRET_KEY_FILE_NAME2),
21257
+ join7(agentDir, AIT_FILE_NAME3)
20834
21258
  ];
20835
21259
  for (const filePath of requiredFiles) {
20836
21260
  let content;
20837
21261
  try {
20838
- content = await readFile4(filePath, "utf8");
21262
+ content = await readFile5(filePath, "utf8");
20839
21263
  } catch (error48) {
20840
21264
  if (getErrorCode2(error48) === "ENOENT") {
20841
21265
  throw createCliError5(
@@ -20909,7 +21333,7 @@ async function loadPeersConfig(peersPath) {
20909
21333
  }
20910
21334
  throw error48;
20911
21335
  }
20912
- if (!isRecord7(parsed)) {
21336
+ if (!isRecord8(parsed)) {
20913
21337
  throw createCliError5(
20914
21338
  "CLI_OPENCLAW_INVALID_PEERS_CONFIG",
20915
21339
  "Peer config root must be a JSON object",
@@ -20920,7 +21344,7 @@ async function loadPeersConfig(peersPath) {
20920
21344
  if (peersValue === void 0) {
20921
21345
  return { peers: {} };
20922
21346
  }
20923
- if (!isRecord7(peersValue)) {
21347
+ if (!isRecord8(peersValue)) {
20924
21348
  throw createCliError5(
20925
21349
  "CLI_OPENCLAW_INVALID_PEERS_CONFIG",
20926
21350
  "Peer config peers field must be an object",
@@ -20930,7 +21354,7 @@ async function loadPeersConfig(peersPath) {
20930
21354
  const peers = {};
20931
21355
  for (const [alias, value] of Object.entries(peersValue)) {
20932
21356
  const normalizedAlias = parsePeerAlias(alias);
20933
- if (!isRecord7(value)) {
21357
+ if (!isRecord8(value)) {
20934
21358
  throw createCliError5(
20935
21359
  "CLI_OPENCLAW_INVALID_PEERS_CONFIG",
20936
21360
  "Peer entry must be an object",
@@ -20952,8 +21376,35 @@ async function savePeersConfig(peersPath, config2) {
20952
21376
  await writeSecureFile3(peersPath, `${JSON.stringify(config2, null, 2)}
20953
21377
  `);
20954
21378
  }
21379
+ function parseRelayRuntimeEchoConfig(value, relayRuntimeConfigPath) {
21380
+ if (value === void 0) {
21381
+ return defaultRelayEchoConfig();
21382
+ }
21383
+ if (!isRecord8(value)) {
21384
+ throw createCliError5(
21385
+ "CLI_OPENCLAW_INVALID_RELAY_RUNTIME_CONFIG",
21386
+ "Relay runtime echo config must be an object",
21387
+ { relayRuntimeConfigPath }
21388
+ );
21389
+ }
21390
+ const defaults = defaultRelayEchoConfig();
21391
+ try {
21392
+ return {
21393
+ enabled: value.enabled === void 0 ? defaults.enabled : parseRelayEchoEnabled(value.enabled),
21394
+ channel: value.channel === void 0 ? defaults.channel : parseRelayEchoChannel(value.channel),
21395
+ to: parseRelayEchoTo(value.to),
21396
+ maxLength: value.maxLength === void 0 ? defaults.maxLength : parseRelayEchoMaxLength(value.maxLength)
21397
+ };
21398
+ } catch (error48) {
21399
+ throw createCliError5(
21400
+ "CLI_OPENCLAW_INVALID_RELAY_RUNTIME_CONFIG",
21401
+ error48 instanceof Error ? error48.message : "Relay runtime echo config is invalid",
21402
+ { relayRuntimeConfigPath }
21403
+ );
21404
+ }
21405
+ }
20955
21406
  function parseRelayRuntimeConfig(value, relayRuntimeConfigPath) {
20956
- if (!isRecord7(value)) {
21407
+ if (!isRecord8(value)) {
20957
21408
  throw createCliError5(
20958
21409
  "CLI_OPENCLAW_INVALID_RELAY_RUNTIME_CONFIG",
20959
21410
  "Relay runtime config must be an object",
@@ -20961,9 +21412,11 @@ function parseRelayRuntimeConfig(value, relayRuntimeConfigPath) {
20961
21412
  );
20962
21413
  }
20963
21414
  const updatedAt = typeof value.updatedAt === "string" && value.updatedAt.trim().length > 0 ? value.updatedAt.trim() : void 0;
21415
+ const echo = parseRelayRuntimeEchoConfig(value.echo, relayRuntimeConfigPath);
20964
21416
  return {
20965
- openclawBaseUrl: parseOpenclawBaseUrl(value.openclawBaseUrl),
20966
- updatedAt
21417
+ openclawBaseUrl: parseOpenclawBaseUrl2(value.openclawBaseUrl),
21418
+ updatedAt,
21419
+ echo
20967
21420
  };
20968
21421
  }
20969
21422
  async function loadRelayRuntimeConfig(relayRuntimeConfigPath) {
@@ -20978,10 +21431,11 @@ async function loadRelayRuntimeConfig(relayRuntimeConfigPath) {
20978
21431
  }
20979
21432
  return parseRelayRuntimeConfig(parsed, relayRuntimeConfigPath);
20980
21433
  }
20981
- async function saveRelayRuntimeConfig(relayRuntimeConfigPath, openclawBaseUrl) {
21434
+ async function saveRelayRuntimeConfig(relayRuntimeConfigPath, openclawBaseUrl, relayEchoConfig) {
20982
21435
  const config2 = {
20983
21436
  openclawBaseUrl,
20984
- updatedAt: nowIso()
21437
+ updatedAt: nowIso(),
21438
+ echo: relayEchoConfig
20985
21439
  };
20986
21440
  await writeSecureFile3(
20987
21441
  relayRuntimeConfigPath,
@@ -20989,13 +21443,13 @@ async function saveRelayRuntimeConfig(relayRuntimeConfigPath, openclawBaseUrl) {
20989
21443
  `
20990
21444
  );
20991
21445
  }
20992
- async function resolveOpenclawBaseUrl2(input) {
21446
+ async function resolveOpenclawBaseUrl(input) {
20993
21447
  if (typeof input.optionValue === "string" && input.optionValue.trim().length > 0) {
20994
- return parseOpenclawBaseUrl(input.optionValue);
21448
+ return parseOpenclawBaseUrl2(input.optionValue);
20995
21449
  }
20996
21450
  const envOpenclawBaseUrl = process.env.OPENCLAW_BASE_URL;
20997
21451
  if (typeof envOpenclawBaseUrl === "string" && envOpenclawBaseUrl.trim().length > 0) {
20998
- return parseOpenclawBaseUrl(envOpenclawBaseUrl);
21452
+ return parseOpenclawBaseUrl2(envOpenclawBaseUrl);
20999
21453
  }
21000
21454
  const existingConfig = await loadRelayRuntimeConfig(
21001
21455
  input.relayRuntimeConfigPath
@@ -21022,20 +21476,20 @@ function normalizeStringArrayWithValue(value, requiredValue) {
21022
21476
  return Array.from(normalized);
21023
21477
  }
21024
21478
  function upsertRelayHookMapping(mappingsValue) {
21025
- const mappings = Array.isArray(mappingsValue) ? mappingsValue.filter(isRecord7).map((mapping) => ({ ...mapping })) : [];
21479
+ const mappings = Array.isArray(mappingsValue) ? mappingsValue.filter(isRecord8).map((mapping) => ({ ...mapping })) : [];
21026
21480
  const existingIndex = mappings.findIndex((mapping) => {
21027
21481
  if (mapping.id === HOOK_MAPPING_ID) {
21028
21482
  return true;
21029
21483
  }
21030
- if (!isRecord7(mapping.match)) {
21484
+ if (!isRecord8(mapping.match)) {
21031
21485
  return false;
21032
21486
  }
21033
21487
  return mapping.match.path === HOOK_PATH_SEND_TO_PEER;
21034
21488
  });
21035
- const baseMapping = existingIndex >= 0 && isRecord7(mappings[existingIndex]) ? mappings[existingIndex] : {};
21036
- const nextMatch = isRecord7(baseMapping.match) ? { ...baseMapping.match } : {};
21489
+ const baseMapping = existingIndex >= 0 && isRecord8(mappings[existingIndex]) ? mappings[existingIndex] : {};
21490
+ const nextMatch = isRecord8(baseMapping.match) ? { ...baseMapping.match } : {};
21037
21491
  nextMatch.path = HOOK_PATH_SEND_TO_PEER;
21038
- const nextTransform = isRecord7(baseMapping.transform) ? { ...baseMapping.transform } : {};
21492
+ const nextTransform = isRecord8(baseMapping.transform) ? { ...baseMapping.transform } : {};
21039
21493
  nextTransform.module = RELAY_MODULE_FILE_NAME;
21040
21494
  const relayMapping = {
21041
21495
  ...baseMapping,
@@ -21066,14 +21520,14 @@ async function patchOpenclawConfig(openclawConfigPath) {
21066
21520
  }
21067
21521
  throw error48;
21068
21522
  }
21069
- if (!isRecord7(config2)) {
21523
+ if (!isRecord8(config2)) {
21070
21524
  throw createCliError5(
21071
21525
  "CLI_OPENCLAW_INVALID_CONFIG",
21072
21526
  "OpenClaw config root must be an object",
21073
21527
  { openclawConfigPath }
21074
21528
  );
21075
21529
  }
21076
- const hooks = isRecord7(config2.hooks) ? { ...config2.hooks } : {};
21530
+ const hooks = isRecord8(config2.hooks) ? { ...config2.hooks } : {};
21077
21531
  hooks.enabled = true;
21078
21532
  hooks.allowRequestSessionKey = false;
21079
21533
  hooks.allowedSessionKeyPrefixes = normalizeStringArrayWithValue(
@@ -21103,10 +21557,10 @@ function toDoctorResult(checks) {
21103
21557
  };
21104
21558
  }
21105
21559
  function isRelayHookMapping(value) {
21106
- if (!isRecord7(value)) {
21560
+ if (!isRecord8(value)) {
21107
21561
  return false;
21108
21562
  }
21109
- if (!isRecord7(value.match) || value.match.path !== HOOK_PATH_SEND_TO_PEER) {
21563
+ if (!isRecord8(value.match) || value.match.path !== HOOK_PATH_SEND_TO_PEER) {
21110
21564
  return false;
21111
21565
  }
21112
21566
  if (typeof value.id === "string" && value.id !== HOOK_MAPPING_ID) {
@@ -21115,7 +21569,7 @@ function isRelayHookMapping(value) {
21115
21569
  return true;
21116
21570
  }
21117
21571
  function hasRelayTransformModule(value) {
21118
- if (!isRecord7(value) || !isRecord7(value.transform)) {
21572
+ if (!isRecord8(value) || !isRecord8(value.transform)) {
21119
21573
  return false;
21120
21574
  }
21121
21575
  return value.transform.module === RELAY_MODULE_FILE_NAME;
@@ -21232,7 +21686,7 @@ async function runOpenclawDoctor(options = {}) {
21232
21686
  const selectedAgentPath = resolveOpenclawAgentNamePath(homeDir);
21233
21687
  let selectedAgentName;
21234
21688
  try {
21235
- const selectedAgentRaw = await readFile4(selectedAgentPath, "utf8");
21689
+ const selectedAgentRaw = await readFile5(selectedAgentPath, "utf8");
21236
21690
  selectedAgentName = assertValidAgentName(selectedAgentRaw.trim());
21237
21691
  checks.push(
21238
21692
  toDoctorCheck({
@@ -21354,7 +21808,7 @@ async function runOpenclawDoctor(options = {}) {
21354
21808
  }
21355
21809
  const transformTargetPath = resolveTransformTargetPath(openclawDir);
21356
21810
  try {
21357
- const transformContents = await readFile4(transformTargetPath, "utf8");
21811
+ const transformContents = await readFile5(transformTargetPath, "utf8");
21358
21812
  if (transformContents.trim().length === 0) {
21359
21813
  checks.push(
21360
21814
  toDoctorCheck({
@@ -21392,11 +21846,11 @@ async function runOpenclawDoctor(options = {}) {
21392
21846
  const openclawConfigPath = resolveOpenclawConfigPath(openclawDir);
21393
21847
  try {
21394
21848
  const openclawConfig = await readJsonFile(openclawConfigPath);
21395
- if (!isRecord7(openclawConfig)) {
21849
+ if (!isRecord8(openclawConfig)) {
21396
21850
  throw new Error("root");
21397
21851
  }
21398
- const hooks = isRecord7(openclawConfig.hooks) ? openclawConfig.hooks : {};
21399
- const mappings = Array.isArray(hooks.mappings) ? hooks.mappings.filter(isRecord7) : [];
21852
+ const hooks = isRecord8(openclawConfig.hooks) ? openclawConfig.hooks : {};
21853
+ const mappings = Array.isArray(hooks.mappings) ? hooks.mappings.filter(isRecord8) : [];
21400
21854
  const relayMapping = mappings.find(
21401
21855
  (mapping) => isRelayHookMapping(mapping)
21402
21856
  );
@@ -21436,7 +21890,7 @@ async function runOpenclawDoctor(options = {}) {
21436
21890
  }
21437
21891
  const relayRuntimeConfigPath = resolveRelayRuntimeConfigPath(homeDir);
21438
21892
  try {
21439
- const openclawBaseUrl = await resolveOpenclawBaseUrl2({
21893
+ const openclawBaseUrl = await resolveOpenclawBaseUrl({
21440
21894
  relayRuntimeConfigPath
21441
21895
  });
21442
21896
  checks.push(
@@ -21498,7 +21952,7 @@ async function runOpenclawRelayTest(options) {
21498
21952
  const relayRuntimeConfigPath = resolveRelayRuntimeConfigPath(homeDir);
21499
21953
  let openclawBaseUrl = DEFAULT_OPENCLAW_BASE_URL2;
21500
21954
  try {
21501
- openclawBaseUrl = await resolveOpenclawBaseUrl2({
21955
+ openclawBaseUrl = await resolveOpenclawBaseUrl({
21502
21956
  optionValue: options.openclawBaseUrl,
21503
21957
  relayRuntimeConfigPath
21504
21958
  });
@@ -21624,10 +22078,17 @@ async function setupOpenclawRelayFromInvite(agentName, options) {
21624
22078
  const transformSource = typeof options.transformSource === "string" && options.transformSource.trim().length > 0 ? options.transformSource.trim() : resolveDefaultTransformSource(openclawDir);
21625
22079
  const transformTargetPath = resolveTransformTargetPath(openclawDir);
21626
22080
  const relayRuntimeConfigPath = resolveRelayRuntimeConfigPath(homeDir);
21627
- const openclawBaseUrl = await resolveOpenclawBaseUrl2({
22081
+ const existingRelayRuntimeConfig = await loadRelayRuntimeConfig(
22082
+ relayRuntimeConfigPath
22083
+ );
22084
+ const openclawBaseUrl = await resolveOpenclawBaseUrl({
21628
22085
  optionValue: options.openclawBaseUrl,
21629
22086
  relayRuntimeConfigPath
21630
22087
  });
22088
+ const relayEchoConfig = resolveSetupRelayEchoConfig({
22089
+ existingConfig: existingRelayRuntimeConfig,
22090
+ options
22091
+ });
21631
22092
  const invite = decodeInvitePayload(options.inviteCode);
21632
22093
  const peerAliasCandidate = options.peerAlias ?? invite.alias;
21633
22094
  if (!peerAliasCandidate) {
@@ -21659,7 +22120,11 @@ async function setupOpenclawRelayFromInvite(agentName, options) {
21659
22120
  const agentNamePath = resolveOpenclawAgentNamePath(homeDir);
21660
22121
  await writeSecureFile3(agentNamePath, `${normalizedAgentName}
21661
22122
  `);
21662
- await saveRelayRuntimeConfig(relayRuntimeConfigPath, openclawBaseUrl);
22123
+ await saveRelayRuntimeConfig(
22124
+ relayRuntimeConfigPath,
22125
+ openclawBaseUrl,
22126
+ relayEchoConfig
22127
+ );
21663
22128
  logger8.info("cli.openclaw_setup_completed", {
21664
22129
  agentName: normalizedAgentName,
21665
22130
  peerAlias,
@@ -21667,7 +22132,8 @@ async function setupOpenclawRelayFromInvite(agentName, options) {
21667
22132
  openclawConfigPath,
21668
22133
  transformTargetPath,
21669
22134
  openclawBaseUrl,
21670
- relayRuntimeConfigPath
22135
+ relayRuntimeConfigPath,
22136
+ relayEchoConfig
21671
22137
  });
21672
22138
  return {
21673
22139
  peerAlias,
@@ -21676,7 +22142,8 @@ async function setupOpenclawRelayFromInvite(agentName, options) {
21676
22142
  openclawConfigPath,
21677
22143
  transformTargetPath,
21678
22144
  openclawBaseUrl,
21679
- relayRuntimeConfigPath
22145
+ relayRuntimeConfigPath,
22146
+ relayEcho: relayEchoConfig
21680
22147
  };
21681
22148
  }
21682
22149
  var createOpenclawCommand = () => {
@@ -21712,6 +22179,20 @@ var createOpenclawCommand = () => {
21712
22179
  ).option(
21713
22180
  "--openclaw-base-url <url>",
21714
22181
  "Base URL for local OpenClaw hook API (default http://127.0.0.1:18789)"
22182
+ ).option(
22183
+ "--echo-enabled <true|false>",
22184
+ "Enable or disable relay echo to the operator chat channel (default true)",
22185
+ parseRelayEchoEnabled
22186
+ ).option(
22187
+ "--echo-channel <channel>",
22188
+ "Relay echo delivery channel (default last)"
22189
+ ).option(
22190
+ "--echo-to <target>",
22191
+ "Relay echo delivery target id/recipient (optional)"
22192
+ ).option(
22193
+ "--echo-max-length <number>",
22194
+ `Relay echo message truncation limit (${MIN_RELAY_ECHO_MAX_LENGTH}-${MAX_RELAY_ECHO_MAX_LENGTH}, default ${DEFAULT_RELAY_ECHO_MAX_LENGTH2})`,
22195
+ parseRelayEchoMaxLength
21715
22196
  ).action(
21716
22197
  withErrorHandling(
21717
22198
  "openclaw setup",
@@ -21725,6 +22206,16 @@ var createOpenclawCommand = () => {
21725
22206
  );
21726
22207
  writeStdoutLine(`Installed transform: ${result.transformTargetPath}`);
21727
22208
  writeStdoutLine(`OpenClaw base URL: ${result.openclawBaseUrl}`);
22209
+ writeStdoutLine(
22210
+ `Relay echo enabled: ${result.relayEcho.enabled ? "true" : "false"}`
22211
+ );
22212
+ writeStdoutLine(`Relay echo channel: ${result.relayEcho.channel}`);
22213
+ writeStdoutLine(
22214
+ `Relay echo target: ${result.relayEcho.to ?? "(last route default)"}`
22215
+ );
22216
+ writeStdoutLine(
22217
+ `Relay echo max length: ${result.relayEcho.maxLength}`
22218
+ );
21728
22219
  writeStdoutLine(
21729
22220
  `Relay runtime config: ${result.relayRuntimeConfigPath}`
21730
22221
  );
@@ -21796,8 +22287,8 @@ var createOpenclawCommand = () => {
21796
22287
 
21797
22288
  // src/commands/pair.ts
21798
22289
  import { randomBytes as randomBytes3 } from "crypto";
21799
- import { mkdir as mkdir6, readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
21800
- import { dirname as dirname5, join as join7, resolve } from "path";
22290
+ import { mkdir as mkdir6, readdir, readFile as readFile6, unlink as unlink2, writeFile as writeFile6 } from "fs/promises";
22291
+ import { dirname as dirname5, join as join8, resolve } from "path";
21801
22292
  import { Command as Command8 } from "commander";
21802
22293
  import jsQR from "jsqr";
21803
22294
  import { PNG } from "pngjs";
@@ -21812,7 +22303,9 @@ var PAIR_CONFIRM_PATH = "/pair/confirm";
21812
22303
  var OWNER_PAT_HEADER = "x-claw-owner-pat";
21813
22304
  var NONCE_SIZE2 = 24;
21814
22305
  var PAIRING_TICKET_PREFIX = "clwpair1_";
21815
- var isRecord8 = (value) => {
22306
+ var PAIRING_QR_MAX_AGE_SECONDS = 900;
22307
+ var PAIRING_QR_FILENAME_PATTERN = /-pair-(\d+)\.png$/;
22308
+ var isRecord9 = (value) => {
21816
22309
  return typeof value === "object" && value !== null;
21817
22310
  };
21818
22311
  function createCliError6(code, message2) {
@@ -21879,7 +22372,7 @@ function toPathWithQuery3(url2) {
21879
22372
  return `${parsed.pathname}${parsed.search}`;
21880
22373
  }
21881
22374
  function extractErrorCode(payload) {
21882
- if (!isRecord8(payload)) {
22375
+ if (!isRecord9(payload)) {
21883
22376
  return void 0;
21884
22377
  }
21885
22378
  const envelope = payload;
@@ -21890,7 +22383,7 @@ function extractErrorCode(payload) {
21890
22383
  return code.length > 0 ? code : void 0;
21891
22384
  }
21892
22385
  function extractErrorMessage(payload) {
21893
- if (!isRecord8(payload)) {
22386
+ if (!isRecord9(payload)) {
21894
22387
  return void 0;
21895
22388
  }
21896
22389
  const envelope = payload;
@@ -21958,7 +22451,7 @@ function mapConfirmPairError(status, payload) {
21958
22451
  return `Pair confirm failed (${status})`;
21959
22452
  }
21960
22453
  function parsePairStartResponse(payload) {
21961
- if (!isRecord8(payload)) {
22454
+ if (!isRecord9(payload)) {
21962
22455
  throw createCliError6(
21963
22456
  "CLI_PAIR_START_INVALID_RESPONSE",
21964
22457
  "Pair start response is invalid"
@@ -21980,7 +22473,7 @@ function parsePairStartResponse(payload) {
21980
22473
  };
21981
22474
  }
21982
22475
  function parsePairConfirmResponse(payload) {
21983
- if (!isRecord8(payload)) {
22476
+ if (!isRecord9(payload)) {
21984
22477
  throw createCliError6(
21985
22478
  "CLI_PAIR_CONFIRM_INVALID_RESPONSE",
21986
22479
  "Pair confirm response is invalid"
@@ -22002,16 +22495,16 @@ function parsePairConfirmResponse(payload) {
22002
22495
  };
22003
22496
  }
22004
22497
  async function readAgentProofMaterial(agentName, dependencies) {
22005
- const readFileImpl = dependencies.readFileImpl ?? readFile5;
22498
+ const readFileImpl = dependencies.readFileImpl ?? readFile6;
22006
22499
  const getConfigDirImpl = dependencies.getConfigDirImpl ?? getConfigDir;
22007
22500
  const normalizedAgentName = assertValidAgentName(agentName);
22008
- const agentDir = join7(
22501
+ const agentDir = join8(
22009
22502
  getConfigDirImpl(),
22010
22503
  AGENTS_DIR_NAME5,
22011
22504
  normalizedAgentName
22012
22505
  );
22013
- const aitPath = join7(agentDir, AIT_FILE_NAME4);
22014
- const secretKeyPath = join7(agentDir, SECRET_KEY_FILE_NAME3);
22506
+ const aitPath = join8(agentDir, AIT_FILE_NAME4);
22507
+ const secretKeyPath = join8(agentDir, SECRET_KEY_FILE_NAME3);
22015
22508
  let ait;
22016
22509
  try {
22017
22510
  ait = (await readFileImpl(aitPath, "utf-8")).trim();
@@ -22120,14 +22613,47 @@ function decodeTicketFromPng(imageBytes) {
22120
22613
  }
22121
22614
  async function persistPairingQr(input) {
22122
22615
  const mkdirImpl = input.dependencies.mkdirImpl ?? mkdir6;
22616
+ const readdirImpl = input.dependencies.readdirImpl ?? readdir;
22617
+ const unlinkImpl = input.dependencies.unlinkImpl ?? unlink2;
22123
22618
  const writeFileImpl = input.dependencies.writeFileImpl ?? writeFile6;
22124
22619
  const getConfigDirImpl = input.dependencies.getConfigDirImpl ?? getConfigDir;
22125
22620
  const qrEncodeImpl = input.dependencies.qrEncodeImpl ?? encodeTicketQrPng;
22126
- const baseDir = join7(getConfigDirImpl(), PAIRING_QR_DIR_NAME);
22127
- const outputPath = parseNonEmptyString8(input.qrOutput) ? resolve(input.qrOutput ?? "") : join7(
22621
+ const baseDir = join8(getConfigDirImpl(), PAIRING_QR_DIR_NAME);
22622
+ const outputPath = parseNonEmptyString8(input.qrOutput) ? resolve(input.qrOutput ?? "") : join8(
22128
22623
  baseDir,
22129
22624
  `${assertValidAgentName(input.agentName)}-pair-${input.nowSeconds}.png`
22130
22625
  );
22626
+ const existingFiles = await readdirImpl(baseDir).catch((error48) => {
22627
+ const nodeError = error48;
22628
+ if (nodeError.code === "ENOENT") {
22629
+ return [];
22630
+ }
22631
+ throw error48;
22632
+ });
22633
+ for (const fileName of existingFiles) {
22634
+ if (typeof fileName !== "string") {
22635
+ continue;
22636
+ }
22637
+ const match2 = PAIRING_QR_FILENAME_PATTERN.exec(fileName);
22638
+ if (!match2) {
22639
+ continue;
22640
+ }
22641
+ const issuedAtSeconds = Number.parseInt(match2[1] ?? "", 10);
22642
+ if (!Number.isInteger(issuedAtSeconds)) {
22643
+ continue;
22644
+ }
22645
+ if (issuedAtSeconds + PAIRING_QR_MAX_AGE_SECONDS > input.nowSeconds) {
22646
+ continue;
22647
+ }
22648
+ const stalePath = join8(baseDir, fileName);
22649
+ await unlinkImpl(stalePath).catch((error48) => {
22650
+ const nodeError = error48;
22651
+ if (nodeError.code === "ENOENT") {
22652
+ return;
22653
+ }
22654
+ throw error48;
22655
+ });
22656
+ }
22131
22657
  await mkdirImpl(dirname5(outputPath), { recursive: true });
22132
22658
  const imageBytes = await qrEncodeImpl(input.ticket);
22133
22659
  await writeFileImpl(outputPath, imageBytes);
@@ -22232,7 +22758,7 @@ async function confirmPairing(agentName, options, dependencies = {}) {
22232
22758
  const fetchImpl = dependencies.fetchImpl ?? fetch;
22233
22759
  const nowSecondsImpl = dependencies.nowSecondsImpl ?? (() => Math.floor(Date.now() / 1e3));
22234
22760
  const nonceFactoryImpl = dependencies.nonceFactoryImpl ?? (() => randomBytes3(NONCE_SIZE2).toString("base64url"));
22235
- const readFileImpl = dependencies.readFileImpl ?? readFile5;
22761
+ const readFileImpl = dependencies.readFileImpl ?? readFile6;
22236
22762
  const qrDecodeImpl = dependencies.qrDecodeImpl ?? decodeTicketFromPng;
22237
22763
  const ticketSource = resolveConfirmTicketSource(options);
22238
22764
  const proxyUrl = resolveProxyUrl(options.proxyUrl);
@@ -22297,6 +22823,19 @@ async function confirmPairing(agentName, options, dependencies = {}) {
22297
22823
  );
22298
22824
  }
22299
22825
  const parsed = parsePairConfirmResponse(responseBody);
22826
+ if (ticketSource.source === "qr-file" && ticketSource.qrFilePath) {
22827
+ const unlinkImpl = dependencies.unlinkImpl ?? unlink2;
22828
+ await unlinkImpl(ticketSource.qrFilePath).catch((error48) => {
22829
+ const nodeError = error48;
22830
+ if (nodeError.code === "ENOENT") {
22831
+ return;
22832
+ }
22833
+ logger9.warn("cli.pair.confirm.qr_cleanup_failed", {
22834
+ path: ticketSource.qrFilePath,
22835
+ reason: error48 instanceof Error && error48.message.length > 0 ? error48.message : "unknown"
22836
+ });
22837
+ });
22838
+ }
22300
22839
  return {
22301
22840
  ...parsed,
22302
22841
  proxyUrl
@@ -22357,7 +22896,7 @@ var createPairCommand = (dependencies = {}) => {
22357
22896
  };
22358
22897
 
22359
22898
  // src/commands/verify.ts
22360
- import { readFile as readFile6 } from "fs/promises";
22899
+ import { readFile as readFile7 } from "fs/promises";
22361
22900
  import { Command as Command9 } from "commander";
22362
22901
  var logger10 = createLogger({ service: "cli", module: "verify" });
22363
22902
  var REGISTRY_KEYS_CACHE_FILE = "registry-keys.json";
@@ -22370,7 +22909,7 @@ var VerifyCommandError = class extends Error {
22370
22909
  this.name = "VerifyCommandError";
22371
22910
  }
22372
22911
  };
22373
- var isRecord9 = (value) => {
22912
+ var isRecord10 = (value) => {
22374
22913
  return typeof value === "object" && value !== null;
22375
22914
  };
22376
22915
  var normalizeRegistryUrl = (registryUrl) => {
@@ -22406,7 +22945,7 @@ var resolveToken = async (tokenOrFile) => {
22406
22945
  throw new VerifyCommandError("invalid token (value is empty)");
22407
22946
  }
22408
22947
  try {
22409
- const fileContents = await readFile6(input, "utf-8");
22948
+ const fileContents = await readFile7(input, "utf-8");
22410
22949
  const token = fileContents.trim();
22411
22950
  if (token.length === 0) {
22412
22951
  throw new VerifyCommandError(`invalid token (${input} is empty)`);
@@ -22438,7 +22977,7 @@ var parseResponseJson = async (response) => {
22438
22977
  }
22439
22978
  };
22440
22979
  var parseSigningKeys = (payload) => {
22441
- if (!isRecord9(payload) || !Array.isArray(payload.keys)) {
22980
+ if (!isRecord10(payload) || !Array.isArray(payload.keys)) {
22442
22981
  throw new VerifyCommandError(
22443
22982
  "verification keys unavailable (response payload is invalid)"
22444
22983
  );
@@ -22457,7 +22996,7 @@ var parseSigningKeys = (payload) => {
22457
22996
  };
22458
22997
  var parseRegistryKeysCache = (rawCache) => {
22459
22998
  const parsed = parseJson(rawCache);
22460
- if (!isRecord9(parsed)) {
22999
+ if (!isRecord10(parsed)) {
22461
23000
  return void 0;
22462
23001
  }
22463
23002
  const { registryUrl, fetchedAtMs, keys } = parsed;
@@ -22483,7 +23022,7 @@ var parseRegistryKeysCache = (rawCache) => {
22483
23022
  };
22484
23023
  var parseCrlCache = (rawCache) => {
22485
23024
  const parsed = parseJson(rawCache);
22486
- if (!isRecord9(parsed)) {
23025
+ if (!isRecord10(parsed)) {
22487
23026
  return void 0;
22488
23027
  }
22489
23028
  const { registryUrl, fetchedAtMs, claims } = parsed;
@@ -22581,7 +23120,7 @@ var fetchCrlClaims = async (input) => {
22581
23120
  );
22582
23121
  }
22583
23122
  const payload = await parseResponseJson(response);
22584
- if (!isRecord9(payload) || typeof payload.crl !== "string") {
23123
+ if (!isRecord10(payload) || typeof payload.crl !== "string") {
22585
23124
  throw new VerifyCommandError(
22586
23125
  "revocation check unavailable (response payload is invalid)"
22587
23126
  );
@@ -22626,7 +23165,7 @@ var loadCrlClaims = async (input) => {
22626
23165
  return claims;
22627
23166
  };
22628
23167
  var toInvalidTokenReason = (error48) => {
22629
- if (isRecord9(error48) && typeof error48.message === "string") {
23168
+ if (isRecord10(error48) && typeof error48.message === "string") {
22630
23169
  return `invalid token (${error48.message})`;
22631
23170
  }
22632
23171
  if (error48 instanceof Error && error48.message.length > 0) {