mu-agent 0.2.0 → 0.2.2

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.
@@ -1,8 +1,8 @@
1
1
  import { createRequire as __muCreateRequire } from "node:module"; const require = __muCreateRequire(import.meta.url);
2
2
 
3
3
  // packages/kyrn-judge/src/setup/main.ts
4
- import { constants } from "node:os";
5
- import { join as join4 } from "node:path";
4
+ import { constants as constants2 } from "node:os";
5
+ import { join as join9 } from "node:path";
6
6
  import {
7
7
  applyHttpProxySettings,
8
8
  configureHttpDispatcher,
@@ -51,6 +51,16 @@ function drop(lock) {
51
51
  }
52
52
  }
53
53
 
54
+ // packages/kyrn-judge/src/mude/command.ts
55
+ import { execFileSync as execFileSync2 } from "node:child_process";
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";
58
+ import { homedir as homedir4, release, totalmem as totalmem2 } from "node:os";
59
+ import { join as join7 } from "node:path";
60
+
61
+ // packages/kyrn-judge/src/mude/config.ts
62
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
63
+
54
64
  // packages/kyrn-judge/src/naming.ts
55
65
  import { existsSync } from "node:fs";
56
66
  import { homedir } from "node:os";
@@ -66,42 +76,58 @@ function muHome(home2 = homedir()) {
66
76
  return existsSync(current) || !existsSync(legacy) ? current : legacy;
67
77
  }
68
78
 
69
- // packages/kyrn-judge/src/redact.ts
70
- var REDACTED = "[redacted]";
71
- var SHAPES = [
72
- // A private key, the whole block, or to the end when the text was cut inside it.
73
- /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z0-9 ]*PRIVATE KEY-----|$)/g,
74
- // OpenAI, Anthropic, OpenRouter, DeepSeek and the like.
75
- /\bsk-[A-Za-z0-9_-]{20,}/g,
76
- /\b(?:gh[pousr]_[A-Za-z0-9]{30,}|github_pat_[A-Za-z0-9_]{22,})/g,
77
- /\bglpat-[A-Za-z0-9_-]{20,}/g,
78
- /\bxox[abposr]-[A-Za-z0-9-]{10,}/g,
79
- /\bAIza[0-9A-Za-z_-]{35}/g,
80
- /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g,
81
- /\b[rs]k_(?:live|test)_[0-9A-Za-z]{16,}/g,
82
- /\bnpm_[A-Za-z0-9]{36}\b/g,
83
- /\bhf_[A-Za-z0-9]{30,}/g,
84
- // A JSON web token.
85
- /\beyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g
86
- ];
87
- var BEARER = /\b(Bearer|Basic)(\s+)(?=[A-Za-z._~+/=-]*[0-9])[A-Za-z0-9._~+/=-]{12,}/g;
88
- var URL_PASSWORD = /\b([a-z][a-z0-9+.-]*:\/\/[^\s:@/]+:)[^\s@/]+(@)/gi;
89
- var KEY_WORD = "(?:SECRET|TOKEN|PASSWD|PASSWORD|API_?KEY|ACCESS_?KEY|PRIVATE_?KEY)";
90
- var ENV_VALUE = new RegExp(`\\b([A-Z0-9_]*${KEY_WORD}[A-Z0-9_]*=)("[^"\\n]{8,}"|'[^'\\n]{8,}'|[^\\s"';&|]{8,})`, "g");
91
- var QUOTED_VALUE = new RegExp(
92
- `(["']?[A-Za-z0-9_-]*${KEY_WORD.replaceAll("_?", "[_-]?")}[A-Za-z0-9_-]*["']?\\s*[:=]\\s*)(["'])[^"'\\s]{8,}\\2`,
93
- "gi"
94
- );
95
- var LINE_VALUE = new RegExp(
96
- `^(\\s*[A-Za-z0-9_.-]*${KEY_WORD.replaceAll("_?", "[_.-]?")}[A-Za-z0-9_.-]*\\s*[:=][ \\t]*)[^\\s#"']{8,}[ \\t]*$`,
97
- "gim"
98
- );
99
- function redactSecrets(text, known = []) {
100
- let clean = text;
101
- for (const secret of known) if (clean.includes(secret)) clean = clean.split(secret).join(REDACTED);
102
- for (const shape of SHAPES) clean = clean.replace(shape, REDACTED);
103
- return clean.replace(BEARER, `$1$2${REDACTED}`).replace(URL_PASSWORD, `$1${REDACTED}$2`).replace(ENV_VALUE, `$1${REDACTED}`).replace(QUOTED_VALUE, `$1$2${REDACTED}$2`).replace(LINE_VALUE, `$1${REDACTED}`);
104
- }
79
+ // packages/kyrn-judge/src/config.ts
80
+ var BUILT_IN_JUDGES = {
81
+ jev: { type: "jev" },
82
+ "jev-direct": { type: "typesafe" },
83
+ // OpenRouter serves Jev over the same System One protocol. The key is Jev's own, not the one for OpenRouter's
84
+ // chat models, so setting one never changes the other.
85
+ "jev-openrouter": {
86
+ type: "typesafe",
87
+ baseUrl: "https://openrouter.ai/api/v1/systemone",
88
+ model: "~typesafe/jev-latest",
89
+ apiKeyEnv: "MU_JUDGE_OPENROUTER_API_KEY"
90
+ },
91
+ "jev-gateway": { type: "gateway", model: "typesafe-ai/jev" },
92
+ // Jev on OpenCode Zen, with the key of OpenCode's models (OPENCODE_API_KEY, or `mu auth`). The free one is a
93
+ // limited-time offer of OpenCode's and needs no key at all. OpenCode does not train on what Jev is sent.
94
+ "jev-opencode": { type: "classifier", model: "opencode/jev-1.13" },
95
+ "jev-opencode-free": { type: "classifier", model: "opencode/jev-1.13-free" },
96
+ // Jev on Cloudflare Workers AI, with Cloudflare's key and account (CLOUDFLARE_API_KEY, CLOUDFLARE_ACCOUNT_ID).
97
+ "jev-cloudflare": { type: "classifier", model: "cloudflare-workers-ai/typesafe/jev" },
98
+ // Cloudflare's own System One classifiers. Not measured on mu's questions yet.
99
+ clef: { type: "classifier", model: "cloudflare-workers-ai/@cf/cloudflare/clef" },
100
+ "clef-flash": { type: "classifier", model: "cloudflare-workers-ai/@cf/cloudflare/clef-flash" },
101
+ // CLM-8B on this machine, where `clm-serve` listens by default. Its profile is not measured on mu's questions yet.
102
+ clm: { type: "clm" },
103
+ // Measured in kyrn/docs/03-local-judge.md: the base checkpoint classifies one text well, says "yes" to
104
+ // nearly every relational question, and cannot use a rubric or judge the request itself. Measured on
105
+ // Terminal-Bench 2.1 (Oct 2026): on tool output it is wrong or late. All 15 passages it withheld as injections were
106
+ // clean (directory listings, href lists, headers, JSON), its one admission drop was a makefile, and most of its
107
+ // calls on both failed or came too late. So screening tool output (`screen`) goes to the next tier, or to the
108
+ // rules. It also read a finished "Done. Created /app/meeting_scheduled.ics …" as a promise of more work, twice in one
109
+ // run (constraints-scheduling): 4 extra requests and the model searching / for test files. So whether a closing
110
+ // message promised more (`intent`, turn.continue) goes to the next tier too; `continuation.localPromised` asks it.
111
+ laya: {
112
+ type: "local",
113
+ profile: { capabilities: { relate: false, rate: false, meta: false, screen: false, intent: false } }
114
+ },
115
+ // mude, the decision model trained for mu's judgment layer, on this Mac (`mu judge mude install`, Apple silicon):
116
+ // the System One server that comes in its package, on 127.0.0.1:8790, with the key the install wrote to the .env.
117
+ // Trusted on every capability: on mu's own dev scenarios (an internal benchmark, not public yet) it did as well as Jev
118
+ // or better on relate, rate, meta, screening tool output (`tool.admission`) and promises (`turn.continue`), and on
119
+ // classify but for one check: a tool's description screened for injected instructions (`tool.description`), where
120
+ // Jev did better. Nothing measured it on injections in fetched content (`tool.injection`). So with a judge behind it,
121
+ // `mu judge mude` routes those two checks to that judge (`routes` in mu.json). It answers in about 0.1 s here.
122
+ mude: {
123
+ type: "typesafe",
124
+ baseUrl: "http://127.0.0.1:8790/v1/systemone",
125
+ model: "mude",
126
+ apiKeyEnv: "MU_JUDGE_MUDE_KEY",
127
+ timeoutMs: 4e3
128
+ },
129
+ mock: { type: "mock" }
130
+ };
105
131
 
106
132
  // packages/kyrn-judge/src/setup/env-file.ts
107
133
  var DEFINITION = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/;
@@ -128,21 +154,21 @@ function readEnvEntries(text) {
128
154
  }
129
155
  let body = rest.slice(1);
130
156
  let end = closingQuote(body, quote);
131
- const startedAt = index;
157
+ const startedAt2 = index;
132
158
  while (end < 0 && index + 1 < lines.length) {
133
159
  body += `
134
160
  ${lines[++index]}`;
135
161
  end = closingQuote(body, quote);
136
162
  }
137
163
  if (end < 0) {
138
- index = startedAt;
164
+ index = startedAt2;
139
165
  continue;
140
166
  }
141
167
  const value = body.slice(0, end);
