@a2a-exposed/control-core 0.8.7 → 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
|
@@ -33,7 +33,7 @@ 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
|
|
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;
|