@infimind-next/ai-hive-mcp 0.3.2 → 0.4.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.
Files changed (42) hide show
  1. package/README.md +78 -16
  2. package/dist/api-client.d.ts +15 -0
  3. package/dist/api-client.js +89 -9
  4. package/dist/cli.d.ts +4 -0
  5. package/dist/cli.js +34 -0
  6. package/dist/config.d.ts +19 -1
  7. package/dist/config.js +104 -7
  8. package/dist/index.js +9 -1
  9. package/dist/managed-config.d.ts +75 -0
  10. package/dist/managed-config.js +71 -0
  11. package/dist/managed-download.d.ts +13 -0
  12. package/dist/managed-download.js +138 -0
  13. package/dist/managed-files.d.ts +35 -0
  14. package/dist/managed-files.js +240 -0
  15. package/dist/managed-images.d.ts +70 -0
  16. package/dist/managed-images.js +449 -0
  17. package/dist/managed-server.d.ts +15 -0
  18. package/dist/managed-server.js +195 -0
  19. package/dist/managed-token-provider.d.ts +11 -0
  20. package/dist/managed-token-provider.js +31 -0
  21. package/dist/rest-commands.d.ts +14 -0
  22. package/dist/rest-commands.js +164 -0
  23. package/dist/schemas.d.ts +121 -13
  24. package/dist/schemas.js +81 -1
  25. package/dist/server.d.ts +4 -3
  26. package/dist/server.js +119 -46
  27. package/dist/setup/detect.d.ts +9 -0
  28. package/dist/setup/detect.js +34 -0
  29. package/dist/setup/merge.d.ts +5 -0
  30. package/dist/setup/merge.js +142 -0
  31. package/dist/setup/run-setup.d.ts +20 -0
  32. package/dist/setup/run-setup.js +332 -0
  33. package/dist/setup/skills.d.ts +8 -0
  34. package/dist/setup/skills.js +35 -0
  35. package/dist/setup/targets.d.ts +14 -0
  36. package/dist/setup/targets.js +46 -0
  37. package/package.json +11 -9
  38. package/skills/ai-hive-billing/SKILL.md +33 -0
  39. package/skills/ai-hive-image-video-generation/SKILL.md +41 -0
  40. package/skills/ai-hive-media-upload/SKILL.md +33 -0
  41. package/skills/ai-hive-model-selection/SKILL.md +50 -0
  42. package/skills/ai-hive-text-generation/SKILL.md +39 -0
package/README.md CHANGED
@@ -4,38 +4,80 @@
4
4
 
5
5
  ## 安装
6
6
 
7
- 在 MCP 客户端中加入以下配置,把 `<APIKEY>` 换成你在蜂巢 AI 网页端「MCP 接入」里生成的 API Key:
7
+ 0.4.0 为本轮待发布版本;发布前继续使用已发布的 0.3.3。发布后在 MCP 客户端中加入以下配置,把 `<APIKEY>` 换成你在蜂巢 AI 网页端「MCP 接入」里生成的 MCP 密钥:
8
8
 
9
9
  ```json
10
10
  {
11
11
  "mcpServers": {
12
12
  "ai-hive": {
13
13
  "command": "npx",
14
- "args": ["-y", "@infimind-next/ai-hive-mcp@0.3.2", "--key", "<APIKEY>"]
14
+ "args": ["-y", "@infimind-next/ai-hive-mcp@0.4.0"],
15
+ "env": { "AI_HIVE_MCP_KEY": "<APIKEY>" }
15
16
  }
16
17
  }
17
18
  }
18
19
  ```
19
20
 
20
- 要求 Node.js >= 20.19.0。默认请求超时为 300 秒,可通过 `--timeout-ms` 或 `AI_HIVE_MCP_TIMEOUT_MS` 覆盖;无效值回退为 300 秒。WorkBuddy Connector 的启动与认证命令保持引用 `@latest`,Connector 和 Skills 版本仍为 1.2.0。
21
+ 手动配置也把密钥放在 `env` 里;`--key` 仍兼容,但会出现在进程参数中,不建议继续使用。
22
+
23
+ 也可以在终端或 Agent 里运行下面的命令,自动检测本机客户端、写入配置并安装技能:
24
+
25
+ ```bash
26
+ AI_HIVE_MCP_KEY=<APIKEY> npx -y @infimind-next/ai-hive-mcp@latest setup
27
+ ```
28
+
29
+ `setup` 会检测 Claude Code、Cursor、Codex、Claude Desktop 和 OpenCode,只写入 `ai-hive` 这一项,保留你已有的 MCP 配置和已有密钥;密钥以环境变量写入配置文件,不放进启动参数。没有密钥且本机没有登录凭证时会直接失败,不会写入一份不可用的配置。Claude Code 和 Codex 会同时安装技能。重复执行是安全的。常用选项:
30
+
31
+ - `--client cursor,codex`:只处理指定客户端。
32
+ - `--dry-run`:只预览,不写文件。
33
+ - `--skip-skills`:只写 MCP 配置,不安装技能。
34
+
35
+ 要求 Node.js >= 20.19.0。默认请求超时为 300 秒,可通过 `--timeout-ms` 或 `AI_HIVE_MCP_TIMEOUT_MS` 覆盖;无效值回退为 300 秒。WorkBuddy Connector 的启动与认证命令保持引用 `@latest`,Connector 和 Skills 版本仍为 1.2.1。
21
36
 
