scalemax 0.1.47 → 0.1.48
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.
|
@@ -714,7 +714,7 @@ export function selectHelperModel(models, requested) {
|
|
|
714
714
|
throw new Error(`Model "${requested}" is not available to this API key. Run \`scalemax models\` to see available models.`);
|
|
715
715
|
return exact.id;
|
|
716
716
|
}
|
|
717
|
-
const preferred = ['gpt-5.
|
|
717
|
+
const preferred = ['gpt-5.6-luna', 'claude-haiku-4-5-20251001', 'fable-5', 'gpt-5.6-terra', 'gpt-5.6-sol'];
|
|
718
718
|
for (const id of preferred) {
|
|
719
719
|
const match = models.find(model => model.id.toLowerCase() === id);
|
|
720
720
|
if (match)
|
|
@@ -6,7 +6,23 @@ export interface CodexVerificationResult {
|
|
|
6
6
|
configPath: string;
|
|
7
7
|
authPath: string;
|
|
8
8
|
}
|
|
9
|
-
export declare function mergeCodexConfig(existing: string, baseUrl: string, model?: string, authHash?: string): string;
|
|
9
|
+
export declare function mergeCodexConfig(existing: string, baseUrl: string, model?: string, authHash?: string, keyHash?: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Take ScaleMax OUT of a config.toml, keeping everything else the user has --
|
|
12
|
+
* including what Codex or the user added after setup (trusted projects,
|
|
13
|
+
* notices, other providers).
|
|
14
|
+
*
|
|
15
|
+
* Ownership is read from the CONTENT, not only from the generated header: a
|
|
16
|
+
* Codex rewrite that drops comments still leaves `model_provider = "scalemax"`
|
|
17
|
+
* and the `[model_providers.scalemax]` table, and those must go. The three
|
|
18
|
+
* top-level keys are removed only while ScaleMax is still the provider; if the
|
|
19
|
+
* user switched provider after setup they are the user's.
|
|
20
|
+
*/
|
|
21
|
+
export declare function stripScaleMaxCodexConfig(existing: string): {
|
|
22
|
+
text: string;
|
|
23
|
+
found: boolean;
|
|
24
|
+
strippedTopLevel: boolean;
|
|
25
|
+
};
|
|
10
26
|
export declare function verifyCodexConfiguration(ctx: ConfigureContext): Promise<CodexVerificationResult>;
|
|
11
27
|
export declare function install(ctx: InstallContext): Promise<ConfigureResult>;
|
|
12
28
|
export declare function verify(ctx: ConfigureContext): Promise<ConfigureResult>;
|
package/dist/configure/codex.js
CHANGED
|
@@ -10,17 +10,29 @@ import { AUTO_MODEL_ID, preferredOpenAiResponsesModel } from './models.js';
|
|
|
10
10
|
const id = 'codex';
|
|
11
11
|
const name = 'Codex';
|
|
12
12
|
const provider = 'scalemax';
|
|
13
|
-
const fallbackModel = 'gpt-5.
|
|
13
|
+
const fallbackModel = 'gpt-5.6-luna'; // gpt-5.5 retired 2026-09-30; same price
|
|
14
14
|
const reasoningEffort = 'medium';
|
|
15
15
|
const managedAuthHashPrefix = '# ScaleMax managed auth.json SHA-256: ';
|
|
16
|
+
// Fingerprint of the API key setup wrote into auth.json. Unlike the auth.json
|
|
17
|
+
// hash above it survives Codex rewriting auth.json (a token refresh, its own
|
|
18
|
+
// serialisation), so remove() can still tell OUR key from one the user put
|
|
19
|
+
// there later.
|
|
20
|
+
const managedKeyHashPrefix = '# ScaleMax managed API key SHA-256: ';
|
|
21
|
+
const modelsComment = '# ScaleMax models are loaded from the authenticated live model catalog.';
|
|
22
|
+
// The top-level keys setup owns while ScaleMax is the provider. remove() puts
|
|
23
|
+
// the user's pre-ScaleMax values for exactly these back from the backup.
|
|
24
|
+
const managedTopLevelKey = /^(model|model_provider|model_reasoning_effort)\s*=/;
|
|
16
25
|
function contentSha256(content) {
|
|
17
26
|
return crypto.createHash('sha256').update(content, 'utf8').digest('hex');
|
|
18
27
|
}
|
|
19
|
-
function
|
|
20
|
-
const marker = config.split(/\r?\n/).find(line => line.startsWith(
|
|
21
|
-
const hash = marker?.slice(
|
|
28
|
+
function markerHash(config, prefix) {
|
|
29
|
+
const marker = config.split(/\r?\n/).find(line => line.startsWith(prefix));
|
|
30
|
+
const hash = marker?.slice(prefix.length).trim();
|
|
22
31
|
return hash && /^[a-f0-9]{64}$/.test(hash) ? hash : undefined;
|
|
23
32
|
}
|
|
33
|
+
function managedAuthHash(config) {
|
|
34
|
+
return markerHash(config, managedAuthHashPrefix);
|
|
35
|
+
}
|
|
24
36
|
function selectedModel(ctx) {
|
|
25
37
|
if (!ctx.models) {
|
|
26
38
|
// An auto-api key's ONLY model is `auto`; gpt-5.5 (the generic fallback
|
|
@@ -30,11 +42,22 @@ function selectedModel(ctx) {
|
|
|
30
42
|
}
|
|
31
43
|
return preferredOpenAiResponsesModel(ctx);
|
|
32
44
|
}
|
|
33
|
-
function scalemaxCodexBlock(baseUrl, model = fallbackModel, authHash) {
|
|
45
|
+
function scalemaxCodexBlock(baseUrl, model = fallbackModel, authHash, keyHash) {
|
|
34
46
|
const authMarker = authHash ? `${managedAuthHashPrefix}${authHash}\n` : '';
|
|
35
|
-
|
|
47
|
+
const keyMarker = keyHash ? `${managedKeyHashPrefix}${keyHash}\n` : '';
|
|
48
|
+
return `# ${GENERATED_HEADER}\n${authMarker}${keyMarker}model = "${model}"\nmodel_provider = "${provider}"\nmodel_reasoning_effort = "${reasoningEffort}"\n${modelsComment}\n\n[model_providers.${provider}]\nname = "ScaleMax"\nbase_url = "${baseUrl}"\nwire_api = "responses"\nrequires_openai_auth = true\n`;
|
|
49
|
+
}
|
|
50
|
+
/** A line setup writes as a comment. Never user content, and never preserved. */
|
|
51
|
+
function isManagedCommentLine(line) {
|
|
52
|
+
return line === `# ${GENERATED_HEADER}`
|
|
53
|
+
|| line === modelsComment
|
|
54
|
+
|| line.startsWith(managedAuthHashPrefix)
|
|
55
|
+
|| line.startsWith(managedKeyHashPrefix);
|
|
36
56
|
}
|
|
37
|
-
|
|
57
|
+
function isScaleMaxProviderSection(section) {
|
|
58
|
+
return section === `model_providers.${provider}` || section.startsWith(`model_providers.${provider}.`);
|
|
59
|
+
}
|
|
60
|
+
export function mergeCodexConfig(existing, baseUrl, model = fallbackModel, authHash, keyHash) {
|
|
38
61
|
const preserved = [];
|
|
39
62
|
let section = '';
|
|
40
63
|
let skipScaleMaxProvider = false;
|
|
@@ -43,20 +66,94 @@ export function mergeCodexConfig(existing, baseUrl, model = fallbackModel, authH
|
|
|
43
66
|
const sectionMatch = line.match(/^\[([^\]]+)\]$/);
|
|
44
67
|
if (sectionMatch) {
|
|
45
68
|
section = sectionMatch[1];
|
|
46
|
-
skipScaleMaxProvider = section
|
|
69
|
+
skipScaleMaxProvider = isScaleMaxProviderSection(section);
|
|
47
70
|
if (skipScaleMaxProvider)
|
|
48
71
|
continue;
|
|
49
72
|
}
|
|
50
73
|
if (skipScaleMaxProvider)
|
|
51
74
|
continue;
|
|
52
|
-
if (!section &&
|
|
75
|
+
if (!section && managedTopLevelKey.test(line))
|
|
53
76
|
continue;
|
|
54
|
-
|
|
77
|
+
// Includes the models comment: it used to be kept here, so every re-run of
|
|
78
|
+
// setup stacked one more copy of it into the preserved tail.
|
|
79
|
+
if (isManagedCommentLine(line))
|
|
55
80
|
continue;
|
|
56
81
|
preserved.push(raw);
|
|
57
82
|
}
|
|
58
83
|
const tail = preserved.join('\n').trim();
|
|
59
|
-
return `${scalemaxCodexBlock(baseUrl, model, authHash)}${tail ? `\n${tail}\n` : ''}`;
|
|
84
|
+
return `${scalemaxCodexBlock(baseUrl, model, authHash, keyHash)}${tail ? `\n${tail}\n` : ''}`;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Take ScaleMax OUT of a config.toml, keeping everything else the user has --
|
|
88
|
+
* including what Codex or the user added after setup (trusted projects,
|
|
89
|
+
* notices, other providers).
|
|
90
|
+
*
|
|
91
|
+
* Ownership is read from the CONTENT, not only from the generated header: a
|
|
92
|
+
* Codex rewrite that drops comments still leaves `model_provider = "scalemax"`
|
|
93
|
+
* and the `[model_providers.scalemax]` table, and those must go. The three
|
|
94
|
+
* top-level keys are removed only while ScaleMax is still the provider; if the
|
|
95
|
+
* user switched provider after setup they are the user's.
|
|
96
|
+
*/
|
|
97
|
+
export function stripScaleMaxCodexConfig(existing) {
|
|
98
|
+
const lines = existing.split(/\r?\n/);
|
|
99
|
+
let section = '';
|
|
100
|
+
let topProvider;
|
|
101
|
+
let found = false;
|
|
102
|
+
for (const raw of lines) {
|
|
103
|
+
const line = raw.trim();
|
|
104
|
+
const sectionMatch = line.match(/^\[([^\]]+)\]$/);
|
|
105
|
+
if (sectionMatch) {
|
|
106
|
+
section = sectionMatch[1];
|
|
107
|
+
if (isScaleMaxProviderSection(section))
|
|
108
|
+
found = true;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (isManagedCommentLine(line))
|
|
112
|
+
found = true;
|
|
113
|
+
if (!section) {
|
|
114
|
+
const providerMatch = line.match(/^model_provider\s*=\s*"([^"]*)"/);
|
|
115
|
+
if (providerMatch)
|
|
116
|
+
topProvider = providerMatch[1];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const strippedTopLevel = topProvider === provider;
|
|
120
|
+
if (strippedTopLevel)
|
|
121
|
+
found = true;
|
|
122
|
+
if (!found)
|
|
123
|
+
return { text: existing, found: false, strippedTopLevel: false };
|
|
124
|
+
const kept = [];
|
|
125
|
+
section = '';
|
|
126
|
+
let skip = false;
|
|
127
|
+
for (const raw of lines) {
|
|
128
|
+
const line = raw.trim();
|
|
129
|
+
const sectionMatch = line.match(/^\[([^\]]+)\]$/);
|
|
130
|
+
if (sectionMatch) {
|
|
131
|
+
section = sectionMatch[1];
|
|
132
|
+
skip = isScaleMaxProviderSection(section);
|
|
133
|
+
if (skip)
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (skip)
|
|
137
|
+
continue;
|
|
138
|
+
if (!section && strippedTopLevel && managedTopLevelKey.test(line))
|
|
139
|
+
continue;
|
|
140
|
+
if (isManagedCommentLine(line))
|
|
141
|
+
continue;
|
|
142
|
+
kept.push(raw);
|
|
143
|
+
}
|
|
144
|
+
return { text: kept.join('\n').trim(), found: true, strippedTopLevel };
|
|
145
|
+
}
|
|
146
|
+
/** The pre-ScaleMax top-level model / model_provider / model_reasoning_effort lines of a backup. */
|
|
147
|
+
function originalTopLevelLines(backup) {
|
|
148
|
+
const out = [];
|
|
149
|
+
for (const raw of backup.split(/\r?\n/)) {
|
|
150
|
+
const line = raw.trim();
|
|
151
|
+
if (/^\[[^\]]+\]$/.test(line))
|
|
152
|
+
break;
|
|
153
|
+
if (managedTopLevelKey.test(line))
|
|
154
|
+
out.push(line);
|
|
155
|
+
}
|
|
156
|
+
return out;
|
|
60
157
|
}
|
|
61
158
|
function codexAuth(apiKey, existing = {}) {
|
|
62
159
|
return JSON.stringify({ ...existing, auth_mode: 'apikey', OPENAI_API_KEY: apiKey }, null, 2) + '\n';
|
|
@@ -111,22 +208,99 @@ async function latestRestorableBackup(file, home) {
|
|
|
111
208
|
}
|
|
112
209
|
return undefined;
|
|
113
210
|
}
|
|
114
|
-
async function
|
|
211
|
+
async function removeCodexConfig(file, ctx) {
|
|
115
212
|
if (!(await fs.pathExists(file)))
|
|
116
213
|
return { changed: false, message: 'no ScaleMax configuration found' };
|
|
117
214
|
const current = await fs.readFile(file, 'utf8').catch(() => '');
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
215
|
+
const stripped = stripScaleMaxCodexConfig(current);
|
|
216
|
+
if (!stripped.found)
|
|
217
|
+
return { changed: false, message: 'left unmanaged configuration unchanged' };
|
|
218
|
+
// Put back the user's own model settings from before setup (the first
|
|
219
|
+
// pre-ScaleMax backup) -- only when ScaleMax's were actually taken out.
|
|
220
|
+
let restoredTop = [];
|
|
221
|
+
if (stripped.strippedTopLevel) {
|
|
222
|
+
const backup = await latestRestorableBackup(file, ctx.home);
|
|
223
|
+
if (backup)
|
|
224
|
+
restoredTop = originalTopLevelLines(await fs.readFile(backup, 'utf8').catch(() => ''));
|
|
225
|
+
}
|
|
226
|
+
const next = [restoredTop.join('\n'), stripped.text].filter(Boolean).join('\n\n');
|
|
227
|
+
if (!next.trim()) {
|
|
228
|
+
await fs.remove(file);
|
|
229
|
+
return { changed: true, message: 'removed ScaleMax configuration' };
|
|
230
|
+
}
|
|
231
|
+
await atomicWrite(file, `${next}\n`, 0o600, ctx.platform);
|
|
232
|
+
return {
|
|
233
|
+
changed: true,
|
|
234
|
+
message: restoredTop.length
|
|
235
|
+
? 'removed ScaleMax settings and restored your previous model settings'
|
|
236
|
+
: 'removed ScaleMax settings; your other settings were kept',
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Take the ScaleMax credential OUT of auth.json.
|
|
241
|
+
*
|
|
242
|
+
* Owned when ANY of: the file is byte-identical to what setup wrote (auth
|
|
243
|
+
* hash marker); its key is the key the CLI has saved; its key matches the
|
|
244
|
+
* key fingerprint setup wrote into config.toml. The last two survive Codex
|
|
245
|
+
* rewriting the file -- a ChatGPT token refresh or its own serialisation --
|
|
246
|
+
* which the exact hash alone did not, and which left the ScaleMax key in
|
|
247
|
+
* place after "remove". A DIFFERENT key the user put there after setup is
|
|
248
|
+
* none of these, and is left alone.
|
|
249
|
+
*
|
|
250
|
+
* Only the two fields setup sets are touched: auth_mode and OPENAI_API_KEY go
|
|
251
|
+
* back to their pre-setup values from the backup, or away if there were none.
|
|
252
|
+
* Tokens and refresh times stay as Codex last wrote them (they are fresher
|
|
253
|
+
* than the backup). A file left with nothing in it is deleted.
|
|
254
|
+
*/
|
|
255
|
+
async function removeCodexAuth(file, ctx, ownership) {
|
|
256
|
+
if (!(await fs.pathExists(file)))
|
|
257
|
+
return { changed: false, message: 'no ScaleMax configuration found' };
|
|
258
|
+
const text = await fs.readFile(file, 'utf8').catch(() => '');
|
|
259
|
+
let current;
|
|
260
|
+
try {
|
|
261
|
+
const parsed = JSON.parse(text);
|
|
262
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
263
|
+
throw new Error('not an object');
|
|
264
|
+
current = parsed;
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
122
267
|
return { changed: false, message: 'left unmanaged configuration unchanged' };
|
|
123
|
-
const restore = await latestRestorableBackup(file, ctx.home);
|
|
124
|
-
if (restore) {
|
|
125
|
-
await atomicWrite(file, await fs.readFile(restore, 'utf8'), 0o600, ctx.platform);
|
|
126
|
-
return { changed: true, message: `restored backup ${restore}` };
|
|
127
268
|
}
|
|
128
|
-
|
|
129
|
-
|
|
269
|
+
const key = typeof current.OPENAI_API_KEY === 'string' ? current.OPENAI_API_KEY : undefined;
|
|
270
|
+
const owned = (ownership.authHash !== undefined && contentSha256(text) === ownership.authHash)
|
|
271
|
+
|| (key !== undefined && typeof ctx.apiKey === 'string' && ctx.apiKey.length > 0 && key === ctx.apiKey)
|
|
272
|
+
|| (key !== undefined && ownership.keyHash !== undefined && contentSha256(key) === ownership.keyHash)
|
|
273
|
+
|| (ownership.authHash === undefined && ownership.keyHash === undefined && ownership.legacyStructural && isManagedCodexAuth(text));
|
|
274
|
+
if (!owned)
|
|
275
|
+
return { changed: false, message: 'left unmanaged configuration unchanged' };
|
|
276
|
+
let original;
|
|
277
|
+
const backup = await latestRestorableBackup(file, ctx.home);
|
|
278
|
+
if (backup) {
|
|
279
|
+
try {
|
|
280
|
+
const parsed = JSON.parse(await fs.readFile(backup, 'utf8'));
|
|
281
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
|
|
282
|
+
original = parsed;
|
|
283
|
+
}
|
|
284
|
+
catch { /* an unreadable backup restores nothing */ }
|
|
285
|
+
}
|
|
286
|
+
const next = { ...current };
|
|
287
|
+
for (const field of ['auth_mode', 'OPENAI_API_KEY']) {
|
|
288
|
+
if (original && Object.prototype.hasOwnProperty.call(original, field))
|
|
289
|
+
next[field] = original[field];
|
|
290
|
+
else
|
|
291
|
+
delete next[field];
|
|
292
|
+
}
|
|
293
|
+
const meaningful = Object.values(next).some(value => value !== null && value !== undefined
|
|
294
|
+
&& !(typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0));
|
|
295
|
+
if (!original && !meaningful) {
|
|
296
|
+
await fs.remove(file);
|
|
297
|
+
return { changed: true, message: 'removed ScaleMax configuration' };
|
|
298
|
+
}
|
|
299
|
+
await atomicWrite(file, JSON.stringify(next, null, 2) + '\n', 0o600, ctx.platform);
|
|
300
|
+
return {
|
|
301
|
+
changed: true,
|
|
302
|
+
message: original ? 'removed the ScaleMax key and restored your previous sign-in' : 'removed the ScaleMax key',
|
|
303
|
+
};
|
|
130
304
|
}
|
|
131
305
|
export async function verifyCodexConfiguration(ctx) {
|
|
132
306
|
const configPath = codexConfigPath(ctx.home);
|
|
@@ -261,7 +435,7 @@ export async function configure(ctx) {
|
|
|
261
435
|
}
|
|
262
436
|
catch { }
|
|
263
437
|
const nextAuth = codexAuth(ctx.apiKey, existingAuth);
|
|
264
|
-
const nextConfig = mergeCodexConfig(originalConfig, ctx.baseUrl, selectedModel(ctx), contentSha256(nextAuth));
|
|
438
|
+
const nextConfig = mergeCodexConfig(originalConfig, ctx.baseUrl, selectedModel(ctx), contentSha256(nextAuth), contentSha256(ctx.apiKey));
|
|
265
439
|
const changed = originalConfig !== nextConfig || originalAuth !== nextAuth;
|
|
266
440
|
const alreadyManaged = originalConfig.includes(GENERATED_HEADER);
|
|
267
441
|
let verification;
|
|
@@ -312,24 +486,18 @@ export async function configure(ctx) {
|
|
|
312
486
|
export async function remove(ctx) {
|
|
313
487
|
const configPath = codexConfigPath(ctx.home);
|
|
314
488
|
const authPath = codexAuthPath(ctx.home);
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
// configs without the marker retain the conservative structural fallback.
|
|
324
|
-
const authManagedOverride = expectedAuthHash
|
|
325
|
-
? contentSha256(authText) === expectedAuthHash
|
|
326
|
-
: configText.includes(GENERATED_HEADER)
|
|
327
|
-
? undefined
|
|
328
|
-
: false;
|
|
489
|
+
// Read the ownership markers BEFORE config.toml is rewritten below.
|
|
490
|
+
const configText = await fs.readFile(configPath, 'utf8').catch(() => '');
|
|
491
|
+
const ownership = {
|
|
492
|
+
authHash: managedAuthHash(configText),
|
|
493
|
+
keyHash: markerHash(configText, managedKeyHashPrefix),
|
|
494
|
+
// Configs written before either marker existed: the original structural rule.
|
|
495
|
+
legacyStructural: configText.includes(GENERATED_HEADER),
|
|
496
|
+
};
|
|
329
497
|
const snapshots = await captureFileSnapshots([configPath, authPath]);
|
|
330
498
|
try {
|
|
331
|
-
const config = await
|
|
332
|
-
const auth = await
|
|
499
|
+
const config = await removeCodexConfig(configPath, ctx);
|
|
500
|
+
const auth = await removeCodexAuth(authPath, ctx, ownership);
|
|
333
501
|
return {
|
|
334
502
|
id,
|
|
335
503
|
name,
|
package/dist/configure/models.js
CHANGED
|
@@ -15,9 +15,8 @@ const preferredOpenAi = [
|
|
|
15
15
|
'gpt-5.6-sol',
|
|
16
16
|
'gpt-6-sol',
|
|
17
17
|
'gpt-6-luna',
|
|
18
|
-
|
|
19
|
-
'gpt-5.
|
|
20
|
-
'gpt-5.4-mini',
|
|
18
|
+
// gpt-5.5 / gpt-5.4 / gpt-5.4-mini retired 2026-09-30.
|
|
19
|
+
'gpt-5.6-luna',
|
|
21
20
|
'gpt-5.3-codex',
|
|
22
21
|
'codex-auto-review',
|
|
23
22
|
// Gemini / Grok / Qwen ride the same OpenAI-compatible route, so they are
|
|
@@ -126,7 +125,8 @@ export function fastModel(ctx) {
|
|
|
126
125
|
}
|
|
127
126
|
export function fastOpenAiModel(ctx) {
|
|
128
127
|
const ids = openAiModelIds(ctx);
|
|
129
|
-
return ids.find(id => id === 'gpt-5.
|
|
128
|
+
return ids.find(id => id === 'gpt-5.6-luna')
|
|
129
|
+
|| ids.find(id => id === 'gpt-6-luna')
|
|
130
130
|
|| ids.find(id => /(^|[-.])(mini|nano)([-.]|$)/i.test(id));
|
|
131
131
|
}
|
|
132
132
|
/** The lightweight model for an OpenAI-compatible chat client, if one exists. */
|
|
@@ -11,7 +11,7 @@ export function jsonConfig(client, apiKey, baseUrl, extra = {}) {
|
|
|
11
11
|
}, null, 2) + '\n';
|
|
12
12
|
}
|
|
13
13
|
export function continueConfig(apiKey, baseUrl) {
|
|
14
|
-
const models = ['gpt-6-astra', 'gpt-6-sol', 'gpt-6-luna', 'gpt-5.6-sol', 'gpt-5.6-terra', 'gpt-5.
|
|
14
|
+
const models = ['gpt-6-astra', 'gpt-6-sol', 'gpt-6-luna', 'gpt-5.6-sol', 'gpt-5.6-terra', 'gpt-5.6-luna', 'gpt-5.3-codex', 'codex-auto-review', 'deepseek-v4-flash'];
|
|
15
15
|
return JSON.stringify({
|
|
16
16
|
models: models.map(model => ({
|
|
17
17
|
title: model === 'deepseek-v4-flash' ? 'ScaleMax DeepSeek V4 Flash' : `ScaleMax ${model}`,
|
|
@@ -21,7 +21,7 @@ export function continueConfig(apiKey, baseUrl) {
|
|
|
21
21
|
apiKey,
|
|
22
22
|
...(model === 'deepseek-v4-flash' ? { useResponsesApi: false } : {})
|
|
23
23
|
})),
|
|
24
|
-
tabAutocompleteModel: { title: 'ScaleMax gpt-5.
|
|
24
|
+
tabAutocompleteModel: { title: 'ScaleMax gpt-5.6-luna', provider: 'openai', model: 'gpt-5.6-luna', apiBase: baseUrl, apiKey },
|
|
25
25
|
contextProviders: [{ name: 'code' }, { name: 'docs' }, { name: 'diff' }, { name: 'terminal' }]
|
|
26
26
|
}, null, 2) + '\n';
|
|
27
27
|
}
|