@perkos/perkos-a2a 0.12.67 → 0.12.68

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.
@@ -0,0 +1,19 @@
1
+ export declare const MAINTENANCE_ERRORS: readonly ["npx_unavailable", "npx_exec_failed", "invalid_update_receipt", "update_failed", "managed_install_not_found", "managed_config_invalid", "managed_identity_mismatch", "bridge_install_missing", "bridge_version_mismatch"];
2
+ export type MaintenanceError = typeof MAINTENANCE_ERRORS[number];
3
+ export type MaintenanceResult = {
4
+ schemaVersion: 2;
5
+ state: "completed";
6
+ installedVersion: string;
7
+ } | {
8
+ schemaVersion: 2;
9
+ state: "failed";
10
+ errorCode: MaintenanceError;
11
+ };
12
+ export declare const TARGET_VERSION: RegExp;
13
+ export declare const REQUEST_ID: RegExp;
14
+ export declare function safeMaintenanceError(value: unknown): MaintenanceError;
15
+ /** Construct a new allowlisted object; never replay raw child output or legacy metadata. */
16
+ export declare function parseMaintenanceResult(value: unknown): MaintenanceResult | null;
17
+ export declare function maintenanceReportBody(result: MaintenanceResult): Record<string, string>;
18
+ export declare function atomicPrivateJson(path: string, value: unknown): Promise<void>;
19
+ //# sourceMappingURL=maintenance-result.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"maintenance-result.d.ts","sourceRoot":"","sources":["../src/maintenance-result.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,kBAAkB,qOAIrB,CAAC;AACX,MAAM,MAAM,gBAAgB,GAAG,OAAO,kBAAkB,CAAC,MAAM,CAAC,CAAC;AACjE,MAAM,MAAM,iBAAiB,GACzB;IAAE,aAAa,EAAE,CAAC,CAAC;IAAC,KAAK,EAAE,WAAW,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAA;CAAE,GAClE;IAAE,aAAa,EAAE,CAAC,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAEvE,eAAO,MAAM,cAAc,QAAwC,CAAC;AACpE,eAAO,MAAM,UAAU,QAA+E,CAAC;AAEvG,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAGrE;AAED,4FAA4F;AAC5F,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,iBAAiB,GAAG,IAAI,CAQ/E;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIvF;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAMnF"}
@@ -0,0 +1,38 @@
1
+ import { chmod, mkdir, rename, writeFile } from "node:fs/promises";
2
+ import { dirname } from "node:path";
3
+ export const MAINTENANCE_ERRORS = [
4
+ "npx_unavailable", "npx_exec_failed", "invalid_update_receipt", "update_failed",
5
+ "managed_install_not_found", "managed_config_invalid", "managed_identity_mismatch",
6
+ "bridge_install_missing", "bridge_version_mismatch",
7
+ ];
8
+ export const TARGET_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
9
+ export const REQUEST_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
10
+ export function safeMaintenanceError(value) {
11
+ return typeof value === "string" && MAINTENANCE_ERRORS.includes(value)
12
+ ? value : "update_failed";
13
+ }
14
+ /** Construct a new allowlisted object; never replay raw child output or legacy metadata. */
15
+ export function parseMaintenanceResult(value) {
16
+ if (!value || typeof value !== "object")
17
+ return null;
18
+ const v = value;
19
+ if (v.state === "completed" && typeof v.installedVersion === "string" && TARGET_VERSION.test(v.installedVersion)) {
20
+ return { schemaVersion: 2, state: "completed", installedVersion: v.installedVersion };
21
+ }
22
+ if (v.state === "failed")
23
+ return { schemaVersion: 2, state: "failed", errorCode: safeMaintenanceError(v.errorCode) };
24
+ return null;
25
+ }
26
+ export function maintenanceReportBody(result) {
27
+ return result.state === "completed"
28
+ ? { state: result.state, installedVersion: result.installedVersion }
29
+ : { state: result.state, errorCode: result.errorCode };
30
+ }
31
+ export async function atomicPrivateJson(path, value) {
32
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
33
+ const temporary = `${path}.${process.pid}.tmp`;
34
+ await writeFile(temporary, `${JSON.stringify(value)}\n`, { mode: 0o600 });
35
+ await chmod(temporary, 0o600);
36
+ await rename(temporary, path);
37
+ }
38
+ //# sourceMappingURL=maintenance-result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"maintenance-result.js","sourceRoot":"","sources":["../src/maintenance-result.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,iBAAiB,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,eAAe;IAC/E,2BAA2B,EAAE,wBAAwB,EAAE,2BAA2B;IAClF,wBAAwB,EAAE,yBAAyB;CAC3C,CAAC;AAMX,MAAM,CAAC,MAAM,cAAc,GAAG,qCAAqC,CAAC;AACpE,MAAM,CAAC,MAAM,UAAU,GAAG,4EAA4E,CAAC;AAEvG,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,kBAAwC,CAAC,QAAQ,CAAC,KAAK,CAAC;QAC3F,CAAC,CAAC,KAAyB,CAAC,CAAC,CAAC,eAAe,CAAC;AAClD,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,sBAAsB,CAAC,KAAc;IACnD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACrD,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,IAAI,CAAC,CAAC,KAAK,KAAK,WAAW,IAAI,OAAO,CAAC,CAAC,gBAAgB,KAAK,QAAQ,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACjH,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC,CAAC,gBAAgB,EAAE,CAAC;IACxF,CAAC;IACD,IAAI,CAAC,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,oBAAoB,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;IACrH,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAAyB;IAC7D,OAAO,MAAM,CAAC,KAAK,KAAK,WAAW;QACjC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,EAAE;QACpE,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC;AAC3D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY,EAAE,KAAc;IAClE,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,MAAM,SAAS,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;IAC/C,MAAM,SAAS,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1E,MAAM,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC9B,MAAM,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AAChC,CAAC"}
@@ -1,3 +1,18 @@
1
1
  #!/usr/bin/env node
