mu-agent 0.2.2 → 0.2.3

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.
@@ -2,7 +2,7 @@ import { createRequire as __muCreateRequire } from "node:module"; const require
2
2
 
3
3
  // packages/kyrn-judge/src/setup/main.ts
4
4
  import { constants as constants2 } from "node:os";
5
- import { join as join9 } from "node:path";
5
+ import { join as join10 } from "node:path";
6
6
  import {
7
7
  applyHttpProxySettings,
8
8
  configureHttpDispatcher,
@@ -54,9 +54,9 @@ function drop(lock) {
54
54
  // packages/kyrn-judge/src/mude/command.ts
55
55
  import { execFileSync as execFileSync2 } from "node:child_process";
56
56
  import { randomBytes } from "node:crypto";
57
- import { existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync6, rmSync as rmSync4, statfsSync, writeFileSync as writeFileSync6 } from "node:fs";
57
+ import { existsSync as existsSync6, mkdirSync as mkdirSync7, readdirSync as readdirSync2, readFileSync as readFileSync7, rmSync as rmSync4, statfsSync, writeFileSync as writeFileSync7 } from "node:fs";
58
58
  import { homedir as homedir4, release, totalmem as totalmem2 } from "node:os";
59
- import { join as join7 } from "node:path";
59
+ import { join as join8 } from "node:path";
60
60
 
61
61
  // packages/kyrn-judge/src/mude/config.ts
62
62
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
@@ -432,8 +432,8 @@ var FILES = [
432
432
  ];
433
433
  var MUDE_FILES = FILES.map(([path, size, sha256]) => ({ path, size, sha256 }));
434
434
  var MUDE_BYTES = MUDE_FILES.reduce((sum, file) => sum + file.size, 0);
435
- function mudeFileUrl(path, endpoint = "https://huggingface.co") {
436
- return `${endpoint.replace(/\/+$/, "")}/${MUDE_REPO}/resolve/${MUDE_REVISION}/${path.split("/").map(encodeURIComponent).join("/")}`;
435
+ function fileUrl(repo, revision, path, endpoint = "https://huggingface.co") {
436
+ return `${endpoint.replace(/\/+$/, "")}/${repo}/resolve/${revision}/${path.split("/").map(encodeURIComponent).join("/")}`;
437
437
  }
438
438
 
439
439
  // packages/kyrn-judge/src/mude/server.ts
@@ -539,7 +539,7 @@ async function startServer(paths, options) {
539
539
  "--port",
540
540
  String(options.port),
541
541
  "--name",
542
- MUDE_MODEL,
542
+ options.name ?? MUDE_MODEL,
543
543
  "--verify",
544
544
  "fast"
545
545
  ],
@@ -620,6 +620,11 @@ function serverMemory(pid) {
620
620
  var ROUTED_AWAY = ["tool.description", "tool.injection"];
621
621
  var MUDE = "mude";
622
622
  var KEY_VARIABLE = "MU_JUDGE_MUDE_KEY";
623
+ var MUDE_JUDGE = {
624
+ name: MUDE,
625
+ keyVariable: KEY_VARIABLE,
626
+ entry: (port2) => port2 === DEFAULT_PORT ? void 0 : entryFor(port2)
627
+ };
623
628
  function readState(paths) {
624
629
  try {
625
630
  const value = JSON.parse(readFileSync3(paths.state, "utf8"));
@@ -632,18 +637,22 @@ function writeState(paths, state) {
632
637
  writeFileSync3(paths.state, `${JSON.stringify(state, null, 2)}
633
638
  `, { mode: 384 });
634
639
  }
635
- function judgeSetting(files) {
640
+ function judgeSetting(files, name = MUDE) {
636
641
  const file = readJsonFile(judgeConfigPath(files.agentDir));
637
642
  if (file.state === "unreadable") return { enabled: false, localOnly: false, fallback: [], unreadable: true };
638
643
  const config = file.state === "ok" ? file.value : {};
639
644
  const tiers = tiersOf(config);
640
- const enabled = tiers[0] === MUDE && !judgeOff(config);
641
- const fallback = tiers.filter((tier) => tier !== MUDE);
645
+ const enabled = tiers[0] === name && !judgeOff(config);
646
+ const fallback = tiers.filter((tier) => tier !== name);
642
647
  return { enabled, localOnly: enabled && fallback.length === 0, fallback };
643
648
  }
644
- function mudeInTiers(files) {
649
+ function mudeInTiers(files, name = MUDE) {
650
+ const file = readJsonFile(judgeConfigPath(files.agentDir));
651
+ return file.state === "ok" && tiersOf(file.value).includes(name);
652
+ }
653
+ function currentTiers(files) {
645
654
  const file = readJsonFile(judgeConfigPath(files.agentDir));
646
- return file.state === "ok" && tiersOf(file.value).includes(MUDE);
655
+ return file.state === "ok" ? tiersOf(file.value) : [];
647
656
  }
648
657
  function readConfig(files) {
649
658
  const path = judgeConfigPath(files.agentDir);
@@ -677,18 +686,20 @@ function withoutOwnRoutes(config, state) {
677
686
  function entryFor(port2) {
678
687
  return { ...BUILT_IN_JUDGES[MUDE], baseUrl: mudeBaseUrl(port2) };
679
688
  }
680
- function enableMude(paths, files, options) {
689
+ function enableLocal(judge, paths, files, options) {
690
+ const name = judge.name;
681
691
  const { path, config } = readConfig(files);
682
692
  const state = readState(paths);
683
- const current = tiersOf(config).filter((tier) => tier !== MUDE);
693
+ const current = tiersOf(config).filter((tier) => tier !== name);
684
694
  const before = current.length > 0 ? current : [...state?.previousTiers ?? ["jev"]];
685
- const tiers = options.localOnly ? [MUDE] : [MUDE, ...before.filter((tier) => tier !== MUDE)];
695
+ const tiers = options.localOnly ? [name] : [name, ...before.filter((tier) => tier !== name)];
686
696
  const judgeWasOff = state?.judgeWasOff ?? judgeOff(config);
687
697
  let next = withJudge(withTiers(withoutOwnRoutes(config, state), tiers), true);
688
698
  const judges = judgesOf(next);
689
- const entry = options.port !== DEFAULT_PORT;
690
- if (entry) judges[MUDE] = entryFor(options.port);
691
- else if (state?.wroteEntry) delete judges[MUDE];
699
+ const written = judge.entry(options.port);
700
+ if (written) judges[name] = written;
701
+ else if (state?.wroteEntry) delete judges[name];
702
+ const entry = written !== void 0;
692
703
  next = withSection(next, "judges", judges);
693
704
  const fallback = tiers.slice(1);
694
705
  const routes = recordOf(next.routes);
@@ -704,9 +715,9 @@ function enableMude(paths, files, options) {
704
715
  const backups = new Backups(files.home, files.now ?? /* @__PURE__ */ new Date());
705
716
  backups.save(path);
706
717
  writeJsonFile(path, next);
707
- if (envFileValue(readText(files.envFile), KEY_VARIABLE) !== options.key) {
718
+ if (envFileValue(readText(files.envFile), judge.keyVariable) !== options.key) {
708
719
  backups.save(files.envFile);
709
- writeEnvValue(files.envFile, KEY_VARIABLE, options.key);
720
+ writeEnvValue(files.envFile, judge.keyVariable, options.key);
710
721
  }
711
722
  writeState(paths, {
712
723
  port: options.port,
@@ -718,15 +729,18 @@ function enableMude(paths, files, options) {
718
729
  return tiers;
719
730
  }
720
731
  function disableMude(paths, files, options = {}) {
732
+ return disableLocal(MUDE_JUDGE, paths, files, options);
733
+ }
734
+ function disableLocal(judge, paths, files, options = {}) {
721
735
  const { path, config } = readConfig(files);
722
736
  const state = readState(paths);
723
- const rest = tiersOf(config).filter((tier) => tier !== MUDE);
737
+ const rest = tiersOf(config).filter((tier) => tier !== judge.name);
724
738
  const tiers = rest.length > 0 ? rest : [...state?.previousTiers ?? ["jev"]];
725
739
  let next = withTiers(withoutOwnRoutes(config, state), tiers);
726
740
  if (state?.judgeWasOff) next = withJudge(next, false);
727
741
  if (options.removeEntry && state?.wroteEntry) {
728
742
  const judges = judgesOf(next);
729
- delete judges[MUDE];
743
+ delete judges[judge.name];
730
744
  next = withSection(next, "judges", judges);
731
745
  }
732
746
  new Backups(files.home, files.now ?? /* @__PURE__ */ new Date()).save(path);
@@ -792,6 +806,14 @@ async function sha256File(path) {
792
806
  await pipeline(createReadStream(path), hash);
793
807
  return hash.digest("hex");
794
808
  }
809
+ var pinnedHash = (file) => file.sha256 ?? file.gitSha1 ?? "";
810
+ async function hashOf(path, file) {
811
+ if (file.sha256 || !file.gitSha1) return sha256File(path);
812
+ const hash = createHash("sha1");
813
+ hash.update(`blob ${statSync3(path).size}\0`);
814
+ await pipeline(createReadStream(path), hash);
815
+ return hash.digest("hex");
816
+ }
795
817
  function readVerified(dir) {
796
818
  try {
797
819
  const value = JSON.parse(readFileSync4(join4(dir, ".mu-verified.json"), "utf8"));
@@ -802,7 +824,7 @@ function readVerified(dir) {
802
824
  }
803
825
  function knownGood(dir, file, verified) {
804
826
  const seen = verified[file.path];
805
- if (!seen || seen.sha256 !== file.sha256) return false;
827
+ if (!seen || seen.sha256 !== pinnedHash(file)) return false;
806
828
  try {
807
829
  const stats = statSync3(join4(dir, file.path));
808
830
  return stats.size === file.size && stats.mtimeMs === seen.mtimeMs;
@@ -812,7 +834,7 @@ function knownGood(dir, file, verified) {
812
834
  }
813
835
  function remember(dir, verified, file) {
814
836
  const stats = statSync3(join4(dir, file.path));
815
- verified[file.path] = { size: stats.size, mtimeMs: stats.mtimeMs, sha256: file.sha256 };
837
+ verified[file.path] = { size: stats.size, mtimeMs: stats.mtimeMs, sha256: pinnedHash(file) };
816
838
  writeFileSync4(join4(dir, ".mu-verified.json"), `${JSON.stringify(verified, null, 1)}
817
839
  `);
818
840
  }
@@ -863,7 +885,7 @@ async function downloadFiles(options) {
863
885
  mkdirSync4(dirname2(target), { recursive: true });
864
886
  if (existsSync4(target) && statSync3(target).size === file.size) {
865
887
  options.onProgress?.({ file: file.path, checking: true, done: settled, total });
866
- if (await sha256File(target) === file.sha256) {
888
+ if (await hashOf(target, file) === pinnedHash(file)) {
867
889
  remember(dir, verified, file);
868
890
  settled += file.size;
869
891
  continue;
@@ -874,17 +896,17 @@ async function downloadFiles(options) {
874
896
  const onBytes = (bytes) => options.onProgress?.({ file: file.path, checking: false, done: settled + bytes, total });
875
897
  let resumed = await fetchFile(options, file, part, onBytes);
876
898
  options.onProgress?.({ file: file.path, checking: true, done: settled + file.size, total });
877
- let actual = await sha256File(part);
878
- if (actual !== file.sha256 && resumed) {
899
+ let actual = await hashOf(part, file);
900
+ if (actual !== pinnedHash(file) && resumed) {
879
901
  dropPart(part);
880
902
  resumed = await fetchFile(options, file, part, onBytes);
881
903
  options.onProgress?.({ file: file.path, checking: true, done: settled + file.size, total });
882
- actual = await sha256File(part);
904
+ actual = await hashOf(part, file);
883
905
  }
884
- if (actual !== file.sha256) {
906
+ if (actual !== pinnedHash(file)) {
885
907
  dropPart(part);
886
908
  throw new DownloadError(
887
- `${file.path} does not match its pinned sha256 (got ${actual.slice(0, 12)}\u2026, want ${file.sha256.slice(0, 12)}\u2026); it was removed`,
909
+ `${file.path} does not match its pinned ${file.sha256 ? "sha256" : "git hash"} (got ${actual.slice(0, 12)}\u2026, want ${pinnedHash(file).slice(0, 12)}\u2026); it was removed`,
888
910
  "integrity"
889
911
  );
890
912
  }
@@ -1028,6 +1050,10 @@ async function fetchOnce(options, file, part, onBytes) {
1028
1050
  }
1029
1051
  }
1030
1052
 
1053
+ // packages/kyrn-judge/src/mude/models.ts
1054
+ import { mkdirSync as mkdirSync5, readdirSync, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
1055
+ import { join as join6 } from "node:path";
1056
+
1031
1057
  // packages/kyrn-judge/src/mude/paths.ts
1032
1058
  import { join as join5 } from "node:path";
1033
1059
  function mudePaths(muHome2) {
@@ -1051,12 +1077,72 @@ function mudePaths(muHome2) {
1051
1077
  };
1052
1078
  }
1053
1079
 
1080
+ // packages/kyrn-judge/src/mude/models.ts
1081
+ function modelsDir(muHome2) {
1082
+ return join6(muHome2, "local-judge", "models");
1083
+ }
1084
+ function modelPaths(muHome2, record) {
1085
+ const shared = mudePaths(muHome2);
1086
+ const root = join6(modelsDir(muHome2), record.id);
1087
+ const model = join6(root, "model", record.revision);
1088
+ return {
1089
+ ...shared,
1090
+ root,
1091
+ model,
1092
+ verified: join6(model, ".mu-verified.json"),
1093
+ key: join6(root, "key"),
1094
+ pid: join6(root, "server.pid"),
1095
+ log: join6(root, "server.log"),
1096
+ state: join6(root, "state.json"),
1097
+ install: join6(root, "install.json"),
1098
+ installLock: join6(root, "install.lock")
1099
+ };
1100
+ }
1101
+ function isRecord2(value) {
1102
+ if (typeof value !== "object" || value === null) return false;
1103
+ const record = value;
1104
+ return typeof record.id === "string" && typeof record.repo === "string" && typeof record.revision === "string" && typeof record.bytes === "number" && Array.isArray(record.files);
1105
+ }
1106
+ function listRecords(muHome2) {
1107
+ let names;
1108
+ try {
1109
+ names = readdirSync(modelsDir(muHome2));
1110
+ } catch {
1111
+ return [];
1112
+ }
1113
+ const records = [];
1114
+ for (const name of names.sort()) {
1115
+ try {
1116
+ const value = JSON.parse(readFileSync5(join6(modelsDir(muHome2), name, "model.json"), "utf8"));
1117
+ if (isRecord2(value) && value.id === name) records.push(value);
1118
+ } catch {
1119
+ }
1120
+ }
1121
+ return records;
1122
+ }
1123
+ function keyVariable(id) {
1124
+ return `MU_JUDGE_${id.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}_KEY`;
1125
+ }
1126
+ function localJudgeOf(id) {
1127
+ return {
1128
+ name: id,
1129
+ keyVariable: keyVariable(id),
1130
+ entry: (port2) => ({
1131
+ type: "typesafe",
1132
+ baseUrl: mudeBaseUrl(port2),
1133
+ model: id,
1134
+ apiKeyEnv: keyVariable(id),
1135
+ timeoutMs: 4e3
1136
+ })
1137
+ };
1138
+ }
1139
+
1054
1140
  // packages/kyrn-judge/src/mude/python.ts
1055
1141
  import { spawn as spawn2 } from "node:child_process";
1056
1142
  import { createHash as createHash2 } from "node:crypto";
1057
- import { accessSync, constants, existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "node:fs";
1143
+ import { accessSync, constants, existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync6, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "node:fs";
1058
1144
  import { homedir as homedir3 } from "node:os";
1059
- import { delimiter, join as join6 } from "node:path";
1145
+ import { delimiter, join as join7 } from "node:path";
1060
1146
  var PYTHON_VERSION = "3.13";
1061
1147
  var UV_RELEASE = {
1062
1148
  version: "0.13.1",
@@ -1074,14 +1160,14 @@ function executable(path) {
1074
1160
  function findUv(paths, env = process.env, home2 = homedir3()) {
1075
1161
  const dirs = [
1076
1162
  ...(env.PATH ?? "").split(delimiter),
1077
- join6(home2, ".local", "bin"),
1078
- join6(home2, ".cargo", "bin"),
1163
+ join7(home2, ".local", "bin"),
1164
+ join7(home2, ".cargo", "bin"),
1079
1165
  "/opt/homebrew/bin",
1080
1166
  "/usr/local/bin",
1081
1167
  paths.tools
1082
1168
  ];
1083
1169
  for (const dir of dirs) {
1084
- if (dir && executable(join6(dir, "uv"))) return join6(dir, "uv");
1170
+ if (dir && executable(join7(dir, "uv"))) return join7(dir, "uv");
1085
1171
  }
1086
1172
  return void 0;
1087
1173
  }
@@ -1111,7 +1197,7 @@ function run(command, args, options = {}) {
1111
1197
  });
1112
1198
  }
1113
1199
  async function downloadUv(paths, options = {}) {
1114
- mkdirSync5(paths.tools, { recursive: true });
1200
+ mkdirSync6(paths.tools, { recursive: true });
1115
1201
  const archive = "uv.tar.gz";
1116
1202
  options.onLine?.(`Downloading uv ${UV_RELEASE.version}`);
1117
1203
  const timeoutMs = options.timeoutMs ?? 3e5;
@@ -1131,27 +1217,27 @@ async function downloadUv(paths, options = {}) {
1131
1217
  const actual = createHash2("sha256").update(bytes).digest("hex");
1132
1218
  if (actual !== UV_RELEASE.sha256)
1133
1219
  throw new Error("The downloaded uv does not match its pinned sha256; nothing was installed");
1134
- writeFileSync5(join6(paths.tools, archive), bytes);
1220
+ writeFileSync6(join7(paths.tools, archive), bytes);
1135
1221
  const unpacked = await run("/usr/bin/tar", [
1136
1222
  "-xzf",
1137
- join6(paths.tools, archive),
1223
+ join7(paths.tools, archive),
1138
1224
  "-C",
1139
1225
  paths.tools,
1140
1226
  "--strip-components",
1141
1227
  "1"
1142
1228
  ]);
1143
- rmSync3(join6(paths.tools, archive), { force: true });
1144
- if (unpacked.code !== 0 || !executable(join6(paths.tools, "uv")))
1229
+ rmSync3(join7(paths.tools, archive), { force: true });
1230
+ if (unpacked.code !== 0 || !executable(join7(paths.tools, "uv")))
1145
1231
  throw new Error(`Couldn't unpack uv: ${unpacked.output}`);
1146
- return join6(paths.tools, "uv");
1232
+ return join7(paths.tools, "uv");
1147
1233
  }
1148
1234
  function lockDigest(lockFile) {
1149
- return createHash2("sha256").update(readFileSync5(lockFile)).digest("hex");
1235
+ return createHash2("sha256").update(readFileSync6(lockFile)).digest("hex");
1150
1236
  }
1151
1237
  function venvReady(paths, lockFile) {
1152
1238
  if (!existsSync5(paths.python)) return false;
1153
1239
  try {
1154
- return readFileSync5(paths.venvLock, "utf8").trim() === lockDigest(lockFile);
1240
+ return readFileSync6(paths.venvLock, "utf8").trim() === lockDigest(lockFile);
1155
1241
  } catch {
1156
1242
  return false;
1157
1243
  }
@@ -1185,7 +1271,7 @@ ${made.output}`);
1185
1271
  if (installed2.code !== 0)
1186
1272
  throw new Error(`Couldn't install mude's Python packages (uv pip install):
1187
1273
  ${installed2.output}`);
1188
- writeFileSync5(paths.venvLock, `${lockDigest(lockFile)}
1274
+ writeFileSync6(paths.venvLock, `${lockDigest(lockFile)}
1189
1275
  `);
1190
1276
  }
1191
1277
 
@@ -1223,7 +1309,7 @@ function jsonOut(write) {
1223
1309
  error: (message, kind) => emit({ type: "error", message, kind })
1224
1310
  };
1225
1311
  }
1226
- function humanOut(write, tty) {
1312
+ function humanOut(write, tty, pkg = MUDE_PACKAGE) {
1227
1313
  let shown = -1;
1228
1314
  return {
1229
1315
  step: (_step, message) => {
@@ -1247,7 +1333,7 @@ function humanOut(write, tty) {
1247
1333
  },
1248
1334
  line: (text) => write(` ${text}
1249
1335
  `),
1250
- status: (status2) => write(describe(status2)),
1336
+ status: (status2) => write(describe(status2, pkg)),
1251
1337
  error: (message) => {
1252
1338
  if (shown >= 0 && tty) write("\n");
1253
1339
  shown = -1;
@@ -1256,31 +1342,43 @@ function humanOut(write, tty) {
1256
1342
  }
1257
1343
  };
1258
1344
  }
1259
- function describe(status2) {
1345
+ function describe(status2, pkg) {
1260
1346
  const lines = [];
1261
1347
  if (!status2.supported) {
1262
- lines.push(`mude 1.0 can't run here: ${status2.problems.join("; ")}`);
1348
+ lines.push(`${pkg.label} can't run here: ${status2.problems.join("; ")}`);
1263
1349
  return `${lines.join("\n")}
1264
1350
  `;
1265
1351
  }
1266
1352
  lines.push(
1267
- `model ${status2.installed ? `installed (${MUDE_REPO} @ ${MUDE_REVISION.slice(0, 7)})` : status2.downloadedBytes > 0 ? `partly downloaded (${(status2.downloadedBytes / 1e9).toFixed(1)} of ${(MUDE_BYTES / 1e9).toFixed(1)} GB): mu judge mude install resumes it` : "not installed: mu judge mude install"}`
1353
+ `model ${status2.installed ? `installed (${pkg.repo} @ ${pkg.revision.slice(0, 7)})` : status2.downloadedBytes > 0 ? `partly downloaded (${(status2.downloadedBytes / 1e9).toFixed(1)} of ${(pkg.bytes / 1e9).toFixed(1)} GB): ${pkg.install} resumes it` : `not installed: ${pkg.install}`}`
1268
1354
  );
1269
- if (status2.installed && !status2.pythonReady) lines.push("python not set up: mu judge mude install");
1355
+ if (status2.installed && !status2.pythonReady) lines.push(`python not set up: ${pkg.install}`);
1270
1356
  lines.push(
1271
1357
  `server ${status2.ready ? `running on 127.0.0.1:${status2.port}${status2.memoryBytes ? ` \xB7 ${(status2.memoryBytes / 1e9).toFixed(1)} GB of memory` : ""}` : status2.running ? `starting on 127.0.0.1:${status2.port}` : "stopped"}`
1272
1358
  );
1273
1359
  if (status2.enabled) {
1274
1360
  lines.push(
1275
- status2.localOnly ? "judge mude 1.0 only: a decision it cannot answer takes its fallback, and nothing goes to a hosted judge" : `judge mude 1.0, then ${status2.fallback.join(" > ")} for what it cannot answer (what those read leaves this machine)`
1361
+ status2.localOnly ? `judge ${pkg.label} only: a decision it cannot answer takes its fallback, and nothing goes to a hosted judge` : `judge ${pkg.label}, then ${status2.fallback.join(" > ")} for what it cannot answer (what those read leaves this machine)`
1276
1362
  );
1277
1363
  } else {
1278
- lines.push(`judge ${status2.installed ? "not enabled: mu judge mude enable" : "not enabled"}`);
1364
+ lines.push(`judge ${status2.installed ? `not enabled: ${pkg.again("enable")}` : "not enabled"}`);
1279
1365
  }
1280
1366
  lines.push(`folder ${status2.folder}`);
1281
1367
  return `${lines.join("\n")}
1282
1368
  `;
1283
1369
  }
1370
+ var MUDE_PACKAGE = {
1371
+ label: "mude 1.0",
1372
+ repo: MUDE_REPO,
1373
+ revision: MUDE_REVISION,
1374
+ files: MUDE_FILES,
1375
+ bytes: MUDE_BYTES,
1376
+ judge: MUDE_JUDGE,
1377
+ served: MUDE_MODEL,
1378
+ install: "mu judge mude install",
1379
+ again: (what) => `mu judge mude ${what}`
1380
+ };
1381
+ var pkgOf = (ctx) => ctx.pkg ?? MUDE_PACKAGE;
1284
1382
  function problems(options = {}) {
1285
1383
  const platform = options.platform ?? process.platform;
1286
1384
  if (platform !== "darwin") return ["it needs a Mac with Apple silicon (an M1 or newer)"];
@@ -1307,7 +1405,7 @@ function appleSilicon() {
1307
1405
  function freeBytes(path) {
1308
1406
  let dir = path;
1309
1407
  while (!existsSync6(dir)) {
1310
- const parent = join7(dir, "..");
1408
+ const parent = join8(dir, "..");
1311
1409
  if (parent === dir) return void 0;
1312
1410
  dir = parent;
1313
1411
  }
@@ -1323,7 +1421,7 @@ function installLocalOnly(ctx, args) {
1323
1421
  if (args.includes("--local-only")) return true;
1324
1422
  if (args.includes("--with-fallback")) return false;
1325
1423
  try {
1326
- const value = JSON.parse(readFileSync6(ctx.paths.install, "utf8"));
1424
+ const value = JSON.parse(readFileSync7(ctx.paths.install, "utf8"));
1327
1425
  return typeof value === "object" && value !== null && "localOnly" in value && value.localOnly === true;
1328
1426
  } catch {
1329
1427
  return false;
@@ -1336,46 +1434,46 @@ function startedAt(pid) {
1336
1434
  return void 0;
1337
1435
  }
1338
1436
  }
1339
- function holdInstall(paths) {
1340
- mkdirSync6(paths.root, { recursive: true });
1437
+ function holdInstall(paths, pkg = MUDE_PACKAGE) {
1438
+ mkdirSync7(paths.root, { recursive: true });
1341
1439
  const mine = `${JSON.stringify({ pid: process.pid, started: startedAt(process.pid) ?? "" })}
1342
1440
  `;
1343
1441
  try {
1344
- writeFileSync6(paths.installLock, mine, { flag: "wx" });
1442
+ writeFileSync7(paths.installLock, mine, { flag: "wx" });
1345
1443
  } catch (error) {
1346
1444
  if (error.code !== "EEXIST") throw error;
1347
1445
  let holder = {};
1348
1446
  try {
1349
- holder = JSON.parse(readFileSync6(paths.installLock, "utf8"));
1447
+ holder = JSON.parse(readFileSync7(paths.installLock, "utf8"));
1350
1448
  } catch {
1351
1449
  }
1352
1450
  const pid = typeof holder.pid === "number" ? holder.pid : 0;
1353
1451
  if (pid > 0 && pid !== process.pid && holder.started && startedAt(pid) === holder.started) {
1354
1452
  throw new Error(
1355
- `Another mude 1.0 install is running (process ${pid}). Wait for it to finish, or stop it, then run mu judge mude install again`
1453
+ `Another ${pkg.label} install is running (process ${pid}). Wait for it to finish, or stop it, then run ${pkg.install} again`
1356
1454
  );
1357
1455
  }
1358
- writeFileSync6(paths.installLock, mine);
1456
+ writeFileSync7(paths.installLock, mine);
1359
1457
  }
1360
1458
  return () => {
1361
1459
  try {
1362
- if (readFileSync6(paths.installLock, "utf8") === mine) rmSync4(paths.installLock, { force: true });
1460
+ if (readFileSync7(paths.installLock, "utf8") === mine) rmSync4(paths.installLock, { force: true });
1363
1461
  } catch {
1364
1462
  }
1365
1463
  };
1366
1464
  }
1367
1465
  function installed(ctx) {
1368
- return missingFiles(ctx.paths.model, MUDE_FILES).length === 0;
1466
+ return missingFiles(ctx.paths.model, pkgOf(ctx).files).length === 0;
1369
1467
  }
1370
1468
  function key(ctx) {
1371
1469
  try {
1372
- const existing = readFileSync6(ctx.paths.key, "utf8").trim();
1470
+ const existing = readFileSync7(ctx.paths.key, "utf8").trim();
1373
1471
  if (existing) return existing;
1374
1472
  } catch {
1375
1473
  }
1376
- mkdirSync6(ctx.paths.root, { recursive: true });
1474
+ mkdirSync7(ctx.paths.root, { recursive: true });
1377
1475
  const made = randomBytes(32).toString("hex");
1378
- writeFileSync6(ctx.paths.key, `${made}
1476
+ writeFileSync7(ctx.paths.key, `${made}
1379
1477
  `, { mode: 384 });
1380
1478
  return made;
1381
1479
  }
@@ -1385,18 +1483,20 @@ async function port(ctx) {
1385
1483
  return choosePort(DEFAULT_PORT);
1386
1484
  }
1387
1485
  async function status(ctx) {
1486
+ const pkg = pkgOf(ctx);
1388
1487
  const found = problems();
1389
1488
  const state = readState(ctx.paths);
1390
1489
  const at = state?.port ?? DEFAULT_PORT;
1391
1490
  const pid = readPid(ctx.paths);
1392
1491
  const running = serverAlive(ctx.paths, pid);
1393
1492
  const ready = running && (await health(at))?.status === "ready";
1394
- const setting = judgeSetting(ctx.files);
1493
+ const setting = judgeSetting(ctx.files, pkg.judge.name);
1395
1494
  return {
1495
+ name: pkg.judge.name,
1396
1496
  supported: found.length === 0,
1397
1497
  problems: found,
1398
1498
  installed: installed(ctx),
1399
- downloadedBytes: downloadedBytes(ctx.paths.model, MUDE_FILES),
1499
+ downloadedBytes: downloadedBytes(ctx.paths.model, pkg.files),
1400
1500
  pythonReady: venvReady(ctx.paths, ctx.lockFile),
1401
1501
  running,
1402
1502
  ready,
@@ -1406,32 +1506,33 @@ async function status(ctx) {
1406
1506
  enabled: setting.enabled,
1407
1507
  localOnly: setting.localOnly,
1408
1508
  fallback: setting.fallback,
1409
- model: `${MUDE_REPO}@${MUDE_REVISION}`,
1509
+ model: `${pkg.repo}@${pkg.revision}`,
1410
1510
  folder: ctx.paths.root,
1411
1511
  log: ctx.paths.log
1412
1512
  };
1413
1513
  }
1414
1514
  async function ensureInstalled(ctx, out) {
1415
- const missing = missingFiles(ctx.paths.model, MUDE_FILES);
1515
+ const pkg = pkgOf(ctx);
1516
+ const missing = missingFiles(ctx.paths.model, pkg.files);
1416
1517
  if (missing.length > 0) {
1417
1518
  const need = missing.reduce((sum, file) => sum + file.size, 0);
1418
- const already = downloadedBytes(ctx.paths.model, MUDE_FILES);
1519
+ const already = downloadedBytes(ctx.paths.model, pkg.files);
1419
1520
  const free = freeBytes(ctx.paths.root);
1420
- const room = MUDE_BYTES - already + 2e9;
1521
+ const room = pkg.bytes - already + 2e9;
1421
1522
  if (free !== void 0 && free < room) {
1422
1523
  throw new Error(
1423
- `Not enough free disk space: mude 1.0 needs about ${gb(room)} GB, and only ${gb(free)} GB are free. Free up some space, then run mu judge mude install again`
1524
+ `Not enough free disk space: ${pkg.label} needs about ${gb(room)} GB, and only ${gb(free)} GB are free. Free up some space, then run ${pkg.install} again`
1424
1525
  );
1425
1526
  }
1426
1527
  out.step(
1427
1528
  "download",
1428
- already > 0 ? `Resuming the mude 1.0 download (${MUDE_REPO}): ${gb(already)} of ${gb(MUDE_BYTES)} GB already downloaded` : `Downloading mude 1.0 (${MUDE_REPO}, ${gb(need)} GB) and checking every file`
1529
+ already > 0 ? `Resuming the ${pkg.label} download (${pkg.repo}): ${gb(already)} of ${gb(pkg.bytes)} GB already downloaded` : `Downloading ${pkg.label} (${pkg.repo}, ${gb(need)} GB) and checking every file`
1429
1530
  );
1430
1531
  try {
1431
1532
  await downloadFiles({
1432
1533
  dir: ctx.paths.model,
1433
- files: MUDE_FILES,
1434
- url: (path) => mudeFileUrl(path, ctx.env.HF_ENDPOINT || void 0),
1534
+ files: pkg.files,
1535
+ url: (path) => fileUrl(pkg.repo, pkg.revision, path, ctx.env.HF_ENDPOINT || void 0),
1435
1536
  token: huggingFaceToken(ctx.env),
1436
1537
  onProgress: (event) => out.progress(event),
1437
1538
  signal: ctx.signal
@@ -1439,7 +1540,7 @@ async function ensureInstalled(ctx, out) {
1439
1540
  } catch (error) {
1440
1541
  if (error instanceof DownloadError && error.kind === "aborted") {
1441
1542
  throw new DownloadError(
1442
- `Stopped with ${gb(downloadedBytes(ctx.paths.model, MUDE_FILES))} of ${gb(MUDE_BYTES)} GB downloaded. Run mu judge mude install to resume`,
1543
+ `Stopped with ${gb(downloadedBytes(ctx.paths.model, pkg.files))} of ${gb(pkg.bytes)} GB downloaded. Run ${pkg.install} to resume`,
1443
1544
  "aborted"
1444
1545
  );
1445
1546
  }
@@ -1447,7 +1548,7 @@ async function ensureInstalled(ctx, out) {
1447
1548
  }
1448
1549
  }
1449
1550
  if (!venvReady(ctx.paths, ctx.lockFile)) {
1450
- out.step("python", "Setting up the Python environment for mude 1.0 (MLX, transformers; about 1 GB, once)");
1551
+ out.step("python", `Setting up the Python environment for ${pkg.label} (MLX, transformers; about 1 GB, once)`);
1451
1552
  try {
1452
1553
  let uv = findUv(ctx.paths, ctx.env);
1453
1554
  if (!uv) uv = await downloadUv(ctx.paths, { onLine: (line) => out.line(line), signal: ctx.signal });
@@ -1455,7 +1556,7 @@ async function ensureInstalled(ctx, out) {
1455
1556
  } catch (error) {
1456
1557
  if (ctx.signal?.aborted) {
1457
1558
  throw new DownloadError(
1458
- "Stopped while setting up the Python environment. Run mu judge mude install to resume",
1559
+ `Stopped while setting up the Python environment. Run ${pkg.install} to resume`,
1459
1560
  "aborted"
1460
1561
  );
1461
1562
  }
@@ -1465,21 +1566,23 @@ async function ensureInstalled(ctx, out) {
1465
1566
  }
1466
1567
  function requireInstalled(ctx) {
1467
1568
  if (!installed(ctx) || !venvReady(ctx.paths, ctx.lockFile)) {
1468
- throw new Error("mude 1.0 is not installed here yet: mu judge mude install");
1569
+ const pkg = pkgOf(ctx);
1570
+ throw new Error(`${pkg.label} is not installed here yet: ${pkg.install}`);
1469
1571
  }
1470
1572
  }
1471
1573
  async function start(ctx, out, command, wait2 = true) {
1574
+ const pkg = pkgOf(ctx);
1472
1575
  const at = await port(ctx);
1473
1576
  out.step(
1474
1577
  "start",
1475
- serverAlive(ctx.paths) ? `The mude 1.0 server is running on 127.0.0.1:${at}` : `Starting the mude 1.0 server on 127.0.0.1:${at} (loading the model takes a few seconds)`
1578
+ serverAlive(ctx.paths) ? `The ${pkg.label} server is running on 127.0.0.1:${at}` : `Starting the ${pkg.label} server on 127.0.0.1:${at} (loading the model takes a few seconds)`
1476
1579
  );
1477
1580
  try {
1478
- await startServer(ctx.paths, { port: at, key: key(ctx), wait: wait2, signal: ctx.signal });
1581
+ await startServer(ctx.paths, { port: at, key: key(ctx), wait: wait2, signal: ctx.signal, name: pkg.served });
1479
1582
  } catch (error) {
1480
1583
  if (error instanceof StartStopped) {
1481
1584
  throw new DownloadError(
1482
- error.serverStopped ? `Stopped while the mude 1.0 server was starting, and stopped the server too. Run mu judge mude ${command} again to finish` : `Stopped waiting for the mude 1.0 server, which is still starting. Run mu judge mude ${command} again to finish`,
1585
+ error.serverStopped ? `Stopped while the ${pkg.label} server was starting, and stopped the server too. Run ${pkg.again(command)} again to finish` : `Stopped waiting for the ${pkg.label} server, which is still starting. Run ${pkg.again(command)} again to finish`,
1483
1586
  "aborted"
1484
1587
  );
1485
1588
  }
@@ -1487,13 +1590,57 @@ async function start(ctx, out, command, wait2 = true) {
1487
1590
  }
1488
1591
  return at;
1489
1592
  }
1490
- function enable(ctx, out, at, localOnly) {
1491
- const tiers = enableMude(ctx.paths, ctx.files, { port: at, key: key(ctx), localOnly });
1593
+ function others(ctx) {
1594
+ const name = pkgOf(ctx).judge.name;
1595
+ const home2 = ctx.files.home;
1596
+ const found = [];
1597
+ if (name !== MUDE) found.push({ judge: MUDE_JUDGE, paths: mudePaths(home2) });
1598
+ for (const record of listRecords(home2)) {
1599
+ if (record.id !== name) found.push({ judge: localJudgeOf(record.id), paths: modelPaths(home2, record) });
1600
+ }
1601
+ return found;
1602
+ }
1603
+ async function stopOthers(ctx) {
1604
+ for (const other of others(ctx)) await stopServer(other.paths);
1605
+ }
1606
+ async function enable(ctx, out, at, localOnly) {
1607
+ const pkg = pkgOf(ctx);
1608
+ const tiers = currentTiers(ctx.files);
1609
+ for (const other of others(ctx)) {
1610
+ if (tiers.includes(other.judge.name)) disableLocal(other.judge, other.paths, ctx.files);
1611
+ }
1612
+ const order = enableLocal(pkg.judge, ctx.paths, ctx.files, { port: at, key: key(ctx), localOnly });
1492
1613
  out.step(
1493
1614
  "enable",
1494
- localOnly ? "mude 1.0 is the judge, on its own. New mu sessions use it; one that is open now picks it up when it starts again." : `mude 1.0 is the judge, with ${tiers.slice(1).join(" > ")} behind it for what it cannot answer. New mu sessions use it.`
1615
+ localOnly ? `${pkg.label} is the judge, on its own. New mu sessions use it; one that is open now picks it up when it starts again.` : `${pkg.label} is the judge, with ${order.slice(1).join(" > ")} behind it for what it cannot answer. New mu sessions use it.`
1495
1616
  );
1496
1617
  }
1618
+ async function install(ctx, out, args, holdAlso) {
1619
+ const localOnly = installLocalOnly(ctx, args);
1620
+ const release2 = holdInstall(ctx.paths, pkgOf(ctx));
1621
+ let releaseAlso;
1622
+ try {
1623
+ if (holdAlso && !venvReady(ctx.paths, ctx.lockFile)) releaseAlso = holdInstall(holdAlso);
1624
+ writeFileSync7(ctx.paths.install, `${JSON.stringify({ localOnly })}
1625
+ `);
1626
+ await ensureInstalled(ctx, out);
1627
+ await stopOthers(ctx);
1628
+ const at = await start(ctx, out, "install");
1629
+ await enable(ctx, out, at, localOnly);
1630
+ rmSync4(ctx.paths.install, { force: true });
1631
+ } finally {
1632
+ releaseAlso?.();
1633
+ release2();
1634
+ }
1635
+ }
1636
+ async function enableCommand(ctx, out, args) {
1637
+ requireInstalled(ctx);
1638
+ const current = judgeSetting(ctx.files, pkgOf(ctx).judge.name);
1639
+ const localOnly = args.includes("--local-only") ? true : args.includes("--with-fallback") ? false : current.enabled && current.localOnly;
1640
+ await stopOthers(ctx);
1641
+ const at = await start(ctx, out, "enable");
1642
+ await enable(ctx, out, at, localOnly);
1643
+ }
1497
1644
  async function main(argv, ctx, write, tty = false) {
1498
1645
  const json = argv.includes("--json");
1499
1646
  const args = argv.filter((arg) => arg !== "--json");
@@ -1520,29 +1667,12 @@ async function main(argv, ctx, write, tty = false) {
1520
1667
  return 1;
1521
1668
  }
1522
1669
  switch (command) {
1523
- case "install": {
1524
- const localOnly = installLocalOnly(ctx, args);
1525
- const release2 = holdInstall(ctx.paths);
1526
- try {
1527
- writeFileSync6(ctx.paths.install, `${JSON.stringify({ localOnly })}
1528
- `);
1529
- await ensureInstalled(ctx, out);
1530
- const at = await start(ctx, out, "install");
1531
- enable(ctx, out, at, localOnly);
1532
- rmSync4(ctx.paths.install, { force: true });
1533
- } finally {
1534
- release2();
1535
- }
1670
+ case "install":
1671
+ await install(ctx, out, args);
1536
1672
  break;
1537
- }
1538
- case "enable": {
1539
- requireInstalled(ctx);
1540
- const current = judgeSetting(ctx.files);
1541
- const localOnly = flag("--local-only") ? true : flag("--with-fallback") ? false : current.enabled && current.localOnly;
1542
- const at = await start(ctx, out, "enable");
1543
- enable(ctx, out, at, localOnly);
1673
+ case "enable":
1674
+ await enableCommand(ctx, out, args);
1544
1675
  break;
1545
- }
1546
1676
  case "disable": {
1547
1677
  if (!mudeInTiers(ctx.files)) {
1548
1678
  const stopped2 = await stopServer(ctx.paths);
@@ -1573,8 +1703,20 @@ async function main(argv, ctx, write, tty = false) {
1573
1703
  out.step("disable", `mude 1.0 is no longer the judge: ${tiers.join(" > ")} again.`);
1574
1704
  }
1575
1705
  await stopServer(ctx.paths);
1576
- rmSync4(ctx.paths.root, { recursive: true, force: true });
1577
- out.step("uninstall", `Deleted ${ctx.paths.root}: the model, its Python environment and its key.`);
1706
+ if (listRecords(ctx.files.home).length > 0) {
1707
+ for (const name of existsSync6(ctx.paths.root) ? readdirSync2(ctx.paths.root) : []) {
1708
+ const path = join8(ctx.paths.root, name);
1709
+ if (path !== ctx.paths.venv && path !== ctx.paths.tools)
1710
+ rmSync4(path, { recursive: true, force: true });
1711
+ }
1712
+ out.step(
1713
+ "uninstall",
1714
+ `Deleted mude 1.0 and its key from ${ctx.paths.root}. Its Python environment stays there for the other decision models.`
1715
+ );
1716
+ } else {
1717
+ rmSync4(ctx.paths.root, { recursive: true, force: true });
1718
+ out.step("uninstall", `Deleted ${ctx.paths.root}: the model, its Python environment and its key.`);
1719
+ }
1578
1720
  break;
1579
1721
  }
1580
1722
  default:
@@ -1592,12 +1734,12 @@ ${USAGE}`, "usage");
1592
1734
  }
1593
1735
  }
1594
1736
  function contextFromEnv(env = process.env, home2 = homedir4()) {
1595
- const muHome2 = env.MU_MUDE_HOME || join7(home2, ".mu");
1596
- const agentDir2 = env.MU_AGENT_DIR || join7(muHome2, "agent");
1737
+ const muHome2 = env.MU_MUDE_HOME || join8(home2, ".mu");
1738
+ const agentDir2 = env.MU_AGENT_DIR || join8(muHome2, "agent");
1597
1739
  return {
1598
1740
  paths: mudePaths(muHome2),
1599
- files: { agentDir: agentDir2, home: muHome2, envFile: env.MU_MUDE_ENV_FILE || join7(muHome2, ".env") },
1600
- lockFile: env.MU_MUDE_LOCK || join7(import.meta.dirname, "..", "..", "..", "..", "kyrn", "local-judge", "mude-requirements.lock.txt"),
1741
+ files: { agentDir: agentDir2, home: muHome2, envFile: env.MU_MUDE_ENV_FILE || join8(muHome2, ".env") },
1742
+ lockFile: env.MU_MUDE_LOCK || join8(import.meta.dirname, "..", "..", "..", "..", "kyrn", "local-judge", "mude-requirements.lock.txt"),
1601
1743
  env
1602
1744
  };
1603
1745
  }
@@ -1799,8 +1941,8 @@ async function readAll(input = process.stdin) {
1799
1941
  }
1800
1942
 
1801
1943
  // packages/kyrn-judge/src/setup/wizard.ts
1802
- import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync7 } from "node:fs";
1803
- import { join as join8 } from "node:path";
1944
+ import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync8 } from "node:fs";
1945
+ import { join as join9 } from "node:path";
1804
1946
 
1805
1947
  // packages/kyrn-judge/src/setup/check.ts
1806
1948
  var CHECK_TIMEOUT_MS = 1e4;
@@ -2600,14 +2742,6 @@ var Setup = class {
2600
2742
  SETUP_EXIT.usage
2601
2743
  );
2602
2744
  }
2603
- if (!args.firstRun) {
2604
- this.io.say(
2605
- this.t({
2606
- zh: "mu setup\uFF1A\u7ED9 mu \u63A5\u4E0A\u4E00\u4E2A\u5E72\u6D3B\u7684\u6A21\u578B\uFF0C\u518D\u9009\u4E00\u4E2A\u5224\u5B9A\u5668\u3002",
2607
- en: "mu setup: connect a model for mu to work with, and choose a judge."
2608
- })
2609
- );
2610
- }
2611
2745
  const start2 = await this.start(key2 === void 0 ? void 0 : cleanKey(key2));
2612
2746
  if (start2 === "subscription") {
2613
2747
  const judge2 = await this.judge(void 0);
@@ -2655,9 +2789,9 @@ var Setup = class {
2655
2789
  );
2656
2790
  if (yes) return true;
2657
2791
  try {
2658
- mkdirSync7(this.deps.paths.agentDir, { recursive: true });
2659
- writeFileSync7(
2660
- join8(this.deps.paths.agentDir, SETUP_DECLINED_FILE),
2792
+ mkdirSync8(this.deps.paths.agentDir, { recursive: true });
2793
+ writeFileSync8(
2794
+ join9(this.deps.paths.agentDir, SETUP_DECLINED_FILE),
2661
2795
  "mu asked on its first start whether to set up a model, and the answer was no.\nDelete this file to be asked again; run mu setup any time.\n"
2662
2796
  );
2663
2797
  } catch {
@@ -2698,11 +2832,10 @@ var Setup = class {
2698
2832
  ]
2699
2833
  );
2700
2834
  if (route === "subscription") {
2701
- const auth = this.path(join8(this.deps.paths.agentDir, "auth.json"));
2702
2835
  this.io.say(
2703
2836
  this.t({
2704
- zh: `\u8BA2\u9605\u8D26\u53F7\u5728 mu \u91CC\u7528 /login \u767B\u5F55\uFF1A\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\u3002setup \u63A5\u7740\u542F\u52A8 mu \u65F6\uFF0C/login \u5DF2\u7ECF\u6253\u5F00\u3002\u767B\u5F55\u4FE1\u606F\u4FDD\u5B58\u5728 ${auth}\u3002`,
2705
- en: `Sign in to your subscription from inside mu with /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. When setup starts mu, /login is already open. The sign-in is saved in ${auth}.`
2837
+ zh: "\u8BA2\u9605\u8D26\u53F7\u5728 mu \u91CC\u7528 /login \u767B\u5F55\uFF1A\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\u3002setup \u63A5\u7740\u542F\u52A8 mu \u65F6\uFF0C/login \u5DF2\u7ECF\u6253\u5F00\u3002",
2838
+ en: "Sign in to your subscription from inside mu with /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. When setup starts mu, /login is already open."
2706
2839
  })
2707
2840
  );
2708
2841
  return "subscription";
@@ -2871,7 +3004,7 @@ var Setup = class {
2871
3004
  return [{ provider, builtIn: false, baseUrl: base, api }];
2872
3005
  }
2873
3006
  inModelsFile(provider) {
2874
- const models = readJsonFile(join8(this.deps.paths.agentDir, "models.json"));
3007
+ const models = readJsonFile(join9(this.deps.paths.agentDir, "models.json"));
2875
3008
  return models.state === "ok" && existingProvider(models.value, provider) !== void 0;
2876
3009
  }
2877
3010
  /** A failure as the person can act on it: a local service that is silent is not ready yet, whatever the key. */
@@ -2991,7 +3124,7 @@ var Setup = class {
2991
3124
  }
2992
3125
  }
2993
3126
  modelsFileWritable() {
2994
- const path = join8(this.deps.paths.agentDir, "models.json");
3127
+ const path = join9(this.deps.paths.agentDir, "models.json");
2995
3128
  if (readJsonFile(path).state !== "unreadable") return;
2996
3129
  throw this.fail({
2997
3130
  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`,
@@ -3073,7 +3206,7 @@ var Setup = class {
3073
3206
  /** A provider models.json has at another address is moved only with a yes. */
3074
3207
  async conflicts(target) {
3075
3208
  if (target.reach.builtIn && !this.args.baseUrl) return;
3076
- const models = readJsonFile(join8(this.deps.paths.agentDir, "models.json"));
3209
+ const models = readJsonFile(join9(this.deps.paths.agentDir, "models.json"));
3077
3210
  if (models.state !== "ok") return;
3078
3211
  const current = existingProvider(models.value, target.reach.provider)?.baseUrl;
3079
3212
  const old = typeof current === "string" ? current.replace(/\/+$/, "") : void 0;
@@ -3191,8 +3324,8 @@ var Setup = class {
3191
3324
  });
3192
3325
  const picked = await this.choose(
3193
3326
  this.t({
3194
- 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",
3195
- 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?"
3327
+ zh: "mu \u5DE5\u4F5C\u4E2D\u7684\u5C0F\u5224\u65AD\u4EA4\u7ED9\u54EA\u4E2A\u5224\u5B9A\u5668\uFF1F",
3328
+ en: "Which judge should answer mu's small decisions?"
3196
3329
  }),
3197
3330
  choices
3198
3331
  );
@@ -3298,9 +3431,9 @@ var Setup = class {
3298
3431
  const backups = new Backups(deps.paths.home, deps.now());
3299
3432
  const saved = [];
3300
3433
  const notes = [];
3301
- const modelsPath = join8(agentDir2, "models.json");
3302
- const authPath2 = join8(agentDir2, "auth.json");
3303
- const settingsPath = join8(agentDir2, "settings.json");
3434
+ const modelsPath = join9(agentDir2, "models.json");
3435
+ const authPath2 = join9(agentDir2, "auth.json");
3436
+ const settingsPath = join9(agentDir2, "settings.json");
3304
3437
  try {
3305
3438
  if (target && model) {
3306
3439
  const { service, reach, key: key2 } = target;
@@ -3504,12 +3637,8 @@ var Setup = class {
3504
3637
  }) : this.t({ zh: "\u5224\u5B9A\u5668\uFF1AJev\u3002", en: " Judge: Jev." });
3505
3638
  this.io.say("");
3506
3639
  this.io.say(`${done}${judged}`);
3507
- this.io.say(
3508
- target ? this.t({
3509
- zh: "\u5728 mu \u91CC\u7528 /model \u6362\u6A21\u578B\uFF1B\u968F\u65F6\u53EF\u4EE5\u518D\u8FD0\u884C mu setup\u3002",
3510
- en: "Switch models inside mu with /model, and run mu setup again any time."
3511
- }) : this.t({ zh: "\u63A5\u4E0B\u6765\u5728 mu \u91CC\u7528 /login \u767B\u5F55\u3002", en: "Next, sign in with /login in mu." })
3512
- );
3640
+ if (!target)
3641
+ this.io.say(this.t({ zh: "\u63A5\u4E0B\u6765\u5728 mu \u91CC\u7528 /login \u767B\u5F55\u3002", en: "Next, sign in with /login in mu." }));
3513
3642
  }
3514
3643
  /**
3515
3644
  * On its own, a finished setup offers a session; on a first start the launcher goes on into one anyway. After a
@@ -3549,10 +3678,10 @@ async function runSetup(argv, deps) {
3549
3678
  var givenKey = process.env.MU_SETUP_KEY || void 0;
3550
3679
  delete process.env.MU_SETUP_KEY;
3551
3680
  var home = process.env.MU_SETUP_HOME || muHome();
3552
- var agentDir = muEnv("AGENT_DIR") || join9(home, "agent");
3553
- var envFile = process.env.MU_SETUP_ENV_FILE || join9(home, ".env");
3554
- var authPath = join9(agentDir, "auth.json");
3555
- var modelsStorePath = join9(agentDir, "models-store.json");
3681
+ var agentDir = muEnv("AGENT_DIR") || join10(home, "agent");
3682
+ var envFile = process.env.MU_SETUP_ENV_FILE || join10(home, ".env");
3683
+ var authPath = join10(agentDir, "auth.json");
3684
+ var modelsStorePath = join10(agentDir, "models-store.json");
3556
3685
  var givenEnv = { ...process.env };
3557
3686
  var PROXY_VARIABLES = ["HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy"];
3558
3687
  function useSessionProxy() {
@@ -3576,7 +3705,7 @@ async function main2() {
3576
3705
  const proxy = useSessionProxy();
3577
3706
  const runtime = await ModelRuntime.create({
3578
3707
  authPath,
3579
- modelsPath: join9(agentDir, "models.json"),
3708
+ modelsPath: join10(agentDir, "models.json"),
3580
3709
  modelsStorePath,
3581
3710
  refreshOnCreate: false
3582
3711
  });