@ryubyte/dsh-a2a 0.1.5 → 0.2.1

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.en.md CHANGED
@@ -9,6 +9,23 @@ It turns a DSH instance into a full A2A peer in both directions:
9
9
  - **Client mode (outbound)** — discover a remote agent via its **AgentCard**, map each **skill** to a DSH model tool (`a2a__<name>__<skill>`), and execute remote tasks through A2A **JSON-RPC**.
10
10
  - **Server mode (inbound)** — expose this DSH to other agents: `/.well-known/agent-card.json` plus a JSON-RPC endpoint (`SendMessage`, `GetTask`, `ListTasks`, `CancelTask`, `SendStreamingMessage` over SSE) on the DSH webserver.
11
11
 
12
+ ## Installation
13
+
14
+ Install into a profile with one command and restart:
15
+
16
+ dsh plugin --profile web add @ryubyte/dsh-a2a
17
+ dsh --profile web
18
+
19
+ `dsh plugin` installs the package into the profile and auto-registers the
20
+ plugin layer (this package declares `dsh.bundle.patch` in `package.json`) — no
21
+ manual configuration is needed.
22
+
23
+ ## Screenshots
24
+
25
+ | Connected agents (this DSH as a client) | Public service (this DSH as an agent) |
26
+ |---|---|
27
+ | ![Connected agents panel](docs/screenshots/panel-client.png) | ![Public service panel](docs/screenshots/panel-server.png) |
28
+
12
29
  ## Features
13
30
 
14
31
  ### Protocol surface (A2A v1.0)
@@ -22,62 +39,34 @@ It turns a DSH instance into a full A2A peer in both directions:
22
39
 
23
40
  ### Connection dashboard (设置 → A2A 连接)
24
41
 
