@vibeapi/api-helper 0.0.95 → 0.0.97

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 (60) hide show
  1. package/client-config.snapshot.json +123 -10
  2. package/dist/brand.json +76 -0
  3. package/dist/cli.js +1 -1
  4. package/dist/client-config.snapshot.json +123 -10
  5. package/dist/commands/base.js +1 -1
  6. package/dist/commands/completion.js +1 -1
  7. package/dist/commands/setup.d.ts +2 -0
  8. package/dist/commands/setup.js +1 -1
  9. package/dist/commands/update.d.ts +2 -1
  10. package/dist/commands/update.js +1 -1
  11. package/dist/lib/api-validator.js +1 -1
  12. package/dist/lib/auto-update.js +1 -1
  13. package/dist/lib/brand.d.ts +82 -8
  14. package/dist/lib/brand.js +1 -1
  15. package/dist/lib/cli-help.d.ts +1 -1
  16. package/dist/lib/cli-help.js +1 -1
  17. package/dist/lib/client-config-sync.js +1 -1
  18. package/dist/lib/client-config.d.ts +5 -4
  19. package/dist/lib/client-config.js +1 -1
  20. package/dist/lib/codex-sessions.js +1 -1
  21. package/dist/lib/command-docs.js +1 -1
  22. package/dist/lib/command.js +1 -1
  23. package/dist/lib/config.d.ts +1 -1
  24. package/dist/lib/config.js +1 -1
  25. package/dist/lib/errors.d.ts +10 -10
  26. package/dist/lib/errors.js +1 -1
  27. package/dist/lib/gateway-models.js +1 -1
  28. package/dist/lib/gateway-protocol.d.ts +13 -0
  29. package/dist/lib/gateway-protocol.js +1 -0
  30. package/dist/lib/i18n.js +1 -1
  31. package/dist/lib/model-refresh.js +1 -1
  32. package/dist/lib/model-registry.d.ts +2 -4
  33. package/dist/lib/model-registry.js +1 -1
  34. package/dist/lib/persistent-env.js +1 -1
  35. package/dist/lib/program.js +1 -1
  36. package/dist/lib/secret-store.js +1 -1
  37. package/dist/lib/tool-manager.d.ts +8 -0
  38. package/dist/lib/tool-manager.js +1 -1
  39. package/dist/lib/tools/antigravity.js +1 -1
  40. package/dist/lib/tools/claude-code.js +1 -1
  41. package/dist/lib/tools/claude-desktop.js +1 -1
  42. package/dist/lib/tools/claude-plugins.d.ts +8 -13
  43. package/dist/lib/tools/claude-plugins.js +1 -1
  44. package/dist/lib/tools/codex-catalog.d.ts +1 -1
  45. package/dist/lib/tools/codex-catalog.js +1 -1
  46. package/dist/lib/tools/codex.js +1 -1
  47. package/dist/lib/tools/elevated-reg.js +1 -1
  48. package/dist/lib/tools/grok.js +1 -1
  49. package/dist/lib/tools/openclaw.js +1 -1
  50. package/dist/lib/tools/tool-installs.d.ts +2 -2
  51. package/dist/lib/tools/tool-installs.js +1 -1
  52. package/dist/lib/tools/types.js +1 -1
  53. package/dist/lib/windows-env.js +1 -1
  54. package/dist/lib/wizard.js +1 -1
  55. package/dist/locales/en_US.json +105 -97
  56. package/dist/locales/zh_CN.json +105 -97
  57. package/dist/models.json +1 -27
  58. package/dist/utils/logger.js +1 -1
  59. package/models.json +1 -27
  60. package/package.json +2 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema": 1,
3
- "revision": "2026-10-14T09:43:10Z-3",
3
+ "revision": "2026-10-14T15:38:35Z-5",
4
4
  "min_client_version": "",
5
5
  "upgrade_message": "",
6
6
  "defaults": {
@@ -275,7 +275,10 @@
275
275
  "recommended": true,
276
276
  "hidden": false,
277
277
  "context_window": 1050000,
278
- "max_output_tokens": 128000
278
+ "max_output_tokens": 128000,
279
+ "clients": {
280
+ "codex": false
281
+ }
279
282
  },
280
283
  {
281
284
  "id": "gpt-6.1-sol",
@@ -286,7 +289,10 @@
286
289
  "recommended": false,
287
290
  "hidden": false,
288
291
  "context_window": 1050000,
289
- "max_output_tokens": 128000
292
+ "max_output_tokens": 128000,
293
+ "clients": {
294
+ "codex": false
295
+ }
290
296
  },
291
297
  {
292
298
  "id": "gpt-6-sol",
@@ -297,7 +303,10 @@
297
303
  "recommended": false,
298
304
  "hidden": false,
299
305
  "context_window": 1050000,
300
- "max_output_tokens": 128000
306
+ "max_output_tokens": 128000,
307
+ "clients": {
308
+ "codex": false
309
+ }
301
310
  },
302
311
  {
303
312
  "id": "gpt-5.6-sol",
@@ -308,7 +317,11 @@
308
317
  "recommended": false,
309
318
  "hidden": false,
310
319
  "context_window": 1050000,
311
- "max_output_tokens": 128000
320
+ "max_output_tokens": 128000,
321
+ "clients": {
322
+ "claude-code": false,
323
+ "codex": false
324
+ }
312
325
  },
313
326
  {
314
327
  "id": "gpt-5.6-terra",
@@ -319,7 +332,11 @@
319
332
  "recommended": false,
320
333
  "hidden": false,
321
334
  "context_window": 1050000,
322
- "max_output_tokens": 128000
335
+ "max_output_tokens": 128000,
336
+ "clients": {
337
+ "claude-code": false,
338
+ "codex": false
339
+ }
323
340
  },
324
341
  {
325
342
  "id": "gpt-5.6-luna",
@@ -330,7 +347,11 @@
330
347
  "recommended": false,
331
348
  "hidden": false,
332
349
  "context_window": 1050000,
333
- "max_output_tokens": 128000
350
+ "max_output_tokens": 128000,
351
+ "clients": {
352
+ "claude-code": false,
353
+ "codex": false
354
+ }
334
355
  },
335
356
  {
336
357
  "id": "deepseek-v4-pro",
@@ -503,7 +524,11 @@
503
524
  "group_name": "",
504
525
  "recommended": false,
505
526
  "hidden": false,
506
- "context_window": 1000000
527
+ "context_window": 1000000,
528
+ "clients": {
529
+ "claude-code": false,
530
+ "codex": false
531
+ }
507
532
  }
508
533
  ],
509
534
  "grok": [
@@ -525,7 +550,11 @@
525
550
  "group_name": "",
526
551
  "recommended": false,
527
552
  "hidden": false,
528
- "context_window": 500000
553
+ "context_window": 500000,
554
+ "clients": {
555
+ "claude-code": false,
556
+ "codex": false
557
+ }
529
558
  }
530
559
  ]
531
560
  },
@@ -544,5 +573,89 @@
544
573
  }
545
574
  },
546
575
  "features": {},
547
- "announcements": []
576
+ "announcements": [],
577
+ "vibecli": {
578
+ "default_models": {
579
+ "haiku": "claude-haiku-4-5-20251001",
580
+ "opus": "claude-opus-5-5[1m]",
581
+ "reasoning": "claude-opus-5-5[1m]",
582
+ "sonnet": "claude-sonnet-5[1m]"
583
+ },
584
+ "retired_default_models": {
585
+ "opus": [
586
+ "claude-opus-4-6",
587
+ "claude-opus-4-8[1m]"
588
+ ],
589
+ "reasoning": [
590
+ "claude-opus-4-6",
591
+ "claude-opus-4-8[1m]"
592
+ ],
593
+ "sonnet": [
594
+ "claude-sonnet-4-6"
595
+ ]
596
+ },
597
+ "tool_defaults": {
598
+ "atomcode": "gpt-5.5",
599
+ "claude-code": "",
600
+ "codex": "gpt-6-astra",
601
+ "grok": "grok-4.6",
602
+ "hermes": "gpt-5.5",
603
+ "openclaw": "gpt-5.5",
604
+ "opencode": "gpt-5.5"
605
+ },
606
+ "tier_chains": {
607
+ "fable": [
608
+ "claude-fable-5-1",
609
+ "claude-fable-5",
610
+ "claude-opus-5-5",
611
+ "claude-opus-5",
612
+ "claude-opus-4-8"
613
+ ],
614
+ "haiku": [
615
+ "claude-haiku-4-5-20251001"
616
+ ],
617
+ "opus": [
618
+ "claude-opus-5-5",
619
+ "claude-opus-5",
620
+ "claude-opus-4-8",
621
+ "claude-opus-4-7",
622
+ "claude-opus-4-6"
623
+ ],
624
+ "sonnet": [
625
+ "claude-sonnet-5",
626
+ "claude-sonnet-4-6"
627
+ ]
628
+ },
629
+ "retired_models": [
630
+ "deepseek-v4-flash",
631
+ "glm-5.2",
632
+ "qwen3.7-max",
633
+ "qwen3.7-plus"
634
+ ],
635
+ "tool_min_versions": {
636
+ "agy": "1.3.5",
637
+ "claude-code": "2.1.291",
638
+ "claude-desktop": "2.2553.0",
639
+ "codex": "0.122.0"
640
+ },
641
+ "agy_models": [
642
+ "gemini-4-argon",
643
+ "gemini-3.8-flash",
644
+ "gemini-3.7-flash",
645
+ "gemini-3.6-flash",
646
+ "gemini-3.1-pro-preview"
647
+ ],
648
+ "one_million_fallback": [
649
+ "claude-opus-4-8",
650
+ "claude-opus-4-7",
651
+ "claude-opus-4-6",
652
+ "claude-opus-5",
653
+ "claude-opus-5-5",
654
+ "claude-sonnet-5",
655
+ "claude-sonnet-5-5",
656
+ "claude-haiku-5-5",
657
+ "claude-fable-5",
658
+ "claude-fable-5-1"
659
+ ]
660
+ }
548
661
  }
