clawdentity 0.0.19 → 0.0.21
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 +1232 -249
- package/dist/index.js +1232 -249
- package/package.json +1 -1
- package/skill-bundle/openclaw-skill/skill/SKILL.md +7 -3
package/dist/bin.js
CHANGED
|
@@ -15713,6 +15713,7 @@ var registryConfigSchema = external_exports.object({
|
|
|
15713
15713
|
ENVIRONMENT: environmentSchema,
|
|
15714
15714
|
APP_VERSION: external_exports.string().min(1).optional(),
|
|
15715
15715
|
PROXY_URL: external_exports.string().url().optional(),
|
|
15716
|
+
REGISTRY_ISSUER_URL: external_exports.string().url().optional(),
|
|
15716
15717
|
EVENT_BUS_BACKEND: registryEventBusBackendSchema.optional(),
|
|
15717
15718
|
BOOTSTRAP_SECRET: external_exports.string().min(1).optional(),
|
|
15718
15719
|
REGISTRY_SIGNING_KEY: external_exports.string().min(1).optional(),
|
|
@@ -18656,9 +18657,9 @@ var createConfigCommand = (dependencies = {}) => {
|
|
|
18656
18657
|
|
|
18657
18658
|
// src/commands/connector.ts
|
|
18658
18659
|
import { execFile as execFileCallback } from "child_process";
|
|
18659
|
-
import { mkdir as
|
|
18660
|
+
import { mkdir as mkdir5, readFile as readFile5, rm, writeFile as writeFile5 } from "fs/promises";
|
|
18660
18661
|
import { homedir as homedir2 } from "os";
|
|
18661
|
-
import { dirname as
|
|
18662
|
+
import { dirname as dirname4, join as join6 } from "path";
|
|
18662
18663
|
import { fileURLToPath } from "url";
|
|
18663
18664
|
import { promisify } from "util";
|
|
18664
18665
|
|
|
@@ -18681,6 +18682,13 @@ var DEFAULT_CONNECTOR_OUTBOUND_PATH = "/v1/outbound";
|
|
|
18681
18682
|
var DEFAULT_CONNECTOR_STATUS_PATH = "/v1/status";
|
|
18682
18683
|
var DEFAULT_RELAY_DELIVER_TIMEOUT_MS = 15e3;
|
|
18683
18684
|
var DEFAULT_OPENCLAW_DELIVER_RETRY_BUDGET_MS = DEFAULT_RELAY_DELIVER_TIMEOUT_MS - 1e3;
|
|
18685
|
+
var DEFAULT_CONNECTOR_INBOUND_INBOX_MAX_MESSAGES = 1e5;
|
|
18686
|
+
var DEFAULT_CONNECTOR_INBOUND_INBOX_MAX_BYTES = 2 * 1024 * 1024 * 1024;
|
|
18687
|
+
var DEFAULT_CONNECTOR_INBOUND_REPLAY_INTERVAL_MS = 2e3;
|
|
18688
|
+
var DEFAULT_CONNECTOR_INBOUND_REPLAY_BATCH_SIZE = 25;
|
|
18689
|
+
var DEFAULT_CONNECTOR_INBOUND_RETRY_INITIAL_DELAY_MS = 1e3;
|
|
18690
|
+
var DEFAULT_CONNECTOR_INBOUND_RETRY_MAX_DELAY_MS = 6e4;
|
|
18691
|
+
var DEFAULT_CONNECTOR_INBOUND_RETRY_BACKOFF_FACTOR = 2;
|
|
18684
18692
|
var AGENT_ACCESS_HEADER = "x-claw-agent-access";
|
|
18685
18693
|
var WS_READY_STATE_OPEN = 1;
|
|
18686
18694
|
|
|
@@ -18942,6 +18950,7 @@ var ConnectorClient = class {
|
|
|
18942
18950
|
fetchImpl;
|
|
18943
18951
|
logger;
|
|
18944
18952
|
hooks;
|
|
18953
|
+
inboundDeliverHandler;
|
|
18945
18954
|
now;
|
|
18946
18955
|
random;
|
|
18947
18956
|
ulidFactory;
|
|
@@ -18996,6 +19005,7 @@ var ConnectorClient = class {
|
|
|
18996
19005
|
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
18997
19006
|
this.logger = options.logger ?? createLogger({ service: "connector", module: "client" });
|
|
18998
19007
|
this.hooks = options.hooks ?? {};
|
|
19008
|
+
this.inboundDeliverHandler = options.inboundDeliverHandler;
|
|
18999
19009
|
this.now = options.now ?? Date.now;
|
|
19000
19010
|
this.random = options.random ?? Math.random;
|
|
19001
19011
|
this.ulidFactory = options.ulidFactory ?? generateUlid;
|
|
@@ -19208,6 +19218,44 @@ var ConnectorClient = class {
|
|
|
19208
19218
|
this.sendFrame(ackFrame);
|
|
19209
19219
|
}
|
|
19210
19220
|
async handleDeliverFrame(frame) {
|
|
19221
|
+
if (this.inboundDeliverHandler !== void 0) {
|
|
19222
|
+
try {
|
|
19223
|
+
const result = await this.inboundDeliverHandler(frame);
|
|
19224
|
+
const ackFrame = {
|
|
19225
|
+
v: CONNECTOR_FRAME_VERSION,
|
|
19226
|
+
type: "deliver_ack",
|
|
19227
|
+
id: this.makeFrameId(),
|
|
19228
|
+
ts: this.makeTimestamp(),
|
|
19229
|
+
ackId: frame.id,
|
|
19230
|
+
accepted: result.accepted,
|
|
19231
|
+
reason: result.reason
|
|
19232
|
+
};
|
|
19233
|
+
this.sendFrame(ackFrame);
|
|
19234
|
+
if (result.accepted) {
|
|
19235
|
+
this.hooks.onDeliverSucceeded?.(frame);
|
|
19236
|
+
} else {
|
|
19237
|
+
this.hooks.onDeliverFailed?.(
|
|
19238
|
+
frame,
|
|
19239
|
+
new Error(
|
|
19240
|
+
result.reason ?? "Inbound delivery was rejected by runtime handler"
|
|
19241
|
+
)
|
|
19242
|
+
);
|
|
19243
|
+
}
|
|
19244
|
+
} catch (error48) {
|
|
19245
|
+
const ackFrame = {
|
|
19246
|
+
v: CONNECTOR_FRAME_VERSION,
|
|
19247
|
+
type: "deliver_ack",
|
|
19248
|
+
id: this.makeFrameId(),
|
|
19249
|
+
ts: this.makeTimestamp(),
|
|
19250
|
+
ackId: frame.id,
|
|
19251
|
+
accepted: false,
|
|
19252
|
+
reason: sanitizeErrorReason(error48)
|
|
19253
|
+
};
|
|
19254
|
+
this.sendFrame(ackFrame);
|
|
19255
|
+
this.hooks.onDeliverFailed?.(frame, error48);
|
|
19256
|
+
}
|
|
19257
|
+
return;
|
|
19258
|
+
}
|
|
19211
19259
|
try {
|
|
19212
19260
|
await this.deliverToLocalOpenclawWithRetry(frame);
|
|
19213
19261
|
const ackFrame = {
|
|
@@ -19323,13 +19371,353 @@ var ConnectorClient = class {
|
|
|
19323
19371
|
}
|
|
19324
19372
|
};
|
|
19325
19373
|
|
|
19374
|
+
// ../../packages/connector/src/inbound-inbox.ts
|
|
19375
|
+
import {
|
|
19376
|
+
appendFile,
|
|
19377
|
+
mkdir as mkdir3,
|
|
19378
|
+
readFile as readFile3,
|
|
19379
|
+
rename as rename2,
|
|
19380
|
+
writeFile as writeFile3
|
|
19381
|
+
} from "fs/promises";
|
|
19382
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
19383
|
+
var INBOUND_INBOX_DIR_NAME = "inbound-inbox";
|
|
19384
|
+
var INBOUND_INBOX_INDEX_FILE_NAME = "index.json";
|
|
19385
|
+
var INBOUND_INBOX_EVENTS_FILE_NAME = "events.jsonl";
|
|
19386
|
+
var INBOUND_INBOX_SCHEMA_VERSION = 1;
|
|
19387
|
+
function nowIso2() {
|
|
19388
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
19389
|
+
}
|
|
19390
|
+
function isRecord5(value) {
|
|
19391
|
+
return typeof value === "object" && value !== null;
|
|
19392
|
+
}
|
|
19393
|
+
function parsePendingItem(value) {
|
|
19394
|
+
if (!isRecord5(value)) {
|
|
19395
|
+
return void 0;
|
|
19396
|
+
}
|
|
19397
|
+
const id = typeof value.id === "string" ? value.id.trim() : "";
|
|
19398
|
+
const requestId = typeof value.requestId === "string" ? value.requestId.trim() : "";
|
|
19399
|
+
const fromAgentDid = typeof value.fromAgentDid === "string" ? value.fromAgentDid.trim() : "";
|
|
19400
|
+
const toAgentDid = typeof value.toAgentDid === "string" ? value.toAgentDid.trim() : "";
|
|
19401
|
+
const receivedAt = typeof value.receivedAt === "string" ? value.receivedAt.trim() : "";
|
|
19402
|
+
const nextAttemptAt = typeof value.nextAttemptAt === "string" ? value.nextAttemptAt.trim() : "";
|
|
19403
|
+
const attemptCount = typeof value.attemptCount === "number" && Number.isInteger(value.attemptCount) ? value.attemptCount : NaN;
|
|
19404
|
+
const payloadBytes = typeof value.payloadBytes === "number" && Number.isInteger(value.payloadBytes) ? value.payloadBytes : NaN;
|
|
19405
|
+
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) {
|
|
19406
|
+
return void 0;
|
|
19407
|
+
}
|
|
19408
|
+
const lastError = typeof value.lastError === "string" ? value.lastError : void 0;
|
|
19409
|
+
const lastAttemptAt = typeof value.lastAttemptAt === "string" ? value.lastAttemptAt : void 0;
|
|
19410
|
+
return {
|
|
19411
|
+
id,
|
|
19412
|
+
requestId,
|
|
19413
|
+
fromAgentDid,
|
|
19414
|
+
toAgentDid,
|
|
19415
|
+
payload: value.payload,
|
|
19416
|
+
payloadBytes,
|
|
19417
|
+
receivedAt,
|
|
19418
|
+
nextAttemptAt,
|
|
19419
|
+
attemptCount,
|
|
19420
|
+
lastError,
|
|
19421
|
+
lastAttemptAt
|
|
19422
|
+
};
|
|
19423
|
+
}
|
|
19424
|
+
function toDefaultIndexFile() {
|
|
19425
|
+
return {
|
|
19426
|
+
version: INBOUND_INBOX_SCHEMA_VERSION,
|
|
19427
|
+
pendingBytes: 0,
|
|
19428
|
+
pendingByRequestId: {},
|
|
19429
|
+
updatedAt: nowIso2()
|
|
19430
|
+
};
|
|
19431
|
+
}
|
|
19432
|
+
function normalizeIndexFile(raw) {
|
|
19433
|
+
if (!isRecord5(raw)) {
|
|
19434
|
+
throw new Error("Inbound inbox index root must be an object");
|
|
19435
|
+
}
|
|
19436
|
+
const pendingByRequestIdRaw = raw.pendingByRequestId;
|
|
19437
|
+
if (!isRecord5(pendingByRequestIdRaw)) {
|
|
19438
|
+
throw new Error("Inbound inbox index pendingByRequestId must be an object");
|
|
19439
|
+
}
|
|
19440
|
+
const pendingByRequestId = {};
|
|
19441
|
+
let pendingBytes = 0;
|
|
19442
|
+
for (const [requestId, candidate] of Object.entries(pendingByRequestIdRaw)) {
|
|
19443
|
+
const entry = parsePendingItem(candidate);
|
|
19444
|
+
if (entry === void 0 || entry.requestId !== requestId) {
|
|
19445
|
+
continue;
|
|
19446
|
+
}
|
|
19447
|
+
pendingByRequestId[requestId] = entry;
|
|
19448
|
+
pendingBytes += entry.payloadBytes;
|
|
19449
|
+
}
|
|
19450
|
+
return {
|
|
19451
|
+
version: typeof raw.version === "number" && Number.isFinite(raw.version) ? raw.version : INBOUND_INBOX_SCHEMA_VERSION,
|
|
19452
|
+
pendingBytes,
|
|
19453
|
+
pendingByRequestId,
|
|
19454
|
+
updatedAt: typeof raw.updatedAt === "string" && raw.updatedAt.trim().length > 0 ? raw.updatedAt : nowIso2()
|
|
19455
|
+
};
|
|
19456
|
+
}
|
|
19457
|
+
function toComparableTimeMs(value) {
|
|
19458
|
+
const parsed = Date.parse(value);
|
|
19459
|
+
if (Number.isFinite(parsed)) {
|
|
19460
|
+
return parsed;
|
|
19461
|
+
}
|
|
19462
|
+
return Number.MAX_SAFE_INTEGER;
|
|
19463
|
+
}
|
|
19464
|
+
var ConnectorInboundInbox = class {
|
|
19465
|
+
agentName;
|
|
19466
|
+
eventsPath;
|
|
19467
|
+
indexPath;
|
|
19468
|
+
maxPendingBytes;
|
|
19469
|
+
maxPendingMessages;
|
|
19470
|
+
inboxDir;
|
|
19471
|
+
writeChain = Promise.resolve();
|
|
19472
|
+
constructor(options) {
|
|
19473
|
+
this.agentName = options.agentName;
|
|
19474
|
+
this.inboxDir = join4(
|
|
19475
|
+
options.configDir,
|
|
19476
|
+
"agents",
|
|
19477
|
+
this.agentName,
|
|
19478
|
+
INBOUND_INBOX_DIR_NAME
|
|
19479
|
+
);
|
|
19480
|
+
this.indexPath = join4(this.inboxDir, INBOUND_INBOX_INDEX_FILE_NAME);
|
|
19481
|
+
this.eventsPath = join4(this.inboxDir, INBOUND_INBOX_EVENTS_FILE_NAME);
|
|
19482
|
+
this.maxPendingBytes = options.maxPendingBytes;
|
|
19483
|
+
this.maxPendingMessages = options.maxPendingMessages;
|
|
19484
|
+
}
|
|
19485
|
+
async enqueue(frame) {
|
|
19486
|
+
return await this.withWriteLock(async () => {
|
|
19487
|
+
const index = await this.loadIndex();
|
|
19488
|
+
const existing = index.pendingByRequestId[frame.id];
|
|
19489
|
+
if (existing !== void 0) {
|
|
19490
|
+
await this.appendEvent({
|
|
19491
|
+
type: "inbound_duplicate",
|
|
19492
|
+
requestId: frame.id
|
|
19493
|
+
});
|
|
19494
|
+
return {
|
|
19495
|
+
accepted: true,
|
|
19496
|
+
duplicate: true,
|
|
19497
|
+
pendingCount: Object.keys(index.pendingByRequestId).length
|
|
19498
|
+
};
|
|
19499
|
+
}
|
|
19500
|
+
const payloadBytes = Buffer.byteLength(
|
|
19501
|
+
JSON.stringify(frame.payload ?? null),
|
|
19502
|
+
"utf8"
|
|
19503
|
+
);
|
|
19504
|
+
const pendingCount = Object.keys(index.pendingByRequestId).length;
|
|
19505
|
+
if (pendingCount >= this.maxPendingMessages) {
|
|
19506
|
+
return {
|
|
19507
|
+
accepted: false,
|
|
19508
|
+
duplicate: false,
|
|
19509
|
+
pendingCount,
|
|
19510
|
+
reason: "connector inbound inbox is full (message cap reached)"
|
|
19511
|
+
};
|
|
19512
|
+
}
|
|
19513
|
+
if (index.pendingBytes + payloadBytes > this.maxPendingBytes) {
|
|
19514
|
+
return {
|
|
19515
|
+
accepted: false,
|
|
19516
|
+
duplicate: false,
|
|
19517
|
+
pendingCount,
|
|
19518
|
+
reason: "connector inbound inbox is full (byte cap reached)"
|
|
19519
|
+
};
|
|
19520
|
+
}
|
|
19521
|
+
const pendingItem = {
|
|
19522
|
+
id: frame.id,
|
|
19523
|
+
requestId: frame.id,
|
|
19524
|
+
fromAgentDid: frame.fromAgentDid,
|
|
19525
|
+
toAgentDid: frame.toAgentDid,
|
|
19526
|
+
payload: frame.payload,
|
|
19527
|
+
payloadBytes,
|
|
19528
|
+
receivedAt: nowIso2(),
|
|
19529
|
+
nextAttemptAt: nowIso2(),
|
|
19530
|
+
attemptCount: 0
|
|
19531
|
+
};
|
|
19532
|
+
index.pendingByRequestId[pendingItem.requestId] = pendingItem;
|
|
19533
|
+
index.pendingBytes += pendingItem.payloadBytes;
|
|
19534
|
+
index.updatedAt = nowIso2();
|
|
19535
|
+
await this.saveIndex(index);
|
|
19536
|
+
await this.appendEvent({
|
|
19537
|
+
type: "inbound_persisted",
|
|
19538
|
+
requestId: pendingItem.requestId,
|
|
19539
|
+
details: {
|
|
19540
|
+
payloadBytes,
|
|
19541
|
+
fromAgentDid: pendingItem.fromAgentDid,
|
|
19542
|
+
toAgentDid: pendingItem.toAgentDid
|
|
19543
|
+
}
|
|
19544
|
+
});
|
|
19545
|
+
return {
|
|
19546
|
+
accepted: true,
|
|
19547
|
+
duplicate: false,
|
|
19548
|
+
pendingCount: Object.keys(index.pendingByRequestId).length
|
|
19549
|
+
};
|
|
19550
|
+
});
|
|
19551
|
+
}
|
|
19552
|
+
async listDuePending(input) {
|
|
19553
|
+
const index = await this.loadIndex();
|
|
19554
|
+
const due = Object.values(index.pendingByRequestId).filter((item) => toComparableTimeMs(item.nextAttemptAt) <= input.nowMs).sort((left, right) => {
|
|
19555
|
+
const leftNext = toComparableTimeMs(left.nextAttemptAt);
|
|
19556
|
+
const rightNext = toComparableTimeMs(right.nextAttemptAt);
|
|
19557
|
+
if (leftNext !== rightNext) {
|
|
19558
|
+
return leftNext - rightNext;
|
|
19559
|
+
}
|
|
19560
|
+
return toComparableTimeMs(left.receivedAt) - toComparableTimeMs(right.receivedAt);
|
|
19561
|
+
});
|
|
19562
|
+
return due.slice(0, Math.max(1, input.limit));
|
|
19563
|
+
}
|
|
19564
|
+
async markDelivered(requestId) {
|
|
19565
|
+
await this.withWriteLock(async () => {
|
|
19566
|
+
const index = await this.loadIndex();
|
|
19567
|
+
const entry = index.pendingByRequestId[requestId];
|
|
19568
|
+
if (entry === void 0) {
|
|
19569
|
+
return;
|
|
19570
|
+
}
|
|
19571
|
+
delete index.pendingByRequestId[requestId];
|
|
19572
|
+
index.pendingBytes = Math.max(0, index.pendingBytes - entry.payloadBytes);
|
|
19573
|
+
index.updatedAt = nowIso2();
|
|
19574
|
+
await this.saveIndex(index);
|
|
19575
|
+
await this.appendEvent({
|
|
19576
|
+
type: "replay_succeeded",
|
|
19577
|
+
requestId
|
|
19578
|
+
});
|
|
19579
|
+
});
|
|
19580
|
+
}
|
|
19581
|
+
async markReplayFailure(input) {
|
|
19582
|
+
await this.withWriteLock(async () => {
|
|
19583
|
+
const index = await this.loadIndex();
|
|
19584
|
+
const entry = index.pendingByRequestId[input.requestId];
|
|
19585
|
+
if (entry === void 0) {
|
|
19586
|
+
return;
|
|
19587
|
+
}
|
|
19588
|
+
entry.attemptCount += 1;
|
|
19589
|
+
entry.lastError = input.errorMessage;
|
|
19590
|
+
entry.lastAttemptAt = nowIso2();
|
|
19591
|
+
entry.nextAttemptAt = input.nextAttemptAt;
|
|
19592
|
+
index.updatedAt = nowIso2();
|
|
19593
|
+
await this.saveIndex(index);
|
|
19594
|
+
await this.appendEvent({
|
|
19595
|
+
type: "replay_failed",
|
|
19596
|
+
requestId: input.requestId,
|
|
19597
|
+
details: {
|
|
19598
|
+
attemptCount: entry.attemptCount,
|
|
19599
|
+
nextAttemptAt: input.nextAttemptAt,
|
|
19600
|
+
errorMessage: input.errorMessage
|
|
19601
|
+
}
|
|
19602
|
+
});
|
|
19603
|
+
});
|
|
19604
|
+
}
|
|
19605
|
+
async pruneDelivered() {
|
|
19606
|
+
await this.withWriteLock(async () => {
|
|
19607
|
+
const index = await this.loadIndex();
|
|
19608
|
+
const beforeCount = Object.keys(index.pendingByRequestId).length;
|
|
19609
|
+
if (beforeCount === 0) {
|
|
19610
|
+
return;
|
|
19611
|
+
}
|
|
19612
|
+
const after = {};
|
|
19613
|
+
let pendingBytes = 0;
|
|
19614
|
+
for (const [requestId, entry] of Object.entries(
|
|
19615
|
+
index.pendingByRequestId
|
|
19616
|
+
)) {
|
|
19617
|
+
if (entry.attemptCount < 0) {
|
|
19618
|
+
continue;
|
|
19619
|
+
}
|
|
19620
|
+
after[requestId] = entry;
|
|
19621
|
+
pendingBytes += entry.payloadBytes;
|
|
19622
|
+
}
|
|
19623
|
+
index.pendingByRequestId = after;
|
|
19624
|
+
index.pendingBytes = pendingBytes;
|
|
19625
|
+
index.updatedAt = nowIso2();
|
|
19626
|
+
await this.saveIndex(index);
|
|
19627
|
+
await this.appendEvent({
|
|
19628
|
+
type: "inbox_pruned",
|
|
19629
|
+
details: {
|
|
19630
|
+
beforeCount,
|
|
19631
|
+
afterCount: Object.keys(after).length
|
|
19632
|
+
}
|
|
19633
|
+
});
|
|
19634
|
+
});
|
|
19635
|
+
}
|
|
19636
|
+
async getSnapshot() {
|
|
19637
|
+
const index = await this.loadIndex();
|
|
19638
|
+
const entries = Object.values(index.pendingByRequestId);
|
|
19639
|
+
if (entries.length === 0) {
|
|
19640
|
+
return {
|
|
19641
|
+
pendingCount: 0,
|
|
19642
|
+
pendingBytes: index.pendingBytes
|
|
19643
|
+
};
|
|
19644
|
+
}
|
|
19645
|
+
entries.sort((left, right) => {
|
|
19646
|
+
return toComparableTimeMs(left.receivedAt) - toComparableTimeMs(right.receivedAt);
|
|
19647
|
+
});
|
|
19648
|
+
const nextAttemptAt = entries.map((entry) => entry.nextAttemptAt).sort(
|
|
19649
|
+
(left, right) => toComparableTimeMs(left) - toComparableTimeMs(right)
|
|
19650
|
+
)[0];
|
|
19651
|
+
return {
|
|
19652
|
+
pendingCount: entries.length,
|
|
19653
|
+
pendingBytes: index.pendingBytes,
|
|
19654
|
+
oldestPendingAt: entries[0]?.receivedAt,
|
|
19655
|
+
nextAttemptAt
|
|
19656
|
+
};
|
|
19657
|
+
}
|
|
19658
|
+
async withWriteLock(fn) {
|
|
19659
|
+
const previous = this.writeChain;
|
|
19660
|
+
let release;
|
|
19661
|
+
this.writeChain = new Promise((resolve2) => {
|
|
19662
|
+
release = resolve2;
|
|
19663
|
+
});
|
|
19664
|
+
await previous;
|
|
19665
|
+
try {
|
|
19666
|
+
return await fn();
|
|
19667
|
+
} finally {
|
|
19668
|
+
release?.();
|
|
19669
|
+
}
|
|
19670
|
+
}
|
|
19671
|
+
async loadIndex() {
|
|
19672
|
+
await mkdir3(this.inboxDir, { recursive: true });
|
|
19673
|
+
let raw;
|
|
19674
|
+
try {
|
|
19675
|
+
raw = await readFile3(this.indexPath, "utf8");
|
|
19676
|
+
} catch (error48) {
|
|
19677
|
+
if (error48 && typeof error48 === "object" && "code" in error48 && error48.code === "ENOENT") {
|
|
19678
|
+
return toDefaultIndexFile();
|
|
19679
|
+
}
|
|
19680
|
+
throw error48;
|
|
19681
|
+
}
|
|
19682
|
+
if (raw.trim().length === 0) {
|
|
19683
|
+
return toDefaultIndexFile();
|
|
19684
|
+
}
|
|
19685
|
+
const parsed = JSON.parse(raw);
|
|
19686
|
+
return normalizeIndexFile(parsed);
|
|
19687
|
+
}
|
|
19688
|
+
async saveIndex(index) {
|
|
19689
|
+
await mkdir3(dirname2(this.indexPath), { recursive: true });
|
|
19690
|
+
const payload = {
|
|
19691
|
+
...index,
|
|
19692
|
+
version: INBOUND_INBOX_SCHEMA_VERSION,
|
|
19693
|
+
updatedAt: nowIso2()
|
|
19694
|
+
};
|
|
19695
|
+
const tmpPath = `${this.indexPath}.tmp-${Date.now()}`;
|
|
19696
|
+
await writeFile3(tmpPath, `${JSON.stringify(payload, null, 2)}
|
|
19697
|
+
`, "utf8");
|
|
19698
|
+
await rename2(tmpPath, this.indexPath);
|
|
19699
|
+
}
|
|
19700
|
+
async appendEvent(event) {
|
|
19701
|
+
await mkdir3(dirname2(this.eventsPath), { recursive: true });
|
|
19702
|
+
await appendFile(
|
|
19703
|
+
this.eventsPath,
|
|
19704
|
+
`${JSON.stringify({ ...event, at: nowIso2() })}
|
|
19705
|
+
`,
|
|
19706
|
+
"utf8"
|
|
19707
|
+
);
|
|
19708
|
+
}
|
|
19709
|
+
};
|
|
19710
|
+
function createConnectorInboundInbox(options) {
|
|
19711
|
+
return new ConnectorInboundInbox(options);
|
|
19712
|
+
}
|
|
19713
|
+
|
|
19326
19714
|
// ../../packages/connector/src/runtime.ts
|
|
19327
19715
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
19328
|
-
import { mkdir as
|
|
19716
|
+
import { mkdir as mkdir4, readFile as readFile4, rename as rename3, writeFile as writeFile4 } from "fs/promises";
|
|
19329
19717
|
import {
|
|
19330
19718
|
createServer
|
|
19331
19719
|
} from "http";
|
|
19332
|
-
import { dirname as
|
|
19720
|
+
import { dirname as dirname3, join as join5 } from "path";
|
|
19333
19721
|
import { WebSocket as NodeWebSocket } from "ws";
|
|
19334
19722
|
var REGISTRY_AUTH_FILENAME = "registry-auth.json";
|
|
19335
19723
|
var AGENTS_DIR_NAME2 = "agents";
|
|
@@ -19337,7 +19725,7 @@ var REFRESH_SINGLE_FLIGHT_PREFIX = "connector-runtime";
|
|
|
19337
19725
|
var NONCE_SIZE = 16;
|
|
19338
19726
|
var MAX_OUTBOUND_BODY_BYTES = 1024 * 1024;
|
|
19339
19727
|
var ACCESS_TOKEN_REFRESH_SKEW_MS = 3e4;
|
|
19340
|
-
function
|
|
19728
|
+
function isRecord6(value) {
|
|
19341
19729
|
return typeof value === "object" && value !== null;
|
|
19342
19730
|
}
|
|
19343
19731
|
function toPathWithQuery2(url2) {
|
|
@@ -19405,6 +19793,121 @@ function resolveOpenclawHookToken(input) {
|
|
|
19405
19793
|
}
|
|
19406
19794
|
return value;
|
|
19407
19795
|
}
|
|
19796
|
+
function toOpenclawHookUrl2(baseUrl, hookPath) {
|
|
19797
|
+
const normalizedBase = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
19798
|
+
const normalizedHookPath = hookPath.startsWith("/") ? hookPath.slice(1) : hookPath;
|
|
19799
|
+
return new URL(normalizedHookPath, normalizedBase).toString();
|
|
19800
|
+
}
|
|
19801
|
+
function parsePositiveIntEnv(key, fallback, minimum = 1) {
|
|
19802
|
+
const raw = process.env[key]?.trim();
|
|
19803
|
+
if (!raw) {
|
|
19804
|
+
return fallback;
|
|
19805
|
+
}
|
|
19806
|
+
const parsed = Number.parseInt(raw, 10);
|
|
19807
|
+
if (!Number.isFinite(parsed) || parsed < minimum) {
|
|
19808
|
+
return fallback;
|
|
19809
|
+
}
|
|
19810
|
+
return parsed;
|
|
19811
|
+
}
|
|
19812
|
+
function sanitizeErrorReason2(error48) {
|
|
19813
|
+
if (!(error48 instanceof Error)) {
|
|
19814
|
+
return "Unknown error";
|
|
19815
|
+
}
|
|
19816
|
+
return error48.message.trim().slice(0, 240) || "Unknown error";
|
|
19817
|
+
}
|
|
19818
|
+
var LocalOpenclawDeliveryError2 = class extends Error {
|
|
19819
|
+
retryable;
|
|
19820
|
+
constructor(input) {
|
|
19821
|
+
super(input.message);
|
|
19822
|
+
this.name = "LocalOpenclawDeliveryError";
|
|
19823
|
+
this.retryable = input.retryable;
|
|
19824
|
+
}
|
|
19825
|
+
};
|
|
19826
|
+
function loadInboundReplayPolicy() {
|
|
19827
|
+
const retryBackoffFactor = Number.parseFloat(
|
|
19828
|
+
process.env.CONNECTOR_INBOUND_RETRY_BACKOFF_FACTOR ?? ""
|
|
19829
|
+
);
|
|
19830
|
+
return {
|
|
19831
|
+
inboxMaxMessages: parsePositiveIntEnv(
|
|
19832
|
+
"CONNECTOR_INBOUND_INBOX_MAX_MESSAGES",
|
|
19833
|
+
DEFAULT_CONNECTOR_INBOUND_INBOX_MAX_MESSAGES
|
|
19834
|
+
),
|
|
19835
|
+
inboxMaxBytes: parsePositiveIntEnv(
|
|
19836
|
+
"CONNECTOR_INBOUND_INBOX_MAX_BYTES",
|
|
19837
|
+
DEFAULT_CONNECTOR_INBOUND_INBOX_MAX_BYTES
|
|
19838
|
+
),
|
|
19839
|
+
replayIntervalMs: parsePositiveIntEnv(
|
|
19840
|
+
"CONNECTOR_INBOUND_REPLAY_INTERVAL_MS",
|
|
19841
|
+
DEFAULT_CONNECTOR_INBOUND_REPLAY_INTERVAL_MS
|
|
19842
|
+
),
|
|
19843
|
+
batchSize: parsePositiveIntEnv(
|
|
19844
|
+
"CONNECTOR_INBOUND_REPLAY_BATCH_SIZE",
|
|
19845
|
+
DEFAULT_CONNECTOR_INBOUND_REPLAY_BATCH_SIZE
|
|
19846
|
+
),
|
|
19847
|
+
retryInitialDelayMs: parsePositiveIntEnv(
|
|
19848
|
+
"CONNECTOR_INBOUND_RETRY_INITIAL_DELAY_MS",
|
|
19849
|
+
DEFAULT_CONNECTOR_INBOUND_RETRY_INITIAL_DELAY_MS
|
|
19850
|
+
),
|
|
19851
|
+
retryMaxDelayMs: parsePositiveIntEnv(
|
|
19852
|
+
"CONNECTOR_INBOUND_RETRY_MAX_DELAY_MS",
|
|
19853
|
+
DEFAULT_CONNECTOR_INBOUND_RETRY_MAX_DELAY_MS
|
|
19854
|
+
),
|
|
19855
|
+
retryBackoffFactor: Number.isFinite(retryBackoffFactor) && retryBackoffFactor >= 1 ? retryBackoffFactor : DEFAULT_CONNECTOR_INBOUND_RETRY_BACKOFF_FACTOR
|
|
19856
|
+
};
|
|
19857
|
+
}
|
|
19858
|
+
function computeReplayDelayMs(input) {
|
|
19859
|
+
const exponent = Math.max(0, input.attemptCount - 1);
|
|
19860
|
+
const delay = Math.min(
|
|
19861
|
+
input.policy.retryMaxDelayMs,
|
|
19862
|
+
Math.floor(
|
|
19863
|
+
input.policy.retryInitialDelayMs * input.policy.retryBackoffFactor ** exponent
|
|
19864
|
+
)
|
|
19865
|
+
);
|
|
19866
|
+
return Math.max(1, delay);
|
|
19867
|
+
}
|
|
19868
|
+
async function deliverToOpenclawHook(input) {
|
|
19869
|
+
const controller = new AbortController();
|
|
19870
|
+
const timeoutHandle = setTimeout(() => {
|
|
19871
|
+
controller.abort();
|
|
19872
|
+
}, DEFAULT_OPENCLAW_DELIVER_TIMEOUT_MS);
|
|
19873
|
+
const headers = {
|
|
19874
|
+
"content-type": "application/json",
|
|
19875
|
+
"x-request-id": input.requestId
|
|
19876
|
+
};
|
|
19877
|
+
if (input.openclawHookToken !== void 0) {
|
|
19878
|
+
headers["x-openclaw-token"] = input.openclawHookToken;
|
|
19879
|
+
}
|
|
19880
|
+
try {
|
|
19881
|
+
const response = await input.fetchImpl(input.openclawHookUrl, {
|
|
19882
|
+
method: "POST",
|
|
19883
|
+
headers,
|
|
19884
|
+
body: JSON.stringify(input.payload),
|
|
19885
|
+
signal: controller.signal
|
|
19886
|
+
});
|
|
19887
|
+
if (!response.ok) {
|
|
19888
|
+
throw new LocalOpenclawDeliveryError2({
|
|
19889
|
+
message: `Local OpenClaw hook rejected payload with status ${response.status}`,
|
|
19890
|
+
retryable: response.status >= 500 || response.status === 404 || response.status === 429
|
|
19891
|
+
});
|
|
19892
|
+
}
|
|
19893
|
+
} catch (error48) {
|
|
19894
|
+
if (error48 instanceof Error && error48.name === "AbortError") {
|
|
19895
|
+
throw new LocalOpenclawDeliveryError2({
|
|
19896
|
+
message: "Local OpenClaw hook request timed out",
|
|
19897
|
+
retryable: true
|
|
19898
|
+
});
|
|
19899
|
+
}
|
|
19900
|
+
if (error48 instanceof LocalOpenclawDeliveryError2) {
|
|
19901
|
+
throw error48;
|
|
19902
|
+
}
|
|
19903
|
+
throw new LocalOpenclawDeliveryError2({
|
|
19904
|
+
message: sanitizeErrorReason2(error48),
|
|
19905
|
+
retryable: true
|
|
19906
|
+
});
|
|
19907
|
+
} finally {
|
|
19908
|
+
clearTimeout(timeoutHandle);
|
|
19909
|
+
}
|
|
19910
|
+
}
|
|
19408
19911
|
function toInitialAuthBundle(credentials) {
|
|
19409
19912
|
return {
|
|
19410
19913
|
tokenType: "Bearer",
|
|
@@ -19432,7 +19935,7 @@ function shouldRefreshAccessToken(auth, nowMs) {
|
|
|
19432
19935
|
return expiresAtMs <= nowMs + ACCESS_TOKEN_REFRESH_SKEW_MS;
|
|
19433
19936
|
}
|
|
19434
19937
|
function parseOutboundRelayRequest(payload) {
|
|
19435
|
-
if (!
|
|
19938
|
+
if (!isRecord6(payload)) {
|
|
19436
19939
|
throw new AppError({
|
|
19437
19940
|
code: "CONNECTOR_OUTBOUND_INVALID_REQUEST",
|
|
19438
19941
|
message: "Outbound relay request must be an object",
|
|
@@ -19490,20 +19993,20 @@ function createWebSocketFactory() {
|
|
|
19490
19993
|
};
|
|
19491
19994
|
}
|
|
19492
19995
|
async function writeRegistryAuthAtomic(input) {
|
|
19493
|
-
const targetPath =
|
|
19996
|
+
const targetPath = join5(
|
|
19494
19997
|
input.configDir,
|
|
19495
19998
|
AGENTS_DIR_NAME2,
|
|
19496
19999
|
input.agentName,
|
|
19497
20000
|
REGISTRY_AUTH_FILENAME
|
|
19498
20001
|
);
|
|
19499
20002
|
const tmpPath = `${targetPath}.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
19500
|
-
await
|
|
19501
|
-
await
|
|
20003
|
+
await mkdir4(dirname3(targetPath), { recursive: true });
|
|
20004
|
+
await writeFile4(tmpPath, `${JSON.stringify(input.auth, null, 2)}
|
|
19502
20005
|
`, "utf8");
|
|
19503
|
-
await
|
|
20006
|
+
await rename3(tmpPath, targetPath);
|
|
19504
20007
|
}
|
|
19505
20008
|
function parseRegistryAuthFromDisk(payload) {
|
|
19506
|
-
if (!
|
|
20009
|
+
if (!isRecord6(payload)) {
|
|
19507
20010
|
return void 0;
|
|
19508
20011
|
}
|
|
19509
20012
|
const tokenType = payload.tokenType;
|
|
@@ -19523,7 +20026,7 @@ function parseRegistryAuthFromDisk(payload) {
|
|
|
19523
20026
|
};
|
|
19524
20027
|
}
|
|
19525
20028
|
async function readRegistryAuthFromDisk(input) {
|
|
19526
|
-
const authPath =
|
|
20029
|
+
const authPath = join5(
|
|
19527
20030
|
input.configDir,
|
|
19528
20031
|
AGENTS_DIR_NAME2,
|
|
19529
20032
|
input.agentName,
|
|
@@ -19531,7 +20034,7 @@ async function readRegistryAuthFromDisk(input) {
|
|
|
19531
20034
|
);
|
|
19532
20035
|
let raw;
|
|
19533
20036
|
try {
|
|
19534
|
-
raw = await
|
|
20037
|
+
raw = await readFile4(authPath, "utf8");
|
|
19535
20038
|
} catch (error48) {
|
|
19536
20039
|
if (error48 && typeof error48 === "object" && "code" in error48 && error48.code === "ENOENT") {
|
|
19537
20040
|
return void 0;
|
|
@@ -19660,6 +20163,23 @@ async function startConnectorRuntime(input) {
|
|
|
19660
20163
|
await refreshCurrentAuthIfNeeded();
|
|
19661
20164
|
const wsUrl = normalizeWebSocketUrl(input.proxyWebsocketUrl);
|
|
19662
20165
|
const wsParsed = new URL(wsUrl);
|
|
20166
|
+
const openclawBaseUrl = resolveOpenclawBaseUrl(input.openclawBaseUrl);
|
|
20167
|
+
const openclawHookPath = resolveOpenclawHookPath(input.openclawHookPath);
|
|
20168
|
+
const openclawHookToken = resolveOpenclawHookToken(input.openclawHookToken);
|
|
20169
|
+
const openclawHookUrl = toOpenclawHookUrl2(openclawBaseUrl, openclawHookPath);
|
|
20170
|
+
const inboundReplayPolicy = loadInboundReplayPolicy();
|
|
20171
|
+
const inboundInbox = createConnectorInboundInbox({
|
|
20172
|
+
configDir: input.configDir,
|
|
20173
|
+
agentName: input.agentName,
|
|
20174
|
+
maxPendingMessages: inboundReplayPolicy.inboxMaxMessages,
|
|
20175
|
+
maxPendingBytes: inboundReplayPolicy.inboxMaxBytes
|
|
20176
|
+
});
|
|
20177
|
+
const inboundReplayStatus = {
|
|
20178
|
+
replayerActive: false
|
|
20179
|
+
};
|
|
20180
|
+
let runtimeStopping = false;
|
|
20181
|
+
let replayInFlight = false;
|
|
20182
|
+
let replayIntervalHandle;
|
|
19663
20183
|
const resolveUpgradeHeaders = async () => {
|
|
19664
20184
|
await refreshCurrentAuthIfNeeded();
|
|
19665
20185
|
return buildUpgradeHeaders({
|
|
@@ -19669,14 +20189,108 @@ async function startConnectorRuntime(input) {
|
|
|
19669
20189
|
secretKey
|
|
19670
20190
|
});
|
|
19671
20191
|
};
|
|
20192
|
+
const replayPendingInboundMessages = async () => {
|
|
20193
|
+
if (runtimeStopping || replayInFlight) {
|
|
20194
|
+
return;
|
|
20195
|
+
}
|
|
20196
|
+
replayInFlight = true;
|
|
20197
|
+
inboundReplayStatus.replayerActive = true;
|
|
20198
|
+
try {
|
|
20199
|
+
const dueItems = await inboundInbox.listDuePending({
|
|
20200
|
+
nowMs: Date.now(),
|
|
20201
|
+
limit: inboundReplayPolicy.batchSize
|
|
20202
|
+
});
|
|
20203
|
+
for (const pending of dueItems) {
|
|
20204
|
+
inboundReplayStatus.lastAttemptAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
20205
|
+
try {
|
|
20206
|
+
await deliverToOpenclawHook({
|
|
20207
|
+
fetchImpl,
|
|
20208
|
+
openclawHookUrl,
|
|
20209
|
+
openclawHookToken,
|
|
20210
|
+
requestId: pending.requestId,
|
|
20211
|
+
payload: pending.payload
|
|
20212
|
+
});
|
|
20213
|
+
await inboundInbox.markDelivered(pending.requestId);
|
|
20214
|
+
inboundReplayStatus.lastReplayAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
20215
|
+
inboundReplayStatus.lastReplayError = void 0;
|
|
20216
|
+
inboundReplayStatus.lastAttemptStatus = "ok";
|
|
20217
|
+
logger12.info("connector.inbound.replay_succeeded", {
|
|
20218
|
+
requestId: pending.requestId,
|
|
20219
|
+
attemptCount: pending.attemptCount + 1
|
|
20220
|
+
});
|
|
20221
|
+
} catch (error48) {
|
|
20222
|
+
const reason = sanitizeErrorReason2(error48);
|
|
20223
|
+
const retryable = error48 instanceof LocalOpenclawDeliveryError2 ? error48.retryable : true;
|
|
20224
|
+
const nextAttemptAt = new Date(
|
|
20225
|
+
Date.now() + computeReplayDelayMs({
|
|
20226
|
+
attemptCount: pending.attemptCount + 1,
|
|
20227
|
+
policy: inboundReplayPolicy
|
|
20228
|
+
}) * (retryable ? 1 : 10)
|
|
20229
|
+
).toISOString();
|
|
20230
|
+
await inboundInbox.markReplayFailure({
|
|
20231
|
+
requestId: pending.requestId,
|
|
20232
|
+
errorMessage: reason,
|
|
20233
|
+
nextAttemptAt
|
|
20234
|
+
});
|
|
20235
|
+
inboundReplayStatus.lastReplayError = reason;
|
|
20236
|
+
inboundReplayStatus.lastAttemptStatus = "failed";
|
|
20237
|
+
logger12.warn("connector.inbound.replay_failed", {
|
|
20238
|
+
requestId: pending.requestId,
|
|
20239
|
+
attemptCount: pending.attemptCount + 1,
|
|
20240
|
+
retryable,
|
|
20241
|
+
nextAttemptAt,
|
|
20242
|
+
reason
|
|
20243
|
+
});
|
|
20244
|
+
}
|
|
20245
|
+
}
|
|
20246
|
+
} finally {
|
|
20247
|
+
replayInFlight = false;
|
|
20248
|
+
inboundReplayStatus.replayerActive = false;
|
|
20249
|
+
}
|
|
20250
|
+
};
|
|
20251
|
+
const readInboundReplayView = async () => {
|
|
20252
|
+
const pending = await inboundInbox.getSnapshot();
|
|
20253
|
+
return {
|
|
20254
|
+
pending,
|
|
20255
|
+
replayerActive: inboundReplayStatus.replayerActive || replayInFlight,
|
|
20256
|
+
lastReplayAt: inboundReplayStatus.lastReplayAt,
|
|
20257
|
+
lastReplayError: inboundReplayStatus.lastReplayError,
|
|
20258
|
+
openclawHook: {
|
|
20259
|
+
url: openclawHookUrl,
|
|
20260
|
+
lastAttemptAt: inboundReplayStatus.lastAttemptAt,
|
|
20261
|
+
lastAttemptStatus: inboundReplayStatus.lastAttemptStatus
|
|
20262
|
+
}
|
|
20263
|
+
};
|
|
20264
|
+
};
|
|
19672
20265
|
const connectorClient = new ConnectorClient({
|
|
19673
20266
|
connectorUrl: wsParsed.toString(),
|
|
19674
20267
|
connectionHeadersProvider: resolveUpgradeHeaders,
|
|
19675
|
-
openclawBaseUrl
|
|
19676
|
-
openclawHookPath
|
|
19677
|
-
openclawHookToken
|
|
20268
|
+
openclawBaseUrl,
|
|
20269
|
+
openclawHookPath,
|
|
20270
|
+
openclawHookToken,
|
|
19678
20271
|
fetchImpl,
|
|
19679
20272
|
logger: logger12,
|
|
20273
|
+
inboundDeliverHandler: async (frame) => {
|
|
20274
|
+
const persisted = await inboundInbox.enqueue(frame);
|
|
20275
|
+
if (!persisted.accepted) {
|
|
20276
|
+
logger12.warn("connector.inbound.persist_rejected", {
|
|
20277
|
+
requestId: frame.id,
|
|
20278
|
+
reason: persisted.reason ?? "inbox limit reached",
|
|
20279
|
+
pendingCount: persisted.pendingCount
|
|
20280
|
+
});
|
|
20281
|
+
return {
|
|
20282
|
+
accepted: false,
|
|
20283
|
+
reason: persisted.reason
|
|
20284
|
+
};
|
|
20285
|
+
}
|
|
20286
|
+
logger12.info("connector.inbound.persisted", {
|
|
20287
|
+
requestId: frame.id,
|
|
20288
|
+
duplicate: persisted.duplicate,
|
|
20289
|
+
pendingCount: persisted.pendingCount
|
|
20290
|
+
});
|
|
20291
|
+
void replayPendingInboundMessages();
|
|
20292
|
+
return { accepted: true };
|
|
20293
|
+
},
|
|
19680
20294
|
webSocketFactory: createWebSocketFactory()
|
|
19681
20295
|
});
|
|
19682
20296
|
const outboundBaseUrl = normalizeOutboundBaseUrl(input.outboundBaseUrl);
|
|
@@ -19760,11 +20374,40 @@ async function startConnectorRuntime(input) {
|
|
|
19760
20374
|
writeJson(res, 405, { error: "Method Not Allowed" });
|
|
19761
20375
|
return;
|
|
19762
20376
|
}
|
|
20377
|
+
let inboundReplayView;
|
|
20378
|
+
try {
|
|
20379
|
+
inboundReplayView = await readInboundReplayView();
|
|
20380
|
+
} catch (error48) {
|
|
20381
|
+
logger12.warn("connector.status.inbound_inbox_unavailable", {
|
|
20382
|
+
reason: sanitizeErrorReason2(error48)
|
|
20383
|
+
});
|
|
20384
|
+
writeJson(res, 500, {
|
|
20385
|
+
status: "error",
|
|
20386
|
+
error: {
|
|
20387
|
+
code: "CONNECTOR_INBOUND_INBOX_UNAVAILABLE",
|
|
20388
|
+
message: "Connector inbound inbox status is unavailable"
|
|
20389
|
+
},
|
|
20390
|
+
outboundUrl,
|
|
20391
|
+
websocketUrl: wsUrl,
|
|
20392
|
+
websocketConnected: connectorClient.isConnected()
|
|
20393
|
+
});
|
|
20394
|
+
return;
|
|
20395
|
+
}
|
|
19763
20396
|
writeJson(res, 200, {
|
|
19764
20397
|
status: "ok",
|
|
19765
20398
|
outboundUrl,
|
|
19766
20399
|
websocketUrl: wsUrl,
|
|
19767
|
-
websocketConnected: connectorClient.isConnected()
|
|
20400
|
+
websocketConnected: connectorClient.isConnected(),
|
|
20401
|
+
inboundInbox: {
|
|
20402
|
+
pendingCount: inboundReplayView.pending.pendingCount,
|
|
20403
|
+
pendingBytes: inboundReplayView.pending.pendingBytes,
|
|
20404
|
+
oldestPendingAt: inboundReplayView.pending.oldestPendingAt,
|
|
20405
|
+
nextAttemptAt: inboundReplayView.pending.nextAttemptAt,
|
|
20406
|
+
replayerActive: inboundReplayView.replayerActive,
|
|
20407
|
+
lastReplayAt: inboundReplayView.lastReplayAt,
|
|
20408
|
+
lastReplayError: inboundReplayView.lastReplayError
|
|
20409
|
+
},
|
|
20410
|
+
openclawHook: inboundReplayView.openclawHook
|
|
19768
20411
|
});
|
|
19769
20412
|
return;
|
|
19770
20413
|
}
|
|
@@ -19814,6 +20457,11 @@ async function startConnectorRuntime(input) {
|
|
|
19814
20457
|
stoppedResolve = resolve2;
|
|
19815
20458
|
});
|
|
19816
20459
|
const stop = async () => {
|
|
20460
|
+
runtimeStopping = true;
|
|
20461
|
+
if (replayIntervalHandle !== void 0) {
|
|
20462
|
+
clearInterval(replayIntervalHandle);
|
|
20463
|
+
replayIntervalHandle = void 0;
|
|
20464
|
+
}
|
|
19817
20465
|
connectorClient.disconnect();
|
|
19818
20466
|
await new Promise((resolve2, reject) => {
|
|
19819
20467
|
server.close((error48) => {
|
|
@@ -19838,6 +20486,11 @@ async function startConnectorRuntime(input) {
|
|
|
19838
20486
|
);
|
|
19839
20487
|
});
|
|
19840
20488
|
connectorClient.connect();
|
|
20489
|
+
await inboundInbox.pruneDelivered();
|
|
20490
|
+
void replayPendingInboundMessages();
|
|
20491
|
+
replayIntervalHandle = setInterval(() => {
|
|
20492
|
+
void replayPendingInboundMessages();
|
|
20493
|
+
}, inboundReplayPolicy.replayIntervalMs);
|
|
19841
20494
|
logger12.info("connector.runtime.started", {
|
|
19842
20495
|
outboundUrl,
|
|
19843
20496
|
websocketUrl: wsUrl,
|
|
@@ -19865,11 +20518,11 @@ var OPENCLAW_CONNECTORS_FILE_NAME = "openclaw-connectors.json";
|
|
|
19865
20518
|
var SERVICE_LOG_DIR_NAME = "logs";
|
|
19866
20519
|
var DEFAULT_CONNECTOR_BASE_URL2 = "http://127.0.0.1:19400";
|
|
19867
20520
|
var DEFAULT_CONNECTOR_OUTBOUND_PATH2 = "/v1/outbound";
|
|
19868
|
-
function
|
|
20521
|
+
function isRecord7(value) {
|
|
19869
20522
|
return typeof value === "object" && value !== null;
|
|
19870
20523
|
}
|
|
19871
20524
|
function getErrorCode(error48) {
|
|
19872
|
-
if (!
|
|
20525
|
+
if (!isRecord7(error48)) {
|
|
19873
20526
|
return void 0;
|
|
19874
20527
|
}
|
|
19875
20528
|
return typeof error48.code === "string" ? error48.code : void 0;
|
|
@@ -20008,7 +20661,7 @@ function resolveConnectorBaseUrlFromEnv() {
|
|
|
20008
20661
|
return parseConnectorBaseUrl(value.trim());
|
|
20009
20662
|
}
|
|
20010
20663
|
async function readConnectorAssignedBaseUrl(configDir, agentName, readFileImpl) {
|
|
20011
|
-
const assignmentsPath =
|
|
20664
|
+
const assignmentsPath = join6(configDir, OPENCLAW_CONNECTORS_FILE_NAME);
|
|
20012
20665
|
let raw;
|
|
20013
20666
|
try {
|
|
20014
20667
|
raw = await readFileImpl(assignmentsPath, "utf8");
|
|
@@ -20028,11 +20681,11 @@ async function readConnectorAssignedBaseUrl(configDir, agentName, readFileImpl)
|
|
|
20028
20681
|
{ assignmentsPath }
|
|
20029
20682
|
);
|
|
20030
20683
|
}
|
|
20031
|
-
if (!
|
|
20684
|
+
if (!isRecord7(parsed) || !isRecord7(parsed.agents)) {
|
|
20032
20685
|
return void 0;
|
|
20033
20686
|
}
|
|
20034
20687
|
const entry = parsed.agents[agentName];
|
|
20035
|
-
if (!
|
|
20688
|
+
if (!isRecord7(entry) || typeof entry.connectorBaseUrl !== "string") {
|
|
20036
20689
|
return void 0;
|
|
20037
20690
|
}
|
|
20038
20691
|
return parseConnectorBaseUrl(entry.connectorBaseUrl);
|
|
@@ -20072,7 +20725,7 @@ async function readRequiredTrimmedFile(filePath, label, readFileImpl) {
|
|
|
20072
20725
|
return trimmed;
|
|
20073
20726
|
}
|
|
20074
20727
|
async function readRelayRuntimeConfig(configDir, readFileImpl) {
|
|
20075
|
-
const filePath =
|
|
20728
|
+
const filePath = join6(configDir, OPENCLAW_RELAY_RUNTIME_FILE_NAME);
|
|
20076
20729
|
let raw;
|
|
20077
20730
|
try {
|
|
20078
20731
|
raw = await readFileImpl(filePath, "utf8");
|
|
@@ -20088,7 +20741,7 @@ async function readRelayRuntimeConfig(configDir, readFileImpl) {
|
|
|
20088
20741
|
} catch {
|
|
20089
20742
|
return void 0;
|
|
20090
20743
|
}
|
|
20091
|
-
if (!
|
|
20744
|
+
if (!isRecord7(parsed)) {
|
|
20092
20745
|
return void 0;
|
|
20093
20746
|
}
|
|
20094
20747
|
const openclawHookToken = typeof parsed.openclawHookToken === "string" && parsed.openclawHookToken.trim().length > 0 ? parsed.openclawHookToken.trim() : void 0;
|
|
@@ -20106,7 +20759,7 @@ function parseJsonRecord(value, code, message2) {
|
|
|
20106
20759
|
} catch {
|
|
20107
20760
|
throw createCliError4(code, message2);
|
|
20108
20761
|
}
|
|
20109
|
-
if (!
|
|
20762
|
+
if (!isRecord7(parsed)) {
|
|
20110
20763
|
throw createCliError4(code, message2);
|
|
20111
20764
|
}
|
|
20112
20765
|
return parsed;
|
|
@@ -20146,7 +20799,7 @@ async function loadDefaultConnectorModule() {
|
|
|
20146
20799
|
};
|
|
20147
20800
|
}
|
|
20148
20801
|
function resolveWaitPromise(runtime) {
|
|
20149
|
-
if (!runtime || !
|
|
20802
|
+
if (!runtime || !isRecord7(runtime)) {
|
|
20150
20803
|
return void 0;
|
|
20151
20804
|
}
|
|
20152
20805
|
if (typeof runtime.waitUntilStopped === "function") {
|
|
@@ -20211,7 +20864,7 @@ function buildConnectorStartArgs(agentName, commandOptions) {
|
|
|
20211
20864
|
}
|
|
20212
20865
|
function resolveCliEntryPath(resolveCurrentModulePathImpl) {
|
|
20213
20866
|
const modulePath = resolveCurrentModulePathImpl?.() ?? fileURLToPath(import.meta.url);
|
|
20214
|
-
return
|
|
20867
|
+
return join6(dirname4(modulePath), "..", "bin.js");
|
|
20215
20868
|
}
|
|
20216
20869
|
function escapeXml(value) {
|
|
20217
20870
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
@@ -20281,7 +20934,7 @@ function resolveServiceDependencies(dependencies) {
|
|
|
20281
20934
|
execFileImpl,
|
|
20282
20935
|
getConfigDirImpl: dependencies.getConfigDirImpl ?? getConfigDir,
|
|
20283
20936
|
getHomeDirImpl: dependencies.getHomeDirImpl ?? homedir2,
|
|
20284
|
-
mkdirImpl: dependencies.mkdirImpl ??
|
|
20937
|
+
mkdirImpl: dependencies.mkdirImpl ?? mkdir5,
|
|
20285
20938
|
removeFileImpl: dependencies.removeFileImpl ?? rm,
|
|
20286
20939
|
resolveCurrentModulePathImpl: dependencies.resolveCurrentModulePathImpl,
|
|
20287
20940
|
resolveCurrentPlatformImpl: dependencies.resolveCurrentPlatformImpl ?? (() => process.platform),
|
|
@@ -20295,7 +20948,7 @@ function resolveServiceDependencies(dependencies) {
|
|
|
20295
20948
|
return process.getuid();
|
|
20296
20949
|
}),
|
|
20297
20950
|
resolveNodeExecPathImpl: dependencies.resolveNodeExecPathImpl ?? (() => process.execPath),
|
|
20298
|
-
writeFileImpl: dependencies.writeFileImpl ??
|
|
20951
|
+
writeFileImpl: dependencies.writeFileImpl ?? writeFile5
|
|
20299
20952
|
};
|
|
20300
20953
|
}
|
|
20301
20954
|
async function installConnectorServiceForAgent(agentName, commandOptions = {}, dependencies = {}) {
|
|
@@ -20309,7 +20962,7 @@ async function installConnectorServiceForAgent(agentName, commandOptions = {}, d
|
|
|
20309
20962
|
);
|
|
20310
20963
|
const configDir = serviceDependencies.getConfigDirImpl();
|
|
20311
20964
|
const homeDir = serviceDependencies.getHomeDirImpl();
|
|
20312
|
-
const logsDir =
|
|
20965
|
+
const logsDir = join6(configDir, SERVICE_LOG_DIR_NAME);
|
|
20313
20966
|
const serviceName = sanitizeServiceSegment(
|
|
20314
20967
|
`clawdentity-connector-${agentName}`
|
|
20315
20968
|
);
|
|
@@ -20319,12 +20972,12 @@ async function installConnectorServiceForAgent(agentName, commandOptions = {}, d
|
|
|
20319
20972
|
resolveCliEntryPath(serviceDependencies.resolveCurrentModulePathImpl),
|
|
20320
20973
|
...startArgs
|
|
20321
20974
|
];
|
|
20322
|
-
const outputLogPath =
|
|
20323
|
-
const errorLogPath =
|
|
20975
|
+
const outputLogPath = join6(logsDir, `${serviceName}.out.log`);
|
|
20976
|
+
const errorLogPath = join6(logsDir, `${serviceName}.err.log`);
|
|
20324
20977
|
await serviceDependencies.mkdirImpl(logsDir, { recursive: true });
|
|
20325
20978
|
if (platform === "systemd") {
|
|
20326
|
-
const serviceDir =
|
|
20327
|
-
const serviceFilePath2 =
|
|
20979
|
+
const serviceDir = join6(homeDir, ".config", "systemd", "user");
|
|
20980
|
+
const serviceFilePath2 = join6(serviceDir, `${serviceName}.service`);
|
|
20328
20981
|
await serviceDependencies.mkdirImpl(serviceDir, { recursive: true });
|
|
20329
20982
|
await serviceDependencies.writeFileImpl(
|
|
20330
20983
|
serviceFilePath2,
|
|
@@ -20363,9 +21016,9 @@ async function installConnectorServiceForAgent(agentName, commandOptions = {}, d
|
|
|
20363
21016
|
serviceFilePath: serviceFilePath2
|
|
20364
21017
|
};
|
|
20365
21018
|
}
|
|
20366
|
-
const launchAgentsDir =
|
|
21019
|
+
const launchAgentsDir = join6(homeDir, "Library", "LaunchAgents");
|
|
20367
21020
|
const serviceNameWithDomain = `com.clawdentity.${serviceName}`;
|
|
20368
|
-
const serviceFilePath =
|
|
21021
|
+
const serviceFilePath = join6(
|
|
20369
21022
|
launchAgentsDir,
|
|
20370
21023
|
`${serviceNameWithDomain}.plist`
|
|
20371
21024
|
);
|
|
@@ -20424,7 +21077,7 @@ async function uninstallConnectorServiceForAgent(agentName, commandOptions = {},
|
|
|
20424
21077
|
`clawdentity-connector-${agentName}`
|
|
20425
21078
|
);
|
|
20426
21079
|
if (platform === "systemd") {
|
|
20427
|
-
const serviceFilePath2 =
|
|
21080
|
+
const serviceFilePath2 = join6(
|
|
20428
21081
|
homeDir,
|
|
20429
21082
|
".config",
|
|
20430
21083
|
"systemd",
|
|
@@ -20455,7 +21108,7 @@ async function uninstallConnectorServiceForAgent(agentName, commandOptions = {},
|
|
|
20455
21108
|
};
|
|
20456
21109
|
}
|
|
20457
21110
|
const serviceNameWithDomain = `com.clawdentity.${serviceName}`;
|
|
20458
|
-
const serviceFilePath =
|
|
21111
|
+
const serviceFilePath = join6(
|
|
20459
21112
|
homeDir,
|
|
20460
21113
|
"Library",
|
|
20461
21114
|
"LaunchAgents",
|
|
@@ -20479,11 +21132,11 @@ async function uninstallConnectorServiceForAgent(agentName, commandOptions = {},
|
|
|
20479
21132
|
async function startConnectorForAgent(agentName, commandOptions = {}, dependencies = {}) {
|
|
20480
21133
|
const resolveConfigImpl = dependencies.resolveConfigImpl ?? resolveConfig;
|
|
20481
21134
|
const getConfigDirImpl = dependencies.getConfigDirImpl ?? getConfigDir;
|
|
20482
|
-
const readFileImpl = dependencies.readFileImpl ?? ((path, encoding) =>
|
|
21135
|
+
const readFileImpl = dependencies.readFileImpl ?? ((path, encoding) => readFile5(path, encoding));
|
|
20483
21136
|
const fetchImpl = dependencies.fetchImpl ?? globalThis.fetch;
|
|
20484
21137
|
const loadConnectorModule = dependencies.loadConnectorModule ?? loadDefaultConnectorModule;
|
|
20485
21138
|
const configDir = getConfigDirImpl();
|
|
20486
|
-
const agentDirectory =
|
|
21139
|
+
const agentDirectory = join6(configDir, AGENTS_DIR_NAME3, agentName);
|
|
20487
21140
|
const [
|
|
20488
21141
|
rawAit,
|
|
20489
21142
|
rawSecretKey,
|
|
@@ -20495,22 +21148,22 @@ async function startConnectorForAgent(agentName, commandOptions = {}, dependenci
|
|
|
20495
21148
|
connectorModule
|
|
20496
21149
|
] = await Promise.all([
|
|
20497
21150
|
readRequiredTrimmedFile(
|
|
20498
|
-
|
|
21151
|
+
join6(agentDirectory, AIT_FILE_NAME2),
|
|
20499
21152
|
AIT_FILE_NAME2,
|
|
20500
21153
|
readFileImpl
|
|
20501
21154
|
),
|
|
20502
21155
|
readRequiredTrimmedFile(
|
|
20503
|
-
|
|
21156
|
+
join6(agentDirectory, SECRET_KEY_FILE_NAME),
|
|
20504
21157
|
SECRET_KEY_FILE_NAME,
|
|
20505
21158
|
readFileImpl
|
|
20506
21159
|
),
|
|
20507
21160
|
readRequiredTrimmedFile(
|
|
20508
|
-
|
|
21161
|
+
join6(agentDirectory, IDENTITY_FILE_NAME2),
|
|
20509
21162
|
IDENTITY_FILE_NAME2,
|
|
20510
21163
|
readFileImpl
|
|
20511
21164
|
),
|
|
20512
21165
|
readRequiredTrimmedFile(
|
|
20513
|
-
|
|
21166
|
+
join6(agentDirectory, REGISTRY_AUTH_FILE_NAME2),
|
|
20514
21167
|
REGISTRY_AUTH_FILE_NAME2,
|
|
20515
21168
|
readFileImpl
|
|
20516
21169
|
),
|
|
@@ -20557,8 +21210,8 @@ async function startConnectorForAgent(agentName, commandOptions = {}, dependenci
|
|
|
20557
21210
|
tokenType: registryAuth.tokenType
|
|
20558
21211
|
}
|
|
20559
21212
|
});
|
|
20560
|
-
const outboundUrl = runtime &&
|
|
20561
|
-
const proxyWebsocketUrl = runtime &&
|
|
21213
|
+
const outboundUrl = runtime && isRecord7(runtime) && typeof runtime.outboundUrl === "string" ? runtime.outboundUrl : resolveOutboundUrl(outboundBaseUrl, outboundPath);
|
|
21214
|
+
const proxyWebsocketUrl = runtime && isRecord7(runtime) ? typeof runtime.websocketUrl === "string" ? runtime.websocketUrl : typeof runtime.proxyWebsocketUrl === "string" ? runtime.proxyWebsocketUrl : resolvedProxyWebsocketUrl : void 0;
|
|
20562
21215
|
return {
|
|
20563
21216
|
outboundUrl,
|
|
20564
21217
|
proxyWebsocketUrl,
|
|
@@ -20689,7 +21342,7 @@ function createConnectorCommand(dependencies = {}) {
|
|
|
20689
21342
|
// src/commands/invite.ts
|
|
20690
21343
|
import { Command as Command6 } from "commander";
|
|
20691
21344
|
var logger7 = createLogger({ service: "cli", module: "invite" });
|
|
20692
|
-
var
|
|
21345
|
+
var isRecord8 = (value) => {
|
|
20693
21346
|
return typeof value === "object" && value !== null;
|
|
20694
21347
|
};
|
|
20695
21348
|
function parseNonEmptyString7(value) {
|
|
@@ -20733,7 +21386,7 @@ function requireApiKey2(config2) {
|
|
|
20733
21386
|
);
|
|
20734
21387
|
}
|
|
20735
21388
|
function extractRegistryErrorCode(payload) {
|
|
20736
|
-
if (!
|
|
21389
|
+
if (!isRecord8(payload)) {
|
|
20737
21390
|
return void 0;
|
|
20738
21391
|
}
|
|
20739
21392
|
const envelope = payload;
|
|
@@ -20744,7 +21397,7 @@ function extractRegistryErrorCode(payload) {
|
|
|
20744
21397
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
20745
21398
|
}
|
|
20746
21399
|
function extractRegistryErrorMessage4(payload) {
|
|
20747
|
-
if (!
|
|
21400
|
+
if (!isRecord8(payload)) {
|
|
20748
21401
|
return void 0;
|
|
20749
21402
|
}
|
|
20750
21403
|
const envelope = payload;
|
|
@@ -20818,13 +21471,13 @@ function mapRedeemInviteError(status, payload) {
|
|
|
20818
21471
|
return `Invite redeem failed (${status})`;
|
|
20819
21472
|
}
|
|
20820
21473
|
function parseInviteRecord(payload) {
|
|
20821
|
-
if (!
|
|
21474
|
+
if (!isRecord8(payload)) {
|
|
20822
21475
|
throw createCliError5(
|
|
20823
21476
|
"CLI_INVITE_CREATE_INVALID_RESPONSE",
|
|
20824
21477
|
"Invite response is invalid"
|
|
20825
21478
|
);
|
|
20826
21479
|
}
|
|
20827
|
-
const source =
|
|
21480
|
+
const source = isRecord8(payload.invite) ? payload.invite : payload;
|
|
20828
21481
|
const code = parseNonEmptyString7(source.code);
|
|
20829
21482
|
if (code.length === 0) {
|
|
20830
21483
|
throw createCliError5(
|
|
@@ -20849,15 +21502,15 @@ function parseInviteRecord(payload) {
|
|
|
20849
21502
|
return invite;
|
|
20850
21503
|
}
|
|
20851
21504
|
function parseInviteRedeemResponse(payload) {
|
|
20852
|
-
if (!
|
|
21505
|
+
if (!isRecord8(payload)) {
|
|
20853
21506
|
throw createCliError5(
|
|
20854
21507
|
"CLI_INVITE_REDEEM_INVALID_RESPONSE",
|
|
20855
21508
|
"Invite redeem response is invalid"
|
|
20856
21509
|
);
|
|
20857
21510
|
}
|
|
20858
|
-
const apiKeySource =
|
|
21511
|
+
const apiKeySource = isRecord8(payload.apiKey) ? payload.apiKey : payload;
|
|
20859
21512
|
const apiKeyToken = parseNonEmptyString7(
|
|
20860
|
-
|
|
21513
|
+
isRecord8(payload.apiKey) ? payload.apiKey.token : payload.token
|
|
20861
21514
|
);
|
|
20862
21515
|
if (apiKeyToken.length === 0) {
|
|
20863
21516
|
throw createCliError5(
|
|
@@ -20867,7 +21520,7 @@ function parseInviteRedeemResponse(payload) {
|
|
|
20867
21520
|
}
|
|
20868
21521
|
const apiKeyId = parseNonEmptyString7(apiKeySource.id);
|
|
20869
21522
|
const apiKeyName = parseNonEmptyString7(apiKeySource.name);
|
|
20870
|
-
const humanSource =
|
|
21523
|
+
const humanSource = isRecord8(payload.human) ? payload.human : void 0;
|
|
20871
21524
|
const humanName = parseNonEmptyString7(humanSource?.displayName);
|
|
20872
21525
|
const proxyUrl = parseNonEmptyString7(payload.proxyUrl);
|
|
20873
21526
|
if (humanName.length === 0) {
|
|
@@ -21055,9 +21708,9 @@ var createInviteCommand = (dependencies = {}) => {
|
|
|
21055
21708
|
import { spawn } from "child_process";
|
|
21056
21709
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
21057
21710
|
import { existsSync } from "fs";
|
|
21058
|
-
import { chmod as chmod3, copyFile, mkdir as
|
|
21711
|
+
import { chmod as chmod3, copyFile, mkdir as mkdir6, readFile as readFile6, writeFile as writeFile6 } from "fs/promises";
|
|
21059
21712
|
import { homedir as homedir3 } from "os";
|
|
21060
|
-
import { dirname as
|
|
21713
|
+
import { dirname as dirname5, join as join7, resolve as resolvePath } from "path";
|
|
21061
21714
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
21062
21715
|
import { Command as Command7 } from "commander";
|
|
21063
21716
|
var logger8 = createLogger({ service: "cli", module: "openclaw" });
|
|
@@ -21106,10 +21759,12 @@ var OPENCLAW_SETUP_COMMAND_HINT = "Run: clawdentity openclaw setup <agentName>";
|
|
|
21106
21759
|
var OPENCLAW_SETUP_RESTART_COMMAND_HINT = `${OPENCLAW_SETUP_COMMAND_HINT} and restart OpenClaw`;
|
|
21107
21760
|
var OPENCLAW_SETUP_WITH_BASE_URL_HINT = `${OPENCLAW_SETUP_COMMAND_HINT} --openclaw-base-url <url>`;
|
|
21108
21761
|
var OPENCLAW_PAIRING_COMMAND_HINT = "Run QR pairing first: clawdentity pair start <agentName> --qr and clawdentity pair confirm <agentName> --qr-file <path>";
|
|
21109
|
-
var
|
|
21762
|
+
var OPENCLAW_DEVICE_APPROVAL_RECOVERY_HINT = "Run: clawdentity openclaw setup <agentName> (auto-recovers pending OpenClaw gateway device approvals)";
|
|
21763
|
+
var OPENCLAW_GATEWAY_APPROVAL_COMMAND = "openclaw";
|
|
21764
|
+
var OPENCLAW_GATEWAY_APPROVAL_TIMEOUT_MS = 1e4;
|
|
21110
21765
|
var textEncoder2 = new TextEncoder();
|
|
21111
21766
|
var textDecoder = new TextDecoder();
|
|
21112
|
-
function
|
|
21767
|
+
function isRecord9(value) {
|
|
21113
21768
|
return typeof value === "object" && value !== null;
|
|
21114
21769
|
}
|
|
21115
21770
|
function createCliError6(code, message2, details) {
|
|
@@ -21121,7 +21776,7 @@ function createCliError6(code, message2, details) {
|
|
|
21121
21776
|
});
|
|
21122
21777
|
}
|
|
21123
21778
|
function getErrorCode2(error48) {
|
|
21124
|
-
if (!
|
|
21779
|
+
if (!isRecord9(error48)) {
|
|
21125
21780
|
return void 0;
|
|
21126
21781
|
}
|
|
21127
21782
|
return typeof error48.code === "string" ? error48.code : void 0;
|
|
@@ -21242,12 +21897,12 @@ function resolveOpenclawHomeDir(homeDir) {
|
|
|
21242
21897
|
return envOpenclawHome ?? homeDir;
|
|
21243
21898
|
}
|
|
21244
21899
|
function resolveDefaultOpenclawStateDir(openclawHomeDir) {
|
|
21245
|
-
const newStateDir =
|
|
21900
|
+
const newStateDir = join7(openclawHomeDir, OPENCLAW_DIR_NAME);
|
|
21246
21901
|
if (existsSync(newStateDir)) {
|
|
21247
21902
|
return newStateDir;
|
|
21248
21903
|
}
|
|
21249
21904
|
for (const legacyDirName of LEGACY_OPENCLAW_STATE_DIR_NAMES) {
|
|
21250
|
-
const legacyStateDir =
|
|
21905
|
+
const legacyStateDir = join7(openclawHomeDir, legacyDirName);
|
|
21251
21906
|
if (existsSync(legacyStateDir)) {
|
|
21252
21907
|
return legacyStateDir;
|
|
21253
21908
|
}
|
|
@@ -21270,16 +21925,16 @@ function resolveOpenclawDir(openclawDir, homeDir) {
|
|
|
21270
21925
|
homeDir
|
|
21271
21926
|
);
|
|
21272
21927
|
if (envConfigPath !== void 0) {
|
|
21273
|
-
return
|
|
21928
|
+
return dirname5(envConfigPath);
|
|
21274
21929
|
}
|
|
21275
21930
|
const openclawHomeDir = resolveOpenclawHomeDir(homeDir);
|
|
21276
21931
|
return resolveDefaultOpenclawStateDir(openclawHomeDir);
|
|
21277
21932
|
}
|
|
21278
21933
|
function resolveAgentDirectory(homeDir, agentName) {
|
|
21279
|
-
return
|
|
21934
|
+
return join7(homeDir, CLAWDENTITY_DIR_NAME, AGENTS_DIR_NAME4, agentName);
|
|
21280
21935
|
}
|
|
21281
21936
|
function resolvePeersPath(homeDir) {
|
|
21282
|
-
return
|
|
21937
|
+
return join7(homeDir, CLAWDENTITY_DIR_NAME, PEERS_FILE_NAME);
|
|
21283
21938
|
}
|
|
21284
21939
|
function resolveOpenclawConfigPath(openclawDir, homeDir) {
|
|
21285
21940
|
const envConfigPath = readNonEmptyEnvPath(
|
|
@@ -21290,9 +21945,9 @@ function resolveOpenclawConfigPath(openclawDir, homeDir) {
|
|
|
21290
21945
|
return envConfigPath;
|
|
21291
21946
|
}
|
|
21292
21947
|
const configCandidates = [
|
|
21293
|
-
|
|
21948
|
+
join7(openclawDir, OPENCLAW_CONFIG_FILE_NAME),
|
|
21294
21949
|
...LEGACY_OPENCLAW_CONFIG_FILE_NAMES.map(
|
|
21295
|
-
(fileName) =>
|
|
21950
|
+
(fileName) => join7(openclawDir, fileName)
|
|
21296
21951
|
)
|
|
21297
21952
|
];
|
|
21298
21953
|
for (const candidate of configCandidates) {
|
|
@@ -21303,28 +21958,166 @@ function resolveOpenclawConfigPath(openclawDir, homeDir) {
|
|
|
21303
21958
|
return configCandidates[0];
|
|
21304
21959
|
}
|
|
21305
21960
|
function resolveDefaultTransformSource(openclawDir) {
|
|
21306
|
-
return
|
|
21961
|
+
return join7(openclawDir, "skills", SKILL_DIR_NAME, RELAY_MODULE_FILE_NAME);
|
|
21307
21962
|
}
|
|
21308
21963
|
function resolveTransformTargetPath(openclawDir) {
|
|
21309
|
-
return
|
|
21964
|
+
return join7(openclawDir, "hooks", "transforms", RELAY_MODULE_FILE_NAME);
|
|
21310
21965
|
}
|
|
21311
21966
|
function resolveOpenclawAgentNamePath(homeDir) {
|
|
21312
|
-
return
|
|
21967
|
+
return join7(homeDir, CLAWDENTITY_DIR_NAME, OPENCLAW_AGENT_FILE_NAME);
|
|
21313
21968
|
}
|
|
21314
21969
|
function resolveRelayRuntimeConfigPath(homeDir) {
|
|
21315
|
-
return
|
|
21970
|
+
return join7(homeDir, CLAWDENTITY_DIR_NAME, OPENCLAW_RELAY_RUNTIME_FILE_NAME2);
|
|
21316
21971
|
}
|
|
21317
21972
|
function resolveConnectorAssignmentsPath(homeDir) {
|
|
21318
|
-
return
|
|
21973
|
+
return join7(homeDir, CLAWDENTITY_DIR_NAME, OPENCLAW_CONNECTORS_FILE_NAME2);
|
|
21319
21974
|
}
|
|
21320
21975
|
function resolveTransformRuntimePath(openclawDir) {
|
|
21321
|
-
return
|
|
21976
|
+
return join7(openclawDir, "hooks", "transforms", RELAY_RUNTIME_FILE_NAME);
|
|
21322
21977
|
}
|
|
21323
21978
|
function resolveTransformPeersPath(openclawDir) {
|
|
21324
|
-
return
|
|
21979
|
+
return join7(openclawDir, "hooks", "transforms", RELAY_PEERS_FILE_NAME);
|
|
21980
|
+
}
|
|
21981
|
+
async function readOpenclawGatewayPendingState(openclawDir) {
|
|
21982
|
+
const gatewayDevicePendingPath = join7(openclawDir, "devices", "pending.json");
|
|
21983
|
+
try {
|
|
21984
|
+
const pendingPayload = await readJsonFile(gatewayDevicePendingPath);
|
|
21985
|
+
if (!isRecord9(pendingPayload)) {
|
|
21986
|
+
return {
|
|
21987
|
+
status: "invalid",
|
|
21988
|
+
gatewayDevicePendingPath
|
|
21989
|
+
};
|
|
21990
|
+
}
|
|
21991
|
+
return {
|
|
21992
|
+
status: "ok",
|
|
21993
|
+
gatewayDevicePendingPath,
|
|
21994
|
+
pendingRequestIds: Object.keys(pendingPayload)
|
|
21995
|
+
};
|
|
21996
|
+
} catch (error48) {
|
|
21997
|
+
if (getErrorCode2(error48) === "ENOENT") {
|
|
21998
|
+
return {
|
|
21999
|
+
status: "missing",
|
|
22000
|
+
gatewayDevicePendingPath
|
|
22001
|
+
};
|
|
22002
|
+
}
|
|
22003
|
+
return {
|
|
22004
|
+
status: "unreadable",
|
|
22005
|
+
gatewayDevicePendingPath
|
|
22006
|
+
};
|
|
22007
|
+
}
|
|
22008
|
+
}
|
|
22009
|
+
function resolveOpenclawGatewayApprovalCommand() {
|
|
22010
|
+
const envOverride = process.env.OPENCLAW_GATEWAY_APPROVAL_COMMAND?.trim();
|
|
22011
|
+
if (typeof envOverride === "string" && envOverride.length > 0) {
|
|
22012
|
+
return envOverride;
|
|
22013
|
+
}
|
|
22014
|
+
return OPENCLAW_GATEWAY_APPROVAL_COMMAND;
|
|
22015
|
+
}
|
|
22016
|
+
async function runOpenclawGatewayApprovalCommand(input) {
|
|
22017
|
+
return await new Promise(
|
|
22018
|
+
(resolve2) => {
|
|
22019
|
+
const child = spawn(input.command, input.args, {
|
|
22020
|
+
env: {
|
|
22021
|
+
...process.env,
|
|
22022
|
+
OPENCLAW_STATE_DIR: input.openclawDir,
|
|
22023
|
+
OPENCLAW_CONFIG_PATH: input.openclawConfigPath
|
|
22024
|
+
},
|
|
22025
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
22026
|
+
});
|
|
22027
|
+
let settled = false;
|
|
22028
|
+
let stdout = "";
|
|
22029
|
+
let stderr = "";
|
|
22030
|
+
const finalize2 = (result) => {
|
|
22031
|
+
if (settled) {
|
|
22032
|
+
return;
|
|
22033
|
+
}
|
|
22034
|
+
settled = true;
|
|
22035
|
+
resolve2({
|
|
22036
|
+
...result,
|
|
22037
|
+
stdout: stdout.trim(),
|
|
22038
|
+
stderr: stderr.trim()
|
|
22039
|
+
});
|
|
22040
|
+
};
|
|
22041
|
+
const timeout = setTimeout(() => {
|
|
22042
|
+
try {
|
|
22043
|
+
child.kill("SIGTERM");
|
|
22044
|
+
} catch {
|
|
22045
|
+
}
|
|
22046
|
+
finalize2({
|
|
22047
|
+
ok: false,
|
|
22048
|
+
errorMessage: `command timed out after ${OPENCLAW_GATEWAY_APPROVAL_TIMEOUT_MS}ms`
|
|
22049
|
+
});
|
|
22050
|
+
}, OPENCLAW_GATEWAY_APPROVAL_TIMEOUT_MS);
|
|
22051
|
+
child.stdout?.on("data", (chunk) => {
|
|
22052
|
+
stdout += String(chunk);
|
|
22053
|
+
});
|
|
22054
|
+
child.stderr?.on("data", (chunk) => {
|
|
22055
|
+
stderr += String(chunk);
|
|
22056
|
+
});
|
|
22057
|
+
child.once("error", (error48) => {
|
|
22058
|
+
clearTimeout(timeout);
|
|
22059
|
+
const errorCode = getErrorCode2(error48);
|
|
22060
|
+
finalize2({
|
|
22061
|
+
ok: false,
|
|
22062
|
+
unavailable: errorCode === "ENOENT",
|
|
22063
|
+
errorMessage: error48 instanceof Error ? error48.message : "failed to run openclaw command"
|
|
22064
|
+
});
|
|
22065
|
+
});
|
|
22066
|
+
child.once("close", (exitCode) => {
|
|
22067
|
+
clearTimeout(timeout);
|
|
22068
|
+
finalize2({
|
|
22069
|
+
ok: exitCode === 0,
|
|
22070
|
+
exitCode: typeof exitCode === "number" ? exitCode : void 0
|
|
22071
|
+
});
|
|
22072
|
+
});
|
|
22073
|
+
}
|
|
22074
|
+
);
|
|
22075
|
+
}
|
|
22076
|
+
async function runOpenclawGatewayDeviceApproval(input) {
|
|
22077
|
+
const command = resolveOpenclawGatewayApprovalCommand();
|
|
22078
|
+
return await runOpenclawGatewayApprovalCommand({
|
|
22079
|
+
command,
|
|
22080
|
+
args: ["devices", "approve", input.requestId, "--json"],
|
|
22081
|
+
openclawDir: input.openclawDir,
|
|
22082
|
+
openclawConfigPath: input.openclawConfigPath
|
|
22083
|
+
});
|
|
22084
|
+
}
|
|
22085
|
+
async function autoApproveOpenclawGatewayDevices(input) {
|
|
22086
|
+
const pendingState = await readOpenclawGatewayPendingState(input.openclawDir);
|
|
22087
|
+
if (pendingState.status !== "ok" || pendingState.pendingRequestIds.length === 0) {
|
|
22088
|
+
return void 0;
|
|
22089
|
+
}
|
|
22090
|
+
const openclawConfigPath = resolveOpenclawConfigPath(
|
|
22091
|
+
input.openclawDir,
|
|
22092
|
+
input.homeDir
|
|
22093
|
+
);
|
|
22094
|
+
const approvalRunner = input.runner ?? runOpenclawGatewayDeviceApproval;
|
|
22095
|
+
const attempts = [];
|
|
22096
|
+
for (const requestId of pendingState.pendingRequestIds) {
|
|
22097
|
+
const execution = await approvalRunner({
|
|
22098
|
+
requestId,
|
|
22099
|
+
openclawDir: input.openclawDir,
|
|
22100
|
+
openclawConfigPath
|
|
22101
|
+
});
|
|
22102
|
+
attempts.push({
|
|
22103
|
+
requestId,
|
|
22104
|
+
ok: execution.ok,
|
|
22105
|
+
unavailable: execution.unavailable === true,
|
|
22106
|
+
reason: execution.errorMessage ?? (execution.stderr && execution.stderr.length > 0 ? execution.stderr : execution.stdout && execution.stdout.length > 0 ? execution.stdout : void 0),
|
|
22107
|
+
exitCode: execution.exitCode
|
|
22108
|
+
});
|
|
22109
|
+
if (execution.unavailable === true) {
|
|
22110
|
+
break;
|
|
22111
|
+
}
|
|
22112
|
+
}
|
|
22113
|
+
return {
|
|
22114
|
+
gatewayDevicePendingPath: pendingState.gatewayDevicePendingPath,
|
|
22115
|
+
pendingRequestIds: pendingState.pendingRequestIds,
|
|
22116
|
+
attempts
|
|
22117
|
+
};
|
|
21325
22118
|
}
|
|
21326
22119
|
async function readJsonFile(filePath) {
|
|
21327
|
-
const raw = await
|
|
22120
|
+
const raw = await readFile6(filePath, "utf8");
|
|
21328
22121
|
try {
|
|
21329
22122
|
return JSON.parse(raw);
|
|
21330
22123
|
} catch {
|
|
@@ -21336,13 +22129,13 @@ async function readJsonFile(filePath) {
|
|
|
21336
22129
|
async function ensureLocalAgentCredentials(homeDir, agentName) {
|
|
21337
22130
|
const agentDir = resolveAgentDirectory(homeDir, agentName);
|
|
21338
22131
|
const requiredFiles = [
|
|
21339
|
-
|
|
21340
|
-
|
|
22132
|
+
join7(agentDir, SECRET_KEY_FILE_NAME2),
|
|
22133
|
+
join7(agentDir, AIT_FILE_NAME3)
|
|
21341
22134
|
];
|
|
21342
22135
|
for (const filePath of requiredFiles) {
|
|
21343
22136
|
let content;
|
|
21344
22137
|
try {
|
|
21345
|
-
content = await
|
|
22138
|
+
content = await readFile6(filePath, "utf8");
|
|
21346
22139
|
} catch (error48) {
|
|
21347
22140
|
if (getErrorCode2(error48) === "ENOENT") {
|
|
21348
22141
|
throw createCliError6(
|
|
@@ -21363,8 +22156,8 @@ async function ensureLocalAgentCredentials(homeDir, agentName) {
|
|
|
21363
22156
|
}
|
|
21364
22157
|
}
|
|
21365
22158
|
async function writeSecureFile3(filePath, content) {
|
|
21366
|
-
await
|
|
21367
|
-
await
|
|
22159
|
+
await mkdir6(dirname5(filePath), { recursive: true });
|
|
22160
|
+
await writeFile6(filePath, content, "utf8");
|
|
21368
22161
|
await chmod3(filePath, FILE_MODE3);
|
|
21369
22162
|
}
|
|
21370
22163
|
async function loadPeersConfig(peersPath) {
|
|
@@ -21377,7 +22170,7 @@ async function loadPeersConfig(peersPath) {
|
|
|
21377
22170
|
}
|
|
21378
22171
|
throw error48;
|
|
21379
22172
|
}
|
|
21380
|
-
if (!
|
|
22173
|
+
if (!isRecord9(parsed)) {
|
|
21381
22174
|
throw createCliError6(
|
|
21382
22175
|
"CLI_OPENCLAW_INVALID_PEERS_CONFIG",
|
|
21383
22176
|
"Peer config root must be a JSON object",
|
|
@@ -21388,7 +22181,7 @@ async function loadPeersConfig(peersPath) {
|
|
|
21388
22181
|
if (peersValue === void 0) {
|
|
21389
22182
|
return { peers: {} };
|
|
21390
22183
|
}
|
|
21391
|
-
if (!
|
|
22184
|
+
if (!isRecord9(peersValue)) {
|
|
21392
22185
|
throw createCliError6(
|
|
21393
22186
|
"CLI_OPENCLAW_INVALID_PEERS_CONFIG",
|
|
21394
22187
|
"Peer config peers field must be an object",
|
|
@@ -21398,7 +22191,7 @@ async function loadPeersConfig(peersPath) {
|
|
|
21398
22191
|
const peers = {};
|
|
21399
22192
|
for (const [alias, value] of Object.entries(peersValue)) {
|
|
21400
22193
|
const normalizedAlias = parsePeerAlias(alias);
|
|
21401
|
-
if (!
|
|
22194
|
+
if (!isRecord9(value)) {
|
|
21402
22195
|
throw createCliError6(
|
|
21403
22196
|
"CLI_OPENCLAW_INVALID_PEERS_CONFIG",
|
|
21404
22197
|
"Peer entry must be an object",
|
|
@@ -21429,7 +22222,7 @@ function parseConnectorBaseUrlForAssignment(value, label) {
|
|
|
21429
22222
|
});
|
|
21430
22223
|
}
|
|
21431
22224
|
function parseConnectorAssignments(value, connectorAssignmentsPath) {
|
|
21432
|
-
if (!
|
|
22225
|
+
if (!isRecord9(value)) {
|
|
21433
22226
|
throw createCliError6(
|
|
21434
22227
|
"CLI_OPENCLAW_INVALID_CONNECTOR_ASSIGNMENTS",
|
|
21435
22228
|
"Connector assignments config must be an object",
|
|
@@ -21437,12 +22230,12 @@ function parseConnectorAssignments(value, connectorAssignmentsPath) {
|
|
|
21437
22230
|
);
|
|
21438
22231
|
}
|
|
21439
22232
|
const agentsRaw = value.agents;
|
|
21440
|
-
if (!
|
|
22233
|
+
if (!isRecord9(agentsRaw)) {
|
|
21441
22234
|
return { agents: {} };
|
|
21442
22235
|
}
|
|
21443
22236
|
const agents = {};
|
|
21444
22237
|
for (const [agentName, entryValue] of Object.entries(agentsRaw)) {
|
|
21445
|
-
if (!
|
|
22238
|
+
if (!isRecord9(entryValue)) {
|
|
21446
22239
|
throw createCliError6(
|
|
21447
22240
|
"CLI_OPENCLAW_INVALID_CONNECTOR_ASSIGNMENTS",
|
|
21448
22241
|
"Connector assignment entry must be an object",
|
|
@@ -21547,14 +22340,28 @@ function resolveConnectorStatusUrl(connectorBaseUrl) {
|
|
|
21547
22340
|
).toString();
|
|
21548
22341
|
}
|
|
21549
22342
|
function parseConnectorStatusPayload(payload) {
|
|
21550
|
-
if (!
|
|
22343
|
+
if (!isRecord9(payload) || typeof payload.websocketConnected !== "boolean") {
|
|
21551
22344
|
throw createCliError6(
|
|
21552
22345
|
"CLI_OPENCLAW_SETUP_CONNECTOR_STATUS_INVALID",
|
|
21553
22346
|
"Connector status response is invalid"
|
|
21554
22347
|
);
|
|
21555
22348
|
}
|
|
21556
22349
|
return {
|
|
21557
|
-
websocketConnected: payload.websocketConnected
|
|
22350
|
+
websocketConnected: payload.websocketConnected,
|
|
22351
|
+
inboundInbox: isRecord9(payload.inboundInbox) ? {
|
|
22352
|
+
pendingCount: typeof payload.inboundInbox.pendingCount === "number" ? payload.inboundInbox.pendingCount : void 0,
|
|
22353
|
+
pendingBytes: typeof payload.inboundInbox.pendingBytes === "number" ? payload.inboundInbox.pendingBytes : void 0,
|
|
22354
|
+
oldestPendingAt: typeof payload.inboundInbox.oldestPendingAt === "string" ? payload.inboundInbox.oldestPendingAt : void 0,
|
|
22355
|
+
nextAttemptAt: typeof payload.inboundInbox.nextAttemptAt === "string" ? payload.inboundInbox.nextAttemptAt : void 0,
|
|
22356
|
+
lastReplayAt: typeof payload.inboundInbox.lastReplayAt === "string" ? payload.inboundInbox.lastReplayAt : void 0,
|
|
22357
|
+
lastReplayError: typeof payload.inboundInbox.lastReplayError === "string" ? payload.inboundInbox.lastReplayError : void 0,
|
|
22358
|
+
replayerActive: typeof payload.inboundInbox.replayerActive === "boolean" ? payload.inboundInbox.replayerActive : void 0
|
|
22359
|
+
} : void 0,
|
|
22360
|
+
openclawHook: isRecord9(payload.openclawHook) ? {
|
|
22361
|
+
url: typeof payload.openclawHook.url === "string" ? payload.openclawHook.url : void 0,
|
|
22362
|
+
lastAttemptAt: typeof payload.openclawHook.lastAttemptAt === "string" ? payload.openclawHook.lastAttemptAt : void 0,
|
|
22363
|
+
lastAttemptStatus: payload.openclawHook.lastAttemptStatus === "ok" || payload.openclawHook.lastAttemptStatus === "failed" ? payload.openclawHook.lastAttemptStatus : void 0
|
|
22364
|
+
} : void 0
|
|
21558
22365
|
};
|
|
21559
22366
|
}
|
|
21560
22367
|
async function fetchConnectorHealthStatus(input) {
|
|
@@ -21588,6 +22395,8 @@ async function fetchConnectorHealthStatus(input) {
|
|
|
21588
22395
|
const parsed = parseConnectorStatusPayload(payload);
|
|
21589
22396
|
return {
|
|
21590
22397
|
connected: parsed.websocketConnected,
|
|
22398
|
+
inboundInbox: parsed.inboundInbox,
|
|
22399
|
+
openclawHook: parsed.openclawHook,
|
|
21591
22400
|
reachable: true,
|
|
21592
22401
|
statusUrl,
|
|
21593
22402
|
reason: parsed.websocketConnected ? void 0 : "connector websocket is disconnected"
|
|
@@ -21630,14 +22439,14 @@ async function waitForConnectorConnected(input) {
|
|
|
21630
22439
|
return latest;
|
|
21631
22440
|
}
|
|
21632
22441
|
function resolveConnectorRunDir(homeDir) {
|
|
21633
|
-
return
|
|
22442
|
+
return join7(homeDir, CLAWDENTITY_DIR_NAME, CONNECTOR_RUN_DIR_NAME);
|
|
21634
22443
|
}
|
|
21635
22444
|
function resolveConnectorPidPath(homeDir, agentName) {
|
|
21636
|
-
return
|
|
22445
|
+
return join7(resolveConnectorRunDir(homeDir), `connector-${agentName}.pid`);
|
|
21637
22446
|
}
|
|
21638
22447
|
async function readConnectorPidFile(pidPath) {
|
|
21639
22448
|
try {
|
|
21640
|
-
const raw = (await
|
|
22449
|
+
const raw = (await readFile6(pidPath, "utf8")).trim();
|
|
21641
22450
|
if (raw.length === 0) {
|
|
21642
22451
|
return void 0;
|
|
21643
22452
|
}
|
|
@@ -21678,7 +22487,7 @@ function resolveCliEntryPathForDetachedStart() {
|
|
|
21678
22487
|
return argvEntry;
|
|
21679
22488
|
}
|
|
21680
22489
|
const modulePath = fileURLToPath2(import.meta.url);
|
|
21681
|
-
return
|
|
22490
|
+
return join7(dirname5(modulePath), "..", "bin.js");
|
|
21682
22491
|
}
|
|
21683
22492
|
async function startDetachedConnectorRuntime(input) {
|
|
21684
22493
|
await stopDetachedConnectorIfRunning({
|
|
@@ -21686,7 +22495,7 @@ async function startDetachedConnectorRuntime(input) {
|
|
|
21686
22495
|
agentName: input.agentName
|
|
21687
22496
|
});
|
|
21688
22497
|
const runDir = resolveConnectorRunDir(input.homeDir);
|
|
21689
|
-
await
|
|
22498
|
+
await mkdir6(runDir, { recursive: true });
|
|
21690
22499
|
const cliEntryPath = resolveCliEntryPathForDetachedStart();
|
|
21691
22500
|
const args = [
|
|
21692
22501
|
cliEntryPath,
|
|
@@ -21764,7 +22573,7 @@ async function startSetupConnectorRuntime(input) {
|
|
|
21764
22573
|
};
|
|
21765
22574
|
}
|
|
21766
22575
|
function parseRelayRuntimeConfig(value, relayRuntimeConfigPath) {
|
|
21767
|
-
if (!
|
|
22576
|
+
if (!isRecord9(value)) {
|
|
21768
22577
|
throw createCliError6(
|
|
21769
22578
|
"CLI_OPENCLAW_INVALID_RELAY_RUNTIME_CONFIG",
|
|
21770
22579
|
"Relay runtime config must be an object",
|
|
@@ -21844,7 +22653,7 @@ function normalizeStringArrayWithValues(value, requiredValues) {
|
|
|
21844
22653
|
return Array.from(normalized);
|
|
21845
22654
|
}
|
|
21846
22655
|
function resolveHookDefaultSessionKey(config2, hooks) {
|
|
21847
|
-
const session =
|
|
22656
|
+
const session = isRecord9(config2.session) ? config2.session : {};
|
|
21848
22657
|
const scope = typeof session.scope === "string" ? session.scope.trim().toLowerCase() : "";
|
|
21849
22658
|
const configuredMainSessionKey = resolveConfiguredOpenclawMainSessionKey(session);
|
|
21850
22659
|
if (typeof hooks.defaultSessionKey === "string" && hooks.defaultSessionKey.trim().length > 0) {
|
|
@@ -21883,20 +22692,20 @@ function generateOpenclawHookToken() {
|
|
|
21883
22692
|
return randomBytes3(OPENCLAW_HOOK_TOKEN_BYTES).toString("hex");
|
|
21884
22693
|
}
|
|
21885
22694
|
function upsertRelayHookMapping(mappingsValue) {
|
|
21886
|
-
const mappings = Array.isArray(mappingsValue) ? mappingsValue.filter(
|
|
22695
|
+
const mappings = Array.isArray(mappingsValue) ? mappingsValue.filter(isRecord9).map((mapping) => ({ ...mapping })) : [];
|
|
21887
22696
|
const existingIndex = mappings.findIndex((mapping) => {
|
|
21888
22697
|
if (mapping.id === HOOK_MAPPING_ID) {
|
|
21889
22698
|
return true;
|
|
21890
22699
|
}
|
|
21891
|
-
if (!
|
|
22700
|
+
if (!isRecord9(mapping.match)) {
|
|
21892
22701
|
return false;
|
|
21893
22702
|
}
|
|
21894
22703
|
return mapping.match.path === HOOK_PATH_SEND_TO_PEER;
|
|
21895
22704
|
});
|
|
21896
|
-
const baseMapping = existingIndex >= 0 &&
|
|
21897
|
-
const nextMatch =
|
|
22705
|
+
const baseMapping = existingIndex >= 0 && isRecord9(mappings[existingIndex]) ? mappings[existingIndex] : {};
|
|
22706
|
+
const nextMatch = isRecord9(baseMapping.match) ? { ...baseMapping.match } : {};
|
|
21898
22707
|
nextMatch.path = HOOK_PATH_SEND_TO_PEER;
|
|
21899
|
-
const nextTransform =
|
|
22708
|
+
const nextTransform = isRecord9(baseMapping.transform) ? { ...baseMapping.transform } : {};
|
|
21900
22709
|
nextTransform.module = RELAY_MODULE_FILE_NAME;
|
|
21901
22710
|
const relayMapping = {
|
|
21902
22711
|
...baseMapping,
|
|
@@ -21927,14 +22736,14 @@ async function patchOpenclawConfig(openclawConfigPath, hookToken) {
|
|
|
21927
22736
|
}
|
|
21928
22737
|
throw error48;
|
|
21929
22738
|
}
|
|
21930
|
-
if (!
|
|
22739
|
+
if (!isRecord9(config2)) {
|
|
21931
22740
|
throw createCliError6(
|
|
21932
22741
|
"CLI_OPENCLAW_INVALID_CONFIG",
|
|
21933
22742
|
"OpenClaw config root must be an object",
|
|
21934
22743
|
{ openclawConfigPath }
|
|
21935
22744
|
);
|
|
21936
22745
|
}
|
|
21937
|
-
const hooks =
|
|
22746
|
+
const hooks = isRecord9(config2.hooks) ? { ...config2.hooks } : {};
|
|
21938
22747
|
const existingHookToken = typeof hooks.token === "string" && hooks.token.trim().length > 0 ? hooks.token.trim() : void 0;
|
|
21939
22748
|
const preferredHookToken = typeof hookToken === "string" && hookToken.trim().length > 0 ? hookToken.trim() : void 0;
|
|
21940
22749
|
const resolvedHookToken = existingHookToken ?? preferredHookToken ?? generateOpenclawHookToken();
|
|
@@ -21952,7 +22761,7 @@ async function patchOpenclawConfig(openclawConfigPath, hookToken) {
|
|
|
21952
22761
|
...config2,
|
|
21953
22762
|
hooks
|
|
21954
22763
|
};
|
|
21955
|
-
await
|
|
22764
|
+
await writeFile6(
|
|
21956
22765
|
openclawConfigPath,
|
|
21957
22766
|
`${JSON.stringify(nextConfig, null, 2)}
|
|
21958
22767
|
`,
|
|
@@ -21973,10 +22782,10 @@ function toDoctorResult(checks) {
|
|
|
21973
22782
|
};
|
|
21974
22783
|
}
|
|
21975
22784
|
function isRelayHookMapping(value) {
|
|
21976
|
-
if (!
|
|
22785
|
+
if (!isRecord9(value)) {
|
|
21977
22786
|
return false;
|
|
21978
22787
|
}
|
|
21979
|
-
if (!
|
|
22788
|
+
if (!isRecord9(value.match) || value.match.path !== HOOK_PATH_SEND_TO_PEER) {
|
|
21980
22789
|
return false;
|
|
21981
22790
|
}
|
|
21982
22791
|
if (typeof value.id === "string" && value.id !== HOOK_MAPPING_ID) {
|
|
@@ -21985,7 +22794,7 @@ function isRelayHookMapping(value) {
|
|
|
21985
22794
|
return true;
|
|
21986
22795
|
}
|
|
21987
22796
|
function hasRelayTransformModule(value) {
|
|
21988
|
-
if (!
|
|
22797
|
+
if (!isRecord9(value) || !isRecord9(value.transform)) {
|
|
21989
22798
|
return false;
|
|
21990
22799
|
}
|
|
21991
22800
|
return value.transform.module === RELAY_MODULE_FILE_NAME;
|
|
@@ -22164,7 +22973,7 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22164
22973
|
const selectedAgentPath = resolveOpenclawAgentNamePath(homeDir);
|
|
22165
22974
|
let selectedAgentName;
|
|
22166
22975
|
try {
|
|
22167
|
-
const selectedAgentRaw = await
|
|
22976
|
+
const selectedAgentRaw = await readFile6(selectedAgentPath, "utf8");
|
|
22168
22977
|
selectedAgentName = assertValidAgentName(selectedAgentRaw.trim());
|
|
22169
22978
|
checks.push(
|
|
22170
22979
|
toDoctorCheck({
|
|
@@ -22287,9 +23096,9 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22287
23096
|
const relayTransformRuntimePath = resolveTransformRuntimePath(openclawDir);
|
|
22288
23097
|
const relayTransformPeersPath = resolveTransformPeersPath(openclawDir);
|
|
22289
23098
|
try {
|
|
22290
|
-
const transformContents = await
|
|
22291
|
-
const runtimeContents = await
|
|
22292
|
-
const peersSnapshotContents = await
|
|
23099
|
+
const transformContents = await readFile6(transformTargetPath, "utf8");
|
|
23100
|
+
const runtimeContents = await readFile6(relayTransformRuntimePath, "utf8");
|
|
23101
|
+
const peersSnapshotContents = await readFile6(
|
|
22293
23102
|
relayTransformPeersPath,
|
|
22294
23103
|
"utf8"
|
|
22295
23104
|
);
|
|
@@ -22342,10 +23151,10 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22342
23151
|
const openclawConfigPath = resolveOpenclawConfigPath(openclawDir, homeDir);
|
|
22343
23152
|
try {
|
|
22344
23153
|
const openclawConfig = await readJsonFile(openclawConfigPath);
|
|
22345
|
-
if (!
|
|
23154
|
+
if (!isRecord9(openclawConfig)) {
|
|
22346
23155
|
throw new Error("root");
|
|
22347
23156
|
}
|
|
22348
|
-
const hooks =
|
|
23157
|
+
const hooks = isRecord9(openclawConfig.hooks) ? openclawConfig.hooks : {};
|
|
22349
23158
|
const hooksEnabled = hooks.enabled === true;
|
|
22350
23159
|
const hookToken = typeof hooks.token === "string" && hooks.token.trim().length > 0 ? hooks.token.trim() : void 0;
|
|
22351
23160
|
const defaultSessionKey = typeof hooks.defaultSessionKey === "string" && hooks.defaultSessionKey.trim().length > 0 ? hooks.defaultSessionKey.trim() : void 0;
|
|
@@ -22357,7 +23166,7 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22357
23166
|
const missingRequiredSessionPrefixes = defaultSessionKey === void 0 ? ["hook:"] : ["hook:", defaultSessionKey].filter(
|
|
22358
23167
|
(prefix) => !allowedSessionKeyPrefixes.includes(prefix)
|
|
22359
23168
|
);
|
|
22360
|
-
const mappings = Array.isArray(hooks.mappings) ? hooks.mappings.filter(
|
|
23169
|
+
const mappings = Array.isArray(hooks.mappings) ? hooks.mappings.filter(isRecord9) : [];
|
|
22361
23170
|
const relayMapping = mappings.find(
|
|
22362
23171
|
(mapping) => isRelayHookMapping(mapping)
|
|
22363
23172
|
);
|
|
@@ -22511,81 +23320,99 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22511
23320
|
})
|
|
22512
23321
|
);
|
|
22513
23322
|
}
|
|
22514
|
-
const
|
|
22515
|
-
|
|
22516
|
-
|
|
22517
|
-
|
|
23323
|
+
const gatewayPendingState = await readOpenclawGatewayPendingState(openclawDir);
|
|
23324
|
+
if (gatewayPendingState.status === "missing") {
|
|
23325
|
+
checks.push(
|
|
23326
|
+
toDoctorCheck({
|
|
23327
|
+
id: "state.gatewayDevicePairing",
|
|
23328
|
+
label: "OpenClaw gateway device pairing",
|
|
23329
|
+
status: "pass",
|
|
23330
|
+
message: "no pending gateway device approvals file was found",
|
|
23331
|
+
details: {
|
|
23332
|
+
gatewayDevicePendingPath: gatewayPendingState.gatewayDevicePendingPath
|
|
23333
|
+
}
|
|
23334
|
+
})
|
|
23335
|
+
);
|
|
23336
|
+
} else if (gatewayPendingState.status === "invalid") {
|
|
23337
|
+
checks.push(
|
|
23338
|
+
toDoctorCheck({
|
|
23339
|
+
id: "state.gatewayDevicePairing",
|
|
23340
|
+
label: "OpenClaw gateway device pairing",
|
|
23341
|
+
status: "fail",
|
|
23342
|
+
message: `invalid pending device approvals file: ${gatewayPendingState.gatewayDevicePendingPath}`,
|
|
23343
|
+
remediationHint: OPENCLAW_DEVICE_APPROVAL_RECOVERY_HINT,
|
|
23344
|
+
details: {
|
|
23345
|
+
gatewayDevicePendingPath: gatewayPendingState.gatewayDevicePendingPath
|
|
23346
|
+
}
|
|
23347
|
+
})
|
|
23348
|
+
);
|
|
23349
|
+
} else if (gatewayPendingState.status === "unreadable") {
|
|
23350
|
+
checks.push(
|
|
23351
|
+
toDoctorCheck({
|
|
23352
|
+
id: "state.gatewayDevicePairing",
|
|
23353
|
+
label: "OpenClaw gateway device pairing",
|
|
23354
|
+
status: "fail",
|
|
23355
|
+
message: `unable to read pending device approvals at ${gatewayPendingState.gatewayDevicePendingPath}`,
|
|
23356
|
+
remediationHint: OPENCLAW_DEVICE_APPROVAL_RECOVERY_HINT,
|
|
23357
|
+
details: {
|
|
23358
|
+
gatewayDevicePendingPath: gatewayPendingState.gatewayDevicePendingPath
|
|
23359
|
+
}
|
|
23360
|
+
})
|
|
23361
|
+
);
|
|
23362
|
+
} else if (gatewayPendingState.pendingRequestIds.length === 0) {
|
|
23363
|
+
checks.push(
|
|
23364
|
+
toDoctorCheck({
|
|
23365
|
+
id: "state.gatewayDevicePairing",
|
|
23366
|
+
label: "OpenClaw gateway device pairing",
|
|
23367
|
+
status: "pass",
|
|
23368
|
+
message: "no pending gateway device approvals",
|
|
23369
|
+
details: {
|
|
23370
|
+
gatewayDevicePendingPath: gatewayPendingState.gatewayDevicePendingPath
|
|
23371
|
+
}
|
|
23372
|
+
})
|
|
23373
|
+
);
|
|
23374
|
+
} else {
|
|
23375
|
+
checks.push(
|
|
23376
|
+
toDoctorCheck({
|
|
23377
|
+
id: "state.gatewayDevicePairing",
|
|
23378
|
+
label: "OpenClaw gateway device pairing",
|
|
23379
|
+
status: "fail",
|
|
23380
|
+
message: `pending gateway device approvals: ${gatewayPendingState.pendingRequestIds.length}`,
|
|
23381
|
+
remediationHint: OPENCLAW_DEVICE_APPROVAL_RECOVERY_HINT,
|
|
23382
|
+
details: {
|
|
23383
|
+
gatewayDevicePendingPath: gatewayPendingState.gatewayDevicePendingPath,
|
|
23384
|
+
pendingRequestIds: gatewayPendingState.pendingRequestIds
|
|
23385
|
+
}
|
|
23386
|
+
})
|
|
23387
|
+
);
|
|
23388
|
+
}
|
|
23389
|
+
if (options.includeConnectorRuntimeCheck !== false) {
|
|
23390
|
+
if (selectedAgentName === void 0) {
|
|
22518
23391
|
checks.push(
|
|
22519
23392
|
toDoctorCheck({
|
|
22520
|
-
id: "state.
|
|
22521
|
-
label: "
|
|
23393
|
+
id: "state.connectorRuntime",
|
|
23394
|
+
label: "Connector runtime",
|
|
22522
23395
|
status: "fail",
|
|
22523
|
-
message:
|
|
22524
|
-
remediationHint:
|
|
22525
|
-
details: { gatewayDevicePendingPath }
|
|
22526
|
-
})
|
|
22527
|
-
);
|
|
22528
|
-
} else {
|
|
22529
|
-
const pendingRequestIds = Object.keys(pendingPayload);
|
|
22530
|
-
if (pendingRequestIds.length === 0) {
|
|
22531
|
-
checks.push(
|
|
22532
|
-
toDoctorCheck({
|
|
22533
|
-
id: "state.gatewayDevicePairing",
|
|
22534
|
-
label: "OpenClaw gateway device pairing",
|
|
22535
|
-
status: "pass",
|
|
22536
|
-
message: "no pending gateway device approvals",
|
|
22537
|
-
details: { gatewayDevicePendingPath }
|
|
22538
|
-
})
|
|
22539
|
-
);
|
|
22540
|
-
} else {
|
|
22541
|
-
checks.push(
|
|
22542
|
-
toDoctorCheck({
|
|
22543
|
-
id: "state.gatewayDevicePairing",
|
|
22544
|
-
label: "OpenClaw gateway device pairing",
|
|
22545
|
-
status: "fail",
|
|
22546
|
-
message: `pending gateway device approvals: ${pendingRequestIds.length}`,
|
|
22547
|
-
remediationHint: OPENCLAW_DEVICE_APPROVAL_COMMAND_HINT,
|
|
22548
|
-
details: {
|
|
22549
|
-
gatewayDevicePendingPath,
|
|
22550
|
-
pendingRequestIds
|
|
22551
|
-
}
|
|
22552
|
-
})
|
|
22553
|
-
);
|
|
22554
|
-
}
|
|
22555
|
-
}
|
|
22556
|
-
} catch (error48) {
|
|
22557
|
-
if (getErrorCode2(error48) === "ENOENT") {
|
|
22558
|
-
checks.push(
|
|
22559
|
-
toDoctorCheck({
|
|
22560
|
-
id: "state.gatewayDevicePairing",
|
|
22561
|
-
label: "OpenClaw gateway device pairing",
|
|
22562
|
-
status: "pass",
|
|
22563
|
-
message: "no pending gateway device approvals file was found",
|
|
22564
|
-
details: { gatewayDevicePendingPath }
|
|
23396
|
+
message: "cannot validate connector runtime without selected agent marker",
|
|
23397
|
+
remediationHint: OPENCLAW_SETUP_COMMAND_HINT
|
|
22565
23398
|
})
|
|
22566
23399
|
);
|
|
22567
|
-
} else {
|
|
22568
23400
|
checks.push(
|
|
22569
23401
|
toDoctorCheck({
|
|
22570
|
-
id: "state.
|
|
22571
|
-
label: "
|
|
23402
|
+
id: "state.connectorInboundInbox",
|
|
23403
|
+
label: "Connector inbound inbox",
|
|
22572
23404
|
status: "fail",
|
|
22573
|
-
message:
|
|
22574
|
-
remediationHint:
|
|
22575
|
-
details: { gatewayDevicePendingPath }
|
|
23405
|
+
message: "cannot validate connector inbound inbox without selected agent marker",
|
|
23406
|
+
remediationHint: OPENCLAW_SETUP_COMMAND_HINT
|
|
22576
23407
|
})
|
|
22577
23408
|
);
|
|
22578
|
-
}
|
|
22579
|
-
}
|
|
22580
|
-
if (options.includeConnectorRuntimeCheck !== false) {
|
|
22581
|
-
if (selectedAgentName === void 0) {
|
|
22582
23409
|
checks.push(
|
|
22583
23410
|
toDoctorCheck({
|
|
22584
|
-
id: "state.
|
|
22585
|
-
label: "
|
|
23411
|
+
id: "state.openclawHookHealth",
|
|
23412
|
+
label: "OpenClaw hook health",
|
|
22586
23413
|
status: "fail",
|
|
22587
|
-
message: "cannot validate
|
|
22588
|
-
remediationHint:
|
|
23414
|
+
message: "cannot validate OpenClaw hook health without selected agent marker",
|
|
23415
|
+
remediationHint: OPENCLAW_SETUP_RESTART_COMMAND_HINT
|
|
22589
23416
|
})
|
|
22590
23417
|
);
|
|
22591
23418
|
} else {
|
|
@@ -22606,6 +23433,26 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22606
23433
|
details: { connectorAssignmentsPath, selectedAgentName }
|
|
22607
23434
|
})
|
|
22608
23435
|
);
|
|
23436
|
+
checks.push(
|
|
23437
|
+
toDoctorCheck({
|
|
23438
|
+
id: "state.connectorInboundInbox",
|
|
23439
|
+
label: "Connector inbound inbox",
|
|
23440
|
+
status: "fail",
|
|
23441
|
+
message: `no connector assignment found for ${selectedAgentName}`,
|
|
23442
|
+
remediationHint: OPENCLAW_SETUP_COMMAND_HINT,
|
|
23443
|
+
details: { connectorAssignmentsPath, selectedAgentName }
|
|
23444
|
+
})
|
|
23445
|
+
);
|
|
23446
|
+
checks.push(
|
|
23447
|
+
toDoctorCheck({
|
|
23448
|
+
id: "state.openclawHookHealth",
|
|
23449
|
+
label: "OpenClaw hook health",
|
|
23450
|
+
status: "fail",
|
|
23451
|
+
message: `no connector assignment found for ${selectedAgentName}`,
|
|
23452
|
+
remediationHint: OPENCLAW_SETUP_RESTART_COMMAND_HINT,
|
|
23453
|
+
details: { connectorAssignmentsPath, selectedAgentName }
|
|
23454
|
+
})
|
|
23455
|
+
);
|
|
22609
23456
|
} else {
|
|
22610
23457
|
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
22611
23458
|
if (typeof fetchImpl !== "function") {
|
|
@@ -22618,6 +23465,24 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22618
23465
|
remediationHint: "Run doctor in a Node runtime with fetch support, or rerun openclaw setup"
|
|
22619
23466
|
})
|
|
22620
23467
|
);
|
|
23468
|
+
checks.push(
|
|
23469
|
+
toDoctorCheck({
|
|
23470
|
+
id: "state.connectorInboundInbox",
|
|
23471
|
+
label: "Connector inbound inbox",
|
|
23472
|
+
status: "fail",
|
|
23473
|
+
message: "fetch implementation is unavailable for connector inbox checks",
|
|
23474
|
+
remediationHint: OPENCLAW_SETUP_COMMAND_HINT
|
|
23475
|
+
})
|
|
23476
|
+
);
|
|
23477
|
+
checks.push(
|
|
23478
|
+
toDoctorCheck({
|
|
23479
|
+
id: "state.openclawHookHealth",
|
|
23480
|
+
label: "OpenClaw hook health",
|
|
23481
|
+
status: "fail",
|
|
23482
|
+
message: "fetch implementation is unavailable for OpenClaw hook health checks",
|
|
23483
|
+
remediationHint: OPENCLAW_SETUP_RESTART_COMMAND_HINT
|
|
23484
|
+
})
|
|
23485
|
+
);
|
|
22621
23486
|
} else {
|
|
22622
23487
|
const connectorStatus = await fetchConnectorHealthStatus({
|
|
22623
23488
|
connectorBaseUrl: assignment.connectorBaseUrl,
|
|
@@ -22636,6 +23501,36 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22636
23501
|
}
|
|
22637
23502
|
})
|
|
22638
23503
|
);
|
|
23504
|
+
const inboxPendingCount = connectorStatus.inboundInbox?.pendingCount ?? 0;
|
|
23505
|
+
const replayError = connectorStatus.inboundInbox?.lastReplayError;
|
|
23506
|
+
checks.push(
|
|
23507
|
+
toDoctorCheck({
|
|
23508
|
+
id: "state.connectorInboundInbox",
|
|
23509
|
+
label: "Connector inbound inbox",
|
|
23510
|
+
status: "pass",
|
|
23511
|
+
message: inboxPendingCount === 0 ? "connector inbound inbox is empty" : `connector inbound inbox has ${inboxPendingCount} pending message(s)`,
|
|
23512
|
+
details: {
|
|
23513
|
+
connectorStatusUrl: connectorStatus.statusUrl,
|
|
23514
|
+
connectorBaseUrl: assignment.connectorBaseUrl,
|
|
23515
|
+
...connectorStatus.inboundInbox
|
|
23516
|
+
}
|
|
23517
|
+
})
|
|
23518
|
+
);
|
|
23519
|
+
checks.push(
|
|
23520
|
+
toDoctorCheck({
|
|
23521
|
+
id: "state.openclawHookHealth",
|
|
23522
|
+
label: "OpenClaw hook health",
|
|
23523
|
+
status: connectorStatus.openclawHook?.lastAttemptStatus === "failed" && inboxPendingCount > 0 ? "fail" : "pass",
|
|
23524
|
+
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",
|
|
23525
|
+
remediationHint: connectorStatus.openclawHook?.lastAttemptStatus === "failed" && inboxPendingCount > 0 ? OPENCLAW_SETUP_RESTART_COMMAND_HINT : void 0,
|
|
23526
|
+
details: {
|
|
23527
|
+
connectorStatusUrl: connectorStatus.statusUrl,
|
|
23528
|
+
connectorBaseUrl: assignment.connectorBaseUrl,
|
|
23529
|
+
...connectorStatus.openclawHook,
|
|
23530
|
+
inboxPendingCount
|
|
23531
|
+
}
|
|
23532
|
+
})
|
|
23533
|
+
);
|
|
22639
23534
|
} else {
|
|
22640
23535
|
const reason = connectorStatus.reason ?? "connector runtime is unavailable";
|
|
22641
23536
|
checks.push(
|
|
@@ -22651,6 +23546,32 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22651
23546
|
}
|
|
22652
23547
|
})
|
|
22653
23548
|
);
|
|
23549
|
+
checks.push(
|
|
23550
|
+
toDoctorCheck({
|
|
23551
|
+
id: "state.connectorInboundInbox",
|
|
23552
|
+
label: "Connector inbound inbox",
|
|
23553
|
+
status: "fail",
|
|
23554
|
+
message: `unable to read connector inbound inbox status: ${reason}`,
|
|
23555
|
+
remediationHint: OPENCLAW_SETUP_COMMAND_HINT,
|
|
23556
|
+
details: {
|
|
23557
|
+
connectorStatusUrl: connectorStatus.statusUrl,
|
|
23558
|
+
connectorBaseUrl: assignment.connectorBaseUrl
|
|
23559
|
+
}
|
|
23560
|
+
})
|
|
23561
|
+
);
|
|
23562
|
+
checks.push(
|
|
23563
|
+
toDoctorCheck({
|
|
23564
|
+
id: "state.openclawHookHealth",
|
|
23565
|
+
label: "OpenClaw hook health",
|
|
23566
|
+
status: "fail",
|
|
23567
|
+
message: `unable to verify OpenClaw hook health: ${reason}`,
|
|
23568
|
+
remediationHint: OPENCLAW_SETUP_RESTART_COMMAND_HINT,
|
|
23569
|
+
details: {
|
|
23570
|
+
connectorStatusUrl: connectorStatus.statusUrl,
|
|
23571
|
+
connectorBaseUrl: assignment.connectorBaseUrl
|
|
23572
|
+
}
|
|
23573
|
+
})
|
|
23574
|
+
);
|
|
22654
23575
|
}
|
|
22655
23576
|
}
|
|
22656
23577
|
}
|
|
@@ -22665,6 +23586,24 @@ async function runOpenclawDoctor(options = {}) {
|
|
|
22665
23586
|
details: { connectorAssignmentsPath }
|
|
22666
23587
|
})
|
|
22667
23588
|
);
|
|
23589
|
+
checks.push(
|
|
23590
|
+
toDoctorCheck({
|
|
23591
|
+
id: "state.connectorInboundInbox",
|
|
23592
|
+
label: "Connector inbound inbox",
|
|
23593
|
+
status: "fail",
|
|
23594
|
+
message: "cannot validate connector inbound inbox without connector assignment",
|
|
23595
|
+
remediationHint: OPENCLAW_SETUP_COMMAND_HINT
|
|
23596
|
+
})
|
|
23597
|
+
);
|
|
23598
|
+
checks.push(
|
|
23599
|
+
toDoctorCheck({
|
|
23600
|
+
id: "state.openclawHookHealth",
|
|
23601
|
+
label: "OpenClaw hook health",
|
|
23602
|
+
status: "fail",
|
|
23603
|
+
message: "cannot validate OpenClaw hook health without connector assignment",
|
|
23604
|
+
remediationHint: OPENCLAW_SETUP_RESTART_COMMAND_HINT
|
|
23605
|
+
})
|
|
23606
|
+
);
|
|
22668
23607
|
}
|
|
22669
23608
|
}
|
|
22670
23609
|
}
|
|
@@ -22692,7 +23631,7 @@ function parseRelayProbeFailure(input) {
|
|
|
22692
23631
|
if (input.status === 500) {
|
|
22693
23632
|
return {
|
|
22694
23633
|
message: "Relay probe failed inside local relay pipeline",
|
|
22695
|
-
remediationHint: "Check
|
|
23634
|
+
remediationHint: "Check peer pairing and rerun: clawdentity openclaw setup <agentName>"
|
|
22696
23635
|
};
|
|
22697
23636
|
}
|
|
22698
23637
|
return {
|
|
@@ -22870,7 +23809,7 @@ async function setupOpenclawRelay(agentName, options) {
|
|
|
22870
23809
|
relayRuntimeConfigPath
|
|
22871
23810
|
});
|
|
22872
23811
|
await ensureLocalAgentCredentials(homeDir, normalizedAgentName);
|
|
22873
|
-
await
|
|
23812
|
+
await mkdir6(dirname5(transformTargetPath), { recursive: true });
|
|
22874
23813
|
try {
|
|
22875
23814
|
await copyFile(transformSource, transformTargetPath);
|
|
22876
23815
|
} catch (error48) {
|
|
@@ -22963,7 +23902,7 @@ async function setupOpenclawRelay(agentName, options) {
|
|
|
22963
23902
|
};
|
|
22964
23903
|
}
|
|
22965
23904
|
async function assertSetupChecklistHealthy(input) {
|
|
22966
|
-
|
|
23905
|
+
let checklist = await runOpenclawDoctor({
|
|
22967
23906
|
homeDir: input.homeDir,
|
|
22968
23907
|
openclawDir: input.openclawDir,
|
|
22969
23908
|
includeConfigCheck: false,
|
|
@@ -22972,14 +23911,56 @@ async function assertSetupChecklistHealthy(input) {
|
|
|
22972
23911
|
if (checklist.status === "healthy") {
|
|
22973
23912
|
return;
|
|
22974
23913
|
}
|
|
22975
|
-
|
|
23914
|
+
let gatewayApprovalSummary;
|
|
23915
|
+
const gatewayPairingFailure = checklist.checks.find(
|
|
23916
|
+
(check2) => check2.id === "state.gatewayDevicePairing" && check2.status === "fail"
|
|
23917
|
+
);
|
|
23918
|
+
if (gatewayPairingFailure !== void 0) {
|
|
23919
|
+
gatewayApprovalSummary = await autoApproveOpenclawGatewayDevices({
|
|
23920
|
+
homeDir: input.homeDir,
|
|
23921
|
+
openclawDir: input.openclawDir,
|
|
23922
|
+
runner: input.gatewayDeviceApprovalRunner
|
|
23923
|
+
});
|
|
23924
|
+
if (gatewayApprovalSummary !== void 0) {
|
|
23925
|
+
const successfulAttempts = gatewayApprovalSummary.attempts.filter(
|
|
23926
|
+
(attempt) => attempt.ok
|
|
23927
|
+
).length;
|
|
23928
|
+
const failedAttempts = gatewayApprovalSummary.attempts.filter(
|
|
23929
|
+
(attempt) => !attempt.ok
|
|
23930
|
+
);
|
|
23931
|
+
logger8.info("cli.openclaw_setup_gateway_device_recovery_attempted", {
|
|
23932
|
+
openclawDir: input.openclawDir,
|
|
23933
|
+
pendingCount: gatewayApprovalSummary.pendingRequestIds.length,
|
|
23934
|
+
successfulAttempts,
|
|
23935
|
+
failedAttempts: failedAttempts.length,
|
|
23936
|
+
commandUnavailable: failedAttempts.some(
|
|
23937
|
+
(attempt) => attempt.unavailable
|
|
23938
|
+
)
|
|
23939
|
+
});
|
|
23940
|
+
checklist = await runOpenclawDoctor({
|
|
23941
|
+
homeDir: input.homeDir,
|
|
23942
|
+
openclawDir: input.openclawDir,
|
|
23943
|
+
includeConfigCheck: false,
|
|
23944
|
+
includeConnectorRuntimeCheck: input.includeConnectorRuntimeCheck
|
|
23945
|
+
});
|
|
23946
|
+
if (checklist.status === "healthy") {
|
|
23947
|
+
return;
|
|
23948
|
+
}
|
|
23949
|
+
}
|
|
23950
|
+
}
|
|
23951
|
+
const firstFailure = checklist.checks.find(
|
|
23952
|
+
(check2) => check2.status === "fail"
|
|
23953
|
+
);
|
|
23954
|
+
const unavailableGatewayApprovalAttempt = gatewayApprovalSummary?.attempts.find((attempt) => attempt.unavailable);
|
|
23955
|
+
const remediationHint = unavailableGatewayApprovalAttempt !== void 0 && firstFailure?.id === "state.gatewayDevicePairing" ? `${OPENCLAW_DEVICE_APPROVAL_RECOVERY_HINT}. Ensure the \`${resolveOpenclawGatewayApprovalCommand()}\` command is available.` : firstFailure?.remediationHint;
|
|
22976
23956
|
throw createCliError6(
|
|
22977
23957
|
"CLI_OPENCLAW_SETUP_CHECKLIST_FAILED",
|
|
22978
23958
|
"OpenClaw setup checklist failed",
|
|
22979
23959
|
{
|
|
22980
23960
|
firstFailedCheckId: firstFailure?.id,
|
|
22981
23961
|
firstFailedCheckMessage: firstFailure?.message,
|
|
22982
|
-
remediationHint
|
|
23962
|
+
remediationHint,
|
|
23963
|
+
gatewayDeviceApproval: gatewayApprovalSummary,
|
|
22983
23964
|
checks: checklist.checks
|
|
22984
23965
|
}
|
|
22985
23966
|
);
|
|
@@ -22999,7 +23980,8 @@ async function setupOpenclawSelfReady(agentName, options) {
|
|
|
22999
23980
|
await assertSetupChecklistHealthy({
|
|
23000
23981
|
homeDir: resolvedHomeDir,
|
|
23001
23982
|
openclawDir: resolvedOpenclawDir,
|
|
23002
|
-
includeConnectorRuntimeCheck: false
|
|
23983
|
+
includeConnectorRuntimeCheck: false,
|
|
23984
|
+
gatewayDeviceApprovalRunner: options.gatewayDeviceApprovalRunner
|
|
23003
23985
|
});
|
|
23004
23986
|
return {
|
|
23005
23987
|
...setup,
|
|
@@ -23031,7 +24013,8 @@ async function setupOpenclawSelfReady(agentName, options) {
|
|
|
23031
24013
|
await assertSetupChecklistHealthy({
|
|
23032
24014
|
homeDir: resolvedHomeDir,
|
|
23033
24015
|
openclawDir: resolvedOpenclawDir,
|
|
23034
|
-
includeConnectorRuntimeCheck: true
|
|
24016
|
+
includeConnectorRuntimeCheck: true,
|
|
24017
|
+
gatewayDeviceApprovalRunner: options.gatewayDeviceApprovalRunner
|
|
23035
24018
|
});
|
|
23036
24019
|
return {
|
|
23037
24020
|
...setup,
|
|
@@ -23161,13 +24144,13 @@ var createOpenclawCommand = () => {
|
|
|
23161
24144
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
23162
24145
|
import {
|
|
23163
24146
|
chmod as chmod4,
|
|
23164
|
-
mkdir as
|
|
24147
|
+
mkdir as mkdir7,
|
|
23165
24148
|
readdir,
|
|
23166
|
-
readFile as
|
|
24149
|
+
readFile as readFile7,
|
|
23167
24150
|
unlink as unlink2,
|
|
23168
|
-
writeFile as
|
|
24151
|
+
writeFile as writeFile7
|
|
23169
24152
|
} from "fs/promises";
|
|
23170
|
-
import { dirname as
|
|
24153
|
+
import { dirname as dirname6, join as join8, resolve } from "path";
|
|
23171
24154
|
import { Command as Command8 } from "commander";
|
|
23172
24155
|
import jsQR from "jsqr";
|
|
23173
24156
|
import { PNG } from "pngjs";
|
|
@@ -23191,7 +24174,7 @@ var PEER_ALIAS_PATTERN2 = /^[a-zA-Z0-9._-]+$/;
|
|
|
23191
24174
|
var DEFAULT_STATUS_WAIT_SECONDS = 300;
|
|
23192
24175
|
var DEFAULT_STATUS_POLL_INTERVAL_SECONDS = 3;
|
|
23193
24176
|
var MAX_PROFILE_NAME_LENGTH = 64;
|
|
23194
|
-
var
|
|
24177
|
+
var isRecord10 = (value) => {
|
|
23195
24178
|
return typeof value === "object" && value !== null;
|
|
23196
24179
|
};
|
|
23197
24180
|
function createCliError7(code, message2) {
|
|
@@ -23239,7 +24222,7 @@ function parseProfileName(value, label) {
|
|
|
23239
24222
|
return candidate;
|
|
23240
24223
|
}
|
|
23241
24224
|
function parsePeerProfile(payload) {
|
|
23242
|
-
if (!
|
|
24225
|
+
if (!isRecord10(payload)) {
|
|
23243
24226
|
throw createCliError7(
|
|
23244
24227
|
"CLI_PAIR_PROFILE_INVALID",
|
|
23245
24228
|
"Pair profile must be an object"
|
|
@@ -23286,7 +24269,7 @@ function parsePairingTicketIssuerOrigin(ticket) {
|
|
|
23286
24269
|
"Pairing ticket is invalid"
|
|
23287
24270
|
);
|
|
23288
24271
|
}
|
|
23289
|
-
if (!
|
|
24272
|
+
if (!isRecord10(payload) || typeof payload.iss !== "string") {
|
|
23290
24273
|
throw createCliError7(
|
|
23291
24274
|
"CLI_PAIR_CONFIRM_TICKET_INVALID",
|
|
23292
24275
|
"Pairing ticket is invalid"
|
|
@@ -23335,7 +24318,7 @@ function parseAitAgentDid(ait) {
|
|
|
23335
24318
|
"Agent AIT is invalid. Recreate the agent before pairing."
|
|
23336
24319
|
);
|
|
23337
24320
|
}
|
|
23338
|
-
if (!
|
|
24321
|
+
if (!isRecord10(payload) || typeof payload.sub !== "string") {
|
|
23339
24322
|
throw createCliError7(
|
|
23340
24323
|
"CLI_PAIR_AGENT_NOT_FOUND",
|
|
23341
24324
|
"Agent AIT is invalid. Recreate the agent before pairing."
|
|
@@ -23397,10 +24380,10 @@ function resolvePeerAlias(input) {
|
|
|
23397
24380
|
return `${baseAlias}-${index}`;
|
|
23398
24381
|
}
|
|
23399
24382
|
function resolvePeersConfigPath(getConfigDirImpl) {
|
|
23400
|
-
return
|
|
24383
|
+
return join8(getConfigDirImpl(), PEERS_FILE_NAME2);
|
|
23401
24384
|
}
|
|
23402
24385
|
function parsePeerEntry(value) {
|
|
23403
|
-
if (!
|
|
24386
|
+
if (!isRecord10(value)) {
|
|
23404
24387
|
throw createCliError7(
|
|
23405
24388
|
"CLI_PAIR_PEERS_CONFIG_INVALID",
|
|
23406
24389
|
"Peer entry must be an object"
|
|
@@ -23449,7 +24432,7 @@ async function loadPeersConfig2(input) {
|
|
|
23449
24432
|
"Peer config is not valid JSON"
|
|
23450
24433
|
);
|
|
23451
24434
|
}
|
|
23452
|
-
if (!
|
|
24435
|
+
if (!isRecord10(parsed)) {
|
|
23453
24436
|
throw createCliError7(
|
|
23454
24437
|
"CLI_PAIR_PEERS_CONFIG_INVALID",
|
|
23455
24438
|
"Peer config must be a JSON object"
|
|
@@ -23458,7 +24441,7 @@ async function loadPeersConfig2(input) {
|
|
|
23458
24441
|
if (parsed.peers === void 0) {
|
|
23459
24442
|
return { peers: {} };
|
|
23460
24443
|
}
|
|
23461
|
-
if (!
|
|
24444
|
+
if (!isRecord10(parsed.peers)) {
|
|
23462
24445
|
throw createCliError7(
|
|
23463
24446
|
"CLI_PAIR_PEERS_CONFIG_INVALID",
|
|
23464
24447
|
"Peer config peers field must be an object"
|
|
@@ -23472,7 +24455,7 @@ async function loadPeersConfig2(input) {
|
|
|
23472
24455
|
}
|
|
23473
24456
|
async function savePeersConfig2(input) {
|
|
23474
24457
|
const peersPath = resolvePeersConfigPath(input.getConfigDirImpl);
|
|
23475
|
-
await input.mkdirImpl(
|
|
24458
|
+
await input.mkdirImpl(dirname6(peersPath), { recursive: true });
|
|
23476
24459
|
await input.writeFileImpl(
|
|
23477
24460
|
peersPath,
|
|
23478
24461
|
`${JSON.stringify(input.config, null, 2)}
|
|
@@ -23482,7 +24465,7 @@ async function savePeersConfig2(input) {
|
|
|
23482
24465
|
await input.chmodImpl(peersPath, FILE_MODE4);
|
|
23483
24466
|
}
|
|
23484
24467
|
function resolveRelayRuntimeConfigPath2(getConfigDirImpl) {
|
|
23485
|
-
return
|
|
24468
|
+
return join8(getConfigDirImpl(), OPENCLAW_RELAY_RUNTIME_FILE_NAME3);
|
|
23486
24469
|
}
|
|
23487
24470
|
async function loadRelayTransformPeersPath(input) {
|
|
23488
24471
|
const relayRuntimeConfigPath = resolveRelayRuntimeConfigPath2(
|
|
@@ -23511,7 +24494,7 @@ async function loadRelayTransformPeersPath(input) {
|
|
|
23511
24494
|
});
|
|
23512
24495
|
return void 0;
|
|
23513
24496
|
}
|
|
23514
|
-
if (!
|
|
24497
|
+
if (!isRecord10(parsed)) {
|
|
23515
24498
|
return void 0;
|
|
23516
24499
|
}
|
|
23517
24500
|
const relayTransformPeersPath = parseNonEmptyString9(
|
|
@@ -23544,7 +24527,7 @@ async function syncOpenclawRelayPeersSnapshot(input) {
|
|
|
23544
24527
|
return;
|
|
23545
24528
|
}
|
|
23546
24529
|
try {
|
|
23547
|
-
await input.mkdirImpl(
|
|
24530
|
+
await input.mkdirImpl(dirname6(relayTransformPeersPath), {
|
|
23548
24531
|
recursive: true
|
|
23549
24532
|
});
|
|
23550
24533
|
await input.writeFileImpl(
|
|
@@ -23644,7 +24627,7 @@ function toPathWithQuery3(url2) {
|
|
|
23644
24627
|
return `${parsed.pathname}${parsed.search}`;
|
|
23645
24628
|
}
|
|
23646
24629
|
function extractErrorCode(payload) {
|
|
23647
|
-
if (!
|
|
24630
|
+
if (!isRecord10(payload)) {
|
|
23648
24631
|
return void 0;
|
|
23649
24632
|
}
|
|
23650
24633
|
const envelope = payload;
|
|
@@ -23655,7 +24638,7 @@ function extractErrorCode(payload) {
|
|
|
23655
24638
|
return code.length > 0 ? code : void 0;
|
|
23656
24639
|
}
|
|
23657
24640
|
function extractErrorMessage(payload) {
|
|
23658
|
-
if (!
|
|
24641
|
+
if (!isRecord10(payload)) {
|
|
23659
24642
|
return void 0;
|
|
23660
24643
|
}
|
|
23661
24644
|
const envelope = payload;
|
|
@@ -23743,7 +24726,7 @@ function mapStatusPairError(status, payload) {
|
|
|
23743
24726
|
return `Pair status failed (${status})`;
|
|
23744
24727
|
}
|
|
23745
24728
|
function parsePairStartResponse(payload) {
|
|
23746
|
-
if (!
|
|
24729
|
+
if (!isRecord10(payload)) {
|
|
23747
24730
|
throw createCliError7(
|
|
23748
24731
|
"CLI_PAIR_START_INVALID_RESPONSE",
|
|
23749
24732
|
"Pair start response is invalid"
|
|
@@ -23775,7 +24758,7 @@ function parsePairStartResponse(payload) {
|
|
|
23775
24758
|
};
|
|
23776
24759
|
}
|
|
23777
24760
|
function parsePairConfirmResponse(payload) {
|
|
23778
|
-
if (!
|
|
24761
|
+
if (!isRecord10(payload)) {
|
|
23779
24762
|
throw createCliError7(
|
|
23780
24763
|
"CLI_PAIR_CONFIRM_INVALID_RESPONSE",
|
|
23781
24764
|
"Pair confirm response is invalid"
|
|
@@ -23810,7 +24793,7 @@ function parsePairConfirmResponse(payload) {
|
|
|
23810
24793
|
};
|
|
23811
24794
|
}
|
|
23812
24795
|
function parsePairStatusResponse(payload) {
|
|
23813
|
-
if (!
|
|
24796
|
+
if (!isRecord10(payload)) {
|
|
23814
24797
|
throw createCliError7(
|
|
23815
24798
|
"CLI_PAIR_STATUS_INVALID_RESPONSE",
|
|
23816
24799
|
"Pair status response is invalid"
|
|
@@ -23876,16 +24859,16 @@ function parsePairStatusResponse(payload) {
|
|
|
23876
24859
|
};
|
|
23877
24860
|
}
|
|
23878
24861
|
async function readAgentProofMaterial(agentName, dependencies) {
|
|
23879
|
-
const readFileImpl = dependencies.readFileImpl ??
|
|
24862
|
+
const readFileImpl = dependencies.readFileImpl ?? readFile7;
|
|
23880
24863
|
const getConfigDirImpl = dependencies.getConfigDirImpl ?? getConfigDir;
|
|
23881
24864
|
const normalizedAgentName = assertValidAgentName(agentName);
|
|
23882
|
-
const agentDir =
|
|
24865
|
+
const agentDir = join8(
|
|
23883
24866
|
getConfigDirImpl(),
|
|
23884
24867
|
AGENTS_DIR_NAME5,
|
|
23885
24868
|
normalizedAgentName
|
|
23886
24869
|
);
|
|
23887
|
-
const aitPath =
|
|
23888
|
-
const secretKeyPath =
|
|
24870
|
+
const aitPath = join8(agentDir, AIT_FILE_NAME4);
|
|
24871
|
+
const secretKeyPath = join8(agentDir, SECRET_KEY_FILE_NAME3);
|
|
23889
24872
|
let ait;
|
|
23890
24873
|
try {
|
|
23891
24874
|
ait = (await readFileImpl(aitPath, "utf-8")).trim();
|
|
@@ -23983,14 +24966,14 @@ function decodeTicketFromPng(imageBytes) {
|
|
|
23983
24966
|
return parsePairingTicket(decoded.data);
|
|
23984
24967
|
}
|
|
23985
24968
|
async function persistPairingQr(input) {
|
|
23986
|
-
const mkdirImpl = input.dependencies.mkdirImpl ??
|
|
24969
|
+
const mkdirImpl = input.dependencies.mkdirImpl ?? mkdir7;
|
|
23987
24970
|
const readdirImpl = input.dependencies.readdirImpl ?? readdir;
|
|
23988
24971
|
const unlinkImpl = input.dependencies.unlinkImpl ?? unlink2;
|
|
23989
|
-
const writeFileImpl = input.dependencies.writeFileImpl ??
|
|
24972
|
+
const writeFileImpl = input.dependencies.writeFileImpl ?? writeFile7;
|
|
23990
24973
|
const getConfigDirImpl = input.dependencies.getConfigDirImpl ?? getConfigDir;
|
|
23991
24974
|
const qrEncodeImpl = input.dependencies.qrEncodeImpl ?? encodeTicketQrPng;
|
|
23992
|
-
const baseDir =
|
|
23993
|
-
const outputPath = parseNonEmptyString9(input.qrOutput) ? resolve(input.qrOutput ?? "") :
|
|
24975
|
+
const baseDir = join8(getConfigDirImpl(), PAIRING_QR_DIR_NAME);
|
|
24976
|
+
const outputPath = parseNonEmptyString9(input.qrOutput) ? resolve(input.qrOutput ?? "") : join8(
|
|
23994
24977
|
baseDir,
|
|
23995
24978
|
`${assertValidAgentName(input.agentName)}-pair-${input.nowSeconds}.png`
|
|
23996
24979
|
);
|
|
@@ -24016,7 +24999,7 @@ async function persistPairingQr(input) {
|
|
|
24016
24999
|
if (issuedAtSeconds + PAIRING_QR_MAX_AGE_SECONDS > input.nowSeconds) {
|
|
24017
25000
|
continue;
|
|
24018
25001
|
}
|
|
24019
|
-
const stalePath =
|
|
25002
|
+
const stalePath = join8(baseDir, fileName);
|
|
24020
25003
|
await unlinkImpl(stalePath).catch((error48) => {
|
|
24021
25004
|
const nodeError = error48;
|
|
24022
25005
|
if (nodeError.code === "ENOENT") {
|
|
@@ -24025,7 +25008,7 @@ async function persistPairingQr(input) {
|
|
|
24025
25008
|
throw error48;
|
|
24026
25009
|
});
|
|
24027
25010
|
}
|
|
24028
|
-
await mkdirImpl(
|
|
25011
|
+
await mkdirImpl(dirname6(outputPath), { recursive: true });
|
|
24029
25012
|
const imageBytes = await qrEncodeImpl(input.ticket);
|
|
24030
25013
|
await writeFileImpl(outputPath, imageBytes);
|
|
24031
25014
|
return outputPath;
|
|
@@ -24059,9 +25042,9 @@ function resolveConfirmTicketSource(options) {
|
|
|
24059
25042
|
}
|
|
24060
25043
|
async function persistPairedPeer(input) {
|
|
24061
25044
|
const getConfigDirImpl = input.dependencies.getConfigDirImpl ?? getConfigDir;
|
|
24062
|
-
const readFileImpl = input.dependencies.readFileImpl ??
|
|
24063
|
-
const mkdirImpl = input.dependencies.mkdirImpl ??
|
|
24064
|
-
const writeFileImpl = input.dependencies.writeFileImpl ??
|
|
25045
|
+
const readFileImpl = input.dependencies.readFileImpl ?? readFile7;
|
|
25046
|
+
const mkdirImpl = input.dependencies.mkdirImpl ?? mkdir7;
|
|
25047
|
+
const writeFileImpl = input.dependencies.writeFileImpl ?? writeFile7;
|
|
24065
25048
|
const chmodImpl = input.dependencies.chmodImpl ?? chmod4;
|
|
24066
25049
|
const issuerOrigin = parsePairingTicketIssuerOrigin(input.ticket);
|
|
24067
25050
|
const peerProxyUrl = new URL("/hooks/agent", `${issuerOrigin}/`).toString();
|
|
@@ -24173,7 +25156,7 @@ async function confirmPairing(agentName, options, dependencies = {}) {
|
|
|
24173
25156
|
const resolveConfigImpl = dependencies.resolveConfigImpl ?? resolveConfig;
|
|
24174
25157
|
const nowSecondsImpl = dependencies.nowSecondsImpl ?? (() => Math.floor(Date.now() / 1e3));
|
|
24175
25158
|
const nonceFactoryImpl = dependencies.nonceFactoryImpl ?? (() => randomBytes4(NONCE_SIZE2).toString("base64url"));
|
|
24176
|
-
const readFileImpl = dependencies.readFileImpl ??
|
|
25159
|
+
const readFileImpl = dependencies.readFileImpl ?? readFile7;
|
|
24177
25160
|
const qrDecodeImpl = dependencies.qrDecodeImpl ?? decodeTicketFromPng;
|
|
24178
25161
|
const config2 = await resolveConfigImpl();
|
|
24179
25162
|
const normalizedAgentName = assertValidAgentName(agentName);
|
|
@@ -24602,16 +25585,16 @@ import { Command as Command9 } from "commander";
|
|
|
24602
25585
|
|
|
24603
25586
|
// src/install-skill-mode.ts
|
|
24604
25587
|
import { constants, existsSync as existsSync2 } from "fs";
|
|
24605
|
-
import { access as access3, copyFile as copyFile2, mkdir as
|
|
25588
|
+
import { access as access3, copyFile as copyFile2, mkdir as mkdir8, readdir as readdir2, readFile as readFile8 } from "fs/promises";
|
|
24606
25589
|
import { createRequire } from "module";
|
|
24607
25590
|
import { homedir as homedir4 } from "os";
|
|
24608
|
-
import { dirname as
|
|
25591
|
+
import { dirname as dirname7, join as join9, relative } from "path";
|
|
24609
25592
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
24610
25593
|
var OPENCLAW_DIR_NAME2 = ".openclaw";
|
|
24611
25594
|
var SKILL_PACKAGE_NAME = "@clawdentity/openclaw-skill";
|
|
24612
25595
|
var SKILL_DIR_NAME2 = "clawdentity-openclaw-relay";
|
|
24613
25596
|
var RELAY_MODULE_FILE_NAME2 = "relay-to-peer.mjs";
|
|
24614
|
-
function
|
|
25597
|
+
function isRecord11(value) {
|
|
24615
25598
|
return typeof value === "object" && value !== null;
|
|
24616
25599
|
}
|
|
24617
25600
|
var SkillInstallError = class extends Error {
|
|
@@ -24625,7 +25608,7 @@ var SkillInstallError = class extends Error {
|
|
|
24625
25608
|
}
|
|
24626
25609
|
};
|
|
24627
25610
|
function getErrorCode3(error48) {
|
|
24628
|
-
if (!
|
|
25611
|
+
if (!isRecord11(error48)) {
|
|
24629
25612
|
return void 0;
|
|
24630
25613
|
}
|
|
24631
25614
|
return typeof error48.code === "string" ? error48.code : void 0;
|
|
@@ -24640,7 +25623,7 @@ function resolveOpenclawDir2(homeDir, inputOpenclawDir) {
|
|
|
24640
25623
|
if (typeof inputOpenclawDir === "string" && inputOpenclawDir.trim().length > 0) {
|
|
24641
25624
|
return inputOpenclawDir.trim();
|
|
24642
25625
|
}
|
|
24643
|
-
return
|
|
25626
|
+
return join9(homeDir, OPENCLAW_DIR_NAME2);
|
|
24644
25627
|
}
|
|
24645
25628
|
function resolveSkillPackageRoot(input) {
|
|
24646
25629
|
if (typeof input.skillPackageRoot === "string" && input.skillPackageRoot.trim().length > 0) {
|
|
@@ -24650,8 +25633,8 @@ function resolveSkillPackageRoot(input) {
|
|
|
24650
25633
|
if (typeof overriddenRoot === "string" && overriddenRoot.trim().length > 0) {
|
|
24651
25634
|
return overriddenRoot.trim();
|
|
24652
25635
|
}
|
|
24653
|
-
const bundledSkillRoot =
|
|
24654
|
-
|
|
25636
|
+
const bundledSkillRoot = join9(
|
|
25637
|
+
dirname7(fileURLToPath3(import.meta.url)),
|
|
24655
25638
|
"..",
|
|
24656
25639
|
"skill-bundle",
|
|
24657
25640
|
"openclaw-skill"
|
|
@@ -24663,10 +25646,10 @@ function resolveSkillPackageRoot(input) {
|
|
|
24663
25646
|
let packageJsonPath;
|
|
24664
25647
|
try {
|
|
24665
25648
|
packageJsonPath = require3.resolve(`${SKILL_PACKAGE_NAME}/package.json`);
|
|
24666
|
-
return
|
|
25649
|
+
return dirname7(packageJsonPath);
|
|
24667
25650
|
} catch {
|
|
24668
|
-
const workspaceFallbackRoot =
|
|
24669
|
-
|
|
25651
|
+
const workspaceFallbackRoot = join9(
|
|
25652
|
+
dirname7(fileURLToPath3(import.meta.url)),
|
|
24670
25653
|
"..",
|
|
24671
25654
|
"..",
|
|
24672
25655
|
"openclaw-skill"
|
|
@@ -24708,7 +25691,7 @@ async function listFilesRecursively(directoryPath) {
|
|
|
24708
25691
|
for (const entry of entries.sort(
|
|
24709
25692
|
(left, right) => left.name.localeCompare(right.name)
|
|
24710
25693
|
)) {
|
|
24711
|
-
const entryPath =
|
|
25694
|
+
const entryPath = join9(directoryPath, entry.name);
|
|
24712
25695
|
if (entry.isDirectory()) {
|
|
24713
25696
|
files.push(...await listFilesRecursively(entryPath));
|
|
24714
25697
|
continue;
|
|
@@ -24720,10 +25703,10 @@ async function listFilesRecursively(directoryPath) {
|
|
|
24720
25703
|
return files;
|
|
24721
25704
|
}
|
|
24722
25705
|
async function resolveArtifacts(input) {
|
|
24723
|
-
const skillRoot =
|
|
24724
|
-
const skillDocSource =
|
|
24725
|
-
const referencesRoot =
|
|
24726
|
-
const relaySource =
|
|
25706
|
+
const skillRoot = join9(input.skillPackageRoot, "skill");
|
|
25707
|
+
const skillDocSource = join9(skillRoot, "SKILL.md");
|
|
25708
|
+
const referencesRoot = join9(skillRoot, "references");
|
|
25709
|
+
const relaySource = join9(
|
|
24727
25710
|
input.skillPackageRoot,
|
|
24728
25711
|
"dist",
|
|
24729
25712
|
RELAY_MODULE_FILE_NAME2
|
|
@@ -24759,19 +25742,19 @@ async function resolveArtifacts(input) {
|
|
|
24759
25742
|
}
|
|
24760
25743
|
});
|
|
24761
25744
|
}
|
|
24762
|
-
const targetSkillRoot =
|
|
25745
|
+
const targetSkillRoot = join9(input.openclawDir, "skills", SKILL_DIR_NAME2);
|
|
24763
25746
|
const artifacts = [
|
|
24764
25747
|
{
|
|
24765
25748
|
sourcePath: skillDocSource,
|
|
24766
|
-
targetPath:
|
|
25749
|
+
targetPath: join9(targetSkillRoot, "SKILL.md")
|
|
24767
25750
|
},
|
|
24768
25751
|
{
|
|
24769
25752
|
sourcePath: relaySource,
|
|
24770
|
-
targetPath:
|
|
25753
|
+
targetPath: join9(targetSkillRoot, RELAY_MODULE_FILE_NAME2)
|
|
24771
25754
|
},
|
|
24772
25755
|
{
|
|
24773
25756
|
sourcePath: relaySource,
|
|
24774
|
-
targetPath:
|
|
25757
|
+
targetPath: join9(
|
|
24775
25758
|
input.openclawDir,
|
|
24776
25759
|
"hooks",
|
|
24777
25760
|
"transforms",
|
|
@@ -24783,7 +25766,7 @@ async function resolveArtifacts(input) {
|
|
|
24783
25766
|
const relativePath = relative(referencesRoot, referenceFile);
|
|
24784
25767
|
artifacts.push({
|
|
24785
25768
|
sourcePath: referenceFile,
|
|
24786
|
-
targetPath:
|
|
25769
|
+
targetPath: join9(targetSkillRoot, "references", relativePath)
|
|
24787
25770
|
});
|
|
24788
25771
|
}
|
|
24789
25772
|
return artifacts.sort(
|
|
@@ -24791,10 +25774,10 @@ async function resolveArtifacts(input) {
|
|
|
24791
25774
|
);
|
|
24792
25775
|
}
|
|
24793
25776
|
async function copyArtifact(input) {
|
|
24794
|
-
const sourceContent = await
|
|
25777
|
+
const sourceContent = await readFile8(input.sourcePath);
|
|
24795
25778
|
let existingContent;
|
|
24796
25779
|
try {
|
|
24797
|
-
existingContent = await
|
|
25780
|
+
existingContent = await readFile8(input.targetPath);
|
|
24798
25781
|
} catch (error48) {
|
|
24799
25782
|
if (getErrorCode3(error48) !== "ENOENT") {
|
|
24800
25783
|
throw error48;
|
|
@@ -24803,7 +25786,7 @@ async function copyArtifact(input) {
|
|
|
24803
25786
|
if (existingContent !== void 0 && sourceContent.equals(existingContent)) {
|
|
24804
25787
|
return "unchanged";
|
|
24805
25788
|
}
|
|
24806
|
-
await
|
|
25789
|
+
await mkdir8(dirname7(input.targetPath), { recursive: true });
|
|
24807
25790
|
await copyFile2(input.sourcePath, input.targetPath);
|
|
24808
25791
|
if (existingContent !== void 0) {
|
|
24809
25792
|
return "updated";
|
|
@@ -24835,7 +25818,7 @@ async function installOpenclawSkillArtifacts(options = {}) {
|
|
|
24835
25818
|
homeDir,
|
|
24836
25819
|
openclawDir,
|
|
24837
25820
|
skillPackageRoot,
|
|
24838
|
-
targetSkillDirectory:
|
|
25821
|
+
targetSkillDirectory: join9(openclawDir, "skills", SKILL_DIR_NAME2),
|
|
24839
25822
|
records
|
|
24840
25823
|
};
|
|
24841
25824
|
}
|
|
@@ -24926,7 +25909,7 @@ var createSkillCommand = () => {
|
|
|
24926
25909
|
};
|
|
24927
25910
|
|
|
24928
25911
|
// src/commands/verify.ts
|
|
24929
|
-
import { readFile as
|
|
25912
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
24930
25913
|
import { Command as Command10 } from "commander";
|
|
24931
25914
|
var logger10 = createLogger({ service: "cli", module: "verify" });
|
|
24932
25915
|
var REGISTRY_KEYS_CACHE_FILE = "registry-keys.json";
|
|
@@ -24939,7 +25922,7 @@ var VerifyCommandError = class extends Error {
|
|
|
24939
25922
|
this.name = "VerifyCommandError";
|
|
24940
25923
|
}
|
|
24941
25924
|
};
|
|
24942
|
-
var
|
|
25925
|
+
var isRecord12 = (value) => {
|
|
24943
25926
|
return typeof value === "object" && value !== null;
|
|
24944
25927
|
};
|
|
24945
25928
|
var normalizeRegistryUrl2 = (registryUrl) => {
|
|
@@ -24975,7 +25958,7 @@ var resolveToken = async (tokenOrFile) => {
|
|
|
24975
25958
|
throw new VerifyCommandError("invalid token (value is empty)");
|
|
24976
25959
|
}
|
|
24977
25960
|
try {
|
|
24978
|
-
const fileContents = await
|
|
25961
|
+
const fileContents = await readFile9(input, "utf-8");
|
|
24979
25962
|
const token = fileContents.trim();
|
|
24980
25963
|
if (token.length === 0) {
|
|
24981
25964
|
throw new VerifyCommandError(`invalid token (${input} is empty)`);
|
|
@@ -25007,7 +25990,7 @@ var parseResponseJson = async (response) => {
|
|
|
25007
25990
|
}
|
|
25008
25991
|
};
|
|
25009
25992
|
var parseSigningKeys = (payload) => {
|
|
25010
|
-
if (!
|
|
25993
|
+
if (!isRecord12(payload) || !Array.isArray(payload.keys)) {
|
|
25011
25994
|
throw new VerifyCommandError(
|
|
25012
25995
|
"verification keys unavailable (response payload is invalid)"
|
|
25013
25996
|
);
|
|
@@ -25026,7 +26009,7 @@ var parseSigningKeys = (payload) => {
|
|
|
25026
26009
|
};
|
|
25027
26010
|
var parseRegistryKeysCache = (rawCache) => {
|
|
25028
26011
|
const parsed = parseJson(rawCache);
|
|
25029
|
-
if (!
|
|
26012
|
+
if (!isRecord12(parsed)) {
|
|
25030
26013
|
return void 0;
|
|
25031
26014
|
}
|
|
25032
26015
|
const { registryUrl, fetchedAtMs, keys } = parsed;
|
|
@@ -25052,7 +26035,7 @@ var parseRegistryKeysCache = (rawCache) => {
|
|
|
25052
26035
|
};
|
|
25053
26036
|
var parseCrlCache = (rawCache) => {
|
|
25054
26037
|
const parsed = parseJson(rawCache);
|
|
25055
|
-
if (!
|
|
26038
|
+
if (!isRecord12(parsed)) {
|
|
25056
26039
|
return void 0;
|
|
25057
26040
|
}
|
|
25058
26041
|
const { registryUrl, fetchedAtMs, claims } = parsed;
|
|
@@ -25150,7 +26133,7 @@ var fetchCrlClaims = async (input) => {
|
|
|
25150
26133
|
);
|
|
25151
26134
|
}
|
|
25152
26135
|
const payload = await parseResponseJson(response);
|
|
25153
|
-
if (!
|
|
26136
|
+
if (!isRecord12(payload) || typeof payload.crl !== "string") {
|
|
25154
26137
|
throw new VerifyCommandError(
|
|
25155
26138
|
"revocation check unavailable (response payload is invalid)"
|
|
25156
26139
|
);
|
|
@@ -25195,7 +26178,7 @@ var loadCrlClaims = async (input) => {
|
|
|
25195
26178
|
return claims;
|
|
25196
26179
|
};
|
|
25197
26180
|
var toInvalidTokenReason = (error48) => {
|
|
25198
|
-
if (
|
|
26181
|
+
if (isRecord12(error48) && typeof error48.message === "string") {
|
|
25199
26182
|
return `invalid token (${error48.message})`;
|
|
25200
26183
|
}
|
|
25201
26184
|
if (error48 instanceof Error && error48.message.length > 0) {
|