25
- - **Outbound agents** — remote agents this DSH connects to (name, connection state, AgentCard URL, skill/tool counts, last activity), each with **Reconnect / Enable / Disable / Delete** controls. When adding an agent you can supply an optional **Bearer token** — it is sent as `Authorization: Bearer …` on both the AgentCard fetch and every JSON-RPC call, and persisted alongside the agent in `a2a.json`.
26
- - **Inbound peers** — who is calling this DSH (label, source address, first/last seen, task count, streaming flag), with a close control.
27
- - **A2A server** — inbound server status with an online/offline toggle, endpoint and skill overview; it also shows **who executes inbound tasks** (custom executor / this DSH's agent session / none) and an **inbound auth** control to set or clear the shared Bearer token that gates the endpoint (persisted to `a2a.json`, effective immediately).
28
- - The panel refreshes every 3 s; `/a2a/api` serves a JSON snapshot and control endpoints, fenced to loopback/same-origin (the token value is never returned in the snapshot — only whether one is configured).
29
-
30
- ### Runtime configuration (a2a.json)
31
-
32
- Configuration is driven by **`a2a.json`** (per profile, UI-editable) — manage
33
- agents without touching the composition. The file is resolved from the active
34
- profile, not the launch directory: the plugin parses `--profile <name>` from
35
- the process command line (the `dsh web` alias is recognized as the `web`
36
- profile too) and reads/writes `~/.dsh/profiles/<name>/a2a.json`.
37
-
38
- ## Screenshots
39
-
40
- | Outbound agents (who this DSH connects to) | Inbound peers (who calls this DSH) |
41
- |---|---|
42
- | ![Outbound agents dashboard](docs/screenshots/dashboard-outbound.png) | ![Inbound peers dashboard](docs/screenshots/dashboard-inbound.png) |
42
+ The panel is split into two tabs by **role**, and **everything is configurable in the UI — no need to hand-edit `a2a.json`**:
43
43
 
44
- | Inbound with a live record | A2A server status panel |
45
- |---|---|
46
- | ![Inbound peer record](docs/screenshots/server-inbound.png) | ![A2A server status](docs/screenshots/server-serve.png) |
47
-
48
- ## Installation
44
+ **"Connected agents" (this DSH as a client, outbound)**
49
45
 
50
- ### Option 1: from npm (recommended)
46
+ - Lists connected remote agents (name, connection state, AgentCard URL, skill/tool counts, last activity); expand to see each skill's name/tags/description. Each agent has **Reconnect / Settings / Disable / Delete** controls (a disabled agent shows as a greyed-out placeholder card whose control becomes **Enable**).
47
+ - **Settings** — edit that agent's **timeout (timeoutMs)**, **skill mapping (mapSkills)**, and **Bearer token** in place; saving reconnects with the new config to apply it.
48
+ - **Add agent** — enter an AgentCard URL (optional Bearer token) → "Import" to preview the remote name/description/skills → "Connect". Config is written back to `a2a.json`.
51
49
 
52
- Once published to npm, install into the target profile with one command and
53
- restart:
50
+ **"Public service" (this DSH as an agent, inbound)**
54
51
 
55
- dsh plugin --profile <profile> add @ryubyte/dsh-a2a
56
- dsh --profile <profile>
57
-
58
- `dsh plugin` installs the package into the profile and auto-registers the
59
- plugin layer (this package declares `dsh.bundle.patch` in `package.json`) — no
60
- manual configuration is needed.
61
-
62
- ### Option 2: from the repository (development)
63
-
64
- For hacking on the code, requires Node.js >= 22:
65
-
66
- # 1. Clone the repo
67
- git clone https://github.com/ryubyte/dsh-a2a.git
68
- cd dsh-a2a
69
-
70
- # 2. Install deps and build
71
- npm install
72
- npm run build
73
-
74
- # 3. Link into the target profile
75
- dsh plugin --profile <profile> add link:$(pwd)
52
+ - **Visual identity editing** — read-only view of the service name/description/version, endpoint, Agent Card URL (one-click copy) and Skills; click "Edit" to change these fields in place and **fully add/edit/remove Skills**, saving rebuilds the AgentCard immediately (no restart).
53
+ - **Guided setup** — on a fresh install (no `a2a.json`), this tab shows a form pre-filled with sensible defaults; tweak as needed and click "Create & go online" to publish the service — one step generates `a2a.json` and starts serving.
54
+ - **Online / offline** toggle; shows **who executes inbound tasks** (custom executor / this DSH's agent session / none).
55
+ - **Inbound auth** — set or clear the shared Bearer token that gates the endpoint (persisted to `a2a.json`, effective immediately).
56
+ - **Inbound peers** — who is calling this DSH (label, source address, first/last seen, task count, streaming flag), with a close control.
57
+ - The panel refreshes every 3 s; `/a2a/api` serves a JSON snapshot and control endpoints, fenced to loopback/same-origin (the token value is never returned in the snapshot — only whether one is configured).
76
58
 
77
- # 4. Restart
78
- dsh --profile <profile>
59
+ ### Runtime configuration (a2a.json)
79
60
 
80
- After changing source, re-run `npm run build` and restart.
61
+ Configuration is driven by **`a2a.json`** (per profile, fully manageable from the
62
+ UI) — manage agents and the public service without touching the composition. The
63
+ file is resolved from the active profile, not the launch directory: the plugin
64
+ parses `--profile <name>` from the process command line (the `dsh web` alias is
65
+ recognized as the `web` profile too) and reads/writes
66
+ `~/.dsh/profiles/<name>/a2a.json` — configs the UI generates on first use also
67
+ land in that profile directory, so they are read no matter which working
68
+ directory you launch from. Hand-writing `a2a.json` is still supported (below),
69
+ but **a new user can do everything through the UI**.
81
70
 
82
71
  ## Usage
83
72
 
@@ -100,9 +89,10 @@ Declare agents to connect to in the profile's `a2a.json`:
100
89
  }
101
90
  ```
102
91
 
103
- Alternatively, in the Web GUI go to 设置 → A2A 连接 → 添加 Agent, paste an
104
- AgentCard URL to import and connect — the config is written back to
105
- `a2a.json`.
92
+ Alternatively, in the Web GUI go to 设置 → A2A 连接 → "Connected agents" → Add
93
+ agent, paste an AgentCard URL to import and connect — the config is written back
94
+ to `a2a.json`. After connecting, the agent's "Settings" lets you adjust its
95
+ timeout, skill mapping, and token.
106
96
 
107
97
  Once connected, the remote agent's skills appear as model tools:
108
98
  `a2a__<name>__<skill>`.
@@ -113,14 +103,19 @@ different local sessions are isolated into separate conversations.
113
103
 
114
104
  ### 2. Expose this DSH as an A2A agent (inbound)
115
105
 
116
- Enable server mode in `a2a.json`:
106
+ > **Recommended: use the UI.** Open 设置 → A2A 连接 → "Public service"; a fresh
107
+ > install shows a pre-filled form — tweak a few fields and click "Create & go
108
+ > online". No hand-written JSON required. The config below is the equivalent
109
+ > for reference or scripted deployment.
110
+
111
+ Or enable server mode manually in `a2a.json`:
117
112
 
118
113
  ```jsonc
119
114
  {
120
115
  "mode": "server",
121
116
  "server": {
122
117
  "enabled": true, // explicitly turn the inbound server on
123
- // "baseUrl": "http://127.0.0.1:3080", // optional; default: derive from webServer
118
+ // "baseUrl": omit to auto-infer (see below); set only behind a proxy / public domain
124
119
  "agentName": "My DSH Agent",
125
120
  "agentDescription": "A DSH agent over A2A v1.0",
126
121
  "agentVersion": "0.1.0",
@@ -131,14 +126,18 @@ Enable server mode in `a2a.json`:
131
126
  }
132
127
  ```
133
128
 
134
- Other A2A clients discover this DSH via
129
+ When `baseUrl` is left empty (or cleared in the UI), the AgentCard auto-infers the webServer's **real listen address**, so it never advertises a stale port; set it explicitly only when this DSH sits behind a reverse proxy or public domain whose external address differs from the local listener.
130
+
131
+ > **About `mode`**: `mode` (`client` / `server` / `both`) selects which half mounts at startup, defaulting to `client`. It is **optional** — `server.enabled: true` or the presence of `agents` turns on the respective half, so an `a2a.json` generated through the UI usually **omits `mode`** and relies on those fields. Writing `mode` by hand is clearer but equivalent.
132
+
133
+ Other A2A clients discover this DSH via the auto-inferred
135
134
  `http://<host>:<port>/.well-known/agent-card.json` and call the `/a2a` endpoint.
136
135
 
137
136
  #### Who executes an inbound task (executor precedence)
138
137
 
139
138
  On each inbound task the server picks an executor in this order:
140
139
 
141
- 1. **Explicit `execute`** (injected programmatically) — yours wins.
140
+ 1. **Explicit `execute`** (injected via composition config) — yours wins.
142
141
  2. **This DSH's own agent session** (the default, when an agent loop is
143
142
  present) — **one session per A2A `contextId`**: the first task for a context
144
143
  `ctx.agents.create()`s a session (later tasks for the same context reuse it,
@@ -173,37 +172,6 @@ On each inbound task the server picks an executor in this order:
173
172
  > a `/bin/sh -c` command — trusted clients only. For production put a TLS
174
173
  > reverse proxy in front and set `authToken` as needed.
175
174
 
176
- ### 3. Programmatic use
177
-
178
- ```ts
179
- import { A2AClient, A2AServer, TaskStore } from '@ryubyte/dsh-a2a';
180
-
181
- // outbound: connect to a remote agent
182
- const client = await A2AClient.connect('https://agent.example.com');
183
- const { task } = await client.sendMessage({
184
- messageId: crypto.randomUUID(),
185
- role: 'ROLE_USER',
186
- parts: [{ text: 'Draft a report' }],
187
- });
188
- for await (const ev of client.streamMessage({ messageId: crypto.randomUUID(), role: 'ROLE_USER', parts: [{ text: 'hi' }] })) {
189
- // ev.statusUpdate | ev.artifactUpdate | ev.task | ev.message
190
- }
191
-
192
- // inbound: wire a custom executor to your own agent loop
193
- const store = new TaskStore();
194
- const server = new A2AServer({
195
- baseUrl: 'http://127.0.0.1:3080',
196
- agentName: 'My DSH',
197
- agentDescription: '...',
198
- agentVersion: '1.0.0',
199
- execute: async ({ message }) => ({
200
- messageId: crypto.randomUUID(),
201
- role: 'ROLE_AGENT',
202
- parts: [{ text: await myAgentRun(message.parts) }],
203
- }),
204
- }, store);
205
- ```
206
-
207
175
  ## Directory structure
208
176
 
209
177
  src/
@@ -260,12 +228,4 @@ const server = new A2AServer({
260
228
  webserver binds loopback by default, so put a TLS reverse proxy in front
261
229
  before exposing beyond localhost.
262
230
  - Bearer tokens — both the outbound per-agent token and the inbound endpoint
263
- `authToken` — are stored in plaintext in `a2a.json`; secure the file yourself.
264
-
265
- ## Development
266
-
267
- ```bash
268
- npm install
269
- npm run build # tsc(types) + tsdown(lib) + wrap client → lib/client.js
270
- npm test # node:test + tsx (unit/integration) + client-bundle smoke
271
- ```
231
+ `authToken` — are stored in plaintext in `a2a.json`; secure the file yourself.
package/README.md CHANGED
@@ -9,6 +9,21 @@
9
9
  - **Client 模式(出站)**——通过远程 Agent 的 **AgentCard** 发现它,把每个 **skill** 映射为 DSH 模型工具(`a2a__<name>__<skill>`),通过 A2A **JSON-RPC** 执行远程任务。
10
10
  - **Server 模式(入站)**——把本 DSH 暴露给其他 Agent:在 DSH webserver 上提供 `/.well-known/agent-card.json` 与 JSON-RPC 端点(`SendMessage`、`GetTask`、`ListTasks`、`CancelTask`、基于 SSE 的 `SendStreamingMessage`)。
11
11
 
12
+ ## 安装
13
+
14
+ 一行命令装进 profile,重启即生效:
15
+
16
+ dsh plugin --profile web add @ryubyte/dsh-a2a
17
+ dsh --profile web
18
+
19
+ `dsh plugin` 会把包装进 profile 并自动加入插件层(本包在 package.json 里声明了 `dsh.bundle.patch`),无需手动改任何配置。
20
+
21
+ ## 截图
22
+
23
+ | 连接的 Agent(本 DSH 作为客户端) | 对外服务(本 DSH 作为 Agent) |
24
+ |---|---|
25
+ | ![连接的 Agent 面板](docs/screenshots/panel-client.png) | ![对外服务面板](docs/screenshots/panel-server.png) |
26
+
12
27
  ## 功能
13
28
 
14
29
  ### 协议能力(A2A v1.0)
@@ -22,55 +37,26 @@
22
37
 
23
38
  ### 连接面板(设置 → A2A 连接)
24
39
 
25
- - **出站 Agent**:列出本 DSH 连接的远程 Agent(名称、连接状态、AgentCard URL、技能/工具数、最近活动),每个 Agent 支持 **重连 / 启用 / 禁用 / 删除**。添加 Agent 时可填 **Bearer Token**(可选),导入与后续 JSON-RPC 调用都会带上 `Authorization: Bearer …`,并随 Agent 一起持久化到 `a2a.json`。
26
- - **入站连接**:谁在调用本 DSH(来源、地址、首/末次连接、任务数、流式标记),支持关闭。
27
- - **A2A 服务**:入站服务的运行状态,上线 / 下线开关,端点与技能一览;并显示 **入站任务由谁执行**(自定义 executor / 本 DSH agent 会话 / 未配置),以及 **入站鉴权** 开关——可设置或清除保护入站端点的共享 Bearer Token(持久化到 `a2a.json`,实时生效)。
28
- - 面板每 3 秒自动刷新;`/a2a/api` 提供 JSON 快照与控制接口,受 loopback/same-origin 保护(token 值不会通过快照回传,只暴露「是否已配置」)。
29
-
30
- ### 运行时配置(a2a.json)
31
-
32
- 配置以 **`a2a.json`(按 profile 存放,UI 可编辑)为准**,无需改动组合配置即可管理 Agent。文件定位不依赖启动目录:插件解析进程命令行里的 `--profile <name>`(`dsh web` 别名同样识别为 `web` profile),读写 `~/.dsh/profiles/<name>/a2a.json`。
33
-
34
- ## 截图
40
+ 面板按**角色**分为两个 tab,**全部配置都可在 UI 里完成,无需手改 `a2a.json`**:
35
41
 
36
- | 出站 Agent(本 DSH 连接了谁) | 入站连接(谁在调用本 DSH) |
37
- |---|---|
38
- | ![出站 Agent 面板](docs/screenshots/dashboard-outbound.png) | ![入站连接面板](docs/screenshots/dashboard-inbound.png) |
39
-
40
- | 含真实记录的入站 | A2A 服务状态面板 |
41
- |---|---|
42
- | ![入站连接记录](docs/screenshots/server-inbound.png) | ![A2A 服务状态](docs/screenshots/server-serve.png) |
43
-
44
- ## 安装
42
+ **「连接的 Agent」(本 DSH 作为客户端,出站)**
45
43
 
46
- ### 方式一:从 npm 安装(推荐)
44
+ - 列出已连接的远程 Agent(名称、连接状态、AgentCard URL、技能/工具数、最近活动),展开可看每个 skill 的名称/标签/描述;每个 Agent 支持 **重连 / 设置 / 禁用 / 删除**(已禁用的 Agent 显示为灰化占位卡,操作按钮变为 **启用**)。
45
+ - **设置**:就地编辑该 Agent 的 **超时(timeoutMs)**、**技能拆分(mapSkills)**、**Bearer Token**,保存后自动以新配置重连生效。
46
+ - **添加 Agent**:填 AgentCard URL(可选 Bearer Token)→「导入」预览远端名称/描述/技能 →「连接」。配置写回 `a2a.json`。
47
47
 
48
- 插件发布到 npm 后,一行命令装进目标 profile,重启即生效:
48
+ **「对外服务」(本 DSH 作为 Agent 对外,入站)**
49
49
 
50
- dsh plugin --profile <profile> add @ryubyte/dsh-a2a
51
- dsh --profile <profile>
52
-
53
- `dsh plugin` 会把包装进 profile 并自动加入插件层(本包在 package.json 里声明了 `dsh.bundle.patch`),无需手动改任何配置。
54
-
55
- ### 方式二:从仓库安装(开发调试)
56
-
57
- 用于改代码调试,需要 Node.js >= 22 与 pnpm:
58
-
59
- # 1. 克隆仓库
60
- git clone https://github.com/ryubyte/dsh-a2a.git
61
- cd dsh-a2a
62
-
63
- # 2. 安装依赖并构建
64
- npm install
65
- npm run build
66
-
67
- # 3. 链接进目标 profile
68
- dsh plugin --profile <profile> add link:$(pwd)
50
+ - **服务身份可视化编辑**:只读展示服务名/描述/版本、端点、Agent Card URL(一键复制)与 Skills;点「编辑」就地改这些字段并**完整增删改 Skills**,保存即时重建 AgentCard 生效(无需重启)。
51
+ - **引导式初始化**:全新安装(无 `a2a.json`)时,本页显示预填好合理默认值的表单,用户按需修改后点「创建并上线」即可发布服务——一步生成 `a2a.json` 并启动。
52
+ - **上线 / 下线** 开关;显示 **入站任务由谁执行**(自定义 executor / 本 DSH agent 会话 / 未配置)。
53
+ - **入站鉴权**:设置或清除保护入站端点的共享 Bearer Token(持久化到 `a2a.json`,实时生效)。
54
+ - **入站连接**:谁在调用本 DSH(来源、地址、首/末次连接、任务数、流式标记),支持关闭。
55
+ - 面板每 3 秒自动刷新;`/a2a/api` 提供 JSON 快照与控制接口,受 loopback/same-origin 保护(token 值不会通过快照回传,只暴露「是否已配置」)。
69
56
 
70
- # 4. 重启
71
- dsh --profile <profile>
57
+ ### 运行时配置(a2a.json)
72
58
 
73
- 改完源码后重跑 `npm run build` 再重启即可。
59
+ 配置以 **`a2a.json`(按 profile 存放,可完全通过 UI 管理)为准**,无需改动组合配置即可管理 Agent 与对外服务。文件定位不依赖启动目录:插件解析进程命令行里的 `--profile <name>`(`dsh web` 别名同样识别为 `web` profile),读写 `~/.dsh/profiles/<name>/a2a.json`——UI 首次生成的配置也会写到该 profile 目录,从任意工作目录启动都能读到。手写 `a2a.json` 仍然支持(见下文),但**新用户完全靠 UI 即可完成配置与上线**。
74
60
 
75
61
  ## 使用
76
62
 
@@ -93,7 +79,7 @@
93
79
  }
94
80
  ```
95
81
 
96
- 也可以在 Web GUI「设置 → A2A 连接 → 添加 Agent」里输入 AgentCard URL 导入并连接,配置会写回 `a2a.json`。
82
+ 也可以在 Web GUI「设置 → A2A 连接 →『连接的 Agent』→ 添加 Agent」里输入 AgentCard URL 导入并连接,配置会写回 `a2a.json`;连接后点该 Agent 的「设置」还能调整超时、技能拆分与 Token。
97
83
 
98
84
  连接成功后,对方 Agent 的 skills 会作为模型工具出现:`a2a__<name>__<skill>`。
99
85
 
@@ -101,14 +87,16 @@
101
87
 
102
88
  ### 2. 对外提供 A2A 服务(入站)
103
89
 
104
- 同样在 `a2a.json` 里开启 server 模式:
90
+ > **推荐:直接用 UI。** 打开「设置 → A2A 连接 →『对外服务』」,全新安装会显示预填好的表单,改几个字段后点「创建并上线」即可——无需手写下面的 JSON。以下为等价的手写配置,供了解字段或脚本化部署时参考。
91
+
92
+ 也可以在 `a2a.json` 里手动开启 server 模式:
105
93
 
106
94
  ```jsonc
107
95
  {
108
96
  "mode": "server",
109
97
  "server": {
110
98
  "enabled": true, // 显式开启入站服务
111
- // "baseUrl": "http://127.0.0.1:3080", // 可选;默认从 webServer 监听地址推导
99
+ // "baseUrl": 省略即自动推导(见下),仅反向代理/公网域名时才需显式设置
112
100
  "agentName": "My DSH Agent",
113
101
  "agentDescription": "A DSH agent over A2A v1.0",
114
102
  "agentVersion": "0.1.0",
@@ -119,13 +107,17 @@
119
107
  }
120
108
  ```
121
109
 
122
- 之后其他 A2A 客户端通过 `http://<host>:<port>/.well-known/agent-card.json` 发现本 DSH,通过 `/a2a` 端点调用。
110
+ `baseUrl` 留空(或在 UI 里清空)时,AgentCard 会按 webServer 的**真实监听地址**自动推导,不会公布过期端口;仅当本 DSH 位于反向代理或公网域名之后、对外地址与本地监听不同时,才需要显式填写。
111
+
112
+ > **关于 `mode`**:`mode`(`client` / `server` / `both`)控制启动时挂载哪一半,缺省为 `client`。它是**可选**的——`server.enabled: true` 或存在 `agents` 就会分别启用对应半区,所以通过 UI 生成的 `a2a.json` 通常**不写 `mode`**,仅靠这些字段隐式推导行为。手写时显式写 `mode` 更直观,二者等价。
113
+
114
+ 之后其他 A2A 客户端通过自动推导出的 `http://<host>:<port>/.well-known/agent-card.json` 发现本 DSH,通过 `/a2a` 端点调用。
123
115
 
124
116
  #### 入站任务由谁执行(executor 优先级)
125
117
 
126
118
  server 模式收到任务后,按以下优先级选择 executor:
127
119
 
128
- 1. **显式 `execute`**(编程接入时注入)—— 用你的。
120
+ 1. **显式 `execute`**(通过 composition 配置注入)—— 用你的。
129
121
  2. **本 DSH 的 agent 会话**(默认,当 agent 循环就位时)—— 按 A2A `contextId` **一个对话一个会话**:某 `contextId` 首个任务 `ctx.agents.create()` 建会话(后续同 `contextId` 复用同一会话,历史累积;进程重启后按需 `resume`),把消息作为 `next-turn` 唤醒项发给它,等 `whenIdle()`,取最新 assistant 回复作为 A2A artifact。同一 `contextId` 的并发任务按序串行,不同 `contextId` 并行。会话不逐任务释放(否则无法累积上下文),插件卸载/下线时统一释放。开 server 即可「接收并处理任务」,无需写代码。
130
122
  3. **`notConfiguredExecutor`**(兜底)—— 既没注入 `execute`、composition 里又没有可用 agent 循环(如仅 dsh-base 的最小 profile)时,返回「no executor configured — inject one」,**不执行任何命令**。
131
123
 
@@ -137,37 +129,6 @@ server 模式收到任务后,按以下优先级选择 executor:
137
129
  >
138
130
  > 本机自测也可显式传入 `shellExecutor`(把 prompt 当作 shell 命令执行,仅限可信客户端);生产请置于 TLS 反向代理之后并按需配 `authToken`。
139
131
 
140
- ### 3. 编程接入
141
-
142
- ```ts
143
- import { A2AClient, A2AServer, TaskStore } from '@ryubyte/dsh-a2a';
144
-
145
- // 出站:连接远程 Agent
146
- const client = await A2AClient.connect('https://agent.example.com');
147
- const { task } = await client.sendMessage({
148
- messageId: crypto.randomUUID(),
149
- role: 'ROLE_USER',
150
- parts: [{ text: 'Draft a report' }],
151
- });
152
- for await (const ev of client.streamMessage({ messageId: crypto.randomUUID(), role: 'ROLE_USER', parts: [{ text: 'hi' }] })) {
153
- // ev.statusUpdate | ev.artifactUpdate | ev.task | ev.message
154
- }
155
-
156
- // 入站:自定义 executor 接入 DSH agent 循环
157
- const store = new TaskStore();
158
- const server = new A2AServer({
159
- baseUrl: 'http://127.0.0.1:3080',
160
- agentName: 'My DSH',
161
- agentDescription: '...',
162
- agentVersion: '1.0.0',
163
- execute: async ({ message }) => ({
164
- messageId: crypto.randomUUID(),
165
- role: 'ROLE_AGENT',
166
- parts: [{ text: await myAgentRun(message.parts) }],
167
- }),
168
- }, store);
169
- ```
170
-
171
132
  ## 目录结构
172
133
 
173
134
  src/
@@ -203,11 +164,3 @@ const server = new A2AServer({
203
164
  - 入站服务的执行策略见[入站任务由谁执行](#入站任务由谁执行executor-优先级):有 agent 循环时默认由本 DSH 会话执行(绑定工作目录 cwd),否则(且未注入 executor)**拒绝执行**并返回「no executor configured」。`shellExecutor` 仅限本地自测。无论哪种 executor,都切勿向不受信网络暴露 server 模式(默认无鉴权,除非配置 `authToken`)。
204
165
  - A2A 规范要求生产 `AgentInterface.url` 使用 HTTPS;DSH webServer 默认仅绑 loopback,对外暴露前需自行加 TLS。
205
166
  - Bearer token(出站按 Agent 配置的、入站保护端点的 `authToken`)均明文存于 `a2a.json`,请自行保证该文件权限。
206
-
207
- ## 开发
208
-
209
- ```bash
210
- npm install
211
- npm run build # tsc(types) + tsdown(lib) + wrap client → lib/client.js
212
- npm test # node:test + tsx(单元 / 集成)+ client-bundle smoke
213
- ```