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