create-zleap-app 0.10.0 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/index.js +24 -2
  2. package/lib/scaffold.js +10 -0
  3. package/package.json +4 -4
  4. package/skills/zleap-cloud-app/SKILL.md +6 -6
  5. package/skills/zleap-cloud-app/references/app-runtime.md +82 -75
  6. package/skills/zleap-cloud-app/references/cloud-functions.md +230 -43
  7. package/skills/zleap-cloud-app/references/console-database.md +8 -0
  8. package/skills/zleap-cloud-app/references/console-members.md +44 -30
  9. package/skills/zleap-cloud-app/references/limits.md +57 -56
  10. package/skills/zleap-cloud-app/references/manifest.md +36 -35
  11. package/skills/zleap-cloud-app/references/permissions.md +40 -47
  12. package/skills/zleap-cloud-app/references/sdk-database-files.md +116 -49
  13. package/skills/zleap-cloud-app/references/status-codes.md +126 -94
  14. package/skills/zleap-cloud-app/references/triggers.md +24 -24
  15. package/skills/zleap-cloud-app-release/SKILL.md +5 -2
  16. package/skills/zleap-cloud-app-release/references/app-center-install.md +64 -52
  17. package/skills/zleap-cloud-app-release/references/app-center-publish.md +118 -49
  18. package/skills/zleap-cloud-app-release/references/console-functions.md +12 -4
  19. package/skills/zleap-cloud-app-release/references/console-legacy-release.md +5 -2
  20. package/skills/zleap-cloud-app-release/references/console-members.md +44 -30
  21. package/skills/zleap-cloud-app-release/references/console-release.md +39 -28
  22. package/skills/zleap-cloud-app-release/references/console-workspace.md +32 -16
  23. package/skills/zleap-cloud-app-release/references/dev-to-production.md +330 -113
  24. package/skills/zleap-cloud-app-release/references/permissions.md +40 -47
  25. package/skills/zleap-cloud-app-release/references/status-codes.md +126 -94
  26. package/templates/react-ts/AGENTS.md +2 -2
  27. package/templates/react-ts/README.md +1 -1
  28. package/templates/react-ts/package.json +3 -0
  29. package/templates/react-ts/scripts/zleap.mjs +25 -3
  30. package/templates/react-ts/src/cloud/client.ts +2 -2
@@ -65,11 +65,11 @@
65
65
 
66
66
  除 `requires` 外,以下声明也属于安装时要授予的权限:
67
67
 
68
- | 声明位置 | 可用值 | 在哪里调整 |
69
- | ------------------------------------------------------------ | ------------------------------------------------------------- | -------------------- |
70
- | `host_api.requires` | `clipboard.write`、`workspace.files`、`workspace.images.read` | 不可在窗口里单独撤销 |
71
- | `host_api.requires`(个人信息) | `user.profile`、`user.phone`、`user.email` | 安装时**不授予**,使用时由用户在授权框里逐项同意;应用窗口「已授权信息」撤销 |
72
- | 顶层 `capabilities` / `storage.capabilities`(宿主托管数据) | `storage.kv`、`space.read`、`space.write`、`notify.send` | 应用窗口「权限」 |
68
+ | 声明位置 | 可用值 | 在哪里调整 |
69
+ | ------------------------------------------------------------ | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
70
+ | `host_api.requires` | `clipboard.write`、`workspace.files`、`workspace.images.read`、`credentials.use` | 不可在窗口里单独撤销 |
71
+ | `host_api.requires`(个人信息) | `user.profile`、`user.phone`、`user.email` | 安装时**不授予**,使用时由用户在授权框里逐项同意;应用窗口「已授权信息」撤销 |
72
+ | 顶层 `capabilities` / `storage.capabilities`(宿主托管数据) | `storage.kv`、`space.read`、`space.write`、`notify.send` | 应用窗口「权限」 |
73
73
 
74
74
  ### 也要声明集合
75
75
 
@@ -109,10 +109,10 @@
109
109
 
110
110
  打开应用后,窗口内容区顶部右侧只有**一个**「**权限**」按钮,但背后是两类**独立**的授权,对应服务端两套闸门,不要混为一谈:
111
111
 
112
- | 授权 | 管什么 | 怎么打开 |
113
- | ------------------------------------------ | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
114
- | **Cloud 数据授权**(「应用权限」对话框) | 云数据库 / 存储读写、后台能力 | 应用声明了 `runtime.backend`,或 `requires` 里有 `cloud.*` / `os.agent.complete` 时,点「权限」直接打开 |
115
- | **安装级权限**(宿主托管数据) | `storage.kv` / `space.read` / `space.write` / `notify.send` | 在「应用权限」对话框底部点「**管理其他权限**」;只声明了这些权限、没用 Cloud 的应用,点「权限」直接打开 |
112
+ | 授权 | 管什么 | 怎么打开 |
113
+ | ---------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
114
+ | **Cloud 数据授权**(「应用权限」对话框) | 云数据库 / 存储读写、后台能力 | 应用声明了 `runtime.backend`,或 `requires` 里有 `cloud.*` / `os.agent.complete` 时,点「权限」直接打开 |
115
+ | **安装级权限**(宿主托管数据) | `storage.kv` / `space.read` / `space.write` / `notify.send` | 在「应用权限」对话框底部点「**管理其他权限**」;只声明了这些权限、没用 Cloud 的应用,点「权限」直接打开 |
116
116
 
117
117
  两类都没声明的应用不显示「权限」按钮。两个对话框的完整文案见 [安装与卸载 · 应用窗口顶部的按钮](app-center-install.md)。这里只讲授权语义。
118
118
 
@@ -138,29 +138,29 @@
138
138
 
139
139
  对话框里**没有环境选择**。数据域(dev「调试」/ prod「生产」)由宿主根据部署决定,对话框只显示当前是哪个:
140
140
 
141
- | 部署状态 | 数据域 | 谁能用 |
142
- | ---------- | ------ | ------------------------------------------------------------------------------------------------------------------------------- |
143
- | 未开放生产 | dev | 默认**只有该版本的发布者本人**(非发布者报 `PUBLISHER_REQUIRED`,对话框显示「当前安装未申请此能力,或你无权访问应用云空间。」) |
144
- | 已开放生产 | prod | 其他安装者一律走生产(应用没有生产环境时报 `ENV_FORBIDDEN`);发布者本人在应用有生产环境后走生产,否则仍走 dev |
141
+ | 部署状态 | 数据域 | 谁能用 |
142
+ | ---------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
143
+ | 未开放生产 | dev | 默认只有开发团队:`appid` 应用的全部成员;旧 `app_key` 应用只有该版本的上传者本人(其他人报 `PUBLISHER_REQUIRED`,对话框显示「当前安装未申请此能力,或你无权访问应用云空间。」) |
144
+ | 已开放生产 | prod | 其他安装者一律走生产(应用没有生产环境时报 `ENV_FORBIDDEN`);发布者本人在应用有生产环境后走生产,否则仍走 dev |
145
145
 
146
146
  生产是否开放取决于部署:发布信任配置就绪即开放(由运维自动配置),不由应用决定。