@@ -0,0 +1,76 @@
1
+ {
2
+ "id": "vibeapi",
3
+ "displayName": "VibeAPI",
4
+ "productName": "VibeCLI",
5
+ "cliName": "vibecli",
6
+ "envPrefix": "VIBECLI",
7
+ "configDirName": ".vibecli",
8
+ "providerKey": "vibeapi",
9
+ "apiKeyEnv": "VIBEAPI_API_KEY",
10
+ "desktopEntryName": "VibeAPI",
11
+ "packageName": "@vibeapi/api-helper",
12
+ "gatewayAddresses": [
13
+ {
14
+ "url": "https://www.vibeapi.cn",
15
+ "label": "主站"
16
+ },
17
+ {
18
+ "url": "https://www.vibeapi.si",
19
+ "label": "主站备用域名(证书报错时用)"
20
+ },
21
+ {
22
+ "url": "https://hk.vibeapi.cn",
23
+ "label": "香港线路"
24
+ },
25
+ {
26
+ "url": "https://us13-www.vibeapi.cn",
27
+ "label": "US13 线路"
28
+ },
29
+ {
30
+ "url": "https://cf.vibeapi.cn",
31
+ "label": "Cloudflare 线路"
32
+ },
33
+ {
34
+ "url": "https://token.nfeyre.top",
35
+ "label": "全球加速(兜底)"
36
+ }
37
+ ],
38
+ "links": {
39
+ "tokenPage": "https://www.vibeapi.cn/console/token",
40
+ "commandDocs": "https://docs.vibeapi.cn/zh/docs/coding-tools/commands"
41
+ },
42
+ "installMirror": {
43
+ "scripts": "https://cnb.cool/vibeapi/install/-/git/raw/main",
44
+ "agy": "https://cnb.cool/vibeapi/agy/-/releases/latest/download",
45
+ "claudeDesktop": "https://cnb.cool/vibeapi/claude-desktop/-/releases/latest"
46
+ },
47
+ "pluginMarketplaces": [
48
+ {
49
+ "name": "vibeapi-plugins",
50
+ "add": "https://cnb.cool/sla/claude-plugins-official.git",
51
+ "probe": "https://cnb.cool/sla/claude-plugins-official.git"
52
+ }
53
+ ],
54
+ "updateRegistries": [
55
+ {
56
+ "id": "cnb",
57
+ "label": "CNB",
58
+ "url": "https://npm.cnb.cool/vibeapi/npm/-/packages/"
59
+ },
60
+ {
61
+ "id": "npm",
62
+ "label": "npmjs",
63
+ "url": "https://registry.npmjs.org/"
64
+ },
65
+ {
66
+ "id": "npmmirror",
67
+ "label": "npmmirror",
68
+ "url": "https://registry.npmmirror.com/"
69
+ },
70
+ {
71
+ "id": "yarn",
72
+ "label": "Yarn",
73
+ "url": "https://registry.yarnpkg.com/"
74
+ }
75
+ ]
76
+ }
package/dist/cli.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- Number(process.versions.node.split(".")[0])<20&&(console.error(`VibeCLI 需要 Node.js 20 或更高版本,当前是 v${process.versions.node}。\n请安装 Node.js LTS(国内镜像:https://registry.npmmirror.com/binary.html?path=node/ ,或用 nvm install --lts),装好后重开终端再运行。\nVibeCLI needs Node.js 20 or later (found v${process.versions.node}); install the LTS release and open a new terminal.`),process.exit(1));const{Command:e}=await import("./lib/command.js"),{logger:r}=await import("./utils/logger.js"),{reportCliError:o}=await import("./lib/errors.js");try{await(new e).execute(process.argv.slice(2))}catch(e){r.logError("CLI",e),o(e)}export{};
2
+ if(Number(process.versions.node.split(".")[0])<20){const{brand:r}=await import("./lib/brand.js"),e=r().productName;console.error(`${e} 需要 Node.js 20 或更高版本,当前是 v${process.versions.node}。\n请安装 Node.js LTS(国内镜像:https://registry.npmmirror.com/binary.html?path=node/ ,或用 nvm install --lts),装好后重开终端再运行。\n${e} needs Node.js 20 or later (found v${process.versions.node}); install the LTS release and open a new terminal.`),process.exit(1)}const{Command:r}=await import("./lib/command.js"),{logger:e}=await import("./utils/logger.js"),{reportCliError:o}=await import("./lib/errors.js");try{await(new r).execute(process.argv.slice(2))}catch(r){e.logError("CLI",r),o(r)}export{};
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema": 1,
3
- "revision": "2026-10-14T09:43:10Z-3",
3
+ "revision": "2026-10-14T15:38:35Z-5",
4
4
  "min_client_version": "",
5
5
  "upgrade_message": "",
6
6
  "defaults": {
@@ -275,7 +275,10 @@
275
275
  "recommended": true,
276
276
  "hidden": false,
277
277
  "context_window": 1050000,
278
- "max_output_tokens": 128000
278
+ "max_output_tokens": 128000,
279
+ "clients": {
280
+ "codex": false
281
+ }
279
282
  },
280
283
  {
281
284
  "id": "gpt-6.1-sol",
@@ -286,7 +289,10 @@
286
289
  "recommended": false,
287
290
  "hidden": false,
288
291
  "context_window": 1050000,
289
- "max_output_tokens": 128000
292
+ "max_output_tokens": 128000,
293
+ "clients": {
294
+ "codex": false
295
+ }
290
296
  },
291
297
  {
292
298
  "id": "gpt-6-sol",
@@ -297,7 +303,10 @@
297
303
  "recommended": false,
298
304
  "hidden": false,
299
305
  "context_window": 1050000,
300
- "max_output_tokens": 128000
306
+ "max_output_tokens": 128000,
307
+ "clients": {
308
+ "codex": false
309
+ }
301
310
  },
302
311
  {
303
312
  "id": "gpt-5.6-sol",
@@ -308,7 +317,11 @@
308
317
  "recommended": false,
309
318
  "hidden": false,
310
319
  "context_window": 1050000,
311
- "max_output_tokens": 128000
320
+ "max_output_tokens": 128000,
321
+ "clients": {
322
+ "claude-code": false,
323
+ "codex": false
324
+ }
312
325
  },
313
326
  {
314
327
  "id": "gpt-5.6-terra",
@@ -319,7 +332,11 @@
319
332
  "recommended": false,
320
333
  "hidden": false,
321
334
  "context_window": 1050000,
322
- "max_output_tokens": 128000
335
+ "max_output_tokens": 128000,
336
+ "clients": {
337
+ "claude-code": false,
338
+ "codex": false
339
+ }
323
340
  },
324
341
  {
325
342
  "id": "gpt-5.6-luna",
@@ -330,7 +347,11 @@
330
347
  "recommended": false,
331
348
  "hidden": false,
332
349
  "context_window": 1050000,
333
- "max_output_tokens": 128000
350
+ "max_output_tokens": 128000,
351
+ "clients": {
352
+ "claude-code": false,
353
+ "codex": false
354
+ }
334
355
  },
335
356
  {
336
357
  "id": "deepseek-v4-pro",
@@ -503,7 +524,11 @@
503
524
  "group_name": "",
504
525
  "recommended": false,
505
526
  "hidden": false,
506
- "context_window": 1000000
527
+ "context_window": 1000000,
528
+ "clients": {
529
+ "claude-code": false,
530
+ "codex": false
531
+ }
507
532
  }
508
533
  ],
509
534
  "grok": [
@@ -525,7 +550,11 @@
525
550
  "group_name": "",
526
551
  "recommended": false,
527
552
  "hidden": false,
528
- "context_window": 500000
553
+ "context_window": 500000,
554
+ "clients": {
555
+ "claude-code": false,
556
+ "codex": false
557
+ }
529
558
  }
530
559
  ]
531
560
  },
@@ -544,5 +573,89 @@
544
573
  }
545
574
  },
546
575
  "features": {},
