tyc-cli 0.3.7 → 0.3.9

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.
@@ -25,8 +25,7 @@ export function registerCompanyCapabilitiesCommand(companyCmd, program) {
25
25
  }
26
26
  catch (err) {
27
27
  const msg = err instanceof Error ? err.message : String(err);
28
- console.error(`请求失败: ${msg}`);
29
- process.exit(1);
28
+ throw new Error(`请求失败: ${msg}`, { cause: err });
30
29
  }
31
30
  });
32
31
  }
@@ -36,7 +36,7 @@ function bindMethod(catCmd, tool, program) {
36
36
  // 都能直观看到分层信息;徽章不影响 Agent 对 description 的语义理解。
37
37
  let methodCmd = catCmd
38
38
  .command(tool.cliMethod)
39
- .description(`[${tool.layer}] ${tool.description}`);
39
+ .description(`[${tool.layer}] ${tool.description} 调用额度:成功且返回有效结果计 1 次;报错、全部无结果和系统异常计 0 次。`);
40
40
  if (positional) {
41
41
  methodCmd = methodCmd.argument(`<${positional.name}>`, positional.description);
42
42
  }
@@ -63,8 +63,7 @@ function bindMethod(catCmd, tool, program) {
63
63
  }
64
64
  catch (err) {
65
65
  const msg = err instanceof Error ? err.message : String(err);
66
- console.error(`请求失败: ${msg}`);
67
- process.exit(1);
66
+ throw new Error(`请求失败: ${msg}`, { cause: err });
68
67
  }
69
68
  });
70
69
  }
@@ -1,9 +1,10 @@
1
- import { DEFAULT_MCP_URL, defaultCoreURL, loadConfig, resolveConfig, saveConfig, } from "../config.js";
2
- import { verifyCoreEndpoint } from "../coreClient.js";
1
+ import { DEFAULT_MCP_URL, defaultCoreURL, loadConfig, saveConfig, } from "../config.js";
2
+ import { verifyCoreAuthEndpoint, verifyCoreEndpoint } from "../coreClient.js";
3
+ import { printLoginSuccessBanner } from "../logo.js";
3
4
  export function registerInitCommand(program) {
4
5
  program
5
6
  .command("init")
6
- .description("配置 endpoint / Authorization(保存到 ~/.tyc/config.json,并校验 shared core 连通性)")
7
+ .description("配置 endpoint / Authorization(保存到 ~/.tyc/config.json,并校验配置)")
7
8
  .option("--authorization <token>", "tyc OpenAPI Authorization(写入 headers.Authorization)")
8
9
  .option("--url <url>", `MCP endpoint(默认 ${DEFAULT_MCP_URL})`)
9
10
  .option("--core-url <url>", "Shared Core HTTP endpoint(默认从 --url 推导 /v1/core/tools/call)")
@@ -52,22 +53,45 @@ export function registerInitCommand(program) {
52
53
  }
53
54
  if (resetOAuth)
54
55
  delete cfg.oauth;
56
+ const persistedAuthorization = cfg.headers.Authorization?.trim();
55
57
  saveConfig(cfg);
56
- console.log(`已保存 ~/.tyc/config.json (url=${cfg.url}, coreUrl=${cfg.coreUrl})`);
58
+ const verbose = !!program.opts().verbose;
59
+ if (verbose) {
60
+ console.error(`> config saved to ~/.tyc/config.json (url=${cfg.url}, coreUrl=${cfg.coreUrl})`);
61
+ }
57
62
  if (opts.verify === false) {
63
+ console.log(`已保存 ~/.tyc/config.json (url=${cfg.url}, coreUrl=${cfg.coreUrl})`);
58
64
  return;
59
65
  }
60
66
  try {
61
- const resolved = resolveConfig();
62
- const verbose = !!program.opts().verbose;
63
- await verifyCoreEndpoint(resolved, verbose);
64
- console.log("Shared Core endpoint 校验通过");
67
+ const verifyConfig = {
68
+ url: cfg.url,
69
+ coreUrl: cfg.coreUrl || defaultCoreURL(cfg.url),
70
+ headers: { ...(cfg.headers || {}) },
71
+ };
72
+ if (!verifyConfig.headers.Authorization && process.env.TYC_AUTHORIZATION) {
73
+ verifyConfig.headers.Authorization = process.env.TYC_AUTHORIZATION;
74
+ }
75
+ if (verifyConfig.headers.Authorization?.trim()) {
76
+ await verifyCoreAuthEndpoint(verifyConfig, verbose);
77
+ if (verbose) {
78
+ console.error("> Shared Core auth check passed");
79
+ }
80
+ if (persistedAuthorization) {
81
+ printLoginSuccessBanner();
82
+ }
83
+ else {
84
+ console.log("Authorization 校验通过(来自 TYC_AUTHORIZATION;未写入 ~/.tyc/config.json)");
85
+ }
86
+ }
87
+ else {
88
+ await verifyCoreEndpoint(verifyConfig, verbose);
89
+ console.log("Shared Core endpoint 校验通过");
90
+ }
65
91
  }
66
92
  catch (err) {
67
93
  const msg = err instanceof Error ? err.message : String(err);
68
- console.error(`连通性校验失败:${msg}`);
69
- console.error("配置已保存;确认 --url 可达后重新运行 tyc init,或加 --no-verify 跳过校验。");
70
- process.exit(1);
94
+ throw new Error(`配置校验失败:${msg}\n配置已保存;确认 --url 可达且 Authorization 有效后重新运行 tyc init,或加 --no-verify 跳过校验。`, { cause: err });
71
95
  }
72
96
  });
