@leeoohoo/ui-apps-devkit 0.1.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 +70 -0
- package/bin/chatos-uiapp.js +5 -0
- package/package.json +22 -0
- package/src/cli.js +53 -0
- package/src/commands/dev.js +14 -0
- package/src/commands/init.js +141 -0
- package/src/commands/install.js +55 -0
- package/src/commands/pack.js +72 -0
- package/src/commands/validate.js +103 -0
- package/src/lib/args.js +49 -0
- package/src/lib/config.js +29 -0
- package/src/lib/fs.js +78 -0
- package/src/lib/path-boundary.js +16 -0
- package/src/lib/plugin.js +45 -0
- package/src/lib/template.js +168 -0
- package/src/sandbox/server.js +861 -0
- package/templates/basic/README.md +58 -0
- package/templates/basic/chatos.config.json +5 -0
- package/templates/basic/docs/CHATOS_UI_APPS_AI_CONTRIBUTIONS.md +181 -0
- package/templates/basic/docs/CHATOS_UI_APPS_BACKEND_PROTOCOL.md +74 -0
- package/templates/basic/docs/CHATOS_UI_APPS_HOST_API.md +123 -0
- package/templates/basic/docs/CHATOS_UI_APPS_OVERVIEW.md +110 -0
- package/templates/basic/docs/CHATOS_UI_APPS_PLUGIN_MANIFEST.md +227 -0
- package/templates/basic/docs/CHATOS_UI_PROMPTS_PROTOCOL.md +392 -0
- package/templates/basic/plugin/apps/app/index.mjs +263 -0
- package/templates/basic/plugin/apps/app/mcp-prompt.en.md +7 -0
- package/templates/basic/plugin/apps/app/mcp-prompt.zh.md +7 -0
- package/templates/basic/plugin/apps/app/mcp-server.mjs +15 -0
- package/templates/basic/plugin/backend/index.mjs +37 -0
- package/templates/basic/template.json +7 -0
- package/templates/notepad/README.md +36 -0
- package/templates/notepad/chatos.config.json +4 -0
- package/templates/notepad/docs/CHATOS_UI_APPS_AI_CONTRIBUTIONS.md +181 -0
- package/templates/notepad/docs/CHATOS_UI_APPS_BACKEND_PROTOCOL.md +74 -0
- package/templates/notepad/docs/CHATOS_UI_APPS_HOST_API.md +123 -0
- package/templates/notepad/docs/CHATOS_UI_APPS_OVERVIEW.md +110 -0
- package/templates/notepad/docs/CHATOS_UI_APPS_PLUGIN_MANIFEST.md +227 -0
- package/templates/notepad/docs/CHATOS_UI_PROMPTS_PROTOCOL.md +392 -0
- package/templates/notepad/plugin/apps/app/api.mjs +30 -0
- package/templates/notepad/plugin/apps/app/dom.mjs +14 -0
- package/templates/notepad/plugin/apps/app/ds-tree.mjs +35 -0
- package/templates/notepad/plugin/apps/app/index.mjs +1056 -0
- package/templates/notepad/plugin/apps/app/layers.mjs +338 -0
- package/templates/notepad/plugin/apps/app/markdown.mjs +120 -0
- package/templates/notepad/plugin/apps/app/mcp-prompt.en.md +22 -0
- package/templates/notepad/plugin/apps/app/mcp-prompt.zh.md +22 -0
- package/templates/notepad/plugin/apps/app/mcp-server.mjs +200 -0
- package/templates/notepad/plugin/apps/app/styles.mjs +355 -0
- package/templates/notepad/plugin/apps/app/tags.mjs +21 -0
- package/templates/notepad/plugin/apps/app/ui.mjs +280 -0
- package/templates/notepad/plugin/backend/index.mjs +99 -0
- package/templates/notepad/plugin/plugin.json +23 -0
- package/templates/notepad/plugin/shared/notepad-paths.mjs +62 -0
- package/templates/notepad/plugin/shared/notepad-store.mjs +765 -0
- package/templates/notepad/template.json +8 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# __PLUGIN_NAME__
|
|
2
|
+
|
|
3
|
+
这是一个 **ChatOS UI Apps 插件工程**(UI Apps Plugins)。
|
|
4
|
+
|
|
5
|
+
## 你应该在哪写什么
|
|
6
|
+
|
|
7
|
+
- `plugin/plugin.json`:插件清单(应用列表、入口、后端、AI 贡献)
|
|
8
|
+
- `plugin/apps/__APP_ID__/index.mjs`:**module 入口**(导出 `mount({ container, host, slots })`)
|
|
9
|
+
- `plugin/backend/index.mjs`:**插件后端**(导出 `createUiAppsBackend(ctx)`,通过 `host.backend.invoke()` 调用)
|
|
10
|
+
- `plugin/apps/__APP_ID__/mcp-server.mjs`:应用自带 MCP Server(可选)
|
|
11
|
+
- `plugin/apps/__APP_ID__/mcp-prompt.zh.md` / `.en.md`:MCP Prompt(可选)
|
|
12
|
+
|
|
13
|
+
## 开发与预览(本地沙箱)
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install
|
|
17
|
+
npm run dev
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
沙箱会:
|
|
21
|
+
|
|
22
|
+
- 用 HTTP 运行你的 `module` 入口(模拟 ChatOS 的 `mount()` 调用)
|
|
23
|
+
- 提供 `host.*` 的 mock(含 `host.backend.invoke()`、`host.uiPrompts.*`、`host.chat.*`)
|
|
24
|
+
|
|
25
|
+
## 复用 ChatOS 的 AI 调用(推荐)
|
|
26
|
+
|
|
27
|
+
本模板演示两种“复用宿主模型/密钥/工具链”的方式:
|
|
28
|
+
|
|
29
|
+
1) **前端直连 Chat 域**:用 `host.chat.*` 创建 agent/session、`host.chat.send()` 发送消息、`host.chat.events.subscribe()` 订阅流式事件。
|
|
30
|
+
2) **后端调用 LLM**:在 `plugin/backend/index.mjs` 里通过 `ctx.llm.complete()` 调用模型;前端用 `host.backend.invoke('llmComplete', { input })` 触发。
|
|
31
|
+
|
|
32
|
+
说明:本地沙箱只提供 mock(不会真实调用模型);要验证真实 AI 行为请安装到 ChatOS 后运行。
|
|
33
|
+
|
|
34
|
+
## 安装到本机 ChatOS
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm run validate
|
|
38
|
+
npm run install:chatos
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
或打包成 zip(用于 ChatOS UI:应用 → 导入应用包):
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm run pack
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 协议文档
|
|
48
|
+
|
|
49
|
+
`docs/` 目录包含当前版本的协议快照(建议团队内统一对齐)。
|
|
50
|
+
|
|
51
|
+
## 启用 MCP(可选)
|
|
52
|
+
|
|
53
|
+
本模板默认只启用 `ai.mcpPrompt`(不启用 `ai.mcp`),避免第三方插件在未打包依赖时运行失败。
|
|
54
|
+
|
|
55
|
+
如果你要启用 MCP:
|
|
56
|
+
|
|
57
|
+
1) 实现并打包 `plugin/apps/__APP_ID__/mcp-server.mjs`(建议 bundle 成单文件)
|
|
58
|
+
2) 在 `plugin/plugin.json` 的 `apps[i].ai.mcp` 写入 `entry/command/args/...`
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# ChatOS UI Apps:AI 暴露(MCP / Prompts)协议
|
|
2
|
+
|
|
3
|
+
本文件描述应用如何对 ChatOS 的 Chat Agent 暴露能力,包括:
|
|
4
|
+
|
|
5
|
+
- 声明一个专属 MCP Server(让工具随应用交付、随插件安装)
|
|
6
|
+
- 声明应用的默认 MCP Prompt(告诉模型如何使用这些工具/这个应用)
|
|
7
|
+
- 聚合暴露“已有的”全局 MCP Servers / Prompts(面板型应用常用)
|
|
8
|
+
- 内置应用的“开关 + 精细清单”两层机制(清单在 `aide` 维护)
|
|
9
|
+
|
|
10
|
+
实现对照(以代码为准):
|
|
11
|
+
|
|
12
|
+
- schema:`deepseek_cli/electron/ui-apps/schemas.js`
|
|
13
|
+
- 扫描与贡献解析:`deepseek_cli/electron/ui-apps/index.js`(`#resolveAi` / `getAiContribution()`;可选 `#syncAiContributes` 持久化到 Admin DB)
|
|
14
|
+
- 内置默认清单:`aide/shared/defaults/ui-apps-expose/`
|
|
15
|
+
|
|
16
|
+
## 1. 两种暴露方式(应用侧选择其一或组合)
|
|
17
|
+
|
|
18
|
+
### 方式 A:应用自带 MCP Server + MCP Prompt
|
|
19
|
+
|
|
20
|
+
用于:应用包含专属 tools(数据库查询、系统诊断、内部系统调用等)。
|
|
21
|
+
|
|
22
|
+
在 `apps[i].ai` 里声明:
|
|
23
|
+
|
|
24
|
+
- `ai.mcp`:宿主会创建/更新一个 MCP server(`serverName` 固定为 `${pluginId}.${appId}`)
|
|
25
|
+
- `ai.mcpPrompt`:宿主会创建/更新对应 system prompt(prompt 名称固定派生)
|
|
26
|
+
|
|
27
|
+
示例(节选):
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"id": "db-client",
|
|
32
|
+
"name": "数据库客户端",
|
|
33
|
+
"entry": { "type": "module", "path": "db-client/index.mjs" },
|
|
34
|
+
"ai": {
|
|
35
|
+
"mcp": { "entry": "db-client/mcp-server.mjs", "command": "node", "allowMain": true, "allowSub": true },
|
|
36
|
+
"mcpPrompt": { "zh": "db-client/mcp-prompt.zh.md", "en": "db-client/mcp-prompt.en.md" }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 方式 B:聚合暴露“已有的” MCP servers / Prompts(面板型应用常用)
|
|
42
|
+
|
|
43
|
+
用于:应用以“工作台/面板”形态聚合展示能力,不提供新的 MCP server,但需要按应用维度暴露已有 MCP servers / Prompts。
|
|
44
|
+
|
|
45
|
+
在 `apps[i].ai` 里声明:
|
|
46
|
+
|
|
47
|
+
- `ai.mcpServers: true | false | string[]`
|
|
48
|
+
- `ai.prompts: true | false | string[]`
|
|
49
|
+
|
|
50
|
+
注意:这是“可见/可选范围”的声明;最终是否启用、启用哪些,仍由 Agent 编辑页对该应用的多选结果决定。
|
|
51
|
+
|
|
52
|
+
## 2. 命名规则(宿主自动派生)
|
|
53
|
+
|
|
54
|
+
对每个 app:
|
|
55
|
+
|
|
56
|
+
- MCP `serverName`:`${pluginId}.${appId}`
|
|
57
|
+
- Prompt name:
|
|
58
|
+
- 中文:`mcp_<normalize(serverName)>`
|
|
59
|
+
- 英文:`mcp_<normalize(serverName)>__en`
|
|
60
|
+
|
|
61
|
+
`normalize()`:
|
|
62
|
+
|
|
63
|
+
- 转小写
|
|
64
|
+
- 把非 `[a-z0-9_-]` 的字符替换为 `_`
|
|
65
|
+
- 去掉首尾 `_`
|
|
66
|
+
|
|
67
|
+
## 3. `ai.mcp` 的 url 生成规则(entry → cmd://)
|
|
68
|
+
|
|
69
|
+
当 `ai.mcp.entry` 存在时,宿主会把它转换为:
|
|
70
|
+
|
|
71
|
+
- `cmd://<command> <absEntryOrRelEntry> <args...>`
|
|
72
|
+
- `command` 默认为 `node`
|
|
73
|
+
|
|
74
|
+
当 `ai.mcp.url` 存在时,直接使用该 url(远程/HTTP/WS 等)。
|
|
75
|
+
|
|
76
|
+
## 4. Agent UI 如何消费应用暴露(你需要知道的运行机制)
|
|
77
|
+
|
|
78
|
+
### 4.1 体现在哪里(持久化 vs 运行时注入)
|
|
79
|
+
|
|
80
|
+
当你把插件放进插件目录并在 UI「应用」页点击“刷新”后:
|
|
81
|
+
|
|
82
|
+
1) 宿主扫描插件并解析 `plugin.json`
|
|
83
|
+
2) 若发现 `apps[i].ai`:
|
|
84
|
+
- 宿主会在 UI Apps 注册表中暴露该应用的 `ai` 声明;
|
|
85
|
+
- **无论是否写入 Admin DB**,Chat Agent 运行时都可通过 `uiApps.getAiContribution()` 读取 `mcp.url` 与 `mcpPrompt` 文本,并按需“临时注入”到本次运行(见第 4.3 节)。
|
|
86
|
+
- (可选)若宿主启用 `syncAiContributes`,则会把 `ai.mcp` / `ai.mcpPrompt` **持久化同步**到 Admin DB(出现在 `MCP Servers` / `Prompts` 列表中,便于管理与复用)。
|
|
87
|
+
3) 在 `Chat → Agents` 创建/编辑 agent 时:
|
|
88
|
+
- 先选择应用(按应用维度)
|
|
89
|
+
- 再为每个应用分别勾选 MCP/Prompt,并在多选框中选择要挂载的 servers/prompts
|
|
90
|
+
|
|
91
|
+
### 4.2 保存结构(简化)
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"uiApps": [
|
|
96
|
+
{
|
|
97
|
+
"pluginId": "com.example.tools",
|
|
98
|
+
"appId": "db-client",
|
|
99
|
+
"mcp": true,
|
|
100
|
+
"prompt": true,
|
|
101
|
+
"mcpServerIds": ["<admin.mcpServers.id>", "..."],
|
|
102
|
+
"promptIds": ["<admin.prompts.id>", "..."]
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 4.3 运行时关键规则
|
|
109
|
+
|
|
110
|
+
- 主流程(MAIN)默认只启用 `allowMain=true` 的 MCP server(安全/权限控制)
|
|
111
|
+
- 如果希望 prompt 生效,请确保在应用下勾选 Prompt 并选择需要的 prompts(或提供 app 自己的 `ai.mcpPrompt`)
|
|
112
|
+
|
|
113
|
+
## 5. 内置应用的“开关 + 精细清单”机制
|
|
114
|
+
|
|
115
|
+
内置应用采用两层设计:
|
|
116
|
+
|
|
117
|
+
1) 宿主侧(`deepseek_cli` 的 `plugin.json`)只保留粗粒度开关:
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{ "ai": { "mcpServers": true, "prompts": true } }
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
2) 精细清单由应用团队在 `aide` 侧维护:
|
|
124
|
+
|
|
125
|
+
- 路径:`aide/shared/defaults/ui-apps-expose/<pluginId>__<appId>.yaml`(也支持 `.yml/.json`)
|
|
126
|
+
|
|
127
|
+
示例:
|
|
128
|
+
|
|
129
|
+
```yaml
|
|
130
|
+
mcpServers:
|
|
131
|
+
- project_files
|
|
132
|
+
- task_manager
|
|
133
|
+
|
|
134
|
+
prompts:
|
|
135
|
+
- default
|
|
136
|
+
- internal_main
|
|
137
|
+
- mcp_project_files
|
|
138
|
+
- mcp_task_manager
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
文件名规范:
|
|
142
|
+
|
|
143
|
+
- `<pluginId>__<appId>.yaml`(全部小写;不合法字符替换为 `_`)
|
|
144
|
+
|
|
145
|
+
重要:如果 `plugin.json` 未显式写 `ai.mcpServers/prompts`(或写成 `false`),宿主不会自动启用默认清单,避免“意外暴露”。
|
|
146
|
+
|
|
147
|
+
## 6. 暴露范围的合并优先级(精确规则)
|
|
148
|
+
|
|
149
|
+
对 `mcpServers` / `prompts` 分别适用相同规则(从高到低):
|
|
150
|
+
|
|
151
|
+
1) `plugin.json` inline 的 `ai.mcpServers/ai.prompts`
|
|
152
|
+
2) `ai.config` 文件(插件目录内)的 `mcpServers/prompts`
|
|
153
|
+
3) 内置默认清单(`aide/shared/defaults/ui-apps-expose`)
|
|
154
|
+
|
|
155
|
+
规则要点:
|
|
156
|
+
|
|
157
|
+
- inline 为 `false`:强制关闭(无视文件与默认清单)
|
|
158
|
+
- inline 为 `string[]`:强制使用该列表
|
|
159
|
+
- inline 为 `true`:优先用 `ai.config` 的值;若没有,再用默认清单;若仍没有,表示“全部”
|
|
160
|
+
- inline 未设置:只允许 `ai.config` 生效(不会自动启用默认清单)
|
|
161
|
+
|
|
162
|
+
## 7. MCP Server 最小骨架(Node.js / stdio)
|
|
163
|
+
|
|
164
|
+
MCP server 进程通过 stdio 与宿主通信(不要输出到 stdout,日志写 stderr):
|
|
165
|
+
|
|
166
|
+
```js
|
|
167
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
168
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
169
|
+
|
|
170
|
+
const server = new McpServer({ name: 'my_server', version: '0.1.0' });
|
|
171
|
+
|
|
172
|
+
// server.tool('tool_name', schema, handler) ...
|
|
173
|
+
|
|
174
|
+
const transport = new StdioServerTransport();
|
|
175
|
+
await server.connect(transport);
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
提示:
|
|
179
|
+
|
|
180
|
+
- tools 最终名字通常会变成 `mcp_<serverName>_<toolName>`(由宿主注册/派生)
|
|
181
|
+
- `serverName` 为 MCP server 的 `name`(应用方式 A 下由宿主固定派生为 `${pluginId}.${appId}`)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# ChatOS UI Apps:插件后端协议(Electron main 进程)
|
|
2
|
+
|
|
3
|
+
插件后端用于承载需要 Node 能力的逻辑(数据库/SSH/I/O/调用本地二进制等)。后端运行在 Electron main 进程,通过 IPC 由 `module` 前端调用。
|
|
4
|
+
|
|
5
|
+
实现对照(以代码为准):
|
|
6
|
+
|
|
7
|
+
- `deepseek_cli/electron/ui-apps/index.js`(`uiApps:invoke` 与后端加载/缓存)
|
|
8
|
+
|
|
9
|
+
## 1. 开启后端:`plugin.json`
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"backend": { "entry": "backend/index.mjs" }
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
约束:
|
|
18
|
+
|
|
19
|
+
- `backend.entry` 必须位于插件目录内;
|
|
20
|
+
- 必须是文件;
|
|
21
|
+
- 文件变更后会按 `mtime` 自动 reload(旧实例会尝试调用 `dispose()`)。
|
|
22
|
+
|
|
23
|
+
## 2. 后端入口:`createUiAppsBackend(ctx)`
|
|
24
|
+
|
|
25
|
+
后端入口模块必须导出:
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
export async function createUiAppsBackend(ctx) {
|
|
29
|
+
return {
|
|
30
|
+
methods: {
|
|
31
|
+
async ping(params, ctx2) {
|
|
32
|
+
return { ok: true, echo: params, pluginId: ctx2.pluginId };
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
async dispose() {},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
硬约束:
|
|
41
|
+
|
|
42
|
+
- 必须导出函数 `createUiAppsBackend`
|
|
43
|
+
- `createUiAppsBackend()` 返回值必须包含 `{ methods }`
|
|
44
|
+
|
|
45
|
+
方法调用约定:
|
|
46
|
+
|
|
47
|
+
- 前端调用:`await host.backend.invoke('ping', params)`
|
|
48
|
+
- 实际执行:`methods[method](params, ctx)`
|
|
49
|
+
- 第二个参数 `ctx` 会再次传入(即使你在 `createUiAppsBackend(ctx)` 的闭包里也已拿到一份)
|
|
50
|
+
|
|
51
|
+
## 3. `ctx`(宿主注入的运行时上下文)
|
|
52
|
+
|
|
53
|
+
`ctx` 字段(当前实现):
|
|
54
|
+
|
|
55
|
+
- `pluginId`:插件 ID
|
|
56
|
+
- `pluginDir`:插件安装目录(只读引用;用于读取插件资源)
|
|
57
|
+
- `dataDir`:`<stateDir>/ui_apps/data/<pluginId>`(插件可写数据目录;宿主会确保存在)
|
|
58
|
+
- `stateDir`:`<home>/.deepseek_cli/<hostApp>`(ChatOS 的 `hostApp=chatos`)
|
|
59
|
+
- `sessionRoot`:会话根
|
|
60
|
+
- `projectRoot`:宿主工程根(开发态下有用)
|
|
61
|
+
- `llm`:可选(共享模型调用接口)
|
|
62
|
+
- `ctx.llm.complete({ input, modelId?, modelName?, systemPrompt?, disableTools? })`
|
|
63
|
+
- 在 ChatOS 宿主实现中,`disableTools` 默认启用(除非显式传 `disableTools:false`);返回形如 `{ ok:true, model, content }`
|
|
64
|
+
|
|
65
|
+
规则:
|
|
66
|
+
|
|
67
|
+
- 持久化只写 `dataDir`,并自行做版本化与迁移。
|
|
68
|
+
- 返回值请保持可 JSON 序列化(避免传递函数/循环引用)。
|
|
69
|
+
- 插件后端不保存或读取 API Keys;密钥由宿主注入到环境变量;模型调用通过 `ctx.llm.complete()`。
|
|
70
|
+
|
|
71
|
+
## 4. 错误处理与返回结构
|
|
72
|
+
|
|
73
|
+
- 后端方法抛错会被宿主捕获并返回 `{ ok:false, message }`;
|
|
74
|
+
- 前端侧 `host.backend.invoke()` 会把 `ok:false` 转成异常抛出(便于应用用 `try/catch` 处理)。
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# ChatOS UI Apps:`module` 入口与 Host API(协议)
|
|
2
|
+
|
|
3
|
+
本文件定义 UI Apps 的 `module` 入口规范,以及宿主注入的 `host` 对象(应用与 ChatOS 交互的稳定契约)。
|
|
4
|
+
|
|
5
|
+
实现对照(以代码为准):
|
|
6
|
+
|
|
7
|
+
- `deepseek_cli/apps/ui/src/features/apps/AppsPluginView.jsx`
|
|
8
|
+
|
|
9
|
+
## 1. module 入口规范
|
|
10
|
+
|
|
11
|
+
`plugin.json` 中的入口示例:
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{ "type": "module", "path": "my-app/index.mjs" }
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
入口模块需导出 `mount`(以下三种写法宿主都接受):
|
|
18
|
+
|
|
19
|
+
- `export function mount(...) { ... }`
|
|
20
|
+
- `export default { mount(...) { ... } }`
|
|
21
|
+
- `export default function mount(...) { ... }`
|
|
22
|
+
|
|
23
|
+
`mount` 签名:
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
export function mount({ container, host, slots }) {
|
|
27
|
+
return () => {}; // or return { dispose() {} }
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- `container`:应用正文容器(body)
|
|
32
|
+
- `slots.header`:可选固定 Header 容器(不随 body 滚动)
|
|
33
|
+
- 返回值:
|
|
34
|
+
- `() => void|Promise<void>`:卸载回调;或
|
|
35
|
+
- `{ dispose(): void|Promise<void> }`
|
|
36
|
+
|
|
37
|
+
布局规范:
|
|
38
|
+
|
|
39
|
+
- 不要使用 `window/body` 作为滚动容器;把滚动放到应用内部。
|
|
40
|
+
- 将 Tabs/二级导航/操作按钮等放到 `slots.header`,可滚动内容放到 `container`。
|
|
41
|
+
|
|
42
|
+
## 2. Host Bridge 可用性
|
|
43
|
+
|
|
44
|
+
宿主仅在“file-based entry + preload bridge 可用”的情况下提供 `host` 能力;否则会抛错:
|
|
45
|
+
|
|
46
|
+
- 若你在非 ChatOS 宿主环境下单独打开页面(或 preload 未加载),`host.bridge.enabled` 可能为 `false`。
|
|
47
|
+
- 生产场景(桌面端应用内)一般为 `true`。
|
|
48
|
+
|
|
49
|
+
## 3. `host` API 约定
|
|
50
|
+
|
|
51
|
+
### 3.1 上下文与主题
|
|
52
|
+
|
|
53
|
+
- `host.bridge.enabled: boolean`
|
|
54
|
+
- `host.context.get(): { pluginId, appId, theme, bridge: { enabled } }`
|
|
55
|
+
- `host.theme.get(): string`(当前实现读取 `document.documentElement.dataset.theme`)
|
|
56
|
+
- `host.theme.onChange(listener): () => void`
|
|
57
|
+
|
|
58
|
+
### 3.2 Admin(全局配置读取)
|
|
59
|
+
|
|
60
|
+
- `host.admin.state(): Promise<any>`:读取全局 Admin 状态快照
|
|
61
|
+
- `host.admin.onUpdate(listener): () => void`:订阅 Admin 更新事件
|
|
62
|
+
- `host.admin.models.list(): Promise<any>`:列出 Models
|
|
63
|
+
- `host.admin.secrets.list(): Promise<any>`:列出 Secrets(脱敏;不会返回明文 key)
|
|
64
|
+
|
|
65
|
+
说明:
|
|
66
|
+
|
|
67
|
+
- UI Apps **不得**在自己的配置里保存/管理 API Keys;应完全复用宿主全局配置。
|
|
68
|
+
|
|
69
|
+
### 3.3 Registry(插件注册表)
|
|
70
|
+
|
|
71
|
+
- `host.registry.list(): Promise<any>`:等价于 `uiApps:list`(包含 apps 列表、插件目录、加载错误等)
|
|
72
|
+
|
|
73
|
+
### 3.4 Backend(插件后端调用)
|
|
74
|
+
|
|
75
|
+
- `host.backend.invoke(method: string, params?: any): Promise<any>`
|
|
76
|
+
- `method`:后端 `methods` 的 key
|
|
77
|
+
- 返回值:后端方法返回的 `result`(宿主会把 `{ ok, result }` 解包)
|
|
78
|
+
|
|
79
|
+
后端协议详见:[CHATOS_UI_APPS_BACKEND_PROTOCOL.md](./CHATOS_UI_APPS_BACKEND_PROTOCOL.md)。
|
|
80
|
+
|
|
81
|
+
### 3.5 UI Prompts(右下角笑脸:交互待办)
|
|
82
|
+
|
|
83
|
+
用于把“需要用户确认/输入”的交互投递到全局队列,用户无需进入对应应用即可处理。
|
|
84
|
+
|
|
85
|
+
- `host.uiPrompts.read(): Promise<any>`:读取当前队列/最新状态
|
|
86
|
+
- `host.uiPrompts.onUpdate(listener): () => void`:订阅队列变化
|
|
87
|
+
- `host.uiPrompts.request({ prompt, runId?, requestId?, ... }): Promise<any>`:投递待办(`prompt.source` 为空时宿主写入 `${pluginId}:${appId}`)
|
|
88
|
+
- `host.uiPrompts.respond({ requestId, runId?, response }): Promise<any>`:写入用户响应
|
|
89
|
+
- `host.uiPrompts.open(): { ok: true }`:打开笑脸面板
|
|
90
|
+
- `host.uiPrompts.close(): { ok: true }`
|
|
91
|
+
- `host.uiPrompts.toggle(): { ok: true }`
|
|
92
|
+
|
|
93
|
+
`prompt` / `response` 的字段协议见:[CHATOS_UI_PROMPTS_PROTOCOL.md](./CHATOS_UI_PROMPTS_PROTOCOL.md)。
|
|
94
|
+
|
|
95
|
+
### 3.6 导航
|
|
96
|
+
|
|
97
|
+
- `host.ui.navigate(menu: string): { ok: true }`
|
|
98
|
+
- 用于跨应用/跨页面跳转(由宿主统一处理路由)
|
|
99
|
+
|
|
100
|
+
### 3.7 Chat(Agents / Sessions / Messages / Send / Events)
|
|
101
|
+
|
|
102
|
+
`host.chat` 提供对聊天域的直接操作能力(节选):
|
|
103
|
+
|
|
104
|
+
- `host.chat.agents.list()`
|
|
105
|
+
- `host.chat.agents.ensureDefault()`
|
|
106
|
+
- `host.chat.agents.create(payload)`
|
|
107
|
+
- `host.chat.agents.update(id, patch)`
|
|
108
|
+
- `host.chat.agents.delete(id)`
|
|
109
|
+
- `host.chat.agents.createForApp({ name?, description?, modelId?, promptIds?, subagentIds?, skills?, mcpServerIds? })`
|
|
110
|
+
- 创建一个“绑定当前应用”的 Agent,并自动写入 `uiApps: [{ pluginId, appId, mcp:true, prompt:true }]`
|
|
111
|
+
- `host.chat.sessions.list()`
|
|
112
|
+
- `host.chat.sessions.ensureDefault(payload?)`
|
|
113
|
+
- `host.chat.sessions.create(payload)`
|
|
114
|
+
- `host.chat.messages.list(payload)`
|
|
115
|
+
- `host.chat.send(payload)`
|
|
116
|
+
- `host.chat.abort(payload)`
|
|
117
|
+
- `host.chat.events.subscribe({ sessionId?, types? }, listener): () => void`
|
|
118
|
+
- `host.chat.events.unsubscribe(): { ok: true }`
|
|
119
|
+
|
|
120
|
+
事件订阅说明:
|
|
121
|
+
|
|
122
|
+
- `subscribe` 支持用 `sessionId` 与 `types` 做过滤;
|
|
123
|
+
- 当最后一个 listener 解绑后,宿主会自动取消底层订阅。
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# ChatOS UI Apps(嵌入应用)概览
|
|
2
|
+
|
|
3
|
+
本文把散落在仓库各处的“ChatOS 嵌入应用(UI Apps 插件)框架/协议”说明提取到仓库根目录,并补齐了若干关键约束(路径边界、打包导入、schema 字段等)。
|
|
4
|
+
|
|
5
|
+
配套文档(按从概览 → 细节的阅读顺序):
|
|
6
|
+
|
|
7
|
+
- [`CHATOS_UI_APPS_PLUGIN_MANIFEST.md`](./CHATOS_UI_APPS_PLUGIN_MANIFEST.md):`plugin.json` / `apps[i].ai` 的清单与字段规范(含完整字段、示例与约束)。
|
|
8
|
+
- [`CHATOS_UI_APPS_HOST_API.md`](./CHATOS_UI_APPS_HOST_API.md):`module` 应用入口与 `host.*` 交互协议(前端侧)。
|
|
9
|
+
- [`CHATOS_UI_PROMPTS_PROTOCOL.md`](./CHATOS_UI_PROMPTS_PROTOCOL.md):右下角笑脸「交互待办(UI Prompts)」协议(表单/单选/多选/复杂确认)。
|
|
10
|
+
- [`CHATOS_UI_APPS_BACKEND_PROTOCOL.md`](./CHATOS_UI_APPS_BACKEND_PROTOCOL.md):插件后端(Electron main 进程)协议与 `ctx` 运行时上下文。
|
|
11
|
+
- [`CHATOS_UI_APPS_AI_CONTRIBUTIONS.md`](./CHATOS_UI_APPS_AI_CONTRIBUTIONS.md):应用如何对 Chat Agent 暴露 MCP/Prompt(含命名规则、合并规则、内置清单机制)。
|
|
12
|
+
|
|
13
|
+
## 术语与角色
|
|
14
|
+
|
|
15
|
+
- **ChatOS(宿主)**:桌面端 Electron 应用(本仓库的 `deepseek_cli`)。
|
|
16
|
+
- **AIDE(引擎)**:模型调用/工具/MCP/子代理等核心能力(本仓库的 `aide`)。
|
|
17
|
+
- **UI Apps(嵌入应用/小应用)**:以插件形式注入到桌面端「应用」中心的应用。
|
|
18
|
+
- **MCP**:Model Context Protocol;通过 MCP Server 暴露 tools,最终工具名通常为 `mcp_<serverName>_<toolName>`。
|
|
19
|
+
- **Prompt**:system prompt 模板;可被 Agent 勾选并注入运行时 system prompt。
|
|
20
|
+
|
|
21
|
+
## 目录与状态(sessionRoot / stateDir)
|
|
22
|
+
|
|
23
|
+
- `sessionRoot`:会话根目录
|
|
24
|
+
- 默认:用户主目录(Home)
|
|
25
|
+
- 可通过环境变量覆盖:`MODEL_CLI_SESSION_ROOT=/path/to/root`
|
|
26
|
+
- 桌面端/CLI 会把“上次使用的 sessionRoot”记录到 `<home>/.deepseek_cli/last-session-root.txt`(未设置 env 时会优先读取)
|
|
27
|
+
- `stateDir`:`<home>/.deepseek_cli/<hostApp>`(ChatOS 的 `hostApp=chatos`)
|
|
28
|
+
|
|
29
|
+
全局配置(由宿主维护;应用侧只读/复用):
|
|
30
|
+
|
|
31
|
+
- `stateDir/chatos.db.sqlite`:Admin DB(Models / Secrets / MCP Servers / Prompts / Settings…)
|
|
32
|
+
- 文件镜像(从 Admin DB 同步出来,便于运行时读取):
|
|
33
|
+
- `stateDir/auth/models.yaml`
|
|
34
|
+
- `stateDir/auth/mcp.config.json`
|
|
35
|
+
- `stateDir/auth/system-prompt.yaml`
|
|
36
|
+
- `stateDir/auth/*prompt*.yaml`
|
|
37
|
+
|
|
38
|
+
原则:
|
|
39
|
+
|
|
40
|
+
- **Secrets(API Keys)只存 DB**,不会明文同步到 yaml。
|
|
41
|
+
- 运行前宿主会把 Secrets 写入 `process.env`(受 `override` 控制),Provider 通过 `apiKeyEnv` 取值。
|
|
42
|
+
|
|
43
|
+
## UI Apps 插件目录(宿主扫描)
|
|
44
|
+
|
|
45
|
+
宿主会扫描两个目录(并在 UI「应用」页展示实际路径):
|
|
46
|
+
|
|
47
|
+
- **内置/开发目录**:`deepseek_cli/ui_apps/plugins`
|
|
48
|
+
- **用户插件目录**:`<stateDir>/ui_apps/plugins`(即 `~/.deepseek_cli/chatos/ui_apps/plugins`)
|
|
49
|
+
|
|
50
|
+
同名 `plugin.id` 的覆盖规则:
|
|
51
|
+
|
|
52
|
+
- 用户目录的插件会覆盖内置目录的同 `plugin.id` 插件(便于开发调试/热替换)。
|
|
53
|
+
|
|
54
|
+
插件数据目录(供插件后端存放自己的持久化数据):
|
|
55
|
+
|
|
56
|
+
- `dataDir`:`<stateDir>/ui_apps/data/<pluginId>`
|
|
57
|
+
- 插件后端的持久化数据写入目录为 `dataDir`;插件安装目录用于读取插件资源。
|
|
58
|
+
|
|
59
|
+
## 插件包导入(目录或 .zip)
|
|
60
|
+
|
|
61
|
+
桌面端 UI 支持:`应用` → `导入应用包` → 选择插件目录或 `.zip`。
|
|
62
|
+
|
|
63
|
+
包结构要求:
|
|
64
|
+
|
|
65
|
+
- `plugin.json` 在包根目录;或
|
|
66
|
+
- 包根目录下一层目录中包含一个或多个插件目录(每个目录内有 `plugin.json`)。
|
|
67
|
+
|
|
68
|
+
导入时的复制规则(重要):
|
|
69
|
+
|
|
70
|
+
- 会拷贝到用户插件目录 `~/.deepseek_cli/chatos/ui_apps/plugins/<sanitized(plugin.id)>/`;
|
|
71
|
+
- 默认会排除:`node_modules/`、`.git/`、`.DS_Store`、`*.map`;
|
|
72
|
+
- 因此若插件需要依赖,请在构建时做 bundle(不要指望随包携带 `node_modules` 生效)。
|
|
73
|
+
|
|
74
|
+
## 安全边界与硬约束(协议的一部分)
|
|
75
|
+
|
|
76
|
+
为避免插件越权读取宿主文件系统,宿主对“路径型字段”做了强约束:
|
|
77
|
+
|
|
78
|
+
- `apps[i].entry.path` 必须位于插件目录内,且必须是文件(`module` 入口)。
|
|
79
|
+
- `backend.entry` 必须位于插件目录内,且必须是文件。
|
|
80
|
+
- `apps[i].ai.config`、`ai.mcp.entry`、`ai.mcpPrompt.*.path` 等所有 path 都必须位于插件目录内。
|
|
81
|
+
|
|
82
|
+
尺寸限制(默认值,防止误导入超大文件):
|
|
83
|
+
|
|
84
|
+
- `plugin.json` 最大 `256 KiB`
|
|
85
|
+
- `mcpPrompt` 内容最大 `128 KiB`(读取 path 或 inline content 都受限)
|
|
86
|
+
|
|
87
|
+
## 最短接入路径(TL;DR)
|
|
88
|
+
|
|
89
|
+
1) 从模板复制:`deepseek_cli/ui_apps/template/basic-plugin` → 放进任一插件目录
|
|
90
|
+
2) 修改 `plugin.json`:只支持 `apps[i].entry.type="module"`
|
|
91
|
+
3) 桌面端打开「应用」页 → 点“刷新” → 进入你的应用
|
|
92
|
+
4) (可选)需要 Node 能力:加 `backend.entry`,前端用 `host.backend.invoke()`
|
|
93
|
+
5) (可选)需要给 Agent 暴露工具/说明:配置 `apps[i].ai`(见 [`CHATOS_UI_APPS_AI_CONTRIBUTIONS.md`](./CHATOS_UI_APPS_AI_CONTRIBUTIONS.md))
|
|
94
|
+
|
|
95
|
+
提示:`ai.mcp` / `ai.mcpPrompt` 是否持久化写入 Admin DB,取决于宿主是否启用 `syncAiContributes`;即使不持久化,Agent 运行时也可以按 `apps[i].ai` 声明进行“临时注入”。
|
|
96
|
+
|
|
97
|
+
## 实现位置(便于对照代码)
|
|
98
|
+
|
|
99
|
+
- schema(`plugin.json` / `apps[i].ai`):`deepseek_cli/electron/ui-apps/schemas.js`
|
|
100
|
+
- 插件扫描/入口校验/AI 同步:`deepseek_cli/electron/ui-apps/index.js`
|
|
101
|
+
- 应用包导入(目录/zip):`deepseek_cli/electron/ui-apps/plugin-installer.js`
|
|
102
|
+
- module 运行时 Host API 注入:`deepseek_cli/apps/ui/src/features/apps/AppsPluginView.jsx`
|
|
103
|
+
|
|
104
|
+
## 原始文档来源(本次提取/整合)
|
|
105
|
+
|
|
106
|
+
- `deepseek_cli/doc/app-dev-handbook.md`
|
|
107
|
+
- `deepseek_cli/doc/app-integration.md`
|
|
108
|
+
- `deepseek_cli/doc/ui-apps-plugins.md`
|
|
109
|
+
- `deepseek_cli/doc/ui-apps-dev-guide.md`
|
|
110
|
+
- `aide/shared/defaults/ui-apps-expose/README.md`
|