clawdentity 0.0.13 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js
CHANGED
|
@@ -15643,6 +15643,7 @@ var runtimeEnvironmentValues = [
|
|
|
15643
15643
|
// ../../packages/sdk/src/config.ts
|
|
15644
15644
|
var environmentSchema = external_exports.enum(runtimeEnvironmentValues);
|
|
15645
15645
|
var registrySigningKeyStatusSchema = external_exports.enum(["active", "revoked"]);
|
|
15646
|
+
var registryEventBusBackendSchema = external_exports.enum(["memory", "queue"]);
|
|
15646
15647
|
var ED25519_PUBLIC_KEY_LENGTH2 = 32;
|
|
15647
15648
|
var registrySigningPublicKeySchema = external_exports.object({
|
|
15648
15649
|
kid: external_exports.string().min(1),
|
|
@@ -15712,6 +15713,7 @@ var registryConfigSchema = external_exports.object({
|
|
|
15712
15713
|
ENVIRONMENT: environmentSchema,
|
|
15713
15714
|
APP_VERSION: external_exports.string().min(1).optional(),
|
|
15714
15715
|
PROXY_URL: external_exports.string().url().optional(),
|
|
15716
|
+
EVENT_BUS_BACKEND: registryEventBusBackendSchema.optional(),
|
|
15715
15717
|
BOOTSTRAP_SECRET: external_exports.string().min(1).optional(),
|
|
15716
15718
|
REGISTRY_SIGNING_KEY: external_exports.string().min(1).optional(),
|
|
15717
15719
|
REGISTRY_SIGNING_KEYS: registrySigningKeysEnvSchema.optional()
|
|
@@ -18671,6 +18673,7 @@ var DEFAULT_RECONNECT_BACKOFF_FACTOR = 2;
|
|
|
18671
18673
|
var DEFAULT_RECONNECT_JITTER_RATIO = 0.2;
|
|
18672
18674
|
var DEFAULT_CONNECTOR_BASE_URL = "http://127.0.0.1:19400";
|
|
18673
18675
|
var DEFAULT_CONNECTOR_OUTBOUND_PATH = "/v1/outbound";
|
|
18676
|
+
var DEFAULT_CONNECTOR_STATUS_PATH = "/v1/status";
|
|
18674
18677
|
var DEFAULT_RELAY_DELIVER_TIMEOUT_MS = 15e3;
|
|
18675
18678
|
var DEFAULT_OPENCLAW_DELIVER_RETRY_BUDGET_MS = DEFAULT_RELAY_DELIVER_TIMEOUT_MS - 1e3;
|
|
18676
18679
|
var AGENT_ACCESS_HEADER = "x-claw-agent-access";
|
|
@@ -19598,6 +19601,7 @@ async function startConnectorRuntime(input) {
|
|
|
19598
19601
|
});
|
|
19599
19602
|
const outboundBaseUrl = normalizeOutboundBaseUrl(input.outboundBaseUrl);
|
|
19600
19603
|
const outboundPath = normalizeOutboundPath(input.outboundPath);
|
|
19604
|
+
const statusPath = DEFAULT_CONNECTOR_STATUS_PATH;
|
|
19601
19605
|
const outboundUrl = new URL(outboundPath, outboundBaseUrl).toString();
|
|
19602
19606
|
const relayToPeer = async (request) => {
|
|
19603
19607
|
const peerUrl = new URL(request.peerProxyUrl);
|
|
@@ -19665,6 +19669,21 @@ async function startConnectorRuntime(input) {
|
|
|
19665
19669
|
};
|
|
19666
19670
|
const server = createServer(async (req, res) => {
|
|
19667
19671
|
const requestPath = req.url ? new URL(req.url, outboundBaseUrl).pathname : "/";
|
|
19672
|
+
if (requestPath === statusPath) {
|
|
19673
|
+
if (req.method !== "GET") {
|
|
19674
|
+
res.statusCode = 405;
|
|
19675
|
+
res.setHeader("allow", "GET");
|
|
19676
|
+
writeJson(res, 405, { error: "Method Not Allowed" });
|
|
19677
|
+
return;
|
|
19678
|
+
}
|
|
19679
|
+
writeJson(res, 200, {
|
|
19680
|
+
status: "ok",
|
|
19681
|
+
outboundUrl,
|
|
19682
|
+
websocketUrl: wsUrl,
|
|
19683
|
+
websocketConnected: connectorClient.isConnected()
|
|
19684
|
+
});
|
|
19685
|
+
return;
|
|
19686
|
+
}
|
|
19668
19687
|
if (requestPath !== outboundPath) {
|
|
19669
19688
|
writeJson(res, 404, { error: "Not Found" });
|
|
19670
19689
|
return;
|
|
@@ -20921,11 +20940,13 @@ var createInviteCommand = (dependencies = {}) => {
|
|
|
20921
20940
|
};
|
|
20922
20941
|
|
|
20923
20942
|
// src/commands/openclaw.ts
|
|
20943
|
+
import { spawn } from "child_process";
|
|
20924
20944
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
20925
20945
|
import { existsSync } from "fs";
|
|
20926
20946
|
import { chmod as chmod3, copyFile, mkdir as mkdir5, readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
20927
20947
|
import { homedir as homedir3 } from "os";
|
|
20928
20948
|
import { dirname as dirname4, join as join6, resolve as resolvePath } from "path";
|
|
20949
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
20929
20950
|
import { Command as Command7 } from "commander";
|
|
20930
20951
|
var logger8 = createLogger({ service: "cli", module: "openclaw" });
|
|
20931
20952
|
var CLAWDENTITY_DIR_NAME = ".clawdentity";
|
|
@@ -20960,10 +20981,13 @@ var DEFAULT_OPENCLAW_MAIN_SESSION_KEY = "main";
|
|
|
20960
20981
|
var DEFAULT_OPENCLAW_AGENT_ID = "main";
|
|
20961
20982
|
var DEFAULT_CONNECTOR_PORT = 19400;
|
|
20962
20983
|
var DEFAULT_CONNECTOR_OUTBOUND_PATH3 = "/v1/outbound";
|
|
20984
|
+
var DEFAULT_CONNECTOR_STATUS_PATH2 = "/v1/status";
|
|
20985
|
+
var DEFAULT_SETUP_WAIT_TIMEOUT_SECONDS = 30;
|
|
20963
20986
|
var CONNECTOR_HOST_LOOPBACK = "127.0.0.1";
|
|
20964
20987
|
var CONNECTOR_HOST_DOCKER = "host.docker.internal";
|
|
20965
20988
|
var CONNECTOR_HOST_DOCKER_GATEWAY = "gateway.docker.internal";
|
|
20966
20989
|
var CONNECTOR_HOST_LINUX_BRIDGE = "172.17.0.1";
|
|
20990
|
+
var CONNECTOR_RUN_DIR_NAME = "run";
|
|
20967
20991
|
var PEER_ALIAS_PATTERN = /^[a-zA-Z0-9._-]+$/;
|
|
20968
20992
|
var FILE_MODE3 = 384;
|
|
20969
20993
|
var OPENCLAW_HOOK_TOKEN_BYTES = 32;
|
|
@@ -21376,6 +21400,256 @@ function buildRelayConnectorBaseUrls(port) {
|
|
|
21376
21400
|
buildConnectorBaseUrl(CONNECTOR_HOST_LOOPBACK, port)
|
|
21377
21401
|
];
|
|
21378
21402
|
}
|
|
21403
|
+
function parseOpenclawRuntimeMode(value) {
|
|
21404
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
21405
|
+
return "auto";
|
|
21406
|
+
}
|
|
21407
|
+
const normalized = value.trim().toLowerCase();
|
|
21408
|
+
if (normalized === "auto" || normalized === "service" || normalized === "detached") {
|
|
21409
|
+
return normalized;
|
|
21410
|
+
}
|
|
21411
|
+
throw createCliError6(
|
|
21412
|
+
"CLI_OPENCLAW_SETUP_RUNTIME_MODE_INVALID",
|
|
21413
|
+
"runtimeMode must be one of: auto, service, detached"
|
|
21414
|
+
);
|
|
21415
|
+
}
|
|
21416
|
+
function parseWaitTimeoutSeconds(value) {
|
|
21417
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
21418
|
+
return DEFAULT_SETUP_WAIT_TIMEOUT_SECONDS;
|
|
21419
|
+
}
|
|
21420
|
+
const parsed = Number.parseInt(value, 10);
|
|
21421
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
21422
|
+
throw createCliError6(
|
|
21423
|
+
"CLI_OPENCLAW_SETUP_TIMEOUT_INVALID",
|
|
21424
|
+
"waitTimeoutSeconds must be a positive integer"
|
|
21425
|
+
);
|
|
21426
|
+
}
|
|
21427
|
+
return parsed;
|
|
21428
|
+
}
|
|
21429
|
+
function resolveConnectorStatusUrl(connectorBaseUrl) {
|
|
21430
|
+
const normalizedBase = connectorBaseUrl.endsWith("/") ? connectorBaseUrl : `${connectorBaseUrl}/`;
|
|
21431
|
+
return new URL(
|
|
21432
|
+
DEFAULT_CONNECTOR_STATUS_PATH2.slice(1),
|
|
21433
|
+
normalizedBase
|
|
21434
|
+
).toString();
|
|
21435
|
+
}
|
|
21436
|
+
function parseConnectorStatusPayload(payload) {
|
|
21437
|
+
if (!isRecord8(payload) || typeof payload.websocketConnected !== "boolean") {
|
|
21438
|
+
throw createCliError6(
|
|
21439
|
+
"CLI_OPENCLAW_SETUP_CONNECTOR_STATUS_INVALID",
|
|
21440
|
+
"Connector status response is invalid"
|
|
21441
|
+
);
|
|
21442
|
+
}
|
|
21443
|
+
return {
|
|
21444
|
+
websocketConnected: payload.websocketConnected
|
|
21445
|
+
};
|
|
21446
|
+
}
|
|
21447
|
+
async function fetchConnectorHealthStatus(input) {
|
|
21448
|
+
const statusUrl = resolveConnectorStatusUrl(input.connectorBaseUrl);
|
|
21449
|
+
try {
|
|
21450
|
+
const response = await input.fetchImpl(statusUrl, {
|
|
21451
|
+
method: "GET",
|
|
21452
|
+
headers: {
|
|
21453
|
+
accept: "application/json"
|
|
21454
|
+
}
|
|
21455
|
+
});
|
|
21456
|
+
if (!response.ok) {
|
|
21457
|
+
return {
|
|
21458
|
+
connected: false,
|
|
21459
|
+
reachable: false,
|
|
21460
|
+
statusUrl,
|
|
21461
|
+
reason: `HTTP ${response.status}`
|
|
21462
|
+
};
|
|
21463
|
+
}
|
|
21464
|
+
let payload;
|
|
21465
|
+
try {
|
|
21466
|
+
payload = await response.json();
|
|
21467
|
+
} catch {
|
|
21468
|
+
return {
|
|
21469
|
+
connected: false,
|
|
21470
|
+
reachable: false,
|
|
21471
|
+
statusUrl,
|
|
21472
|
+
reason: "invalid JSON payload"
|
|
21473
|
+
};
|
|
21474
|
+
}
|
|
21475
|
+
const parsed = parseConnectorStatusPayload(payload);
|
|
21476
|
+
return {
|
|
21477
|
+
connected: parsed.websocketConnected,
|
|
21478
|
+
reachable: true,
|
|
21479
|
+
statusUrl,
|
|
21480
|
+
reason: parsed.websocketConnected ? void 0 : "connector websocket is disconnected"
|
|
21481
|
+
};
|
|
21482
|
+
} catch {
|
|
21483
|
+
return {
|
|
21484
|
+
connected: false,
|
|
21485
|
+
reachable: false,
|
|
21486
|
+
statusUrl,
|
|
21487
|
+
reason: "connector status endpoint is unreachable"
|
|
21488
|
+
};
|
|
21489
|
+
}
|
|
21490
|
+
}
|
|
21491
|
+
async function waitForConnectorConnected(input) {
|
|
21492
|
+
const deadline = Date.now() + input.waitTimeoutSeconds * 1e3;
|
|
21493
|
+
let latest = await fetchConnectorHealthStatus({
|
|
21494
|
+
connectorBaseUrl: input.connectorBaseUrl,
|
|
21495
|
+
fetchImpl: input.fetchImpl
|
|
21496
|
+
});
|
|
21497
|
+
while (!latest.connected && Date.now() < deadline) {
|
|
21498
|
+
await new Promise((resolve2) => {
|
|
21499
|
+
setTimeout(resolve2, 1e3);
|
|
21500
|
+
});
|
|
21501
|
+
latest = await fetchConnectorHealthStatus({
|
|
21502
|
+
connectorBaseUrl: input.connectorBaseUrl,
|
|
21503
|
+
fetchImpl: input.fetchImpl
|
|
21504
|
+
});
|
|
21505
|
+
}
|
|
21506
|
+
if (!latest.connected) {
|
|
21507
|
+
throw createCliError6(
|
|
21508
|
+
"CLI_OPENCLAW_SETUP_CONNECTOR_NOT_READY",
|
|
21509
|
+
`Connector runtime is not websocket-connected after ${input.waitTimeoutSeconds} seconds`,
|
|
21510
|
+
{
|
|
21511
|
+
connectorBaseUrl: input.connectorBaseUrl,
|
|
21512
|
+
connectorStatusUrl: latest.statusUrl,
|
|
21513
|
+
reason: latest.reason
|
|
21514
|
+
}
|
|
21515
|
+
);
|
|
21516
|
+
}
|
|
21517
|
+
return latest;
|
|
21518
|
+
}
|
|
21519
|
+
function resolveConnectorRunDir(homeDir) {
|
|
21520
|
+
return join6(homeDir, CLAWDENTITY_DIR_NAME, CONNECTOR_RUN_DIR_NAME);
|
|
21521
|
+
}
|
|
21522
|
+
function resolveConnectorPidPath(homeDir, agentName) {
|
|
21523
|
+
return join6(resolveConnectorRunDir(homeDir), `connector-${agentName}.pid`);
|
|
21524
|
+
}
|
|
21525
|
+
async function readConnectorPidFile(pidPath) {
|
|
21526
|
+
try {
|
|
21527
|
+
const raw = (await readFile4(pidPath, "utf8")).trim();
|
|
21528
|
+
if (raw.length === 0) {
|
|
21529
|
+
return void 0;
|
|
21530
|
+
}
|
|
21531
|
+
const parsed = Number.parseInt(raw, 10);
|
|
21532
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
21533
|
+
return void 0;
|
|
21534
|
+
}
|
|
21535
|
+
return parsed;
|
|
21536
|
+
} catch (error48) {
|
|
21537
|
+
if (getErrorCode2(error48) === "ENOENT") {
|
|
21538
|
+
return void 0;
|
|
21539
|
+
}
|
|
21540
|
+
throw error48;
|
|
21541
|
+
}
|
|
21542
|
+
}
|
|
21543
|
+
function isPidRunning(pid) {
|
|
21544
|
+
try {
|
|
21545
|
+
process.kill(pid, 0);
|
|
21546
|
+
return true;
|
|
21547
|
+
} catch {
|
|
21548
|
+
return false;
|
|
21549
|
+
}
|
|
21550
|
+
}
|
|
21551
|
+
async function stopDetachedConnectorIfRunning(input) {
|
|
21552
|
+
const pidPath = resolveConnectorPidPath(input.homeDir, input.agentName);
|
|
21553
|
+
const pid = await readConnectorPidFile(pidPath);
|
|
21554
|
+
if (pid === void 0 || !isPidRunning(pid)) {
|
|
21555
|
+
return;
|
|
21556
|
+
}
|
|
21557
|
+
try {
|
|
21558
|
+
process.kill(pid, "SIGTERM");
|
|
21559
|
+
} catch {
|
|
21560
|
+
}
|
|
21561
|
+
}
|
|
21562
|
+
function resolveCliEntryPathForDetachedStart() {
|
|
21563
|
+
const argvEntry = typeof process.argv[1] === "string" ? process.argv[1] : "";
|
|
21564
|
+
if (argvEntry.length > 0 && existsSync(argvEntry)) {
|
|
21565
|
+
return argvEntry;
|
|
21566
|
+
}
|
|
21567
|
+
const modulePath = fileURLToPath2(import.meta.url);
|
|
21568
|
+
return join6(dirname4(modulePath), "..", "bin.js");
|
|
21569
|
+
}
|
|
21570
|
+
async function startDetachedConnectorRuntime(input) {
|
|
21571
|
+
await stopDetachedConnectorIfRunning({
|
|
21572
|
+
homeDir: input.homeDir,
|
|
21573
|
+
agentName: input.agentName
|
|
21574
|
+
});
|
|
21575
|
+
const runDir = resolveConnectorRunDir(input.homeDir);
|
|
21576
|
+
await mkdir5(runDir, { recursive: true });
|
|
21577
|
+
const cliEntryPath = resolveCliEntryPathForDetachedStart();
|
|
21578
|
+
const args = [
|
|
21579
|
+
cliEntryPath,
|
|
21580
|
+
"connector",
|
|
21581
|
+
"start",
|
|
21582
|
+
input.agentName,
|
|
21583
|
+
"--openclaw-base-url",
|
|
21584
|
+
input.openclawBaseUrl
|
|
21585
|
+
];
|
|
21586
|
+
const child = spawn(process.execPath, args, {
|
|
21587
|
+
detached: true,
|
|
21588
|
+
stdio: "ignore",
|
|
21589
|
+
env: process.env
|
|
21590
|
+
});
|
|
21591
|
+
child.unref();
|
|
21592
|
+
await writeSecureFile3(
|
|
21593
|
+
resolveConnectorPidPath(input.homeDir, input.agentName),
|
|
21594
|
+
`${child.pid}
|
|
21595
|
+
`
|
|
21596
|
+
);
|
|
21597
|
+
}
|
|
21598
|
+
async function startSetupConnectorRuntime(input) {
|
|
21599
|
+
if (input.mode !== "service") {
|
|
21600
|
+
const existingStatus = await fetchConnectorHealthStatus({
|
|
21601
|
+
connectorBaseUrl: input.connectorBaseUrl,
|
|
21602
|
+
fetchImpl: input.fetchImpl
|
|
21603
|
+
});
|
|
21604
|
+
if (existingStatus.connected) {
|
|
21605
|
+
return {
|
|
21606
|
+
runtimeMode: "existing",
|
|
21607
|
+
runtimeStatus: "running",
|
|
21608
|
+
websocketStatus: "connected",
|
|
21609
|
+
connectorStatusUrl: existingStatus.statusUrl
|
|
21610
|
+
};
|
|
21611
|
+
}
|
|
21612
|
+
}
|
|
21613
|
+
let runtimeMode = "service";
|
|
21614
|
+
if (input.mode === "detached") {
|
|
21615
|
+
runtimeMode = "detached";
|
|
21616
|
+
} else {
|
|
21617
|
+
try {
|
|
21618
|
+
await installConnectorServiceForAgent(input.agentName, {
|
|
21619
|
+
platform: "auto",
|
|
21620
|
+
openclawBaseUrl: input.openclawBaseUrl
|
|
21621
|
+
});
|
|
21622
|
+
runtimeMode = "service";
|
|
21623
|
+
} catch (error48) {
|
|
21624
|
+
if (input.mode === "service") {
|
|
21625
|
+
throw error48;
|
|
21626
|
+
}
|
|
21627
|
+
runtimeMode = "detached";
|
|
21628
|
+
logger8.warn("cli.openclaw.setup.service_fallback_detached", {
|
|
21629
|
+
agentName: input.agentName,
|
|
21630
|
+
reason: error48 instanceof Error ? error48.message : "unknown"
|
|
21631
|
+
});
|
|
21632
|
+
}
|
|
21633
|
+
}
|
|
21634
|
+
if (runtimeMode === "detached") {
|
|
21635
|
+
await startDetachedConnectorRuntime({
|
|
21636
|
+
agentName: input.agentName,
|
|
21637
|
+
homeDir: input.homeDir,
|
|
21638
|
+
openclawBaseUrl: input.openclawBaseUrl
|
|
21639
|
+
});
|
|
21640
|
+
}
|
|
21641
|
+
const connectedStatus = await waitForConnectorConnected({
|
|
21642
|
+
connectorBaseUrl: input.connectorBaseUrl,
|
|
21643
|
+
fetchImpl: input.fetchImpl,
|
|
21644
|
+
waitTimeoutSeconds: input.waitTimeoutSeconds
|
|
21645
|
+
});
|
|
21646
|
+
return {
|
|
21647
|
+
runtimeMode,
|
|
21648
|
+
runtimeStatus: "running",
|
|
21649
|
+
websocketStatus: "connected",
|
|
21650
|
+
connectorStatusUrl: connectedStatus.statusUrl
|
|
21651
|
+
};
|
|
21652
|
+
}
|
|
21379
21653
|
function parseRelayRuntimeConfig(value, relayRuntimeConfigPath) {
|
|
21380
21654
|
if (!isRecord8(value)) {
|
|
21381
21655
|
throw createCliError6(
|
|
@@ -22054,6 +22328,97 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22054
22328
|
})
|
|
22055
22329
|
);
|
|
22056
22330
|
}
|
|
22331
|
+
if (options.includeConnectorRuntimeCheck !== false) {
|
|
22332
|
+
if (selectedAgentName === void 0) {
|
|
22333
|
+
checks.push(
|
|
22334
|
+
toDoctorCheck({
|
|
22335
|
+
id: "state.connectorRuntime",
|
|
22336
|
+
label: "Connector runtime",
|
|
22337
|
+
status: "fail",
|
|
22338
|
+
message: "cannot validate connector runtime without selected agent marker",
|
|
22339
|
+
remediationHint: OPENCLAW_SETUP_COMMAND_HINT
|
|
22340
|
+
})
|
|
22341
|
+
);
|
|
22342
|
+
} else {
|
|
22343
|
+
const connectorAssignmentsPath = resolveConnectorAssignmentsPath(homeDir);
|
|
22344
|
+
try {
|
|
22345
|
+
const connectorAssignments = await loadConnectorAssignments(
|
|
22346
|
+
connectorAssignmentsPath
|
|
22347
|
+
);
|
|
22348
|
+
const assignment = connectorAssignments.agents[selectedAgentName];
|
|
22349
|
+
if (assignment === void 0) {
|
|
22350
|
+
checks.push(
|
|
22351
|
+
toDoctorCheck({
|
|
22352
|
+
id: "state.connectorRuntime",
|
|
22353
|
+
label: "Connector runtime",
|
|
22354
|
+
status: "fail",
|
|
22355
|
+
message: `no connector assignment found for ${selectedAgentName}`,
|
|
22356
|
+
remediationHint: OPENCLAW_SETUP_COMMAND_HINT,
|
|
22357
|
+
details: { connectorAssignmentsPath, selectedAgentName }
|
|
22358
|
+
})
|
|
22359
|
+
);
|
|
22360
|
+
} else {
|
|
22361
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
22362
|
+
if (typeof fetchImpl !== "function") {
|
|
22363
|
+
checks.push(
|
|
22364
|
+
toDoctorCheck({
|
|
22365
|
+
id: "state.connectorRuntime",
|
|
22366
|
+
label: "Connector runtime",
|
|
22367
|
+
status: "fail",
|
|
22368
|
+
message: "fetch implementation is unavailable for connector checks",
|
|
22369
|
+
remediationHint: "Run doctor in a Node runtime with fetch support, or rerun openclaw setup"
|
|
22370
|
+
})
|
|
22371
|
+
);
|
|
22372
|
+
} else {
|
|
22373
|
+
const connectorStatus = await fetchConnectorHealthStatus({
|
|
22374
|
+
connectorBaseUrl: assignment.connectorBaseUrl,
|
|
22375
|
+
fetchImpl
|
|
22376
|
+
});
|
|
22377
|
+
if (connectorStatus.connected) {
|
|
22378
|
+
checks.push(
|
|
22379
|
+
toDoctorCheck({
|
|
22380
|
+
id: "state.connectorRuntime",
|
|
22381
|
+
label: "Connector runtime",
|
|
22382
|
+
status: "pass",
|
|
22383
|
+
message: `connector websocket is connected (${assignment.connectorBaseUrl})`,
|
|
22384
|
+
details: {
|
|
22385
|
+
connectorStatusUrl: connectorStatus.statusUrl,
|
|
22386
|
+
connectorBaseUrl: assignment.connectorBaseUrl
|
|
22387
|
+
}
|
|
22388
|
+
})
|
|
22389
|
+
);
|
|
22390
|
+
} else {
|
|
22391
|
+
const reason = connectorStatus.reason ?? "connector runtime is unavailable";
|
|
22392
|
+
checks.push(
|
|
22393
|
+
toDoctorCheck({
|
|
22394
|
+
id: "state.connectorRuntime",
|
|
22395
|
+
label: "Connector runtime",
|
|
22396
|
+
status: "fail",
|
|
22397
|
+
message: `connector runtime is not ready: ${reason}`,
|
|
22398
|
+
remediationHint: OPENCLAW_SETUP_COMMAND_HINT,
|
|
22399
|
+
details: {
|
|
22400
|
+
connectorStatusUrl: connectorStatus.statusUrl,
|
|
22401
|
+
connectorBaseUrl: assignment.connectorBaseUrl
|
|
22402
|
+
}
|
|
22403
|
+
})
|
|
22404
|
+
);
|
|
22405
|
+
}
|
|
22406
|
+
}
|
|
22407
|
+
}
|
|
22408
|
+
} catch {
|
|
22409
|
+
checks.push(
|
|
22410
|
+
toDoctorCheck({
|
|
22411
|
+
id: "state.connectorRuntime",
|
|
22412
|
+
label: "Connector runtime",
|
|
22413
|
+
status: "fail",
|
|
22414
|
+
message: `unable to read connector assignments at ${connectorAssignmentsPath}`,
|
|
22415
|
+
remediationHint: OPENCLAW_SETUP_COMMAND_HINT,
|
|
22416
|
+
details: { connectorAssignmentsPath }
|
|
22417
|
+
})
|
|
22418
|
+
);
|
|
22419
|
+
}
|
|
22420
|
+
}
|
|
22421
|
+
}
|
|
22057
22422
|
return toDoctorResult(checks);
|
|
22058
22423
|
}
|
|
22059
22424
|
function parseRelayProbeFailure(input) {
|
|
@@ -22135,7 +22500,8 @@ async function runOpenclawRelayTest(options) {
|
|
|
22135
22500
|
homeDir,
|
|
22136
22501
|
openclawDir,
|
|
22137
22502
|
peerAlias,
|
|
22138
|
-
resolveConfigImpl: options.resolveConfigImpl
|
|
22503
|
+
resolveConfigImpl: options.resolveConfigImpl,
|
|
22504
|
+
includeConnectorRuntimeCheck: false
|
|
22139
22505
|
});
|
|
22140
22506
|
const relayRuntimeConfigPath = resolveRelayRuntimeConfigPath(homeDir);
|
|
22141
22507
|
let openclawBaseUrl = DEFAULT_OPENCLAW_BASE_URL2;
|
|
@@ -22346,6 +22712,42 @@ async function setupOpenclawRelay(agentName, options) {
|
|
|
22346
22712
|
relayRuntimeConfigPath
|
|
22347
22713
|
};
|
|
22348
22714
|
}
|
|
22715
|
+
async function setupOpenclawSelfReady(agentName, options) {
|
|
22716
|
+
const setup = await setupOpenclawRelay(agentName, options);
|
|
22717
|
+
if (options.noRuntimeStart === true) {
|
|
22718
|
+
return {
|
|
22719
|
+
...setup,
|
|
22720
|
+
runtimeMode: "none",
|
|
22721
|
+
runtimeStatus: "skipped",
|
|
22722
|
+
websocketStatus: "skipped"
|
|
22723
|
+
};
|
|
22724
|
+
}
|
|
22725
|
+
const fetchImpl = globalThis.fetch;
|
|
22726
|
+
if (typeof fetchImpl !== "function") {
|
|
22727
|
+
throw createCliError6(
|
|
22728
|
+
"CLI_OPENCLAW_SETUP_FETCH_UNAVAILABLE",
|
|
22729
|
+
"Runtime fetch is unavailable for connector readiness checks"
|
|
22730
|
+
);
|
|
22731
|
+
}
|
|
22732
|
+
const resolvedMode = parseOpenclawRuntimeMode(options.runtimeMode);
|
|
22733
|
+
const waitTimeoutSeconds = parseWaitTimeoutSeconds(
|
|
22734
|
+
options.waitTimeoutSeconds
|
|
22735
|
+
);
|
|
22736
|
+
const resolvedHomeDir = resolveHomeDir(options.homeDir);
|
|
22737
|
+
const runtime = await startSetupConnectorRuntime({
|
|
22738
|
+
agentName: assertValidAgentName(agentName),
|
|
22739
|
+
homeDir: resolvedHomeDir,
|
|
22740
|
+
openclawBaseUrl: setup.openclawBaseUrl,
|
|
22741
|
+
connectorBaseUrl: setup.connectorBaseUrl,
|
|
22742
|
+
mode: resolvedMode,
|
|
22743
|
+
waitTimeoutSeconds,
|
|
22744
|
+
fetchImpl
|
|
22745
|
+
});
|
|
22746
|
+
return {
|
|
22747
|
+
...setup,
|
|
22748
|
+
...runtime
|
|
22749
|
+
};
|
|
22750
|
+
}
|
|
22349
22751
|
var createOpenclawCommand = () => {
|
|
22350
22752
|
const openclawCommand = new Command7("openclaw").description(
|
|
22351
22753
|
"Manage OpenClaw relay setup"
|
|
@@ -22359,11 +22761,20 @@ var createOpenclawCommand = () => {
|
|
|
22359
22761
|
).option(
|
|
22360
22762
|
"--openclaw-base-url <url>",
|
|
22361
22763
|
"Base URL for local OpenClaw hook API (default http://127.0.0.1:18789)"
|
|
22764
|
+
).option(
|
|
22765
|
+
"--runtime-mode <mode>",
|
|
22766
|
+
"Connector runtime mode: auto | service | detached (default auto)"
|
|
22767
|
+
).option(
|
|
22768
|
+
"--wait-timeout-seconds <seconds>",
|
|
22769
|
+
"Seconds to wait for connector websocket readiness (default 30)"
|
|
22770
|
+
).option(
|
|
22771
|
+
"--no-runtime-start",
|
|
22772
|
+
"Skip connector runtime startup (advanced/manual mode)"
|
|
22362
22773
|
).action(
|
|
22363
22774
|
withErrorHandling(
|
|
22364
22775
|
"openclaw setup",
|
|
22365
22776
|
async (agentName, options) => {
|
|
22366
|
-
const result = await
|
|
22777
|
+
const result = await setupOpenclawSelfReady(agentName, options);
|
|
22367
22778
|
writeStdoutLine("Self setup complete");
|
|
22368
22779
|
writeStdoutLine(
|
|
22369
22780
|
`Updated OpenClaw config: ${result.openclawConfigPath}`
|
|
@@ -22380,6 +22791,14 @@ var createOpenclawCommand = () => {
|
|
|
22380
22791
|
writeStdoutLine(
|
|
22381
22792
|
`Relay runtime config: ${result.relayRuntimeConfigPath}`
|
|
22382
22793
|
);
|
|
22794
|
+
writeStdoutLine(`Runtime mode: ${result.runtimeMode}`);
|
|
22795
|
+
writeStdoutLine(`Runtime status: ${result.runtimeStatus}`);
|
|
22796
|
+
writeStdoutLine(`WebSocket status: ${result.websocketStatus}`);
|
|
22797
|
+
if (result.connectorStatusUrl) {
|
|
22798
|
+
writeStdoutLine(
|
|
22799
|
+
`Connector status URL: ${result.connectorStatusUrl}`
|
|
22800
|
+
);
|
|
22801
|
+
}
|
|
22383
22802
|
}
|
|
22384
22803
|
)
|
|
22385
22804
|
);
|
|
@@ -22471,13 +22890,15 @@ var PAIRING_QR_DIR_NAME = "pairing";
|
|
|
22471
22890
|
var PEERS_FILE_NAME2 = "peers.json";
|
|
22472
22891
|
var PAIR_START_PATH = "/pair/start";
|
|
22473
22892
|
var PAIR_CONFIRM_PATH = "/pair/confirm";
|
|
22474
|
-
var
|
|
22893
|
+
var PAIR_STATUS_PATH = "/pair/status";
|
|
22475
22894
|
var NONCE_SIZE2 = 24;
|
|
22476
22895
|
var PAIRING_TICKET_PREFIX = "clwpair1_";
|
|
22477
22896
|
var PAIRING_QR_MAX_AGE_SECONDS = 900;
|
|
22478
22897
|
var PAIRING_QR_FILENAME_PATTERN = /-pair-(\d+)\.png$/;
|
|
22479
22898
|
var FILE_MODE4 = 384;
|
|
22480
22899
|
var PEER_ALIAS_PATTERN2 = /^[a-zA-Z0-9._-]+$/;
|
|
22900
|
+
var DEFAULT_STATUS_WAIT_SECONDS = 300;
|
|
22901
|
+
var DEFAULT_STATUS_POLL_INTERVAL_SECONDS = 3;
|
|
22481
22902
|
var isRecord9 = (value) => {
|
|
22482
22903
|
return typeof value === "object" && value !== null;
|
|
22483
22904
|
};
|
|
@@ -22553,6 +22974,52 @@ function parsePairingTicketIssuerOrigin(ticket) {
|
|
|
22553
22974
|
}
|
|
22554
22975
|
return issuerUrl.origin;
|
|
22555
22976
|
}
|
|
22977
|
+
function parseAitAgentDid(ait) {
|
|
22978
|
+
const parts = ait.split(".");
|
|
22979
|
+
if (parts.length < 2) {
|
|
22980
|
+
throw createCliError7(
|
|
22981
|
+
"CLI_PAIR_AGENT_NOT_FOUND",
|
|
22982
|
+
"Agent AIT is invalid. Recreate the agent before pairing."
|
|
22983
|
+
);
|
|
22984
|
+
}
|
|
22985
|
+
let payloadRaw;
|
|
22986
|
+
try {
|
|
22987
|
+
payloadRaw = new TextDecoder().decode(decodeBase64url(parts[1] ?? ""));
|
|
22988
|
+
} catch {
|
|
22989
|
+
throw createCliError7(
|
|
22990
|
+
"CLI_PAIR_AGENT_NOT_FOUND",
|
|
22991
|
+
"Agent AIT is invalid. Recreate the agent before pairing."
|
|
22992
|
+
);
|
|
22993
|
+
}
|
|
22994
|
+
let payload;
|
|
22995
|
+
try {
|
|
22996
|
+
payload = JSON.parse(payloadRaw);
|
|
22997
|
+
} catch {
|
|
22998
|
+
throw createCliError7(
|
|
22999
|
+
"CLI_PAIR_AGENT_NOT_FOUND",
|
|
23000
|
+
"Agent AIT is invalid. Recreate the agent before pairing."
|
|
23001
|
+
);
|
|
23002
|
+
}
|
|
23003
|
+
if (!isRecord9(payload) || typeof payload.sub !== "string") {
|
|
23004
|
+
throw createCliError7(
|
|
23005
|
+
"CLI_PAIR_AGENT_NOT_FOUND",
|
|
23006
|
+
"Agent AIT is invalid. Recreate the agent before pairing."
|
|
23007
|
+
);
|
|
23008
|
+
}
|
|
23009
|
+
const candidate = payload.sub.trim();
|
|
23010
|
+
try {
|
|
23011
|
+
const parsed = parseDid(candidate);
|
|
23012
|
+
if (parsed.kind !== "agent") {
|
|
23013
|
+
throw new Error("invalid kind");
|
|
23014
|
+
}
|
|
23015
|
+
} catch {
|
|
23016
|
+
throw createCliError7(
|
|
23017
|
+
"CLI_PAIR_AGENT_NOT_FOUND",
|
|
23018
|
+
"Agent AIT is invalid. Recreate the agent before pairing."
|
|
23019
|
+
);
|
|
23020
|
+
}
|
|
23021
|
+
return candidate;
|
|
23022
|
+
}
|
|
22556
23023
|
function parsePeerAlias2(value) {
|
|
22557
23024
|
if (value.length === 0 || value.length > 128) {
|
|
22558
23025
|
throw createCliError7(
|
|
@@ -22684,6 +23151,20 @@ function parseTtlSeconds(value) {
|
|
|
22684
23151
|
}
|
|
22685
23152
|
return parsed;
|
|
22686
23153
|
}
|
|
23154
|
+
function parsePositiveIntegerOption(input) {
|
|
23155
|
+
const raw = parseNonEmptyString9(input.value);
|
|
23156
|
+
if (raw.length === 0) {
|
|
23157
|
+
return input.defaultValue;
|
|
23158
|
+
}
|
|
23159
|
+
const parsed = Number.parseInt(raw, 10);
|
|
23160
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
23161
|
+
throw createCliError7(
|
|
23162
|
+
"CLI_PAIR_STATUS_WAIT_INVALID",
|
|
23163
|
+
`${input.optionName} must be a positive integer`
|
|
23164
|
+
);
|
|
23165
|
+
}
|
|
23166
|
+
return parsed;
|
|
23167
|
+
}
|
|
22687
23168
|
function parseProxyUrl2(candidate) {
|
|
22688
23169
|
try {
|
|
22689
23170
|
const parsed = new URL(candidate);
|
|
@@ -22767,11 +23248,8 @@ async function executePairRequest(input) {
|
|
|
22767
23248
|
function mapStartPairError(status, payload) {
|
|
22768
23249
|
const code = extractErrorCode(payload);
|
|
22769
23250
|
const message2 = extractErrorMessage(payload);
|
|
22770
|
-
if (code === "
|
|
22771
|
-
return message2 ? `
|
|
22772
|
-
}
|
|
22773
|
-
if (code === "PROXY_PAIR_OWNER_PAT_FORBIDDEN" || status === 403) {
|
|
22774
|
-
return message2 ? `Owner PAT does not control initiator agent DID (403): ${message2}` : "Owner PAT does not control initiator agent DID (403).";
|
|
23251
|
+
if (code === "PROXY_PAIR_OWNERSHIP_FORBIDDEN" || status === 403) {
|
|
23252
|
+
return message2 ? `Initiator agent ownership check failed (403): ${message2}` : "Initiator agent ownership check failed (403).";
|
|
22775
23253
|
}
|
|
22776
23254
|
if (status === 400) {
|
|
22777
23255
|
return message2 ? `Pair start request is invalid (400): ${message2}` : "Pair start request is invalid (400).";
|
|
@@ -22804,6 +23282,29 @@ function mapConfirmPairError(status, payload) {
|
|
|
22804
23282
|
}
|
|
22805
23283
|
return `Pair confirm failed (${status})`;
|
|
22806
23284
|
}
|
|
23285
|
+
function mapStatusPairError(status, payload) {
|
|
23286
|
+
const code = extractErrorCode(payload);
|
|
23287
|
+
const message2 = extractErrorMessage(payload);
|
|
23288
|
+
if (code === "PROXY_PAIR_TICKET_NOT_FOUND" || status === 404) {
|
|
23289
|
+
return "Pairing ticket not found";
|
|
23290
|
+
}
|
|
23291
|
+
if (code === "PROXY_PAIR_TICKET_EXPIRED" || status === 410) {
|
|
23292
|
+
return "Pairing ticket has expired";
|
|
23293
|
+
}
|
|
23294
|
+
if (code === "PROXY_PAIR_STATUS_FORBIDDEN" || status === 403) {
|
|
23295
|
+
return message2 ? `Pair status request is forbidden (403): ${message2}` : "Pair status request is forbidden (403).";
|
|
23296
|
+
}
|
|
23297
|
+
if (status === 400) {
|
|
23298
|
+
return message2 ? `Pair status request is invalid (400): ${message2}` : "Pair status request is invalid (400).";
|
|
23299
|
+
}
|
|
23300
|
+
if (status >= 500) {
|
|
23301
|
+
return `Proxy pairing service is unavailable (${status}).`;
|
|
23302
|
+
}
|
|
23303
|
+
if (message2) {
|
|
23304
|
+
return `Pair status failed (${status}): ${message2}`;
|
|
23305
|
+
}
|
|
23306
|
+
return `Pair status failed (${status})`;
|
|
23307
|
+
}
|
|
22807
23308
|
function parsePairStartResponse(payload) {
|
|
22808
23309
|
if (!isRecord9(payload)) {
|
|
22809
23310
|
throw createCliError7(
|
|
@@ -22848,6 +23349,44 @@ function parsePairConfirmResponse(payload) {
|
|
|
22848
23349
|
responderAgentDid
|
|
22849
23350
|
};
|
|
22850
23351
|
}
|
|
23352
|
+
function parsePairStatusResponse(payload) {
|
|
23353
|
+
if (!isRecord9(payload)) {
|
|
23354
|
+
throw createCliError7(
|
|
23355
|
+
"CLI_PAIR_STATUS_INVALID_RESPONSE",
|
|
23356
|
+
"Pair status response is invalid"
|
|
23357
|
+
);
|
|
23358
|
+
}
|
|
23359
|
+
const statusRaw = parseNonEmptyString9(payload.status);
|
|
23360
|
+
if (statusRaw !== "pending" && statusRaw !== "confirmed") {
|
|
23361
|
+
throw createCliError7(
|
|
23362
|
+
"CLI_PAIR_STATUS_INVALID_RESPONSE",
|
|
23363
|
+
"Pair status response is invalid"
|
|
23364
|
+
);
|
|
23365
|
+
}
|
|
23366
|
+
const initiatorAgentDid = parseNonEmptyString9(payload.initiatorAgentDid);
|
|
23367
|
+
const responderAgentDid = parseNonEmptyString9(payload.responderAgentDid);
|
|
23368
|
+
const expiresAt = parseNonEmptyString9(payload.expiresAt);
|
|
23369
|
+
const confirmedAt = parseNonEmptyString9(payload.confirmedAt);
|
|
23370
|
+
if (initiatorAgentDid.length === 0 || expiresAt.length === 0) {
|
|
23371
|
+
throw createCliError7(
|
|
23372
|
+
"CLI_PAIR_STATUS_INVALID_RESPONSE",
|
|
23373
|
+
"Pair status response is invalid"
|
|
23374
|
+
);
|
|
23375
|
+
}
|
|
23376
|
+
if (statusRaw === "confirmed" && responderAgentDid.length === 0) {
|
|
23377
|
+
throw createCliError7(
|
|
23378
|
+
"CLI_PAIR_STATUS_INVALID_RESPONSE",
|
|
23379
|
+
"Pair status response is invalid"
|
|
23380
|
+
);
|
|
23381
|
+
}
|
|
23382
|
+
return {
|
|
23383
|
+
status: statusRaw,
|
|
23384
|
+
initiatorAgentDid,
|
|
23385
|
+
responderAgentDid: responderAgentDid.length > 0 ? responderAgentDid : void 0,
|
|
23386
|
+
expiresAt,
|
|
23387
|
+
confirmedAt: confirmedAt.length > 0 ? confirmedAt : void 0
|
|
23388
|
+
};
|
|
23389
|
+
}
|
|
22851
23390
|
async function readAgentProofMaterial(agentName, dependencies) {
|
|
22852
23391
|
const readFileImpl = dependencies.readFileImpl ?? readFile5;
|
|
22853
23392
|
const getConfigDirImpl = dependencies.getConfigDirImpl ?? getConfigDir;
|
|
@@ -22911,16 +23450,6 @@ async function readAgentProofMaterial(agentName, dependencies) {
|
|
|
22911
23450
|
secretKey
|
|
22912
23451
|
};
|
|
22913
23452
|
}
|
|
22914
|
-
function resolveOwnerPat(options) {
|
|
22915
|
-
const ownerPat = parseNonEmptyString9(options.explicitOwnerPat) || parseNonEmptyString9(options.config.apiKey);
|
|
22916
|
-
if (ownerPat.length > 0) {
|
|
22917
|
-
return ownerPat;
|
|
22918
|
-
}
|
|
22919
|
-
throw createCliError7(
|
|
22920
|
-
"CLI_PAIR_START_OWNER_PAT_REQUIRED",
|
|
22921
|
-
"Owner PAT is required. Pass --owner-pat <token> or configure API key with `clawdentity invite redeem` / `clawdentity config set apiKey <token>`."
|
|
22922
|
-
);
|
|
22923
|
-
}
|
|
22924
23453
|
async function buildSignedHeaders(input) {
|
|
22925
23454
|
const signed = await signHttpRequest({
|
|
22926
23455
|
method: input.method,
|
|
@@ -23076,10 +23605,6 @@ async function startPairing(agentName, options, dependencies = {}) {
|
|
|
23076
23605
|
const nonceFactoryImpl = dependencies.nonceFactoryImpl ?? (() => randomBytes4(NONCE_SIZE2).toString("base64url"));
|
|
23077
23606
|
const ttlSeconds = parseTtlSeconds(options.ttlSeconds);
|
|
23078
23607
|
const config2 = await resolveConfigImpl();
|
|
23079
|
-
const ownerPat = resolveOwnerPat({
|
|
23080
|
-
explicitOwnerPat: options.ownerPat,
|
|
23081
|
-
config: config2
|
|
23082
|
-
});
|
|
23083
23608
|
const proxyUrl = await resolveProxyUrl({
|
|
23084
23609
|
config: config2,
|
|
23085
23610
|
fetchImpl
|
|
@@ -23111,7 +23636,6 @@ async function startPairing(agentName, options, dependencies = {}) {
|
|
|
23111
23636
|
headers: {
|
|
23112
23637
|
authorization: `Claw ${ait}`,
|
|
23113
23638
|
"content-type": "application/json",
|
|
23114
|
-
[OWNER_PAT_HEADER]: ownerPat,
|
|
23115
23639
|
...signedHeaders
|
|
23116
23640
|
},
|
|
23117
23641
|
body: requestBody
|
|
@@ -23238,14 +23762,157 @@ async function confirmPairing(agentName, options, dependencies = {}) {
|
|
|
23238
23762
|
peerAlias
|
|
23239
23763
|
};
|
|
23240
23764
|
}
|
|
23765
|
+
async function getPairingStatusOnce(agentName, options, dependencies = {}) {
|
|
23766
|
+
const fetchImpl = dependencies.fetchImpl ?? fetch;
|
|
23767
|
+
const resolveConfigImpl = dependencies.resolveConfigImpl ?? resolveConfig;
|
|
23768
|
+
const nowSecondsImpl = dependencies.nowSecondsImpl ?? (() => Math.floor(Date.now() / 1e3));
|
|
23769
|
+
const nonceFactoryImpl = dependencies.nonceFactoryImpl ?? (() => randomBytes4(NONCE_SIZE2).toString("base64url"));
|
|
23770
|
+
const config2 = await resolveConfigImpl();
|
|
23771
|
+
const proxyUrl = await resolveProxyUrl({
|
|
23772
|
+
config: config2,
|
|
23773
|
+
fetchImpl
|
|
23774
|
+
});
|
|
23775
|
+
const ticket = parsePairingTicket(options.ticket);
|
|
23776
|
+
const { ait, secretKey } = await readAgentProofMaterial(
|
|
23777
|
+
agentName,
|
|
23778
|
+
dependencies
|
|
23779
|
+
);
|
|
23780
|
+
const callerAgentDid = parseAitAgentDid(ait);
|
|
23781
|
+
const requestUrl = toProxyRequestUrl(proxyUrl, PAIR_STATUS_PATH);
|
|
23782
|
+
const requestBody = JSON.stringify({ ticket });
|
|
23783
|
+
const bodyBytes = new TextEncoder().encode(requestBody);
|
|
23784
|
+
const timestampSeconds = nowSecondsImpl();
|
|
23785
|
+
const nonce = nonceFactoryImpl();
|
|
23786
|
+
const signedHeaders = await buildSignedHeaders({
|
|
23787
|
+
method: "POST",
|
|
23788
|
+
requestUrl,
|
|
23789
|
+
bodyBytes,
|
|
23790
|
+
secretKey,
|
|
23791
|
+
timestampSeconds,
|
|
23792
|
+
nonce
|
|
23793
|
+
});
|
|
23794
|
+
const response = await executePairRequest({
|
|
23795
|
+
fetchImpl,
|
|
23796
|
+
url: requestUrl,
|
|
23797
|
+
init: {
|
|
23798
|
+
method: "POST",
|
|
23799
|
+
headers: {
|
|
23800
|
+
authorization: `Claw ${ait}`,
|
|
23801
|
+
"content-type": "application/json",
|
|
23802
|
+
...signedHeaders
|
|
23803
|
+
},
|
|
23804
|
+
body: requestBody
|
|
23805
|
+
}
|
|
23806
|
+
});
|
|
23807
|
+
const responseBody = await parseJsonResponse6(response);
|
|
23808
|
+
if (!response.ok) {
|
|
23809
|
+
throw createCliError7(
|
|
23810
|
+
"CLI_PAIR_STATUS_FAILED",
|
|
23811
|
+
mapStatusPairError(response.status, responseBody)
|
|
23812
|
+
);
|
|
23813
|
+
}
|
|
23814
|
+
const parsed = parsePairStatusResponse(responseBody);
|
|
23815
|
+
let peerAlias;
|
|
23816
|
+
if (parsed.status === "confirmed") {
|
|
23817
|
+
const responderAgentDid = parsed.responderAgentDid;
|
|
23818
|
+
if (!responderAgentDid) {
|
|
23819
|
+
throw createCliError7(
|
|
23820
|
+
"CLI_PAIR_STATUS_INVALID_RESPONSE",
|
|
23821
|
+
"Pair status response is invalid"
|
|
23822
|
+
);
|
|
23823
|
+
}
|
|
23824
|
+
const peerDid = callerAgentDid === parsed.initiatorAgentDid ? responderAgentDid : callerAgentDid === responderAgentDid ? parsed.initiatorAgentDid : void 0;
|
|
23825
|
+
if (!peerDid) {
|
|
23826
|
+
throw createCliError7(
|
|
23827
|
+
"CLI_PAIR_STATUS_FORBIDDEN",
|
|
23828
|
+
"Local agent is not a participant in the pairing ticket"
|
|
23829
|
+
);
|
|
23830
|
+
}
|
|
23831
|
+
peerAlias = await persistPairedPeer({
|
|
23832
|
+
ticket,
|
|
23833
|
+
peerDid,
|
|
23834
|
+
dependencies
|
|
23835
|
+
});
|
|
23836
|
+
}
|
|
23837
|
+
return {
|
|
23838
|
+
...parsed,
|
|
23839
|
+
proxyUrl,
|
|
23840
|
+
peerAlias
|
|
23841
|
+
};
|
|
23842
|
+
}
|
|
23843
|
+
async function waitForPairingStatus(input) {
|
|
23844
|
+
const nowSecondsImpl = input.dependencies.nowSecondsImpl ?? (() => Math.floor(Date.now() / 1e3));
|
|
23845
|
+
const sleepImpl = input.dependencies.sleepImpl ?? (async (ms) => {
|
|
23846
|
+
await new Promise((resolve2) => {
|
|
23847
|
+
setTimeout(resolve2, ms);
|
|
23848
|
+
});
|
|
23849
|
+
});
|
|
23850
|
+
const deadlineSeconds = nowSecondsImpl() + input.waitSeconds;
|
|
23851
|
+
while (true) {
|
|
23852
|
+
const status = await getPairingStatusOnce(
|
|
23853
|
+
input.agentName,
|
|
23854
|
+
{ ticket: input.ticket },
|
|
23855
|
+
input.dependencies
|
|
23856
|
+
);
|
|
23857
|
+
if (status.status === "confirmed") {
|
|
23858
|
+
return status;
|
|
23859
|
+
}
|
|
23860
|
+
const nowSeconds = nowSecondsImpl();
|
|
23861
|
+
if (nowSeconds >= deadlineSeconds) {
|
|
23862
|
+
throw createCliError7(
|
|
23863
|
+
"CLI_PAIR_STATUS_WAIT_TIMEOUT",
|
|
23864
|
+
`Pairing is still pending after ${input.waitSeconds} seconds`
|
|
23865
|
+
);
|
|
23866
|
+
}
|
|
23867
|
+
const remainingSeconds = Math.max(0, deadlineSeconds - nowSeconds);
|
|
23868
|
+
const sleepSeconds = Math.min(input.pollIntervalSeconds, remainingSeconds);
|
|
23869
|
+
await sleepImpl(sleepSeconds * 1e3);
|
|
23870
|
+
}
|
|
23871
|
+
}
|
|
23872
|
+
async function getPairingStatus(agentName, options, dependencies = {}) {
|
|
23873
|
+
const ticketRaw = parseNonEmptyString9(options.ticket);
|
|
23874
|
+
if (ticketRaw.length === 0) {
|
|
23875
|
+
throw createCliError7(
|
|
23876
|
+
"CLI_PAIR_STATUS_TICKET_REQUIRED",
|
|
23877
|
+
"Pair status requires --ticket <clwpair1_...>"
|
|
23878
|
+
);
|
|
23879
|
+
}
|
|
23880
|
+
const ticket = parsePairingTicket(ticketRaw);
|
|
23881
|
+
if (options.wait !== true) {
|
|
23882
|
+
return getPairingStatusOnce(agentName, { ticket }, dependencies);
|
|
23883
|
+
}
|
|
23884
|
+
const waitSeconds = parsePositiveIntegerOption({
|
|
23885
|
+
value: options.waitSeconds,
|
|
23886
|
+
optionName: "waitSeconds",
|
|
23887
|
+
defaultValue: DEFAULT_STATUS_WAIT_SECONDS
|
|
23888
|
+
});
|
|
23889
|
+
const pollIntervalSeconds = parsePositiveIntegerOption({
|
|
23890
|
+
value: options.pollIntervalSeconds,
|
|
23891
|
+
optionName: "pollIntervalSeconds",
|
|
23892
|
+
defaultValue: DEFAULT_STATUS_POLL_INTERVAL_SECONDS
|
|
23893
|
+
});
|
|
23894
|
+
return waitForPairingStatus({
|
|
23895
|
+
agentName,
|
|
23896
|
+
ticket,
|
|
23897
|
+
waitSeconds,
|
|
23898
|
+
pollIntervalSeconds,
|
|
23899
|
+
dependencies
|
|
23900
|
+
});
|
|
23901
|
+
}
|
|
23241
23902
|
var createPairCommand = (dependencies = {}) => {
|
|
23242
23903
|
const pairCommand = new Command8("pair").description(
|
|
23243
23904
|
"Manage proxy trust pairing between agents"
|
|
23244
23905
|
);
|
|
23245
|
-
pairCommand.command("start <agentName>").description("Start pairing and issue one-time pairing ticket").option(
|
|
23246
|
-
"--
|
|
23247
|
-
"
|
|
23248
|
-
).option(
|
|
23906
|
+
pairCommand.command("start <agentName>").description("Start pairing and issue one-time pairing ticket").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").option(
|
|
23907
|
+
"--wait",
|
|
23908
|
+
"Wait for responder confirmation and auto-save peer on initiator"
|
|
23909
|
+
).option(
|
|
23910
|
+
"--wait-seconds <seconds>",
|
|
23911
|
+
"Max seconds to poll for confirmation (default: 300)"
|
|
23912
|
+
).option(
|
|
23913
|
+
"--poll-interval-seconds <seconds>",
|
|
23914
|
+
"Polling interval in seconds while waiting (default: 3)"
|
|
23915
|
+
).action(
|
|
23249
23916
|
withErrorHandling(
|
|
23250
23917
|
"pair start",
|
|
23251
23918
|
async (agentName, options) => {
|
|
@@ -23263,6 +23930,48 @@ var createPairCommand = (dependencies = {}) => {
|
|
|
23263
23930
|
if (result.qrPath) {
|
|
23264
23931
|
writeStdoutLine(`QR File: ${result.qrPath}`);
|
|
23265
23932
|
}
|
|
23933
|
+
if (options.wait === true) {
|
|
23934
|
+
const waitSeconds = parsePositiveIntegerOption({
|
|
23935
|
+
value: options.waitSeconds,
|
|
23936
|
+
optionName: "waitSeconds",
|
|
23937
|
+
defaultValue: DEFAULT_STATUS_WAIT_SECONDS
|
|
23938
|
+
});
|
|
23939
|
+
const pollIntervalSeconds = parsePositiveIntegerOption({
|
|
23940
|
+
value: options.pollIntervalSeconds,
|
|
23941
|
+
optionName: "pollIntervalSeconds",
|
|
23942
|
+
defaultValue: DEFAULT_STATUS_POLL_INTERVAL_SECONDS
|
|
23943
|
+
});
|
|
23944
|
+
writeStdoutLine(
|
|
23945
|
+
`Waiting for confirmation (timeout=${waitSeconds}s, interval=${pollIntervalSeconds}s) ...`
|
|
23946
|
+
);
|
|
23947
|
+
const status = await waitForPairingStatus({
|
|
23948
|
+
agentName,
|
|
23949
|
+
ticket: result.ticket,
|
|
23950
|
+
waitSeconds,
|
|
23951
|
+
pollIntervalSeconds,
|
|
23952
|
+
dependencies
|
|
23953
|
+
});
|
|
23954
|
+
logger9.info("cli.pair_status_confirmed_after_start", {
|
|
23955
|
+
initiatorAgentDid: status.initiatorAgentDid,
|
|
23956
|
+
responderAgentDid: status.responderAgentDid,
|
|
23957
|
+
peerAlias: status.peerAlias
|
|
23958
|
+
});
|
|
23959
|
+
writeStdoutLine("Pairing confirmed");
|
|
23960
|
+
writeStdoutLine(`Status: ${status.status}`);
|
|
23961
|
+
if (status.initiatorAgentDid) {
|
|
23962
|
+
writeStdoutLine(
|
|
23963
|
+
`Initiator Agent DID: ${status.initiatorAgentDid}`
|
|
23964
|
+
);
|
|
23965
|
+
}
|
|
23966
|
+
if (status.responderAgentDid) {
|
|
23967
|
+
writeStdoutLine(
|
|
23968
|
+
`Responder Agent DID: ${status.responderAgentDid}`
|
|
23969
|
+
);
|
|
23970
|
+
}
|
|
23971
|
+
if (status.peerAlias) {
|
|
23972
|
+
writeStdoutLine(`Peer alias saved: ${status.peerAlias}`);
|
|
23973
|
+
}
|
|
23974
|
+
}
|
|
23266
23975
|
}
|
|
23267
23976
|
)
|
|
23268
23977
|
);
|
|
@@ -23287,6 +23996,43 @@ var createPairCommand = (dependencies = {}) => {
|
|
|
23287
23996
|
}
|
|
23288
23997
|
)
|
|
23289
23998
|
);
|
|
23999
|
+
pairCommand.command("status <agentName>").description("Check pairing ticket status and sync local peer on confirm").option("--ticket <ticket>", "One-time pairing ticket (clwpair1_...)").option("--wait", "Poll until ticket is confirmed or timeout is reached").option(
|
|
24000
|
+
"--wait-seconds <seconds>",
|
|
24001
|
+
"Max seconds to poll for confirmation (default: 300)"
|
|
24002
|
+
).option(
|
|
24003
|
+
"--poll-interval-seconds <seconds>",
|
|
24004
|
+
"Polling interval in seconds while waiting (default: 3)"
|
|
24005
|
+
).action(
|
|
24006
|
+
withErrorHandling(
|
|
24007
|
+
"pair status",
|
|
24008
|
+
async (agentName, options) => {
|
|
24009
|
+
const result = await getPairingStatus(
|
|
24010
|
+
agentName,
|
|
24011
|
+
options,
|
|
24012
|
+
dependencies
|
|
24013
|
+
);
|
|
24014
|
+
logger9.info("cli.pair_status", {
|
|
24015
|
+
initiatorAgentDid: result.initiatorAgentDid,
|
|
24016
|
+
responderAgentDid: result.responderAgentDid,
|
|
24017
|
+
status: result.status,
|
|
24018
|
+
proxyUrl: result.proxyUrl,
|
|
24019
|
+
peerAlias: result.peerAlias
|
|
24020
|
+
});
|
|
24021
|
+
writeStdoutLine(`Status: ${result.status}`);
|
|
24022
|
+
writeStdoutLine(`Initiator Agent DID: ${result.initiatorAgentDid}`);
|
|
24023
|
+
if (result.responderAgentDid) {
|
|
24024
|
+
writeStdoutLine(`Responder Agent DID: ${result.responderAgentDid}`);
|
|
24025
|
+
}
|
|
24026
|
+
writeStdoutLine(`Expires At: ${result.expiresAt}`);
|
|
24027
|
+
if (result.confirmedAt) {
|
|
24028
|
+
writeStdoutLine(`Confirmed At: ${result.confirmedAt}`);
|
|
24029
|
+
}
|
|
24030
|
+
if (result.peerAlias) {
|
|
24031
|
+
writeStdoutLine(`Peer alias saved: ${result.peerAlias}`);
|
|
24032
|
+
}
|
|
24033
|
+
}
|
|
24034
|
+
)
|
|
24035
|
+
);
|
|
23290
24036
|
return pairCommand;
|
|
23291
24037
|
};
|
|
23292
24038
|
|
|
@@ -23299,7 +24045,7 @@ import { access as access3, copyFile as copyFile2, mkdir as mkdir7, readdir as r
|
|
|
23299
24045
|
import { createRequire } from "module";
|
|
23300
24046
|
import { homedir as homedir4 } from "os";
|
|
23301
24047
|
import { dirname as dirname6, join as join8, relative } from "path";
|
|
23302
|
-
import { fileURLToPath as
|
|
24048
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
23303
24049
|
var OPENCLAW_DIR_NAME2 = ".openclaw";
|
|
23304
24050
|
var SKILL_PACKAGE_NAME = "@clawdentity/openclaw-skill";
|
|
23305
24051
|
var SKILL_DIR_NAME2 = "clawdentity-openclaw-relay";
|
|
@@ -23344,7 +24090,7 @@ function resolveSkillPackageRoot(input) {
|
|
|
23344
24090
|
return overriddenRoot.trim();
|
|
23345
24091
|
}
|
|
23346
24092
|
const bundledSkillRoot = join8(
|
|
23347
|
-
dirname6(
|
|
24093
|
+
dirname6(fileURLToPath3(import.meta.url)),
|
|
23348
24094
|
"..",
|
|
23349
24095
|
"skill-bundle",
|
|
23350
24096
|
"openclaw-skill"
|
|
@@ -23359,7 +24105,7 @@ function resolveSkillPackageRoot(input) {
|
|
|
23359
24105
|
return dirname6(packageJsonPath);
|
|
23360
24106
|
} catch {
|
|
23361
24107
|
const workspaceFallbackRoot = join8(
|
|
23362
|
-
dirname6(
|
|
24108
|
+
dirname6(fileURLToPath3(import.meta.url)),
|
|
23363
24109
|
"..",
|
|
23364
24110
|
"..",
|
|
23365
24111
|
"openclaw-skill"
|
|
@@ -23452,11 +24198,7 @@ async function resolveArtifacts(input) {
|
|
|
23452
24198
|
}
|
|
23453
24199
|
});
|
|
23454
24200
|
}
|
|
23455
|
-
const targetSkillRoot = join8(
|
|
23456
|
-
input.openclawDir,
|
|
23457
|
-
"skills",
|
|
23458
|
-
SKILL_DIR_NAME2
|
|
23459
|
-
);
|
|
24201
|
+
const targetSkillRoot = join8(input.openclawDir, "skills", SKILL_DIR_NAME2);
|
|
23460
24202
|
const artifacts = [
|
|
23461
24203
|
{
|
|
23462
24204
|
sourcePath: skillDocSource,
|