sneakoscope 6.0.1 → 6.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/bin/fast-inline.js +0 -55
- package/dist/bin/sks-dispatch.js +0 -22
- package/dist/cli/codex-app-command.js +1 -2
- package/dist/cli/help-fast.js +2 -2
- package/dist/cli/install-helpers.js +66 -37
- package/dist/commands/codex-app.js +8 -3
- package/dist/commands/codex-lb.js +37 -12
- package/dist/config/skills-manifest.json +57 -57
- package/dist/core/agents/agent-effort-policy.js +61 -67
- package/dist/core/agents/agent-orchestrator.js +3 -3
- package/dist/core/agents/agent-proof-evidence.js +16 -2
- package/dist/core/agents/agent-role-config.js +17 -10
- package/dist/core/agents/agent-roster.js +21 -41
- package/dist/core/agents/agent-scheduler.js +6 -5
- package/dist/core/agents/agent-schema.js +4 -5
- package/dist/core/agents/agent-task-graph.js +1 -1
- package/dist/core/agents/agent-work-partition.js +2 -1
- package/dist/core/agents/native-cli-session-swarm.js +18 -1
- package/dist/core/agents/native-worker-backend-router.js +179 -108
- package/dist/core/agents/parallel-write-fixture.js +15 -10
- package/dist/core/agents/route-collaboration-ledger.js +2 -2
- package/dist/core/agents/runtime-proof-summary.js +21 -3
- package/dist/core/auto-review.js +1 -4
- package/dist/core/codex/codex-config-guard.js +9 -13
- package/dist/core/codex-adapter.js +2 -2
- package/dist/core/codex-app/codex-agent-role-sync.js +0 -1
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +6 -40
- package/dist/core/codex-app/codex-app-restart.js +38 -8
- package/dist/core/codex-app/codex-app-ui-state-snapshot.js +1 -7
- package/dist/core/codex-app/sks-menubar.js +92 -37
- package/dist/core/codex-app.js +26 -4
- package/dist/core/codex-control/codex-model-capabilities.js +1 -2
- package/dist/core/codex-control/codex-model-metadata.js +18 -7
- package/dist/core/codex-control/codex-sdk-config-policy.js +3 -3
- package/dist/core/codex-control/codex-task-runner.js +2 -3
- package/dist/core/codex-lb/codex-lb-env.js +78 -0
- package/dist/core/codex-model-guard.js +24 -87
- package/dist/core/commands/basic-cli.js +21 -0
- package/dist/core/commands/loop-command.js +2 -2
- package/dist/core/commands/naruto-command.js +48 -20
- package/dist/core/commands/profile-command.js +14 -5
- package/dist/core/commands/qa-loop-command.js +2 -2
- package/dist/core/commands/research-command.js +2 -2
- package/dist/core/doctor/doctor-codex-startup-repair.js +0 -3
- package/dist/core/evaluation.js +1 -1
- package/dist/core/feature-fixtures.js +2 -2
- package/dist/core/feature-registry.js +1 -1
- package/dist/core/hooks-runtime.js +7 -45
- package/dist/core/image-ux-review/imagegen-adapter.js +17 -2
- package/dist/core/init/skills.js +6 -7
- package/dist/core/init.js +6 -20
- package/dist/core/loops/loop-concurrency-budget.js +6 -4
- package/dist/core/loops/loop-planner.js +1 -1
- package/dist/core/loops/loop-runtime.js +2 -2
- package/dist/core/loops/loop-scheduler.js +7 -7
- package/dist/core/managed-assets/managed-assets-manifest.js +1 -4
- package/dist/core/naruto/naruto-concurrency-governor.js +20 -17
- package/dist/core/naruto/naruto-loop-mesh.js +3 -3
- package/dist/core/naruto/naruto-write-e2e.js +3 -4
- package/dist/core/naruto/resource-pressure-monitor.js +5 -2
- package/dist/core/provider/model-router.js +77 -36
- package/dist/core/release/package-size-budget.js +2 -0
- package/dist/core/release/release-gate-batch-runner.js +2 -1
- package/dist/core/release/release-gate-resource-governor.js +12 -12
- package/dist/core/release/resource-class-budget.js +5 -5
- package/dist/core/routes/constants.js +4 -5
- package/dist/core/routes/model-mode-router.js +3 -0
- package/dist/core/routes.js +8 -10
- package/dist/core/structured-output-adapter.js +15 -1
- package/dist/core/update/update-migration-state.js +4 -33
- package/dist/core/update/update-notice.js +23 -0
- package/dist/core/update-check.js +46 -10
- package/dist/core/version.js +1 -1
- package/dist/scripts/agent-backfill-replenishment-check.js +1 -1
- package/dist/scripts/agent-dynamic-pool-check.js +3 -3
- package/dist/scripts/agent-dynamic-pool-fixture.js +1 -1
- package/dist/scripts/agent-goal-mode-propagation-check.js +1 -1
- package/dist/scripts/agent-patch-swarm-gate-lib.js +2 -2
- package/dist/scripts/agent-role-config-repair-check.js +9 -5
- package/dist/scripts/agent-scheduler-proof-check.js +1 -1
- package/dist/scripts/agent-scheduler-proof-hardening-check.js +1 -1
- package/dist/scripts/agent-source-intelligence-propagation-check.js +1 -1
- package/dist/scripts/agent-strategy-to-lease-wiring-check.js +2 -2
- package/dist/scripts/agent-zellij-dynamic-backfill-panes-check.js +1 -1
- package/dist/scripts/codex-app-fast-ui-preservation-check.js +2 -2
- package/dist/scripts/codex-app-provider-model-ui-check.js +17 -2
- package/dist/scripts/codex-app-ui-preservation-check.js +6 -5
- package/dist/scripts/codex-lb-fast-ui-preservation-check.js +3 -3
- package/dist/scripts/codex-lb-gpt55-fast-profile-check.js +2 -3
- package/dist/scripts/doctor-fix-proves-codex-read-check.js +1 -1
- package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +7 -7
- package/dist/scripts/gate-policy-audit-check.js +2 -2
- package/dist/scripts/install-update-preserves-config-check.js +4 -4
- package/dist/scripts/legacy-upgrade-matrix-check.js +1 -1
- package/dist/scripts/lib/native-cli-session-swarm-check-lib.js +2 -1
- package/dist/scripts/loop-hardening-check-lib.js +1 -1
- package/dist/scripts/naruto-parallelism-mode-check.js +2 -1
- package/dist/scripts/naruto-shadow-clone-swarm-check.js +103 -74
- package/dist/scripts/npm-publish-performance-check.js +2 -1
- package/dist/scripts/packlist-performance-check.js +5 -2
- package/dist/scripts/release-aggressive-resource-governor-check.js +2 -2
- package/dist/scripts/release-dag-full-coverage-check.js +7 -7
- package/dist/scripts/release-gate-existence-audit.js +8 -9
- package/dist/scripts/release-parallel-check.js +1 -2
- package/dist/scripts/release-real-check.js +1 -1
- package/dist/scripts/scheduler-batch-dispatch-check.js +1 -1
- package/dist/scripts/sizecheck.js +9 -4
- package/dist/scripts/sks-menubar-install-check.js +7 -0
- package/dist/scripts/update-default-command-check.js +4 -2
- package/package.json +5 -17
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ sneakoscope is a proof-first Codex trust layer for bounded agent workflows, sear
|
|
|
25
25
|
|
|
26
26
|
Sneakoscope Codex (`sks`) is a Codex CLI and Codex App harness for people who want parallel AI coding without losing proof. It gives Codex a simple front door, a dynamic worker swarm, a local dashboard, TriWiki project memory, and release gates that separate machine evidence from LLM opinion.
|
|
27
27
|
|
|
28
|
-
Current release: SKS **6.0.
|
|
28
|
+
Current release: SKS **6.0.3**. Normal Codex sessions continue to inherit the complete Codex catalog without silent replacement. Naruto workers now use a scoped, live-catalog-verified GPT-5.6 policy: Terra `xhigh/max` for coding, Sol `max` for refactoring/planning/strategy/integration, and Luna `xhigh/max` for E2E/browser/Computer Use/GUI verification. This release also fixes menu-bar auth restarts, cross-version menu updates, status checkmarks, dashboard readiness, and stale update badges. See [CHANGELOG.md](CHANGELOG.md).
|
|
29
29
|
|
|
30
30
|
## Install
|
|
31
31
|
|
|
@@ -96,6 +96,7 @@ It shows the v5 flow: one-line install, `$Plan`, `$Work`/`$Swarm`, `sks review`,
|
|
|
96
96
|
- Native capability repair: `sks doctor --fix` (imagegen/Computer Use/Browser Use), `.sneakoscope/reports/native-capability-readiness.json`
|
|
97
97
|
- Agent bridge for any agent system: `sks mcp-server`, `sks agent-bridge setup`, `SKS_AGENT_MODE=1` — see [docs/AGENT-BRIDGE.md](docs/AGENT-BRIDGE.md)
|
|
98
98
|
- Release gates: `node ./dist/scripts/release-gate-dag-runner.js --preset release --full`
|
|
99
|
+
- Lifecycle-disabled npm release: run `npm run publish:verify-ignore-scripts` first, then `npm publish --ignore-scripts`; the raw publish command intentionally cannot run build or validation hooks.
|
|
99
100
|
- Release readiness notes: [docs/release-readiness.md](docs/release-readiness.md) and [CHANGELOG.md](CHANGELOG.md)
|
|
100
101
|
- Image generation review routes require Codex App `$imagegen`/`gpt-image-2` evidence with recorded output hashes; direct API fallback and mock fixtures do not satisfy full route gates.
|
|
101
102
|
|
|
@@ -106,6 +107,8 @@ It shows the v5 flow: one-line install, `$Plan`, `$Work`/`$Swarm`, `sks review`,
|
|
|
106
107
|
- macOS optional: menu bar integration and `/usr/bin/open`
|
|
107
108
|
- The menubar icon shows and hides itself automatically as the Codex desktop app launches/quits; set `quit_with_codex: true` in `~/.codex/sks-menubar/config.json` to have the menubar fully quit with Codex instead of just hiding (default `false`).
|
|
108
109
|
- Native input dialogs (API keys, codex-lb setup) pass secrets to `sks` via `--api-key-stdin` instead of a visible Terminal window or process arguments.
|
|
110
|
+
- Auth/provider changes wait for the ChatGPT/Codex app to exit and reopen by bundle id; a failed restart is reported as a failed menu action.
|
|
111
|
+
- Update installs always rebuild the companion with the newly installed SKS package, preventing a previous-version updater from restoring a stale menu binary.
|
|
109
112
|
- The menubar dropdown's `View Last Log` item opens the most recent background action's log file, so you don't need to keep a Terminal window open to see command output.
|
|
110
113
|
- `sks menubar status --json` reports a `codex_sync` object with `bundle_id`, `codex_running`, and `icon_visible_expected` to show Codex-lifecycle detection state.
|
|
111
114
|
- Zellij optional but recommended for terminal worker panes
|
package/dist/bin/fast-inline.js
CHANGED
|
@@ -156,58 +156,3 @@ exports.rootJsonFastInline = rootJsonFastInline;
|
|
|
156
156
|
exports.doctorJsonFastInline = doctorJsonFastInline;
|
|
157
157
|
exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
|
|
158
158
|
exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
|
|
159
|
-
|
|
160
|
-
exports.rootJsonFastInline = rootJsonFastInline;
|
|
161
|
-
exports.doctorJsonFastInline = doctorJsonFastInline;
|
|
162
|
-
exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
|
|
163
|
-
exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
|
|
164
|
-
|
|
165
|
-
exports.rootJsonFastInline = rootJsonFastInline;
|
|
166
|
-
exports.doctorJsonFastInline = doctorJsonFastInline;
|
|
167
|
-
exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
|
|
168
|
-
exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
|
|
169
|
-
|
|
170
|
-
exports.rootJsonFastInline = rootJsonFastInline;
|
|
171
|
-
exports.doctorJsonFastInline = doctorJsonFastInline;
|
|
172
|
-
exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
|
|
173
|
-
exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
|
|
174
|
-
|
|
175
|
-
exports.rootJsonFastInline = rootJsonFastInline;
|
|
176
|
-
exports.doctorJsonFastInline = doctorJsonFastInline;
|
|
177
|
-
exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
|
|
178
|
-
exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
|
|
179
|
-
|
|
180
|
-
exports.rootJsonFastInline = rootJsonFastInline;
|
|
181
|
-
exports.doctorJsonFastInline = doctorJsonFastInline;
|
|
182
|
-
exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
|
|
183
|
-
exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
|
|
184
|
-
|
|
185
|
-
exports.rootJsonFastInline = rootJsonFastInline;
|
|
186
|
-
exports.doctorJsonFastInline = doctorJsonFastInline;
|
|
187
|
-
exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
|
|
188
|
-
exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
|
|
189
|
-
|
|
190
|
-
exports.rootJsonFastInline = rootJsonFastInline;
|
|
191
|
-
exports.doctorJsonFastInline = doctorJsonFastInline;
|
|
192
|
-
exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
|
|
193
|
-
exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
|
|
194
|
-
|
|
195
|
-
exports.rootJsonFastInline = rootJsonFastInline;
|
|
196
|
-
exports.doctorJsonFastInline = doctorJsonFastInline;
|
|
197
|
-
exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
|
|
198
|
-
exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
|
|
199
|
-
|
|
200
|
-
exports.rootJsonFastInline = rootJsonFastInline;
|
|
201
|
-
exports.doctorJsonFastInline = doctorJsonFastInline;
|
|
202
|
-
exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
|
|
203
|
-
exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
|
|
204
|
-
|
|
205
|
-
exports.rootJsonFastInline = rootJsonFastInline;
|
|
206
|
-
exports.doctorJsonFastInline = doctorJsonFastInline;
|
|
207
|
-
exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
|
|
208
|
-
exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
|
|
209
|
-
|
|
210
|
-
exports.rootJsonFastInline = rootJsonFastInline;
|
|
211
|
-
exports.doctorJsonFastInline = doctorJsonFastInline;
|
|
212
|
-
exports.narutoHelpJsonFastInline = narutoHelpJsonFastInline;
|
|
213
|
-
exports.hookUserPromptSubmitPerfInline = hookUserPromptSubmitPerfInline;
|
package/dist/bin/sks-dispatch.js
CHANGED
|
@@ -62,25 +62,3 @@ async function runSks(args) {
|
|
|
62
62
|
exports.runSks = runSks;
|
|
63
63
|
|
|
64
64
|
exports.runSks = runSks;
|
|
65
|
-
|
|
66
|
-
exports.runSks = runSks;
|
|
67
|
-
|
|
68
|
-
exports.runSks = runSks;
|
|
69
|
-
|
|
70
|
-
exports.runSks = runSks;
|
|
71
|
-
|
|
72
|
-
exports.runSks = runSks;
|
|
73
|
-
|
|
74
|
-
exports.runSks = runSks;
|
|
75
|
-
|
|
76
|
-
exports.runSks = runSks;
|
|
77
|
-
|
|
78
|
-
exports.runSks = runSks;
|
|
79
|
-
|
|
80
|
-
exports.runSks = runSks;
|
|
81
|
-
|
|
82
|
-
exports.runSks = runSks;
|
|
83
|
-
|
|
84
|
-
exports.runSks = runSks;
|
|
85
|
-
|
|
86
|
-
exports.runSks = runSks;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { codexRemoteControlStatus, formatCodexRemoteControlStatus } from '../core/codex-app.js';
|
|
3
|
-
import { forceRequiredCodexModelConfigArgs } from '../core/codex-model-guard.js';
|
|
4
3
|
export async function codexAppRemoteControlCommand(args = [], opts = {}) {
|
|
5
4
|
const controlArgs = argsBeforeSeparator(args);
|
|
6
5
|
if (controlArgs.includes('--help') || controlArgs.includes('-h')) {
|
|
@@ -25,7 +24,7 @@ export async function codexAppRemoteControlCommand(args = [], opts = {}) {
|
|
|
25
24
|
process.exitCode = 1;
|
|
26
25
|
return;
|
|
27
26
|
}
|
|
28
|
-
const passthrough =
|
|
27
|
+
const passthrough = stripSeparator(args).map((arg) => String(arg));
|
|
29
28
|
const spawnFn = opts.spawn || spawn;
|
|
30
29
|
const code = await spawnInherited(spawnFn, status.codex_cli.bin, ['remote-control', ...passthrough], {
|
|
31
30
|
cwd: process.cwd(),
|
package/dist/cli/help-fast.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { COMMAND_MANIFEST_LITE } from './command-manifest-lite.js';
|
|
2
|
-
|
|
2
|
+
import { PACKAGE_VERSION } from '../core/version.js';
|
|
3
3
|
export function helpFast() {
|
|
4
4
|
console.log(`SKS
|
|
5
|
-
SNEAKOSCOPE CODEX v${
|
|
5
|
+
SNEAKOSCOPE CODEX v${PACKAGE_VERSION}
|
|
6
6
|
|
|
7
7
|
3-pillar frontdoor
|
|
8
8
|
|
|
@@ -23,7 +23,6 @@ import { extractTomlTable, writeCodexConfigGuarded } from '../core/codex/codex-c
|
|
|
23
23
|
import { cleanupCodexConfigBackups, validateCodexConfigRoundTrip } from '../core/codex/codex-config-toml.js';
|
|
24
24
|
import { runPostinstallGlobalDoctorAndMarkPending } from '../core/update/update-migration-state.js';
|
|
25
25
|
import { repairCodexImagegen } from '../core/doctor/imagegen-repair.js';
|
|
26
|
-
import { GPT55_CODEX_MODEL, GPT56_CODEX_MODELS, REQUIRED_CODEX_MODEL } from '../core/codex-model-guard.js';
|
|
27
26
|
const CODEX_LB_PROVIDER_NAME = 'openai';
|
|
28
27
|
const CODEX_LB_PROVIDER_ENV_KEY = 'CODEX_LB_API_KEY';
|
|
29
28
|
const CODEX_LB_CANONICAL_FAST_SERVICE_TIER = 'priority';
|
|
@@ -276,7 +275,7 @@ async function postinstallHarnessConflictNotice(conflictScan) {
|
|
|
276
275
|
console.log(formatHarnessConflictReport(conflictScan, { includePrompt: false }));
|
|
277
276
|
console.log('\nWhat this means: npm can finish installing the package, but `sks setup` and `sks doctor --fix` will refuse to activate SKS until the conflicting harness is removed with human approval.');
|
|
278
277
|
console.log('No files were removed by postinstall.');
|
|
279
|
-
console.log(
|
|
278
|
+
console.log('Cleanup requires a human-approved Codex App session. Keep the model selected in Codex and use high reasoning effort.');
|
|
280
279
|
if (shouldAskPostinstallQuestion()) {
|
|
281
280
|
const answer = await askPostinstallQuestion('Show the cleanup prompt now? [y/N] ');
|
|
282
281
|
if (/^(y|yes|예|네|응)$/i.test(answer.trim())) {
|
|
@@ -902,7 +901,9 @@ export async function checkCodexLbResponseChain(status = {}, opts = {}) {
|
|
|
902
901
|
const fetchImpl = opts.fetch || globalThis.fetch;
|
|
903
902
|
if (typeof fetchImpl !== 'function')
|
|
904
903
|
return { ok: true, status: 'skipped', skipped: true, reason: 'fetch unavailable' };
|
|
905
|
-
const model = opts.model || env.SKS_CODEX_MODEL ||
|
|
904
|
+
const model = String(opts.model || env.SKS_CODEX_MODEL || env.CODEX_MODEL || '').trim();
|
|
905
|
+
if (!model)
|
|
906
|
+
return { ok: true, status: 'skipped', skipped: true, reason: 'model_unselected_use_explicit_model_or_codex_catalog' };
|
|
906
907
|
const timeoutMs = Number(opts.timeoutMs || env.SKS_CODEX_LB_CHAIN_CHECK_TIMEOUT_MS || 8000);
|
|
907
908
|
const serviceTier = opts.fastMode === true || opts.serviceTier === 'fast' || opts.serviceTier === CODEX_LB_CANONICAL_FAST_SERVICE_TIER
|
|
908
909
|
? CODEX_LB_CANONICAL_FAST_SERVICE_TIER
|
|
@@ -1396,6 +1397,30 @@ export async function releaseCodexLbAuthHold(opts = {}) {
|
|
|
1396
1397
|
const authPath = opts.authPath || codexAuthPath(home);
|
|
1397
1398
|
const backupPath = opts.backupPath || codexAuthChatgptBackupPath(home);
|
|
1398
1399
|
const configPath = opts.configPath || codexLbConfigPath(home);
|
|
1400
|
+
const currentAuthText = await readText(authPath, '');
|
|
1401
|
+
const trimmedCurrent = currentAuthText.trim();
|
|
1402
|
+
// Repeated "Use ChatGPT OAuth" is idempotent. If OAuth/browser auth is
|
|
1403
|
+
// already active, a historical backup is unnecessary; only ensure that the
|
|
1404
|
+
// codex-lb provider is no longer selected.
|
|
1405
|
+
const currentAuthMode = codexAuthModeSummary(currentAuthText);
|
|
1406
|
+
if (!opts.force && (currentAuthMode.mode === 'chatgpt_oauth' || currentAuthMode.mode === 'browser_marker')) {
|
|
1407
|
+
let providerUnselected = false;
|
|
1408
|
+
let providerError = null;
|
|
1409
|
+
if (!opts.keepProvider) {
|
|
1410
|
+
const unselected = await unselectCodexLbProvider({ ...opts, home, configPath });
|
|
1411
|
+
if (unselected.status === 'unselected' || unselected.status === 'not_selected')
|
|
1412
|
+
providerUnselected = true;
|
|
1413
|
+
else if (unselected.status === 'failed')
|
|
1414
|
+
providerError = unselected.error || unselected.reason || 'unselect_failed';
|
|
1415
|
+
}
|
|
1416
|
+
return {
|
|
1417
|
+
status: providerError ? 'failed' : 'already_chatgpt',
|
|
1418
|
+
auth_path: authPath,
|
|
1419
|
+
backup_path: backupPath,
|
|
1420
|
+
provider_unselected: providerUnselected,
|
|
1421
|
+
provider_error: providerError
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1399
1424
|
const backupExists = await exists(backupPath);
|
|
1400
1425
|
const backupText = backupExists ? await readText(backupPath, '') : '';
|
|
1401
1426
|
if (!backupExists || !backupText.trim()) {
|
|
@@ -1415,8 +1440,6 @@ export async function releaseCodexLbAuthHold(opts = {}) {
|
|
|
1415
1440
|
provider_unselected: false
|
|
1416
1441
|
};
|
|
1417
1442
|
}
|
|
1418
|
-
const currentAuthText = await readText(authPath, '');
|
|
1419
|
-
const trimmedCurrent = currentAuthText.trim();
|
|
1420
1443
|
// If auth.json already looks like ChatGPT OAuth (user re-logged in some other way), don't
|
|
1421
1444
|
// clobber it — but still honor the deselect request so the OAuth blob takes effect.
|
|
1422
1445
|
if (trimmedCurrent && hasChatgptOAuthTokens(currentAuthText) && !opts.force) {
|
|
@@ -1894,10 +1917,9 @@ function normalizeCodexFastModeUiConfigOnce(text = '', opts = {}) {
|
|
|
1894
1917
|
// `default_profile`, `[profiles.<name>]` tables, `[user.fast_mode]`, and
|
|
1895
1918
|
// `notice.fast_default_opt_out`. The only documented fast-default mechanism is
|
|
1896
1919
|
// the plain top-level `service_tier = "fast"`. This pass strips every legacy
|
|
1897
|
-
//
|
|
1920
|
+
// schema stamps SKS wrote and keeps user-authored top-level choices intact.
|
|
1898
1921
|
let next = String(text || '');
|
|
1899
1922
|
next = removeLegacyTopLevelCodexModeLocks(next);
|
|
1900
|
-
next = normalizeCodexAppDefaultModel(next);
|
|
1901
1923
|
next = removeTopLevelTomlKeyIfValue(next, 'default_profile', 'sks-fast-high');
|
|
1902
1924
|
next = removeTomlTable(next, 'user.fast_mode');
|
|
1903
1925
|
next = removeTomlTable(next, 'profiles.sks-fast-high');
|
|
@@ -1910,7 +1932,9 @@ function normalizeCodexFastModeUiConfigOnce(text = '', opts = {}) {
|
|
|
1910
1932
|
'browser_use', 'browser_use_external', 'image_generation', 'in_app_browser',
|
|
1911
1933
|
'guardian_approval', 'tool_suggest', 'plugins'
|
|
1912
1934
|
]) {
|
|
1913
|
-
|
|
1935
|
+
// Earlier SKS releases wrote these flags as `true`. Remove only that
|
|
1936
|
+
// managed legacy value; an explicit user `false` remains authoritative.
|
|
1937
|
+
next = removeTomlTableKey(next, 'features', legacyFlag, 'true');
|
|
1914
1938
|
}
|
|
1915
1939
|
if (opts.forceFastMode === true) {
|
|
1916
1940
|
next = upsertTopLevelTomlString(next, 'service_tier', 'fast');
|
|
@@ -1926,6 +1950,10 @@ function normalizeCodexFastModeUiConfigOnce(text = '', opts = {}) {
|
|
|
1926
1950
|
for (const featureLine of ['hooks = true', 'multi_agent = true', 'fast_mode = true', 'apps = true']) {
|
|
1927
1951
|
next = upsertTomlTableKeyIfAbsent(next, 'features', featureLine);
|
|
1928
1952
|
}
|
|
1953
|
+
if (process.env.SKS_ALLOW_HIGH_AGENT_CONCURRENCY !== '1') {
|
|
1954
|
+
next = upsertTomlTableKey(next, 'agents', 'max_threads = 4');
|
|
1955
|
+
}
|
|
1956
|
+
next = removeTomlTable(next, 'features.multi_agent_v2');
|
|
1929
1957
|
// Plugin auto-enable is OPT-IN only. Force-writing `[plugins."name@marketplace"] enabled =
|
|
1930
1958
|
// true` for marketplace plugins the App may not have installed (different build/channel)
|
|
1931
1959
|
// makes the App reference plugins it cannot load -> broken/blocked plugin UI. It also
|
|
@@ -1967,28 +1995,18 @@ function removeLegacyTopLevelCodexModeLocks(text = '') {
|
|
|
1967
1995
|
const lines = String(text || '').split('\n');
|
|
1968
1996
|
const firstTable = lines.findIndex((x) => /^\s*\[.+\]\s*$/.test(x));
|
|
1969
1997
|
const end = firstTable === -1 ? lines.length : firstTable;
|
|
1970
|
-
const topLevelModel = topLevelTomlString(text, 'model');
|
|
1971
|
-
const removeSksOwnedModeLock = isLegacySksCodexModelStamp(topLevelModel);
|
|
1972
1998
|
return lines.filter((line, index) => {
|
|
1973
1999
|
if (index >= end)
|
|
1974
2000
|
return true;
|
|
1975
|
-
if (
|
|
2001
|
+
if (!/^\s*(?:model|model_reasoning_effort)\s*=/.test(line))
|
|
1976
2002
|
return true;
|
|
1977
|
-
return
|
|
2003
|
+
return ![line, lines[index - 1] || ''].some((candidate) => {
|
|
2004
|
+
const trimmed = String(candidate || '').trim();
|
|
2005
|
+
const comment = trimmed.startsWith('#') ? trimmed : trimmed.includes('#') ? trimmed.slice(trimmed.indexOf('#')) : '';
|
|
2006
|
+
return /(?:SKS|Sneakoscope|codex-lb|sks fast)/i.test(comment);
|
|
2007
|
+
});
|
|
1978
2008
|
}).join('\n').replace(/^\n+/, '').replace(/\n{3,}/g, '\n\n');
|
|
1979
2009
|
}
|
|
1980
|
-
function normalizeCodexAppDefaultModel(text = '') {
|
|
1981
|
-
const currentModel = topLevelTomlString(text, 'model');
|
|
1982
|
-
if (!currentModel)
|
|
1983
|
-
return upsertTopLevelTomlString(text, 'model', REQUIRED_CODEX_MODEL);
|
|
1984
|
-
if (isLegacySksCodexModelStamp(currentModel))
|
|
1985
|
-
return upsertTopLevelTomlString(text, 'model', REQUIRED_CODEX_MODEL);
|
|
1986
|
-
return String(text || '');
|
|
1987
|
-
}
|
|
1988
|
-
function isLegacySksCodexModelStamp(value = '') {
|
|
1989
|
-
const model = String(value || '').trim().toLowerCase();
|
|
1990
|
-
return model === REQUIRED_CODEX_MODEL || model === GPT55_CODEX_MODEL || GPT56_CODEX_MODELS.includes(model);
|
|
1991
|
-
}
|
|
1992
2010
|
function removeTopLevelTomlKeyIfValue(text = '', key = '', value = '') {
|
|
1993
2011
|
const lines = String(text || '').split('\n');
|
|
1994
2012
|
const firstTable = lines.findIndex((x) => /^\s*\[.+\]\s*$/.test(x));
|
|
@@ -1996,7 +2014,7 @@ function removeTopLevelTomlKeyIfValue(text = '', key = '', value = '') {
|
|
|
1996
2014
|
const keyPattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*"${escapeRegExp(value)}"\\s*(?:#.*)?$`);
|
|
1997
2015
|
return lines.filter((line, index) => index >= end || !keyPattern.test(line)).join('\n').replace(/^\n+/, '').replace(/\n{3,}/g, '\n\n');
|
|
1998
2016
|
}
|
|
1999
|
-
function removeTomlTableKey(text, table, key) {
|
|
2017
|
+
function removeTomlTableKey(text, table, key, expectedValue = null) {
|
|
2000
2018
|
const lines = String(text || '').trimEnd().split('\n');
|
|
2001
2019
|
if (lines.length === 1 && lines[0] === '')
|
|
2002
2020
|
return '';
|
|
@@ -2014,7 +2032,10 @@ function removeTomlTableKey(text, table, key) {
|
|
|
2014
2032
|
break;
|
|
2015
2033
|
}
|
|
2016
2034
|
}
|
|
2017
|
-
const
|
|
2035
|
+
const valuePattern = expectedValue === null
|
|
2036
|
+
? ''
|
|
2037
|
+
: `\\s*${escapeRegExp(String(expectedValue))}\\s*(?:#.*)?$`;
|
|
2038
|
+
const keyPattern = new RegExp(`^\\s*${key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*=${valuePattern}`);
|
|
2018
2039
|
return lines.filter((line, index) => index <= start || index >= end || !keyPattern.test(line)).join('\n').replace(/\n{3,}/g, '\n\n');
|
|
2019
2040
|
}
|
|
2020
2041
|
function upsertTomlTableKey(text, table, line) {
|
|
@@ -2168,7 +2189,15 @@ export function codexFastModeDesktopStatus(text = '') {
|
|
|
2168
2189
|
service_tier: validation.service_tier ?? null,
|
|
2169
2190
|
model: validation.model ?? null,
|
|
2170
2191
|
legacy_keys: validation.legacy_keys,
|
|
2171
|
-
validation
|
|
2192
|
+
validation: {
|
|
2193
|
+
ok: validation.ok,
|
|
2194
|
+
blockers: validation.blockers,
|
|
2195
|
+
parse_error: validation.parse_error || null,
|
|
2196
|
+
service_tier: validation.service_tier ?? null,
|
|
2197
|
+
model: validation.model ?? null,
|
|
2198
|
+
model_reasoning_effort: validation.model_reasoning_effort ?? null,
|
|
2199
|
+
legacy_keys: validation.legacy_keys
|
|
2200
|
+
}
|
|
2172
2201
|
};
|
|
2173
2202
|
}
|
|
2174
2203
|
function upsertTopLevelTomlBoolean(text, key, value) {
|
|
@@ -2877,8 +2906,8 @@ export async function selftestCodexLb(tmp) {
|
|
|
2877
2906
|
// escape that dash emits literally and bash collapses to `"`).
|
|
2878
2907
|
await writeTextAtomic(codexLbFakeCodex, "#!/bin/sh\nif [ \"$1\" = \"--version\" ]; then echo \"codex-cli 99.0.0\"; exit 0; fi\nif [ \"$1\" = \"login\" ] && [ \"$2\" = \"status\" ]; then echo \"logged in with browser auth\"; exit 0; fi\nif [ \"$1\" = \"login\" ] && [ \"$2\" = \"--with-api-key\" ]; then read key; mkdir -p \"$HOME/.codex\"; printf '{\"auth_mode\":\"apikey\",\"OPENAI_API_KEY\":\"%s\"}\\n' \"$key\" > \"$HOME/.codex/auth.json\"; printf '%s\\n' \"$key\" >> \"$HOME/.codex/login-calls.log\"; exit 0; fi\necho \"fake codex unsupported\" >&2\nexit 1\n");
|
|
2879
2908
|
await fsp.chmod(codexLbFakeCodex, 0o755);
|
|
2880
|
-
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), `
|
|
2881
|
-
const codexLbEnvForSelftest = { HOME: codexLbHome, SKS_GLOBAL_ROOT: path.join(tmp, 'codex-lb-global'), PATH: `${codexLbFakeBin}${path.delimiter}${process.env.PATH || ''}`, SKS_SKIP_CODEX_LB_LAUNCH_ENV: '1' };
|
|
2909
|
+
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), `model_reasoning_effort = "low"\nservice_tier = "fast"\nsuppress_unstable_features_warning = true\n\n[profiles.custom]\nmodel_reasoning_effort = "low"\n\n[notice]\nfast_default_opt_out = true\n\n[features]\nhooks = true\n`);
|
|
2910
|
+
const codexLbEnvForSelftest = { HOME: codexLbHome, SKS_GLOBAL_ROOT: path.join(tmp, 'codex-lb-global'), PATH: `${codexLbFakeBin}${path.delimiter}${process.env.PATH || ''}`, SKS_SKIP_CODEX_LB_LAUNCH_ENV: '1', SKS_CODEX_MODEL: 'selftest-codex-model' };
|
|
2882
2911
|
const codexLbSetup = await runProcess(process.execPath, [packagedSksEntrypoint(), 'codex-lb', 'setup', '--host', 'lb.example.test', '--api-key', 'sk-test', '--json'], {
|
|
2883
2912
|
cwd: tmp,
|
|
2884
2913
|
env: codexLbEnvForSelftest,
|
|
@@ -2982,7 +3011,7 @@ export async function selftestCodexLb(tmp) {
|
|
|
2982
3011
|
await postinstall({
|
|
2983
3012
|
bootstrap: async () => {
|
|
2984
3013
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'auth.json'), '{"auth_mode":"browser"}\n');
|
|
2985
|
-
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), `
|
|
3014
|
+
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), `service_tier = "fast"\nsuppress_unstable_features_warning = true\n\n[features]\nhooks = true\n`);
|
|
2986
3015
|
}
|
|
2987
3016
|
});
|
|
2988
3017
|
}
|
|
@@ -3008,7 +3037,7 @@ export async function selftestCodexLb(tmp) {
|
|
|
3008
3037
|
await writeTextAtomic(path.join(doctorProject, 'package.json'), '{"name":"codex-lb-doctor-project","version":"0.0.0"}\n');
|
|
3009
3038
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'sks-codex-lb.env'), "export CODEX_LB_BASE_URL='https://lb.example.test/backend-api/codex'\nexport CODEX_LB_API_KEY='sk-test'\n");
|
|
3010
3039
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'auth.json'), '{"auth_mode":"browser"}\n');
|
|
3011
|
-
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), `
|
|
3040
|
+
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), `service_tier = "fast"\nsuppress_unstable_features_warning = true\n\n[features]\nhooks = true\n`);
|
|
3012
3041
|
const codexLbDoctorRepair = await runProcess(process.execPath, [packagedSksEntrypoint(), 'doctor', '--fix', '--json'], {
|
|
3013
3042
|
cwd: doctorProject,
|
|
3014
3043
|
env: { ...codexLbEnvForSelftest, SKS_GLOBAL_ROOT: path.join(tmp, 'codex-lb-doctor-global') },
|
|
@@ -3201,7 +3230,7 @@ export async function selftestCodexLb(tmp) {
|
|
|
3201
3230
|
if (!codexLbLegacyDoctorJson.repair?.codex_lb?.ok || !codexLbLegacyDoctorJson.repair.codex_lb.legacy_auth_migrated || !codexLbLegacyDoctorEnv.includes("CODEX_LB_API_KEY='sk-legacy-doctor'") || !codexLbLegacyDoctorAuth.includes('"auth_mode":"apikey"') || !codexLbLegacyDoctorAuth.includes('sk-legacy-doctor') || !hasTopLevelCodexLbSelected(codexLbLegacyDoctorConfig) || !codexLbLegacyDoctorConfig.includes('env_key = "CODEX_LB_API_KEY"'))
|
|
3202
3231
|
throw new Error('selftest: legacy doctor codex-lb restore');
|
|
3203
3232
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'sks-codex-lb.env'), "export CODEX_LB_BASE_URL='https://lb.example.test/backend-api/codex'\n");
|
|
3204
|
-
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'),
|
|
3233
|
+
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), 'service_tier = "fast"\n');
|
|
3205
3234
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'auth.json'), '{"auth_mode":"apikey","key":"sk-env-only"}\n');
|
|
3206
3235
|
const codexLbLoginCallsBeforeEnvOnlyPostinstall = await codexLbLoginCallCount(codexLbHome);
|
|
3207
3236
|
const codexLbEnvOnlyPostinstall = await runProcess(process.execPath, [packagedSksEntrypoint(), 'postinstall'], {
|
|
@@ -3219,7 +3248,7 @@ export async function selftestCodexLb(tmp) {
|
|
|
3219
3248
|
if (!String(codexLbEnvOnlyPostinstall.stdout || '').includes('codex-lb auth: restored from existing Codex login cache') || !codexLbEnvOnlyPostinstallEnv.includes("CODEX_LB_API_KEY='sk-env-only'") || !codexLbEnvOnlyPostinstallConfig.includes('env_key = "CODEX_LB_API_KEY"') || !hasTopLevelCodexLbSelected(codexLbEnvOnlyPostinstallConfig) || !codexLbEnvOnlyPostinstallAuth.includes('sk-env-only') || codexLbLoginCallsAfterEnvOnlyPostinstall !== codexLbLoginCallsBeforeEnvOnlyPostinstall)
|
|
3220
3249
|
throw new Error('selftest: env-only codex-lb postinstall restore');
|
|
3221
3250
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'sks-codex-lb.env'), "export CODEX_LB_BASE_URL='https://lb.example.test/backend-api/codex'\n");
|
|
3222
|
-
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'),
|
|
3251
|
+
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), 'service_tier = "fast"\n');
|
|
3223
3252
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'auth.json'), '{"auth_mode":"apikey","key":"sk-env-only-doctor"}\n');
|
|
3224
3253
|
const codexLbEnvOnlyDoctorProject = tmpdir();
|
|
3225
3254
|
await ensureDir(path.join(codexLbEnvOnlyDoctorProject, '.git'));
|
|
@@ -3349,7 +3378,7 @@ export async function selftestCodexLb(tmp) {
|
|
|
3349
3378
|
}
|
|
3350
3379
|
if (autobypassLaunch.status !== 'chain_unhealthy' || autobypassLaunch.bypass_codex_lb !== true || autobypassLaunch.chain_health?.status !== 'second_request_failed')
|
|
3351
3380
|
throw new Error('selftest: SKS_CODEX_LB_AUTOBYPASS=1 should bypass codex-lb on hard chain failure');
|
|
3352
|
-
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'),
|
|
3381
|
+
await writeTextAtomic(path.join(codexLbHome, '.codex', 'config.toml'), 'service_tier = "fast"\n');
|
|
3353
3382
|
await writeTextAtomic(path.join(codexLbHome, '.codex', 'sks-codex-lb.env'), "export CODEX_LB_BASE_URL='https://lb.example.test/backend-api/codex'\nexport CODEX_LB_API_KEY='sk-test'\n");
|
|
3354
3383
|
const missingProviderLaunchCalls = [];
|
|
3355
3384
|
const missingProviderLaunch = await maybePromptCodexLbSetupForLaunch([], {
|
|
@@ -3416,11 +3445,11 @@ export async function selftestCodexLb(tmp) {
|
|
|
3416
3445
|
throw new Error(`selftest: codex-lb setup did not enforce the renewed feature-flag contract (documented flags present, legacy stamps stripped, user profile tables preserved)${survivingLegacy.length ? ` — surviving legacy stamps: ${survivingLegacy.join(', ')}` : ''}`);
|
|
3417
3446
|
if (!hasCodexUnstableFeatureWarningSuppression(codexLbConfig))
|
|
3418
3447
|
throw new Error('selftest: codex-lb setup did not suppress Codex unstable feature warning');
|
|
3419
|
-
const codexLbLaunch = `source ${path.join(tmp, '.codex', 'sks-codex-lb.env')} && codex
|
|
3448
|
+
const codexLbLaunch = `source ${path.join(tmp, '.codex', 'sks-codex-lb.env')} && codex`;
|
|
3420
3449
|
if (!codexLbLaunch.includes('sks-codex-lb.env'))
|
|
3421
3450
|
throw new Error('selftest: Zellij launch command does not source codex-lb env file');
|
|
3422
|
-
if (
|
|
3423
|
-
throw new Error(
|
|
3451
|
+
if (codexLbLaunch.includes('--model'))
|
|
3452
|
+
throw new Error('selftest: Zellij launch command without an explicit model must inherit the Codex selection');
|
|
3424
3453
|
const madLaunchSource = await safeReadText(path.join(packageRoot(), 'src', 'core', 'commands', 'mad-sks-command.js'));
|
|
3425
3454
|
if (!madLaunchSource.includes('const lb = await deps.maybePromptCodexLbSetupForLaunch(args)') || !madLaunchSource.includes("const launchLb = lb.status === 'present'") || !madLaunchSource.includes('codexLbImmediateLaunchOpts(cleanArgs, launchLb') || !madLaunchSource.includes('bypass_codex_lb') || !madLaunchSource.includes('model_provider="openai"') || !madLaunchSource.includes('codexLbFreshSession: true'))
|
|
3426
3455
|
throw new Error('selftest: MAD launch does not sync codex-lb auth and fresh-session launch options');
|
|
@@ -12,8 +12,11 @@ import { resolveCodexAppExecutionProfile } from '../core/codex-app/codex-app-exe
|
|
|
12
12
|
import { repairCodexNativeManagedAssets } from '../core/codex-native/codex-native-repair-transaction.js';
|
|
13
13
|
import { doctorCodexAppGlmProfile, installCodexAppGlmProfile } from '../core/codex-app/glm-profile-installer.js';
|
|
14
14
|
import { promptForOpenRouterKeyHidden, writeStoredOpenRouterKey } from '../core/providers/openrouter/openrouter-secret-store.js';
|
|
15
|
+
import { restartCodexApp } from '../core/codex-app/codex-app-restart.js';
|
|
15
16
|
export async function run(_command, args = []) {
|
|
16
17
|
const action = args[0] || 'check';
|
|
18
|
+
if (action === 'restart')
|
|
19
|
+
return printCodexAppResult(args, await restartCodexApp());
|
|
17
20
|
if (action === 'remote-control' || action === 'remote')
|
|
18
21
|
return codexAppRemoteControlCommand(args.slice(1));
|
|
19
22
|
if (action === 'harness-matrix') {
|
|
@@ -47,15 +50,17 @@ export async function run(_command, args = []) {
|
|
|
47
50
|
}
|
|
48
51
|
const record = await writeStoredOpenRouterKey(key);
|
|
49
52
|
const profile = await installCodexAppGlmProfile({ root, apply: true });
|
|
53
|
+
const restart = await restartCodexApp({ enabled: flag(args, '--restart-app') || flag(args, '--restart') });
|
|
50
54
|
const result = {
|
|
51
55
|
schema: 'sks.codex-app-openrouter-key.v1',
|
|
52
|
-
ok: profile.ok,
|
|
53
|
-
status: profile.ok ? 'stored' : '
|
|
56
|
+
ok: Boolean(profile.ok && restart.ok),
|
|
57
|
+
status: !profile.ok ? 'stored_profile_blocked' : restart.ok ? 'stored' : 'stored_restart_blocked',
|
|
54
58
|
key_preview: record.key_preview,
|
|
55
59
|
raw_key_recorded: false,
|
|
56
60
|
secret_store: 'sks-openrouter-secret-store',
|
|
57
61
|
glm_profile: profile,
|
|
58
|
-
|
|
62
|
+
restart_app: restart,
|
|
63
|
+
blockers: [...(profile.blockers || []), ...(restart.blockers || [])],
|
|
59
64
|
warnings: profile.warnings || []
|
|
60
65
|
};
|
|
61
66
|
return printCodexAppResult(args, result);
|
|
@@ -126,8 +126,10 @@ export async function run(command, args = []) {
|
|
|
126
126
|
const host = status.base_url;
|
|
127
127
|
if (!host) {
|
|
128
128
|
const result = { schema: 'sks.codex-lb-set-key.v1', ok: false, status: 'not_configured' };
|
|
129
|
-
if (flag(args, '--json'))
|
|
129
|
+
if (flag(args, '--json')) {
|
|
130
|
+
process.exitCode = 1;
|
|
130
131
|
return printJson(result);
|
|
132
|
+
}
|
|
131
133
|
console.error('codex-lb is not configured yet. Run: sks codex-lb setup --host <domain> --api-key-stdin');
|
|
132
134
|
process.exitCode = 1;
|
|
133
135
|
return;
|
|
@@ -135,21 +137,26 @@ export async function run(command, args = []) {
|
|
|
135
137
|
const newKey = await resolveNewApiKey(args);
|
|
136
138
|
if (!newKey) {
|
|
137
139
|
const result = { schema: 'sks.codex-lb-set-key.v1', ok: false, status: 'missing_api_key' };
|
|
138
|
-
if (flag(args, '--json'))
|
|
140
|
+
if (flag(args, '--json')) {
|
|
141
|
+
process.exitCode = 1;
|
|
139
142
|
return printJson(result);
|
|
143
|
+
}
|
|
140
144
|
console.error('No new API key provided. Run: sks codex-lb set-key --api-key-stdin (or --api-key <key>)');
|
|
141
145
|
process.exitCode = 1;
|
|
142
146
|
return;
|
|
143
147
|
}
|
|
144
148
|
const result = await configureCodexLb({ host, apiKey: newKey, authMode: flag(args, '--preserve-auth') ? 'preserve' : 'codex-lb', forceCodexLbApiKeyAuth: !flag(args, '--preserve-auth') });
|
|
145
149
|
const restart = await maybeRestartCodexAppForAuthSwitch(args, Boolean(result.ok));
|
|
146
|
-
const
|
|
150
|
+
const ok = Boolean(result.ok && restart?.ok !== false);
|
|
151
|
+
const output = { ...result, ok, action: 'set-key', restart_app: restart };
|
|
152
|
+
if (!ok)
|
|
153
|
+
process.exitCode = 1;
|
|
147
154
|
if (flag(args, '--json'))
|
|
148
155
|
return printJson(output);
|
|
149
156
|
console.log(result.ok ? `codex-lb API key updated (${result.base_url || host}).` : `codex-lb key update failed: ${result.status}${result.error ? `: ${result.error}` : ''}`);
|
|
150
157
|
if (restart?.status === 'restarted')
|
|
151
158
|
console.log('Codex App restarted for the new codex-lb auth mode.');
|
|
152
|
-
if (!
|
|
159
|
+
if (!ok)
|
|
153
160
|
process.exitCode = 1;
|
|
154
161
|
return;
|
|
155
162
|
}
|
|
@@ -157,21 +164,30 @@ export async function run(command, args = []) {
|
|
|
157
164
|
// Switch auth mode -> codex-lb (API key). Re-selects the provider and re-syncs auth.
|
|
158
165
|
const result = await repairCodexLbAuth({ forceCodexLbApiKeyAuth: true, authMode: 'codex-lb', forceFastMode: !flag(args, '--no-fast') });
|
|
159
166
|
const restart = await maybeRestartCodexAppForAuthSwitch(args, Boolean(result.ok));
|
|
167
|
+
const ok = Boolean(result.ok && restart?.ok !== false);
|
|
168
|
+
const shaped = { ...result, ok, mode: 'codex-lb', restart_app: restart };
|
|
169
|
+
if (!ok)
|
|
170
|
+
process.exitCode = 1;
|
|
160
171
|
if (flag(args, '--json'))
|
|
161
|
-
return printJson(
|
|
172
|
+
return printJson(shaped);
|
|
162
173
|
console.log(result.ok ? 'Auth mode: codex-lb selected (API key).' : `Switch to codex-lb failed: ${result.status}${result.error ? `: ${result.error}` : ''}`);
|
|
163
174
|
if (restart?.status === 'restarted')
|
|
164
175
|
console.log('Codex App restarted for codex-lb auth.');
|
|
165
|
-
if (!
|
|
176
|
+
if (!ok)
|
|
166
177
|
process.exitCode = 1;
|
|
167
178
|
return;
|
|
168
179
|
}
|
|
169
180
|
if (action === 'use-oauth' || action === 'use-chatgpt') {
|
|
170
181
|
// Switch auth mode -> ChatGPT OAuth. Restores the saved OAuth login if present.
|
|
171
182
|
const result = await releaseCodexLbAuthHold({ force: flag(args, '--force') });
|
|
172
|
-
const
|
|
183
|
+
const authOk = !['no_backup', 'auth_in_use', 'failed'].includes(result.status);
|
|
184
|
+
const restart = await maybeRestartCodexAppForAuthSwitch(args, authOk);
|
|
185
|
+
const ok = Boolean(authOk && restart?.ok !== false);
|
|
186
|
+
const shaped = { ...result, ok, mode: 'oauth', restart_app: restart };
|
|
187
|
+
if (!ok)
|
|
188
|
+
process.exitCode = 1;
|
|
173
189
|
if (flag(args, '--json'))
|
|
174
|
-
return printJson(
|
|
190
|
+
return printJson(shaped);
|
|
175
191
|
if (result.status === 'no_backup') {
|
|
176
192
|
console.log('No saved ChatGPT OAuth credentials to restore. Switch to OAuth by logging in:');
|
|
177
193
|
console.log(' codex login');
|
|
@@ -210,8 +226,10 @@ export async function run(command, args = []) {
|
|
|
210
226
|
'Or: sks codex-lb setup --host <domain> --api-key-stdin --yes'
|
|
211
227
|
]
|
|
212
228
|
};
|
|
213
|
-
if (flag(args, '--json'))
|
|
229
|
+
if (flag(args, '--json')) {
|
|
230
|
+
process.exitCode = 1;
|
|
214
231
|
return printJson(result);
|
|
232
|
+
}
|
|
215
233
|
console.error('codex-lb API key is not configured.');
|
|
216
234
|
console.error('Run:');
|
|
217
235
|
console.error(' sks codex-lb setup');
|
|
@@ -222,6 +240,8 @@ export async function run(command, args = []) {
|
|
|
222
240
|
}
|
|
223
241
|
if (flag(args, '--plan')) {
|
|
224
242
|
const result = { schema: 'sks.codex-lb-setup-plan-result.v1', ok: plan.blockers.length === 0, plan, writes: false, expected_actions: plan.expected_actions, persistence: plan.persistence };
|
|
243
|
+
if (!result.ok)
|
|
244
|
+
process.exitCode = 1;
|
|
225
245
|
if (flag(args, '--json'))
|
|
226
246
|
return printJson(result);
|
|
227
247
|
process.stdout.write(renderCodexLbSetupPlan(plan));
|
|
@@ -235,8 +255,10 @@ export async function run(command, args = []) {
|
|
|
235
255
|
const confirm = (await ask('Apply this codex-lb setup plan? [y/N] ')).trim();
|
|
236
256
|
if (!/^(y|yes|예|네|응)$/i.test(confirm)) {
|
|
237
257
|
const result = { schema: 'sks.codex-lb-setup.v1', ok: false, status: 'cancelled', plan, applied_actions: [] };
|
|
238
|
-
if (flag(args, '--json'))
|
|
258
|
+
if (flag(args, '--json')) {
|
|
259
|
+
process.exitCode = 1;
|
|
239
260
|
return printJson(result);
|
|
261
|
+
}
|
|
240
262
|
console.log('codex-lb setup cancelled.');
|
|
241
263
|
process.exitCode = 1;
|
|
242
264
|
return;
|
|
@@ -298,12 +320,15 @@ export async function run(command, args = []) {
|
|
|
298
320
|
shaped.applied_actions = [...(shaped.applied_actions || []), { type: 'run_health_check', target: 'codex-lb response chain', ok: true }];
|
|
299
321
|
if (options.health)
|
|
300
322
|
shaped.chain_health = result.ok ? await checkCodexLbResponseChain(result, { force: true, root }) : null;
|
|
323
|
+
shaped.ok = Boolean(result.ok && restart?.ok !== false);
|
|
324
|
+
if (!shaped.ok)
|
|
325
|
+
process.exitCode = 1;
|
|
301
326
|
if (flag(args, '--json'))
|
|
302
327
|
return printJson(shaped);
|
|
303
328
|
console.log(`codex-lb configured: ${result.base_url || result.status}`);
|
|
304
329
|
if (shaped.persistence?.warning)
|
|
305
330
|
console.log(`warning: ${shaped.persistence.warning}`);
|
|
306
|
-
if (!
|
|
331
|
+
if (!shaped.ok)
|
|
307
332
|
process.exitCode = 1;
|
|
308
333
|
return;
|
|
309
334
|
}
|
|
@@ -345,7 +370,7 @@ export async function run(command, args = []) {
|
|
|
345
370
|
}
|
|
346
371
|
async function maybeRestartCodexAppForAuthSwitch(args = [], enabled) {
|
|
347
372
|
if (!enabled)
|
|
348
|
-
return { schema: 'sks.codex-app-restart.v1', ok: true, status: 'skipped', skipped: true, reason: 'previous_step_failed', app_name: '
|
|
373
|
+
return { schema: 'sks.codex-app-restart.v1', ok: true, status: 'skipped', skipped: true, reason: 'previous_step_failed', app_name: 'ChatGPT', bundle_id: 'com.openai.codex', blockers: [] };
|
|
349
374
|
const requested = flag(args, '--restart-app') || flag(args, '--restart');
|
|
350
375
|
const shouldRestart = requested || (!flag(args, '--json') && !flag(args, '--no-restart-app') && !flag(args, '--no-restart'));
|
|
351
376
|
return restartCodexApp({ enabled: shouldRestart });
|