clawdentity 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +1222 -121
- package/dist/index.js +1220 -119
- package/dist/postinstall.js +0 -0
- package/package.json +19 -14
- package/skill-bundle/AGENTS.md +2 -1
- package/skill-bundle/openclaw-skill/skill/SKILL.md +24 -13
- package/skill-bundle/openclaw-skill/skill/references/clawdentity-protocol.md +20 -9
package/dist/bin.js
CHANGED
|
@@ -17062,7 +17062,7 @@ var DEFAULT_NONCE_TTL_MS = 5 * 60 * 1e3;
|
|
|
17062
17062
|
|
|
17063
17063
|
// src/index.ts
|
|
17064
17064
|
import { createRequire } from "module";
|
|
17065
|
-
import { Command as
|
|
17065
|
+
import { Command as Command10 } from "commander";
|
|
17066
17066
|
|
|
17067
17067
|
// src/commands/admin.ts
|
|
17068
17068
|
import { Command } from "commander";
|
|
@@ -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 readFile4, 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 join6 } from "path";
|
|
18501
18501
|
import { fileURLToPath } from "url";
|
|
18502
18502
|
import { promisify } from "util";
|
|
18503
18503
|
|
|
@@ -19133,8 +19133,8 @@ var ConnectorClient = class {
|
|
|
19133
19133
|
}
|
|
19134
19134
|
}
|
|
19135
19135
|
async wait(delayMs) {
|
|
19136
|
-
await new Promise((
|
|
19137
|
-
setTimeout(
|
|
19136
|
+
await new Promise((resolve2) => {
|
|
19137
|
+
setTimeout(resolve2, delayMs);
|
|
19138
19138
|
});
|
|
19139
19139
|
}
|
|
19140
19140
|
makeFrameId() {
|
|
@@ -19151,15 +19151,271 @@ import { mkdir as mkdir3, rename as rename2, writeFile as writeFile3 } from "fs/
|
|
|
19151
19151
|
import {
|
|
19152
19152
|
createServer
|
|
19153
19153
|
} from "http";
|
|
19154
|
-
import { dirname as dirname2, join as
|
|
19154
|
+
import { dirname as dirname2, join as join5 } from "path";
|
|
19155
19155
|
import { WebSocket as NodeWebSocket } from "ws";
|
|
19156
|
+
|
|
19157
|
+
// ../../packages/connector/src/relay-echo.ts
|
|
19158
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
19159
|
+
import { join as join4 } from "path";
|
|
19160
|
+
var OPENCLAW_RELAY_RUNTIME_CONFIG_FILENAME = "openclaw-relay.json";
|
|
19161
|
+
var PEERS_CONFIG_FILENAME = "peers.json";
|
|
19162
|
+
var ECHO_EMPTY_MESSAGE_FALLBACK = "(no message content)";
|
|
19163
|
+
var MIN_ECHO_MAX_LENGTH = 50;
|
|
19164
|
+
var MAX_ECHO_MAX_LENGTH = 2e3;
|
|
19165
|
+
var DEFAULT_RELAY_ECHO_ENABLED = true;
|
|
19166
|
+
var DEFAULT_RELAY_ECHO_CHANNEL = "last";
|
|
19167
|
+
var DEFAULT_RELAY_ECHO_MAX_LENGTH = 500;
|
|
19168
|
+
function isRecord4(value) {
|
|
19169
|
+
return typeof value === "object" && value !== null;
|
|
19170
|
+
}
|
|
19171
|
+
function parseAbsoluteHttpUrl(value, field) {
|
|
19172
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
19173
|
+
throw new Error(`${field} must be a non-empty string`);
|
|
19174
|
+
}
|
|
19175
|
+
let parsed;
|
|
19176
|
+
try {
|
|
19177
|
+
parsed = new URL(value.trim());
|
|
19178
|
+
} catch {
|
|
19179
|
+
throw new Error(`${field} must be a valid absolute URL`);
|
|
19180
|
+
}
|
|
19181
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
19182
|
+
throw new Error(`${field} must use http or https`);
|
|
19183
|
+
}
|
|
19184
|
+
if (parsed.pathname === "/" && parsed.search.length === 0 && parsed.hash.length === 0) {
|
|
19185
|
+
return parsed.origin;
|
|
19186
|
+
}
|
|
19187
|
+
return parsed.toString();
|
|
19188
|
+
}
|
|
19189
|
+
function parseBoolean(value, field) {
|
|
19190
|
+
if (typeof value !== "boolean") {
|
|
19191
|
+
throw new Error(`${field} must be a boolean`);
|
|
19192
|
+
}
|
|
19193
|
+
return value;
|
|
19194
|
+
}
|
|
19195
|
+
function parsePositiveInt(value, field, min, max) {
|
|
19196
|
+
const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : Number.NaN;
|
|
19197
|
+
if (!Number.isInteger(parsed) || parsed < min || parsed > max) {
|
|
19198
|
+
throw new Error(`${field} must be an integer between ${min} and ${max}`);
|
|
19199
|
+
}
|
|
19200
|
+
return parsed;
|
|
19201
|
+
}
|
|
19202
|
+
function parseOptionalString(value, field) {
|
|
19203
|
+
if (value === void 0) {
|
|
19204
|
+
return void 0;
|
|
19205
|
+
}
|
|
19206
|
+
if (typeof value !== "string") {
|
|
19207
|
+
throw new Error(`${field} must be a string`);
|
|
19208
|
+
}
|
|
19209
|
+
const trimmed = value.trim();
|
|
19210
|
+
if (trimmed.length === 0) {
|
|
19211
|
+
return void 0;
|
|
19212
|
+
}
|
|
19213
|
+
return trimmed;
|
|
19214
|
+
}
|
|
19215
|
+
function parseEchoConfig(value) {
|
|
19216
|
+
if (value === void 0) {
|
|
19217
|
+
return {
|
|
19218
|
+
enabled: DEFAULT_RELAY_ECHO_ENABLED,
|
|
19219
|
+
channel: DEFAULT_RELAY_ECHO_CHANNEL,
|
|
19220
|
+
maxLength: DEFAULT_RELAY_ECHO_MAX_LENGTH
|
|
19221
|
+
};
|
|
19222
|
+
}
|
|
19223
|
+
if (!isRecord4(value)) {
|
|
19224
|
+
throw new Error("echo config must be an object");
|
|
19225
|
+
}
|
|
19226
|
+
return {
|
|
19227
|
+
enabled: value.enabled === void 0 ? DEFAULT_RELAY_ECHO_ENABLED : parseBoolean(value.enabled, "echo.enabled"),
|
|
19228
|
+
channel: parseOptionalString(value.channel, "echo.channel") ?? DEFAULT_RELAY_ECHO_CHANNEL,
|
|
19229
|
+
to: parseOptionalString(value.to, "echo.to"),
|
|
19230
|
+
maxLength: value.maxLength === void 0 ? DEFAULT_RELAY_ECHO_MAX_LENGTH : parsePositiveInt(
|
|
19231
|
+
value.maxLength,
|
|
19232
|
+
"echo.maxLength",
|
|
19233
|
+
MIN_ECHO_MAX_LENGTH,
|
|
19234
|
+
MAX_ECHO_MAX_LENGTH
|
|
19235
|
+
)
|
|
19236
|
+
};
|
|
19237
|
+
}
|
|
19238
|
+
async function loadRelayRuntimeConfigFile(input) {
|
|
19239
|
+
const relayRuntimeConfigPath = join4(
|
|
19240
|
+
input.configDir,
|
|
19241
|
+
OPENCLAW_RELAY_RUNTIME_CONFIG_FILENAME
|
|
19242
|
+
);
|
|
19243
|
+
let raw;
|
|
19244
|
+
try {
|
|
19245
|
+
raw = await readFile3(relayRuntimeConfigPath, "utf8");
|
|
19246
|
+
} catch (error48) {
|
|
19247
|
+
if (typeof error48 === "object" && error48 !== null && "code" in error48 && error48.code === "ENOENT") {
|
|
19248
|
+
return void 0;
|
|
19249
|
+
}
|
|
19250
|
+
throw new Error(
|
|
19251
|
+
`Unable to read relay runtime config at ${relayRuntimeConfigPath}`
|
|
19252
|
+
);
|
|
19253
|
+
}
|
|
19254
|
+
let parsed;
|
|
19255
|
+
try {
|
|
19256
|
+
parsed = JSON.parse(raw);
|
|
19257
|
+
} catch {
|
|
19258
|
+
throw new Error(
|
|
19259
|
+
`Unable to parse relay runtime config at ${relayRuntimeConfigPath}`
|
|
19260
|
+
);
|
|
19261
|
+
}
|
|
19262
|
+
if (!isRecord4(parsed)) {
|
|
19263
|
+
throw new Error("Relay runtime config root must be a JSON object");
|
|
19264
|
+
}
|
|
19265
|
+
const config2 = parsed;
|
|
19266
|
+
const openclawBaseUrl = config2.openclawBaseUrl === void 0 ? void 0 : parseAbsoluteHttpUrl(config2.openclawBaseUrl, "openclawBaseUrl");
|
|
19267
|
+
return {
|
|
19268
|
+
openclawBaseUrl,
|
|
19269
|
+
echo: parseEchoConfig(config2.echo)
|
|
19270
|
+
};
|
|
19271
|
+
}
|
|
19272
|
+
function compactString(value) {
|
|
19273
|
+
return value.replaceAll(/\s+/g, " ").trim();
|
|
19274
|
+
}
|
|
19275
|
+
function toCompactJson(value) {
|
|
19276
|
+
try {
|
|
19277
|
+
const raw = JSON.stringify(value);
|
|
19278
|
+
if (typeof raw !== "string" || raw.length === 0) {
|
|
19279
|
+
return ECHO_EMPTY_MESSAGE_FALLBACK;
|
|
19280
|
+
}
|
|
19281
|
+
return raw;
|
|
19282
|
+
} catch {
|
|
19283
|
+
return ECHO_EMPTY_MESSAGE_FALLBACK;
|
|
19284
|
+
}
|
|
19285
|
+
}
|
|
19286
|
+
function truncateMessage(value, maxLength) {
|
|
19287
|
+
if (value.length <= maxLength) {
|
|
19288
|
+
return value;
|
|
19289
|
+
}
|
|
19290
|
+
return `${value.slice(0, maxLength)}...`;
|
|
19291
|
+
}
|
|
19292
|
+
function extractRelayMessageContent(payload) {
|
|
19293
|
+
if (typeof payload === "string") {
|
|
19294
|
+
const compact = compactString(payload);
|
|
19295
|
+
return compact.length > 0 ? compact : ECHO_EMPTY_MESSAGE_FALLBACK;
|
|
19296
|
+
}
|
|
19297
|
+
if (isRecord4(payload)) {
|
|
19298
|
+
for (const candidateKey of ["message", "text", "content"]) {
|
|
19299
|
+
const candidateValue = payload[candidateKey];
|
|
19300
|
+
if (typeof candidateValue === "string") {
|
|
19301
|
+
const compact = compactString(candidateValue);
|
|
19302
|
+
if (compact.length > 0) {
|
|
19303
|
+
return compact;
|
|
19304
|
+
}
|
|
19305
|
+
}
|
|
19306
|
+
}
|
|
19307
|
+
}
|
|
19308
|
+
return toCompactJson(payload);
|
|
19309
|
+
}
|
|
19310
|
+
function formatInboundRelayEcho(input) {
|
|
19311
|
+
const content = truncateMessage(
|
|
19312
|
+
extractRelayMessageContent(input.payload),
|
|
19313
|
+
input.maxLength
|
|
19314
|
+
);
|
|
19315
|
+
return `\u{1F517} [${input.fromLabel}]: ${content}`;
|
|
19316
|
+
}
|
|
19317
|
+
function formatOutboundRelayEcho(input) {
|
|
19318
|
+
const content = truncateMessage(
|
|
19319
|
+
extractRelayMessageContent(input.payload),
|
|
19320
|
+
input.maxLength
|
|
19321
|
+
);
|
|
19322
|
+
return `\u21A9\uFE0F Reply to [${input.toLabel}]: ${content}`;
|
|
19323
|
+
}
|
|
19324
|
+
async function loadPeerLabelsByDid(input) {
|
|
19325
|
+
const peersPath = join4(input.configDir, PEERS_CONFIG_FILENAME);
|
|
19326
|
+
let raw;
|
|
19327
|
+
try {
|
|
19328
|
+
raw = await readFile3(peersPath, "utf8");
|
|
19329
|
+
} catch (error48) {
|
|
19330
|
+
if (typeof error48 === "object" && error48 !== null && "code" in error48 && error48.code === "ENOENT") {
|
|
19331
|
+
return /* @__PURE__ */ new Map();
|
|
19332
|
+
}
|
|
19333
|
+
throw new Error(`Unable to read peers config at ${peersPath}`);
|
|
19334
|
+
}
|
|
19335
|
+
let parsed;
|
|
19336
|
+
try {
|
|
19337
|
+
parsed = JSON.parse(raw);
|
|
19338
|
+
} catch {
|
|
19339
|
+
throw new Error(`Unable to parse peers config at ${peersPath}`);
|
|
19340
|
+
}
|
|
19341
|
+
if (!isRecord4(parsed) || !isRecord4(parsed.peers)) {
|
|
19342
|
+
return /* @__PURE__ */ new Map();
|
|
19343
|
+
}
|
|
19344
|
+
const labels = /* @__PURE__ */ new Map();
|
|
19345
|
+
for (const [alias, entry] of Object.entries(parsed.peers)) {
|
|
19346
|
+
if (!isRecord4(entry) || typeof entry.did !== "string") {
|
|
19347
|
+
continue;
|
|
19348
|
+
}
|
|
19349
|
+
const did = entry.did.trim();
|
|
19350
|
+
if (did.length === 0) {
|
|
19351
|
+
continue;
|
|
19352
|
+
}
|
|
19353
|
+
const name = typeof entry.name === "string" && entry.name.trim().length > 0 ? entry.name.trim() : alias.trim();
|
|
19354
|
+
if (name.length === 0) {
|
|
19355
|
+
continue;
|
|
19356
|
+
}
|
|
19357
|
+
labels.set(did, name);
|
|
19358
|
+
}
|
|
19359
|
+
return labels;
|
|
19360
|
+
}
|
|
19361
|
+
function resolvePeerLabel(input) {
|
|
19362
|
+
if (input.peerDid !== void 0) {
|
|
19363
|
+
const byDid = input.peerLabelsByDid.get(input.peerDid);
|
|
19364
|
+
if (typeof byDid === "string" && byDid.length > 0) {
|
|
19365
|
+
return byDid;
|
|
19366
|
+
}
|
|
19367
|
+
}
|
|
19368
|
+
const fallback = input.fallbackLabel.trim();
|
|
19369
|
+
if (fallback.length > 0) {
|
|
19370
|
+
return fallback;
|
|
19371
|
+
}
|
|
19372
|
+
return input.peerDid?.trim() || "Unknown peer";
|
|
19373
|
+
}
|
|
19374
|
+
async function sendRelayEchoToOpenclaw(input) {
|
|
19375
|
+
if (!input.echoConfig.enabled) {
|
|
19376
|
+
return;
|
|
19377
|
+
}
|
|
19378
|
+
const payload = {
|
|
19379
|
+
message: input.message,
|
|
19380
|
+
name: "Clawdentity Relay",
|
|
19381
|
+
wakeMode: "now",
|
|
19382
|
+
deliver: true,
|
|
19383
|
+
channel: input.echoConfig.channel
|
|
19384
|
+
};
|
|
19385
|
+
if (input.echoConfig.to !== void 0) {
|
|
19386
|
+
payload.to = input.echoConfig.to;
|
|
19387
|
+
}
|
|
19388
|
+
const headers = {
|
|
19389
|
+
"content-type": "application/json"
|
|
19390
|
+
};
|
|
19391
|
+
if (input.requestId !== void 0 && input.requestId.trim().length > 0) {
|
|
19392
|
+
headers["x-request-id"] = input.requestId.trim();
|
|
19393
|
+
}
|
|
19394
|
+
if (input.hookToken !== void 0 && input.hookToken.trim().length > 0) {
|
|
19395
|
+
headers["x-openclaw-token"] = input.hookToken.trim();
|
|
19396
|
+
}
|
|
19397
|
+
const response = await input.fetchImpl(input.endpoint, {
|
|
19398
|
+
method: "POST",
|
|
19399
|
+
headers,
|
|
19400
|
+
body: JSON.stringify(payload)
|
|
19401
|
+
});
|
|
19402
|
+
if (!response.ok) {
|
|
19403
|
+
input.logger.warn("connector.relay_echo.rejected", {
|
|
19404
|
+
status: response.status,
|
|
19405
|
+
requestId: input.requestId
|
|
19406
|
+
});
|
|
19407
|
+
throw new Error(`Relay echo rejected with status ${response.status}`);
|
|
19408
|
+
}
|
|
19409
|
+
}
|
|
19410
|
+
|
|
19411
|
+
// ../../packages/connector/src/runtime.ts
|
|
19156
19412
|
var REGISTRY_AUTH_FILENAME = "registry-auth.json";
|
|
19157
19413
|
var AGENTS_DIR_NAME2 = "agents";
|
|
19158
19414
|
var REFRESH_SINGLE_FLIGHT_PREFIX = "connector-runtime";
|
|
19159
19415
|
var NONCE_SIZE = 16;
|
|
19160
19416
|
var MAX_OUTBOUND_BODY_BYTES = 1024 * 1024;
|
|
19161
19417
|
var ACCESS_TOKEN_REFRESH_SKEW_MS = 3e4;
|
|
19162
|
-
function
|
|
19418
|
+
function isRecord5(value) {
|
|
19163
19419
|
return typeof value === "object" && value !== null;
|
|
19164
19420
|
}
|
|
19165
19421
|
function toPathWithQuery2(url2) {
|
|
@@ -19209,9 +19465,38 @@ function normalizeWebSocketUrl(urlInput) {
|
|
|
19209
19465
|
}
|
|
19210
19466
|
return parsed.toString();
|
|
19211
19467
|
}
|
|
19212
|
-
function
|
|
19213
|
-
|
|
19214
|
-
|
|
19468
|
+
function parseOpenclawBaseUrl(value) {
|
|
19469
|
+
let parsed;
|
|
19470
|
+
try {
|
|
19471
|
+
parsed = new URL(value);
|
|
19472
|
+
} catch {
|
|
19473
|
+
throw new Error("OpenClaw base URL is invalid");
|
|
19474
|
+
}
|
|
19475
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
19476
|
+
throw new Error("OpenClaw base URL is invalid");
|
|
19477
|
+
}
|
|
19478
|
+
if (parsed.pathname === "/" && parsed.search.length === 0 && parsed.hash.length === 0) {
|
|
19479
|
+
return parsed.origin;
|
|
19480
|
+
}
|
|
19481
|
+
return parsed.toString();
|
|
19482
|
+
}
|
|
19483
|
+
async function resolveOpenclawRuntimeSettings(input) {
|
|
19484
|
+
const fileConfig = await loadRelayRuntimeConfigFile({
|
|
19485
|
+
configDir: input.configDir
|
|
19486
|
+
});
|
|
19487
|
+
const baseUrlFromOption = input.openclawBaseUrlOption?.trim();
|
|
19488
|
+
const baseUrlFromEnv = process.env.OPENCLAW_BASE_URL?.trim();
|
|
19489
|
+
const openclawBaseUrl = parseOpenclawBaseUrl(
|
|
19490
|
+
baseUrlFromOption || baseUrlFromEnv || fileConfig?.openclawBaseUrl || DEFAULT_OPENCLAW_BASE_URL
|
|
19491
|
+
);
|
|
19492
|
+
return {
|
|
19493
|
+
openclawBaseUrl,
|
|
19494
|
+
echoConfig: fileConfig?.echo ?? {
|
|
19495
|
+
enabled: DEFAULT_RELAY_ECHO_ENABLED,
|
|
19496
|
+
channel: DEFAULT_RELAY_ECHO_CHANNEL,
|
|
19497
|
+
maxLength: DEFAULT_RELAY_ECHO_MAX_LENGTH
|
|
19498
|
+
}
|
|
19499
|
+
};
|
|
19215
19500
|
}
|
|
19216
19501
|
function resolveOpenclawHookPath(input) {
|
|
19217
19502
|
const value = input?.trim() || process.env.OPENCLAW_HOOK_PATH?.trim() || DEFAULT_OPENCLAW_HOOK_PATH;
|
|
@@ -19251,7 +19536,7 @@ function shouldRefreshAccessToken(auth, nowMs) {
|
|
|
19251
19536
|
return expiresAtMs <= nowMs + ACCESS_TOKEN_REFRESH_SKEW_MS;
|
|
19252
19537
|
}
|
|
19253
19538
|
function parseOutboundRelayRequest(payload) {
|
|
19254
|
-
if (!
|
|
19539
|
+
if (!isRecord5(payload)) {
|
|
19255
19540
|
throw new AppError({
|
|
19256
19541
|
code: "CONNECTOR_OUTBOUND_INVALID_REQUEST",
|
|
19257
19542
|
message: "Outbound relay request must be an object",
|
|
@@ -19309,7 +19594,7 @@ function createWebSocketFactory() {
|
|
|
19309
19594
|
};
|
|
19310
19595
|
}
|
|
19311
19596
|
async function writeRegistryAuthAtomic(input) {
|
|
19312
|
-
const targetPath =
|
|
19597
|
+
const targetPath = join5(
|
|
19313
19598
|
input.configDir,
|
|
19314
19599
|
AGENTS_DIR_NAME2,
|
|
19315
19600
|
input.agentName,
|
|
@@ -19378,7 +19663,7 @@ async function buildUpgradeHeaders(input) {
|
|
|
19378
19663
|
};
|
|
19379
19664
|
}
|
|
19380
19665
|
async function startConnectorRuntime(input) {
|
|
19381
|
-
const
|
|
19666
|
+
const logger12 = input.logger ?? createLogger({ service: "connector", module: "runtime" });
|
|
19382
19667
|
const fetchImpl = input.fetchImpl ?? fetch;
|
|
19383
19668
|
const secretKey = decodeBase64url(
|
|
19384
19669
|
parseRequiredString(input.credentials.secretKey, "secretKey")
|
|
@@ -19406,14 +19691,63 @@ async function startConnectorRuntime(input) {
|
|
|
19406
19691
|
accessToken: currentAuth.accessToken,
|
|
19407
19692
|
secretKey
|
|
19408
19693
|
});
|
|
19694
|
+
const openclawRuntimeSettings = await resolveOpenclawRuntimeSettings({
|
|
19695
|
+
configDir: input.configDir,
|
|
19696
|
+
openclawBaseUrlOption: input.openclawBaseUrl
|
|
19697
|
+
});
|
|
19698
|
+
const peerLabelsByDid = await loadPeerLabelsByDid({
|
|
19699
|
+
configDir: input.configDir
|
|
19700
|
+
});
|
|
19701
|
+
const openclawHookPath = resolveOpenclawHookPath(input.openclawHookPath);
|
|
19702
|
+
const openclawHookToken = resolveOpenclawHookToken(input.openclawHookToken);
|
|
19703
|
+
const openclawHookUrl = new URL(
|
|
19704
|
+
openclawHookPath,
|
|
19705
|
+
openclawRuntimeSettings.openclawBaseUrl
|
|
19706
|
+
).toString();
|
|
19707
|
+
const queueRelayEcho = (echoInput) => {
|
|
19708
|
+
void sendRelayEchoToOpenclaw({
|
|
19709
|
+
endpoint: openclawHookUrl,
|
|
19710
|
+
echoConfig: openclawRuntimeSettings.echoConfig,
|
|
19711
|
+
fetchImpl,
|
|
19712
|
+
hookToken: openclawHookToken,
|
|
19713
|
+
logger: logger12,
|
|
19714
|
+
message: echoInput.message,
|
|
19715
|
+
requestId: echoInput.requestId
|
|
19716
|
+
}).catch((error48) => {
|
|
19717
|
+
logger12.warn("connector.relay_echo.failed", {
|
|
19718
|
+
direction: echoInput.direction,
|
|
19719
|
+
errorName: error48 instanceof Error ? error48.name : "unknown",
|
|
19720
|
+
requestId: echoInput.requestId
|
|
19721
|
+
});
|
|
19722
|
+
});
|
|
19723
|
+
};
|
|
19409
19724
|
const connectorClient = new ConnectorClient({
|
|
19410
19725
|
connectorUrl: wsParsed.toString(),
|
|
19411
19726
|
connectionHeaders: upgradeHeaders,
|
|
19412
|
-
openclawBaseUrl:
|
|
19413
|
-
openclawHookPath
|
|
19414
|
-
openclawHookToken
|
|
19727
|
+
openclawBaseUrl: openclawRuntimeSettings.openclawBaseUrl,
|
|
19728
|
+
openclawHookPath,
|
|
19729
|
+
openclawHookToken,
|
|
19415
19730
|
fetchImpl,
|
|
19416
|
-
logger:
|
|
19731
|
+
logger: logger12,
|
|
19732
|
+
hooks: {
|
|
19733
|
+
onDeliverSucceeded: (frame) => {
|
|
19734
|
+
const fromLabel = resolvePeerLabel({
|
|
19735
|
+
peerLabelsByDid,
|
|
19736
|
+
peerDid: frame.fromAgentDid,
|
|
19737
|
+
fallbackLabel: frame.fromAgentDid
|
|
19738
|
+
});
|
|
19739
|
+
const echoMessage = formatInboundRelayEcho({
|
|
19740
|
+
fromLabel,
|
|
19741
|
+
payload: frame.payload,
|
|
19742
|
+
maxLength: openclawRuntimeSettings.echoConfig.maxLength
|
|
19743
|
+
});
|
|
19744
|
+
queueRelayEcho({
|
|
19745
|
+
direction: "inbound",
|
|
19746
|
+
message: echoMessage,
|
|
19747
|
+
requestId: frame.id
|
|
19748
|
+
});
|
|
19749
|
+
}
|
|
19750
|
+
},
|
|
19417
19751
|
webSocketFactory: createWebSocketFactory()
|
|
19418
19752
|
});
|
|
19419
19753
|
const outboundBaseUrl = normalizeOutboundBaseUrl(input.outboundBaseUrl);
|
|
@@ -19499,10 +19833,26 @@ async function startConnectorRuntime(input) {
|
|
|
19499
19833
|
const requestBody = await readRequestJson(req);
|
|
19500
19834
|
const relayRequest = parseOutboundRelayRequest(requestBody);
|
|
19501
19835
|
await relayToPeer(relayRequest);
|
|
19836
|
+
const toLabel = resolvePeerLabel({
|
|
19837
|
+
peerLabelsByDid,
|
|
19838
|
+
peerDid: relayRequest.peerDid,
|
|
19839
|
+
fallbackLabel: relayRequest.peer
|
|
19840
|
+
});
|
|
19841
|
+
const requestId = typeof req.headers["x-request-id"] === "string" ? req.headers["x-request-id"] : void 0;
|
|
19842
|
+
const echoMessage = formatOutboundRelayEcho({
|
|
19843
|
+
toLabel,
|
|
19844
|
+
payload: relayRequest.payload,
|
|
19845
|
+
maxLength: openclawRuntimeSettings.echoConfig.maxLength
|
|
19846
|
+
});
|
|
19847
|
+
queueRelayEcho({
|
|
19848
|
+
direction: "outbound",
|
|
19849
|
+
message: echoMessage,
|
|
19850
|
+
requestId
|
|
19851
|
+
});
|
|
19502
19852
|
writeJson(res, 202, { accepted: true, peer: relayRequest.peer });
|
|
19503
19853
|
} catch (error48) {
|
|
19504
19854
|
if (error48 instanceof AppError) {
|
|
19505
|
-
|
|
19855
|
+
logger12.warn("connector.outbound.rejected", {
|
|
19506
19856
|
code: error48.code,
|
|
19507
19857
|
status: error48.status,
|
|
19508
19858
|
message: error48.message
|
|
@@ -19515,7 +19865,7 @@ async function startConnectorRuntime(input) {
|
|
|
19515
19865
|
});
|
|
19516
19866
|
return;
|
|
19517
19867
|
}
|
|
19518
|
-
|
|
19868
|
+
logger12.error("connector.outbound.failed", {
|
|
19519
19869
|
errorName: error48 instanceof Error ? error48.name : "unknown"
|
|
19520
19870
|
});
|
|
19521
19871
|
writeJson(res, 500, {
|
|
@@ -19527,35 +19877,35 @@ async function startConnectorRuntime(input) {
|
|
|
19527
19877
|
}
|
|
19528
19878
|
});
|
|
19529
19879
|
let stoppedResolve;
|
|
19530
|
-
const stoppedPromise = new Promise((
|
|
19531
|
-
stoppedResolve =
|
|
19880
|
+
const stoppedPromise = new Promise((resolve2) => {
|
|
19881
|
+
stoppedResolve = resolve2;
|
|
19532
19882
|
});
|
|
19533
19883
|
const stop = async () => {
|
|
19534
19884
|
connectorClient.disconnect();
|
|
19535
|
-
await new Promise((
|
|
19885
|
+
await new Promise((resolve2, reject) => {
|
|
19536
19886
|
server.close((error48) => {
|
|
19537
19887
|
if (error48) {
|
|
19538
19888
|
reject(error48);
|
|
19539
19889
|
return;
|
|
19540
19890
|
}
|
|
19541
|
-
|
|
19891
|
+
resolve2();
|
|
19542
19892
|
});
|
|
19543
19893
|
});
|
|
19544
19894
|
stoppedResolve?.();
|
|
19545
19895
|
};
|
|
19546
|
-
await new Promise((
|
|
19896
|
+
await new Promise((resolve2, reject) => {
|
|
19547
19897
|
server.once("error", reject);
|
|
19548
19898
|
server.listen(
|
|
19549
19899
|
Number(outboundBaseUrl.port || "80"),
|
|
19550
19900
|
outboundBaseUrl.hostname,
|
|
19551
19901
|
() => {
|
|
19552
19902
|
server.off("error", reject);
|
|
19553
|
-
|
|
19903
|
+
resolve2();
|
|
19554
19904
|
}
|
|
19555
19905
|
);
|
|
19556
19906
|
});
|
|
19557
19907
|
connectorClient.connect();
|
|
19558
|
-
|
|
19908
|
+
logger12.info("connector.runtime.started", {
|
|
19559
19909
|
outboundUrl,
|
|
19560
19910
|
websocketUrl: wsUrl,
|
|
19561
19911
|
agentDid: input.credentials.agentDid
|
|
@@ -19580,11 +19930,11 @@ var REGISTRY_AUTH_FILE_NAME2 = "registry-auth.json";
|
|
|
19580
19930
|
var SERVICE_LOG_DIR_NAME = "logs";
|
|
19581
19931
|
var DEFAULT_CONNECTOR_BASE_URL2 = "http://127.0.0.1:19400";
|
|
19582
19932
|
var DEFAULT_CONNECTOR_OUTBOUND_PATH2 = "/v1/outbound";
|
|
19583
|
-
function
|
|
19933
|
+
function isRecord6(value) {
|
|
19584
19934
|
return typeof value === "object" && value !== null;
|
|
19585
19935
|
}
|
|
19586
19936
|
function getErrorCode(error48) {
|
|
19587
|
-
if (!
|
|
19937
|
+
if (!isRecord6(error48)) {
|
|
19588
19938
|
return void 0;
|
|
19589
19939
|
}
|
|
19590
19940
|
return typeof error48.code === "string" ? error48.code : void 0;
|
|
@@ -19708,7 +20058,7 @@ function parseJsonRecord(value, code, message2) {
|
|
|
19708
20058
|
} catch {
|
|
19709
20059
|
throw createCliError3(code, message2);
|
|
19710
20060
|
}
|
|
19711
|
-
if (!
|
|
20061
|
+
if (!isRecord6(parsed)) {
|
|
19712
20062
|
throw createCliError3(code, message2);
|
|
19713
20063
|
}
|
|
19714
20064
|
return parsed;
|
|
@@ -19748,7 +20098,7 @@ async function loadDefaultConnectorModule() {
|
|
|
19748
20098
|
};
|
|
19749
20099
|
}
|
|
19750
20100
|
function resolveWaitPromise(runtime) {
|
|
19751
|
-
if (!runtime || !
|
|
20101
|
+
if (!runtime || !isRecord6(runtime)) {
|
|
19752
20102
|
return void 0;
|
|
19753
20103
|
}
|
|
19754
20104
|
if (typeof runtime.waitUntilStopped === "function") {
|
|
@@ -19813,7 +20163,7 @@ function buildConnectorStartArgs(agentName, commandOptions) {
|
|
|
19813
20163
|
}
|
|
19814
20164
|
function resolveCliEntryPath(resolveCurrentModulePathImpl) {
|
|
19815
20165
|
const modulePath = resolveCurrentModulePathImpl?.() ?? fileURLToPath(import.meta.url);
|
|
19816
|
-
return
|
|
20166
|
+
return join6(dirname3(modulePath), "..", "bin.js");
|
|
19817
20167
|
}
|
|
19818
20168
|
function escapeXml(value) {
|
|
19819
20169
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
@@ -19911,7 +20261,7 @@ async function installConnectorServiceForAgent(agentName, commandOptions = {}, d
|
|
|
19911
20261
|
);
|
|
19912
20262
|
const configDir = serviceDependencies.getConfigDirImpl();
|
|
19913
20263
|
const homeDir = serviceDependencies.getHomeDirImpl();
|
|
19914
|
-
const logsDir =
|
|
20264
|
+
const logsDir = join6(configDir, SERVICE_LOG_DIR_NAME);
|
|
19915
20265
|
const serviceName = sanitizeServiceSegment(
|
|
19916
20266
|
`clawdentity-connector-${agentName}`
|
|
19917
20267
|
);
|
|
@@ -19921,12 +20271,12 @@ async function installConnectorServiceForAgent(agentName, commandOptions = {}, d
|
|
|
19921
20271
|
resolveCliEntryPath(serviceDependencies.resolveCurrentModulePathImpl),
|
|
19922
20272
|
...startArgs
|
|
19923
20273
|
];
|
|
19924
|
-
const outputLogPath =
|
|
19925
|
-
const errorLogPath =
|
|
20274
|
+
const outputLogPath = join6(logsDir, `${serviceName}.out.log`);
|
|
20275
|
+
const errorLogPath = join6(logsDir, `${serviceName}.err.log`);
|
|
19926
20276
|
await serviceDependencies.mkdirImpl(logsDir, { recursive: true });
|
|
19927
20277
|
if (platform === "systemd") {
|
|
19928
|
-
const serviceDir =
|
|
19929
|
-
const serviceFilePath2 =
|
|
20278
|
+
const serviceDir = join6(homeDir, ".config", "systemd", "user");
|
|
20279
|
+
const serviceFilePath2 = join6(serviceDir, `${serviceName}.service`);
|
|
19930
20280
|
await serviceDependencies.mkdirImpl(serviceDir, { recursive: true });
|
|
19931
20281
|
await serviceDependencies.writeFileImpl(
|
|
19932
20282
|
serviceFilePath2,
|
|
@@ -19965,9 +20315,9 @@ async function installConnectorServiceForAgent(agentName, commandOptions = {}, d
|
|
|
19965
20315
|
serviceFilePath: serviceFilePath2
|
|
19966
20316
|
};
|
|
19967
20317
|
}
|
|
19968
|
-
const launchAgentsDir =
|
|
20318
|
+
const launchAgentsDir = join6(homeDir, "Library", "LaunchAgents");
|
|
19969
20319
|
const serviceNameWithDomain = `com.clawdentity.${serviceName}`;
|
|
19970
|
-
const serviceFilePath =
|
|
20320
|
+
const serviceFilePath = join6(
|
|
19971
20321
|
launchAgentsDir,
|
|
19972
20322
|
`${serviceNameWithDomain}.plist`
|
|
19973
20323
|
);
|
|
@@ -20026,7 +20376,7 @@ async function uninstallConnectorServiceForAgent(agentName, commandOptions = {},
|
|
|
20026
20376
|
`clawdentity-connector-${agentName}`
|
|
20027
20377
|
);
|
|
20028
20378
|
if (platform === "systemd") {
|
|
20029
|
-
const serviceFilePath2 =
|
|
20379
|
+
const serviceFilePath2 = join6(
|
|
20030
20380
|
homeDir,
|
|
20031
20381
|
".config",
|
|
20032
20382
|
"systemd",
|
|
@@ -20057,7 +20407,7 @@ async function uninstallConnectorServiceForAgent(agentName, commandOptions = {},
|
|
|
20057
20407
|
};
|
|
20058
20408
|
}
|
|
20059
20409
|
const serviceNameWithDomain = `com.clawdentity.${serviceName}`;
|
|
20060
|
-
const serviceFilePath =
|
|
20410
|
+
const serviceFilePath = join6(
|
|
20061
20411
|
homeDir,
|
|
20062
20412
|
"Library",
|
|
20063
20413
|
"LaunchAgents",
|
|
@@ -20081,10 +20431,10 @@ async function uninstallConnectorServiceForAgent(agentName, commandOptions = {},
|
|
|
20081
20431
|
async function startConnectorForAgent(agentName, commandOptions = {}, dependencies = {}) {
|
|
20082
20432
|
const resolveConfigImpl = dependencies.resolveConfigImpl ?? resolveConfig;
|
|
20083
20433
|
const getConfigDirImpl = dependencies.getConfigDirImpl ?? getConfigDir;
|
|
20084
|
-
const readFileImpl = dependencies.readFileImpl ?? ((path, encoding) =>
|
|
20434
|
+
const readFileImpl = dependencies.readFileImpl ?? ((path, encoding) => readFile4(path, encoding));
|
|
20085
20435
|
const loadConnectorModule = dependencies.loadConnectorModule ?? loadDefaultConnectorModule;
|
|
20086
20436
|
const configDir = getConfigDirImpl();
|
|
20087
|
-
const agentDirectory =
|
|
20437
|
+
const agentDirectory = join6(configDir, AGENTS_DIR_NAME3, agentName);
|
|
20088
20438
|
const [
|
|
20089
20439
|
rawAit,
|
|
20090
20440
|
rawSecretKey,
|
|
@@ -20094,22 +20444,22 @@ async function startConnectorForAgent(agentName, commandOptions = {}, dependenci
|
|
|
20094
20444
|
connectorModule
|
|
20095
20445
|
] = await Promise.all([
|
|
20096
20446
|
readRequiredTrimmedFile(
|
|
20097
|
-
|
|
20447
|
+
join6(agentDirectory, AIT_FILE_NAME2),
|
|
20098
20448
|
AIT_FILE_NAME2,
|
|
20099
20449
|
readFileImpl
|
|
20100
20450
|
),
|
|
20101
20451
|
readRequiredTrimmedFile(
|
|
20102
|
-
|
|
20452
|
+
join6(agentDirectory, SECRET_KEY_FILE_NAME),
|
|
20103
20453
|
SECRET_KEY_FILE_NAME,
|
|
20104
20454
|
readFileImpl
|
|
20105
20455
|
),
|
|
20106
20456
|
readRequiredTrimmedFile(
|
|
20107
|
-
|
|
20457
|
+
join6(agentDirectory, IDENTITY_FILE_NAME2),
|
|
20108
20458
|
IDENTITY_FILE_NAME2,
|
|
20109
20459
|
readFileImpl
|
|
20110
20460
|
),
|
|
20111
20461
|
readRequiredTrimmedFile(
|
|
20112
|
-
|
|
20462
|
+
join6(agentDirectory, REGISTRY_AUTH_FILE_NAME2),
|
|
20113
20463
|
REGISTRY_AUTH_FILE_NAME2,
|
|
20114
20464
|
readFileImpl
|
|
20115
20465
|
),
|
|
@@ -20147,8 +20497,8 @@ async function startConnectorForAgent(agentName, commandOptions = {}, dependenci
|
|
|
20147
20497
|
tokenType: registryAuth.tokenType
|
|
20148
20498
|
}
|
|
20149
20499
|
});
|
|
20150
|
-
const outboundUrl = runtime &&
|
|
20151
|
-
const proxyWebsocketUrl = runtime &&
|
|
20500
|
+
const outboundUrl = runtime && isRecord6(runtime) && typeof runtime.outboundUrl === "string" ? runtime.outboundUrl : resolveOutboundUrl(outboundBaseUrl, outboundPath);
|
|
20501
|
+
const proxyWebsocketUrl = runtime && isRecord6(runtime) ? typeof runtime.websocketUrl === "string" ? runtime.websocketUrl : typeof runtime.proxyWebsocketUrl === "string" ? runtime.proxyWebsocketUrl : void 0 : void 0;
|
|
20152
20502
|
return {
|
|
20153
20503
|
outboundUrl,
|
|
20154
20504
|
proxyWebsocketUrl,
|
|
@@ -20279,7 +20629,7 @@ function createConnectorCommand(dependencies = {}) {
|
|
|
20279
20629
|
// src/commands/invite.ts
|
|
20280
20630
|
import { Command as Command6 } from "commander";
|
|
20281
20631
|
var logger7 = createLogger({ service: "cli", module: "invite" });
|
|
20282
|
-
var
|
|
20632
|
+
var isRecord7 = (value) => {
|
|
20283
20633
|
return typeof value === "object" && value !== null;
|
|
20284
20634
|
};
|
|
20285
20635
|
function parseNonEmptyString6(value) {
|
|
@@ -20320,7 +20670,7 @@ function toRegistryRequestUrl(registryUrl, path) {
|
|
|
20320
20670
|
return new URL(path.slice(1), normalizedBaseUrl).toString();
|
|
20321
20671
|
}
|
|
20322
20672
|
function extractRegistryErrorCode(payload) {
|
|
20323
|
-
if (!
|
|
20673
|
+
if (!isRecord7(payload)) {
|
|
20324
20674
|
return void 0;
|
|
20325
20675
|
}
|
|
20326
20676
|
const envelope = payload;
|
|
@@ -20331,7 +20681,7 @@ function extractRegistryErrorCode(payload) {
|
|
|
20331
20681
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
20332
20682
|
}
|
|
20333
20683
|
function extractRegistryErrorMessage3(payload) {
|
|
20334
|
-
if (!
|
|
20684
|
+
if (!isRecord7(payload)) {
|
|
20335
20685
|
return void 0;
|
|
20336
20686
|
}
|
|
20337
20687
|
const envelope = payload;
|
|
@@ -20405,13 +20755,13 @@ function mapRedeemInviteError(status, payload) {
|
|
|
20405
20755
|
return `Invite redeem failed (${status})`;
|
|
20406
20756
|
}
|
|
20407
20757
|
function parseInviteRecord(payload) {
|
|
20408
|
-
if (!
|
|
20758
|
+
if (!isRecord7(payload)) {
|
|
20409
20759
|
throw createCliError4(
|
|
20410
20760
|
"CLI_INVITE_CREATE_INVALID_RESPONSE",
|
|
20411
20761
|
"Invite response is invalid"
|
|
20412
20762
|
);
|
|
20413
20763
|
}
|
|
20414
|
-
const source =
|
|
20764
|
+
const source = isRecord7(payload.invite) ? payload.invite : payload;
|
|
20415
20765
|
const code = parseNonEmptyString6(source.code);
|
|
20416
20766
|
if (code.length === 0) {
|
|
20417
20767
|
throw createCliError4(
|
|
@@ -20436,15 +20786,15 @@ function parseInviteRecord(payload) {
|
|
|
20436
20786
|
return invite;
|
|
20437
20787
|
}
|
|
20438
20788
|
function parseInviteRedeemResponse(payload) {
|
|
20439
|
-
if (!
|
|
20789
|
+
if (!isRecord7(payload)) {
|
|
20440
20790
|
throw createCliError4(
|
|
20441
20791
|
"CLI_INVITE_REDEEM_INVALID_RESPONSE",
|
|
20442
20792
|
"Invite redeem response is invalid"
|
|
20443
20793
|
);
|
|
20444
20794
|
}
|
|
20445
|
-
const apiKeySource =
|
|
20795
|
+
const apiKeySource = isRecord7(payload.apiKey) ? payload.apiKey : payload;
|
|
20446
20796
|
const apiKeyToken = parseNonEmptyString6(
|
|
20447
|
-
|
|
20797
|
+
isRecord7(payload.apiKey) ? payload.apiKey.token : payload.token
|
|
20448
20798
|
);
|
|
20449
20799
|
if (apiKeyToken.length === 0) {
|
|
20450
20800
|
throw createCliError4(
|
|
@@ -20602,9 +20952,9 @@ var createInviteCommand = (dependencies = {}) => {
|
|
|
20602
20952
|
};
|
|
20603
20953
|
|
|
20604
20954
|
// src/commands/openclaw.ts
|
|
20605
|
-
import { chmod as chmod3, copyFile, mkdir as mkdir5, readFile as
|
|
20955
|
+
import { chmod as chmod3, copyFile, mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
20606
20956
|
import { homedir as homedir3 } from "os";
|
|
20607
|
-
import { dirname as dirname4, join as
|
|
20957
|
+
import { dirname as dirname4, join as join7 } from "path";
|
|
20608
20958
|
import { Command as Command7 } from "commander";
|
|
20609
20959
|
var logger8 = createLogger({ service: "cli", module: "openclaw" });
|
|
20610
20960
|
var CLAWDENTITY_DIR_NAME = ".clawdentity";
|
|
@@ -20622,12 +20972,17 @@ var HOOK_MAPPING_ID = "clawdentity-send-to-peer";
|
|
|
20622
20972
|
var HOOK_PATH_SEND_TO_PEER = "send-to-peer";
|
|
20623
20973
|
var OPENCLAW_SEND_TO_PEER_HOOK_PATH = "hooks/send-to-peer";
|
|
20624
20974
|
var DEFAULT_OPENCLAW_BASE_URL2 = "http://127.0.0.1:18789";
|
|
20975
|
+
var DEFAULT_RELAY_ECHO_ENABLED2 = true;
|
|
20976
|
+
var DEFAULT_RELAY_ECHO_CHANNEL2 = "last";
|
|
20977
|
+
var DEFAULT_RELAY_ECHO_MAX_LENGTH2 = 500;
|
|
20978
|
+
var MIN_RELAY_ECHO_MAX_LENGTH = 50;
|
|
20979
|
+
var MAX_RELAY_ECHO_MAX_LENGTH = 2e3;
|
|
20625
20980
|
var INVITE_CODE_PREFIX = "clawd1_";
|
|
20626
20981
|
var PEER_ALIAS_PATTERN = /^[a-zA-Z0-9._-]+$/;
|
|
20627
20982
|
var FILE_MODE3 = 384;
|
|
20628
20983
|
var textEncoder2 = new TextEncoder();
|
|
20629
20984
|
var textDecoder = new TextDecoder();
|
|
20630
|
-
function
|
|
20985
|
+
function isRecord8(value) {
|
|
20631
20986
|
return typeof value === "object" && value !== null;
|
|
20632
20987
|
}
|
|
20633
20988
|
function createCliError5(code, message2, details) {
|
|
@@ -20639,7 +20994,7 @@ function createCliError5(code, message2, details) {
|
|
|
20639
20994
|
});
|
|
20640
20995
|
}
|
|
20641
20996
|
function getErrorCode2(error48) {
|
|
20642
|
-
if (!
|
|
20997
|
+
if (!isRecord8(error48)) {
|
|
20643
20998
|
return void 0;
|
|
20644
20999
|
}
|
|
20645
21000
|
return typeof error48.code === "string" ? error48.code : void 0;
|
|
@@ -20709,13 +21064,82 @@ function parseHttpUrl(value, input) {
|
|
|
20709
21064
|
}
|
|
20710
21065
|
return parsedUrl.toString();
|
|
20711
21066
|
}
|
|
20712
|
-
function
|
|
21067
|
+
function parseOpenclawBaseUrl2(value) {
|
|
20713
21068
|
return parseHttpUrl(value, {
|
|
20714
21069
|
label: "OpenClaw base URL",
|
|
20715
21070
|
code: "CLI_OPENCLAW_INVALID_OPENCLAW_BASE_URL",
|
|
20716
21071
|
message: "OpenClaw base URL must be a valid URL"
|
|
20717
21072
|
});
|
|
20718
21073
|
}
|
|
21074
|
+
function parseBooleanValue(value, label) {
|
|
21075
|
+
if (typeof value === "boolean") {
|
|
21076
|
+
return value;
|
|
21077
|
+
}
|
|
21078
|
+
if (typeof value === "string") {
|
|
21079
|
+
const normalized = value.trim().toLowerCase();
|
|
21080
|
+
if (normalized === "true" || normalized === "1" || normalized === "yes" || normalized === "on") {
|
|
21081
|
+
return true;
|
|
21082
|
+
}
|
|
21083
|
+
if (normalized === "false" || normalized === "0" || normalized === "no" || normalized === "off") {
|
|
21084
|
+
return false;
|
|
21085
|
+
}
|
|
21086
|
+
}
|
|
21087
|
+
throw createCliError5(
|
|
21088
|
+
"CLI_OPENCLAW_INVALID_INPUT",
|
|
21089
|
+
`${label} must be true or false`,
|
|
21090
|
+
{ label }
|
|
21091
|
+
);
|
|
21092
|
+
}
|
|
21093
|
+
function parseRelayEchoChannel(value) {
|
|
21094
|
+
return parseNonEmptyString7(value, "relay echo channel");
|
|
21095
|
+
}
|
|
21096
|
+
function parseRelayEchoMaxLength(value) {
|
|
21097
|
+
const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : Number.NaN;
|
|
21098
|
+
if (!Number.isInteger(parsed) || parsed < MIN_RELAY_ECHO_MAX_LENGTH || parsed > MAX_RELAY_ECHO_MAX_LENGTH) {
|
|
21099
|
+
throw createCliError5(
|
|
21100
|
+
"CLI_OPENCLAW_INVALID_ECHO_MAX_LENGTH",
|
|
21101
|
+
`echo max length must be an integer between ${MIN_RELAY_ECHO_MAX_LENGTH} and ${MAX_RELAY_ECHO_MAX_LENGTH}`
|
|
21102
|
+
);
|
|
21103
|
+
}
|
|
21104
|
+
return parsed;
|
|
21105
|
+
}
|
|
21106
|
+
function parseRelayEchoEnabled(value) {
|
|
21107
|
+
return parseBooleanValue(value, "relay echo enabled");
|
|
21108
|
+
}
|
|
21109
|
+
function parseRelayEchoTo(value) {
|
|
21110
|
+
if (value === void 0) {
|
|
21111
|
+
return void 0;
|
|
21112
|
+
}
|
|
21113
|
+
if (typeof value !== "string") {
|
|
21114
|
+
throw createCliError5(
|
|
21115
|
+
"CLI_OPENCLAW_INVALID_INPUT",
|
|
21116
|
+
"relay echo target must be a string",
|
|
21117
|
+
{ label: "relay echo target" }
|
|
21118
|
+
);
|
|
21119
|
+
}
|
|
21120
|
+
const trimmed = value.trim();
|
|
21121
|
+
if (trimmed.length === 0) {
|
|
21122
|
+
return void 0;
|
|
21123
|
+
}
|
|
21124
|
+
return trimmed;
|
|
21125
|
+
}
|
|
21126
|
+
function defaultRelayEchoConfig() {
|
|
21127
|
+
return {
|
|
21128
|
+
enabled: DEFAULT_RELAY_ECHO_ENABLED2,
|
|
21129
|
+
channel: DEFAULT_RELAY_ECHO_CHANNEL2,
|
|
21130
|
+
maxLength: DEFAULT_RELAY_ECHO_MAX_LENGTH2
|
|
21131
|
+
};
|
|
21132
|
+
}
|
|
21133
|
+
function resolveSetupRelayEchoConfig(input) {
|
|
21134
|
+
const current = input.existingConfig?.echo ?? defaultRelayEchoConfig();
|
|
21135
|
+
const options = input.options;
|
|
21136
|
+
return {
|
|
21137
|
+
enabled: options.echoEnabled === void 0 ? current.enabled : parseRelayEchoEnabled(options.echoEnabled),
|
|
21138
|
+
channel: options.echoChannel === void 0 ? current.channel : parseRelayEchoChannel(options.echoChannel),
|
|
21139
|
+
to: options.echoTo === void 0 ? current.to : parseRelayEchoTo(options.echoTo),
|
|
21140
|
+
maxLength: options.echoMaxLength === void 0 ? current.maxLength : parseRelayEchoMaxLength(options.echoMaxLength)
|
|
21141
|
+
};
|
|
21142
|
+
}
|
|
20719
21143
|
function parseAgentDid2(value, label) {
|
|
20720
21144
|
const did = parseNonEmptyString7(value, label);
|
|
20721
21145
|
try {
|
|
@@ -20734,7 +21158,7 @@ function parseAgentDid2(value, label) {
|
|
|
20734
21158
|
return did;
|
|
20735
21159
|
}
|
|
20736
21160
|
function parseInvitePayload(value) {
|
|
20737
|
-
if (!
|
|
21161
|
+
if (!isRecord8(value)) {
|
|
20738
21162
|
throw createCliError5(
|
|
20739
21163
|
"CLI_OPENCLAW_INVALID_INVITE",
|
|
20740
21164
|
"invite payload must be an object"
|
|
@@ -20787,19 +21211,19 @@ function resolveOpenclawDir(openclawDir, homeDir) {
|
|
|
20787
21211
|
if (typeof openclawDir === "string" && openclawDir.trim().length > 0) {
|
|
20788
21212
|
return openclawDir.trim();
|
|
20789
21213
|
}
|
|
20790
|
-
return
|
|
21214
|
+
return join7(homeDir, OPENCLAW_DIR_NAME);
|
|
20791
21215
|
}
|
|
20792
21216
|
function resolveAgentDirectory(homeDir, agentName) {
|
|
20793
|
-
return
|
|
21217
|
+
return join7(homeDir, CLAWDENTITY_DIR_NAME, AGENTS_DIR_NAME4, agentName);
|
|
20794
21218
|
}
|
|
20795
21219
|
function resolvePeersPath(homeDir) {
|
|
20796
|
-
return
|
|
21220
|
+
return join7(homeDir, CLAWDENTITY_DIR_NAME, PEERS_FILE_NAME);
|
|
20797
21221
|
}
|
|
20798
21222
|
function resolveOpenclawConfigPath(openclawDir) {
|
|
20799
|
-
return
|
|
21223
|
+
return join7(openclawDir, OPENCLAW_CONFIG_FILE_NAME);
|
|
20800
21224
|
}
|
|
20801
21225
|
function resolveDefaultTransformSource(openclawDir) {
|
|
20802
|
-
return
|
|
21226
|
+
return join7(
|
|
20803
21227
|
openclawDir,
|
|
20804
21228
|
"workspace",
|
|
20805
21229
|
"skills",
|
|
@@ -20808,16 +21232,16 @@ function resolveDefaultTransformSource(openclawDir) {
|
|
|
20808
21232
|
);
|
|
20809
21233
|
}
|
|
20810
21234
|
function resolveTransformTargetPath(openclawDir) {
|
|
20811
|
-
return
|
|
21235
|
+
return join7(openclawDir, "hooks", "transforms", RELAY_MODULE_FILE_NAME);
|
|
20812
21236
|
}
|
|
20813
21237
|
function resolveOpenclawAgentNamePath(homeDir) {
|
|
20814
|
-
return
|
|
21238
|
+
return join7(homeDir, CLAWDENTITY_DIR_NAME, OPENCLAW_AGENT_FILE_NAME);
|
|
20815
21239
|
}
|
|
20816
21240
|
function resolveRelayRuntimeConfigPath(homeDir) {
|
|
20817
|
-
return
|
|
21241
|
+
return join7(homeDir, CLAWDENTITY_DIR_NAME, OPENCLAW_RELAY_RUNTIME_FILE_NAME);
|
|
20818
21242
|
}
|
|
20819
21243
|
async function readJsonFile(filePath) {
|
|
20820
|
-
const raw = await
|
|
21244
|
+
const raw = await readFile5(filePath, "utf8");
|
|
20821
21245
|
try {
|
|
20822
21246
|
return JSON.parse(raw);
|
|
20823
21247
|
} catch {
|
|
@@ -20829,13 +21253,13 @@ async function readJsonFile(filePath) {
|
|
|
20829
21253
|
async function ensureLocalAgentCredentials(homeDir, agentName) {
|
|
20830
21254
|
const agentDir = resolveAgentDirectory(homeDir, agentName);
|
|
20831
21255
|
const requiredFiles = [
|
|
20832
|
-
|
|
20833
|
-
|
|
21256
|
+
join7(agentDir, SECRET_KEY_FILE_NAME2),
|
|
21257
|
+
join7(agentDir, AIT_FILE_NAME3)
|
|
20834
21258
|
];
|
|
20835
21259
|
for (const filePath of requiredFiles) {
|
|
20836
21260
|
let content;
|
|
20837
21261
|
try {
|
|
20838
|
-
content = await
|
|
21262
|
+
content = await readFile5(filePath, "utf8");
|
|
20839
21263
|
} catch (error48) {
|
|
20840
21264
|
if (getErrorCode2(error48) === "ENOENT") {
|
|
20841
21265
|
throw createCliError5(
|
|
@@ -20909,7 +21333,7 @@ async function loadPeersConfig(peersPath) {
|
|
|
20909
21333
|
}
|
|
20910
21334
|
throw error48;
|
|
20911
21335
|
}
|
|
20912
|
-
if (!
|
|
21336
|
+
if (!isRecord8(parsed)) {
|
|
20913
21337
|
throw createCliError5(
|
|
20914
21338
|
"CLI_OPENCLAW_INVALID_PEERS_CONFIG",
|
|
20915
21339
|
"Peer config root must be a JSON object",
|
|
@@ -20920,7 +21344,7 @@ async function loadPeersConfig(peersPath) {
|
|
|
20920
21344
|
if (peersValue === void 0) {
|
|
20921
21345
|
return { peers: {} };
|
|
20922
21346
|
}
|
|
20923
|
-
if (!
|
|
21347
|
+
if (!isRecord8(peersValue)) {
|
|
20924
21348
|
throw createCliError5(
|
|
20925
21349
|
"CLI_OPENCLAW_INVALID_PEERS_CONFIG",
|
|
20926
21350
|
"Peer config peers field must be an object",
|
|
@@ -20930,7 +21354,7 @@ async function loadPeersConfig(peersPath) {
|
|
|
20930
21354
|
const peers = {};
|
|
20931
21355
|
for (const [alias, value] of Object.entries(peersValue)) {
|
|
20932
21356
|
const normalizedAlias = parsePeerAlias(alias);
|
|
20933
|
-
if (!
|
|
21357
|
+
if (!isRecord8(value)) {
|
|
20934
21358
|
throw createCliError5(
|
|
20935
21359
|
"CLI_OPENCLAW_INVALID_PEERS_CONFIG",
|
|
20936
21360
|
"Peer entry must be an object",
|
|
@@ -20952,8 +21376,35 @@ async function savePeersConfig(peersPath, config2) {
|
|
|
20952
21376
|
await writeSecureFile3(peersPath, `${JSON.stringify(config2, null, 2)}
|
|
20953
21377
|
`);
|
|
20954
21378
|
}
|
|
21379
|
+
function parseRelayRuntimeEchoConfig(value, relayRuntimeConfigPath) {
|
|
21380
|
+
if (value === void 0) {
|
|
21381
|
+
return defaultRelayEchoConfig();
|
|
21382
|
+
}
|
|
21383
|
+
if (!isRecord8(value)) {
|
|
21384
|
+
throw createCliError5(
|
|
21385
|
+
"CLI_OPENCLAW_INVALID_RELAY_RUNTIME_CONFIG",
|
|
21386
|
+
"Relay runtime echo config must be an object",
|
|
21387
|
+
{ relayRuntimeConfigPath }
|
|
21388
|
+
);
|
|
21389
|
+
}
|
|
21390
|
+
const defaults = defaultRelayEchoConfig();
|
|
21391
|
+
try {
|
|
21392
|
+
return {
|
|
21393
|
+
enabled: value.enabled === void 0 ? defaults.enabled : parseRelayEchoEnabled(value.enabled),
|
|
21394
|
+
channel: value.channel === void 0 ? defaults.channel : parseRelayEchoChannel(value.channel),
|
|
21395
|
+
to: parseRelayEchoTo(value.to),
|
|
21396
|
+
maxLength: value.maxLength === void 0 ? defaults.maxLength : parseRelayEchoMaxLength(value.maxLength)
|
|
21397
|
+
};
|
|
21398
|
+
} catch (error48) {
|
|
21399
|
+
throw createCliError5(
|
|
21400
|
+
"CLI_OPENCLAW_INVALID_RELAY_RUNTIME_CONFIG",
|
|
21401
|
+
error48 instanceof Error ? error48.message : "Relay runtime echo config is invalid",
|
|
21402
|
+
{ relayRuntimeConfigPath }
|
|
21403
|
+
);
|
|
21404
|
+
}
|
|
21405
|
+
}
|
|
20955
21406
|
function parseRelayRuntimeConfig(value, relayRuntimeConfigPath) {
|
|
20956
|
-
if (!
|
|
21407
|
+
if (!isRecord8(value)) {
|
|
20957
21408
|
throw createCliError5(
|
|
20958
21409
|
"CLI_OPENCLAW_INVALID_RELAY_RUNTIME_CONFIG",
|
|
20959
21410
|
"Relay runtime config must be an object",
|
|
@@ -20961,9 +21412,11 @@ function parseRelayRuntimeConfig(value, relayRuntimeConfigPath) {
|
|
|
20961
21412
|
);
|
|
20962
21413
|
}
|
|
20963
21414
|
const updatedAt = typeof value.updatedAt === "string" && value.updatedAt.trim().length > 0 ? value.updatedAt.trim() : void 0;
|
|
21415
|
+
const echo = parseRelayRuntimeEchoConfig(value.echo, relayRuntimeConfigPath);
|
|
20964
21416
|
return {
|
|
20965
|
-
openclawBaseUrl:
|
|
20966
|
-
updatedAt
|
|
21417
|
+
openclawBaseUrl: parseOpenclawBaseUrl2(value.openclawBaseUrl),
|
|
21418
|
+
updatedAt,
|
|
21419
|
+
echo
|
|
20967
21420
|
};
|
|
20968
21421
|
}
|
|
20969
21422
|
async function loadRelayRuntimeConfig(relayRuntimeConfigPath) {
|
|
@@ -20978,10 +21431,11 @@ async function loadRelayRuntimeConfig(relayRuntimeConfigPath) {
|
|
|
20978
21431
|
}
|
|
20979
21432
|
return parseRelayRuntimeConfig(parsed, relayRuntimeConfigPath);
|
|
20980
21433
|
}
|
|
20981
|
-
async function saveRelayRuntimeConfig(relayRuntimeConfigPath, openclawBaseUrl) {
|
|
21434
|
+
async function saveRelayRuntimeConfig(relayRuntimeConfigPath, openclawBaseUrl, relayEchoConfig) {
|
|
20982
21435
|
const config2 = {
|
|
20983
21436
|
openclawBaseUrl,
|
|
20984
|
-
updatedAt: nowIso()
|
|
21437
|
+
updatedAt: nowIso(),
|
|
21438
|
+
echo: relayEchoConfig
|
|
20985
21439
|
};
|
|
20986
21440
|
await writeSecureFile3(
|
|
20987
21441
|
relayRuntimeConfigPath,
|
|
@@ -20989,13 +21443,13 @@ async function saveRelayRuntimeConfig(relayRuntimeConfigPath, openclawBaseUrl) {
|
|
|
20989
21443
|
`
|
|
20990
21444
|
);
|
|
20991
21445
|
}
|
|
20992
|
-
async function
|
|
21446
|
+
async function resolveOpenclawBaseUrl(input) {
|
|
20993
21447
|
if (typeof input.optionValue === "string" && input.optionValue.trim().length > 0) {
|
|
20994
|
-
return
|
|
21448
|
+
return parseOpenclawBaseUrl2(input.optionValue);
|
|
20995
21449
|
}
|
|
20996
21450
|
const envOpenclawBaseUrl = process.env.OPENCLAW_BASE_URL;
|
|
20997
21451
|
if (typeof envOpenclawBaseUrl === "string" && envOpenclawBaseUrl.trim().length > 0) {
|
|
20998
|
-
return
|
|
21452
|
+
return parseOpenclawBaseUrl2(envOpenclawBaseUrl);
|
|
20999
21453
|
}
|
|
21000
21454
|
const existingConfig = await loadRelayRuntimeConfig(
|
|
21001
21455
|
input.relayRuntimeConfigPath
|
|
@@ -21022,20 +21476,20 @@ function normalizeStringArrayWithValue(value, requiredValue) {
|
|
|
21022
21476
|
return Array.from(normalized);
|
|
21023
21477
|
}
|
|
21024
21478
|
function upsertRelayHookMapping(mappingsValue) {
|
|
21025
|
-
const mappings = Array.isArray(mappingsValue) ? mappingsValue.filter(
|
|
21479
|
+
const mappings = Array.isArray(mappingsValue) ? mappingsValue.filter(isRecord8).map((mapping) => ({ ...mapping })) : [];
|
|
21026
21480
|
const existingIndex = mappings.findIndex((mapping) => {
|
|
21027
21481
|
if (mapping.id === HOOK_MAPPING_ID) {
|
|
21028
21482
|
return true;
|
|
21029
21483
|
}
|
|
21030
|
-
if (!
|
|
21484
|
+
if (!isRecord8(mapping.match)) {
|
|
21031
21485
|
return false;
|
|
21032
21486
|
}
|
|
21033
21487
|
return mapping.match.path === HOOK_PATH_SEND_TO_PEER;
|
|
21034
21488
|
});
|
|
21035
|
-
const baseMapping = existingIndex >= 0 &&
|
|
21036
|
-
const nextMatch =
|
|
21489
|
+
const baseMapping = existingIndex >= 0 && isRecord8(mappings[existingIndex]) ? mappings[existingIndex] : {};
|
|
21490
|
+
const nextMatch = isRecord8(baseMapping.match) ? { ...baseMapping.match } : {};
|
|
21037
21491
|
nextMatch.path = HOOK_PATH_SEND_TO_PEER;
|
|
21038
|
-
const nextTransform =
|
|
21492
|
+
const nextTransform = isRecord8(baseMapping.transform) ? { ...baseMapping.transform } : {};
|
|
21039
21493
|
nextTransform.module = RELAY_MODULE_FILE_NAME;
|
|
21040
21494
|
const relayMapping = {
|
|
21041
21495
|
...baseMapping,
|
|
@@ -21066,14 +21520,14 @@ async function patchOpenclawConfig(openclawConfigPath) {
|
|
|
21066
21520
|
}
|
|
21067
21521
|
throw error48;
|
|
21068
21522
|
}
|
|
21069
|
-
if (!
|
|
21523
|
+
if (!isRecord8(config2)) {
|
|
21070
21524
|
throw createCliError5(
|
|
21071
21525
|
"CLI_OPENCLAW_INVALID_CONFIG",
|
|
21072
21526
|
"OpenClaw config root must be an object",
|
|
21073
21527
|
{ openclawConfigPath }
|
|
21074
21528
|
);
|
|
21075
21529
|
}
|
|
21076
|
-
const hooks =
|
|
21530
|
+
const hooks = isRecord8(config2.hooks) ? { ...config2.hooks } : {};
|
|
21077
21531
|
hooks.enabled = true;
|
|
21078
21532
|
hooks.allowRequestSessionKey = false;
|
|
21079
21533
|
hooks.allowedSessionKeyPrefixes = normalizeStringArrayWithValue(
|
|
@@ -21103,10 +21557,10 @@ function toDoctorResult(checks) {
|
|
|
21103
21557
|
};
|
|
21104
21558
|
}
|
|
21105
21559
|
function isRelayHookMapping(value) {
|
|
21106
|
-
if (!
|
|
21560
|
+
if (!isRecord8(value)) {
|
|
21107
21561
|
return false;
|
|
21108
21562
|
}
|
|
21109
|
-
if (!
|
|
21563
|
+
if (!isRecord8(value.match) || value.match.path !== HOOK_PATH_SEND_TO_PEER) {
|
|
21110
21564
|
return false;
|
|
21111
21565
|
}
|
|
21112
21566
|
if (typeof value.id === "string" && value.id !== HOOK_MAPPING_ID) {
|
|
@@ -21115,7 +21569,7 @@ function isRelayHookMapping(value) {
|
|
|
21115
21569
|
return true;
|
|
21116
21570
|
}
|
|
21117
21571
|
function hasRelayTransformModule(value) {
|
|
21118
|
-
if (!
|
|
21572
|
+
if (!isRecord8(value) || !isRecord8(value.transform)) {
|
|
21119
21573
|
return false;
|
|
21120
21574
|
}
|
|
21121
21575
|
return value.transform.module === RELAY_MODULE_FILE_NAME;
|
|
@@ -21232,7 +21686,7 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
21232
21686
|
const selectedAgentPath = resolveOpenclawAgentNamePath(homeDir);
|
|
21233
21687
|
let selectedAgentName;
|
|
21234
21688
|
try {
|
|
21235
|
-
const selectedAgentRaw = await
|
|
21689
|
+
const selectedAgentRaw = await readFile5(selectedAgentPath, "utf8");
|
|
21236
21690
|
selectedAgentName = assertValidAgentName(selectedAgentRaw.trim());
|
|
21237
21691
|
checks.push(
|
|
21238
21692
|
toDoctorCheck({
|
|
@@ -21354,7 +21808,7 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
21354
21808
|
}
|
|
21355
21809
|
const transformTargetPath = resolveTransformTargetPath(openclawDir);
|
|
21356
21810
|
try {
|
|
21357
|
-
const transformContents = await
|
|
21811
|
+
const transformContents = await readFile5(transformTargetPath, "utf8");
|
|
21358
21812
|
if (transformContents.trim().length === 0) {
|
|
21359
21813
|
checks.push(
|
|
21360
21814
|
toDoctorCheck({
|
|
@@ -21392,11 +21846,11 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
21392
21846
|
const openclawConfigPath = resolveOpenclawConfigPath(openclawDir);
|
|
21393
21847
|
try {
|
|
21394
21848
|
const openclawConfig = await readJsonFile(openclawConfigPath);
|
|
21395
|
-
if (!
|
|
21849
|
+
if (!isRecord8(openclawConfig)) {
|
|
21396
21850
|
throw new Error("root");
|
|
21397
21851
|
}
|
|
21398
|
-
const hooks =
|
|
21399
|
-
const mappings = Array.isArray(hooks.mappings) ? hooks.mappings.filter(
|
|
21852
|
+
const hooks = isRecord8(openclawConfig.hooks) ? openclawConfig.hooks : {};
|
|
21853
|
+
const mappings = Array.isArray(hooks.mappings) ? hooks.mappings.filter(isRecord8) : [];
|
|
21400
21854
|
const relayMapping = mappings.find(
|
|
21401
21855
|
(mapping) => isRelayHookMapping(mapping)
|
|
21402
21856
|
);
|
|
@@ -21436,7 +21890,7 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
21436
21890
|
}
|
|
21437
21891
|
const relayRuntimeConfigPath = resolveRelayRuntimeConfigPath(homeDir);
|
|
21438
21892
|
try {
|
|
21439
|
-
const openclawBaseUrl = await
|
|
21893
|
+
const openclawBaseUrl = await resolveOpenclawBaseUrl({
|
|
21440
21894
|
relayRuntimeConfigPath
|
|
21441
21895
|
});
|
|
21442
21896
|
checks.push(
|
|
@@ -21498,7 +21952,7 @@ async function runOpenclawRelayTest(options) {
|
|
|
21498
21952
|
const relayRuntimeConfigPath = resolveRelayRuntimeConfigPath(homeDir);
|
|
21499
21953
|
let openclawBaseUrl = DEFAULT_OPENCLAW_BASE_URL2;
|
|
21500
21954
|
try {
|
|
21501
|
-
openclawBaseUrl = await
|
|
21955
|
+
openclawBaseUrl = await resolveOpenclawBaseUrl({
|
|
21502
21956
|
optionValue: options.openclawBaseUrl,
|
|
21503
21957
|
relayRuntimeConfigPath
|
|
21504
21958
|
});
|
|
@@ -21624,10 +22078,17 @@ async function setupOpenclawRelayFromInvite(agentName, options) {
|
|
|
21624
22078
|
const transformSource = typeof options.transformSource === "string" && options.transformSource.trim().length > 0 ? options.transformSource.trim() : resolveDefaultTransformSource(openclawDir);
|
|
21625
22079
|
const transformTargetPath = resolveTransformTargetPath(openclawDir);
|
|
21626
22080
|
const relayRuntimeConfigPath = resolveRelayRuntimeConfigPath(homeDir);
|
|
21627
|
-
const
|
|
22081
|
+
const existingRelayRuntimeConfig = await loadRelayRuntimeConfig(
|
|
22082
|
+
relayRuntimeConfigPath
|
|
22083
|
+
);
|
|
22084
|
+
const openclawBaseUrl = await resolveOpenclawBaseUrl({
|
|
21628
22085
|
optionValue: options.openclawBaseUrl,
|
|
21629
22086
|
relayRuntimeConfigPath
|
|
21630
22087
|
});
|
|
22088
|
+
const relayEchoConfig = resolveSetupRelayEchoConfig({
|
|
22089
|
+
existingConfig: existingRelayRuntimeConfig,
|
|
22090
|
+
options
|
|
22091
|
+
});
|
|
21631
22092
|
const invite = decodeInvitePayload(options.inviteCode);
|
|
21632
22093
|
const peerAliasCandidate = options.peerAlias ?? invite.alias;
|
|
21633
22094
|
if (!peerAliasCandidate) {
|
|
@@ -21659,7 +22120,11 @@ async function setupOpenclawRelayFromInvite(agentName, options) {
|
|
|
21659
22120
|
const agentNamePath = resolveOpenclawAgentNamePath(homeDir);
|
|
21660
22121
|
await writeSecureFile3(agentNamePath, `${normalizedAgentName}
|
|
21661
22122
|
`);
|
|
21662
|
-
await saveRelayRuntimeConfig(
|
|
22123
|
+
await saveRelayRuntimeConfig(
|
|
22124
|
+
relayRuntimeConfigPath,
|
|
22125
|
+
openclawBaseUrl,
|
|
22126
|
+
relayEchoConfig
|
|
22127
|
+
);
|
|
21663
22128
|
logger8.info("cli.openclaw_setup_completed", {
|
|
21664
22129
|
agentName: normalizedAgentName,
|
|
21665
22130
|
peerAlias,
|
|
@@ -21667,7 +22132,8 @@ async function setupOpenclawRelayFromInvite(agentName, options) {
|
|
|
21667
22132
|
openclawConfigPath,
|
|
21668
22133
|
transformTargetPath,
|
|
21669
22134
|
openclawBaseUrl,
|
|
21670
|
-
relayRuntimeConfigPath
|
|
22135
|
+
relayRuntimeConfigPath,
|
|
22136
|
+
relayEchoConfig
|
|
21671
22137
|
});
|
|
21672
22138
|
return {
|
|
21673
22139
|
peerAlias,
|
|
@@ -21676,7 +22142,8 @@ async function setupOpenclawRelayFromInvite(agentName, options) {
|
|
|
21676
22142
|
openclawConfigPath,
|
|
21677
22143
|
transformTargetPath,
|
|
21678
22144
|
openclawBaseUrl,
|
|
21679
|
-
relayRuntimeConfigPath
|
|
22145
|
+
relayRuntimeConfigPath,
|
|
22146
|
+
relayEcho: relayEchoConfig
|
|
21680
22147
|
};
|
|
21681
22148
|
}
|
|
21682
22149
|
var createOpenclawCommand = () => {
|
|
@@ -21712,6 +22179,20 @@ var createOpenclawCommand = () => {
|
|
|
21712
22179
|
).option(
|
|
21713
22180
|
"--openclaw-base-url <url>",
|
|
21714
22181
|
"Base URL for local OpenClaw hook API (default http://127.0.0.1:18789)"
|
|
22182
|
+
).option(
|
|
22183
|
+
"--echo-enabled <true|false>",
|
|
22184
|
+
"Enable or disable relay echo to the operator chat channel (default true)",
|
|
22185
|
+
parseRelayEchoEnabled
|
|
22186
|
+
).option(
|
|
22187
|
+
"--echo-channel <channel>",
|
|
22188
|
+
"Relay echo delivery channel (default last)"
|
|
22189
|
+
).option(
|
|
22190
|
+
"--echo-to <target>",
|
|
22191
|
+
"Relay echo delivery target id/recipient (optional)"
|
|
22192
|
+
).option(
|
|
22193
|
+
"--echo-max-length <number>",
|
|
22194
|
+
`Relay echo message truncation limit (${MIN_RELAY_ECHO_MAX_LENGTH}-${MAX_RELAY_ECHO_MAX_LENGTH}, default ${DEFAULT_RELAY_ECHO_MAX_LENGTH2})`,
|
|
22195
|
+
parseRelayEchoMaxLength
|
|
21715
22196
|
).action(
|
|
21716
22197
|
withErrorHandling(
|
|
21717
22198
|
"openclaw setup",
|
|
@@ -21725,6 +22206,16 @@ var createOpenclawCommand = () => {
|
|
|
21725
22206
|
);
|
|
21726
22207
|
writeStdoutLine(`Installed transform: ${result.transformTargetPath}`);
|
|
21727
22208
|
writeStdoutLine(`OpenClaw base URL: ${result.openclawBaseUrl}`);
|
|
22209
|
+
writeStdoutLine(
|
|
22210
|
+
`Relay echo enabled: ${result.relayEcho.enabled ? "true" : "false"}`
|
|
22211
|
+
);
|
|
22212
|
+
writeStdoutLine(`Relay echo channel: ${result.relayEcho.channel}`);
|
|
22213
|
+
writeStdoutLine(
|
|
22214
|
+
`Relay echo target: ${result.relayEcho.to ?? "(last route default)"}`
|
|
22215
|
+
);
|
|
22216
|
+
writeStdoutLine(
|
|
22217
|
+
`Relay echo max length: ${result.relayEcho.maxLength}`
|
|
22218
|
+
);
|
|
21728
22219
|
writeStdoutLine(
|
|
21729
22220
|
`Relay runtime config: ${result.relayRuntimeConfigPath}`
|
|
21730
22221
|
);
|
|
@@ -21794,10 +22285,620 @@ var createOpenclawCommand = () => {
|
|
|
21794
22285
|
return openclawCommand;
|
|
21795
22286
|
};
|
|
21796
22287
|
|
|
21797
|
-
// src/commands/
|
|
21798
|
-
import {
|
|
22288
|
+
// src/commands/pair.ts
|
|
22289
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
22290
|
+
import { mkdir as mkdir6, readdir, readFile as readFile6, unlink as unlink2, writeFile as writeFile6 } from "fs/promises";
|
|
22291
|
+
import { dirname as dirname5, join as join8, resolve } from "path";
|
|
21799
22292
|
import { Command as Command8 } from "commander";
|
|
21800
|
-
|
|
22293
|
+
import jsQR from "jsqr";
|
|
22294
|
+
import { PNG } from "pngjs";
|
|
22295
|
+
import QRCode from "qrcode";
|
|
22296
|
+
var logger9 = createLogger({ service: "cli", module: "pair" });
|
|
22297
|
+
var AGENTS_DIR_NAME5 = "agents";
|
|
22298
|
+
var AIT_FILE_NAME4 = "ait.jwt";
|
|
22299
|
+
var SECRET_KEY_FILE_NAME3 = "secret.key";
|
|
22300
|
+
var PAIRING_QR_DIR_NAME = "pairing";
|
|
22301
|
+
var PAIR_START_PATH = "/pair/start";
|
|
22302
|
+
var PAIR_CONFIRM_PATH = "/pair/confirm";
|
|
22303
|
+
var OWNER_PAT_HEADER = "x-claw-owner-pat";
|
|
22304
|
+
var NONCE_SIZE2 = 24;
|
|
22305
|
+
var PAIRING_TICKET_PREFIX = "clwpair1_";
|
|
22306
|
+
var PAIRING_QR_MAX_AGE_SECONDS = 900;
|
|
22307
|
+
var PAIRING_QR_FILENAME_PATTERN = /-pair-(\d+)\.png$/;
|
|
22308
|
+
var isRecord9 = (value) => {
|
|
22309
|
+
return typeof value === "object" && value !== null;
|
|
22310
|
+
};
|
|
22311
|
+
function createCliError6(code, message2) {
|
|
22312
|
+
return new AppError({
|
|
22313
|
+
code,
|
|
22314
|
+
message: message2,
|
|
22315
|
+
status: 400
|
|
22316
|
+
});
|
|
22317
|
+
}
|
|
22318
|
+
function parseNonEmptyString8(value) {
|
|
22319
|
+
if (typeof value !== "string") {
|
|
22320
|
+
return "";
|
|
22321
|
+
}
|
|
22322
|
+
return value.trim();
|
|
22323
|
+
}
|
|
22324
|
+
function parsePairingTicket(value) {
|
|
22325
|
+
const ticket = parseNonEmptyString8(value);
|
|
22326
|
+
if (!ticket.startsWith(PAIRING_TICKET_PREFIX)) {
|
|
22327
|
+
throw createCliError6(
|
|
22328
|
+
"CLI_PAIR_CONFIRM_TICKET_INVALID",
|
|
22329
|
+
"Pairing ticket is invalid"
|
|
22330
|
+
);
|
|
22331
|
+
}
|
|
22332
|
+
return ticket;
|
|
22333
|
+
}
|
|
22334
|
+
function parseTtlSeconds(value) {
|
|
22335
|
+
const raw = parseNonEmptyString8(value);
|
|
22336
|
+
if (raw.length === 0) {
|
|
22337
|
+
return void 0;
|
|
22338
|
+
}
|
|
22339
|
+
const parsed = Number.parseInt(raw, 10);
|
|
22340
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
22341
|
+
throw createCliError6(
|
|
22342
|
+
"CLI_PAIR_START_INVALID_TTL",
|
|
22343
|
+
"ttlSeconds must be a positive integer"
|
|
22344
|
+
);
|
|
22345
|
+
}
|
|
22346
|
+
return parsed;
|
|
22347
|
+
}
|
|
22348
|
+
function resolveProxyUrl(overrideProxyUrl) {
|
|
22349
|
+
const candidate = parseNonEmptyString8(overrideProxyUrl) || parseNonEmptyString8(process.env.CLAWDENTITY_PROXY_URL);
|
|
22350
|
+
if (candidate.length === 0) {
|
|
22351
|
+
throw createCliError6(
|
|
22352
|
+
"CLI_PAIR_PROXY_URL_REQUIRED",
|
|
22353
|
+
"Proxy URL is required. Pass --proxy-url <url> or set CLAWDENTITY_PROXY_URL."
|
|
22354
|
+
);
|
|
22355
|
+
}
|
|
22356
|
+
try {
|
|
22357
|
+
const parsed = new URL(candidate);
|
|
22358
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
22359
|
+
throw new Error("invalid protocol");
|
|
22360
|
+
}
|
|
22361
|
+
return parsed.toString();
|
|
22362
|
+
} catch {
|
|
22363
|
+
throw createCliError6("CLI_PAIR_INVALID_PROXY_URL", "Proxy URL is invalid");
|
|
22364
|
+
}
|
|
22365
|
+
}
|
|
22366
|
+
function toProxyRequestUrl(proxyUrl, path) {
|
|
22367
|
+
const normalizedBase = proxyUrl.endsWith("/") ? proxyUrl : `${proxyUrl}/`;
|
|
22368
|
+
return new URL(path.slice(1), normalizedBase).toString();
|
|
22369
|
+
}
|
|
22370
|
+
function toPathWithQuery3(url2) {
|
|
22371
|
+
const parsed = new URL(url2);
|
|
22372
|
+
return `${parsed.pathname}${parsed.search}`;
|
|
22373
|
+
}
|
|
22374
|
+
function extractErrorCode(payload) {
|
|
22375
|
+
if (!isRecord9(payload)) {
|
|
22376
|
+
return void 0;
|
|
22377
|
+
}
|
|
22378
|
+
const envelope = payload;
|
|
22379
|
+
if (!envelope.error || typeof envelope.error.code !== "string") {
|
|
22380
|
+
return void 0;
|
|
22381
|
+
}
|
|
22382
|
+
const code = envelope.error.code.trim();
|
|
22383
|
+
return code.length > 0 ? code : void 0;
|
|
22384
|
+
}
|
|
22385
|
+
function extractErrorMessage(payload) {
|
|
22386
|
+
if (!isRecord9(payload)) {
|
|
22387
|
+
return void 0;
|
|
22388
|
+
}
|
|
22389
|
+
const envelope = payload;
|
|
22390
|
+
if (!envelope.error || typeof envelope.error.message !== "string") {
|
|
22391
|
+
return void 0;
|
|
22392
|
+
}
|
|
22393
|
+
const message2 = envelope.error.message.trim();
|
|
22394
|
+
return message2.length > 0 ? message2 : void 0;
|
|
22395
|
+
}
|
|
22396
|
+
async function parseJsonResponse5(response) {
|
|
22397
|
+
try {
|
|
22398
|
+
return await response.json();
|
|
22399
|
+
} catch {
|
|
22400
|
+
return void 0;
|
|
22401
|
+
}
|
|
22402
|
+
}
|
|
22403
|
+
async function executePairRequest(input) {
|
|
22404
|
+
try {
|
|
22405
|
+
return await input.fetchImpl(input.url, input.init);
|
|
22406
|
+
} catch {
|
|
22407
|
+
throw createCliError6(
|
|
22408
|
+
"CLI_PAIR_REQUEST_FAILED",
|
|
22409
|
+
"Unable to connect to proxy URL. Check network access and proxyUrl."
|
|
22410
|
+
);
|
|
22411
|
+
}
|
|
22412
|
+
}
|
|
22413
|
+
function mapStartPairError(status, payload) {
|
|
22414
|
+
const code = extractErrorCode(payload);
|
|
22415
|
+
const message2 = extractErrorMessage(payload);
|
|
22416
|
+
if (code === "PROXY_PAIR_OWNER_PAT_INVALID" || status === 401) {
|
|
22417
|
+
return message2 ? `Owner PAT is invalid (401): ${message2}` : "Owner PAT is invalid or expired (401).";
|
|
22418
|
+
}
|
|
22419
|
+
if (code === "PROXY_PAIR_OWNER_PAT_FORBIDDEN" || status === 403) {
|
|
22420
|
+
return message2 ? `Owner PAT does not control initiator agent DID (403): ${message2}` : "Owner PAT does not control initiator agent DID (403).";
|
|
22421
|
+
}
|
|
22422
|
+
if (status === 400) {
|
|
22423
|
+
return message2 ? `Pair start request is invalid (400): ${message2}` : "Pair start request is invalid (400).";
|
|
22424
|
+
}
|
|
22425
|
+
if (status >= 500) {
|
|
22426
|
+
return `Proxy pairing service is unavailable (${status}).`;
|
|
22427
|
+
}
|
|
22428
|
+
if (message2) {
|
|
22429
|
+
return `Pair start failed (${status}): ${message2}`;
|
|
22430
|
+
}
|
|
22431
|
+
return `Pair start failed (${status})`;
|
|
22432
|
+
}
|
|
22433
|
+
function mapConfirmPairError(status, payload) {
|
|
22434
|
+
const code = extractErrorCode(payload);
|
|
22435
|
+
const message2 = extractErrorMessage(payload);
|
|
22436
|
+
if (code === "PROXY_PAIR_TICKET_NOT_FOUND" || status === 404) {
|
|
22437
|
+
return "Pairing ticket is invalid or expired";
|
|
22438
|
+
}
|
|
22439
|
+
if (code === "PROXY_PAIR_TICKET_EXPIRED" || status === 410) {
|
|
22440
|
+
return "Pairing ticket has expired";
|
|
22441
|
+
}
|
|
22442
|
+
if (status === 400) {
|
|
22443
|
+
return message2 ? `Pair confirm request is invalid (400): ${message2}` : "Pair confirm request is invalid (400).";
|
|
22444
|
+
}
|
|
22445
|
+
if (status >= 500) {
|
|
22446
|
+
return `Proxy pairing service is unavailable (${status}).`;
|
|
22447
|
+
}
|
|
22448
|
+
if (message2) {
|
|
22449
|
+
return `Pair confirm failed (${status}): ${message2}`;
|
|
22450
|
+
}
|
|
22451
|
+
return `Pair confirm failed (${status})`;
|
|
22452
|
+
}
|
|
22453
|
+
function parsePairStartResponse(payload) {
|
|
22454
|
+
if (!isRecord9(payload)) {
|
|
22455
|
+
throw createCliError6(
|
|
22456
|
+
"CLI_PAIR_START_INVALID_RESPONSE",
|
|
22457
|
+
"Pair start response is invalid"
|
|
22458
|
+
);
|
|
22459
|
+
}
|
|
22460
|
+
const ticket = parsePairingTicket(payload.ticket);
|
|
22461
|
+
const initiatorAgentDid = parseNonEmptyString8(payload.initiatorAgentDid);
|
|
22462
|
+
const expiresAt = parseNonEmptyString8(payload.expiresAt);
|
|
22463
|
+
if (initiatorAgentDid.length === 0 || expiresAt.length === 0) {
|
|
22464
|
+
throw createCliError6(
|
|
22465
|
+
"CLI_PAIR_START_INVALID_RESPONSE",
|
|
22466
|
+
"Pair start response is invalid"
|
|
22467
|
+
);
|
|
22468
|
+
}
|
|
22469
|
+
return {
|
|
22470
|
+
ticket,
|
|
22471
|
+
initiatorAgentDid,
|
|
22472
|
+
expiresAt
|
|
22473
|
+
};
|
|
22474
|
+
}
|
|
22475
|
+
function parsePairConfirmResponse(payload) {
|
|
22476
|
+
if (!isRecord9(payload)) {
|
|
22477
|
+
throw createCliError6(
|
|
22478
|
+
"CLI_PAIR_CONFIRM_INVALID_RESPONSE",
|
|
22479
|
+
"Pair confirm response is invalid"
|
|
22480
|
+
);
|
|
22481
|
+
}
|
|
22482
|
+
const paired = payload.paired === true;
|
|
22483
|
+
const initiatorAgentDid = parseNonEmptyString8(payload.initiatorAgentDid);
|
|
22484
|
+
const responderAgentDid = parseNonEmptyString8(payload.responderAgentDid);
|
|
22485
|
+
if (!paired || initiatorAgentDid.length === 0 || responderAgentDid.length === 0) {
|
|
22486
|
+
throw createCliError6(
|
|
22487
|
+
"CLI_PAIR_CONFIRM_INVALID_RESPONSE",
|
|
22488
|
+
"Pair confirm response is invalid"
|
|
22489
|
+
);
|
|
22490
|
+
}
|
|
22491
|
+
return {
|
|
22492
|
+
paired,
|
|
22493
|
+
initiatorAgentDid,
|
|
22494
|
+
responderAgentDid
|
|
22495
|
+
};
|
|
22496
|
+
}
|
|
22497
|
+
async function readAgentProofMaterial(agentName, dependencies) {
|
|
22498
|
+
const readFileImpl = dependencies.readFileImpl ?? readFile6;
|
|
22499
|
+
const getConfigDirImpl = dependencies.getConfigDirImpl ?? getConfigDir;
|
|
22500
|
+
const normalizedAgentName = assertValidAgentName(agentName);
|
|
22501
|
+
const agentDir = join8(
|
|
22502
|
+
getConfigDirImpl(),
|
|
22503
|
+
AGENTS_DIR_NAME5,
|
|
22504
|
+
normalizedAgentName
|
|
22505
|
+
);
|
|
22506
|
+
const aitPath = join8(agentDir, AIT_FILE_NAME4);
|
|
22507
|
+
const secretKeyPath = join8(agentDir, SECRET_KEY_FILE_NAME3);
|
|
22508
|
+
let ait;
|
|
22509
|
+
try {
|
|
22510
|
+
ait = (await readFileImpl(aitPath, "utf-8")).trim();
|
|
22511
|
+
} catch (error48) {
|
|
22512
|
+
const nodeError = error48;
|
|
22513
|
+
if (nodeError.code === "ENOENT") {
|
|
22514
|
+
throw createCliError6(
|
|
22515
|
+
"CLI_PAIR_AGENT_NOT_FOUND",
|
|
22516
|
+
`Agent "${normalizedAgentName}" is missing ${AIT_FILE_NAME4}. Run agent create first.`
|
|
22517
|
+
);
|
|
22518
|
+
}
|
|
22519
|
+
throw error48;
|
|
22520
|
+
}
|
|
22521
|
+
if (ait.length === 0) {
|
|
22522
|
+
throw createCliError6(
|
|
22523
|
+
"CLI_PAIR_AGENT_NOT_FOUND",
|
|
22524
|
+
`Agent "${normalizedAgentName}" has an empty ${AIT_FILE_NAME4}`
|
|
22525
|
+
);
|
|
22526
|
+
}
|
|
22527
|
+
let encodedSecretKey;
|
|
22528
|
+
try {
|
|
22529
|
+
encodedSecretKey = (await readFileImpl(secretKeyPath, "utf-8")).trim();
|
|
22530
|
+
} catch (error48) {
|
|
22531
|
+
const nodeError = error48;
|
|
22532
|
+
if (nodeError.code === "ENOENT") {
|
|
22533
|
+
throw createCliError6(
|
|
22534
|
+
"CLI_PAIR_AGENT_NOT_FOUND",
|
|
22535
|
+
`Agent "${normalizedAgentName}" is missing ${SECRET_KEY_FILE_NAME3}. Run agent create first.`
|
|
22536
|
+
);
|
|
22537
|
+
}
|
|
22538
|
+
throw error48;
|
|
22539
|
+
}
|
|
22540
|
+
if (encodedSecretKey.length === 0) {
|
|
22541
|
+
throw createCliError6(
|
|
22542
|
+
"CLI_PAIR_AGENT_NOT_FOUND",
|
|
22543
|
+
`Agent "${normalizedAgentName}" has an empty ${SECRET_KEY_FILE_NAME3}`
|
|
22544
|
+
);
|
|
22545
|
+
}
|
|
22546
|
+
let secretKey;
|
|
22547
|
+
try {
|
|
22548
|
+
secretKey = decodeBase64url(encodedSecretKey);
|
|
22549
|
+
} catch {
|
|
22550
|
+
throw createCliError6(
|
|
22551
|
+
"CLI_PAIR_AGENT_NOT_FOUND",
|
|
22552
|
+
`Agent "${normalizedAgentName}" has invalid ${SECRET_KEY_FILE_NAME3}`
|
|
22553
|
+
);
|
|
22554
|
+
}
|
|
22555
|
+
return {
|
|
22556
|
+
ait,
|
|
22557
|
+
secretKey
|
|
22558
|
+
};
|
|
22559
|
+
}
|
|
22560
|
+
function resolveOwnerPat(options) {
|
|
22561
|
+
const ownerPat = parseNonEmptyString8(options.explicitOwnerPat) || parseNonEmptyString8(options.config.apiKey);
|
|
22562
|
+
if (ownerPat.length > 0) {
|
|
22563
|
+
return ownerPat;
|
|
22564
|
+
}
|
|
22565
|
+
throw createCliError6(
|
|
22566
|
+
"CLI_PAIR_START_OWNER_PAT_REQUIRED",
|
|
22567
|
+
"Owner PAT is required. Pass --owner-pat <token> or configure API key with `clawdentity invite redeem` / `clawdentity config set apiKey <token>`."
|
|
22568
|
+
);
|
|
22569
|
+
}
|
|
22570
|
+
async function buildSignedHeaders(input) {
|
|
22571
|
+
const signed = await signHttpRequest({
|
|
22572
|
+
method: input.method,
|
|
22573
|
+
pathWithQuery: toPathWithQuery3(input.requestUrl),
|
|
22574
|
+
timestamp: String(input.timestampSeconds),
|
|
22575
|
+
nonce: input.nonce,
|
|
22576
|
+
body: input.bodyBytes,
|
|
22577
|
+
secretKey: input.secretKey
|
|
22578
|
+
});
|
|
22579
|
+
return signed.headers;
|
|
22580
|
+
}
|
|
22581
|
+
async function encodeTicketQrPng(ticket) {
|
|
22582
|
+
const buffer = await QRCode.toBuffer(ticket, {
|
|
22583
|
+
type: "png",
|
|
22584
|
+
width: 512,
|
|
22585
|
+
margin: 2,
|
|
22586
|
+
errorCorrectionLevel: "M"
|
|
22587
|
+
});
|
|
22588
|
+
return new Uint8Array(buffer);
|
|
22589
|
+
}
|
|
22590
|
+
function decodeTicketFromPng(imageBytes) {
|
|
22591
|
+
let decodedPng;
|
|
22592
|
+
try {
|
|
22593
|
+
decodedPng = PNG.sync.read(Buffer.from(imageBytes));
|
|
22594
|
+
} catch {
|
|
22595
|
+
throw createCliError6(
|
|
22596
|
+
"CLI_PAIR_CONFIRM_QR_FILE_INVALID",
|
|
22597
|
+
"QR image file is invalid or unsupported"
|
|
22598
|
+
);
|
|
22599
|
+
}
|
|
22600
|
+
const imageData = new Uint8ClampedArray(
|
|
22601
|
+
decodedPng.data.buffer,
|
|
22602
|
+
decodedPng.data.byteOffset,
|
|
22603
|
+
decodedPng.data.byteLength
|
|
22604
|
+
);
|
|
22605
|
+
const decoded = jsQR(imageData, decodedPng.width, decodedPng.height);
|
|
22606
|
+
if (!decoded || parseNonEmptyString8(decoded.data).length === 0) {
|
|
22607
|
+
throw createCliError6(
|
|
22608
|
+
"CLI_PAIR_CONFIRM_QR_NOT_FOUND",
|
|
22609
|
+
"No pairing QR code was found in the image"
|
|
22610
|
+
);
|
|
22611
|
+
}
|
|
22612
|
+
return parsePairingTicket(decoded.data);
|
|
22613
|
+
}
|
|
22614
|
+
async function persistPairingQr(input) {
|
|
22615
|
+
const mkdirImpl = input.dependencies.mkdirImpl ?? mkdir6;
|
|
22616
|
+
const readdirImpl = input.dependencies.readdirImpl ?? readdir;
|
|
22617
|
+
const unlinkImpl = input.dependencies.unlinkImpl ?? unlink2;
|
|
22618
|
+
const writeFileImpl = input.dependencies.writeFileImpl ?? writeFile6;
|
|
22619
|
+
const getConfigDirImpl = input.dependencies.getConfigDirImpl ?? getConfigDir;
|
|
22620
|
+
const qrEncodeImpl = input.dependencies.qrEncodeImpl ?? encodeTicketQrPng;
|
|
22621
|
+
const baseDir = join8(getConfigDirImpl(), PAIRING_QR_DIR_NAME);
|
|
22622
|
+
const outputPath = parseNonEmptyString8(input.qrOutput) ? resolve(input.qrOutput ?? "") : join8(
|
|
22623
|
+
baseDir,
|
|
22624
|
+
`${assertValidAgentName(input.agentName)}-pair-${input.nowSeconds}.png`
|
|
22625
|
+
);
|
|
22626
|
+
const existingFiles = await readdirImpl(baseDir).catch((error48) => {
|
|
22627
|
+
const nodeError = error48;
|
|
22628
|
+
if (nodeError.code === "ENOENT") {
|
|
22629
|
+
return [];
|
|
22630
|
+
}
|
|
22631
|
+
throw error48;
|
|
22632
|
+
});
|
|
22633
|
+
for (const fileName of existingFiles) {
|
|
22634
|
+
if (typeof fileName !== "string") {
|
|
22635
|
+
continue;
|
|
22636
|
+
}
|
|
22637
|
+
const match2 = PAIRING_QR_FILENAME_PATTERN.exec(fileName);
|
|
22638
|
+
if (!match2) {
|
|
22639
|
+
continue;
|
|
22640
|
+
}
|
|
22641
|
+
const issuedAtSeconds = Number.parseInt(match2[1] ?? "", 10);
|
|
22642
|
+
if (!Number.isInteger(issuedAtSeconds)) {
|
|
22643
|
+
continue;
|
|
22644
|
+
}
|
|
22645
|
+
if (issuedAtSeconds + PAIRING_QR_MAX_AGE_SECONDS > input.nowSeconds) {
|
|
22646
|
+
continue;
|
|
22647
|
+
}
|
|
22648
|
+
const stalePath = join8(baseDir, fileName);
|
|
22649
|
+
await unlinkImpl(stalePath).catch((error48) => {
|
|
22650
|
+
const nodeError = error48;
|
|
22651
|
+
if (nodeError.code === "ENOENT") {
|
|
22652
|
+
return;
|
|
22653
|
+
}
|
|
22654
|
+
throw error48;
|
|
22655
|
+
});
|
|
22656
|
+
}
|
|
22657
|
+
await mkdirImpl(dirname5(outputPath), { recursive: true });
|
|
22658
|
+
const imageBytes = await qrEncodeImpl(input.ticket);
|
|
22659
|
+
await writeFileImpl(outputPath, imageBytes);
|
|
22660
|
+
return outputPath;
|
|
22661
|
+
}
|
|
22662
|
+
function resolveConfirmTicketSource(options) {
|
|
22663
|
+
const inlineTicket = parseNonEmptyString8(options.ticket);
|
|
22664
|
+
const qrFile = parseNonEmptyString8(options.qrFile);
|
|
22665
|
+
if (inlineTicket.length > 0 && qrFile.length > 0) {
|
|
22666
|
+
throw createCliError6(
|
|
22667
|
+
"CLI_PAIR_CONFIRM_INPUT_CONFLICT",
|
|
22668
|
+
"Provide either --ticket or --qr-file, not both"
|
|
22669
|
+
);
|
|
22670
|
+
}
|
|
22671
|
+
if (inlineTicket.length > 0) {
|
|
22672
|
+
return {
|
|
22673
|
+
ticket: parsePairingTicket(inlineTicket),
|
|
22674
|
+
source: "ticket"
|
|
22675
|
+
};
|
|
22676
|
+
}
|
|
22677
|
+
if (qrFile.length > 0) {
|
|
22678
|
+
return {
|
|
22679
|
+
ticket: "",
|
|
22680
|
+
source: "qr-file",
|
|
22681
|
+
qrFilePath: resolve(qrFile)
|
|
22682
|
+
};
|
|
22683
|
+
}
|
|
22684
|
+
throw createCliError6(
|
|
22685
|
+
"CLI_PAIR_CONFIRM_TICKET_REQUIRED",
|
|
22686
|
+
"Pairing ticket is required. Pass --ticket <clwpair1_...> or --qr-file <path>."
|
|
22687
|
+
);
|
|
22688
|
+
}
|
|
22689
|
+
async function startPairing(agentName, options, dependencies = {}) {
|
|
22690
|
+
const fetchImpl = dependencies.fetchImpl ?? fetch;
|
|
22691
|
+
const resolveConfigImpl = dependencies.resolveConfigImpl ?? resolveConfig;
|
|
22692
|
+
const nowSecondsImpl = dependencies.nowSecondsImpl ?? (() => Math.floor(Date.now() / 1e3));
|
|
22693
|
+
const nonceFactoryImpl = dependencies.nonceFactoryImpl ?? (() => randomBytes3(NONCE_SIZE2).toString("base64url"));
|
|
22694
|
+
const ttlSeconds = parseTtlSeconds(options.ttlSeconds);
|
|
22695
|
+
const proxyUrl = resolveProxyUrl(options.proxyUrl);
|
|
22696
|
+
const config2 = await resolveConfigImpl();
|
|
22697
|
+
const ownerPat = resolveOwnerPat({
|
|
22698
|
+
explicitOwnerPat: options.ownerPat,
|
|
22699
|
+
config: config2
|
|
22700
|
+
});
|
|
22701
|
+
const { ait, secretKey } = await readAgentProofMaterial(
|
|
22702
|
+
agentName,
|
|
22703
|
+
dependencies
|
|
22704
|
+
);
|
|
22705
|
+
const requestUrl = toProxyRequestUrl(proxyUrl, PAIR_START_PATH);
|
|
22706
|
+
const requestBody = JSON.stringify({
|
|
22707
|
+
ttlSeconds
|
|
22708
|
+
});
|
|
22709
|
+
const bodyBytes = new TextEncoder().encode(requestBody);
|
|
22710
|
+
const timestampSeconds = nowSecondsImpl();
|
|
22711
|
+
const nonce = nonceFactoryImpl();
|
|
22712
|
+
const signedHeaders = await buildSignedHeaders({
|
|
22713
|
+
method: "POST",
|
|
22714
|
+
requestUrl,
|
|
22715
|
+
bodyBytes,
|
|
22716
|
+
secretKey,
|
|
22717
|
+
timestampSeconds,
|
|
22718
|
+
nonce
|
|
22719
|
+
});
|
|
22720
|
+
const response = await executePairRequest({
|
|
22721
|
+
fetchImpl,
|
|
22722
|
+
url: requestUrl,
|
|
22723
|
+
init: {
|
|
22724
|
+
method: "POST",
|
|
22725
|
+
headers: {
|
|
22726
|
+
authorization: `Claw ${ait}`,
|
|
22727
|
+
"content-type": "application/json",
|
|
22728
|
+
[OWNER_PAT_HEADER]: ownerPat,
|
|
22729
|
+
...signedHeaders
|
|
22730
|
+
},
|
|
22731
|
+
body: requestBody
|
|
22732
|
+
}
|
|
22733
|
+
});
|
|
22734
|
+
const responseBody = await parseJsonResponse5(response);
|
|
22735
|
+
if (!response.ok) {
|
|
22736
|
+
throw createCliError6(
|
|
22737
|
+
"CLI_PAIR_START_FAILED",
|
|
22738
|
+
mapStartPairError(response.status, responseBody)
|
|
22739
|
+
);
|
|
22740
|
+
}
|
|
22741
|
+
const parsed = parsePairStartResponse(responseBody);
|
|
22742
|
+
const result = {
|
|
22743
|
+
...parsed,
|
|
22744
|
+
proxyUrl
|
|
22745
|
+
};
|
|
22746
|
+
if (options.qr === true) {
|
|
22747
|
+
result.qrPath = await persistPairingQr({
|
|
22748
|
+
agentName,
|
|
22749
|
+
qrOutput: options.qrOutput,
|
|
22750
|
+
ticket: parsed.ticket,
|
|
22751
|
+
dependencies,
|
|
22752
|
+
nowSeconds: timestampSeconds
|
|
22753
|
+
});
|
|
22754
|
+
}
|
|
22755
|
+
return result;
|
|
22756
|
+
}
|
|
22757
|
+
async function confirmPairing(agentName, options, dependencies = {}) {
|
|
22758
|
+
const fetchImpl = dependencies.fetchImpl ?? fetch;
|
|
22759
|
+
const nowSecondsImpl = dependencies.nowSecondsImpl ?? (() => Math.floor(Date.now() / 1e3));
|
|
22760
|
+
const nonceFactoryImpl = dependencies.nonceFactoryImpl ?? (() => randomBytes3(NONCE_SIZE2).toString("base64url"));
|
|
22761
|
+
const readFileImpl = dependencies.readFileImpl ?? readFile6;
|
|
22762
|
+
const qrDecodeImpl = dependencies.qrDecodeImpl ?? decodeTicketFromPng;
|
|
22763
|
+
const ticketSource = resolveConfirmTicketSource(options);
|
|
22764
|
+
const proxyUrl = resolveProxyUrl(options.proxyUrl);
|
|
22765
|
+
let ticket = ticketSource.ticket;
|
|
22766
|
+
if (ticketSource.source === "qr-file") {
|
|
22767
|
+
if (!ticketSource.qrFilePath) {
|
|
22768
|
+
throw createCliError6(
|
|
22769
|
+
"CLI_PAIR_CONFIRM_QR_FILE_REQUIRED",
|
|
22770
|
+
"QR file path is required"
|
|
22771
|
+
);
|
|
22772
|
+
}
|
|
22773
|
+
let imageBytes;
|
|
22774
|
+
try {
|
|
22775
|
+
imageBytes = await readFileImpl(ticketSource.qrFilePath);
|
|
22776
|
+
} catch (error48) {
|
|
22777
|
+
const nodeError = error48;
|
|
22778
|
+
if (nodeError.code === "ENOENT") {
|
|
22779
|
+
throw createCliError6(
|
|
22780
|
+
"CLI_PAIR_CONFIRM_QR_FILE_NOT_FOUND",
|
|
22781
|
+
`QR file not found: ${ticketSource.qrFilePath}`
|
|
22782
|
+
);
|
|
22783
|
+
}
|
|
22784
|
+
throw error48;
|
|
22785
|
+
}
|
|
22786
|
+
ticket = parsePairingTicket(qrDecodeImpl(new Uint8Array(imageBytes)));
|
|
22787
|
+
}
|
|
22788
|
+
const { ait, secretKey } = await readAgentProofMaterial(
|
|
22789
|
+
agentName,
|
|
22790
|
+
dependencies
|
|
22791
|
+
);
|
|
22792
|
+
const requestUrl = toProxyRequestUrl(proxyUrl, PAIR_CONFIRM_PATH);
|
|
22793
|
+
const requestBody = JSON.stringify({ ticket });
|
|
22794
|
+
const bodyBytes = new TextEncoder().encode(requestBody);
|
|
22795
|
+
const timestampSeconds = nowSecondsImpl();
|
|
22796
|
+
const nonce = nonceFactoryImpl();
|
|
22797
|
+
const signedHeaders = await buildSignedHeaders({
|
|
22798
|
+
method: "POST",
|
|
22799
|
+
requestUrl,
|
|
22800
|
+
bodyBytes,
|
|
22801
|
+
secretKey,
|
|
22802
|
+
timestampSeconds,
|
|
22803
|
+
nonce
|
|
22804
|
+
});
|
|
22805
|
+
const response = await executePairRequest({
|
|
22806
|
+
fetchImpl,
|
|
22807
|
+
url: requestUrl,
|
|
22808
|
+
init: {
|
|
22809
|
+
method: "POST",
|
|
22810
|
+
headers: {
|
|
22811
|
+
authorization: `Claw ${ait}`,
|
|
22812
|
+
"content-type": "application/json",
|
|
22813
|
+
...signedHeaders
|
|
22814
|
+
},
|
|
22815
|
+
body: requestBody
|
|
22816
|
+
}
|
|
22817
|
+
});
|
|
22818
|
+
const responseBody = await parseJsonResponse5(response);
|
|
22819
|
+
if (!response.ok) {
|
|
22820
|
+
throw createCliError6(
|
|
22821
|
+
"CLI_PAIR_CONFIRM_FAILED",
|
|
22822
|
+
mapConfirmPairError(response.status, responseBody)
|
|
22823
|
+
);
|
|
22824
|
+
}
|
|
22825
|
+
const parsed = parsePairConfirmResponse(responseBody);
|
|
22826
|
+
if (ticketSource.source === "qr-file" && ticketSource.qrFilePath) {
|
|
22827
|
+
const unlinkImpl = dependencies.unlinkImpl ?? unlink2;
|
|
22828
|
+
await unlinkImpl(ticketSource.qrFilePath).catch((error48) => {
|
|
22829
|
+
const nodeError = error48;
|
|
22830
|
+
if (nodeError.code === "ENOENT") {
|
|
22831
|
+
return;
|
|
22832
|
+
}
|
|
22833
|
+
logger9.warn("cli.pair.confirm.qr_cleanup_failed", {
|
|
22834
|
+
path: ticketSource.qrFilePath,
|
|
22835
|
+
reason: error48 instanceof Error && error48.message.length > 0 ? error48.message : "unknown"
|
|
22836
|
+
});
|
|
22837
|
+
});
|
|
22838
|
+
}
|
|
22839
|
+
return {
|
|
22840
|
+
...parsed,
|
|
22841
|
+
proxyUrl
|
|
22842
|
+
};
|
|
22843
|
+
}
|
|
22844
|
+
var createPairCommand = (dependencies = {}) => {
|
|
22845
|
+
const pairCommand = new Command8("pair").description(
|
|
22846
|
+
"Manage proxy trust pairing between agents"
|
|
22847
|
+
);
|
|
22848
|
+
pairCommand.command("start <agentName>").description("Start pairing and issue one-time pairing ticket").option(
|
|
22849
|
+
"--proxy-url <url>",
|
|
22850
|
+
"Initiator proxy base URL (or set CLAWDENTITY_PROXY_URL)"
|
|
22851
|
+
).option(
|
|
22852
|
+
"--owner-pat <token>",
|
|
22853
|
+
"Owner PAT override (defaults to configured API key)"
|
|
22854
|
+
).option("--ttl-seconds <seconds>", "Pairing ticket expiry in seconds").option("--qr", "Generate a local QR file for sharing").option("--qr-output <path>", "Write QR PNG to a specific file path").action(
|
|
22855
|
+
withErrorHandling(
|
|
22856
|
+
"pair start",
|
|
22857
|
+
async (agentName, options) => {
|
|
22858
|
+
const result = await startPairing(agentName, options, dependencies);
|
|
22859
|
+
logger9.info("cli.pair_started", {
|
|
22860
|
+
initiatorAgentDid: result.initiatorAgentDid,
|
|
22861
|
+
proxyUrl: result.proxyUrl,
|
|
22862
|
+
expiresAt: result.expiresAt,
|
|
22863
|
+
qrPath: result.qrPath
|
|
22864
|
+
});
|
|
22865
|
+
writeStdoutLine("Pairing ticket created");
|
|
22866
|
+
writeStdoutLine(`Ticket: ${result.ticket}`);
|
|
22867
|
+
writeStdoutLine(`Initiator Agent DID: ${result.initiatorAgentDid}`);
|
|
22868
|
+
writeStdoutLine(`Expires At: ${result.expiresAt}`);
|
|
22869
|
+
if (result.qrPath) {
|
|
22870
|
+
writeStdoutLine(`QR File: ${result.qrPath}`);
|
|
22871
|
+
}
|
|
22872
|
+
}
|
|
22873
|
+
)
|
|
22874
|
+
);
|
|
22875
|
+
pairCommand.command("confirm <agentName>").description("Confirm pairing using one-time pairing ticket").option("--ticket <ticket>", "One-time pairing ticket (clwpair1_...)").option("--qr-file <path>", "Path to pairing QR PNG file").option(
|
|
22876
|
+
"--proxy-url <url>",
|
|
22877
|
+
"Responder proxy base URL (or set CLAWDENTITY_PROXY_URL)"
|
|
22878
|
+
).action(
|
|
22879
|
+
withErrorHandling(
|
|
22880
|
+
"pair confirm",
|
|
22881
|
+
async (agentName, options) => {
|
|
22882
|
+
const result = await confirmPairing(agentName, options, dependencies);
|
|
22883
|
+
logger9.info("cli.pair_confirmed", {
|
|
22884
|
+
initiatorAgentDid: result.initiatorAgentDid,
|
|
22885
|
+
responderAgentDid: result.responderAgentDid,
|
|
22886
|
+
proxyUrl: result.proxyUrl
|
|
22887
|
+
});
|
|
22888
|
+
writeStdoutLine("Pairing confirmed");
|
|
22889
|
+
writeStdoutLine(`Initiator Agent DID: ${result.initiatorAgentDid}`);
|
|
22890
|
+
writeStdoutLine(`Responder Agent DID: ${result.responderAgentDid}`);
|
|
22891
|
+
writeStdoutLine(`Paired: ${result.paired ? "true" : "false"}`);
|
|
22892
|
+
}
|
|
22893
|
+
)
|
|
22894
|
+
);
|
|
22895
|
+
return pairCommand;
|
|
22896
|
+
};
|
|
22897
|
+
|
|
22898
|
+
// src/commands/verify.ts
|
|
22899
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
22900
|
+
import { Command as Command9 } from "commander";
|
|
22901
|
+
var logger10 = createLogger({ service: "cli", module: "verify" });
|
|
21801
22902
|
var REGISTRY_KEYS_CACHE_FILE = "registry-keys.json";
|
|
21802
22903
|
var CRL_CLAIMS_CACHE_FILE = "crl-claims.json";
|
|
21803
22904
|
var REGISTRY_KEYS_CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
@@ -21808,7 +22909,7 @@ var VerifyCommandError = class extends Error {
|
|
|
21808
22909
|
this.name = "VerifyCommandError";
|
|
21809
22910
|
}
|
|
21810
22911
|
};
|
|
21811
|
-
var
|
|
22912
|
+
var isRecord10 = (value) => {
|
|
21812
22913
|
return typeof value === "object" && value !== null;
|
|
21813
22914
|
};
|
|
21814
22915
|
var normalizeRegistryUrl = (registryUrl) => {
|
|
@@ -21844,7 +22945,7 @@ var resolveToken = async (tokenOrFile) => {
|
|
|
21844
22945
|
throw new VerifyCommandError("invalid token (value is empty)");
|
|
21845
22946
|
}
|
|
21846
22947
|
try {
|
|
21847
|
-
const fileContents = await
|
|
22948
|
+
const fileContents = await readFile7(input, "utf-8");
|
|
21848
22949
|
const token = fileContents.trim();
|
|
21849
22950
|
if (token.length === 0) {
|
|
21850
22951
|
throw new VerifyCommandError(`invalid token (${input} is empty)`);
|
|
@@ -21876,7 +22977,7 @@ var parseResponseJson = async (response) => {
|
|
|
21876
22977
|
}
|
|
21877
22978
|
};
|
|
21878
22979
|
var parseSigningKeys = (payload) => {
|
|
21879
|
-
if (!
|
|
22980
|
+
if (!isRecord10(payload) || !Array.isArray(payload.keys)) {
|
|
21880
22981
|
throw new VerifyCommandError(
|
|
21881
22982
|
"verification keys unavailable (response payload is invalid)"
|
|
21882
22983
|
);
|
|
@@ -21895,7 +22996,7 @@ var parseSigningKeys = (payload) => {
|
|
|
21895
22996
|
};
|
|
21896
22997
|
var parseRegistryKeysCache = (rawCache) => {
|
|
21897
22998
|
const parsed = parseJson(rawCache);
|
|
21898
|
-
if (!
|
|
22999
|
+
if (!isRecord10(parsed)) {
|
|
21899
23000
|
return void 0;
|
|
21900
23001
|
}
|
|
21901
23002
|
const { registryUrl, fetchedAtMs, keys } = parsed;
|
|
@@ -21921,7 +23022,7 @@ var parseRegistryKeysCache = (rawCache) => {
|
|
|
21921
23022
|
};
|
|
21922
23023
|
var parseCrlCache = (rawCache) => {
|
|
21923
23024
|
const parsed = parseJson(rawCache);
|
|
21924
|
-
if (!
|
|
23025
|
+
if (!isRecord10(parsed)) {
|
|
21925
23026
|
return void 0;
|
|
21926
23027
|
}
|
|
21927
23028
|
const { registryUrl, fetchedAtMs, claims } = parsed;
|
|
@@ -22019,7 +23120,7 @@ var fetchCrlClaims = async (input) => {
|
|
|
22019
23120
|
);
|
|
22020
23121
|
}
|
|
22021
23122
|
const payload = await parseResponseJson(response);
|
|
22022
|
-
if (!
|
|
23123
|
+
if (!isRecord10(payload) || typeof payload.crl !== "string") {
|
|
22023
23124
|
throw new VerifyCommandError(
|
|
22024
23125
|
"revocation check unavailable (response payload is invalid)"
|
|
22025
23126
|
);
|
|
@@ -22064,7 +23165,7 @@ var loadCrlClaims = async (input) => {
|
|
|
22064
23165
|
return claims;
|
|
22065
23166
|
};
|
|
22066
23167
|
var toInvalidTokenReason = (error48) => {
|
|
22067
|
-
if (
|
|
23168
|
+
if (isRecord10(error48) && typeof error48.message === "string") {
|
|
22068
23169
|
return `invalid token (${error48.message})`;
|
|
22069
23170
|
}
|
|
22070
23171
|
if (error48 instanceof Error && error48.message.length > 0) {
|
|
@@ -22132,7 +23233,7 @@ var runVerify = async (tokenOrFile) => {
|
|
|
22132
23233
|
printResult(false, "revoked");
|
|
22133
23234
|
return;
|
|
22134
23235
|
}
|
|
22135
|
-
|
|
23236
|
+
logger10.info("cli.verify.success", {
|
|
22136
23237
|
did: claims.sub,
|
|
22137
23238
|
jti: claims.jti,
|
|
22138
23239
|
issuer: claims.iss
|
|
@@ -22140,7 +23241,7 @@ var runVerify = async (tokenOrFile) => {
|
|
|
22140
23241
|
printResult(true, `token verified (${claims.sub})`);
|
|
22141
23242
|
};
|
|
22142
23243
|
var createVerifyCommand = () => {
|
|
22143
|
-
return new
|
|
23244
|
+
return new Command9("verify").description("Verify an AIT using registry keys and CRL state").argument(
|
|
22144
23245
|
"<tokenOrFile>",
|
|
22145
23246
|
"Raw AIT token or file path containing the token"
|
|
22146
23247
|
).action(
|
|
@@ -22161,15 +23262,15 @@ var resolveCliVersion = () => {
|
|
|
22161
23262
|
};
|
|
22162
23263
|
var CLI_VERSION = resolveCliVersion();
|
|
22163
23264
|
var createProgram = () => {
|
|
22164
|
-
return new
|
|
23265
|
+
return new Command10("clawdentity").description("Clawdentity CLI - Agent identity management").version(CLI_VERSION).addCommand(createAdminCommand()).addCommand(createAgentCommand()).addCommand(createApiKeyCommand()).addCommand(createConnectorCommand()).addCommand(createConfigCommand()).addCommand(createInviteCommand()).addCommand(createOpenclawCommand()).addCommand(createPairCommand()).addCommand(createVerifyCommand());
|
|
22165
23266
|
};
|
|
22166
23267
|
|
|
22167
23268
|
// src/bin.ts
|
|
22168
|
-
var
|
|
23269
|
+
var logger11 = createLogger({ service: "cli", module: "bin" });
|
|
22169
23270
|
createProgram().parseAsync(process.argv).catch((error48) => {
|
|
22170
23271
|
process.exitCode = 1;
|
|
22171
23272
|
const message2 = error48 instanceof Error ? error48.message : String(error48);
|
|
22172
|
-
|
|
23273
|
+
logger11.error("cli.execution_failed", { errorMessage: message2 });
|
|
22173
23274
|
writeStderrLine(message2);
|
|
22174
23275
|
});
|
|
22175
23276
|
/*! Bundled license information:
|