release-skill 0.6.3 → 0.7.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/.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 +25 -0
- package/INSTALL.md +2 -2
- package/INSTALL.zh-CN.md +2 -2
- package/README.md +25 -17
- package/README.zh-CN.md +16 -14
- package/adapters/claude/.claude-plugin/marketplace.json +1 -1
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/bin/error-codes.json +1 -1
- package/adapters/claude/bin/kernel-protocol.json +1 -1
- package/adapters/claude/bin/license-texts/Apache-2.0.txt +201 -0
- package/adapters/claude/bin/license-texts/MIT.txt +21 -0
- package/adapters/claude/bin/registry.json +64 -1
- package/adapters/claude/bin/release-skill.bundle.mjs +34860 -31252
- package/adapters/claude/bin/rules.json +1 -1
- package/adapters/claude/schemas/.render-manifest.json +6 -6
- package/adapters/claude/schemas/release-plan.schema.json +136 -4
- package/adapters/claude/schemas/release-project.schema.json +13 -4
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/error-codes.json +1 -1
- package/adapters/codex/bin/kernel-protocol.json +1 -1
- package/adapters/codex/bin/license-texts/Apache-2.0.txt +201 -0
- package/adapters/codex/bin/license-texts/MIT.txt +21 -0
- package/adapters/codex/bin/registry.json +64 -1
- package/adapters/codex/bin/release-skill.bundle.mjs +34860 -31252
- package/adapters/codex/bin/rules.json +1 -1
- package/adapters/codex/schemas/.render-manifest.json +6 -6
- package/adapters/codex/schemas/release-plan.schema.json +136 -4
- package/adapters/codex/schemas/release-project.schema.json +13 -4
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/error-codes.json +1 -1
- package/adapters/kimi/bin/kernel-protocol.json +1 -1
- package/adapters/kimi/bin/license-texts/Apache-2.0.txt +201 -0
- package/adapters/kimi/bin/license-texts/MIT.txt +21 -0
- package/adapters/kimi/bin/registry.json +64 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +34860 -31252
- package/adapters/kimi/bin/rules.json +1 -1
- package/adapters/kimi/schemas/.render-manifest.json +6 -6
- package/adapters/kimi/schemas/release-plan.schema.json +136 -4
- package/adapters/kimi/schemas/release-project.schema.json +13 -4
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/error-codes.json +1 -1
- package/adapters/workbuddy/bin/kernel-protocol.json +1 -1
- package/adapters/workbuddy/bin/license-texts/Apache-2.0.txt +201 -0
- package/adapters/workbuddy/bin/license-texts/MIT.txt +21 -0
- package/adapters/workbuddy/bin/registry.json +64 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +34860 -31252
- package/adapters/workbuddy/bin/rules.json +1 -1
- package/adapters/workbuddy/schemas/.render-manifest.json +6 -6
- package/adapters/workbuddy/schemas/release-plan.schema.json +136 -4
- package/adapters/workbuddy/schemas/release-project.schema.json +13 -4
- package/bin/error-codes.json +1 -1
- package/bin/kernel-protocol.json +1 -1
- package/bin/license-texts/Apache-2.0.txt +201 -0
- package/bin/license-texts/MIT.txt +21 -0
- package/bin/registry.json +64 -1
- package/bin/release-skill-cli.mjs +6 -6
- package/bin/release-skill.bundle.mjs +34860 -31252
- package/bin/rules.json +1 -1
- package/package.json +5 -4
- package/platform-manifest.json +2 -2
- package/references/02-project-config.md +1 -1
- package/references/06-adapter-contract.md +6 -3
- package/schemas/.render-manifest.json +6 -6
- package/schemas/release-plan.schema.json +136 -4
- package/schemas/release-project.schema.json +13 -4
- package/src/adapters/distribute-git.mjs +17 -11
- package/src/commands/approve.mjs +6 -10
- package/src/commands/distribute.mjs +251 -69
- package/src/commands/postverify.mjs +81 -7
- package/src/commands/prepare.mjs +176 -3
- package/src/core/docs-refresh-preset.mjs +8 -4
- package/src/core/evidence.mjs +8 -1
- package/src/core/git-url-policy.mjs +214 -0
- package/src/core/marketplace-registry-entry.mjs +4 -3
- package/src/core/postpublish-approval.mjs +132 -1
- package/src/core/postpublish-bundle.mjs +342 -0
- package/src/core/postpublish-projection.mjs +210 -0
- package/src/core/postpublish.mjs +26 -19
- package/src/core/preset-executor.mjs +25 -6
- package/src/core/preset-gitwrite.mjs +37 -16
- package/src/core/presets.mjs +67 -19
- package/src/core/proposal-inbox.mjs +36 -20
- package/src/core/redact.mjs +16 -3
- package/src/producers/build-adapters.mjs +28 -14
package/bin/rules.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "release-skill",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "广州市风荷科技有限公司"
|
|
@@ -78,8 +78,9 @@
|
|
|
78
78
|
"ajv-formats": "3.0.1",
|
|
79
79
|
"libnpmpublish": "11.1.0",
|
|
80
80
|
"npm-registry-fetch": "18.0.2",
|
|
81
|
-
"skill-family-contracts": "0.
|
|
82
|
-
"skill-family-
|
|
81
|
+
"skill-family-contracts": "0.8.0",
|
|
82
|
+
"skill-family-engineering-kit": "0.8.0",
|
|
83
|
+
"skill-family-harness-node": "0.8.0",
|
|
83
84
|
"yaml": "2.9.0"
|
|
84
85
|
},
|
|
85
86
|
"devDependencies": {
|
|
@@ -87,7 +88,7 @@
|
|
|
87
88
|
"node-gyp": "12.3.0"
|
|
88
89
|
},
|
|
89
90
|
"engines": {
|
|
90
|
-
"node": ">=22.
|
|
91
|
+
"node": ">=22.22.2 <23"
|
|
91
92
|
},
|
|
92
93
|
"keywords": [
|
|
93
94
|
"release",
|
package/platform-manifest.json
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
3
|
"platformId": "release-skill",
|
|
4
4
|
"platformName": "release-skill Plugin Platform",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.7.0",
|
|
6
6
|
"logicalRoot": "skills-src/",
|
|
7
7
|
"logicalRootDigest": "3767fc36f42ca561f5e839cea7b7e5bc16d08c47867b5bd6086f25efd0cb0f06",
|
|
8
8
|
"projectionDigest": "419f7f9601015459956e10aae74f42240ab2ac248238ff7494f3e761643d1749",
|
|
9
|
-
"projectionId": "release-skill:platform:0.
|
|
9
|
+
"projectionId": "release-skill:platform:0.7.0",
|
|
10
10
|
"manifestRequired": true,
|
|
11
11
|
"projectionSources": {
|
|
12
12
|
"skills-src-root": {
|
|
@@ -103,7 +103,7 @@ postPublish: # 可选;发布后分发配置(W2 closure)
|
|
|
103
103
|
email: <string> # RFC 5322 valid
|
|
104
104
|
payloadDir: <string> # 相对路径
|
|
105
105
|
dryRun?: boolean # 可选;只本地执行
|
|
106
|
-
materialize?: { #
|
|
106
|
+
materialize?: { # 可选;缺省时载荷由冻结 publicFiles 经 Foundation 托管投影物化
|
|
107
107
|
command: <string> # 可执行文件
|
|
108
108
|
args: [<string>] # 参数数组,禁止 shell 字符串
|
|
109
109
|
outputMarker?: <string> # 输出标记,用于验证
|
|
@@ -170,12 +170,14 @@ marketplace install action 的安装侧载荷校验以 action 参数 `payloadCon
|
|
|
170
170
|
1. 发布计划已冻结且 schema 验证通过。
|
|
171
171
|
2. 发布计划摘要与批准记录中的摘要匹配。
|
|
172
172
|
3. 批准记录存在且未超过 24 小时有效期。
|
|
173
|
-
4.
|
|
173
|
+
4. 工作区基线按 `planVersion` 分叉校验(与 `01-state-machine.md` §4.4 及根 `AGENTS.md` 的 Release Authorization 语义一致):
|
|
174
|
+
- **planVersion 1(旧版)计划**:继续绑定基线——Git tree hash(及 workspace digest)必须与批准记录中的匹配;批准后基线变化即使批准失效,硬失败(`BASELINE_CHANGED`)。
|
|
175
|
+
- **planVersion 2 计划**:digest 只绑定信任边界(冻结产物身份:git commit/tree、manifestDigest、npm tarball 摘要;外部动作列表;目标版本;配置摘要)。工作区基线(tree hash、workspace digest)是记录层审计数据:不参与 digest、不使批准失效;publish/reconcile 检测到基线漂移时记录 warning 级证据事件并继续执行,产物身份与完整性由外部写之前的冻结产物复验保证。
|
|
174
176
|
5. 目标版本与批准记录中的匹配。
|
|
175
177
|
6. 远端无冲突状态(preflight 通过)。
|
|
176
178
|
7. `context.externalWritesAuthorized === true`。
|
|
177
179
|
|
|
178
|
-
任一条件不满足时,execute 返回对应错误码(`AUTH_MISSING`、`BASELINE_CHANGED
|
|
180
|
+
任一条件不满足时,execute 返回对应错误码(`AUTH_MISSING`、`BASELINE_CHANGED`(仅 planVersion 1)、`REMOTE_CONFLICT` 等)并拒绝执行。
|
|
179
181
|
|
|
180
182
|
### 3.2 批准记录结构
|
|
181
183
|
|
|
@@ -195,6 +197,7 @@ marketplace install action 的安装侧载荷校验以 action 参数 `payloadCon
|
|
|
195
197
|
|
|
196
198
|
- `approvedActions` 不得包含通配符。
|
|
197
199
|
- `expiresAt` 不得晚于 `approvedAt` + 24 小时。
|
|
200
|
+
- `baseline` 的约束力按 `planVersion` 分叉(见 §3.1 条件 4):planVersion 1 计划中它是批准绑定字段;planVersion 2 计划中它只是记录层审计数据,不使批准失效。
|
|
198
201
|
|
|
199
202
|
---
|
|
200
203
|
|
|
@@ -234,7 +237,7 @@ marketplace install action 的安装侧载荷校验以 action 参数 `payloadCon
|
|
|
234
237
|
1. 重新验证发布计划 schema。
|
|
235
238
|
2. 重新验证发布计划摘要。
|
|
236
239
|
3. 检查批准记录是否过期。
|
|
237
|
-
4.
|
|
240
|
+
4. 按 planVersion 分叉复验工作区基线:planVersion 1 计划基线变化即失败(`BASELINE_CHANGED`);planVersion 2 计划基线漂移仅记录 warning 级证据事件并继续,产物身份由外部写之前的冻结产物复验保证。
|
|
238
241
|
5. 执行远程 preflight。
|
|
239
242
|
6. 按批准的操作列表顺序执行每个检查点。
|
|
240
243
|
7. 每个检查点:preflight -> execute -> observe -> 记录。
|
|
@@ -26,16 +26,16 @@
|
|
|
26
26
|
"bytes": 1744
|
|
27
27
|
},
|
|
28
28
|
"release-plan.schema.json": {
|
|
29
|
-
"digest": "
|
|
30
|
-
"bytes":
|
|
29
|
+
"digest": "48b7755dfbeee88ccf78c3200000ab2f5dd06db42bc7b423aed0f81a91cc0418",
|
|
30
|
+
"bytes": 54672
|
|
31
31
|
},
|
|
32
32
|
"release-project.schema.json": {
|
|
33
|
-
"digest": "
|
|
34
|
-
"bytes":
|
|
33
|
+
"digest": "cc2061799c3a0dd6daea940148e3497b7a4ae8cafb987bf51de7c34c1fed921d",
|
|
34
|
+
"bytes": 46932
|
|
35
35
|
},
|
|
36
36
|
"release-run.schema.json": {
|
|
37
|
-
"digest": "
|
|
38
|
-
"bytes":
|
|
37
|
+
"digest": "9755a8caa58dd02509ac8e6f869aec5e74d8ae3f7339a3f944977bfad7b729ad",
|
|
38
|
+
"bytes": 17897
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
}
|
|
@@ -1304,9 +1304,8 @@
|
|
|
1304
1304
|
},
|
|
1305
1305
|
"planPostPublish": {
|
|
1306
1306
|
"type": "object",
|
|
1307
|
-
"description": "冻结的发布后分发块:声明本身 + tag/tagCommit/unitId/payloadSource 绑定。整体参与 plan digest(planVersion 2 记录层剥离不包含该块),任何配置变更都会改变 planDigest。tagCommit 仅在 prepare 能冻结 create-tag 预期 commit 时存在(production 资产);缺省时 distribute fail-closed。",
|
|
1307
|
+
"description": "冻结的发布后分发块:声明本身 + tag/tagCommit/unitId/payloadSource 绑定。整体参与 plan digest(planVersion 2 记录层剥离不包含该块),任何配置变更都会改变 planDigest。tagCommit 仅在 prepare 能冻结 create-tag 预期 commit 时存在(production 资产);缺省时 distribute fail-closed。materialize 钩子自 T6 起可选:载荷物化要么由消费者 materialize 钩子完成,要么由冻结 executionBundle.publicFiles 经 Foundation Engineering Kit 托管投影完成(两者至少其一,见 anyOf)。",
|
|
1308
1308
|
"required": [
|
|
1309
|
-
"materialize",
|
|
1310
1309
|
"commitIdentity",
|
|
1311
1310
|
"tag",
|
|
1312
1311
|
"unitId",
|
|
@@ -1376,8 +1375,141 @@
|
|
|
1376
1375
|
"payloadSource": {
|
|
1377
1376
|
"const": "tag-worktree",
|
|
1378
1377
|
"description": "时序契约:载荷只能来自 tagCommit 处的 detached worktree,绝不读取工作区状态"
|
|
1378
|
+
},
|
|
1379
|
+
"executionBundle": {
|
|
1380
|
+
"type": "object",
|
|
1381
|
+
"description": "冻结的私有执行包(F-01/T1):closure 直接采用 Foundation computeResourceClosure() 的返回(排序、去重、受收容读取与摘要均由 Foundation 完成),publicFiles 是从发布单元投影的公开映射。计划是该执行包的唯一事实源:随整个 plan digest 绑定,不维护平行 manifest,也不另造 bundle digest。字节写入计划权威 .release-skill 的 postpublish-bundles/<closure.digest>/<resource.path>;distribute/postVerify 在任何 hook/外部写之前经 Foundation 严格复验并重算闭包,任何不一致即失败。",
|
|
1382
|
+
"required": [
|
|
1383
|
+
"foundationMechanism",
|
|
1384
|
+
"closure",
|
|
1385
|
+
"publicFiles"
|
|
1386
|
+
],
|
|
1387
|
+
"additionalProperties": false,
|
|
1388
|
+
"properties": {
|
|
1389
|
+
"foundationMechanism": {
|
|
1390
|
+
"const": "foundation.harness.resource-closure"
|
|
1391
|
+
},
|
|
1392
|
+
"closure": {
|
|
1393
|
+
"type": "object",
|
|
1394
|
+
"description": "Foundation skill-family.resource-closure(schemaVersion 1)原样冻结",
|
|
1395
|
+
"required": [
|
|
1396
|
+
"schemaVersion",
|
|
1397
|
+
"kind",
|
|
1398
|
+
"digestAlgorithm",
|
|
1399
|
+
"digest",
|
|
1400
|
+
"resources"
|
|
1401
|
+
],
|
|
1402
|
+
"additionalProperties": false,
|
|
1403
|
+
"properties": {
|
|
1404
|
+
"schemaVersion": {
|
|
1405
|
+
"const": 1
|
|
1406
|
+
},
|
|
1407
|
+
"kind": {
|
|
1408
|
+
"const": "skill-family.resource-closure"
|
|
1409
|
+
},
|
|
1410
|
+
"digestAlgorithm": {
|
|
1411
|
+
"const": "sha256"
|
|
1412
|
+
},
|
|
1413
|
+
"digest": {
|
|
1414
|
+
"type": "string",
|
|
1415
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
1416
|
+
},
|
|
1417
|
+
"resources": {
|
|
1418
|
+
"type": "array",
|
|
1419
|
+
"items": {
|
|
1420
|
+
"type": "object",
|
|
1421
|
+
"required": [
|
|
1422
|
+
"path",
|
|
1423
|
+
"role",
|
|
1424
|
+
"exists",
|
|
1425
|
+
"sha256"
|
|
1426
|
+
],
|
|
1427
|
+
"additionalProperties": false,
|
|
1428
|
+
"properties": {
|
|
1429
|
+
"path": {
|
|
1430
|
+
"type": "string",
|
|
1431
|
+
"minLength": 1,
|
|
1432
|
+
"pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$))(?!.*\\\\).+$"
|
|
1433
|
+
},
|
|
1434
|
+
"role": {
|
|
1435
|
+
"const": "input",
|
|
1436
|
+
"description": "私有执行输入只有 input 资源;output 角色不属于执行包"
|
|
1437
|
+
},
|
|
1438
|
+
"exists": {
|
|
1439
|
+
"const": true
|
|
1440
|
+
},
|
|
1441
|
+
"sha256": {
|
|
1442
|
+
"type": "string",
|
|
1443
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
},
|
|
1450
|
+
"publicFiles": {
|
|
1451
|
+
"type": "array",
|
|
1452
|
+
"description": "从已通过 schema 与运行时校验的发布单元投影进冻结计划的公开映射(T6 托管投影输入)",
|
|
1453
|
+
"items": {
|
|
1454
|
+
"type": "object",
|
|
1455
|
+
"required": [
|
|
1456
|
+
"from",
|
|
1457
|
+
"to",
|
|
1458
|
+
"mode"
|
|
1459
|
+
],
|
|
1460
|
+
"additionalProperties": false,
|
|
1461
|
+
"properties": {
|
|
1462
|
+
"sourceScope": {
|
|
1463
|
+
"type": "string",
|
|
1464
|
+
"enum": [
|
|
1465
|
+
"unit",
|
|
1466
|
+
"workspace"
|
|
1467
|
+
]
|
|
1468
|
+
},
|
|
1469
|
+
"from": {
|
|
1470
|
+
"type": "string",
|
|
1471
|
+
"minLength": 1,
|
|
1472
|
+
"pattern": "^(?!/)(?!\\.\\/)(?!.*\\\\)(?!.*:)(?!\\.\\.(/|$))(?!\\.$)(?!.*\\/\\.\\.\\/)(?!.*\\/\\.\\.$)(?!.*\\/\\.\\/)(?!.*\\/\\/)(?!.*\\0)(?!.*\\/\\.$)(?!.*\\/$).*$"
|
|
1473
|
+
},
|
|
1474
|
+
"to": {
|
|
1475
|
+
"type": "string",
|
|
1476
|
+
"minLength": 1,
|
|
1477
|
+
"pattern": "^(?!/)(?!\\.\\/)(?!.*\\\\)(?!.*:)(?!\\.\\.(/|$))(?!\\.$)(?!.*\\/\\.\\.\\/)(?!.*\\/\\.\\.$)(?!.*\\/\\.\\/)(?!.*\\/\\/)(?!.*\\0)(?!.*\\/\\.$)(?!.*\\/$).*$"
|
|
1478
|
+
},
|
|
1479
|
+
"mode": {
|
|
1480
|
+
"type": "string",
|
|
1481
|
+
"enum": [
|
|
1482
|
+
"preserve"
|
|
1483
|
+
]
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1379
1489
|
}
|
|
1380
|
-
}
|
|
1490
|
+
},
|
|
1491
|
+
"anyOf": [
|
|
1492
|
+
{
|
|
1493
|
+
"required": [
|
|
1494
|
+
"materialize"
|
|
1495
|
+
]
|
|
1496
|
+
},
|
|
1497
|
+
{
|
|
1498
|
+
"description": "无 materialize 钩子时,冻结的 executionBundle.publicFiles 必须非空:载荷由 Foundation 托管投影物化(F-06/T6)",
|
|
1499
|
+
"required": [
|
|
1500
|
+
"executionBundle"
|
|
1501
|
+
],
|
|
1502
|
+
"properties": {
|
|
1503
|
+
"executionBundle": {
|
|
1504
|
+
"properties": {
|
|
1505
|
+
"publicFiles": {
|
|
1506
|
+
"minItems": 1
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
]
|
|
1381
1513
|
},
|
|
1382
1514
|
"planPostPublishHook": {
|
|
1383
1515
|
"type": "object",
|
|
@@ -1552,7 +1684,7 @@
|
|
|
1552
1684
|
},
|
|
1553
1685
|
"remoteUrl": {
|
|
1554
1686
|
"type": "string",
|
|
1555
|
-
"pattern": "^(?:https?|file)
|
|
1687
|
+
"pattern": "^(?:https?|file)://[^@]+\\.git$"
|
|
1556
1688
|
},
|
|
1557
1689
|
"visibility": {
|
|
1558
1690
|
"type": "string",
|
|
@@ -886,9 +886,8 @@
|
|
|
886
886
|
},
|
|
887
887
|
"postPublish": {
|
|
888
888
|
"type": "object",
|
|
889
|
-
"description": "
|
|
889
|
+
"description": "发布后分发声明:载荷要么由 materialize 钩子从冻结 tag 工作树物化,要么(省略 materialize 时)由冻结的 publicFiles 映射经 Foundation Engineering Kit 托管投影物化(F-06/T6);targets 将载荷镜像或渲染市场索引推送到独立 git 远端。分发在 publish 之后由 distribute 命令执行,时序契约禁止读取工作区状态。",
|
|
890
890
|
"required": [
|
|
891
|
-
"materialize",
|
|
892
891
|
"commitIdentity"
|
|
893
892
|
],
|
|
894
893
|
"additionalProperties": false,
|
|
@@ -939,6 +938,16 @@
|
|
|
939
938
|
"assertMainVersionAhead": {
|
|
940
939
|
"type": "boolean",
|
|
941
940
|
"description": "分发前校验主干版本源仍领先于目标版本,防止在已推进的主干上分发旧版本"
|
|
941
|
+
},
|
|
942
|
+
"executionFiles": {
|
|
943
|
+
"type": "array",
|
|
944
|
+
"description": "私有执行输入闭世界清单(F-01):仅列出发布后命令需要、但冻结 tag 中不存在的父工作区领域文件;辅助文件(helper)必须显式列入,执行时不做任何临时发现。每个路径经 Foundation 路径分类与受收容读取;列表排序去重后随计划冻结为 executionBundle.closure,字节发布到计划权威 .release-skill 下的摘要寻址目录。tag 中已有的文件继续由 tagCommit 绑定,不得在此声明(执行包不得覆盖 tag 文件)。路径语法只由 schema 做第一层形状检查,最终以运行时 Foundation 分类为准。",
|
|
945
|
+
"items": {
|
|
946
|
+
"type": "string",
|
|
947
|
+
"minLength": 1,
|
|
948
|
+
"pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$))(?!.*\\\\).+$"
|
|
949
|
+
},
|
|
950
|
+
"uniqueItems": true
|
|
942
951
|
}
|
|
943
952
|
}
|
|
944
953
|
},
|
|
@@ -1129,8 +1138,8 @@
|
|
|
1129
1138
|
},
|
|
1130
1139
|
"remoteUrl": {
|
|
1131
1140
|
"type": "string",
|
|
1132
|
-
"pattern": "^(?:https?|file)
|
|
1133
|
-
"description": "仅允许 http(s) 或 file(测试传输)且以 .git 结尾的远端 URL"
|
|
1141
|
+
"pattern": "^(?:https?|file)://[^@]+\\.git$",
|
|
1142
|
+
"description": "仅允许 http(s) 或 file(测试传输)且以 .git 结尾的远端 URL;禁止任何 userinfo(user:password@)形态——凭证只属于宿主 git credential helper。schema 只是第一道门,运行时 Git URL 策略(new URL 解析)是最终权威"
|
|
1134
1143
|
},
|
|
1135
1144
|
"visibility": {
|
|
1136
1145
|
"type": "string",
|
|
@@ -50,6 +50,12 @@ import {
|
|
|
50
50
|
createResult,
|
|
51
51
|
} from './contract.mjs';
|
|
52
52
|
import { ReleaseError, GATE_FAILED, REMOTE_CONFLICT, REMOTE_UNAVAILABLE } from '../core/errors.mjs';
|
|
53
|
+
import {
|
|
54
|
+
checkGitRemoteUrl,
|
|
55
|
+
describeGitRemoteUrlFailure,
|
|
56
|
+
isAllowedGitRemoteUrl,
|
|
57
|
+
redactUrlCredentialsIfPresent,
|
|
58
|
+
} from '../core/git-url-policy.mjs';
|
|
53
59
|
|
|
54
60
|
const execFile = promisify(execFileCb);
|
|
55
61
|
|
|
@@ -59,8 +65,6 @@ const GIT_TIMEOUT_MS = 120_000;
|
|
|
59
65
|
const TRANSFER_TIMEOUT_MS = 300_000;
|
|
60
66
|
const TMP_PREFIX = 'release-skill-distribute-';
|
|
61
67
|
|
|
62
|
-
/** Remote URL pattern: http(s) or file (test transport), .git-suffixed. */
|
|
63
|
-
const SAFE_REMOTE_URL_RE = /^(?:https?|file):\/\/.+\.git$/;
|
|
64
68
|
/** Branch pattern (leading alphanumeric blocks option-like names). */
|
|
65
69
|
const SAFE_BRANCH_RE = /^[A-Za-z0-9][A-Za-z0-9._/-]*$/;
|
|
66
70
|
/** Safe tag characters (belt + braces; prepare already rendered the tag). */
|
|
@@ -164,8 +168,8 @@ export function renderMarketplaceIndex(marketplace, params = {}) {
|
|
|
164
168
|
...(sha ? { sha } : {}),
|
|
165
169
|
};
|
|
166
170
|
} else {
|
|
167
|
-
if (typeof dependencyUrl !== 'string' || !
|
|
168
|
-
fail('url form requires the payload-mirror dependency remoteUrl', {
|
|
171
|
+
if (typeof dependencyUrl !== 'string' || !isAllowedGitRemoteUrl(dependencyUrl)) {
|
|
172
|
+
fail('url form requires the payload-mirror dependency remoteUrl', {});
|
|
169
173
|
}
|
|
170
174
|
source = {
|
|
171
175
|
source: 'url',
|
|
@@ -187,11 +191,12 @@ function hasControlChars(value) {
|
|
|
187
191
|
}
|
|
188
192
|
|
|
189
193
|
function assertSafeRemoteUrl(remoteUrl) {
|
|
190
|
-
|
|
194
|
+
const verdict = checkGitRemoteUrl(remoteUrl);
|
|
195
|
+
if (!verdict.ok) {
|
|
191
196
|
throw new ReleaseError(
|
|
192
197
|
GATE_FAILED,
|
|
193
|
-
|
|
194
|
-
{
|
|
198
|
+
`distribute remoteUrl ${describeGitRemoteUrlFailure(verdict.reason)}`,
|
|
199
|
+
{ reason: verdict.reason },
|
|
195
200
|
);
|
|
196
201
|
}
|
|
197
202
|
}
|
|
@@ -302,9 +307,10 @@ export function createDistributeGitAdapter(deps = {}) {
|
|
|
302
307
|
function probeFailureResult(action, error) {
|
|
303
308
|
const classification = classifyProbeFailure(stderrText(error));
|
|
304
309
|
const code = classification === 'auth' ? REMOTE_CONFLICT : REMOTE_UNAVAILABLE;
|
|
310
|
+
const safeRemoteUrl = redactUrlCredentialsIfPresent(action.remoteUrl);
|
|
305
311
|
const message = classification === 'auth'
|
|
306
|
-
? `remote refused authentication for ${
|
|
307
|
-
: `cannot reach ${
|
|
312
|
+
? `remote refused authentication for ${safeRemoteUrl}; distribute reuses the host keychain credential and never prompts, reads, or retries credentials`
|
|
313
|
+
: `cannot reach ${safeRemoteUrl} — start VPN / check network; distribute fails closed and never retries with credentials`;
|
|
308
314
|
return createResult({
|
|
309
315
|
actionType: action.actionType,
|
|
310
316
|
status: ActionStatus.PREFLIGHT_FAILED,
|
|
@@ -312,7 +318,7 @@ export function createDistributeGitAdapter(deps = {}) {
|
|
|
312
318
|
details: {
|
|
313
319
|
code,
|
|
314
320
|
classification,
|
|
315
|
-
remoteUrl:
|
|
321
|
+
remoteUrl: safeRemoteUrl,
|
|
316
322
|
stderrTail: stderrTail(error),
|
|
317
323
|
},
|
|
318
324
|
});
|
|
@@ -468,7 +474,7 @@ export function createDistributeGitAdapter(deps = {}) {
|
|
|
468
474
|
if (existingTag) {
|
|
469
475
|
throw new ReleaseError(
|
|
470
476
|
REMOTE_CONFLICT,
|
|
471
|
-
`target ${action.remoteUrl} already carries tag ${action.tag} and the content differs; moving an existing tag would be force-equivalent — human decision required`,
|
|
477
|
+
`target ${redactUrlCredentialsIfPresent(action.remoteUrl)} already carries tag ${action.tag} and the content differs; moving an existing tag would be force-equivalent — human decision required`,
|
|
472
478
|
{ targetId: action.targetId, tag: action.tag },
|
|
473
479
|
);
|
|
474
480
|
}
|
package/src/commands/approve.mjs
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
validateApprovalRecordSchema,
|
|
31
31
|
MAX_APPROVAL_MS,
|
|
32
32
|
} from '../core/approval.mjs';
|
|
33
|
-
import { validatePostPublishApproval } from '../core/postpublish-approval.mjs';
|
|
33
|
+
import { validatePostPublishApproval, derivePostPublishApprovalAuthorityPath } from '../core/postpublish-approval.mjs';
|
|
34
34
|
import { normalizePostPublishHook, effectiveHookRequiresApproval } from '../core/postpublish.mjs';
|
|
35
35
|
import { WORKSPACE_DIGEST_ALGORITHM } from '../core/baseline.mjs';
|
|
36
36
|
|
|
@@ -406,18 +406,14 @@ export async function approvePostPublishHook(options) {
|
|
|
406
406
|
validatePostPublishApproval(plan, approvalRecord, { clock: clockFn });
|
|
407
407
|
|
|
408
408
|
// --- Write the digest-addressed authority ---
|
|
409
|
-
const planDir = dirname(resolve(planPath));
|
|
410
|
-
const releaseDir = basename(planDir) === 'plans' && basename(planPath) === `${actualDigest}.json`
|
|
411
|
-
? dirname(planDir)
|
|
412
|
-
: planDir;
|
|
413
409
|
const json = JSON.stringify(approvalRecord, null, 2);
|
|
414
410
|
const approvalDigest = computeApprovalDigest(json);
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
411
|
+
// Single authority-layout source (core/postpublish-approval.mjs): the
|
|
412
|
+
// consumer-side assertion derives the exact same path fail-closed.
|
|
413
|
+
const { authorityPath: immutableApprovalPath } = derivePostPublishApprovalAuthorityPath(
|
|
414
|
+
planPath,
|
|
419
415
|
actualDigest,
|
|
420
|
-
|
|
416
|
+
approvalDigest,
|
|
421
417
|
);
|
|
422
418
|
await prepareAuthorityDirectory(dirname(immutableApprovalPath));
|
|
423
419
|
await assertAuthorityFileTarget(immutableApprovalPath);
|