Sync third-party marketplace plugins

Constraint: Public skills are published only by explicit administrator action unless they are tracked third-party market sources.
Confidence: high
Scope-risk: narrow
Directive: Keep private/internal skills out of the public marketplace.
Tested: Marketplace validation passed.
This commit is contained in:
KeyInfo Bot
2026-06-11 16:40:38 +08:00
parent 488e2bd620
commit bb633f3f31
40 changed files with 6305 additions and 2 deletions
+54 -2
View File
@@ -9,6 +9,7 @@ committing malformed marketplace data.
from __future__ import annotations
import json
import os
import re
import sys
from pathlib import Path
@@ -36,6 +37,50 @@ def require(condition: bool, message: str) -> None:
raise AssertionError(message)
def resolve_manifest_path(plugin_dir: Path, manifest_path: Path, value: str, field: str) -> Path:
require(value.startswith("./"), f"{manifest_path} {field} path must start with ./")
target = (plugin_dir / value[2:]).resolve()
plugin_root = plugin_dir.resolve()
require(
os.path.commonpath([str(plugin_root), str(target)]) == str(plugin_root),
f"{manifest_path} {field} path must stay inside plugin root: {value}",
)
return target
def validate_manifest_path(plugin_dir: Path, manifest_path: Path, value: str, field: str) -> Path:
target = resolve_manifest_path(plugin_dir, manifest_path, value, field)
require(target.exists(), f"{manifest_path} {field} target does not exist: {target}")
return target
def validate_hooks_field(plugin_dir: Path, manifest_path: Path, hooks: object) -> None:
"""Validate Codex-supported plugin hook declarations.
Codex supports a hook path string, a list of path strings, an inline hooks
object, or a list containing inline hooks objects. The marketplace only
verifies path safety and basic type shape; Codex owns hook schema details.
"""
if isinstance(hooks, str):
validate_manifest_path(plugin_dir, manifest_path, hooks, "hooks")
return
if isinstance(hooks, dict):
require(bool(hooks), f"{manifest_path} hooks inline object must not be empty")
return
if isinstance(hooks, list):
require(bool(hooks), f"{manifest_path} hooks list must not be empty")
for index, item in enumerate(hooks):
if isinstance(item, str):
validate_manifest_path(plugin_dir, manifest_path, item, f"hooks[{index}]")
elif isinstance(item, dict):
require(bool(item), f"{manifest_path} hooks[{index}] inline object must not be empty")
else:
raise AssertionError(f"{manifest_path} hooks[{index}] must be a path or inline object")
return
raise AssertionError(f"{manifest_path} hooks must be a path, path list, inline object, or inline object list")
def validate_manifest(plugin_dir: Path, expected_name: str) -> None:
manifest_path = plugin_dir / ".codex-plugin" / "plugin.json"
require(manifest_path.exists(), f"Missing manifest: {manifest_path}")
@@ -44,20 +89,27 @@ def validate_manifest(plugin_dir: Path, expected_name: str) -> None:
require(manifest.get("name") == expected_name, f"{manifest_path} name must be {expected_name}")
require(SEMVER_RE.match(str(manifest.get("version", ""))) is not None, f"{manifest_path} version must be semver-like")
require(bool(manifest.get("description")), f"{manifest_path} missing description")
require("hooks" not in manifest, f"{manifest_path} must not declare unsupported hooks field")
if "hooks" in manifest:
validate_hooks_field(plugin_dir, manifest_path, manifest["hooks"])
author = manifest.get("author")
require(isinstance(author, dict) and bool(author.get("name")), f"{manifest_path} missing author.name")
skills_rel = manifest.get("skills")
require(isinstance(skills_rel, str) and skills_rel.startswith("./"), f"{manifest_path} skills path must be relative ./...")
skills_dir = plugin_dir / skills_rel[2:]
skills_dir = validate_manifest_path(plugin_dir, manifest_path, skills_rel, "skills")
require(skills_dir.exists(), f"{manifest_path} skills directory does not exist: {skills_dir}")
skill_dirs = [path for path in skills_dir.iterdir() if path.is_dir()]
require(bool(skill_dirs), f"{manifest_path} must contain at least one skill directory")
for skill_dir in skill_dirs:
require((skill_dir / "SKILL.md").exists(), f"Missing SKILL.md in {skill_dir}")
for optional_path_field in ["mcpServers", "apps"]:
value = manifest.get(optional_path_field)
if value is not None:
require(isinstance(value, str), f"{manifest_path} {optional_path_field} must be a path string")
validate_manifest_path(plugin_dir, manifest_path, value, optional_path_field)
interface = manifest.get("interface")
require(isinstance(interface, dict), f"{manifest_path} missing interface object")
for key in ["displayName", "shortDescription", "longDescription", "developerName", "category"]: