clawdentity 0.0.4 → 0.0.6

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 readFile4, rm, writeFile as writeFile4 } from "fs/promises";
18498
+ import { mkdir as mkdir4, readFile as readFile3, rm, writeFile as writeFile4 } from "fs/promises";
18499
18499
  import { homedir as homedir2 } from "os";
18500
- import { dirname as dirname3, join as join6 } from "path";
18500
+ import { dirname as dirname3, join as join5 } from "path";
18501
18501
  import { fileURLToPath } from "url";
18502
18502
  import { promisify } from "util";
18503
18503
 
@@ -19151,271 +19151,15 @@ 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 join5 } from "path";
19154
+ import { dirname as dirname2, join as join4 } 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
19412
19156
  var REGISTRY_AUTH_FILENAME = "registry-auth.json";
19413
19157
  var AGENTS_DIR_NAME2 = "agents";
19414
19158
  var REFRESH_SINGLE_FLIGHT_PREFIX = "connector-runtime";
19415
19159
  var NONCE_SIZE = 16;
19416
19160
  var MAX_OUTBOUND_BODY_BYTES = 1024 * 1024;
19417
19161
  var ACCESS_TOKEN_REFRESH_SKEW_MS = 3e4;
19418
- function isRecord5(value) {
19162
+ function isRecord4(value) {
19419
19163
  return typeof value === "object" && value !== null;
19420
19164
  }
19421
19165
  function toPathWithQuery2(url2) {
@@ -19465,38 +19209,9 @@ function normalizeWebSocketUrl(urlInput) {
19465
19209
  }
19466
19210
  return parsed.toString();
19467
19211
  }
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
- };
19212
+ function resolveOpenclawBaseUrl(input) {
19213
+ const value = input?.trim() || process.env.OPENCLAW_BASE_URL?.trim() || DEFAULT_OPENCLAW_BASE_URL;
19214
+ return value;
19500
19215
  }
