Files
deta-agent-plugins/plugins/studio
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
..

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 查看

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)

# 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 由启动器自动注册,无需手工创建):

~/.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 启动的宿主不继承终端环境,令牌由启动器直接读取才可靠。

登录助手(云侧能力用)

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)

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 单一管道,远程端点声明从插件移除