mirror of
https://github.com/iflytek/skillhub.git
synced 2026-08-28 11:25:00 +00:00
* feat(skill): add UPLOADED status for PRIVATE skill lifecycle ## Summary - Add UPLOADED status for PRIVATE skills after security scan passes - PRIVATE skill owners can test before confirming publish or submitting for review - Rerelease now follows visibility rules (PRIVATE→UPLOADED, PUBLIC→PENDING_REVIEW) - Auto-withdraw changes status to UPLOADED (not DRAFT) to keep versions visible ## Changes - SkillVersionStatus: Add UPLOADED enum value - SkillPublishService: PRIVATE skills go to UPLOADED after scan - SecurityScanService: Visibility-based status transition after scan - SkillGovernanceService: Withdraw→UPLOADED, delete allows UPLOADED - SkillQueryService: Include UPLOADED in version list filters - SkillReviewSubmitService: New service for submit-review and confirm-publish - SkillLifecycleController: Add submit-review and confirm-publish endpoints - Frontend: Add buttons, dialogs, and hooks for new operations ## Workflow - PRIVATE: Publish → SCANNING → UPLOADED → confirm-publish → PUBLISHED - PUBLIC: Publish → SCANNING → PENDING_REVIEW → PUBLISHED * feat(review): add backward compatibility for DRAFT status Support both DRAFT (legacy) and UPLOADED (new flow) status in: - SkillReviewSubmitService.submitForReview - SkillReviewSubmitService.confirmPublish - ReviewService.submitReview (both overloads) This ensures existing data with DRAFT status continues to work with the new visibility-based workflow introduced in OSS-02.
460 lines
19 KiB
Markdown
460 lines
19 KiB
Markdown
# OSS-01 Core 契约审计与冻结
|
||
|
||
## 1. 审计结论
|
||
|
||
SkillHub 开源项目已具备 AstronClaw 主链路所需的绝大部分 Core 能力。现有接口覆盖了 skill 唯一标识查询、版本元数据查询、创建(发布)和删除。**无需在开源 Core 中新增 AstronClaw 专属接口**;对 AstronClaw 而言,查询类和主链路类能力都应统一由 SaaS 层 `AstronClaw Adapter` 封装后对外提供,而不是直接绑定开源 Core 的接口形态。
|
||
|
||
---
|
||
|
||
## 2. Core 接口清单
|
||
|
||
以下接口构成 Core 基线能力,供 SaaS 层统一封装后对 AstronClaw 提供;这些接口本身不应被视为 AstronClaw 的长期直接契约。
|
||
|
||
### 2.1 skill 唯一标识与详情查询
|
||
|
||
| 接口 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| skill 详情 | `GET /api/v1/skills/{namespace}/{slug}` | 返回 `SkillDetailResponse`,包含完整 identity 和状态 |
|
||
| 版本解析 | `GET /api/v1/skills/{namespace}/{slug}/resolve?version=&tag=&hash=` | 返回 `ResolveVersionResponse`,解析人类可读版本选择器到精确版本 |
|
||
|
||
### 2.2 指定版本安装元数据查询
|
||
|
||
| 接口 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| 版本详情 | `GET /api/v1/skills/{namespace}/{slug}/versions/{version}` | 返回 `SkillVersionDetailResponse`,含 metadata 和 manifest |
|
||
| 版本文件列表 | `GET /api/v1/skills/{namespace}/{slug}/versions/{version}/files` | 返回 `List<SkillFileResponse>` |
|
||
| 版本下载 | `GET /api/v1/skills/{namespace}/{slug}/versions/{version}/download` | 下载指定版本 bundle |
|
||
| 版本列表 | `GET /api/v1/skills/{namespace}/{slug}/versions?page=&size=` | 分页返回版本列表 |
|
||
|
||
### 2.3 创建(发布)个人 skill
|
||
|
||
| 接口 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| 发布 skill | `POST /api/v1/skills/{namespace}/publish` | 上传包并发布,返回 `PublishResponse` |
|
||
|
||
### 2.4 删除个人 skill
|
||
|
||
| 接口 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| 硬删除(by ID) | `DELETE /api/v1/skills/id/{skillId}` | 需 SUPER_ADMIN 权限 |
|
||
| 硬删除(by 坐标) | `DELETE /api/v1/skills/{namespace}/{slug}` | 需 SUPER_ADMIN 权限 |
|
||
| 归档 | `POST /api/v1/skills/{namespace}/{slug}/archive` | owner 或 namespace admin 可操作 |
|
||
| 取消归档 | `POST /api/v1/skills/{namespace}/{slug}/unarchive` | 恢复为 ACTIVE |
|
||
|
||
### 2.5 版本生命周期
|
||
|
||
| 接口 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| 删除版本 | `DELETE /api/v1/skills/{namespace}/{slug}/versions/{version}` | 仅 DRAFT/REJECTED/SCAN_FAILED 可删 |
|
||
| 撤回审核 | `POST /api/v1/skills/{namespace}/{slug}/versions/{version}/withdraw-review` | PENDING_REVIEW → DRAFT |
|
||
| 重新发布 | `POST /api/v1/skills/{namespace}/{slug}/versions/{version}/rerelease` | 重新发布版本 |
|
||
|
||
### 2.6 ClawHub 兼容接口(已有)
|
||
|
||
| 接口 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| 解析 skill | `GET /api/v1/resolve?slug=&version=` | ClawHub 协议兼容 |
|
||
| 解析 skill(路径) | `GET /api/v1/resolve/{canonicalSlug}?version=` | ClawHub 协议兼容 |
|
||
| 下载 | `GET /api/v1/download/{canonicalSlug}?version=` | 302 重定向到下载地址 |
|
||
| 删除 skill | `DELETE /api/v1/skills/{canonicalSlug}` | owner 可操作 |
|
||
| 取消删除 | `POST /api/v1/skills/{canonicalSlug}/undelete` | owner 可操作 |
|
||
| 发布 skill | `POST /api/v1/skills` | ClawHub 协议兼容 |
|
||
| 发布到 namespace | `POST /api/v1/publish` | ClawHub 协议兼容 |
|
||
|
||
---
|
||
|
||
## 3. 字段语义冻结表
|
||
|
||
### 3.1 Skill Identity 字段
|
||
|
||
| 字段 | 类型 | 含义 | 稳定性 | 说明 |
|
||
|------|------|------|--------|------|
|
||
| `skill.id` | Long | skill 全局唯一主键 | 不可变 | 自增,创建后永不改变,可作为外部映射主键 |
|
||
| `namespace` (slug) | String(64) | skill 所属命名空间标识 | 不可变 | 全局唯一,创建后不可改名 |
|
||
| `skill.slug` | String(100) | skill 在 namespace 内的唯一标识 | 不可变 | 创建后不可改名,`namespace + slug` 构成业务坐标 |
|
||
| `skill.displayName` | String(200) | skill 展示名称 | 可变 | 仅用于展示,不可作为映射依据 |
|
||
| `skill.ownerId` | String | skill 创建者 ID | 不可变 | 创建时绑定,不可转移 |
|
||
| `skill.summary` | String(TEXT) | skill 简介 | 可变 | 展示用 |
|
||
| `skill.visibility` | Enum | 可见性 | 可变 | `PUBLIC` / `NAMESPACE_ONLY` / `PRIVATE` |
|
||
| `skill.status` | Enum | skill 状态 | 可变 | `ACTIVE` / `HIDDEN` / `ARCHIVED` |
|
||
| `skill.hidden` | boolean | 是否被管理员隐藏 | 可变 | 与 status 独立的隐藏标记 |
|
||
| `skill.latestVersionId` | Long | 最新版本指针 | 可变 | 指向当前最新已发布版本,yank/删除后自动回退 |
|
||
| `skill.downloadCount` | Long | 下载次数 | 可变 | 累计值 |
|
||
| `skill.starCount` | Integer | 收藏数 | 可变 | 累计值 |
|
||
|
||
### 3.2 SkillVersion 字段
|
||
|
||
| 字段 | 类型 | 含义 | 稳定性 | 说明 |
|
||
|------|------|------|--------|------|
|
||
| `version.id` | Long | 版本全局唯一主键 | 不可变 | 自增 |
|
||
| `version.skillId` | Long | 所属 skill ID | 不可变 | 外键 |
|
||
| `version.version` | String(64) | 版本号 | 不可变 | 如 `1.0.0`,创建后不可改 |
|
||
| `version.status` | Enum | 版本状态 | 可变 | 见状态语义表 |
|
||
| `version.bundleReady` | boolean | bundle 是否可用 | 可变 | `true` 表示 bundle 已构建完成,可下载安装 |
|
||
| `version.downloadReady` | boolean | 是否允许下载 | 可变 | yank 后设为 `false` |
|
||
| `version.publishedAt` | Instant | 发布时间 | 一次写入 | 首次发布时设置 |
|
||
| `version.parsedMetadataJson` | JSONB | 解析后的元数据 | 一次写入 | 包含 `package_name` 等运行时信息 |
|
||
| `version.manifestJson` | JSONB | manifest 原始内容 | 一次写入 | skill 包的 manifest |
|
||
| `version.changelog` | String(TEXT) | 变更日志 | 可变 | 展示用 |
|
||
| `version.fileCount` | Integer | 文件数量 | 一次写入 | 发布时确定 |
|
||
| `version.totalSize` | Long | 总大小(字节) | 一次写入 | 发布时确定 |
|
||
| `version.yankedAt` | Instant | yank 时间 | 一次写入 | yank 时设置 |
|
||
| `version.yankReason` | String(TEXT) | yank 原因 | 一次写入 | yank 时设置 |
|
||
|
||
### 3.3 关键字段含义冻结
|
||
|
||
| 字段 | 冻结定义 |
|
||
|------|----------|
|
||
| `skill_id` | `skill.id`,Long 类型自增主键,全局唯一,创建后不可变。AstronClaw 应以此作为 `external_skill_mapping` 的外部主键 |
|
||
| `namespace` | `namespace.slug`,String(64),全局唯一,不可改名。与 `slug` 组合构成业务坐标 |
|
||
| `slug` | `skill.slug`,String(100),namespace 内唯一,不可改名。`namespace/slug` 是人类可读的稳定坐标 |
|
||
| `version` | `skill_version.version`,String(64),同一 skill 内唯一,不可改。如 `1.0.0` |
|
||
| `bundle_url` | 通过 `GET /{namespace}/{slug}/versions/{version}/download` 获取,或通过 `resolve` 接口的 `downloadUrl` 字段获取。不是数据库字段,而是动态生成的下载地址 |
|
||
| `bundle_ready` | `skill_version.bundleReady`,boolean。`true` 表示 bundle 已构建完成可安装。AstronClaw 安装前必须校验此字段 |
|
||
| `package_name` | 存储在 `skill_version.parsedMetadataJson` 中,从 skill 包的 manifest 解析而来。同一 skill 跨版本应保持稳定。AstronClaw 用于运行时安装/卸载标识 |
|
||
|
||
### 3.4 Namespace 字段
|
||
|
||
| 字段 | 类型 | 含义 | 稳定性 |
|
||
|------|------|------|--------|
|
||
| `namespace.id` | Long | 命名空间主键 | 不可变 |
|
||
| `namespace.slug` | String(64) | 命名空间标识 | 不可变,全局唯一 |
|
||
| `namespace.displayName` | String(128) | 展示名称 | 可变 |
|
||
| `namespace.type` | Enum | 类型 | 不可变,`GLOBAL` / `TEAM` |
|
||
| `namespace.status` | Enum | 状态 | 可变,`ACTIVE` / `FROZEN` / `ARCHIVED` |
|
||
|
||
---
|
||
|
||
## 4. 状态语义冻结表
|
||
|
||
### 4.1 Skill 状态(`SkillStatus`)
|
||
|
||
| 状态 | 市场可见 | 可新装 | 已装是否保留 | 可被 owner 操作 | 说明 |
|
||
|------|----------|--------|------------|----------------|------|
|
||
| `ACTIVE` | 是(受 visibility 控制) | 是(需有 PUBLISHED 版本) | 是 | 是 | 正常状态 |
|
||
| `HIDDEN` | 否 | 否 | 是 | 受限 | 管理员隐藏,独立于 status 的 `hidden` 标记 |
|
||
| `ARCHIVED` | 否 | 否 | 是 | 可取消归档 | owner 或 namespace admin 归档 |
|
||
|
||
### 4.2 版本状态(`SkillVersionStatus`)
|
||
|
||
| 状态 | 是否允许安装 | 是否允许下载 | 市场可见 | 可转换到 | 说明 |
|
||
|------|------------|------------|---------|---------|------|
|
||
| `DRAFT` | 否 | 否 | 否 | SCANNING, 可删除 | 初始状态,编辑中 |
|
||
| `SCANNING` | 否 | 否 | 否 | SCAN_FAILED, PENDING_REVIEW, PUBLISHED | 安全扫描中 |
|
||
| `SCAN_FAILED` | 否 | 否 | 否 | 可删除 | 安全扫描失败 |
|
||
| `PENDING_REVIEW` | 否 | 否 | 否 | PUBLISHED, REJECTED, → DRAFT(撤回) | 等待审核 |
|
||
| `PUBLISHED` | 是 | 是 | 是 | YANKED | 已发布,可安装 |
|
||
| `REJECTED` | 否 | 否 | 否 | 可删除 | 审核拒绝 |
|
||
| `YANKED` | 否 | 否 | 否(或弱可见) | 不可逆 | 已撤回,已装不受影响 |
|
||
|
||
### 4.3 可见性(`SkillVisibility`)
|
||
|
||
| 可见性 | 市场列表可见 | 谁可查看 | 谁可安装 |
|
||
|--------|------------|---------|---------|
|
||
| `PUBLIC` | 是 | 所有人 | 所有人(需 PUBLISHED + bundleReady) |
|
||
| `NAMESPACE_ONLY` | 否 | namespace 成员 | namespace 成员 |
|
||
| `PRIVATE` | 否 | 仅 owner | 仅 owner |
|
||
|
||
### 4.4 删除语义
|
||
|
||
| 操作 | 类型 | 可逆 | 数据影响 | 已装实例影响 |
|
||
|------|------|------|---------|------------|
|
||
| 硬删除 skill | 永久删除 | 否 | 删除所有记录、文件、存储对象,slug 可复用 | 不影响,AstronClaw 已装快照独立 |
|
||
| 归档 skill | 状态变更 | 是 | 无数据删除,status → ARCHIVED | 不影响 |
|
||
| 隐藏 skill | 标记变更 | 是 | 无数据删除,hidden → true | 不影响 |
|
||
| 删除版本 | 永久删除 | 否 | 仅删除 DRAFT/REJECTED/SCAN_FAILED 版本 | 不影响(这些版本未被安装) |
|
||
| Yank 版本 | 状态变更 | 否 | status → YANKED,downloadReady → false | 不影响已装实例 |
|
||
|
||
### 4.5 AstronClaw 安装判断规则
|
||
|
||
AstronClaw 判断一个 skill 版本是否可安装,需同时满足:
|
||
|
||
```
|
||
skill.status == ACTIVE
|
||
AND skill.hidden == false
|
||
AND skill.visibility 允许当前用户访问
|
||
AND version.status == PUBLISHED
|
||
AND version.bundleReady == true
|
||
```
|
||
|
||
已安装实例不受后续状态变更影响。即使 skill 被删除/归档/隐藏,或版本被 yank,AstronClaw 本地安装快照仍可正常使用和卸载。
|
||
|
||
## 5. 错误语义表
|
||
|
||
### 5.1 统一响应结构
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "操作成功",
|
||
"data": { ... },
|
||
"timestamp": "2026-04-10T08:00:00Z",
|
||
"requestId": "req-xxx"
|
||
}
|
||
```
|
||
|
||
- `code = 0` 表示成功
|
||
- `code > 0` 表示错误,值为 HTTP 状态码
|
||
|
||
### 5.2 错误码映射
|
||
|
||
| HTTP 状态码 | 场景 | 异常类型 | 说明 |
|
||
|------------|------|---------|------|
|
||
| 400 | 参数非法 | `BadRequestException` / `DomainBadRequestException` | 请求参数校验失败 |
|
||
| 401 | 未认证 | `UnauthorizedException` / `AuthFlowException` | 未登录或 token 过期 |
|
||
| 403 | 无权限 | `ForbiddenException` / `DomainForbiddenException` | 无操作权限 |
|
||
| 404 | 未找到 | `DomainNotFoundException` | skill/version/namespace 不存在 |
|
||
| 408 | 请求超时 | `AsyncRequestTimeoutException` | 异步请求超时 |
|
||
| 503 | 存储不可用 | `StorageAccessException` | 对象存储访问失败 |
|
||
| 500 | 服务异常 | `Exception` | 未预期的内部错误 |
|
||
|
||
### 5.3 Core 主链路关键错误场景
|
||
|
||
| 场景 | HTTP 状态码 | msg 示例 | AstronClaw 处理建议 |
|
||
|------|-----------|---------|-------------------|
|
||
| skill 不存在 | 404 | `error.skill.notFound` | 映射失败,提示用户 |
|
||
| 版本不存在 | 404 | `error.skill.notFound` | 安装/升级失败,提示用户 |
|
||
| 版本不可安装(非 PUBLISHED) | 400 | `error.badRequest` | 拒绝安装,提示版本状态 |
|
||
| bundle 未就绪 | 400 | `error.badRequest` | 拒绝安装,提示稍后重试 |
|
||
| 无权访问(PRIVATE skill) | 403 | `error.forbidden` | 提示无权限 |
|
||
| namespace 不存在 | 404 | `error.namespace.notFound` | 映射失败 |
|
||
| 存储服务不可用 | 503 | `error.storage.unavailable` | 降级处理,已装 skill 不受影响 |
|
||
| 删除不允许(非 owner) | 403 | `error.forbidden` | 提示无权限 |
|
||
|
||
---
|
||
|
||
## 6. Core vs SaaS Adapter 能力分界
|
||
|
||
### 6.1 Core 已满足的能力
|
||
|
||
说明:
|
||
|
||
下表表示“开源 Core 已具备、可供 SaaS 封装”的能力,并不表示 AstronClaw 应直接调用这些开源接口。
|
||
|
||
| PRD 需求 | Core 接口 | 满足程度 | 备注 |
|
||
|---------|----------|---------|------|
|
||
| skill 唯一标识查询 | `GET /{namespace}/{slug}` | 完全满足 | 返回 `id`、`namespace`、`slug` |
|
||
| 指定版本安装元数据 | `GET /{namespace}/{slug}/versions/{version}` | 基本满足 | 返回 status、metadata;`package_name` 在 `parsedMetadataJson` 中 |
|
||
| 版本解析 | `GET /{namespace}/{slug}/resolve` | 完全满足 | 支持 version/tag/hash 解析 |
|
||
| bundle 下载 | `GET /{namespace}/{slug}/versions/{version}/download` | 完全满足 | 直接下载 |
|
||
| 创建(发布)个人 skill | `POST /{namespace}/publish` | 完全满足 | 返回 skillId、namespace、slug、version、status |
|
||
| 删除个人 skill | `DELETE /{namespace}/{slug}` (ClawHub 兼容) | 完全满足 | owner 可操作 |
|
||
| 归档 skill | `POST /{namespace}/{slug}/archive` | 完全满足 | 可逆操作 |
|
||
| 版本状态查询 | `GET /{namespace}/{slug}` 中的 headlineVersion/publishedVersion | 完全满足 | 包含版本状态 |
|
||
| labels 数据 | `GET /{namespace}/{slug}` 中的 labels 字段 | 完全满足 | 返回 `List<SkillLabelDto>` |
|
||
|
||
### 6.2 需要 SaaS Adapter 新增的能力
|
||
|
||
| PRD 需求 | 原因 | Adapter 建议 |
|
||
|---------|------|-------------|
|
||
| 市场列表查询(搜索/过滤/排序) | Core 不提供面向页面的聚合列表 | `GET /api/v1/astronclaw/adapter/skills/market` |
|
||
| 市场详情(AstronClaw DTO) | Core 返回的 DTO 包含 Core 内部字段,需适配 | `GET /api/v1/astronclaw/adapter/skills/{id}` |
|
||
| owner 维度"我创建的"查询 | Core 的 `/me/skills` 返回 Core DTO,需适配 | `GET /api/v1/astronclaw/adapter/skills/mine` |
|
||
| `is_installed` 补全 | 安装关系在 AstronClaw 侧 | AstronClaw 本地补全,不在 Adapter |
|
||
| `package_name` 顶层字段 | 当前在 `parsedMetadataJson` 内,需提取 | Adapter 解析 JSON 后平铺返回 |
|
||
| `bundle_url` 直接返回 | 当前需通过 download 接口获取 | Adapter 可直接返回预签名 URL |
|
||
| 统一 `can_install` 判断 | 需组合 status + visibility + bundleReady | Adapter 计算后返回布尔值 |
|
||
| 统一 `can_delete` 判断 | 需组合 owner + status | Adapter 计算后返回布尔值 |
|
||
|
||
### 6.3 分界原则
|
||
|
||
```
|
||
Core 负责:skill 生命周期真相(identity、version、status、artifact)
|
||
Adapter 负责:面向 AstronClaw 的 DTO 适配(字段平铺、状态聚合、权限预判断)
|
||
```
|
||
|
||
补充原则:
|
||
|
||
1. 即使开源 `Core` 已经具备某项主链路能力,`AstronClaw` 仍应统一通过 SaaS Adapter 消费。
|
||
2. 该原则同时适用于唯一标识查询、版本元数据、创建个人 skill、删除个人 skill。
|
||
3. 开源文档中的接口清单用于说明 `Core` 能力边界,不应被解读为 AstronClaw 的直接对接建议。
|
||
|
||
---
|
||
|
||
## 7. 成功 / 失败 / 边界样例
|
||
|
||
### 7.1 查询 skill identity — 成功
|
||
|
||
```
|
||
GET /api/v1/skills/my-namespace/my-skill
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"id": 42,
|
||
"slug": "my-skill",
|
||
"displayName": "My Skill",
|
||
"ownerId": "user-123",
|
||
"status": "ACTIVE",
|
||
"visibility": "PUBLIC",
|
||
"namespace": "my-namespace",
|
||
"labels": [{"slug": "nlp", "type": "CATEGORY", "displayName": "NLP"}],
|
||
"headlineVersion": {"id": 100, "version": "1.2.0", "status": "PUBLISHED"},
|
||
"publishedVersion": {"id": 100, "version": "1.2.0", "status": "PUBLISHED"}
|
||
}
|
||
}
|
||
```
|
||
|
||
AstronClaw 映射关键字段:`id=42`,`namespace=my-namespace`,`slug=my-skill`。
|
||
|
||
### 7.2 查询 skill identity — 不存在
|
||
|
||
```
|
||
GET /api/v1/skills/my-namespace/nonexistent
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 404,
|
||
"msg": "Skill not found",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
### 7.3 查询指定版本元数据 — 成功
|
||
|
||
```
|
||
GET /api/v1/skills/my-namespace/my-skill/versions/1.2.0
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"id": 100,
|
||
"version": "1.2.0",
|
||
"status": "PUBLISHED",
|
||
"changelog": "Bug fixes",
|
||
"fileCount": 3,
|
||
"totalSize": 102400,
|
||
"publishedAt": "2026-04-01T10:00:00Z",
|
||
"parsedMetadataJson": "{\"name\":\"my-skill\",\"package_name\":\"my_namespace__my_skill\",\"version\":\"1.2.0\"}",
|
||
"manifestJson": "{...}"
|
||
}
|
||
}
|
||
```
|
||
|
||
`package_name` 从 `parsedMetadataJson` 中提取。
|
||
|
||
### 7.4 查询已 YANKED 版本
|
||
|
||
```
|
||
GET /api/v1/skills/my-namespace/my-skill/versions/1.0.0
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"id": 98,
|
||
"version": "1.0.0",
|
||
"status": "YANKED",
|
||
"publishedAt": "2026-03-01T10:00:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
AstronClaw 判断 `status != PUBLISHED`,拒绝新安装。已装实例不受影响。
|
||
|
||
### 7.5 发布(创建)个人 skill — 成功
|
||
|
||
```
|
||
POST /api/v1/skills/my-namespace/publish
|
||
Content-Type: multipart/form-data
|
||
file: <skill-package.tar.gz>
|
||
visibility: PRIVATE
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"skillId": 43,
|
||
"namespace": "my-namespace",
|
||
"slug": "new-skill",
|
||
"version": "0.1.0",
|
||
"status": "DRAFT",
|
||
"fileCount": 2,
|
||
"totalSize": 51200
|
||
}
|
||
}
|
||
```
|
||
|
||
### 7.6 删除个人 skill — 成功
|
||
|
||
```
|
||
DELETE /api/v1/skills/my-namespace/my-skill
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"ok": true
|
||
}
|
||
}
|
||
```
|
||
|
||
### 7.7 删除个人 skill — 无权限
|
||
|
||
```
|
||
DELETE /api/v1/skills/other-namespace/other-skill
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 403,
|
||
"msg": "Forbidden",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
### 7.8 边界:skill 已归档后查询
|
||
|
||
```
|
||
GET /api/v1/skills/my-namespace/archived-skill
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"id": 44,
|
||
"slug": "archived-skill",
|
||
"status": "ARCHIVED",
|
||
"visibility": "PUBLIC"
|
||
}
|
||
}
|
||
```
|
||
|
||
skill 仍可查询,但 AstronClaw 应根据 `status=ARCHIVED` 判断不可新装。
|
||
|
||
---
|
||
|
||
## 8. 遗留问题与建议
|
||
|
||
### 8.1 `package_name` 提取
|
||
|
||
当前 `package_name` 嵌套在 `parsedMetadataJson` JSONB 字段中,不是顶层字段。
|
||
|
||
建议:SaaS Adapter 在返回 AstronClaw DTO 时,解析 JSON 并将 `package_name` 提取为顶层字段。Core 不需要改动。
|
||
|
||
### 8.2 `bundle_url` 获取方式
|
||
|
||
当前没有直接返回 `bundle_url` 的字段,需通过 download 接口获取。`ResolveVersionResponse` 中有 `downloadUrl` 字段。
|
||
|
||
建议:SaaS Adapter 可通过 `resolve` 接口获取 `downloadUrl`,或直接生成预签名 URL 返回给 AstronClaw。
|
||
|
||
### 8.3 删除接口权限
|
||
|
||
当前 `DELETE /api/v1/skills/{namespace}/{slug}`(portal 路径)需要 SUPER_ADMIN 权限。ClawHub 兼容接口 `DELETE /api/v1/skills/{canonicalSlug}` 允许 owner 操作。
|
||
|
||
建议:SaaS Adapter 应统一封装 owner 可操作的删除接口,对 AstronClaw 暴露稳定契约;AstronClaw 不直接依赖开源删除接口路径。
|
||
|
||
### 8.4 `hidden` 与 `status` 的关系
|
||
|
||
当前 `hidden` 是独立于 `status` 的布尔标记(管理员操作),而 `HIDDEN` 是 `SkillStatus` 枚举值之一但实际代码中 skill 的 status 枚举包含 `ACTIVE`、`HIDDEN`、`ARCHIVED`。
|
||
|
||
建议:SaaS Adapter 统一为 AstronClaw 提供一个 `is_visible` 聚合字段,屏蔽内部 hidden 标记与 status 的复杂关系。
|