mu-agent 0.1.7 → 0.1.10

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 (198) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +34 -29
  3. package/dist/bundle/chunks/anthropic-messages-4PAJY4R6.js +6 -0
  4. package/dist/bundle/chunks/anthropic.js +2 -2
  5. package/dist/bundle/chunks/azure-openai-responses-Y4BBWQ3N.js +2 -0
  6. package/dist/bundle/chunks/bedrock-converse-stream.js +12 -9
  7. package/dist/bundle/chunks/{chunk-TRDNDS6A.js → chunk-3V45WARH.js} +6 -3
  8. package/dist/bundle/chunks/{chunk-XDUWOHPL.js → chunk-5AGIVGSV.js} +3 -3
  9. package/dist/bundle/chunks/chunk-6QGV3IWD.js +6 -0
  10. package/dist/bundle/chunks/chunk-7CBJINYJ.js +20 -0
  11. package/dist/bundle/chunks/{chunk-DRQDKNXA.js → chunk-7DDKXS2Q.js} +1 -1
  12. package/dist/bundle/chunks/chunk-BSS6T6KQ.js +2 -0
  13. package/dist/bundle/chunks/chunk-CF5GWZHW.js +31 -0
  14. package/dist/bundle/chunks/chunk-DNNHZBQR.js +4 -0
  15. package/dist/bundle/chunks/chunk-FN6OEZYR.js +2 -0
  16. package/dist/bundle/chunks/chunk-FTJT2X6E.js +2 -0
  17. package/dist/bundle/chunks/chunk-HZNN636Z.js +130 -0
  18. package/dist/bundle/chunks/{chunk-4L3WN2XY.js → chunk-JXWJOIH4.js} +1 -1
  19. package/dist/bundle/chunks/{chunk-4O2SCVYF.js → chunk-KIDDRTZP.js} +4 -4
  20. package/dist/bundle/chunks/chunk-LMNWTX2L.js +11 -0
  21. package/dist/bundle/chunks/chunk-LPXNMA2W.js +2 -0
  22. package/dist/bundle/chunks/{chunk-6CYCI3EJ.js → chunk-LVVZS2NO.js} +1 -1
  23. package/dist/bundle/chunks/chunk-NN4BEPYX.js +2 -0
  24. package/dist/bundle/chunks/chunk-O7IPTASE.js +1221 -0
  25. package/dist/bundle/chunks/chunk-PMRZO46Y.js +2 -0
  26. package/dist/bundle/chunks/chunk-QOEEJKS4.js +9 -0
  27. package/dist/bundle/chunks/chunk-R4VPBBDS.js +2 -0
  28. package/dist/bundle/chunks/chunk-RAQ6TU76.js +2 -0
  29. package/dist/bundle/chunks/chunk-RDZVAGPR.js +2 -0
  30. package/dist/bundle/chunks/chunk-SGOTYFMT.js +91 -0
  31. package/dist/bundle/chunks/chunk-TMTOQ7H7.js +2 -0
  32. package/dist/bundle/chunks/chunk-UKVNB6C4.js +2 -0
  33. package/dist/bundle/chunks/chunk-VJLOQZKR.js +9 -0
  34. package/dist/bundle/chunks/chunk-WCGU75WO.js +11 -0
  35. package/dist/bundle/chunks/chunk-X234222Q.js +64 -0
  36. package/dist/bundle/chunks/cli-722OL3K5.js +51 -0
  37. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-MEZHQVNP.js +2 -0
  38. package/dist/bundle/chunks/codemode-worker.js +478 -0
  39. package/dist/bundle/chunks/easter-egg-3d-6H34EDGX.js +2 -0
  40. package/dist/bundle/chunks/execute-KVSEIDWL.js +499 -0
  41. package/dist/bundle/chunks/github-copilot.js +1 -1
  42. package/dist/bundle/chunks/{google-generative-ai-C44KRRZ4.js → google-generative-ai-D2VJQBTK.js} +1 -1
  43. package/dist/bundle/chunks/{google-vertex-25DUXPTJ.js → google-vertex-SQG2BO6C.js} +1 -1
  44. package/dist/bundle/chunks/https-proxy-agent-LVOO5TPL.js +13 -0
  45. package/dist/bundle/chunks/image-resize-worker.js +1 -1
  46. package/dist/bundle/chunks/{jiti-loader-C3NU2SDH.js → jiti-loader-GL4HJFAD.js} +1 -1
  47. package/dist/bundle/chunks/{jiti-static-loader-E6ANQD5T.js → jiti-static-loader-2RCDI6B4.js} +1 -1
  48. package/dist/bundle/chunks/{lib-HGQMNYVT.js → lib-4AVRLBHM.js} +2 -2
  49. package/dist/bundle/chunks/llama-cpp-classify-5YHLY3RC.js +22 -0
  50. package/dist/bundle/chunks/mistral-conversations-BDARVJTT.js +5 -0
  51. package/dist/bundle/chunks/{multipart-parser-SQ6GDJL7.js → multipart-parser-FMGQ7342.js} +1 -1
  52. package/dist/bundle/chunks/{node-TWVVAGPO.js → node-AUOGOBCN.js} +1 -1
  53. package/dist/bundle/chunks/node-KCMANYC2.js +16 -0
  54. package/dist/bundle/chunks/openai-chatgpt.js +86 -0
  55. package/dist/bundle/chunks/openai-codex-responses-CECGTDYD.js +10 -0
  56. package/dist/bundle/chunks/openai-codex.js +2 -2
  57. package/dist/bundle/chunks/openai-completions-UOA7MELD.js +7 -0
  58. package/dist/bundle/chunks/openai-decisions-HLM372N6.js +5 -0
  59. package/dist/bundle/chunks/openai-responses-TII7ZY5X.js +3 -0
  60. package/dist/bundle/chunks/{openrouter-images-JVEJN44Q.js → openrouter-images-7T452AS5.js} +1 -1
  61. package/dist/bundle/chunks/openrouter.js +2 -2
  62. package/dist/bundle/chunks/{pi-messages-TM5SOOIW.js → pi-messages-2UDKLKOO.js} +1 -1
  63. package/dist/bundle/chunks/radius.js +2 -2
  64. package/dist/bundle/chunks/runtime-O3ELDOM7.js +2 -0
  65. package/dist/bundle/chunks/{src-UK77SYZE.js → src-QKGKDFSU.js} +1 -1
  66. package/dist/bundle/chunks/typesafe-system-one-KNCICMO5.js +2 -0
  67. package/dist/bundle/chunks/virtual-modules-IKEARUOZ.js +2 -0
  68. package/dist/bundle/cli-runtime.js +1 -1
  69. package/dist/bundle/index.js +1 -1
  70. package/dist/bundle/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
  71. package/dist/bundle/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
  72. package/dist/bundle/rpc-entry.js +1 -1
  73. package/dist/core/export-html/template.js +37 -16
  74. package/dist/modes/interactive/theme/dark.json +60 -73
  75. package/dist/modes/interactive/theme/light.json +58 -70
  76. package/dist/modes/interactive/theme/system-theme.d.ts +55 -0
  77. package/dist/modes/interactive/theme/system-theme.d.ts.map +1 -0
  78. package/dist/modes/interactive/theme/system-theme.js +546 -0
  79. package/dist/modes/interactive/theme/system-theme.js.map +1 -0
  80. package/dist/modes/interactive/theme/theme-controller.d.ts +43 -9
  81. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  82. package/dist/modes/interactive/theme/theme-controller.js +121 -79
  83. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  84. package/dist/modes/interactive/theme/theme-schema.d.ts +28 -0
  85. package/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -0
  86. package/dist/modes/interactive/theme/theme-schema.js +105 -0
  87. package/dist/modes/interactive/theme/theme-schema.js.map +1 -0
  88. package/dist/modes/interactive/theme/theme-tokens.d.ts +257 -0
  89. package/dist/modes/interactive/theme/theme-tokens.d.ts.map +1 -0
  90. package/dist/modes/interactive/theme/theme-tokens.js +94 -0
  91. package/dist/modes/interactive/theme/theme-tokens.js.map +1 -0
  92. package/dist/modes/interactive/theme/theme.d.ts +47 -56
  93. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  94. package/dist/modes/interactive/theme/theme.js +149 -143
  95. package/dist/modes/interactive/theme/theme.js.map +1 -1
  96. package/dist/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
  97. package/dist/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
  98. package/docs/cli-integration.md +1 -1
  99. package/docs/cli.md +78 -13
  100. package/docs/codemode.md +230 -0
  101. package/docs/compaction.md +5 -5
  102. package/docs/configuration.md +2 -0
  103. package/docs/custom-provider.md +53 -1
  104. package/docs/docs.json +21 -1
  105. package/docs/environment-variables.md +5 -3
  106. package/docs/extensions.md +172 -10
  107. package/docs/index.md +1 -1
  108. package/docs/json.md +3 -3
  109. package/docs/keybindings.md +7 -6
  110. package/docs/llama-cpp.md +21 -1
  111. package/docs/mcp.md +272 -0
  112. package/docs/message-types.md +22 -4
  113. package/docs/models-configuration.md +187 -0
  114. package/docs/models.md +100 -2
  115. package/docs/nvidia-rtx-llama-cpp.md +96 -0
  116. package/docs/packages.md +1 -1
  117. package/docs/providers.md +66 -6
  118. package/docs/quickstart.md +16 -2
  119. package/docs/rpc-commands.md +9 -3
  120. package/docs/rpc.md +3 -1
  121. package/docs/sdk.md +8 -3
  122. package/docs/security.md +2 -0
  123. package/docs/session-format.md +5 -1
  124. package/docs/sessions.md +1 -1
  125. package/docs/settings.md +30 -8
  126. package/docs/terminal-setup.md +14 -0
  127. package/docs/themes.md +31 -7
  128. package/docs/tui.md +71 -1
  129. package/docs/usage.md +2 -2
  130. package/docs/virtual-models.md +114 -0
  131. package/docs/windows.md +2 -0
  132. package/examples/README.md +1 -4
  133. package/examples/extensions/README.md +1 -0
  134. package/examples/extensions/built-in-tool-renderer.ts +16 -40
  135. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  136. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  137. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  138. package/examples/extensions/custom-provider-gitlab-duo/test.ts +5 -5
  139. package/examples/extensions/dynamic-resources/dynamic.json +1 -1
  140. package/examples/extensions/gondolin/package-lock.json +2 -2
  141. package/examples/extensions/gondolin/package.json +1 -1
  142. package/examples/extensions/jev-router.ts +113 -0
  143. package/examples/extensions/minimal-mode.ts +28 -101
  144. package/examples/extensions/overlay-test.ts +2 -2
  145. package/examples/extensions/plan-mode/index.ts +6 -2
  146. package/examples/extensions/sandbox/package-lock.json +2 -2
  147. package/examples/extensions/sandbox/package.json +1 -1
  148. package/examples/extensions/subagent/index.ts +4 -6
  149. package/examples/extensions/with-deps/package-lock.json +2 -2
  150. package/examples/extensions/with-deps/package.json +1 -1
  151. package/examples/rpc-client.ts +1 -1
  152. package/examples/rpc-extension-ui.ts +1 -1
  153. package/examples/sdk/14-codemode-mcp.ts +61 -0
  154. package/examples/sdk/README.md +2 -1
  155. package/judge/THIRD_PARTY_NOTICES.md +63 -0
  156. package/judge/dist/auth.js +169 -123
  157. package/judge/dist/import.js +36 -30
  158. package/judge/dist/setup.js +250 -120
  159. package/judge/manifest.json +1008 -316
  160. package/judge/{dist/kyrn-judge.js → mu/index.js} +6933 -5995
  161. package/judge/themes/mu-dark.json +80 -0
  162. package/judge/themes/mu-light.json +80 -0
  163. package/kyrn/bin/kyrn-doctor +75 -25
  164. package/kyrn/bin/kyrn-judge-local +2 -2
  165. package/kyrn/bin/kyrn-ledger +1 -1
  166. package/kyrn/bin/mu.mjs +149 -107
  167. package/package.json +4 -5
  168. package/dist/bundle/chunks/anthropic-messages-VMESUHQ7.js +0 -6
  169. package/dist/bundle/chunks/azure-openai-responses-K232MQFF.js +0 -2
  170. package/dist/bundle/chunks/chunk-A3JYRWB6.js +0 -2
  171. package/dist/bundle/chunks/chunk-AYFRR3OD.js +0 -2
  172. package/dist/bundle/chunks/chunk-DTD7JQ7Y.js +0 -42
  173. package/dist/bundle/chunks/chunk-FU4XQUSL.js +0 -11
  174. package/dist/bundle/chunks/chunk-GNYZK6XN.js +0 -2
  175. package/dist/bundle/chunks/chunk-HEMXN7VJ.js +0 -1653
  176. package/dist/bundle/chunks/chunk-MVPXOVOJ.js +0 -2
  177. package/dist/bundle/chunks/chunk-NUHFSC37.js +0 -25
  178. package/dist/bundle/chunks/chunk-V7IXECST.js +0 -11
  179. package/dist/bundle/chunks/chunk-XNGRGP62.js +0 -2
  180. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-NR2TDCXU.js +0 -2
  181. package/dist/bundle/chunks/https-proxy-agent-2VXB7436.js +0 -13
  182. package/dist/bundle/chunks/mistral-conversations-6IHQOR4R.js +0 -5
  183. package/dist/bundle/chunks/node-O3RIBJRH.js +0 -16
  184. package/dist/bundle/chunks/openai-codex-responses-3UAYOSSR.js +0 -10
  185. package/dist/bundle/chunks/openai-completions-FIEQGWAH.js +0 -7
  186. package/dist/bundle/chunks/openai-responses-OZJXLHFC.js +0 -2
  187. package/dist/bundle/chunks/typesafe-system-one-VKYX4OGY.js +0 -2
  188. package/dist/bundle/chunks/virtual-modules-V7SXVVLP.js +0 -2
  189. package/dist/modes/interactive/theme/theme-json.d.ts +0 -85
  190. package/dist/modes/interactive/theme/theme-json.d.ts.map +0 -1
  191. package/dist/modes/interactive/theme/theme-json.js +0 -132
  192. package/dist/modes/interactive/theme/theme-json.js.map +0 -1
  193. package/dist/modes/interactive/theme/theme-schema.json +0 -361
  194. package/examples/plugins/pi-example-plugin/README.md +0 -38
  195. package/examples/plugins/pi-example-plugin/package.json +0 -10
  196. package/examples/plugins/pi-example-plugin/src/contract.ts +0 -13
  197. package/examples/plugins/pi-example-plugin/src/session.ts +0 -24
  198. package/examples/plugins/pi-example-plugin/src/tui.ts +0 -31
