From bd83f9164875145a57669dafd3d6b82cc6f9729b Mon Sep 17 00:00:00 2001 From: tww <1451548858@qq.com> Date: Sun, 15 Mar 2026 20:34:36 +0800 Subject: [PATCH] cli fix --- docs/00-product-direction.md | 2 +- docs/03-authentication-design.md | 12 ++++---- docs/05-business-flows.md | 2 +- docs/06-api-design.md | 30 +++++++++---------- docs/10-delivery-roadmap.md | 4 +-- .../2026-03-11-phase1-foundation-auth.md | 4 +-- .../2026-03-12-phase2-namespace-skill-core.md | 2 +- .../2026-03-12-phase3-review-cli-social.md | 14 ++++----- ...6-03-12-phase3-review-cli-social-design.md | 18 +++++------ .../docs/03-user-guide/discovery/install.md | 6 ++-- .../docs/03-user-guide/discovery/ratings.md | 2 +- .../docs/03-user-guide/publishing/publish.md | 4 +-- .../03-user-guide/publishing/versioning.md | 4 +-- document/docs/04-developer/api/cli-compat.md | 14 ++++----- .../03-user-guide/discovery/install.md | 6 ++-- .../03-user-guide/discovery/ratings.md | 2 +- .../03-user-guide/publishing/versioning.md | 4 +-- .../current/04-developer/api/cli-compat.md | 14 ++++----- .../compat/ClawHubCompatController.java | 22 ++++++++++++-- .../skillhub/filter/RequestLoggingFilter.java | 18 +++++++++++ .../compat/WellKnownControllerTest.java | 2 +- .../skillhub/auth/config/SecurityConfig.java | 8 ++--- .../token/ApiTokenAuthenticationFilter.java | 3 +- .../auth/token/ApiTokenScopeFilter.java | 3 +- .../auth/token/ApiTokenScopeService.java | 11 +++---- web/src/api/generated/schema.d.ts | 10 +++---- web/src/features/skill/install-command.tsx | 4 +-- 27 files changed, 129 insertions(+), 96 deletions(-) diff --git a/docs/00-product-direction.md b/docs/00-product-direction.md index fd9f4fda..da47d4ab 100644 --- a/docs/00-product-direction.md +++ b/docs/00-product-direction.md @@ -50,7 +50,7 @@ ClawHub CLI 使用单一 slug 模型,slug 校验规则为 `[a-z0-9]([a-z0-9-]* - skillhub 自有 CLI 支持两种格式输入,内部统一转换为 namespace 坐标 **Well-known 发现:** -- skillhub 服务端提供 `/.well-known/clawhub.json`,返回 `{ "apiBase": "/api/compat/v1" }` +- skillhub 服务端提供 `/.well-known/clawhub.json`,返回 `{ "apiBase": "/api/v1" }` - ClawHub CLI 通过此机制自动发现兼容层 API 基地址 ## 2. 参考项目取舍 diff --git a/docs/03-authentication-design.md b/docs/03-authentication-design.md index a07bc69c..c79746ce 100644 --- a/docs/03-authentication-design.md +++ b/docs/03-authentication-design.md @@ -458,7 +458,7 @@ Session 中存储以下字段: - 后端设置 `XSRF-TOKEN` Cookie(`HttpOnly=false`) - 前端从 Cookie 读取 Token,放入请求 Header `X-XSRF-TOKEN` - 后端校验 Header 与 Cookie 是否一致 -- CLI API(`/api/v1/**`)与兼容层(`/api/compat/v1/**`)豁免 CSRF(使用 Bearer Token,无 Cookie) +- CLI API(`/api/v1/**`)与兼容层(`/api/v1/**`)豁免 CSRF(使用 Bearer Token,无 Cookie) ## 9. 前端权限控制 @@ -639,8 +639,8 @@ window.location.href = '/oauth2/authorization/github' | 接口 | 所需凭证 | 额外判定 | |------|---------|---------| -| `GET /api/compat/v1/whoami` | 任意有效 Bearer Token | 无 | -| `GET /api/compat/v1/search` | 可选(匿名限 PUBLIC) | `SearchVisibilityScope` | -| `GET /api/compat/v1/resolve` | 可选(匿名限 PUBLIC) | visibility | -| `GET /api/compat/v1/download/{slug}/{version}` | 可选(匿名限 PUBLIC) | visibility | -| `POST /api/compat/v1/publish` | Bearer Token + `skill:publish` | 用户是目标 namespace 的 MEMBER 以上(namespace 由 canonical slug 解析) | +| `GET /api/v1/whoami` | 任意有效 Bearer Token | 无 | +| `GET /api/v1/search` | 可选(匿名限 PUBLIC) | `SearchVisibilityScope` | +| `GET /api/v1/resolve` | 可选(匿名限 PUBLIC) | visibility | +| `GET /api/v1/download/{slug}/{version}` | 可选(匿名限 PUBLIC) | visibility | +| `POST /api/v1/publish` | Bearer Token + `skill:publish` | 用户是目标 namespace 的 MEMBER 以上(namespace 由 canonical slug 解析) | diff --git a/docs/05-business-flows.md b/docs/05-business-flows.md index 73c8e794..d2f22fe6 100644 --- a/docs/05-business-flows.md +++ b/docs/05-business-flows.md @@ -49,7 +49,7 @@ - 审核通过后转为 `PUBLISHED` - 审核拒绝后转为 `REJECTED` - 例外:提交人持有 `SUPER_ADMIN` 平台角色时,发布入口直接创建 `skill_version(status=PUBLISHED)`,跳过 `review_task` 创建,同时不再要求其必须是目标 namespace 成员 -- 上述例外必须对 Web、`/api/v1/publish`、`/api/compat/v1/publish` 保持一致 +- 上述例外必须对 Web、`/api/v1/publish`、`/api/v1/publish` 保持一致 ### 对象存储写入策略 diff --git a/docs/06-api-design.md b/docs/06-api-design.md index 976ed7e2..6946db25 100644 --- a/docs/06-api-design.md +++ b/docs/06-api-design.md @@ -239,7 +239,7 @@ Public API 的可见性规则: 发布状态约束: - 普通用户发布成功后,`status` 为 `PENDING_REVIEW` -- 持有 `SUPER_ADMIN` 的用户通过 Web、`/api/v1/publish`、`/api/compat/v1/publish` 发布时,`status` 为 `PUBLISHED`,且不要求其必须是目标 namespace 成员 +- 持有 `SUPER_ADMIN` 的用户通过 Web、`/api/v1/publish`、`/api/v1/publish` 发布时,`status` 为 `PUBLISHED`,且不要求其必须是目标 namespace 成员 - 当前版本保持该审核策略,不再提供“全员直发”的运行模式 ## 7.4 Token API(需登录) @@ -387,7 +387,7 @@ Admin API 按最小权限拆分,不再统一要求 SUPER_ADMIN: ## 7.10 ClawHub CLI 兼容层 API -兼容层 API 基地址为 `/api/compat/v1`,通过 `/.well-known/clawhub.json` 发现。兼容层使用 canonical slug(双连字符映射规则,详见 `00-product-direction.md` 1.1 节)。 +兼容层 API 基地址为 `/api/v1`,通过 `/.well-known/clawhub.json` 发现。兼容层使用 canonical slug(双连字符映射规则,详见 `00-product-direction.md` 1.1 节)。 认证方式:`Authorization: Bearer `。Bearer Token 可来自 CLI Device Flow 或平台 API Token。 @@ -398,7 +398,7 @@ GET /.well-known/clawhub.json 响应: { - "apiBase": "/api/compat/v1" + "apiBase": "/api/v1" } ``` @@ -406,15 +406,15 @@ GET /.well-known/clawhub.json | 方法 | 路径 | 说明 | |------|------|------| -| GET | `/api/compat/v1/whoami` | 当前用户信息 | -| GET | `/api/compat/v1/search` | 搜索技能 | -| GET | `/api/compat/v1/resolve` | 通过 slug + version 解析版本 | -| GET | `/api/compat/v1/download/{slug}/{version}` | 下载技能 zip 包 | -| POST | `/api/compat/v1/publish` | 发布技能(multipart/form-data,`SUPER_ADMIN` 直发) | +| GET | `/api/v1/whoami` | 当前用户信息 | +| GET | `/api/v1/search` | 搜索技能 | +| GET | `/api/v1/resolve` | 通过 slug + version 解析版本 | +| GET | `/api/v1/download/{slug}/{version}` | 下载技能 zip 包 | +| POST | `/api/v1/publish` | 发布技能(multipart/form-data,`SUPER_ADMIN` 直发) | ### 兼容层请求/响应格式 -**GET `/api/compat/v1/whoami`** +**GET `/api/v1/whoami`** ```json { @@ -424,7 +424,7 @@ GET /.well-known/clawhub.json } ``` -**GET `/api/compat/v1/search?q={keyword}&page={page}&limit={limit}`** +**GET `/api/v1/search?q={keyword}&page={page}&limit={limit}`** ```json { @@ -449,21 +449,21 @@ GET /.well-known/clawhub.json 注意:兼容层返回的 `slug` 为 canonical slug 格式(全局空间直接返回 skill slug,团队空间返回 `namespace--skill`)。 -**GET `/api/compat/v1/resolve?slug={slug}&version={version}`** +**GET `/api/v1/resolve?slug={slug}&version={version}`** ```json { "slug": "my-skill", "version": "1.2.0", - "downloadUrl": "/api/compat/v1/download/my-skill/1.2.0" + "downloadUrl": "/api/v1/download/my-skill/1.2.0" } ``` -**GET `/api/compat/v1/download/{slug}/{version}`** +**GET `/api/v1/download/{slug}/{version}`** 返回指定版本的 zip 文件流。默认版本解析由 `resolve` 接口负责。 -**POST `/api/compat/v1/publish`** +**POST `/api/v1/publish`** ``` Content-Type: multipart/form-data @@ -521,7 +521,7 @@ Parts: - 版本策略:URL path 版本 `/api/v1/` - 幂等性:写操作通过 `X-Request-Id` + Redis 去重(TTL 24h) -### Compatibility API(`/api/compat/v1/*`) +### Compatibility API(`/api/v1/*`) - 响应格式完全遵循 ClawHub 协议,不套统一响应包裹 - 错误响应遵循 ClawHub 格式:`{ error: string, message: string }` diff --git a/docs/10-delivery-roadmap.md b/docs/10-delivery-roadmap.md index 5ef9eb3c..924dd84c 100644 --- a/docs/10-delivery-roadmap.md +++ b/docs/10-delivery-roadmap.md @@ -9,7 +9,7 @@ - 一期同步发布模型,暂不考虑异步发布 - API Token 一期继承用户全部权限(非最小权限),后续版本细化 - CLI 主认证切换为 OAuth Device Flow,Bearer Token 统一用于 CLI API 和兼容层 -- ClawHub CLI 兼容层基地址 `/api/compat/v1`,通过 `/.well-known/clawhub.json` 发现 +- ClawHub CLI 兼容层基地址 `/api/v1`,通过 `/.well-known/clawhub.json` 发现 ## Phase 1:工程骨架 + 认证打通 @@ -75,7 +75,7 @@ - 评分 + 收藏 + 计数器(原子更新) - OAuth Device Flow(device code、授权确认、轮询换取 Bearer Token) - CLI API(whoami、publish、resolve、check) -- ClawHub CLI 协议兼容层(`/api/compat/v1` 端点:search、resolve、download、publish、whoami) +- ClawHub CLI 协议兼容层(`/api/v1` 端点:search、resolve、download、publish、whoami) - 兼容层 canonical slug 映射(`--` 双连字符规则) - `/.well-known/clawhub.json` 发现端点 - 协议适配器与兼容性测试(针对 ClawHub CLI 的真实请求/响应样例) diff --git a/docs/superpowers/plans/2026-03-11-phase1-foundation-auth.md b/docs/superpowers/plans/2026-03-11-phase1-foundation-auth.md index 84b3914e..41711bc7 100644 --- a/docs/superpowers/plans/2026-03-11-phase1-foundation-auth.md +++ b/docs/superpowers/plans/2026-03-11-phase1-foundation-auth.md @@ -2756,7 +2756,7 @@ public class ApiTokenAuthenticationFilter extends OncePerRequestFilter { // 仅对 CLI 和 Token API 路径生效 String path = request.getRequestURI(); return !(path.startsWith("/api/v1/cli/") || path.startsWith("/api/v1/tokens") - || path.startsWith("/api/compat/")); + || path.startsWith("/api/")); } } ``` @@ -2974,7 +2974,7 @@ public class SecurityConfig { .csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .csrfTokenRequestHandler(csrfHandler) - .ignoringRequestMatchers("/api/v1/cli/**", "/api/compat/**") + .ignoringRequestMatchers("/api/v1/cli/**", "/api/**") ) .authorizeHttpRequests(auth -> auth // 公开端点 diff --git a/docs/superpowers/plans/2026-03-12-phase2-namespace-skill-core.md b/docs/superpowers/plans/2026-03-12-phase2-namespace-skill-core.md index f49935be..ead018da 100644 --- a/docs/superpowers/plans/2026-03-12-phase2-namespace-skill-core.md +++ b/docs/superpowers/plans/2026-03-12-phase2-namespace-skill-core.md @@ -6709,7 +6709,7 @@ export function FileTree({ files, onSelect }: { import { CopyButton } from '@/shared/components/copy-button'; export function InstallCommand({ namespace, slug }: { namespace: string; slug: string }) { - const command = `skillhub install @${namespace}/${slug}`; + const command = `clawhub install @${namespace}/${slug}`; return (
{command} diff --git a/docs/superpowers/plans/2026-03-12-phase3-review-cli-social.md b/docs/superpowers/plans/2026-03-12-phase3-review-cli-social.md index 2a47bd6f..bf9548e2 100644 --- a/docs/superpowers/plans/2026-03-12-phase3-review-cli-social.md +++ b/docs/superpowers/plans/2026-03-12-phase3-review-cli-social.md @@ -6428,7 +6428,7 @@ class WellKnownControllerTest { void returns_api_base() throws Exception { mockMvc.perform(get("/.well-known/clawhub.json")) .andExpect(status().isOk()) - .andExpect(jsonPath("$.apiBase").value("/api/compat/v1")); + .andExpect(jsonPath("$.apiBase").value("/api/v1")); } } ``` @@ -6446,7 +6446,7 @@ import java.util.Map; public class WellKnownController { @GetMapping("/.well-known/clawhub.json") public Map clawHubDiscovery() { - return Map.of("apiBase", "/api/compat/v1"); + return Map.of("apiBase", "/api/v1"); } } ``` @@ -6527,7 +6527,7 @@ class ClawHubCompatControllerTest { @Test void search_returns_compat_format() throws Exception { - mockMvc.perform(get("/api/compat/v1/search").param("q", "test")) + mockMvc.perform(get("/api/v1/search").param("q", "test")) .andExpect(status().isOk()) .andExpect(jsonPath("$.items").isArray()); } @@ -6536,14 +6536,14 @@ class ClawHubCompatControllerTest { void resolve_parses_canonical_slug() throws Exception { when(slugMapper.fromCanonical("my-skill")) .thenReturn(new SkillCoordinate("global", "my-skill")); - mockMvc.perform(get("/api/compat/v1/resolve") + mockMvc.perform(get("/api/v1/resolve") .param("slug", "my-skill")) .andExpect(status().isOk()); } @Test void whoami_requires_auth() throws Exception { - mockMvc.perform(get("/api/compat/v1/whoami")) + mockMvc.perform(get("/api/v1/whoami")) .andExpect(status().isUnauthorized()); } } @@ -6566,7 +6566,7 @@ import org.springframework.web.multipart.MultipartFile; import java.util.List; @RestController -@RequestMapping("/api/compat/v1") +@RequestMapping("/api/v1") public class ClawHubCompatController { private final CanonicalSlugMapper slugMapper; @@ -6591,7 +6591,7 @@ public class ClawHubCompatController { SkillCoordinate coord = slugMapper.fromCanonical(slug); // TODO: 调用 SkillQueryService 获取版本详情 return new ClawHubResolveResponse(slug, "", version, - "/api/compat/v1/download/" + slug + "/" + version, 0, 0); + "/api/v1/download/" + slug + "/" + version, 0, 0); } @GetMapping("/download/{slug}/{version}") diff --git a/docs/superpowers/specs/2026-03-12-phase3-review-cli-social-design.md b/docs/superpowers/specs/2026-03-12-phase3-review-cli-social-design.md index 08316d4d..95781606 100644 --- a/docs/superpowers/specs/2026-03-12-phase3-review-cli-social-design.md +++ b/docs/superpowers/specs/2026-03-12-phase3-review-cli-social-design.md @@ -1016,14 +1016,14 @@ GET /.well-known/clawhub.json Response 200: { - "apiBase": "/api/compat/v1" + "apiBase": "/api/v1" } ``` #### search - 搜索技能 ``` -GET /api/compat/v1/search?q=keyword&page=0&size=20 +GET /api/v1/search?q=keyword&page=0&size=20 Authorization: Bearer sk_xxx(可选) Response 200: @@ -1057,7 +1057,7 @@ Response 200: #### resolve - 解析技能版本 ``` -GET /api/compat/v1/resolve?slug=my-skill&version=1.2.0 +GET /api/v1/resolve?slug=my-skill&version=1.2.0 Authorization: Bearer sk_xxx(可选) Response 200: @@ -1065,7 +1065,7 @@ Response 200: "slug": "my-skill", "name": "My Skill", "version": "1.2.0", - "downloadUrl": "/api/compat/v1/download/my-skill/1.2.0", + "downloadUrl": "/api/v1/download/my-skill/1.2.0", "fileCount": 5, "totalSize": 12345 } @@ -1079,7 +1079,7 @@ Response 200: #### download - 下载技能包 ``` -GET /api/compat/v1/download/{slug}/{version} +GET /api/v1/download/{slug}/{version} Authorization: Bearer sk_xxx(可选) Response 200: @@ -1097,7 +1097,7 @@ Content-Disposition: attachment; filename="my-skill-1.2.0.zip" #### publish - 发布技能 ``` -POST /api/compat/v1/publish +POST /api/v1/publish Authorization: Bearer sk_xxx Content-Type: multipart/form-data @@ -1118,7 +1118,7 @@ Response 200: #### whoami - 查询当前用户 ``` -GET /api/compat/v1/whoami +GET /api/v1/whoami Authorization: Bearer sk_xxx Response 200: @@ -1135,7 +1135,7 @@ Response 200: ```java @RestController -@RequestMapping("/api/compat/v1") +@RequestMapping("/api/v1") public class ClawHubCompatController { private final CanonicalSlugMapper slugMapper; @@ -1193,7 +1193,7 @@ public class ClawHubCompatController { slug, detail.displayName(), detail.version(), - "/api/compat/v1/download/" + slug + "/" + detail.version(), + "/api/v1/download/" + slug + "/" + detail.version(), detail.fileCount(), detail.totalSize() ); diff --git a/document/docs/03-user-guide/discovery/install.md b/document/docs/03-user-guide/discovery/install.md index 33ca1649..9cdf9254 100644 --- a/document/docs/03-user-guide/discovery/install.md +++ b/document/docs/03-user-guide/discovery/install.md @@ -11,19 +11,19 @@ description: 安装和使用技能 ### 安装最新版本 ```bash -skillhub install @team/my-skill +clawhub install @team/my-skill ``` ### 安装指定版本 ```bash -skillhub install @team/my-skill@1.2.0 +clawhub install @team/my-skill@1.2.0 ``` ### 按标签安装 ```bash -skillhub install @team/my-skill@beta +clawhub install @team/my-skill@beta ``` ### 使用 ClawHub CLI 安装 diff --git a/document/docs/03-user-guide/discovery/ratings.md b/document/docs/03-user-guide/discovery/ratings.md index 7e4ec9db..8745a87f 100644 --- a/document/docs/03-user-guide/discovery/ratings.md +++ b/document/docs/03-user-guide/discovery/ratings.md @@ -12,7 +12,7 @@ description: 技能评分和收藏功能 查看已收藏的技能: - Web:进入"我的收藏" -- CLI:`skillhub stars` +- CLI:`clawhub stars` ## 技能评分 diff --git a/document/docs/03-user-guide/publishing/publish.md b/document/docs/03-user-guide/publishing/publish.md index 98040ea0..4fd86f69 100644 --- a/document/docs/03-user-guide/publishing/publish.md +++ b/document/docs/03-user-guide/publishing/publish.md @@ -21,10 +21,10 @@ description: 发布技能到 SkillHub ```bash # 1. 登录 -skillhub login +clawhub login # 2. 发布 -skillhub publish ./my-skill.zip --namespace @team-myteam +clawhub publish ./my-skill.zip --namespace @team-myteam ``` ## 通过 ClawHub CLI 发布 diff --git a/document/docs/03-user-guide/publishing/versioning.md b/document/docs/03-user-guide/publishing/versioning.md index 2cb2c20b..a0a4d05f 100644 --- a/document/docs/03-user-guide/publishing/versioning.md +++ b/document/docs/03-user-guide/publishing/versioning.md @@ -31,13 +31,13 @@ SkillHub 使用语义化版本(Semantic Versioning):`MAJOR.MINOR.PATCH` ### 创建/移动标签 ```bash -skillhub tag set @team/my-skill beta 1.2.0 +clawhub tag set @team/my-skill beta 1.2.0 ``` ### 删除标签 ```bash -skillhub tag delete @team/my-skill beta +clawhub tag delete @team/my-skill beta ``` ## 版本撤回 diff --git a/document/docs/04-developer/api/cli-compat.md b/document/docs/04-developer/api/cli-compat.md index 04ac889f..07f54afe 100644 --- a/document/docs/04-developer/api/cli-compat.md +++ b/document/docs/04-developer/api/cli-compat.md @@ -18,7 +18,7 @@ GET /.well-known/clawhub.json ```json { - "apiBase": "/api/compat/v1" + "apiBase": "/api/v1" } ``` @@ -27,7 +27,7 @@ GET /.well-known/clawhub.json ### Whoami ```http -GET /api/compat/v1/whoami +GET /api/v1/whoami ``` 响应: @@ -43,7 +43,7 @@ GET /api/compat/v1/whoami ### 搜索 ```http -GET /api/compat/v1/search?q={keyword}&page={page}&limit={limit} +GET /api/v1/search?q={keyword}&page={page}&limit={limit} ``` 响应: @@ -75,7 +75,7 @@ GET /api/compat/v1/search?q={keyword}&page={page}&limit={limit} ### 解析 ```http -GET /api/compat/v1/resolve?slug={slug}&version={version} +GET /api/v1/resolve?slug={slug}&version={version} ``` 响应: @@ -84,20 +84,20 @@ GET /api/compat/v1/resolve?slug={slug}&version={version} { "slug": "my-skill", "version": "1.2.0", - "downloadUrl": "/api/compat/v1/download/my-skill/1.2.0" + "downloadUrl": "/api/v1/download/my-skill/1.2.0" } ``` ### 下载 ```http -GET /api/compat/v1/download/{slug}/{version} +GET /api/v1/download/{slug}/{version} ``` ### 发布 ```http -POST /api/compat/v1/publish +POST /api/v1/publish Content-Type: multipart/form-data file: diff --git a/document/i18n/en/docusaurus-plugin-content-docs/current/03-user-guide/discovery/install.md b/document/i18n/en/docusaurus-plugin-content-docs/current/03-user-guide/discovery/install.md index 2a3e5f7b..a3f2bc74 100644 --- a/document/i18n/en/docusaurus-plugin-content-docs/current/03-user-guide/discovery/install.md +++ b/document/i18n/en/docusaurus-plugin-content-docs/current/03-user-guide/discovery/install.md @@ -11,19 +11,19 @@ description: Install and use skills ### Install Latest Version ```bash -skillhub install @team/my-skill +clawhub install @team/my-skill ``` ### Install Specific Version ```bash -skillhub install @team/my-skill@1.2.0 +clawhub install @team/my-skill@1.2.0 ``` ### Install by Tag ```bash -skillhub install @team/my-skill@beta +clawhub install @team/my-skill@beta ``` ### Install with ClawHub CLI diff --git a/document/i18n/en/docusaurus-plugin-content-docs/current/03-user-guide/discovery/ratings.md b/document/i18n/en/docusaurus-plugin-content-docs/current/03-user-guide/discovery/ratings.md index 15673a55..3dbf1abf 100644 --- a/document/i18n/en/docusaurus-plugin-content-docs/current/03-user-guide/discovery/ratings.md +++ b/document/i18n/en/docusaurus-plugin-content-docs/current/03-user-guide/discovery/ratings.md @@ -12,7 +12,7 @@ Click the "Favorite" button on the skill detail page to favorite a skill. View your favorite skills: - Web: Go to "My Favorites" -- CLI: `skillhub stars` +- CLI: `clawhub stars` ## Skill Rating diff --git a/document/i18n/en/docusaurus-plugin-content-docs/current/03-user-guide/publishing/versioning.md b/document/i18n/en/docusaurus-plugin-content-docs/current/03-user-guide/publishing/versioning.md index 19bad7c2..f73e9393 100644 --- a/document/i18n/en/docusaurus-plugin-content-docs/current/03-user-guide/publishing/versioning.md +++ b/document/i18n/en/docusaurus-plugin-content-docs/current/03-user-guide/publishing/versioning.md @@ -31,13 +31,13 @@ You can create custom tags for version channel management: ### Create/Move Tag ```bash -skillhub tag set @team/my-skill beta 1.2.0 +clawhub tag set @team/my-skill beta 1.2.0 ``` ### Delete Tag ```bash -skillhub tag delete @team/my-skill beta +clawhub tag delete @team/my-skill beta ``` ## Version Withdrawal diff --git a/document/i18n/en/docusaurus-plugin-content-docs/current/04-developer/api/cli-compat.md b/document/i18n/en/docusaurus-plugin-content-docs/current/04-developer/api/cli-compat.md index 5a8b8f7c..eac1fb54 100644 --- a/document/i18n/en/docusaurus-plugin-content-docs/current/04-developer/api/cli-compat.md +++ b/document/i18n/en/docusaurus-plugin-content-docs/current/04-developer/api/cli-compat.md @@ -18,7 +18,7 @@ Response: ```json { - "apiBase": "/api/compat/v1" + "apiBase": "/api/v1" } ``` @@ -27,7 +27,7 @@ Response: ### Whoami ```http -GET /api/compat/v1/whoami +GET /api/v1/whoami ``` Response: @@ -43,7 +43,7 @@ Response: ### Search ```http -GET /api/compat/v1/search?q={keyword}&page={page}&limit={limit} +GET /api/v1/search?q={keyword}&page={page}&limit={limit} ``` Response: @@ -75,7 +75,7 @@ Response: ### Resolve ```http -GET /api/compat/v1/resolve?slug={slug}&version={version} +GET /api/v1/resolve?slug={slug}&version={version} ``` Response: @@ -84,20 +84,20 @@ Response: { "slug": "my-skill", "version": "1.2.0", - "downloadUrl": "/api/compat/v1/download/my-skill/1.2.0" + "downloadUrl": "/api/v1/download/my-skill/1.2.0" } ``` ### Download ```http -GET /api/compat/v1/download/{slug}/{version} +GET /api/v1/download/{slug}/{version} ``` ### Publish ```http -POST /api/compat/v1/publish +POST /api/v1/publish Content-Type: multipart/form-data file: diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/compat/ClawHubCompatController.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/compat/ClawHubCompatController.java index 151e3315..6436b8ad 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/compat/ClawHubCompatController.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/compat/ClawHubCompatController.java @@ -188,8 +188,8 @@ public class ClawHubCompatController { } @GetMapping("/download/{canonicalSlug}") - public ResponseEntity download(@PathVariable String canonicalSlug, - @RequestParam(defaultValue = "latest") String version) { + public ResponseEntity downloadByPath(@PathVariable String canonicalSlug, + @RequestParam(defaultValue = "latest") String version) { SkillCoordinate coord = mapper.fromCanonical(canonicalSlug); String location = "latest".equals(version) ? "/api/v1/skills/" + coord.namespace() + "/" + coord.slug() + "/download" @@ -199,6 +199,22 @@ public class ClawHubCompatController { .build(); } + @GetMapping("/download") + public ResponseEntity downloadByQuery(@RequestParam String slug, + @RequestParam(defaultValue = "latest") String version) { + // For query param version, slug is just the skill slug without namespace + Skill skill = skillRepository.findBySlug(slug).stream().findFirst() + .orElseThrow(() -> new DomainNotFoundException("error.skill.notFound", slug)); + Namespace ns = namespaceRepository.findById(skill.getNamespaceId()) + .orElseThrow(() -> new DomainNotFoundException("error.namespace.notFound", skill.getNamespaceId())); + String location = "latest".equals(version) + ? "/api/v1/skills/" + ns.getSlug() + "/" + skill.getSlug() + "/download" + : "/api/v1/skills/" + ns.getSlug() + "/" + skill.getSlug() + "/versions/" + version + "/download"; + return ResponseEntity.status(HttpStatus.FOUND) + .header(HttpHeaders.LOCATION, location) + .build(); + } + @GetMapping("/skills") public ClawHubSearchResponse listSkills( @RequestParam(defaultValue = "0") int page, @@ -269,7 +285,7 @@ public class ClawHubCompatController { versionInfo = new ClawHubSkillResponse.VersionInfo( latestVersionEntity.getVersion(), versionCreatedAt, - latestVersionEntity.getChangelog(), + latestVersionEntity.getChangelog() == null ? "" : latestVersionEntity.getChangelog(), null // license ); } diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/filter/RequestLoggingFilter.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/filter/RequestLoggingFilter.java index db1e5572..7c46bd34 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/filter/RequestLoggingFilter.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/filter/RequestLoggingFilter.java @@ -60,6 +60,12 @@ public class RequestLoggingFilter extends OncePerRequestFilter { } sb.append("Response Status: ").append(response.getStatus()).append("\n"); + + String responseBody = getResponseBody(response); + if (responseBody != null && !responseBody.isBlank()) { + sb.append("Response Body: ").append(responseBody).append("\n"); + } + sb.append("Duration: ").append(duration).append("ms\n"); sb.append("==================================="); @@ -87,4 +93,16 @@ public class RequestLoggingFilter extends OncePerRequestFilter { } return null; } + + private String getResponseBody(ContentCachingResponseWrapper response) { + byte[] buf = response.getContentAsByteArray(); + if (buf.length > 0) { + try { + return new String(buf, response.getCharacterEncoding()); + } catch (UnsupportedEncodingException e) { + return "[unknown encoding]"; + } + } + return null; + } } diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/compat/WellKnownControllerTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/compat/WellKnownControllerTest.java index eca4f21b..df14d907 100644 --- a/server/skillhub-app/src/test/java/com/iflytek/skillhub/compat/WellKnownControllerTest.java +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/compat/WellKnownControllerTest.java @@ -31,6 +31,6 @@ class WellKnownControllerTest { void clawhubConfig_returns_apiBase() throws Exception { mockMvc.perform(get("/.well-known/clawhub.json")) .andExpect(status().isOk()) - .andExpect(jsonPath("$.apiBase").value("/api/compat/v1")); + .andExpect(jsonPath("$.apiBase").value("/api/v1")); } } diff --git a/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/config/SecurityConfig.java b/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/config/SecurityConfig.java index a3cecd29..0208e7b1 100644 --- a/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/config/SecurityConfig.java +++ b/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/config/SecurityConfig.java @@ -88,7 +88,7 @@ public class SecurityConfig { if (path == null) { return false; } - return path.startsWith("/api/compat/") + return path.startsWith("/api/") || path.equals("/api/v1/publish") || path.startsWith("/api/v1/auth/device/"); }; @@ -117,9 +117,9 @@ public class SecurityConfig { "/v3/api-docs/**", "/swagger-ui/**", "/.well-known/**", - "/api/compat/v1/search", - "/api/compat/v1/resolve/**", - "/api/compat/v1/download/**" + "/api/v1/search", + "/api/v1/resolve/**", + "/api/v1/download/**" ).permitAll() .requestMatchers("/actuator/prometheus").hasAnyRole("SUPER_ADMIN", "AUDITOR") .requestMatchers( diff --git a/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/token/ApiTokenAuthenticationFilter.java b/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/token/ApiTokenAuthenticationFilter.java index 75e34918..3b227842 100644 --- a/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/token/ApiTokenAuthenticationFilter.java +++ b/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/token/ApiTokenAuthenticationFilter.java @@ -84,7 +84,6 @@ public class ApiTokenAuthenticationFilter extends OncePerRequestFilter { protected boolean shouldNotFilter(HttpServletRequest request) { String path = request.getRequestURI(); return !(path.startsWith("/api/v1/") - || path.startsWith("/api/web/") - || path.startsWith("/api/compat/")); + || path.startsWith("/api/web/")); } } diff --git a/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/token/ApiTokenScopeFilter.java b/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/token/ApiTokenScopeFilter.java index c5a55832..aea4dfe0 100644 --- a/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/token/ApiTokenScopeFilter.java +++ b/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/token/ApiTokenScopeFilter.java @@ -67,8 +67,7 @@ public class ApiTokenScopeFilter extends OncePerRequestFilter { protected boolean shouldNotFilter(HttpServletRequest request) { String path = request.getRequestURI(); return path == null || (!path.startsWith("/api/v1/") - && !path.startsWith("/api/web/") - && !path.startsWith("/api/compat/")); + && !path.startsWith("/api/web/")); } private boolean isApiTokenAuthentication(Authentication authentication) { diff --git a/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/token/ApiTokenScopeService.java b/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/token/ApiTokenScopeService.java index d5477d00..69c569c2 100644 --- a/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/token/ApiTokenScopeService.java +++ b/server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/token/ApiTokenScopeService.java @@ -31,9 +31,10 @@ public class ApiTokenScopeService { ScopeRule.allow("GET", "/api/v1/namespaces/*"), ScopeRule.allow("GET", "/api/web/namespaces"), ScopeRule.allow("GET", "/api/web/namespaces/*"), - ScopeRule.allow("GET", "/api/compat/v1/search"), - ScopeRule.allow("GET", "/api/compat/v1/resolve/**"), - ScopeRule.allow("GET", "/api/compat/v1/whoami"), + ScopeRule.allow("GET", "/api/v1/search"), + ScopeRule.allow("GET", "/api/v1/resolve/**"), + ScopeRule.allow("GET", "/api/v1/whoami"), + ScopeRule.allow("GET", "/api/v1/download"), ScopeRule.allow(null, "/.well-known/**"), ScopeRule.allow(null, "/actuator/health"), ScopeRule.allow(null, "/v3/api-docs/**"), @@ -47,7 +48,7 @@ public class ApiTokenScopeService { ScopeRule.require("POST", "/api/v1/skills/*/publish", "skill:publish"), ScopeRule.require("POST", "/api/web/skills/*/publish", "skill:publish"), ScopeRule.require("POST", "/api/v1/publish", "skill:publish"), - ScopeRule.require("POST", "/api/compat/v1/publish", "skill:publish") + ScopeRule.require("POST", "/api/v1/publish", "skill:publish") ); private final ObjectMapper objectMapper; @@ -103,7 +104,7 @@ public class ApiTokenScopeService { } private boolean isApiPath(String path) { - return path != null && (path.startsWith("/api/v1/") || path.startsWith("/api/web/") || path.startsWith("/api/compat/")); + return path != null && (path.startsWith("/api/v1/") || path.startsWith("/api/web/") || path.startsWith("/api/")); } public record AuthorizationDecision(boolean allowed, String requiredScope, String message) { diff --git a/web/src/api/generated/schema.d.ts b/web/src/api/generated/schema.d.ts index b1917563..542100c2 100644 --- a/web/src/api/generated/schema.d.ts +++ b/web/src/api/generated/schema.d.ts @@ -564,7 +564,7 @@ export interface paths { patch?: never; trace?: never; }; - "/api/compat/v1/publish": { + "/api/v1/publish": { parameters: { query?: never; header?: never; @@ -1012,7 +1012,7 @@ export interface paths { patch?: never; trace?: never; }; - "/api/compat/v1/whoami": { + "/api/v1/whoami": { parameters: { query?: never; header?: never; @@ -1028,7 +1028,7 @@ export interface paths { patch?: never; trace?: never; }; - "/api/compat/v1/search": { + "/api/v1/search": { parameters: { query?: never; header?: never; @@ -1044,7 +1044,7 @@ export interface paths { patch?: never; trace?: never; }; - "/api/compat/v1/resolve/{canonicalSlug}": { + "/api/v1/resolve/{canonicalSlug}": { parameters: { query?: never; header?: never; @@ -1060,7 +1060,7 @@ export interface paths { patch?: never; trace?: never; }; - "/api/compat/v1/download/{canonicalSlug}": { + "/api/v1/download/{canonicalSlug}": { parameters: { query?: never; header?: never; diff --git a/web/src/features/skill/install-command.tsx b/web/src/features/skill/install-command.tsx index 12a18a32..49ed15c7 100644 --- a/web/src/features/skill/install-command.tsx +++ b/web/src/features/skill/install-command.tsx @@ -13,8 +13,8 @@ export function InstallCommand({ namespace, slug, version }: InstallCommandProps const { t } = useTranslation() const [copied, setCopied] = useState(false) const command = version - ? `skillhub install ${namespace}/${slug}@${version}` - : `skillhub install ${namespace}/${slug}` + ? `clawhub install ${namespace}/${slug}@${version}` + : `clawhub install ${namespace}/${slug}` const handleCopy = async () => { try {