ability-cli 0.2.2 → 0.3.1

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 (3) hide show
  1. package/README.md +48 -58
  2. package/dist/index.js +199 -127
  3. package/package.json +53 -53
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## 1. 项目简介
4
4
 
5
- **ability-cli** 是面向原子能力平台的命令行工具,用于在终端中搜索能力、查看详情、执行调用,以及直接访问底层 HTTP 接口。
5
+ **ability-cli** 是面向原子能力平台的命令行工具,用于在终端中搜索能力、查看详情、执行调用,以及直接访问 **Agent API**(路径前缀 `/ability-service/api/agent/v1`)。
6
6
 
7
7
  采用**双层设计**:
8
8
 
@@ -48,20 +48,20 @@ pnpm link --global
48
48
  ## 3. 快速上手
49
49
 
50
50
  ```bash
51
- # 配置(首次使用)
52
- ability-cli config set --app-id <your-app-id> --app-secret <your-secret>
51
+ # 配置授权密钥(sk)与默认设备 ID(也可改用环境变量,见下文)
52
+ ability-cli config set --auth-token sk-xxxx --device-id <device-id>
53
53
 
54
- # 搜索能力
55
- ability-cli search "帮我导航回家" --device-id abc123
54
+ # 搜索能力(请求头:Authorization + zy-device-id,不发网关 zy-sign)
55
+ ability-cli search --natural-lang "发送一条微信消息"
56
56
 
57
- # 查看能力详情(端侧能力需带设备 ID,也可用 config 里的 defaults.deviceId
58
- ability-cli inspect --ability-id 20001 --device-id <device-id>
57
+ # 查看能力详情(端侧能力建议带设备 ID)
58
+ ability-cli inspect --ability-id 20001
59
59
 
60
60
  # 生成参数模板
61
61
  ability-cli inspect --ability-id 20001 --gen-template > params.json
62
62
 
63
- # 执行能力
64
- ability-cli exec --ability-id 20001 --params params.json --device-id abc123
63
+ # 执行能力(对应 POST /ability-service/api/agent/v1/ability/call)
64
+ ability-cli exec --ability-id 20001 --params '{"contact":"张三","message":"你好"}'
65
65
 
66
66
  # 健康检查
67
67
  ability-cli doctor
@@ -73,16 +73,16 @@ ability-cli doctor
73
73
 
74
74
  ### 全局选项
75
75
 
76
- 下列选项可写在任意子命令**之前**,作用于本次整条命令(例如 `ability-cli --env prod search "查询天气"`)。
76
+ 下列选项可写在任意子命令**之前**,作用于本次整条命令(例如 `ability-cli --auth-token sk-xxx search --natural-lang "查询"`)。
77
+
78
+ 同一字段的优先级为:**子命令参数 > 全局参数 > 环境变量 > 配置文件**(合并后的命令行优先于 `ABILITY_CLI_*` 与 `~/.ability-cli/config.json`)。
77
79
 
78
80
  | 选项 | 说明 |
79
81
  |------|------|
80
- | `--env <env>` | 环境:`test` / `prod`,决定使用配置文件中哪一套 `profiles` |
81
- | `--app-id <id>` | `zy-app-id` |
82
- | `--app-secret <secret>` | `appSecret` |
83
- | `--auth-token <token>` | `Authorization` 请求头 |
84
- | `--base-url <url>` | 网关根地址 |
85
- | `--magic-sign` | 测试环境跳过验签(`zy-sign` 固定为测试用魔数) |
82
+ | `--env <env>` | 环境:`test` / `prod`,选用配置文件中哪一套 `profiles`(主要影响 `baseUrl`) |
83
+ | `--auth-token <token>` | 授权密钥 `sk-…`,写入请求头 `Authorization` |
84
+ | `--base-url <url>` | 网关根地址(真实路径为 `{baseUrl}/ability-service/api/agent/v1/...`) |
85
+ | `--device-id <id>` | 设备 ID,写入请求头 `zy-device-id`,并参与部分接口 query |
86
86
  | `-v, --verbose` | 详细输出(请求 URL、头、体与响应等) |
87
87
 
88
88
  ### 上层命令
@@ -113,28 +113,30 @@ ability-cli doctor
113
113
 
114
114
  #### `exec`
115
115
 
116
- 执行能力(异步 `call-tool`)。
116
+ 执行能力(`POST /ability-service/api/agent/v1/ability/call`,同步等待结果)。
117
117
 
118
118
  | 选项 | 说明 |
119
119
  |------|------|
120
120
  | `--ability-id <id>` | **必填**,能力 ID |
121
121
  | `--params <json>` | 参数:JSON 字符串,或指向 JSON 文件的路径 |
122
- | `--device-id <id>` | 设备 ID;默认可来自 `defaults.deviceId` |
122
+ | `--device-id <id>` | 设备 ID;默认可来自全局/环境变量/配置的合并结果 |
123
123
  | `--session-id <id>` | `sessionId` |
124
124
  | `--tool-call-id <id>` | `toolCallId` |
125
125
  | `--task-id <id>` | `taskId` |
126
126
  | `--message-id <id>` | `messageId` |
127
127
  | `--trace-id <id>` | `traceId`;未指定时自动生成 |
128
- | `--timeout <ms>` | 超时(毫秒),默认 `30000` |
128
+ | `--timeout <ms>` | 超时(毫秒);不传则用配置文件 `defaults.timeout`,再默认 `30000` |
129
+ | `--response-type <type>` | `NONE` / `MCP` / `CONVERT`(可选) |
130
+ | `--mock-token <token>` | 模拟调用 token(可选,测试) |
129
131
  | `--json` | 以 JSON 展示返回 `data` |
130
132
 
131
133
  ### 下层命令:`raw`
132
134
 
133
- `raw` 子命令与网关路径一一对应,适合精确控制请求参数。
135
+ `raw` 子命令与 **Agent API** 路径一一对应(前缀 `/ability-service/api/agent/v1`)。
134
136
 
135
137
  #### `raw ability-list`
136
138
 
137
- GET 能力列表。
139
+ GET `/ability-service/api/agent/v1/ability/list`。
138
140
 
139
141
  | 选项 | 说明 |
140
142
  |------|------|
@@ -144,18 +146,9 @@ GET 能力列表。
144
146
  | `--natural-lang <text>` | 自然语言筛选 |
145
147
  | `--json` | JSON 输出 |
146
148
 
147
- #### `raw ability-record`
148
-
149
- POST 能力上报。
150
-
151
- | 选项 | 说明 |
152
- |------|------|
153
- | `--file <path>` | **必填**,请求体 JSON 文件路径 |
154
- | `--json` | JSON 输出 |
155
-
156
149
  #### `raw app-info`
157
150
 
158
- GET 应用信息。
151
+ GET `/ability-service/api/agent/v1/ability/appInfo`。
159
152
 
160
153
  | 选项 | 说明 |
161
154
  |------|------|
@@ -164,7 +157,7 @@ GET 应用信息。
164
157
 
165
158
  #### `raw ability-info`
166
159
 
167
- GET 能力详情。
160
+ GET `/ability-service/api/agent/v1/ability/abilityInfo`。
168
161
 
169
162
  | 选项 | 说明 |
170
163
  |------|------|
@@ -172,9 +165,9 @@ GET 能力详情。
172
165
  | `--device-id <id>` | 设备 ID;**端侧能力**时通常必填;未指定时可使用 `defaults.deviceId` |
173
166
  | `--json` | JSON 输出 |
174
167
 
175
- #### `raw call-tool`
168
+ #### `raw call`
176
169
 
177
- POST 执行能力。
170
+ POST `/ability-service/api/agent/v1/ability/call`(请求体由 `--file` 提供完整 JSON)。
178
171
 
179
172
  | 选项 | 说明 |
180
173
  |------|------|
@@ -183,7 +176,7 @@ POST 执行能力。
183
176
 
184
177
  #### `raw category-list`
185
178
 
186
- GET 全量分类。
179
+ GET `/ability-service/api/agent/v1/ability/category`。
187
180
 
188
181
  | 选项 | 说明 |
189
182
  |------|------|
@@ -198,13 +191,10 @@ GET 全量分类。
198
191
  | 选项 | 说明 |
199
192
  |------|------|
200
193
  | `--env <env>` | 目标环境;影响写入哪一段 `profiles`,并更新当前 `env` |
201
- | `--app-id <id>` | `zy-app-id` |
202
- | `--app-secret <secret>` | `appSecret` |
203
- | `--auth-token <token>` | Authorization token |
194
+ | `--auth-token <token>` | 授权密钥 `sk-…`(写入 `profiles.*.authToken`) |
204
195
  | `--base-url <url>` | 网关地址 |
205
196
  | `--device-id <id>` | 默认设备 ID(`defaults.deviceId`) |
206
- | `--device-model <model>` | 默认设备型号 |
207
- | `--os-version <ver>` | 默认系统版本 |
197
+ | `--timeout <ms>` | 默认调用超时毫秒数(`defaults.timeout`);`exec` 未带 `--timeout` 时使用 |
208
198
 
209
199
  #### `config show`
210
200
 
@@ -216,14 +206,14 @@ GET 全量分类。
216
206
 
217
207
  | 选项 | 说明 |
218
208
  |------|------|
219
- | `--app-id <id>` | 覆盖 `zy-app-id`(默认取自当前上下文) |
220
- | `--app-secret <secret>` | 覆盖密钥 |
209
+ | `--app-id <id>` | 覆盖 `zy-app-id`(或环境变量 `ABILITY_CLI_APP_ID`) |
210
+ | `--app-secret <secret>` | 覆盖密钥(或环境变量 `ABILITY_CLI_APP_SECRET`) |
221
211
  | `--nonce <nonce>` | 固定 nonce |
222
212
  | `--timestamp <ts>` | 固定时间戳 |
223
213
 
224
214
  #### `doctor`
225
215
 
226
- 检查配置文件路径、当前环境、`zy-app-id` / `appSecret` / `baseUrl` 是否已配置,并请求 `{baseUrl}/time` 做网关连通性探测。
216
+ 检查配置文件路径、当前环境、解析后的 **`Authorization(sk)`** / **`zy-device-id`** / **`baseUrl`**,并请求 `{baseUrl}/time` 做网关连通性探测。
227
217
 
228
218
  ---
229
219
 
@@ -233,39 +223,39 @@ GET 全量分类。
233
223
 
234
224
  默认路径:**`~/.ability-cli/config.json`**。
235
225
 
236
- 首次运行若文件不存在,会使用内置默认:当前环境 `test`,`test` / `prod` 各有一套 `baseUrl`(测试/生产网关),`zy-appId`、`appSecret` 等为空;`defaults` 中可存默认设备信息等。
226
+ 首次运行若文件不存在,会使用内置默认:当前环境 **`prod`**,`test` / `prod` 各有一套 `baseUrl`;`profiles.*.authToken`(sk)、`defaults.deviceId` 等为空字符串。
237
227
 
238
- 通过 `ability-cli config set` 写入后,会按 `env` 更新对应 `profiles[env]` 下的 `baseUrl`、`zyAppId`、`appSecret`、`authToken` 等。
228
+ 通过 `ability-cli config set` 写入后,会按 `env` 更新对应 `profiles[env]` 下的 `baseUrl`、`authToken`(sk)等;`defaults` 下可保存默认设备 ID 等。
239
229
 
240
230
  ### 环境变量
241
231
 
242
232
  | 变量 | 作用 |
243
233
  |------|------|
244
234
  | `ABILITY_CLI_ENV` | 选用 `profiles` 的环境键(如 `test` / `prod`) |
245
- | `ABILITY_CLI_APP_ID` | 覆盖 `zy-app-id` |
246
- | `ABILITY_CLI_APP_SECRET` | 覆盖 `appSecret` |
247
- | `ABILITY_CLI_AUTH_TOKEN` | 覆盖 Authorization |
248
- | `ABILITY_CLI_BASE_URL` | 覆盖网关根地址 |
235
+ | `ABILITY_CLI_AUTH_TOKEN` | 授权密钥 `sk-…`,对应请求头 `Authorization` |
236
+ | `ABILITY_CLI_DEVICE_ID` | 设备 ID,对应请求头 `zy-device-id`,并参与部分 query |
237
+ | `ABILITY_CLI_BASE_URL` | 网关根地址 |
238
+ | `ABILITY_CLI_APP_ID` | 仅用于 `sign debug`(网关 zy-sign 调试) |
239
+ | `ABILITY_CLI_APP_SECRET` | 仅用于 `sign debug`(网关 zy-sign 调试) |
249
240
 
250
241
  ### 优先级
251
242
 
252
- **appId / appSecret / authToken / baseUrl** 等请求上下文:**命令行全局选项 > 对应环境变量 > 配置文件当前 profile > 内置默认值**。
243
+ **`authToken` / `deviceId` / `baseUrl`**:**子命令参数 > 全局参数 > 对应环境变量 > 配置文件**(子命令与全局选项先做对象合并,后者覆盖前者)。
253
244
 
254
245
  当前激活的 **profile(test 或 prod)** 由 **`ABILITY_CLI_ENV`(若设置)** 与配置文件中的 **`env`** 字段共同决定(环境变量优先)。
255
246
 
256
247
  ---
257
248
 
258
- ## 6. 签名机制
249
+ ## 6. 鉴权说明(Agent API)
259
250
 
260
- 请求头中的 `zy-sign` 使用 **HMAC-SHA256** 计算:
251
+ 调用 **`/ability-service/api/agent/v1/**`** 时,CLI 仅发送:
261
252
 
262
- 1. 拼接**原串**(字符串直接相连,无分隔符):
263
- `zy-app-id` + `zy-nonce` + `zy-timestamp` + `appSecret`
264
- 2. 以 **`appSecret` 作为 HMAC 密钥**,对原串做 UTF-8 编码后的 HMAC-SHA256,结果取 **hex** 字符串,作为 `zy-sign`。
253
+ - `Authorization`:`sk-…`(由 `--auth-token` / `ABILITY_CLI_AUTH_TOKEN` / 配置文件 `authToken` 解析)
254
+ - `zy-device-id`:设备 ID(由 `--device-id` / `ABILITY_CLI_DEVICE_ID` / `defaults.deviceId` 解析)
265
255
 
266
- 同时会带上 `zy-app-id`、`zy-nonce`、`zy-timestamp`。
256
+ **不发送**网关侧的 `zy-app-id`、`zy-nonce`、`zy-timestamp`、`zy-sign`。
267
257
 
268
- 在**测试环境**下,若使用全局选项 **`--magic-sign`**,则跳过上述计算,`zy-sign` 使用固定测试值(便于跳过网关验签联调)。
258
+ 如需调试历史网关 **HMAC 签名字段**,可使用 `ability-cli sign debug`(凭据为命令行 `--app-id` / `--app-secret` 或环境变量 `ABILITY_CLI_APP_ID` / `ABILITY_CLI_APP_SECRET`,与 Agent API 请求无关)。
269
259
 
270
260
  ---
271
261
 
@@ -284,7 +274,7 @@ GET 全量分类。
284
274
  | 0 | 成功 |
285
275
  | 1 | 参数错误或未捕获的运行时错误 |
286
276
  | 2 | 网络错误(如连接被拒绝、DNS 失败等) |
287
- | 3 | 签名/认证失败 |
277
+ | 3 | 认证失败(保留码位;Agent API 常见错误见服务端 `code`) |
288
278
  | 4 | 业务错误(接口返回 `code !== 200`) |
289
279
 
290
280
  ---
package/dist/index.js CHANGED
@@ -15,22 +15,16 @@ var DEFAULT_CONFIG = {
15
15
  profiles: {
16
16
  test: {
17
17
  baseUrl: "https://biz-gw.zyqltest.com",
18
- zyAppId: "",
19
- appSecret: "",
20
18
  authToken: ""
21
19
  },
22
20
  prod: {
23
21
  baseUrl: "https://biz-gw.zyql.com",
24
- zyAppId: "",
25
- appSecret: "",
26
22
  authToken: ""
27
23
  }
28
24
  },
29
25
  defaults: {
30
26
  deviceId: "",
31
- deviceModel: "",
32
- osVersion: "",
33
- timeout: 30
27
+ timeout: 3e4
34
28
  }
35
29
  };
36
30
  function getDefaultConfigPath() {
@@ -101,7 +95,7 @@ function printAbilityDetail(a) {
101
95
  console.log(` ${chalk.gray("\u5E94\u7528:")} ${a.appName} (${a.packageName})`);
102
96
  console.log(` ${chalk.gray("\u63CF\u8FF0:")} ${a.description}`);
103
97
  console.log(` ${chalk.gray("\u8C03\u7528\u65B9\u5F0F:")} ${a.callingMethod}`);
104
- console.log(` ${chalk.gray("\u8D85\u65F6:")} ${a.timeout}s`);
98
+ console.log(` ${chalk.gray("\u8D85\u65F6:")} ${a.timeout ?? ""} \u6BEB\u79D2`);
105
99
  console.log(` ${chalk.gray("\u6743\u9650:")} ${a.permission}`);
106
100
  console.log(` ${chalk.gray("\u8BBE\u5907\u7C7B\u578B:")} ${a.deviceType}`);
107
101
  if (a.inputSchema) {
@@ -127,24 +121,36 @@ function handleApiResponse(res, jsonMode, formatter) {
127
121
  }
128
122
 
129
123
  // src/commands/config.ts
124
+ function mergeConfigSetOpts(program2, opts) {
125
+ const g = program2.opts();
126
+ return {
127
+ ...g,
128
+ ...opts,
129
+ env: opts.env ?? g.env,
130
+ authToken: opts.authToken ?? g.authToken,
131
+ baseUrl: opts.baseUrl ?? g.baseUrl,
132
+ deviceId: opts.deviceId ?? g.deviceId,
133
+ timeout: opts.timeout
134
+ };
135
+ }
130
136
  function registerConfigCommand(program2) {
131
137
  const cfg = program2.command("config").description("\u7BA1\u7406 CLI \u914D\u7F6E");
132
- cfg.command("set").description("\u8BBE\u7F6E\u914D\u7F6E\u9879").option("--env <env>", "\u73AF\u5883 (test/prod)").option("--app-id <id>", "zy-app-id").option("--app-secret <secret>", "appSecret").option("--auth-token <token>", "Authorization token").option("--base-url <url>", "\u7F51\u5173\u5730\u5740").option("--device-id <id>", "\u9ED8\u8BA4\u8BBE\u5907ID").option("--device-model <model>", "\u9ED8\u8BA4\u8BBE\u5907\u578B\u53F7").option("--os-version <ver>", "\u9ED8\u8BA4\u7CFB\u7EDF\u7248\u672C").action((opts) => {
138
+ cfg.command("set").description("\u8BBE\u7F6E\u914D\u7F6E\u9879").option("--env <env>", "\u73AF\u5883 (test/prod)").option("--auth-token <token>", "Authorization token\uFF08sk\uFF09").option("--base-url <url>", "\u7F51\u5173\u5730\u5740").option("--device-id <id>", "\u9ED8\u8BA4\u8BBE\u5907ID").option("--timeout <ms>", "\u9ED8\u8BA4\u8C03\u7528\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09\uFF0C\u5199\u5165 defaults.timeout").action((opts) => {
139
+ const m = mergeConfigSetOpts(program2, opts);
133
140
  const configPath = getDefaultConfigPath();
134
141
  const config = loadConfig(configPath);
135
- const env = opts.env ?? config.env;
142
+ const env = m.env ?? config.env;
136
143
  config.env = env;
137
144
  if (!config.profiles[env]) {
138
- config.profiles[env] = { baseUrl: "", zyAppId: "", appSecret: "", authToken: "" };
145
+ config.profiles[env] = { baseUrl: "", authToken: "" };
139
146
  }
140
147
  const p = config.profiles[env];
141
- if (opts.appId) p.zyAppId = opts.appId;
142
- if (opts.appSecret) p.appSecret = opts.appSecret;
143
- if (opts.authToken) p.authToken = opts.authToken;
144
- if (opts.baseUrl) p.baseUrl = opts.baseUrl;
145
- if (opts.deviceId) config.defaults.deviceId = opts.deviceId;
146
- if (opts.deviceModel) config.defaults.deviceModel = opts.deviceModel;
147
- if (opts.osVersion) config.defaults.osVersion = opts.osVersion;
148
+ if (m.authToken) p.authToken = m.authToken;
149
+ if (m.baseUrl) p.baseUrl = m.baseUrl;
150
+ if (m.deviceId) config.defaults.deviceId = m.deviceId;
151
+ if (m.timeout !== void 0 && String(m.timeout).trim() !== "") {
152
+ config.defaults.timeout = Number(m.timeout);
153
+ }
148
154
  saveConfig(configPath, config);
149
155
  printSuccess(`\u914D\u7F6E\u5DF2\u4FDD\u5B58\u5230 ${configPath}`);
150
156
  });
@@ -155,38 +161,38 @@ function registerConfigCommand(program2) {
155
161
  }
156
162
 
157
163
  // src/context.ts
158
- function buildContext(opts) {
164
+ function firstNonEmpty(...vals) {
165
+ for (const v of vals) {
166
+ if (v !== void 0 && v !== null && String(v).trim() !== "") return String(v).trim();
167
+ }
168
+ return "";
169
+ }
170
+ function buildAgentRequestContext(merged) {
159
171
  const config = loadConfig();
160
- if (opts.env) config.env = opts.env;
172
+ if (merged.env) config.env = merged.env;
161
173
  const profile = getActiveProfile(config);
162
- profile.zyAppId = resolveOption(opts.appId, process.env.ABILITY_CLI_APP_ID, profile.zyAppId, "");
163
- profile.appSecret = resolveOption(opts.appSecret, process.env.ABILITY_CLI_APP_SECRET, profile.appSecret, "");
164
- profile.authToken = resolveOption(opts.authToken, process.env.ABILITY_CLI_AUTH_TOKEN, profile.authToken, "");
165
- profile.baseUrl = resolveOption(opts.baseUrl, process.env.ABILITY_CLI_BASE_URL, profile.baseUrl, "https://biz-gw.zyql.com");
166
- return {
167
- profile,
168
- signOpts: { magicSign: opts.magicSign },
169
- verbose: opts.verbose
170
- };
171
- }
172
- function getDefaults() {
173
- return loadConfig().defaults;
174
- }
175
-
176
- // src/signer.ts
177
- import { createHmac, randomBytes } from "crypto";
178
- function sign(message, secret) {
179
- return createHmac("sha256", secret).update(message, "utf8").digest("hex");
180
- }
181
- function buildSignHeaders(appId, appSecret, opts = {}) {
182
- const nonce = opts.nonce ?? randomBytes(8).toString("hex");
183
- const timestamp = opts.timestamp ?? Date.now().toString();
184
- const zySign = opts.magicSign ? "123456" : sign(`${appId}${nonce}${timestamp}${appSecret}`, appSecret);
174
+ const defaults = config.defaults;
175
+ const authToken = firstNonEmpty(
176
+ merged.authToken,
177
+ process.env.ABILITY_CLI_AUTH_TOKEN,
178
+ profile.authToken
179
+ );
180
+ const deviceId = firstNonEmpty(
181
+ merged.deviceId,
182
+ process.env.ABILITY_CLI_DEVICE_ID,
183
+ defaults.deviceId
184
+ );
185
+ const baseUrl = resolveOption(
186
+ merged.baseUrl,
187
+ process.env.ABILITY_CLI_BASE_URL,
188
+ profile.baseUrl,
189
+ "https://biz-gw.zyql.com"
190
+ );
185
191
  return {
186
- "zy-app-id": appId,
187
- "zy-nonce": nonce,
188
- "zy-timestamp": timestamp,
189
- "zy-sign": zySign
192
+ baseUrl,
193
+ authToken,
194
+ deviceId,
195
+ verbose: merged.verbose
190
196
  };
191
197
  }
192
198
 
@@ -200,15 +206,23 @@ function buildUrl(base, path2, params) {
200
206
  }
201
207
  return url.toString();
202
208
  }
203
- function buildRequestHeaders(signHeaders, authToken) {
204
- const headers = { ...signHeaders };
205
- if (authToken) headers["Authorization"] = authToken;
209
+ function normalizeAuthorization(token) {
210
+ const t = token.trim();
211
+ return t;
212
+ }
213
+ function buildAgentHeaders(authToken, deviceId) {
214
+ const headers = {};
215
+ if (authToken) {
216
+ headers["Authorization"] = normalizeAuthorization(authToken);
217
+ }
218
+ if (deviceId) {
219
+ headers["zy-device-id"] = deviceId;
220
+ }
206
221
  return headers;
207
222
  }
208
223
  async function apiGet(ctx, path2, params) {
209
- const signHeaders = buildSignHeaders(ctx.profile.zyAppId, ctx.profile.appSecret, ctx.signOpts);
210
- const headers = buildRequestHeaders(signHeaders, ctx.profile.authToken);
211
- const url = buildUrl(ctx.profile.baseUrl, path2, params);
224
+ const headers = buildAgentHeaders(ctx.authToken, ctx.deviceId);
225
+ const url = buildUrl(ctx.baseUrl, path2, params);
212
226
  if (ctx.verbose) {
213
227
  console.error(`[verbose] GET ${url}`);
214
228
  console.error(`[verbose] Headers: ${JSON.stringify(headers, null, 2)}`);
@@ -219,10 +233,9 @@ async function apiGet(ctx, path2, params) {
219
233
  return body;
220
234
  }
221
235
  async function apiPost(ctx, path2, data) {
222
- const signHeaders = buildSignHeaders(ctx.profile.zyAppId, ctx.profile.appSecret, ctx.signOpts);
223
- const headers = buildRequestHeaders(signHeaders, ctx.profile.authToken);
236
+ const headers = buildAgentHeaders(ctx.authToken, ctx.deviceId);
224
237
  headers["Content-Type"] = "application/json";
225
- const url = buildUrl(ctx.profile.baseUrl, path2);
238
+ const url = buildUrl(ctx.baseUrl, path2);
226
239
  if (ctx.verbose) {
227
240
  console.error(`[verbose] POST ${url}`);
228
241
  console.error(`[verbose] Headers: ${JSON.stringify(headers, null, 2)}`);
@@ -240,53 +253,65 @@ async function apiPost(ctx, path2, data) {
240
253
 
241
254
  // src/commands/raw.ts
242
255
  import fs2 from "fs";
256
+
257
+ // src/agent-paths.ts
258
+ var AGENT_API_PREFIX = "/ability-service/api/agent/v1";
259
+ var agentPath = {
260
+ abilityList: `${AGENT_API_PREFIX}/ability/list`,
261
+ abilityCategory: `${AGENT_API_PREFIX}/ability/category`,
262
+ appInfo: `${AGENT_API_PREFIX}/ability/appInfo`,
263
+ abilityInfo: `${AGENT_API_PREFIX}/ability/abilityInfo`,
264
+ abilityCall: `${AGENT_API_PREFIX}/ability/call`
265
+ };
266
+
267
+ // src/commands/raw.ts
243
268
  function registerRawCommand(program2) {
244
269
  const raw = program2.command("raw").description("\u539F\u59CB\u63A5\u53E3\u76F4\u901A\uFF08\u8054\u8C03/\u6392\u969C\uFF09");
245
270
  raw.command("ability-list").description("GET \u80FD\u529B\u5217\u8868").option("--device-id <id>", "\u8BBE\u5907ID").option("--app-name <name>", "\u5E94\u7528\u540D\u79F0").option("--category <category>", "\u4E09\u7EA7\u5206\u7C7B").option("--natural-lang <text>", "\u81EA\u7136\u8BED\u8A00\u7B5B\u9009").option("--json", "JSON \u8F93\u51FA").action(async (opts) => {
246
- const ctx = buildContext(program2.opts());
271
+ const merged = { ...program2.opts(), ...opts };
272
+ const ctx = buildAgentRequestContext(merged);
247
273
  const params = {};
248
- if (opts.deviceId) params.deviceId = opts.deviceId;
274
+ if (ctx.deviceId) params.deviceId = ctx.deviceId;
249
275
  if (opts.appName) params.appName = opts.appName;
250
276
  if (opts.category) params.category = opts.category;
251
277
  if (opts.naturalLang) params.naturalLang = opts.naturalLang;
252
- const res = await apiGet(ctx, "/ability-service/api/v1/ability/list", params);
278
+ const res = await apiGet(ctx, agentPath.abilityList, params);
253
279
  handleApiResponse(res, opts.json, (data) => printAbilityTable(data));
254
280
  });
255
- raw.command("ability-record").description("POST \u80FD\u529B\u4E0A\u62A5").requiredOption("--file <path>", "JSON \u6587\u4EF6\u8DEF\u5F84").option("--json", "JSON \u8F93\u51FA").action(async (opts) => {
256
- const ctx = buildContext(program2.opts());
257
- const data = JSON.parse(fs2.readFileSync(opts.file, "utf-8"));
258
- const res = await apiPost(ctx, "/ability-service/api/v1/ability/record", data);
259
- handleApiResponse(res, opts.json, () => printSuccess("\u80FD\u529B\u4E0A\u62A5\u6210\u529F"));
260
- });
261
- raw.command("app-info").description("GET \u5E94\u7528\u4FE1\u606F").requiredOption("--package-name <pkg>", "\u5E94\u7528\u5305\u540D").option("--json", "JSON \u8F93\u51FA").action(async (opts) => {
262
- const ctx = buildContext(program2.opts());
263
- const res = await apiGet(ctx, "/ability-service/api/v1/ability/appInfo", { packageName: opts.packageName });
281
+ raw.command("app-info").description("GET \u5E94\u7528\u4FE1\u606F").addHelpText(
282
+ "after",
283
+ "\n\u63D0\u793A\uFF1A\u9009\u9879\u8BF4\u660E\u4E2D\u4EE5\u300C\u3010\u5FC5\u586B\u3011\u300D\u5F00\u5934\u7684\u5FC5\u987B\u63D0\u4F9B\uFF0C\u5176\u4F59\u5747\u4E3A\u53EF\u9009\u3002\n"
284
+ ).requiredOption("--package-name <pkg>", "\u3010\u5FC5\u586B\u3011\u5E94\u7528\u5305\u540D").option("--json", "JSON \u8F93\u51FA").action(async (opts) => {
285
+ const merged = { ...program2.opts(), ...opts };
286
+ const ctx = buildAgentRequestContext(merged);
287
+ const res = await apiGet(ctx, agentPath.appInfo, { packageName: opts.packageName });
264
288
  handleApiResponse(res, opts.json, (data) => printJson(data));
265
289
  });
266
- raw.command("ability-info").description("GET \u80FD\u529B\u8BE6\u60C5").requiredOption("--ability-id <id>", "\u80FD\u529BID").option("--device-id <id>", "\u8BBE\u5907 ID\uFF1B\u5F53\u80FD\u529B\u4E3A\u7AEF\u4FA7\u80FD\u529B\u65F6\u5FC5\u586B").option("--json", "JSON \u8F93\u51FA").action(async (opts) => {
267
- const ctx = buildContext(program2.opts());
268
- const defaults = getDefaults();
290
+ raw.command("ability-info").description("GET \u80FD\u529B\u8BE6\u60C5").addHelpText(
291
+ "after",
292
+ "\n\u63D0\u793A\uFF1A\u9009\u9879\u8BF4\u660E\u4E2D\u4EE5\u300C\u3010\u5FC5\u586B\u3011\u300D\u5F00\u5934\u7684\u5FC5\u987B\u63D0\u4F9B\uFF0C\u5176\u4F59\u5747\u4E3A\u53EF\u9009\u3002\n"
293
+ ).requiredOption("--ability-id <id>", "\u3010\u5FC5\u586B\u3011\u80FD\u529B ID").option("--device-id <id>", "\u8BBE\u5907 ID\uFF1B\u7AEF\u4FA7\u80FD\u529B\u5EFA\u8BAE\u586B\u5199").option("--json", "JSON \u8F93\u51FA").action(async (opts) => {
294
+ const merged = { ...program2.opts(), ...opts };
295
+ const ctx = buildAgentRequestContext(merged);
269
296
  const params = { abilityId: opts.abilityId };
270
- const deviceId = opts.deviceId ?? defaults.deviceId;
271
- if (deviceId) params.deviceId = deviceId;
272
- const res = await apiGet(ctx, "/ability-service/api/v1/ability/abilityInfo", params);
297
+ if (ctx.deviceId) params.deviceId = ctx.deviceId;
298
+ const res = await apiGet(ctx, agentPath.abilityInfo, params);
273
299
  handleApiResponse(res, opts.json, (data) => printJson(data));
274
300
  });
275
- raw.command("call-tool").description("POST \u6267\u884C\u80FD\u529B").requiredOption("--file <path>", "JSON \u6587\u4EF6\u8DEF\u5F84").option("--json", "JSON \u8F93\u51FA").action(async (opts) => {
276
- const ctx = buildContext(program2.opts());
277
- const data = JSON.parse(fs2.readFileSync(opts.file, "utf-8"));
278
- const res = await apiPost(ctx, "/ability-service/api/mcp/async/call-tool", data);
279
- handleApiResponse(res, opts.json, (data2) => printJson(data2));
280
- });
281
- raw.command("ability-callback").description("POST \u4E0A\u62A5\u80FD\u529B\u6267\u884C\u7ED3\u679C").requiredOption("--file <path>", "JSON \u6587\u4EF6\u8DEF\u5F84\uFF0C\u9700\u5305\u542B requestId\uFF0Cbody \u53EF\u9009").option("--json", "JSON \u8F93\u51FA").action(async (opts) => {
282
- const ctx = buildContext(program2.opts());
301
+ raw.command("call").description("POST \u6267\u884C\u80FD\u529B\uFF08Agent API\uFF09").addHelpText(
302
+ "after",
303
+ "\n\u63D0\u793A\uFF1A\u9009\u9879\u8BF4\u660E\u4E2D\u4EE5\u300C\u3010\u5FC5\u586B\u3011\u300D\u5F00\u5934\u7684\u5FC5\u987B\u63D0\u4F9B\uFF0C\u5176\u4F59\u5747\u4E3A\u53EF\u9009\u3002\n"
304
+ ).requiredOption("--file <path>", "\u3010\u5FC5\u586B\u3011\u8BF7\u6C42\u4F53 JSON \u6587\u4EF6\u8DEF\u5F84").option("--json", "JSON \u8F93\u51FA").action(async (opts) => {
305
+ const merged = { ...program2.opts(), ...opts };
306
+ const ctx = buildAgentRequestContext(merged);
283
307
  const data = JSON.parse(fs2.readFileSync(opts.file, "utf-8"));
284
- const res = await apiPost(ctx, "/ability-service/api/v1/ability/callback", data);
308
+ const res = await apiPost(ctx, agentPath.abilityCall, data);
285
309
  handleApiResponse(res, opts.json, (data2) => printJson(data2));
286
310
  });
287
311
  raw.command("category-list").description("GET \u5168\u91CF\u5206\u7C7B").option("--json", "JSON \u8F93\u51FA").action(async (opts) => {
288
- const ctx = buildContext(program2.opts());
289
- const res = await apiGet(ctx, "/ability-service/api/v1/ability/category");
312
+ const merged = { ...program2.opts(), ...opts };
313
+ const ctx = buildAgentRequestContext(merged);
314
+ const res = await apiGet(ctx, agentPath.abilityCategory);
290
315
  handleApiResponse(res, opts.json, (data) => printJson(data));
291
316
  });
292
317
  }
@@ -294,16 +319,15 @@ function registerRawCommand(program2) {
294
319
  // src/commands/search.ts
295
320
  function registerSearchCommand(program2) {
296
321
  program2.command("search [query]").description("\u641C\u7D22\u80FD\u529B\uFF08\u652F\u6301\u81EA\u7136\u8BED\u8A00\uFF09").option("--device-id <id>", "\u8BBE\u5907ID").option("--app-name <name>", "\u5E94\u7528\u540D\u79F0").option("--category <category>", "\u4E09\u7EA7\u5206\u7C7B").option("--natural-lang <text>", "\u81EA\u7136\u8BED\u8A00\uFF08\u4F18\u5148\u4E8E query\uFF09").option("--json", "JSON \u8F93\u51FA").action(async (query, opts) => {
297
- const ctx = buildContext(program2.opts());
298
- const defaults = getDefaults();
322
+ const merged = { ...program2.opts(), ...opts };
323
+ const ctx = buildAgentRequestContext(merged);
299
324
  const params = {};
300
- const deviceId = opts.deviceId ?? defaults.deviceId;
301
- if (deviceId) params.deviceId = deviceId;
325
+ if (ctx.deviceId) params.deviceId = ctx.deviceId;
302
326
  const naturalLang = opts.naturalLang ?? query;
303
327
  if (naturalLang) params.naturalLang = naturalLang;
304
328
  if (opts.appName) params.appName = opts.appName;
305
329
  if (opts.category) params.category = opts.category;
306
- const res = await apiGet(ctx, "/ability-service/api/v1/ability/list", params);
330
+ const res = await apiGet(ctx, agentPath.abilityList, params);
307
331
  handleApiResponse(res, opts.json, (data) => {
308
332
  const list = Array.isArray(data) ? data : [];
309
333
  if (list.length === 0) {
@@ -343,13 +367,15 @@ function generateTemplate(schema) {
343
367
  return result;
344
368
  }
345
369
  function registerInspectCommand(program2) {
346
- program2.command("inspect").description("\u67E5\u770B\u80FD\u529B\u8BE6\u60C5\u4E0E inputSchema").requiredOption("--ability-id <id>", "\u80FD\u529BID").option("--device-id <id>", "\u8BBE\u5907 ID\uFF1B\u5F53\u80FD\u529B\u4E3A\u7AEF\u4FA7\u80FD\u529B\u65F6\u5FC5\u586B").option("--gen-template", "\u751F\u6210\u53C2\u6570\u6A21\u677F JSON").option("--json", "JSON \u8F93\u51FA").action(async (opts) => {
347
- const ctx = buildContext(program2.opts());
348
- const defaults = getDefaults();
370
+ program2.command("inspect").description("\u67E5\u770B\u80FD\u529B\u8BE6\u60C5\u4E0E inputSchema").addHelpText(
371
+ "after",
372
+ "\n\u63D0\u793A\uFF1A\u9009\u9879\u8BF4\u660E\u4E2D\u4EE5\u300C\u3010\u5FC5\u586B\u3011\u300D\u5F00\u5934\u7684\u5FC5\u987B\u63D0\u4F9B\uFF0C\u5176\u4F59\u5747\u4E3A\u53EF\u9009\u3002\n"
373
+ ).requiredOption("--ability-id <id>", "\u3010\u5FC5\u586B\u3011\u80FD\u529B ID").option("--device-id <id>", "\u8BBE\u5907 ID\uFF1B\u7AEF\u4FA7\u80FD\u529B\u5EFA\u8BAE\u586B\u5199").option("--gen-template", "\u751F\u6210\u53C2\u6570\u6A21\u677F JSON").option("--json", "JSON \u8F93\u51FA").action(async (opts) => {
374
+ const merged = { ...program2.opts(), ...opts };
375
+ const ctx = buildAgentRequestContext(merged);
349
376
  const params = { abilityId: opts.abilityId };
350
- const deviceId = opts.deviceId ?? defaults.deviceId;
351
- if (deviceId) params.deviceId = deviceId;
352
- const res = await apiGet(ctx, "/ability-service/api/v1/ability/abilityInfo", params);
377
+ if (ctx.deviceId) params.deviceId = ctx.deviceId;
378
+ const res = await apiGet(ctx, agentPath.abilityInfo, params);
353
379
  handleApiResponse(res, opts.json, (data) => {
354
380
  if (opts.genTemplate && data.inputSchema) {
355
381
  printJson(generateTemplate(data.inputSchema));
@@ -363,16 +389,44 @@ function registerInspectCommand(program2) {
363
389
  // src/commands/exec.ts
364
390
  import { randomUUID } from "crypto";
365
391
  import fs3 from "fs";
392
+
393
+ // src/parse-cli-json.ts
394
+ function parseCliJsonString(raw, label) {
395
+ const s = raw.replace(/^\uFEFF/, "").trim();
396
+ if (!s) return {};
397
+ try {
398
+ return JSON.parse(s);
399
+ } catch (e) {
400
+ const msg = e instanceof Error ? e.message : String(e);
401
+ let hint = "";
402
+ if (/^\s*\{\s*'/.test(s)) {
403
+ hint = " JSON \u952E\u540D\u987B\u7528\u82F1\u6587\u53CC\u5F15\u53F7\uFF1B\u5F53\u524D\u5185\u5BB9\u5728 { \u540E\u51FA\u73B0\u4E86\u5355\u5F15\u53F7\u3002\u6700\u7A33\u59A5\uFF1A\u5C06 JSON \u5B58\u4E3A params.json\uFF08UTF-8\uFF09\uFF0C\u518D\u4F7F\u7528 --params params.json\u3002";
404
+ } else if (/[\u201c\u201d\u2018\u2019]/.test(s)) {
405
+ hint = ' \u68C0\u6D4B\u5230\u5F2F\u5F15\u53F7\uFF08\u201C \u201D \u2018 \u2019\uFF09\u3002\u8BF7\u6539\u4E3A ASCII \u53CC\u5F15\u53F7 " \u3002';
406
+ }
407
+ const preview = s.length > 160 ? `${s.slice(0, 160)}\u2026` : s;
408
+ throw new Error(`${label} \u89E3\u6790\u5931\u8D25\uFF1A${msg}.${hint}
409
+ \u5B9E\u9645\u6536\u5230\u7684\u5F00\u5934\uFF1A${JSON.stringify(preview.slice(0, 80))}`);
410
+ }
411
+ }
412
+
413
+ // src/commands/exec.ts
366
414
  function registerExecCommand(program2) {
367
- program2.command("exec").description("\u6267\u884C\u80FD\u529B").requiredOption("--ability-id <id>", "\u80FD\u529BID").option("--params <json>", "\u53C2\u6570 JSON \u5B57\u7B26\u4E32\u6216\u6587\u4EF6\u8DEF\u5F84").option("--device-id <id>", "\u8BBE\u5907ID").option("--session-id <id>", "sessionId").option("--tool-call-id <id>", "toolCallId").option("--task-id <id>", "taskId").option("--message-id <id>", "messageId").option("--trace-id <id>", "traceId").option("--timeout <ms>", "\u8D85\u65F6\u65F6\u95F4(\u6BEB\u79D2)", "30000").option("--json", "JSON \u8F93\u51FA").action(async (opts) => {
368
- const ctx = buildContext(program2.opts());
369
- const defaults = getDefaults();
415
+ program2.command("exec").description("\u6267\u884C\u80FD\u529B").addHelpText(
416
+ "after",
417
+ "\n\u63D0\u793A\uFF1A\u9009\u9879\u8BF4\u660E\u4E2D\u4EE5\u300C\u3010\u5FC5\u586B\u3011\u300D\u5F00\u5934\u7684\u5FC5\u987B\u63D0\u4F9B\uFF0C\u5176\u4F59\u5747\u4E3A\u53EF\u9009\u3002\n"
418
+ ).requiredOption("--ability-id <id>", "\u3010\u5FC5\u586B\u3011\u80FD\u529B ID").option("--params <json>", "\u53C2\u6570 JSON \u5B57\u7B26\u4E32\u6216\u6587\u4EF6\u8DEF\u5F84").option("--device-id <id>", "\u8BBE\u5907ID").option("--message-id <id>", "messageId").option("--trace-id <id>", "traceId").option("--timeout <ms>", "\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09\uFF1B\u4E0D\u4F20\u5219\u7528\u914D\u7F6E\u6587\u4EF6 defaults.timeout\uFF0C\u9ED8\u8BA4 30000").option("--response-type <type>", "\u8FD4\u56DE\u503C\u5904\u7406\u65B9\u5F0F: NONE | MCP | CONVERT").option("--json", "JSON \u8F93\u51FA").action(async (opts) => {
419
+ const merged = { ...program2.opts(), ...opts };
420
+ const ctx = buildAgentRequestContext(merged);
421
+ const cliCfg = loadConfig();
422
+ const timeoutMillis = opts.timeout !== void 0 && String(opts.timeout).trim() !== "" ? Number(opts.timeout) : cliCfg.defaults.timeout ?? 3e4;
370
423
  let params = {};
371
424
  if (opts.params) {
372
425
  if (fs3.existsSync(opts.params)) {
373
- params = JSON.parse(fs3.readFileSync(opts.params, "utf-8"));
426
+ const text = fs3.readFileSync(opts.params, "utf-8");
427
+ params = parseCliJsonString(text, "\u53C2\u6570\u6587\u4EF6");
374
428
  } else {
375
- params = JSON.parse(opts.params);
429
+ params = parseCliJsonString(opts.params, "--params");
376
430
  }
377
431
  }
378
432
  const body = {
@@ -382,11 +436,13 @@ function registerExecCommand(program2) {
382
436
  toolCallId: opts.toolCallId ?? "",
383
437
  taskId: opts.taskId ?? "",
384
438
  messageId: opts.messageId ?? "",
385
- deviceId: opts.deviceId ?? defaults.deviceId ?? "",
386
- timeoutMillis: Number(opts.timeout),
439
+ deviceId: ctx.deviceId ?? "",
440
+ timeoutMillis,
387
441
  params
388
442
  };
389
- const res = await apiPost(ctx, "/ability-service/api/mcp/async/call-tool", body);
443
+ if (opts.responseType) body.responseType = opts.responseType;
444
+ if (opts.mockToken) body.mockToken = opts.mockToken;
445
+ const res = await apiPost(ctx, agentPath.abilityCall, body);
390
446
  handleApiResponse(res, opts.json, (data) => {
391
447
  printSuccess("\u80FD\u529B\u6267\u884C\u5B8C\u6210");
392
448
  printJson(data);
@@ -396,18 +452,36 @@ function registerExecCommand(program2) {
396
452
 
397
453
  // src/commands/sign.ts
398
454
  import chalk2 from "chalk";
455
+
456
+ // src/signer.ts
457
+ import { createHmac, randomBytes } from "crypto";
458
+ function sign(message, secret) {
459
+ return createHmac("sha256", secret).update(message, "utf8").digest("hex");
460
+ }
461
+ function buildSignHeaders(appId, appSecret, opts = {}) {
462
+ const nonce = opts.nonce ?? randomBytes(8).toString("hex");
463
+ const timestamp = opts.timestamp ?? Date.now().toString();
464
+ const zySign = opts.magicSign ? "123456" : sign(`${appId}${nonce}${timestamp}${appSecret}`, appSecret);
465
+ return {
466
+ "zy-app-id": appId,
467
+ "zy-nonce": nonce,
468
+ "zy-timestamp": timestamp,
469
+ "zy-sign": zySign
470
+ };
471
+ }
472
+
473
+ // src/commands/sign.ts
399
474
  function registerSignCommand(program2) {
400
- const signCmd = program2.command("sign").description("\u7B7E\u540D\u5DE5\u5177");
401
- signCmd.command("debug").description("\u8C03\u8BD5\u7B7E\u540D\uFF08\u6253\u5370\u539F\u4E32\u548C\u7ED3\u679C\uFF09").option("--app-id <id>", "zy-app-id").option("--app-secret <secret>", "appSecret").option("--nonce <nonce>", "\u6307\u5B9A nonce").option("--timestamp <ts>", "\u6307\u5B9A timestamp").action((opts) => {
402
- const ctx = buildContext(program2.opts());
403
- const appId = opts.appId ?? ctx.profile.zyAppId;
404
- const secret = opts.appSecret ?? ctx.profile.appSecret;
475
+ const signCmd = program2.command("sign").description("\u7B7E\u540D\u5DE5\u5177\uFF08\u7F51\u5173 zy-sign \u8C03\u8BD5\uFF0C\u975E Agent API \u8BF7\u6C42\uFF09");
476
+ signCmd.command("debug").description("\u8C03\u8BD5\u7F51\u5173\u7B7E\u540D\uFF08\u6253\u5370\u539F\u4E32\u548C zy-sign\uFF09\uFF1B\u51ED\u636E\u6765\u81EA\u53C2\u6570\u6216\u73AF\u5883\u53D8\u91CF").option("--app-id <id>", "zy-app-id").option("--app-secret <secret>", "appSecret").option("--nonce <nonce>", "\u6307\u5B9A nonce").option("--timestamp <ts>", "\u6307\u5B9A timestamp").action((opts) => {
477
+ const appId = opts.appId ?? process.env.ABILITY_CLI_APP_ID ?? "";
478
+ const secret = opts.appSecret ?? process.env.ABILITY_CLI_APP_SECRET ?? "";
405
479
  const headers = buildSignHeaders(appId, secret, {
406
480
  nonce: opts.nonce,
407
481
  timestamp: opts.timestamp
408
482
  });
409
483
  const rawStr = `${appId}${headers["zy-nonce"]}${headers["zy-timestamp"]}${secret}`;
410
- console.log(chalk2.bold.cyan("\n\u2500\u2500 \u7B7E\u540D\u8C03\u8BD5 \u2500\u2500\n"));
484
+ console.log(chalk2.bold.cyan("\n\u2500\u2500 \u7F51\u5173\u7B7E\u540D\u8C03\u8BD5 \u2500\u2500\n"));
411
485
  console.log(` ${chalk2.gray("appId:")} ${appId}`);
412
486
  console.log(` ${chalk2.gray("nonce:")} ${headers["zy-nonce"]}`);
413
487
  console.log(` ${chalk2.gray("timestamp:")} ${headers["zy-timestamp"]}`);
@@ -420,16 +494,19 @@ function registerSignCommand(program2) {
420
494
  import chalk3 from "chalk";
421
495
  function registerDoctorCommand(program2) {
422
496
  program2.command("doctor").description("\u68C0\u67E5\u914D\u7F6E\u5B8C\u6574\u6027\u548C\u7F51\u5173\u8FDE\u901A\u6027").action(async () => {
423
- const config = loadConfig();
424
- const profile = getActiveProfile(config);
497
+ const merged = program2.opts();
498
+ const ctx = buildAgentRequestContext(merged);
425
499
  let ok = true;
426
500
  console.log(chalk3.bold.cyan("\n\u2500\u2500 CLI \u5065\u5EB7\u68C0\u67E5 \u2500\u2500\n"));
501
+ const configPath = getDefaultConfigPath();
502
+ const config = loadConfig();
503
+ const effectiveEnv = process.env.ABILITY_CLI_ENV ?? merged.env ?? config.env;
427
504
  const checks = [
428
- ["\u914D\u7F6E\u6587\u4EF6", getDefaultConfigPath(), true],
429
- ["\u5F53\u524D\u73AF\u5883", config.env, true],
430
- ["zy-app-id", profile.zyAppId || "(\u7A7A)", !!profile.zyAppId],
431
- ["appSecret", profile.appSecret ? "******" : "(\u7A7A)", !!profile.appSecret],
432
- ["baseUrl", profile.baseUrl || "(\u7A7A)", !!profile.baseUrl]
505
+ ["\u914D\u7F6E\u6587\u4EF6", configPath, true],
506
+ ["\u5F53\u524D\u73AF\u5883", effectiveEnv, true],
507
+ ["baseUrl", ctx.baseUrl || "(\u7A7A)", !!ctx.baseUrl],
508
+ ["Authorization(sk)", ctx.authToken ? "******" : "(\u7A7A)", !!ctx.authToken],
509
+ ["zy-device-id", ctx.deviceId || "(\u7A7A)", !!ctx.deviceId]
433
510
  ];
434
511
  for (const [label, val, pass] of checks) {
435
512
  const icon = pass ? chalk3.green("\u2714") : chalk3.red("\u2716");
@@ -437,21 +514,16 @@ function registerDoctorCommand(program2) {
437
514
  if (!pass) ok = false;
438
515
  }
439
516
  try {
440
- const res = await fetch(`${profile.baseUrl}/time`);
517
+ const res = await fetch(`${ctx.baseUrl}/time`);
441
518
  const body = await res.json();
442
519
  if (body.code === 200) {
443
- console.log(` ${chalk3.green("\u2714")} ${chalk3.gray("\u7F51\u5173\u8FDE\u901A:")} ${body.data}`);
444
- const serverTs = Number(String(body.data).slice(0, -1) + "0");
445
- const drift = Math.abs(Date.now() - serverTs);
446
- if (drift > 3e4) {
447
- console.log(` ${chalk3.yellow("\u26A0")} ${chalk3.gray("\u65F6\u949F\u504F\u5DEE:")} ${drift}ms\uFF08\u5EFA\u8BAE\u4F7F\u7528 --sync-time\uFF09`);
448
- }
520
+ console.log(` ${chalk3.green("\u2714")} ${chalk3.gray("\u7F51\u5173\u8FDE\u901A(/time):")} ${body.data}`);
449
521
  } else {
450
- console.log(` ${chalk3.red("\u2716")} ${chalk3.gray("\u7F51\u5173\u8FDE\u901A:")} \u8FD4\u56DE code=${body.code}`);
522
+ console.log(` ${chalk3.red("\u2716")} ${chalk3.gray("\u7F51\u5173\u8FDE\u901A(/time):")} \u8FD4\u56DE code=${body.code}`);
451
523
  ok = false;
452
524
  }
453
525
  } catch (e) {
454
- console.log(` ${chalk3.red("\u2716")} ${chalk3.gray("\u7F51\u5173\u8FDE\u901A:")} ${e.message}`);
526
+ console.log(` ${chalk3.red("\u2716")} ${chalk3.gray("\u7F51\u5173\u8FDE\u901A(/time):")} ${e.message}`);
455
527
  ok = false;
456
528
  }
457
529
  console.log();
@@ -471,7 +543,7 @@ function readPackageVersion() {
471
543
  const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
472
544
  return pkg.version ?? "0.0.0";
473
545
  }
474
- program.name("ability-cli").description("\u539F\u5B50\u80FD\u529B\u5E73\u53F0 CLI \u5DE5\u5177").version(readPackageVersion()).option("--env <env>", "\u73AF\u5883 (test/prod)").option("--app-id <id>", "zy-app-id").option("--app-secret <secret>", "appSecret").option("--auth-token <token>", "Authorization").option("--base-url <url>", "\u7F51\u5173\u5730\u5740").option("--magic-sign", "\u6D4B\u8BD5\u73AF\u5883\u8DF3\u8FC7\u9A8C\u7B7E").option("-v, --verbose", "\u8BE6\u7EC6\u8F93\u51FA");
546
+ program.name("ability-cli").description("\u539F\u5B50\u80FD\u529B\u5E73\u53F0 CLI \u5DE5\u5177").version(readPackageVersion()).option("--env <env>", "\u73AF\u5883 (test/prod)\uFF0C\u9009\u62E9 profiles \u4E2D\u54EA\u5957 baseUrl").option("--auth-token <token>", "Authorization\uFF08sk-\u2026\uFF09\uFF0C\u8986\u76D6\u73AF\u5883\u53D8\u91CF\u4E0E\u914D\u7F6E").option("--base-url <url>", "\u7F51\u5173\u6839\u5730\u5740\uFF0C\u8986\u76D6\u73AF\u5883\u53D8\u91CF\u4E0E\u914D\u7F6E").option("--device-id <id>", "\u8BBE\u5907 ID\uFF08zy-device-id \u8BF7\u6C42\u5934 / \u90E8\u5206 query\uFF09\uFF0C\u8986\u76D6\u73AF\u5883\u53D8\u91CF\u4E0E\u914D\u7F6E").option("-v, --verbose", "\u8BE6\u7EC6\u8F93\u51FA");
475
547
  registerConfigCommand(program);
476
548
  registerRawCommand(program);
477
549
  registerSearchCommand(program);
package/package.json CHANGED
@@ -1,53 +1,53 @@
1
- {
2
- "name": "ability-cli",
3
- "version": "0.2.2",
4
- "description": "原子能力平台 CLI 工具",
5
- "repository": {
6
- "type": "git",
7
- "url": "<repo-url>"
8
- },
9
- "homepage": "<repo-url>",
10
- "bugs": {
11
- "url": "<repo-url>/issues"
12
- },
13
- "type": "module",
14
- "bin": {
15
- "ability-cli": "./dist/index.js"
16
- },
17
- "files": [
18
- "dist",
19
- "README.md",
20
- "LICENSE"
21
- ],
22
- "scripts": {
23
- "build": "tsup src/index.ts --format esm --dts --clean",
24
- "dev": "tsup src/index.ts --format esm --watch",
25
- "test": "vitest run",
26
- "test:watch": "vitest",
27
- "prepublishOnly": "npm run build && npm test"
28
- },
29
- "keywords": [
30
- "ability",
31
- "cli",
32
- "atomic-ability",
33
- "mcp",
34
- "zyql"
35
- ],
36
- "author": "",
37
- "license": "ISC",
38
- "packageManager": "pnpm@10.28.2",
39
- "dependencies": {
40
- "chalk": "^5.6.2",
41
- "cli-table3": "^0.6.5",
42
- "commander": "^14.0.3"
43
- },
44
- "pnpm": {
45
- "onlyBuiltDependencies": ["esbuild"]
46
- },
47
- "devDependencies": {
48
- "@types/node": "^25.5.2",
49
- "tsup": "^8.5.1",
50
- "typescript": "^6.0.2",
51
- "vitest": "^4.1.3"
52
- }
53
- }
1
+ {
2
+ "name": "ability-cli",
3
+ "version": "0.3.1",
4
+ "description": "原子能力平台 CLI 工具",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "<repo-url>"
8
+ },
9
+ "homepage": "<repo-url>",
10
+ "bugs": {
11
+ "url": "<repo-url>/issues"
12
+ },
13
+ "type": "module",
14
+ "bin": {
15
+ "ability-cli": "./dist/index.js"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsup src/index.ts --format esm --dts --clean",
24
+ "dev": "tsup src/index.ts --format esm --watch",
25
+ "test": "vitest run",
26
+ "test:watch": "vitest",
27
+ "prepublishOnly": "npm run build && npm test"
28
+ },
29
+ "keywords": [
30
+ "ability",
31
+ "cli",
32
+ "atomic-ability",
33
+ "mcp",
34
+ "zyql"
35
+ ],
36
+ "author": "",
37
+ "license": "ISC",
38
+ "packageManager": "pnpm@10.28.2",
39
+ "dependencies": {
40
+ "chalk": "^5.6.2",
41
+ "cli-table3": "^0.6.5",
42
+ "commander": "^14.0.3"
43
+ },
44
+ "pnpm": {
45
+ "onlyBuiltDependencies": ["esbuild"]
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^25.5.2",
49
+ "tsup": "^8.5.1",
50
+ "typescript": "^6.0.2",
51
+ "vitest": "^4.1.3"
52
+ }
53
+ }