147
147
  应用代码里只写 `zl.cloud.init({ env: zl.cloud.DYNAMIC_CURRENT_ENV })`,**不要写死 `'dev'` / `'prod'`**。
148
148
 
149
149
  > **生产未开放时,同事用不了 Cloud 数据**(注意)
150
150
  >
151
- > 此时所有人都走 dev,而 dev 默认只对发布者开放(部署可另行放开)。你把应用发布到企业目录后,
151
+ > 此时所有人都走 dev,而 dev 默认只对开发团队(`appid` 应用成员 / 旧应用上传者)开放(部署可另行放开)。你把应用发布到企业目录后,
152
152
  > 同事能安装、能打开,但云数据读写会被拒绝。这属于「平台未开放」,不是代码写错了。
153
153
 
154
154
  ### 授权与会话的关系
155
155
 
156
156
  应用打开时,宿主为当前数据域建立 Cloud 会话,并按此刻的授权**一次性算出**可用的能力集合(profiles):
157
157
 
158
- | profile | 需要 | 缺了的现象 |
159
- | ------------------ | ---------------------------------------------------- | ----------------------------------------------------------------- |
160
- | `document-core/1` | 数据库已开通 + 声明了集合 + 至少授权了数据库读或写 | 数据库调用抛 `UNSUPPORTED_OPERATION` |
161
- | `watch-snapshot/1` | 上面 + 授权了数据库读 | `watch` 不可用 |
162
- | `storage-object/1` | 存储已启用 + 授权了文件读/写(生产还需绑定就绪发布) | 文件接口抛 `UNSUPPORTED_OPERATION` |
163
- | `app-execution/1` | 至少一项后台能力已启用 | `callFunction` / `capabilities.invoke` 抛 `UNSUPPORTED_OPERATION` |
158
+ | profile | 需要 | 缺了的现象 |
159
+ | ------------------ | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
160
+ | `document-core/1` | 数据库已开通 + 声明了集合 + 至少授权了数据库读或写 | 数据库调用抛 `UNSUPPORTED_OPERATION` |
161
+ | `watch-snapshot/1` | 上面 + 授权了数据库读 | `watch` 不可用 |
162
+ | `storage-object/1` | 存储已启用 + 授权了文件读/写(生产还需绑定就绪发布) | 文件接口抛 `UNSUPPORTED_OPERATION` |
163
+ | `app-execution/1` | 至少一项后台能力已启用 | `callFunction` / `capabilities.invoke` 抛 `BACKGROUND_CAPABILITY_UNAVAILABLE`(旧 SDK 抛 `UNSUPPORTED_OPERATION`) |
164
164
 
165
165
  数据库和存储一项都没授权、也没有已启用的后台能力时,会话直接被拒:`CONSENT_REQUIRED`。
166
166
 
@@ -196,7 +196,7 @@ Cloud 授权的键形如 `cloud.database.dev.read`,记录在**这个安装的
196
196
  > 授权后,本应用可在关窗后按你勾选的能力运行(如定时巡检),只作用于你本人的数据,其他使用者看不到。
197
197
 
198
198
  - 授权限定在**这个安装 + 这个版本 + 这个环境**;每一步执行前都会重新检查;
199
- - `managed_function`(云函数)还要求绑定**就绪并已激活**的发布,否则提示「该版本尚未激活发布版本,暂时无法执行。请到「发布版本」页完成「激活开发」。」,调用返回 `FUNCTION_RELEASE_REQUIRED`;
199
+ - `managed_function`(云函数)还要求绑定**就绪并已激活**的发布,否则提示「该版本尚未激活发布版本,暂时无法执行。请到「发布版本」页完成「激活开发」。」,调用返回 `FUNCTION_RELEASE_REQUIRED`。appid 应用到控制台「版本管理」页下方的「开发环境:云函数激活」完成这一步;
200
200
  - `logic_unit` / `builtin` provider 不需要发布;
201
201
  - `os.agent.complete` 等内置动作同样是一项后台能力,要在这里启用后才能调用。
202
202
 
@@ -245,7 +245,7 @@ Cloud 授权的键形如 `cloud.database.dev.read`,记录在**这个安装的
245
245
 
246
246
  > **云函数的 `fields` 不能省略**(注意)
247
247
  >
248
- > Manifest 校验允许省略 `fields`,但云函数的数据网关把空 `fields` 当成「没有任何字段授权」,读写都会被拒(「云函数数据字段未授权」)。
248
+ > Manifest 校验技术上允许省略 `fields`(默认空数组),但云函数的数据网关把空 `fields` 当成「没有任何字段授权」,读写都会被拒(「云函数数据字段未授权」)。
249
249
  > 网关逐请求核对的完整规则见 云函数开发(文档站 ./cloud-function)。
250
250
 
251
251
  `actions` 合法值:`get`、`query`、`count`、`add`、`set`、`update`、`remove`。注意 `get` 是按文档键读**单条**,`query` 是按条件**查多条**——
@@ -315,19 +315,12 @@ Cloud 授权的键形如 `cloud.database.dev.read`,记录在**这个安装的
315
315
 
316
316
  | 能力 | 说明 | 用法 |
317
317
  | ----------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
318
- | `clipboard.write` | 写入剪贴板 | `sdk/zleap-sdk.js` 的 `connectHostedApp()` |
318
+ | `clipboard.write` | 写入剪贴板 | 宿主桥支持这个能力,但当前 SDK 的 `connectHostedApp()` 只有 `kv`、`files`、`close`,**没有剪贴板方法**;本文档站暂不提供调用示例 |
319
319
  | `workspace.files` | 读取及整理选定空间文件夹内的文件 | 模板里的 `sdk/workspace.js`;运行时由用户选择工作文件夹 |
320
320
  | `workspace.images.read` | 读取选定文件夹里的图片 | 需同时声明 `workspace.files`;`workspace.readImage(handle, fileId, expectedVersion)` 返回校验过的图片 Blob(JPEG/PNG/GIF/WebP,≤ 20 MiB) |
321
+ | `credentials.use` | 使用钥匙串里的登录凭据 | 宿主桥能力;当前 SDK 未提供封装,本文档站暂不展开 |
321
322
 
322
- ```js
323
- import { connectHostedApp } from './sdk/zleap-sdk.js';
324
-
325
- const host = await connectHostedApp();
326
- // 需在用户点击等手势里调用
327
- await host.clipboard.writeText({ text: '工作总结' });
328
- ```
329
-
330
- - `host_api.profile` 必须是 `app-runtime/2`;`requires` 写这两项之外的值,应用变成不可运行;
323
+ - `host_api.profile` 必须是 `app-runtime/2`;`requires` 写了白名单之外的值,应用变成不可运行;
331
324
  - 宿主桥会拒绝**未声明**的方法调用,返回错误而不是静默忽略;
332
325
  - `workspace.files` 弹出的「选择工作文件夹」只是选范围,权限已在安装时授予,实际操作仍受你的空间权限限制;
333
326
  - `workspace.files` 与 `cloud.storage.*` 是两回事:后者是本应用自己的云存储,不能互相代替;