22
37
  本包通过标准 MCP stdio 与客户端通信,仅保证与支持 MCP 的客户端配合使用。
23
38
 
39
+ ## 命令行
40
+
41
+ 没有 MCP 宿主时,也可以用同一个 MCP 密钥直接调用。`run` 会先读取所选线路的报价再提交,金额单位是蜂蜜;只有充值实付金额才是元。输出默认适合人读,加 `--json` 返回原始结构:
42
+
43
+ ```bash
44
+ ai-hive-mcp models --type IMAGE --json # 列模型与售价
45
+ ai-hive-mcp model <模型 ID> --json # 查参数契约
46
+ ai-hive-mcp usage --json # 查积分与账单汇总
47
+ ai-hive-mcp run <模型 ID> "写一句欢迎语" # 付费文字调用
48
+ ```
49
+
50
+ `run` 是付费调用,每次都会生成新的 `clientRequestId`;需要重试同一次请求时改用 MCP 的 `chat_text` 并复用原标识。
51
+
24
52
  ## 工具
25
53
 
26
- - `get_user_info`:查询当前用户和普通蜂蜜余额
54
+ - `get_user_info`:查询当前用户和普通蜂蜜积分
27
55
 
28
- 专属蜂蜜由服务端按实际模型自动选择抵扣,无需新增调用参数。不能仅凭普通蜂蜜余额为 0 拒绝模型调用;只有实际调用返回余额不足时,才使用 `rechargeUrl` 引导充值并重试。
56
+ 专属蜂蜜由服务端按实际模型自动选择抵扣,无需新增调用参数。不能仅凭普通蜂蜜积分为 0 拒绝模型调用;只有实际调用返回积分不足时,才使用 `rechargeUrl` 引导充值并重试。
29
57
 
30
58
  - `list_models`:按 TEXT、IMAGE 或 VIDEO 查询可用模型与公开价格
31
59
  - `get_model`:查询单个模型的参数 schema、媒体要求、计费因素和调用示例
32
- - `upload_media_from_path`:上传本地图片、视频或音频,返回 mediaId
60
+ - `upload_media_from_path`:上传本地图片、视频、音频或文档,返回 mediaId
33
61
  - `chat_text`:文本对话
34
62
  - `generate_image`:图片预检或正式生成
35
63
  - `generate_video`:视频预检或正式生成
36
64
  - `get_material_mode`:查询认证资格和网页授权入口(可选)
37
65
  - `quote_video_materials`:获取素材报价(可选)
38
- - `get_generation_task`:查询生成任务进度和结果
66
+ - `list_generation_tasks`:查询当前账号全部入口的图片、视频任务摘要,最新优先
67
+ - `get_generation_task`:按任务 ID 查询状态、结果和费用
68
+ - `get_billing_summary`:查询当前账户全部入口的账单汇总
69
+ - `list_billing_records`:分页查询账单记录
70
+ - `get_billing_record`:按记录 ID 查询账单详情
71
+
72
+ ### 账单查询(待发布)
73
+
74
+ 3 个账单工具均为只读,需要所连接服务端支持。查询当前账户在网页、API、MCP 等全部入口的记录,不接受手机号或其他用户 ID。现有模型、任务和账户工具的调用方式不变;本地包版本和发布状态不因本次代码变更而改变。
75
+
76
+ 账单单位为 HONEY(蜂蜜虚拟积分),积分数值沿用服务端的 micros 整数字符串,避免精度损失。消费仅指实际结算,预占、释放分别展示,不计入消费。查询某批生成任务时,传入该批的 `taskIds`,不要用前后积分之差推断批次费用;尚未结算的任务不能当作免费。
77
+
78
+ `get_billing_summary` 和 `list_billing_records` 支持可选 `from`、`to`、`taskIds`、`publicModelId`、`invocationSource`。不传时间时由服务端使用北京时间今日;指定时间须成对传入含时区的 ISO 8601 字符串,按左闭右开区间 `[from,to)` 查询。`taskIds` 传 1~100 个非空任务 ID。`invocationSource` 可选 `UNKNOWN`、`WEB`、`MCP`、`WORKBUDDY`、`QWENWORK`、`DOUBAO_WORK`、`MIAOSHOU`、`OPENAPI`。
79
+
80
+ `list_billing_records` 的 `pageSize` 默认 20、最多 100,后续分页原样传回 `cursor`。`get_billing_record` 接收列表返回的 `recordId`,例如 `charge:<id>` 或 `reservation:<id>`。这些工具不请求活动或公告通知。
39
81
 
40
82
  ### 认证素材模式
41
83
 
@@ -53,14 +95,20 @@
53
95
 
54
96
  1. 用 `list_models` 选择模型,记录 `publicModelId`。
55
97
  2. 用 `get_model` 查看参数契约,`params` 的字段名、类型、枚举值和默认值逐项来自返回的 `paramsSchema`。
56
- 3. 调用 `generate_image` 或 `generate_video` 时传 `dryRun: true` 预检,核对返回的 `estimatedReservationMicros` 估价(单位:人民币 micros,1 元 = 1,000,000 micros)。
98
+ 3. 调用 `generate_image` 或 `generate_video` 时传 `dryRun: true` 预检,核对返回的 `estimatedReservationMicros` 估价(单位:蜂蜜 micros,1,000,000 micros = 1 蜂蜜;只有充值实付金额使用元)。
57
99
  4. 确认费用后去掉 `dryRun` 正式生成,`params` 替换为预检返回的 `resolvedParams`,并传入预检返回的 `pricingSnapshot` 和本次调用稳定唯一的 `clientRequestId`。
