mirror of
https://git.openapi.site/https://github.com/desirecore/market.git
synced 2026-09-05 20:03:43 +08:00
## 变更 / What
市场此前只有 `agents` 与 `skills` 两类条目。本 PR 加入**团队(teams)**条目类型,并上架第一条真实团队
listing。
The market supported only `agents` and `skills`. This PR adds a
**teams** entry type and lists the first real team.
## 一、支持团队条目类型
「支持一种新条目类型」实际涉及 4 组共 11 个文件,比表面看到的多:
**客户端契约快照**
- 新增 `schemas/market-team-entry.client.schema.json`,用 esbuild 打包客户端
`packages/schemas/src/market.ts` 后导出生成。用同样方法重新生成
`market-agent-entry.client.schema.json` 验证过管线——字节完全一致(含属性顺序),确认不是手工誊抄。
**Sidecar schema**
- `identity.kind` 枚举加 `team`;新增 `$defs.teamSpec`;接入 `spec.oneOf` 与
kind→spec 派发
**校验器(工作量主要在这里)**
- `scripts/catalog/validate_catalog_metadata.py`:`load_legacy`
原先硬编码只认两个根目录。抽出 `CATALOG_ROOTS` 常量同时驱动允许的父目录集合与错误文案;按 kind 分派客户端 schema
校验;`teams` 进 stats 与 `--require-complete` 覆盖统计;把**严格 provenance
比对**与「可安装 pointer 必须自带不可变 ref」两道门禁扩展到团队
- `scripts/i18n/validate-i18n.py`:**它独立重算计数并逐个校验 `entry.json`**,不接团队会漏校
- `.github/workflows/i18n-validate.yml`:变更检测的 grep 不含 `teams/`——**一个只改
teams 的 PR 会报「无 i18n 相关变更,跳过校验」然后零校验通过**
- 测试:`test_validate_catalog_metadata.py` 29→47,`test_validate_i18n.py`
9→17
**顺带修正一条本就不对的规则**:`icon` 此前被要求「每个 entry.json 都必须有非空内联 SVG」,但运行时 schema 里
`marketAgentSchema` 与 `marketTeamSchema` **都没有 `icon` 字段**(只有 skill
有)。也就是说这条规则对 Agent pointer 同样在强加死重量,只因本仓库暂无 agent pointer 条目而未暴露。改为
`ICON_RENDERED_KINDS = {"skill"}`,agent/team 声明 icon
时给**警告**而非错误,文案说明「下一个维护者会以为改它能改变卡片」。
## 二、上架合同审查团队
`teams/contract-review-team/`(`entry.json` + sidecar)。
**团队条目是 fork 指针卡,不分发正文**:市场只存展示元数据 + git-only `source`,真实定义(`team.json`
/ `members.json` / `shared/`)在 `source.repoUrl` 指向的仓库里。安装即
`forkTeam`,更新即 `git pull`——组合固定,因此**没有** `installPolicy` /
`updatePolicy`。
| 字段 | 值 | 依据 |
|---|---|---|
| `source.ref` | `73cd87a9901cc548871927e9d5dbec8e4cc6c2b1` | v0.1.1
的**完整 SHA**。tag 不是可复现 pin,validator 有测试专门拒绝 |
| `latestVersion` | `0.1.1` | 上游真实 tag,与 `release.version` 交叉校验 |
| `license` | `MIT` | 上游仓库真有 LICENSE,已在 pinned ref 的快照中复验 |
| `redistribution` | `source-pointer-only` |
市场从不打包团队正文,只给指针——这是交付形态,与许可证宽松与否无关 |
| `requiredClientVersion` | `10.0.137` | 六个成员都声明了 `FileDigest`
内置工具,它随该版本发布 |
| `memberCount` / `memberNames` | 6 / 5 名 | schema 规定前者**含**组长、后者**不含**
|
| `availability` | `listing-only` | 见下 |
**`availability` 为什么不是 `installable`**:四项证据满足两项(不可变 pin ✓、已知 license
✓),缺的 `reviewedAt` 与 `governance.compliance`
本质是**一次尚未发生的治理审查**——需要具名方在具体日期针对这个确切 ref 审过许可合规、第三方内容与商标使用。没发生的事不能写进目录。
补充一个事实:本仓库**零个 sidecar 有 `compliance` 块,29 个 pointer 条目全是
listing-only**,`installable` 路径从未在任何真实条目上走过。这不阻止安装——fork 由 `source` 驱动。
**`license.evidencePath` 的基准此前是未定义的**:schema 只说
`safeRelativePath`,没规定相对谁。仓库里仅有的两个先例(`guizang-ppt`、`presentation-forge`)都是
vendored 技能,LICENSE 物理上在条目目录里。按那个读法,pointer 条目写 `evidencePath`
断言的是市场目录下有该文件——对 pointer 永远不成立。新增 `license-evidence` 规则按条目形态分派:vendored
要求文件存在(error),pointer 要求条目已 pin(warning),两种读法写进 README。
## 校验 / Validation
```
test_validate_catalog_metadata.py 47 tests OK
test_validate_i18n.py 17 tests OK
test_collection_generator.py exit 0
validate_catalog_metadata.py --require-complete
0 error, 116 warning (agents=1, teams=1, publishableSkills=62, sidecars=64)
validate-i18n.py / --online 0 error, 116 warning
translate.py --check exit 0
gen-collection-children.py --check exit 0
```
116 warnings 即加入团队之前的基线——**本条 listing 贡献 0 个警告**。
真实条目上的反向控制(跑在 rsync 副本上,仓库保持干净):
```
source.kind=zip → team-entry-schema (error)
install/updatePolicy 出现 → team-entry-schema (error)
requiredClientVersion 漂移 → legacy-consistency (error)
memberCount 漂移 → legacy-consistency (error)
provenance ref 漂移 → legacy-consistency (error)
可安装但无不可变 ref → installable-evidence (error)
evidencePath 在未 pin 的 pointer → license-evidence (warning)
```
另用**客户端真实校验器**(`parseMarketTeamEntry`,不是快照)验证条目通过,且多写一个字段会被拒。
## 公开信息边界 / Public information boundary
全树扫描无新增命中。团队内容使用「某某科技(北京)有限公司」这类标准中文占位。
---------
Co-authored-by: yi-ge <mizan57533@gmail.com>
347 lines
15 KiB
Markdown
347 lines
15 KiB
Markdown
# DesireCore Market
|
||
|
||
DesireCore 官方市场仓库,存放官方维护的 Agent/Team/Skill 定义,以及经过整理的第三方 Skill 入口。
|
||
|
||
## Repository Shape
|
||
|
||
```
|
||
.
|
||
├── manifest.json # Market metadata, supported locales, aggregate stats
|
||
├── categories.json # Category registry and localized labels
|
||
├── builtin-skills.json # Built-in local SKILL.md skills
|
||
├── agents/
|
||
│ └── desirecore/
|
||
│ └── agent.json
|
||
├── teams/
|
||
│ └── <team>/
|
||
│ └── entry.json
|
||
└── skills/
|
||
├── <local-skill>/
|
||
│ ├── SKILL.md
|
||
│ └── SKILL.<locale>.md
|
||
└── <external-entry>/
|
||
└── entry.json
|
||
```
|
||
|
||
The market currently contains:
|
||
|
||
- `1` Agent: `desirecore`
|
||
- `1` Team: `contract-review-team`
|
||
- `34` local built-in skills with `SKILL.md`
|
||
- `28` external skill entries with `entry.json`
|
||
- `62` publishable skills in total (`SKILL.md` + `entry.json`)
|
||
|
||
## Skill Sources
|
||
|
||
Local built-in skills are installable from this repository and must be listed in `builtin-skills.json`:
|
||
|
||
```text
|
||
code-intelligence, configuring-compute, create-agent, dashscope-image-gen, delete-agent,
|
||
dev-environment-setup, discover-agent, docx, frontend-design, guizang-ppt,
|
||
image-to-image, mail-operations, manage-skills, manage-teams, markdown,
|
||
minimax-music-gen, minimax-video-gen, nodejs-runtime, pdf, pptx,
|
||
presentation-forge, python-runtime, registering-services, s3-storage-operations, skill-creator,
|
||
tech-diagram, update-agent, using-services, web-access, workflow, workforce-optimization,
|
||
xiaomi-tts, xlsx
|
||
```
|
||
|
||
`builtin-skills.json#retired` lists old built-in Skill IDs that clients may safely retire during
|
||
startup. Clients only remove copies tracked in `skills.lock` as market/bundled content whose
|
||
`SKILL.md` hash still matches the installed record; manually installed or locally modified copies
|
||
are preserved. An ID must not appear in both `skills` and `retired`.
|
||
|
||
External entries are marketplace pointers to Git/Web/ZIP sources:
|
||
|
||
```text
|
||
agent-reach, ai-news-radar, amap-jsapi-skill, baoyu-skills, dingtalk-api,
|
||
flyai-skill, follow-builders, humanizer, humanizer-zh,
|
||
ian-xiaohei-illustrations, impeccable, karpathy-guidelines, khazix-skills,
|
||
larksuite-cli, last30days, luckin-my-coffee, marketingskills,
|
||
mattpocock-skills, minimax-image-gen, minimax-tts, mt-paotui-for-client,
|
||
netease-skills, nuwa-skill, taste-skill, watch, watchless,
|
||
wechatpay-skills, wecom-cli
|
||
```
|
||
|
||
## Data Formats
|
||
|
||
### Local Skill (`skills/<id>/SKILL.md`)
|
||
|
||
Local skills use YAML frontmatter plus Markdown body. The top-level `name` must equal the directory slug. Display strings live in `metadata.i18n`.
|
||
|
||
```yaml
|
||
---
|
||
name: web-access
|
||
description: >-
|
||
Use this skill when ...
|
||
version: 2.0.1
|
||
type: procedural
|
||
risk_level: low
|
||
status: enabled
|
||
metadata:
|
||
author: desirecore
|
||
updated_at: '2026-05-05'
|
||
i18n:
|
||
default_locale: en-US
|
||
source_locale: zh-CN
|
||
locales: [zh-CN, en-US]
|
||
zh-CN:
|
||
name: 联网访问
|
||
short_desc: 联网搜索、网页抓取、登录态浏览器访问
|
||
body: ./SKILL.zh-CN.md
|
||
translated_by: human
|
||
en-US:
|
||
name: Web Access
|
||
short_desc: Web search, page fetching, logged-in browser access
|
||
body: ./SKILL.md
|
||
source_hash: sha256:...
|
||
translated_by: human
|
||
market:
|
||
category: research
|
||
channel: latest
|
||
maintainer:
|
||
name: DesireCore Official
|
||
verified: true
|
||
---
|
||
```
|
||
|
||
### External Entry (`skills/<id>/entry.json`)
|
||
|
||
External entries point to upstream packages or repositories. They are counted in `manifest.stats.totalSkills` but are not included in `builtin-skills.json`.
|
||
|
||
```json
|
||
{
|
||
"id": "example-skill",
|
||
"name": "Example Skill",
|
||
"category": "development",
|
||
"icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\">...</svg>",
|
||
"tags": ["example"],
|
||
"maintainer": {
|
||
"name": "Example",
|
||
"verified": false,
|
||
"account": "example",
|
||
"url": "https://github.com/example/example-skill"
|
||
},
|
||
"stewardship": "community",
|
||
"license": "MIT",
|
||
"redistribution": "allowed",
|
||
"source": {
|
||
"kind": "git",
|
||
"repoUrl": "https://github.com/example/example-skill.git",
|
||
"repoBranch": "main"
|
||
}
|
||
}
|
||
```
|
||
|
||
### Team Listing (`teams/<id>/entry.json`)
|
||
|
||
A Team is a group of Agents with a supervisor. It is published as a **fork pointer
|
||
only**: the team body (`team.json`, `members.json`, `shared/`) always stays in the
|
||
upstream team repository, and the catalog registers just "what it is and where to
|
||
fork it from". Installation forks that repository and installs the declared members;
|
||
updates are a `git pull` on the fork. Because both actions are Git actions,
|
||
`source.kind` must be `git` and `source.repoUrl` is required — `zip` and `web` cannot
|
||
express either one — and a Team deliberately has **no** `installPolicy` /
|
||
`updatePolicy` pair: it is always market-initiated fork plus repository-driven update.
|
||
|
||
`teams/<id>/` therefore holds exactly `entry.json` plus the sidecar. There is no
|
||
inline form; a `team.json` in the catalog is rejected.
|
||
|
||
```json
|
||
{
|
||
"id": "example-team",
|
||
"name": "Example Team",
|
||
"category": "development",
|
||
"tags": ["example"],
|
||
"latestVersion": "0.1.0",
|
||
"maintainer": {
|
||
"name": "Example",
|
||
"verified": false,
|
||
"account": "example",
|
||
"url": "https://github.com/example"
|
||
},
|
||
"stewardship": "community",
|
||
"license": "MIT",
|
||
"redistribution": "source-pointer-only",
|
||
"source": {
|
||
"kind": "git",
|
||
"repoUrl": "https://github.com/example/example-team.git",
|
||
"repoBranch": "main",
|
||
"ref": "0123456789abcdef0123456789abcdef01234567"
|
||
},
|
||
"requiredClientVersion": "10.0.0",
|
||
"avatar": { "t": "示", "bg": "linear-gradient(135deg, #5856D6, #3634A3)" },
|
||
"supervisorName": "Example Supervisor",
|
||
"supervisorAgentId": "example-lead",
|
||
"memberCount": 3,
|
||
"memberNames": ["Example Member One", "Example Member Two"]
|
||
}
|
||
```
|
||
|
||
A Team card is rendered from `avatar`, not from `icon`. The client's runtime
|
||
projection `marketTeamSchema` requires `avatar` and has no `icon` property at all —
|
||
the same is true of `marketAgentSchema`, while only `marketSkillSchema` exposes
|
||
`icon`. The entry contract inherits `icon` from the shared common properties, so it
|
||
is *accepted*, but it can never reach a card. The validator therefore requires `icon`
|
||
on Skill listings only, and warns when an Agent or Team listing declares one.
|
||
|
||
`redistribution` stays `source-pointer-only` for a Team even under a permissive
|
||
license: the market never ships the team body, it only points at the repository the
|
||
client forks. The license governs what a fork may do; `redistribution` describes how
|
||
the content is delivered, and for teams that is always "fetch from upstream".
|
||
|
||
`supervisorName`, `supervisorAgentId`, `memberCount`, `memberNames` and
|
||
`requiredSkills` are display metadata declared by the publisher. They may drift from
|
||
the upstream repository, so installation, permissions and member resolution must read
|
||
the forked `team.json` / `members.json` instead. Teams are counted in
|
||
`manifest.stats.totalTeams`, which the client keeps optional: a catalog with no teams
|
||
may omit it, and once the key is present it must be exact.
|
||
|
||
### Catalog metadata sidecar (`catalog-metadata.v1.json`)
|
||
|
||
The versioned catalog metadata contract is stored at one fixed path next to each
|
||
legacy item:
|
||
|
||
```text
|
||
agents/<id>/catalog-metadata.v1.json
|
||
teams/<id>/catalog-metadata.v1.json
|
||
skills/<id>/catalog-metadata.v1.json
|
||
```
|
||
|
||
Legacy `agent.json`, `SKILL.md`, and `entry.json` files remain the compatibility
|
||
surface for older clients. New clients merge the sidecar through a deterministic
|
||
adapter. Any field repeated in both files must have the same value; the validator
|
||
rejects drift rather than choosing one copy silently.
|
||
|
||
Agent listings support exactly one of `agents/<slug>/agent.json` (inline metadata)
|
||
or `agents/<slug>/entry.json` (an external pointer), alongside the sidecar. Missing
|
||
or simultaneous primary files are rejected. For a pointer, `entry.id` and sidecar
|
||
`identity.id` use the catalog directory slug and `identity.kind` is `agent`; the
|
||
upstream AgentFS `agent.json.id` remains its own UUID and must not be rewritten.
|
||
|
||
Agent pointers first pass the complete raw client contract in
|
||
[`schemas/market-agent-entry.client.schema.json`](schemas/market-agent-entry.client.schema.json),
|
||
exported from `marketAgentEntrySchema` in the DesireCore repository at commit
|
||
`18bbb86f62e1288b1f945209bed74ec72620a9d4`. The schema's `$comment` records the
|
||
source blob as well. Refresh this generated snapshot from the TypeScript export
|
||
when changing client compatibility; do not replace it with permissive sidecar
|
||
validation. Version fields keep their original types and the client's supported
|
||
format. Installation/update policies must either both be absent (effective
|
||
`market/market`) or form a complete supported pair; the sidecar must preserve
|
||
that effective pair.
|
||
|
||
Agent pointer `latestVersion` maps to sidecar `release.version`; optional
|
||
`requiredClientVersion`, `installPolicy`, and `updatePolicy` must agree with the
|
||
sidecar compatibility/spec fields. Pointer source fields must describe the same
|
||
artifact as `provenance.content`, and `maintainer` maps to `upstreamMaintainer`.
|
||
An installable Agent pointer must itself pin `source.ref` (Git) or `source.sha256`
|
||
(Web/ZIP); an immutable ref supplied only by the sidecar cannot pin a mutable
|
||
entry. Existing immutable-source, license, governance-review and complete-coverage
|
||
checks still apply. Agent pointers do not receive the built-in Skill exceptions.
|
||
|
||
Agent 目录必须在 `agent.json` 内联元数据和 `entry.json` 外部指针中二选一,并提供 sidecar。
|
||
Pointer 原始 JSON 先通过固定客户端提交导出的完整 Schema;版本类型与格式、来源路径和策略组合不能由 sidecar 掩盖。
|
||
Pointer 的目录 slug、`entry.id`、sidecar `identity.id` 必须一致;上游 AgentFS 的 UUID 不改写。
|
||
安装/更新策略双缺省时有效值仍是 `market/market`,sidecar 不得将其改成系统条目。
|
||
`latestVersion`、最低客户端版本和安装/更新策略须与 sidecar 对齐;来源必须是同一个制品。
|
||
可安装指针自身必须固定 Git ref 或 Web/ZIP 摘要,不能只在 sidecar 宣称不可变版本。
|
||
现有许可、治理审查、不可变来源和完整覆盖门禁继续有效,不适用内置 Skill 的宽松例外。
|
||
|
||
Team listings are pointer-only, so `teams/<slug>/` carries exactly `entry.json` plus
|
||
the sidecar; an inline `team.json` is rejected. Team pointers first pass the complete
|
||
raw client contract in
|
||
[`schemas/market-team-entry.client.schema.json`](schemas/market-team-entry.client.schema.json),
|
||
exported from `marketTeamEntrySchema` the same way as the Agent snapshot; its
|
||
`$comment` records the source commit and blob. `entry.id`, the directory slug and
|
||
sidecar `identity.id` must agree, `identity.kind` is `team`, and `latestVersion` maps
|
||
to `release.version`. `supervisorName`, `supervisorAgentId`, `memberCount`,
|
||
`memberNames`, `requiredSkills` and `requiredClientVersion` are compared symmetrically:
|
||
the sidecar may neither drop a fact the pointer declares nor invent one it omits,
|
||
because the client reads the pointer and a version gate that exists only in the
|
||
sidecar would not gate anything. Pointer source fields must describe the same
|
||
artifact as `provenance.content`, and an installable Team pointer must itself pin a
|
||
full-SHA `source.ref` — a tag is not a reproducible pin, because a tag can be moved
|
||
to a different commit after the listing is reviewed.
|
||
|
||
团队条目只有指针形态:`teams/<slug>/` 仅放 `entry.json` 与 sidecar,目录内出现 `team.json` 直接判非法。
|
||
Pointer 原始 JSON 先通过由 `marketTeamEntrySchema` 导出的完整客户端 Schema;
|
||
`source.kind` 恒为 `git` 且必须有 `repoUrl`,团队没有 `installPolicy` / `updatePolicy` 组合。
|
||
目录 slug、`entry.id`、sidecar `identity.id` 必须一致,`identity.kind` 为 `team`。
|
||
展示字段与最低客户端版本双向比对:sidecar 既不得丢弃指针声明的事实,也不得凭空补上指针没有的事实。
|
||
可安装团队指针自身必须固定完整 SHA 的 `source.ref`,tag 或分支不算可复现锁定。
|
||
|
||
The sidecar records source-owned presentation, release, timestamp, content
|
||
provenance, governance, compatibility, and type-specific facts. It deliberately
|
||
cannot declare `catalogSourceId`, catalog commit/path/trust, effective official
|
||
status, installation state, device state, health, URLs discovered at runtime, or
|
||
`syncedAt`. DesireCore injects trusted catalog provenance and runtime facts.
|
||
|
||
`license.evidencePath`, `compliance.licenseEvidencePath` and `compliance.noticePath`
|
||
resolve differently by item shape, because the schema constrains only the string
|
||
form. Vendored content (built-in Skills, inline Agents) ships inside this repository,
|
||
so the path is relative to the catalog item directory and the file must actually be
|
||
there — a missing file is an error. A pointer distributes nothing, so its evidence
|
||
can only be inside the upstream snapshot at the pinned revision; the validator cannot
|
||
read that offline, so it warns when such a claim is made against an unpinned pointer.
|
||
Pin `source.ref` to a full commit SHA and the claim becomes falsifiable by anyone who
|
||
fetches it.
|
||
|
||
Time facts are explicit `known`/`unknown` values. A known day uses
|
||
`YYYY-MM-DD` with `precision: "day"`; a known second uses an RFC 3339 UTC value
|
||
ending in `Z` with `precision: "second"`. Never use the current date, clone time,
|
||
or synchronization time to fill an unknown catalog or release timestamp.
|
||
|
||
Collection children stay in their parent's sidecar. Each child declares the
|
||
canonical `skill + parentId + id` identity and its own release fact; a collection
|
||
parent may have an unknown version, and a child version must not be inferred from
|
||
the parent.
|
||
|
||
The strict source schema is
|
||
[`schemas/catalog-metadata.v1.schema.json`](schemas/catalog-metadata.v1.schema.json).
|
||
|
||
## Categories
|
||
|
||
Valid category slugs are declared in `categories.json`:
|
||
|
||
```text
|
||
productivity, development, business, creative, design, media,
|
||
communication, research, data, management
|
||
```
|
||
|
||
## Validation
|
||
|
||
Run these checks before submitting changes:
|
||
|
||
```bash
|
||
# Full market + i18n validation
|
||
uv run scripts/i18n/validate-i18n.py
|
||
|
||
# Catalog sidecar validator unit tests and standalone validation
|
||
uv run scripts/catalog/test_validate_catalog_metadata.py
|
||
uv run scripts/catalog/test_collection_generator.py
|
||
uv run scripts/catalog/validate_catalog_metadata.py
|
||
|
||
# Translation freshness check
|
||
uv run scripts/i18n/translate.py --check
|
||
|
||
# Verify pinned collection children without changing entry.json (network required;
|
||
# mutable collections are reported and skipped because their output is not reproducible)
|
||
uv run scripts/gen-collection-children.py --check
|
||
|
||
# Optional network check for entry.json source URLs
|
||
uv run scripts/i18n/validate-i18n.py --online
|
||
```
|
||
|
||
The validators check market stats, category references, `builtin-skills.json`,
|
||
`entry.json` structure, sidecar schema and legacy consistency, immutable source
|
||
evidence, collection identity, i18n completeness, and translation freshness.
|
||
Human-locked translations (`translated_by: human`) must keep `source_hash`
|
||
aligned after manual review. During a data migration,
|
||
`scripts/catalog/validate_catalog_metadata.py --require-complete` additionally
|
||
requires one sidecar for every top-level Agent, Team and Skill.
|
||
|
||
Detailed i18n guidance is in [docs/I18N.md](docs/I18N.md).
|
||
|
||
## License
|
||
|
||
MIT License. See [LICENSE](LICENSE).
|