2
+ import type { MaintenanceJob } from "./a2a-maintenance.js";
3
+ import { type MaintenanceResult } from "./maintenance-result.js";
4
+ type NpxCommand = {
5
+ file: string;
6
+ cli: string;
7
+ env: NodeJS.ProcessEnv;
8
+ };
9
+ /** Resolve and probe the CLI with the same Node and environment used for execution. */
10
+ export declare function resolveNpx(nodePath?: string, env?: NodeJS.ProcessEnv, candidates?: string[]): Promise<NpxCommand>;
11
+ export declare function executeManagedUpdate(job: MaintenanceJob, command?: NpxCommand): Promise<MaintenanceResult>;
12
+ declare function report(job: MaintenanceJob, body: Record<string, string>): Promise<boolean>;
13
+ export declare function runMaintenanceJob(path: string, deps?: {
14
+ execute?: typeof executeManagedUpdate;
15
+ report?: typeof report;
16
+ }): Promise<void>;
2
17
  export {};
3
18
  //# sourceMappingURL=maintenance-runner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"maintenance-runner.d.ts","sourceRoot":"","sources":["../src/maintenance-runner.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"maintenance-runner.d.ts","sourceRoot":"","sources":["../src/maintenance-runner.ts"],"names":[],"mappings":";AAMA,OAAO,KAAK,EAAE,cAAc,EAAsB,MAAM,sBAAsB,CAAC;AAE/E,OAAO,EACL,KAAK,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAG1D,KAAK,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,CAAC;AAExE,uFAAuF;AACvF,wBAAsB,UAAU,CAC9B,QAAQ,SAAmB,EAC3B,GAAG,oBAA+B,EAClC,UAAU,WAAkG,GAC3G,OAAO,CAAC,UAAU,CAAC,CAkBrB;AASD,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAyBhH;AAED,iBAAe,MAAM,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAOzF;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE;IAC1D,OAAO,CAAC,EAAE,OAAO,oBAAoB,CAAC;IACtC,MAAM,CAAC,EAAE,OAAO,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBrB"}
