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