unrestricted 1.0.0 → 1.1.0
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/dist/unrestricted.mjs +2040 -492
- package/package.json +1 -1
package/dist/unrestricted.mjs
CHANGED
|
@@ -12,6 +12,10 @@ var __commonJS = (cb, mod) => function __require() {
|
|
|
12
12
|
throw mod = 0, e2;
|
|
13
13
|
}
|
|
14
14
|
};
|
|
15
|
+
var __export = (target, all) => {
|
|
16
|
+
for (var name in all)
|
|
17
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
18
|
+
};
|
|
15
19
|
var __copyProps = (to, from, except, desc) => {
|
|
16
20
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
21
|
for (let key of __getOwnPropNames(from))
|
|
@@ -489,6 +493,23 @@ var x = class {
|
|
|
489
493
|
}
|
|
490
494
|
}
|
|
491
495
|
};
|
|
496
|
+
var dD = class extends x {
|
|
497
|
+
get cursor() {
|
|
498
|
+
return this.value ? 0 : 1;
|
|
499
|
+
}
|
|
500
|
+
get _value() {
|
|
501
|
+
return this.cursor === 0;
|
|
502
|
+
}
|
|
503
|
+
constructor(u2) {
|
|
504
|
+
super(u2, false), this.value = !!u2.initialValue, this.on("value", () => {
|
|
505
|
+
this.value = this._value;
|
|
506
|
+
}), this.on("confirm", (t) => {
|
|
507
|
+
this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = t, this.state = "submit", this.close();
|
|
508
|
+
}), this.on("cursor", () => {
|
|
509
|
+
this.value = !this.value;
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
};
|
|
492
513
|
var A;
|
|
493
514
|
A = /* @__PURE__ */ new WeakMap();
|
|
494
515
|
var kD = Object.defineProperty;
|
|
@@ -580,6 +601,25 @@ var G2 = (t) => {
|
|
|
580
601
|
return j2 || E ? import_picocolors.default.dim("...") : i(p2, v + l2 === n);
|
|
581
602
|
});
|
|
582
603
|
};
|
|
604
|
+
var ye = (t) => {
|
|
605
|
+
const n = t.active ?? "Yes", r2 = t.inactive ?? "No";
|
|
606
|
+
return new dD({ active: n, inactive: r2, initialValue: t.initialValue ?? true, render() {
|
|
607
|
+
const i = `${import_picocolors.default.gray(o)}
|
|
608
|
+
${b2(this.state)} ${t.message}
|
|
609
|
+
`, s = this.value ? n : r2;
|
|
610
|
+
switch (this.state) {
|
|
611
|
+
case "submit":
|
|
612
|
+
return `${i}${import_picocolors.default.gray(o)} ${import_picocolors.default.dim(s)}`;
|
|
613
|
+
case "cancel":
|
|
614
|
+
return `${i}${import_picocolors.default.gray(o)} ${import_picocolors.default.strikethrough(import_picocolors.default.dim(s))}
|
|
615
|
+
${import_picocolors.default.gray(o)}`;
|
|
616
|
+
default:
|
|
617
|
+
return `${i}${import_picocolors.default.cyan(o)} ${this.value ? `${import_picocolors.default.green(k2)} ${n}` : `${import_picocolors.default.dim(P2)} ${import_picocolors.default.dim(n)}`} ${import_picocolors.default.dim("/")} ${this.value ? `${import_picocolors.default.dim(P2)} ${import_picocolors.default.dim(r2)}` : `${import_picocolors.default.green(k2)} ${r2}`}
|
|
618
|
+
${import_picocolors.default.cyan(d2)}
|
|
619
|
+
`;
|
|
620
|
+
}
|
|
621
|
+
} }).prompt();
|
|
622
|
+
};
|
|
583
623
|
var fe = (t) => {
|
|
584
624
|
const n = (r2, i) => {
|
|
585
625
|
const s = r2.label ?? String(r2.value);
|
|
@@ -724,13 +764,11 @@ var Y2 = ({ indicator: t = "dots" } = {}) => {
|
|
|
724
764
|
|
|
725
765
|
// cli/src/command.ts
|
|
726
766
|
var FAILED = { code: 1 };
|
|
767
|
+
var NOTHING_CHANGED = "Nothing changed.";
|
|
727
768
|
function reason(error) {
|
|
728
769
|
return error instanceof Error ? error.message : String(error);
|
|
729
770
|
}
|
|
730
771
|
|
|
731
|
-
// cli/src/writers/json-file.ts
|
|
732
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
733
|
-
|
|
734
772
|
// cli/src/text-file.ts
|
|
735
773
|
var BOM = "\uFEFF";
|
|
736
774
|
var NEW_FILE = { eol: "\n", bom: false };
|
|
@@ -752,19 +790,375 @@ function fromLf(text, layout) {
|
|
|
752
790
|
return withBom(layout.eol === "\n" ? text : text.replace(/\r?\n/g, "\r\n"), layout);
|
|
753
791
|
}
|
|
754
792
|
|
|
793
|
+
// cli/src/writers/tool-file.ts
|
|
794
|
+
import { readFileSync as readFileSync2, rmSync as rmSync2, statSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
795
|
+
|
|
796
|
+
// cli/src/backup.ts
|
|
797
|
+
import { mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
798
|
+
|
|
799
|
+
// cli/src/config.ts
|
|
800
|
+
import { homedir } from "node:os";
|
|
801
|
+
import { posix, win32 } from "node:path";
|
|
802
|
+
var THIS_MACHINE = { platform: process.platform, homedir };
|
|
803
|
+
var AUTO_UPDATE_TOOLS = ["opencode", "pi"];
|
|
804
|
+
var ALL_TOOLS = ["opencode", "codex", "qwen-code", "goose", "openhands", "aider", "continue", "crush", "pi", "claude"];
|
|
805
|
+
var DISABLED_TOOLS = /* @__PURE__ */ new Set(["aider", "claude"]);
|
|
806
|
+
var OFFERED_TOOLS = ALL_TOOLS.filter((tool) => !DISABLED_TOOLS.has(tool));
|
|
807
|
+
var TOOL_LABELS = {
|
|
808
|
+
opencode: "OpenCode",
|
|
809
|
+
codex: "Codex CLI",
|
|
810
|
+
"qwen-code": "Qwen Code",
|
|
811
|
+
goose: "Goose",
|
|
812
|
+
openhands: "OpenHands",
|
|
813
|
+
aider: "Aider",
|
|
814
|
+
continue: "Continue",
|
|
815
|
+
crush: "Crush",
|
|
816
|
+
pi: "Pi",
|
|
817
|
+
claude: "Claude Code"
|
|
818
|
+
};
|
|
819
|
+
var SETUP_INTRO = "unrestricted.sh setup";
|
|
820
|
+
var SETUP_OUTRO = "Done.";
|
|
821
|
+
var WINDOWS_PLATFORM = "win32";
|
|
822
|
+
var CONFIG_HOME_DIR = ".config";
|
|
823
|
+
var OPENCODE_CONFIG_DIR = "opencode";
|
|
824
|
+
var OPENCODE_CONFIG_FILE = "opencode.json";
|
|
825
|
+
var OPENCODE_JSONC_FILE = "opencode.jsonc";
|
|
826
|
+
var AIDER_CONFIG_FILE = ".aider.conf.yml";
|
|
827
|
+
var CONTINUE_CONFIG_DIR = ".continue";
|
|
828
|
+
var CONTINUE_CONFIG_FILE = "config.yaml";
|
|
829
|
+
var CRUSH_CONFIG_DIR = "crush";
|
|
830
|
+
var CRUSH_CONFIG_FILE = "crushrc";
|
|
831
|
+
var PI_AGENT_DIR = ".pi/agent";
|
|
832
|
+
var PI_AGENT_DIR_ENV = "PI_CODING_AGENT_DIR";
|
|
833
|
+
var PI_MODELS_FILE = "models.json";
|
|
834
|
+
var PI_SETTINGS_FILE = "settings.json";
|
|
835
|
+
var PI_API = "openai-completions";
|
|
836
|
+
var PI_THINKING_LEVEL_MAP = { off: "none", minimal: null, low: "low", medium: "medium", high: "xhigh" };
|
|
837
|
+
var CODEX_CONFIG_DIR = ".codex";
|
|
838
|
+
var CODEX_CONFIG_FILE = "config.toml";
|
|
839
|
+
var QWEN_CONFIG_DIR = ".qwen";
|
|
840
|
+
var QWEN_SETTINGS_FILE = "settings.json";
|
|
841
|
+
var GOOSE_CONFIG_DIR = "goose";
|
|
842
|
+
var GOOSE_WINDOWS_CONFIG_DIR = "Block/goose/config";
|
|
843
|
+
var GOOSE_CONFIG_FILE = "config.yaml";
|
|
844
|
+
var GOOSE_CUSTOM_PROVIDERS_DIR = "custom_providers";
|
|
845
|
+
var OPENHANDS_CONFIG_DIR = ".openhands";
|
|
846
|
+
var OPENHANDS_SETTINGS_FILE = "agent_settings.json";
|
|
847
|
+
var WINDOWS_APP_DATA_ENV = "APPDATA";
|
|
848
|
+
var WINDOWS_APP_DATA_DIR = "AppData/Roaming";
|
|
849
|
+
var DEFAULT_BASE_URL = "https://api.unrestricted.sh/v1";
|
|
850
|
+
var DEFAULT_ORIGIN = "https://unrestricted.sh";
|
|
851
|
+
var DEFAULT_MODEL = "qwen3.8-27b-uncensored";
|
|
852
|
+
var DEFAULT_MODEL_NAME = "Qwen3.8 27B Uncensored";
|
|
853
|
+
var PREVIOUS_DEFAULT_MODELS = ["abliterated-model"];
|
|
854
|
+
var CONTEXT = 262144;
|
|
855
|
+
var MAX_OUTPUT = 32768;
|
|
856
|
+
var PROVIDER = "unrestricted";
|
|
857
|
+
var PROVIDER_NAME = "unrestricted.sh";
|
|
858
|
+
var API_KEY_ENV = "UNRESTRICTED_API_KEY";
|
|
859
|
+
var KEY_PREFIX = "unr_sk_";
|
|
860
|
+
var KEY_LENGTH = 39;
|
|
861
|
+
var KEY_ERROR = `UNRESTRICTED_API_KEY is not an ${KEY_PREFIX} key. Leave it unset to approve the connection in the browser instead.`;
|
|
862
|
+
function keyAcceptedMessage(base) {
|
|
863
|
+
return `Key accepted by ${base}`;
|
|
864
|
+
}
|
|
865
|
+
function piModel(id, name, context, maxOutput) {
|
|
866
|
+
return {
|
|
867
|
+
id,
|
|
868
|
+
name,
|
|
869
|
+
reasoning: true,
|
|
870
|
+
thinkingLevelMap: PI_THINKING_LEVEL_MAP,
|
|
871
|
+
input: ["text"],
|
|
872
|
+
contextWindow: context,
|
|
873
|
+
maxTokens: maxOutput
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
function isOwnModel(model, owner) {
|
|
877
|
+
return model === owner.model || model === DEFAULT_MODEL || PREVIOUS_DEFAULT_MODELS.includes(model) || owner.models.some((listed) => listed.id === model);
|
|
878
|
+
}
|
|
879
|
+
function isOwnDefault(value, owner) {
|
|
880
|
+
const prefix = `${owner.provider}/`;
|
|
881
|
+
return value.startsWith(prefix) && isOwnModel(value.slice(prefix.length), owner);
|
|
882
|
+
}
|
|
883
|
+
function disabledToolMessage(tool, origin) {
|
|
884
|
+
if (tool === "aider") return `${TOOL_LABELS.aider} is no longer set up by unrestricted.sh. See ${origin}/guides/aider to set it up by hand`;
|
|
885
|
+
return `${TOOL_LABELS[tool]} is not available yet`;
|
|
886
|
+
}
|
|
887
|
+
function isToolId(value) {
|
|
888
|
+
return ALL_TOOLS.includes(value);
|
|
889
|
+
}
|
|
890
|
+
function isAutoUpdateTool(tool) {
|
|
891
|
+
return AUTO_UPDATE_TOOLS.includes(tool);
|
|
892
|
+
}
|
|
893
|
+
function parseTools(value) {
|
|
894
|
+
const tools = [];
|
|
895
|
+
const unknown = [];
|
|
896
|
+
const disabled = [];
|
|
897
|
+
for (const entry of value.split(",")) {
|
|
898
|
+
const name = entry.trim();
|
|
899
|
+
if (!name) continue;
|
|
900
|
+
if (!isToolId(name)) unknown.push(name);
|
|
901
|
+
else if (DISABLED_TOOLS.has(name)) disabled.push(name);
|
|
902
|
+
else tools.push(name);
|
|
903
|
+
}
|
|
904
|
+
return { tools, unknown, disabled };
|
|
905
|
+
}
|
|
906
|
+
function stripTrailingSlashes(value) {
|
|
907
|
+
return value.replace(/\/+$/, "");
|
|
908
|
+
}
|
|
909
|
+
function pathsFor(platform) {
|
|
910
|
+
return platform === WINDOWS_PLATFORM ? win32 : posix;
|
|
911
|
+
}
|
|
912
|
+
function windowsShellPath(dir) {
|
|
913
|
+
if (!dir.startsWith("/") || dir.startsWith("//") || dir.includes("\\")) return dir;
|
|
914
|
+
const drive = /^\/(?:mnt\/|cygdrive\/)?([a-z])(?:\/(.*))?$/i.exec(dir);
|
|
915
|
+
if (drive === null) return dir;
|
|
916
|
+
return `${drive[1].toUpperCase()}:\\${(drive[2] ?? "").replaceAll("/", "\\")}`;
|
|
917
|
+
}
|
|
918
|
+
function resolvePiAgentDir(env, home, platform) {
|
|
919
|
+
const { join: join4 } = pathsFor(platform);
|
|
920
|
+
const set = env[PI_AGENT_DIR_ENV];
|
|
921
|
+
if (!set) return join4(home, ...PI_AGENT_DIR.split("/"));
|
|
922
|
+
const windows = platform === WINDOWS_PLATFORM;
|
|
923
|
+
const dir = windows ? windowsShellPath(set) : set;
|
|
924
|
+
if (dir === "~") return home;
|
|
925
|
+
if (dir.startsWith("~/") || windows && dir.startsWith("~\\")) return join4(home, dir.slice(2));
|
|
926
|
+
return dir;
|
|
927
|
+
}
|
|
928
|
+
function windowsVariable(env, name) {
|
|
929
|
+
const key = Object.keys(env).find((candidate) => candidate.toUpperCase() === name);
|
|
930
|
+
return key === void 0 ? void 0 : env[key];
|
|
931
|
+
}
|
|
932
|
+
function windowsAppData(env, home) {
|
|
933
|
+
return windowsVariable(env, WINDOWS_APP_DATA_ENV) || win32.join(home, ...WINDOWS_APP_DATA_DIR.split("/"));
|
|
934
|
+
}
|
|
935
|
+
function resolveGooseConfigDir(env, home, configHome, platform) {
|
|
936
|
+
if (platform !== WINDOWS_PLATFORM) return posix.join(configHome, GOOSE_CONFIG_DIR);
|
|
937
|
+
return win32.join(windowsAppData(env, home), ...GOOSE_WINDOWS_CONFIG_DIR.split("/"));
|
|
938
|
+
}
|
|
939
|
+
function homeOf(env, machine) {
|
|
940
|
+
if (env.HOME) return env.HOME;
|
|
941
|
+
try {
|
|
942
|
+
return machine.homedir();
|
|
943
|
+
} catch {
|
|
944
|
+
return "";
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
function readConfig(env, machine = THIS_MACHINE) {
|
|
948
|
+
const key = env.UNRESTRICTED_API_KEY ?? "";
|
|
949
|
+
if (key && !key.startsWith(KEY_PREFIX)) throw new Error(KEY_ERROR);
|
|
950
|
+
if (key && key.length !== KEY_LENGTH) throw new Error("UNRESTRICTED_API_KEY has the wrong length");
|
|
951
|
+
const home = homeOf(env, machine);
|
|
952
|
+
if (!home) throw new Error("HOME is not set and the home directory could not be found");
|
|
953
|
+
const { join: join4 } = pathsFor(machine.platform);
|
|
954
|
+
const model = env.UNRESTRICTED_MODEL || DEFAULT_MODEL;
|
|
955
|
+
const modelName = env.UNRESTRICTED_MODEL_NAME || DEFAULT_MODEL_NAME;
|
|
956
|
+
const configHome = env.XDG_CONFIG_HOME || join4(home, CONFIG_HOME_DIR);
|
|
957
|
+
return {
|
|
958
|
+
key: key || null,
|
|
959
|
+
base: env.UNRESTRICTED_BASE_URL || DEFAULT_BASE_URL,
|
|
960
|
+
origin: stripTrailingSlashes(env.UNRESTRICTED_ORIGIN || env.UNRESTRICTED_SETUP_ORIGIN || DEFAULT_ORIGIN),
|
|
961
|
+
model,
|
|
962
|
+
modelName,
|
|
963
|
+
context: CONTEXT,
|
|
964
|
+
maxOutput: MAX_OUTPUT,
|
|
965
|
+
models: [{ id: model, name: modelName, context: CONTEXT, maxOutput: MAX_OUTPUT }],
|
|
966
|
+
provider: PROVIDER,
|
|
967
|
+
platform: machine.platform,
|
|
968
|
+
home,
|
|
969
|
+
configHome,
|
|
970
|
+
piAgentDir: resolvePiAgentDir(env, home, machine.platform),
|
|
971
|
+
gooseConfigDir: resolveGooseConfigDir(env, home, configHome, machine.platform)
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// cli/src/file-mode.ts
|
|
976
|
+
import { chmodSync } from "node:fs";
|
|
977
|
+
var WINDOWS = "win32";
|
|
978
|
+
function setMode(path, mode, platform = process.platform) {
|
|
979
|
+
if (platform === WINDOWS) return;
|
|
980
|
+
chmodSync(path, mode);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// cli/src/paths.ts
|
|
984
|
+
var GOOSE_PROVIDER_FILE = `${PROVIDER}.json`;
|
|
985
|
+
var CLAUDE_CONFIG_DIR = ".claude";
|
|
986
|
+
var CLAUDE_SETTINGS_FILE = "settings.json";
|
|
987
|
+
var CLAUDE_SESSION_FILE = "claude-settings.json";
|
|
988
|
+
var CLAUDE_LAUNCHER = "claude-unrestricted";
|
|
989
|
+
var STORE_DIR_NAME = "unrestricted";
|
|
990
|
+
var OPENCODE_PLUGINS_DIR = "plugins";
|
|
991
|
+
var PI_EXTENSIONS_DIR = "extensions";
|
|
992
|
+
var AUTO_UPDATE_FILE = "unrestricted.ts";
|
|
993
|
+
function tildePath(path, home, platform) {
|
|
994
|
+
const { sep } = pathsFor(platform);
|
|
995
|
+
if (home === "" || home.endsWith(sep)) return path;
|
|
996
|
+
if (path === home) return "~";
|
|
997
|
+
return path.startsWith(`${home}${sep}`) ? `~${sep}${path.slice(home.length + sep.length)}` : path;
|
|
998
|
+
}
|
|
999
|
+
function toolPaths(locations) {
|
|
1000
|
+
const { join: join4 } = pathsFor(locations.platform);
|
|
1001
|
+
const { home, configHome, piAgentDir, gooseConfigDir: gooseDir } = locations;
|
|
1002
|
+
const opencodeDir = join4(configHome, OPENCODE_CONFIG_DIR);
|
|
1003
|
+
const crushDir = join4(configHome, CRUSH_CONFIG_DIR);
|
|
1004
|
+
const continueDir = join4(home, CONTINUE_CONFIG_DIR);
|
|
1005
|
+
const codexDir = join4(home, CODEX_CONFIG_DIR);
|
|
1006
|
+
const qwenDir = join4(home, QWEN_CONFIG_DIR);
|
|
1007
|
+
const openhandsDir = join4(home, OPENHANDS_CONFIG_DIR);
|
|
1008
|
+
const claudeDir = join4(home, CLAUDE_CONFIG_DIR);
|
|
1009
|
+
const claudeLauncherDir = join4(home, ".local", "bin");
|
|
1010
|
+
return {
|
|
1011
|
+
opencodeDir,
|
|
1012
|
+
opencodeJson: join4(opencodeDir, OPENCODE_CONFIG_FILE),
|
|
1013
|
+
opencodeJsonc: join4(opencodeDir, OPENCODE_JSONC_FILE),
|
|
1014
|
+
opencodePlugin: join4(opencodeDir, OPENCODE_PLUGINS_DIR, AUTO_UPDATE_FILE),
|
|
1015
|
+
crushDir,
|
|
1016
|
+
crushrc: join4(crushDir, CRUSH_CONFIG_FILE),
|
|
1017
|
+
piAgentDir,
|
|
1018
|
+
piModels: join4(piAgentDir, PI_MODELS_FILE),
|
|
1019
|
+
piSettings: join4(piAgentDir, PI_SETTINGS_FILE),
|
|
1020
|
+
piExtension: join4(piAgentDir, PI_EXTENSIONS_DIR, AUTO_UPDATE_FILE),
|
|
1021
|
+
continueDir,
|
|
1022
|
+
continueConfig: join4(continueDir, CONTINUE_CONFIG_FILE),
|
|
1023
|
+
aiderConfig: join4(home, AIDER_CONFIG_FILE),
|
|
1024
|
+
codexDir,
|
|
1025
|
+
codexConfig: join4(codexDir, CODEX_CONFIG_FILE),
|
|
1026
|
+
qwenDir,
|
|
1027
|
+
qwenSettings: join4(qwenDir, QWEN_SETTINGS_FILE),
|
|
1028
|
+
gooseDir,
|
|
1029
|
+
gooseConfig: join4(gooseDir, GOOSE_CONFIG_FILE),
|
|
1030
|
+
gooseProvider: join4(gooseDir, GOOSE_CUSTOM_PROVIDERS_DIR, GOOSE_PROVIDER_FILE),
|
|
1031
|
+
openhandsDir,
|
|
1032
|
+
openhandsSettings: join4(openhandsDir, OPENHANDS_SETTINGS_FILE),
|
|
1033
|
+
claudeDir,
|
|
1034
|
+
claudeGlobalSettings: join4(claudeDir, CLAUDE_SETTINGS_FILE),
|
|
1035
|
+
claudeSessionSettings: join4(configHome, STORE_DIR_NAME, CLAUDE_SESSION_FILE),
|
|
1036
|
+
claudeLauncherDir,
|
|
1037
|
+
claudeLauncher: join4(claudeLauncherDir, CLAUDE_LAUNCHER)
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// cli/src/backup.ts
|
|
1042
|
+
var BACKUPS_DIR_NAME = "backups";
|
|
1043
|
+
var BACKUPS_KEPT = 5;
|
|
1044
|
+
var DIR_MODE = 448;
|
|
1045
|
+
var FILE_MODE = 384;
|
|
1046
|
+
var STAMP = /^\d{8}T\d{6}Z$/;
|
|
1047
|
+
var backupsTaken = 0;
|
|
1048
|
+
function backupsDir(place) {
|
|
1049
|
+
return pathsFor(place.platform).join(place.configHome, STORE_DIR_NAME, BACKUPS_DIR_NAME);
|
|
1050
|
+
}
|
|
1051
|
+
function backupStamp(date) {
|
|
1052
|
+
return date.toISOString().replace(/\.\d+Z$/, "Z").replace(/[-:]/g, "");
|
|
1053
|
+
}
|
|
1054
|
+
function toolBackupsDir(place, tool) {
|
|
1055
|
+
return pathsFor(place.platform).join(backupsDir(place), tool);
|
|
1056
|
+
}
|
|
1057
|
+
function isExisting(error) {
|
|
1058
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
|
|
1059
|
+
}
|
|
1060
|
+
function makePrivateDir(dir, platform) {
|
|
1061
|
+
mkdirSync(dir, { recursive: true, mode: DIR_MODE });
|
|
1062
|
+
setMode(dir, DIR_MODE, platform);
|
|
1063
|
+
}
|
|
1064
|
+
function backupsOf(place, tool, file) {
|
|
1065
|
+
const { basename, join: join4 } = pathsFor(place.platform);
|
|
1066
|
+
const dir = toolBackupsDir(place, tool);
|
|
1067
|
+
const prefix = `${basename(file)}.`;
|
|
1068
|
+
let names;
|
|
1069
|
+
try {
|
|
1070
|
+
names = readdirSync(dir);
|
|
1071
|
+
} catch {
|
|
1072
|
+
return [];
|
|
1073
|
+
}
|
|
1074
|
+
return names.filter((name) => name.startsWith(prefix) && STAMP.test(name.slice(prefix.length))).sort().map((name) => join4(dir, name));
|
|
1075
|
+
}
|
|
1076
|
+
function newestBackup(place, tool, file) {
|
|
1077
|
+
return backupsOf(place, tool, file).at(-1) ?? null;
|
|
1078
|
+
}
|
|
1079
|
+
function backUp(place, tool, file, now = /* @__PURE__ */ new Date()) {
|
|
1080
|
+
const { basename, join: join4 } = pathsFor(place.platform);
|
|
1081
|
+
const dir = toolBackupsDir(place, tool);
|
|
1082
|
+
for (const parent of [join4(place.configHome, STORE_DIR_NAME), backupsDir(place), dir]) makePrivateDir(parent, place.platform);
|
|
1083
|
+
const target = join4(dir, `${basename(file)}.${backupStamp(now)}`);
|
|
1084
|
+
try {
|
|
1085
|
+
writeFileSync(target, readFileSync(file), { mode: FILE_MODE, flag: "wx" });
|
|
1086
|
+
} catch (error) {
|
|
1087
|
+
if (!isExisting(error)) throw error;
|
|
1088
|
+
}
|
|
1089
|
+
setMode(target, FILE_MODE, place.platform);
|
|
1090
|
+
for (const older of backupsOf(place, tool, file).slice(0, -BACKUPS_KEPT)) rmSync(older, { force: true });
|
|
1091
|
+
backupsTaken += 1;
|
|
1092
|
+
return target;
|
|
1093
|
+
}
|
|
1094
|
+
function backupsMade() {
|
|
1095
|
+
return backupsTaken;
|
|
1096
|
+
}
|
|
1097
|
+
function backupsLine(place) {
|
|
1098
|
+
return `Backups of the changed files: ${tildePath(backupsDir(place), place.home, place.platform)}`;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
// cli/src/writers/tool-file.ts
|
|
1102
|
+
function isMissing(error) {
|
|
1103
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
1104
|
+
}
|
|
1105
|
+
function isFile(path) {
|
|
1106
|
+
try {
|
|
1107
|
+
return statSync(path).isFile();
|
|
1108
|
+
} catch {
|
|
1109
|
+
return false;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
function readText(file) {
|
|
1113
|
+
try {
|
|
1114
|
+
return readFileSync2(file, "utf8");
|
|
1115
|
+
} catch (error) {
|
|
1116
|
+
if (isMissing(error)) return null;
|
|
1117
|
+
throw error;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
function saveText(place, tool, file, text, current) {
|
|
1121
|
+
if (current === text) return false;
|
|
1122
|
+
if (current !== null) backUp(place, tool, file);
|
|
1123
|
+
writeFileSync2(file, text);
|
|
1124
|
+
return true;
|
|
1125
|
+
}
|
|
1126
|
+
function deleteText(place, tool, file) {
|
|
1127
|
+
if (readText(file) === null) return false;
|
|
1128
|
+
backUp(place, tool, file);
|
|
1129
|
+
rmSync2(file, { force: true });
|
|
1130
|
+
return true;
|
|
1131
|
+
}
|
|
1132
|
+
function applyChanges(place, tool, changes) {
|
|
1133
|
+
for (const change of changes) {
|
|
1134
|
+
if (change.next === null) deleteText(place, tool, change.file);
|
|
1135
|
+
else saveText(place, tool, change.file, change.next, change.raw);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
function splitLines(content) {
|
|
1139
|
+
if (content === "") return [];
|
|
1140
|
+
return (content.endsWith("\n") ? content.slice(0, -1) : content).split("\n");
|
|
1141
|
+
}
|
|
1142
|
+
function isBlankText(text) {
|
|
1143
|
+
return text.trim() === "";
|
|
1144
|
+
}
|
|
1145
|
+
|
|
755
1146
|
// cli/src/writers/json-file.ts
|
|
756
1147
|
function asObject(value) {
|
|
757
1148
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
758
1149
|
}
|
|
759
1150
|
function readJsonDocument(file) {
|
|
760
|
-
|
|
761
|
-
|
|
1151
|
+
const raw = readText(file);
|
|
1152
|
+
if (raw === null) return { value: {}, layout: NEW_FILE, raw };
|
|
762
1153
|
const text = toLf(raw).trim();
|
|
763
|
-
return { value: text ? asObject(JSON.parse(text)) : {}, layout: layoutOf(raw) };
|
|
1154
|
+
return { value: text ? asObject(JSON.parse(text)) : {}, layout: layoutOf(raw), raw };
|
|
1155
|
+
}
|
|
1156
|
+
function jsonText(value, layout = NEW_FILE) {
|
|
1157
|
+
return fromLf(`${JSON.stringify(value, null, 2)}
|
|
1158
|
+
`, layout);
|
|
764
1159
|
}
|
|
765
|
-
function
|
|
766
|
-
|
|
767
|
-
`, layout));
|
|
1160
|
+
function isBareDocument(value) {
|
|
1161
|
+
return Object.keys(value).every((key) => key === "$schema");
|
|
768
1162
|
}
|
|
769
1163
|
|
|
770
1164
|
// cli/src/models.ts
|
|
@@ -868,180 +1262,42 @@ async function verifyKey(base, key) {
|
|
|
868
1262
|
if (!response.ok) throw new Error(`the key was rejected by ${url}`);
|
|
869
1263
|
return parseModelList(await readJson(response));
|
|
870
1264
|
} finally {
|
|
871
|
-
clearTimeout(timer);
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
async function fetchModelCatalog(base, timeoutMs = TIMEOUT_MS) {
|
|
875
|
-
const url = `${base}/models`;
|
|
876
|
-
const answer = await getJson(url, { accept: "application/json" }, timeoutMs);
|
|
877
|
-
if (!answer.ok) throw new Error(`${url} answered ${answer.status}.`);
|
|
878
|
-
return parseModelCatalog(answer.body);
|
|
879
|
-
}
|
|
880
|
-
function parsePlan(value) {
|
|
881
|
-
if (value === null) return null;
|
|
882
|
-
if (typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
883
|
-
const { name, allowance_usd: allowanceUsd, used_usd: usedUsd, renews_at: renews } = asObject(value);
|
|
884
|
-
const planName = printable(name, PLAN_NAME_MAX_LENGTH);
|
|
885
|
-
const renewsAt = typeof renews === "string" ? new Date(renews) : null;
|
|
886
|
-
if (planName === null || !isAmount(allowanceUsd) || !isAmount(usedUsd) || renewsAt === null || Number.isNaN(renewsAt.getTime())) {
|
|
887
|
-
return void 0;
|
|
888
|
-
}
|
|
889
|
-
return { name: planName, allowanceUsd, usedUsd, renewsAt };
|
|
890
|
-
}
|
|
891
|
-
function parseAccount(body) {
|
|
892
|
-
if (typeof body !== "object" || body === null || Array.isArray(body)) return null;
|
|
893
|
-
const { balance_usd: balanceUsd, plan, month_spend_usd: monthSpendUsd, api_locked: apiLocked } = asObject(body);
|
|
894
|
-
const parsedPlan = parsePlan(plan);
|
|
895
|
-
if (!isAmount(balanceUsd) || !isAmount(monthSpendUsd) || typeof apiLocked !== "boolean" || parsedPlan === void 0) return null;
|
|
896
|
-
return { balanceUsd, plan: parsedPlan, monthSpendUsd, apiLocked };
|
|
897
|
-
}
|
|
898
|
-
async function fetchAccount(base, key) {
|
|
899
|
-
const url = `${base}/account`;
|
|
900
|
-
const answer = await getJson(url, { Authorization: `Bearer ${key}`, accept: "application/json" }, TIMEOUT_MS);
|
|
901
|
-
if (answer.status === 401) throw new Error('The key was rejected. Run "unrestricted login" to sign in again.');
|
|
902
|
-
if (answer.status === 403) throw new Error(errorMessage(answer.body) ?? `${url} refused the key.`);
|
|
903
|
-
if (!answer.ok) throw new Error(`${url} answered ${answer.status}.`);
|
|
904
|
-
const account = parseAccount(answer.body);
|
|
905
|
-
if (account === null) throw new Error(`Unexpected answer from ${url}.`);
|
|
906
|
-
return account;
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
// cli/src/config.ts
|
|
910
|
-
import { homedir } from "node:os";
|
|
911
|
-
import { posix, win32 } from "node:path";
|
|
912
|
-
var THIS_MACHINE = { platform: process.platform, homedir };
|
|
913
|
-
var AUTO_UPDATE_TOOLS = ["opencode", "pi"];
|
|
914
|
-
var ALL_TOOLS = ["opencode", "codex", "aider", "continue", "crush", "pi", "claude"];
|
|
915
|
-
var DISABLED_TOOLS = /* @__PURE__ */ new Set(["codex", "claude"]);
|
|
916
|
-
var OFFERED_TOOLS = ALL_TOOLS.filter((tool) => !DISABLED_TOOLS.has(tool));
|
|
917
|
-
var TOOL_LABELS = {
|
|
918
|
-
opencode: "OpenCode",
|
|
919
|
-
codex: "Codex CLI",
|
|
920
|
-
aider: "Aider",
|
|
921
|
-
continue: "Continue",
|
|
922
|
-
crush: "Crush",
|
|
923
|
-
pi: "Pi",
|
|
924
|
-
claude: "Claude Code"
|
|
925
|
-
};
|
|
926
|
-
var SETUP_INTRO = "unrestricted.sh setup";
|
|
927
|
-
var SETUP_OUTRO = "Done.";
|
|
928
|
-
var WINDOWS_PLATFORM = "win32";
|
|
929
|
-
var CONFIG_HOME_DIR = ".config";
|
|
930
|
-
var OPENCODE_CONFIG_DIR = "opencode";
|
|
931
|
-
var OPENCODE_CONFIG_FILE = "opencode.json";
|
|
932
|
-
var OPENCODE_JSONC_FILE = "opencode.jsonc";
|
|
933
|
-
var AIDER_CONFIG_FILE = ".aider.conf.yml";
|
|
934
|
-
var CONTINUE_CONFIG_DIR = ".continue";
|
|
935
|
-
var CONTINUE_CONFIG_FILE = "config.yaml";
|
|
936
|
-
var CRUSH_CONFIG_DIR = "crush";
|
|
937
|
-
var CRUSH_CONFIG_FILE = "crushrc";
|
|
938
|
-
var PI_AGENT_DIR = ".pi/agent";
|
|
939
|
-
var PI_AGENT_DIR_ENV = "PI_CODING_AGENT_DIR";
|
|
940
|
-
var PI_MODELS_FILE = "models.json";
|
|
941
|
-
var PI_SETTINGS_FILE = "settings.json";
|
|
942
|
-
var PI_API = "openai-completions";
|
|
943
|
-
var PI_THINKING_LEVEL_MAP = { off: "none", minimal: null, low: "low", medium: "medium", high: "xhigh" };
|
|
944
|
-
var DEFAULT_BASE_URL = "https://api.unrestricted.sh/v1";
|
|
945
|
-
var DEFAULT_ORIGIN = "https://unrestricted.sh";
|
|
946
|
-
var DEFAULT_MODEL = "qwen3.8-27b-uncensored";
|
|
947
|
-
var DEFAULT_MODEL_NAME = "Qwen3.8 27B Uncensored";
|
|
948
|
-
var PREVIOUS_DEFAULT_MODELS = ["abliterated-model"];
|
|
949
|
-
var CONTEXT = 262144;
|
|
950
|
-
var MAX_OUTPUT = 32768;
|
|
951
|
-
var PROVIDER = "unrestricted";
|
|
952
|
-
var KEY_PREFIX = "unr_sk_";
|
|
953
|
-
var KEY_LENGTH = 39;
|
|
954
|
-
var KEY_ERROR = `UNRESTRICTED_API_KEY is not an ${KEY_PREFIX} key. Leave it unset to approve the connection in the browser instead.`;
|
|
955
|
-
function keyAcceptedMessage(base) {
|
|
956
|
-
return `Key accepted by ${base}`;
|
|
957
|
-
}
|
|
958
|
-
function piModel(id, name, context, maxOutput) {
|
|
959
|
-
return {
|
|
960
|
-
id,
|
|
961
|
-
name,
|
|
962
|
-
reasoning: true,
|
|
963
|
-
thinkingLevelMap: PI_THINKING_LEVEL_MAP,
|
|
964
|
-
input: ["text"],
|
|
965
|
-
contextWindow: context,
|
|
966
|
-
maxTokens: maxOutput
|
|
967
|
-
};
|
|
968
|
-
}
|
|
969
|
-
function isOwnModel(model, config) {
|
|
970
|
-
return model === config.model || model === DEFAULT_MODEL || PREVIOUS_DEFAULT_MODELS.includes(model) || config.models.some((listed) => listed.id === model);
|
|
971
|
-
}
|
|
972
|
-
function isToolId(value) {
|
|
973
|
-
return ALL_TOOLS.includes(value);
|
|
974
|
-
}
|
|
975
|
-
function isAutoUpdateTool(tool) {
|
|
976
|
-
return AUTO_UPDATE_TOOLS.includes(tool);
|
|
977
|
-
}
|
|
978
|
-
function parseTools(value) {
|
|
979
|
-
const tools = [];
|
|
980
|
-
const unknown = [];
|
|
981
|
-
const disabled = [];
|
|
982
|
-
for (const entry of value.split(",")) {
|
|
983
|
-
const name = entry.trim();
|
|
984
|
-
if (!name) continue;
|
|
985
|
-
if (!isToolId(name)) unknown.push(name);
|
|
986
|
-
else if (DISABLED_TOOLS.has(name)) disabled.push(name);
|
|
987
|
-
else tools.push(name);
|
|
1265
|
+
clearTimeout(timer);
|
|
988
1266
|
}
|
|
989
|
-
return { tools, unknown, disabled };
|
|
990
|
-
}
|
|
991
|
-
function stripTrailingSlashes(value) {
|
|
992
|
-
return value.replace(/\/+$/, "");
|
|
993
|
-
}
|
|
994
|
-
function pathsFor(platform) {
|
|
995
|
-
return platform === WINDOWS_PLATFORM ? win32 : posix;
|
|
996
1267
|
}
|
|
997
|
-
function
|
|
998
|
-
|
|
999
|
-
const
|
|
1000
|
-
if (
|
|
1001
|
-
return
|
|
1002
|
-
}
|
|
1003
|
-
function resolvePiAgentDir(env, home, platform) {
|
|
1004
|
-
const { join: join4 } = pathsFor(platform);
|
|
1005
|
-
const set = env[PI_AGENT_DIR_ENV];
|
|
1006
|
-
if (!set) return join4(home, ...PI_AGENT_DIR.split("/"));
|
|
1007
|
-
const windows = platform === WINDOWS_PLATFORM;
|
|
1008
|
-
const dir = windows ? windowsShellPath(set) : set;
|
|
1009
|
-
if (dir === "~") return home;
|
|
1010
|
-
if (dir.startsWith("~/") || windows && dir.startsWith("~\\")) return join4(home, dir.slice(2));
|
|
1011
|
-
return dir;
|
|
1268
|
+
async function fetchModelCatalog(base, timeoutMs = TIMEOUT_MS) {
|
|
1269
|
+
const url = `${base}/models`;
|
|
1270
|
+
const answer = await getJson(url, { accept: "application/json" }, timeoutMs);
|
|
1271
|
+
if (!answer.ok) throw new Error(`${url} answered ${answer.status}.`);
|
|
1272
|
+
return parseModelCatalog(answer.body);
|
|
1012
1273
|
}
|
|
1013
|
-
function
|
|
1014
|
-
if (
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1274
|
+
function parsePlan(value) {
|
|
1275
|
+
if (value === null) return null;
|
|
1276
|
+
if (typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1277
|
+
const { name, allowance_usd: allowanceUsd, used_usd: usedUsd, renews_at: renews } = asObject(value);
|
|
1278
|
+
const planName = printable(name, PLAN_NAME_MAX_LENGTH);
|
|
1279
|
+
const renewsAt = typeof renews === "string" ? new Date(renews) : null;
|
|
1280
|
+
if (planName === null || !isAmount(allowanceUsd) || !isAmount(usedUsd) || renewsAt === null || Number.isNaN(renewsAt.getTime())) {
|
|
1281
|
+
return void 0;
|
|
1019
1282
|
}
|
|
1283
|
+
return { name: planName, allowanceUsd, usedUsd, renewsAt };
|
|
1020
1284
|
}
|
|
1021
|
-
function
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
const
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
maxOutput: MAX_OUTPUT,
|
|
1038
|
-
models: [{ id: model, name: modelName, context: CONTEXT, maxOutput: MAX_OUTPUT }],
|
|
1039
|
-
provider: PROVIDER,
|
|
1040
|
-
platform: machine.platform,
|
|
1041
|
-
home,
|
|
1042
|
-
configHome: env.XDG_CONFIG_HOME || join4(home, CONFIG_HOME_DIR),
|
|
1043
|
-
piAgentDir: resolvePiAgentDir(env, home, machine.platform)
|
|
1044
|
-
};
|
|
1285
|
+
function parseAccount(body) {
|
|
1286
|
+
if (typeof body !== "object" || body === null || Array.isArray(body)) return null;
|
|
1287
|
+
const { balance_usd: balanceUsd, plan, month_spend_usd: monthSpendUsd, api_locked: apiLocked } = asObject(body);
|
|
1288
|
+
const parsedPlan = parsePlan(plan);
|
|
1289
|
+
if (!isAmount(balanceUsd) || !isAmount(monthSpendUsd) || typeof apiLocked !== "boolean" || parsedPlan === void 0) return null;
|
|
1290
|
+
return { balanceUsd, plan: parsedPlan, monthSpendUsd, apiLocked };
|
|
1291
|
+
}
|
|
1292
|
+
async function fetchAccount(base, key) {
|
|
1293
|
+
const url = `${base}/account`;
|
|
1294
|
+
const answer = await getJson(url, { Authorization: `Bearer ${key}`, accept: "application/json" }, TIMEOUT_MS);
|
|
1295
|
+
if (answer.status === 401) throw new Error('The key was rejected. Run "unrestricted login" to sign in again.');
|
|
1296
|
+
if (answer.status === 403) throw new Error(errorMessage(answer.body) ?? `${url} refused the key.`);
|
|
1297
|
+
if (!answer.ok) throw new Error(`${url} answered ${answer.status}.`);
|
|
1298
|
+
const account = parseAccount(answer.body);
|
|
1299
|
+
if (account === null) throw new Error(`Unexpected answer from ${url}.`);
|
|
1300
|
+
return account;
|
|
1045
1301
|
}
|
|
1046
1302
|
|
|
1047
1303
|
// cli/src/format.ts
|
|
@@ -1081,68 +1337,17 @@ function table(rows, rightAligned = /* @__PURE__ */ new Set()) {
|
|
|
1081
1337
|
}
|
|
1082
1338
|
|
|
1083
1339
|
// cli/src/store.ts
|
|
1084
|
-
import { existsSync as
|
|
1340
|
+
import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3, renameSync, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1085
1341
|
import { join } from "node:path";
|
|
1086
|
-
|
|
1087
|
-
// cli/src/file-mode.ts
|
|
1088
|
-
import { chmodSync } from "node:fs";
|
|
1089
|
-
var WINDOWS = "win32";
|
|
1090
|
-
function setMode(path, mode, platform = process.platform) {
|
|
1091
|
-
if (platform === WINDOWS) return;
|
|
1092
|
-
chmodSync(path, mode);
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
// cli/src/paths.ts
|
|
1096
|
-
var CODEX_CONFIG_DIR = ".codex";
|
|
1097
|
-
var CODEX_CONFIG_FILE = "config.toml";
|
|
1098
|
-
var CLAUDE_CONFIG_DIR = ".claude";
|
|
1099
|
-
var CLAUDE_SETTINGS_FILE = "settings.json";
|
|
1100
|
-
var CLAUDE_SESSION_FILE = "claude-settings.json";
|
|
1101
|
-
var CLAUDE_LAUNCHER = "claude-unrestricted";
|
|
1102
|
-
var STORE_DIR_NAME = "unrestricted";
|
|
1103
|
-
var OPENCODE_PLUGINS_DIR = "plugins";
|
|
1104
|
-
var PI_EXTENSIONS_DIR = "extensions";
|
|
1105
|
-
var AUTO_UPDATE_FILE = "unrestricted.ts";
|
|
1106
|
-
function toolPaths(locations) {
|
|
1107
|
-
const { join: join4 } = pathsFor(locations.platform);
|
|
1108
|
-
const { home, configHome, piAgentDir } = locations;
|
|
1109
|
-
const opencodeDir = join4(configHome, OPENCODE_CONFIG_DIR);
|
|
1110
|
-
const crushDir = join4(configHome, CRUSH_CONFIG_DIR);
|
|
1111
|
-
const continueDir = join4(home, CONTINUE_CONFIG_DIR);
|
|
1112
|
-
const codexDir = join4(home, CODEX_CONFIG_DIR);
|
|
1113
|
-
const claudeDir = join4(home, CLAUDE_CONFIG_DIR);
|
|
1114
|
-
const claudeLauncherDir = join4(home, ".local", "bin");
|
|
1115
|
-
return {
|
|
1116
|
-
opencodeDir,
|
|
1117
|
-
opencodeJson: join4(opencodeDir, OPENCODE_CONFIG_FILE),
|
|
1118
|
-
opencodeJsonc: join4(opencodeDir, OPENCODE_JSONC_FILE),
|
|
1119
|
-
opencodePlugin: join4(opencodeDir, OPENCODE_PLUGINS_DIR, AUTO_UPDATE_FILE),
|
|
1120
|
-
crushDir,
|
|
1121
|
-
crushrc: join4(crushDir, CRUSH_CONFIG_FILE),
|
|
1122
|
-
piAgentDir,
|
|
1123
|
-
piModels: join4(piAgentDir, PI_MODELS_FILE),
|
|
1124
|
-
piSettings: join4(piAgentDir, PI_SETTINGS_FILE),
|
|
1125
|
-
piExtension: join4(piAgentDir, PI_EXTENSIONS_DIR, AUTO_UPDATE_FILE),
|
|
1126
|
-
continueDir,
|
|
1127
|
-
continueConfig: join4(continueDir, CONTINUE_CONFIG_FILE),
|
|
1128
|
-
aiderConfig: join4(home, AIDER_CONFIG_FILE),
|
|
1129
|
-
codexDir,
|
|
1130
|
-
codexConfig: join4(codexDir, CODEX_CONFIG_FILE),
|
|
1131
|
-
claudeDir,
|
|
1132
|
-
claudeGlobalSettings: join4(claudeDir, CLAUDE_SETTINGS_FILE),
|
|
1133
|
-
claudeSessionSettings: join4(configHome, STORE_DIR_NAME, CLAUDE_SESSION_FILE),
|
|
1134
|
-
claudeLauncherDir,
|
|
1135
|
-
claudeLauncher: join4(claudeLauncherDir, CLAUDE_LAUNCHER)
|
|
1136
|
-
};
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
// cli/src/store.ts
|
|
1140
1342
|
var CREDENTIALS_FILE = "credentials.json";
|
|
1141
1343
|
var STATE_FILE = "state.json";
|
|
1142
|
-
var
|
|
1143
|
-
var
|
|
1344
|
+
var DIR_MODE2 = 448;
|
|
1345
|
+
var FILE_MODE2 = 384;
|
|
1144
1346
|
var WEB_ORIGIN = /^https?:\/\/\S+$/;
|
|
1145
1347
|
var SHA256 = /^[0-9a-f]{64}$/;
|
|
1348
|
+
var MAX_DEFAULT_LENGTH = 512;
|
|
1349
|
+
var FIRST_PRINTABLE = " ";
|
|
1350
|
+
var DELETE = "\x7F";
|
|
1146
1351
|
function storeDir(configHome) {
|
|
1147
1352
|
return join(configHome, STORE_DIR_NAME);
|
|
1148
1353
|
}
|
|
@@ -1158,7 +1363,7 @@ function isKey(value) {
|
|
|
1158
1363
|
function readStoreFile(file) {
|
|
1159
1364
|
let parsed;
|
|
1160
1365
|
try {
|
|
1161
|
-
parsed = JSON.parse(
|
|
1366
|
+
parsed = JSON.parse(readFileSync3(file, "utf8"));
|
|
1162
1367
|
} catch {
|
|
1163
1368
|
return null;
|
|
1164
1369
|
}
|
|
@@ -1166,12 +1371,12 @@ function readStoreFile(file) {
|
|
|
1166
1371
|
}
|
|
1167
1372
|
function writeStoreFile(configHome, file, value) {
|
|
1168
1373
|
const dir = storeDir(configHome);
|
|
1169
|
-
|
|
1170
|
-
setMode(dir,
|
|
1374
|
+
mkdirSync2(dir, { recursive: true, mode: DIR_MODE2 });
|
|
1375
|
+
setMode(dir, DIR_MODE2);
|
|
1171
1376
|
const pending = `${file}.${process.pid}.tmp`;
|
|
1172
|
-
|
|
1173
|
-
`, { mode:
|
|
1174
|
-
setMode(pending,
|
|
1377
|
+
writeFileSync3(pending, `${JSON.stringify(value, null, 2)}
|
|
1378
|
+
`, { mode: FILE_MODE2 });
|
|
1379
|
+
setMode(pending, FILE_MODE2);
|
|
1175
1380
|
renameSync(pending, file);
|
|
1176
1381
|
return file;
|
|
1177
1382
|
}
|
|
@@ -1190,8 +1395,8 @@ function saveCredentials(configHome, credentials) {
|
|
|
1190
1395
|
}
|
|
1191
1396
|
function deleteCredentials(configHome) {
|
|
1192
1397
|
const file = credentialsPath(configHome);
|
|
1193
|
-
if (!
|
|
1194
|
-
|
|
1398
|
+
if (!existsSync(file)) return false;
|
|
1399
|
+
rmSync3(file, { force: true });
|
|
1195
1400
|
return true;
|
|
1196
1401
|
}
|
|
1197
1402
|
function readAutoUpdate(value) {
|
|
@@ -1206,16 +1411,54 @@ function readAutoUpdate(value) {
|
|
|
1206
1411
|
function withAutoUpdate(value, records) {
|
|
1207
1412
|
return records === void 0 || Object.keys(records).length === 0 ? value : { ...value, autoUpdate: records };
|
|
1208
1413
|
}
|
|
1414
|
+
function isDefaultValue(value) {
|
|
1415
|
+
return typeof value === "string" && value.length > 0 && value.length <= MAX_DEFAULT_LENGTH && [...value].every((character) => character >= FIRST_PRINTABLE && character !== DELETE);
|
|
1416
|
+
}
|
|
1417
|
+
function readDefaults(value) {
|
|
1418
|
+
const stored = asObject(value);
|
|
1419
|
+
const defaults = {};
|
|
1420
|
+
for (const tool of ALL_TOOLS) {
|
|
1421
|
+
const recorded = stored[tool];
|
|
1422
|
+
if (isDefaultValue(recorded)) defaults[tool] = recorded;
|
|
1423
|
+
}
|
|
1424
|
+
return defaults;
|
|
1425
|
+
}
|
|
1426
|
+
function withDefaults(value, defaults) {
|
|
1427
|
+
return defaults === void 0 || Object.keys(defaults).length === 0 ? value : { ...value, defaults };
|
|
1428
|
+
}
|
|
1209
1429
|
function readState(configHome) {
|
|
1210
1430
|
const stored = readStoreFile(statePath(configHome));
|
|
1211
1431
|
if (stored === null || !Array.isArray(stored.tools)) return null;
|
|
1212
1432
|
const tools = unique(stored.tools.filter((tool) => typeof tool === "string" && isToolId(tool)));
|
|
1213
1433
|
const models2 = Array.isArray(stored.models) ? unique(stored.models.filter(isModelId)) : [];
|
|
1214
|
-
|
|
1434
|
+
const state = withAutoUpdate({ tools, models: models2, model: isModelId(stored.model) ? stored.model : null }, readAutoUpdate(stored.autoUpdate));
|
|
1435
|
+
return withDefaults(state, readDefaults(stored.defaults));
|
|
1215
1436
|
}
|
|
1216
1437
|
function saveState(configHome, state) {
|
|
1217
1438
|
const { tools, models: models2, model } = state;
|
|
1218
|
-
|
|
1439
|
+
const value = withDefaults(withAutoUpdate({ tools, models: models2, model }, state.autoUpdate), state.defaults);
|
|
1440
|
+
return writeStoreFile(configHome, statePath(configHome), value);
|
|
1441
|
+
}
|
|
1442
|
+
function deleteState(configHome) {
|
|
1443
|
+
const file = statePath(configHome);
|
|
1444
|
+
if (!existsSync(file)) return false;
|
|
1445
|
+
rmSync3(file, { force: true });
|
|
1446
|
+
return true;
|
|
1447
|
+
}
|
|
1448
|
+
function forgetTools(configHome, state, forgotten) {
|
|
1449
|
+
if (!state.tools.some((tool) => forgotten.includes(tool))) return;
|
|
1450
|
+
const tools = state.tools.filter((tool) => !forgotten.includes(tool));
|
|
1451
|
+
if (tools.length === 0) {
|
|
1452
|
+
deleteState(configHome);
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
const autoUpdate = { ...state.autoUpdate };
|
|
1456
|
+
const defaults = { ...state.defaults };
|
|
1457
|
+
for (const tool of forgotten) {
|
|
1458
|
+
if (isAutoUpdateTool(tool)) delete autoUpdate[tool];
|
|
1459
|
+
delete defaults[tool];
|
|
1460
|
+
}
|
|
1461
|
+
saveState(configHome, { ...state, tools, autoUpdate, defaults });
|
|
1219
1462
|
}
|
|
1220
1463
|
function mergeTools(previous, configured) {
|
|
1221
1464
|
return unique([...previous, ...configured]);
|
|
@@ -1634,7 +1877,7 @@ async function models(context) {
|
|
|
1634
1877
|
|
|
1635
1878
|
// cli/src/auto-update.ts
|
|
1636
1879
|
import { createHash } from "node:crypto";
|
|
1637
|
-
import { lstatSync, mkdirSync as
|
|
1880
|
+
import { lstatSync, mkdirSync as mkdirSync3, readFileSync as readFileSync4, renameSync as renameSync2, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1638
1881
|
import { dirname } from "node:path";
|
|
1639
1882
|
|
|
1640
1883
|
// cli/src/templates/opencode-plugin.ts?raw
|
|
@@ -1647,29 +1890,25 @@ var pi_extension_default = 'type ListedModel = {\n id: string;\n name: string;
|
|
|
1647
1890
|
import { spawnSync } from "node:child_process";
|
|
1648
1891
|
|
|
1649
1892
|
// cli/src/detect.ts
|
|
1650
|
-
import { accessSync, constants, readdirSync, statSync } from "node:fs";
|
|
1893
|
+
import { accessSync, constants, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
|
|
1651
1894
|
import { join as join2 } from "node:path";
|
|
1652
1895
|
var DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD";
|
|
1653
|
-
function
|
|
1896
|
+
function isFile2(path) {
|
|
1654
1897
|
try {
|
|
1655
|
-
return
|
|
1898
|
+
return statSync2(path).isFile();
|
|
1656
1899
|
} catch {
|
|
1657
1900
|
return false;
|
|
1658
1901
|
}
|
|
1659
1902
|
}
|
|
1660
1903
|
function isDirectory(path) {
|
|
1661
1904
|
try {
|
|
1662
|
-
return
|
|
1905
|
+
return statSync2(path).isDirectory();
|
|
1663
1906
|
} catch {
|
|
1664
1907
|
return false;
|
|
1665
1908
|
}
|
|
1666
1909
|
}
|
|
1667
|
-
function windowsVariable(env, name) {
|
|
1668
|
-
const key = Object.keys(env).find((candidate) => candidate.toUpperCase() === name);
|
|
1669
|
-
return key === void 0 ? void 0 : env[key];
|
|
1670
|
-
}
|
|
1671
1910
|
function isExecutable(path) {
|
|
1672
|
-
if (!
|
|
1911
|
+
if (!isFile2(path)) return false;
|
|
1673
1912
|
try {
|
|
1674
1913
|
accessSync(path, constants.X_OK);
|
|
1675
1914
|
return true;
|
|
@@ -1680,11 +1919,11 @@ function isExecutable(path) {
|
|
|
1680
1919
|
function hasWindowsCommand(dir, names) {
|
|
1681
1920
|
let entries;
|
|
1682
1921
|
try {
|
|
1683
|
-
entries =
|
|
1922
|
+
entries = readdirSync2(dir);
|
|
1684
1923
|
} catch {
|
|
1685
1924
|
return false;
|
|
1686
1925
|
}
|
|
1687
|
-
return entries.some((entry) => names.has(entry.toLowerCase()) &&
|
|
1926
|
+
return entries.some((entry) => names.has(entry.toLowerCase()) && isFile2(join2(dir, entry)));
|
|
1688
1927
|
}
|
|
1689
1928
|
function searchPath(value, platform) {
|
|
1690
1929
|
const { delimiter } = pathsFor(platform);
|
|
@@ -1702,14 +1941,14 @@ function onPath(name, env = process.env, platform = process.platform) {
|
|
|
1702
1941
|
function windowsCommandIn(dir, name, extensions) {
|
|
1703
1942
|
let entries;
|
|
1704
1943
|
try {
|
|
1705
|
-
entries =
|
|
1944
|
+
entries = readdirSync2(dir);
|
|
1706
1945
|
} catch {
|
|
1707
1946
|
return null;
|
|
1708
1947
|
}
|
|
1709
1948
|
for (const extension of extensions) {
|
|
1710
1949
|
const wanted = `${name}${extension}`.toLowerCase();
|
|
1711
1950
|
const entry = entries.find((candidate) => candidate.toLowerCase() === wanted);
|
|
1712
|
-
if (entry !== void 0 &&
|
|
1951
|
+
if (entry !== void 0 && isFile2(join2(dir, entry))) return join2(dir, entry);
|
|
1713
1952
|
}
|
|
1714
1953
|
return null;
|
|
1715
1954
|
}
|
|
@@ -1733,8 +1972,14 @@ function detect(tool, config, env = process.env) {
|
|
|
1733
1972
|
return found("opencode") || isDirectory(paths.opencodeDir);
|
|
1734
1973
|
case "codex":
|
|
1735
1974
|
return found("codex") || isDirectory(paths.codexDir);
|
|
1975
|
+
case "qwen-code":
|
|
1976
|
+
return found("qwen") || isDirectory(paths.qwenDir);
|
|
1977
|
+
case "goose":
|
|
1978
|
+
return found("goose") || isDirectory(paths.gooseDir);
|
|
1979
|
+
case "openhands":
|
|
1980
|
+
return found("openhands") || isDirectory(paths.openhandsDir);
|
|
1736
1981
|
case "aider":
|
|
1737
|
-
return found("aider") ||
|
|
1982
|
+
return found("aider") || isFile2(paths.aiderConfig);
|
|
1738
1983
|
case "continue":
|
|
1739
1984
|
return isDirectory(paths.continueDir);
|
|
1740
1985
|
case "crush":
|
|
@@ -1826,7 +2071,7 @@ function sha256(text) {
|
|
|
1826
2071
|
function isOwnFile(text, marker) {
|
|
1827
2072
|
return text.split(/\r?\n/).some((line) => line.trim() === marker);
|
|
1828
2073
|
}
|
|
1829
|
-
function
|
|
2074
|
+
function isMissing2(error) {
|
|
1830
2075
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
1831
2076
|
}
|
|
1832
2077
|
function readExisting(path, marker) {
|
|
@@ -1834,21 +2079,21 @@ function readExisting(path, marker) {
|
|
|
1834
2079
|
try {
|
|
1835
2080
|
stats = lstatSync(path);
|
|
1836
2081
|
} catch (error) {
|
|
1837
|
-
if (
|
|
2082
|
+
if (isMissing2(error)) return { kind: "missing" };
|
|
1838
2083
|
throw error;
|
|
1839
2084
|
}
|
|
1840
2085
|
if (!stats.isFile()) return { kind: "foreign" };
|
|
1841
|
-
const text =
|
|
2086
|
+
const text = readFileSync4(path, "utf8");
|
|
1842
2087
|
return isOwnFile(text, marker) ? { kind: "ours", text } : { kind: "foreign" };
|
|
1843
2088
|
}
|
|
1844
2089
|
function writeAtomically(path, text) {
|
|
1845
|
-
|
|
2090
|
+
mkdirSync3(dirname(path), { recursive: true });
|
|
1846
2091
|
const pending = `${path}.${process.pid}.tmp`;
|
|
1847
2092
|
try {
|
|
1848
|
-
|
|
2093
|
+
writeFileSync4(pending, text);
|
|
1849
2094
|
renameSync2(pending, path);
|
|
1850
2095
|
} finally {
|
|
1851
|
-
|
|
2096
|
+
rmSync4(pending, { force: true });
|
|
1852
2097
|
}
|
|
1853
2098
|
}
|
|
1854
2099
|
function writeAutoUpdateFile(tool, config, version2) {
|
|
@@ -1858,7 +2103,7 @@ function writeAutoUpdateFile(tool, config, version2) {
|
|
|
1858
2103
|
const existing = readExisting(path, file.marker);
|
|
1859
2104
|
if (!isAtLeast(version2, file.minimum)) {
|
|
1860
2105
|
if (existing.kind !== "ours") return { status: "unsupported" };
|
|
1861
|
-
|
|
2106
|
+
rmSync4(path, { force: true });
|
|
1862
2107
|
return { status: "removed", path };
|
|
1863
2108
|
}
|
|
1864
2109
|
if (existing.kind === "foreign") return { status: "foreign", path };
|
|
@@ -1867,6 +2112,17 @@ function writeAutoUpdateFile(tool, config, version2) {
|
|
|
1867
2112
|
writeAtomically(path, file.source);
|
|
1868
2113
|
return { status: "written", path, sha256: hash };
|
|
1869
2114
|
}
|
|
2115
|
+
function hasOwnAutoUpdateFile(tool, locations) {
|
|
2116
|
+
const file = AUTO_UPDATE_FILES[tool];
|
|
2117
|
+
return readExisting(file.path(toolPaths(locations)), file.marker).kind === "ours";
|
|
2118
|
+
}
|
|
2119
|
+
function removeAutoUpdateFile(tool, locations) {
|
|
2120
|
+
const file = AUTO_UPDATE_FILES[tool];
|
|
2121
|
+
const path = file.path(toolPaths(locations));
|
|
2122
|
+
if (readExisting(path, file.marker).kind !== "ours") return null;
|
|
2123
|
+
rmSync4(path, { force: true });
|
|
2124
|
+
return path;
|
|
2125
|
+
}
|
|
1870
2126
|
function installAutoUpdate(tool, config, versionOf) {
|
|
1871
2127
|
const file = AUTO_UPDATE_FILES[tool];
|
|
1872
2128
|
try {
|
|
@@ -1900,27 +2156,116 @@ function autoUpdateMessage(tool, result) {
|
|
|
1900
2156
|
}
|
|
1901
2157
|
|
|
1902
2158
|
// cli/src/writers/aider.ts
|
|
1903
|
-
|
|
1904
|
-
|
|
2159
|
+
var aider_exports = {};
|
|
2160
|
+
__export(aider_exports, {
|
|
2161
|
+
currentDefault: () => currentDefault,
|
|
2162
|
+
hasOwnEntries: () => hasOwnEntries,
|
|
2163
|
+
remove: () => remove,
|
|
2164
|
+
write: () => write
|
|
2165
|
+
});
|
|
2166
|
+
var TOOL = "aider";
|
|
2167
|
+
var SETTING = /^(openai-api-base|openai-api-key|model):\s*(.*?)\s*$/;
|
|
2168
|
+
var MODEL_PREFIX = "openai/";
|
|
2169
|
+
function setting(line) {
|
|
2170
|
+
const match = SETTING.exec(line);
|
|
2171
|
+
return match === null ? null : { key: match[1], value: match[2] };
|
|
2172
|
+
}
|
|
2173
|
+
function isModelLine(line) {
|
|
2174
|
+
return setting(line)?.key === "model";
|
|
2175
|
+
}
|
|
2176
|
+
function isOwnLine(line, owner) {
|
|
2177
|
+
const found = setting(line);
|
|
2178
|
+
if (found === null) return false;
|
|
2179
|
+
switch (found.key) {
|
|
2180
|
+
case "openai-api-base":
|
|
2181
|
+
return found.value === owner.base;
|
|
2182
|
+
case "openai-api-key":
|
|
2183
|
+
return found.value.startsWith(KEY_PREFIX);
|
|
2184
|
+
default:
|
|
2185
|
+
return found.value.startsWith(MODEL_PREFIX) && isOwnModel(found.value.slice(MODEL_PREFIX.length), owner);
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
function write(config, options) {
|
|
1905
2189
|
const file = toolPaths(config).aiderConfig;
|
|
1906
|
-
|
|
1907
|
-
const
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
kept
|
|
1912
|
-
|
|
1913
|
-
|
|
2190
|
+
const raw = readText(file);
|
|
2191
|
+
const replaced = (line) => {
|
|
2192
|
+
const key = setting(line)?.key;
|
|
2193
|
+
return key !== void 0 && (key !== "model" || options.setDefault);
|
|
2194
|
+
};
|
|
2195
|
+
const kept = splitLines(toLf(raw ?? "")).filter((line) => !replaced(line));
|
|
2196
|
+
kept.push(`openai-api-base: ${config.base}`, `openai-api-key: ${config.key}`);
|
|
2197
|
+
if (options.setDefault) kept.push(`model: ${MODEL_PREFIX}${config.model}`);
|
|
2198
|
+
saveText(config, TOOL, file, fromLf(`${kept.join("\n")}
|
|
2199
|
+
`, layoutOf(raw ?? "")), raw);
|
|
1914
2200
|
setMode(file, 384, config.platform);
|
|
1915
2201
|
return { path: file, next: "aider" };
|
|
1916
2202
|
}
|
|
2203
|
+
function currentDefault(config) {
|
|
2204
|
+
let raw;
|
|
2205
|
+
try {
|
|
2206
|
+
raw = readText(toolPaths(config).aiderConfig);
|
|
2207
|
+
} catch {
|
|
2208
|
+
return null;
|
|
2209
|
+
}
|
|
2210
|
+
const models2 = splitLines(toLf(raw ?? "")).map(setting).filter((found) => found !== null && found.key === "model" && found.value !== "");
|
|
2211
|
+
return models2.at(-1)?.value ?? null;
|
|
2212
|
+
}
|
|
2213
|
+
function removal(config, previousDefault) {
|
|
2214
|
+
const file = toolPaths(config).aiderConfig;
|
|
2215
|
+
const raw = readText(file);
|
|
2216
|
+
if (raw === null) return [];
|
|
2217
|
+
const lines = splitLines(toLf(raw));
|
|
2218
|
+
const own = (line) => isOwnLine(line, config);
|
|
2219
|
+
if (!lines.some(own)) return [];
|
|
2220
|
+
const kept = lines.filter((line) => !own(line));
|
|
2221
|
+
const lastModel = lines.findLastIndex(isModelLine);
|
|
2222
|
+
if (previousDefault !== null && lastModel !== -1 && own(lines[lastModel]) && !kept.some(isModelLine)) {
|
|
2223
|
+
kept.splice(lines.slice(0, lastModel).filter((line) => !own(line)).length, 0, `model: ${previousDefault}`);
|
|
2224
|
+
}
|
|
2225
|
+
return [{ file, raw, next: kept.every(isBlankText) ? null : fromLf(`${kept.join("\n")}
|
|
2226
|
+
`, layoutOf(raw)) }];
|
|
2227
|
+
}
|
|
2228
|
+
function remove(config, previousDefault) {
|
|
2229
|
+
const changes = removal(config, previousDefault);
|
|
2230
|
+
applyChanges(config, TOOL, changes);
|
|
2231
|
+
return { path: toolPaths(config).aiderConfig, changed: changes.length > 0 };
|
|
2232
|
+
}
|
|
2233
|
+
function hasOwnEntries(config) {
|
|
2234
|
+
return removal(config, null).length > 0;
|
|
2235
|
+
}
|
|
1917
2236
|
|
|
1918
2237
|
// cli/src/writers/claude.ts
|
|
1919
|
-
|
|
2238
|
+
var claude_exports = {};
|
|
2239
|
+
__export(claude_exports, {
|
|
2240
|
+
apiOrigin: () => apiOrigin,
|
|
2241
|
+
currentDefault: () => currentDefault2,
|
|
2242
|
+
hasOwnEntries: () => hasOwnEntries2,
|
|
2243
|
+
launcherPath: () => launcherPath,
|
|
2244
|
+
remove: () => remove2,
|
|
2245
|
+
settingsPath: () => settingsPath,
|
|
2246
|
+
write: () => write2
|
|
2247
|
+
});
|
|
2248
|
+
import { mkdirSync as mkdirSync4, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
1920
2249
|
import { dirname as dirname2, resolve } from "node:path";
|
|
2250
|
+
var TOOL2 = "claude";
|
|
2251
|
+
var SESSION_ENV_KEYS = [
|
|
2252
|
+
"ANTHROPIC_BASE_URL",
|
|
2253
|
+
"ANTHROPIC_AUTH_TOKEN",
|
|
2254
|
+
"ANTHROPIC_MODEL",
|
|
2255
|
+
"ANTHROPIC_SMALL_FAST_MODEL",
|
|
2256
|
+
"CLAUDE_CODE_MAX_CONTEXT_TOKENS",
|
|
2257
|
+
"CLAUDE_CODE_DISABLE_UNKNOWN_MODEL_WINDOW_ENFORCEMENT"
|
|
2258
|
+
];
|
|
2259
|
+
var MODEL_PICKER = "modelPicker";
|
|
1921
2260
|
function apiOrigin(base) {
|
|
1922
2261
|
return base.replace(/\/v1$/, "");
|
|
1923
2262
|
}
|
|
2263
|
+
function settingsPath(config) {
|
|
2264
|
+
return toolPaths(config).claudeSessionSettings;
|
|
2265
|
+
}
|
|
2266
|
+
function launcherPath(config) {
|
|
2267
|
+
return toolPaths(config).claudeLauncher;
|
|
2268
|
+
}
|
|
1924
2269
|
function underHome(path, home) {
|
|
1925
2270
|
return path.startsWith(`${home}/`) ? path.slice(home.length + 1) : "";
|
|
1926
2271
|
}
|
|
@@ -1932,50 +2277,61 @@ function shellPath(path, home) {
|
|
|
1932
2277
|
const rest = underHome(path, home);
|
|
1933
2278
|
return rest ? `"$HOME/${rest}"` : `"${path}"`;
|
|
1934
2279
|
}
|
|
2280
|
+
function launcherText(settings, home) {
|
|
2281
|
+
return `#!/bin/sh
|
|
2282
|
+
exec claude --settings ${shellPath(settings, home)} "$@"
|
|
2283
|
+
`;
|
|
2284
|
+
}
|
|
1935
2285
|
function pathContains(dir, env, platform) {
|
|
1936
2286
|
return (env.PATH ?? "").split(pathsFor(platform).delimiter).filter(Boolean).some((entry) => resolve(entry) === resolve(dir));
|
|
1937
2287
|
}
|
|
1938
|
-
function isOurs(name, value,
|
|
2288
|
+
function isOurs(name, value, owner) {
|
|
1939
2289
|
if (typeof value !== "string") return false;
|
|
1940
2290
|
switch (name) {
|
|
1941
2291
|
case "ANTHROPIC_BASE_URL":
|
|
1942
|
-
return value === apiOrigin(
|
|
2292
|
+
return value === apiOrigin(owner.base);
|
|
1943
2293
|
case "ANTHROPIC_AUTH_TOKEN":
|
|
1944
2294
|
return value.startsWith(KEY_PREFIX);
|
|
1945
2295
|
case "ANTHROPIC_MODEL":
|
|
1946
2296
|
case "ANTHROPIC_SMALL_FAST_MODEL":
|
|
1947
|
-
return isOwnModel(value,
|
|
2297
|
+
return isOwnModel(value, owner);
|
|
1948
2298
|
default:
|
|
1949
2299
|
return false;
|
|
1950
2300
|
}
|
|
1951
2301
|
}
|
|
1952
|
-
function
|
|
1953
|
-
const file = toolPaths(config).claudeGlobalSettings;
|
|
1954
|
-
if (!existsSync4(file)) return void 0;
|
|
1955
|
-
let document;
|
|
1956
|
-
try {
|
|
1957
|
-
document = readJsonDocument(file);
|
|
1958
|
-
} catch {
|
|
1959
|
-
return void 0;
|
|
1960
|
-
}
|
|
1961
|
-
const settings = document.value;
|
|
2302
|
+
function withoutOwnGlobalEnv(settings, owner) {
|
|
1962
2303
|
const env = asObject(settings.env);
|
|
1963
2304
|
const kept = {};
|
|
1964
2305
|
let removed = 0;
|
|
1965
2306
|
for (const [name, value] of Object.entries(env)) {
|
|
1966
|
-
if (isOurs(name, value,
|
|
2307
|
+
if (isOurs(name, value, owner)) removed += 1;
|
|
1967
2308
|
else kept[name] = value;
|
|
1968
2309
|
}
|
|
1969
|
-
if (removed === 0) return
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
2310
|
+
if (removed === 0) return null;
|
|
2311
|
+
const next = { ...settings, env: kept };
|
|
2312
|
+
if (Object.keys(kept).length === 0) delete next.env;
|
|
2313
|
+
return next;
|
|
2314
|
+
}
|
|
2315
|
+
function readGlobalSettings(file) {
|
|
2316
|
+
try {
|
|
2317
|
+
const document = readJsonDocument(file);
|
|
2318
|
+
return document.raw === null ? null : document;
|
|
2319
|
+
} catch {
|
|
2320
|
+
return null;
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
function removeGlobalEnv(config) {
|
|
2324
|
+
const file = toolPaths(config).claudeGlobalSettings;
|
|
2325
|
+
const document = readGlobalSettings(file);
|
|
2326
|
+
const next = document === null ? null : withoutOwnGlobalEnv(document.value, config);
|
|
2327
|
+
if (document === null || next === null) return void 0;
|
|
2328
|
+
saveText(config, TOOL2, file, jsonText(next, document.layout), document.raw);
|
|
1973
2329
|
return `Removed the old global unrestricted.sh env from ${displayPath(file, config.home)}`;
|
|
1974
2330
|
}
|
|
1975
|
-
function write2(config, env = process.env) {
|
|
2331
|
+
function write2(config, _options, env = process.env) {
|
|
1976
2332
|
const paths = toolPaths(config);
|
|
1977
2333
|
const file = paths.claudeSessionSettings;
|
|
1978
|
-
|
|
2334
|
+
mkdirSync4(dirname2(file), { recursive: true });
|
|
1979
2335
|
const document = readJsonDocument(file);
|
|
1980
2336
|
const settings = document.value;
|
|
1981
2337
|
settings.env = {
|
|
@@ -1987,63 +2343,129 @@ function write2(config, env = process.env) {
|
|
|
1987
2343
|
CLAUDE_CODE_MAX_CONTEXT_TOKENS: String(config.context),
|
|
1988
2344
|
CLAUDE_CODE_DISABLE_UNKNOWN_MODEL_WINDOW_ENFORCEMENT: "1"
|
|
1989
2345
|
};
|
|
1990
|
-
settings
|
|
2346
|
+
settings[MODEL_PICKER] = {
|
|
1991
2347
|
options: [{ model: config.model, label: config.modelName, description: "unrestricted.sh" }],
|
|
1992
2348
|
replaceBuiltInOptions: true
|
|
1993
2349
|
};
|
|
1994
|
-
|
|
2350
|
+
saveText(config, TOOL2, file, jsonText(settings, document.layout), document.raw);
|
|
1995
2351
|
setMode(file, 384, config.platform);
|
|
1996
2352
|
const launcher = paths.claudeLauncher;
|
|
1997
2353
|
const bin = paths.claudeLauncherDir;
|
|
1998
|
-
|
|
1999
|
-
writeFileSync5(launcher,
|
|
2000
|
-
exec claude --settings ${shellPath(file, config.home)} "$@"
|
|
2001
|
-
`);
|
|
2354
|
+
mkdirSync4(bin, { recursive: true });
|
|
2355
|
+
writeFileSync5(launcher, launcherText(file, config.home));
|
|
2002
2356
|
setMode(launcher, 493, config.platform);
|
|
2003
2357
|
const hint = removeGlobalEnv(config);
|
|
2004
2358
|
const next = pathContains(bin, env, config.platform) ? CLAUDE_LAUNCHER : `claude --settings ${displayPath(file, config.home)} (or add ~/.local/bin to PATH for ${CLAUDE_LAUNCHER})`;
|
|
2005
2359
|
return hint ? { path: file, next, hint } : { path: file, next };
|
|
2006
2360
|
}
|
|
2361
|
+
function currentDefault2(_config) {
|
|
2362
|
+
return null;
|
|
2363
|
+
}
|
|
2364
|
+
function withoutOwnSessionSettings(settings) {
|
|
2365
|
+
const env = asObject(settings.env);
|
|
2366
|
+
const keptEnv = Object.fromEntries(Object.entries(env).filter(([name]) => !SESSION_ENV_KEYS.includes(name)));
|
|
2367
|
+
const ownEnv = Object.keys(keptEnv).length < Object.keys(env).length;
|
|
2368
|
+
if (!ownEnv && !(MODEL_PICKER in settings)) return null;
|
|
2369
|
+
const next = { ...settings, env: keptEnv };
|
|
2370
|
+
if (Object.keys(keptEnv).length === 0) delete next.env;
|
|
2371
|
+
delete next[MODEL_PICKER];
|
|
2372
|
+
return next;
|
|
2373
|
+
}
|
|
2374
|
+
function changeTo(file, document, value) {
|
|
2375
|
+
return { file, raw: document.raw, next: isBareDocument(value) ? null : jsonText(value, document.layout) };
|
|
2376
|
+
}
|
|
2377
|
+
function removal2(config) {
|
|
2378
|
+
const paths = toolPaths(config);
|
|
2379
|
+
const changes = [];
|
|
2380
|
+
const session = readJsonDocument(paths.claudeSessionSettings);
|
|
2381
|
+
const cleaned = session.raw === null ? null : withoutOwnSessionSettings(session.value);
|
|
2382
|
+
if (cleaned !== null) changes.push(changeTo(paths.claudeSessionSettings, session, cleaned));
|
|
2383
|
+
const global = readGlobalSettings(paths.claudeGlobalSettings);
|
|
2384
|
+
const globalCleaned = global === null ? null : withoutOwnGlobalEnv(global.value, config);
|
|
2385
|
+
if (global !== null && globalCleaned !== null) changes.push(changeTo(paths.claudeGlobalSettings, global, globalCleaned));
|
|
2386
|
+
const launcher = readText(paths.claudeLauncher);
|
|
2387
|
+
const ownLauncher = launcher !== null && launcher === launcherText(paths.claudeSessionSettings, config.home);
|
|
2388
|
+
return { changes, launcher: ownLauncher ? paths.claudeLauncher : null };
|
|
2389
|
+
}
|
|
2390
|
+
function remove2(config, _previousDefault) {
|
|
2391
|
+
const plan = removal2(config);
|
|
2392
|
+
applyChanges(config, TOOL2, plan.changes);
|
|
2393
|
+
if (plan.launcher !== null) rmSync5(plan.launcher, { force: true });
|
|
2394
|
+
return {
|
|
2395
|
+
path: plan.changes[0]?.file ?? plan.launcher ?? toolPaths(config).claudeSessionSettings,
|
|
2396
|
+
changed: plan.changes.length > 0 || plan.launcher !== null
|
|
2397
|
+
};
|
|
2398
|
+
}
|
|
2399
|
+
function hasOwnEntries2(config) {
|
|
2400
|
+
const plan = removal2(config);
|
|
2401
|
+
return plan.changes.length > 0 || plan.launcher !== null;
|
|
2402
|
+
}
|
|
2007
2403
|
|
|
2008
2404
|
// cli/src/writers/codex.ts
|
|
2009
|
-
|
|
2405
|
+
var codex_exports = {};
|
|
2406
|
+
__export(codex_exports, {
|
|
2407
|
+
applyTopLevelKeys: () => applyTopLevelKeys,
|
|
2408
|
+
currentDefault: () => currentDefault3,
|
|
2409
|
+
hasOwnEntries: () => hasOwnEntries3,
|
|
2410
|
+
remove: () => remove3,
|
|
2411
|
+
startCommand: () => startCommand,
|
|
2412
|
+
write: () => write3
|
|
2413
|
+
});
|
|
2414
|
+
import { mkdirSync as mkdirSync5, rmSync as rmSync6 } from "node:fs";
|
|
2010
2415
|
|
|
2011
2416
|
// cli/src/env-file.ts
|
|
2012
|
-
import { appendFileSync, mkdirSync as mkdirSync4, readFileSync as readFileSync5, statSync as statSync2, writeFileSync as writeFileSync6 } from "node:fs";
|
|
2013
2417
|
import { join as join3 } from "node:path";
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
return false;
|
|
2019
|
-
}
|
|
2418
|
+
var ENV_FILE = "env";
|
|
2419
|
+
var KEY_EXPORT = new RegExp(`^export ${API_KEY_ENV}=unr_sk_\\S*$`);
|
|
2420
|
+
function envFilePath(config) {
|
|
2421
|
+
return join3(config.configHome, STORE_DIR_NAME, ENV_FILE);
|
|
2020
2422
|
}
|
|
2021
|
-
function
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2423
|
+
function shellProfiles(config) {
|
|
2424
|
+
return [join3(config.home, ".zshrc"), join3(config.home, ".bashrc")];
|
|
2425
|
+
}
|
|
2426
|
+
function sourceLine(file) {
|
|
2427
|
+
return `. "${file}"`;
|
|
2428
|
+
}
|
|
2429
|
+
function isOwnEnvFile(text) {
|
|
2430
|
+
const lines = splitLines(toLf(text)).filter((line) => !isBlankText(line));
|
|
2431
|
+
return lines.length > 0 && lines.every((line) => KEY_EXPORT.test(line));
|
|
2432
|
+
}
|
|
2433
|
+
function withoutSourceLine(lines, source) {
|
|
2434
|
+
const kept = [];
|
|
2435
|
+
for (const line of lines) {
|
|
2436
|
+
if (line !== source) kept.push(line);
|
|
2437
|
+
else if (kept.length > 0 && kept[kept.length - 1] === "") kept.pop();
|
|
2035
2438
|
}
|
|
2036
|
-
return
|
|
2439
|
+
return kept;
|
|
2440
|
+
}
|
|
2441
|
+
function envFileRemoval(config) {
|
|
2442
|
+
const file = envFilePath(config);
|
|
2443
|
+
const text = readText(file);
|
|
2444
|
+
if (text === null || !isOwnEnvFile(text)) return { envFile: null, changes: [] };
|
|
2445
|
+
const changes = [];
|
|
2446
|
+
for (const rc of shellProfiles(config)) {
|
|
2447
|
+
const raw = isFile(rc) ? readText(rc) : null;
|
|
2448
|
+
if (raw === null) continue;
|
|
2449
|
+
const lines = splitLines(toLf(raw));
|
|
2450
|
+
const kept = withoutSourceLine(lines, sourceLine(file));
|
|
2451
|
+
if (kept.length === lines.length) continue;
|
|
2452
|
+
changes.push({ file: rc, raw, next: fromLf(kept.length === 0 ? "" : `${kept.join("\n")}
|
|
2453
|
+
`, layoutOf(raw)) });
|
|
2454
|
+
}
|
|
2455
|
+
return { envFile: file, changes };
|
|
2037
2456
|
}
|
|
2038
2457
|
|
|
2039
2458
|
// cli/src/writers/codex.ts
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2459
|
+
var TOOL3 = "codex";
|
|
2460
|
+
var WIRE_API = "responses";
|
|
2461
|
+
var PROVIDER_KEY = /^model_provider[ \t]*=/;
|
|
2462
|
+
var MODEL_KEY = /^model[ \t]*=/;
|
|
2463
|
+
var STRING_VALUE = /^[A-Za-z_]+[ \t]*=[ \t]*"([^"]*)"/;
|
|
2464
|
+
var PROVIDER_SEPARATOR = "/";
|
|
2465
|
+
var LEGACY_ENV_KEY_LINE = `env_key = "${API_KEY_ENV}"`;
|
|
2044
2466
|
function applyTopLevelKeys(content, provider, model) {
|
|
2045
2467
|
const providerLine = `model_provider = "${provider}"`;
|
|
2046
|
-
const
|
|
2468
|
+
const modelLine3 = `model = "${model}"`;
|
|
2047
2469
|
const lines = [];
|
|
2048
2470
|
let inTable = false;
|
|
2049
2471
|
let seenProvider = false;
|
|
@@ -2052,7 +2474,7 @@ function applyTopLevelKeys(content, provider, model) {
|
|
|
2052
2474
|
if (line.startsWith("[")) {
|
|
2053
2475
|
if (!inTable) {
|
|
2054
2476
|
if (!seenProvider) lines.push(providerLine);
|
|
2055
|
-
if (!seenModel) lines.push(
|
|
2477
|
+
if (!seenModel) lines.push(modelLine3);
|
|
2056
2478
|
seenProvider = true;
|
|
2057
2479
|
seenModel = true;
|
|
2058
2480
|
}
|
|
@@ -2060,49 +2482,167 @@ function applyTopLevelKeys(content, provider, model) {
|
|
|
2060
2482
|
lines.push(line);
|
|
2061
2483
|
continue;
|
|
2062
2484
|
}
|
|
2063
|
-
if (!inTable &&
|
|
2485
|
+
if (!inTable && PROVIDER_KEY.test(line)) {
|
|
2064
2486
|
lines.push(providerLine);
|
|
2065
2487
|
seenProvider = true;
|
|
2066
2488
|
continue;
|
|
2067
2489
|
}
|
|
2068
|
-
if (!inTable &&
|
|
2069
|
-
lines.push(
|
|
2490
|
+
if (!inTable && MODEL_KEY.test(line)) {
|
|
2491
|
+
lines.push(modelLine3);
|
|
2070
2492
|
seenModel = true;
|
|
2071
2493
|
continue;
|
|
2072
2494
|
}
|
|
2073
2495
|
lines.push(line);
|
|
2074
2496
|
}
|
|
2075
2497
|
if (!seenProvider) lines.push(providerLine);
|
|
2076
|
-
if (!seenModel) lines.push(
|
|
2498
|
+
if (!seenModel) lines.push(modelLine3);
|
|
2077
2499
|
return `${lines.join("\n")}
|
|
2078
2500
|
`;
|
|
2079
2501
|
}
|
|
2080
|
-
function
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2502
|
+
function tableHeader(provider) {
|
|
2503
|
+
return `[model_providers.${provider}]`;
|
|
2504
|
+
}
|
|
2505
|
+
function tableKeys(config) {
|
|
2506
|
+
return [
|
|
2507
|
+
{ name: "name", value: PROVIDER_NAME },
|
|
2508
|
+
{ name: "base_url", value: config.base },
|
|
2509
|
+
{ name: "experimental_bearer_token", value: config.key },
|
|
2510
|
+
{ name: "wire_api", value: WIRE_API }
|
|
2511
|
+
];
|
|
2512
|
+
}
|
|
2513
|
+
function keyLine(key) {
|
|
2514
|
+
return `${key.name} = "${key.value}"`;
|
|
2515
|
+
}
|
|
2516
|
+
function keyPattern(name) {
|
|
2517
|
+
return new RegExp(`^${name}[ \\t]*=`);
|
|
2518
|
+
}
|
|
2519
|
+
function tableEnd(lines, start2) {
|
|
2520
|
+
const next = lines.findIndex((line, index) => index > start2 && line.startsWith("["));
|
|
2521
|
+
return next === -1 ? lines.length : next;
|
|
2522
|
+
}
|
|
2523
|
+
function withOwnTable(content, config) {
|
|
2524
|
+
const lines = splitLines(content);
|
|
2525
|
+
const header = tableHeader(config.provider);
|
|
2526
|
+
const start2 = lines.findIndex((line) => line.trim() === header);
|
|
2527
|
+
if (start2 === -1) return `${content}
|
|
2528
|
+
${[header, ...tableKeys(config).map(keyLine)].join("\n")}
|
|
2094
2529
|
`;
|
|
2530
|
+
const end = tableEnd(lines, start2);
|
|
2531
|
+
const body = lines.slice(start2 + 1, end).filter((line) => line.trim() !== LEGACY_ENV_KEY_LINE);
|
|
2532
|
+
for (const key of tableKeys(config)) {
|
|
2533
|
+
const at = body.findIndex((line) => keyPattern(key.name).test(line));
|
|
2534
|
+
if (at !== -1) {
|
|
2535
|
+
body[at] = keyLine(key);
|
|
2536
|
+
continue;
|
|
2537
|
+
}
|
|
2538
|
+
const lastContent = body.findLastIndex((line) => !isBlankText(line));
|
|
2539
|
+
body.splice(lastContent + 1, 0, keyLine(key));
|
|
2095
2540
|
}
|
|
2096
|
-
|
|
2541
|
+
return `${[...lines.slice(0, start2 + 1), ...body, ...lines.slice(end)].join("\n")}
|
|
2542
|
+
`;
|
|
2543
|
+
}
|
|
2544
|
+
function topLevelEnd(lines) {
|
|
2545
|
+
const firstTable = lines.findIndex((line) => line.startsWith("["));
|
|
2546
|
+
return firstTable === -1 ? lines.length : firstTable;
|
|
2547
|
+
}
|
|
2548
|
+
function stringValue(line) {
|
|
2549
|
+
const match = line === void 0 ? null : STRING_VALUE.exec(line);
|
|
2550
|
+
return match === null ? null : match[1];
|
|
2551
|
+
}
|
|
2552
|
+
function defaultIn(content) {
|
|
2553
|
+
const lines = splitLines(content);
|
|
2554
|
+
const top = lines.slice(0, topLevelEnd(lines));
|
|
2555
|
+
const model = stringValue(top.find((line) => MODEL_KEY.test(line)));
|
|
2556
|
+
if (model === null || model === "") return null;
|
|
2557
|
+
const provider = stringValue(top.find((line) => PROVIDER_KEY.test(line)));
|
|
2558
|
+
return provider === null || provider === "" ? model : `${provider}${PROVIDER_SEPARATOR}${model}`;
|
|
2559
|
+
}
|
|
2560
|
+
function startCommand(config) {
|
|
2561
|
+
return `codex -c model_provider=${config.provider} -m ${config.model}`;
|
|
2562
|
+
}
|
|
2563
|
+
function write3(config, options) {
|
|
2564
|
+
const { codexDir: dir, codexConfig: file } = toolPaths(config);
|
|
2565
|
+
mkdirSync5(dir, { recursive: true });
|
|
2566
|
+
const raw = readText(file);
|
|
2567
|
+
const content = withOwnTable(toLf(raw ?? ""), config);
|
|
2568
|
+
const updated = options.setDefault ? applyTopLevelKeys(content, config.provider, config.model) : content;
|
|
2569
|
+
saveText(config, TOOL3, file, fromLf(updated, layoutOf(raw ?? "")), raw);
|
|
2097
2570
|
setMode(file, 384, config.platform);
|
|
2098
|
-
const
|
|
2099
|
-
return { path: file, next:
|
|
2571
|
+
const startsOnOwnModel = defaultIn(updated)?.startsWith(`${config.provider}${PROVIDER_SEPARATOR}`) ?? false;
|
|
2572
|
+
return { path: file, next: startsOnOwnModel ? "codex" : startCommand(config) };
|
|
2573
|
+
}
|
|
2574
|
+
function currentDefault3(config) {
|
|
2575
|
+
let raw;
|
|
2576
|
+
try {
|
|
2577
|
+
raw = readText(toolPaths(config).codexConfig);
|
|
2578
|
+
} catch {
|
|
2579
|
+
return null;
|
|
2580
|
+
}
|
|
2581
|
+
return defaultIn(toLf(raw ?? ""));
|
|
2582
|
+
}
|
|
2583
|
+
function restoredLines(previousDefault) {
|
|
2584
|
+
const slash = previousDefault.indexOf(PROVIDER_SEPARATOR);
|
|
2585
|
+
if (slash <= 0) return [`model = "${previousDefault}"`];
|
|
2586
|
+
return [`model_provider = "${previousDefault.slice(0, slash)}"`, `model = "${previousDefault.slice(slash + 1)}"`];
|
|
2587
|
+
}
|
|
2588
|
+
function withoutOwnSettings(raw, owner, previousDefault) {
|
|
2589
|
+
const lines = splitLines(toLf(raw));
|
|
2590
|
+
const end = topLevelEnd(lines);
|
|
2591
|
+
const providerAt = lines.findIndex((line, index) => index < end && PROVIDER_KEY.test(line));
|
|
2592
|
+
const modelAt = lines.findIndex((line, index) => index < end && MODEL_KEY.test(line));
|
|
2593
|
+
const model = stringValue(lines[modelAt]);
|
|
2594
|
+
const ownProvider2 = providerAt !== -1 && stringValue(lines[providerAt]) === owner.provider;
|
|
2595
|
+
const ownDefault = ownProvider2 && model !== null && isOwnModel(model, owner);
|
|
2596
|
+
const dropped = /* @__PURE__ */ new Set();
|
|
2597
|
+
if (ownProvider2) dropped.add(providerAt);
|
|
2598
|
+
if (ownDefault) dropped.add(modelAt);
|
|
2599
|
+
const tableAt = lines.findIndex((line) => line.trim() === tableHeader(owner.provider));
|
|
2600
|
+
if (tableAt !== -1) {
|
|
2601
|
+
if (tableAt > 0 && isBlankText(lines[tableAt - 1])) dropped.add(tableAt - 1);
|
|
2602
|
+
for (let index = tableAt; index < tableEnd(lines, tableAt); index += 1) dropped.add(index);
|
|
2603
|
+
}
|
|
2604
|
+
if (dropped.size === 0) return void 0;
|
|
2605
|
+
const kept = lines.filter((_line, index) => !dropped.has(index));
|
|
2606
|
+
if (ownDefault && previousDefault !== null) {
|
|
2607
|
+
const firstOwn = Math.min(...[providerAt, modelAt].filter((index) => index !== -1));
|
|
2608
|
+
kept.splice(lines.slice(0, firstOwn).filter((_line, index) => !dropped.has(index)).length, 0, ...restoredLines(previousDefault));
|
|
2609
|
+
}
|
|
2610
|
+
return kept.every(isBlankText) ? null : fromLf(`${kept.join("\n")}
|
|
2611
|
+
`, layoutOf(raw));
|
|
2612
|
+
}
|
|
2613
|
+
function removal3(config, previousDefault) {
|
|
2614
|
+
const file = toolPaths(config).codexConfig;
|
|
2615
|
+
const raw = readText(file);
|
|
2616
|
+
const next = raw === null ? void 0 : withoutOwnSettings(raw, config, previousDefault);
|
|
2617
|
+
const env = envFileRemoval(config);
|
|
2618
|
+
const changes = next === void 0 ? [] : [{ file, raw, next }];
|
|
2619
|
+
return { changes: [...changes, ...env.changes], envFile: env.envFile };
|
|
2620
|
+
}
|
|
2621
|
+
function remove3(config, previousDefault) {
|
|
2622
|
+
const plan = removal3(config, previousDefault);
|
|
2623
|
+
applyChanges(config, TOOL3, plan.changes);
|
|
2624
|
+
if (plan.envFile !== null) rmSync6(plan.envFile, { force: true });
|
|
2625
|
+
const file = toolPaths(config).codexConfig;
|
|
2626
|
+
return { path: plan.changes[0]?.file ?? plan.envFile ?? file, changed: plan.changes.length > 0 || plan.envFile !== null };
|
|
2627
|
+
}
|
|
2628
|
+
function hasOwnEntries3(config) {
|
|
2629
|
+
const plan = removal3(config, null);
|
|
2630
|
+
return plan.changes.length > 0 || plan.envFile !== null;
|
|
2100
2631
|
}
|
|
2101
2632
|
|
|
2102
2633
|
// cli/src/writers/continue.ts
|
|
2103
|
-
|
|
2634
|
+
var continue_exports = {};
|
|
2635
|
+
__export(continue_exports, {
|
|
2636
|
+
currentDefault: () => currentDefault4,
|
|
2637
|
+
hasOwnEntries: () => hasOwnEntries4,
|
|
2638
|
+
remove: () => remove4,
|
|
2639
|
+
write: () => write4
|
|
2640
|
+
});
|
|
2641
|
+
import { mkdirSync as mkdirSync6 } from "node:fs";
|
|
2642
|
+
var TOOL4 = "continue";
|
|
2104
2643
|
var ITEM_START = " - ";
|
|
2105
2644
|
var ITEM_FIELD = /^ {4}([A-Za-z]+):\s*(.*?)\s*$/;
|
|
2645
|
+
var CREATED_HEADER = ["name: unrestricted.sh", "version: 0.0.1", "schema: v1", "models:"];
|
|
2106
2646
|
function modelEntry(config, model) {
|
|
2107
2647
|
return [
|
|
2108
2648
|
` - name: unrestricted.sh ${model.name}`,
|
|
@@ -2123,10 +2663,6 @@ function lastTopLevelLine(content) {
|
|
|
2123
2663
|
const lines = content.split("\n").filter((line) => /^[A-Za-z]/.test(line));
|
|
2124
2664
|
return lines.length ? lines[lines.length - 1] : "";
|
|
2125
2665
|
}
|
|
2126
|
-
function splitLines2(content) {
|
|
2127
|
-
if (content === "") return [];
|
|
2128
|
-
return (content.endsWith("\n") ? content.slice(0, -1) : content).split("\n");
|
|
2129
|
-
}
|
|
2130
2666
|
function modelItems(lines) {
|
|
2131
2667
|
const header = lines.findIndex((line) => /^models:\s*$/.test(line));
|
|
2132
2668
|
if (header === -1) return [];
|
|
@@ -2147,12 +2683,12 @@ function itemField(lines, item, field) {
|
|
|
2147
2683
|
}
|
|
2148
2684
|
return void 0;
|
|
2149
2685
|
}
|
|
2150
|
-
function isOwnItem(lines, item,
|
|
2686
|
+
function isOwnItem(lines, item, owner) {
|
|
2151
2687
|
const model = itemField(lines, item, "model");
|
|
2152
|
-
return model !== void 0 && isOwnModel(model,
|
|
2688
|
+
return model !== void 0 && isOwnModel(model, owner) && itemField(lines, item, "apiBase") === owner.base;
|
|
2153
2689
|
}
|
|
2154
2690
|
function replaceOwnItems(content, entries, config) {
|
|
2155
|
-
const lines =
|
|
2691
|
+
const lines = splitLines(content);
|
|
2156
2692
|
const own = modelItems(lines).filter((item) => isOwnItem(lines, item, config));
|
|
2157
2693
|
if (own.length === 0) return null;
|
|
2158
2694
|
const updated = [];
|
|
@@ -2166,81 +2702,460 @@ function replaceOwnItems(content, entries, config) {
|
|
|
2166
2702
|
return `${updated.join("\n")}
|
|
2167
2703
|
`;
|
|
2168
2704
|
}
|
|
2169
|
-
function write4(config) {
|
|
2705
|
+
function write4(config, _options) {
|
|
2170
2706
|
const { continueDir: dir, continueConfig: file } = toolPaths(config);
|
|
2171
2707
|
mkdirSync6(dir, { recursive: true });
|
|
2172
2708
|
const entries = modelEntries(config);
|
|
2173
2709
|
const next = `reload Continue and pick "unrestricted.sh ${config.modelName}"`;
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
schema: v1
|
|
2178
|
-
models:
|
|
2710
|
+
const raw = readText(file);
|
|
2711
|
+
if (raw === null) {
|
|
2712
|
+
saveText(config, TOOL4, file, `${CREATED_HEADER.join("\n")}
|
|
2179
2713
|
${entries}
|
|
2180
|
-
|
|
2714
|
+
`, null);
|
|
2181
2715
|
setMode(file, 384, config.platform);
|
|
2182
2716
|
return { path: file, next };
|
|
2183
2717
|
}
|
|
2184
|
-
const raw = readFileSync7(file, "utf8");
|
|
2185
2718
|
const layout = layoutOf(raw);
|
|
2186
2719
|
const content = toLf(raw);
|
|
2187
2720
|
const replaced = replaceOwnItems(content, entries, config);
|
|
2188
2721
|
if (replaced !== null) {
|
|
2189
|
-
if (replaced !== content)
|
|
2722
|
+
if (replaced !== content) saveText(config, TOOL4, file, fromLf(replaced, layout), raw);
|
|
2190
2723
|
return { path: file, next };
|
|
2191
2724
|
}
|
|
2192
2725
|
if (content.includes(`apiBase: ${config.base}`)) return { path: file, next };
|
|
2193
2726
|
if (lastTopLevelLine(content) === "models:") {
|
|
2194
|
-
|
|
2195
|
-
`, layout));
|
|
2727
|
+
saveText(config, TOOL4, file, fromLf(`${content}${entries}
|
|
2728
|
+
`, layout), raw);
|
|
2196
2729
|
return { path: file, next };
|
|
2197
2730
|
}
|
|
2198
2731
|
return { path: file, next, hint: `could not merge into ${file} automatically; add this under models:
|
|
2199
2732
|
${entries}` };
|
|
2200
2733
|
}
|
|
2734
|
+
function currentDefault4(_config) {
|
|
2735
|
+
return null;
|
|
2736
|
+
}
|
|
2737
|
+
function isCreatedHeader(lines) {
|
|
2738
|
+
return lines.length === CREATED_HEADER.length && lines.every((line, index) => line === CREATED_HEADER[index]);
|
|
2739
|
+
}
|
|
2740
|
+
function removal4(config) {
|
|
2741
|
+
const file = toolPaths(config).continueConfig;
|
|
2742
|
+
const raw = readText(file);
|
|
2743
|
+
if (raw === null) return [];
|
|
2744
|
+
const lines = splitLines(toLf(raw));
|
|
2745
|
+
const own = modelItems(lines).filter((item) => isOwnItem(lines, item, config));
|
|
2746
|
+
if (own.length === 0) return [];
|
|
2747
|
+
const kept = lines.filter((_line, index) => !own.some((item) => index >= item.start && index < item.end));
|
|
2748
|
+
const next = isCreatedHeader(kept) || kept.every(isBlankText) ? null : fromLf(`${kept.join("\n")}
|
|
2749
|
+
`, layoutOf(raw));
|
|
2750
|
+
return [{ file, raw, next }];
|
|
2751
|
+
}
|
|
2752
|
+
function remove4(config, _previousDefault) {
|
|
2753
|
+
const changes = removal4(config);
|
|
2754
|
+
applyChanges(config, TOOL4, changes);
|
|
2755
|
+
return { path: toolPaths(config).continueConfig, changed: changes.length > 0 };
|
|
2756
|
+
}
|
|
2757
|
+
function hasOwnEntries4(config) {
|
|
2758
|
+
return removal4(config).length > 0;
|
|
2759
|
+
}
|
|
2201
2760
|
|
|
2202
2761
|
// cli/src/writers/crush.ts
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2762
|
+
var crush_exports = {};
|
|
2763
|
+
__export(crush_exports, {
|
|
2764
|
+
currentDefault: () => currentDefault5,
|
|
2765
|
+
hasOwnEntries: () => hasOwnEntries5,
|
|
2766
|
+
remove: () => remove5,
|
|
2767
|
+
write: () => write5
|
|
2768
|
+
});
|
|
2769
|
+
import { mkdirSync as mkdirSync7 } from "node:fs";
|
|
2770
|
+
var TOOL5 = "crush";
|
|
2771
|
+
function words(line) {
|
|
2772
|
+
return line.trim().split(/\s+/);
|
|
2773
|
+
}
|
|
2774
|
+
function isUseLine(line) {
|
|
2775
|
+
const [command, action] = words(line);
|
|
2776
|
+
return command === "model" && action === "use";
|
|
2777
|
+
}
|
|
2778
|
+
function useTarget(line) {
|
|
2779
|
+
const [command, action, target = ""] = words(line);
|
|
2780
|
+
return command === "model" && action === "use" && target !== "" ? target : null;
|
|
2781
|
+
}
|
|
2782
|
+
function isOwnLine2(line, owner) {
|
|
2783
|
+
const [command, action, target = ""] = words(line);
|
|
2784
|
+
if (command === "provider" && action === "add") return target === owner.provider;
|
|
2211
2785
|
if (command !== "model" || action !== "add" && action !== "use") return false;
|
|
2212
|
-
const prefix = `${
|
|
2213
|
-
return target.startsWith(prefix) && isOwnModel(target.slice(prefix.length),
|
|
2786
|
+
const prefix = `${owner.provider}/`;
|
|
2787
|
+
return target.startsWith(prefix) && isOwnModel(target.slice(prefix.length), owner);
|
|
2214
2788
|
}
|
|
2215
2789
|
function modelLine(config, model) {
|
|
2216
2790
|
return `model add ${config.provider}/${model.id} --name "${model.name}" --context-window ${model.context} --default-max-tokens ${model.maxOutput} --can-reason true`;
|
|
2217
2791
|
}
|
|
2218
|
-
function
|
|
2792
|
+
function ownBlock(config) {
|
|
2219
2793
|
return [
|
|
2220
2794
|
`provider add ${config.provider} --type openai-compat --base-url "${config.base}" --api-key "${config.key}"`,
|
|
2221
|
-
...config.models.map((model) => modelLine(config, model))
|
|
2222
|
-
`model use ${config.provider}/${config.model}`
|
|
2795
|
+
...config.models.map((model) => modelLine(config, model))
|
|
2223
2796
|
];
|
|
2224
2797
|
}
|
|
2225
|
-
function write5(config) {
|
|
2798
|
+
function write5(config, options) {
|
|
2226
2799
|
const { crushDir: dir, crushrc: file } = toolPaths(config);
|
|
2227
2800
|
mkdirSync7(dir, { recursive: true });
|
|
2228
|
-
const raw =
|
|
2229
|
-
const lines =
|
|
2230
|
-
const
|
|
2231
|
-
const
|
|
2232
|
-
kept
|
|
2233
|
-
const
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2801
|
+
const raw = readText(file);
|
|
2802
|
+
const lines = splitLines(toLf(raw ?? ""));
|
|
2803
|
+
const replaced = (line) => isOwnLine2(line, config) && (options.setDefault || !isUseLine(line));
|
|
2804
|
+
const firstOwn = lines.findIndex((line) => isOwnLine2(line, config));
|
|
2805
|
+
const kept = lines.filter((line) => !replaced(line));
|
|
2806
|
+
const at = firstOwn === -1 ? kept.length : firstOwn;
|
|
2807
|
+
const block = ownBlock(config);
|
|
2808
|
+
kept.splice(at, 0, ...block);
|
|
2809
|
+
if (options.setDefault) {
|
|
2810
|
+
const end = at + block.length;
|
|
2811
|
+
kept.splice(kept.slice(end).some(isUseLine) ? kept.length : end, 0, `model use ${config.provider}/${config.model}`);
|
|
2238
2812
|
}
|
|
2813
|
+
const updated = fromLf(`${kept.join("\n")}
|
|
2814
|
+
`, layoutOf(raw ?? ""));
|
|
2815
|
+
if (saveText(config, TOOL5, file, updated, raw)) setMode(file, 384, config.platform);
|
|
2239
2816
|
return { path: file, next: "crush" };
|
|
2240
2817
|
}
|
|
2818
|
+
function currentDefault5(config) {
|
|
2819
|
+
let raw;
|
|
2820
|
+
try {
|
|
2821
|
+
raw = readText(toolPaths(config).crushrc);
|
|
2822
|
+
} catch {
|
|
2823
|
+
return null;
|
|
2824
|
+
}
|
|
2825
|
+
if (raw === null) return null;
|
|
2826
|
+
const targets = splitLines(toLf(raw)).map(useTarget).filter((target) => target !== null);
|
|
2827
|
+
return targets.at(-1) ?? null;
|
|
2828
|
+
}
|
|
2829
|
+
function removal5(config, previousDefault) {
|
|
2830
|
+
const file = toolPaths(config).crushrc;
|
|
2831
|
+
const raw = readText(file);
|
|
2832
|
+
if (raw === null) return [];
|
|
2833
|
+
const lines = splitLines(toLf(raw));
|
|
2834
|
+
const own = (line) => isOwnLine2(line, config);
|
|
2835
|
+
if (!lines.some(own)) return [];
|
|
2836
|
+
const kept = lines.filter((line) => !own(line));
|
|
2837
|
+
const lastUse = lines.findLastIndex(isUseLine);
|
|
2838
|
+
if (previousDefault !== null && lastUse !== -1 && own(lines[lastUse]) && !kept.some(isUseLine)) {
|
|
2839
|
+
kept.splice(lines.slice(0, lastUse).filter((line) => !own(line)).length, 0, `model use ${previousDefault}`);
|
|
2840
|
+
}
|
|
2841
|
+
return [{ file, raw, next: kept.every(isBlankText) ? null : fromLf(`${kept.join("\n")}
|
|
2842
|
+
`, layoutOf(raw)) }];
|
|
2843
|
+
}
|
|
2844
|
+
function remove5(config, previousDefault) {
|
|
2845
|
+
const changes = removal5(config, previousDefault);
|
|
2846
|
+
applyChanges(config, TOOL5, changes);
|
|
2847
|
+
return { path: toolPaths(config).crushrc, changed: changes.length > 0 };
|
|
2848
|
+
}
|
|
2849
|
+
function hasOwnEntries5(config) {
|
|
2850
|
+
return removal5(config, null).length > 0;
|
|
2851
|
+
}
|
|
2852
|
+
|
|
2853
|
+
// cli/src/writers/goose.ts
|
|
2854
|
+
var goose_exports = {};
|
|
2855
|
+
__export(goose_exports, {
|
|
2856
|
+
currentDefault: () => currentDefault6,
|
|
2857
|
+
hasOwnEntries: () => hasOwnEntries6,
|
|
2858
|
+
remove: () => remove6,
|
|
2859
|
+
scalarValue: () => scalarValue,
|
|
2860
|
+
startCommand: () => startCommand2,
|
|
2861
|
+
withOwnDefault: () => withOwnDefault,
|
|
2862
|
+
write: () => write6,
|
|
2863
|
+
yamlScalar: () => yamlScalar
|
|
2864
|
+
});
|
|
2865
|
+
import { mkdirSync as mkdirSync8 } from "node:fs";
|
|
2866
|
+
|
|
2867
|
+
// cli/src/tool-entries.ts
|
|
2868
|
+
var QWEN_PROTOCOL = "openai";
|
|
2869
|
+
var QWEN_SETTINGS_VERSION = 4;
|
|
2870
|
+
var GOOSE_ENGINE = "openai";
|
|
2871
|
+
var OPENHANDS_MODEL_PREFIX = "openai/";
|
|
2872
|
+
var OPENHANDS_AGENT_USAGE = "agent";
|
|
2873
|
+
var OPENHANDS_CONDENSER_USAGE = "condenser";
|
|
2874
|
+
function qwenModel(source, model) {
|
|
2875
|
+
return {
|
|
2876
|
+
id: model.id,
|
|
2877
|
+
name: model.name,
|
|
2878
|
+
description: PROVIDER_NAME,
|
|
2879
|
+
envKey: API_KEY_ENV,
|
|
2880
|
+
baseUrl: source.base,
|
|
2881
|
+
generationConfig: { contextWindowSize: model.context, samplingParams: { max_tokens: model.maxOutput } }
|
|
2882
|
+
};
|
|
2883
|
+
}
|
|
2884
|
+
function gooseProvider(source, models2) {
|
|
2885
|
+
return {
|
|
2886
|
+
name: source.provider,
|
|
2887
|
+
engine: GOOSE_ENGINE,
|
|
2888
|
+
display_name: PROVIDER_NAME,
|
|
2889
|
+
description: `${PROVIDER_NAME} models`,
|
|
2890
|
+
base_url: source.base,
|
|
2891
|
+
models: models2.map((model) => ({ name: model.id, context_limit: model.context, reasoning: true })),
|
|
2892
|
+
headers: { Authorization: `Bearer ${source.key}` },
|
|
2893
|
+
supports_streaming: true,
|
|
2894
|
+
requires_auth: false
|
|
2895
|
+
};
|
|
2896
|
+
}
|
|
2897
|
+
function openhandsLlm(source, model, usage) {
|
|
2898
|
+
return {
|
|
2899
|
+
model: `${OPENHANDS_MODEL_PREFIX}${model.id}`,
|
|
2900
|
+
api_key: source.key,
|
|
2901
|
+
base_url: source.base,
|
|
2902
|
+
usage_id: usage,
|
|
2903
|
+
max_input_tokens: model.context,
|
|
2904
|
+
max_output_tokens: model.maxOutput
|
|
2905
|
+
};
|
|
2906
|
+
}
|
|
2907
|
+
function openhandsAgent(source, model) {
|
|
2908
|
+
return {
|
|
2909
|
+
llm: openhandsLlm(source, model, OPENHANDS_AGENT_USAGE),
|
|
2910
|
+
system_prompt_kwargs: { cli_mode: true },
|
|
2911
|
+
condenser: { kind: "LLMSummarizingCondenser", llm: openhandsLlm(source, model, OPENHANDS_CONDENSER_USAGE), max_size: 80, keep_first: 4 },
|
|
2912
|
+
kind: "Agent"
|
|
2913
|
+
};
|
|
2914
|
+
}
|
|
2915
|
+
|
|
2916
|
+
// cli/src/writers/goose.ts
|
|
2917
|
+
var TOOL6 = "goose";
|
|
2918
|
+
var ACTIVE_PROVIDER = "active_provider";
|
|
2919
|
+
var PROVIDERS = "providers";
|
|
2920
|
+
var LEGACY_PROVIDER = "GOOSE_PROVIDER";
|
|
2921
|
+
var LEGACY_MODEL = "GOOSE_MODEL";
|
|
2922
|
+
var MODEL_FIELD = "model";
|
|
2923
|
+
var EMPTY_FLOW_MAP = "{}";
|
|
2924
|
+
var DEFAULT_INDENT = " ";
|
|
2925
|
+
var PROVIDER_SEPARATOR2 = "/";
|
|
2926
|
+
var TOP_LEVEL_KEY = /^([A-Za-z0-9_][^:#\s]*)[ \t]*:(.*)$/;
|
|
2927
|
+
var NESTED_KEY = /^([ \t]+)([^\s:#][^:#]*?)[ \t]*:(.*)$/;
|
|
2928
|
+
var PLAIN_SCALAR = /^[A-Za-z][A-Za-z0-9._/-]*$/;
|
|
2929
|
+
var RESERVED_SCALARS = ["true", "false", "yes", "no", "on", "off", "null", "y", "n"];
|
|
2930
|
+
function yamlScalar(value) {
|
|
2931
|
+
if (PLAIN_SCALAR.test(value) && !RESERVED_SCALARS.includes(value.toLowerCase())) return value;
|
|
2932
|
+
return `'${value.replaceAll("'", "''")}'`;
|
|
2933
|
+
}
|
|
2934
|
+
function withoutComment(value) {
|
|
2935
|
+
const hash = value.search(/[ \t]#/);
|
|
2936
|
+
return (hash === -1 ? value : value.slice(0, hash)).trim();
|
|
2937
|
+
}
|
|
2938
|
+
function scalarValue(raw) {
|
|
2939
|
+
const value = raw.trim();
|
|
2940
|
+
if (value.startsWith("'")) {
|
|
2941
|
+
const close = value.lastIndexOf("'");
|
|
2942
|
+
return close > 0 ? value.slice(1, close).replaceAll("''", "'") : null;
|
|
2943
|
+
}
|
|
2944
|
+
if (value.startsWith('"')) {
|
|
2945
|
+
const close = value.lastIndexOf('"');
|
|
2946
|
+
return close > 0 ? value.slice(1, close).replace(/\\(["\\])/g, "$1") : null;
|
|
2947
|
+
}
|
|
2948
|
+
const plain = withoutComment(value);
|
|
2949
|
+
return plain === "" || /^[[{|>&*!]/.test(plain) ? null : plain;
|
|
2950
|
+
}
|
|
2951
|
+
function isContentLine(line) {
|
|
2952
|
+
return !isBlankText(line) && !line.trimStart().startsWith("#");
|
|
2953
|
+
}
|
|
2954
|
+
function topLevelAt(lines, key) {
|
|
2955
|
+
return lines.findIndex((line) => TOP_LEVEL_KEY.exec(line)?.[1] === key);
|
|
2956
|
+
}
|
|
2957
|
+
function topLevelValue(lines, key) {
|
|
2958
|
+
const at = topLevelAt(lines, key);
|
|
2959
|
+
const match = at === -1 ? null : TOP_LEVEL_KEY.exec(lines[at]);
|
|
2960
|
+
return match === null ? null : scalarValue(match[2]);
|
|
2961
|
+
}
|
|
2962
|
+
function blockEnd(lines, header) {
|
|
2963
|
+
let end = header + 1;
|
|
2964
|
+
for (let index = header + 1; index < lines.length; index += 1) {
|
|
2965
|
+
const line = lines[index];
|
|
2966
|
+
if (!isContentLine(line)) continue;
|
|
2967
|
+
if (!/^[ \t]/.test(line)) break;
|
|
2968
|
+
end = index + 1;
|
|
2969
|
+
}
|
|
2970
|
+
return end;
|
|
2971
|
+
}
|
|
2972
|
+
function providersBlock(lines) {
|
|
2973
|
+
const header = topLevelAt(lines, PROVIDERS);
|
|
2974
|
+
if (header === -1) return null;
|
|
2975
|
+
const inline = withoutComment(TOP_LEVEL_KEY.exec(lines[header])?.[2] ?? "");
|
|
2976
|
+
return { header, end: inline === "" ? blockEnd(lines, header) : header + 1, inline };
|
|
2977
|
+
}
|
|
2978
|
+
function indentOf(line) {
|
|
2979
|
+
return /^[ \t]*/.exec(line)?.[0] ?? "";
|
|
2980
|
+
}
|
|
2981
|
+
function childIndent(lines, block) {
|
|
2982
|
+
const first = block === null ? void 0 : lines.slice(block.header + 1, block.end).find(isContentLine);
|
|
2983
|
+
return first === void 0 ? DEFAULT_INDENT : indentOf(first);
|
|
2984
|
+
}
|
|
2985
|
+
function children(lines, block) {
|
|
2986
|
+
if (block.inline !== "") return [];
|
|
2987
|
+
const indent = childIndent(lines, block);
|
|
2988
|
+
const found = [];
|
|
2989
|
+
for (let index = block.header + 1; index < block.end; index += 1) {
|
|
2990
|
+
const match = NESTED_KEY.exec(lines[index]);
|
|
2991
|
+
if (match !== null && match[1] === indent) found.push({ name: match[2], start: index, end: index + 1 });
|
|
2992
|
+
else if (found.length > 0 && isContentLine(lines[index])) found[found.length - 1].end = index + 1;
|
|
2993
|
+
}
|
|
2994
|
+
return found;
|
|
2995
|
+
}
|
|
2996
|
+
function findChild(lines, name) {
|
|
2997
|
+
const block = providersBlock(lines);
|
|
2998
|
+
return block === null ? null : children(lines, block).find((child) => child.name === name) ?? null;
|
|
2999
|
+
}
|
|
3000
|
+
function modelLineOf(lines, child) {
|
|
3001
|
+
for (let index = child.start + 1; index < child.end; index += 1) {
|
|
3002
|
+
if (NESTED_KEY.exec(lines[index])?.[2] === MODEL_FIELD) return index;
|
|
3003
|
+
}
|
|
3004
|
+
return -1;
|
|
3005
|
+
}
|
|
3006
|
+
function modelOf(lines, child) {
|
|
3007
|
+
const at = child === null ? -1 : modelLineOf(lines, child);
|
|
3008
|
+
const match = at === -1 ? null : NESTED_KEY.exec(lines[at]);
|
|
3009
|
+
return match === null ? null : scalarValue(match[3]);
|
|
3010
|
+
}
|
|
3011
|
+
function defaultIn2(lines) {
|
|
3012
|
+
const provider = topLevelValue(lines, ACTIVE_PROVIDER) || topLevelValue(lines, LEGACY_PROVIDER);
|
|
3013
|
+
if (!provider) return null;
|
|
3014
|
+
return { provider, model: modelOf(lines, findChild(lines, provider)) || topLevelValue(lines, LEGACY_MODEL) || null };
|
|
3015
|
+
}
|
|
3016
|
+
function defaultLabel(value) {
|
|
3017
|
+
if (value === null) return null;
|
|
3018
|
+
return value.model === null ? value.provider : `${value.provider}${PROVIDER_SEPARATOR2}${value.model}`;
|
|
3019
|
+
}
|
|
3020
|
+
function modelLine2(indent, model) {
|
|
3021
|
+
return `${indent}${indent}${MODEL_FIELD}: ${yamlScalar(model)}`;
|
|
3022
|
+
}
|
|
3023
|
+
function childLines(indent, name, model) {
|
|
3024
|
+
return [`${indent}${name}:`, `${indent}${indent}enabled: true`, modelLine2(indent, model), `${indent}${indent}configured: true`];
|
|
3025
|
+
}
|
|
3026
|
+
function withChild(lines, name, model) {
|
|
3027
|
+
const block = providersBlock(lines);
|
|
3028
|
+
if (block === null) return [...lines, `${PROVIDERS}:`, ...childLines(DEFAULT_INDENT, name, model)];
|
|
3029
|
+
if (block.inline !== "") {
|
|
3030
|
+
if (block.inline !== EMPTY_FLOW_MAP) throw new Error(`its ${PROVIDERS} entry is not a block the setup can edit`);
|
|
3031
|
+
return [...lines.slice(0, block.header), `${PROVIDERS}:`, ...childLines(DEFAULT_INDENT, name, model), ...lines.slice(block.header + 1)];
|
|
3032
|
+
}
|
|
3033
|
+
const own = childLines(childIndent(lines, block), name, model);
|
|
3034
|
+
const existing = children(lines, block).find((child) => child.name === name);
|
|
3035
|
+
if (existing === void 0) return [...lines.slice(0, block.end), ...own, ...lines.slice(block.end)];
|
|
3036
|
+
return [...lines.slice(0, existing.start), ...own, ...lines.slice(existing.end)];
|
|
3037
|
+
}
|
|
3038
|
+
function withActiveProvider(lines, provider, position) {
|
|
3039
|
+
const line = `${ACTIVE_PROVIDER}: ${yamlScalar(provider)}`;
|
|
3040
|
+
const at = topLevelAt(lines, ACTIVE_PROVIDER);
|
|
3041
|
+
if (at !== -1) return [...lines.slice(0, at), line, ...lines.slice(at + 1)];
|
|
3042
|
+
const block = providersBlock(lines);
|
|
3043
|
+
const insertAt = position ?? (block === null ? lines.length : block.header);
|
|
3044
|
+
return [...lines.slice(0, insertAt), line, ...lines.slice(insertAt)];
|
|
3045
|
+
}
|
|
3046
|
+
function withOwnDefault(content, config) {
|
|
3047
|
+
const lines = withChild(withActiveProvider(splitLines(content), config.provider), config.provider, config.model);
|
|
3048
|
+
return `${lines.join("\n")}
|
|
3049
|
+
`;
|
|
3050
|
+
}
|
|
3051
|
+
function startCommand2(config) {
|
|
3052
|
+
return `goose session --provider ${config.provider} --model ${config.model}`;
|
|
3053
|
+
}
|
|
3054
|
+
function writeDefault(config) {
|
|
3055
|
+
const file = toolPaths(config).gooseConfig;
|
|
3056
|
+
const raw = readText(file);
|
|
3057
|
+
let updated;
|
|
3058
|
+
try {
|
|
3059
|
+
updated = withOwnDefault(toLf(raw ?? ""), config);
|
|
3060
|
+
} catch (error) {
|
|
3061
|
+
throw new Error(`${file}: ${error instanceof Error ? error.message : String(error)}; pick the model with "goose configure"`);
|
|
3062
|
+
}
|
|
3063
|
+
saveText(config, TOOL6, file, fromLf(updated, layoutOf(raw ?? "")), raw);
|
|
3064
|
+
}
|
|
3065
|
+
function write6(config, options) {
|
|
3066
|
+
const paths = toolPaths(config);
|
|
3067
|
+
mkdirSync8(pathsFor(config.platform).dirname(paths.gooseProvider), { recursive: true });
|
|
3068
|
+
const existing = readText(paths.gooseProvider);
|
|
3069
|
+
saveText(config, TOOL6, paths.gooseProvider, jsonText(gooseProvider(config, config.models), layoutOf(existing ?? "")), existing);
|
|
3070
|
+
setMode(paths.gooseProvider, 384, config.platform);
|
|
3071
|
+
if (options.setDefault) writeDefault(config);
|
|
3072
|
+
const startsOnOwnModel = currentDefault6(config)?.startsWith(`${config.provider}${PROVIDER_SEPARATOR2}`) ?? false;
|
|
3073
|
+
return { path: paths.gooseProvider, next: startsOnOwnModel ? "goose session" : startCommand2(config) };
|
|
3074
|
+
}
|
|
3075
|
+
function currentDefault6(config) {
|
|
3076
|
+
let raw;
|
|
3077
|
+
try {
|
|
3078
|
+
raw = readText(toolPaths(config).gooseConfig);
|
|
3079
|
+
} catch {
|
|
3080
|
+
return null;
|
|
3081
|
+
}
|
|
3082
|
+
return raw === null ? null : defaultLabel(defaultIn2(splitLines(toLf(raw))));
|
|
3083
|
+
}
|
|
3084
|
+
function parsedDefault(value) {
|
|
3085
|
+
const slash = value.indexOf(PROVIDER_SEPARATOR2);
|
|
3086
|
+
return slash <= 0 ? { provider: value, model: null } : { provider: value.slice(0, slash), model: value.slice(slash + 1) };
|
|
3087
|
+
}
|
|
3088
|
+
function withModelOf(lines, provider, model) {
|
|
3089
|
+
const child = findChild(lines, provider);
|
|
3090
|
+
if (child === null) return withChild(lines, provider, model);
|
|
3091
|
+
const line = modelLine2(childIndent(lines, providersBlock(lines)), model);
|
|
3092
|
+
const at = modelLineOf(lines, child);
|
|
3093
|
+
if (at === -1) return [...lines.slice(0, child.end), line, ...lines.slice(child.end)];
|
|
3094
|
+
return [...lines.slice(0, at), line, ...lines.slice(at + 1)];
|
|
3095
|
+
}
|
|
3096
|
+
function withPreviousDefault(lines, position, previousDefault) {
|
|
3097
|
+
if (defaultLabel(defaultIn2(lines)) === previousDefault) return [...lines];
|
|
3098
|
+
const previous = parsedDefault(previousDefault);
|
|
3099
|
+
const restored = withActiveProvider(lines, previous.provider, Math.min(position, lines.length));
|
|
3100
|
+
if (previous.model === null || defaultLabel(defaultIn2(restored)) === previousDefault) return restored;
|
|
3101
|
+
return withModelOf(restored, previous.provider, previous.model);
|
|
3102
|
+
}
|
|
3103
|
+
function withoutChild(lines, name) {
|
|
3104
|
+
const child = findChild(lines, name);
|
|
3105
|
+
if (child === null) return [...lines];
|
|
3106
|
+
const kept = [...lines.slice(0, child.start), ...lines.slice(child.end)];
|
|
3107
|
+
const block = providersBlock(kept);
|
|
3108
|
+
if (block === null || children(kept, block).length > 0) return kept;
|
|
3109
|
+
return [...kept.slice(0, block.header), ...kept.slice(block.end)];
|
|
3110
|
+
}
|
|
3111
|
+
function withoutOwnDefault(lines, provider, previousDefault) {
|
|
3112
|
+
const at = topLevelAt(lines, ACTIVE_PROVIDER);
|
|
3113
|
+
if (at === -1 || topLevelValue(lines, ACTIVE_PROVIDER) !== provider) return [...lines];
|
|
3114
|
+
const kept = [...lines.slice(0, at), ...lines.slice(at + 1)];
|
|
3115
|
+
return previousDefault === null ? kept : withPreviousDefault(kept, at, previousDefault);
|
|
3116
|
+
}
|
|
3117
|
+
function configRemoval(config, previousDefault) {
|
|
3118
|
+
const file = toolPaths(config).gooseConfig;
|
|
3119
|
+
const raw = readText(file);
|
|
3120
|
+
if (raw === null) return [];
|
|
3121
|
+
const lines = splitLines(toLf(raw));
|
|
3122
|
+
const kept = withoutChild(withoutOwnDefault(lines, config.provider, previousDefault), config.provider);
|
|
3123
|
+
if (kept.join("\n") === lines.join("\n")) return [];
|
|
3124
|
+
return [{ file, raw, next: kept.every(isBlankText) ? null : fromLf(`${kept.join("\n")}
|
|
3125
|
+
`, layoutOf(raw)) }];
|
|
3126
|
+
}
|
|
3127
|
+
function providerRemoval(config) {
|
|
3128
|
+
const file = toolPaths(config).gooseProvider;
|
|
3129
|
+
if (!isFile(file)) return [];
|
|
3130
|
+
try {
|
|
3131
|
+
readJsonDocument(file);
|
|
3132
|
+
} catch (error) {
|
|
3133
|
+
if (error instanceof SyntaxError) throw new Error(`${file} is not JSON Goose can read; fix it, then run "unrestricted remove" again`);
|
|
3134
|
+
throw error;
|
|
3135
|
+
}
|
|
3136
|
+
return [{ file, raw: readText(file), next: null }];
|
|
3137
|
+
}
|
|
3138
|
+
function removal6(config, previousDefault) {
|
|
3139
|
+
return [...providerRemoval(config), ...configRemoval(config, previousDefault)];
|
|
3140
|
+
}
|
|
3141
|
+
function remove6(config, previousDefault) {
|
|
3142
|
+
const changes = removal6(config, previousDefault);
|
|
3143
|
+
applyChanges(config, TOOL6, changes);
|
|
3144
|
+
return { path: changes[0]?.file ?? toolPaths(config).gooseProvider, changed: changes.length > 0 };
|
|
3145
|
+
}
|
|
3146
|
+
function hasOwnEntries6(config) {
|
|
3147
|
+
return removal6(config, null).length > 0;
|
|
3148
|
+
}
|
|
2241
3149
|
|
|
2242
3150
|
// cli/src/writers/opencode.ts
|
|
2243
|
-
|
|
3151
|
+
var opencode_exports = {};
|
|
3152
|
+
__export(opencode_exports, {
|
|
3153
|
+
currentDefault: () => currentDefault7,
|
|
3154
|
+
hasOwnEntries: () => hasOwnEntries7,
|
|
3155
|
+
remove: () => remove7,
|
|
3156
|
+
write: () => write7
|
|
3157
|
+
});
|
|
3158
|
+
import { mkdirSync as mkdirSync9 } from "node:fs";
|
|
2244
3159
|
|
|
2245
3160
|
// node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js
|
|
2246
3161
|
function createScanner(text, ignoreTrivia = false) {
|
|
@@ -3574,6 +4489,7 @@ var SyntaxKind;
|
|
|
3574
4489
|
var parse2 = parse;
|
|
3575
4490
|
var parseTree2 = parseTree;
|
|
3576
4491
|
var findNodeAtLocation2 = findNodeAtLocation;
|
|
4492
|
+
var visit2 = visit;
|
|
3577
4493
|
var ParseErrorCode;
|
|
3578
4494
|
(function(ParseErrorCode2) {
|
|
3579
4495
|
ParseErrorCode2[ParseErrorCode2["InvalidSymbol"] = 1] = "InvalidSymbol";
|
|
@@ -3626,6 +4542,19 @@ function parseJsoncObject(text) {
|
|
|
3626
4542
|
if (errors.length > 0 || typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
3627
4543
|
return value;
|
|
3628
4544
|
}
|
|
4545
|
+
function hasComments(text) {
|
|
4546
|
+
let found = false;
|
|
4547
|
+
visit2(
|
|
4548
|
+
text,
|
|
4549
|
+
{
|
|
4550
|
+
onComment: () => {
|
|
4551
|
+
found = true;
|
|
4552
|
+
}
|
|
4553
|
+
},
|
|
4554
|
+
PARSE_OPTIONS
|
|
4555
|
+
);
|
|
4556
|
+
return found;
|
|
4557
|
+
}
|
|
3629
4558
|
function isJsonObject(value) {
|
|
3630
4559
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3631
4560
|
}
|
|
@@ -3659,7 +4588,7 @@ function lineBreakAt(text, offset) {
|
|
|
3659
4588
|
if (text.startsWith("\r\n", offset)) return 2;
|
|
3660
4589
|
return text[offset] === "\n" ? 1 : 0;
|
|
3661
4590
|
}
|
|
3662
|
-
function
|
|
4591
|
+
function removal7(text, start2, end) {
|
|
3663
4592
|
let from = start2;
|
|
3664
4593
|
while (isBlank(text[from - 1])) from -= 1;
|
|
3665
4594
|
let to = end;
|
|
@@ -3677,20 +4606,22 @@ function removeJsonc(text, path) {
|
|
|
3677
4606
|
if (property === void 0 || property.type !== "property" || siblings === void 0) return text;
|
|
3678
4607
|
const end = property.offset + property.length;
|
|
3679
4608
|
const following = nextToken(text, end);
|
|
3680
|
-
if (text[following.offset] === ",") return applyEdits(text, [
|
|
4609
|
+
if (text[following.offset] === ",") return applyEdits(text, [removal7(text, property.offset, following.end)]);
|
|
3681
4610
|
const index = siblings.indexOf(property);
|
|
3682
|
-
if (index <= 0) return applyEdits(text, [
|
|
4611
|
+
if (index <= 0) return applyEdits(text, [removal7(text, property.offset, end)]);
|
|
3683
4612
|
const previous = siblings[index - 1];
|
|
3684
4613
|
const comma = nextToken(text, previous.offset + previous.length);
|
|
3685
|
-
return applyEdits(text, [{ offset: comma.offset, length: comma.end - comma.offset, content: "" },
|
|
4614
|
+
return applyEdits(text, [{ offset: comma.offset, length: comma.end - comma.offset, content: "" }, removal7(text, property.offset, end)]);
|
|
3686
4615
|
}
|
|
3687
4616
|
|
|
3688
4617
|
// cli/src/writers/opencode.ts
|
|
4618
|
+
var TOOL7 = "opencode";
|
|
3689
4619
|
var SCHEMA = "https://opencode.ai/config.json";
|
|
3690
4620
|
var PROVIDER_PACKAGE = "@ai-sdk/openai-compatible";
|
|
3691
|
-
var
|
|
4621
|
+
var PROVIDER_NAME2 = "unrestricted.sh";
|
|
3692
4622
|
var OWN_PROVIDER_KEYS = ["npm", "name"];
|
|
3693
4623
|
var OWN_OPTION_KEYS = ["baseURL", "apiKey"];
|
|
4624
|
+
var MODEL_PATH = ["model"];
|
|
3694
4625
|
var FIRST = () => 0;
|
|
3695
4626
|
var AFTER_SCHEMA = (keys) => keys.indexOf("$schema") + 1;
|
|
3696
4627
|
function withoutOwnModels(models2, config) {
|
|
@@ -3706,34 +4637,38 @@ function ownModels(config) {
|
|
|
3706
4637
|
function ownProvider(config) {
|
|
3707
4638
|
return {
|
|
3708
4639
|
npm: PROVIDER_PACKAGE,
|
|
3709
|
-
name:
|
|
4640
|
+
name: PROVIDER_NAME2,
|
|
3710
4641
|
options: { baseURL: config.base, apiKey: config.key },
|
|
3711
4642
|
models: ownModels(config)
|
|
3712
4643
|
};
|
|
3713
4644
|
}
|
|
3714
|
-
function
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
} catch {
|
|
3718
|
-
return false;
|
|
3719
|
-
}
|
|
4645
|
+
function defaultModel(settings) {
|
|
4646
|
+
const model = settings.model;
|
|
4647
|
+
return typeof model === "string" && model !== "" ? model : null;
|
|
3720
4648
|
}
|
|
3721
|
-
function
|
|
3722
|
-
const
|
|
3723
|
-
|
|
3724
|
-
|
|
4649
|
+
function hasOwnDefault(settings, owner) {
|
|
4650
|
+
const model = defaultModel(settings);
|
|
4651
|
+
return model !== null && isOwnDefault(model, owner);
|
|
4652
|
+
}
|
|
4653
|
+
function hasForeignDefault(settings, owner) {
|
|
4654
|
+
const model = defaultModel(settings);
|
|
4655
|
+
return model !== null && !isOwnDefault(model, owner);
|
|
4656
|
+
}
|
|
4657
|
+
function writeJson(config, file, options) {
|
|
4658
|
+
const document = readJsonDocument(file);
|
|
4659
|
+
const settings = document.raw === null || isBlankText(document.raw) ? { $schema: SCHEMA } : document.value;
|
|
3725
4660
|
const providers = asObject(settings.provider);
|
|
3726
4661
|
settings.provider = providers;
|
|
3727
4662
|
const existing = asObject(providers[config.provider]);
|
|
3728
4663
|
providers[config.provider] = {
|
|
3729
4664
|
...existing,
|
|
3730
4665
|
npm: PROVIDER_PACKAGE,
|
|
3731
|
-
name:
|
|
4666
|
+
name: PROVIDER_NAME2,
|
|
3732
4667
|
options: { ...asObject(existing.options), baseURL: config.base, apiKey: config.key },
|
|
3733
4668
|
models: { ...withoutOwnModels(asObject(existing.models), config), ...ownModels(config) }
|
|
3734
4669
|
};
|
|
3735
|
-
settings.model = `${config.provider}/${config.model}`;
|
|
3736
|
-
|
|
4670
|
+
if (options.setDefault) settings.model = `${config.provider}/${config.model}`;
|
|
4671
|
+
saveText(config, TOOL7, file, jsonText(settings, document.layout), document.raw);
|
|
3737
4672
|
setMode(file, 384, config.platform);
|
|
3738
4673
|
return { path: file, next: "opencode" };
|
|
3739
4674
|
}
|
|
@@ -3760,7 +4695,7 @@ function withOwnProvider(text, config, settings) {
|
|
|
3760
4695
|
const existing = providers[config.provider];
|
|
3761
4696
|
if (!isJsonObject(existing)) return setJsonc(text, path, ownProvider(config), FIRST);
|
|
3762
4697
|
let updated = setJsonc(text, [...path, "npm"], PROVIDER_PACKAGE, FIRST);
|
|
3763
|
-
updated = setJsonc(updated, [...path, "name"],
|
|
4698
|
+
updated = setJsonc(updated, [...path, "name"], PROVIDER_NAME2, (keys) => keys.indexOf("npm") + 1);
|
|
3764
4699
|
if (isJsonObject(existing.options)) {
|
|
3765
4700
|
updated = setJsonc(updated, [...path, "options", "baseURL"], config.base, FIRST);
|
|
3766
4701
|
updated = setJsonc(updated, [...path, "options", "apiKey"], config.key, (keys) => keys.indexOf("baseURL") + 1);
|
|
@@ -3770,29 +4705,23 @@ function withOwnProvider(text, config, settings) {
|
|
|
3770
4705
|
if (isJsonObject(existing.models)) return withOwnModels(updated, config, existing.models);
|
|
3771
4706
|
return setJsonc(updated, [...path, "models"], ownModels(config), FIRST);
|
|
3772
4707
|
}
|
|
3773
|
-
function
|
|
3774
|
-
const paths = [];
|
|
3775
|
-
const model = settings.model;
|
|
3776
|
-
const prefix = `${config.provider}/`;
|
|
3777
|
-
if (typeof model === "string" && model.startsWith(prefix) && isOwnModel(model.slice(prefix.length), config)) paths.push(["model"]);
|
|
4708
|
+
function ownProviderPaths(settings, owner) {
|
|
3778
4709
|
const providers = asObject(settings.provider);
|
|
3779
|
-
const entry = providers[
|
|
3780
|
-
if (!isJsonObject(entry)) return
|
|
3781
|
-
const path = ["provider",
|
|
4710
|
+
const entry = providers[owner.provider];
|
|
4711
|
+
if (!isJsonObject(entry)) return [];
|
|
4712
|
+
const path = ["provider", owner.provider];
|
|
3782
4713
|
const options = entry.options;
|
|
3783
4714
|
const models2 = entry.models;
|
|
3784
4715
|
const keptOptions = isJsonObject(options) ? Object.keys(options).filter((key) => !OWN_OPTION_KEYS.includes(key)) : [];
|
|
3785
|
-
const keptModels = isJsonObject(models2) ? Object.keys(models2).filter((id) => !isOwnModel(id,
|
|
4716
|
+
const keptModels = isJsonObject(models2) ? Object.keys(models2).filter((id) => !isOwnModel(id, owner)) : [];
|
|
3786
4717
|
const keptKeys = Object.keys(entry).filter((key) => {
|
|
3787
4718
|
if (OWN_PROVIDER_KEYS.includes(key)) return false;
|
|
3788
4719
|
if (key === "options" && isJsonObject(options)) return keptOptions.length > 0;
|
|
3789
4720
|
if (key === "models" && isJsonObject(models2)) return keptModels.length > 0;
|
|
3790
4721
|
return true;
|
|
3791
4722
|
});
|
|
3792
|
-
if (keptKeys.length === 0)
|
|
3793
|
-
|
|
3794
|
-
return paths;
|
|
3795
|
-
}
|
|
4723
|
+
if (keptKeys.length === 0) return [Object.keys(providers).length === 1 ? ["provider"] : path];
|
|
4724
|
+
const paths = [];
|
|
3796
4725
|
for (const key of OWN_PROVIDER_KEYS) if (key in entry) paths.push([...path, key]);
|
|
3797
4726
|
if (isJsonObject(options)) {
|
|
3798
4727
|
if (keptOptions.length === 0) paths.push([...path, "options"]);
|
|
@@ -3800,51 +4729,227 @@ function ownJsonPaths(settings, config) {
|
|
|
3800
4729
|
}
|
|
3801
4730
|
if (isJsonObject(models2)) {
|
|
3802
4731
|
if (keptModels.length === 0) paths.push([...path, "models"]);
|
|
3803
|
-
else for (const id of Object.keys(models2)) if (isOwnModel(id,
|
|
4732
|
+
else for (const id of Object.keys(models2)) if (isOwnModel(id, owner)) paths.push([...path, "models", id]);
|
|
3804
4733
|
}
|
|
3805
4734
|
return paths;
|
|
3806
4735
|
}
|
|
3807
|
-
function withoutOwnJsonEntries(file,
|
|
3808
|
-
if (!
|
|
3809
|
-
const raw =
|
|
4736
|
+
function withoutOwnJsonEntries(config, file, options) {
|
|
4737
|
+
if (!isFile(file)) return void 0;
|
|
4738
|
+
const raw = readText(file) ?? "";
|
|
3810
4739
|
const original = withoutBom(raw);
|
|
3811
|
-
const settings = original
|
|
4740
|
+
const settings = isBlankText(original) ? {} : parseJsoncObject(original);
|
|
3812
4741
|
if (settings === null) return `${file} could not be read, so the setup left it as it is; OpenCode reads ${OPENCODE_JSONC_FILE} after it`;
|
|
4742
|
+
const paths = ownProviderPaths(settings, config);
|
|
4743
|
+
if (options.setDefault && hasOwnDefault(settings, config)) paths.push(MODEL_PATH);
|
|
3813
4744
|
let updated = original;
|
|
3814
|
-
for (const path of
|
|
3815
|
-
|
|
4745
|
+
for (const path of paths) updated = removeJsonc(updated, path);
|
|
4746
|
+
saveText(config, TOOL7, file, withBom(updated, layoutOf(raw)), raw);
|
|
3816
4747
|
return void 0;
|
|
3817
4748
|
}
|
|
3818
|
-
function writeJsonc(config, file, jsonFile) {
|
|
3819
|
-
const raw =
|
|
4749
|
+
function writeJsonc(config, file, jsonFile, options) {
|
|
4750
|
+
const raw = readText(file) ?? "";
|
|
3820
4751
|
const layout = layoutOf(raw);
|
|
3821
4752
|
const original = withoutBom(raw);
|
|
3822
|
-
const
|
|
4753
|
+
const blank = isBlankText(original);
|
|
4754
|
+
const text = blank ? `{}${layout.eol}` : original;
|
|
3823
4755
|
const settings = parseJsoncObject(text);
|
|
3824
4756
|
if (settings === null) throw new Error(`${file} is not a JSON object OpenCode can read; fix it, then run the setup again`);
|
|
3825
|
-
|
|
3826
|
-
let updated = typeof schema === "string" && schema ? text : setJsonc(text, ["$schema"], SCHEMA, FIRST);
|
|
4757
|
+
let updated = blank ? setJsonc(text, ["$schema"], SCHEMA, FIRST) : text;
|
|
3827
4758
|
updated = withOwnProvider(updated, config, settings);
|
|
3828
|
-
updated = setJsonc(updated,
|
|
3829
|
-
|
|
3830
|
-
if (output !== raw) writeFileSync10(file, output);
|
|
4759
|
+
if (options.setDefault) updated = setJsonc(updated, MODEL_PATH, `${config.provider}/${config.model}`, AFTER_SCHEMA);
|
|
4760
|
+
saveText(config, TOOL7, file, withBom(updated, layout), raw);
|
|
3831
4761
|
setMode(file, 384, config.platform);
|
|
3832
|
-
const hint = withoutOwnJsonEntries(jsonFile,
|
|
4762
|
+
const hint = withoutOwnJsonEntries(config, jsonFile, options);
|
|
3833
4763
|
return hint === void 0 ? { path: file, next: "opencode" } : { path: file, next: "opencode", hint };
|
|
3834
4764
|
}
|
|
3835
|
-
function
|
|
4765
|
+
function write7(config, options) {
|
|
4766
|
+
const paths = toolPaths(config);
|
|
4767
|
+
mkdirSync9(paths.opencodeDir, { recursive: true });
|
|
4768
|
+
if (isFile(paths.opencodeJsonc)) return writeJsonc(config, paths.opencodeJsonc, paths.opencodeJson, options);
|
|
4769
|
+
return writeJson(config, paths.opencodeJson, options);
|
|
4770
|
+
}
|
|
4771
|
+
function settingsIn(file) {
|
|
4772
|
+
try {
|
|
4773
|
+
if (!isFile(file)) return null;
|
|
4774
|
+
const text = withoutBom(readText(file) ?? "");
|
|
4775
|
+
return isBlankText(text) ? {} : parseJsoncObject(text);
|
|
4776
|
+
} catch {
|
|
4777
|
+
return null;
|
|
4778
|
+
}
|
|
4779
|
+
}
|
|
4780
|
+
function currentDefault7(config) {
|
|
4781
|
+
const paths = toolPaths(config);
|
|
4782
|
+
for (const file of [paths.opencodeJsonc, paths.opencodeJson]) {
|
|
4783
|
+
const settings = settingsIn(file);
|
|
4784
|
+
const model = settings === null ? null : defaultModel(settings);
|
|
4785
|
+
if (model !== null) return model;
|
|
4786
|
+
}
|
|
4787
|
+
return null;
|
|
4788
|
+
}
|
|
4789
|
+
function readSettingsFile(file) {
|
|
4790
|
+
if (!isFile(file)) return null;
|
|
4791
|
+
const raw = readText(file) ?? "";
|
|
4792
|
+
const text = withoutBom(raw);
|
|
4793
|
+
const settings = isBlankText(text) ? {} : parseJsoncObject(text);
|
|
4794
|
+
if (settings === null) throw new Error(`${file} is not a JSON object OpenCode can read; fix it, then run "unrestricted remove" again`);
|
|
4795
|
+
return { file, raw, text, settings };
|
|
4796
|
+
}
|
|
4797
|
+
function isBareText(text) {
|
|
4798
|
+
const settings = parseJsoncObject(text);
|
|
4799
|
+
return settings !== null && isBareDocument(settings) && !hasComments(text);
|
|
4800
|
+
}
|
|
4801
|
+
function removal8(config, previousDefault) {
|
|
4802
|
+
const paths = toolPaths(config);
|
|
4803
|
+
const files = [paths.opencodeJsonc, paths.opencodeJson].map(readSettingsFile).filter((entry) => entry !== null);
|
|
4804
|
+
const holder = files.find((entry) => defaultModel(entry.settings) !== null);
|
|
4805
|
+
const restore = previousDefault !== null && holder !== void 0 && hasOwnDefault(holder.settings, config) && !files.some((entry) => hasForeignDefault(entry.settings, config));
|
|
4806
|
+
const changes = [];
|
|
4807
|
+
for (const entry of files) {
|
|
4808
|
+
let updated = entry.text;
|
|
4809
|
+
for (const path of ownProviderPaths(entry.settings, config)) updated = removeJsonc(updated, path);
|
|
4810
|
+
if (restore && entry === holder && previousDefault !== null) updated = setJsonc(updated, MODEL_PATH, previousDefault);
|
|
4811
|
+
else if (hasOwnDefault(entry.settings, config)) updated = removeJsonc(updated, MODEL_PATH);
|
|
4812
|
+
if (updated === entry.text) continue;
|
|
4813
|
+
changes.push({ file: entry.file, raw: entry.raw, next: isBareText(updated) ? null : withBom(updated, layoutOf(entry.raw)) });
|
|
4814
|
+
}
|
|
4815
|
+
return changes;
|
|
4816
|
+
}
|
|
4817
|
+
function remove7(config, previousDefault) {
|
|
4818
|
+
const changes = removal8(config, previousDefault);
|
|
4819
|
+
applyChanges(config, TOOL7, changes);
|
|
4820
|
+
const paths = toolPaths(config);
|
|
4821
|
+
const main = isFile(paths.opencodeJsonc) ? paths.opencodeJsonc : paths.opencodeJson;
|
|
4822
|
+
return { path: changes[0]?.file ?? main, changed: changes.length > 0 };
|
|
4823
|
+
}
|
|
4824
|
+
function hasOwnEntries7(config) {
|
|
4825
|
+
return removal8(config, null).length > 0;
|
|
4826
|
+
}
|
|
4827
|
+
|
|
4828
|
+
// cli/src/writers/openhands.ts
|
|
4829
|
+
var openhands_exports = {};
|
|
4830
|
+
__export(openhands_exports, {
|
|
4831
|
+
currentDefault: () => currentDefault8,
|
|
4832
|
+
hasOwnEntries: () => hasOwnEntries8,
|
|
4833
|
+
remove: () => remove8,
|
|
4834
|
+
write: () => write8
|
|
4835
|
+
});
|
|
4836
|
+
import { mkdirSync as mkdirSync10 } from "node:fs";
|
|
4837
|
+
var TOOL8 = "openhands";
|
|
4838
|
+
function withoutTrailingSlashes(value) {
|
|
4839
|
+
return value.replace(/\/+$/, "");
|
|
4840
|
+
}
|
|
4841
|
+
function ownModelId(value, owner) {
|
|
4842
|
+
const llm = asObject(value);
|
|
4843
|
+
const { model, base_url: base } = llm;
|
|
4844
|
+
if (typeof model !== "string" || !model.startsWith(OPENHANDS_MODEL_PREFIX) || typeof base !== "string") return null;
|
|
4845
|
+
const id = model.slice(OPENHANDS_MODEL_PREFIX.length);
|
|
4846
|
+
return isOwnModel(id, owner) && withoutTrailingSlashes(base) === withoutTrailingSlashes(owner.base) ? id : null;
|
|
4847
|
+
}
|
|
4848
|
+
function modelFor(config, id) {
|
|
4849
|
+
return config.models.find((model) => model.id === id) ?? { id, name: config.modelName, context: config.context, maxOutput: config.maxOutput };
|
|
4850
|
+
}
|
|
4851
|
+
function readDocument(file, command) {
|
|
4852
|
+
try {
|
|
4853
|
+
return readJsonDocument(file);
|
|
4854
|
+
} catch (error) {
|
|
4855
|
+
if (error instanceof SyntaxError) throw new Error(`${file} is not JSON OpenHands can read; fix it, then run ${command} again`);
|
|
4856
|
+
throw error;
|
|
4857
|
+
}
|
|
4858
|
+
}
|
|
4859
|
+
function withOwnLlm(settings, config, model) {
|
|
4860
|
+
const next = { ...settings, llm: openhandsLlm(config, model, OPENHANDS_AGENT_USAGE) };
|
|
4861
|
+
const condenser = settings.condenser;
|
|
4862
|
+
if (isJsonObject(condenser) && isJsonObject(condenser.llm)) next.condenser = { ...condenser, llm: openhandsLlm(config, model, OPENHANDS_CONDENSER_USAGE) };
|
|
4863
|
+
return next;
|
|
4864
|
+
}
|
|
4865
|
+
function write8(config, options) {
|
|
3836
4866
|
const paths = toolPaths(config);
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
4867
|
+
const file = paths.openhandsSettings;
|
|
4868
|
+
mkdirSync10(paths.openhandsDir, { recursive: true });
|
|
4869
|
+
const document = readDocument(file, "the setup");
|
|
4870
|
+
const settings = document.value;
|
|
4871
|
+
const current = ownModelId(settings.llm, config);
|
|
4872
|
+
if (!options.setDefault && current === null) return { path: file, next: "openhands" };
|
|
4873
|
+
const model = modelFor(config, options.setDefault ? config.model : current ?? config.model);
|
|
4874
|
+
const fresh = Object.keys(settings).length === 0;
|
|
4875
|
+
const next = fresh ? openhandsAgent(config, model) : withOwnLlm(settings, config, model);
|
|
4876
|
+
saveText(config, TOOL8, file, jsonText(next, document.layout), document.raw);
|
|
4877
|
+
setMode(file, 384, config.platform);
|
|
4878
|
+
return { path: file, next: "openhands" };
|
|
4879
|
+
}
|
|
4880
|
+
function currentDefault8(config) {
|
|
4881
|
+
let settings;
|
|
4882
|
+
try {
|
|
4883
|
+
settings = readJsonDocument(toolPaths(config).openhandsSettings).value;
|
|
4884
|
+
} catch {
|
|
4885
|
+
return null;
|
|
4886
|
+
}
|
|
4887
|
+
const model = asObject(settings.llm).model;
|
|
4888
|
+
if (typeof model !== "string" || model === "") return null;
|
|
4889
|
+
const own = ownModelId(settings.llm, config);
|
|
4890
|
+
return own === null ? model : `${config.provider}/${own}`;
|
|
4891
|
+
}
|
|
4892
|
+
function earlierSettings(config, file, previousDefault) {
|
|
4893
|
+
for (const backup of backupsOf(config, TOOL8, file).reverse()) {
|
|
4894
|
+
let settings;
|
|
4895
|
+
try {
|
|
4896
|
+
settings = readJsonDocument(backup).value;
|
|
4897
|
+
} catch {
|
|
4898
|
+
continue;
|
|
4899
|
+
}
|
|
4900
|
+
if (asObject(settings.llm).model === previousDefault && ownModelId(settings.llm, config) === null) return settings;
|
|
4901
|
+
}
|
|
4902
|
+
return null;
|
|
4903
|
+
}
|
|
4904
|
+
function withEarlierLlm(settings, earlier, owner) {
|
|
4905
|
+
const next = { ...settings, llm: earlier.llm };
|
|
4906
|
+
const condenser = asObject(settings.condenser);
|
|
4907
|
+
if (ownModelId(condenser.llm, owner) === null) return next;
|
|
4908
|
+
const earlierCondenser = asObject(earlier.condenser).llm;
|
|
4909
|
+
next.condenser = { ...condenser, llm: isJsonObject(earlierCondenser) ? earlierCondenser : { ...asObject(earlier.llm), usage_id: OPENHANDS_CONDENSER_USAGE } };
|
|
4910
|
+
return next;
|
|
4911
|
+
}
|
|
4912
|
+
function withAgentCondenser(settings) {
|
|
4913
|
+
return { ...settings, condenser: { ...asObject(settings.condenser), llm: { ...asObject(settings.llm), usage_id: OPENHANDS_CONDENSER_USAGE } } };
|
|
4914
|
+
}
|
|
4915
|
+
function removal9(config, previousDefault) {
|
|
4916
|
+
const file = toolPaths(config).openhandsSettings;
|
|
4917
|
+
const document = readDocument(file, '"unrestricted remove"');
|
|
4918
|
+
if (document.raw === null) return [];
|
|
4919
|
+
const settings = document.value;
|
|
4920
|
+
const ownAgent = ownModelId(settings.llm, config) !== null;
|
|
4921
|
+
const ownCondenser = ownModelId(asObject(settings.condenser).llm, config) !== null;
|
|
4922
|
+
if (!ownAgent && !ownCondenser) return [];
|
|
4923
|
+
if (!ownAgent) return [{ file, raw: document.raw, next: jsonText(withAgentCondenser(settings), document.layout) }];
|
|
4924
|
+
const earlier = previousDefault === null ? null : earlierSettings(config, file, previousDefault);
|
|
4925
|
+
if (earlier === null) return [{ file, raw: document.raw, next: null }];
|
|
4926
|
+
return [{ file, raw: document.raw, next: jsonText(withEarlierLlm(settings, earlier, config), document.layout) }];
|
|
4927
|
+
}
|
|
4928
|
+
function remove8(config, previousDefault) {
|
|
4929
|
+
const changes = removal9(config, previousDefault);
|
|
4930
|
+
applyChanges(config, TOOL8, changes);
|
|
4931
|
+
return { path: toolPaths(config).openhandsSettings, changed: changes.length > 0 };
|
|
4932
|
+
}
|
|
4933
|
+
function hasOwnEntries8(config) {
|
|
4934
|
+
return removal9(config, null).length > 0;
|
|
3840
4935
|
}
|
|
3841
4936
|
|
|
3842
4937
|
// cli/src/writers/pi.ts
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
4938
|
+
var pi_exports = {};
|
|
4939
|
+
__export(pi_exports, {
|
|
4940
|
+
currentDefault: () => currentDefault9,
|
|
4941
|
+
hasOwnEntries: () => hasOwnEntries9,
|
|
4942
|
+
remove: () => remove9,
|
|
4943
|
+
write: () => write9
|
|
4944
|
+
});
|
|
4945
|
+
import { mkdirSync as mkdirSync11 } from "node:fs";
|
|
4946
|
+
var TOOL9 = "pi";
|
|
4947
|
+
var PROVIDER_NAME3 = "unrestricted.sh";
|
|
4948
|
+
var OWN_PROVIDER_KEYS2 = ["name", "baseUrl", "api", "apiKey"];
|
|
4949
|
+
var PROVIDER_SEPARATOR3 = "/";
|
|
4950
|
+
function isOwnEntry(entry, owner) {
|
|
3846
4951
|
const id = asObject(entry).id;
|
|
3847
|
-
return typeof id === "string" && isOwnModel(id,
|
|
4952
|
+
return typeof id === "string" && isOwnModel(id, owner);
|
|
3848
4953
|
}
|
|
3849
4954
|
function withOwnModels2(existing, config) {
|
|
3850
4955
|
const models2 = Array.isArray(existing) ? existing : [];
|
|
@@ -3859,7 +4964,7 @@ function withOwnProvider2(document, config) {
|
|
|
3859
4964
|
const existing = asObject(providers[config.provider]);
|
|
3860
4965
|
const provider = {
|
|
3861
4966
|
...existing,
|
|
3862
|
-
name:
|
|
4967
|
+
name: PROVIDER_NAME3,
|
|
3863
4968
|
baseUrl: config.base,
|
|
3864
4969
|
api: PI_API,
|
|
3865
4970
|
apiKey: config.key,
|
|
@@ -3867,33 +4972,423 @@ function withOwnProvider2(document, config) {
|
|
|
3867
4972
|
};
|
|
3868
4973
|
return { ...document, providers: { ...providers, [config.provider]: provider } };
|
|
3869
4974
|
}
|
|
3870
|
-
function
|
|
4975
|
+
function changeTo2(file, document, value) {
|
|
4976
|
+
return { file, raw: document.raw, next: isBareDocument(value) && document.raw !== null ? null : jsonText(value, document.layout) };
|
|
4977
|
+
}
|
|
4978
|
+
function write9(config, options) {
|
|
3871
4979
|
const paths = toolPaths(config);
|
|
3872
|
-
|
|
4980
|
+
mkdirSync11(paths.piAgentDir, { recursive: true });
|
|
3873
4981
|
const models2 = readJsonDocument(paths.piModels);
|
|
3874
|
-
const settings = readJsonDocument(paths.piSettings);
|
|
4982
|
+
const settings = options.setDefault ? readJsonDocument(paths.piSettings) : null;
|
|
4983
|
+
const changes = [{ file: paths.piModels, raw: models2.raw, next: jsonText(withOwnProvider2(models2.value, config), models2.layout) }];
|
|
4984
|
+
if (settings !== null) {
|
|
4985
|
+
const value = { ...settings.value, defaultProvider: config.provider, defaultModel: config.model };
|
|
4986
|
+
changes.push({ file: paths.piSettings, raw: settings.raw, next: jsonText(value, settings.layout) });
|
|
4987
|
+
}
|
|
4988
|
+
applyChanges(config, TOOL9, changes);
|
|
3875
4989
|
setMode(paths.piModels, 384, config.platform);
|
|
3876
|
-
writeJsonDocument(paths.piModels, withOwnProvider2(models2.value, config), models2.layout);
|
|
3877
|
-
writeJsonDocument(
|
|
3878
|
-
paths.piSettings,
|
|
3879
|
-
{ ...settings.value, defaultProvider: config.provider, defaultModel: config.model },
|
|
3880
|
-
settings.layout
|
|
3881
|
-
);
|
|
3882
4990
|
return { path: paths.piModels, next: "pi" };
|
|
3883
4991
|
}
|
|
4992
|
+
function readSettings(file) {
|
|
4993
|
+
try {
|
|
4994
|
+
return readJsonDocument(file).value;
|
|
4995
|
+
} catch {
|
|
4996
|
+
return null;
|
|
4997
|
+
}
|
|
4998
|
+
}
|
|
4999
|
+
function currentDefault9(config) {
|
|
5000
|
+
const settings = readSettings(toolPaths(config).piSettings);
|
|
5001
|
+
if (settings === null) return null;
|
|
5002
|
+
const { defaultProvider: provider, defaultModel: model } = settings;
|
|
5003
|
+
if (typeof model !== "string" || model === "") return null;
|
|
5004
|
+
return typeof provider === "string" && provider !== "" ? `${provider}${PROVIDER_SEPARATOR3}${model}` : model;
|
|
5005
|
+
}
|
|
5006
|
+
function withoutOwnProvider(document, owner) {
|
|
5007
|
+
const providers = document.providers;
|
|
5008
|
+
if (!isJsonObject(providers)) return null;
|
|
5009
|
+
const entry = providers[owner.provider];
|
|
5010
|
+
if (!isJsonObject(entry)) return null;
|
|
5011
|
+
const kept = {};
|
|
5012
|
+
for (const [key, value] of Object.entries(entry)) {
|
|
5013
|
+
if (OWN_PROVIDER_KEYS2.includes(key)) continue;
|
|
5014
|
+
if (key === "models" && Array.isArray(value)) {
|
|
5015
|
+
const models2 = value.filter((model) => !isOwnEntry(model, owner));
|
|
5016
|
+
if (models2.length > 0) kept.models = models2;
|
|
5017
|
+
continue;
|
|
5018
|
+
}
|
|
5019
|
+
kept[key] = value;
|
|
5020
|
+
}
|
|
5021
|
+
if (JSON.stringify(kept) === JSON.stringify(entry)) return null;
|
|
5022
|
+
const others = { ...providers };
|
|
5023
|
+
if (Object.keys(kept).length > 0) others[owner.provider] = kept;
|
|
5024
|
+
else delete others[owner.provider];
|
|
5025
|
+
const next = { ...document, providers: others };
|
|
5026
|
+
if (Object.keys(others).length === 0) delete next.providers;
|
|
5027
|
+
return next;
|
|
5028
|
+
}
|
|
5029
|
+
function withPreviousDefault2(settings, previousDefault) {
|
|
5030
|
+
const next = { ...settings };
|
|
5031
|
+
const slash = previousDefault === null ? -1 : previousDefault.indexOf(PROVIDER_SEPARATOR3);
|
|
5032
|
+
if (previousDefault === null) {
|
|
5033
|
+
delete next.defaultProvider;
|
|
5034
|
+
delete next.defaultModel;
|
|
5035
|
+
} else if (slash > 0) {
|
|
5036
|
+
next.defaultProvider = previousDefault.slice(0, slash);
|
|
5037
|
+
next.defaultModel = previousDefault.slice(slash + 1);
|
|
5038
|
+
} else {
|
|
5039
|
+
delete next.defaultProvider;
|
|
5040
|
+
next.defaultModel = previousDefault;
|
|
5041
|
+
}
|
|
5042
|
+
return next;
|
|
5043
|
+
}
|
|
5044
|
+
function hasOwnDefault2(settings, owner) {
|
|
5045
|
+
const { defaultProvider: provider, defaultModel: model } = settings;
|
|
5046
|
+
return provider === owner.provider && typeof model === "string" && isOwnModel(model, owner);
|
|
5047
|
+
}
|
|
5048
|
+
function readPiDocument(file) {
|
|
5049
|
+
try {
|
|
5050
|
+
return readJsonDocument(file);
|
|
5051
|
+
} catch (error) {
|
|
5052
|
+
if (error instanceof SyntaxError) throw new Error(`${file} is not JSON Pi can read; fix it, then run "unrestricted remove" again`);
|
|
5053
|
+
throw error;
|
|
5054
|
+
}
|
|
5055
|
+
}
|
|
5056
|
+
function removal10(config, previousDefault) {
|
|
5057
|
+
const paths = toolPaths(config);
|
|
5058
|
+
const changes = [];
|
|
5059
|
+
const models2 = readPiDocument(paths.piModels);
|
|
5060
|
+
const cleaned = models2.raw === null ? null : withoutOwnProvider(models2.value, config);
|
|
5061
|
+
if (cleaned !== null) changes.push(changeTo2(paths.piModels, models2, cleaned));
|
|
5062
|
+
const settings = readPiDocument(paths.piSettings);
|
|
5063
|
+
if (settings.raw !== null && hasOwnDefault2(settings.value, config)) {
|
|
5064
|
+
changes.push(changeTo2(paths.piSettings, settings, withPreviousDefault2(settings.value, previousDefault)));
|
|
5065
|
+
}
|
|
5066
|
+
return changes;
|
|
5067
|
+
}
|
|
5068
|
+
function remove9(config, previousDefault) {
|
|
5069
|
+
const changes = removal10(config, previousDefault);
|
|
5070
|
+
applyChanges(config, TOOL9, changes);
|
|
5071
|
+
return { path: changes[0]?.file ?? toolPaths(config).piModels, changed: changes.length > 0 };
|
|
5072
|
+
}
|
|
5073
|
+
function hasOwnEntries9(config) {
|
|
5074
|
+
return removal10(config, null).length > 0;
|
|
5075
|
+
}
|
|
5076
|
+
|
|
5077
|
+
// cli/src/writers/qwen-code.ts
|
|
5078
|
+
var qwen_code_exports = {};
|
|
5079
|
+
__export(qwen_code_exports, {
|
|
5080
|
+
currentDefault: () => currentDefault10,
|
|
5081
|
+
hasOwnEntries: () => hasOwnEntries10,
|
|
5082
|
+
parseSelection: () => parseSelection,
|
|
5083
|
+
remove: () => remove10,
|
|
5084
|
+
selectionLabel: () => selectionLabel,
|
|
5085
|
+
write: () => write10
|
|
5086
|
+
});
|
|
5087
|
+
import { mkdirSync as mkdirSync12 } from "node:fs";
|
|
5088
|
+
var TOOL10 = "qwen-code";
|
|
5089
|
+
var SETTINGS_VERSION_KEY = "$version";
|
|
5090
|
+
var BARE_KEYS = [SETTINGS_VERSION_KEY, "$schema"];
|
|
5091
|
+
var AUTH_TYPES = ["openai", "openai-responses", "qwen-oauth", "gemini", "vertex-ai", "anthropic"];
|
|
5092
|
+
var MODEL_PATH2 = ["model", "name"];
|
|
5093
|
+
var AUTH_TYPE_PATH = ["security", "auth", "selectedType"];
|
|
5094
|
+
var AFTER_VERSION = (keys) => keys.indexOf(SETTINGS_VERSION_KEY) + 1;
|
|
5095
|
+
function bucketPath(owner) {
|
|
5096
|
+
return ["modelProviders", owner.provider];
|
|
5097
|
+
}
|
|
5098
|
+
function protocolPath(owner) {
|
|
5099
|
+
return ["providerProtocol", owner.provider];
|
|
5100
|
+
}
|
|
5101
|
+
var KEY_PATH = ["env", API_KEY_ENV];
|
|
5102
|
+
function isOwnEntry2(entry, owner) {
|
|
5103
|
+
const id = asObject(entry).id;
|
|
5104
|
+
return typeof id === "string" && isOwnModel(id, owner);
|
|
5105
|
+
}
|
|
5106
|
+
function withOwnModels3(existing, config) {
|
|
5107
|
+
const models2 = Array.isArray(existing) ? existing : [];
|
|
5108
|
+
const firstOwn = models2.findIndex((entry) => isOwnEntry2(entry, config));
|
|
5109
|
+
const kept = models2.filter((entry) => !isOwnEntry2(entry, config));
|
|
5110
|
+
kept.splice(firstOwn === -1 ? kept.length : firstOwn, 0, ...config.models.map((model) => qwenModel(config, model)));
|
|
5111
|
+
return kept;
|
|
5112
|
+
}
|
|
5113
|
+
function stringAt(settings, path) {
|
|
5114
|
+
const value = valueAt(settings, path);
|
|
5115
|
+
return typeof value === "string" && value !== "" ? value : null;
|
|
5116
|
+
}
|
|
5117
|
+
function selectionIn(settings) {
|
|
5118
|
+
const model = stringAt(settings, MODEL_PATH2);
|
|
5119
|
+
return model === null ? null : { model, authType: stringAt(settings, AUTH_TYPE_PATH) };
|
|
5120
|
+
}
|
|
5121
|
+
function isOwnSelection(selection, owner) {
|
|
5122
|
+
return selection !== null && selection.authType === QWEN_PROTOCOL && isOwnModel(selection.model, owner);
|
|
5123
|
+
}
|
|
5124
|
+
function selectionLabel(selection) {
|
|
5125
|
+
return selection.authType === null ? selection.model : `${selection.model}(${selection.authType})`;
|
|
5126
|
+
}
|
|
5127
|
+
function parseSelection(value) {
|
|
5128
|
+
const open = value.lastIndexOf("(");
|
|
5129
|
+
if (open > 0 && value.endsWith(")")) {
|
|
5130
|
+
const authType = value.slice(open + 1, -1);
|
|
5131
|
+
if (AUTH_TYPES.includes(authType)) return { model: value.slice(0, open), authType };
|
|
5132
|
+
}
|
|
5133
|
+
return { model: value, authType: null };
|
|
5134
|
+
}
|
|
5135
|
+
function unreadable(file, command) {
|
|
5136
|
+
return new Error(`${file} is not a JSON object Qwen Code can read; fix it, then run ${command} again`);
|
|
5137
|
+
}
|
|
5138
|
+
function newSettings(config, options) {
|
|
5139
|
+
const settings = {
|
|
5140
|
+
[SETTINGS_VERSION_KEY]: QWEN_SETTINGS_VERSION,
|
|
5141
|
+
modelProviders: { [config.provider]: withOwnModels3([], config) },
|
|
5142
|
+
providerProtocol: { [config.provider]: QWEN_PROTOCOL },
|
|
5143
|
+
env: { [API_KEY_ENV]: config.key }
|
|
5144
|
+
};
|
|
5145
|
+
return options.setDefault ? { ...settings, security: { auth: { selectedType: QWEN_PROTOCOL } }, model: { name: config.model } } : settings;
|
|
5146
|
+
}
|
|
5147
|
+
function withOwnSettings(text, settings, config, options) {
|
|
5148
|
+
let updated = text;
|
|
5149
|
+
if (options.setDefault) {
|
|
5150
|
+
updated = setJsonc(updated, MODEL_PATH2, config.model, AFTER_VERSION);
|
|
5151
|
+
updated = setJsonc(updated, AUTH_TYPE_PATH, QWEN_PROTOCOL, AFTER_VERSION);
|
|
5152
|
+
}
|
|
5153
|
+
updated = setJsonc(updated, KEY_PATH, config.key, AFTER_VERSION);
|
|
5154
|
+
updated = setJsonc(updated, protocolPath(config), QWEN_PROTOCOL, AFTER_VERSION);
|
|
5155
|
+
return setJsonc(updated, bucketPath(config), withOwnModels3(valueAt(settings, bucketPath(config)), config), AFTER_VERSION);
|
|
5156
|
+
}
|
|
5157
|
+
function write10(config, options) {
|
|
5158
|
+
const paths = toolPaths(config);
|
|
5159
|
+
const file = paths.qwenSettings;
|
|
5160
|
+
mkdirSync12(paths.qwenDir, { recursive: true });
|
|
5161
|
+
const existing = readText(file);
|
|
5162
|
+
const layout = layoutOf(existing ?? "");
|
|
5163
|
+
const original = withoutBom(existing ?? "");
|
|
5164
|
+
const settings = isBlankText(original) ? null : parseJsoncObject(original);
|
|
5165
|
+
if (!isBlankText(original) && settings === null) throw unreadable(file, "the setup");
|
|
5166
|
+
const updated = settings === null ? jsonText(newSettings(config, options), layout) : withBom(withOwnSettings(original, settings, config, options), layout);
|
|
5167
|
+
saveText(config, TOOL10, file, updated, existing);
|
|
5168
|
+
setMode(file, 384, config.platform);
|
|
5169
|
+
return { path: file, next: "qwen" };
|
|
5170
|
+
}
|
|
5171
|
+
function settingsIn2(file) {
|
|
5172
|
+
try {
|
|
5173
|
+
if (!isFile(file)) return null;
|
|
5174
|
+
const text = withoutBom(readText(file) ?? "");
|
|
5175
|
+
return isBlankText(text) ? {} : parseJsoncObject(text);
|
|
5176
|
+
} catch {
|
|
5177
|
+
return null;
|
|
5178
|
+
}
|
|
5179
|
+
}
|
|
5180
|
+
function currentDefault10(config) {
|
|
5181
|
+
const settings = settingsIn2(toolPaths(config).qwenSettings);
|
|
5182
|
+
const selection = settings === null ? null : selectionIn(settings);
|
|
5183
|
+
if (selection === null) return null;
|
|
5184
|
+
return isOwnSelection(selection, config) ? `${config.provider}/${selection.model}` : selectionLabel(selection);
|
|
5185
|
+
}
|
|
5186
|
+
function readSettingsFile2(file) {
|
|
5187
|
+
if (!isFile(file)) return null;
|
|
5188
|
+
const raw = readText(file) ?? "";
|
|
5189
|
+
const text = withoutBom(raw);
|
|
5190
|
+
const settings = isBlankText(text) ? {} : parseJsoncObject(text);
|
|
5191
|
+
if (settings === null) throw unreadable(file, '"unrestricted remove"');
|
|
5192
|
+
return { file, raw, text, settings };
|
|
5193
|
+
}
|
|
5194
|
+
function removeWithEmptyParents(text, path) {
|
|
5195
|
+
const settings = parseJsoncObject(text) ?? {};
|
|
5196
|
+
let target = [...path];
|
|
5197
|
+
while (target.length > 1) {
|
|
5198
|
+
const parent = valueAt(settings, target.slice(0, -1));
|
|
5199
|
+
if (!isJsonObject(parent) || Object.keys(parent).length !== 1) break;
|
|
5200
|
+
target = target.slice(0, -1);
|
|
5201
|
+
}
|
|
5202
|
+
return removeJsonc(text, target);
|
|
5203
|
+
}
|
|
5204
|
+
function withoutOwnModels2(text, settings, owner) {
|
|
5205
|
+
const bucket = valueAt(settings, bucketPath(owner));
|
|
5206
|
+
if (!Array.isArray(bucket)) return text;
|
|
5207
|
+
const kept = bucket.filter((entry) => !isOwnEntry2(entry, owner));
|
|
5208
|
+
if (kept.length === bucket.length) return text;
|
|
5209
|
+
if (kept.length > 0) return setJsonc(text, bucketPath(owner), kept);
|
|
5210
|
+
const updated = removeWithEmptyParents(text, bucketPath(owner));
|
|
5211
|
+
return valueAt(settings, protocolPath(owner)) === void 0 ? updated : removeWithEmptyParents(updated, protocolPath(owner));
|
|
5212
|
+
}
|
|
5213
|
+
function withoutOwnKey(text, settings) {
|
|
5214
|
+
const key = valueAt(settings, KEY_PATH);
|
|
5215
|
+
return typeof key === "string" && key.startsWith(KEY_PREFIX) ? removeWithEmptyParents(text, KEY_PATH) : text;
|
|
5216
|
+
}
|
|
5217
|
+
function withPreviousSelection(text, previousDefault) {
|
|
5218
|
+
if (previousDefault === null) return removeWithEmptyParents(removeWithEmptyParents(text, MODEL_PATH2), AUTH_TYPE_PATH);
|
|
5219
|
+
const previous = parseSelection(previousDefault);
|
|
5220
|
+
const updated = setJsonc(text, MODEL_PATH2, previous.model);
|
|
5221
|
+
return previous.authType === null ? removeWithEmptyParents(updated, AUTH_TYPE_PATH) : setJsonc(updated, AUTH_TYPE_PATH, previous.authType);
|
|
5222
|
+
}
|
|
5223
|
+
function isBareText2(text) {
|
|
5224
|
+
const settings = parseJsoncObject(text);
|
|
5225
|
+
return settings !== null && Object.keys(settings).every((key) => BARE_KEYS.includes(key)) && !hasComments(text);
|
|
5226
|
+
}
|
|
5227
|
+
function removal11(config, previousDefault) {
|
|
5228
|
+
const entry = readSettingsFile2(toolPaths(config).qwenSettings);
|
|
5229
|
+
if (entry === null) return [];
|
|
5230
|
+
let updated = withoutOwnModels2(entry.text, entry.settings, config);
|
|
5231
|
+
updated = withoutOwnKey(updated, entry.settings);
|
|
5232
|
+
if (isOwnSelection(selectionIn(entry.settings), config)) updated = withPreviousSelection(updated, previousDefault);
|
|
5233
|
+
if (updated === entry.text) return [];
|
|
5234
|
+
return [{ file: entry.file, raw: entry.raw, next: isBareText2(updated) ? null : withBom(updated, layoutOf(entry.raw)) }];
|
|
5235
|
+
}
|
|
5236
|
+
function remove10(config, previousDefault) {
|
|
5237
|
+
const changes = removal11(config, previousDefault);
|
|
5238
|
+
applyChanges(config, TOOL10, changes);
|
|
5239
|
+
return { path: toolPaths(config).qwenSettings, changed: changes.length > 0 };
|
|
5240
|
+
}
|
|
5241
|
+
function hasOwnEntries10(config) {
|
|
5242
|
+
return removal11(config, null).length > 0;
|
|
5243
|
+
}
|
|
3884
5244
|
|
|
3885
5245
|
// cli/src/writers/index.ts
|
|
3886
5246
|
var writers = {
|
|
3887
|
-
opencode:
|
|
3888
|
-
codex:
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
5247
|
+
opencode: opencode_exports,
|
|
5248
|
+
codex: codex_exports,
|
|
5249
|
+
"qwen-code": qwen_code_exports,
|
|
5250
|
+
goose: goose_exports,
|
|
5251
|
+
openhands: openhands_exports,
|
|
5252
|
+
aider: aider_exports,
|
|
5253
|
+
continue: continue_exports,
|
|
5254
|
+
crush: crush_exports,
|
|
5255
|
+
pi: pi_exports,
|
|
5256
|
+
claude: claude_exports
|
|
3894
5257
|
};
|
|
3895
5258
|
|
|
5259
|
+
// cli/src/commands/remove.ts
|
|
5260
|
+
var REMOVE_INTRO = "unrestricted.sh remove";
|
|
5261
|
+
var NOTHING_TO_REMOVE = "Nothing to remove.";
|
|
5262
|
+
function labelList(tools) {
|
|
5263
|
+
const labels = tools.map((tool) => TOOL_LABELS[tool]);
|
|
5264
|
+
if (labels.length === 1) return labels[0];
|
|
5265
|
+
return `${labels.slice(0, -1).join(", ")} and ${labels[labels.length - 1]}`;
|
|
5266
|
+
}
|
|
5267
|
+
function removeQuestion(tools) {
|
|
5268
|
+
return `Remove unrestricted.sh from ${labelList(tools)}?`;
|
|
5269
|
+
}
|
|
5270
|
+
function removedLine(tool, path) {
|
|
5271
|
+
return `Removed from ${TOOL_LABELS[tool]} \xB7 ${path}`;
|
|
5272
|
+
}
|
|
5273
|
+
function nothingToRemoveLine(tool) {
|
|
5274
|
+
return `${TOOL_LABELS[tool]}: nothing to remove`;
|
|
5275
|
+
}
|
|
5276
|
+
function earlierAiderLine(path) {
|
|
5277
|
+
return `Your earlier Aider settings are in ${path}`;
|
|
5278
|
+
}
|
|
5279
|
+
function removeConfig(pending, state) {
|
|
5280
|
+
const ids = [...state?.models ?? [], ...state?.model ? [state.model] : []];
|
|
5281
|
+
return {
|
|
5282
|
+
platform: pending.platform,
|
|
5283
|
+
home: pending.home,
|
|
5284
|
+
configHome: pending.configHome,
|
|
5285
|
+
piAgentDir: pending.piAgentDir,
|
|
5286
|
+
gooseConfigDir: pending.gooseConfigDir,
|
|
5287
|
+
provider: pending.provider,
|
|
5288
|
+
base: pending.base,
|
|
5289
|
+
model: pending.model,
|
|
5290
|
+
models: ids.map((id) => ({ id }))
|
|
5291
|
+
};
|
|
5292
|
+
}
|
|
5293
|
+
function isSetUp(tool, config) {
|
|
5294
|
+
try {
|
|
5295
|
+
return writers[tool].hasOwnEntries(config) || isAutoUpdateTool(tool) && hasOwnAutoUpdateFile(tool, config);
|
|
5296
|
+
} catch {
|
|
5297
|
+
return false;
|
|
5298
|
+
}
|
|
5299
|
+
}
|
|
5300
|
+
function namedTools(args) {
|
|
5301
|
+
const tools = [];
|
|
5302
|
+
for (const name of args) {
|
|
5303
|
+
if (!isToolId(name)) M2.warn(`unknown tool: ${name} (skipped)`);
|
|
5304
|
+
else if (!tools.includes(name)) tools.push(name);
|
|
5305
|
+
}
|
|
5306
|
+
return tools;
|
|
5307
|
+
}
|
|
5308
|
+
function foundTools(config, state) {
|
|
5309
|
+
return ALL_TOOLS.filter((tool) => state?.tools.includes(tool) || isSetUp(tool, config));
|
|
5310
|
+
}
|
|
5311
|
+
async function confirmed(tools) {
|
|
5312
|
+
if (tools.length === 0 || !process.stdin.isTTY) return true;
|
|
5313
|
+
const answer = await ye({ message: removeQuestion(tools), initialValue: true });
|
|
5314
|
+
return !pD(answer) && answer;
|
|
5315
|
+
}
|
|
5316
|
+
function removeFrom(tool, config, previousDefault) {
|
|
5317
|
+
const result = writers[tool].remove(config, previousDefault);
|
|
5318
|
+
const file = isAutoUpdateTool(tool) ? removeAutoUpdateFile(tool, config) : null;
|
|
5319
|
+
return result.changed || file === null ? result : { path: file, changed: true };
|
|
5320
|
+
}
|
|
5321
|
+
function reportEarlierAider(config) {
|
|
5322
|
+
const earlier = newestBackup(config, "aider", toolPaths(config).aiderConfig);
|
|
5323
|
+
if (earlier !== null) M2.info(earlierAiderLine(earlier));
|
|
5324
|
+
}
|
|
5325
|
+
function forgetState(configHome, state, done) {
|
|
5326
|
+
if (state === null || state.tools.every((tool) => done.includes(tool))) return deleteState(configHome);
|
|
5327
|
+
forgetTools(configHome, state, done);
|
|
5328
|
+
return true;
|
|
5329
|
+
}
|
|
5330
|
+
function keyLines(forgotKey, configHome, origin) {
|
|
5331
|
+
const [removedKey, revoke] = logoutLines(true, credentialsPath(configHome), origin);
|
|
5332
|
+
return forgotKey ? `${removedKey}
|
|
5333
|
+
${revoke}` : revoke;
|
|
5334
|
+
}
|
|
5335
|
+
async function remove11(context, args) {
|
|
5336
|
+
let pending;
|
|
5337
|
+
try {
|
|
5338
|
+
pending = readConfig(context.env, context.machine);
|
|
5339
|
+
} catch (error) {
|
|
5340
|
+
xe(reason(error));
|
|
5341
|
+
return FAILED;
|
|
5342
|
+
}
|
|
5343
|
+
const state = readState(pending.configHome);
|
|
5344
|
+
const config = removeConfig(pending, state);
|
|
5345
|
+
const everything = args.length === 0;
|
|
5346
|
+
Ie(REMOVE_INTRO);
|
|
5347
|
+
const tools = everything ? foundTools(config, state) : namedTools(args);
|
|
5348
|
+
if (!await confirmed(tools)) {
|
|
5349
|
+
xe(NOTHING_CHANGED);
|
|
5350
|
+
return { code: 0 };
|
|
5351
|
+
}
|
|
5352
|
+
const backupsBefore = backupsMade();
|
|
5353
|
+
const done = [];
|
|
5354
|
+
const failed = [];
|
|
5355
|
+
for (const tool of tools) {
|
|
5356
|
+
try {
|
|
5357
|
+
const result = removeFrom(tool, config, state?.defaults?.[tool] ?? null);
|
|
5358
|
+
if (result.changed) M2.success(removedLine(tool, result.path));
|
|
5359
|
+
else M2.info(nothingToRemoveLine(tool));
|
|
5360
|
+
if (tool === "aider") reportEarlierAider(config);
|
|
5361
|
+
done.push(tool);
|
|
5362
|
+
} catch (error) {
|
|
5363
|
+
failed.push(tool);
|
|
5364
|
+
M2.error(`${TOOL_LABELS[tool]} failed: ${reason(error)}`);
|
|
5365
|
+
}
|
|
5366
|
+
}
|
|
5367
|
+
if (backupsMade() > backupsBefore) M2.info(backupsLine(config));
|
|
5368
|
+
let anything = tools.length > 0;
|
|
5369
|
+
if (everything) {
|
|
5370
|
+
const forgotState = forgetState(config.configHome, state, done);
|
|
5371
|
+
const forgotKey = deleteCredentials(config.configHome);
|
|
5372
|
+
anything = anything || forgotState || forgotKey;
|
|
5373
|
+
if (anything) M2.info(keyLines(forgotKey, config.configHome, pending.origin));
|
|
5374
|
+
} else if (state !== null) {
|
|
5375
|
+
forgetTools(config.configHome, state, done);
|
|
5376
|
+
}
|
|
5377
|
+
Se(anything ? SETUP_OUTRO : NOTHING_TO_REMOVE);
|
|
5378
|
+
return { code: failed.length > 0 ? 1 : 0 };
|
|
5379
|
+
}
|
|
5380
|
+
|
|
3896
5381
|
// cli/src/configure.ts
|
|
5382
|
+
var SWITCH_DEFAULT = { setDefault: true, kept: null };
|
|
5383
|
+
var KEEP_DEFAULT = { setDefault: false, kept: null };
|
|
5384
|
+
var START_COMMAND_TOOLS = /* @__PURE__ */ new Set(["codex", "goose"]);
|
|
5385
|
+
var ONE_MODEL_TOOLS = /* @__PURE__ */ new Set(["openhands"]);
|
|
5386
|
+
function keptDefaultMessage(current, tool, modelName) {
|
|
5387
|
+
const label = TOOL_LABELS[tool];
|
|
5388
|
+
if (ONE_MODEL_TOOLS.has(tool)) return `Kept ${current} in ${label}, which runs one model. Run the setup again and switch to use ${modelName}`;
|
|
5389
|
+
if (START_COMMAND_TOOLS.has(tool)) return `Kept ${current} as the default in ${label}. Start it with the command under Next to use ${modelName}`;
|
|
5390
|
+
return `Kept ${current} as the default in ${label}. Pick ${modelName} in its model list`;
|
|
5391
|
+
}
|
|
3897
5392
|
function recordOf(result) {
|
|
3898
5393
|
switch (result.status) {
|
|
3899
5394
|
case "written":
|
|
@@ -3912,19 +5407,25 @@ function reportAutoUpdate(tool, result) {
|
|
|
3912
5407
|
const message = autoUpdateMessage(tool, result);
|
|
3913
5408
|
if (message !== null) M2[message.level](message.text);
|
|
3914
5409
|
}
|
|
3915
|
-
function
|
|
5410
|
+
function reportBackups(config, before) {
|
|
5411
|
+
if (backupsMade() > before) M2.info(backupsLine(config));
|
|
5412
|
+
}
|
|
5413
|
+
function configureTools(config, tools, choiceFor, versionOf) {
|
|
3916
5414
|
const steps = [];
|
|
3917
5415
|
const autoUpdate = {};
|
|
5416
|
+
const backupsBefore = backupsMade();
|
|
3918
5417
|
for (const tool of tools) {
|
|
3919
5418
|
const label = TOOL_LABELS[tool];
|
|
5419
|
+
const choice = choiceFor(tool);
|
|
3920
5420
|
const step = Y2();
|
|
3921
5421
|
step.start(`Configuring ${label}`);
|
|
3922
5422
|
try {
|
|
3923
|
-
const result = writers[tool](config);
|
|
5423
|
+
const result = writers[tool].write(config, { setDefault: choice.setDefault });
|
|
3924
5424
|
const live = isAutoUpdateTool(tool) ? { tool, result: installAutoUpdate(tool, config, versionOf) } : null;
|
|
3925
5425
|
step.stop(`Configuring ${label}`);
|
|
3926
5426
|
M2.success(`${label} \xB7 ${result.path}`);
|
|
3927
5427
|
if (result.hint) M2.warn(result.hint);
|
|
5428
|
+
if (choice.kept !== null) M2.info(keptDefaultMessage(choice.kept, tool, config.modelName));
|
|
3928
5429
|
if (live !== null) {
|
|
3929
5430
|
reportAutoUpdate(live.tool, live.result);
|
|
3930
5431
|
const record = recordOf(live.result);
|
|
@@ -3933,13 +5434,15 @@ function configureTools(config, tools, versionOf) {
|
|
|
3933
5434
|
steps.push(result.next);
|
|
3934
5435
|
} catch (error) {
|
|
3935
5436
|
step.stop(`${label} failed`);
|
|
5437
|
+
reportBackups(config, backupsBefore);
|
|
3936
5438
|
xe(reason(error));
|
|
3937
5439
|
return null;
|
|
3938
5440
|
}
|
|
3939
5441
|
}
|
|
5442
|
+
reportBackups(config, backupsBefore);
|
|
3940
5443
|
return { steps, autoUpdate };
|
|
3941
5444
|
}
|
|
3942
|
-
function rememberSetup(config, tools, autoUpdate) {
|
|
5445
|
+
function rememberSetup(config, tools, autoUpdate, replaced) {
|
|
3943
5446
|
try {
|
|
3944
5447
|
const previous = readState(config.configHome);
|
|
3945
5448
|
saveCredentials(config.configHome, { key: config.key, origin: config.origin });
|
|
@@ -3947,7 +5450,8 @@ function rememberSetup(config, tools, autoUpdate) {
|
|
|
3947
5450
|
tools: mergeTools(previous?.tools ?? [], tools),
|
|
3948
5451
|
models: config.models.map((model) => model.id),
|
|
3949
5452
|
model: config.model,
|
|
3950
|
-
autoUpdate: mergeAutoUpdate(previous?.autoUpdate ?? {}, autoUpdate)
|
|
5453
|
+
autoUpdate: mergeAutoUpdate(previous?.autoUpdate ?? {}, autoUpdate),
|
|
5454
|
+
defaults: { ...previous?.defaults, ...replaced }
|
|
3951
5455
|
});
|
|
3952
5456
|
} catch (error) {
|
|
3953
5457
|
M2.warn(`could not save the setup in ${storeDir(config.configHome)}: ${reason(error)}`);
|
|
@@ -3956,12 +5460,15 @@ function rememberSetup(config, tools, autoUpdate) {
|
|
|
3956
5460
|
|
|
3957
5461
|
// cli/src/commands/setup.ts
|
|
3958
5462
|
var SETUP_TIP = 'Tip: install the CLI with "npm i -g unrestricted", then run "unrestricted update" when new models come out.';
|
|
3959
|
-
|
|
5463
|
+
function switchQuestion(tool, current, modelName) {
|
|
5464
|
+
return `${TOOL_LABELS[tool]} uses ${current} by default. Switch to ${modelName}?`;
|
|
5465
|
+
}
|
|
5466
|
+
async function chooseTools(context, config, detected) {
|
|
3960
5467
|
const requested = context.env.UNRESTRICTED_TOOLS ?? "";
|
|
3961
5468
|
if (requested.trim()) {
|
|
3962
5469
|
const { tools, unknown, disabled } = parseTools(requested);
|
|
3963
5470
|
for (const name of unknown) M2.warn(`unknown tool: ${name} (skipped)`);
|
|
3964
|
-
for (const tool of disabled) M2.warn(
|
|
5471
|
+
for (const tool of disabled) M2.warn(disabledToolMessage(tool, config.origin));
|
|
3965
5472
|
return tools;
|
|
3966
5473
|
}
|
|
3967
5474
|
if (context.env.UNRESTRICTED_YES || !process.stdin.isTTY) return detected;
|
|
@@ -3977,6 +5484,31 @@ async function chooseTools(context, detected) {
|
|
|
3977
5484
|
});
|
|
3978
5485
|
return pD(answer) ? null : answer;
|
|
3979
5486
|
}
|
|
5487
|
+
async function chooseDefaults(context, config, tools) {
|
|
5488
|
+
const choices = {};
|
|
5489
|
+
const replaced = {};
|
|
5490
|
+
for (const tool of tools) {
|
|
5491
|
+
const current = writers[tool].currentDefault(config);
|
|
5492
|
+
if (current === null || isOwnDefault(current, config)) {
|
|
5493
|
+
choices[tool] = SWITCH_DEFAULT;
|
|
5494
|
+
continue;
|
|
5495
|
+
}
|
|
5496
|
+
if (context.env.UNRESTRICTED_YES) {
|
|
5497
|
+
choices[tool] = SWITCH_DEFAULT;
|
|
5498
|
+
replaced[tool] = current;
|
|
5499
|
+
continue;
|
|
5500
|
+
}
|
|
5501
|
+
if (!process.stdin.isTTY) {
|
|
5502
|
+
choices[tool] = { setDefault: false, kept: current };
|
|
5503
|
+
continue;
|
|
5504
|
+
}
|
|
5505
|
+
const answer = await ye({ message: switchQuestion(tool, current, config.modelName), initialValue: true });
|
|
5506
|
+
if (pD(answer)) return null;
|
|
5507
|
+
choices[tool] = answer ? SWITCH_DEFAULT : KEEP_DEFAULT;
|
|
5508
|
+
if (answer) replaced[tool] = current;
|
|
5509
|
+
}
|
|
5510
|
+
return { choices, replaced };
|
|
5511
|
+
}
|
|
3980
5512
|
async function setup(context) {
|
|
3981
5513
|
let pending;
|
|
3982
5514
|
try {
|
|
@@ -4010,18 +5542,28 @@ async function setup(context) {
|
|
|
4010
5542
|
check.stop(keyAcceptedMessage(pending.base));
|
|
4011
5543
|
const config = withListedModels({ ...pending, key }, listed);
|
|
4012
5544
|
const listedIds = config.models.map((model) => model.id);
|
|
4013
|
-
const selected = await chooseTools(context, detectTools(config, context.env));
|
|
5545
|
+
const selected = await chooseTools(context, config, detectTools(config, context.env));
|
|
4014
5546
|
if (selected === null) {
|
|
4015
|
-
xe(
|
|
5547
|
+
xe(NOTHING_CHANGED);
|
|
4016
5548
|
return { code: 0, listed: listedIds };
|
|
4017
5549
|
}
|
|
4018
5550
|
if (selected.length === 0) {
|
|
4019
5551
|
Se("Nothing to configure.");
|
|
4020
5552
|
return { code: 0, listed: listedIds };
|
|
4021
5553
|
}
|
|
4022
|
-
const
|
|
5554
|
+
const defaults = await chooseDefaults(context, config, selected);
|
|
5555
|
+
if (defaults === null) {
|
|
5556
|
+
xe(NOTHING_CHANGED);
|
|
5557
|
+
return { code: 0, listed: listedIds };
|
|
5558
|
+
}
|
|
5559
|
+
const configured = configureTools(
|
|
5560
|
+
config,
|
|
5561
|
+
selected,
|
|
5562
|
+
(tool) => defaults.choices[tool] ?? SWITCH_DEFAULT,
|
|
5563
|
+
versionReader(context.env, context.machine.platform)
|
|
5564
|
+
);
|
|
4023
5565
|
if (configured === null) return FAILED;
|
|
4024
|
-
rememberSetup(config, selected, configured.autoUpdate);
|
|
5566
|
+
rememberSetup(config, selected, configured.autoUpdate, defaults.replaced);
|
|
4025
5567
|
if (balanceNote) M2.info(balanceNote);
|
|
4026
5568
|
Me(configured.steps.join("\n"), "Next");
|
|
4027
5569
|
if (!context.installed) M2.info(SETUP_TIP);
|
|
@@ -4033,6 +5575,10 @@ async function setup(context) {
|
|
|
4033
5575
|
var UPDATE_INTRO = "unrestricted.sh update";
|
|
4034
5576
|
var NO_TOOLS_MESSAGE = 'No tools are set up yet. Run "unrestricted setup" to configure them.';
|
|
4035
5577
|
var NO_KEY_MESSAGE2 = 'No key is stored. Run "unrestricted setup" to sign in and configure your tools.';
|
|
5578
|
+
var AIDER_NOT_UPDATED = 'Aider is no longer updated. Run "unrestricted remove aider" to take it out';
|
|
5579
|
+
function skippedToolMessage(tool) {
|
|
5580
|
+
return tool === "aider" ? AIDER_NOT_UPDATED : `${TOOL_LABELS[tool]} is not available yet`;
|
|
5581
|
+
}
|
|
4036
5582
|
function selectedModel(env, pending, state, listed) {
|
|
4037
5583
|
if (env.UNRESTRICTED_MODEL || state.model === null) return pending.model;
|
|
4038
5584
|
return listed.some((model) => model.id === state.model) ? state.model : pending.model;
|
|
@@ -4068,11 +5614,11 @@ async function update(context) {
|
|
|
4068
5614
|
}
|
|
4069
5615
|
check.stop(keyAcceptedMessage(pending.base));
|
|
4070
5616
|
const config = withListedModels({ ...pending, key, model: selectedModel(context.env, pending, state, listed) }, listed);
|
|
4071
|
-
for (const tool of state.tools.filter((tool2) => DISABLED_TOOLS.has(tool2))) M2.warn(
|
|
5617
|
+
for (const tool of state.tools.filter((tool2) => DISABLED_TOOLS.has(tool2))) M2.warn(skippedToolMessage(tool));
|
|
4072
5618
|
const tools = state.tools.filter((tool) => !DISABLED_TOOLS.has(tool));
|
|
4073
|
-
const configured = configureTools(config, tools, versionReader(context.env, context.machine.platform));
|
|
5619
|
+
const configured = configureTools(config, tools, () => KEEP_DEFAULT, versionReader(context.env, context.machine.platform));
|
|
4074
5620
|
if (configured === null) return FAILED;
|
|
4075
|
-
rememberSetup(config, tools, configured.autoUpdate);
|
|
5621
|
+
rememberSetup(config, tools, configured.autoUpdate, {});
|
|
4076
5622
|
const added = config.models.filter((model) => !state.models.includes(model.id));
|
|
4077
5623
|
if (added.length > 0) M2.info(`New: ${added.map((model) => model.name).join(", ")}`);
|
|
4078
5624
|
Se(SETUP_OUTRO);
|
|
@@ -4111,14 +5657,15 @@ async function announceNewModels(context, listed) {
|
|
|
4111
5657
|
}
|
|
4112
5658
|
|
|
4113
5659
|
// cli/package.json
|
|
4114
|
-
var version = "1.
|
|
5660
|
+
var version = "1.1.0";
|
|
4115
5661
|
|
|
4116
5662
|
// cli/src/version.ts
|
|
4117
5663
|
var VERSION = version;
|
|
4118
5664
|
|
|
4119
5665
|
// cli/src/cli.ts
|
|
4120
|
-
var COMMAND_NAMES = ["setup", "update", "models", "balance", "login", "logout"];
|
|
4121
|
-
var COMMANDS = { setup, update, models, balance, login, logout };
|
|
5666
|
+
var COMMAND_NAMES = ["setup", "update", "remove", "models", "balance", "login", "logout"];
|
|
5667
|
+
var COMMANDS = { setup, update, remove: remove11, models, balance, login, logout };
|
|
5668
|
+
var TAKES_ARGUMENTS = /* @__PURE__ */ new Set(["remove"]);
|
|
4122
5669
|
var HELP_FLAGS = ["-h", "--help"];
|
|
4123
5670
|
var VERSION_FLAGS = ["-v", "--version"];
|
|
4124
5671
|
var HELP_HINT = 'Run "unrestricted --help" for the commands.';
|
|
@@ -4136,6 +5683,7 @@ function helpText() {
|
|
|
4136
5683
|
"Commands:",
|
|
4137
5684
|
" setup Configure your coding tools (the default)",
|
|
4138
5685
|
" update Rewrite the tools you set up with the current models and your stored key",
|
|
5686
|
+
" remove Take unrestricted.sh out of your coding tools",
|
|
4139
5687
|
" models List the models with their context and prices per million tokens",
|
|
4140
5688
|
" balance Show your credit, your plan's usage and this month's spend",
|
|
4141
5689
|
" login Approve this machine in the browser and store its key",
|
|
@@ -4172,10 +5720,10 @@ function parseArguments(argv) {
|
|
|
4172
5720
|
if (argv.some((argument) => VERSION_FLAGS.includes(argument))) return { kind: "version" };
|
|
4173
5721
|
const option = argv.find((argument) => argument.startsWith("-"));
|
|
4174
5722
|
if (option !== void 0) return { kind: "error", message: `unknown option: ${option}. ${HELP_HINT}` };
|
|
4175
|
-
const [name = "setup",
|
|
5723
|
+
const [name = "setup", ...args] = argv;
|
|
4176
5724
|
if (!isCommandName(name)) return { kind: "error", message: `unknown command: ${name}. ${HELP_HINT}` };
|
|
4177
|
-
if (
|
|
4178
|
-
return { kind: "command", name };
|
|
5725
|
+
if (args.length > 0 && !TAKES_ARGUMENTS.has(name)) return { kind: "error", message: `unexpected argument: ${args[0]}. ${HELP_HINT}` };
|
|
5726
|
+
return { kind: "command", name, args };
|
|
4179
5727
|
}
|
|
4180
5728
|
async function runCli(argv, context) {
|
|
4181
5729
|
const parsed = parseArguments(argv);
|
|
@@ -4193,7 +5741,7 @@ async function runCli(argv, context) {
|
|
|
4193
5741
|
`);
|
|
4194
5742
|
return 1;
|
|
4195
5743
|
}
|
|
4196
|
-
const result = await COMMANDS[parsed.name](context);
|
|
5744
|
+
const result = await COMMANDS[parsed.name](context, parsed.args);
|
|
4197
5745
|
if (result.code === 0 && parsed.name !== "logout") await announceNewModels(context, result.listed);
|
|
4198
5746
|
return result.code;
|
|
4199
5747
|
}
|