@@ -201,8 +201,11 @@ function backupStamp(now) {
201
201
  return `${now.getFullYear()}-${two(now.getMonth() + 1)}-${two(now.getDate())}T${two(now.getHours())}-${two(now.getMinutes())}-${two(now.getSeconds())}`;
202
202
  }
203
203
  var Backups = class {
204
+ root;
205
+ stamp;
206
+ made;
207
+ saved = /* @__PURE__ */ new Set();
204
208
  constructor(home2, now) {
205
- this.saved = /* @__PURE__ */ new Set();
206
209
  this.root = join2(home2, "backups");
207
210
  this.stamp = backupStamp(now);
208
211
  }
@@ -587,6 +590,10 @@ async function checkKey({
587
590
  fetch,
588
591
  timeoutMs = CHECK_TIMEOUT_MS
589
592
  }) {
593
+ if (kind === "opencode-key") {
594
+ const refused = await opencodeRefuses(baseUrl, key, fetch, timeoutMs);
595
+ if (refused) return { ok: false, problem: refused };
596
+ }
590
597
  const request = checkRequest(kind, baseUrl, key);
591
598
  const url = request.url.split("?")[0];
592
599
  let response;
@@ -634,42 +641,68 @@ function explainProblem(problem, context, language) {
634
641
  const host = hostOf(problem.url);
635
642
  const seconds = Math.round((context.timeoutMs ?? CHECK_TIMEOUT_MS) / 1e3);
636
643
  const local = /^(localhost|127\.|\[::1\])/.test(host);
637
- const proxyHint = language === "zh" ? context.proxy ? `\u8FD9\u6B21\u8BF7\u6C42\u8D70\u4E86\u4EE3\u7406\uFF08${context.proxy}\uFF09\uFF0C\u786E\u8BA4\u4EE3\u7406\u6B63\u5728\u8FD0\u884C\u3002` : "\u5982\u679C\u9700\u8981\u4EE3\u7406\uFF1A\u8BBE\u7F6E HTTPS_PROXY\uFF0C\u6216\u5728 ~/.mu/agent/settings.json \u91CC\u5199 httpProxy\uFF0C\u7136\u540E\u91CD\u65B0\u8FD0\u884C mu setup\u3002" : context.proxy ? `The request went through your proxy (${context.proxy}): check that it is running.` : "If you need a proxy, set HTTPS_PROXY (or httpProxy in ~/.mu/agent/settings.json) and run mu setup again.";
644
+ const proxyHint = language === "zh" ? context.proxy ? `\u8FD9\u6B21\u8BF7\u6C42\u8D70\u4E86\u4EE3\u7406\uFF08${context.proxy}\uFF09\uFF0C\u786E\u8BA4\u4EE3\u7406\u6B63\u5728\u8FD0\u884C\u3002` : "\u5982\u679C\u9700\u8981\u4EE3\u7406\uFF1A\u8BBE\u7F6E HTTPS_PROXY\uFF0C\u6216\u5728 ~/.mu/agent/settings.json \u91CC\u5199 httpProxy\uFF0C\u7136\u540E\u91CD\u65B0\u8FD0\u884C mu setup\u3002" : context.proxy ? `The request went through your proxy (${context.proxy}). Check that it's running.` : "If you need a proxy, set HTTPS_PROXY (or httpProxy in ~/.mu/agent/settings.json) and run mu setup again.";
638
645
  const said = problem.said ? language === "zh" ? `\uFF08\u670D\u52A1\u7684\u539F\u8BDD\uFF1A${problem.said}\uFF09` : ` (The service said: ${problem.said})` : "";
639
646
  const zh = language === "zh";
640
647
  switch (problem.kind) {
641
648
  case "key":
642
- return zh ? `${service} \u4E0D\u63A5\u53D7\u8FD9\u4E2A\u5BC6\u94A5\u3002\u68C0\u67E5\u662F\u5426\u5B8C\u6574\u590D\u5236\uFF0C\u4EE5\u53CA\u5B83\u786E\u5B9E\u662F ${service} \u7684\u5BC6\u94A5\u3002` : `${service} does not accept this key. Check that you copied all of it, and that it is ${article(service)} key.`;
649
+ return zh ? `${service} \u4E0D\u63A5\u53D7\u8FD9\u4E2A\u5BC6\u94A5\u3002\u68C0\u67E5\u662F\u5426\u5B8C\u6574\u590D\u5236\uFF0C\u4EE5\u53CA\u5B83\u786E\u5B9E\u662F ${service} \u7684\u5BC6\u94A5\u3002` : `${service} didn't accept this key. Check that you copied all of it and that it's ${article(service)} key.`;
643
650
  case "refused":
644
- return zh ? `${service} \u62D2\u7EDD\u4E86\u8FD9\u4E2A\u5BC6\u94A5\uFF08HTTP 403\uFF09\uFF1A\u53EF\u80FD\u662F\u5BC6\u94A5\u4E0D\u5BF9\uFF0C\u6216\u5B83\u6CA1\u6709\u8BBF\u95EE\u6743\u9650\u3002${said}` : `${service} refused this key (HTTP 403): it may be wrong, or not allowed access.${said}`;
651
+ return zh ? `${service} \u62D2\u7EDD\u4E86\u8FD9\u4E2A\u5BC6\u94A5\uFF08HTTP 403\uFF09\uFF1A\u53EF\u80FD\u662F\u5BC6\u94A5\u4E0D\u5BF9\uFF0C\u6216\u5B83\u6CA1\u6709\u8BBF\u95EE\u6743\u9650\u3002${said}` : `${service} refused this key (HTTP 403). The key may be wrong or lack access.${said}`;
645
652
  case "permission":
646
- return zh ? "\u8FD9\u4E2A\u5BC6\u94A5\u6709\u6548\uFF0C\u4F46\u6CA1\u6709\u8BFB\u53D6\u6A21\u578B\u5217\u8868\u7684\u6743\u9650\uFF08\u53D7\u9650\u5BC6\u94A5\uFF09\u3002\u7ED9\u5B83\u8BFB\u53D6\u6A21\u578B\u7684\u6743\u9650\uFF0C\u6216\u6362\u4E00\u4E2A\u5BC6\u94A5\u3002" : "The key works, but may not read the list of models (a restricted key). Allow it to read models, or use another key.";
653
+ return zh ? "\u8FD9\u4E2A\u5BC6\u94A5\u6709\u6548\uFF0C\u4F46\u6CA1\u6709\u8BFB\u53D6\u6A21\u578B\u5217\u8868\u7684\u6743\u9650\uFF08\u53D7\u9650\u5BC6\u94A5\uFF09\u3002\u7ED9\u5B83\u8BFB\u53D6\u6A21\u578B\u7684\u6743\u9650\uFF0C\u6216\u6362\u4E00\u4E2A\u5BC6\u94A5\u3002" : "The key works but can't list models (it's a restricted key). Give it permission to read models, or use another key.";
647
654
  case "balance":
648
655
  return zh ? `${service} \u7684\u8D26\u6237\u6CA1\u6709\u4F59\u989D\u6216\u989D\u5EA6\u4E86\u3002\u5145\u503C\u540E\u91CD\u65B0\u8FD0\u884C mu setup\u3002` : `The ${service} account has no balance or quota left. Top it up, then run mu setup again.`;
649
656
  case "region":
650
- return zh ? `${service} \u4E0D\u5411\u4F60\u6240\u5728\u7684\u5730\u533A\u63D0\u4F9B\u670D\u52A1\u3002\u9700\u8981\u4E00\u4E2A\u4F4D\u4E8E\u652F\u6301\u5730\u533A\u7684\u4EE3\u7406\u6216 VPN\u3002${proxyHint}` : `${service} does not serve the region this request came from. A proxy or VPN in a supported region is needed. ${proxyHint}`;
657
+ return zh ? `${service} \u4E0D\u5411\u4F60\u6240\u5728\u7684\u5730\u533A\u63D0\u4F9B\u670D\u52A1\u3002\u9700\u8981\u4E00\u4E2A\u4F4D\u4E8E\u652F\u6301\u5730\u533A\u7684\u4EE3\u7406\u6216 VPN\u3002${proxyHint}` : `${service} isn't available in the region this request came from. You need a proxy or VPN in a supported region. ${proxyHint}`;
651
658
  case "rate":
652
- return zh ? `${service} \u8BF4\u8BF7\u6C42\u592A\u9891\u7E41\u3002\u7B49\u4E00\u5206\u949F\u518D\u8BD5\u3002` : `${service} says there were too many requests. Wait a minute and try again.`;
659
+ return zh ? `${service} \u8BF4\u8BF7\u6C42\u592A\u9891\u7E41\u3002\u7B49\u4E00\u5206\u949F\u518D\u8BD5\u3002` : `${service} is getting too many requests from you. Wait a minute and try again.`;
653
660
  case "address":
654
- return zh ? `${problem.url} \u8FD9\u4E2A\u5730\u5740\u4E0D\u5B58\u5728\uFF08HTTP 404\uFF09\uFF1A\u5730\u5740\u586B\u9519\u4E86\u3002` : `Nothing answers at ${problem.url} (HTTP 404): the address is wrong.`;
661
+ return zh ? `${problem.url} \u8FD9\u4E2A\u5730\u5740\u4E0D\u5B58\u5728\uFF08HTTP 404\uFF09\uFF1A\u5730\u5740\u586B\u9519\u4E86\u3002` : `Nothing was found at ${problem.url} (HTTP 404). Check the address.`;
655
662
  case "server":
656
- return zh ? `${service} \u81EA\u5DF1\u51FA\u9519\u4E86\uFF08HTTP ${problem.status}\uFF09\u3002\u8FC7\u4E00\u4F1A\u513F\u518D\u8BD5\u3002` : `${service} had an error of its own (HTTP ${problem.status}). Try again in a while.`;
663
+ return zh ? `${service} \u81EA\u5DF1\u51FA\u9519\u4E86\uFF08HTTP ${problem.status}\uFF09\u3002\u8FC7\u4E00\u4F1A\u513F\u518D\u8BD5\u3002` : `${service} returned a server error (HTTP ${problem.status}). Try again later.`;
657
664
  case "response":
658
- return zh ? `${problem.url} \u6709\u56DE\u5E94\uFF0C\u4F46\u4E0D\u662F\u6A21\u578B\u5217\u8868\uFF1A\u68C0\u67E5\u5730\u5740\u548C\u63A5\u53E3\u7C7B\u578B\u3002` : `${problem.url} answered, but not with a list of models: check the address and the API type.`;
665
+ return zh ? `${problem.url} \u6709\u56DE\u5E94\uFF0C\u4F46\u4E0D\u662F\u6A21\u578B\u5217\u8868\uFF1A\u68C0\u67E5\u5730\u5740\u548C\u63A5\u53E3\u7C7B\u578B\u3002` : `${problem.url} responded, but not with a list of models. Check the address and the API type.`;
659
666
  case "timeout":
660
- return zh ? `${seconds} \u79D2\u5185 ${host} \u6CA1\u6709\u56DE\u5E94\u3002\u7F51\u7EDC\u53EF\u80FD\u4E0D\u901A\uFF1A\u6709\u4E9B\u5730\u533A\u8981\u7528\u4EE3\u7406\u6216 VPN \u624D\u80FD\u8BBF\u95EE\u3002${proxyHint}` : `No answer from ${host} within ${seconds} s. The network may block it: in some regions a proxy or VPN is needed. ${proxyHint}`;
667
+ return zh ? `${seconds} \u79D2\u5185 ${host} \u6CA1\u6709\u56DE\u5E94\u3002\u7F51\u7EDC\u53EF\u80FD\u4E0D\u901A\uFF1A\u6709\u4E9B\u5730\u533A\u8981\u7528\u4EE3\u7406\u6216 VPN \u624D\u80FD\u8BBF\u95EE\u3002${proxyHint}` : `No response from ${host} within ${seconds} s. The network may be blocking it; some regions need a proxy or VPN. ${proxyHint}`;
661
668
  case "dns":
662
- return zh ? `\u627E\u4E0D\u5230 ${host}\uFF1A\u6CA1\u6709\u8054\u7F51\uFF0C\u6216\u8FD9\u91CC\u89E3\u6790\u4E0D\u4E86\u8FD9\u4E2A\u57DF\u540D\u3002${proxyHint}` : `Could not find ${host}: there is no internet connection, or the name cannot be resolved here. ${proxyHint}`;
669
+ return zh ? `\u627E\u4E0D\u5230 ${host}\uFF1A\u6CA1\u6709\u8054\u7F51\uFF0C\u6216\u8FD9\u91CC\u89E3\u6790\u4E0D\u4E86\u8FD9\u4E2A\u57DF\u540D\u3002${proxyHint}` : `Couldn't resolve ${host}. Check your internet connection and DNS. ${proxyHint}`;
663
670
  case "connection":
664
671
  if (local) {
665
- return zh ? `${host} \u4E0A\u6CA1\u6709\u670D\u52A1\u5728\u76D1\u542C\uFF1A\u5B83\u542F\u52A8\u4E86\u5417\uFF1F` : `Nothing is listening at ${host}: is the service running?`;
672
+ return zh ? `${host} \u4E0A\u6CA1\u6709\u670D\u52A1\u5728\u76D1\u542C\uFF1A\u5B83\u542F\u52A8\u4E86\u5417\uFF1F` : `Nothing is listening at ${host}. Is the service running?`;
666
673
  }
667
- return zh ? `\u8FDE\u4E0D\u4E0A ${host}\u3002${proxyHint}` : `Could not connect to ${host}. ${proxyHint}`;
674
+ return zh ? `\u8FDE\u4E0D\u4E0A ${host}\u3002${proxyHint}` : `Couldn't connect to ${host}. ${proxyHint}`;
668
675
  case "tls":
669
- return zh ? `\u548C ${host} \u7684\u5B89\u5168\u8FDE\u63A5\u5931\u8D25\u4E86\uFF1A\u53EF\u80FD\u6709\u4EE3\u7406\u6216\u9632\u706B\u5899\u5728\u62E6\u622A HTTPS\u3002` : `The secure connection to ${host} failed: a proxy or firewall may be intercepting HTTPS.`;
676
+ return zh ? `\u548C ${host} \u7684\u5B89\u5168\u8FDE\u63A5\u5931\u8D25\u4E86\uFF1A\u53EF\u80FD\u6709\u4EE3\u7406\u6216\u9632\u706B\u5899\u5728\u62E6\u622A HTTPS\u3002` : `The secure connection to ${host} failed. A proxy or firewall may be intercepting HTTPS.`;
670
677
  default:
671
- return zh ? `${service} \u62D2\u7EDD\u4E86\u8FD9\u6B21\u68C0\u67E5\uFF08HTTP ${problem.status ?? "?"}\uFF09\u3002${said}` : `${service} refused the check (HTTP ${problem.status ?? "?"}).${said}`;
678
+ return zh ? `${service} \u62D2\u7EDD\u4E86\u8FD9\u6B21\u68C0\u67E5\uFF08HTTP ${problem.status ?? "?"}\uFF09\u3002${said}` : `${service} rejected the check (HTTP ${problem.status ?? "?"}).${said}`;
679
+ }
680
+ }
681
+ async function opencodeRefuses(baseUrl, key, fetch, timeoutMs) {
682
+ const url = `${baseUrl.replace(/\/+$/, "")}/systemone`;
683
+ let response;
684
+ try {
685
+ response = await fetch(url, {
686
+ method: "POST",
687
+ headers: { "content-type": "application/json", ...key ? { authorization: `Bearer ${key}` } : {} },
688
+ body: JSON.stringify({
689
+ model: "jev-1.13-free",
690
+ state: "mu setup is checking a key.",
691
+ questions: { check: { type: "noul", instructions: "Is this a key check?" } }
692
+ }),
693
+ signal: AbortSignal.timeout(timeoutMs)
694
+ });
695
+ } catch (error) {
696
+ return { kind: classifyFailure(error), url };
672
697
  }
698
+ if (response.ok) return void 0;
699
+ const text = await response.text().catch(() => "");
700
+ return {
701
+ kind: classifyAnswer(response.status, text),
702
+ url,
703
+ status: response.status,
704
+ said: safeMessage(messageOf(text), key) || void 0
705
+ };
673
706
  }
674
707
 
675
708
  // packages/kyrn-judge/src/setup/services.ts
@@ -773,6 +806,17 @@ var SETUP_SERVICES = [
773
806
  builtIn: true,
774
807
  check: "openrouter-key"
775
808
  },
809
+ {
810
+ id: "opencode",
811
+ name: "OpenCode Zen",
812
+ baseUrls: ["https://opencode.ai/inference/v1"],
813
+ api: "openai-completions",
814
+ models: ["kimi-k2.6"],
815
+ keyPage: "https://opencode.ai/auth",
816
+ providers: ["opencode"],
817
+ builtIn: true,
818
+ check: "opencode-key"
819
+ },
776
820
  {
777
821
  id: "openai",
778
822
  name: "OpenAI",
@@ -915,7 +959,16 @@ var SETUP_FLAGS = {
915
959
  "--key-stdin": "keyStdin"
916
960
  };
917
961
  var JEV_KEY_NAMES = ["TYPESAFE_API_KEY", "MU_JUDGE_OPENROUTER_API_KEY", "AI_GATEWAY_API_KEY"];
918
- var JEV_TIERS = /* @__PURE__ */ new Set(["jev", "jev-direct", "jev-openrouter", "jev-gateway"]);
962
+ var JEV_TIERS = /* @__PURE__ */ new Set([
963
+ "jev",
964
+ "jev-direct",
965
+ "jev-openrouter",
966
+ "jev-gateway",
967
+ "jev-opencode",
968
+ "jev-opencode-free",
969
+ "jev-cloudflare"
970
+ ]);
971
+ var OPENCODE_KEY = "OPENCODE_API_KEY";
919
972
  var JEV_PAGE = "https://typesafe.ai";
920
973
  var BRANDED = /* @__PURE__ */ new Set(["openai", "anthropic", "openrouter", "google", "xai"]);
921
974
  var OTHER = {
@@ -924,7 +977,7 @@ var OTHER = {
924
977
  };
925
978
  var NOT_READY = {
926
979
  zh: "Ollama \u8FD8\u6CA1\u51C6\u5907\u597D\uFF08\u7528 `ollama serve` \u542F\u52A8\u5B83\uFF0C\u518D\u62C9\u53D6\u4E00\u4E2A\u6A21\u578B\uFF09\u3002",
927
- en: "Ollama is not ready yet (start it with `ollama serve`, pull a model)."
980
+ en: "Ollama isn't ready yet. Start it with `ollama serve`, then pull a model."
928
981
  };
929
982
  function parseSetupArgs(argv) {
930
983
  const values = /* @__PURE__ */ new Map();
@@ -947,22 +1000,27 @@ function parseSetupArgs(argv) {
947
1000
  }
948
1001
  const api = values.get("--api");
949
1002
  if (api !== void 0 && api !== "openai" && api !== "anthropic") {
950
- return { error: { zh: "--api \u53EA\u80FD\u662F openai \u6216 anthropic", en: "--api is openai or anthropic" } };
1003
+ return { error: { zh: "--api \u53EA\u80FD\u662F openai \u6216 anthropic", en: "--api must be openai or anthropic" } };
951
1004
  }
952
1005
  const judge = values.get("--judge");
953
- if (judge !== void 0 && judge !== "jev" && judge !== "model" && judge !== "none") {
954
- return { error: { zh: "--judge \u53EA\u80FD\u662F jev\u3001model \u6216 none", en: "--judge is jev, model or none" } };
1006
+ if (judge !== void 0 && !["jev", "opencode", "free", "model", "none"].includes(judge)) {
1007
+ return {
1008
+ error: {
1009
+ zh: "--judge \u53EA\u80FD\u662F jev\u3001opencode\u3001free\u3001model \u6216 none",
1010
+ en: "--judge must be jev, opencode, free, model or none"
1011
+ }
1012
+ };
955
1013
  }
956
1014
  const service = values.get("--service");
957
1015
  if (service !== void 0 && !serviceById(service)) {
958
1016
  return {
959
1017
  error: {
960
1018
  zh: `\u6CA1\u6709\u53EB ${service} \u7684\u670D\u52A1\uFF08mu setup --list \u5217\u51FA\u5168\u90E8\uFF09`,
961
- en: `there is no service called ${service} (mu setup --list shows them)`
1019
+ en: `no service named ${service} (mu setup --list shows them all)`
962
1020
  }
963
1021
  };
964
1022
  }
965
- if (words.length > 1) return { error: { zh: "\u53EA\u80FD\u7ED9\u4E00\u4E2A\u5BC6\u94A5", en: "give one key at most" } };
1023
+ if (words.length > 1) return { error: { zh: "\u53EA\u80FD\u7ED9\u4E00\u4E2A\u5BC6\u94A5", en: "give at most one key" } };
966
1024
  return {
967
1025
  help: flags.has("help"),
968
1026
  list: flags.has("list"),
@@ -985,7 +1043,7 @@ function setupUsage(language) {
985
1043
  "",
986
1044
  " mu setup \u5728\u7EC8\u7AEF\u91CC\u4E00\u6B65\u4E00\u6B65\u95EE",
987
1045
  " mu setup <\u5BC6\u94A5> \u4ECE\u4E00\u4E2A API \u5BC6\u94A5\u5F00\u59CB\uFF1Amu \u6309\u5BC6\u94A5\u7684\u6837\u5B50\u8BA4\u51FA\u5B83\u5C5E\u4E8E\u54EA\u4E2A\u670D\u52A1",
988
- " mu setup --service <id> --key-stdin [--model <id>] [--base-url <\u5730\u5740>] [--judge jev|model|none] [--yes]",
1046
+ " mu setup --service <id> --key-stdin [--model <id>] [--base-url <\u5730\u5740>] [--judge <\u5224\u5B9A\u5668>] [--yes]",
989
1047
  " \u7ED9\u811A\u672C\u7528\uFF1A\u5BC6\u94A5\u4ECE\u6807\u51C6\u8F93\u5165\u8BFB\u53D6",
990
1048
  " mu setup --list mu \u8BA4\u8BC6\u7684\u670D\u52A1\u548C\u5B83\u4EEC\u7684 id",
991
1049
  "",
@@ -993,6 +1051,8 @@ function setupUsage(language) {
993
1051
  "\u8FD9\u53F0\u7535\u8111\u4E0A\u80FD\u5217\u51FA\u8FDB\u7A0B\u7684\u7A0B\u5E8F\u4E5F\u90FD\u770B\u5F97\u5230\u5B83\u3002mu setup <\u5BC6\u94A5> \u53EA\u662F\u4E3A\u4E86\u4E0A\u624B\u5FEB\uFF1B\u80FD\u5728\u63D0\u793A\u91CC\u7C98\u8D34\u5C31\u7C98\u8D34\u3002",
994
1052
  "",
995
1053
  " --judge jev Jev \u5BC6\u94A5\uFF08TypeSafe\uFF09\u5199\u5728\u6807\u51C6\u8F93\u5165\u7684\u7B2C\u4E8C\u884C\uFF1B\u5DF2\u7ECF\u8BBE\u7F6E\u4E86 TYPESAFE_API_KEY \u5C31\u4E0D\u7528",
1054
+ " --judge opencode OpenCode Zen \u4E0A\u7684 Jev\uFF0C\u7528 OpenCode \u7684\u5BC6\u94A5\uFF08--service opencode \u6216 OPENCODE_API_KEY\uFF09",
1055
+ " --judge free OpenCode Zen \u4E0A\u9650\u65F6\u514D\u8D39\u7684 Jev\uFF0C\u4E0D\u7528\u5BC6\u94A5\uFF08\u6CA1\u6709\u5BC6\u94A5\u65F6\u672C\u6765\u5C31\u7528\u5B83\uFF09",
996
1056
  " --judge model \u7528\u521A\u8BBE\u7F6E\u7684\u6A21\u578B\u5F53\u5224\u5B9A\u5668\uFF08\u66F4\u6162\uFF0C\u6BCF\u6B21\u5224\u65AD\u90FD\u82B1 token\uFF09",
997
1057
  " --judge none \u4E0D\u6539\u5224\u5B9A\u5668\uFF1B\u4E0D\u5199 --judge \u65F6\u4E5F\u4E0D\u6539",
998
1058
  " --api, --name \u670D\u52A1 other \u7684\u63A5\u53E3\uFF08openai \u6216 anthropic\uFF09\u548C\u5B83\u5728 mu \u91CC\u7684\u540D\u5B57",
@@ -1003,27 +1063,29 @@ function setupUsage(language) {
1003
1063
  ].join("\n");
1004
1064
  }
1005
1065
  return [
1006
- "mu setup: connect a model for mu to work with, and choose its judge.",
1066
+ "mu setup: connect a model for mu to work with, and choose a judge.",
1007
1067
  "",
1008
- " mu setup asks step by step, in a terminal",
1009
- " mu setup <key> starts from an API key: mu tells by its shape which service it belongs to",
1010
- " mu setup --service <id> --key-stdin [--model <id>] [--base-url <url>] [--judge jev|model|none] [--yes]",
1011
- " for scripts: the key is read from stdin",
1012
- " mu setup --list the services mu knows, with their ids",
1068
+ " mu setup walks you through it, one question at a time",
1069
+ " mu setup <key> starts from an API key and detects its service from the key's format",
1070
+ " mu setup --service <id> --key-stdin [--model <id>] [--base-url <url>] [--judge <judge>] [--yes]",
1071
+ " for scripts: reads the key from stdin",
1072
+ " mu setup --list lists the services mu knows and their ids",
1013
1073
  "",
1014
- "Why stdin for scripts: a key on the command line stays in your shell's history, and every program on this",
1015
- "computer that lists processes can read it. `mu setup <key>` is there for a quick start; paste the key at the",
1016
- "prompt when you can.",
1074
+ "Why stdin for scripts: a key on the command line stays in your shell history, and any program on this",
1075
+ "computer that lists processes can read it. `mu setup <key>` is a shortcut for getting started; paste the key",
1076
+ "at the prompt when you can.",
1017
1077
  "",
1018
- " --judge jev the Jev key (TypeSafe) is the second line of stdin, unless TYPESAFE_API_KEY is set already",
1019
- " --judge model the model just set up answers as the judge (slower, and every decision costs tokens)",
1020
- " --judge none leaves the judge as it is, as leaving out --judge does",
1078
+ " --judge jev read the Jev key (TypeSafe) from stdin's second line, unless TYPESAFE_API_KEY is already set",
1079
+ " --judge opencode use Jev on OpenCode Zen with an OpenCode key (--service opencode or OPENCODE_API_KEY)",
1080
+ " --judge free use the free Jev on OpenCode Zen (limited time, no key; the default while no key is set)",
1081
+ " --judge model use the model you just set up as the judge (slower, and every verdict costs tokens)",
1082
+ " --judge none leave the judge unchanged (the same as omitting --judge)",
1021
1083
  " --api, --name for the service `other`: its API (openai or anthropic) and its name in mu",
1022
- " --yes no questions; replaces what is set up already (a subscription sign-in, a provider's",
1023
- " address, the Jev key)",
1084
+ " --yes don't ask; replace what's already set up (a subscription sign-in, a provider's address,",
1085
+ " the Jev key)",
1024
1086
  "",
1025
- "Files: auth.json (keys), models.json, settings.json and mu.json in ~/.mu/agent, and the .env mu reads",
1026
- "(the Jev key). Each is copied to ~/.mu/backups/<time>/ before it changes."
1087
+ "Files it changes: auth.json (keys), models.json, settings.json and mu.json in ~/.mu/agent, and the .env",
1088
+ "mu reads (the Jev key). Each one is backed up to ~/.mu/backups/<time>/ before it changes."
1027
1089
  ].join("\n");
1028
1090
  }
1029
1091
  function label(service, language) {
@@ -1070,12 +1132,25 @@ function jevKeyAt(env, envFileText) {
1070
1132
  return void 0;
1071
1133
  }
1072
1134
  var Stop = class extends Error {
1135
+ code;
1073
1136
  constructor(code) {
1074
1137
  super(`mu setup stopped (${code})`);
1075
1138
  this.code = code;
1076
1139
  }
1077
1140
  };
1078
1141
  var Setup = class {
1142
+ args;
1143
+ deps;
1144
+ io;
1145
+ language;
1146
+ /** No questions: a script, or --yes. */
1147
+ scripted;
1148
+ /** The second line of stdin, for --judge jev. */
1149
+ jevFromStdin;
1150
+ /** The key given for the model: a Jev key is never the same one. */
1151
+ modelKey;
1152
+ /** The provider id a service of one's own (`other`) gets in mu. */
1153
+ customName;
1079
1154
  constructor(args, deps) {
1080
1155
  this.args = args;
1081
1156
  this.deps = deps;
@@ -1129,7 +1204,7 @@ var Setup = class {
1129
1204
  }
1130
1205
  pasteKey() {
1131
1206
  return this.secret(
1132
- this.t({ zh: "\u7C98\u8D34 API \u5BC6\u94A5\uFF08\u4E0D\u4F1A\u663E\u793A\u51FA\u6765\uFF09\uFF1A", en: "Paste the API key (it is not shown): " })
1207
+ this.t({ zh: "\u7C98\u8D34 API \u5BC6\u94A5\uFF08\u4E0D\u4F1A\u663E\u793A\u51FA\u6765\uFF09\uFF1A", en: "Paste the API key (it won't be shown): " })
1133
1208
  );
1134
1209
  }
1135
1210
  async choose(question, choices) {
@@ -1141,7 +1216,7 @@ var Setup = class {
1141
1216
  if (answer === "") return choices[0].value;
1142
1217
  const number = Number(answer);
1143
1218
  if (Number.isInteger(number) && number >= 1 && number <= choices.length) return choices[number - 1].value;
1144
- this.io.say(this.t({ zh: `\u8BF7\u8F93\u5165 ${range} \u4E4B\u95F4\u7684\u6570\u5B57\u3002`, en: `Type a number from ${range}.` }));
1219
+ this.io.say(this.t({ zh: `\u8BF7\u8F93\u5165 ${range} \u4E4B\u95F4\u7684\u6570\u5B57\u3002`, en: `Type a number from 1 to ${choices.length}.` }));
1145
1220
  }
1146
1221
  }
1147
1222
  async confirm(question, yes) {
@@ -1161,7 +1236,7 @@ var Setup = class {
1161
1236
  throw this.fail(
1162
1237
  {
1163
1238
  zh: "--key-stdin \u4ECE\u7BA1\u9053\u8BFB\u5BC6\u94A5\u3002\u5728\u7EC8\u7AEF\u91CC\u8BF7\u76F4\u63A5\u8FD0\u884C mu setup\uFF0C\u5728\u63D0\u793A\u91CC\u7C98\u8D34\u5BC6\u94A5\uFF08\u4E0D\u4F1A\u663E\u793A\uFF09\u3002",
1164
- en: "--key-stdin reads the key from a pipe. In a terminal, run mu setup and paste the key at its prompt (it is not shown)."
1239
+ en: "--key-stdin reads the key from a pipe. In a terminal, run mu setup and paste the key at the prompt (it won't be shown)."
1165
1240
  },
1166
1241
  SETUP_EXIT.usage
1167
1242
  );
@@ -1172,9 +1247,9 @@ var Setup = class {
1172
1247
  }
1173
1248
  if (this.scripted && key === void 0 && args.service === void 0) {
1174
1249
  throw this.fail(
1175
- deps.interactive ? { zh: "\u6CA1\u6709\u5BC6\u94A5\uFF0C\u4E5F\u6CA1\u6709 --service", en: "no key and no --service were given" } : {
1250
+ deps.interactive ? { zh: "\u6CA1\u6709\u5BC6\u94A5\uFF0C\u4E5F\u6CA1\u6709 --service", en: "no key or --service given" } : {
1176
1251
  zh: "\u95EE\u9898\u8981\u5728\u7EC8\u7AEF\u91CC\u56DE\u7B54\u3002\u811A\u672C\u91CC\u7528\uFF1Amu setup --service <id> --key-stdin\uFF08\u89C1 mu setup --help\uFF09",
1177
- en: "its questions need a terminal. For a script: mu setup --service <id> --key-stdin (see mu setup --help)"
1252
+ en: "no terminal to ask in. In a script, use mu setup --service <id> --key-stdin (see mu setup --help)"
1178
1253
  },
1179
1254
  SETUP_EXIT.usage
1180
1255
  );
@@ -1183,7 +1258,7 @@ var Setup = class {
1183
1258
  this.io.say(
1184
1259
  this.t({
1185
1260
  zh: "mu setup\uFF1A\u7ED9 mu \u63A5\u4E0A\u4E00\u4E2A\u5E72\u6D3B\u7684\u6A21\u578B\uFF0C\u518D\u9009\u4E00\u4E2A\u5224\u5B9A\u5668\u3002",
1186
- en: "mu setup: connect a model for mu to work with, and choose its judge."
1261
+ en: "mu setup: connect a model for mu to work with, and choose a judge."
1187
1262
  })
1188
1263
  );
1189
1264
  }
@@ -1221,7 +1296,7 @@ var Setup = class {
1221
1296
  this.io.say(
1222
1297
  this.t({
1223
1298
  zh: "\u597D\u7684\uFF0C\u4EE5\u540E\u4E0D\u518D\u95EE\u4E86\u3002\u9700\u8981\u65F6\u8FD0\u884C mu setup\uFF0C\u6216\u5728 mu \u91CC\u8F93\u5165 /login\u3002",
1224
- en: "OK, mu will not ask again. Run mu setup when you want, or type /login inside mu."
1299
+ en: "OK, mu won't ask again. Run mu setup whenever you're ready, or type /login inside mu."
1225
1300
  })
1226
1301
  );
1227
1302
  return false;
@@ -1233,29 +1308,32 @@ var Setup = class {
1233
1308
  const named2 = this.args.service === void 0 ? void 0 : serviceById(this.args.service);
1234
1309
  if (named2) return this.keyFor(named2, key);
1235
1310
  if (key !== void 0) return { service: await this.serviceOf(key), key };
1236
- const route = await this.choose(this.t({ zh: "mu \u7528\u4EC0\u4E48\u65B9\u5F0F\u8FDE\u63A5\u6A21\u578B\uFF1F", en: "How should mu reach a model?" }), [
1237
- {
1238
- label: this.t({
1239
- zh: "\u7C98\u8D34 API \u5BC6\u94A5\uFF08mu \u4F1A\u8BA4\u51FA\u5B83\u5C5E\u4E8E\u54EA\u4E2A\u670D\u52A1\uFF09",
1240
- en: "Paste an API key (mu tells which service it belongs to)"
1241
- }),
1242
- value: "key"
1243
- },
1244
- { label: this.t({ zh: "\u4ECE\u5217\u8868\u91CC\u9009\u4E00\u4E2A\u670D\u52A1", en: "Choose a service from the list" }), value: "list" },
1245
- {
1246
- label: this.t({
1247
- zh: "\u7528\u8BA2\u9605\u8D26\u53F7\u767B\u5F55\uFF1AChatGPT\u3001Claude\u3001Grok \u6216 Google",
1248
- en: "Sign in with a subscription: ChatGPT, Claude, Grok or Google"
1249
- }),
1250
- value: "subscription"
1251
- }
1252
- ]);
1311
+ const route = await this.choose(
1312
+ this.t({ zh: "mu \u7528\u4EC0\u4E48\u65B9\u5F0F\u8FDE\u63A5\u6A21\u578B\uFF1F", en: "How should mu connect to a model?" }),
1313
+ [
1314
+ {
1315
+ label: this.t({
1316
+ zh: "\u7C98\u8D34 API \u5BC6\u94A5\uFF08mu \u4F1A\u8BA4\u51FA\u5B83\u5C5E\u4E8E\u54EA\u4E2A\u670D\u52A1\uFF09",
1317
+ en: "Paste an API key (mu detects which service it belongs to)"
1318
+ }),
1319
+ value: "key"
1320
+ },
1321
+ { label: this.t({ zh: "\u4ECE\u5217\u8868\u91CC\u9009\u4E00\u4E2A\u670D\u52A1", en: "Choose a service from the list" }), value: "list" },
1322
+ {
1323
+ label: this.t({
1324
+ zh: "\u7528\u8BA2\u9605\u8D26\u53F7\u767B\u5F55\uFF1AChatGPT\u3001Claude\u3001Grok \u6216 Google",
1325
+ en: "Sign in with a subscription: ChatGPT, Claude, Grok or Google"
1326
+ }),
1327
+ value: "subscription"
1328
+ }
1329
+ ]
1330
+ );
1253
1331
  if (route === "subscription") {
1254
1332
  const auth = this.path(join3(this.deps.paths.agentDir, "auth.json"));
1255
1333
  this.io.say(
1256
1334
  this.t({
1257
1335
  zh: `\u8BA2\u9605\u8D26\u53F7\u5728 mu \u91CC\u9762\u767B\u5F55\uFF1A\u542F\u52A8 mu\uFF0C\u8F93\u5165 /login\uFF0C\u9009 ChatGPT\uFF08OpenAI Codex\uFF09\u3001Claude\u3001Grok \u6216 Google\uFF0C\u6309\u63D0\u793A\u5728\u6D4F\u89C8\u5668\u91CC\u5B8C\u6210\u3002\u767B\u5F55\u4FE1\u606F\u4FDD\u5B58\u5728 ${auth}\u3002`,
1258
- en: `A subscription signs in inside mu: start mu, type /login, choose ChatGPT (OpenAI Codex), Claude, Grok or Google, and finish in the browser. The sign-in is kept in ${auth}.`
1336
+ en: `Subscriptions sign in from inside mu: start mu, type /login, choose ChatGPT (OpenAI Codex), Claude, Grok or Google, and finish in the browser. The sign-in is saved in ${auth}.`
1259
1337
  })
1260
1338
  );
1261
1339
  return "subscription";
@@ -1289,10 +1367,10 @@ var Setup = class {
1289
1367
  throw this.fail(
1290
1368
  matches.length > 1 ? {
1291
1369
  zh: `${this.names(matches)} \u7684\u5BC6\u94A5\u957F\u5F97\u4E00\u6837\u3002\u7528 --service \u8BF4\u660E\u662F\u54EA\u5BB6\uFF1A${matches.map((service) => service.id).join(" \u6216 ")}`,
1292
- en: `keys of ${this.names(matches)} look the same. Say which with --service ${matches.map((service) => service.id).join(" or ")}`
1370
+ en: `${this.names(matches)} keys look the same. Choose one with --service ${matches.map((service) => service.id).join(" or ")}`
1293
1371
  } : {
1294
1372
  zh: "\u8BA4\u4E0D\u51FA\u8FD9\u4E2A\u5BC6\u94A5\u5C5E\u4E8E\u54EA\u4E2A\u670D\u52A1\u3002\u7528 --service <id> \u8BF4\u660E\uFF08mu setup --list \u5217\u51FA\u5168\u90E8\uFF09",
1295
- en: "this key's shape names no service mu knows. Say which with --service <id> (mu setup --list)"
1373
+ en: "mu doesn't recognize this key's format. Say which service it's for with --service <id> (mu setup --list)"
1296
1374
  },
1297
1375
  SETUP_EXIT.usage
1298
1376
  );
@@ -1302,7 +1380,7 @@ var Setup = class {
1302
1380
  matches,
1303
1381
  this.t({
1304
1382
  zh: `${this.names(matches)} \u7684\u5BC6\u94A5\u957F\u5F97\u4E00\u6837\u3002\u8FD9\u4E2A\u5BC6\u94A5\u662F\u54EA\u5BB6\u7684\uFF1Fmu \u53EA\u628A\u5BC6\u94A5\u53D1\u7ED9\u5B83\u6240\u5C5E\u7684\u670D\u52A1\u3002`,
1305
- en: `Keys of ${this.names(matches)} look the same. Which service is this key for? mu sends a key only to the service it belongs to.`
1383
+ en: `${this.names(matches)} keys look the same. Which service is this key for? mu only sends a key to the service it belongs to.`
1306
1384
  })
1307
1385
  );
1308
1386
  }
@@ -1310,7 +1388,7 @@ var Setup = class {
1310
1388
  SETUP_SERVICES.filter((service) => !service.keyless),
1311
1389
  this.t({
1312
1390
  zh: "mu \u8BA4\u4E0D\u51FA\u8FD9\u4E2A\u5BC6\u94A5\u3002\u5B83\u662F\u54EA\u4E2A\u670D\u52A1\u7684\uFF1F",
1313
- en: "mu does not recognise this key. Which service is it for?"
1391
+ en: "mu doesn't recognize this key. Which service is it for?"
1314
1392
  })
1315
1393
  );
1316
1394
  }
@@ -1321,9 +1399,7 @@ var Setup = class {
1321
1399
  let key = given;
1322
1400
  if (key === void 0 && !this.scripted) {
1323
1401
  if (service.keyPage) {
1324
- this.io.say(
1325
- this.t({ zh: `\u5BC6\u94A5\u5728\u8FD9\u91CC\u521B\u5EFA\uFF1A${service.keyPage}`, en: `Keys are made at ${service.keyPage}` })
1326
- );
1402
+ this.io.say(this.t({ zh: `\u5BC6\u94A5\u5728\u8FD9\u91CC\u521B\u5EFA\uFF1A${service.keyPage}`, en: `Create a key at ${service.keyPage}` }));
1327
1403
  }
1328
1404
  key = await this.pasteKey();
1329
1405
  }
@@ -1341,7 +1417,7 @@ var Setup = class {
1341
1417
  this.io.warn(
1342
1418
  this.t({
1343
1419
  zh: `\u6CE8\u610F\uFF1A\u8FD9\u4E2A\u5BC6\u94A5\u770B\u8D77\u6765\u662F ${this.names(matches)} \u7684\uFF1B\u6309 --service \u53D1\u7ED9 ${this.name(service)}\u3002`,
1344
- en: `Note: this key looks like ${article(this.names(matches))} key; it goes to ${this.name(service)}, as --service says.`
1420
+ en: `Note: this key looks like ${article(this.names(matches))} key; sending it to ${this.name(service)}, as --service says.`
1345
1421
  })
1346
1422
  );
1347
1423
  return { service, key };
@@ -1388,11 +1464,14 @@ var Setup = class {
1388
1464
  );
1389
1465
  if (/^https?:\/\/[^\s/]+/i.test(baseUrl)) break;
1390
1466
  this.io.say(
1391
- this.t({ zh: "\u5730\u5740\u4EE5 http:// \u6216 https:// \u5F00\u5934\u3002", en: "The address starts with http:// or https://." })
1467
+ this.t({
1468
+ zh: "\u5730\u5740\u4EE5 http:// \u6216 https:// \u5F00\u5934\u3002",
1469
+ en: "The address must start with http:// or https://."
1470
+ })
1392
1471
  );
1393
1472
  }
1394
1473
  }
1395
- const api = args.api === "anthropic" ? "anthropic-messages" : args.api === "openai" || this.scripted ? "openai-completions" : await this.choose(this.t({ zh: "\u5B83\u7528\u54EA\u79CD\u63A5\u53E3\uFF1F", en: "Which API does it speak?" }), [
1474
+ const api = args.api === "anthropic" ? "anthropic-messages" : args.api === "openai" || this.scripted ? "openai-completions" : await this.choose(this.t({ zh: "\u5B83\u7528\u54EA\u79CD\u63A5\u53E3\uFF1F", en: "Which API does it use?" }), [
1396
1475
  {
1397
1476
  label: this.t({
1398
1477
  zh: "OpenAI \u517C\u5BB9\uFF08chat completions\uFF09",
@@ -1414,7 +1493,7 @@ var Setup = class {
1414
1493
  const typed = await this.ask(
1415
1494
  this.t({
1416
1495
  zh: `\u7ED9\u5B83\u8D77\u4E2A\u7B80\u77ED\u7684\u540D\u5B57\uFF08\u5B57\u6BCD\u3001\u6570\u5B57\u3001-\uFF09[${provider}]\uFF1A`,
1417
- en: `A short name for it in mu (letters, digits, -) [${provider}]: `
1496
+ en: `A short name for it in mu (letters, digits and -) [${provider}]: `
1418
1497
  })
1419
1498
  );
1420
1499
  if (typed) provider = typed.toLowerCase().replace(/[^a-z0-9-]+/g, "-");
@@ -1443,7 +1522,7 @@ var Setup = class {
1443
1522
  key = await this.secret(
1444
1523
  this.t({
1445
1524
  zh: "\u7C98\u8D34\u5B83\u7684 API \u5BC6\u94A5\uFF1B\u4E0D\u9700\u8981\u5BC6\u94A5\u5C31\u76F4\u63A5\u56DE\u8F66\uFF08\u4E0D\u4F1A\u663E\u793A\uFF09\uFF1A",
1446
- en: "Paste its API key, or press Enter if it takes none (not shown): "
1525
+ en: "Paste its API key, or press Enter if it doesn't need one (it won't be shown): "
1447
1526
  }),
1448
1527
  true
1449
1528
  ) || void 0;
@@ -1462,15 +1541,15 @@ var Setup = class {
1462
1541
  choices.push({ label: this.t({ zh: "\u518D\u8BD5\u4E00\u6B21", en: "Try again" }), value: "again" });
1463
1542
  if (result.problem.kind !== "key") {
1464
1543
  choices.push({
1465
- label: this.t({ zh: "\u4E0D\u68C0\u67E5\u4E86\uFF0C\u7167\u6837\u4FDD\u5B58", en: "Save it without the check" }),
1544
+ label: this.t({ zh: "\u4E0D\u68C0\u67E5\u4E86\uFF0C\u7167\u6837\u4FDD\u5B58", en: "Save it without checking" }),
1466
1545
  value: "unchecked"
1467
1546
  });
1468
1547
  }
1469
1548
  choices.push({
1470
- label: this.t({ zh: "\u5230\u6B64\u4E3A\u6B62\uFF08\u4EC0\u4E48\u90FD\u4E0D\u6539\uFF09", en: "Stop here (nothing is changed)" }),
1549
+ label: this.t({ zh: "\u5230\u6B64\u4E3A\u6B62\uFF08\u4EC0\u4E48\u90FD\u4E0D\u6539\uFF09", en: "Stop here (change nothing)" }),
1471
1550
  value: "stop"
1472
1551
  });
1473
- const next = await this.choose(this.t({ zh: "\u63A5\u4E0B\u6765\u600E\u4E48\u529E\uFF1F", en: "What now?" }), choices);
1552
+ const next = await this.choose(this.t({ zh: "\u63A5\u4E0B\u6765\u600E\u4E48\u529E\uFF1F", en: "What next?" }), choices);
1474
1553
  if (next === "stop") throw this.cancelled();
1475
1554
  if (next === "unchecked") return { service, reach: reaches[0], key };
1476
1555
  if (next === "key") key = await this.pasteKey();
@@ -1486,10 +1565,10 @@ var Setup = class {
1486
1565
  this.io.say(
1487
1566
  key ? this.t({
1488
1567
  zh: `\u6B63\u5728\u7528 ${where}\u68C0\u67E5\u5BC6\u94A5\u2026\u2026`,
1489
- en: `Checking the key with ${this.name(service)} (${hostOf2(reach.baseUrl)})...`
1568
+ en: `Checking the key with ${this.name(service)} (${hostOf2(reach.baseUrl)})\u2026`
1490
1569
  }) : this.t({
1491
1570
  zh: `\u6B63\u5728\u68C0\u67E5 ${where}\u2026\u2026`,
1492
- en: `Checking ${this.name(service)} (${hostOf2(reach.baseUrl)})...`
1571
+ en: `Checking ${this.name(service)} (${hostOf2(reach.baseUrl)})\u2026`
1493
1572
  })
1494
1573
  );
1495
1574
  const result = await checkKey({
@@ -1504,7 +1583,7 @@ var Setup = class {
1504
1583
  this.io.say(
1505
1584
  count === void 0 ? this.t({ zh: "\u5BC6\u94A5\u53EF\u4EE5\u7528\u3002", en: "The key works." }) : this.t({
1506
1585
  zh: `\u53EF\u4EE5\u7528\uFF1A\u5217\u51FA\u4E86 ${count} \u4E2A\u6A21\u578B\u3002`,
1507
- en: `It works: ${count} model${count === 1 ? "" : "s"} listed.`
1586
+ en: `It works: ${count} model${count === 1 ? "" : "s"} available.`
1508
1587
  })
1509
1588
  );
1510
1589
  return { ok: true, reach, listed: result.models };
@@ -1528,13 +1607,13 @@ var Setup = class {
1528
1607
  if (this.scripted) {
1529
1608
  throw this.fail({
1530
1609
  zh: `${this.name(service)} \u5DF2\u7ECF\u7528\u8BA2\u9605\u8D26\u53F7\u767B\u5F55\u4E86\u3002\u8981\u6539\u5B58\u5BC6\u94A5\uFF0C\u52A0\u4E0A --yes`,
1531
- en: `${this.name(service)} is signed in with a subscription. To store a key instead, add --yes`
1610
+ en: `${this.name(service)} is signed in with a subscription. To use a key instead, add --yes`
1532
1611
  });
1533
1612
  }
1534
1613
  const replace = await this.confirm(
1535
1614
  this.t({
1536
1615
  zh: `\u4F60\u5DF2\u7ECF\u7528\u8BA2\u9605\u8D26\u53F7\u767B\u5F55\u4E86 ${this.name(service)}\u3002\u6539\u7528\u8FD9\u4E2A\u5BC6\u94A5\u5417\uFF1F\u767B\u5F55\u4F1A\u88AB\u66FF\u6362\uFF08\u7559\u6709\u5907\u4EFD\uFF09\u3002[y/N] `,
1537
- en: `You are signed in to ${this.name(service)} with a subscription. Use this key instead? The sign-in is replaced (a copy is kept). [y/N] `
1616
+ en: `You're signed in to ${this.name(service)} with a subscription. Use this key instead? It replaces the sign-in (a backup is kept). [y/N] `
1538
1617
  }),
1539
1618
  false
1540
1619
  );
@@ -1547,7 +1626,7 @@ var Setup = class {
1547
1626
  if (readJsonFile(path).state !== "unreadable") return;
1548
1627
  throw this.fail({
1549
1628
  zh: `${this.path(path)} \u4E0D\u662F\u7EAF JSON\uFF08\u4E5F\u8BB8\u6709\u6CE8\u91CA\u6216\u5199\u9519\u4E86\uFF09\uFF0Cmu setup \u4E0D\u6539\u5199\u5B83\u3002\u5148\u4FEE\u597D\u5B83\uFF0C\u6216\u624B\u52A8\u628A\u670D\u52A1\u52A0\u8FDB\u53BB\u3002`,
1550
- en: `${this.path(path)} is not plain JSON (it may have comments, or a typo), and mu setup does not rewrite it. Fix it, or add the service by hand.`
1629
+ en: `${this.path(path)} isn't plain JSON (it may have comments or a typo), so mu setup won't rewrite it. Fix it, or add the service by hand.`
1551
1630
  });
1552
1631
  }
1553
1632
  // -----------------------------------------------------------------------------------------------------------
@@ -1560,7 +1639,7 @@ var Setup = class {
1560
1639
  this.io.say(
1561
1640
  this.t({
1562
1641
  zh: `\u6CE8\u610F\uFF1A${this.name(service)} \u6CA1\u6709\u5217\u51FA ${this.args.model}\uFF0C\u7167\u6837\u4F7F\u7528\u3002`,
1563
- en: `Note: ${this.name(service)} does not list ${this.args.model}; it is used anyway.`
1642
+ en: `Note: ${this.name(service)} doesn't list ${this.args.model}; using it anyway.`
1564
1643
  })
1565
1644
  );
1566
1645
  }
@@ -1576,14 +1655,14 @@ var Setup = class {
1576
1655
  if (options.length === 0) {
1577
1656
  const none = service.keyless ? NOT_READY : {
1578
1657
  zh: `${this.name(service)} \u6CA1\u6709\u5217\u51FA\u6A21\u578B\u3002`,
1579
- en: `${this.name(service)} lists no models.`
1658
+ en: `${this.name(service)} doesn't list any models.`
1580
1659
  };
1581
1660
  if (this.scripted) {
1582
- throw this.fail({ zh: `${none.zh}\u53EF\u4EE5\u7528 --model \u6307\u5B9A\u4E00\u4E2A\u3002`, en: `${none.en} Name one with --model.` });
1661
+ throw this.fail({ zh: `${none.zh}\u53EF\u4EE5\u7528 --model \u6307\u5B9A\u4E00\u4E2A\u3002`, en: `${none.en} Specify one with --model.` });
1583
1662
  }
1584
1663
  this.io.say(this.t(none));
1585
1664
  for (; ; ) {
1586
- const typed = await this.ask(this.t({ zh: "\u8981\u7528\u7684\u6A21\u578B\u540D\uFF1A", en: "The name of the model to use: " }));
1665
+ const typed = await this.ask(this.t({ zh: "\u8981\u7528\u7684\u6A21\u578B\u540D\uFF1A", en: "Name of the model to use: " }));
1587
1666
  if (typed) return typed;
1588
1667
  }
1589
1668
  }
@@ -1599,7 +1678,7 @@ var Setup = class {
1599
1678
  this.io.say(
1600
1679
  this.t({
1601
1680
  zh: ` \uFF08\u8FD8\u6709 ${more} \u4E2A\uFF0C\u8F93\u5165\u540D\u5B57\u4E5F\u53EF\u4EE5\uFF09`,
1602
- en: ` (${more} more: type a name to use one of them)`
1681
+ en: ` (${more} more; type a name to use one of them)`
1603
1682
  })
1604
1683
  );
1605
1684
  }
@@ -1617,7 +1696,7 @@ var Setup = class {
1617
1696
  this.io.say(
1618
1697
  this.t({
1619
1698
  zh: `\u8BF7\u8F93\u5165 1-${shown.length} \u4E4B\u95F4\u7684\u6570\u5B57\uFF0C\u6216\u6A21\u578B\u540D\u3002`,
1620
- en: `Type a number from 1-${shown.length}, or a model name.`
1699
+ en: `Type a number from 1 to ${shown.length}, or a model name.`
1621
1700
  })
1622
1701
  );
1623
1702
  }
@@ -1633,13 +1712,13 @@ var Setup = class {
1633
1712
  if (this.scripted) {
1634
1713
  throw this.fail({
1635
1714
  zh: `models.json \u91CC\u7684 ${target.reach.provider} \u6307\u5411 ${old}\u3002\u8981\u6539\u6210 ${target.reach.baseUrl}\uFF0C\u52A0\u4E0A --yes`,
1636
- en: `models.json has ${target.reach.provider} at ${old}. To point it at ${target.reach.baseUrl}, add --yes`
1715
+ en: `models.json already points ${target.reach.provider} at ${old}. To point it at ${target.reach.baseUrl}, add --yes`
1637
1716
  });
1638
1717
  }
1639
1718
  const move = await this.confirm(
1640
1719
  this.t({
1641
1720
  zh: `models.json \u91CC\u5DF2\u7ECF\u6709 ${target.reach.provider}\uFF0C\u5730\u5740\u662F ${old}\u3002\u6539\u6210 ${target.reach.baseUrl} \u5417\uFF1F[y/N] `,
1642
- en: `models.json already has ${target.reach.provider}, at ${old}. Point it at ${target.reach.baseUrl}? [y/N] `
1721
+ en: `models.json already points ${target.reach.provider} at ${old}. Point it at ${target.reach.baseUrl} instead? [y/N] `
1643
1722
  }),
1644
1723
  false
1645
1724
  );
@@ -1652,33 +1731,65 @@ var Setup = class {
1652
1731
  const { env, paths } = this.deps;
1653
1732
  const envText = readTextFile(paths.envFile);
1654
1733
  const existing = jevKeyAt(env, envText);
1655
- if (this.scripted) return this.scriptedJudge(model, envText, existing !== void 0);
1734
+ const opencode = model?.provider === "opencode" || Boolean(env[OPENCODE_KEY]) || !(OPENCODE_KEY in env) && envText !== void 0 && Boolean(envFileValue(envText, OPENCODE_KEY)) || await this.deps.pi.credential("opencode") !== void 0;
1735
+ if (this.scripted) return this.scriptedJudge(model, envText, existing !== void 0, opencode);
1656
1736
  const where = existing ? existing.where === "env" ? this.t({ zh: `\u73AF\u5883\u53D8\u91CF ${existing.name}`, en: `${existing.name} in your environment` }) : this.t({
1657
1737
  zh: `${this.path(paths.envFile)} \u91CC\u7684 ${existing.name}`,
1658
1738
  en: `${existing.name} in ${this.path(paths.envFile)}`
1659
1739
  }) : void 0;
1660
1740
  const choices = [];
1741
+ const keyless = !opencode && !where;
1742
+ if (keyless) {
1743
+ choices.push({
1744
+ label: this.t({
1745
+ zh: "Jev \u514D\u8D39\u7248\uFF08\u63A8\u8350\uFF09\uFF0C\u5728 OpenCode Zen \u4E0A\uFF1A\u4E0D\u7528\u5BC6\u94A5\uFF0C\u9650\u65F6\u514D\u8D39\uFF1B\u4EE5\u540E\u52A0\u4E86\u5BC6\u94A5\u81EA\u52A8\u6362\u7528",
1746
+ en: "Free Jev on OpenCode Zen (recommended): no key needed, free for a limited time; a key you add later takes over"
1747
+ }),
1748
+ value: "free"
1749
+ });
1750
+ }
1751
+ if (opencode && !where) {
1752
+ choices.push({
1753
+ label: this.t({
1754
+ zh: "Jev\uFF08\u63A8\u8350\uFF09\uFF0C\u5728 OpenCode Zen \u4E0A\uFF0C\u7528\u4F60\u7684 OpenCode \u5BC6\u94A5\uFF0C\u6309\u6B21\u8BA1\u8D39",
1755
+ en: "Jev on OpenCode Zen with your OpenCode key (recommended), billed per call"
1756
+ }),
1757
+ value: "opencode"
1758
+ });
1759
+ }
1661
1760
  if (where) {
1662
1761
  choices.push({
1663
1762
  label: this.t({
1664
1763
  zh: `Jev\uFF08\u63A8\u8350\uFF09\uFF0C\u7528\u5DF2\u7ECF\u8BBE\u7F6E\u7684\u5BC6\u94A5\uFF08${where}\uFF09`,
1665
- en: `Jev (recommended), with the key already set (${where})`
1764
+ en: `Jev with the key that's already set (recommended): ${where}`
1666
1765
  }),
1667
1766
  value: "keep"
1668
1767
  });
1669
1768
  }
1670
1769
  choices.push({
1671
- label: where ? this.t({ zh: "Jev\uFF0C\u6362\u4E00\u4E2A\u65B0\u5BC6\u94A5", en: "Jev, with a new key" }) : this.t({
1672
- zh: `Jev\uFF08\u63A8\u8350\uFF09\uFF1A\u53C8\u5FEB\u53C8\u51C6\uFF0C\u6309\u6B21\u8BA1\u8D39\u3002\u5BC6\u94A5\u5728 TypeSafe \u7533\u8BF7\uFF1A${JEV_PAGE}`,
1673
- en: `Jev (recommended): fast and accurate, billed per call. Get a key from TypeSafe: ${JEV_PAGE}`
1770
+ label: where ? this.t({ zh: "Jev\uFF0C\u6362\u4E00\u4E2A\u65B0\u5BC6\u94A5", en: "Jev with a new key" }) : opencode ? this.t({
1771
+ zh: `Jev\uFF0C\u7528 TypeSafe \u7684\u5BC6\u94A5\uFF1A${JEV_PAGE}`,
1772
+ en: `Jev with a TypeSafe key: ${JEV_PAGE}`
1773
+ }) : this.t({
1774
+ zh: `Jev\uFF0C\u7528\u81EA\u5DF1\u7684\u5BC6\u94A5\uFF1A\u6BCF\u4E2A\u95EE\u9898\u7EA6 0.3 \u79D2\uFF0C\u6309\u6B21\u8BA1\u8D39\u3002\u5BC6\u94A5\u5728 TypeSafe \u7533\u8BF7\uFF1A${JEV_PAGE}`,
1775
+ en: `Jev with your own key: about 0.3 s per question, billed per call. Get a key from TypeSafe: ${JEV_PAGE}`
1674
1776
  }),
1675
1777
  value: "jev"
1676
1778
  });
1779
+ if (!keyless) {
1780
+ choices.push({
1781
+ label: this.t({
1782
+ zh: "Jev \u514D\u8D39\u7248\uFF0C\u5728 OpenCode Zen \u4E0A\uFF1A\u4E0D\u7528\u5BC6\u94A5\uFF0C\u9650\u65F6\u514D\u8D39",
1783
+ en: "Free Jev on OpenCode Zen: no key needed, free for a limited time"
1784
+ }),
1785
+ value: "free"
1786
+ });
1787
+ }
1677
1788
  if (model) {
1678
1789
  choices.push({
1679
1790
  label: this.t({
1680
1791
  zh: `\u521A\u8BBE\u7F6E\u7684\u6A21\u578B\uFF08${model.provider}/${model.model}\uFF09\uFF1A\u66F4\u6162\uFF0C\u800C\u4E14\u6BCF\u6B21\u5224\u65AD\u90FD\u8981\u82B1 token`,
1681
- en: `The model you just set up (${model.provider}/${model.model}): slower, and every decision costs tokens`
1792
+ en: `The model you just set up (${model.provider}/${model.model}): slower, and every verdict costs tokens`
1682
1793
  }),
1683
1794
  value: "model"
1684
1795
  });
@@ -1686,22 +1797,35 @@ var Setup = class {
1686
1797
  choices.push({
1687
1798
  label: this.t({
1688
1799
  zh: "\u6682\u65F6\u4E0D\u8981\uFF1Amu \u5C31\u50CF pi \u4E00\u6837\u5DE5\u4F5C\uFF0C\u6CA1\u6709\u5224\u5B9A\u5668\u56DE\u7B54",
1689
- en: "Not now: mu then works like pi, with no judge answering"
1800
+ en: "Not now: mu works like plain pi, with no judge"
1690
1801
  }),
1691
1802
  value: "none"
1692
1803
  });
1693
1804
  const picked = await this.choose(
1694
1805
  this.t({
1695
1806
  zh: "mu \u5DE5\u4F5C\u65F6\u8981\u505A\u5F88\u591A\u5C0F\u5224\u65AD\uFF1A\u8FD9\u6761\u6D88\u606F\u8BE5\u600E\u4E48\u5904\u7406\u3001\u8FD9\u4E00\u6B65\u4F1A\u4E0D\u4F1A\u8FDD\u53CD\u4F60\u5B9A\u7684\u89C4\u5219\u2026\u2026\u5224\u5B9A\u5668\u8D1F\u8D23\u56DE\u7B54\u3002\u9009\u54EA\u4E2A\u5224\u5B9A\u5668\uFF1F",
1696
- en: "mu makes many small decisions as it works: how to take a message, whether a step breaks your rules. A judge answers them. Which judge?"
1807
+ en: "As it works, mu faces many small decisions: how to handle a message, whether a step breaks your rules. A judge answers them. Which judge do you want?"
1697
1808
  }),
1698
1809
  choices
1699
1810
  );
1700
1811
  if (picked === "jev") return { kind: "jev", key: await this.jevKey() };
1701
1812
  return { kind: picked };
1702
1813
  }
1703
- scriptedJudge(model, envText, keySet) {
1814
+ scriptedJudge(model, envText, keySet, opencode) {
1704
1815
  switch (this.args.judge) {
1816
+ case "opencode":
1817
+ if (!opencode) {
1818
+ throw this.fail(
1819
+ {
1820
+ zh: "--judge opencode \u9700\u8981 OpenCode \u7684\u5BC6\u94A5\uFF1A\u7528 --service opencode \u8BBE\u7F6E\u5B83\uFF0C\u6216\u5148\u8BBE\u7F6E OPENCODE_API_KEY",
1821
+ en: "--judge opencode needs an OpenCode key: set one up with --service opencode, or set OPENCODE_API_KEY first"
1822
+ },
1823
+ SETUP_EXIT.usage
1824
+ );
1825
+ }
1826
+ return { kind: "opencode" };
1827
+ case "free":
1828
+ return { kind: "free" };
1705
1829
  case "jev": {
1706
1830
  const key = this.jevFromStdin;
1707
1831
  if (!key) {
@@ -1709,14 +1833,14 @@ var Setup = class {
1709
1833
  throw this.fail(
1710
1834
  {
1711
1835
  zh: "--judge jev \u9700\u8981 Jev \u5BC6\u94A5\uFF1A\u5199\u5728\u6807\u51C6\u8F93\u5165\u7684\u7B2C\u4E8C\u884C\uFF0C\u6216\u5148\u8BBE\u7F6E TYPESAFE_API_KEY",
1712
- en: "--judge jev needs a Jev key: the second line of stdin, or TYPESAFE_API_KEY set beforehand"
1836
+ en: "--judge jev needs a Jev key: put it on the second line of stdin, or set TYPESAFE_API_KEY first"
1713
1837
  },
1714
1838
  SETUP_EXIT.usage
1715
1839
  );
1716
1840
  }
1717
1841
  if (key === this.modelKey) {
1718
1842
  throw this.fail(
1719
- { zh: "Jev \u5BC6\u94A5\u548C\u6A21\u578B\u7684\u5BC6\u94A5\u662F\u540C\u4E00\u4E2A", en: "the Jev key is the same as the model's key" },
1843
+ { zh: "Jev \u5BC6\u94A5\u548C\u6A21\u578B\u7684\u5BC6\u94A5\u662F\u540C\u4E00\u4E2A", en: "the Jev key is the same as the model key" },
1720
1844
  SETUP_EXIT.usage
1721
1845
  );
1722
1846
  }
@@ -1724,7 +1848,7 @@ var Setup = class {
1724
1848
  if (current && current !== key && !this.args.yes) {
1725
1849
  throw this.fail({
1726
1850
  zh: `${this.path(this.deps.paths.envFile)} \u91CC\u5DF2\u7ECF\u6709 TYPESAFE_API_KEY\u3002\u8981\u66FF\u6362\u5B83\uFF0C\u52A0\u4E0A --yes`,
1727
- en: `${this.path(this.deps.paths.envFile)} has a TYPESAFE_API_KEY already. To replace it, add --yes`
1851
+ en: `${this.path(this.deps.paths.envFile)} already has a TYPESAFE_API_KEY. To replace it, add --yes`
1728
1852
  });
1729
1853
  }
1730
1854
  return { kind: "jev", key };
@@ -1740,14 +1864,14 @@ var Setup = class {
1740
1864
  const key = await this.secret(
1741
1865
  this.t({
1742
1866
  zh: "\u7C98\u8D34 Jev \u5BC6\u94A5\uFF08TYPESAFE_API_KEY\uFF0C\u4E0D\u4F1A\u663E\u793A\uFF09\uFF1A",
1743
- en: "Paste the Jev key (TYPESAFE_API_KEY, not shown): "
1867
+ en: "Paste the Jev key (TYPESAFE_API_KEY; it won't be shown): "
1744
1868
  })
1745
1869
  );
1746
1870
  if (key === this.modelKey) {
1747
1871
  this.io.say(
1748
1872
  this.t({
1749
1873
  zh: `\u8FD9\u662F\u521A\u624D\u7ED9\u6A21\u578B\u7684\u5BC6\u94A5\u3002Jev \u7684\u5BC6\u94A5\u5728 TypeSafe \u7533\u8BF7\uFF1A${JEV_PAGE}`,
1750
- en: `That is the key you gave for the model. A Jev key comes from TypeSafe: ${JEV_PAGE}`
1874
+ en: `That's the key you gave for the model. Get a Jev key from TypeSafe: ${JEV_PAGE}`
1751
1875
  })
1752
1876
  );
1753
1877
  continue;
@@ -1815,7 +1939,7 @@ var Setup = class {
1815
1939
  saved.push(
1816
1940
  this.savedLine(
1817
1941
  modelsPath,
1818
- custom || moved ? this.t({ zh: `${provider}\uFF1A${reach.baseUrl}`, en: `${provider} at ${reach.baseUrl}` }) : this.t({ zh: `\u7ED9 ${provider} \u52A0\u4E0A ${model}`, en: `${model} added to ${provider}` })
1942
+ custom || moved ? this.t({ zh: `${provider}\uFF1A${reach.baseUrl}`, en: `${provider} at ${reach.baseUrl}` }) : this.t({ zh: `\u7ED9 ${provider} \u52A0\u4E0A ${model}`, en: `added ${model} to ${provider}` })
1819
1943
  )
1820
1944
  );
1821
1945
  }
@@ -1835,14 +1959,14 @@ var Setup = class {
1835
1959
  saved.push(
1836
1960
  this.savedLine(
1837
1961
  settingsPath,
1838
- this.t({ zh: `\u9ED8\u8BA4\u6A21\u578B ${provider}/${model}`, en: `start on ${provider}/${model}` })
1962
+ this.t({ zh: `\u9ED8\u8BA4\u6A21\u578B ${provider}/${model}`, en: `default model ${provider}/${model}` })
1839
1963
  )
1840
1964
  );
1841
1965
  } catch {
1842
1966
  notes.push(
1843
1967
  this.t({
1844
1968
  zh: `${this.path(settingsPath)} \u8BFB\u4E0D\u4E86\uFF0C\u9ED8\u8BA4\u6A21\u578B\u6CA1\u6709\u4FDD\u5B58\u3002\u5728 mu \u91CC\u7528 /model \u9009\u5B83\u3002`,
1845
- en: `${this.path(settingsPath)} could not be read, so the default model was not saved. Choose it inside mu with /model.`
1969
+ en: `Couldn't read ${this.path(settingsPath)}, so the default model wasn't saved. Choose it inside mu with /model.`
1846
1970
  })
1847
1971
  );
1848
1972
  }
@@ -1856,14 +1980,14 @@ var Setup = class {
1856
1980
  }
1857
1981
  this.io.warn(this.t({ zh: `mu setup\uFF1A\u4FDD\u5B58\u65F6\u51FA\u9519\uFF1A${reason}`, en: `mu setup: saving failed: ${reason}` }));
1858
1982
  if (saved.length > 0) {
1859
- this.io.warn(this.t({ zh: "\u51FA\u9519\u524D\u5DF2\u7ECF\u4FDD\u5B58\u7684\uFF1A", en: "Saved before that:" }));
1983
+ this.io.warn(this.t({ zh: "\u51FA\u9519\u524D\u5DF2\u7ECF\u4FDD\u5B58\u7684\uFF1A", en: "Saved before the error:" }));
1860
1984
  for (const entry of saved) this.io.warn(entry);
1861
1985
  }
1862
1986
  if (backups.path) {
1863
1987
  this.io.warn(
1864
1988
  this.t({
1865
1989
  zh: `\u539F\u6765\u7684\u6587\u4EF6\u5728 ${this.path(backups.path)}`,
1866
- en: `The earlier versions are in ${this.path(backups.path)}`
1990
+ en: `The previous versions are in ${this.path(backups.path)}`
1867
1991
  })
1868
1992
  );
1869
1993
  }
@@ -1877,7 +2001,7 @@ var Setup = class {
1877
2001
  this.io.say(
1878
2002
  this.t({
1879
2003
  zh: `\u6539\u52A8\u524D\u7684\u6587\u4EF6\u5DF2\u590D\u5236\u5230 ${this.path(backups.path)}`,
1880
- en: `The files as they were are copied to ${this.path(backups.path)}`
2004
+ en: `Backed up the previous files to ${this.path(backups.path)}`
1881
2005
  })
1882
2006
  );
1883
2007
  }
@@ -1896,12 +2020,15 @@ var Setup = class {
1896
2020
  let tiers;
1897
2021
  if (judge.kind === "model") {
1898
2022
  if (target && model) tiers = [`llm:${target.reach.provider}/${model}`];
2023
+ } else if (judge.kind === "opencode") tiers = ["jev-opencode"];
2024
+ else if (judge.kind === "free") {
2025
+ if (!tiersOf(current).some((tier) => tier === "jev" || tier === "jev-opencode-free")) tiers = ["jev"];
1899
2026
  } else if (!tiersOf(current).some((tier) => JEV_TIERS.has(tier))) tiers = ["jev"];
1900
2027
  if (tiers && config.state === "unreadable") {
1901
2028
  notes.push(
1902
2029
  this.t({
1903
2030
  zh: `${this.path(configPath)} \u4E0D\u662F\u7EAF JSON\uFF0C\u6CA1\u6709\u6539\u5B83\u3002\u8BF7\u624B\u52A8\u52A0\u4E0A "tiers": ${JSON.stringify(tiers)}`,
1904
- en: `${this.path(configPath)} is not plain JSON and was left as it is. Add "tiers": ${JSON.stringify(tiers)} by hand.`
2031
+ en: `${this.path(configPath)} isn't plain JSON, so it was left unchanged. Add "tiers": ${JSON.stringify(tiers)} by hand.`
1905
2032
  })
1906
2033
  );
1907
2034
  } else if (tiers) {
@@ -1924,7 +2051,7 @@ var Setup = class {
1924
2051
  notes.push(
1925
2052
  this.t({
1926
2053
  zh: `\u6CE8\u610F\uFF1A\u73AF\u5883\u53D8\u91CF\u91CC\u4E5F\u6709 TYPESAFE_API_KEY\uFF0C\u5B83\u6BD4 ${this.path(paths.envFile)} \u4F18\u5148\u3002\u5230\u8BBE\u7F6E\u5B83\u7684\u5730\u65B9\uFF08\u4F8B\u5982 ~/.zshrc\uFF09\u6539\u6389\u6216\u5220\u6389\u3002`,
1927
- en: `Note: TYPESAFE_API_KEY is also set in your environment, and it wins over ${this.path(paths.envFile)}. Change or remove it where it is set (~/.zshrc, for example).`
2054
+ en: `Note: TYPESAFE_API_KEY is also set in your environment and takes precedence over ${this.path(paths.envFile)}. Change or remove it where it's set (~/.zshrc, for example).`
1928
2055
  })
1929
2056
  );
1930
2057
  }
@@ -1934,7 +2061,7 @@ var Setup = class {
1934
2061
  notes.push(
1935
2062
  this.t({
1936
2063
  zh: `\u6CE8\u610F\uFF1A\u73AF\u5883\u53D8\u91CF MU_JUDGE=${override} \u6BD4 mu.json \u4F18\u5148\uFF0C\u7531\u5B83\u51B3\u5B9A\u5224\u5B9A\u5668\u3002`,
1937
- en: `Note: MU_JUDGE=${override} is set in your environment; it decides the judge over mu.json.`
2064
+ en: `Note: MU_JUDGE=${override} is set in your environment and takes precedence over mu.json for the judge.`
1938
2065
  })
1939
2066
  );
1940
2067
  }
@@ -1942,15 +2069,18 @@ var Setup = class {
1942
2069
  summary(target, model, judge) {
1943
2070
  const done = target && model ? this.t({
1944
2071
  zh: `\u5B8C\u6210\u3002mu \u4F1A\u7528 ${this.name(target.service)} \xB7 ${model} \u5F00\u59CB\u5DE5\u4F5C\u3002`,
1945
- en: `Done. mu starts on ${this.name(target.service)} \xB7 ${model}.`
2072
+ en: `Done. mu will start on ${this.name(target.service)} \xB7 ${model}.`
1946
2073
  }) : this.t({ zh: "\u5B8C\u6210\u3002", en: "Done." });
1947
- const judged = judge === void 0 ? "" : judge.kind === "model" ? this.t({ zh: "\u5224\u5B9A\u5668\uFF1A\u540C\u4E00\u4E2A\u6A21\u578B\u3002", en: " Judge: the same model." }) : judge.kind === "none" ? this.t({ zh: "\u5224\u5B9A\u5668\uFF1A\u6682\u65F6\u6CA1\u6709\u3002", en: " Judge: none for now." }) : this.t({ zh: "\u5224\u5B9A\u5668\uFF1AJev\u3002", en: " Judge: Jev." });
2074
+ const judged = judge === void 0 ? "" : judge.kind === "model" ? this.t({ zh: "\u5224\u5B9A\u5668\uFF1A\u540C\u4E00\u4E2A\u6A21\u578B\u3002", en: " Judge: the same model." }) : judge.kind === "none" ? this.t({ zh: "\u5224\u5B9A\u5668\uFF1A\u6682\u65F6\u6CA1\u6709\u3002", en: " Judge: none for now." }) : judge.kind === "opencode" ? this.t({ zh: "\u5224\u5B9A\u5668\uFF1AOpenCode Zen \u4E0A\u7684 Jev\u3002", en: " Judge: Jev on OpenCode Zen." }) : judge.kind === "free" ? this.t({
2075
+ zh: "\u5224\u5B9A\u5668\uFF1AJev\uFF0C\u52A0\u5BC6\u94A5\u4E4B\u524D\u7528 OpenCode Zen \u4E0A\u9650\u65F6\u514D\u8D39\u7684\u7248\u672C\u3002",
2076
+ en: " Judge: Jev, using the free version on OpenCode Zen until you add a key."
2077
+ }) : this.t({ zh: "\u5224\u5B9A\u5668\uFF1AJev\u3002", en: " Judge: Jev." });
1948
2078
  this.io.say("");
1949
2079
  this.io.say(`${done}${judged}`);
1950
2080
  this.io.say(
1951
2081
  target ? this.t({
1952
2082
  zh: "\u5728 mu \u91CC\u7528 /model \u6362\u6A21\u578B\uFF1B\u968F\u65F6\u53EF\u4EE5\u518D\u8FD0\u884C mu setup\u3002",
1953
- en: "Switch models inside mu with /model; run mu setup again any time."
2083
+ en: "Switch models inside mu with /model, and run mu setup again any time."
1954
2084
  }) : this.t({ zh: "\u542F\u52A8 mu \u540E\u8F93\u5165 /login \u767B\u5F55\u3002", en: "Start mu and type /login to sign in." })
1955
2085
  );
1956
2086
  }