sneakoscope 10.7.1 → 10.7.3
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 +1 -1
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/cli/command-manifest-lite.js +1 -1
- package/dist/cli/install-helpers-codex-lb-config.js +44 -12
- package/dist/commands/bridge.js +14 -1
- package/dist/commands/codex-app.js +3 -3
- package/dist/commands/codex.js +5 -1
- package/dist/commands/doctor-console.js +2 -0
- package/dist/commands/doctor.js +8 -9
- package/dist/config/skills-manifest.json +1 -1
- package/dist/core/codex-app/codex-context-window.js +23 -8
- package/dist/core/codex-app/menubar/constants.js +0 -1
- package/dist/core/codex-control/model-call-concurrency.js +6 -5
- package/dist/core/codex-lb/combined-catalog/persistence.js +4 -0
- package/dist/core/codex-lb/combined-catalog/storage.js +4 -1
- package/dist/core/codex-lb/desktop-bridge/capacity-retry-advice.js +35 -11
- package/dist/core/codex-lb/desktop-bridge/http-forward.js +48 -8
- package/dist/core/codex-lb/desktop-bridge/models-endpoint.js +62 -0
- package/dist/core/codex-lb/desktop-bridge/provider-handoff.js +13 -4
- package/dist/core/codex-lb/desktop-bridge/provider-request-compat.js +89 -0
- package/dist/core/codex-lb/desktop-bridge/rejection-log.js +61 -0
- package/dist/core/codex-lb/desktop-bridge/responses-websocket.js +52 -4
- package/dist/core/codex-lb/desktop-bridge/server.js +5 -0
- package/dist/core/codex-lb/desktop-bridge-migration/historical-config.js +2 -6
- package/dist/core/codex-lb/desktop-bridge-migration.js +14 -2
- package/dist/core/codex-lb/desktop-controller-v3/catalog.js +114 -5
- package/dist/core/codex-lb/desktop-controller-v3/model-refresh.js +140 -0
- package/dist/core/codex-lb/desktop-controller-v3/openrouter-only-commands.js +5 -5
- package/dist/core/codex-lb/desktop-controller-v3/provider-commands.js +14 -2
- package/dist/core/codex-lb/desktop-controller-v3/status.js +2 -3
- package/dist/core/codex-lb/desktop-controller-v3.js +3 -1
- package/dist/core/codex-lb/desktop-service.js +41 -4
- package/dist/core/codex-lb/model-refresh-after.js +16 -0
- package/dist/core/commands/basic-cli.js +5 -0
- package/dist/core/commands/menubar-command.js +5 -1
- package/dist/core/doctor/desktop-bridge-catalog-repair.js +20 -17
- package/dist/core/subagents/model-tiers.js +19 -3
- package/dist/core/update/update-migration-state/desktop-bridge-catalog-repair-stage.js +4 -3
- package/dist/core/version.js +1 -1
- package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +0 -1
- package/dist/native/sks-menubar/Sources/OverviewSummary.swift +32 -0
- package/dist/native/sks-menubar/Sources/OverviewViewController.swift +15 -5
- package/dist/native/sks-menubar/Sources/SidebarItem.swift +0 -2
- package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +3 -0
- package/package.json +1 -1
- package/dist/native/sks-menubar/Sources/RemoteCodingViewController.swift +0 -49
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
Sneakoscope Codex (`sks`) is an open-source local tool for the Codex CLI and Codex desktop app. It adds a macOS control center, bounded agent workflows, repository context, and evidence checks for SKS releases.
|
|
18
18
|
<!-- END SKS SEARCH VISIBILITY MARKETING -->
|
|
19
19
|
|
|
20
|
-
Current package: **SKS 10.7.
|
|
20
|
+
Current package: **SKS 10.7.3**, prepared for npm publication.
|
|
21
21
|
[Latest release notes](https://github.com/mandarange/Sneakoscope-Codex/releases/latest)
|
|
22
22
|
· [npm package](https://www.npmjs.com/package/sneakoscope)
|
|
23
23
|
· [Changelog](CHANGELOG.md). Install the latest stable release from npm.
|
|
@@ -273,7 +273,7 @@ const COMMAND_HELP_LITE = {
|
|
|
273
273
|
},
|
|
274
274
|
doctor: {
|
|
275
275
|
usage: 'sks doctor [--fix] [--local-only] [--json] [--install-scope global|project]',
|
|
276
|
-
description: 'Check and repair SKS generated files, while blocking setup if another Codex harness is detected.'
|
|
276
|
+
description: 'Check and repair SKS generated files, while blocking setup if another Codex harness is detected. Every run refreshes the Desktop Bridge model list; without --fix it writes only SKS\'s own catalog files, never ~/.codex/config.toml.'
|
|
277
277
|
},
|
|
278
278
|
git: {
|
|
279
279
|
usage: 'sks git policy|install|status|doctor|precommit|publish-plan|summary [--json]',
|
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { ensureTrailingNewline, upsertTopLevelTomlString } from '../core/codex-runtime/codex-desktop-config-policy.js';
|
|
3
3
|
import { escapeRegExp } from '../core/text/regex.js';
|
|
4
|
+
export function codexReadsBridgeModels(text) {
|
|
5
|
+
const source = String(text || '');
|
|
6
|
+
return topLevelHasLine(source, DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER)
|
|
7
|
+
&& Boolean(topLevelTomlString(source, 'openai_base_url'))
|
|
8
|
+
&& !topLevelTomlString(source, 'model_catalog_json');
|
|
9
|
+
}
|
|
10
|
+
export function hasUserOwnedModelCatalog(text) {
|
|
11
|
+
const source = String(text || '');
|
|
12
|
+
return Boolean(topLevelTomlString(source, 'model_catalog_json'))
|
|
13
|
+
&& !topLevelHasLine(source, DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER);
|
|
14
|
+
}
|
|
15
|
+
export function setDesktopBridgeManagedModelCatalog(text, combinedCatalogPath) {
|
|
16
|
+
const source = String(text || '');
|
|
17
|
+
if (!topLevelHasLine(source, DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER) || hasUserOwnedModelCatalog(source))
|
|
18
|
+
return source;
|
|
19
|
+
if (combinedCatalogPath === null) {
|
|
20
|
+
if (!topLevelTomlString(source, 'model_catalog_json'))
|
|
21
|
+
return source;
|
|
22
|
+
return ensureTrailingNewline(removeTopLevelLine(removeTopLevelTomlKey(source, 'model_catalog_json'), DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER));
|
|
23
|
+
}
|
|
24
|
+
assertCombinedCatalogPath(combinedCatalogPath);
|
|
25
|
+
if (topLevelTomlString(source, 'model_catalog_json') === combinedCatalogPath)
|
|
26
|
+
return source;
|
|
27
|
+
const next = upsertTopLevelTomlString(source, 'model_catalog_json', combinedCatalogPath);
|
|
28
|
+
return ensureTrailingNewline(addTopLevelMarkerBeforeKey(next, 'model_catalog_json', DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER));
|
|
29
|
+
}
|
|
30
|
+
function assertCombinedCatalogPath(combinedCatalogPath) {
|
|
31
|
+
if (!combinedCatalogPath || !path.isAbsolute(combinedCatalogPath)
|
|
32
|
+
|| path.basename(combinedCatalogPath) !== 'sks-bridge-catalog.json') {
|
|
33
|
+
throw new Error('desktop_bridge_combined_catalog_path_invalid');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
4
36
|
export const DESKTOP_BRIDGE_MANAGED_MARKER = '# sks-desktop-bridge-managed';
|
|
5
37
|
export const DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER = '# sks-desktop-bridge-managed-base-url';
|
|
6
38
|
export const DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER = '# sks-desktop-bridge-managed-model-catalog';
|
|
@@ -18,12 +50,8 @@ export function hasCodexLbProviderTable(text) {
|
|
|
18
50
|
export function upsertDesktopBridgeManagedConfig(text, input) {
|
|
19
51
|
const bridgeBaseUrl = normalizeManagedBridgeBaseUrl(input.bridgeBaseUrl);
|
|
20
52
|
const combinedCatalogPath = String(input.combinedCatalogPath || '').trim();
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
if (path.basename(combinedCatalogPath) !== 'sks-bridge-catalog.json') {
|
|
25
|
-
throw new Error('desktop_bridge_combined_catalog_path_invalid');
|
|
26
|
-
}
|
|
53
|
+
assertCombinedCatalogPath(combinedCatalogPath);
|
|
54
|
+
const bindModelCatalog = input.bindModelCatalog !== false;
|
|
27
55
|
const orphanCleanup = removeDesktopBridgeOrphanManagedMarkers(String(text || ''));
|
|
28
56
|
let next = orphanCleanup.text;
|
|
29
57
|
const selectedProvider = topLevelTomlString(next, 'model_provider');
|
|
@@ -39,17 +67,20 @@ export function upsertDesktopBridgeManagedConfig(text, input) {
|
|
|
39
67
|
}
|
|
40
68
|
const existingCatalog = topLevelTomlString(next, 'model_catalog_json');
|
|
41
69
|
const catalogOwned = topLevelHasLine(next, DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER);
|
|
42
|
-
if (existingCatalog && !catalogOwned) {
|
|
70
|
+
if (existingCatalog && !catalogOwned && bindModelCatalog) {
|
|
43
71
|
throw new Error('historical_user_owned_config_conflict:model_catalog_json');
|
|
44
72
|
}
|
|
73
|
+
const userCatalog = existingCatalog && !catalogOwned ? existingCatalog : null;
|
|
45
74
|
const keepCodexLb = selectedProvider === 'codex-lb' && hasCodexLbProviderTable(next);
|
|
46
|
-
next = removeManagedBridgeTopLevelBindings(next);
|
|
75
|
+
next = removeManagedBridgeTopLevelBindings(next, { keepModelCatalog: userCatalog !== null });
|
|
47
76
|
next = upsertTopLevelTomlString(next, 'model_provider', keepCodexLb ? 'codex-lb' : 'openai');
|
|
48
77
|
next = addTopLevelMarkerBeforeKey(next, 'model_provider', DESKTOP_BRIDGE_MANAGED_MARKER);
|
|
49
78
|
next = upsertTopLevelTomlString(next, 'openai_base_url', bridgeBaseUrl);
|
|
50
79
|
next = addTopLevelMarkerBeforeKey(next, 'openai_base_url', DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER);
|
|
51
|
-
|
|
52
|
-
|
|
80
|
+
if (bindModelCatalog) {
|
|
81
|
+
next = upsertTopLevelTomlString(next, 'model_catalog_json', combinedCatalogPath);
|
|
82
|
+
next = addTopLevelMarkerBeforeKey(next, 'model_catalog_json', DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER);
|
|
83
|
+
}
|
|
53
84
|
return ensureTrailingNewline(next);
|
|
54
85
|
}
|
|
55
86
|
export function removeDesktopBridgeManagedConfig(text) {
|
|
@@ -135,11 +166,12 @@ function removeTopLevelTomlKey(text, key) {
|
|
|
135
166
|
.replace(/^\n+/, '')
|
|
136
167
|
.replace(/\n{3,}/g, '\n\n');
|
|
137
168
|
}
|
|
138
|
-
function removeManagedBridgeTopLevelBindings(text) {
|
|
169
|
+
function removeManagedBridgeTopLevelBindings(text, options = {}) {
|
|
139
170
|
let next = String(text || '');
|
|
140
171
|
next = removeTopLevelTomlKey(next, 'model_provider');
|
|
141
172
|
next = removeTopLevelTomlKey(next, 'openai_base_url');
|
|
142
|
-
|
|
173
|
+
if (!options.keepModelCatalog)
|
|
174
|
+
next = removeTopLevelTomlKey(next, 'model_catalog_json');
|
|
143
175
|
for (const marker of [
|
|
144
176
|
DESKTOP_BRIDGE_MANAGED_MARKER,
|
|
145
177
|
DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER,
|
package/dist/commands/bridge.js
CHANGED
|
@@ -31,6 +31,7 @@ export function usage(command = 'bridge') {
|
|
|
31
31
|
` sks ${command} catalog sync|status [--json]`,
|
|
32
32
|
` sks ${command} models list [--json]`,
|
|
33
33
|
` sks ${command} models select --set <public-id,...> [--json]`,
|
|
34
|
+
` sks ${command} models refresh [--json]`,
|
|
34
35
|
` sks ${command} route list [--json]`,
|
|
35
36
|
` sks ${command} route set-default <codex-lb|openrouter> [--json]`,
|
|
36
37
|
` sks ${command} route official-models <passthrough|gateway|auto> [--json]`,
|
|
@@ -86,7 +87,7 @@ export async function executeBridgeCommand(args = [], options = {}) {
|
|
|
86
87
|
: await (options.facade || await loadBridgeCommandFacade()).execute(parsed.request);
|
|
87
88
|
const output = parsed.request.operation === 'verify'
|
|
88
89
|
? verificationOutput(raw, parsed.request.level, parsed.strict)
|
|
89
|
-
: ordinaryOutput(raw, parsed.label);
|
|
90
|
+
: ordinaryOutput(raw, parsed.request.operation === 'models.refresh' ? modelRefreshLine(raw) ?? parsed.label : parsed.label);
|
|
90
91
|
const sanitized = sanitizeBridgeValue(output, parsed.secrets);
|
|
91
92
|
const decorated = mergeMetadata(sanitized, options.metadata);
|
|
92
93
|
return {
|
|
@@ -104,6 +105,10 @@ export async function executeBridgeCommand(args = [], options = {}) {
|
|
|
104
105
|
};
|
|
105
106
|
}
|
|
106
107
|
}
|
|
108
|
+
function modelRefreshLine(raw) {
|
|
109
|
+
const result = raw?.result;
|
|
110
|
+
return typeof result?.model_refresh?.line === 'string' ? result.model_refresh.line : null;
|
|
111
|
+
}
|
|
107
112
|
export async function loadBridgeCommandFacade() {
|
|
108
113
|
const controller = await import('../core/codex-lb/desktop-controller.js');
|
|
109
114
|
const execute = controller[CONTROLLER_FACADE_EXPORT];
|
|
@@ -260,6 +265,14 @@ async function parseInvocation(args, io) {
|
|
|
260
265
|
allowOnly(parsed, ['--json'], []);
|
|
261
266
|
return { ...base, request: { operation: 'models.list' }, label: 'Selectable bridge models' };
|
|
262
267
|
}
|
|
268
|
+
if (action === 'refresh' && target === undefined) {
|
|
269
|
+
allowOnly(parsed, ['--json'], []);
|
|
270
|
+
return {
|
|
271
|
+
...base,
|
|
272
|
+
request: { operation: 'models.refresh', reason: 'cli', force: true, allow_config_writes: true },
|
|
273
|
+
label: 'Model list refresh'
|
|
274
|
+
};
|
|
275
|
+
}
|
|
263
276
|
if (action === 'select') {
|
|
264
277
|
allowOnly(parsed, ['--json'], ['--set']);
|
|
265
278
|
const raw = parsed.values.get('--set') || '';
|
|
@@ -36,13 +36,13 @@ export async function run(_command, args = []) {
|
|
|
36
36
|
process.exitCode = 1;
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
39
|
-
console.log(`Codex 1M Context: ${result.enabled ? 'enabled' : 'disabled'}`);
|
|
39
|
+
console.log(`Codex 1M Context: ${result.enabled === null ? 'unavailable' : result.enabled ? 'enabled' : 'disabled'}`);
|
|
40
40
|
console.log(`Config: ${result.config_path}`);
|
|
41
41
|
const window = result.model_window;
|
|
42
|
-
console.log(`Model: ${result.model || 'not set'}${window.effective_window ? ` · window ${window.effective_window} (default ${window.default_window ?? 'unknown'}, max ${window.max_window ?? 'uncapped'})` : ' · window unknown'}`);
|
|
42
|
+
console.log(`Model: ${result.enabled === null ? 'unavailable' : result.model || 'not set'}${window.effective_window ? ` · window ${window.effective_window} (default ${window.default_window ?? 'unknown'}, max ${window.max_window ?? 'uncapped'})` : ' · window unknown'}`);
|
|
43
43
|
if (result.larger_window_models.length)
|
|
44
44
|
console.log(`Models with a larger window: ${result.larger_window_models.join(', ')}`);
|
|
45
|
-
for (const [key, target] of Object.entries(result.target)) {
|
|
45
|
+
for (const [key, target] of result.keys ? Object.entries(result.target) : []) {
|
|
46
46
|
const state = result.keys[key];
|
|
47
47
|
const detail = state?.present ? `${state.value ?? 'unparsed'}${state.managed ? ' (SKS-managed)' : ''}` : 'not set';
|
|
48
48
|
console.log(`${key}: ${detail} · target ${target}`);
|
package/dist/commands/codex.js
CHANGED
|
@@ -45,7 +45,9 @@ export async function run(_command, args = []) {
|
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
if (action === 'update' || action === 'update-now') {
|
|
48
|
-
const
|
|
48
|
+
const updated = await updateCodexCliNow();
|
|
49
|
+
const modelRefresh = await (await import('../core/codex-lb/model-refresh-after.js')).refreshModelListAfter('codex-update');
|
|
50
|
+
const result = modelRefresh ? { ...updated, model_refresh: modelRefresh } : updated;
|
|
49
51
|
if (flag(args, '--json')) {
|
|
50
52
|
printJson(result);
|
|
51
53
|
if (!result.ok)
|
|
@@ -54,6 +56,8 @@ export async function run(_command, args = []) {
|
|
|
54
56
|
}
|
|
55
57
|
for (const line of codexCliUpdateConsoleLines(result))
|
|
56
58
|
console.log(line);
|
|
59
|
+
if (typeof modelRefresh?.line === 'string')
|
|
60
|
+
console.log(modelRefresh.line);
|
|
57
61
|
if (!result.ok)
|
|
58
62
|
process.exitCode = 1;
|
|
59
63
|
return;
|
|
@@ -238,6 +238,8 @@ export function renderDoctorConsoleReport(result, extras) {
|
|
|
238
238
|
if (codexCurrentAppDoctor.fixed?.length)
|
|
239
239
|
line(` doctor --fix repaired: ${codexCurrentAppDoctor.fixed.join(', ')}`);
|
|
240
240
|
line(`Desktop Bridge: ${desktopBridge.ok ? 'ready' : 'blocked'} (${desktopBridge.status?.readiness?.state || 'unavailable'})`);
|
|
241
|
+
if (desktopBridge.model_refresh?.line)
|
|
242
|
+
line(` ${desktopBridge.model_refresh.line}`);
|
|
241
243
|
for (const providerId of ['codex-lb', 'openrouter']) {
|
|
242
244
|
const provider = desktopBridge.providers?.[providerId];
|
|
243
245
|
if (!provider)
|
package/dist/commands/doctor.js
CHANGED
|
@@ -376,6 +376,8 @@ async function runDoctorGlobalOnlyFix(args = [], root, deps = {}) {
|
|
|
376
376
|
? 'repaired'
|
|
377
377
|
: bridgeRepair?.ok === false ? 'failed' : 'nothing_to_repair'}`);
|
|
378
378
|
console.log(`Desktop Bridge: ${result.desktop_bridge.ok ? 'ready' : 'blocked'} (${result.desktop_bridge.status?.readiness?.state || 'unavailable'})`);
|
|
379
|
+
if (result.desktop_bridge_repair?.model_refresh?.line)
|
|
380
|
+
console.log(result.desktop_bridge_repair.model_refresh.line);
|
|
379
381
|
if (result.migration_receipt) {
|
|
380
382
|
console.log(`Migration receipt: ${result.migration_receipt.status} (${result.home})`);
|
|
381
383
|
}
|
|
@@ -737,9 +739,8 @@ async function runDoctor(args = [], root, doctorFix, deps = {}) {
|
|
|
737
739
|
const codexApp = deepDiagnostics
|
|
738
740
|
? await codexAppIntegrationStatus({ codex }).catch((err) => ({ ok: false, error: err.message }))
|
|
739
741
|
: { ok: false, skipped: true, warnings: ['codex_app_optional_diagnostic_skipped'] };
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
}, { desktopBridgeStatusImpl: deps.desktopBridgeStatusImpl });
|
|
742
|
+
let desktopBridgeModelRefresh = doctorFix ? null : (await (deps.desktopBridgeRepairImpl || repairDoctorDesktopBridgeCatalog)({ fix: false, reason: 'doctor' }).catch(() => null))?.model_refresh ?? null;
|
|
743
|
+
const desktopBridgeBeforeFix = await inspectDoctorDesktopBridgeStatus({ processEnv: process.env }, { desktopBridgeStatusImpl: deps.desktopBridgeStatusImpl });
|
|
743
744
|
const providerContext = deepDiagnostics
|
|
744
745
|
? await resolveProviderContext({ root, route: '$Doctor', serviceTier: process.env.SKS_SERVICE_TIER || 'fast' }).catch((err) => ({
|
|
745
746
|
schema: 'sks.provider-context.v2',
|
|
@@ -1091,7 +1092,7 @@ async function runDoctor(args = [], root, doctorFix, deps = {}) {
|
|
|
1091
1092
|
{
|
|
1092
1093
|
id: 'desktop_bridge_catalog_repair',
|
|
1093
1094
|
required_for_ready: false,
|
|
1094
|
-
run: async () => repairDoctorDesktopBridgeCatalog({ fix: doctorFix })
|
|
1095
|
+
run: async () => { const repair = await repairDoctorDesktopBridgeCatalog({ fix: doctorFix }); desktopBridgeModelRefresh = repair.model_refresh ?? null; return repair; }
|
|
1095
1096
|
}
|
|
1096
1097
|
].filter((phase) => doctorPhaseIds.includes(phase.id))
|
|
1097
1098
|
}).catch((err) => ({
|
|
@@ -1118,11 +1119,9 @@ async function runDoctor(args = [], root, doctorFix, deps = {}) {
|
|
|
1118
1119
|
}))
|
|
1119
1120
|
: null;
|
|
1120
1121
|
const doctorFixPostcheck = doctorFix ? (await import('../core/doctor/doctor-repair-postcheck.js')).doctorRepairPostcheck(doctorFixTransaction) : null;
|
|
1121
|
-
const desktopBridge = doctorFixTransaction
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
}, { desktopBridgeStatusImpl: deps.desktopBridgeStatusImpl })
|
|
1125
|
-
: desktopBridgeBeforeFix;
|
|
1122
|
+
const desktopBridge = { ...(doctorFixTransaction
|
|
1123
|
+
? await inspectDoctorDesktopBridgeStatus({ processEnv: process.env }, { desktopBridgeStatusImpl: deps.desktopBridgeStatusImpl })
|
|
1124
|
+
: desktopBridgeBeforeFix), model_refresh: desktopBridgeModelRefresh };
|
|
1126
1125
|
const startupRoleRepair = startupConfigRepair?.role_repair;
|
|
1127
1126
|
const agentRoleConfigRepair = doctorFix && startupRoleRepair
|
|
1128
1127
|
? startupRoleRepair
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import {
|
|
2
|
+
import { readText } from '../fsx.js';
|
|
3
3
|
import { escapeRegExp } from '../text/regex.js';
|
|
4
4
|
import { safeWriteCodexConfigToml } from '../codex-runtime/codex-desktop-config-policy.js';
|
|
5
5
|
import { codexHomePath, codexUserConfigPath, readTopLevelTomlString } from './codex-model-catalog.js';
|
|
@@ -177,15 +177,28 @@ export async function codexContext1mCommand(args = [], opts = {}) {
|
|
|
177
177
|
const action = normalizeCodexContext1mAction(args[0]);
|
|
178
178
|
const noRestart = args.includes('--no-restart');
|
|
179
179
|
const configPath = codexUserConfigPath({ env, ...(opts.home ? { home: opts.home } : {}) });
|
|
180
|
-
const before = String(await readText(configPath, ''));
|
|
181
|
-
const fileExists = await exists(configPath);
|
|
182
180
|
const blockers = [];
|
|
183
181
|
const warnings = [];
|
|
182
|
+
let before = '';
|
|
183
|
+
let fileExists = true;
|
|
184
|
+
let configReadable = true;
|
|
185
|
+
try {
|
|
186
|
+
before = await readText(configPath);
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
if (error?.code === 'ENOENT') {
|
|
190
|
+
fileExists = false;
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
configReadable = false;
|
|
194
|
+
blockers.push('codex_context_config_unreadable');
|
|
195
|
+
}
|
|
196
|
+
}
|
|
184
197
|
let write = null;
|
|
185
198
|
let changed = false;
|
|
186
199
|
let afterText = before;
|
|
187
200
|
let mutation = null;
|
|
188
|
-
if (action === 'on' || action === 'off') {
|
|
201
|
+
if (configReadable && (action === 'on' || action === 'off')) {
|
|
189
202
|
mutation = action === 'on' ? enableCodexContext1m(before) : disableCodexContext1m(before);
|
|
190
203
|
blockers.push(...mutation.blockers);
|
|
191
204
|
warnings.push(...mutation.warnings);
|
|
@@ -205,9 +218,11 @@ export async function codexContext1mCommand(args = [], opts = {}) {
|
|
|
205
218
|
}
|
|
206
219
|
const inspection = inspectCodexContext1m(afterText);
|
|
207
220
|
warnings.push(...inspection.warnings);
|
|
208
|
-
const windowRows =
|
|
221
|
+
const windowRows = configReadable
|
|
222
|
+
? await readCodexModelWindows({ env, ...(opts.home ? { home: opts.home } : {}) })
|
|
223
|
+
: [];
|
|
209
224
|
const modelWindow = codexContextModelWindow(windowRows, inspection.model);
|
|
210
|
-
if (action === 'on' || inspection.enabled) {
|
|
225
|
+
if (configReadable && (action === 'on' || inspection.enabled)) {
|
|
211
226
|
if (!inspection.model)
|
|
212
227
|
warnings.push('codex_context_model_line_missing');
|
|
213
228
|
else if (modelWindow.source === 'unknown')
|
|
@@ -235,7 +250,7 @@ export async function codexContext1mCommand(args = [], opts = {}) {
|
|
|
235
250
|
schema: CODEX_CONTEXT_1M_SCHEMA,
|
|
236
251
|
ok: blockers.length === 0,
|
|
237
252
|
action,
|
|
238
|
-
enabled: inspection.enabled,
|
|
253
|
+
enabled: configReadable ? inspection.enabled : null,
|
|
239
254
|
config_path: configPath,
|
|
240
255
|
model: inspection.model,
|
|
241
256
|
model_window: modelWindow,
|
|
@@ -243,7 +258,7 @@ export async function codexContext1mCommand(args = [], opts = {}) {
|
|
|
243
258
|
.filter((row) => row.listed && row.max_window !== null && row.default_window !== null && row.max_window > row.default_window)
|
|
244
259
|
.map((row) => row.slug),
|
|
245
260
|
target: { ...CODEX_CONTEXT_1M_TARGETS },
|
|
246
|
-
keys: inspection.keys,
|
|
261
|
+
keys: configReadable ? inspection.keys : null,
|
|
247
262
|
previous: mutation && action === 'on' ? mutation.previous : null,
|
|
248
263
|
restored: mutation && action === 'off' ? mutation.restored : null,
|
|
249
264
|
changed,
|
|
@@ -33,7 +33,6 @@ export const NATIVE_SOURCE_FILES = [
|
|
|
33
33
|
'ProvidersOpenRouterOnly.swift',
|
|
34
34
|
'SubagentModelsModels.swift',
|
|
35
35
|
'SubagentModelsViewController.swift',
|
|
36
|
-
'RemoteCodingViewController.swift',
|
|
37
36
|
'DiagnosticsViewController.swift',
|
|
38
37
|
'SettingsViewController.swift',
|
|
39
38
|
'LocalDecisionModels.swift',
|
|
@@ -83,15 +83,16 @@ class ModelCallSemaphoreImpl {
|
|
|
83
83
|
}
|
|
84
84
|
this.queued += 1;
|
|
85
85
|
await new Promise((resolve) => this.waiters.push(resolve));
|
|
86
|
-
this.queued = Math.max(0, this.queued - 1);
|
|
87
|
-
this.active += 1;
|
|
88
|
-
this.maxObserved = Math.max(this.maxObserved, this.active);
|
|
89
86
|
}
|
|
90
87
|
release() {
|
|
91
|
-
this.active = Math.max(0, this.active - 1);
|
|
92
88
|
const next = this.waiters.shift();
|
|
93
|
-
if (next)
|
|
89
|
+
if (next) {
|
|
90
|
+
this.queued -= 1;
|
|
94
91
|
next();
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
this.active = Math.max(0, this.active - 1);
|
|
95
|
+
}
|
|
95
96
|
}
|
|
96
97
|
}
|
|
97
98
|
function envInt(name, fallback) {
|
|
@@ -43,6 +43,10 @@ export async function stageCombinedBridgeCatalog(input) {
|
|
|
43
43
|
|| existing.route_index.generation !== input.build.route_index.generation) {
|
|
44
44
|
throw new Error('combined_catalog_generation_bundle_mismatch');
|
|
45
45
|
}
|
|
46
|
+
if (JSON.stringify(existing.catalog.provider_statuses) !== JSON.stringify(input.build.catalog.provider_statuses)) {
|
|
47
|
+
await fs.rename(tempCatalog, layout.catalogPath);
|
|
48
|
+
await fsyncDirectory(layout.bundleDirectory);
|
|
49
|
+
}
|
|
46
50
|
}
|
|
47
51
|
else {
|
|
48
52
|
await fs.rename(tempBundle, layout.bundleDirectory);
|
|
@@ -139,7 +139,10 @@ export function emptyRouteIndex() {
|
|
|
139
139
|
}).route_index;
|
|
140
140
|
}
|
|
141
141
|
export function catalogObservationGeneration(catalog) {
|
|
142
|
-
return sha256Stable(catalog.provider_statuses)
|
|
142
|
+
return sha256Stable(Object.fromEntries(Object.entries(catalog.provider_statuses).map(([providerId, row]) => {
|
|
143
|
+
const { checked_at: _checkedAt, expires_at: _expiresAt, ...observed } = row;
|
|
144
|
+
return [providerId, observed];
|
|
145
|
+
})));
|
|
143
146
|
}
|
|
144
147
|
function validProviderStatuses(value) {
|
|
145
148
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
@@ -67,23 +67,47 @@ function adviseSseEvents(events, onAdvised) {
|
|
|
67
67
|
return Buffer.from(parts.join(''));
|
|
68
68
|
}
|
|
69
69
|
export function modelCapacityAdvisingSse(onAdvised) {
|
|
70
|
+
const maxEventBytes = 1024 * 1024;
|
|
70
71
|
let carry = Buffer.alloc(0);
|
|
71
|
-
|
|
72
|
+
let passthrough = false;
|
|
73
|
+
let endingBytes = 0;
|
|
74
|
+
const stream = new Transform({
|
|
72
75
|
transform(chunk, _encoding, done) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
let advised = false;
|
|
77
|
+
let start = 0;
|
|
78
|
+
for (let index = 0; index < chunk.length; index += 1) {
|
|
79
|
+
endingBytes = ((endingBytes << 8) | chunk[index]) >>> 0;
|
|
80
|
+
if ((endingBytes & 0xffff) !== 0x0a0a && endingBytes !== 0x0d0a0d0a)
|
|
81
|
+
continue;
|
|
82
|
+
append(chunk.subarray(start, index + 1));
|
|
83
|
+
if (!passthrough)
|
|
84
|
+
this.push(adviseSseEvents(carry, () => { advised = true; }));
|
|
85
|
+
carry = Buffer.alloc(0);
|
|
86
|
+
passthrough = false;
|
|
87
|
+
endingBytes = 0;
|
|
88
|
+
start = index + 1;
|
|
81
89
|
}
|
|
82
|
-
|
|
83
|
-
|
|
90
|
+
if (start < chunk.length)
|
|
91
|
+
append(chunk.subarray(start));
|
|
92
|
+
if (advised)
|
|
93
|
+
onAdvised();
|
|
94
|
+
done();
|
|
84
95
|
},
|
|
85
96
|
flush(done) {
|
|
86
97
|
done(null, carry.length ? adviseSseEvents(carry, onAdvised) : null);
|
|
87
98
|
},
|
|
88
99
|
});
|
|
100
|
+
function append(part) {
|
|
101
|
+
if (passthrough || carry.length + part.length > maxEventBytes) {
|
|
102
|
+
if (carry.length)
|
|
103
|
+
stream.push(carry);
|
|
104
|
+
carry = Buffer.alloc(0);
|
|
105
|
+
passthrough = true;
|
|
106
|
+
stream.push(part);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
carry = carry.length ? Buffer.concat([carry, part]) : Buffer.from(part);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return stream;
|
|
89
113
|
}
|
|
@@ -4,12 +4,14 @@ import zlib from 'node:zlib';
|
|
|
4
4
|
import { pipeline } from 'node:stream/promises';
|
|
5
5
|
import { BRIDGE_OFFICIAL_ROUTE_ID, BRIDGE_REMOTE_COMPACTION } from '../bridge-contracts.js';
|
|
6
6
|
import { buildOfficialPassthroughHeaders, buildProviderUpstreamHeaders, rewriteResponseHeaders } from './header-policy.js';
|
|
7
|
-
import { createDesktopBridgeRejectionLogger } from './rejection-log.js';
|
|
7
|
+
import { createDesktopBridgeRejectionLogger, desktopBridgeRequestShape, safeUpstreamParam } from './rejection-log.js';
|
|
8
8
|
import { OPENROUTER_ONLY_REFUSAL_CODES } from './exclusive-provider-guard.js';
|
|
9
9
|
import { ensureDesktopBridgeRemoteTarget, isNoRouteUpstreamError, isResponsesFamilyPath, isUnreachableUpstreamError, pinnedPublicModel, refreshDesktopBridgeRemoteTarget, resolveAndBindDesktopBridgeRouteContext, resolveCodexSessionIdentity, resolveDesktopBridgeTarget, safeBridgeErrorCode, singleBridgeHeader, threadRouteMove } from './security.js';
|
|
10
10
|
import { admitCodexLbTurnState, forgetCodexLbTurnStates, rememberCodexLbTurnState } from './codex-lb-turn-state.js';
|
|
11
11
|
import { ProviderHandoffRefusedError, assertProviderHandoffAllowed, providerHandoffRefusalBody, stripProviderBoundItems, } from './provider-handoff.js';
|
|
12
12
|
import { desktopBridgeListenOrigin } from './state.js';
|
|
13
|
+
import { adaptRequestForRoute, logRequestAdaptation, providerExtraInclude } from './provider-request-compat.js';
|
|
14
|
+
import { desktopBridgeModelsEtag } from './models-endpoint.js';
|
|
13
15
|
import { buildCompactionSummaryRequest, compactEndpointResult, compactionCreatedEvent, compactionFailedEvent, compactionResponseEvents, expandBridgeCompactionItems, newCompactionResponseId, parseProviderSummary, requestsRemoteCompaction, safeUpstreamErrorId, sseEvent, summaryFailure, summaryWithinBounds, } from './compaction-adapter.js';
|
|
14
16
|
import { MODEL_CAPACITY_ERROR_CODE, MODEL_CAPACITY_RETRY_AFTER_SECONDS, isAdvisableEventStream, modelCapacityAdvisingSse } from './capacity-retry-advice.js';
|
|
15
17
|
import { DEFAULT_DESKTOP_BRIDGE_MAX_REQUEST_BODY_BYTES, DesktopBridgeError, DesktopBridgeRequestBodyTooLargeError, isDesktopBridgeRoutingConflict } from './types.js';
|
|
@@ -69,6 +71,12 @@ function modelBodyKind(req, pathname) {
|
|
|
69
71
|
return req.method === 'POST' ? 'responses' : null;
|
|
70
72
|
return req.method === 'POST' && RESPONSES_PATHS.some((prefix) => pathname.startsWith(`${prefix}/`)) ? 'sub-endpoint' : null;
|
|
71
73
|
}
|
|
74
|
+
function advertisedModelsEtag(req, config) {
|
|
75
|
+
if (!config.modelsCatalog)
|
|
76
|
+
return null;
|
|
77
|
+
const pathname = new URL(String(req.url || '/'), 'http://bridge.invalid').pathname;
|
|
78
|
+
return modelBodyKind(req, pathname) === 'responses' ? desktopBridgeModelsEtag(config.modelsCatalog.generation) : null;
|
|
79
|
+
}
|
|
72
80
|
function isCompactEndpoint(req, pathname) {
|
|
73
81
|
return req.method === 'POST' && RESPONSES_PATHS.some((prefix) => pathname === `${prefix}/compact`);
|
|
74
82
|
}
|
|
@@ -155,6 +163,11 @@ export async function prepareDesktopBridgeRequest(req, config) {
|
|
|
155
163
|
rewrite = true;
|
|
156
164
|
if (route.session_pin?.handoff_from && stripProviderBoundItems(payload, Boolean(move)))
|
|
157
165
|
rewrite = true;
|
|
166
|
+
const adapted = adaptRequestForRoute(payload, route, providerExtraInclude(config, route));
|
|
167
|
+
if (adapted.length) {
|
|
168
|
+
rewrite = true;
|
|
169
|
+
logRequestAdaptation(config, route, adapted, 'http');
|
|
170
|
+
}
|
|
158
171
|
if (payload && rewrite)
|
|
159
172
|
body = Buffer.from(JSON.stringify(payload));
|
|
160
173
|
const compaction = bodyKind === 'responses' && requestsRemoteCompaction(payload)
|
|
@@ -260,6 +273,7 @@ async function readRedactedUpstreamError(response) {
|
|
|
260
273
|
}
|
|
261
274
|
let upstreamCode = null;
|
|
262
275
|
let upstreamType = null;
|
|
276
|
+
let upstreamParam = null;
|
|
263
277
|
const raw = Buffer.concat(chunks);
|
|
264
278
|
const encoding = String(response.headers['content-encoding'] || '').trim().toLowerCase();
|
|
265
279
|
let decoded = complete ? raw : null;
|
|
@@ -276,12 +290,14 @@ async function readRedactedUpstreamError(response) {
|
|
|
276
290
|
const parsed = JSON.parse(text);
|
|
277
291
|
upstreamCode = safeUpstreamErrorId(parsed?.error?.code) ?? safeUpstreamErrorId(parsed?.detail);
|
|
278
292
|
upstreamType = safeUpstreamErrorId(parsed?.error?.type);
|
|
293
|
+
upstreamParam = safeUpstreamParam(parsed?.error?.param);
|
|
279
294
|
}
|
|
280
295
|
catch {
|
|
281
296
|
}
|
|
282
297
|
return {
|
|
283
298
|
upstreamCode,
|
|
284
299
|
upstreamType,
|
|
300
|
+
upstreamParam,
|
|
285
301
|
unreadableBody: UNREADABLE_BODY_HINT.test(text.slice(0, 4096)),
|
|
286
302
|
body: decoded,
|
|
287
303
|
};
|
|
@@ -400,6 +416,7 @@ export async function forwardHttp(req, res, config, prepared, authenticatedLocal
|
|
|
400
416
|
delete headers['content-encoding'];
|
|
401
417
|
const agent = upstreamAgent(remote.secure, `${remote.address}:${remote.port}`, config.idleTimeoutMs);
|
|
402
418
|
const idleTimeoutMs = request.compaction ? Math.max(config.idleTimeoutMs, NATIVE_COMPACTION_IDLE_FLOOR_MS) : config.idleTimeoutMs;
|
|
419
|
+
const modelsEtag = advertisedModelsEtag(req, config);
|
|
403
420
|
const replayable = Buffer.isBuffer(request.body);
|
|
404
421
|
let body = request.body;
|
|
405
422
|
let plainFallbackActive = false;
|
|
@@ -468,7 +485,10 @@ export async function forwardHttp(req, res, config, prepared, authenticatedLocal
|
|
|
468
485
|
}
|
|
469
486
|
responseStarted = true;
|
|
470
487
|
try {
|
|
471
|
-
|
|
488
|
+
const responseHeaders = rewriteResponseHeaders(response.headers, upstreamBaseUrl, authenticatedLocalBaseUrl);
|
|
489
|
+
if (modelsEtag)
|
|
490
|
+
responseHeaders['x-models-etag'] = modelsEtag;
|
|
491
|
+
res.writeHead(statusCode, responseHeaders);
|
|
472
492
|
}
|
|
473
493
|
catch (error) {
|
|
474
494
|
response.destroy(error instanceof Error ? error : undefined);
|
|
@@ -479,7 +499,7 @@ export async function forwardHttp(req, res, config, prepared, authenticatedLocal
|
|
|
479
499
|
return;
|
|
480
500
|
}
|
|
481
501
|
if (statusCode >= 400) {
|
|
482
|
-
void readRedactedUpstreamError(response).then(({ upstreamCode, upstreamType, unreadableBody, body: upstreamBody }) => {
|
|
502
|
+
void readRedactedUpstreamError(response).then(({ upstreamCode, upstreamType, upstreamParam, unreadableBody, body: upstreamBody }) => {
|
|
483
503
|
if (budget.plainFallback && (statusCode === 415
|
|
484
504
|
|| (unreadableBody && !upstreamType && !upstreamCode && (statusCode === 400 || statusCode === 422)))) {
|
|
485
505
|
responseStarted = true;
|
|
@@ -499,16 +519,26 @@ export async function forwardHttp(req, res, config, prepared, authenticatedLocal
|
|
|
499
519
|
const body = verbatim
|
|
500
520
|
? scrubUpstreamErrorBody(upstreamBody, request.credential.value)
|
|
501
521
|
: buildRedactedUpstreamErrorBody(statusCode, upstreamCode, upstreamType, transientMislabel);
|
|
522
|
+
const rejected = statusCode >= 400 && statusCode <= 499;
|
|
502
523
|
logHttpRejection({
|
|
503
|
-
code:
|
|
504
|
-
?
|
|
505
|
-
:
|
|
524
|
+
code: rejected
|
|
525
|
+
? 'bridge_upstream_request_rejected'
|
|
526
|
+
: transientMislabel && !verbatim
|
|
527
|
+
? `bridge_upstream_status_${statusCode}_translated_503`
|
|
528
|
+
: upstreamCode ? `bridge_upstream_status_${statusCode}:${upstreamCode}` : `bridge_upstream_status_${statusCode}`,
|
|
506
529
|
transport: 'http',
|
|
507
530
|
...(req.method === undefined ? {} : { method: req.method }),
|
|
508
531
|
...(req.url === undefined ? {} : { url: req.url }),
|
|
509
532
|
status: clientStatus,
|
|
510
533
|
provider_id: provider.provider_id,
|
|
511
534
|
public_model: request.route.public_model,
|
|
535
|
+
...(rejected ? {
|
|
536
|
+
upstream_status: statusCode,
|
|
537
|
+
upstream_code: upstreamCode,
|
|
538
|
+
upstream_type: upstreamType,
|
|
539
|
+
upstream_param: upstreamParam,
|
|
540
|
+
request_shape: desktopBridgeRequestShape(request.payload),
|
|
541
|
+
} : {}),
|
|
512
542
|
});
|
|
513
543
|
responseStarted = true;
|
|
514
544
|
const responseHeaders = rewriteResponseHeaders(response.headers, upstreamBaseUrl, authenticatedLocalBaseUrl);
|
|
@@ -518,6 +548,8 @@ export async function forwardHttp(req, res, config, prepared, authenticatedLocal
|
|
|
518
548
|
if (verbatim && upstreamCode === MODEL_CAPACITY_ERROR_CODE && responseHeaders['retry-after'] === undefined) {
|
|
519
549
|
responseHeaders['retry-after'] = String(MODEL_CAPACITY_RETRY_AFTER_SECONDS);
|
|
520
550
|
}
|
|
551
|
+
if (modelsEtag)
|
|
552
|
+
responseHeaders['x-models-etag'] = modelsEtag;
|
|
521
553
|
res.writeHead(clientStatus, responseHeaders);
|
|
522
554
|
res.end(body, () => finish());
|
|
523
555
|
}).catch(finish);
|
|
@@ -536,6 +568,8 @@ export async function forwardHttp(req, res, config, prepared, authenticatedLocal
|
|
|
536
568
|
const responseHeaders = rewriteResponseHeaders(response.headers, upstreamBaseUrl, authenticatedLocalBaseUrl);
|
|
537
569
|
if (advising)
|
|
538
570
|
delete responseHeaders['content-length'];
|
|
571
|
+
if (modelsEtag)
|
|
572
|
+
responseHeaders['x-models-etag'] = modelsEtag;
|
|
539
573
|
res.writeHead(statusCode, responseHeaders);
|
|
540
574
|
}
|
|
541
575
|
catch (error) {
|
|
@@ -715,12 +749,18 @@ async function answerCompactionWithProviderSummary(req, res, config, input) {
|
|
|
715
749
|
const streaming = input.kind === 'trigger';
|
|
716
750
|
const responseId = newCompactionResponseId();
|
|
717
751
|
const publicModel = input.route.public_model;
|
|
718
|
-
const
|
|
752
|
+
const summaryRequest = buildCompactionSummaryRequest(input.payload, input.route.upstream_model);
|
|
753
|
+
adaptRequestForRoute(summaryRequest, input.route, providerExtraInclude(config, input.route));
|
|
754
|
+
const body = Buffer.from(JSON.stringify(summaryRequest));
|
|
719
755
|
let keepalive = null;
|
|
720
756
|
const startStream = () => {
|
|
721
757
|
if (!streaming || res.headersSent)
|
|
722
758
|
return;
|
|
723
|
-
|
|
759
|
+
const modelsEtag = advertisedModelsEtag(req, config);
|
|
760
|
+
res.writeHead(200, {
|
|
761
|
+
'content-type': 'text/event-stream', 'cache-control': 'no-store', connection: 'keep-alive',
|
|
762
|
+
...(modelsEtag ? { 'x-models-etag': modelsEtag } : {}),
|
|
763
|
+
});
|
|
724
764
|
res.write(sseEvent(compactionCreatedEvent(responseId, publicModel)));
|
|
725
765
|
keepalive = setInterval(() => { if (!res.writableEnded)
|
|
726
766
|
res.write(': keepalive\n\n'); }, COMPACTION_KEEPALIVE_MS);
|