sneakoscope 8.0.2 → 8.0.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.
Files changed (122) hide show
  1. package/README.md +47 -23
  2. package/config/skills-hash-ledger.v1.json +1 -1
  3. package/crates/sks-core/Cargo.lock +1 -1
  4. package/crates/sks-core/Cargo.toml +1 -1
  5. package/dist/bin/fast-inline.js +55 -13
  6. package/dist/bin/sks-dispatch.js +1 -1
  7. package/dist/cli/command-manifest-lite.js +4 -0
  8. package/dist/cli/command-registry.js +4 -0
  9. package/dist/cli/install-helpers-codex-lb-chain.js +187 -0
  10. package/dist/cli/install-helpers-codex-lb-config.js +74 -8
  11. package/dist/cli/install-helpers.js +89 -44
  12. package/dist/commands/codex-lb.js +117 -19
  13. package/dist/commands/doctor.js +697 -37
  14. package/dist/config/skills-manifest.json +3 -2
  15. package/dist/core/agents/agent-runner-codex-exec.js +5 -3
  16. package/dist/core/codex/codex-config-guard.js +238 -52
  17. package/dist/core/codex-adapter.js +5 -3
  18. package/dist/core/codex-app/menubar/constants.js +10 -1
  19. package/dist/core/codex-app/menubar/global-install.js +288 -0
  20. package/dist/core/codex-app/menubar/installer.js +79 -14
  21. package/dist/core/codex-app/menubar/launch-agent.js +267 -45
  22. package/dist/core/codex-app/menubar/paths.js +3 -0
  23. package/dist/core/codex-app/menubar/rollback.js +55 -7
  24. package/dist/core/codex-app/menubar/status.js +75 -4
  25. package/dist/core/codex-control/codex-0139-image-path-real-probe.js +3 -1
  26. package/dist/core/codex-control/codex-0139-web-search-probe.js +3 -1
  27. package/dist/core/codex-control/codex-app-server-runtime-env.js +13 -4
  28. package/dist/core/codex-control/codex-app-server-v2-client.js +3 -3
  29. package/dist/core/codex-exec-output-schema.js +4 -1
  30. package/dist/core/codex-lb/codex-lb-env.js +63 -72
  31. package/dist/core/codex-lb/codex-lb-setup.js +15 -2
  32. package/dist/core/codex-lb/desktop-center-credentials.js +517 -58
  33. package/dist/core/codex-lb/desktop-controller.js +153 -24
  34. package/dist/core/codex-lb/routing-truth.js +327 -0
  35. package/dist/core/codex-native/core-skill-manifest.js +1 -1
  36. package/dist/core/commands/basic-cli.js +7 -1
  37. package/dist/core/commands/qa-loop-command.js +3 -1
  38. package/dist/core/commands/telegram-command.js +241 -0
  39. package/dist/core/config-adopt/config-adopt-command.js +35 -0
  40. package/dist/core/config-adopt/config-adopt.js +346 -0
  41. package/dist/core/config-adopt/index.js +3 -0
  42. package/dist/core/doctor/command-alias-cleanup.js +4 -1
  43. package/dist/core/doctor/current-project-guidance-nested.js +37 -6
  44. package/dist/core/doctor/current-project-guidance.js +11 -6
  45. package/dist/core/doctor/doctor-readiness-matrix.js +14 -0
  46. package/dist/core/doctor/retired-managed-residue-private.js +1 -1
  47. package/dist/core/doctor/sks-menubar-doctor.js +4 -0
  48. package/dist/core/feature-fixtures.js +8 -0
  49. package/dist/core/hooks-runtime/hook-context.js +9 -0
  50. package/dist/core/hooks-runtime/managed-skill-admission.js +54 -4
  51. package/dist/core/hooks-runtime/official-subagent-lifecycle.js +69 -1
  52. package/dist/core/hooks-runtime/subagent-skill-availability-contract.js +2 -2
  53. package/dist/core/hooks-runtime/subagent-skill-availability-evidence.js +3 -3
  54. package/dist/core/hooks-runtime/subagent-skill-availability-guards.js +43 -5
  55. package/dist/core/hooks-runtime/subagent-skill-availability.js +64 -30
  56. package/dist/core/hooks-runtime/subagent-stop-hook.js +15 -2
  57. package/dist/core/hooks-runtime.js +148 -4
  58. package/dist/core/imagegen/imagegen-capability.js +46 -0
  59. package/dist/core/imagegen/require-imagegen.js +2 -2
  60. package/dist/core/init/skills.js +17 -3
  61. package/dist/core/install/installed-package-smoke.js +8 -2
  62. package/dist/core/naruto/hardware-capacity-probe.js +9 -3
  63. package/dist/core/naruto/naruto-concurrency-governor.js +68 -24
  64. package/dist/core/perf/import-graph-budget.js +0 -1
  65. package/dist/core/product-design-app-server.js +13 -1
  66. package/dist/core/release/package-size-budget.js +2 -2
  67. package/dist/core/release/release-pack-receipt.js +1 -2
  68. package/dist/core/routes/dollar-manifest-lite.js +1 -1
  69. package/dist/core/routes.js +1 -1
  70. package/dist/core/security/private-credential-file.js +209 -0
  71. package/dist/core/subagents/agent-catalog.js +17 -32
  72. package/dist/core/subagents/naruto-help-contract.js +5 -5
  73. package/dist/core/subagents/official-subagent-config.js +16 -6
  74. package/dist/core/subagents/official-subagent-preparation.js +34 -15
  75. package/dist/core/subagents/official-subagent-prompt.js +4 -4
  76. package/dist/core/subagents/official-subagent-runner.js +27 -4
  77. package/dist/core/subagents/thread-budget.js +19 -12
  78. package/dist/core/subagents/wave-lifecycle.js +18 -6
  79. package/dist/core/telegram/access.js +54 -0
  80. package/dist/core/telegram/client.js +144 -0
  81. package/dist/core/telegram/confirmation.js +37 -0
  82. package/dist/core/telegram/doctor.js +151 -0
  83. package/dist/core/telegram/index.js +9 -0
  84. package/dist/core/telegram/keychain.js +265 -0
  85. package/dist/core/telegram/liveness.js +109 -0
  86. package/dist/core/telegram/poller.js +90 -0
  87. package/dist/core/telegram/redaction.js +15 -0
  88. package/dist/core/telegram/types.js +1 -0
  89. package/dist/core/update/update-diagnostics.js +52 -0
  90. package/dist/core/update/update-migration-state.js +5 -1
  91. package/dist/core/update/update-operation.js +2 -1
  92. package/dist/core/update/update-stage-diagnostics.js +38 -0
  93. package/dist/core/update/update-status.js +2 -9
  94. package/dist/core/update-check.js +147 -31
  95. package/dist/core/version.js +1 -1
  96. package/dist/core/zellij/zellij-launcher.js +23 -4
  97. package/dist/native/sks-menubar/Sources/AppDelegate.swift +13 -0
  98. package/dist/native/sks-menubar/Sources/AppIdentity.swift +7 -0
  99. package/dist/native/sks-menubar/Sources/OperationCoordinator.swift +3 -2
  100. package/dist/native/sks-menubar/Sources/ProvidersConnectTest.swift +115 -0
  101. package/dist/native/sks-menubar/Sources/ProvidersFastMode.swift +17 -0
  102. package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +190 -0
  103. package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +100 -59
  104. package/dist/native/sks-menubar/Sources/SingletonInstanceGuard.swift +328 -0
  105. package/dist/native/sks-menubar/Sources/TelegramPrivateFileStore.swift +320 -0
  106. package/dist/native/sks-menubar/Sources/TelegramPrivateFileSupport.swift +303 -0
  107. package/dist/native/sks-menubar/Sources/TelegramProcessGateway.swift +238 -0
  108. package/dist/native/sks-menubar/Sources/TelegramRuntimeSupport.swift +56 -0
  109. package/dist/native/sks-menubar/Sources/TelegramSupport.swift +56 -0
  110. package/dist/native/sks-menubar/Sources/TelegramTransport.swift +474 -0
  111. package/dist/native/sks-menubar/Sources/main.swift +15 -1
  112. package/dist/native/sks-menubar/Tests/OperationCoordinatorTests.swift +4 -3
  113. package/dist/native/sks-menubar/Tests/ProvidersViewControllerTests.swift +129 -0
  114. package/dist/native/sks-menubar/Tests/SingletonInstanceGuardTests.swift +183 -0
  115. package/dist/native/sks-menubar/Tests/TelegramRuntimeSmokeTests.swift +267 -0
  116. package/dist/native/sks-menubar/Tests/TelegramRuntimeTests.swift +222 -0
  117. package/dist/scripts/codex-lb-fast-mode-truth-check.js +1 -1
  118. package/dist/scripts/codex-lb-fast-ui-preservation-check.js +1 -1
  119. package/dist/scripts/current-command-surface-check.js +2 -4
  120. package/dist/scripts/official-subagent-workflow-check.js +1 -1
  121. package/dist/scripts/packlist-performance-check.js +1 -2
  122. package/package.json +1 -1
