# 发布 SDK

新应用默认使用标准 `plugin.json`；旧 `aidc.app.json` 继续支持。Apps 与 OpenAI Plugin 的包、UI、默认 AIDC 登录和可选 OAuth 规范见 [Plugin 兼容](plugins.md)。

把一个应用从本地开发、到 Developer 测试、再到 Nexus 上线。发布 SDK = `aidc app` 命令 + Developer API；UI（Console）以后也只调同一套 API。

## 应用清单 `aidc.app.json`

```json
{
  "$schema": "https://www.ai-dc.ai/developer/schemas/aidc.app.schema.json",
  "manifestVersion": 1,
  "slug": "defect-inspection",
  "version": "1.0.0",
  "title": "瑕疵检测",
  "summary": "拍一张照片，判断制作工艺有没有污渍、破损",
  "category": "vision",
  "entry": "index.html",
  "sdk": ["vision", "model", "log", "ui"],
  "permissions": { "camera": true, "microphone": false },
  "models": ["gpt-6-luna"],
  "datasets": [],
  "streams": [],
  "cameras": [],
  "semantic": { "types": [], "actions": [], "write": [] },
  "auth": { "access": "company", "publicLink": "signin" },
  "agents": false,
  "storage": { "type": "none" },
  "limits": { "dailyTokens": 1000000, "requestsPerMinute": 30, "realtimeSessionsPerDay": 100, "computeMinutesPerMonth": 2000, "notificationsPerDay": 20 },
  "cdn": []
}
```