@@ -1,80 +1,105 @@
1
1
  #!/usr/bin/env node
2
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";
3
+ import { readFile, realpath, unlink } from "node:fs/promises";
4
+ import { dirname, join, resolve } from "node:path";
5
+ import { pathToFileURL } from "node:url";
5
6
  import { promisify } from "node:util";
7
+ import { serviceEnvironment } from "./service-environment.js";
8
+ import { atomicPrivateJson, maintenanceReportBody, REQUEST_ID, safeMaintenanceError, TARGET_VERSION } from "./maintenance-result.js";
6
9
  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
- ];
10
+ /** Resolve and probe the CLI with the same Node and environment used for execution. */
11
+ export async function resolveNpx(nodePath = process.execPath, env = serviceEnvironment(nodePath), candidates = [join(dirname(nodePath), "npx"), "/opt/homebrew/bin/npx", "/usr/local/bin/npx", "/usr/bin/npx"]) {
12
+ let found = false;
37
13
  for (const candidate of candidates) {
38
- if (await access(candidate).then(() => true).catch(() => false))
39
- return candidate;
14
+ const cli = await realpath(candidate).catch(() => null);
15
+ if (!cli)
16
+ continue;
17
+ found = true;
18
+ try {
19
+ // A shell wrapper fails this probe rather than being passed to a shell.
20
+ const result = await execFileAsync(nodePath, [cli, "--version"], {
21
+ env, timeout: 15_000, maxBuffer: 64 * 1024, encoding: "utf8",
22
+ });
23
+ if (!/^\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(result.stdout.trim()))
24
+ continue;
25
+ return { file: nodePath, cli, env };
26
+ }
27
+ catch {
28
+ // Never propagate raw command errors: they can include private paths.
29
+ }
40
30
  }
41
- throw new Error("npx_unavailable");
31
+ throw new Error(found ? "npx_exec_failed" : "npx_unavailable");
42
32
  }
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");
33
+ function lastJson(stdout) {
34
+ try {
35
+ const parsed = JSON.parse(stdout.trim().split(/\r?\n/).filter(Boolean).at(-1) ?? "null");
36
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
37
+ }
38
+ catch {
39
+ return null;
48
40
  }
49
- await report(job, { state: "running" });
50
- let receipt;
41
+ }
42
+ export async function executeManagedUpdate(job, command) {
51
43
  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");
44
+ const npx = command ?? await resolveNpx();
45
+ let stdout;
46
+ try {
47
+ const result = await execFileAsync(npx.file, [npx.cli,
48
+ "--yes", `@perkos/perkos-a2a@${job.targetVersion}`, "update-hermes", "--agent-id", job.agentId, "--json",
49
+ ], { env: npx.env, timeout: 8 * 60_000, maxBuffer: 1024 * 1024, encoding: "utf8" });
50
+ stdout = result.stdout;
51
+ }
52
+ catch (error) {
53
+ // Preserve only explicitly recognized installer codes, never stdout/stderr.
54
+ const output = error.stdout;
55
+ const parsed = typeof output === "string" ? lastJson(output) : null;
56
+ const code = typeof parsed?.acceptanceBlockedReason === "string"
57
+ ? safeMaintenanceError(parsed.acceptanceBlockedReason.toLowerCase()) : "update_failed";
58
+ throw new Error(code !== "update_failed" ? code : "npx_exec_failed");
59
+ }
60
+ const parsed = lastJson(stdout);
62
61
  if (!parsed || parsed.ok !== true || parsed.version !== job.targetVersion || parsed.secretsRedacted !== true) {
63
62
  throw new Error("invalid_update_receipt");
64
63
  }
65
- receipt = { schemaVersion: 1, requestId: job.requestId, agentId: job.agentId, apiBaseUrl: job.apiBaseUrl, state: "completed", installedVersion: job.targetVersion };
64
+ return { schemaVersion: 2, state: "completed", installedVersion: job.targetVersion };
66
65
  }
