@ryubyte/dsh-a2a 0.2.1 → 0.3.0
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/client.js +63 -18
- package/lib/index.js +119 -13
- package/lib/types/a2a-config.d.ts +2 -0
- package/lib/types/a2a-config.d.ts.map +1 -1
- package/lib/types/client/dashboard.css.d.ts.map +1 -1
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/dashboard.d.ts +2 -0
- package/lib/types/dashboard.d.ts.map +1 -1
- package/lib/types/index.d.ts +16 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/server.d.ts +12 -3
- package/lib/types/server.d.ts.map +1 -1
- package/package.json +24 -8
- package/src/a2a-config.ts +3 -0
- package/src/client/dashboard.css.ts +5 -0
- package/src/client/index.ts +127 -20
- package/src/dashboard.ts +10 -0
- package/src/index.ts +132 -9
- package/src/server.ts +14 -3
package/lib/client.js
CHANGED
|
@@ -199,6 +199,11 @@ window.__ModuleLoader__.load({
|
|
|
199
199
|
.a2a-auth-state[data-on='false'] { color: var(--dsw-alias-label-tertiary, #81858c); }
|
|
200
200
|
.a2a-auth-input-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
201
201
|
.a2a-auth-input-row .a2a-input { flex: 1; min-width: 220px; }
|
|
202
|
+
.a2a-cors-list { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
203
|
+
.a2a-cors-chip { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-family: var(--ds-font-family-code, ui-monospace, monospace); padding: 2px 6px 2px 8px; border-radius: var(--dsw-radius-sm, 6px); background: var(--dsw-alias-interactive-bg-hover, rgba(0,0,0,0.05)); border: .5px solid var(--dsw-alias-border-l3, #d0d3d9); color: var(--dsw-alias-label-secondary, #4a4f57); }
|
|
204
|
+
.a2a-cors-chip-x { border: none; background: none; cursor: pointer; font-size: 14px; line-height: 1; padding: 0 2px; color: var(--dsw-alias-label-tertiary, #81858c); }
|
|
205
|
+
.a2a-cors-chip-x:hover:not(:disabled) { color: var(--dsw-alias-state-error-primary, #f04438); }
|
|
206
|
+
.a2a-cors-chip-x:disabled { cursor: default; opacity: .4; }
|
|
202
207
|
|
|
203
208
|
/* ---- inbound table ---- */
|
|
204
209
|
.a2a-table-wrap { overflow-x: auto; border: 1px solid var(--dsw-alias-border-l1, rgba(127,127,127,.12)); border-radius: 10px; }
|
|
@@ -303,21 +308,13 @@ window.__ModuleLoader__.load({
|
|
|
303
308
|
const dispose = injectDashboardStyles();
|
|
304
309
|
return () => dispose();
|
|
305
310
|
}, "dsh-a2a: dashboard styles");
|
|
306
|
-
ctx.effect(() => {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
inject: () => ({})
|
|
314
|
-
}, DashboardSection);
|
|
315
|
-
return () => off();
|
|
316
|
-
} catch (err) {
|
|
317
|
-
console.error("[dsh-a2a] failed to register settings section:", err);
|
|
318
|
-
return () => {};
|
|
319
|
-
}
|
|
320
|
-
}, "dsh-a2a: settings section");
|
|
311
|
+
ctx.effect(() => ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
312
|
+
name: "settings.section",
|
|
313
|
+
id: "a2a",
|
|
314
|
+
order: 90,
|
|
315
|
+
label: () => "A2A 连接",
|
|
316
|
+
inject: () => ({})
|
|
317
|
+
}, DashboardSection)), "dsh-a2a: settings section");
|
|
321
318
|
}
|
|
322
319
|
async function fetchSnapshot() {
|
|
323
320
|
const res = await fetch("/a2a/api", { headers: { accept: "application/json" } });
|
|
@@ -390,6 +387,8 @@ window.__ModuleLoader__.load({
|
|
|
390
387
|
const [addToken, setAddToken] = (0, react.useState)("");
|
|
391
388
|
/** Inbound authToken input (serve tab); empty string means "clear". */
|
|
392
389
|
const [authInput, setAuthInput] = (0, react.useState)("");
|
|
390
|
+
/** New-origin input for the inbound CORS allowlist editor (serve tab). */
|
|
391
|
+
const [corsInput, setCorsInput] = (0, react.useState)("");
|
|
393
392
|
/** Serve-identity edit mode + form draft (server tab). All hooks stay at the
|
|
394
393
|
* top level in fixed order; a single shared draft avoids per-card hooks. */
|
|
395
394
|
const [editingServe, setEditingServe] = (0, react.useState)(false);
|
|
@@ -541,7 +540,7 @@ window.__ModuleLoader__.load({
|
|
|
541
540
|
})), (0, react.createElement)("div", { className: "a2a-hint" }, "若远程 Agent 需要鉴权,填入 token;导入与后续调用都会带上。token 会明文存入 a2a.json。"), discovering ? (0, react.createElement)("div", { className: "a2a-hint" }, "正在读取 Agent Card…") : discovered ? (0, react.createElement)("div", { className: "a2a-preview" }, (0, react.createElement)("div", { className: "a2a-preview-head" }, (0, react.createElement)("span", { className: "a2a-preview-name" }, discovered.name), discovered.version ? (0, react.createElement)("span", { className: "a2a-preview-version a2a-mono" }, `v${discovered.version}`) : null, stateDot("connected")), discovered.description ? (0, react.createElement)("div", { className: "a2a-preview-desc" }, discovered.description) : null, (discovered.skills?.length ?? 0) > 0 ? (0, react.createElement)("div", { className: "a2a-skill-list" }, (0, react.createElement)("div", { className: "a2a-skill-list-title" }, "Skills"), ...(discovered.skills ?? []).map((s) => (0, react.createElement)("div", { key: s.id }, `• ${s.name}${s.description ? ` — ${s.description}` : ""}`))) : null, discovered.endpoint ? (0, react.createElement)("div", { className: "a2a-preview-meta a2a-mono" }, `端点 ${discovered.endpoint}`) : null, (0, react.createElement)("div", { className: "a2a-btn-row" }, actionBtn("连接", true, () => void doConnect(), busy?.startsWith("add-agent:") ?? false), actionBtn("取消", false, () => {
|
|
542
541
|
setDiscovered(null);
|
|
543
542
|
setAddUrl("");
|
|
544
|
-
}, false))) : (0, react.createElement)("div", { className: "a2a-hint" }, "输入远程 Agent 的 Agent Card URL 并点击\"导入\",将自动读取其名称、描述、技能与能力,确认后建立连接。"))) : null, tab === "server" ? (0, react.createElement)("div", { className: "a2a-stack" }, serve ? editingServe ? serveEditPanel(serveDraft, setServeDraft, (also) => void saveServeIdentity(also), () => setEditingServe(false), busy, false) : needsServeSetup(serve) ? serveEditPanel(serveDraft, setServeDraft, (also) => void saveServeIdentity(also), () => setEditingServe(false), busy, true) : serveViewPanel(serve, busy, act, authInput, setAuthInput, startEditServe) : null, (0, react.createElement)("div", { className: "a2a-crew" }, sec("入站连接(谁在连接本 DSH)", inbound.length), inbound.length === 0 ? emptyRow("暂无入站连接。其他 A2A 客户端调用本 DSH 的 JSON-RPC 端点后会显示在这里。") : (0, react.createElement)("div", { className: "a2a-table-wrap" }, (0, react.createElement)("table", { className: "a2a-table" }, (0, react.createElement)("thead", null, headRow([
|
|
543
|
+
}, false))) : (0, react.createElement)("div", { className: "a2a-hint" }, "输入远程 Agent 的 Agent Card URL 并点击\"导入\",将自动读取其名称、描述、技能与能力,确认后建立连接。"))) : null, tab === "server" ? (0, react.createElement)("div", { className: "a2a-stack" }, serve ? editingServe ? serveEditPanel(serveDraft, setServeDraft, (also) => void saveServeIdentity(also), () => setEditingServe(false), busy, false) : needsServeSetup(serve) ? serveEditPanel(serveDraft, setServeDraft, (also) => void saveServeIdentity(also), () => setEditingServe(false), busy, true) : serveViewPanel(serve, busy, act, authInput, setAuthInput, corsInput, setCorsInput, startEditServe) : null, (0, react.createElement)("div", { className: "a2a-crew" }, sec("入站连接(谁在连接本 DSH)", inbound.length), inbound.length === 0 ? emptyRow("暂无入站连接。其他 A2A 客户端调用本 DSH 的 JSON-RPC 端点后会显示在这里。") : (0, react.createElement)("div", { className: "a2a-table-wrap" }, (0, react.createElement)("table", { className: "a2a-table" }, (0, react.createElement)("thead", null, headRow([
|
|
545
544
|
"来源",
|
|
546
545
|
"地址",
|
|
547
546
|
"首次连接",
|
|
@@ -722,7 +721,7 @@ window.__ModuleLoader__.load({
|
|
|
722
721
|
* "编辑" button that flips the section into {@link serveEditPanel}. Pure (no
|
|
723
722
|
* hooks): the edit-mode flag and draft live in the parent section.
|
|
724
723
|
*/
|
|
725
|
-
function serveViewPanel(serve, busy, act, authInput, setAuthInput, onEdit) {
|
|
724
|
+
function serveViewPanel(serve, busy, act, authInput, setAuthInput, corsInput, setCorsInput, onEdit) {
|
|
726
725
|
return (0, react.createElement)("div", { className: "a2a-panel" }, (0, react.createElement)("div", { className: "a2a-serve-head" }, sec("A2A 服务(本 DSH 作为 Agent 对外)", 0), stateDot(serve.enabled ? "connected" : "disconnected"), (0, react.createElement)("span", {
|
|
727
726
|
className: "a2a-serve-status",
|
|
728
727
|
"data-on": String(serve.enabled)
|
|
@@ -732,7 +731,7 @@ window.__ModuleLoader__.load({
|
|
|
732
731
|
})) : null, serve.agentCardUrl ? (0, react.createElement)("div", { className: "a2a-meta-row" }, (0, react.createElement)("span", { className: "a2a-meta-key" }, "Agent Card"), (0, react.createElement)("span", { className: "a2a-serve-endpoint a2a-mono" }, serve.agentCardUrl), (0, react.createElement)(CopyButton, {
|
|
733
732
|
text: serve.agentCardUrl,
|
|
734
733
|
label: "Agent Card URL"
|
|
735
|
-
})) : null, (serve.skills?.length ?? 0) > 0 ? (0, react.createElement)("div", { className: "a2a-skill-list" }, (0, react.createElement)("div", { className: "a2a-skill-list-title" }, "Skills"), ...(serve.skills ?? []).map((s) => (0, react.createElement)("div", { key: s.id }, `• ${s.name}${s.description ? ` — ${s.description}` : ""}`))) : null), serve.enabled ? executorNote(serve) : null, serve.enabled ? authControl(serve, busy, act, authInput, setAuthInput) : null);
|
|
734
|
+
})) : null, (serve.skills?.length ?? 0) > 0 ? (0, react.createElement)("div", { className: "a2a-skill-list" }, (0, react.createElement)("div", { className: "a2a-skill-list-title" }, "Skills"), ...(serve.skills ?? []).map((s) => (0, react.createElement)("div", { key: s.id }, `• ${s.name}${s.description ? ` — ${s.description}` : ""}`))) : null), serve.enabled ? executorNote(serve) : null, serve.enabled ? authControl(serve, busy, act, authInput, setAuthInput) : null, serve.enabled ? corsControl(serve, busy, act, corsInput, setCorsInput) : null);
|
|
736
735
|
}
|
|
737
736
|
/**
|
|
738
737
|
* Serve-identity edit form (also the guided-setup card for fresh installs).
|
|
@@ -814,6 +813,52 @@ window.__ModuleLoader__.load({
|
|
|
814
813
|
setAuthInput("");
|
|
815
814
|
}, busy === "set-server-auth:") : null), (0, react.createElement)("div", { className: "a2a-hint" }, "token 会明文存入 a2a.json,重启后仍生效。设置后,入站 JSON-RPC 请求需带 Authorization: Bearer <token>。"));
|
|
816
815
|
}
|
|
816
|
+
/**
|
|
817
|
+
* Inbound CORS allowlist control for the serve tab: shows the allowed browser
|
|
818
|
+
* origins and lets the operator add/remove them. Empty = no CORS (default),
|
|
819
|
+
* so cross-origin browser clients are blocked. Origins are exact
|
|
820
|
+
* scheme+host+port strings; the literal `*` opens it to any site (warned).
|
|
821
|
+
* Applies live (persisted to a2a.json) — no restart needed.
|
|
822
|
+
*/
|
|
823
|
+
function corsControl(serve, busy, act, corsInput, setCorsInput) {
|
|
824
|
+
const origins = serve.corsOrigins ?? [];
|
|
825
|
+
const hasWildcard = origins.includes("*");
|
|
826
|
+
const isBusy = busy === "set-server-cors:";
|
|
827
|
+
const save = (next) => {
|
|
828
|
+
act("set-server-cors", "", { corsOrigins: next });
|
|
829
|
+
};
|
|
830
|
+
const addOrigin = () => {
|
|
831
|
+
const v = corsInput.trim();
|
|
832
|
+
if (!v || origins.includes(v)) {
|
|
833
|
+
setCorsInput("");
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
save([...origins, v]);
|
|
837
|
+
setCorsInput("");
|
|
838
|
+
};
|
|
839
|
+
return (0, react.createElement)("div", { className: "a2a-auth" }, (0, react.createElement)("div", { className: "a2a-auth-row" }, (0, react.createElement)("span", { className: "a2a-auth-label" }, "跨域访问 (CORS)"), stateDot(origins.length > 0 ? hasWildcard ? "reconnecting" : "connected" : "disabled"), (0, react.createElement)("span", {
|
|
840
|
+
className: "a2a-auth-state",
|
|
841
|
+
"data-on": String(origins.length > 0)
|
|
842
|
+
}, origins.length === 0 ? "未开启(浏览器跨源调用被拒)" : hasWildcard ? "允许任意来源(*)" : `已允许 ${origins.length} 个来源`)), origins.length > 0 ? (0, react.createElement)("div", { className: "a2a-cors-list" }, ...origins.map((o) => (0, react.createElement)("span", {
|
|
843
|
+
key: o,
|
|
844
|
+
className: "a2a-cors-chip"
|
|
845
|
+
}, o, (0, react.createElement)("button", {
|
|
846
|
+
className: "a2a-cors-chip-x",
|
|
847
|
+
title: "移除",
|
|
848
|
+
disabled: isBusy,
|
|
849
|
+
onClick: () => save(origins.filter((x) => x !== o))
|
|
850
|
+
}, "×")))) : null, (0, react.createElement)("div", { className: "a2a-auth-input-row" }, (0, react.createElement)("input", {
|
|
851
|
+
className: "a2a-input",
|
|
852
|
+
type: "text",
|
|
853
|
+
placeholder: "添加来源,如 http://localhost:8080 或 *",
|
|
854
|
+
value: corsInput,
|
|
855
|
+
disabled: isBusy,
|
|
856
|
+
onChange: (e) => setCorsInput(e.target.value),
|
|
857
|
+
onKeyDown: (e) => {
|
|
858
|
+
if (e.key === "Enter") addOrigin();
|
|
859
|
+
}
|
|
860
|
+
}), actionBtn("添加", true, addOrigin, isBusy || !corsInput.trim()), origins.length > 0 ? actionBtn("清空", false, () => save([]), isBusy) : null), (0, react.createElement)("div", { className: "a2a-hint" }, hasWildcard ? "⚠️ 已允许任意来源(*):用户浏览器打开的任何网站都能跨源调用本入站端点。仅在完全可信的本机环境使用,并建议同时配置入站鉴权 token。" : "填入完整来源(协议+主机+端口,精确匹配)。留空则默认禁止浏览器跨源调用;写 * 可放开任意来源(不推荐)。跨域配置明文存入 a2a.json,实时生效。"));
|
|
861
|
+
}
|
|
817
862
|
function inboundRow(p, busy, act) {
|
|
818
863
|
return (0, react.createElement)("tr", { key: p.id }, (0, react.createElement)("td", { "data-label": "来源" }, p.label), (0, react.createElement)("td", {
|
|
819
864
|
className: "a2a-cell-mono",
|
package/lib/index.js
CHANGED
|
@@ -695,6 +695,16 @@ var A2AServer = class {
|
|
|
695
695
|
applyAuthScheme(card, token);
|
|
696
696
|
return card;
|
|
697
697
|
}
|
|
698
|
+
/**
|
|
699
|
+
* True when the request carries the configured bearer token. Public wrapper
|
|
700
|
+
* for transports that must decide response headers/status BEFORE streaming
|
|
701
|
+
* frames — see index.ts's SSE branch, which must send `content-type:
|
|
702
|
+
* text/event-stream` + CORS headers before the first `res.write()`, and
|
|
703
|
+
* therefore needs the auth outcome up front.
|
|
704
|
+
*/
|
|
705
|
+
checkAuth(req) {
|
|
706
|
+
return this.authorized(req);
|
|
707
|
+
}
|
|
698
708
|
/** True when the request carries the configured bearer token. */
|
|
699
709
|
authorized(req) {
|
|
700
710
|
const token = this.options.authToken;
|
|
@@ -1190,10 +1200,7 @@ function createDshAgentExecutor(agents, opts) {
|
|
|
1190
1200
|
type: "text",
|
|
1191
1201
|
text: prompt
|
|
1192
1202
|
}],
|
|
1193
|
-
source: {
|
|
1194
|
-
kind: "plugin",
|
|
1195
|
-
plugin
|
|
1196
|
-
}
|
|
1203
|
+
source: { kind: `plugin:${plugin}` }
|
|
1197
1204
|
});
|
|
1198
1205
|
agent.send(userMessage, "next-turn", true);
|
|
1199
1206
|
if (justOpened) try {
|
|
@@ -1440,6 +1447,14 @@ var DashboardRegistry = class {
|
|
|
1440
1447
|
message: "set-server-auth is not wired (server mode not mounted)"
|
|
1441
1448
|
};
|
|
1442
1449
|
}
|
|
1450
|
+
case "set-server-cors": {
|
|
1451
|
+
const raw = payload?.corsOrigins;
|
|
1452
|
+
const origins = Array.isArray(raw) ? raw.filter((o) => typeof o === "string") : [];
|
|
1453
|
+
return this.hooks.setServerCors ? this.hooks.setServerCors(origins) : {
|
|
1454
|
+
ok: false,
|
|
1455
|
+
message: "set-server-cors is not wired (server mode not mounted)"
|
|
1456
|
+
};
|
|
1457
|
+
}
|
|
1443
1458
|
case "server-status": return this.hooks.serverStatus ? Promise.resolve(this.hooks.serverStatus()) : {
|
|
1444
1459
|
ok: false,
|
|
1445
1460
|
message: "server-status is not wired (server mode not mounted)"
|
|
@@ -1676,6 +1691,7 @@ function mergePersisted(base, persisted) {
|
|
|
1676
1691
|
if (pServer.agentVersion) merged.agentVersion = pServer.agentVersion;
|
|
1677
1692
|
if (pServer.endpointPath) merged.endpointPath = pServer.endpointPath;
|
|
1678
1693
|
if (pServer.authToken !== void 0) merged.authToken = pServer.authToken;
|
|
1694
|
+
if (pServer.corsOrigins !== void 0) merged.corsOrigins = pServer.corsOrigins;
|
|
1679
1695
|
if (pServer.skills) merged.skills = pServer.skills;
|
|
1680
1696
|
}
|
|
1681
1697
|
return merged;
|
|
@@ -2222,12 +2238,23 @@ function apply(ctx, config = {}) {
|
|
|
2222
2238
|
}, store);
|
|
2223
2239
|
let enabled = wantsServer;
|
|
2224
2240
|
let disposers = [];
|
|
2241
|
+
let corsAllowlist = normalizeOrigins(mergedConfig.corsOrigins);
|
|
2242
|
+
if (corsAllowlist.includes("*")) logger?.warn?.("[a2a] corsOrigins includes \"*\": the inbound A2A endpoint accepts cross-origin browser calls from ANY site. Set an explicit origin allowlist unless this host is fully trusted and, ideally, gate it with authToken.");
|
|
2225
2243
|
const registerRoutes = () => {
|
|
2244
|
+
const cors = (req) => corsHeadersFor(req.headers.origin, corsAllowlist);
|
|
2226
2245
|
disposers.push(webServer.register({
|
|
2227
2246
|
kind: "exact",
|
|
2228
2247
|
path: "/.well-known/agent-card.json",
|
|
2229
|
-
handler: async (
|
|
2230
|
-
|
|
2248
|
+
handler: async (req, res) => {
|
|
2249
|
+
if (req.method === "OPTIONS") {
|
|
2250
|
+
res.writeHead(204, cors(req));
|
|
2251
|
+
res.end();
|
|
2252
|
+
return;
|
|
2253
|
+
}
|
|
2254
|
+
res.writeHead(200, {
|
|
2255
|
+
"content-type": "application/json",
|
|
2256
|
+
...cors(req)
|
|
2257
|
+
});
|
|
2231
2258
|
res.end(JSON.stringify(server.card));
|
|
2232
2259
|
}
|
|
2233
2260
|
}));
|
|
@@ -2235,34 +2262,54 @@ function apply(ctx, config = {}) {
|
|
|
2235
2262
|
kind: "prefix",
|
|
2236
2263
|
path: server.endpointPath,
|
|
2237
2264
|
handler: async (req, res) => {
|
|
2265
|
+
const ch = cors(req);
|
|
2266
|
+
if (req.method === "OPTIONS") {
|
|
2267
|
+
res.writeHead(204, ch);
|
|
2268
|
+
res.end();
|
|
2269
|
+
return;
|
|
2270
|
+
}
|
|
2238
2271
|
const isStream = (req.headers.accept ?? "").includes("text/event-stream");
|
|
2239
2272
|
if (req.method === "GET" && (req.url ?? "").startsWith(server.endpointPath + "/card")) {
|
|
2240
|
-
res.writeHead(200, {
|
|
2273
|
+
res.writeHead(200, {
|
|
2274
|
+
"content-type": "application/json",
|
|
2275
|
+
...ch
|
|
2276
|
+
});
|
|
2241
2277
|
res.end(JSON.stringify(server.card));
|
|
2242
2278
|
return;
|
|
2243
2279
|
}
|
|
2244
2280
|
if (req.method !== "POST") {
|
|
2245
|
-
res.writeHead(405, {
|
|
2281
|
+
res.writeHead(405, {
|
|
2282
|
+
"content-type": "text/plain",
|
|
2283
|
+
...ch
|
|
2284
|
+
});
|
|
2246
2285
|
res.end("Method Not Allowed");
|
|
2247
2286
|
return;
|
|
2248
2287
|
}
|
|
2249
2288
|
const body = await readBody(req);
|
|
2250
2289
|
if (isStream) {
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
res.writeHead(stream.status, {
|
|
2290
|
+
if (!server.checkAuth(toServerReq(req))) {
|
|
2291
|
+
res.writeHead(401, {
|
|
2254
2292
|
"content-type": "text/plain",
|
|
2255
|
-
...
|
|
2293
|
+
...ch,
|
|
2294
|
+
"WWW-Authenticate": "Bearer"
|
|
2256
2295
|
});
|
|
2257
2296
|
res.end("Unauthorized");
|
|
2258
2297
|
return;
|
|
2259
2298
|
}
|
|
2299
|
+
res.writeHead(200, {
|
|
2300
|
+
"content-type": "text/event-stream",
|
|
2301
|
+
"cache-control": "no-cache",
|
|
2302
|
+
connection: "keep-alive",
|
|
2303
|
+
...ch
|
|
2304
|
+
});
|
|
2305
|
+
await server.handleStream(toServerReq(req), body, (frame) => res.write(frame));
|
|
2260
2306
|
res.end();
|
|
2261
2307
|
return;
|
|
2262
2308
|
}
|
|
2263
2309
|
const out = await server.handle(toServerReq(req), body);
|
|
2264
2310
|
res.writeHead(out.status, {
|
|
2265
2311
|
"content-type": out.contentType,
|
|
2312
|
+
...ch,
|
|
2266
2313
|
...out.headers ?? {}
|
|
2267
2314
|
});
|
|
2268
2315
|
res.end(out.body);
|
|
@@ -2385,11 +2432,34 @@ function apply(ctx, config = {}) {
|
|
|
2385
2432
|
message: token ? "已设置入站鉴权 token" : "已清除入站鉴权 token"
|
|
2386
2433
|
};
|
|
2387
2434
|
},
|
|
2435
|
+
setServerCors: async (origins) => {
|
|
2436
|
+
corsAllowlist = normalizeOrigins(origins);
|
|
2437
|
+
if (corsAllowlist.includes("*")) logger?.warn?.("[a2a] corsOrigins set to include \"*\": the inbound A2A endpoint now accepts cross-origin browser calls from ANY site.");
|
|
2438
|
+
const next = loadConfig();
|
|
2439
|
+
next.server = {
|
|
2440
|
+
...next.server ?? {},
|
|
2441
|
+
corsOrigins: corsAllowlist.length > 0 ? corsAllowlist : void 0
|
|
2442
|
+
};
|
|
2443
|
+
const res = saveConfig(next);
|
|
2444
|
+
if (!res.ok) {
|
|
2445
|
+
logger?.error?.(`[a2a] failed to persist corsOrigins to ${res.path}: ${res.message}`);
|
|
2446
|
+
return {
|
|
2447
|
+
ok: false,
|
|
2448
|
+
message: `applied in-memory but failed to save: ${res.message}`
|
|
2449
|
+
};
|
|
2450
|
+
}
|
|
2451
|
+
logger?.info?.(`[a2a] inbound CORS allowlist updated (${corsAllowlist.length} origin(s), saved to ${res.path})`);
|
|
2452
|
+
return {
|
|
2453
|
+
ok: true,
|
|
2454
|
+
message: corsAllowlist.length > 0 ? `已更新跨域白名单 (${corsAllowlist.length} 个来源)` : "已清空跨域白名单(默认禁止跨源)"
|
|
2455
|
+
};
|
|
2456
|
+
},
|
|
2388
2457
|
serverStatus: () => ({
|
|
2389
2458
|
ok: true,
|
|
2390
2459
|
message: JSON.stringify({
|
|
2391
2460
|
enabled,
|
|
2392
|
-
...serverCard()
|
|
2461
|
+
...serverCard(),
|
|
2462
|
+
corsOrigins: corsAllowlist
|
|
2393
2463
|
})
|
|
2394
2464
|
})
|
|
2395
2465
|
});
|
|
@@ -2538,6 +2608,42 @@ async function readBody(req) {
|
|
|
2538
2608
|
req.once("error", reject);
|
|
2539
2609
|
});
|
|
2540
2610
|
}
|
|
2611
|
+
/** Normalize a CORS origin allowlist: trim, drop empties, de-duplicate. */
|
|
2612
|
+
function normalizeOrigins(origins) {
|
|
2613
|
+
if (!origins) return [];
|
|
2614
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2615
|
+
for (const o of origins) {
|
|
2616
|
+
const t = typeof o === "string" ? o.trim() : "";
|
|
2617
|
+
if (t) seen.add(t);
|
|
2618
|
+
}
|
|
2619
|
+
return [...seen];
|
|
2620
|
+
}
|
|
2621
|
+
/**
|
|
2622
|
+
* Resolve CORS headers for one inbound A2A request against the configured
|
|
2623
|
+
* origin allowlist. Returns `{}` (no CORS) when the request has no `Origin`,
|
|
2624
|
+
* the allowlist is empty, or the origin isn't allowed — so a disallowed
|
|
2625
|
+
* cross-origin browser call is blocked by the browser exactly as before this
|
|
2626
|
+
* feature existed. An allowed origin is echoed back verbatim (never `*` unless
|
|
2627
|
+
* the operator explicitly configured `['*']`), with `Vary: Origin` so shared
|
|
2628
|
+
* caches don't leak one origin's response to another. Credentials are never
|
|
2629
|
+
* allowed: inbound auth is a Bearer header, not a cookie.
|
|
2630
|
+
*/
|
|
2631
|
+
function corsHeadersFor(reqOrigin, allowed) {
|
|
2632
|
+
if (allowed.length === 0) return {};
|
|
2633
|
+
const wildcard = allowed.includes("*");
|
|
2634
|
+
let allowOrigin;
|
|
2635
|
+
if (wildcard) allowOrigin = reqOrigin ?? "*";
|
|
2636
|
+
else if (reqOrigin && allowed.includes(reqOrigin)) allowOrigin = reqOrigin;
|
|
2637
|
+
if (!allowOrigin) return {};
|
|
2638
|
+
const headers = {
|
|
2639
|
+
"Access-Control-Allow-Origin": allowOrigin,
|
|
2640
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
2641
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
|
2642
|
+
"Access-Control-Max-Age": "86400"
|
|
2643
|
+
};
|
|
2644
|
+
if (allowOrigin !== "*") headers["Vary"] = "Origin";
|
|
2645
|
+
return headers;
|
|
2646
|
+
}
|
|
2541
2647
|
/** Loopback/same-origin trust fence for the dashboard API (no remote write). */
|
|
2542
2648
|
function trustedLoopback(req) {
|
|
2543
2649
|
const host = req.headers.host;
|
|
@@ -42,6 +42,8 @@ export interface PersistedServerConfig {
|
|
|
42
42
|
endpointPath?: string;
|
|
43
43
|
/** Optional shared bearer token protecting the inbound /a2a endpoint. */
|
|
44
44
|
authToken?: string;
|
|
45
|
+
/** Browser origins allowed to call the inbound A2A routes (CORS allowlist); empty/absent = no CORS. `["*"]` opts into wildcard. */
|
|
46
|
+
corsOrigins?: string[];
|
|
45
47
|
skills?: Array<{
|
|
46
48
|
id: string;
|
|
47
49
|
name: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"a2a-config.d.ts","sourceRoot":"","sources":["../../src/a2a-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,eAAO,MAAM,mBAAmB,aAAa,CAAC;AAE9C,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,0DAA0D;IAC1D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CACzG;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,oBAAoB,EAAE,CAAC;IAChC,MAAM,CAAC,EAAE,qBAAqB,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,SAAS,CAaxD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,GAAG,SAAS,CAW5D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAoD1C;AAED,0EAA0E;AAC1E,wBAAgB,UAAU,IAAI,kBAAkB,CAW/C;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,MAAM,EAAE,kBAAkB,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAatG;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,kBAAkB,GAAG,eAAe,
|
|
1
|
+
{"version":3,"file":"a2a-config.d.ts","sourceRoot":"","sources":["../../src/a2a-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,eAAO,MAAM,mBAAmB,aAAa,CAAC;AAE9C,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,0DAA0D;IAC1D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mIAAmI;IACnI,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CACzG;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,oBAAoB,EAAE,CAAC;IAChC,MAAM,CAAC,EAAE,qBAAqB,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,SAAS,CAaxD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,GAAG,SAAS,CAW5D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAoD1C;AAED,0EAA0E;AAC1E,wBAAgB,UAAU,IAAI,kBAAkB,CAW/C;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,MAAM,EAAE,kBAAkB,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAatG;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,kBAAkB,GAAG,eAAe,CAoBpG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.css.d.ts","sourceRoot":"","sources":["../../../src/client/dashboard.css.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,gBAAgB,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"dashboard.css.d.ts","sourceRoot":"","sources":["../../../src/client/dashboard.css.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,gBAAgB,mCAAmC,CAAC;AAwQjE;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,IAAI,CAiBlD"}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* on the product's `--dsw-alias-*` design tokens, so light/dark themes and
|
|
14
14
|
* narrow-window layouts follow the DSH shell. No inline styles.
|
|
15
15
|
*/
|
|
16
|
-
import type { ClientContext } from '@deepseek-ai/
|
|
16
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
17
17
|
import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
18
18
|
import { type ReactElement } from 'react';
|
|
19
19
|
/** Services this plugin needs from the client runtime. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAKpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAsC,KAAK,YAAY,EAAE,MAAM,OAAO,CAAC;AAG9E,0DAA0D;AAC1D,eAAO,MAAM,MAAM,UAAY,CAAC;AAEhC,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CA6B9C;AA8DD,KAAK,YAAY,GAAG,YAAY,CAAC,kBAAkB,CAAC,CAAC;AAiGrD,qDAAqD;AACrD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAgVnE"}
|
package/lib/types/dashboard.d.ts
CHANGED
|
@@ -101,6 +101,8 @@ export interface DashboardControlHooks {
|
|
|
101
101
|
setServerEnabled?: (enabled: boolean) => Promise<ControlResult>;
|
|
102
102
|
/** Set or clear the inbound server's shared bearer token at runtime (persisted). */
|
|
103
103
|
setServerAuthToken?: (token?: string) => Promise<ControlResult>;
|
|
104
|
+
/** Replace the inbound CORS origin allowlist at runtime (persisted); empty = no CORS. */
|
|
105
|
+
setServerCors?: (origins: string[]) => Promise<ControlResult>;
|
|
104
106
|
/** Read current inbound server status (for the serve panel). */
|
|
105
107
|
serverStatus?: () => ControlResult;
|
|
106
108
|
/** Runtime-remove an outbound agent by its connection id. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/dashboard.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/dashboard.ts"],"names":[],"mappings":"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"}
|
package/lib/types/index.d.ts
CHANGED
|
@@ -41,6 +41,22 @@ export interface A2APluginConfig {
|
|
|
41
41
|
execute?: ServerOptions['execute'];
|
|
42
42
|
/** Optional shared bearer token protecting the inbound /a2a endpoint. */
|
|
43
43
|
authToken?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Browser origins allowed to call the inbound A2A routes cross-origin (CORS).
|
|
46
|
+
* An explicit allowlist of exact origins (scheme+host+port), e.g.
|
|
47
|
+
* `['http://localhost:8080', 'https://app.example.com']`.
|
|
48
|
+
*
|
|
49
|
+
* DEFAULT IS EMPTY = NO CORS: a browser page from another origin cannot call
|
|
50
|
+
* the endpoint, which is the safe default — with server mode's optional (and
|
|
51
|
+
* off-by-default) `authToken`, a wildcard would let any site the user visits
|
|
52
|
+
* drive their local agent. Each request's `Origin` is matched against this
|
|
53
|
+
* list; a hit echoes that exact origin back (with `Vary: Origin`), a miss
|
|
54
|
+
* sends no CORS headers. The literal `['*']` opts into a wildcard (logged as
|
|
55
|
+
* a warning) for fully-trusted local setups. Credentials are never allowed
|
|
56
|
+
* (auth rides the Bearer header, not cookies). Scoped to the A2A protocol
|
|
57
|
+
* routes only — the dashboard API (`/a2a/api`) stays loopback/same-origin.
|
|
58
|
+
*/
|
|
59
|
+
corsOrigins?: string[];
|
|
44
60
|
/** Serve the dashboard API on the webserver (default true). */
|
|
45
61
|
dashboard?: boolean;
|
|
46
62
|
}
|
package/lib/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAMjE,OAAO,EAAa,KAAK,aAAa,EAA0B,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAY5G,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IAEpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,8DAA8D;IAC9D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IACnC,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
package/lib/types/server.d.ts
CHANGED
|
@@ -149,6 +149,16 @@ export declare class A2AServer {
|
|
|
149
149
|
endpointChanged: boolean;
|
|
150
150
|
};
|
|
151
151
|
private buildCard;
|
|
152
|
+
/**
|
|
153
|
+
* True when the request carries the configured bearer token. Public wrapper
|
|
154
|
+
* for transports that must decide response headers/status BEFORE streaming
|
|
155
|
+
* frames — see index.ts's SSE branch, which must send `content-type:
|
|
156
|
+
* text/event-stream` + CORS headers before the first `res.write()`, and
|
|
157
|
+
* therefore needs the auth outcome up front.
|
|
158
|
+
*/
|
|
159
|
+
checkAuth(req: {
|
|
160
|
+
headers?: Record<string, string>;
|
|
161
|
+
}): boolean;
|
|
152
162
|
/** True when the request carries the configured bearer token. */
|
|
153
163
|
private authorized;
|
|
154
164
|
private emit;
|
|
@@ -238,8 +248,7 @@ interface AgentUserMessage {
|
|
|
238
248
|
readonly role: 'user';
|
|
239
249
|
readonly content: AgentTextBlock[];
|
|
240
250
|
readonly source: {
|
|
241
|
-
kind:
|
|
242
|
-
plugin: string;
|
|
251
|
+
kind: string;
|
|
243
252
|
};
|
|
244
253
|
}
|
|
245
254
|
/** The slice of a live `Agent` we drive (mirrors `@deepseek-ai/dsh-agent` `Agent`). */
|
|
@@ -324,7 +333,7 @@ export interface DshAgentExecutorOptions {
|
|
|
324
333
|
* absolute — pass `process.cwd()` (the profile's workspace) at wire time.
|
|
325
334
|
*/
|
|
326
335
|
cwd: string;
|
|
327
|
-
/**
|
|
336
|
+
/** Producer tag stamped on inbound messages (defaults to `'a2a'`); serialized as `plugin:<name>` under session format v4. */
|
|
328
337
|
plugin?: string;
|
|
329
338
|
/** Optional static per-agent model routing forwarded to `create()`. */
|
|
330
339
|
agentOptions?: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ryubyte/dsh-a2a",
|
|
3
3
|
"description": "Agent2Agent (A2A) protocol v1.0 plugin for DeepSeek Harness (DSH): outbound client (remote AgentCard skills as ctx.tools), inbound server (A2A JSON-RPC + AgentCard endpoint), and a settings dashboard of live A2A connections",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"a2a",
|
|
7
|
+
"agent2agent",
|
|
8
|
+
"agent-to-agent",
|
|
9
|
+
"a2a-protocol",
|
|
10
|
+
"deepseek-harness",
|
|
11
|
+
"dsh",
|
|
12
|
+
"dsh-plugin",
|
|
13
|
+
"dsh-bundle",
|
|
14
|
+
"agent",
|
|
15
|
+
"ai-agent",
|
|
16
|
+
"agent-card",
|
|
17
|
+
"json-rpc",
|
|
18
|
+
"sse",
|
|
19
|
+
"interoperability"
|
|
20
|
+
],
|
|
5
21
|
"type": "module",
|
|
6
22
|
"main": "lib/index.js",
|
|
7
23
|
"types": "lib/types/index.d.ts",
|
|
@@ -33,12 +49,12 @@
|
|
|
33
49
|
],
|
|
34
50
|
"license": "MIT",
|
|
35
51
|
"peerDependencies": {
|
|
36
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
37
|
-
"@deepseek-ai/dsh-client-
|
|
38
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.
|
|
39
|
-
"@deepseek-ai/dsh-client-ui-settings": "^0.1.
|
|
40
|
-
"@deepseek-ai/dsh-host-webserver": "^0.1.
|
|
41
|
-
"@deepseek-ai/dsh-tools": "^0.1.
|
|
52
|
+
"@deepseek-ai/cordis": "^4.0.4",
|
|
53
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.7-rc.2",
|
|
54
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.7-rc.2",
|
|
55
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.7-rc.2",
|
|
56
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.7-rc.2",
|
|
57
|
+
"@deepseek-ai/dsh-tools": "^0.1.7-rc.2",
|
|
42
58
|
"react": "^18.2.0",
|
|
43
59
|
"react-dom": "^18.2.0"
|
|
44
60
|
},
|
|
@@ -56,7 +72,7 @@
|
|
|
56
72
|
"dsh": {
|
|
57
73
|
"client": {
|
|
58
74
|
"inject": [
|
|
59
|
-
"@deepseek-ai/dsh-client-
|
|
75
|
+
"@deepseek-ai/dsh-client-ui-renderer",
|
|
60
76
|
"@deepseek-ai/dsh-client-ui-slots",
|
|
61
77
|
"@deepseek-ai/dsh-client-ui-settings"
|
|
62
78
|
],
|
package/src/a2a-config.ts
CHANGED
|
@@ -49,6 +49,8 @@ export interface PersistedServerConfig {
|
|
|
49
49
|
endpointPath?: string;
|
|
50
50
|
/** Optional shared bearer token protecting the inbound /a2a endpoint. */
|
|
51
51
|
authToken?: string;
|
|
52
|
+
/** Browser origins allowed to call the inbound A2A routes (CORS allowlist); empty/absent = no CORS. `["*"]` opts into wildcard. */
|
|
53
|
+
corsOrigins?: string[];
|
|
52
54
|
skills?: Array<{ id: string; name: string; description: string; tags?: string[]; examples?: string[] }>;
|
|
53
55
|
}
|
|
54
56
|
|
|
@@ -247,6 +249,7 @@ export function mergePersisted(base: A2APluginConfig, persisted: PersistedA2ACon
|
|
|
247
249
|
if (pServer.agentVersion) merged.agentVersion = pServer.agentVersion;
|
|
248
250
|
if (pServer.endpointPath) merged.endpointPath = pServer.endpointPath;
|
|
249
251
|
if (pServer.authToken !== undefined) merged.authToken = pServer.authToken;
|
|
252
|
+
if (pServer.corsOrigins !== undefined) merged.corsOrigins = pServer.corsOrigins;
|
|
250
253
|
if (pServer.skills) merged.skills = pServer.skills;
|
|
251
254
|
}
|
|
252
255
|
return merged;
|
|
@@ -193,6 +193,11 @@ const css = String.raw`
|
|
|
193
193
|
.a2a-auth-state[data-on='false'] { color: var(--dsw-alias-label-tertiary, #81858c); }
|
|
194
194
|
.a2a-auth-input-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
195
195
|
.a2a-auth-input-row .a2a-input { flex: 1; min-width: 220px; }
|
|
196
|
+
.a2a-cors-list { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
197
|
+
.a2a-cors-chip { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-family: var(--ds-font-family-code, ui-monospace, monospace); padding: 2px 6px 2px 8px; border-radius: var(--dsw-radius-sm, 6px); background: var(--dsw-alias-interactive-bg-hover, rgba(0,0,0,0.05)); border: .5px solid var(--dsw-alias-border-l3, #d0d3d9); color: var(--dsw-alias-label-secondary, #4a4f57); }
|
|
198
|
+
.a2a-cors-chip-x { border: none; background: none; cursor: pointer; font-size: 14px; line-height: 1; padding: 0 2px; color: var(--dsw-alias-label-tertiary, #81858c); }
|
|
199
|
+
.a2a-cors-chip-x:hover:not(:disabled) { color: var(--dsw-alias-state-error-primary, #f04438); }
|
|
200
|
+
.a2a-cors-chip-x:disabled { cursor: default; opacity: .4; }
|
|
196
201
|
|
|
197
202
|
/* ---- inbound table ---- */
|
|
198
203
|
.a2a-table-wrap { overflow-x: auto; border: 1px solid var(--dsw-alias-border-l1, rgba(127,127,127,.12)); border-radius: 10px; }
|
package/src/client/index.ts
CHANGED
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
* narrow-window layouts follow the DSH shell. No inline styles.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import type { ClientContext } from '@deepseek-ai/
|
|
17
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
18
|
+
// Side-effect type import: augments cordis `Context` with `slots` (the renderer
|
|
19
|
+
// owns that service; the retired `dsh-client-runtime` used to re-export it).
|
|
20
|
+
import type {} from '@deepseek-ai/dsh-client-ui-renderer/client';
|
|
18
21
|
import type {} from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
19
22
|
import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
20
23
|
import { createElement, useEffect, useState, type ReactElement } from 'react';
|
|
@@ -30,24 +33,28 @@ export function apply(ctx: ClientContext): void {
|
|
|
30
33
|
return () => dispose();
|
|
31
34
|
}, 'dsh-a2a: dashboard styles');
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
36
|
+
// Contribute the "A2A 连接" settings section. The `settings.section` slot is
|
|
37
|
+
// declared by `dsh-client-ui-settings`, which may activate AFTER this plugin.
|
|
38
|
+
// A bare `slots.register` throws "slot not declared" when it wins that race
|
|
39
|
+
// (silently swallowed here, so the section never appears). `slots.inject`
|
|
40
|
+
// defers the register until the declaration is committed and re-runs it across
|
|
41
|
+
// declaration lifetimes — the pattern every first-party settings section uses.
|
|
42
|
+
ctx.effect(
|
|
43
|
+
() =>
|
|
44
|
+
ctx.slots.inject('settings.section', () =>
|
|
45
|
+
ctx.slots.register(
|
|
46
|
+
{
|
|
47
|
+
name: 'settings.section',
|
|
48
|
+
id: 'a2a',
|
|
49
|
+
order: 90,
|
|
50
|
+
label: () => 'A2A 连接',
|
|
51
|
+
inject: () => ({}),
|
|
52
|
+
},
|
|
53
|
+
DashboardSection,
|
|
54
|
+
),
|
|
55
|
+
),
|
|
56
|
+
'dsh-a2a: settings section',
|
|
57
|
+
);
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
// ── wire types (mirror the host dashboard.ts) ──────────────────────────────
|
|
@@ -129,6 +136,8 @@ interface ServeStatus {
|
|
|
129
136
|
executor?: 'custom' | 'dsh-agent' | 'none';
|
|
130
137
|
/** Whether the inbound endpoint is token-gated (never the token value). */
|
|
131
138
|
authConfigured?: boolean;
|
|
139
|
+
/** Browser origins allowed to call inbound cross-origin (CORS allowlist); empty = no CORS. */
|
|
140
|
+
corsOrigins?: string[];
|
|
132
141
|
}
|
|
133
142
|
|
|
134
143
|
interface DiscoveredCard {
|
|
@@ -227,6 +236,8 @@ export function DashboardSection(_props: SectionProps): ReactElement {
|
|
|
227
236
|
const [addToken, setAddToken] = useState('');
|
|
228
237
|
/** Inbound authToken input (serve tab); empty string means "clear". */
|
|
229
238
|
const [authInput, setAuthInput] = useState('');
|
|
239
|
+
/** New-origin input for the inbound CORS allowlist editor (serve tab). */
|
|
240
|
+
const [corsInput, setCorsInput] = useState('');
|
|
230
241
|
/** Serve-identity edit mode + form draft (server tab). All hooks stay at the
|
|
231
242
|
* top level in fixed order; a single shared draft avoids per-card hooks. */
|
|
232
243
|
const [editingServe, setEditingServe] = useState(false);
|
|
@@ -511,7 +522,7 @@ export function DashboardSection(_props: SectionProps): ReactElement {
|
|
|
511
522
|
? serveEditPanel(serveDraft, setServeDraft, (also) => void saveServeIdentity(also), () => setEditingServe(false), busy, false)
|
|
512
523
|
: needsServeSetup(serve)
|
|
513
524
|
? serveEditPanel(serveDraft, setServeDraft, (also) => void saveServeIdentity(also), () => setEditingServe(false), busy, true)
|
|
514
|
-
: serveViewPanel(serve, busy, act, authInput, setAuthInput, startEditServe)
|
|
525
|
+
: serveViewPanel(serve, busy, act, authInput, setAuthInput, corsInput, setCorsInput, startEditServe)
|
|
515
526
|
: null,
|
|
516
527
|
// (2) inbound connections — who is calling this DSH's A2A endpoint
|
|
517
528
|
createElement(
|
|
@@ -857,6 +868,8 @@ function serveViewPanel(
|
|
|
857
868
|
act: (a: string, t: string, p?: Record<string, unknown>) => Promise<void>,
|
|
858
869
|
authInput: string,
|
|
859
870
|
setAuthInput: (v: string) => void,
|
|
871
|
+
corsInput: string,
|
|
872
|
+
setCorsInput: (v: string) => void,
|
|
860
873
|
onEdit: () => void,
|
|
861
874
|
): ReactElement {
|
|
862
875
|
return createElement(
|
|
@@ -926,6 +939,7 @@ function serveViewPanel(
|
|
|
926
939
|
),
|
|
927
940
|
serve.enabled ? executorNote(serve) : null,
|
|
928
941
|
serve.enabled ? authControl(serve, busy, act, authInput, setAuthInput) : null,
|
|
942
|
+
serve.enabled ? corsControl(serve, busy, act, corsInput, setCorsInput) : null,
|
|
929
943
|
);
|
|
930
944
|
}
|
|
931
945
|
|
|
@@ -1097,6 +1111,99 @@ function authControl(
|
|
|
1097
1111
|
);
|
|
1098
1112
|
}
|
|
1099
1113
|
|
|
1114
|
+
/**
|
|
1115
|
+
* Inbound CORS allowlist control for the serve tab: shows the allowed browser
|
|
1116
|
+
* origins and lets the operator add/remove them. Empty = no CORS (default),
|
|
1117
|
+
* so cross-origin browser clients are blocked. Origins are exact
|
|
1118
|
+
* scheme+host+port strings; the literal `*` opens it to any site (warned).
|
|
1119
|
+
* Applies live (persisted to a2a.json) — no restart needed.
|
|
1120
|
+
*/
|
|
1121
|
+
function corsControl(
|
|
1122
|
+
serve: { corsOrigins?: string[] },
|
|
1123
|
+
busy: string | null,
|
|
1124
|
+
act: (a: string, t: string, p?: Record<string, unknown>) => Promise<void>,
|
|
1125
|
+
corsInput: string,
|
|
1126
|
+
setCorsInput: (v: string) => void,
|
|
1127
|
+
): ReactElement {
|
|
1128
|
+
const origins = serve.corsOrigins ?? [];
|
|
1129
|
+
const hasWildcard = origins.includes('*');
|
|
1130
|
+
const isBusy = busy === 'set-server-cors:';
|
|
1131
|
+
const save = (next: string[]): void => {
|
|
1132
|
+
void act('set-server-cors', '', { corsOrigins: next });
|
|
1133
|
+
};
|
|
1134
|
+
const addOrigin = (): void => {
|
|
1135
|
+
const v = corsInput.trim();
|
|
1136
|
+
if (!v || origins.includes(v)) { setCorsInput(''); return; }
|
|
1137
|
+
save([...origins, v]);
|
|
1138
|
+
setCorsInput('');
|
|
1139
|
+
};
|
|
1140
|
+
return createElement(
|
|
1141
|
+
'div',
|
|
1142
|
+
{ className: 'a2a-auth' },
|
|
1143
|
+
createElement(
|
|
1144
|
+
'div',
|
|
1145
|
+
{ className: 'a2a-auth-row' },
|
|
1146
|
+
createElement('span', { className: 'a2a-auth-label' }, '跨域访问 (CORS)'),
|
|
1147
|
+
stateDot(origins.length > 0 ? (hasWildcard ? 'reconnecting' : 'connected') : 'disabled'),
|
|
1148
|
+
createElement(
|
|
1149
|
+
'span',
|
|
1150
|
+
{ className: 'a2a-auth-state', 'data-on': String(origins.length > 0) },
|
|
1151
|
+
origins.length === 0
|
|
1152
|
+
? '未开启(浏览器跨源调用被拒)'
|
|
1153
|
+
: hasWildcard
|
|
1154
|
+
? '允许任意来源(*)'
|
|
1155
|
+
: `已允许 ${origins.length} 个来源`,
|
|
1156
|
+
),
|
|
1157
|
+
),
|
|
1158
|
+
// Current allowlist as removable chips.
|
|
1159
|
+
origins.length > 0
|
|
1160
|
+
? createElement(
|
|
1161
|
+
'div',
|
|
1162
|
+
{ className: 'a2a-cors-list' },
|
|
1163
|
+
...origins.map((o) =>
|
|
1164
|
+
createElement(
|
|
1165
|
+
'span',
|
|
1166
|
+
{ key: o, className: 'a2a-cors-chip' },
|
|
1167
|
+
o,
|
|
1168
|
+
createElement(
|
|
1169
|
+
'button',
|
|
1170
|
+
{
|
|
1171
|
+
className: 'a2a-cors-chip-x',
|
|
1172
|
+
title: '移除',
|
|
1173
|
+
disabled: isBusy,
|
|
1174
|
+
onClick: () => save(origins.filter((x) => x !== o)),
|
|
1175
|
+
},
|
|
1176
|
+
'×',
|
|
1177
|
+
),
|
|
1178
|
+
),
|
|
1179
|
+
),
|
|
1180
|
+
)
|
|
1181
|
+
: null,
|
|
1182
|
+
createElement(
|
|
1183
|
+
'div',
|
|
1184
|
+
{ className: 'a2a-auth-input-row' },
|
|
1185
|
+
createElement('input', {
|
|
1186
|
+
className: 'a2a-input',
|
|
1187
|
+
type: 'text',
|
|
1188
|
+
placeholder: '添加来源,如 http://localhost:8080 或 *',
|
|
1189
|
+
value: corsInput,
|
|
1190
|
+
disabled: isBusy,
|
|
1191
|
+
onChange: (e: { target: { value: string } }) => setCorsInput(e.target.value),
|
|
1192
|
+
onKeyDown: (e: { key: string }) => { if (e.key === 'Enter') addOrigin(); },
|
|
1193
|
+
}),
|
|
1194
|
+
actionBtn('添加', true, addOrigin, isBusy || !corsInput.trim()),
|
|
1195
|
+
origins.length > 0 ? actionBtn('清空', false, () => save([]), isBusy) : null,
|
|
1196
|
+
),
|
|
1197
|
+
createElement(
|
|
1198
|
+
'div',
|
|
1199
|
+
{ className: 'a2a-hint' },
|
|
1200
|
+
hasWildcard
|
|
1201
|
+
? '⚠️ 已允许任意来源(*):用户浏览器打开的任何网站都能跨源调用本入站端点。仅在完全可信的本机环境使用,并建议同时配置入站鉴权 token。'
|
|
1202
|
+
: '填入完整来源(协议+主机+端口,精确匹配)。留空则默认禁止浏览器跨源调用;写 * 可放开任意来源(不推荐)。跨域配置明文存入 a2a.json,实时生效。',
|
|
1203
|
+
),
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1100
1207
|
function inboundRow(p: InboundPeerApi, busy: string | null, act: (a: string, t: string) => void): ReactElement {
|
|
1101
1208
|
return createElement(
|
|
1102
1209
|
'tr',
|
package/src/dashboard.ts
CHANGED
|
@@ -98,6 +98,8 @@ export interface DashboardControlHooks {
|
|
|
98
98
|
setServerEnabled?: (enabled: boolean) => Promise<ControlResult>;
|
|
99
99
|
/** Set or clear the inbound server's shared bearer token at runtime (persisted). */
|
|
100
100
|
setServerAuthToken?: (token?: string) => Promise<ControlResult>;
|
|
101
|
+
/** Replace the inbound CORS origin allowlist at runtime (persisted); empty = no CORS. */
|
|
102
|
+
setServerCors?: (origins: string[]) => Promise<ControlResult>;
|
|
101
103
|
/** Read current inbound server status (for the serve panel). */
|
|
102
104
|
serverStatus?: () => ControlResult;
|
|
103
105
|
/** Runtime-remove an outbound agent by its connection id. */
|
|
@@ -321,6 +323,14 @@ export class DashboardRegistry {
|
|
|
321
323
|
? this.hooks.setServerAuthToken(token)
|
|
322
324
|
: { ok: false, message: 'set-server-auth is not wired (server mode not mounted)' };
|
|
323
325
|
}
|
|
326
|
+
case 'set-server-cors': {
|
|
327
|
+
// Replace the CORS origin allowlist wholesale; empty array = no CORS.
|
|
328
|
+
const raw = payload?.corsOrigins;
|
|
329
|
+
const origins = Array.isArray(raw) ? raw.filter((o): o is string => typeof o === 'string') : [];
|
|
330
|
+
return this.hooks.setServerCors
|
|
331
|
+
? this.hooks.setServerCors(origins)
|
|
332
|
+
: { ok: false, message: 'set-server-cors is not wired (server mode not mounted)' };
|
|
333
|
+
}
|
|
324
334
|
case 'server-status':
|
|
325
335
|
return this.hooks.serverStatus
|
|
326
336
|
? Promise.resolve(this.hooks.serverStatus())
|
package/src/index.ts
CHANGED
|
@@ -60,6 +60,22 @@ export interface A2APluginConfig {
|
|
|
60
60
|
execute?: ServerOptions['execute'];
|
|
61
61
|
/** Optional shared bearer token protecting the inbound /a2a endpoint. */
|
|
62
62
|
authToken?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Browser origins allowed to call the inbound A2A routes cross-origin (CORS).
|
|
65
|
+
* An explicit allowlist of exact origins (scheme+host+port), e.g.
|
|
66
|
+
* `['http://localhost:8080', 'https://app.example.com']`.
|
|
67
|
+
*
|
|
68
|
+
* DEFAULT IS EMPTY = NO CORS: a browser page from another origin cannot call
|
|
69
|
+
* the endpoint, which is the safe default — with server mode's optional (and
|
|
70
|
+
* off-by-default) `authToken`, a wildcard would let any site the user visits
|
|
71
|
+
* drive their local agent. Each request's `Origin` is matched against this
|
|
72
|
+
* list; a hit echoes that exact origin back (with `Vary: Origin`), a miss
|
|
73
|
+
* sends no CORS headers. The literal `['*']` opts into a wildcard (logged as
|
|
74
|
+
* a warning) for fully-trusted local setups. Credentials are never allowed
|
|
75
|
+
* (auth rides the Bearer header, not cookies). Scoped to the A2A protocol
|
|
76
|
+
* routes only — the dashboard API (`/a2a/api`) stays loopback/same-origin.
|
|
77
|
+
*/
|
|
78
|
+
corsOrigins?: string[];
|
|
63
79
|
// dashboard
|
|
64
80
|
/** Serve the dashboard API on the webserver (default true). */
|
|
65
81
|
dashboard?: boolean;
|
|
@@ -671,15 +687,37 @@ export function apply(ctx: Context, config: A2APluginConfig = {}) {
|
|
|
671
687
|
// keeps routes off until the UI enables the server.
|
|
672
688
|
let enabled = wantsServer;
|
|
673
689
|
let disposers: Array<() => void> = [];
|
|
690
|
+
// Mutable CORS allowlist, read per-request so a UI change takes effect
|
|
691
|
+
// live (like authToken) without rebuilding routes. Normalized to unique,
|
|
692
|
+
// trimmed, non-empty entries. Empty = no CORS (safe default).
|
|
693
|
+
let corsAllowlist: string[] = normalizeOrigins(mergedConfig.corsOrigins);
|
|
694
|
+
if (corsAllowlist.includes('*')) {
|
|
695
|
+
logger?.warn?.(
|
|
696
|
+
'[a2a] corsOrigins includes "*": the inbound A2A endpoint accepts cross-origin browser calls from ANY site. ' +
|
|
697
|
+
'Set an explicit origin allowlist unless this host is fully trusted and, ideally, gate it with authToken.',
|
|
698
|
+
);
|
|
699
|
+
}
|
|
674
700
|
|
|
675
701
|
const registerRoutes = (): void => {
|
|
702
|
+
// CORS for the A2A protocol routes so allowlisted browser clients
|
|
703
|
+
// (Flutter Web, plain fetch()) can call cross-origin. Resolved
|
|
704
|
+
// per-request against `corsAllowlist` (read live so a UI change lands
|
|
705
|
+
// without a route rebuild); empty allowlist = no CORS. Scoped to these
|
|
706
|
+
// routes only — /a2a/api stays loopback-fenced.
|
|
707
|
+
const cors = (req: IncomingMessage): Record<string, string> =>
|
|
708
|
+
corsHeadersFor(req.headers.origin, corsAllowlist);
|
|
676
709
|
// AgentCard discovery route.
|
|
677
710
|
disposers.push(
|
|
678
711
|
webServer.register({
|
|
679
712
|
kind: 'exact',
|
|
680
713
|
path: '/.well-known/agent-card.json',
|
|
681
|
-
handler: async (
|
|
682
|
-
|
|
714
|
+
handler: async (req: IncomingMessage, res: ServerResponse) => {
|
|
715
|
+
if (req.method === 'OPTIONS') {
|
|
716
|
+
res.writeHead(204, cors(req));
|
|
717
|
+
res.end();
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
res.writeHead(200, { 'content-type': 'application/json', ...cors(req) });
|
|
683
721
|
res.end(JSON.stringify(server.card));
|
|
684
722
|
},
|
|
685
723
|
}),
|
|
@@ -690,30 +728,49 @@ export function apply(ctx: Context, config: A2APluginConfig = {}) {
|
|
|
690
728
|
kind: 'prefix',
|
|
691
729
|
path: server.endpointPath,
|
|
692
730
|
handler: async (req: IncomingMessage, res: ServerResponse) => {
|
|
731
|
+
const ch = cors(req);
|
|
732
|
+
// Answer the CORS preflight before any routing — a browser sends
|
|
733
|
+
// OPTIONS first and refuses the real request without these headers.
|
|
734
|
+
if (req.method === 'OPTIONS') {
|
|
735
|
+
res.writeHead(204, ch);
|
|
736
|
+
res.end();
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
693
739
|
const isStream = (req.headers.accept ?? '').includes('text/event-stream');
|
|
694
740
|
if (req.method === 'GET' && (req.url ?? '').startsWith(server.endpointPath + '/card')) {
|
|
695
|
-
res.writeHead(200, { 'content-type': 'application/json' });
|
|
741
|
+
res.writeHead(200, { 'content-type': 'application/json', ...ch });
|
|
696
742
|
res.end(JSON.stringify(server.card));
|
|
697
743
|
return;
|
|
698
744
|
}
|
|
699
745
|
if (req.method !== 'POST') {
|
|
700
|
-
res.writeHead(405, { 'content-type': 'text/plain' });
|
|
746
|
+
res.writeHead(405, { 'content-type': 'text/plain', ...ch });
|
|
701
747
|
res.end('Method Not Allowed');
|
|
702
748
|
return;
|
|
703
749
|
}
|
|
704
750
|
const body = await readBody(req);
|
|
705
751
|
if (isStream) {
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
752
|
+
// Auth must be decided BEFORE the first frame: the SSE success
|
|
753
|
+
// path writes frames via the onEvent callback, and the first
|
|
754
|
+
// res.write() implicitly flushes headers — so we cannot set
|
|
755
|
+
// text/event-stream + CORS afterward. Gate here, write the
|
|
756
|
+
// stream headers up front, then stream.
|
|
757
|
+
if (!server.checkAuth(toServerReq(req))) {
|
|
758
|
+
res.writeHead(401, { 'content-type': 'text/plain', ...ch, 'WWW-Authenticate': 'Bearer' });
|
|
709
759
|
res.end('Unauthorized');
|
|
710
760
|
return;
|
|
711
761
|
}
|
|
762
|
+
res.writeHead(200, {
|
|
763
|
+
'content-type': 'text/event-stream',
|
|
764
|
+
'cache-control': 'no-cache',
|
|
765
|
+
connection: 'keep-alive',
|
|
766
|
+
...ch,
|
|
767
|
+
});
|
|
768
|
+
await server.handleStream(toServerReq(req), body, (frame) => res.write(frame));
|
|
712
769
|
res.end();
|
|
713
770
|
return;
|
|
714
771
|
}
|
|
715
772
|
const out = await server.handle(toServerReq(req), body);
|
|
716
|
-
res.writeHead(out.status, { 'content-type': out.contentType, ...(out.headers ?? {}) });
|
|
773
|
+
res.writeHead(out.status, { 'content-type': out.contentType, ...ch, ...(out.headers ?? {}) });
|
|
717
774
|
res.end(out.body);
|
|
718
775
|
},
|
|
719
776
|
}),
|
|
@@ -830,9 +887,34 @@ export function apply(ctx: Context, config: A2APluginConfig = {}) {
|
|
|
830
887
|
logger?.info?.(`[a2a] inbound authToken ${token ? 'set' : 'cleared'} (saved to ${res.path})`);
|
|
831
888
|
return { ok: true, message: token ? '已设置入站鉴权 token' : '已清除入站鉴权 token' };
|
|
832
889
|
},
|
|
890
|
+
setServerCors: async (origins) => {
|
|
891
|
+
// Replace the allowlist wholesale; read live by the route handlers,
|
|
892
|
+
// so this takes effect on the next request without a route rebuild.
|
|
893
|
+
corsAllowlist = normalizeOrigins(origins);
|
|
894
|
+
if (corsAllowlist.includes('*')) {
|
|
895
|
+
logger?.warn?.(
|
|
896
|
+
'[a2a] corsOrigins set to include "*": the inbound A2A endpoint now accepts cross-origin browser calls from ANY site.',
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
const next: PersistedA2AConfig = loadPersistedA2A();
|
|
900
|
+
next.server = {
|
|
901
|
+
...(next.server ?? {}),
|
|
902
|
+
corsOrigins: corsAllowlist.length > 0 ? corsAllowlist : undefined,
|
|
903
|
+
};
|
|
904
|
+
const res = savePersistedA2A(next);
|
|
905
|
+
if (!res.ok) {
|
|
906
|
+
logger?.error?.(`[a2a] failed to persist corsOrigins to ${res.path}: ${res.message}`);
|
|
907
|
+
return { ok: false, message: `applied in-memory but failed to save: ${res.message}` };
|
|
908
|
+
}
|
|
909
|
+
logger?.info?.(`[a2a] inbound CORS allowlist updated (${corsAllowlist.length} origin(s), saved to ${res.path})`);
|
|
910
|
+
return {
|
|
911
|
+
ok: true,
|
|
912
|
+
message: corsAllowlist.length > 0 ? `已更新跨域白名单 (${corsAllowlist.length} 个来源)` : '已清空跨域白名单(默认禁止跨源)',
|
|
913
|
+
};
|
|
914
|
+
},
|
|
833
915
|
serverStatus: () => ({
|
|
834
916
|
ok: true,
|
|
835
|
-
message: JSON.stringify({ enabled, ...serverCard() }),
|
|
917
|
+
message: JSON.stringify({ enabled, ...serverCard(), corsOrigins: corsAllowlist }),
|
|
836
918
|
}),
|
|
837
919
|
});
|
|
838
920
|
|
|
@@ -1008,6 +1090,47 @@ async function readBody(req: IncomingMessage): Promise<string> {
|
|
|
1008
1090
|
});
|
|
1009
1091
|
}
|
|
1010
1092
|
|
|
1093
|
+
/** Normalize a CORS origin allowlist: trim, drop empties, de-duplicate. */
|
|
1094
|
+
function normalizeOrigins(origins: readonly string[] | undefined): string[] {
|
|
1095
|
+
if (!origins) return [];
|
|
1096
|
+
const seen = new Set<string>();
|
|
1097
|
+
for (const o of origins) {
|
|
1098
|
+
const t = typeof o === 'string' ? o.trim() : '';
|
|
1099
|
+
if (t) seen.add(t);
|
|
1100
|
+
}
|
|
1101
|
+
return [...seen];
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* Resolve CORS headers for one inbound A2A request against the configured
|
|
1106
|
+
* origin allowlist. Returns `{}` (no CORS) when the request has no `Origin`,
|
|
1107
|
+
* the allowlist is empty, or the origin isn't allowed — so a disallowed
|
|
1108
|
+
* cross-origin browser call is blocked by the browser exactly as before this
|
|
1109
|
+
* feature existed. An allowed origin is echoed back verbatim (never `*` unless
|
|
1110
|
+
* the operator explicitly configured `['*']`), with `Vary: Origin` so shared
|
|
1111
|
+
* caches don't leak one origin's response to another. Credentials are never
|
|
1112
|
+
* allowed: inbound auth is a Bearer header, not a cookie.
|
|
1113
|
+
*/
|
|
1114
|
+
function corsHeadersFor(reqOrigin: string | undefined, allowed: readonly string[]): Record<string, string> {
|
|
1115
|
+
if (allowed.length === 0) return {};
|
|
1116
|
+
const wildcard = allowed.includes('*');
|
|
1117
|
+
// `*` echoes the caller's origin when present (so the value is still concrete
|
|
1118
|
+
// and future credentialed use stays possible), else the literal `*`.
|
|
1119
|
+
let allowOrigin: string | undefined;
|
|
1120
|
+
if (wildcard) allowOrigin = reqOrigin ?? '*';
|
|
1121
|
+
else if (reqOrigin && allowed.includes(reqOrigin)) allowOrigin = reqOrigin;
|
|
1122
|
+
if (!allowOrigin) return {};
|
|
1123
|
+
const headers: Record<string, string> = {
|
|
1124
|
+
'Access-Control-Allow-Origin': allowOrigin,
|
|
1125
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
1126
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
1127
|
+
'Access-Control-Max-Age': '86400',
|
|
1128
|
+
};
|
|
1129
|
+
// Vary on Origin whenever the value is request-dependent (i.e. not a static `*`).
|
|
1130
|
+
if (allowOrigin !== '*') headers['Vary'] = 'Origin';
|
|
1131
|
+
return headers;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1011
1134
|
/** Loopback/same-origin trust fence for the dashboard API (no remote write). */
|
|
1012
1135
|
function trustedLoopback(req: IncomingMessage): boolean {
|
|
1013
1136
|
const host = req.headers.host;
|
package/src/server.ts
CHANGED
|
@@ -303,6 +303,17 @@ export class A2AServer {
|
|
|
303
303
|
return card;
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
+
/**
|
|
307
|
+
* True when the request carries the configured bearer token. Public wrapper
|
|
308
|
+
* for transports that must decide response headers/status BEFORE streaming
|
|
309
|
+
* frames — see index.ts's SSE branch, which must send `content-type:
|
|
310
|
+
* text/event-stream` + CORS headers before the first `res.write()`, and
|
|
311
|
+
* therefore needs the auth outcome up front.
|
|
312
|
+
*/
|
|
313
|
+
checkAuth(req: { headers?: Record<string, string> }): boolean {
|
|
314
|
+
return this.authorized(req);
|
|
315
|
+
}
|
|
316
|
+
|
|
306
317
|
/** True when the request carries the configured bearer token. */
|
|
307
318
|
private authorized(req: { headers?: Record<string, string> }): boolean {
|
|
308
319
|
const token = this.options.authToken;
|
|
@@ -697,7 +708,7 @@ interface AgentUserMessage {
|
|
|
697
708
|
readonly id: string;
|
|
698
709
|
readonly role: 'user';
|
|
699
710
|
readonly content: AgentTextBlock[];
|
|
700
|
-
readonly source: { kind:
|
|
711
|
+
readonly source: { kind: string };
|
|
701
712
|
}
|
|
702
713
|
|
|
703
714
|
/** The slice of a live `Agent` we drive (mirrors `@deepseek-ai/dsh-agent` `Agent`). */
|
|
@@ -772,7 +783,7 @@ export interface DshAgentExecutorOptions {
|
|
|
772
783
|
* absolute — pass `process.cwd()` (the profile's workspace) at wire time.
|
|
773
784
|
*/
|
|
774
785
|
cwd: string;
|
|
775
|
-
/**
|
|
786
|
+
/** Producer tag stamped on inbound messages (defaults to `'a2a'`); serialized as `plugin:<name>` under session format v4. */
|
|
776
787
|
plugin?: string;
|
|
777
788
|
/** Optional static per-agent model routing forwarded to `create()`. */
|
|
778
789
|
agentOptions?: { provider?: string; model?: string; maxTokens?: number };
|
|
@@ -935,7 +946,7 @@ export function createDshAgentExecutor(
|
|
|
935
946
|
id: crypto.randomUUID(),
|
|
936
947
|
role: 'user',
|
|
937
948
|
content: [{ type: 'text', text: prompt }],
|
|
938
|
-
source: { kind:
|
|
949
|
+
source: { kind: `plugin:${plugin}` },
|
|
939
950
|
});
|
|
940
951
|
agent.send(userMessage, 'next-turn', true);
|
|
941
952
|
// Fire the one-time naming/grouping hook after the first prompt is queued
|