547
- "announcements": []
576
+ "announcements": [],
577
+ "vibecli": {
578
+ "default_models": {
579
+ "haiku": "claude-haiku-4-5-20251001",
580
+ "opus": "claude-opus-5-5[1m]",
581
+ "reasoning": "claude-opus-5-5[1m]",
582
+ "sonnet": "claude-sonnet-5[1m]"
583
+ },
584
+ "retired_default_models": {
585
+ "opus": [
586
+ "claude-opus-4-6",
587
+ "claude-opus-4-8[1m]"
588
+ ],
589
+ "reasoning": [
590
+ "claude-opus-4-6",
591
+ "claude-opus-4-8[1m]"
592
+ ],
593
+ "sonnet": [
594
+ "claude-sonnet-4-6"
595
+ ]
596
+ },
597
+ "tool_defaults": {
598
+ "atomcode": "gpt-5.5",
599
+ "claude-code": "",
600
+ "codex": "gpt-6-astra",
601
+ "grok": "grok-4.6",
602
+ "hermes": "gpt-5.5",
603
+ "openclaw": "gpt-5.5",
604
+ "opencode": "gpt-5.5"
605
+ },
606
+ "tier_chains": {
607
+ "fable": [
608
+ "claude-fable-5-1",
609
+ "claude-fable-5",
610
+ "claude-opus-5-5",
611
+ "claude-opus-5",
612
+ "claude-opus-4-8"
613
+ ],
614
+ "haiku": [
615
+ "claude-haiku-4-5-20251001"
616
+ ],
617
+ "opus": [
618
+ "claude-opus-5-5",
619
+ "claude-opus-5",
620
+ "claude-opus-4-8",
621
+ "claude-opus-4-7",
622
+ "claude-opus-4-6"
623
+ ],
624
+ "sonnet": [
625
+ "claude-sonnet-5",
626
+ "claude-sonnet-4-6"
627
+ ]
628
+ },
629
+ "retired_models": [
630
+ "deepseek-v4-flash",
631
+ "glm-5.2",
632
+ "qwen3.7-max",
633
+ "qwen3.7-plus"
634
+ ],
635
+ "tool_min_versions": {
636
+ "agy": "1.3.5",
637
+ "claude-code": "2.1.291",
638
+ "claude-desktop": "2.2553.0",
639
+ "codex": "0.122.0"
640
+ },
641
+ "agy_models": [
642
+ "gemini-4-argon",
643
+ "gemini-3.8-flash",
644
+ "gemini-3.7-flash",
645
+ "gemini-3.6-flash",
646
+ "gemini-3.1-pro-preview"
647
+ ],
648
+ "one_million_fallback": [
649
+ "claude-opus-4-8",
650
+ "claude-opus-4-7",
651
+ "claude-opus-4-6",
652
+ "claude-opus-5",
653
+ "claude-opus-5-5",
654
+ "claude-sonnet-5",
655
+ "claude-sonnet-5-5",
656
+ "claude-haiku-5-5",
657
+ "claude-fable-5",
658
+ "claude-fable-5-1"
659
+ ]
660
+ }
548
661
  }