73
97
  }
@@ -1,6 +1,6 @@
1
1
  import { DEFAULT_MCP_URL, applyOAuthTokenToConfig, defaultCoreURL, loadConfig, resolveConfig, saveConfig, } from "../config.js";
2
2
  import { verifyCoreAuthEndpoint } from "../coreClient.js";
3
- import { buildAuthorizationURL, buildCliCallbackRedirectUri, createPKCE, discoverAuthorizationServer, discoverProtectedResourceMetadata, exchangeAuthorizationCode, knownOAuthDefaultsForMcpURL, openBrowser, parseAuthorizationCallbackCode, randomState, registerDynamicOAuthClient, startCallbackServer, } from "../oauth.js";
3
+ import { buildAuthorizationURL, buildCliCallbackRedirectUri, createPKCE, DEVICE_CODE_GRANT_TYPE, discoverAuthorizationServer, discoverProtectedResourceMetadata, exchangeAuthorizationCode, exchangeDeviceCode, knownOAuthDefaultsForMcpURL, OAuthTokenEndpointError, openBrowser, parseAuthorizationCallbackCode, randomState, registerDynamicOAuthClient, requestDeviceAuthorization, startCallbackServer, } from "../oauth.js";
4
4
  import { clearPendingOAuthLogin, loadPendingOAuthLogin, savePendingOAuthLogin, } from "../oauthPending.js";
5
5
  import { printLoginSuccessBanner } from "../logo.js";
6
6
  function parsePort(raw) {
@@ -45,6 +45,15 @@ async function verifySavedOAuthLogin(verify, verbose) {
45
45
  }
46
46
  printLoginSuccessBanner();
47
47
  }
