Files
deta-agent-plugins/plugins/studio/README.md
T
ringo 54924cd4fc
CI / contract (push) Has been cancelled
feat(plugin): 稳定命令行入口 ~/.deta/bin/deta(login / status / doctor)
为什么需要:登录助手原先只在插件目录里(宿主缓存路径 <缓存>/bin/deta-login.mjs),普通用户不知道插件装在哪;工具回执让用户「跑登录助手」时给不出可复制的命令。

实现:新增 bin/deta-cli.mjs(login 转交登录助手、status 看凭据来源/运行时/工作区、doctor 自检运行时+凭据+GSA health+MCP 握手,永不打印令牌);启动器每次拉起时幂等注册 ~/.deta/bin/deta(路径随插件更新自动刷新,幂等比较避免重复写);技能与三份 README 的登录口径统一为 \`~/.deta/bin/deta login\`。版本 studio 0.3.3 / writer 0.1.1。

实测:shim 生成与 help/status/doctor 均符合预期,doctor 准确报出唯一缺口(未登录)并握手成功 37 工具。
2026-09-20 12:10:49 +08:00

183 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DETA Studio 插件(Codex 插件 / dsh 组合包)
DETA 领域能力(AML 建筑模型读写、编译校验、围护结构统计、空间邻接查询)
以**本机 MCP 服务**形态挂进 Agent harness:同一个目录既是 Codex 插件根,
也是 dsh 组合包根,两端共享 `skills/` 与**同一个启动器**。
- 发行标识:**`studio@deta`**(插件 `studio` + 市场 `deta`)
- MCP 服务名:**`studio_mcp`**(工具前缀 `mcp__studio_mcp__…`)
- 产品档位:启动器 `--profile studio`(工作区缺省 `~/.deta/studio/workspace`)
## 连接形态与凭据(唯一管道:本机 stdio)
| 项 | 值 |
|----|-----|
| 形态 | **本机 stdio**:宿主拉起 `bin/deta-mcp.sh --profile studio`,由它解析领域服务运行时与工作目录 |
| MCP 服务名 | `studio_mcp`(工具前缀 `mcp__studio_mcp__…`);其他产品用各自服务名,可同时安装 |
| 宿主声明 | Codex 读 `.mcp.json`;dsh 读 `cordis.patch.yml`——两边都只声明"调用同一个脚本、带哪个档位" |
| 工作目录 | `DETA_WORKSPACE` → `ANT_HEADLESS_WORKSPACE` → 缺省 `~/.deta/studio/workspace`(启动器创建并在 stderr 回显) |
| 运行时 | `DETA_SERVER_ROOT` → `~/.deta/runtime` → 本机开发检出(回落到开发检出时启动器打印提示) |
| 云侧能力(可选) | `DETA_TOKEN`(`node bin/deta-login.mjs` 获取):项目/楼栋列表、`push_project` / `pull_project` |
| Studio 执行器中继(可选) | `ANT_WORKSPACE_BRIDGE_URL` + 项目 ID(三要素齐备才启用;供模拟/画布类工具) |
**本机形态不做 MCP 鉴权**(进程由宿主或用户在本机拉起,没有 OAuth、没有 401、没有令牌续期)。
未注入 `DETA_TOKEN` 时,云类工具返回 `auth_required`,其余工具(编辑、编译、校验、统计、
脚本、模拟)照常工作——这是与远程形态**同一份工具实现**的不同输入,不是两套代码。
## 工具面:编辑归宿主,MCP 只做引擎与同步
AML 是纯文本 DSL,读写、搜索、重构一律由**宿主原生的文件工具**完成
(Codex 的 read / patch / grep / shell;dsh 的同名能力),MCP 不是编辑入口:
| 分工 | 谁做 |
|------|------|
| 读取 / 修改 / 搜索 `aml.xml`、`bss.md`、`rules.yml`、`behaviors.yml`、`design-brief.md` | 宿主原生工具(本地项目目录里的文件) |
| 编译、规范校验、规则检查、围护结构统计、空间邻接、领域脚本 | MCP 引擎工具(编译 / 校验 / 统计 / 邻接 / 脚本等) |
| 改动送进引擎工作区、取回工作区 | MCP 同步工具 |
本机形态为**全量工具面**(含工作区文件类工具)。**当前可用的完整工具名以运行时
`list_capabilities` 为准**,本仓文档不列举工具清单。
## 本地项目目录约定
一个 DETA 项目就是一个普通目录;引擎工具读的是**引擎工作区**,因此改动要先同步:
```
my-project/
├── deta.json # 可选:项目绑定({ "projectId": "...", "name": "..." })
├── aml.xml # 必需:建筑模型唯一事实源
├── bss.md # 可选:样式/构造附属文档
├── rules.yml # 可选:校验规则
├── behaviors.yml # 可选:声明式行为规则
└── design-brief.md # 可选:设计任务书
```
- 想让引擎直接以项目目录为工作区:启动宿主前设 `DETA_WORKSPACE=<项目目录>`。
- `deta.json` 里的 `projectId` 是 deta.city 云项目 ID;带上它的同步调用会把同一个 ID
绑定到引擎工作区,之后生成/拉取会带同一个 Studio 深链 `https://deta.city/design/<projectId>`。
- 文件名沿用引擎侧约定(`aml` ↔ `aml.xml`、`brief` ↔ `design-brief.md` 等),
本地目录与引擎工作区结构一致,拉取结果可直接落盘回本地目录。
## 端到端工作流:本地编辑 → 同步 → 校验 → Studio 查看
```text
1. 本地编辑 read/patch/grep 直接改 aml.xml(MCP 不参与编辑)
2. 同步 把改动送进引擎工作区(带 projectId 时同时绑定云项目)
→ { ok, files, projectId, studioUrl: "https://deta.city/design/<ID>" }
3. 引擎校验 编译 AML 为建筑模型(工作区最新版本)→ 语法/几何/规则校验逐条修复
围护结构统计与空间邻接查询按需调用
4. Studio 查看 在宿主内置浏览器面板打开 studioUrl;已有相同 URL 的标签优先复用,
不为同一项目重复开新标签,也不要 goto 重载已有标签
5. 跑模拟 在面板里的 Studio 界面运行——模拟由 Studio 执行,本插件 MCP 不替代它
6. 反向核对 从引擎工作区拉回文件(工作区为空时返回空集)
```
路径安全:同步只接受工作区相对路径,绝对路径与 `..` 穿越会被拒绝,单次载荷上限 1 MiB。
## 安装(Codex)
```bash
# 1. 添加市场并安装插件
codex plugin marketplace add https://git.deta.city/deta/deta-agent-plugins.git
codex plugin add studio@deta
# 2. 准备领域服务运行时(二选一)
# a) 本机引擎包已安装 → 启动器自动找到(~/.deta/runtime)
# b) 从源码构建:在领域服务仓库执行 npm run build:mcp,并让 DETA_SERVER_ROOT 指向它
#
# 冒烟(应等待 stdin/stdout):
bash <插件目录>/bin/deta-mcp.sh --profile studio
```
装完即用,**没有 OAuth 登录步骤**。需要云侧能力(项目列表 / 项目同步 / 打开 Studio)时登录一次
(`~/.deta/bin/deta` 由启动器自动注册,无需手工创建):
```bash
~/.deta/bin/deta login # 输入账号 + 密码,或 --email-code <邮箱> 走验证码
~/.deta/bin/deta status # 看凭据来源 / 运行时 / 工作区(不打印令牌)
~/.deta/bin/deta doctor # 端到端自检:运行时、凭据、GSA、MCP 握手
```
登录后**开新会话**(MCP 服务重新拉起时读到凭据)。启动器自己按
`DETA_TOKEN` 环境变量 → `~/.deta/env` → `~/.deta/credentials.json` 的顺序解析令牌,
所以**不需要 source、不需要 launchctl 注入、不需要改 .zshrc、也不需要重启应用**——
Finder/Dock 启动的宿主不继承终端环境,令牌由启动器直接读取才可靠。
### 登录助手(云侧能力用)
```bash
node bin/deta-login.mjs # 交互式:读标识与密码,写 ~/.deta/*
node bin/deta-login.mjs -u <用户名|已验证邮箱|已验证手机号> # 免交互输入标识
node bin/deta-login.mjs --email-code <邮箱> # 邮箱验证码登录(未设密码的账号用)
node bin/deta-login.mjs --base-url https://<host> # 自建/预发环境
```
**标识(identifier)填门户登录页上用的那一个**:用户名、已验证邮箱、已验证手机号,服务端都会解析到同一账号。
助手先匿名调用 `/login/check` 预检标识是否已注册,再走 `/login` 做密码校验(不走门户的「登录即注册」端点,
不会误建账号)。失败时按服务端提示给出下一步:
| 服务端提示 | 含义与处理 |
|------------|------------|
| `该标识在 <host> 未注册` | 标识不是门户上用的那一个;邮箱验证码注册的账号标识填邮箱 |
| `用户不存在/密码错误` | 服务端不区分「账号不存在」与「密码不匹配」;已确认标识存在,即密码不符,或改用 `--email-code` |
| `该邮箱/手机号尚未验证…` | 先用验证码方式登录一次门户完成验证 |
| `该登录方式未开放` | 用 `GET <base>/prod-api/login/methods` 查当前启用项 |
## 安装(dsh)
```bash
export DETA_PLUGIN_ROOT=<本包所在目录> # 配置层按它定位启动器
export DETA_WORKSPACE=<你的项目目录> # 可选;缺省 ~/.deta/workspace
dsh plugin --profile <name> add <本包>
```
`cordis.patch.yml` 声明本机 stdio,经 `bash -lc` 调用同一个 `bin/deta-mcp.sh`;
未设置 `DETA_PLUGIN_ROOT` 时启动器明确报错退出,不会静默失败。
## 文件
```
.codex-plugin/plugin.json Codex 插件清单
.mcp.json Codex MCP 声明(stdio → ./bin/deta-mcp.sh)
package.json dsh 组合包声明(dsh.bundle.patch)
cordis.patch.yml dsh 配置层(同一启动器,stdio)
bin/deta-login.mjs 登录助手(写 ~/.deta/credentials.json + ~/.deta/env,供云侧能力用)
bin/deta-mcp.sh 本机 stdio 启动器(两宿主共用;解析运行时与工作目录)
README.md 本文件(安装 / 连接 / 排障)
skills/aml-design/SKILL.md 宿主侧技能(工具使用规则;不含工具清单)
```
## 已知缺陷(实测,2026-09-10)
**上游:编码代理 × 模型服务 命名空间重复**
会话累积较多本插件工具调用后,代理会在请求里重复声明 MCP 命名空间,模型服务的
Responses 兼容层拒绝请求:
```
Duplicate namespace name 'deta'; Namespace names must be unique.
```
复现记录(同一插件、不同会话长度):
| 代理版本 | 会话规模 | 触发位置 |
|---------|---------|---------|
| 0.144.4 | 一步并发 2 次调用 | input[10]、input[18] |
| 0.144.4 | 串行 7–8 次调用 | input[37]、input[47] |
| 0.154.0 | 短会话 5 工具 | 未触发 |
| 0.154.0 | 长会话(≈15+ 工具) | input[260] |
已排除/无效的手段:`wire_api = "chat"`(该版本已移除,仅支持 responses)、
`features.non_prefixed_mcp_tool_names`(仅去掉命名空间前缀,重复依旧)、
`supports_parallel_tool_calls = false`(削弱并发触发面,长会话仍复现)。
**应对**:完成一个任务后另起会话;撞到该错误不要重试,重开会话即可(工作区文件保留)。
## 已修缺陷(实测)
| 现象 | 根因 | 修复 |
|------|------|------|
| 写类工具返回 `user cancelled MCP tool call` | MCP 工具默认需审批,非交互模式下无法弹窗 | `.mcp.json` 声明 `default_tools_approval_mode: "approve"`(实测 `auto`/`writes` 都会被取消,只有 `approve` 放行) |
| 本机 stdio 形态起不来:`ERR_UNSUPPORTED_DIR_IMPORT` | 启动器指向未打包的产物,依赖包的目录 import 在 Node ESM 下非法 | 领域服务提供打包入口;启动器优先取打包产物,回落未打包产物时打印警告 |
| 一个插件两种形态并存(远程 URL + 本机 stdio) | 历史遗留:远程端点声明与本地启动器同时存在,语义冲突 | 统一为**本机 stdio 单一管道**,远程端点声明从插件移除 |