关于 community-publish
community-publish 是用于管理项目公开发布、开源代码和社区或服务市场上架的技能,面向需要分享或推广项目的用户。它将 URL 发布、项目广场展示与付费服务上架视为独立操作,并提供各自的状态查询流程。
主要功能
- 01发布和撤销公开预览 URL
- 02将项目代码推送到 GitHub
- 03免费项目上架与撤架
- 04创建、审核和发布付费服务
- 05查询发布、审核及上架状态
- 06关联公开预览与开源代码
适用场景
- 01分享正在运行的服务预览
- 02将项目代码发布到 GitHub
- 03把免费项目展示在项目广场
- 04提交付费服务审核并上架
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ starchild-ai-agent/ official-skills --skill community-publish安装后在 Claude Code 中输入/community-publish确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
__init__.pyexports.pylib/gateway.pyinstall.pymanifest.pyvalidate.py详细介绍
两个概念:发布(PUBLISH)与上架(LIST)——切勿混淆
此技能涉及两个本质不同的概念。混淆它们是给出错误答案的首要原因。
| 概念 | 含义 | 函数 |
|---|---|---|
| 发布(PUBLISH) | 让某项内容可访问——URL 可以正常使用,或代码已在 GitHub 上 | publish_preview、unpublish_preview、list_published_previews、open_source、remove_open_source、list_open_source、get_open_source、fork、validate_open_source |
| 上架(LIST) | 让某项内容在市场中可发现/可购买 | 免费:list_in_dashboard、unlist_from_dashboard、delete_listing、get_listing_status付费: create_paid_service、submit_for_review、get_review_status、publish_service、unpublish_service、list_my_services、get_service、update_service、delete_service、restore_service封面: upload_cover_image浏览与消费者: explore_services、get_service_detail、get_service_pricing、get_service_reviews、write_service_review、favorite_service、unfavorite_service、get_favorite_services、get_user_services、get_service_earnings、get_earnings_summary、get_service_tags、get_featured_services项目查询: explore_projects、my_projects、favorite_projects、get_tab_counts、get_popular_tags、get_user_projects、favorite_project、unfavorite_project |
发布不会自动上架。 publish_preview() 只会分配 URL。open_source() 只会推送代码。两者都不会让项目在市场中可被发现——这需要另行主动调用 LIST 函数。
上架有两种流程
| 流程 | 适用情况 | 需要审核? | 需要定价? | 函数 |
|---|---|---|---|---|
| 免费上架 | 免费项目,展示在 /projects 项目库中 | 否 | 否 | list_in_dashboard() |
| 付费上架 | 通过 x402 收取访问费用 | 需要(6 项检查的审核,发布前必须通过) | 是(平台网络上的 USDC/USDG/USDC(Solana)——默认 Base+Monad+Robinhood+X Layer+Solana,遵循 all) | create_paid_service() → submit_for_review()(必需)→ publish_service() |
POST /api/services不再接受service_type: "free_project"。 免费上架通过list_in_dashboard()完成(项目库流程)。付费上架通过create_paid_service()+ 审核 + 发布完成(服务 API 流程)。
限时免费促销 ≠ 此技能的内容
付费服务上架后,所有者可以开展限时免费促销
(free_promo_start / free_promo_end)。这不属于市场上架工作,
此处也未实现。
| 概念 | 内容 | 位置 |
|---|---|---|
| 免费上架 | /projects 项目库中的免费项目 | 此技能 → list_in_dashboard() |
free_trial_count | 收费前的 N 次免费调用(仅限 pay_per_use) | 此技能 → create_paid_service(..., free_trial_count=N) |
| 限时免费促销 | 指定时间段:金额为 0 的验证,不进行结算/扣款 | x402 技能 → skills/x402/references/selling.md 的 限时免费促销章节 |
如果用户要求对已付费上架的项目“开限时免费 / free promo / free for N days”:
请阅读 x402 技能(自检 P1–P5,然后 PUT 免费促销)。不要在
community-publish 中编造 API,也不要将其与 free_trial_count 混淆。
可见性模型——回答“其他人能看到吗”之前请先阅读
项目的“公开性”由三个相互独立的开关决定,而不是一个:
| 开关 | 关闭状态 | 开启状态 | 触发操作 |
|---|---|---|---|
| URL 访问权限 | 访问该 URL 会返回 404 | 任何拥有该链接的人都能访问 URL | publish_preview / unpublish_preview |
| Gallery 可发现性 | 不会显示在 /projects Gallery 中 | 会显示在 Gallery 中 | list_in_dashboard / unlist_from_dashboard |
| Marketplace 上架状态 | 不会显示在 Service Marketplace 中 | 可被发现并购买 | create_paid_service + publish_service / unpublish_service |
项目可以处于任意组合状态。切勿把这些状态统称为“是否已经公开”。
状态查询属于只读操作。 每当用户询问:
- “现在能看到吗 / 已公开吗 / 已经可以被发现了吗?”
- “上架了吗 / 在 dashboard 上吗 / 别人能看到吗”
- “上架信息现在生效了吗?”
权威答案只能来自新调用的 get_listing_status(slug)(免费)或 get_review_status(service_id)(付费)。不要根据之前的操作推断。
项目类型——仅有三种
| type | 含义 | 是否符合 publish_preview() 的条件? |
|---|---|---|
task | 定时 cron/interval 作业 | 否(没有 HTTP 端口) |
service | 长期运行的 HTTP 服务(dashboard、API、页面) | 是 |
script | 一次性脚本 | 否(没有 HTTP 端口) |
路由——将用户意图匹配到正确操作
A. 状态意图——用户想了解当前状态
| 示例说法 | 操作 |
|---|---|
| “现在能看到吗 / 已公开吗 / 已经可以被发现了吗 / 已上线吗?” | get_listing_status(slug) |
| “上架了吗 / 在 dashboard 上吗 / 别人能不能看到” | get_listing_status(slug) |
| “我发布了哪些 URL?” / “我发布了哪些” | list_published_previews() |
| “有哪些代码是开源的?” / “都有哪些开源代码” | list_open_source(...) |
| “我的服务” / “我的服务” / “我的付费服务” | list_my_services() |
| “审核状态” / “审核通过了吗” / “审核状态” | get_review_status(service_id) |
B. 操作意图——用户想要更改状态
| 示例表述 | 操作 | 说明 |
|---|---|---|
| "发布" / "分享" / "公开" / "公开" / "发布"(无限定条件) | publish_preview(preview_id) | 仅分配 URL。不会自动切换上架状态。 |
| "在仪表板上架" / "上架" / "在社区展示" / "设为可发现" / "发到广场" | list_in_dashboard(slug) | 免费上架。预览必须已存在。 |
| "上架付费服务" / "将其设为付费服务" / "上架到服务市场(付费)" | create_paid_service(...) → submit_for_review()(推荐)→ publish_service() | 付费上架。需先配置 x402。 |
| "发布并上架" / "发布并上架" | publish_preview(),然后调用 list_in_dashboard() | 按顺序分两次调用。 |
| "从仪表板移除" / "下架" / "取消上架" / "从展示墙隐藏" | unlist_from_dashboard(slug) | 仅限免费上架。软取消上架(设置 is_public=false、review_status='unlisted',保留统计数据)。预览 URL 仍可访问。 |
| "下架付费服务" / "取消发布服务" | unpublish_service(service_id) | 仅限付费上架。 |
| "开源" / "将代码开源" / "开源代码" | open_source(project_dir) | 将代码推送到 GitHub。不会上架。 |
| "取消发布 URL" / "移除链接" / "停止服务" | unpublish_preview(slug) | 仅停止预览容器服务。不会影响上架状态(is_public/review_status 保持不变)。URL 将无法访问(404)。 |
| "移除开源项目" / "从 GitHub 删除" | remove_open_source(slug) | |
| "分叉" / "安装别人的项目" | fork(source) | |
| "提交审核" / "提交审核" | submit_for_review(service_id) | 仅限付费服务。必需——发布前必须通过审核。 |
| "发布服务" / "发布我的服务" | publish_service(service_id) | 仅限付费服务,且需处于已批准或已下架状态。 |
| "更新服务" / "更新服务" | update_service(service_id, ...) | 仅限付费服务。 |
| "删除服务" / "删除服务" | delete_service(service_id) | 仅限付费服务。 |
| "删除项目" / "删除上架项目" / "从市场永久移除" | delete_listing(slug) | 仅限免费上架。永久删除上架记录和 community_slugs 记录。URL 将无法访问(404)。同时从探索页和我的项目中移除。若要隐藏但不删除,请使用 unlist_from_dashboard()。 |
| 重新阅读后仍有歧义 | 提出一个问题 | "你是要 (a) 发布公开 URL,(b) 免费上架到广场,(c) 付费上架到服务市场,还是 (d) 开源代码?" |
通过 publisher: 绑定进行交叉链接
同一个项目同时拥有公开 URL 和开源代码时,你需要将两者配对,这样前端就会在上架卡片上显示“查看源代码”,并在代码卡片上显示“访问在线演示”。此技能通过在 project.yaml 中显式设置一次绑定来完成配对。
如何声明绑定
在 project.yaml 中添加 publisher: 块:
name: my-app
type: service
version: 1.0.0
publisher:
code_slug: my-app # OPTIONAL — defaults to manifest.name
public_slug: my-app-pub # OPTIONAL — URL suffix; defaults to code_slug
两个字段均为可选项。若省略,两者默认使用 manifest.name。
任一侧都可以先发布
网关会保留待处理条目,直到另一侧出现。没有顺序要求,也不需要手动链接。
| 顺序 | 后续操作 |
|---|---|
先调用 open_source → 再调用 publish_preview | open_source 记录待处理条目;publish_preview 使用该条目并完成链接 |
先调用 publish_preview → 再调用 open_source | publish_preview 记录待处理条目(需要 publisher_code_slug 参数);open_source 使用该条目并完成链接 |
手动修复(少见)
如果配对配置错误(例如重命名后),请使用:
link_to_listing(listing_slug="2004-my-app-pub", code_slug="my-app")
架构
community.iamstarchild.com (single gateway domain)
│
┌─────────────────┼─────────────────────┐
│ │ │
┌────────▼─────────┐ ┌───▼────────────┐ ┌─────▼──────────┐
│ /api/register │ │/api/code- │ │ /api/services │
│ /api/unregister │ │ projects/* │ │ /api/projects- │
│ /api/list │ │ (GitHub-backed)│ │ query/* │
└────────┬─────────┘ └───┬────────────┘ └─────┬──────────┘
│ │ │
┌────────▼─────────┐ ┌───▼────────────┐ ┌─────▼──────────┐
│ DB: route table │ │ GitHub: │ │ DB: │
│ + project_ │ │ community- │ │ service_ │
│ listings │ │ projects repo │ │ listings │
└──────────────────┘ └────────────────┘ │ (paid services)│
publish_preview() open_source() └────────────────┘
list_in_dashboard()
create_paid_service()
发布:publish_preview() — 公共 URL
publish_preview(preview_id, slug="", title="", publisher_code_slug="")
将运行中的服务映射到 https://community.iamstarchild.com/{user_id}-{slug}。
preview_id:来自preview(action='serve')。状态必须为status=running。slug:仅作为 URL 后缀(小写字母数字字符和连字符,3-50 个字符)。系统会自动添加user_id前缀。title:列表项的显示名称。publisher_code_slug:可选,用于与代码项目的 slug 建立交叉链接绑定。
返回 {"ok": True, "url": "...", "publisher": {...}, "hint": "...", "x402_detected": bool};当 x402_detected 为 true 时,还会附带 next_step 警告(完成付费列表项流程)。
限制:
publish_preview不会创建付费列表项。 如果端点通过 x402 收费(返回 402),则发布流程尚未完成,除非你还运行create_paid_service→submit_for_review(推荐)→publish_service;否则,市场中不会显示任何内容,或会显示为“免费”。检测到计费时,返回值会标记这一点(x402_detected: true+next_step)。- 每位用户最多可发布 20 个预览(超出时网关会返回 429)。
- 服务必须处于运行状态。容器停止后,服务将无法使用。
- 仅在 Starchild Fly 容器内有效(需要
FLY_MACHINE_ID)。 - 列表项默认不可见(
is_public=false)。publish_preview成功后会分配 URL,但不会让该列表项可被发现。要让其可被发现,必须单独调用list_in_dashboard()。
配套工具:
unpublish_preview(slug)— 停止预览容器服务。URL 将无法访问(404)。不会影响列表项状态(is_public/review_status保持不变)。list_published_previews()— 列出此用户当前发布的所有预览 URL。
发布:open_source() — 将代码推送到 GitHub
open_source(project_dir, version_bump="patch", message="")
将项目源代码推送到 GitHub 上的 community-projects/projects/{user_id}/{slug}/。
project_dir:例如output/projects/my-taskversion_bump:patch|minor|major|nonemessage:描述此版本更改内容的提交消息正文。 你(Agent)应始终根据本次会话中实际进行的代码更改撰写此消息;如果你知道更改了什么,就不要留空。尽量用一到三行简短描述面向用户的更改。
这只是发布操作,不会在市场中列出任何内容。
若要让项目可被发现,请在发布后单独调用 list_in_dashboard()(免费)或
create_paid_service()(付费)。
配套工具:
fork(source, dest_dir=None)— 在本地安装他人开源的项目list_open_source(type=None, tag=None, user=None, q=None)— 浏览 GitHub 目录get_open_source(source)— 获取某个项目的完整元数据remove_open_source(slug)— 从 GitHub 目录中删除项目目录(仅限所有者)validate_open_source(project_dir)— 发布前检查
项目结构
output/projects/{slug}/ 下的每个项目:
project.yaml # metadata (name, version, type, env_required, sc_proxy, publisher)
PROJECT.md # required sections: What / Required env / How to start / Outputs / Troubleshooting
.env.example # all env vars with placeholder values
.gitignore # secrets blacklist
src/
├── run.py # for type=task (must start: # -*- task-system: v3 -*-)
├── index.html # for type=service (or app.py + frontend)
└── main.py # for type=script
列出(免费):list_in_dashboard() — 显示在 /projects 展示页
list_in_dashboard(slug, name=None, description="", cover_url=None, tags=None)
让已发布的预览可在 https://community.iamstarchild.com/projects 公共展示页中被发现。若不执行此操作,预览 URL 虽然有效,但除了已知晓该 URL 的人,其他人都看不到。
slug:publish_preview()返回的完整 slug(即{user_id}-{suffix})。name:展示页卡片的显示名称。默认为slug。description:不超过 500 个字符。cover_url:必须位于storage.googleapis.com、image.thum.io或api.microlink.io。要上传用户提供的图片,请先调用upload_cover_image(slug, file_path)— 它会处理预签名 → GCS 上传 → 返回公开 URL。请参阅下文的封面图片上传。tags:最多 5 个标签,每个不超过 20 个字符。
返回 {"ok": True, "listing": {...}, "url": "...", "dashboard_url": "..."}。
限制条件:
- 必须先对同一个 slug 调用
publish_preview(),否则返回 404。 - 幂等:再次调用时使用不同的名称或标签,会更新现有条目。
- 无审核、无定价 — 这是免费列出流程。
配套方法:
unlist_from_dashboard(slug)— 将条目从展示页中隐藏(设置is_public=false、review_status='unlisted',并保留浏览量和收藏数)。URL 仍然有效。要重新列出,请再次调用list_in_dashboard()。delete_listing(slug)— 永久删除条目行和community_slugs记录(移除浏览量和收藏数)。URL 将无法访问(404)。同时从探索页和“我的项目”中移除。若只需隐藏而不删除,请使用unlist_from_dashboard()。get_listing_status(slug)— 只读检查:返回{ok, exists, is_public, listing}。
列出(付费):在服务市场列出付费服务
付费服务通过 x402 收取访问费用(在平台已启用的网络上进行链上 USDC/USDG 结算 — 默认包括 Base + Monad + Robinhood + X Layer + Solana,遵循 all 模式)。发布前必须通过自动化的 6 项审核 — 服务必须通过所有检查(获批),publish_service() 才能正常工作。API 调用示例可选,但建议提供。
多链支付网络(plans-280)
每项付费服务都有一个 networks_mode,用于决定买家可以在哪些链上付款:
networks_mode | 行为 | 使用场景 |
|---|---|---|
"all"(默认) | 接受所有平台主网上的付款(目前包括 Base + Monad + Robinhood + X Layer + Solana;新增链会自动纳入,无需更改代码)。网关将 supported_networks 存储为 NULL,并在读取时展开。 | 常见情况——不传入参数或设置 networks_mode="all"。 |
"custom" | 仅接受 supported_networks 中所列链上的付款(非空的 CAIP-2 ID 列表,例如 ["eip155:8453"])。不会随平台扩展而自动更新。 | 用户明确表示“仅限 Base”/“仅限 Monad”/某个特定子集。 |
规则:
- 默认值为
all。 切勿将单条链(如['eip155:8453'])硬编码为默认值,否则会重新引入旧版仅限 Base 的行为。 custom必须提供非空的supported_networks;空列表会被拒绝。provider_wallet是一个在所有已启用链上使用的 EVM 地址(Starchild facilitator 会在每条链上结算至同一地址)。它并非仅用于 Base。- 买家会看到 402
accepts数组(每条已启用链对应一个条目,价格相同),并且每笔付款选择一条链——这是标准的 x402 多接受项,不是协议变更。 - 结算所需的 Gas 由平台(Starchild facilitator)支付,而不是由服务提供方支付。
- 要将现有服务切回
all:update_service(service_id, networks_mode="all")。 - 要限制为某个子集:
update_service(service_id, networks_mode="custom", supported_networks=["eip155:8453"])。
这与 x402 技能中 monetize 的默认值(all)一致。这两项技能采用相同的发布节奏——如果网关 402 accepts 和市场列表显示的链不同,则其中一方配置为了 custom,而另一方仍为 all。
服务生命周期与审核状态(审核仅供参考)
create ──▶ published ──▶ submit_for_review ─▶ pending ─▶ approved / rejected
│ (required before publishing — must pass to go live)
│ │ fix via update_service(), re-check
▼ ▼
publish_service() ─────────────────▶ listed ◀─▶ unlisted (owner takedown / re-list)
│
▼
unavailable ──▶ restore ──▶ listed
审核是自检,不是门槛:submit_for_review() 会运行 5 项自动检查(api_reachable、pricing_consistency、x402_payment、response_match、doc_completeness、examples_provided),并为所有者保存一份报告。
publish_service() 要求服务处于 approved 状态(重新上架时则为 unlisted)。发布前必须通过审核——先运行 submit_for_review(),以便在买家付款前发现失效的端点。rejected 报告不会阻止上架;对已处于 listed 状态的服务运行检查,也绝不会将其下架。
⚡ 场景选择决策树 — 创建任何付费服务前必须遵循
步骤 1:该服务是否有 Starchild 项目页面(通过 publish_preview() 发布)?
- 是,且页面可免费浏览 → 流程 D。使用
service_type="paid_project"+project_slug。免费页面通过publish_preview()发布,付费 API 则位于/api/*路由后,由 x402 保护。上游应用在/提供免费介绍页,在/api/*提供付费 API。 - 是,但整个页面都需要付费才能访问 → 流程 B(表单 1)。使用
service_type="paid_project"+project_slug。用户自行实现访问控制(付费墙 + 凭据验证)。请参阅 x402 技能中的“付费项目:两种形式”部分。 - 否(独立 API,没有项目页面) → 流程 C 或 E。使用
service_type="paid_api",不带project_slug。不要创建 index.html 或发布预览 — 没有免费页面。公开 URL 根路径会显示 x402 402 挑战或网关信息。
步骤 2:用户是否需要多个定价不同的 API 端点?
- 是 → 在一次
create_paid_service()调用中使用api_endpoints数组(流程 E)。不要创建多个独立服务。 - 否 → 单个端点,仅使用
api_endpoint。
步骤 3:组合以上答案:
| 用户需求 | 免费页面? | 多个端点? | 流程 | service_type | project_slug | api_endpoints |
|---|---|---|---|---|---|---|
| 付费订阅项目(整个网站都由付费墙保护) | 是 | 否 | B | paid_project | 必填 | — |
| 独立付费 API(没有网页) | 否 | 否 | C | paid_api | 省略 | — |
| 免费介绍页 + 付费 API | 是 | 否 | D | paid_project | 必填 | — |
| 免费介绍页 + 多个付费 API | 是 | 是 | D+E | paid_project | 必填 | 必填 |
| 多个付费 API(没有网页) | 否 | 是 | E | paid_api | 省略 | 必填 |
⚠️ 常见的 Flow 混淆错误(来自真实事件)
| 错误 | 问题所在 | 正确做法 |
|---|---|---|
用户说“编写一个介绍页 AND 一个付费 API”,但 Agent 使用 paid_api 并另建一个项目预览 | 服务和项目彼此脱节——市场中显示两个条目,一个免费(空白),一个付费 | 使用 paid_project + project_slug(Flow D)。介绍页和 API 是同一个服务。 |
| 用户说“纯付费 API”,但 Agent 创建了 index.html 并发布预览 | 多余的免费项目页面使市场杂乱;介绍页可能显示为空白/JSON | 不要创建 index.html 或调用 publish_preview。使用 paid_api(Flow C)。x402 网关的 402 响应就是 API 的自我说明。 |
| 用户说“多个 API 端点”,但 Agent 创建了 N 个独立服务 | 市场中出现 N 个卡片,而非 1 个;端口冲突;上游混淆 | 使用 api_endpoints 数组创建一个服务(Flow E)。 |
| Agent 重用了另一个服务已占用的上游端口 | 网关代理到了错误的上游——响应来自另一个服务 | 每个服务必须使用唯一的上游端口。检查 .x402/services.json 是否存在冲突。 |
Agent 创建了带有 /docs 路由的 start.py,与上游的 /docs 路由冲突 | Flask AssertionError: View function mapping is overwriting an existing endpoint | 不要在 start.py 和上游应用中同时定义 /、/docs 或 /index.html 路由——只在其中一处定义。 |
关键规则:
- 独立付费 API 不要传入
project_slug。project_slug仅适用于paid_project——包括“免费网页 + 付费 API”模式(Flow D,使用paid_project)。为独立的paid_api传入预览 slug 或不存在的 slug 会产生虚假关联——后端会静默清除它,但你一开始就不应该传入。 - 路由规则:服务关联到项目页面 →
paid_project;paid_api仅用于没有项目页面的独立 API。 如果你的 API 有已发布的 Starchild 项目(落地页/仪表板),供用户免费浏览,请使用service_type="paid_project"+project_slug——这会将服务合并到市场中的项目卡片。如果没有免费项目页面,请使用paid_api,并且不要设置project_slug。传入paid_api+project_slug时,create_paid_service()会自动升级为paid_project(响应中会附带project_slug_warning)——最终列表项始终是paid_project。 project_slug必须是包含用户前缀的完整已发布 slug(例如33-my-app),并且必须对应project_listings中的现有行(即必须先调用publish_preview()+list_in_dashboard())。api_endpoints适用于具有多个不同价格端点的服务;每个端点都有自己的path、price和可选的label。- 设置了
project_slug的项目不会显示在“免费”选项卡中——它会移至“全部”和“付费”选项卡。 - 合并到项目卡片后的可见性: 当已上架服务的
project_slug指向一个公开项目时,该服务会折叠到统一市场视图中的项目卡片内。因此,该服务不会作为独立条目出现在explore_services()或list_my_services()中——这是预期行为,不代表上架失败。用户仍可通过项目卡片、get_service(service_id)和get_user_services(user_id)使用并购买该服务,而且可通过explore_marketplace()(统一信息流)发现该服务。要确认已合并的服务已上架,请检查get_service()→review_status == "listed",而不是查看explore_services()的结果。 - 用户要求提供多个 API 时,创建一个带有
api_endpoints的服务——不要创建多个独立服务。参见 Flow E。
标签 — 用于市场筛选的预定义标签标识
创建付费服务时,传入 tags,并从下方预定义列表中选择 1–3 个标签标识。Agent 应根据服务的名称和描述选择最相关的标签。标签用于市场筛选和发现,可替代旧的 category 字段。
预定义标签标识(选择 1–3 个最相关的标签):
| 领域 | 标签 |
|---|---|
| DeFi 与交易 | defi、trading、dex、dex-swap、lending、lending-yield、yield、staking、derivatives、bridge |
| 链上数据 | onchain-data、token-analytics、price-feed、wallet、wallet-portfolio、nft |
| AI 与 ML | ai-inference、llm-inference、text-analysis、image-generation、text-to-speech、video-transcription、translation |
| Web 与数据 | web-search、web-scraping、screenshot-pdf、news-feed、seo、data-service、data-storage、analytics |
| 安全与合规 | aml-sanctions、security、privacy、threat-detection、agent-safety、agent-trust |
| 基础设施 | smart-contract、oracle、zk-proofs、layer2、mev、compute、storage、developer-tools、identity、payment、payments |
| 社交与媒体 | social、social-media、gaming、metaverse |
| 金融(传统金融) | stock-equity、sec-edgar、real-estate、insurance、prediction-market |
| 其他 | dao、governance、email-sms、weather、geolocation、healthcare、agriculture、astrology-fortune、rwa、research-academic、legal-gov、launchpad |
示例:DeFi 价格 API → tags=["defi", "price-feed", "trading"]
流程 B — 付费项目上架
付费项目会对访问收费。有两种形式——两者都使用
service_type="paid_project" + project_slug:
形式 1:整个页面位于付费墙后 — 页面本身需要付费才能访问。 用户自行实现访问控制(带凭据验证的类登录组件)。平台提供 x402 支付协议; 用户实现付费墙 UI 和凭据逻辑。实现详情请参阅 x402 技能中的“付费项目:两种形式”部分,以及“如何使用 Agent 付款” 文档模板。
形式 2:免费页面 + 付费 API — 页面可免费浏览,API 调用需要付费。
这就是下面的流程 D。上游应用在 / 提供免费的介绍页面,并在 /api/* 提供付费 API。
两种形式采用相同的模式——唯一的区别在于用户需要实现什么(形式 1 需要实现付费墙拦截器,形式 2 无需额外实现)。
- 准备一个正在运行的项目,并提供一个公开 URL(通过
publish_preview())。 - 使用 x402 技能为项目的访问端点配置收费。
未付款时,端点必须返回
402 Payment Required,付款后返回200+ 数据。 - 创建服务记录:
create_paid_service(
name="Premium Trading Signals",
description="Real-time trading signals with on-chain confirmation.",
service_type="paid_project",
tags=["trading", "onchain-data"],
project_slug="33-premium-signals", # FULL published slug WITH user prefix (the URL path segment)
api_endpoint="https://community.iamstarchild.com/33-premium-signals",
provider_wallet="0xAbC...yourEvmWallet", # EVM address for Base/Monad/Robinhood/X Layer; Solana address auto-fetched from Privy wallet
pricing_model="monthly",
price=10,
service_description="Subscribers get a dashboard with live trading signals.",
)
付费项目必填字段:name、description、service_type、
project_slug、api_endpoint、provider_wallet、pricing_model、price、
service_description。推荐填写:tags(用于市场筛选的 1–3 个预定义标签 slug)。
⚠️ project_slug 必须是包含用户前缀的完整已发布 slug
(例如 33-premium-signals,必须与项目 URL
https://community.iamstarchild.com/<slug>/ 中的路径段完全一致)。当未设置 api_endpoint 时,网关会将 API
端点推导为 publicUrl + "/" + project_slug,因此,不带前缀或错误的 slug 会导致端点推导失败,并破坏
项目↔服务关联。可通过
update_service(service_id, project_slug="<full-slug>") 修复现有记录——无需重新上架。
- 必需:运行自动审核 — 付费服务必须通过审核 才能发布。若市场上列出的端点不可用,你可能会在察觉之前就收到买家的款项:
submit_for_review(service_id) # kicks off 6 automated checks asynchronously
get_review_status(service_id) # poll until no longer pending, then show the
# report to the user — THEY decide what to fix
rejected 报告会阻止发布。阅读 review_feedback +
latest_task.checks,使用 update_service() 修复,然后
重新运行 submit_for_review(),直到审核通过。
- 审核通过后(approved)即可发布:
publish_service(service_id)
对已上架的服务也可以稍后再次运行该检查——它绝不会将服务下架。
流程 C — 付费 API 上架
付费 API 是已经实现 x402 收费的外部 API 服务。
⚠️ 独立付费 API 不要传入
project_slug。project_slug仅适用于paid_project(必填),或“免费网页 + 付费 API” 模式(流程 D,其中存在已发布的 Starchild 项目页面)。对于没有关联免费项目页面的独立paid_api,请完全省略project_slug。 后端会根据project_listings验证project_slug,并会静默清除不存在的 slug,但你本就不应该传入它。⚠️ 如果 API 属于已发布的 Starchild 项目,请选择
paid_project。 如果你的 API 有通过publish_preview()发布的着陆页 / 仪表板(即它作为项目存在于 community.iamstarchild.com),请使用service_type="paid_project"
project_slug=<full published slug WITH user prefix>(流程 B)——不要使用paid_api。project_slug用于 将服务关联到项目卡片(价格标签、交叉导航)。paid_api上架项没有项目关联,因此项目卡片仍会显示“免费”。 仅当 API 确实是外部 / 独立 API,且没有 Starchild 项目时,才使用paid_api。 忘记关联了吗?使用update更新服务记录中的project_slug——无需重新上架。
-
提供启用了 x402 的 API — 未付款时,端点必须返回
402;收到有效的X-PAYMENT标头后,返回200和数据。如果需要,请使用 x402 skill 实现。确保 Starchild 记录购买信息
为了让 Starchild 市场跟踪购买、收益和使用统计信息,请根据你的 facilitator 设置选择以下两种方式之一:
选项 A — 使用 Starchild facilitator(推荐)
将 x402 中间件的 facilitator URL 设置为:
https://starchild-x402-facilitator.fly.dev结算成功后,Starchild facilitator 会自动回调 community-gateway 以记录购买信息。无需额外设置 — 使用默认的
create_paid_service()调用继续执行第 2 步。选项 B — 使用自己的 facilitator + 代理模式
如果你使用自己的 facilitator(或第三方 facilitator),Starchild 将无法接收结算回调。创建服务记录时(第 2 步),请改为传入
source="manual":create_paid_service( ..., source="manual", # ← enables proxy mode )这会让市场为你的 API 生成一个代理 URL:
https://community.iamstarchild.com/proxy/{service_id}/...用户通过此代理 URL 访问你的 API。代理会透明地将请求转发到你的真实
api_endpoint,并在付款成功时(HTTP 200 且带有payment-signature标头)自动在 Starchild 数据库中记录购买信息。你无需更改 facilitator URL,也无需设置任何回调。402 响应要求(审核时检查):
402响应正文必须包含pricingModel字段(平台格式)。payTo必须是你的实际收款 EVM 钱包地址(在所有启用的链上使用)。accepts数组中每条启用的链对应一个条目(多种接受方式);买家每笔付款选择一条链。每个条目的amount(USDC,6 位小数)相同 — 此版本的平台不支持按链设置不同价格。- 响应必须是客户端能够解析的有效 x402 挑战。
-
创建服务记录(
service_type = "paid_api"):
create_paid_service(
name="On-chain Whale Tracker API",
description="REST API returning real-time whale wallet movements across 12 chains.",
service_type="paid_api",
tags=["onchain-data", "wallet-portfolio", "trading"],
api_endpoint="https://api.example.com/v1/whales",
provider_wallet="0xAbC...yourEvmWallet", # EVM address for Base/Monad/Robinhood/X Layer; Solana address auto-fetched from Privy wallet
pricing_model="pay_per_use",
price=0.01,
free_trial_count=3,
api_documentation="# Whale Tracker API\n\n## GET /v1/whales\n\nReturns recent whale transactions.\n\n### Parameters\n| name | type | required | description |\n|---|---|---|---|\n| chain | string | no | Filter by chain id (default: all) |\n| limit | int | no | Max results (default: 50, max: 200) |\n\n### Response\n```json\n[{\"hash\":\"0x...\",\"from\":\"0x...\",\"to\":\"0x...\",\"value\":\"1000000\",\"token\":\"USDC\",\"chain\":\"base\",\"ts\":1700000000}]\n```",
example_request="curl https://api.example.com/v1/whales?chain=base&limit=10",
example_response='[{"hash":"0xabc...","from":"0x111...","to":"0x222...","value":"5000000","token":"USDC","chain":"base","ts":1700000000}]',
)
付费 API 必填字段:name、description、service_type、api_endpoint、provider_wallet、pricing_model、price、api_documentation。
推荐字段(可选):example_request、example_response(可提升买家体验)。
可选字段:free_trial_count(仅适用于 pay_per_use)、
source(代理模式下设为 "manual" — 参见上文第 1 步选项 B;Starchild facilitator 默认模式下省略)、
cover_url(自定义封面图片 URL — 必须位于 storage.googleapis.com 或其他允许的域名上;如果未提供,Agent 应根据服务名称和描述自动生成合适的封面图片,通过图片上传服务上传,并传入生成的 URL)。
付费服务的封面图片
付费服务不会自动生成封面图片(与会自动截取屏幕截图的免费项目不同)。在 create_paid_service() 中传入 cover_url — 图片必须位于 storage.googleapis.com(或 image.thum.io / api.microlink.io)。
⚠️ 必须:当用户提供图片,或你需要设置封面时,调用 upload_cover_image(slug, file_path)。 此函数会处理完整流程:预签名 URL → 压缩 → 上传到 GCS → 返回 storage.googleapis.com 公共 URL。请勿使用 imgur、数据 URI 或任何其他托管服务 — 网关会验证域名。
如果用户未提供图片,请生成一张(例如使用图片生成 skill),将其保存在本地,然后调用 upload_cover_image()。
之后也可以使用 update_service(cover_url=...) 更改封面。
完整参考请参见封面图片上传。
- 运行审核 → 与流程 B 第 4 步相同(发布前必须完成)。
- 发布 → 与流程 B 第 5 步相同(状态必须为已批准)。
流程 D — 免费网页 + 付费 API(混合模式)
你的项目有一个免费落地页(通过 publish_preview() 发布),还有一个付费 API
端点。用户可以免费浏览项目页面,但 API 调用需要付费。
市场会显示一张合并卡片,其中包含“访问项目”和“调用 API”按钮。
- 发布项目:通过
publish_preview()发布,这会创建免费落地页。 - 为 API 端点配置 x402 收费(例如,
/api/random返回 402)。 - 创建服务记录,并设置
service_type="paid_project"+project_slug:
create_paid_service(
name="Random9 API",
description="Random 9-digit number API. Free docs page + paid API calls.",
service_type="paid_project",
tags=["developer-tools"],
project_slug="33-random9-api", # FULL slug WITH user prefix — links to the free project page
api_endpoint="https://community.iamstarchild.com/33-random9-api/api/random",
provider_wallet="0xAbC...yourEvmWallet", # EVM address for Base/Monad/Robinhood/X Layer; Solana address auto-fetched from Privy wallet
pricing_model="pay_per_use",
price=0.01,
service_description="Paid access to the Random9 API endpoint; the docs page stays free.", # required for paid_project
api_documentation="# Random9 API\n## GET /api/random\nReturns a random 9-digit number.",
example_request="curl https://community.iamstarchild.com/33-random9-api/api/random",
example_response='{"random":"482917365","digits":9}',
)
project_slug 会将此服务合并到项目卡片中。项目页面(/)
保持免费;只有 API 端点(/api/random)需要付费。
注意:如果同时传入
service_type="paid_api"和project_slug,create_paid_service()会自动将其升级为paid_project,并返回project_slug_warning——存储的列表项始终为paid_project。直接传入paid_project(如上所示)是规范写法。
- 发布 + 可选的自检——与流程 B 的步骤 4–5 相同。
流程 E — 多端点 API
你的服务有多个价格不同的 API 端点(例如,基础版 $0.01,高级版 $0.10)。 每个端点都会在市场详情视图中单独列出。
⚠️ 当用户要求提供多个 API 时,请创建一个包含
api_endpoints的服务——不要创建多个独立服务。 例如,如果用户说“开发三个付费 API 并将它们上架”,不要调用create_paid_service()三次。应创建一个服务,其api_endpoints数组中包含所有三个端点。 这样用户就能在一张统一的市场卡片中查看并购买各个端点。 只有当这些 API 确实无关时(领域、受众或定价模式不同),才创建多个服务。
-
配置 x402 收费,并为每个路由单独定价:
# Default networks_mode is "all" (Base + Monad). Omit --networks to follow # the platform mainnet set; pass --networks eip155:8453 only if the user # explicitly wants to restrict to a single chain. python3 skills/x402/scripts/monetize.py --name my-api --upstream-port 5173 \ --mode pay_per_use --price 0.01 \ --route "GET /api/basic=$0.01" --route "GET /api/premium=$0.10" \ --route "POST /api/batch=$0.50" \ --facilitator $FAC -
创建服务记录,并设置
api_endpoints:
create_paid_service(
name="Data API Service",
description="Multiple API endpoints at different prices.",
service_type="paid_api",
tags=["data-service"],
api_endpoint="https://example.com/api/basic", # primary endpoint for review
api_endpoints=[
{"path": "GET /api/basic", "price": 0.01, "label": "Basic Query"},
{"path": "GET /api/premium", "price": 0.10, "label": "Premium Query"},
{"path": "POST /api/batch", "price": 0.50, "label": "Batch Process"},
],
provider_wallet="0xAbC...yourEvmWallet", # EVM address for Base/Monad/Robinhood/X Layer; Solana address auto-fetched from Privy wallet
pricing_model="pay_per_use",
price=0.01, # price of the primary/default endpoint
api_documentation="# Data API\n## GET /api/basic\nBasic data.\n## GET /api/premium\nPremium analytics.",
example_request="curl https://example.com/api/basic",
example_response='{"data":"basic market info"}',
)
你可以结合流程 D 和流程 E:使用 service_type="paid_project" + project_slug
并搭配 api_endpoints,将免费项目页面与多端点定价关联起来。
市场会显示一张合并的项目卡片,并在详情视图中列出各个端点。
- 发布 + 可选的自检——与流程 B 的步骤 4–5 相同。
审核检查(6 项自动检查——发布必需)
submit_for_review() 会针对 api_endpoint 运行这些检查;服务必须通过所有检查才能获批发布。针对已上架服务运行检查不会将其下架:
| # | 检查 | 验证内容 |
|---|---|---|
| 1 | api_reachable | 未发送 X-PAYMENT 标头时,端点会返回 402 Payment Required |
| 2 | pricing_consistency | 402 响应中 accepts 的 amount 与你声明的 price 一致(以 USDC 基础单位计)。使用多链 accepts(每条已启用的链对应一个条目)时,每个条目都必须携带相同的 amount——当前版本的平台不支持按链定价。 |
| 3 | x402_payment | 完成有效的 x402 支付后,端点会返回 200 + 数据 |
| 4 | response_match | 实际响应的关键字段与 example_response 一致 |
| 5 | doc_completeness | api_documentation 包含参数说明、响应格式和至少一个示例 |
检查 #5 会通过关键词匹配:文档必须包含一个“Response”(或“响应格式”)
章节,且标题下方有实际正文——空章节无法通过审核。
service_description(paid_project)和 api_documentation / example_request /
example_response(paid_api)会在调用时由 create_paid_service() 强制校验;
如果这些字段缺失,会在创建无法审核的记录之前报错。
常见的驳回原因:
- 402 响应的
amount与声明的price不一致(小数位错误/单位错误)。 - 端点完全不返回 402(未接入 x402,或向未经身份验证的请求返回 200)。
example_response与 API 在支付后实际返回的内容不一致。- 文档缺少参数表或响应架构。
定价模式
所有付费服务均使用 x402 exact 支付方案(通过平台已启用的网络在链上结算 USDC/USDG——默认情况下为 Base + Monad + Robinhood + X Layer + Solana,遵循 networks_mode="all")。结算 Gas 费由 Starchild facilitator 支付,而非服务提供方。
pricing_model | 含义 | x402 行为 | 典型用途 |
|---|---|---|---|
pay_per_use | 按次收费 | 每个携带有效 X-PAYMENT 的请求 → 结算(扣费) | API 调用 |
lifetime | 一次性买断 | 首次付款时结算;后续请求验证此前的结算记录,不再扣费 | 一次性购买 |
monthly | 月度订阅 | 每个计费月结算一次;到期后重新扣费 | Web 订阅、API 月度套餐 |
weekly | 周度订阅 | 每 7 天结算一次;到期后重新扣费 | 短期订阅 |
quarterly | 季度订阅 | 每 90 天结算一次;到期后重新扣费 | 季度套餐 |
yearly | 年度订阅 | 每 365 天结算一次;到期后重新扣费 | 年度套餐(通常有折扣) |
prepaid | 预付余额 | 用户通过 deposit-settle 存入资金(一次链上交易),之后每次调用都从链下余额中扣款(无 Gas 费) | 高频小额支付 |
free_trial_count仅适用于pay_per_use——允许先免费调用 N 次,再开始收费。 它不是按日历时间计算的免费促销。限时免费(free_promo_*)→ x402 技能(selling.md→ 限时免费促销)。
多种方案(多种定价选项)
一项服务可以同时提供多种定价方案(例如:按周 + 按月 + 按年)。创建服务时传入 pricing_options 数组:
create_paid_service(
...,
pricing_options=[
{"pricing_model": "weekly", "price": 3, "is_default": True, "label": "Weekly"},
{"pricing_model": "monthly", "price": 10, "label": "Monthly"},
{"pricing_model": "yearly", "price": 90, "label": "Yearly (Save 42%)"},
],
)
规则:
pay_per_use不能与其他定价模式组合。- 订阅模式(按周/按月/按季度/按年)可以自由组合。
lifetime和prepaid可以与订阅模式组合。- 必须将一个选项标记为
is_default: True(或者自动将第一个选项标记为默认选项)。 - 服务的
pricing_model和price字段会自动与默认选项同步。
多方案的 402 要求:服务的 x402 中间件必须支持 X-Pricing-Model 标头——当客户端发送 X-Pricing-Model: yearly 时,402 响应必须返回按年方案的价格。审核会逐一验证每个方案的 402 金额。
参考:完整规范见 x402-facilitator/docs/pricing-models.md。
限制可使用的支付链(自定义网络)
默认的 networks_mode="all" 会使用平台的主网集合(Base + Monad + Robinhood + X Layer + Solana)。只有当用户明确提出要求(“只接受 Base”“不接受 Monad 支付”等)时,才限制为部分网络:
# Create a service that ONLY accepts Base USDC (not Monad)
create_paid_service(
...,
networks_mode="custom",
supported_networks=["eip155:8453"], # CAIP-2 chain id; non-empty required
)
# Switch an existing service from all → custom (only Monad)
update_service(service_id, networks_mode="custom", supported_networks=["eip155:143"])
# Switch back to all (follow platform mainnets; clears the custom list)
update_service(service_id, networks_mode="all")
不要默认使用 custom + ['eip155:8453']。 这会重新引入旧版仅支持 Base 的行为。默认值为 all;只有用户明确要求限制时才使用 custom。
服务示例(API 调用示例)——建议提供,但非必需
API 调用示例非必需,但强烈建议提供。示例会展示 API 的返回内容——在服务详情页中以可折叠的请求/响应对呈现。没有示例的服务仍可通过审核,但审核报告会注明缺少示例。
建议的列表顺序:
1. create_paid_service(...) → creates the service
2. set_service_examples(service_id, examples) → recommended (improves buyer experience)
3. submit_for_review(service_id) → required before publishing
4. publish_service(service_id) → go live (requires approved)
添加示例:
set_service_examples("service-uuid", [
{
"title": "Query BTC Price",
"description": "Get current Bitcoin price in USD",
"request": 'curl -X GET "https://api.example.com/v1/price?symbol=BTC"',
"response": '{"symbol": "BTC", "price": 67234.56, "currency": "USD"}'
},
{
"title": "Query ETH Price",
"request": 'curl -X GET "https://api.example.com/v1/price?symbol=ETH"',
"response": '{"symbol": "ETH", "price": 3456.78, "currency": "USD"}'
}
])
清除示例(较少需要——set_service_examples 会替换全部示例):
clear_service_examples("service-uuid")
最佳实践:
- 添加 2-5 个示例,涵盖最常见的使用场景
- 使用能说明场景的描述性标题
- 加入实际的请求参数和响应数据
- 展示简单和复杂的使用模式
set_service_examples()会替换全部示例——每次都传入完整列表
这取代了传入 create_paid_service() 的旧版单一 example_request / example_response 字段。包含旧版字段的服务仍可通过审核(向后兼容),但新服务应使用 set_service_examples() 展示更丰富的多场景示例。
付费服务管理函数
| 函数 | 用途 |
|---|---|
create_paid_service(...) | 创建服务记录(已发布状态) |
set_service_examples(service_id, examples) | 设置 API 调用示例(可选,建议提供)——替换全部示例 |
clear_service_examples(service_id) | 移除全部 API 调用示例 |
submit_for_review(service_id) | 运行包含 6 项检查的自动审核(发布前必需) |
get_review_status(service_id) | 轮询审核进度和各项检查详情 |
publish_service(service_id) | 正式上线(需要处于已批准或未列出状态) |
unpublish_service(service_id) | 下架(已列出 → 未列出) |
list_my_services(cursor, limit) | 列出你的服务(分页) |
get_service(service_id) | 按 ID 获取一项服务 |
update_service(service_id, **fields) | 更新服务字段(例如,修正被拒绝后的问题) |
delete_service(service_id) | 永久删除一项服务 |
restore_service(service_id) | 将不可用的服务恢复为已列出状态 |
市场浏览和消费者功能
这些函数可让 Agent 浏览服务市场、阅读评论、撰写评论、管理收藏并查看收益,与 Web 前端的功能相同。
| 函数 | 用途 |
|---|---|
explore_marketplace(search, paid_only, ...) | ⭐ 统一浏览——首先使用此函数查找付费服务/API。 项目卡片和独立服务显示在同一信息流中(与 Web 上的 All/Paid 标签页相同);这是唯一能搜索并显示合并到公开项目卡片中的服务的途径。条目包含 type:service(使用 id)或 project(付费卡片包含 service_id)——将其传入 get_service_detail() |
explore_services(search, sort, tags, ...) | 仅浏览独立服务条目(服务 API)。⚠️ 合并到公开项目卡片中的服务不会显示在此处——如需完整覆盖范围,请使用 explore_marketplace() |
get_service_detail(service_id) | 获取已发布服务的公开详情(包含文档,并增加浏览量) |
get_service_pricing(service_id) | 获取经过验证的定价,并进行实时 x402 检查 |
get_service_reviews(service_id, sort) | 列出服务评论(公开) |
write_service_review(service_id, rating, comment) | 提交或更新评论(必须先购买或使用过服务) |
get_user_services(user_id) | 获取用户已发布的付费服务(公开,用于个人资料展示) |
favorite_service(service_id) | 将服务添加到收藏夹 |
unfavorite_service(service_id) | 从收藏夹中移除服务 |
get_favorite_services(cursor, limit) | 列出当前用户收藏的服务 |
get_service_purchase_status(service_id) | 检查当前用户是否已购买或使用过服务 |
get_service_earnings(service_id) | 单项服务的收益统计(仅限所有者) |
get_earnings_summary() | 所有服务的收益汇总(仅限所有者) |
get_service_onchain_records(service_id) | 链上 USDC 结算记录(仅限所有者) |
在 bash 代码块中使用
python3 - <<'EOF'
import sys
# Prefer the registered skill tools (read this SKILL.md via read_file to
# load them) over hand-written imports of exports.py. If you DO need a
# direct import: the directory name has a HYPHEN, so dotted imports
# (`from skills.community_publish import ...`) raise ModuleNotFoundError.
# Use this sys.path pattern (or importlib.util.spec_from_file_location).
sys.path.insert(0, "/data/workspace/skills/community-publish")
from exports import (
# PUBLISH: public URL
publish_preview, unpublish_preview, list_published_previews,
# PUBLISH: open source code
open_source, remove_open_source, fork,
list_open_source, get_open_source, validate_open_source,
# LIST: free (project gallery)
list_in_dashboard, unlist_from_dashboard, get_listing_status,
# LIST: paid (service marketplace)
create_paid_service, submit_for_review, get_review_status,
publish_service, unpublish_service,
list_my_services, get_service, update_service, delete_service,
restore_service, set_service_examples, clear_service_examples,
# MARKETPLACE: browse + consumer actions
explore_marketplace, explore_services, get_service_detail,
get_service_pricing, get_service_reviews, write_service_review,
get_user_services, favorite_service, unfavorite_service,
get_favorite_services, get_service_purchase_status,
get_service_earnings, get_earnings_summary, get_service_onchain_records,
# Manual repair (rare)
link_to_listing,
)
# Step 1: Publish the URL
print(publish_preview(preview_id="my-app-a3f1", slug="my-app"))
# Step 2a: Free listing — show on gallery
print(list_in_dashboard(slug="33-my-app", name="My App", description="A cool app"))
# OR Step 2b: Paid listing — create service + review + publish
res = create_paid_service(
name="My Paid App",
description="Premium features",
service_type="paid_project",
tags=["developer-tools"],
project_slug="33-my-app", # full published slug WITH user prefix
api_endpoint="https://community.iamstarchild.com/33-my-app",
provider_wallet="0xAbC...",
pricing_model="monthly",
price=5,
service_description="Subscribers get premium features.",
)
print(res)
# Then: publish_service(res["service_id"]) — optionally submit_for_review() first for a self-check report
EOF
行为规则
- 在
open_source()之前显示差异。在validate_open_source之后,总结即将推送的内容并请求确认。例外情况:用户明确要求“无需确认直接发布”,或重新发布已知正常的项目。 - 分叉时绝不自动运行 setup.sh。显示命令,让用户确认。
- 分叉时始终一次性收集所有环境变量。读取项目的
env_required,与workspace/.env进行比较,然后仅针对缺失的键调用一次request_env_input。 - 发布必须经过审核。
publish_service()要求状态为approved——始终先运行submit_for_review()。向用户展示报告;如果被拒绝,则通过update_service()修正并重新运行submit_for_review()。对已上架服务运行检查不会将其下架。 api_endpoint必须是 x402 收费端点。对于付费项目,这是项目的公开 URL。对于付费 API,这是外部 API URL。审核人员会访问此 URL,并预期收到402。- 价格单位为 USDC。402 响应中的
accepts.amount使用基础单位(USDC 为 6 位小数)。$0.01的价格对应amount: "10000"。此处不匹配是审核失败的首要原因。 - 不要捏造审核结果。始终调用
get_review_status()进行检查——绝不要因为提交了审核就假定审核已通过。 - 不要混淆发布与上架。
publish_preview()会分配一个 URL。list_in_dashboard()/create_paid_service()会使服务可被发现。这是两个独立且需明确执行的步骤。 - Slug 规则:小写字母、数字和连字符,长度为 3-50 个字符,且不能以连字符开头或结尾。
- 版本规则(
open_source):严格遵循语义化版本规范。重新发布相同版本会被拒绝。 - URL ≠ 代码 ≠ 上架条目:公开 URL 失效不会移除开源代码或市场上架条目,反之亦然。它们彼此独立。
- 独立的
paid_api服务不要传入project_slug。project_slug仅适用于paid_project——包括“免费网页 + 付费 API”模式(Flow D 使用paid_project;传入paid_api+project_slug会自动升级为paid_project,并产生project_slug_warning)。为独立 API 传入预览 slug 或不存在的 slug 会创建虚假关联。后端会静默清除不存在的 slug,但除非用户明确希望将免费项目页面与付费 API 关联,否则不应传入project_slug。 - 用户要求多个 API 时,创建一个包含
api_endpoints的服务。不要为相关 API 多次调用create_paid_service()。使用api_endpoints数组在单个服务中列出所有端点(Flow E)。只有当这些 API 确实互不相关时(域名、受众或定价模式不同),才创建多个服务。 - 支付网络默认设为
all。绝不要将单条链(例如['eip155:8453'])硬编码为默认值——这会重新引入旧版仅支持 Base 的行为。省略networks_mode/supported_networks(或传入networks_mode="all"),让服务遵循平台的主网集合(Base + Monad + Robinhood + X Layer + Solana;新增链会自动启用)。仅当用户明确要求限制为部分网络(“仅 Base”“仅 Monad”等)时,才使用networks_mode="custom"+ 非空的supported_networks。 provider_wallet是一个在所有已启用 EVM 链上使用的 EVM 地址。Starchild facilitator 在每条 EVM 链上都会将结算款发送到同一地址;它并非仅用于 Base。不要向用户将其描述为“Base 钱包”。对于 Solana 支付,平台会自动使用用户的 Privy Solana 钱包地址(provider_sol_wallet)。如果用户未明确提供 Solana 地址,create_paid_service()会自动从 Privy 钱包获取。没有 Solana 地址的服务将不接受 Solana 支付(Solana 不会包含在 402 accepts 列表中)。- 结算 Gas 费用由平台支付,而非服务提供者。不要告知服务提供者需要准备 ETH/MON 来支付结算 Gas。
Common gotchas
| 症状 | 原因 | 修复方法 |
|---|---|---|
publish_preview:Preview not found | preview_id 错误,或服务已停止 | 检查 /data/previews.json,使用 preview(action='serve') 重启 |
publish_preview:429 Too many published previews | 已达到每位用户 20 个的网关上限 | 先使用 unpublish_preview() 取消发布一个旧预览 |
publish_preview:FLY_MACHINE_ID not set | 在本地运行,而不是在 Starchild 容器中运行 | 只有在生产容器中才能发布 URL |
list_in_dashboard:404 No preview found | 此 slug 尚未运行 publish_preview() | 先调用 publish_preview() |
open_source:400 Validation failed: env names not in .env.example | 在 env_required 中列出了 MY_KEY,但忘了在 .env.example 中添加 | 将缺少的密钥添加到 .env.example |
open_source:400 Possible secret detected | 密钥扫描器发现了看起来像真实密钥的 API 密钥 | 将其移至环境变量;.env.example 中的值应为 your-key-here |
| 发布后,市场显示服务为免费或未显示 | 只运行了 publish_preview()——发布 URL ≠ 发布付费服务 | 完成整个流程:create_paid_service → publish_service |
create_paid_service:400 Free services should be published through the Project publish flow | 尝试使用了 service_type: "free_project" | 免费项目请使用 list_in_dashboard(),而不是 create_paid_service() |
publish_service:400 not in a publishable state | 服务已上架、不可用或已删除 | 检查 get_service() 状态;如果为 unavailable,则使用 restore_service() |
submit_for_review:400 Free services do not require review | 服务记录创建为 FREE 类型——付费载荷是手动构建的(缺少 service_type/钱包/定价),而不是通过 create_paid_service() 创建的 | 删除后使用 create_paid_service() 重新创建(所有付费字段都是必需的位置参数,因此通过该函数不会出现此问题) |
审核被拒:pricing_consistency 未通过 | 402 响应中的 amount 与声明的 price 不匹配 | 确保 amount = price * 1000000(USDC 为 6 位小数) |
审核被拒:api_reachable 未通过 | 端点未返回 402 | 先在端点接入 x402 收费 |
create_paid_service 响应中包含 project_slug_warning | 为 paid_api 传入了 project_slug,但该 slug 不存在于 project_listings 中 | 后端已自动将其清除。如果这是独立 API,请勿传入 project_slug。如果你想使用 Flow D,请先对项目运行 publish_preview() + list_in_dashboard(),然后使用正确的 slug 调用 update_service()。 |
删除再创建同名服务多次后,create_paid_service 返回 500 Failed to create service | 已删除的服务仍会保留其 slug(软删除),而 slug 生成只会尝试有限数量的后缀——反复删除并用同一名称重建会耗尽这些后缀 | 不要等待后重试——该名称将永久无法使用。请改用不同的服务名称,或使用 restore_service(service_id) + update_service(),而不是删除后重建 |
| 用户要求“多个 API”,却创建了多个服务 | 每个 API 都调用了一次 create_paid_service(),而不是使用 api_endpoints | 使用 Flow E:调用一次 create_paid_service() 并传入 api_endpoints=[...] 数组。只有在 API 确实互不相关时才拆分为多个服务。 |
| 外部 API(使用自有 facilitator)的购买未被记录 | 创建服务时未设置 source="manual",因此不会生成代理 URL,Starchild 也无法观测到付款 | 改用 Starchild facilitator(选项 A),或使用 source="manual" 重新创建服务(选项 B) |
source="manual" 服务的代理 URL 返回 502 | Starchild 服务器无法访问 api_endpoint URL | 确认外部 API 可公开访问,且未被防火墙拦截 |
市场显示的链与 402 accepts 中的链不一致(反之亦然) | 服务的 networks_mode 与 x402 网关的 accepts 不同步——例如,上架设置为 all(Base+Monad),但 x402 网关使用 --networks eip155:8453(自定义,仅 Base)进行收费 | 重新运行 x402 技能的 monetize,不带 --networks(以遵循 all);或者使用 update_service(service_id, networks_mode="custom", supported_networks=[...]) 以匹配网关。两者必须一致。 |
create_paid_service / update_service 因“supported_networks must be a non-empty list”而拒绝请求 | 传入了 networks_mode="custom",但 supported_networks 缺失、为空或为 None | 传入非空的 CAIP-2 id 列表(例如 ["eip155:8453"]),或切换到 networks_mode="all"(默认值)以接受所有平台主网。 |
买家无法在 Monad 上付款(402 中没有 Monad accepts),但上架信息显示 Monad | x402 网关在多链版本发布前就已设置收费,或使用了 --networks eip155:8453 | 重新运行 monetize,不带 --networks,这样 402 的 accepts 数组就会包含每条平台主网。上架的 all 模式没有问题;网关侧的配置已过时。 |
| 在一条链上结算失败,但在另一条链上正常 | Starchild facilitator 的结算器在该链上 Gas 不足(Base 需要 ETH,Monad 需要 MON) | 这是平台侧问题(Gas 由平台补贴)。另一条链仍可正常使用。请向运维团队报告——不要要求服务提供方补充 Gas。 |
封面图片上传流程
⚠️ 必须 — 每次需要设置或更改封面图片时,都要阅读本节。
网关会验证 cover_url 的域名。仅接受 storage.googleapis.com、image.thum.io
和 api.microlink.io。不要使用 imgur、数据 URI 或任何其他托管服务。
快速路径:upload_cover_image()
from skills.community_publish.exports import upload_cover_image
result = upload_cover_image("my-slug", "/path/to/image.png")
# result = {"ok": True, "public_url": "https://storage.googleapis.com/..."}
# Then use the URL:
list_in_dashboard("my-slug", name="My Project", cover_url=result["public_url"])
# or:
create_paid_service(..., cover_url=result["public_url"])
# or:
update_service(service_id, cover_url=result["public_url"])
upload_cover_image() 的内部操作
- 获取预签名 URL — 使用
slug、content_type、file_size调用POST /api/projects/cover/presign(通过Authorization: Bearer $CONTAINER_JWT传入容器 JWT) - 上传 — 将原始图片字节 PUT 到 GCS V4 签名 URL
- 返回 —
storage.googleapis.com上的public_url
支持的格式
image/png、image/jpeg、image/webp- 最大 2MB — 如有需要,请在上传前压缩
如果用户提供了大尺寸图片
# Compress first (PIL example)
from PIL import Image
import io
img = Image.open("/path/to/large.png")
img.thumbnail((1200, 630)) # reasonable cover dimensions
buf = io.BytesIO()
img.save(buf, format="JPEG", quality=85)
buf.seek(0)
# Save compressed version
compressed_path = "/tmp/cover_compressed.jpg"
with open(compressed_path, "wb") as f:
f.write(buf.getvalue())
# Upload
result = upload_cover_image("my-slug", compressed_path)
常见错误
| 错误 | 失败原因 | 修正方法 |
|---|---|---|
将 imgur URL 用作 cover_url | 域名不在允许列表中(项目/内部路由会以 400 拒绝请求) | 使用 upload_cover_image() → GCS URL |
将数据 URI 作为 cover_url 传入 | 网关返回 500(URL 过长/不是有效 URL) | 保存到文件,然后使用 upload_cover_image() |
| 使用 thum.io 截取预览页面的屏幕截图 | 无法从公网访问内部端口上的预览页面 | 使用实际图片文件调用 upload_cover_image() |
| 未阅读知识库文档 | 缺少 GCS 配置、路径约定和域名规则相关背景信息 | 务必查阅 starchild-knowledge/starchild-community-gateway/service-cover-upload.md |
前端展示 — 项目与服务
Web 前端为社区内容提供了两个独立的弹窗:
| 弹窗 | 内容类型 | 展示内容 | 查询函数 |
|---|---|---|---|
| ProjectMarketplaceModal | 免费项目 | 卡片网格:封面图片、名称、描述、标签、浏览量、收藏量 | explore_projects()、my_projects()、favorite_projects()、get_tab_counts()、get_popular_tags()、get_user_projects()、favorite_project()、unfavorite_project() |
| MarketplaceModal | 付费服务(x402) | 服务卡片:定价、评分、购买按钮 | explore_services()、list_my_services()、get_service_detail()、get_service_tags()、get_featured_services() |
两者也会显示在 AgentProfile 中(“项目”标签页/“服务”标签页)和着陆页上。
每个项目都有一个直接 URL(基于路径的 /{slug} 或子域名 {slug}.community.iamstarchild.com)。/projects 和 /services 页面是由前端渲染的浏览页面。
项目查询函数
| 函数 | 数据范围 | 用途 |
|---|---|---|
explore_projects(search, tag, sort, limit, cursor) | 公开 | 浏览所有公开项目。排序方式:all(最新)或 trending。 |
my_projects(tag) | 个人 | 列出当前用户已发布的项目及其统计数据。 |
favorite_projects(tag, limit, cursor) | 个人 | 列出当前用户收藏的项目。 |
get_tab_counts() | 个人 | 获取各标签页的计数(explore、mine、favorites、purchased、services_mine、services_favorites)。 |
get_popular_tags() | 公开 | 获取热门项目标签,用于筛选。 |
get_user_projects(user_id, limit) | 公开 | 获取特定用户的公开项目(用于个人资料页面)。 |
favorite_project(slug) | 个人 | 将项目添加到当前用户的收藏夹。 |
unfavorite_project(slug) | 个人 | 将项目从当前用户的收藏夹中移除。 |
服务查询函数
| 函数 | 数据范围 | 用途 |
|---|---|---|
explore_services(search, sort, tags, ...) | 公开 | 仅浏览独立付费服务。 |
list_my_services(cursor, limit) | 个人 | 列出当前用户的付费服务。 |
get_service_detail(service_id) | 公开 | 获取已发布服务的公开详情。 |
get_service_pricing(service_id) | 公开 | 获取经过验证的定价信息(实时 x402 验证)。 |
get_service_reviews(service_id, sort, cursor, limit) | 公开 | 列出服务的评价。 |
write_service_review(service_id, rating, comment, is_anonymous) | 个人 | 提交或更新评价(upsert)。 |
get_service_tags() | 公开 | 获取带有 i18n 名称的预定义服务标签。 |
get_featured_services() | 公开 | 获取用于首页展示的精选服务。 |
get_user_services(user_id, limit) | 公开 | 获取特定用户已发布的付费服务。 |
favorite_service(service_id) | 个人 | 将服务添加到收藏。 |
unfavorite_service(service_id) | 个人 | 将服务从收藏中移除。 |
get_favorite_services(cursor, limit) | 个人 | 列出当前用户收藏的服务。 |
get_service_purchase_status(service_id) | 个人 | 检查用户是否购买或使用过某项服务。 |
get_service_earnings(service_id) | 个人 | 获取服务的收入统计数据(仅限所有者)。 |
get_earnings_summary() | 个人 | 获取所有服务的收入汇总。 |
get_service_onchain_records(service_id, cursor, limit) | 个人 | 获取链上交易记录(仅限所有者)。 |
参考资料
lib/manifest.py— project.yaml 解析器/写入器及 semver 辅助函数lib/validate.py— 本地发布前验证(与网关侧检查一致)lib/install.py— 特定类型的安装处理程序(task/service/script)lib/gateway.py— 用于/api/register(URL)、/api/code-projects/*(代码)、/api/projects-query/*(免费列表 + 浏览)、/api/services/*(付费列表)、/api/projects/cover/presign(封面上传)的 HTTP 客户端