@@ -13,14 +13,42 @@ import { escapeRegExp } from '../core/text/regex.js';
13
13
  export const CODEX_LB_DESKTOP_BRIDGE_MARKER = '# sks-codex-lb-managed-desktop-bridge';
14
14
  export const CODEX_LB_DESKTOP_COMPAT_MARKER = '# sks-codex-lb-managed-desktop-compat';
15
15
  export const CODEX_LB_MODEL_CATALOG_MARKER = '# sks-codex-lb-managed-model-catalog';
16
- const LEGACY_CODEX_LB_OPENAI_ROUTING_MARKER = '# sks-codex-lb-managed-openai-base-url';
16
+ export const CODEX_LB_PROVIDER_SELECTION_MARKER = '# sks-codex-lb-managed-provider-selection';
17
+ export const CODEX_LB_OAUTH_SELECTION_MARKER = '# sks-codex-lb-managed-oauth-selection';
18
+ export const LEGACY_CODEX_LB_OPENAI_ROUTING_MARKER = '# sks-codex-lb-managed-openai-base-url';
19
+ export function removeCodexLbOrphanManagedMarkers(text) {
20
+ const source = String(text || '');
21
+ const selectedProvider = topLevelTomlString(source, 'model_provider');
22
+ const markerTargets = [
23
+ [CODEX_LB_DESKTOP_BRIDGE_MARKER, Boolean(topLevelTomlString(source, 'openai_base_url'))],
24
+ [LEGACY_CODEX_LB_OPENAI_ROUTING_MARKER, Boolean(topLevelTomlString(source, 'openai_base_url'))],
25
+ [CODEX_LB_DESKTOP_COMPAT_MARKER, selectedProvider === 'codex-lb'],
26
+ [CODEX_LB_MODEL_CATALOG_MARKER, Boolean(topLevelTomlString(source, 'model_catalog_json'))],
27
+ [CODEX_LB_PROVIDER_SELECTION_MARKER, selectedProvider === 'codex-lb'],
28
+ [CODEX_LB_OAUTH_SELECTION_MARKER, selectedProvider === 'openai']
29
+ ];
30
+ const orphanMarkers = markerTargets
31
+ .filter(([marker, hasManagedContent]) => topLevelHasLine(source, marker) && !hasManagedContent)
32
+ .map(([marker]) => marker);
33
+ let next = source;
34
+ for (const marker of orphanMarkers)
35
+ next = removeTopLevelLine(next, marker);
36
+ return {
37
+ schema: 'sks.codex-lb-orphan-managed-marker-cleanup.v1',
38
+ changed: orphanMarkers.length > 0,
39
+ orphan_markers: orphanMarkers,
40
+ text: next
41
+ };
42
+ }
17
43
  export function upsertCodexLbNativeDesktopConfig(text, input) {
18
- let next = String(text || '');
44
+ let next = removeCodexLbOrphanManagedMarkers(String(text || '')).text;
19
45
  const selectedProvider = topLevelTomlString(next, 'model_provider');
20
46
  if (selectedProvider && selectedProvider !== 'openai' && selectedProvider !== 'codex-lb') {
21
47
  throw new Error('codex_lb_user_owned_model_provider_conflict');
22
48
  }
23
- if (selectedProvider === 'codex-lb' && !topLevelHasLine(next, CODEX_LB_DESKTOP_COMPAT_MARKER)) {
49
+ if (selectedProvider === 'codex-lb'
50
+ && !topLevelHasLine(next, CODEX_LB_DESKTOP_COMPAT_MARKER)
51
+ && !topLevelHasLine(next, CODEX_LB_PROVIDER_SELECTION_MARKER)) {
24
52
  throw new Error('codex_lb_legacy_desktop_config_requires_migration');
25
53
  }
26
54
  if (topLevelHasLine(next, LEGACY_CODEX_LB_OPENAI_ROUTING_MARKER)
@@ -31,10 +59,10 @@ export function upsertCodexLbNativeDesktopConfig(text, input) {
31
59
  if (modelCatalog && !topLevelHasLine(next, CODEX_LB_MODEL_CATALOG_MARKER)) {
32
60
  throw new Error('codex_lb_user_owned_model_catalog_json_conflict');
33
61
  }
34
- next = removeTopLevelTomlKeyIfValue(next, 'model_provider', 'codex-lb');
62
+ next = removeManagedCodexLbSelection(next);
35
63
  next = removeTopLevelLine(next, CODEX_LB_DESKTOP_COMPAT_MARKER);
36
64
  next = removeManagedModelCatalogJson(next);
37
- next = upsertManagedTopLevelTomlString(next, 'openai_base_url', input.bridgeBaseUrl, CODEX_LB_DESKTOP_BRIDGE_MARKER);
65
+ next = upsertManagedTopLevelTomlString(next, 'openai_base_url', input.bridgeBaseUrl, CODEX_LB_DESKTOP_BRIDGE_MARKER, [LEGACY_CODEX_LB_OPENAI_ROUTING_MARKER]);
38
66
  next = upsertTomlTable(next, 'model_providers.codex-lb', cliProviderBlock(input.remoteBaseUrl));
39
67
  return ensureTrailingNewline(next);
40
68
  }
@@ -55,19 +83,50 @@ export function upsertCodexLbCompatDesktopConfig(text, input) {
55
83
  return ensureTrailingNewline(next);
56
84
  }
57
85
  export function upsertCodexLbCliProviderConfig(text, input) {
86
+ const cleaned = removeCodexLbOrphanManagedMarkers(String(text || '')).text;
87
+ if (topLevelHasLine(cleaned, LEGACY_CODEX_LB_OPENAI_ROUTING_MARKER)) {
88
+ throw new Error('codex_lb_legacy_desktop_config_requires_migration');
89
+ }
90
+ const cliBase = removeCodexLbManagedDesktopConfig(cleaned);
91
+ const claimedSelection = input.selectGlobally === true
92
+ && topLevelTomlString(cliBase, 'model_provider') === 'codex-lb'
93
+ && !topLevelHasLine(cliBase, CODEX_LB_PROVIDER_SELECTION_MARKER)
94
+ ? addTopLevelMarkerBeforeKey(cliBase, 'model_provider', CODEX_LB_PROVIDER_SELECTION_MARKER)
95
+ : cliBase;
58
96
  let next = input.selectGlobally === true
59
- ? upsertTopLevelTomlString(text, 'model_provider', 'codex-lb')
60
- : removeTopLevelTomlKeyIfValue(text, 'model_provider', 'codex-lb');
97
+ ? upsertManagedTopLevelTomlString(removeTopLevelTomlKeyIfValue(removeManagedOAuthSelection(claimedSelection), 'model_provider', 'openai'), 'model_provider', 'codex-lb', CODEX_LB_PROVIDER_SELECTION_MARKER)
98
+ : removeManagedCodexLbSelection(cliBase);
61
99
  next = upsertTomlTable(next, 'model_providers.codex-lb', cliProviderBlock(input.remoteBaseUrl));
62
100
  return ensureTrailingNewline(next);
63
101
  }
102
+ export function restoreCodexLbOAuthSelectionConfig(text) {
103
+ let next = removeManagedCodexLbSelection(String(text || ''));
104
+ next = upsertManagedTopLevelTomlString(next, 'model_provider', 'openai', CODEX_LB_OAUTH_SELECTION_MARKER);
105
+ return ensureTrailingNewline(next);
106
+ }
107
+ export function removeManagedCodexLbSelection(text) {
108
+ let next = removeTopLevelTomlKeyIfValue(String(text || ''), 'model_provider', 'codex-lb');
109
+ next = removeTopLevelLine(next, CODEX_LB_PROVIDER_SELECTION_MARKER);
110
+ return next;
111
+ }
112
+ function removeManagedOAuthSelection(text) {
113
+ let next = String(text || '');
114
+ if (topLevelHasLine(next, CODEX_LB_OAUTH_SELECTION_MARKER)) {
115
+ next = removeTopLevelTomlKeyIfValue(next, 'model_provider', 'openai');
116
+ next = removeTopLevelLine(next, CODEX_LB_OAUTH_SELECTION_MARKER);
117
+ }
118
+ return next;
119
+ }
64
120
  export function removeCodexLbManagedDesktopConfig(text) {
65
- let next = removeManagedDesktopBridgeRouting(String(text || ''));
121
+ let next = removeManagedDesktopBridgeRouting(removeCodexLbOrphanManagedMarkers(String(text || '')).text);
66
122
  if (topLevelHasLine(next, CODEX_LB_DESKTOP_COMPAT_MARKER)) {
67
123
  next = removeTopLevelTomlKeyIfValue(next, 'model_provider', 'codex-lb');
68
124
  next = removeTopLevelLine(next, CODEX_LB_DESKTOP_COMPAT_MARKER);
69
125
  }
70
126
  next = removeManagedModelCatalogJson(next);
127
+ if (topLevelTomlString(next, 'model_provider') === 'codex-lb') {
128
+ next = restoreCodexLbOAuthSelectionConfig(next);
129
+ }
71
130
  return ensureTrailingNewline(next);
72
131
  }
73
132
  export function removeManagedModelCatalogJson(text) {
@@ -159,6 +218,13 @@ export function upsertCodexLbSharedOpenAiRouting(text = '', baseUrl = '') {
159
218
  }
160
219
  export function removeCodexLbSharedOpenAiRouting(text = '', baseUrl = '') {
161
220
  const state = codexLbSharedOpenAiRoutingState(text, baseUrl);
221
+ if (state.status === 'missing' && state.managed) {
222
+ return {
223
+ ...state,
224
+ changed: true,
225
+ text: removeTopLevelLine(String(text || ''), CODEX_LB_SHARED_OPENAI_ROUTING_MARKER)
226
+ };
227
+ }
162
228
  if (state.status !== 'matched' || !state.managed)
163
229
  return { ...state, changed: false, text: String(text || '') };
164
230
  const withoutValue = removeTopLevelTomlKeyIfValue(text, 'openai_base_url', state.expected_base_url);
@@ -29,7 +29,7 @@ import { canAskYesNo, compareVersions, hasCodexUnstableFeatureWarningSuppression
29
29
  import { printCodexLbSetupWarnings } from './codex-lb-setup-warning-output.js';
30
30
  import { checkCodexLbResponseChain } from './install-helpers-codex-lb-chain.js';
31
31
  import { CODEX_LB_CANONICAL_FAST_SERVICE_TIER, CODEX_LB_PROVIDER_ENV_KEY, askPostinstallQuestion, codexAuthChatgptBackupPath, codexAuthPath, codexLbConfigPath, codexLbEnvPath, hasTopLevelCodexLbSelected, normalizeCodexLbBaseUrl, parseCodexLbEnvKey, redactSecretText } from './install-helpers-codex-lb-shared.js';
32
- import { CODEX_LB_DESKTOP_BRIDGE_MARKER, CODEX_LB_DESKTOP_COMPAT_MARKER, appliedCodexLbPersistenceModes, captureCodexLbSetupWriteState, codexLbSharedOpenAiRoutingState, detectCodexLbSetupDrift, ensureGlobalCodexAppGlmProfile, ensureStoredOpenRouterProviderDuringInstall, hardenCodexLbSetupRecoveryPath, parseCodexLbEnvBaseUrl, removeCodexLbSetupRecoveryPath, removeCodexLbSharedOpenAiRouting, removeCodexLbManagedDesktopConfig, restoreCodexLbSetupWriteState, sha256Text, shellSingleQuote, upsertCodexAppGlmConfig, upsertCodexLbConfig, upsertCodexLbCliProviderConfig, upsertCodexLbCompatDesktopConfig, upsertCodexLbNativeDesktopConfig, upsertCodexLbSharedOpenAiRouting, writeCodexLbSetupFileIfUnchanged } from './install-helpers-codex-lb-config.js';
32
+ import { CODEX_LB_DESKTOP_BRIDGE_MARKER, CODEX_LB_DESKTOP_COMPAT_MARKER, CODEX_LB_PROVIDER_SELECTION_MARKER, appliedCodexLbPersistenceModes, captureCodexLbSetupWriteState, codexLbSharedOpenAiRoutingState, detectCodexLbSetupDrift, ensureGlobalCodexAppGlmProfile, ensureStoredOpenRouterProviderDuringInstall, hardenCodexLbSetupRecoveryPath, removeCodexLbSetupRecoveryPath, removeCodexLbOrphanManagedMarkers, removeCodexLbSharedOpenAiRouting, removeCodexLbManagedDesktopConfig, restoreCodexLbSetupWriteState, sha256Text, shellSingleQuote, upsertCodexAppGlmConfig, upsertCodexLbConfig, upsertCodexLbCliProviderConfig, upsertCodexLbCompatDesktopConfig, upsertCodexLbNativeDesktopConfig, upsertCodexLbSharedOpenAiRouting, writeCodexLbSetupFileIfUnchanged } from './install-helpers-codex-lb-config.js';
33
33
  import { detectLegacyCodexLbDesktopState } from '../core/codex-lb/legacy-migration.js';
34
34
  import { ensureCodexImagegenDuringInstall, ensureGlobalCodexSkillsDuringInstall, ensureGlobalContext7DuringInstall, ensureGlobalGetdesignSkillDuringInstall, ensureSksCommandDuringInstall } from './install-helpers-install-support.js';
35
35
  import { escapeRegExp } from '../core/text/regex.js';
@@ -236,7 +236,7 @@ async function reportPostinstallCodexLbAuth(snapshot = null) {
236
236
  else if (codexLbAuth.status === 'skipped')
237
237
  console.log(`codex-lb auth: skipped (${codexLbAuth.reason}).`);
238
238
  else if (codexLbAuth.status === 'missing_env_key')
239
- console.log('codex-lb auth: stored key missing. Run `sks codex-lb setup --host <domain> --api-key-stdin` to repair.');
239
+ console.log('codex-lb auth: stored key missing. Store it in ~/.codex/sks-codex-lb.env, run `sks codex-lb setup --host <domain> --api-key-stdin --yes`, or provide CODEX_LB_API_KEY in the environment.');
240
240
  else if (codexLbAuth.status === 'missing_base_url')
241
241
  console.log('codex-lb auth: stored key has no recoverable base URL. Run `sks codex-lb reconfigure --host <domain> --api-key-stdin` once.');
242
242
  else if (codexLbAuth.status === 'legacy_migration_required')
@@ -557,17 +557,16 @@ function codexLbRoutingPlane(mode) {
557
557
  return 'desktop_compat_provider';
558
558
  return 'cli_provider';
559
559
  }
560
- async function capturePostinstallCodexLbConfigSnapshot(home = process.env.HOME || os.homedir()) {
560
+ export async function capturePostinstallCodexLbConfigSnapshot(home = process.env.HOME || os.homedir()) {
561
561
  const configPath = codexLbConfigPath(home);
562
562
  const envPath = codexLbEnvPath(home);
563
563
  const authPath = codexAuthPath(home);
564
564
  const config = await readText(configPath, '');
565
- const envText = await readText(envPath, '');
566
565
  const auth = await captureCodexAuthSnapshot({ home, authPath });
567
566
  const envLoad = await loadCodexLbEnv({ home, envPath });
568
567
  const envKey = envLoad.secret_api_key || '';
569
568
  const providerConfigured = /\[model_providers\.codex-lb\]/.test(config);
570
- const baseUrl = envLoad.base_url || codexLbProviderBaseUrl(config) || parseCodexLbEnvBaseUrl(envText);
569
+ const baseUrl = envLoad.base_url || codexLbProviderBaseUrl(config);
571
570
  const desktopMode = managedCodexLbDesktopMode(config);
572
571
  const bridgeBaseUrl = desktopMode === 'desktop-native-bridge'
573
572
  ? topLevelTomlString(config, 'openai_base_url')
@@ -590,7 +589,7 @@ async function capturePostinstallCodexLbConfigSnapshot(home = process.env.HOME |
590
589
  secret_api_key: envKey || null
591
590
  };
592
591
  }
593
- async function restorePostinstallCodexLbConfigSnapshot(snapshot) {
592
+ export async function restorePostinstallCodexLbConfigSnapshot(snapshot) {
594
593
  if (!snapshot)
595
594
  return { status: 'skipped', reason: 'no_snapshot' };
596
595
  let configRestored = false;
@@ -608,15 +607,12 @@ async function restorePostinstallCodexLbConfigSnapshot(snapshot) {
608
607
  else if (snapshot.desktop_mode === 'desktop-dual-auth-compat') {
609
608
  next = upsertCodexLbCompatDesktopConfig(current, { remoteBaseUrl: snapshot.base_url });
610
609
  }
611
- else if (snapshot.selected !== true) {
610
+ else {
612
611
  next = upsertCodexLbCliProviderConfig(current, {
613
612
  remoteBaseUrl: snapshot.base_url,
614
- selectGlobally: false
613
+ selectGlobally: snapshot.selected === true
615
614
  });
616
615
  }
617
- else {
618
- configStatus = 'legacy_migration_required';
619
- }
620
616
  }
621
617
  catch (error) {
622
618
  return {
@@ -629,7 +625,7 @@ async function restorePostinstallCodexLbConfigSnapshot(snapshot) {
629
625
  error: error instanceof Error ? error.message : String(error)
630
626
  };
631
627
  }
632
- if (configStatus !== 'legacy_migration_required' && ensureTrailingNewline(next) !== ensureTrailingNewline(current)) {
628
+ if (ensureTrailingNewline(next) !== ensureTrailingNewline(current)) {
633
629
  const safeWrite = await safeWriteCodexConfigToml(snapshot.config_path, current, next, 'codex-lb-safe-routing-restore', { verifyUnchangedBeforeWrite: true });
634
630
  configRestored = safeWrite.ok && safeWrite.changed === true;
635
631
  configStatus = safeWrite.ok ? (configRestored ? 'restored' : 'present') : safeWrite.status;
@@ -728,7 +724,11 @@ export async function configureCodexLb(opts = {}) {
728
724
  ? Buffer.from(String(configBeforeEntry.bytes_base64 || ''), 'base64').toString('utf8')
729
725
  : '';
730
726
  const preservedDesktopMode = managedCodexLbDesktopMode(initialConfig);
731
- const preservedLegacySelection = hasTopLevelCodexLbSelected(initialConfig) && !preservedDesktopMode;
727
+ const managedCliSelection = hasTopLevelMarker(initialConfig, CODEX_LB_PROVIDER_SELECTION_MARKER)
728
+ && hasTopLevelCodexLbSelected(initialConfig);
729
+ const preservedLegacySelection = hasTopLevelCodexLbSelected(initialConfig)
730
+ && !preservedDesktopMode
731
+ && !managedCliSelection;
732
732
  const desktopMode = preservedDesktopMode || 'cli-provider';
733
733
  if (desktopModeRequiresGlobalSecretEnvironment(desktopMode)) {
734
734
  return {
@@ -739,11 +739,15 @@ export async function configureCodexLb(opts = {}) {
739
739
  error: 'desktop_dual_auth_compat_requires_global_secret_environment'
740
740
  };
741
741
  }
742
- const useDefaultProvider = desktopMode === 'desktop-dual-auth-compat' || preservedLegacySelection;
742
+ const useDefaultProvider = desktopMode === 'desktop-dual-auth-compat'
743
+ || preservedLegacySelection
744
+ || managedCliSelection
745
+ || (desktopMode === 'cli-provider' && opts.useDefaultProvider === true);
743
746
  const setupAnswers = {
744
747
  host_or_base_url: rawHost,
745
748
  api_key_source: opts.apiKeySource || 'stdin',
746
749
  desktop_mode: 'cli-provider',
750
+ use_as_default_provider: useDefaultProvider,
747
751
  gateway_auth_transport: (opts.gatewayAuthTransport || DEFAULT_CODEX_LB_GATEWAY_AUTH_TRANSPORT),
748
752
  write_env_file: writeEnvFile,
749
753
  store_keychain: storeKeychain,
@@ -823,7 +827,7 @@ export async function configureCodexLb(opts = {}) {
823
827
  else if (!preservedLegacySelection) {
824
828
  providerOnly = upsertCodexLbCliProviderConfig(current, {
825
829
  remoteBaseUrl: baseUrl,
826
- selectGlobally: false
830
+ selectGlobally: useDefaultProvider
827
831
  });
828
832
  }
829
833
  }
@@ -1235,7 +1239,7 @@ export async function configureCodexLb(opts = {}) {
1235
1239
  }
1236
1240
  const skipLaunchEnvironment = opts.syncLaunchEnv === false
1237
1241
  || process.env.SKS_SKIP_CODEX_LB_LAUNCH_ENV === '1';
1238
- const { purgeStaleCodexLbCredentialTwins, syncDesktopCenterLaunchCredentials, verifyCodexLbLegacyKeychainReplacementStore } = await import('../core/codex-lb/desktop-center-credentials.js');
1242
+ const { repairCodexLbLegacyKeychainMigration, syncDesktopCenterLaunchCredentials } = await import('../core/codex-lb/desktop-center-credentials.js');
1239
1243
  const centerCredentials = skipLaunchEnvironment
1240
1244
  ? {
1241
1245
  schema: 'sks.codex-lb-desktop-center-credentials.v1',
@@ -1493,43 +1497,36 @@ export async function configureCodexLb(opts = {}) {
1493
1497
  };
1494
1498
  if ((opts.platform || process.platform) === 'darwin' && writeEnvFile && !storeKeychain) {
1495
1499
  await opts.testHooks?.beforeLegacyKeychainCleanup?.({ envPath, metadataPath });
1496
- const replacement = await verifyCodexLbLegacyKeychainReplacementStore({
1500
+ const migration = await repairCodexLbLegacyKeychainMigration({
1497
1501
  home,
1502
+ baseUrl,
1498
1503
  envPath,
1499
1504
  metadataPath,
1500
1505
  account: opts.account || process.env.USER || 'sks',
1501
- expectedApiKeySha256: keyFingerprint
1502
- });
1503
- const cleanup = await purgeStaleCodexLbCredentialTwins({
1504
- home,
1505
- account: opts.account || process.env.USER || 'sks',
1506
- platform: 'darwin',
1507
- skipStaleEnvTwins: true,
1506
+ platform: opts.platform || process.platform,
1508
1507
  ...(opts.securityBin ? { securityBin: opts.securityBin } : {}),
1509
1508
  ...(opts.runProcessImpl ? { runProcessImpl: opts.runProcessImpl } : {}),
1510
- ...(replacement.receipt
1511
- ? { legacyKeychainMigrationReceipt: replacement.receipt }
1512
- : {})
1509
+ expectedApiKeySha256: keyFingerprint
1513
1510
  });
1514
- const cleanupBlockers = [
1515
- ...(!replacement.ok && replacement.blockers.length === 0
1516
- ? ['legacy_keychain_replacement_store_unverified']
1517
- : replacement.blockers),
1518
- ...cleanup.blockers
1519
- ];
1520
- const cleanupOk = replacement.ok && cleanupBlockers.length === 0;
1511
+ const cleanupBlockers = [...new Set(migration.blockers)];
1512
+ const replacementStoreVerified = migration.env_key_valid
1513
+ && migration.status !== 'replacement_store_unverified'
1514
+ && migration.status !== 'env_file_unsafe';
1515
+ const cleanupOk = replacementStoreVerified
1516
+ && migration.ok
1517
+ && cleanupBlockers.length === 0;
1521
1518
  legacyKeychainCleanup = {
1522
1519
  ok: cleanupOk,
1523
- status: !replacement.ok
1520
+ status: !replacementStoreVerified
1524
1521
  ? 'legacy_keychain_cleanup_blocked_replacement_store_unverified'
1525
- : cleanupBlockers.length === 0
1526
- ? cleanup.keychain_cleared.length > 0
1522
+ : cleanupOk
1523
+ ? migration.keychain_cleared.length > 0
1527
1524
  ? 'legacy_keychain_removed'
1528
1525
  : 'legacy_keychain_absent'
1529
1526
  : 'legacy_keychain_cleanup_failed_secure_store_retained',
1530
- replacement_store_verified: replacement.ok,
1531
- keychain_cleared: cleanup.keychain_cleared,
1532
- blockers: [...new Set(cleanupBlockers)]
1527
+ replacement_store_verified: replacementStoreVerified,
1528
+ keychain_cleared: migration.keychain_cleared,
1529
+ blockers: cleanupBlockers
1533
1530
  };
1534
1531
  appliedActions.push({
1535
1532
  type: 'migrate_legacy_keychain',
@@ -1601,9 +1598,10 @@ export async function codexLbStatus(opts = {}) {
1601
1598
  const home = opts.home || process.env.HOME || os.homedir();
1602
1599
  const configPath = opts.configPath || codexLbConfigPath(home);
1603
1600
  const envPath = opts.envPath || codexLbEnvPath(home);
1604
- const config = await readText(configPath, '');
1601
+ const rawConfig = await readText(configPath, '');
1602
+ const orphanManagedMarkerCleanup = removeCodexLbOrphanManagedMarkers(rawConfig);
1603
+ const config = orphanManagedMarkerCleanup.text;
1605
1604
  const envExists = await exists(envPath);
1606
- const envText = envExists ? await readText(envPath, '') : '';
1607
1605
  const envLoad = await loadCodexLbEnv({ ...opts, home, envPath });
1608
1606
  const authPath = opts.authPath || codexAuthPath(home);
1609
1607
  const authText = await readText(authPath, '');
@@ -1638,6 +1636,7 @@ export async function codexLbStatus(opts = {}) {
1638
1636
  && providerSupportsWebsockets === true
1639
1637
  && providerOpenAiAuthDisabled;
1640
1638
  const retiredCompatConfigured = desktopMode === 'desktop-dual-auth-compat';
1639
+ const definedButNotSelected = providerConfigured && !selected && desktopMode === null;
1641
1640
  const providerContractOk = !retiredCompatConfigured && cliProviderContractOk;
1642
1641
  const providerUsesCodexLbEnvAuth = providerConfigured && providerEnvKey === CODEX_LB_PROVIDER_ENV_KEY && providerOpenAiAuthDisabled;
1643
1642
  const oauthAvailable = authMode.mode === 'chatgpt_oauth' || authMode.mode === 'browser_marker';
@@ -1682,9 +1681,23 @@ export async function codexLbStatus(opts = {}) {
1682
1681
  allowUnverified: opts.allowUnverifiedToolOutputRecovery === true
1683
1682
  || codexLbToolOutputRecoveryOverrideAcknowledged({ env: opts.env || process.env })
1684
1683
  });
1684
+ const secretResolution = {
1685
+ source: envLoad.source,
1686
+ path: envLoad.source === 'env-file' ? envLoad.env_paths[0] || envPath : null,
1687
+ prompt_risk: 'none'
1688
+ };
1685
1689
  return {
1686
1690
  ok: providerReady && (!selected || !probeToolOutputRecovery || toolOutputRecovery.ok),
1687
1691
  blockers,
1692
+ warnings: definedButNotSelected ? ['codex_lb_defined_but_not_selected'] : [],
1693
+ orphan_managed_markers: orphanManagedMarkerCleanup.orphan_markers,
1694
+ managed_marker_cleanup_required: orphanManagedMarkerCleanup.changed,
1695
+ activation_guidance: definedButNotSelected
1696
+ ? [
1697
+ 'Run `sks codex-lb use-cli` to select the CLI provider.',
1698
+ 'Run `sks codex-lb use-desktop-full` to activate managed Desktop bridge mode.'
1699
+ ]
1700
+ : [],
1688
1701
  provider_ready: providerReady,
1689
1702
  config_path: configPath,
1690
1703
  env_path: envPath,
@@ -1717,10 +1730,13 @@ export async function codexLbStatus(opts = {}) {
1717
1730
  source: envLoad.source,
1718
1731
  source_priority: envLoad.source_priority,
1719
1732
  api_key: envLoad.api_key,
1733
+ blockers: envLoad.blockers,
1734
+ guidance: envLoad.guidance,
1720
1735
  credential_binding: envLoad.credential_binding,
1721
1736
  keychain: envLoad.keychain,
1722
1737
  env_paths: envLoad.env_paths
1723
1738
  },
1739
+ secret_resolution: secretResolution,
1724
1740
  base_url: baseUrl,
1725
1741
  auth_path: authPath,
1726
1742
  auth_mode: authMode.mode,
@@ -1746,6 +1762,19 @@ export async function codexLbStatus(opts = {}) {
1746
1762
  export function formatCodexLbStatusText(status = {}, opts = {}) {
1747
1763
  const backupPresent = Boolean(opts.backupPresent);
1748
1764
  const backupPath = opts.backupPath || '';
1765
+ const resolution = status.secret_resolution || {};
1766
+ const resolutionSource = String(resolution.source || status.env_loader?.source || 'missing');
1767
+ const resolutionPath = resolution.path
1768
+ || (resolutionSource === 'env-file' ? status.env_loader?.env_paths?.[0] || status.env_path : null);
1769
+ const displayResolutionPath = resolutionPath
1770
+ ? formatCodexLbStatusPath(String(resolutionPath), opts.home || os.homedir())
1771
+ : '';
1772
+ const resolutionLabel = displayResolutionPath
1773
+ ? `${resolutionSource} (${displayResolutionPath})`
1774
+ : resolutionSource;
1775
+ const keychainUsage = resolutionSource === 'keychain' ? 'used' : 'not used';
1776
+ const promptRisk = String(resolution.prompt_risk
1777
+ || (resolutionSource === 'keychain' ? 'possible' : 'none'));
1749
1778
  const lines = [
1750
1779
  'SKS codex-lb',
1751
1780
  '',
@@ -1755,7 +1784,8 @@ export function formatCodexLbStatusText(status = {}, opts = {}) {
1755
1784
  `Provider OpenAI Auth: ${status.provider_requires_openai_auth ? 'required' : 'not required/drifted'} (${status.provider_name || 'missing'})`,
1756
1785
  `Codex App auth: ${status.auth_usable_for_codex_app ? 'ok' : 'needs sign-in/repair'} (${status.auth_mode || 'unknown'})`,
1757
1786
  `Shared OpenAI routing: ${status.shared_openai_routing?.safe === false ? 'unsafe' : status.shared_openai_routing?.status || 'unknown'}${status.shared_openai_routing?.managed ? ' (sks-managed)' : ''}`,
1758
- `Auth/routing coherent: ${status.auth_routing_coherent ? 'yes' : 'no'}`
1787
+ `Auth/routing coherent: ${status.auth_routing_coherent ? 'yes' : 'no'}`,
1788
+ `Key source: ${resolutionLabel} · keychain: ${keychainUsage} · prompt risk: ${promptRisk}`
1759
1789
  ];
1760
1790
  if (status.tool_output_recovery?.status && status.tool_output_recovery.status !== 'not_selected') {
1761
1791
  const recovery = status.tool_output_recovery;
@@ -1777,13 +1807,18 @@ export function formatCodexLbStatusText(status = {}, opts = {}) {
1777
1807
  if (status.base_url)
1778
1808
  lines.push(`Base URL: ${status.base_url}`);
1779
1809
  lines.push(`ChatGPT backup: ${backupPresent ? `yes (${backupPath})` : 'no'}`);
1810
+ if ((status.warnings || []).includes('codex_lb_defined_but_not_selected')) {
1811
+ lines.push('', 'Warning [codex_lb_defined_but_not_selected]: the codex-lb provider is defined but not selected.');
1812
+ for (const action of status.activation_guidance || [])
1813
+ lines.push(` ${action}`);
1814
+ }
1780
1815
  if (status.legacy_migration_required || status.shared_openai_routing?.safe === false)
1781
1816
  lines.push('', 'Run: sks codex-lb migrate-legacy-desktop --restart-app. Ordinary repair will not rewrite shared Codex auth.');
1782
1817
  else if (status.provider_configured && !status.provider_contract_ok)
1783
1818
  lines.push('', 'Run: sks codex-lb repair to rewrite the provider block to the current codex-lb App contract.');
1784
1819
  else if (status.desktop_mode === 'desktop-native-bridge' && !status.auth_usable_for_codex_app)
1785
1820
  lines.push('', 'Sign in with ChatGPT OAuth, then run: sks codex-lb use-desktop-full.');
1786
- else if (status.ok && status.desktop_mode === 'cli-provider' && !status.selected)
1821
+ else if (status.ok && status.desktop_mode === 'cli-provider' && !status.selected && !(status.warnings || []).includes('codex_lb_defined_but_not_selected'))
1787
1822
  lines.push('', 'CLI provider is stored but unselected. Run `sks codex-lb use-cli` for CLI use or `sks codex-lb use-desktop-full` for managed Desktop routing.');
1788
1823
  else if (status.desktop_mode === 'desktop-dual-auth-compat')
1789
1824
  lines.push('', 'The retired compatibility route is blocked. Run `sks codex-lb use-desktop-full` or `sks codex-lb disable`.');
@@ -1791,12 +1826,22 @@ export function formatCodexLbStatusText(status = {}, opts = {}) {
1791
1826
  lines.push('', 'Status: configured; no ordinary repair needed.');
1792
1827
  else if (!status.ok && status.base_url && status.env_key_configured)
1793
1828
  lines.push('', 'Run: sks codex-lb repair. To change routing explicitly, use `use-desktop-full`, `use-cli`, or `disable`.');
1829
+ else if (!status.ok && !status.env_key_configured)
1830
+ lines.push('', 'Gateway key missing. Store CODEX_LB_API_KEY in ~/.codex/sks-codex-lb.env, run `sks codex-lb setup --host <domain> --api-key-stdin --yes`, or provide CODEX_LB_API_KEY in the environment.');
1794
1831
  else if (!status.ok)
1795
1832
  lines.push('', 'Run: sks codex-lb setup --host <domain> --api-key-stdin');
1796
1833
  if (backupPresent)
1797
1834
  lines.push('Legacy OAuth backup detected; use `sks codex-lb migrate-legacy-desktop --restart-app` instead of background auth switching.');
1798
1835
  return `${lines.join('\n')}\n`;
1799
1836
  }
1837
+ function formatCodexLbStatusPath(file, home) {
1838
+ const resolvedFile = path.resolve(file);
1839
+ const resolvedHome = path.resolve(String(home || ''));
1840
+ const relative = path.relative(resolvedHome, resolvedFile);
1841
+ return relative && relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative)
1842
+ ? `~/${relative.split(path.sep).join('/')}`
1843
+ : file;
1844
+ }
1800
1845
  export function formatCodexLbRepairResultText(result = {}) {
1801
1846
  const lines = [
1802
1847
  'codex-lb credential/provider configuration repaired without changing Codex shared auth.',