sneakoscope 7.0.5 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/cli/command-manifest-lite.js +1 -1
- package/dist/cli/command-registry.js +1 -1
- package/dist/cli/install-helpers-codex-lb-config.js +54 -6
- package/dist/cli/install-helpers.js +12 -3
- package/dist/commands/codex-app.js +27 -1
- package/dist/commands/doctor.js +54 -25
- package/dist/config/skills-manifest.json +52 -52
- package/dist/core/agent-bridge/agent-manifest.js +8 -2
- package/dist/core/agent-bridge/host-capability-runtime.js +86 -30
- package/dist/core/codex/codex-config-toml.js +16 -2
- package/dist/core/codex-app/glm-profile-installer.js +38 -8
- package/dist/core/codex-app/openrouter-activate.js +75 -10
- package/dist/core/codex-app/openrouter-provider.js +7 -0
- package/dist/core/commands/basic-cli.js +16 -22
- package/dist/core/harness-conflicts.js +27 -13
- package/dist/core/hooks-runtime/hook-context.js +47 -2
- package/dist/core/hooks-runtime/naruto-terminal-finalization.js +114 -1
- package/dist/core/hooks-runtime/subagent-skill-availability-guards.js +64 -4
- package/dist/core/hooks-runtime/subagent-skill-availability.js +101 -2
- package/dist/core/hooks-runtime.js +21 -5
- package/dist/core/init/skills.js +13 -4
- package/dist/core/init.js +3 -1
- package/dist/core/managed-assets/managed-assets-manifest.js +1 -1
- package/dist/core/providers/openrouter/openrouter-account.js +306 -0
- package/dist/core/providers/openrouter/openrouter-error.js +7 -7
- package/dist/core/release/package-size-budget.js +1 -1
- package/dist/core/release/release-gate-affected-selector.js +3 -2
- package/dist/core/subagents/official-subagent-preparation.js +17 -5
- package/dist/core/subagents/official-subagent-prompt.js +21 -0
- package/dist/core/subagents/role-model-preferences.js +179 -0
- package/dist/core/update/update-migration-state.js +24 -16
- package/dist/core/update/update-status.js +31 -3
- package/dist/core/version.js +1 -1
- package/dist/native/sks-menubar/Resources/AppIcon.icns +0 -0
- package/dist/native/sks-menubar/Resources/SKSStatusTemplate.pdf +0 -0
- package/dist/native/sks-menubar/Sources/OverviewViewController.swift +85 -4
- package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +401 -23
- package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +65 -23
- package/dist/native/sks-menubar/Sources/StatusItemController.swift +3 -4
- package/dist/scripts/sks-menubar-install-check.js +1 -1
- package/package.json +1 -1
- package/release-gates.v2.json +13 -8
package/README.md
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
Proof-first orchestration for Codex CLI, ChatGPT Desktop, AI coding agents, multi-agent workflows, release verification, and the macOS menu bar.
|
|
8
8
|
|
|
9
|
+
<p align="center">
|
|
10
|
+
<img src="docs/assets/sks-logo.svg" alt="Sneakoscope Codex logo" width="160" height="160" />
|
|
11
|
+
</p>
|
|
12
|
+
|
|
9
13
|
[](https://www.npmjs.com/package/sneakoscope)
|
|
10
14
|
[](#requirements)
|
|
11
15
|
[](#license)
|
|
@@ -18,7 +22,7 @@ Proof-first orchestration for Codex CLI, ChatGPT Desktop, AI coding agents, mult
|
|
|
18
22
|
Sneakoscope Codex (`sks`) is an open-source trust layer for Codex CLI and ChatGPT Desktop. It coordinates bounded AI coding agents, records machine-verifiable evidence, preserves project memory, and blocks release claims that are not supported by current tests or artifacts. Search visibility outcomes are measured separately; SKS does not promise rankings or traffic.
|
|
19
23
|
<!-- END SKS SEARCH VISIBILITY MARKETING -->
|
|
20
24
|
|
|
21
|
-
Current release: **SKS 7.0
|
|
25
|
+
Current release: **SKS 7.1.0**, with the package preferred Codex channel at **CLI 0.145.0**. SKS stays version-agnostic: older hosts keep working where capabilities allow, while Menu Bar / Center induce updates to the preferred latest. It resolves managed SKS skills from the authoritative global install, preserves a runnable Naruto child slot when `max_threads=2`, and keeps current-version Menu Bar repair transactional so stamped generations remain verifiable. Naruto uses stable opt-in multi-agent V2 when the host exposes it. See [CHANGELOG.md](CHANGELOG.md).
|
|
22
26
|
|
|
23
27
|
## What 7.0.0 Ships
|
|
24
28
|
|
|
@@ -16,7 +16,7 @@ export const COMMAND_MANIFEST_LITE = [
|
|
|
16
16
|
{ name: 'update', summary: 'Inspect, review, apply, or roll back the global SKS update', maturity: 'stable' },
|
|
17
17
|
{ name: 'uninstall', summary: 'Uninstall SKS global skills, hooks, config, menu bar, and optional project residue', maturity: 'stable', allowedDuringActiveRoute: true },
|
|
18
18
|
{ name: 'update-check', summary: 'Show the shared SKS, Codex CLI, and Menu Bar update status', maturity: 'stable', readonly: true, skipMigrationGate: true, allowedDuringActiveRoute: true, diagnostic: true },
|
|
19
|
-
{ name: 'mcp', summary: 'Manage scoped Codex MCP configuration', maturity: 'beta' },
|
|
19
|
+
{ name: 'mcp', summary: 'Manage scoped Codex MCP configuration', maturity: 'beta', skipMigrationGate: true },
|
|
20
20
|
{ name: 'wizard', summary: 'Open setup wizard help', maturity: 'stable' },
|
|
21
21
|
{ name: 'usage', summary: 'Show focused usage topic', maturity: 'stable', readonly: true, allowedDuringActiveRoute: true, diagnostic: true },
|
|
22
22
|
{ name: 'quickstart', summary: 'Show quickstart flow', maturity: 'stable' },
|
|
@@ -135,7 +135,7 @@ const COMMAND_DEFINITIONS = {
|
|
|
135
135
|
activeRoutePolicy: 'always'
|
|
136
136
|
}),
|
|
137
137
|
'update-check': readOnly(entry('stable', 'Show the shared SKS, Codex CLI, and Menu Bar update status', 'dist/core/commands/basic-cli.js', basicArgs('updateCheckCommand'))),
|
|
138
|
-
mcp: entry('beta', 'Manage scoped Codex MCP configuration', 'dist/core/commands/mcp-config-command.js', argsCommand(() => import('../core/commands/mcp-config-command.js'), 'mcpConfigCommand', 'dist/core/commands/mcp-config-command.js')),
|
|
138
|
+
mcp: skipMigrationGate(entry('beta', 'Manage scoped Codex MCP configuration', 'dist/core/commands/mcp-config-command.js', argsCommand(() => import('../core/commands/mcp-config-command.js'), 'mcpConfigCommand', 'dist/core/commands/mcp-config-command.js'))),
|
|
139
139
|
wizard: entry('stable', 'Open setup wizard help', 'dist/core/commands/basic-cli.js', basicNoArgs('quickstartCommand')),
|
|
140
140
|
usage: readOnly(entry('stable', 'Show focused usage topic', 'dist/core/commands/basic-cli.js', basicArgs('usageCommand'))),
|
|
141
141
|
quickstart: entry('stable', 'Show quickstart flow', 'dist/core/commands/basic-cli.js', basicNoArgs('quickstartCommand')),
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import { ensureDir, readText } from '../core/fsx.js';
|
|
4
|
-
import { GLM_CODEX_CONFIG_PROVIDER_ID, GLM_52_OPENROUTER_MODEL, OPENROUTER_DEFAULT_PROFILE_ID, RETIRED_GLM_DESKTOP_CONFIG_PROFILE_IDS } from '../core/codex-app/openrouter-provider.js';
|
|
4
|
+
import { GLM_CODEX_CONFIG_PROVIDER_ID, GLM_52_OPENROUTER_MODEL, OPENROUTER_AUTH_COMMAND, OPENROUTER_AUTH_REFRESH_INTERVAL_MS, OPENROUTER_AUTH_TIMEOUT_MS, openRouterAuthCommandArgs, OPENROUTER_DEFAULT_PROFILE_ID, RETIRED_GLM_DESKTOP_CONFIG_PROFILE_IDS } from '../core/codex-app/openrouter-provider.js';
|
|
5
|
+
import { openRouterSecretPaths, resolveOpenRouterApiKey } from '../core/providers/openrouter/openrouter-secret-store.js';
|
|
5
6
|
import { reconcileRetiredSksConfigText } from '../core/auto-review.js';
|
|
6
7
|
import { removeTopLevelTomlKeyIfValue, safeWriteCodexConfigToml, upsertTopLevelTomlString, upsertTomlTable } from '../core/codex-runtime/codex-desktop-config-policy.js';
|
|
7
8
|
import { codexLbConfigPath, normalizeCodexLbBaseUrl } from './install-helpers-codex-lb-shared.js';
|
|
@@ -58,30 +59,41 @@ export function removeCodexLbSharedOpenAiRouting(text = '', baseUrl = '') {
|
|
|
58
59
|
text: removeTopLevelLine(withoutValue, CODEX_LB_SHARED_OPENAI_ROUTING_MARKER)
|
|
59
60
|
};
|
|
60
61
|
}
|
|
61
|
-
export function upsertCodexAppGlmConfig(text = '') {
|
|
62
|
+
export function upsertCodexAppGlmConfig(text = '', input = {}) {
|
|
62
63
|
let next = String(text || '');
|
|
64
|
+
const authEnv = { ...(input.env || process.env), ...(input.home ? { HOME: input.home } : {}) };
|
|
65
|
+
const authArgs = openRouterAuthCommandArgs(openRouterSecretPaths(authEnv).keyPath);
|
|
63
66
|
const providerBlock = [
|
|
64
67
|
`[model_providers.${GLM_CODEX_CONFIG_PROVIDER_ID}]`,
|
|
65
68
|
'name = "OpenRouter"',
|
|
66
69
|
'base_url = "https://openrouter.ai/api/v1"',
|
|
67
70
|
'wire_api = "responses"',
|
|
68
|
-
'env_key = "OPENROUTER_API_KEY"',
|
|
69
71
|
'requires_openai_auth = false'
|
|
70
72
|
].join('\n');
|
|
71
73
|
next = upsertTomlTable(next, `model_providers.${GLM_CODEX_CONFIG_PROVIDER_ID}`, providerBlock);
|
|
74
|
+
const authBlock = [
|
|
75
|
+
`[model_providers.${GLM_CODEX_CONFIG_PROVIDER_ID}.auth]`,
|
|
76
|
+
`command = ${JSON.stringify(OPENROUTER_AUTH_COMMAND)}`,
|
|
77
|
+
`args = [${authArgs.map((value) => JSON.stringify(value)).join(', ')}]`,
|
|
78
|
+
`timeout_ms = ${OPENROUTER_AUTH_TIMEOUT_MS}`,
|
|
79
|
+
`refresh_interval_ms = ${OPENROUTER_AUTH_REFRESH_INTERVAL_MS}`
|
|
80
|
+
].join('\n');
|
|
81
|
+
next = upsertTomlTable(next, `model_providers.${GLM_CODEX_CONFIG_PROVIDER_ID}.auth`, authBlock);
|
|
82
|
+
next = next.replace(new RegExp(`\\n+\\[model_providers\\.${GLM_CODEX_CONFIG_PROVIDER_ID}\\.auth\\]`), `\n\n[model_providers.${GLM_CODEX_CONFIG_PROVIDER_ID}.auth]`);
|
|
72
83
|
next = reconcileRetiredSksConfigText(next).text;
|
|
73
84
|
return `${next.trim()}\n`;
|
|
74
85
|
}
|
|
75
86
|
export async function ensureGlobalCodexAppGlmProfile(opts = {}) {
|
|
76
|
-
|
|
87
|
+
const env = opts.env || process.env;
|
|
88
|
+
if (env.SKS_SKIP_CODEX_GLM_PROFILE_REPAIR === '1' && opts.force !== true) {
|
|
77
89
|
return { ok: true, status: 'skipped', reason: 'SKS_SKIP_CODEX_GLM_PROFILE_REPAIR=1' };
|
|
78
90
|
}
|
|
79
|
-
const home = opts.home ||
|
|
91
|
+
const home = opts.home || env.HOME || os.homedir();
|
|
80
92
|
const configPath = opts.configPath || codexLbConfigPath(home);
|
|
81
93
|
try {
|
|
82
94
|
await ensureDir(path.dirname(configPath));
|
|
83
95
|
const current = await readText(configPath, '');
|
|
84
|
-
const next = upsertCodexAppGlmConfig(current);
|
|
96
|
+
const next = upsertCodexAppGlmConfig(current, { home, env: opts.env });
|
|
85
97
|
const safeWrite = await safeWriteCodexConfigToml(configPath, current, next, 'openrouter-provider');
|
|
86
98
|
return {
|
|
87
99
|
...safeWrite,
|
|
@@ -107,6 +119,42 @@ export async function ensureGlobalCodexAppGlmProfile(opts = {}) {
|
|
|
107
119
|
};
|
|
108
120
|
}
|
|
109
121
|
}
|
|
122
|
+
export async function ensureStoredOpenRouterProviderDuringInstall(opts = {}) {
|
|
123
|
+
const home = opts.home || opts.env?.HOME || process.env.HOME || os.homedir();
|
|
124
|
+
const env = { ...(opts.env || process.env), HOME: home };
|
|
125
|
+
const key = await resolveOpenRouterApiKey({ env });
|
|
126
|
+
if (!key.key) {
|
|
127
|
+
return {
|
|
128
|
+
schema: 'sks.openrouter-provider-upgrade-repair.v1',
|
|
129
|
+
ok: true,
|
|
130
|
+
status: 'skipped',
|
|
131
|
+
reason: 'openrouter_key_missing',
|
|
132
|
+
key_present: false,
|
|
133
|
+
key_source: null,
|
|
134
|
+
blockers: [],
|
|
135
|
+
warnings: key.warnings
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const repair = await ensureGlobalCodexAppGlmProfile({
|
|
139
|
+
...opts,
|
|
140
|
+
home,
|
|
141
|
+
env
|
|
142
|
+
});
|
|
143
|
+
const ok = repair?.ok !== false;
|
|
144
|
+
return {
|
|
145
|
+
schema: 'sks.openrouter-provider-upgrade-repair.v1',
|
|
146
|
+
ok,
|
|
147
|
+
status: ok ? repair?.status || 'present' : 'failed',
|
|
148
|
+
reason: ok ? 'stored_openrouter_key_provider_reconciled' : 'stored_openrouter_key_provider_repair_failed',
|
|
149
|
+
key_present: true,
|
|
150
|
+
key_source: key.source,
|
|
151
|
+
config_path: repair?.config_path || opts.configPath || codexLbConfigPath(home),
|
|
152
|
+
provider: GLM_CODEX_CONFIG_PROVIDER_ID,
|
|
153
|
+
blockers: ok ? [] : ['openrouter_provider_repair_failed'],
|
|
154
|
+
warnings: key.warnings,
|
|
155
|
+
repair
|
|
156
|
+
};
|
|
157
|
+
}
|
|
110
158
|
export function detectCodexLbSetupDrift(state = {}) {
|
|
111
159
|
const drift = [];
|
|
112
160
|
if (state.useDefaultProvider && state.selected !== true)
|
|
@@ -27,7 +27,7 @@ import { repairCodexImagegen } from '../core/doctor/imagegen-repair.js';
|
|
|
27
27
|
import { canAskYesNo, compareVersions, hasCodexUnstableFeatureWarningSuppression, hasDeprecatedCodexHooksFeatureFlag, hasTopLevelCodexModeLock, isProjectSetupCandidate } from './install-tool-helpers.js';
|
|
28
28
|
import { checkCodexLbResponseChain } from './install-helpers-codex-lb-chain.js';
|
|
29
29
|
import { CODEX_LB_CANONICAL_FAST_SERVICE_TIER, CODEX_LB_PROVIDER_ENV_KEY, CODEX_LB_PROVIDER_NAME, askPostinstallQuestion, codexAuthChatgptBackupPath, codexAuthPath, codexLbConfigPath, codexLbEnvPath, hasTopLevelCodexLbSelected, normalizeCodexLbBaseUrl, parseCodexLbEnvKey, redactSecretText } from './install-helpers-codex-lb-shared.js';
|
|
30
|
-
import { appliedCodexLbPersistenceModes, captureCodexLbSetupWriteState, codexLbSharedOpenAiRoutingState, detectCodexLbSetupDrift, ensureGlobalCodexAppGlmProfile, parseCodexLbEnvBaseUrl, parseCodexSharedLoginApiKey, removeCodexLbSharedOpenAiRouting, sha256Text, shellSingleQuote, upsertCodexAppGlmConfig, upsertCodexLbConfig, upsertCodexLbSharedOpenAiRouting } from './install-helpers-codex-lb-config.js';
|
|
30
|
+
import { appliedCodexLbPersistenceModes, captureCodexLbSetupWriteState, codexLbSharedOpenAiRoutingState, detectCodexLbSetupDrift, ensureGlobalCodexAppGlmProfile, ensureStoredOpenRouterProviderDuringInstall, parseCodexLbEnvBaseUrl, parseCodexSharedLoginApiKey, removeCodexLbSharedOpenAiRouting, sha256Text, shellSingleQuote, upsertCodexAppGlmConfig, upsertCodexLbConfig, upsertCodexLbSharedOpenAiRouting } from './install-helpers-codex-lb-config.js';
|
|
31
31
|
import { ensureCodexImagegenDuringInstall, ensureGlobalCodexSkillsDuringInstall, ensureGlobalContext7DuringInstall, ensureGlobalGetdesignSkillDuringInstall, ensureSksCommandDuringInstall } from './install-helpers-install-support.js';
|
|
32
32
|
function packagedSksEntrypoint() {
|
|
33
33
|
return path.join(packageRoot(), 'dist', 'bin', 'sks.js');
|
|
@@ -89,6 +89,15 @@ export async function postinstall({ bootstrap, args = [] }) {
|
|
|
89
89
|
console.log(`Codex App Fast mode: skipped (${fastModeRepair.reason}).`);
|
|
90
90
|
else if (fastModeRepair.status === 'failed')
|
|
91
91
|
console.log(`Codex App Fast mode: auto repair failed. Run \`sks setup\`. ${fastModeRepair.error || ''}`.trim());
|
|
92
|
+
const openRouterProviderRepair = await ensureStoredOpenRouterProviderDuringInstall();
|
|
93
|
+
if (openRouterProviderRepair.status === 'updated')
|
|
94
|
+
console.log('OpenRouter provider: repaired for the stored key (credentials and active model were preserved).');
|
|
95
|
+
else if (openRouterProviderRepair.status === 'present')
|
|
96
|
+
console.log('OpenRouter provider: stored-key configuration already compatible.');
|
|
97
|
+
else if (openRouterProviderRepair.status === 'skipped')
|
|
98
|
+
console.log('OpenRouter provider: no stored key; no provider configuration was added.');
|
|
99
|
+
else if (openRouterProviderRepair.status === 'failed')
|
|
100
|
+
console.log('OpenRouter provider: stored key was preserved, but provider repair failed. Run `sks doctor --fix`.');
|
|
92
101
|
const imagegenRepair = await ensureCodexImagegenDuringInstall();
|
|
93
102
|
if (imagegenRepair.status === 'ready')
|
|
94
103
|
console.log('Codex App Image Gen: ready ($imagegen/gpt-image-2 detected).');
|
|
@@ -258,7 +267,7 @@ async function reportPostinstallCodexLbAuth(snapshot = null) {
|
|
|
258
267
|
async function postinstallHarnessConflictNotice(conflictScan) {
|
|
259
268
|
console.log('\nSneakoscope Codex package installed, but SKS setup is blocked.');
|
|
260
269
|
console.log(formatHarnessConflictReport(conflictScan, { includePrompt: false }));
|
|
261
|
-
console.log('\nWhat this means: npm can finish installing the package. Conflicting OMX/DCodex markers
|
|
270
|
+
console.log('\nWhat this means: npm can finish installing the package. Conflicting OMX/DCodex markers must be removed explicitly with `sks conflicts cleanup --yes` before `sks setup`, `sks doctor --fix`, or `sks update` can proceed.');
|
|
262
271
|
console.log('No files were removed by postinstall.');
|
|
263
272
|
console.log('Cleanup requires a human-approved Codex App session. Keep the model selected in Codex and use high reasoning effort.');
|
|
264
273
|
if (shouldAskPostinstallQuestion()) {
|
|
@@ -1924,7 +1933,7 @@ export { checkContext7, ensureCodexCliTool, ensureMadLaunchDependencies, ensureR
|
|
|
1924
1933
|
export { codexFastModeDesktopStatus, ensureGlobalCodexFastModeDuringInstall, normalizeCodexFastModeUiConfig, safeWriteCodexConfigToml } from '../core/codex-runtime/codex-desktop-config-policy.js';
|
|
1925
1934
|
export { checkRequiredSkills, context7GlobalMcpStatus, ensureCodexImagegenDuringInstall, ensureGlobalCodexSkillsDuringInstall, ensureProjectContext7Config, ensureSksCommandDuringInstall, globalCodexSkillsRoot, selftestSksShimRepair } from './install-helpers-install-support.js';
|
|
1926
1935
|
export { askPostinstallQuestion, codexLbConfigPath, codexLbEnvPath, normalizeCodexLbBaseUrl } from './install-helpers-codex-lb-shared.js';
|
|
1927
|
-
export { ensureGlobalCodexAppGlmProfile, upsertCodexAppGlmConfig, upsertCodexLbConfig } from './install-helpers-codex-lb-config.js';
|
|
1936
|
+
export { ensureGlobalCodexAppGlmProfile, ensureStoredOpenRouterProviderDuringInstall, upsertCodexAppGlmConfig, upsertCodexLbConfig } from './install-helpers-codex-lb-config.js';
|
|
1928
1937
|
export { checkCodexLbResponseChain } from './install-helpers-codex-lb-chain.js';
|
|
1929
1938
|
export { selftestCodexLb } from './install-helpers-codex-lb-selftest.js';
|
|
1930
1939
|
function escapeRegExp(value) {
|
|
@@ -14,7 +14,9 @@ import { doctorCodexAppGlmProfile, installCodexAppGlmProfile } from '../core/cod
|
|
|
14
14
|
import { openRouterStatus, useOpenRouter } from '../core/codex-app/openrouter-activate.js';
|
|
15
15
|
import { OPENROUTER_DEFAULT_MODEL } from '../core/codex-app/openrouter-provider.js';
|
|
16
16
|
import { promptForOpenRouterKeyHidden, writeStoredOpenRouterKey } from '../core/providers/openrouter/openrouter-secret-store.js';
|
|
17
|
+
import { compactOpenRouterModelsResult, listOpenRouterModels, testOpenRouterConnection } from '../core/providers/openrouter/openrouter-account.js';
|
|
17
18
|
import { restartCodexApp } from '../core/codex-app/codex-app-restart.js';
|
|
19
|
+
import { resetRoleModelPreference, roleModelPreferencesStatus, setRoleModelPreference } from '../core/subagents/role-model-preferences.js';
|
|
18
20
|
export async function run(_command, args = []) {
|
|
19
21
|
const action = args[0] || 'check';
|
|
20
22
|
if (action === 'restart')
|
|
@@ -82,6 +84,30 @@ export async function run(_command, args = []) {
|
|
|
82
84
|
const result = await openRouterStatus({});
|
|
83
85
|
return printCodexAppResult(args, result);
|
|
84
86
|
}
|
|
87
|
+
if (action === 'openrouter-models') {
|
|
88
|
+
const models = await listOpenRouterModels({});
|
|
89
|
+
const result = flag(args, '--ids-only') ? compactOpenRouterModelsResult(models) : models;
|
|
90
|
+
return printCodexAppResult(args, result);
|
|
91
|
+
}
|
|
92
|
+
if (action === 'openrouter-test') {
|
|
93
|
+
const result = await testOpenRouterConnection({ model: readOption(args, '--model', '') });
|
|
94
|
+
return printCodexAppResult(args, result);
|
|
95
|
+
}
|
|
96
|
+
if (action === 'role-models') {
|
|
97
|
+
return printCodexAppResult(args, await roleModelPreferencesStatus());
|
|
98
|
+
}
|
|
99
|
+
if (action === 'set-role-model') {
|
|
100
|
+
const result = await setRoleModelPreference({
|
|
101
|
+
role: readOption(args, '--role', ''),
|
|
102
|
+
model: readOption(args, '--model', ''),
|
|
103
|
+
reasoning: readOption(args, '--reasoning', '')
|
|
104
|
+
});
|
|
105
|
+
return printCodexAppResult(args, result);
|
|
106
|
+
}
|
|
107
|
+
if (action === 'reset-role-model') {
|
|
108
|
+
const result = await resetRoleModelPreference({ role: readOption(args, '--role', '') });
|
|
109
|
+
return printCodexAppResult(args, result);
|
|
110
|
+
}
|
|
85
111
|
if (action === 'product-design' || action === 'design-product' || action === 'ensure-product-design') {
|
|
86
112
|
const checkOnly = flag(args, '--check-only') || flag(args, '--no-install');
|
|
87
113
|
const status = await codexProductDesignPluginStatus({
|
|
@@ -142,7 +168,7 @@ export async function run(_command, args = []) {
|
|
|
142
168
|
process.exitCode = 1;
|
|
143
169
|
return;
|
|
144
170
|
}
|
|
145
|
-
console.error('Usage: sks codex-app check|status|harness-matrix|skill-sync|agent-role-sync|init-deep|hook-lifecycle|execution-profile|set-openrouter-key [--api-key-stdin]|use-openrouter --model <id>|openrouter-status|product-design [--check-only]|ensure-product-design|chrome-extension|pat status|remote-control [--json]');
|
|
171
|
+
console.error('Usage: sks codex-app check|status|harness-matrix|skill-sync|agent-role-sync|init-deep|hook-lifecycle|execution-profile|set-openrouter-key [--api-key-stdin]|use-openrouter --model <id>|openrouter-status|openrouter-models [--ids-only]|openrouter-test [--model <id>]|role-models|set-role-model --role <name> --model <id> --reasoning <effort>|reset-role-model --role <name>|product-design [--check-only]|ensure-product-design|chrome-extension|pat status|remote-control [--json]');
|
|
146
172
|
console.error('Note: glm-profile is retired (strips leftover Desktop GLM profiles); use set-openrouter-key / use-openrouter.');
|
|
147
173
|
process.exitCode = 1;
|
|
148
174
|
}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -35,32 +35,27 @@ export async function run(_command, args = [], deps = {}) {
|
|
|
35
35
|
const doctorFix = flag(args, '--fix');
|
|
36
36
|
const globalOnly = doctorFix && flag(args, '--global-only');
|
|
37
37
|
if (doctorFix) {
|
|
38
|
-
const { cleanupOtherHarnessConflicts } = await import('../core/harness-conflicts.js');
|
|
39
38
|
const conflictScan = await scanHarnessConflicts(root);
|
|
40
39
|
if (conflictScan.hard_block) {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
process.exitCode = 1;
|
|
56
|
-
if (flag(args, '--json')) {
|
|
57
|
-
printJson(blocked);
|
|
58
|
-
return blocked;
|
|
59
|
-
}
|
|
60
|
-
console.error(formatHarnessConflictReport(cleanup.after || conflictScan, { includePrompt: false }));
|
|
61
|
-
console.error('Automatic OMX/DCodex removal did not clear every conflict. Live markers must be removed; inspect backup under .sneakoscope/quarantine/other-harness/.');
|
|
40
|
+
const blocked = {
|
|
41
|
+
schema: 'sks.doctor-status.v3',
|
|
42
|
+
ok: false,
|
|
43
|
+
status: 'blocked_harness_conflict',
|
|
44
|
+
diagnostic_depth: 'fix',
|
|
45
|
+
root,
|
|
46
|
+
blockers: conflictScan.hard.map((item) => `${item.name || 'harness'}:${item.path}`),
|
|
47
|
+
conflicts: conflictScan.conflicts,
|
|
48
|
+
cleanup_prompt_command: 'sks conflicts cleanup --yes',
|
|
49
|
+
no_fix_writes_performed: true
|
|
50
|
+
};
|
|
51
|
+
process.exitCode = 1;
|
|
52
|
+
if (flag(args, '--json')) {
|
|
53
|
+
printJson(blocked);
|
|
62
54
|
return blocked;
|
|
63
55
|
}
|
|
56
|
+
console.error(formatHarnessConflictReport(conflictScan, { includePrompt: false }));
|
|
57
|
+
console.error('Run `sks conflicts cleanup --yes` to quarantine OMX/DCodex markers before doctor --fix.');
|
|
58
|
+
return blocked;
|
|
64
59
|
}
|
|
65
60
|
}
|
|
66
61
|
const doctorProfile = doctorProfileFromArgs(args, doctorFix);
|
|
@@ -89,6 +84,8 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
|
|
|
89
84
|
|| (await import('../core/doctor/command-alias-cleanup.js')).runDoctorCommandAliasCleanup;
|
|
90
85
|
const ensureGlobalFastModeImpl = deps.ensureGlobalCodexFastModeDuringInstallImpl
|
|
91
86
|
|| (await import('../cli/install-helpers.js')).ensureGlobalCodexFastModeDuringInstall;
|
|
87
|
+
const ensureStoredOpenRouterProviderImpl = deps.ensureStoredOpenRouterProviderDuringInstallImpl
|
|
88
|
+
|| (await import('../cli/install-helpers.js')).ensureStoredOpenRouterProviderDuringInstall;
|
|
92
89
|
const installMenuBarImpl = deps.installSksMenuBarImpl || installSksMenuBar;
|
|
93
90
|
const codexLbStatusImpl = deps.codexLbStatusImpl || codexLbStatus;
|
|
94
91
|
const providerStatus = await codexLbStatusImpl({
|
|
@@ -131,6 +128,16 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
|
|
|
131
128
|
status: 'failed',
|
|
132
129
|
error: err?.message || String(err)
|
|
133
130
|
}));
|
|
131
|
+
const openRouterEnv = deps.env || (deps.home
|
|
132
|
+
? { ...process.env, HOME: home, SKS_HOME: path.join(home, '.sneakoscope') }
|
|
133
|
+
: { ...process.env, HOME: home });
|
|
134
|
+
const openRouterProvider = await ensureStoredOpenRouterProviderImpl({ home, env: openRouterEnv }).catch((err) => ({
|
|
135
|
+
schema: 'sks.openrouter-provider-upgrade-repair.v1',
|
|
136
|
+
ok: false,
|
|
137
|
+
status: 'failed',
|
|
138
|
+
blockers: ['openrouter_provider_repair_failed'],
|
|
139
|
+
error: err?.message || String(err)
|
|
140
|
+
}));
|
|
134
141
|
const menuBar = await installMenuBarImpl({
|
|
135
142
|
home,
|
|
136
143
|
root: home,
|
|
@@ -151,10 +158,12 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
|
|
|
151
158
|
const globalFastModeReady = globalFastMode?.status !== 'failed'
|
|
152
159
|
&& globalFastMode?.ok !== false;
|
|
153
160
|
const menuBarReady = menuBar?.ok !== false;
|
|
161
|
+
const openRouterProviderReady = openRouterProvider?.ok !== false;
|
|
154
162
|
const blockers = [...new Set([
|
|
155
163
|
...(!globalSkillsReady ? [`global_skills_reconcile_failed:${globalSkills?.error || 'core_skill_integrity'}`] : []),
|
|
156
164
|
...(currentSurface?.ok !== true ? (currentSurface?.blockers || ['global_current_surface_reconcile_failed']) : []),
|
|
157
165
|
...(!globalFastModeReady ? [`global_fast_mode_repair_failed:${globalFastMode?.error || globalFastMode?.status || 'unknown'}`] : []),
|
|
166
|
+
...(!openRouterProviderReady ? (openRouterProvider?.blockers || ['openrouter_provider_repair_failed']) : []),
|
|
158
167
|
...(!menuBarReady ? (menuBar?.blockers || ['sks_menubar_repair_failed']) : []),
|
|
159
168
|
...(!recoveryReady ? (providerStatus?.tool_output_recovery?.blockers || ['codex_lb_tool_output_recovery_unverified']) : [])
|
|
160
169
|
].map(String).filter(Boolean))];
|
|
@@ -183,6 +192,7 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
|
|
|
183
192
|
skills: { global: globalSkills, project: { skipped: true, reason: 'global_only_doctor' } },
|
|
184
193
|
current_public_surface: currentSurface,
|
|
185
194
|
codex_app_fast_mode: globalFastMode,
|
|
195
|
+
openrouter_provider: openRouterProvider,
|
|
186
196
|
sks_menubar: menuBar,
|
|
187
197
|
codex_lb: {
|
|
188
198
|
provider_status: providerStatus,
|
|
@@ -192,6 +202,7 @@ export async function executeDoctorGlobalOnlyFix(args = [], root, deps = {}) {
|
|
|
192
202
|
blockers,
|
|
193
203
|
next_actions: [
|
|
194
204
|
...(recoveryReady ? [] : (providerStatus?.tool_output_recovery?.operator_actions || [])),
|
|
205
|
+
...(!openRouterProviderReady ? ['Run `sks codex-app set-openrouter-key` to repair the stored-key provider configuration without switching models.'] : []),
|
|
195
206
|
'Run `sks doctor --fix --json` from a specific project directory when project-scoped repair is required.'
|
|
196
207
|
]
|
|
197
208
|
};
|
|
@@ -432,10 +443,20 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
432
443
|
const doctorDirtyPlan = doctorFix ? (await import('../core/doctor/doctor-dirty-planner.js')).planDoctorDirtyRepair(root, doctorPhaseIds) : null;
|
|
433
444
|
let setupRepair = null;
|
|
434
445
|
let sksUpdate = null;
|
|
446
|
+
let openRouterProviderRepair = { ok: true, status: 'skipped', reason: 'doctor_without_fix' };
|
|
435
447
|
let migrationPreFix = null;
|
|
436
448
|
if (doctorFix) {
|
|
437
449
|
migrationPreFix = await captureCodexConfigSnapshot();
|
|
438
450
|
const installScope = installScopeFromArgs(args);
|
|
451
|
+
openRouterProviderRepair = flag(args, '--local-only')
|
|
452
|
+
? { ok: true, status: 'skipped', reason: 'local-only repair' }
|
|
453
|
+
: await (await import('../cli/install-helpers.js')).ensureStoredOpenRouterProviderDuringInstall().catch((err) => ({
|
|
454
|
+
schema: 'sks.openrouter-provider-upgrade-repair.v1',
|
|
455
|
+
ok: false,
|
|
456
|
+
status: 'failed',
|
|
457
|
+
blockers: ['openrouter_provider_repair_failed'],
|
|
458
|
+
error: err?.message || String(err)
|
|
459
|
+
}));
|
|
439
460
|
setupRepair = {
|
|
440
461
|
schema: 'sks.doctor-setup-phase.v2',
|
|
441
462
|
ok: true,
|
|
@@ -449,7 +470,8 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
449
470
|
: { status: 'skipped', reason: 'project or local-only repair' },
|
|
450
471
|
codex_app_fast_mode: flag(args, '--local-only')
|
|
451
472
|
? { status: 'skipped', reason: 'local-only repair' }
|
|
452
|
-
: await (await import('../cli/install-helpers.js')).ensureGlobalCodexFastModeDuringInstall().catch((err) => ({ status: 'failed', error: err?.message || String(err) }))
|
|
473
|
+
: await (await import('../cli/install-helpers.js')).ensureGlobalCodexFastModeDuringInstall().catch((err) => ({ status: 'failed', error: err?.message || String(err) })),
|
|
474
|
+
openrouter_provider: openRouterProviderRepair
|
|
453
475
|
};
|
|
454
476
|
}
|
|
455
477
|
const skillsReconcile = await reconcileDoctorSkills(root, doctorFix);
|
|
@@ -1217,7 +1239,12 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
1217
1239
|
]
|
|
1218
1240
|
});
|
|
1219
1241
|
if (doctorFix) {
|
|
1220
|
-
const readinessBlockers =
|
|
1242
|
+
const readinessBlockers = [
|
|
1243
|
+
...(Array.isArray(ready.blockers) ? ready.blockers.map(String).filter(Boolean) : []),
|
|
1244
|
+
...(openRouterProviderRepair?.ok === false
|
|
1245
|
+
? ((openRouterProviderRepair?.blockers || ['openrouter_provider_repair_failed']).map(String))
|
|
1246
|
+
: [])
|
|
1247
|
+
];
|
|
1221
1248
|
const migrationWarnings = [
|
|
1222
1249
|
...(doctorNativeCapabilityRepair?.optional_warnings || []),
|
|
1223
1250
|
...(doctorFixPostcheck?.optional_warnings || [])
|
|
@@ -1265,6 +1292,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
1265
1292
|
&& commandAliasCleanup.ok !== false
|
|
1266
1293
|
&& codexStartupRepair.ok !== false
|
|
1267
1294
|
&& agentRoleConfigRepair.ok !== false
|
|
1295
|
+
&& openRouterProviderRepair.ok !== false
|
|
1268
1296
|
&& (officialSubagentConfig.blockers || []).length === 0
|
|
1269
1297
|
&& codexLbRecoveryReady;
|
|
1270
1298
|
const result = {
|
|
@@ -1284,6 +1312,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
1284
1312
|
rust,
|
|
1285
1313
|
codex_app: codexApp,
|
|
1286
1314
|
codex_app_ui: codexAppUi,
|
|
1315
|
+
openrouter_provider: openRouterProviderRepair,
|
|
1287
1316
|
sks_menubar: sksMenuBar,
|
|
1288
1317
|
provider_context: providerContext,
|
|
1289
1318
|
codex_lb: codexLb,
|
|
@@ -1344,7 +1373,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
1344
1373
|
sneakoscope: { ok: await exists(`${root}/.sneakoscope`) },
|
|
1345
1374
|
package: { bytes: pkgBytes, human: formatBytes(pkgBytes) },
|
|
1346
1375
|
skills: skillsReconcile,
|
|
1347
|
-
repair: { sks_update: sksUpdate, setup: setupRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup, agent_role_config: agentRoleConfigRepair, zellij: zellijRepair, context7: context7Repair, codex_startup: codexStartupRepair, startup_config: startupConfigRepair, context7_mcp: context7McpRepair, supabase_mcp: supabaseMcpRepair, mcp_transport_collision: mcpTransportCollisionRepair, imagegen: imagegenRepair, computer_use: computerUseRepair, browser_use: browserUseRepair, hook_trust: hookTrustRepair, sks_menubar: sksMenuBar, doctor_transaction: doctorFixTransaction, doctor_dirty_plan: doctorDirtyPlan, doctor_postcheck: doctorFixPostcheck, codex_native: codexNativeRepair, doctor_native_capability: doctorNativeCapabilityRepair, command_aliases: commandAliasCleanup, skills: skillsReconcile, sks_temp_sweep: sksTempSweep }
|
|
1376
|
+
repair: { sks_update: sksUpdate, setup: setupRepair, openrouter_provider: openRouterProviderRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup, agent_role_config: agentRoleConfigRepair, zellij: zellijRepair, context7: context7Repair, codex_startup: codexStartupRepair, startup_config: startupConfigRepair, context7_mcp: context7McpRepair, supabase_mcp: supabaseMcpRepair, mcp_transport_collision: mcpTransportCollisionRepair, imagegen: imagegenRepair, computer_use: computerUseRepair, browser_use: browserUseRepair, hook_trust: hookTrustRepair, sks_menubar: sksMenuBar, doctor_transaction: doctorFixTransaction, doctor_dirty_plan: doctorDirtyPlan, doctor_postcheck: doctorFixPostcheck, codex_native: codexNativeRepair, doctor_native_capability: doctorNativeCapabilityRepair, command_aliases: commandAliasCleanup, skills: skillsReconcile, sks_temp_sweep: sksTempSweep }
|
|
1348
1377
|
};
|
|
1349
1378
|
if (reportFile)
|
|
1350
1379
|
await writeJsonReportFile(reportFile, result);
|