mu-agent 0.1.10 → 0.1.13

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 (41) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +6 -6
  3. package/dist/bundle/chunks/{chunk-O7IPTASE.js → chunk-3J3HSHLX.js} +22 -21
  4. package/dist/bundle/chunks/{chunk-DNNHZBQR.js → chunk-6Z2347ML.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-X234222Q.js → chunk-DYTENXHX.js} +2 -2
  6. package/dist/bundle/chunks/{chunk-RAQ6TU76.js → chunk-FLHKNMLQ.js} +1 -1
  7. package/dist/bundle/chunks/chunk-LHRPMXVI.js +2 -0
  8. package/dist/bundle/chunks/{chunk-HZNN636Z.js → chunk-LT2C56HV.js} +1 -1
  9. package/dist/bundle/chunks/{chunk-SGOTYFMT.js → chunk-MF3VB62R.js} +1 -1
  10. package/dist/bundle/chunks/{chunk-QOEEJKS4.js → chunk-URBBMV3M.js} +1 -1
  11. package/dist/bundle/chunks/{chunk-VJLOQZKR.js → chunk-WCJNJQO5.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-FN6OEZYR.js → chunk-WDURT4ZM.js} +1 -1
  13. package/dist/bundle/chunks/{cli-722OL3K5.js → cli-M4YBZZ6Q.js} +1 -1
  14. package/dist/bundle/chunks/{easter-egg-3d-6H34EDGX.js → easter-egg-3d-VFY3YRQR.js} +1 -1
  15. package/dist/bundle/chunks/{execute-KVSEIDWL.js → execute-FKI7Y4XR.js} +1 -1
  16. package/dist/bundle/chunks/{runtime-O3ELDOM7.js → runtime-IKE5OQIW.js} +1 -1
  17. package/dist/bundle/chunks/{virtual-modules-IKEARUOZ.js → virtual-modules-TJPHLTRT.js} +1 -1
  18. package/dist/bundle/cli-runtime.js +1 -1
  19. package/dist/bundle/index.js +1 -1
  20. package/dist/bundle/rpc-entry.js +1 -1
  21. package/docs/settings.md +1 -1
  22. package/judge/dist/auth.js +51 -26
  23. package/judge/dist/chrome.js +2 -2
  24. package/judge/dist/import.js +833 -161
  25. package/judge/dist/setup.js +129 -46
  26. package/judge/manifest.json +1935 -349
  27. package/judge/mu/chunk-3NQ5BIBI.js +4773 -0
  28. package/judge/mu/chunk-SKGYWHW6.js +51 -0
  29. package/judge/mu/chunk-WQM4MV6L.js +58 -0
  30. package/judge/mu/cloud-code-V7FOGB4J.js +22802 -0
  31. package/judge/mu/dist-75PS4OC4.js +1283 -0
  32. package/judge/mu/index.js +4509 -32801
  33. package/judge/mu/multipart-parser-ZDXVIOOP.js +372 -0
  34. package/judge/mu/src-TVVA5ZK2.js +1409 -0
  35. package/kyrn/bin/kyrn-doctor +28 -3
  36. package/kyrn/bin/kyrn-judge-local +42 -13
  37. package/kyrn/bin/kyrn-ledger +16 -1
  38. package/kyrn/bin/mu.mjs +77 -23
  39. package/kyrn/local-judge/server.py +26 -14
  40. package/package.json +6 -3
  41. package/dist/bundle/chunks/chunk-BSS6T6KQ.js +0 -2
