@a2a-exposed/control-core 0.8.6 → 0.8.8
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
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/a2a-exposed) [](https://github.com/telegraphic-dev/a2a-exposed/actions/workflows/ci.yml)
|
|
4
4
|
|
|
5
|
-
a2a-exposed gives an AI agent a public [A2A](https://a2a-protocol.org) address. A Cloudflare Worker serves the agent card and the JSON-RPC endpoint. Inbound messages become tasks
|
|
5
|
+
a2a-exposed gives an AI agent a public [A2A](https://a2a-protocol.org) address. A Cloudflare Worker serves the agent card and the JSON-RPC endpoint. Inbound messages become tasks on the A2A endpoint. Webhooks wake your agent. Without one, it polls, guided by next-check hints. The agent reads the A2A endpoint and replies with `npx -y a2a-exposed@latest`. The same command sends messages to other A2A agents (1.0 or 0.3). The owner pages on that A2A endpoint are `/` and `/device`.
|
|
6
6
|
|
|
7
7
|
## Who it's for
|
|
8
8
|
|
|
@@ -18,7 +18,7 @@ Requires Node 22.18+. The skills are installed into the project the agent works
|
|
|
18
18
|
npx -y skills add telegraphic-dev/a2a-exposed
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
2. **Deploy an
|
|
21
|
+
2. **Deploy an A2A endpoint** in your Cloudflare account. [Self-hosting](docs/self-hosting.md) covers the login: `cf auth login`, a named profile (`--cf-profile`), an API token, and `--account-id` when that token can see more than one account.
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
24
|
npx -y a2a-exposed@latest init --hostname agent.example.com --agent-name "My Agent"
|
|
@@ -33,13 +33,13 @@ Requires Node 22.18+. The skills are installed into the project the agent works
|
|
|
33
33
|
npx -y a2a-exposed@latest pair set-password --web
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
`status` is read-only and prints the next step. Open the one-time link (15 minutes by default, `--ttl` from 1 to 60) and choose the approval password yourself. Another agent then runs `npx -y a2a-exposed@latest connect https://agent.example.com` (the code lasts
|
|
36
|
+
`status` is read-only and prints the next step. Open the one-time link (15 minutes by default, `--ttl` from 1 to 60) and choose the approval password yourself. Another agent then runs `npx -y a2a-exposed@latest connect https://agent.example.com` (the code lasts 60 minutes). You approve that request on the `/device` page, or with `npx -y a2a-exposed@latest pair approve <code>`. Removing the A2A endpoint later is `npx -y a2a-exposed@latest uninstall` ([Self-hosting](docs/self-hosting.md#remove-an-inbox)).
|
|
37
37
|
|
|
38
38
|
After the skill is in the project, you can ask the agent to set this up. It follows the setup skill. Do not run setup from a Claude Code cloud session: that environment cannot install, tunnel, or reach your Worker. Claude Code: `/plugin marketplace add telegraphic-dev/a2a-exposed`, then `/plugin install a2a-exposed@a2a-exposed`. [Getting started](docs/getting-started.md) has Node, the client table, and what to do if setup stops halfway.
|
|
39
39
|
|
|
40
40
|
## Hosted
|
|
41
41
|
|
|
42
|
-
[a2a.exposed](https://a2a.exposed) hosts the
|
|
42
|
+
[a2a.exposed](https://a2a.exposed) hosts the A2A endpoint and is open to everyone. Pricing is at [a2a.exposed/pricing](https://a2a.exposed/pricing). When to use it, and the sentence to say, is in [Getting started](docs/getting-started.md#hosted-inbox).
|
|
43
43
|
|
|
44
44
|
## Docs
|
|
45
45
|
|
|
@@ -47,13 +47,13 @@ After the skill is in the project, you can ask the agent to set this up. It foll
|
|
|
47
47
|
| --- | --- |
|
|
48
48
|
| [Getting started](docs/getting-started.md) | Skills, Node, first deploy, pairing |
|
|
49
49
|
| [Receiving messages](docs/receiving-messages.md) | Webhooks wake your agent. Without one, it polls, guided by next-check hints |
|
|
50
|
-
| [Self-hosting](docs/self-hosting.md) | Cloudflare
|
|
50
|
+
| [Self-hosting](docs/self-hosting.md) | Cloudflare A2A endpoint, workers.dev, tunnel, uninstall, public façade |
|
|
51
51
|
| [CLI](docs/cli.md) | Commands, flags, config |
|
|
52
52
|
| [MCP](docs/mcp.md) | Remote connector at `/mcp` |
|
|
53
53
|
| [Feedback](docs/feedback.md) | Report a bug or an idea |
|
|
54
54
|
| [Security](docs/security.md) | Tokens, pairing, wakes, façade |
|
|
55
55
|
| [Troubleshooting](docs/troubleshooting.md) | FAQ and common failures |
|
|
56
|
-
| [Upgrading](docs/upgrading.md) | Move an existing
|
|
56
|
+
| [Upgrading](docs/upgrading.md) | Move an existing A2A endpoint forward |
|
|
57
57
|
| [Protocol](docs/protocol.md) | A2A 1.0 and 0.3, OAuth, endpoints |
|
|
58
58
|
|
|
59
59
|
Changes are listed in [CHANGELOG.md](CHANGELOG.md). Agent workflows live in [`skills/`](skills/a2a-exposed/SKILL.md).
|
package/dist/index.js
CHANGED
|
@@ -2097,7 +2097,45 @@ async function releaseWorking(db, inboxId, now) {
|
|
|
2097
2097
|
const row = await readInboxById(db, inboxId);
|
|
2098
2098
|
if (row?.status === "working") await patchInbox(db, inboxId, now, { status: "pending" });
|
|
2099
2099
|
}
|
|
2100
|
+
function httpsIssuer(value) {
|
|
2101
|
+
const raw = value.trim();
|
|
2102
|
+
if (!raw || raw.length > 300) return "";
|
|
2103
|
+
try {
|
|
2104
|
+
const url = new URL(raw);
|
|
2105
|
+
if (url.protocol !== "https:" || url.username || url.password) return "";
|
|
2106
|
+
return raw;
|
|
2107
|
+
} catch {
|
|
2108
|
+
return "";
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
function plainCredential(value, max) {
|
|
2112
|
+
const text = value.trim();
|
|
2113
|
+
if (!text || text.length > max || /[\u0000-\u001f]/.test(text)) return "";
|
|
2114
|
+
return text;
|
|
2115
|
+
}
|
|
2116
|
+
function subjectsText(value) {
|
|
2117
|
+
const list = typeof value === "string" ? value.split(",") : value;
|
|
2118
|
+
const subjects = list.map((item) => item.trim()).filter((item) => item && !/[\u0000-\u001f]/.test(item));
|
|
2119
|
+
const text = subjects.join(",");
|
|
2120
|
+
return text && text.length <= 2e3 ? text : "";
|
|
2121
|
+
}
|
|
2122
|
+
function inboxApprovalBindings(input) {
|
|
2123
|
+
if (!input) return null;
|
|
2124
|
+
const issuer = httpsIssuer(input.issuer);
|
|
2125
|
+
const clientId = plainCredential(input.clientId, 200);
|
|
2126
|
+
const clientSecret = plainCredential(input.clientSecret, 512);
|
|
2127
|
+
const allowed = subjectsText(input.allowedSubjects);
|
|
2128
|
+
if (!issuer || !clientId || !clientSecret || !allowed) return null;
|
|
2129
|
+
return [
|
|
2130
|
+
{ type: "plain_text", name: "APPROVAL_OIDC_ISSUER", text: issuer },
|
|
2131
|
+
{ type: "plain_text", name: "APPROVAL_OIDC_CLIENT_ID", text: clientId },
|
|
2132
|
+
{ type: "secret_text", name: "APPROVAL_OIDC_CLIENT_SECRET", text: clientSecret },
|
|
2133
|
+
{ type: "plain_text", name: "APPROVAL_OIDC_ALLOWED_SUBJECTS", text: allowed },
|
|
2134
|
+
{ type: "plain_text", name: "APPROVAL_METHODS", text: "oidc" }
|
|
2135
|
+
];
|
|
2136
|
+
}
|
|
2100
2137
|
function scriptMetadata(input) {
|
|
2138
|
+
const approval = inboxApprovalBindings(input.approvalOidc);
|
|
2101
2139
|
return {
|
|
2102
2140
|
main_module: "index.js",
|
|
2103
2141
|
compatibility_date: INBOX_COMPATIBILITY_DATE,
|
|
@@ -2108,7 +2146,8 @@ function scriptMetadata(input) {
|
|
|
2108
2146
|
{ type: "plain_text", name: "PAIRING_APPROVAL", text: "human" },
|
|
2109
2147
|
{ type: "plain_text", name: "WAKE_PRESET", text: "generic" },
|
|
2110
2148
|
{ type: "plain_text", name: PROVISION_TAG_NAME, text: input.scriptTag },
|
|
2111
|
-
{ type: "secret_text", name: "OWNER_TOKEN", text: input.ownerToken }
|
|
2149
|
+
{ type: "secret_text", name: "OWNER_TOKEN", text: input.ownerToken },
|
|
2150
|
+
...approval ?? []
|
|
2112
2151
|
]
|
|
2113
2152
|
};
|
|
2114
2153
|
}
|
|
@@ -2527,7 +2566,8 @@ async function runInboxStep(input) {
|
|
|
2527
2566
|
script: input.script,
|
|
2528
2567
|
migrations: input.migrations,
|
|
2529
2568
|
grant,
|
|
2530
|
-
permissionDelaysMs: input.permissionDelaysMs ?? PERMISSION_DELAYS_MS
|
|
2569
|
+
permissionDelaysMs: input.permissionDelaysMs ?? PERMISSION_DELAYS_MS,
|
|
2570
|
+
approvalOidc: input.approvalOidc
|
|
2531
2571
|
};
|
|
2532
2572
|
try {
|
|
2533
2573
|
if (next !== "verify") {
|
|
@@ -2813,7 +2853,7 @@ async function runUpload(ctx) {
|
|
|
2813
2853
|
accountId,
|
|
2814
2854
|
name: workerName,
|
|
2815
2855
|
script: ctx.script,
|
|
2816
|
-
metadata: scriptMetadata({ d1Id: databaseId, publicUrl, workerName, ownerToken, scriptTag })
|
|
2856
|
+
metadata: scriptMetadata({ d1Id: databaseId, publicUrl, workerName, ownerToken, scriptTag, approvalOidc: ctx.approvalOidc })
|
|
2817
2857
|
});
|
|
2818
2858
|
if (uploaded2 !== true) {
|
|
2819
2859
|
return { wizard: await failOrWait(ctx, "upload", uploaded2, "Cloudflare stops while uploading the inbox.", "upload") };
|
|
@@ -3079,6 +3119,7 @@ export {
|
|
|
3079
3119
|
hashOwnerToken,
|
|
3080
3120
|
headerSafeToken,
|
|
3081
3121
|
httpsOrigin,
|
|
3122
|
+
inboxApprovalBindings,
|
|
3082
3123
|
inboxCanProvision,
|
|
3083
3124
|
listAccounts,
|
|
3084
3125
|
listCloudflareInboxes,
|
|
@@ -26,8 +26,8 @@ export * from "../provision/statements.js";
|
|
|
26
26
|
export * from "../provision/permission-wait.js";
|
|
27
27
|
export * from "../provision/inbox-row.js";
|
|
28
28
|
export * from "../provision/remove.js";
|
|
29
|
-
export { INBOX_COMPATIBILITY_DATE, PERMISSION_DELAYS_MS, PERMISSION_TRIES, WIZARD_STEPS, createCloudflareAgent, dashboardLinks, fieldsFromForm, permissionRetryDue, resumeFields, runInboxStep, viewInboxWizard, } from "../provision/inbox.js";
|
|
30
|
-
export type { InboxMigration, InboxWizard, ProvisionFields, WizardControl, WizardState, WizardStepId, WizardStepView, } from "../provision/inbox.js";
|
|
29
|
+
export { INBOX_COMPATIBILITY_DATE, PERMISSION_DELAYS_MS, PERMISSION_TRIES, WIZARD_STEPS, createCloudflareAgent, dashboardLinks, fieldsFromForm, permissionRetryDue, inboxApprovalBindings, resumeFields, runInboxStep, viewInboxWizard, } from "../provision/inbox.js";
|
|
30
|
+
export type { ApprovalOidcInput, InboxMigration, InboxWizard, ProvisionFields, WizardControl, WizardState, WizardStepId, WizardStepView, } from "../provision/inbox.js";
|
|
31
31
|
export { INBOX_MIGRATIONS } from "../provision/inbox-migrations.js";
|
|
32
32
|
export { CONTROL_MIGRATIONS, CONTROL_MIGRATIONS_DIR } from "./migrations.js";
|
|
33
33
|
export type { ControlMigration } from "./migrations.js";
|
|
@@ -89,6 +89,31 @@ interface StoredStepNote {
|
|
|
89
89
|
}
|
|
90
90
|
/** A new Cloudflare agent. The finished ones stay. */
|
|
91
91
|
export declare function createCloudflareAgent(db: Sql, userId: string, host: SetupHost, now?: number): Promise<string>;
|
|
92
|
+
/** Account sign-in for a Cloudflare agent. The dashboard creates the client and passes these four fields. */
|
|
93
|
+
export type ApprovalOidcInput = {
|
|
94
|
+
issuer: string;
|
|
95
|
+
clientId: string;
|
|
96
|
+
clientSecret: string;
|
|
97
|
+
allowedSubjects: string | readonly string[];
|
|
98
|
+
};
|
|
99
|
+
type ScriptBinding = {
|
|
100
|
+
type: "d1";
|
|
101
|
+
name: string;
|
|
102
|
+
id: string;
|
|
103
|
+
} | {
|
|
104
|
+
type: "plain_text";
|
|
105
|
+
name: string;
|
|
106
|
+
text: string;
|
|
107
|
+
} | {
|
|
108
|
+
type: "secret_text";
|
|
109
|
+
name: string;
|
|
110
|
+
text: string;
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Worker bindings for account sign-in. Returns the list when the issuer is https and every field
|
|
114
|
+
* has a value. Otherwise returns null and the upload keeps the approval password.
|
|
115
|
+
*/
|
|
116
|
+
export declare function inboxApprovalBindings(input: ApprovalOidcInput | undefined): ScriptBinding[] | null;
|
|
92
117
|
export declare function fieldsFromForm(form: FormData): ProvisionFields;
|
|
93
118
|
export declare function resumeFields(value: {
|
|
94
119
|
name?: unknown;
|
|
@@ -136,6 +161,12 @@ export declare function runInboxStep(input: {
|
|
|
136
161
|
fetchImpl?: typeof fetch;
|
|
137
162
|
/** Pauses after a permission error. Tests pass zeros. Production waits about 15 seconds across 4 tries. */
|
|
138
163
|
permissionDelaysMs?: number[];
|
|
164
|
+
/**
|
|
165
|
+
* Account sign-in for this Cloudflare agent. When issuer, client id, client secret, and allowed
|
|
166
|
+
* subjects are all set, the upload writes the APPROVAL_OIDC_* bindings and APPROVAL_METHODS=oidc.
|
|
167
|
+
* Omit it and the agent stays on the approval password.
|
|
168
|
+
*/
|
|
169
|
+
approvalOidc?: ApprovalOidcInput;
|
|
139
170
|
}): Promise<{
|
|
140
171
|
wizard: InboxWizard;
|
|
141
172
|
ownerToken?: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@a2a-exposed/control-core",
|
|
3
|
-
"version": "0.8.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.8.8",
|
|
4
|
+
"description": "A2A endpoint provisioning, routing, and D1 migrations for a2a-exposed.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"prepare": "node control/scripts/build-core.mjs",
|