anna-app-publish
把纯前端静态 SPA 打包并上架到 Anna AI OS 应用平台(anna.partners)。覆盖 manifest schema 选择、三条会被 Pydantic extra="forbid" 直接拒的硬规则、CSP 内联事件处理器改造、bundle 上传与"发布≠上架"的状态机。适用于用户要求"上架 Anna""把我的网页应用做成 Anna App"等场景。
SKILL.mdanna-app-publish/SKILL.md
GitHubAnna AI OS 应用上架
何时用
- 用户要把一个纯前端(HTML/CSS/JS,无后端)应用上架到 Anna 应用市场
- 用户已经在 Anna Developer Console 建好了应用,卡在 manifest 校验 / bundle 上传 / Publish 被拦
先分清三件事(搞混必卡)
| 概念 | 位置 | 说明 |
|---|---|---|
| Listing 元数据 | Console 的「基本信息 / Listing」标签 | 名称、slug、分类、简介、logo、截图。不在 manifest 里 |
| Manifest | 版本编辑器里的 JSON | 声明 Executas、prompt、ui 段 |
| Bundle | CLI 上传的静态文件 | 真正被 iframe 加载的 HTML/CSS/JS |
三条硬规则(踩一次就退回重来)
规则 1:manifest JSON 里绝对不能写 name 和 version
AppManifest 是 Pydantic extra="forbid" 模型,多一个字段整体拒绝:
✗ schema (root): must NOT have additional properties {"additionalProperty":"name"}
✗ schema (root): must NOT have additional properties {"additionalProperty":"version"}
这两个值由 Console 上面的表单字段(应用名称 / 版本号)自动合并进去。 ⚠️ Console 在编辑框下面写着「必填:schema、name、version、required_executas[]」——这句是误导,别照抄。
规则 2:required_executas 在 schema ≥ 2 时可以留空
官方校验清单原文:非空检查只对 schema 1 生效;schema ≥ 2 的纯 Bundle UI 应用(靠 ui.host_api 的 llm.* / storage.* / image.*)两个数组都可以留空。
Console 的 Executas 标签写着「发布前至少需要一个必需 Executa」——那是 schema 1 时代的过期提示。 ⚠️ 但 Console 的「验证」按钮打的是服务端 Python 校验器,比本地 JSON Schema 更严,最终以它为准。
规则 3:apps publish ≠ 上架
DRAFT ──submit-review──▶ PENDING_REVIEW ──管理员通过──▶ APPROVED ──release 版本──▶ PUBLISHED
在 APPROVED 之前点 Publish 会得到 App must be APPROVED before publishing versions。
没有邮件通知,也没有承诺的审核 SLA——只看 Console「My Apps」的状态,别干等。
schema 怎么选
| schema | 用途 |
|---|---|
1 |
纯聊天增强,必须有 ≥1 个 Executa |
2 |
有 UI(ui 段),Executas 可留空。大多数网页应用用这个 |
3 |
额外支持顶层 storage 声明(真持久化 APS),但会拒绝 permissions 字段 |
⚠️ schema 2 下 ui.host_api.storage 是 per-window runtime_state(≤256KB),不一定跨设备持久。
要跨设备持久得升 schema 3 并声明 storage: {kv: true, ...}。
标准流程
1. 改代码:把所有内联事件处理器改成事件委托
bundle 的基线 CSP 是 script-src 'self' <sdk-origin>,不含 'unsafe-inline'。
而 csp_overrides.script-src 只接受 'self' | 'wasm-unsafe-eval' | 'sha256-…' | 'nonce-…'——加不了 unsafe-inline,无解,只能改代码。
<!-- Before -->
<button onclick="save()">保存</button>
<!-- After -->
<button data-act="save">保存</button>
配套在 document.body 上挂一个委托监听器统一分派。
2. 接宿主 runtime
import { AnnaAppRuntime } from "/static/anna-apps/_sdk/latest/index.js";
const anna = await AnnaAppRuntime.connect(); // 本地/开源版要能优雅返回 null
LLM 走 anna.llm.complete,存储走 anna.storage.{get,set,delete,list}——不需要任何 Executa 插件,也不需要自带 API Key。
3. 写 manifest(schema 2 模板)
{
"schema": 2,
"required_executas": [],
"optional_executas": [],
"ui": {
"bundle": { "format": "static-spa", "entry": "index.html", "external_origins": [] },
"form_factors": ["desktop"],
"views": [{ "name": "main", "title": "应用名", "default": true,
"min_size": {"w":360,"h":480}, "default_size": {"w":420,"h":760},
"max_size": {"w":1200,"h":1400} }],
"host_api": { "llm": ["complete"], "storage": ["get","set","delete"], "window": ["set_title"] }
}
}
约束:views 1–16 项且最多一个 default: true;尺寸 120 ≤ w,h ≤ 4096 且 default_size 落在 [min,max] 内。
4. 本地先过校验(零成本,别跳过)
anna-app validate --strict
用的是和服务端同一份 @anna-ai/app-schema。
⚠️ 本地是 JSON Schema,比服务端宽松(schema 1 + 空 executas 本地都放行,服务端才拦)。本地过了不代表一定过。
5. 上传 bundle
Console 只能声明 bundle(入口 / 格式 / Origin),传不了文件。
上传只有两条路:官方 CLI(anna-app apps push)或 REST(bundle/init → 逐个 PUT → bundle/finalize)。
配额:总量 50MB / 文件数 2000 / 单文件 10MB。bundle_ready 之后不可改。
6. 提交审核前的四项预检
- Listing 字段填完(logo 只收 PNG/JPG/WebP/GIF,SVG 不行)
- Console 里 Validate 返回
valid: true - bundle 状态
bundle_ready - ⭐ 自己完整装一遍、用一遍(官方明确列在预检里)
Example
用户:「把我这个日计划网页应用上架到 Anna。」
- 查代码有没有
onclick=——有就先改成data-act委托,这一步不做后面必被 CSP 拦。 - 按上面模板写 manifest,不写 name/version,executas 留空。
anna-app validate --strict通过。- Console 建应用 → 粘贴 manifest → 先点「验证」 → 再创建版本
0.1.0。 - CLI 上传 bundle,确认
bundle_ready。 submit-review→ 看 My Apps 状态(别等邮件)。- 被 REJECTED 无惩罚,改完重提即可。
坑位清单
- logo 只收 PNG/JPG/WebP/GIF(≤2MB,自动裁 256×256),SVG 不在允许格式里
- 开发者 handle 规则 2–39 字符、小写/数字/单个连字符,应用发布为
@handle/slug;改 handle 后旧名冻结 90 天 - 官方限额:App 创建 10/天、提交审核 5/天、活跃非归档 App 20
ui.host_api那列表里灰色的* (allow all)是占位符不是默认值——留空 = 拒绝该命名空间- 平台仍是 beta,约每周一版,接口可能变
隐私与边界
只处理用户自己拥有的仓库代码与 manifest。不代填任何凭据;PAT 走本机凭据管理器,用完不落盘。 简历口径注意:审核通过前不能写「已上架」,只能写「已提交审核 + 开源同步 GitHub」——审核状态外人看不到,GitHub 谁都能点开验证。