@@ -328,6 +328,16 @@ function tiersOf(config) {
328
328
  function withTiers(config, tiers) {
329
329
  return { ...config, tiers: [...tiers] };
330
330
  }
331
+ function judgeOff(config) {
332
+ return isRecord(config?.modes) && config.modes.default === "off";
333
+ }
334
+ function withJudge(config, on) {
335
+ const { default: _default, ...modes } = isRecord(config.modes) ? config.modes : {};
336
+ if (!on) return { ...config, modes: { default: "off", ...modes } };
337
+ if (!judgeOff(config)) return config;
338
+ const { modes: _modes, ...rest } = config;
339
+ return Object.keys(modes).length > 0 ? { ...rest, modes } : rest;
340
+ }
331
341
  function readTextFile(path) {
332
342
  try {
333
343
  return readFileSync(path, "utf8");
@@ -378,14 +388,17 @@ function piAccess(runtime, agentDir2) {
378
388
  storeKey: async (provider, key) => {
379
389
  await runtime.refresh({ allowNetwork: false, providers: [provider] });
380
390
  if (!runtime.getProvider(provider)) {
381
- throw new Error(`pi has no provider ${provider}${runtime.getError() ? `: ${runtime.getError()}` : ""}`);
391
+ throw new Error(
392
+ `mu has no provider named ${provider}${runtime.getError() ? `: ${runtime.getError()}` : ""}`
393
+ );
382
394
  }
383
395
  let asked = false;
384
396
  try {
385
397
  await runtime.login(provider, "api_key", {
386
398
  // pi's key login asks one question, the key. Anything else is not answered, and nothing is guessed.
387
399
  prompt: async (prompt) => {
388
- if (asked || prompt.type !== "secret") throw new Error(`pi asked ${provider} for more than a key`);
400
+ if (asked || prompt.type !== "secret")
401
+ throw new Error(`the sign-in for ${provider} asked for more than a key`);
389
402
  asked = true;
390
403
  return key;
391
404
  },
@@ -459,11 +472,31 @@ function askSecret(input, output, question) {
459
472
  input.resume();
460
473
  });
461
474
  }
475
+ var columnsOf = (char) => /[\u1100-\u115f\u2e80-\ua4cf\uac00-\ud7a3\uf900-\ufaff\ufe30-\ufe4f\uff00-\uff60\uffe0-\uffe6]/u.test(char) ? 2 : 1;
476
+ var widthOf = (text) => Array.from(text).reduce((sum, char) => sum + columnsOf(char), 0);
477
+ function wrapToWidth(text, width) {
478
+ if (!width || width < 20) return text;
479
+ return text.split("\n").map((line) => {
480
+ if (widthOf(line) <= width) return line;
481
+ const indent = " ".repeat(/^\s*(?:\d+\)\s+)?/.exec(line)?.[0].length ?? 0);
482
+ const out = [];
483
+ let current = "";
484
+ for (const word of line.trimEnd().split(/(?<=\S) (?=\S)/)) {
485
+ const next = current ? `${current} ${word}` : word;
486
+ if (current && widthOf(next) > width) {
487
+ out.push(current);
488
+ current = `${indent}${word.trimStart()}`;
489
+ } else current = next;
490
+ }
491
+ out.push(current);
492
+ return out.join("\n");
493
+ }).join("\n");
494
+ }
462
495
  function terminalPrompter(input = process.stdin, output = process.stdout, errors = process.stderr) {
463
496
  return {
464
- say: (text) => output.write(`${text}
497
+ say: (text) => output.write(`${wrapToWidth(text, output.isTTY ? output.columns : void 0)}
465
498
  `),
466
- warn: (text) => errors.write(`${text}
499
+ warn: (text) => errors.write(`${wrapToWidth(text, errors.isTTY ? errors.columns : void 0)}
467
500
  `),
468
501
  ask: (question) => askLine(input, output, question),
469
502
  secret: (question) => askSecret(input, output, question)
@@ -885,6 +918,12 @@ var SETUP_SERVICES = [
885
918
  keyOptional: true
886
919
  }
887
920
  ];
921
+ var FIRST_IN_ENGLISH = ["openai", "anthropic", "google", "xai", "openrouter", "opencode"];
922
+ function servicesInOrder(language) {
923
+ if (language === "zh") return SETUP_SERVICES;
924
+ const first = FIRST_IN_ENGLISH.flatMap((id) => SETUP_SERVICES.filter((service) => service.id === id));
925
+ return [...first, ...SETUP_SERVICES.filter((service) => !FIRST_IN_ENGLISH.includes(service.id))];
926
+ }
888
927
  function serviceById(id) {
889
928
  return SETUP_SERVICES.find((service) => service.id === id);
890
929
  }
@@ -893,6 +932,9 @@ function cleanKey(raw) {
893
932
  if (key.length >= 2 && (key[0] === '"' || key[0] === "'") && key.at(-1) === key[0]) key = key.slice(1, -1).trim();
894
933
  return key;
895
934
  }
935
+ function isClaudeSubscriptionToken(key) {
936
+ return cleanKey(key).startsWith("sk-ant-oat");
937
+ }
896
938
  function servicesForKey(key) {
897
939
  const clean = cleanKey(key);
898
940
  return clean ? SETUP_SERVICES.filter((service) => service.keyShape?.test(clean)) : [];
@@ -939,7 +981,7 @@ function namedModel(service) {
939
981
 
940
982
  // packages/kyrn-judge/src/setup/wizard.ts
941
983
  var SETUP_DECLINED_FILE = "setup-declined";
942
- var SETUP_EXIT = { done: 0, failed: 1, usage: 2, start: 3, cancelled: 130 };
984
+ var SETUP_EXIT = { done: 0, failed: 1, usage: 2, start: 3, signIn: 4, cancelled: 130 };
943
985
  var SETUP_VALUE_FLAGS = [
944
986
  "--service",
945
987
  "--model",
@@ -970,6 +1012,10 @@ var JEV_TIERS = /* @__PURE__ */ new Set([
970
1012
  ]);
971
1013
  var OPENCODE_KEY = "OPENCODE_API_KEY";
972
1014
  var JEV_PAGE = "https://typesafe.ai";
1015
+ var CLAUDE_TOKEN = {
1016
+ zh: "\u8FD9\u662F Claude \u8BA2\u9605\u4EE4\u724C\uFF08claude setup-token \u751F\u6210\u7684\uFF09\uFF0C\u4E0D\u662F API \u5BC6\u94A5\u3002Anthropic \u4E0D\u5141\u8BB8\u5176\u4ED6\u5E94\u7528\u4F7F\u7528 Claude \u8BA2\u9605\uFF0C\u6240\u4EE5 mu \u7528\u4E0D\u4E86\u5B83\u3002\u8BF7\u6539\u7528 Anthropic API \u5BC6\u94A5\uFF08sk-ant-api\u2026\uFF09\uFF0C\u5728\u8FD9\u91CC\u521B\u5EFA\uFF1Ahttps://platform.claude.com/settings/keys",
1017
+ en: "That's a Claude subscription token (from claude setup-token), not an API key. Anthropic doesn't allow other apps to use a Claude subscription, so mu can't use it. Use an Anthropic API key (sk-ant-api\u2026) instead; create one at https://platform.claude.com/settings/keys"
1018
+ };
973
1019
  var BRANDED = /* @__PURE__ */ new Set(["openai", "anthropic", "openrouter", "google", "xai"]);
974
1020
  var OTHER = {
975
1021
  zh: "\u5176\u4ED6 OpenAI \u6216 Anthropic \u517C\u5BB9\u7684\u670D\u52A1",
@@ -1003,11 +1049,11 @@ function parseSetupArgs(argv) {
1003
1049
  return { error: { zh: "--api \u53EA\u80FD\u662F openai \u6216 anthropic", en: "--api must be openai or anthropic" } };
1004
1050
  }
1005
1051
  const judge = values.get("--judge");
1006
- if (judge !== void 0 && !["jev", "opencode", "free", "model", "none"].includes(judge)) {
1052
+ if (judge !== void 0 && !["jev", "opencode", "free", "model", "off", "none"].includes(judge)) {
1007
1053
  return {
1008
1054
  error: {
1009
- zh: "--judge \u53EA\u80FD\u662F jev\u3001opencode\u3001free\u3001model \u6216 none",
1010
- en: "--judge must be jev, opencode, free, model or none"
1055
+ zh: "--judge \u53EA\u80FD\u662F jev\u3001opencode\u3001free\u3001model\u3001off \u6216 none",
1056
+ en: "--judge must be jev, opencode, free, model, off or none"
1011
1057
  }
1012
1058
  };
1013
1059
  }
@@ -1050,13 +1096,14 @@ function setupUsage(language) {
1050
1096
  "\u4E3A\u4EC0\u4E48\u811A\u672C\u8981\u4ECE\u6807\u51C6\u8F93\u5165\u7ED9\u5BC6\u94A5\uFF1A\u5199\u5728\u547D\u4EE4\u884C\u4E0A\u7684\u5BC6\u94A5\u4F1A\u7559\u5728 shell \u7684\u5386\u53F2\u8BB0\u5F55\u91CC\uFF0C",
1051
1097
  "\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",
1052
1098
  "",
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",
1099
+ " --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
1100
  " --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",
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",
1057
- " --judge none \u4E0D\u6539\u5224\u5B9A\u5668\uFF1B\u4E0D\u5199 --judge \u65F6\u4E5F\u4E0D\u6539",
1058
- " --api, --name \u670D\u52A1 other \u7684\u63A5\u53E3\uFF08openai \u6216 anthropic\uFF09\u548C\u5B83\u5728 mu \u91CC\u7684\u540D\u5B57",
1059
- " --yes \u4E0D\u63D0\u95EE\uFF1B\u66FF\u6362\u5DF2\u6709\u7684\u8BBE\u7F6E\uFF08\u8BA2\u9605\u767B\u5F55\u3001\u540C\u540D\u670D\u52A1\u7684\u5730\u5740\u3001Jev \u5BC6\u94A5\uFF09",
1101
+ " --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",
1102
+ " --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",
1103
+ " --judge off \u4E0D\u7528\u5224\u5B9A\u5668\uFF1A\u6240\u6709\u5224\u5B9A\u70B9\u5173\u95ED\uFF0C\u4EC0\u4E48\u90FD\u4E0D\u53D1\u7ED9\u5224\u5B9A\u5668",
1104
+ " --judge none \u4E0D\u6539\u5224\u5B9A\u5668\uFF1B\u4E0D\u5199 --judge \u65F6\u4E5F\u4E0D\u6539",
1105
+ " --api, --name \u670D\u52A1 other \u7684\u63A5\u53E3\uFF08openai \u6216 anthropic\uFF09\u548C\u5B83\u5728 mu \u91CC\u7684\u540D\u5B57",
1106
+ " --yes \u4E0D\u63D0\u95EE\uFF1B\u66FF\u6362\u5DF2\u6709\u7684\u8BBE\u7F6E\uFF08\u8BA2\u9605\u767B\u5F55\u3001\u540C\u540D\u670D\u52A1\u7684\u5730\u5740\u3001Jev \u5BC6\u94A5\uFF09",
1060
1107
  "",
1061
1108
  "\u4F1A\u6539\u52A8\u7684\u6587\u4EF6\uFF1A~/.mu/agent \u91CC\u7684 auth.json\uFF08\u5BC6\u94A5\uFF09\u3001models.json\u3001settings.json\u3001mu.json\uFF0C",
1062
1109
  "\u4EE5\u53CA mu \u8BFB\u53D6\u7684 .env\uFF08Jev \u5BC6\u94A5\uFF09\u3002\u6BCF\u4E2A\u6587\u4EF6\u6539\u52A8\u524D\u90FD\u4F1A\u5148\u590D\u5236\u5230 ~/.mu/backups/<\u65F6\u95F4>/\u3002"
@@ -1075,14 +1122,15 @@ function setupUsage(language) {
1075
1122
  "computer that lists processes can read it. `mu setup <key>` is a shortcut for getting started; paste the key",
1076
1123
  "at the prompt when you can.",
1077
1124
  "",
1078
- " --judge jev read the Jev key (TypeSafe) from stdin's second line, unless TYPESAFE_API_KEY is already set",
1125
+ " --judge jev read the Jev key (TypeSafe) from stdin's second line, unless TYPESAFE_API_KEY is already set",
1079
1126
  " --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)",
1083
- " --api, --name for the service `other`: its API (openai or anthropic) and its name in mu",
1084
- " --yes don't ask; replace what's already set up (a subscription sign-in, a provider's address,",
1085
- " the Jev key)",
1127
+ " --judge free use the free Jev on OpenCode Zen (limited time, no key; the default while no key is set)",
1128
+ " --judge model use the model you just set up as the judge (slower, and every verdict costs tokens)",
1129
+ " --judge off no judge: every decision point is off, and nothing is sent to a judge",
1130
+ " --judge none leave the judge unchanged (the same as omitting --judge)",
1131
+ " --api, --name for the service `other`: its API (openai or anthropic) and its name in mu",
1132
+ " --yes don't ask; replace what's already set up (a subscription sign-in, a provider's",
1133
+ " address, the Jev key)",
1086
1134
  "",
1087
1135
  "Files it changes: auth.json (keys), models.json, settings.json and mu.json in ~/.mu/agent, and the .env",
1088
1136
  "mu reads (the Jev key). Each one is backed up to ~/.mu/backups/<time>/ before it changes."
@@ -1093,7 +1141,7 @@ function label(service, language) {
1093
1141
  }
1094
1142
  function serviceList(language) {
1095
1143
  const width = Math.max(...SETUP_SERVICES.map((service) => service.id.length));
1096
- return SETUP_SERVICES.map((service) => ` ${service.id.padEnd(width)} ${label(service, language)}`).join("\n");
1144
+ return servicesInOrder(language).map((service) => ` ${service.id.padEnd(width)} ${label(service, language)}`).join("\n");
1097
1145
  }
1098
1146
  function providerIdFor(baseUrl) {
1099
1147
  let host;
@@ -1202,10 +1250,14 @@ var Setup = class {
1202
1250
  if (key || allowEmpty) return key;
1203
1251
  }
1204
1252
  }
1205
- pasteKey() {
1206
- return this.secret(
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): " })
1208
- );
1253
+ async pasteKey() {
1254
+ for (; ; ) {
1255
+ const key = await this.secret(
1256
+ 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): " })
1257
+ );
1258
+ if (!isClaudeSubscriptionToken(key)) return key;
1259
+ this.io.warn(this.t(CLAUDE_TOKEN));
1260
+ }
1209
1261
  }
1210
1262
  async choose(question, choices) {
1211
1263
  this.io.say(question);
@@ -1245,6 +1297,7 @@ var Setup = class {
1245
1297
  key = modelKey;
1246
1298
  this.jevFromStdin = jevKey;
1247
1299
  }
1300
+ if (key !== void 0 && isClaudeSubscriptionToken(key)) throw this.fail(CLAUDE_TOKEN, SETUP_EXIT.usage);
1248
1301
  if (this.scripted && key === void 0 && args.service === void 0) {
1249
1302
  throw this.fail(
1250
1303
  deps.interactive ? { zh: "\u6CA1\u6709\u5BC6\u94A5\uFF0C\u4E5F\u6CA1\u6709 --service", en: "no key or --service given" } : {
@@ -1265,7 +1318,7 @@ var Setup = class {
1265
1318
  const start = await this.start(key === void 0 ? void 0 : cleanKey(key));
1266
1319
  if (start === "subscription") {
1267
1320
  const code2 = await this.write(void 0, void 0, await this.judge(void 0));
1268
- return code2 === SETUP_EXIT.done ? this.offerSession() : code2;
1321
+ return code2 === SETUP_EXIT.done ? this.offerSession(true) : code2;
1269
1322
  }
1270
1323
  const target = await this.checked(start.service, start.key);
1271
1324
  this.modelKey = target.key;
@@ -1321,8 +1374,8 @@ var Setup = class {
1321
1374
  { label: this.t({ zh: "\u4ECE\u5217\u8868\u91CC\u9009\u4E00\u4E2A\u670D\u52A1", en: "Choose a service from the list" }), value: "list" },
1322
1375
  {
1323
1376
  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"
1377
+ zh: "\u7528\u8BA2\u9605\u8D26\u53F7\u767B\u5F55\uFF1AChatGPT\u3001Grok \u6216 Google",
1378
+ en: "Sign in with a subscription: ChatGPT, Grok or Google"
1326
1379
  }),
1327
1380
  value: "subscription"
1328
1381
  }
@@ -1332,13 +1385,13 @@ var Setup = class {
1332
1385
  const auth = this.path(join3(this.deps.paths.agentDir, "auth.json"));
1333
1386
  this.io.say(
1334
1387
  this.t({
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`,
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}.`
1388
+ zh: `\u8BA2\u9605\u8D26\u53F7\u5728 mu \u91CC\u9762\u767B\u5F55\uFF1A\u542F\u52A8 mu\uFF0C\u8F93\u5165 /login\uFF0C\u9009 Sign in with an account\uFF0C\u518D\u9009 OpenAI\uFF08ChatGPT\uFF09\u3001xAI\uFF08Grok\uFF09\u6216 Google \u7684\u4E00\u9879\uFF0C\u6309\u63D0\u793A\u5728\u6D4F\u89C8\u5668\u91CC\u5B8C\u6210\u3002\u767B\u5F55\u4FE1\u606F\u4FDD\u5B58\u5728 ${auth}\u3002`,
1389
+ en: `To use a subscription, sign in from inside mu: start mu, type /login, choose Sign in with an account, then OpenAI for ChatGPT, xAI for Grok, or one of the Google sign-ins, and finish in the browser. The sign-in is saved in ${auth}.`
1337
1390
  })
1338
1391
  );
1339
1392
  return "subscription";
1340
1393
  }
1341
- if (route === "list") return this.keyFor(await this.pickService(SETUP_SERVICES), void 0);
1394
+ if (route === "list") return this.keyFor(await this.pickService(servicesInOrder(this.language)), void 0);
1342
1395
  const pasted = await this.pasteKey();
1343
1396
  return { service: await this.serviceOf(pasted), key: pasted };
1344
1397
  }
@@ -1385,7 +1438,7 @@ var Setup = class {
1385
1438
  );
1386
1439
  }
1387
1440
  return this.pickService(
1388
- SETUP_SERVICES.filter((service) => !service.keyless),
1441
+ servicesInOrder(this.language).filter((service) => !service.keyless),
1389
1442
  this.t({
1390
1443
  zh: "mu \u8BA4\u4E0D\u51FA\u8FD9\u4E2A\u5BC6\u94A5\u3002\u5B83\u662F\u54EA\u4E2A\u670D\u52A1\u7684\uFF1F",
1391
1444
  en: "mu doesn't recognize this key. Which service is it for?"
@@ -1607,7 +1660,7 @@ var Setup = class {
1607
1660
  if (this.scripted) {
1608
1661
  throw this.fail({
1609
1662
  zh: `${this.name(service)} \u5DF2\u7ECF\u7528\u8BA2\u9605\u8D26\u53F7\u767B\u5F55\u4E86\u3002\u8981\u6539\u5B58\u5BC6\u94A5\uFF0C\u52A0\u4E0A --yes`,
1610
- en: `${this.name(service)} is signed in with a subscription. To use a key instead, add --yes`
1663
+ en: `You're signed in to ${this.name(service)} with a subscription. To use a key instead, add --yes`
1611
1664
  });
1612
1665
  }
1613
1666
  const replace = await this.confirm(
@@ -1796,8 +1849,8 @@ var Setup = class {
1796
1849
  }
1797
1850
  choices.push({
1798
1851
  label: this.t({
1799
- zh: "\u6682\u65F6\u4E0D\u8981\uFF1Amu \u5C31\u50CF pi \u4E00\u6837\u5DE5\u4F5C\uFF0C\u6CA1\u6709\u5224\u5B9A\u5668\u56DE\u7B54",
1800
- en: "Not now: mu works like plain pi, with no judge"
1852
+ zh: "\u6682\u65F6\u4E0D\u8981\uFF1A\u4E0D\u95EE\u5224\u5B9A\u5668\uFF0C\u4EC0\u4E48\u90FD\u4E0D\u53D1\u7ED9\u5B83\uFF0Cmu \u6309\u9ED8\u8BA4\u89C4\u5219\u5DE5\u4F5C",
1853
+ en: "Not now: no judge is asked and nothing is sent to one; mu uses its fallbacks"
1801
1854
  }),
1802
1855
  value: "none"
1803
1856
  });
@@ -1826,6 +1879,8 @@ var Setup = class {
1826
1879
  return { kind: "opencode" };
1827
1880
  case "free":
1828
1881
  return { kind: "free" };
1882
+ case "off":
1883
+ return { kind: "none" };
1829
1884
  case "jev": {
1830
1885
  const key = this.jevFromStdin;
1831
1886
  if (!key) {
@@ -1971,14 +2026,15 @@ var Setup = class {
1971
2026
  );
1972
2027
  }
1973
2028
  }
1974
- if (judge && judge.kind !== "none") this.writeJudge(judge, target, model, backups, saved, notes);
2029
+ if (judge?.kind === "none") this.writeNoJudge(backups, saved, notes);
2030
+ else if (judge) this.writeJudge(judge, target, model, backups, saved, notes);
1975
2031
  } catch (error) {
1976
2032
  if (error instanceof Stop) throw error;
1977
2033
  let reason = error instanceof Error ? error.message : String(error);
1978
2034
  for (const secret of [target?.key, judge?.kind === "jev" ? judge.key : void 0]) {
1979
2035
  if (secret) reason = reason.replaceAll(secret, "\u2026");
1980
2036
  }
1981
- this.io.warn(this.t({ zh: `mu setup\uFF1A\u4FDD\u5B58\u65F6\u51FA\u9519\uFF1A${reason}`, en: `mu setup: saving failed: ${reason}` }));
2037
+ this.io.warn(this.t({ zh: `mu setup\uFF1A\u4FDD\u5B58\u65F6\u51FA\u9519\uFF1A${reason}`, en: `mu setup: couldn't save: ${reason}` }));
1982
2038
  if (saved.length > 0) {
1983
2039
  this.io.warn(this.t({ zh: "\u51FA\u9519\u524D\u5DF2\u7ECF\u4FDD\u5B58\u7684\uFF1A", en: "Saved before the error:" }));
1984
2040
  for (const entry of saved) this.io.warn(entry);
@@ -2012,6 +2068,25 @@ var Setup = class {
2012
2068
  savedLine(path, what) {
2013
2069
  return ` ${this.path(path).padEnd(30)} ${what}`;
2014
2070
  }
2071
+ /** "Not now": no decision point asks a judge (the default mode "off"), so nothing goes to one. */
2072
+ writeNoJudge(backups, saved, notes) {
2073
+ const configPath = judgeConfigPath(this.deps.paths.agentDir);
2074
+ const config = readJsonFile(configPath);
2075
+ if (config.state === "unreadable") {
2076
+ notes.push(
2077
+ this.t({
2078
+ zh: `${this.path(configPath)} \u4E0D\u662F\u7EAF JSON\uFF0C\u6CA1\u6709\u6539\u5B83\u3002\u8BF7\u624B\u52A8\u52A0\u4E0A "modes": { "default": "off" }`,
2079
+ en: `${this.path(configPath)} isn't plain JSON, so it was left unchanged. Add "modes": { "default": "off" } by hand.`
2080
+ })
2081
+ );
2082
+ return;
2083
+ }
2084
+ const current = config.state === "ok" ? config.value : void 0;
2085
+ if (judgeOff(current)) return;
2086
+ backups.save(configPath);
2087
+ writeJsonFile(configPath, withJudge(current ?? {}, false));
2088
+ saved.push(this.savedLine(configPath, this.t({ zh: "\u5224\u5B9A\u5668\uFF1A\u5173\u95ED", en: "judge: off" })));
2089
+ }
2015
2090
  writeJudge(judge, target, model, backups, saved, notes) {
2016
2091
  const { env, paths } = this.deps;
2017
2092
  const configPath = judgeConfigPath(paths.agentDir);
@@ -2024,6 +2099,7 @@ var Setup = class {
2024
2099
  else if (judge.kind === "free") {
2025
2100
  if (!tiersOf(current).some((tier) => tier === "jev" || tier === "jev-opencode-free")) tiers = ["jev"];
2026
2101
  } else if (!tiersOf(current).some((tier) => JEV_TIERS.has(tier))) tiers = ["jev"];
2102
+ const turnOn = judgeOff(current);
2027
2103
  if (tiers && config.state === "unreadable") {
2028
2104
  notes.push(
2029
2105
  this.t({
@@ -2031,11 +2107,13 @@ var Setup = class {
2031
2107
  en: `${this.path(configPath)} isn't plain JSON, so it was left unchanged. Add "tiers": ${JSON.stringify(tiers)} by hand.`
2032
2108
  })
2033
2109
  );
2034
- } else if (tiers) {
2110
+ } else if (tiers || turnOn) {
2035
2111
  backups.save(configPath);
2036
- writeJsonFile(configPath, withTiers(current ?? {}, tiers));
2112
+ const next = tiers ? withTiers(current ?? {}, tiers) : current ?? {};
2113
+ writeJsonFile(configPath, turnOn ? withJudge(next, true) : next);
2114
+ const named2 = tiers ?? tiersOf(current);
2037
2115
  saved.push(
2038
- this.savedLine(configPath, this.t({ zh: `\u5224\u5B9A\u5668 ${tiers.join(", ")}`, en: `judge: ${tiers.join(", ")}` }))
2116
+ this.savedLine(configPath, this.t({ zh: `\u5224\u5B9A\u5668 ${named2.join(", ")}`, en: `judge: ${named2.join(", ")}` }))
2039
2117
  );
2040
2118
  }
2041
2119
  if (judge.kind === "jev") {
@@ -2081,14 +2159,19 @@ var Setup = class {
2081
2159
  target ? this.t({
2082
2160
  zh: "\u5728 mu \u91CC\u7528 /model \u6362\u6A21\u578B\uFF1B\u968F\u65F6\u53EF\u4EE5\u518D\u8FD0\u884C mu setup\u3002",
2083
2161
  en: "Switch models inside mu with /model, and run mu setup again any time."
2084
- }) : this.t({ zh: "\u542F\u52A8 mu \u540E\u8F93\u5165 /login \u767B\u5F55\u3002", en: "Start mu and type /login to sign in." })
2162
+ }) : this.t({ zh: "\u63A5\u4E0B\u6765\u5728 mu \u91CC\u7528 /login \u767B\u5F55\u3002", en: "Next, sign in with /login in mu." })
2085
2163
  );
2086
2164
  }
2087
- /** On its own, a finished setup offers a session; on a first start the launcher goes on into one anyway. */
2088
- async offerSession() {
2089
- if (this.args.firstRun || this.scripted) return SETUP_EXIT.done;
2165
+ /**
2166
+ * On its own, a finished setup offers a session; on a first start the launcher goes on into one anyway. After a
2167
+ * subscription was chosen (`signIn`), that session opens on /login: SETUP_EXIT.signIn.
2168
+ */
2169
+ async offerSession(signIn = false) {
2170
+ if (this.scripted) return SETUP_EXIT.done;
2171
+ if (this.args.firstRun) return signIn ? SETUP_EXIT.signIn : SETUP_EXIT.done;
2090
2172
  const start = await this.confirm(this.t({ zh: "\u73B0\u5728\u542F\u52A8 mu \u5417\uFF1F[Y/n] ", en: "Start mu now? [Y/n] " }), true);
2091
- return start ? SETUP_EXIT.start : SETUP_EXIT.done;
2173
+ if (!start) return SETUP_EXIT.done;
2174
+ return signIn ? SETUP_EXIT.signIn : SETUP_EXIT.start;
2092
2175
  }
2093
2176
  };
2094
2177
  function existingProviders(models) {