@@ -335,21 +328,21 @@ await host.clipboard.writeText({ text: '工作总结' });
335
328
 
336
329
  ## 排查「没权限」
337
330
 
338
- | 症状 | 检查项 |
339
- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
340
- | 应用显示「暂不可用」,装不上 | `requires` 里有白名单外的非可选能力?声明了 `agent.interaction`、前台 provider / 前台 provides?`host_api` 写错? |
341
- | 上传就失败 | `appid` 是否已在控制台创建、你是否是应用成员、provider `permissions` 是否 ⊆ `requires`、集合是否已声明 |
342
- | 打开应用报 `CONSENT_REQUIRED` | 「权限」里的四项全被取消且没有已启用的后台能力——勾选后保存 |
343
- | `PUBLISHER_REQUIRED` | 当前数据域是 dev,而你不是该版本的发布者。等部署开放生产,或用发布者账号验证 |
344
- | `ENV_FORBIDDEN` | 当前数据域的环境未创建 / 不可用,或部署未开放生产 |
345
- | 数据库调用 `UNSUPPORTED_OPERATION` | 环境、数据库是否就绪?是否授权了数据库读写?改完后重新加载应用了吗? |
346
- | `SCOPE_FORBIDDEN` / `RULE_DENIED` | 授权是否被取消?集合是否在 `data.collections` 里?记录规则是否允许?详见 [状态与错误码](status-codes.md) |
347
- | 云函数访问不到字段 | provider 的 `permissions.data[].fields` 写了且包含该字段吗?`actions` 允许这个操作吗? |
348
- | 用户之间看不到彼此数据 | 集合是私有的(默认)。需要共享就在**创建集合之前**声明 `shared_collections` / `partition: "shared"` |
349
- | 前端调用后台能力 `UNSUPPORTED_OPERATION` | 没有任何已启用的后台能力——在应用窗口「权限」里勾选并重新加载 |
350
- | 云函数 `FUNCTION_RELEASE_REQUIRED` | 到「发布版本」完成签发与「激活开发」 |
351
- | 更新版本后后台任务不跑了 | 应用中心更新时权限变宽的能力需重新确认;CLI `update` 会停用全部后台能力 |
352
- | 跨应用调用失败 | 普通 SDK 不支持跨应用调用 |
331
+ | 症状 | 检查项 |
332
+ | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
333
+ | 应用显示「暂不可用」,装不上 | `requires` 里有白名单外的非可选能力?声明了 `agent.interaction`、前台 provider / 前台 provides?`host_api` 写错? |
334
+ | 上传就失败 | `appid` 是否已在控制台创建、你是否是应用成员、provider `permissions` 是否 ⊆ `requires`、集合是否已声明 |
335
+ | 打开应用报 `CONSENT_REQUIRED` | 「权限」里的四项全被取消且没有已启用的后台能力——勾选后保存 |
336
+ | `PUBLISHER_REQUIRED` | 当前数据域是 dev,而你不是该版本的发布者。等部署开放生产,或用发布者账号验证 |
337
+ | `ENV_FORBIDDEN` | 当前数据域的环境未创建 / 不可用,或部署未开放生产 |
338
+ | 数据库调用 `UNSUPPORTED_OPERATION` | 环境、数据库是否就绪?是否授权了数据库读写?改完后重新加载应用了吗? |
339
+ | `SCOPE_FORBIDDEN` / `RULE_DENIED` | 授权是否被取消?集合是否在 `data.collections` 里?记录规则是否允许?详见 [状态与错误码](status-codes.md) |
340
+ | 云函数访问不到字段 | provider 的 `permissions.data[].fields` 写了且包含该字段吗?`actions` 允许这个操作吗? |
341
+ | 用户之间看不到彼此数据 | 集合是私有的(默认)。需要共享就在**创建集合之前**声明 `shared_collections` / `partition: "shared"` |
342
+ | 前端调用后台能力 `BACKGROUND_CAPABILITY_UNAVAILABLE` | 没有任何已启用的后台能力——在应用窗口「权限」里勾选并重新加载(旧 SDK 报 `UNSUPPORTED_OPERATION`) |
343
+ | 云函数 `FUNCTION_RELEASE_REQUIRED` | 到「版本管理」页下方「开发环境:云函数激活」(或概览的「去签名」)完成签发与「激活开发」 |
344
+ | 更新版本后后台任务不跑了 | 应用中心更新时权限变宽的能力需重新确认;CLI `update` 会停用全部后台能力 |
345
+ | 跨应用调用失败 | 普通 SDK 不支持跨应用调用 |
353
346
 
354
347
  ## 相关
355
348
 
@@ -58,14 +58,14 @@
58
58
 
59
59
  应用里的 Cloud SDK 抛出 `CloudError`:
60
60
 
61
- | 字段 | 含义 |
62
- | ------------------------ | ---------------------------------------------------------------------- |
63
- | `errCode` | 错误码 |
64
- | `errMsg` | 固定为 `App Cloud: <errCode>`,**不含服务端的中文说明** |
65
- | `retryable` | 只有 `CALL_TIMEOUT`、`RUNTIME_UNAVAILABLE`、`COMMIT_UNKNOWN` 为 `true` |
66
- | `requestId` | 本次请求编号 |
67
- | `operationId` | 写操作的操作编号(`op_<秒级时间戳>_<32 位十六进制>`) |
68
- | `jobId` / `invocationId` | 后台调用的作业编号,可用 `jobs.get` / `jobs.wait` 继续查 |
61
+ | 字段 | 含义 |
62
+ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
63
+ | `errCode` | 错误码 |
64
+ | `errMsg` | 一般是 `App Cloud: <errCode>`,**不含服务端的中文说明**;少数由 SDK 本地判定的错误(如 `BACKGROUND_CAPABILITY_UNAVAILABLE`)带一段中文说明 |
65
+ | `retryable` | 只有 `CALL_TIMEOUT`、`RUNTIME_UNAVAILABLE`、`COMMIT_UNKNOWN` 为 `true` |
66
+ | `requestId` | 本次请求编号 |
67
+ | `operationId` | 写操作的操作编号(`op_<秒级时间戳>_<32 位十六进制>`) |
68
+ | `jobId` / `invocationId` | 后台调用的作业编号,可用 `jobs.get` / `jobs.wait` 继续查 |
69
69
 
70
70
  宿主只把服务端的 `reason` 交给应用,**不传 `message` 和 `next_action`**。要给用户看的提示文案,
71
71
  按 `errCode` 在应用里自己写(脚手架模板 `src/cloud/client.ts` 的 `describeError` 就是这样做的)。
@@ -112,35 +112,36 @@
112
112
 
113
113
  ### 通道与调用
114
114
 