67
- catch {
68
- receipt = { schemaVersion: 1, requestId: job.requestId, agentId: job.agentId, apiBaseUrl: job.apiBaseUrl, state: "failed", errorCode: "update_failed" };
66
+ catch (error) {
67
+ return { schemaVersion: 2, state: "failed", errorCode: safeMaintenanceError(error instanceof Error ? error.message : null) };
68
+ }
69
+ }
70
+ async function report(job, body) {
71
+ const response = await fetch(`${job.apiBaseUrl}/agents/${encodeURIComponent(job.agentId)}/maintenance/a2a-update/${job.requestId}/report`, { method: "POST", headers: { authorization: `Bearer ${job.relayApiKey}`, "content-type": "application/json" },
72
+ body: JSON.stringify(body), signal: AbortSignal.timeout(15_000) }).catch(() => null);
73
+ return response?.ok === true;
74
+ }
75
+ export async function runMaintenanceJob(path, deps = {}) {
76
+ const job = JSON.parse(await readFile(path, "utf8"));
77
+ if (job.schemaVersion !== 1 || !REQUEST_ID.test(job.requestId) || !TARGET_VERSION.test(job.targetVersion)
78
+ || typeof job.agentId !== "string" || typeof job.apiBaseUrl !== "string" || typeof job.relayApiKey !== "string") {
79
+ throw new Error("job_invalid");
69
80
  }
70
- const receiptPath = join(dirname(dirname(path)), "receipts", `${job.requestId}.json`);
81
+ const sendReport = deps.report ?? report;
82
+ await sendReport(job, { state: "running" }).catch(() => false);
83
+ const receipt = await (deps.execute ?? executeManagedUpdate)(job).catch(() => ({ schemaVersion: 2, state: "failed", errorCode: "update_failed" }));
84
+ const root = dirname(dirname(path));
85
+ const receiptPath = join(root, "receipts", `${job.requestId}.json`);
86
+ const routingPath = join(root, "pending-reports", `${job.requestId}.json`);
87
+ const routing = { schemaVersion: 1, requestId: job.requestId,
88
+ agentId: job.agentId, apiBaseUrl: job.apiBaseUrl };
89
+ // Routing is private and separate. Results never contain raw subprocess output.
90
+ await atomicPrivateJson(routingPath, routing);
71
91
  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);
92
+ await unlink(path); // Discard the job credential once the result is durable.
93
+ if (await sendReport(job, maintenanceReportBody(receipt)).catch(() => false)) {
94
+ await unlink(receiptPath).catch(() => undefined);
95
+ await unlink(routingPath).catch(() => undefined);
76
96
  }
77
- await unlink(path).catch(() => undefined);
78
97
  }
79
- main().catch(() => { process.exitCode = 1; });
98
+ // Importable for tests; only the actual detached CLI runs a job.
99
+ if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
100
+ const index = process.argv.indexOf("--job");
101
+ const path = index >= 0 ? process.argv[index + 1] : undefined;
102
+ (path ? runMaintenanceJob(path) : Promise.reject(new Error("job_missing")))
103
+ .catch(() => { process.exitCode = 1; });
104
+ }
80
105
  //# sourceMappingURL=maintenance-runner.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"maintenance-runner.js","sourceRoot":"","sources":["../src/maintenance-runner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"maintenance-runner.js","sourceRoot":"","sources":["../src/maintenance-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=maintenance-runner.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"maintenance-runner.test.d.ts","sourceRoot":"","sources":["../src/maintenance-runner.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,153 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { execFile } from "node:child_process";
