Mini-app 沙箱
Mini-app(迷你应用)是 Agent Spaces 提供的一种轻量应用形态。每个 Mini-app 是一个独立、自包含的 React 或 HTML 项目,自带源码、配置、数据与(可选)SQLite 数据库,可以在平台内独立编辑、预览和运行,也可以挂载 Agent 运行时让 Agent 帮你编写和增强它。
什么是 Mini-app?
一个 Mini-app 由以下要素组成:
- 项目类型
react或html,入口文件分别是index.jsx与index.html。 - 源码目录
src/,存放所有用户编写的代码与资源。 - 配置目录
configs/,存放 JSON 形式的应用配置(由沙箱服务端读写,对项目内所有客户端实时同步)。 - 数据目录
data/,存放应用落盘的二进制或文本数据文件。 - SQLite 数据库
data/db/<dbName>.sqlite,每个项目可按需创建多个命名数据库(基于 better-sqlite3)。 - Agent 配置(可选)
agents.json,声明项目内可对话的多个 Agent 角色。 - manifest.json,记录项目元数据(名称、类型、入口文件、启用的插件、头像等)。
所有 Mini-app 项目存放在 ~/.agent-spaces-data/mini-apps/{projectId}/ 下,由 packages/server/src/storage/mini-app-store.ts 管理目录结构与越界保护。项目 id 由名称经清洗生成(替换文件系统/URL 非法字符为 _),创建后固定,改名只更新 manifest.name。
创建与编辑
1. 创建项目
通过 POST /api/mini-apps 创建项目,请求体需要 name 与 type(react 或 html,否则返回 400)。后端会按类型生成默认入口文件:
- React 类型:生成
index.jsx(用window.AgentSpacesUI解构出Button、Card等宿主组件)和一份自动生成的CLAUDE.md项目说明模板。 - HTML 类型:生成一份最小
index.html与同名CLAUDE.md。
名称必须全局唯一(大小写敏感、trim 后精确匹配),重名返回 409。
2. 编辑源码
编辑入口在前端 packages/web/src/app/mini-apps/[id]/,集成 Monaco 编辑器与 TypeScript LSP。后端提供一组文件管理 REST 路由:
GET /api/mini-apps/:id/files— 文件树GET /api/mini-apps/:id/files/manifest— 扁平文件清单 +mtimeMs,前端据此做增量刷新GET|PUT /api/mini-apps/:id/files/content?path=— 读取/写入文件内容POST /api/mini-apps/:id/files/rename— 重命名或移动POST /api/mini-apps/:id/files/folder— 创建空目录POST /api/mini-apps/:id/files/upload— multipart 批量上传(单文件上限 500MB)DELETE /api/mini-apps/:id/files— 删除文件或目录
文件路径全部经过 safeSrcPath 越界校验,禁止绝对路径与 .. 逃逸 src/ 目录。所有写入操作都会更新 manifest 的 updatedAt 时间戳。
3. 导入与导出
GET /api/mini-apps/:id/export— 用 archiver 打包为 ZIP(含 manifest.json、src/、icon 与 avatar)。POST /api/mini-apps/import— 接收 base64 编码的 ZIP,用 yauzl 解压。导入时会自动定位内容根(兼容src/子目录或扁平布局,最多下钻 4 层),并解压时校验条目路径安全(拒绝..与绝对路径)。
预览与运行
预览入口在前端 packages/web/src/app/mini-apps-preview/[id]/。预览页会一次性加载项目所有源码文件,按 manifest.mainFile 解析入口;若入口文件不在文件树中(通常是导入时多余顶层目录没剥掉),会显式报错而非静默 fallback 到 tree[0],避免渲染错误的入口导致模块解析错乱。
React 模式下,宿主通过 window.AgentSpacesUI 注入一批 React 组件与 lucide-react 图标供应用解构使用;HTML 模式下则运行普通 HTML/CSS/JS,window.AgentSpacesUI 与 window.AgentSpaces 同样可用。
沙箱服务编译机制
Mini-app 允许在项目 src/services/*.js 中定义服务端可调用的服务处理函数。服务编译由 packages/server/src/services/mini-app-services.ts 的 compileService() 实现,机制是:
- 剥离 import 行 — 用正则移除所有
import ...语句(服务不依赖外部模块)。 - ESM → CJS 转换 — 把
export default替换成module.exports =。 - 沙箱求值 — 用
new Function('module', 'exports', stripped)构造函数并在沙箱中执行,默认导出应为{ eventName: handler }形式的方法表。
服务文件按项目维度缓存到内存 registry(registries Map),首次访问或显式 reloadServices() 时重新加载。项目删除时通过 unloadServices() 清理缓存。
每个 handler 被调用时获得一个 MiniAppServiceContext,提供:
readConfig(path)/writeConfig(path, value)/updateConfig(path, updater)— 读写configs/,写入后向该 projectId 频道广播miniApp.configChanged事件。listRunningTasks()— 返回当前进程内的运行中任务(含executorId)。broadcast(event, data)— 向该 projectId 频道广播任意事件。
服务通过 POST /api/mini-apps/:id/services/invoke 调用,请求体为 { name, payload }。服务是 configs/ 的唯一写入方,保证状态变更集中、可广播。
Agent 运行时与工具注入
Mini-app 内置独立的 Agent 对话能力,运行链路封装在 packages/server/src/services/mini-app-agent.ts 的 runMiniAppAgent() 中。它自包含执行,不依赖 workspace 系统:读取 agents.json → 解析凭据 → 组装 functionTools → 注入 systemPrompt → 执行 → 落盘消息。
运行时选择
当前 Mini-app 内置 Agent 固定使用 langchain 运行时(runtimeConfig.kind = 'langchain'),并解析凭据优先级为:
agentId指向的 Agent preset 作为默认(modelProvider/modelId/apiKey/apiBase/systemPrompt/temperature/maxTokens)。- Agent 本地字段覆盖 preset 值。
systemPrompt本地优先,缺失才用 preset。- 全都没有则调用方走服务端默认模型兜底。
凭据解析后,可通过 providerId 或 apiBase+apiKey 匹配已配置的 provider。
函数工具注入
当 agent.tools 配置允许时(默认 { api: true, plugin: true }),运行时注入以下工具:
- plugin 工具(由
createMiniAppFunctionTools()提供,定义在packages/server/src/services/builtin-tools/mini-app-tools.ts):list_agent_spaces_ui_components— 按分类列出window.AgentSpacesUI暴露的宿主 React 组件(共约 9 个分类:actions / forms / layout / navigation / overlays / feedback / data-display / media / utilities)。list_plugin_tools— 列出项目已启用插件(enabledPlugins)注册的所有 tool 与内置 tool 的轻量摘要。get_plugin_tool_detail— 查看指定插件 tool 的完整input_schema。execute_plugin_tool— 执行插件 tool,内置虚拟插件@agent-spaces/builtin提供list_agent_presets与agent_run两个内置工具。
- api.js 方法工具:项目
src/api.js编译出的方法表(编译机制与 services 一致),每个方法被包装成一个 function tool;同时注入一个get_mini_app_tools元工具,让 Agent 查询src/tools.js里声明的工具元数据(描述与 inputSchema)后再调用项目自定义方法。
systemPrompt 注入
运行时会拼接出 systemPrompt,包含:Agent 自带 systemPrompt、当前路由(route)、当前 projectId、可用 api.js 方法名清单(提示先调 get_mini_app_tools 看描述),以及启用的插件清单(提示用 list_plugin_tools 等三个工具)。
SSE 流式对话
对话通过 POST /api/mini-apps/:id/agents/:agentId/chat 触发,以 Server-Sent Events 流式返回以下事件:
reasoning— 思考过程(含 status)tool_use— 工具调用(id / name / input)tool_result— 工具结果(toolUseId / result)text— 输出文本行message_saved— 对话结束,落盘的用户与 agent 消息done/error
用户消息时间戳在执行前捕获,agent 消息时间戳在执行后生成,保证严格 user 早于 agent,避免同毫秒落盘导致历史排序错乱。agent 消息会附带 toolCalls 数组(name / input / result)。
Agent 配置管理
GET /api/mini-apps/:id/agents— 脱敏返回 agents 清单与enableAgents开关(不含 apiKey)。GET /api/mini-apps/:id/agents/:agentId— 返回完整 agent config(含 apiKey,仅供编辑器加载)。PUT /api/mini-apps/:id/agents/:agentId— 整条替换 agent config;写入时会剥离apiKey/apiBase/baseURL,按 provider 解析后只存非敏感字段。GET /api/mini-apps/:id/agents/chat?sessionId=&agentId=— 拉取历史消息(按 timestamp 升序)。DELETE /api/mini-apps/:id/agents/chat?sessionId=&agentId=— 清空 session(可按 agentId 过滤)。
服务器启动时会调用 ensureAgentsConfigs():若某项目的 agents.json 尚不存在但 manifest 声明了 agents 种子数组,则把种子写入 agents.json;已存在的 agents.json 一律跳过,绝不覆盖(哪怕文件损坏)。
客户端 RPC
Mini-app 支持服务端向项目内预览客户端发起请求-响应 RPC,由 packages/server/src/services/mini-app-client-rpc.ts 实现:
- 服务端通过
requestMiniAppClient(projectId, type, payload, timeoutMs=5000)发起请求,生成随机requestId,向该 projectId 频道广播miniApp.clientRequest事件(携带 requestId / type / payload)。 - 客户端处理后将结果通过 WebSocket
miniApp.clientResponse事件回传(携带 requestId / ok / result / error),由handleMiniAppClientResponse()配对解决或拒绝 Promise。 - 超时默认 5 秒,超时则 reject。
Agent 的 api.js 方法可通过 ApiCtx.requestClient() 复用这条 RPC 通道向预览客户端要数据。
SQLite 数据持久化
每个 Mini-app 可拥有多个命名 SQLite 数据库,由 packages/server/src/storage/mini-app-db.ts 管理(better-sqlite3):
- 数据库文件位于
~/.agent-spaces-data/mini-apps/{projectId}/data/db/{dbName}.sqlite。 - 连接按
projectId/dbName复用(连接池),首次打开设置journal_mode = WAL与busy_timeout = 5000。 - 项目删除时
closeProjectDbs()关闭并清理该项目所有连接。
REST 路由:
POST /api/mini-apps/:id/db/:dbName— 单条语句执行,body 为{ sql, params?, mode },mode取值all|get|run|exec。POST /api/mini-apps/:id/db/:dbName/transaction— 批量语句原子执行(statements: [{sql, params?}]),任一抛错则整个事务回滚。
所有 SQL 经 checkSql 安全校验,参数通过 bindArgs 绑定,all 模式结果超过 MAX_ROWS 上限会抛错。writeDataFile(PUT /api/mini-apps/:id/data/content)则用于将文本或 base64 二进制写入 data/ 目录。
任务缓存
Mini-app 维护一份进程内任务状态缓存(packages/server/src/services/mini-app-tasks.ts),按 projectId 维护任务列表,供多客户端通过 WS 频道同步任务状态。startTask() 登记 running 任务(同 taskId 已为 running 时幂等保留),finishTask() / failTask() 标记终态;终态任务保留 TTL(10 分钟)后由 listTasks() 触发清理,running 任务永不清理。
WebSocket 事件
Mini-app 相关事件遵循 domain.action 命名约定:
miniApp.configChanged— 沙箱服务写入 configs 后广播(含 path / value)。miniApp.clientRequest— 服务端向客户端发起 RPC 请求(含 requestId / type / payload)。miniApp.clientResponse— 客户端回传 RPC 响应(含 requestId / ok / result / error)。miniApp.taskSnapshot— 客户端连入时推送当前任务快照(断线/刷新/新标签恢复视图用)。miniApp.configSnapshot— 客户端连入时推送配置全量快照(UI 据此建立内存缓存,不再直接 readConfig)。
模板
平台内置少量 Mini-app 模板(位于 packages/templates/mini-app/,含 minimax_tts 等),通过模板索引 index.json 注册。模板含 manifest.json、src/index.jsx 与可选 avatar.png,可用于快速创建带预设功能(如语音合成)的项目。
相关链接
- 关于 Issue 自动化与 Workflow 调度:议题管理、Workflow 编辑器
- 关于插件系统与可注入的工具:插件
- 设计与实现细节参见
docs/mini-app-agent.md、docs/mini-app-preview-agent.md、docs/mini-app-renderer.md、docs/mini-app-state-sync-ws-plan.md。