48
+ function isPendingDeviceLogin(pending) {
49
+ return pending.version === 2;
50
+ }
51
+ function isPendingPKCELogin(pending) {
52
+ return pending.version === 1;
53
+ }
54
+ function sleep(ms) {
55
+ return new Promise((resolve) => setTimeout(resolve, ms));
56
+ }
48
57
  async function completePendingOAuthLogin(pending, callbackToken, verify, verbose) {
49
58
  const code = parseAuthorizationCallbackCode(callbackToken, pending.state);
50
59
  if (verbose) {
@@ -68,6 +77,63 @@ async function completePendingOAuthLogin(pending, callbackToken, verify, verbose
68
77
  clearPendingOAuthLogin(verbose);
69
78
  await verifySavedOAuthLogin(verify, verbose);
70
79
  }
80
+ async function completePendingDeviceLogin(pending, verify, verbose) {
81
+ if (pending.expiresAt <= Date.now()) {
82
+ clearPendingOAuthLogin(verbose);
83
+ throw new Error("OAuth device 授权已过期,请重新运行 tyc login --no-block。");
84
+ }
85
+ let intervalMs = Math.max(1, pending.interval || 5) * 1000;
86
+ const deadline = Date.now() + 30_000;
87
+ for (;;) {
88
+ try {
89
+ if (verbose) {
90
+ console.error("> polling OAuth device token endpoint");
91
+ }
92
+ const token = await exchangeDeviceCode({
93
+ tokenEndpoint: pending.tokenEndpoint,
94
+ clientId: pending.clientId,
95
+ clientSecret: pending.clientSecret,
96
+ deviceCode: pending.deviceCode,
97
+ resource: pending.resource,
98
+ });
99
+ saveOAuthTokenConfig(pending.mcpURL, token, {
100
+ tokenEndpoint: pending.tokenEndpoint,
101
+ clientId: pending.clientId,
102
+ clientSecret: pending.clientSecret,
103
+ resource: pending.resource,
104
+ scope: pending.scope,
105
+ }, verbose);
106
+ clearPendingOAuthLogin(verbose);
107
+ await verifySavedOAuthLogin(verify, verbose);
108
+ return;
109
+ }
110
+ catch (err) {
111
+ if (!(err instanceof OAuthTokenEndpointError)) {
112
+ throw err;
113
+ }
114
+ if (err.error === "authorization_pending" || err.error === "slow_down") {
115
+ if (err.error === "slow_down") {
116
+ intervalMs += 5_000;
117
+ }
118
+ if (Date.now() + intervalMs > deadline) {
119
+ throw new Error("授权尚未完成。请在网页输入 6 位验证码并确认后,再运行 tyc login --resume。");
120
+ }
121
+ await sleep(intervalMs);
122
+ continue;
123
+ }
124
+ if (["expired_token", "access_denied", "invalid_grant"].includes(err.error)) {
125
+ clearPendingOAuthLogin(verbose);
126
+ }
127
+ if (err.error === "expired_token") {
128
+ throw new Error("OAuth device 授权已过期,请重新运行 tyc login --no-block。");
129
+ }
130
+ if (err.error === "access_denied") {
131
+ throw new Error("OAuth device 授权已被拒绝,请重新运行 tyc login --no-block。");
132
+ }
133
+ throw err;
134
+ }
135
+ }
136
+ }
71
137
  export function registerLoginCommand(program) {
72
138
  program
73
139
  .command("login")
@@ -81,21 +147,40 @@ export function registerLoginCommand(program) {
81
147
  .option("--resource-metadata-url <url>", "手动指定 MCP protected resource metadata URL")
82
148
  .option("--authorization-endpoint <url>", "手动指定 OAuth authorization endpoint")
83
149
  .option("--token-endpoint <url>", "手动指定 OAuth token endpoint")
150
+ .option("--device-authorization-endpoint <url>", "手动指定 OAuth device authorization endpoint")
84
151
  .option("--redirect-host <host>", "本地回调 host(默认 localhost)")
85
152
  .option("--redirect-port <port>", "本地回调端口(默认 7078)")
86
153
  .option("--callback-path <path>", "本地回调路径(默认 /oauth/callback)")
87
154
  .option("--callback-token <code-or-url>", "继续非阻塞登录:传入授权回调 code 或包含 code 的完整 callback URL")
88
155
  .option("--token <code-or-url>", "同 --callback-token(兼容别名;仍需传授权回调 code)")
156
+ .option("--resume", "继续 RFC 8628 device flow 非阻塞登录")
89
157
  .option("--no-open", "不自动打开浏览器,只打印授权 URL")
90
- .option("--no-block", "配合 --no-open:打印授权 URL 并保存 pending 状态后立即退出")
158
+ .option("--no-block", "打印 Device Flow 授权 URL 和 6 位验证码并保存 pending 状态后立即退出")
91
159
  .option("--no-verify", "保存 token 后跳过 shared core 鉴权校验")
92
160
  .action(async (opts) => {
93
161
  const verbose = !!program.opts().verbose;
94
162
  const callbackToken = pickCallbackToken(opts);
163
+ if (opts.resume) {
164
+ if (callbackToken) {
165
+ throw new Error("--resume 不能与 --callback-token/--token 同时使用。");
166
+ }
167
+ const pending = loadPendingOAuthLogin();
168
+ if (!pending) {
169
+ throw new Error("未找到待完成的 OAuth device 登录状态。请先运行 tyc login --no-block。");
170
+ }
171
+ if (!isPendingDeviceLogin(pending)) {
172
+ throw new Error("当前待完成的是旧版 callback 登录,请使用 tyc login --callback-token <code-or-url>。");
173
+ }
174
+ await completePendingDeviceLogin(pending, opts.verify !== false && pending.verify !== false, verbose);
175
+ return;
176
+ }
95
177
  if (callbackToken) {
96
178
  const pending = loadPendingOAuthLogin();
97
179
  if (!pending) {
98
- throw new Error("未找到待完成的 OAuth 登录状态。请先运行 tyc login --no-open --no-block。");
180
+ throw new Error("未找到待完成的 OAuth 登录状态。请先运行 tyc login --no-block。");
181
+ }
182
+ if (!isPendingPKCELogin(pending)) {
183
+ throw new Error("当前待完成的是 device 登录,请在网页确认后运行 tyc login --resume。");
99
184
  }
100
185
  if (verbose) {
101
186
  console.error(`> loaded pending OAuth login from ~/.tyc/oauth_pending.json (url=${pending.mcpURL})`);
@@ -104,9 +189,6 @@ export function registerLoginCommand(program) {
104
189
  return;
105
190
  }
106
191
  const noBlock = opts.block === false;
107
- if (noBlock && opts.open !== false) {
108
- throw new Error("--no-block 需要与 --no-open 一起使用。");
109
- }
110
192
  const current = loadConfig() || {};
111
193
  const mcpURL = opts.url || process.env.TYC_MCP_ENDPOINT || current.url || DEFAULT_MCP_URL;
112
194
  const knownDefaults = knownOAuthDefaultsForMcpURL(mcpURL);
@@ -158,6 +240,7 @@ export function registerLoginCommand(program) {
158
240
  endpoints = {
159
241
  authorizationEndpoint: opts.authorizationEndpoint,
160
242
  tokenEndpoint: opts.tokenEndpoint,
243
+ deviceAuthorizationEndpoint: opts.deviceAuthorizationEndpoint,
161
244
  issuer,
162
245
  };
163
246
  }
@@ -166,11 +249,16 @@ export function registerLoginCommand(program) {
166
249
  endpoints.authorizationEndpoint =
167
250
  opts.authorizationEndpoint || endpoints.authorizationEndpoint;
168
251
  endpoints.tokenEndpoint = opts.tokenEndpoint || endpoints.tokenEndpoint;
252
+ endpoints.deviceAuthorizationEndpoint =
253
+ opts.deviceAuthorizationEndpoint || endpoints.deviceAuthorizationEndpoint;
169
254
  }
170
255
  resource ||= endpoints.resourceDocumentation || mcpURL;
171
256
  if (verbose) {
172
257
  console.error(`> authorization endpoint: ${endpoints.authorizationEndpoint}`);
173
258
  console.error(`> token endpoint: ${endpoints.tokenEndpoint}`);
259
+ if (endpoints.deviceAuthorizationEndpoint) {
260
+ console.error(`> device authorization endpoint: ${endpoints.deviceAuthorizationEndpoint}`);
261
+ }
174
262
  if (endpoints.registrationEndpoint) {
175
263
  console.error(`> registration endpoint: ${endpoints.registrationEndpoint}`);
176
264
  }
@@ -187,98 +275,114 @@ export function registerLoginCommand(program) {
187
275
  state,
188
276
  timeoutMs: 5 * 60 * 1000,
189
277
  })).redirectUri;
190
- if (!clientId) {
191
- if (!endpoints.registrationEndpoint) {
192
- callback?.close();
193
- throw new Error("OAuth metadata 未提供 registration_endpoint,请用 --client-id 指定预注册客户端。");
194
- }
195
- if (verbose) {
196
- console.error("> registering OAuth client via Dynamic Client Registration");
197
- }
198
- let registeredClient;
199
- try {
200
- registeredClient = await registerDynamicOAuthClient({
278
+ try {
279
+ if (!clientId) {
280
+ if (!endpoints.registrationEndpoint) {
281
+ throw new Error("OAuth metadata 未提供 registration_endpoint,请用 --client-id 指定预注册客户端。");
282
+ }
283
+ if (verbose) {
284
+ console.error("> registering OAuth client via Dynamic Client Registration");
285
+ }
286
+ const registeredClient = await registerDynamicOAuthClient({
201
287
  registrationEndpoint: endpoints.registrationEndpoint,
202
288
  clientName: "tyc-cli",
203
289
  redirectUris: [redirectUri],
290
+ grantTypes: noBlock
291
+ ? ["authorization_code", "refresh_token", DEVICE_CODE_GRANT_TYPE]
292
+ : undefined,
293
+ responseTypes: ["code"],
204
294
  tokenEndpointAuthMethod: "none",
205
295
  });
296
+ clientId = registeredClient.clientId;
297
+ clientSecret ||= registeredClient.clientSecret;
298
+ if (verbose) {
299
+ console.error(`> registered client_id: ${clientId}`);
300
+ }
206
301
  }
207
- catch (err) {
208
- callback?.close();
209
- throw err;
302
+ if (noBlock) {
303
+ if (!endpoints.deviceAuthorizationEndpoint) {
304
+ throw new Error("OAuth metadata 未提供 device_authorization_endpoint,请升级授权服务或使用 --device-authorization-endpoint 指定。");
305
+ }
306
+ const device = await requestDeviceAuthorization({
307
+ deviceAuthorizationEndpoint: endpoints.deviceAuthorizationEndpoint,
308
+ clientId,
309
+ clientSecret,
310
+ scope,
311
+ resource,
312
+ });
313
+ savePendingOAuthLogin({
314
+ version: 2,
315
+ flow: "device",
316
+ createdAt: Date.now(),
317
+ mcpURL,
318
+ tokenEndpoint: endpoints.tokenEndpoint,
319
+ clientId,
320
+ clientSecret,
321
+ deviceCode: device.device_code,
322
+ userCode: device.user_code,
323
+ verificationUri: device.verification_uri,
324
+ expiresAt: Date.now() + device.expires_in * 1000,
325
+ interval: device.interval || 5,
326
+ resource,
327
+ scope,
328
+ verify: opts.verify !== false,
329
+ });
330
+ console.error("请在浏览器打开以下 URL 完成登录:");
331
+ console.error(device.verification_uri);
332
+ console.error("");
333
+ console.error(`验证码:${device.user_code}`);
334
+ console.error("");
335
+ console.error("在网页输入 6 位验证码并确认授权后,运行:");
336
+ console.error("tyc login --resume");
337
+ return;
210
338
  }
211
- clientId = registeredClient.clientId;
212
- clientSecret ||= registeredClient.clientSecret;
339
+ const authorizationURL = buildAuthorizationURL({
340
+ authorizationEndpoint: endpoints.authorizationEndpoint,
341
+ clientId,
342
+ redirectUri,
343
+ scope,
344
+ state,
345
+ codeChallenge: pkce.codeChallenge,
346
+ resource,
347
+ });
213
348
  if (verbose) {
214
- console.error(`> registered client_id: ${clientId}`);
349
+ console.error(`> OAuth callback: ${redirectUri}`);
215
350
  }
216
- }
217
- const authorizationURL = buildAuthorizationURL({
218
- authorizationEndpoint: endpoints.authorizationEndpoint,
219
- clientId,
220
- redirectUri,
221
- scope,
222
- state,
223
- codeChallenge: pkce.codeChallenge,
224
- resource,
225
- });
226
- if (verbose) {
227
- console.error(`> OAuth callback: ${redirectUri}`);
228
- }
229
- if (noBlock) {
230
- savePendingOAuthLogin({
231
- version: 1,
232
- createdAt: Date.now(),
233
- mcpURL,
351
+ if (opts.open === false) {
352
+ console.error("请在浏览器打开以下 URL 完成登录:");
353
+ console.error(authorizationURL);
354
+ }
355
+ else {
356
+ console.error("正在打开浏览器完成 OAuth 登录...");
357
+ openBrowser(authorizationURL);
358
+ }
359
+ if (!callback) {
360
+ throw new Error("OAuth callback server not initialized");
361
+ }
362
+ const code = await callback.waitForCode();
363
+ if (verbose) {
364
+ console.error("> authorization code received; exchanging access token");
365
+ }
366
+ const token = await exchangeAuthorizationCode({
234
367
  tokenEndpoint: endpoints.tokenEndpoint,
235
368
  clientId,
236
369
  clientSecret,
370
+ code,
237
371
  redirectUri,
238
372
  codeVerifier: pkce.codeVerifier,
239
373
  resource,
240
- state,
241
- verify: opts.verify !== false,
242
374
  });
243
- console.error("请在浏览器打开以下 URL 完成登录:");
244
- console.error(authorizationURL);
245
- console.error("");
246
- console.error("完成授权后,浏览器会打开天眼查 CLI 回调页。请按页面提示复制命令到终端执行。");
247
- console.error("也可以复制完整回调 URL 后运行:");
248
- console.error(`tyc login --callback-token "${redirectUri}?code=...&state=..."`);
249
- return;
250
- }
251
- if (opts.open === false) {
252
- console.error("请在浏览器打开以下 URL 完成登录:");
253
- console.error(authorizationURL);
254
- }
255
- else {
256
- console.error("正在打开浏览器完成 OAuth 登录...");
257
- openBrowser(authorizationURL);
258
- }
259
- if (!callback) {
260
- throw new Error("OAuth callback server not initialized");
375
+ saveOAuthTokenConfig(mcpURL, token, {
376
+ tokenEndpoint: endpoints.tokenEndpoint,
377
+ clientId,
378
+ clientSecret,
379
+ resource,
380
+ }, verbose);
381
+ clearPendingOAuthLogin(verbose);
382
+ await verifySavedOAuthLogin(opts.verify !== false, verbose);
261
383
  }
262
- const code = await callback.waitForCode();
263
- if (verbose) {
264
- console.error("> authorization code received; exchanging access token");
384
+ finally {
385
+ callback?.close();
265
386
  }
266
- const token = await exchangeAuthorizationCode({
267
- tokenEndpoint: endpoints.tokenEndpoint,
268
- clientId,
269
- clientSecret,
270
- code,
271
- redirectUri,
272
- codeVerifier: pkce.codeVerifier,
273
- resource,
274
- });
275
- saveOAuthTokenConfig(mcpURL, token, {
276
- tokenEndpoint: endpoints.tokenEndpoint,
277
- clientId,
278
- clientSecret,
279
- resource,
280
- }, verbose);
281
- clearPendingOAuthLogin(verbose);
282
- await verifySavedOAuthLogin(opts.verify !== false, verbose);
283
387
  });
284
388
  }
@@ -3,13 +3,11 @@ import { applyTruncation, resolveOptionalInt, TRUNCATE_DEFAULTS, } from "../util
3
3
  export function emitToolResult(result, opts, toolName) {
4
4
  if (result.isError) {
5
5
  const errText = extractText(result) || "未知业务错误";
6
- console.error(errText);
7
- process.exit(1);
6
+ throw new Error(errText);
8
7
  }
9
8
  const text = extractText(result);
10
9
  if (text === null) {
11
- console.error("tools/call 返回无可解析内容");
12
- process.exit(1);
10
+ throw new Error("tools/call 返回无可解析内容");
13
11
  }
14
12
  let payload;
15
13
  try {
package/dist/config.js CHANGED
@@ -109,8 +109,7 @@ export function resolveConfig() {
109
109
  headers.Authorization = process.env.TYC_AUTHORIZATION;
110
110
  }
111
111
  if (!headers.Authorization) {
112
- console.error("未配置 Authorization。请先运行:tyc init --authorization YOUR_API_KEY");
113
- process.exit(1);
112
+ throw new Error("未配置 Authorization。请先运行:tyc init --authorization YOUR_API_KEY");
114
113
  }
115
114
  return { url, coreUrl, headers, oauth: cfg.oauth ? { ...cfg.oauth } : undefined };
116
115
  }
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { Command } from "commander";
2
+ import { Command, CommanderError } from "commander";
3
3
  import { registerInitCommand } from "./commands/init.js";
4
4
  import { registerLoginCommand } from "./commands/login.js";
5
5
  import { registerCategoryCommands } from "./commands/category.js";
@@ -67,8 +67,9 @@ ${renderCategoryLines()}
67
67
  初始化与登录
68
68
 
69
69
  tyc login OAuth 浏览器登录
70
- tyc login --no-open --no-block 打印 OAuth URL 后立即退出,手动完成
71
- tyc login --callback-token <CODE_OR_CALLBACK_URL> 继续非阻塞 OAuth 登录
70
+ tyc login --no-block 打印 Device Flow URL 和 6 位验证码后立即退出
71
+ tyc login --resume 继续非阻塞 Device Flow 登录
72
+ tyc login --callback-token <CODE_OR_CALLBACK_URL> 继续旧版 callback OAuth 登录
72
73
  tyc login --url <MCP_URL> 登录本地、预发或自托管 MCP
73
74
  tyc init --authorization <KEY> 使用 API Key 兼容路径
74
75
  tyc init --url <MCP_URL> --authorization <KEY> 配置本地或自托管服务
@@ -92,6 +93,7 @@ function renderPriorityLines() {
92
93
  return lines.join("\n");
93
94
  }
94
95
  const program = new Command()
96
+ .exitOverride()
95
97
  .name("tyc")
96
98
  .description(`天眼查 AI CLI:${getTotalCount()} 个商业数据工具 · L0=${getLayerCount("L0")} L1=${getLayerCount("L1")} L2=${getLayerCount("L2")} L3=${getLayerCount("L3")} · 面向 Agent 的 MCP 工具网关`)
97
99
  .version(VERSION, "-V, --version", "输出版本号")
@@ -124,7 +126,14 @@ registerLoginCommand(program);
124
126
  registerLayerCommands(program);
125
127
  registerCategoryCommands(program);
126
128
  program.parseAsync(process.argv).catch((err) => {
129
+ // Commander has already printed help/version/argument errors. Preserve its
130
+ // exit status without printing twice or interrupting pending stream writes.
131
+ if (err instanceof CommanderError) {
132
+ process.exitCode = err.exitCode;
133
+ return;
134
+ }
127
135
  const msg = err instanceof Error ? err.message : String(err);
128
136
  console.error(msg);
129
- process.exit(1);
137
+ // Let Node finish asynchronous I/O and teardown (including fetch on Windows).
138
+ process.exitCode = 1;
130
139
  });