cckit 0.3.0__tar.gz → 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cckit-0.3.0 → cckit-0.3.1}/Docs/05-design-log.md +6 -3
- {cckit-0.3.0 → cckit-0.3.1}/Docs/11-web-panel.md +22 -1
- {cckit-0.3.0 → cckit-0.3.1}/Docs/README.md +11 -2
- {cckit-0.3.0 → cckit-0.3.1}/Docs/cli-spec.md +21 -0
- {cckit-0.3.0 → cckit-0.3.1}/PKG-INFO +6 -2
- {cckit-0.3.0 → cckit-0.3.1}/README.md +5 -1
- cckit-0.3.1/hatch_build.py +34 -0
- {cckit-0.3.0 → cckit-0.3.1}/pyproject.toml +1 -1
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/__init__.py +1 -1
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/alt.py +15 -5
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/cli.py +3 -1
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/web/app.py +49 -8
- cckit-0.3.1/tests/test_web_base.py +51 -0
- {cckit-0.3.0 → cckit-0.3.1}/uv.lock +1 -1
- cckit-0.3.0/web/dist/assets/index-Cm-V9D5_.js → cckit-0.3.1/web/dist/assets/index-CSuOYCbo.js +15 -15
- cckit-0.3.0/web/dist/assets/index-RaOeRefK.css → cckit-0.3.1/web/dist/assets/index-Cp1Xoa9J.css +1 -1
- {cckit-0.3.0 → cckit-0.3.1}/web/dist/index.html +13 -13
- {cckit-0.3.0 → cckit-0.3.1}/web/index.html +12 -12
- {cckit-0.3.0 → cckit-0.3.1}/web/package-lock.json +6233 -6233
- {cckit-0.3.0 → cckit-0.3.1}/web/package.json +38 -38
- {cckit-0.3.0 → cckit-0.3.1}/web/src/components/AddKitDialog.tsx +10 -3
- {cckit-0.3.0 → cckit-0.3.1}/web/src/components/AppSidebar.tsx +1 -1
- {cckit-0.3.0 → cckit-0.3.1}/web/src/lib/api.ts +27 -16
- {cckit-0.3.0 → cckit-0.3.1}/web/tsconfig.node.tsbuildinfo +1 -1
- {cckit-0.3.0 → cckit-0.3.1}/web/vite.config.js +3 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/vite.config.ts +26 -23
- cckit-0.3.0/hatch_build.py +0 -27
- {cckit-0.3.0 → cckit-0.3.1}/.gitignore +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/.gitignore +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/01-overview.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/02-architecture.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/03-manifest-spec.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/04-cli-spec.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/06-platform-findings.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/07-security.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/08-installation.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/09-state-api.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/10-kit-authoring.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/examples/video-toolkit/cckit.yaml +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/kit-development.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/pics/architecture.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/pics/directory-layout.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/pics/four-state-model.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/pics/install-flow.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/pics/pain-points-solution.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/pics/state-transitions.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/schema/cckit.schema.json +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/test/hello-skill/SKILL.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/test/test-skill-dependency/.gitignore +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/test/test-skill-dependency/README.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/test/test-skill-dependency/json-pretty/SKILL.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/test/test-skill-dependency/json-pretty/scripts/pretty.js +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/test/test-skill-dependency/package.json +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/test/test-skill-dependency/plain-advisor/SKILL.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/test/test-skill-dependency/requirements.txt +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/test/test-skill-dependency/test-skill-dependency/SKILL.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/Docs/test/test-skill-dependency/test-skill-dependency/scripts/test.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/LICENSE +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/skills/kit-builder/cckit.yaml +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/skills/kit-builder/kit-builder/SKILL.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/skills/kit-builder/kit-builder/kit-authoring.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/skills/kit-builder/kit-builder/manifest-spec.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/skills/kit-builder/kit-builder/templates/SKILL.md +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/skills/kit-builder/kit-builder/templates/cckit.yaml +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/__main__.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/cckit.schema.json +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/config.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/doctor.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/env.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/errors.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/exec.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/installer.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/link.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/lint.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/manifest.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/projects.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/registry.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/schema.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/state.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/src/cckit/web/__init__.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/tests/conftest.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/tests/helpers.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/tests/test_alt.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/tests/test_budget.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/tests/test_env.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/tests/test_exec.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/tests/test_installer.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/tests/test_link.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/tests/test_lint.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/tests/test_manifest.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/tests/test_registry.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/tests/test_state.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/tests/test_web_alt.py +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/.gitignore +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/components.json +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/App.tsx +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/components/AddScopeDialog.tsx +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/components/DoctorDialog.tsx +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/components/KitList.tsx +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/components/ui/badge.tsx +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/components/ui/button.tsx +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/components/ui/card.tsx +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/components/ui/dialog.tsx +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/components/ui/dropdown-menu.tsx +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/components/ui/input.tsx +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/components/ui/popover.tsx +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/components/ui/progress.tsx +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/components/ui/separator.tsx +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/components/ui/sheet.tsx +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/components/ui/sidebar.tsx +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/components/ui/skeleton.tsx +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/components/ui/sonner.tsx +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/components/ui/tooltip.tsx +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/hooks/use-mobile.ts +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/hooks/useSSE.ts +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/index.css +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/lib/utils.ts +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/src/main.tsx +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/tsconfig.json +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/tsconfig.node.json +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/tsconfig.tsbuildinfo +0 -0
- {cckit-0.3.0 → cckit-0.3.1}/web/vite.config.d.ts +0 -0
|
@@ -430,9 +430,12 @@ Web 把全局 skill 单独折叠一组、用独立覆盖选择器(不复用四
|
|
|
430
430
|
- **Claude Code 只做改造与审计,不做安装。** 安装仍是 `stage_install →
|
|
431
431
|
execute_install` 的确定性管线。审计是语义层补充,不可绕过的安全闸门仍是
|
|
432
432
|
manifest 校验 + lint + 安装计划确认 + 回滚。
|
|
433
|
-
- **`kit-builder`
|
|
434
|
-
|
|
435
|
-
|
|
433
|
+
- **`kit-builder` 的唯一可信来源是随 cckit 分发的 `./skills/kit-builder`。** 同名
|
|
434
|
+
Skill 在网络上不可依赖,所以自动安装只能等价 `cckit add <abs>/skills/kit-builder
|
|
435
|
+
--local` 到全局。该目录由 `alt.kit_builder_source_dir()` 在「源码树 → 随包
|
|
436
|
+
`cckit/skills/kit-builder`」两处候选里定位(后者由 hatch_build.py 打进 wheel),
|
|
437
|
+
因而 `uv tool install` 后仍能安装。检测到同名冲突、来源冲突、项目作用域安装时
|
|
438
|
+
一律停止,不静默覆盖。
|
|
436
439
|
- **不伪造 SHA。** 改造后临时目录已非原始仓库状态,本地目录可能根本没有 Git。
|
|
437
440
|
alt 最终按本地目录交给安装接口,registry 不记录改造后 SHA,只保留原始来源 URL/ref。
|
|
438
441
|
- **权限用 `auto`,不设 `can_use_tool`、不用 `bypassPermissions`。** 一旦设了
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Web 管理面板
|
|
2
2
|
|
|
3
|
-
配套 v0.3.
|
|
3
|
+
配套 v0.3.1 的可选管理面板:平时展示 skill 列表,支持导入(add)、卸载(remove)、
|
|
4
4
|
四态开关切换、诊断(doctor)。**纯增量**——不装 `[web]` 额外依赖时,`cckit` 本体
|
|
5
5
|
与命令行工具完全不变。
|
|
6
6
|
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
| 提示 | sonner(shadcn 包装) | 四类 toast + 弹窗/toast 分工 |
|
|
25
25
|
| 依赖隔离 | `[web]` 可选 extra | `uv tool install cckit` 仍是 3 依赖纯本体 |
|
|
26
26
|
| 安全 | 后端默认 `127.0.0.1` | Web 能装 kit(跑作者代码)、能改状态,不放开监听 |
|
|
27
|
+
| 子路径部署 | `--base` 运行时挂载 + `window.__CCKIT_BASE__` 注入 + Vite `base: "./"` | 嵌入 dashboard 子路径时避免与宿主 `/api` 冲突,无需重建前端 |
|
|
27
28
|
|
|
28
29
|
## 架构
|
|
29
30
|
|
|
@@ -195,6 +196,24 @@ uv run cckit web --host 0.0.0.0 --port 8000 # 自动定位前端,缺省也
|
|
|
195
196
|
不设 `frame-ancestors` CSP(Starlette 默认即可嵌)。HTTPS 宿主嵌 `http://<server>`
|
|
196
197
|
需在目标浏览器实测(Safari 较严)。
|
|
197
198
|
|
|
199
|
+
**子路径部署(`--base`)**:当面板要挂到宿主 dashboard 的某个子路径(如 `/cckit/`)下,
|
|
200
|
+
且不想与宿主页自身的 `/api` 冲突时,启动时指定根路径前缀:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
cckit web --host 0.0.0.0 --base /cckit
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
此时后端把整站(所有 `/api/*`、`/assets`、SPA fallback)整体 mount 到 `/cckit` 下,
|
|
207
|
+
反向代理只需把 `/cckit/*` **原样转发**(无需 strip 前缀),宿主页
|
|
208
|
+
`<iframe src="http://<server>:<port>/cckit/">` 即可。实现要点:
|
|
209
|
+
|
|
210
|
+
- **运行时注入**:后端 serve 时把 `window.__CCKIT_BASE__` 注入 `index.html` 的 `<head>`,
|
|
211
|
+
前端据此给 `/api/*` 拼前缀(SSE 同走 `fetch`,一并覆盖)。根部署(base 为空)不注入、
|
|
212
|
+
行为与从前完全一致。
|
|
213
|
+
- **资源相对化**:前端 Vite 用 `base: "./"` 构建,`./assets/...` 与字体 `url(...)` 自动
|
|
214
|
+
跟随当前子路径,因此**同一份产物**在根部署与任意子路径部署下都可用,`--base` 是纯运行
|
|
215
|
+
时配置,无需重新构建前端。
|
|
216
|
+
|
|
198
217
|
## 验证
|
|
199
218
|
|
|
200
219
|
- `uv run pytest -q` 82 项全绿(状态层 root 推广、installer 拆分后 CLI 行为不回归)。
|
|
@@ -203,3 +222,5 @@ uv run cckit web --host 0.0.0.0 --port 8000 # 自动定位前端,缺省也
|
|
|
203
222
|
均可达,非法 API 路径返回 404 而非 index.html。
|
|
204
223
|
- 纯净性:`uv build`(不先跑前端构建)产出的 wheel 不含前端,`[web]` 依赖也不随
|
|
205
224
|
`uv tool install cckit` 安装;前端产物仅在前端 build 后才由 hatch 钩子打进 wheel。
|
|
225
|
+
`skills/kit-builder`(alt 的自举工具)则**始终**随 wheel 分发到 `cckit/skills/kit-builder`,
|
|
226
|
+
与前端是否构建无关。
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
|
|
55
55
|
## 当前状态
|
|
56
56
|
|
|
57
|
-
**v0.3.
|
|
57
|
+
**v0.3.1 已实现**:`cckit` 命令行工具与全部 9 个命令(`add` / `list` / `enable` /
|
|
58
58
|
`disable` / `name-only` / `remove` / `doctor` / `exec` / `web`),`uv run cckit --help` 可用。(0.2.0因为打包失误,版本号被弃用)
|
|
59
59
|
|
|
60
60
|
已落地模块(`src/cckit/`):
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
- `projects` —— 关注项目清单 projects.json 原子读写
|
|
73
73
|
- `web` —— Web 管理面板后端(FastAPI,惰性 import,`[web]` 可选依赖)
|
|
74
74
|
|
|
75
|
-
测试:`tests/` 下
|
|
75
|
+
测试:`tests/` 下 132 项,`uv run pytest -q` 全绿;`Docs/examples/video-toolkit` 作为夹具。
|
|
76
76
|
|
|
77
77
|
## v0.1 范围
|
|
78
78
|
|
|
@@ -93,6 +93,15 @@
|
|
|
93
93
|
由 Claude Code(kit-builder)改造 + 审计后复用现有安装流程;标准仓库不碰 LLM。
|
|
94
94
|
详见 [cli-spec](cli-spec.md)、[02](02-architecture.md) 的 alt 节、[05](05-design-log.md) 的 D-16。
|
|
95
95
|
|
|
96
|
+
## v0.3.1 范围
|
|
97
|
+
|
|
98
|
+
- **Web 面板子路径部署(`cckit web --base /cckit`)**:整站运行时挂到指定根路径前缀下,
|
|
99
|
+
便于把面板 iframe 嵌入宿主 dashboard 而不与宿主自身的 `/api` 冲突;`--base` 是运行时
|
|
100
|
+
配置,无需重建前端(见 [11](11-web-panel.md))。
|
|
101
|
+
- **kit-builder 随 wheel 分发**:修复 `uv tool install 'cckit[web,alt]'` 后,Web 端勾选
|
|
102
|
+
「允许导入非标准仓库」检测不到 `skills/kit-builder`、无法给出安装选项的问题。
|
|
103
|
+
- **修复安装弹窗残留状态**:安装成功后关闭弹窗,再次打开不再显示上一次的「安装成功」。
|
|
104
|
+
|
|
96
105
|
**推迟到 v0.4+**:`cckit.lock` 与 `sync`、`profile`、`update` 的 diff 展示、
|
|
97
106
|
用量统计。
|
|
98
107
|
|
|
@@ -169,3 +169,24 @@ skill 脚本的统一入口。SKILL.md 里只写这一句,不写解释器路径
|
|
|
169
169
|
|
|
170
170
|
要点:cwd **保持调用方 cwd**(让用户给的相对路径正常工作),脚本自身资源通过
|
|
171
171
|
`CCKIT_SKILL_DIR` 定位。`scripts` 未在 manifest 声明的路径应拒绝执行。
|
|
172
|
+
|
|
173
|
+
## `cckit web`
|
|
174
|
+
|
|
175
|
+
启动 Web 管理面板(需 `[web]` extra,缺失时提示 `uv tool install 'cckit[web]'`)。
|
|
176
|
+
前端静态产物随 wheel 打包,后端自动定位(包内 `static` → 源码 `web/dist`)。
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
cckit web # 前端后端一起起,打开 http://127.0.0.1:8000 即用
|
|
180
|
+
cckit web --host 0.0.0.0 --port 8000 # 部署到服务器
|
|
181
|
+
cckit web --base /cckit # 挂到根路径前缀 /cckit 下
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
| 选项 | 说明 |
|
|
185
|
+
|---|---|
|
|
186
|
+
| `--host` | 监听地址,默认 `127.0.0.1`;部署时 `0.0.0.0` |
|
|
187
|
+
| `--port` | 端口,默认 `8000` |
|
|
188
|
+
| `--static-dir` | 前端静态产物目录(缺省不托管,配合 vite dev 使用) |
|
|
189
|
+
| `--base` | 根路径前缀(如 `/cckit`)。运行时把整站(`/api/*`、`/assets/*`)挂到该前缀下,反向代理只需原样透传 `/base/*`;用于把面板以 iframe 嵌入宿主 dashboard 的子路径而不与宿主 `/api` 冲突 |
|
|
190
|
+
|
|
191
|
+
`--base` 是**运行时**配置:后端 serve 时把 `window.__CCKIT_BASE__` 注入 `index.html`,
|
|
192
|
+
前端据此拼 `/api` 前缀;前端资源用相对路径(`Vite base: "./"`)自动跟随子路径,无需重新构建。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: cckit
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Claude Code 的 Skill 工具箱管理器
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -18,7 +18,7 @@ Description-Content-Type: text/markdown
|
|
|
18
18
|
|
|
19
19
|
# CCKitKit
|
|
20
20
|
|
|
21
|
-
[](#) [](#) [](#) [](#)
|
|
22
22
|
|
|
23
23
|
</div>
|
|
24
24
|
|
|
@@ -125,6 +125,10 @@ cd web && npm run dev # 前端 dev server(:5173),代理 /api 到 :8000
|
|
|
125
125
|
- 后端**默认只绑 `127.0.0.1`**。要作为服务器面板被浏览器访问时,用 `cckit web --host 0.0.0.0`。
|
|
126
126
|
⚠️ 放开即把「能装 kit(跑作者代码)、能改 skill 状态」的能力暴露给网段,请只在可信内网使用,必要时在反向代理层加鉴权。
|
|
127
127
|
- 嵌入其他页面用 **iframe**:后端托管前端后,宿主页 `<iframe src="http://<server>:<port>/">` 即可(同源隔离,无 CORS 问题)。
|
|
128
|
+
- **子路径部署**:要把面板挂到宿主 dashboard 的某个子路径下(如 `/cckit/`),用 `cckit web --base /cckit`。
|
|
129
|
+
此时整站(`/cckit/api/*`、`/cckit/assets/*`)都挂到该前缀下,反向代理只需把 `/cckit/*` 原样转发给 cckit,
|
|
130
|
+
宿主页 `<iframe src="http://<server>:<port>/cckit/">` 即可,不会与 dashboard 自身的 `/api` 冲突。
|
|
131
|
+
`--base` 是**运行时**配置,无需重新构建前端。
|
|
128
132
|
|
|
129
133
|
## 特色
|
|
130
134
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# CCKitKit
|
|
4
4
|
|
|
5
|
-
[](#) [](#) [](#) [](#)
|
|
6
6
|
|
|
7
7
|
</div>
|
|
8
8
|
|
|
@@ -109,6 +109,10 @@ cd web && npm run dev # 前端 dev server(:5173),代理 /api 到 :8000
|
|
|
109
109
|
- 后端**默认只绑 `127.0.0.1`**。要作为服务器面板被浏览器访问时,用 `cckit web --host 0.0.0.0`。
|
|
110
110
|
⚠️ 放开即把「能装 kit(跑作者代码)、能改 skill 状态」的能力暴露给网段,请只在可信内网使用,必要时在反向代理层加鉴权。
|
|
111
111
|
- 嵌入其他页面用 **iframe**:后端托管前端后,宿主页 `<iframe src="http://<server>:<port>/">` 即可(同源隔离,无 CORS 问题)。
|
|
112
|
+
- **子路径部署**:要把面板挂到宿主 dashboard 的某个子路径下(如 `/cckit/`),用 `cckit web --base /cckit`。
|
|
113
|
+
此时整站(`/cckit/api/*`、`/cckit/assets/*`)都挂到该前缀下,反向代理只需把 `/cckit/*` 原样转发给 cckit,
|
|
114
|
+
宿主页 `<iframe src="http://<server>:<port>/cckit/">` 即可,不会与 dashboard 自身的 `/api` 冲突。
|
|
115
|
+
`--base` 是**运行时**配置,无需重新构建前端。
|
|
112
116
|
|
|
113
117
|
## 特色
|
|
114
118
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""hatch 构建钩子:把 skills/kit-builder 与前端产物 web/dist 作为 package data 打进 wheel。
|
|
2
|
+
|
|
3
|
+
- skills/kit-builder:alt 流程的自举工具可信来源,始终随包分发(见 alt.kit_builder_source_dir)。
|
|
4
|
+
- web/dist:若前端已构建,打进 wheel(完整构建);未构建则不含前端(纯本体构建)。
|
|
5
|
+
|
|
6
|
+
要点:wheel 是从 sdist 解压目录构建的(uv build),所以 sdist 阶段要把 web/dist
|
|
7
|
+
以**原始路径**带进 sdist,否则 wheel 阶段在解压目录里找不到它。skills/kit-builder
|
|
8
|
+
是 git 跟踪文件,sdist 默认已含,无需在 sdist 阶段 force_include(见 Docs/11-web-panel.md)。
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CustomBuildHook(BuildHookInterface):
|
|
18
|
+
def initialize(self, version, build_data):
|
|
19
|
+
force = build_data.setdefault("force_include", {})
|
|
20
|
+
|
|
21
|
+
# kit-builder 自举工具:alt 流程的可信来源,始终随包分发。
|
|
22
|
+
kb = Path(self.root) / "skills" / "kit-builder"
|
|
23
|
+
if self.target_name == "wheel" and (kb / "cckit.yaml").is_file():
|
|
24
|
+
force[str(kb)] = "cckit/skills/kit-builder"
|
|
25
|
+
|
|
26
|
+
dist = Path(self.root) / "web" / "dist"
|
|
27
|
+
if not (dist / "index.html").is_file():
|
|
28
|
+
return
|
|
29
|
+
if self.target_name == "sdist":
|
|
30
|
+
# sdist 保留原始路径,供 wheel 阶段从解压目录读取
|
|
31
|
+
force[str(dist)] = "web/dist"
|
|
32
|
+
else: # wheel
|
|
33
|
+
# wheel 阶段把解压目录里的 web/dist 放到最终位置 cckit/web/static
|
|
34
|
+
force[str(dist)] = "cckit/web/static"
|
|
@@ -74,13 +74,23 @@ class AgentResult:
|
|
|
74
74
|
# ---- kit-builder 前置条件 ----
|
|
75
75
|
|
|
76
76
|
def kit_builder_source_dir() -> Path | None:
|
|
77
|
-
"""当前 cckit
|
|
77
|
+
"""当前 cckit 的 ./skills/kit-builder 绝对路径;找不到返回 None。
|
|
78
78
|
|
|
79
|
-
kit-builder
|
|
80
|
-
搜索同名 Skill
|
|
79
|
+
kit-builder 唯一可信来源是随 cckit 分发的 ./skills/kit-builder,不能从网络
|
|
80
|
+
搜索同名 Skill。这里按「源码树 → 随包分发」的顺序定位,不依赖 cwd 恰好是仓库根:
|
|
81
|
+
|
|
82
|
+
- 源码树:仓库根的 skills/kit-builder(开发/editable 安装);
|
|
83
|
+
- 随包:cckit/skills/kit-builder(hatch_build.py 打进 wheel 的位置)。
|
|
81
84
|
"""
|
|
82
|
-
|
|
83
|
-
|
|
85
|
+
here = Path(__file__).resolve()
|
|
86
|
+
candidates = (
|
|
87
|
+
here.parents[2] / "skills" / "kit-builder", # 源码树 ./skills/kit-builder
|
|
88
|
+
here.parent / "skills" / "kit-builder", # 随包 cckit/skills/kit-builder
|
|
89
|
+
)
|
|
90
|
+
for p in candidates:
|
|
91
|
+
if (p / "cckit.yaml").is_file():
|
|
92
|
+
return p
|
|
93
|
+
return None
|
|
84
94
|
|
|
85
95
|
|
|
86
96
|
def _kit_builder_registry_conflicts() -> list[str]:
|
|
@@ -238,7 +238,7 @@ def cmd_web(args) -> int:
|
|
|
238
238
|
except ImportError:
|
|
239
239
|
print("web 端未安装,请运行: uv tool install 'cckit[web]'", file=sys.stderr)
|
|
240
240
|
return 1
|
|
241
|
-
serve(args.host, args.port, args.static_dir)
|
|
241
|
+
serve(args.host, args.port, args.static_dir, args.base)
|
|
242
242
|
return 0
|
|
243
243
|
|
|
244
244
|
|
|
@@ -307,6 +307,8 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
307
307
|
w.add_argument("--port", type=int, default=8000, help="端口(默认 8000)")
|
|
308
308
|
w.add_argument("--static-dir", default=None,
|
|
309
309
|
help="前端静态产物目录(缺省不托管,配合 vite dev 使用)")
|
|
310
|
+
w.add_argument("--base", default="",
|
|
311
|
+
help="根路径前缀(如 /cckit);经反向代理挂到子路径、iframe 嵌入时用")
|
|
310
312
|
w.set_defaults(func=cmd_web)
|
|
311
313
|
|
|
312
314
|
return p
|
|
@@ -18,7 +18,7 @@ from pathlib import Path
|
|
|
18
18
|
|
|
19
19
|
from fastapi import FastAPI, HTTPException, Request
|
|
20
20
|
from fastapi.exceptions import RequestValidationError
|
|
21
|
-
from fastapi.responses import FileResponse, JSONResponse, StreamingResponse
|
|
21
|
+
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse, StreamingResponse
|
|
22
22
|
from fastapi.staticfiles import StaticFiles
|
|
23
23
|
from pydantic import BaseModel
|
|
24
24
|
|
|
@@ -26,7 +26,9 @@ from .. import alt, config, doctor, installer, projects, registry, state
|
|
|
26
26
|
from ..errors import CckitError
|
|
27
27
|
from ..manifest import MissingManifestError
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
CCKIT_VERSION = "0.3.1"
|
|
30
|
+
|
|
31
|
+
app = FastAPI(title="cckit web", version=CCKIT_VERSION)
|
|
30
32
|
|
|
31
33
|
|
|
32
34
|
# ---- 错误契约 ----
|
|
@@ -490,8 +492,34 @@ def run_doctor(body: DoctorBody):
|
|
|
490
492
|
|
|
491
493
|
# ---- 静态托管(前端 dist 存在时挂载) ----
|
|
492
494
|
|
|
493
|
-
def
|
|
494
|
-
"""
|
|
495
|
+
def _normalize_base(base: str) -> str:
|
|
496
|
+
"""规范化根路径前缀:根部署(""或"/")返回 "",其余折叠重复斜杠并确保以 / 开头、无尾斜杠。"""
|
|
497
|
+
b = (base or "").strip()
|
|
498
|
+
if not b or b == "/":
|
|
499
|
+
return ""
|
|
500
|
+
parts = [seg for seg in b.split("/") if seg]
|
|
501
|
+
if not parts:
|
|
502
|
+
return ""
|
|
503
|
+
return "/" + "/".join(parts)
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
def _inject_base(html: str, base: str) -> str:
|
|
507
|
+
"""把 window.__CCKIT_BASE__ 注入 index.html 的 <head>,前端运行时据此拼 /api 前缀。
|
|
508
|
+
|
|
509
|
+
对注入值做 < > & 转义,防止 --base 携带的字符跳出 <script> 标签(防御性,非用户输入)。
|
|
510
|
+
"""
|
|
511
|
+
if not base:
|
|
512
|
+
return html
|
|
513
|
+
value = (json.dumps(base)
|
|
514
|
+
.replace("<", "\\u003c")
|
|
515
|
+
.replace(">", "\\u003e")
|
|
516
|
+
.replace("&", "\\u0026"))
|
|
517
|
+
script = f"<script>window.__CCKIT_BASE__={value};</script>"
|
|
518
|
+
return html.replace("<head>", "<head>" + script, 1)
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
def _mount_static(static_dir: str, base: str = "") -> None:
|
|
522
|
+
"""挂载前端静态产物 + SPA fallback(不遮挡 /api)。base 非空时注入 __CCKIT_BASE__。"""
|
|
495
523
|
sd = Path(static_dir).resolve()
|
|
496
524
|
if not (sd / "index.html").is_file():
|
|
497
525
|
return
|
|
@@ -499,6 +527,8 @@ def _mount_static(static_dir: str) -> None:
|
|
|
499
527
|
if assets.is_dir():
|
|
500
528
|
app.mount("/assets", StaticFiles(directory=str(assets)), name="assets")
|
|
501
529
|
|
|
530
|
+
index_html = _inject_base((sd / "index.html").read_text(encoding="utf-8"), base)
|
|
531
|
+
|
|
502
532
|
@app.get("/{full_path:path}", include_in_schema=False)
|
|
503
533
|
async def _spa(full_path: str):
|
|
504
534
|
if full_path.startswith("api/"):
|
|
@@ -506,7 +536,7 @@ def _mount_static(static_dir: str) -> None:
|
|
|
506
536
|
candidate = sd / full_path
|
|
507
537
|
if full_path and candidate.is_file():
|
|
508
538
|
return FileResponse(str(candidate))
|
|
509
|
-
return
|
|
539
|
+
return HTMLResponse(index_html)
|
|
510
540
|
|
|
511
541
|
|
|
512
542
|
def _default_static_dir() -> Path | None:
|
|
@@ -521,14 +551,25 @@ def _default_static_dir() -> Path | None:
|
|
|
521
551
|
|
|
522
552
|
|
|
523
553
|
def serve(host: str = "127.0.0.1", port: int = 8000,
|
|
524
|
-
static_dir: str | None = None) -> None:
|
|
554
|
+
static_dir: str | None = None, base: str = "") -> None:
|
|
525
555
|
"""启动 uvicorn。host 默认 127.0.0.1;部署时显式传 0.0.0.0(见 Docs/09 安全要求)。
|
|
526
556
|
|
|
557
|
+
base 非空时把整站挂到该根路径前缀下(如 --base /cckit → /cckit/api、/cckit/assets),
|
|
558
|
+
便于经反向代理挂到 dashboard 子路径、用 iframe 嵌入而不与宿主页 /api 冲突。
|
|
559
|
+
|
|
527
560
|
static_dir 缺省时自动定位前端产物(包内 static → 源码 web/dist);找不到则只跑 API。
|
|
528
561
|
"""
|
|
529
562
|
import uvicorn
|
|
530
563
|
|
|
564
|
+
base = _normalize_base(base)
|
|
531
565
|
target = Path(static_dir) if static_dir else _default_static_dir()
|
|
532
566
|
if target is not None:
|
|
533
|
-
_mount_static(str(target))
|
|
534
|
-
|
|
567
|
+
_mount_static(str(target), base=base)
|
|
568
|
+
|
|
569
|
+
root_app = app
|
|
570
|
+
if base:
|
|
571
|
+
# 把现有 app(所有 /api/* 与 /assets)整体挂到前缀下,代理只需原样透传 /base/*。
|
|
572
|
+
parent = FastAPI(title="cckit web", version=CCKIT_VERSION)
|
|
573
|
+
parent.mount(base, app, name="cckit")
|
|
574
|
+
root_app = parent
|
|
575
|
+
uvicorn.run(root_app, host=host, port=port)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Web 根路径前缀(--base)的纯函数测试:规范化与 index.html 注入。
|
|
2
|
+
|
|
3
|
+
直接调模块函数,不经过 ASGI 测试客户端(仓库未依赖 httpx)。
|
|
4
|
+
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import importlib
|
|
8
|
+
|
|
9
|
+
# 与 test_web_alt.py 同理:cckit.web.__init__ 把 app 覆盖到了 cckit.web.app 属性上。
|
|
10
|
+
web = importlib.import_module("cckit.web.app")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_normalize_base_root():
|
|
14
|
+
assert web._normalize_base("") == ""
|
|
15
|
+
assert web._normalize_base("/") == ""
|
|
16
|
+
assert web._normalize_base(" ") == ""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_normalize_base_subpath():
|
|
20
|
+
assert web._normalize_base("/cckit") == "/cckit"
|
|
21
|
+
assert web._normalize_base("/cckit/") == "/cckit"
|
|
22
|
+
assert web._normalize_base("cckit") == "/cckit"
|
|
23
|
+
assert web._normalize_base("cckit/") == "/cckit"
|
|
24
|
+
assert web._normalize_base("/cckit/nested/") == "/cckit/nested"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_normalize_base_collapses_slashes():
|
|
28
|
+
assert web._normalize_base("//cckit//") == "/cckit"
|
|
29
|
+
assert web._normalize_base("/cckit//nested/") == "/cckit/nested"
|
|
30
|
+
assert web._normalize_base("///") == ""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_inject_base_adds_script():
|
|
34
|
+
html = "<!doctype html><html><head><title>x</title></head><body></body></html>"
|
|
35
|
+
out = web._inject_base(html, "/cckit")
|
|
36
|
+
assert '<script>window.__CCKIT_BASE__="/cckit";</script>' in out
|
|
37
|
+
assert out.startswith("<!doctype html><html><head>")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_inject_base_escapes_angle_brackets():
|
|
41
|
+
html = "<head></head>"
|
|
42
|
+
out = web._inject_base(html, "</script><script>alert(1)</script>")
|
|
43
|
+
# 注入值里的 < > 被转义成 \u003c / \u003e,不会跳出 <script> 标签
|
|
44
|
+
assert "\\u003c/script\\u003e" in out
|
|
45
|
+
assert out.count("<script>") == 1
|
|
46
|
+
assert out.count("</script>") == 1
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_inject_base_root_is_noop():
|
|
50
|
+
html = "<head></head>"
|
|
51
|
+
assert web._inject_base(html, "") == html
|