@perkos/perkos-a2a 0.12.33 → 0.12.35
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/README.md +35 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +178 -35
- package/dist/index.js.map +3 -3
- package/dist/openclaw-supervisor-client.d.ts +8 -0
- package/dist/openclaw-supervisor-client.d.ts.map +1 -1
- package/dist/openclaw-supervisor-client.js +184 -21
- package/dist/openclaw-supervisor-client.js.map +1 -1
- package/dist/openclaw-supervisor.d.ts +8 -0
- package/dist/openclaw-supervisor.d.ts.map +1 -1
- package/dist/openclaw-supervisor.js +63 -2
- package/dist/openclaw-supervisor.js.map +1 -1
- package/dist/repair.d.ts +2 -0
- package/dist/repair.d.ts.map +1 -1
- package/dist/repair.js +121 -136
- package/dist/repair.js.map +1 -1
- package/dist/supervisor-protocol.d.ts +12 -0
- package/dist/supervisor-protocol.d.ts.map +1 -1
- package/dist/supervisor-protocol.js +5 -1
- package/dist/supervisor-protocol.js.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -170,7 +170,7 @@ openclaw gateway restart
|
|
|
170
170
|
|
|
171
171
|
#### Official idempotent connection
|
|
172
172
|
|
|
173
|
-
Version 0.12.
|
|
173
|
+
Version 0.12.28+ ships the machine-readable `perkos-a2a connect` CLI. The
|
|
174
174
|
official PerkOS invitation supplies the artifact URL, SHA-256, agent name, and
|
|
175
175
|
agent id; it invokes the exact npm version with `npx`. The same command performs
|
|
176
176
|
a first install or repairs the matching existing identity, even when an invalid
|
|
@@ -183,21 +183,24 @@ schema, and protects identity, relay/chat credentials, endpoints, runtime kind,
|
|
|
183
183
|
and session key. Bind-mounted `openclaw.json` files are updated in place when an
|
|
184
184
|
atomic rename is unavailable, while a persistent backup and the quarantined
|
|
185
185
|
legacy extension remain recoverable. A process lock prevents two simultaneous
|
|
186
|
-
repairs from registering the same agent.
|
|
186
|
+
repairs from registering the same agent. Version 0.12.35 writes the signed
|
|
187
|
+
attempt before OpenClaw can load the newly enabled plugin, so a clean first
|
|
188
|
+
generation started during `config validate` is attributable to that exact
|
|
189
|
+
command rather than being rejected as pre-restart evidence.
|
|
187
190
|
|
|
188
191
|
Every successful install is anchored by a private, content-addressed receipt;
|
|
189
192
|
the resumable repair journal rolls forward after an install has committed
|
|
190
|
-
instead of restoring an incompatible pre-upgrade source.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
It validates config and plugin health,
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
193
|
+
instead of restoring an incompatible pre-upgrade source. The plugin writes
|
|
194
|
+
HMAC-bound runtime evidence proving that the expected version and normalized
|
|
195
|
+
config were actually loaded. The receipt, journal, attempt, and runtime status
|
|
196
|
+
files are created with owner-only permissions.
|
|
197
|
+
|
|
198
|
+
It validates config and plugin health, then reads only plugin-owned runtime
|
|
199
|
+
evidence. It never mutates `commands.restart`, sends `SIGUSR1`, waits for an
|
|
200
|
+
OpenClaw gateway drain, launches a second relay/chat client, or scrapes gateway
|
|
201
|
+
status/logs. Clean installs complete through hot reload. Certified upgrades use
|
|
202
|
+
a fenced supervisor rollover while the gateway and its active model/runtime
|
|
203
|
+
remain untouched. Relay, registration, heartbeat, and chat authentication must
|
|
201
204
|
remain healthy—with no duplicate plugin or registration messages—for three
|
|
202
205
|
continuous minutes and at least three fresh successful heartbeats. Success or
|
|
203
206
|
failure is emitted as one JSON object; secret values are never printed.
|
|
@@ -217,25 +220,35 @@ READY and accept the matching claim. The supervisor records its PID, process
|
|
|
217
220
|
start identity, boot id, claim time, and transport counts in private health
|
|
218
221
|
evidence so `connect` can distinguish a certified owner from stale files.
|
|
219
222
|
|
|
220
|
-
For a clean first install, OpenClaw's PID 1 hot reload remains supported
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
223
|
+
For a clean first install, OpenClaw's PID 1 hot reload remains supported without
|
|
224
|
+
a gateway restart. A certified upgrade fences and stops the old supervisor,
|
|
225
|
+
waits for `STOPPED` plus socket/process exit, starts the supervisor CLI from the
|
|
226
|
+
new verified install root, and requires target-version health before opening a
|
|
227
|
+
single relay client. When upgrading a pre-supervisor or otherwise uncertified
|
|
228
|
+
installation, `connect` exits promptly with the machine-readable action
|
|
229
|
+
`ACTION_REQUIRED_REAL_RESTART`; rerunning the same idempotent command after an
|
|
230
|
+
out-of-band service/container restart resumes the migration without replacing
|
|
231
|
+
identity or credentials.
|
|
232
|
+
|
|
233
|
+
On successful verification, `connect` removes the active-attempt file and marks
|
|
234
|
+
the private repair journal `done`. Stale OpenClaw install metadata is reported
|
|
235
|
+
as `stale-ignored` only when the content-addressed receipt, active realpath,
|
|
236
|
+
package, plugin manifest, bundle, and CLI hashes all prove the requested
|
|
237
|
+
version; stale metadata is never allowed to weaken those identity checks.
|
|
227
238
|
|
|
228
239
|
Each claim carries the parent PID/start identity, boot id, instance id, and
|
|
229
240
|
generation. Before accepting a newer generation, the supervisor waits for
|
|
230
241
|
confirmed transport shutdown; a timeout fails closed with
|
|
231
|
-
`OWNER_STOP_TIMEOUT`.
|
|
242
|
+
`OWNER_STOP_TIMEOUT`. A private TTL rollover lock serializes concurrent upgrades,
|
|
243
|
+
and health certifies the supervisor package version and real install root. Lease
|
|
244
|
+
and health evidence are owner-only files and never
|
|
232
245
|
contain relay or chat credentials. This ownership boundary applies to OpenClaw;
|
|
233
246
|
Hermes/custom runtimes keep their existing runtime-owned model and transport
|
|
234
247
|
routing.
|
|
235
248
|
|
|
236
249
|
```bash
|
|
237
250
|
A2A_RELAY_API_KEY='<credential-from-invitation>' \
|
|
238
|
-
npx --yes @perkos/perkos-a2a@0.12.
|
|
251
|
+
npx --yes @perkos/perkos-a2a@0.12.35 connect \
|
|
239
252
|
--artifact ./perkos-a2a.tgz \
|
|
240
253
|
--sha256 '<sha256-from-the-official-invitation>' \
|
|
241
254
|
--agent-name '<agent-name>' \
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAGV,WAAW,EAEZ,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,cAAc,YAAY,CAAC;AA0C3B,kEAAkE;AAClE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAItF;AAED,KAAK,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AASpD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,OAAO,EACd,oBAAoB,EAAE,MAAM,GAC3B,OAAO,CA2BT;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,oBAAoB,EAC3B,eAAe,CAAC,EAAE,MAAM,GACvB,oBAAoB,CA+BtB;AAiED,KAAK,qBAAqB,GAAG;IAC3B,QAAQ,CAAC,EAAE,KAAK,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,CAAC,CAAC;IACH,IAAI,CAAC,EAAE;QACL,yBAAyB,CAAC,EAAE,MAAM,CAAC;QACnC,qBAAqB,CAAC,EAAE,MAAM,CAAC;KAChC,CAAC;CACH,CAAC;AAEF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,CAYrF;AAQD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,eAAe,EACvB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE;IAAE,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;CAAE,GAClF,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAuFxB;AAYD,oFAAoF;AACpF,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,WAAW,EAAE,EACvB,gBAAgB,EAAE,MAAM,GACvB,MAAM,CAWR;AA4CD,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAe/D;AAED,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,GAAG,EAAE,GAAG,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAGV,WAAW,EAEZ,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,cAAc,YAAY,CAAC;AA0C3B,kEAAkE;AAClE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAItF;AAED,KAAK,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AASpD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,OAAO,EACd,oBAAoB,EAAE,MAAM,GAC3B,OAAO,CA2BT;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,oBAAoB,EAC3B,eAAe,CAAC,EAAE,MAAM,GACvB,oBAAoB,CA+BtB;AAiED,KAAK,qBAAqB,GAAG;IAC3B,QAAQ,CAAC,EAAE,KAAK,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,CAAC,CAAC;IACH,IAAI,CAAC,EAAE;QACL,yBAAyB,CAAC,EAAE,MAAM,CAAC;QACnC,qBAAqB,CAAC,EAAE,MAAM,CAAC;KAChC,CAAC;CACH,CAAC;AAEF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,CAYrF;AAQD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,eAAe,EACvB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE;IAAE,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;CAAE,GAClF,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAuFxB;AAYD,oFAAoF;AACpF,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,WAAW,EAAE,EACvB,gBAAgB,EAAE,MAAM,GACvB,MAAM,CAWR;AA4CD,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAe/D;AAED,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,GAAG,EAAE,GAAG,QAuzBxC"}
|
package/dist/index.js
CHANGED
|
@@ -18429,7 +18429,7 @@ var require_view = __commonJS({
|
|
|
18429
18429
|
var debug = require_src()("express:view");
|
|
18430
18430
|
var path = __require("path");
|
|
18431
18431
|
var fs = __require("fs");
|
|
18432
|
-
var
|
|
18432
|
+
var dirname4 = path.dirname;
|
|
18433
18433
|
var basename = path.basename;
|
|
18434
18434
|
var extname = path.extname;
|
|
18435
18435
|
var join4 = path.join;
|
|
@@ -18468,7 +18468,7 @@ var require_view = __commonJS({
|
|
|
18468
18468
|
for (var i = 0; i < roots.length && !path2; i++) {
|
|
18469
18469
|
var root = roots[i];
|
|
18470
18470
|
var loc = resolve4(root, name);
|
|
18471
|
-
var dir =
|
|
18471
|
+
var dir = dirname4(loc);
|
|
18472
18472
|
var file = basename(loc);
|
|
18473
18473
|
path2 = this.resolve(dir, file);
|
|
18474
18474
|
}
|
|
@@ -27718,7 +27718,7 @@ var A2AServer = class {
|
|
|
27718
27718
|
// src/index.ts
|
|
27719
27719
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
27720
27720
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
27721
|
-
import { dirname as
|
|
27721
|
+
import { dirname as dirname3, resolve as resolve3 } from "node:path";
|
|
27722
27722
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
27723
27723
|
|
|
27724
27724
|
// src/agentic-actions.ts
|
|
@@ -28339,10 +28339,10 @@ function isNotFound(err) {
|
|
|
28339
28339
|
// src/openclaw-supervisor-client.ts
|
|
28340
28340
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
28341
28341
|
import { spawn } from "node:child_process";
|
|
28342
|
-
import { existsSync, readFileSync as readFileSync3, unlinkSync } from "node:fs";
|
|
28342
|
+
import { closeSync, existsSync, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync3, realpathSync, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
28343
28343
|
import { homedir as homedir3 } from "node:os";
|
|
28344
28344
|
import net from "node:net";
|
|
28345
|
-
import { resolve } from "node:path";
|
|
28345
|
+
import { dirname as dirname2, resolve } from "node:path";
|
|
28346
28346
|
import { fileURLToPath } from "node:url";
|
|
28347
28347
|
|
|
28348
28348
|
// src/supervisor-protocol.ts
|
|
@@ -28352,6 +28352,7 @@ import { tmpdir } from "node:os";
|
|
|
28352
28352
|
import { dirname, join as join2 } from "node:path";
|
|
28353
28353
|
var SUPERVISOR_PROTOCOL_VERSION = 2;
|
|
28354
28354
|
var SUPERVISOR_RENEW_MS = 5e3;
|
|
28355
|
+
var CURRENT_PROCESS_EPOCH_MS = Math.floor(Date.now() - process.uptime() * 1e3);
|
|
28355
28356
|
function supervisorOwnerHash(lifecycleKey) {
|
|
28356
28357
|
return createHash2("sha256").update(lifecycleKey).digest("hex");
|
|
28357
28358
|
}
|
|
@@ -28367,6 +28368,9 @@ function supervisorSocketPath(stateDir, ownerHash) {
|
|
|
28367
28368
|
function supervisorBootstrapPath(stateDir, ownerHash) {
|
|
28368
28369
|
return join2(supervisorDir(stateDir), `${ownerHash}.bootstrap.json`);
|
|
28369
28370
|
}
|
|
28371
|
+
function supervisorRolloverLockPath(stateDir, ownerHash) {
|
|
28372
|
+
return join2(supervisorDir(stateDir), `${ownerHash}.rollover.lock`);
|
|
28373
|
+
}
|
|
28370
28374
|
function currentBootId() {
|
|
28371
28375
|
try {
|
|
28372
28376
|
return readFileSync2("/proc/sys/kernel/random/boot_id", "utf8").trim();
|
|
@@ -28380,7 +28384,7 @@ function processStartIdentity(pid = process.pid) {
|
|
|
28380
28384
|
const afterName = stat.slice(stat.lastIndexOf(")") + 2).trim().split(/\s+/u);
|
|
28381
28385
|
return `linux-start-ticks:${afterName[19] ?? "unknown"}`;
|
|
28382
28386
|
} catch {
|
|
28383
|
-
if (pid === process.pid) return `epoch-ms:${
|
|
28387
|
+
if (pid === process.pid) return `epoch-ms:${CURRENT_PROCESS_EPOCH_MS}`;
|
|
28384
28388
|
return "unknown";
|
|
28385
28389
|
}
|
|
28386
28390
|
}
|
|
@@ -28390,6 +28394,14 @@ function encodeSupervisorMessage(message) {
|
|
|
28390
28394
|
}
|
|
28391
28395
|
|
|
28392
28396
|
// src/openclaw-supervisor-client.ts
|
|
28397
|
+
function processIsAlive(pid) {
|
|
28398
|
+
try {
|
|
28399
|
+
process.kill(pid, 0);
|
|
28400
|
+
return true;
|
|
28401
|
+
} catch {
|
|
28402
|
+
return false;
|
|
28403
|
+
}
|
|
28404
|
+
}
|
|
28393
28405
|
var OpenClawSupervisorClient = class {
|
|
28394
28406
|
stateDir;
|
|
28395
28407
|
ownerHash;
|
|
@@ -28398,6 +28410,7 @@ var OpenClawSupervisorClient = class {
|
|
|
28398
28410
|
logger;
|
|
28399
28411
|
handlers;
|
|
28400
28412
|
supervisorCliPath;
|
|
28413
|
+
spawnSupervisor;
|
|
28401
28414
|
lease;
|
|
28402
28415
|
socket = null;
|
|
28403
28416
|
startPromise = null;
|
|
@@ -28410,10 +28423,16 @@ var OpenClawSupervisorClient = class {
|
|
|
28410
28423
|
this.stateDir = resolve(input.stateDir ?? process.env.OPENCLAW_STATE_DIR ?? `${homedir3()}/.openclaw`);
|
|
28411
28424
|
this.ownerHash = input.ownerHash;
|
|
28412
28425
|
this.socketPath = supervisorSocketPath(this.stateDir, this.ownerHash);
|
|
28413
|
-
|
|
28426
|
+
let installRoot = resolve(input.config.installRoot);
|
|
28427
|
+
try {
|
|
28428
|
+
installRoot = realpathSync(installRoot);
|
|
28429
|
+
} catch {
|
|
28430
|
+
}
|
|
28431
|
+
this.config = { ...input.config, installRoot };
|
|
28414
28432
|
this.logger = input.logger;
|
|
28415
28433
|
this.handlers = input.handlers;
|
|
28416
28434
|
this.supervisorCliPath = input.supervisorCliPath;
|
|
28435
|
+
this.spawnSupervisor = input.spawnSupervisor;
|
|
28417
28436
|
this.lease = {
|
|
28418
28437
|
ownerKey: input.ownerHash,
|
|
28419
28438
|
instanceId: input.instanceId,
|
|
@@ -28439,15 +28458,18 @@ var OpenClawSupervisorClient = class {
|
|
|
28439
28458
|
this.stopped = false;
|
|
28440
28459
|
const starting = (async () => {
|
|
28441
28460
|
await this.connectOrSpawn();
|
|
28442
|
-
|
|
28443
|
-
|
|
28444
|
-
|
|
28445
|
-
|
|
28446
|
-
|
|
28447
|
-
|
|
28448
|
-
}
|
|
28461
|
+
let claim;
|
|
28462
|
+
try {
|
|
28463
|
+
claim = await this.claimCurrentSupervisor();
|
|
28464
|
+
} catch (error) {
|
|
28465
|
+
if (!String(error).includes("SUPERVISOR_TARGET_MISMATCH")) throw error;
|
|
28466
|
+
claim = await this.rolloverSupervisor("target-mismatch");
|
|
28467
|
+
}
|
|
28468
|
+
if (!this.healthMatchesTarget(claim.health)) {
|
|
28469
|
+
claim = await this.rolloverSupervisor("legacy-health-missing-target");
|
|
28470
|
+
}
|
|
28449
28471
|
const health = claim?.health;
|
|
28450
|
-
if (claim?.state !== "READY" || health?.schemaVersion !== 2 || health.authority !== "process-external-supervisor" || health.state !== "running" || health.ownerKey !== this.ownerHash || health.instanceId !== this.lease.instanceId || health.generation !== this.lease.generation || health.relayClientCount !== (this.config.relay?.enabled && this.config.relay.url ? 1 : 0) || !health.claimedAt) {
|
|
28472
|
+
if (claim?.state !== "READY" || health?.schemaVersion !== 2 || health.authority !== "process-external-supervisor" || health.state !== "running" || health.ownerKey !== this.ownerHash || health.instanceId !== this.lease.instanceId || health.generation !== this.lease.generation || !this.healthMatchesTarget(health) || health.relayClientCount !== (this.config.relay?.enabled && this.config.relay.url ? 1 : 0) || !health.claimedAt) {
|
|
28451
28473
|
throw new Error("OWNERSHIP_PRIMITIVE_UNAVAILABLE:supervisor claim did not return certified READY/CLAIMED health");
|
|
28452
28474
|
}
|
|
28453
28475
|
this.health = health;
|
|
@@ -28460,6 +28482,123 @@ var OpenClawSupervisorClient = class {
|
|
|
28460
28482
|
this.startPromise = starting;
|
|
28461
28483
|
return starting;
|
|
28462
28484
|
}
|
|
28485
|
+
claimCurrentSupervisor() {
|
|
28486
|
+
return this.requestWithMessage({
|
|
28487
|
+
protocolVersion: 2,
|
|
28488
|
+
type: "claim",
|
|
28489
|
+
requestId: randomUUID5(),
|
|
28490
|
+
lease: this.freshLease(),
|
|
28491
|
+
config: this.config
|
|
28492
|
+
}, 15e3);
|
|
28493
|
+
}
|
|
28494
|
+
healthMatchesTarget(health) {
|
|
28495
|
+
if (!health || health.targetVersion !== this.config.version || !health.installRoot) return false;
|
|
28496
|
+
let healthRoot = resolve(health.installRoot);
|
|
28497
|
+
try {
|
|
28498
|
+
healthRoot = realpathSync(healthRoot);
|
|
28499
|
+
} catch {
|
|
28500
|
+
}
|
|
28501
|
+
return healthRoot === this.config.installRoot;
|
|
28502
|
+
}
|
|
28503
|
+
async rolloverSupervisor(reason) {
|
|
28504
|
+
const releaseLock = await this.acquireRolloverLock();
|
|
28505
|
+
try {
|
|
28506
|
+
this.closeSocket(new Error("SUPERVISOR_ROLLOVER_RECHECK"));
|
|
28507
|
+
await this.connectOrSpawn();
|
|
28508
|
+
try {
|
|
28509
|
+
const current = await this.claimCurrentSupervisor();
|
|
28510
|
+
if (this.healthMatchesTarget(current.health)) return current;
|
|
28511
|
+
const stopped = await this.requestWithMessage({
|
|
28512
|
+
protocolVersion: 2,
|
|
28513
|
+
type: "release",
|
|
28514
|
+
requestId: randomUUID5(),
|
|
28515
|
+
lease: this.freshLease()
|
|
28516
|
+
}, 8e3);
|
|
28517
|
+
if (stopped?.state !== "STOPPED") throw new Error("OWNER_STOP_TIMEOUT:legacy supervisor did not return STOPPED");
|
|
28518
|
+
} catch (error) {
|
|
28519
|
+
if (!String(error).includes("SUPERVISOR_TARGET_MISMATCH")) throw error;
|
|
28520
|
+
const stopped = await this.requestWithMessage({
|
|
28521
|
+
protocolVersion: 2,
|
|
28522
|
+
type: "prepareRollover",
|
|
28523
|
+
requestId: randomUUID5(),
|
|
28524
|
+
lease: this.freshLease(),
|
|
28525
|
+
targetVersion: this.config.version,
|
|
28526
|
+
targetInstallRoot: this.config.installRoot
|
|
28527
|
+
}, 8e3);
|
|
28528
|
+
if (stopped?.state !== "STOPPED") throw new Error("OWNER_STOP_TIMEOUT:supervisor rollover did not return STOPPED");
|
|
28529
|
+
}
|
|
28530
|
+
const oldHealth = this.health;
|
|
28531
|
+
this.closeSocket(new Error("SUPERVISOR_ROLLOVER"));
|
|
28532
|
+
await this.waitForSupervisorStop(oldHealth, 1e4);
|
|
28533
|
+
await this.connectOrSpawn();
|
|
28534
|
+
const target = await this.claimCurrentSupervisor();
|
|
28535
|
+
if (!this.healthMatchesTarget(target.health)) {
|
|
28536
|
+
throw new Error(`SUPERVISOR_ROLLOVER_TARGET_MISMATCH:${reason}`);
|
|
28537
|
+
}
|
|
28538
|
+
this.logger.info(`[perkos-a2a] Supervisor rollover complete targetVersion=${this.config.version}`);
|
|
28539
|
+
return target;
|
|
28540
|
+
} finally {
|
|
28541
|
+
releaseLock();
|
|
28542
|
+
}
|
|
28543
|
+
}
|
|
28544
|
+
async acquireRolloverLock(timeoutMs = 15e3) {
|
|
28545
|
+
const path = supervisorRolloverLockPath(this.stateDir, this.ownerHash);
|
|
28546
|
+
mkdirSync2(dirname2(path), { recursive: true, mode: 448 });
|
|
28547
|
+
const deadline = Date.now() + timeoutMs;
|
|
28548
|
+
while (Date.now() < deadline) {
|
|
28549
|
+
try {
|
|
28550
|
+
const fd = openSync(path, "wx", 384);
|
|
28551
|
+
writeFileSync2(fd, `${JSON.stringify({
|
|
28552
|
+
schemaVersion: 1,
|
|
28553
|
+
instanceId: this.lease.instanceId,
|
|
28554
|
+
pid: process.pid,
|
|
28555
|
+
processStartTime: processStartIdentity(),
|
|
28556
|
+
expiresAt: new Date(Date.now() + 2e4).toISOString()
|
|
28557
|
+
})}
|
|
28558
|
+
`);
|
|
28559
|
+
closeSync(fd);
|
|
28560
|
+
return () => {
|
|
28561
|
+
try {
|
|
28562
|
+
const lock = JSON.parse(readFileSync3(path, "utf8"));
|
|
28563
|
+
if (lock.instanceId === this.lease.instanceId) unlinkSync(path);
|
|
28564
|
+
} catch {
|
|
28565
|
+
}
|
|
28566
|
+
};
|
|
28567
|
+
} catch (error) {
|
|
28568
|
+
if (error.code !== "EEXIST") throw error;
|
|
28569
|
+
try {
|
|
28570
|
+
const lock = JSON.parse(readFileSync3(path, "utf8"));
|
|
28571
|
+
const expired = !lock.expiresAt || Date.parse(lock.expiresAt) <= Date.now();
|
|
28572
|
+
const observedStart = lock.pid ? processStartIdentity(lock.pid) : "unknown";
|
|
28573
|
+
const reusedLinuxPid = observedStart.startsWith("linux-start-ticks:") && Boolean(lock.processStartTime?.startsWith("linux-start-ticks:")) && observedStart !== lock.processStartTime;
|
|
28574
|
+
const dead = !lock.pid || !processIsAlive(lock.pid) || reusedLinuxPid;
|
|
28575
|
+
if (expired || dead) {
|
|
28576
|
+
unlinkSync(path);
|
|
28577
|
+
continue;
|
|
28578
|
+
}
|
|
28579
|
+
} catch {
|
|
28580
|
+
try {
|
|
28581
|
+
unlinkSync(path);
|
|
28582
|
+
} catch {
|
|
28583
|
+
}
|
|
28584
|
+
continue;
|
|
28585
|
+
}
|
|
28586
|
+
await new Promise((resolveWait) => setTimeout(resolveWait, 100));
|
|
28587
|
+
}
|
|
28588
|
+
}
|
|
28589
|
+
throw new Error("ROLLOVER_LOCK_TIMEOUT");
|
|
28590
|
+
}
|
|
28591
|
+
async waitForSupervisorStop(health, timeoutMs) {
|
|
28592
|
+
const deadline = Date.now() + timeoutMs;
|
|
28593
|
+
while (Date.now() < deadline) {
|
|
28594
|
+
const socketGone = !existsSync(this.socketPath);
|
|
28595
|
+
const processGone = !health || processStartIdentity(health.pid) !== health.supervisorStartTime;
|
|
28596
|
+
if (socketGone && (processGone || health?.state === "stopped")) return;
|
|
28597
|
+
if (socketGone && processStartIdentity(health?.pid ?? -1) === "unknown") return;
|
|
28598
|
+
await new Promise((resolveWait) => setTimeout(resolveWait, 100));
|
|
28599
|
+
}
|
|
28600
|
+
throw new Error("OWNER_STOP_TIMEOUT:old supervisor process/socket remained active");
|
|
28601
|
+
}
|
|
28463
28602
|
stop() {
|
|
28464
28603
|
if (this.stopPromise) return this.stopPromise;
|
|
28465
28604
|
if (this.stopped && !this.socket) return Promise.resolve();
|
|
@@ -28541,27 +28680,29 @@ var OpenClawSupervisorClient = class {
|
|
|
28541
28680
|
} catch {
|
|
28542
28681
|
}
|
|
28543
28682
|
const cliPath = this.supervisorCliPath ?? fileURLToPath(new URL("./openclaw-supervisor-cli.js", import.meta.url));
|
|
28544
|
-
if (!existsSync(cliPath)) {
|
|
28545
|
-
throw new Error(`OWNERSHIP_PRIMITIVE_UNAVAILABLE:supervisor CLI is missing at ${cliPath}`);
|
|
28546
|
-
}
|
|
28683
|
+
if (!this.spawnSupervisor && !existsSync(cliPath)) throw new Error(`OWNERSHIP_PRIMITIVE_UNAVAILABLE:supervisor CLI is missing at ${cliPath}`);
|
|
28547
28684
|
const bootstrapPath = supervisorBootstrapPath(this.stateDir, this.ownerHash);
|
|
28548
28685
|
try {
|
|
28549
28686
|
unlinkSync(bootstrapPath);
|
|
28550
28687
|
} catch {
|
|
28551
28688
|
}
|
|
28552
28689
|
let childExit;
|
|
28553
|
-
|
|
28554
|
-
|
|
28555
|
-
|
|
28556
|
-
|
|
28557
|
-
|
|
28558
|
-
|
|
28559
|
-
|
|
28560
|
-
|
|
28561
|
-
|
|
28562
|
-
|
|
28563
|
-
|
|
28564
|
-
|
|
28690
|
+
if (this.spawnSupervisor) {
|
|
28691
|
+
await this.spawnSupervisor();
|
|
28692
|
+
} else {
|
|
28693
|
+
const child = spawn(process.execPath, [cliPath, "--state-dir", this.stateDir, "--owner-key", this.ownerHash], {
|
|
28694
|
+
detached: true,
|
|
28695
|
+
stdio: "ignore",
|
|
28696
|
+
env: { ...process.env }
|
|
28697
|
+
});
|
|
28698
|
+
child.once("error", (error) => {
|
|
28699
|
+
childExit = `spawn error: ${error.message}`;
|
|
28700
|
+
});
|
|
28701
|
+
child.once("exit", (code, signal) => {
|
|
28702
|
+
childExit = `exit code=${code ?? "null"} signal=${signal ?? "none"}`;
|
|
28703
|
+
});
|
|
28704
|
+
child.unref();
|
|
28705
|
+
}
|
|
28565
28706
|
const deadline = Date.now() + 1e4;
|
|
28566
28707
|
let lastError;
|
|
28567
28708
|
while (Date.now() < deadline) {
|
|
@@ -28692,10 +28833,10 @@ var OpenClawSupervisorClient = class {
|
|
|
28692
28833
|
import { createHmac } from "node:crypto";
|
|
28693
28834
|
import {
|
|
28694
28835
|
chmodSync as chmodSync2,
|
|
28695
|
-
mkdirSync as
|
|
28836
|
+
mkdirSync as mkdirSync3,
|
|
28696
28837
|
readFileSync as readFileSync4,
|
|
28697
28838
|
renameSync as renameSync2,
|
|
28698
|
-
writeFileSync as
|
|
28839
|
+
writeFileSync as writeFileSync3
|
|
28699
28840
|
} from "node:fs";
|
|
28700
28841
|
import { homedir as homedir4 } from "node:os";
|
|
28701
28842
|
import { join as join3, resolve as resolve2 } from "node:path";
|
|
@@ -28754,10 +28895,10 @@ function recordRuntimeLoadEvidence(input) {
|
|
|
28754
28895
|
bootId: currentBootId(),
|
|
28755
28896
|
pluginRoot: resolve2(input.pluginRoot)
|
|
28756
28897
|
};
|
|
28757
|
-
|
|
28898
|
+
mkdirSync3(evidenceDir, { recursive: true, mode: 448 });
|
|
28758
28899
|
const target = runtimeStatusPath(input.stateDir);
|
|
28759
28900
|
const temporary = `${target}.${process.pid}.tmp`;
|
|
28760
|
-
|
|
28901
|
+
writeFileSync3(temporary, `${JSON.stringify(status, null, 2)}
|
|
28761
28902
|
`, { mode: 384 });
|
|
28762
28903
|
renameSync2(temporary, target);
|
|
28763
28904
|
chmodSync2(target, 384);
|
|
@@ -29478,10 +29619,11 @@ function register(api) {
|
|
|
29478
29619
|
};
|
|
29479
29620
|
const logger = api.logger || console;
|
|
29480
29621
|
const pluginVersion = loadedPluginVersion();
|
|
29622
|
+
const pluginRoot = resolve3(dirname3(fileURLToPath2(import.meta.url)), "..");
|
|
29481
29623
|
recordRuntimeLoadEvidence({
|
|
29482
29624
|
config: pluginConfig,
|
|
29483
29625
|
version: pluginVersion,
|
|
29484
|
-
pluginRoot
|
|
29626
|
+
pluginRoot,
|
|
29485
29627
|
logger
|
|
29486
29628
|
});
|
|
29487
29629
|
const server = new A2AServer(pluginConfig, logger, { relayOwnership: "external-required" });
|
|
@@ -29747,6 +29889,7 @@ function register(api) {
|
|
|
29747
29889
|
agentName: pluginConfig.agentName,
|
|
29748
29890
|
agentId: platformAgentId,
|
|
29749
29891
|
version: pluginVersion,
|
|
29892
|
+
installRoot: pluginRoot,
|
|
29750
29893
|
relay: pluginConfig.relay,
|
|
29751
29894
|
chat: chatEnabled ? {
|
|
29752
29895
|
enabled: true,
|