@bitkyc08/opencodex 2.74.0 → 2.75.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/gui/dist/assets/{App-KLz1TAXi.css → App-ia4U0ufY.css} +1 -1
- package/gui/dist/assets/{App-B00Yq11u.js → App-xm0zGNOe.js} +3 -3
- package/gui/dist/assets/{Tray-D3Ij1Cvf.js → Tray-CGAD2AGk.js} +1 -1
- package/gui/dist/assets/{index-BYQQfBGK.js → index-D_RGAT6Q.js} +2 -2
- package/gui/dist/assets/{quota-summary-yPtcEhGI.js → quota-summary-CUFq48t-.js} +1 -1
- package/gui/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/adapters/devin/cloud-direct/chat.ts +38 -6
- package/src/adapters/devin/live-models.ts +28 -8
- package/src/adapters/devin.ts +1 -1
- package/src/claude/devin-output-order.ts +98 -0
- package/src/codex/auth-api/pool-quota-probe.ts +2 -1
- package/src/codex/catalog/retained-sync.ts +9 -0
- package/src/codex/inject/config-toml.ts +179 -248
- package/src/codex/inject/provider-table.ts +5 -2
- package/src/codex/inject/remove.ts +23 -8
- package/src/codex/injected-marker.ts +28 -23
- package/src/codex/quota-auto-refresh.ts +1 -1
- package/src/codex/quota.ts +12 -4
- package/src/codex/toml-source-lines.ts +93 -1
- package/src/config/live-reconcile.ts +18 -0
- package/src/config/rebase-provenance.ts +12 -0
- package/src/config.ts +4 -3
- package/src/generated/compatibility-version.json +50 -38
- package/src/images/loop.ts +2 -0
- package/src/integrations/cursor-local-installer.ts +29 -2
- package/src/lib/redact.ts +54 -8
- package/src/oauth/anthropic-account-refusal.ts +76 -0
- package/src/oauth/anthropic-routing.ts +38 -10
- package/src/providers/new-model-policy-runtime.ts +83 -0
- package/src/providers/new-model-policy.ts +25 -0
- package/src/providers/registry/entries-core.ts +3 -0
- package/src/providers/registry/model-seeds.ts +31 -2
- package/src/providers/xai-transport.ts +2 -2
- package/src/server/claude-messages.ts +33 -10
- package/src/server/management/agent-settings-routes.ts +3 -1
- package/src/server/management/model-rows.ts +34 -4
- package/src/server/management/shared.ts +18 -2
- package/src/server/responses/adapter-continuation.ts +13 -21
- package/src/server/responses/adapter-delivery.ts +2 -2
- package/src/server/responses/adapter-dispatch.ts +10 -21
- package/src/server/responses/compact.ts +3 -2
- package/src/server/responses/core-codex-account.ts +4 -2
- package/src/server/responses/passthrough-delivery.ts +2 -1
- package/src/server/responses/request-transport.ts +2 -0
- package/src/server/responses/run-turn-execution.ts +25 -5
- package/src/server/responses/sidecar-execution.ts +9 -14
- package/src/web-search/loop.ts +7 -2
|
@@ -3,23 +3,22 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
3
3
|
import { contextCompatibleBaseLine } from "../context-compat";
|
|
4
4
|
import { resolveEffectiveProjectModelProvider } from "../project-config-warnings";
|
|
5
5
|
import {
|
|
6
|
-
OCX_SECTION_MARKER,
|
|
7
6
|
OCX_ROUTING_MARKER_LINE,
|
|
8
7
|
REALTIME_WS_BASE_URL_KEY,
|
|
8
|
+
isOcxRoutingMarkerLine,
|
|
9
9
|
isRootOpenaiBaseUrlLine,
|
|
10
10
|
isRootRealtimeWsBaseUrlLine,
|
|
11
11
|
providerTableStart,
|
|
12
12
|
providerTableString,
|
|
13
13
|
rootTomlString,
|
|
14
|
-
tomlStringPattern,
|
|
15
14
|
} from "../injected-marker";
|
|
16
15
|
import {
|
|
17
16
|
CODEX_CONFIG_PATH,
|
|
18
17
|
DEFAULT_CATALOG_PATH,
|
|
19
|
-
parseTomlString,
|
|
20
18
|
resolveCodexConfigPath,
|
|
21
19
|
tomlString,
|
|
22
20
|
} from "../paths";
|
|
21
|
+
import { normalizeStructuralWhitespace, rootAssignmentKey, rootSourceLines, sourceAssignment, sourceAssignmentSpan, sourceText, type SourceLine } from "../toml-source-lines";
|
|
23
22
|
import { readBoundedCodexConfig } from "./bounded-config-reader";
|
|
24
23
|
import {
|
|
25
24
|
type CodexRoutingTarget,
|
|
@@ -67,10 +66,9 @@ export function observedExternalCodexModelProvider(): string | null {
|
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
/**
|
|
70
|
-
* Detect the file's dominant line ending.
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* single inject would leave a mixed-EOL file.
|
|
69
|
+
* Detect the file's dominant line ending. Structural edits retain physical line endings;
|
|
70
|
+
* the full injection/removal pipeline still normalizes to LF at its boundary and converts
|
|
71
|
+
* back on write so generated blocks do not leave a mixed-EOL file.
|
|
74
72
|
*/
|
|
75
73
|
export function dominantEol(content: string): "\r\n" | "\n" {
|
|
76
74
|
const crlf = (content.match(/\r\n/g) ?? []).length;
|
|
@@ -234,66 +232,43 @@ export function setRootOpenaiBaseUrl(
|
|
|
234
232
|
if (typeof portOrTarget !== "number") {
|
|
235
233
|
return setRootOpenaiBaseUrlForTarget(content, validateCodexRoutingTarget(portOrTarget));
|
|
236
234
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
const rootEnd = firstTable === -1 ? lines.length : firstTable;
|
|
240
|
-
const key = contextCompatibleBaseLine(content, buildOpenaiBaseUrlLine(portOrTarget, hostname));
|
|
235
|
+
return setRootUrl(content, contextCompatibleBaseLine(content, buildOpenaiBaseUrlLine(portOrTarget, hostname)));
|
|
236
|
+
}
|
|
241
237
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
// Refresh the marker too, so a config injected by a build that predates the recovery
|
|
247
|
-
// hint gains it on the next `ocx start` instead of keeping a bare marker forever.
|
|
248
|
-
lines[i - 1] = OCX_ROUTING_MARKER_LINE;
|
|
249
|
-
lines[i] = key;
|
|
250
|
-
return { content: lines.join("\n"), keptUserBaseUrl: false };
|
|
251
|
-
}
|
|
238
|
+
function ownedRootUrl(lines: SourceLine[], index: number, key: string): boolean {
|
|
239
|
+
return lines[index]!.structural && rootTomlString(lines[index]!.text, key) !== null
|
|
240
|
+
&& index > 0 && lines[index - 1]!.structural && isOcxRoutingMarkerLine(lines[index - 1]!.text);
|
|
241
|
+
}
|
|
252
242
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
243
|
+
function insertRootSourceLines(content: string, added: string[]): string {
|
|
244
|
+
const { bom, lines, rootEnd } = rootSourceLines(content);
|
|
245
|
+
const eol = dominantEol(content);
|
|
246
|
+
if (rootEnd === lines.length) {
|
|
247
|
+
return content.replace(/(?:\r?\n)+$/, "") + eol + added.join(eol) + eol;
|
|
248
|
+
}
|
|
249
|
+
let at = rootEnd;
|
|
250
|
+
while (at > 0 && lines[at - 1]!.structural && lines[at - 1]!.text.trim() === "") at -= 1;
|
|
251
|
+
lines.splice(at, 0, ...added.map(text => ({ text, eol, structural: true })));
|
|
252
|
+
return bom + sourceText(lines);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function setRootUrl(content: string, key: string): { content: string; keptUserBaseUrl: boolean } {
|
|
256
|
+
const { bom, lines, rootEnd } = rootSourceLines(content);
|
|
257
|
+
for (let index = 0; index < rootEnd; index++) {
|
|
258
|
+
if (!lines[index]!.structural || !isRootOpenaiBaseUrlLine(lines[index]!.text)) continue;
|
|
259
|
+
if (!ownedRootUrl(lines, index, "openai_base_url")) return { content, keptUserBaseUrl: true };
|
|
260
|
+
lines[index - 1]!.text = OCX_ROUTING_MARKER_LINE;
|
|
261
|
+
lines[index]!.text = key;
|
|
262
|
+
return { content: bom + sourceText(lines), keptUserBaseUrl: false };
|
|
264
263
|
}
|
|
265
|
-
|
|
266
|
-
while (insertAt > 0 && lines[insertAt - 1].trim() === "") insertAt--;
|
|
267
|
-
lines.splice(insertAt, 0, OCX_ROUTING_MARKER_LINE, key);
|
|
268
|
-
return { content: lines.join("\n"), keptUserBaseUrl: false };
|
|
264
|
+
return { content: insertRootSourceLines(content, [OCX_ROUTING_MARKER_LINE, key]), keptUserBaseUrl: false };
|
|
269
265
|
}
|
|
270
266
|
|
|
271
267
|
export function setRootOpenaiBaseUrlForTarget(
|
|
272
268
|
content: string,
|
|
273
269
|
target: CodexRoutingTarget,
|
|
274
270
|
): { content: string; keptUserBaseUrl: boolean } {
|
|
275
|
-
|
|
276
|
-
const firstTable = lines.findIndex((line) => /^\s*\[/.test(line));
|
|
277
|
-
const rootEnd = firstTable === -1 ? lines.length : firstTable;
|
|
278
|
-
const key = contextCompatibleBaseLine(content, buildOpenaiBaseUrlLineForTarget(target));
|
|
279
|
-
for (let index = 0; index < rootEnd; index += 1) {
|
|
280
|
-
if (!isRootOpenaiBaseUrlLine(lines[index])) continue;
|
|
281
|
-
const markerOwned = index > 0 && lines[index - 1].includes(OCX_SECTION_MARKER);
|
|
282
|
-
if (!markerOwned) return { content, keptUserBaseUrl: true };
|
|
283
|
-
lines[index - 1] = OCX_ROUTING_MARKER_LINE;
|
|
284
|
-
lines[index] = key;
|
|
285
|
-
return { content: lines.join("\n"), keptUserBaseUrl: false };
|
|
286
|
-
}
|
|
287
|
-
if (firstTable === -1) {
|
|
288
|
-
return {
|
|
289
|
-
content: `${content.replace(/\n+$/, "")}\n${OCX_ROUTING_MARKER_LINE}\n${key}\n`,
|
|
290
|
-
keptUserBaseUrl: false,
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
let insertAt = firstTable;
|
|
294
|
-
while (insertAt > 0 && lines[insertAt - 1].trim() === "") insertAt -= 1;
|
|
295
|
-
lines.splice(insertAt, 0, OCX_ROUTING_MARKER_LINE, key);
|
|
296
|
-
return { content: lines.join("\n"), keptUserBaseUrl: false };
|
|
271
|
+
return setRootUrl(content, contextCompatibleBaseLine(content, buildOpenaiBaseUrlLineForTarget(target)));
|
|
297
272
|
}
|
|
298
273
|
|
|
299
274
|
/**
|
|
@@ -310,25 +285,22 @@ export function setRootRealtimeWsBaseUrl(
|
|
|
310
285
|
content: string,
|
|
311
286
|
target: CodexRoutingTarget,
|
|
312
287
|
): { content: string; keptUserRealtimeWsBaseUrl: boolean } {
|
|
313
|
-
const lines = content
|
|
314
|
-
const firstTable = lines.findIndex((line) => /^\s*\[/.test(line));
|
|
315
|
-
const rootEnd = firstTable === -1 ? lines.length : firstTable;
|
|
288
|
+
const { bom, lines, rootEnd } = rootSourceLines(content);
|
|
316
289
|
const key = buildRealtimeWsBaseUrlLine(validateCodexRoutingTarget(target));
|
|
317
|
-
for (let index = 0; index < rootEnd; index
|
|
318
|
-
if (!isRootRealtimeWsBaseUrlLine(lines[index])) continue;
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
lines[index
|
|
322
|
-
lines
|
|
323
|
-
return { content: lines.join("\n"), keptUserRealtimeWsBaseUrl: false };
|
|
290
|
+
for (let index = 0; index < rootEnd; index++) {
|
|
291
|
+
if (!lines[index]!.structural || !isRootRealtimeWsBaseUrlLine(lines[index]!.text)) continue;
|
|
292
|
+
if (!ownedRootUrl(lines, index, REALTIME_WS_BASE_URL_KEY)) return { content, keptUserRealtimeWsBaseUrl: true };
|
|
293
|
+
lines[index - 1]!.text = OCX_ROUTING_MARKER_LINE;
|
|
294
|
+
lines[index]!.text = key;
|
|
295
|
+
return { content: bom + sourceText(lines), keptUserRealtimeWsBaseUrl: false };
|
|
324
296
|
}
|
|
325
|
-
for (let index = 0; index < rootEnd; index
|
|
326
|
-
if (!isRootOpenaiBaseUrlLine(lines[index])) continue;
|
|
327
|
-
|
|
328
|
-
lines
|
|
329
|
-
|
|
297
|
+
for (let index = 0; index < rootEnd; index++) {
|
|
298
|
+
if (!isRootOpenaiBaseUrlLine(lines[index]!.text) || !ownedRootUrl(lines, index, "openai_base_url")) continue;
|
|
299
|
+
const eol = dominantEol(content);
|
|
300
|
+
lines[index]!.eol ||= eol;
|
|
301
|
+
lines.splice(index + 1, 0, ...[OCX_ROUTING_MARKER_LINE, key].map(text => ({ text, eol, structural: true })));
|
|
302
|
+
return { content: bom + sourceText(lines), keptUserRealtimeWsBaseUrl: false };
|
|
330
303
|
}
|
|
331
|
-
// No marker-owned routing override to attach to: the override has no owner, so inject nothing.
|
|
332
304
|
return { content, keptUserRealtimeWsBaseUrl: false };
|
|
333
305
|
}
|
|
334
306
|
|
|
@@ -345,40 +317,30 @@ export const ROOT_WEB_SEARCH_DISABLED_LINE = 'web_search = "disabled"';
|
|
|
345
317
|
export const ROOT_WEB_SEARCH_DISABLED_VALUE = "disabled";
|
|
346
318
|
|
|
347
319
|
export function isRootWebSearchLine(line: string): boolean {
|
|
348
|
-
//
|
|
349
|
-
//
|
|
320
|
+
// Quoted spellings (including escaped basic keys) name the same key to TOML.
|
|
321
|
+
// A predicate that matched only the bare
|
|
350
322
|
// spelling would leave `"web_search" = "live"` in place while inserting our own line, and two
|
|
351
323
|
// root keys of the same name stop Codex from loading the file at all.
|
|
352
|
-
return
|
|
324
|
+
return rootAssignmentKey(line) === ROOT_WEB_SEARCH_KEY;
|
|
353
325
|
}
|
|
354
326
|
|
|
355
327
|
/** What an earlier injection recorded about this key, read back from the journal. */
|
|
356
328
|
export interface RootWebSearchJournal {
|
|
357
329
|
/** The value that injection wrote, or null when it wrote none. */
|
|
358
330
|
injectedValue?: string | null;
|
|
359
|
-
/** The user-owned
|
|
331
|
+
/** The complete user-owned assignment removed (possibly multiline), or null. */
|
|
360
332
|
replacedUserLine?: string | null;
|
|
361
333
|
}
|
|
362
334
|
|
|
363
335
|
/** What one pass of {@link ensureRootWebSearchDisabled} did, for the journal to record. */
|
|
364
336
|
export interface RootWebSearchOutcome {
|
|
365
337
|
content: string;
|
|
366
|
-
/** The user-owned root
|
|
338
|
+
/** The complete user-owned root assignment removed (possibly multiline), or null. */
|
|
367
339
|
replacedUserLine: string | null;
|
|
368
340
|
/** The value this pass wrote, or null when it wrote none. */
|
|
369
341
|
wroteValue: string | null;
|
|
370
342
|
}
|
|
371
343
|
|
|
372
|
-
/** Insert root-level lines ahead of the first table; TOML root keys may not follow one. */
|
|
373
|
-
function insertRootLines(lines: string[], text: string): string {
|
|
374
|
-
const firstTable = lines.findIndex((line) => /^\s*\[/.test(line));
|
|
375
|
-
if (firstTable === -1) return `${lines.join("\n").replace(/\n+$/, "")}\n${text}\n`;
|
|
376
|
-
let at = firstTable;
|
|
377
|
-
while (at > 0 && lines[at - 1].trim() === "") at -= 1;
|
|
378
|
-
lines.splice(at, 0, ...text.split("\n"));
|
|
379
|
-
return lines.join("\n");
|
|
380
|
-
}
|
|
381
|
-
|
|
382
344
|
/**
|
|
383
345
|
* Ensure the root `web_search` key follows the web-search sidecar's master switch.
|
|
384
346
|
*
|
|
@@ -406,14 +368,13 @@ export function ensureRootWebSearchDisabled(
|
|
|
406
368
|
disabled: boolean,
|
|
407
369
|
journal: RootWebSearchJournal = {},
|
|
408
370
|
): RootWebSearchOutcome {
|
|
409
|
-
const
|
|
410
|
-
const
|
|
411
|
-
const rootEnd = firstTable === -1 ? lines.length : firstTable;
|
|
371
|
+
const stripped = stripInjectedRootWebSearch(content, journal.injectedValue);
|
|
372
|
+
const { bom, lines, rootEnd } = rootSourceLines(stripped);
|
|
412
373
|
if (!disabled) {
|
|
413
|
-
const restore = journal.replacedUserLine
|
|
414
|
-
const owned = lines.slice(0, rootEnd).some(isRootWebSearchLine);
|
|
374
|
+
const restore = journal.replacedUserLine;
|
|
375
|
+
const owned = lines.slice(0, rootEnd).some(line => line.structural && isRootWebSearchLine(line.text));
|
|
415
376
|
return {
|
|
416
|
-
content: restore && !owned ?
|
|
377
|
+
content: restore?.trim() && !owned ? insertRootSourceLines(stripped, [applyEol(restore, dominantEol(stripped))]) : stripped,
|
|
417
378
|
replacedUserLine: null,
|
|
418
379
|
wroteValue: null,
|
|
419
380
|
};
|
|
@@ -424,14 +385,21 @@ export function ensureRootWebSearchDisabled(
|
|
|
424
385
|
// line keeps the one an earlier off pass recorded — the ordinary way to reach that state is a
|
|
425
386
|
// second injection while the switch is still off (a model change), and the operator's mode must
|
|
426
387
|
// not evaporate because the line it came from is already gone.
|
|
427
|
-
const
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
388
|
+
const drop = new Set<number>();
|
|
389
|
+
let replacedUserLine = journal.replacedUserLine ?? null;
|
|
390
|
+
let foundUserAssignment = false;
|
|
391
|
+
for (let index = 0; index < rootEnd; index++) {
|
|
392
|
+
if (!lines[index]!.structural || !isRootWebSearchLine(lines[index]!.text)) continue;
|
|
393
|
+
const assignment = sourceAssignmentSpan(lines, index, rootEnd);
|
|
394
|
+
if (!assignment) throw new Error("Cannot safely rewrite an incomplete root web_search assignment.");
|
|
395
|
+
if (!foundUserAssignment) replacedUserLine = assignment.text;
|
|
396
|
+
foundUserAssignment = true;
|
|
397
|
+
for (let at = index; at < assignment.end; at++) drop.add(at);
|
|
398
|
+
}
|
|
431
399
|
return {
|
|
432
|
-
content:
|
|
433
|
-
lines.filter((
|
|
434
|
-
|
|
400
|
+
content: insertRootSourceLines(
|
|
401
|
+
bom + sourceText(lines.filter((_, index) => !drop.has(index))),
|
|
402
|
+
[OCX_ROUTING_MARKER_LINE, ROOT_WEB_SEARCH_DISABLED_LINE],
|
|
435
403
|
),
|
|
436
404
|
replacedUserLine,
|
|
437
405
|
wroteValue: ROOT_WEB_SEARCH_DISABLED_VALUE,
|
|
@@ -446,26 +414,20 @@ export function ensureRootWebSearchDisabled(
|
|
|
446
414
|
* comment a Codex app reserialize dropped is still recognized as ours.
|
|
447
415
|
*/
|
|
448
416
|
export function stripInjectedRootWebSearch(content: string, injectedValue?: string | null): string {
|
|
449
|
-
const lines = content
|
|
450
|
-
const firstTable = lines.findIndex((line) => /^\s*\[/.test(line));
|
|
451
|
-
const rootEnd = firstTable === -1 ? lines.length : firstTable;
|
|
417
|
+
const { bom, lines, rootEnd } = rootSourceLines(content);
|
|
452
418
|
const drop = new Set<number>();
|
|
453
|
-
for (let
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
419
|
+
for (let index = 0; index < rootEnd; index++) {
|
|
420
|
+
const line = lines[index]!;
|
|
421
|
+
if (!line.structural || !isRootWebSearchLine(line.text)) continue;
|
|
422
|
+
const assignment = sourceAssignment(lines, index, rootEnd);
|
|
423
|
+
if (!assignment || typeof assignment.value !== "string") continue;
|
|
424
|
+
const value = assignment.value;
|
|
425
|
+
const markerOwned = index > 0 && lines[index - 1]!.structural && isOcxRoutingMarkerLine(lines[index - 1]!.text);
|
|
426
|
+
if (!markerOwned && (!injectedValue || value !== injectedValue)) continue;
|
|
427
|
+
for (let at = index; at < assignment.end; at++) drop.add(at);
|
|
428
|
+
if (markerOwned) drop.add(index - 1);
|
|
458
429
|
}
|
|
459
|
-
|
|
460
|
-
for (let i = 0; i < rootEnd; i += 1) {
|
|
461
|
-
if (!isRootWebSearchLine(lines[i])) continue;
|
|
462
|
-
if (rootTomlString(lines[i], ROOT_WEB_SEARCH_KEY) !== injectedValue) continue;
|
|
463
|
-
drop.add(i);
|
|
464
|
-
if (i > 0 && lines[i - 1].includes(OCX_SECTION_MARKER)) drop.add(i - 1);
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
if (drop.size === 0) return content;
|
|
468
|
-
return lines.filter((_, index) => !drop.has(index)).join("\n");
|
|
430
|
+
return drop.size ? bom + sourceText(lines.filter((_, index) => !drop.has(index))) : content;
|
|
469
431
|
}
|
|
470
432
|
|
|
471
433
|
/**
|
|
@@ -475,21 +437,23 @@ export function stripInjectedRootWebSearch(content: string, injectedValue?: stri
|
|
|
475
437
|
* A marker-owned `experimental_realtime_ws_base_url` pair is removed by the same rule.
|
|
476
438
|
*/
|
|
477
439
|
export function stripInjectedOpenaiBaseUrl(content: string): string {
|
|
478
|
-
const lines = content
|
|
479
|
-
const firstTable = lines.findIndex((l) => /^\s*\[/.test(l));
|
|
480
|
-
const rootEnd = firstTable === -1 ? lines.length : firstTable;
|
|
440
|
+
const { bom, lines, rootEnd } = rootSourceLines(content);
|
|
481
441
|
const drop = new Set<number>();
|
|
482
|
-
for (let
|
|
483
|
-
|
|
484
|
-
if (
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
442
|
+
for (let index = 0; index < rootEnd; index++) {
|
|
443
|
+
const line = lines[index]!;
|
|
444
|
+
if (!line.structural || !isOcxRoutingMarkerLine(line.text)) continue;
|
|
445
|
+
const next = lines[index + 1];
|
|
446
|
+
if (index + 1 < rootEnd && next?.structural
|
|
447
|
+
&& (isRootOpenaiBaseUrlLine(next.text) || isRootRealtimeWsBaseUrlLine(next.text))) {
|
|
448
|
+
const key = rootAssignmentKey(next.text)!;
|
|
449
|
+
if (rootTomlString(next.text, key) === null) continue;
|
|
450
|
+
drop.add(index);
|
|
451
|
+
drop.add(index + 1);
|
|
452
|
+
} else if (index + 1 >= rootEnd || (next?.structural && next.text.trim() === "")) {
|
|
453
|
+
drop.add(index);
|
|
489
454
|
}
|
|
490
455
|
}
|
|
491
|
-
|
|
492
|
-
return lines.filter((_, i) => !drop.has(i)).join("\n");
|
|
456
|
+
return drop.size ? bom + sourceText(lines.filter((_, index) => !drop.has(index))) : content;
|
|
493
457
|
}
|
|
494
458
|
|
|
495
459
|
/**
|
|
@@ -500,18 +464,19 @@ export function stripInjectedOpenaiBaseUrl(content: string): string {
|
|
|
500
464
|
* untouched.
|
|
501
465
|
*/
|
|
502
466
|
export function stripExistingModelProvider(content: string): string {
|
|
503
|
-
const lines = content
|
|
504
|
-
const
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
467
|
+
const { bom, lines, rootEnd } = rootSourceLines(content);
|
|
468
|
+
const drop = new Set<number>();
|
|
469
|
+
for (let index = 0; index < lines.length; index++) {
|
|
470
|
+
if (!lines[index]!.structural || rootAssignmentKey(lines[index]!.text) !== "model_provider") continue;
|
|
471
|
+
const assignment = sourceAssignmentSpan(lines, index, index < rootEnd ? rootEnd : lines.length);
|
|
472
|
+
if (!assignment) throw new Error("Cannot safely rewrite an incomplete model_provider assignment.");
|
|
473
|
+
if (index >= rootEnd) {
|
|
474
|
+
const decoded = sourceAssignment(lines, index);
|
|
475
|
+
if (typeof decoded?.value !== "string" || decoded.value.trim() !== "opencodex") continue;
|
|
511
476
|
}
|
|
512
|
-
|
|
513
|
-
}
|
|
514
|
-
return
|
|
477
|
+
for (let at = index; at < assignment.end; at++) drop.add(at);
|
|
478
|
+
}
|
|
479
|
+
return bom + sourceText(lines.filter((_, index) => !drop.has(index)));
|
|
515
480
|
}
|
|
516
481
|
|
|
517
482
|
/**
|
|
@@ -521,29 +486,21 @@ export function stripExistingModelProvider(content: string): string {
|
|
|
521
486
|
* compaction limits do not alter the advertised context window and must survive reinjection.
|
|
522
487
|
*/
|
|
523
488
|
export function stripRootContextWindowOverrides(content: string): string {
|
|
524
|
-
const lines = content
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
489
|
+
const { bom, lines, rootEnd } = rootSourceLines(content);
|
|
490
|
+
const drop = new Set<number>();
|
|
491
|
+
for (let index = 0; index < rootEnd; index++) {
|
|
492
|
+
if (!lines[index]!.structural || rootAssignmentKey(lines[index]!.text) !== "model_context_window") continue;
|
|
493
|
+
const assignment = sourceAssignmentSpan(lines, index, rootEnd);
|
|
494
|
+
if (!assignment) throw new Error("Cannot safely rewrite an incomplete model_context_window assignment.");
|
|
495
|
+
for (let at = index; at < assignment.end; at++) drop.add(at);
|
|
496
|
+
}
|
|
497
|
+
return bom + sourceText(lines.filter((_, index) => !drop.has(index)));
|
|
532
498
|
}
|
|
533
499
|
|
|
534
500
|
export function stripRootRoutedModel(content: string): string {
|
|
535
|
-
const lines = content
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
.filter((line, i) => {
|
|
539
|
-
const isRoot = firstTable === -1 || i < firstTable;
|
|
540
|
-
if (!isRoot) return true;
|
|
541
|
-
const m = line.match(/^\s*model\s*=\s*("(?:\\.|[^"\\])*"|'[^']*')\s*$/);
|
|
542
|
-
if (!m) return true;
|
|
543
|
-
const model = parseTomlString(m[1]);
|
|
544
|
-
return !model?.includes("/");
|
|
545
|
-
})
|
|
546
|
-
.join("\n");
|
|
501
|
+
const { bom, lines, rootEnd } = rootSourceLines(content);
|
|
502
|
+
return bom + sourceText(lines.filter((line, index) => index >= rootEnd || !line.structural
|
|
503
|
+
|| !rootTomlString(line.text, "model")?.includes("/")));
|
|
547
504
|
}
|
|
548
505
|
|
|
549
506
|
/**
|
|
@@ -551,28 +508,16 @@ export function stripRootRoutedModel(content: string): string {
|
|
|
551
508
|
* header (TOML root keys must precede all tables). If there are no tables, append it to the root body.
|
|
552
509
|
*/
|
|
553
510
|
export function setRootModelProvider(content: string): string {
|
|
554
|
-
|
|
555
|
-
const firstTable = lines.findIndex((l) => /^\s*\[/.test(l));
|
|
556
|
-
const key = 'model_provider = "opencodex"';
|
|
557
|
-
if (firstTable === -1) {
|
|
558
|
-
return content.replace(/\n+$/, "") + "\n" + key + "\n";
|
|
559
|
-
}
|
|
560
|
-
let insertAt = firstTable;
|
|
561
|
-
while (insertAt > 0 && lines[insertAt - 1].trim() === "") insertAt--;
|
|
562
|
-
lines.splice(insertAt, 0, key);
|
|
563
|
-
return lines.join("\n");
|
|
511
|
+
return insertRootSourceLines(content, ['model_provider = "opencodex"']);
|
|
564
512
|
}
|
|
565
513
|
|
|
566
514
|
function readRootModelCatalogPath(content: string): string | null {
|
|
567
|
-
const lines = content
|
|
568
|
-
const firstTable = lines.findIndex((line) => /^\s*\[/.test(line));
|
|
569
|
-
const rootEnd = firstTable === -1 ? lines.length : firstTable;
|
|
570
|
-
const modelCatalogAssignment = tomlStringPattern("model_catalog_json");
|
|
515
|
+
const { lines, rootEnd } = rootSourceLines(content);
|
|
571
516
|
let ownedCatalogPath: string | null = null;
|
|
572
517
|
for (let index = 0; index < rootEnd; index += 1) {
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
518
|
+
if (!lines[index].structural) continue;
|
|
519
|
+
const catalogPath = rootTomlString(lines[index].text, "model_catalog_json");
|
|
520
|
+
if (catalogPath === null) continue;
|
|
576
521
|
if (!isOpencodexCatalogPath(catalogPath)) return catalogPath;
|
|
577
522
|
ownedCatalogPath ??= catalogPath;
|
|
578
523
|
}
|
|
@@ -580,18 +525,14 @@ function readRootModelCatalogPath(content: string): string | null {
|
|
|
580
525
|
}
|
|
581
526
|
|
|
582
527
|
export function setRootModelCatalogPath(content: string, catalogPath: string): string {
|
|
583
|
-
const lines = content
|
|
584
|
-
const firstTable = lines.findIndex((l) => /^\s*\[/.test(l));
|
|
528
|
+
const { bom, lines, rootEnd } = rootSourceLines(content);
|
|
585
529
|
const key = `model_catalog_json = ${tomlString(catalogPath)}`;
|
|
586
|
-
const modelCatalogAssignment = tomlStringPattern("model_catalog_json");
|
|
587
|
-
const rootEnd = firstTable === -1 ? lines.length : firstTable;
|
|
588
530
|
const ownedAssignments: number[] = [];
|
|
589
531
|
let hasUserAssignment = false;
|
|
590
532
|
for (let i = 0; i < rootEnd; i++) {
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
if (isOpencodexCatalogPath(existing)) {
|
|
533
|
+
if (!lines[i].structural || rootAssignmentKey(lines[i].text) !== "model_catalog_json") continue;
|
|
534
|
+
const existing = rootTomlString(lines[i].text, "model_catalog_json");
|
|
535
|
+
if (existing !== null && isOpencodexCatalogPath(existing)) {
|
|
595
536
|
ownedAssignments.push(i);
|
|
596
537
|
} else {
|
|
597
538
|
hasUserAssignment = true;
|
|
@@ -599,53 +540,38 @@ export function setRootModelCatalogPath(content: string, catalogPath: string): s
|
|
|
599
540
|
}
|
|
600
541
|
if (hasUserAssignment) {
|
|
601
542
|
const owned = new Set(ownedAssignments);
|
|
602
|
-
return lines.filter((_, index) => !owned.has(index))
|
|
543
|
+
return bom + sourceText(lines.filter((_, index) => !owned.has(index)));
|
|
603
544
|
}
|
|
604
545
|
if (ownedAssignments.length > 0) {
|
|
605
|
-
lines[ownedAssignments[0]] = key;
|
|
546
|
+
lines[ownedAssignments[0]].text = key;
|
|
606
547
|
const duplicates = new Set(ownedAssignments.slice(1));
|
|
607
|
-
return lines.filter((_, index) => !duplicates.has(index))
|
|
548
|
+
return bom + sourceText(lines.filter((_, index) => !duplicates.has(index)));
|
|
608
549
|
}
|
|
609
|
-
|
|
610
|
-
return content.replace(/\n+$/, "") + "\n" + key + "\n";
|
|
611
|
-
}
|
|
612
|
-
let insertAt = firstTable;
|
|
613
|
-
while (insertAt > 0 && lines[insertAt - 1].trim() === "") insertAt--;
|
|
614
|
-
lines.splice(insertAt, 0, key);
|
|
615
|
-
return lines.join("\n");
|
|
550
|
+
return insertRootSourceLines(content, [key]);
|
|
616
551
|
}
|
|
617
552
|
|
|
618
553
|
export function removeProfileSection(content: string): string {
|
|
619
|
-
const lines = content
|
|
620
|
-
const filtered: string[] = [];
|
|
554
|
+
const { bom, lines } = rootSourceLines(content);
|
|
621
555
|
let inProfile = false;
|
|
622
|
-
|
|
623
|
-
if (line.
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
inProfile = false;
|
|
630
|
-
filtered.push(line);
|
|
631
|
-
}
|
|
632
|
-
continue;
|
|
556
|
+
const kept = lines.filter(line => {
|
|
557
|
+
if (line.structural && /^\s*\[/.test(line.text)) {
|
|
558
|
+
try {
|
|
559
|
+
const parsed = Bun.TOML.parse(line.text) as Record<string, unknown>;
|
|
560
|
+
inProfile = typeof parsed.profiles === "object" && parsed.profiles !== null
|
|
561
|
+
&& Object.hasOwn(parsed.profiles, "opencodex");
|
|
562
|
+
} catch { inProfile = false; }
|
|
633
563
|
}
|
|
634
|
-
|
|
635
|
-
}
|
|
636
|
-
return (
|
|
637
|
-
filtered
|
|
638
|
-
.join("\n")
|
|
639
|
-
.replace(/\n{3,}/g, "\n\n")
|
|
640
|
-
.trimEnd() + "\n"
|
|
641
|
-
);
|
|
564
|
+
return !inProfile;
|
|
565
|
+
});
|
|
566
|
+
return normalizeStructuralWhitespace(bom + sourceText(kept));
|
|
642
567
|
}
|
|
643
568
|
|
|
644
569
|
export function normalizeServiceTier(content: string): string {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
'$1"fast"'
|
|
648
|
-
|
|
570
|
+
const { bom, lines, rootEnd } = rootSourceLines(content);
|
|
571
|
+
for (const line of lines.slice(0, rootEnd)) {
|
|
572
|
+
if (line.structural) line.text = line.text.replace(/^(\s*service_tier\s*=\s*)["']priority["']\s*$/, '$1"fast"');
|
|
573
|
+
}
|
|
574
|
+
return bom + sourceText(lines);
|
|
649
575
|
}
|
|
650
576
|
|
|
651
577
|
export function ensureFastModeFeature(content: string, fastMode?: boolean): string {
|
|
@@ -654,32 +580,41 @@ export function ensureFastModeFeature(content: string, fastMode?: boolean): stri
|
|
|
654
580
|
// untouched (no [features] table is added and an existing fast_mode line is
|
|
655
581
|
// preserved as-is). Table and key matching accept the valid TOML spellings
|
|
656
582
|
// `[features] # comment`, `["features"]` / `['features']`, and quoted keys.
|
|
657
|
-
|
|
658
|
-
const
|
|
659
|
-
const
|
|
660
|
-
const featuresStart = lines.findIndex(line =>
|
|
583
|
+
if (fastMode === undefined) return content;
|
|
584
|
+
const { bom, lines } = rootSourceLines(content);
|
|
585
|
+
const eol = lines.find(line => line.eol)?.eol || "\n";
|
|
586
|
+
const featuresStart = lines.findIndex(line => {
|
|
587
|
+
if (!line.structural || !/^\s*\[/.test(line.text)) return false;
|
|
588
|
+
try {
|
|
589
|
+
const parsed = Bun.TOML.parse(line.text) as Record<string, unknown>;
|
|
590
|
+
return Object.keys(parsed).length === 1 && typeof parsed.features === "object"
|
|
591
|
+
&& parsed.features !== null && !Array.isArray(parsed.features)
|
|
592
|
+
&& Object.keys(parsed.features).length === 0;
|
|
593
|
+
} catch { return false; }
|
|
594
|
+
});
|
|
661
595
|
if (featuresStart === -1) {
|
|
662
|
-
|
|
663
|
-
|
|
596
|
+
return content.replace(/[\r\n]+$/, "") + eol + eol + "[features]" + eol
|
|
597
|
+
+ "fast_mode = " + (fastMode ? "true" : "false") + eol;
|
|
664
598
|
}
|
|
665
599
|
|
|
666
600
|
const nextTable = lines.findIndex(
|
|
667
|
-
(line, index) => index > featuresStart && /^\s*\[/.test(line),
|
|
601
|
+
(line, index) => index > featuresStart && line.structural && /^\s*\[/.test(line.text),
|
|
668
602
|
);
|
|
669
603
|
const featuresEnd = nextTable === -1 ? lines.length : nextTable;
|
|
670
604
|
for (let i = featuresStart + 1; i < featuresEnd; i++) {
|
|
671
|
-
if (
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
605
|
+
if (lines[i].structural && rootAssignmentKey(lines[i].text) === "fast_mode") {
|
|
606
|
+
// Refuse to rewrite an incomplete physical view of a collection/string value.
|
|
607
|
+
try { Bun.TOML.parse(lines[i].text); } catch { return content; }
|
|
608
|
+
lines[i].text = `${/^\s*/.exec(lines[i].text)![0]}fast_mode = ${fastMode ? "true" : "false"}`;
|
|
609
|
+
return bom + sourceText(lines);
|
|
675
610
|
}
|
|
676
611
|
}
|
|
677
612
|
|
|
678
|
-
if (fastMode === undefined) return lines.join("\n");
|
|
679
613
|
let insertAt = featuresEnd;
|
|
680
|
-
while (insertAt > featuresStart + 1 && lines[insertAt - 1].trim() === "") insertAt--;
|
|
681
|
-
|
|
682
|
-
|
|
614
|
+
while (insertAt > featuresStart + 1 && lines[insertAt - 1].structural && lines[insertAt - 1].text.trim() === "") insertAt--;
|
|
615
|
+
if (insertAt > 0 && !lines[insertAt - 1].eol) lines[insertAt - 1].eol = eol;
|
|
616
|
+
lines.splice(insertAt, 0, { text: `fast_mode = ${fastMode ? "true" : "false"}`, eol, structural: true });
|
|
617
|
+
return bom + sourceText(lines);
|
|
683
618
|
}
|
|
684
619
|
|
|
685
620
|
function isOpencodexCatalogPath(path: string): boolean {
|
|
@@ -687,17 +622,13 @@ function isOpencodexCatalogPath(path: string): boolean {
|
|
|
687
622
|
}
|
|
688
623
|
|
|
689
624
|
export function stripOpencodexCatalogPath(content: string): string {
|
|
690
|
-
const
|
|
691
|
-
|
|
692
|
-
const firstTable = lines.findIndex((line) => /^\s*\[/.test(line));
|
|
693
|
-
const rootEnd = firstTable === -1 ? lines.length : firstTable;
|
|
694
|
-
return lines
|
|
625
|
+
const { bom, lines, rootEnd } = rootSourceLines(content);
|
|
626
|
+
return bom + sourceText(lines
|
|
695
627
|
.filter((line, index) => {
|
|
696
|
-
if (index >= rootEnd) return true;
|
|
697
|
-
const
|
|
698
|
-
return
|
|
699
|
-
})
|
|
700
|
-
.join("\n");
|
|
628
|
+
if (index >= rootEnd || !line.structural) return true;
|
|
629
|
+
const path = rootTomlString(line.text, "model_catalog_json");
|
|
630
|
+
return path === null || !isOpencodexCatalogPath(path);
|
|
631
|
+
}));
|
|
701
632
|
}
|
|
702
633
|
|
|
703
634
|
export function buildProfileFile(port: number, catalogPath?: string | null, supportsWebsockets?: boolean, includeApiAuthHeader?: boolean, hostname?: string, fastMode?: boolean): string;
|