a2a-exposed 0.8.10 → 0.8.11
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/lib/commands.mjs +23 -8
- package/lib/main.mjs +2 -0
- package/package.json +3 -2
- package/worker/deploy.env.example +1 -1
- package/worker/package.json +4 -3
- package/worker/src/version.ts +1 -1
- package/worker/test/owner-rpc.test.ts +1 -1
- package/worker/tsconfig.json +1 -1
package/lib/commands.mjs
CHANGED
|
@@ -253,13 +253,24 @@ export function peers(sub, args, o) {
|
|
|
253
253
|
}
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
+
const ownerConfigured = () => !!(baseUrl() && C.get("A2A_OWNER_TOKEN"));
|
|
257
|
+
|
|
258
|
+
// Outbound delivery works with just a peer token unless --push is requested.
|
|
259
|
+
// Persistence failures must not hide successful delivery (and invite a duplicate send).
|
|
260
|
+
async function persistOutbound(method, path, body, action) {
|
|
261
|
+
if (!ownerConfigured()) return false;
|
|
262
|
+
try { await owner(method, path, body); return true; }
|
|
263
|
+
catch { console.error(`warning: could not ${action} to the worker`); return false; }
|
|
264
|
+
}
|
|
265
|
+
|
|
256
266
|
async function logHistory(ctx, entry) {
|
|
257
|
-
|
|
258
|
-
catch { console.error("warning: could not log history to the worker"); }
|
|
267
|
+
await persistOutbound("POST", "/owner/history", { contextId: ctx, ...entry }, "log history");
|
|
259
268
|
}
|
|
260
269
|
|
|
261
270
|
export async function send(o) {
|
|
262
271
|
if (!o.to) die("--to required");
|
|
272
|
+
// The callback authenticates against the token saved through the owner API.
|
|
273
|
+
if (o.push && !ownerConfigured()) die("--push needs A2A_BASE_URL and A2A_OWNER_TOKEN (your Worker); configure both or omit --push and poll instead");
|
|
263
274
|
const [alias, base, tok] = resolvePeer(o.to);
|
|
264
275
|
const card = await fetchCard(base);
|
|
265
276
|
const [url, version] = pickEndpoint(base, card, o.proto);
|
|
@@ -272,7 +283,6 @@ export async function send(o) {
|
|
|
272
283
|
const conf = v1 ? { returnImmediately: true } : { blocking: false };
|
|
273
284
|
let pushToken = null;
|
|
274
285
|
if (o.push) {
|
|
275
|
-
if (!baseUrl()) die("--push needs A2A_BASE_URL (your Worker)");
|
|
276
286
|
pushToken = randomToken(24);
|
|
277
287
|
conf[v1 ? "taskPushNotificationConfig" : "pushNotificationConfig"] = { url: baseUrl() + "/push", token: pushToken };
|
|
278
288
|
}
|
|
@@ -282,10 +292,13 @@ export async function send(o) {
|
|
|
282
292
|
const isTask = obj && typeof obj === "object" && "status" in obj;
|
|
283
293
|
const ctx = (obj && obj.contextId) || o.context || newId();
|
|
284
294
|
const tid = isTask ? obj.id : null;
|
|
295
|
+
out(obj);
|
|
285
296
|
await logHistory(ctx, { dir: "out", peer: alias, taskId: tid, role: "user", text, data: { messageId: msg.messageId, endpoint: url, protocol: version } });
|
|
286
|
-
if (tid)
|
|
297
|
+
if (tid) {
|
|
298
|
+
const saved = await persistOutbound("POST", "/owner/outbound", { taskId: tid, contextId: ctx, peer: alias, endpoint: url, protocol: version, pushToken, task: obj }, "save outbound task");
|
|
299
|
+
if (o.push && !saved) console.error(`warning: push updates cannot authenticate because the outbound task token could not be saved; use ${CLI} poll --to ${alias} ${tid} instead; the message was delivered, do not resend`);
|
|
300
|
+
}
|
|
287
301
|
else if (obj && obj.parts) await logHistory(ctx, { dir: "in", peer: alias, role: "agent", event: "direct_message", text: textOf(obj) });
|
|
288
|
-
out(obj);
|
|
289
302
|
if (tid) console.error(`# task ${tid}: ${plainState(obj)} (check: ${CLI} poll --to ${alias} ${tid})`);
|
|
290
303
|
}
|
|
291
304
|
|
|
@@ -294,17 +307,19 @@ export async function poll(taskId, o) {
|
|
|
294
307
|
const [alias, base, tok] = resolvePeer(o.to);
|
|
295
308
|
const tid = checkId(taskId, "taskId");
|
|
296
309
|
let rec = {};
|
|
297
|
-
|
|
310
|
+
if (ownerConfigured()) {
|
|
311
|
+
try { rec = await owner("GET", `/owner/outbound/${q(tid)}`); } catch { rec = {}; }
|
|
312
|
+
}
|
|
298
313
|
const [url, version] = rec.endpoint ? [rec.endpoint, rec.protocol || "0.3"] : pickEndpoint(base, await fetchCard(base), o.proto);
|
|
299
314
|
const res = await rpc(url, version, tok, "tasks/get", "GetTask", { id: tid }, { alias: C.loadPeers()[alias] ? alias : null, base });
|
|
315
|
+
out(res);
|
|
300
316
|
if (rec.endpoint) {
|
|
301
|
-
await
|
|
317
|
+
await persistOutbound("PUT", `/owner/outbound/${q(tid)}`, { task: res }, "update outbound task");
|
|
302
318
|
let txt = textOf((res.status || {}).message || {});
|
|
303
319
|
const arts = (res.artifacts || []).map((x) => textOf(x)).join("\n");
|
|
304
320
|
if (arts && arts !== txt) txt = (txt ? txt + "\n" : "") + arts;
|
|
305
321
|
await logHistory(rec.contextId, { dir: "in", peer: alias, taskId: tid, event: "poll", state: plainState(res), text: txt });
|
|
306
322
|
}
|
|
307
|
-
out(res);
|
|
308
323
|
console.error(`# task ${tid}: ${plainState(res)}`);
|
|
309
324
|
}
|
|
310
325
|
|
package/lib/main.mjs
CHANGED
|
@@ -228,6 +228,8 @@ Outbound (agents you call)
|
|
|
228
228
|
differs between the two, connect/send/poll/peers/status warn (values never shown)
|
|
229
229
|
send --to <alias|url> [--text T | stdin] [--context C] [--task T] [--push] [--proto 0.3|1.0]
|
|
230
230
|
poll --to <alias|url> <taskId> [--proto 0.3|1.0]
|
|
231
|
+
send/poll need only peer config; owner history/task storage is optional.
|
|
232
|
+
Storage failures warn on stderr; the peer response still prints and exits 0
|
|
231
233
|
outbound <taskId> stored state of a task you sent (incl. pushed updates)
|
|
232
234
|
|
|
233
235
|
Owner
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "a2a-exposed",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.11",
|
|
4
4
|
"description": "Give any AI agent a public A2A (Agent2Agent) endpoint: a Cloudflare Worker, a wake webhook, or scheduled checks",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
17
|
"prepack": "node scripts/sync-worker.mjs && node -e \"require('fs').copyFileSync('../LICENSE','LICENSE')\"",
|
|
18
|
-
"test": "node --test test/*.test.mjs"
|
|
18
|
+
"test": "node --test test/*.test.mjs",
|
|
19
|
+
"test:coverage": "node --experimental-test-coverage --test-coverage-include=lib/** --test-reporter=spec --test-reporter=lcov --test-reporter-destination=stdout --test-reporter-destination=../coverage/cli.lcov --test test/*.test.mjs"
|
|
19
20
|
},
|
|
20
21
|
"engines": {
|
|
21
22
|
"node": ">=22.18"
|
|
@@ -16,7 +16,7 @@ A2A_AGENT_DESCRIPTION=An AI agent reachable over A2A. Replies asynchronously.
|
|
|
16
16
|
WAKE_PRESET=generic
|
|
17
17
|
# WAKE_AGENT_ID=main
|
|
18
18
|
# WAKE_KEY_HEADER=authorization
|
|
19
|
-
# WAKE_KEY_PREFIX=Bearer
|
|
19
|
+
# WAKE_KEY_PREFIX="Bearer "
|
|
20
20
|
# WAKE_BODY_TEMPLATE={"text":"{{summary}}"}
|
|
21
21
|
# Command shown in wake hints (CLI: --cli-command; default "npx -y a2a-exposed@latest"). For a source checkout:
|
|
22
22
|
# WAKE_CLI_COMMAND=node /path/to/a2a-exposed/cli/bin/a2a-exposed.mjs
|
package/worker/package.json
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"deploy": "cf deploy",
|
|
10
10
|
"pretest": "node scripts/stamp-version.mjs",
|
|
11
11
|
"test": "node --import ./test/register-hooks.mjs --test test/*.test.ts",
|
|
12
|
+
"test:coverage": "node scripts/stamp-version.mjs && node --import ./test/register-hooks.mjs --experimental-test-coverage --test-coverage-include=src/** --test-reporter=spec --test-reporter=lcov --test-reporter-destination=stdout --test-reporter-destination=../coverage/worker.lcov --test test/*.test.ts",
|
|
12
13
|
"typecheck": "cf workers types && tsc"
|
|
13
14
|
},
|
|
14
15
|
"engines": {
|
|
@@ -16,11 +17,11 @@
|
|
|
16
17
|
},
|
|
17
18
|
"devDependencies": {
|
|
18
19
|
"@cloudflare/vite-plugin": "beta",
|
|
19
|
-
"cf": "^1.0.0-beta.
|
|
20
|
-
"esbuild": "^0.28.
|
|
20
|
+
"cf": "^1.0.0-beta.18",
|
|
21
|
+
"esbuild": "^0.28.2",
|
|
21
22
|
"miniflare": "^5.20261006.0-alpha",
|
|
22
23
|
"typescript": "^7.0.2",
|
|
23
|
-
"vite": "^8.3.
|
|
24
|
+
"vite": "^8.3.4"
|
|
24
25
|
},
|
|
25
26
|
"dependencies": {
|
|
26
27
|
"tunnelfetch": "1.13.0"
|
package/worker/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Inbox build. Keep this equal to `version` in cli/package.json. */
|
|
2
|
-
export const VERSION = "0.8.
|
|
2
|
+
export const VERSION = "0.8.11";
|
|
@@ -111,4 +111,4 @@ test("a token-shaped claim is blanked and the rest of the list is returned", ()
|
|
|
111
111
|
assert.equal(JSON.stringify(listed).includes(token), false);
|
|
112
112
|
assert.equal(JSON.stringify(listed).includes("t=" + "c".repeat(16)), false);
|
|
113
113
|
assert.equal(JSON.stringify(listed).includes("user:secret"), false);
|
|
114
|
-
});
|
|
114
|
+
});
|
package/worker/tsconfig.json
CHANGED