cc-vision-hook 0.1.1 → 0.2.0
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.
- package/CHANGELOG.md +23 -0
- package/README.md +31 -7
- package/README.zh-CN.md +31 -7
- package/dist/claudeMcp.d.ts +29 -0
- package/dist/claudeMcp.js +85 -0
- package/dist/claudeMcp.js.map +1 -0
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +22 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/configCmd.js +18 -5
- package/dist/commands/configCmd.js.map +1 -1
- package/dist/commands/doctor.js +29 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.d.ts +16 -0
- package/dist/commands/init.js +112 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/install.d.ts +3 -1
- package/dist/commands/install.js +13 -5
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/mcpCmd.d.ts +29 -0
- package/dist/commands/mcpCmd.js +58 -0
- package/dist/commands/mcpCmd.js.map +1 -0
- package/dist/commands/status.js +6 -0
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/testImage.js +1 -7
- package/dist/commands/testImage.js.map +1 -1
- package/dist/config.d.ts +8 -2
- package/dist/config.js +16 -1
- package/dist/config.js.map +1 -1
- package/dist/imageMime.d.ts +14 -0
- package/dist/imageMime.js +22 -0
- package/dist/imageMime.js.map +1 -0
- package/dist/mcpServer.d.ts +31 -0
- package/dist/mcpServer.js +141 -0
- package/dist/mcpServer.js.map +1 -0
- package/dist/pasteScanner.js +1 -7
- package/dist/pasteScanner.js.map +1 -1
- package/dist/sdk/index.d.ts +2 -0
- package/dist/sdk/index.js +2 -0
- package/dist/sdk/index.js.map +1 -1
- package/dist/vision.d.ts +13 -1
- package/dist/vision.js +58 -17
- package/dist/vision.js.map +1 -1
- package/package.json +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 新增 `cvh init` 交互式安装向导,以及可选的 MCP server(`cvh mcp install`/`uninstall`/`status`/`serve`),暴露 `vision_ask`/`vision_describe_image`/`vision_describe_data_url` 三个工具,让 Agent 可以对已解析过的图片主动追问,或直接解析一张新图片/data URL(不依赖历史缓存命中)。
|
|
8
|
+
|
|
9
|
+
修复:
|
|
10
|
+
|
|
11
|
+
- `cvh install` 此前从未真正创建过配置文件——`loadConfig()` 内部自带 try/catch 兜底,文件不存在时会静默返回默认配置对象而不是抛错,导致 `install.ts` 里 `loadConfig().catch(() => null)` 的判空逻辑永远不会触发。新增 `configFileExists()` 显式检查磁盘状态并修正该逻辑。
|
|
12
|
+
- `cvh uninstall`(含 `--purge`)现在会同时移除 MCP server 注册,避免卸载后残留一个孤立的 MCP 条目。
|
|
13
|
+
- 移除了从未被读取的 `CvhConfig.mcpInstalled` 死字段——MCP 注册状态统一通过 `checkMcpInstalled()` 实时读取 `~/.claude.json`,不再有可能与真实状态不一致的静态字段。
|
|
14
|
+
- **`baseUrl` 缺少路径段时报错信息完全看不出原因**:真机回归测试(Claude Code 2.1.205 + 一个自建兼容网关)时发现,AI SDK 不会给自定义网关的 `baseUrl` 自动补路径段(如 `/v1`),传入裸域名会在真正调用视觉模型时收到一句语义不明的 404 `"Not Found"`,排查成本很高。现在 `describeImage()` 会在 404 + `baseUrl` 明显缺路径段时补充排查提示;`cvh config set baseUrl` 会在设置时提前给出非阻断性警告;`cvh doctor` 新增 `baseUrl 路径段检查` 项。
|
|
15
|
+
|
|
16
|
+
其他:
|
|
17
|
+
|
|
18
|
+
- `cvh doctor`/`cvh status` 新增 MCP 注册状态展示(MCP 未注册不影响整体自检结果,属于可选功能)。
|
|
19
|
+
- 补齐 `pasteScanner.ts`/`seenTracker.ts`/`commands/*.ts` 的单测覆盖。
|
|
20
|
+
|
|
21
|
+
真机回归(Claude Code 升级到 2.1.205 后重新验证):
|
|
22
|
+
|
|
23
|
+
- `PostToolUse` Hook(`Read` 工具触发):真实调用一个静默忽略图片型模型 + 自建兼容网关,图片识别正确率、`additionalContext` 注入、2 轮完成对话均验证通过。
|
|
24
|
+
- MCP server:`claude mcp list` 确认 stdio 连接成功;真实让 Agent 主动调用 `mcp__cc-vision-hook__vision_describe_image` 工具,返回结果正确。
|
|
25
|
+
|
|
3
26
|
## 0.1.1
|
|
4
27
|
|
|
5
28
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -49,6 +49,7 @@ flowchart LR
|
|
|
49
49
|
- Both hooks **only ever output `additionalContext`** — never `updatedToolOutput`. This keeps `cvh` simple and safe: no per-tool schema replication, no risk of a silently-dropped hook output because the replacement shape didn't match.
|
|
50
50
|
- Vision inference goes through the [Vercel AI SDK](https://sdk.vercel.ai/), so you can point `cvh` at OpenAI (Chat Completions or Responses), Anthropic, or Gemini — or any OpenAI/Anthropic-compatible gateway.
|
|
51
51
|
- Descriptions are cached on disk by image content hash (`~/.claude/cc-vision-hook/cache/`), shared globally, with a 7-day lazy-expiring TTL — the same image is only ever described once, no matter where it came from.
|
|
52
|
+
- An optional **MCP server** (`cvh mcp install`) exposes `vision_ask` / `vision_describe_image` / `vision_describe_data_url`, so the agent can proactively follow up on an image it already saw ("what does the text in the top-right corner say?") instead of only getting a one-shot description. MCP registration is independent from the `enabled` switch — disabling `cvh` does not uninstall the MCP server, and vice versa.
|
|
52
53
|
|
|
53
54
|
## Scope & limitations
|
|
54
55
|
|
|
@@ -64,6 +65,15 @@ Other known limitations:
|
|
|
64
65
|
|
|
65
66
|
## Install
|
|
66
67
|
|
|
68
|
+
Interactive (recommended for a first-time setup):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm install -g cc-vision-hook
|
|
72
|
+
cvh init # asks for provider/model/apiKey, registers hooks, optionally MCP + enable
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Or non-interactive, e.g. for scripting:
|
|
76
|
+
|
|
67
77
|
```bash
|
|
68
78
|
npm install -g cc-vision-hook
|
|
69
79
|
|
|
@@ -79,15 +89,18 @@ cvh enable
|
|
|
79
89
|
|
|
80
90
|
| Command | Description |
|
|
81
91
|
|---|---|
|
|
82
|
-
| `cvh
|
|
83
|
-
| `cvh
|
|
92
|
+
| `cvh init` | Interactive setup wizard (provider/model/apiKey, hooks, optional MCP + enable) |
|
|
93
|
+
| `cvh install` | Non-interactive: create config + register hooks (idempotent, safe to re-run) |
|
|
94
|
+
| `cvh uninstall [--purge]` | Remove hook + MCP registration; `--purge` also deletes config and cache |
|
|
84
95
|
| `cvh enable` / `cvh disable` | The one and only runtime switch |
|
|
85
|
-
| `cvh status` | Show current state, hook registration, cache stats |
|
|
96
|
+
| `cvh status` | Show current state, hook/MCP registration, cache stats |
|
|
86
97
|
| `cvh doctor` | Self-check config / hooks / vision model connectivity, plus a boundary reminder |
|
|
87
98
|
| `cvh config get` / `cvh config set <key> <value>` | Read/write config (`provider`/`model`/`baseUrl`/`apiKey`/`timeoutMs`/`maxTokens`) |
|
|
88
99
|
| `cvh test-image <path>` | Manually verify the pipeline: local image → vision model → description |
|
|
100
|
+
| `cvh mcp install` / `cvh mcp uninstall` / `cvh mcp status` | Register/remove/inspect the optional MCP server (`vision_ask` etc.) |
|
|
101
|
+
| `cvh mcp serve` | Run the MCP stdio server (invoked by Claude Code itself, not by hand) |
|
|
89
102
|
|
|
90
|
-
Every command supports `--json` output for scripting.
|
|
103
|
+
Every command except `init` supports `--json` output for scripting.
|
|
91
104
|
|
|
92
105
|
## Configuration
|
|
93
106
|
|
|
@@ -110,10 +123,21 @@ The API key is stored in plaintext; the file permission is automatically set to
|
|
|
110
123
|
|
|
111
124
|
Environment variable overrides (take priority over the config file): `CVH_ENABLED` / `CVH_PROVIDER` / `CVH_MODEL` / `CVH_BASE_URL` / `CVH_API_KEY` / `CVH_TIMEOUT_MS` / `CVH_MAX_TOKENS`.
|
|
112
125
|
|
|
113
|
-
##
|
|
126
|
+
## MCP tools (optional)
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
cvh mcp install # register the MCP server in ~/.claude.json (independent of `enabled`)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
This exposes three tools to the agent:
|
|
133
|
+
|
|
134
|
+
| Tool | Purpose |
|
|
135
|
+
|---|---|
|
|
136
|
+
| `vision_ask` | Follow up on an image previously seen via a hook or another MCP call, by `image_id` (from the `image_vision`/`tool_image_vision` tag in `additionalContext`). |
|
|
137
|
+
| `vision_describe_image` | Describe a local image file directly, without relying on a prior cache hit. |
|
|
138
|
+
| `vision_describe_data_url` | Describe an inline `data:image/...;base64,...` URL directly. |
|
|
114
139
|
|
|
115
|
-
-
|
|
116
|
-
- [ ] MCP tools (`vision_ask`, `vision_describe_image`, `vision_describe_data_url`) so an agent can follow up on a previously seen image. The on-disk cache already stores everything needed — the MCP surface just hasn't been wired up yet.
|
|
140
|
+
`vision_describe_image`/`vision_describe_data_url` results are cached on disk the same way hook-produced descriptions are, so a later `vision_ask` can follow up on them too.
|
|
117
141
|
|
|
118
142
|
## Troubleshooting
|
|
119
143
|
|
package/README.zh-CN.md
CHANGED
|
@@ -49,6 +49,7 @@ flowchart LR
|
|
|
49
49
|
- 两个 Hook **永远只输出 `additionalContext`**,从不使用 `updatedToolOutput`。这让 `cvh` 保持简单又安全:不需要为每个工具单独复刻输出 schema,也不用担心替换值 shape 不匹配导致 Hook 输出被 Claude Code 静默丢弃。
|
|
50
50
|
- 视觉推理统一通过 [Vercel AI SDK](https://sdk.vercel.ai/) 完成,你可以把 `cvh` 指向 OpenAI(Chat Completions 或 Responses)、Anthropic、Gemini,或任何兼容 OpenAI/Anthropic 协议的网关。
|
|
51
51
|
- 解析结果按图片内容 hash 缓存在本地磁盘(`~/.claude/cc-vision-hook/cache/`),全局共享,TTL 7 天惰性过期——同一张图无论来自哪里,只会被解析一次。
|
|
52
|
+
- 可选的 **MCP server**(`cvh mcp install`)暴露 `vision_ask` / `vision_describe_image` / `vision_describe_data_url` 三个工具,让 Agent 可以对已经看过的图片主动追问(比如"右上角的文字写了什么?"),而不是只能拿到一次性的通用描述。MCP 的注册状态与主开关 `enabled` 完全独立——停用 `cvh` 不会卸载 MCP server,反之亦然。
|
|
52
53
|
|
|
53
54
|
## 适用范围与已知限制
|
|
54
55
|
|
|
@@ -64,6 +65,15 @@ flowchart LR
|
|
|
64
65
|
|
|
65
66
|
## 安装
|
|
66
67
|
|
|
68
|
+
交互式(推荐首次配置时使用):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm install -g cc-vision-hook
|
|
72
|
+
cvh init # 依次询问 provider/model/apiKey,注册 Hook,可选注册 MCP + 启用
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
或非交互式(例如用于脚本化场景):
|
|
76
|
+
|
|
67
77
|
```bash
|
|
68
78
|
npm install -g cc-vision-hook
|
|
69
79
|
|
|
@@ -79,15 +89,18 @@ cvh enable
|
|
|
79
89
|
|
|
80
90
|
| 命令 | 说明 |
|
|
81
91
|
|---|---|
|
|
82
|
-
| `cvh
|
|
83
|
-
| `cvh
|
|
92
|
+
| `cvh init` | 交互式安装向导(provider/model/apiKey、Hook、可选 MCP + 启用) |
|
|
93
|
+
| `cvh install` | 非交互安装:创建配置 + 注册 Hook(幂等,可重复执行) |
|
|
94
|
+
| `cvh uninstall [--purge]` | 移除 Hook 与 MCP 注册;`--purge` 连带删除配置和缓存 |
|
|
84
95
|
| `cvh enable` / `cvh disable` | 唯一的运行开关 |
|
|
85
|
-
| `cvh status` | 查看当前状态、Hook 注册情况、缓存统计 |
|
|
96
|
+
| `cvh status` | 查看当前状态、Hook/MCP 注册情况、缓存统计 |
|
|
86
97
|
| `cvh doctor` | 自检配置 / Hook / 视觉模型连通性,并提示适用边界 |
|
|
87
98
|
| `cvh config get` / `cvh config set <key> <value>` | 读写配置(`provider`/`model`/`baseUrl`/`apiKey`/`timeoutMs`/`maxTokens`) |
|
|
88
99
|
| `cvh test-image <path>` | 手动验证:本地图片 → 视觉模型 → 描述文字 |
|
|
100
|
+
| `cvh mcp install` / `cvh mcp uninstall` / `cvh mcp status` | 注册/移除/查看可选的 MCP server(`vision_ask` 等) |
|
|
101
|
+
| `cvh mcp serve` | 以 stdio 模式运行 MCP server(由 Claude Code 自动调起,不要手动执行) |
|
|
89
102
|
|
|
90
|
-
|
|
103
|
+
除 `init` 外,所有命令均支持 `--json` 输出,方便脚本调用。
|
|
91
104
|
|
|
92
105
|
## 配置文件
|
|
93
106
|
|
|
@@ -110,10 +123,21 @@ API Key 以明文存储,文件权限会自动设为 `0600`(仅当前用户
|
|
|
110
123
|
|
|
111
124
|
环境变量覆盖(优先级高于配置文件):`CVH_ENABLED` / `CVH_PROVIDER` / `CVH_MODEL` / `CVH_BASE_URL` / `CVH_API_KEY` / `CVH_TIMEOUT_MS` / `CVH_MAX_TOKENS`。
|
|
112
125
|
|
|
113
|
-
##
|
|
126
|
+
## MCP 工具(可选)
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
cvh mcp install # 注册 MCP server 到 ~/.claude.json(与 enabled 开关无关)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
会向 Agent 暴露三个工具:
|
|
133
|
+
|
|
134
|
+
| 工具 | 用途 |
|
|
135
|
+
|---|---|
|
|
136
|
+
| `vision_ask` | 按 `image_id`(来自 `additionalContext` 里的 `image_vision`/`tool_image_vision` 标签)追问此前已看过的图片。 |
|
|
137
|
+
| `vision_describe_image` | 直接解析一张本地图片文件,不依赖历史缓存命中。 |
|
|
138
|
+
| `vision_describe_data_url` | 直接解析一个内联的 `data:image/...;base64,...` URL。 |
|
|
114
139
|
|
|
115
|
-
|
|
116
|
-
- [ ] MCP 工具(`vision_ask`、`vision_describe_image`、`vision_describe_data_url`),支持 Agent 对已解析过的图片进行事后追问。底层磁盘缓存已经具备所需数据,只是还没有暴露 MCP 入口。
|
|
140
|
+
`vision_describe_image`/`vision_describe_data_url` 的解析结果会按 Hook 同样的方式落盘缓存,方便后续用 `vision_ask` 继续追问。
|
|
117
141
|
|
|
118
142
|
## 排障
|
|
119
143
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 对 `~/.claude.json` 里 `mcpServers.cc-vision-hook` 条目的幂等读写。
|
|
3
|
+
*
|
|
4
|
+
* 注意与 `claudeSettings.ts`(Hook 注册,写 `~/.claude/settings.json`)的区别:
|
|
5
|
+
* MCP server 注册写的是 `~/.claude.json`(用户级全局配置,与 Hook 配置文件不是同一个文件)。
|
|
6
|
+
* 两者是完全独立的开关,遵循既定设计:`cvh disable` 不卸载 MCP,`cvh mcp install` 不影响 enabled。
|
|
7
|
+
*/
|
|
8
|
+
/** MCP server 在 `~/.claude.json` 里注册的固定名字,同时作为幂等标识。 */
|
|
9
|
+
export declare const MCP_SERVER_NAME = "cc-vision-hook";
|
|
10
|
+
/**
|
|
11
|
+
* 幂等地在 `~/.claude.json` 中注册 `cc-vision-hook` MCP stdio server。
|
|
12
|
+
* 已存在同名条目时直接覆盖(确保 command/args 始终指向当前可执行文件路径,
|
|
13
|
+
* 例如用户重新全局安装到不同路径后,重新运行 install 能自动纠正)。
|
|
14
|
+
*
|
|
15
|
+
* @returns 本次是否发生了实际写入变化(新增或内容变化),false 表示已经是最新状态
|
|
16
|
+
*/
|
|
17
|
+
export declare const installMcpServer: () => Promise<boolean>;
|
|
18
|
+
/**
|
|
19
|
+
* 从 `~/.claude.json` 移除 `cc-vision-hook` MCP server 注册,不影响用户配置的其他 MCP server。
|
|
20
|
+
*
|
|
21
|
+
* @returns 本次是否实际移除了条目(false 表示原本就没安装)
|
|
22
|
+
*/
|
|
23
|
+
export declare const uninstallMcpServer: () => Promise<boolean>;
|
|
24
|
+
/**
|
|
25
|
+
* 检查 MCP server 是否已注册(用于 `cvh status`/`doctor`)。
|
|
26
|
+
*
|
|
27
|
+
* @returns 是否已注册
|
|
28
|
+
*/
|
|
29
|
+
export declare const checkMcpInstalled: () => Promise<boolean>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 对 `~/.claude.json` 里 `mcpServers.cc-vision-hook` 条目的幂等读写。
|
|
3
|
+
*
|
|
4
|
+
* 注意与 `claudeSettings.ts`(Hook 注册,写 `~/.claude/settings.json`)的区别:
|
|
5
|
+
* MCP server 注册写的是 `~/.claude.json`(用户级全局配置,与 Hook 配置文件不是同一个文件)。
|
|
6
|
+
* 两者是完全独立的开关,遵循既定设计:`cvh disable` 不卸载 MCP,`cvh mcp install` 不影响 enabled。
|
|
7
|
+
*/
|
|
8
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
9
|
+
import { join, dirname } from "node:path";
|
|
10
|
+
import { getClaudeHomeDir } from "./config.js";
|
|
11
|
+
/** MCP server 在 `~/.claude.json` 里注册的固定名字,同时作为幂等标识。 */
|
|
12
|
+
export const MCP_SERVER_NAME = "cc-vision-hook";
|
|
13
|
+
/**
|
|
14
|
+
* `~/.claude.json` 的路径。与 `getClaudeHomeDir()`(`~/.claude/` 目录)是两个不同的东西——
|
|
15
|
+
* 真实 Claude Code 场景下 `~/.claude.json` 和 `~/.claude/` 目录同级(都在 `$HOME` 下),
|
|
16
|
+
* 这里始终从 `getClaudeHomeDir()` 的父目录推导,不做任何字符串匹配猜测,也不在推导失败时
|
|
17
|
+
* 回退到真实 `homedir()`——单测/隔离环境下 `CVH_CLAUDE_HOME` 可能被设成任意路径(不一定
|
|
18
|
+
* 以 `.claude` 结尾),如果这里悄悄兜底到真实 `homedir()`,会有污染开发者真实
|
|
19
|
+
* `~/.claude.json` 的风险,必须严格用 `dirname()` 推导,不猜测任何字符串模式。
|
|
20
|
+
*
|
|
21
|
+
* @returns `~/.claude.json` 的绝对路径
|
|
22
|
+
*/
|
|
23
|
+
const getGlobalConfigPath = () => join(dirname(getClaudeHomeDir()), ".claude.json");
|
|
24
|
+
/**
|
|
25
|
+
* 读取 `~/.claude.json`,文件不存在或解析失败时返回空对象(不覆盖用户已有配置)。
|
|
26
|
+
*
|
|
27
|
+
* @returns 解析后的全局配置对象
|
|
28
|
+
*/
|
|
29
|
+
const readGlobalConfig = async () => {
|
|
30
|
+
try {
|
|
31
|
+
const raw = await readFile(getGlobalConfigPath(), "utf8");
|
|
32
|
+
return JSON.parse(raw);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* 写回 `~/.claude.json`。
|
|
40
|
+
*
|
|
41
|
+
* @param config - 完整的全局配置对象
|
|
42
|
+
*/
|
|
43
|
+
const writeGlobalConfig = async (config) => {
|
|
44
|
+
await writeFile(getGlobalConfigPath(), `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* 幂等地在 `~/.claude.json` 中注册 `cc-vision-hook` MCP stdio server。
|
|
48
|
+
* 已存在同名条目时直接覆盖(确保 command/args 始终指向当前可执行文件路径,
|
|
49
|
+
* 例如用户重新全局安装到不同路径后,重新运行 install 能自动纠正)。
|
|
50
|
+
*
|
|
51
|
+
* @returns 本次是否发生了实际写入变化(新增或内容变化),false 表示已经是最新状态
|
|
52
|
+
*/
|
|
53
|
+
export const installMcpServer = async () => {
|
|
54
|
+
const config = await readGlobalConfig();
|
|
55
|
+
config.mcpServers ??= {};
|
|
56
|
+
const desired = { type: "stdio", command: "cvh", args: ["mcp", "serve"] };
|
|
57
|
+
const existing = config.mcpServers[MCP_SERVER_NAME];
|
|
58
|
+
const changed = !existing || existing.command !== desired.command || JSON.stringify(existing.args) !== JSON.stringify(desired.args);
|
|
59
|
+
config.mcpServers[MCP_SERVER_NAME] = desired;
|
|
60
|
+
await writeGlobalConfig(config);
|
|
61
|
+
return changed;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* 从 `~/.claude.json` 移除 `cc-vision-hook` MCP server 注册,不影响用户配置的其他 MCP server。
|
|
65
|
+
*
|
|
66
|
+
* @returns 本次是否实际移除了条目(false 表示原本就没安装)
|
|
67
|
+
*/
|
|
68
|
+
export const uninstallMcpServer = async () => {
|
|
69
|
+
const config = await readGlobalConfig();
|
|
70
|
+
if (!config.mcpServers || !(MCP_SERVER_NAME in config.mcpServers))
|
|
71
|
+
return false;
|
|
72
|
+
delete config.mcpServers[MCP_SERVER_NAME];
|
|
73
|
+
await writeGlobalConfig(config);
|
|
74
|
+
return true;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* 检查 MCP server 是否已注册(用于 `cvh status`/`doctor`)。
|
|
78
|
+
*
|
|
79
|
+
* @returns 是否已注册
|
|
80
|
+
*/
|
|
81
|
+
export const checkMcpInstalled = async () => {
|
|
82
|
+
const config = await readGlobalConfig();
|
|
83
|
+
return Boolean(config.mcpServers?.[MCP_SERVER_NAME]);
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=claudeMcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claudeMcp.js","sourceRoot":"","sources":["../src/claudeMcp.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,uDAAuD;AACvD,MAAM,CAAC,MAAM,eAAe,GAAG,gBAAgB,CAAC;AAahD;;;;;;;;;GASG;AACH,MAAM,mBAAmB,GAAG,GAAW,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,EAAE,cAAc,CAAC,CAAC;AAE5F;;;;GAIG;AACH,MAAM,gBAAgB,GAAG,KAAK,IAAiC,EAAE;IAC/D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,mBAAmB,EAAE,EAAE,MAAM,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAuB,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,iBAAiB,GAAG,KAAK,EAAE,MAA0B,EAAiB,EAAE;IAC5E,MAAM,SAAS,CAAC,mBAAmB,EAAE,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACzF,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,IAAsB,EAAE;IAC3D,MAAM,MAAM,GAAG,MAAM,gBAAgB,EAAE,CAAC;IACxC,MAAM,CAAC,UAAU,KAAK,EAAE,CAAC;IACzB,MAAM,OAAO,GAAmB,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;IAC1F,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,CAAC,QAAQ,IAAI,QAAQ,CAAC,OAAO,KAAK,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACpI,MAAM,CAAC,UAAU,CAAC,eAAe,CAAC,GAAG,OAAO,CAAC;IAC7C,MAAM,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAChC,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,IAAsB,EAAE;IAC7D,MAAM,MAAM,GAAG,MAAM,gBAAgB,EAAE,CAAC;IACxC,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,CAAC,CAAC,eAAe,IAAI,MAAM,CAAC,UAAU,CAAC;QAAE,OAAO,KAAK,CAAC;IAChF,OAAO,MAAM,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;IAC1C,MAAM,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAChC,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,IAAsB,EAAE;IAC5D,MAAM,MAAM,GAAG,MAAM,gBAAgB,EAAE,CAAC;IACxC,OAAO,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;AACvD,CAAC,CAAC"}
|
package/dist/cli.d.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* cvh CLI 入口(命令分发)。
|
|
4
4
|
* P0 范围:install/uninstall/enable/disable/status/doctor/config/test-image + 两个 hook 入口。
|
|
5
|
-
* P1
|
|
5
|
+
* P1:init 交互式安装向导。
|
|
6
|
+
* P2:mcp install/uninstall/serve(`vision_ask`/`vision_describe_image`/`vision_describe_data_url`)。
|
|
6
7
|
*/
|
|
7
8
|
export {};
|
package/dist/cli.js
CHANGED
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* cvh CLI 入口(命令分发)。
|
|
4
4
|
* P0 范围:install/uninstall/enable/disable/status/doctor/config/test-image + 两个 hook 入口。
|
|
5
|
-
* P1
|
|
5
|
+
* P1:init 交互式安装向导。
|
|
6
|
+
* P2:mcp install/uninstall/serve(`vision_ask`/`vision_describe_image`/`vision_describe_data_url`)。
|
|
6
7
|
*/
|
|
7
8
|
import { runInstall, runUninstall } from "./commands/install.js";
|
|
9
|
+
import { runInit } from "./commands/init.js";
|
|
8
10
|
import { runToggle } from "./commands/toggle.js";
|
|
9
11
|
import { runStatus } from "./commands/status.js";
|
|
10
12
|
import { runDoctor } from "./commands/doctor.js";
|
|
11
13
|
import { runConfigGet, runConfigSet } from "./commands/configCmd.js";
|
|
12
14
|
import { runTestImage } from "./commands/testImage.js";
|
|
15
|
+
import { runMcpInstall, runMcpUninstall, runMcpStatus, runMcpServe } from "./commands/mcpCmd.js";
|
|
13
16
|
import { handleUserPromptSubmit } from "./hooks/userPromptSubmit.js";
|
|
14
17
|
import { handlePostToolUse } from "./hooks/postToolUse.js";
|
|
15
18
|
const printHelp = () => {
|
|
@@ -19,7 +22,8 @@ const printHelp = () => {
|
|
|
19
22
|
⚠️ 对"协议层硬拒绝"型模型(收到图片直接导致请求失败)无效,详见 \`cvh doctor\` 提示。
|
|
20
23
|
|
|
21
24
|
Usage:
|
|
22
|
-
cvh
|
|
25
|
+
cvh init 交互式安装向导(选 provider/model/apiKey,一步到位)
|
|
26
|
+
cvh install 非交互安装:创建配置 + 注册 Hook(幂等)
|
|
23
27
|
cvh uninstall [--purge] 卸载:移除 Hook 注册(--purge 连带删除配置和缓存)
|
|
24
28
|
cvh enable / cvh disable 开关(唯一决定是否处理图片的开关)
|
|
25
29
|
cvh status 查看当前状态
|
|
@@ -27,6 +31,10 @@ Usage:
|
|
|
27
31
|
cvh config get 查看当前配置
|
|
28
32
|
cvh config set <key> <value> 设置配置项(provider/model/baseUrl/apiKey/timeoutMs/maxTokens)
|
|
29
33
|
cvh test-image <path> 手动测试:本地图片 -> 视觉解析
|
|
34
|
+
cvh mcp install 注册 MCP server 到 ~/.claude.json(幂等,与 enabled 开关无关)
|
|
35
|
+
cvh mcp uninstall 移除 MCP server 注册
|
|
36
|
+
cvh mcp status 查看 MCP server 注册状态
|
|
37
|
+
cvh mcp serve 以 stdio 模式启动 MCP server(由 Claude Code 调起,不要手动执行)
|
|
30
38
|
|
|
31
39
|
Global options:
|
|
32
40
|
--json 以 JSON 格式输出(供脚本消费)
|
|
@@ -50,6 +58,8 @@ const main = async () => {
|
|
|
50
58
|
case "--help":
|
|
51
59
|
printHelp();
|
|
52
60
|
return;
|
|
61
|
+
case "init":
|
|
62
|
+
return runInit();
|
|
53
63
|
case "install":
|
|
54
64
|
return runInstall(jsonOutput);
|
|
55
65
|
case "uninstall":
|
|
@@ -72,6 +82,16 @@ const main = async () => {
|
|
|
72
82
|
if (!rest[0])
|
|
73
83
|
throw new Error("用法:cvh test-image <path>");
|
|
74
84
|
return runTestImage(rest[0], jsonOutput);
|
|
85
|
+
case "mcp":
|
|
86
|
+
if (rest[0] === "install")
|
|
87
|
+
return runMcpInstall(jsonOutput);
|
|
88
|
+
if (rest[0] === "uninstall")
|
|
89
|
+
return runMcpUninstall(jsonOutput);
|
|
90
|
+
if (rest[0] === "status")
|
|
91
|
+
return runMcpStatus(jsonOutput);
|
|
92
|
+
if (rest[0] === "serve")
|
|
93
|
+
return runMcpServe();
|
|
94
|
+
throw new Error("用法:cvh mcp install | uninstall | status | serve");
|
|
75
95
|
case "hook": {
|
|
76
96
|
const raw = await readStdin();
|
|
77
97
|
const payload = JSON.parse(raw || "{}");
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* `cvh config get/set` 命令实现。
|
|
3
3
|
*/
|
|
4
4
|
import { loadConfig, saveConfig } from "../config.js";
|
|
5
|
+
import { EXPECTED_BASE_URL_SEGMENT } from "../vision.js";
|
|
5
6
|
const EDITABLE_KEYS = ["provider", "model", "baseUrl", "apiKey", "timeoutMs", "maxTokens"];
|
|
6
7
|
/** provider 字段允许的合法值,与 vision.ts 的 resolveModel() 分支一一对应。 */
|
|
7
8
|
const VALID_PROVIDERS = ["oai", "responses", "anthropic", "gemini"];
|
|
@@ -14,6 +15,7 @@ const isEditableKey = (key) => EDITABLE_KEYS.includes(key);
|
|
|
14
15
|
* @param config - 待修改的配置对象(原地修改)
|
|
15
16
|
* @param key - 目标字段名
|
|
16
17
|
* @param value - 字符串形式的新值
|
|
18
|
+
* @returns 非阻断性的警告信息(目前仅 baseUrl 疑似缺少路径段时返回),无警告时返回 undefined
|
|
17
19
|
* @throws 当值不满足对应字段的校验规则时抛出错误(provider 非法、数字字段非数字/非正数等)
|
|
18
20
|
*/
|
|
19
21
|
const applyConfigValue = (config, key, value) => {
|
|
@@ -23,13 +25,13 @@ const applyConfigValue = (config, key, value) => {
|
|
|
23
25
|
throw new Error(`不支持的 provider:${value}。可用值:${VALID_PROVIDERS.join(", ")}`);
|
|
24
26
|
}
|
|
25
27
|
config.provider = value;
|
|
26
|
-
return;
|
|
28
|
+
return undefined;
|
|
27
29
|
}
|
|
28
30
|
case "model": {
|
|
29
31
|
if (!value.trim())
|
|
30
32
|
throw new Error("model 不能为空");
|
|
31
33
|
config.model = value;
|
|
32
|
-
return;
|
|
34
|
+
return undefined;
|
|
33
35
|
}
|
|
34
36
|
case "baseUrl": {
|
|
35
37
|
try {
|
|
@@ -40,7 +42,16 @@ const applyConfigValue = (config, key, value) => {
|
|
|
40
42
|
throw new Error(`baseUrl 不是合法的 URL:${value}`);
|
|
41
43
|
}
|
|
42
44
|
config.baseUrl = value;
|
|
43
|
-
|
|
45
|
+
// 软性提示,不阻断:AI SDK 不会给自定义网关自动补路径段,裸域名会在真正调用时才报
|
|
46
|
+
// 一个语义不明的 404(真机验证时踩过这个坑)。官方地址是例外(SDK 内部特殊处理过),
|
|
47
|
+
// 不需要提示。
|
|
48
|
+
const expectedSegment = EXPECTED_BASE_URL_SEGMENT[config.provider];
|
|
49
|
+
const isOfficialEndpoint = /^https:\/\/(api\.openai\.com|api\.anthropic\.com|generativelanguage\.googleapis\.com)/.test(value);
|
|
50
|
+
if (!isOfficialEndpoint && !value.includes(expectedSegment)) {
|
|
51
|
+
return (`baseUrl "${value}" 看起来缺少 ${config.provider} provider 期望的路径段 "${expectedSegment}"。` +
|
|
52
|
+
`AI SDK 不会自动补全自定义网关的路径,如果这不是故意的,建议改成 "${value}${expectedSegment}"。`);
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
44
55
|
}
|
|
45
56
|
case "apiKey": {
|
|
46
57
|
if (!value.trim())
|
|
@@ -93,12 +104,14 @@ export const runConfigSet = async (key, value, jsonOutput) => {
|
|
|
93
104
|
throw new Error(`不支持的配置项:${key}。可用项:${EDITABLE_KEYS.join(", ")}`);
|
|
94
105
|
}
|
|
95
106
|
const config = await loadConfig();
|
|
96
|
-
applyConfigValue(config, key, value);
|
|
107
|
+
const warning = applyConfigValue(config, key, value);
|
|
97
108
|
await saveConfig(config);
|
|
98
109
|
if (jsonOutput) {
|
|
99
|
-
console.log(JSON.stringify({ ok: true, key, value }));
|
|
110
|
+
console.log(JSON.stringify({ ok: true, key, value, ...(warning ? { warning } : {}) }));
|
|
100
111
|
return;
|
|
101
112
|
}
|
|
102
113
|
console.log(`✅ ${key} = ${key === "apiKey" ? "***" : value}`);
|
|
114
|
+
if (warning)
|
|
115
|
+
console.log(`⚠️ ${warning}`);
|
|
103
116
|
};
|
|
104
117
|
//# sourceMappingURL=configCmd.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configCmd.js","sourceRoot":"","sources":["../../src/commands/configCmd.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAoC,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"configCmd.js","sourceRoot":"","sources":["../../src/commands/configCmd.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
import { stat } from "node:fs/promises";
|
|
6
6
|
import { loadConfig, getConfigPath } from "../config.js";
|
|
7
7
|
import { checkHooksInstalled } from "../claudeSettings.js";
|
|
8
|
-
import {
|
|
8
|
+
import { checkMcpInstalled } from "../claudeMcp.js";
|
|
9
|
+
import { describeImage, EXPECTED_BASE_URL_SEGMENT } from "../vision.js";
|
|
10
|
+
/** 官方 API 地址不需要提示——AI SDK 对这几个官方域名有特殊处理,会自动补路径段。 */
|
|
11
|
+
const OFFICIAL_ENDPOINT_RE = /^https:\/\/(api\.openai\.com|api\.anthropic\.com|generativelanguage\.googleapis\.com)/;
|
|
9
12
|
// 1x1 透明 PNG,仅用于自检时验证视觉模型连通性,不产生真实业务含义。
|
|
10
13
|
const PROBE_IMAGE_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=";
|
|
11
14
|
/**
|
|
@@ -29,7 +32,32 @@ export const runDoctor = async (jsonOutput) => {
|
|
|
29
32
|
const hooks = await checkHooksInstalled();
|
|
30
33
|
checks.push({ name: "UserPromptSubmit Hook 已注册", ok: hooks.userPromptSubmit, detail: hooks.userPromptSubmit ? "" : "请运行 cvh install" });
|
|
31
34
|
checks.push({ name: "PostToolUse Hook 已注册", ok: hooks.postToolUse, detail: hooks.postToolUse ? "" : "请运行 cvh install" });
|
|
35
|
+
// MCP 是可选功能,未安装不算失败项(不计入 allOk),只是提示性展示——
|
|
36
|
+
// 呼应既定设计:"cvh disable 不卸载 MCP,cvh enable 不安装 MCP",MCP 与主开关正交。
|
|
37
|
+
const mcpInstalled = await checkMcpInstalled();
|
|
38
|
+
checks.push({
|
|
39
|
+
name: "MCP server 已注册(可选)",
|
|
40
|
+
ok: true,
|
|
41
|
+
detail: mcpInstalled ? "已注册,Agent 可调用 vision_ask 等工具" : "未注册(可选功能,运行 cvh mcp install 启用)",
|
|
42
|
+
});
|
|
32
43
|
checks.push({ name: "API Key 已配置", ok: Boolean(config.apiKey), detail: config.apiKey ? "已设置" : "请运行 cvh config set apiKey <key>" });
|
|
44
|
+
// 真机验证时踩过的坑:AI SDK 不会给自定义网关的 baseUrl 自动补路径段(如 "/v1"),
|
|
45
|
+
// 裸域名会在真正调用视觉模型时才报一个语义不明的 404,排查成本很高。这里提前检查,
|
|
46
|
+
// 不管 provider/baseUrl 是以什么顺序设置的都能覆盖(config set 时的警告只能覆盖
|
|
47
|
+
// "先设 provider 再设 baseUrl" 这一种顺序,这里用最终生效的配置兜底检查所有顺序)。
|
|
48
|
+
if (config.baseUrl) {
|
|
49
|
+
const expectedSegment = EXPECTED_BASE_URL_SEGMENT[config.provider];
|
|
50
|
+
const isOfficial = OFFICIAL_ENDPOINT_RE.test(config.baseUrl);
|
|
51
|
+
const looksOk = isOfficial || config.baseUrl.includes(expectedSegment);
|
|
52
|
+
checks.push({
|
|
53
|
+
name: "baseUrl 路径段检查",
|
|
54
|
+
ok: looksOk,
|
|
55
|
+
detail: looksOk
|
|
56
|
+
? ""
|
|
57
|
+
: `baseUrl "${config.baseUrl}" 可能缺少 ${config.provider} provider 期望的路径段 "${expectedSegment}",` +
|
|
58
|
+
`建议改成 "${config.baseUrl}${expectedSegment}",否则调用时会收到语义不明的 404`,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
33
61
|
if (config.apiKey) {
|
|
34
62
|
try {
|
|
35
63
|
const start = Date.now();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cvh init` 命令实现:交互式安装向导,一步到位完成
|
|
3
|
+
* 「选 provider → 填 model/baseUrl/apiKey → 写配置 → 注册 Hook → 可选注册 MCP → 可选立即 enable」。
|
|
4
|
+
*
|
|
5
|
+
* 设计取舍:
|
|
6
|
+
* - 只在 TTY 环境下可用(`process.stdin.isTTY`),非交互环境(CI/脚本管道)直接报错并提示
|
|
7
|
+
* 改用 `cvh install` + `cvh config set` 组合命令,避免卡在 readline 等待输入。
|
|
8
|
+
* - 不提供 `--json` 模式——`init` 的本质是"人工问答",脚本化场景应该走 `install`/`config set`,
|
|
9
|
+
* 这与其他命令的 `--json` 支持约定不冲突(`init` 本来就不是给脚本用的)。
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* 运行交互式安装向导。
|
|
13
|
+
*
|
|
14
|
+
* @throws 当 stdin 不是 TTY(非交互环境)时抛出错误,提示改用 `install`/`config set`
|
|
15
|
+
*/
|
|
16
|
+
export declare const runInit: () => Promise<void>;
|