sneakoscope 7.0.5 → 7.1.1
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 -1
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/cli/command-manifest-lite.js +2 -2
- package/dist/cli/command-registry.js +2 -2
- 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 +54 -1
- package/dist/commands/doctor-profile.js +78 -0
- package/dist/commands/doctor.js +71 -90
- 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 +118 -51
- package/dist/core/codex/codex-config-toml.js +16 -2
- package/dist/core/codex-app/codex-model-catalog.js +371 -0
- package/dist/core/codex-app/glm-profile-installer.js +38 -8
- package/dist/core/codex-app/menubar/constants.js +1 -0
- package/dist/core/codex-app/menubar/installer.js +11 -1
- package/dist/core/codex-app/multi-provider-router-support.js +206 -0
- package/dist/core/codex-app/multi-provider-router.js +317 -0
- package/dist/core/codex-app/openrouter-activate.js +75 -10
- package/dist/core/codex-app/openrouter-provider.js +7 -0
- package/dist/core/codex-control/codex-app-server-v2-client.js +39 -2
- package/dist/core/codex-runtime/codex-desktop-config-policy.js +2 -0
- package/dist/core/commands/basic-cli.js +16 -22
- package/dist/core/commands/telegram-command.js +224 -108
- 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/macos-menubar-proof.js +58 -1
- package/dist/core/release/main-push-guard.js +287 -10
- package/dist/core/release/main-push-receipt.js +8 -1
- package/dist/core/release/package-size-budget.js +1 -1
- package/dist/core/release/release-gate-affected-selector.js +3 -2
- package/dist/core/release/runtime-script-pack-closure.js +67 -3
- package/dist/core/remote/index.js +2 -0
- package/dist/core/remote/local-worker-client.js +35 -0
- package/dist/core/remote/machine-registry.js +17 -10
- package/dist/core/remote/session-binding.js +153 -0
- package/dist/core/remote/types.js +1 -0
- package/dist/core/remote/worker.js +230 -14
- package/dist/core/routes.js +1 -1
- package/dist/core/subagents/official-subagent-preparation.js +53 -5
- package/dist/core/subagents/official-subagent-prompt.js +23 -0
- package/dist/core/subagents/official-subagent-runner.js +5 -0
- package/dist/core/subagents/role-model-preferences.js +315 -0
- package/dist/core/telegram/config.js +28 -9
- package/dist/core/telegram/hub.js +8 -2
- package/dist/core/telegram/index.js +2 -0
- package/dist/core/telegram/messages.js +8 -2
- package/dist/core/telegram/runtime-projection.js +6 -2
- package/dist/core/telegram/runtime.js +19 -9
- package/dist/core/telegram/service.js +181 -0
- package/dist/core/telegram/setup.js +220 -0
- package/dist/core/update/update-migration-state.js +24 -16
- package/dist/core/update/update-status.js +31 -3
- package/dist/core/update-check.js +13 -5
- 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/ControlCenterWindowController.swift +1 -1
- package/dist/native/sks-menubar/Sources/OverviewViewController.swift +85 -4
- package/dist/native/sks-menubar/Sources/ProcessClient.swift +1 -1
- package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +271 -0
- package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +412 -23
- package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +68 -23
- package/dist/native/sks-menubar/Sources/RemoteTelegramViewController.swift +157 -50
- package/dist/native/sks-menubar/Sources/StatusItemController.swift +3 -4
- package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +4 -1
- package/dist/scripts/postinstall-global-doctor-blackbox.js +10 -0
- package/dist/scripts/release-macos-menubar-proof.js +38 -3
- package/dist/scripts/release-provenance-check.js +3 -1
- package/dist/scripts/sks-menubar-install-check.js +1 -1
- package/package.json +3 -1
- package/release-gates.v2.json +13 -8
- package/runtime-required-scripts.json +9 -0
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.
|
|
25
|
+
Current release: **SKS 7.1.1**, 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
|
|
|
@@ -50,6 +54,12 @@ The SKS menu bar shows the installed Codex CLI version and latest known version.
|
|
|
50
54
|
|
|
51
55
|
**Manage MCP Servers…** opens a native macOS manager for the global `~/.codex/config.toml`. It can add remote URL or local stdio servers, enable/disable existing entries, remove entries after confirmation, and refresh the current state. Mutations are lock-protected, backed up, TOML-validated, and written with mode `0600`; configured environment values and command arguments are never rendered in the list. Changes apply to new Codex sessions. The same plumbing is available through the canonical `sks mcp config list|get|add|edit|duplicate|enable|disable|remove|test|login|logout|backups|restore` surface for diagnostics and automation.
|
|
52
56
|
|
|
57
|
+
### Telegram remote coding on this Mac
|
|
58
|
+
|
|
59
|
+
Open **SKS Center → Remote & Telegram**, create a private bot with BotFather, send `/start` to that bot from the Telegram account you want to pair, then choose **Connect Bot & Register Coding Session…** and paste the token. SKS verifies the bot, stores the token only in macOS Keychain, pairs only that private chat/user, and registers one dedicated session bound to the current project. Choose **Start Hub**, then send ordinary text to the bot. On the first real message, the Hub creates the Codex thread and starts its first turn in the same App Server connection so the thread is durably resumable; later messages resume that exact thread and return the final response to Telegram.
|
|
60
|
+
|
|
61
|
+
The Hub runs as a user LaunchAgent and uses `caffeinate -i` while the logged-in Mac is available. The Mac still needs to remain powered on, logged in, awake, and network-connected; closing the lid or logging out can stop access. Telegram input has no arbitrary shell path, runs with approvals disabled and network access disabled inside the Codex workspace sandbox, and cannot target an unpaired chat or another project.
|
|
62
|
+
|
|
53
63
|
## The Front Door
|
|
54
64
|
|
|
55
65
|
| Command | What it does |
|
|
@@ -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' },
|
|
@@ -36,7 +36,7 @@ export const COMMAND_MANIFEST_LITE = [
|
|
|
36
36
|
{ name: 'codex-lb', summary: 'Inspect codex-lb status and circuit health', maturity: 'beta' },
|
|
37
37
|
{ name: 'menubar', summary: 'Inspect/install/restart/uninstall SKS menu bar', maturity: 'beta', skipMigrationGate: true, allowedDuringActiveRoute: true, diagnostic: true },
|
|
38
38
|
{ name: 'remote', summary: 'Inspect official Remote readiness and run the proof-aware SSH stdio worker', maturity: 'beta' },
|
|
39
|
-
{ name: 'telegram', summary: '
|
|
39
|
+
{ name: 'telegram', summary: 'Pair and run the private local Codex Telegram Hub', maturity: 'beta' },
|
|
40
40
|
{ name: 'hooks', summary: 'Explain and inspect Codex hooks', maturity: 'beta', skipMigrationGate: true },
|
|
41
41
|
{ name: 'zellij-lane', summary: 'Render a Zellij lane frame for SKS sessions', maturity: 'beta' },
|
|
42
42
|
{ name: 'zellij-slot-pane', summary: 'Render a compact Zellij worker slot pane', maturity: 'beta' },
|
|
@@ -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')),
|
|
@@ -155,7 +155,7 @@ const COMMAND_DEFINITIONS = {
|
|
|
155
155
|
'codex-lb': entry('beta', 'Inspect codex-lb status and circuit health', 'dist/commands/codex-lb.js', directCommand(() => import('../commands/codex-lb.js'), 'dist/commands/codex-lb.js')),
|
|
156
156
|
menubar: activeRouteDiagnostic(entry('beta', 'Inspect/install/restart/uninstall SKS menu bar', 'dist/core/commands/menubar-command.js', subcommand(() => import('../core/commands/menubar-command.js'), 'menubarCommand', 'dist/core/commands/menubar-command.js', 'status'))),
|
|
157
157
|
remote: entry('beta', 'Inspect official Remote readiness and run the proof-aware SSH stdio worker', 'dist/core/commands/remote-command.js', argsCommand(() => import('../core/commands/remote-command.js'), 'remoteCommand', 'dist/core/commands/remote-command.js')),
|
|
158
|
-
telegram: entry('beta', '
|
|
158
|
+
telegram: entry('beta', 'Pair and run the private local Codex Telegram Hub', 'dist/core/commands/telegram-command.js', argsCommand(() => import('../core/commands/telegram-command.js'), 'telegramCommand', 'dist/core/commands/telegram-command.js')),
|
|
159
159
|
hooks: skipMigrationGate(entry('beta', 'Explain and inspect Codex hooks', 'dist/commands/hooks.js', directCommand(() => import('../commands/hooks.js'), 'dist/commands/hooks.js'))),
|
|
160
160
|
'zellij-lane': entry('beta', 'Render a Zellij lane frame for SKS sessions', 'dist/commands/zellij-lane.js', directCommand(() => import('../commands/zellij-lane.js'), 'dist/commands/zellij-lane.js')),
|
|
161
161
|
'zellij-slot-pane': entry('beta', 'Render a compact Zellij worker slot pane', 'dist/commands/zellij-slot-pane.js', directCommand(() => import('../commands/zellij-slot-pane.js'), 'dist/commands/zellij-slot-pane.js')),
|
|
@@ -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,10 @@ 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 { MULTI_PROVIDER_ROUTER_DEFAULT_BASE_URL, multiProviderRouterStatus, testMultiProviderRouter, useMultiProviderRouter } from '../core/codex-app/multi-provider-router.js';
|
|
20
|
+
import { resetRoleModelPreference, roleModelPreferencesStatus, setRoleModelPreference } from '../core/subagents/role-model-preferences.js';
|
|
18
21
|
export async function run(_command, args = []) {
|
|
19
22
|
const action = args[0] || 'check';
|
|
20
23
|
if (action === 'restart')
|
|
@@ -82,6 +85,56 @@ export async function run(_command, args = []) {
|
|
|
82
85
|
const result = await openRouterStatus({});
|
|
83
86
|
return printCodexAppResult(args, result);
|
|
84
87
|
}
|
|
88
|
+
if (action === 'openrouter-models') {
|
|
89
|
+
const models = await listOpenRouterModels({});
|
|
90
|
+
const result = flag(args, '--ids-only') ? compactOpenRouterModelsResult(models) : models;
|
|
91
|
+
return printCodexAppResult(args, result);
|
|
92
|
+
}
|
|
93
|
+
if (action === 'openrouter-test') {
|
|
94
|
+
const result = await testOpenRouterConnection({ model: readOption(args, '--model', '') });
|
|
95
|
+
return printCodexAppResult(args, result);
|
|
96
|
+
}
|
|
97
|
+
if (action === 'router-status' || action === 'multi-provider-status' || action === 'opencodex-status') {
|
|
98
|
+
const result = await multiProviderRouterStatus({
|
|
99
|
+
baseUrl: readOption(args, '--base-url', ''),
|
|
100
|
+
catalogPath: readOption(args, '--catalog', '')
|
|
101
|
+
});
|
|
102
|
+
return printCodexAppResult(args, result);
|
|
103
|
+
}
|
|
104
|
+
if (action === 'router-test' || action === 'multi-provider-test' || action === 'opencodex-test') {
|
|
105
|
+
const result = await testMultiProviderRouter({
|
|
106
|
+
baseUrl: readOption(args, '--base-url', MULTI_PROVIDER_ROUTER_DEFAULT_BASE_URL),
|
|
107
|
+
catalogPath: readOption(args, '--catalog', ''),
|
|
108
|
+
model: readOption(args, '--model', '')
|
|
109
|
+
});
|
|
110
|
+
return printCodexAppResult(args, result);
|
|
111
|
+
}
|
|
112
|
+
if (action === 'use-router' || action === 'use-multi-provider' || action === 'use-opencodex') {
|
|
113
|
+
const result = await useMultiProviderRouter({
|
|
114
|
+
model: readOption(args, '--model', ''),
|
|
115
|
+
baseUrl: readOption(args, '--base-url', MULTI_PROVIDER_ROUTER_DEFAULT_BASE_URL),
|
|
116
|
+
catalogPath: readOption(args, '--catalog', ''),
|
|
117
|
+
replaceCatalog: flag(args, '--replace-catalog'),
|
|
118
|
+
restartApp: flag(args, '--restart-app') || flag(args, '--restart') || !flag(args, '--no-restart-app')
|
|
119
|
+
});
|
|
120
|
+
return printCodexAppResult(args, result);
|
|
121
|
+
}
|
|
122
|
+
if (action === 'role-models') {
|
|
123
|
+
return printCodexAppResult(args, await roleModelPreferencesStatus());
|
|
124
|
+
}
|
|
125
|
+
if (action === 'set-role-model') {
|
|
126
|
+
const result = await setRoleModelPreference({
|
|
127
|
+
role: readOption(args, '--role', ''),
|
|
128
|
+
provider: readOption(args, '--provider', ''),
|
|
129
|
+
model: readOption(args, '--model', ''),
|
|
130
|
+
reasoning: readOption(args, '--reasoning', '')
|
|
131
|
+
});
|
|
132
|
+
return printCodexAppResult(args, result);
|
|
133
|
+
}
|
|
134
|
+
if (action === 'reset-role-model') {
|
|
135
|
+
const result = await resetRoleModelPreference({ role: readOption(args, '--role', '') });
|
|
136
|
+
return printCodexAppResult(args, result);
|
|
137
|
+
}
|
|
85
138
|
if (action === 'product-design' || action === 'design-product' || action === 'ensure-product-design') {
|
|
86
139
|
const checkOnly = flag(args, '--check-only') || flag(args, '--no-install');
|
|
87
140
|
const status = await codexProductDesignPluginStatus({
|
|
@@ -142,7 +195,7 @@ export async function run(_command, args = []) {
|
|
|
142
195
|
process.exitCode = 1;
|
|
143
196
|
return;
|
|
144
197
|
}
|
|
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]');
|
|
198
|
+
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>]|router-status [--base-url <loopback-url>] [--catalog <path>]|router-test --model <provider/model> [--base-url <loopback-url>] [--catalog <path>]|use-router --model <provider/model> [--base-url <loopback-url>] [--catalog <path>] [--replace-catalog]|role-models|set-role-model --role <name> [--provider <id>] --model <catalog-slug> --reasoning <effort>|reset-role-model --role <name>|product-design [--check-only]|ensure-product-design|chrome-extension|pat status|remote-control [--json]');
|
|
146
199
|
console.error('Note: glm-profile is retired (strips leftover Desktop GLM profiles); use set-openrouter-key / use-openrouter.');
|
|
147
200
|
process.exitCode = 1;
|
|
148
201
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { flag } from '../cli/args.js';
|
|
2
|
+
import { sksMenuBarRestartDeferred } from '../core/codex-app/sks-menubar.js';
|
|
3
|
+
export function doctorProfileFromArgs(args = [], doctorFix = false) {
|
|
4
|
+
const explicit = readOption(args, '--profile');
|
|
5
|
+
if (isDoctorProfile(explicit))
|
|
6
|
+
return explicit;
|
|
7
|
+
if (flag(args, '--full'))
|
|
8
|
+
return 'full';
|
|
9
|
+
if (flag(args, '--capabilities'))
|
|
10
|
+
return 'capabilities';
|
|
11
|
+
return doctorFix ? 'fix' : 'fast';
|
|
12
|
+
}
|
|
13
|
+
export function doctorArgWarnings(args = []) {
|
|
14
|
+
const warnings = [];
|
|
15
|
+
const explicit = readOption(args, '--profile');
|
|
16
|
+
if (explicit && !isDoctorProfile(explicit)) {
|
|
17
|
+
warnings.push(`unknown_profile:${explicit}; supported profiles: migration, full, capabilities, fast, fix`);
|
|
18
|
+
}
|
|
19
|
+
for (const value of unknownDoctorFlags(args))
|
|
20
|
+
warnings.push(`unknown_flag:${value}`);
|
|
21
|
+
return warnings;
|
|
22
|
+
}
|
|
23
|
+
export function doctorMenuBarInstallPolicy(args = [], doctorFix = false, env = process.env) {
|
|
24
|
+
const profile = doctorProfileFromArgs(args, doctorFix);
|
|
25
|
+
const phaseEnabled = doctorPhaseIdsForProfile(profile).includes('sks_menubar');
|
|
26
|
+
const apply = doctorFix && phaseEnabled;
|
|
27
|
+
return {
|
|
28
|
+
profile,
|
|
29
|
+
phase_enabled: phaseEnabled,
|
|
30
|
+
apply,
|
|
31
|
+
launch: apply && !sksMenuBarRestartDeferred(env)
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function doctorPhaseIdsForProfile(profile) {
|
|
35
|
+
const required = [
|
|
36
|
+
'codex_startup_repair',
|
|
37
|
+
'startup_config_repair',
|
|
38
|
+
'context7_repair',
|
|
39
|
+
'context7_mcp_repair',
|
|
40
|
+
'hook_trust_repair',
|
|
41
|
+
'command_alias_cleanup'
|
|
42
|
+
];
|
|
43
|
+
if (profile === 'migration')
|
|
44
|
+
return required;
|
|
45
|
+
const optional = ['supabase_mcp_repair', 'native_capability_repair', 'sks_menubar'];
|
|
46
|
+
if (profile === 'full' || profile === 'capabilities')
|
|
47
|
+
return ['setup', ...required, ...optional];
|
|
48
|
+
return [...required, ...optional];
|
|
49
|
+
}
|
|
50
|
+
function unknownDoctorFlags(args) {
|
|
51
|
+
const knownBoolean = new Set([
|
|
52
|
+
'--fix', '--yes', '-y', '--machine-only', '--actual-codex', '--require-actual-codex',
|
|
53
|
+
'--full', '--capabilities', '--repair-codex-app-ui', '--repair-zellij', '--install-homebrew',
|
|
54
|
+
'--repair-native-capabilities', '--repair-codex-native', '--local-only', '--global-only', '--project', '--global',
|
|
55
|
+
'--dry-run', '--json'
|
|
56
|
+
]);
|
|
57
|
+
const knownValue = new Set(['--profile', '--report-file', '--codex-bin', '--install-scope']);
|
|
58
|
+
const unknown = [];
|
|
59
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
60
|
+
const arg = String(args[index] || '');
|
|
61
|
+
if (!arg.startsWith('-'))
|
|
62
|
+
continue;
|
|
63
|
+
if (knownValue.has(arg)) {
|
|
64
|
+
index += 1;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (!knownBoolean.has(arg))
|
|
68
|
+
unknown.push(arg);
|
|
69
|
+
}
|
|
70
|
+
return unknown;
|
|
71
|
+
}
|
|
72
|
+
function isDoctorProfile(value) {
|
|
73
|
+
return ['migration', 'full', 'capabilities', 'fast', 'fix'].includes(String(value || ''));
|
|
74
|
+
}
|
|
75
|
+
function readOption(args, name) {
|
|
76
|
+
const index = args.indexOf(name);
|
|
77
|
+
return index >= 0 && args[index + 1] ? String(args[index + 1]) : null;
|
|
78
|
+
}
|