@@ -1 +1 @@
1
- import e from"chalk";import{configManager as r,DEFAULT_BASE_URL as o}from"../lib/config.js";import{i18n as t}from"../lib/i18n.js";import{toolManager as s}from"../lib/tool-manager.js";import{listAdapters as a}from"../lib/tools/registry.js";import{postApplyPrompts as n}from"../lib/post-apply.js";import{emitJson as l,isJsonMode as i}from"../lib/runtime-options.js";import{logger as u}from"../utils/logger.js";import{reportCliError as c,usageError as p,authenticationError as d,networkError as f,operationError as b}from"../lib/errors.js";import{gatewayAddresses as m,normalizeBaseUrl as y,probeGatewayAddress as g}from"../lib/gateway-addresses.js";export async function baseCommand(s){const a=r.getBaseUrl(),n=addressesWithCurrent(),u=s?.trim().toLowerCase();if(!u)return void await async function(r,o){const s=savedApiKey(),a=await probeAll(r,{apiKey:s,samples:3});i()?l({ok:!0,current:o,addresses:r.map((e,r)=>({...e,...a[r],current:e.url===o}))}):(console.log(e.bold(t.t("base.current",{url:o}))),console.log(e.gray(t.t(s?"base.probe_with_key":"base.probe_without_key"))+"\n"),_(r,a,o),console.log("\n"+e.gray(t.t("base.usage_hint"))))}(n,a);if("auto"===u){const e=await probeAll(n,{apiKey:savedApiKey()}),r=e.find(e=>e.ok);if(!r){const r=e.every(e=>"key_rejected"===e.error);return h(r?"key_rejected":"no_reachable",t.t(r?"base.key_rejected_everywhere":"base.none_reachable")),void _(n,e,a)}return void await switchGatewayAddress(r.url)}let c;if("default"===u||"reset"===u)c=o;else if(/^\d+$/.test(u)){if(c=n[Number(u)-1]?.url??null,!c)return void h("bad_index",t.t("base.bad_index",{index:u,count:String(n.length)}))}else if(c=y(s),!c)return void h("bad_url",t.t("base.bad_url",{value:s}));const p=await g(c,{apiKey:savedApiKey()});p.ok?await switchGatewayAddress(c):h("unreachable",t.t("base.unreachable",{url:c,reason:reasonText(p)}))}export async function ensureReachableGatewayAddress(s=savedApiKey()){if(r.hasRuntimeBaseUrl())return null;const a=r.getBaseUrl(),n=await g(a,{apiKey:s});if(n.ok||"key_rejected"===n.error)return null;const l=m().filter(e=>e.url!==a),u=(await probeAll(l,{apiKey:s})).find(e=>e.ok);return u?(u.url===o?r.resetBaseUrl():r.setBaseUrl(u.url,!0),i()||console.log(e.yellow(t.t("base.setup_fallback",{from:a,reason:reasonText(n),url:u.url}))),u.url):null}export function addressesWithCurrent(){const e=r.getBaseUrl(),o=m();return o.some(r=>r.url===e)||o.push({url:e,label:t.t("base.custom_label")}),o}export function savedApiKey(){return r.getApiKey()||a().map(e=>r.getApiKeyForTool(e.name)).find(Boolean)}export async function probeAll(e,r={}){return Promise.all(e.map(e=>g(e.url,r)))}export function probeSummary(r,o){if(!r.ok)return e.red(`× ${reasonText(r)}`);const s=[`${r.ms} ms`];return void 0!==r.models&&s.push(t.t("base.models_count",{count:String(r.models)})),o&&s.push(t.t("base.fastest")),e.green(`√ ${s.join(" · ")}`)}export function fastestIndex(e){let r=-1;return e.forEach((o,t)=>{o.ok&&(r<0||o.ms<e[r].ms)&&(r=t)}),r}export function reasonText(e){if(!e.error)return"";const r=t.t(`base.error_${e.error}`);return e.detail?`${r}(${e.detail})`:r}function h(e,r){const o="bad_index"===e||"bad_url"===e?p(r,"下一步:运行 vibecli base --help",e):"key_rejected"===e?d(r,"下一步:运行 vibecli setup --key <你的 Key>",e):f(r,"下一步:运行 vibecli base auto 查看可用接入地址",e);c(o)}function _(r,o,s){if(i())return;const a=o.length>1?fastestIndex(o):-1;r.forEach((r,n)=>{const l=r.url===s?e.cyan(` ← ${t.t("base.current_mark")}`):"";console.log(`${n+1}. ${r.url} ${e.gray(r.label)}\n ${probeSummary(o[n],n===a)}${l}`)})}export async function switchGatewayAddress(p){const d=r.getBaseUrl();p===o?r.resetBaseUrl():r.setBaseUrl(p,!0);const f=[],m=[];for(const e of function(){const e=[];for(const o of a()){const t=r.getApiKeyForTool(o.name);if(t)try{o.detectCurrentConfig().apiKey===t&&e.push(o.name)}catch(e){u.logError(`base.detectCurrentConfig(${o.name})`,e)}}return e}()){const o=r.getApiKeyForTool(e);try{await s.applyPrivateConfig(e,o,r.getModelsForTool(e)),f.push(e)}catch(r){m.push({tool:e,reason:r instanceof Error?r.message:String(r)})}}if(m.length>0&&c(b(m.map(({tool:e,reason:r})=>`${e}: ${r}`).join("; "),"下一步:运行 vibecli setup --key <你的 Key>","config_write_failed")),i()&&0===m.length)l({ok:0===m.length,previous:d,base_url:p,applied:f,failed:m});else{console.log(e.green(t.t(p===d?"base.kept":"base.switched",{url:p}))),f.length>0&&console.log(t.t("base.tools_moved"));for(const e of f)s.printApplied(e);for(const{tool:r,reason:o}of m)console.error(e.red(`× ${t.t("setup.apply_failed",{tool:r,reason:o})}`));0===f.length&&0===m.length&&console.log(e.yellow(t.t("base.no_tools",{url:p}))),await n(f)}}
1
+ import e from"chalk";import{configManager as r,DEFAULT_BASE_URL as o}from"../lib/config.js";import{i18n as t}from"../lib/i18n.js";import{toolManager as s}from"../lib/tool-manager.js";import{listAdapters as a}from"../lib/tools/registry.js";import{postApplyPrompts as n}from"../lib/post-apply.js";import{emitJson as l,isJsonMode as i}from"../lib/runtime-options.js";import{logger as u}from"../utils/logger.js";import{reportCliError as c,usageError as p,authenticationError as d,networkError as f,operationError as b}from"../lib/errors.js";import{gatewayAddresses as m,normalizeBaseUrl as y,probeGatewayAddress as g}from"../lib/gateway-addresses.js";import{CLI_NAME as h}from"../lib/brand.js";export async function baseCommand(s){const a=r.getBaseUrl(),n=addressesWithCurrent(),u=s?.trim().toLowerCase();if(!u)return void await async function(r,o){const s=savedApiKey(),a=await probeAll(r,{apiKey:s,samples:3});i()?l({ok:!0,current:o,addresses:r.map((e,r)=>({...e,...a[r],current:e.url===o}))}):(console.log(e.bold(t.t("base.current",{url:o}))),console.log(e.gray(t.t(s?"base.probe_with_key":"base.probe_without_key"))+"\n"),w(r,a,o),console.log("\n"+e.gray(t.t("base.usage_hint"))))}(n,a);if("auto"===u){const e=await probeAll(n,{apiKey:savedApiKey()}),r=e.find(e=>e.ok);if(!r){const r=e.every(e=>"key_rejected"===e.error);return _(r?"key_rejected":"no_reachable",t.t(r?"base.key_rejected_everywhere":"base.none_reachable")),void w(n,e,a)}return void await switchGatewayAddress(r.url)}let c;if("default"===u||"reset"===u)c=o;else if(/^\d+$/.test(u)){if(c=n[Number(u)-1]?.url??null,!c)return void _("bad_index",t.t("base.bad_index",{index:u,count:String(n.length)}))}else if(c=y(s),!c)return void _("bad_url",t.t("base.bad_url",{value:s}));const p=await g(c,{apiKey:savedApiKey()});p.ok?await switchGatewayAddress(c):_("unreachable",t.t("base.unreachable",{url:c,reason:reasonText(p)}))}export async function ensureReachableGatewayAddress(s=savedApiKey()){if(r.hasRuntimeBaseUrl())return null;const a=r.getBaseUrl(),n=await g(a,{apiKey:s});if(n.ok||"key_rejected"===n.error)return null;const l=m().filter(e=>e.url!==a),u=(await probeAll(l,{apiKey:s})).find(e=>e.ok);return u?(u.url===o?r.resetBaseUrl():r.setBaseUrl(u.url,!0),i()||console.log(e.yellow(t.t("base.setup_fallback",{from:a,reason:reasonText(n),url:u.url}))),u.url):null}export function addressesWithCurrent(){const e=r.getBaseUrl(),o=m();return o.some(r=>r.url===e)||o.push({url:e,label:t.t("base.custom_label")}),o}export function savedApiKey(){return r.getApiKey()||a().map(e=>r.getApiKeyForTool(e.name)).find(Boolean)}export async function probeAll(e,r={}){return Promise.all(e.map(e=>g(e.url,r)))}export function probeSummary(r,o){if(!r.ok)return e.red(`× ${reasonText(r)}`);const s=[`${r.ms} ms`];return void 0!==r.models&&s.push(t.t("base.models_count",{count:String(r.models)})),o&&s.push(t.t("base.fastest")),e.green(`√ ${s.join(" · ")}`)}export function fastestIndex(e){let r=-1;return e.forEach((o,t)=>{o.ok&&(r<0||o.ms<e[r].ms)&&(r=t)}),r}export function reasonText(e){if(!e.error)return"";const r=t.t(`base.error_${e.error}`);return e.detail?`${r}(${e.detail})`:r}function _(e,r){const o="bad_index"===e||"bad_url"===e?p(r,`下一步:运行 ${h} base --help`,e):"key_rejected"===e?d(r,`下一步:运行 ${h} setup --key <你的 Key>`,e):f(r,`下一步:运行 ${h} base auto 查看可用接入地址`,e);c(o)}function w(r,o,s){if(i())return;const a=o.length>1?fastestIndex(o):-1;r.forEach((r,n)=>{const l=r.url===s?e.cyan(` ← ${t.t("base.current_mark")}`):"";console.log(`${n+1}. ${r.url} ${e.gray(r.label)}\n ${probeSummary(o[n],n===a)}${l}`)})}export async function switchGatewayAddress(p){const d=r.getBaseUrl();p===o?r.resetBaseUrl():r.setBaseUrl(p,!0);const f=[],m=[];for(const e of function(){const e=[];for(const o of a()){const t=r.getApiKeyForTool(o.name);if(t)try{o.detectCurrentConfig().apiKey===t&&e.push(o.name)}catch(e){u.logError(`base.detectCurrentConfig(${o.name})`,e)}}return e}()){const o=r.getApiKeyForTool(e);try{await s.applyPrivateConfig(e,o,r.getModelsForTool(e)),f.push(e)}catch(r){m.push({tool:e,reason:r instanceof Error?r.message:String(r)})}}if(m.length>0&&c(b(m.map(({tool:e,reason:r})=>`${e}: ${r}`).join("; "),`下一步:运行 ${h} setup --key <你的 Key>`,"config_write_failed")),i()&&0===m.length)l({ok:0===m.length,previous:d,base_url:p,applied:f,failed:m});else{console.log(e.green(t.t(p===d?"base.kept":"base.switched",{url:p}))),f.length>0&&console.log(t.t("base.tools_moved"));for(const e of f)s.printApplied(e);for(const{tool:r,reason:o}of m)console.error(e.red(`× ${t.t("setup.apply_failed",{tool:r,reason:o})}`));0===f.length&&0===m.length&&console.log(e.yellow(t.t("base.no_tools",{url:p}))),await n(f)}}
@@ -1 +1 @@
1
- import{i18n as n}from"../lib/i18n.js";import{isJsonMode as e,emitJson as o}from"../lib/runtime-options.js";const i=["init","setup","lang","auth","doctor","status","logs","completion","update","upgrade","codex","bench","base","config","enter"],c=["profile","preset","backup","claude-code","codex"],s=["list","create","use","delete"],l=["list","save","use","delete"],t=["list","create","restore"],r=["revoke","reload"],m=["show","set"],a=["path","list","tail"],u=["bash","zsh","fish"],p=["--base","--non-interactive","--json","--yes","--dry-run","--no-color","--help","--version"];export async function completionCommand(_){const b=(_[0]||"").toLowerCase();if(!u.includes(b))return e()?void o({ok:!1,error:"unsupported_shell",shell:b}):void console.log(n.t("completion.unsupported_shell",{shell:b}));const $="bash"===b?`# vibecli bash completion\n_vibecli_completions() {\n local cur prev\n COMPREPLY=()\n cur="\${COMP_WORDS[COMP_CWORD]}"\n prev="\${COMP_WORDS[COMP_CWORD-1]}"\n\n case "\${prev}" in\n vibecli)\n COMPREPLY=( $(compgen -W "${i.join(" ")} ${p.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n config)\n COMPREPLY=( $(compgen -W "${c.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n profile)\n COMPREPLY=( $(compgen -W "${s.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n preset)\n COMPREPLY=( $(compgen -W "${l.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n backup)\n COMPREPLY=( $(compgen -W "${t.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n auth)\n COMPREPLY=( $(compgen -W "${r.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n lang)\n COMPREPLY=( $(compgen -W "${m.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n logs)\n COMPREPLY=( $(compgen -W "${a.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n completion)\n COMPREPLY=( $(compgen -W "${u.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n esac\n\n if [[ "\${cur}" == --* ]]; then\n COMPREPLY=( $(compgen -W "${p.join(" ")}" -- "\${cur}") )\n return 0\n fi\n}\ncomplete -F _vibecli_completions vibecli\n`:"zsh"===b?`#compdef vibecli\n_vibecli() {\n local -a commands subcommands\n local context state line\n\n _arguments -C \\\n '(-h --help)'{-h,--help}'[Show help]' \\\n '(-v --version)'{-v,--version}'[Show version]' \\\n '--base[Custom API base URL]:url:' \\\n '--non-interactive[Disable interactive prompts]' \\\n '--json[Emit JSON output]' \\\n '(-y --yes)'{-y,--yes}'[Auto-confirm prompts]' \\\n '--dry-run[Show planned changes without writing]' \\\n '--no-color[Disable colored output]' \\\n '1:command:->command' \\\n '*::arg:->args'\n\n case $state in\n command)\n commands=(\n${i.map(n=>` '${n}:vibecli ${n}'`).join("\n")}\n )\n _describe 'command' commands\n ;;\n args)\n case $line[1] in\n config)\n subcommands=(${c.map(n=>`'${n}'`).join(" ")})\n _describe 'config sub' subcommands\n ;;\n auth) _values 'auth sub' ${r.join(" ")} ;;\n lang) _values 'lang sub' ${m.join(" ")} ;;\n logs) _values 'logs action' ${a.join(" ")} ;;\n completion) _values 'shell' ${u.join(" ")} ;;\n esac\n ;;\n esac\n}\ncompdef _vibecli vibecli\n`:`# vibecli fish completion\ncomplete -c vibecli -n "__fish_use_subcommand" -a "${i.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from config" -a "${c.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from profile" -a "${s.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from preset" -a "${l.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from backup" -a "${t.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from auth" -a "${r.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from lang" -a "${m.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from logs" -a "${a.join(" ")}"\ncomplete -c vibecli -n "__fish_seen_subcommand_from completion" -a "${u.join(" ")}"\ncomplete -c vibecli -l base -d 'Custom API base URL'\ncomplete -c vibecli -l non-interactive -d 'Disable interactive prompts'\ncomplete -c vibecli -l json -d 'Emit JSON output'\ncomplete -c vibecli -l yes -s y -d 'Auto-confirm prompts'\ncomplete -c vibecli -l dry-run -d 'Show planned changes without writing'\ncomplete -c vibecli -l no-color -d 'Disable colored output'\n`;e()?o({shell:b,script:$}):(console.log($),"bash"===b?console.log("# "+n.t("completion.instructions_bash")):"zsh"===b?console.log("# "+n.t("completion.instructions_zsh")):console.log("# "+n.t("completion.instructions_fish")))}
1
+ import{i18n as n}from"../lib/i18n.js";import{isJsonMode as o,emitJson as e}from"../lib/runtime-options.js";import{CLI_NAME as s}from"../lib/brand.js";const c=s.replace(/-/g,"_"),t=["init","setup","lang","auth","doctor","status","logs","completion","update","upgrade","codex","bench","base","config","enter"],i=["profile","preset","backup","claude-code","codex"],r=["list","create","use","delete"],l=["list","save","use","delete"],m=["list","create","restore"],a=["revoke","reload"],p=["show","set"],u=["path","list","tail"],$=["bash","zsh","fish"],_=["--base","--non-interactive","--json","--yes","--dry-run","--no-color","--help","--version"];export async function completionCommand(d){const h=(d[0]||"").toLowerCase();if(!$.includes(h))return o()?void e({ok:!1,error:"unsupported_shell",shell:h}):void console.log(n.t("completion.unsupported_shell",{shell:h}));const b="bash"===h?`# ${s} bash completion\n_${c}_completions() {\n local cur prev\n COMPREPLY=()\n cur="\${COMP_WORDS[COMP_CWORD]}"\n prev="\${COMP_WORDS[COMP_CWORD-1]}"\n\n case "\${prev}" in\n ${s})\n COMPREPLY=( $(compgen -W "${t.join(" ")} ${_.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n config)\n COMPREPLY=( $(compgen -W "${i.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n profile)\n COMPREPLY=( $(compgen -W "${r.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n preset)\n COMPREPLY=( $(compgen -W "${l.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n backup)\n COMPREPLY=( $(compgen -W "${m.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n auth)\n COMPREPLY=( $(compgen -W "${a.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n lang)\n COMPREPLY=( $(compgen -W "${p.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n logs)\n COMPREPLY=( $(compgen -W "${u.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n completion)\n COMPREPLY=( $(compgen -W "${$.join(" ")}" -- "\${cur}") )\n return 0\n ;;\n esac\n\n if [[ "\${cur}" == --* ]]; then\n COMPREPLY=( $(compgen -W "${_.join(" ")}" -- "\${cur}") )\n return 0\n fi\n}\ncomplete -F _${c}_completions ${s}\n`:"zsh"===h?`#compdef ${s}\n_${c}() {\n local -a commands subcommands\n local context state line\n\n _arguments -C \\\n '(-h --help)'{-h,--help}'[Show help]' \\\n '(-v --version)'{-v,--version}'[Show version]' \\\n '--base[Custom API base URL]:url:' \\\n '--non-interactive[Disable interactive prompts]' \\\n '--json[Emit JSON output]' \\\n '(-y --yes)'{-y,--yes}'[Auto-confirm prompts]' \\\n '--dry-run[Show planned changes without writing]' \\\n '--no-color[Disable colored output]' \\\n '1:command:->command' \\\n '*::arg:->args'\n\n case $state in\n command)\n commands=(\n${t.map(n=>` '${n}:${s} ${n}'`).join("\n")}\n )\n _describe 'command' commands\n ;;\n args)\n case $line[1] in\n config)\n subcommands=(${i.map(n=>`'${n}'`).join(" ")})\n _describe 'config sub' subcommands\n ;;\n auth) _values 'auth sub' ${a.join(" ")} ;;\n lang) _values 'lang sub' ${p.join(" ")} ;;\n logs) _values 'logs action' ${u.join(" ")} ;;\n completion) _values 'shell' ${$.join(" ")} ;;\n esac\n ;;\n esac\n}\ncompdef _${c} ${s}\n`:`# ${s} fish completion\ncomplete -c ${s} -n "__fish_use_subcommand" -a "${t.join(" ")}"\ncomplete -c ${s} -n "__fish_seen_subcommand_from config" -a "${i.join(" ")}"\ncomplete -c ${s} -n "__fish_seen_subcommand_from profile" -a "${r.join(" ")}"\ncomplete -c ${s} -n "__fish_seen_subcommand_from preset" -a "${l.join(" ")}"\ncomplete -c ${s} -n "__fish_seen_subcommand_from backup" -a "${m.join(" ")}"\ncomplete -c ${s} -n "__fish_seen_subcommand_from auth" -a "${a.join(" ")}"\ncomplete -c ${s} -n "__fish_seen_subcommand_from lang" -a "${p.join(" ")}"\ncomplete -c ${s} -n "__fish_seen_subcommand_from logs" -a "${u.join(" ")}"\ncomplete -c ${s} -n "__fish_seen_subcommand_from completion" -a "${$.join(" ")}"\ncomplete -c ${s} -l base -d 'Custom API base URL'\ncomplete -c ${s} -l non-interactive -d 'Disable interactive prompts'\ncomplete -c ${s} -l json -d 'Emit JSON output'\ncomplete -c ${s} -l yes -s y -d 'Auto-confirm prompts'\ncomplete -c ${s} -l dry-run -d 'Show planned changes without writing'\ncomplete -c ${s} -l no-color -d 'Disable colored output'\n`;o()?e({shell:h,script:b}):(console.log(b),"bash"===h?console.log("# "+n.t("completion.instructions_bash")):"zsh"===h?console.log("# "+n.t("completion.instructions_zsh")):console.log("# "+n.t("completion.instructions_fish")))}
@@ -80,6 +80,8 @@ export interface SetupCommandOptions extends Partial<Record<DedicatedKeyOption,
80
80
  force?: boolean;