142
168
  entries.push({
143
169
  name,
144
170
  value: quote === '"' ? value.replace(/\\(["\\$`])/g, "$1") : value,
145
- first: startedAt,
171
+ first: startedAt2,
146
172
  last: index
147
173
  });
148
174
  }
@@ -360,6 +386,1259 @@ function writeEnvValue(path, name, value) {
360
386
  writeFileAtomic(path, setEnvValue(readTextFile(path) ?? "", name, value));
361
387
  }
362
388
 
389
+ // packages/kyrn-judge/src/mude/server.ts
390
+ import { execFileSync, spawn } from "node:child_process";
391
+ import { closeSync, existsSync as existsSync3, mkdirSync as mkdirSync3, openSync, readFileSync as readFileSync2, rmSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "node:fs";
392
+ import { createServer } from "node:net";
393
+ import { totalmem } from "node:os";
394
+ import { join as join3 } from "node:path";
395
+
396
+ // packages/kyrn-judge/src/mude/manifest.ts
397
+ var MUDE_REPO = "qybaihe/mude-1.0-mlx-8bit";
398
+ var MUDE_REVISION = "269b71dbc4276770b5a021d7adbad100010473c8";
399
+ var MUDE_MODEL = "mude";
400
+ var FILES = [
401
+ [".gitattributes", 1570, "34448b82c17d60fec9b65b1f093c115ddbaadc04beb1b0140b6bfed2e012a930"],
402
+ ["LICENSE", 1482, "793a9f63a9cdfd3436ca19a4a70e54d1acf9d639d84ba27129918284765f3a05"],
403
+ ["LICENSES/Apache-2.0.txt", 11357, "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4"],
404
+ ["MODEL_MANIFEST.json", 19177, "2d197005a61e693566e75946e034937ed13b192a150df583cfe2dde4f9f6ebcd"],
405
+ ["NOTICE", 1899, "d57889a1687573fb059329fde16fc6d1feeadc683fc50aa592370b6a727bf42b"],
406
+ ["README.md", 2356, "f86e44bd8573b306daafa86a3a4a261b430d99d5cd0df6bda223f187c5f56756"],
407
+ ["chat_template.jinja", 7755, "273d8e0e683b885071fb17e08d71e5f2a5ddfb5309756181681de4f5a1822d80"],
408
+ ["config.json", 2783, "4fba61002dea71507b2ea7d9910a70bb7df8f0a0639e2b2ffae2b978ddb9874b"],
409
+ ["d3_engine.py", 4413, "086238942762df45047a32cd9a8e2bedd9b63167eb40be260c776de567979f53"],
410
+ ["d3_fast.py", 22331, "68a1fb9a2c3bde9ac2763bf504decae592ca12722698fbde1b39a31262955535"],
411
+ ["d3_format.py", 7008, "3badfbc70561bb3f5b8ec16a11d2c455b96ddb32caace0a8a54ca102eae1f3fa"],
412
+ ["d3_kernels.py", 16129, "8f7534ca1e1846ebf7c4c73058002f3be4dbf0d5d426f0815150ae0cbe7677bd"],
413
+ ["d3_mac.py", 10824, "5ab4f773a7361656a765b5be351e2cde519b3094c0b4a66123d01b2ac2f40372"],
414
+ ["d3_mlx.py", 15876, "04890d713a240f7ff7d53ebb250b88ef85f9c04948f322a46006506f1e33e48d"],
415
+ ["d3_runtime.py", 98229, "a77a0b84c1732a0fd6296e0daa44cd86130f67bfea5e9c87eee8c5a1df293def"],
416
+ ["d3_server.py", 17705, "766a20a69101e36ab0535a1ec683379db422380a83091f0eccd7e82edb6a02de"],
417
+ ["d3_server_mac.py", 1216, "0e351b255fac166d71489e95b68d761d1d91d10297dbcfc16a1c42a659b7b396"],
418
+ ["decision_config.json", 5509, "19c62c4a8b0ff7bd1f82c05ef0d7af8df4b2ce7af2cfe19e3989e7d9f6c1c1cc"],
419
+ ["merges.txt", 3353259, "a9d356d7bdf1ef4949e3e748e95b8e10ad9d4e2e838eddc38a0a7b6b94d1db8d"],
420
+ ["mlx-q8.safetensors", 1756751774, "9757238f601c7b2eea997069add5856ba5ed8370786bfffae0b9ac550c33f697"],
421
+ ["mlx_weights.json", 293, "747ece4d690e493dda9637917b3d8b9c1296ccfa4a258f51c0ae0ad716a1d698"],
422
+ ["modeling_d3.py", 12079, "f8f8db23ff58f1a990ffa24977d744a50e1b4446d02a64c22070967705b6dbef"],
423
+ ["pipeline_d3.py", 2936, "4f9297c7c74e596e9b67df91a1e6823cfada48bf781336f141974ff6a2f1038e"],
424
+ ["preprocessor_config.json", 390, "27225450ac9c6529872ee1924fcb0962ff5634834f817040f444118116f4e516"],
425
+ ["readout.safetensors", 2089040, "aeb18e2f9289aab0012e2059264326fb850aa8c5d8537975a891cf9655811674"],
426
+ ["requirements-mac.txt", 289, "ed16543a58d7ffd2f19e17b38f8b9e46775548700d9d3eaca7a592309a8741b5"],
427
+ ["requirements.txt", 723, "de9defd71ae4e559a7f5bdcc935b23716f9788e8755f2ba6db1ab6862bcda12e"],
428
+ ["tokenizer.json", 12807982, "5f9e4d4901a92b997e463c1f46055088b6cca5ca61a6522d1b9f64c4bb81cb42"],
429
+ ["tokenizer_config.json", 16709, "49e2b6e395f959f077f1e992b338919c0d4a9732fc6e613995e06557f843500c"],
430
+ ["video_preprocessor_config.json", 385, "7768af27c1fafa9cc9011c1dc20067e03f8915e03b63504550e11d5066986d13"],
431
+ ["vocab.json", 6722759, "ce99b4cb2983d118806ce0a8b777a35b093e2000a503ebde25853284c9dfa003"]
432
+ ];
433
+ var MUDE_FILES = FILES.map(([path, size, sha256]) => ({ path, size, sha256 }));
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("/")}`;
437
+ }
438
+
439
+ // packages/kyrn-judge/src/mude/server.ts
440
+ var DEFAULT_PORT = 8790;
441
+ function mudeBaseUrl(port2) {
442
+ return `http://127.0.0.1:${port2}/v1/systemone`;
443
+ }
444
+ async function health(port2, timeoutMs = 2e3, fetcher = fetch) {
445
+ try {
446
+ const response = await fetcher(`http://127.0.0.1:${port2}/health`, { signal: AbortSignal.timeout(timeoutMs) });
447
+ if (!response.ok) return void 0;
448
+ const body = await response.json();
449
+ return { status: String(body.status ?? ""), model: typeof body.model === "string" ? body.model : void 0 };
450
+ } catch {
451
+ return void 0;
452
+ }
453
+ }
454
+ function portFree(port2) {
455
+ return new Promise((resolve2) => {
456
+ const probe = createServer();
457
+ probe.once("error", () => resolve2(false));
458
+ probe.listen({ port: port2, host: "127.0.0.1", exclusive: true }, () => probe.close(() => resolve2(true)));
459
+ });
460
+ }
461
+ async function choosePort(preferred = DEFAULT_PORT) {
462
+ if (await portFree(preferred)) return preferred;
463
+ return new Promise((resolve2, reject) => {
464
+ const probe = createServer();
465
+ probe.once("error", reject);
466
+ probe.listen({ port: 0, host: "127.0.0.1" }, () => {
467
+ const address = probe.address();
468
+ const port2 = typeof address === "object" && address ? address.port : 0;
469
+ probe.close(() => resolve2(port2));
470
+ });
471
+ });
472
+ }
473
+ function readPid(paths) {
474
+ try {
475
+ const pid = Number.parseInt(readFileSync2(paths.pid, "utf8").trim(), 10);
476
+ return Number.isInteger(pid) && pid > 0 ? pid : void 0;
477
+ } catch {
478
+ return void 0;
479
+ }
480
+ }
481
+ function serverAlive(paths, pid = readPid(paths)) {
482
+ if (!pid) return false;
483
+ try {
484
+ process.kill(pid, 0);
485
+ } catch {
486
+ return false;
487
+ }
488
+ try {
489
+ const command = execFileSync("/bin/ps", ["-p", String(pid), "-o", "command="], { encoding: "utf8" });
490
+ return command.includes("d3_server_mac.py") && command.includes(paths.model);
491
+ } catch {
492
+ return false;
493
+ }
494
+ }
495
+ function cacheGb(memoryBytes = totalmem()) {
496
+ return memoryBytes >= 16 * 1024 ** 3 ? "1" : "0.25";
497
+ }
498
+ function tail(file, lines = 12) {
499
+ try {
500
+ const text = readFileSync2(file, "utf8");
501
+ return text.split(/\r?\n/).filter(Boolean).slice(-lines).join("\n");
502
+ } catch {
503
+ return "";
504
+ }
505
+ }
506
+ var StartStopped = class extends Error {
507
+ /** Whether the server went too: one this call started does, one that was running before does not. */
508
+ serverStopped;
509
+ constructor(serverStopped = false) {
510
+ super("Stopped while the mude 1.0 server was starting");
511
+ this.name = "StartStopped";
512
+ this.serverStopped = serverStopped;
513
+ }
514
+ };
515
+ async function startServer(paths, options) {
516
+ const sleep2 = options.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
517
+ const timeoutMs = options.timeoutMs ?? 18e4;
518
+ const running = readPid(paths);
519
+ if (running && serverAlive(paths, running)) {
520
+ if (options.wait !== false) await untilReady(paths, running, options.port, timeoutMs, sleep2, options.signal);
521
+ return { pid: running, port: options.port, already: true };
522
+ }
523
+ if (!await portFree(options.port)) {
524
+ throw new Error(
525
+ `Port ${options.port} on 127.0.0.1 is taken by another program; mu judge mude enable picks a free one`
526
+ );
527
+ }
528
+ mkdirSync3(paths.root, { recursive: true });
529
+ rmSync(paths.log, { force: true });
530
+ const log = openSync(paths.log, "a", 384);
531
+ const child = spawn(
532
+ paths.python,
533
+ [
534
+ join3(paths.model, "d3_server_mac.py"),
535
+ "--model",
536
+ paths.model,
537
+ "--host",
538
+ "127.0.0.1",
539
+ "--port",
540
+ String(options.port),
541
+ "--name",
542
+ MUDE_MODEL,
543
+ "--verify",
544
+ "fast"
545
+ ],
546
+ {
547
+ cwd: paths.model,
548
+ detached: true,
549
+ stdio: ["ignore", log, log],
550
+ env: {
551
+ PATH: "/usr/bin:/bin:/usr/sbin:/sbin",
552
+ HOME: process.env.HOME,
553
+ LANG: process.env.LANG ?? "en_US.UTF-8",
554
+ DECISION_API_KEY: options.key,
555
+ D3_MLX_CACHE_GB: cacheGb(),
556
+ // Everything it needs is in the package: nothing is fetched from the Hub at run time.
557
+ HF_HUB_OFFLINE: "1",
558
+ TRANSFORMERS_OFFLINE: "1",
559
+ PYTHONUNBUFFERED: "1"
560
+ }
561
+ }
562
+ );
563
+ closeSync(log);
564
+ const pid = child.pid;
565
+ if (!pid) throw new Error("mude's server could not be started");
566
+ child.unref();
567
+ writeFileSync2(paths.pid, `${pid}
568
+ `, { mode: 384 });
569
+ if (options.wait !== false) {
570
+ try {
571
+ await untilReady(paths, pid, options.port, timeoutMs, sleep2, options.signal);
572
+ } catch (error) {
573
+ if (!(error instanceof StartStopped)) throw error;
574
+ await stopServer(paths);
575
+ throw new StartStopped(true);
576
+ }
577
+ }
578
+ return { pid, port: options.port, already: false };
579
+ }
580
+ async function untilReady(paths, pid, port2, timeoutMs, sleep2, signal) {
581
+ const deadline = Date.now() + timeoutMs;
582
+ while (Date.now() < deadline) {
583
+ if (signal?.aborted) throw new StartStopped();
584
+ if ((await health(port2))?.status === "ready") return;
585
+ if (!serverAlive(paths, pid)) {
586
+ rmSync(paths.pid, { force: true });
587
+ throw new Error(`mude's server stopped while starting. Its log (${paths.log}) ends with:
588
+ ${tail(paths.log)}`);
589
+ }
590
+ await sleep2(500);
591
+ }
592
+ throw new Error(`mude's server did not answer within ${Math.round(timeoutMs / 1e3)} s; see ${paths.log}`);
593
+ }
594
+ async function stopServer(paths, sleep2 = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms))) {
595
+ const pid = readPid(paths);
596
+ if (!pid || !serverAlive(paths, pid)) {
597
+ rmSync(paths.pid, { force: true });
598
+ return false;
599
+ }
600
+ process.kill(pid, "SIGTERM");
601
+ for (let waited = 0; waited < 5e3 && serverAlive(paths, pid); waited += 200) await sleep2(200);
602
+ if (serverAlive(paths, pid)) process.kill(pid, "SIGKILL");
603
+ rmSync(paths.pid, { force: true });
604
+ return true;
605
+ }
606
+ function serverMemory(pid) {
607
+ if (!pid) return void 0;
608
+ try {
609
+ const kb = Number.parseInt(
610
+ execFileSync("/bin/ps", ["-p", String(pid), "-o", "rss="], { encoding: "utf8" }).trim(),
611
+ 10
612
+ );
613
+ return Number.isFinite(kb) ? kb * 1024 : void 0;
614
+ } catch {
615
+ return void 0;
616
+ }
617
+ }
618
+
619
+ // packages/kyrn-judge/src/mude/config.ts
620
+ var ROUTED_AWAY = ["tool.description", "tool.injection"];
621
+ var MUDE = "mude";
622
+ var KEY_VARIABLE = "MU_JUDGE_MUDE_KEY";
623
+ function readState(paths) {
624
+ try {
625
+ const value = JSON.parse(readFileSync3(paths.state, "utf8"));
626
+ return Number.isInteger(value.port) ? value : void 0;
627
+ } catch {
628
+ return void 0;
629
+ }
630
+ }
631
+ function writeState(paths, state) {
632
+ writeFileSync3(paths.state, `${JSON.stringify(state, null, 2)}
633
+ `, { mode: 384 });
634
+ }
635
+ function judgeSetting(files) {
636
+ const file = readJsonFile(judgeConfigPath(files.agentDir));
637
+ if (file.state === "unreadable") return { enabled: false, localOnly: false, fallback: [], unreadable: true };
638
+ const config = file.state === "ok" ? file.value : {};
639
+ const tiers = tiersOf(config);
640
+ const enabled = tiers[0] === MUDE && !judgeOff(config);
641
+ const fallback = tiers.filter((tier) => tier !== MUDE);
642
+ return { enabled, localOnly: enabled && fallback.length === 0, fallback };
643
+ }
644
+ function mudeInTiers(files) {
645
+ const file = readJsonFile(judgeConfigPath(files.agentDir));
646
+ return file.state === "ok" && tiersOf(file.value).includes(MUDE);
647
+ }
648
+ function readConfig(files) {
649
+ const path = judgeConfigPath(files.agentDir);
650
+ const file = readJsonFile(path);
651
+ if (file.state === "unreadable") {
652
+ throw new Error(
653
+ `${path} is not plain JSON (comments, or a typo), so mu leaves it alone. Fix it, then run this again.`
654
+ );
655
+ }
656
+ return { path, config: file.state === "ok" ? file.value : {} };
657
+ }
658
+ function recordOf(value) {
659
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? { ...value } : {};
660
+ }
661
+ function judgesOf(config) {
662
+ return recordOf(config.judges);
663
+ }
664
+ function withSection(config, key2, value) {
665
+ if (Object.keys(value).length > 0) return { ...config, [key2]: value };
666
+ const { [key2]: _gone, ...rest } = config;
667
+ return rest;
668
+ }
669
+ var sameTiers = (a, b) => Array.isArray(a) && a.length === b.length && a.every((tier, index) => tier === b[index]);
670
+ function withoutOwnRoutes(config, state) {
671
+ const routes = recordOf(config.routes);
672
+ for (const [spec, tiers] of Object.entries(state?.wroteRoutes ?? {})) {
673
+ if (sameTiers(routes[spec], tiers)) delete routes[spec];
674
+ }
675
+ return withSection(config, "routes", routes);
676
+ }
677
+ function entryFor(port2) {
678
+ return { ...BUILT_IN_JUDGES[MUDE], baseUrl: mudeBaseUrl(port2) };
679
+ }
680
+ function enableMude(paths, files, options) {
681
+ const { path, config } = readConfig(files);
682
+ const state = readState(paths);
683
+ const current = tiersOf(config).filter((tier) => tier !== MUDE);
684
+ const before = current.length > 0 ? current : [...state?.previousTiers ?? ["jev"]];
685
+ const tiers = options.localOnly ? [MUDE] : [MUDE, ...before.filter((tier) => tier !== MUDE)];
686
+ const judgeWasOff = state?.judgeWasOff ?? judgeOff(config);
687
+ let next = withJudge(withTiers(withoutOwnRoutes(config, state), tiers), true);
688
+ 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];
692
+ next = withSection(next, "judges", judges);
693
+ const fallback = tiers.slice(1);
694
+ const routes = recordOf(next.routes);
695
+ const wroteRoutes = {};
696
+ if (fallback.length > 0) {
697
+ for (const spec of ROUTED_AWAY) {
698
+ if (routes[spec] !== void 0) continue;
699
+ routes[spec] = [...fallback];
700
+ wroteRoutes[spec] = fallback;
701
+ }
702
+ }
703
+ next = withSection(next, "routes", routes);
704
+ const backups = new Backups(files.home, files.now ?? /* @__PURE__ */ new Date());
705
+ backups.save(path);
706
+ writeJsonFile(path, next);
707
+ if (envFileValue(readText(files.envFile), KEY_VARIABLE) !== options.key) {
708
+ backups.save(files.envFile);
709
+ writeEnvValue(files.envFile, KEY_VARIABLE, options.key);
710
+ }
711
+ writeState(paths, {
712
+ port: options.port,
713
+ previousTiers: before,
714
+ judgeWasOff,
715
+ wroteEntry: entry || void 0,
716
+ wroteRoutes: Object.keys(wroteRoutes).length > 0 ? wroteRoutes : void 0
717
+ });
718
+ return tiers;
719
+ }
720
+ function disableMude(paths, files, options = {}) {
721
+ const { path, config } = readConfig(files);
722
+ const state = readState(paths);
723
+ const rest = tiersOf(config).filter((tier) => tier !== MUDE);
724
+ const tiers = rest.length > 0 ? rest : [...state?.previousTiers ?? ["jev"]];
725
+ let next = withTiers(withoutOwnRoutes(config, state), tiers);
726
+ if (state?.judgeWasOff) next = withJudge(next, false);
727
+ if (options.removeEntry && state?.wroteEntry) {
728
+ const judges = judgesOf(next);
729
+ delete judges[MUDE];
730
+ next = withSection(next, "judges", judges);
731
+ }
732
+ new Backups(files.home, files.now ?? /* @__PURE__ */ new Date()).save(path);
733
+ writeJsonFile(path, next);
734
+ if (state) {
735
+ writeState(paths, {
736
+ ...state,
737
+ judgeWasOff: void 0,
738
+ wroteRoutes: void 0,
739
+ wroteEntry: options.removeEntry ? void 0 : state.wroteEntry
740
+ });
741
+ }
742
+ return tiers;
743
+ }
744
+ function readText(path) {
745
+ try {
746
+ return readFileSync3(path, "utf8");
747
+ } catch {
748
+ return "";
749
+ }
750
+ }
751
+
752
+ // packages/kyrn-judge/src/mude/download.ts
753
+ import { createHash } from "node:crypto";
754
+ import {
755
+ createReadStream,
756
+ existsSync as existsSync4,
757
+ mkdirSync as mkdirSync4,
758
+ readFileSync as readFileSync4,
759
+ renameSync as renameSync2,
760
+ rmSync as rmSync2,
761
+ statSync as statSync3,
762
+ truncateSync,
763
+ writeFileSync as writeFileSync4
764
+ } from "node:fs";
765
+ import { open } from "node:fs/promises";
766
+ import { homedir as homedir2 } from "node:os";
767
+ import { dirname as dirname2, join as join4 } from "node:path";
768
+ import { pipeline } from "node:stream/promises";
769
+ import { setTimeout as wait } from "node:timers/promises";
770
+ var CHECKPOINT_BYTES = 32 * 1024 * 1024;
771
+ var RETRY_WAITS = [2e3, 5e3, 1e4, 2e4, 3e4];
772
+ function huggingFaceToken(env = process.env, home2 = homedir2()) {
773
+ const direct = env.HF_TOKEN?.trim();
774
+ if (direct) return direct;
775
+ const files = [
776
+ env.HF_TOKEN_PATH,
777
+ env.HF_HOME ? join4(env.HF_HOME, "token") : void 0,
778
+ join4(env.XDG_CACHE_HOME || join4(home2, ".cache"), "huggingface", "token")
779
+ ];
780
+ for (const file of files) {
781
+ if (!file) continue;
782
+ try {
783
+ const token = readFileSync4(file, "utf8").trim();
784
+ if (token) return token;
785
+ } catch {
786
+ }
787
+ }
788
+ return void 0;
789
+ }
790
+ async function sha256File(path) {
791
+ const hash = createHash("sha256");
792
+ await pipeline(createReadStream(path), hash);
793
+ return hash.digest("hex");
794
+ }
795
+ function readVerified(dir) {
796
+ try {
797
+ const value = JSON.parse(readFileSync4(join4(dir, ".mu-verified.json"), "utf8"));
798
+ return typeof value === "object" && value !== null ? value : {};
799
+ } catch {
800
+ return {};
801
+ }
802
+ }
803
+ function knownGood(dir, file, verified) {
804
+ const seen = verified[file.path];
805
+ if (!seen || seen.sha256 !== file.sha256) return false;
806
+ try {
807
+ const stats = statSync3(join4(dir, file.path));
808
+ return stats.size === file.size && stats.mtimeMs === seen.mtimeMs;
809
+ } catch {
810
+ return false;
811
+ }
812
+ }
813
+ function remember(dir, verified, file) {
814
+ const stats = statSync3(join4(dir, file.path));
815
+ verified[file.path] = { size: stats.size, mtimeMs: stats.mtimeMs, sha256: file.sha256 };
816
+ writeFileSync4(join4(dir, ".mu-verified.json"), `${JSON.stringify(verified, null, 1)}
817
+ `);
818
+ }
819
+ function missingFiles(dir, files) {
820
+ const verified = readVerified(dir);
821
+ return files.filter((file) => !knownGood(dir, file, verified));
822
+ }
823
+ function downloadedBytes(dir, files) {
824
+ const verified = readVerified(dir);
825
+ let bytes = 0;
826
+ for (const file of files) {
827
+ if (knownGood(dir, file, verified)) {
828
+ bytes += file.size;
829
+ continue;
830
+ }
831
+ const target = join4(dir, file.path);
832
+ bytes += Math.min(file.size, Math.max(sizeOf(target), sizeOf(`${target}.part`)));
833
+ }
834
+ return bytes;
835
+ }
836
+ function sizeOf(path) {
837
+ try {
838
+ return statSync3(path).size;
839
+ } catch {
840
+ return 0;
841
+ }
842
+ }
843
+ var DownloadError = class extends Error {
844
+ kind;
845
+ constructor(message, kind) {
846
+ super(message);
847
+ this.name = "DownloadError";
848
+ this.kind = kind;
849
+ }
850
+ };
851
+ async function downloadFiles(options) {
852
+ const { dir, files } = options;
853
+ const total = files.reduce((sum, file) => sum + file.size, 0);
854
+ mkdirSync4(dir, { recursive: true });
855
+ const verified = readVerified(dir);
856
+ let settled = 0;
857
+ for (const file of files) {
858
+ if (knownGood(dir, file, verified)) {
859
+ settled += file.size;
860
+ continue;
861
+ }
862
+ const target = join4(dir, file.path);
863
+ mkdirSync4(dirname2(target), { recursive: true });
864
+ if (existsSync4(target) && statSync3(target).size === file.size) {
865
+ options.onProgress?.({ file: file.path, checking: true, done: settled, total });
866
+ if (await sha256File(target) === file.sha256) {
867
+ remember(dir, verified, file);
868
+ settled += file.size;
869
+ continue;
870
+ }
871
+ rmSync2(target, { force: true });
872
+ }
873
+ const part = `${target}.part`;
874
+ const onBytes = (bytes) => options.onProgress?.({ file: file.path, checking: false, done: settled + bytes, total });
875
+ let resumed = await fetchFile(options, file, part, onBytes);
876
+ options.onProgress?.({ file: file.path, checking: true, done: settled + file.size, total });
877
+ let actual = await sha256File(part);
878
+ if (actual !== file.sha256 && resumed) {
879
+ dropPart(part);
880
+ resumed = await fetchFile(options, file, part, onBytes);
881
+ options.onProgress?.({ file: file.path, checking: true, done: settled + file.size, total });
882
+ actual = await sha256File(part);
883
+ }
884
+ if (actual !== file.sha256) {
885
+ dropPart(part);
886
+ 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`,
888
+ "integrity"
889
+ );
890
+ }
891
+ renameSync2(part, target);
892
+ rmSync2(savedPath(part), { force: true });
893
+ remember(dir, verified, file);
894
+ settled += file.size;
895
+ }
896
+ options.onProgress?.({ file: "", checking: false, done: total, total });
897
+ }
898
+ var savedPath = (part) => `${part}.saved`;
899
+ function dropPart(part) {
900
+ rmSync2(part, { force: true });
901
+ rmSync2(savedPath(part), { force: true });
902
+ }
903
+ function resumeFrom(part, size, checkpointBytes = CHECKPOINT_BYTES) {
904
+ const have = Math.min(sizeOf(part), size);
905
+ let saved;
906
+ try {
907
+ const value = Number.parseInt(readFileSync4(savedPath(part), "utf8").trim(), 10);
908
+ if (Number.isSafeInteger(value) && value >= 0) saved = value;
909
+ } catch {
910
+ }
911
+ return saved === void 0 ? Math.max(0, have - checkpointBytes) : Math.min(saved, have);
912
+ }
913
+ function writeSaved(part, bytes) {
914
+ const path = savedPath(part);
915
+ writeFileSync4(`${path}.tmp`, `${bytes}
916
+ `);
917
+ renameSync2(`${path}.tmp`, path);
918
+ }
919
+ async function fetchFile(options, file, part, onBytes) {
920
+ const attempts = options.attempts ?? 8;
921
+ const sleep2 = options.sleep ?? ((ms) => wait(ms, void 0, { signal: options.signal }).catch(() => {
922
+ }));
923
+ let resumed = false;
924
+ let failures = 0;
925
+ let lastError;
926
+ for (; ; ) {
927
+ if (options.signal?.aborted) throw new DownloadError("The download was stopped", "aborted");
928
+ const before = resumeFrom(part, file.size, options.checkpointBytes);
929
+ try {
930
+ resumed = await fetchOnce(options, file, part, onBytes) || resumed;
931
+ return resumed;
932
+ } catch (error) {
933
+ if (error instanceof DownloadError && error.kind !== "network") throw error;
934
+ const code = error.code;
935
+ if (code === "ENOSPC" || code === "EDQUOT") {
936
+ throw new Error(
937
+ `The disk is full: couldn't write ${file.path}. Free up some space, then run mu judge mude install to resume`
938
+ );
939
+ }
940
+ lastError = error;
941
+ failures = resumeFrom(part, file.size, options.checkpointBytes) > before ? 1 : failures + 1;
942
+ if (failures >= attempts) break;
943
+ await sleep2(RETRY_WAITS[Math.min(failures, RETRY_WAITS.length) - 1]);
944
+ }
945
+ }
946
+ const reason = lastError instanceof Error ? lastError.message : String(lastError);
947
+ throw new DownloadError(
948
+ `${file.path} could not be downloaded (${reason}). Run mu judge mude install again to resume`,
949
+ "network"
950
+ );
951
+ }
952
+ async function fetchOnce(options, file, part, onBytes) {
953
+ const checkpointBytes = options.checkpointBytes ?? CHECKPOINT_BYTES;
954
+ let have = resumeFrom(part, file.size, checkpointBytes);
955
+ if (have === file.size) {
956
+ if (sizeOf(part) > file.size) truncateSync(part, file.size);
957
+ return true;
958
+ }
959
+ const controller = new AbortController();
960
+ const stallMs = options.stallMs ?? 6e4;
961
+ let stall = setTimeout(() => controller.abort(new Error(`no data for ${stallMs / 1e3} s`)), stallMs);
962
+ const onAbort = () => controller.abort(options.signal?.reason);
963
+ options.signal?.addEventListener("abort", onAbort, { once: true });
964
+ let handle;
965
+ let written = 0;
966
+ let synced = 0;
967
+ try {
968
+ const headers = {};
969
+ if (have > 0) headers.Range = `bytes=${have}-`;
970
+ if (options.token) headers.Authorization = `Bearer ${options.token}`;
971
+ const response = await (options.fetch ?? fetch)(options.url(file.path), {
972
+ headers,
973
+ redirect: "follow",
974
+ signal: controller.signal
975
+ });
976
+ if (response.status === 401 || response.status === 403 || response.status === 404) {
977
+ throw new DownloadError(
978
+ `Hugging Face returned ${response.status} for ${file.path}: the model isn't public yet, or this machine's token can't access it`,
979
+ "access"
980
+ );
981
+ }
982
+ if (response.status === 416) {
983
+ dropPart(part);
984
+ throw new Error("HTTP 416");
985
+ }
986
+ if (!response.ok || !response.body) throw new Error(`HTTP ${response.status}`);
987
+ const resumed = response.status === 206 && have > 0;
988
+ if (!resumed) have = 0;
989
+ mkdirSync4(dirname2(part), { recursive: true });
990
+ handle = await open(part, existsSync4(part) ? "r+" : "w");
991
+ await handle.truncate(have);
992
+ writeSaved(part, have);
993
+ written = have;
994
+ synced = have;
995
+ onBytes(written);
996
+ const reader = response.body.getReader();
997
+ for (; ; ) {
998
+ const { done, value: chunk } = await reader.read();
999
+ if (done) break;
1000
+ clearTimeout(stall);
1001
+ stall = setTimeout(() => controller.abort(new Error(`no data for ${stallMs / 1e3} s`)), stallMs);
1002
+ if (written + chunk.length > file.size) throw new Error(`got more than the ${file.size} bytes of the file`);
1003
+ await handle.write(chunk, 0, chunk.length, written);
1004
+ written += chunk.length;
1005
+ if (written - synced >= checkpointBytes) {
1006
+ await handle.sync();
1007
+ synced = written;
1008
+ writeSaved(part, synced);
1009
+ }
1010
+ onBytes(written);
1011
+ }
1012
+ if (written !== file.size) throw new Error(`got ${written} of ${file.size} bytes`);
1013
+ return resumed;
1014
+ } catch (error) {
1015
+ if (options.signal?.aborted) throw new DownloadError("The download was stopped", "aborted");
1016
+ throw error;
1017
+ } finally {
1018
+ clearTimeout(stall);
1019
+ options.signal?.removeEventListener("abort", onAbort);
1020
+ if (handle) {
1021
+ try {
1022
+ await handle.sync();
1023
+ if (written > synced) writeSaved(part, written);
1024
+ } catch {
1025
+ }
1026
+ await handle.close();
1027
+ }
1028
+ }
1029
+ }
1030
+
1031
+ // packages/kyrn-judge/src/mude/paths.ts
1032
+ import { join as join5 } from "node:path";
1033
+ function mudePaths(muHome2) {
1034
+ const root = join5(muHome2, "local-judge", "mude");
1035
+ const model = join5(root, "model", MUDE_REVISION);
1036
+ const venv = join5(root, "venv");
1037
+ return {
1038
+ root,
1039
+ model,
1040
+ verified: join5(model, ".mu-verified.json"),
1041
+ venv,
1042
+ python: join5(venv, "bin", "python"),
1043
+ venvLock: join5(venv, ".mu-lock-sha256"),
1044
+ tools: join5(root, "tools"),
1045
+ key: join5(root, "key"),
1046
+ pid: join5(root, "server.pid"),
1047
+ log: join5(root, "server.log"),
1048
+ state: join5(root, "state.json"),
1049
+ install: join5(root, "install.json"),
1050
+ installLock: join5(root, "install.lock")
1051
+ };
1052
+ }
1053
+
1054
+ // packages/kyrn-judge/src/mude/python.ts
1055
+ import { spawn as spawn2 } from "node:child_process";
1056
+ 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";
1058
+ import { homedir as homedir3 } from "node:os";
1059
+ import { delimiter, join as join6 } from "node:path";
1060
+ var PYTHON_VERSION = "3.13";
1061
+ var UV_RELEASE = {
1062
+ version: "0.13.1",
1063
+ url: "https://github.com/astral-sh/uv/releases/download/0.13.1/uv-aarch64-apple-darwin.tar.gz",
1064
+ sha256: "cfe454573412ff7380d15fc7dae5997ab53ffce72fce0a78e91ddbc84001acd2"
1065
+ };
1066
+ function executable(path) {
1067
+ try {
1068
+ accessSync(path, constants.X_OK);
1069
+ return true;
1070
+ } catch {
1071
+ return false;
1072
+ }
1073
+ }
1074
+ function findUv(paths, env = process.env, home2 = homedir3()) {
1075
+ const dirs = [
1076
+ ...(env.PATH ?? "").split(delimiter),
1077
+ join6(home2, ".local", "bin"),
1078
+ join6(home2, ".cargo", "bin"),
1079
+ "/opt/homebrew/bin",
1080
+ "/usr/local/bin",
1081
+ paths.tools
1082
+ ];
1083
+ for (const dir of dirs) {
1084
+ if (dir && executable(join6(dir, "uv"))) return join6(dir, "uv");
1085
+ }
1086
+ return void 0;
1087
+ }
1088
+ function run(command, args, options = {}) {
1089
+ return new Promise((resolve2) => {
1090
+ const child = spawn2(command, args, { env: options.env ?? process.env, stdio: ["ignore", "pipe", "pipe"] });
1091
+ const lines = [];
1092
+ let pending = "";
1093
+ const take = (chunk) => {
1094
+ pending += chunk.toString("utf8");
1095
+ const parts = pending.split(/\r?\n/);
1096
+ pending = parts.pop() ?? "";
1097
+ for (const line of parts) {
1098
+ if (!line.trim()) continue;
1099
+ lines.push(line);
1100
+ if (lines.length > 40) lines.shift();
1101
+ options.onLine?.(line);
1102
+ }
1103
+ };
1104
+ child.stdout.on("data", take);
1105
+ child.stderr.on("data", take);
1106
+ child.once("error", (error) => resolve2({ code: null, output: error.message }));
1107
+ child.once("close", (code) => {
1108
+ if (pending.trim()) lines.push(pending);
1109
+ resolve2({ code, output: lines.slice(-12).join("\n") });
1110
+ });
1111
+ });
1112
+ }
1113
+ async function downloadUv(paths, options = {}) {
1114
+ mkdirSync5(paths.tools, { recursive: true });
1115
+ const archive = "uv.tar.gz";
1116
+ options.onLine?.(`Downloading uv ${UV_RELEASE.version}`);
1117
+ const timeoutMs = options.timeoutMs ?? 3e5;
1118
+ const timeout = AbortSignal.timeout(timeoutMs);
1119
+ let bytes;
1120
+ try {
1121
+ const response = await (options.fetch ?? fetch)(UV_RELEASE.url, {
1122
+ redirect: "follow",
1123
+ signal: options.signal ? AbortSignal.any([options.signal, timeout]) : timeout
1124
+ });
1125
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
1126
+ bytes = Buffer.from(await response.arrayBuffer());
1127
+ } catch (error) {
1128
+ const reason = timeout.aborted ? `no answer in ${timeoutMs / 1e3} s` : error instanceof Error ? error.message : String(error);
1129
+ throw new Error(`uv could not be downloaded from GitHub (${reason}): run mu judge mude install again`);
1130
+ }
1131
+ const actual = createHash2("sha256").update(bytes).digest("hex");
1132
+ if (actual !== UV_RELEASE.sha256)
1133
+ throw new Error("The downloaded uv does not match its pinned sha256; nothing was installed");
1134
+ writeFileSync5(join6(paths.tools, archive), bytes);
1135
+ const unpacked = await run("/usr/bin/tar", [
1136
+ "-xzf",
1137
+ join6(paths.tools, archive),
1138
+ "-C",
1139
+ paths.tools,
1140
+ "--strip-components",
1141
+ "1"
1142
+ ]);
1143
+ rmSync3(join6(paths.tools, archive), { force: true });
1144
+ if (unpacked.code !== 0 || !executable(join6(paths.tools, "uv")))
1145
+ throw new Error(`Couldn't unpack uv: ${unpacked.output}`);
1146
+ return join6(paths.tools, "uv");
1147
+ }
1148
+ function lockDigest(lockFile) {
1149
+ return createHash2("sha256").update(readFileSync5(lockFile)).digest("hex");
1150
+ }
1151
+ function venvReady(paths, lockFile) {
1152
+ if (!existsSync5(paths.python)) return false;
1153
+ try {
1154
+ return readFileSync5(paths.venvLock, "utf8").trim() === lockDigest(lockFile);
1155
+ } catch {
1156
+ return false;
1157
+ }
1158
+ }
1159
+ async function ensureVenv(paths, lockFile, uv, options = {}) {
1160
+ if (venvReady(paths, lockFile)) return;
1161
+ rmSync3(paths.venv, { recursive: true, force: true });
1162
+ const env = { ...options.env ?? process.env, UV_NO_CONFIG: "1", VIRTUAL_ENV: void 0 };
1163
+ const made = await run(uv, ["venv", "--python", PYTHON_VERSION, "--no-project", "--quiet", paths.venv], {
1164
+ env,
1165
+ onLine: options.onLine
1166
+ });
1167
+ if (made.code !== 0) throw new Error(`Couldn't create the Python environment (uv venv):
1168
+ ${made.output}`);
1169
+ const installed2 = await run(
1170
+ uv,
1171
+ [
1172
+ "pip",
1173
+ "install",
1174
+ "--python",
1175
+ paths.python,
1176
+ "--require-hashes",
1177
+ "--no-deps",
1178
+ "--index-url",
1179
+ "https://pypi.org/simple",
1180
+ "-r",
1181
+ lockFile
1182
+ ],
1183
+ { env, onLine: options.onLine }
1184
+ );
1185
+ if (installed2.code !== 0)
1186
+ throw new Error(`Couldn't install mude's Python packages (uv pip install):
1187
+ ${installed2.output}`);
1188
+ writeFileSync5(paths.venvLock, `${lockDigest(lockFile)}
1189
+ `);
1190
+ }
1191
+
1192
+ // packages/kyrn-judge/src/mude/command.ts
1193
+ var USAGE = `mu judge mude: mude 1.0, mu's own decision model optimized for coding, as the judge on this Mac
1194
+ (Apple silicon, macOS 14 or later)
1195
+
1196
+ mu judge mude install [--local-only] download it (${(MUDE_BYTES / 1e9).toFixed(1)} GB), set it up, start it and make it the judge;
1197
+ after a stop (Ctrl-C, a lost connection, a power cut), it picks up where it left off
1198
+ mu judge mude enable [--local-only | --with-fallback]
1199
+ make it the judge again, or change what happens when it can't answer
1200
+ mu judge mude disable back to the judges from before, and stop its server
1201
+ mu judge mude start | stop start or stop its server (about 4 GB of memory while it runs)
1202
+ mu judge mude status what is installed, running and enabled
1203
+ mu judge mude uninstall disable it and delete the model and its Python environment
1204
+
1205
+ What mude 1.0 cannot answer, because its server is not running or it is unsure, goes to the judges that were there
1206
+ before it (Jev by default): those are hosted, so what they read leaves this machine. With --local-only, it is the
1207
+ only judge, and such a decision takes its fallback instead. Add --json for one JSON object per line.
1208
+ `;
1209
+ function jsonOut(write) {
1210
+ const emit = (value) => write(`${JSON.stringify(value)}
1211
+ `);
1212
+ let last = 0;
1213
+ return {
1214
+ step: (step, message) => emit({ type: "step", step, message }),
1215
+ progress: (event) => {
1216
+ const now = Date.now();
1217
+ if (now - last < 250 && event.done < event.total && !event.checking) return;
1218
+ last = now;
1219
+ emit({ type: "progress", ...event });
1220
+ },
1221
+ line: (text) => emit({ type: "log", line: text }),
1222
+ status: (status2) => emit({ type: "status", ...status2 }),
1223
+ error: (message, kind) => emit({ type: "error", message, kind })
1224
+ };
1225
+ }
1226
+ function humanOut(write, tty) {
1227
+ let shown = -1;
1228
+ return {
1229
+ step: (_step, message) => {
1230
+ if (shown >= 0 && tty) write("\n");
1231
+ shown = -1;
1232
+ write(`${message}
1233
+ `);
1234
+ },
1235
+ progress: ({ done, total, checking }) => {
1236
+ const percent = total > 0 ? Math.floor(done / total * 100) : 100;
1237
+ if (tty) {
1238
+ write(
1239
+ `\r ${percent}% ${(done / 1e9).toFixed(2)} of ${(total / 1e9).toFixed(2)} GB${checking ? " (checking)" : ""} `
1240
+ );
1241
+ shown = percent;
1242
+ } else if (percent >= shown + 10 || percent === 100) {
1243
+ shown = percent - percent % 10;
1244
+ write(` ${percent}%
1245
+ `);
1246
+ }
1247
+ },
1248
+ line: (text) => write(` ${text}
1249
+ `),
1250
+ status: (status2) => write(describe(status2)),
1251
+ error: (message) => {
1252
+ if (shown >= 0 && tty) write("\n");
1253
+ shown = -1;
1254
+ write(`${message}
1255
+ `);
1256
+ }
1257
+ };
1258
+ }
1259
+ function describe(status2) {
1260
+ const lines = [];
1261
+ if (!status2.supported) {
1262
+ lines.push(`mude 1.0 can't run here: ${status2.problems.join("; ")}`);
1263
+ return `${lines.join("\n")}
1264
+ `;
1265
+ }
1266
+ 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"}`
1268
+ );
1269
+ if (status2.installed && !status2.pythonReady) lines.push("python not set up: mu judge mude install");
1270
+ lines.push(
1271
+ `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
+ );
1273
+ if (status2.enabled) {
1274
+ 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)`
1276
+ );
1277
+ } else {
1278
+ lines.push(`judge ${status2.installed ? "not enabled: mu judge mude enable" : "not enabled"}`);
1279
+ }
1280
+ lines.push(`folder ${status2.folder}`);
1281
+ return `${lines.join("\n")}
1282
+ `;
1283
+ }
1284
+ function problems(options = {}) {
1285
+ const platform = options.platform ?? process.platform;
1286
+ if (platform !== "darwin") return ["it needs a Mac with Apple silicon (an M1 or newer)"];
1287
+ const out = [];
1288
+ const arm64 = options.arm64 ?? appleSilicon();
1289
+ if (!arm64) out.push("it needs Apple silicon (an M1 or newer); this Mac has an Intel processor");
1290
+ const darwinMajor = options.darwinMajor ?? Number.parseInt(release().split(".")[0] ?? "0", 10);
1291
+ if (darwinMajor < 23) out.push("it needs macOS 14 or later");
1292
+ const memory = options.memoryBytes ?? totalmem2();
1293
+ if (memory < 8 * 1024 ** 3)
1294
+ out.push(
1295
+ `it needs 8 GB of memory or more (it uses about 4 GB); this Mac has ${Math.round(memory / 1024 ** 3)} GB`
1296
+ );
1297
+ return out;
1298
+ }
1299
+ function appleSilicon() {
1300
+ if (process.arch === "arm64") return true;
1301
+ try {
1302
+ return execFileSync2("/usr/sbin/sysctl", ["-n", "hw.optional.arm64"], { encoding: "utf8" }).trim() === "1";
1303
+ } catch {
1304
+ return false;
1305
+ }
1306
+ }
1307
+ function freeBytes(path) {
1308
+ let dir = path;
1309
+ while (!existsSync6(dir)) {
1310
+ const parent = join7(dir, "..");
1311
+ if (parent === dir) return void 0;
1312
+ dir = parent;
1313
+ }
1314
+ try {
1315
+ const stats = statfsSync(dir);
1316
+ return stats.bavail * stats.bsize;
1317
+ } catch {
1318
+ return void 0;
1319
+ }
1320
+ }
1321
+ var gb = (bytes) => (bytes / 1e9).toFixed(1);
1322
+ function installLocalOnly(ctx, args) {
1323
+ if (args.includes("--local-only")) return true;
1324
+ if (args.includes("--with-fallback")) return false;
1325
+ try {
1326
+ const value = JSON.parse(readFileSync6(ctx.paths.install, "utf8"));
1327
+ return typeof value === "object" && value !== null && "localOnly" in value && value.localOnly === true;
1328
+ } catch {
1329
+ return false;
1330
+ }
1331
+ }
1332
+ function startedAt(pid) {
1333
+ try {
1334
+ return execFileSync2("/bin/ps", ["-p", String(pid), "-o", "lstart="], { encoding: "utf8" }).trim() || void 0;
1335
+ } catch {
1336
+ return void 0;
1337
+ }
1338
+ }
1339
+ function holdInstall(paths) {
1340
+ mkdirSync6(paths.root, { recursive: true });
1341
+ const mine = `${JSON.stringify({ pid: process.pid, started: startedAt(process.pid) ?? "" })}
1342
+ `;
1343
+ try {
1344
+ writeFileSync6(paths.installLock, mine, { flag: "wx" });
1345
+ } catch (error) {
1346
+ if (error.code !== "EEXIST") throw error;
1347
+ let holder = {};
1348
+ try {
1349
+ holder = JSON.parse(readFileSync6(paths.installLock, "utf8"));
1350
+ } catch {
1351
+ }
1352
+ const pid = typeof holder.pid === "number" ? holder.pid : 0;
1353
+ if (pid > 0 && pid !== process.pid && holder.started && startedAt(pid) === holder.started) {
1354
+ 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`
1356
+ );
1357
+ }
1358
+ writeFileSync6(paths.installLock, mine);
1359
+ }
1360
+ return () => {
1361
+ try {
1362
+ if (readFileSync6(paths.installLock, "utf8") === mine) rmSync4(paths.installLock, { force: true });
1363
+ } catch {
1364
+ }
1365
+ };
1366
+ }
1367
+ function installed(ctx) {
1368
+ return missingFiles(ctx.paths.model, MUDE_FILES).length === 0;
1369
+ }
1370
+ function key(ctx) {
1371
+ try {
1372
+ const existing = readFileSync6(ctx.paths.key, "utf8").trim();
1373
+ if (existing) return existing;
1374
+ } catch {
1375
+ }
1376
+ mkdirSync6(ctx.paths.root, { recursive: true });
1377
+ const made = randomBytes(32).toString("hex");
1378
+ writeFileSync6(ctx.paths.key, `${made}
1379
+ `, { mode: 384 });
1380
+ return made;
1381
+ }
1382
+ async function port(ctx) {
1383
+ const previous = readState(ctx.paths)?.port;
1384
+ if (previous && (serverAlive(ctx.paths) || await portFree(previous))) return previous;
1385
+ return choosePort(DEFAULT_PORT);
1386
+ }
1387
+ async function status(ctx) {
1388
+ const found = problems();
1389
+ const state = readState(ctx.paths);
1390
+ const at = state?.port ?? DEFAULT_PORT;
1391
+ const pid = readPid(ctx.paths);
1392
+ const running = serverAlive(ctx.paths, pid);
1393
+ const ready = running && (await health(at))?.status === "ready";
1394
+ const setting = judgeSetting(ctx.files);
1395
+ return {
1396
+ supported: found.length === 0,
1397
+ problems: found,
1398
+ installed: installed(ctx),
1399
+ downloadedBytes: downloadedBytes(ctx.paths.model, MUDE_FILES),
1400
+ pythonReady: venvReady(ctx.paths, ctx.lockFile),
1401
+ running,
1402
+ ready,
1403
+ port: at,
1404
+ pid: running ? pid : void 0,
1405
+ memoryBytes: running ? serverMemory(pid) : void 0,
1406
+ enabled: setting.enabled,
1407
+ localOnly: setting.localOnly,
1408
+ fallback: setting.fallback,
1409
+ model: `${MUDE_REPO}@${MUDE_REVISION}`,
1410
+ folder: ctx.paths.root,
1411
+ log: ctx.paths.log
1412
+ };
1413
+ }
1414
+ async function ensureInstalled(ctx, out) {
1415
+ const missing = missingFiles(ctx.paths.model, MUDE_FILES);
1416
+ if (missing.length > 0) {
1417
+ const need = missing.reduce((sum, file) => sum + file.size, 0);
1418
+ const already = downloadedBytes(ctx.paths.model, MUDE_FILES);
1419
+ const free = freeBytes(ctx.paths.root);
1420
+ const room = MUDE_BYTES - already + 2e9;
1421
+ if (free !== void 0 && free < room) {
1422
+ 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`
1424
+ );
1425
+ }
1426
+ out.step(
1427
+ "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`
1429
+ );
1430
+ try {
1431
+ await downloadFiles({
1432
+ dir: ctx.paths.model,
1433
+ files: MUDE_FILES,
1434
+ url: (path) => mudeFileUrl(path, ctx.env.HF_ENDPOINT || void 0),
1435
+ token: huggingFaceToken(ctx.env),
1436
+ onProgress: (event) => out.progress(event),
1437
+ signal: ctx.signal
1438
+ });
1439
+ } catch (error) {
1440
+ if (error instanceof DownloadError && error.kind === "aborted") {
1441
+ 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`,
1443
+ "aborted"
1444
+ );
1445
+ }
1446
+ throw error;
1447
+ }
1448
+ }
1449
+ 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)");
1451
+ try {
1452
+ let uv = findUv(ctx.paths, ctx.env);
1453
+ if (!uv) uv = await downloadUv(ctx.paths, { onLine: (line) => out.line(line), signal: ctx.signal });
1454
+ await ensureVenv(ctx.paths, ctx.lockFile, uv, { onLine: (line) => out.line(line), env: ctx.env });
1455
+ } catch (error) {
1456
+ if (ctx.signal?.aborted) {
1457
+ throw new DownloadError(
1458
+ "Stopped while setting up the Python environment. Run mu judge mude install to resume",
1459
+ "aborted"
1460
+ );
1461
+ }
1462
+ throw error;
1463
+ }
1464
+ }
1465
+ }
1466
+ function requireInstalled(ctx) {
1467
+ if (!installed(ctx) || !venvReady(ctx.paths, ctx.lockFile)) {
1468
+ throw new Error("mude 1.0 is not installed here yet: mu judge mude install");
1469
+ }
1470
+ }
1471
+ async function start(ctx, out, command, wait2 = true) {
1472
+ const at = await port(ctx);
1473
+ out.step(
1474
+ "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)`
1476
+ );
1477
+ try {
1478
+ await startServer(ctx.paths, { port: at, key: key(ctx), wait: wait2, signal: ctx.signal });
1479
+ } catch (error) {
1480
+ if (error instanceof StartStopped) {
1481
+ 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`,
1483
+ "aborted"
1484
+ );
1485
+ }
1486
+ throw error;
1487
+ }
1488
+ return at;
1489
+ }
1490
+ function enable(ctx, out, at, localOnly) {
1491
+ const tiers = enableMude(ctx.paths, ctx.files, { port: at, key: key(ctx), localOnly });
1492
+ out.step(
1493
+ "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.`
1495
+ );
1496
+ }
1497
+ async function main(argv, ctx, write, tty = false) {
1498
+ const json = argv.includes("--json");
1499
+ const args = argv.filter((arg) => arg !== "--json");
1500
+ const out = json ? jsonOut(write) : humanOut(write, tty);
1501
+ const command = args[0] ?? "status";
1502
+ const flag = (name) => args.includes(name);
1503
+ try {
1504
+ if (command === "help" || command === "--help" || command === "-h") {
1505
+ write(USAGE);
1506
+ return 0;
1507
+ }
1508
+ const unknown = args.slice(1).find((arg) => !["--local-only", "--with-fallback", "--no-wait", "--yes"].includes(arg));
1509
+ if (unknown) {
1510
+ out.error(`Unknown option for mu judge mude ${command}: ${unknown} (mu judge mude help)`, "usage");
1511
+ return 2;
1512
+ }
1513
+ if (command === "status") {
1514
+ out.status(await status(ctx));
1515
+ return 0;
1516
+ }
1517
+ const found = problems();
1518
+ if (found.length > 0 && command !== "stop" && command !== "disable" && command !== "uninstall") {
1519
+ out.error(`mude 1.0 can't run on this machine: ${found.join("; ")}.`, "unsupported");
1520
+ return 1;
1521
+ }
1522
+ 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
+ }
1536
+ 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);
1544
+ break;
1545
+ }
1546
+ case "disable": {
1547
+ if (!mudeInTiers(ctx.files)) {
1548
+ const stopped2 = await stopServer(ctx.paths);
1549
+ out.step("disable", `mude 1.0 was not the judge${stopped2 ? "; its server is stopped" : ""}.`);
1550
+ break;
1551
+ }
1552
+ const tiers = disableMude(ctx.paths, ctx.files);
1553
+ const stopped = await stopServer(ctx.paths);
1554
+ out.step(
1555
+ "disable",
1556
+ `mude 1.0 is no longer the judge: ${tiers.join(" > ")} again${stopped ? ", and its server is stopped" : ""}.`
1557
+ );
1558
+ break;
1559
+ }
1560
+ case "start": {
1561
+ requireInstalled(ctx);
1562
+ await start(ctx, out, "start", !flag("--no-wait"));
1563
+ break;
1564
+ }
1565
+ case "stop": {
1566
+ const stopped = await stopServer(ctx.paths);
1567
+ out.step("stop", stopped ? "The mude 1.0 server is stopped." : "The mude 1.0 server was not running.");
1568
+ break;
1569
+ }
1570
+ case "uninstall": {
1571
+ if (mudeInTiers(ctx.files) || readState(ctx.paths)?.wroteEntry) {
1572
+ const tiers = disableMude(ctx.paths, ctx.files, { removeEntry: true });
1573
+ out.step("disable", `mude 1.0 is no longer the judge: ${tiers.join(" > ")} again.`);
1574
+ }
1575
+ 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.`);
1578
+ break;
1579
+ }
1580
+ default:
1581
+ out.error(`Unknown command: mu judge mude ${command}
1582
+
1583
+ ${USAGE}`, "usage");
1584
+ return 2;
1585
+ }
1586
+ out.status(await status(ctx));
1587
+ return 0;
1588
+ } catch (error) {
1589
+ const kind = error instanceof DownloadError ? error.kind : void 0;
1590
+ out.error(error instanceof Error ? error.message : String(error), kind);
1591
+ return 1;
1592
+ }
1593
+ }
1594
+ 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");
1597
+ return {
1598
+ 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"),
1601
+ env
1602
+ };
1603
+ }
1604
+
1605
+ // packages/kyrn-judge/src/redact.ts
1606
+ var REDACTED = "[redacted]";
1607
+ var SHAPES = [
1608
+ // A private key, the whole block, or to the end when the text was cut inside it.
1609
+ /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z0-9 ]*PRIVATE KEY-----|$)/g,
1610
+ // OpenAI, Anthropic, OpenRouter, DeepSeek and the like.
1611
+ /\bsk-[A-Za-z0-9_-]{20,}/g,
1612
+ /\b(?:gh[pousr]_[A-Za-z0-9]{30,}|github_pat_[A-Za-z0-9_]{22,})/g,
1613
+ /\bglpat-[A-Za-z0-9_-]{20,}/g,
1614
+ /\bxox[abposr]-[A-Za-z0-9-]{10,}/g,
1615
+ /\bAIza[0-9A-Za-z_-]{35}/g,
1616
+ /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g,
1617
+ /\b[rs]k_(?:live|test)_[0-9A-Za-z]{16,}/g,
1618
+ /\bnpm_[A-Za-z0-9]{36}\b/g,
1619
+ /\bhf_[A-Za-z0-9]{30,}/g,
1620
+ // A JSON web token.
1621
+ /\beyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g
1622
+ ];
1623
+ var BEARER = /\b(Bearer|Basic)(\s+)(?=[A-Za-z._~+/=-]*[0-9])[A-Za-z0-9._~+/=-]{12,}/g;
1624
+ var URL_PASSWORD = /\b([a-z][a-z0-9+.-]*:\/\/[^\s:@/]+:)[^\s@/]+(@)/gi;
1625
+ var KEY_WORD = "(?:SECRET|TOKEN|PASSWD|PASSWORD|API_?KEY|ACCESS_?KEY|PRIVATE_?KEY)";
1626
+ var ENV_VALUE = new RegExp(`\\b([A-Z0-9_]*${KEY_WORD}[A-Z0-9_]*=)("[^"\\n]{8,}"|'[^'\\n]{8,}'|[^\\s"';&|]{8,})`, "g");
1627
+ var QUOTED_VALUE = new RegExp(
1628
+ `(["']?[A-Za-z0-9_-]*${KEY_WORD.replaceAll("_?", "[_-]?")}[A-Za-z0-9_-]*["']?\\s*[:=]\\s*)(["'])[^"'\\s]{8,}\\2`,
1629
+ "gi"
1630
+ );
1631
+ var LINE_VALUE = new RegExp(
1632
+ `^(\\s*[A-Za-z0-9_.-]*${KEY_WORD.replaceAll("_?", "[_.-]?")}[A-Za-z0-9_.-]*\\s*[:=][ \\t]*)[^\\s#"']{8,}[ \\t]*$`,
1633
+ "gim"
1634
+ );
1635
+ function redactSecrets(text, known = []) {
1636
+ let clean = text;
1637
+ for (const secret of known) if (clean.includes(secret)) clean = clean.split(secret).join(REDACTED);
1638
+ for (const shape of SHAPES) clean = clean.replace(shape, REDACTED);
1639
+ return clean.replace(BEARER, `$1$2${REDACTED}`).replace(URL_PASSWORD, `$1${REDACTED}$2`).replace(ENV_VALUE, `$1${REDACTED}`).replace(QUOTED_VALUE, `$1$2${REDACTED}$2`).replace(LINE_VALUE, `$1${REDACTED}`);
1640
+ }
1641
+
363
1642
  // packages/kyrn-judge/src/setup/language.ts
364
1643
  function languageOf(value) {
365
1644
  const tag = value?.trim().split(/[.@]/)[0];
@@ -396,7 +1675,7 @@ function piAccess(runtime, agentDir2) {
396
1675
  if (!found) return void 0;
397
1676
  return found.type === "oauth" ? "oauth" : "api_key";
398
1677
  },
399
- storeKey: async (provider, key) => {
1678
+ storeKey: async (provider, key2) => {
400
1679
  await runtime.refresh({ allowNetwork: false, providers: [provider] });
401
1680
  if (!runtime.getProvider(provider)) {
402
1681
  throw new Error(
@@ -411,7 +1690,7 @@ function piAccess(runtime, agentDir2) {
411
1690
  if (asked || prompt.type !== "secret")
412
1691
  throw new Error(`the sign-in for ${provider} asked for more than a key`);
413
1692
  asked = true;
414
- return key;
1693
+ return key2;
415
1694
  },
416
1695
  notify: () => {
417
1696
  }
@@ -520,30 +1799,30 @@ async function readAll(input = process.stdin) {
520
1799
  }
521
1800
 
522
1801
  // packages/kyrn-judge/src/setup/wizard.ts
523
- import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync2 } from "node:fs";
524
- import { join as join3 } from "node:path";
1802
+ import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync7 } from "node:fs";
1803
+ import { join as join8 } from "node:path";
525
1804
 
526
1805
  // packages/kyrn-judge/src/setup/check.ts
527
1806
  var CHECK_TIMEOUT_MS = 1e4;
528
- function checkRequest(kind, baseUrl, key) {
1807
+ function checkRequest(kind, baseUrl, key2) {
529
1808
  const base = baseUrl.replace(/\/+$/, "");
530
1809
  const headers = { accept: "application/json" };
531
1810
  switch (kind) {
532
1811
  case "anthropic-models":
533
1812
  headers["anthropic-version"] = "2023-06-01";
534
- if (key?.includes("sk-ant-oat")) {
535
- headers.authorization = `Bearer ${key}`;
1813
+ if (key2?.includes("sk-ant-oat")) {
1814
+ headers.authorization = `Bearer ${key2}`;
536
1815
  headers["anthropic-beta"] = "oauth-2025-04-20";
537
- } else if (key) headers["x-api-key"] = key;
1816
+ } else if (key2) headers["x-api-key"] = key2;
538
1817
  return { url: `${base}/v1/models?limit=1000`, headers };
539
1818
  case "google-models":
540
- if (key) headers["x-goog-api-key"] = key;
1819
+ if (key2) headers["x-goog-api-key"] = key2;
541
1820
  return { url: `${base}/models?pageSize=1000`, headers };
542
1821
  case "openrouter-key":
543
- if (key) headers.authorization = `Bearer ${key}`;
1822
+ if (key2) headers.authorization = `Bearer ${key2}`;
544
1823
  return { url: `${base}/key`, headers };
545
1824
  default:
546
- if (key) headers.authorization = `Bearer ${key}`;
1825
+ if (key2) headers.authorization = `Bearer ${key2}`;
547
1826
  return { url: `${base}/models`, headers };
548
1827
  }
549
1828
  }
@@ -568,23 +1847,23 @@ function messageOf(body) {
568
1847
  }
569
1848
  return body.trimStart().startsWith("<") ? "" : body;
570
1849
  }
571
- function safeMessage(text, key) {
572
- const known = key ? [key] : [];
1850
+ function safeMessage(text, key2) {
1851
+ const known = key2 ? [key2] : [];
573
1852
  const clean = redactSecrets(text, known).replace(/\*{2,}[A-Za-z0-9_-]{0,12}/g, "\u2026").replace(/\s+/g, " ").trim();
574
1853
  return clean.length > 160 ? `${clean.slice(0, 157)}...` : clean;
575
1854
  }
576
- function classifyAnswer(status, body) {
1855
+ function classifyAnswer(status2, body) {
577
1856
  const text = `${messageOf(body)} ${body}`;
578
- if (status === 401) return "key";
579
- if (status === 402) return "balance";
580
- if (status === 404) return "address";
581
- if (status >= 500) return "server";
582
- if (status === 400 || status === 403 || status === 429) {
1857
+ if (status2 === 401) return "key";
1858
+ if (status2 === 402) return "balance";
1859
+ if (status2 === 404) return "address";
1860
+ if (status2 >= 500) return "server";
1861
+ if (status2 === 400 || status2 === 403 || status2 === 429) {
583
1862
  if (BALANCE.test(text)) return "balance";
584
1863
  if (REGION.test(text)) return "region";
585
1864
  if (WRONG_KEY.test(text)) return "key";
586
- if (status === 429) return "rate";
587
- if (status === 403) return PERMISSION.test(text) ? "permission" : "refused";
1865
+ if (status2 === 429) return "rate";
1866
+ if (status2 === 403) return PERMISSION.test(text) ? "permission" : "refused";
588
1867
  }
589
1868
  return "other";
590
1869
  }
@@ -630,19 +1909,19 @@ function listedModels(kind, body) {
630
1909
  async function checkKey({
631
1910
  kind,
632
1911
  baseUrl,
633
- key,
634
- fetch,
1912
+ key: key2,
1913
+ fetch: fetch2,
635
1914
  timeoutMs = CHECK_TIMEOUT_MS
636
1915
  }) {
637
1916
  if (kind === "opencode-key") {
638
- const refused = await opencodeRefuses(baseUrl, key, fetch, timeoutMs);
1917
+ const refused = await opencodeRefuses(baseUrl, key2, fetch2, timeoutMs);
639
1918
  if (refused) return { ok: false, problem: refused };
640
1919
  }
641
- const request = checkRequest(kind, baseUrl, key);
1920
+ const request = checkRequest(kind, baseUrl, key2);
642
1921
  const url = request.url.split("?")[0];
643
1922
  let response;
644
1923
  try {
645
- response = await fetch(request.url, { headers: request.headers, signal: AbortSignal.timeout(timeoutMs) });
1924
+ response = await fetch2(request.url, { headers: request.headers, signal: AbortSignal.timeout(timeoutMs) });
646
1925
  } catch (error) {
647
1926
  return { ok: false, problem: { kind: classifyFailure(error), url } };
648
1927
  }
@@ -658,7 +1937,7 @@ async function checkKey({
658
1937
  kind: classifyAnswer(response.status, text),
659
1938
  url,
660
1939
  status: response.status,
661
- said: safeMessage(messageOf(text), key) || void 0
1940
+ said: safeMessage(messageOf(text), key2) || void 0
662
1941
  }
663
1942
  };
664
1943
  }
@@ -722,13 +2001,13 @@ function explainProblem(problem, context, language) {
722
2001
  return zh ? `${service} \u62D2\u7EDD\u4E86\u8FD9\u6B21\u68C0\u67E5\uFF08HTTP ${problem.status ?? "?"}\uFF09\u3002${said}` : `${service} rejected the check (HTTP ${problem.status ?? "?"}).${said}`;
723
2002
  }
724
2003
  }
725
- async function opencodeRefuses(baseUrl, key, fetch, timeoutMs) {
2004
+ async function opencodeRefuses(baseUrl, key2, fetch2, timeoutMs) {
726
2005
  const url = `${baseUrl.replace(/\/+$/, "")}/systemone`;
727
2006
  let response;
728
2007
  try {
729
- response = await fetch(url, {
2008
+ response = await fetch2(url, {
730
2009
  method: "POST",
731
- headers: { "content-type": "application/json", ...key ? { authorization: `Bearer ${key}` } : {} },
2010
+ headers: { "content-type": "application/json", ...key2 ? { authorization: `Bearer ${key2}` } : {} },
732
2011
  body: JSON.stringify({
733
2012
  model: "jev-1.13-free",
734
2013
  state: "mu setup is checking a key.",
@@ -745,7 +2024,7 @@ async function opencodeRefuses(baseUrl, key, fetch, timeoutMs) {
745
2024
  kind: classifyAnswer(response.status, text),
746
2025
  url,
747
2026
  status: response.status,
748
- said: safeMessage(messageOf(text), key) || void 0
2027
+ said: safeMessage(messageOf(text), key2) || void 0
749
2028
  };
750
2029
  }
751
2030
 
@@ -939,15 +2218,15 @@ function serviceById(id) {
939
2218
  return SETUP_SERVICES.find((service) => service.id === id);
940
2219
  }
941
2220
  function cleanKey(raw) {
942
- let key = raw.trim();
943
- if (key.length >= 2 && (key[0] === '"' || key[0] === "'") && key.at(-1) === key[0]) key = key.slice(1, -1).trim();
944
- return key;
2221
+ let key2 = raw.trim();
2222
+ if (key2.length >= 2 && (key2[0] === '"' || key2[0] === "'") && key2.at(-1) === key2[0]) key2 = key2.slice(1, -1).trim();
2223
+ return key2;
945
2224
  }
946
- function isClaudeSubscriptionToken(key) {
947
- return cleanKey(key).startsWith("sk-ant-oat");
2225
+ function isClaudeSubscriptionToken(key2) {
2226
+ return cleanKey(key2).startsWith("sk-ant-oat");
948
2227
  }
949
- function servicesForKey(key) {
950
- const clean = cleanKey(key);
2228
+ function servicesForKey(key2) {
2229
+ const clean = cleanKey(key2);
951
2230
  return clean ? SETUP_SERVICES.filter((service) => service.keyShape?.test(clean)) : [];
952
2231
  }
953
2232
  function checkKindOf(service, api) {
@@ -1060,11 +2339,11 @@ function parseSetupArgs(argv) {
1060
2339
  return { error: { zh: "--api \u53EA\u80FD\u662F openai \u6216 anthropic", en: "--api must be openai or anthropic" } };
1061
2340
  }
1062
2341
  const judge = values.get("--judge");
1063
- if (judge !== void 0 && !["jev", "opencode", "free", "model", "off", "none"].includes(judge)) {
2342
+ if (judge !== void 0 && !["jev", "opencode", "free", "model", "mude", "off", "none"].includes(judge)) {
1064
2343
  return {
1065
2344
  error: {
1066
- zh: "--judge \u53EA\u80FD\u662F jev\u3001opencode\u3001free\u3001model\u3001off \u6216 none",
1067
- en: "--judge must be jev, opencode, free, model, off or none"
2345
+ zh: "--judge \u53EA\u80FD\u662F jev\u3001opencode\u3001free\u3001model\u3001mude\u3001off \u6216 none",
2346
+ en: "--judge must be jev, opencode, free, model, mude, off or none"
1068
2347
  }
1069
2348
  };
1070
2349
  }
@@ -1111,6 +2390,7 @@ function setupUsage(language) {
1111
2390
  " --judge opencode OpenCode Zen \u4E0A\u7684 Jev\uFF0C\u7528 OpenCode \u7684\u5BC6\u94A5\uFF08--service opencode \u6216 OPENCODE_API_KEY\uFF09",
1112
2391
  " --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",
1113
2392
  " --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",
2393
+ " --judge mude \u5728\u8FD9\u53F0 Mac \u4E0A\u88C5\u597D mude 1.0\uFF08mu \u81EA\u5DF1\u7684\u51B3\u7B56\u6A21\u578B\uFF0C\u4E0B\u8F7D 1.8 GB\uFF09\u5F53\u5224\u5B9A\u5668\uFF0CJev \u5728\u5B83\u540E\u9762",
1114
2394
  " --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",
1115
2395
  " --judge none \u4E0D\u6539\u5224\u5B9A\u5668\uFF1B\u4E0D\u5199 --judge \u65F6\u4E5F\u4E0D\u6539",
1116
2396
  " --api, --name \u670D\u52A1 other \u7684\u63A5\u53E3\uFF08openai \u6216 anthropic\uFF09\u548C\u5B83\u5728 mu \u91CC\u7684\u540D\u5B57",
@@ -1137,6 +2417,8 @@ function setupUsage(language) {
1137
2417
  " --judge opencode use Jev on OpenCode Zen with an OpenCode key (--service opencode or OPENCODE_API_KEY)",
1138
2418
  " --judge free use the free Jev on OpenCode Zen (limited time, no key; the default while no key is set)",
1139
2419
  " --judge model use the model you just set up as the judge (slower, and every verdict costs tokens)",
2420
+ " --judge mude install mude 1.0, mu's own decision model, on this Mac (a 1.8 GB download) as the judge,",
2421
+ " with Jev behind it",
1140
2422
  " --judge off no judge: every decision point is off, and nothing is sent to a judge",
1141
2423
  " --judge none leave the judge unchanged (the same as omitting --judge)",
1142
2424
  " --api, --name for the service `other`: its API (openai or anthropic) and its name in mu",
@@ -1161,13 +2443,13 @@ function providerIdFor(baseUrl) {
1161
2443
  } catch {
1162
2444
  return "custom";
1163
2445
  }
1164
- const [name, port] = host.split(/:(?=\d+$)/);
2446
+ const [name, port2] = host.split(/:(?=\d+$)/);
1165
2447
  if (name === "localhost" || /^\d+(\.\d+){3}$/.test(name) || name.startsWith("[")) {
1166
- return port ? `local-${port}` : "local";
2448
+ return port2 ? `local-${port2}` : "local";
1167
2449
  }
1168
2450
  const parts = name.split(".").filter((part) => part !== "api" && part !== "www");
1169
- const main2 = parts.length >= 2 ? parts[parts.length - 2] : parts[0] ?? "";
1170
- return main2.replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") || "custom";
2451
+ const main3 = parts.length >= 2 ? parts[parts.length - 2] : parts[0] ?? "";
2452
+ return main3.replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") || "custom";
1171
2453
  }
1172
2454
  function hostOf2(url) {
1173
2455
  try {
@@ -1257,16 +2539,16 @@ var Setup = class {
1257
2539
  for (; ; ) {
1258
2540
  const answer = await this.io.secret(question);
1259
2541
  if (answer === void 0) throw this.cancelled();
1260
- const key = cleanKey(answer);
1261
- if (key || allowEmpty) return key;
2542
+ const key2 = cleanKey(answer);
2543
+ if (key2 || allowEmpty) return key2;
1262
2544
  }
1263
2545
  }
1264
2546
  async pasteKey() {
1265
2547
  for (; ; ) {
1266
- const key = await this.secret(
2548
+ const key2 = await this.secret(
1267
2549
  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): " })
1268
2550
  );
1269
- if (!isClaudeSubscriptionToken(key)) return key;
2551
+ if (!isClaudeSubscriptionToken(key2)) return key2;
1270
2552
  this.io.warn(this.t(CLAUDE_TOKEN));
1271
2553
  }
1272
2554
  }
@@ -1293,7 +2575,7 @@ var Setup = class {
1293
2575
  async steps() {
1294
2576
  const { args, deps } = this;
1295
2577
  if (args.firstRun && !await this.firstStart()) return SETUP_EXIT.done;
1296
- let key = deps.givenKey ?? args.words[0];
2578
+ let key2 = deps.givenKey ?? args.words[0];
1297
2579
  if (args.keyStdin) {
1298
2580
  if (deps.stdinTerminal) {
1299
2581
  throw this.fail(
@@ -1305,11 +2587,11 @@ var Setup = class {
1305
2587
  );
1306
2588
  }
1307
2589
  const [modelKey, jevKey] = (await deps.readStdin()).split(/\r?\n/).map((line) => cleanKey(line)).filter(Boolean);
1308
- key = modelKey;
2590
+ key2 = modelKey;
1309
2591
  this.jevFromStdin = jevKey;
1310
2592
  }
1311
- if (key !== void 0 && isClaudeSubscriptionToken(key)) throw this.fail(CLAUDE_TOKEN, SETUP_EXIT.usage);
1312
- if (this.scripted && key === void 0 && args.service === void 0) {
2593
+ if (key2 !== void 0 && isClaudeSubscriptionToken(key2)) throw this.fail(CLAUDE_TOKEN, SETUP_EXIT.usage);
2594
+ if (this.scripted && key2 === void 0 && args.service === void 0) {
1313
2595
  throw this.fail(
1314
2596
  deps.interactive ? { zh: "\u6CA1\u6709\u5BC6\u94A5\uFF0C\u4E5F\u6CA1\u6709 --service", en: "no key or --service given" } : {
1315
2597
  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",
@@ -1326,18 +2608,41 @@ var Setup = class {
1326
2608
  })
1327
2609
  );
1328
2610
  }
1329
- const start = await this.start(key === void 0 ? void 0 : cleanKey(key));
1330
- if (start === "subscription") {
1331
- const code2 = await this.write(void 0, void 0, await this.judge(void 0));
1332
- return code2 === SETUP_EXIT.done ? this.offerSession(true) : code2;
2611
+ const start2 = await this.start(key2 === void 0 ? void 0 : cleanKey(key2));
2612
+ if (start2 === "subscription") {
2613
+ const judge2 = await this.judge(void 0);
2614
+ const code2 = await this.write(void 0, void 0, judge2);
2615
+ if (code2 !== SETUP_EXIT.done) return code2;
2616
+ const installed3 = await this.installMude(judge2);
2617
+ return installed3 === SETUP_EXIT.done ? this.offerSession(true) : installed3;
1333
2618
  }
1334
- const target = await this.checked(start.service, start.key);
2619
+ const target = await this.checked(start2.service, start2.key);
1335
2620
  this.modelKey = target.key;
1336
2621
  const model = await this.model(target);
1337
2622
  await this.conflicts(target);
1338
2623
  const judge = await this.judge({ provider: target.reach.provider, model });
1339
2624
  const code = await this.write(target, model, judge);
1340
- return code === SETUP_EXIT.done ? this.offerSession() : code;
2625
+ if (code !== SETUP_EXIT.done) return code;
2626
+ const installed2 = await this.installMude(judge);
2627
+ return installed2 === SETUP_EXIT.done ? this.offerSession() : installed2;
2628
+ }
2629
+ /**
2630
+ * mude, when it was picked: installed now that the files name Jev, which it goes in front of. One that does not get
2631
+ * installed (no connection, stopped) leaves Jev as the judge, and `mu judge mude install` goes on from where it got.
2632
+ */
2633
+ async installMude(judge) {
2634
+ const mude = this.deps.mude;
2635
+ if (judge?.kind !== "mude" || !mude) return SETUP_EXIT.done;
2636
+ this.io.say("");
2637
+ const code = await mude.install();
2638
+ if (code === 0) return SETUP_EXIT.done;
2639
+ this.io.warn(
2640
+ this.t({
2641
+ zh: "mude 1.0 \u8FD8\u6CA1\u6709\u88C5\u597D\uFF0C\u5728\u90A3\u4E4B\u524D\u7531 Jev \u5224\u5B9A\u3002\u8FD0\u884C mu judge mude install \u4F1A\u4ECE\u4E2D\u65AD\u7684\u5730\u65B9\u63A5\u7740\u88C5\u3002",
2642
+ en: "mude 1.0 isn't installed yet, so Jev is the judge until it is. Run mu judge mude install to resume it."
2643
+ })
2644
+ );
2645
+ return this.scripted ? SETUP_EXIT.failed : SETUP_EXIT.done;
1341
2646
  }
1342
2647
  /** The launcher's question on a first start. A no is remembered: the question is never asked again. */
1343
2648
  async firstStart() {
@@ -1350,9 +2655,9 @@ var Setup = class {
1350
2655
  );
1351
2656
  if (yes) return true;
1352
2657
  try {
1353
- mkdirSync3(this.deps.paths.agentDir, { recursive: true });
1354
- writeFileSync2(
1355
- join3(this.deps.paths.agentDir, SETUP_DECLINED_FILE),
2658
+ mkdirSync7(this.deps.paths.agentDir, { recursive: true });
2659
+ writeFileSync7(
2660
+ join8(this.deps.paths.agentDir, SETUP_DECLINED_FILE),
1356
2661
  "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"
1357
2662
  );
1358
2663
  } catch {
@@ -1368,10 +2673,10 @@ var Setup = class {
1368
2673
  // -----------------------------------------------------------------------------------------------------------
1369
2674
  // Steps 1 and 2: how a model is reached, and which service a key belongs to
1370
2675
  // -----------------------------------------------------------------------------------------------------------
1371
- async start(key) {
2676
+ async start(key2) {
1372
2677
  const named2 = this.args.service === void 0 ? void 0 : serviceById(this.args.service);
1373
- if (named2) return this.keyFor(named2, key);
1374
- if (key !== void 0) return { service: await this.serviceOf(key), key };
2678
+ if (named2) return this.keyFor(named2, key2);
2679
+ if (key2 !== void 0) return { service: await this.serviceOf(key2), key: key2 };
1375
2680
  const route = await this.choose(
1376
2681
  this.t({ zh: "mu \u7528\u4EC0\u4E48\u65B9\u5F0F\u8FDE\u63A5\u6A21\u578B\uFF1F", en: "How should mu connect to a model?" }),
1377
2682
  [
@@ -1393,11 +2698,11 @@ var Setup = class {
1393
2698
  ]
1394
2699
  );
1395
2700
  if (route === "subscription") {
1396
- const auth = this.path(join3(this.deps.paths.agentDir, "auth.json"));
2701
+ const auth = this.path(join8(this.deps.paths.agentDir, "auth.json"));
1397
2702
  this.io.say(
1398
2703
  this.t({
1399
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`,
1400
- en: `You sign in to a subscription 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}.`
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}.`
1401
2706
  })
1402
2707
  );
1403
2708
  return "subscription";
@@ -1416,8 +2721,8 @@ var Setup = class {
1416
2721
  * The service a key belongs to, by its shape. When the shape fits several, or none, the person says which: a key is
1417
2722
  * never tried on one vendor after another.
1418
2723
  */
1419
- async serviceOf(key) {
1420
- const matches = servicesForKey(key);
2724
+ async serviceOf(key2) {
2725
+ const matches = servicesForKey(key2);
1421
2726
  if (matches.length === 1) {
1422
2727
  this.io.say(
1423
2728
  this.t({
@@ -1460,23 +2765,23 @@ var Setup = class {
1460
2765
  async keyFor(service, given) {
1461
2766
  if (service.keyless) return { service };
1462
2767
  if (service.keyOptional && given === void 0) return { service };
1463
- let key = given;
1464
- if (key === void 0 && !this.scripted) {
2768
+ let key2 = given;
2769
+ if (key2 === void 0 && !this.scripted) {
1465
2770
  if (service.keyPage) {
1466
2771
  this.io.say(this.t({ zh: `\u5BC6\u94A5\u5728\u8FD9\u91CC\u521B\u5EFA\uFF1A${service.keyPage}`, en: `Create a key at ${service.keyPage}` }));
1467
2772
  }
1468
- key = await this.pasteKey();
2773
+ key2 = await this.pasteKey();
1469
2774
  }
1470
- if (!key) {
2775
+ if (!key2) {
1471
2776
  if (service.keyOptional) return { service };
1472
2777
  throw this.fail(
1473
2778
  { zh: `${this.name(service)} \u9700\u8981\u4E00\u4E2A API \u5BC6\u94A5`, en: `${this.name(service)} needs an API key` },
1474
2779
  SETUP_EXIT.usage
1475
2780
  );
1476
2781
  }
1477
- const matches = servicesForKey(key);
2782
+ const matches = servicesForKey(key2);
1478
2783
  const ownAddress = service.id === "other" || this.args.baseUrl !== void 0;
1479
- if (matches.length === 0 || matches.includes(service) || ownAddress) return { service, key };
2784
+ if (matches.length === 0 || matches.includes(service) || ownAddress) return { service, key: key2 };
1480
2785
  if (this.scripted) {
1481
2786
  this.io.warn(
1482
2787
  this.t({
@@ -1484,7 +2789,7 @@ var Setup = class {
1484
2789
  en: `Note: this key looks like ${article(this.names(matches))} key; sending it to ${this.name(service)}, as --service says.`
1485
2790
  })
1486
2791
  );
1487
- return { service, key };
2792
+ return { service, key: key2 };
1488
2793
  }
1489
2794
  const chosen = await this.pickService(
1490
2795
  [service, ...matches],
@@ -1493,7 +2798,7 @@ var Setup = class {
1493
2798
  en: `This key looks like ${article(this.names(matches))} key, not ${article(this.name(service))} one. Which service is it for?`
1494
2799
  })
1495
2800
  );
1496
- return { service: chosen, key };
2801
+ return { service: chosen, key: key2 };
1497
2802
  }
1498
2803
  // -----------------------------------------------------------------------------------------------------------
1499
2804
  // Step 3: one request to the service
@@ -1566,7 +2871,7 @@ var Setup = class {
1566
2871
  return [{ provider, builtIn: false, baseUrl: base, api }];
1567
2872
  }
1568
2873
  inModelsFile(provider) {
1569
- const models = readJsonFile(join3(this.deps.paths.agentDir, "models.json"));
2874
+ const models = readJsonFile(join8(this.deps.paths.agentDir, "models.json"));
1570
2875
  return models.state === "ok" && existingProvider(models.value, provider) !== void 0;
1571
2876
  }
1572
2877
  /** A failure as the person can act on it: a local service that is silent is not ready yet, whatever the key. */
@@ -1580,10 +2885,10 @@ var Setup = class {
1580
2885
  }
1581
2886
  /** The key, checked: asked again, retried, or taken unchecked until the person has one that works, or stops. */
1582
2887
  async checked(service, firstKey) {
1583
- let key = firstKey;
2888
+ let key2 = firstKey;
1584
2889
  const reaches = await this.reaches(service);
1585
- if (service.keyOptional && key === void 0 && !this.scripted) {
1586
- key = await this.secret(
2890
+ if (service.keyOptional && key2 === void 0 && !this.scripted) {
2891
+ key2 = await this.secret(
1587
2892
  this.t({
1588
2893
  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",
1589
2894
  en: "Paste its API key, or press Enter if it doesn't need one (it won't be shown): "
@@ -1594,8 +2899,8 @@ var Setup = class {
1594
2899
  if (!reaches[0].builtIn || this.args.baseUrl) this.modelsFileWritable();
1595
2900
  await this.oauthConflict(service, reaches);
1596
2901
  for (; ; ) {
1597
- const result = await this.tryReaches(service, reaches, key);
1598
- if (result.ok) return { service, reach: result.reach, key, listed: result.listed };
2902
+ const result = await this.tryReaches(service, reaches, key2);
2903
+ if (result.ok) return { service, reach: result.reach, key: key2, listed: result.listed };
1599
2904
  const said = this.explain(service, result.problem);
1600
2905
  if (this.scripted) throw this.fail({ zh: said, en: said });
1601
2906
  this.io.say(said);
@@ -1615,19 +2920,19 @@ var Setup = class {
1615
2920
  });
1616
2921
  const next = await this.choose(this.t({ zh: "\u63A5\u4E0B\u6765\u600E\u4E48\u529E\uFF1F", en: "What next?" }), choices);
1617
2922
  if (next === "stop") throw this.cancelled();
1618
- if (next === "unchecked") return { service, reach: reaches[0], key };
1619
- if (next === "key") key = await this.pasteKey();
2923
+ if (next === "unchecked") return { service, reach: reaches[0], key: key2 };
2924
+ if (next === "key") key2 = await this.pasteKey();
1620
2925
  }
1621
2926
  }
1622
2927
  /** Each address of the service in turn: Kimi's .cn, then .ai; an address of one's own, then with /v1 added. */
1623
- async tryReaches(service, reaches, key) {
2928
+ async tryReaches(service, reaches, key2) {
1624
2929
  let first;
1625
2930
  const candidates = [...reaches];
1626
2931
  for (let index = 0; index < candidates.length; index++) {
1627
2932
  const reach = candidates[index];
1628
2933
  const where = `${this.name(service)}\uFF08${hostOf2(reach.baseUrl)}\uFF09`;
1629
2934
  this.io.say(
1630
- key ? this.t({
2935
+ key2 ? this.t({
1631
2936
  zh: `\u6B63\u5728\u7528 ${where}\u68C0\u67E5\u5BC6\u94A5\u2026\u2026`,
1632
2937
  en: `Checking the key with ${this.name(service)} (${hostOf2(reach.baseUrl)})\u2026`
1633
2938
  }) : this.t({
@@ -1638,7 +2943,7 @@ var Setup = class {
1638
2943
  const result = await checkKey({
1639
2944
  kind: checkKindOf(service, reach.api),
1640
2945
  baseUrl: reach.baseUrl,
1641
- key,
2946
+ key: key2,
1642
2947
  fetch: this.deps.fetch,
1643
2948
  timeoutMs: this.deps.timeoutMs ?? CHECK_TIMEOUT_MS
1644
2949
  });
@@ -1686,7 +2991,7 @@ var Setup = class {
1686
2991
  }
1687
2992
  }
1688
2993
  modelsFileWritable() {
1689
- const path = join3(this.deps.paths.agentDir, "models.json");
2994
+ const path = join8(this.deps.paths.agentDir, "models.json");
1690
2995
  if (readJsonFile(path).state !== "unreadable") return;
1691
2996
  throw this.fail({
1692
2997
  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`,
@@ -1768,7 +3073,7 @@ var Setup = class {
1768
3073
  /** A provider models.json has at another address is moved only with a yes. */
1769
3074
  async conflicts(target) {
1770
3075
  if (target.reach.builtIn && !this.args.baseUrl) return;
1771
- const models = readJsonFile(join3(this.deps.paths.agentDir, "models.json"));
3076
+ const models = readJsonFile(join8(this.deps.paths.agentDir, "models.json"));
1772
3077
  if (models.state !== "ok") return;
1773
3078
  const current = existingProvider(models.value, target.reach.provider)?.baseUrl;
1774
3079
  const old = typeof current === "string" ? current.replace(/\/+$/, "") : void 0;
@@ -1802,10 +3107,23 @@ var Setup = class {
1802
3107
  en: `${existing.name} in ${this.path(paths.envFile)}`
1803
3108
  }) : void 0;
1804
3109
  const choices = [];
3110
+ const mude = this.deps.mude !== void 0;
3111
+ if (mude) {
3112
+ choices.push({
3113
+ label: this.t({
3114
+ zh: "mude 1.0\uFF08\u63A8\u8350\uFF09\uFF0C\u5728\u8FD9\u53F0 Mac \u4E0A\u8FD0\u884C\uFF1Amu \u81EA\u5DF1\u7684\u51B3\u7B56\u6A21\u578B\uFF0C\u5224\u5B9A\u4E0D\u51FA\u672C\u673A\u3002\u4E0B\u8F7D\u4E00\u6B21 1.8 GB\uFF0C\u8FD0\u884C\u65F6\u7EA6\u5360 4 GB \u5185\u5B58\uFF1B\u5B83\u7B54\u4E0D\u4E86\u7684\u4EA4\u7ED9 Jev",
3115
+ en: "mude 1.0 on this Mac (recommended): mu's own decision model, and its verdicts stay on this Mac. A one-time 1.8 GB download; it uses about 4 GB of memory while it runs, and Jev takes what it can't answer"
3116
+ }),
3117
+ value: "mude"
3118
+ });
3119
+ }
1805
3120
  const keyless = !opencode && !where;
1806
3121
  if (keyless) {
1807
3122
  choices.push({
1808
- label: this.t({
3123
+ label: mude ? this.t({
3124
+ zh: "Jev \u514D\u8D39\u7248\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",
3125
+ en: "Free Jev on OpenCode Zen: no key needed, free for a limited time; a key you add later takes over"
3126
+ }) : this.t({
1809
3127
  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",
1810
3128
  en: "Free Jev on OpenCode Zen (recommended): no key needed, free for a limited time; a key you add later takes over"
1811
3129
  }),
@@ -1814,7 +3132,10 @@ var Setup = class {
1814
3132
  }
1815
3133
  if (opencode && !where) {
1816
3134
  choices.push({
1817
- label: this.t({
3135
+ label: mude ? this.t({
3136
+ zh: "Jev\uFF0C\u5728 OpenCode Zen \u4E0A\uFF0C\u7528\u4F60\u7684 OpenCode \u5BC6\u94A5\uFF0C\u6309\u6B21\u8BA1\u8D39",
3137
+ en: "Jev on OpenCode Zen with your OpenCode key, billed per call"
3138
+ }) : this.t({
1818
3139
  zh: "Jev\uFF08\u63A8\u8350\uFF09\uFF0C\u5728 OpenCode Zen \u4E0A\uFF0C\u7528\u4F60\u7684 OpenCode \u5BC6\u94A5\uFF0C\u6309\u6B21\u8BA1\u8D39",
1819
3140
  en: "Jev on OpenCode Zen with your OpenCode key (recommended), billed per call"
1820
3141
  }),
@@ -1823,7 +3144,10 @@ var Setup = class {
1823
3144
  }
1824
3145
  if (where) {
1825
3146
  choices.push({
1826
- label: this.t({
3147
+ label: mude ? this.t({
3148
+ zh: `Jev\uFF0C\u7528\u5DF2\u7ECF\u8BBE\u7F6E\u7684\u5BC6\u94A5\uFF08${where}\uFF09`,
3149
+ en: `Jev with the key that's already set: ${where}`
3150
+ }) : this.t({
1827
3151
  zh: `Jev\uFF08\u63A8\u8350\uFF09\uFF0C\u7528\u5DF2\u7ECF\u8BBE\u7F6E\u7684\u5BC6\u94A5\uFF08${where}\uFF09`,
1828
3152
  en: `Jev with the key that's already set (recommended): ${where}`
1829
3153
  }),
@@ -1890,11 +3214,22 @@ var Setup = class {
1890
3214
  return { kind: "opencode" };
1891
3215
  case "free":
1892
3216
  return { kind: "free" };
3217
+ case "mude":
3218
+ if (!this.deps.mude) {
3219
+ throw this.fail(
3220
+ {
3221
+ zh: "--judge mude \u9700\u8981 Apple \u82AF\u7247\u7684 Mac\uFF08macOS 14 \u53CA\u4EE5\u4E0A\uFF0C\u5185\u5B58 8 GB \u53CA\u4EE5\u4E0A\uFF09",
3222
+ en: "--judge mude needs a Mac with Apple silicon (macOS 14 or later, 8 GB of memory or more)"
3223
+ },
3224
+ SETUP_EXIT.usage
3225
+ );
3226
+ }
3227
+ return { kind: "mude" };
1893
3228
  case "off":
1894
3229
  return { kind: "none" };
1895
3230
  case "jev": {
1896
- const key = this.jevFromStdin;
1897
- if (!key) {
3231
+ const key2 = this.jevFromStdin;
3232
+ if (!key2) {
1898
3233
  if (keySet) return { kind: "keep" };
1899
3234
  throw this.fail(
1900
3235
  {
@@ -1904,20 +3239,20 @@ var Setup = class {
1904
3239
  SETUP_EXIT.usage
1905
3240
  );
1906
3241
  }
1907
- if (key === this.modelKey) {
3242
+ if (key2 === this.modelKey) {
1908
3243
  throw this.fail(
1909
3244
  { 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" },
1910
3245
  SETUP_EXIT.usage
1911
3246
  );
1912
3247
  }
1913
3248
  const current = envText === void 0 ? void 0 : envFileValue(envText, "TYPESAFE_API_KEY");
1914
- if (current && current !== key && !this.args.yes) {
3249
+ if (current && current !== key2 && !this.args.yes) {
1915
3250
  throw this.fail({
1916
3251
  zh: `${this.path(this.deps.paths.envFile)} \u91CC\u5DF2\u7ECF\u6709 TYPESAFE_API_KEY\u3002\u8981\u66FF\u6362\u5B83\uFF0C\u52A0\u4E0A --yes`,
1917
3252
  en: `${this.path(this.deps.paths.envFile)} already has a TYPESAFE_API_KEY. To replace it, add --yes`
1918
3253
  });
1919
3254
  }
1920
- return { kind: "jev", key };
3255
+ return { kind: "jev", key: key2 };
1921
3256
  }
1922
3257
  case "model":
1923
3258
  return model ? { kind: "model" } : void 0;
@@ -1927,13 +3262,13 @@ var Setup = class {
1927
3262
  }
1928
3263
  async jevKey() {
1929
3264
  for (; ; ) {
1930
- const key = await this.secret(
3265
+ const key2 = await this.secret(
1931
3266
  this.t({
1932
3267
  zh: "\u7C98\u8D34 Jev \u5BC6\u94A5\uFF08TYPESAFE_API_KEY\uFF0C\u4E0D\u4F1A\u663E\u793A\uFF09\uFF1A",
1933
3268
  en: "Paste the Jev key (TYPESAFE_API_KEY; it won't be shown): "
1934
3269
  })
1935
3270
  );
1936
- if (key === this.modelKey) {
3271
+ if (key2 === this.modelKey) {
1937
3272
  this.io.say(
1938
3273
  this.t({
1939
3274
  zh: `\u8FD9\u662F\u521A\u624D\u7ED9\u6A21\u578B\u7684\u5BC6\u94A5\u3002Jev \u7684\u5BC6\u94A5\u5728 TypeSafe \u7533\u8BF7\uFF1A${JEV_PAGE}`,
@@ -1942,8 +3277,8 @@ var Setup = class {
1942
3277
  );
1943
3278
  continue;
1944
3279
  }
1945
- const branded = servicesForKey(key).find((service) => BRANDED.has(service.id));
1946
- if (!branded) return key;
3280
+ const branded = servicesForKey(key2).find((service) => BRANDED.has(service.id));
3281
+ if (!branded) return key2;
1947
3282
  const anyway = await this.confirm(
1948
3283
  this.t({
1949
3284
  zh: `\u8FD9\u770B\u8D77\u6765\u662F ${this.name(branded)} \u7684\u5BC6\u94A5\uFF0C\u4E0D\u662F Jev \u7684\uFF0C\u5B83\u4F1A\u88AB\u53D1\u7ED9 TypeSafe\u3002\u4ECD\u7136\u4F7F\u7528\u5417\uFF1F[y/N] `,
@@ -1951,7 +3286,7 @@ var Setup = class {
1951
3286
  }),
1952
3287
  false
1953
3288
  );
1954
- if (anyway) return key;
3289
+ if (anyway) return key2;
1955
3290
  }
1956
3291
  }
1957
3292
  // -----------------------------------------------------------------------------------------------------------
@@ -1963,12 +3298,12 @@ var Setup = class {
1963
3298
  const backups = new Backups(deps.paths.home, deps.now());
1964
3299
  const saved = [];
1965
3300
  const notes = [];
1966
- const modelsPath = join3(agentDir2, "models.json");
1967
- const authPath2 = join3(agentDir2, "auth.json");
1968
- const settingsPath = join3(agentDir2, "settings.json");
3301
+ const modelsPath = join8(agentDir2, "models.json");
3302
+ const authPath2 = join8(agentDir2, "auth.json");
3303
+ const settingsPath = join8(agentDir2, "settings.json");
1969
3304
  try {
1970
3305
  if (target && model) {
1971
- const { service, reach, key } = target;
3306
+ const { service, reach, key: key2 } = target;
1972
3307
  const provider = reach.provider;
1973
3308
  const custom = !reach.builtIn;
1974
3309
  const moved = reach.builtIn && this.args.baseUrl !== void 0;
@@ -1983,7 +3318,7 @@ var Setup = class {
1983
3318
  baseUrl: reach.baseUrl,
1984
3319
  api: reach.api,
1985
3320
  // pi counts a provider without a key as unusable: a keyless service gets a placeholder, never a real key.
1986
- apiKey: service.keyless ? "ollama" : key ? void 0 : "none",
3321
+ apiKey: service.keyless ? "ollama" : key2 ? void 0 : "none",
1987
3322
  compat: service.compat,
1988
3323
  // What the service lists goes into /model, the one to start with first.
1989
3324
  models: [model, ...chatModels(target.listed ?? []).filter((id) => id !== model)].map((id) => ({
@@ -2009,9 +3344,9 @@ var Setup = class {
2009
3344
  )
2010
3345
  );
2011
3346
  }
2012
- if (key) {
3347
+ if (key2) {
2013
3348
  backups.save(authPath2);
2014
- await deps.pi.storeKey(provider, key);
3349
+ await deps.pi.storeKey(provider, key2);
2015
3350
  saved.push(
2016
3351
  this.savedLine(
2017
3352
  authPath2,
@@ -2107,7 +3442,7 @@ var Setup = class {
2107
3442
  if (judge.kind === "model") {
2108
3443
  if (target && model) tiers = [`llm:${target.reach.provider}/${model}`];
2109
3444
  } else if (judge.kind === "opencode") tiers = ["jev-opencode"];
2110
- else if (judge.kind === "free") {
3445
+ else if (judge.kind === "free" || judge.kind === "mude") {
2111
3446
  if (!tiersOf(current).some((tier) => tier === "jev" || tier === "jev-opencode-free")) tiers = ["jev"];
2112
3447
  } else if (!tiersOf(current).some((tier) => JEV_TIERS.has(tier))) tiers = ["jev"];
2113
3448
  const turnOn = judgeOff(current);
@@ -2160,7 +3495,10 @@ var Setup = class {
2160
3495
  zh: `\u5B8C\u6210\u3002mu \u4F1A\u7528 ${this.name(target.service)} \xB7 ${model} \u5F00\u59CB\u5DE5\u4F5C\u3002`,
2161
3496
  en: `Done. mu will start on ${this.name(target.service)} \xB7 ${model}.`
2162
3497
  }) : this.t({ zh: "\u5B8C\u6210\u3002", en: "Done." });
2163
- 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({
3498
+ 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 === "mude" ? this.t({
3499
+ zh: "\u5224\u5B9A\u5668\uFF1Amude 1.0\uFF0C\u4E0B\u9762\u63A5\u7740\u5B89\u88C5\uFF1B\u5B83\u7B54\u4E0D\u4E86\u7684\u4EA4\u7ED9 Jev\u3002",
3500
+ en: " Judge: mude 1.0, installed next, with Jev for what it can't answer."
3501
+ }) : judge.kind === "free" ? this.t({
2164
3502
  zh: "\u5224\u5B9A\u5668\uFF1AJev\uFF0C\u52A0\u5BC6\u94A5\u4E4B\u524D\u7528 OpenCode Zen \u4E0A\u9650\u65F6\u514D\u8D39\u7684\u7248\u672C\u3002",
2165
3503
  en: " Judge: Jev, using the free version on OpenCode Zen until you add a key."
2166
3504
  }) : this.t({ zh: "\u5224\u5B9A\u5668\uFF1AJev\u3002", en: " Judge: Jev." });
@@ -2180,8 +3518,8 @@ var Setup = class {
2180
3518
  async offerSession(signIn = false) {
2181
3519
  if (this.scripted) return SETUP_EXIT.done;
2182
3520
  if (this.args.firstRun) return signIn ? SETUP_EXIT.signIn : SETUP_EXIT.done;
2183
- const start = await this.confirm(this.t({ zh: "\u73B0\u5728\u542F\u52A8 mu \u5417\uFF1F[Y/n] ", en: "Start mu now? [Y/n] " }), true);
2184
- if (!start) return SETUP_EXIT.done;
3521
+ const start2 = await this.confirm(this.t({ zh: "\u73B0\u5728\u542F\u52A8 mu \u5417\uFF1F[Y/n] ", en: "Start mu now? [Y/n] " }), true);
3522
+ if (!start2) return SETUP_EXIT.done;
2185
3523
  return signIn ? SETUP_EXIT.signIn : SETUP_EXIT.start;
2186
3524
  }
2187
3525
  };
@@ -2211,10 +3549,10 @@ async function runSetup(argv, deps) {
2211
3549
  var givenKey = process.env.MU_SETUP_KEY || void 0;
2212
3550
  delete process.env.MU_SETUP_KEY;
2213
3551
  var home = process.env.MU_SETUP_HOME || muHome();
2214
- var agentDir = muEnv("AGENT_DIR") || join4(home, "agent");
2215
- var envFile = process.env.MU_SETUP_ENV_FILE || join4(home, ".env");
2216
- var authPath = join4(agentDir, "auth.json");
2217
- var modelsStorePath = join4(agentDir, "models-store.json");
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");
2218
3556
  var givenEnv = { ...process.env };
2219
3557
  var PROXY_VARIABLES = ["HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy"];
2220
3558
  function useSessionProxy() {
@@ -2234,11 +3572,11 @@ function useSessionProxy() {
2234
3572
  if (named2) return named2 in givenEnv ? named2 : `${named2}, .env`;
2235
3573
  return fromSettings ? "httpProxy, settings.json" : void 0;
2236
3574
  }
2237
- async function main() {
3575
+ async function main2() {
2238
3576
  const proxy = useSessionProxy();
2239
3577
  const runtime = await ModelRuntime.create({
2240
3578
  authPath,
2241
- modelsPath: join4(agentDir, "models.json"),
3579
+ modelsPath: join9(agentDir, "models.json"),
2242
3580
  modelsStorePath,
2243
3581
  refreshOnCreate: false
2244
3582
  });
@@ -2255,7 +3593,16 @@ async function main() {
2255
3593
  stdinTerminal: process.stdin.isTTY === true,
2256
3594
  readStdin: () => readAll(),
2257
3595
  givenKey,
2258
- proxy
3596
+ proxy,
3597
+ // mude 1.0 where this Mac can run it: `mu judge mude install`, here, its progress in this terminal.
3598
+ mude: problems().length === 0 ? {
3599
+ install: () => main(
3600
+ ["install"],
3601
+ contextFromEnv(process.env),
3602
+ (text) => process.stdout.write(text),
3603
+ process.stdout.isTTY === true
3604
+ )
3605
+ } : void 0
2259
3606
  });
2260
3607
  }
2261
3608
  var leaving = false;
@@ -2266,8 +3613,8 @@ var leave = (code) => {
2266
3613
  void exitWhenUnlocked(code, [authPath, modelsStorePath], (exitCode) => process.exit(exitCode));
2267
3614
  };
2268
3615
  for (const signal of ["SIGTERM", "SIGINT", "SIGHUP"])
2269
- process.on(signal, () => leave(128 + constants.signals[signal]));
2270
- main().then(leave, (error) => {
3616
+ process.on(signal, () => leave(128 + constants2.signals[signal]));
3617
+ main2().then(leave, (error) => {
2271
3618
  const message = error instanceof Error ? error.message : String(error);
2272
3619
  process.stderr.write(`mu setup: ${redactSecrets(message, givenKey ? [givenKey] : [])}
2273
3620
  `);