19501
19216
  function resolveOpenclawHookPath(input) {
19502
19217
  const value = input?.trim() || process.env.OPENCLAW_HOOK_PATH?.trim() || DEFAULT_OPENCLAW_HOOK_PATH;
@@ -19536,7 +19251,7 @@ function shouldRefreshAccessToken(auth, nowMs) {
19536
19251
  return expiresAtMs <= nowMs + ACCESS_TOKEN_REFRESH_SKEW_MS;
19537
19252
  }
19538
19253
  function parseOutboundRelayRequest(payload) {
19539
- if (!isRecord5(payload)) {
19254
+ if (!isRecord4(payload)) {
19540
19255
  throw new AppError({
19541
19256
  code: "CONNECTOR_OUTBOUND_INVALID_REQUEST",
19542
19257
  message: "Outbound relay request must be an object",
@@ -19594,7 +19309,7 @@ function createWebSocketFactory() {
19594
19309
  };
19595
19310
  }
19596
19311
  async function writeRegistryAuthAtomic(input) {
19597
- const targetPath = join5(
19312
+ const targetPath = join4(
19598
19313
  input.configDir,
19599
19314
  AGENTS_DIR_NAME2,
19600
19315
  input.agentName,
@@ -19691,63 +19406,14 @@ async function startConnectorRuntime(input) {
19691
19406
  accessToken: currentAuth.accessToken,
19692
19407
  secretKey
19693
19408
  });
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
- };
19724
19409
  const connectorClient = new ConnectorClient({
19725
19410
  connectorUrl: wsParsed.toString(),
19726
19411
  connectionHeaders: upgradeHeaders,
19727
- openclawBaseUrl: openclawRuntimeSettings.openclawBaseUrl,
19728
- openclawHookPath,
19729
- openclawHookToken,
19412
+ openclawBaseUrl: resolveOpenclawBaseUrl(input.openclawBaseUrl),
19413
+ openclawHookPath: resolveOpenclawHookPath(input.openclawHookPath),
19414
+ openclawHookToken: resolveOpenclawHookToken(input.openclawHookToken),
19730
19415
  fetchImpl,
19731
19416
  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
- },
19751
19417
  webSocketFactory: createWebSocketFactory()
19752
19418
  });
19753
19419
  const outboundBaseUrl = normalizeOutboundBaseUrl(input.outboundBaseUrl);
@@ -19833,22 +19499,6 @@ async function startConnectorRuntime(input) {
19833
19499
  const requestBody = await readRequestJson(req);
19834
19500
  const relayRequest = parseOutboundRelayRequest(requestBody);
19835
19501
  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
- });
19852
19502
  writeJson(res, 202, { accepted: true, peer: relayRequest.peer });
19853
19503
  } catch (error48) {
19854
19504
  if (error48 instanceof AppError) {
@@ -19927,14 +19577,16 @@ var IDENTITY_FILE_NAME2 = "identity.json";
19927
19577
  var AIT_FILE_NAME2 = "ait.jwt";
19928
19578
  var SECRET_KEY_FILE_NAME = "secret.key";
19929
19579
  var REGISTRY_AUTH_FILE_NAME2 = "registry-auth.json";
19580
+ var OPENCLAW_RELAY_RUNTIME_FILE_NAME = "openclaw-relay.json";
19581
+ var OPENCLAW_CONNECTORS_FILE_NAME = "openclaw-connectors.json";
19930
19582
  var SERVICE_LOG_DIR_NAME = "logs";
19931
19583
  var DEFAULT_CONNECTOR_BASE_URL2 = "http://127.0.0.1:19400";
19932
19584
  var DEFAULT_CONNECTOR_OUTBOUND_PATH2 = "/v1/outbound";
19933
- function isRecord6(value) {
19585
+ function isRecord5(value) {
19934
19586
  return typeof value === "object" && value !== null;
19935
19587
  }
19936
19588
  function getErrorCode(error48) {
19937
- if (!isRecord6(error48)) {
19589
+ if (!isRecord5(error48)) {
19938
19590
  return void 0;
19939
19591
  }
19940
19592
  return typeof error48.code === "string" ? error48.code : void 0;
@@ -20010,13 +19662,43 @@ function normalizeOutboundPath2(pathValue) {
20010
19662
  }
20011
19663
  return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
20012
19664
  }
20013
- function resolveConnectorBaseUrl() {
19665
+ function resolveConnectorBaseUrlFromEnv() {
20014
19666
  const value = process.env.CLAWDENTITY_CONNECTOR_BASE_URL;
20015
19667
  if (typeof value !== "string" || value.trim().length === 0) {
20016
- return DEFAULT_CONNECTOR_BASE_URL2;
19668
+ return void 0;
20017
19669
  }
20018
19670
  return parseConnectorBaseUrl(value.trim());
20019
19671
  }
19672
+ async function readConnectorAssignedBaseUrl(configDir, agentName, readFileImpl) {
19673
+ const assignmentsPath = join5(configDir, OPENCLAW_CONNECTORS_FILE_NAME);
19674
+ let raw;
19675
+ try {
19676
+ raw = await readFileImpl(assignmentsPath, "utf8");
19677
+ } catch (error48) {
19678
+ if (getErrorCode(error48) === "ENOENT") {
19679
+ return void 0;
19680
+ }
19681
+ throw error48;
19682
+ }
19683
+ let parsed;
19684
+ try {
19685
+ parsed = JSON.parse(raw);
19686
+ } catch {
19687
+ throw createCliError3(
19688
+ "CLI_CONNECTOR_INVALID_ASSIGNMENTS",
19689
+ "Connector assignments config is invalid JSON",
19690
+ { assignmentsPath }
19691
+ );
19692
+ }
19693
+ if (!isRecord5(parsed) || !isRecord5(parsed.agents)) {
19694
+ return void 0;
19695
+ }
19696
+ const entry = parsed.agents[agentName];
19697
+ if (!isRecord5(entry) || typeof entry.connectorBaseUrl !== "string") {
19698
+ return void 0;
19699
+ }
19700
+ return parseConnectorBaseUrl(entry.connectorBaseUrl);
19701
+ }
20020
19702
  function resolveConnectorOutboundPath() {
20021
19703
  const value = process.env.CLAWDENTITY_CONNECTOR_OUTBOUND_PATH;
20022
19704
  if (typeof value !== "string" || value.trim().length === 0) {
@@ -20051,6 +19733,34 @@ async function readRequiredTrimmedFile(filePath, label, readFileImpl) {
20051
19733
  }
20052
19734
  return trimmed;
20053
19735
  }
19736
+ async function readRelayRuntimeConfig(configDir, readFileImpl) {
19737
+ const filePath = join5(configDir, OPENCLAW_RELAY_RUNTIME_FILE_NAME);
19738
+ let raw;
19739
+ try {
19740
+ raw = await readFileImpl(filePath, "utf8");
19741
+ } catch (error48) {
19742
+ if (getErrorCode(error48) === "ENOENT") {
19743
+ return void 0;
19744
+ }
19745
+ throw error48;
19746
+ }
19747
+ let parsed;
19748
+ try {
19749
+ parsed = JSON.parse(raw);
19750
+ } catch {
19751
+ return void 0;
19752
+ }
19753
+ if (!isRecord5(parsed)) {
19754
+ return void 0;
19755
+ }
19756
+ const openclawHookToken = typeof parsed.openclawHookToken === "string" && parsed.openclawHookToken.trim().length > 0 ? parsed.openclawHookToken.trim() : void 0;
19757
+ if (!openclawHookToken) {
19758
+ return void 0;
19759
+ }
19760
+ return {
19761
+ openclawHookToken
19762
+ };
19763
+ }
20054
19764
  function parseJsonRecord(value, code, message2) {
20055
19765
  let parsed;
20056
19766
  try {
@@ -20058,7 +19768,7 @@ function parseJsonRecord(value, code, message2) {
20058
19768
  } catch {
20059
19769
  throw createCliError3(code, message2);
20060
19770
  }
20061
- if (!isRecord6(parsed)) {
19771
+ if (!isRecord5(parsed)) {
20062
19772
  throw createCliError3(code, message2);
20063
19773
  }
20064
19774
  return parsed;
@@ -20098,7 +19808,7 @@ async function loadDefaultConnectorModule() {
20098
19808
  };
20099
19809
  }
20100
19810
  function resolveWaitPromise(runtime) {
20101
- if (!runtime || !isRecord6(runtime)) {
19811
+ if (!runtime || !isRecord5(runtime)) {
20102
19812
  return void 0;
20103
19813
  }
20104
19814
  if (typeof runtime.waitUntilStopped === "function") {
@@ -20163,7 +19873,7 @@ function buildConnectorStartArgs(agentName, commandOptions) {
20163
19873
  }
20164
19874
  function resolveCliEntryPath(resolveCurrentModulePathImpl) {
20165
19875
  const modulePath = resolveCurrentModulePathImpl?.() ?? fileURLToPath(import.meta.url);
20166
- return join6(dirname3(modulePath), "..", "bin.js");
19876
+ return join5(dirname3(modulePath), "..", "bin.js");
20167
19877
  }
20168
19878
  function escapeXml(value) {
20169
19879
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
@@ -20261,7 +19971,7 @@ async function installConnectorServiceForAgent(agentName, commandOptions = {}, d
20261
19971
  );
20262
19972
  const configDir = serviceDependencies.getConfigDirImpl();
20263
19973
  const homeDir = serviceDependencies.getHomeDirImpl();
20264
- const logsDir = join6(configDir, SERVICE_LOG_DIR_NAME);
19974
+ const logsDir = join5(configDir, SERVICE_LOG_DIR_NAME);
20265
19975
  const serviceName = sanitizeServiceSegment(
20266
19976
  `clawdentity-connector-${agentName}`
20267
19977
  );
@@ -20271,12 +19981,12 @@ async function installConnectorServiceForAgent(agentName, commandOptions = {}, d
20271
19981
  resolveCliEntryPath(serviceDependencies.resolveCurrentModulePathImpl),
20272
19982
  ...startArgs
20273
19983
  ];
20274
- const outputLogPath = join6(logsDir, `${serviceName}.out.log`);
20275
- const errorLogPath = join6(logsDir, `${serviceName}.err.log`);
19984
+ const outputLogPath = join5(logsDir, `${serviceName}.out.log`);
19985
+ const errorLogPath = join5(logsDir, `${serviceName}.err.log`);
20276
19986
  await serviceDependencies.mkdirImpl(logsDir, { recursive: true });
20277
19987
  if (platform === "systemd") {
20278
- const serviceDir = join6(homeDir, ".config", "systemd", "user");
20279
- const serviceFilePath2 = join6(serviceDir, `${serviceName}.service`);
19988
+ const serviceDir = join5(homeDir, ".config", "systemd", "user");
19989
+ const serviceFilePath2 = join5(serviceDir, `${serviceName}.service`);
20280
19990
  await serviceDependencies.mkdirImpl(serviceDir, { recursive: true });
20281
19991
  await serviceDependencies.writeFileImpl(
20282
19992
  serviceFilePath2,
@@ -20315,9 +20025,9 @@ async function installConnectorServiceForAgent(agentName, commandOptions = {}, d
20315
20025
  serviceFilePath: serviceFilePath2
20316
20026
  };
20317
20027
  }
20318
- const launchAgentsDir = join6(homeDir, "Library", "LaunchAgents");
20028
+ const launchAgentsDir = join5(homeDir, "Library", "LaunchAgents");
20319
20029
  const serviceNameWithDomain = `com.clawdentity.${serviceName}`;
20320
- const serviceFilePath = join6(
20030
+ const serviceFilePath = join5(
20321
20031
  launchAgentsDir,
20322
20032
  `${serviceNameWithDomain}.plist`
20323
20033
  );
@@ -20376,7 +20086,7 @@ async function uninstallConnectorServiceForAgent(agentName, commandOptions = {},
20376
20086
  `clawdentity-connector-${agentName}`
20377
20087
  );
20378
20088
  if (platform === "systemd") {
20379
- const serviceFilePath2 = join6(
20089
+ const serviceFilePath2 = join5(
20380
20090
  homeDir,
20381
20091
  ".config",
20382
20092
  "systemd",
@@ -20407,7 +20117,7 @@ async function uninstallConnectorServiceForAgent(agentName, commandOptions = {},
20407
20117
  };
20408
20118
  }
20409
20119
  const serviceNameWithDomain = `com.clawdentity.${serviceName}`;
20410
- const serviceFilePath = join6(
20120
+ const serviceFilePath = join5(
20411
20121
  homeDir,
20412
20122
  "Library",
20413
20123
  "LaunchAgents",
@@ -20431,38 +20141,42 @@ async function uninstallConnectorServiceForAgent(agentName, commandOptions = {},
20431
20141
  async function startConnectorForAgent(agentName, commandOptions = {}, dependencies = {}) {
20432
20142
  const resolveConfigImpl = dependencies.resolveConfigImpl ?? resolveConfig;
20433
20143
  const getConfigDirImpl = dependencies.getConfigDirImpl ?? getConfigDir;
20434
- const readFileImpl = dependencies.readFileImpl ?? ((path, encoding) => readFile4(path, encoding));
20144
+ const readFileImpl = dependencies.readFileImpl ?? ((path, encoding) => readFile3(path, encoding));
20435
20145
  const loadConnectorModule = dependencies.loadConnectorModule ?? loadDefaultConnectorModule;
20436
20146
  const configDir = getConfigDirImpl();
20437
- const agentDirectory = join6(configDir, AGENTS_DIR_NAME3, agentName);
20147
+ const agentDirectory = join5(configDir, AGENTS_DIR_NAME3, agentName);
20438
20148
  const [
20439
20149
  rawAit,
20440
20150
  rawSecretKey,
20441
20151
  rawIdentity,
20442
20152
  rawRegistryAuth,
20153
+ assignedConnectorBaseUrl,
20154
+ relayRuntimeConfig,
20443
20155
  config2,
20444
20156
  connectorModule
20445
20157
  ] = await Promise.all([
20446
20158
  readRequiredTrimmedFile(
20447
- join6(agentDirectory, AIT_FILE_NAME2),
20159
+ join5(agentDirectory, AIT_FILE_NAME2),
20448
20160
  AIT_FILE_NAME2,
20449
20161
  readFileImpl
20450
20162
  ),
20451
20163
  readRequiredTrimmedFile(
20452
- join6(agentDirectory, SECRET_KEY_FILE_NAME),
20164
+ join5(agentDirectory, SECRET_KEY_FILE_NAME),
20453
20165
  SECRET_KEY_FILE_NAME,
20454
20166
  readFileImpl
20455
20167
  ),
20456
20168
  readRequiredTrimmedFile(
20457
- join6(agentDirectory, IDENTITY_FILE_NAME2),
20169
+ join5(agentDirectory, IDENTITY_FILE_NAME2),
20458
20170
  IDENTITY_FILE_NAME2,
20459
20171
  readFileImpl
20460
20172
  ),
20461
20173
  readRequiredTrimmedFile(
20462
- join6(agentDirectory, REGISTRY_AUTH_FILE_NAME2),
20174
+ join5(agentDirectory, REGISTRY_AUTH_FILE_NAME2),
20463
20175
  REGISTRY_AUTH_FILE_NAME2,
20464
20176
  readFileImpl
20465
20177
  ),
20178
+ readConnectorAssignedBaseUrl(configDir, agentName, readFileImpl),
20179
+ readRelayRuntimeConfig(configDir, readFileImpl),
20466
20180
  resolveConfigImpl(),
20467
20181
  loadConnectorModule()
20468
20182
  ]);
@@ -20474,7 +20188,8 @@ async function startConnectorForAgent(agentName, commandOptions = {}, dependenci
20474
20188
  }
20475
20189
  const identity = parseAgentIdentity(rawIdentity);
20476
20190
  const registryAuth = parseRegistryAuth(rawRegistryAuth);
20477
- const outboundBaseUrl = resolveConnectorBaseUrl();
20191
+ const openclawHookToken = commandOptions.openclawHookToken ?? relayRuntimeConfig?.openclawHookToken;
20192
+ const outboundBaseUrl = resolveConnectorBaseUrlFromEnv() ?? assignedConnectorBaseUrl ?? DEFAULT_CONNECTOR_BASE_URL2;
20478
20193
  const outboundPath = resolveConnectorOutboundPath();
20479
20194
  const runtime = await connectorModule.startConnectorRuntime({
20480
20195
  agentName,
@@ -20485,7 +20200,7 @@ async function startConnectorForAgent(agentName, commandOptions = {}, dependenci
20485
20200
  proxyWebsocketUrl: commandOptions.proxyWsUrl,
20486
20201
  openclawBaseUrl: commandOptions.openclawBaseUrl,
20487
20202
  openclawHookPath: commandOptions.openclawHookPath,
20488
- openclawHookToken: commandOptions.openclawHookToken,
20203
+ openclawHookToken,
20489
20204
  credentials: {
20490
20205
  agentDid: identity.did,
20491
20206
  ait: rawAit,
@@ -20497,8 +20212,8 @@ async function startConnectorForAgent(agentName, commandOptions = {}, dependenci
20497
20212
  tokenType: registryAuth.tokenType
20498
20213
  }
20499
20214
  });
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;
20215
+ const outboundUrl = runtime && isRecord5(runtime) && typeof runtime.outboundUrl === "string" ? runtime.outboundUrl : resolveOutboundUrl(outboundBaseUrl, outboundPath);
20216
+ const proxyWebsocketUrl = runtime && isRecord5(runtime) ? typeof runtime.websocketUrl === "string" ? runtime.websocketUrl : typeof runtime.proxyWebsocketUrl === "string" ? runtime.proxyWebsocketUrl : void 0 : void 0;
20502
20217
  return {
20503
20218
  outboundUrl,
20504
20219
  proxyWebsocketUrl,
@@ -20629,7 +20344,7 @@ function createConnectorCommand(dependencies = {}) {
20629
20344
  // src/commands/invite.ts
20630
20345
  import { Command as Command6 } from "commander";
20631
20346
  var logger7 = createLogger({ service: "cli", module: "invite" });
20632
- var isRecord7 = (value) => {
20347
+ var isRecord6 = (value) => {
20633
20348
  return typeof value === "object" && value !== null;
20634
20349
  };
20635
20350
  function parseNonEmptyString6(value) {
@@ -20670,7 +20385,7 @@ function toRegistryRequestUrl(registryUrl, path) {
20670
20385
  return new URL(path.slice(1), normalizedBaseUrl).toString();
20671
20386
  }
20672
20387
  function extractRegistryErrorCode(payload) {
20673
- if (!isRecord7(payload)) {
20388
+ if (!isRecord6(payload)) {
20674
20389
  return void 0;
20675
20390
  }
20676
20391
  const envelope = payload;
@@ -20681,7 +20396,7 @@ function extractRegistryErrorCode(payload) {
20681
20396
  return trimmed.length > 0 ? trimmed : void 0;
20682
20397
  }
20683
20398
  function extractRegistryErrorMessage3(payload) {
20684
- if (!isRecord7(payload)) {
20399
+ if (!isRecord6(payload)) {
20685
20400
  return void 0;
20686
20401
  }
20687
20402
  const envelope = payload;
@@ -20755,13 +20470,13 @@ function mapRedeemInviteError(status, payload) {
20755
20470
  return `Invite redeem failed (${status})`;
20756
20471
  }
20757
20472
  function parseInviteRecord(payload) {
20758
- if (!isRecord7(payload)) {
20473
+ if (!isRecord6(payload)) {
20759
20474
  throw createCliError4(
20760
20475
  "CLI_INVITE_CREATE_INVALID_RESPONSE",
20761
20476
  "Invite response is invalid"
20762
20477
  );
20763
20478
  }
20764
- const source = isRecord7(payload.invite) ? payload.invite : payload;
20479
+ const source = isRecord6(payload.invite) ? payload.invite : payload;
20765
20480
  const code = parseNonEmptyString6(source.code);
20766
20481
  if (code.length === 0) {
20767
20482
  throw createCliError4(
@@ -20786,15 +20501,15 @@ function parseInviteRecord(payload) {
20786
20501
  return invite;
20787
20502
  }
20788
20503
  function parseInviteRedeemResponse(payload) {
20789
- if (!isRecord7(payload)) {
20504
+ if (!isRecord6(payload)) {
20790
20505
  throw createCliError4(
20791
20506
  "CLI_INVITE_REDEEM_INVALID_RESPONSE",
20792
20507
  "Invite redeem response is invalid"
20793
20508
  );
20794
20509
  }
20795
- const apiKeySource = isRecord7(payload.apiKey) ? payload.apiKey : payload;
20510
+ const apiKeySource = isRecord6(payload.apiKey) ? payload.apiKey : payload;
20796
20511
  const apiKeyToken = parseNonEmptyString6(
20797
- isRecord7(payload.apiKey) ? payload.apiKey.token : payload.token
20512
+ isRecord6(payload.apiKey) ? payload.apiKey.token : payload.token
20798
20513
  );
20799
20514
  if (apiKeyToken.length === 0) {
20800
20515
  throw createCliError4(
@@ -20902,7 +20617,7 @@ async function persistRedeemConfig(registryUrl, apiKeyToken, dependencies = {})
20902
20617
  }
20903
20618
  var createInviteCommand = (dependencies = {}) => {
20904
20619
  const inviteCommand = new Command6("invite").description(
20905
- "Manage registry onboarding invites (not OpenClaw peer relay invites)"
20620
+ "Manage registry onboarding invites"
20906
20621
  );
20907
20622
  inviteCommand.command("create").description("Create a registry invite code (admin only)").option("--expires-at <timestamp>", "Optional invite expiry (ISO-8601)").option("--registry-url <url>", "Override registry URL").action(
20908
20623
  withErrorHandling(
@@ -20945,6 +20660,7 @@ var createInviteCommand = (dependencies = {}) => {
20945
20660
  dependencies
20946
20661
  );
20947
20662
  writeStdoutLine("API key saved to local config");
20663
+ writeStdoutLine("Onboarding auth complete");
20948
20664
  }
20949
20665
  )
20950
20666
  );
@@ -20952,9 +20668,11 @@ var createInviteCommand = (dependencies = {}) => {
20952
20668
  };
20953
20669
 
20954
20670
  // src/commands/openclaw.ts
20955
- import { chmod as chmod3, copyFile, mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
20671
+ import { randomBytes as randomBytes3 } from "crypto";
20672
+ import { existsSync } from "fs";
20673
+ import { chmod as chmod3, copyFile, mkdir as mkdir5, readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
20956
20674
  import { homedir as homedir3 } from "os";
20957
- import { dirname as dirname4, join as join7 } from "path";
20675
+ import { dirname as dirname4, join as join6, resolve as resolvePath } from "path";
20958
20676
  import { Command as Command7 } from "commander";
20959
20677
  var logger8 = createLogger({ service: "cli", module: "openclaw" });
20960
20678
  var CLAWDENTITY_DIR_NAME = ".clawdentity";
@@ -20964,25 +20682,35 @@ var SECRET_KEY_FILE_NAME2 = "secret.key";
20964
20682
  var PEERS_FILE_NAME = "peers.json";
20965
20683
  var OPENCLAW_DIR_NAME = ".openclaw";
20966
20684
  var OPENCLAW_CONFIG_FILE_NAME = "openclaw.json";
20685
+ var LEGACY_OPENCLAW_STATE_DIR_NAMES = [".clawdbot", ".moldbot", ".moltbot"];
20686
+ var LEGACY_OPENCLAW_CONFIG_FILE_NAMES = ["clawdbot.json", "moldbot.json", "moltbot.json"];
20967
20687
  var OPENCLAW_AGENT_FILE_NAME = "openclaw-agent-name";
20968
- var OPENCLAW_RELAY_RUNTIME_FILE_NAME = "openclaw-relay.json";
20688
+ var OPENCLAW_RELAY_RUNTIME_FILE_NAME2 = "openclaw-relay.json";
20689
+ var OPENCLAW_CONNECTORS_FILE_NAME2 = "openclaw-connectors.json";
20969
20690
  var SKILL_DIR_NAME = "clawdentity-openclaw-relay";
20970
20691
  var RELAY_MODULE_FILE_NAME = "relay-to-peer.mjs";
20692
+ var RELAY_RUNTIME_FILE_NAME = "clawdentity-relay.json";
20693
+ var RELAY_PEERS_FILE_NAME = "clawdentity-peers.json";
20971
20694
  var HOOK_MAPPING_ID = "clawdentity-send-to-peer";
20972
20695
  var HOOK_PATH_SEND_TO_PEER = "send-to-peer";
20973
20696
  var OPENCLAW_SEND_TO_PEER_HOOK_PATH = "hooks/send-to-peer";
20974
20697
  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;
20698
+ var DEFAULT_CONNECTOR_PORT = 19400;
20699
+ var DEFAULT_CONNECTOR_OUTBOUND_PATH3 = "/v1/outbound";
20700
+ var CONNECTOR_HOST_LOOPBACK = "127.0.0.1";
20701
+ var CONNECTOR_HOST_DOCKER = "host.docker.internal";
20702
+ var CONNECTOR_HOST_DOCKER_GATEWAY = "gateway.docker.internal";
20703
+ var CONNECTOR_HOST_LINUX_BRIDGE = "172.17.0.1";
20980
20704
  var INVITE_CODE_PREFIX = "clawd1_";
20981
20705
  var PEER_ALIAS_PATTERN = /^[a-zA-Z0-9._-]+$/;
20982
20706
  var FILE_MODE3 = 384;
20707
+ var OPENCLAW_HOOK_TOKEN_BYTES = 32;
20708
+ var OPENCLAW_SETUP_COMMAND_HINT = "Run: clawdentity openclaw setup <agentName> --peer-alias <alias> --peer-did <did> --peer-proxy-url <proxy-url>";
20709
+ var OPENCLAW_SETUP_RESTART_COMMAND_HINT = `${OPENCLAW_SETUP_COMMAND_HINT} and restart OpenClaw`;
20710
+ var OPENCLAW_SETUP_WITH_BASE_URL_HINT = `${OPENCLAW_SETUP_COMMAND_HINT} --openclaw-base-url <url>`;
20983
20711
  var textEncoder2 = new TextEncoder();
20984
20712
  var textDecoder = new TextDecoder();
20985
- function isRecord8(value) {
20713
+ function isRecord7(value) {
20986
20714
  return typeof value === "object" && value !== null;
20987
20715
  }
20988
20716
  function createCliError5(code, message2, details) {
@@ -20994,7 +20722,7 @@ function createCliError5(code, message2, details) {
20994
20722
  });
20995
20723
  }
20996
20724
  function getErrorCode2(error48) {
20997
- if (!isRecord8(error48)) {
20725
+ if (!isRecord7(error48)) {
20998
20726
  return void 0;
20999
20727
  }
21000
20728
  return typeof error48.code === "string" ? error48.code : void 0;
@@ -21064,82 +20792,13 @@ function parseHttpUrl(value, input) {
21064
20792
  }
21065
20793
  return parsedUrl.toString();
21066
20794
  }
21067
- function parseOpenclawBaseUrl2(value) {
20795
+ function parseOpenclawBaseUrl(value) {
21068
20796
  return parseHttpUrl(value, {
21069
20797
  label: "OpenClaw base URL",
21070
20798
  code: "CLI_OPENCLAW_INVALID_OPENCLAW_BASE_URL",
21071
20799
  message: "OpenClaw base URL must be a valid URL"
21072
20800
  });
21073
20801
  }
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
- }
21143
20802
  function parseAgentDid2(value, label) {
21144
20803
  const did = parseNonEmptyString7(value, label);
21145
20804
  try {
@@ -21158,7 +20817,7 @@ function parseAgentDid2(value, label) {
21158
20817
  return did;
21159
20818
  }
21160
20819
  function parseInvitePayload(value) {
21161
- if (!isRecord8(value)) {
20820
+ if (!isRecord7(value)) {
21162
20821
  throw createCliError5(
21163
20822
  "CLI_OPENCLAW_INVALID_INVITE",
21164
20823
  "invite payload must be an object"
@@ -21207,23 +20866,86 @@ function resolveHomeDir(homeDir) {
21207
20866
  }
21208
20867
  return homedir3();
21209
20868
  }
20869
+ function resolveHomePrefixedPath(input, homeDir) {
20870
+ const trimmed = input.trim();
20871
+ if (trimmed.startsWith("~")) {
20872
+ return resolvePath(trimmed.replace(/^~(?=$|[\\/])/, homeDir));
20873
+ }
20874
+ return resolvePath(trimmed);
20875
+ }
20876
+ function readNonEmptyEnvPath(value, homeDir) {
20877
+ if (typeof value !== "string" || value.trim().length === 0) {
20878
+ return void 0;
20879
+ }
20880
+ return resolveHomePrefixedPath(value, homeDir);
20881
+ }
20882
+ function resolveOpenclawHomeDir(homeDir) {
20883
+ const envOpenclawHome = readNonEmptyEnvPath(process.env.OPENCLAW_HOME, homeDir);
20884
+ return envOpenclawHome ?? homeDir;
20885
+ }
20886
+ function resolveDefaultOpenclawStateDir(openclawHomeDir) {
20887
+ const newStateDir = join6(openclawHomeDir, OPENCLAW_DIR_NAME);
20888
+ if (existsSync(newStateDir)) {
20889
+ return newStateDir;
20890
+ }
20891
+ for (const legacyDirName of LEGACY_OPENCLAW_STATE_DIR_NAMES) {
20892
+ const legacyStateDir = join6(openclawHomeDir, legacyDirName);
20893
+ if (existsSync(legacyStateDir)) {
20894
+ return legacyStateDir;
20895
+ }
20896
+ }
20897
+ return newStateDir;
20898
+ }
21210
20899
  function resolveOpenclawDir(openclawDir, homeDir) {
21211
20900
  if (typeof openclawDir === "string" && openclawDir.trim().length > 0) {
21212
- return openclawDir.trim();
20901
+ return resolveHomePrefixedPath(openclawDir, homeDir);
21213
20902
  }
21214
- return join7(homeDir, OPENCLAW_DIR_NAME);
20903
+ const envStateDir = readNonEmptyEnvPath(
20904
+ process.env.OPENCLAW_STATE_DIR ?? process.env.CLAWDBOT_STATE_DIR,
20905
+ homeDir
20906
+ );
20907
+ if (envStateDir !== void 0) {
20908
+ return envStateDir;
20909
+ }
20910
+ const envConfigPath = readNonEmptyEnvPath(
20911
+ process.env.OPENCLAW_CONFIG_PATH ?? process.env.CLAWDBOT_CONFIG_PATH,
20912
+ homeDir
20913
+ );
20914
+ if (envConfigPath !== void 0) {
20915
+ return dirname4(envConfigPath);
20916
+ }
20917
+ const openclawHomeDir = resolveOpenclawHomeDir(homeDir);
20918
+ return resolveDefaultOpenclawStateDir(openclawHomeDir);
21215
20919
  }
21216
20920
  function resolveAgentDirectory(homeDir, agentName) {
21217
- return join7(homeDir, CLAWDENTITY_DIR_NAME, AGENTS_DIR_NAME4, agentName);
20921
+ return join6(homeDir, CLAWDENTITY_DIR_NAME, AGENTS_DIR_NAME4, agentName);
21218
20922
  }
21219
20923
  function resolvePeersPath(homeDir) {
21220
- return join7(homeDir, CLAWDENTITY_DIR_NAME, PEERS_FILE_NAME);
20924
+ return join6(homeDir, CLAWDENTITY_DIR_NAME, PEERS_FILE_NAME);
21221
20925
  }
21222
- function resolveOpenclawConfigPath(openclawDir) {
21223
- return join7(openclawDir, OPENCLAW_CONFIG_FILE_NAME);
20926
+ function resolveOpenclawConfigPath(openclawDir, homeDir) {
20927
+ const envConfigPath = readNonEmptyEnvPath(
20928
+ process.env.OPENCLAW_CONFIG_PATH ?? process.env.CLAWDBOT_CONFIG_PATH,
20929
+ homeDir
20930
+ );
20931
+ if (envConfigPath !== void 0) {
20932
+ return envConfigPath;
20933
+ }
20934
+ const configCandidates = [
20935
+ join6(openclawDir, OPENCLAW_CONFIG_FILE_NAME),
20936
+ ...LEGACY_OPENCLAW_CONFIG_FILE_NAMES.map(
20937
+ (fileName) => join6(openclawDir, fileName)
20938
+ )
20939
+ ];
20940
+ for (const candidate of configCandidates) {
20941
+ if (existsSync(candidate)) {
20942
+ return candidate;
20943
+ }
20944
+ }
20945
+ return configCandidates[0];
21224
20946
  }
21225
20947
  function resolveDefaultTransformSource(openclawDir) {
21226
- return join7(
20948
+ return join6(
21227
20949
  openclawDir,
21228
20950
  "workspace",
21229
20951
  "skills",
@@ -21232,16 +20954,25 @@ function resolveDefaultTransformSource(openclawDir) {
21232
20954
  );
21233
20955
  }
21234
20956
  function resolveTransformTargetPath(openclawDir) {
21235
- return join7(openclawDir, "hooks", "transforms", RELAY_MODULE_FILE_NAME);
20957
+ return join6(openclawDir, "hooks", "transforms", RELAY_MODULE_FILE_NAME);
21236
20958
  }
21237
20959
  function resolveOpenclawAgentNamePath(homeDir) {
21238
- return join7(homeDir, CLAWDENTITY_DIR_NAME, OPENCLAW_AGENT_FILE_NAME);
20960
+ return join6(homeDir, CLAWDENTITY_DIR_NAME, OPENCLAW_AGENT_FILE_NAME);
21239
20961
  }
21240
20962
  function resolveRelayRuntimeConfigPath(homeDir) {
21241
- return join7(homeDir, CLAWDENTITY_DIR_NAME, OPENCLAW_RELAY_RUNTIME_FILE_NAME);
20963
+ return join6(homeDir, CLAWDENTITY_DIR_NAME, OPENCLAW_RELAY_RUNTIME_FILE_NAME2);
20964
+ }
20965
+ function resolveConnectorAssignmentsPath(homeDir) {
20966
+ return join6(homeDir, CLAWDENTITY_DIR_NAME, OPENCLAW_CONNECTORS_FILE_NAME2);
20967
+ }
20968
+ function resolveTransformRuntimePath(openclawDir) {
20969
+ return join6(openclawDir, "hooks", "transforms", RELAY_RUNTIME_FILE_NAME);
20970
+ }
20971
+ function resolveTransformPeersPath(openclawDir) {
20972
+ return join6(openclawDir, "hooks", "transforms", RELAY_PEERS_FILE_NAME);
21242
20973
  }
21243
20974
  async function readJsonFile(filePath) {
21244
- const raw = await readFile5(filePath, "utf8");
20975
+ const raw = await readFile4(filePath, "utf8");
21245
20976
  try {
21246
20977
  return JSON.parse(raw);
21247
20978
  } catch {
@@ -21253,13 +20984,13 @@ async function readJsonFile(filePath) {
21253
20984
  async function ensureLocalAgentCredentials(homeDir, agentName) {
21254
20985
  const agentDir = resolveAgentDirectory(homeDir, agentName);
21255
20986
  const requiredFiles = [
21256
- join7(agentDir, SECRET_KEY_FILE_NAME2),
21257
- join7(agentDir, AIT_FILE_NAME3)
20987
+ join6(agentDir, SECRET_KEY_FILE_NAME2),
20988
+ join6(agentDir, AIT_FILE_NAME3)
21258
20989
  ];
21259
20990
  for (const filePath of requiredFiles) {
21260
20991
  let content;
21261
20992
  try {
21262
- content = await readFile5(filePath, "utf8");
20993
+ content = await readFile4(filePath, "utf8");
21263
20994
  } catch (error48) {
21264
20995
  if (getErrorCode2(error48) === "ENOENT") {
21265
20996
  throw createCliError5(
@@ -21279,10 +21010,6 @@ async function ensureLocalAgentCredentials(homeDir, agentName) {
21279
21010
  }
21280
21011
  }
21281
21012
  }
21282
- function encodeInvitePayload(payload) {
21283
- const encoded = encodeBase64url(textEncoder2.encode(JSON.stringify(payload)));
21284
- return `${INVITE_CODE_PREFIX}${encoded}`;
21285
- }
21286
21013
  function decodeInvitePayload(code) {
21287
21014
  const rawCode = parseNonEmptyString7(code, "invite code");
21288
21015
  if (!rawCode.startsWith(INVITE_CODE_PREFIX)) {
@@ -21333,7 +21060,7 @@ async function loadPeersConfig(peersPath) {
21333
21060
  }
21334
21061
  throw error48;
21335
21062
  }
21336
- if (!isRecord8(parsed)) {
21063
+ if (!isRecord7(parsed)) {
21337
21064
  throw createCliError5(
21338
21065
  "CLI_OPENCLAW_INVALID_PEERS_CONFIG",
21339
21066
  "Peer config root must be a JSON object",
@@ -21344,7 +21071,7 @@ async function loadPeersConfig(peersPath) {
21344
21071
  if (peersValue === void 0) {
21345
21072
  return { peers: {} };
21346
21073
  }
21347
- if (!isRecord8(peersValue)) {
21074
+ if (!isRecord7(peersValue)) {
21348
21075
  throw createCliError5(
21349
21076
  "CLI_OPENCLAW_INVALID_PEERS_CONFIG",
21350
21077
  "Peer config peers field must be an object",
@@ -21354,7 +21081,7 @@ async function loadPeersConfig(peersPath) {
21354
21081
  const peers = {};
21355
21082
  for (const [alias, value] of Object.entries(peersValue)) {
21356
21083
  const normalizedAlias = parsePeerAlias(alias);
21357
- if (!isRecord8(value)) {
21084
+ if (!isRecord7(value)) {
21358
21085
  throw createCliError5(
21359
21086
  "CLI_OPENCLAW_INVALID_PEERS_CONFIG",
21360
21087
  "Peer entry must be an object",
@@ -21376,35 +21103,100 @@ async function savePeersConfig(peersPath, config2) {
21376
21103
  await writeSecureFile3(peersPath, `${JSON.stringify(config2, null, 2)}
21377
21104
  `);
21378
21105
  }
21379
- function parseRelayRuntimeEchoConfig(value, relayRuntimeConfigPath) {
21380
- if (value === void 0) {
21381
- return defaultRelayEchoConfig();
21382
- }
21383
- if (!isRecord8(value)) {
21106
+ function parseConnectorBaseUrlForAssignment(value, label) {
21107
+ return parseHttpUrl(value, {
21108
+ label,
21109
+ code: "CLI_OPENCLAW_INVALID_CONNECTOR_BASE_URL",
21110
+ message: "Connector base URL must be a valid URL"
21111
+ });
21112
+ }
21113
+ function parseConnectorAssignments(value, connectorAssignmentsPath) {
21114
+ if (!isRecord7(value)) {
21384
21115
  throw createCliError5(
21385
- "CLI_OPENCLAW_INVALID_RELAY_RUNTIME_CONFIG",
21386
- "Relay runtime echo config must be an object",
21387
- { relayRuntimeConfigPath }
21116
+ "CLI_OPENCLAW_INVALID_CONNECTOR_ASSIGNMENTS",
21117
+ "Connector assignments config must be an object",
21118
+ { connectorAssignmentsPath }
21388
21119
  );
21389
21120
  }
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)
21121
+ const agentsRaw = value.agents;
21122
+ if (!isRecord7(agentsRaw)) {
21123
+ return { agents: {} };
21124
+ }
21125
+ const agents = {};
21126
+ for (const [agentName, entryValue] of Object.entries(agentsRaw)) {
21127
+ if (!isRecord7(entryValue)) {
21128
+ throw createCliError5(
21129
+ "CLI_OPENCLAW_INVALID_CONNECTOR_ASSIGNMENTS",
21130
+ "Connector assignment entry must be an object",
21131
+ { connectorAssignmentsPath, agentName }
21132
+ );
21133
+ }
21134
+ const connectorBaseUrl = parseConnectorBaseUrlForAssignment(
21135
+ entryValue.connectorBaseUrl,
21136
+ "connectorBaseUrl"
21137
+ );
21138
+ const updatedAt = typeof entryValue.updatedAt === "string" && entryValue.updatedAt.trim().length > 0 ? entryValue.updatedAt.trim() : nowIso();
21139
+ agents[assertValidAgentName(agentName)] = {
21140
+ connectorBaseUrl,
21141
+ updatedAt
21397
21142
  };
21143
+ }
21144
+ return { agents };
21145
+ }
21146
+ async function loadConnectorAssignments(connectorAssignmentsPath) {
21147
+ let parsed;
21148
+ try {
21149
+ parsed = await readJsonFile(connectorAssignmentsPath);
21398
21150
  } 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
- );
21151
+ if (getErrorCode2(error48) === "ENOENT") {
21152
+ return { agents: {} };
21153
+ }
21154
+ throw error48;
21155
+ }
21156
+ return parseConnectorAssignments(parsed, connectorAssignmentsPath);
21157
+ }
21158
+ async function saveConnectorAssignments(connectorAssignmentsPath, config2) {
21159
+ await writeSecureFile3(
21160
+ connectorAssignmentsPath,
21161
+ `${JSON.stringify(config2, null, 2)}
21162
+ `
21163
+ );
21164
+ }
21165
+ function parseConnectorPortFromBaseUrl(baseUrl) {
21166
+ const parsed = new URL(baseUrl);
21167
+ if (parsed.port) {
21168
+ return Number(parsed.port);
21404
21169
  }
21170
+ return parsed.protocol === "https:" ? 443 : 80;
21171
+ }
21172
+ function allocateConnectorPort(assignments, agentName) {
21173
+ const existing = assignments.agents[agentName];
21174
+ if (existing) {
21175
+ return parseConnectorPortFromBaseUrl(existing.connectorBaseUrl);
21176
+ }
21177
+ const usedPorts = /* @__PURE__ */ new Set();
21178
+ for (const entry of Object.values(assignments.agents)) {
21179
+ usedPorts.add(parseConnectorPortFromBaseUrl(entry.connectorBaseUrl));
21180
+ }
21181
+ let nextPort = DEFAULT_CONNECTOR_PORT;
21182
+ while (usedPorts.has(nextPort)) {
21183
+ nextPort += 1;
21184
+ }
21185
+ return nextPort;
21186
+ }
21187
+ function buildConnectorBaseUrl(host, port) {
21188
+ return `http://${host}:${port}`;
21189
+ }
21190
+ function buildRelayConnectorBaseUrls(port) {
21191
+ return [
21192
+ buildConnectorBaseUrl(CONNECTOR_HOST_DOCKER, port),
21193
+ buildConnectorBaseUrl(CONNECTOR_HOST_DOCKER_GATEWAY, port),
21194
+ buildConnectorBaseUrl(CONNECTOR_HOST_LINUX_BRIDGE, port),
21195
+ buildConnectorBaseUrl(CONNECTOR_HOST_LOOPBACK, port)
21196
+ ];
21405
21197
  }
21406
21198
  function parseRelayRuntimeConfig(value, relayRuntimeConfigPath) {
21407
- if (!isRecord8(value)) {
21199
+ if (!isRecord7(value)) {
21408
21200
  throw createCliError5(
21409
21201
  "CLI_OPENCLAW_INVALID_RELAY_RUNTIME_CONFIG",
21410
21202
  "Relay runtime config must be an object",
@@ -21412,11 +21204,11 @@ function parseRelayRuntimeConfig(value, relayRuntimeConfigPath) {
21412
21204
  );
21413
21205
  }
21414
21206
  const updatedAt = typeof value.updatedAt === "string" && value.updatedAt.trim().length > 0 ? value.updatedAt.trim() : void 0;
21415
- const echo = parseRelayRuntimeEchoConfig(value.echo, relayRuntimeConfigPath);
21207
+ const openclawHookToken = typeof value.openclawHookToken === "string" && value.openclawHookToken.trim().length > 0 ? value.openclawHookToken.trim() : void 0;
21416
21208
  return {
21417
- openclawBaseUrl: parseOpenclawBaseUrl2(value.openclawBaseUrl),
21418
- updatedAt,
21419
- echo
21209
+ openclawBaseUrl: parseOpenclawBaseUrl(value.openclawBaseUrl),
21210
+ openclawHookToken,
21211
+ updatedAt
21420
21212
  };
21421
21213
  }
21422
21214
  async function loadRelayRuntimeConfig(relayRuntimeConfigPath) {
@@ -21431,11 +21223,11 @@ async function loadRelayRuntimeConfig(relayRuntimeConfigPath) {
21431
21223
  }
21432
21224
  return parseRelayRuntimeConfig(parsed, relayRuntimeConfigPath);
21433
21225
  }
21434
- async function saveRelayRuntimeConfig(relayRuntimeConfigPath, openclawBaseUrl, relayEchoConfig) {
21226
+ async function saveRelayRuntimeConfig(relayRuntimeConfigPath, openclawBaseUrl, openclawHookToken) {
21435
21227
  const config2 = {
21436
21228
  openclawBaseUrl,
21437
- updatedAt: nowIso(),
21438
- echo: relayEchoConfig
21229
+ ...openclawHookToken ? { openclawHookToken } : {},
21230
+ updatedAt: nowIso()
21439
21231
  };
21440
21232
  await writeSecureFile3(
21441
21233
  relayRuntimeConfigPath,
@@ -21443,13 +21235,13 @@ async function saveRelayRuntimeConfig(relayRuntimeConfigPath, openclawBaseUrl, r
21443
21235
  `
21444
21236
  );
21445
21237
  }
21446
- async function resolveOpenclawBaseUrl(input) {
21238
+ async function resolveOpenclawBaseUrl2(input) {
21447
21239
  if (typeof input.optionValue === "string" && input.optionValue.trim().length > 0) {
21448
- return parseOpenclawBaseUrl2(input.optionValue);
21240
+ return parseOpenclawBaseUrl(input.optionValue);
21449
21241
  }
21450
21242
  const envOpenclawBaseUrl = process.env.OPENCLAW_BASE_URL;
21451
21243
  if (typeof envOpenclawBaseUrl === "string" && envOpenclawBaseUrl.trim().length > 0) {
21452
- return parseOpenclawBaseUrl2(envOpenclawBaseUrl);
21244
+ return parseOpenclawBaseUrl(envOpenclawBaseUrl);
21453
21245
  }
21454
21246
  const existingConfig = await loadRelayRuntimeConfig(
21455
21247
  input.relayRuntimeConfigPath
@@ -21475,21 +21267,24 @@ function normalizeStringArrayWithValue(value, requiredValue) {
21475
21267
  normalized.add(requiredValue);
21476
21268
  return Array.from(normalized);
21477
21269
  }
21270
+ function generateOpenclawHookToken() {
21271
+ return randomBytes3(OPENCLAW_HOOK_TOKEN_BYTES).toString("hex");
21272
+ }
21478
21273
  function upsertRelayHookMapping(mappingsValue) {
21479
- const mappings = Array.isArray(mappingsValue) ? mappingsValue.filter(isRecord8).map((mapping) => ({ ...mapping })) : [];
21274
+ const mappings = Array.isArray(mappingsValue) ? mappingsValue.filter(isRecord7).map((mapping) => ({ ...mapping })) : [];
21480
21275
  const existingIndex = mappings.findIndex((mapping) => {
21481
21276
  if (mapping.id === HOOK_MAPPING_ID) {
21482
21277
  return true;
21483
21278
  }
21484
- if (!isRecord8(mapping.match)) {
21279
+ if (!isRecord7(mapping.match)) {
21485
21280
  return false;
21486
21281
  }
21487
21282
  return mapping.match.path === HOOK_PATH_SEND_TO_PEER;
21488
21283
  });
21489
- const baseMapping = existingIndex >= 0 && isRecord8(mappings[existingIndex]) ? mappings[existingIndex] : {};
21490
- const nextMatch = isRecord8(baseMapping.match) ? { ...baseMapping.match } : {};
21284
+ const baseMapping = existingIndex >= 0 && isRecord7(mappings[existingIndex]) ? mappings[existingIndex] : {};
21285
+ const nextMatch = isRecord7(baseMapping.match) ? { ...baseMapping.match } : {};
21491
21286
  nextMatch.path = HOOK_PATH_SEND_TO_PEER;
21492
- const nextTransform = isRecord8(baseMapping.transform) ? { ...baseMapping.transform } : {};
21287
+ const nextTransform = isRecord7(baseMapping.transform) ? { ...baseMapping.transform } : {};
21493
21288
  nextTransform.module = RELAY_MODULE_FILE_NAME;
21494
21289
  const relayMapping = {
21495
21290
  ...baseMapping,
@@ -21506,7 +21301,7 @@ function upsertRelayHookMapping(mappingsValue) {
21506
21301
  mappings.push(relayMapping);
21507
21302
  return mappings;
21508
21303
  }
21509
- async function patchOpenclawConfig(openclawConfigPath) {
21304
+ async function patchOpenclawConfig(openclawConfigPath, hookToken) {
21510
21305
  let config2;
21511
21306
  try {
21512
21307
  config2 = await readJsonFile(openclawConfigPath);
@@ -21520,15 +21315,19 @@ async function patchOpenclawConfig(openclawConfigPath) {
21520
21315
  }
21521
21316
  throw error48;
21522
21317
  }
21523
- if (!isRecord8(config2)) {
21318
+ if (!isRecord7(config2)) {
21524
21319
  throw createCliError5(
21525
21320
  "CLI_OPENCLAW_INVALID_CONFIG",
21526
21321
  "OpenClaw config root must be an object",
21527
21322
  { openclawConfigPath }
21528
21323
  );
21529
21324
  }
21530
- const hooks = isRecord8(config2.hooks) ? { ...config2.hooks } : {};
21325
+ const hooks = isRecord7(config2.hooks) ? { ...config2.hooks } : {};
21326
+ const existingHookToken = typeof hooks.token === "string" && hooks.token.trim().length > 0 ? hooks.token.trim() : void 0;
21327
+ const preferredHookToken = typeof hookToken === "string" && hookToken.trim().length > 0 ? hookToken.trim() : void 0;
21328
+ const resolvedHookToken = existingHookToken ?? preferredHookToken ?? generateOpenclawHookToken();
21531
21329
  hooks.enabled = true;
21330
+ hooks.token = resolvedHookToken;
21532
21331
  hooks.allowRequestSessionKey = false;
21533
21332
  hooks.allowedSessionKeyPrefixes = normalizeStringArrayWithValue(
21534
21333
  hooks.allowedSessionKeyPrefixes,
@@ -21545,6 +21344,9 @@ async function patchOpenclawConfig(openclawConfigPath) {
21545
21344
  `,
21546
21345
  "utf8"
21547
21346
  );
21347
+ return {
21348
+ hookToken: resolvedHookToken
21349
+ };
21548
21350
  }
21549
21351
  function toDoctorCheck(input) {
21550
21352
  return input;
@@ -21557,10 +21359,10 @@ function toDoctorResult(checks) {
21557
21359
  };
21558
21360
  }
21559
21361
  function isRelayHookMapping(value) {
21560
- if (!isRecord8(value)) {
21362
+ if (!isRecord7(value)) {
21561
21363
  return false;
21562
21364
  }
21563
- if (!isRecord8(value.match) || value.match.path !== HOOK_PATH_SEND_TO_PEER) {
21365
+ if (!isRecord7(value.match) || value.match.path !== HOOK_PATH_SEND_TO_PEER) {
21564
21366
  return false;
21565
21367
  }
21566
21368
  if (typeof value.id === "string" && value.id !== HOOK_MAPPING_ID) {
@@ -21569,7 +21371,7 @@ function isRelayHookMapping(value) {
21569
21371
  return true;
21570
21372
  }
21571
21373
  function hasRelayTransformModule(value) {
21572
- if (!isRecord8(value) || !isRecord8(value.transform)) {
21374
+ if (!isRecord7(value) || !isRecord7(value.transform)) {
21573
21375
  return false;
21574
21376
  }
21575
21377
  return value.transform.module === RELAY_MODULE_FILE_NAME;
@@ -21580,8 +21382,8 @@ function parseDoctorPeerAlias(peerAlias) {
21580
21382
  }
21581
21383
  return parsePeerAlias(peerAlias);
21582
21384
  }
21583
- function resolveHookToken(optionValue) {
21584
- const trimmedOption = optionValue?.trim();
21385
+ async function resolveHookToken(input) {
21386
+ const trimmedOption = input.optionValue?.trim();
21585
21387
  if (trimmedOption !== void 0 && trimmedOption.length > 0) {
21586
21388
  return trimmedOption;
21587
21389
  }
@@ -21589,6 +21391,12 @@ function resolveHookToken(optionValue) {
21589
21391
  if (envValue !== void 0 && envValue.length > 0) {
21590
21392
  return envValue;
21591
21393
  }
21394
+ const existingConfig = await loadRelayRuntimeConfig(
21395
+ input.relayRuntimeConfigPath
21396
+ );
21397
+ if (existingConfig?.openclawHookToken) {
21398
+ return existingConfig.openclawHookToken;
21399
+ }
21592
21400
  return void 0;
21593
21401
  }
21594
21402
  function resolveProbeMessage(optionValue) {
@@ -21686,7 +21494,7 @@ async function runOpenclawDoctor(options = {}) {
21686
21494
  const selectedAgentPath = resolveOpenclawAgentNamePath(homeDir);
21687
21495
  let selectedAgentName;
21688
21496
  try {
21689
- const selectedAgentRaw = await readFile5(selectedAgentPath, "utf8");
21497
+ const selectedAgentRaw = await readFile4(selectedAgentPath, "utf8");
21690
21498
  selectedAgentName = assertValidAgentName(selectedAgentRaw.trim());
21691
21499
  checks.push(
21692
21500
  toDoctorCheck({
@@ -21704,7 +21512,7 @@ async function runOpenclawDoctor(options = {}) {
21704
21512
  label: "Selected agent marker",
21705
21513
  status: "fail",
21706
21514
  message: missing ? `missing ${selectedAgentPath}` : "selected agent marker is invalid",
21707
- remediationHint: "Run: clawdentity openclaw setup <agentName> --invite-code <code>"
21515
+ remediationHint: OPENCLAW_SETUP_COMMAND_HINT
21708
21516
  })
21709
21517
  );
21710
21518
  }
@@ -21715,7 +21523,7 @@ async function runOpenclawDoctor(options = {}) {
21715
21523
  label: "Local agent credentials",
21716
21524
  status: "fail",
21717
21525
  message: "cannot validate credentials without selected agent marker",
21718
- remediationHint: "Run: clawdentity openclaw setup <agentName> --invite-code <code>"
21526
+ remediationHint: OPENCLAW_SETUP_COMMAND_HINT
21719
21527
  })
21720
21528
  );
21721
21529
  } else {
@@ -21757,7 +21565,7 @@ async function runOpenclawDoctor(options = {}) {
21757
21565
  label: "Peers map",
21758
21566
  status: "fail",
21759
21567
  message: `peer alias is missing: ${peerAlias}`,
21760
- remediationHint: "Run: clawdentity openclaw setup <agentName> --invite-code <code> --peer-alias <alias>",
21568
+ remediationHint: OPENCLAW_SETUP_COMMAND_HINT,
21761
21569
  details: { peersPath, peerAlias }
21762
21570
  })
21763
21571
  );
@@ -21779,7 +21587,7 @@ async function runOpenclawDoctor(options = {}) {
21779
21587
  label: "Peers map",
21780
21588
  status: "fail",
21781
21589
  message: "no peers are configured",
21782
- remediationHint: "Run: clawdentity openclaw setup <agentName> --invite-code <code>",
21590
+ remediationHint: OPENCLAW_SETUP_COMMAND_HINT,
21783
21591
  details: { peersPath }
21784
21592
  })
21785
21593
  );
@@ -21807,17 +21615,28 @@ async function runOpenclawDoctor(options = {}) {
21807
21615
  );
21808
21616
  }
21809
21617
  const transformTargetPath = resolveTransformTargetPath(openclawDir);
21618
+ const relayTransformRuntimePath = resolveTransformRuntimePath(openclawDir);
21619
+ const relayTransformPeersPath = resolveTransformPeersPath(openclawDir);
21810
21620
  try {
21811
- const transformContents = await readFile5(transformTargetPath, "utf8");
21812
- if (transformContents.trim().length === 0) {
21621
+ const transformContents = await readFile4(transformTargetPath, "utf8");
21622
+ const runtimeContents = await readFile4(relayTransformRuntimePath, "utf8");
21623
+ const peersSnapshotContents = await readFile4(
21624
+ relayTransformPeersPath,
21625
+ "utf8"
21626
+ );
21627
+ if (transformContents.trim().length === 0 || runtimeContents.trim().length === 0 || peersSnapshotContents.trim().length === 0) {
21813
21628
  checks.push(
21814
21629
  toDoctorCheck({
21815
21630
  id: "state.transform",
21816
21631
  label: "Relay transform",
21817
21632
  status: "fail",
21818
- message: `transform file is empty: ${transformTargetPath}`,
21633
+ message: "relay transform artifacts are missing or empty",
21819
21634
  remediationHint: "Run: npm install clawdentity --skill",
21820
- details: { transformTargetPath }
21635
+ details: {
21636
+ transformTargetPath,
21637
+ relayTransformRuntimePath,
21638
+ relayTransformPeersPath
21639
+ }
21821
21640
  })
21822
21641
  );
21823
21642
  } else {
@@ -21826,8 +21645,12 @@ async function runOpenclawDoctor(options = {}) {
21826
21645
  id: "state.transform",
21827
21646
  label: "Relay transform",
21828
21647
  status: "pass",
21829
- message: "relay transform file exists",
21830
- details: { transformTargetPath }
21648
+ message: "relay transform artifacts are present",
21649
+ details: {
21650
+ transformTargetPath,
21651
+ relayTransformRuntimePath,
21652
+ relayTransformPeersPath
21653
+ }
21831
21654
  })
21832
21655
  );
21833
21656
  }
@@ -21837,20 +21660,26 @@ async function runOpenclawDoctor(options = {}) {
21837
21660
  id: "state.transform",
21838
21661
  label: "Relay transform",
21839
21662
  status: "fail",
21840
- message: `missing transform file: ${transformTargetPath}`,
21663
+ message: "missing relay transform artifacts",
21841
21664
  remediationHint: "Run: npm install clawdentity --skill",
21842
- details: { transformTargetPath }
21665
+ details: {
21666
+ transformTargetPath,
21667
+ relayTransformRuntimePath,
21668
+ relayTransformPeersPath
21669
+ }
21843
21670
  })
21844
21671
  );
21845
21672
  }
21846
- const openclawConfigPath = resolveOpenclawConfigPath(openclawDir);
21673
+ const openclawConfigPath = resolveOpenclawConfigPath(openclawDir, homeDir);
21847
21674
  try {
21848
21675
  const openclawConfig = await readJsonFile(openclawConfigPath);
21849
- if (!isRecord8(openclawConfig)) {
21676
+ if (!isRecord7(openclawConfig)) {
21850
21677
  throw new Error("root");
21851
21678
  }
21852
- const hooks = isRecord8(openclawConfig.hooks) ? openclawConfig.hooks : {};
21853
- const mappings = Array.isArray(hooks.mappings) ? hooks.mappings.filter(isRecord8) : [];
21679
+ const hooks = isRecord7(openclawConfig.hooks) ? openclawConfig.hooks : {};
21680
+ const hooksEnabled = hooks.enabled === true;
21681
+ const hookToken = typeof hooks.token === "string" && hooks.token.trim().length > 0 ? hooks.token.trim() : void 0;
21682
+ const mappings = Array.isArray(hooks.mappings) ? hooks.mappings.filter(isRecord7) : [];
21854
21683
  const relayMapping = mappings.find(
21855
21684
  (mapping) => isRelayHookMapping(mapping)
21856
21685
  );
@@ -21861,7 +21690,7 @@ async function runOpenclawDoctor(options = {}) {
21861
21690
  label: "OpenClaw hook mapping",
21862
21691
  status: "fail",
21863
21692
  message: `missing send-to-peer mapping in ${openclawConfigPath}`,
21864
- remediationHint: "Run: clawdentity openclaw setup <agentName> --invite-code <code>",
21693
+ remediationHint: OPENCLAW_SETUP_COMMAND_HINT,
21865
21694
  details: { openclawConfigPath }
21866
21695
  })
21867
21696
  );
@@ -21876,6 +21705,39 @@ async function runOpenclawDoctor(options = {}) {
21876
21705
  })
21877
21706
  );
21878
21707
  }
21708
+ if (!hooksEnabled) {
21709
+ checks.push(
21710
+ toDoctorCheck({
21711
+ id: "state.hookToken",
21712
+ label: "OpenClaw hook auth",
21713
+ status: "fail",
21714
+ message: `hooks.enabled is not true in ${openclawConfigPath}`,
21715
+ remediationHint: OPENCLAW_SETUP_RESTART_COMMAND_HINT,
21716
+ details: { openclawConfigPath }
21717
+ })
21718
+ );
21719
+ } else if (hookToken === void 0) {
21720
+ checks.push(
21721
+ toDoctorCheck({
21722
+ id: "state.hookToken",
21723
+ label: "OpenClaw hook auth",
21724
+ status: "fail",
21725
+ message: `hooks.token is missing in ${openclawConfigPath}`,
21726
+ remediationHint: OPENCLAW_SETUP_RESTART_COMMAND_HINT,
21727
+ details: { openclawConfigPath }
21728
+ })
21729
+ );
21730
+ } else {
21731
+ checks.push(
21732
+ toDoctorCheck({
21733
+ id: "state.hookToken",
21734
+ label: "OpenClaw hook auth",
21735
+ status: "pass",
21736
+ message: "hooks token is configured",
21737
+ details: { openclawConfigPath }
21738
+ })
21739
+ );
21740
+ }
21879
21741
  } catch {
21880
21742
  checks.push(
21881
21743
  toDoctorCheck({
@@ -21883,14 +21745,24 @@ async function runOpenclawDoctor(options = {}) {
21883
21745
  label: "OpenClaw hook mapping",
21884
21746
  status: "fail",
21885
21747
  message: `unable to read ${openclawConfigPath}`,
21886
- remediationHint: "Ensure ~/.openclaw/openclaw.json exists and rerun openclaw setup",
21748
+ remediationHint: "Ensure the OpenClaw config file exists (OPENCLAW_CONFIG_PATH/CLAWDBOT_CONFIG_PATH, or state dir) and rerun openclaw setup",
21749
+ details: { openclawConfigPath }
21750
+ })
21751
+ );
21752
+ checks.push(
21753
+ toDoctorCheck({
21754
+ id: "state.hookToken",
21755
+ label: "OpenClaw hook auth",
21756
+ status: "fail",
21757
+ message: `unable to read ${openclawConfigPath}`,
21758
+ remediationHint: "Ensure the OpenClaw config file exists (OPENCLAW_CONFIG_PATH/CLAWDBOT_CONFIG_PATH, or state dir) and rerun openclaw setup",
21887
21759
  details: { openclawConfigPath }
21888
21760
  })
21889
21761
  );
21890
21762
  }
21891
21763
  const relayRuntimeConfigPath = resolveRelayRuntimeConfigPath(homeDir);
21892
21764
  try {
21893
- const openclawBaseUrl = await resolveOpenclawBaseUrl({
21765
+ const openclawBaseUrl = await resolveOpenclawBaseUrl2({
21894
21766
  relayRuntimeConfigPath
21895
21767
  });
21896
21768
  checks.push(
@@ -21908,7 +21780,7 @@ async function runOpenclawDoctor(options = {}) {
21908
21780
  label: "OpenClaw base URL",
21909
21781
  status: "fail",
21910
21782
  message: `unable to resolve OpenClaw base URL from ${relayRuntimeConfigPath}`,
21911
- remediationHint: "Run: clawdentity openclaw setup <agentName> --invite-code <code> --openclaw-base-url <url>"
21783
+ remediationHint: OPENCLAW_SETUP_WITH_BASE_URL_HINT
21912
21784
  })
21913
21785
  );
21914
21786
  }
@@ -21924,7 +21796,13 @@ function parseRelayProbeFailure(input) {
21924
21796
  if (input.status === 404) {
21925
21797
  return {
21926
21798
  message: "OpenClaw send-to-peer hook is unavailable",
21927
- remediationHint: "Run: clawdentity openclaw setup <agentName> --invite-code <code>"
21799
+ remediationHint: OPENCLAW_SETUP_COMMAND_HINT
21800
+ };
21801
+ }
21802
+ if (input.status === 405) {
21803
+ return {
21804
+ message: "OpenClaw send-to-peer hook is not enabled for POST requests",
21805
+ remediationHint: `${OPENCLAW_SETUP_COMMAND_HINT}, then restart OpenClaw`
21928
21806
  };
21929
21807
  }
21930
21808
  if (input.status === 500) {
@@ -21952,7 +21830,7 @@ async function runOpenclawRelayTest(options) {
21952
21830
  const relayRuntimeConfigPath = resolveRelayRuntimeConfigPath(homeDir);
21953
21831
  let openclawBaseUrl = DEFAULT_OPENCLAW_BASE_URL2;
21954
21832
  try {
21955
- openclawBaseUrl = await resolveOpenclawBaseUrl({
21833
+ openclawBaseUrl = await resolveOpenclawBaseUrl2({
21956
21834
  optionValue: options.openclawBaseUrl,
21957
21835
  relayRuntimeConfigPath
21958
21836
  });
@@ -21982,7 +21860,10 @@ async function runOpenclawRelayTest(options) {
21982
21860
  preflight
21983
21861
  };
21984
21862
  }
21985
- const hookToken = resolveHookToken(options.hookToken);
21863
+ const hookToken = await resolveHookToken({
21864
+ optionValue: options.hookToken,
21865
+ relayRuntimeConfigPath
21866
+ });
21986
21867
  const fetchImpl = options.fetchImpl ?? globalThis.fetch;
21987
21868
  if (typeof fetchImpl !== "function") {
21988
21869
  return {
@@ -22048,56 +21929,53 @@ async function runOpenclawRelayTest(options) {
22048
21929
  preflight
22049
21930
  };
22050
21931
  }
22051
- function createOpenclawInviteCode(options) {
22052
- const did = parseAgentDid2(options.did, "invite did");
22053
- const proxyUrl = parseProxyUrl(options.proxyUrl);
22054
- const peerAlias = options.peerAlias === void 0 ? void 0 : parsePeerAlias(options.peerAlias);
22055
- const name = parseOptionalName(options.name);
22056
- const payload = parseInvitePayload({
22057
- v: 1,
22058
- issuedAt: nowIso(),
22059
- did,
22060
- proxyUrl,
22061
- alias: peerAlias,
22062
- name
22063
- });
22064
- const result = {
22065
- code: encodeInvitePayload(payload),
22066
- did: payload.did,
22067
- proxyUrl: payload.proxyUrl,
22068
- peerAlias: payload.alias,
22069
- name: payload.name
21932
+ function resolveOpenclawSetupPeerInput(options) {
21933
+ const inviteCode = options.inviteCode?.trim();
21934
+ const invite = inviteCode !== void 0 && inviteCode.length > 0 ? decodeInvitePayload(inviteCode) : void 0;
21935
+ const peerAliasCandidate = options.peerAlias ?? invite?.alias;
21936
+ const peerDidCandidate = options.peerDid ?? invite?.did;
21937
+ const peerProxyUrlCandidate = options.peerProxyUrl ?? invite?.proxyUrl;
21938
+ const peerNameCandidate = options.peerName ?? invite?.name;
21939
+ const missingFields = [];
21940
+ if (peerAliasCandidate === void 0 || peerAliasCandidate.trim().length === 0) {
21941
+ missingFields.push("peerAlias");
21942
+ }
21943
+ if (peerDidCandidate === void 0 || peerDidCandidate.trim().length === 0) {
21944
+ missingFields.push("peerDid");
21945
+ }
21946
+ if (peerProxyUrlCandidate === void 0 || peerProxyUrlCandidate.trim().length === 0) {
21947
+ missingFields.push("peerProxyUrl");
21948
+ }
21949
+ if (missingFields.length > 0) {
21950
+ throw createCliError5(
21951
+ "CLI_OPENCLAW_PEER_INPUT_REQUIRED",
21952
+ "Peer routing details are required. Provide --peer-alias, --peer-did, and --peer-proxy-url.",
21953
+ { missingFields }
21954
+ );
21955
+ }
21956
+ return {
21957
+ peerAlias: parsePeerAlias(peerAliasCandidate),
21958
+ peerDid: parseAgentDid2(peerDidCandidate, "peer DID"),
21959
+ peerProxyUrl: parseProxyUrl(peerProxyUrlCandidate),
21960
+ peerName: parseOptionalName(peerNameCandidate)
22070
21961
  };
22071
- return result;
22072
21962
  }
22073
- async function setupOpenclawRelayFromInvite(agentName, options) {
21963
+ async function setupOpenclawRelay(agentName, options) {
22074
21964
  const normalizedAgentName = assertValidAgentName(agentName);
22075
21965
  const homeDir = resolveHomeDir(options.homeDir);
22076
21966
  const openclawDir = resolveOpenclawDir(options.openclawDir, homeDir);
22077
- const openclawConfigPath = resolveOpenclawConfigPath(openclawDir);
21967
+ const openclawConfigPath = resolveOpenclawConfigPath(openclawDir, homeDir);
22078
21968
  const transformSource = typeof options.transformSource === "string" && options.transformSource.trim().length > 0 ? options.transformSource.trim() : resolveDefaultTransformSource(openclawDir);
22079
21969
  const transformTargetPath = resolveTransformTargetPath(openclawDir);
22080
21970
  const relayRuntimeConfigPath = resolveRelayRuntimeConfigPath(homeDir);
22081
21971
  const existingRelayRuntimeConfig = await loadRelayRuntimeConfig(
22082
21972
  relayRuntimeConfigPath
22083
21973
  );
22084
- const openclawBaseUrl = await resolveOpenclawBaseUrl({
21974
+ const openclawBaseUrl = await resolveOpenclawBaseUrl2({
22085
21975
  optionValue: options.openclawBaseUrl,
22086
21976
  relayRuntimeConfigPath
22087
21977
  });
22088
- const relayEchoConfig = resolveSetupRelayEchoConfig({
22089
- existingConfig: existingRelayRuntimeConfig,
22090
- options
22091
- });
22092
- const invite = decodeInvitePayload(options.inviteCode);
22093
- const peerAliasCandidate = options.peerAlias ?? invite.alias;
22094
- if (!peerAliasCandidate) {
22095
- throw createCliError5(
22096
- "CLI_OPENCLAW_PEER_ALIAS_REQUIRED",
22097
- "Peer alias is required. Include alias in invite code or pass --peer-alias."
22098
- );
22099
- }
22100
- const peerAlias = parsePeerAlias(peerAliasCandidate);
21978
+ const peerInput = resolveOpenclawSetupPeerInput(options);
22101
21979
  await ensureLocalAgentCredentials(homeDir, normalizedAgentName);
22102
21980
  await mkdir5(dirname4(transformTargetPath), { recursive: true });
22103
21981
  try {
@@ -22112,65 +21990,103 @@ async function setupOpenclawRelayFromInvite(agentName, options) {
22112
21990
  }
22113
21991
  throw error48;
22114
21992
  }
22115
- await patchOpenclawConfig(openclawConfigPath);
21993
+ const patchedOpenclawConfig = await patchOpenclawConfig(
21994
+ openclawConfigPath,
21995
+ existingRelayRuntimeConfig?.openclawHookToken
21996
+ );
22116
21997
  const peersPath = resolvePeersPath(homeDir);
22117
21998
  const peers = await loadPeersConfig(peersPath);
22118
- peers.peers[peerAlias] = invite.name === void 0 ? { did: invite.did, proxyUrl: invite.proxyUrl } : { did: invite.did, proxyUrl: invite.proxyUrl, name: invite.name };
21999
+ peers.peers[peerInput.peerAlias] = peerInput.peerName === void 0 ? { did: peerInput.peerDid, proxyUrl: peerInput.peerProxyUrl } : {
22000
+ did: peerInput.peerDid,
22001
+ proxyUrl: peerInput.peerProxyUrl,
22002
+ name: peerInput.peerName
22003
+ };
22119
22004
  await savePeersConfig(peersPath, peers);
22005
+ const relayTransformPeersPath = resolveTransformPeersPath(openclawDir);
22006
+ await writeSecureFile3(
22007
+ relayTransformPeersPath,
22008
+ `${JSON.stringify(peers, null, 2)}
22009
+ `
22010
+ );
22011
+ const connectorAssignmentsPath = resolveConnectorAssignmentsPath(homeDir);
22012
+ const connectorAssignments = await loadConnectorAssignments(
22013
+ connectorAssignmentsPath
22014
+ );
22015
+ const connectorPort = allocateConnectorPort(
22016
+ connectorAssignments,
22017
+ normalizedAgentName
22018
+ );
22019
+ const connectorBaseUrl = buildConnectorBaseUrl(
22020
+ CONNECTOR_HOST_LOOPBACK,
22021
+ connectorPort
22022
+ );
22023
+ connectorAssignments.agents[normalizedAgentName] = {
22024
+ connectorBaseUrl,
22025
+ updatedAt: nowIso()
22026
+ };
22027
+ await saveConnectorAssignments(
22028
+ connectorAssignmentsPath,
22029
+ connectorAssignments
22030
+ );
22031
+ const relayTransformRuntimePath = resolveTransformRuntimePath(openclawDir);
22032
+ await writeSecureFile3(
22033
+ relayTransformRuntimePath,
22034
+ `${JSON.stringify(
22035
+ {
22036
+ version: 1,
22037
+ connectorBaseUrl: buildRelayConnectorBaseUrls(connectorPort)[0],
22038
+ connectorBaseUrls: buildRelayConnectorBaseUrls(connectorPort),
22039
+ connectorPath: DEFAULT_CONNECTOR_OUTBOUND_PATH3,
22040
+ peersConfigPath: RELAY_PEERS_FILE_NAME,
22041
+ updatedAt: nowIso()
22042
+ },
22043
+ null,
22044
+ 2
22045
+ )}
22046
+ `
22047
+ );
22120
22048
  const agentNamePath = resolveOpenclawAgentNamePath(homeDir);
22121
22049
  await writeSecureFile3(agentNamePath, `${normalizedAgentName}
22122
22050
  `);
22123
22051
  await saveRelayRuntimeConfig(
22124
22052
  relayRuntimeConfigPath,
22125
22053
  openclawBaseUrl,
22126
- relayEchoConfig
22054
+ patchedOpenclawConfig.hookToken
22127
22055
  );
22128
22056
  logger8.info("cli.openclaw_setup_completed", {
22129
22057
  agentName: normalizedAgentName,
22130
- peerAlias,
22131
- peerDid: invite.did,
22058
+ peerAlias: peerInput.peerAlias,
22059
+ peerDid: peerInput.peerDid,
22060
+ peerProxyUrl: peerInput.peerProxyUrl,
22132
22061
  openclawConfigPath,
22133
22062
  transformTargetPath,
22063
+ relayTransformRuntimePath,
22064
+ relayTransformPeersPath,
22134
22065
  openclawBaseUrl,
22135
- relayRuntimeConfigPath,
22136
- relayEchoConfig
22066
+ connectorBaseUrl,
22067
+ relayRuntimeConfigPath
22137
22068
  });
22138
22069
  return {
22139
- peerAlias,
22140
- peerDid: invite.did,
22141
- peerProxyUrl: invite.proxyUrl,
22070
+ peerAlias: peerInput.peerAlias,
22071
+ peerDid: peerInput.peerDid,
22072
+ peerProxyUrl: peerInput.peerProxyUrl,
22142
22073
  openclawConfigPath,
22143
22074
  transformTargetPath,
22075
+ relayTransformRuntimePath,
22076
+ relayTransformPeersPath,
22144
22077
  openclawBaseUrl,
22145
- relayRuntimeConfigPath,
22146
- relayEcho: relayEchoConfig
22078
+ connectorBaseUrl,
22079
+ relayRuntimeConfigPath
22147
22080
  };
22148
22081
  }
22149
22082
  var createOpenclawCommand = () => {
22150
22083
  const openclawCommand = new Command7("openclaw").description(
22151
- "Manage OpenClaw invite codes and relay setup"
22152
- );
22153
- openclawCommand.command("invite").description("Create an invite code for peer relay onboarding").requiredOption("--did <did>", "Peer agent DID (did:claw:agent:...)").requiredOption(
22154
- "--proxy-url <url>",
22155
- "Public proxy URL ending in /hooks/agent"
22156
- ).option("--peer-alias <alias>", "Suggested peer alias for the receiver").option("--name <displayName>", "Human-friendly peer display name").action(
22157
- withErrorHandling(
22158
- "openclaw invite",
22159
- async (options) => {
22160
- const invite = createOpenclawInviteCode(options);
22161
- writeStdoutLine(`Invite code: ${invite.code}`);
22162
- writeStdoutLine(`Agent DID: ${invite.did}`);
22163
- writeStdoutLine(`Proxy URL: ${invite.proxyUrl}`);
22164
- if (invite.peerAlias) {
22165
- writeStdoutLine(`Suggested Alias: ${invite.peerAlias}`);
22166
- }
22167
- }
22168
- )
22084
+ "Manage OpenClaw relay setup"
22169
22085
  );
22170
- openclawCommand.command("setup <agentName>").description("Apply OpenClaw relay setup using an invite code").requiredOption(
22171
- "--invite-code <code>",
22172
- "Invite code shared by peer operator"
22173
- ).option("--peer-alias <alias>", "Override peer alias for local routing").option(
22086
+ openclawCommand.command("setup <agentName>").description("Apply OpenClaw relay setup using peer routing details").requiredOption("--peer-alias <alias>", "Peer alias for local routing").requiredOption("--peer-did <did>", "Peer agent DID (did:claw:agent:...)").requiredOption(
22087
+ "--peer-proxy-url <url>",
22088
+ "Peer proxy URL ending in /hooks/agent"
22089
+ ).option("--peer-name <displayName>", "Human-friendly peer display name").option(
22174
22090
  "--openclaw-dir <path>",
22175
22091
  "OpenClaw state directory (default ~/.openclaw)"
22176
22092
  ).option(
@@ -22179,25 +22095,11 @@ var createOpenclawCommand = () => {
22179
22095
  ).option(
22180
22096
  "--openclaw-base-url <url>",
22181
22097
  "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
22196
22098
  ).action(
22197
22099
  withErrorHandling(
22198
22100
  "openclaw setup",
22199
22101
  async (agentName, options) => {
22200
- const result = await setupOpenclawRelayFromInvite(agentName, options);
22102
+ const result = await setupOpenclawRelay(agentName, options);
22201
22103
  writeStdoutLine(`Peer alias configured: ${result.peerAlias}`);
22202
22104
  writeStdoutLine(`Peer DID: ${result.peerDid}`);
22203
22105
  writeStdoutLine(`Peer proxy URL: ${result.peerProxyUrl}`);
@@ -22205,17 +22107,14 @@ var createOpenclawCommand = () => {
22205
22107
  `Updated OpenClaw config: ${result.openclawConfigPath}`
22206
22108
  );
22207
22109
  writeStdoutLine(`Installed transform: ${result.transformTargetPath}`);
22208
- writeStdoutLine(`OpenClaw base URL: ${result.openclawBaseUrl}`);
22209
22110
  writeStdoutLine(
22210
- `Relay echo enabled: ${result.relayEcho.enabled ? "true" : "false"}`
22111
+ `Transform runtime config: ${result.relayTransformRuntimePath}`
22211
22112
  );
22212
- writeStdoutLine(`Relay echo channel: ${result.relayEcho.channel}`);
22213
22113
  writeStdoutLine(
22214
- `Relay echo target: ${result.relayEcho.to ?? "(last route default)"}`
22215
- );
22216
- writeStdoutLine(
22217
- `Relay echo max length: ${result.relayEcho.maxLength}`
22114
+ `Transform peers snapshot: ${result.relayTransformPeersPath}`
22218
22115
  );
22116
+ writeStdoutLine(`Connector base URL: ${result.connectorBaseUrl}`);
22117
+ writeStdoutLine(`OpenClaw base URL: ${result.openclawBaseUrl}`);
22219
22118
  writeStdoutLine(
22220
22119
  `Relay runtime config: ${result.relayRuntimeConfigPath}`
22221
22120
  );
@@ -22286,9 +22185,9 @@ var createOpenclawCommand = () => {
22286
22185
  };
22287
22186
 
22288
22187
  // src/commands/pair.ts
22289
- import { randomBytes as randomBytes3 } from "crypto";
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";
22188
+ import { randomBytes as randomBytes4 } from "crypto";
22189
+ import { mkdir as mkdir6, readdir, readFile as readFile5, unlink as unlink2, writeFile as writeFile6 } from "fs/promises";
22190
+ import { dirname as dirname5, join as join7, resolve } from "path";
22292
22191
  import { Command as Command8 } from "commander";
22293
22192
  import jsQR from "jsqr";
22294
22193
  import { PNG } from "pngjs";
@@ -22305,7 +22204,7 @@ var NONCE_SIZE2 = 24;
22305
22204
  var PAIRING_TICKET_PREFIX = "clwpair1_";
22306
22205
  var PAIRING_QR_MAX_AGE_SECONDS = 900;
22307
22206
  var PAIRING_QR_FILENAME_PATTERN = /-pair-(\d+)\.png$/;
22308
- var isRecord9 = (value) => {
22207
+ var isRecord8 = (value) => {
22309
22208
  return typeof value === "object" && value !== null;
22310
22209
  };
22311
22210
  function createCliError6(code, message2) {
@@ -22372,7 +22271,7 @@ function toPathWithQuery3(url2) {
22372
22271
  return `${parsed.pathname}${parsed.search}`;
22373
22272
  }
22374
22273
  function extractErrorCode(payload) {
22375
- if (!isRecord9(payload)) {
22274
+ if (!isRecord8(payload)) {
22376
22275
  return void 0;
22377
22276
  }
22378
22277
  const envelope = payload;
@@ -22383,7 +22282,7 @@ function extractErrorCode(payload) {
22383
22282
  return code.length > 0 ? code : void 0;
22384
22283
  }
22385
22284
  function extractErrorMessage(payload) {
22386
- if (!isRecord9(payload)) {
22285
+ if (!isRecord8(payload)) {
22387
22286
  return void 0;
22388
22287
  }
22389
22288
  const envelope = payload;
@@ -22451,7 +22350,7 @@ function mapConfirmPairError(status, payload) {
22451
22350
  return `Pair confirm failed (${status})`;
22452
22351
  }
22453
22352
  function parsePairStartResponse(payload) {
22454
- if (!isRecord9(payload)) {
22353
+ if (!isRecord8(payload)) {
22455
22354
  throw createCliError6(
22456
22355
  "CLI_PAIR_START_INVALID_RESPONSE",
22457
22356
  "Pair start response is invalid"
@@ -22473,7 +22372,7 @@ function parsePairStartResponse(payload) {
22473
22372
  };
22474
22373
  }
22475
22374
  function parsePairConfirmResponse(payload) {
22476
- if (!isRecord9(payload)) {
22375
+ if (!isRecord8(payload)) {
22477
22376
  throw createCliError6(
22478
22377
  "CLI_PAIR_CONFIRM_INVALID_RESPONSE",
22479
22378
  "Pair confirm response is invalid"
@@ -22495,16 +22394,16 @@ function parsePairConfirmResponse(payload) {
22495
22394
  };
22496
22395
  }
22497
22396
  async function readAgentProofMaterial(agentName, dependencies) {
22498
- const readFileImpl = dependencies.readFileImpl ?? readFile6;
22397
+ const readFileImpl = dependencies.readFileImpl ?? readFile5;
22499
22398
  const getConfigDirImpl = dependencies.getConfigDirImpl ?? getConfigDir;
22500
22399
  const normalizedAgentName = assertValidAgentName(agentName);
22501
- const agentDir = join8(
22400
+ const agentDir = join7(
22502
22401
  getConfigDirImpl(),
22503
22402
  AGENTS_DIR_NAME5,
22504
22403
  normalizedAgentName
22505
22404
  );
22506
- const aitPath = join8(agentDir, AIT_FILE_NAME4);
22507
- const secretKeyPath = join8(agentDir, SECRET_KEY_FILE_NAME3);
22405
+ const aitPath = join7(agentDir, AIT_FILE_NAME4);
22406
+ const secretKeyPath = join7(agentDir, SECRET_KEY_FILE_NAME3);
22508
22407
  let ait;
22509
22408
  try {
22510
22409
  ait = (await readFileImpl(aitPath, "utf-8")).trim();
@@ -22618,8 +22517,8 @@ async function persistPairingQr(input) {
22618
22517
  const writeFileImpl = input.dependencies.writeFileImpl ?? writeFile6;
22619
22518
  const getConfigDirImpl = input.dependencies.getConfigDirImpl ?? getConfigDir;
22620
22519
  const qrEncodeImpl = input.dependencies.qrEncodeImpl ?? encodeTicketQrPng;
22621
- const baseDir = join8(getConfigDirImpl(), PAIRING_QR_DIR_NAME);
22622
- const outputPath = parseNonEmptyString8(input.qrOutput) ? resolve(input.qrOutput ?? "") : join8(
22520
+ const baseDir = join7(getConfigDirImpl(), PAIRING_QR_DIR_NAME);
22521
+ const outputPath = parseNonEmptyString8(input.qrOutput) ? resolve(input.qrOutput ?? "") : join7(
22623
22522
  baseDir,
22624
22523
  `${assertValidAgentName(input.agentName)}-pair-${input.nowSeconds}.png`
22625
22524
  );
@@ -22645,7 +22544,7 @@ async function persistPairingQr(input) {
22645
22544
  if (issuedAtSeconds + PAIRING_QR_MAX_AGE_SECONDS > input.nowSeconds) {
22646
22545
  continue;
22647
22546
  }
22648
- const stalePath = join8(baseDir, fileName);
22547
+ const stalePath = join7(baseDir, fileName);
22649
22548
  await unlinkImpl(stalePath).catch((error48) => {
22650
22549
  const nodeError = error48;
22651
22550
  if (nodeError.code === "ENOENT") {
@@ -22690,7 +22589,7 @@ async function startPairing(agentName, options, dependencies = {}) {
22690
22589
  const fetchImpl = dependencies.fetchImpl ?? fetch;
22691
22590
  const resolveConfigImpl = dependencies.resolveConfigImpl ?? resolveConfig;
22692
22591
  const nowSecondsImpl = dependencies.nowSecondsImpl ?? (() => Math.floor(Date.now() / 1e3));
22693
- const nonceFactoryImpl = dependencies.nonceFactoryImpl ?? (() => randomBytes3(NONCE_SIZE2).toString("base64url"));
22592
+ const nonceFactoryImpl = dependencies.nonceFactoryImpl ?? (() => randomBytes4(NONCE_SIZE2).toString("base64url"));
22694
22593
  const ttlSeconds = parseTtlSeconds(options.ttlSeconds);
22695
22594
  const proxyUrl = resolveProxyUrl(options.proxyUrl);
22696
22595
  const config2 = await resolveConfigImpl();
@@ -22757,8 +22656,8 @@ async function startPairing(agentName, options, dependencies = {}) {
22757
22656
  async function confirmPairing(agentName, options, dependencies = {}) {
22758
22657
  const fetchImpl = dependencies.fetchImpl ?? fetch;
22759
22658
  const nowSecondsImpl = dependencies.nowSecondsImpl ?? (() => Math.floor(Date.now() / 1e3));
22760
- const nonceFactoryImpl = dependencies.nonceFactoryImpl ?? (() => randomBytes3(NONCE_SIZE2).toString("base64url"));
22761
- const readFileImpl = dependencies.readFileImpl ?? readFile6;
22659
+ const nonceFactoryImpl = dependencies.nonceFactoryImpl ?? (() => randomBytes4(NONCE_SIZE2).toString("base64url"));
22660
+ const readFileImpl = dependencies.readFileImpl ?? readFile5;
22762
22661
  const qrDecodeImpl = dependencies.qrDecodeImpl ?? decodeTicketFromPng;
22763
22662
  const ticketSource = resolveConfirmTicketSource(options);
22764
22663
  const proxyUrl = resolveProxyUrl(options.proxyUrl);
@@ -22896,7 +22795,7 @@ var createPairCommand = (dependencies = {}) => {
22896
22795
  };
22897
22796
 
22898
22797
  // src/commands/verify.ts
22899
- import { readFile as readFile7 } from "fs/promises";
22798
+ import { readFile as readFile6 } from "fs/promises";
22900
22799
  import { Command as Command9 } from "commander";
22901
22800
  var logger10 = createLogger({ service: "cli", module: "verify" });
22902
22801
  var REGISTRY_KEYS_CACHE_FILE = "registry-keys.json";
@@ -22909,7 +22808,7 @@ var VerifyCommandError = class extends Error {
22909
22808
  this.name = "VerifyCommandError";
22910
22809
  }
22911
22810
  };
22912
- var isRecord10 = (value) => {
22811
+ var isRecord9 = (value) => {
22913
22812
  return typeof value === "object" && value !== null;
22914
22813
  };
22915
22814
  var normalizeRegistryUrl = (registryUrl) => {
@@ -22945,7 +22844,7 @@ var resolveToken = async (tokenOrFile) => {
22945
22844
  throw new VerifyCommandError("invalid token (value is empty)");
22946
22845
  }
22947
22846
  try {
22948
- const fileContents = await readFile7(input, "utf-8");
22847
+ const fileContents = await readFile6(input, "utf-8");
22949
22848
  const token = fileContents.trim();
22950
22849
  if (token.length === 0) {
22951
22850
  throw new VerifyCommandError(`invalid token (${input} is empty)`);
@@ -22977,7 +22876,7 @@ var parseResponseJson = async (response) => {
22977
22876
  }
22978
22877
  };
22979
22878
  var parseSigningKeys = (payload) => {
22980
- if (!isRecord10(payload) || !Array.isArray(payload.keys)) {
22879
+ if (!isRecord9(payload) || !Array.isArray(payload.keys)) {
22981
22880
  throw new VerifyCommandError(
22982
22881
  "verification keys unavailable (response payload is invalid)"
22983
22882
  );
@@ -22996,7 +22895,7 @@ var parseSigningKeys = (payload) => {
22996
22895
  };
22997
22896
  var parseRegistryKeysCache = (rawCache) => {
22998
22897
  const parsed = parseJson(rawCache);
22999
- if (!isRecord10(parsed)) {
22898
+ if (!isRecord9(parsed)) {
23000
22899
  return void 0;
23001
22900
  }
23002
22901
  const { registryUrl, fetchedAtMs, keys } = parsed;
@@ -23022,7 +22921,7 @@ var parseRegistryKeysCache = (rawCache) => {
23022
22921
  };
23023
22922
  var parseCrlCache = (rawCache) => {
23024
22923
  const parsed = parseJson(rawCache);
23025
- if (!isRecord10(parsed)) {
22924
+ if (!isRecord9(parsed)) {
23026
22925
  return void 0;
23027
22926
  }
23028
22927
  const { registryUrl, fetchedAtMs, claims } = parsed;
@@ -23120,7 +23019,7 @@ var fetchCrlClaims = async (input) => {
23120
23019
  );
23121
23020
  }
23122
23021
  const payload = await parseResponseJson(response);
23123
- if (!isRecord10(payload) || typeof payload.crl !== "string") {
23022
+ if (!isRecord9(payload) || typeof payload.crl !== "string") {
23124
23023
  throw new VerifyCommandError(
23125
23024
  "revocation check unavailable (response payload is invalid)"
23126
23025
  );
@@ -23165,7 +23064,7 @@ var loadCrlClaims = async (input) => {
23165
23064
  return claims;
23166
23065
  };
23167
23066
  var toInvalidTokenReason = (error48) => {
23168
- if (isRecord10(error48) && typeof error48.message === "string") {
23067
+ if (isRecord9(error48) && typeof error48.message === "string") {
23169
23068
  return `invalid token (${error48.message})`;
23170
23069
  }
23171
23070
  if (error48 instanceof Error && error48.message.length > 0) {