clawdentity 0.0.18 → 0.0.20
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 +1559 -311
- package/dist/index.js +1559 -311
- package/dist/postinstall.js +0 -0
- package/package.json +17 -16
- package/skill-bundle/openclaw-skill/skill/SKILL.md +9 -2
package/dist/bin.js
CHANGED
|
@@ -18656,9 +18656,9 @@ var createConfigCommand = (dependencies = {}) => {
|
|
|
18656
18656
|
|
|
18657
18657
|
// src/commands/connector.ts
|
|
18658
18658
|
import { execFile as execFileCallback } from "child_process";
|
|
18659
|
-
import { mkdir as
|
|
18659
|
+
import { mkdir as mkdir5, readFile as readFile5, rm, writeFile as writeFile5 } from "fs/promises";
|
|
18660
18660
|
import { homedir as homedir2 } from "os";
|
|
18661
|
-
import { dirname as
|
|
18661
|
+
import { dirname as dirname4, join as join6 } from "path";
|
|
18662
18662
|
import { fileURLToPath } from "url";
|
|
18663
18663
|
import { promisify } from "util";
|
|
18664
18664
|
|
|
@@ -18681,6 +18681,13 @@ var DEFAULT_CONNECTOR_OUTBOUND_PATH = "/v1/outbound";
|
|
|
18681
18681
|
var DEFAULT_CONNECTOR_STATUS_PATH = "/v1/status";
|
|
18682
18682
|
var DEFAULT_RELAY_DELIVER_TIMEOUT_MS = 15e3;
|
|
18683
18683
|
var DEFAULT_OPENCLAW_DELIVER_RETRY_BUDGET_MS = DEFAULT_RELAY_DELIVER_TIMEOUT_MS - 1e3;
|
|
18684
|
+
var DEFAULT_CONNECTOR_INBOUND_INBOX_MAX_MESSAGES = 1e5;
|
|
18685
|
+
var DEFAULT_CONNECTOR_INBOUND_INBOX_MAX_BYTES = 2 * 1024 * 1024 * 1024;
|
|
18686
|
+
var DEFAULT_CONNECTOR_INBOUND_REPLAY_INTERVAL_MS = 2e3;
|
|
18687
|
+
var DEFAULT_CONNECTOR_INBOUND_REPLAY_BATCH_SIZE = 25;
|
|
18688
|
+
var DEFAULT_CONNECTOR_INBOUND_RETRY_INITIAL_DELAY_MS = 1e3;
|
|
18689
|
+
var DEFAULT_CONNECTOR_INBOUND_RETRY_MAX_DELAY_MS = 6e4;
|
|
18690
|
+
var DEFAULT_CONNECTOR_INBOUND_RETRY_BACKOFF_FACTOR = 2;
|
|
18684
18691
|
var AGENT_ACCESS_HEADER = "x-claw-agent-access";
|
|
18685
18692
|
var WS_READY_STATE_OPEN = 1;
|
|
18686
18693
|
|
|
@@ -18942,6 +18949,7 @@ var ConnectorClient = class {
|
|
|
18942
18949
|
fetchImpl;
|
|
18943
18950
|
logger;
|
|
18944
18951
|
hooks;
|
|
18952
|
+
inboundDeliverHandler;
|
|
18945
18953
|
now;
|
|
18946
18954
|
random;
|
|
18947
18955
|
ulidFactory;
|
|
@@ -18996,6 +19004,7 @@ var ConnectorClient = class {
|
|
|
18996
19004
|
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
18997
19005
|
this.logger = options.logger ?? createLogger({ service: "connector", module: "client" });
|
|
18998
19006
|
this.hooks = options.hooks ?? {};
|
|
19007
|
+
this.inboundDeliverHandler = options.inboundDeliverHandler;
|
|
18999
19008
|
this.now = options.now ?? Date.now;
|
|
19000
19009
|
this.random = options.random ?? Math.random;
|
|
19001
19010
|
this.ulidFactory = options.ulidFactory ?? generateUlid;
|
|
@@ -19208,6 +19217,44 @@ var ConnectorClient = class {
|
|
|
19208
19217
|
this.sendFrame(ackFrame);
|
|
19209
19218
|
}
|
|
19210
19219
|
async handleDeliverFrame(frame) {
|
|
19220
|
+
if (this.inboundDeliverHandler !== void 0) {
|
|
19221
|
+
try {
|
|
19222
|
+
const result = await this.inboundDeliverHandler(frame);
|
|
19223
|
+
const ackFrame = {
|
|
19224
|
+
v: CONNECTOR_FRAME_VERSION,
|
|
19225
|
+
type: "deliver_ack",
|
|
19226
|
+
id: this.makeFrameId(),
|
|
19227
|
+
ts: this.makeTimestamp(),
|
|
19228
|
+
ackId: frame.id,
|
|
19229
|
+
accepted: result.accepted,
|
|
19230
|
+
reason: result.reason
|
|
19231
|
+
};
|
|
19232
|
+
this.sendFrame(ackFrame);
|
|
19233
|
+
if (result.accepted) {
|
|
19234
|
+
this.hooks.onDeliverSucceeded?.(frame);
|
|
19235
|
+
} else {
|
|
19236
|
+
this.hooks.onDeliverFailed?.(
|
|
19237
|
+
frame,
|
|
19238
|
+
new Error(
|
|
19239
|
+
result.reason ?? "Inbound delivery was rejected by runtime handler"
|
|
19240
|
+
)
|
|
19241
|
+
);
|
|
19242
|
+
}
|
|
19243
|
+
} catch (error48) {
|
|
19244
|
+
const ackFrame = {
|
|
19245
|
+
v: CONNECTOR_FRAME_VERSION,
|
|
19246
|
+
type: "deliver_ack",
|
|
19247
|
+
id: this.makeFrameId(),
|
|
19248
|
+
ts: this.makeTimestamp(),
|
|
19249
|
+
ackId: frame.id,
|
|
19250
|
+
accepted: false,
|
|
19251
|
+
reason: sanitizeErrorReason(error48)
|
|
19252
|
+
};
|
|
19253
|
+
this.sendFrame(ackFrame);
|
|
19254
|
+
this.hooks.onDeliverFailed?.(frame, error48);
|
|
19255
|
+
}
|
|
19256
|
+
return;
|
|
19257
|
+
}
|
|
19211
19258
|
try {
|
|
19212
19259
|
await this.deliverToLocalOpenclawWithRetry(frame);
|
|
19213
19260
|
const ackFrame = {
|
|
@@ -19323,13 +19370,353 @@ var ConnectorClient = class {
|
|
|
19323
19370
|
}
|
|
19324
19371
|
};
|
|
19325
19372
|
|
|
19373
|
+
// ../../packages/connector/src/inbound-inbox.ts
|
|
19374
|
+
import {
|
|
19375
|
+
appendFile,
|
|
19376
|
+
mkdir as mkdir3,
|
|
19377
|
+
readFile as readFile3,
|
|
19378
|
+
rename as rename2,
|
|
19379
|
+
writeFile as writeFile3
|
|
19380
|
+
} from "fs/promises";
|
|
19381
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
19382
|
+
var INBOUND_INBOX_DIR_NAME = "inbound-inbox";
|
|
19383
|
+
var INBOUND_INBOX_INDEX_FILE_NAME = "index.json";
|
|
19384
|
+
var INBOUND_INBOX_EVENTS_FILE_NAME = "events.jsonl";
|
|
19385
|
+
var INBOUND_INBOX_SCHEMA_VERSION = 1;
|
|
19386
|
+
function nowIso2() {
|
|
19387
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
19388
|
+
}
|
|
19389
|
+
function isRecord5(value) {
|
|
19390
|
+
return typeof value === "object" && value !== null;
|
|
19391
|
+
}
|
|
19392
|
+
function parsePendingItem(value) {
|
|
19393
|
+
if (!isRecord5(value)) {
|
|
19394
|
+
return void 0;
|
|
19395
|
+
}
|
|
19396
|
+
const id = typeof value.id === "string" ? value.id.trim() : "";
|
|
19397
|
+
const requestId = typeof value.requestId === "string" ? value.requestId.trim() : "";
|
|
19398
|
+
const fromAgentDid = typeof value.fromAgentDid === "string" ? value.fromAgentDid.trim() : "";
|
|
19399
|
+
const toAgentDid = typeof value.toAgentDid === "string" ? value.toAgentDid.trim() : "";
|
|
19400
|
+
const receivedAt = typeof value.receivedAt === "string" ? value.receivedAt.trim() : "";
|
|
19401
|
+
const nextAttemptAt = typeof value.nextAttemptAt === "string" ? value.nextAttemptAt.trim() : "";
|
|
19402
|
+
const attemptCount = typeof value.attemptCount === "number" && Number.isInteger(value.attemptCount) ? value.attemptCount : NaN;
|
|
19403
|
+
const payloadBytes = typeof value.payloadBytes === "number" && Number.isInteger(value.payloadBytes) ? value.payloadBytes : NaN;
|
|
19404
|
+
if (id.length === 0 || requestId.length === 0 || fromAgentDid.length === 0 || toAgentDid.length === 0 || receivedAt.length === 0 || nextAttemptAt.length === 0 || !Number.isFinite(attemptCount) || attemptCount < 0 || !Number.isFinite(payloadBytes) || payloadBytes < 0) {
|
|
19405
|
+
return void 0;
|
|
19406
|
+
}
|
|
19407
|
+
const lastError = typeof value.lastError === "string" ? value.lastError : void 0;
|
|
19408
|
+
const lastAttemptAt = typeof value.lastAttemptAt === "string" ? value.lastAttemptAt : void 0;
|
|
19409
|
+
return {
|
|
19410
|
+
id,
|
|
19411
|
+
requestId,
|
|
19412
|
+
fromAgentDid,
|
|
19413
|
+
toAgentDid,
|
|
19414
|
+
payload: value.payload,
|
|
19415
|
+
payloadBytes,
|
|
19416
|
+
receivedAt,
|
|
19417
|
+
nextAttemptAt,
|
|
19418
|
+
attemptCount,
|
|
19419
|
+
lastError,
|
|
19420
|
+
lastAttemptAt
|
|
19421
|
+
};
|
|
19422
|
+
}
|
|
19423
|
+
function toDefaultIndexFile() {
|
|
19424
|
+
return {
|
|
19425
|
+
version: INBOUND_INBOX_SCHEMA_VERSION,
|
|
19426
|
+
pendingBytes: 0,
|
|
19427
|
+
pendingByRequestId: {},
|
|
19428
|
+
updatedAt: nowIso2()
|
|
19429
|
+
};
|
|
19430
|
+
}
|
|
19431
|
+
function normalizeIndexFile(raw) {
|
|
19432
|
+
if (!isRecord5(raw)) {
|
|
19433
|
+
throw new Error("Inbound inbox index root must be an object");
|
|
19434
|
+
}
|
|
19435
|
+
const pendingByRequestIdRaw = raw.pendingByRequestId;
|
|
19436
|
+
if (!isRecord5(pendingByRequestIdRaw)) {
|
|
19437
|
+
throw new Error("Inbound inbox index pendingByRequestId must be an object");
|
|
19438
|
+
}
|
|
19439
|
+
const pendingByRequestId = {};
|
|
19440
|
+
let pendingBytes = 0;
|
|
19441
|
+
for (const [requestId, candidate] of Object.entries(pendingByRequestIdRaw)) {
|
|
19442
|
+
const entry = parsePendingItem(candidate);
|
|
19443
|
+
if (entry === void 0 || entry.requestId !== requestId) {
|
|
19444
|
+
continue;
|
|
19445
|
+
}
|
|
19446
|
+
pendingByRequestId[requestId] = entry;
|
|
19447
|
+
pendingBytes += entry.payloadBytes;
|
|
19448
|
+
}
|
|
19449
|
+
return {
|
|
19450
|
+
version: typeof raw.version === "number" && Number.isFinite(raw.version) ? raw.version : INBOUND_INBOX_SCHEMA_VERSION,
|
|
19451
|
+
pendingBytes,
|
|
19452
|
+
pendingByRequestId,
|
|
19453
|
+
updatedAt: typeof raw.updatedAt === "string" && raw.updatedAt.trim().length > 0 ? raw.updatedAt : nowIso2()
|
|
19454
|
+
};
|
|
19455
|
+
}
|
|
19456
|
+
function toComparableTimeMs(value) {
|
|
19457
|
+
const parsed = Date.parse(value);
|
|
19458
|
+
if (Number.isFinite(parsed)) {
|
|
19459
|
+
return parsed;
|
|
19460
|
+
}
|
|
19461
|
+
return Number.MAX_SAFE_INTEGER;
|
|
19462
|
+
}
|
|
19463
|
+
var ConnectorInboundInbox = class {
|
|
19464
|
+
agentName;
|
|
19465
|
+
eventsPath;
|
|
19466
|
+
indexPath;
|
|
19467
|
+
maxPendingBytes;
|
|
19468
|
+
maxPendingMessages;
|
|
19469
|
+
inboxDir;
|
|
19470
|
+
writeChain = Promise.resolve();
|
|
19471
|
+
constructor(options) {
|
|
19472
|
+
this.agentName = options.agentName;
|
|
19473
|
+
this.inboxDir = join4(
|
|
19474
|
+
options.configDir,
|
|
19475
|
+
"agents",
|
|
19476
|
+
this.agentName,
|
|
19477
|
+
INBOUND_INBOX_DIR_NAME
|
|
19478
|
+
);
|
|
19479
|
+
this.indexPath = join4(this.inboxDir, INBOUND_INBOX_INDEX_FILE_NAME);
|
|
19480
|
+
this.eventsPath = join4(this.inboxDir, INBOUND_INBOX_EVENTS_FILE_NAME);
|
|
19481
|
+
this.maxPendingBytes = options.maxPendingBytes;
|
|
19482
|
+
this.maxPendingMessages = options.maxPendingMessages;
|
|
19483
|
+
}
|
|
19484
|
+
async enqueue(frame) {
|
|
19485
|
+
return await this.withWriteLock(async () => {
|
|
19486
|
+
const index = await this.loadIndex();
|
|
19487
|
+
const existing = index.pendingByRequestId[frame.id];
|
|
19488
|
+
if (existing !== void 0) {
|
|
19489
|
+
await this.appendEvent({
|
|
19490
|
+
type: "inbound_duplicate",
|
|
19491
|
+
requestId: frame.id
|
|
19492
|
+
});
|
|
19493
|
+
return {
|
|
19494
|
+
accepted: true,
|
|
19495
|
+
duplicate: true,
|
|
19496
|
+
pendingCount: Object.keys(index.pendingByRequestId).length
|
|
19497
|
+
};
|
|
19498
|
+
}
|
|
19499
|
+
const payloadBytes = Buffer.byteLength(
|
|
19500
|
+
JSON.stringify(frame.payload ?? null),
|
|
19501
|
+
"utf8"
|
|
19502
|
+
);
|
|
19503
|
+
const pendingCount = Object.keys(index.pendingByRequestId).length;
|
|
19504
|
+
if (pendingCount >= this.maxPendingMessages) {
|
|
19505
|
+
return {
|
|
19506
|
+
accepted: false,
|
|
19507
|
+
duplicate: false,
|
|
19508
|
+
pendingCount,
|
|
19509
|
+
reason: "connector inbound inbox is full (message cap reached)"
|
|
19510
|
+
};
|
|
19511
|
+
}
|
|
19512
|
+
if (index.pendingBytes + payloadBytes > this.maxPendingBytes) {
|
|
19513
|
+
return {
|
|
19514
|
+
accepted: false,
|
|
19515
|
+
duplicate: false,
|
|
19516
|
+
pendingCount,
|
|
19517
|
+
reason: "connector inbound inbox is full (byte cap reached)"
|
|
19518
|
+
};
|
|
19519
|
+
}
|
|
19520
|
+
const pendingItem = {
|
|
19521
|
+
id: frame.id,
|
|
19522
|
+
requestId: frame.id,
|
|
19523
|
+
fromAgentDid: frame.fromAgentDid,
|
|
19524
|
+
toAgentDid: frame.toAgentDid,
|
|
19525
|
+
payload: frame.payload,
|
|
19526
|
+
payloadBytes,
|
|
19527
|
+
receivedAt: nowIso2(),
|
|
19528
|
+
nextAttemptAt: nowIso2(),
|
|
19529
|
+
attemptCount: 0
|
|
19530
|
+
};
|
|
19531
|
+
index.pendingByRequestId[pendingItem.requestId] = pendingItem;
|
|
19532
|
+
index.pendingBytes += pendingItem.payloadBytes;
|
|
19533
|
+
index.updatedAt = nowIso2();
|
|
19534
|
+
await this.saveIndex(index);
|
|
19535
|
+
await this.appendEvent({
|
|
19536
|
+
type: "inbound_persisted",
|
|
19537
|
+
requestId: pendingItem.requestId,
|
|
19538
|
+
details: {
|
|
19539
|
+
payloadBytes,
|
|
19540
|
+
fromAgentDid: pendingItem.fromAgentDid,
|
|
19541
|
+
toAgentDid: pendingItem.toAgentDid
|
|
19542
|
+
}
|
|
19543
|
+
});
|
|
19544
|
+
return {
|
|
19545
|
+
accepted: true,
|
|
19546
|
+
duplicate: false,
|
|
19547
|
+
pendingCount: Object.keys(index.pendingByRequestId).length
|
|
19548
|
+
};
|
|
19549
|
+
});
|
|
19550
|
+
}
|
|
19551
|
+
async listDuePending(input) {
|
|
19552
|
+
const index = await this.loadIndex();
|
|
19553
|
+
const due = Object.values(index.pendingByRequestId).filter((item) => toComparableTimeMs(item.nextAttemptAt) <= input.nowMs).sort((left, right) => {
|
|
19554
|
+
const leftNext = toComparableTimeMs(left.nextAttemptAt);
|
|
19555
|
+
const rightNext = toComparableTimeMs(right.nextAttemptAt);
|
|
19556
|
+
if (leftNext !== rightNext) {
|
|
19557
|
+
return leftNext - rightNext;
|
|
19558
|
+
}
|
|
19559
|
+
return toComparableTimeMs(left.receivedAt) - toComparableTimeMs(right.receivedAt);
|
|
19560
|
+
});
|
|
19561
|
+
return due.slice(0, Math.max(1, input.limit));
|
|
19562
|
+
}
|
|
19563
|
+
async markDelivered(requestId) {
|
|
19564
|
+
await this.withWriteLock(async () => {
|
|
19565
|
+
const index = await this.loadIndex();
|
|
19566
|
+
const entry = index.pendingByRequestId[requestId];
|
|
19567
|
+
if (entry === void 0) {
|
|
19568
|
+
return;
|
|
19569
|
+
}
|
|
19570
|
+
delete index.pendingByRequestId[requestId];
|
|
19571
|
+
index.pendingBytes = Math.max(0, index.pendingBytes - entry.payloadBytes);
|
|
19572
|
+
index.updatedAt = nowIso2();
|
|
19573
|
+
await this.saveIndex(index);
|
|
19574
|
+
await this.appendEvent({
|
|
19575
|
+
type: "replay_succeeded",
|
|
19576
|
+
requestId
|
|
19577
|
+
});
|
|
19578
|
+
});
|
|
19579
|
+
}
|
|
19580
|
+
async markReplayFailure(input) {
|
|
19581
|
+
await this.withWriteLock(async () => {
|
|
19582
|
+
const index = await this.loadIndex();
|
|
19583
|
+
const entry = index.pendingByRequestId[input.requestId];
|
|
19584
|
+
if (entry === void 0) {
|
|
19585
|
+
return;
|
|
19586
|
+
}
|
|
19587
|
+
entry.attemptCount += 1;
|
|
19588
|
+
entry.lastError = input.errorMessage;
|
|
19589
|
+
entry.lastAttemptAt = nowIso2();
|
|
19590
|
+
entry.nextAttemptAt = input.nextAttemptAt;
|
|
19591
|
+
index.updatedAt = nowIso2();
|
|
19592
|
+
await this.saveIndex(index);
|
|
19593
|
+
await this.appendEvent({
|
|
19594
|
+
type: "replay_failed",
|
|
19595
|
+
requestId: input.requestId,
|
|
19596
|
+
details: {
|
|
19597
|
+
attemptCount: entry.attemptCount,
|
|
19598
|
+
nextAttemptAt: input.nextAttemptAt,
|
|
19599
|
+
errorMessage: input.errorMessage
|
|
19600
|
+
}
|
|
19601
|
+
});
|
|
19602
|
+
});
|
|
19603
|
+
}
|
|
19604
|
+
async pruneDelivered() {
|
|
19605
|
+
await this.withWriteLock(async () => {
|
|
19606
|
+
const index = await this.loadIndex();
|
|
19607
|
+
const beforeCount = Object.keys(index.pendingByRequestId).length;
|
|
19608
|
+
if (beforeCount === 0) {
|
|
19609
|
+
return;
|
|
19610
|
+
}
|
|
19611
|
+
const after = {};
|
|
19612
|
+
let pendingBytes = 0;
|
|
19613
|
+
for (const [requestId, entry] of Object.entries(
|
|
19614
|
+
index.pendingByRequestId
|
|
19615
|
+
)) {
|
|
19616
|
+
if (entry.attemptCount < 0) {
|
|
19617
|
+
continue;
|
|
19618
|
+
}
|
|
19619
|
+
after[requestId] = entry;
|
|
19620
|
+
pendingBytes += entry.payloadBytes;
|
|
19621
|
+
}
|
|
19622
|
+
index.pendingByRequestId = after;
|
|
19623
|
+
index.pendingBytes = pendingBytes;
|
|
19624
|
+
index.updatedAt = nowIso2();
|
|
19625
|
+
await this.saveIndex(index);
|
|
19626
|
+
await this.appendEvent({
|
|
19627
|
+
type: "inbox_pruned",
|
|
19628
|
+
details: {
|
|
19629
|
+
beforeCount,
|
|
19630
|
+
afterCount: Object.keys(after).length
|
|
19631
|
+
}
|
|
19632
|
+
});
|
|
19633
|
+
});
|
|
19634
|
+
}
|
|
19635
|
+
async getSnapshot() {
|
|
19636
|
+
const index = await this.loadIndex();
|
|
19637
|
+
const entries = Object.values(index.pendingByRequestId);
|
|
19638
|
+
if (entries.length === 0) {
|
|
19639
|
+
return {
|
|
19640
|
+
pendingCount: 0,
|
|
19641
|
+
pendingBytes: index.pendingBytes
|
|
19642
|
+
};
|
|
19643
|
+
}
|
|
19644
|
+
entries.sort((left, right) => {
|
|
19645
|
+
return toComparableTimeMs(left.receivedAt) - toComparableTimeMs(right.receivedAt);
|
|
19646
|
+
});
|
|
19647
|
+
const nextAttemptAt = entries.map((entry) => entry.nextAttemptAt).sort(
|
|
19648
|
+
(left, right) => toComparableTimeMs(left) - toComparableTimeMs(right)
|
|
19649
|
+
)[0];
|
|
19650
|
+
return {
|
|
19651
|
+
pendingCount: entries.length,
|
|
19652
|
+
pendingBytes: index.pendingBytes,
|
|
19653
|
+
oldestPendingAt: entries[0]?.receivedAt,
|
|
19654
|
+
nextAttemptAt
|
|
19655
|
+
};
|
|
19656
|
+
}
|
|
19657
|
+
async withWriteLock(fn) {
|
|
19658
|
+
const previous = this.writeChain;
|
|
19659
|
+
let release;
|
|
19660
|
+
this.writeChain = new Promise((resolve2) => {
|
|
19661
|
+
release = resolve2;
|
|
19662
|
+
});
|
|
19663
|
+
await previous;
|
|
19664
|
+
try {
|
|
19665
|
+
return await fn();
|
|
19666
|
+
} finally {
|
|
19667
|
+
release?.();
|
|
19668
|
+
}
|
|
19669
|
+
}
|
|
19670
|
+
async loadIndex() {
|
|
19671
|
+
await mkdir3(this.inboxDir, { recursive: true });
|
|
19672
|
+
let raw;
|
|
19673
|
+
try {
|
|
19674
|
+
raw = await readFile3(this.indexPath, "utf8");
|
|
19675
|
+
} catch (error48) {
|
|
19676
|
+
if (error48 && typeof error48 === "object" && "code" in error48 && error48.code === "ENOENT") {
|
|
19677
|
+
return toDefaultIndexFile();
|
|
19678
|
+
}
|
|
19679
|
+
throw error48;
|
|
19680
|
+
}
|
|
19681
|
+
if (raw.trim().length === 0) {
|
|
19682
|
+
return toDefaultIndexFile();
|
|
19683
|
+
}
|
|
19684
|
+
const parsed = JSON.parse(raw);
|
|
19685
|
+
return normalizeIndexFile(parsed);
|
|
19686
|
+
}
|
|
19687
|
+
async saveIndex(index) {
|
|
19688
|
+
await mkdir3(dirname2(this.indexPath), { recursive: true });
|
|
19689
|
+
const payload = {
|
|
19690
|
+
...index,
|
|
19691
|
+
version: INBOUND_INBOX_SCHEMA_VERSION,
|
|
19692
|
+
updatedAt: nowIso2()
|
|
19693
|
+
};
|
|
19694
|
+
const tmpPath = `${this.indexPath}.tmp-${Date.now()}`;
|
|
19695
|
+
await writeFile3(tmpPath, `${JSON.stringify(payload, null, 2)}
|
|
19696
|
+
`, "utf8");
|
|
19697
|
+
await rename2(tmpPath, this.indexPath);
|
|
19698
|
+
}
|
|
19699
|
+
async appendEvent(event) {
|
|
19700
|
+
await mkdir3(dirname2(this.eventsPath), { recursive: true });
|
|
19701
|
+
await appendFile(
|
|
19702
|
+
this.eventsPath,
|
|
19703
|
+
`${JSON.stringify({ ...event, at: nowIso2() })}
|
|
19704
|
+
`,
|
|
19705
|
+
"utf8"
|
|
19706
|
+
);
|
|
19707
|
+
}
|
|
19708
|
+
};
|
|
19709
|
+
function createConnectorInboundInbox(options) {
|
|
19710
|
+
return new ConnectorInboundInbox(options);
|
|
19711
|
+
}
|
|
19712
|
+
|
|
19326
19713
|
// ../../packages/connector/src/runtime.ts
|
|
19327
19714
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
19328
|
-
import { mkdir as
|
|
19715
|
+
import { mkdir as mkdir4, readFile as readFile4, rename as rename3, writeFile as writeFile4 } from "fs/promises";
|
|
19329
19716
|
import {
|
|
19330
19717
|
createServer
|
|
19331
19718
|
} from "http";
|
|
19332
|
-
import { dirname as
|
|
19719
|
+
import { dirname as dirname3, join as join5 } from "path";
|
|
19333
19720
|
import { WebSocket as NodeWebSocket } from "ws";
|
|
19334
19721
|
var REGISTRY_AUTH_FILENAME = "registry-auth.json";
|
|
19335
19722
|
var AGENTS_DIR_NAME2 = "agents";
|
|
@@ -19337,7 +19724,7 @@ var REFRESH_SINGLE_FLIGHT_PREFIX = "connector-runtime";
|
|
|
19337
19724
|
var NONCE_SIZE = 16;
|
|
19338
19725
|
var MAX_OUTBOUND_BODY_BYTES = 1024 * 1024;
|
|
19339
19726
|
var ACCESS_TOKEN_REFRESH_SKEW_MS = 3e4;
|
|
19340
|
-
function
|
|
19727
|
+
function isRecord6(value) {
|
|
19341
19728
|
return typeof value === "object" && value !== null;
|
|
19342
19729
|
}
|
|
19343
19730
|
function toPathWithQuery2(url2) {
|
|
@@ -19405,6 +19792,121 @@ function resolveOpenclawHookToken(input) {
|
|
|
19405
19792
|
}
|
|
19406
19793
|
return value;
|
|
19407
19794
|
}
|
|
19795
|
+
function toOpenclawHookUrl2(baseUrl, hookPath) {
|
|
19796
|
+
const normalizedBase = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
19797
|
+
const normalizedHookPath = hookPath.startsWith("/") ? hookPath.slice(1) : hookPath;
|
|
19798
|
+
return new URL(normalizedHookPath, normalizedBase).toString();
|
|
19799
|
+
}
|
|
19800
|
+
function parsePositiveIntEnv(key, fallback, minimum = 1) {
|
|
19801
|
+
const raw = process.env[key]?.trim();
|
|
19802
|
+
if (!raw) {
|
|
19803
|
+
return fallback;
|
|
19804
|
+
}
|
|
19805
|
+
const parsed = Number.parseInt(raw, 10);
|
|
19806
|
+
if (!Number.isFinite(parsed) || parsed < minimum) {
|
|
19807
|
+
return fallback;
|
|
19808
|
+
}
|
|
19809
|
+
return parsed;
|
|
19810
|
+
}
|
|
19811
|
+
function sanitizeErrorReason2(error48) {
|
|
19812
|
+
if (!(error48 instanceof Error)) {
|
|
19813
|
+
return "Unknown error";
|
|
19814
|
+
}
|
|
19815
|
+
return error48.message.trim().slice(0, 240) || "Unknown error";
|
|
19816
|
+
}
|
|
19817
|
+
var LocalOpenclawDeliveryError2 = class extends Error {
|
|
19818
|
+
retryable;
|
|
19819
|
+
constructor(input) {
|
|
19820
|
+
super(input.message);
|
|
19821
|
+
this.name = "LocalOpenclawDeliveryError";
|
|
19822
|
+
this.retryable = input.retryable;
|
|
19823
|
+
}
|
|
19824
|
+
};
|
|
19825
|
+
function loadInboundReplayPolicy() {
|
|
19826
|
+
const retryBackoffFactor = Number.parseFloat(
|
|
19827
|
+
process.env.CONNECTOR_INBOUND_RETRY_BACKOFF_FACTOR ?? ""
|
|
19828
|
+
);
|
|
19829
|
+
return {
|
|
19830
|
+
inboxMaxMessages: parsePositiveIntEnv(
|
|
19831
|
+
"CONNECTOR_INBOUND_INBOX_MAX_MESSAGES",
|
|
19832
|
+
DEFAULT_CONNECTOR_INBOUND_INBOX_MAX_MESSAGES
|
|
19833
|
+
),
|
|
19834
|
+
inboxMaxBytes: parsePositiveIntEnv(
|
|
19835
|
+
"CONNECTOR_INBOUND_INBOX_MAX_BYTES",
|
|
19836
|
+
DEFAULT_CONNECTOR_INBOUND_INBOX_MAX_BYTES
|
|
19837
|
+
),
|
|
19838
|
+
replayIntervalMs: parsePositiveIntEnv(
|
|
19839
|
+
"CONNECTOR_INBOUND_REPLAY_INTERVAL_MS",
|
|
19840
|
+
DEFAULT_CONNECTOR_INBOUND_REPLAY_INTERVAL_MS
|
|
19841
|
+
),
|
|
19842
|
+
batchSize: parsePositiveIntEnv(
|
|
19843
|
+
"CONNECTOR_INBOUND_REPLAY_BATCH_SIZE",
|
|
19844
|
+
DEFAULT_CONNECTOR_INBOUND_REPLAY_BATCH_SIZE
|
|
19845
|
+
),
|
|
19846
|
+
retryInitialDelayMs: parsePositiveIntEnv(
|
|
19847
|
+
"CONNECTOR_INBOUND_RETRY_INITIAL_DELAY_MS",
|
|
19848
|
+
DEFAULT_CONNECTOR_INBOUND_RETRY_INITIAL_DELAY_MS
|
|
19849
|
+
),
|
|
19850
|
+
retryMaxDelayMs: parsePositiveIntEnv(
|
|
19851
|
+
"CONNECTOR_INBOUND_RETRY_MAX_DELAY_MS",
|
|
19852
|
+
DEFAULT_CONNECTOR_INBOUND_RETRY_MAX_DELAY_MS
|
|
19853
|
+
),
|
|
19854
|
+
retryBackoffFactor: Number.isFinite(retryBackoffFactor) && retryBackoffFactor >= 1 ? retryBackoffFactor : DEFAULT_CONNECTOR_INBOUND_RETRY_BACKOFF_FACTOR
|
|
19855
|
+
};
|
|
19856
|
+
}
|
|
19857
|
+
function computeReplayDelayMs(input) {
|
|
19858
|
+
const exponent = Math.max(0, input.attemptCount - 1);
|
|
19859
|
+
const delay = Math.min(
|
|
19860
|
+
input.policy.retryMaxDelayMs,
|
|
19861
|
+
Math.floor(
|
|
19862
|
+
input.policy.retryInitialDelayMs * input.policy.retryBackoffFactor ** exponent
|
|
19863
|
+
)
|
|
19864
|
+
);
|
|
19865
|
+
return Math.max(1, delay);
|
|
19866
|
+
}
|
|
19867
|
+
async function deliverToOpenclawHook(input) {
|
|
19868
|
+
const controller = new AbortController();
|
|
19869
|
+
const timeoutHandle = setTimeout(() => {
|
|
19870
|
+
controller.abort();
|
|
19871
|
+
}, DEFAULT_OPENCLAW_DELIVER_TIMEOUT_MS);
|
|
19872
|
+
const headers = {
|
|
19873
|
+
"content-type": "application/json",
|
|
19874
|
+
"x-request-id": input.requestId
|
|
19875
|
+
};
|
|
19876
|
+
if (input.openclawHookToken !== void 0) {
|
|
19877
|
+
headers["x-openclaw-token"] = input.openclawHookToken;
|
|
19878
|
+
}
|
|
19879
|
+
try {
|
|
19880
|
+
const response = await input.fetchImpl(input.openclawHookUrl, {
|
|
19881
|
+
method: "POST",
|
|
19882
|
+
headers,
|
|
19883
|
+
body: JSON.stringify(input.payload),
|
|
19884
|
+
signal: controller.signal
|
|
19885
|
+
});
|
|
19886
|
+
if (!response.ok) {
|
|
19887
|
+
throw new LocalOpenclawDeliveryError2({
|
|
19888
|
+
message: `Local OpenClaw hook rejected payload with status ${response.status}`,
|
|
19889
|
+
retryable: response.status >= 500 || response.status === 404 || response.status === 429
|
|
19890
|
+
});
|
|
19891
|
+
}
|
|
19892
|
+
} catch (error48) {
|
|
19893
|
+
if (error48 instanceof Error && error48.name === "AbortError") {
|
|
19894
|
+
throw new LocalOpenclawDeliveryError2({
|
|
19895
|
+
message: "Local OpenClaw hook request timed out",
|
|
19896
|
+
retryable: true
|
|
19897
|
+
});
|
|
19898
|
+
}
|
|
19899
|
+
if (error48 instanceof LocalOpenclawDeliveryError2) {
|
|
19900
|
+
throw error48;
|
|
19901
|
+
}
|
|
19902
|
+
throw new LocalOpenclawDeliveryError2({
|
|
19903
|
+
message: sanitizeErrorReason2(error48),
|
|
19904
|
+
retryable: true
|
|
19905
|
+
});
|
|
19906
|
+
} finally {
|
|
19907
|
+
clearTimeout(timeoutHandle);
|
|
19908
|
+
}
|
|
19909
|
+
}
|
|
19408
19910
|
function toInitialAuthBundle(credentials) {
|
|
19409
19911
|
return {
|
|
19410
19912
|
tokenType: "Bearer",
|
|
@@ -19432,7 +19934,7 @@ function shouldRefreshAccessToken(auth, nowMs) {
|
|
|
19432
19934
|
return expiresAtMs <= nowMs + ACCESS_TOKEN_REFRESH_SKEW_MS;
|
|
19433
19935
|
}
|
|
19434
19936
|
function parseOutboundRelayRequest(payload) {
|
|
19435
|
-
if (!
|
|
19937
|
+
if (!isRecord6(payload)) {
|
|
19436
19938
|
throw new AppError({
|
|
19437
19939
|
code: "CONNECTOR_OUTBOUND_INVALID_REQUEST",
|
|
19438
19940
|
message: "Outbound relay request must be an object",
|
|
@@ -19490,20 +19992,20 @@ function createWebSocketFactory() {
|
|
|
19490
19992
|
};
|
|
19491
19993
|
}
|
|
19492
19994
|
async function writeRegistryAuthAtomic(input) {
|
|
19493
|
-
const targetPath =
|
|
19995
|
+
const targetPath = join5(
|
|
19494
19996
|
input.configDir,
|
|
19495
19997
|
AGENTS_DIR_NAME2,
|
|
19496
19998
|
input.agentName,
|
|
19497
19999
|
REGISTRY_AUTH_FILENAME
|
|
19498
20000
|
);
|
|
19499
20001
|
const tmpPath = `${targetPath}.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
19500
|
-
await
|
|
19501
|
-
await
|
|
20002
|
+
await mkdir4(dirname3(targetPath), { recursive: true });
|
|
20003
|
+
await writeFile4(tmpPath, `${JSON.stringify(input.auth, null, 2)}
|
|
19502
20004
|
`, "utf8");
|
|
19503
|
-
await
|
|
20005
|
+
await rename3(tmpPath, targetPath);
|
|
19504
20006
|
}
|
|
19505
20007
|
function parseRegistryAuthFromDisk(payload) {
|
|
19506
|
-
if (!
|
|
20008
|
+
if (!isRecord6(payload)) {
|
|
19507
20009
|
return void 0;
|
|
19508
20010
|
}
|
|
19509
20011
|
const tokenType = payload.tokenType;
|
|
@@ -19523,7 +20025,7 @@ function parseRegistryAuthFromDisk(payload) {
|
|
|
19523
20025
|
};
|
|
19524
20026
|
}
|
|
19525
20027
|
async function readRegistryAuthFromDisk(input) {
|
|
19526
|
-
const authPath =
|
|
20028
|
+
const authPath = join5(
|
|
19527
20029
|
input.configDir,
|
|
19528
20030
|
AGENTS_DIR_NAME2,
|
|
19529
20031
|
input.agentName,
|
|
@@ -19531,7 +20033,7 @@ async function readRegistryAuthFromDisk(input) {
|
|
|
19531
20033
|
);
|
|
19532
20034
|
let raw;
|
|
19533
20035
|
try {
|
|
19534
|
-
raw = await
|
|
20036
|
+
raw = await readFile4(authPath, "utf8");
|
|
19535
20037
|
} catch (error48) {
|
|
19536
20038
|
if (error48 && typeof error48 === "object" && "code" in error48 && error48.code === "ENOENT") {
|
|
19537
20039
|
return void 0;
|
|
@@ -19660,6 +20162,23 @@ async function startConnectorRuntime(input) {
|
|
|
19660
20162
|
await refreshCurrentAuthIfNeeded();
|
|
19661
20163
|
const wsUrl = normalizeWebSocketUrl(input.proxyWebsocketUrl);
|
|
19662
20164
|
const wsParsed = new URL(wsUrl);
|
|
20165
|
+
const openclawBaseUrl = resolveOpenclawBaseUrl(input.openclawBaseUrl);
|
|
20166
|
+
const openclawHookPath = resolveOpenclawHookPath(input.openclawHookPath);
|
|
20167
|
+
const openclawHookToken = resolveOpenclawHookToken(input.openclawHookToken);
|
|
20168
|
+
const openclawHookUrl = toOpenclawHookUrl2(openclawBaseUrl, openclawHookPath);
|
|
20169
|
+
const inboundReplayPolicy = loadInboundReplayPolicy();
|
|
20170
|
+
const inboundInbox = createConnectorInboundInbox({
|
|
20171
|
+
configDir: input.configDir,
|
|
20172
|
+
agentName: input.agentName,
|
|
20173
|
+
maxPendingMessages: inboundReplayPolicy.inboxMaxMessages,
|
|
20174
|
+
maxPendingBytes: inboundReplayPolicy.inboxMaxBytes
|
|
20175
|
+
});
|
|
20176
|
+
const inboundReplayStatus = {
|
|
20177
|
+
replayerActive: false
|
|
20178
|
+
};
|
|
20179
|
+
let runtimeStopping = false;
|
|
20180
|
+
let replayInFlight = false;
|
|
20181
|
+
let replayIntervalHandle;
|
|
19663
20182
|
const resolveUpgradeHeaders = async () => {
|
|
19664
20183
|
await refreshCurrentAuthIfNeeded();
|
|
19665
20184
|
return buildUpgradeHeaders({
|
|
@@ -19669,14 +20188,108 @@ async function startConnectorRuntime(input) {
|
|
|
19669
20188
|
secretKey
|
|
19670
20189
|
});
|
|
19671
20190
|
};
|
|
20191
|
+
const replayPendingInboundMessages = async () => {
|
|
20192
|
+
if (runtimeStopping || replayInFlight) {
|
|
20193
|
+
return;
|
|
20194
|
+
}
|
|
20195
|
+
replayInFlight = true;
|
|
20196
|
+
inboundReplayStatus.replayerActive = true;
|
|
20197
|
+
try {
|
|
20198
|
+
const dueItems = await inboundInbox.listDuePending({
|
|
20199
|
+
nowMs: Date.now(),
|
|
20200
|
+
limit: inboundReplayPolicy.batchSize
|
|
20201
|
+
});
|
|
20202
|
+
for (const pending of dueItems) {
|
|
20203
|
+
inboundReplayStatus.lastAttemptAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
20204
|
+
try {
|
|
20205
|
+
await deliverToOpenclawHook({
|
|
20206
|
+
fetchImpl,
|
|
20207
|
+
openclawHookUrl,
|
|
20208
|
+
openclawHookToken,
|
|
20209
|
+
requestId: pending.requestId,
|
|
20210
|
+
payload: pending.payload
|
|
20211
|
+
});
|
|
20212
|
+
await inboundInbox.markDelivered(pending.requestId);
|
|
20213
|
+
inboundReplayStatus.lastReplayAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
20214
|
+
inboundReplayStatus.lastReplayError = void 0;
|
|
20215
|
+
inboundReplayStatus.lastAttemptStatus = "ok";
|
|
20216
|
+
logger12.info("connector.inbound.replay_succeeded", {
|
|
20217
|
+
requestId: pending.requestId,
|
|
20218
|
+
attemptCount: pending.attemptCount + 1
|
|
20219
|
+
});
|
|
20220
|
+
} catch (error48) {
|
|
20221
|
+
const reason = sanitizeErrorReason2(error48);
|
|
20222
|
+
const retryable = error48 instanceof LocalOpenclawDeliveryError2 ? error48.retryable : true;
|
|
20223
|
+
const nextAttemptAt = new Date(
|
|
20224
|
+
Date.now() + computeReplayDelayMs({
|
|
20225
|
+
attemptCount: pending.attemptCount + 1,
|
|
20226
|
+
policy: inboundReplayPolicy
|
|
20227
|
+
}) * (retryable ? 1 : 10)
|
|
20228
|
+
).toISOString();
|
|
20229
|
+
await inboundInbox.markReplayFailure({
|
|
20230
|
+
requestId: pending.requestId,
|
|
20231
|
+
errorMessage: reason,
|
|
20232
|
+
nextAttemptAt
|
|
20233
|
+
});
|
|
20234
|
+
inboundReplayStatus.lastReplayError = reason;
|
|
20235
|
+
inboundReplayStatus.lastAttemptStatus = "failed";
|
|
20236
|
+
logger12.warn("connector.inbound.replay_failed", {
|
|
20237
|
+
requestId: pending.requestId,
|
|
20238
|
+
attemptCount: pending.attemptCount + 1,
|
|
20239
|
+
retryable,
|
|
20240
|
+
nextAttemptAt,
|
|
20241
|
+
reason
|
|
20242
|
+
});
|
|
20243
|
+
}
|
|
20244
|
+
}
|
|
20245
|
+
} finally {
|
|
20246
|
+
replayInFlight = false;
|
|
20247
|
+
inboundReplayStatus.replayerActive = false;
|
|
20248
|
+
}
|
|
20249
|
+
};
|
|
20250
|
+
const readInboundReplayView = async () => {
|
|
20251
|
+
const pending = await inboundInbox.getSnapshot();
|
|
20252
|
+
return {
|
|
20253
|
+
pending,
|
|
20254
|
+
replayerActive: inboundReplayStatus.replayerActive || replayInFlight,
|
|
20255
|
+
lastReplayAt: inboundReplayStatus.lastReplayAt,
|
|
20256
|
+
lastReplayError: inboundReplayStatus.lastReplayError,
|
|
20257
|
+
openclawHook: {
|
|
20258
|
+
url: openclawHookUrl,
|
|
20259
|
+
lastAttemptAt: inboundReplayStatus.lastAttemptAt,
|
|
20260
|
+
lastAttemptStatus: inboundReplayStatus.lastAttemptStatus
|
|
20261
|
+
}
|
|
20262
|
+
};
|
|
20263
|
+
};
|
|
19672
20264
|
const connectorClient = new ConnectorClient({
|
|
19673
20265
|
connectorUrl: wsParsed.toString(),
|
|
19674
20266
|
connectionHeadersProvider: resolveUpgradeHeaders,
|
|
19675
|
-
openclawBaseUrl
|
|
19676
|
-
openclawHookPath
|
|
19677
|
-
openclawHookToken
|
|
20267
|
+
openclawBaseUrl,
|
|
20268
|
+
openclawHookPath,
|
|
20269
|
+
openclawHookToken,
|
|
19678
20270
|
fetchImpl,
|
|
19679
20271
|
logger: logger12,
|
|
20272
|
+
inboundDeliverHandler: async (frame) => {
|
|
20273
|
+
const persisted = await inboundInbox.enqueue(frame);
|
|
20274
|
+
if (!persisted.accepted) {
|
|
20275
|
+
logger12.warn("connector.inbound.persist_rejected", {
|
|
20276
|
+
requestId: frame.id,
|
|
20277
|
+
reason: persisted.reason ?? "inbox limit reached",
|
|
20278
|
+
pendingCount: persisted.pendingCount
|
|
20279
|
+
});
|
|
20280
|
+
return {
|
|
20281
|
+
accepted: false,
|
|
20282
|
+
reason: persisted.reason
|
|
20283
|
+
};
|
|
20284
|
+
}
|
|
20285
|
+
logger12.info("connector.inbound.persisted", {
|
|
20286
|
+
requestId: frame.id,
|
|
20287
|
+
duplicate: persisted.duplicate,
|
|
20288
|
+
pendingCount: persisted.pendingCount
|
|
20289
|
+
});
|
|
20290
|
+
void replayPendingInboundMessages();
|
|
20291
|
+
return { accepted: true };
|
|
20292
|
+
},
|
|
19680
20293
|
webSocketFactory: createWebSocketFactory()
|
|
19681
20294
|
});
|
|
19682
20295
|
const outboundBaseUrl = normalizeOutboundBaseUrl(input.outboundBaseUrl);
|
|
@@ -19760,11 +20373,40 @@ async function startConnectorRuntime(input) {
|
|
|
19760
20373
|
writeJson(res, 405, { error: "Method Not Allowed" });
|
|
19761
20374
|
return;
|
|
19762
20375
|
}
|
|
20376
|
+
let inboundReplayView;
|
|
20377
|
+
try {
|
|
20378
|
+
inboundReplayView = await readInboundReplayView();
|
|
20379
|
+
} catch (error48) {
|
|
20380
|
+
logger12.warn("connector.status.inbound_inbox_unavailable", {
|
|
20381
|
+
reason: sanitizeErrorReason2(error48)
|
|
20382
|
+
});
|
|
20383
|
+
writeJson(res, 500, {
|
|
20384
|
+
status: "error",
|
|
20385
|
+
error: {
|
|
20386
|
+
code: "CONNECTOR_INBOUND_INBOX_UNAVAILABLE",
|
|
20387
|
+
message: "Connector inbound inbox status is unavailable"
|
|
20388
|
+
},
|
|
20389
|
+
outboundUrl,
|
|
20390
|
+
websocketUrl: wsUrl,
|
|
20391
|
+
websocketConnected: connectorClient.isConnected()
|
|
20392
|
+
});
|
|
20393
|
+
return;
|
|
20394
|
+
}
|
|
19763
20395
|
writeJson(res, 200, {
|
|
19764
20396
|
status: "ok",
|
|
19765
20397
|
outboundUrl,
|
|
19766
20398
|
websocketUrl: wsUrl,
|
|
19767
|
-
websocketConnected: connectorClient.isConnected()
|
|
20399
|
+
websocketConnected: connectorClient.isConnected(),
|
|
20400
|
+
inboundInbox: {
|
|
20401
|
+
pendingCount: inboundReplayView.pending.pendingCount,
|
|
20402
|
+
pendingBytes: inboundReplayView.pending.pendingBytes,
|
|
20403
|
+
oldestPendingAt: inboundReplayView.pending.oldestPendingAt,
|
|
20404
|
+
nextAttemptAt: inboundReplayView.pending.nextAttemptAt,
|
|
20405
|
+
replayerActive: inboundReplayView.replayerActive,
|
|
20406
|
+
lastReplayAt: inboundReplayView.lastReplayAt,
|
|
20407
|
+
lastReplayError: inboundReplayView.lastReplayError
|
|
20408
|
+
},
|
|
20409
|
+
openclawHook: inboundReplayView.openclawHook
|
|
19768
20410
|
});
|
|
19769
20411
|
return;
|
|
19770
20412
|
}
|
|
@@ -19814,6 +20456,11 @@ async function startConnectorRuntime(input) {
|
|
|
19814
20456
|
stoppedResolve = resolve2;
|
|
19815
20457
|
});
|
|
19816
20458
|
const stop = async () => {
|
|
20459
|
+
runtimeStopping = true;
|
|
20460
|
+
if (replayIntervalHandle !== void 0) {
|
|
20461
|
+
clearInterval(replayIntervalHandle);
|
|
20462
|
+
replayIntervalHandle = void 0;
|
|
20463
|
+
}
|
|
19817
20464
|
connectorClient.disconnect();
|
|
19818
20465
|
await new Promise((resolve2, reject) => {
|
|
19819
20466
|
server.close((error48) => {
|
|
@@ -19838,6 +20485,11 @@ async function startConnectorRuntime(input) {
|
|
|
19838
20485
|
);
|
|
19839
20486
|
});
|
|
19840
20487
|
connectorClient.connect();
|
|
20488
|
+
await inboundInbox.pruneDelivered();
|
|
20489
|
+
void replayPendingInboundMessages();
|
|
20490
|
+
replayIntervalHandle = setInterval(() => {
|
|
20491
|
+
void replayPendingInboundMessages();
|
|
20492
|
+
}, inboundReplayPolicy.replayIntervalMs);
|
|
19841
20493
|
logger12.info("connector.runtime.started", {
|
|
19842
20494
|
outboundUrl,
|
|
19843
20495
|
websocketUrl: wsUrl,
|
|
@@ -19865,11 +20517,11 @@ var OPENCLAW_CONNECTORS_FILE_NAME = "openclaw-connectors.json";
|
|
|
19865
20517
|
var SERVICE_LOG_DIR_NAME = "logs";
|
|
19866
20518
|
var DEFAULT_CONNECTOR_BASE_URL2 = "http://127.0.0.1:19400";
|
|
19867
20519
|
var DEFAULT_CONNECTOR_OUTBOUND_PATH2 = "/v1/outbound";
|
|
19868
|
-
function
|
|
20520
|
+
function isRecord7(value) {
|
|
19869
20521
|
return typeof value === "object" && value !== null;
|
|
19870
20522
|
}
|
|
19871
20523
|
function getErrorCode(error48) {
|
|
19872
|
-
if (!
|
|
20524
|
+
if (!isRecord7(error48)) {
|
|
19873
20525
|
return void 0;
|
|
19874
20526
|
}
|
|
19875
20527
|
return typeof error48.code === "string" ? error48.code : void 0;
|
|
@@ -20008,7 +20660,7 @@ function resolveConnectorBaseUrlFromEnv() {
|
|
|
20008
20660
|
return parseConnectorBaseUrl(value.trim());
|
|
20009
20661
|
}
|
|
20010
20662
|
async function readConnectorAssignedBaseUrl(configDir, agentName, readFileImpl) {
|
|
20011
|
-
const assignmentsPath =
|
|
20663
|
+
const assignmentsPath = join6(configDir, OPENCLAW_CONNECTORS_FILE_NAME);
|
|
20012
20664
|
let raw;
|
|
20013
20665
|
try {
|
|
20014
20666
|
raw = await readFileImpl(assignmentsPath, "utf8");
|
|
@@ -20028,11 +20680,11 @@ async function readConnectorAssignedBaseUrl(configDir, agentName, readFileImpl)
|
|
|
20028
20680
|
{ assignmentsPath }
|
|
20029
20681
|
);
|
|
20030
20682
|
}
|
|
20031
|
-
if (!
|
|
20683
|
+
if (!isRecord7(parsed) || !isRecord7(parsed.agents)) {
|
|
20032
20684
|
return void 0;
|
|
20033
20685
|
}
|
|
20034
20686
|
const entry = parsed.agents[agentName];
|
|
20035
|
-
if (!
|
|
20687
|
+
if (!isRecord7(entry) || typeof entry.connectorBaseUrl !== "string") {
|
|
20036
20688
|
return void 0;
|
|
20037
20689
|
}
|
|
20038
20690
|
return parseConnectorBaseUrl(entry.connectorBaseUrl);
|
|
@@ -20072,7 +20724,7 @@ async function readRequiredTrimmedFile(filePath, label, readFileImpl) {
|
|
|
20072
20724
|
return trimmed;
|
|
20073
20725
|
}
|
|
20074
20726
|
async function readRelayRuntimeConfig(configDir, readFileImpl) {
|
|
20075
|
-
const filePath =
|
|
20727
|
+
const filePath = join6(configDir, OPENCLAW_RELAY_RUNTIME_FILE_NAME);
|
|
20076
20728
|
let raw;
|
|
20077
20729
|
try {
|
|
20078
20730
|
raw = await readFileImpl(filePath, "utf8");
|
|
@@ -20088,7 +20740,7 @@ async function readRelayRuntimeConfig(configDir, readFileImpl) {
|
|
|
20088
20740
|
} catch {
|
|
20089
20741
|
return void 0;
|
|
20090
20742
|
}
|
|
20091
|
-
if (!
|
|
20743
|
+
if (!isRecord7(parsed)) {
|
|
20092
20744
|
return void 0;
|
|
20093
20745
|
}
|
|
20094
20746
|
const openclawHookToken = typeof parsed.openclawHookToken === "string" && parsed.openclawHookToken.trim().length > 0 ? parsed.openclawHookToken.trim() : void 0;
|
|
@@ -20106,7 +20758,7 @@ function parseJsonRecord(value, code, message2) {
|
|
|
20106
20758
|
} catch {
|
|
20107
20759
|
throw createCliError4(code, message2);
|
|
20108
20760
|
}
|
|
20109
|
-
if (!
|
|
20761
|
+
if (!isRecord7(parsed)) {
|
|
20110
20762
|
throw createCliError4(code, message2);
|
|
20111
20763
|
}
|
|
20112
20764
|
return parsed;
|
|
@@ -20146,7 +20798,7 @@ async function loadDefaultConnectorModule() {
|
|
|
20146
20798
|
};
|
|
20147
20799
|
}
|
|
20148
20800
|
function resolveWaitPromise(runtime) {
|
|
20149
|
-
if (!runtime || !
|
|
20801
|
+
if (!runtime || !isRecord7(runtime)) {
|
|
20150
20802
|
return void 0;
|
|
20151
20803
|
}
|
|
20152
20804
|
if (typeof runtime.waitUntilStopped === "function") {
|
|
@@ -20211,7 +20863,7 @@ function buildConnectorStartArgs(agentName, commandOptions) {
|
|
|
20211
20863
|
}
|
|
20212
20864
|
function resolveCliEntryPath(resolveCurrentModulePathImpl) {
|
|
20213
20865
|
const modulePath = resolveCurrentModulePathImpl?.() ?? fileURLToPath(import.meta.url);
|
|
20214
|
-
return
|
|
20866
|
+
return join6(dirname4(modulePath), "..", "bin.js");
|
|
20215
20867
|
}
|
|
20216
20868
|
function escapeXml(value) {
|
|
20217
20869
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
@@ -20281,7 +20933,7 @@ function resolveServiceDependencies(dependencies) {
|
|
|
20281
20933
|
execFileImpl,
|
|
20282
20934
|
getConfigDirImpl: dependencies.getConfigDirImpl ?? getConfigDir,
|
|
20283
20935
|
getHomeDirImpl: dependencies.getHomeDirImpl ?? homedir2,
|
|
20284
|
-
mkdirImpl: dependencies.mkdirImpl ??
|
|
20936
|
+
mkdirImpl: dependencies.mkdirImpl ?? mkdir5,
|
|
20285
20937
|
removeFileImpl: dependencies.removeFileImpl ?? rm,
|
|
20286
20938
|
resolveCurrentModulePathImpl: dependencies.resolveCurrentModulePathImpl,
|
|
20287
20939
|
resolveCurrentPlatformImpl: dependencies.resolveCurrentPlatformImpl ?? (() => process.platform),
|
|
@@ -20295,7 +20947,7 @@ function resolveServiceDependencies(dependencies) {
|
|
|
20295
20947
|
return process.getuid();
|
|
20296
20948
|
}),
|
|
20297
20949
|
resolveNodeExecPathImpl: dependencies.resolveNodeExecPathImpl ?? (() => process.execPath),
|
|
20298
|
-
writeFileImpl: dependencies.writeFileImpl ??
|
|
20950
|
+
writeFileImpl: dependencies.writeFileImpl ?? writeFile5
|
|
20299
20951
|
};
|
|
20300
20952
|
}
|
|
20301
20953
|
async function installConnectorServiceForAgent(agentName, commandOptions = {}, dependencies = {}) {
|
|
@@ -20309,7 +20961,7 @@ async function installConnectorServiceForAgent(agentName, commandOptions = {}, d
|
|
|
20309
20961
|
);
|
|
20310
20962
|
const configDir = serviceDependencies.getConfigDirImpl();
|
|
20311
20963
|
const homeDir = serviceDependencies.getHomeDirImpl();
|
|
20312
|
-
const logsDir =
|
|
20964
|
+
const logsDir = join6(configDir, SERVICE_LOG_DIR_NAME);
|
|
20313
20965
|
const serviceName = sanitizeServiceSegment(
|
|
20314
20966
|
`clawdentity-connector-${agentName}`
|
|
20315
20967
|
);
|
|
@@ -20319,12 +20971,12 @@ async function installConnectorServiceForAgent(agentName, commandOptions = {}, d
|
|
|
20319
20971
|
resolveCliEntryPath(serviceDependencies.resolveCurrentModulePathImpl),
|
|
20320
20972
|
...startArgs
|
|
20321
20973
|
];
|
|
20322
|
-
const outputLogPath =
|
|
20323
|
-
const errorLogPath =
|
|
20974
|
+
const outputLogPath = join6(logsDir, `${serviceName}.out.log`);
|
|
20975
|
+
const errorLogPath = join6(logsDir, `${serviceName}.err.log`);
|
|
20324
20976
|
await serviceDependencies.mkdirImpl(logsDir, { recursive: true });
|
|
20325
20977
|
if (platform === "systemd") {
|
|
20326
|
-
const serviceDir =
|
|
20327
|
-
const serviceFilePath2 =
|
|
20978
|
+
const serviceDir = join6(homeDir, ".config", "systemd", "user");
|
|
20979
|
+
const serviceFilePath2 = join6(serviceDir, `${serviceName}.service`);
|
|
20328
20980
|
await serviceDependencies.mkdirImpl(serviceDir, { recursive: true });
|
|
20329
20981
|
await serviceDependencies.writeFileImpl(
|
|
20330
20982
|
serviceFilePath2,
|
|
@@ -20363,9 +21015,9 @@ async function installConnectorServiceForAgent(agentName, commandOptions = {}, d
|
|
|
20363
21015
|
serviceFilePath: serviceFilePath2
|
|
20364
21016
|
};
|
|
20365
21017
|
}
|
|
20366
|
-
const launchAgentsDir =
|
|
21018
|
+
const launchAgentsDir = join6(homeDir, "Library", "LaunchAgents");
|
|
20367
21019
|
const serviceNameWithDomain = `com.clawdentity.${serviceName}`;
|
|
20368
|
-
const serviceFilePath =
|
|
21020
|
+
const serviceFilePath = join6(
|
|
20369
21021
|
launchAgentsDir,
|
|
20370
21022
|
`${serviceNameWithDomain}.plist`
|
|
20371
21023
|
);
|
|
@@ -20424,7 +21076,7 @@ async function uninstallConnectorServiceForAgent(agentName, commandOptions = {},
|
|
|
20424
21076
|
`clawdentity-connector-${agentName}`
|
|
20425
21077
|
);
|
|
20426
21078
|
if (platform === "systemd") {
|
|
20427
|
-
const serviceFilePath2 =
|
|
21079
|
+
const serviceFilePath2 = join6(
|
|
20428
21080
|
homeDir,
|
|
20429
21081
|
".config",
|
|
20430
21082
|
"systemd",
|
|
@@ -20455,7 +21107,7 @@ async function uninstallConnectorServiceForAgent(agentName, commandOptions = {},
|
|
|
20455
21107
|
};
|
|
20456
21108
|
}
|
|
20457
21109
|
const serviceNameWithDomain = `com.clawdentity.${serviceName}`;
|
|
20458
|
-
const serviceFilePath =
|
|
21110
|
+
const serviceFilePath = join6(
|
|
20459
21111
|
homeDir,
|
|
20460
21112
|
"Library",
|
|
20461
21113
|
"LaunchAgents",
|
|
@@ -20479,11 +21131,11 @@ async function uninstallConnectorServiceForAgent(agentName, commandOptions = {},
|
|
|
20479
21131
|
async function startConnectorForAgent(agentName, commandOptions = {}, dependencies = {}) {
|
|
20480
21132
|
const resolveConfigImpl = dependencies.resolveConfigImpl ?? resolveConfig;
|
|
20481
21133
|
const getConfigDirImpl = dependencies.getConfigDirImpl ?? getConfigDir;
|
|
20482
|
-
const readFileImpl = dependencies.readFileImpl ?? ((path, encoding) =>
|
|
21134
|
+
const readFileImpl = dependencies.readFileImpl ?? ((path, encoding) => readFile5(path, encoding));
|
|
20483
21135
|
const fetchImpl = dependencies.fetchImpl ?? globalThis.fetch;
|
|
20484
21136
|
const loadConnectorModule = dependencies.loadConnectorModule ?? loadDefaultConnectorModule;
|
|
20485
21137
|
const configDir = getConfigDirImpl();
|
|
20486
|
-
const agentDirectory =
|
|
21138
|
+
const agentDirectory = join6(configDir, AGENTS_DIR_NAME3, agentName);
|
|
20487
21139
|
const [
|
|
20488
21140
|
rawAit,
|
|
20489
21141
|
rawSecretKey,
|
|
@@ -20495,22 +21147,22 @@ async function startConnectorForAgent(agentName, commandOptions = {}, dependenci
|
|
|
20495
21147
|
connectorModule
|
|
20496
21148
|
] = await Promise.all([
|
|
20497
21149
|
readRequiredTrimmedFile(
|
|
20498
|
-
|
|
21150
|
+
join6(agentDirectory, AIT_FILE_NAME2),
|
|
20499
21151
|
AIT_FILE_NAME2,
|
|
20500
21152
|
readFileImpl
|
|
20501
21153
|
),
|
|
20502
21154
|
readRequiredTrimmedFile(
|
|
20503
|
-
|
|
21155
|
+
join6(agentDirectory, SECRET_KEY_FILE_NAME),
|
|
20504
21156
|
SECRET_KEY_FILE_NAME,
|
|
20505
21157
|
readFileImpl
|
|
20506
21158
|
),
|
|
20507
21159
|
readRequiredTrimmedFile(
|
|
20508
|
-
|
|
21160
|
+
join6(agentDirectory, IDENTITY_FILE_NAME2),
|
|
20509
21161
|
IDENTITY_FILE_NAME2,
|
|
20510
21162
|
readFileImpl
|
|
20511
21163
|
),
|
|
20512
21164
|
readRequiredTrimmedFile(
|
|
20513
|
-
|
|
21165
|
+
join6(agentDirectory, REGISTRY_AUTH_FILE_NAME2),
|
|
20514
21166
|
REGISTRY_AUTH_FILE_NAME2,
|
|
20515
21167
|
readFileImpl
|
|
20516
21168
|
),
|
|
@@ -20557,8 +21209,8 @@ async function startConnectorForAgent(agentName, commandOptions = {}, dependenci
|
|
|
20557
21209
|
tokenType: registryAuth.tokenType
|
|
20558
21210
|
}
|
|
20559
21211
|
});
|
|
20560
|
-
const outboundUrl = runtime &&
|
|
20561
|
-
const proxyWebsocketUrl = runtime &&
|
|
21212
|
+
const outboundUrl = runtime && isRecord7(runtime) && typeof runtime.outboundUrl === "string" ? runtime.outboundUrl : resolveOutboundUrl(outboundBaseUrl, outboundPath);
|
|
21213
|
+
const proxyWebsocketUrl = runtime && isRecord7(runtime) ? typeof runtime.websocketUrl === "string" ? runtime.websocketUrl : typeof runtime.proxyWebsocketUrl === "string" ? runtime.proxyWebsocketUrl : resolvedProxyWebsocketUrl : void 0;
|
|
20562
21214
|
return {
|
|
20563
21215
|
outboundUrl,
|
|
20564
21216
|
proxyWebsocketUrl,
|
|
@@ -20689,7 +21341,7 @@ function createConnectorCommand(dependencies = {}) {
|
|
|
20689
21341
|
// src/commands/invite.ts
|
|
20690
21342
|
import { Command as Command6 } from "commander";
|
|
20691
21343
|
var logger7 = createLogger({ service: "cli", module: "invite" });
|
|
20692
|
-
var
|
|
21344
|
+
var isRecord8 = (value) => {
|
|
20693
21345
|
return typeof value === "object" && value !== null;
|
|
20694
21346
|
};
|
|
20695
21347
|
function parseNonEmptyString7(value) {
|
|
@@ -20733,7 +21385,7 @@ function requireApiKey2(config2) {
|
|
|
20733
21385
|
);
|
|
20734
21386
|
}
|
|
20735
21387
|
function extractRegistryErrorCode(payload) {
|
|
20736
|
-
if (!
|
|
21388
|
+
if (!isRecord8(payload)) {
|
|
20737
21389
|
return void 0;
|
|
20738
21390
|
}
|
|
20739
21391
|
const envelope = payload;
|
|
@@ -20744,7 +21396,7 @@ function extractRegistryErrorCode(payload) {
|
|
|
20744
21396
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
20745
21397
|
}
|
|
20746
21398
|
function extractRegistryErrorMessage4(payload) {
|
|
20747
|
-
if (!
|
|
21399
|
+
if (!isRecord8(payload)) {
|
|
20748
21400
|
return void 0;
|
|
20749
21401
|
}
|
|
20750
21402
|
const envelope = payload;
|
|
@@ -20818,13 +21470,13 @@ function mapRedeemInviteError(status, payload) {
|
|
|
20818
21470
|
return `Invite redeem failed (${status})`;
|
|
20819
21471
|
}
|
|
20820
21472
|
function parseInviteRecord(payload) {
|
|
20821
|
-
if (!
|
|
21473
|
+
if (!isRecord8(payload)) {
|
|
20822
21474
|
throw createCliError5(
|
|
20823
21475
|
"CLI_INVITE_CREATE_INVALID_RESPONSE",
|
|
20824
21476
|
"Invite response is invalid"
|
|
20825
21477
|
);
|
|
20826
21478
|
}
|
|
20827
|
-
const source =
|
|
21479
|
+
const source = isRecord8(payload.invite) ? payload.invite : payload;
|
|
20828
21480
|
const code = parseNonEmptyString7(source.code);
|
|
20829
21481
|
if (code.length === 0) {
|
|
20830
21482
|
throw createCliError5(
|
|
@@ -20849,15 +21501,15 @@ function parseInviteRecord(payload) {
|
|
|
20849
21501
|
return invite;
|
|
20850
21502
|
}
|
|
20851
21503
|
function parseInviteRedeemResponse(payload) {
|
|
20852
|
-
if (!
|
|
21504
|
+
if (!isRecord8(payload)) {
|
|
20853
21505
|
throw createCliError5(
|
|
20854
21506
|
"CLI_INVITE_REDEEM_INVALID_RESPONSE",
|
|
20855
21507
|
"Invite redeem response is invalid"
|
|
20856
21508
|
);
|
|
20857
21509
|
}
|
|
20858
|
-
const apiKeySource =
|
|
21510
|
+
const apiKeySource = isRecord8(payload.apiKey) ? payload.apiKey : payload;
|
|
20859
21511
|
const apiKeyToken = parseNonEmptyString7(
|
|
20860
|
-
|
|
21512
|
+
isRecord8(payload.apiKey) ? payload.apiKey.token : payload.token
|
|
20861
21513
|
);
|
|
20862
21514
|
if (apiKeyToken.length === 0) {
|
|
20863
21515
|
throw createCliError5(
|
|
@@ -20867,7 +21519,7 @@ function parseInviteRedeemResponse(payload) {
|
|
|
20867
21519
|
}
|
|
20868
21520
|
const apiKeyId = parseNonEmptyString7(apiKeySource.id);
|
|
20869
21521
|
const apiKeyName = parseNonEmptyString7(apiKeySource.name);
|
|
20870
|
-
const humanSource =
|
|
21522
|
+
const humanSource = isRecord8(payload.human) ? payload.human : void 0;
|
|
20871
21523
|
const humanName = parseNonEmptyString7(humanSource?.displayName);
|
|
20872
21524
|
const proxyUrl = parseNonEmptyString7(payload.proxyUrl);
|
|
20873
21525
|
if (humanName.length === 0) {
|
|
@@ -21055,9 +21707,9 @@ var createInviteCommand = (dependencies = {}) => {
|
|
|
21055
21707
|
import { spawn } from "child_process";
|
|
21056
21708
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
21057
21709
|
import { existsSync } from "fs";
|
|
21058
|
-
import { chmod as chmod3, copyFile, mkdir as
|
|
21710
|
+
import { chmod as chmod3, copyFile, mkdir as mkdir6, readFile as readFile6, writeFile as writeFile6 } from "fs/promises";
|
|
21059
21711
|
import { homedir as homedir3 } from "os";
|
|
21060
|
-
import { dirname as
|
|
21712
|
+
import { dirname as dirname5, join as join7, resolve as resolvePath } from "path";
|
|
21061
21713
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
21062
21714
|
import { Command as Command7 } from "commander";
|
|
21063
21715
|
var logger8 = createLogger({ service: "cli", module: "openclaw" });
|
|
@@ -21090,7 +21742,6 @@ var HOOK_PATH_SEND_TO_PEER = "send-to-peer";
|
|
|
21090
21742
|
var OPENCLAW_SEND_TO_PEER_HOOK_PATH = "hooks/send-to-peer";
|
|
21091
21743
|
var DEFAULT_OPENCLAW_BASE_URL2 = "http://127.0.0.1:18789";
|
|
21092
21744
|
var DEFAULT_OPENCLAW_MAIN_SESSION_KEY = "main";
|
|
21093
|
-
var DEFAULT_OPENCLAW_AGENT_ID = "main";
|
|
21094
21745
|
var DEFAULT_CONNECTOR_PORT = 19400;
|
|
21095
21746
|
var DEFAULT_CONNECTOR_OUTBOUND_PATH3 = "/v1/outbound";
|
|
21096
21747
|
var DEFAULT_CONNECTOR_STATUS_PATH2 = "/v1/status";
|
|
@@ -21107,9 +21758,12 @@ var OPENCLAW_SETUP_COMMAND_HINT = "Run: clawdentity openclaw setup <agentName>";
|
|
|
21107
21758
|
var OPENCLAW_SETUP_RESTART_COMMAND_HINT = `${OPENCLAW_SETUP_COMMAND_HINT} and restart OpenClaw`;
|
|
21108
21759
|
var OPENCLAW_SETUP_WITH_BASE_URL_HINT = `${OPENCLAW_SETUP_COMMAND_HINT} --openclaw-base-url <url>`;
|
|
21109
21760
|
var OPENCLAW_PAIRING_COMMAND_HINT = "Run QR pairing first: clawdentity pair start <agentName> --qr and clawdentity pair confirm <agentName> --qr-file <path>";
|
|
21761
|
+
var OPENCLAW_DEVICE_APPROVAL_RECOVERY_HINT = "Run: clawdentity openclaw setup <agentName> (auto-recovers pending OpenClaw gateway device approvals)";
|
|
21762
|
+
var OPENCLAW_GATEWAY_APPROVAL_COMMAND = "openclaw";
|
|
21763
|
+
var OPENCLAW_GATEWAY_APPROVAL_TIMEOUT_MS = 1e4;
|
|
21110
21764
|
var textEncoder2 = new TextEncoder();
|
|
21111
21765
|
var textDecoder = new TextDecoder();
|
|
21112
|
-
function
|
|
21766
|
+
function isRecord9(value) {
|
|
21113
21767
|
return typeof value === "object" && value !== null;
|
|
21114
21768
|
}
|
|
21115
21769
|
function createCliError6(code, message2, details) {
|
|
@@ -21121,7 +21775,7 @@ function createCliError6(code, message2, details) {
|
|
|
21121
21775
|
});
|
|
21122
21776
|
}
|
|
21123
21777
|
function getErrorCode2(error48) {
|
|
21124
|
-
if (!
|
|
21778
|
+
if (!isRecord9(error48)) {
|
|
21125
21779
|
return void 0;
|
|
21126
21780
|
}
|
|
21127
21781
|
return typeof error48.code === "string" ? error48.code : void 0;
|
|
@@ -21242,12 +21896,12 @@ function resolveOpenclawHomeDir(homeDir) {
|
|
|
21242
21896
|
return envOpenclawHome ?? homeDir;
|
|
21243
21897
|
}
|
|
21244
21898
|
function resolveDefaultOpenclawStateDir(openclawHomeDir) {
|
|
21245
|
-
const newStateDir =
|
|
21899
|
+
const newStateDir = join7(openclawHomeDir, OPENCLAW_DIR_NAME);
|
|
21246
21900
|
if (existsSync(newStateDir)) {
|
|
21247
21901
|
return newStateDir;
|
|
21248
21902
|
}
|
|
21249
21903
|
for (const legacyDirName of LEGACY_OPENCLAW_STATE_DIR_NAMES) {
|
|
21250
|
-
const legacyStateDir =
|
|
21904
|
+
const legacyStateDir = join7(openclawHomeDir, legacyDirName);
|
|
21251
21905
|
if (existsSync(legacyStateDir)) {
|
|
21252
21906
|
return legacyStateDir;
|
|
21253
21907
|
}
|
|
@@ -21270,16 +21924,16 @@ function resolveOpenclawDir(openclawDir, homeDir) {
|
|
|
21270
21924
|
homeDir
|
|
21271
21925
|
);
|
|
21272
21926
|
if (envConfigPath !== void 0) {
|
|
21273
|
-
return
|
|
21927
|
+
return dirname5(envConfigPath);
|
|
21274
21928
|
}
|
|
21275
21929
|
const openclawHomeDir = resolveOpenclawHomeDir(homeDir);
|
|
21276
21930
|
return resolveDefaultOpenclawStateDir(openclawHomeDir);
|
|
21277
21931
|
}
|
|
21278
21932
|
function resolveAgentDirectory(homeDir, agentName) {
|
|
21279
|
-
return
|
|
21933
|
+
return join7(homeDir, CLAWDENTITY_DIR_NAME, AGENTS_DIR_NAME4, agentName);
|
|
21280
21934
|
}
|
|
21281
21935
|
function resolvePeersPath(homeDir) {
|
|
21282
|
-
return
|
|
21936
|
+
return join7(homeDir, CLAWDENTITY_DIR_NAME, PEERS_FILE_NAME);
|
|
21283
21937
|
}
|
|
21284
21938
|
function resolveOpenclawConfigPath(openclawDir, homeDir) {
|
|
21285
21939
|
const envConfigPath = readNonEmptyEnvPath(
|
|
@@ -21290,9 +21944,9 @@ function resolveOpenclawConfigPath(openclawDir, homeDir) {
|
|
|
21290
21944
|
return envConfigPath;
|
|
21291
21945
|
}
|
|
21292
21946
|
const configCandidates = [
|
|
21293
|
-
|
|
21947
|
+
join7(openclawDir, OPENCLAW_CONFIG_FILE_NAME),
|
|
21294
21948
|
...LEGACY_OPENCLAW_CONFIG_FILE_NAMES.map(
|
|
21295
|
-
(fileName) =>
|
|
21949
|
+
(fileName) => join7(openclawDir, fileName)
|
|
21296
21950
|
)
|
|
21297
21951
|
];
|
|
21298
21952
|
for (const candidate of configCandidates) {
|
|
@@ -21303,81 +21957,219 @@ function resolveOpenclawConfigPath(openclawDir, homeDir) {
|
|
|
21303
21957
|
return configCandidates[0];
|
|
21304
21958
|
}
|
|
21305
21959
|
function resolveDefaultTransformSource(openclawDir) {
|
|
21306
|
-
return
|
|
21960
|
+
return join7(openclawDir, "skills", SKILL_DIR_NAME, RELAY_MODULE_FILE_NAME);
|
|
21307
21961
|
}
|
|
21308
21962
|
function resolveTransformTargetPath(openclawDir) {
|
|
21309
|
-
return
|
|
21963
|
+
return join7(openclawDir, "hooks", "transforms", RELAY_MODULE_FILE_NAME);
|
|
21310
21964
|
}
|
|
21311
21965
|
function resolveOpenclawAgentNamePath(homeDir) {
|
|
21312
|
-
return
|
|
21966
|
+
return join7(homeDir, CLAWDENTITY_DIR_NAME, OPENCLAW_AGENT_FILE_NAME);
|
|
21313
21967
|
}
|
|
21314
21968
|
function resolveRelayRuntimeConfigPath(homeDir) {
|
|
21315
|
-
return
|
|
21969
|
+
return join7(homeDir, CLAWDENTITY_DIR_NAME, OPENCLAW_RELAY_RUNTIME_FILE_NAME2);
|
|
21316
21970
|
}
|
|
21317
21971
|
function resolveConnectorAssignmentsPath(homeDir) {
|
|
21318
|
-
return
|
|
21972
|
+
return join7(homeDir, CLAWDENTITY_DIR_NAME, OPENCLAW_CONNECTORS_FILE_NAME2);
|
|
21319
21973
|
}
|
|
21320
21974
|
function resolveTransformRuntimePath(openclawDir) {
|
|
21321
|
-
return
|
|
21975
|
+
return join7(openclawDir, "hooks", "transforms", RELAY_RUNTIME_FILE_NAME);
|
|
21322
21976
|
}
|
|
21323
21977
|
function resolveTransformPeersPath(openclawDir) {
|
|
21324
|
-
return
|
|
21978
|
+
return join7(openclawDir, "hooks", "transforms", RELAY_PEERS_FILE_NAME);
|
|
21325
21979
|
}
|
|
21326
|
-
async function
|
|
21327
|
-
const
|
|
21980
|
+
async function readOpenclawGatewayPendingState(openclawDir) {
|
|
21981
|
+
const gatewayDevicePendingPath = join7(openclawDir, "devices", "pending.json");
|
|
21328
21982
|
try {
|
|
21329
|
-
|
|
21330
|
-
|
|
21331
|
-
|
|
21332
|
-
|
|
21333
|
-
|
|
21334
|
-
|
|
21335
|
-
}
|
|
21336
|
-
async function ensureLocalAgentCredentials(homeDir, agentName) {
|
|
21337
|
-
const agentDir = resolveAgentDirectory(homeDir, agentName);
|
|
21338
|
-
const requiredFiles = [
|
|
21339
|
-
join6(agentDir, SECRET_KEY_FILE_NAME2),
|
|
21340
|
-
join6(agentDir, AIT_FILE_NAME3)
|
|
21341
|
-
];
|
|
21342
|
-
for (const filePath of requiredFiles) {
|
|
21343
|
-
let content;
|
|
21344
|
-
try {
|
|
21345
|
-
content = await readFile5(filePath, "utf8");
|
|
21346
|
-
} catch (error48) {
|
|
21347
|
-
if (getErrorCode2(error48) === "ENOENT") {
|
|
21348
|
-
throw createCliError6(
|
|
21349
|
-
"CLI_OPENCLAW_MISSING_AGENT_CREDENTIALS",
|
|
21350
|
-
"Local agent credentials are missing",
|
|
21351
|
-
{ agentName, filePath }
|
|
21352
|
-
);
|
|
21353
|
-
}
|
|
21354
|
-
throw error48;
|
|
21983
|
+
const pendingPayload = await readJsonFile(gatewayDevicePendingPath);
|
|
21984
|
+
if (!isRecord9(pendingPayload)) {
|
|
21985
|
+
return {
|
|
21986
|
+
status: "invalid",
|
|
21987
|
+
gatewayDevicePendingPath
|
|
21988
|
+
};
|
|
21355
21989
|
}
|
|
21356
|
-
|
|
21357
|
-
|
|
21358
|
-
|
|
21359
|
-
|
|
21360
|
-
|
|
21361
|
-
|
|
21990
|
+
return {
|
|
21991
|
+
status: "ok",
|
|
21992
|
+
gatewayDevicePendingPath,
|
|
21993
|
+
pendingRequestIds: Object.keys(pendingPayload)
|
|
21994
|
+
};
|
|
21995
|
+
} catch (error48) {
|
|
21996
|
+
if (getErrorCode2(error48) === "ENOENT") {
|
|
21997
|
+
return {
|
|
21998
|
+
status: "missing",
|
|
21999
|
+
gatewayDevicePendingPath
|
|
22000
|
+
};
|
|
21362
22001
|
}
|
|
22002
|
+
return {
|
|
22003
|
+
status: "unreadable",
|
|
22004
|
+
gatewayDevicePendingPath
|
|
22005
|
+
};
|
|
21363
22006
|
}
|
|
21364
22007
|
}
|
|
21365
|
-
|
|
21366
|
-
|
|
21367
|
-
|
|
21368
|
-
|
|
22008
|
+
function resolveOpenclawGatewayApprovalCommand() {
|
|
22009
|
+
const envOverride = process.env.OPENCLAW_GATEWAY_APPROVAL_COMMAND?.trim();
|
|
22010
|
+
if (typeof envOverride === "string" && envOverride.length > 0) {
|
|
22011
|
+
return envOverride;
|
|
22012
|
+
}
|
|
22013
|
+
return OPENCLAW_GATEWAY_APPROVAL_COMMAND;
|
|
21369
22014
|
}
|
|
21370
|
-
async function
|
|
21371
|
-
|
|
21372
|
-
|
|
21373
|
-
|
|
22015
|
+
async function runOpenclawGatewayApprovalCommand(input) {
|
|
22016
|
+
return await new Promise(
|
|
22017
|
+
(resolve2) => {
|
|
22018
|
+
const child = spawn(input.command, input.args, {
|
|
22019
|
+
env: {
|
|
22020
|
+
...process.env,
|
|
22021
|
+
OPENCLAW_STATE_DIR: input.openclawDir,
|
|
22022
|
+
OPENCLAW_CONFIG_PATH: input.openclawConfigPath
|
|
22023
|
+
},
|
|
22024
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
22025
|
+
});
|
|
22026
|
+
let settled = false;
|
|
22027
|
+
let stdout = "";
|
|
22028
|
+
let stderr = "";
|
|
22029
|
+
const finalize2 = (result) => {
|
|
22030
|
+
if (settled) {
|
|
22031
|
+
return;
|
|
22032
|
+
}
|
|
22033
|
+
settled = true;
|
|
22034
|
+
resolve2({
|
|
22035
|
+
...result,
|
|
22036
|
+
stdout: stdout.trim(),
|
|
22037
|
+
stderr: stderr.trim()
|
|
22038
|
+
});
|
|
22039
|
+
};
|
|
22040
|
+
const timeout = setTimeout(() => {
|
|
22041
|
+
try {
|
|
22042
|
+
child.kill("SIGTERM");
|
|
22043
|
+
} catch {
|
|
22044
|
+
}
|
|
22045
|
+
finalize2({
|
|
22046
|
+
ok: false,
|
|
22047
|
+
errorMessage: `command timed out after ${OPENCLAW_GATEWAY_APPROVAL_TIMEOUT_MS}ms`
|
|
22048
|
+
});
|
|
22049
|
+
}, OPENCLAW_GATEWAY_APPROVAL_TIMEOUT_MS);
|
|
22050
|
+
child.stdout?.on("data", (chunk) => {
|
|
22051
|
+
stdout += String(chunk);
|
|
22052
|
+
});
|
|
22053
|
+
child.stderr?.on("data", (chunk) => {
|
|
22054
|
+
stderr += String(chunk);
|
|
22055
|
+
});
|
|
22056
|
+
child.once("error", (error48) => {
|
|
22057
|
+
clearTimeout(timeout);
|
|
22058
|
+
const errorCode = getErrorCode2(error48);
|
|
22059
|
+
finalize2({
|
|
22060
|
+
ok: false,
|
|
22061
|
+
unavailable: errorCode === "ENOENT",
|
|
22062
|
+
errorMessage: error48 instanceof Error ? error48.message : "failed to run openclaw command"
|
|
22063
|
+
});
|
|
22064
|
+
});
|
|
22065
|
+
child.once("close", (exitCode) => {
|
|
22066
|
+
clearTimeout(timeout);
|
|
22067
|
+
finalize2({
|
|
22068
|
+
ok: exitCode === 0,
|
|
22069
|
+
exitCode: typeof exitCode === "number" ? exitCode : void 0
|
|
22070
|
+
});
|
|
22071
|
+
});
|
|
22072
|
+
}
|
|
22073
|
+
);
|
|
22074
|
+
}
|
|
22075
|
+
async function runOpenclawGatewayDeviceApproval(input) {
|
|
22076
|
+
const command = resolveOpenclawGatewayApprovalCommand();
|
|
22077
|
+
return await runOpenclawGatewayApprovalCommand({
|
|
22078
|
+
command,
|
|
22079
|
+
args: ["devices", "approve", input.requestId, "--json"],
|
|
22080
|
+
openclawDir: input.openclawDir,
|
|
22081
|
+
openclawConfigPath: input.openclawConfigPath
|
|
22082
|
+
});
|
|
22083
|
+
}
|
|
22084
|
+
async function autoApproveOpenclawGatewayDevices(input) {
|
|
22085
|
+
const pendingState = await readOpenclawGatewayPendingState(input.openclawDir);
|
|
22086
|
+
if (pendingState.status !== "ok" || pendingState.pendingRequestIds.length === 0) {
|
|
22087
|
+
return void 0;
|
|
22088
|
+
}
|
|
22089
|
+
const openclawConfigPath = resolveOpenclawConfigPath(
|
|
22090
|
+
input.openclawDir,
|
|
22091
|
+
input.homeDir
|
|
22092
|
+
);
|
|
22093
|
+
const approvalRunner = input.runner ?? runOpenclawGatewayDeviceApproval;
|
|
22094
|
+
const attempts = [];
|
|
22095
|
+
for (const requestId of pendingState.pendingRequestIds) {
|
|
22096
|
+
const execution = await approvalRunner({
|
|
22097
|
+
requestId,
|
|
22098
|
+
openclawDir: input.openclawDir,
|
|
22099
|
+
openclawConfigPath
|
|
22100
|
+
});
|
|
22101
|
+
attempts.push({
|
|
22102
|
+
requestId,
|
|
22103
|
+
ok: execution.ok,
|
|
22104
|
+
unavailable: execution.unavailable === true,
|
|
22105
|
+
reason: execution.errorMessage ?? (execution.stderr && execution.stderr.length > 0 ? execution.stderr : execution.stdout && execution.stdout.length > 0 ? execution.stdout : void 0),
|
|
22106
|
+
exitCode: execution.exitCode
|
|
22107
|
+
});
|
|
22108
|
+
if (execution.unavailable === true) {
|
|
22109
|
+
break;
|
|
22110
|
+
}
|
|
22111
|
+
}
|
|
22112
|
+
return {
|
|
22113
|
+
gatewayDevicePendingPath: pendingState.gatewayDevicePendingPath,
|
|
22114
|
+
pendingRequestIds: pendingState.pendingRequestIds,
|
|
22115
|
+
attempts
|
|
22116
|
+
};
|
|
22117
|
+
}
|
|
22118
|
+
async function readJsonFile(filePath) {
|
|
22119
|
+
const raw = await readFile6(filePath, "utf8");
|
|
22120
|
+
try {
|
|
22121
|
+
return JSON.parse(raw);
|
|
22122
|
+
} catch {
|
|
22123
|
+
throw createCliError6("CLI_OPENCLAW_INVALID_JSON", "JSON file is invalid", {
|
|
22124
|
+
filePath
|
|
22125
|
+
});
|
|
22126
|
+
}
|
|
22127
|
+
}
|
|
22128
|
+
async function ensureLocalAgentCredentials(homeDir, agentName) {
|
|
22129
|
+
const agentDir = resolveAgentDirectory(homeDir, agentName);
|
|
22130
|
+
const requiredFiles = [
|
|
22131
|
+
join7(agentDir, SECRET_KEY_FILE_NAME2),
|
|
22132
|
+
join7(agentDir, AIT_FILE_NAME3)
|
|
22133
|
+
];
|
|
22134
|
+
for (const filePath of requiredFiles) {
|
|
22135
|
+
let content;
|
|
22136
|
+
try {
|
|
22137
|
+
content = await readFile6(filePath, "utf8");
|
|
22138
|
+
} catch (error48) {
|
|
22139
|
+
if (getErrorCode2(error48) === "ENOENT") {
|
|
22140
|
+
throw createCliError6(
|
|
22141
|
+
"CLI_OPENCLAW_MISSING_AGENT_CREDENTIALS",
|
|
22142
|
+
"Local agent credentials are missing",
|
|
22143
|
+
{ agentName, filePath }
|
|
22144
|
+
);
|
|
22145
|
+
}
|
|
22146
|
+
throw error48;
|
|
22147
|
+
}
|
|
22148
|
+
if (content.trim().length === 0) {
|
|
22149
|
+
throw createCliError6(
|
|
22150
|
+
"CLI_OPENCLAW_EMPTY_AGENT_CREDENTIALS",
|
|
22151
|
+
"Agent credential file is empty",
|
|
22152
|
+
{ filePath }
|
|
22153
|
+
);
|
|
22154
|
+
}
|
|
22155
|
+
}
|
|
22156
|
+
}
|
|
22157
|
+
async function writeSecureFile3(filePath, content) {
|
|
22158
|
+
await mkdir6(dirname5(filePath), { recursive: true });
|
|
22159
|
+
await writeFile6(filePath, content, "utf8");
|
|
22160
|
+
await chmod3(filePath, FILE_MODE3);
|
|
22161
|
+
}
|
|
22162
|
+
async function loadPeersConfig(peersPath) {
|
|
22163
|
+
let parsed;
|
|
22164
|
+
try {
|
|
22165
|
+
parsed = await readJsonFile(peersPath);
|
|
21374
22166
|
} catch (error48) {
|
|
21375
22167
|
if (getErrorCode2(error48) === "ENOENT") {
|
|
21376
22168
|
return { peers: {} };
|
|
21377
22169
|
}
|
|
21378
22170
|
throw error48;
|
|
21379
22171
|
}
|
|
21380
|
-
if (!
|
|
22172
|
+
if (!isRecord9(parsed)) {
|
|
21381
22173
|
throw createCliError6(
|
|
21382
22174
|
"CLI_OPENCLAW_INVALID_PEERS_CONFIG",
|
|
21383
22175
|
"Peer config root must be a JSON object",
|
|
@@ -21388,7 +22180,7 @@ async function loadPeersConfig(peersPath) {
|
|
|
21388
22180
|
if (peersValue === void 0) {
|
|
21389
22181
|
return { peers: {} };
|
|
21390
22182
|
}
|
|
21391
|
-
if (!
|
|
22183
|
+
if (!isRecord9(peersValue)) {
|
|
21392
22184
|
throw createCliError6(
|
|
21393
22185
|
"CLI_OPENCLAW_INVALID_PEERS_CONFIG",
|
|
21394
22186
|
"Peer config peers field must be an object",
|
|
@@ -21398,7 +22190,7 @@ async function loadPeersConfig(peersPath) {
|
|
|
21398
22190
|
const peers = {};
|
|
21399
22191
|
for (const [alias, value] of Object.entries(peersValue)) {
|
|
21400
22192
|
const normalizedAlias = parsePeerAlias(alias);
|
|
21401
|
-
if (!
|
|
22193
|
+
if (!isRecord9(value)) {
|
|
21402
22194
|
throw createCliError6(
|
|
21403
22195
|
"CLI_OPENCLAW_INVALID_PEERS_CONFIG",
|
|
21404
22196
|
"Peer entry must be an object",
|
|
@@ -21429,7 +22221,7 @@ function parseConnectorBaseUrlForAssignment(value, label) {
|
|
|
21429
22221
|
});
|
|
21430
22222
|
}
|
|
21431
22223
|
function parseConnectorAssignments(value, connectorAssignmentsPath) {
|
|
21432
|
-
if (!
|
|
22224
|
+
if (!isRecord9(value)) {
|
|
21433
22225
|
throw createCliError6(
|
|
21434
22226
|
"CLI_OPENCLAW_INVALID_CONNECTOR_ASSIGNMENTS",
|
|
21435
22227
|
"Connector assignments config must be an object",
|
|
@@ -21437,12 +22229,12 @@ function parseConnectorAssignments(value, connectorAssignmentsPath) {
|
|
|
21437
22229
|
);
|
|
21438
22230
|
}
|
|
21439
22231
|
const agentsRaw = value.agents;
|
|
21440
|
-
if (!
|
|
22232
|
+
if (!isRecord9(agentsRaw)) {
|
|
21441
22233
|
return { agents: {} };
|
|
21442
22234
|
}
|
|
21443
22235
|
const agents = {};
|
|
21444
22236
|
for (const [agentName, entryValue] of Object.entries(agentsRaw)) {
|
|
21445
|
-
if (!
|
|
22237
|
+
if (!isRecord9(entryValue)) {
|
|
21446
22238
|
throw createCliError6(
|
|
21447
22239
|
"CLI_OPENCLAW_INVALID_CONNECTOR_ASSIGNMENTS",
|
|
21448
22240
|
"Connector assignment entry must be an object",
|
|
@@ -21547,14 +22339,28 @@ function resolveConnectorStatusUrl(connectorBaseUrl) {
|
|
|
21547
22339
|
).toString();
|
|
21548
22340
|
}
|
|
21549
22341
|
function parseConnectorStatusPayload(payload) {
|
|
21550
|
-
if (!
|
|
22342
|
+
if (!isRecord9(payload) || typeof payload.websocketConnected !== "boolean") {
|
|
21551
22343
|
throw createCliError6(
|
|
21552
22344
|
"CLI_OPENCLAW_SETUP_CONNECTOR_STATUS_INVALID",
|
|
21553
22345
|
"Connector status response is invalid"
|
|
21554
22346
|
);
|
|
21555
22347
|
}
|
|
21556
22348
|
return {
|
|
21557
|
-
websocketConnected: payload.websocketConnected
|
|
22349
|
+
websocketConnected: payload.websocketConnected,
|
|
22350
|
+
inboundInbox: isRecord9(payload.inboundInbox) ? {
|
|
22351
|
+
pendingCount: typeof payload.inboundInbox.pendingCount === "number" ? payload.inboundInbox.pendingCount : void 0,
|
|
22352
|
+
pendingBytes: typeof payload.inboundInbox.pendingBytes === "number" ? payload.inboundInbox.pendingBytes : void 0,
|
|
22353
|
+
oldestPendingAt: typeof payload.inboundInbox.oldestPendingAt === "string" ? payload.inboundInbox.oldestPendingAt : void 0,
|
|
22354
|
+
nextAttemptAt: typeof payload.inboundInbox.nextAttemptAt === "string" ? payload.inboundInbox.nextAttemptAt : void 0,
|
|
22355
|
+
lastReplayAt: typeof payload.inboundInbox.lastReplayAt === "string" ? payload.inboundInbox.lastReplayAt : void 0,
|
|
22356
|
+
lastReplayError: typeof payload.inboundInbox.lastReplayError === "string" ? payload.inboundInbox.lastReplayError : void 0,
|
|
22357
|
+
replayerActive: typeof payload.inboundInbox.replayerActive === "boolean" ? payload.inboundInbox.replayerActive : void 0
|
|
22358
|
+
} : void 0,
|
|
22359
|
+
openclawHook: isRecord9(payload.openclawHook) ? {
|
|
22360
|
+
url: typeof payload.openclawHook.url === "string" ? payload.openclawHook.url : void 0,
|
|
22361
|
+
lastAttemptAt: typeof payload.openclawHook.lastAttemptAt === "string" ? payload.openclawHook.lastAttemptAt : void 0,
|
|
22362
|
+
lastAttemptStatus: payload.openclawHook.lastAttemptStatus === "ok" || payload.openclawHook.lastAttemptStatus === "failed" ? payload.openclawHook.lastAttemptStatus : void 0
|
|
22363
|
+
} : void 0
|
|
21558
22364
|
};
|
|
21559
22365
|
}
|
|
21560
22366
|
async function fetchConnectorHealthStatus(input) {
|
|
@@ -21588,6 +22394,8 @@ async function fetchConnectorHealthStatus(input) {
|
|
|
21588
22394
|
const parsed = parseConnectorStatusPayload(payload);
|
|
21589
22395
|
return {
|
|
21590
22396
|
connected: parsed.websocketConnected,
|
|
22397
|
+
inboundInbox: parsed.inboundInbox,
|
|
22398
|
+
openclawHook: parsed.openclawHook,
|
|
21591
22399
|
reachable: true,
|
|
21592
22400
|
statusUrl,
|
|
21593
22401
|
reason: parsed.websocketConnected ? void 0 : "connector websocket is disconnected"
|
|
@@ -21630,14 +22438,14 @@ async function waitForConnectorConnected(input) {
|
|
|
21630
22438
|
return latest;
|
|
21631
22439
|
}
|
|
21632
22440
|
function resolveConnectorRunDir(homeDir) {
|
|
21633
|
-
return
|
|
22441
|
+
return join7(homeDir, CLAWDENTITY_DIR_NAME, CONNECTOR_RUN_DIR_NAME);
|
|
21634
22442
|
}
|
|
21635
22443
|
function resolveConnectorPidPath(homeDir, agentName) {
|
|
21636
|
-
return
|
|
22444
|
+
return join7(resolveConnectorRunDir(homeDir), `connector-${agentName}.pid`);
|
|
21637
22445
|
}
|
|
21638
22446
|
async function readConnectorPidFile(pidPath) {
|
|
21639
22447
|
try {
|
|
21640
|
-
const raw = (await
|
|
22448
|
+
const raw = (await readFile6(pidPath, "utf8")).trim();
|
|
21641
22449
|
if (raw.length === 0) {
|
|
21642
22450
|
return void 0;
|
|
21643
22451
|
}
|
|
@@ -21678,7 +22486,7 @@ function resolveCliEntryPathForDetachedStart() {
|
|
|
21678
22486
|
return argvEntry;
|
|
21679
22487
|
}
|
|
21680
22488
|
const modulePath = fileURLToPath2(import.meta.url);
|
|
21681
|
-
return
|
|
22489
|
+
return join7(dirname5(modulePath), "..", "bin.js");
|
|
21682
22490
|
}
|
|
21683
22491
|
async function startDetachedConnectorRuntime(input) {
|
|
21684
22492
|
await stopDetachedConnectorIfRunning({
|
|
@@ -21686,7 +22494,7 @@ async function startDetachedConnectorRuntime(input) {
|
|
|
21686
22494
|
agentName: input.agentName
|
|
21687
22495
|
});
|
|
21688
22496
|
const runDir = resolveConnectorRunDir(input.homeDir);
|
|
21689
|
-
await
|
|
22497
|
+
await mkdir6(runDir, { recursive: true });
|
|
21690
22498
|
const cliEntryPath = resolveCliEntryPathForDetachedStart();
|
|
21691
22499
|
const args = [
|
|
21692
22500
|
cliEntryPath,
|
|
@@ -21764,7 +22572,7 @@ async function startSetupConnectorRuntime(input) {
|
|
|
21764
22572
|
};
|
|
21765
22573
|
}
|
|
21766
22574
|
function parseRelayRuntimeConfig(value, relayRuntimeConfigPath) {
|
|
21767
|
-
if (!
|
|
22575
|
+
if (!isRecord9(value)) {
|
|
21768
22576
|
throw createCliError6(
|
|
21769
22577
|
"CLI_OPENCLAW_INVALID_RELAY_RUNTIME_CONFIG",
|
|
21770
22578
|
"Relay runtime config must be an object",
|
|
@@ -21773,9 +22581,11 @@ function parseRelayRuntimeConfig(value, relayRuntimeConfigPath) {
|
|
|
21773
22581
|
}
|
|
21774
22582
|
const updatedAt = typeof value.updatedAt === "string" && value.updatedAt.trim().length > 0 ? value.updatedAt.trim() : void 0;
|
|
21775
22583
|
const openclawHookToken = typeof value.openclawHookToken === "string" && value.openclawHookToken.trim().length > 0 ? value.openclawHookToken.trim() : void 0;
|
|
22584
|
+
const relayTransformPeersPath = typeof value.relayTransformPeersPath === "string" && value.relayTransformPeersPath.trim().length > 0 ? value.relayTransformPeersPath.trim() : void 0;
|
|
21776
22585
|
return {
|
|
21777
22586
|
openclawBaseUrl: parseOpenclawBaseUrl(value.openclawBaseUrl),
|
|
21778
22587
|
openclawHookToken,
|
|
22588
|
+
relayTransformPeersPath,
|
|
21779
22589
|
updatedAt
|
|
21780
22590
|
};
|
|
21781
22591
|
}
|
|
@@ -21791,10 +22601,11 @@ async function loadRelayRuntimeConfig(relayRuntimeConfigPath) {
|
|
|
21791
22601
|
}
|
|
21792
22602
|
return parseRelayRuntimeConfig(parsed, relayRuntimeConfigPath);
|
|
21793
22603
|
}
|
|
21794
|
-
async function saveRelayRuntimeConfig(relayRuntimeConfigPath, openclawBaseUrl, openclawHookToken) {
|
|
22604
|
+
async function saveRelayRuntimeConfig(relayRuntimeConfigPath, openclawBaseUrl, openclawHookToken, relayTransformPeersPath) {
|
|
21795
22605
|
const config2 = {
|
|
21796
22606
|
openclawBaseUrl,
|
|
21797
22607
|
...openclawHookToken ? { openclawHookToken } : {},
|
|
22608
|
+
...relayTransformPeersPath ? { relayTransformPeersPath } : {},
|
|
21798
22609
|
updatedAt: nowIso()
|
|
21799
22610
|
};
|
|
21800
22611
|
await writeSecureFile3(
|
|
@@ -21841,52 +22652,59 @@ function normalizeStringArrayWithValues(value, requiredValues) {
|
|
|
21841
22652
|
return Array.from(normalized);
|
|
21842
22653
|
}
|
|
21843
22654
|
function resolveHookDefaultSessionKey(config2, hooks) {
|
|
22655
|
+
const session = isRecord9(config2.session) ? config2.session : {};
|
|
22656
|
+
const scope = typeof session.scope === "string" ? session.scope.trim().toLowerCase() : "";
|
|
22657
|
+
const configuredMainSessionKey = resolveConfiguredOpenclawMainSessionKey(session);
|
|
21844
22658
|
if (typeof hooks.defaultSessionKey === "string" && hooks.defaultSessionKey.trim().length > 0) {
|
|
21845
|
-
return
|
|
22659
|
+
return normalizeLegacyHookDefaultSessionKey(
|
|
22660
|
+
hooks.defaultSessionKey,
|
|
22661
|
+
configuredMainSessionKey
|
|
22662
|
+
);
|
|
21846
22663
|
}
|
|
21847
|
-
const session = isRecord8(config2.session) ? config2.session : {};
|
|
21848
|
-
const scope = typeof session.scope === "string" ? session.scope.trim().toLowerCase() : "";
|
|
21849
22664
|
if (scope === "global") {
|
|
21850
22665
|
return "global";
|
|
21851
22666
|
}
|
|
21852
|
-
|
|
21853
|
-
const agentList = Array.isArray(agents.list) ? agents.list : [];
|
|
21854
|
-
const defaultAgentId = resolveDefaultOpenclawAgentId(agentList);
|
|
21855
|
-
return `agent:${defaultAgentId}:${DEFAULT_OPENCLAW_MAIN_SESSION_KEY}`;
|
|
22667
|
+
return configuredMainSessionKey;
|
|
21856
22668
|
}
|
|
21857
|
-
function
|
|
21858
|
-
|
|
21859
|
-
|
|
21860
|
-
) ?? agentList.find(
|
|
21861
|
-
(agent) => isRecord8(agent) && typeof agent.id === "string" && agent.id.trim().length > 0
|
|
21862
|
-
);
|
|
21863
|
-
if (isRecord8(preferred) && typeof preferred.id === "string" && preferred.id.trim().length > 0) {
|
|
21864
|
-
return normalizeOpenclawIdToken(preferred.id, DEFAULT_OPENCLAW_AGENT_ID);
|
|
22669
|
+
function resolveConfiguredOpenclawMainSessionKey(session) {
|
|
22670
|
+
if (typeof session.mainKey === "string" && session.mainKey.trim().length > 0) {
|
|
22671
|
+
return session.mainKey.trim();
|
|
21865
22672
|
}
|
|
21866
|
-
return
|
|
22673
|
+
return DEFAULT_OPENCLAW_MAIN_SESSION_KEY;
|
|
21867
22674
|
}
|
|
21868
|
-
function
|
|
21869
|
-
const
|
|
21870
|
-
|
|
22675
|
+
function normalizeLegacyHookDefaultSessionKey(value, fallbackSessionKey) {
|
|
22676
|
+
const trimmed = value.trim();
|
|
22677
|
+
const legacyMatch = /^agent:[^:]+:(.+)$/i.exec(trimmed);
|
|
22678
|
+
if (!legacyMatch) {
|
|
22679
|
+
return trimmed;
|
|
22680
|
+
}
|
|
22681
|
+
const routedSessionKey = legacyMatch[1]?.trim();
|
|
22682
|
+
if (typeof routedSessionKey === "string" && routedSessionKey.length > 0) {
|
|
22683
|
+
return routedSessionKey;
|
|
22684
|
+
}
|
|
22685
|
+
return fallbackSessionKey;
|
|
22686
|
+
}
|
|
22687
|
+
function isCanonicalAgentSessionKey(value) {
|
|
22688
|
+
return /^agent:[^:]+:.+/i.test(value.trim());
|
|
21871
22689
|
}
|
|
21872
22690
|
function generateOpenclawHookToken() {
|
|
21873
22691
|
return randomBytes3(OPENCLAW_HOOK_TOKEN_BYTES).toString("hex");
|
|
21874
22692
|
}
|
|
21875
22693
|
function upsertRelayHookMapping(mappingsValue) {
|
|
21876
|
-
const mappings = Array.isArray(mappingsValue) ? mappingsValue.filter(
|
|
22694
|
+
const mappings = Array.isArray(mappingsValue) ? mappingsValue.filter(isRecord9).map((mapping) => ({ ...mapping })) : [];
|
|
21877
22695
|
const existingIndex = mappings.findIndex((mapping) => {
|
|
21878
22696
|
if (mapping.id === HOOK_MAPPING_ID) {
|
|
21879
22697
|
return true;
|
|
21880
22698
|
}
|
|
21881
|
-
if (!
|
|
22699
|
+
if (!isRecord9(mapping.match)) {
|
|
21882
22700
|
return false;
|
|
21883
22701
|
}
|
|
21884
22702
|
return mapping.match.path === HOOK_PATH_SEND_TO_PEER;
|
|
21885
22703
|
});
|
|
21886
|
-
const baseMapping = existingIndex >= 0 &&
|
|
21887
|
-
const nextMatch =
|
|
22704
|
+
const baseMapping = existingIndex >= 0 && isRecord9(mappings[existingIndex]) ? mappings[existingIndex] : {};
|
|
22705
|
+
const nextMatch = isRecord9(baseMapping.match) ? { ...baseMapping.match } : {};
|
|
21888
22706
|
nextMatch.path = HOOK_PATH_SEND_TO_PEER;
|
|
21889
|
-
const nextTransform =
|
|
22707
|
+
const nextTransform = isRecord9(baseMapping.transform) ? { ...baseMapping.transform } : {};
|
|
21890
22708
|
nextTransform.module = RELAY_MODULE_FILE_NAME;
|
|
21891
22709
|
const relayMapping = {
|
|
21892
22710
|
...baseMapping,
|
|
@@ -21917,14 +22735,14 @@ async function patchOpenclawConfig(openclawConfigPath, hookToken) {
|
|
|
21917
22735
|
}
|
|
21918
22736
|
throw error48;
|
|
21919
22737
|
}
|
|
21920
|
-
if (!
|
|
22738
|
+
if (!isRecord9(config2)) {
|
|
21921
22739
|
throw createCliError6(
|
|
21922
22740
|
"CLI_OPENCLAW_INVALID_CONFIG",
|
|
21923
22741
|
"OpenClaw config root must be an object",
|
|
21924
22742
|
{ openclawConfigPath }
|
|
21925
22743
|
);
|
|
21926
22744
|
}
|
|
21927
|
-
const hooks =
|
|
22745
|
+
const hooks = isRecord9(config2.hooks) ? { ...config2.hooks } : {};
|
|
21928
22746
|
const existingHookToken = typeof hooks.token === "string" && hooks.token.trim().length > 0 ? hooks.token.trim() : void 0;
|
|
21929
22747
|
const preferredHookToken = typeof hookToken === "string" && hookToken.trim().length > 0 ? hookToken.trim() : void 0;
|
|
21930
22748
|
const resolvedHookToken = existingHookToken ?? preferredHookToken ?? generateOpenclawHookToken();
|
|
@@ -21942,7 +22760,7 @@ async function patchOpenclawConfig(openclawConfigPath, hookToken) {
|
|
|
21942
22760
|
...config2,
|
|
21943
22761
|
hooks
|
|
21944
22762
|
};
|
|
21945
|
-
await
|
|
22763
|
+
await writeFile6(
|
|
21946
22764
|
openclawConfigPath,
|
|
21947
22765
|
`${JSON.stringify(nextConfig, null, 2)}
|
|
21948
22766
|
`,
|
|
@@ -21963,10 +22781,10 @@ function toDoctorResult(checks) {
|
|
|
21963
22781
|
};
|
|
21964
22782
|
}
|
|
21965
22783
|
function isRelayHookMapping(value) {
|
|
21966
|
-
if (!
|
|
22784
|
+
if (!isRecord9(value)) {
|
|
21967
22785
|
return false;
|
|
21968
22786
|
}
|
|
21969
|
-
if (!
|
|
22787
|
+
if (!isRecord9(value.match) || value.match.path !== HOOK_PATH_SEND_TO_PEER) {
|
|
21970
22788
|
return false;
|
|
21971
22789
|
}
|
|
21972
22790
|
if (typeof value.id === "string" && value.id !== HOOK_MAPPING_ID) {
|
|
@@ -21975,7 +22793,7 @@ function isRelayHookMapping(value) {
|
|
|
21975
22793
|
return true;
|
|
21976
22794
|
}
|
|
21977
22795
|
function hasRelayTransformModule(value) {
|
|
21978
|
-
if (!
|
|
22796
|
+
if (!isRecord9(value) || !isRecord9(value.transform)) {
|
|
21979
22797
|
return false;
|
|
21980
22798
|
}
|
|
21981
22799
|
return value.transform.module === RELAY_MODULE_FILE_NAME;
|
|
@@ -22051,108 +22869,110 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22051
22869
|
const openclawDir = resolveOpenclawDir(options.openclawDir, homeDir);
|
|
22052
22870
|
const peerAlias = parseDoctorPeerAlias(options.peerAlias);
|
|
22053
22871
|
const checks = [];
|
|
22054
|
-
|
|
22055
|
-
|
|
22056
|
-
|
|
22057
|
-
|
|
22058
|
-
|
|
22059
|
-
|
|
22060
|
-
toDoctorCheck({
|
|
22061
|
-
id: "config.registry",
|
|
22062
|
-
label: "CLI config",
|
|
22063
|
-
status: "fail",
|
|
22064
|
-
message: "registryUrl is missing",
|
|
22065
|
-
remediationHint: "Run: clawdentity config set registryUrl <REGISTRY_URL>"
|
|
22066
|
-
})
|
|
22067
|
-
);
|
|
22068
|
-
} else if (typeof resolvedConfig.apiKey !== "string" || resolvedConfig.apiKey.trim().length === 0) {
|
|
22069
|
-
checks.push(
|
|
22070
|
-
toDoctorCheck({
|
|
22071
|
-
id: "config.registry",
|
|
22072
|
-
label: "CLI config",
|
|
22073
|
-
status: "fail",
|
|
22074
|
-
message: "apiKey is missing",
|
|
22075
|
-
remediationHint: "Run: clawdentity config set apiKey <API_KEY>"
|
|
22076
|
-
})
|
|
22077
|
-
);
|
|
22078
|
-
} else if (envProxyUrl.length > 0) {
|
|
22079
|
-
let hasValidEnvProxyUrl = true;
|
|
22080
|
-
try {
|
|
22081
|
-
parseProxyUrl(envProxyUrl);
|
|
22082
|
-
} catch {
|
|
22083
|
-
hasValidEnvProxyUrl = false;
|
|
22872
|
+
if (options.includeConfigCheck !== false) {
|
|
22873
|
+
const resolveConfigImpl = options.resolveConfigImpl ?? resolveConfig;
|
|
22874
|
+
try {
|
|
22875
|
+
const resolvedConfig = await resolveConfigImpl();
|
|
22876
|
+
const envProxyUrl = typeof process.env.CLAWDENTITY_PROXY_URL === "string" ? process.env.CLAWDENTITY_PROXY_URL.trim() : "";
|
|
22877
|
+
if (typeof resolvedConfig.registryUrl !== "string" || resolvedConfig.registryUrl.trim().length === 0) {
|
|
22084
22878
|
checks.push(
|
|
22085
22879
|
toDoctorCheck({
|
|
22086
22880
|
id: "config.registry",
|
|
22087
22881
|
label: "CLI config",
|
|
22088
22882
|
status: "fail",
|
|
22089
|
-
message: "
|
|
22090
|
-
remediationHint: "
|
|
22883
|
+
message: "registryUrl is missing",
|
|
22884
|
+
remediationHint: "Run: clawdentity config set registryUrl <REGISTRY_URL>"
|
|
22091
22885
|
})
|
|
22092
22886
|
);
|
|
22093
|
-
}
|
|
22094
|
-
if (hasValidEnvProxyUrl) {
|
|
22887
|
+
} else if (typeof resolvedConfig.apiKey !== "string" || resolvedConfig.apiKey.trim().length === 0) {
|
|
22095
22888
|
checks.push(
|
|
22096
22889
|
toDoctorCheck({
|
|
22097
22890
|
id: "config.registry",
|
|
22098
22891
|
label: "CLI config",
|
|
22099
|
-
status: "
|
|
22100
|
-
message: "
|
|
22892
|
+
status: "fail",
|
|
22893
|
+
message: "apiKey is missing",
|
|
22894
|
+
remediationHint: "Run: clawdentity config set apiKey <API_KEY>"
|
|
22101
22895
|
})
|
|
22102
22896
|
);
|
|
22103
|
-
}
|
|
22104
|
-
|
|
22105
|
-
|
|
22106
|
-
|
|
22107
|
-
|
|
22108
|
-
|
|
22109
|
-
|
|
22110
|
-
|
|
22111
|
-
|
|
22112
|
-
|
|
22113
|
-
|
|
22114
|
-
|
|
22115
|
-
|
|
22116
|
-
|
|
22117
|
-
|
|
22118
|
-
|
|
22119
|
-
|
|
22897
|
+
} else if (envProxyUrl.length > 0) {
|
|
22898
|
+
let hasValidEnvProxyUrl = true;
|
|
22899
|
+
try {
|
|
22900
|
+
parseProxyUrl(envProxyUrl);
|
|
22901
|
+
} catch {
|
|
22902
|
+
hasValidEnvProxyUrl = false;
|
|
22903
|
+
checks.push(
|
|
22904
|
+
toDoctorCheck({
|
|
22905
|
+
id: "config.registry",
|
|
22906
|
+
label: "CLI config",
|
|
22907
|
+
status: "fail",
|
|
22908
|
+
message: "CLAWDENTITY_PROXY_URL is invalid",
|
|
22909
|
+
remediationHint: "Set CLAWDENTITY_PROXY_URL to a valid http(s) URL or unset it"
|
|
22910
|
+
})
|
|
22911
|
+
);
|
|
22912
|
+
}
|
|
22913
|
+
if (hasValidEnvProxyUrl) {
|
|
22914
|
+
checks.push(
|
|
22915
|
+
toDoctorCheck({
|
|
22916
|
+
id: "config.registry",
|
|
22917
|
+
label: "CLI config",
|
|
22918
|
+
status: "pass",
|
|
22919
|
+
message: "registryUrl and apiKey are configured (proxy URL override is active via CLAWDENTITY_PROXY_URL)"
|
|
22920
|
+
})
|
|
22921
|
+
);
|
|
22922
|
+
}
|
|
22923
|
+
} else if (typeof resolvedConfig.proxyUrl !== "string" || resolvedConfig.proxyUrl.trim().length === 0) {
|
|
22120
22924
|
checks.push(
|
|
22121
22925
|
toDoctorCheck({
|
|
22122
22926
|
id: "config.registry",
|
|
22123
22927
|
label: "CLI config",
|
|
22124
22928
|
status: "fail",
|
|
22125
|
-
message: "proxyUrl is
|
|
22929
|
+
message: "proxyUrl is missing",
|
|
22126
22930
|
remediationHint: "Run: clawdentity invite redeem <clw_inv_...> or clawdentity config init"
|
|
22127
22931
|
})
|
|
22128
22932
|
);
|
|
22933
|
+
} else {
|
|
22934
|
+
let hasValidConfigProxyUrl = true;
|
|
22935
|
+
try {
|
|
22936
|
+
parseProxyUrl(resolvedConfig.proxyUrl);
|
|
22937
|
+
} catch {
|
|
22938
|
+
hasValidConfigProxyUrl = false;
|
|
22939
|
+
checks.push(
|
|
22940
|
+
toDoctorCheck({
|
|
22941
|
+
id: "config.registry",
|
|
22942
|
+
label: "CLI config",
|
|
22943
|
+
status: "fail",
|
|
22944
|
+
message: "proxyUrl is invalid",
|
|
22945
|
+
remediationHint: "Run: clawdentity invite redeem <clw_inv_...> or clawdentity config init"
|
|
22946
|
+
})
|
|
22947
|
+
);
|
|
22948
|
+
}
|
|
22949
|
+
if (hasValidConfigProxyUrl) {
|
|
22950
|
+
checks.push(
|
|
22951
|
+
toDoctorCheck({
|
|
22952
|
+
id: "config.registry",
|
|
22953
|
+
label: "CLI config",
|
|
22954
|
+
status: "pass",
|
|
22955
|
+
message: "registryUrl, apiKey, and proxyUrl are configured"
|
|
22956
|
+
})
|
|
22957
|
+
);
|
|
22958
|
+
}
|
|
22129
22959
|
}
|
|
22130
|
-
|
|
22131
|
-
|
|
22132
|
-
|
|
22133
|
-
|
|
22134
|
-
|
|
22135
|
-
|
|
22136
|
-
|
|
22137
|
-
|
|
22138
|
-
)
|
|
22139
|
-
|
|
22960
|
+
} catch {
|
|
22961
|
+
checks.push(
|
|
22962
|
+
toDoctorCheck({
|
|
22963
|
+
id: "config.registry",
|
|
22964
|
+
label: "CLI config",
|
|
22965
|
+
status: "fail",
|
|
22966
|
+
message: "unable to resolve CLI config",
|
|
22967
|
+
remediationHint: "Fix ~/.clawdentity/config.json or rerun: clawdentity config init"
|
|
22968
|
+
})
|
|
22969
|
+
);
|
|
22140
22970
|
}
|
|
22141
|
-
} catch {
|
|
22142
|
-
checks.push(
|
|
22143
|
-
toDoctorCheck({
|
|
22144
|
-
id: "config.registry",
|
|
22145
|
-
label: "CLI config",
|
|
22146
|
-
status: "fail",
|
|
22147
|
-
message: "unable to resolve CLI config",
|
|
22148
|
-
remediationHint: "Fix ~/.clawdentity/config.json or rerun: clawdentity config init"
|
|
22149
|
-
})
|
|
22150
|
-
);
|
|
22151
22971
|
}
|
|
22152
22972
|
const selectedAgentPath = resolveOpenclawAgentNamePath(homeDir);
|
|
22153
22973
|
let selectedAgentName;
|
|
22154
22974
|
try {
|
|
22155
|
-
const selectedAgentRaw = await
|
|
22975
|
+
const selectedAgentRaw = await readFile6(selectedAgentPath, "utf8");
|
|
22156
22976
|
selectedAgentName = assertValidAgentName(selectedAgentRaw.trim());
|
|
22157
22977
|
checks.push(
|
|
22158
22978
|
toDoctorCheck({
|
|
@@ -22275,9 +23095,9 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22275
23095
|
const relayTransformRuntimePath = resolveTransformRuntimePath(openclawDir);
|
|
22276
23096
|
const relayTransformPeersPath = resolveTransformPeersPath(openclawDir);
|
|
22277
23097
|
try {
|
|
22278
|
-
const transformContents = await
|
|
22279
|
-
const runtimeContents = await
|
|
22280
|
-
const peersSnapshotContents = await
|
|
23098
|
+
const transformContents = await readFile6(transformTargetPath, "utf8");
|
|
23099
|
+
const runtimeContents = await readFile6(relayTransformRuntimePath, "utf8");
|
|
23100
|
+
const peersSnapshotContents = await readFile6(
|
|
22281
23101
|
relayTransformPeersPath,
|
|
22282
23102
|
"utf8"
|
|
22283
23103
|
);
|
|
@@ -22330,13 +23150,22 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22330
23150
|
const openclawConfigPath = resolveOpenclawConfigPath(openclawDir, homeDir);
|
|
22331
23151
|
try {
|
|
22332
23152
|
const openclawConfig = await readJsonFile(openclawConfigPath);
|
|
22333
|
-
if (!
|
|
23153
|
+
if (!isRecord9(openclawConfig)) {
|
|
22334
23154
|
throw new Error("root");
|
|
22335
23155
|
}
|
|
22336
|
-
const hooks =
|
|
23156
|
+
const hooks = isRecord9(openclawConfig.hooks) ? openclawConfig.hooks : {};
|
|
22337
23157
|
const hooksEnabled = hooks.enabled === true;
|
|
22338
23158
|
const hookToken = typeof hooks.token === "string" && hooks.token.trim().length > 0 ? hooks.token.trim() : void 0;
|
|
22339
|
-
const
|
|
23159
|
+
const defaultSessionKey = typeof hooks.defaultSessionKey === "string" && hooks.defaultSessionKey.trim().length > 0 ? hooks.defaultSessionKey.trim() : void 0;
|
|
23160
|
+
const allowRequestSessionKey = hooks.allowRequestSessionKey === false;
|
|
23161
|
+
const allowedSessionKeyPrefixes = normalizeStringArrayWithValues(
|
|
23162
|
+
hooks.allowedSessionKeyPrefixes,
|
|
23163
|
+
[]
|
|
23164
|
+
);
|
|
23165
|
+
const missingRequiredSessionPrefixes = defaultSessionKey === void 0 ? ["hook:"] : ["hook:", defaultSessionKey].filter(
|
|
23166
|
+
(prefix) => !allowedSessionKeyPrefixes.includes(prefix)
|
|
23167
|
+
);
|
|
23168
|
+
const mappings = Array.isArray(hooks.mappings) ? hooks.mappings.filter(isRecord9) : [];
|
|
22340
23169
|
const relayMapping = mappings.find(
|
|
22341
23170
|
(mapping) => isRelayHookMapping(mapping)
|
|
22342
23171
|
);
|
|
@@ -22395,6 +23224,45 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22395
23224
|
})
|
|
22396
23225
|
);
|
|
22397
23226
|
}
|
|
23227
|
+
const sessionRoutingIssues = [];
|
|
23228
|
+
if (defaultSessionKey === void 0) {
|
|
23229
|
+
sessionRoutingIssues.push("hooks.defaultSessionKey is missing");
|
|
23230
|
+
}
|
|
23231
|
+
if (!allowRequestSessionKey) {
|
|
23232
|
+
sessionRoutingIssues.push("hooks.allowRequestSessionKey is not false");
|
|
23233
|
+
}
|
|
23234
|
+
if (missingRequiredSessionPrefixes.length > 0) {
|
|
23235
|
+
sessionRoutingIssues.push(
|
|
23236
|
+
`hooks.allowedSessionKeyPrefixes is missing: ${missingRequiredSessionPrefixes.join(", ")}`
|
|
23237
|
+
);
|
|
23238
|
+
}
|
|
23239
|
+
if (defaultSessionKey !== void 0 && isCanonicalAgentSessionKey(defaultSessionKey)) {
|
|
23240
|
+
sessionRoutingIssues.push(
|
|
23241
|
+
"hooks.defaultSessionKey uses canonical agent format (agent:<id>:...); use OpenClaw request session keys like main, global, or subagent:*"
|
|
23242
|
+
);
|
|
23243
|
+
}
|
|
23244
|
+
if (sessionRoutingIssues.length > 0) {
|
|
23245
|
+
checks.push(
|
|
23246
|
+
toDoctorCheck({
|
|
23247
|
+
id: "state.hookSessionRouting",
|
|
23248
|
+
label: "OpenClaw hook session routing",
|
|
23249
|
+
status: "fail",
|
|
23250
|
+
message: sessionRoutingIssues.join("; "),
|
|
23251
|
+
remediationHint: OPENCLAW_SETUP_RESTART_COMMAND_HINT,
|
|
23252
|
+
details: { openclawConfigPath }
|
|
23253
|
+
})
|
|
23254
|
+
);
|
|
23255
|
+
} else {
|
|
23256
|
+
checks.push(
|
|
23257
|
+
toDoctorCheck({
|
|
23258
|
+
id: "state.hookSessionRouting",
|
|
23259
|
+
label: "OpenClaw hook session routing",
|
|
23260
|
+
status: "pass",
|
|
23261
|
+
message: "hooks default session and allowed session prefixes are configured",
|
|
23262
|
+
details: { openclawConfigPath }
|
|
23263
|
+
})
|
|
23264
|
+
);
|
|
23265
|
+
}
|
|
22398
23266
|
} catch {
|
|
22399
23267
|
checks.push(
|
|
22400
23268
|
toDoctorCheck({
|
|
@@ -22416,6 +23284,16 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22416
23284
|
details: { openclawConfigPath }
|
|
22417
23285
|
})
|
|
22418
23286
|
);
|
|
23287
|
+
checks.push(
|
|
23288
|
+
toDoctorCheck({
|
|
23289
|
+
id: "state.hookSessionRouting",
|
|
23290
|
+
label: "OpenClaw hook session routing",
|
|
23291
|
+
status: "fail",
|
|
23292
|
+
message: `unable to read ${openclawConfigPath}`,
|
|
23293
|
+
remediationHint: "Ensure the OpenClaw config file exists (OPENCLAW_CONFIG_PATH/CLAWDBOT_CONFIG_PATH, or state dir) and rerun openclaw setup",
|
|
23294
|
+
details: { openclawConfigPath }
|
|
23295
|
+
})
|
|
23296
|
+
);
|
|
22419
23297
|
}
|
|
22420
23298
|
const relayRuntimeConfigPath = resolveRelayRuntimeConfigPath(homeDir);
|
|
22421
23299
|
try {
|
|
@@ -22441,6 +23319,72 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22441
23319
|
})
|
|
22442
23320
|
);
|
|
22443
23321
|
}
|
|
23322
|
+
const gatewayPendingState = await readOpenclawGatewayPendingState(openclawDir);
|
|
23323
|
+
if (gatewayPendingState.status === "missing") {
|
|
23324
|
+
checks.push(
|
|
23325
|
+
toDoctorCheck({
|
|
23326
|
+
id: "state.gatewayDevicePairing",
|
|
23327
|
+
label: "OpenClaw gateway device pairing",
|
|
23328
|
+
status: "pass",
|
|
23329
|
+
message: "no pending gateway device approvals file was found",
|
|
23330
|
+
details: {
|
|
23331
|
+
gatewayDevicePendingPath: gatewayPendingState.gatewayDevicePendingPath
|
|
23332
|
+
}
|
|
23333
|
+
})
|
|
23334
|
+
);
|
|
23335
|
+
} else if (gatewayPendingState.status === "invalid") {
|
|
23336
|
+
checks.push(
|
|
23337
|
+
toDoctorCheck({
|
|
23338
|
+
id: "state.gatewayDevicePairing",
|
|
23339
|
+
label: "OpenClaw gateway device pairing",
|
|
23340
|
+
status: "fail",
|
|
23341
|
+
message: `invalid pending device approvals file: ${gatewayPendingState.gatewayDevicePendingPath}`,
|
|
23342
|
+
remediationHint: OPENCLAW_DEVICE_APPROVAL_RECOVERY_HINT,
|
|
23343
|
+
details: {
|
|
23344
|
+
gatewayDevicePendingPath: gatewayPendingState.gatewayDevicePendingPath
|
|
23345
|
+
}
|
|
23346
|
+
})
|
|
23347
|
+
);
|
|
23348
|
+
} else if (gatewayPendingState.status === "unreadable") {
|
|
23349
|
+
checks.push(
|
|
23350
|
+
toDoctorCheck({
|
|
23351
|
+
id: "state.gatewayDevicePairing",
|
|
23352
|
+
label: "OpenClaw gateway device pairing",
|
|
23353
|
+
status: "fail",
|
|
23354
|
+
message: `unable to read pending device approvals at ${gatewayPendingState.gatewayDevicePendingPath}`,
|
|
23355
|
+
remediationHint: OPENCLAW_DEVICE_APPROVAL_RECOVERY_HINT,
|
|
23356
|
+
details: {
|
|
23357
|
+
gatewayDevicePendingPath: gatewayPendingState.gatewayDevicePendingPath
|
|
23358
|
+
}
|
|
23359
|
+
})
|
|
23360
|
+
);
|
|
23361
|
+
} else if (gatewayPendingState.pendingRequestIds.length === 0) {
|
|
23362
|
+
checks.push(
|
|
23363
|
+
toDoctorCheck({
|
|
23364
|
+
id: "state.gatewayDevicePairing",
|
|
23365
|
+
label: "OpenClaw gateway device pairing",
|
|
23366
|
+
status: "pass",
|
|
23367
|
+
message: "no pending gateway device approvals",
|
|
23368
|
+
details: {
|
|
23369
|
+
gatewayDevicePendingPath: gatewayPendingState.gatewayDevicePendingPath
|
|
23370
|
+
}
|
|
23371
|
+
})
|
|
23372
|
+
);
|
|
23373
|
+
} else {
|
|
23374
|
+
checks.push(
|
|
23375
|
+
toDoctorCheck({
|
|
23376
|
+
id: "state.gatewayDevicePairing",
|
|
23377
|
+
label: "OpenClaw gateway device pairing",
|
|
23378
|
+
status: "fail",
|
|
23379
|
+
message: `pending gateway device approvals: ${gatewayPendingState.pendingRequestIds.length}`,
|
|
23380
|
+
remediationHint: OPENCLAW_DEVICE_APPROVAL_RECOVERY_HINT,
|
|
23381
|
+
details: {
|
|
23382
|
+
gatewayDevicePendingPath: gatewayPendingState.gatewayDevicePendingPath,
|
|
23383
|
+
pendingRequestIds: gatewayPendingState.pendingRequestIds
|
|
23384
|
+
}
|
|
23385
|
+
})
|
|
23386
|
+
);
|
|
23387
|
+
}
|
|
22444
23388
|
if (options.includeConnectorRuntimeCheck !== false) {
|
|
22445
23389
|
if (selectedAgentName === void 0) {
|
|
22446
23390
|
checks.push(
|
|
@@ -22452,6 +23396,24 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22452
23396
|
remediationHint: OPENCLAW_SETUP_COMMAND_HINT
|
|
22453
23397
|
})
|
|
22454
23398
|
);
|
|
23399
|
+
checks.push(
|
|
23400
|
+
toDoctorCheck({
|
|
23401
|
+
id: "state.connectorInboundInbox",
|
|
23402
|
+
label: "Connector inbound inbox",
|
|
23403
|
+
status: "fail",
|
|
23404
|
+
message: "cannot validate connector inbound inbox without selected agent marker",
|
|
23405
|
+
remediationHint: OPENCLAW_SETUP_COMMAND_HINT
|
|
23406
|
+
})
|
|
23407
|
+
);
|
|
23408
|
+
checks.push(
|
|
23409
|
+
toDoctorCheck({
|
|
23410
|
+
id: "state.openclawHookHealth",
|
|
23411
|
+
label: "OpenClaw hook health",
|
|
23412
|
+
status: "fail",
|
|
23413
|
+
message: "cannot validate OpenClaw hook health without selected agent marker",
|
|
23414
|
+
remediationHint: OPENCLAW_SETUP_RESTART_COMMAND_HINT
|
|
23415
|
+
})
|
|
23416
|
+
);
|
|
22455
23417
|
} else {
|
|
22456
23418
|
const connectorAssignmentsPath = resolveConnectorAssignmentsPath(homeDir);
|
|
22457
23419
|
try {
|
|
@@ -22470,6 +23432,26 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22470
23432
|
details: { connectorAssignmentsPath, selectedAgentName }
|
|
22471
23433
|
})
|
|
22472
23434
|
);
|
|
23435
|
+
checks.push(
|
|
23436
|
+
toDoctorCheck({
|
|
23437
|
+
id: "state.connectorInboundInbox",
|
|
23438
|
+
label: "Connector inbound inbox",
|
|
23439
|
+
status: "fail",
|
|
23440
|
+
message: `no connector assignment found for ${selectedAgentName}`,
|
|
23441
|
+
remediationHint: OPENCLAW_SETUP_COMMAND_HINT,
|
|
23442
|
+
details: { connectorAssignmentsPath, selectedAgentName }
|
|
23443
|
+
})
|
|
23444
|
+
);
|
|
23445
|
+
checks.push(
|
|
23446
|
+
toDoctorCheck({
|
|
23447
|
+
id: "state.openclawHookHealth",
|
|
23448
|
+
label: "OpenClaw hook health",
|
|
23449
|
+
status: "fail",
|
|
23450
|
+
message: `no connector assignment found for ${selectedAgentName}`,
|
|
23451
|
+
remediationHint: OPENCLAW_SETUP_RESTART_COMMAND_HINT,
|
|
23452
|
+
details: { connectorAssignmentsPath, selectedAgentName }
|
|
23453
|
+
})
|
|
23454
|
+
);
|
|
22473
23455
|
} else {
|
|
22474
23456
|
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
22475
23457
|
if (typeof fetchImpl !== "function") {
|
|
@@ -22482,6 +23464,24 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22482
23464
|
remediationHint: "Run doctor in a Node runtime with fetch support, or rerun openclaw setup"
|
|
22483
23465
|
})
|
|
22484
23466
|
);
|
|
23467
|
+
checks.push(
|
|
23468
|
+
toDoctorCheck({
|
|
23469
|
+
id: "state.connectorInboundInbox",
|
|
23470
|
+
label: "Connector inbound inbox",
|
|
23471
|
+
status: "fail",
|
|
23472
|
+
message: "fetch implementation is unavailable for connector inbox checks",
|
|
23473
|
+
remediationHint: OPENCLAW_SETUP_COMMAND_HINT
|
|
23474
|
+
})
|
|
23475
|
+
);
|
|
23476
|
+
checks.push(
|
|
23477
|
+
toDoctorCheck({
|
|
23478
|
+
id: "state.openclawHookHealth",
|
|
23479
|
+
label: "OpenClaw hook health",
|
|
23480
|
+
status: "fail",
|
|
23481
|
+
message: "fetch implementation is unavailable for OpenClaw hook health checks",
|
|
23482
|
+
remediationHint: OPENCLAW_SETUP_RESTART_COMMAND_HINT
|
|
23483
|
+
})
|
|
23484
|
+
);
|
|
22485
23485
|
} else {
|
|
22486
23486
|
const connectorStatus = await fetchConnectorHealthStatus({
|
|
22487
23487
|
connectorBaseUrl: assignment.connectorBaseUrl,
|
|
@@ -22500,6 +23500,36 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22500
23500
|
}
|
|
22501
23501
|
})
|
|
22502
23502
|
);
|
|
23503
|
+
const inboxPendingCount = connectorStatus.inboundInbox?.pendingCount ?? 0;
|
|
23504
|
+
const replayError = connectorStatus.inboundInbox?.lastReplayError;
|
|
23505
|
+
checks.push(
|
|
23506
|
+
toDoctorCheck({
|
|
23507
|
+
id: "state.connectorInboundInbox",
|
|
23508
|
+
label: "Connector inbound inbox",
|
|
23509
|
+
status: "pass",
|
|
23510
|
+
message: inboxPendingCount === 0 ? "connector inbound inbox is empty" : `connector inbound inbox has ${inboxPendingCount} pending message(s)`,
|
|
23511
|
+
details: {
|
|
23512
|
+
connectorStatusUrl: connectorStatus.statusUrl,
|
|
23513
|
+
connectorBaseUrl: assignment.connectorBaseUrl,
|
|
23514
|
+
...connectorStatus.inboundInbox
|
|
23515
|
+
}
|
|
23516
|
+
})
|
|
23517
|
+
);
|
|
23518
|
+
checks.push(
|
|
23519
|
+
toDoctorCheck({
|
|
23520
|
+
id: "state.openclawHookHealth",
|
|
23521
|
+
label: "OpenClaw hook health",
|
|
23522
|
+
status: connectorStatus.openclawHook?.lastAttemptStatus === "failed" && inboxPendingCount > 0 ? "fail" : "pass",
|
|
23523
|
+
message: connectorStatus.openclawHook?.lastAttemptStatus === "failed" && inboxPendingCount > 0 ? `connector replay to local OpenClaw hook is failing: ${replayError ?? "unknown error"}` : "connector replay to local OpenClaw hook is healthy",
|
|
23524
|
+
remediationHint: connectorStatus.openclawHook?.lastAttemptStatus === "failed" && inboxPendingCount > 0 ? OPENCLAW_SETUP_RESTART_COMMAND_HINT : void 0,
|
|
23525
|
+
details: {
|
|
23526
|
+
connectorStatusUrl: connectorStatus.statusUrl,
|
|
23527
|
+
connectorBaseUrl: assignment.connectorBaseUrl,
|
|
23528
|
+
...connectorStatus.openclawHook,
|
|
23529
|
+
inboxPendingCount
|
|
23530
|
+
}
|
|
23531
|
+
})
|
|
23532
|
+
);
|
|
22503
23533
|
} else {
|
|
22504
23534
|
const reason = connectorStatus.reason ?? "connector runtime is unavailable";
|
|
22505
23535
|
checks.push(
|
|
@@ -22515,6 +23545,32 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22515
23545
|
}
|
|
22516
23546
|
})
|
|
22517
23547
|
);
|
|
23548
|
+
checks.push(
|
|
23549
|
+
toDoctorCheck({
|
|
23550
|
+
id: "state.connectorInboundInbox",
|
|
23551
|
+
label: "Connector inbound inbox",
|
|
23552
|
+
status: "fail",
|
|
23553
|
+
message: `unable to read connector inbound inbox status: ${reason}`,
|
|
23554
|
+
remediationHint: OPENCLAW_SETUP_COMMAND_HINT,
|
|
23555
|
+
details: {
|
|
23556
|
+
connectorStatusUrl: connectorStatus.statusUrl,
|
|
23557
|
+
connectorBaseUrl: assignment.connectorBaseUrl
|
|
23558
|
+
}
|
|
23559
|
+
})
|
|
23560
|
+
);
|
|
23561
|
+
checks.push(
|
|
23562
|
+
toDoctorCheck({
|
|
23563
|
+
id: "state.openclawHookHealth",
|
|
23564
|
+
label: "OpenClaw hook health",
|
|
23565
|
+
status: "fail",
|
|
23566
|
+
message: `unable to verify OpenClaw hook health: ${reason}`,
|
|
23567
|
+
remediationHint: OPENCLAW_SETUP_RESTART_COMMAND_HINT,
|
|
23568
|
+
details: {
|
|
23569
|
+
connectorStatusUrl: connectorStatus.statusUrl,
|
|
23570
|
+
connectorBaseUrl: assignment.connectorBaseUrl
|
|
23571
|
+
}
|
|
23572
|
+
})
|
|
23573
|
+
);
|
|
22518
23574
|
}
|
|
22519
23575
|
}
|
|
22520
23576
|
}
|
|
@@ -22529,6 +23585,24 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22529
23585
|
details: { connectorAssignmentsPath }
|
|
22530
23586
|
})
|
|
22531
23587
|
);
|
|
23588
|
+
checks.push(
|
|
23589
|
+
toDoctorCheck({
|
|
23590
|
+
id: "state.connectorInboundInbox",
|
|
23591
|
+
label: "Connector inbound inbox",
|
|
23592
|
+
status: "fail",
|
|
23593
|
+
message: "cannot validate connector inbound inbox without connector assignment",
|
|
23594
|
+
remediationHint: OPENCLAW_SETUP_COMMAND_HINT
|
|
23595
|
+
})
|
|
23596
|
+
);
|
|
23597
|
+
checks.push(
|
|
23598
|
+
toDoctorCheck({
|
|
23599
|
+
id: "state.openclawHookHealth",
|
|
23600
|
+
label: "OpenClaw hook health",
|
|
23601
|
+
status: "fail",
|
|
23602
|
+
message: "cannot validate OpenClaw hook health without connector assignment",
|
|
23603
|
+
remediationHint: OPENCLAW_SETUP_RESTART_COMMAND_HINT
|
|
23604
|
+
})
|
|
23605
|
+
);
|
|
22532
23606
|
}
|
|
22533
23607
|
}
|
|
22534
23608
|
}
|
|
@@ -22556,7 +23630,7 @@ function parseRelayProbeFailure(input) {
|
|
|
22556
23630
|
if (input.status === 500) {
|
|
22557
23631
|
return {
|
|
22558
23632
|
message: "Relay probe failed inside local relay pipeline",
|
|
22559
|
-
remediationHint: "Check
|
|
23633
|
+
remediationHint: "Check peer pairing and rerun: clawdentity openclaw setup <agentName>"
|
|
22560
23634
|
};
|
|
22561
23635
|
}
|
|
22562
23636
|
return {
|
|
@@ -22734,7 +23808,7 @@ async function setupOpenclawRelay(agentName, options) {
|
|
|
22734
23808
|
relayRuntimeConfigPath
|
|
22735
23809
|
});
|
|
22736
23810
|
await ensureLocalAgentCredentials(homeDir, normalizedAgentName);
|
|
22737
|
-
await
|
|
23811
|
+
await mkdir6(dirname5(transformTargetPath), { recursive: true });
|
|
22738
23812
|
try {
|
|
22739
23813
|
await copyFile(transformSource, transformTargetPath);
|
|
22740
23814
|
} catch (error48) {
|
|
@@ -22803,7 +23877,8 @@ async function setupOpenclawRelay(agentName, options) {
|
|
|
22803
23877
|
await saveRelayRuntimeConfig(
|
|
22804
23878
|
relayRuntimeConfigPath,
|
|
22805
23879
|
openclawBaseUrl,
|
|
22806
|
-
patchedOpenclawConfig.hookToken
|
|
23880
|
+
patchedOpenclawConfig.hookToken,
|
|
23881
|
+
relayTransformPeersPath
|
|
22807
23882
|
);
|
|
22808
23883
|
logger8.info("cli.openclaw_setup_completed", {
|
|
22809
23884
|
agentName: normalizedAgentName,
|
|
@@ -22825,9 +23900,88 @@ async function setupOpenclawRelay(agentName, options) {
|
|
|
22825
23900
|
relayRuntimeConfigPath
|
|
22826
23901
|
};
|
|
22827
23902
|
}
|
|
23903
|
+
async function assertSetupChecklistHealthy(input) {
|
|
23904
|
+
let checklist = await runOpenclawDoctor({
|
|
23905
|
+
homeDir: input.homeDir,
|
|
23906
|
+
openclawDir: input.openclawDir,
|
|
23907
|
+
includeConfigCheck: false,
|
|
23908
|
+
includeConnectorRuntimeCheck: input.includeConnectorRuntimeCheck
|
|
23909
|
+
});
|
|
23910
|
+
if (checklist.status === "healthy") {
|
|
23911
|
+
return;
|
|
23912
|
+
}
|
|
23913
|
+
let gatewayApprovalSummary;
|
|
23914
|
+
const gatewayPairingFailure = checklist.checks.find(
|
|
23915
|
+
(check2) => check2.id === "state.gatewayDevicePairing" && check2.status === "fail"
|
|
23916
|
+
);
|
|
23917
|
+
if (gatewayPairingFailure !== void 0) {
|
|
23918
|
+
gatewayApprovalSummary = await autoApproveOpenclawGatewayDevices({
|
|
23919
|
+
homeDir: input.homeDir,
|
|
23920
|
+
openclawDir: input.openclawDir,
|
|
23921
|
+
runner: input.gatewayDeviceApprovalRunner
|
|
23922
|
+
});
|
|
23923
|
+
if (gatewayApprovalSummary !== void 0) {
|
|
23924
|
+
const successfulAttempts = gatewayApprovalSummary.attempts.filter(
|
|
23925
|
+
(attempt) => attempt.ok
|
|
23926
|
+
).length;
|
|
23927
|
+
const failedAttempts = gatewayApprovalSummary.attempts.filter(
|
|
23928
|
+
(attempt) => !attempt.ok
|
|
23929
|
+
);
|
|
23930
|
+
logger8.info("cli.openclaw_setup_gateway_device_recovery_attempted", {
|
|
23931
|
+
openclawDir: input.openclawDir,
|
|
23932
|
+
pendingCount: gatewayApprovalSummary.pendingRequestIds.length,
|
|
23933
|
+
successfulAttempts,
|
|
23934
|
+
failedAttempts: failedAttempts.length,
|
|
23935
|
+
commandUnavailable: failedAttempts.some(
|
|
23936
|
+
(attempt) => attempt.unavailable
|
|
23937
|
+
)
|
|
23938
|
+
});
|
|
23939
|
+
checklist = await runOpenclawDoctor({
|
|
23940
|
+
homeDir: input.homeDir,
|
|
23941
|
+
openclawDir: input.openclawDir,
|
|
23942
|
+
includeConfigCheck: false,
|
|
23943
|
+
includeConnectorRuntimeCheck: input.includeConnectorRuntimeCheck
|
|
23944
|
+
});
|
|
23945
|
+
if (checklist.status === "healthy") {
|
|
23946
|
+
return;
|
|
23947
|
+
}
|
|
23948
|
+
}
|
|
23949
|
+
}
|
|
23950
|
+
const firstFailure = checklist.checks.find(
|
|
23951
|
+
(check2) => check2.status === "fail"
|
|
23952
|
+
);
|
|
23953
|
+
const unavailableGatewayApprovalAttempt = gatewayApprovalSummary?.attempts.find((attempt) => attempt.unavailable);
|
|
23954
|
+
const remediationHint = unavailableGatewayApprovalAttempt !== void 0 && firstFailure?.id === "state.gatewayDevicePairing" ? `${OPENCLAW_DEVICE_APPROVAL_RECOVERY_HINT}. Ensure the \`${resolveOpenclawGatewayApprovalCommand()}\` command is available.` : firstFailure?.remediationHint;
|
|
23955
|
+
throw createCliError6(
|
|
23956
|
+
"CLI_OPENCLAW_SETUP_CHECKLIST_FAILED",
|
|
23957
|
+
"OpenClaw setup checklist failed",
|
|
23958
|
+
{
|
|
23959
|
+
firstFailedCheckId: firstFailure?.id,
|
|
23960
|
+
firstFailedCheckMessage: firstFailure?.message,
|
|
23961
|
+
remediationHint,
|
|
23962
|
+
gatewayDeviceApproval: gatewayApprovalSummary,
|
|
23963
|
+
checks: checklist.checks
|
|
23964
|
+
}
|
|
23965
|
+
);
|
|
23966
|
+
}
|
|
22828
23967
|
async function setupOpenclawSelfReady(agentName, options) {
|
|
22829
|
-
const
|
|
23968
|
+
const resolvedHomeDir = resolveHomeDir(options.homeDir);
|
|
23969
|
+
const resolvedOpenclawDir = resolveOpenclawDir(
|
|
23970
|
+
options.openclawDir,
|
|
23971
|
+
resolvedHomeDir
|
|
23972
|
+
);
|
|
23973
|
+
const setup = await setupOpenclawRelay(agentName, {
|
|
23974
|
+
...options,
|
|
23975
|
+
homeDir: resolvedHomeDir,
|
|
23976
|
+
openclawDir: resolvedOpenclawDir
|
|
23977
|
+
});
|
|
22830
23978
|
if (options.noRuntimeStart === true) {
|
|
23979
|
+
await assertSetupChecklistHealthy({
|
|
23980
|
+
homeDir: resolvedHomeDir,
|
|
23981
|
+
openclawDir: resolvedOpenclawDir,
|
|
23982
|
+
includeConnectorRuntimeCheck: false,
|
|
23983
|
+
gatewayDeviceApprovalRunner: options.gatewayDeviceApprovalRunner
|
|
23984
|
+
});
|
|
22831
23985
|
return {
|
|
22832
23986
|
...setup,
|
|
22833
23987
|
runtimeMode: "none",
|
|
@@ -22846,7 +24000,6 @@ async function setupOpenclawSelfReady(agentName, options) {
|
|
|
22846
24000
|
const waitTimeoutSeconds = parseWaitTimeoutSeconds(
|
|
22847
24001
|
options.waitTimeoutSeconds
|
|
22848
24002
|
);
|
|
22849
|
-
const resolvedHomeDir = resolveHomeDir(options.homeDir);
|
|
22850
24003
|
const runtime = await startSetupConnectorRuntime({
|
|
22851
24004
|
agentName: assertValidAgentName(agentName),
|
|
22852
24005
|
homeDir: resolvedHomeDir,
|
|
@@ -22856,6 +24009,12 @@ async function setupOpenclawSelfReady(agentName, options) {
|
|
|
22856
24009
|
waitTimeoutSeconds,
|
|
22857
24010
|
fetchImpl
|
|
22858
24011
|
});
|
|
24012
|
+
await assertSetupChecklistHealthy({
|
|
24013
|
+
homeDir: resolvedHomeDir,
|
|
24014
|
+
openclawDir: resolvedOpenclawDir,
|
|
24015
|
+
includeConnectorRuntimeCheck: true,
|
|
24016
|
+
gatewayDeviceApprovalRunner: options.gatewayDeviceApprovalRunner
|
|
24017
|
+
});
|
|
22859
24018
|
return {
|
|
22860
24019
|
...setup,
|
|
22861
24020
|
...runtime
|
|
@@ -22984,13 +24143,13 @@ var createOpenclawCommand = () => {
|
|
|
22984
24143
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
22985
24144
|
import {
|
|
22986
24145
|
chmod as chmod4,
|
|
22987
|
-
mkdir as
|
|
24146
|
+
mkdir as mkdir7,
|
|
22988
24147
|
readdir,
|
|
22989
|
-
readFile as
|
|
24148
|
+
readFile as readFile7,
|
|
22990
24149
|
unlink as unlink2,
|
|
22991
|
-
writeFile as
|
|
24150
|
+
writeFile as writeFile7
|
|
22992
24151
|
} from "fs/promises";
|
|
22993
|
-
import { dirname as
|
|
24152
|
+
import { dirname as dirname6, join as join8, resolve } from "path";
|
|
22994
24153
|
import { Command as Command8 } from "commander";
|
|
22995
24154
|
import jsQR from "jsqr";
|
|
22996
24155
|
import { PNG } from "pngjs";
|
|
@@ -23001,6 +24160,7 @@ var AIT_FILE_NAME4 = "ait.jwt";
|
|
|
23001
24160
|
var SECRET_KEY_FILE_NAME3 = "secret.key";
|
|
23002
24161
|
var PAIRING_QR_DIR_NAME = "pairing";
|
|
23003
24162
|
var PEERS_FILE_NAME2 = "peers.json";
|
|
24163
|
+
var OPENCLAW_RELAY_RUNTIME_FILE_NAME3 = "openclaw-relay.json";
|
|
23004
24164
|
var PAIR_START_PATH = "/pair/start";
|
|
23005
24165
|
var PAIR_CONFIRM_PATH = "/pair/confirm";
|
|
23006
24166
|
var PAIR_STATUS_PATH = "/pair/status";
|
|
@@ -23013,7 +24173,7 @@ var PEER_ALIAS_PATTERN2 = /^[a-zA-Z0-9._-]+$/;
|
|
|
23013
24173
|
var DEFAULT_STATUS_WAIT_SECONDS = 300;
|
|
23014
24174
|
var DEFAULT_STATUS_POLL_INTERVAL_SECONDS = 3;
|
|
23015
24175
|
var MAX_PROFILE_NAME_LENGTH = 64;
|
|
23016
|
-
var
|
|
24176
|
+
var isRecord10 = (value) => {
|
|
23017
24177
|
return typeof value === "object" && value !== null;
|
|
23018
24178
|
};
|
|
23019
24179
|
function createCliError7(code, message2) {
|
|
@@ -23061,7 +24221,7 @@ function parseProfileName(value, label) {
|
|
|
23061
24221
|
return candidate;
|
|
23062
24222
|
}
|
|
23063
24223
|
function parsePeerProfile(payload) {
|
|
23064
|
-
if (!
|
|
24224
|
+
if (!isRecord10(payload)) {
|
|
23065
24225
|
throw createCliError7(
|
|
23066
24226
|
"CLI_PAIR_PROFILE_INVALID",
|
|
23067
24227
|
"Pair profile must be an object"
|
|
@@ -23108,7 +24268,7 @@ function parsePairingTicketIssuerOrigin(ticket) {
|
|
|
23108
24268
|
"Pairing ticket is invalid"
|
|
23109
24269
|
);
|
|
23110
24270
|
}
|
|
23111
|
-
if (!
|
|
24271
|
+
if (!isRecord10(payload) || typeof payload.iss !== "string") {
|
|
23112
24272
|
throw createCliError7(
|
|
23113
24273
|
"CLI_PAIR_CONFIRM_TICKET_INVALID",
|
|
23114
24274
|
"Pairing ticket is invalid"
|
|
@@ -23157,7 +24317,7 @@ function parseAitAgentDid(ait) {
|
|
|
23157
24317
|
"Agent AIT is invalid. Recreate the agent before pairing."
|
|
23158
24318
|
);
|
|
23159
24319
|
}
|
|
23160
|
-
if (!
|
|
24320
|
+
if (!isRecord10(payload) || typeof payload.sub !== "string") {
|
|
23161
24321
|
throw createCliError7(
|
|
23162
24322
|
"CLI_PAIR_AGENT_NOT_FOUND",
|
|
23163
24323
|
"Agent AIT is invalid. Recreate the agent before pairing."
|
|
@@ -23219,10 +24379,10 @@ function resolvePeerAlias(input) {
|
|
|
23219
24379
|
return `${baseAlias}-${index}`;
|
|
23220
24380
|
}
|
|
23221
24381
|
function resolvePeersConfigPath(getConfigDirImpl) {
|
|
23222
|
-
return
|
|
24382
|
+
return join8(getConfigDirImpl(), PEERS_FILE_NAME2);
|
|
23223
24383
|
}
|
|
23224
24384
|
function parsePeerEntry(value) {
|
|
23225
|
-
if (!
|
|
24385
|
+
if (!isRecord10(value)) {
|
|
23226
24386
|
throw createCliError7(
|
|
23227
24387
|
"CLI_PAIR_PEERS_CONFIG_INVALID",
|
|
23228
24388
|
"Peer entry must be an object"
|
|
@@ -23271,7 +24431,7 @@ async function loadPeersConfig2(input) {
|
|
|
23271
24431
|
"Peer config is not valid JSON"
|
|
23272
24432
|
);
|
|
23273
24433
|
}
|
|
23274
|
-
if (!
|
|
24434
|
+
if (!isRecord10(parsed)) {
|
|
23275
24435
|
throw createCliError7(
|
|
23276
24436
|
"CLI_PAIR_PEERS_CONFIG_INVALID",
|
|
23277
24437
|
"Peer config must be a JSON object"
|
|
@@ -23280,7 +24440,7 @@ async function loadPeersConfig2(input) {
|
|
|
23280
24440
|
if (parsed.peers === void 0) {
|
|
23281
24441
|
return { peers: {} };
|
|
23282
24442
|
}
|
|
23283
|
-
if (!
|
|
24443
|
+
if (!isRecord10(parsed.peers)) {
|
|
23284
24444
|
throw createCliError7(
|
|
23285
24445
|
"CLI_PAIR_PEERS_CONFIG_INVALID",
|
|
23286
24446
|
"Peer config peers field must be an object"
|
|
@@ -23294,7 +24454,7 @@ async function loadPeersConfig2(input) {
|
|
|
23294
24454
|
}
|
|
23295
24455
|
async function savePeersConfig2(input) {
|
|
23296
24456
|
const peersPath = resolvePeersConfigPath(input.getConfigDirImpl);
|
|
23297
|
-
await input.mkdirImpl(
|
|
24457
|
+
await input.mkdirImpl(dirname6(peersPath), { recursive: true });
|
|
23298
24458
|
await input.writeFileImpl(
|
|
23299
24459
|
peersPath,
|
|
23300
24460
|
`${JSON.stringify(input.config, null, 2)}
|
|
@@ -23303,6 +24463,86 @@ async function savePeersConfig2(input) {
|
|
|
23303
24463
|
);
|
|
23304
24464
|
await input.chmodImpl(peersPath, FILE_MODE4);
|
|
23305
24465
|
}
|
|
24466
|
+
function resolveRelayRuntimeConfigPath2(getConfigDirImpl) {
|
|
24467
|
+
return join8(getConfigDirImpl(), OPENCLAW_RELAY_RUNTIME_FILE_NAME3);
|
|
24468
|
+
}
|
|
24469
|
+
async function loadRelayTransformPeersPath(input) {
|
|
24470
|
+
const relayRuntimeConfigPath = resolveRelayRuntimeConfigPath2(
|
|
24471
|
+
input.getConfigDirImpl
|
|
24472
|
+
);
|
|
24473
|
+
let raw;
|
|
24474
|
+
try {
|
|
24475
|
+
raw = await input.readFileImpl(relayRuntimeConfigPath, "utf8");
|
|
24476
|
+
} catch (error48) {
|
|
24477
|
+
const nodeError = error48;
|
|
24478
|
+
if (nodeError.code === "ENOENT") {
|
|
24479
|
+
return void 0;
|
|
24480
|
+
}
|
|
24481
|
+
logger9.warn("cli.pair.relay_runtime_read_failed", {
|
|
24482
|
+
relayRuntimeConfigPath,
|
|
24483
|
+
reason: error48 instanceof Error && error48.message.length > 0 ? error48.message : "unknown"
|
|
24484
|
+
});
|
|
24485
|
+
return void 0;
|
|
24486
|
+
}
|
|
24487
|
+
let parsed;
|
|
24488
|
+
try {
|
|
24489
|
+
parsed = JSON.parse(raw);
|
|
24490
|
+
} catch {
|
|
24491
|
+
logger9.warn("cli.pair.relay_runtime_invalid_json", {
|
|
24492
|
+
relayRuntimeConfigPath
|
|
24493
|
+
});
|
|
24494
|
+
return void 0;
|
|
24495
|
+
}
|
|
24496
|
+
if (!isRecord10(parsed)) {
|
|
24497
|
+
return void 0;
|
|
24498
|
+
}
|
|
24499
|
+
const relayTransformPeersPath = parseNonEmptyString9(
|
|
24500
|
+
parsed.relayTransformPeersPath
|
|
24501
|
+
);
|
|
24502
|
+
if (relayTransformPeersPath.length === 0) {
|
|
24503
|
+
return void 0;
|
|
24504
|
+
}
|
|
24505
|
+
return resolve(relayTransformPeersPath);
|
|
24506
|
+
}
|
|
24507
|
+
async function syncOpenclawRelayPeersSnapshot(input) {
|
|
24508
|
+
const relayTransformPeersPath = await loadRelayTransformPeersPath({
|
|
24509
|
+
getConfigDirImpl: input.getConfigDirImpl,
|
|
24510
|
+
readFileImpl: input.readFileImpl
|
|
24511
|
+
});
|
|
24512
|
+
if (relayTransformPeersPath === void 0) {
|
|
24513
|
+
return;
|
|
24514
|
+
}
|
|
24515
|
+
try {
|
|
24516
|
+
await input.readFileImpl(relayTransformPeersPath, "utf8");
|
|
24517
|
+
} catch (error48) {
|
|
24518
|
+
const nodeError = error48;
|
|
24519
|
+
if (nodeError.code === "ENOENT") {
|
|
24520
|
+
return;
|
|
24521
|
+
}
|
|
24522
|
+
logger9.warn("cli.pair.relay_peers_snapshot_probe_failed", {
|
|
24523
|
+
relayTransformPeersPath,
|
|
24524
|
+
reason: error48 instanceof Error && error48.message.length > 0 ? error48.message : "unknown"
|
|
24525
|
+
});
|
|
24526
|
+
return;
|
|
24527
|
+
}
|
|
24528
|
+
try {
|
|
24529
|
+
await input.mkdirImpl(dirname6(relayTransformPeersPath), {
|
|
24530
|
+
recursive: true
|
|
24531
|
+
});
|
|
24532
|
+
await input.writeFileImpl(
|
|
24533
|
+
relayTransformPeersPath,
|
|
24534
|
+
`${JSON.stringify(input.config, null, 2)}
|
|
24535
|
+
`,
|
|
24536
|
+
"utf8"
|
|
24537
|
+
);
|
|
24538
|
+
await input.chmodImpl(relayTransformPeersPath, FILE_MODE4);
|
|
24539
|
+
} catch (error48) {
|
|
24540
|
+
logger9.warn("cli.pair.relay_peers_snapshot_write_failed", {
|
|
24541
|
+
relayTransformPeersPath,
|
|
24542
|
+
reason: error48 instanceof Error && error48.message.length > 0 ? error48.message : "unknown"
|
|
24543
|
+
});
|
|
24544
|
+
}
|
|
24545
|
+
}
|
|
23306
24546
|
function parseTtlSeconds(value) {
|
|
23307
24547
|
const raw = parseNonEmptyString9(value);
|
|
23308
24548
|
if (raw.length === 0) {
|
|
@@ -23386,7 +24626,7 @@ function toPathWithQuery3(url2) {
|
|
|
23386
24626
|
return `${parsed.pathname}${parsed.search}`;
|
|
23387
24627
|
}
|
|
23388
24628
|
function extractErrorCode(payload) {
|
|
23389
|
-
if (!
|
|
24629
|
+
if (!isRecord10(payload)) {
|
|
23390
24630
|
return void 0;
|
|
23391
24631
|
}
|
|
23392
24632
|
const envelope = payload;
|
|
@@ -23397,7 +24637,7 @@ function extractErrorCode(payload) {
|
|
|
23397
24637
|
return code.length > 0 ? code : void 0;
|
|
23398
24638
|
}
|
|
23399
24639
|
function extractErrorMessage(payload) {
|
|
23400
|
-
if (!
|
|
24640
|
+
if (!isRecord10(payload)) {
|
|
23401
24641
|
return void 0;
|
|
23402
24642
|
}
|
|
23403
24643
|
const envelope = payload;
|
|
@@ -23485,7 +24725,7 @@ function mapStatusPairError(status, payload) {
|
|
|
23485
24725
|
return `Pair status failed (${status})`;
|
|
23486
24726
|
}
|
|
23487
24727
|
function parsePairStartResponse(payload) {
|
|
23488
|
-
if (!
|
|
24728
|
+
if (!isRecord10(payload)) {
|
|
23489
24729
|
throw createCliError7(
|
|
23490
24730
|
"CLI_PAIR_START_INVALID_RESPONSE",
|
|
23491
24731
|
"Pair start response is invalid"
|
|
@@ -23517,7 +24757,7 @@ function parsePairStartResponse(payload) {
|
|
|
23517
24757
|
};
|
|
23518
24758
|
}
|
|
23519
24759
|
function parsePairConfirmResponse(payload) {
|
|
23520
|
-
if (!
|
|
24760
|
+
if (!isRecord10(payload)) {
|
|
23521
24761
|
throw createCliError7(
|
|
23522
24762
|
"CLI_PAIR_CONFIRM_INVALID_RESPONSE",
|
|
23523
24763
|
"Pair confirm response is invalid"
|
|
@@ -23552,7 +24792,7 @@ function parsePairConfirmResponse(payload) {
|
|
|
23552
24792
|
};
|
|
23553
24793
|
}
|
|
23554
24794
|
function parsePairStatusResponse(payload) {
|
|
23555
|
-
if (!
|
|
24795
|
+
if (!isRecord10(payload)) {
|
|
23556
24796
|
throw createCliError7(
|
|
23557
24797
|
"CLI_PAIR_STATUS_INVALID_RESPONSE",
|
|
23558
24798
|
"Pair status response is invalid"
|
|
@@ -23618,16 +24858,16 @@ function parsePairStatusResponse(payload) {
|
|
|
23618
24858
|
};
|
|
23619
24859
|
}
|
|
23620
24860
|
async function readAgentProofMaterial(agentName, dependencies) {
|
|
23621
|
-
const readFileImpl = dependencies.readFileImpl ??
|
|
24861
|
+
const readFileImpl = dependencies.readFileImpl ?? readFile7;
|
|
23622
24862
|
const getConfigDirImpl = dependencies.getConfigDirImpl ?? getConfigDir;
|
|
23623
24863
|
const normalizedAgentName = assertValidAgentName(agentName);
|
|
23624
|
-
const agentDir =
|
|
24864
|
+
const agentDir = join8(
|
|
23625
24865
|
getConfigDirImpl(),
|
|
23626
24866
|
AGENTS_DIR_NAME5,
|
|
23627
24867
|
normalizedAgentName
|
|
23628
24868
|
);
|
|
23629
|
-
const aitPath =
|
|
23630
|
-
const secretKeyPath =
|
|
24869
|
+
const aitPath = join8(agentDir, AIT_FILE_NAME4);
|
|
24870
|
+
const secretKeyPath = join8(agentDir, SECRET_KEY_FILE_NAME3);
|
|
23631
24871
|
let ait;
|
|
23632
24872
|
try {
|
|
23633
24873
|
ait = (await readFileImpl(aitPath, "utf-8")).trim();
|
|
@@ -23725,14 +24965,14 @@ function decodeTicketFromPng(imageBytes) {
|
|
|
23725
24965
|
return parsePairingTicket(decoded.data);
|
|
23726
24966
|
}
|
|
23727
24967
|
async function persistPairingQr(input) {
|
|
23728
|
-
const mkdirImpl = input.dependencies.mkdirImpl ??
|
|
24968
|
+
const mkdirImpl = input.dependencies.mkdirImpl ?? mkdir7;
|
|
23729
24969
|
const readdirImpl = input.dependencies.readdirImpl ?? readdir;
|
|
23730
24970
|
const unlinkImpl = input.dependencies.unlinkImpl ?? unlink2;
|
|
23731
|
-
const writeFileImpl = input.dependencies.writeFileImpl ??
|
|
24971
|
+
const writeFileImpl = input.dependencies.writeFileImpl ?? writeFile7;
|
|
23732
24972
|
const getConfigDirImpl = input.dependencies.getConfigDirImpl ?? getConfigDir;
|
|
23733
24973
|
const qrEncodeImpl = input.dependencies.qrEncodeImpl ?? encodeTicketQrPng;
|
|
23734
|
-
const baseDir =
|
|
23735
|
-
const outputPath = parseNonEmptyString9(input.qrOutput) ? resolve(input.qrOutput ?? "") :
|
|
24974
|
+
const baseDir = join8(getConfigDirImpl(), PAIRING_QR_DIR_NAME);
|
|
24975
|
+
const outputPath = parseNonEmptyString9(input.qrOutput) ? resolve(input.qrOutput ?? "") : join8(
|
|
23736
24976
|
baseDir,
|
|
23737
24977
|
`${assertValidAgentName(input.agentName)}-pair-${input.nowSeconds}.png`
|
|
23738
24978
|
);
|
|
@@ -23758,7 +24998,7 @@ async function persistPairingQr(input) {
|
|
|
23758
24998
|
if (issuedAtSeconds + PAIRING_QR_MAX_AGE_SECONDS > input.nowSeconds) {
|
|
23759
24999
|
continue;
|
|
23760
25000
|
}
|
|
23761
|
-
const stalePath =
|
|
25001
|
+
const stalePath = join8(baseDir, fileName);
|
|
23762
25002
|
await unlinkImpl(stalePath).catch((error48) => {
|
|
23763
25003
|
const nodeError = error48;
|
|
23764
25004
|
if (nodeError.code === "ENOENT") {
|
|
@@ -23767,7 +25007,7 @@ async function persistPairingQr(input) {
|
|
|
23767
25007
|
throw error48;
|
|
23768
25008
|
});
|
|
23769
25009
|
}
|
|
23770
|
-
await mkdirImpl(
|
|
25010
|
+
await mkdirImpl(dirname6(outputPath), { recursive: true });
|
|
23771
25011
|
const imageBytes = await qrEncodeImpl(input.ticket);
|
|
23772
25012
|
await writeFileImpl(outputPath, imageBytes);
|
|
23773
25013
|
return outputPath;
|
|
@@ -23801,9 +25041,9 @@ function resolveConfirmTicketSource(options) {
|
|
|
23801
25041
|
}
|
|
23802
25042
|
async function persistPairedPeer(input) {
|
|
23803
25043
|
const getConfigDirImpl = input.dependencies.getConfigDirImpl ?? getConfigDir;
|
|
23804
|
-
const readFileImpl = input.dependencies.readFileImpl ??
|
|
23805
|
-
const mkdirImpl = input.dependencies.mkdirImpl ??
|
|
23806
|
-
const writeFileImpl = input.dependencies.writeFileImpl ??
|
|
25044
|
+
const readFileImpl = input.dependencies.readFileImpl ?? readFile7;
|
|
25045
|
+
const mkdirImpl = input.dependencies.mkdirImpl ?? mkdir7;
|
|
25046
|
+
const writeFileImpl = input.dependencies.writeFileImpl ?? writeFile7;
|
|
23807
25047
|
const chmodImpl = input.dependencies.chmodImpl ?? chmod4;
|
|
23808
25048
|
const issuerOrigin = parsePairingTicketIssuerOrigin(input.ticket);
|
|
23809
25049
|
const peerProxyUrl = new URL("/hooks/agent", `${issuerOrigin}/`).toString();
|
|
@@ -23828,6 +25068,14 @@ async function persistPairedPeer(input) {
|
|
|
23828
25068
|
writeFileImpl,
|
|
23829
25069
|
chmodImpl
|
|
23830
25070
|
});
|
|
25071
|
+
await syncOpenclawRelayPeersSnapshot({
|
|
25072
|
+
config: peersConfig,
|
|
25073
|
+
getConfigDirImpl,
|
|
25074
|
+
readFileImpl,
|
|
25075
|
+
mkdirImpl,
|
|
25076
|
+
writeFileImpl,
|
|
25077
|
+
chmodImpl
|
|
25078
|
+
});
|
|
23831
25079
|
return alias;
|
|
23832
25080
|
}
|
|
23833
25081
|
async function startPairing(agentName, options, dependencies = {}) {
|
|
@@ -23907,7 +25155,7 @@ async function confirmPairing(agentName, options, dependencies = {}) {
|
|
|
23907
25155
|
const resolveConfigImpl = dependencies.resolveConfigImpl ?? resolveConfig;
|
|
23908
25156
|
const nowSecondsImpl = dependencies.nowSecondsImpl ?? (() => Math.floor(Date.now() / 1e3));
|
|
23909
25157
|
const nonceFactoryImpl = dependencies.nonceFactoryImpl ?? (() => randomBytes4(NONCE_SIZE2).toString("base64url"));
|
|
23910
|
-
const readFileImpl = dependencies.readFileImpl ??
|
|
25158
|
+
const readFileImpl = dependencies.readFileImpl ?? readFile7;
|
|
23911
25159
|
const qrDecodeImpl = dependencies.qrDecodeImpl ?? decodeTicketFromPng;
|
|
23912
25160
|
const config2 = await resolveConfigImpl();
|
|
23913
25161
|
const normalizedAgentName = assertValidAgentName(agentName);
|
|
@@ -24336,16 +25584,16 @@ import { Command as Command9 } from "commander";
|
|
|
24336
25584
|
|
|
24337
25585
|
// src/install-skill-mode.ts
|
|
24338
25586
|
import { constants, existsSync as existsSync2 } from "fs";
|
|
24339
|
-
import { access as access3, copyFile as copyFile2, mkdir as
|
|
25587
|
+
import { access as access3, copyFile as copyFile2, mkdir as mkdir8, readdir as readdir2, readFile as readFile8 } from "fs/promises";
|
|
24340
25588
|
import { createRequire } from "module";
|
|
24341
25589
|
import { homedir as homedir4 } from "os";
|
|
24342
|
-
import { dirname as
|
|
25590
|
+
import { dirname as dirname7, join as join9, relative } from "path";
|
|
24343
25591
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
24344
25592
|
var OPENCLAW_DIR_NAME2 = ".openclaw";
|
|
24345
25593
|
var SKILL_PACKAGE_NAME = "@clawdentity/openclaw-skill";
|
|
24346
25594
|
var SKILL_DIR_NAME2 = "clawdentity-openclaw-relay";
|
|
24347
25595
|
var RELAY_MODULE_FILE_NAME2 = "relay-to-peer.mjs";
|
|
24348
|
-
function
|
|
25596
|
+
function isRecord11(value) {
|
|
24349
25597
|
return typeof value === "object" && value !== null;
|
|
24350
25598
|
}
|
|
24351
25599
|
var SkillInstallError = class extends Error {
|
|
@@ -24359,7 +25607,7 @@ var SkillInstallError = class extends Error {
|
|
|
24359
25607
|
}
|
|
24360
25608
|
};
|
|
24361
25609
|
function getErrorCode3(error48) {
|
|
24362
|
-
if (!
|
|
25610
|
+
if (!isRecord11(error48)) {
|
|
24363
25611
|
return void 0;
|
|
24364
25612
|
}
|
|
24365
25613
|
return typeof error48.code === "string" ? error48.code : void 0;
|
|
@@ -24374,7 +25622,7 @@ function resolveOpenclawDir2(homeDir, inputOpenclawDir) {
|
|
|
24374
25622
|
if (typeof inputOpenclawDir === "string" && inputOpenclawDir.trim().length > 0) {
|
|
24375
25623
|
return inputOpenclawDir.trim();
|
|
24376
25624
|
}
|
|
24377
|
-
return
|
|
25625
|
+
return join9(homeDir, OPENCLAW_DIR_NAME2);
|
|
24378
25626
|
}
|
|
24379
25627
|
function resolveSkillPackageRoot(input) {
|
|
24380
25628
|
if (typeof input.skillPackageRoot === "string" && input.skillPackageRoot.trim().length > 0) {
|
|
@@ -24384,8 +25632,8 @@ function resolveSkillPackageRoot(input) {
|
|
|
24384
25632
|
if (typeof overriddenRoot === "string" && overriddenRoot.trim().length > 0) {
|
|
24385
25633
|
return overriddenRoot.trim();
|
|
24386
25634
|
}
|
|
24387
|
-
const bundledSkillRoot =
|
|
24388
|
-
|
|
25635
|
+
const bundledSkillRoot = join9(
|
|
25636
|
+
dirname7(fileURLToPath3(import.meta.url)),
|
|
24389
25637
|
"..",
|
|
24390
25638
|
"skill-bundle",
|
|
24391
25639
|
"openclaw-skill"
|
|
@@ -24397,10 +25645,10 @@ function resolveSkillPackageRoot(input) {
|
|
|
24397
25645
|
let packageJsonPath;
|
|
24398
25646
|
try {
|
|
24399
25647
|
packageJsonPath = require3.resolve(`${SKILL_PACKAGE_NAME}/package.json`);
|
|
24400
|
-
return
|
|
25648
|
+
return dirname7(packageJsonPath);
|
|
24401
25649
|
} catch {
|
|
24402
|
-
const workspaceFallbackRoot =
|
|
24403
|
-
|
|
25650
|
+
const workspaceFallbackRoot = join9(
|
|
25651
|
+
dirname7(fileURLToPath3(import.meta.url)),
|
|
24404
25652
|
"..",
|
|
24405
25653
|
"..",
|
|
24406
25654
|
"openclaw-skill"
|
|
@@ -24442,7 +25690,7 @@ async function listFilesRecursively(directoryPath) {
|
|
|
24442
25690
|
for (const entry of entries.sort(
|
|
24443
25691
|
(left, right) => left.name.localeCompare(right.name)
|
|
24444
25692
|
)) {
|
|
24445
|
-
const entryPath =
|
|
25693
|
+
const entryPath = join9(directoryPath, entry.name);
|
|
24446
25694
|
if (entry.isDirectory()) {
|
|
24447
25695
|
files.push(...await listFilesRecursively(entryPath));
|
|
24448
25696
|
continue;
|
|
@@ -24454,10 +25702,10 @@ async function listFilesRecursively(directoryPath) {
|
|
|
24454
25702
|
return files;
|
|
24455
25703
|
}
|
|
24456
25704
|
async function resolveArtifacts(input) {
|
|
24457
|
-
const skillRoot =
|
|
24458
|
-
const skillDocSource =
|
|
24459
|
-
const referencesRoot =
|
|
24460
|
-
const relaySource =
|
|
25705
|
+
const skillRoot = join9(input.skillPackageRoot, "skill");
|
|
25706
|
+
const skillDocSource = join9(skillRoot, "SKILL.md");
|
|
25707
|
+
const referencesRoot = join9(skillRoot, "references");
|
|
25708
|
+
const relaySource = join9(
|
|
24461
25709
|
input.skillPackageRoot,
|
|
24462
25710
|
"dist",
|
|
24463
25711
|
RELAY_MODULE_FILE_NAME2
|
|
@@ -24493,19 +25741,19 @@ async function resolveArtifacts(input) {
|
|
|
24493
25741
|
}
|
|
24494
25742
|
});
|
|
24495
25743
|
}
|
|
24496
|
-
const targetSkillRoot =
|
|
25744
|
+
const targetSkillRoot = join9(input.openclawDir, "skills", SKILL_DIR_NAME2);
|
|
24497
25745
|
const artifacts = [
|
|
24498
25746
|
{
|
|
24499
25747
|
sourcePath: skillDocSource,
|
|
24500
|
-
targetPath:
|
|
25748
|
+
targetPath: join9(targetSkillRoot, "SKILL.md")
|
|
24501
25749
|
},
|
|
24502
25750
|
{
|
|
24503
25751
|
sourcePath: relaySource,
|
|
24504
|
-
targetPath:
|
|
25752
|
+
targetPath: join9(targetSkillRoot, RELAY_MODULE_FILE_NAME2)
|
|
24505
25753
|
},
|
|
24506
25754
|
{
|
|
24507
25755
|
sourcePath: relaySource,
|
|
24508
|
-
targetPath:
|
|
25756
|
+
targetPath: join9(
|
|
24509
25757
|
input.openclawDir,
|
|
24510
25758
|
"hooks",
|
|
24511
25759
|
"transforms",
|
|
@@ -24517,7 +25765,7 @@ async function resolveArtifacts(input) {
|
|
|
24517
25765
|
const relativePath = relative(referencesRoot, referenceFile);
|
|
24518
25766
|
artifacts.push({
|
|
24519
25767
|
sourcePath: referenceFile,
|
|
24520
|
-
targetPath:
|
|
25768
|
+
targetPath: join9(targetSkillRoot, "references", relativePath)
|
|
24521
25769
|
});
|
|
24522
25770
|
}
|
|
24523
25771
|
return artifacts.sort(
|
|
@@ -24525,10 +25773,10 @@ async function resolveArtifacts(input) {
|
|
|
24525
25773
|
);
|
|
24526
25774
|
}
|
|
24527
25775
|
async function copyArtifact(input) {
|
|
24528
|
-
const sourceContent = await
|
|
25776
|
+
const sourceContent = await readFile8(input.sourcePath);
|
|
24529
25777
|
let existingContent;
|
|
24530
25778
|
try {
|
|
24531
|
-
existingContent = await
|
|
25779
|
+
existingContent = await readFile8(input.targetPath);
|
|
24532
25780
|
} catch (error48) {
|
|
24533
25781
|
if (getErrorCode3(error48) !== "ENOENT") {
|
|
24534
25782
|
throw error48;
|
|
@@ -24537,7 +25785,7 @@ async function copyArtifact(input) {
|
|
|
24537
25785
|
if (existingContent !== void 0 && sourceContent.equals(existingContent)) {
|
|
24538
25786
|
return "unchanged";
|
|
24539
25787
|
}
|
|
24540
|
-
await
|
|
25788
|
+
await mkdir8(dirname7(input.targetPath), { recursive: true });
|
|
24541
25789
|
await copyFile2(input.sourcePath, input.targetPath);
|
|
24542
25790
|
if (existingContent !== void 0) {
|
|
24543
25791
|
return "updated";
|
|
@@ -24569,7 +25817,7 @@ async function installOpenclawSkillArtifacts(options = {}) {
|
|
|
24569
25817
|
homeDir,
|
|
24570
25818
|
openclawDir,
|
|
24571
25819
|
skillPackageRoot,
|
|
24572
|
-
targetSkillDirectory:
|
|
25820
|
+
targetSkillDirectory: join9(openclawDir, "skills", SKILL_DIR_NAME2),
|
|
24573
25821
|
records
|
|
24574
25822
|
};
|
|
24575
25823
|
}
|
|
@@ -24660,7 +25908,7 @@ var createSkillCommand = () => {
|
|
|
24660
25908
|
};
|
|
24661
25909
|
|
|
24662
25910
|
// src/commands/verify.ts
|
|
24663
|
-
import { readFile as
|
|
25911
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
24664
25912
|
import { Command as Command10 } from "commander";
|
|
24665
25913
|
var logger10 = createLogger({ service: "cli", module: "verify" });
|
|
24666
25914
|
var REGISTRY_KEYS_CACHE_FILE = "registry-keys.json";
|
|
@@ -24673,7 +25921,7 @@ var VerifyCommandError = class extends Error {
|
|
|
24673
25921
|
this.name = "VerifyCommandError";
|
|
24674
25922
|
}
|
|
24675
25923
|
};
|
|
24676
|
-
var
|
|
25924
|
+
var isRecord12 = (value) => {
|
|
24677
25925
|
return typeof value === "object" && value !== null;
|
|
24678
25926
|
};
|
|
24679
25927
|
var normalizeRegistryUrl2 = (registryUrl) => {
|
|
@@ -24709,7 +25957,7 @@ var resolveToken = async (tokenOrFile) => {
|
|
|
24709
25957
|
throw new VerifyCommandError("invalid token (value is empty)");
|
|
24710
25958
|
}
|
|
24711
25959
|
try {
|
|
24712
|
-
const fileContents = await
|
|
25960
|
+
const fileContents = await readFile9(input, "utf-8");
|
|
24713
25961
|
const token = fileContents.trim();
|
|
24714
25962
|
if (token.length === 0) {
|
|
24715
25963
|
throw new VerifyCommandError(`invalid token (${input} is empty)`);
|
|
@@ -24741,7 +25989,7 @@ var parseResponseJson = async (response) => {
|
|
|
24741
25989
|
}
|
|
24742
25990
|
};
|
|
24743
25991
|
var parseSigningKeys = (payload) => {
|
|
24744
|
-
if (!
|
|
25992
|
+
if (!isRecord12(payload) || !Array.isArray(payload.keys)) {
|
|
24745
25993
|
throw new VerifyCommandError(
|
|
24746
25994
|
"verification keys unavailable (response payload is invalid)"
|
|
24747
25995
|
);
|
|
@@ -24760,7 +26008,7 @@ var parseSigningKeys = (payload) => {
|
|
|
24760
26008
|
};
|
|
24761
26009
|
var parseRegistryKeysCache = (rawCache) => {
|
|
24762
26010
|
const parsed = parseJson(rawCache);
|
|
24763
|
-
if (!
|
|
26011
|
+
if (!isRecord12(parsed)) {
|
|
24764
26012
|
return void 0;
|
|
24765
26013
|
}
|
|
24766
26014
|
const { registryUrl, fetchedAtMs, keys } = parsed;
|
|
@@ -24786,7 +26034,7 @@ var parseRegistryKeysCache = (rawCache) => {
|
|
|
24786
26034
|
};
|
|
24787
26035
|
var parseCrlCache = (rawCache) => {
|
|
24788
26036
|
const parsed = parseJson(rawCache);
|
|
24789
|
-
if (!
|
|
26037
|
+
if (!isRecord12(parsed)) {
|
|
24790
26038
|
return void 0;
|
|
24791
26039
|
}
|
|
24792
26040
|
const { registryUrl, fetchedAtMs, claims } = parsed;
|
|
@@ -24884,7 +26132,7 @@ var fetchCrlClaims = async (input) => {
|
|
|
24884
26132
|
);
|
|
24885
26133
|
}
|
|
24886
26134
|
const payload = await parseResponseJson(response);
|
|
24887
|
-
if (!
|
|
26135
|
+
if (!isRecord12(payload) || typeof payload.crl !== "string") {
|
|
24888
26136
|
throw new VerifyCommandError(
|
|
24889
26137
|
"revocation check unavailable (response payload is invalid)"
|
|
24890
26138
|
);
|
|
@@ -24929,7 +26177,7 @@ var loadCrlClaims = async (input) => {
|
|
|
24929
26177
|
return claims;
|
|
24930
26178
|
};
|
|
24931
26179
|
var toInvalidTokenReason = (error48) => {
|
|
24932
|
-
if (
|
|
26180
|
+
if (isRecord12(error48) && typeof error48.message === "string") {
|
|
24933
26181
|
return `invalid token (${error48.message})`;
|
|
24934
26182
|
}
|
|
24935
26183
|
if (error48 instanceof Error && error48.message.length > 0) {
|