58
- 5. 用 `get_generation_task` 查询任务进度和结果,不重复创建任务。
100
+ 5. 用 `get_generation_task` 查询任务进度、结果和费用,不重复创建任务。
101
+
102
+ 需要找回近期任务时,使用 `list_generation_tasks`。可选 `publicModelId` 筛选模型,`page` 默认 1,`pageSize` 默认 20、最多 50。返回 `{tasks,total,page,pageSize}`,按创建时间和任务 ID 倒序;列表仅含任务与子任务 ID、模型、类型、创建时间、批量数量、160 字符以内提示词摘要、状态和进度,完整参数、结果地址及费用见详情。列表是只读工具,不返回公告或消耗通知额度。
103
+
104
+ 详情默认包含 `billing`:金额单位为蜂蜜,`currency=CNY` 仅为历史兼容编码,不作为展示币种;公开价格快照、预估金额 `estimatedReservationMicros`、已入账金额 `settledChargeMicros` 和 `settlementComplete`。金额为 micros 字符串;结算未完成时已入账金额尚非最终费用,不能把 0 当免费。`billing:null` 表示费用暂不可读,任务结果仍可用。关闭通知不影响费用。钱包继续用 `get_user_info`。
105
+
106
+ 远程 MCP 随服务端部署获得列表能力,本地 MCP 需升级到 0.3.4。新版客户端连接旧服务端不支持的列表端点时,会提示继续按任务 ID 查询。已发布的旧客户端无需修改详情调用即可透传 `billing`;严格拒绝新增响应字段的自定义消费者需更新响应校验规则。
59
107
 
60
108
  ## 重试与价格变化
61
109
 
62
110
  - 传输中断或超时后重试:复用同一个 `clientRequestId` 和相同业务参数,不会重复扣费。
63
- - 修正参数、余额或媒体问题后重试:必须使用新的 `clientRequestId`。
111
+ - 修正参数、积分或媒体问题后重试:必须使用新的 `clientRequestId`。
64
112
  - 正式生成提示价格已更新:保持业务参数和原 `clientRequestId` 不变,重新预检获取最新 `pricingSnapshot` 后重试。
65
113
  - 返回 `REQUEST_OUTCOME_UNKNOWN`:停止自动重试,联系管理员核查。
66
114
 
@@ -68,23 +116,37 @@
68
116
 
69
117
  用户提供普通素材即可,后台自动处理认证。使用对应认证模型与当前价格快照,先 dryRun 预检资格、素材和费用;通过后沿用标准版提交格式,认证字段和专用报价工具都可省略。收到任务 ID 后只查询原任务。
70
118
 