81
81
  /** Windows: `on` = always put Claude Desktop's config in the registry policy (for when the local file is ignored); `off` = back to automatic. */
82
82
  desktopRegistry?: string;
83
+ /** Missing tools: `true` = install them from the China mirrors, `false` = only print the command; unset = ask in a terminal. */
84
+ install?: boolean;
83
85
  }
84
86
  /** `--claude-official on|off` (also true/false, show/hide, 1/0, yes/no); undefined when unrecognized. */
85
87
  export declare function parseOnOff(raw: string | undefined): boolean | undefined;
@@ -1 +1 @@
1
- import e from"chalk";import{getAdapter as o}from"../lib/tools/registry.js";import{postApplyPrompts as t}from"../lib/post-apply.js";import{ensureReachableGatewayAddress as i}from"./base.js";import{i18n as s}from"../lib/i18n.js";import{configManager as a}from"../lib/config.js";import{toolManager as n,SUPPORTED_TOOLS as l}from"../lib/tool-manager.js";import{validateApiKey as r,fetchModels as d}from"../lib/api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as p,setCodexOneMillionContext as c}from"../lib/tools/codex.js";import{resolveModelCatalog as u}from"../lib/model-catalog.js";import{CLAUDE_DESKTOP_FORCE_REGISTRY_SETTING as f,CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING as m}from"../lib/tools/claude-desktop.js";import{operationError as g,authenticationError as y,networkError as k,EXIT_CODES as _}from"../lib/errors.js";import{isJsonMode as b}from"../lib/runtime-options.js";export const DEDICATED_KEYS=[{option:"claudekey",tool:"claude-code",need:"claude"},{option:"codexkey",tool:"codex",need:"gpt"},{option:"grokkey",tool:"grok",need:"grok"},{option:"desktopkey",tool:"claude-desktop",need:"claude"},{option:"agykey",tool:"agy",need:"gemini"},{option:"openclawkey",tool:"openclaw",need:"any",hidden:!0},{option:"hermeskey",tool:"hermes",need:"any",hidden:!0},{option:"atomcodekey",tool:"atomcode",need:"any",hidden:!0}];async function h(e){const o=await d(e);if(!o.ok)return{hasClaude:!0,hasGpt:!0,hasGrok:!0,hasGemini:!0,probed:!1};const t=o.models.map(e=>e.toLowerCase());return{hasClaude:t.some(e=>e.includes("claude")),hasGpt:t.some(e=>e.startsWith("gpt")||e.includes("gpt-")||/^o\d/.test(e)),hasGrok:t.some(e=>e.includes("grok")),hasGemini:t.some(e=>e.startsWith("gemini")),probed:!0}}function v(e,o){switch(o){case"claude":return e.hasClaude;case"gpt":return e.hasGpt;case"grok":return e.hasGrok;case"gemini":return e.hasGemini;default:return!0}}export function parseOnOff(e){const o=(e??"").trim().toLowerCase();return!!["on","true","show","1","yes","y"].includes(o)||!["off","false","hide","0","no","n"].includes(o)&&void 0}function w(o){console.log(e.green(o))}function x(o){console.log(e.yellow(o))}function C(o){console.error(e.red(o))}let K=null;function G(e){const o={operation:1,authentication:2,network:3};(!K||o[e]>o[K])&&(K=e)}async function S(o,t){b()||console.log(e.gray(s.t("setup.validating",{label:o})));const i=await r(t);if(i.valid)return!0;const n="zh_CN"===s.getLocale()?"<你的 Key>":"<your key>",l="--key"===o?`vibecli setup --key ${n}`:`vibecli setup ${o} ${n}`;let d;if("network_error"===i.error?(G("network"),d=k(s.t("setup.unreachable",{url:a.getBaseUrl(),reason:i.message||"network error"}),"vibecli base auto","network_error")):"invalid_api_key"===i.error?(G("authentication"),d=y(s.t("setup.invalid_key",{label:o,reason:i.message||"invalid"}),l,"invalid_api_key")):(G("operation"),d=g(s.t("setup.invalid_key",{label:o,reason:i.message||i.error||"unknown"}),l,i.error)),b())throw d;return C(d.message),console.log(e.gray(s.t("errors.next_step",{command:d.hint}))),!1}async function j(o,t,i={}){const{oneMillion:r,officialLogin:d,desktopRegistry:c,force:u}=i,y=l[o];try{"claude-desktop"===o&&void 0!==c&&a.setToolSetting("claude-desktop",f,c),"codex"===o&&void 0!==r&&a.setToolSetting("codex",p,r),"claude-desktop"===o&&void 0!==d&&a.setToolSetting("claude-desktop",m,!d),await n.applyPrivateConfig(o,t,a.getModelsForTool(o),{force:u})}catch(e){if(b())throw g(e instanceof Error?e.message:String(e),"vibecli setup --key "+("zh_CN"===s.getLocale()?"<你的 Key>":"<your key>"),"config_write_failed");const o=(e instanceof Error?e.message:String(e)).replace(/\s+/g," ").trim();return C("× "+s.t("setup.apply_failed",{tool:y.displayName,reason:o})),!1}return n.printApplied(o),u&&console.log(e.gray(" "+s.t("config.force_done",{tool:y.displayName}))),"codex"===o&&void 0!==r&&console.log(e.gray(" "+s.t(r?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))),n.isToolInstalled(o)||console.log(e.yellow(" ! "+s.t("setup.install_hint",{tool:y.displayName,install:y.installCommand}))),!0}export async function setupCommand(e){K=null;const r=e.key?.trim(),d=DEDICATED_KEYS.map(o=>({...o,apiKey:e[o.option]?.trim()})).filter(e=>Boolean(e.apiKey)),f=void 0!==e.oneMillion,m=void 0!==e.claudeOfficial;let g;if(m&&(g=parseOnOff(e.claudeOfficial),void 0===g))return C(s.t("setup.claude_official_invalid",{value:String(e.claudeOfficial)})),void(process.exitCode=1);const y=void 0!==e.desktopRegistry;let k;if(y&&(k=parseOnOff(e.desktopRegistry),void 0===k))return C(s.t("setup.desktop_registry_invalid",{value:String(e.desktopRegistry)})),void(process.exitCode=1);const G=m||y,M=Boolean(r||d.length>0),T={oneMillion:e.oneMillion,officialLogin:g,desktopRegistry:k,force:e.force};if(!M&&(f||G)){if(f){const o=a.getApiKeyForTool("codex");if(o)try{const t=!0===e.oneMillion;a.setToolSetting("codex",p,t);const i=await u(o);c(t,i??void 0),w(s.t(t?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))}catch(e){C(s.t("setup.apply_failed",{tool:l.codex.displayName,reason:e instanceof Error?e.message:String(e)})),process.exitCode=1}else C(s.t("setup.codex_1m_missing_key")),process.exitCode=1}if(G){const e=a.getApiKeyForTool("claude-desktop");e?await j("claude-desktop",e,{...T,oneMillion:void 0})?await t(["claude-desktop"]):process.exitCode=1:(C(s.t("setup.desktop_official_login_missing_key")),process.exitCode=1)}return}if(!M)return C(s.t("setup.usage")),void(process.exitCode=1);await i(r||d[0].apiKey);let E=0,O=0;const N=new Set,A=d.map(e=>({tool:e.tool,label:`--${e.option}`,apiKey:e.apiKey,need:e.need}));for(const e of A){if(!await S(e.label,e.apiKey)){O+=1;continue}const o=await h(e.apiKey);!o.probed||v(o,e.need)?(o.probed||x(s.t("setup.probe_failed",{label:e.label})),a.setToolApiKey(e.tool,e.apiKey),await j(e.tool,e.apiKey,T)?(E+=1,N.add(e.tool)):O+=1):(C(s.t(`setup.key_no_${e.need}`,{label:e.label,tool:l[e.tool].displayName})),O+=1)}if(r){if(!await S("--key",r))return void(process.exitCode=_[K??"operation"]);a.setApiKey(r);const e=await h(r);if(e.probed||x(s.t("setup.probe_failed",{label:"--key"})),e.probed&&!e.hasClaude&&!e.hasGpt&&!e.hasGrok&&!e.hasGemini)return C(s.t("setup.key_no_family")),void(process.exitCode=1);const o=[{tool:"claude-code",capable:e.hasClaude,skipMsg:s.t("setup.skip_no_claude")},{tool:"codex",capable:e.hasGpt,skipMsg:s.t("setup.skip_no_gpt")},{tool:"grok",capable:e.hasGrok,skipMsg:s.t("setup.skip_no_grok")},{tool:"claude-desktop",capable:e.hasClaude&&n.isToolInstalled("claude-desktop"),skipMsg:s.t("setup.skip_desktop_not_installed"),quiet:!e.hasClaude},{tool:"agy",capable:e.hasGemini,skipMsg:s.t("setup.skip_no_gemini")}];for(const e of o)N.has(e.tool)||(e.capable?await j(e.tool,r,T)?(E+=1,N.add(e.tool)):O+=1:e.quiet||x(e.skipMsg))}if(E>0){const e=[...N].filter(e=>!o(e)?.isInstalled).map(e=>l[e]?.command).filter(Boolean).join(" / "),t=s.t("setup.done",{count:String(E)});w("\n"+(e?`${t}${s.t("setup.done_commands",{commands:e})}`:t))}O>0&&(C(s.t("setup.failed_summary",{count:String(O)})),b()||console.error("vibecli setup --key "+("zh_CN"===s.getLocale()?"<你的 Key>":"<your key>"))),await t([...N]),(O>0||0===E)&&(process.exitCode=_[K??"operation"])}
1
+ import o from"chalk";import{getAdapter as e}from"../lib/tools/registry.js";import{postApplyPrompts as t}from"../lib/post-apply.js";import{ensureReachableGatewayAddress as i}from"./base.js";import{i18n as s}from"../lib/i18n.js";import{configManager as l}from"../lib/config.js";import{toolManager as a,SUPPORTED_TOOLS as n}from"../lib/tool-manager.js";import{validateApiKey as r,fetchModels as p}from"../lib/api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as d,setCodexOneMillionContext as c}from"../lib/tools/codex.js";import{resolveModelCatalog as u}from"../lib/model-catalog.js";import{CLAUDE_DESKTOP_FORCE_REGISTRY_SETTING as m,CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING as f}from"../lib/tools/claude-desktop.js";import{operationError as g,authenticationError as y,networkError as k,EXIT_CODES as _}from"../lib/errors.js";import{isJsonMode as h,isNonInteractive as b,shouldAutoConfirm as w}from"../lib/runtime-options.js";import v from"inquirer";import{logger as x}from"../utils/logger.js";import{forgetInstalls as C}from"../lib/tools/tool-installs.js";import{CLI_NAME as j}from"../lib/brand.js";export const DEDICATED_KEYS=[{option:"claudekey",tool:"claude-code",need:"claude"},{option:"codexkey",tool:"codex",need:"gpt"},{option:"grokkey",tool:"grok",need:"grok"},{option:"desktopkey",tool:"claude-desktop",need:"claude"},{option:"agykey",tool:"agy",need:"gemini"},{option:"openclawkey",tool:"openclaw",need:"any",hidden:!0},{option:"hermeskey",tool:"hermes",need:"any",hidden:!0},{option:"atomcodekey",tool:"atomcode",need:"any",hidden:!0}];async function K(o){const e=await p(o);if(!e.ok)return{hasClaude:!0,hasGpt:!0,hasGrok:!0,hasGemini:!0,probed:!1};const t=e.models.map(o=>o.toLowerCase());return{hasClaude:t.some(o=>o.includes("claude")),hasGpt:t.some(o=>o.startsWith("gpt")||o.includes("gpt-")||/^o\d/.test(o)),hasGrok:t.some(o=>o.includes("grok")),hasGemini:t.some(o=>o.startsWith("gemini")),probed:!0}}function T(o,e){switch(e){case"claude":return o.hasClaude;case"gpt":return o.hasGpt;case"grok":return o.hasGrok;case"gemini":return o.hasGemini;default:return!0}}export function parseOnOff(o){const e=(o??"").trim().toLowerCase();return!!["on","true","show","1","yes","y"].includes(e)||!["off","false","hide","0","no","n"].includes(e)&&void 0}function $(e){console.log(o.green(e))}function G(e){console.log(o.yellow(e))}function S(e){console.error(o.red(e))}let M=null;function N(o){const e={operation:1,authentication:2,network:3};(!M||e[o]>e[M])&&(M=o)}async function E(e,t){h()||console.log(o.gray(s.t("setup.validating",{label:e})));const i=await r(t);if(i.valid)return!0;const a="zh_CN"===s.getLocale()?"<你的 Key>":"<your key>",n="--key"===e?`${j} setup --key ${a}`:`${j} setup ${e} ${a}`;let p;if("network_error"===i.error?(N("network"),p=k(s.t("setup.unreachable",{url:l.getBaseUrl(),reason:i.message||"network error"}),`${j} base auto`,"network_error")):"invalid_api_key"===i.error?(N("authentication"),p=y(s.t("setup.invalid_key",{label:e,reason:i.message||"invalid"}),n,"invalid_api_key")):(N("operation"),p=g(s.t("setup.invalid_key",{label:e,reason:i.message||i.error||"unknown"}),n,i.error)),h())throw p;return S(p.message),console.log(o.gray(s.t("errors.next_step",{command:p.hint}))),!1}async function O(t,i,r={}){const{oneMillion:p,officialLogin:c,desktopRegistry:u,force:y}=r,k=n[t];try{"claude-desktop"===t&&void 0!==u&&l.setToolSetting("claude-desktop",m,u),"codex"===t&&void 0!==p&&l.setToolSetting("codex",d,p),"claude-desktop"===t&&void 0!==c&&l.setToolSetting("claude-desktop",f,!c),await a.applyPrivateConfig(t,i,l.getModelsForTool(t),{force:y})}catch(o){if(h())throw g(o instanceof Error?o.message:String(o),`${j} setup --key ${"zh_CN"===s.getLocale()?"<你的 Key>":"<your key>"}`,"config_write_failed");const e=(o instanceof Error?o.message:String(o)).replace(/\s+/g," ").trim();return S("× "+s.t("setup.apply_failed",{tool:k.displayName,reason:e})),!1}return a.printApplied(t),y&&console.log(o.gray(" "+s.t("config.force_done",{tool:k.displayName}))),"codex"===t&&void 0!==p&&console.log(o.gray(" "+s.t(p?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))),e(t)?.isInstalled&&!a.isToolInstalled(t)&&console.log(o.yellow(" ! "+s.t("setup.install_hint",{tool:k.displayName,install:k.installCommand}))),!0}export async function setupCommand(r){M=null;const p=r.key?.trim(),m=DEDICATED_KEYS.map(o=>({...o,apiKey:r[o.option]?.trim()})).filter(o=>Boolean(o.apiKey)),f=void 0!==r.oneMillion,g=void 0!==r.claudeOfficial;let y;if(g&&(y=parseOnOff(r.claudeOfficial),void 0===y))return S(s.t("setup.claude_official_invalid",{value:String(r.claudeOfficial)})),void(process.exitCode=1);const k=void 0!==r.desktopRegistry;let N;if(k&&(N=parseOnOff(r.desktopRegistry),void 0===N))return S(s.t("setup.desktop_registry_invalid",{value:String(r.desktopRegistry)})),void(process.exitCode=1);const I=g||k,A=Boolean(p||m.length>0),L={oneMillion:r.oneMillion,officialLogin:y,desktopRegistry:N,force:r.force};if(!A&&(f||I)){if(f){const o=l.getApiKeyForTool("codex");if(o)try{const e=!0===r.oneMillion;l.setToolSetting("codex",d,e);const t=await u(o);c(e,t??void 0),$(s.t(e?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))}catch(o){S(s.t("setup.apply_failed",{tool:n.codex.displayName,reason:o instanceof Error?o.message:String(o)})),process.exitCode=1}else S(s.t("setup.codex_1m_missing_key")),process.exitCode=1}if(I){const o=l.getApiKeyForTool("claude-desktop");o?await O("claude-desktop",o,{...L,oneMillion:void 0})?await t(["claude-desktop"]):process.exitCode=1:(S(s.t("setup.desktop_official_login_missing_key")),process.exitCode=1)}return}if(!A)return S(s.t("setup.usage")),void(process.exitCode=1);await i(p||m[0].apiKey);let D=0,R=0;const B=new Set,q=m.map(o=>({tool:o.tool,label:`--${o.option}`,apiKey:o.apiKey,need:o.need}));for(const o of q){if(!await E(o.label,o.apiKey)){R+=1;continue}const e=await K(o.apiKey);!e.probed||T(e,o.need)?(e.probed||G(s.t("setup.probe_failed",{label:o.label})),l.setToolApiKey(o.tool,o.apiKey),await O(o.tool,o.apiKey,L)?(D+=1,B.add(o.tool)):R+=1):(S(s.t(`setup.key_no_${o.need}`,{label:o.label,tool:n[o.tool].displayName})),R+=1)}if(p){if(!await E("--key",p))return void(process.exitCode=_[M??"operation"]);l.setApiKey(p);const o=await K(p);if(o.probed||G(s.t("setup.probe_failed",{label:"--key"})),o.probed&&!o.hasClaude&&!o.hasGpt&&!o.hasGrok&&!o.hasGemini)return S(s.t("setup.key_no_family")),void(process.exitCode=1);const e=[{tool:"claude-code",capable:o.hasClaude,skipMsg:s.t("setup.skip_no_claude")},{tool:"codex",capable:o.hasGpt,skipMsg:s.t("setup.skip_no_gpt")},{tool:"grok",capable:o.hasGrok,skipMsg:s.t("setup.skip_no_grok")},{tool:"claude-desktop",capable:o.hasClaude&&a.isToolInstalled("claude-desktop"),skipMsg:s.t("setup.skip_desktop_not_installed"),quiet:!o.hasClaude},{tool:"agy",capable:o.hasGemini,skipMsg:s.t("setup.skip_no_gemini")}];for(const o of e)B.has(o.tool)||(o.capable?await O(o.tool,p,L)?(D+=1,B.add(o.tool)):R+=1:o.quiet||G(o.skipMsg))}if(D>0){const t=await async function(t,i){const l=t.filter(o=>!e(o)?.isInstalled&&!a.isToolInstalled(o));if(0===l.length)return[];const r=l.map(o=>n[o].displayName).join(s.t("setup.list_separator"));let p=i;if(void 0!==p||h()||(w()?p=!b():process.stdin.isTTY&&(console.log(""),p=(await v.prompt([{type:"confirm",name:"go",message:s.t("setup.install_confirm",{tools:r}),default:!0}])).go)),!p){console.log("");for(const e of l)console.log(o.yellow("! "+s.t("setup.install_hint",{tool:n[e].displayName,install:n[e].installCommand})));return h()||console.log(o.gray(s.t("setup.install_all_hint",{cli:j}))),l}const d=[];for(const e of l){console.log(o.bold(`\n${n[e].displayName}`));try{await a.installTool(e)}catch(o){x.logError(`setup: install ${e}`,o)}C(),a.isToolInstalled(e)||d.push(e)}return d}([...B],r.install),i=[...B].filter(o=>!e(o)?.isInstalled&&!t.includes(o)),l=i.map(o=>n[o]?.command).filter(Boolean).join(" / "),p=s.t("setup.done",{count:String(D)});$("\n"+(l?`${p}${s.t("setup.done_commands",{commands:l})}`:p)),t.length>0&&G(s.t("setup.still_missing",{tools:t.map(o=>n[o].displayName).join(s.t("setup.list_separator"))}));const d=i.filter(o=>!a.isOnPath(o));if(d.length>0&&!h()){const e=d.map(o=>n[o].command).join(" / ");console.log(o.yellow(" ! "+s.t("win32"===process.platform?"setup.path_reload_windows":"setup.path_reload",{commands:e})))}}R>0&&(S(s.t("setup.failed_summary",{count:String(R)})),h()||console.error(`${j} setup --key ${"zh_CN"===s.getLocale()?"<你的 Key>":"<your key>"}`)),await t([...B]),(R>0||0===D)&&(process.exitCode=_[M??"operation"])}
@@ -4,7 +4,8 @@ export interface LocalPackageInfo {
4
4
  version: string;
5
5
  root: string;
6
6
  }
