Files
market/README.md
Yige e77c658958 feat: 市场支持团队条目类型并上架合同审查团队 (#110)
## 变更 / 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>
2026-09-01 04:15:45 -04:00

347 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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).