3
+ import { chmod, mkdir, mkdtemp, readFile, readdir, realpath, rm, stat, symlink, writeFile } from "node:fs/promises";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { promisify } from "node:util";
7
+ import { executeManagedUpdate, resolveNpx, runMaintenanceJob } from "./maintenance-runner.js";
8
+ import { flushMaintenanceReceipts } from "./a2a-maintenance.js";
9
+ import { atomicPrivateJson, safeMaintenanceError } from "./maintenance-result.js";
10
+ import { serviceEnvironment } from "./service-environment.js";
11
+ const exec = promisify(execFile);
12
+ const job = { schemaVersion: 1, requestId: "1e1719e8-7e50-4dad-a7cf-754a86699d7d",
13
+ agentId: "fixture-agent", targetVersion: "0.12.68", apiBaseUrl: "https://dev.example.test", relayApiKey: "fixture-secret" };
14
+ const config = { agentName: "fixture", port: 5050, skills: [], peers: {},
15
+ runtime: { kind: "hermes-api" }, relay: { enabled: true, url: "wss://relay.example.test", apiKey: job.relayApiKey },
16
+ platform: { agentId: job.agentId, heartbeatUrl: `${job.apiBaseUrl}/agents/${job.agentId}/heartbeat` } };
17
+ let root;
18
+ let node;
19
+ let npx;
20
+ beforeEach(async () => {
21
+ root = await mkdtemp(join(tmpdir(), "a2a-maintenance-test-"));
22
+ node = join(root, "bin", "node");
23
+ npx = join(root, "bin", "npx");
24
+ await mkdir(join(root, "bin"));
25
+ await symlink(process.execPath, node);
26
+ });
27
+ afterEach(async () => { vi.unstubAllGlobals(); vi.unstubAllEnvs(); await rm(root, { recursive: true, force: true }); });
28
+ async function cli(body) {
29
+ await writeFile(npx, `#!/usr/bin/env node\n${body}\n`);
30
+ await chmod(npx, 0o755);
31
+ }
32
+ function env() { return serviceEnvironment(node, { PATH: "/usr/bin:/bin:/usr/sbin:/sbin" }); }
33
+ const completed = { schemaVersion: 2, state: "completed", installedVersion: job.targetVersion };
34
+ const failed = { schemaVersion: 2, state: "failed", errorCode: "npx_exec_failed" };
35
+ describe("managed runner service environment", () => {
36
+ it("preserves env while prepending Node, removing relative/empty PATH entries and duplicates", () => {
37
+ expect(serviceEnvironment("/opt/node/bin/node", { PATH: ":.:/usr/bin:/opt/node/bin", KEEP: "yes" }))
38
+ .toEqual({ KEEP: "yes", PATH: "/opt/node/bin:/usr/bin:/bin:/usr/sbin:/sbin" });
39
+ expect(serviceEnvironment("/opt/node/bin/node", {}).PATH).toBe("/opt/node/bin:/usr/bin:/bin:/usr/sbin:/sbin");
40
+ });
41
+ it("reproduces env-node exit 127, then completes using absolute Node and the corrected child PATH", async () => {
42
+ // npm also starts child executables with env-node shebangs. Exercise both levels.
43
+ await cli(`const {execFileSync}=require('node:child_process');
44
+ execFileSync('node',['--version'],{env:process.env});
45
+ if(process.argv.includes('--version')) console.log('10.9.4');
46
+ else {
47
+ const expected=['--yes','@perkos/perkos-a2a@0.12.68','update-hermes','--agent-id','fixture-agent','--json'];
48
+ if(JSON.stringify(process.argv.slice(2))!==JSON.stringify(expected)) process.exit(9);
49
+ console.log(JSON.stringify({ok:true,version:'0.12.68',secretsRedacted:true}));
50
+ }`);
51
+ // Use an empty absolute directory for a deterministic baseline on Linux too,
52
+ // where /usr/bin/node may exist unlike the incident's macOS service.
53
+ const absent = join(root, "without-node");
54
+ await mkdir(absent);
55
+ await expect(exec(npx, ["--version"], { env: { PATH: absent } })).rejects.toMatchObject({ code: 127 });
56
+ const command = await resolveNpx(node, env(), [npx]);
57
+ expect(command.file).toBe(node);
58
+ expect(command.env.PATH).toBe(env().PATH);
59
+ await expect(executeManagedUpdate(job, command)).resolves.toEqual(completed);
60
+ });
61
+ it("distinguishes absent CLI from an existing but unrunnable CLI", async () => {
62
+ await expect(resolveNpx(node, env(), [npx])).rejects.toThrow("npx_unavailable");
63
+ await cli("process.exit(127)");
64
+ await expect(resolveNpx(node, env(), [npx])).rejects.toThrow("npx_exec_failed");
65
+ });
66
+ it("continues to a runnable candidate when an earlier CLI fails its probe", async () => {
67
+ await cli("console.log('10.9.4')");
68
+ const broken = join(root, "broken");
69
+ await writeFile(broken, "not javascript");
70
+ expect((await resolveNpx(node, env(), [broken, npx])).cli).toBe(await realpath(npx));
71
+ });
72
+ it("reports execution failure without exposing child output or touching the prior install", async () => {
73
+ const installed = join(root, "installed-version");
74
+ await writeFile(installed, "0.12.66");
75
+ await cli("if(process.argv.includes('--version')) console.log('10.9.4'); else {console.error('fixture-secret /private/fixture');process.exit(127)}");
76
+ const command = await resolveNpx(node, env(), [npx]);
77
+ await expect(executeManagedUpdate(job, command)).resolves.toEqual(failed);
78
+ expect(await readFile(installed, "utf8")).toBe("0.12.66");
79
+ });
80
+ it.each(["not-json", "null", "[]", '{"ok":true,"version":"0.12.66","secretsRedacted":true}',
81
+ '{"ok":true,"version":"0.12.68"}'])("rejects invalid/mismatching receipt %s", async (output) => {
82
+ await cli(`console.log(${JSON.stringify(output)})`);
83
+ await expect(executeManagedUpdate(job, { file: node, cli: npx, env: env() }))
84
+ .resolves.toEqual({ schemaVersion: 2, state: "failed", errorCode: "invalid_update_receipt" });
85
+ });
86
+ it("keeps only known installer codes and uses a safe last resort", async () => {
87
+ await cli("console.log(JSON.stringify({acceptanceBlockedReason:'MANAGED_IDENTITY_MISMATCH',errors:['fixture-secret']}));process.exit(1)");
88
+ await expect(executeManagedUpdate(job, { file: node, cli: npx, env: env() }))
89
+ .resolves.toEqual({ schemaVersion: 2, state: "failed", errorCode: "managed_identity_mismatch" });
90
+ expect(safeMaintenanceError("/private/fixture")).toBe("update_failed");
91
+ });
92
+ });
93
+ describe("durable sanitized maintenance results", () => {
94
+ const filename = `${job.requestId}.json`;
95
+ async function run(report = vi.fn().mockResolvedValue(false)) {
96
+ const path = join(root, "jobs", filename);
97
+ await atomicPrivateJson(path, job);
98
+ await runMaintenanceJob(path, { execute: async () => failed, report });
99
+ return report;
100
+ }
101
+ it("retains only a sanitized private result after failed report and removes the credential-bearing job", async () => {
102
+ const report = await run();
103
+ const path = join(root, "receipts", filename);
104
+ expect(JSON.parse(await readFile(path, "utf8"))).toEqual(failed);
105
+ expect((await stat(path)).mode & 0o777).toBe(0o600);
106
+ expect((await stat(join(root, "receipts"))).mode & 0o777).toBe(0o700);
107
+ expect(await readdir(join(root, "jobs"))).toEqual([]);
108
+ expect(await readFile(join(root, "pending-reports", filename), "utf8")).not.toContain(job.relayApiKey);
109
+ expect(report.mock.calls.at(-1)?.[1]).toEqual({ state: "failed", errorCode: "npx_exec_failed" });
110
+ });
111
+ it("cleans the result and routing after a successful terminal report", async () => {
112
+ await run(vi.fn().mockResolvedValue(true));
113
+ expect(await readdir(join(root, "receipts"))).toEqual([]);
114
+ expect(await readdir(join(root, "pending-reports"))).toEqual([]);
115
+ });
116
+ it("replays after restart with current authentication, retaining the result while offline", async () => {
117
+ await run();
118
+ const fetchMock = vi.fn().mockRejectedValue(new Error("offline"));
119
+ vi.stubGlobal("fetch", fetchMock);
120
+ await flushMaintenanceReceipts(config, root);
121
+ expect(await readdir(join(root, "receipts"))).toEqual([filename]);
122
+ fetchMock.mockResolvedValue({ ok: true, json: async () => ({ ok: true }) });
123
+ await flushMaintenanceReceipts(config, root);
124
+ expect(JSON.parse(fetchMock.mock.calls.at(-1)?.[1].body)).toEqual({ state: "failed", errorCode: "npx_exec_failed" });
125
+ expect(await readdir(join(root, "receipts"))).toEqual([]);
126
+ });
127
+ it("never replays into a different environment or identity", async () => {
128
+ await run();
129
+ const fetchMock = vi.fn();
130
+ vi.stubGlobal("fetch", fetchMock);
131
+ await flushMaintenanceReceipts({ ...config, platform: { agentId: job.agentId,
132
+ heartbeatUrl: `https://prod.example.test/agents/${job.agentId}/heartbeat` } }, root);
133
+ await flushMaintenanceReceipts({ ...config, platform: { agentId: "other-fixture",
134
+ heartbeatUrl: `${job.apiBaseUrl}/agents/other-fixture/heartbeat` } }, root);
135
+ expect(fetchMock).not.toHaveBeenCalled();
136
+ expect(await readdir(join(root, "receipts"))).toEqual([filename]);
137
+ });
138
+ it("migrates legacy receipts before reporting, stripping unknown errors and metadata even offline", async () => {
139
+ await atomicPrivateJson(join(root, "receipts", filename), { ...job, state: "failed", errorCode: "fixture-secret" });
140
+ vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("offline")));
141
+ await flushMaintenanceReceipts(config, root);
142
+ expect(JSON.parse(await readFile(join(root, "receipts", filename), "utf8")))
143
+ .toEqual({ schemaVersion: 2, state: "failed", errorCode: "update_failed" });
144
+ });
145
+ it("does not execute malformed jobs or permit a non-exact target", async () => {
146
+ const path = join(root, "jobs", filename);
147
+ const execute = vi.fn();
148
+ await atomicPrivateJson(path, { ...job, targetVersion: "0.12.68 --extra" });
149
+ await expect(runMaintenanceJob(path, { execute })).rejects.toThrow("job_invalid");
150
+ expect(execute).not.toHaveBeenCalled();
151
+ });
152
+ });
153
+ //# sourceMappingURL=maintenance-runner.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"maintenance-runner.test.js","sourceRoot":"","sources":["../src/maintenance-runner.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /** launchd does not inherit an interactive shell's Node installation. */
2
+ export declare function serviceEnvironment(nodePath?: string, inherited?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
3
+ //# sourceMappingURL=service-environment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-environment.d.ts","sourceRoot":"","sources":["../src/service-environment.ts"],"names":[],"mappings":"AAEA,yEAAyE;AACzE,wBAAgB,kBAAkB,CAChC,QAAQ,SAAmB,EAC3B,SAAS,GAAE,MAAM,CAAC,UAAwB,GACzC,MAAM,CAAC,UAAU,CAInB"}
@@ -0,0 +1,8 @@
1
+ import { delimiter, dirname, isAbsolute } from "node:path";
2
+ /** launchd does not inherit an interactive shell's Node installation. */
3
+ export function serviceEnvironment(nodePath = process.execPath, inherited = process.env) {
4
+ const paths = [dirname(nodePath), ...(inherited.PATH ?? "").split(delimiter),
5
+ "/usr/bin", "/bin", "/usr/sbin", "/sbin"];
6
+ return { ...inherited, PATH: [...new Set(paths.filter(isAbsolute))].join(delimiter) };
7
+ }
8
+ //# sourceMappingURL=service-environment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-environment.js","sourceRoot":"","sources":["../src/service-environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE3D,yEAAyE;AACzE,MAAM,UAAU,kBAAkB,CAChC,QAAQ,GAAG,OAAO,CAAC,QAAQ,EAC3B,YAA+B,OAAO,CAAC,GAAG;IAE1C,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,SAAS,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC;QAC1E,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;IAC5C,OAAO,EAAE,GAAG,SAAS,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;AACxF,CAAC"}
@@ -0,0 +1,38 @@
1
+ # Actualización administrada de Hermes bajo launchd
2
+
3
+ Diseño solicitado: corregir el actualizador A2A, sin modificar Voice, Hermes,
4
+ la UI, credenciales o endpoints. La existencia del archivo npx no prueba que
5
+ su shebang pueda encontrar Node en un servicio.
6
+
7
+ ## Decisión
8
+
9
+ Se prefiere Node absoluto + CLI de npx con PATH corregido, frente a únicamente
10
+ corregir PATH y seguir dependiendo del shebang. El mismo entorno se utiliza
11
+ en el runner separado, la comprobación de npx y el comando de actualización.
12
+ Los nuevos servicios incluyen un PATH explícito con el directorio de su Node.
13
+
14
+ Los errores públicos están limitados a códigos conocidos. Los resultados
15
+ persistidos no contienen identidades, rutas, endpoints ni salida de comandos.
16
+ La correlación privada vive separada, conserva la validación de identidad y
17
+ entorno al reintentar, y no conserva la credencial del trabajo finalizado.
18
+ Los recibos antiguos se migran antes de reintentarlos.
19
+
20
+ ## Verificación y límites
21
+
22
+ - Reproducir el fallo env-node con un subproceso y PATH de servicio.
23
+ - Probar CLI con Node absoluto y PATH idéntico, incluyendo subprocesos hijos.
24
+ - Probar error de ejecución, npx ausente, recibo inválido y versión incorrecta.
25
+ - Probar retención/reintento, aislamiento de entorno y saneamiento de recibos.
26
+ - Validar el paquete compilado, no solamente imports del código fuente.
27
+ - No confundir un fixture con una actualización real de Athena.
28
+
29
+ ## Publicación y migración
30
+
31
+ La versión publicada 0.12.67 es inmutable; el arreglo se prepara en 0.12.68.
32
+ Un bridge que todavía ejecuta el runner anterior no adquiere este arreglo sólo
33
+ por publicar npm. Antes de reintentar desde la App necesita una migración
34
+ controlada del PATH de su servicio y del bridge, autorizada por su propietario,
35
+ o un canal de mantenimiento independiente ya instalado. No usar npx interactivo
36
+ ni delegarlo al modelo por chat. No modificar servicios Voice. La actualización
37
+ real sólo se considera completa con recibo terminal y versión del bridge activo
38
+ coincidentes con el objetivo. Hasta entonces la validación en el host queda pendiente.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "perkos-a2a",
3
3
  "name": "PerkOS A2A",
4
- "version": "0.12.67",
4
+ "version": "0.12.68",
5
5
  "description": "Agent-to-Agent (A2A) protocol communication for OpenClaw agents",
6
6
  "author": "PerkOS",
7
7
  "configSchema": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@perkos/perkos-a2a",
3
- "version": "0.12.67",
3
+ "version": "0.12.68",
4
4
  "description": "A2A Protocol communication plugin for OpenClaw — Agent-to-Agent protocol implementation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -40,6 +40,7 @@
40
40
  "LICENSE"
41
41
  ],
42
42
  "scripts": {
43
+ "test:maintenance-package": "node scripts/test-managed-maintenance-package.mjs",
43
44
  "build": "tsc && node scripts/bundle.mjs",
44
45
  "dev": "tsc --watch",
45
46
  "lint": "tsc --noEmit",