@perkos/perkos-a2a 0.12.28 → 0.12.30
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 +22 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +128 -33
- package/dist/index.js.map +4 -4
- package/dist/repair-cli.js +31 -2
- package/dist/repair-cli.js.map +1 -1
- package/dist/repair.d.ts +40 -0
- package/dist/repair.d.ts.map +1 -1
- package/dist/repair.js +530 -127
- package/dist/repair.js.map +1 -1
- package/dist/runtime-evidence.d.ts +42 -0
- package/dist/runtime-evidence.d.ts.map +1 -0
- package/dist/runtime-evidence.js +81 -0
- package/dist/runtime-evidence.js.map +1 -0
- 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.30+ 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
|
|
@@ -180,14 +180,30 @@ The command selects OpenClaw's recorded `npm-pack:` installation path (including
|
|
|
180
180
|
the `npm/node_modules` layout used by OpenClaw 2026.5.x), quarantines a competing
|
|
181
181
|
`extensions/perkos-a2a` source, normalizes config against the packaged manifest
|
|
182
182
|
schema, and protects identity, relay/chat credentials, endpoints, runtime kind,
|
|
183
|
-
and session key.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
183
|
+
and session key. Bind-mounted `openclaw.json` files are updated in place when an
|
|
184
|
+
atomic rename is unavailable, while a persistent backup and the quarantined
|
|
185
|
+
legacy extension remain recoverable. A process lock prevents two simultaneous
|
|
186
|
+
repairs from registering the same agent.
|
|
187
|
+
|
|
188
|
+
Every successful install is anchored by a private, content-addressed receipt;
|
|
189
|
+
the resumable repair journal rolls forward after an install has committed
|
|
190
|
+
instead of restoring an incompatible pre-upgrade source. On restart, the
|
|
191
|
+
plugin writes HMAC-bound runtime evidence proving that the expected version and
|
|
192
|
+
normalized config were actually loaded. The receipt, journal, attempt, and
|
|
193
|
+
runtime status files are created with owner-only permissions.
|
|
194
|
+
|
|
195
|
+
It then validates config and plugin health and performs a verifiable restart. A
|
|
196
|
+
normal service must return a new PID/start identity; a containerized PID 1
|
|
197
|
+
gateway restarts in-process through OpenClaw's authorized `SIGUSR1` path and must
|
|
198
|
+
emit fresh restart logs. Relay, registration, heartbeat, and chat authentication
|
|
199
|
+
must remain healthy—with no duplicate plugin or registration messages—for three
|
|
200
|
+
continuous minutes and at least three fresh successful heartbeats. Success or
|
|
201
|
+
failure is emitted as one JSON object; secret
|
|
202
|
+
values are never printed.
|
|
187
203
|
|
|
188
204
|
```bash
|
|
189
205
|
A2A_RELAY_API_KEY='<credential-from-invitation>' \
|
|
190
|
-
npx --yes @perkos/perkos-a2a@0.12.
|
|
206
|
+
npx --yes @perkos/perkos-a2a@0.12.30 connect \
|
|
191
207
|
--artifact ./perkos-a2a.tgz \
|
|
192
208
|
--sha256 '<sha256-from-the-official-invitation>' \
|
|
193
209
|
--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;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAYH,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,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,GAAG,EAAE,GAAG,QAq1BxC"}
|
package/dist/index.js
CHANGED
|
@@ -4944,10 +4944,10 @@ var require_raw_body = __commonJS({
|
|
|
4944
4944
|
if (done) {
|
|
4945
4945
|
return readStream(stream, encoding, length, limit, wrap(done));
|
|
4946
4946
|
}
|
|
4947
|
-
return new Promise(function executor(
|
|
4947
|
+
return new Promise(function executor(resolve3, reject) {
|
|
4948
4948
|
readStream(stream, encoding, length, limit, function onRead(err, buf) {
|
|
4949
4949
|
if (err) return reject(err);
|
|
4950
|
-
|
|
4950
|
+
resolve3(buf);
|
|
4951
4951
|
});
|
|
4952
4952
|
});
|
|
4953
4953
|
}
|
|
@@ -18429,11 +18429,11 @@ 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 dirname2 = path.dirname;
|
|
18433
18433
|
var basename = path.basename;
|
|
18434
18434
|
var extname = path.extname;
|
|
18435
|
-
var
|
|
18436
|
-
var
|
|
18435
|
+
var join3 = path.join;
|
|
18436
|
+
var resolve3 = path.resolve;
|
|
18437
18437
|
module.exports = View;
|
|
18438
18438
|
function View(name, options) {
|
|
18439
18439
|
var opts = options || {};
|
|
@@ -18467,8 +18467,8 @@ var require_view = __commonJS({
|
|
|
18467
18467
|
debug('lookup "%s"', name);
|
|
18468
18468
|
for (var i = 0; i < roots.length && !path2; i++) {
|
|
18469
18469
|
var root = roots[i];
|
|
18470
|
-
var loc =
|
|
18471
|
-
var dir =
|
|
18470
|
+
var loc = resolve3(root, name);
|
|
18471
|
+
var dir = dirname2(loc);
|
|
18472
18472
|
var file = basename(loc);
|
|
18473
18473
|
path2 = this.resolve(dir, file);
|
|
18474
18474
|
}
|
|
@@ -18478,14 +18478,14 @@ var require_view = __commonJS({
|
|
|
18478
18478
|
debug('render "%s"', this.path);
|
|
18479
18479
|
this.engine(this.path, options, callback);
|
|
18480
18480
|
};
|
|
18481
|
-
View.prototype.resolve = function
|
|
18481
|
+
View.prototype.resolve = function resolve4(dir, file) {
|
|
18482
18482
|
var ext = this.ext;
|
|
18483
|
-
var path2 =
|
|
18483
|
+
var path2 = join3(dir, file);
|
|
18484
18484
|
var stat = tryStat(path2);
|
|
18485
18485
|
if (stat && stat.isFile()) {
|
|
18486
18486
|
return path2;
|
|
18487
18487
|
}
|
|
18488
|
-
path2 =
|
|
18488
|
+
path2 = join3(dir, basename(file, ext), "index" + ext);
|
|
18489
18489
|
stat = tryStat(path2);
|
|
18490
18490
|
if (stat && stat.isFile()) {
|
|
18491
18491
|
return path2;
|
|
@@ -19118,9 +19118,9 @@ var require_send = __commonJS({
|
|
|
19118
19118
|
var Stream = __require("stream");
|
|
19119
19119
|
var util = __require("util");
|
|
19120
19120
|
var extname = path.extname;
|
|
19121
|
-
var
|
|
19121
|
+
var join3 = path.join;
|
|
19122
19122
|
var normalize = path.normalize;
|
|
19123
|
-
var
|
|
19123
|
+
var resolve3 = path.resolve;
|
|
19124
19124
|
var sep = path.sep;
|
|
19125
19125
|
var BYTES_RANGE_REGEXP = /^ *bytes=/;
|
|
19126
19126
|
var MAX_MAXAGE = 60 * 60 * 24 * 365 * 1e3;
|
|
@@ -19157,7 +19157,7 @@ var require_send = __commonJS({
|
|
|
19157
19157
|
this._maxage = opts.maxAge || opts.maxage;
|
|
19158
19158
|
this._maxage = typeof this._maxage === "string" ? ms(this._maxage) : Number(this._maxage);
|
|
19159
19159
|
this._maxage = !isNaN(this._maxage) ? Math.min(Math.max(0, this._maxage), MAX_MAXAGE) : 0;
|
|
19160
|
-
this._root = opts.root ?
|
|
19160
|
+
this._root = opts.root ? resolve3(opts.root) : null;
|
|
19161
19161
|
if (!this._root && opts.from) {
|
|
19162
19162
|
this.from(opts.from);
|
|
19163
19163
|
}
|
|
@@ -19181,7 +19181,7 @@ var require_send = __commonJS({
|
|
|
19181
19181
|
return this;
|
|
19182
19182
|
}, "send.index: pass index as option");
|
|
19183
19183
|
SendStream.prototype.root = function root(path2) {
|
|
19184
|
-
this._root =
|
|
19184
|
+
this._root = resolve3(String(path2));
|
|
19185
19185
|
debug("root %s", this._root);
|
|
19186
19186
|
return this;
|
|
19187
19187
|
};
|
|
@@ -19337,7 +19337,7 @@ var require_send = __commonJS({
|
|
|
19337
19337
|
return res;
|
|
19338
19338
|
}
|
|
19339
19339
|
parts = path2.split(sep);
|
|
19340
|
-
path2 = normalize(
|
|
19340
|
+
path2 = normalize(join3(root, path2));
|
|
19341
19341
|
} else {
|
|
19342
19342
|
if (UP_PATH_REGEXP.test(path2)) {
|
|
19343
19343
|
debug('malicious path "%s"', path2);
|
|
@@ -19345,7 +19345,7 @@ var require_send = __commonJS({
|
|
|
19345
19345
|
return res;
|
|
19346
19346
|
}
|
|
19347
19347
|
parts = normalize(path2).split(sep);
|
|
19348
|
-
path2 =
|
|
19348
|
+
path2 = resolve3(path2);
|
|
19349
19349
|
}
|
|
19350
19350
|
if (containsDotFile(parts)) {
|
|
19351
19351
|
var access = this._dotfiles;
|
|
@@ -19472,7 +19472,7 @@ var require_send = __commonJS({
|
|
|
19472
19472
|
if (err) return self.onStatError(err);
|
|
19473
19473
|
return self.error(404);
|
|
19474
19474
|
}
|
|
19475
|
-
var p =
|
|
19475
|
+
var p = join3(path2, self._index[i]);
|
|
19476
19476
|
debug('stat "%s"', p);
|
|
19477
19477
|
fs.stat(p, function(err2, stat) {
|
|
19478
19478
|
if (err2) return next(err2);
|
|
@@ -20625,7 +20625,7 @@ var require_application = __commonJS({
|
|
|
20625
20625
|
var deprecate = require_depd()("express");
|
|
20626
20626
|
var flatten = require_array_flatten();
|
|
20627
20627
|
var merge = require_utils_merge();
|
|
20628
|
-
var
|
|
20628
|
+
var resolve3 = __require("path").resolve;
|
|
20629
20629
|
var setPrototypeOf = require_setprototypeof();
|
|
20630
20630
|
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
20631
20631
|
var slice = Array.prototype.slice;
|
|
@@ -20664,7 +20664,7 @@ var require_application = __commonJS({
|
|
|
20664
20664
|
this.mountpath = "/";
|
|
20665
20665
|
this.locals.settings = this.settings;
|
|
20666
20666
|
this.set("view", View);
|
|
20667
|
-
this.set("views",
|
|
20667
|
+
this.set("views", resolve3("views"));
|
|
20668
20668
|
this.set("jsonp callback name", "callback");
|
|
20669
20669
|
if (env === "production") {
|
|
20670
20670
|
this.enable("view cache");
|
|
@@ -21909,7 +21909,7 @@ var require_response = __commonJS({
|
|
|
21909
21909
|
var send = require_send();
|
|
21910
21910
|
var extname = path.extname;
|
|
21911
21911
|
var mime = send.mime;
|
|
21912
|
-
var
|
|
21912
|
+
var resolve3 = path.resolve;
|
|
21913
21913
|
var vary = require_vary();
|
|
21914
21914
|
var res = Object.create(http.ServerResponse.prototype);
|
|
21915
21915
|
module.exports = res;
|
|
@@ -22168,7 +22168,7 @@ var require_response = __commonJS({
|
|
|
22168
22168
|
}
|
|
22169
22169
|
opts = Object.create(opts);
|
|
22170
22170
|
opts.headers = headers;
|
|
22171
|
-
var fullPath = !opts.root ?
|
|
22171
|
+
var fullPath = !opts.root ? resolve3(path2) : path2;
|
|
22172
22172
|
return this.sendFile(fullPath, opts, done);
|
|
22173
22173
|
};
|
|
22174
22174
|
res.contentType = res.type = function contentType(type) {
|
|
@@ -22434,7 +22434,7 @@ var require_serve_static = __commonJS({
|
|
|
22434
22434
|
var encodeUrl = require_encodeurl();
|
|
22435
22435
|
var escapeHtml = require_escape_html();
|
|
22436
22436
|
var parseUrl = require_parseurl();
|
|
22437
|
-
var
|
|
22437
|
+
var resolve3 = __require("path").resolve;
|
|
22438
22438
|
var send = require_send();
|
|
22439
22439
|
var url = __require("url");
|
|
22440
22440
|
module.exports = serveStatic;
|
|
@@ -22454,7 +22454,7 @@ var require_serve_static = __commonJS({
|
|
|
22454
22454
|
throw new TypeError("option setHeaders must be function");
|
|
22455
22455
|
}
|
|
22456
22456
|
opts.maxage = opts.maxage || opts.maxAge || 0;
|
|
22457
|
-
opts.root =
|
|
22457
|
+
opts.root = resolve3(root);
|
|
22458
22458
|
var onDirectory = redirect ? createRedirectDirectoryListener() : createNotFoundDirectoryListener();
|
|
22459
22459
|
return function serveStatic2(req, res, next) {
|
|
22460
22460
|
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
@@ -26450,7 +26450,7 @@ var RelayClient = class {
|
|
|
26450
26450
|
}
|
|
26451
26451
|
}
|
|
26452
26452
|
async sendAndWait(msg, expectedType, timeoutMs) {
|
|
26453
|
-
return new Promise((
|
|
26453
|
+
return new Promise((resolve3, reject) => {
|
|
26454
26454
|
const timer = setTimeout(() => {
|
|
26455
26455
|
this.pendingCallbacks.delete(msg.id);
|
|
26456
26456
|
reject(new Error(`Relay request timed out after ${timeoutMs}ms`));
|
|
@@ -26462,7 +26462,7 @@ var RelayClient = class {
|
|
|
26462
26462
|
if (response.type === "error") {
|
|
26463
26463
|
reject(new Error(`Relay error: ${response.payload.message}`));
|
|
26464
26464
|
} else {
|
|
26465
|
-
|
|
26465
|
+
resolve3(response);
|
|
26466
26466
|
}
|
|
26467
26467
|
}
|
|
26468
26468
|
});
|
|
@@ -27237,7 +27237,7 @@ var A2AServer = class {
|
|
|
27237
27237
|
async waitForRemoteTaskTerminal(targetUrl, headers, taskId, timeoutMs = 45e3, pollMs = 1e3) {
|
|
27238
27238
|
const deadline = Date.now() + timeoutMs;
|
|
27239
27239
|
while (Date.now() < deadline) {
|
|
27240
|
-
await new Promise((
|
|
27240
|
+
await new Promise((resolve3) => setTimeout(resolve3, pollMs));
|
|
27241
27241
|
const response = await fetch(`${targetUrl}/a2a/jsonrpc`, {
|
|
27242
27242
|
method: "POST",
|
|
27243
27243
|
headers,
|
|
@@ -27261,7 +27261,7 @@ var A2AServer = class {
|
|
|
27261
27261
|
if (task && isTerminalTaskState(task.status?.state)) {
|
|
27262
27262
|
return this.success(randomUUID4(), structuredClone(task));
|
|
27263
27263
|
}
|
|
27264
|
-
await new Promise((
|
|
27264
|
+
await new Promise((resolve3) => setTimeout(resolve3, pollMs));
|
|
27265
27265
|
}
|
|
27266
27266
|
return null;
|
|
27267
27267
|
}
|
|
@@ -27473,6 +27473,9 @@ var A2AServer = class {
|
|
|
27473
27473
|
|
|
27474
27474
|
// src/index.ts
|
|
27475
27475
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
27476
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
27477
|
+
import { dirname, resolve as resolve2 } from "node:path";
|
|
27478
|
+
import { fileURLToPath } from "node:url";
|
|
27476
27479
|
|
|
27477
27480
|
// src/agentic-actions.ts
|
|
27478
27481
|
var FALLBACK_AGENT_ALIASES = [
|
|
@@ -28017,7 +28020,7 @@ var ChatStore = class {
|
|
|
28017
28020
|
let firstMessageAt = null;
|
|
28018
28021
|
let lastMessageAt = null;
|
|
28019
28022
|
let leftover = "";
|
|
28020
|
-
return new Promise((
|
|
28023
|
+
return new Promise((resolve3, reject) => {
|
|
28021
28024
|
stream.on("data", (chunk) => {
|
|
28022
28025
|
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
28023
28026
|
hash.update(text);
|
|
@@ -28057,7 +28060,7 @@ var ChatStore = class {
|
|
|
28057
28060
|
hash.update(metaRaw);
|
|
28058
28061
|
} catch {
|
|
28059
28062
|
}
|
|
28060
|
-
|
|
28063
|
+
resolve3({
|
|
28061
28064
|
transcriptHash: hash.digest("hex"),
|
|
28062
28065
|
hashAlgo: "sha256",
|
|
28063
28066
|
messageCount,
|
|
@@ -28526,12 +28529,97 @@ function startPlatformHeartbeat(opts) {
|
|
|
28526
28529
|
};
|
|
28527
28530
|
}
|
|
28528
28531
|
|
|
28532
|
+
// src/runtime-evidence.ts
|
|
28533
|
+
import { createHmac } from "node:crypto";
|
|
28534
|
+
import {
|
|
28535
|
+
chmodSync,
|
|
28536
|
+
mkdirSync,
|
|
28537
|
+
readFileSync as readFileSync2,
|
|
28538
|
+
renameSync,
|
|
28539
|
+
writeFileSync
|
|
28540
|
+
} from "node:fs";
|
|
28541
|
+
import { homedir as homedir3 } from "node:os";
|
|
28542
|
+
import { join as join2, resolve } from "node:path";
|
|
28543
|
+
var RUNTIME_EVIDENCE_SCHEMA_VERSION = 1;
|
|
28544
|
+
function canonicalize(value) {
|
|
28545
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
28546
|
+
if (!value || typeof value !== "object") return value;
|
|
28547
|
+
return Object.fromEntries(
|
|
28548
|
+
Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, child]) => [key, canonicalize(child)])
|
|
28549
|
+
);
|
|
28550
|
+
}
|
|
28551
|
+
function canonicalConfigJson(config) {
|
|
28552
|
+
return JSON.stringify({ schemaVersion: RUNTIME_EVIDENCE_SCHEMA_VERSION, config: canonicalize(config) });
|
|
28553
|
+
}
|
|
28554
|
+
function configFingerprint(config, hmacKeyHex) {
|
|
28555
|
+
if (!/^[a-f0-9]{64}$/u.test(hmacKeyHex)) throw new Error("runtime evidence HMAC key is invalid");
|
|
28556
|
+
return createHmac("sha256", Buffer.from(hmacKeyHex, "hex")).update(canonicalConfigJson(config)).digest("hex");
|
|
28557
|
+
}
|
|
28558
|
+
function runtimeEvidenceDir(stateDir) {
|
|
28559
|
+
return resolve(stateDir ?? process.env.OPENCLAW_STATE_DIR ?? join2(homedir3(), ".openclaw"), "perkos-a2a");
|
|
28560
|
+
}
|
|
28561
|
+
function runtimeAttemptPath(stateDir) {
|
|
28562
|
+
return join2(runtimeEvidenceDir(stateDir), "active-attempt.json");
|
|
28563
|
+
}
|
|
28564
|
+
function runtimeStatusPath(stateDir) {
|
|
28565
|
+
return join2(runtimeEvidenceDir(stateDir), "runtime-status.json");
|
|
28566
|
+
}
|
|
28567
|
+
function configAgentId(config) {
|
|
28568
|
+
if (config.platform?.agentId) return config.platform.agentId;
|
|
28569
|
+
const match = config.platform?.heartbeatUrl?.match(/\/agents\/([^/]+)\/heartbeat/u);
|
|
28570
|
+
return match?.[1] ?? "unknown";
|
|
28571
|
+
}
|
|
28572
|
+
function recordRuntimeLoadEvidence(input) {
|
|
28573
|
+
const evidenceDir = runtimeEvidenceDir(input.stateDir);
|
|
28574
|
+
let attempt;
|
|
28575
|
+
try {
|
|
28576
|
+
attempt = JSON.parse(readFileSync2(runtimeAttemptPath(input.stateDir), "utf8"));
|
|
28577
|
+
} catch {
|
|
28578
|
+
input.logger.info(`[perkos-a2a] config loaded version=${input.version} agentId=${configAgentId(input.config)} attemptId=none`);
|
|
28579
|
+
return null;
|
|
28580
|
+
}
|
|
28581
|
+
const agentId = configAgentId(input.config);
|
|
28582
|
+
if (attempt.schemaVersion !== RUNTIME_EVIDENCE_SCHEMA_VERSION || attempt.targetVersion !== input.version || attempt.expectedAgentId !== agentId) {
|
|
28583
|
+
input.logger.warn?.("[perkos-a2a] runtime evidence attempt does not match the loaded plugin identity");
|
|
28584
|
+
return null;
|
|
28585
|
+
}
|
|
28586
|
+
const status = {
|
|
28587
|
+
schemaVersion: RUNTIME_EVIDENCE_SCHEMA_VERSION,
|
|
28588
|
+
loadedAt: (input.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
28589
|
+
version: input.version,
|
|
28590
|
+
agentId,
|
|
28591
|
+
fingerprint: configFingerprint(input.config, attempt.hmacKeyHex),
|
|
28592
|
+
attemptId: attempt.attemptId,
|
|
28593
|
+
pid: process.pid,
|
|
28594
|
+
pluginRoot: resolve(input.pluginRoot)
|
|
28595
|
+
};
|
|
28596
|
+
mkdirSync(evidenceDir, { recursive: true, mode: 448 });
|
|
28597
|
+
const target = runtimeStatusPath(input.stateDir);
|
|
28598
|
+
const temporary = `${target}.${process.pid}.tmp`;
|
|
28599
|
+
writeFileSync(temporary, `${JSON.stringify(status, null, 2)}
|
|
28600
|
+
`, { mode: 384 });
|
|
28601
|
+
renameSync(temporary, target);
|
|
28602
|
+
chmodSync(target, 384);
|
|
28603
|
+
input.logger.info(
|
|
28604
|
+
`[perkos-a2a] config loaded version=${status.version} agentId=${status.agentId} attemptId=${status.attemptId} fingerprint=${status.fingerprint}`
|
|
28605
|
+
);
|
|
28606
|
+
return status;
|
|
28607
|
+
}
|
|
28608
|
+
|
|
28529
28609
|
// src/types.ts
|
|
28530
28610
|
function isWebhookEvent(msg) {
|
|
28531
28611
|
return msg.type === "webhook_event";
|
|
28532
28612
|
}
|
|
28533
28613
|
|
|
28534
28614
|
// src/index.ts
|
|
28615
|
+
function loadedPluginVersion() {
|
|
28616
|
+
try {
|
|
28617
|
+
const manifest = JSON.parse(readFileSync3(new URL("../openclaw.plugin.json", import.meta.url), "utf8"));
|
|
28618
|
+
return typeof manifest.version === "string" ? manifest.version : "unknown";
|
|
28619
|
+
} catch {
|
|
28620
|
+
return "unknown";
|
|
28621
|
+
}
|
|
28622
|
+
}
|
|
28535
28623
|
function wakeGatewayAgent(requestHeartbeatNow, reason, logger, sessionKey = "agent:main") {
|
|
28536
28624
|
if (typeof requestHeartbeatNow === "function") {
|
|
28537
28625
|
try {
|
|
@@ -28791,6 +28879,13 @@ function register(api) {
|
|
|
28791
28879
|
peers: {}
|
|
28792
28880
|
};
|
|
28793
28881
|
const logger = api.logger || console;
|
|
28882
|
+
const pluginVersion = loadedPluginVersion();
|
|
28883
|
+
recordRuntimeLoadEvidence({
|
|
28884
|
+
config: pluginConfig,
|
|
28885
|
+
version: pluginVersion,
|
|
28886
|
+
pluginRoot: resolve2(dirname(fileURLToPath(import.meta.url)), ".."),
|
|
28887
|
+
logger
|
|
28888
|
+
});
|
|
28794
28889
|
const server = new A2AServer(pluginConfig, logger);
|
|
28795
28890
|
const pendingTasks = [];
|
|
28796
28891
|
const enqueueSystemEvent = api.runtime?.system?.enqueueSystemEvent;
|
|
@@ -29114,7 +29209,7 @@ function register(api) {
|
|
|
29114
29209
|
url: pluginConfig.platform?.heartbeatUrl,
|
|
29115
29210
|
relayKey: pluginConfig.relay?.apiKey,
|
|
29116
29211
|
runtimeKind: "openclaw",
|
|
29117
|
-
version:
|
|
29212
|
+
version: pluginVersion,
|
|
29118
29213
|
intervalMs: pluginConfig.platform?.heartbeatIntervalMs
|
|
29119
29214
|
});
|
|
29120
29215
|
},
|
|
@@ -29443,18 +29538,18 @@ function register(api) {
|
|
|
29443
29538
|
let portAvailable = true;
|
|
29444
29539
|
try {
|
|
29445
29540
|
const netMod = await import("net");
|
|
29446
|
-
await new Promise((
|
|
29541
|
+
await new Promise((resolve3, reject) => {
|
|
29447
29542
|
const srv = netMod.createServer();
|
|
29448
29543
|
srv.once("error", (err) => {
|
|
29449
29544
|
if (err.code === "EADDRINUSE") {
|
|
29450
29545
|
portAvailable = false;
|
|
29451
|
-
|
|
29546
|
+
resolve3();
|
|
29452
29547
|
} else {
|
|
29453
29548
|
reject(err);
|
|
29454
29549
|
}
|
|
29455
29550
|
});
|
|
29456
29551
|
srv.listen(pluginConfig.port, () => {
|
|
29457
|
-
srv.close(() =>
|
|
29552
|
+
srv.close(() => resolve3());
|
|
29458
29553
|
});
|
|
29459
29554
|
});
|
|
29460
29555
|
} catch {
|