release-skill 0.2.1 → 0.2.3
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 +4 -2
- package/.codex-plugin/plugin.json +2 -2
- package/.kimi-plugin/plugin.json +1 -1
- package/CHANGELOG.md +40 -0
- package/INSTALL.md +183 -21
- package/INSTALL.zh-CN.md +160 -16
- package/README.md +112 -13
- package/README.zh-CN.md +72 -11
- 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 +2240 -587
- package/adapters/claude/schemas/release-plan.schema.json +44 -2
- package/adapters/claude/schemas/release-project.schema.json +46 -4
- package/adapters/claude/schemas/release-run.schema.json +1 -0
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/release-skill.bundle.mjs +2240 -587
- package/adapters/codex/schemas/release-plan.schema.json +44 -2
- package/adapters/codex/schemas/release-project.schema.json +46 -4
- package/adapters/codex/schemas/release-run.schema.json +1 -0
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +2240 -587
- package/adapters/kimi/schemas/release-plan.schema.json +44 -2
- package/adapters/kimi/schemas/release-project.schema.json +46 -4
- package/adapters/kimi/schemas/release-run.schema.json +1 -0
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +4 -2
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +2240 -587
- package/adapters/workbuddy/schemas/release-plan.schema.json +44 -2
- package/adapters/workbuddy/schemas/release-project.schema.json +46 -4
- package/adapters/workbuddy/schemas/release-run.schema.json +1 -0
- package/bin/release-skill-cli.mjs +46 -4
- package/bin/release-skill.bundle.mjs +2240 -587
- package/package.json +1 -1
- package/references/02-project-config.md +7 -0
- package/references/06-adapter-contract.md +21 -2
- package/schemas/release-plan.schema.json +44 -2
- package/schemas/release-project.schema.json +46 -4
- package/schemas/release-run.schema.json +1 -0
- package/scripts/sync-public-files.mjs +8 -4
- package/src/adapters/contract.mjs +1 -0
- package/src/adapters/plugin-marketplace.mjs +796 -41
- package/src/commands/assess.mjs +50 -1
- package/src/commands/prepare.mjs +342 -9
- package/src/commands/publish.mjs +1 -0
- package/src/commands/reconcile.mjs +1 -0
- package/src/commands/setup.mjs +7 -3
- package/src/commands/verify.mjs +13 -5
- package/src/core/baseline.mjs +13 -0
- package/src/core/checkpoints.mjs +7 -2
- package/src/core/plan.mjs +275 -4
- package/src/core/verification-gates.mjs +1 -1
- package/src/platforms/codebuddy.mjs +658 -0
- package/src/platforms/registry.mjs +258 -5
- package/src/producers/build-adapters.mjs +30 -15
package/src/core/plan.mjs
CHANGED
|
@@ -776,6 +776,19 @@ export function validatePlanActionCompleteness(plan, options = {}) {
|
|
|
776
776
|
);
|
|
777
777
|
}
|
|
778
778
|
|
|
779
|
+
// External independent marketplace form: the distribution declares
|
|
780
|
+
// marketplaceRepo, so the install targets the external marketplace
|
|
781
|
+
// repository instead of publicRepo. Only platforms with a marketplace
|
|
782
|
+
// add capability (marketplaceRefForm !== null: claude=name, codex=sha)
|
|
783
|
+
// may carry it; kimi/codebuddy fail closed. Inline form (no
|
|
784
|
+
// marketplaceRepo) keeps every assertion below byte-for-byte.
|
|
785
|
+
const externalMarketplace = dist.marketplaceRepo !== undefined && dist.marketplaceRepo !== null;
|
|
786
|
+
if (externalMarketplace && platform.marketplaceRefForm === null) {
|
|
787
|
+
failures.push(
|
|
788
|
+
`unit "${unitId}", action "${action.id}": ${platform.distributionType} distribution declares marketplaceRepo but the platform has no marketplace add capability`,
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
|
|
779
792
|
// Parameter checks
|
|
780
793
|
_checkRequired(action, 'parameters.consumer', action.parameters?.consumer, platform.id, unitId, failures);
|
|
781
794
|
_checkRequired(action, 'parameters.plugin', action.parameters?.plugin, plugin, unitId, failures);
|
|
@@ -792,12 +805,53 @@ export function validatePlanActionCompleteness(plan, options = {}) {
|
|
|
792
805
|
`unit "${unitId}", action "${action.id}": parameters.marketplace is "${action.parameters.marketplace}", expected optional legacy value "${marketplace}"`,
|
|
793
806
|
);
|
|
794
807
|
}
|
|
795
|
-
|
|
808
|
+
if (externalMarketplace) {
|
|
809
|
+
// External form: the install repo is the external marketplace repo,
|
|
810
|
+
// not publicRepo; marketplaceLocation is the action-level marker.
|
|
811
|
+
_checkRequired(action, 'parameters.repo', action.parameters?.repo, dist.marketplaceRepo, unitId, failures);
|
|
812
|
+
_checkRequired(action, 'parameters.marketplaceLocation', action.parameters?.marketplaceLocation, 'external', unitId, failures);
|
|
813
|
+
} else {
|
|
814
|
+
_checkRequired(action, 'parameters.repo', action.parameters?.repo, publicRepo, unitId, failures);
|
|
815
|
+
}
|
|
796
816
|
_checkRequired(action, 'parameters.version', action.parameters?.version, targetVersion, unitId, failures);
|
|
797
817
|
_checkRequired(action, 'parameters.entrySkill', action.parameters?.entrySkill, entrySkill, unitId, failures);
|
|
798
818
|
if (production) {
|
|
819
|
+
// snapshotPath/manifestDigest always bind the unit's own frozen
|
|
820
|
+
// snapshot — the payload authority is unchanged for external form.
|
|
799
821
|
_checkRequired(action, 'parameters.snapshotPath', action.parameters?.snapshotPath, frozen?.path, unitId, failures);
|
|
800
|
-
|
|
822
|
+
if (externalMarketplace) {
|
|
823
|
+
// The add-ref is frozen online by prepare (codex=commit sha,
|
|
824
|
+
// claude=default branch name) and cannot be re-derived offline;
|
|
825
|
+
// validate structural safety plus self-consistency with expected.ref.
|
|
826
|
+
const ref = action.parameters?.ref;
|
|
827
|
+
if (ref === undefined || ref === null) {
|
|
828
|
+
failures.push(
|
|
829
|
+
`unit "${unitId}", action "${action.id}": parameters.ref is missing, expected the frozen external marketplace add-ref`,
|
|
830
|
+
);
|
|
831
|
+
} else if (!_isStructurallySafeExternalRef(ref)) {
|
|
832
|
+
failures.push(
|
|
833
|
+
`unit "${unitId}", action "${action.id}": parameters.ref is not a structurally safe ref: ${JSON.stringify(ref)}`,
|
|
834
|
+
);
|
|
835
|
+
} else if (ref !== action.expected?.ref) {
|
|
836
|
+
failures.push(
|
|
837
|
+
`unit "${unitId}", action "${action.id}": parameters.ref is "${ref}", expected self-consistent expected.ref "${action.expected?.ref}"`,
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
// marketplaceCommitSha is the frozen 40-hex marketplace HEAD
|
|
841
|
+
// (online freeze, offline completeness — same model as manifestDigest).
|
|
842
|
+
const sha = action.parameters?.marketplaceCommitSha;
|
|
843
|
+
if (sha === undefined || sha === null) {
|
|
844
|
+
failures.push(
|
|
845
|
+
`unit "${unitId}", action "${action.id}": parameters.marketplaceCommitSha is missing, expected a frozen 40-hex commit sha`,
|
|
846
|
+
);
|
|
847
|
+
} else if (typeof sha !== 'string' || !EXTERNAL_MARKETPLACE_SHA_RE.test(sha)) {
|
|
848
|
+
failures.push(
|
|
849
|
+
`unit "${unitId}", action "${action.id}": parameters.marketplaceCommitSha must be a 40-hex commit sha, got: ${JSON.stringify(sha)}`,
|
|
850
|
+
);
|
|
851
|
+
}
|
|
852
|
+
} else {
|
|
853
|
+
_checkRequired(action, 'parameters.ref', action.parameters?.ref, expectedTag, unitId, failures);
|
|
854
|
+
}
|
|
801
855
|
_checkRequired(action, 'parameters.manifestDigest', action.parameters?.manifestDigest, frozen?.manifestDigest, unitId, failures);
|
|
802
856
|
}
|
|
803
857
|
// timeoutMs is mandatory for all marketplace install actions.
|
|
@@ -828,6 +882,184 @@ export function validatePlanActionCompleteness(plan, options = {}) {
|
|
|
828
882
|
}
|
|
829
883
|
}
|
|
830
884
|
|
|
885
|
+
// sourceCommit: frozen plugin source commit, binding the marketplace
|
|
886
|
+
// install action to the unit's frozen snapshot commit. Both forms
|
|
887
|
+
// require it in production plans; non-production diagnostic plans may
|
|
888
|
+
// omit it (never冒充生产计划).
|
|
889
|
+
if (production) {
|
|
890
|
+
_checkRequired(action, 'parameters.sourceCommit', action.parameters?.sourceCommit, frozen?.commit, unitId, failures);
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// --- sourceDescriptor validation (marketplace source identity contract) ---
|
|
894
|
+
// Validates that marketplaceForm and sourceDescriptor.form are consistent,
|
|
895
|
+
// all form-specific fields are present and non-null, pluginRepo !== marketplaceRepo
|
|
896
|
+
// for standalone-index, and no mixed/extra fields exist.
|
|
897
|
+
// Only applies to platforms that require marketplace (claude, codex).
|
|
898
|
+
// Non-automatable platforms (kimi, codebuddy) use human-attestation
|
|
899
|
+
// and carry no marketplace form or source descriptor.
|
|
900
|
+
// Legacy plans (pre-sourceDescriptor) with legacyCompatibility are tolerated.
|
|
901
|
+
if (requiresMarketplace) {
|
|
902
|
+
const sd = action.parameters?.sourceDescriptor;
|
|
903
|
+
const mf = action.parameters?.marketplaceForm;
|
|
904
|
+
if (sd === undefined || sd === null) {
|
|
905
|
+
if (!options.legacyCompatibility) {
|
|
906
|
+
failures.push(
|
|
907
|
+
`unit "${unitId}", action "${action.id}": parameters.sourceDescriptor is missing; every marketplace install action must carry a complete source descriptor`,
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
} else if (typeof sd === 'object') {
|
|
911
|
+
// 1. marketplaceForm must be present when sourceDescriptor is present
|
|
912
|
+
if (mf === undefined || mf === null) {
|
|
913
|
+
failures.push(
|
|
914
|
+
`unit "${unitId}", action "${action.id}": parameters.marketplaceForm is missing but sourceDescriptor is present; form must be explicitly declared`,
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
// 2. sourceDescriptor.form must match marketplaceForm
|
|
918
|
+
if (sd.form !== mf) {
|
|
919
|
+
failures.push(
|
|
920
|
+
`unit "${unitId}", action "${action.id}": sourceDescriptor.form "${sd.form}" does not match parameters.marketplaceForm "${mf}"`,
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
// 3. Form-specific field completeness and non-null checks
|
|
924
|
+
// payloadDigest is allowed to be null in non-production plans
|
|
925
|
+
// (the frozen manifestDigest is only available after production freeze)
|
|
926
|
+
const BUNDLED_REQUIRED = ['form', 'repo', 'marketplaceEntry', 'pluginSubpath'];
|
|
927
|
+
const STANDALONE_REQUIRED = ['form', 'marketplaceRepo', 'marketplaceEntry', 'pluginRepo', 'sourceType'];
|
|
928
|
+
// Production-only fields that may be null in non-production plans
|
|
929
|
+
const PROD_ONLY_FIELDS = ['marketplaceCommitSha', 'ref', 'payloadDigest'];
|
|
930
|
+
|
|
931
|
+
if (sd.form === 'bundled-family') {
|
|
932
|
+
// Check bundled-family required fields are present and non-null
|
|
933
|
+
for (const field of BUNDLED_REQUIRED) {
|
|
934
|
+
if (sd[field] === undefined || sd[field] === null) {
|
|
935
|
+
failures.push(
|
|
936
|
+
`unit "${unitId}", action "${action.id}": sourceDescriptor.${field} is missing or null; bundled-family form requires all identity fields to be non-null`,
|
|
937
|
+
);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
// commit must be non-null in production plans (frozen from asset)
|
|
941
|
+
if (production && (sd.commit === undefined || sd.commit === null)) {
|
|
942
|
+
failures.push(
|
|
943
|
+
`unit "${unitId}", action "${action.id}": sourceDescriptor.commit is missing or null; production bundled-family plans require a frozen commit`,
|
|
944
|
+
);
|
|
945
|
+
}
|
|
946
|
+
// commit must match frozenSnapshot.commit (production binding)
|
|
947
|
+
if (production && sd.commit !== undefined && sd.commit !== null &&
|
|
948
|
+
frozen?.commit !== undefined && sd.commit !== frozen.commit) {
|
|
949
|
+
failures.push(
|
|
950
|
+
`unit "${unitId}", action "${action.id}": sourceDescriptor.commit does not match frozenSnapshot.commit`,
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
// payloadDigest must be non-null in production plans
|
|
954
|
+
if (production && (sd.payloadDigest === undefined || sd.payloadDigest === null)) {
|
|
955
|
+
failures.push(
|
|
956
|
+
`unit "${unitId}", action "${action.id}": sourceDescriptor.payloadDigest is missing or null; production plans require a frozen payload digest`,
|
|
957
|
+
);
|
|
958
|
+
}
|
|
959
|
+
// Prohibit standalone-index fields in bundled-family
|
|
960
|
+
const STANDALONE_ONLY = ['marketplaceRepo', 'pluginRepo', 'sourceType', 'marketplaceCommitSha', 'ref'];
|
|
961
|
+
for (const field of STANDALONE_ONLY) {
|
|
962
|
+
if (sd[field] !== undefined) {
|
|
963
|
+
failures.push(
|
|
964
|
+
`unit "${unitId}", action "${action.id}": sourceDescriptor.${field} is unexpected for bundled-family form; mixed fields are prohibited`,
|
|
965
|
+
);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
// repo must match the action's repo (publicRepo for bundled-family)
|
|
969
|
+
if (sd.repo !== undefined && sd.repo !== null && sd.repo !== publicRepo) {
|
|
970
|
+
failures.push(
|
|
971
|
+
`unit "${unitId}", action "${action.id}": sourceDescriptor.repo "${sd.repo}" does not match unit publicRepo "${publicRepo}"`,
|
|
972
|
+
);
|
|
973
|
+
}
|
|
974
|
+
// marketplaceEntry must match action.plugin
|
|
975
|
+
if (sd.marketplaceEntry !== undefined && sd.marketplaceEntry !== null && sd.marketplaceEntry !== plugin) {
|
|
976
|
+
failures.push(
|
|
977
|
+
`unit "${unitId}", action "${action.id}": sourceDescriptor.marketplaceEntry "${sd.marketplaceEntry}" does not match action plugin "${plugin}"`,
|
|
978
|
+
);
|
|
979
|
+
}
|
|
980
|
+
// payloadDigest must match manifestDigest (when both are present)
|
|
981
|
+
if (production && sd.payloadDigest !== undefined && sd.payloadDigest !== null &&
|
|
982
|
+
frozen?.manifestDigest !== undefined && sd.payloadDigest !== frozen.manifestDigest) {
|
|
983
|
+
failures.push(
|
|
984
|
+
`unit "${unitId}", action "${action.id}": sourceDescriptor.payloadDigest does not match frozen manifestDigest`,
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
} else if (sd.form === 'standalone-index') {
|
|
988
|
+
// Check standalone-index required fields are present and non-null
|
|
989
|
+
for (const field of STANDALONE_REQUIRED) {
|
|
990
|
+
if (sd[field] === undefined || sd[field] === null) {
|
|
991
|
+
failures.push(
|
|
992
|
+
`unit "${unitId}", action "${action.id}": sourceDescriptor.${field} is missing or null; standalone-index form requires all identity fields to be non-null`,
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
// Production-only fields: must be non-null in production plans
|
|
997
|
+
if (production) {
|
|
998
|
+
for (const field of PROD_ONLY_FIELDS) {
|
|
999
|
+
if (sd[field] === undefined || sd[field] === null) {
|
|
1000
|
+
failures.push(
|
|
1001
|
+
`unit "${unitId}", action "${action.id}": sourceDescriptor.${field} is missing or null; production standalone-index requires all fields to be non-null`,
|
|
1002
|
+
);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
// Prohibit bundled-family fields in standalone-index
|
|
1007
|
+
const BUNDLED_ONLY = ['repo', 'commit', 'pluginSubpath'];
|
|
1008
|
+
for (const field of BUNDLED_ONLY) {
|
|
1009
|
+
if (sd[field] !== undefined) {
|
|
1010
|
+
failures.push(
|
|
1011
|
+
`unit "${unitId}", action "${action.id}": sourceDescriptor.${field} is unexpected for standalone-index form; mixed fields are prohibited`,
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
// pluginRepo must NOT equal marketplaceRepo
|
|
1016
|
+
if (sd.pluginRepo !== undefined && sd.pluginRepo !== null &&
|
|
1017
|
+
sd.marketplaceRepo !== undefined && sd.marketplaceRepo !== null &&
|
|
1018
|
+
sd.pluginRepo === sd.marketplaceRepo) {
|
|
1019
|
+
failures.push(
|
|
1020
|
+
`unit "${unitId}", action "${action.id}": sourceDescriptor.pluginRepo must not equal marketplaceRepo; the plugin repo and marketplace repo are distinct repositories`,
|
|
1021
|
+
);
|
|
1022
|
+
}
|
|
1023
|
+
// marketplaceRepo must match action.repo (for standalone-index, repo is the external marketplace)
|
|
1024
|
+
if (externalMarketplace && sd.marketplaceRepo !== undefined && sd.marketplaceRepo !== null &&
|
|
1025
|
+
sd.marketplaceRepo !== dist.marketplaceRepo) {
|
|
1026
|
+
failures.push(
|
|
1027
|
+
`unit "${unitId}", action "${action.id}": sourceDescriptor.marketplaceRepo "${sd.marketplaceRepo}" does not match distribution marketplaceRepo "${dist.marketplaceRepo}"`,
|
|
1028
|
+
);
|
|
1029
|
+
}
|
|
1030
|
+
// pluginRepo must match the unit's publicRepo
|
|
1031
|
+
if (sd.pluginRepo !== undefined && sd.pluginRepo !== null && sd.pluginRepo !== publicRepo) {
|
|
1032
|
+
failures.push(
|
|
1033
|
+
`unit "${unitId}", action "${action.id}": sourceDescriptor.pluginRepo "${sd.pluginRepo}" does not match unit publicRepo "${publicRepo}"`,
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
// marketplaceEntry must match action.plugin
|
|
1037
|
+
if (sd.marketplaceEntry !== undefined && sd.marketplaceEntry !== null && sd.marketplaceEntry !== plugin) {
|
|
1038
|
+
failures.push(
|
|
1039
|
+
`unit "${unitId}", action "${action.id}": sourceDescriptor.marketplaceEntry "${sd.marketplaceEntry}" does not match action plugin "${plugin}"`,
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
// sourceType must be 'marketplace-entry'
|
|
1043
|
+
if (sd.sourceType !== undefined && sd.sourceType !== null && sd.sourceType !== 'marketplace-entry') {
|
|
1044
|
+
failures.push(
|
|
1045
|
+
`unit "${unitId}", action "${action.id}": sourceDescriptor.sourceType "${sd.sourceType}" is unexpected; standalone-index sourceType must be "marketplace-entry"`,
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
// payloadDigest must match manifestDigest (when both are present)
|
|
1049
|
+
if (production && sd.payloadDigest !== undefined && sd.payloadDigest !== null &&
|
|
1050
|
+
frozen?.manifestDigest !== undefined && sd.payloadDigest !== frozen.manifestDigest) {
|
|
1051
|
+
failures.push(
|
|
1052
|
+
`unit "${unitId}", action "${action.id}": sourceDescriptor.payloadDigest does not match frozen manifestDigest`,
|
|
1053
|
+
);
|
|
1054
|
+
}
|
|
1055
|
+
} else {
|
|
1056
|
+
failures.push(
|
|
1057
|
+
`unit "${unitId}", action "${action.id}": sourceDescriptor.form "${sd.form}" is unknown; expected "bundled-family" or "standalone-index"`,
|
|
1058
|
+
);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
831
1063
|
// Expected checks. Marketplace identity is bound only on platforms
|
|
832
1064
|
// whose schema requires it (MINOR-1): a non-marketplace platform's
|
|
833
1065
|
// observation never binds a marketplace.
|
|
@@ -840,8 +1072,15 @@ export function validatePlanActionCompleteness(plan, options = {}) {
|
|
|
840
1072
|
_checkRequired(action, 'expected.entrySkill', action.expected?.entrySkill, entrySkill, unitId, failures);
|
|
841
1073
|
if (production) {
|
|
842
1074
|
_checkRequired(action, 'expected.consumer', action.expected?.consumer, platform.id, unitId, failures);
|
|
843
|
-
|
|
844
|
-
|
|
1075
|
+
if (externalMarketplace) {
|
|
1076
|
+
_checkRequired(action, 'expected.repo', action.expected?.repo, dist.marketplaceRepo, unitId, failures);
|
|
1077
|
+
_checkRequired(action, 'expected.ref', action.expected?.ref, action.parameters?.ref, unitId, failures);
|
|
1078
|
+
_checkRequired(action, 'expected.marketplaceLocation', action.expected?.marketplaceLocation, 'external', unitId, failures);
|
|
1079
|
+
_checkRequired(action, 'expected.marketplaceCommitSha', action.expected?.marketplaceCommitSha, action.parameters?.marketplaceCommitSha, unitId, failures);
|
|
1080
|
+
} else {
|
|
1081
|
+
_checkRequired(action, 'expected.repo', action.expected?.repo, publicRepo, unitId, failures);
|
|
1082
|
+
_checkRequired(action, 'expected.ref', action.expected?.ref, expectedTag, unitId, failures);
|
|
1083
|
+
}
|
|
845
1084
|
_checkRequired(action, 'expected.entrySkillFound', action.expected?.entrySkillFound, true, unitId, failures);
|
|
846
1085
|
_checkRequired(action, 'expected.manifestDigest', action.expected?.manifestDigest, frozen?.manifestDigest, unitId, failures);
|
|
847
1086
|
}
|
|
@@ -946,3 +1185,35 @@ function _checkRequired(action, fieldPath, actual, expected, unitId, failures) {
|
|
|
946
1185
|
);
|
|
947
1186
|
}
|
|
948
1187
|
}
|
|
1188
|
+
|
|
1189
|
+
// External independent marketplace form (external-marketplace): a frozen
|
|
1190
|
+
// marketplace HEAD commit sha is a 40-char lowercase hex string.
|
|
1191
|
+
const EXTERNAL_MARKETPLACE_SHA_RE = /^[0-9a-f]{40}$/;
|
|
1192
|
+
|
|
1193
|
+
/**
|
|
1194
|
+
* Structural safety check for an externally frozen marketplace add-ref.
|
|
1195
|
+
*
|
|
1196
|
+
* The add-ref (codex=commit sha, claude=default branch name) is frozen online
|
|
1197
|
+
* by prepare and cannot be re-derived offline; full injection-safety validation
|
|
1198
|
+
* already happened at freeze time (plugin-marketplace `validateSafeRef`). The
|
|
1199
|
+
* offline completeness gate re-checks structural well-formedness so a tampered
|
|
1200
|
+
* or corrupted frozen ref fails closed here too. Mirrors that validator's rules.
|
|
1201
|
+
*
|
|
1202
|
+
* @param {unknown} ref
|
|
1203
|
+
* @returns {boolean}
|
|
1204
|
+
*/
|
|
1205
|
+
function _isStructurallySafeExternalRef(ref) {
|
|
1206
|
+
if (typeof ref !== 'string' || ref.length === 0) return false;
|
|
1207
|
+
if (/[\x00-\x1f]/.test(ref)) return false;
|
|
1208
|
+
if (ref.startsWith('-')) return false;
|
|
1209
|
+
if (ref.includes('\\')) return false;
|
|
1210
|
+
if (ref.includes('//')) return false;
|
|
1211
|
+
if (ref.startsWith('/') || ref.endsWith('/')) return false;
|
|
1212
|
+
if (ref.endsWith('.')) return false;
|
|
1213
|
+
if (ref.endsWith('.lock')) return false;
|
|
1214
|
+
if (ref.includes('@{')) return false;
|
|
1215
|
+
if (ref === '@') return false;
|
|
1216
|
+
if (ref.includes('..')) return false;
|
|
1217
|
+
if (/[;|&`$(){}]/.test(ref)) return false;
|
|
1218
|
+
return /^[a-zA-Z0-9][a-zA-Z0-9._/-]*$/.test(ref);
|
|
1219
|
+
}
|
|
@@ -57,7 +57,7 @@ function validateGate(gate) {
|
|
|
57
57
|
if (!gate.scope || typeof gate.scope.unit !== 'string') {
|
|
58
58
|
throw gateError(gate, 'must declare scope.unit');
|
|
59
59
|
}
|
|
60
|
-
if (gate.phase === 'consumer-verify' && !['npm', 'claude-plugin', 'codex-plugin', 'kimi-plugin'].includes(gate.scope.distribution)) {
|
|
60
|
+
if (gate.phase === 'consumer-verify' && !['npm', 'claude-plugin', 'codex-plugin', 'kimi-plugin', 'codebuddy-plugin'].includes(gate.scope.distribution)) {
|
|
61
61
|
throw gateError(gate, 'consumer-verify must declare a supported scope.distribution');
|
|
62
62
|
}
|
|
63
63
|
if (!Array.isArray(gate.command) || gate.command.length === 0 || gate.command.some((value) => typeof value !== 'string')) {
|