clawdentity 0.0.11 → 0.0.12

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
@@ -14355,6 +14355,8 @@ var AGENT_AUTH_REFRESH_PATH = "/v1/agents/auth/refresh";
14355
14355
  var INVITES_PATH = "/v1/invites";
14356
14356
  var INVITES_REDEEM_PATH = "/v1/invites/redeem";
14357
14357
  var ME_API_KEYS_PATH = "/v1/me/api-keys";
14358
+ var REGISTRY_METADATA_PATH = "/v1/metadata";
14359
+ var RELAY_CONNECT_PATH = "/v1/relay/connect";
14358
14360
  var RELAY_RECIPIENT_AGENT_DID_HEADER = "x-claw-recipient-agent-did";
14359
14361
 
14360
14362
  // ../../packages/protocol/src/http-signing.ts
@@ -18395,6 +18397,139 @@ var createApiKeyCommand = (dependencies = {}) => {
18395
18397
  // src/commands/config.ts
18396
18398
  import { access as access2 } from "fs/promises";
18397
18399
  import { Command as Command4 } from "commander";
18400
+
18401
+ // src/config/registry-metadata.ts
18402
+ function isRecord4(value) {
18403
+ return typeof value === "object" && value !== null;
18404
+ }
18405
+ function parseNonEmptyString5(value) {
18406
+ if (typeof value !== "string") {
18407
+ return "";
18408
+ }
18409
+ return value.trim();
18410
+ }
18411
+ function createCliError3(code, message2) {
18412
+ return new AppError({
18413
+ code,
18414
+ message: message2,
18415
+ status: 400
18416
+ });
18417
+ }
18418
+ function parseUrl(candidate, label) {
18419
+ let parsed;
18420
+ try {
18421
+ parsed = new URL(candidate);
18422
+ } catch {
18423
+ throw createCliError3("CLI_REGISTRY_URL_INVALID", `${label} is invalid`);
18424
+ }
18425
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
18426
+ throw createCliError3("CLI_REGISTRY_URL_INVALID", `${label} is invalid`);
18427
+ }
18428
+ return parsed;
18429
+ }
18430
+ function normalizeRegistryUrl(registryUrl) {
18431
+ return parseUrl(registryUrl, "Registry URL").toString();
18432
+ }
18433
+ function toRegistryRequestUrl(registryUrl, path) {
18434
+ const normalizedRegistryUrl = normalizeRegistryUrl(registryUrl);
18435
+ const base = normalizedRegistryUrl.endsWith("/") ? normalizedRegistryUrl : `${normalizedRegistryUrl}/`;
18436
+ return new URL(path.slice(1), base).toString();
18437
+ }
18438
+ function extractRegistryErrorMessage3(payload) {
18439
+ if (!isRecord4(payload)) {
18440
+ return void 0;
18441
+ }
18442
+ const envelope = payload;
18443
+ if (!envelope.error || typeof envelope.error.message !== "string") {
18444
+ return void 0;
18445
+ }
18446
+ const trimmed = envelope.error.message.trim();
18447
+ return trimmed.length > 0 ? trimmed : void 0;
18448
+ }
18449
+ async function parseJsonResponse4(response) {
18450
+ try {
18451
+ return await response.json();
18452
+ } catch {
18453
+ return void 0;
18454
+ }
18455
+ }
18456
+ function parseMetadataPayload(payload, fallbackRegistryUrl) {
18457
+ if (!isRecord4(payload)) {
18458
+ throw createCliError3(
18459
+ "CLI_REGISTRY_METADATA_INVALID_RESPONSE",
18460
+ "Registry metadata response is invalid"
18461
+ );
18462
+ }
18463
+ const proxyUrlRaw = parseNonEmptyString5(payload.proxyUrl);
18464
+ if (proxyUrlRaw.length === 0) {
18465
+ throw createCliError3(
18466
+ "CLI_REGISTRY_METADATA_INVALID_RESPONSE",
18467
+ "Registry metadata response is invalid"
18468
+ );
18469
+ }
18470
+ const proxyUrl = parseUrl(proxyUrlRaw, "Proxy URL").toString();
18471
+ const registryUrlRaw = parseNonEmptyString5(payload.registryUrl);
18472
+ const registryUrl = registryUrlRaw.length > 0 ? parseUrl(registryUrlRaw, "Registry URL").toString() : normalizeRegistryUrl(fallbackRegistryUrl);
18473
+ const environment = parseNonEmptyString5(payload.environment);
18474
+ const version2 = parseNonEmptyString5(payload.version);
18475
+ return {
18476
+ proxyUrl,
18477
+ registryUrl,
18478
+ environment: environment.length > 0 ? environment : void 0,
18479
+ version: version2.length > 0 ? version2 : void 0
18480
+ };
18481
+ }
18482
+ function mapMetadataError(status, payload) {
18483
+ const registryMessage = extractRegistryErrorMessage3(payload);
18484
+ if (status === 404) {
18485
+ return "Registry metadata endpoint is unavailable (404).";
18486
+ }
18487
+ if (status >= 500) {
18488
+ return `Registry metadata request failed (${status}). Try again later.`;
18489
+ }
18490
+ if (registryMessage) {
18491
+ return `Registry metadata request failed (${status}): ${registryMessage}`;
18492
+ }
18493
+ return `Registry metadata request failed (${status}).`;
18494
+ }
18495
+ async function fetchRegistryMetadata(registryUrl, dependencies = {}) {
18496
+ const fetchImpl = dependencies.fetchImpl ?? globalThis.fetch;
18497
+ if (typeof fetchImpl !== "function") {
18498
+ throw createCliError3(
18499
+ "CLI_REGISTRY_METADATA_FETCH_UNAVAILABLE",
18500
+ "Runtime fetch is unavailable for registry metadata lookup"
18501
+ );
18502
+ }
18503
+ const normalizedRegistryUrl = normalizeRegistryUrl(registryUrl);
18504
+ const requestUrl = toRegistryRequestUrl(
18505
+ normalizedRegistryUrl,
18506
+ REGISTRY_METADATA_PATH
18507
+ );
18508
+ let response;
18509
+ try {
18510
+ response = await fetchImpl(requestUrl, {
18511
+ method: "GET",
18512
+ headers: {
18513
+ accept: "application/json"
18514
+ }
18515
+ });
18516
+ } catch {
18517
+ throw createCliError3(
18518
+ "CLI_REGISTRY_METADATA_REQUEST_FAILED",
18519
+ "Unable to reach registry metadata endpoint. Check registryUrl and network access."
18520
+ );
18521
+ }
18522
+ const payload = await parseJsonResponse4(response);
18523
+ if (!response.ok) {
18524
+ throw createCliError3(
18525
+ "CLI_REGISTRY_METADATA_FETCH_FAILED",
18526
+ mapMetadataError(response.status, payload)
18527
+ );
18528
+ }
18529
+ return parseMetadataPayload(payload, normalizedRegistryUrl);
18530
+ }
18531
+
18532
+ // src/commands/config.ts
18398
18533
  var logger5 = createLogger({ service: "cli", module: "config" });
18399
18534
  var VALID_KEYS = [
18400
18535
  "registryUrl",
@@ -18437,7 +18572,7 @@ var getEnvRegistryUrlOverride = () => {
18437
18572
  return typeof value === "string" && value.length > 0;
18438
18573
  });
18439
18574
  };
18440
- var createConfigCommand = () => {
18575
+ var createConfigCommand = (dependencies = {}) => {
18441
18576
  const configCommand = new Command4("config").description(
18442
18577
  "Manage local CLI configuration"
18443
18578
  );
@@ -18454,14 +18589,27 @@ var createConfigCommand = () => {
18454
18589
  }
18455
18590
  }
18456
18591
  const config2 = await readConfig();
18457
- const registryUrl = options.registryUrl ?? getEnvRegistryUrlOverride() ?? config2.registryUrl;
18592
+ const requestedRegistryUrl = options.registryUrl ?? getEnvRegistryUrlOverride() ?? config2.registryUrl;
18593
+ const normalizedRegistryUrl = normalizeRegistryUrl(requestedRegistryUrl);
18594
+ const metadata = await fetchRegistryMetadata(normalizedRegistryUrl, {
18595
+ fetchImpl: dependencies.fetchImpl
18596
+ });
18458
18597
  await writeConfig({
18459
18598
  ...config2,
18460
- registryUrl
18599
+ registryUrl: metadata.registryUrl,
18600
+ proxyUrl: metadata.proxyUrl
18461
18601
  });
18462
18602
  writeStdoutLine(`Initialized config at ${configFilePath}`);
18463
18603
  writeStdoutLine(
18464
- JSON.stringify(maskApiKey({ ...config2, registryUrl }), null, 2)
18604
+ JSON.stringify(
18605
+ maskApiKey({
18606
+ ...config2,
18607
+ registryUrl: metadata.registryUrl,
18608
+ proxyUrl: metadata.proxyUrl
18609
+ }),
18610
+ null,
18611
+ 2
18612
+ )
18465
18613
  );
18466
18614
  })
18467
18615
  );
