@allin-ai/agent-client 0.1.1 → 0.2.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/README.md +75 -42
- package/dist/cli/commands.d.ts +9 -0
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +244 -7
- package/dist/cli/commands.js.map +1 -1
- package/dist/client/index.d.ts +7 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +6 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/state-store.d.ts +1 -1
- package/dist/client/state-store.d.ts.map +1 -1
- package/dist/client/state-store.js +1 -1
- package/dist/client/state-store.js.map +1 -1
- package/dist/client/supervisor.d.ts +37 -1
- package/dist/client/supervisor.d.ts.map +1 -1
- package/dist/client/supervisor.js +159 -10
- package/dist/client/supervisor.js.map +1 -1
- package/dist/client/transport.d.ts +5 -1
- package/dist/client/transport.d.ts.map +1 -1
- package/dist/client/types.d.ts +6 -64
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/types.js +9 -12
- package/dist/client/types.js.map +1 -1
- package/dist/client/wire.d.ts +2 -31
- package/dist/client/wire.d.ts.map +1 -1
- package/dist/client/wire.js +2 -301
- package/dist/client/wire.js.map +1 -1
- package/dist/client/ws-transport.d.ts +5 -1
- package/dist/client/ws-transport.d.ts.map +1 -1
- package/dist/client/ws-transport.js +30 -6
- package/dist/client/ws-transport.js.map +1 -1
- package/dist/config.d.ts +10 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +30 -1
- package/dist/config.js.map +1 -1
- package/dist/control.d.ts +6 -0
- package/dist/control.d.ts.map +1 -1
- package/dist/control.js +25 -0
- package/dist/control.js.map +1 -1
- package/dist/demo/index.d.ts +3 -0
- package/dist/demo/index.d.ts.map +1 -0
- package/dist/demo/index.js +2 -0
- package/dist/demo/index.js.map +1 -0
- package/dist/demo/observable-store.d.ts +55 -0
- package/dist/demo/observable-store.d.ts.map +1 -0
- package/dist/demo/observable-store.js +88 -0
- package/dist/demo/observable-store.js.map +1 -0
- package/dist/demo/projection.d.ts +23 -0
- package/dist/demo/projection.d.ts.map +1 -0
- package/dist/demo/projection.js +47 -0
- package/dist/demo/projection.js.map +1 -0
- package/dist/demo/site.d.ts +30 -0
- package/dist/demo/site.d.ts.map +1 -0
- package/dist/demo/site.js +330 -0
- package/dist/demo/site.js.map +1 -0
- package/dist/demo/static.d.ts +5 -0
- package/dist/demo/static.d.ts.map +1 -0
- package/dist/demo/static.js +53 -0
- package/dist/demo/static.js.map +1 -0
- package/dist/demo/token-registry.d.ts +20 -0
- package/dist/demo/token-registry.d.ts.map +1 -0
- package/dist/demo/token-registry.js +34 -0
- package/dist/demo/token-registry.js.map +1 -0
- package/dist/demo/types.d.ts +8 -0
- package/dist/demo/types.d.ts.map +1 -0
- package/dist/demo/types.js +2 -0
- package/dist/demo/types.js.map +1 -0
- package/dist/hub/agent-hub.d.ts +4 -0
- package/dist/hub/agent-hub.d.ts.map +1 -0
- package/dist/hub/agent-hub.js +352 -0
- package/dist/hub/agent-hub.js.map +1 -0
- package/dist/hub/index.d.ts +3 -0
- package/dist/hub/index.d.ts.map +1 -0
- package/dist/hub/index.js +2 -0
- package/dist/hub/index.js.map +1 -0
- package/dist/hub/testkit/index.d.ts +4 -0
- package/dist/hub/testkit/index.d.ts.map +1 -0
- package/dist/hub/testkit/index.js +4 -0
- package/dist/hub/testkit/index.js.map +1 -0
- package/dist/hub/testkit/memory-hub.d.ts +23 -0
- package/dist/hub/testkit/memory-hub.d.ts.map +1 -0
- package/dist/hub/testkit/memory-hub.js +33 -0
- package/dist/hub/testkit/memory-hub.js.map +1 -0
- package/dist/hub/testkit/memory-store.d.ts +62 -0
- package/dist/hub/testkit/memory-store.d.ts.map +1 -0
- package/dist/hub/testkit/memory-store.js +186 -0
- package/dist/hub/testkit/memory-store.js.map +1 -0
- package/dist/hub/types.d.ts +118 -0
- package/dist/hub/types.d.ts.map +1 -0
- package/dist/hub/types.js +2 -0
- package/dist/hub/types.js.map +1 -0
- package/dist/hub.d.ts +2 -82
- package/dist/hub.d.ts.map +1 -1
- package/dist/hub.js +2 -411
- package/dist/hub.js.map +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/login.d.ts +22 -0
- package/dist/login.d.ts.map +1 -0
- package/dist/login.js +94 -0
- package/dist/login.js.map +1 -0
- package/dist/protocol/index.d.ts +3 -0
- package/dist/protocol/index.d.ts.map +1 -0
- package/dist/protocol/index.js +3 -0
- package/dist/protocol/index.js.map +1 -0
- package/dist/protocol/types.d.ts +111 -0
- package/dist/protocol/types.d.ts.map +1 -0
- package/dist/protocol/types.js +13 -0
- package/dist/protocol/types.js.map +1 -0
- package/dist/protocol/wire.d.ts +25 -0
- package/dist/protocol/wire.d.ts.map +1 -0
- package/dist/protocol/wire.js +337 -0
- package/dist/protocol/wire.js.map +1 -0
- package/dist/runtime/claude.d.ts +32 -9
- package/dist/runtime/claude.d.ts.map +1 -1
- package/dist/runtime/claude.js +18 -2
- package/dist/runtime/claude.js.map +1 -1
- package/dist/runtime/codex.d.ts +12 -0
- package/dist/runtime/codex.d.ts.map +1 -1
- package/dist/runtime/codex.js +31 -6
- package/dist/runtime/codex.js.map +1 -1
- package/dist/runtime/index.d.ts +4 -4
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/pi.d.ts +25 -2
- package/dist/runtime/pi.d.ts.map +1 -1
- package/dist/runtime/pi.js +20 -3
- package/dist/runtime/pi.js.map +1 -1
- package/dist/runtime/registry.d.ts +9 -4
- package/dist/runtime/registry.d.ts.map +1 -1
- package/dist/runtime/registry.js +7 -7
- package/dist/runtime/registry.js.map +1 -1
- package/dist/runtime/runner-child.js +48 -4
- package/dist/runtime/runner-child.js.map +1 -1
- package/dist/wire.d.ts +2 -23
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +2 -6
- package/dist/wire.js.map +1 -1
- package/package.json +80 -24
- package/web/app.js +253 -0
- package/web/index.html +421 -0
- package/web/login.html +85 -0
- package/web/style.css +484 -0
package/web/index.html
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<meta name="color-scheme" content="dark" />
|
|
7
|
+
<title>allinai-agentkit · 独立持久 Agent Client</title>
|
|
8
|
+
<link rel="stylesheet" href="/style.css" />
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<nav class="nav">
|
|
12
|
+
<a class="brand" href="#top">allinai-agentkit</a>
|
|
13
|
+
<div class="nav-links">
|
|
14
|
+
<a href="#how">运作方式</a>
|
|
15
|
+
<a href="#scenarios">使用场景</a>
|
|
16
|
+
<a href="#exports">模块导出</a>
|
|
17
|
+
<a href="#console">控制台</a>
|
|
18
|
+
</div>
|
|
19
|
+
</nav>
|
|
20
|
+
|
|
21
|
+
<main id="top">
|
|
22
|
+
<section class="hero">
|
|
23
|
+
<div class="hero-copy">
|
|
24
|
+
<h1>独立持久的<br />Agent Client</h1>
|
|
25
|
+
<p class="subtext">
|
|
26
|
+
连接 Hub,可靠执行本地 Agent 与受控能力,向上游回报状态。
|
|
27
|
+
本页就是内置 demo:授权你的 client 接入,实时看协议流动。
|
|
28
|
+
</p>
|
|
29
|
+
<div class="cta-row">
|
|
30
|
+
<a class="btn primary" href="#console">接入你的 Agent</a>
|
|
31
|
+
<a class="btn ghost" href="#exports">查看模块导出</a>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
<div class="hero-terminal" aria-label="快速开始命令">
|
|
35
|
+
<div class="term-bar">
|
|
36
|
+
<span class="term-dot"></span>
|
|
37
|
+
<span class="term-title">quickstart.sh</span>
|
|
38
|
+
<button class="copy-btn" type="button" data-copy>复制全部</button>
|
|
39
|
+
</div>
|
|
40
|
+
<pre class="term-body"><code><span class="c"># 终端 A:启动官网与 demo hub</span>
|
|
41
|
+
node bin/allinai-agent demo
|
|
42
|
+
|
|
43
|
+
<span class="c"># 终端 B:浏览器授权(会打开本站授权页)</span>
|
|
44
|
+
node bin/allinai-agent login \
|
|
45
|
+
--hub http://127.0.0.1:4317
|
|
46
|
+
|
|
47
|
+
<span class="c"># 终端 B:常驻接入,然后回到本页控制台</span>
|
|
48
|
+
node bin/allinai-agent daemon</code></pre>
|
|
49
|
+
</div>
|
|
50
|
+
</section>
|
|
51
|
+
|
|
52
|
+
<section id="how" class="section">
|
|
53
|
+
<h2>运作方式</h2>
|
|
54
|
+
<ol class="steps">
|
|
55
|
+
<li>
|
|
56
|
+
<span class="step-no">01</span>
|
|
57
|
+
<div>
|
|
58
|
+
<h3>浏览器授权</h3>
|
|
59
|
+
<p>
|
|
60
|
+
client 持有唯一 clientId,执行 login 后在授权页确认。
|
|
61
|
+
web 只在内存登记 token 到 clientId 的映射,重启即清空,
|
|
62
|
+
不发放任何全局令牌。
|
|
63
|
+
</p>
|
|
64
|
+
</div>
|
|
65
|
+
</li>
|
|
66
|
+
<li>
|
|
67
|
+
<span class="step-no">02</span>
|
|
68
|
+
<div>
|
|
69
|
+
<h3>连接与注册</h3>
|
|
70
|
+
<p>
|
|
71
|
+
daemon 通过 WebSocket 长连 Hub,凭已授权 token 注册,
|
|
72
|
+
断线自动重连,注册与心跳在控制台实时可见。
|
|
73
|
+
</p>
|
|
74
|
+
</div>
|
|
75
|
+
</li>
|
|
76
|
+
<li>
|
|
77
|
+
<span class="step-no">03</span>
|
|
78
|
+
<div>
|
|
79
|
+
<h3>派发与回报</h3>
|
|
80
|
+
<p>
|
|
81
|
+
从控制台派发 agent.run,client 在本地执行并持续上报事件,
|
|
82
|
+
全程走版本化的 v2 wire 协议。
|
|
83
|
+
</p>
|
|
84
|
+
</div>
|
|
85
|
+
</li>
|
|
86
|
+
</ol>
|
|
87
|
+
</section>
|
|
88
|
+
|
|
89
|
+
<section id="scenarios" class="section">
|
|
90
|
+
<h2>使用场景</h2>
|
|
91
|
+
|
|
92
|
+
<div class="scenario">
|
|
93
|
+
<div class="scenario-head">
|
|
94
|
+
<span class="step-no">A</span>
|
|
95
|
+
<div>
|
|
96
|
+
<h3>宿主服务:挂一个 Hub</h3>
|
|
97
|
+
<p>
|
|
98
|
+
把协议 Hub 挂进你现有的 HTTP 服务,鉴权与持久化全部由你实现,
|
|
99
|
+
包只负责协议编排。
|
|
100
|
+
</p>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
<pre class="code-block"><code><span class="c">// 宿主侧:node:http + 你自己的 HubStore</span>
|
|
104
|
+
import { createAgentHub } from "@allin-ai/agent-client/hub";
|
|
105
|
+
|
|
106
|
+
const hub = createAgentHub<MyUser>({
|
|
107
|
+
authorize: (token) => loadUserByToken(token), <span class="c">// 你的鉴权</span>
|
|
108
|
+
store: myDurableStore(), <span class="c">// 你的持久化</span>
|
|
109
|
+
});
|
|
110
|
+
hub.attach(httpServer); <span class="c">// WS 挂到 /api/agent-hub/v2/ws</span>
|
|
111
|
+
|
|
112
|
+
<span class="c">// 向某个 client 派发一次执行</span>
|
|
113
|
+
await hub.offer({
|
|
114
|
+
principal: user,
|
|
115
|
+
targetClientId: "agent-01",
|
|
116
|
+
command: {
|
|
117
|
+
kind: "agent.run",
|
|
118
|
+
commandId: randomUUID(),
|
|
119
|
+
executionId: randomUUID(),
|
|
120
|
+
taskId: "task-1",
|
|
121
|
+
attempt: 1,
|
|
122
|
+
runtime: "codex",
|
|
123
|
+
payload: { prompt: "修复登录页的空指针" },
|
|
124
|
+
},
|
|
125
|
+
});</code></pre>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<div class="scenario">
|
|
129
|
+
<div class="scenario-head">
|
|
130
|
+
<span class="step-no">B</span>
|
|
131
|
+
<div>
|
|
132
|
+
<h3>Agent 端:接入并执行任务</h3>
|
|
133
|
+
<p>
|
|
134
|
+
supervisor 负责连接、注册、断线重连与事件回报;
|
|
135
|
+
状态落 sqlite,执行交给可选的运行时适配。
|
|
136
|
+
</p>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
<pre class="code-block"><code><span class="c">// client 侧:持久执行器</span>
|
|
140
|
+
import {
|
|
141
|
+
ClientStateStore, ClientSupervisor, WsClientTransport,
|
|
142
|
+
} from "@allin-ai/agent-client/client";
|
|
143
|
+
import { createRunnerManager } from "@allin-ai/agent-client/runtime";
|
|
144
|
+
|
|
145
|
+
const supervisor = new ClientSupervisor({
|
|
146
|
+
store: new ClientStateStore(stateDbPath),
|
|
147
|
+
transport: new WsClientTransport({
|
|
148
|
+
hubBaseUrl: "https://hub.example",
|
|
149
|
+
token: await loadToken(), <span class="c">// login 授权后保存的 token</span>
|
|
150
|
+
clientId: "agent-01",
|
|
151
|
+
}),
|
|
152
|
+
runner: createRunnerManager(), <span class="c">// Codex / Claude / Pi,可选</span>
|
|
153
|
+
maxConcurrentRuns: 1,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
await supervisor.start(); <span class="c">// 连接、注册、收任务、上报事件</span></code></pre>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<div class="scenario">
|
|
160
|
+
<div class="scenario-head">
|
|
161
|
+
<span class="step-no">C</span>
|
|
162
|
+
<div>
|
|
163
|
+
<h3>测试与本地联调:内存 Hub</h3>
|
|
164
|
+
<p>
|
|
165
|
+
单测里不需要数据库:内存 Hub 一行起步,协议行为与真实 Hub 完全一致。
|
|
166
|
+
</p>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
<pre class="code-block"><code><span class="c">// 测试侧:易失内存实现,重启即清空</span>
|
|
170
|
+
import { createMemoryHub } from "@allin-ai/agent-client/hub/testkit";
|
|
171
|
+
|
|
172
|
+
const hub = createMemoryHub({ token: "test-token" });
|
|
173
|
+
const server = http.createServer();
|
|
174
|
+
hub.attach(server);
|
|
175
|
+
server.listen(0, "127.0.0.1");
|
|
176
|
+
|
|
177
|
+
<span class="c">// 断言你的宿主逻辑:注册、offer、事件上报</span>
|
|
178
|
+
assert.equal(hub.listClients().length, 0);</code></pre>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
<div class="scenario">
|
|
182
|
+
<div class="scenario-head">
|
|
183
|
+
<span class="step-no">D</span>
|
|
184
|
+
<div>
|
|
185
|
+
<h3>一键 demo 站</h3>
|
|
186
|
+
<p>
|
|
187
|
+
就是本页:官网落地页、内存 Hub、浏览器授权与控制台,
|
|
188
|
+
两个参数起一个。
|
|
189
|
+
</p>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
<pre class="code-block"><code><span class="c">// 程序化启动本站(等价于 allinai-agent demo)</span>
|
|
193
|
+
import { startDemoSite } from "@allin-ai/agent-client/demo";
|
|
194
|
+
|
|
195
|
+
const site = await startDemoSite({ port: 4317 });
|
|
196
|
+
console.log(site.url, site.hubUrl);
|
|
197
|
+
|
|
198
|
+
await site.close();</code></pre>
|
|
199
|
+
</div>
|
|
200
|
+
<div class="scenario">
|
|
201
|
+
<div class="scenario-head">
|
|
202
|
+
<span class="step-no">E</span>
|
|
203
|
+
<div>
|
|
204
|
+
<h3>在 Next.js 集成默认内存 Hub</h3>
|
|
205
|
+
<p>
|
|
206
|
+
Next.js 的 Route Handler 拿不到 WebSocket 升级事件,所以走
|
|
207
|
+
custom server 挂 Hub;页面用 Server Component 直接读
|
|
208
|
+
Store,列出 client 与在线状态。
|
|
209
|
+
</p>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
<pre class="code-block"><code><span class="c">// 1) src/server/hub.ts —— 单例;dev 热更新下也保持同一实例</span>
|
|
213
|
+
import { createMemoryHub } from "@allin-ai/agent-client/hub/testkit";
|
|
214
|
+
|
|
215
|
+
const g = globalThis as unknown as {
|
|
216
|
+
hub?: ReturnType<typeof createMemoryHub>;
|
|
217
|
+
};
|
|
218
|
+
export const hub =
|
|
219
|
+
g.hub ?? createMemoryHub({ authorize: myAuthorize }); <span class="c">// 你的鉴权</span>
|
|
220
|
+
if (process.env.NODE_ENV !== "production") g.hub = hub;
|
|
221
|
+
|
|
222
|
+
<span class="c">// 2) server.ts —— custom server,拿到底层 http.Server 才能挂 WS</span>
|
|
223
|
+
import { createServer } from "node:http";
|
|
224
|
+
import next from "next";
|
|
225
|
+
import { hub } from "./src/server/hub";
|
|
226
|
+
|
|
227
|
+
const app = next({ dev: false });
|
|
228
|
+
await app.prepare();
|
|
229
|
+
const server = createServer((req, res) => app.getRequestHandler()(req, res));
|
|
230
|
+
hub.attach(server); <span class="c">// WS: /api/agent-hub/v2/ws</span>
|
|
231
|
+
server.listen(3000);
|
|
232
|
+
|
|
233
|
+
<span class="c">// 3) src/app/agents/page.tsx —— 页面上列出 client 与状态</span>
|
|
234
|
+
import { hub } from "@/server/hub";
|
|
235
|
+
|
|
236
|
+
export const dynamic = "force-dynamic";
|
|
237
|
+
|
|
238
|
+
export default function AgentsPage() {
|
|
239
|
+
const online = (t: number) => Date.now() - t < 30_000;
|
|
240
|
+
return (
|
|
241
|
+
<ul>
|
|
242
|
+
{hub.listClients().map((c) => (
|
|
243
|
+
<li key={c.clientId}>
|
|
244
|
+
{c.clientId} · {online(c.lastSeen) ? "在线" : "心跳超时"}
|
|
245
|
+
· {c.pendingOutboxCount} 条待投递
|
|
246
|
+
</li>
|
|
247
|
+
))}
|
|
248
|
+
</ul>
|
|
249
|
+
);
|
|
250
|
+
}</code></pre>
|
|
251
|
+
</div>
|
|
252
|
+
|
|
253
|
+
<div class="scenario">
|
|
254
|
+
<div class="scenario-head">
|
|
255
|
+
<span class="step-no">F</span>
|
|
256
|
+
<div>
|
|
257
|
+
<h3>在 Next.js 实现自己的 HubStore</h3>
|
|
258
|
+
<p>
|
|
259
|
+
生产持久化:实现六个方法换成你的数据库,挂法与内存版完全一致;
|
|
260
|
+
页面查询也换成你自己 Store 暴露的读方法。
|
|
261
|
+
</p>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
<pre class="code-block"><code><span class="c">// src/server/store.ts —— HubStore 的数据库实现(六个方法)</span>
|
|
265
|
+
import {
|
|
266
|
+
createAgentHub, type HubStore, type StoredOffer,
|
|
267
|
+
} from "@allin-ai/agent-client/hub";
|
|
268
|
+
|
|
269
|
+
export class MyDbHubStore implements HubStore<MyUser> {
|
|
270
|
+
async registerClient(input) {
|
|
271
|
+
<span class="c">// 幂等:同 principal+clientId 返回稳定 connectionKey;</span>
|
|
272
|
+
<span class="c">// 归属冲突(同 clientId 不同 principal)必须拒绝</span>
|
|
273
|
+
}
|
|
274
|
+
async heartbeat(input) { <span class="c">/* UPDATE clients SET last_seen = now() */</span> }
|
|
275
|
+
async listPendingOffers(input): Promise<StoredOffer[]> {
|
|
276
|
+
<span class="c">/* 只返回该 principal 拥有、未确认的 offer,按持久投递顺序 */</span>
|
|
277
|
+
}
|
|
278
|
+
async enqueueOffer(input): Promise<StoredOffer> {
|
|
279
|
+
<span class="c">/* 先落库再返回;按命令身份去重,connectionKey 与注册一致 */</span>
|
|
280
|
+
}
|
|
281
|
+
async ingestEvents(input) {
|
|
282
|
+
<span class="c">/* (executionId, eventSeq) 去重落库;只回报已持久化的连续</span>
|
|
283
|
+
<span class="c"> 水位;收到即退役 run offer,done/failed 等终态取消 offer */</span>
|
|
284
|
+
}
|
|
285
|
+
async acknowledgePluginSync(input) { <span class="c">/* 记录插件确认水位 */</span> }
|
|
286
|
+
|
|
287
|
+
<span class="c">// 页面查询:内存版的 listClients() 是 testkit 专属糖,</span>
|
|
288
|
+
<span class="c">// 换库后由你的 Store 自己暴露读方法,例如 listClientsFromDb()</span>
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
<span class="c">// src/server/hub.ts —— 换个构造参数,其余(server.ts、页面)不变</span>
|
|
292
|
+
export const hub = createAgentHub({
|
|
293
|
+
authorize: myAuthorize,
|
|
294
|
+
store: new MyDbHubStore(),
|
|
295
|
+
});</code></pre>
|
|
296
|
+
</div>
|
|
297
|
+
</section>
|
|
298
|
+
|
|
299
|
+
<section id="exports" class="section">
|
|
300
|
+
<h2>模块导出</h2>
|
|
301
|
+
<div class="exports-grid">
|
|
302
|
+
<div class="export-cell tint">
|
|
303
|
+
<code>/protocol</code>
|
|
304
|
+
<p>版本化 wire 协议与编解码</p>
|
|
305
|
+
</div>
|
|
306
|
+
<div class="export-cell">
|
|
307
|
+
<code>/hub</code>
|
|
308
|
+
<p>Node HTTP/WebSocket Hub,HubStore 端口</p>
|
|
309
|
+
</div>
|
|
310
|
+
<div class="export-cell">
|
|
311
|
+
<code>/hub/testkit</code>
|
|
312
|
+
<p>内存 Store 与 Hub,本站所用</p>
|
|
313
|
+
</div>
|
|
314
|
+
<div class="export-cell">
|
|
315
|
+
<code>/client</code>
|
|
316
|
+
<p>可重连执行 client 与传输层</p>
|
|
317
|
+
</div>
|
|
318
|
+
<div class="export-cell">
|
|
319
|
+
<code>/runtime</code>
|
|
320
|
+
<p>Codex / Claude / Pi 适配,可选 peer</p>
|
|
321
|
+
</div>
|
|
322
|
+
<div class="export-cell">
|
|
323
|
+
<code>/demo</code>
|
|
324
|
+
<p>本站服务,startDemoSite()</p>
|
|
325
|
+
</div>
|
|
326
|
+
</div>
|
|
327
|
+
</section>
|
|
328
|
+
|
|
329
|
+
<section id="console" class="section console-section">
|
|
330
|
+
<div class="console-head">
|
|
331
|
+
<h2>Demo 控制台</h2>
|
|
332
|
+
<span class="sse-status" id="sse-status" data-state="connecting">
|
|
333
|
+
观测流连接中
|
|
334
|
+
</span>
|
|
335
|
+
</div>
|
|
336
|
+
<p class="hint" id="console-hint">
|
|
337
|
+
内存 Store,重启即清空;demo 仅监听本机回环地址。
|
|
338
|
+
</p>
|
|
339
|
+
<p class="error" id="host-warning" hidden></p>
|
|
340
|
+
|
|
341
|
+
<div class="console-grid">
|
|
342
|
+
<div class="console-left">
|
|
343
|
+
<div class="panel">
|
|
344
|
+
<h3>已接入 Client</h3>
|
|
345
|
+
<ul id="client-list" class="client-list">
|
|
346
|
+
<li class="empty">等待接入。先在终端执行左侧 login 与 daemon。</li>
|
|
347
|
+
</ul>
|
|
348
|
+
</div>
|
|
349
|
+
<div class="panel">
|
|
350
|
+
<h3>派发任务</h3>
|
|
351
|
+
<label class="field-label" for="offer-client">目标 client</label>
|
|
352
|
+
<select id="offer-client"></select>
|
|
353
|
+
<label class="field-label" for="offer-runtime">runtime</label>
|
|
354
|
+
<select id="offer-runtime">
|
|
355
|
+
<option value="codex">codex</option>
|
|
356
|
+
<option value="claude">claude</option>
|
|
357
|
+
<option value="pi">pi</option>
|
|
358
|
+
<option value="zcode">zcode</option>
|
|
359
|
+
</select>
|
|
360
|
+
<label class="field-label" for="offer-project">project(可选,client 本地注册的目录名)</label>
|
|
361
|
+
<input id="offer-project" placeholder="留空使用 runner 默认目录" />
|
|
362
|
+
<label class="field-label" for="offer-prompt">prompt</label>
|
|
363
|
+
<textarea
|
|
364
|
+
id="offer-prompt"
|
|
365
|
+
rows="3"
|
|
366
|
+
placeholder="输入要派发给 Agent 的 prompt"
|
|
367
|
+
></textarea>
|
|
368
|
+
<button id="offer-send" class="btn primary full">派发 agent.run</button>
|
|
369
|
+
<p id="offer-result" class="hint" hidden></p>
|
|
370
|
+
</div>
|
|
371
|
+
<div class="panel">
|
|
372
|
+
<h3>插件推送</h3>
|
|
373
|
+
<p class="hint">向在线 client 下发 plugin.sync 期望状态(Git 插件仓库列表)。</p>
|
|
374
|
+
<label class="field-label" for="plugin-client">目标 client</label>
|
|
375
|
+
<select id="plugin-client"></select>
|
|
376
|
+
<label class="field-label" for="plugin-url">插件 Git URL</label>
|
|
377
|
+
<input id="plugin-url" placeholder="https://example.com/org/plugin.git" />
|
|
378
|
+
<label class="field-label" for="plugin-id">插件 ID</label>
|
|
379
|
+
<input id="plugin-id" placeholder="my-plugin" />
|
|
380
|
+
<button id="plugin-sync" class="btn ghost full">推送 plugin.sync</button>
|
|
381
|
+
<p id="plugin-result" class="hint" hidden></p>
|
|
382
|
+
</div>
|
|
383
|
+
<div class="panel">
|
|
384
|
+
<h3>插件与平台清单</h3>
|
|
385
|
+
<p class="hint">触发 client 上报本地已安装插件与平台探测结果。</p>
|
|
386
|
+
<label class="field-label" for="inventory-client">目标 client</label>
|
|
387
|
+
<select id="inventory-client"></select>
|
|
388
|
+
<button id="inventory-query" class="btn ghost full">查询清单</button>
|
|
389
|
+
<p id="inventory-result" class="hint" hidden></p>
|
|
390
|
+
<div id="inventory-platforms" hidden>
|
|
391
|
+
<h4>平台</h4>
|
|
392
|
+
<table class="inventory-table">
|
|
393
|
+
<thead><tr><th>platform</th><th>installed</th><th>version</th><th>reason</th></tr></thead>
|
|
394
|
+
<tbody></tbody>
|
|
395
|
+
</table>
|
|
396
|
+
</div>
|
|
397
|
+
<div id="inventory-plugins" hidden>
|
|
398
|
+
<h4>插件</h4>
|
|
399
|
+
<table class="inventory-table">
|
|
400
|
+
<thead><tr><th>id</th><th>status</th><th>enabled</th><th>commit</th><th>ref</th></tr></thead>
|
|
401
|
+
<tbody></tbody>
|
|
402
|
+
</table>
|
|
403
|
+
</div>
|
|
404
|
+
</div>
|
|
405
|
+
</div>
|
|
406
|
+
<div class="panel timeline-panel">
|
|
407
|
+
<h3>协议事件时间线</h3>
|
|
408
|
+
<ul id="timeline" class="timeline"></ul>
|
|
409
|
+
</div>
|
|
410
|
+
</div>
|
|
411
|
+
</section>
|
|
412
|
+
</main>
|
|
413
|
+
|
|
414
|
+
<footer class="footer">
|
|
415
|
+
<span>MIT License</span>
|
|
416
|
+
<span>@allin-ai/agent-client</span>
|
|
417
|
+
</footer>
|
|
418
|
+
|
|
419
|
+
<script type="module" src="/app.js"></script>
|
|
420
|
+
</body>
|
|
421
|
+
</html>
|
package/web/login.html
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<meta name="color-scheme" content="dark" />
|
|
7
|
+
<title>授权接入 · allinai-agentkit</title>
|
|
8
|
+
<link rel="stylesheet" href="/style.css" />
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<main class="login-wrap">
|
|
12
|
+
<div class="login-card">
|
|
13
|
+
<a class="brand" href="/">allinai-agentkit</a>
|
|
14
|
+
<h1>授权接入请求</h1>
|
|
15
|
+
<p class="subtext">一个 Agent Client 请求接入本 demo Hub。</p>
|
|
16
|
+
|
|
17
|
+
<div class="login-client">
|
|
18
|
+
<span class="field-label">Client ID</span>
|
|
19
|
+
<code id="client-id">-</code>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<p class="hint">
|
|
23
|
+
仅当你刚刚在本机终端执行了 allinai-agent login,才应点击允许。
|
|
24
|
+
授权仅对本机 demo 有效,web 重启后自动失效。
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
<div class="cta-row">
|
|
28
|
+
<button id="approve" class="btn primary">允许接入</button>
|
|
29
|
+
<button id="deny" class="btn ghost">拒绝</button>
|
|
30
|
+
</div>
|
|
31
|
+
<p id="error" class="error" hidden></p>
|
|
32
|
+
</div>
|
|
33
|
+
</main>
|
|
34
|
+
|
|
35
|
+
<script>
|
|
36
|
+
const params = new URLSearchParams(location.search);
|
|
37
|
+
const clientId = params.get("client_id") ?? "";
|
|
38
|
+
const state = params.get("state") ?? "";
|
|
39
|
+
const redirectUri = params.get("redirect_uri") ?? "";
|
|
40
|
+
document.getElementById("client-id").textContent = clientId || "(缺失)";
|
|
41
|
+
|
|
42
|
+
async function post(path, body) {
|
|
43
|
+
const response = await fetch(path, {
|
|
44
|
+
method: "POST",
|
|
45
|
+
headers: { "content-type": "application/json" },
|
|
46
|
+
body: JSON.stringify(body),
|
|
47
|
+
});
|
|
48
|
+
const payload = await response.json().catch(() => ({}));
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
throw new Error(payload.error ?? `HTTP ${response.status}`);
|
|
51
|
+
}
|
|
52
|
+
return payload;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
document.getElementById("approve").onclick = async () => {
|
|
56
|
+
try {
|
|
57
|
+
const { redirectUrl } = await post("/login/approve", {
|
|
58
|
+
clientId,
|
|
59
|
+
state,
|
|
60
|
+
redirectUri,
|
|
61
|
+
});
|
|
62
|
+
location.href = redirectUrl;
|
|
63
|
+
} catch (error) {
|
|
64
|
+
showError(error);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
document.getElementById("deny").onclick = async () => {
|
|
68
|
+
try {
|
|
69
|
+
const { redirectUrl } = await post("/login/deny", {
|
|
70
|
+
state,
|
|
71
|
+
redirectUri,
|
|
72
|
+
});
|
|
73
|
+
location.href = redirectUrl;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
showError(error);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
function showError(error) {
|
|
79
|
+
const node = document.getElementById("error");
|
|
80
|
+
node.textContent = `授权失败:${error.message}`;
|
|
81
|
+
node.hidden = false;
|
|
82
|
+
}
|
|
83
|
+
</script>
|
|
84
|
+
</body>
|
|
85
|
+
</html>
|