page-agent-sdk 2.10.2 → 2.11.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "page-agent-sdk",
3
- "version": "2.10.2",
3
+ "version": "2.11.0",
4
4
  "type": "module",
5
5
  "description": "框架无关的页面内 Agent JS SDK —— 以对话框形态挂载到任意网页,通过自定义 tool 读写宿主预注册的数据槽(经 schema 校验 + jsonPath 增量 patch + 快照回退;GET 抓文档),具备 planning/skills/虚拟工作区/context 管理能力。Vue 打包进库,使用者无需安装 Vue。",
6
6
  "main": "./dist/page-agent-sdk.umd.cjs",
@@ -28,6 +28,7 @@
28
28
  "dev": "vite",
29
29
  "mcp:mock": "tsx scripts/mcp-mock-server.ts",
30
30
  "mcp:probe": "tsx scripts/mcp-probe.ts",
31
+ "proxy:mock": "tsx scripts/proxy-mock-server.ts",
31
32
  "verify:probe": "tsx scripts/verify-probe.ts",
32
33
  "build": "npm run build:lib && npm run build:iife",
33
34
  "build:lib": "vite build",
@@ -173,3 +173,45 @@ createChatSdk({
173
173
  - Middleware factories: `createApprovalMiddleware`, `createVerifyMiddleware`, `createWriteBackCheck`, `createSubagentMiddleware`, `createCheckpointMiddleware`, `createUsageHintsMiddleware`
174
174
  - Storage: `createSessionStore`, `createMemoryBackend`, `createWebStorageBackend`, `isQuotaError`
175
175
  - JSON helpers: `jpEval`, `searchJson`, `runSandboxedScript`, `toolError`, `zodError`
176
+
177
+ ## Proxy connection (`createProxyLlm`) — prevent apiKey leakage
178
+
179
+ Browser-direct LLM calls expose `apiKey` in DevTools. Use `createProxyLlm` to unify dev/prod access:
180
+
181
+ ```ts
182
+ import { createChatSdk, createProxyLlm } from 'page-agent-sdk'
183
+
184
+ // Production: proxy mode (server injects real key)
185
+ createChatSdk({
186
+ llm: createProxyLlm({
187
+ mode: 'proxy',
188
+ baseUrl: '/api/llm', // your proxy (same-origin avoids CORS)
189
+ userToken: getUserToken(), // session token (server validates)
190
+ model: 'deepseek-chat',
191
+ refreshToken?: async () => ..., // optional: refresh on 401
192
+ headers?: { 'X-Tenant': 'acme' }, // optional: extra headers
193
+ }),
194
+ ...
195
+ })
196
+
197
+ // Dev: direct mode (browser holds real key; dev only)
198
+ createChatSdk({
199
+ llm: createProxyLlm({
200
+ mode: 'direct',
201
+ apiKey: 'sk-xxx',
202
+ baseUrl: 'https://api.deepseek.com/v1',
203
+ model: 'deepseek-chat',
204
+ }),
205
+ ...
206
+ })
207
+ ```
208
+
209
+ | | `proxy` (prod) | `direct` (dev) |
210
+ |---|---|---|
211
+ | apiKey | server (invisible) | browser (DevTools visible) |
212
+ | browser holds | userToken | real apiKey |
213
+ | token refresh | yes (401 retry) | n/a |
214
+ | headers | supported | n/a |
215
+
216
+ Server-side proxy essentials: validate userToken → inject real apiKey → forward to LLM API; handle CORS; stream SSE through; pass tool-calling fields. See `doc/usage-guide*.md` §8.6 for full guide.
217
+
@@ -146,6 +146,7 @@ const sdk = createChatSdk({
146
146
  5. **Schema whitelist**: `z.object` auto-enables whitelist (only declared fields exposed); `discriminatedUnion`/`record`/`lazy` non-top-level don't enable (fully open)
147
147
  6. **Vue2 new-property non-reactive**: `write` patch `set` on a new field — Vue2 `Object.defineProperty` won't react → `onEvent('data_change')` `tick++`, use `:key="tick"` to force rebuild
148
148
  7. **MCP cold-start injects 0 tools**: `vite.config.ts` `optimizeDeps.include` pre-declares SDK sub-paths; keep those entries when forking config, else first MCP page load injects nothing (reload fixes)
149
+ 8. **apiKey leakage in production**: browser-direct LLM calls expose `apiKey` in DevTools. Use `createProxyLlm({ mode:'proxy', baseUrl:'/api/llm', userToken })` — browser holds only user token, your server injects the real key. See `references/quickstart.md` Stage 8 + `doc/usage-guide*.md` §8.6.
149
150
 
150
151
  ## Verification checklist
151
152
 
@@ -155,6 +156,8 @@ const sdk = createChatSdk({
155
156
  - [ ] [Reactive bind] edits → UI reacts; [non-reactive] `data_change` → re-render
156
157
  - [ ] [Drawer mode] close then open (`show()`) → history & in-flight generation preserved
157
158
  - [ ] Schema validation failure → structured error, no write
159
+ - [ ] [Production] `createProxyLlm({ mode:'proxy' })` — real apiKey not in browser bundle / network tab
160
+ - [ ] [Production] `createProxyLlm({ mode:'proxy' })` — real apiKey not in browser bundle / network tab
158
161
 
159
162
  ## References
160
163
 
@@ -177,3 +180,4 @@ Per your business scenario, fill in `systemPrompt` / `skills` / `data.schema`:
177
180
  - **Multi-agent on one page**: same `id` + `shareContext:true` → multiple dialogs share one `AgentCore`; or independent `id`s + `dialog.drawer:true` + `hide`/`show` for exclusive switching
178
181
  - **MCP integration**: `mcp:[{transport,url}]` remote tool servers; `@modelcontextprotocol/sdk` optional peerDep
179
182
  - **Dynamic/lazy-loaded schema**: `sdk.setData({ schema, bind })` on component mount to swap main data; tools pick up immediately, no rebuild
183
+ - **Production proxy (prevent apiKey leakage)**: `createProxyLlm({ mode:'proxy', baseUrl:'/api/llm', userToken, refreshToken })` — browser holds only user token, server injects real key + forwards; dev uses `mode:'direct'` with real key. See `examples/proxy-demo/` + `npm run proxy:mock`
@@ -147,6 +147,40 @@ sdk.getData() // read current config
147
147
 
148
148
  **Full runnable demo**: `examples/dynamic-demo/` (`npm run dev` → `/examples/dynamic-demo/`).
149
149
 
150
+ ## Stage 8 — Production: proxy the LLM (prevent apiKey leakage)
151
+
152
+ Browser-direct LLM calls expose your `apiKey` in DevTools — anyone can drain your quota. **Production must proxy through your server**: the browser holds only a user token; your server injects the real `apiKey` and forwards. Use `createProxyLlm` to unify dev (direct) and prod (proxy) without restructuring:
153
+
154
+ ```ts
155
+ import { createChatSdk, createProxyLlm } from 'page-agent-sdk'
156
+
157
+ // Production: proxy mode (safe — real key stays server-side)
158
+ const sdk = createChatSdk({
159
+ container: '#root',
160
+ llm: createProxyLlm({
161
+ mode: 'proxy',
162
+ baseUrl: '/api/llm', // your proxy (same-origin avoids CORS)
163
+ userToken: getUserToken(), // session token (server validates, swaps in real key)
164
+ model: 'deepseek-chat',
165
+ refreshToken: async () => (await (await fetch('/api/refresh')).json()).token, // auto-refresh on 401
166
+ headers: { 'X-Tenant': 'acme' }, // optional custom headers
167
+ }),
168
+ // ...data, systemPrompt...
169
+ }).mount()
170
+
171
+ // Dev: direct mode (convenient — real key in browser; dev only)
172
+ const sdkDev = createChatSdk({
173
+ container: '#root',
174
+ llm: createProxyLlm({
175
+ mode: 'direct',
176
+ apiKey: 'sk-...', baseUrl: 'https://api.deepseek.com/v1', model: 'deepseek-chat',
177
+ }),
178
+ // ...data, systemPrompt...
179
+ }).mount()
180
+ ```
181
+
182
+ Both modes require an **OpenAI Chat Completions compatible** endpoint (the SDK uses `ChatOpenAI` internally). The proxy must: validate `userToken` → inject real `apiKey` → forward to upstream → stream SSE through → pass `tools`/`tool_calls` through. See `doc/usage-guide*.md` §8.6 for the full guide + a Node.js proxy example, and `examples/proxy-demo/` for a runnable demo (`npm run proxy:mock` + `npm run dev` → `/examples/proxy-demo/`).
183
+
150
184
  ## Next
151
185
 
152
186
  - All options: see [options.md](options.md)
package/types/index.d.ts CHANGED
@@ -1,6 +1,21 @@
1
1
  import { DefineComponent, Ref } from 'vue';
2
2
  export { z } from 'zod';
3
3
 
4
+ // 代理连接模块(防 apiKey 泄露:proxy 代理模式 / direct 直连模式)
5
+ export type ProxyLlmMode = 'proxy' | 'direct';
6
+ export interface ProxyLlmOptions {
7
+ mode: ProxyLlmMode;
8
+ baseUrl?: string;
9
+ userToken?: string;
10
+ apiKey?: string;
11
+ model?: string;
12
+ temperature?: number;
13
+ maxTokens?: number;
14
+ refreshToken?: () => Promise<string>;
15
+ headers?: Record<string, string>;
16
+ }
17
+ export declare function createProxyLlm(opts: ProxyLlmOptions): import('@langchain/core/language_models/chat_models').BaseChatModel;
18
+
4
19
  export interface ToolStep {
5
20
  name: string;
6
21
  args?: any;