115
- | 错误码 | 含义 | 可重试 | 处理 |
116
- | ----------------------- | ---------------------------------------------------- | ------ | ---------------------------------------------------------- |
117
- | `COMMIT_UNKNOWN` | **写操作结果未知**(写请求 30 秒未返回等) | ✅ | **用原 `operationId` 查回执**,确认前不要换编号重新写 |
118
- | `CALL_TIMEOUT` | 读请求 30 秒未返回 / 握手 15 秒未完成 / 作业等待超时 | ✅ | 可重试;带 `jobId` 时用 `jobs.wait` 继续等 |
119
- | `RUNTIME_UNAVAILABLE` | 通道已关闭或服务暂不可用 | ✅ | 稍后重试;页面卸载后通道不可再用 |
120
- | `APP_RATE_LIMITED` | 本应用此刻并发请求过多(单应用并发槽被占满) | ❌\* | 稍后重试;不要立刻循环重试 |
121
- | `RESOURCE_EXHAUSTED` | 超出限制(并发 >8、分页/排序/大小上限、配额) | — | 对照 限制(文档站 ./limits) 调整;配额类需清理数据 |
122
- | `REQUEST_TOO_LARGE` | 请求体超过协商上限(默认 256 KiB) | ❌ | 拆分请求 |
123
- | `IDEMPOTENCY_CONFLICT` | 同一请求编号被重复使用,或同一操作编号用于不同内容 | ❌ | 新请求用新编号;重试同一写入时内容必须完全相同 |
124
- | `OPERATION_EXPIRED` | 操作编号超出首次使用窗口(300 秒)或回执已过期 | ❌ | 重新读取最新状态后,用新编号发起 |
125
- | `PROTOCOL_MISMATCH` | SDK 与宿主协议不一致,或响应结构异常 | ❌ | 使用与当前 OS 匹配的 SDK 版本 |
126
- | `UNSUPPORTED_OPERATION` | 平台未实现或未协商的操作 | ❌ | 改实现,见 未实现的 SDK 功能(文档站 ./limits#未实现的-sdk-功能) |
115
+ | 错误码 | 含义 | 可重试 | 处理 |
116
+ | ----------------------------------- | --------------------------------------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
117
+ | `COMMIT_UNKNOWN` | **写操作结果未知**(写请求 30 秒未返回等) | ✅ | **用原 `operationId` 查回执**,确认前不要换编号重新写 |
118
+ | `CALL_TIMEOUT` | 读请求 30 秒未返回 / 握手 15 秒未完成 / 作业等待超时 | ✅ | 可重试;带 `jobId` 时用 `jobs.wait` 继续等 |
119
+ | `RUNTIME_UNAVAILABLE` | 通道已关闭或服务暂不可用 | ✅ | 稍后重试;页面卸载后通道不可再用 |
120
+ | `APP_RATE_LIMITED` | 本应用此刻并发请求过多(单应用并发槽被占满) | ❌\* | 稍后重试;不要立刻循环重试 |
121
+ | `RESOURCE_EXHAUSTED` | 超出限制(并发 >8、分页/排序/大小上限、配额) | — | 对照 限制(文档站 ./limits) 调整;配额类需清理数据 |
122
+ | `REQUEST_TOO_LARGE` | 请求体超过协商上限(默认 256 KiB) | ❌ | 拆分请求 |
123
+ | `IDEMPOTENCY_CONFLICT` | 同一请求编号被重复使用,或同一操作编号用于不同内容 | ❌ | 新请求用新编号;重试同一写入时内容必须完全相同 |
124
+ | `OPERATION_EXPIRED` | 操作编号超出首次使用窗口(300 秒)或回执已过期 | ❌ | 重新读取最新状态后,用新编号发起 |
125
+ | `PROTOCOL_MISMATCH` | SDK 与宿主协议不一致,或响应结构异常 | ❌ | 使用与当前 OS 匹配的 SDK 版本 |
126
+ | `BACKGROUND_CAPABILITY_UNAVAILABLE` | 宿主没有协商到 `app-execution/1`,`callFunction` / `capabilities.invoke` 无法执行 | ❌ | 先让使用者在应用窗口「权限」里勾选后台能力并保存;确认版本已签发并激活、部署开放后台执行。旧 SDK 报的是 `UNSUPPORTED_OPERATION`,升级 SDK |
127
+ | `UNSUPPORTED_OPERATION` | 平台未实现或未协商的操作 | ❌ | 改实现,见 未实现的 SDK 功能(文档站 ./limits#未实现的-sdk-功能) |
127
128
 
128
129
  \* `retryable` 为 `false`,但稍等后重试通常能成功;持续出现说明有长任务占满了并发。
129
130
 
130
131
  ### 初始化与授权
131
132
 
132
- | 错误码 | 含义 | 处理 |
133
- | ---------------------- | ------------------------------------------------------------ | ------------------------------------------------------- |
134
- | `INIT_REQUIRED` | 调用 `ready()` 前没有 `zl.cloud.init()` | 先初始化 |
135
- | `INIT_CONFLICT` | 同一页面用不同环境再次初始化 | 一个页面只绑定一个环境 |
136
- | `ENV_MISMATCH` | `env` 取值非法 | 只能是 `dev` / `prod`(或 `DYNAMIC_CURRENT_ENV`) |
137
- | `AUTH_REQUIRED` | 登录态或运行会话失效 | 重新打开应用 |
138
- | `SCOPE_FORBIDDEN` | 权限范围不允许(含未授权数据访问、生产文件存储未开放) | 见本页末尾「排查:`SCOPE_FORBIDDEN`」 |
139
- | `ENV_FORBIDDEN` | 应用要用的环境不存在或已停用 | 见下方「打开应用时的错误」 |
140
- | `PUBLISHER_REQUIRED` | 开发数据域只对发布者本人开放 | 用发布者账号;给其他人用需要上线到生产 |
141
- | `STALE_GENERATION` | 授权或数据绑定已换代(如重新授权、恢复备份) | 重新打开应用,建立新会话 |
142
- | `RULE_DENIED` | 集合记录规则拒绝了本次读/写 | 检查 `data.policies` 的 `read`/`write` 表达式与文档内容 |
143
- | `QUERY_NOT_AUTHORIZED` | 查询不在授权范围内(宿主白名单中的码,服务端来源**未验证**) | 检查查询条件与授权 |
133
+ | 错误码 | 含义 | 处理 |
134
+ | ---------------------- | ------------------------------------------------------------------- | ---------------------------------------------------------------------- |
135
+ | `INIT_REQUIRED` | 调用 `ready()` 前没有 `zl.cloud.init()` | 先初始化 |
136
+ | `INIT_CONFLICT` | 同一页面用不同环境再次初始化 | 一个页面只绑定一个环境 |
137
+ | `ENV_MISMATCH` | `env` 取值非法 | 只能是 `dev` / `prod`(或 `DYNAMIC_CURRENT_ENV`) |
138
+ | `AUTH_REQUIRED` | 登录态或运行会话失效 | 重新打开应用 |
139
+ | `SCOPE_FORBIDDEN` | 权限范围不允许(含未授权数据访问、生产文件存储未开放) | 见本页末尾「排查:`SCOPE_FORBIDDEN`」 |
140
+ | `ENV_FORBIDDEN` | 应用要用的环境不存在或已停用 | 见下方「打开应用时的错误」 |
141
+ | `PUBLISHER_REQUIRED` | 开发数据域只对开发团队开放(`appid` 应用成员 / 旧应用该版本上传者) | 请管理员在控制台「成员」里把你加为开发者;给其他人用需要发布为线上版本 |
142
+ | `STALE_GENERATION` | 授权或数据绑定已换代(如重新授权、恢复备份) | 重新打开应用,建立新会话 |
143
+ | `RULE_DENIED` | 集合记录规则拒绝了本次读/写 | 检查 `data.policies` 的 `read`/`write` 表达式与文档内容 |
144
+ | `QUERY_NOT_AUTHORIZED` | 查询不在授权范围内(宿主白名单中的码,服务端来源**未验证**) | 检查查询条件与授权 |
144
145
 
145
146
  ### 数据
146
147
 
@@ -163,17 +164,17 @@
163
164
 
164
165
  ### 发布与后台调用
165
166
 
166
- | 错误码 | 含义 | 处理 |
167
- | ----------------------------------- | -------------------------------------------------- | ------------------------------------------------------ |
168
- | `FUNCTION_RELEASE_REQUIRED` | 当前安装还没有绑定已签发并激活的发布 | 在控制台「发布版本」签发并激活 |
169
- | `RELEASE_UNAVAILABLE` | 发布代数已变化(激活了新版本),旧会话作废 | 宿主会自动重开会话并重试一次;仍失败时重新打开应用 |
170
- | `RELEASE_NOT_READY` | 发布候选尚未就绪 | 先完成签发 |
171
- | `RELEASE_DISABLED` | 发布候选已撤回 | 使用其他就绪候选 |
172
- | `RELEASE_ACTIVATION_UNAVAILABLE` | 目标环境不存在且无法自动创建 | 见 [发布与激活](#发布与激活) |
173
- | `RELEASE_ACTIVATION_TARGET_MISSING` | 激活目标与校验后的 Manifest 不一致(少见) | 见 [发布与激活](#发布与激活) |
174
- | `NODE_LOST` | 执行节点失联,**结果未确认**(作业状态 `unknown`) | 不要重复提交;用 `jobId` / `invocationId` 核对 |
175
- | `NODE_LOST_EXPIRED` | 节点失联且超过对账期限 | 到控制台运行记录核对是否已产生写入,再决定是否重新提交 |
176
- | `EXECUTION_FAILED` | `callFunction` 作业失败且没有更具体的错误码 | 到控制台运行记录或日志查看失败原因 |
167
+ | 错误码 | 含义 | 处理 |
168
+ | ----------------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------- |
169
+ | `FUNCTION_RELEASE_REQUIRED` | 当前安装还没有绑定已签发并激活的发布 | 在控制台版本页签发并激活(`appid` 应用:版本管理 → 开发环境:云函数激活) |
170
+ | `RELEASE_UNAVAILABLE` | 发布代数已变化(激活了新版本),旧会话作废 | 宿主会自动重开会话并重试一次;仍失败时重新打开应用 |
171
+ | `RELEASE_NOT_READY` | 发布候选尚未就绪 | 先完成签发 |
172
+ | `RELEASE_DISABLED` | 发布候选已撤回 | 使用其他就绪候选 |
173
+ | `RELEASE_ACTIVATION_UNAVAILABLE` | 目标环境不存在且无法自动创建 | 见 [发布与激活](#发布与激活) |
174
+ | `RELEASE_ACTIVATION_TARGET_MISSING` | 激活目标与校验后的 Manifest 不一致(少见) | 见 [发布与激活](#发布与激活) |
175
+ | `NODE_LOST` | 执行节点失联,**结果未确认**(作业状态 `unknown`) | 不要重复提交;用 `jobId` / `invocationId` 核对 |
176
+ | `NODE_LOST_EXPIRED` | 节点失联且超过对账期限 | 到控制台运行记录核对是否已产生写入,再决定是否重新提交 |
177
+ | `EXECUTION_FAILED` | `callFunction` 作业失败且没有更具体的错误码 | 到控制台运行记录或日志查看失败原因 |
177
178
 
178
179
  `callFunction` 失败时,`errCode` 优先使用作业结果里的 `errorCode`;作业状态为 `unknown` 时抛 `COMMIT_UNKNOWN`,
179
180
  仍在排队/执行时抛 `CALL_TIMEOUT`(都带 `jobId`,可用 `jobs.wait` 继续等)。`capabilities.invoke` 不抛错,
@@ -184,19 +185,19 @@
184
185
  `zl.app`(更新管理器)和 `zl.auth`(授权登录)失败时抛出的是 `AppError`(不是 `CloudError`),只有 `errCode` 和 `message`。
185
186
  详见 版本更新与授权登录(文档站 ./app-runtime#错误码)。
186
187
 
187
- | errCode | 含义 | 处理 |
188
- | ----------------------- | ------------------------------------------------------------ | -------------------------------------- |
189
- | `AUTH_DENIED` | 用户在系统授权框里点了「拒绝」,或 24 小时内拒绝过这些项 | 按未授权处理,等用户主动操作时再申请 |
190
- | `AUTH_IN_PROGRESS` | 授权框已打开,又发起了一次 `authorize` | 等上一次结果 |
191
- | `AUTH_GESTURE_REQUIRED` | 需要弹授权框,但不是在用户点击 / 按键之后调用 | 放到按钮点击事件里调用 |
192
- | `AUTH_SCOPE_UNDECLARED` | 申请了 Manifest `host_api.requires` 没声明的 `user.*` | 补上声明,重新上传 |
193
- | `SCHEMA_INVALID` | `scopes` 为空或含其他值,或 `reason` 超过 60 字 | 改代码 |
194
- | `OPEN_IN_ZLEAP_OS` | 不在 zleapOS 窗口里打开 | 部署后从启动台打开 |
195
- | `HOST_UNAVAILABLE` | 宿主 15 秒内没有响应 | 升级 zleapOS 后重试 |
196
- | `RUNTIME_UNAVAILABLE` | 通道已关闭或平台接口暂不可用 | 稍后重试;仍失败时重新打开应用 |
197
- | `CALL_TIMEOUT` | 调用超时 | 可以重试 |
198
- | `UPDATE_FAILED` | `applyUpdate()` 切换失败 | 提示用户重新打开应用或到应用中心更新 |
199
- | `UNSUPPORTED_OPERATION` | 宿主不支持这个方法 | 升级 zleapOS |
188
+ | errCode | 含义 | 处理 |
189
+ | ----------------------- | -------------------------------------------------------- | ------------------------------------ |
190
+ | `AUTH_DENIED` | 用户在系统授权框里点了「拒绝」,或 24 小时内拒绝过这些项 | 按未授权处理,等用户主动操作时再申请 |
191
+ | `AUTH_IN_PROGRESS` | 授权框已打开,又发起了一次 `authorize` | 等上一次结果 |
192
+ | `AUTH_GESTURE_REQUIRED` | 需要弹授权框,但不是在用户点击 / 按键之后调用 | 放到按钮点击事件里调用 |
193
+ | `AUTH_SCOPE_UNDECLARED` | 申请了 Manifest `host_api.requires` 没声明的 `user.*` | 补上声明,重新上传 |
194
+ | `SCHEMA_INVALID` | `scopes` 为空或含其他值,或 `reason` 超过 60 字 | 改代码 |
195
+ | `OPEN_IN_ZLEAP_OS` | 不在 zleapOS 窗口里打开 | 部署后从启动台打开 |
196
+ | `HOST_UNAVAILABLE` | 宿主 15 秒内没有响应 | 升级 zleapOS 后重试 |
197
+ | `RUNTIME_UNAVAILABLE` | 通道已关闭或平台接口暂不可用 | 稍后重试;仍失败时重新打开应用 |
198
+ | `CALL_TIMEOUT` | 调用超时 | 可以重试 |
199
+ | `UPDATE_FAILED` | `applyUpdate()` 切换失败 | 提示用户重新打开应用或到应用中心更新 |
200
+ | `UNSUPPORTED_OPERATION` | 宿主不支持这个方法 | 升级 zleapOS |
200
201
 
201
202
  ## 打开应用时的错误
202
203
 
@@ -400,19 +401,29 @@
400
401
  ```
401
402
 
402
403
  `next_action`、`reason` 都在 `detail=` 的 JSON 里。退出码:结果未知(输出含 `COMMIT_UNKNOWN`)为 **6**,其他错误为 **2**;
403
- 通过 npm 包 `@zleap-web/app-cli` 运行但本机缺少对应平台的二进制时为 **127**。MCP 工具出错时返回 `isError: true`,
404
+ 通过 npm 包 `@zleap-ai/app-cli` 运行但本机缺少对应平台的二进制时为 **127**。MCP 工具出错时返回 `isError: true`,
404
405
  内容是 `{"status": "failed" | "unknown" | "unconfirmed", "message": "平台错误 …"}`。
405
406
 
406
407
  `zleap-app prepare` 的特殊结果:
407
408
 
408
- - 应用没有声明云资源(无 `cloud.*` 权限、无后台能力、无集合)时直接成功,`status` 为 **`not_required`**,
409
+ - **平台 `appid` 应用**只准备开发环境:只声明数据的应用在开发权限阶段后结束,`status` 为 **`ready`**,响应里 `"environment": "dev"`;
410
+ Manifest 声明了 headless 后台能力时,还会创建发布候选、签名并激活到开发。
411
+ - **旧 `app_key` 应用**没有声明云资源(无 `cloud.*` 权限、无后台能力、无集合)时直接返回 `status` 为 **`not_required`**,
409
412
  `next` 提示「应用未声明云资源或后台执行,安装后即可运行」。
410
413
  - 某个阶段失败时,`data` 里带 `stage`(失败的阶段)、`completed_stages`(已完成的阶段)、`cause.reason`(原因码)、
411
414
  `next`(「用app_diagnose核对当前状态;不自动重试准备写操作」),内层错误有 `next_action` 时也一并带上。
412
415
  用**同一个** `--request-id` 重试会从失败的阶段继续。
413
416
  - 其他失败:42200「version_id和32位小写十六进制request_id必填」、40900「请先安装指定版本」、
414
- 40300「只能准备本人private应用」、42200「应用执行或共享调度未启用,需要平台管理员完成部署」、
415
- 40900「准备结果未确认,请先诊断」。不传 `--request-id` 时命令行会自动生成。
417
+ 40300「只能准备本人private应用」(旧应用)、40300「只能准备本人上传的版本」(`appid` 应用传了别人上传的 `version_id`)、
418
+ 42200「应用执行或共享调度未启用,需要平台管理员完成部署」、40900「准备结果未确认,请先诊断」。不传 `--request-id` 时命令行会自动生成。
419
+
420
+ > **`prepare` 支持哪些版本**(说明)
421
+ >
422
+ >
423
+ > - **平台 `appid` 应用**:接受**本人上传**的版本(可见性 `private` 或 `members`),只准备开发环境
424
+ > (开发环境 → 数据库 → 开发数据域权限,声明了 headless 时再加签名并激活到开发),不会碰生产;
425
+ > 生产由**首次 `release`(发布为线上版本)**自动准备。声明了 headless 时,它等同于控制台「版本管理」下方「**开发环境:云函数激活**」。
426
+ > - **旧 `app_key` 应用**:只接受**本人 private、且已安装**的包,一路准备到生产。
416
427
 
417
428
  `zleap-app publish` 等写操作网络结果不明时返回 `COMMIT_UNKNOWN`,**不会自动重传**:
418
429
  用 `zleap-app operation --request-id <原操作号>` 或 `zleap-app status` 查询原结果。
@@ -421,58 +432,76 @@
421
432
 
422
433
  上传、`zleap-app check` 或安装时,Manifest 相关失败通过 `detail.reason` 返回:
423
434
 
424
- | 原因 | 含义 | 处理 |
425
- | ----------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------- |
435
+ | 原因 | 含义 | 处理 |
436
+ | ----------------------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------- |
426
437
  | `MANIFEST_UNSUPPORTED` | `schema` 不是 `zleap.app/2`、结构/引用/执行形态无效,或 `appid` 与 `app_key` 不一致 | 对照 Manifest(文档站 ./manifest) 逐字段核对 |
427
- | `SCHEMA_INVALID` | 集合 Schema 不符合数据面契约(`detail.collection` 指出集合) | 只用集合 Schema 子集 |
428
- | `RULE_DENIED` | 集合记录规则表达式无效 | 修正表达式 |
429
- | `RULE_FIELD_UNDECLARED` | 记录规则引用了 Schema 未声明的字段 | 在 Schema 顶层声明该字段 |
430
- | `RUNTIME_UNAVAILABLE` | 运行环境未开放(`builtin` UI、纯后台带函数等) | 见 不可安装的配置(文档站 ./limits#已知会导致「应用不可安装」的配置) |
431
- | `FEATURE_UNAVAILABLE` | 声明了未开放的能力或写法 | 同上 |
438
+ | `SCHEMA_INVALID` | 集合 Schema 不符合数据面契约(`detail.collection` 指出集合) | 只用集合 Schema 子集 |
439
+ | `RULE_DENIED` | 集合记录规则表达式无效 | 修正表达式 |
440
+ | `RULE_FIELD_UNDECLARED` | 记录规则引用了 Schema 未声明的字段 | 在 Schema 顶层声明该字段 |
441
+ | `RUNTIME_UNAVAILABLE` | 运行环境未开放(`builtin` UI、纯后台带函数等) | 见 不可安装的配置(文档站 ./limits#已知会导致「应用不可安装」的配置) |
442
+ | `FEATURE_UNAVAILABLE` | 声明了未开放的能力或写法 | 同上 |
432
443
 
433
444
  常见的上传失败文案(没有原因码,直接看提示):
434
445
 
435
- | 提示 | 原因 |
436
- | ----------------------------------------------------- | --------------------------------------------------- |
437
- | 应用包根目录必须包含非空 LICENSE 文件 | 缺少 `LICENSE` 或内容为空 |
438
- | manifest entry 必须指向存在的包内 HTML 文件 | `entry` 不是包内 `.html`/`.htm`,或文件不存在 |
446
+ | 提示 | 原因 |
447
+ | ----------------------------------------------------- | ----------------------------------------------------------------- |
448
+ | 应用包根目录必须包含非空 LICENSE 文件 | 缺少 `LICENSE` 或内容为空 |
449
+ | manifest entry 必须指向存在的包内 HTML 文件 | `entry` 不是包内 `.html`/`.htm`,或文件不存在 |
439
450
  | manifest 的 appid 与 app_key 不一致 | 同时写了 `appid` 和 `app_key` 且两者不同(42200),只保留 `appid` |
440
- | manifest app_key 必须使用包含发布者前缀的小写命名空间 | 应用标识既不是平台 `appid`,也不符合旧 `app_key` 命名空间规则 |
441
- | 发布者声明不属于当前登录用户 | 仅旧 `app_key` 应用:`publisher.publisher_id` 不是上传者本人 |
442
- | 不能覆盖其他发布者的应用标识 | 仅旧 `app_key` 应用:`app_key` 已被别人使用 |
443
- | 该版本已经存在,请更换版本号 | 同版本号、内容不同 |
444
- | 云函数制品入口缺失 | 函数 `entry` 在包里不存在 |
445
- | 应用包不在开发会话授权范围 | 包里的 `appid` 与 `zleap-app auth login` 时不一致 |
451
+ | manifest app_key 必须使用包含发布者前缀的小写命名空间 | 应用标识既不是平台 `appid`,也不符合旧 `app_key` 命名空间规则 |
452
+ | 发布者声明不属于当前登录用户 | 仅旧 `app_key` 应用:`publisher.publisher_id` 不是上传者本人 |
453
+ | 不能覆盖其他发布者的应用标识 | 仅旧 `app_key` 应用:`app_key` 已被别人使用 |
454
+ | 该版本已经存在,请更换版本号 | 同版本号、内容不同 |
455
+ | 云函数制品入口缺失 | 函数 `entry` 在包里不存在 |
456
+ | 应用包不在开发会话授权范围 | 包里的 `appid` 与 `zleap-app auth login` 时不一致 |
446
457
 
447
458
  ### 应用与成员
448
459
 
449
460
  用 `appid` 的应用,上传和成员管理按应用成员判权:
450
461
 
451
- | 业务码 | message | `detail.reason` | 处理(`next_action`) |
452
- | ------ | -------------------------------- | ---------------- | -------------------------------------------------------------- |
453
- | 40400 | appid 不存在 | `APP_NOT_FOUND` | 先在云开发控制台「创建应用」 |
454
- | 40300 | 你不是这个应用的开发者 | `NOT_APP_MEMBER` | 请联系应用管理员在云开发控制台「成员」里把你加为开发者 |
455
- | 40300 | 只有应用管理员可以管理成员 | — | 请管理员添加、移除成员或修改角色 |
456
- | 40900 | 应用至少要保留一位管理员 | — | 先把另一位成员设为管理员,再做这个操作 |
462
+ | 业务码 | message | `detail.reason` | 处理(`next_action`) |
463
+ | ------ | -------------------------- | ---------------- | ------------------------------------------------------ |
464
+ | 40400 | appid 不存在 | `APP_NOT_FOUND` | 先在云开发控制台「创建应用」 |
465
+ | 40300 | 你不是这个应用的开发者 | `NOT_APP_MEMBER` | 请联系应用管理员在云开发控制台「成员」里把你加为开发者 |
466
+ | 40300 | 只有应用管理员可以管理成员 | — | 请管理员添加、移除成员或修改角色 |
467
+ | 40900 | 应用至少要保留一位管理员 | — | 先把另一位成员设为管理员,再做这个操作 |
457
468
 
458
469
  成员管理见 [成员管理](console-members.md)。
459
470
 
460
- ### 发布为线上版本
471
+ ### 发布为线上版本与目录可见性
461
472
 
462
- 控制台「版本管理」发布或回退时(`POST /api/v1/app-cloud/apps/{app_id}/publish`):
473
+ 控制台「版本管理」发布 / 回退(`POST /api/v1/app-cloud/apps/{app_id}/publish`),以及应用中心「发布到企业目录 / 撤回企业目录」:
463
474
 
464
- | 业务码 | message | `detail.reason` | 处理(`next_action`) |
465
- | ------ | ------------------------------------------------------------------ | ------------------------- | ---------------------------------------------------------------------- |
466
- | 40900 | 发布带云资源的版本前,上传者需要正在使用这个版本 | `UPLOADER_NOT_ON_VERSION` | 请上传者打开应用并切换到这个版本(npm run deploy 会自动切换),再发布 |
467
- | 40900 | 该应用使用旧的命名空间标识,请在「版本与激活」里管理版本 | `LEGACY_APP` | 旧 `app_key` 应用走「版本与激活」 |
468
- | 40400 | 版本不存在或已删除 / 该版本没有可用制品 | — | 刷新版本列表后重试 |
469
- | 42200 | 发布说明最多 500 字 | — | 缩短发布说明 |
470
- | 40400 | 应用不存在,或你不是这个应用的成员 | — | 请联系应用管理员在云开发控制台「成员」里把你加为开发者 |
475
+ | 业务码 | message | `detail.reason` | 处理(`next_action`) |
476
+ | ------ | -------------------------------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------ |
477
+ | 40900 | 发布带云资源的版本前,上传者需要正在使用这个版本 | `UPLOADER_NOT_ON_VERSION` | 请上传者打开应用并切换到这个版本(npm run deploy 会自动切换),再发布 |
478
+ | 40900 | 这个应用请在云开发控制台「版本管理」里发布或回退 | `USE_VERSION_MANAGEMENT` | 应用中心不能改平台 `appid` 应用的线上版本;到控制台「发布 → 版本管理」点「发布为线上版本」或「回退到此版本」 |
479
+ | 40900 | 该应用使用旧的命名空间标识,请在「版本与激活」里管理版本 | `LEGACY_APP` | 旧 `app_key` 应用走「版本与激活」 |
480
+ | 40400 | 版本不存在或已删除 / 该版本没有可用制品 | — | 刷新版本列表后重试 |
481
+ | 42200 | 发布说明最多 500 字 | — | 缩短发布说明 |
482
+ | 40400 | 应用不存在,或你不是这个应用的成员 | — | 请联系应用管理员在云开发控制台「成员」里把你加为开发者 |
471
483
 
472
- `UPLOADER_NOT_ON_VERSION` 只出现在带云资源(集合、`cloud.*` 能力、`runtime.backend` 或 headless 能力)的版本上,见 [版本管理 · 带云资源的版本](console-release.md#带云资源的版本)。
484
+ `USE_VERSION_MANAGEMENT` 来自应用中心的「发布到企业目录 / 撤回企业目录」接口,不是版本管理接口本身的错误;
485
+ 它表示**这个应用不该在应用中心改线上状态**。平台 `appid` 应用请改用控制台「版本管理」。
486
+
487
+ `UPLOADER_NOT_ON_VERSION` 只出现在带云资源(集合、`cloud.*` 能力、`runtime.backend` 或 headless 能力)的版本上,且只在**第一次发布**时要求;已经发布过的版本再次发布会复用生产准备。见 [版本管理 · 带云资源的版本](console-release.md#带云资源的版本)。
473
488
 
474
489
  ## 版本更新与回滚
475
490
 
491
+ ### 版本切换(控制台「我也用这个」/ CLI `update`、`rollback`)
492
+
493
+ 这些码来自安装版本切换接口(`appversion/read.go`、`official.go`、`apply.go`):
494
+
495
+ | 业务码 | message | `detail.reason` | 处理 |
496
+ | ------ | -------------------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
497
+ | 42200 | 应用没有可切换的版本 | `VERSION_LIST_EMPTY` | 该应用还没有任何有效版本,或你的账号看不到任何版本;先在控制台的版本页确认已有上传(`appid`:版本管理;旧应用:版本与激活),再刷新 |
498
+ | 42200 | 官方版本切换必须指定当前安装版本 | `EXPECTED_VERSION_REQUIRED` | 请求里带上当前安装的版本 ID(CLI 用 `--expected-version-id`),不要省略 |
499
+ | 40900 | 安装版本已变化 | `INSTALLATION_VERSION_STALE` | 你带来的「当前版本」与安装记录不一致;重新读取安装状态后,用最新的版本 ID 再操作 |
500
+ | 40900 | 安装版本已变化 | `INSTALLATION_VERSION_CHANGED` | 提交期间安装被其他操作改掉了;刷新后重新读取,再决定是否重试 |
501
+
502
+ `INSTALLATION_VERSION_STALE` 在写入前检查就失败;`INSTALLATION_VERSION_CHANGED` 是写入时的并发校验失败(CAS 没命中)。
503
+ 两者都**不要用旧的版本 ID 盲目重试**,先 `zleap-app diagnose` 或在控制台刷新安装状态。
504
+
476
505
  ### 应用中心「更新」的预检
477
506
 
478
507
  | 原因码 | 级别 | 界面文案 |
@@ -485,6 +514,9 @@
485
514
  | `CONTINUITY_UNKNOWN` | 提醒 | 旧版本清单不可解析,本次升级将撤销全部后台能力授权。 |
486
515
  | `DATA_DELTA_UNKNOWN` | 提醒 | 数据声明无法比对,请人工确认集合变更。 |
487
516
 
517
+ `VERSION_NOT_AVAILABLE` 不在这张预检表里:它是「更新」页读取可切换版本列表时的**前端阻断**(`upgrade-model.ts`),
518
+ 表示目标版本不在当前账号可见的版本列表里,界面文案为「该版本当前不可切换(可能已下架或不可见)。」
519
+
488
520
  > **预检阻断只是提示**(注意)
489
521
  >
490
522
  > 实测预检返回「阻断」(`can_upgrade: false`)时,直接调用升级接口仍会执行。应用中心界面会按预检拦住,
@@ -503,7 +535,7 @@
503
535
 
504
536
  ## 签名证明验证
505
537
 
506
- 在控制台「发布版本」验证签名证明时,失败按原因码归类显示:
538
+ 在控制台版本页(`appid` 应用是「版本管理」,旧 `app_key` 应用是「版本与激活」)验证签名证明时,失败按原因码归类显示:
507
539
 
508
540
  | 服务端原因码 | 界面文案 |
509
541
  | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
@@ -9,7 +9,7 @@
9
9
  | --- | --- |
10
10
  | 类型检查 | `npm run typecheck` |
11
11
  | 构建 + 平台静态检查 | `npm run check`(`runtime_test=not_checked`,不代表能用) |
12
- | 登录 OS(浏览器授权,30 分钟) | `npm run login -- --target <OS 地址>` |
12
+ | 登录 OS(浏览器授权,自动续期,最长 30 天) | `npm run login -- --target <OS 地址>` |
13
13
  | 构建→检查→上传开发版本→装到本人 | `npm run deploy` |
14
14
  | 安装/权限/版本诊断 | `npm run status` |
15
15
  | 页面错误与云函数日志(24h) | `npm run logs`(`-- --environment dev\|prod`、`--invocation <调用编号>`) |
@@ -23,7 +23,7 @@
23
23
  - **一切先声明**:集合与结构写在 `manifest.json` 的 `data.collections` + `data.schemas`;权限写在 `runtime.capabilities.requires`。没声明的运行时一律拿不到。
24
24
  - **集合结构上线后不可改**:已建集合不会随新版本更新 Schema,改字段要新建集合并迁移。`additionalProperties` 缺省按 `false`。
25
25
  - **沙箱**:opaque origin + CSP `connect-src 'none'`、`worker-src 'none'`、只允许包内脚本/样式/字体。不能 `fetch`、不能用外部 CDN/字体、不能用 `localStorage`、不能 `alert/confirm`、不能开 Worker。
26
- - **只通过 `src/cloud/` 访问 SDK**;环境用 `DYNAMIC_CURRENT_ENV`,不要写死 dev/prod。SDK 是 npm 包 `@zleap-web/app-sdk`(另有 `/host` 宿主能力、`/workspace` 工作区入口),API 以 `node_modules/@zleap-web/app-sdk/dist/types/*.d.ts` 为准;升级 SDK 时与 `@zleap-web/app-cli` 保持同版本。
26
+ - **只通过 `src/cloud/` 访问 SDK**;环境用 `DYNAMIC_CURRENT_ENV`,不要写死 dev/prod。SDK 是 npm 包 `@zleap-ai/app-sdk`(另有 `/host` 宿主能力、`/workspace` 工作区入口),API 以 `node_modules/@zleap-ai/app-sdk/dist/types/*.d.ts` 为准;升级 SDK 时与 `@zleap-ai/app-cli` 保持同版本。
27
27
  - **单次读取最多 100 条**,用 `skip` 翻页(skip ≤ 1000)。更新/删除带 `expectedVersion`。
28
28
  - **结果未知不重试**:`COMMIT_UNKNOWN` 时先重读核对;部署脚本退出码 6 同理。
29
29
  - **明确失败优于假成功**:不要用模拟数据、假登录或吞掉错误来让界面「看起来能用」。
@@ -33,7 +33,7 @@ npm run deploy # 构建 → 检查 → 上
33
33
  ```
34
34
  manifest.json 应用声明(权限、集合与结构)。构建时复制到 dist/
35
35
  LICENSE 必需,构建时复制到 dist/
36
- src/cloud/ Cloud SDK(npm 包 @zleap-web/app-sdk)的封装:连接、错误说明、todos 读写
36
+ src/cloud/ Cloud SDK(npm 包 @zleap-ai/app-sdk)的封装:连接、错误说明、todos 读写
37
37
  scripts/zleap.mjs login / deploy / status / logs / versions / release
38
38
  AGENTS.md 给 AI 编程工具的项目规则
39
39
  .agents/skills/ AI 编程工具 skills(Codex、Pi、OpenCode、Cursor 等)
@@ -3,6 +3,9 @@
3
3
  "private": true,
4
4
  "version": "0.0.0",
5
5
  "type": "module",
6
+ "engines": {
7
+ "node": "^20.19.0 || >=22.12.0"
8
+ },
6
9
  "scripts": {
7
10
  "dev": "vite",
8
11
  "build": "tsc -b && vite build",