shadok-ai 0.13.5 → 0.13.6
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/README.md +14 -5
- package/dist/claude-home.d.ts +19 -0
- package/dist/claude-home.js +54 -0
- package/dist/claude-home.js.map +1 -1
- package/dist/claude-models.d.ts +68 -0
- package/dist/claude-models.js +181 -0
- package/dist/claude-models.js.map +1 -0
- package/dist/server.js +36 -1
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/public/index.html +117 -1
package/README.md
CHANGED
|
@@ -87,11 +87,20 @@ Enter to skip; you can add it later from the web UI).
|
|
|
87
87
|
(e.g. forbid `git commit`), model, and which secrets to inject. Applied at
|
|
88
88
|
spawn, remembered across resume.
|
|
89
89
|
- **A model per agent** — the creation form picks the model this one agent runs
|
|
90
|
-
on
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
90
|
+
on, overriding its profile's, with a **long context (1M)** tick beside it.
|
|
91
|
+
Pick a family (Opus, Sonnet, Haiku, Fable) and it follows Claude Code's
|
|
92
|
+
releases, or fold out **Pin an exact version** — that list is read from the
|
|
93
|
+
installed `claude` binary, so a model Anthropic ships appears on its own at
|
|
94
|
+
the next Claude Code update, with nothing to maintain here. Leave it on
|
|
95
|
+
*profile default* and nothing changes: no flag is sent at all. The choice is
|
|
96
|
+
stored on the agent, so a resume or a restart keeps it, and the quota gauge
|
|
97
|
+
meters it against the window it actually asked for.
|
|
98
|
+
- **A default model for the machine** — ⋯ → the version menu → *Default model*,
|
|
99
|
+
beside the permission mode. It writes Claude Code's own `settings.json`, which
|
|
100
|
+
is already the last step of the resolution chain (**agent → profile →
|
|
101
|
+
machine**), and applies at each agent's next spawn. Agents that carry no model
|
|
102
|
+
of their own simply follow Claude Code's default, which tracks the newest
|
|
103
|
+
model — so this is for **pinning** one, not for getting the latest. Eight roles ship with it: a lead that
|
|
95
104
|
delegates, a dev, a QA agent that writes the failing test and cannot fix the
|
|
96
105
|
code it covers, a release agent that prepares a deployment and leaves the
|
|
97
106
|
trigger to you, a product agent that writes the spec instead of the change,
|
package/dist/claude-home.d.ts
CHANGED
|
@@ -64,3 +64,22 @@ export declare function ensureClaudeHome(): void;
|
|
|
64
64
|
* for every agent, therefore a brand-new trust dialog every time.
|
|
65
65
|
*/
|
|
66
66
|
export declare function ensureProjectTrusted(cwd: string): void;
|
|
67
|
+
/**
|
|
68
|
+
* The machine-wide default model, read from and written to the SAME
|
|
69
|
+
* `~/.claude/settings.json` this module already owns.
|
|
70
|
+
*
|
|
71
|
+
* Deliberately here and not in a module of its own: `seedSettings` already
|
|
72
|
+
* writes that file, and two writers on one JSON file is how a `tui` seeded at
|
|
73
|
+
* boot and a model written from the GUI would eventually erase each other. One
|
|
74
|
+
* owner, one atomic write.
|
|
75
|
+
*
|
|
76
|
+
* Verified 2026-09-25 against claude 2.1.282, because the whole feature rests
|
|
77
|
+
* on it: with `model` in that file and NO `--model` flag, the run used
|
|
78
|
+
* `claude-haiku-4-5-20251001`; and it is still honoured when shadok also passes
|
|
79
|
+
* `--settings` for a profile's guardrails, so the flag MERGES rather than
|
|
80
|
+
* replacing the file. Without that second check the setting would have been
|
|
81
|
+
* silently inert for every agent that carries a profile.
|
|
82
|
+
*/
|
|
83
|
+
export declare function getDefaultModel(): string | null;
|
|
84
|
+
/** Write (or clear, with null) that default. Returns false if it could not. */
|
|
85
|
+
export declare function setDefaultModel(model: string | null): boolean;
|
package/dist/claude-home.js
CHANGED
|
@@ -210,4 +210,58 @@ function seedSettings() {
|
|
|
210
210
|
export function ensureProjectTrusted(cwd) {
|
|
211
211
|
seed(cwd);
|
|
212
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* The machine-wide default model, read from and written to the SAME
|
|
215
|
+
* `~/.claude/settings.json` this module already owns.
|
|
216
|
+
*
|
|
217
|
+
* Deliberately here and not in a module of its own: `seedSettings` already
|
|
218
|
+
* writes that file, and two writers on one JSON file is how a `tui` seeded at
|
|
219
|
+
* boot and a model written from the GUI would eventually erase each other. One
|
|
220
|
+
* owner, one atomic write.
|
|
221
|
+
*
|
|
222
|
+
* Verified 2026-09-25 against claude 2.1.282, because the whole feature rests
|
|
223
|
+
* on it: with `model` in that file and NO `--model` flag, the run used
|
|
224
|
+
* `claude-haiku-4-5-20251001`; and it is still honoured when shadok also passes
|
|
225
|
+
* `--settings` for a profile's guardrails, so the flag MERGES rather than
|
|
226
|
+
* replacing the file. Without that second check the setting would have been
|
|
227
|
+
* silently inert for every agent that carries a profile.
|
|
228
|
+
*/
|
|
229
|
+
export function getDefaultModel() {
|
|
230
|
+
try {
|
|
231
|
+
const raw = fs.readFileSync(settingsFile(), "utf8");
|
|
232
|
+
const m = JSON.parse(raw)?.model;
|
|
233
|
+
return typeof m === "string" && m.trim() ? m.trim() : null;
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/** Write (or clear, with null) that default. Returns false if it could not. */
|
|
240
|
+
export function setDefaultModel(model) {
|
|
241
|
+
try {
|
|
242
|
+
const file = settingsFile();
|
|
243
|
+
let existing = {};
|
|
244
|
+
if (fs.existsSync(file)) {
|
|
245
|
+
const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
246
|
+
// Hand-edited into invalid JSON, or not an object: leave it ALONE rather
|
|
247
|
+
// than "repair" it — same rule as `seed`, and for the same reason.
|
|
248
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
249
|
+
return false;
|
|
250
|
+
existing = parsed;
|
|
251
|
+
}
|
|
252
|
+
const next = { ...existing };
|
|
253
|
+
if (model?.trim())
|
|
254
|
+
next.model = model.trim();
|
|
255
|
+
else
|
|
256
|
+
delete next.model;
|
|
257
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
258
|
+
const tmp = `${file}.shadok-${process.pid}.tmp`;
|
|
259
|
+
fs.writeFileSync(tmp, JSON.stringify(next, null, 2));
|
|
260
|
+
fs.renameSync(tmp, file);
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
213
267
|
//# sourceMappingURL=claude-home.js.map
|
package/dist/claude-home.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-home.js","sourceRoot":"","sources":["../src/claude-home.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"claude-home.js","sourceRoot":"","sources":["../src/claude-home.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which models THIS Claude Code build supports, read out of the binary.
|
|
3
|
+
*
|
|
4
|
+
* The alternative was a hardcoded list, and it is the alternative that rots:
|
|
5
|
+
* the profile panel still suggests `claude-opus-4-8`, current the day it was
|
|
6
|
+
* written and stale now, and `claude-opus-5-5` shipped without shadok noticing.
|
|
7
|
+
* The CLI has no command that enumerates models (`claude models` is not a
|
|
8
|
+
* subcommand — it is taken as a PROMPT and answered, which is its own trap), so
|
|
9
|
+
* the names are taken from where they actually live: the binary's own strings.
|
|
10
|
+
*
|
|
11
|
+
* Read, never executed, and never trusted blindly — see `parseModelNames`.
|
|
12
|
+
*/
|
|
13
|
+
/** Families the CLI accepts as aliases. An alias is always offered, scan or no scan. */
|
|
14
|
+
export declare const MODEL_FAMILIES: readonly ["opus", "sonnet", "haiku", "fable"];
|
|
15
|
+
/**
|
|
16
|
+
* Pure: the model names in a blob of binary strings, conservatively.
|
|
17
|
+
*
|
|
18
|
+
* DELIBERATELY NARROW, and the narrowness is the point. A `strings` dump glues
|
|
19
|
+
* adjacent bytes together, which mints plausible-looking rubbish: this binary
|
|
20
|
+
* contains `claude-haiku-3-55` (a `3-5` with the next byte stuck to it) and
|
|
21
|
+
* `claude-fable-5-mythos-5`. Offering those as choices would produce an agent
|
|
22
|
+
* that dies at spawn on a model nobody can find. So only a plain
|
|
23
|
+
* `claude-<family>-<major>[-<minor>]` is kept, with a single-digit minor.
|
|
24
|
+
*
|
|
25
|
+
* Dropped on purpose:
|
|
26
|
+
* - `-v1` suffixes and 8-digit dated forms (`claude-opus-4-5-20251101`): the
|
|
27
|
+
* same model spelled longer, and three spellings of one model in a picker is
|
|
28
|
+
* noise, not choice.
|
|
29
|
+
* - anything with a non-numeric segment, which is where the glue artifacts are.
|
|
30
|
+
*
|
|
31
|
+
* KNOWN LIMIT, written down rather than discovered later: a two-digit minor
|
|
32
|
+
* (`claude-opus-5-10`) would be skipped, because admitting two digits is what
|
|
33
|
+
* lets `3-55` back in. The family row (`opus`, `sonnet`…) never depends on this
|
|
34
|
+
* scan, so a missed name costs discoverability, never the ability to run it.
|
|
35
|
+
*/
|
|
36
|
+
export declare function parseModelNames(text: string): string[];
|
|
37
|
+
/** Pure: family, then newest version first — the order a picker should show. */
|
|
38
|
+
export declare function rankModels(names: readonly string[]): string[];
|
|
39
|
+
export interface ModelCache {
|
|
40
|
+
/** Identity of the binary these names came from. */
|
|
41
|
+
bin: string;
|
|
42
|
+
size: number;
|
|
43
|
+
mtimeMs: number;
|
|
44
|
+
models: string[];
|
|
45
|
+
}
|
|
46
|
+
/** Pure: is a cache entry still about the binary on disk right now? */
|
|
47
|
+
export declare function cacheIsFresh(cache: ModelCache | null, stat: {
|
|
48
|
+
size: number;
|
|
49
|
+
mtimeMs: number;
|
|
50
|
+
} | null, bin: string): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Scan the binary. Returns [] on ANY failure — a mid-upgrade binary is the
|
|
53
|
+
* normal case here, not the exception (invariant 32): the launcher is unlinked
|
|
54
|
+
* and relinked on every claude-code update, so a scan can meet ETXTBSY, a
|
|
55
|
+
* 500-byte placeholder, or nothing at all. An empty result is never cached, so
|
|
56
|
+
* the next boot simply tries again.
|
|
57
|
+
*/
|
|
58
|
+
export declare function scanModels(bin: string): string[];
|
|
59
|
+
/**
|
|
60
|
+
* The models this build supports, cached per binary identity.
|
|
61
|
+
*
|
|
62
|
+
* One scan per claude-code upgrade, not per call: the cache key is the
|
|
63
|
+
* binary's path, size and mtime, exactly the trio that changes when it is
|
|
64
|
+
* replaced. `force` re-reads without touching the memo's contract.
|
|
65
|
+
*/
|
|
66
|
+
export declare function availableModels(bin: string, force?: boolean): string[];
|
|
67
|
+
/** Test seam: forget the in-process memo. */
|
|
68
|
+
export declare function resetModelMemo(): void;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
/**
|
|
5
|
+
* Which models THIS Claude Code build supports, read out of the binary.
|
|
6
|
+
*
|
|
7
|
+
* The alternative was a hardcoded list, and it is the alternative that rots:
|
|
8
|
+
* the profile panel still suggests `claude-opus-4-8`, current the day it was
|
|
9
|
+
* written and stale now, and `claude-opus-5-5` shipped without shadok noticing.
|
|
10
|
+
* The CLI has no command that enumerates models (`claude models` is not a
|
|
11
|
+
* subcommand — it is taken as a PROMPT and answered, which is its own trap), so
|
|
12
|
+
* the names are taken from where they actually live: the binary's own strings.
|
|
13
|
+
*
|
|
14
|
+
* Read, never executed, and never trusted blindly — see `parseModelNames`.
|
|
15
|
+
*/
|
|
16
|
+
/** Families the CLI accepts as aliases. An alias is always offered, scan or no scan. */
|
|
17
|
+
export const MODEL_FAMILIES = ["opus", "sonnet", "haiku", "fable"];
|
|
18
|
+
/**
|
|
19
|
+
* Pure: the model names in a blob of binary strings, conservatively.
|
|
20
|
+
*
|
|
21
|
+
* DELIBERATELY NARROW, and the narrowness is the point. A `strings` dump glues
|
|
22
|
+
* adjacent bytes together, which mints plausible-looking rubbish: this binary
|
|
23
|
+
* contains `claude-haiku-3-55` (a `3-5` with the next byte stuck to it) and
|
|
24
|
+
* `claude-fable-5-mythos-5`. Offering those as choices would produce an agent
|
|
25
|
+
* that dies at spawn on a model nobody can find. So only a plain
|
|
26
|
+
* `claude-<family>-<major>[-<minor>]` is kept, with a single-digit minor.
|
|
27
|
+
*
|
|
28
|
+
* Dropped on purpose:
|
|
29
|
+
* - `-v1` suffixes and 8-digit dated forms (`claude-opus-4-5-20251101`): the
|
|
30
|
+
* same model spelled longer, and three spellings of one model in a picker is
|
|
31
|
+
* noise, not choice.
|
|
32
|
+
* - anything with a non-numeric segment, which is where the glue artifacts are.
|
|
33
|
+
*
|
|
34
|
+
* KNOWN LIMIT, written down rather than discovered later: a two-digit minor
|
|
35
|
+
* (`claude-opus-5-10`) would be skipped, because admitting two digits is what
|
|
36
|
+
* lets `3-55` back in. The family row (`opus`, `sonnet`…) never depends on this
|
|
37
|
+
* scan, so a missed name costs discoverability, never the ability to run it.
|
|
38
|
+
*/
|
|
39
|
+
export function parseModelNames(text) {
|
|
40
|
+
const fam = MODEL_FAMILIES.join("|");
|
|
41
|
+
const re = new RegExp(`claude-(?:${fam})-\\d{1,2}(?:-\\d)?(?![\\w-])`, "g");
|
|
42
|
+
return [...new Set(text.match(re) ?? [])];
|
|
43
|
+
}
|
|
44
|
+
/** Pure: family, then newest version first — the order a picker should show. */
|
|
45
|
+
export function rankModels(names) {
|
|
46
|
+
const key = (n) => {
|
|
47
|
+
const m = /^claude-([a-z]+)-(\d{1,2})(?:-(\d))?$/.exec(n);
|
|
48
|
+
if (!m)
|
|
49
|
+
return null;
|
|
50
|
+
return { family: m[1], major: Number(m[2]), minor: m[3] ? Number(m[3]) : 0 };
|
|
51
|
+
};
|
|
52
|
+
return [...names]
|
|
53
|
+
.filter((n) => key(n))
|
|
54
|
+
.sort((a, b) => {
|
|
55
|
+
const x = key(a), y = key(b);
|
|
56
|
+
const fx = MODEL_FAMILIES.indexOf(x.family);
|
|
57
|
+
const fy = MODEL_FAMILIES.indexOf(y.family);
|
|
58
|
+
if (fx !== fy)
|
|
59
|
+
return fx - fy;
|
|
60
|
+
if (x.major !== y.major)
|
|
61
|
+
return y.major - x.major;
|
|
62
|
+
return y.minor - x.minor;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/** Pure: is a cache entry still about the binary on disk right now? */
|
|
66
|
+
export function cacheIsFresh(cache, stat, bin) {
|
|
67
|
+
if (!cache || !stat)
|
|
68
|
+
return false;
|
|
69
|
+
return cache.bin === bin && cache.size === stat.size && cache.mtimeMs === stat.mtimeMs;
|
|
70
|
+
}
|
|
71
|
+
const cacheFile = () => path.join(os.homedir(), ".shadok-ai", "models.json");
|
|
72
|
+
/** How much of a ~240MB binary we are willing to read in one pass. */
|
|
73
|
+
const CHUNK = 1 << 22;
|
|
74
|
+
/** Names can straddle a chunk boundary; carry this much across. */
|
|
75
|
+
const OVERLAP = 64;
|
|
76
|
+
/**
|
|
77
|
+
* Scan the binary. Returns [] on ANY failure — a mid-upgrade binary is the
|
|
78
|
+
* normal case here, not the exception (invariant 32): the launcher is unlinked
|
|
79
|
+
* and relinked on every claude-code update, so a scan can meet ETXTBSY, a
|
|
80
|
+
* 500-byte placeholder, or nothing at all. An empty result is never cached, so
|
|
81
|
+
* the next boot simply tries again.
|
|
82
|
+
*/
|
|
83
|
+
export function scanModels(bin) {
|
|
84
|
+
let fd = null;
|
|
85
|
+
try {
|
|
86
|
+
fd = fs.openSync(bin, "r");
|
|
87
|
+
const buf = Buffer.allocUnsafe(CHUNK);
|
|
88
|
+
let carry = "";
|
|
89
|
+
const found = new Set();
|
|
90
|
+
for (;;) {
|
|
91
|
+
const n = fs.readSync(fd, buf, 0, CHUNK, null);
|
|
92
|
+
if (n <= 0)
|
|
93
|
+
break;
|
|
94
|
+
// latin1: every byte maps to one char, so offsets stay sane and an
|
|
95
|
+
// invalid UTF-8 run cannot swallow a name next to it.
|
|
96
|
+
const text = carry + buf.toString("latin1", 0, n);
|
|
97
|
+
for (const m of parseModelNames(text))
|
|
98
|
+
found.add(m);
|
|
99
|
+
carry = text.slice(-OVERLAP);
|
|
100
|
+
}
|
|
101
|
+
return rankModels([...found]);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
if (fd !== null)
|
|
108
|
+
try {
|
|
109
|
+
fs.closeSync(fd);
|
|
110
|
+
}
|
|
111
|
+
catch { /* already gone */ }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function readCache() {
|
|
115
|
+
try {
|
|
116
|
+
const j = JSON.parse(fs.readFileSync(cacheFile(), "utf8"));
|
|
117
|
+
return Array.isArray(j?.models) ? j : null;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function writeCache(c) {
|
|
124
|
+
try {
|
|
125
|
+
const f = cacheFile();
|
|
126
|
+
fs.mkdirSync(path.dirname(f), { recursive: true });
|
|
127
|
+
const tmp = `${f}.shadok-${process.pid}.tmp`;
|
|
128
|
+
fs.writeFileSync(tmp, JSON.stringify(c, null, 2));
|
|
129
|
+
fs.renameSync(tmp, f);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
/* a cache we cannot write just means we scan again next boot */
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
let memo = null;
|
|
136
|
+
/**
|
|
137
|
+
* The models this build supports, cached per binary identity.
|
|
138
|
+
*
|
|
139
|
+
* One scan per claude-code upgrade, not per call: the cache key is the
|
|
140
|
+
* binary's path, size and mtime, exactly the trio that changes when it is
|
|
141
|
+
* replaced. `force` re-reads without touching the memo's contract.
|
|
142
|
+
*/
|
|
143
|
+
export function availableModels(bin, force = false) {
|
|
144
|
+
// `memo.length`, NOT `memo`: an empty array is truthy, so memoising a failed
|
|
145
|
+
// scan would freeze the picker family-only for the life of the process — and
|
|
146
|
+
// a failed scan is the NORMAL case here, since the binary is unlinked and
|
|
147
|
+
// relinked on every claude-code upgrade (invariant 32). Same rule as the
|
|
148
|
+
// file cache below: an empty result is a "could not look", never an answer.
|
|
149
|
+
if (memo?.length && !force)
|
|
150
|
+
return memo;
|
|
151
|
+
let stat = null;
|
|
152
|
+
try {
|
|
153
|
+
const s = fs.statSync(bin);
|
|
154
|
+
stat = { size: s.size, mtimeMs: s.mtimeMs };
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
stat = null;
|
|
158
|
+
}
|
|
159
|
+
const cached = readCache();
|
|
160
|
+
if (!force && cacheIsFresh(cached, stat, bin))
|
|
161
|
+
return (memo = cached.models);
|
|
162
|
+
const models = scanModels(bin);
|
|
163
|
+
// NEVER cache an empty result: that is what a mid-upgrade binary produces,
|
|
164
|
+
// and freezing it would leave the picker permanently family-only.
|
|
165
|
+
if (models.length && stat)
|
|
166
|
+
writeCache({ bin, ...stat, models });
|
|
167
|
+
// Falling back to the cache is what carries the picker through an upgrade
|
|
168
|
+
// window — but only for the SAME binary. Without the path check, an entry
|
|
169
|
+
// written for one launcher answered a question about another, which is a
|
|
170
|
+
// cache bleeding across identities rather than a stale-but-useful answer.
|
|
171
|
+
const stale = cached && cached.bin === bin ? cached.models : [];
|
|
172
|
+
memo = models.length ? models : stale;
|
|
173
|
+
// `memo` may legitimately be [] here; the `memo?.length` guard above is what
|
|
174
|
+
// stops that from hardening into a permanent answer.
|
|
175
|
+
return memo;
|
|
176
|
+
}
|
|
177
|
+
/** Test seam: forget the in-process memo. */
|
|
178
|
+
export function resetModelMemo() {
|
|
179
|
+
memo = null;
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=claude-models.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-models.js","sourceRoot":"","sources":["../src/claude-models.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -16,7 +16,7 @@ import { extractLiveText } from "../public/live-text.js";
|
|
|
16
16
|
import { findTransientErrors, newTransientErrors, RETRY_DELAYS_MS } from "./retry.js";
|
|
17
17
|
import { backgroundTasks, screenShowsWork, moveToOption } from "./detect.js";
|
|
18
18
|
import { PtyPilot } from "./session.js";
|
|
19
|
-
import { ensureClaudeHome, ensureProjectTrusted } from "./claude-home.js";
|
|
19
|
+
import { ensureClaudeHome, ensureProjectTrusted, getDefaultModel, setDefaultModel, } from "./claude-home.js";
|
|
20
20
|
import { authStatus, cancelLogin, startLogin, submitLoginCode } from "./claude-auth.js";
|
|
21
21
|
import { starCount } from "./stars.js";
|
|
22
22
|
import { announceFirstAgent, ensureFirstAgent, firstAgentStatus, settleFirstAgent, } from "./first-agent.js";
|
|
@@ -53,6 +53,7 @@ import { RELOAD_EXIT_CODE } from "./supervisor.js";
|
|
|
53
53
|
import { acquireInstanceLock, releaseInstanceLock } from "./lock.js";
|
|
54
54
|
import { bindRefusal, originAllowed, parseOrigins, resolveHost, browserOrigin, } from "./net.js";
|
|
55
55
|
import { pctFromUsage, windowForModel } from "./context.js";
|
|
56
|
+
import { availableModels, MODEL_FAMILIES } from "./claude-models.js";
|
|
56
57
|
import { startHeartbeat } from "./heartbeat.js";
|
|
57
58
|
import { ensureClaude, claudeInstallInProgress, claudeCommand, classifyBin, findClaudeBinWithRetry, startWithBusyRetry, liveClaudeDeps, rememberClaudeBin, resolveBin, sampleBin, } from "./claude-bin.js";
|
|
58
59
|
import { ensureTmux, tmuxInstallCommand } from "./tmux-install.js";
|
|
@@ -1912,6 +1913,40 @@ app.post("/permission-mode", (req, res) => {
|
|
|
1912
1913
|
broadcastAll(versionMessage());
|
|
1913
1914
|
res.json({ permissionMode });
|
|
1914
1915
|
});
|
|
1916
|
+
/**
|
|
1917
|
+
* What models this build offers, and which one is the machine-wide default.
|
|
1918
|
+
*
|
|
1919
|
+
* `families` is never empty and never depends on the scan: an alias always
|
|
1920
|
+
* works, so a binary that is mid-upgrade (invariant 32) costs discoverability
|
|
1921
|
+
* of exact versions, never the ability to choose a model at all.
|
|
1922
|
+
*/
|
|
1923
|
+
app.get("/models", (_req, res) => {
|
|
1924
|
+
res.json({
|
|
1925
|
+
families: MODEL_FAMILIES,
|
|
1926
|
+
models: availableModels(claudeCommand()),
|
|
1927
|
+
default: getDefaultModel(),
|
|
1928
|
+
});
|
|
1929
|
+
});
|
|
1930
|
+
// Writing it touches `~/.claude/settings.json`, which is MACHINE-wide, so this
|
|
1931
|
+
// is browser-only for the same reason `PUT /profiles` is (invariant 26): an
|
|
1932
|
+
// agent must not be able to move every other agent onto another model.
|
|
1933
|
+
app.post("/models/default", (req, res) => {
|
|
1934
|
+
if (!requestFromBrowser(req))
|
|
1935
|
+
return res.status(403).json({ error: "browser only" });
|
|
1936
|
+
const raw = req.body?.model;
|
|
1937
|
+
const model = typeof raw === "string" && raw.trim() ? raw.trim() : null;
|
|
1938
|
+
// Accept an alias or a model this build actually knows — never free text.
|
|
1939
|
+
// A typo here would not fail loudly: the agent spawns and Claude Code picks
|
|
1940
|
+
// its own default, so the cockpit would show a pin that does nothing.
|
|
1941
|
+
const known = new Set([...MODEL_FAMILIES, ...availableModels(claudeCommand())]);
|
|
1942
|
+
const bare = model?.replace(/\[1m\]$/i, "") ?? null;
|
|
1943
|
+
if (bare && !known.has(bare))
|
|
1944
|
+
return res.status(400).json({ error: `unknown model: ${bare}` });
|
|
1945
|
+
if (!setDefaultModel(model))
|
|
1946
|
+
return res.status(500).json({ error: "could not write settings.json" });
|
|
1947
|
+
console.log(`models: default is now ${model ?? "(none)"} — applies at each agent's next spawn`);
|
|
1948
|
+
res.json({ default: getDefaultModel() });
|
|
1949
|
+
});
|
|
1915
1950
|
/**
|
|
1916
1951
|
* Selects the transport. tmux is the DEFAULT whenever it is installed: the
|
|
1917
1952
|
* agent runs in a detached tmux session named after the Claude session id, so
|