7
- export type UpdateSourceId = 'cnb' | 'npm' | 'npmmirror' | 'yarn';
7
+ /** Registry id from the brand file (`cnb`, `npm`, `npmmirror`, `yarn`, …). */
8
+ export type UpdateSourceId = string;
8
9
  export interface UpdateSource {
9
10
  id: UpdateSourceId;
10
11
  registry: string;
@@ -1 +1 @@
1
- import r from"chalk";import{readFileSync as t}from"fs";import{join as e,dirname as o}from"path";import{fileURLToPath as n}from"url";import{i18n as a}from"../lib/i18n.js";import{isJsonMode as s,emitJson as i,isColorEnabled as c}from"../lib/runtime-options.js";const l="@vibeapi/api-helper";export const UPDATE_TIMEOUT_MS=1500;const p=(r,t,e="")=>`${r}${encodeURIComponent(t)}${e}`,m=r=>{const t=r["dist-tags"];if(!t||"object"!=typeof t)return null;const e=t.latest;return"string"==typeof e?e:null};export const UPDATE_SOURCES=[{id:"cnb",registry:"https://npm.cnb.cool/vibeapi/npm/-/packages/",metadataUrl:r=>p("https://npm.cnb.cool/vibeapi/npm/-/packages/",r),readVersion:m},{id:"npm",registry:"https://registry.npmjs.org/",metadataUrl:r=>p("https://registry.npmjs.org/",r),readVersion:m},{id:"npmmirror",registry:"https://registry.npmmirror.com/",metadataUrl:r=>p("https://registry.npmmirror.com/",r),readVersion:m},{id:"yarn",registry:"https://registry.yarnpkg.com/",metadataUrl:r=>p("https://registry.yarnpkg.com/",r),readVersion:m}];function u(r){return c()?r:r=>r}export function readLocalPackage(){const r=n(import.meta.url),a=o(r),s=e(a,"..","..");try{const r=JSON.parse(t(e(s,"package.json"),"utf-8"));return{name:r.name||l,version:r.version||"0.0.0",root:s}}catch{return{name:l,version:"0.0.0",root:s}}}function g(r){const[t,...e]=r.split("-"),o=e.length?e.join("-").split("."):[];return{main:t.split(".").map(r=>Number.parseInt(r,10)||0),pre:o}}function d(r,t){const e=Number.parseInt(r,10),o=Number.parseInt(t,10),n=Number.isFinite(e)&&String(e)===r,a=Number.isFinite(o)&&String(o)===t;return n&&a?e-o:n?-1:a?1:r<t?-1:r>t?1:0}export function compareSemver(r,t){const e=g(r),o=g(t),n=Math.max(e.main.length,o.main.length);for(let r=0;r<n;r+=1){const t=(e.main[r]||0)-(o.main[r]||0);if(0!==t)return t}if(0===e.pre.length&&0===o.pre.length)return 0;if(0===e.pre.length)return 1;if(0===o.pre.length)return-1;const a=Math.max(e.pre.length,o.pre.length);for(let r=0;r<a;r+=1){const t=e.pre[r],n=o.pre[r];if(void 0===t)return-1;if(void 0===n)return 1;const a=d(t,n);if(0!==a)return a}return 0}async function f(r,t,e,o){const n=new AbortController,a=setTimeout(()=>n.abort(),o);try{const o=await e(r.metadataUrl(t),{headers:{Accept:"application/json"},signal:n.signal});if(!o.ok)throw new Error(`HTTP ${o.status}`);const a=await o.json(),s=r.readVersion(a);if(!s)throw new Error("invalid registry metadata");return s}catch(r){if(n.signal.aborted)throw new Error(`timeout after ${o}ms`);throw r}finally{clearTimeout(a)}}export async function checkForUpdate(r={}){const t=r.localPackage||readLocalPackage(),e=r.localVersion||t.version,o=r.packageName||t.name,n=r.fetchFn||fetch,a=r.timeoutMs||1500,s=r.sources||UPDATE_SOURCES,i=[];for(const r of s)try{const t=await f(r,o,n,a);return{current:e,latest:t,up_to_date:compareSemver(e,t)>=0,source:r.id,registry:r.registry,errors:i,error:null}}catch(t){i.push({source:r.id,error:t instanceof Error?t.message:String(t)})}return{current:e,latest:null,up_to_date:null,source:null,registry:null,errors:i,error:i.map(r=>`${r.source}: ${r.error}`).join("; ")||"unknown"}}export async function updateCommand(t={}){const e=t.localPackage||readLocalPackage();s()||console.log(u(r.gray)(a.t("update.checking")));const o=await checkForUpdate({...t,localPackage:e});if(s())i(o);else if(console.log(` ${u(r.cyan)(a.t("update.current",{version:o.current}))}`),o.latest)if(console.log(` ${u(r.cyan)(a.t("update.latest",{version:o.latest}))}`),console.log(` ${u(r.cyan)(a.t("update.source",{source:o.source||"unknown"}))}`),o.up_to_date)console.log(" "+u(r.green)(a.t("update.up_to_date")));else{const t="npm"===o.source?"":` --registry ${o.registry}`,n=`npm install -g ${e.name}@latest${t}`;console.log(" "+u(r.yellow)(a.t("update.available"))),console.log(" "+u(r.gray)(a.t("update.command_hint",{command:n})))}else console.log(" "+u(r.yellow)(a.t("update.fetch_failed",{error:o.error||"unknown"})))}
1
+ import e from"chalk";import{readFileSync as t}from"fs";import{join as r,dirname as o}from"path";import{fileURLToPath as n}from"url";import{i18n as a}from"../lib/i18n.js";import{isJsonMode as s,emitJson as i,isColorEnabled as c}from"../lib/runtime-options.js";import{brand as l}from"../lib/brand.js";const u=l().packageName;export const UPDATE_TIMEOUT_MS=1500;const p=e=>{const t=e["dist-tags"];if(!t||"object"!=typeof t)return null;const r=t.latest;return"string"==typeof r?r:null};export const UPDATE_SOURCES=l().updateRegistries.map(({id:e,url:t})=>({id:e,registry:t,metadataUrl:e=>((e,t,r="")=>`${e}${encodeURIComponent(t)}${r}`)(t,e),readVersion:p}));function m(e){return c()?e:e=>e}export function readLocalPackage(){const e=n(import.meta.url),a=o(e),s=r(a,"..","..");try{const e=JSON.parse(t(r(s,"package.json"),"utf-8"));return{name:e.name||u,version:e.version||"0.0.0",root:s}}catch{return{name:u,version:"0.0.0",root:s}}}function g(e){const[t,...r]=e.split("-"),o=r.length?r.join("-").split("."):[];return{main:t.split(".").map(e=>Number.parseInt(e,10)||0),pre:o}}function f(e,t){const r=Number.parseInt(e,10),o=Number.parseInt(t,10),n=Number.isFinite(r)&&String(r)===e,a=Number.isFinite(o)&&String(o)===t;return n&&a?r-o:n?-1:a?1:e<t?-1:e>t?1:0}export function compareSemver(e,t){const r=g(e),o=g(t),n=Math.max(r.main.length,o.main.length);for(let e=0;e<n;e+=1){const t=(r.main[e]||0)-(o.main[e]||0);if(0!==t)return t}if(0===r.pre.length&&0===o.pre.length)return 0;if(0===r.pre.length)return 1;if(0===o.pre.length)return-1;const a=Math.max(r.pre.length,o.pre.length);for(let e=0;e<a;e+=1){const t=r.pre[e],n=o.pre[e];if(void 0===t)return-1;if(void 0===n)return 1;const a=f(t,n);if(0!==a)return a}return 0}async function d(e,t,r,o){const n=new AbortController,a=setTimeout(()=>n.abort(),o);try{const o=await r(e.metadataUrl(t),{headers:{Accept:"application/json"},signal:n.signal});if(!o.ok)throw new Error(`HTTP ${o.status}`);const a=await o.json(),s=e.readVersion(a);if(!s)throw new Error("invalid registry metadata");return s}catch(e){if(n.signal.aborted)throw new Error(`timeout after ${o}ms`);throw e}finally{clearTimeout(a)}}export async function checkForUpdate(e={}){const t=e.localPackage||readLocalPackage(),r=e.localVersion||t.version,o=e.packageName||t.name,n=e.fetchFn||fetch,a=e.timeoutMs||1500,s=e.sources||UPDATE_SOURCES,i=[];for(const e of s)try{const t=await d(e,o,n,a);return{current:r,latest:t,up_to_date:compareSemver(r,t)>=0,source:e.id,registry:e.registry,errors:i,error:null}}catch(t){i.push({source:e.id,error:t instanceof Error?t.message:String(t)})}return{current:r,latest:null,up_to_date:null,source:null,registry:null,errors:i,error:i.map(e=>`${e.source}: ${e.error}`).join("; ")||"unknown"}}export async function updateCommand(t={}){const r=t.localPackage||readLocalPackage();s()||console.log(m(e.gray)(a.t("update.checking")));const o=await checkForUpdate({...t,localPackage:r});if(s())i(o);else if(console.log(` ${m(e.cyan)(a.t("update.current",{version:o.current}))}`),o.latest)if(console.log(` ${m(e.cyan)(a.t("update.latest",{version:o.latest}))}`),console.log(` ${m(e.cyan)(a.t("update.source",{source:o.source||"unknown"}))}`),o.up_to_date)console.log(" "+m(e.green)(a.t("update.up_to_date")));else{const t="npm"===o.source?"":` --registry ${o.registry}`,n=`npm install -g ${r.name}@latest${t}`;console.log(" "+m(e.yellow)(a.t("update.available"))),console.log(" "+m(e.gray)(a.t("update.command_hint",{command:n})))}else console.log(" "+m(e.yellow)(a.t("update.fetch_failed",{error:o.error||"unknown"})))}