71
- 仅缺少授权、充值资格或余额时向用户说明必要操作,并提供[认证与充值入口](https://ai-hive.iclip.cn/certified-materials#guide);不擅自换模型提交。费用变化时重新确认,预检通过不代表内容审核通过。
119
+ 仅缺少授权、充值资格或积分时向用户说明必要操作,并提供[认证与充值入口](https://ai-hive.iclip.cn/certified-materials#guide);不擅自换模型提交。费用变化时重新确认,预检通过不代表内容审核通过。
72
120
 
73
121
  更新信息仅在用户询问或与当前任务有关时介绍,不向用户转述内部参数、错误码、报价与重试机制。客户端可选升级提示默认静默,仅开启 `--verbose` 时输出维护诊断。技术维护记录见仓库 `docs/openapi/technical-changelog.md`。
74
122
 
75
123
  `recoveryAction` 是给调用端的恢复指引,不要逐字转成用户待办。响应中断用原 `clientRequestId` 和相同参数恢复;明确失败后再次生成,或改变报价/输入时,使用新请求标识。结果未知时先核查,不自动重建付费任务。
76
124
 
77
- 上述已发布客户端部分依据已校验的 npm 0.3.1 tarball。发布时间为 2026-09-30 18:32(北京时间),核对日期为 2026-10-03。发布物、仓库差异与验证边界见 [0.3.1 核对记录](../../docs/release/mcp-0.3.1-runbook.md);下方另列本轮未发布扩展。
125
+ 当前代码准备 0.4.0,尚未发布;registry 已发布版本为 0.3.3(2026-10-09)。发布物、兼容验证与服务端验收边界见 [0.3.2 发布记录](../../docs/release/mcp-0.3.2-runbook.md)。
126
+
127
+ ### 0.4.0(待发布):一条命令接入
78
128
 
79
- ### 后续未发布增量:通知协商与直接上传
129
+ 新增 `setup` 命令:检测本机客户端、写入 MCP 配置、安装随包发布的 5 个技能,并用 `user-info` 验证连接。新增 `models`、`model`、`usage`、`run` 四个命令行子命令,让没有 MCP 宿主的 Agent 也能选型、试跑和查账。`setup` 写入的配置统一走 `AI_HIVE_MCP_KEY`,MCP 服务的启动参数里不再带密钥。本版本需要 npm 发布后才对 `@latest` 生效。
80
130
 
81
- 0.3.1 保留旧充值活动返回;下面的新公告扩展与直接上传工具需要先部署本轮服务端源码,再另行发布支持它们的客户端。0.3.1 的已发布工具不包含这些增量。
131
+ 向下兼容:0.3.3 的 12 个工具名称、入参和返回保持不变,0.4.0 只新增 4 个只读工具;`server`、`auth`、`status`、`logout` 的行为不变。旧服务端不接受 `detail` 参数时,`list_models` 会自动去掉它重试一次。账单工具需要服务端已部署账单接口,未部署时会提示到控制台查看,不影响其他工具。
132
+
133
+ ### 0.3.2:通知协商与直接上传
134
+
135
+ 0.3.1 保留旧充值活动返回。下面的公告扩展与直接上传工具已包含在 0.3.2 中,需要所连接服务端支持;客户端发布不代表生产服务已部署。
82
136
 
83
137
  - `create_media_upload`:直接创建上传地址;`complete_media_upload`:直接确认上传完成。两者可在结果中返回通知。
84
138
  - 支持通知的查询工具固定声明 `readOnlyHint=false`,因为服务端配置可启用返回状态写入。通知保留条件、截止时间和链接,结合对话转述,同一对话避免机械重复;实际展示由宿主决定。
85
139
  - REST 请求级 `X-AI-Hive-Announcements: 1` 启用新的公告扩展;账户 POST 不要求该头。已发布的 0.3.1 不发送新请求头,仍可通过账户 GET 与原有付费请求接收旧充值活动,无需升级客户端或新增参数。SDK 直接工具请求协商,内部价格快照和自动上传初始化关闭;`upload_media_from_path` 展示完整上传完成结果。
86
- - 正式付费通知请求必须有合法 `clientRequestId`,重放与恢复不重复通知,dryRun 无通知。这些是本地开发分支行为,尚未发布或完成真实宿主验收。
140
+ - 正式付费通知请求必须有合法 `clientRequestId`,重放与恢复不重复通知,dryRun 无通知。这些规则已通过本地协议验证;生产服务与真实宿主展示仍需独立验收。
87
141
 
88
142
  兼容模式(COMPAT)下,账户 GET 和 POST 常驻返回进行中的旧充值活动,不占用模型调用的每日活动通知次数。原有付费请求仍按用户每天首次成功调用返回活动;重放、恢复与 dryRun 不重复通知。管理员启用通知管理(MANAGED)后按入口设置返回;旧客户端保留 `activePromotion` 结构,功能公告通过支持新扩展的客户端返回。
89
143
 
90
- 本轮未发布增量中,CLI login/status 的凭证检查使用账户 GET 并明确发送 `X-AI-Hive-Announcements: 0`,完整验证凭证后仅返回账户核心字段、`activePromotion: null`,不写通知台账、共享次数或最短间隔状态。SDK 的 POST 404/405 后默认 GET 回退仍返回兼容活动。
144
+ 0.3.2 中,CLI login/status 的凭证检查使用账户 GET 并明确发送 `X-AI-Hive-Announcements: 0`,完整验证凭证后仅返回账户核心字段、`activePromotion: null`,不写通知台账、共享次数或最短间隔状态。SDK 的 POST 404/405 后默认 GET 回退仍返回兼容活动。
145
+
146
+ ### 0.3.3:简化提示与报价对象兼容
147
+
148
+ 工具说明精简为用途和必要输入;充值活动仅说明优惠、截止时间和入口。未拥有专属蜂蜜的账户不介绍相关信息。报价支持嵌套对象与 JSON 字符串,沿用服务端校验与原有支付规则。
149
+
150
+ ### 0.3.4(待发布):任务列表与详情费用
151
+
152
+ 新增 `list_generation_tasks`;现有详情调用默认返回费用,API 与 MCP 使用相同口径。推送、服务端部署和 npm 发布需另行授权。
@@ -1,3 +1,4 @@
1
+ import type { BillingQueryInput, ListBillingRecordsInput } from "./schemas.js";
1
2
  import type { BearerTokenProvider } from "./auth-token-provider.js";
2
3
  import { type McpClientInfo } from "./client-info.js";
3
4
  import { type UpdateNotifier } from "./update-notifier.js";
@@ -12,6 +13,7 @@ type AiHiveApiClientInput = {
12
13
  fetchImpl?: FetchImpl;
13
14
  clientInfoProvider?: () => McpClientInfo | undefined;
14
15
  updateNotifier?: UpdateNotifier;
16
+ clientKind?: "codex-launcher";
15
17
  };
16
18
  export declare class AiHiveApiError extends Error {
17
19
  readonly status: number | undefined;
@@ -39,6 +41,7 @@ export declare class AiHiveApiClient {
39
41
  private readonly fetchImpl;
40
42
  private readonly clientInfoProvider?;
41
43
  private readonly updateNotifier?;
44
+ private readonly clientKind?;
42
45
  constructor(input: AiHiveApiClientInput);
43
46
  private withTimeout;
44
47
  private request;
@@ -55,6 +58,18 @@ export declare class AiHiveApiClient {
55
58
  generateImage(input: unknown, options?: AnnouncementOptions): Promise<unknown>;
56
59
  generateVideo(input: unknown, options?: AnnouncementOptions): Promise<unknown>;
57
60
  private requestGeneration;
61
+ private billingQuery;
62
+ getBillingSummary(input: BillingQueryInput): Promise<unknown>;
63
+ listBillingRecords(input: ListBillingRecordsInput): Promise<unknown>;
64
+ getBillingRecord(input: {
65
+ recordId: string;
66
+ }): Promise<unknown>;
67
+ private withBillingSupport;
68
+ listGenerationTasks(input: {
69
+ publicModelId?: string;
70
+ page?: number;
71
+ pageSize?: number;
72
+ }): Promise<unknown>;
58
73
  getGenerationTask(input: {
59
74
  taskId: string;
60
75
  }, options?: AnnouncementOptions): Promise<unknown>;
@@ -15,8 +15,8 @@ export class AiHiveApiError extends Error {
15
15
  this.name = "AiHiveApiError";
16
16
  }
17
17
  }
18
- const GET_MODEL_UNSUPPORTED_MESSAGE = "当前 AI-HIVE 服务端版本不支持 get_model,请先升级服务端后再试";
19
- const DRY_RUN_UNSUPPORTED_MESSAGE = "当前 AI-HIVE 服务端版本不支持 dryRun,请先升级服务端后再试";
18
+ const GET_MODEL_UNSUPPORTED_MESSAGE = "当前接入暂时无法读取模型详情(get_model),请确认 API 地址和模型 ID;如果仍有问题,请联系技术支持。";
19
+ const DRY_RUN_UNSUPPORTED_MESSAGE = "当前接入暂不支持 dryRun,请联系技术支持确认预检方式;不要用正式生成代替预检。";
20
20
  function formatIssue(value) {
21
21
  if (typeof value === "string") {
22
22
  return value;
@@ -93,6 +93,7 @@ export class AiHiveApiClient {
93
93
  fetchImpl;
94
94
  clientInfoProvider;
95
95
  updateNotifier;
96
+ clientKind;
96
97
  constructor(input) {
97
98
  this.endpoint = input.endpoint.replace(/\/+$/, "");
98
99
  this.tokenProvider = input.tokenProvider;
@@ -100,6 +101,7 @@ export class AiHiveApiClient {
100
101
  this.fetchImpl = input.fetchImpl ?? fetch;
101
102
  this.clientInfoProvider = input.clientInfoProvider;
102
103
  this.updateNotifier = input.updateNotifier;
104
+ this.clientKind = input.clientKind;
103
105
  }
104
106
  async withTimeout(operation) {
105
107
  const controller = new AbortController();
@@ -157,6 +159,7 @@ export class AiHiveApiClient {
157
159
  signal,
158
160
  headers: {
159
161
  Authorization: `Bearer ${token}`,
162
+ ...(this.clientKind ? { "x-ai-hive-client": this.clientKind } : {}),
160
163
  ...(encodedClientInfo
161
164
  ? { [MCP_CLIENT_INFO_HEADER]: encodedClientInfo }
162
165
  : {}),
@@ -220,15 +223,33 @@ export class AiHiveApiClient {
220
223
  });
221
224
  }
222
225
  async listModels(input, options) {
223
- const query = new URLSearchParams();
224
- if (typeof input.modelType === "string") {
225
- query.set("modelType", input.modelType);
226
+ const buildPath = (withDetail) => {
227
+ const query = new URLSearchParams();
228
+ if (typeof input.modelType === "string") {
229
+ query.set("modelType", input.modelType);
230
+ }
231
+ if (withDetail && typeof input.detail === "string") {
232
+ query.set("detail", input.detail);
233
+ }
234
+ const serialized = query.toString();
235
+ return `/mcp/models${serialized ? `?${serialized}` : ""}`;
236
+ };
237
+ try {
238
+ return redactInternalPricing(await this.request(buildPath(true), {
239
+ headers: announcementHeaders(options),
240
+ }));
226
241
  }
227
- if (typeof input.detail === "string") {
228
- query.set("detail", input.detail);
242
+ catch (error) {
243
+ // 旧服务端的严格入参不认识 detail; 去掉它重试一次, 让新客户端的选型命令继续可用.
244
+ if (typeof input.detail === "string" &&
245
+ error instanceof AiHiveApiError &&
246
+ (error.status === 400 || error.status === 404)) {
247
+ return redactInternalPricing(await this.request(buildPath(false), {
248
+ headers: announcementHeaders(options),
249
+ }));
250
+ }
251
+ throw error;
229
252
  }
230
- const serialized = query.toString();
231
- return redactInternalPricing(await this.request(`/mcp/models${serialized ? `?${serialized}` : ""}`, { headers: announcementHeaders(options) }));
232
253
  }
233
254
  // getModel 与 listModels 的 detail 参数依赖服务端新部署; 旧服务端会 404 或被
234
255
  // strict 入参校验拒绝, 所以 npm 0.3.0 必须在服务端升级之后发布.
@@ -303,6 +324,65 @@ export class AiHiveApiClient {
303
324
  throw error;
304
325
  }
305
326
  }
327
+ billingQuery(input) {
328
+ const query = new URLSearchParams();
329
+ if (input.from !== undefined)
330
+ query.set("from", input.from);
331
+ if (input.to !== undefined)
332
+ query.set("to", input.to);
333
+ for (const taskId of input.taskIds ?? [])
334
+ query.append("taskIds", taskId);
335
+ if (input.publicModelId !== undefined)
336
+ query.set("publicModelId", input.publicModelId);
337
+ if (input.invocationSource !== undefined)
338
+ query.set("invocationSource", input.invocationSource);
339
+ if (input.pageSize !== undefined)
340
+ query.set("pageSize", String(input.pageSize));
341
+ if (input.cursor !== undefined)
342
+ query.set("cursor", input.cursor);
343
+ return query.size ? `?${query}` : "";
344
+ }
345
+ getBillingSummary(input) {
346
+ return this.withBillingSupport("账单查询", () => this.request(`/mcp/billing/summary${this.billingQuery(input)}`));
347
+ }
348
+ listBillingRecords(input) {
349
+ return this.withBillingSupport("账单查询", () => this.request(`/mcp/billing/records${this.billingQuery(input)}`));
350
+ }
351
+ getBillingRecord(input) {
352
+ return this.withBillingSupport("账单详情", () => this.request(`/mcp/billing/records/${encodeURIComponent(input.recordId)}`), "账单记录不存在,或当前服务端暂不支持账单查询;请到控制台核对。");
353
+ }
354
+ // 0.4.0 会通过 @latest 自动升级到还没有账单接口的旧服务端, 不能把 404 原样抛给用户.
355
+ async withBillingSupport(feature, request, notFoundMessage = `当前服务端暂不支持${feature},请到控制台查看积分与消费。`) {
356
+ try {
357
+ return await request();
358
+ }
359
+ catch (error) {
360
+ if (error instanceof AiHiveApiError &&
361
+ (error.status === 404 || error.status === 405)) {
362
+ throw new Error(notFoundMessage);
363
+ }
364
+ throw error;
365
+ }
366
+ }
367
+ async listGenerationTasks(input) {
368
+ const query = new URLSearchParams();
369
+ if (input.publicModelId !== undefined)
370
+ query.set("publicModelId", input.publicModelId);
371
+ if (input.page !== undefined)
372
+ query.set("page", String(input.page));
373
+ if (input.pageSize !== undefined)
374
+ query.set("pageSize", String(input.pageSize));
375
+ try {
376
+ return await this.request(`/mcp/generation/tasks${query.size ? `?${query}` : ""}`);
377
+ }
378
+ catch (error) {
379
+ if (error instanceof AiHiveApiError &&
380
+ [404, 405].includes(error.status ?? 0)) {
381
+ throw new Error("当前服务端暂不支持近期任务列表,请先按任务 ID 查询已有任务");
382
+ }
383
+ throw error;
384
+ }
385
+ }
306
386
  getGenerationTask(input, options) {
307
387
  return this.request(`/mcp/generation/tasks/${encodeURIComponent(input.taskId)}?include=user-notices`, { headers: announcementHeaders(options) });
308
388
  }
package/dist/cli.d.ts CHANGED
@@ -3,6 +3,7 @@ import { runServer as runMcpServer } from "./server.js";
3
3
  import { WorkBuddyCredentialStore } from "./workbuddy-credentials.js";
4
4
  import { WorkBuddyOAuthClient } from "./workbuddy-oauth-client.js";
5
5
  import { listenForWorkBuddyCallback, type WorkBuddyResultRedirects } from "./workbuddy-loopback.js";
6
+ import { runManagedServer } from "./managed-server.js";
6
7
  export type CliDependencies = {
7
8
  stdout(line: string): void;
8
9
  stderr(line: string): void;
@@ -10,6 +11,9 @@ export type CliDependencies = {
10
11
  createOAuthClient(endpoint: string): WorkBuddyOAuthClient;
11
12
  listenForCallback: typeof listenForWorkBuddyCallback;
12
13
  runServer: typeof runMcpServer;
14
+ runManagedServer: typeof runManagedServer;
15
+ runSetup(config: McpClientConfig): Promise<number>;
16
+ runRestCommand(config: McpClientConfig): Promise<number>;
13
17
  };
14
18
  export declare function runCli(config: McpClientConfig, dependencies?: Partial<CliDependencies>): Promise<number>;
15
19
  export declare function createWorkBuddyResultRedirects(webOrigin: string): WorkBuddyResultRedirects;
package/dist/cli.js CHANGED
@@ -1,9 +1,15 @@
1
+ import { homedir } from "node:os";
2
+ import { fileURLToPath } from "node:url";
1
3
  import { normalizeApiEndpoint } from "./auth-token-provider.js";
2
4
  import { resolveServerAuth, runServer as runMcpServer } from "./server.js";
5
+ import { defaultRestCommandDependencies, runRestCommand, } from "./rest-commands.js";
6
+ import { runSetup } from "./setup/run-setup.js";
3
7
  import { WorkBuddyCredentialStore, } from "./workbuddy-credentials.js";
4
8
  import { WorkBuddyOAuthClient } from "./workbuddy-oauth-client.js";
5
9
  import { listenForWorkBuddyCallback, } from "./workbuddy-loopback.js";
6
10
  import { createPkcePair } from "./workbuddy-pkce.js";
11
+ import { loadManagedConfig } from "./managed-config.js";
12
+ import { runManagedServer } from "./managed-server.js";
7
13
  const AUTH_URL_DEADLINE_MS = 10_000;
8
14
  const AUTH_CALLBACK_DEADLINE_MS = 5 * 60_000;
9
15
  const STATUS_DEADLINE_MS = 8_000;
@@ -28,6 +34,13 @@ export async function runCli(config, dependencies) {
28
34
  return runLogout(config, resolved);
29
35
  case "server":
30
36
  return runMcp(config, resolved);
37
+ case "setup":
38
+ return resolved.runSetup(config);
39
+ case "models":
40
+ case "model":
41
+ case "usage":
42
+ case "run":
43
+ return resolved.runRestCommand(config);
31
44
  }
32
45
  }
33
46
  function resolveDependencies(config, overrides) {
@@ -46,6 +59,19 @@ function resolveDependencies(config, overrides) {
46
59
  : createOAuthClientWithTimeout,
47
60
  listenForCallback: overrides?.listenForCallback ?? listenForWorkBuddyCallback,
48
61
  runServer: overrides?.runServer ?? runMcpServer,
62
+ runManagedServer: overrides?.runManagedServer ?? runManagedServer,
63
+ runSetup: overrides?.runSetup ??
64
+ ((setupConfig) => runSetup(setupConfig, {
65
+ home: homedir(),
66
+ platform: process.platform,
67
+ env: process.env,
68
+ // 编译产物在 dist/cli.js, 包根目录的同级 skills 随之发布.
69
+ skillsRoot: fileURLToPath(new URL("../skills", import.meta.url)),
70
+ stdout: overrides?.stdout ?? ((line) => console.log(line)),
71
+ stderr: overrides?.stderr ?? ((line) => console.error(line)),
72
+ })),
73
+ runRestCommand: overrides?.runRestCommand ??
74
+ ((restConfig) => runRestCommand(restConfig, defaultRestCommandDependencies)),
49
75
  };
50
76
  }
51
77
  async function runAuth(config, dependencies) {
@@ -296,6 +322,14 @@ async function revokeAndClear(config, dependencies, store, deadlineAt) {
296
322
  }
297
323
  async function runMcp(config, dependencies) {
298
324
  try {
325
+ if (config.managedConfig) {
326
+ await dependencies.runManagedServer({
327
+ config: await loadManagedConfig(config.managedConfig),
328
+ managedConfigPath: config.managedConfig,
329
+ timeoutMs: config.timeoutMs,
330
+ });
331
+ return 0;
332
+ }
299
333
  const resolved = await resolveServerAuth(config, dependencies.createStore(config.credentialsFile));
300
334
  await dependencies.runServer({
301
335
  ...(config.verbose ? { verbose: true } : {}),
package/dist/config.d.ts CHANGED
@@ -1,13 +1,31 @@
1
- export type CliCommand = "server" | "auth" | "status" | "logout";
1
+ export type CliCommand = "server" | "auth" | "status" | "logout" | "setup" | "models" | "model" | "usage" | "run";
2
+ export type SetupClientId = "claude-code" | "cursor" | "codex" | "claude-desktop" | "opencode";
3
+ export declare const SETUP_CLIENT_IDS: readonly SetupClientId[];
4
+ export type SetupOptions = {
5
+ clients: SetupClientId[] | null;
6
+ dryRun: boolean;
7
+ skipSkills: boolean;
8
+ };
9
+ export type ModelType = "TEXT" | "IMAGE" | "VIDEO";
2
10
  export type McpClientConfig = {
3
11
  command: CliCommand;
4
12
  endpoint: string;
5
13
  endpointExplicit: boolean;
6
14
  webOrigin: string;
15
+ webOriginExplicit?: boolean;
7
16
  key: string | null;
8
17
  credentialsFile: string;
9
18
  timeoutMs: number;
10
19
  verbose: boolean;
20
+ managedConfig?: string;
21
+ positionals?: string[];
22
+ json?: boolean;
23
+ modelId?: string;
24
+ prompt?: string;
25
+ modelType?: ModelType;
26
+ detail?: "lite" | "full";
27
+ routingMode?: string;
28
+ setup?: SetupOptions;
11
29
  };
12
30
  export declare function parseConfig(input: {
13
31
  argv: string[];
package/dist/config.js CHANGED
@@ -1,4 +1,12 @@
1
1
  import { resolveWorkBuddyCredentialsFile } from "./workbuddy-credentials.js";
2
+ import { isAbsolute } from "node:path";
3
+ export const SETUP_CLIENT_IDS = [
4
+ "claude-code",
5
+ "cursor",
6
+ "codex",
7
+ "claude-desktop",
8
+ "opencode",
9
+ ];
2
10
  const DEFAULT_ENDPOINT = "https://ai-hive.iclip.cn/api";
3
11
  const DEFAULT_WEB_ORIGIN = "https://ai-hive.iclip.cn";
4
12
  // 文本对话由上游同步返回, 长推理模型实测可达 157s+ (gpt-6/claude-sonnet 系列).
@@ -10,29 +18,74 @@ const CLI_COMMANDS = new Set([
10
18
  "auth",
11
19
  "status",
12
20
  "logout",
21
+ "setup",
22
+ "models",
23
+ "model",
24
+ "usage",
25
+ "run",
13
26
  ]);
14
- const FLAG_OPTIONS = new Set(["verbose"]);
27
+ // setup 的目标由检测结果决定, 不接受位置参数, 避免误把路径当成客户端名.
28
+ const POSITIONAL_COMMANDS = new Set([
29
+ "models",
30
+ "model",
31
+ "usage",
32
+ "run",
33
+ ]);
34
+ const FLAG_OPTIONS = new Set(["verbose", "dry-run", "skip-skills", "json"]);
15
35
  const VALUE_OPTIONS = new Set([
16
36
  "endpoint",
17
37
  "web-origin",
18
38
  "key",
19
39
  "timeout-ms",
20
40
  "credentials-file",
41
+ "managed-config",
42
+ "client",
43
+ "model",
44
+ "prompt",
45
+ "type",
46
+ "detail",
47
+ "routing-mode",
21
48
  ]);
49
+ function parseClientIds(value) {
50
+ const ids = value
51
+ .split(",")
52
+ .map((item) => item.trim())
53
+ .filter((item) => item.length > 0);
54
+ if (ids.length === 0) {
55
+ throw new Error("选项缺少值");
56
+ }
57
+ for (const id of ids) {
58
+ if (!SETUP_CLIENT_IDS.includes(id)) {
59
+ throw new Error("未知客户端");
60
+ }
61
+ }
62
+ return ids;
63
+ }
22
64
  export function parseConfig(input) {
23
65
  const args = input.argv.slice(2);
24
66
  const values = new Map();
25
67
  const flags = new Set();
68
+ const positionals = [];
26
69
  let command = "server";
27
70
  let commandSeen = false;
28
71
  for (let index = 0; index < args.length; index += 1) {
29
72
  const arg = args[index];
30
- if (!arg?.startsWith("--")) {
31
- if (commandSeen || !CLI_COMMANDS.has(arg)) {
32
- throw new Error("未知命令");
73
+ if (arg === undefined) {
74
+ break;
75
+ }
76
+ if (!arg.startsWith("--")) {
77
+ if (!commandSeen) {
78
+ if (!CLI_COMMANDS.has(arg)) {
79
+ throw new Error("未知命令");
80
+ }
81
+ command = arg;
82
+ commandSeen = true;
83
+ continue;
33
84
  }
34
- command = arg;
35
- commandSeen = true;
85
+ if (!POSITIONAL_COMMANDS.has(command)) {
86
+ throw new Error("未知参数");
87
+ }
88
+ positionals.push(arg);
36
89
  continue;
37
90
  }
38
91
  const name = arg.slice(2);
@@ -54,13 +107,22 @@ export function parseConfig(input) {
54
107
  input.env.AI_HIVE_MCP_TIMEOUT_MS ??
55
108
  String(DEFAULT_TIMEOUT_MS), 10);
56
109
  const endpointOverride = values.get("endpoint") ?? input.env.AI_HIVE_API_ENDPOINT;
57
- return {
110
+ const managedConfig = values.get("managed-config");
111
+ if (managedConfig &&
112
+ (!isAbsolute(managedConfig) ||
113
+ command !== "server" ||
114
+ ["key", "endpoint", "credentials-file", "web-origin"].some((name) => values.has(name)))) {
115
+ throw new Error("受管图片工具需要独立配置,请从 AI-HIVE 启动器打开");
116
+ }
117
+ const webOriginExplicit = values.has("web-origin") || input.env.AI_HIVE_WEB_ORIGIN !== undefined;
118
+ const config = {
58
119
  command,
59
120
  endpoint: endpointOverride ?? DEFAULT_ENDPOINT,
60
121
  endpointExplicit: endpointOverride !== undefined,
61
122
  webOrigin: values.get("web-origin") ??
62
123
  input.env.AI_HIVE_WEB_ORIGIN ??
63
124
  DEFAULT_WEB_ORIGIN,
125
+ ...(webOriginExplicit ? { webOriginExplicit: true } : {}),
64
126
  key: values.get("key") ?? input.env.AI_HIVE_MCP_KEY ?? null,
65
127
  credentialsFile: values.get("credentials-file") ??
66
128
  resolveWorkBuddyCredentialsFile(input.env),
@@ -68,5 +130,40 @@ export function parseConfig(input) {
68
130
  ? timeoutMs
69
131
  : DEFAULT_TIMEOUT_MS,
70
132
  verbose: flags.has("verbose") || input.env.AI_HIVE_MCP_VERBOSE === "true",
133
+ ...(managedConfig ? { managedConfig } : {}),
71
134
  };
135
+ if (POSITIONAL_COMMANDS.has(command)) {
136
+ config.positionals = positionals;
137
+ config.json = flags.has("json");
138
+ }
139
+ if (command === "setup") {
140
+ const clientValue = values.get("client");
141
+ config.setup = {
142
+ clients: clientValue ? parseClientIds(clientValue) : null,
143
+ dryRun: flags.has("dry-run"),
144
+ skipSkills: flags.has("skip-skills"),
145
+ };
146
+ }
147
+ if (command === "models") {
148
+ const type = values.get("type");
149
+ if (type && !["TEXT", "IMAGE", "VIDEO"].includes(type)) {
150
+ throw new Error("参数无效");
151
+ }
152
+ config.modelType = type;
153
+ const detail = values.get("detail");
154
+ if (detail && !["lite", "full"].includes(detail)) {
155
+ throw new Error("参数无效");
156
+ }
157
+ config.detail = detail;
158
+ }
159
+ if (command === "model") {
160
+ config.modelId = values.get("model") ?? positionals[0];
161
+ config.routingMode = values.get("routing-mode");
162
+ }
163
+ if (command === "run") {
164
+ config.modelId = values.get("model") ?? positionals[0];
165
+ config.prompt = values.get("prompt") ?? positionals.slice(1).join(" ");
166
+ config.routingMode = values.get("routing-mode");
167
+ }
168
+ return config;
72
169
  }
package/dist/index.js CHANGED
@@ -3,7 +3,15 @@ import { runCli } from "./cli.js";
3
3
  import { parseConfig } from "./config.js";
4
4
  function safeTopLevelMessage(error) {
5
5
  if (error instanceof Error &&
6
- ["未知命令", "未知选项", "选项缺少值"].includes(error.message)) {
6
+ [
7
+ "未知命令",
8
+ "未知选项",
9
+ "选项缺少值",
10
+ "未知参数",
11
+ "未知客户端",
12
+ "参数无效",
13
+ "受管图片工具需要独立配置,请从 AI-HIVE 启动器打开",
14
+ ].includes(error.message)) {
7
15
  return error.message;
8
16
  }
9
17
  return "命令执行失败";