create-zleap-app 0.10.6 → 0.12.0
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.
- package/index.js +16 -14
- package/lib/scaffold.js +18 -5
- package/package.json +4 -4
- package/skills/zleap-cloud-app/SKILL.md +47 -11
- package/skills/zleap-cloud-app/references/cloud-functions.md +11 -6
- package/skills/zleap-cloud-app/references/limits.md +22 -19
- package/skills/zleap-cloud-app/references/sdk-database-files.md +4 -4
- package/skills/zleap-cloud-app/references/status-codes.md +24 -20
- package/skills/zleap-cloud-app/references/ui.md +304 -0
- package/skills/zleap-cloud-app-release/references/app-center-install.md +19 -11
- package/skills/zleap-cloud-app-release/references/app-center-publish.md +6 -6
- package/skills/zleap-cloud-app-release/references/console-release.md +1 -1
- package/skills/zleap-cloud-app-release/references/dev-to-production.md +67 -1
- package/skills/zleap-cloud-app-release/references/status-codes.md +24 -20
- package/templates/_shared/.cursor/mcp.json +14 -0
- package/templates/_shared/.mcp.json +14 -0
- package/templates/{react-ts → _shared}/scripts/zleap.mjs +138 -83
- package/templates/react-ts/AGENTS.md +9 -3
- package/templates/react-ts/package.json +4 -0
- package/templates/react-ts/src/App.tsx +227 -96
- package/templates/react-ts/src/cloud/client.ts +21 -6
- package/templates/react-ts/src/cloud/preview.ts +116 -0
- package/templates/react-ts/src/cloud/useCloudConnection.ts +2 -2
- package/templates/react-ts/src/index.css +5 -241
- package/templates/react-ts/src/main.tsx +3 -0
- package/templates/vanilla/AGENTS.md +37 -0
- package/templates/vanilla/CLAUDE.md +1 -0
- package/templates/vanilla/README.md +37 -0
- package/templates/vanilla/_gitignore +7 -0
- package/templates/vanilla/app/LICENSE +21 -0
- package/templates/vanilla/app/index.html +67 -0
- package/templates/vanilla/app/main.js +222 -0
- package/templates/vanilla/app/manifest.json +33 -0
- package/templates/vanilla/app/preview.js +114 -0
- package/templates/vanilla/app/style.css +5 -0
- package/templates/vanilla/package.json +32 -0
package/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import { createInterface } from 'node:readline/promises';
|
|
|
13
13
|
import { fileURLToPath } from 'node:url';
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
|
+
BUILD_TEMPLATES,
|
|
16
17
|
TEMPLATES,
|
|
17
18
|
detectPackageManager,
|
|
18
19
|
installSkills,
|
|
@@ -21,8 +22,8 @@ import {
|
|
|
21
22
|
parseArgs,
|
|
22
23
|
scaffold,
|
|
23
24
|
toPackageName,
|
|
24
|
-
validateAppId,
|
|
25
25
|
validateName,
|
|
26
|
+
validateOptionalAppId,
|
|
26
27
|
} from './lib/scaffold.js';
|
|
27
28
|
|
|
28
29
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
@@ -45,9 +46,9 @@ ${bold('create-zleap-app')} ${dim(`v${pkg.version}`)} — 创建 zleapOS 云开
|
|
|
45
46
|
npx create-zleap-app@latest skills [目录] 给已有项目补装 AI 编程工具 skills(默认当前目录)
|
|
46
47
|
|
|
47
48
|
选项
|
|
48
|
-
--appid <appid>
|
|
49
|
+
--appid <appid> 已有应用的 appid(形如 zl3f9a2c7e1b0d4a68);不填时第一次 npm run login 会在授权时自动新建应用
|
|
49
50
|
--name <名称> 应用显示名称
|
|
50
|
-
--template <名>
|
|
51
|
+
--template <名> 模板:react-ts(React + TS + Vite,默认)/ vanilla(原生 HTML/JS,不用构建,装得快)
|
|
51
52
|
--target <URL> zleapOS 地址,写入项目配置,之后 npm run login 不再询问
|
|
52
53
|
--pm <名> 包管理器:npm / pnpm / yarn / bun(默认跟随调用方)
|
|
53
54
|
--no-install 不安装依赖
|
|
@@ -92,10 +93,10 @@ async function main() {
|
|
|
92
93
|
const options = parseArgs(argv);
|
|
93
94
|
if (options.help) return console.log(HELP);
|
|
94
95
|
if (options.version) return console.log(pkg.version);
|
|
95
|
-
// 生成器本身在旧 Node 上也能跑,但生成的项目构建不了:先说清楚,不中断(用户可能稍后再升级)。
|
|
96
|
-
const nodeProblem = nodeVersionProblem();
|
|
97
|
-
if (nodeProblem) console.log(red(`! ${nodeProblem}`));
|
|
98
96
|
if (!TEMPLATES.includes(options.template)) throw new Error(`未知模板:${options.template}(可选 ${TEMPLATES.join(' / ')})`);
|
|
97
|
+
// 生成器本身在旧 Node 上也能跑,但构建型模板生成的项目构建不了:先说清楚,不中断(用户可能稍后再升级)。
|
|
98
|
+
const nodeProblem = BUILD_TEMPLATES.includes(options.template) ? nodeVersionProblem() : null;
|
|
99
|
+
if (nodeProblem) console.log(red(`! ${nodeProblem}`));
|
|
99
100
|
|
|
100
101
|
const interactive = process.stdin.isTTY && !options.yes;
|
|
101
102
|
const rl = interactive ? createInterface({ input: process.stdin, output: process.stdout }) : null;
|
|
@@ -107,11 +108,11 @@ async function main() {
|
|
|
107
108
|
|
|
108
109
|
let appid = options.appid;
|
|
109
110
|
if (!appid && rl) {
|
|
110
|
-
console.log(dim(' appid
|
|
111
|
-
appid = await prompt(rl, 'appid', '',
|
|
111
|
+
console.log(dim(' 已有应用就粘贴它的 appid;留空则在第一次 npm run login 授权时自动新建应用。'));
|
|
112
|
+
appid = await prompt(rl, 'appid(可留空)', '', validateOptionalAppId);
|
|
112
113
|
}
|
|
113
|
-
const idProblem =
|
|
114
|
-
if (idProblem) throw new Error(`${idProblem}\n 用法:npm create zleap-app@latest ${dir} -- --appid <appid
|
|
114
|
+
const idProblem = validateOptionalAppId(appid);
|
|
115
|
+
if (idProblem) throw new Error(`${idProblem}\n 用法:npm create zleap-app@latest ${dir} -- --appid <appid>(或不带 --appid,登录时自动新建应用)`);
|
|
115
116
|
|
|
116
117
|
let name = options.name;
|
|
117
118
|
if (!name) name = rl ? await prompt(rl, '应用名称', toPackageName(dir), validateName) : toPackageName(dir);
|
|
@@ -122,7 +123,7 @@ async function main() {
|
|
|
122
123
|
if (!options.name && !rl)
|
|
123
124
|
console.log(red(`! 没有提供 --name,应用名称暂用目录名「${name}」。发布时应用名以 manifest.json 的 name 为准,请改成控制台里的应用名(或重新生成时加 --name "应用名")`));
|
|
124
125
|
|
|
125
|
-
console.log(`\n在 ${cyan(targetDir)} 创建 ${bold(name)}(${appid})`);
|
|
126
|
+
console.log(`\n在 ${cyan(targetDir)} 创建 ${bold(name)}(${appid || '登录时新建应用'})`);
|
|
126
127
|
scaffold({
|
|
127
128
|
templateDir: join(here, 'templates', options.template),
|
|
128
129
|
targetDir,
|
|
@@ -165,13 +166,14 @@ async function main() {
|
|
|
165
166
|
${bold('完成!接下来:')}
|
|
166
167
|
|
|
167
168
|
cd ${cd}${installed ? '' : `\n ${pm} install`}
|
|
168
|
-
${runScript('login')}${options.target ? '' : ' -- --target https://你的-os-地址'} ${dim('# 浏览器授权')}
|
|
169
|
-
${runScript('deploy')} ${dim('# 构建→检查→上传→装到你的桌面')}
|
|
169
|
+
${runScript('login')}${options.target ? '' : ' -- --target https://你的-os-地址'} ${dim(appid ? '# 浏览器授权' : '# 浏览器授权,同时新建应用')}
|
|
170
|
+
${runScript('deploy')} ${dim(BUILD_TEMPLATES.includes(options.template) ? '# 构建→检查→上传→装到你的桌面' : '# 检查→上传→装到你的桌面(不用构建)')}
|
|
170
171
|
|
|
171
172
|
然后从 zleapOS 启动台打开应用(开发环境与数据库在安装时自动开通)。
|
|
172
173
|
|
|
173
174
|
${bold('让 AI 编程工具帮你开发')}
|
|
174
|
-
项目里已带 AGENTS.md 和
|
|
175
|
+
项目里已带 AGENTS.md、skills 和 MCP 配置(.mcp.json、.cursor/mcp.json),直接在 Claude Code / Cursor / Codex / OpenCode 里打开本目录即可。
|
|
176
|
+
AI 会先通过 MCP 查清你账号下可用的 OS 接口与 SDK,没有的再用云函数补充。
|
|
175
177
|
已有项目补装 skills:${cyan('npx create-zleap-app@latest skills')}
|
|
176
178
|
`);
|
|
177
179
|
} finally {
|
package/lib/scaffold.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 项目生成的纯逻辑:参数解析、命名校验、模板复制与占位符替换、skills 安装。
|
|
2
2
|
// 不依赖第三方包,便于 npx 秒开,也便于单元测试。
|
|
3
3
|
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, writeFileSync } from 'node:fs';
|
|
4
|
-
import { basename, join, relative, resolve } from 'node:path';
|
|
4
|
+
import { basename, dirname, join, relative, resolve } from 'node:path';
|
|
5
5
|
|
|
6
6
|
/** 平台生成的 appid(云开发控制台「创建应用」得到),与服务端 pkg/appid.Platform 一致。 */
|
|
7
7
|
export const APPID_PATTERN = /^zl[0-9a-f]{16}$/;
|
|
@@ -9,10 +9,14 @@ export const APPID_PATTERN = /^zl[0-9a-f]{16}$/;
|
|
|
9
9
|
/** 旧的命名空间 app_key(com.example.todo)。服务端在清理阶段之前仍接受,只为已有应用保留。 */
|
|
10
10
|
export const APP_KEY_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*(?:\.[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*)+$/;
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
/** react-ts:React + TypeScript + Vite(构建到 dist/);vanilla:原生 HTML / JS,不用构建(应用在 app/)。 */
|
|
13
|
+
export const TEMPLATES = ['react-ts', 'vanilla'];
|
|
14
|
+
|
|
15
|
+
/** 需要 Vite 7 的模板(对 Node.js 版本有要求)。 */
|
|
16
|
+
export const BUILD_TEMPLATES = ['react-ts'];
|
|
13
17
|
|
|
14
18
|
/** 需要做占位符替换的文本文件(相对模板根目录)。 */
|
|
15
|
-
const SUBSTITUTED = ['package.json', 'manifest.json', 'index.html', 'LICENSE', 'README.md', 'AGENTS.md'];
|
|
19
|
+
const SUBSTITUTED = ['package.json', 'manifest.json', 'index.html', 'LICENSE', 'README.md', 'AGENTS.md', 'app/manifest.json', 'app/index.html', 'app/LICENSE'];
|
|
16
20
|
|
|
17
21
|
/** 为 AI 编程工具安装 skills 的项目内目录:.agents/skills 覆盖 Codex、Pi、OpenCode、Cursor、Gemini CLI、Copilot 等,.claude/skills 给 Claude Code。 */
|
|
18
22
|
export const SKILL_TARGETS = ['.agents/skills', '.claude/skills'];
|
|
@@ -30,6 +34,11 @@ export function copyTree(from, to, skip = () => false, base = from) {
|
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
/** appid:优先平台 appid;旧命名空间 app_key 仍可用(已有应用)。 */
|
|
37
|
+
/** appid 可以留空:留空时 npm run login 会在授权时自动新建应用。 */
|
|
38
|
+
export function validateOptionalAppId(value) {
|
|
39
|
+
return value ? validateAppId(value) : null;
|
|
40
|
+
}
|
|
41
|
+
|
|
33
42
|
export function validateAppId(value) {
|
|
34
43
|
if (!value) return '请提供 appid:在 zleapOS 云开发控制台「创建应用」后复制(形如 zl3f9a2c7e1b0d4a68)';
|
|
35
44
|
if (APPID_PATTERN.test(value)) return null;
|
|
@@ -153,11 +162,15 @@ const escapeHTML = (text) => text.replace(/[&<>"']/g, (c) => ({ '&': '&', '<
|
|
|
153
162
|
|
|
154
163
|
/** 复制模板并替换占位符。返回写入的文件数。 */
|
|
155
164
|
export function scaffold({ templateDir, targetDir, appid, appKey, name, packageName, cliPackage, cliVersion, sdkPackage, sdkVersion = cliVersion, year = new Date().getFullYear() }) {
|
|
156
|
-
|
|
165
|
+
const skip = (rel) => /(^|[\\/])(node_modules|dist|out|\.zleap)([\\/]|$)/.test(rel);
|
|
166
|
+
copyTree(templateDir, targetDir, skip);
|
|
167
|
+
// 各模板共用的文件(部署脚本 scripts/zleap.mjs)只维护一份,放在 templates/_shared。
|
|
168
|
+
const shared = join(dirname(templateDir), '_shared');
|
|
169
|
+
if (existsSync(shared)) copyTree(shared, targetDir, skip);
|
|
157
170
|
if (existsSync(join(targetDir, '_gitignore'))) renameSync(join(targetDir, '_gitignore'), join(targetDir, '.gitignore'));
|
|
158
171
|
|
|
159
172
|
const replacements = {
|
|
160
|
-
__APPID__: appid ?? appKey,
|
|
173
|
+
__APPID__: appid ?? appKey ?? '',
|
|
161
174
|
__APP_NAME__: name,
|
|
162
175
|
__PKG_NAME__: packageName,
|
|
163
176
|
__CLI_PACKAGE__: cliPackage,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-zleap-app",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "一条命令创建 zleapOS 云开发应用(React + TypeScript + Vite),并为 Codex / Claude Code / Pi / OpenCode / Cursor 等 AI 编程工具配好 skills",
|
|
3
|
+
"version": "0.12.0",
|
|
4
|
+
"description": "一条命令创建 zleapOS 云开发应用(React + TypeScript + Vite,或不用构建的原生 HTML/JS),并为 Codex / Claude Code / Pi / OpenCode / Cursor 等 AI 编程工具配好 skills",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
},
|
|
24
24
|
"zleapKit": {
|
|
25
25
|
"cliPackage": "@zleap-ai/app-cli",
|
|
26
|
-
"cliVersion": "0.
|
|
26
|
+
"cliVersion": "0.12.0",
|
|
27
27
|
"sdkPackage": "@zleap-ai/app-sdk",
|
|
28
|
-
"sdkVersion": "0.
|
|
28
|
+
"sdkVersion": "0.12.0",
|
|
29
29
|
"skillsRepo": "RuoFeng2015/zleap-app-kit"
|
|
30
30
|
},
|
|
31
31
|
"keywords": [
|
|
@@ -12,23 +12,43 @@ zleapOS 云开发应用 = 一个网页(运行在 zleapOS 桌面的沙箱 ifram
|
|
|
12
12
|
|
|
13
13
|
| 看到 | 说明 | 构建/打包目录 |
|
|
14
14
|
| --- | --- | --- |
|
|
15
|
-
| `vite.config.ts` + `src/cloud/` + `scripts/zleap.mjs` | create-zleap-app 的 React
|
|
16
|
-
|
|
|
15
|
+
| `vite.config.ts` + `src/cloud/` + `scripts/zleap.mjs` | create-zleap-app 的 React 模板(组件化、复杂界面) | `dist/`(构建产物) |
|
|
16
|
+
| `package.json` 里 `"zleap": { "packageDir": "app" }` + `app/main.js` + `scripts/zleap.mjs` | create-zleap-app 的 vanilla 模板(原生 HTML/JS,不用构建,表单 / 列表 / 统计类首选) | `app/` |
|
|
17
|
+
| 根目录 `index.html` + `main.js` + `sdk/`,没有 package.json | `zleap-app init hosted-package` 的原生模板 | 项目根目录 |
|
|
17
18
|
| 都没有 | 新建项目,见第 1 节 | — |
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
以 `node_modules/@zleap-ai/app-sdk/dist/types/*.d.ts`
|
|
20
|
+
几种形态的 SDK 是同一套 API,只是来源不同:React 模板从 npm 包 `@zleap-ai/app-sdk` 导入(`/host` 宿主能力、`/workspace` 工作区),
|
|
21
|
+
以 `node_modules/@zleap-ai/app-sdk/dist/types/*.d.ts` 为准(SDK 与 `@zleap-ai/app-cli` 各自独立发版,版本号不必相同);vanilla 模板用 `app/sdk/*.js`(`npm install` / `npm run deploy` 从同一个 npm 包复制,不要手改);原生模板用项目里的 `./sdk/*.js`。
|
|
21
22
|
参考资料里写 `./sdk/index.js` / `./sdk/zleap-sdk.js` 的地方,在 React 模板里分别对应 `@zleap-ai/app-sdk` / `@zleap-ai/app-sdk/host`。
|
|
22
23
|
|
|
24
|
+
## 开发路线:先查现成能力,再补云函数(所有模板都一样)
|
|
25
|
+
|
|
26
|
+
React 模板、vanilla 模板、原生模板走同一条路线。目的是不重复造轮子、少踩运行时限制:
|
|
27
|
+
|
|
28
|
+
1. **先查当前用户能用什么**(项目里已配好 MCP 服务 `zleap-app`,见 `.mcp.json`;没登录时 MCP 工具会报未登录,请用户先 `npm run login`):
|
|
29
|
+
- `os_business_search(query)`:按需求搜索当前账号有权限的 OS 接口(待办、文件、日程、项目、AI 生成文字、定时任务……),中文、英文关键词各搜一次。**先看返回的 `page_api.enabled`**:`false` 表示这个部署没开放页面 API,页面里 `api.fetch` 一律失败,改用 SDK 能力、集合或云函数,并告诉用户;
|
|
30
|
+
- `os_business_get(operation)`:读契约和页面写法 `page_call`(`page_unavailable` 非空表示页面不能直接调,结果里给替代方案);
|
|
31
|
+
- `os_business_call(operation, input)`:用用户自己的账号试调。读接口直接调;**写 / 删接口先传 `dry_run: true`**(只校验参数、预览请求,不写数据),真正调用会改用户的真实数据,要调就先告诉用户、试完删掉;
|
|
32
|
+
- 页面里要 AI 生成内容:用 `POST /agents/complete`(要 JSON 传 `output_schema`,结果在 `data.result`;涉及日期把今天和两周日历写进提示词;契约模式可能要 40–60 秒,界面要提示等待)。没有页面参与(定时、触发器)才用后台能力 `os.agent.complete`。写法见文档「Cloud SDK 使用 → 调用 AI 生成内容」;
|
|
33
|
+
- `app_capabilities_search` / `app_capability_get`:平台后台能力;`app_sdk_get` / `zleap://sdk/guide`:SDK 能力与类型;`app_spec_get(section=model)`:Manifest 字段。
|
|
34
|
+
2. **OS 已有的数据和功能直接用**:页面里 `zl.cloud.os.api.fetch('/路径')`(不带 `/api/v1`),按使用者权限执行,不要在应用里另存一份。
|
|
35
|
+
`api.fetch` 不依赖云环境:**只调 OS 接口、没有集合 / 云函数 / 云文件的应用,不要调用 `zl.cloud.init` 和 `zl.cloud.os.ready()`**(这类应用平台不准备云环境,`ready()` 会报 `ENV_FORBIDDEN`),连接状态以第一次 `api.fetch` 成功为准。模板里的 `init`/`ready` 是给云数据库用的,删掉集合时一并删掉。
|
|
36
|
+
3. **应用自己产生的数据才建集合**(`db.collection`,在 manifest 声明)。
|
|
37
|
+
4. **以上都没有的后端逻辑才写云函数**(references/cloud-functions.md)。云函数不能调 OS 接口,需要 OS 数据时由页面取到后传入。
|
|
38
|
+
|
|
39
|
+
一个接口不可用时先找替代接口,搜不到再判断 OS 没有;不要猜接口路径,也不要读平台源码。
|
|
40
|
+
|
|
23
41
|
## 1. 新建项目(非交互,适合 AI 执行)
|
|
24
42
|
|
|
25
43
|
```bash
|
|
26
44
|
npm create zleap-app@latest <目录> -- --appid <appid> --name "<应用名>" -y
|
|
27
45
|
# 已知 OS 地址时加:--target https://os.example.com
|
|
46
|
+
# 表单 / 列表 / 统计这类不需要组件化的应用,加 --template vanilla:不装 React/Vite,不用构建,部署更快
|
|
28
47
|
```
|
|
29
48
|
|
|
30
|
-
`appid`
|
|
31
|
-
|
|
49
|
+
`appid` 由平台生成,**不能自己编**。还没有应用时**不带 `--appid`** 生成项目,然后请用户运行 `npm run login`:浏览器授权页会写明「新建应用「<应用名>」」,
|
|
50
|
+
用户核对确认码点允许后,平台创建应用(用户是管理员,一次授权只建一个),CLI 把 `appid`(`zl` + 16 位小写十六进制)写进 manifest。
|
|
51
|
+
用户已经有应用时,用 `--appid <appid>` 生成(或填进 manifest),不要再建新的。
|
|
32
52
|
manifest 里写 `"appid": "<appid>"`,不要写 `app_key` 和 `publisher`。CLI 登录参数名是 `--app-key`,填的就是 appid;CLI 输出里的 `app_key` 字段也是 appid。
|
|
33
53
|
同事要一起开发,请管理员在控制台「团队」→「成员」里把他加为开发者。每个成员的每次上传都是一个开发版本,在控制台「版本管理」里可见;
|
|
34
54
|
成员可以在「版本管理」用「切换到此版本」运行同事上传、未发布的版本(只切自己的安装;非成员看不到开发版本)。带云函数的版本上传时平台自动签名、安装 / 切换 / 允许后台运行时自动绑定,appid 应用不需要签名激活操作。给全企业用 = 在「版本管理」里「发布为线上版本」,见 skill `zleap-cloud-app-release`。
|
|
@@ -38,20 +58,35 @@ manifest 里写 `"appid": "<appid>"`,不要写 `app_key` 和 `publisher`。CLI
|
|
|
38
58
|
1. **一切先在 manifest 声明**:集合 `data.collections` + 结构 `data.schemas`;权限 `runtime.capabilities.requires`。没声明的拿不到。
|
|
39
59
|
2. **集合结构上线即固定**:已建集合不随新版本更新 Schema;`additionalProperties` 缺省按 `false`;集合键 `^[a-z][a-z0-9_]{0,63}$`,不能用 `sys_`/`ac_`/`t_` 前缀。要改字段 → 新集合 + 迁移。动手改 schema 前先告诉用户这一点。
|
|
40
60
|
3. **沙箱**:opaque origin,CSP `connect-src 'none'`、`worker-src 'none'`、脚本/样式/字体只能来自包内。所以:不能 `fetch`/XHR/WebSocket、不能用 CDN 或在线字体、不能用 `localStorage`/`sessionStorage`/`indexedDB`、不能 `alert/confirm/prompt`、不能开 Worker、不能弹新窗口。状态放内存或云数据库。
|
|
41
|
-
4.
|
|
61
|
+
4. **环境由宿主决定**:用云数据库 / 云函数 / 云文件时 `zl.cloud.init({ env: zl.cloud.DYNAMIC_CURRENT_ENV })`,绝不写死 `'dev'`/`'prod'`;只用 OS 接口的应用不调 `init`/`ready()`(见上文开发顺序第 2 条)。
|
|
42
62
|
5. **读取上限**:单次 `limit` ≤ 100,`skip` ≤ 1000,用分页;查询有筛选/排序但没索引时可能 `INDEX_REQUIRED`。
|
|
43
63
|
6. **乐观锁**:`update`/`remove` 带 `agentos: { expectedVersion: doc._version }`;冲突 `VERSION_CONFLICT` → 重读再改。
|
|
44
64
|
7. **结果未知不重试**:`COMMIT_UNKNOWN`(CLI 退出码 6)→ 先重读或查询原操作,绝不自动重复提交。
|
|
45
|
-
8.
|
|
65
|
+
8. **明确失败优于假成功**:在 zleapOS 里不要用 mock 数据、假登录、吞错误让界面「看起来能用」。唯一例外是**预览模式**:浏览器直接打开(`npm run preview`)没有宿主时,模板用 `preview` 模块的示例数据渲染界面并显示「预览模式」横幅;不要把示例数据用到宿主里的失败兜底上。
|
|
46
66
|
9. **凭据**:登录只能由用户在浏览器确认(`npm run login`),不要索要或输入密码,不要读取/打印 `~/.config/zleap-app/session.json`。
|
|
47
67
|
|
|
68
|
+
## 界面:用官方样式,做完整的状态(外观与功能同样要验收)
|
|
69
|
+
|
|
70
|
+
应用跑在 zleapOS 桌面窗口里,要和桌面其他应用看起来是一套。**不要写浏览器默认样式的输入框加按钮就交差。**
|
|
71
|
+
|
|
72
|
+
1. **用官方样式 `zleap.css`**:模板已引入(vanilla `app/sdk/zleap.css`,React `import '@zleap-ai/app-sdk/zleap.css'`)。颜色、圆角、字号与桌面同源,**自动跟随桌面亮 / 暗**。
|
|
73
|
+
页面骨架 `zl-page` → `zl-header`(`zl-title` + `zl-subtitle` + `zl-status`)→ 若干 `zl-section`;控件用 `zl-btn`(`--primary` 每屏最多一个)、`zl-input` / `zl-textarea`、`zl-segmented`、`zl-list` / `zl-list-item`、`zl-card`、`zl-stats`、`zl-tag`、`zl-alert`、`zl-empty`、`zl-skeleton`、`zl-toast`。
|
|
74
|
+
全部类与常用版式见 references/ui.md。自己的样式写在 `style.css` / `index.css`,颜色只用 `var(--zl-*)`,**不要写死色值**(暗色会坏)。
|
|
75
|
+
2. **每块数据四种状态都要有**:加载中(`zl-skeleton`,不要空白)、出错(`zl-alert--danger` + 中文说明 + 重试)、空(`zl-empty`:图标 + 一句话 + 下一步)、有数据。
|
|
76
|
+
操作时按钮 `aria-busy="true"` 防重复提交;成功用 `zl-toast` 轻提示;危险操作行内二次确认(不能用 `confirm`)。AI 生成这类耗时操作要显示进度文字。
|
|
77
|
+
3. **信息层次**:主操作放在最显眼处;次要信息用 `zl-muted` / `zl-item-meta`;数字用 `zl-num`;长文本(AI 结果)用 `zl-prose`。不要所有东西同一个字号、同一个颜色堆在一起。
|
|
78
|
+
4. **原生 `<select>` 在沙箱窗口里体验差**:选项少(≤ 5)用 `zl-segmented`,多的用一组按钮或列表。
|
|
79
|
+
5. **看一眼再交付**:改完界面运行 `npm run preview`(浏览器直接打开,示例数据,亮 / 暗都看),能截图的工具截图自查布局、对齐、空状态;
|
|
80
|
+
新增集合或 OS 接口时在 `preview` 模块补一份示例数据。功能仍以 `npm run deploy` 后在 zleapOS 里打开为准。
|
|
81
|
+
|
|
48
82
|
## 3. 开发一个功能的标准流程
|
|
49
83
|
|
|
84
|
+
0. **先查现成能力**:按上面「开发路线」用 MCP 查 OS 接口与 SDK,能直接调的就不建集合、不写云函数。
|
|
50
85
|
1. **建模**:在 `manifest.json` 加集合与 strict schema(类型只支持 object/array/string/number/integer/boolean/null,关键字子集见 references/manifest.md)。日期用 `number` 存毫秒,或 `integer` + `"x-agentos-value-type": "date_ms"`。
|
|
51
86
|
2. **权限**:需要的能力加进 `requires`(`cloud.database.read/write`、`cloud.storage.read/write`、`os.agent.complete` 等,白名单外的非可选能力会让应用「暂不可用」)。`reason` 会展示给用户,写具体用途。
|
|
52
|
-
3. **数据层**:React 模板在 `src/cloud/` 下按集合写一个模块(参照 `src/cloud/todos.ts`),组件只调用这些函数;错误用 `describeError()` 展示。
|
|
53
|
-
4.
|
|
54
|
-
5. **部署到自己桌面**:`npm run deploy`(构建 → 检查 → 上传开发版本(appid
|
|
87
|
+
3. **数据层**:React 模板在 `src/cloud/` 下按集合写一个模块(参照 `src/cloud/todos.ts`),组件只调用这些函数;错误用 `describeError()` 展示。OS 接口用 `osFetch()`(vanilla 用 `zl.cloud.os.api.fetch`),预览模式会自动换成示例数据。
|
|
88
|
+
4. **界面与本地验证**:按上面「界面」一节写界面,`npm run preview` 看效果;再 `npm run typecheck && npm run check`(`check` 通过只代表静态契约,`runtime_test=not_checked`)。
|
|
89
|
+
5. **部署到自己桌面**:`npm run deploy`(构建 → 检查 → 上传开发版本(appid 应用仅本人和开发人员可见)→ 安装/切换本人安装 → 按平台诊断准备资源;内容变化时自动递增补丁号)。上传之后的步骤由 `zleap-app deploy` 完成,可重复执行,部署记录在 `.zleap/state.json`;不要再手动 publish / install / update 另起一套记录。
|
|
55
90
|
- 报未登录/会话过期 → 请用户运行 `npm run login -- --target <OS 地址>` 并在浏览器核对确认码。
|
|
56
91
|
- 报 `DATA_MIGRATION_REQUIRED` → 新版本改了 data/storage 声明,见第 5 节。
|
|
57
92
|
6. **请用户验收**:安装时平台会自动创建开发环境、开通数据库并按声明建集合,`npm run deploy` 结束时会打印状态;显示未就绪时,请用户在「云开发控制台」选中应用 →「概览」按提示处理。然后从启动台打开应用(已打开的点「重新加载应用」)。
|
|
@@ -115,6 +150,7 @@ manifest 里写 `"appid": "<appid>"`,不要写 `app_key` 和 `publisher`。CLI
|
|
|
115
150
|
|
|
116
151
|
## 6. 参考资料(按需读取,别一次全读)
|
|
117
152
|
|
|
153
|
+
- references/ui.md — 官方样式 zleap.css 全部类、常用版式(列表、表单、详情、统计、AI 结果)、四种状态写法
|
|
118
154
|
- references/manifest.md — Manifest v2 全字段、集合 Schema 子集、分区与记录规则、打包规则
|
|
119
155
|
- references/sdk-database-files.md — Cloud SDK:初始化与 profile、查询/写入/watch/onChange、文件上传下载、操作编号、COMMIT_UNKNOWN
|
|
120
156
|
- references/cloud-functions.md — 云函数、headless 能力、`os.agent.complete`、`capabilities.invoke` / `jobs.wait`
|
|
@@ -284,7 +284,12 @@ await zl.cloud.os.database.mutate({
|
|
|
284
284
|
`document._version` 是写入后的版本号,下次 `update` / `remove` 时作为 `expected_version` 传回去。
|
|
285
285
|
|
|
286
286
|
```js
|
|
287
|
-
const saved = await zl.cloud.os.database.mutate({
|
|
287
|
+
const saved = await zl.cloud.os.database.mutate({
|
|
288
|
+
collection: 'drafts',
|
|
289
|
+
operation: 'add',
|
|
290
|
+
data,
|
|
291
|
+
request_id: `${context.invocationId}:add`,
|
|
292
|
+
});
|
|
288
293
|
return { id: saved.document._id, version: saved.document._version };
|
|
289
294
|
```
|
|
290
295
|
|
|
@@ -664,11 +669,11 @@ console.log(job.result);
|
|
|
664
669
|
|
|
665
670
|
### 其他作业 API
|
|
666
671
|
|
|
667
|
-
| API | 说明
|
|
668
|
-
| ------------------------------------------------------------------------------------ |
|
|
669
|
-
| `jobs.wait({ jobId, timeoutSeconds?, pollMilliseconds? })` | 等到终态;`timeoutSeconds` 0–300(默认 25),`pollMilliseconds` 100–5000(默认 500);超时仍未结束抛 `CALL_TIMEOUT`(带 `jobId`)
|
|
670
|
-
| `jobs.get({ jobId })` / `jobs.result({ jobId })` | 读一次当前状态
|
|
671
|
-
| `jobs.submit({ kind: 'capability', capability, input, operationId?, waitSeconds? })` | 与 `capabilities.invoke` 等价的另一种写法
|
|
672
|
+
| API | 说明 |
|
|
673
|
+
| ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
674
|
+
| `jobs.wait({ jobId, timeoutSeconds?, pollMilliseconds? })` | 等到终态;`timeoutSeconds` 0–300(默认 25),`pollMilliseconds` 100–5000(默认 500);超时仍未结束抛 `CALL_TIMEOUT`(带 `jobId`) |
|
|
675
|
+
| `jobs.get({ jobId })` / `jobs.result({ jobId })` | 读一次当前状态 |
|
|
676
|
+
| `jobs.submit({ kind: 'capability', capability, input, operationId?, waitSeconds? })` | 与 `capabilities.invoke` 等价的另一种写法 |
|
|
672
677
|
| `jobs.cancel({ jobId })` | 请求取消,返回 `{ cancelRequested: true }`。排队中或函数还没开始写数据时,作业以 `cancelled` 结束;正在执行的函数会被立即停止,结果为 `cancelled`。**取消不是回滚**:已提交的写入不会撤销 |
|
|
673
678
|
|
|
674
679
|
同一个 `operationId` 再次提交、输入相同 → 返回原作业;输入不同 → 冲突「同一操作编号的输入不同」。
|
|
@@ -14,22 +14,22 @@
|
|
|
14
14
|
|
|
15
15
|
## 能力开放状态
|
|
16
16
|
|
|
17
|
-
| 能力 | 状态 | 说明
|
|
18
|
-
| ---------------------------------- | ------------- |
|
|
19
|
-
| 云数据库 | ✅ 开放 | 集合按 Manifest 自动创建;文档、索引、备份恢复
|
|
20
|
-
| 文件存储 | ✅ 开放 | 分块上传、下载、条件覆盖、软删除
|
|
21
|
-
| 日志 | ✅ 开放 | 查询 + 追尾;生产环境只读,发布者可见全部调用日志
|
|
22
|
-
| 云函数 / 后台能力 | ⚠️ 视部署而定 | 需要部署了函数运行环境;还需就绪的签名发布(带后台能力的版本上传时由平台自动签名,安装 / 切换版本 / 授权时自动绑定)并授权
|
|
17
|
+
| 能力 | 状态 | 说明 |
|
|
18
|
+
| ---------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
19
|
+
| 云数据库 | ✅ 开放 | 集合按 Manifest 自动创建;文档、索引、备份恢复 |
|
|
20
|
+
| 文件存储 | ✅ 开放 | 分块上传、下载、条件覆盖、软删除 |
|
|
21
|
+
| 日志 | ✅ 开放 | 查询 + 追尾;生产环境只读,发布者可见全部调用日志 |
|
|
22
|
+
| 云函数 / 后台能力 | ⚠️ 视部署而定 | 需要部署了函数运行环境;还需就绪的签名发布(带后台能力的版本上传时由平台自动签名,安装 / 切换版本 / 授权时自动绑定)并授权 |
|
|
23
23
|
| 发布候选与签名 | ✅ 自动 | 平台在上传时自动签发开发版本、首次发布时自动签发生产发布,没有手动步骤。签名需部署配置发布信任策略,未配置时发布报「发布信任策略尚未配置…」 |
|
|
24
|
-
| 生产环境数据 / 文件管理(控制台) | ⚠️ 视部署而定 | 生产开放后可在控制台查看和修改(页面有影响范围提示)
|
|
25
|
-
| 应用中心 · 更新 / 回滚 | ✅ 开放 | 有升级预检;只切换当前用户自己的安装
|
|
26
|
-
| 控制台 · 发布为线上版本 | ✅ 开放 | 控制台「发布 → 版本管理」;发布后全企业可见,无审核步骤;回退 = 把旧版本再发布一次
|
|
27
|
-
| 生产准备(首次发布带云资源的版本) | ⚠️ 视部署而定 | 平台自动完成生产环境、生产发布、生产门禁、生产库与集合;发布信任配置未就绪时没有生产入口
|
|
28
|
-
| 应用成员(管理员 / 开发者) | ✅ 开放 | 控制台「团队」→「成员」,见 [成员管理](console-members.md)
|
|
29
|
-
| 使用其他成员上传的开发版本 | ✅ 开放 | 版本可见性为 `members`;成员在「版本管理」里用「切换到此版本」切换(只影响自己)
|
|
30
|
-
| 纯后台(`headless`)应用 | ❌ 基本不可用 | 声明云函数等即「运行环境未开放」,请用 `both`
|
|
31
|
-
| `agent.interaction` | ❌ 未开放 | 声明会导致应用无法安装
|
|
32
|
-
| 集合分区 `app` | ❌ 未开放 | Manifest 写 `partition: "app"` 会被拒绝
|
|
24
|
+
| 生产环境数据 / 文件管理(控制台) | ⚠️ 视部署而定 | 生产开放后可在控制台查看和修改(页面有影响范围提示) |
|
|
25
|
+
| 应用中心 · 更新 / 回滚 | ✅ 开放 | 有升级预检;只切换当前用户自己的安装 |
|
|
26
|
+
| 控制台 · 发布为线上版本 | ✅ 开放 | 控制台「发布 → 版本管理」;发布后全企业可见,无审核步骤;回退 = 把旧版本再发布一次 |
|
|
27
|
+
| 生产准备(首次发布带云资源的版本) | ⚠️ 视部署而定 | 平台自动完成生产环境、生产发布、生产门禁、生产库与集合;发布信任配置未就绪时没有生产入口 |
|
|
28
|
+
| 应用成员(管理员 / 开发者) | ✅ 开放 | 控制台「团队」→「成员」,见 [成员管理](console-members.md) |
|
|
29
|
+
| 使用其他成员上传的开发版本 | ✅ 开放 | 版本可见性为 `members`;成员在「版本管理」里用「切换到此版本」切换(只影响自己) |
|
|
30
|
+
| 纯后台(`headless`)应用 | ❌ 基本不可用 | 声明云函数等即「运行环境未开放」,请用 `both` |
|
|
31
|
+
| `agent.interaction` | ❌ 未开放 | 声明会导致应用无法安装 |
|
|
32
|
+
| 集合分区 `app` | ❌ 未开放 | Manifest 写 `partition: "app"` 会被拒绝 |
|
|
33
33
|
|
|
34
34
|
## 未实现的 SDK 功能
|
|
35
35
|
|
|
@@ -178,11 +178,14 @@
|
|
|
178
178
|
| `jobs.wait` 轮询间隔 | 100–**5000** 毫秒,默认 500 |
|
|
179
179
|
| `callFunction` 同步等待 | 25 秒,未结束抛 `CALL_TIMEOUT`(带 `jobId`) |
|
|
180
180
|
|
|
181
|
-
### Agent(`os.agent.complete`)
|
|
181
|
+
### Agent(`os.agent.complete` / 页面 `POST /agents/complete`)
|
|
182
182
|
|
|
183
|
-
| 项
|
|
184
|
-
|
|
|
185
|
-
| 输出 `output` | ≤ **8000** 字符(服务端截断)
|
|
183
|
+
| 项 | 限制 |
|
|
184
|
+
| ------------------------- | ---------------------------------------------------- |
|
|
185
|
+
| 输出 `output` / `text` | ≤ **8000** 字符(服务端截断) |
|
|
186
|
+
| `timeout_ms` | 等待模型响应,默认 **20000** 毫秒 |
|
|
187
|
+
| 契约模式 `max_retries` | 不含首轮;建议 1–2,每次重试再调一次模型 |
|
|
188
|
+
| 页面 `api.fetch` 单次调用 | 服务端最多等 **120** 秒;SDK 不设超时,可传 `signal` |
|
|
186
189
|
|
|
187
190
|
输入长度以你在能力 `input_schema` 里声明的为准;`copilot-studio` 示例用的是 `input` ≤ 16000、`system` ≤ 4000,
|
|
188
191
|
平台侧的输入上限**未验证**。
|
|
@@ -34,15 +34,15 @@ my-todo/
|
|
|
34
34
|
|
|
35
35
|
SDK 版本与 `zleap-app` 可执行文件一起发布。用 `npm create zleap-app` 生成的 React 项目改为依赖 npm 包
|
|
36
36
|
`@zleap-ai/app-sdk`:`@zleap-ai/app-sdk` 对应 `sdk/index.js`,`/host` 对应 `sdk/zleap-sdk.js`,`/workspace` 对应 `sdk/workspace.js`,
|
|
37
|
-
API
|
|
37
|
+
API 完全相同。SDK 与 `@zleap-ai/app-cli` 各自发版,版本号不必相同:
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
npm i @zleap-ai/app-sdk
|
|
41
|
-
npm i -D @zleap-ai/app-cli
|
|
40
|
+
npm i @zleap-ai/app-sdk@latest
|
|
41
|
+
npm i -D @zleap-ai/app-cli@latest
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
**不要**再从 `zleap-web` 源码自行构建、复制 SDK——旧文档里的做法已过时,
|
|
45
|
-
|
|
45
|
+
与宿主协议不匹配的 SDK 会在握手时报 `PROTOCOL_MISMATCH`。
|
|
46
46
|
|
|
47
47
|
> **三种「存储」别混用**(提示)
|
|
48
48
|
>
|
|
@@ -145,15 +145,15 @@
|
|
|
145
145
|
|
|
146
146
|
### 数据
|
|
147
147
|
|
|
148
|
-
| 错误码 | 含义
|
|
149
|
-
| ---------------------- |
|
|
150
|
-
| `SCHEMA_INVALID` | 参数或文档不符合规则(含大小、嵌套等超限)
|
|
151
|
-
| `VERSION_CONFLICT` | 乐观锁冲突,或键已存在等 409 类错误
|
|
152
|
-
| `DOCUMENT_NOT_FOUND` | 文档不存在(或 404 类错误)
|
|
153
|
-
| `COLLECTION_NOT_FOUND` | 集合不存在
|
|
148
|
+
| 错误码 | 含义 | 处理 |
|
|
149
|
+
| ---------------------- | -------------------------------------------- | -------------------------------------------------------------------------------- |
|
|
150
|
+
| `SCHEMA_INVALID` | 参数或文档不符合规则(含大小、嵌套等超限) | 见本页末尾「排查:`SCHEMA_INVALID`」 |
|
|
151
|
+
| `VERSION_CONFLICT` | 乐观锁冲突,或键已存在等 409 类错误 | 重新读取最新版本再改 |
|
|
152
|
+
| `DOCUMENT_NOT_FOUND` | 文档不存在(或 404 类错误) | 检查文档键;读取时可用 `throwOnNotFound: false` |
|
|
153
|
+
| `COLLECTION_NOT_FOUND` | 集合不存在 | 在 Manifest `data.collections` 声明;生产环境要单独开通集合 |
|
|
154
154
|
| `SCOPE_FORBIDDEN` | 读写了 manifest **没声明**的集合(或没授权) | 先在 `data.collections` 声明再部署;已声明还报这个,看「权限」里是否勾了数据读写 |
|
|
155
|
-
| `INDEX_REQUIRED` | 查询需要扫描的行数超过单次上限
|
|
156
|
-
| `RESOURCE_NOT_READY` | 云数据库正在开通,或数据服务还没加载本应用
|
|
155
|
+
| `INDEX_REQUIRED` | 查询需要扫描的行数超过单次上限 | 给筛选字段建索引;已有索引仍超限时缩小筛选范围 |
|
|
156
|
+
| `RESOURCE_NOT_READY` | 云数据库正在开通,或数据服务还没加载本应用 | 通常一分钟内自动就绪,稍后刷新重试 |
|
|
157
157
|
|
|
158
158
|
### 文件存储
|
|
159
159
|
|
|
@@ -419,6 +419,10 @@
|
|
|
419
419
|
`zleap-app publish` 等写操作网络结果不明时返回 `COMMIT_UNKNOWN`,**不会自动重传**:
|
|
420
420
|
用 `zleap-app operation --request-id <原操作号>` 或 `zleap-app status` 查询原结果。
|
|
421
421
|
|
|
422
|
+
`zleap-app business get` / `call`(以及 MCP 的 `os_business_get` / `os_business_call`)请求超管、账号与组织、内部控制或未审核的接口时,
|
|
423
|
+
返回 403 `OPERATION_NOT_EXPOSED`。应用页面直接调用 OS 接口时,未向应用开放的接口同样返回 `OPERATION_NOT_EXPOSED`(「该接口未向应用开放」)。
|
|
424
|
+
这类接口不会开放,先用 `business search` 找可用的替代接口。
|
|
425
|
+
|
|
422
426
|
## 上传与 Manifest 校验
|
|
423
427
|
|
|
424
428
|
上传、`zleap-app check` 或安装时,Manifest 相关失败通过 `detail.reason` 返回:
|
|
@@ -524,18 +528,18 @@
|
|
|
524
528
|
平台在上传带后台能力的版本时自动签发开发发布,首次发布为线上版本时自动签发生产发布。
|
|
525
529
|
签发或验签失败时按原因码归类返回:
|
|
526
530
|
|
|
527
|
-
| 服务端原因码 | 含义
|
|
528
|
-
| --------------------------------------------------------------------------------- |
|
|
529
|
-
| `RELEASE_PROOF_TOO_LARGE` | 证明文件超过 2 MiB 限制
|
|
530
|
-
| `RELEASE_PROOF_INVALID` | 证明 JSON 或签名信封无效
|
|
531
|
-
| `RELEASE_IDENTITY_MISMATCH`、`RELEASE_DIGEST_MISMATCH`、`RELEASE_SOURCE_MISMATCH` | 证明与当前应用、发布、包、来源、摘要或策略不匹配
|
|
532
|
-
| `RELEASE_SIGNATURE_INVALID` | 无法验证发布签名
|
|
533
|
-
| `RELEASE_ARTIFACT_MISMATCH`、`RELEASE_MANIFEST_MISMATCH` | 签名 Manifest 或包制品清单不匹配
|
|
534
|
-
| `RELEASE_POLICY_STALE` | 策略审核已过期或不是当前策略
|
|
535
|
-
| `RELEASE_POLICY_UNAVAILABLE` | 尚未配置发布策略,无法验证
|
|
536
|
-
| `RELEASE_TRUST_UNAVAILABLE` | 发布信任策略尚未配置,或发布者密钥不可用、已撤销或已过期;由管理员检查
|
|
537
|
-
| `RELEASE_DISABLED` | 发布候选已撤回
|
|
538
|
-
| 其他 | 签名验证请求被拒绝
|
|
531
|
+
| 服务端原因码 | 含义 |
|
|
532
|
+
| --------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
|
|
533
|
+
| `RELEASE_PROOF_TOO_LARGE` | 证明文件超过 2 MiB 限制 |
|
|
534
|
+
| `RELEASE_PROOF_INVALID` | 证明 JSON 或签名信封无效 |
|
|
535
|
+
| `RELEASE_IDENTITY_MISMATCH`、`RELEASE_DIGEST_MISMATCH`、`RELEASE_SOURCE_MISMATCH` | 证明与当前应用、发布、包、来源、摘要或策略不匹配 |
|
|
536
|
+
| `RELEASE_SIGNATURE_INVALID` | 无法验证发布签名 |
|
|
537
|
+
| `RELEASE_ARTIFACT_MISMATCH`、`RELEASE_MANIFEST_MISMATCH` | 签名 Manifest 或包制品清单不匹配 |
|
|
538
|
+
| `RELEASE_POLICY_STALE` | 策略审核已过期或不是当前策略 |
|
|
539
|
+
| `RELEASE_POLICY_UNAVAILABLE` | 尚未配置发布策略,无法验证 |
|
|
540
|
+
| `RELEASE_TRUST_UNAVAILABLE` | 发布信任策略尚未配置,或发布者密钥不可用、已撤销或已过期;由管理员检查 |
|
|
541
|
+
| `RELEASE_DISABLED` | 发布候选已撤回 |
|
|
542
|
+
| 其他 | 签名验证请求被拒绝 |
|
|
539
543
|
|
|
540
544
|
签发时的 `RELEASE_POLICY_UNAVAILABLE`、`RELEASE_TRUST_UNAVAILABLE` 属于**部署未配置**;
|
|
541
545
|
发布生产版本时的 `RELEASE_TRUST_UNAVAILABLE` 则表示这个发布的签名与当前信任策略对不上,重新发布即可(见 [发布与生产门禁](#发布与生产门禁))。
|