@@ -18768,6 +18916,7 @@ function normalizeConnectionHeaders(headers) {
18768
18916
  var ConnectorClient = class {
18769
18917
  connectorUrl;
18770
18918
  connectionHeaders;
18919
+ connectionHeadersProvider;
18771
18920
  openclawHookUrl;
18772
18921
  openclawHookToken;
18773
18922
  heartbeatIntervalMs;
@@ -18799,6 +18948,7 @@ var ConnectorClient = class {
18799
18948
  this.connectionHeaders = normalizeConnectionHeaders(
18800
18949
  options.connectionHeaders
18801
18950
  );
18951
+ this.connectionHeadersProvider = options.connectionHeadersProvider;
18802
18952
  this.openclawHookToken = options.openclawHookToken;
18803
18953
  this.heartbeatIntervalMs = options.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
18804
18954
  this.reconnectMinDelayMs = options.reconnectMinDelayMs ?? DEFAULT_RECONNECT_MIN_DELAY_MS;
@@ -18851,7 +19001,7 @@ var ConnectorClient = class {
18851
19001
  return;
18852
19002
  }
18853
19003
  this.started = true;
18854
- this.connectSocket();
19004
+ void this.connectSocket();
18855
19005
  }
18856
19006
  disconnect() {
18857
19007
  this.started = false;
@@ -18884,13 +19034,27 @@ var ConnectorClient = class {
18884
19034
  this.flushOutboundQueue();
18885
19035
  return frame;
18886
19036
  }
18887
- connectSocket() {
19037
+ async connectSocket() {
18888
19038
  this.clearReconnectTimeout();
19039
+ let connectionHeaders = this.connectionHeaders;
19040
+ if (this.connectionHeadersProvider) {
19041
+ try {
19042
+ connectionHeaders = normalizeConnectionHeaders(
19043
+ await this.connectionHeadersProvider()
19044
+ );
19045
+ } catch (error48) {
19046
+ this.logger.warn("connector.websocket.create_failed", {
19047
+ reason: sanitizeErrorReason(error48)
19048
+ });
19049
+ this.scheduleReconnect();
19050
+ return;
19051
+ }
19052
+ }
19053
+ if (!this.started) {
19054
+ return;
19055
+ }
18889
19056
  try {
18890
- this.socket = this.webSocketFactory(
18891
- this.connectorUrl,
18892
- this.connectionHeaders
18893
- );
19057
+ this.socket = this.webSocketFactory(this.connectorUrl, connectionHeaders);
18894
19058
  } catch (error48) {
18895
19059
  this.logger.warn("connector.websocket.create_failed", {
18896
19060
  reason: sanitizeErrorReason(error48)
@@ -18945,7 +19109,7 @@ var ConnectorClient = class {
18945
19109
  const delayMs = Math.max(0, Math.floor(boundedDelay + jitterOffset));
18946
19110
  this.reconnectAttempt += 1;
18947
19111
  this.reconnectTimeout = setTimeout(() => {
18948
- this.connectSocket();
19112
+ void this.connectSocket();
18949
19113
  }, delayMs);
18950
19114
  }
18951
19115
  clearReconnectTimeout() {
@@ -19165,7 +19329,7 @@ var REFRESH_SINGLE_FLIGHT_PREFIX = "connector-runtime";
19165
19329
  var NONCE_SIZE = 16;
19166
19330
  var MAX_OUTBOUND_BODY_BYTES = 1024 * 1024;
19167
19331
  var ACCESS_TOKEN_REFRESH_SKEW_MS = 3e4;
19168
- function isRecord4(value) {
19332
+ function isRecord5(value) {
19169
19333
  return typeof value === "object" && value !== null;
19170
19334
  }
19171
19335
  function toPathWithQuery2(url2) {
@@ -19213,6 +19377,9 @@ function normalizeWebSocketUrl(urlInput) {
19213
19377
  if (parsed.protocol !== "wss:" && parsed.protocol !== "ws:") {
19214
19378
  throw new Error("Proxy websocket URL must use ws:// or wss://");
19215
19379
  }
19380
+ if (parsed.pathname === "/") {
19381
+ parsed.pathname = RELAY_CONNECT_PATH;
19382
+ }
19216
19383
  return parsed.toString();
19217
19384
  }
19218
19385
  function resolveOpenclawBaseUrl(input) {
@@ -19257,7 +19424,7 @@ function shouldRefreshAccessToken(auth, nowMs) {
19257
19424
  return expiresAtMs <= nowMs + ACCESS_TOKEN_REFRESH_SKEW_MS;
19258
19425
  }
19259
19426
  function parseOutboundRelayRequest(payload) {
19260
- if (!isRecord4(payload)) {
19427
+ if (!isRecord5(payload)) {
19261
19428
  throw new AppError({
19262
19429
  code: "CONNECTOR_OUTBOUND_INVALID_REQUEST",
19263
19430
  message: "Outbound relay request must be an object",
@@ -19390,7 +19557,10 @@ async function startConnectorRuntime(input) {
19390
19557
  parseRequiredString(input.credentials.secretKey, "secretKey")
19391
19558
  );
19392
19559
  let currentAuth = toInitialAuthBundle(input.credentials);
19393
- if (shouldRefreshAccessToken(currentAuth, Date.now())) {
19560
+ const refreshCurrentAuthIfNeeded = async () => {
19561
+ if (!shouldRefreshAccessToken(currentAuth, Date.now())) {
19562
+ return;
19563
+ }
19394
19564
  currentAuth = await refreshAgentAuthWithClawProof({
19395
19565
  registryUrl: input.registryUrl,
19396
19566
  ait: input.credentials.ait,
@@ -19403,18 +19573,22 @@ async function startConnectorRuntime(input) {
19403
19573
  agentName: input.agentName,
19404
19574
  auth: currentAuth
19405
19575
  });
19406
- }
19576
+ };
19577
+ await refreshCurrentAuthIfNeeded();
19407
19578
  const wsUrl = normalizeWebSocketUrl(input.proxyWebsocketUrl);
19408
19579
  const wsParsed = new URL(wsUrl);
19409
- const upgradeHeaders = await buildUpgradeHeaders({
19410
- wsUrl: wsParsed,
19411
- ait: input.credentials.ait,
19412
- accessToken: currentAuth.accessToken,
19413
- secretKey
19414
- });
19580
+ const resolveUpgradeHeaders = async () => {
19581
+ await refreshCurrentAuthIfNeeded();
19582
+ return buildUpgradeHeaders({
19583
+ wsUrl: wsParsed,
19584
+ ait: input.credentials.ait,
19585
+ accessToken: currentAuth.accessToken,
19586
+ secretKey
19587
+ });
19588
+ };
19415
19589
  const connectorClient = new ConnectorClient({
19416
19590
  connectorUrl: wsParsed.toString(),
19417
- connectionHeaders: upgradeHeaders,
19591
+ connectionHeadersProvider: resolveUpgradeHeaders,
19418
19592
  openclawBaseUrl: resolveOpenclawBaseUrl(input.openclawBaseUrl),
19419
19593
  openclawHookPath: resolveOpenclawHookPath(input.openclawHookPath),
19420
19594
  openclawHookToken: resolveOpenclawHookToken(input.openclawHookToken),
@@ -19588,16 +19762,16 @@ var OPENCLAW_CONNECTORS_FILE_NAME = "openclaw-connectors.json";
19588
19762
  var SERVICE_LOG_DIR_NAME = "logs";
19589
19763
  var DEFAULT_CONNECTOR_BASE_URL2 = "http://127.0.0.1:19400";
19590
19764
  var DEFAULT_CONNECTOR_OUTBOUND_PATH2 = "/v1/outbound";
19591
- function isRecord5(value) {
19765
+ function isRecord6(value) {
19592
19766
  return typeof value === "object" && value !== null;
19593
19767
  }
19594
19768
  function getErrorCode(error48) {
19595
- if (!isRecord5(error48)) {
19769
+ if (!isRecord6(error48)) {
19596
19770
  return void 0;
19597
19771
  }
19598
19772
  return typeof error48.code === "string" ? error48.code : void 0;
19599
19773
  }
19600
- function createCliError3(code, message2, details) {
19774
+ function createCliError4(code, message2, details) {
19601
19775
  return new AppError({
19602
19776
  code,
19603
19777
  message: message2,
@@ -19605,9 +19779,9 @@ function createCliError3(code, message2, details) {
19605
19779
  details
19606
19780
  });
19607
19781
  }
19608
- function parseNonEmptyString5(value, label) {
19782
+ function parseNonEmptyString6(value, label) {
19609
19783
  if (typeof value !== "string") {
19610
- throw createCliError3(
19784
+ throw createCliError4(
19611
19785
  "CLI_CONNECTOR_INVALID_INPUT",
19612
19786
  "Connector input is invalid",
19613
19787
  {
@@ -19617,7 +19791,7 @@ function parseNonEmptyString5(value, label) {
19617
19791
  }
19618
19792
  const trimmed = value.trim();
19619
19793
  if (trimmed.length === 0) {
19620
- throw createCliError3(
19794
+ throw createCliError4(
19621
19795
  "CLI_CONNECTOR_INVALID_INPUT",
19622
19796
  "Connector input is invalid",
19623
19797
  {
@@ -19628,9 +19802,9 @@ function parseNonEmptyString5(value, label) {
19628
19802
  return trimmed;
19629
19803
  }
19630
19804
  function parseAgentDid(value) {
19631
- const did = parseNonEmptyString5(value, "agent did");
19805
+ const did = parseNonEmptyString6(value, "agent did");
19632
19806
  if (!did.startsWith("did:claw:agent:")) {
19633
- throw createCliError3(
19807
+ throw createCliError4(
19634
19808
  "CLI_CONNECTOR_INVALID_AGENT_IDENTITY",
19635
19809
  "Agent identity is invalid for connector startup"
19636
19810
  );
@@ -19642,13 +19816,13 @@ function parseConnectorBaseUrl(value) {
19642
19816
  try {
19643
19817
  parsed = new URL(value);
19644
19818
  } catch {
19645
- throw createCliError3(
19819
+ throw createCliError4(
19646
19820
  "CLI_CONNECTOR_INVALID_BASE_URL",
19647
19821
  "Connector base URL is invalid"
19648
19822
  );
19649
19823
  }
19650
19824
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
19651
- throw createCliError3(
19825
+ throw createCliError4(
19652
19826
  "CLI_CONNECTOR_INVALID_BASE_URL",
19653
19827
  "Connector base URL is invalid"
19654
19828
  );
@@ -19658,10 +19832,65 @@ function parseConnectorBaseUrl(value) {
19658
19832
  }
19659
19833
  return parsed.toString();
19660
19834
  }
19835
+ function parseProxyWebsocketUrl(value) {
19836
+ let parsed;
19837
+ try {
19838
+ parsed = new URL(value);
19839
+ } catch {
19840
+ throw createCliError4(
19841
+ "CLI_CONNECTOR_INVALID_PROXY_URL",
19842
+ "Proxy websocket URL is invalid"
19843
+ );
19844
+ }
19845
+ if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:" && parsed.protocol !== "http:" && parsed.protocol !== "https:") {
19846
+ throw createCliError4(
19847
+ "CLI_CONNECTOR_INVALID_PROXY_URL",
19848
+ "Proxy websocket URL is invalid"
19849
+ );
19850
+ }
19851
+ return parsed.toString();
19852
+ }
19853
+ function resolveProxyWebsocketUrlFromEnv() {
19854
+ const explicitProxyWsUrl = process.env.CLAWDENTITY_PROXY_WS_URL;
19855
+ if (typeof explicitProxyWsUrl === "string" && explicitProxyWsUrl.trim().length > 0) {
19856
+ return parseProxyWebsocketUrl(explicitProxyWsUrl.trim());
19857
+ }
19858
+ const proxyUrl = process.env.CLAWDENTITY_PROXY_URL;
19859
+ if (typeof proxyUrl === "string" && proxyUrl.trim().length > 0) {
19860
+ return parseProxyWebsocketUrl(proxyUrl.trim());
19861
+ }
19862
+ return void 0;
19863
+ }
19864
+ async function resolveProxyWebsocketUrl(input) {
19865
+ if (typeof input.explicitProxyWsUrl === "string" && input.explicitProxyWsUrl.trim().length > 0) {
19866
+ return parseProxyWebsocketUrl(input.explicitProxyWsUrl.trim());
19867
+ }
19868
+ const fromEnv = resolveProxyWebsocketUrlFromEnv();
19869
+ if (fromEnv !== void 0) {
19870
+ return fromEnv;
19871
+ }
19872
+ if (typeof input.configProxyUrl === "string" && input.configProxyUrl.trim().length > 0) {
19873
+ return parseProxyWebsocketUrl(input.configProxyUrl.trim());
19874
+ }
19875
+ const fetchImpl = input.fetchImpl ?? globalThis.fetch;
19876
+ if (typeof fetchImpl === "function") {
19877
+ try {
19878
+ const metadata = await fetchRegistryMetadata(input.registryUrl, {
19879
+ fetchImpl
19880
+ });
19881
+ return parseProxyWebsocketUrl(metadata.proxyUrl);
19882
+ } catch {
19883
+ }
19884
+ }
19885
+ throw createCliError4(
19886
+ "CLI_CONNECTOR_PROXY_URL_REQUIRED",
19887
+ "Proxy URL is required for connector startup. Run `clawdentity invite redeem <clw_inv_...>` or set CLAWDENTITY_PROXY_URL / CLAWDENTITY_PROXY_WS_URL."
19888
+ );
19889
+ }
19661
19890
  function normalizeOutboundPath2(pathValue) {
19662
19891
  const trimmed = pathValue.trim();
19663
19892
  if (trimmed.length === 0) {
19664
- throw createCliError3(
19893
+ throw createCliError4(
19665
19894
  "CLI_CONNECTOR_INVALID_OUTBOUND_PATH",
19666
19895
  "Connector outbound path is invalid"
19667
19896
  );
@@ -19690,17 +19919,17 @@ async function readConnectorAssignedBaseUrl(configDir, agentName, readFileImpl)
19690
19919
  try {
19691
19920
  parsed = JSON.parse(raw);
19692
19921
  } catch {
19693
- throw createCliError3(
19922
+ throw createCliError4(
19694
19923
  "CLI_CONNECTOR_INVALID_ASSIGNMENTS",
19695
19924
  "Connector assignments config is invalid JSON",
19696
19925
  { assignmentsPath }
19697
19926
  );
19698
19927
  }
19699
- if (!isRecord5(parsed) || !isRecord5(parsed.agents)) {
19928
+ if (!isRecord6(parsed) || !isRecord6(parsed.agents)) {
19700
19929
  return void 0;
19701
19930
  }
19702
19931
  const entry = parsed.agents[agentName];
19703
- if (!isRecord5(entry) || typeof entry.connectorBaseUrl !== "string") {
19932
+ if (!isRecord6(entry) || typeof entry.connectorBaseUrl !== "string") {
19704
19933
  return void 0;
19705
19934
  }
19706
19935
  return parseConnectorBaseUrl(entry.connectorBaseUrl);
@@ -19721,7 +19950,7 @@ async function readRequiredTrimmedFile(filePath, label, readFileImpl) {
19721
19950
  raw = await readFileImpl(filePath, "utf8");
19722
19951
  } catch (error48) {
19723
19952
  if (getErrorCode(error48) === "ENOENT") {
19724
- throw createCliError3(
19953
+ throw createCliError4(
19725
19954
  "CLI_CONNECTOR_MISSING_AGENT_MATERIAL",
19726
19955
  "Local agent credentials are missing for connector startup",
19727
19956
  { label }
@@ -19731,7 +19960,7 @@ async function readRequiredTrimmedFile(filePath, label, readFileImpl) {
19731
19960
  }
19732
19961
  const trimmed = raw.trim();
19733
19962
  if (trimmed.length === 0) {
19734
- throw createCliError3(
19963
+ throw createCliError4(
19735
19964
  "CLI_CONNECTOR_MISSING_AGENT_MATERIAL",
19736
19965
  "Local agent credentials are missing for connector startup",
19737
19966
  { label }
@@ -19756,7 +19985,7 @@ async function readRelayRuntimeConfig(configDir, readFileImpl) {
19756
19985
  } catch {
19757
19986
  return void 0;
19758
19987
  }
19759
- if (!isRecord5(parsed)) {
19988
+ if (!isRecord6(parsed)) {
19760
19989
  return void 0;
19761
19990
  }
19762
19991
  const openclawHookToken = typeof parsed.openclawHookToken === "string" && parsed.openclawHookToken.trim().length > 0 ? parsed.openclawHookToken.trim() : void 0;
@@ -19772,10 +20001,10 @@ function parseJsonRecord(value, code, message2) {
19772
20001
  try {
19773
20002
  parsed = JSON.parse(value);
19774
20003
  } catch {
19775
- throw createCliError3(code, message2);
20004
+ throw createCliError4(code, message2);
19776
20005
  }
19777
- if (!isRecord5(parsed)) {
19778
- throw createCliError3(code, message2);
20006
+ if (!isRecord6(parsed)) {
20007
+ throw createCliError4(code, message2);
19779
20008
  }
19780
20009
  return parsed;
19781
20010
  }
@@ -19785,7 +20014,7 @@ function parseRegistryAuth(rawRegistryAuth) {
19785
20014
  "CLI_CONNECTOR_INVALID_REGISTRY_AUTH",
19786
20015
  "Agent registry auth is invalid for connector startup"
19787
20016
  );
19788
- const refreshToken = parseNonEmptyString5(parsed.refreshToken, "refreshToken");
20017
+ const refreshToken = parseNonEmptyString6(parsed.refreshToken, "refreshToken");
19789
20018
  const accessToken = typeof parsed.accessToken === "string" && parsed.accessToken.trim().length > 0 ? parsed.accessToken.trim() : void 0;
19790
20019
  const accessExpiresAt = typeof parsed.accessExpiresAt === "string" && parsed.accessExpiresAt.trim().length > 0 ? parsed.accessExpiresAt.trim() : void 0;
19791
20020
  const refreshExpiresAt = typeof parsed.refreshExpiresAt === "string" && parsed.refreshExpiresAt.trim().length > 0 ? parsed.refreshExpiresAt.trim() : void 0;
@@ -19814,7 +20043,7 @@ async function loadDefaultConnectorModule() {
19814
20043
  };
19815
20044
  }
19816
20045
  function resolveWaitPromise(runtime) {
19817
- if (!runtime || !isRecord5(runtime)) {
20046
+ if (!runtime || !isRecord6(runtime)) {
19818
20047
  return void 0;
19819
20048
  }
19820
20049
  if (typeof runtime.waitUntilStopped === "function") {
@@ -19838,7 +20067,7 @@ function parseConnectorServicePlatformOption(value) {
19838
20067
  if (value === "auto" || value === "launchd" || value === "systemd") {
19839
20068
  return value;
19840
20069
  }
19841
- throw createCliError3(
20070
+ throw createCliError4(
19842
20071
  "CLI_CONNECTOR_SERVICE_PLATFORM_INVALID",
19843
20072
  "Connector service platform must be one of: auto, launchd, systemd"
19844
20073
  );
@@ -19853,7 +20082,7 @@ function resolveConnectorServicePlatform(inputPlatform, currentPlatform) {
19853
20082
  if (currentPlatform === "linux") {
19854
20083
  return "systemd";
19855
20084
  }
19856
- throw createCliError3(
20085
+ throw createCliError4(
19857
20086
  "CLI_CONNECTOR_SERVICE_PLATFORM_UNSUPPORTED",
19858
20087
  "Connector service install is supported only on macOS (launchd) and Linux (systemd)",
19859
20088
  {
@@ -19955,7 +20184,7 @@ function resolveServiceDependencies(dependencies) {
19955
20184
  resolveCurrentPlatformImpl: dependencies.resolveCurrentPlatformImpl ?? (() => process.platform),
19956
20185
  resolveCurrentUidImpl: dependencies.resolveCurrentUidImpl ?? (() => {
19957
20186
  if (typeof process.getuid !== "function") {
19958
- throw createCliError3(
20187
+ throw createCliError4(
19959
20188
  "CLI_CONNECTOR_SERVICE_UID_UNAVAILABLE",
19960
20189
  "Current user id is unavailable in this runtime"
19961
20190
  );
@@ -20017,7 +20246,7 @@ async function installConnectorServiceForAgent(agentName, commandOptions = {}, d
20017
20246
  `${serviceName}.service`
20018
20247
  ]);
20019
20248
  } catch (error48) {
20020
- throw createCliError3(
20249
+ throw createCliError4(
20021
20250
  "CLI_CONNECTOR_SERVICE_INSTALL_FAILED",
20022
20251
  "Failed to install systemd connector service",
20023
20252
  {
@@ -20064,7 +20293,7 @@ async function installConnectorServiceForAgent(agentName, commandOptions = {}, d
20064
20293
  serviceFilePath
20065
20294
  ]);
20066
20295
  } catch (error48) {
20067
- throw createCliError3(
20296
+ throw createCliError4(
20068
20297
  "CLI_CONNECTOR_SERVICE_INSTALL_FAILED",
20069
20298
  "Failed to install launchd connector service",
20070
20299
  {
@@ -20148,6 +20377,7 @@ async function startConnectorForAgent(agentName, commandOptions = {}, dependenci
20148
20377
  const resolveConfigImpl = dependencies.resolveConfigImpl ?? resolveConfig;
20149
20378
  const getConfigDirImpl = dependencies.getConfigDirImpl ?? getConfigDir;
20150
20379
  const readFileImpl = dependencies.readFileImpl ?? ((path, encoding) => readFile3(path, encoding));
20380
+ const fetchImpl = dependencies.fetchImpl ?? globalThis.fetch;
20151
20381
  const loadConnectorModule = dependencies.loadConnectorModule ?? loadDefaultConnectorModule;
20152
20382
  const configDir = getConfigDirImpl();
20153
20383
  const agentDirectory = join5(configDir, AGENTS_DIR_NAME3, agentName);
@@ -20187,13 +20417,19 @@ async function startConnectorForAgent(agentName, commandOptions = {}, dependenci
20187
20417
  loadConnectorModule()
20188
20418
  ]);
20189
20419
  if (typeof connectorModule.startConnectorRuntime !== "function") {
20190
- throw createCliError3(
20420
+ throw createCliError4(
20191
20421
  "CLI_CONNECTOR_INVALID_PACKAGE_API",
20192
20422
  "Connector package does not expose startConnectorRuntime"
20193
20423
  );
20194
20424
  }
20195
20425
  const identity = parseAgentIdentity(rawIdentity);
20196
20426
  const registryAuth = parseRegistryAuth(rawRegistryAuth);
20427
+ const resolvedProxyWebsocketUrl = await resolveProxyWebsocketUrl({
20428
+ explicitProxyWsUrl: commandOptions.proxyWsUrl,
20429
+ configProxyUrl: config2.proxyUrl,
20430
+ registryUrl: config2.registryUrl,
20431
+ fetchImpl
20432
+ });
20197
20433
  const openclawHookToken = commandOptions.openclawHookToken ?? relayRuntimeConfig?.openclawHookToken;
20198
20434
  const outboundBaseUrl = resolveConnectorBaseUrlFromEnv() ?? assignedConnectorBaseUrl ?? DEFAULT_CONNECTOR_BASE_URL2;
20199
20435
  const outboundPath = resolveConnectorOutboundPath();
@@ -20203,7 +20439,7 @@ async function startConnectorForAgent(agentName, commandOptions = {}, dependenci
20203
20439
  registryUrl: config2.registryUrl,
20204
20440
  outboundBaseUrl,
20205
20441
  outboundPath,
20206
- proxyWebsocketUrl: commandOptions.proxyWsUrl,
20442
+ proxyWebsocketUrl: resolvedProxyWebsocketUrl,
20207
20443
  openclawBaseUrl: commandOptions.openclawBaseUrl,
20208
20444
  openclawHookPath: commandOptions.openclawHookPath,
20209
20445
  openclawHookToken,
@@ -20218,8 +20454,8 @@ async function startConnectorForAgent(agentName, commandOptions = {}, dependenci
20218
20454
  tokenType: registryAuth.tokenType
20219
20455
  }
20220
20456
  });
20221
- const outboundUrl = runtime && isRecord5(runtime) && typeof runtime.outboundUrl === "string" ? runtime.outboundUrl : resolveOutboundUrl(outboundBaseUrl, outboundPath);
20222
- const proxyWebsocketUrl = runtime && isRecord5(runtime) ? typeof runtime.websocketUrl === "string" ? runtime.websocketUrl : typeof runtime.proxyWebsocketUrl === "string" ? runtime.proxyWebsocketUrl : void 0 : void 0;
20457
+ const outboundUrl = runtime && isRecord6(runtime) && typeof runtime.outboundUrl === "string" ? runtime.outboundUrl : resolveOutboundUrl(outboundBaseUrl, outboundPath);
20458
+ const proxyWebsocketUrl = runtime && isRecord6(runtime) ? typeof runtime.websocketUrl === "string" ? runtime.websocketUrl : typeof runtime.proxyWebsocketUrl === "string" ? runtime.proxyWebsocketUrl : resolvedProxyWebsocketUrl : void 0;
20223
20459
  return {
20224
20460
  outboundUrl,
20225
20461
  proxyWebsocketUrl,
@@ -20349,107 +20585,52 @@ function createConnectorCommand(dependencies = {}) {
20349
20585
 
20350
20586
  // src/commands/invite.ts
20351
20587
  import { Command as Command6 } from "commander";
20352
-
20353
- // src/config/endpoints.ts
20354
- var PRODUCTION_REGISTRY_HOST = "registry.clawdentity.com";
20355
- var DEVELOPMENT_REGISTRY_HOST = "dev.registry.clawdentity.com";
20356
- var PRODUCTION_PROXY_HOST = "proxy.clawdentity.com";
20357
- var DEVELOPMENT_PROXY_HOST = "dev.proxy.clawdentity.com";
20358
- var LOCAL_REGISTRY_PORT = "8788";
20359
- var LOCAL_PROXY_PORT = "8787";
20360
- var LOCAL_HOSTNAMES = /* @__PURE__ */ new Set([
20361
- "localhost",
20362
- "127.0.0.1",
20363
- "host.docker.internal",
20364
- "gateway.docker.internal"
20365
- ]);
20366
- var normalizeUrl = (candidate) => {
20367
- try {
20368
- const parsed = new URL(candidate);
20369
- if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
20370
- return void 0;
20371
- }
20372
- return parsed;
20373
- } catch {
20374
- return void 0;
20375
- }
20376
- };
20377
- var toProxyHostname = (registryHostname) => {
20378
- if (registryHostname === PRODUCTION_REGISTRY_HOST) {
20379
- return PRODUCTION_PROXY_HOST;
20380
- }
20381
- if (registryHostname === DEVELOPMENT_REGISTRY_HOST) {
20382
- return DEVELOPMENT_PROXY_HOST;
20383
- }
20384
- if (registryHostname.startsWith("dev.registry.")) {
20385
- return `dev.proxy.${registryHostname.slice("dev.registry.".length)}`;
20386
- }
20387
- if (registryHostname.startsWith("registry.")) {
20388
- return `proxy.${registryHostname.slice("registry.".length)}`;
20389
- }
20390
- return void 0;
20391
- };
20392
- var deriveProxyUrlFromRegistryUrl = (registryUrl) => {
20393
- const parsedRegistryUrl = normalizeUrl(registryUrl);
20394
- if (!parsedRegistryUrl) {
20395
- return void 0;
20396
- }
20397
- const hostname3 = parsedRegistryUrl.hostname.toLowerCase();
20398
- if (LOCAL_HOSTNAMES.has(hostname3) && parsedRegistryUrl.port === LOCAL_REGISTRY_PORT) {
20399
- return `${parsedRegistryUrl.protocol}//${hostname3}:${LOCAL_PROXY_PORT}/`;
20400
- }
20401
- const mappedHostname = toProxyHostname(hostname3);
20402
- if (!mappedHostname) {
20403
- return void 0;
20404
- }
20405
- const port = parsedRegistryUrl.port.length > 0 ? `:${parsedRegistryUrl.port}` : "";
20406
- return `${parsedRegistryUrl.protocol}//${mappedHostname}${port}/`;
20407
- };
20408
-
20409
- // src/commands/invite.ts
20410
20588
  var logger7 = createLogger({ service: "cli", module: "invite" });
20411
- var isRecord6 = (value) => {
20589
+ var isRecord7 = (value) => {
20412
20590
  return typeof value === "object" && value !== null;
20413
20591
  };
20414
- function parseNonEmptyString6(value) {
20592
+ function parseNonEmptyString7(value) {
20415
20593
  if (typeof value !== "string") {
20416
20594
  return "";
20417
20595
  }
20418
20596
  return value.trim();
20419
20597
  }
20420
- function createCliError4(code, message2) {
20598
+ function createCliError5(code, message2) {
20421
20599
  return new AppError({
20422
20600
  code,
20423
20601
  message: message2,
20424
20602
  status: 400
20425
20603
  });
20426
20604
  }
20427
- function resolveRegistryUrl2(input) {
20428
- const candidate = parseNonEmptyString6(input.overrideRegistryUrl) || input.configRegistryUrl;
20605
+ function normalizeProxyUrl(value) {
20429
20606
  try {
20430
- return new URL(candidate).toString();
20607
+ const parsed = new URL(value);
20608
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
20609
+ throw new Error("invalid protocol");
20610
+ }
20611
+ return parsed.toString();
20431
20612
  } catch {
20432
- throw createCliError4(
20433
- "CLI_INVITE_INVALID_REGISTRY_URL",
20434
- "Registry URL is invalid"
20613
+ throw createCliError5(
20614
+ "CLI_INVITE_REDEEM_INVALID_RESPONSE",
20615
+ "Invite redeem response is invalid"
20435
20616
  );
20436
20617
  }
20437
20618
  }
20619
+ function resolveRegistryUrl2(input) {
20620
+ const candidate = parseNonEmptyString7(input.overrideRegistryUrl) || input.configRegistryUrl;
20621
+ return normalizeRegistryUrl(candidate);
20622
+ }
20438
20623
  function requireApiKey2(config2) {
20439
20624
  if (typeof config2.apiKey === "string" && config2.apiKey.trim().length > 0) {
20440
20625
  return config2.apiKey;
20441
20626
  }
20442
- throw createCliError4(
20627
+ throw createCliError5(
20443
20628
  "CLI_INVITE_MISSING_LOCAL_CREDENTIALS",
20444
20629
  "API key is not configured. Run `clawdentity config set apiKey <token>` or set CLAWDENTITY_API_KEY."
20445
20630
  );
20446
20631
  }
20447
- function toRegistryRequestUrl(registryUrl, path) {
20448
- const normalizedBaseUrl = registryUrl.endsWith("/") ? registryUrl : `${registryUrl}/`;
20449
- return new URL(path.slice(1), normalizedBaseUrl).toString();
20450
- }
20451
20632
  function extractRegistryErrorCode(payload) {
20452
- if (!isRecord6(payload)) {
20633
+ if (!isRecord7(payload)) {
20453
20634
  return void 0;
20454
20635
  }
20455
20636
  const envelope = payload;
@@ -20459,8 +20640,8 @@ function extractRegistryErrorCode(payload) {
20459
20640
  const trimmed = envelope.error.code.trim();
20460
20641
  return trimmed.length > 0 ? trimmed : void 0;
20461
20642
  }
20462
- function extractRegistryErrorMessage3(payload) {
20463
- if (!isRecord6(payload)) {
20643
+ function extractRegistryErrorMessage4(payload) {
20644
+ if (!isRecord7(payload)) {
20464
20645
  return void 0;
20465
20646
  }
20466
20647
  const envelope = payload;
@@ -20470,7 +20651,7 @@ function extractRegistryErrorMessage3(payload) {
20470
20651
  const trimmed = envelope.error.message.trim();
20471
20652
  return trimmed.length > 0 ? trimmed : void 0;
20472
20653
  }
20473
- async function parseJsonResponse4(response) {
20654
+ async function parseJsonResponse5(response) {
20474
20655
  try {
20475
20656
  return await response.json();
20476
20657
  } catch {
@@ -20481,7 +20662,7 @@ async function executeInviteRequest(input) {
20481
20662
  try {
20482
20663
  return await input.fetchImpl(input.url, input.init);
20483
20664
  } catch {
20484
- throw createCliError4(
20665
+ throw createCliError5(
20485
20666
  "CLI_INVITE_REQUEST_FAILED",
20486
20667
  "Unable to connect to the registry. Check network access and registryUrl."
20487
20668
  );
@@ -20489,7 +20670,7 @@ async function executeInviteRequest(input) {
20489
20670
  }
20490
20671
  function mapCreateInviteError(status, payload) {
20491
20672
  const errorCode = extractRegistryErrorCode(payload);
20492
- const registryMessage = extractRegistryErrorMessage3(payload);
20673
+ const registryMessage = extractRegistryErrorMessage4(payload);
20493
20674
  if (status === 401) {
20494
20675
  return registryMessage ? `Registry authentication failed (401): ${registryMessage}` : "Registry authentication failed (401). Check your API key.";
20495
20676
  }
@@ -20512,7 +20693,7 @@ function mapCreateInviteError(status, payload) {
20512
20693
  }
20513
20694
  function mapRedeemInviteError(status, payload) {
20514
20695
  const errorCode = extractRegistryErrorCode(payload);
20515
- const registryMessage = extractRegistryErrorMessage3(payload);
20696
+ const registryMessage = extractRegistryErrorMessage4(payload);
20516
20697
  if (errorCode === "INVITE_REDEEM_ALREADY_USED" || errorCode === "INVITE_REDEEM_ALREADY_REDEEMED") {
20517
20698
  return "Invite code has already been redeemed";
20518
20699
  }
@@ -20534,16 +20715,16 @@ function mapRedeemInviteError(status, payload) {
20534
20715
  return `Invite redeem failed (${status})`;
20535
20716
  }
20536
20717
  function parseInviteRecord(payload) {
20537
- if (!isRecord6(payload)) {
20538
- throw createCliError4(
20718
+ if (!isRecord7(payload)) {
20719
+ throw createCliError5(
20539
20720
  "CLI_INVITE_CREATE_INVALID_RESPONSE",
20540
20721
  "Invite response is invalid"
20541
20722
  );
20542
20723
  }
20543
- const source = isRecord6(payload.invite) ? payload.invite : payload;
20544
- const code = parseNonEmptyString6(source.code);
20724
+ const source = isRecord7(payload.invite) ? payload.invite : payload;
20725
+ const code = parseNonEmptyString7(source.code);
20545
20726
  if (code.length === 0) {
20546
- throw createCliError4(
20727
+ throw createCliError5(
20547
20728
  "CLI_INVITE_CREATE_INVALID_RESPONSE",
20548
20729
  "Invite response is invalid"
20549
20730
  );
@@ -20551,11 +20732,11 @@ function parseInviteRecord(payload) {
20551
20732
  const invite = {
20552
20733
  code
20553
20734
  };
20554
- const id = parseNonEmptyString6(source.id);
20735
+ const id = parseNonEmptyString7(source.id);
20555
20736
  if (id.length > 0) {
20556
20737
  invite.id = id;
20557
20738
  }
20558
- const createdAt = parseNonEmptyString6(source.createdAt);
20739
+ const createdAt = parseNonEmptyString7(source.createdAt);
20559
20740
  if (createdAt.length > 0) {
20560
20741
  invite.createdAt = createdAt;
20561
20742
  }
@@ -20565,36 +20746,25 @@ function parseInviteRecord(payload) {
20565
20746
  return invite;
20566
20747
  }
20567
20748
  function parseInviteRedeemResponse(payload) {
20568
- if (!isRecord6(payload)) {
20569
- throw createCliError4(
20749
+ if (!isRecord7(payload)) {
20750
+ throw createCliError5(
20570
20751
  "CLI_INVITE_REDEEM_INVALID_RESPONSE",
20571
20752
  "Invite redeem response is invalid"
20572
20753
  );
20573
20754
  }
20574
- const apiKeySource = isRecord6(payload.apiKey) ? payload.apiKey : payload;
20575
- const apiKeyToken = parseNonEmptyString6(
20576
- isRecord6(payload.apiKey) ? payload.apiKey.token : payload.token
20755
+ const apiKeySource = isRecord7(payload.apiKey) ? payload.apiKey : payload;
20756
+ const apiKeyToken = parseNonEmptyString7(
20757
+ isRecord7(payload.apiKey) ? payload.apiKey.token : payload.token
20577
20758
  );
20578
20759
  if (apiKeyToken.length === 0) {
20579
- throw createCliError4(
20760
+ throw createCliError5(
20580
20761
  "CLI_INVITE_REDEEM_INVALID_RESPONSE",
20581
20762
  "Invite redeem response is invalid"
20582
20763
  );
20583
20764
  }
20584
- const apiKeyId = parseNonEmptyString6(apiKeySource.id);
20585
- const apiKeyName = parseNonEmptyString6(apiKeySource.name);
20586
- const proxyUrlCandidate = parseNonEmptyString6(payload.proxyUrl);
20587
- let proxyUrl;
20588
- if (proxyUrlCandidate.length > 0) {
20589
- try {
20590
- const parsed = new URL(proxyUrlCandidate);
20591
- if (parsed.protocol === "https:" || parsed.protocol === "http:") {
20592
- proxyUrl = parsed.toString();
20593
- }
20594
- } catch {
20595
- proxyUrl = void 0;
20596
- }
20597
- }
20765
+ const apiKeyId = parseNonEmptyString7(apiKeySource.id);
20766
+ const apiKeyName = parseNonEmptyString7(apiKeySource.name);
20767
+ const proxyUrl = parseNonEmptyString7(payload.proxyUrl);
20598
20768
  return {
20599
20769
  apiKeyToken,
20600
20770
  apiKeyId: apiKeyId.length > 0 ? apiKeyId : void 0,
@@ -20629,13 +20799,13 @@ async function createInvite(options, dependencies = {}) {
20629
20799
  "content-type": "application/json"
20630
20800
  },
20631
20801
  body: JSON.stringify({
20632
- expiresAt: parseNonEmptyString6(options.expiresAt) || void 0
20802
+ expiresAt: parseNonEmptyString7(options.expiresAt) || void 0
20633
20803
  })
20634
20804
  }
20635
20805
  });
20636
- const responseBody = await parseJsonResponse4(response);
20806
+ const responseBody = await parseJsonResponse5(response);
20637
20807
  if (!response.ok) {
20638
- throw createCliError4(
20808
+ throw createCliError5(
20639
20809
  "CLI_INVITE_CREATE_FAILED",
20640
20810
  mapCreateInviteError(response.status, responseBody)
20641
20811
  );
@@ -20646,9 +20816,9 @@ async function createInvite(options, dependencies = {}) {
20646
20816
  };
20647
20817
  }
20648
20818
  async function redeemInvite(code, options, dependencies = {}) {
20649
- const inviteCode = parseNonEmptyString6(code);
20819
+ const inviteCode = parseNonEmptyString7(code);
20650
20820
  if (inviteCode.length === 0) {
20651
- throw createCliError4(
20821
+ throw createCliError5(
20652
20822
  "CLI_INVITE_REDEEM_CODE_REQUIRED",
20653
20823
  "Invite code is required"
20654
20824
  );
@@ -20665,36 +20835,34 @@ async function redeemInvite(code, options, dependencies = {}) {
20665
20835
  body: JSON.stringify({ code: inviteCode })
20666
20836
  }
20667
20837
  });
20668
- const responseBody = await parseJsonResponse4(response);
20838
+ const responseBody = await parseJsonResponse5(response);
20669
20839
  if (!response.ok) {
20670
- throw createCliError4(
20840
+ throw createCliError5(
20671
20841
  "CLI_INVITE_REDEEM_FAILED",
20672
20842
  mapRedeemInviteError(response.status, responseBody)
20673
20843
  );
20674
20844
  }
20845
+ const parsedRedeem = parseInviteRedeemResponse(responseBody);
20846
+ const proxyUrl = parsedRedeem.proxyUrl.length > 0 ? parsedRedeem.proxyUrl : (await fetchRegistryMetadata(runtime.registryUrl, {
20847
+ fetchImpl: runtime.fetchImpl
20848
+ })).proxyUrl;
20675
20849
  return {
20676
- ...parseInviteRedeemResponse(responseBody),
20850
+ ...parsedRedeem,
20851
+ proxyUrl: normalizeProxyUrl(proxyUrl),
20677
20852
  registryUrl: runtime.registryUrl
20678
20853
  };
20679
20854
  }
20680
- async function persistRedeemConfig(input, dependencies = {}) {
20855
+ async function persistRedeemConfig(registryUrl, apiKeyToken, proxyUrl, dependencies = {}) {
20681
20856
  const setConfigValueImpl = dependencies.setConfigValueImpl ?? setConfigValue;
20682
20857
  try {
20683
- await setConfigValueImpl("registryUrl", input.registryUrl);
20684
- await setConfigValueImpl("apiKey", input.apiKeyToken);
20685
- const resolvedProxyUrl = parseNonEmptyString6(input.proxyUrl) || deriveProxyUrlFromRegistryUrl(input.registryUrl);
20686
- if (!resolvedProxyUrl) {
20687
- throw createCliError4(
20688
- "CLI_INVITE_REDEEM_PROXY_URL_MISSING",
20689
- "Proxy URL is missing from onboarding response and could not be derived from registry URL."
20690
- );
20691
- }
20692
- await setConfigValueImpl("proxyUrl", resolvedProxyUrl);
20858
+ await setConfigValueImpl("registryUrl", registryUrl);
20859
+ await setConfigValueImpl("apiKey", apiKeyToken);
20860
+ await setConfigValueImpl("proxyUrl", proxyUrl);
20693
20861
  } catch (error48) {
20694
20862
  logger7.warn("cli.invite_redeem_config_persist_failed", {
20695
20863
  errorName: error48 instanceof Error ? error48.name : "unknown"
20696
20864
  });
20697
- throw createCliError4(
20865
+ throw createCliError5(
20698
20866
  "CLI_INVITE_REDEEM_CONFIG_PERSISTENCE_FAILED",
20699
20867
  "Failed to save redeemed API key locally"
20700
20868
  );
@@ -20702,7 +20870,7 @@ async function persistRedeemConfig(input, dependencies = {}) {
20702
20870
  }
20703
20871
  var createInviteCommand = (dependencies = {}) => {
20704
20872
  const inviteCommand = new Command6("invite").description(
20705
- "Manage registry onboarding invites"
20873
+ "Manage registry onboarding invites (not OpenClaw peer relay invites)"
20706
20874
  );
20707
20875
  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(
20708
20876
  withErrorHandling(
@@ -20740,15 +20908,12 @@ var createInviteCommand = (dependencies = {}) => {
20740
20908
  writeStdoutLine("API key token (shown once):");
20741
20909
  writeStdoutLine(result.apiKeyToken);
20742
20910
  await persistRedeemConfig(
20743
- {
20744
- registryUrl: result.registryUrl,
20745
- apiKeyToken: result.apiKeyToken,
20746
- proxyUrl: result.proxyUrl
20747
- },
20911
+ result.registryUrl,
20912
+ result.apiKeyToken,
20913
+ result.proxyUrl,
20748
20914
  dependencies
20749
20915
  );
20750
20916
  writeStdoutLine("API key saved to local config");
20751
- writeStdoutLine("Onboarding auth complete");
20752
20917
  }
20753
20918
  )
20754
20919
  );
@@ -20770,8 +20935,16 @@ var SECRET_KEY_FILE_NAME2 = "secret.key";
20770
20935
  var PEERS_FILE_NAME = "peers.json";
20771
20936
  var OPENCLAW_DIR_NAME = ".openclaw";
20772
20937
  var OPENCLAW_CONFIG_FILE_NAME = "openclaw.json";
20773
- var LEGACY_OPENCLAW_STATE_DIR_NAMES = [".clawdbot", ".moldbot", ".moltbot"];
20774
- var LEGACY_OPENCLAW_CONFIG_FILE_NAMES = ["clawdbot.json", "moldbot.json", "moltbot.json"];
20938
+ var LEGACY_OPENCLAW_STATE_DIR_NAMES = [
20939
+ ".clawdbot",
20940
+ ".moldbot",
20941
+ ".moltbot"
20942
+ ];
20943
+ var LEGACY_OPENCLAW_CONFIG_FILE_NAMES = [
20944
+ "clawdbot.json",
20945
+ "moldbot.json",
20946
+ "moltbot.json"
20947
+ ];
20775
20948
  var OPENCLAW_AGENT_FILE_NAME = "openclaw-agent-name";
20776
20949
  var OPENCLAW_RELAY_RUNTIME_FILE_NAME2 = "openclaw-relay.json";
20777
20950
  var OPENCLAW_CONNECTORS_FILE_NAME2 = "openclaw-connectors.json";
@@ -20783,6 +20956,8 @@ var HOOK_MAPPING_ID = "clawdentity-send-to-peer";
20783
20956
  var HOOK_PATH_SEND_TO_PEER = "send-to-peer";
20784
20957
  var OPENCLAW_SEND_TO_PEER_HOOK_PATH = "hooks/send-to-peer";
20785
20958
  var DEFAULT_OPENCLAW_BASE_URL2 = "http://127.0.0.1:18789";
20959
+ var DEFAULT_OPENCLAW_MAIN_SESSION_KEY = "main";
20960
+ var DEFAULT_OPENCLAW_AGENT_ID = "main";
20786
20961
  var DEFAULT_CONNECTOR_PORT = 19400;
20787
20962
  var DEFAULT_CONNECTOR_OUTBOUND_PATH3 = "/v1/outbound";
20788
20963
  var CONNECTOR_HOST_LOOPBACK = "127.0.0.1";
@@ -20798,10 +20973,10 @@ var OPENCLAW_SETUP_WITH_BASE_URL_HINT = `${OPENCLAW_SETUP_COMMAND_HINT} --opencl
20798
20973
  var OPENCLAW_PAIRING_COMMAND_HINT = "Run QR pairing first: clawdentity pair start <agentName> --qr and clawdentity pair confirm <agentName> --qr-file <path>";
20799
20974
  var textEncoder2 = new TextEncoder();
20800
20975
  var textDecoder = new TextDecoder();
20801
- function isRecord7(value) {
20976
+ function isRecord8(value) {
20802
20977
  return typeof value === "object" && value !== null;
20803
20978
  }
20804
- function createCliError5(code, message2, details) {
20979
+ function createCliError6(code, message2, details) {
20805
20980
  return new AppError({
20806
20981
  code,
20807
20982
  message: message2,
@@ -20810,14 +20985,14 @@ function createCliError5(code, message2, details) {
20810
20985
  });
20811
20986
  }
20812
20987
  function getErrorCode2(error48) {
20813
- if (!isRecord7(error48)) {
20988
+ if (!isRecord8(error48)) {
20814
20989
  return void 0;
20815
20990
  }
20816
20991
  return typeof error48.code === "string" ? error48.code : void 0;
20817
20992
  }
20818
- function parseNonEmptyString7(value, label) {
20993
+ function parseNonEmptyString8(value, label) {
20819
20994
  if (typeof value !== "string") {
20820
- throw createCliError5(
20995
+ throw createCliError6(
20821
20996
  "CLI_OPENCLAW_INVALID_INPUT",
20822
20997
  "Input must be a string",
20823
20998
  {
@@ -20827,7 +21002,7 @@ function parseNonEmptyString7(value, label) {
20827
21002
  }
20828
21003
  const trimmed = value.trim();
20829
21004
  if (trimmed.length === 0) {
20830
- throw createCliError5(
21005
+ throw createCliError6(
20831
21006
  "CLI_OPENCLAW_INVALID_INPUT",
20832
21007
  "Input must not be empty",
20833
21008
  { label }
@@ -20839,18 +21014,18 @@ function parseOptionalName(value) {
20839
21014
  if (value === void 0) {
20840
21015
  return void 0;
20841
21016
  }
20842
- return parseNonEmptyString7(value, "name");
21017
+ return parseNonEmptyString8(value, "name");
20843
21018
  }
20844
21019
  function parsePeerAlias(value) {
20845
- const alias = parseNonEmptyString7(value, "peer alias");
21020
+ const alias = parseNonEmptyString8(value, "peer alias");
20846
21021
  if (alias.length > 128) {
20847
- throw createCliError5(
21022
+ throw createCliError6(
20848
21023
  "CLI_OPENCLAW_INVALID_PEER_ALIAS",
20849
21024
  "peer alias must be at most 128 characters"
20850
21025
  );
20851
21026
  }
20852
21027
  if (!PEER_ALIAS_PATTERN.test(alias)) {
20853
- throw createCliError5(
21028
+ throw createCliError6(
20854
21029
  "CLI_OPENCLAW_INVALID_PEER_ALIAS",
20855
21030
  "peer alias must use only letters, numbers, dot, underscore, or hyphen"
20856
21031
  );
@@ -20865,15 +21040,15 @@ function parseProxyUrl(value) {
20865
21040
  });
20866
21041
  }
20867
21042
  function parseHttpUrl(value, input) {
20868
- const candidate = parseNonEmptyString7(value, input.label);
21043
+ const candidate = parseNonEmptyString8(value, input.label);
20869
21044
  let parsedUrl;
20870
21045
  try {
20871
21046
  parsedUrl = new URL(candidate);
20872
21047
  } catch {
20873
- throw createCliError5(input.code, input.message);
21048
+ throw createCliError6(input.code, input.message);
20874
21049
  }
20875
21050
  if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
20876
- throw createCliError5(input.code, `${input.label} must use http or https`);
21051
+ throw createCliError6(input.code, `${input.label} must use http or https`);
20877
21052
  }
20878
21053
  if (parsedUrl.pathname === "/" && parsedUrl.search.length === 0 && parsedUrl.hash.length === 0) {
20879
21054
  return parsedUrl.origin;
@@ -20888,17 +21063,17 @@ function parseOpenclawBaseUrl(value) {
20888
21063
  });
20889
21064
  }
20890
21065
  function parseAgentDid2(value, label) {
20891
- const did = parseNonEmptyString7(value, label);
21066
+ const did = parseNonEmptyString8(value, label);
20892
21067
  try {
20893
21068
  const parsed = parseDid(did);
20894
21069
  if (parsed.kind !== "agent") {
20895
- throw createCliError5(
21070
+ throw createCliError6(
20896
21071
  "CLI_OPENCLAW_INVALID_DID",
20897
21072
  "DID is not an agent DID"
20898
21073
  );
20899
21074
  }
20900
21075
  } catch {
20901
- throw createCliError5("CLI_OPENCLAW_INVALID_DID", "Agent DID is invalid", {
21076
+ throw createCliError6("CLI_OPENCLAW_INVALID_DID", "Agent DID is invalid", {
20902
21077
  label
20903
21078
  });
20904
21079
  }
@@ -20924,7 +21099,10 @@ function readNonEmptyEnvPath(value, homeDir) {
20924
21099
  return resolveHomePrefixedPath(value, homeDir);
20925
21100
  }
20926
21101
  function resolveOpenclawHomeDir(homeDir) {
20927
- const envOpenclawHome = readNonEmptyEnvPath(process.env.OPENCLAW_HOME, homeDir);
21102
+ const envOpenclawHome = readNonEmptyEnvPath(
21103
+ process.env.OPENCLAW_HOME,
21104
+ homeDir
21105
+ );
20928
21106
  return envOpenclawHome ?? homeDir;
20929
21107
  }
20930
21108
  function resolveDefaultOpenclawStateDir(openclawHomeDir) {
@@ -21020,7 +21198,7 @@ async function readJsonFile(filePath) {
21020
21198
  try {
21021
21199
  return JSON.parse(raw);
21022
21200
  } catch {
21023
- throw createCliError5("CLI_OPENCLAW_INVALID_JSON", "JSON file is invalid", {
21201
+ throw createCliError6("CLI_OPENCLAW_INVALID_JSON", "JSON file is invalid", {
21024
21202
  filePath
21025
21203
  });
21026
21204
  }
@@ -21037,7 +21215,7 @@ async function ensureLocalAgentCredentials(homeDir, agentName) {
21037
21215
  content = await readFile4(filePath, "utf8");
21038
21216
  } catch (error48) {
21039
21217
  if (getErrorCode2(error48) === "ENOENT") {
21040
- throw createCliError5(
21218
+ throw createCliError6(
21041
21219
  "CLI_OPENCLAW_MISSING_AGENT_CREDENTIALS",
21042
21220
  "Local agent credentials are missing",
21043
21221
  { agentName, filePath }
@@ -21046,7 +21224,7 @@ async function ensureLocalAgentCredentials(homeDir, agentName) {
21046
21224
  throw error48;
21047
21225
  }
21048
21226
  if (content.trim().length === 0) {
21049
- throw createCliError5(
21227
+ throw createCliError6(
21050
21228
  "CLI_OPENCLAW_EMPTY_AGENT_CREDENTIALS",
21051
21229
  "Agent credential file is empty",
21052
21230
  { filePath }
@@ -21069,8 +21247,8 @@ async function loadPeersConfig(peersPath) {
21069
21247
  }
21070
21248
  throw error48;
21071
21249
  }
21072
- if (!isRecord7(parsed)) {
21073
- throw createCliError5(
21250
+ if (!isRecord8(parsed)) {
21251
+ throw createCliError6(
21074
21252
  "CLI_OPENCLAW_INVALID_PEERS_CONFIG",
21075
21253
  "Peer config root must be a JSON object",
21076
21254
  { peersPath }
@@ -21080,8 +21258,8 @@ async function loadPeersConfig(peersPath) {
21080
21258
  if (peersValue === void 0) {
21081
21259
  return { peers: {} };
21082
21260
  }
21083
- if (!isRecord7(peersValue)) {
21084
- throw createCliError5(
21261
+ if (!isRecord8(peersValue)) {
21262
+ throw createCliError6(
21085
21263
  "CLI_OPENCLAW_INVALID_PEERS_CONFIG",
21086
21264
  "Peer config peers field must be an object",
21087
21265
  { peersPath }
@@ -21090,8 +21268,8 @@ async function loadPeersConfig(peersPath) {
21090
21268
  const peers = {};
21091
21269
  for (const [alias, value] of Object.entries(peersValue)) {
21092
21270
  const normalizedAlias = parsePeerAlias(alias);
21093
- if (!isRecord7(value)) {
21094
- throw createCliError5(
21271
+ if (!isRecord8(value)) {
21272
+ throw createCliError6(
21095
21273
  "CLI_OPENCLAW_INVALID_PEERS_CONFIG",
21096
21274
  "Peer entry must be an object",
21097
21275
  { alias: normalizedAlias }
@@ -21120,21 +21298,21 @@ function parseConnectorBaseUrlForAssignment(value, label) {
21120
21298
  });
21121
21299
  }
21122
21300
  function parseConnectorAssignments(value, connectorAssignmentsPath) {
21123
- if (!isRecord7(value)) {
21124
- throw createCliError5(
21301
+ if (!isRecord8(value)) {
21302
+ throw createCliError6(
21125
21303
  "CLI_OPENCLAW_INVALID_CONNECTOR_ASSIGNMENTS",
21126
21304
  "Connector assignments config must be an object",
21127
21305
  { connectorAssignmentsPath }
21128
21306
  );
21129
21307
  }
21130
21308
  const agentsRaw = value.agents;
21131
- if (!isRecord7(agentsRaw)) {
21309
+ if (!isRecord8(agentsRaw)) {
21132
21310
  return { agents: {} };
21133
21311
  }
21134
21312
  const agents = {};
21135
21313
  for (const [agentName, entryValue] of Object.entries(agentsRaw)) {
21136
- if (!isRecord7(entryValue)) {
21137
- throw createCliError5(
21314
+ if (!isRecord8(entryValue)) {
21315
+ throw createCliError6(
21138
21316
  "CLI_OPENCLAW_INVALID_CONNECTOR_ASSIGNMENTS",
21139
21317
  "Connector assignment entry must be an object",
21140
21318
  { connectorAssignmentsPath, agentName }
@@ -21205,8 +21383,8 @@ function buildRelayConnectorBaseUrls(port) {
21205
21383
  ];
21206
21384
  }
21207
21385
  function parseRelayRuntimeConfig(value, relayRuntimeConfigPath) {
21208
- if (!isRecord7(value)) {
21209
- throw createCliError5(
21386
+ if (!isRecord8(value)) {
21387
+ throw createCliError6(
21210
21388
  "CLI_OPENCLAW_INVALID_RELAY_RUNTIME_CONFIG",
21211
21389
  "Relay runtime config must be an object",
21212
21390
  { relayRuntimeConfigPath }
@@ -21260,7 +21438,7 @@ async function resolveOpenclawBaseUrl2(input) {
21260
21438
  }
21261
21439
  return DEFAULT_OPENCLAW_BASE_URL2;
21262
21440
  }
21263
- function normalizeStringArrayWithValue(value, requiredValue) {
21441
+ function normalizeStringArrayWithValues(value, requiredValues) {
21264
21442
  const normalized = /* @__PURE__ */ new Set();
21265
21443
  if (Array.isArray(value)) {
21266
21444
  for (const item of value) {
@@ -21273,27 +21451,61 @@ function normalizeStringArrayWithValue(value, requiredValue) {
21273
21451
  }
21274
21452
  }
21275
21453
  }
21276
- normalized.add(requiredValue);
21454
+ for (const requiredValue of requiredValues) {
21455
+ const trimmed = requiredValue.trim();
21456
+ if (trimmed.length > 0) {
21457
+ normalized.add(trimmed);
21458
+ }
21459
+ }
21277
21460
  return Array.from(normalized);
21278
21461
  }
21462
+ function resolveHookDefaultSessionKey(config2, hooks) {
21463
+ if (typeof hooks.defaultSessionKey === "string" && hooks.defaultSessionKey.trim().length > 0) {
21464
+ return hooks.defaultSessionKey.trim();
21465
+ }
21466
+ const session = isRecord8(config2.session) ? config2.session : {};
21467
+ const scope = typeof session.scope === "string" ? session.scope.trim().toLowerCase() : "";
21468
+ if (scope === "global") {
21469
+ return "global";
21470
+ }
21471
+ const agents = isRecord8(config2.agents) ? config2.agents : {};
21472
+ const agentList = Array.isArray(agents.list) ? agents.list : [];
21473
+ const defaultAgentId = resolveDefaultOpenclawAgentId(agentList);
21474
+ return `agent:${defaultAgentId}:${DEFAULT_OPENCLAW_MAIN_SESSION_KEY}`;
21475
+ }
21476
+ function resolveDefaultOpenclawAgentId(agentList) {
21477
+ const preferred = agentList.find(
21478
+ (agent) => isRecord8(agent) && agent.default === true && typeof agent.id === "string" && agent.id.trim().length > 0
21479
+ ) ?? agentList.find(
21480
+ (agent) => isRecord8(agent) && typeof agent.id === "string" && agent.id.trim().length > 0
21481
+ );
21482
+ if (isRecord8(preferred) && typeof preferred.id === "string" && preferred.id.trim().length > 0) {
21483
+ return normalizeOpenclawIdToken(preferred.id, DEFAULT_OPENCLAW_AGENT_ID);
21484
+ }
21485
+ return DEFAULT_OPENCLAW_AGENT_ID;
21486
+ }
21487
+ function normalizeOpenclawIdToken(value, fallback) {
21488
+ const normalized = value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+/g, "").replace(/-+$/g, "").slice(0, 64);
21489
+ return normalized.length > 0 ? normalized : fallback;
21490
+ }
21279
21491
  function generateOpenclawHookToken() {
21280
21492
  return randomBytes3(OPENCLAW_HOOK_TOKEN_BYTES).toString("hex");
21281
21493
  }
21282
21494
  function upsertRelayHookMapping(mappingsValue) {
21283
- const mappings = Array.isArray(mappingsValue) ? mappingsValue.filter(isRecord7).map((mapping) => ({ ...mapping })) : [];
21495
+ const mappings = Array.isArray(mappingsValue) ? mappingsValue.filter(isRecord8).map((mapping) => ({ ...mapping })) : [];
21284
21496
  const existingIndex = mappings.findIndex((mapping) => {
21285
21497
  if (mapping.id === HOOK_MAPPING_ID) {
21286
21498
  return true;
21287
21499
  }
21288
- if (!isRecord7(mapping.match)) {
21500
+ if (!isRecord8(mapping.match)) {
21289
21501
  return false;
21290
21502
  }
21291
21503
  return mapping.match.path === HOOK_PATH_SEND_TO_PEER;
21292
21504
  });
21293
- const baseMapping = existingIndex >= 0 && isRecord7(mappings[existingIndex]) ? mappings[existingIndex] : {};
21294
- const nextMatch = isRecord7(baseMapping.match) ? { ...baseMapping.match } : {};
21505
+ const baseMapping = existingIndex >= 0 && isRecord8(mappings[existingIndex]) ? mappings[existingIndex] : {};
21506
+ const nextMatch = isRecord8(baseMapping.match) ? { ...baseMapping.match } : {};
21295
21507
  nextMatch.path = HOOK_PATH_SEND_TO_PEER;
21296
- const nextTransform = isRecord7(baseMapping.transform) ? { ...baseMapping.transform } : {};
21508
+ const nextTransform = isRecord8(baseMapping.transform) ? { ...baseMapping.transform } : {};
21297
21509
  nextTransform.module = RELAY_MODULE_FILE_NAME;
21298
21510
  const relayMapping = {
21299
21511
  ...baseMapping,
@@ -21316,7 +21528,7 @@ async function patchOpenclawConfig(openclawConfigPath, hookToken) {
21316
21528
  config2 = await readJsonFile(openclawConfigPath);
21317
21529
  } catch (error48) {
21318
21530
  if (getErrorCode2(error48) === "ENOENT") {
21319
- throw createCliError5(
21531
+ throw createCliError6(
21320
21532
  "CLI_OPENCLAW_CONFIG_NOT_FOUND",
21321
21533
  "OpenClaw config file was not found",
21322
21534
  { openclawConfigPath }
@@ -21324,23 +21536,25 @@ async function patchOpenclawConfig(openclawConfigPath, hookToken) {
21324
21536
  }
21325
21537
  throw error48;
21326
21538
  }
21327
- if (!isRecord7(config2)) {
21328
- throw createCliError5(
21539
+ if (!isRecord8(config2)) {
21540
+ throw createCliError6(
21329
21541
  "CLI_OPENCLAW_INVALID_CONFIG",
21330
21542
  "OpenClaw config root must be an object",
21331
21543
  { openclawConfigPath }
21332
21544
  );
21333
21545
  }
21334
- const hooks = isRecord7(config2.hooks) ? { ...config2.hooks } : {};
21546
+ const hooks = isRecord8(config2.hooks) ? { ...config2.hooks } : {};
21335
21547
  const existingHookToken = typeof hooks.token === "string" && hooks.token.trim().length > 0 ? hooks.token.trim() : void 0;
21336
21548
  const preferredHookToken = typeof hookToken === "string" && hookToken.trim().length > 0 ? hookToken.trim() : void 0;
21337
21549
  const resolvedHookToken = existingHookToken ?? preferredHookToken ?? generateOpenclawHookToken();
21550
+ const defaultSessionKey = resolveHookDefaultSessionKey(config2, hooks);
21338
21551
  hooks.enabled = true;
21339
21552
  hooks.token = resolvedHookToken;
21553
+ hooks.defaultSessionKey = defaultSessionKey;
21340
21554
  hooks.allowRequestSessionKey = false;
21341
- hooks.allowedSessionKeyPrefixes = normalizeStringArrayWithValue(
21555
+ hooks.allowedSessionKeyPrefixes = normalizeStringArrayWithValues(
21342
21556
  hooks.allowedSessionKeyPrefixes,
21343
- "hook:"
21557
+ ["hook:", defaultSessionKey]
21344
21558
  );
21345
21559
  hooks.mappings = upsertRelayHookMapping(hooks.mappings);
21346
21560
  const nextConfig = {
@@ -21368,10 +21582,10 @@ function toDoctorResult(checks) {
21368
21582
  };
21369
21583
  }
21370
21584
  function isRelayHookMapping(value) {
21371
- if (!isRecord7(value)) {
21585
+ if (!isRecord8(value)) {
21372
21586
  return false;
21373
21587
  }
21374
- if (!isRecord7(value.match) || value.match.path !== HOOK_PATH_SEND_TO_PEER) {
21588
+ if (!isRecord8(value.match) || value.match.path !== HOOK_PATH_SEND_TO_PEER) {
21375
21589
  return false;
21376
21590
  }
21377
21591
  if (typeof value.id === "string" && value.id !== HOOK_MAPPING_ID) {
@@ -21380,7 +21594,7 @@ function isRelayHookMapping(value) {
21380
21594
  return true;
21381
21595
  }
21382
21596
  function hasRelayTransformModule(value) {
21383
- if (!isRecord7(value) || !isRecord7(value.transform)) {
21597
+ if (!isRecord8(value) || !isRecord8(value.transform)) {
21384
21598
  return false;
21385
21599
  }
21386
21600
  return value.transform.module === RELAY_MODULE_FILE_NAME;
@@ -21459,6 +21673,7 @@ async function runOpenclawDoctor(options = {}) {
21459
21673
  const resolveConfigImpl = options.resolveConfigImpl ?? resolveConfig;
21460
21674
  try {
21461
21675
  const resolvedConfig = await resolveConfigImpl();
21676
+ const envProxyUrl = typeof process.env.CLAWDENTITY_PROXY_URL === "string" ? process.env.CLAWDENTITY_PROXY_URL.trim() : "";
21462
21677
  if (typeof resolvedConfig.registryUrl !== "string" || resolvedConfig.registryUrl.trim().length === 0) {
21463
21678
  checks.push(
21464
21679
  toDoctorCheck({
@@ -21479,15 +21694,68 @@ async function runOpenclawDoctor(options = {}) {
21479
21694
  remediationHint: "Run: clawdentity config set apiKey <API_KEY>"
21480
21695
  })
21481
21696
  );
21482
- } else {
21697
+ } else if (envProxyUrl.length > 0) {
21698
+ let hasValidEnvProxyUrl = true;
21699
+ try {
21700
+ parseProxyUrl(envProxyUrl);
21701
+ } catch {
21702
+ hasValidEnvProxyUrl = false;
21703
+ checks.push(
21704
+ toDoctorCheck({
21705
+ id: "config.registry",
21706
+ label: "CLI config",
21707
+ status: "fail",
21708
+ message: "CLAWDENTITY_PROXY_URL is invalid",
21709
+ remediationHint: "Set CLAWDENTITY_PROXY_URL to a valid http(s) URL or unset it"
21710
+ })
21711
+ );
21712
+ }
21713
+ if (hasValidEnvProxyUrl) {
21714
+ checks.push(
21715
+ toDoctorCheck({
21716
+ id: "config.registry",
21717
+ label: "CLI config",
21718
+ status: "pass",
21719
+ message: "registryUrl and apiKey are configured (proxy URL override is active via CLAWDENTITY_PROXY_URL)"
21720
+ })
21721
+ );
21722
+ }
21723
+ } else if (typeof resolvedConfig.proxyUrl !== "string" || resolvedConfig.proxyUrl.trim().length === 0) {
21483
21724
  checks.push(
21484
21725
  toDoctorCheck({
21485
21726
  id: "config.registry",
21486
21727
  label: "CLI config",
21487
- status: "pass",
21488
- message: "registryUrl and apiKey are configured"
21728
+ status: "fail",
21729
+ message: "proxyUrl is missing",
21730
+ remediationHint: "Run: clawdentity invite redeem <clw_inv_...> or clawdentity config init"
21489
21731
  })
21490
21732
  );
21733
+ } else {
21734
+ let hasValidConfigProxyUrl = true;
21735
+ try {
21736
+ parseProxyUrl(resolvedConfig.proxyUrl);
21737
+ } catch {
21738
+ hasValidConfigProxyUrl = false;
21739
+ checks.push(
21740
+ toDoctorCheck({
21741
+ id: "config.registry",
21742
+ label: "CLI config",
21743
+ status: "fail",
21744
+ message: "proxyUrl is invalid",
21745
+ remediationHint: "Run: clawdentity invite redeem <clw_inv_...> or clawdentity config init"
21746
+ })
21747
+ );
21748
+ }
21749
+ if (hasValidConfigProxyUrl) {
21750
+ checks.push(
21751
+ toDoctorCheck({
21752
+ id: "config.registry",
21753
+ label: "CLI config",
21754
+ status: "pass",
21755
+ message: "registryUrl, apiKey, and proxyUrl are configured"
21756
+ })
21757
+ );
21758
+ }
21491
21759
  }
21492
21760
  } catch {
21493
21761
  checks.push(
@@ -21681,13 +21949,13 @@ async function runOpenclawDoctor(options = {}) {
21681
21949
  const openclawConfigPath = resolveOpenclawConfigPath(openclawDir, homeDir);
21682
21950
  try {
21683
21951
  const openclawConfig = await readJsonFile(openclawConfigPath);
21684
- if (!isRecord7(openclawConfig)) {
21952
+ if (!isRecord8(openclawConfig)) {
21685
21953
  throw new Error("root");
21686
21954
  }
21687
- const hooks = isRecord7(openclawConfig.hooks) ? openclawConfig.hooks : {};
21955
+ const hooks = isRecord8(openclawConfig.hooks) ? openclawConfig.hooks : {};
21688
21956
  const hooksEnabled = hooks.enabled === true;
21689
21957
  const hookToken = typeof hooks.token === "string" && hooks.token.trim().length > 0 ? hooks.token.trim() : void 0;
21690
- const mappings = Array.isArray(hooks.mappings) ? hooks.mappings.filter(isRecord7) : [];
21958
+ const mappings = Array.isArray(hooks.mappings) ? hooks.mappings.filter(isRecord8) : [];
21691
21959
  const relayMapping = mappings.find(
21692
21960
  (mapping) => isRelayHookMapping(mapping)
21693
21961
  );
@@ -21835,13 +22103,13 @@ async function resolveRelayProbePeerAlias(input) {
21835
22103
  return peerAliases[0];
21836
22104
  }
21837
22105
  if (peerAliases.length === 0) {
21838
- throw createCliError5(
22106
+ throw createCliError6(
21839
22107
  "CLI_OPENCLAW_RELAY_TEST_PEER_REQUIRED",
21840
22108
  "No paired peer is configured yet. Complete QR pairing first.",
21841
22109
  { peersPath }
21842
22110
  );
21843
22111
  }
21844
- throw createCliError5(
22112
+ throw createCliError6(
21845
22113
  "CLI_OPENCLAW_RELAY_TEST_PEER_REQUIRED",
21846
22114
  "Multiple peers are configured. Pass --peer <alias> to choose one.",
21847
22115
  { peersPath, peerAliases }
@@ -21998,7 +22266,7 @@ async function setupOpenclawRelay(agentName, options) {
21998
22266
  await copyFile(transformSource, transformTargetPath);
21999
22267
  } catch (error48) {
22000
22268
  if (getErrorCode2(error48) === "ENOENT") {
22001
- throw createCliError5(
22269
+ throw createCliError6(
22002
22270
  "CLI_OPENCLAW_TRANSFORM_NOT_FOUND",
22003
22271
  "Relay transform source file was not found",
22004
22272
  { transformSource }
@@ -22216,26 +22484,26 @@ var PAIRING_QR_MAX_AGE_SECONDS = 900;
22216
22484
  var PAIRING_QR_FILENAME_PATTERN = /-pair-(\d+)\.png$/;
22217
22485
  var FILE_MODE4 = 384;
22218
22486
  var PEER_ALIAS_PATTERN2 = /^[a-zA-Z0-9._-]+$/;
22219
- var isRecord8 = (value) => {
22487
+ var isRecord9 = (value) => {
22220
22488
  return typeof value === "object" && value !== null;
22221
22489
  };
22222
- function createCliError6(code, message2) {
22490
+ function createCliError7(code, message2) {
22223
22491
  return new AppError({
22224
22492
  code,
22225
22493
  message: message2,
22226
22494
  status: 400
22227
22495
  });
22228
22496
  }
22229
- function parseNonEmptyString8(value) {
22497
+ function parseNonEmptyString9(value) {
22230
22498
  if (typeof value !== "string") {
22231
22499
  return "";
22232
22500
  }
22233
22501
  return value.trim();
22234
22502
  }
22235
22503
  function parsePairingTicket(value) {
22236
- const ticket = parseNonEmptyString8(value);
22504
+ const ticket = parseNonEmptyString9(value);
22237
22505
  if (!ticket.startsWith(PAIRING_TICKET_PREFIX)) {
22238
- throw createCliError6(
22506
+ throw createCliError7(
22239
22507
  "CLI_PAIR_CONFIRM_TICKET_INVALID",
22240
22508
  "Pairing ticket is invalid"
22241
22509
  );
@@ -22245,7 +22513,7 @@ function parsePairingTicket(value) {
22245
22513
  function parsePairingTicketIssuerOrigin(ticket) {
22246
22514
  const encodedPayload = ticket.slice(PAIRING_TICKET_PREFIX.length);
22247
22515
  if (encodedPayload.length === 0) {
22248
- throw createCliError6(
22516
+ throw createCliError7(
22249
22517
  "CLI_PAIR_CONFIRM_TICKET_INVALID",
22250
22518
  "Pairing ticket is invalid"
22251
22519
  );
@@ -22254,7 +22522,7 @@ function parsePairingTicketIssuerOrigin(ticket) {
22254
22522
  try {
22255
22523
  payloadRaw = new TextDecoder().decode(decodeBase64url(encodedPayload));
22256
22524
  } catch {
22257
- throw createCliError6(
22525
+ throw createCliError7(
22258
22526
  "CLI_PAIR_CONFIRM_TICKET_INVALID",
22259
22527
  "Pairing ticket is invalid"
22260
22528
  );
@@ -22263,13 +22531,13 @@ function parsePairingTicketIssuerOrigin(ticket) {
22263
22531
  try {
22264
22532
  payload = JSON.parse(payloadRaw);
22265
22533
  } catch {
22266
- throw createCliError6(
22534
+ throw createCliError7(
22267
22535
  "CLI_PAIR_CONFIRM_TICKET_INVALID",
22268
22536
  "Pairing ticket is invalid"
22269
22537
  );
22270
22538
  }
22271
- if (!isRecord8(payload) || typeof payload.iss !== "string") {
22272
- throw createCliError6(
22539
+ if (!isRecord9(payload) || typeof payload.iss !== "string") {
22540
+ throw createCliError7(
22273
22541
  "CLI_PAIR_CONFIRM_TICKET_INVALID",
22274
22542
  "Pairing ticket is invalid"
22275
22543
  );
@@ -22278,13 +22546,13 @@ function parsePairingTicketIssuerOrigin(ticket) {
22278
22546
  try {
22279
22547
  issuerUrl = new URL(payload.iss);
22280
22548
  } catch {
22281
- throw createCliError6(
22549
+ throw createCliError7(
22282
22550
  "CLI_PAIR_CONFIRM_TICKET_INVALID",
22283
22551
  "Pairing ticket is invalid"
22284
22552
  );
22285
22553
  }
22286
22554
  if (issuerUrl.protocol !== "https:" && issuerUrl.protocol !== "http:") {
22287
- throw createCliError6(
22555
+ throw createCliError7(
22288
22556
  "CLI_PAIR_CONFIRM_TICKET_INVALID",
22289
22557
  "Pairing ticket is invalid"
22290
22558
  );
@@ -22293,13 +22561,13 @@ function parsePairingTicketIssuerOrigin(ticket) {
22293
22561
  }
22294
22562
  function parsePeerAlias2(value) {
22295
22563
  if (value.length === 0 || value.length > 128) {
22296
- throw createCliError6(
22564
+ throw createCliError7(
22297
22565
  "CLI_PAIR_PEER_ALIAS_INVALID",
22298
22566
  "Generated peer alias is invalid"
22299
22567
  );
22300
22568
  }
22301
22569
  if (!PEER_ALIAS_PATTERN2.test(value)) {
22302
- throw createCliError6(
22570
+ throw createCliError7(
22303
22571
  "CLI_PAIR_PEER_ALIAS_INVALID",
22304
22572
  "Generated peer alias is invalid"
22305
22573
  );
@@ -22336,16 +22604,16 @@ function resolvePeersConfigPath(getConfigDirImpl) {
22336
22604
  return join7(getConfigDirImpl(), PEERS_FILE_NAME2);
22337
22605
  }
22338
22606
  function parsePeerEntry(value) {
22339
- if (!isRecord8(value)) {
22340
- throw createCliError6(
22607
+ if (!isRecord9(value)) {
22608
+ throw createCliError7(
22341
22609
  "CLI_PAIR_PEERS_CONFIG_INVALID",
22342
22610
  "Peer entry must be an object"
22343
22611
  );
22344
22612
  }
22345
- const did = parseNonEmptyString8(value.did);
22346
- const proxyUrl = parseNonEmptyString8(value.proxyUrl);
22613
+ const did = parseNonEmptyString9(value.did);
22614
+ const proxyUrl = parseNonEmptyString9(value.proxyUrl);
22347
22615
  if (did.length === 0 || proxyUrl.length === 0) {
22348
- throw createCliError6(
22616
+ throw createCliError7(
22349
22617
  "CLI_PAIR_PEERS_CONFIG_INVALID",
22350
22618
  "Peer entry is invalid"
22351
22619
  );
@@ -22371,13 +22639,13 @@ async function loadPeersConfig2(input) {
22371
22639
  try {
22372
22640
  parsed = JSON.parse(raw);
22373
22641
  } catch {
22374
- throw createCliError6(
22642
+ throw createCliError7(
22375
22643
  "CLI_PAIR_PEERS_CONFIG_INVALID",
22376
22644
  "Peer config is not valid JSON"
22377
22645
  );
22378
22646
  }
22379
- if (!isRecord8(parsed)) {
22380
- throw createCliError6(
22647
+ if (!isRecord9(parsed)) {
22648
+ throw createCliError7(
22381
22649
  "CLI_PAIR_PEERS_CONFIG_INVALID",
22382
22650
  "Peer config must be a JSON object"
22383
22651
  );
@@ -22385,8 +22653,8 @@ async function loadPeersConfig2(input) {
22385
22653
  if (parsed.peers === void 0) {
22386
22654
  return { peers: {} };
22387
22655
  }
22388
- if (!isRecord8(parsed.peers)) {
22389
- throw createCliError6(
22656
+ if (!isRecord9(parsed.peers)) {
22657
+ throw createCliError7(
22390
22658
  "CLI_PAIR_PEERS_CONFIG_INVALID",
22391
22659
  "Peer config peers field must be an object"
22392
22660
  );
@@ -22409,13 +22677,13 @@ async function savePeersConfig2(input) {
22409
22677
  await input.chmodImpl(peersPath, FILE_MODE4);
22410
22678
  }
22411
22679
  function parseTtlSeconds(value) {
22412
- const raw = parseNonEmptyString8(value);
22680
+ const raw = parseNonEmptyString9(value);
22413
22681
  if (raw.length === 0) {
22414
22682
  return void 0;
22415
22683
  }
22416
22684
  const parsed = Number.parseInt(raw, 10);
22417
22685
  if (!Number.isInteger(parsed) || parsed < 1) {
22418
- throw createCliError6(
22686
+ throw createCliError7(
22419
22687
  "CLI_PAIR_START_INVALID_TTL",
22420
22688
  "ttlSeconds must be a positive integer"
22421
22689
  );
@@ -22430,36 +22698,31 @@ function parseProxyUrl2(candidate) {
22430
22698
  }
22431
22699
  return parsed.toString();
22432
22700
  } catch {
22433
- throw createCliError6("CLI_PAIR_INVALID_PROXY_URL", "Proxy URL is invalid");
22701
+ throw createCliError7("CLI_PAIR_INVALID_PROXY_URL", "Proxy URL is invalid");
22434
22702
  }
22435
22703
  }
22436
- function resolveProxyUrlCandidates(input) {
22437
- const explicit = parseNonEmptyString8(input.overrideProxyUrl);
22438
- if (explicit.length > 0) {
22439
- return [parseProxyUrl2(explicit)];
22440
- }
22441
- const fromEnv = parseNonEmptyString8(process.env.CLAWDENTITY_PROXY_URL);
22704
+ async function resolveProxyUrl(input) {
22705
+ const fromEnv = parseNonEmptyString9(process.env.CLAWDENTITY_PROXY_URL);
22442
22706
  if (fromEnv.length > 0) {
22443
- return [parseProxyUrl2(fromEnv)];
22707
+ return parseProxyUrl2(fromEnv);
22444
22708
  }
22445
- const fromConfig = parseNonEmptyString8(input.config.proxyUrl);
22446
- if (fromConfig.length > 0) {
22447
- return [parseProxyUrl2(fromConfig)];
22709
+ const metadata = await fetchRegistryMetadata(input.config.registryUrl, {
22710
+ fetchImpl: input.fetchImpl
22711
+ });
22712
+ const metadataProxyUrl = parseProxyUrl2(metadata.proxyUrl);
22713
+ const configuredProxyUrl = parseNonEmptyString9(input.config.proxyUrl);
22714
+ if (configuredProxyUrl.length === 0) {
22715
+ return metadataProxyUrl;
22448
22716
  }
22449
- const derivedFromRegistry = deriveProxyUrlFromRegistryUrl(
22450
- input.config.registryUrl || DEFAULT_REGISTRY_URL
22451
- );
22452
- if (typeof derivedFromRegistry === "string" && derivedFromRegistry.length > 0) {
22453
- return [parseProxyUrl2(derivedFromRegistry)];
22717
+ const normalizedConfiguredProxyUrl = parseProxyUrl2(configuredProxyUrl);
22718
+ if (normalizedConfiguredProxyUrl === metadataProxyUrl) {
22719
+ return metadataProxyUrl;
22454
22720
  }
22455
- throw createCliError6(
22456
- "CLI_PAIR_PROXY_URL_REQUIRED",
22457
- "Proxy URL could not be resolved. Run onboarding invite redeem again or set proxyUrl via `clawdentity config set proxyUrl <url>`."
22721
+ throw createCliError7(
22722
+ "CLI_PAIR_PROXY_URL_MISMATCH",
22723
+ `Configured proxy URL does not match registry metadata. config=${normalizedConfiguredProxyUrl} metadata=${metadataProxyUrl}. Rerun onboarding invite redeem to refresh config.`
22458
22724
  );
22459
22725
  }
22460
- function resolveProxyUrl(input) {
22461
- return resolveProxyUrlCandidates(input)[0];
22462
- }
22463
22726
  function toProxyRequestUrl(proxyUrl, path) {
22464
22727
  const normalizedBase = proxyUrl.endsWith("/") ? proxyUrl : `${proxyUrl}/`;
22465
22728
  return new URL(path.slice(1), normalizedBase).toString();
@@ -22469,7 +22732,7 @@ function toPathWithQuery3(url2) {
22469
22732
  return `${parsed.pathname}${parsed.search}`;
22470
22733
  }
22471
22734
  function extractErrorCode(payload) {
22472
- if (!isRecord8(payload)) {
22735
+ if (!isRecord9(payload)) {
22473
22736
  return void 0;
22474
22737
  }
22475
22738
  const envelope = payload;
@@ -22480,7 +22743,7 @@ function extractErrorCode(payload) {
22480
22743
  return code.length > 0 ? code : void 0;
22481
22744
  }
22482
22745
  function extractErrorMessage(payload) {
22483
- if (!isRecord8(payload)) {
22746
+ if (!isRecord9(payload)) {
22484
22747
  return void 0;
22485
22748
  }
22486
22749
  const envelope = payload;
@@ -22490,7 +22753,7 @@ function extractErrorMessage(payload) {
22490
22753
  const message2 = envelope.error.message.trim();
22491
22754
  return message2.length > 0 ? message2 : void 0;
22492
22755
  }
22493
- async function parseJsonResponse5(response) {
22756
+ async function parseJsonResponse6(response) {
22494
22757
  try {
22495
22758
  return await response.json();
22496
22759
  } catch {
@@ -22501,7 +22764,7 @@ async function executePairRequest(input) {
22501
22764
  try {
22502
22765
  return await input.fetchImpl(input.url, input.init);
22503
22766
  } catch {
22504
- throw createCliError6(
22767
+ throw createCliError7(
22505
22768
  "CLI_PAIR_REQUEST_FAILED",
22506
22769
  "Unable to connect to proxy URL. Check network access and proxyUrl."
22507
22770
  );
@@ -22548,17 +22811,17 @@ function mapConfirmPairError(status, payload) {
22548
22811
  return `Pair confirm failed (${status})`;
22549
22812
  }
22550
22813
  function parsePairStartResponse(payload) {
22551
- if (!isRecord8(payload)) {
22552
- throw createCliError6(
22814
+ if (!isRecord9(payload)) {
22815
+ throw createCliError7(
22553
22816
  "CLI_PAIR_START_INVALID_RESPONSE",
22554
22817
  "Pair start response is invalid"
22555
22818
  );
22556
22819
  }
22557
22820
  const ticket = parsePairingTicket(payload.ticket);
22558
- const initiatorAgentDid = parseNonEmptyString8(payload.initiatorAgentDid);
22559
- const expiresAt = parseNonEmptyString8(payload.expiresAt);
22821
+ const initiatorAgentDid = parseNonEmptyString9(payload.initiatorAgentDid);
22822
+ const expiresAt = parseNonEmptyString9(payload.expiresAt);
22560
22823
  if (initiatorAgentDid.length === 0 || expiresAt.length === 0) {
22561
- throw createCliError6(
22824
+ throw createCliError7(
22562
22825
  "CLI_PAIR_START_INVALID_RESPONSE",
22563
22826
  "Pair start response is invalid"
22564
22827
  );
@@ -22570,17 +22833,17 @@ function parsePairStartResponse(payload) {
22570
22833
  };
22571
22834
  }
22572
22835
  function parsePairConfirmResponse(payload) {
22573
- if (!isRecord8(payload)) {
22574
- throw createCliError6(
22836
+ if (!isRecord9(payload)) {
22837
+ throw createCliError7(
22575
22838
  "CLI_PAIR_CONFIRM_INVALID_RESPONSE",
22576
22839
  "Pair confirm response is invalid"
22577
22840
  );
22578
22841
  }
22579
22842
  const paired = payload.paired === true;
22580
- const initiatorAgentDid = parseNonEmptyString8(payload.initiatorAgentDid);
22581
- const responderAgentDid = parseNonEmptyString8(payload.responderAgentDid);
22843
+ const initiatorAgentDid = parseNonEmptyString9(payload.initiatorAgentDid);
22844
+ const responderAgentDid = parseNonEmptyString9(payload.responderAgentDid);
22582
22845
  if (!paired || initiatorAgentDid.length === 0 || responderAgentDid.length === 0) {
22583
- throw createCliError6(
22846
+ throw createCliError7(
22584
22847
  "CLI_PAIR_CONFIRM_INVALID_RESPONSE",
22585
22848
  "Pair confirm response is invalid"
22586
22849
  );
@@ -22608,7 +22871,7 @@ async function readAgentProofMaterial(agentName, dependencies) {
22608
22871
  } catch (error48) {
22609
22872
  const nodeError = error48;
22610
22873
  if (nodeError.code === "ENOENT") {
22611
- throw createCliError6(
22874
+ throw createCliError7(
22612
22875
  "CLI_PAIR_AGENT_NOT_FOUND",
22613
22876
  `Agent "${normalizedAgentName}" is missing ${AIT_FILE_NAME4}. Run agent create first.`
22614
22877
  );
@@ -22616,7 +22879,7 @@ async function readAgentProofMaterial(agentName, dependencies) {
22616
22879
  throw error48;
22617
22880
  }
22618
22881
  if (ait.length === 0) {
22619
- throw createCliError6(
22882
+ throw createCliError7(
22620
22883
  "CLI_PAIR_AGENT_NOT_FOUND",
22621
22884
  `Agent "${normalizedAgentName}" has an empty ${AIT_FILE_NAME4}`
22622
22885
  );
@@ -22627,7 +22890,7 @@ async function readAgentProofMaterial(agentName, dependencies) {
22627
22890
  } catch (error48) {
22628
22891
  const nodeError = error48;
22629
22892
  if (nodeError.code === "ENOENT") {
22630
- throw createCliError6(
22893
+ throw createCliError7(
22631
22894
  "CLI_PAIR_AGENT_NOT_FOUND",
22632
22895
  `Agent "${normalizedAgentName}" is missing ${SECRET_KEY_FILE_NAME3}. Run agent create first.`
22633
22896
  );
@@ -22635,7 +22898,7 @@ async function readAgentProofMaterial(agentName, dependencies) {
22635
22898
  throw error48;
22636
22899
  }
22637
22900
  if (encodedSecretKey.length === 0) {
22638
- throw createCliError6(
22901
+ throw createCliError7(
22639
22902
  "CLI_PAIR_AGENT_NOT_FOUND",
22640
22903
  `Agent "${normalizedAgentName}" has an empty ${SECRET_KEY_FILE_NAME3}`
22641
22904
  );
@@ -22644,7 +22907,7 @@ async function readAgentProofMaterial(agentName, dependencies) {
22644
22907
  try {
22645
22908
  secretKey = decodeBase64url(encodedSecretKey);
22646
22909
  } catch {
22647
- throw createCliError6(
22910
+ throw createCliError7(
22648
22911
  "CLI_PAIR_AGENT_NOT_FOUND",
22649
22912
  `Agent "${normalizedAgentName}" has invalid ${SECRET_KEY_FILE_NAME3}`
22650
22913
  );
@@ -22655,11 +22918,11 @@ async function readAgentProofMaterial(agentName, dependencies) {
22655
22918
  };
22656
22919
  }
22657
22920
  function resolveOwnerPat(options) {
22658
- const ownerPat = parseNonEmptyString8(options.explicitOwnerPat) || parseNonEmptyString8(options.config.apiKey);
22921
+ const ownerPat = parseNonEmptyString9(options.explicitOwnerPat) || parseNonEmptyString9(options.config.apiKey);
22659
22922
  if (ownerPat.length > 0) {
22660
22923
  return ownerPat;
22661
22924
  }
22662
- throw createCliError6(
22925
+ throw createCliError7(
22663
22926
  "CLI_PAIR_START_OWNER_PAT_REQUIRED",
22664
22927
  "Owner PAT is required. Pass --owner-pat <token> or configure API key with `clawdentity invite redeem` / `clawdentity config set apiKey <token>`."
22665
22928
  );
@@ -22689,7 +22952,7 @@ function decodeTicketFromPng(imageBytes) {
22689
22952
  try {
22690
22953
  decodedPng = PNG.sync.read(Buffer.from(imageBytes));
22691
22954
  } catch {
22692
- throw createCliError6(
22955
+ throw createCliError7(
22693
22956
  "CLI_PAIR_CONFIRM_QR_FILE_INVALID",
22694
22957
  "QR image file is invalid or unsupported"
22695
22958
  );
@@ -22700,8 +22963,8 @@ function decodeTicketFromPng(imageBytes) {
22700
22963
  decodedPng.data.byteLength
22701
22964
  );
22702
22965
  const decoded = jsQR(imageData, decodedPng.width, decodedPng.height);
22703
- if (!decoded || parseNonEmptyString8(decoded.data).length === 0) {
22704
- throw createCliError6(
22966
+ if (!decoded || parseNonEmptyString9(decoded.data).length === 0) {
22967
+ throw createCliError7(
22705
22968
  "CLI_PAIR_CONFIRM_QR_NOT_FOUND",
22706
22969
  "No pairing QR code was found in the image"
22707
22970
  );
@@ -22716,7 +22979,7 @@ async function persistPairingQr(input) {
22716
22979
  const getConfigDirImpl = input.dependencies.getConfigDirImpl ?? getConfigDir;
22717
22980
  const qrEncodeImpl = input.dependencies.qrEncodeImpl ?? encodeTicketQrPng;
22718
22981
  const baseDir = join7(getConfigDirImpl(), PAIRING_QR_DIR_NAME);
22719
- const outputPath = parseNonEmptyString8(input.qrOutput) ? resolve(input.qrOutput ?? "") : join7(
22982
+ const outputPath = parseNonEmptyString9(input.qrOutput) ? resolve(input.qrOutput ?? "") : join7(
22720
22983
  baseDir,
22721
22984
  `${assertValidAgentName(input.agentName)}-pair-${input.nowSeconds}.png`
22722
22985
  );
@@ -22757,10 +23020,10 @@ async function persistPairingQr(input) {
22757
23020
  return outputPath;
22758
23021
  }
22759
23022
  function resolveConfirmTicketSource(options) {
22760
- const inlineTicket = parseNonEmptyString8(options.ticket);
22761
- const qrFile = parseNonEmptyString8(options.qrFile);
23023
+ const inlineTicket = parseNonEmptyString9(options.ticket);
23024
+ const qrFile = parseNonEmptyString9(options.qrFile);
22762
23025
  if (inlineTicket.length > 0 && qrFile.length > 0) {
22763
- throw createCliError6(
23026
+ throw createCliError7(
22764
23027
  "CLI_PAIR_CONFIRM_INPUT_CONFLICT",
22765
23028
  "Provide either --ticket or --qr-file, not both"
22766
23029
  );
@@ -22778,7 +23041,7 @@ function resolveConfirmTicketSource(options) {
22778
23041
  qrFilePath: resolve(qrFile)
22779
23042
  };
22780
23043
  }
22781
- throw createCliError6(
23044
+ throw createCliError7(
22782
23045
  "CLI_PAIR_CONFIRM_TICKET_REQUIRED",
22783
23046
  "Pairing ticket is required. Pass --ticket <clwpair1_...> or --qr-file <path>."
22784
23047
  );
@@ -22819,14 +23082,14 @@ async function startPairing(agentName, options, dependencies = {}) {
22819
23082
  const nonceFactoryImpl = dependencies.nonceFactoryImpl ?? (() => randomBytes4(NONCE_SIZE2).toString("base64url"));
22820
23083
  const ttlSeconds = parseTtlSeconds(options.ttlSeconds);
22821
23084
  const config2 = await resolveConfigImpl();
22822
- const proxyUrl = resolveProxyUrl({
22823
- overrideProxyUrl: options.proxyUrl,
22824
- config: config2
22825
- });
22826
23085
  const ownerPat = resolveOwnerPat({
22827
23086
  explicitOwnerPat: options.ownerPat,
22828
23087
  config: config2
22829
23088
  });
23089
+ const proxyUrl = await resolveProxyUrl({
23090
+ config: config2,
23091
+ fetchImpl
23092
+ });
22830
23093
  const { ait, secretKey } = await readAgentProofMaterial(
22831
23094
  agentName,
22832
23095
  dependencies
@@ -22860,9 +23123,9 @@ async function startPairing(agentName, options, dependencies = {}) {
22860
23123
  body: requestBody
22861
23124
  }
22862
23125
  });
22863
- const responseBody = await parseJsonResponse5(response);
23126
+ const responseBody = await parseJsonResponse6(response);
22864
23127
  if (!response.ok) {
22865
- throw createCliError6(
23128
+ throw createCliError7(
22866
23129
  "CLI_PAIR_START_FAILED",
22867
23130
  mapStartPairError(response.status, responseBody)
22868
23131
  );
@@ -22892,14 +23155,14 @@ async function confirmPairing(agentName, options, dependencies = {}) {
22892
23155
  const qrDecodeImpl = dependencies.qrDecodeImpl ?? decodeTicketFromPng;
22893
23156
  const config2 = await resolveConfigImpl();
22894
23157
  const ticketSource = resolveConfirmTicketSource(options);
22895
- const proxyUrl = resolveProxyUrl({
22896
- overrideProxyUrl: options.proxyUrl,
22897
- config: config2
23158
+ const proxyUrl = await resolveProxyUrl({
23159
+ config: config2,
23160
+ fetchImpl
22898
23161
  });
22899
23162
  let ticket = ticketSource.ticket;
22900
23163
  if (ticketSource.source === "qr-file") {
22901
23164
  if (!ticketSource.qrFilePath) {
22902
- throw createCliError6(
23165
+ throw createCliError7(
22903
23166
  "CLI_PAIR_CONFIRM_QR_FILE_REQUIRED",
22904
23167
  "QR file path is required"
22905
23168
  );
@@ -22910,7 +23173,7 @@ async function confirmPairing(agentName, options, dependencies = {}) {
22910
23173
  } catch (error48) {
22911
23174
  const nodeError = error48;
22912
23175
  if (nodeError.code === "ENOENT") {
22913
- throw createCliError6(
23176
+ throw createCliError7(
22914
23177
  "CLI_PAIR_CONFIRM_QR_FILE_NOT_FOUND",
22915
23178
  `QR file not found: ${ticketSource.qrFilePath}`
22916
23179
  );
@@ -22949,9 +23212,9 @@ async function confirmPairing(agentName, options, dependencies = {}) {
22949
23212
  body: requestBody
22950
23213
  }
22951
23214
  });
22952
- const responseBody = await parseJsonResponse5(response);
23215
+ const responseBody = await parseJsonResponse6(response);
22953
23216
  if (!response.ok) {
22954
- throw createCliError6(
23217
+ throw createCliError7(
22955
23218
  "CLI_PAIR_CONFIRM_FAILED",
22956
23219
  mapConfirmPairError(response.status, responseBody)
22957
23220
  );
@@ -22985,7 +23248,7 @@ var createPairCommand = (dependencies = {}) => {
22985
23248
  const pairCommand = new Command8("pair").description(
22986
23249
  "Manage proxy trust pairing between agents"
22987
23250
  );
22988
- pairCommand.command("start <agentName>").description("Start pairing and issue one-time pairing ticket").option("--proxy-url <url>", "Optional initiator proxy base URL override").option(
23251
+ pairCommand.command("start <agentName>").description("Start pairing and issue one-time pairing ticket").option(
22989
23252
  "--owner-pat <token>",
22990
23253
  "Owner PAT override (defaults to configured API key)"
22991
23254
  ).option("--ttl-seconds <seconds>", "Pairing ticket expiry in seconds").option("--qr", "Generate a local QR file for sharing").option("--qr-output <path>", "Write QR PNG to a specific file path").action(
@@ -23009,7 +23272,7 @@ var createPairCommand = (dependencies = {}) => {
23009
23272
  }
23010
23273
  )
23011
23274
  );
23012
- pairCommand.command("confirm <agentName>").description("Confirm pairing using one-time pairing ticket").option("--ticket <ticket>", "One-time pairing ticket (clwpair1_...)").option("--qr-file <path>", "Path to pairing QR PNG file").option("--proxy-url <url>", "Optional responder proxy base URL override").action(
23275
+ pairCommand.command("confirm <agentName>").description("Confirm pairing using one-time pairing ticket").option("--ticket <ticket>", "One-time pairing ticket (clwpair1_...)").option("--qr-file <path>", "Path to pairing QR PNG file").action(
23013
23276
  withErrorHandling(
23014
23277
  "pair confirm",
23015
23278
  async (agentName, options) => {
@@ -23047,10 +23310,10 @@ var VerifyCommandError = class extends Error {
23047
23310
  this.name = "VerifyCommandError";
23048
23311
  }
23049
23312
  };
23050
- var isRecord9 = (value) => {
23313
+ var isRecord10 = (value) => {
23051
23314
  return typeof value === "object" && value !== null;
23052
23315
  };
23053
- var normalizeRegistryUrl = (registryUrl) => {
23316
+ var normalizeRegistryUrl2 = (registryUrl) => {
23054
23317
  try {
23055
23318
  return new URL(registryUrl).toString();
23056
23319
  } catch {
@@ -23115,7 +23378,7 @@ var parseResponseJson = async (response) => {
23115
23378
  }
23116
23379
  };
23117
23380
  var parseSigningKeys = (payload) => {
23118
- if (!isRecord9(payload) || !Array.isArray(payload.keys)) {
23381
+ if (!isRecord10(payload) || !Array.isArray(payload.keys)) {
23119
23382
  throw new VerifyCommandError(
23120
23383
  "verification keys unavailable (response payload is invalid)"
23121
23384
  );
@@ -23134,7 +23397,7 @@ var parseSigningKeys = (payload) => {
23134
23397
  };
23135
23398
  var parseRegistryKeysCache = (rawCache) => {
23136
23399
  const parsed = parseJson(rawCache);
23137
- if (!isRecord9(parsed)) {
23400
+ if (!isRecord10(parsed)) {
23138
23401
  return void 0;
23139
23402
  }
23140
23403
  const { registryUrl, fetchedAtMs, keys } = parsed;
@@ -23160,7 +23423,7 @@ var parseRegistryKeysCache = (rawCache) => {
23160
23423
  };
23161
23424
  var parseCrlCache = (rawCache) => {
23162
23425
  const parsed = parseJson(rawCache);
23163
- if (!isRecord9(parsed)) {
23426
+ if (!isRecord10(parsed)) {
23164
23427
  return void 0;
23165
23428
  }
23166
23429
  const { registryUrl, fetchedAtMs, claims } = parsed;
@@ -23258,7 +23521,7 @@ var fetchCrlClaims = async (input) => {
23258
23521
  );
23259
23522
  }
23260
23523
  const payload = await parseResponseJson(response);
23261
- if (!isRecord9(payload) || typeof payload.crl !== "string") {
23524
+ if (!isRecord10(payload) || typeof payload.crl !== "string") {
23262
23525
  throw new VerifyCommandError(
23263
23526
  "revocation check unavailable (response payload is invalid)"
23264
23527
  );
@@ -23303,7 +23566,7 @@ var loadCrlClaims = async (input) => {
23303
23566
  return claims;
23304
23567
  };
23305
23568
  var toInvalidTokenReason = (error48) => {
23306
- if (isRecord9(error48) && typeof error48.message === "string") {
23569
+ if (isRecord10(error48) && typeof error48.message === "string") {
23307
23570
  return `invalid token (${error48.message})`;
23308
23571
  }
23309
23572
  if (error48 instanceof Error && error48.message.length > 0) {
@@ -23321,7 +23584,7 @@ var printResult = (passed, reason) => {
23321
23584
  };
23322
23585
  var runVerify = async (tokenOrFile) => {
23323
23586
  const config2 = await resolveConfig();
23324
- const registryUrl = normalizeRegistryUrl(config2.registryUrl);
23587
+ const registryUrl = normalizeRegistryUrl2(config2.registryUrl);
23325
23588
  const expectedIssuer = toExpectedIssuer(registryUrl);
23326
23589
  const token = await resolveToken(tokenOrFile);
23327
23590
  let keys;