mu-agent 0.2.1 → 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.
- package/CHANGELOG.md +19 -0
- package/judge/dist/mude.js +134 -36
- package/judge/dist/setup.js +136 -38
- package/judge/manifest.json +16 -16
- package/kyrn/bin/mu.mjs +2 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.2.2] - 2026-10-24
|
|
6
|
+
|
|
7
|
+
**0.2.2 makes `mu judge mude install` cope with a full disk, a stop at any step and two installs at once.**
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- English pass on the strings added since 0.1.10, against `docs/writing-style.md`: `mu judge mude` messages say how to resume ("Run mu judge mude install to resume"); errors say what happened and what to do next ("Couldn't create the Python environment (uv venv)", "Hugging Face returned 404 for …", the disk-space error); the kernel's settings descriptions read naturally (their translations kept); `mu --help` describes `mu judge mude install` as "install mude 1.0, mu's own decision model for coding, and make it the judge on this Mac".
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- `mu judge mude install`: Ctrl-C (or SIGTERM) during the wait between tries after a dropped connection now stops the download at once. It prints "Stopped with X of 1.8 GB downloaded. Run mu judge mude install to resume". Before, the wait (up to 30 s) ran on, and the command ended 5 s later with no message. What was downloaded was kept either way.
|
|
16
|
+
- `mu judge mude install`, disk space and stops:
|
|
17
|
+
- A full disk (ENOSPC or EDQUOT) during the download now ends the install at once with "The disk is full: couldn't write <file>. Free up some space, then run mu judge mude install to resume". Before, it was retried as a network error for about two minutes, and the desktop app said it couldn't reach Hugging Face.
|
|
18
|
+
- The free-space check before a download counts only the bytes still to come. Before, it counted the whole of every unfinished file, even when most of it was on the disk already.
|
|
19
|
+
- Ctrl-C (or SIGTERM) while the Python environment is being set up now says "Stopped while setting up the Python environment. Run mu judge mude install to resume", instead of "mude's Python packages could not be installed".
|
|
20
|
+
- `mu judge mude install` on a Mac without uv: downloading the pinned uv (about 20 MB, from GitHub) had no time limit and ignored a stop. It now ends after 5 minutes with "uv could not be downloaded from GitHub (no answer in 300 s): run mu judge mude install again", and Ctrl-C or the app's Cancel ends it at once ("Stopped while setting up the Python environment").
|
|
21
|
+
- `mu judge mude install` (also from `mu setup` and the desktop app) takes `local-judge/mude/install.lock`. A second install while one runs now stops at once with "Another mude 1.0 install is running (process N). Wait for it to finish, or stop it, then run mu judge mude install again". Before, both downloaded into the same `.part` files, and the second failed with `ENOENT … .part` when the first renamed a finished file. The lock records the process's pid and start time, so a lock left by a power cut or a kill is taken over, even if the pid has since been reused.
|
|
22
|
+
- `mu judge mude install`, `enable` and `start`: a stop (Ctrl-C, or SIGTERM from the app's Cancel) while waiting for the server's `/health` now ends the wait at once. It also stops the server if this command started it, with "Stopped while the mude 1.0 server was starting, and stopped the server too. Run mu judge mude install again to finish". Before, the wait ran on until the CLI's 5 s exit timer, with no message. The server is started `detached`, so it survived both that exit and the app's process-group kill, and kept about 4 GB of memory while mude was not the judge. A server that was already running before the command is left running: "Stopped waiting for the mude 1.0 server, which is still starting…".
|
|
23
|
+
|
|
5
24
|
## [0.2.1] - 2026-10-24
|
|
6
25
|
|
|
7
26
|
**0.2.1 makes mude 1.0 the recommended judge on a Mac, set up from `mu setup`, and its install survives a power cut.**
|
package/judge/dist/mude.js
CHANGED
|
@@ -92,21 +92,21 @@ function readEnvEntries(text) {
|
|
|
92
92
|
}
|
|
93
93
|
let body = rest.slice(1);
|
|
94
94
|
let end = closingQuote(body, quote);
|
|
95
|
-
const
|
|
95
|
+
const startedAt2 = index;
|
|
96
96
|
while (end < 0 && index + 1 < lines.length) {
|
|
97
97
|
body += `
|
|
98
98
|
${lines[++index]}`;
|
|
99
99
|
end = closingQuote(body, quote);
|
|
100
100
|
}
|
|
101
101
|
if (end < 0) {
|
|
102
|
-
index =
|
|
102
|
+
index = startedAt2;
|
|
103
103
|
continue;
|
|
104
104
|
}
|
|
105
105
|
const value = body.slice(0, end);
|
|
106
106
|
entries.push({
|
|
107
107
|
name,
|
|
108
108
|
value: quote === '"' ? value.replace(/\\(["\\$`])/g, "$1") : value,
|
|
109
|
-
first:
|
|
109
|
+
first: startedAt2,
|
|
110
110
|
last: index
|
|
111
111
|
});
|
|
112
112
|
}
|
|
@@ -409,12 +409,21 @@ function tail(file, lines = 12) {
|
|
|
409
409
|
return "";
|
|
410
410
|
}
|
|
411
411
|
}
|
|
412
|
+
var StartStopped = class extends Error {
|
|
413
|
+
/** Whether the server went too: one this call started does, one that was running before does not. */
|
|
414
|
+
serverStopped;
|
|
415
|
+
constructor(serverStopped = false) {
|
|
416
|
+
super("Stopped while the mude 1.0 server was starting");
|
|
417
|
+
this.name = "StartStopped";
|
|
418
|
+
this.serverStopped = serverStopped;
|
|
419
|
+
}
|
|
420
|
+
};
|
|
412
421
|
async function startServer(paths, options) {
|
|
413
422
|
const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
414
423
|
const timeoutMs = options.timeoutMs ?? 18e4;
|
|
415
424
|
const running = readPid(paths);
|
|
416
425
|
if (running && serverAlive(paths, running)) {
|
|
417
|
-
if (options.wait !== false) await untilReady(paths, running, options.port, timeoutMs, sleep);
|
|
426
|
+
if (options.wait !== false) await untilReady(paths, running, options.port, timeoutMs, sleep, options.signal);
|
|
418
427
|
return { pid: running, port: options.port, already: true };
|
|
419
428
|
}
|
|
420
429
|
if (!await portFree(options.port)) {
|
|
@@ -463,12 +472,21 @@ async function startServer(paths, options) {
|
|
|
463
472
|
child.unref();
|
|
464
473
|
writeFileSync2(paths.pid, `${pid}
|
|
465
474
|
`, { mode: 384 });
|
|
466
|
-
if (options.wait !== false)
|
|
475
|
+
if (options.wait !== false) {
|
|
476
|
+
try {
|
|
477
|
+
await untilReady(paths, pid, options.port, timeoutMs, sleep, options.signal);
|
|
478
|
+
} catch (error) {
|
|
479
|
+
if (!(error instanceof StartStopped)) throw error;
|
|
480
|
+
await stopServer(paths);
|
|
481
|
+
throw new StartStopped(true);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
467
484
|
return { pid, port: options.port, already: false };
|
|
468
485
|
}
|
|
469
|
-
async function untilReady(paths, pid, port2, timeoutMs, sleep) {
|
|
486
|
+
async function untilReady(paths, pid, port2, timeoutMs, sleep, signal) {
|
|
470
487
|
const deadline = Date.now() + timeoutMs;
|
|
471
488
|
while (Date.now() < deadline) {
|
|
489
|
+
if (signal?.aborted) throw new StartStopped();
|
|
472
490
|
if ((await health(port2))?.status === "ready") return;
|
|
473
491
|
if (!serverAlive(paths, pid)) {
|
|
474
492
|
rmSync(paths.pid, { force: true });
|
|
@@ -654,6 +672,7 @@ import { open } from "node:fs/promises";
|
|
|
654
672
|
import { homedir } from "node:os";
|
|
655
673
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
656
674
|
import { pipeline } from "node:stream/promises";
|
|
675
|
+
import { setTimeout as wait } from "node:timers/promises";
|
|
657
676
|
var CHECKPOINT_BYTES = 32 * 1024 * 1024;
|
|
658
677
|
var RETRY_WAITS = [2e3, 5e3, 1e4, 2e4, 3e4];
|
|
659
678
|
function huggingFaceToken(env = process.env, home = homedir()) {
|
|
@@ -805,7 +824,8 @@ function writeSaved(part, bytes) {
|
|
|
805
824
|
}
|
|
806
825
|
async function fetchFile(options, file, part, onBytes) {
|
|
807
826
|
const attempts = options.attempts ?? 8;
|
|
808
|
-
const sleep = options.sleep ?? ((ms) =>
|
|
827
|
+
const sleep = options.sleep ?? ((ms) => wait(ms, void 0, { signal: options.signal }).catch(() => {
|
|
828
|
+
}));
|
|
809
829
|
let resumed = false;
|
|
810
830
|
let failures = 0;
|
|
811
831
|
let lastError;
|
|
@@ -817,6 +837,12 @@ async function fetchFile(options, file, part, onBytes) {
|
|
|
817
837
|
return resumed;
|
|
818
838
|
} catch (error) {
|
|
819
839
|
if (error instanceof DownloadError && error.kind !== "network") throw error;
|
|
840
|
+
const code = error.code;
|
|
841
|
+
if (code === "ENOSPC" || code === "EDQUOT") {
|
|
842
|
+
throw new Error(
|
|
843
|
+
`The disk is full: couldn't write ${file.path}. Free up some space, then run mu judge mude install to resume`
|
|
844
|
+
);
|
|
845
|
+
}
|
|
820
846
|
lastError = error;
|
|
821
847
|
failures = resumeFrom(part, file.size, options.checkpointBytes) > before ? 1 : failures + 1;
|
|
822
848
|
if (failures >= attempts) break;
|
|
@@ -825,7 +851,7 @@ async function fetchFile(options, file, part, onBytes) {
|
|
|
825
851
|
}
|
|
826
852
|
const reason = lastError instanceof Error ? lastError.message : String(lastError);
|
|
827
853
|
throw new DownloadError(
|
|
828
|
-
`${file.path} could not be downloaded (${reason})
|
|
854
|
+
`${file.path} could not be downloaded (${reason}). Run mu judge mude install again to resume`,
|
|
829
855
|
"network"
|
|
830
856
|
);
|
|
831
857
|
}
|
|
@@ -855,7 +881,7 @@ async function fetchOnce(options, file, part, onBytes) {
|
|
|
855
881
|
});
|
|
856
882
|
if (response.status === 401 || response.status === 403 || response.status === 404) {
|
|
857
883
|
throw new DownloadError(
|
|
858
|
-
`Hugging Face
|
|
884
|
+
`Hugging Face returned ${response.status} for ${file.path}: the model isn't public yet, or this machine's token can't access it`,
|
|
859
885
|
"access"
|
|
860
886
|
);
|
|
861
887
|
}
|
|
@@ -926,7 +952,8 @@ function mudePaths(muHome) {
|
|
|
926
952
|
pid: join4(root, "server.pid"),
|
|
927
953
|
log: join4(root, "server.log"),
|
|
928
954
|
state: join4(root, "state.json"),
|
|
929
|
-
install: join4(root, "install.json")
|
|
955
|
+
install: join4(root, "install.json"),
|
|
956
|
+
installLock: join4(root, "install.lock")
|
|
930
957
|
};
|
|
931
958
|
}
|
|
932
959
|
|
|
@@ -993,9 +1020,20 @@ async function downloadUv(paths, options = {}) {
|
|
|
993
1020
|
mkdirSync4(paths.tools, { recursive: true });
|
|
994
1021
|
const archive = "uv.tar.gz";
|
|
995
1022
|
options.onLine?.(`Downloading uv ${UV_RELEASE.version}`);
|
|
996
|
-
const
|
|
997
|
-
|
|
998
|
-
|
|
1023
|
+
const timeoutMs = options.timeoutMs ?? 3e5;
|
|
1024
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
1025
|
+
let bytes;
|
|
1026
|
+
try {
|
|
1027
|
+
const response = await (options.fetch ?? fetch)(UV_RELEASE.url, {
|
|
1028
|
+
redirect: "follow",
|
|
1029
|
+
signal: options.signal ? AbortSignal.any([options.signal, timeout]) : timeout
|
|
1030
|
+
});
|
|
1031
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
1032
|
+
bytes = Buffer.from(await response.arrayBuffer());
|
|
1033
|
+
} catch (error) {
|
|
1034
|
+
const reason = timeout.aborted ? `no answer in ${timeoutMs / 1e3} s` : error instanceof Error ? error.message : String(error);
|
|
1035
|
+
throw new Error(`uv could not be downloaded from GitHub (${reason}): run mu judge mude install again`);
|
|
1036
|
+
}
|
|
999
1037
|
const actual = createHash2("sha256").update(bytes).digest("hex");
|
|
1000
1038
|
if (actual !== UV_RELEASE.sha256)
|
|
1001
1039
|
throw new Error("The downloaded uv does not match its pinned sha256; nothing was installed");
|
|
@@ -1010,7 +1048,7 @@ async function downloadUv(paths, options = {}) {
|
|
|
1010
1048
|
]);
|
|
1011
1049
|
rmSync3(join5(paths.tools, archive), { force: true });
|
|
1012
1050
|
if (unpacked.code !== 0 || !executable(join5(paths.tools, "uv")))
|
|
1013
|
-
throw new Error(`
|
|
1051
|
+
throw new Error(`Couldn't unpack uv: ${unpacked.output}`);
|
|
1014
1052
|
return join5(paths.tools, "uv");
|
|
1015
1053
|
}
|
|
1016
1054
|
function lockDigest(lockFile) {
|
|
@@ -1032,7 +1070,7 @@ async function ensureVenv(paths, lockFile, uv, options = {}) {
|
|
|
1032
1070
|
env,
|
|
1033
1071
|
onLine: options.onLine
|
|
1034
1072
|
});
|
|
1035
|
-
if (made.code !== 0) throw new Error(`
|
|
1073
|
+
if (made.code !== 0) throw new Error(`Couldn't create the Python environment (uv venv):
|
|
1036
1074
|
${made.output}`);
|
|
1037
1075
|
const installed2 = await run(
|
|
1038
1076
|
uv,
|
|
@@ -1051,7 +1089,7 @@ ${made.output}`);
|
|
|
1051
1089
|
{ env, onLine: options.onLine }
|
|
1052
1090
|
);
|
|
1053
1091
|
if (installed2.code !== 0)
|
|
1054
|
-
throw new Error(`mude's Python packages
|
|
1092
|
+
throw new Error(`Couldn't install mude's Python packages (uv pip install):
|
|
1055
1093
|
${installed2.output}`);
|
|
1056
1094
|
writeFileSync5(paths.venvLock, `${lockDigest(lockFile)}
|
|
1057
1095
|
`);
|
|
@@ -1062,7 +1100,7 @@ var USAGE = `mu judge mude: mude 1.0, mu's own decision model optimized for codi
|
|
|
1062
1100
|
(Apple silicon, macOS 14 or later)
|
|
1063
1101
|
|
|
1064
1102
|
mu judge mude install [--local-only] download it (${(MUDE_BYTES / 1e9).toFixed(1)} GB), set it up, start it and make it the judge;
|
|
1065
|
-
after a stop (Ctrl-C, a lost connection, a power cut), it
|
|
1103
|
+
after a stop (Ctrl-C, a lost connection, a power cut), it picks up where it left off
|
|
1066
1104
|
mu judge mude enable [--local-only | --with-fallback]
|
|
1067
1105
|
make it the judge again, or change what happens when it can't answer
|
|
1068
1106
|
mu judge mude disable back to the judges from before, and stop its server
|
|
@@ -1132,7 +1170,7 @@ function describe(status2) {
|
|
|
1132
1170
|
`;
|
|
1133
1171
|
}
|
|
1134
1172
|
lines.push(
|
|
1135
|
-
`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
|
|
1173
|
+
`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"}`
|
|
1136
1174
|
);
|
|
1137
1175
|
if (status2.installed && !status2.pythonReady) lines.push("python not set up: mu judge mude install");
|
|
1138
1176
|
lines.push(
|
|
@@ -1197,6 +1235,41 @@ function installLocalOnly(ctx, args) {
|
|
|
1197
1235
|
return false;
|
|
1198
1236
|
}
|
|
1199
1237
|
}
|
|
1238
|
+
function startedAt(pid) {
|
|
1239
|
+
try {
|
|
1240
|
+
return execFileSync2("/bin/ps", ["-p", String(pid), "-o", "lstart="], { encoding: "utf8" }).trim() || void 0;
|
|
1241
|
+
} catch {
|
|
1242
|
+
return void 0;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
function holdInstall(paths) {
|
|
1246
|
+
mkdirSync5(paths.root, { recursive: true });
|
|
1247
|
+
const mine = `${JSON.stringify({ pid: process.pid, started: startedAt(process.pid) ?? "" })}
|
|
1248
|
+
`;
|
|
1249
|
+
try {
|
|
1250
|
+
writeFileSync6(paths.installLock, mine, { flag: "wx" });
|
|
1251
|
+
} catch (error) {
|
|
1252
|
+
if (error.code !== "EEXIST") throw error;
|
|
1253
|
+
let holder = {};
|
|
1254
|
+
try {
|
|
1255
|
+
holder = JSON.parse(readFileSync6(paths.installLock, "utf8"));
|
|
1256
|
+
} catch {
|
|
1257
|
+
}
|
|
1258
|
+
const pid = typeof holder.pid === "number" ? holder.pid : 0;
|
|
1259
|
+
if (pid > 0 && pid !== process.pid && holder.started && startedAt(pid) === holder.started) {
|
|
1260
|
+
throw new Error(
|
|
1261
|
+
`Another mude 1.0 install is running (process ${pid}). Wait for it to finish, or stop it, then run mu judge mude install again`
|
|
1262
|
+
);
|
|
1263
|
+
}
|
|
1264
|
+
writeFileSync6(paths.installLock, mine);
|
|
1265
|
+
}
|
|
1266
|
+
return () => {
|
|
1267
|
+
try {
|
|
1268
|
+
if (readFileSync6(paths.installLock, "utf8") === mine) rmSync4(paths.installLock, { force: true });
|
|
1269
|
+
} catch {
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1200
1273
|
function installed(ctx) {
|
|
1201
1274
|
return missingFiles(ctx.paths.model, MUDE_FILES).length === 0;
|
|
1202
1275
|
}
|
|
@@ -1248,16 +1321,17 @@ async function ensureInstalled(ctx, out) {
|
|
|
1248
1321
|
const missing = missingFiles(ctx.paths.model, MUDE_FILES);
|
|
1249
1322
|
if (missing.length > 0) {
|
|
1250
1323
|
const need = missing.reduce((sum, file) => sum + file.size, 0);
|
|
1324
|
+
const already = downloadedBytes(ctx.paths.model, MUDE_FILES);
|
|
1251
1325
|
const free = freeBytes(ctx.paths.root);
|
|
1252
|
-
|
|
1326
|
+
const room = MUDE_BYTES - already + 2e9;
|
|
1327
|
+
if (free !== void 0 && free < room) {
|
|
1253
1328
|
throw new Error(
|
|
1254
|
-
`Not enough free disk space: mude 1.0 needs about ${(
|
|
1329
|
+
`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`
|
|
1255
1330
|
);
|
|
1256
1331
|
}
|
|
1257
|
-
const already = downloadedBytes(ctx.paths.model, MUDE_FILES);
|
|
1258
1332
|
out.step(
|
|
1259
1333
|
"download",
|
|
1260
|
-
already > 0 ? `
|
|
1334
|
+
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`
|
|
1261
1335
|
);
|
|
1262
1336
|
try {
|
|
1263
1337
|
await downloadFiles({
|
|
@@ -1271,7 +1345,7 @@ async function ensureInstalled(ctx, out) {
|
|
|
1271
1345
|
} catch (error) {
|
|
1272
1346
|
if (error instanceof DownloadError && error.kind === "aborted") {
|
|
1273
1347
|
throw new DownloadError(
|
|
1274
|
-
`Stopped with ${gb(downloadedBytes(ctx.paths.model, MUDE_FILES))} of ${gb(MUDE_BYTES)} GB downloaded
|
|
1348
|
+
`Stopped with ${gb(downloadedBytes(ctx.paths.model, MUDE_FILES))} of ${gb(MUDE_BYTES)} GB downloaded. Run mu judge mude install to resume`,
|
|
1275
1349
|
"aborted"
|
|
1276
1350
|
);
|
|
1277
1351
|
}
|
|
@@ -1280,9 +1354,19 @@ async function ensureInstalled(ctx, out) {
|
|
|
1280
1354
|
}
|
|
1281
1355
|
if (!venvReady(ctx.paths, ctx.lockFile)) {
|
|
1282
1356
|
out.step("python", "Setting up the Python environment for mude 1.0 (MLX, transformers; about 1 GB, once)");
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1357
|
+
try {
|
|
1358
|
+
let uv = findUv(ctx.paths, ctx.env);
|
|
1359
|
+
if (!uv) uv = await downloadUv(ctx.paths, { onLine: (line) => out.line(line), signal: ctx.signal });
|
|
1360
|
+
await ensureVenv(ctx.paths, ctx.lockFile, uv, { onLine: (line) => out.line(line), env: ctx.env });
|
|
1361
|
+
} catch (error) {
|
|
1362
|
+
if (ctx.signal?.aborted) {
|
|
1363
|
+
throw new DownloadError(
|
|
1364
|
+
"Stopped while setting up the Python environment. Run mu judge mude install to resume",
|
|
1365
|
+
"aborted"
|
|
1366
|
+
);
|
|
1367
|
+
}
|
|
1368
|
+
throw error;
|
|
1369
|
+
}
|
|
1286
1370
|
}
|
|
1287
1371
|
}
|
|
1288
1372
|
function requireInstalled(ctx) {
|
|
@@ -1290,13 +1374,23 @@ function requireInstalled(ctx) {
|
|
|
1290
1374
|
throw new Error("mude 1.0 is not installed here yet: mu judge mude install");
|
|
1291
1375
|
}
|
|
1292
1376
|
}
|
|
1293
|
-
async function start(ctx, out,
|
|
1377
|
+
async function start(ctx, out, command, wait2 = true) {
|
|
1294
1378
|
const at = await port(ctx);
|
|
1295
1379
|
out.step(
|
|
1296
1380
|
"start",
|
|
1297
1381
|
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)`
|
|
1298
1382
|
);
|
|
1299
|
-
|
|
1383
|
+
try {
|
|
1384
|
+
await startServer(ctx.paths, { port: at, key: key(ctx), wait: wait2, signal: ctx.signal });
|
|
1385
|
+
} catch (error) {
|
|
1386
|
+
if (error instanceof StartStopped) {
|
|
1387
|
+
throw new DownloadError(
|
|
1388
|
+
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`,
|
|
1389
|
+
"aborted"
|
|
1390
|
+
);
|
|
1391
|
+
}
|
|
1392
|
+
throw error;
|
|
1393
|
+
}
|
|
1300
1394
|
return at;
|
|
1301
1395
|
}
|
|
1302
1396
|
function enable(ctx, out, at, localOnly) {
|
|
@@ -1334,20 +1428,24 @@ async function main(argv, ctx, write, tty = false) {
|
|
|
1334
1428
|
switch (command) {
|
|
1335
1429
|
case "install": {
|
|
1336
1430
|
const localOnly = installLocalOnly(ctx, args);
|
|
1337
|
-
|
|
1338
|
-
|
|
1431
|
+
const release2 = holdInstall(ctx.paths);
|
|
1432
|
+
try {
|
|
1433
|
+
writeFileSync6(ctx.paths.install, `${JSON.stringify({ localOnly })}
|
|
1339
1434
|
`);
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1435
|
+
await ensureInstalled(ctx, out);
|
|
1436
|
+
const at = await start(ctx, out, "install");
|
|
1437
|
+
enable(ctx, out, at, localOnly);
|
|
1438
|
+
rmSync4(ctx.paths.install, { force: true });
|
|
1439
|
+
} finally {
|
|
1440
|
+
release2();
|
|
1441
|
+
}
|
|
1344
1442
|
break;
|
|
1345
1443
|
}
|
|
1346
1444
|
case "enable": {
|
|
1347
1445
|
requireInstalled(ctx);
|
|
1348
1446
|
const current = judgeSetting(ctx.files);
|
|
1349
1447
|
const localOnly = flag("--local-only") ? true : flag("--with-fallback") ? false : current.enabled && current.localOnly;
|
|
1350
|
-
const at = await start(ctx, out);
|
|
1448
|
+
const at = await start(ctx, out, "enable");
|
|
1351
1449
|
enable(ctx, out, at, localOnly);
|
|
1352
1450
|
break;
|
|
1353
1451
|
}
|
|
@@ -1367,7 +1465,7 @@ async function main(argv, ctx, write, tty = false) {
|
|
|
1367
1465
|
}
|
|
1368
1466
|
case "start": {
|
|
1369
1467
|
requireInstalled(ctx);
|
|
1370
|
-
await start(ctx, out, !flag("--no-wait"));
|
|
1468
|
+
await start(ctx, out, "start", !flag("--no-wait"));
|
|
1371
1469
|
break;
|
|
1372
1470
|
}
|
|
1373
1471
|
case "stop": {
|
package/judge/dist/setup.js
CHANGED
|
@@ -154,21 +154,21 @@ function readEnvEntries(text) {
|
|
|
154
154
|
}
|
|
155
155
|
let body = rest.slice(1);
|
|
156
156
|
let end = closingQuote(body, quote);
|
|
157
|
-
const
|
|
157
|
+
const startedAt2 = index;
|
|
158
158
|
while (end < 0 && index + 1 < lines.length) {
|
|
159
159
|
body += `
|
|
160
160
|
${lines[++index]}`;
|
|
161
161
|
end = closingQuote(body, quote);
|
|
162
162
|
}
|
|
163
163
|
if (end < 0) {
|
|
164
|
-
index =
|
|
164
|
+
index = startedAt2;
|
|
165
165
|
continue;
|
|
166
166
|
}
|
|
167
167
|
const value = body.slice(0, end);
|
|
168
168
|
entries.push({
|
|
169
169
|
name,
|
|
170
170
|
value: quote === '"' ? value.replace(/\\(["\\$`])/g, "$1") : value,
|
|
171
|
-
first:
|
|
171
|
+
first: startedAt2,
|
|
172
172
|
last: index
|
|
173
173
|
});
|
|
174
174
|
}
|
|
@@ -503,12 +503,21 @@ function tail(file, lines = 12) {
|
|
|
503
503
|
return "";
|
|
504
504
|
}
|
|
505
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
|
+
};
|
|
506
515
|
async function startServer(paths, options) {
|
|
507
516
|
const sleep2 = options.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
|
|
508
517
|
const timeoutMs = options.timeoutMs ?? 18e4;
|
|
509
518
|
const running = readPid(paths);
|
|
510
519
|
if (running && serverAlive(paths, running)) {
|
|
511
|
-
if (options.wait !== false) await untilReady(paths, running, options.port, timeoutMs, sleep2);
|
|
520
|
+
if (options.wait !== false) await untilReady(paths, running, options.port, timeoutMs, sleep2, options.signal);
|
|
512
521
|
return { pid: running, port: options.port, already: true };
|
|
513
522
|
}
|
|
514
523
|
if (!await portFree(options.port)) {
|
|
@@ -557,12 +566,21 @@ async function startServer(paths, options) {
|
|
|
557
566
|
child.unref();
|
|
558
567
|
writeFileSync2(paths.pid, `${pid}
|
|
559
568
|
`, { mode: 384 });
|
|
560
|
-
if (options.wait !== false)
|
|
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
|
+
}
|
|
561
578
|
return { pid, port: options.port, already: false };
|
|
562
579
|
}
|
|
563
|
-
async function untilReady(paths, pid, port2, timeoutMs, sleep2) {
|
|
580
|
+
async function untilReady(paths, pid, port2, timeoutMs, sleep2, signal) {
|
|
564
581
|
const deadline = Date.now() + timeoutMs;
|
|
565
582
|
while (Date.now() < deadline) {
|
|
583
|
+
if (signal?.aborted) throw new StartStopped();
|
|
566
584
|
if ((await health(port2))?.status === "ready") return;
|
|
567
585
|
if (!serverAlive(paths, pid)) {
|
|
568
586
|
rmSync(paths.pid, { force: true });
|
|
@@ -748,6 +766,7 @@ import { open } from "node:fs/promises";
|
|
|
748
766
|
import { homedir as homedir2 } from "node:os";
|
|
749
767
|
import { dirname as dirname2, join as join4 } from "node:path";
|
|
750
768
|
import { pipeline } from "node:stream/promises";
|
|
769
|
+
import { setTimeout as wait } from "node:timers/promises";
|
|
751
770
|
var CHECKPOINT_BYTES = 32 * 1024 * 1024;
|
|
752
771
|
var RETRY_WAITS = [2e3, 5e3, 1e4, 2e4, 3e4];
|
|
753
772
|
function huggingFaceToken(env = process.env, home2 = homedir2()) {
|
|
@@ -899,7 +918,8 @@ function writeSaved(part, bytes) {
|
|
|
899
918
|
}
|
|
900
919
|
async function fetchFile(options, file, part, onBytes) {
|
|
901
920
|
const attempts = options.attempts ?? 8;
|
|
902
|
-
const sleep2 = options.sleep ?? ((ms) =>
|
|
921
|
+
const sleep2 = options.sleep ?? ((ms) => wait(ms, void 0, { signal: options.signal }).catch(() => {
|
|
922
|
+
}));
|
|
903
923
|
let resumed = false;
|
|
904
924
|
let failures = 0;
|
|
905
925
|
let lastError;
|
|
@@ -911,6 +931,12 @@ async function fetchFile(options, file, part, onBytes) {
|
|
|
911
931
|
return resumed;
|
|
912
932
|
} catch (error) {
|
|
913
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
|
+
}
|
|
914
940
|
lastError = error;
|
|
915
941
|
failures = resumeFrom(part, file.size, options.checkpointBytes) > before ? 1 : failures + 1;
|
|
916
942
|
if (failures >= attempts) break;
|
|
@@ -919,7 +945,7 @@ async function fetchFile(options, file, part, onBytes) {
|
|
|
919
945
|
}
|
|
920
946
|
const reason = lastError instanceof Error ? lastError.message : String(lastError);
|
|
921
947
|
throw new DownloadError(
|
|
922
|
-
`${file.path} could not be downloaded (${reason})
|
|
948
|
+
`${file.path} could not be downloaded (${reason}). Run mu judge mude install again to resume`,
|
|
923
949
|
"network"
|
|
924
950
|
);
|
|
925
951
|
}
|
|
@@ -949,7 +975,7 @@ async function fetchOnce(options, file, part, onBytes) {
|
|
|
949
975
|
});
|
|
950
976
|
if (response.status === 401 || response.status === 403 || response.status === 404) {
|
|
951
977
|
throw new DownloadError(
|
|
952
|
-
`Hugging Face
|
|
978
|
+
`Hugging Face returned ${response.status} for ${file.path}: the model isn't public yet, or this machine's token can't access it`,
|
|
953
979
|
"access"
|
|
954
980
|
);
|
|
955
981
|
}
|
|
@@ -1020,7 +1046,8 @@ function mudePaths(muHome2) {
|
|
|
1020
1046
|
pid: join5(root, "server.pid"),
|
|
1021
1047
|
log: join5(root, "server.log"),
|
|
1022
1048
|
state: join5(root, "state.json"),
|
|
1023
|
-
install: join5(root, "install.json")
|
|
1049
|
+
install: join5(root, "install.json"),
|
|
1050
|
+
installLock: join5(root, "install.lock")
|
|
1024
1051
|
};
|
|
1025
1052
|
}
|
|
1026
1053
|
|
|
@@ -1087,9 +1114,20 @@ async function downloadUv(paths, options = {}) {
|
|
|
1087
1114
|
mkdirSync5(paths.tools, { recursive: true });
|
|
1088
1115
|
const archive = "uv.tar.gz";
|
|
1089
1116
|
options.onLine?.(`Downloading uv ${UV_RELEASE.version}`);
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1092
|
-
|
|
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
|
+
}
|
|
1093
1131
|
const actual = createHash2("sha256").update(bytes).digest("hex");
|
|
1094
1132
|
if (actual !== UV_RELEASE.sha256)
|
|
1095
1133
|
throw new Error("The downloaded uv does not match its pinned sha256; nothing was installed");
|
|
@@ -1104,7 +1142,7 @@ async function downloadUv(paths, options = {}) {
|
|
|
1104
1142
|
]);
|
|
1105
1143
|
rmSync3(join6(paths.tools, archive), { force: true });
|
|
1106
1144
|
if (unpacked.code !== 0 || !executable(join6(paths.tools, "uv")))
|
|
1107
|
-
throw new Error(`
|
|
1145
|
+
throw new Error(`Couldn't unpack uv: ${unpacked.output}`);
|
|
1108
1146
|
return join6(paths.tools, "uv");
|
|
1109
1147
|
}
|
|
1110
1148
|
function lockDigest(lockFile) {
|
|
@@ -1126,7 +1164,7 @@ async function ensureVenv(paths, lockFile, uv, options = {}) {
|
|
|
1126
1164
|
env,
|
|
1127
1165
|
onLine: options.onLine
|
|
1128
1166
|
});
|
|
1129
|
-
if (made.code !== 0) throw new Error(`
|
|
1167
|
+
if (made.code !== 0) throw new Error(`Couldn't create the Python environment (uv venv):
|
|
1130
1168
|
${made.output}`);
|
|
1131
1169
|
const installed2 = await run(
|
|
1132
1170
|
uv,
|
|
@@ -1145,7 +1183,7 @@ ${made.output}`);
|
|
|
1145
1183
|
{ env, onLine: options.onLine }
|
|
1146
1184
|
);
|
|
1147
1185
|
if (installed2.code !== 0)
|
|
1148
|
-
throw new Error(`mude's Python packages
|
|
1186
|
+
throw new Error(`Couldn't install mude's Python packages (uv pip install):
|
|
1149
1187
|
${installed2.output}`);
|
|
1150
1188
|
writeFileSync5(paths.venvLock, `${lockDigest(lockFile)}
|
|
1151
1189
|
`);
|
|
@@ -1156,7 +1194,7 @@ var USAGE = `mu judge mude: mude 1.0, mu's own decision model optimized for codi
|
|
|
1156
1194
|
(Apple silicon, macOS 14 or later)
|
|
1157
1195
|
|
|
1158
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;
|
|
1159
|
-
after a stop (Ctrl-C, a lost connection, a power cut), it
|
|
1197
|
+
after a stop (Ctrl-C, a lost connection, a power cut), it picks up where it left off
|
|
1160
1198
|
mu judge mude enable [--local-only | --with-fallback]
|
|
1161
1199
|
make it the judge again, or change what happens when it can't answer
|
|
1162
1200
|
mu judge mude disable back to the judges from before, and stop its server
|
|
@@ -1226,7 +1264,7 @@ function describe(status2) {
|
|
|
1226
1264
|
`;
|
|
1227
1265
|
}
|
|
1228
1266
|
lines.push(
|
|
1229
|
-
`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
|
|
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"}`
|
|
1230
1268
|
);
|
|
1231
1269
|
if (status2.installed && !status2.pythonReady) lines.push("python not set up: mu judge mude install");
|
|
1232
1270
|
lines.push(
|
|
@@ -1291,6 +1329,41 @@ function installLocalOnly(ctx, args) {
|
|
|
1291
1329
|
return false;
|
|
1292
1330
|
}
|
|
1293
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
|
+
}
|
|
1294
1367
|
function installed(ctx) {
|
|
1295
1368
|
return missingFiles(ctx.paths.model, MUDE_FILES).length === 0;
|
|
1296
1369
|
}
|
|
@@ -1342,16 +1415,17 @@ async function ensureInstalled(ctx, out) {
|
|
|
1342
1415
|
const missing = missingFiles(ctx.paths.model, MUDE_FILES);
|
|
1343
1416
|
if (missing.length > 0) {
|
|
1344
1417
|
const need = missing.reduce((sum, file) => sum + file.size, 0);
|
|
1418
|
+
const already = downloadedBytes(ctx.paths.model, MUDE_FILES);
|
|
1345
1419
|
const free = freeBytes(ctx.paths.root);
|
|
1346
|
-
|
|
1420
|
+
const room = MUDE_BYTES - already + 2e9;
|
|
1421
|
+
if (free !== void 0 && free < room) {
|
|
1347
1422
|
throw new Error(
|
|
1348
|
-
`Not enough free disk space: mude 1.0 needs about ${(
|
|
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`
|
|
1349
1424
|
);
|
|
1350
1425
|
}
|
|
1351
|
-
const already = downloadedBytes(ctx.paths.model, MUDE_FILES);
|
|
1352
1426
|
out.step(
|
|
1353
1427
|
"download",
|
|
1354
|
-
already > 0 ? `
|
|
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`
|
|
1355
1429
|
);
|
|
1356
1430
|
try {
|
|
1357
1431
|
await downloadFiles({
|
|
@@ -1365,7 +1439,7 @@ async function ensureInstalled(ctx, out) {
|
|
|
1365
1439
|
} catch (error) {
|
|
1366
1440
|
if (error instanceof DownloadError && error.kind === "aborted") {
|
|
1367
1441
|
throw new DownloadError(
|
|
1368
|
-
`Stopped with ${gb(downloadedBytes(ctx.paths.model, MUDE_FILES))} of ${gb(MUDE_BYTES)} GB downloaded
|
|
1442
|
+
`Stopped with ${gb(downloadedBytes(ctx.paths.model, MUDE_FILES))} of ${gb(MUDE_BYTES)} GB downloaded. Run mu judge mude install to resume`,
|
|
1369
1443
|
"aborted"
|
|
1370
1444
|
);
|
|
1371
1445
|
}
|
|
@@ -1374,9 +1448,19 @@ async function ensureInstalled(ctx, out) {
|
|
|
1374
1448
|
}
|
|
1375
1449
|
if (!venvReady(ctx.paths, ctx.lockFile)) {
|
|
1376
1450
|
out.step("python", "Setting up the Python environment for mude 1.0 (MLX, transformers; about 1 GB, once)");
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
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
|
+
}
|
|
1380
1464
|
}
|
|
1381
1465
|
}
|
|
1382
1466
|
function requireInstalled(ctx) {
|
|
@@ -1384,13 +1468,23 @@ function requireInstalled(ctx) {
|
|
|
1384
1468
|
throw new Error("mude 1.0 is not installed here yet: mu judge mude install");
|
|
1385
1469
|
}
|
|
1386
1470
|
}
|
|
1387
|
-
async function start(ctx, out,
|
|
1471
|
+
async function start(ctx, out, command, wait2 = true) {
|
|
1388
1472
|
const at = await port(ctx);
|
|
1389
1473
|
out.step(
|
|
1390
1474
|
"start",
|
|
1391
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)`
|
|
1392
1476
|
);
|
|
1393
|
-
|
|
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
|
+
}
|
|
1394
1488
|
return at;
|
|
1395
1489
|
}
|
|
1396
1490
|
function enable(ctx, out, at, localOnly) {
|
|
@@ -1428,20 +1522,24 @@ async function main(argv, ctx, write, tty = false) {
|
|
|
1428
1522
|
switch (command) {
|
|
1429
1523
|
case "install": {
|
|
1430
1524
|
const localOnly = installLocalOnly(ctx, args);
|
|
1431
|
-
|
|
1432
|
-
|
|
1525
|
+
const release2 = holdInstall(ctx.paths);
|
|
1526
|
+
try {
|
|
1527
|
+
writeFileSync6(ctx.paths.install, `${JSON.stringify({ localOnly })}
|
|
1433
1528
|
`);
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
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
|
+
}
|
|
1438
1536
|
break;
|
|
1439
1537
|
}
|
|
1440
1538
|
case "enable": {
|
|
1441
1539
|
requireInstalled(ctx);
|
|
1442
1540
|
const current = judgeSetting(ctx.files);
|
|
1443
1541
|
const localOnly = flag("--local-only") ? true : flag("--with-fallback") ? false : current.enabled && current.localOnly;
|
|
1444
|
-
const at = await start(ctx, out);
|
|
1542
|
+
const at = await start(ctx, out, "enable");
|
|
1445
1543
|
enable(ctx, out, at, localOnly);
|
|
1446
1544
|
break;
|
|
1447
1545
|
}
|
|
@@ -1461,7 +1559,7 @@ async function main(argv, ctx, write, tty = false) {
|
|
|
1461
1559
|
}
|
|
1462
1560
|
case "start": {
|
|
1463
1561
|
requireInstalled(ctx);
|
|
1464
|
-
await start(ctx, out, !flag("--no-wait"));
|
|
1562
|
+
await start(ctx, out, "start", !flag("--no-wait"));
|
|
1465
1563
|
break;
|
|
1466
1564
|
}
|
|
1467
1565
|
case "stop": {
|
|
@@ -2541,7 +2639,7 @@ var Setup = class {
|
|
|
2541
2639
|
this.io.warn(
|
|
2542
2640
|
this.t({
|
|
2543
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",
|
|
2544
|
-
en: "mude 1.0 isn't installed yet, so Jev is the judge until it is. Run mu judge mude install to
|
|
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."
|
|
2545
2643
|
})
|
|
2546
2644
|
);
|
|
2547
2645
|
return this.scripted ? SETUP_EXIT.failed : SETUP_EXIT.done;
|
|
@@ -2604,7 +2702,7 @@ var Setup = class {
|
|
|
2604
2702
|
this.io.say(
|
|
2605
2703
|
this.t({
|
|
2606
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`,
|
|
2607
|
-
en: `
|
|
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}.`
|
|
2608
2706
|
})
|
|
2609
2707
|
);
|
|
2610
2708
|
return "subscription";
|
package/judge/manifest.json
CHANGED
|
@@ -270,7 +270,7 @@
|
|
|
270
270
|
},
|
|
271
271
|
"summary": {
|
|
272
272
|
"zh": "任务进行中你又发来一句话:判断是要立刻打断,还是等这一步做完再处理。默认是影子模式:判定只记录,消息按你按的键投递,不用等判定。",
|
|
273
|
-
"en": "For a message that arrives while the agent is working: interrupt now, or handle it after the current step. In shadow by default: the verdict is only logged, and the
|
|
273
|
+
"en": "For a message that arrives while the agent is working: interrupt now, or handle it after the current step. In shadow by default: the verdict is only logged, and the key you pressed decides what happens to the message, without waiting for the verdict.",
|
|
274
274
|
"zh-TW": "代理工作時你又傳來一則訊息:判斷是要立刻打斷,還是等這一步做完再處理。預設為影子模式:判定只記錄下來,訊息照你按的鍵送出,不等判定。",
|
|
275
275
|
"ja-JP": "エージェントの作業中に届いたメッセージについて、すぐに中断するか、現在のステップが終わってから対応するかを判定します。既定ではシャドーモードです。判定は記録するだけで、メッセージは判定を待たずに、押したキーのとおりに届けます。",
|
|
276
276
|
"ko-KR": "에이전트가 작업하는 중에 메시지가 도착하면, 지금 바로 작업을 중단할지 현재 단계를 마친 뒤 처리할지 판정합니다. 기본값은 섀도 모드입니다. 판정은 기록만 하고, 메시지는 판정을 기다리지 않고 누른 키대로 전달됩니다.",
|
|
@@ -1382,7 +1382,7 @@
|
|
|
1382
1382
|
},
|
|
1383
1383
|
"summary": {
|
|
1384
1384
|
"zh": "上下文预算、后台命令结束等事件出现时,判断该现在告诉模型、稍后再说,还是不必说。默认是影子模式:判定只记录,事件按各自的规则投递,后台命令结束和上下文预算都是立刻告知。",
|
|
1385
|
-
"en": "For events such as the context budget or a background job's end: tell the model now, later or not at all. In shadow by default: the verdict is only logged and each event is delivered by its own rule
|
|
1385
|
+
"en": "For events such as the context budget or a background job's end: tell the model now, later or not at all. In shadow by default: the verdict is only logged, and each event is delivered by its own rule: right away for a job's end and the context budget.",
|
|
1386
1386
|
"zh-TW": "上下文預算、背景工作結束這類事件出現時,判斷該現在告訴模型、晚點再說,還是不必說。預設為影子模式:判定只記錄下來,事件各按自己的規則送達;背景工作結束和上下文預算都是立刻告知。",
|
|
1387
1387
|
"ja-JP": "コンテキスト予算やバックグラウンドジョブの終了などのイベントを、モデルにすぐ伝えるか、後で伝えるか、伝えないかを判定します。既定ではシャドーモードです。判定は記録するだけで、各イベントはそれぞれのルールで届けます。ジョブの終了とコンテキスト予算の場合は、すぐに届けます。",
|
|
1388
1388
|
"ko-KR": "컨텍스트 예산이나 백그라운드 작업 종료 같은 이벤트를 모델에게 지금 알릴지, 나중에 알릴지, 알리지 않을지 정합니다. 기본값은 섀도 모드입니다. 판정은 기록만 하고 각 이벤트는 자체 규칙에 따라 전달되는데, 작업 종료와 컨텍스트 예산은 바로 알립니다.",
|
|
@@ -1969,7 +1969,7 @@
|
|
|
1969
1969
|
},
|
|
1970
1970
|
"help": {
|
|
1971
1971
|
"zh": "子代理隐藏主会话隐藏的技能,不再问判定器。主会话还没定下来时,子代理照旧自己问。",
|
|
1972
|
-
"en": "A subagent hides the skills this session hides and doesn't ask the judge again.
|
|
1972
|
+
"en": "A subagent hides the skills this session hides and doesn't ask the judge again. Until this session has decided, a subagent asks the judge itself, as before.",
|
|
1973
1973
|
"zh-TW": "子代理隱藏主對話隱藏的技能,不再問判定器。主對話還沒決定時,子代理照舊自己問。",
|
|
1974
1974
|
"ja-JP": "サブエージェントは、このセッションで非表示にしたスキルを同じく非表示にし、判定器に改めて問い合わせません。このセッションの選択がまだ決まっていないうちは、サブエージェントはこれまでどおり自分で問い合わせます。",
|
|
1975
1975
|
"ko-KR": "하위 에이전트는 이 세션에서 숨긴 스킬을 똑같이 숨기고, 판정기에 다시 묻지 않습니다. 이 세션의 선택이 아직 정해지지 않았다면 하위 에이전트는 전처럼 직접 묻습니다.",
|
|
@@ -2135,7 +2135,7 @@
|
|
|
2135
2135
|
},
|
|
2136
2136
|
"help": {
|
|
2137
2137
|
"zh": "按名字写。这些在一次性运行里几乎没人用,却每次请求都要带上。用命令点名的(比如 /hive)照样保留;清空就全带上。",
|
|
2138
|
-
"en": "
|
|
2138
|
+
"en": "List them by name. One-shot runs almost never use these, yet every request carries them. Anything you call by its command (such as /hive) stays in. Empty keeps them all.",
|
|
2139
2139
|
"zh-TW": "依名稱填寫。單次執行幾乎用不到它們,卻每次請求都要帶上。用指令點名的(例如 /hive)照樣保留。留空則全部保留。",
|
|
2140
2140
|
"ja-JP": "名前で指定します。単発実行ではほとんど使われないのに、どれもリクエストのたびに含まれます。コマンドで呼び出したもの(/hive など)は残ります。空欄の場合は、すべて残します。",
|
|
2141
2141
|
"ko-KR": "이름으로 지정합니다. 단발 실행에서는 거의 쓰이지 않는데도 요청마다 들어갑니다. 명령으로 직접 부른 것(예: /hive)은 남습니다. 비워 두면 모두 남깁니다.",
|
|
@@ -2205,7 +2205,7 @@
|
|
|
2205
2205
|
},
|
|
2206
2206
|
"help": {
|
|
2207
2207
|
"zh": "子代理打开主会话已经打开的能力,不再问判定器;主会话隐藏的照样隐藏。主会话还没选过时,子代理照旧自己问。",
|
|
2208
|
-
"en": "A subagent opens the capabilities this session has open and doesn't ask the judge again; what's hidden here stays hidden there.
|
|
2208
|
+
"en": "A subagent opens the capabilities this session has open and doesn't ask the judge again; what's hidden here stays hidden there. Until this session has chosen, a subagent asks the judge itself, as before.",
|
|
2209
2209
|
"zh-TW": "子代理開啟主對話已經開啟的能力,不再問判定器;主對話隱藏的照樣隱藏。主對話還沒選過時,子代理照舊自己問。",
|
|
2210
2210
|
"ja-JP": "サブエージェントは、このセッションで開いているケイパビリティを開き、判定器に改めて問い合わせません。ここで非表示のものは、サブエージェントでも非表示のままです。このセッションがまだ選んでいないうちは、サブエージェントはこれまでどおり自分で問い合わせます。",
|
|
2211
2211
|
"ko-KR": "하위 에이전트는 이 세션에서 열려 있는 기능 확장을 열고, 판정기에 다시 묻지 않습니다. 여기서 숨긴 것은 하위 에이전트에서도 숨겨집니다. 이 세션이 아직 고르지 않았다면 하위 에이전트는 전처럼 직접 묻습니다.",
|
|
@@ -3379,7 +3379,7 @@
|
|
|
3379
3379
|
"max": 50,
|
|
3380
3380
|
"label": {
|
|
3381
3381
|
"zh": "几个词连成句才送判",
|
|
3382
|
-
"en": "
|
|
3382
|
+
"en": "Minimum words in a row to ask the judge",
|
|
3383
3383
|
"zh-TW": "連續幾個詞才送判定器",
|
|
3384
3384
|
"ja-JP": "判定器に回す連続語数",
|
|
3385
3385
|
"ko-KR": "판정기에 묻는 연속 단어 수",
|
|
@@ -3394,7 +3394,7 @@
|
|
|
3394
3394
|
},
|
|
3395
3395
|
"help": {
|
|
3396
3396
|
"zh": "一段里没有这么多个连着的词(中文、日文这类不空格的文字算连着 8 个字),就不问判定器,只按规则看:目录列表、链接列表、响应头、几个字的 grep 结果里放不下一条指令。0:每段都问。",
|
|
3397
|
-
"en": "A passage without this many words in a row (8 characters in a row for Chinese, Japanese and other scripts written without spaces) isn't
|
|
3397
|
+
"en": "A passage without this many words in a row (8 characters in a row for Chinese, Japanese and other scripts written without spaces) isn't sent to the judge; only the rules check it. Directory listings, link lists, response headers and short grep hits can't hold an instruction. 0: every passage is judged.",
|
|
3398
3398
|
"zh-TW": "一段文字裡沒有這麼多個連著的詞(中文、日文這類不用空格分詞的文字,算連續 8 個字),就不問判定器,只按規則看:目錄清單、連結清單、回應標頭和只有幾個字的 grep 結果裡放不下一條指令。0:每段都問。",
|
|
3399
3399
|
"ja-JP": "この数だけ語が連続しない段落(中国語や日本語など、単語を空白で区切らない文字では 8 文字連続)は判定器に回さず、ルールだけで読みます。ディレクトリ一覧、リンク一覧、レスポンスヘッダー、短い grep の結果には指示を入れる余地がありません。0 にすると、すべての段落を判定します。",
|
|
3400
3400
|
"ko-KR": "연속된 단어가 이만큼 없는 단락(중국어, 일본어처럼 띄어쓰기 없이 쓰는 문자는 연속 8자)은 판정기에 묻지 않고 규칙으로만 읽습니다. 디렉토리 목록, 링크 목록, 응답 헤더, 짧은 grep 결과에는 지시가 들어갈 여지가 없습니다. 0이면 모든 단락을 판정합니다.",
|
|
@@ -4852,7 +4852,7 @@
|
|
|
4852
4852
|
},
|
|
4853
4853
|
"help": {
|
|
4854
4854
|
"zh": "无人值守指 mu --print 和 json 模式的一次性运行。默认不拍:那里没人能确认回退,文件从不会被放回去,拍快照和每次改动后的扫描却让每一步都多等。遇到死路照样提醒模型退一步。",
|
|
4855
|
-
"en": "Unattended means a one-shot mu --print or json-mode run. Off by default: nobody there
|
|
4855
|
+
"en": "Unattended means a one-shot mu --print or json-mode run. Off by default: nobody is there to confirm a rewind, so no file would ever be put back, and the snapshot and scan after every change would only slow down each step. At a dead end the model is still told to step back.",
|
|
4856
4856
|
"zh-TW": "無人值守指 mu --print 或 json 模式的單次執行。預設關閉:那裡沒人能確認回退,檔案從來不會被放回去,拍快照和每次改動後的掃描卻讓每一步都要多等。走進死路時照樣提醒模型退一步。",
|
|
4857
4857
|
"ja-JP": "無人実行とは mu --print や json モードの単発実行のことです。既定ではオフです。そこでは巻き戻しを確認できる人がいないため、ファイルが元に戻されることはないのに、スナップショットと変更のたびのスキャンでどのステップも待たされます。行き止まりでは、これまでどおりモデルに一歩引くよう伝えます。",
|
|
4858
4858
|
"ko-KR": "무인 실행은 mu --print 또는 json 모드의 단발 실행을 말합니다. 기본값은 꺼짐입니다. 거기서는 되감기를 확인해 줄 사람이 없어 파일이 되돌려지는 일이 없는데도, 스냅샷과 변경할 때마다 하는 검사 때문에 단계마다 기다려야 하기 때문입니다. 막다른 길에서는 여전히 모델에게 한 걸음 물러나라고 알려 줍니다.",
|
|
@@ -5118,7 +5118,7 @@
|
|
|
5118
5118
|
},
|
|
5119
5119
|
"help": {
|
|
5120
5120
|
"zh": "判定器的提醒(跑偏或原地打转)离监测的上一次提醒至少隔这么久,规则发现的打转也算一次。",
|
|
5121
|
-
"en": "The judge's steers
|
|
5121
|
+
"en": "The judge's steers (on drift or a loop) wait this long after the monitor's last steer, including one from the loop rule.",
|
|
5122
5122
|
"zh-TW": "判定器的提醒(偏離或原地打轉)距離監控的上一次提醒至少隔這麼久,規則發現的打轉也算一次。",
|
|
5123
5123
|
"ja-JP": "判定器による軌道修正の促し(逸脱や堂々巡り)は、逸脱検知の前回の促しからこの時間を空けます。堂々巡りのルールによる促しも前回に数えます。",
|
|
5124
5124
|
"ko-KR": "판정기의 방향 수정 알림(이탈이나 제자리 맴돌기)은 감시 기능의 마지막 알림 이후 이만큼 기다립니다. 맴돌기 규칙이 보낸 알림도 포함합니다.",
|
|
@@ -5839,7 +5839,7 @@
|
|
|
5839
5839
|
},
|
|
5840
5840
|
"help": {
|
|
5841
5841
|
"zh": "从 mu 进程启动时算起,0 表示不设。环境变量 MU_TIME_BUDGET_S 优先,评测框架用它按任务的超时来设。子代理不用这个预算。",
|
|
5842
|
-
"en": "Counted from when the mu process starts; 0 means none. The environment variable MU_TIME_BUDGET_S takes precedence, and a benchmark harness sets it from the task's timeout.
|
|
5842
|
+
"en": "Counted from when the mu process starts; 0 means none. The environment variable MU_TIME_BUDGET_S takes precedence, and a benchmark harness sets it from the task's timeout. Subagents ignore the budget.",
|
|
5843
5843
|
"zh-TW": "從 mu 啟動時算起,0 表示不設。環境變數 MU_TIME_BUDGET_S 優先,評測框架會依任務的逾時來設定它。子代理不套用這個預算。",
|
|
5844
5844
|
"ja-JP": "mu のプロセスの起動時から数えます。0 にすると予算なしになります。環境変数 MU_TIME_BUDGET_S が優先され、ベンチマークのハーネスはタスクのタイムアウトからこれを設定します。サブエージェントはこの予算を無視します。",
|
|
5845
5845
|
"ko-KR": "mu 프로세스가 시작된 때부터 셉니다. 0이면 예산을 두지 않습니다. 환경 변수 MU_TIME_BUDGET_S가 우선하며, 벤치마크 하네스는 작업의 타임아웃으로 이 값을 설정합니다. 하위 에이전트는 이 예산을 따르지 않습니다.",
|
|
@@ -5909,7 +5909,7 @@
|
|
|
5909
5909
|
},
|
|
5910
5910
|
"summary": {
|
|
5911
5911
|
"zh": "一次回复只思考、迟迟不写字也不调用工具,到时限就打断,用同样的思考强度重问一次,并把它想到哪一步带给它,让它先动手再接着想。重问的那一次不再打断。",
|
|
5912
|
-
"en": "When a reply has
|
|
5912
|
+
"en": "When a reply has done nothing but think for too long, with no text and no tool call yet, it's cut off and retried at the same reasoning effort. The retry is told how far the reasoning got and asked to act on it first. The retry itself is never cut off.",
|
|
5913
5913
|
"zh-TW": "一次回覆只顧著思考太久,還沒寫字也沒呼叫工具,就會被打斷,以同樣的思考強度重問一次。重問時會告訴它原本想到哪一步,請它先動手再接著想。重問的那一次不會再被打斷。",
|
|
5914
5914
|
"ja-JP": "返信が考えるばかりで長くかかり、まだテキストもツール呼び出しもない場合は、打ち切って同じ思考レベルで聞き直します。聞き直すときは推論がどこまで進んでいたかを伝え、まずそれをもとに手を動かすよう求めます。聞き直した返信は打ち切りません。",
|
|
5915
5915
|
"ko-KR": "응답이 텍스트도 도구 호출도 없이 너무 오래 생각만 하고 있으면 중단하고 같은 사고 수준으로 다시 묻습니다. 다시 물을 때는 추론이 어디까지 갔는지 알려 주고, 먼저 그에 따라 행동하라고 요청합니다. 다시 묻는 응답은 중단하지 않습니다.",
|
|
@@ -6942,7 +6942,7 @@
|
|
|
6942
6942
|
},
|
|
6943
6943
|
"help": {
|
|
6944
6944
|
"zh": "超时就不催了,这次判定也随之取消,不拖住单次运行的退出。",
|
|
6945
|
-
"en": "After this, there's no nudge, and the judge call is
|
|
6945
|
+
"en": "After this, there's no nudge, and the judge call is canceled so it doesn't hold up the end of a one-shot run.",
|
|
6946
6946
|
"zh-TW": "超過這個時間就不再提醒,這次判定器呼叫也隨之取消,免得拖住單次執行的結束。",
|
|
6947
6947
|
"ja-JP": "これを過ぎると促しはせず、判定器の呼び出しも取り消して、単発実行の終了を遅らせないようにします。",
|
|
6948
6948
|
"ko-KR": "이 시간이 지나면 알려 주지 않고, 판정기 호출도 취소해 단발 실행이 끝나는 것을 붙잡지 않게 합니다.",
|
|
@@ -7052,7 +7052,7 @@
|
|
|
7052
7052
|
},
|
|
7053
7053
|
"help": {
|
|
7054
7054
|
"zh": "超时就不催了,这次判定也随之取消,不拖住单次运行的退出。",
|
|
7055
|
-
"en": "After this, there's no nudge, and the judge call is
|
|
7055
|
+
"en": "After this, there's no nudge, and the judge call is canceled so it doesn't hold up the end of a one-shot run.",
|
|
7056
7056
|
"zh-TW": "超過這個時間就不再提醒,這次判定器呼叫也隨之取消,免得拖住單次執行的結束。",
|
|
7057
7057
|
"ja-JP": "これを過ぎると促しはせず、判定器の呼び出しも取り消して、単発実行の終了を遅らせないようにします。",
|
|
7058
7058
|
"ko-KR": "이 시간이 지나면 알려 주지 않고, 판정기 호출도 취소해 단발 실행이 끝나는 것을 붙잡지 않게 합니다.",
|
|
@@ -7078,7 +7078,7 @@
|
|
|
7078
7078
|
},
|
|
7079
7079
|
"help": {
|
|
7080
7080
|
"zh": "无人值守指 mu --print 和 json 模式的一次性运行。那里每条消息因为「说了要做却没做」最多催这么多次;所有催促加起来仍受上面的次数限制。判定把做完了的「已完成,已创建 …」当成没做完时,第二次催促在基准测试里多花了 4 次请求。",
|
|
7081
|
-
"en": "Unattended means a one-shot mu --print or json-mode run. There, a run is sent back for work it said it would do
|
|
7081
|
+
"en": "Unattended means a one-shot mu --print or json-mode run. There, a run is sent back at most this many times per message for work it said it would do; Max nudges per message still caps all nudges together. When the judge mistook a finished \"Done. Created …\" for a promise, the second nudge cost a benchmark run 4 more requests."
|
|
7082
7082
|
}
|
|
7083
7083
|
},
|
|
7084
7084
|
{
|
|
@@ -7278,7 +7278,7 @@
|
|
|
7278
7278
|
},
|
|
7279
7279
|
"help": {
|
|
7280
7280
|
"zh": "无人值守指 mu --print 和 json 模式的一次性运行。默认不问:通知照样按各自的规则投递,和默认的影子模式一样,那里的判定本来也只记录、不生效。",
|
|
7281
|
-
"en": "Unattended means a one-shot mu --print or json-mode run. Off by default: notices
|
|
7281
|
+
"en": "Unattended means a one-shot mu --print or json-mode run. Off by default: notices follow their own rules, as they do in shadow mode (the default), where the verdict is only recorded.",
|
|
7282
7282
|
"zh-TW": "無人值守指 mu --print 或 json 模式的單次執行。預設關閉:通知照樣依各自的規則送達,和預設的影子模式一樣,那裡的判定本來也只記錄、不生效。",
|
|
7283
7283
|
"ja-JP": "無人実行とは mu --print や json モードの単発実行のことです。既定ではオフです。通知はそれぞれのルールで届けます。既定のシャドーモードでも同じで、そこでは判定は記録するだけです。",
|
|
7284
7284
|
"ko-KR": "무인 실행은 mu --print 또는 json 모드의 단발 실행을 말합니다. 기본값은 꺼짐입니다. 알림은 각자의 규칙대로 전달됩니다. 기본값인 섀도 모드에서도 마찬가지이며, 거기서 판정은 기록만 됩니다.",
|
|
@@ -7366,7 +7366,7 @@
|
|
|
7366
7366
|
},
|
|
7367
7367
|
"help": {
|
|
7368
7368
|
"zh": "超时就按 pi 自己的算法决定,这次判定也随之取消。",
|
|
7369
|
-
"en": "After this, pi's own estimate decides, and the judge call is
|
|
7369
|
+
"en": "After this, pi's own estimate decides, and the judge call is canceled.",
|
|
7370
7370
|
"zh-TW": "超過這個時間就交給 pi 自己的估算決定,這次判定器呼叫也隨之取消。",
|
|
7371
7371
|
"ja-JP": "これを過ぎると pi 自身の見積もりで決め、判定器の呼び出しは取り消します。",
|
|
7372
7372
|
"ko-KR": "이 시간이 지나면 pi 자체의 추정으로 정하고, 판정기 호출은 취소합니다.",
|
package/kyrn/bin/mu.mjs
CHANGED
|
@@ -102,7 +102,7 @@ export function usage(platform) {
|
|
|
102
102
|
" mu -c | -r continue the last session | pick a session to resume",
|
|
103
103
|
" mu setup connect a model (paste an API key or pick a service) and choose a judge",
|
|
104
104
|
" (see mu setup --help for scripting, reading a key from stdin and the supported services)",
|
|
105
|
-
" mu judge mude install
|
|
105
|
+
" mu judge mude install install mude 1.0, mu's own decision model for coding, and make it the judge on this Mac (Apple silicon, 1.8 GB)",
|
|
106
106
|
" enable | disable | start | stop | status | uninstall (mu judge mude help)",
|
|
107
107
|
" mu judge <cmd> manage the local judge, Laya (macOS on Apple silicon): setup | start | stop | status | run",
|
|
108
108
|
" mu ledger [n] [--json] print the judge's verdicts from the last n sessions",
|
|
@@ -872,7 +872,7 @@ export function planMude({ platform, env, argv, root, home, execPath, fs, strips
|
|
|
872
872
|
if (layout === "package") {
|
|
873
873
|
const built = packageEntries({ root, platform }).mude;
|
|
874
874
|
if (!fs.exists(built)) {
|
|
875
|
-
return { error: `This mu-agent package
|
|
875
|
+
return { error: `This mu-agent package doesn't include mu judge mude (${built} is missing). Update it: npm i -g mu-agent` };
|
|
876
876
|
}
|
|
877
877
|
entry = [built];
|
|
878
878
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mu-agent",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "mu: a coding agent that hands its routine decisions to a small, fast judge model, so the main model can keep its attention on the work. Built on pi.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -42,6 +42,6 @@
|
|
|
42
42
|
"muBuild": {
|
|
43
43
|
"pi": "1.3.0",
|
|
44
44
|
"judge": "0.1.0",
|
|
45
|
-
"commit": "
|
|
45
|
+
"commit": "21f3c2a84"
|
|
46
46
|
}
|
|
47
47
|
}
|