release-skill 0.2.4 → 0.2.6
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codebuddy-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +2 -2
- package/.kimi-plugin/plugin.json +1 -1
- package/CHANGELOG.md +45 -0
- package/INSTALL.md +32 -8
- package/INSTALL.zh-CN.md +28 -6
- package/README.md +29 -15
- package/README.zh-CN.md +27 -15
- package/adapters/claude/.claude-plugin/marketplace.json +1 -1
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/bin/release-skill.bundle.mjs +2202 -708
- package/adapters/claude/schemas/.render-manifest.json +6 -6
- package/adapters/claude/schemas/release-plan.schema.json +158 -0
- package/adapters/claude/schemas/release-project.schema.json +6 -0
- package/adapters/claude/schemas/release-run.schema.json +150 -0
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/release-skill.bundle.mjs +2202 -708
- package/adapters/codex/schemas/.render-manifest.json +6 -6
- package/adapters/codex/schemas/release-plan.schema.json +158 -0
- package/adapters/codex/schemas/release-project.schema.json +6 -0
- package/adapters/codex/schemas/release-run.schema.json +150 -0
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +2202 -708
- package/adapters/kimi/schemas/.render-manifest.json +6 -6
- package/adapters/kimi/schemas/release-plan.schema.json +158 -0
- package/adapters/kimi/schemas/release-project.schema.json +6 -0
- package/adapters/kimi/schemas/release-run.schema.json +150 -0
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +2202 -708
- package/adapters/workbuddy/schemas/.render-manifest.json +6 -6
- package/adapters/workbuddy/schemas/release-plan.schema.json +158 -0
- package/adapters/workbuddy/schemas/release-project.schema.json +6 -0
- package/adapters/workbuddy/schemas/release-run.schema.json +150 -0
- package/bin/release-skill.bundle.mjs +2202 -708
- package/package.json +1 -1
- package/references/.render-manifest.json +4 -4
- package/references/02-project-config.md +28 -2
- package/references/05-evidence-and-errors.md +6 -0
- package/schemas/.render-manifest.json +6 -6
- package/schemas/release-plan.schema.json +158 -0
- package/schemas/release-project.schema.json +6 -0
- package/schemas/release-run.schema.json +150 -0
- package/scripts/sync-public-files.mjs +20 -9
- package/src/adapters/plugin-marketplace.mjs +82 -3
- package/src/commands/prepare.mjs +232 -1
- package/src/commands/publish.mjs +134 -0
- package/src/commands/reconcile.mjs +19 -0
- package/src/commands/setup.mjs +26 -0
- package/src/commands/verify.mjs +183 -1
- package/src/core/errors.mjs +12 -0
- package/src/core/plan.mjs +29 -0
- package/src/core/skill-resource-closure.mjs +425 -0
- package/src/core/source-authority.mjs +547 -0
- package/src/platforms/codebuddy.mjs +16 -3
- package/src/platforms/kimi.mjs +36 -9
package/package.json
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
"bytes": 8840
|
|
11
11
|
},
|
|
12
12
|
"02-project-config.md": {
|
|
13
|
-
"digest": "
|
|
14
|
-
"bytes":
|
|
13
|
+
"digest": "0cff13ebb270f3515290e0ce60b91303143777d1a4948db28cf2c69612ceff8e",
|
|
14
|
+
"bytes": 19029
|
|
15
15
|
},
|
|
16
16
|
"03-readme-quality.md": {
|
|
17
17
|
"digest": "d74e41052592443ebbd450d0ace5f9b9b19c7a94db50f952b17c1c5a4c5dc230",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"bytes": 4682
|
|
23
23
|
},
|
|
24
24
|
"05-evidence-and-errors.md": {
|
|
25
|
-
"digest": "
|
|
26
|
-
"bytes":
|
|
25
|
+
"digest": "ebcb4940ff2a25ce6945c3b11cfcafe751f4a1f515276b7e021717ea5844eebf",
|
|
26
|
+
"bytes": 9688
|
|
27
27
|
},
|
|
28
28
|
"06-adapter-contract.md": {
|
|
29
29
|
"digest": "39f0271bc06eae38dade518ece8f7e16a18f53099a433e3ae07e9d20ff7a142e",
|
|
@@ -23,8 +23,9 @@ apiVersion: release-skill/v1
|
|
|
23
23
|
kind: ReleaseProject
|
|
24
24
|
|
|
25
25
|
project:
|
|
26
|
-
name: <string>
|
|
27
|
-
defaultBranch: <string>
|
|
26
|
+
name: <string> # 项目标识,必填
|
|
27
|
+
defaultBranch: <string> # workspace 远端默认分支,必填;不限定为 main
|
|
28
|
+
sourceRepository: <owner/repo> # workspace 源仓库;生产发布必填
|
|
28
29
|
|
|
29
30
|
releaseUnits: # 发布单元数组,至少 1 个
|
|
30
31
|
- id: <string> # 唯一标识
|
|
@@ -125,6 +126,31 @@ human-owned 权威源,然后重新 prepare、审阅并 approve;后续环节
|
|
|
125
126
|
|
|
126
127
|
旧的 `policy.requiredPublicFiles` 已不支持。加载器必须返回 `CONFIG_INVALID`,并引导迁移到各自的 `releaseUnits[].requiredPublicFiles`。
|
|
127
128
|
|
|
129
|
+
### 3.1.1 Workspace 源码权威内容闭包
|
|
130
|
+
|
|
131
|
+
`project.sourceRepository` 标识承载当前 workspace 人工源文件的 GitHub
|
|
132
|
+
`owner/repo`,它与各发布单元的 `publicRepo` 不是同一概念;多个发布单元可以投影到不同
|
|
133
|
+
公开仓库,但它们仍可共享一个 workspace 源仓库。`project.defaultBranch` 表示该源仓库的
|
|
134
|
+
真实远端默认分支,可以是 `main`、`master`、`develop`、`trunk` 或其他明确名称。
|
|
135
|
+
setup 只在 workspace Git remotes 唯一一致时提出候选;缺失或冲突时要求人工裁决,不保存
|
|
136
|
+
`auto`。
|
|
137
|
+
|
|
138
|
+
生产 prepare 在 hooks 完成后,从所有 `publicFiles.from`(目录递归展开)和每个 unit 的
|
|
139
|
+
`version.source` 计算源码输入闭包,冻结 workspace 相对路径、内容 SHA-256 和 Git mode。
|
|
140
|
+
只有闭包内 staged、unstaged 或 untracked 的文件会阻断 prepare;无关 dirty 文件不阻断。
|
|
141
|
+
离线 prepare 明确记录 `unobserved-offline`,远端内容的强制比较留在 publish。
|
|
142
|
+
|
|
143
|
+
production publish 在任何 adapter `execute` 前,从
|
|
144
|
+
`sourceRepository/defaultBranch` 读取冻结路径集合,先确认配置分支仍是远端实际默认分支,
|
|
145
|
+
再比较内容和 mode。判定只依赖内容闭包,不依赖 commit ancestry,因此普通 merge、
|
|
146
|
+
squash、rebase 和默认分支上的无关后续提交可以通过;冲突解决或 revert 丢失 README、
|
|
147
|
+
版本源或其他公开输入会按具体路径失败关闭。系统不会自动 merge、rebase、切分支、push
|
|
148
|
+
或创建 PR。
|
|
149
|
+
|
|
150
|
+
通过后,publish 写入与 plan digest 绑定的源码权威收据;PARTIAL 经 reconcile 恢复时继承
|
|
151
|
+
并重新校验该收据。verify 只消费这份冻结收据,不在发布完成后用可变默认分支重新判定历史
|
|
152
|
+
发布。
|
|
153
|
+
|
|
128
154
|
### 3.2 公开分支策略
|
|
129
155
|
|
|
130
156
|
- `create-release-branch`:目标分支必须不存在;创建独立 release 分支,冻结提交没有父提交。
|
|
@@ -26,6 +26,12 @@
|
|
|
26
26
|
| `RELEASE_DOCS_CONFLICT` | 目标文档存在人工冲突 | 非受管同版本 CHANGELOG 条目、受管标记缺失/重复/损坏、版本标记非唯一 | 人工修复目标并保留人工修改后重新演练 |
|
|
27
27
|
| `RELEASE_DOCS_REFRESH_STALE` | 写入确认绑定的候选已变化 | 演练后说明源或目标发生变化,仍用旧 `refreshDigest` 写入 | 重新演练取得新的 `refreshDigest` 并重新确认 |
|
|
28
28
|
| `RELEASE_DOCS_STALE` | prepare 检测到发布文档未刷新 | 说明源已更新但 README/CHANGELOG 受管内容未同步 | `docs refresh` 演练 → 确认写入 → 审阅提交 → 重新 prepare |
|
|
29
|
+
| `CONFIG_MISSING` | 生产源码权威配置缺失 | 缺少明确的 `project.sourceRepository` 或 `project.defaultBranch` | 补充经人工确认的 workspace 源仓库与默认分支后重新 prepare |
|
|
30
|
+
| `REMOTE_UNAVAILABLE` | 无法可靠读取 workspace 源仓库 | 网络、认证或远端协议错误导致默认分支内容不可观察 | 修复网络或认证后基于同一冻结计划重试;不得猜测成功 |
|
|
31
|
+
| `REF_MISSING` | 配置的远端默认分支不存在 | `refs/heads/<defaultBranch>` 不存在 | 核对真实分支名并重新 prepare |
|
|
32
|
+
| `NOT_DEFAULT` | 配置分支不是远端实际默认分支 | 默认分支在 prepare 后改变,或配置错误 | 人工确认远端默认分支,更新配置并重新 prepare |
|
|
33
|
+
| `CONTENT_MISMATCH` | 远端默认分支缺少冻结源码内容 | README、版本源、公开映射输入的内容或 mode 不一致 | 人工 merge/adopt/reject;接受的内容进入默认分支后重试 publish |
|
|
34
|
+
| `DIRTY_SOURCE_INPUT` | 源码输入闭包存在未提交变化 | `publicFiles.from` 或 `version.source` 有 staged、unstaged、untracked 变化 | 提交或撤销这些具体输入的变化后重新 prepare;无关 dirty 不受影响 |
|
|
29
35
|
|
|
30
36
|
---
|
|
31
37
|
|
|
@@ -22,16 +22,16 @@
|
|
|
22
22
|
"bytes": 2390
|
|
23
23
|
},
|
|
24
24
|
"release-plan.schema.json": {
|
|
25
|
-
"digest": "
|
|
26
|
-
"bytes":
|
|
25
|
+
"digest": "01c96659223918b5ce0e2d0d01d1ce5095baeb056de401babb8928add4d2a221",
|
|
26
|
+
"bytes": 35256
|
|
27
27
|
},
|
|
28
28
|
"release-project.schema.json": {
|
|
29
|
-
"digest": "
|
|
30
|
-
"bytes":
|
|
29
|
+
"digest": "a3d46be1e1e8105103a505c6d58d14f3733c8da91ab8289e097ffd259bf1cd93",
|
|
30
|
+
"bytes": 30982
|
|
31
31
|
},
|
|
32
32
|
"release-run.schema.json": {
|
|
33
|
-
"digest": "
|
|
34
|
-
"bytes":
|
|
33
|
+
"digest": "bf3c8fb4ca2fc078272a45a6ee6bf663fd2420a722b2f995dbaabf2635e2179e",
|
|
34
|
+
"bytes": 14824
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
}
|
|
@@ -815,6 +815,97 @@
|
|
|
815
815
|
"minLength": 1,
|
|
816
816
|
"description": "Digest of the project configuration at plan freeze time"
|
|
817
817
|
},
|
|
818
|
+
"skillResourceClosure": {
|
|
819
|
+
"type": "object",
|
|
820
|
+
"description": "Skill resource closure gate receipts from prepare phase (per-unit bound)",
|
|
821
|
+
"additionalProperties": false,
|
|
822
|
+
"properties": {
|
|
823
|
+
"unitReceipts": {
|
|
824
|
+
"type": "array",
|
|
825
|
+
"minItems": 1,
|
|
826
|
+
"items": {
|
|
827
|
+
"type": "object",
|
|
828
|
+
"additionalProperties": false,
|
|
829
|
+
"properties": {
|
|
830
|
+
"unitId": {
|
|
831
|
+
"type": "string",
|
|
832
|
+
"minLength": 1
|
|
833
|
+
},
|
|
834
|
+
"checkerVersion": {
|
|
835
|
+
"type": "string",
|
|
836
|
+
"minLength": 1
|
|
837
|
+
},
|
|
838
|
+
"inputDigest": {
|
|
839
|
+
"type": "string",
|
|
840
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
841
|
+
},
|
|
842
|
+
"surfaceCount": {
|
|
843
|
+
"type": "integer",
|
|
844
|
+
"minimum": 0
|
|
845
|
+
},
|
|
846
|
+
"skillCount": {
|
|
847
|
+
"type": "integer",
|
|
848
|
+
"minimum": 0
|
|
849
|
+
},
|
|
850
|
+
"referenceCount": {
|
|
851
|
+
"type": "integer",
|
|
852
|
+
"minimum": 0
|
|
853
|
+
},
|
|
854
|
+
"sourceOnlyCount": {
|
|
855
|
+
"type": "integer",
|
|
856
|
+
"minimum": 0
|
|
857
|
+
},
|
|
858
|
+
"findingCount": {
|
|
859
|
+
"const": 0
|
|
860
|
+
},
|
|
861
|
+
"receiptDigest": {
|
|
862
|
+
"type": "string",
|
|
863
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
864
|
+
}
|
|
865
|
+
},
|
|
866
|
+
"required": [
|
|
867
|
+
"unitId",
|
|
868
|
+
"checkerVersion",
|
|
869
|
+
"inputDigest",
|
|
870
|
+
"surfaceCount",
|
|
871
|
+
"skillCount",
|
|
872
|
+
"referenceCount",
|
|
873
|
+
"sourceOnlyCount",
|
|
874
|
+
"findingCount",
|
|
875
|
+
"receiptDigest"
|
|
876
|
+
]
|
|
877
|
+
}
|
|
878
|
+
},
|
|
879
|
+
"checkerVersion": {
|
|
880
|
+
"type": "string",
|
|
881
|
+
"minLength": 1
|
|
882
|
+
},
|
|
883
|
+
"totalSkillCount": {
|
|
884
|
+
"type": "integer",
|
|
885
|
+
"minimum": 0
|
|
886
|
+
},
|
|
887
|
+
"totalReferenceCount": {
|
|
888
|
+
"type": "integer",
|
|
889
|
+
"minimum": 0
|
|
890
|
+
},
|
|
891
|
+
"totalSourceOnlyCount": {
|
|
892
|
+
"type": "integer",
|
|
893
|
+
"minimum": 0
|
|
894
|
+
},
|
|
895
|
+
"totalFindingCount": {
|
|
896
|
+
"type": "integer",
|
|
897
|
+
"minimum": 0
|
|
898
|
+
}
|
|
899
|
+
},
|
|
900
|
+
"required": [
|
|
901
|
+
"unitReceipts",
|
|
902
|
+
"checkerVersion",
|
|
903
|
+
"totalSkillCount",
|
|
904
|
+
"totalReferenceCount",
|
|
905
|
+
"totalSourceOnlyCount",
|
|
906
|
+
"totalFindingCount"
|
|
907
|
+
]
|
|
908
|
+
},
|
|
818
909
|
"verificationGates": {
|
|
819
910
|
"type": "array",
|
|
820
911
|
"items": {
|
|
@@ -901,6 +992,73 @@
|
|
|
901
992
|
"type": "string",
|
|
902
993
|
"minLength": 1
|
|
903
994
|
},
|
|
995
|
+
"sourceAuthority": {
|
|
996
|
+
"type": "object",
|
|
997
|
+
"description": "Source authority content closure gate binding: proves frozen source inputs exist in the workspace remote default branch",
|
|
998
|
+
"required": [
|
|
999
|
+
"sourceRepository",
|
|
1000
|
+
"defaultBranch",
|
|
1001
|
+
"entries",
|
|
1002
|
+
"inputDigest",
|
|
1003
|
+
"algorithmVersion"
|
|
1004
|
+
],
|
|
1005
|
+
"additionalProperties": false,
|
|
1006
|
+
"properties": {
|
|
1007
|
+
"sourceRepository": {
|
|
1008
|
+
"type": "string",
|
|
1009
|
+
"minLength": 1,
|
|
1010
|
+
"pattern": "^[a-zA-Z0-9._-]+/[a-zA-Z0-9._-]+$",
|
|
1011
|
+
"description": "GitHub owner/repo of the workspace source repository"
|
|
1012
|
+
},
|
|
1013
|
+
"defaultBranch": {
|
|
1014
|
+
"type": "string",
|
|
1015
|
+
"minLength": 1,
|
|
1016
|
+
"description": "Workspace remote default branch name"
|
|
1017
|
+
},
|
|
1018
|
+
"entries": {
|
|
1019
|
+
"type": "array",
|
|
1020
|
+
"minItems": 1,
|
|
1021
|
+
"description": "Frozen workspace-relative source inputs compared with the remote default branch",
|
|
1022
|
+
"items": {
|
|
1023
|
+
"type": "object",
|
|
1024
|
+
"required": [
|
|
1025
|
+
"path",
|
|
1026
|
+
"digest",
|
|
1027
|
+
"mode"
|
|
1028
|
+
],
|
|
1029
|
+
"additionalProperties": false,
|
|
1030
|
+
"properties": {
|
|
1031
|
+
"path": {
|
|
1032
|
+
"type": "string",
|
|
1033
|
+
"minLength": 1,
|
|
1034
|
+
"pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$))(?!.*\\\\).+$"
|
|
1035
|
+
},
|
|
1036
|
+
"digest": {
|
|
1037
|
+
"type": "string",
|
|
1038
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
1039
|
+
},
|
|
1040
|
+
"mode": {
|
|
1041
|
+
"type": "string",
|
|
1042
|
+
"enum": [
|
|
1043
|
+
"100644",
|
|
1044
|
+
"100755"
|
|
1045
|
+
]
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
},
|
|
1050
|
+
"inputDigest": {
|
|
1051
|
+
"type": "string",
|
|
1052
|
+
"pattern": "^[a-f0-9]{64}$",
|
|
1053
|
+
"description": "SHA-256 digest of the deterministic source-input closure"
|
|
1054
|
+
},
|
|
1055
|
+
"algorithmVersion": {
|
|
1056
|
+
"type": "integer",
|
|
1057
|
+
"minimum": 1,
|
|
1058
|
+
"description": "Source-input closure algorithm version"
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
},
|
|
904
1062
|
"waivers": {
|
|
905
1063
|
"type": "array",
|
|
906
1064
|
"items": {
|
|
@@ -35,6 +35,12 @@
|
|
|
35
35
|
"defaultBranch": {
|
|
36
36
|
"type": "string",
|
|
37
37
|
"minLength": 1
|
|
38
|
+
},
|
|
39
|
+
"sourceRepository": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"minLength": 1,
|
|
42
|
+
"pattern": "^[a-zA-Z0-9._-]+/[a-zA-Z0-9._-]+$",
|
|
43
|
+
"description": "GitHub owner/repo of the workspace source repository for source authority content gate"
|
|
38
44
|
}
|
|
39
45
|
}
|
|
40
46
|
},
|
|
@@ -227,6 +227,71 @@
|
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
229
|
},
|
|
230
|
+
"sourceAuthorityReceipts": {
|
|
231
|
+
"type": "array",
|
|
232
|
+
"description": "Source authority content closure receipts: one per publish run proving remote content matches frozen inputs",
|
|
233
|
+
"items": {
|
|
234
|
+
"type": "object",
|
|
235
|
+
"required": [
|
|
236
|
+
"sourceRepository",
|
|
237
|
+
"defaultBranch",
|
|
238
|
+
"inputDigest",
|
|
239
|
+
"algorithmVersion",
|
|
240
|
+
"entryCount",
|
|
241
|
+
"planDigest",
|
|
242
|
+
"verifiedAt",
|
|
243
|
+
"result"
|
|
244
|
+
],
|
|
245
|
+
"additionalProperties": false,
|
|
246
|
+
"properties": {
|
|
247
|
+
"sourceRepository": {
|
|
248
|
+
"type": "string",
|
|
249
|
+
"minLength": 1
|
|
250
|
+
},
|
|
251
|
+
"defaultBranch": {
|
|
252
|
+
"type": "string",
|
|
253
|
+
"minLength": 1
|
|
254
|
+
},
|
|
255
|
+
"inputDigest": {
|
|
256
|
+
"type": "string",
|
|
257
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
258
|
+
},
|
|
259
|
+
"algorithmVersion": {
|
|
260
|
+
"type": "integer",
|
|
261
|
+
"minimum": 1
|
|
262
|
+
},
|
|
263
|
+
"entryCount": {
|
|
264
|
+
"type": "integer",
|
|
265
|
+
"minimum": 1
|
|
266
|
+
},
|
|
267
|
+
"planDigest": {
|
|
268
|
+
"type": "string",
|
|
269
|
+
"minLength": 1
|
|
270
|
+
},
|
|
271
|
+
"verifiedAt": {
|
|
272
|
+
"type": "string",
|
|
273
|
+
"format": "date-time"
|
|
274
|
+
},
|
|
275
|
+
"result": {
|
|
276
|
+
"type": "string",
|
|
277
|
+
"enum": [
|
|
278
|
+
"CONSISTENT",
|
|
279
|
+
"MISMATCH"
|
|
280
|
+
]
|
|
281
|
+
},
|
|
282
|
+
"observedCommit": {
|
|
283
|
+
"type": "string",
|
|
284
|
+
"pattern": "^[a-f0-9]{40,64}$"
|
|
285
|
+
},
|
|
286
|
+
"mismatchedPaths": {
|
|
287
|
+
"type": "array",
|
|
288
|
+
"items": {
|
|
289
|
+
"type": "string"
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
},
|
|
230
295
|
"consumerVerificationReceipts": {
|
|
231
296
|
"type": "array",
|
|
232
297
|
"description": "消费端验证收据,每个 marketplace action 一条记录",
|
|
@@ -366,6 +431,91 @@
|
|
|
366
431
|
}
|
|
367
432
|
}
|
|
368
433
|
}
|
|
434
|
+
},
|
|
435
|
+
"skillResourceClosureReceipts": {
|
|
436
|
+
"type": "array",
|
|
437
|
+
"description": "Per-surface skill resource closure verification receipts from verify phase",
|
|
438
|
+
"items": {
|
|
439
|
+
"type": "object",
|
|
440
|
+
"required": [
|
|
441
|
+
"host",
|
|
442
|
+
"distribution",
|
|
443
|
+
"unitId",
|
|
444
|
+
"checkerVersion",
|
|
445
|
+
"inputDigest",
|
|
446
|
+
"surfaceCount",
|
|
447
|
+
"skillCount",
|
|
448
|
+
"referenceCount",
|
|
449
|
+
"sourceOnlyCount",
|
|
450
|
+
"findingCount",
|
|
451
|
+
"receiptDigest",
|
|
452
|
+
"checkedAt",
|
|
453
|
+
"exitCode"
|
|
454
|
+
],
|
|
455
|
+
"additionalProperties": false,
|
|
456
|
+
"properties": {
|
|
457
|
+
"host": {
|
|
458
|
+
"type": "string",
|
|
459
|
+
"minLength": 1
|
|
460
|
+
},
|
|
461
|
+
"unitId": {
|
|
462
|
+
"type": "string",
|
|
463
|
+
"minLength": 1
|
|
464
|
+
},
|
|
465
|
+
"distribution": {
|
|
466
|
+
"type": "string",
|
|
467
|
+
"enum": [
|
|
468
|
+
"npm",
|
|
469
|
+
"claude-plugin",
|
|
470
|
+
"codex-plugin",
|
|
471
|
+
"kimi-plugin",
|
|
472
|
+
"codebuddy-plugin"
|
|
473
|
+
]
|
|
474
|
+
},
|
|
475
|
+
"actionId": {
|
|
476
|
+
"type": "string",
|
|
477
|
+
"minLength": 1
|
|
478
|
+
},
|
|
479
|
+
"checkerVersion": {
|
|
480
|
+
"type": "string",
|
|
481
|
+
"minLength": 1
|
|
482
|
+
},
|
|
483
|
+
"inputDigest": {
|
|
484
|
+
"type": "string",
|
|
485
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
486
|
+
},
|
|
487
|
+
"surfaceCount": {
|
|
488
|
+
"type": "integer",
|
|
489
|
+
"minimum": 0
|
|
490
|
+
},
|
|
491
|
+
"skillCount": {
|
|
492
|
+
"type": "integer",
|
|
493
|
+
"minimum": 0
|
|
494
|
+
},
|
|
495
|
+
"referenceCount": {
|
|
496
|
+
"type": "integer",
|
|
497
|
+
"minimum": 0
|
|
498
|
+
},
|
|
499
|
+
"sourceOnlyCount": {
|
|
500
|
+
"type": "integer",
|
|
501
|
+
"minimum": 0
|
|
502
|
+
},
|
|
503
|
+
"findingCount": {
|
|
504
|
+
"const": 0
|
|
505
|
+
},
|
|
506
|
+
"receiptDigest": {
|
|
507
|
+
"type": "string",
|
|
508
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
509
|
+
},
|
|
510
|
+
"checkedAt": {
|
|
511
|
+
"type": "string",
|
|
512
|
+
"format": "date-time"
|
|
513
|
+
},
|
|
514
|
+
"exitCode": {
|
|
515
|
+
"const": 0
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
369
519
|
}
|
|
370
520
|
},
|
|
371
521
|
"allOf": [
|
|
@@ -208,9 +208,9 @@ function buildRequiredPublicFilesYaml(entries, indent = 6) {
|
|
|
208
208
|
// Entry collection
|
|
209
209
|
// ---------------------------------------------------------------------------
|
|
210
210
|
|
|
211
|
-
async function collectEntries() {
|
|
211
|
+
async function collectEntries(packageRoot) {
|
|
212
212
|
// 1. Scan all files in the package directory
|
|
213
|
-
const allFiles = await scanFiles(
|
|
213
|
+
const allFiles = await scanFiles(packageRoot);
|
|
214
214
|
const allSet = new Set(allFiles);
|
|
215
215
|
|
|
216
216
|
// 2. Build the adapter exclusion set (all files under adapters/)
|
|
@@ -303,8 +303,8 @@ async function collectEntries() {
|
|
|
303
303
|
// Check / write
|
|
304
304
|
// ---------------------------------------------------------------------------
|
|
305
305
|
|
|
306
|
-
async function readProjectYaml() {
|
|
307
|
-
return await readFile(
|
|
306
|
+
async function readProjectYaml(projectPath) {
|
|
307
|
+
return await readFile(projectPath, 'utf8');
|
|
308
308
|
}
|
|
309
309
|
|
|
310
310
|
function extractExistingEntries(yamlText, key) {
|
|
@@ -369,18 +369,29 @@ function replaceArrayInYaml(yamlText, key, newContent) {
|
|
|
369
369
|
*
|
|
370
370
|
* @param {object} options
|
|
371
371
|
* @param {string} options.packageDir - Package directory.
|
|
372
|
+
* @param {string} [options.projectPath] - Project config path. Defaults to the
|
|
373
|
+
* real workspace config; tests may provide an isolated copy.
|
|
372
374
|
* @param {boolean} [options.check] - Read-only drift detection.
|
|
373
375
|
* @returns {Promise<{mode: string, clean?: boolean, diff?: object}>}
|
|
374
376
|
*/
|
|
375
|
-
export async function syncPublicFiles({
|
|
377
|
+
export async function syncPublicFiles({
|
|
378
|
+
packageDir,
|
|
379
|
+
projectPath = PROJECT_YAML,
|
|
380
|
+
check = false,
|
|
381
|
+
} = {}) {
|
|
376
382
|
if (typeof packageDir !== 'string' || packageDir.length === 0) {
|
|
377
383
|
const err = new Error('sync-public-files: packageDir is required');
|
|
378
384
|
err.code = 'SYNC_PUBLIC_FILES_INVALID_ARGUMENT';
|
|
379
385
|
throw err;
|
|
380
386
|
}
|
|
387
|
+
if (typeof projectPath !== 'string' || projectPath.length === 0) {
|
|
388
|
+
const err = new Error('sync-public-files: projectPath must be a non-empty string');
|
|
389
|
+
err.code = 'SYNC_PUBLIC_FILES_INVALID_ARGUMENT';
|
|
390
|
+
throw err;
|
|
391
|
+
}
|
|
381
392
|
|
|
382
|
-
const entries = await collectEntries();
|
|
383
|
-
const yamlText = await readProjectYaml();
|
|
393
|
+
const entries = await collectEntries(packageDir);
|
|
394
|
+
const yamlText = await readProjectYaml(projectPath);
|
|
384
395
|
|
|
385
396
|
const existingPublic = extractExistingEntries(yamlText, 'publicFiles');
|
|
386
397
|
const existingRequired = extractExistingEntries(yamlText, 'requiredPublicFiles');
|
|
@@ -422,10 +433,10 @@ export async function syncPublicFiles({ packageDir, check = false } = {}) {
|
|
|
422
433
|
let newYaml = replaceArrayInYaml(yamlText, 'publicFiles', publicYaml);
|
|
423
434
|
newYaml = replaceArrayInYaml(newYaml, 'requiredPublicFiles', requiredYaml);
|
|
424
435
|
|
|
425
|
-
await writeFile(
|
|
436
|
+
await writeFile(projectPath, newYaml, 'utf8');
|
|
426
437
|
|
|
427
438
|
// Verify write
|
|
428
|
-
const reread = await readFile(
|
|
439
|
+
const reread = await readFile(projectPath, 'utf8');
|
|
429
440
|
if (reread !== newYaml) {
|
|
430
441
|
const err = new Error('sync-public-files: post-write verification failed');
|
|
431
442
|
err.code = 'SYNC_PUBLIC_FILES_WRITE_VERIFY_FAILED';
|
|
@@ -2882,6 +2882,7 @@ export function createPluginMarketplaceAdapter(deps = {}) {
|
|
|
2882
2882
|
|
|
2883
2883
|
// 使用归一化后的 attestation(兼容旧格式)
|
|
2884
2884
|
const normalizedAttestation = attestationCheck.normalized;
|
|
2885
|
+
const requiresInstalledClosure = Boolean(context.plan?.skillResourceClosure);
|
|
2885
2886
|
|
|
2886
2887
|
// 统一人工判定:如果结果是 failed,直接返回失败
|
|
2887
2888
|
if (normalizedAttestation.result === 'failed') {
|
|
@@ -2901,7 +2902,19 @@ export function createPluginMarketplaceAdapter(deps = {}) {
|
|
|
2901
2902
|
});
|
|
2902
2903
|
}
|
|
2903
2904
|
|
|
2905
|
+
if (requiresInstalledClosure && !normalizedAttestation.installPath) {
|
|
2906
|
+
return createResult({
|
|
2907
|
+
actionType,
|
|
2908
|
+
status: ActionStatus.OBSERVED,
|
|
2909
|
+
observation: {
|
|
2910
|
+
installed: false,
|
|
2911
|
+
error: 'kimi attestation installPath is required by the skill resource closure gate',
|
|
2912
|
+
},
|
|
2913
|
+
});
|
|
2914
|
+
}
|
|
2915
|
+
|
|
2904
2916
|
// installPath 验证:必须在 managed root 内,不得逃逸,不得是符号链接
|
|
2917
|
+
let verifiedInstallPath = null;
|
|
2905
2918
|
if (normalizedAttestation.installPath) {
|
|
2906
2919
|
const managedRoot = resolve(attestationDir, 'kimi-home', 'plugins', 'managed');
|
|
2907
2920
|
const installPathAbs = resolve(normalizedAttestation.installPath);
|
|
@@ -2967,18 +2980,19 @@ export function createPluginMarketplaceAdapter(deps = {}) {
|
|
|
2967
2980
|
},
|
|
2968
2981
|
});
|
|
2969
2982
|
}
|
|
2983
|
+
verifiedInstallPath = installPathReal;
|
|
2970
2984
|
}
|
|
2971
2985
|
|
|
2972
2986
|
// 载荷绑定验证:旧格式含 installPath 时必须绑定冻结载荷并失败关闭。
|
|
2973
2987
|
// 身份、版本、载荷、市场来源不一致绝不能被人工 passed 覆盖。
|
|
2974
2988
|
let manifestDigest = action.manifestDigest;
|
|
2975
2989
|
let payloadBinding = null;
|
|
2976
|
-
if (
|
|
2990
|
+
if (verifiedInstallPath) {
|
|
2977
2991
|
try {
|
|
2978
2992
|
payloadBinding = await verifyInstalledMarketplacePayload(
|
|
2979
2993
|
action,
|
|
2980
2994
|
context,
|
|
2981
|
-
|
|
2995
|
+
verifiedInstallPath,
|
|
2982
2996
|
consumer,
|
|
2983
2997
|
);
|
|
2984
2998
|
manifestDigest = payloadBinding.manifestDigest;
|
|
@@ -3019,6 +3033,7 @@ export function createPluginMarketplaceAdapter(deps = {}) {
|
|
|
3019
3033
|
entrySkillFound: true,
|
|
3020
3034
|
manifestDigest,
|
|
3021
3035
|
planDigest: boundPlanDigest,
|
|
3036
|
+
...(verifiedInstallPath ? { installPath: verifiedInstallPath } : {}),
|
|
3022
3037
|
...extraInstalledPathsAudit(payloadBinding),
|
|
3023
3038
|
},
|
|
3024
3039
|
});
|
|
@@ -3129,6 +3144,7 @@ export function createPluginMarketplaceAdapter(deps = {}) {
|
|
|
3129
3144
|
|
|
3130
3145
|
// 使用归一化后的 attestation(兼容旧格式)
|
|
3131
3146
|
const normalizedAttestation = attestationCheck.normalized;
|
|
3147
|
+
const requiresInstalledClosure = Boolean(context.plan?.skillResourceClosure);
|
|
3132
3148
|
|
|
3133
3149
|
// 统一人工判定:如果结果是 failed,直接返回失败
|
|
3134
3150
|
if (normalizedAttestation.result === 'failed') {
|
|
@@ -3148,7 +3164,69 @@ export function createPluginMarketplaceAdapter(deps = {}) {
|
|
|
3148
3164
|
});
|
|
3149
3165
|
}
|
|
3150
3166
|
|
|
3151
|
-
|
|
3167
|
+
let verifiedInstallPath = null;
|
|
3168
|
+
if (requiresInstalledClosure) {
|
|
3169
|
+
if (!normalizedAttestation.installPath) {
|
|
3170
|
+
return createResult({
|
|
3171
|
+
actionType,
|
|
3172
|
+
status: ActionStatus.OBSERVED,
|
|
3173
|
+
observation: {
|
|
3174
|
+
installed: false,
|
|
3175
|
+
error: 'codebuddy attestation installPath is required by the skill resource closure gate',
|
|
3176
|
+
},
|
|
3177
|
+
});
|
|
3178
|
+
}
|
|
3179
|
+
if (!['desktop', 'cli'].includes(normalizedAttestation.installChannel)) {
|
|
3180
|
+
return createResult({
|
|
3181
|
+
actionType,
|
|
3182
|
+
status: ActionStatus.OBSERVED,
|
|
3183
|
+
observation: {
|
|
3184
|
+
installed: false,
|
|
3185
|
+
error: 'codebuddy attestation installChannel must be "desktop" or "cli" when the skill resource closure gate is active',
|
|
3186
|
+
},
|
|
3187
|
+
});
|
|
3188
|
+
}
|
|
3189
|
+
const normalizedPath = normalizedAttestation.installPath.replaceAll('\\', '/');
|
|
3190
|
+
const expectedSuffix = `.workbuddy/plugins/marketplaces/${CODEBUDDY_MARKETPLACE_NAME}/plugins/${action.plugin}`;
|
|
3191
|
+
if (!normalizedPath.endsWith(expectedSuffix)) {
|
|
3192
|
+
return createResult({
|
|
3193
|
+
actionType,
|
|
3194
|
+
status: ActionStatus.OBSERVED,
|
|
3195
|
+
observation: {
|
|
3196
|
+
installed: false,
|
|
3197
|
+
error: `codebuddy attestation installPath must end with "${expectedSuffix}"`,
|
|
3198
|
+
},
|
|
3199
|
+
});
|
|
3200
|
+
}
|
|
3201
|
+
const installPathAbs = resolve(normalizedAttestation.installPath);
|
|
3202
|
+
let lexicalStat;
|
|
3203
|
+
try {
|
|
3204
|
+
lexicalStat = await lstat(installPathAbs);
|
|
3205
|
+
} catch {
|
|
3206
|
+
return createResult({
|
|
3207
|
+
actionType,
|
|
3208
|
+
status: ActionStatus.OBSERVED,
|
|
3209
|
+
observation: {
|
|
3210
|
+
installed: false,
|
|
3211
|
+
error: `codebuddy attestation installPath does not exist: ${normalizedAttestation.installPath}`,
|
|
3212
|
+
},
|
|
3213
|
+
});
|
|
3214
|
+
}
|
|
3215
|
+
if (lexicalStat.isSymbolicLink() || !lexicalStat.isDirectory()) {
|
|
3216
|
+
return createResult({
|
|
3217
|
+
actionType,
|
|
3218
|
+
status: ActionStatus.OBSERVED,
|
|
3219
|
+
observation: {
|
|
3220
|
+
installed: false,
|
|
3221
|
+
error: 'codebuddy attestation installPath must be a real directory, not a symlink',
|
|
3222
|
+
},
|
|
3223
|
+
});
|
|
3224
|
+
}
|
|
3225
|
+
verifiedInstallPath = await realpath(installPathAbs);
|
|
3226
|
+
}
|
|
3227
|
+
|
|
3228
|
+
// 人工结果仍裁决不可自动化的安装步骤;新资源闭包计划另外
|
|
3229
|
+
// 暴露已校验的真实安装目录,供 verify 的内置只读门禁扫描。
|
|
3152
3230
|
return createResult({
|
|
3153
3231
|
actionType,
|
|
3154
3232
|
status: ActionStatus.OBSERVED,
|
|
@@ -3167,6 +3245,7 @@ export function createPluginMarketplaceAdapter(deps = {}) {
|
|
|
3167
3245
|
entrySkillFound: true,
|
|
3168
3246
|
manifestDigest: action.manifestDigest,
|
|
3169
3247
|
planDigest: boundPlanDigest,
|
|
3248
|
+
...(verifiedInstallPath ? { installPath: verifiedInstallPath } : {}),
|
|
3170
3249
|
},
|
|
3171
3250
|
});
|
|
3172
3251
|
}
|