mirror of
https://git.openapi.site/https://github.com/desirecore/registry.git
synced 2026-10-21 22:33:23 +08:00
feat: 增加固定工作台应用目录兼容格式
This commit is contained in:
18
README.md
18
README.md
@@ -9,7 +9,7 @@ DesireCore 客户端启动时会克隆此仓库,并定期同步更新。用户
|
||||
```text
|
||||
.
|
||||
├── README.md # 本文件
|
||||
├── SCHEMA_VERSION # 数据格式版本号(当前 4.2.0)
|
||||
├── SCHEMA_VERSION # 数据格式版本号(4.3.0兼容候选,尚未开放新工作台条目)
|
||||
├── manifest.json # 仓库元数据(版本、统计、维护者)
|
||||
├── package.json # Registry 校验入口
|
||||
├── schemas/ # legacy entry、仓库 manifest 与 catalog sidecar Schema
|
||||
@@ -68,7 +68,19 @@ Catalog sidecar 的 App spec 同步 `productKind`、可选 `extension` 和入口
|
||||
|
||||
English: Plugins are App products with host contributions, sharing the canonical catalog and installation identity. Deployment type does not decide product kind. Applications may provide contributions or consume direct dependencies; a plugin must provide a real contribution. Artifact packages contain declarative materials without ports or processes. Dependency product references do not grant installation, activation or permission. Existing listings retain their source and compliance evidence; schema migration does not make listing-only content installable or publish a client release.
|
||||
|
||||
## 条目格式
|
||||
## 固定工作台应用(4.3兼容候选)
|
||||
|
||||
4.3保留全部4.2旧分支,新增 `web-bundle` 与带 `appRelease` 的 `native-app`/`docker-app` 严格分支。网页包不冒充无执行声明artifact;宿主/容器服务应用使用 `install.method: release`,运行知识与依赖在固定清单中,不限定Node技术栈,也不自动执行说明文字。Docker显式声明固定imageRefs,每项是repository@sha256,复用原容器/执行/服务owner;没有真实身份adapter时保留Agent管理,不因为Schema合法声称直开。新分支声明来源无关releaseDigest、包内manifestPath/definitionPath与实际能力要求,source.sha256仍为下载归档原始字节摘要;descriptor在安装时按来源物化并单独核验。
|
||||
|
||||
正式目录未新增工作台条目。`fixtures/authoring/`只有合成的listing-only契约样本,example.test地址不是可获取的应用发行物,不表示上传、审核、运行或第二用户安装已完成。主仓内置seed保持旧受审提交;此候选先经外仓与客户端reader分别review,只有客户端真实app-authoring-v1/web-bundle等能力发行后才能开放新entries。最低版本字符串不能代替握手,旧客户端继续读取旧条目并忽略未知类型,不允许改交付类型绕过。
|
||||
|
||||
作者从同一固定导出生成manifest.json、catalog-metadata.v1.json、install.md三文件,产品ID/版本/入口保持一致。证据不足保持listing-only;installable需真实来源、许可和审核记录。用户实例/数据/平台secret/开发机路径不写目录。目录PR、来源上传、准入和安装各自核验;同版本异内容创建新修订,目录更新不热替换已安装快照,撤回不自动卸载或删除数据。
|
||||
|
||||
GitHub/Gitea私有来源依真实ACL和连接;客户端private来源不走公开镜像回退,认证失败拒绝新获取。团队移除不自动改来源ACL,需实际源管理员处理;已下载字节不能收回。离线已安装应用按保存事实维护,不把目录缓存当新安装的完整运行环境。
|
||||
|
||||
English: Version 4.3 is a compatibility candidate, preserving legacy branches and adding fixed web-bundle and authored native release contracts. Synthetic listing-only fixtures are not published artifacts. Official entries wait for reviewed Schema/client support and actual capability release. Archive bytes, portable release identity and source-materialized descriptor identity are distinct. Private-source access depends on actual Git ACL; team membership does not automatically revoke source access, and catalog withdrawal does not delete installed data.
|
||||
|
||||
## 条目格式(兼容字段)
|
||||
|
||||
每个条目是 `entries/<id>/` 下的一个目录,目录名即条目 ID。
|
||||
|
||||
@@ -82,7 +94,7 @@ English: Plugins are App products with host contributions, sharing the canonical
|
||||
|------|------|------|------|
|
||||
| `id` | string | ✅ | 唯一标识,与目录名一致 |
|
||||
| `name` | string | ✅ | 显示名称 |
|
||||
| `type` | string | ✅ | 条目类型:`native-app` / `docker-app` / `artifact` / `mcp` / `http-api` / `external-integration` |
|
||||
| `type` | string | ✅ | 条目类型:`native-app` / `docker-app` / `artifact` / `web-bundle` / `mcp` / `http-api` / `external-integration` |
|
||||
| `version` | string | ✅ | 上游原始版本字符串;可为 SemVer、CalVer 或不透明版本 |
|
||||
| `description` | string | ✅ | 一行功能摘要 |
|
||||
| `author` | string | | 作者或组织 |
|
||||
|
||||
@@ -1 +1 @@
|
||||
4.2.0
|
||||
4.3.0
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"$schema": "../../schemas/catalog-metadata.v1.schema.json",
|
||||
"schemaVersion": 1,
|
||||
"identity": {
|
||||
"kind": "app",
|
||||
"id": "docker-workbench-example"
|
||||
},
|
||||
"presentation": {
|
||||
"defaultLocale": "zh-CN",
|
||||
"i18n": {
|
||||
"zh-CN": {
|
||||
"name": "容器工作台示例",
|
||||
"summary": "仅用于契约核验的合成工作台材料。",
|
||||
"description": "仅用于契约核验的合成工作台材料。"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"workbench",
|
||||
"example"
|
||||
]
|
||||
},
|
||||
"release": {
|
||||
"state": "known",
|
||||
"version": "1.0.0",
|
||||
"versionScheme": "semver"
|
||||
},
|
||||
"timestamps": {
|
||||
"catalogUpdatedAt": {
|
||||
"state": "unknown"
|
||||
},
|
||||
"releasePublishedAt": {
|
||||
"state": "unknown"
|
||||
},
|
||||
"reviewedAt": {
|
||||
"state": "unknown"
|
||||
},
|
||||
"upstreamObservedAt": {
|
||||
"state": "unknown"
|
||||
}
|
||||
},
|
||||
"provenance": {
|
||||
"content": {
|
||||
"kind": "release",
|
||||
"url": "https://git.example.test/examples/releases/download/1.0.0/workbench.zip",
|
||||
"ref": "1111111111111111111111111111111111111111",
|
||||
"sha256": "ed9b876d3162d09d5d7264cf552079d44e8a17d14665a00fb97bffb94c121284"
|
||||
}
|
||||
},
|
||||
"governance": {
|
||||
"stewardship": "community",
|
||||
"availability": "listing-only",
|
||||
"license": {
|
||||
"state": "known",
|
||||
"value": "MIT"
|
||||
},
|
||||
"redistribution": "verify-package-terms"
|
||||
},
|
||||
"compatibility": {
|
||||
"platforms": {
|
||||
"state": "known",
|
||||
"values": [
|
||||
"macos",
|
||||
"windows",
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"requiredClientVersion": "10.0.180"
|
||||
},
|
||||
"spec": {
|
||||
"kind": "app",
|
||||
"category": "tools",
|
||||
"productKind": "application",
|
||||
"entrypointKinds": [
|
||||
"embedded-web"
|
||||
],
|
||||
"placementPolicy": {
|
||||
"showInMyApps": true,
|
||||
"allowNavPin": true,
|
||||
"allowAgentBinding": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
# 容器工作台示例 1.0.0
|
||||
|
||||
目录材料对应固定应用release,上传、目录审阅和实例安装分别核验。
|
||||
|
||||
- 来源:https://git.example.test/examples/releases/download/1.0.0/workbench.zip
|
||||
- 固定来源ref:1111111111111111111111111111111111111111
|
||||
- 归档响应体SHA-256:ed9b876d3162d09d5d7264cf552079d44e8a17d14665a00fb97bffb94c121284
|
||||
- releaseDigest:ebe36976b3bf3f3af1eb833162fe61a1509bdbdb373000da1e2c067dada17540
|
||||
- 作者定义:application.json
|
||||
- 所需能力:app-authoring-v1, release, runtime-binding
|
||||
|
||||
先读取应用安装管理技能 app-install-manager,解析当前请求、精确目标与占用。实际能力握手必须满足以上要求,版本号不能代替握手。
|
||||
|
||||
使用用户已授权的Git/HTTP工具取得此固定材料,核对归档原始字节摘要;安全解包后核读app-release.json和全部文件摘要、作者定义与版本。releaseDigest按app-release-c14n-v1计算,不能换成归档摘要。
|
||||
|
||||
通过已有release prepare接口取得owned固定候选与来源物化descriptor,分别核验descriptor原始字节;沿唯一安装writer提交CAS,登记成功后再检查启动和功能,不能把材料存在或端口响应说成已完成安装。
|
||||
|
||||
运行和依赖说明位于runtime/说明.md;Agent选择实际执行方式并走已有执行owner。网页直接打开须取得真实运行身份与代际绑定,不能用旧PID/端口/核验记录代替。
|
||||
|
||||
固定镜像依赖为registry.example.test/example/app@sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd;镜像许可、真实Docker环境和来源鉴权分别核验。使用原容器/执行/服务owner,缺可信身份adapter时保持Agent管理,不能因为Schema通过声称容器已运行。
|
||||
|
||||
此release未声明平台业务文件合同;安装说明不授予宿主文件或共享数据库权限。持久数据和备份要求须在实际应用中核读。
|
||||
|
||||
同版本异内容必须创建新修订。目录更新只产生候选,不能改写已安装快照。目录撤回阻止新获取,不自动卸载或删除本机数据;离线只能继续使用已经核验且本地依赖完整的版本。
|
||||
|
||||
私有来源由真实Git/Gitea ACL和连接鉴权;凭据不写入本目录、归档或URL。团队成员移除不会自动撤销来源端访问,需源管理员处理;已下载字节不能收回。
|
||||
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"id": "docker-workbench-example",
|
||||
"name": "容器工作台示例",
|
||||
"type": "docker-app",
|
||||
"productKind": "application",
|
||||
"version": "1.0.0",
|
||||
"author": "Example Author",
|
||||
"description": "仅用于契约核验的合成工作台材料。",
|
||||
"tags": [
|
||||
"workbench",
|
||||
"example"
|
||||
],
|
||||
"icon": "AppWindow",
|
||||
"iconLetter": "容",
|
||||
"platformSupport": [
|
||||
"macos",
|
||||
"windows",
|
||||
"linux"
|
||||
],
|
||||
"category": "tools",
|
||||
"shortDesc": "仅用于契约核验的合成工作台材料。",
|
||||
"fullDesc": "仅用于契约核验的合成工作台材料。",
|
||||
"install": {
|
||||
"method": "release",
|
||||
"requirements": {
|
||||
"docker": true,
|
||||
"ports": []
|
||||
}
|
||||
},
|
||||
"appRelease": {
|
||||
"schemaVersion": 1,
|
||||
"releaseDigest": "ebe36976b3bf3f3af1eb833162fe61a1509bdbdb373000da1e2c067dada17540",
|
||||
"manifestPath": "app-release.json",
|
||||
"definitionPath": "application.json",
|
||||
"requiredCapabilities": [
|
||||
"app-authoring-v1",
|
||||
"release",
|
||||
"runtime-binding"
|
||||
],
|
||||
"imageRefs": [
|
||||
"registry.example.test/example/app@sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
|
||||
]
|
||||
},
|
||||
"source": {
|
||||
"kind": "release",
|
||||
"url": "https://git.example.test/examples/releases/download/1.0.0/workbench.zip",
|
||||
"ref": "1111111111111111111111111111111111111111",
|
||||
"sha256": "ed9b876d3162d09d5d7264cf552079d44e8a17d14665a00fb97bffb94c121284"
|
||||
},
|
||||
"requiredClientVersion": "10.0.180",
|
||||
"entrypoints": [
|
||||
{
|
||||
"id": "main",
|
||||
"name": "工作台",
|
||||
"kind": "embedded-web",
|
||||
"serviceRef": "workbench:main"
|
||||
}
|
||||
],
|
||||
"placementPolicy": {
|
||||
"showInMyApps": true,
|
||||
"allowNavPin": true,
|
||||
"allowAgentBinding": true
|
||||
},
|
||||
"stewardship": "community",
|
||||
"availability": "listing-only",
|
||||
"license": "MIT",
|
||||
"redistribution": "verify-package-terms"
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"$schema": "../../schemas/catalog-metadata.v1.schema.json",
|
||||
"schemaVersion": 1,
|
||||
"identity": {
|
||||
"kind": "app",
|
||||
"id": "service-workbench-example"
|
||||
},
|
||||
"presentation": {
|
||||
"defaultLocale": "zh-CN",
|
||||
"i18n": {
|
||||
"zh-CN": {
|
||||
"name": "服务工作台示例",
|
||||
"summary": "仅用于契约核验的合成工作台材料。",
|
||||
"description": "仅用于契约核验的合成工作台材料。"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"workbench",
|
||||
"example"
|
||||
]
|
||||
},
|
||||
"release": {
|
||||
"state": "known",
|
||||
"version": "1.0.0",
|
||||
"versionScheme": "semver"
|
||||
},
|
||||
"timestamps": {
|
||||
"catalogUpdatedAt": {
|
||||
"state": "unknown"
|
||||
},
|
||||
"releasePublishedAt": {
|
||||
"state": "unknown"
|
||||
},
|
||||
"reviewedAt": {
|
||||
"state": "unknown"
|
||||
},
|
||||
"upstreamObservedAt": {
|
||||
"state": "unknown"
|
||||
}
|
||||
},
|
||||
"provenance": {
|
||||
"content": {
|
||||
"kind": "release",
|
||||
"url": "https://git.example.test/examples/releases/download/1.0.0/workbench.zip",
|
||||
"ref": "1111111111111111111111111111111111111111",
|
||||
"sha256": "ed9b876d3162d09d5d7264cf552079d44e8a17d14665a00fb97bffb94c121284"
|
||||
}
|
||||
},
|
||||
"governance": {
|
||||
"stewardship": "community",
|
||||
"availability": "listing-only",
|
||||
"license": {
|
||||
"state": "known",
|
||||
"value": "MIT"
|
||||
},
|
||||
"redistribution": "verify-package-terms"
|
||||
},
|
||||
"compatibility": {
|
||||
"platforms": {
|
||||
"state": "known",
|
||||
"values": [
|
||||
"macos",
|
||||
"windows",
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"requiredClientVersion": "10.0.180"
|
||||
},
|
||||
"spec": {
|
||||
"kind": "app",
|
||||
"category": "tools",
|
||||
"productKind": "application",
|
||||
"entrypointKinds": [
|
||||
"embedded-web"
|
||||
],
|
||||
"placementPolicy": {
|
||||
"showInMyApps": true,
|
||||
"allowNavPin": true,
|
||||
"allowAgentBinding": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
# 服务工作台示例 1.0.0
|
||||
|
||||
目录材料对应固定应用release,上传、目录审阅和实例安装分别核验。
|
||||
|
||||
- 来源:https://git.example.test/examples/releases/download/1.0.0/workbench.zip
|
||||
- 固定来源ref:1111111111111111111111111111111111111111
|
||||
- 归档响应体SHA-256:ed9b876d3162d09d5d7264cf552079d44e8a17d14665a00fb97bffb94c121284
|
||||
- releaseDigest:73393f784bede68657be54fdfcbb9dcb083a5a15f8e272d01dce453152cc966f
|
||||
- 作者定义:application.json
|
||||
- 所需能力:app-authoring-v1, release, runtime-binding
|
||||
|
||||
先读取应用安装管理技能 app-install-manager,解析当前请求、精确目标与占用。实际能力握手必须满足以上要求,版本号不能代替握手。
|
||||
|
||||
使用用户已授权的Git/HTTP工具取得此固定材料,核对归档原始字节摘要;安全解包后核读app-release.json和全部文件摘要、作者定义与版本。releaseDigest按app-release-c14n-v1计算,不能换成归档摘要。
|
||||
|
||||
通过已有release prepare接口取得owned固定候选与来源物化descriptor,分别核验descriptor原始字节;沿唯一安装writer提交CAS,登记成功后再检查启动和功能,不能把材料存在或端口响应说成已完成安装。
|
||||
|
||||
运行和依赖说明位于runtime/说明.md;Agent选择实际执行方式并走已有执行owner。网页直接打开须取得真实运行身份与代际绑定,不能用旧PID/端口/核验记录代替。
|
||||
|
||||
此release未声明平台业务文件合同;安装说明不授予宿主文件或共享数据库权限。持久数据和备份要求须在实际应用中核读。
|
||||
|
||||
同版本异内容必须创建新修订。目录更新只产生候选,不能改写已安装快照。目录撤回阻止新获取,不自动卸载或删除本机数据;离线只能继续使用已经核验且本地依赖完整的版本。
|
||||
|
||||
私有来源由真实Git/Gitea ACL和连接鉴权;凭据不写入本目录、归档或URL。团队成员移除不会自动撤销来源端访问,需源管理员处理;已下载字节不能收回。
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"id": "service-workbench-example",
|
||||
"name": "服务工作台示例",
|
||||
"type": "native-app",
|
||||
"productKind": "application",
|
||||
"version": "1.0.0",
|
||||
"author": "Example Author",
|
||||
"description": "仅用于契约核验的合成工作台材料。",
|
||||
"tags": [
|
||||
"workbench",
|
||||
"example"
|
||||
],
|
||||
"icon": "AppWindow",
|
||||
"iconLetter": "服",
|
||||
"platformSupport": [
|
||||
"macos",
|
||||
"windows",
|
||||
"linux"
|
||||
],
|
||||
"category": "tools",
|
||||
"shortDesc": "仅用于契约核验的合成工作台材料。",
|
||||
"fullDesc": "仅用于契约核验的合成工作台材料。",
|
||||
"install": {
|
||||
"method": "release",
|
||||
"requirements": {
|
||||
"docker": false,
|
||||
"ports": []
|
||||
}
|
||||
},
|
||||
"appRelease": {
|
||||
"schemaVersion": 1,
|
||||
"releaseDigest": "73393f784bede68657be54fdfcbb9dcb083a5a15f8e272d01dce453152cc966f",
|
||||
"manifestPath": "app-release.json",
|
||||
"definitionPath": "application.json",
|
||||
"requiredCapabilities": [
|
||||
"app-authoring-v1",
|
||||
"release",
|
||||
"runtime-binding"
|
||||
]
|
||||
},
|
||||
"source": {
|
||||
"kind": "release",
|
||||
"url": "https://git.example.test/examples/releases/download/1.0.0/workbench.zip",
|
||||
"ref": "1111111111111111111111111111111111111111",
|
||||
"sha256": "ed9b876d3162d09d5d7264cf552079d44e8a17d14665a00fb97bffb94c121284"
|
||||
},
|
||||
"requiredClientVersion": "10.0.180",
|
||||
"entrypoints": [
|
||||
{
|
||||
"id": "main",
|
||||
"name": "工作台",
|
||||
"kind": "embedded-web",
|
||||
"serviceRef": "workbench:main"
|
||||
}
|
||||
],
|
||||
"placementPolicy": {
|
||||
"showInMyApps": true,
|
||||
"allowNavPin": true,
|
||||
"allowAgentBinding": true
|
||||
},
|
||||
"stewardship": "community",
|
||||
"availability": "listing-only",
|
||||
"license": "MIT",
|
||||
"redistribution": "verify-package-terms"
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"$schema": "../../schemas/catalog-metadata.v1.schema.json",
|
||||
"schemaVersion": 1,
|
||||
"identity": {
|
||||
"kind": "app",
|
||||
"id": "static-workbench-example"
|
||||
},
|
||||
"presentation": {
|
||||
"defaultLocale": "zh-CN",
|
||||
"i18n": {
|
||||
"zh-CN": {
|
||||
"name": "静态工作台示例",
|
||||
"summary": "仅用于契约核验的合成工作台材料。",
|
||||
"description": "仅用于契约核验的合成工作台材料。"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"workbench",
|
||||
"example"
|
||||
]
|
||||
},
|
||||
"release": {
|
||||
"state": "known",
|
||||
"version": "1.0.0",
|
||||
"versionScheme": "semver"
|
||||
},
|
||||
"timestamps": {
|
||||
"catalogUpdatedAt": {
|
||||
"state": "unknown"
|
||||
},
|
||||
"releasePublishedAt": {
|
||||
"state": "unknown"
|
||||
},
|
||||
"reviewedAt": {
|
||||
"state": "unknown"
|
||||
},
|
||||
"upstreamObservedAt": {
|
||||
"state": "unknown"
|
||||
}
|
||||
},
|
||||
"provenance": {
|
||||
"content": {
|
||||
"kind": "release",
|
||||
"url": "https://git.example.test/examples/releases/download/1.0.0/workbench.zip",
|
||||
"ref": "1111111111111111111111111111111111111111",
|
||||
"sha256": "ed9b876d3162d09d5d7264cf552079d44e8a17d14665a00fb97bffb94c121284"
|
||||
}
|
||||
},
|
||||
"governance": {
|
||||
"stewardship": "community",
|
||||
"availability": "listing-only",
|
||||
"license": {
|
||||
"state": "known",
|
||||
"value": "MIT"
|
||||
},
|
||||
"redistribution": "verify-package-terms"
|
||||
},
|
||||
"compatibility": {
|
||||
"platforms": {
|
||||
"state": "known",
|
||||
"values": [
|
||||
"macos",
|
||||
"windows",
|
||||
"linux",
|
||||
"web"
|
||||
]
|
||||
},
|
||||
"requiredClientVersion": "10.0.180"
|
||||
},
|
||||
"spec": {
|
||||
"kind": "app",
|
||||
"category": "tools",
|
||||
"productKind": "application",
|
||||
"entrypointKinds": [
|
||||
"embedded-web"
|
||||
],
|
||||
"placementPolicy": {
|
||||
"showInMyApps": true,
|
||||
"allowNavPin": true,
|
||||
"allowAgentBinding": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
# 静态工作台示例 1.0.0
|
||||
|
||||
目录材料对应固定应用release,上传、目录审阅和实例安装分别核验。
|
||||
|
||||
- 来源:https://git.example.test/examples/releases/download/1.0.0/workbench.zip
|
||||
- 固定来源ref:1111111111111111111111111111111111111111
|
||||
- 归档响应体SHA-256:ed9b876d3162d09d5d7264cf552079d44e8a17d14665a00fb97bffb94c121284
|
||||
- releaseDigest:fe8290e42731a0132753b0c99bc0c08c82e039845ac7a489e3ee8c1e36515044
|
||||
- 作者定义:application.json
|
||||
- 所需能力:app-authoring-v1, release, web-bundle
|
||||
|
||||
先读取应用安装管理技能 app-install-manager,解析当前请求、精确目标与占用。实际能力握手必须满足以上要求,版本号不能代替握手。
|
||||
|
||||
使用用户已授权的Git/HTTP工具取得此固定材料,核对归档原始字节摘要;安全解包后核读app-release.json和全部文件摘要、作者定义与版本。releaseDigest按app-release-c14n-v1计算,不能换成归档摘要。
|
||||
|
||||
通过已有release prepare接口取得owned固定候选与来源物化descriptor,分别核验descriptor原始字节;沿唯一安装writer提交CAS,登记成功后再检查启动和功能,不能把材料存在或端口响应说成已完成安装。
|
||||
|
||||
静态文件只能沿release.delivery.entrypoints映射承载,不运行构建/宿主程序,不注册虚假的全局API服务。
|
||||
|
||||
此release未声明平台业务文件合同;安装说明不授予宿主文件或共享数据库权限。持久数据和备份要求须在实际应用中核读。
|
||||
|
||||
同版本异内容必须创建新修订。目录更新只产生候选,不能改写已安装快照。目录撤回阻止新获取,不自动卸载或删除本机数据;离线只能继续使用已经核验且本地依赖完整的版本。
|
||||
|
||||
私有来源由真实Git/Gitea ACL和连接鉴权;凭据不写入本目录、归档或URL。团队成员移除不会自动撤销来源端访问,需源管理员处理;已下载字节不能收回。
|
||||
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"id": "static-workbench-example",
|
||||
"name": "静态工作台示例",
|
||||
"type": "web-bundle",
|
||||
"productKind": "application",
|
||||
"version": "1.0.0",
|
||||
"author": "Example Author",
|
||||
"description": "仅用于契约核验的合成工作台材料。",
|
||||
"tags": [
|
||||
"workbench",
|
||||
"example"
|
||||
],
|
||||
"icon": "AppWindow",
|
||||
"iconLetter": "静",
|
||||
"platformSupport": [
|
||||
"macos",
|
||||
"windows",
|
||||
"linux",
|
||||
"web"
|
||||
],
|
||||
"category": "tools",
|
||||
"shortDesc": "仅用于契约核验的合成工作台材料。",
|
||||
"fullDesc": "仅用于契约核验的合成工作台材料。",
|
||||
"install": {
|
||||
"method": "web-bundle",
|
||||
"requirements": {
|
||||
"docker": false,
|
||||
"ports": []
|
||||
}
|
||||
},
|
||||
"appRelease": {
|
||||
"schemaVersion": 1,
|
||||
"releaseDigest": "fe8290e42731a0132753b0c99bc0c08c82e039845ac7a489e3ee8c1e36515044",
|
||||
"manifestPath": "app-release.json",
|
||||
"definitionPath": "application.json",
|
||||
"requiredCapabilities": [
|
||||
"app-authoring-v1",
|
||||
"release",
|
||||
"web-bundle"
|
||||
]
|
||||
},
|
||||
"source": {
|
||||
"kind": "release",
|
||||
"url": "https://git.example.test/examples/releases/download/1.0.0/workbench.zip",
|
||||
"ref": "1111111111111111111111111111111111111111",
|
||||
"sha256": "ed9b876d3162d09d5d7264cf552079d44e8a17d14665a00fb97bffb94c121284"
|
||||
},
|
||||
"requiredClientVersion": "10.0.180",
|
||||
"entrypoints": [
|
||||
{
|
||||
"id": "main",
|
||||
"name": "工作台",
|
||||
"kind": "embedded-web",
|
||||
"serviceRef": "workbench:main"
|
||||
}
|
||||
],
|
||||
"placementPolicy": {
|
||||
"showInMyApps": true,
|
||||
"allowNavPin": true,
|
||||
"allowAgentBinding": true
|
||||
},
|
||||
"stewardship": "community",
|
||||
"availability": "listing-only",
|
||||
"license": "MIT",
|
||||
"redistribution": "verify-package-terms"
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"id": "desirecore-registry-manifest",
|
||||
"version": "4.2.0",
|
||||
"version": "4.3.0",
|
||||
"name": "DesireCore Registry",
|
||||
"description": "DesireCore 官方注册表 — 应用、插件、MCP 服务、HTTP API 和第三方外部集成的统一目录",
|
||||
"maintainer": "DesireCore Team",
|
||||
"repository": "https://github.com/desirecore/registry",
|
||||
"lastUpdated": "2026-10-11",
|
||||
"lastUpdated": "2026-10-13",
|
||||
"stats": {
|
||||
"totalEntries": 24,
|
||||
"dockerApps": 8,
|
||||
@@ -15,7 +15,7 @@
|
||||
"httpApis": 5,
|
||||
"externalIntegrations": 1
|
||||
},
|
||||
"dataVersion": "4.2.0",
|
||||
"dataVersion": "4.3.0",
|
||||
"catalogMetadata": {
|
||||
"version": "1.0.0",
|
||||
"schema": "schemas/catalog-metadata.v1.schema.json",
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@desirecore/registry",
|
||||
"version": "4.2.0",
|
||||
"version": "4.3.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@desirecore/registry",
|
||||
"version": "4.2.0",
|
||||
"version": "4.3.0",
|
||||
"devDependencies": {
|
||||
"ajv": "8.20.0"
|
||||
},
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@desirecore/registry",
|
||||
"version": "4.2.0",
|
||||
"version": "4.3.0",
|
||||
"private": true,
|
||||
"description": "Validated catalog data for the DesireCore Registry",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"validate": "node scripts/validate-registry.mjs",
|
||||
"test": "node --test scripts/validate-registry.test.mjs scripts/native-app.test.mjs scripts/plugin-contract.test.mjs && npm run validate"
|
||||
"test": "node --test scripts/validate-registry.test.mjs scripts/native-app.test.mjs scripts/plugin-contract.test.mjs scripts/application-authoring.test.mjs && npm run validate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ajv": "8.20.0"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,7 @@
|
||||
"dockerApps": { "type": "integer", "minimum": 0 },
|
||||
"nativeApps": { "type": "integer", "minimum": 0 },
|
||||
"artifactApps": { "type": "integer", "minimum": 0, "description": "纯声明制品的 App 条目数,包括插件;不是安装数。" },
|
||||
"webBundles": { "type": "integer", "minimum": 0, "description": "固定网页包App条目数;不是安装数或验证成功数量。" },
|
||||
"mcpServices": { "type": "integer", "minimum": 0 },
|
||||
"httpApis": { "type": "integer", "minimum": 0 },
|
||||
"externalIntegrations": { "type": "integer", "minimum": 0 }
|
||||
|
||||
68
scripts/application-authoring.test.mjs
Normal file
68
scripts/application-authoring.test.mjs
Normal file
@@ -0,0 +1,68 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync, cpSync, mkdtempSync, writeFileSync, rmSync } from 'node:fs'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { test, afterEach } from 'node:test'
|
||||
import Ajv from 'ajv'
|
||||
import { validateRegistry } from './catalog/validator.mjs'
|
||||
import { validateApplicationReleaseContract } from './catalog/application-release-contract.mjs'
|
||||
|
||||
const source = join(dirname(fileURLToPath(import.meta.url)),'..')
|
||||
const temporary = []
|
||||
afterEach(() => { for (const root of temporary.splice(0)) rmSync(root,{recursive:true,force:true}) })
|
||||
const schema = JSON.parse(readFileSync(join(source,'schemas/registry-entry.schema.json'),'utf8'))
|
||||
const validate = new Ajv({allErrors:true,strict:true,strictTypes:false,strictRequired:false,validateFormats:false}).compile(schema)
|
||||
const load = id => JSON.parse(readFileSync(join(source,'fixtures/authoring/entries',id,'manifest.json'),'utf8'))
|
||||
|
||||
test('4.3只新增兼容与合成fixtures,正式目录仍无web-bundle或appRelease条目',() => {
|
||||
const root = JSON.parse(readFileSync(join(source,'manifest.json'),'utf8'))
|
||||
assert.equal(root.version,'4.3.0')
|
||||
assert.equal(root.stats.totalEntries,24)
|
||||
assert.equal(root.stats.webBundles,undefined)
|
||||
})
|
||||
test('静态与带服务合成目录三文件保持App语义,两个validator同样通过',() => {
|
||||
const root = mkdtempSync(join(tmpdir(),'registry-authoring-'));temporary.push(root)
|
||||
cpSync(join(source,'schemas'),join(root,'schemas'),{recursive:true})
|
||||
cpSync(join(source,'fixtures/authoring/entries'),join(root,'entries'),{recursive:true})
|
||||
const manifest = JSON.parse(readFileSync(join(source,'manifest.json'),'utf8'))
|
||||
manifest.stats={totalEntries:3,dockerApps:1,nativeApps:1,webBundles:1,mcpServices:0,httpApis:0,externalIntegrations:0}
|
||||
writeFileSync(join(root,'manifest.json'),JSON.stringify(manifest));writeFileSync(join(root,'SCHEMA_VERSION'),'4.3.0\n')
|
||||
const report = validateRegistry(root)
|
||||
assert.equal(report.ok,true,JSON.stringify(report.errors))
|
||||
assert.equal(report.counts.webBundles,1)
|
||||
for (const id of ['static-workbench-example','service-workbench-example','docker-workbench-example']) {
|
||||
const item=load(id);assert.equal(validate(item),true,JSON.stringify(validate.errors))
|
||||
assert.deepEqual(validateApplicationReleaseContract(item),[])
|
||||
assert.equal(item.sourceId,undefined)
|
||||
assert.notEqual(item.appRelease.releaseDigest,item.source.sha256)
|
||||
assert.equal(item.availability,'listing-only')
|
||||
}
|
||||
})
|
||||
test('不能把完整HTML伪装成artifact、native-node或内部MCP服务',() => {
|
||||
const item=load('static-workbench-example')
|
||||
for (const change of [copy=>{copy.type='artifact'},copy=>{copy.install.method='native-node'},copy=>{copy.connection={transport:'stdio',command:'node'}},copy=>{copy.appRelease.materialRoot='/private'}]) {
|
||||
const copy=structuredClone(item);change(copy);assert.equal(validate(copy),false)
|
||||
}
|
||||
})
|
||||
test('未知能力、可变ref和私有凭据不成为正式分发事实',() => {
|
||||
const item=load('static-workbench-example')
|
||||
for (const change of [copy=>{copy.source.ref='latest'},copy=>{copy.source.url='https://credential@git.example.test/private.zip'},copy=>{copy.appRelease.requiredCapabilities=['app-authoring-v1']}]) {
|
||||
const copy=structuredClone(item);change(copy);assert.ok(validateApplicationReleaseContract(copy).length>0)
|
||||
}
|
||||
})
|
||||
test('保留既有native-node条目,新native release字段不污染旧分支',() => {
|
||||
const old=JSON.parse(readFileSync(join(source,'entries/desirecore-control/manifest.json'),'utf8'))
|
||||
assert.equal(validate(old),true,JSON.stringify(validate.errors))
|
||||
old.appRelease=load('service-workbench-example').appRelease
|
||||
assert.equal(validate(old),false)
|
||||
})
|
||||
test('Docker发布固定真实镜像内容引用,不把浮动tag或错误端口当成固定材料',() => {
|
||||
const item=load('docker-workbench-example')
|
||||
assert.equal(validate(item),true,JSON.stringify(validate.errors))
|
||||
assert.equal(item.install.method,'release');assert.equal(item.install.requirements.docker,true)
|
||||
for (const image of ['node:latest','registry.example.test/example/app:1.0.0','localhost:70000/app@sha256:'+'d'.repeat(64)]) {
|
||||
const copy=structuredClone(item);copy.appRelease.imageRefs=[image]
|
||||
assert.ok(!validate(copy) || validateApplicationReleaseContract(copy).length>0)
|
||||
}
|
||||
})
|
||||
@@ -1,6 +1,6 @@
|
||||
/** 发布者关系声明的跨字段检查;不推导安装、权限或运行时健康。 */
|
||||
export function validateApplicationContract(manifest) {
|
||||
if (!['docker-app', 'native-app', 'artifact'].includes(manifest.type)) return []
|
||||
if (!['docker-app', 'native-app', 'artifact', 'web-bundle'].includes(manifest.type)) return []
|
||||
const errors = []
|
||||
const ids = new Set()
|
||||
for (const entrypoint of Array.isArray(manifest.entrypoints) ? manifest.entrypoints : []) {
|
||||
|
||||
32
scripts/catalog/application-release-contract.mjs
Normal file
32
scripts/catalog/application-release-contract.mjs
Normal file
@@ -0,0 +1,32 @@
|
||||
/** 作者材料的跨字段检查;身份与运行事实由客户端owner核验,不执行安装说明。 */
|
||||
export function validateApplicationReleaseContract(manifest) {
|
||||
if (manifest.type !== 'web-bundle' && manifest.appRelease === undefined) return []
|
||||
const errors = []
|
||||
if (!['web-bundle', 'native-app', 'docker-app'].includes(manifest.type)) errors.push('appRelease: unsupported delivery type')
|
||||
if (!manifest.appRelease?.requiredCapabilities?.includes('app-authoring-v1')) errors.push('appRelease: app-authoring-v1 capability handshake required')
|
||||
if (!manifest.appRelease?.requiredCapabilities?.includes('release') ||
|
||||
(manifest.type === 'web-bundle' && !manifest.appRelease.requiredCapabilities.includes('web-bundle'))) errors.push('appRelease: delivery capability handshake required')
|
||||
const images = manifest.appRelease?.imageRefs
|
||||
if (manifest.type === 'docker-app' && (!Array.isArray(images) || !images.length || images.some(image =>
|
||||
typeof image !== 'string' || !/^[a-z0-9][a-z0-9._:/-]*@sha256:[a-f0-9]{64}$/.test(image) ||
|
||||
Number(image.split('/')[0].split(':')[1] ?? 0) > 65535))) errors.push('appRelease: Docker images must be pinned by digest')
|
||||
if (manifest.type !== 'docker-app' && images !== undefined) errors.push('appRelease: imageRefs belongs only to Docker release')
|
||||
const source = manifest.source
|
||||
if (!source || !['git', 'release'].includes(source.kind) ||
|
||||
!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(source.ref ?? '') || !/^[a-f0-9]{64}$/.test(source.sha256 ?? '')) {
|
||||
errors.push('source: authored release requires immutable commit and archive sha256')
|
||||
}
|
||||
try {
|
||||
const url = new URL(source?.url)
|
||||
if (url.protocol !== 'https:' || url.username || url.password || url.hash || url.search) errors.push('source: credential-free fixed HTTPS URL required')
|
||||
} catch { errors.push('source: invalid URL') }
|
||||
if (source?.kind === 'git' && !isPortablePath(source.path)) errors.push('source: git archive path must be canonical and relative')
|
||||
if (!isPortablePath(manifest.appRelease?.definitionPath)) errors.push('appRelease: definitionPath must be canonical and relative')
|
||||
return errors
|
||||
}
|
||||
|
||||
function isPortablePath(value) {
|
||||
return typeof value === 'string' && value.length > 0 && value.length <= 512 &&
|
||||
!/[\\\x00-\x1f?#:]/.test(value) && !value.startsWith('/') &&
|
||||
value.split('/').every(part => part.length > 0 && part !== '.' && part !== '..')
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { basename, join, relative, resolve, sep } from 'node:path'
|
||||
import { validateJsonSchema } from './json-schema.mjs'
|
||||
import { findStaticMetrics } from './static-metadata.mjs'
|
||||
import { validateApplicationContract } from './application-contract.mjs'
|
||||
import { validateApplicationReleaseContract } from './application-release-contract.mjs'
|
||||
|
||||
export const CATALOG_SIDECAR_FILENAME = 'catalog-metadata.v1.json'
|
||||
|
||||
@@ -77,7 +78,7 @@ function timestampShapeIsConsistent(timestamp) {
|
||||
function validateSidecarSemantics(manifest, sidecar, file) {
|
||||
const errors = []
|
||||
const add = (code, path, message) => errors.push(diagnostic('error', code, file, path, message))
|
||||
const expectedKind = ['docker-app', 'native-app', 'artifact'].includes(manifest.type) ? 'app' : 'service'
|
||||
const expectedKind = ['docker-app', 'native-app', 'artifact', 'web-bundle'].includes(manifest.type) ? 'app' : 'service'
|
||||
|
||||
if (sidecar.identity?.id !== manifest.id) add('identity-mismatch', '$.identity.id', 'sidecar identity.id 必须与 legacy manifest.id 一致')
|
||||
if (sidecar.identity?.kind !== expectedKind) add('kind-mismatch', '$.identity.kind', `legacy ${manifest.type} 必须映射为 ${expectedKind}`)
|
||||
@@ -140,7 +141,7 @@ function validateSidecarSemantics(manifest, sidecar, file) {
|
||||
if (manifest.toolCount !== undefined && sidecar.spec?.toolCount !== manifest.toolCount) add('spec-mismatch', '$.spec.toolCount', 'toolCount 必须与 legacy 一致')
|
||||
}
|
||||
|
||||
if (['native-app', 'artifact'].includes(manifest.type)) {
|
||||
if (['native-app', 'artifact', 'web-bundle'].includes(manifest.type)) {
|
||||
for (const field of ['stewardship', 'availability', 'redistribution', 'listingMaintainer', 'upstreamMaintainer', 'branding', 'compliance']) {
|
||||
if (!isDeepStrictEqual(manifest[field], sidecar.governance?.[field])) add('native-governance-mismatch', '$.governance.' + field, '原生应用 manifest 与 sidecar 的治理事实必须一致')
|
||||
}
|
||||
@@ -237,12 +238,14 @@ export function validateRegistry(repoRoot, options = {}) {
|
||||
if (entry.type === 'docker-app') counts.dockerApps += 1
|
||||
if (entry.type === 'native-app') counts.nativeApps = (counts.nativeApps ?? 0) + 1
|
||||
if (entry.type === 'artifact') counts.artifactApps = (counts.artifactApps ?? 0) + 1
|
||||
if (entry.type === 'web-bundle') counts.webBundles = (counts.webBundles ?? 0) + 1
|
||||
if (entry.type === 'mcp') counts.mcpServices += 1
|
||||
if (entry.type === 'http-api') counts.httpApis += 1
|
||||
if (entry.type === 'external-integration') counts.externalIntegrations += 1
|
||||
const effectiveEntrySchema = ['external-integration', 'native-app', 'artifact'].includes(entry.type) ? externalEntrySchema : currentEntrySchema
|
||||
const effectiveEntrySchema = ['external-integration', 'native-app', 'artifact', 'web-bundle'].includes(entry.type) ? externalEntrySchema : currentEntrySchema
|
||||
if (effectiveEntrySchema) diagnostics.push(...schemaDiagnostics(entry, effectiveEntrySchema, manifestRelative))
|
||||
for (const message of validateApplicationContract(entry)) diagnostics.push(diagnostic('error', 'application-contract', manifestRelative, '$', message))
|
||||
for (const message of validateApplicationReleaseContract(entry)) diagnostics.push(diagnostic('error', 'application-release-contract', manifestRelative, '$', message))
|
||||
if (entry.id !== basename(entryDir)) diagnostics.push(diagnostic('error', 'directory-id-mismatch', manifestRelative, '$.id', 'manifest.id 必须与 entries/<id> 目录名一致'))
|
||||
if (seenIds.has(entry.id)) diagnostics.push(diagnostic('error', 'duplicate-id', manifestRelative, '$.id', 'Registry 条目 ID 重复'))
|
||||
seenIds.add(entry.id)
|
||||
@@ -264,8 +267,8 @@ export function validateRegistry(repoRoot, options = {}) {
|
||||
}
|
||||
|
||||
if (rootManifest?.stats) {
|
||||
for (const key of ['totalEntries', 'dockerApps', 'nativeApps', 'artifactApps', 'mcpServices', 'httpApis', 'externalIntegrations']) {
|
||||
if (['nativeApps', 'artifactApps'].includes(key) && rootManifest.stats[key] === undefined && !counts[key]) continue
|
||||
for (const key of ['totalEntries', 'dockerApps', 'nativeApps', 'artifactApps', 'webBundles', 'mcpServices', 'httpApis', 'externalIntegrations']) {
|
||||
if (['nativeApps', 'artifactApps', 'webBundles'].includes(key) && rootManifest.stats[key] === undefined && !counts[key]) continue
|
||||
if (rootManifest.stats[key] === undefined && key === 'externalIntegrations' && counts[key] === 0) continue
|
||||
if (rootManifest.stats[key] !== (counts[key] ?? 0)) diagnostics.push(diagnostic('error', 'stats-mismatch', 'manifest.json', `$.stats.${key}`, `声明 ${rootManifest.stats[key]},实际 ${counts[key] ?? 0}`))
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { fileURLToPath } from 'node:url'
|
||||
import Ajv from 'ajv'
|
||||
import { findStaticMetrics } from './catalog/static-metadata.mjs'
|
||||
import { validateApplicationContract } from './catalog/application-contract.mjs'
|
||||
import { validateApplicationReleaseContract } from './catalog/application-release-contract.mjs'
|
||||
|
||||
const scriptDirectory = dirname(fileURLToPath(import.meta.url))
|
||||
const rootArgumentIndex = process.argv.indexOf('--root')
|
||||
@@ -348,6 +349,7 @@ for (const directoryEntry of await readdir(entriesRoot, { withFileTypes: true })
|
||||
addError(`entries/${directoryEntry.name}/manifest.json: ${formatAjvErrors(validateEntry.errors)}`)
|
||||
}
|
||||
for (const message of validateApplicationContract(manifest)) addError(`entries/${directoryEntry.name}/manifest.json: ${message}`)
|
||||
for (const message of validateApplicationReleaseContract(manifest)) addError(`entries/${directoryEntry.name}/manifest.json: ${message}`)
|
||||
if (manifest.type === 'external-integration') {
|
||||
await validateExternalIntegration(entryDirectory, manifest)
|
||||
}
|
||||
@@ -359,6 +361,7 @@ if (rootManifest) {
|
||||
dockerApps: manifests.filter((entry) => entry.type === 'docker-app').length,
|
||||
...(manifests.some((entry) => entry.type === 'native-app') || rootManifest.stats?.nativeApps !== undefined ? { nativeApps: manifests.filter((entry) => entry.type === 'native-app').length } : {}),
|
||||
...(manifests.some((entry) => entry.type === 'artifact') || rootManifest.stats?.artifactApps !== undefined ? { artifactApps: manifests.filter((entry) => entry.type === 'artifact').length } : {}),
|
||||
...(manifests.some((entry) => entry.type === 'web-bundle') || rootManifest.stats?.webBundles !== undefined ? { webBundles: manifests.filter((entry) => entry.type === 'web-bundle').length } : {}),
|
||||
mcpServices: manifests.filter((entry) => entry.type === 'mcp').length,
|
||||
httpApis: manifests.filter((entry) => entry.type === 'http-api').length,
|
||||
externalIntegrations: manifests.filter((entry) => entry.type === 'external-integration').length,
|
||||
|
||||
Reference in New Issue
Block a user