@perkos/perkos-a2a 0.12.62 → 0.12.64
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 +2 -2
- package/dist/a2a-maintenance.d.ts +44 -0
- package/dist/a2a-maintenance.d.ts.map +1 -0
- package/dist/a2a-maintenance.js +145 -0
- package/dist/a2a-maintenance.js.map +1 -0
- package/dist/a2a-maintenance.test.d.ts +2 -0
- package/dist/a2a-maintenance.test.d.ts.map +1 -0
- package/dist/a2a-maintenance.test.js +60 -0
- package/dist/a2a-maintenance.test.js.map +1 -0
- package/dist/bridge-agent.js +19 -3
- package/dist/bridge-agent.js.map +1 -1
- package/dist/chat-client.d.ts +3 -0
- package/dist/chat-client.d.ts.map +1 -1
- package/dist/chat-client.js +9 -3
- package/dist/chat-client.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +170 -14
- package/dist/index.js.map +4 -4
- package/dist/maintenance-runner.d.ts +3 -0
- package/dist/maintenance-runner.d.ts.map +1 -0
- package/dist/maintenance-runner.js +80 -0
- package/dist/maintenance-runner.js.map +1 -0
- package/dist/platform-heartbeat.d.ts +6 -0
- package/dist/platform-heartbeat.d.ts.map +1 -1
- package/dist/platform-heartbeat.js +6 -1
- package/dist/platform-heartbeat.js.map +1 -1
- package/docs/plans/2026-09-02-chat-bound-maintenance-capability-design.md +36 -0
- package/docs/plans/2026-09-02-managed-a2a-self-update-design.md +66 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"maintenance-runner.d.ts","sourceRoot":"","sources":["../src/maintenance-runner.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { access, chmod, mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
function jobPath() {
|
|
8
|
+
const index = process.argv.indexOf("--job");
|
|
9
|
+
const path = index >= 0 ? process.argv[index + 1] : undefined;
|
|
10
|
+
if (!path)
|
|
11
|
+
throw new Error("job_missing");
|
|
12
|
+
return path;
|
|
13
|
+
}
|
|
14
|
+
async function atomicPrivateJson(path, value) {
|
|
15
|
+
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
16
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
17
|
+
await writeFile(temporary, `${JSON.stringify(value)}\n`, { mode: 0o600 });
|
|
18
|
+
await chmod(temporary, 0o600);
|
|
19
|
+
await rename(temporary, path);
|
|
20
|
+
}
|
|
21
|
+
async function report(job, body) {
|
|
22
|
+
const response = await fetch(`${job.apiBaseUrl}/agents/${encodeURIComponent(job.agentId)}/maintenance/a2a-update/${job.requestId}/report`, {
|
|
23
|
+
method: "POST",
|
|
24
|
+
headers: { authorization: `Bearer ${job.relayApiKey}`, "content-type": "application/json" },
|
|
25
|
+
body: JSON.stringify(body),
|
|
26
|
+
signal: AbortSignal.timeout(15_000),
|
|
27
|
+
}).catch(() => null);
|
|
28
|
+
return response?.ok === true;
|
|
29
|
+
}
|
|
30
|
+
async function resolveNpx() {
|
|
31
|
+
const candidates = [
|
|
32
|
+
join(dirname(process.execPath), "npx"),
|
|
33
|
+
"/opt/homebrew/bin/npx",
|
|
34
|
+
"/usr/local/bin/npx",
|
|
35
|
+
"/usr/bin/npx",
|
|
36
|
+
];
|
|
37
|
+
for (const candidate of candidates) {
|
|
38
|
+
if (await access(candidate).then(() => true).catch(() => false))
|
|
39
|
+
return candidate;
|
|
40
|
+
}
|
|
41
|
+
throw new Error("npx_unavailable");
|
|
42
|
+
}
|
|
43
|
+
async function main() {
|
|
44
|
+
const path = jobPath();
|
|
45
|
+
const job = JSON.parse(await readFile(path, "utf8"));
|
|
46
|
+
if (job.schemaVersion !== 1 || !/^[0-9a-f-]{36}$/i.test(job.requestId) || !/^\d+\.\d+\.\d+/.test(job.targetVersion)) {
|
|
47
|
+
throw new Error("job_invalid");
|
|
48
|
+
}
|
|
49
|
+
await report(job, { state: "running" });
|
|
50
|
+
let receipt;
|
|
51
|
+
try {
|
|
52
|
+
const result = await execFileAsync(await resolveNpx(), [
|
|
53
|
+
"--yes",
|
|
54
|
+
`@perkos/perkos-a2a@${job.targetVersion}`,
|
|
55
|
+
"update-hermes",
|
|
56
|
+
"--agent-id",
|
|
57
|
+
job.agentId,
|
|
58
|
+
"--json",
|
|
59
|
+
], { timeout: 8 * 60_000, maxBuffer: 1024 * 1024, encoding: "utf8" });
|
|
60
|
+
const lines = result.stdout.trim().split(/\r?\n/).filter(Boolean);
|
|
61
|
+
const parsed = JSON.parse(lines.at(-1) ?? "null");
|
|
62
|
+
if (!parsed || parsed.ok !== true || parsed.version !== job.targetVersion || parsed.secretsRedacted !== true) {
|
|
63
|
+
throw new Error("invalid_update_receipt");
|
|
64
|
+
}
|
|
65
|
+
receipt = { schemaVersion: 1, requestId: job.requestId, agentId: job.agentId, apiBaseUrl: job.apiBaseUrl, state: "completed", installedVersion: job.targetVersion };
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
receipt = { schemaVersion: 1, requestId: job.requestId, agentId: job.agentId, apiBaseUrl: job.apiBaseUrl, state: "failed", errorCode: "update_failed" };
|
|
69
|
+
}
|
|
70
|
+
const receiptPath = join(dirname(dirname(path)), "receipts", `${job.requestId}.json`);
|
|
71
|
+
await atomicPrivateJson(receiptPath, receipt);
|
|
72
|
+
if (await report(job, receipt.state === "completed"
|
|
73
|
+
? { state: "completed", installedVersion: receipt.installedVersion }
|
|
74
|
+
: { state: "failed", errorCode: receipt.errorCode })) {
|
|
75
|
+
await unlink(receiptPath);
|
|
76
|
+
}
|
|
77
|
+
await unlink(path).catch(() => undefined);
|
|
78
|
+
}
|
|
79
|
+
main().catch(() => { process.exitCode = 1; });
|
|
80
|
+
//# sourceMappingURL=maintenance-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"maintenance-runner.js","sourceRoot":"","sources":["../src/maintenance-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,8 +4,13 @@ export type PlatformHeartbeatLogger = {
|
|
|
4
4
|
};
|
|
5
5
|
export type PlatformHeartbeatHandle = {
|
|
6
6
|
stop(): void;
|
|
7
|
+
reportNow(): void;
|
|
7
8
|
};
|
|
8
9
|
export type PlatformRuntimeStatus = "healthy" | "unreachable" | "unknown";
|
|
10
|
+
export type PlatformMaintenanceCapability = {
|
|
11
|
+
protocolVersion: 1;
|
|
12
|
+
bridgeInstanceId: string;
|
|
13
|
+
};
|
|
9
14
|
/**
|
|
10
15
|
* Keep the PerkOS agent presence record fresh while the bridge is alive.
|
|
11
16
|
*
|
|
@@ -23,6 +28,7 @@ export declare function startPlatformHeartbeat(opts: {
|
|
|
23
28
|
relayKey?: string;
|
|
24
29
|
runtimeKind?: "openclaw" | "hermes" | "custom";
|
|
25
30
|
runtimeStatus?: PlatformRuntimeStatus | (() => PlatformRuntimeStatus | Promise<PlatformRuntimeStatus>);
|
|
31
|
+
maintenanceCapability?: PlatformMaintenanceCapability | null | (() => PlatformMaintenanceCapability | null);
|
|
26
32
|
version?: string;
|
|
27
33
|
onResult?: (result: {
|
|
28
34
|
ok: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform-heartbeat.d.ts","sourceRoot":"","sources":["../src/platform-heartbeat.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,IAAI,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"platform-heartbeat.d.ts","sourceRoot":"","sources":["../src/platform-heartbeat.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,IAAI,IAAI,CAAC;IACb,SAAS,IAAI,IAAI,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAAC;AAE1E,MAAM,MAAM,6BAA6B,GAAG;IAC1C,eAAe,EAAE,CAAC,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAKF;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,MAAM,EAAE,uBAAuB,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC/C,aAAa,CAAC,EAAE,qBAAqB,GAAG,CAAC,MAAM,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC;IACvG,qBAAqB,CAAC,EAAE,6BAA6B,GAAG,IAAI,GAAG,CAAC,MAAM,6BAA6B,GAAG,IAAI,CAAC,CAAC;IAC5G,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAClF,GAAG,uBAAuB,CAqF1B"}
|
|
@@ -14,7 +14,7 @@ export function startPlatformHeartbeat(opts) {
|
|
|
14
14
|
const url = opts.url?.trim() || env.PERKOS_HEARTBEAT_URL?.trim();
|
|
15
15
|
const relayKey = opts.relayKey?.trim() || env.A2A_RELAY_API_KEY?.trim();
|
|
16
16
|
if (!url || !relayKey)
|
|
17
|
-
return { stop: () => undefined };
|
|
17
|
+
return { stop: () => undefined, reportNow: () => undefined };
|
|
18
18
|
const configuredInterval = Number(opts.intervalMs ?? env.PERKOS_HEARTBEAT_INTERVAL_MS ?? DEFAULT_INTERVAL_MS);
|
|
19
19
|
const intervalMs = Number.isFinite(configuredInterval)
|
|
20
20
|
? Math.max(MIN_INTERVAL_MS, configuredInterval)
|
|
@@ -36,6 +36,9 @@ export function startPlatformHeartbeat(opts) {
|
|
|
36
36
|
const runtimeStatus = typeof opts.runtimeStatus === "function"
|
|
37
37
|
? await opts.runtimeStatus()
|
|
38
38
|
: opts.runtimeStatus ?? "unknown";
|
|
39
|
+
const maintenanceCapability = typeof opts.maintenanceCapability === "function"
|
|
40
|
+
? opts.maintenanceCapability()
|
|
41
|
+
: opts.maintenanceCapability ?? null;
|
|
39
42
|
const res = await fetcher(url, {
|
|
40
43
|
method: "POST",
|
|
41
44
|
headers: {
|
|
@@ -48,6 +51,7 @@ export function startPlatformHeartbeat(opts) {
|
|
|
48
51
|
version: opts.version?.trim() || env.PERKOS_A2A_VERSION?.trim() || "0.12.51",
|
|
49
52
|
ts: Date.now(),
|
|
50
53
|
runtimeStatus,
|
|
54
|
+
...(opts.maintenanceCapability !== undefined ? { maintenanceCapability } : {}),
|
|
51
55
|
}),
|
|
52
56
|
});
|
|
53
57
|
if (res.ok) {
|
|
@@ -72,6 +76,7 @@ export function startPlatformHeartbeat(opts) {
|
|
|
72
76
|
const timer = setInterval(() => void report(), intervalMs);
|
|
73
77
|
timer.unref?.();
|
|
74
78
|
return {
|
|
79
|
+
reportNow: () => void report(),
|
|
75
80
|
stop: () => {
|
|
76
81
|
stopped = true;
|
|
77
82
|
clearInterval(timer);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform-heartbeat.js","sourceRoot":"","sources":["../src/platform-heartbeat.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"platform-heartbeat.js","sourceRoot":"","sources":["../src/platform-heartbeat.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Chat-bound maintenance capability
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
The platform inferred managed-update support from the reported A2A package
|
|
6
|
+
version. A bridge could therefore publish `0.12.63` while another legacy
|
|
7
|
+
process owned the active Chat registration. The App offered an update, but the
|
|
8
|
+
marker remained pending because no active consumer claimed it.
|
|
9
|
+
|
|
10
|
+
## Decision
|
|
11
|
+
|
|
12
|
+
Managed maintenance is fail-closed and requires all of the following:
|
|
13
|
+
|
|
14
|
+
1. Hermes runtime and A2A package `>=0.12.64`.
|
|
15
|
+
2. Protocol-v1 capability in a recent heartbeat.
|
|
16
|
+
3. A UUID `bridgeInstanceId` generated once per bridge process.
|
|
17
|
+
4. An authenticated Chat connection owned by that same process.
|
|
18
|
+
5. A request and claim addressed to the same `bridgeInstanceId`.
|
|
19
|
+
|
|
20
|
+
The standalone bridge reports capability immediately after Chat authentication
|
|
21
|
+
and revokes it immediately after disconnect. The API leases the evidence for
|
|
22
|
+
125 seconds using server time. Every ordinary heartbeat without the capability
|
|
23
|
+
clears it. The App only renders the managed action while that lease is valid.
|
|
24
|
+
|
|
25
|
+
## Failure behavior
|
|
26
|
+
|
|
27
|
+
- A compatible semver without fresh capability shows bootstrap required.
|
|
28
|
+
- A duplicate or superseded process cannot claim a marker addressed elsewhere.
|
|
29
|
+
- Expired capability prevents request creation with `BOOTSTRAP_REQUIRED`.
|
|
30
|
+
- Voice remains an independent optional capability.
|
|
31
|
+
|
|
32
|
+
## Migration
|
|
33
|
+
|
|
34
|
+
Version `0.12.63` cannot advertise this stronger contract. Existing Hermes
|
|
35
|
+
agents need the one-time `update-hermes` bootstrap to `0.12.64`; subsequent
|
|
36
|
+
updates can use the managed marker flow.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Managed A2A self-update
|
|
2
|
+
|
|
3
|
+
## Decision
|
|
4
|
+
|
|
5
|
+
Replace the model-facing `Update integration` prompt with a platform-managed,
|
|
6
|
+
one-time maintenance request. The model must never be asked to execute npm or
|
|
7
|
+
shell commands. PerkOS App creates the request through the authenticated API,
|
|
8
|
+
sends only an exact opaque marker through the existing agent chat, and polls
|
|
9
|
+
the API for the durable result.
|
|
10
|
+
|
|
11
|
+
The installed A2A bridge intercepts the marker before model delivery, claims
|
|
12
|
+
the request using its existing relay credential, acknowledges it, and launches
|
|
13
|
+
a detached updater. The updater reuses the managed Hermes identity and
|
|
14
|
+
configuration, reports a fixed-schema result to the API, and never includes a
|
|
15
|
+
credential, command output, or arbitrary model text in the response.
|
|
16
|
+
|
|
17
|
+
## Protocol
|
|
18
|
+
|
|
19
|
+
1. An authenticated owner creates an update request for an invited agent.
|
|
20
|
+
2. The API chooses the allow-listed target package version, stores a random
|
|
21
|
+
request id with a short expiry, and returns `PERKOS_A2A_UPDATE:<requestId>`.
|
|
22
|
+
3. A compatible bridge validates the exact marker and claims the request with
|
|
23
|
+
its relay API key. A Firestore transaction makes this claim single-use.
|
|
24
|
+
4. The bridge returns a fixed acknowledgement and starts a detached Hermes
|
|
25
|
+
updater from a mode-0600 local job file.
|
|
26
|
+
5. The updater runs the existing identity-preserving `update-hermes` flow and
|
|
27
|
+
reports `completed` or `failed` to the API with redacted, bounded fields.
|
|
28
|
+
6. PerkOS App polls the request resource and renders the durable state:
|
|
29
|
+
`pending`, `claimed`, `running`, `completed`, `failed`, or `expired`.
|
|
30
|
+
|
|
31
|
+
The opaque marker is not an authorization credential. Claim and report calls
|
|
32
|
+
still require the agent's relay key, and the API verifies that the request is
|
|
33
|
+
pending, unexpired, and belongs to that exact agent. Target npm specs are never
|
|
34
|
+
accepted from either the App or the agent.
|
|
35
|
+
|
|
36
|
+
## Compatibility and fallback
|
|
37
|
+
|
|
38
|
+
Bridges released before this protocol cannot intercept the marker. The UI must
|
|
39
|
+
therefore expose managed update only when runtime evidence advertises support.
|
|
40
|
+
Older or unknown installations show `Bootstrap required` and a concise manual
|
|
41
|
+
owner instruction. The fallback must not claim that an automatic update was
|
|
42
|
+
started.
|
|
43
|
+
|
|
44
|
+
The first implementation supports managed Hermes installations. Voice remains
|
|
45
|
+
an independent optional capability: A2A update availability must not imply
|
|
46
|
+
Voice support, installation, or enrollment.
|
|
47
|
+
|
|
48
|
+
## Failure handling
|
|
49
|
+
|
|
50
|
+
- Requests expire and are idempotent after a terminal state.
|
|
51
|
+
- Duplicate claims return the existing state without launching another update.
|
|
52
|
+
- The detached updater writes no secret to argv, logs, chat, or API results.
|
|
53
|
+
- A restart or lost chat reply does not lose the result because API state is
|
|
54
|
+
authoritative.
|
|
55
|
+
- Unsupported runtimes fail closed with a fixed error code.
|
|
56
|
+
- The UI stops polling on terminal state or expiry and permits an owner retry
|
|
57
|
+
by creating a new request.
|
|
58
|
+
|
|
59
|
+
## Verification
|
|
60
|
+
|
|
61
|
+
API tests cover ownership, relay authentication, expiry, single-use claims,
|
|
62
|
+
idempotent reports, and target-version allow-listing. A2A tests cover strict
|
|
63
|
+
marker parsing, model bypass, secure job creation, detached launch, redaction,
|
|
64
|
+
and result reporting. App tests cover supported and bootstrap-required states,
|
|
65
|
+
request dispatch, polling, terminal rendering, retry, localization, and the
|
|
66
|
+
absence of shell commands in chat prompts.
|
package/openclaw.plugin.json
CHANGED