sneakoscope 4.7.1 → 4.7.4
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 +11 -11
- 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/command-registry.js +1 -1
- package/dist/cli/install-helpers.js +215 -57
- package/dist/commands/codex-lb.js +143 -7
- package/dist/commands/doctor.js +59 -2
- 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/sks-menubar.js +445 -0
- package/dist/core/codex-app.js +239 -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/commands/basic-cli.js +14 -7
- package/dist/core/doctor/doctor-dirty-planner.js +2 -0
- package/dist/core/doctor/doctor-readiness-matrix.js +4 -0
- 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 +2 -2
- package/dist/core/update-check.js +67 -18
- 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/dist/scripts/sks-menubar-install-check.js +59 -0
- package/dist/scripts/update-default-command-check.js +28 -0
- package/package.json +5 -1
package/dist/core/codex-app.js
CHANGED
|
@@ -6,6 +6,10 @@ import { EMPTY_CODEX_INFO, getCodexInfo } from './codex-adapter.js';
|
|
|
6
6
|
import { CODEX_CHROME_EXTENSION_DOC_URL, DEFAULT_CODEX_APP_PLUGINS as DEFAULT_CODEX_APP_PLUGIN_TUPLES, RESERVED_CODEX_PLUGIN_SKILL_NAMES } from './routes.js';
|
|
7
7
|
import { PRODUCT_DESIGN_PLUGIN, normalizeProductDesignPluginEvidence } from './product-design-plugin.js';
|
|
8
8
|
import { PRODUCT_DESIGN_AUTO_INSTALL_ENV, ensureProductDesignPluginInstalled, productDesignAutoInstallRequested } from './product-design-app-server.js';
|
|
9
|
+
import { GLM_CODEX_CONFIG_PROVIDER_ID, GLM_CODEX_CONFIG_REASONING_PROFILES } from './codex-app/glm-model-profile.js';
|
|
10
|
+
import { GLM_52_OPENROUTER_MODEL } from './providers/glm/glm-52-settings.js';
|
|
11
|
+
import { resolveOpenRouterApiKey } from './providers/openrouter/openrouter-secret-store.js';
|
|
12
|
+
import { codexLbEnvPath, parseShellEnvValue } from './codex-lb/codex-lb-env.js';
|
|
9
13
|
export const CODEX_APP_DOCS_URL = 'https://developers.openai.com/codex/app/features';
|
|
10
14
|
export const CODEX_CHANGELOG_URL = 'https://developers.openai.com/codex/changelog';
|
|
11
15
|
export const CODEX_ACCESS_TOKENS_DOCS_URL = 'https://developers.openai.com/codex/enterprise/access-tokens';
|
|
@@ -133,6 +137,8 @@ export async function codexAppIntegrationStatus(opts = {}) {
|
|
|
133
137
|
const productDesignPlugin = await codexProductDesignPluginStatus(opts);
|
|
134
138
|
const pluginSkillShadows = await codexPluginSkillShadowStatus(opts);
|
|
135
139
|
const fastModeConfig = await codexFastModeConfigStatus(opts);
|
|
140
|
+
const providerModelUi = await codexProviderModelUiStatus(opts);
|
|
141
|
+
const nativeSksMenu = codexNativeSksMenuStatus();
|
|
136
142
|
const computerUseMcpListed = /computer[-_ ]?use/i.test(mcpText);
|
|
137
143
|
const browserUseMcpListed = /browser[-_ ]?use/i.test(mcpText);
|
|
138
144
|
const imageGenerationReady = codexFeatureEnabled(featureText, 'image_generation');
|
|
@@ -161,7 +167,8 @@ export async function codexAppIntegrationStatus(opts = {}) {
|
|
|
161
167
|
app: {
|
|
162
168
|
installed: appInstalled,
|
|
163
169
|
path: appPath,
|
|
164
|
-
docs_url: CODEX_APP_DOCS_URL
|
|
170
|
+
docs_url: CODEX_APP_DOCS_URL,
|
|
171
|
+
sks_menu: nativeSksMenu
|
|
165
172
|
},
|
|
166
173
|
codex_cli: {
|
|
167
174
|
ok: Boolean(codex.bin),
|
|
@@ -186,6 +193,7 @@ export async function codexAppIntegrationStatus(opts = {}) {
|
|
|
186
193
|
required_flags: requiredFeatureFlags,
|
|
187
194
|
required_flags_ok: requiredFeatureFlagsOk,
|
|
188
195
|
fast_mode_config: fastModeConfig,
|
|
196
|
+
provider_model_ui: providerModelUi,
|
|
189
197
|
git_actions: gitActions,
|
|
190
198
|
image_generation: imageGenerationReady,
|
|
191
199
|
image_generation_source: imageGenerationReady ? 'codex_features_list' : 'missing',
|
|
@@ -223,7 +231,7 @@ export async function codexAppIntegrationStatus(opts = {}) {
|
|
|
223
231
|
}
|
|
224
232
|
},
|
|
225
233
|
chrome_extension: chromeExtension,
|
|
226
|
-
guidance: codexAppGuidance({ appInstalled, codex, mcpList, featureList, requiredFeatureFlags, requiredFeatureFlagsOk, defaultPlugins, productDesignPlugin, pluginSkillShadows, fastModeConfig, gitActions, imageGenerationReady, inAppBrowserReady, browserUseFeatureReady, computerUseReady, browserUseReady, browserToolReady, computerUseMcpListed, browserUseMcpListed, chromeExtension, remoteControl })
|
|
234
|
+
guidance: codexAppGuidance({ appInstalled, codex, mcpList, featureList, requiredFeatureFlags, requiredFeatureFlagsOk, defaultPlugins, productDesignPlugin, pluginSkillShadows, fastModeConfig, providerModelUi, nativeSksMenu, gitActions, imageGenerationReady, inAppBrowserReady, browserUseFeatureReady, computerUseReady, browserUseReady, browserToolReady, computerUseMcpListed, browserUseMcpListed, chromeExtension, remoteControl })
|
|
227
235
|
};
|
|
228
236
|
}
|
|
229
237
|
export async function codexChromeExtensionStatus(opts = {}) {
|
|
@@ -442,7 +450,29 @@ export function codexAccessTokenStatus(env = process.env) {
|
|
|
442
450
|
: ['No CODEX_ACCESS_TOKEN detected in the current process environment. This is fine for interactive ChatGPT login or API-key auth.']
|
|
443
451
|
};
|
|
444
452
|
}
|
|
445
|
-
|
|
453
|
+
function codexNativeSksMenuStatus() {
|
|
454
|
+
return {
|
|
455
|
+
schema: 'sks.codex-app-native-menu.v1',
|
|
456
|
+
supported: false,
|
|
457
|
+
status: 'unsupported_official_api',
|
|
458
|
+
requested_label: 'SKS',
|
|
459
|
+
requested_position: 'between Window and Help',
|
|
460
|
+
companion_supported: process.platform === 'darwin',
|
|
461
|
+
companion_position: 'right macOS menu bar status item',
|
|
462
|
+
companion_install_command: 'sks doctor --fix',
|
|
463
|
+
alternatives: [
|
|
464
|
+
'sks doctor --fix',
|
|
465
|
+
'sks codex-lb use-codex-lb',
|
|
466
|
+
'sks codex-lb use-oauth',
|
|
467
|
+
'sks codex-app set-openrouter-key --api-key-stdin',
|
|
468
|
+
'codex://settings'
|
|
469
|
+
],
|
|
470
|
+
evidence: [
|
|
471
|
+
'Codex official docs expose settings deep links, slash commands, skills/plugins, and app-server/remote-control surfaces, not third-party native macOS menu insertion.'
|
|
472
|
+
]
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
export function codexAppGuidance({ appInstalled, codex, mcpList, featureList, requiredFeatureFlags = {}, requiredFeatureFlagsOk = true, defaultPlugins = { ok: true, missing_enabled: [] }, productDesignPlugin = null, pluginSkillShadows = { ok: true, blocking: [] }, fastModeConfig = { ok: true, blockers: [] }, providerModelUi = null, nativeSksMenu = null, gitActions = { ok: true, blockers: [] }, imageGenerationReady, inAppBrowserReady, browserUseFeatureReady, computerUseReady, browserUseReady, browserToolReady, computerUseMcpListed, browserUseMcpListed, chromeExtension, remoteControl }) {
|
|
446
476
|
const lines = [];
|
|
447
477
|
if (!appInstalled) {
|
|
448
478
|
lines.push('Install and open Codex App for first-party MCP/plugin tools. SKS Zellij launch can still run with Codex CLI alone, but Codex Computer Use and imagegen/gpt-image-2 evidence will be unavailable until Codex App is ready.');
|
|
@@ -503,6 +533,28 @@ export function codexAppGuidance({ appInstalled, codex, mcpList, featureList, re
|
|
|
503
533
|
lines.push(`Codex App speed selector can be hidden or locked by config: ${fastModeConfig.blockers.join(', ')}.`);
|
|
504
534
|
lines.push('Run: sks doctor --fix');
|
|
505
535
|
}
|
|
536
|
+
if (providerModelUi?.glm?.exposed && providerModelUi.glm.openrouter_key_present) {
|
|
537
|
+
lines.push(`GLM model selector profile is exposed: ${providerModelUi.glm.model} via profiles ${providerModelUi.glm.profiles_present.join(', ')}.`);
|
|
538
|
+
}
|
|
539
|
+
else if (providerModelUi?.glm?.exposed) {
|
|
540
|
+
lines.push(`GLM model selector profile is exposed for ${providerModelUi.glm.model}, but the OpenRouter key is missing.`);
|
|
541
|
+
lines.push(`Run: ${providerModelUi.glm.key_command}; restart Codex App if the model picker was already open.`);
|
|
542
|
+
}
|
|
543
|
+
else if (providerModelUi?.glm) {
|
|
544
|
+
lines.push(`GLM model selector profile is not exposed yet: ${providerModelUi.glm.blockers.join(', ') || 'profile setup required'}.`);
|
|
545
|
+
lines.push(`Run: ${providerModelUi.glm.key_command}; then ${providerModelUi.glm.install_command}; restart Codex App if the model picker was already open.`);
|
|
546
|
+
}
|
|
547
|
+
if (providerModelUi?.codex_lb?.key_entry_visible && providerModelUi.codex_lb.key_present && providerModelUi.codex_lb.provider_present && providerModelUi.codex_lb.provider_contract_ok && providerModelUi.codex_lb.base_url_present) {
|
|
548
|
+
lines.push(`codex-lb key entry path is visible and configured; update command ${providerModelUi.codex_lb.set_key_command}.`);
|
|
549
|
+
}
|
|
550
|
+
else if (providerModelUi?.codex_lb?.key_entry_visible) {
|
|
551
|
+
lines.push(`codex-lb key entry path is visible, but setup is incomplete: ${providerModelUi.codex_lb.blockers.join(', ') || 'setup required'}.`);
|
|
552
|
+
lines.push(`Run: ${providerModelUi.codex_lb.setup_command} or ${providerModelUi.codex_lb.set_key_command}.`);
|
|
553
|
+
}
|
|
554
|
+
else if (providerModelUi?.codex_lb) {
|
|
555
|
+
lines.push(`codex-lb key entry path is not exposed yet: ${providerModelUi.codex_lb.blockers.join(', ') || 'setup required'}.`);
|
|
556
|
+
lines.push(`Run: ${providerModelUi.codex_lb.setup_command} or ${providerModelUi.codex_lb.set_key_command}.`);
|
|
557
|
+
}
|
|
506
558
|
if (!gitActions?.ok) {
|
|
507
559
|
lines.push(`Codex App git commit/push actions are blocked: ${gitActions?.blockers?.join(', ') || 'git action readiness'}. The app Commit, Push, Commit and Push, and PR flows need codex_git_commit, hooks, and Codex CLI remote-control support.`);
|
|
508
560
|
lines.push(`Run: sks doctor --fix; if remote-control is still blocked, update Codex CLI to ${CODEX_REMOTE_CONTROL_MIN_VERSION}+ and restart older app-server/TUI sessions.`);
|
|
@@ -510,6 +562,9 @@ export function codexAppGuidance({ appInstalled, codex, mcpList, featureList, re
|
|
|
510
562
|
else {
|
|
511
563
|
lines.push('Codex App git actions are enabled for Commit, Push, Commit and Push, and PR flows; SKS hooks treat those app metadata actions as lightweight git UI actions.');
|
|
512
564
|
}
|
|
565
|
+
if (nativeSksMenu?.supported === false) {
|
|
566
|
+
lines.push('Codex App does not expose a documented third-party API for inserting SKS between Window and Help, so SKS uses a right-side macOS menu bar companion instead. Run `sks doctor --fix` to install or refresh it.');
|
|
567
|
+
}
|
|
513
568
|
if (appInstalled && (!computerUseReady || !browserToolReady)) {
|
|
514
569
|
lines.push('Open Codex App settings and enable recommended MCP/plugin tools. Codex CLI 0.130.0+ remote-control/app-server sessions can pick up config changes live; restart older CLI/TUI sessions.');
|
|
515
570
|
lines.push(`Required for SKS web QA/UX/browser evidence: Codex Chrome Extension first (${CODEX_CHROME_EXTENSION_SETUP_DOCS_URL}). Computer Use is reserved for native Mac/non-web surfaces.`);
|
|
@@ -551,6 +606,10 @@ export function formatCodexAppStatus(status, { includeRaw = false } = {}) {
|
|
|
551
606
|
`Remote Ctrl: ${status.remote_control?.ok ? 'ok' : 'missing'}${status.remote_control?.codex_cli?.version_number ? ` min ${status.remote_control.min_version}` : ''}`,
|
|
552
607
|
`App Flags: ${status.features?.required_flags_ok ? 'ok' : `missing ${missingRequiredFeatureFlags(status.features?.required_flags).join(', ') || 'required flags'}`}`,
|
|
553
608
|
`Fast UI: ${status.features?.fast_mode_config?.ok ? 'ok' : `locked ${(status.features?.fast_mode_config?.blockers || []).join(', ') || 'config'}`}`,
|
|
609
|
+
`SKS Menu: ${sksNativeMenuSummary(status.app?.sks_menu)}`,
|
|
610
|
+
`Provider UI:${providerModelUiSummary(status.features?.provider_model_ui)}`,
|
|
611
|
+
`GLM Model: ${glmModelUiSummary(status.features?.provider_model_ui?.glm)}`,
|
|
612
|
+
`codex-lb Key:${codexLbKeyUiSummary(status.features?.provider_model_ui?.codex_lb)}`,
|
|
554
613
|
`Default Plugins:${status.plugins?.default_plugins?.ok ? ' ok' : ` missing ${defaultPluginMissingSummary(status.plugins?.default_plugins) || 'plugin install/config'}`}`,
|
|
555
614
|
`Product Design:${productDesignStatusSummary(status.plugins?.design_product)}`,
|
|
556
615
|
`Plugin Picker:${status.plugins?.picker?.ok ? ' ok' : ` blocked ${pluginPickerBlockers(status).join(', ') || 'config'}`}`,
|
|
@@ -817,6 +876,134 @@ async function codexFastModeConfigStatus(opts = {}) {
|
|
|
817
876
|
blockers
|
|
818
877
|
};
|
|
819
878
|
}
|
|
879
|
+
export async function codexProviderModelUiStatus(opts = {}) {
|
|
880
|
+
const inputEnv = opts.env || process.env;
|
|
881
|
+
const home = opts.home || inputEnv.HOME || os.homedir();
|
|
882
|
+
const cwd = opts.cwd || process.cwd();
|
|
883
|
+
const env = { ...inputEnv, HOME: String(opts.home || inputEnv.HOME || home) };
|
|
884
|
+
const globalConfigPath = opts.configPath || path.join(home || '', '.codex', 'config.toml');
|
|
885
|
+
const projectConfigPath = path.join(cwd || '', '.codex', 'config.toml');
|
|
886
|
+
const globalConfig = await readTextIfExists(globalConfigPath);
|
|
887
|
+
const projectConfig = path.resolve(projectConfigPath) === path.resolve(globalConfigPath)
|
|
888
|
+
? ''
|
|
889
|
+
: await readTextIfExists(projectConfigPath);
|
|
890
|
+
const configText = `${globalConfig}\n${projectConfig}`;
|
|
891
|
+
const openRouterKey = await resolveOpenRouterApiKey({ env }).catch((err) => ({
|
|
892
|
+
key: null,
|
|
893
|
+
source: null,
|
|
894
|
+
key_preview: null,
|
|
895
|
+
blockers: ['openrouter_key_lookup_failed'],
|
|
896
|
+
warnings: [err?.message || 'openrouter key lookup failed']
|
|
897
|
+
}));
|
|
898
|
+
const codexLbEnvFilePath = opts.codexLbEnvPath || codexLbEnvPath(home);
|
|
899
|
+
const codexLbEnvText = await readTextIfExists(codexLbEnvFilePath);
|
|
900
|
+
const codexLbApiKeySource = String(env.CODEX_LB_API_KEY || '').trim()
|
|
901
|
+
? 'process.env'
|
|
902
|
+
: parseShellEnvValue(codexLbEnvText, 'CODEX_LB_API_KEY')
|
|
903
|
+
? 'env-file'
|
|
904
|
+
: 'missing';
|
|
905
|
+
const codexLbBaseUrlSource = String(env.CODEX_LB_BASE_URL || '').trim()
|
|
906
|
+
? 'process.env'
|
|
907
|
+
: parseShellEnvValue(codexLbEnvText, 'CODEX_LB_BASE_URL')
|
|
908
|
+
? 'env-file'
|
|
909
|
+
: 'missing';
|
|
910
|
+
const openrouterProvider = tomlTableAny(configText, [
|
|
911
|
+
`model_providers.${GLM_CODEX_CONFIG_PROVIDER_ID}`,
|
|
912
|
+
`model_providers."${GLM_CODEX_CONFIG_PROVIDER_ID}"`
|
|
913
|
+
]);
|
|
914
|
+
const glmProviderPresent = Boolean(openrouterProvider);
|
|
915
|
+
const profilesPresent = [];
|
|
916
|
+
const profilesMissing = [];
|
|
917
|
+
const glmProfileBlockers = [];
|
|
918
|
+
for (const profile of GLM_CODEX_CONFIG_REASONING_PROFILES) {
|
|
919
|
+
const body = tomlTableAny(configText, [`profiles.${profile.id}`, `profiles."${profile.id}"`]);
|
|
920
|
+
if (!body) {
|
|
921
|
+
profilesMissing.push(profile.id);
|
|
922
|
+
glmProfileBlockers.push(`glm_profile_missing:${profile.id}`);
|
|
923
|
+
continue;
|
|
924
|
+
}
|
|
925
|
+
profilesPresent.push(profile.id);
|
|
926
|
+
if (!hasTomlString(body, 'model_provider', GLM_CODEX_CONFIG_PROVIDER_ID))
|
|
927
|
+
glmProfileBlockers.push(`glm_profile_provider_invalid:${profile.id}`);
|
|
928
|
+
if (!hasTomlString(body, 'model', GLM_52_OPENROUTER_MODEL))
|
|
929
|
+
glmProfileBlockers.push(`glm_profile_model_invalid:${profile.id}`);
|
|
930
|
+
if (!hasTomlString(body, 'model_reasoning_effort', profile.reasoning_effort))
|
|
931
|
+
glmProfileBlockers.push(`glm_profile_reasoning_invalid:${profile.id}`);
|
|
932
|
+
}
|
|
933
|
+
const codexLbProvider = tomlTableAny(configText, ['model_providers.codex-lb', 'model_providers."codex-lb"']);
|
|
934
|
+
const codexLbProviderPresent = Boolean(codexLbProvider);
|
|
935
|
+
const codexLbProviderContractOk = codexLbProviderPresent
|
|
936
|
+
&& hasTomlString(codexLbProvider, 'name', 'openai')
|
|
937
|
+
&& hasTomlString(codexLbProvider, 'wire_api', 'responses')
|
|
938
|
+
&& hasTomlString(codexLbProvider, 'env_key', 'CODEX_LB_API_KEY')
|
|
939
|
+
&& hasTomlBoolean(codexLbProvider, 'supports_websockets', true)
|
|
940
|
+
&& hasTomlBoolean(codexLbProvider, 'requires_openai_auth', true);
|
|
941
|
+
const codexLbSelectedDefault = /(?:^|\n)\s*model_provider\s*=\s*"codex-lb"\s*(?:#.*)?(?=\n|$)/.test(topLevelToml(globalConfig));
|
|
942
|
+
const glmBlockers = [
|
|
943
|
+
...(glmProviderPresent ? [] : ['glm_openrouter_provider_missing']),
|
|
944
|
+
...glmProfileBlockers,
|
|
945
|
+
...(openRouterKey.key ? [] : ['openrouter_key_missing'])
|
|
946
|
+
];
|
|
947
|
+
const codexLbBlockers = [
|
|
948
|
+
...(codexLbProviderPresent ? [] : ['codex_lb_provider_missing']),
|
|
949
|
+
...(codexLbProviderPresent && !codexLbProviderContractOk ? ['codex_lb_provider_contract_drift'] : []),
|
|
950
|
+
...(codexLbApiKeySource !== 'missing' ? [] : ['codex_lb_api_key_missing']),
|
|
951
|
+
...(codexLbBaseUrlSource !== 'missing' ? [] : ['codex_lb_base_url_missing'])
|
|
952
|
+
];
|
|
953
|
+
const blockers = [...glmBlockers, ...codexLbBlockers];
|
|
954
|
+
const keyCommand = 'sks codex-app set-openrouter-key --api-key-stdin';
|
|
955
|
+
const installCommand = 'sks codex-app glm-profile install';
|
|
956
|
+
const setupCommand = 'sks codex-lb setup --host <domain> --api-key-stdin --yes';
|
|
957
|
+
const setKeyCommand = 'sks codex-lb set-key --api-key-stdin';
|
|
958
|
+
return {
|
|
959
|
+
schema: 'sks.codex-app-provider-model-ui.v1',
|
|
960
|
+
ok: blockers.length === 0,
|
|
961
|
+
checked: true,
|
|
962
|
+
status: blockers.length === 0 ? 'ready' : 'setup_required',
|
|
963
|
+
config_paths: {
|
|
964
|
+
codex_config: globalConfigPath,
|
|
965
|
+
project_config: projectConfig ? projectConfigPath : null,
|
|
966
|
+
codex_lb_env: codexLbEnvFilePath
|
|
967
|
+
},
|
|
968
|
+
glm: {
|
|
969
|
+
exposed: glmProviderPresent && profilesMissing.length === 0 && glmProfileBlockers.length === 0,
|
|
970
|
+
provider_present: glmProviderPresent,
|
|
971
|
+
provider: GLM_CODEX_CONFIG_PROVIDER_ID,
|
|
972
|
+
model: GLM_52_OPENROUTER_MODEL,
|
|
973
|
+
profiles_present: profilesPresent,
|
|
974
|
+
profiles_missing: profilesMissing,
|
|
975
|
+
openrouter_key_present: Boolean(openRouterKey.key),
|
|
976
|
+
openrouter_key_source: openRouterKey.source || null,
|
|
977
|
+
openrouter_key_preview: openRouterKey.key_preview || null,
|
|
978
|
+
key_command: keyCommand,
|
|
979
|
+
install_command: installCommand,
|
|
980
|
+
doctor_command: 'sks codex-app glm-profile doctor',
|
|
981
|
+
blockers: glmBlockers
|
|
982
|
+
},
|
|
983
|
+
codex_lb: {
|
|
984
|
+
key_entry_visible: true,
|
|
985
|
+
provider_present: codexLbProviderPresent,
|
|
986
|
+
provider_contract_ok: codexLbProviderContractOk,
|
|
987
|
+
selected_default: codexLbSelectedDefault,
|
|
988
|
+
key_present: codexLbApiKeySource !== 'missing',
|
|
989
|
+
api_key_source: codexLbApiKeySource,
|
|
990
|
+
base_url_present: codexLbBaseUrlSource !== 'missing',
|
|
991
|
+
base_url_source: codexLbBaseUrlSource,
|
|
992
|
+
setup_command: setupCommand,
|
|
993
|
+
set_key_command: setKeyCommand,
|
|
994
|
+
status_command: 'sks codex-lb status',
|
|
995
|
+
blockers: codexLbBlockers
|
|
996
|
+
},
|
|
997
|
+
ui_actions: [
|
|
998
|
+
keyCommand,
|
|
999
|
+
installCommand,
|
|
1000
|
+
setupCommand,
|
|
1001
|
+
setKeyCommand,
|
|
1002
|
+
'sks codex-app status'
|
|
1003
|
+
],
|
|
1004
|
+
blockers
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
820
1007
|
async function readTextIfExists(file) {
|
|
821
1008
|
try {
|
|
822
1009
|
return await fsp.readFile(file, 'utf8');
|
|
@@ -876,6 +1063,38 @@ function productDesignStatusSummary(status = {}) {
|
|
|
876
1063
|
return ' app-server unverified';
|
|
877
1064
|
return ' not checked';
|
|
878
1065
|
}
|
|
1066
|
+
function providerModelUiSummary(status = {}) {
|
|
1067
|
+
if (!status?.checked)
|
|
1068
|
+
return ' not checked';
|
|
1069
|
+
if (status.ok)
|
|
1070
|
+
return ' ok (GLM + codex-lb)';
|
|
1071
|
+
return ` setup ${(status.blockers || []).join(', ') || 'required'}`;
|
|
1072
|
+
}
|
|
1073
|
+
function sksNativeMenuSummary(status = {}) {
|
|
1074
|
+
if (!status)
|
|
1075
|
+
return 'not checked';
|
|
1076
|
+
if (status.supported)
|
|
1077
|
+
return 'ok';
|
|
1078
|
+
return `${status.status || 'unsupported'} (use CLI/settings)`;
|
|
1079
|
+
}
|
|
1080
|
+
function glmModelUiSummary(status = {}) {
|
|
1081
|
+
if (!status)
|
|
1082
|
+
return 'not checked';
|
|
1083
|
+
if (status.exposed) {
|
|
1084
|
+
const key = status.openrouter_key_present ? `key ${status.openrouter_key_source || 'present'}` : 'key missing';
|
|
1085
|
+
return `ok ${status.model} (${status.profiles_present?.length || 0} profiles, ${key})`;
|
|
1086
|
+
}
|
|
1087
|
+
return `setup ${(status.blockers || []).join(', ') || 'profile missing'}; run ${status.key_command || 'sks codex-app set-openrouter-key --api-key-stdin'}`;
|
|
1088
|
+
}
|
|
1089
|
+
function codexLbKeyUiSummary(status = {}) {
|
|
1090
|
+
if (!status)
|
|
1091
|
+
return ' not checked';
|
|
1092
|
+
if (status.key_present && status.provider_contract_ok !== false)
|
|
1093
|
+
return ` configured (${status.api_key_source || 'present'})`;
|
|
1094
|
+
if (status.key_present)
|
|
1095
|
+
return ` setup provider drift; run ${status.setup_command || 'sks codex-lb setup --host <domain> --api-key-stdin --yes'}`;
|
|
1096
|
+
return ` missing (input: ${status.setup_command || 'sks codex-lb setup --host <domain> --api-key-stdin --yes'})`;
|
|
1097
|
+
}
|
|
879
1098
|
function defaultPluginMissingSummary(defaultPlugins = {}) {
|
|
880
1099
|
return [
|
|
881
1100
|
...(defaultPlugins?.missing_installed || []),
|
|
@@ -891,6 +1110,23 @@ function tomlTable(text = '', table = '') {
|
|
|
891
1110
|
const re = new RegExp(`(?:^|\\n)\\[${escapeRegExp(table)}\\]([\\s\\S]*?)(?=\\n\\[[^\\]]+\\]|\\s*$)`);
|
|
892
1111
|
return String(text || '').match(re)?.[1] || '';
|
|
893
1112
|
}
|
|
1113
|
+
function tomlTableAny(text = '', tables = []) {
|
|
1114
|
+
for (const table of tables) {
|
|
1115
|
+
const body = tomlTable(text, table);
|
|
1116
|
+
if (body)
|
|
1117
|
+
return body;
|
|
1118
|
+
}
|
|
1119
|
+
return '';
|
|
1120
|
+
}
|
|
1121
|
+
function hasTomlString(text = '', key = '', value = '') {
|
|
1122
|
+
const re = new RegExp(`(?:^|\\n)\\s*${escapeRegExp(key)}\\s*=\\s*"${escapeRegExp(value)}"\\s*(?:#.*)?(?=\\n|$)`);
|
|
1123
|
+
return re.test(String(text || ''));
|
|
1124
|
+
}
|
|
1125
|
+
function hasTomlBoolean(text = '', key = '', value) {
|
|
1126
|
+
const expected = value ? 'true' : 'false';
|
|
1127
|
+
const re = new RegExp(`(?:^|\\n)\\s*${escapeRegExp(key)}\\s*=\\s*${expected}\\s*(?:#.*)?(?=\\n|$)`);
|
|
1128
|
+
return re.test(String(text || ''));
|
|
1129
|
+
}
|
|
894
1130
|
function escapeRegExp(text = '') {
|
|
895
1131
|
return String(text).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
896
1132
|
}
|
|
@@ -19,12 +19,12 @@ export function buildCodexSdkConfig(input) {
|
|
|
19
19
|
config.model_provider = 'codex-lb';
|
|
20
20
|
config.model_providers = {
|
|
21
21
|
'codex-lb': {
|
|
22
|
-
name: '
|
|
22
|
+
name: 'openai',
|
|
23
23
|
base_url: codexLbBaseUrl,
|
|
24
24
|
wire_api: 'responses',
|
|
25
25
|
env_key: 'CODEX_LB_API_KEY',
|
|
26
26
|
supports_websockets: true,
|
|
27
|
-
requires_openai_auth:
|
|
27
|
+
requires_openai_auth: true
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
30
|
}
|
|
@@ -464,12 +464,12 @@ async function ensurePythonCodexLbConfig(env, config) {
|
|
|
464
464
|
'approval_policy = "never"',
|
|
465
465
|
'',
|
|
466
466
|
'[model_providers.codex-lb]',
|
|
467
|
-
'name = "
|
|
467
|
+
'name = "openai"',
|
|
468
468
|
`base_url = ${tomlQuote(lbBaseUrl)}`,
|
|
469
469
|
'wire_api = "responses"',
|
|
470
470
|
'env_key = "CODEX_LB_API_KEY"',
|
|
471
471
|
'supports_websockets = true',
|
|
472
|
-
'requires_openai_auth =
|
|
472
|
+
'requires_openai_auth = true',
|
|
473
473
|
''
|
|
474
474
|
].join('\n');
|
|
475
475
|
await writeTextAtomic(path.join(codexHome, 'config.toml'), text);
|
|
@@ -14,7 +14,7 @@ export function buildCodexLbSetupPlan(answers, opts = {}) {
|
|
|
14
14
|
if (answers.install_shell_profile !== 'skip' && !answers.write_env_file)
|
|
15
15
|
blockers.push('shell_profile_snippet_requires_env_file');
|
|
16
16
|
const actions = [
|
|
17
|
-
{ type: 'write_config_provider', target: configPath, effect: 'write or update [model_providers.codex-lb] with the normalized base URL, CODEX_LB_API_KEY env_key, and requires_openai_auth=
|
|
17
|
+
{ type: 'write_config_provider', target: configPath, effect: 'write or update [model_providers.codex-lb] with name="openai", the normalized base URL, CODEX_LB_API_KEY env_key, WebSocket support, and requires_openai_auth=true for Codex App routing' }
|
|
18
18
|
];
|
|
19
19
|
if (answers.use_as_default_provider) {
|
|
20
20
|
actions.push({ type: 'select_default_provider', target: configPath, effect: 'set top-level model_provider = "codex-lb"' });
|
|
@@ -113,24 +113,29 @@ export async function updateCheckCommand(args = []) {
|
|
|
113
113
|
return printJson(result);
|
|
114
114
|
console.log(`${sksTextLogo()}\n\n${formatSksUpdateCheckText(result)}`);
|
|
115
115
|
}
|
|
116
|
-
export async function updateCommand(sub = '
|
|
117
|
-
|
|
116
|
+
export async function updateCommand(sub = 'now', args = []) {
|
|
117
|
+
let action = String(sub || 'now').toLowerCase();
|
|
118
|
+
let effectiveArgs = args;
|
|
119
|
+
if (action.startsWith('-')) {
|
|
120
|
+
effectiveArgs = [String(sub), ...args];
|
|
121
|
+
action = 'now';
|
|
122
|
+
}
|
|
118
123
|
if (action === 'check' || action === 'status')
|
|
119
|
-
return updateCheckCommand(
|
|
124
|
+
return updateCheckCommand(effectiveArgs);
|
|
120
125
|
if (action !== 'now') {
|
|
121
|
-
console.error('Usage: sks update check|now [--version <version>] [--json] [--dry-run]');
|
|
126
|
+
console.error('Usage: sks update [check|now] [--version <version>] [--json] [--dry-run]');
|
|
122
127
|
process.exitCode = 1;
|
|
123
128
|
return;
|
|
124
129
|
}
|
|
125
130
|
const root = await projectRoot();
|
|
126
131
|
const result = await withSecretPreservationGuard(root, 'update-now', async () => runSksUpdateNow({
|
|
127
|
-
version: valueAfter(
|
|
128
|
-
dryRun: flag(
|
|
132
|
+
version: valueAfter(effectiveArgs, '--version') || valueAfter(effectiveArgs, '-v'),
|
|
133
|
+
dryRun: flag(effectiveArgs, '--dry-run'),
|
|
129
134
|
projectRoot: root,
|
|
130
135
|
timeoutMs: 10 * 60 * 1000,
|
|
131
136
|
maxOutputBytes: 128 * 1024
|
|
132
137
|
}));
|
|
133
|
-
if (flag(
|
|
138
|
+
if (flag(effectiveArgs, '--json'))
|
|
134
139
|
return printJson(result);
|
|
135
140
|
console.log(`${sksTextLogo()}\n`);
|
|
136
141
|
console.log(`SKS update ${result.status}`);
|
|
@@ -144,6 +149,8 @@ export async function updateCommand(sub = 'check', args = []) {
|
|
|
144
149
|
console.log(`New version: ${result.new_version}`);
|
|
145
150
|
if (result.project_receipt)
|
|
146
151
|
console.log(`Migration receipt: ${result.project_receipt.root} (${result.migration_current ? 'current' : 'not current'})`);
|
|
152
|
+
if (result.sks_menubar)
|
|
153
|
+
console.log(`SKS menu bar: ${result.sks_menubar.status}${result.sks_menubar.app_path ? ` (${result.sks_menubar.app_path})` : ''}`);
|
|
147
154
|
for (const stage of result.stages || [])
|
|
148
155
|
console.log(`Stage ${stage.id}: ${stage.ok ? 'ok' : 'failed'} ${stage.status}`);
|
|
149
156
|
if (result.error)
|
|
@@ -89,6 +89,8 @@ function phaseInputFiles(id) {
|
|
|
89
89
|
return ['src/core/doctor/codex-startup-config-repair.ts', '.codex/config.toml'];
|
|
90
90
|
if (id.includes('supabase'))
|
|
91
91
|
return ['src/core/doctor/supabase-mcp-repair.ts', '.codex/config.toml'];
|
|
92
|
+
if (id.includes('menubar'))
|
|
93
|
+
return ['src/core/codex-app/sks-menubar.ts', 'src/commands/doctor.ts', 'package.json'];
|
|
92
94
|
if (id.includes('skill'))
|
|
93
95
|
return ['.agents/skills', 'src/scripts/skill-registry-ledger-check.ts'];
|
|
94
96
|
if (id.includes('native'))
|
|
@@ -60,6 +60,8 @@ export function buildDoctorReadinessMatrix(input = {}) {
|
|
|
60
60
|
blockers.add('codex_app_fast_ui_repair_requires_confirmation');
|
|
61
61
|
if (input.codex_app_ui?.fast_selector === 'repaired')
|
|
62
62
|
warnings.add('codex_app_fast_selector_repaired_restart_app_if_needed');
|
|
63
|
+
if (input.sks_menubar?.ok === false)
|
|
64
|
+
warnings.add(`sks_menubar_${input.sks_menubar?.status || 'blocked'}`);
|
|
63
65
|
const codex0138Doctor = input.codex_0138_doctor || null;
|
|
64
66
|
if (codex0138Doctor?.ok === false)
|
|
65
67
|
for (const blocker of normalizeList(codex0138Doctor.blockers))
|
|
@@ -148,6 +150,7 @@ export function buildDoctorReadinessMatrix(input = {}) {
|
|
|
148
150
|
codex_app_harness_matrix: codexAppHarness,
|
|
149
151
|
fast_mode_ready: input.fast_mode_ready !== false,
|
|
150
152
|
codex_app_ui: input.codex_app_ui || null,
|
|
153
|
+
sks_menubar: input.sks_menubar || null,
|
|
151
154
|
hooks_ready: input.hooks_ready !== false,
|
|
152
155
|
codex_app_ready: input.codex_app?.ok === true,
|
|
153
156
|
codex_app_required_for_cli: false,
|
|
@@ -205,6 +208,7 @@ function buildRepairReadiness(input = {}) {
|
|
|
205
208
|
add('context7_repair', input.context7_repair, true);
|
|
206
209
|
add('context7_mcp_repair', input.context7_mcp_repair, true);
|
|
207
210
|
add('supabase_mcp_repair', input.supabase_mcp_repair, input.supabase_mcp_repair?.ready_blocking === true);
|
|
211
|
+
add('sks_menubar', input.sks_menubar, false);
|
|
208
212
|
add('command_alias_cleanup', input.command_aliases, true);
|
|
209
213
|
add('native_capability_repair', input.doctor_native_capability, false);
|
|
210
214
|
if (input.doctor_fix_transaction) {
|
package/dist/core/fsx.js
CHANGED
|
@@ -5,7 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import crypto from 'node:crypto';
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
|
-
export const PACKAGE_VERSION = '4.7.
|
|
8
|
+
export const PACKAGE_VERSION = '4.7.4';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
export function nowIso() {
|
|
@@ -137,8 +137,8 @@ function codexLbAuthBlocker(state) {
|
|
|
137
137
|
return 'codex_lb_not_selected';
|
|
138
138
|
if (!state.providerConfigured)
|
|
139
139
|
return 'codex_lb_provider_missing';
|
|
140
|
-
if (state.requiresOpenAiAuth !==
|
|
141
|
-
return state.requiresOpenAiAuth ===
|
|
140
|
+
if (state.requiresOpenAiAuth !== true) {
|
|
141
|
+
return state.requiresOpenAiAuth === false ? 'codex_lb_legacy_openai_auth_disabled' : 'codex_lb_requires_openai_auth_missing';
|
|
142
142
|
}
|
|
143
143
|
if (state.envKey !== 'CODEX_LB_API_KEY')
|
|
144
144
|
return 'codex_lb_env_key_missing_or_unsupported';
|
package/dist/core/init.js
CHANGED
|
@@ -643,7 +643,7 @@ export async function initProject(root, opts = {}) {
|
|
|
643
643
|
next = shouldSelectCodexLb
|
|
644
644
|
? upsertTopLevelTomlString(next, 'model_provider', 'codex-lb')
|
|
645
645
|
: removeTopLevelTomlKeyIfValue(next, 'model_provider', 'codex-lb');
|
|
646
|
-
next = upsertTomlTable(next, 'model_providers.codex-lb', `[model_providers.codex-lb]\nname = "
|
|
646
|
+
next = upsertTomlTable(next, 'model_providers.codex-lb', `[model_providers.codex-lb]\nname = "openai"\nbase_url = "${baseUrl}"\nwire_api = "responses"\nenv_key = "CODEX_LB_API_KEY"\nsupports_websockets = true\nrequires_openai_auth = true`);
|
|
647
647
|
return `${next.trim()}\n`;
|
|
648
648
|
}
|
|
649
649
|
function parseCodexLbEnvKey(text = '') {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { REQUIRED_CODEX_MODEL } from '../codex-model-guard.js';
|
|
2
2
|
export const MANAGED_ASSET_SCHEMA_VERSION = 1;
|
|
3
|
-
export const MANAGED_ASSET_VERSION = '4.7.
|
|
3
|
+
export const MANAGED_ASSET_VERSION = '4.7.4';
|
|
4
4
|
export const MANAGED_ASSET_MARKER = 'SKS-MANAGED-ASSET';
|
|
5
5
|
export const MANAGED_AGENT_ROLES = Object.freeze([
|
|
6
6
|
role('sks-explorer', 'analysis-scout.toml', 'analysis_scout', 'SKS analysis scout for bounded read/write slices retained for stale Codex agent-role config repair.', 'workspace-write', ['analysis-scout', 'analysis_scout']),
|
|
@@ -10,7 +10,7 @@ export async function resolveProviderContext(input = {}) {
|
|
|
10
10
|
const codexLbProviderBlockPresent = hasCodexLbProviderBlock(configText);
|
|
11
11
|
const codexLbEnvKey = codexLbProviderEnvKey(configText) || (codexLbProviderBlockPresent ? 'CODEX_LB_API_KEY' : null);
|
|
12
12
|
const codexLbRequiresOpenAiAuth = codexLbProviderRequiresOpenAiAuth(configText);
|
|
13
|
-
const codexLbProviderValid = codexLbProviderBlockPresent &&
|
|
13
|
+
const codexLbProviderValid = codexLbProviderBlockPresent && codexLbRequiresOpenAiAuth === true;
|
|
14
14
|
const openaiKey = Boolean(String(env.OPENAI_API_KEY || '').trim());
|
|
15
15
|
const lbKey = Boolean(String((codexLbEnvKey ? env[codexLbEnvKey] : env.CODEX_LB_API_KEY) || env.CODEX_LB_API_KEY || '').trim());
|
|
16
16
|
const envProvider = String(env.SKS_MODEL_PROVIDER || env.CODEX_MODEL_PROVIDER || env.OPENAI_MODEL_PROVIDER || '').trim();
|
package/dist/core/routes.js
CHANGED
|
@@ -699,10 +699,10 @@ export const COMMAND_CATALOG = [
|
|
|
699
699
|
{ name: 'quickstart', usage: 'sks quickstart', description: 'Show the shortest safe setup and verification flow.' },
|
|
700
700
|
{ name: 'bootstrap', usage: 'sks bootstrap [--install-scope global|project] [--local-only] [--json]', description: 'Initialize the current project, install SKS Codex App files/skills, check Context7/Codex App/Zellij, and print ready true/false.' },
|
|
701
701
|
{ name: 'root', usage: 'sks root [--json]', description: 'Show whether SKS is using a project root or the per-user global SKS runtime root.' },
|
|
702
|
-
{ name: 'update', usage: 'sks update check|now [--version <version>] [--json] [--dry-run]', description: 'Check for SKS
|
|
702
|
+
{ name: 'update', usage: 'sks update [check|now] [--version <version>] [--json] [--dry-run]', description: 'Check npm for the latest SKS release and update the global package by default; use `check` for status-only.' },
|
|
703
703
|
{ name: 'deps', usage: 'sks deps check [--json] [--yes]', description: 'Check Node/npm, Codex CLI, and Zellij readiness; pass --yes to repair missing Codex CLI/Zellij tooling when supported.' },
|
|
704
704
|
{ name: 'codex', usage: 'sks codex compatibility|version|doctor|schema|0.142 [--json]', description: 'Check Codex CLI rust-v0.142.0 compatibility, installed version, 0.142 manifest/capability evidence, inherited legacy baselines, and vendored hook schema snapshot freshness.' },
|
|
705
|
-
{ name: 'codex-app', usage: 'sks codex-app [check|
|
|
705
|
+
{ name: 'codex-app', usage: 'sks codex-app [check|glm-profile install|set-openrouter-key --api-key-stdin|product-design|chrome-extension|pat status|remote-control]', description: 'Check Codex App install, GLM/OpenRouter model profile visibility, codex-lb key-entry guidance, Product Design plugin readiness, Codex Chrome Extension web verification readiness, PAT-safe status, first-party MCP/plugin readiness, and Codex CLI 0.130.0+ remote-control availability.' },
|
|
706
706
|
{ name: 'codex-native', usage: 'sks codex-native status|feature-broker|invocation-plan|init-deep [--json]', description: 'Inspect Codex Native feature broker readiness, invocation routing, pattern evidence, and managed memory setup.' },
|
|
707
707
|
{ name: 'hooks', usage: 'sks hooks explain|status|trust-report|replay|codex-validate|warning-check ... [--json]', description: 'Explain Codex hook events, validate vendored latest 10-event output schemas, replay fixtures, and enforce warning-zero SKS hook policies under the 0.134 compatibility matrix.' },
|
|
708
708
|
{ name: 'codex-lb', usage: 'sks codex-lb status|health|metrics|doctor|circuit|repair|setup ...', description: 'Configure, health-check, repair, and record circuit evidence for codex-lb provider auth without confusing ChatGPT OAuth and proxy keys.' },
|