sneakoscope 4.7.1 → 4.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 +5 -5
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/bin/sks.js +1 -1
- package/dist/cli/install-helpers.js +215 -57
- package/dist/commands/codex-lb.js +143 -7
- package/dist/commands/doctor.js +9 -0
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +11 -1
- package/dist/core/codex-app/codex-app-restart.js +65 -0
- package/dist/core/codex-app.js +235 -3
- package/dist/core/codex-control/codex-sdk-config-policy.js +2 -2
- package/dist/core/codex-control/codex-task-runner.js +2 -2
- package/dist/core/codex-lb/codex-lb-setup.js +1 -1
- package/dist/core/fsx.js +1 -1
- package/dist/core/imagegen/imagegen-capability.js +2 -2
- package/dist/core/init.js +1 -1
- package/dist/core/managed-assets/managed-assets-manifest.js +1 -1
- package/dist/core/provider/provider-context.js +1 -1
- package/dist/core/routes.js +1 -1
- package/dist/core/version.js +1 -1
- package/dist/scripts/codex-app-provider-model-ui-check.js +114 -0
- package/dist/scripts/codex-lb-fast-mode-truth-check.js +90 -0
- package/dist/scripts/codex-lb-setup-truthfulness-check.js +6 -1
- package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +5 -1
- package/dist/scripts/gpt-image-2-real-file-smoke.js +2 -2
- package/dist/scripts/provider-context-config-toml-check.js +2 -2
- package/package.json +3 -1
|
@@ -12,12 +12,16 @@ import { initProject, installSkills } from '../core/init.js';
|
|
|
12
12
|
import { context7ConfigToml, DOLLAR_SKILL_NAMES, GETDESIGN_REFERENCE, hasContext7ConfigText, RECOMMENDED_SKILLS } from '../core/routes.js';
|
|
13
13
|
import { checkZellijCapability } from '../core/zellij/zellij-capability.js';
|
|
14
14
|
import { reconcileCodexAppUpgradeProcesses } from '../core/codex-app.js';
|
|
15
|
+
import { restartCodexApp } from '../core/codex-app/codex-app-restart.js';
|
|
15
16
|
import { recordCodexLbHealthEvent } from '../core/codex-lb-circuit.js';
|
|
16
17
|
import { loadCodexLbEnv, writeCodexLbKeychain, codexLbMetadataPath } from '../core/codex-lb/codex-lb-env.js';
|
|
17
18
|
import { GLM_CODEX_CONFIG_PROFILE_ID, GLM_CODEX_CONFIG_PROVIDER_ID, GLM_CODEX_CONFIG_REASONING_PROFILES } from '../core/providers/glm/glm-52-profile.js';
|
|
18
19
|
import { GLM_52_OPENROUTER_MODEL } from '../core/providers/glm/glm-52-settings.js';
|
|
19
20
|
import { buildCodexLbSetupPlan, codexLbPersistenceSummary, installCodexLbShellProfileSnippet, selectedCodexLbPersistenceModes } from '../core/codex-lb/codex-lb-setup.js';
|
|
20
21
|
import { runPostinstallGlobalDoctorAndMarkPending } from '../core/update/update-migration-state.js';
|
|
22
|
+
const CODEX_LB_PROVIDER_NAME = 'openai';
|
|
23
|
+
const CODEX_LB_PROVIDER_ENV_KEY = 'CODEX_LB_API_KEY';
|
|
24
|
+
const CODEX_LB_CANONICAL_FAST_SERVICE_TIER = 'priority';
|
|
21
25
|
const DEFAULT_CODEX_APP_PLUGINS = [
|
|
22
26
|
['browser', 'openai-bundled'],
|
|
23
27
|
['chrome', 'openai-bundled'],
|
|
@@ -207,7 +211,7 @@ async function reportPostinstallCodexLbAuth() {
|
|
|
207
211
|
console.log(`codex-lb auth: repair skipped (${codexLbAuth.status}${codexLbAuth.error ? `: ${codexLbAuth.error}` : ''}).`);
|
|
208
212
|
const reconcile = codexLbAuth.auth_reconcile;
|
|
209
213
|
if (reconcile?.status === 'oauth_preserved') {
|
|
210
|
-
console.log(`codex-lb auth: ChatGPT OAuth preserved as backup; codex-lb
|
|
214
|
+
console.log(`codex-lb auth: ChatGPT OAuth preserved as backup; run \`sks codex-lb use-codex-lb\` to switch the App to codex-lb API-key auth (backup at ${reconcile.backup_path ?? 'unknown'}).`);
|
|
211
215
|
}
|
|
212
216
|
else if (reconcile?.status === 'oauth_restored') {
|
|
213
217
|
console.log(`codex-lb auth: restored ChatGPT OAuth from ${reconcile.backup_path ?? 'unknown'} while keeping codex-lb selected.`);
|
|
@@ -428,7 +432,9 @@ export async function configureCodexLb(opts = {}) {
|
|
|
428
432
|
const appliedActions = [];
|
|
429
433
|
await ensureDir(path.dirname(configPath));
|
|
430
434
|
const current = await readText(configPath, '');
|
|
431
|
-
const next = normalizeCodexFastModeUiConfig(upsertCodexLbConfig(current, baseUrl, useDefaultProvider)
|
|
435
|
+
const next = normalizeCodexFastModeUiConfig(upsertCodexLbConfig(current, baseUrl, useDefaultProvider), {
|
|
436
|
+
forceFastMode: opts.forceFastMode !== false
|
|
437
|
+
});
|
|
432
438
|
const safeWrite = await safeWriteCodexConfigToml(configPath, current, next, 'codex-lb');
|
|
433
439
|
if (!safeWrite.ok)
|
|
434
440
|
return { ok: false, status: safeWrite.status, config_path: configPath, env_path: envPath, backup_path: safeWrite.backup_path };
|
|
@@ -462,9 +468,12 @@ export async function configureCodexLb(opts = {}) {
|
|
|
462
468
|
const shellProfileResult = await installCodexLbShellProfileSnippet({ home, envPath, shellProfile }).catch((err) => ({ ok: false, status: 'failed', files: [], error: err.message }));
|
|
463
469
|
if (shellProfile !== 'skip')
|
|
464
470
|
appliedActions.push({ type: 'install_shell_profile_snippet', target: shellProfileResult.files?.join(', ') || shellProfile, ok: shellProfileResult.ok === true, status: shellProfileResult.status });
|
|
465
|
-
const codexLogin = await maybeSyncCodexLbSharedLogin(apiKey, { ...opts, home, force: true });
|
|
466
471
|
const codexLb = await codexLbStatus({ ...opts, home, configPath, envPath });
|
|
467
|
-
const
|
|
472
|
+
const forceCodexLbApiKeyAuth = opts.forceCodexLbApiKeyAuth === true || opts.authMode === 'codex-lb';
|
|
473
|
+
const authReconcile = await reconcileCodexLbAuthConflict({ ...opts, home, status: codexLb, forceCodexLbApiKeyAuth }).catch((err) => ({ status: 'failed', reason: 'exception', error: err.message }));
|
|
474
|
+
const codexLogin = forceCodexLbApiKeyAuth
|
|
475
|
+
? { ok: ['apikey_forced', 'apikey_auth_active'].includes(authReconcile.status), status: authReconcile.status, ...(authReconcile.reason ? { reason: authReconcile.reason } : {}), error: authReconcile.error || null }
|
|
476
|
+
: await maybeSyncCodexLbSharedLogin(apiKey, { ...opts, home, force: true });
|
|
468
477
|
const finalCodexLb = await codexLbStatus({ ...opts, home, configPath, envPath });
|
|
469
478
|
const ok = Boolean(codexEnvironment.ok && codexLogin.ok);
|
|
470
479
|
const afterState = await captureCodexLbSetupWriteState({ home, configPath, envPath, shellProfile });
|
|
@@ -535,11 +544,21 @@ export async function codexLbStatus(opts = {}) {
|
|
|
535
544
|
const providerConfigured = /\[model_providers\.codex-lb\]/.test(config);
|
|
536
545
|
const selected = hasTopLevelCodexLbSelected(config);
|
|
537
546
|
const baseUrl = codexLbProviderBaseUrl(config) || envLoad.base_url || null;
|
|
547
|
+
const providerName = codexLbProviderName(config);
|
|
548
|
+
const providerWireApi = codexLbProviderWireApi(config);
|
|
549
|
+
const providerSupportsWebsockets = codexLbProviderSupportsWebsockets(config);
|
|
538
550
|
const providerRequiresOpenAiAuth = codexLbProviderRequiresOpenAiAuth(config);
|
|
539
551
|
const providerOpenAiAuthDisabled = codexLbProviderOpenAiAuthDisabled(config);
|
|
540
552
|
const providerEnvKey = codexLbProviderEnvKey(config);
|
|
541
|
-
const
|
|
542
|
-
|
|
553
|
+
const providerContractOk = providerConfigured
|
|
554
|
+
&& providerName === CODEX_LB_PROVIDER_NAME
|
|
555
|
+
&& providerWireApi === 'responses'
|
|
556
|
+
&& providerEnvKey === CODEX_LB_PROVIDER_ENV_KEY
|
|
557
|
+
&& providerSupportsWebsockets === true
|
|
558
|
+
&& providerRequiresOpenAiAuth === true;
|
|
559
|
+
const providerUsesCodexLbEnvAuth = providerConfigured && providerEnvKey === CODEX_LB_PROVIDER_ENV_KEY && providerOpenAiAuthDisabled;
|
|
560
|
+
const codexAppUsableWithCodexLb = providerContractOk && envKeyConfigured && Boolean(baseUrl) && authMode.codex_app_usable;
|
|
561
|
+
const fastMode = codexLbFastModeConfigStatus(config);
|
|
543
562
|
const launchEnvironment = await inspectCodexLbMacLaunchEnvironment(baseUrl, opts).catch((err) => ({
|
|
544
563
|
checked: true,
|
|
545
564
|
available: false,
|
|
@@ -547,10 +566,14 @@ export async function codexLbStatus(opts = {}) {
|
|
|
547
566
|
error: err.message
|
|
548
567
|
}));
|
|
549
568
|
return {
|
|
550
|
-
ok:
|
|
569
|
+
ok: providerContractOk && envKeyConfigured && Boolean(baseUrl) && authMode.codex_app_usable,
|
|
551
570
|
config_path: configPath,
|
|
552
571
|
env_path: envPath,
|
|
553
572
|
provider_configured: providerConfigured,
|
|
573
|
+
provider_name: providerName || null,
|
|
574
|
+
provider_wire_api: providerWireApi || null,
|
|
575
|
+
provider_supports_websockets: providerSupportsWebsockets,
|
|
576
|
+
provider_contract_ok: providerContractOk,
|
|
554
577
|
provider_requires_openai_auth: providerRequiresOpenAiAuth,
|
|
555
578
|
provider_openai_auth_disabled: providerOpenAiAuthDisabled,
|
|
556
579
|
provider_env_key: providerEnvKey || null,
|
|
@@ -572,7 +595,8 @@ export async function codexLbStatus(opts = {}) {
|
|
|
572
595
|
auth_path: authPath,
|
|
573
596
|
auth_mode: authMode.mode,
|
|
574
597
|
auth_usable_for_codex_app: authMode.codex_app_usable || codexAppUsableWithCodexLb,
|
|
575
|
-
auth_summary: codexAppUsableWithCodexLb ?
|
|
598
|
+
auth_summary: codexAppUsableWithCodexLb ? `codex-lb provider uses ${authMode.mode} OpenAI-style auth through Codex App` : authMode.summary,
|
|
599
|
+
fast_mode: fastMode,
|
|
576
600
|
launch_environment: launchEnvironment
|
|
577
601
|
};
|
|
578
602
|
}
|
|
@@ -584,20 +608,28 @@ export function formatCodexLbStatusText(status = {}, opts = {}) {
|
|
|
584
608
|
'',
|
|
585
609
|
`Configured: ${status.ok ? 'yes' : 'no'}`,
|
|
586
610
|
`Selected: ${status.selected ? 'yes' : 'no'}`,
|
|
587
|
-
`Provider: ${status.provider_configured ? '
|
|
588
|
-
`Provider OpenAI
|
|
611
|
+
`Provider: ${status.provider_contract_ok ? 'codex-lb App contract ok' : status.provider_configured ? 'drifted' : 'missing'}`,
|
|
612
|
+
`Provider OpenAI Auth: ${status.provider_requires_openai_auth ? 'required' : 'not required/drifted'} (${status.provider_name || 'missing'})`,
|
|
589
613
|
`Codex App auth: ${status.auth_usable_for_codex_app ? 'ok' : 'needs sign-in/repair'} (${status.auth_mode || 'unknown'})`
|
|
590
614
|
];
|
|
591
615
|
if (status.auth_summary)
|
|
592
616
|
lines.push(`Auth detail: ${status.auth_summary}`);
|
|
617
|
+
if (status.fast_mode) {
|
|
618
|
+
const fast = status.fast_mode;
|
|
619
|
+
lines.push(`Fast Mode: ${fast.configured ? `configured request=${fast.codex_request_service_tier} upstream=${fast.codex_lb_upstream_service_tier}` : 'not configured'}`);
|
|
620
|
+
if (!fast.actual_service_tier_verified)
|
|
621
|
+
lines.push(`Fast proof: unverified until ${fast.proof_required}. Run: ${fast.verification_command}`);
|
|
622
|
+
}
|
|
593
623
|
lines.push(`Env file: ${status.env_file ? status.env_path : 'missing'}`);
|
|
594
624
|
if (status.base_url)
|
|
595
625
|
lines.push(`Base URL: ${status.base_url}`);
|
|
596
626
|
lines.push(`ChatGPT backup: ${backupPresent ? `yes (${backupPath})` : 'no'}`);
|
|
597
|
-
if (status.
|
|
598
|
-
lines.push('', 'Run: sks codex-lb repair to
|
|
627
|
+
if (status.provider_configured && !status.provider_contract_ok)
|
|
628
|
+
lines.push('', 'Run: sks codex-lb repair to rewrite the provider block to the current codex-lb App contract.');
|
|
629
|
+
else if (status.ok && !status.auth_usable_for_codex_app && backupPresent)
|
|
630
|
+
lines.push('', 'Run: sks codex-lb use-oauth to restore ChatGPT OAuth, or sks codex-lb use-codex-lb to force codex-lb API-key auth.');
|
|
599
631
|
else if (status.ok && !status.auth_usable_for_codex_app)
|
|
600
|
-
lines.push('', '
|
|
632
|
+
lines.push('', 'Run: sks codex-lb use-codex-lb, or sign in to Codex App/CLI again for ChatGPT OAuth.');
|
|
601
633
|
else if (status.ok && !status.selected)
|
|
602
634
|
lines.push('', 'Run: sks codex-lb repair to activate codex-lb for Codex App.');
|
|
603
635
|
else if (status.ok)
|
|
@@ -619,9 +651,9 @@ export function formatCodexLbRepairResultText(result = {}) {
|
|
|
619
651
|
if (result.auth_reconcile?.status === 'oauth_restored')
|
|
620
652
|
lines.push(`Codex App auth: ChatGPT OAuth restored from ${result.auth_reconcile.backup_path}.`);
|
|
621
653
|
else if (result.auth_reconcile?.status === 'oauth_preserved')
|
|
622
|
-
lines.push('Codex App auth: ChatGPT OAuth preserved as backup; codex-lb
|
|
623
|
-
else if (result.auth_reconcile?.status
|
|
624
|
-
lines.push('Codex App auth: API-key auth.json is
|
|
654
|
+
lines.push('Codex App auth: ChatGPT OAuth preserved as backup; run `sks codex-lb use-codex-lb` to force codex-lb API-key auth.');
|
|
655
|
+
else if (['apikey_auth_active', 'apikey_forced'].includes(result.auth_reconcile?.status))
|
|
656
|
+
lines.push('Codex App auth: codex-lb API-key auth.json is active.');
|
|
625
657
|
return `${lines.join('\n')}\n`;
|
|
626
658
|
}
|
|
627
659
|
function codexLbResponsesEndpoint(baseUrl = '') {
|
|
@@ -752,6 +784,45 @@ function codexLbResponseError(json, events = []) {
|
|
|
752
784
|
}
|
|
753
785
|
return null;
|
|
754
786
|
}
|
|
787
|
+
function codexLbServiceTierEvidence(...responses) {
|
|
788
|
+
const values = [];
|
|
789
|
+
const visit = (value) => {
|
|
790
|
+
if (!value || typeof value !== 'object')
|
|
791
|
+
return;
|
|
792
|
+
values.push(value);
|
|
793
|
+
if (Array.isArray(value)) {
|
|
794
|
+
for (const item of value)
|
|
795
|
+
visit(item);
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
for (const child of Object.values(value))
|
|
799
|
+
visit(child);
|
|
800
|
+
};
|
|
801
|
+
for (const response of responses) {
|
|
802
|
+
visit(response?.json);
|
|
803
|
+
visit(response?.events);
|
|
804
|
+
}
|
|
805
|
+
const firstString = (...keys) => {
|
|
806
|
+
for (const row of values) {
|
|
807
|
+
for (const key of keys) {
|
|
808
|
+
const value = row?.[key];
|
|
809
|
+
if (typeof value === 'string' && value.trim())
|
|
810
|
+
return value.trim().toLowerCase();
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
return null;
|
|
814
|
+
};
|
|
815
|
+
const requested = firstString('requestedServiceTier', 'requested_service_tier', 'requested_serviceTier');
|
|
816
|
+
const actual = firstString('actualServiceTier', 'actual_service_tier', 'actual_serviceTier');
|
|
817
|
+
const effective = firstString('serviceTier', 'service_tier');
|
|
818
|
+
return {
|
|
819
|
+
requested_service_tier: requested,
|
|
820
|
+
actual_service_tier: actual,
|
|
821
|
+
effective_service_tier: effective,
|
|
822
|
+
fast_requested: requested === CODEX_LB_CANONICAL_FAST_SERVICE_TIER || effective === CODEX_LB_CANONICAL_FAST_SERVICE_TIER,
|
|
823
|
+
fast_actual: actual === CODEX_LB_CANONICAL_FAST_SERVICE_TIER || effective === CODEX_LB_CANONICAL_FAST_SERVICE_TIER
|
|
824
|
+
};
|
|
825
|
+
}
|
|
755
826
|
async function fetchCodexLbResponse(fetchImpl, endpoint, apiKey, body, timeoutMs) {
|
|
756
827
|
const controller = new AbortController();
|
|
757
828
|
const timer = setTimeout(() => controller.abort(), timeoutMs).unref?.();
|
|
@@ -802,10 +873,14 @@ export async function checkCodexLbResponseChain(status = {}, opts = {}) {
|
|
|
802
873
|
return { ok: true, status: 'skipped', skipped: true, reason: 'fetch unavailable' };
|
|
803
874
|
const model = opts.model || env.SKS_CODEX_MODEL || 'gpt-5.5';
|
|
804
875
|
const timeoutMs = Number(opts.timeoutMs || env.SKS_CODEX_LB_CHAIN_CHECK_TIMEOUT_MS || 8000);
|
|
876
|
+
const serviceTier = opts.fastMode === true || opts.serviceTier === 'fast' || opts.serviceTier === CODEX_LB_CANONICAL_FAST_SERVICE_TIER
|
|
877
|
+
? CODEX_LB_CANONICAL_FAST_SERVICE_TIER
|
|
878
|
+
: null;
|
|
805
879
|
const baseBody = {
|
|
806
880
|
model,
|
|
807
881
|
instructions: 'You are running a short SKS codex-lb response-chain health check.',
|
|
808
882
|
input: 'SKS codex-lb response-chain health check. Reply with OK.',
|
|
883
|
+
...(serviceTier ? { service_tier: serviceTier } : {}),
|
|
809
884
|
stream: true,
|
|
810
885
|
store: true,
|
|
811
886
|
parallel_tool_calls: false,
|
|
@@ -820,12 +895,14 @@ export async function checkCodexLbResponseChain(status = {}, opts = {}) {
|
|
|
820
895
|
chain_unhealthy: true,
|
|
821
896
|
endpoint,
|
|
822
897
|
http_status: first.status,
|
|
898
|
+
requested_service_tier: serviceTier,
|
|
899
|
+
service_tier_evidence: codexLbServiceTierEvidence(first),
|
|
823
900
|
error: redactSecretText(first.error_payload?.error?.message || first.error_payload?.response?.error?.message || first.text || 'codex-lb first Responses request failed', [apiKey])
|
|
824
901
|
}, { endpoint, home, opts, env }), opts);
|
|
825
902
|
}
|
|
826
903
|
const second = await fetchCodexLbResponse(fetchImpl, endpoint, apiKey, { ...baseBody, previous_response_id: first.response_id }, timeoutMs);
|
|
827
904
|
if (second.ok)
|
|
828
|
-
return recordCodexLbChainHealth(await writeCodexLbChainCache({ ok: true, status: 'chain_ok', endpoint, response_id: first.response_id, chained_response_id: second.response_id || null, http_status: second.status }, { endpoint, home, opts, env }), opts);
|
|
905
|
+
return recordCodexLbChainHealth(await writeCodexLbChainCache({ ok: true, status: 'chain_ok', endpoint, response_id: first.response_id, chained_response_id: second.response_id || null, http_status: second.status, requested_service_tier: serviceTier, service_tier_evidence: codexLbServiceTierEvidence(first, second) }, { endpoint, home, opts, env }), opts);
|
|
829
906
|
const previousMissing = isPreviousResponseNotFound(second.error_payload || second.json || second.text);
|
|
830
907
|
return recordCodexLbChainHealth(await writeCodexLbChainCache({
|
|
831
908
|
ok: false,
|
|
@@ -834,6 +911,8 @@ export async function checkCodexLbResponseChain(status = {}, opts = {}) {
|
|
|
834
911
|
endpoint,
|
|
835
912
|
response_id: first.response_id,
|
|
836
913
|
http_status: second.status,
|
|
914
|
+
requested_service_tier: serviceTier,
|
|
915
|
+
service_tier_evidence: codexLbServiceTierEvidence(first, second),
|
|
837
916
|
error: redactSecretText(second.error_payload?.error?.message || second.error_payload?.response?.error?.message || second.text || 'codex-lb chained Responses request failed', [apiKey])
|
|
838
917
|
}, { endpoint, home, opts, env }), opts);
|
|
839
918
|
}
|
|
@@ -851,6 +930,22 @@ function codexLbProviderBaseUrl(text = '') {
|
|
|
851
930
|
const block = String(text || '').match(/(^|\n)\[model_providers\.codex-lb\]([\s\S]*?)(?=\n\[[^\]]+\]|\s*$)/)?.[2] || '';
|
|
852
931
|
return block.match(/(^|\n)\s*base_url\s*=\s*"([^"]+)"/)?.[2] || '';
|
|
853
932
|
}
|
|
933
|
+
function codexLbProviderName(text = '') {
|
|
934
|
+
const block = String(text || '').match(/(^|\n)\[model_providers\.codex-lb\]([\s\S]*?)(?=\n\[[^\]]+\]|\s*$)/)?.[2] || '';
|
|
935
|
+
return (block.match(/(^|\n)\s*name\s*=\s*"([^"]+)"/)?.[2] || '').trim();
|
|
936
|
+
}
|
|
937
|
+
function codexLbProviderWireApi(text = '') {
|
|
938
|
+
const block = String(text || '').match(/(^|\n)\[model_providers\.codex-lb\]([\s\S]*?)(?=\n\[[^\]]+\]|\s*$)/)?.[2] || '';
|
|
939
|
+
return (block.match(/(^|\n)\s*wire_api\s*=\s*"([^"]+)"/)?.[2] || '').trim();
|
|
940
|
+
}
|
|
941
|
+
function codexLbProviderSupportsWebsockets(text = '') {
|
|
942
|
+
const block = String(text || '').match(/(^|\n)\[model_providers\.codex-lb\]([\s\S]*?)(?=\n\[[^\]]+\]|\s*$)/)?.[2] || '';
|
|
943
|
+
if (/(^|\n)\s*supports_websockets\s*=\s*true\s*(?:#.*)?(?=\n|$)/.test(block))
|
|
944
|
+
return true;
|
|
945
|
+
if (/(^|\n)\s*supports_websockets\s*=\s*false\s*(?:#.*)?(?=\n|$)/.test(block))
|
|
946
|
+
return false;
|
|
947
|
+
return null;
|
|
948
|
+
}
|
|
854
949
|
function codexLbProviderRequiresOpenAiAuth(text = '') {
|
|
855
950
|
const block = String(text || '').match(/(^|\n)\[model_providers\.codex-lb\]([\s\S]*?)(?=\n\[[^\]]+\]|\s*$)/)?.[2] || '';
|
|
856
951
|
return /(^|\n)\s*requires_openai_auth\s*=\s*true\s*(?:#.*)?(?=\n|$)/.test(block);
|
|
@@ -863,6 +958,27 @@ function codexLbProviderEnvKey(text = '') {
|
|
|
863
958
|
const block = String(text || '').match(/(^|\n)\[model_providers\.codex-lb\]([\s\S]*?)(?=\n\[[^\]]+\]|\s*$)/)?.[2] || '';
|
|
864
959
|
return block.match(/(^|\n)\s*env_key\s*=\s*"([^"]+)"/)?.[2] || '';
|
|
865
960
|
}
|
|
961
|
+
function codexLbFastModeConfigStatus(text = '') {
|
|
962
|
+
const globalServiceTier = topLevelTomlString(text, 'service_tier');
|
|
963
|
+
const profileBlock = String(text || '').match(/(^|\n)\[profiles\.sks-fast-high\]([\s\S]*?)(?=\n\[[^\]]+\]|\s*$)/)?.[2] || '';
|
|
964
|
+
const profileServiceTier = profileBlock.match(/(^|\n)\s*service_tier\s*=\s*"([^"]+)"/)?.[2] || '';
|
|
965
|
+
const configured = globalServiceTier === 'fast' || globalServiceTier === CODEX_LB_CANONICAL_FAST_SERVICE_TIER || profileServiceTier === 'fast';
|
|
966
|
+
return {
|
|
967
|
+
schema: 'sks.codex-lb-fast-mode-config.v1',
|
|
968
|
+
configured,
|
|
969
|
+
top_level_service_tier: globalServiceTier || null,
|
|
970
|
+
profile_service_tier: profileServiceTier || null,
|
|
971
|
+
codex_request_service_tier: configured ? 'fast' : null,
|
|
972
|
+
codex_lb_upstream_service_tier: configured ? CODEX_LB_CANONICAL_FAST_SERVICE_TIER : null,
|
|
973
|
+
actual_service_tier_verified: false,
|
|
974
|
+
verification_command: 'sks codex-lb fast-check --json',
|
|
975
|
+
proof_required: 'codex-lb request log must show requestedServiceTier=priority and actualServiceTier/serviceTier=priority'
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
function topLevelTomlString(text = '', key) {
|
|
979
|
+
const topLevel = String(text || '').split(/\n\s*\[/)[0] || '';
|
|
980
|
+
return topLevel.match(new RegExp(`(^|\\n)\\s*${escapeRegExp(key)}\\s*=\\s*"([^"]+)"\\s*(?:#.*)?(?=\\n|$)`))?.[2] || '';
|
|
981
|
+
}
|
|
866
982
|
export async function repairCodexLbAuth(opts = {}) {
|
|
867
983
|
let status = await codexLbStatus(opts);
|
|
868
984
|
let configRepaired = false;
|
|
@@ -877,14 +993,17 @@ export async function repairCodexLbAuth(opts = {}) {
|
|
|
877
993
|
status = await codexLbStatus(opts);
|
|
878
994
|
}
|
|
879
995
|
}
|
|
880
|
-
if (status.env_key_configured && status.base_url && (!status.
|
|
996
|
+
if (status.env_key_configured && status.base_url && (!status.provider_contract_ok || !status.selected || legacyAuthMigrated || hasTopLevelCodexModeLock(currentConfig) || (opts.forceCodexLbApiKeyAuth === true && !status.ok))) {
|
|
881
997
|
await ensureDir(path.dirname(status.config_path));
|
|
882
|
-
const next = normalizeCodexFastModeUiConfig(upsertCodexLbConfig(currentConfig, status.base_url)
|
|
998
|
+
const next = normalizeCodexFastModeUiConfig(upsertCodexLbConfig(currentConfig, status.base_url), {
|
|
999
|
+
forceFastMode: opts.forceFastMode === true || opts.forceCodexLbApiKeyAuth === true
|
|
1000
|
+
});
|
|
883
1001
|
const safeWrite = await safeWriteCodexConfigToml(status.config_path, currentConfig, next, 'codex-lb-repair');
|
|
884
1002
|
configRepaired = safeWrite.ok && safeWrite.changed === true;
|
|
885
1003
|
status = await codexLbStatus(opts);
|
|
886
1004
|
}
|
|
887
|
-
|
|
1005
|
+
const canRepairAuthMode = opts.forceCodexLbApiKeyAuth === true && status.provider_contract_ok && status.env_key_configured && Boolean(status.base_url);
|
|
1006
|
+
if (!status.ok && !canRepairAuthMode) {
|
|
888
1007
|
return {
|
|
889
1008
|
ok: false,
|
|
890
1009
|
status: !status.env_key_configured ? 'missing_env_key' : !status.base_url ? 'missing_base_url' : 'not_configured',
|
|
@@ -896,8 +1015,11 @@ export async function repairCodexLbAuth(opts = {}) {
|
|
|
896
1015
|
await migrateCodexAuthKeyFormat({ home: opts.home });
|
|
897
1016
|
const codexEnvironment = await syncCodexLbProviderEnvironment(status, opts);
|
|
898
1017
|
const apiKey = parseCodexLbEnvKey(await readText(status.env_path, ''));
|
|
899
|
-
const
|
|
900
|
-
const authReconcile = await reconcileCodexLbAuthConflict({ ...opts, status }).catch((err) => ({ status: 'failed', reason: 'exception', error: err.message }));
|
|
1018
|
+
const forceCodexLbApiKeyAuth = opts.forceCodexLbApiKeyAuth === true || opts.authMode === 'codex-lb';
|
|
1019
|
+
const authReconcile = await reconcileCodexLbAuthConflict({ ...opts, status, forceCodexLbApiKeyAuth }).catch((err) => ({ status: 'failed', reason: 'exception', error: err.message }));
|
|
1020
|
+
const codexLogin = forceCodexLbApiKeyAuth
|
|
1021
|
+
? { ok: ['apikey_forced', 'apikey_auth_active'].includes(authReconcile.status), status: authReconcile.status, ...(authReconcile.reason ? { reason: authReconcile.reason } : {}), error: authReconcile.error || null }
|
|
1022
|
+
: await maybeSyncCodexLbSharedLogin(apiKey, { ...opts, home: opts.home || process.env.HOME || os.homedir(), force: true });
|
|
901
1023
|
const finalStatus = await codexLbStatus(opts);
|
|
902
1024
|
const ok = Boolean(codexEnvironment.ok && codexLogin.ok);
|
|
903
1025
|
return {
|
|
@@ -933,7 +1055,7 @@ export async function ensureCodexLbAuthDuringInstall(opts = {}) {
|
|
|
933
1055
|
};
|
|
934
1056
|
}
|
|
935
1057
|
await migrateCodexAuthKeyFormat({ home: opts.home });
|
|
936
|
-
if (status.ok && (!status.selected || !status.
|
|
1058
|
+
if (status.ok && (!status.selected || !status.provider_contract_ok))
|
|
937
1059
|
return repairCodexLbAuth(opts);
|
|
938
1060
|
if (!status.ok) {
|
|
939
1061
|
if (status.base_url && (status.env_key_configured || status.provider_configured || status.selected || status.env_base_url_configured))
|
|
@@ -1017,11 +1139,11 @@ function codexAuthModeSummary(text = '') {
|
|
|
1017
1139
|
return { mode: 'chatgpt_oauth', codex_app_usable: true, summary: 'ChatGPT OAuth token blob present' };
|
|
1018
1140
|
const apiKey = parseCodexAuthApiKey(raw);
|
|
1019
1141
|
if (apiKey)
|
|
1020
|
-
return { mode: 'apikey', codex_app_usable:
|
|
1142
|
+
return { mode: 'apikey', codex_app_usable: true, summary: 'API-key auth.json available for requires_openai_auth providers' };
|
|
1021
1143
|
try {
|
|
1022
1144
|
const parsed = JSON.parse(raw);
|
|
1023
1145
|
if (parsed?.auth_mode === 'browser')
|
|
1024
|
-
return { mode: 'browser_marker', codex_app_usable:
|
|
1146
|
+
return { mode: 'browser_marker', codex_app_usable: true, summary: 'browser auth marker present; token storage is not inspectable' };
|
|
1025
1147
|
}
|
|
1026
1148
|
catch { }
|
|
1027
1149
|
return { mode: 'unknown', codex_app_usable: false, summary: 'unrecognized auth.json shape' };
|
|
@@ -1052,9 +1174,11 @@ async function migrateCodexAuthKeyFormat(opts = {}) {
|
|
|
1052
1174
|
return { status: 'skipped', reason: 'parse_error' };
|
|
1053
1175
|
}
|
|
1054
1176
|
}
|
|
1055
|
-
// codex-lb
|
|
1056
|
-
//
|
|
1057
|
-
//
|
|
1177
|
+
// codex-lb's current Codex App contract uses a custom provider named "openai"
|
|
1178
|
+
// with requires_openai_auth=true. CODEX_LB_API_KEY remains SKS's persisted key
|
|
1179
|
+
// source, but selecting codex-lb auth must also switch Codex's OpenAI-style
|
|
1180
|
+
// auth.json to that API key; otherwise the App can appear configured while still
|
|
1181
|
+
// running under a different ChatGPT/OAuth auth path.
|
|
1058
1182
|
export async function reconcileCodexLbAuthConflict(opts = {}) {
|
|
1059
1183
|
const home = opts.home || process.env.HOME || os.homedir();
|
|
1060
1184
|
const status = opts.status || await codexLbStatus({ ...opts, home });
|
|
@@ -1063,18 +1187,39 @@ export async function reconcileCodexLbAuthConflict(opts = {}) {
|
|
|
1063
1187
|
if (!status.env_key_configured || !status.base_url) {
|
|
1064
1188
|
return { status: 'skipped', reason: 'codex_lb_not_ready', auth_path: authPath };
|
|
1065
1189
|
}
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
}
|
|
1069
|
-
const authText = await readText(authPath, '');
|
|
1070
|
-
if (!authText.trim()) {
|
|
1071
|
-
return { status: 'skipped', reason: 'auth_empty', auth_path: authPath };
|
|
1072
|
-
}
|
|
1190
|
+
const authExists = await exists(authPath);
|
|
1191
|
+
const authText = authExists ? await readText(authPath, '') : '';
|
|
1073
1192
|
const envText = await readText(status.env_path, '');
|
|
1074
1193
|
const apiKey = parseCodexLbEnvKey(envText);
|
|
1075
1194
|
if (!apiKey) {
|
|
1076
1195
|
return { status: 'skipped', reason: 'missing_env_key', auth_path: authPath };
|
|
1077
1196
|
}
|
|
1197
|
+
const forceCodexLbApiKeyAuth = opts.forceCodexLbApiKeyAuth === true;
|
|
1198
|
+
const writeApiKeyAuth = async (reason, backupPathForResult = null) => {
|
|
1199
|
+
try {
|
|
1200
|
+
await writeTextAtomic(authPath, `${JSON.stringify({ auth_mode: 'apikey', OPENAI_API_KEY: apiKey }, null, 2)}\n`);
|
|
1201
|
+
await fsp.chmod(authPath, 0o600).catch(() => { });
|
|
1202
|
+
return {
|
|
1203
|
+
status: 'apikey_forced',
|
|
1204
|
+
reason,
|
|
1205
|
+
auth_path: authPath,
|
|
1206
|
+
backup_path: backupPathForResult || backupPath
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
catch (err) {
|
|
1210
|
+
return { status: 'failed', reason: 'write_failed', auth_path: authPath, backup_path: backupPathForResult || backupPath, error: err.message };
|
|
1211
|
+
}
|
|
1212
|
+
};
|
|
1213
|
+
if (!authExists) {
|
|
1214
|
+
if (forceCodexLbApiKeyAuth)
|
|
1215
|
+
return writeApiKeyAuth('codex_lb_auth_selected_missing_auth');
|
|
1216
|
+
return { status: 'skipped', reason: 'auth_missing', auth_path: authPath };
|
|
1217
|
+
}
|
|
1218
|
+
if (!authText.trim()) {
|
|
1219
|
+
if (forceCodexLbApiKeyAuth)
|
|
1220
|
+
return writeApiKeyAuth('codex_lb_auth_selected_empty_auth');
|
|
1221
|
+
return { status: 'skipped', reason: 'auth_empty', auth_path: authPath };
|
|
1222
|
+
}
|
|
1078
1223
|
if (hasChatgptOAuthTokens(authText)) {
|
|
1079
1224
|
try {
|
|
1080
1225
|
await ensureDir(path.dirname(backupPath));
|
|
@@ -1092,10 +1237,12 @@ export async function reconcileCodexLbAuthConflict(opts = {}) {
|
|
|
1092
1237
|
backup_path: backupPath
|
|
1093
1238
|
};
|
|
1094
1239
|
}
|
|
1240
|
+
if (forceCodexLbApiKeyAuth)
|
|
1241
|
+
return writeApiKeyAuth('codex_lb_auth_selected', backupPath);
|
|
1095
1242
|
if (process.env.SKS_CODEX_LB_FORCE_APIKEY_AUTH !== '1') {
|
|
1096
1243
|
return {
|
|
1097
1244
|
status: 'oauth_preserved',
|
|
1098
|
-
reason: '
|
|
1245
|
+
reason: 'chatgpt_oauth_preserved_until_use_codex_lb_auth',
|
|
1099
1246
|
auth_path: authPath,
|
|
1100
1247
|
backup_path: backupPath
|
|
1101
1248
|
};
|
|
@@ -1116,6 +1263,17 @@ export async function reconcileCodexLbAuthConflict(opts = {}) {
|
|
|
1116
1263
|
};
|
|
1117
1264
|
}
|
|
1118
1265
|
const currentApiKey = parseCodexAuthApiKey(authText);
|
|
1266
|
+
if (forceCodexLbApiKeyAuth) {
|
|
1267
|
+
if (currentApiKey && currentApiKey === apiKey) {
|
|
1268
|
+
return {
|
|
1269
|
+
status: 'apikey_auth_active',
|
|
1270
|
+
reason: 'codex_lb_auth_selected',
|
|
1271
|
+
auth_path: authPath,
|
|
1272
|
+
backup_path: backupPath
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
return writeApiKeyAuth('codex_lb_auth_selected_replace_existing');
|
|
1276
|
+
}
|
|
1119
1277
|
if (currentApiKey && currentApiKey === apiKey) {
|
|
1120
1278
|
const backupText = await readText(backupPath, '');
|
|
1121
1279
|
if (hasChatgptOAuthTokens(backupText) && process.env.SKS_CODEX_LB_KEEP_APIKEY_AUTH !== '1') {
|
|
@@ -1309,7 +1467,7 @@ export async function maybePromptCodexLbSetupForLaunch(args = [], opts = {}) {
|
|
|
1309
1467
|
if (status.env_key_configured && status.base_url && !status.selected && status.auth_mode === 'chatgpt_oauth') {
|
|
1310
1468
|
return { status: 'continued_to_codex', ok: false, chain_health: null, codex_lb: status, reason: 'chatgpt_oauth_active_codex_lb_unselected' };
|
|
1311
1469
|
}
|
|
1312
|
-
if (status.env_key_configured && status.base_url && (!status.provider_configured || !status.selected || !status.
|
|
1470
|
+
if (status.env_key_configured && status.base_url && (!status.provider_configured || !status.selected || !status.provider_contract_ok)) {
|
|
1313
1471
|
let promptedRestore = false;
|
|
1314
1472
|
if (!status.provider_configured && canAskYesNo()) {
|
|
1315
1473
|
promptedRestore = true;
|
|
@@ -1460,7 +1618,7 @@ async function maybeSyncCodexLbSharedLogin(apiKey, opts = {}) {
|
|
|
1460
1618
|
if (!apiKey)
|
|
1461
1619
|
return { ok: false, status: 'missing_env_key' };
|
|
1462
1620
|
if (!shouldSyncCodexLbSharedLogin(opts)) {
|
|
1463
|
-
return { ok: true, status: 'skipped', reason: 'codex-lb
|
|
1621
|
+
return { ok: true, status: 'skipped', reason: 'codex-lb repair preserved the current Codex App auth; run `sks codex-lb use-codex-lb` to switch the app to codex-lb API-key auth.' };
|
|
1464
1622
|
}
|
|
1465
1623
|
return syncCodexApiKeyLogin(apiKey, opts);
|
|
1466
1624
|
}
|
|
@@ -1491,12 +1649,12 @@ export function upsertCodexLbConfig(text = '', baseUrl, selectDefault = true) {
|
|
|
1491
1649
|
: removeTopLevelTomlKeyIfValue(text, 'model_provider', 'codex-lb');
|
|
1492
1650
|
const block = [
|
|
1493
1651
|
'[model_providers.codex-lb]',
|
|
1494
|
-
'name = "
|
|
1652
|
+
'name = "openai"',
|
|
1495
1653
|
`base_url = "${baseUrl}"`,
|
|
1496
1654
|
'wire_api = "responses"',
|
|
1497
1655
|
'env_key = "CODEX_LB_API_KEY"',
|
|
1498
1656
|
'supports_websockets = true',
|
|
1499
|
-
'requires_openai_auth =
|
|
1657
|
+
'requires_openai_auth = true'
|
|
1500
1658
|
].join('\n');
|
|
1501
1659
|
next = upsertTomlTable(next, 'model_providers.codex-lb', block);
|
|
1502
1660
|
return `${next.trim()}\n`;
|
|
@@ -2601,10 +2759,10 @@ export async function selftestCodexLb(tmp) {
|
|
|
2601
2759
|
const codexLbConfig = await safeReadText(path.join(codexLbHome, '.codex', 'config.toml'));
|
|
2602
2760
|
const codexLbEnv = await safeReadText(path.join(codexLbHome, '.codex', 'sks-codex-lb.env'));
|
|
2603
2761
|
const codexLbAuth = await safeReadText(path.join(codexLbHome, '.codex', 'auth.json'));
|
|
2604
|
-
if (!codexLbSetupJson.ok || codexLbSetupJson.base_url !== 'https://lb.example.test/backend-api/codex' || !hasTopLevelCodexLbSelected(codexLbConfig) || !codexLbConfig.includes('[model_providers.codex-lb]') || !codexLbEnv.includes("CODEX_LB_BASE_URL='https://lb.example.test/backend-api/codex'") || !codexLbEnv.includes("CODEX_LB_API_KEY='sk-test'") || codexLbSetupJson.codex_environment?.ok !== true || codexLbSetupJson.codex_login?.status !== '
|
|
2762
|
+
if (!codexLbSetupJson.ok || codexLbSetupJson.base_url !== 'https://lb.example.test/backend-api/codex' || !hasTopLevelCodexLbSelected(codexLbConfig) || !codexLbConfig.includes('[model_providers.codex-lb]') || !codexLbEnv.includes("CODEX_LB_BASE_URL='https://lb.example.test/backend-api/codex'") || !codexLbEnv.includes("CODEX_LB_API_KEY='sk-test'") || codexLbSetupJson.codex_environment?.ok !== true || codexLbSetupJson.codex_login?.status !== 'apikey_forced' || !codexLbAuth.includes('OPENAI_API_KEY') || !codexLbAuth.includes('sk-test'))
|
|
2605
2763
|
throw new Error('selftest: codex-lb setup');
|
|
2606
|
-
if (!codexLbConfig.includes('requires_openai_auth =
|
|
2607
|
-
throw new Error('selftest: codex-lb setup did not
|
|
2764
|
+
if (!codexLbConfig.includes('requires_openai_auth = true') || !codexLbConfig.includes('name = "openai"'))
|
|
2765
|
+
throw new Error('selftest: codex-lb setup did not write current codex-lb App provider contract');
|
|
2608
2766
|
const codexLbFailLaunchctl = path.join(codexLbFakeBin, 'launchctl-fail');
|
|
2609
2767
|
await writeTextAtomic(codexLbFailLaunchctl, '#!/bin/sh\necho "launchctl denied" >&2\nexit 7\n');
|
|
2610
2768
|
await fsp.chmod(codexLbFailLaunchctl, 0o755);
|
|
@@ -2617,8 +2775,8 @@ export async function selftestCodexLb(tmp) {
|
|
|
2617
2775
|
const codexLbRepairSetupConfig = await safeReadText(path.join(codexLbHome, '.codex', 'config.toml'));
|
|
2618
2776
|
if (!hasTopLevelCodexLbSelected(codexLbRepairSetupConfig) || !codexLbRepairSetupConfig.includes('[model_providers.codex-lb]') || !codexLbRepairSetupConfig.includes('https://lb.example.test/backend-api/codex') || codexLbRepairSetupConfig.includes('sk-test'))
|
|
2619
2777
|
throw new Error('selftest: init codex-lb');
|
|
2620
|
-
if (!codexLbRepairSetupConfig.includes('requires_openai_auth =
|
|
2621
|
-
throw new Error('selftest: init codex-lb did not preserve
|
|
2778
|
+
if (!codexLbRepairSetupConfig.includes('requires_openai_auth = true') || !codexLbRepairSetupConfig.includes('name = "openai"'))
|
|
2779
|
+
throw new Error('selftest: init codex-lb did not preserve current App provider contract');
|
|
2622
2780
|
if (!hasCodexUnstableFeatureWarningSuppression(codexLbRepairSetupConfig))
|
|
2623
2781
|
throw new Error('selftest: init codex-lb did not suppress Codex unstable feature warning');
|
|
2624
2782
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), `${codexLbConfig}\n[mcp_servers.supabase]\nurl = "https://mcp.supabase.com/mcp?project_ref=ref&read_only=true&features=database,docs"\n`);
|
|
@@ -2636,8 +2794,8 @@ export async function selftestCodexLb(tmp) {
|
|
|
2636
2794
|
const pcfg = await safeReadText(path.join(ptmp, '.codex', 'config.toml'));
|
|
2637
2795
|
if (!hasTopLevelCodexLbSelected(pcfg) || !pcfg.includes('[model_providers.codex-lb]') || !pcfg.includes('[mcp_servers.supabase]') || !pcfg.includes('read_only=true'))
|
|
2638
2796
|
throw new Error('selftest: project codex-lb');
|
|
2639
|
-
if (!pcfg.includes('requires_openai_auth =
|
|
2640
|
-
throw new Error('selftest: project codex-lb did not copy
|
|
2797
|
+
if (!pcfg.includes('requires_openai_auth = true') || !pcfg.includes('name = "openai"'))
|
|
2798
|
+
throw new Error('selftest: project codex-lb did not copy current App provider contract');
|
|
2641
2799
|
if (!hasCodexUnstableFeatureWarningSuppression(pcfg))
|
|
2642
2800
|
throw new Error('selftest: project codex-lb config did not suppress Codex unstable feature warning');
|
|
2643
2801
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'auth.json'), '{"auth_mode":"browser"}\n');
|
|
@@ -2711,8 +2869,8 @@ export async function selftestCodexLb(tmp) {
|
|
|
2711
2869
|
throw new Error('selftest: postinstall drift auth');
|
|
2712
2870
|
if (!hasTopLevelCodexLbSelected(codexLbPostBootstrapConfig) || !codexLbPostBootstrapConfig.includes('[model_providers.codex-lb]') || !codexLbPostBootstrapConfig.includes('https://lb.example.test/backend-api/codex') || codexLbPostBootstrapConfig.includes('sk-test'))
|
|
2713
2871
|
throw new Error('selftest: postinstall drift config');
|
|
2714
|
-
if (!codexLbPostBootstrapConfig.includes('requires_openai_auth =
|
|
2715
|
-
throw new Error('selftest: postinstall drift config did not restore
|
|
2872
|
+
if (!codexLbPostBootstrapConfig.includes('requires_openai_auth = true') || !codexLbPostBootstrapConfig.includes('name = "openai"'))
|
|
2873
|
+
throw new Error('selftest: postinstall drift config did not restore current App provider contract');
|
|
2716
2874
|
const doctorProject = tmpdir();
|
|
2717
2875
|
await ensureDir(path.join(doctorProject, '.git'));
|
|
2718
2876
|
await writeTextAtomic(path.join(doctorProject, 'package.json'), '{"name":"codex-lb-doctor-project","version":"0.0.0"}\n');
|
|
@@ -2732,8 +2890,8 @@ export async function selftestCodexLb(tmp) {
|
|
|
2732
2890
|
const codexLbDoctorConfig = await safeReadText(path.join(codexLbHome, '.codex', 'config.toml'));
|
|
2733
2891
|
if (!codexLbDoctorJson.repair?.codex_lb?.ok || !codexLbDoctorJson.repair.codex_lb.config_repaired || !codexLbDoctorJson.codex_lb?.ok || !codexLbDoctorAuth.includes('"auth_mode":"browser"') || codexLbDoctorAuth.includes('sk-test') || !hasTopLevelCodexLbSelected(codexLbDoctorConfig) || !codexLbDoctorConfig.includes('https://lb.example.test/backend-api/codex') || !hasCodexUnstableFeatureWarningSuppression(codexLbDoctorConfig))
|
|
2734
2892
|
throw new Error('selftest: doctor codex-lb');
|
|
2735
|
-
if (!codexLbDoctorConfig.includes('requires_openai_auth =
|
|
2736
|
-
throw new Error('selftest: doctor codex-lb did not restore
|
|
2893
|
+
if (!codexLbDoctorConfig.includes('requires_openai_auth = true') || !codexLbDoctorConfig.includes('name = "openai"'))
|
|
2894
|
+
throw new Error('selftest: doctor codex-lb did not restore current App provider contract');
|
|
2737
2895
|
// codex-lb auth: ChatGPT OAuth ↔ codex-lb env_key conflict reconciliation.
|
|
2738
2896
|
const oauthAuthJson = JSON.stringify({
|
|
2739
2897
|
auth_mode: 'chatgpt',
|
|
@@ -2742,7 +2900,7 @@ export async function selftestCodexLb(tmp) {
|
|
|
2742
2900
|
});
|
|
2743
2901
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'auth.json'), `${oauthAuthJson}\n`);
|
|
2744
2902
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'sks-codex-lb.env'), "export CODEX_LB_BASE_URL='https://lb.example.test/backend-api/codex'\nexport CODEX_LB_API_KEY='sk-test'\n");
|
|
2745
|
-
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), 'model_provider = "codex-lb"\n\n[model_providers.codex-lb]\nname = "
|
|
2903
|
+
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), 'model_provider = "codex-lb"\n\n[model_providers.codex-lb]\nname = "openai"\nbase_url = "https://lb.example.test/backend-api/codex"\nwire_api = "responses"\nenv_key = "CODEX_LB_API_KEY"\nsupports_websockets = true\nrequires_openai_auth = true\n');
|
|
2746
2904
|
await fsp.rm(path.join(codexLbHome, '.codex', 'auth.chatgpt-backup.json'), { force: true });
|
|
2747
2905
|
const codexLbReconcileRepair = await runProcess(process.execPath, [packagedSksEntrypoint(), 'auth', 'repair', '--json'], { cwd: tmp, env: codexLbEnvForSelftest, timeoutMs: 15000, maxOutputBytes: 64 * 1024 });
|
|
2748
2906
|
if (codexLbReconcileRepair.code !== 0)
|
|
@@ -2778,7 +2936,7 @@ export async function selftestCodexLb(tmp) {
|
|
|
2778
2936
|
// codex-lb auth: release flow — restore ChatGPT OAuth from backup so the user can return to
|
|
2779
2937
|
// the official ChatGPT account login. Default deselects model_provider; flags control whether
|
|
2780
2938
|
// the provider stays selected and whether the backup file is removed after restore.
|
|
2781
|
-
const codexLbReleaseConfig = 'model_provider = "codex-lb"\n\n[model_providers.codex-lb]\nname = "
|
|
2939
|
+
const codexLbReleaseConfig = 'model_provider = "codex-lb"\n\n[model_providers.codex-lb]\nname = "openai"\nbase_url = "https://lb.example.test/backend-api/codex"\nwire_api = "responses"\nenv_key = "CODEX_LB_API_KEY"\nsupports_websockets = true\nrequires_openai_auth = true\n';
|
|
2782
2940
|
const codexLbReleaseEnv = "export CODEX_LB_BASE_URL='https://lb.example.test/backend-api/codex'\nexport CODEX_LB_API_KEY='sk-test'\n";
|
|
2783
2941
|
const codexLbReleaseApikeyAuth = '{"auth_mode":"apikey","OPENAI_API_KEY":"sk-test"}\n';
|
|
2784
2942
|
const codexLbReleaseOauthBackup = `${oauthAuthJson}\n`;
|
|
@@ -2874,7 +3032,7 @@ export async function selftestCodexLb(tmp) {
|
|
|
2874
3032
|
if (codexLbMalformedPostinstall.code !== 0 || !String(codexLbMalformedPostinstall.stdout || '').includes('codex-lb auth: stored key missing') || codexLbLoginCallsAfterMalformed !== codexLbLoginCallsBeforeMalformed)
|
|
2875
3033
|
throw new Error('selftest: bad codex-lb env');
|
|
2876
3034
|
await fsp.rm(path.join(codexLbHome, '.codex', 'sks-codex-lb.env'), { force: true });
|
|
2877
|
-
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), '[model_providers.codex-lb]\nname = "
|
|
3035
|
+
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), '[model_providers.codex-lb]\nname = "openai"\nbase_url = "https://lb.example.test/backend-api/codex"\nwire_api = "responses"\nsupports_websockets = true\nrequires_openai_auth = true\n');
|
|
2878
3036
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'auth.json'), '{"auth_mode":"apikey","key":"sk-legacy"}\n');
|
|
2879
3037
|
const codexLbLoginCallsBeforeLegacyPostinstall = await codexLbLoginCallCount(codexLbHome);
|
|
2880
3038
|
const codexLbLegacyPostinstall = await runProcess(process.execPath, [packagedSksEntrypoint(), 'postinstall'], {
|
|
@@ -2891,7 +3049,7 @@ export async function selftestCodexLb(tmp) {
|
|
|
2891
3049
|
if (!String(codexLbLegacyPostinstall.stdout || '').includes('codex-lb auth: restored from existing Codex login cache') || !codexLbLegacyPostinstallEnv.includes("CODEX_LB_API_KEY='sk-legacy'") || !codexLbLegacyPostinstallEnv.includes("CODEX_LB_BASE_URL='https://lb.example.test/backend-api/codex'") || !codexLbLegacyPostinstallAuth.includes('"auth_mode":"apikey"') || !codexLbLegacyPostinstallAuth.includes('sk-legacy') || codexLbLoginCallsAfterLegacyPostinstall !== codexLbLoginCallsBeforeLegacyPostinstall)
|
|
2892
3050
|
throw new Error('selftest: legacy codex-lb postinstall restore');
|
|
2893
3051
|
await fsp.rm(path.join(codexLbHome, '.codex', 'sks-codex-lb.env'), { force: true });
|
|
2894
|
-
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), 'model_provider = "codex-lb"\n\n[model_providers.codex-lb]\nname = "
|
|
3052
|
+
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), 'model_provider = "codex-lb"\n\n[model_providers.codex-lb]\nname = "openai"\nbase_url = "https://lb.example.test/backend-api/codex"\nwire_api = "responses"\nsupports_websockets = true\nrequires_openai_auth = true\n');
|
|
2895
3053
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'auth.json'), '{"auth_mode":"apikey","key":"sk-legacy-doctor"}\n');
|
|
2896
3054
|
const codexLbLegacyDoctorProject = tmpdir();
|
|
2897
3055
|
await ensureDir(path.join(codexLbLegacyDoctorProject, '.git'));
|
|
@@ -3074,8 +3232,8 @@ export async function selftestCodexLb(tmp) {
|
|
|
3074
3232
|
}
|
|
3075
3233
|
});
|
|
3076
3234
|
const missingProviderRepairedConfig = await safeReadText(path.join(codexLbHome, '.codex', 'config.toml'));
|
|
3077
|
-
if (!missingProviderLaunch.ok || missingProviderLaunch.status !== 'present' || missingProviderLaunch.chain_health?.status !== 'chain_ok' || missingProviderLaunchCalls.length !== 2 || !hasTopLevelCodexLbSelected(missingProviderRepairedConfig) || !missingProviderRepairedConfig.includes('[model_providers.codex-lb]') || !missingProviderRepairedConfig.includes('env_key = "CODEX_LB_API_KEY"') || !missingProviderRepairedConfig.includes('supports_websockets = true') || !missingProviderRepairedConfig.includes('requires_openai_auth =
|
|
3078
|
-
throw new Error('selftest: bare sks launch did not restore codex-lb provider block
|
|
3235
|
+
if (!missingProviderLaunch.ok || missingProviderLaunch.status !== 'present' || missingProviderLaunch.chain_health?.status !== 'chain_ok' || missingProviderLaunchCalls.length !== 2 || !hasTopLevelCodexLbSelected(missingProviderRepairedConfig) || !missingProviderRepairedConfig.includes('[model_providers.codex-lb]') || !missingProviderRepairedConfig.includes('env_key = "CODEX_LB_API_KEY"') || !missingProviderRepairedConfig.includes('supports_websockets = true') || !missingProviderRepairedConfig.includes('requires_openai_auth = true') || !missingProviderRepairedConfig.includes('name = "openai"'))
|
|
3236
|
+
throw new Error('selftest: bare sks launch did not restore codex-lb provider block to current App contract');
|
|
3079
3237
|
const chainCalls = [];
|
|
3080
3238
|
const okChain = await checkCodexLbResponseChain({ base_url: 'https://lb.example.test/backend-api/codex', env_path: path.join(codexLbHome, '.codex', 'sks-codex-lb.env') }, {
|
|
3081
3239
|
apiKey: 'sk-test',
|