| 字段 | 说明 |
| --- | --- |
| `slug` | 应用标识，出现在 URL 里（小写字母开头，字母 / 数字 / 连字符） |
| `description` | 应用页「介绍」一节：做什么、给谁用、输入输出（≤ 2000 字）。见 [应用（Apps）](apps.md) |
| `examples` | **示例说法**：人对智能体说的一句话（≤ 6 条），应用页「调用方法」里点一下就复制 |
| `version` | **版本号**（语义化：`1.2.0`，可带 `-rc.1`）。测试与发布都用它称呼这个版本；**改了任何东西就要升**——同一应用里一个版本号只对应一份内容 |
| `namespace` | 缺省 = 开发者 Key 所属公司（`cell-…`）；`public` 是 AIDC 官方公开应用，只有 AIDC 平台账号能发 |
| `owner` | **负责方**（[应用卡片](app-card.md) 第一栏）：`{ department, team?, contact? }`——哪个部门的应用、哪个岗位 / 智能体在维护、找谁。工作流画布按部门分泳道 |
| `sdk` | **SDK 登记**：用到了哪些 SDK（`ui` `vision` `voice` `video` `model` `modeling` `data` `connect` `semantic` `auth` `log` `billing` `evolve` `workflow`；旧名 `improve` 照收，归成 `evolve`）。部署时平台静态分析包里对 `aidc.js` 的 import 与对 `ui.css` 的引用，**用了没登记的直接拒收**——应用登记表永远是真的。登记了 `modeling` 的应用，平台在 CSP 里另放行 World Labs 资产 CDN（`cdn.marble.worldlabs.ai`）与 WebAssembly 编译（高斯溅射渲染器） |
| `permissions` | 要不要摄像头 / 麦克风——平台据此下发 `Permissions-Policy` |
| `models` / `datasets` / `streams` | 应用会调的模型、数据集与会订阅的实时数据流（同一命名空间）；应用票据只放行这里列出的 |
| `cameras` | 应用会看的网络摄像机 / NVR（同一命名空间下 `aidc vision camera add` 登记的名字）；票据只放行这里列出的，且只给本公司 developer / member 看。见 [视觉 SDK](vision.md) |
| `semantic` | 语义层：读哪些对象类型（`types`）、能执行哪些 Action（`actions`）、developer 访客能直接写哪些类型（`write`）；`"*"` = 看得见的全部。见 [语义 SDK](semantic.md) |
| `auth` | 谁能打开：`access` = `company`（本公司成员，缺省）/ `restricted`（只有开发者和被分享的人）；`publicLink` = 公开链接的访客（一律登录，没有匿名）`signin`（缺省，先登录，外人登录后只读）/ `members`（登录后只有本公司的人）；旧值 `open` 按 `signin` 执行。见[访问与账号](auth.md) |
| `exports` | **对外能力**：这个应用愿意给别的应用（工作流）用的查询 / 读取 / 聚合 / Action，带名字、参数、说明；只能读写本应用登记过的类型 / Action。见[自动化](workflow.md) |
| `workflow` | **工作流**：把各应用的能力、计算、AI 分析连成流程，手动 / 预演 / 数据变化时自动运行。部署时对照公司的能力目录校验 |
| `agents` | 是否会调智能体（访客自带 Agent Key，见[模型 SDK · 智能体](model.md)） |
| `storage` | 数据存储。Nexus 应用是**无状态**的，只有 `none`：业务数据放公司的语义层（数据 / 语义 SDK），改数据 = 执行 Action，应用自己不存东西 |
| `limits` | 资源上限：每日 token、每 IP 每分钟请求、每日实时转写场次、**每月计算分钟（缺省 2000）**、每日通知封数（不写就是缺省值，见下文[资源上限](#资源上限与成本告警)）；可选 `monthlyBudgetUsd`（每月模型预算，美元，到了就拒绝新的模型调用，见[用量与账单](billing.md)）；`worldCreditsPerDay`：每天最多花多少世界模型 credits（[建模 SDK](modeling.md)，不写 = 0 = 只看；官方公开应用写了也不能生成） |
| `cdn` | 允许加载第三方脚本的 CDN（`cdn.jsdelivr.net`、`cdnjs.cloudflare.com`），写进 CSP |
| `evolve` | **改进面**（[自进化 SDK](evolve.md)）：`slots`（页面上 `data-evolve` 区域的名字与别名）、`tokens`（带 `min` / `max` 的可调参数，CSS 变量）、`baked`（已固化进这个版本的改进，`aidc evolve bake` 写入）、`model`（编译用的模型）。部署时平台扫出页面上所有 `data-evolve` 槽位，改进只能落在它们上面；可调参数挂在不存在的槽位上直接拒收 |

完整 JSON Schema：[`/developer/schemas/aidc.app.schema.json`](https://www.ai-dc.ai/developer/schemas/aidc.app.schema.json)。

## 资源上限与成本告警

每个应用都有资源上限，清单 `limits` 不写就取缺省值——**不存在「不设上限」的应用**。调高上限 = 允许这个应用花更多钱，部署时会告警，确认是有意为之。

| 上限 | 缺省 | 计量 | 用完之后 |
| --- | --- | --- | --- |
| `computeMinutesPerMonth` | **2000** | 应用在服务端占用的时长（UTC 自然月，全体访客合计）：实时连接（`data.live` / `semantic.watch` / `connect.stream` / 运行进度）挂着的时间、工作流运行的时间、模型 / 转写调用的时间。普通读写请求不计分钟（受每分钟请求数限制） | 这个应用的票据请求一律 `429 quota_exhausted`，下月 1 日（UTC）恢复；数据变化 / 定时触发的工作流不再开跑 |
| `dailyTokens` | 1,000,000 | 模型 token（UTC 自然日） | 模型调用 429，明天恢复 |
| `requestsPerMinute` | 30 | 每个来源 IP 每分钟的模型 / 数据请求 | 429 `rate_limited`，稍后重试 |
| `realtimeSessionsPerDay` | 100 | 实时转写场次（每场 ≤ 5 分钟） | 开不了新场次，明天恢复 |
| `notificationsPerDay` | 20 | 工作流 `notify` 步骤每天发出的邮件 | 超出的记为「已拦下」不发 |
| `monthlyBudgetUsd` | 不设 | 本月已计价的模型成本（[用量与账单](billing.md)） | 模型调用 429，下月恢复 |
| `worldCreditsPerDay` | 0（只能看） | 每天生成世界花的 World Labs credits（[建模 SDK](modeling.md)） | 不能再生成，明天恢复 |

**怎么省计算分钟**：数据一变才推送（数据流 / 语义层 watch），不要 `setInterval` 轮询；SDK 的实时连接在页面隐藏超过 1 分钟时自动断开、切回来续上（状态 `paused`），不用自己处理；定时触发放宽到每天或几小时一次。

**部署时的成本告警**（不拦部署，`aidc app check` / `deploy` 打在 stderr，API 返回 `warnings`）：
- 工作流定时 `trigger.schedule` ≤ 1 小时一次（高频）——带上每月次数与预计占用的计算分钟；
- 任何一项 `limits` 高于缺省值；
- 代码里 `setInterval` 与拉数据写在同一个文件（疑似轮询）。

用量：`aidc app usage <slug>` / `GET /api/v1/developer/apps/{namespace}/{slug}/usage`——本月计算分钟（按实时连接 / 工作流 / 模型分列）、快用完（≥ 80%）与已用完、全部上限、定时的下一次、今天的通知与邮件通道是否配置。

## 版本包

应用目录里除清单、隐藏文件与 `node_modules` 之外的所有文件。限制：≤ 200 个文件、解码后 ≤ 3 MB、只接受网页常用类型（html / css / js / json / svg / png / jpg / webp / gif / woff2 / md / txt …）。包里一律写**相对路径**——平台在入口页注入 `<base>`，同一个包挂在 Developer 和 Nexus 两个地址下都能用。

**Skills** 放在包根目录的 `skills/<名字>/SKILL.md`（一条一个目录，可带 `references/`），清单里不用登记；部署时平台逐条检查 frontmatter（`name` 与目录一致、`description` ≤ 1024 字符）、正文与大小，不合规整个版本拒收。见 [应用（Apps）](apps.md#skills)。

**界面可选**：清单 `"entry": null` 的应用没有界面，只有 Skills 与 APIs——打开应用地址直接转到应用页。

应用是单页应用：入口 HTML + 资源。多页请用 hash 路由。SDK 从 `/developer/sdk/v1/aidc.js` 引入、界面样式表是 `/developer/sdk/v1/ui.css`（都是绝对路径）。包里不能带 SDK 的副本（路径含 `developer/sdk/` 的文件直接拒收）：本地预览时伪造的 `aidc.js` 一旦跟着上线，应用跑的就是替身。

## 通道

```
aidc app deploy   →  上传版本 v1.2.0（内容寻址）→ test 通道   /developer/…/apps/<slug>
aidc app publish  →  test 上的 v1.2.0 → production 通道        /nexus/…/apps/<slug>
aidc app rollback →  production 切回更早的已测版本（--version 1.1.0）
```

- **内容寻址**：版本的 digest = 包内「路径 + 内容哈希」再加上清单（去掉 `namespace` / `$schema`）的哈希。同样的内容重复部署，返回已有版本、不产生新版本——重试永远安全。
- **版本号一号一内容**：内容（文件或清单）变了但 `version` 没升，部署被拒（409 `version_conflict`）。
- **测过什么发什么**：production 只接受进过 test 的版本；发布不重新打包。
- **不可变资源**：版本资源挂在 `…/apps/<slug>/_v/<digest 前 16 位>/`，长缓存；入口页不缓存、始终指向通道当前版本。
- 所有写操作支持 `--dry-run`（API：`x-aidc-dry-run: true`，返回 202 + 计划）。

## 版本号与更新状态

每个版本有两个编号：**版本号**（清单 `version`，如 `1.2.0`）和**构建序号**（`#3`，第几次上传）。Developer 预览页与 Nexus 正式页都显示版本号（`app().version`；预览页显示「Developer 预览 · v1.2.0」）。

每个版本都有一个**更新状态**，由通道指针和发布记录推出来：

| 状态 | 含义 |
| --- | --- |
| 已上线 `live` | production 当前版本 |
| 测试中 `testing` | test 当前版本（还没上线） |
| 已测未发 `tested` | 进过 test、比线上新，但被更新的测试版替换、没发布过 |
| 已被替换 `superseded` | 比线上版本旧（被新版本替换下线） |
| 已回滚 `rolled_back` | 上过线、后来被回滚到更早的版本 |
| 未测试 `untested` | 上传了还没进过 test（不对外暴露任何文件） |

通道每变一次记一条**发布记录**：谁、何时、哪个通道、动作（进测试 / 发布 / 回滚）、从哪个版本到哪个版本、说明（`--notes`）。`aidc app status` 看版本与状态，`aidc app history` 看发布记录。

## 应用登记表

```bash
aidc app registry
```

本公司每个应用一行：登记的 SDK 与实际用到的 SDK（部署时静态分析）、读写哪些语义类型 / Action / 数据流 / 模型、谁能打开、测试与正式版本号、最近 7 天打开 / 操作 / 反馈 / 错误；外加「每个 SDK 被哪些应用用」。API：`GET /api/v1/developer/registry`。

## 应用卡片

每个应用都有平台自动生成的[应用卡片](app-card.md)：负责部门、版本、SDK（登记 / 实际）、是否连数据库与读写哪些数据、对外能力与依赖、访问范围、近 7 天使用。`aidc app card <slug>`、SDK `appCard()`、`GET /api/v1/developer/apps/{namespace}/{slug}/card`；应用里的「卡片」按钮。开发规范（写 `owner` 与 `summary`、如实登记 SDK 与数据、把可以对外的能力写进 `exports`）见应用卡片一页。

## 地址

| | test（Developer） | production（Nexus） |
| --- | --- | --- |
| AIDC 官方公开应用 | `/developer/apps/<slug>` | `/nexus/apps/<slug>` |
| 客户应用 | `/developer/<cellId>/apps/<slug>`（本公司 developer） | `/nexus/<cellId>/apps/<slug>`（本公司成员） |

每个应用地址下平台另外生成三样东西（包里不用写，也不会和包里的文件撞名）：`<地址>/about` 应用页（调用方法 · 介绍 · Skills & APIs · Information）、`<地址>/about.md` 给智能体读的说明（应用页「复制给智能体」复制的就是它）、`<地址>/skills/<名字>/SKILL.md` 线上版本的 Skill 原文。应用目录：

| 目录 | 谁看 | 列什么 |
| --- | --- | --- |
| `/nexus` | 登录后 | 转到自己公司的 `/nexus/<cellId>/apps`；不属于任何公司的账号转到 `/nexus/apps` |
| `/nexus/apps` | 任何人 | AIDC 官方应用 |
| `/nexus/<cellId>/apps` | 本公司成员 | 本公司发布到 Nexus 的应用 + AIDC 官方；developer 顶栏多一个「Developer 版本」 |
| `/developer/apps` | 任何人 | Developer 样板（官方应用的 test 版）；登录的 developer 直接转到自己公司的 Developer 版本 |
| `/developer/<cellId>/apps` | 本公司 developer | 本公司应用的 Developer 版本（test 通道）+ 样板；每行写测到哪个版本、Nexus 上是哪个版本。普通成员转到 Nexus 正式版 |

**正式发布到 Nexus 需要 Developer 账号**：`aidc app publish`（`POST …/channels/production`）只认开发者 Key，每次都复核 Key 的主人现在还是不是这家公司的 developer（持 developer License，或公司 admin / operator）；member 账号看得到、用得了 Nexus 上的应用，发不了。

官方公开应用任何人可以打开。客户应用要求登录且属于该公司；它们跑在 CSP 沙箱里（不透明源），凭平台注入的应用票据调 API。客户应用使用摄像头 / 麦克风需要独立的用户内容域名，正在规划中。

## API

| 方法 | 路径 | 说明 |
| --- | --- | --- |
| `GET` | `/api/v1/developer/me` | 当前开发者 Key 的身份与可写命名空间 |
| `GET` | `/api/v1/developer/apps` | 应用列表 |
| `GET` | `/api/v1/developer/apps/{namespace}/{slug}` | 通道、版本（版本号、构建序号、更新状态）、发布记录、地址 |
| `POST` | `/api/v1/developer/apps/{namespace}/{slug}/versions` | 上传版本包 `{manifest, files:[{path, encoding, content}], notes?}` |
| `POST` | `/api/v1/developer/apps/{namespace}/{slug}/channels/{test\|production}` | 晋升 `{version?, notes?}`（version = 版本号 `1.2.0` 或构建序号 `3`），记一条发布记录 |
| `POST` | `/api/v1/developer/apps/{namespace}/{slug}/tickets?channel=` | 应用票据续期（SDK 自动调用） |
| `GET` | `/api/v1/developer/apps/{namespace}/{slug}/usage` | 资源用量：本月计算分钟与上限、全部 limits、定时、今天的通知（开发者 Key 或应用自己的票据） |
| `GET` | `/api/v1/developer/apps/{namespace}/{slug}/apis` | 应用的 APIs（名字、说明、输入 JSON Schema、返回、调用口、CLI）、Skills、示例说法 |
| `POST` | `/api/v1/developer/apps/{namespace}/{slug}/apis/{name}/execute` | 调应用的一个 API `{input, preview?, channel?}`：以「应用 × 调用人角色」执行；`preview` 预演写入 |
| `GET` | `/api/v1/developer/apps/{namespace}/{slug}/about` | 应用说明（给智能体的 Markdown，同 `<地址>/about.md`）与「复制给智能体」的那一句 |
| `POST` | `/api/v1/developer/apps/{namespace}/{slug}/mcp` | 应用的 MCP 服务器（Streamable HTTP）：工具 = APIs，prompts = Skills |

鉴权：`Authorization: Bearer aidc-dk-…`（开发者 Key）。完整契约见 [OpenAPI](https://www.ai-dc.ai/developer/openapi.json)。

## 命令

```bash
aidc app init <slug> --template skills|camera|voice|data|blank   # 每个模板都带一条 Skill；skills 模板没有界面
aidc app check [目录]
aidc app dev [目录] --port 5173
aidc app deploy [目录] --notes "修复框选偏移" [--dry-run]     # 版本号取清单 version
aidc app publish <slug|目录> [--version 1.2.0] [--notes "…"] [--dry-run]
aidc app rollback <slug> --version 1.1.0 --notes "回退：…"
aidc app status <slug>                                           # 版本号 / 构建序号 / 更新状态
aidc app history <slug>                                          # 发布记录
aidc app list
aidc app usage <slug>                                            # 本月计算分钟 / 上限 / 定时 / 通知
```
