sneakoscope 8.1.2 → 8.2.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 +96 -58
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/bin/fast-inline.js +5 -51
- package/dist/cli/command-manifest-lite.js +2 -1
- package/dist/cli/command-registry.js +2 -1
- package/dist/cli/global-mode-router.js +1 -1
- package/dist/cli/install-helpers-codex-lb-config.js +112 -914
- package/dist/cli/install-helpers-codex-lb-shared.js +3 -44
- package/dist/cli/install-helpers-install-support.js +4 -20
- package/dist/cli/install-helpers.js +36 -2716
- package/dist/cli/router.js +9 -9
- package/dist/commands/bridge-command-output.js +111 -0
- package/dist/commands/bridge.js +320 -0
- package/dist/commands/codex-app.js +39 -123
- package/dist/commands/doctor-helpers.js +380 -0
- package/dist/commands/doctor-profile.js +3 -0
- package/dist/commands/doctor.js +114 -873
- package/dist/commands/mad-sks.js +0 -4
- package/dist/config/skills-manifest.json +1 -1
- package/dist/core/agents/agent-effort-policy.js +11 -21
- package/dist/core/agents/agent-recursion-guard.js +26 -0
- package/dist/core/agents/agent-runner-codex-exec.js +7 -9
- package/dist/core/architecture-hardening/contracts/contracts.js +225 -161
- package/dist/core/architecture-hardening/state/state-service.js +49 -13
- package/dist/core/auto-review.js +15 -9
- package/dist/core/codex/codex-config-guard.js +8 -0
- package/dist/core/codex-adapter.js +8 -10
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +7 -29
- package/dist/core/codex-app/codex-app-launcher.js +10 -12
- package/dist/core/codex-app/codex-app-restart.js +10 -12
- package/dist/core/codex-app/menubar/constants.js +2 -5
- package/dist/core/codex-app/menubar/global-install.js +3 -2
- package/dist/core/codex-app/menubar/installer/result.js +23 -0
- package/dist/core/codex-app/menubar/installer/runtime.js +28 -0
- package/dist/core/codex-app/menubar/installer.js +5 -96
- package/dist/core/codex-app/menubar/migration.js +1 -42
- package/dist/core/codex-app/openrouter-provider.js +0 -78
- package/dist/core/codex-app.js +97 -360
- package/dist/core/codex-control/codex-app-server-runtime-env.js +8 -28
- package/dist/core/codex-control/codex-current-image-path-real-probe.js +2 -3
- package/dist/core/codex-control/codex-current-web-search-probe.js +2 -3
- package/dist/core/codex-control/codex-sdk-adapter.js +5 -48
- package/dist/core/codex-control/codex-task-runner.js +16 -60
- package/dist/core/codex-control/desktop-bridge-launch-guard.js +227 -0
- package/dist/core/codex-exec-output-schema.js +11 -13
- package/dist/core/codex-lb/bridge-contracts.js +2 -0
- package/dist/core/codex-lb/bridge-runtime-validation/capability.js +104 -0
- package/dist/core/codex-lb/bridge-runtime-validation/catalog.js +52 -0
- package/dist/core/codex-lb/bridge-runtime-validation/shared.js +109 -0
- package/dist/core/codex-lb/bridge-runtime-validation/status.js +250 -0
- package/dist/core/codex-lb/bridge-runtime-validation.js +106 -0
- package/dist/core/codex-lb/capability-runner.js +505 -201
- package/dist/core/codex-lb/codex-lb-env.js +179 -12
- package/dist/core/codex-lb/combined-catalog/builder.js +86 -0
- package/dist/core/codex-lb/combined-catalog/contracts.js +5 -0
- package/dist/core/codex-lb/combined-catalog/normalize.js +170 -0
- package/dist/core/codex-lb/combined-catalog/persistence.js +207 -0
- package/dist/core/codex-lb/combined-catalog/shared.js +12 -0
- package/dist/core/codex-lb/combined-catalog/storage.js +171 -0
- package/dist/core/codex-lb/combined-catalog.js +12 -0
- package/dist/core/codex-lb/desktop-bridge/client.js +4 -1
- package/dist/core/codex-lb/desktop-bridge/header-policy.js +48 -63
- package/dist/core/codex-lb/desktop-bridge/http-forward.js +115 -83
- package/dist/core/codex-lb/desktop-bridge/launchd.js +20 -13
- package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +4 -1
- package/dist/core/codex-lb/desktop-bridge/security.js +381 -267
- package/dist/core/codex-lb/desktop-bridge/server.js +148 -19
- package/dist/core/codex-lb/desktop-bridge/state.js +56 -70
- package/dist/core/codex-lb/desktop-bridge/types.js +6 -1
- package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +281 -106
- package/dist/core/codex-lb/desktop-bridge-migration/historical-config.js +245 -0
- package/dist/core/codex-lb/desktop-bridge-migration/historical-toml.js +56 -0
- package/dist/core/codex-lb/desktop-bridge-migration/metadata.js +73 -0
- package/dist/core/codex-lb/desktop-bridge-migration/receipt.js +52 -0
- package/dist/core/codex-lb/desktop-bridge-migration/retired-runtime-cleanup.js +106 -0
- package/dist/core/codex-lb/desktop-bridge-migration/types.js +1 -0
- package/dist/core/codex-lb/desktop-bridge-migration.js +238 -0
- package/dist/core/codex-lb/desktop-controller-v3/catalog.js +257 -0
- package/dist/core/codex-lb/desktop-controller-v3/diagnostics.js +62 -0
- package/dist/core/codex-lb/desktop-controller-v3/lifecycle-commands.js +203 -0
- package/dist/core/codex-lb/desktop-controller-v3/live-probes.js +293 -0
- package/dist/core/codex-lb/desktop-controller-v3/preflight-probes.js +144 -0
- package/dist/core/codex-lb/desktop-controller-v3/provider-commands.js +182 -0
- package/dist/core/codex-lb/desktop-controller-v3/shared.js +279 -0
- package/dist/core/codex-lb/desktop-controller-v3/status.js +269 -0
- package/dist/core/codex-lb/desktop-controller-v3/types.js +1 -0
- package/dist/core/codex-lb/desktop-controller-v3/verification.js +89 -0
- package/dist/core/codex-lb/desktop-controller-v3.js +67 -0
- package/dist/core/codex-lb/desktop-controller.js +2 -1730
- package/dist/core/codex-lb/desktop-service.js +462 -627
- package/dist/core/codex-lb/migration-receipt.js +560 -118
- package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +37 -53
- package/dist/core/codex-lb/probes/bridge-probe.js +125 -64
- package/dist/core/codex-lb/probes/catalog-probe.js +36 -136
- package/dist/core/codex-lb/probes/computer-use-probe.js +63 -39
- package/dist/core/codex-lb/probes/image-generation-probe.js +98 -41
- package/dist/core/codex-lb/probes/probe-evidence.js +32 -33
- package/dist/core/codex-lb/probes/voice-realtime-probe.js +69 -43
- package/dist/core/codex-lb/provider-credentials/configuration.js +127 -0
- package/dist/core/codex-lb/provider-credentials/locks.js +11 -0
- package/dist/core/codex-lb/provider-credentials/private-snapshot.js +28 -0
- package/dist/core/codex-lb/provider-credentials/removal.js +33 -0
- package/dist/core/codex-lb/provider-credentials/resolution.js +59 -0
- package/dist/core/codex-lb/provider-credentials/runtime.js +55 -0
- package/dist/core/codex-lb/provider-credentials/types.js +1 -0
- package/dist/core/codex-lb/provider-credentials/validation-store.js +137 -0
- package/dist/core/codex-lb/provider-credentials.js +5 -0
- package/dist/core/codex-lb/provider-registry.js +323 -0
- package/dist/core/codex-lb/provider-route-policy.js +118 -0
- package/dist/core/codex-lb/request-route-resolver.js +172 -0
- package/dist/core/codex-lb/route-index.js +148 -0
- package/dist/core/codex-lb/trusted-deep-evidence.js +158 -286
- package/dist/core/commands/basic-cli.js +1 -1
- package/dist/core/commands/check-command.js +3 -10
- package/dist/core/commands/gate-result-contract.js +10 -14
- package/dist/core/commands/intent-normalization/normalizer.js +1 -1
- package/dist/core/commands/mad-sks-command.js +2 -6
- package/dist/core/commands/release-command.js +11 -1
- package/dist/core/commands/telegram-command.js +46 -6
- package/dist/core/doctor/doctor-readiness-matrix.js +9 -7
- package/dist/core/doctor/skill-legacy-surface.js +5 -5
- package/dist/core/evidence/v2/evidence-key.js +30 -3
- package/dist/core/feature-fixtures.js +8 -1
- package/dist/core/feature-registry.js +3 -2
- package/dist/core/hooks-runtime/stop-finalization.js +11 -1
- package/dist/core/hooks-runtime/subagent-context.js +15 -1
- package/dist/core/hooks-runtime/subagent-skill-availability-contract.js +1 -1
- package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
- package/dist/core/hooks-runtime.js +73 -87
- package/dist/core/image-ux-review/imagegen-adapter.js +108 -94
- package/dist/core/image-ux-review/real-callout-extractor.js +144 -14
- package/dist/core/imagegen/desktop-bridge-imagegen-target.js +77 -0
- package/dist/core/imagegen/imagegen-capability.js +43 -151
- package/dist/core/init/skills/filesystem.js +89 -0
- package/dist/core/init/skills/inventory.js +97 -0
- package/dist/core/init/skills/manifest.js +151 -0
- package/dist/core/init/skills.js +5 -325
- package/dist/core/init.js +0 -63
- package/dist/core/install/installed-package-smoke.js +1 -1
- package/dist/core/managed-assets/managed-assets-manifest.js +0 -1
- package/dist/core/policy/latest-version-guidance.js +15 -2
- package/dist/core/preflight/parallel-preflight-engine.js +27 -51
- package/dist/core/provider/provider-badge.js +9 -4
- package/dist/core/provider/provider-context.js +57 -107
- package/dist/core/providers/openrouter/openrouter-account.js +3 -1
- package/dist/core/providers/openrouter/openrouter-secret-store.js +91 -22
- package/dist/core/release/desktop-bridge-release-evidence.js +457 -0
- package/dist/core/release/gate-manifest.js +0 -1
- package/dist/core/release/main-push-guard.js +13 -0
- package/dist/core/release/main-push-receipt.js +11 -0
- package/dist/core/release/physical-release-gates.js +664 -61
- package/dist/core/release/publish-preflight.js +3 -3
- package/dist/core/release/release-gate-batch-runner.js +19 -6
- package/dist/core/release/release-gate-dag.js +33 -6
- package/dist/core/release/release-gate-node.js +1 -3
- package/dist/core/release/release-gate-output-contract.js +55 -0
- package/dist/core/release/release-pack-contract.js +1 -1
- package/dist/core/release/release-pack-receipt.js +209 -8
- package/dist/core/release/release-real-contract.js +1 -1
- package/dist/core/release/stage-publish.js +106 -28
- package/dist/core/retention/temp-lease.js +204 -0
- package/dist/core/retention.js +18 -195
- package/dist/core/routes.js +1 -2
- package/dist/core/safety/intent-contract/intent-contract.js +3 -3
- package/dist/core/subagents/naruto-host-credentials.js +6 -1
- package/dist/core/subagents/official-subagent-config.js +27 -9
- package/dist/core/subagents/official-subagent-runner.js +14 -31
- package/dist/core/subagents/wave-lifecycle.js +36 -8
- package/dist/core/subagents/wave-parent-guidance.js +2 -1
- package/dist/core/telegram/doctor.js +2 -2
- package/dist/core/telegram/liveness.js +10 -4
- package/dist/core/triwiki/context-graph/compiler/index.js +18 -18
- package/dist/core/update/update-migration-state/fast-mode-config.js +172 -0
- package/dist/core/update/update-migration-state/session-state-split.js +247 -0
- package/dist/core/update/update-migration-state/simple-stages.js +46 -0
- package/dist/core/update/update-migration-state.js +4 -481
- package/dist/core/version.js +1 -1
- package/dist/native/sks-menubar/Sources/OperationCoordinator.swift +28 -4
- package/dist/native/sks-menubar/Sources/OperationModels.swift +112 -2
- package/dist/native/sks-menubar/Sources/ProviderRouteExplanation.swift +21 -0
- package/dist/native/sks-menubar/Sources/ProvidersBridgeCatalog.swift +140 -0
- package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +107 -470
- package/dist/native/sks-menubar/Sources/ProvidersReliability.swift +27 -197
- package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +335 -269
- package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +233 -437
- package/dist/native/sks-menubar/Sources/RemoteCodingViewController.swift +16 -7
- package/dist/native/sks-menubar/Sources/SecureProcessEnvelope.swift +39 -9
- package/dist/native/sks-menubar/Sources/TelegramSupport.swift +56 -0
- package/dist/scripts/canonical-test-runner.js +7 -1
- package/dist/scripts/check-runtime-schemas.js +7 -2
- package/dist/scripts/codex-sdk-real-smoke-check.js +3 -1
- package/dist/scripts/desktop-bridge-capabilities-check.js +123 -0
- package/dist/scripts/{codex-lb-desktop-bridge-latency-check.js → desktop-bridge-latency-check.js} +61 -16
- package/dist/scripts/desktop-bridge-real-evidence-check.js +64 -0
- package/dist/scripts/{codex-lb-desktop-bridge-check.js → desktop-bridge-transport-check.js} +92 -155
- package/dist/scripts/desktop-bridge-transport-client.js +116 -0
- package/dist/scripts/desktop-bridge-unification-check.js +276 -0
- package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +12 -8
- package/dist/scripts/gate-policy-audit-check.js +52 -2
- package/dist/scripts/provider-badge-context-check.js +55 -12
- package/dist/scripts/publish-preflight.js +1 -2
- package/dist/scripts/release-affected-selector-check.js +1 -2
- package/dist/scripts/release-aggressive-resource-governor-check.js +1 -1
- package/dist/scripts/release-cache-glob-hashing-check.js +1 -0
- package/dist/scripts/release-cache-version-neutral-fixture-check.js +1 -0
- package/dist/scripts/release-full-parallelism-blackbox.js +1 -1
- package/dist/scripts/release-gate-batch-runner-check.js +9 -0
- package/dist/scripts/release-gate-dag-runner-check.js +6 -0
- package/dist/scripts/release-gate-dag-runner.js +4 -2
- package/dist/scripts/release-metadata-check.js +16 -3
- package/dist/scripts/release-pack-receipt.js +29 -6
- package/dist/scripts/release-physical-gates-check.js +39 -0
- package/dist/scripts/release-real-check.js +7 -5
- package/package.json +6 -8
- package/release-gates.v2.json +156 -157
- package/runtime-required-scripts.json +11 -11
- package/schemas/codex/desktop-bridge-status-v3.schema.json +286 -0
- package/schemas/codex/desktop-capabilities-v3.schema.json +198 -0
- package/schemas/release/release-gate-node.schema.json +2 -3
- package/dist/cli/codex-lb-setup-warning-output.js +0 -17
- package/dist/cli/install-helpers-codex-lb-chain.js +0 -529
- package/dist/commands/codex-lb.js +0 -1232
- package/dist/core/architecture-hardening/migration/migration.js +0 -111
- package/dist/core/codex-app/auxiliary-oauth-policy.js +0 -36
- package/dist/core/codex-app/catalog-compat/catalog-service.js +0 -104
- package/dist/core/codex-app/child-policy/child-policy.js +0 -35
- package/dist/core/codex-app/desktop-routing-snapshot.js +0 -233
- package/dist/core/codex-app/glm-model-profile.js +0 -1
- package/dist/core/codex-app/glm-profile-installer.js +0 -191
- package/dist/core/codex-app/glm-profile-schema.js +0 -42
- package/dist/core/codex-app/multi-provider-router.js +0 -391
- package/dist/core/codex-app/openrouter-activate.js +0 -405
- package/dist/core/codex-app/openrouter-model-catalog.js +0 -217
- package/dist/core/codex-app/provider-child-policy.js +0 -64
- package/dist/core/codex-app/provider-mode.js +0 -91
- package/dist/core/codex-app/provider-session-policy.js +0 -85
- package/dist/core/codex-app/session-policy/session-pinning.js +0 -68
- package/dist/core/codex-control/codex-lb-launch-recovery.js +0 -319
- package/dist/core/codex-lb/codex-lb-setup.js +0 -293
- package/dist/core/codex-lb/codex-lb-tool-catalog.js +0 -456
- package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +0 -290
- package/dist/core/codex-lb/desktop-center-credentials.js +0 -1083
- package/dist/core/codex-lb/desktop-mode.js +0 -32
- package/dist/core/codex-lb/desktop-real-evidence.js +0 -168
- package/dist/core/codex-lb/legacy-migration.js +0 -576
- package/dist/core/codex-lb/native-openai-transport/transport.js +0 -68
- package/dist/core/codex-lb/provider-routing/provider-router.js +0 -58
- package/dist/core/codex-lb/routing-truth.js +0 -327
- package/dist/core/imagegen/codex-lb-imagegen-target.js +0 -92
- package/dist/native/sks-menubar/Sources/ProvidersConnectTest.swift +0 -118
- package/dist/native/sks-menubar/Sources/ProvidersFastMode.swift +0 -17
- package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +0 -331
- package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +0 -260
- package/dist/scripts/codex-lb-catalog-passthrough-check.js +0 -120
- package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +0 -169
- package/dist/scripts/codex-lb-desktop-capabilities-check.js +0 -287
- package/dist/scripts/codex-lb-desktop-real-evidence-check.js +0 -123
- package/dist/scripts/codex-lb-fast-mode-truth-check.js +0 -114
- package/dist/scripts/codex-lb-fast-ui-preservation-check.js +0 -152
- package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +0 -231
|
@@ -1,499 +1,136 @@
|
|
|
1
1
|
import Cocoa
|
|
2
2
|
|
|
3
3
|
extension ProvidersViewController {
|
|
4
|
-
func
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
// Measured CLI selection is not legacy migration (that badge comes only from refresh snapshot markers).
|
|
31
|
-
if codexLbProvedNow {
|
|
32
|
-
ControlKit.setBadge(activeProviderBadge, text: "Codex LB · CLI · verified", tone: .ok)
|
|
33
|
-
} else {
|
|
34
|
-
ControlKit.setBadge(activeProviderBadge, text: "Codex LB · CLI · selected · connection unproved", tone: .warning)
|
|
35
|
-
}
|
|
36
|
-
} else if chatgptOauthPresentNow {
|
|
37
|
-
ControlKit.setBadge(activeProviderBadge, text: "ChatGPT OAuth mode · built-in OpenAI models", tone: .neutral)
|
|
38
|
-
} else {
|
|
39
|
-
ControlKit.setBadge(activeProviderBadge, text: "ChatGPT OAuth mode unavailable · run codex login", tone: .warning)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
func makeOpenRouterCard() -> NSBox {
|
|
44
|
-
openRouterModelField.delegate = self
|
|
45
|
-
openRouterModelPopup.addItem(withTitle: "Choose from catalog…")
|
|
46
|
-
openRouterModelPopup.target = self
|
|
47
|
-
openRouterModelPopup.action = #selector(selectOpenRouterModel(_:))
|
|
48
|
-
openRouterModelPopup.setAccessibilityLabel("OpenRouter model catalog")
|
|
49
|
-
openRouterModelPopup.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
50
|
-
openRouterModelPopup.widthAnchor.constraint(equalToConstant: 230).isActive = true
|
|
51
|
-
openRouterModelPopup.isEnabled = false
|
|
52
|
-
openRouterModelField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
53
|
-
|
|
54
|
-
let refreshModels = NativeView.button("Refresh Models", target: self, action: #selector(refreshOpenRouterModelsAction(_:)))
|
|
55
|
-
openRouterRefreshButton = refreshModels
|
|
56
|
-
let saveKey = NativeView.button("Save OpenRouter key…", target: self, action: #selector(saveOpenRouterKey))
|
|
57
|
-
let test = NativeView.button("Test Model", target: self, action: #selector(testOpenRouterConnection))
|
|
58
|
-
test.setAccessibilityLabel("Test the selected OpenRouter model")
|
|
59
|
-
let activate = ControlKit.primaryButton("Activate Selected Model", target: self, action: #selector(useOpenRouter))
|
|
60
|
-
activate.setAccessibilityLabel("Activate selected OpenRouter model and restart Codex App")
|
|
61
|
-
let restorePrevious = NativeView.button("Restore previous provider", target: self, action: #selector(restorePreviousDesktopRouting))
|
|
62
|
-
restorePrevious.setAccessibilityLabel("Restore the previous Codex provider so hidden chats and picker state return")
|
|
63
|
-
// Stays disabled until openrouter-status reports a restorable
|
|
64
|
-
// cross-provider snapshot, so the button never promises a restore the
|
|
65
|
-
// CLI would reject with snapshot_missing.
|
|
66
|
-
restorePrevious.isEnabled = false
|
|
67
|
-
openRouterRestoreButton = restorePrevious
|
|
68
|
-
registerProviderAction(refreshModels, id: "sks-provider-refresh-openrouter-catalog")
|
|
69
|
-
registerProviderAction(saveKey, id: "sks-provider-save-openrouter-key")
|
|
70
|
-
registerProviderAction(test, id: "sks-provider-test-openrouter")
|
|
71
|
-
registerProviderAction(activate, id: "sks-provider-activate-openrouter")
|
|
72
|
-
registerProviderAction(restorePrevious, id: "sks-provider-restore-previous")
|
|
73
|
-
actionButtons += [refreshModels, saveKey, test, activate, restorePrevious]
|
|
74
|
-
|
|
75
|
-
let catalogLabel = NSTextField(labelWithString: "Catalog")
|
|
76
|
-
catalogLabel.setContentHuggingPriority(.required, for: .horizontal)
|
|
77
|
-
let manualLabel = NSTextField(labelWithString: "Model ID")
|
|
78
|
-
manualLabel.setContentHuggingPriority(.required, for: .horizontal)
|
|
79
|
-
return NativeView.card(
|
|
80
|
-
title: "OpenRouter",
|
|
81
|
-
subtitle: "Saving the key prepares OpenRouter but does not switch providers. Activate switches Desktop routing; SKS remaps the local thread sidebar so prior provider chats stay visible, and keeps a one-click restore snapshot.",
|
|
4
|
+
func describeOpenRouterStatus(_ json: [String: Any]) -> String {
|
|
5
|
+
let enabled = json["enabled"] as? Bool == true
|
|
6
|
+
return "OpenRouter profile · \(enabled ? "enabled" : "disabled")"
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
func makeProviderCredentialsCard() -> NSBox {
|
|
10
|
+
let lbConfigure = NativeView.button(ProviderReconnectLabel.codexLb, target: self, action: #selector(configureCodexLbProfile))
|
|
11
|
+
let lbValidate = NativeView.button("Validate", target: self, action: #selector(validateCodexLbProfile))
|
|
12
|
+
let lbToggle = NativeView.button("Enable / Disable", target: self, action: #selector(toggleCodexLbProfile))
|
|
13
|
+
let orConfigure = NativeView.button(ProviderReconnectLabel.openRouter, target: self, action: #selector(configureOpenRouterProfile))
|
|
14
|
+
let orValidate = NativeView.button("Validate", target: self, action: #selector(validateOpenRouterProfile))
|
|
15
|
+
let orToggle = NativeView.button("Enable / Disable", target: self, action: #selector(toggleOpenRouterProfile))
|
|
16
|
+
let codexSignIn = NativeView.button(ProviderReconnectLabel.codexSignIn, target: self, action: #selector(openCodexSignInAction))
|
|
17
|
+
registerProviderAction(lbConfigure, id: "sks-provider-reconnect-codex-lb")
|
|
18
|
+
registerProviderAction(lbValidate, id: "sks-provider-validate-codex-lb")
|
|
19
|
+
registerProviderAction(lbToggle, id: "sks-provider-toggle-codex-lb")
|
|
20
|
+
registerProviderAction(orConfigure, id: "sks-provider-reconnect-openrouter")
|
|
21
|
+
registerProviderAction(orValidate, id: "sks-provider-validate-openrouter")
|
|
22
|
+
registerProviderAction(orToggle, id: "sks-provider-toggle-openrouter")
|
|
23
|
+
registerProviderAction(codexSignIn, id: "sks-provider-open-codex-sign-in")
|
|
24
|
+
providerButtons["codex-lb"] = [lbConfigure, lbValidate, lbToggle]
|
|
25
|
+
providerButtons["openrouter"] = [orConfigure, orValidate, orToggle]
|
|
26
|
+
actionButtons += [lbConfigure, lbValidate, lbToggle, orConfigure, orValidate, orToggle, codexSignIn]
|
|
27
|
+
let card = NativeView.card(
|
|
28
|
+
title: "Provider Credentials",
|
|
29
|
+
subtitle: "Profiles coexist. Enable, disable, validate, rotate, or remove one credential without changing the other or Codex-owned ChatGPT OAuth.",
|
|
82
30
|
views: [
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
NativeView.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
NativeView.row([restorePrevious]),
|
|
90
|
-
openRouterStatus
|
|
31
|
+
NativeView.sectionTitle("Codex-LB"), codexLbKeychainStatus, cliProviderStatus,
|
|
32
|
+
ControlKit.actionRow([lbConfigure, lbValidate, lbToggle]),
|
|
33
|
+
NativeView.sectionTitle("OpenRouter"), openRouterKeychainStatus, openRouterCredentialStatus,
|
|
34
|
+
ControlKit.actionRow([orConfigure, orValidate, orToggle]),
|
|
35
|
+
oauthCredentialStatus,
|
|
36
|
+
ControlKit.actionRow([codexSignIn])
|
|
91
37
|
]
|
|
92
38
|
)
|
|
39
|
+
card.setAccessibilityIdentifier("sks-provider-card-credentials")
|
|
40
|
+
return card
|
|
93
41
|
}
|
|
94
42
|
|
|
95
|
-
func
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
self.openRouterActiveStatus.stringValue = "Active provider: status unavailable — no active provider was assumed."
|
|
102
|
-
}
|
|
103
|
-
// An unreadable status cannot vouch for a restore point, so the
|
|
104
|
-
// button must not keep an enabled state from an earlier reply.
|
|
105
|
-
self.openRouterRestoreAvailable = false
|
|
106
|
-
self.openRouterRestoreButton?.isEnabled = false
|
|
107
|
-
self.openRouterRestoreButton?.toolTip = "Restore availability is unknown — refresh the OpenRouter status first."
|
|
108
|
-
return
|
|
109
|
-
}
|
|
110
|
-
let keyPresent = json["key_present"] as? Bool == true
|
|
111
|
-
let keyValidated = json["key_validated"] as? Bool == true
|
|
112
|
-
|| json["credential_validated"] as? Bool == true
|
|
113
|
-
let providerPresent = json["provider_present"] as? Bool == true
|
|
114
|
-
let selected = json["selected"] as? Bool == true
|
|
115
|
-
let activeModel = json["model"] as? String ?? "unset"
|
|
116
|
-
let current = self.openRouterModelField.stringValue.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
117
|
-
if selected, activeModel != "unset", !self.openRouterModelSelectionPending, current.isEmpty {
|
|
118
|
-
self.openRouterModelField.stringValue = activeModel
|
|
119
|
-
}
|
|
120
|
-
self.synchronizeOpenRouterPopupSelection()
|
|
121
|
-
self.openRouterSelectedNow = selected
|
|
122
|
-
self.openRouterCredentialValidatedNow = keyValidated
|
|
123
|
-
self.openRouterActiveModel = activeModel
|
|
124
|
-
self.renderActiveProviderSummary()
|
|
125
|
-
self.openRouterCredentialStatus.stringValue = keyValidated
|
|
126
|
-
? "Credential: validated\(providerPresent ? " · provider route ready" : " · provider setup needs repair")"
|
|
127
|
-
: keyPresent
|
|
128
|
-
? "Credential: stored but not validated · models remain hidden until authenticated refresh succeeds"
|
|
129
|
-
: "Credential: missing · save a key before testing or activation"
|
|
130
|
-
self.openRouterActiveStatus.stringValue = selected
|
|
131
|
-
? "Active provider: OpenRouter · main model \(activeModel)"
|
|
132
|
-
: "Active provider: not OpenRouter · saved credentials remain available"
|
|
133
|
-
if !self.openRouterActionRan {
|
|
134
|
-
let summary = self.describeOpenRouterStatus(json)
|
|
135
|
-
self.openRouterStatus.stringValue = result.code == 0
|
|
136
|
-
? summary
|
|
137
|
-
: "\(summary) \(self.structuredPublicDetail(json, fallback: result.output, codePrefix: "E-OR"))"
|
|
138
|
-
}
|
|
139
|
-
self.openRouterCredentialStatus.textColor = keyValidated ? .secondaryLabelColor : .systemOrange
|
|
140
|
-
if !keyValidated { self.clearOpenRouterModels(reason: "Credential not validated · model and child-agent lists are hidden. Choose Reconnect, then Refresh Models.") }
|
|
141
|
-
self.openRouterActiveStatus.textColor = selected ? .systemGreen : .secondaryLabelColor
|
|
142
|
-
self.openRouterRestoreAvailable = json["previous_routing_restore_available"] as? Bool == true
|
|
143
|
-
self.openRouterRestoreButton?.isEnabled = !self.busy && self.openRouterRestoreAvailable
|
|
144
|
-
self.openRouterRestoreButton?.toolTip = self.openRouterRestoreAvailable
|
|
145
|
-
? "Restore the snapshotted pre-OpenRouter provider, model, and catalog."
|
|
146
|
-
: "No previous provider snapshot to restore."
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
func describeOpenRouterStatus(_ json: [String: Any]) -> String {
|
|
151
|
-
let keyPresent = json["key_present"] as? Bool == true
|
|
152
|
-
let providerPresent = json["provider_present"] as? Bool == true
|
|
153
|
-
let selected = json["selected"] as? Bool == true
|
|
154
|
-
let activeModel = json["model"] as? String ?? "unset"
|
|
155
|
-
if !keyPresent { return "OpenRouter: key missing. Next: save a key, refresh models, then test the model." }
|
|
156
|
-
if !providerPresent { return "OpenRouter: provider missing. Next: save the key again to repair the provider block." }
|
|
157
|
-
if selected { return "OpenRouter: active · main model \(activeModel). Next: test after changing models, or choose another provider to switch away." }
|
|
158
|
-
return "OpenRouter: key stored · activation model \(selectedOpenRouterModel()) · not selected. Next: test, then activate the selected model."
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
@objc func refreshOpenRouterModelsAction(_ sender: NSButton) { refreshOpenRouterModels() }
|
|
162
|
-
|
|
163
|
-
func refreshOpenRouterModels() {
|
|
164
|
-
guard !catalogRefreshInFlight else { return }
|
|
165
|
-
catalogRefreshInFlight = true
|
|
166
|
-
clearOpenRouterModels(reason: "Validating the saved credential before exposing models…")
|
|
167
|
-
openRouterRefreshButton?.isEnabled = false
|
|
168
|
-
openRouterCatalogStatus.stringValue = "Loading OpenRouter model catalog…"
|
|
169
|
-
processClient.run(["codex-app", "openrouter-models", "--ids-only", "--json"], timeout: NativeView.mutationTimeout) { [weak self] result in
|
|
170
|
-
guard let self = self else { return }
|
|
171
|
-
self.catalogRefreshInFlight = false
|
|
172
|
-
self.openRouterRefreshButton?.isEnabled = !self.busy
|
|
173
|
-
guard let json = self.json(result.output) else {
|
|
174
|
-
self.clearOpenRouterModels(reason: "Catalog unavailable · no model list is exposed. Next: reconnect the key or retry.")
|
|
175
|
-
return
|
|
176
|
-
}
|
|
177
|
-
guard result.code == 0, json["ok"] as? Bool == true else {
|
|
178
|
-
self.clearOpenRouterModels(reason: "Catalog unavailable · \(self.structuredPublicDetail(json, fallback: result.output, codePrefix: "E-OR")) No model list is exposed.")
|
|
179
|
-
return
|
|
180
|
-
}
|
|
181
|
-
guard json["authenticated"] as? Bool == true else {
|
|
182
|
-
self.clearOpenRouterModels(reason: "Credential validation failed · model and child-agent lists were withdrawn. Choose Reconnect OpenRouter credential.")
|
|
183
|
-
return
|
|
184
|
-
}
|
|
185
|
-
let models = self.openRouterModelIds(json)
|
|
186
|
-
guard !models.isEmpty else {
|
|
187
|
-
self.clearOpenRouterModels(reason: "Authenticated catalog returned no selectable models · no model list is exposed.")
|
|
188
|
-
return
|
|
189
|
-
}
|
|
190
|
-
self.openRouterCredentialValidatedNow = true
|
|
191
|
-
self.openRouterModels = models
|
|
192
|
-
self.openRouterModelPopup.removeAllItems()
|
|
193
|
-
self.openRouterModelPopup.addItem(withTitle: "Choose from \(models.count) models…")
|
|
194
|
-
self.openRouterModelPopup.addItems(withTitles: models)
|
|
195
|
-
self.synchronizeOpenRouterPopupSelection()
|
|
196
|
-
self.openRouterModelPopup.isEnabled = !self.busy
|
|
197
|
-
self.openRouterCatalogStatus.stringValue = "Catalog ready · \(models.count) models · saved key authenticated. Selecting one copies its id into the editable field."
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
private func clearOpenRouterModels(reason: String) {
|
|
202
|
-
openRouterCredentialValidatedNow = false
|
|
203
|
-
openRouterModels = []
|
|
204
|
-
openRouterModelPopup.removeAllItems()
|
|
205
|
-
openRouterModelPopup.addItem(withTitle: "Models hidden until credential validation")
|
|
206
|
-
openRouterModelPopup.isEnabled = false
|
|
207
|
-
openRouterModelField.stringValue = ""
|
|
208
|
-
openRouterModelField.isEnabled = false
|
|
209
|
-
openRouterCatalogStatus.stringValue = reason
|
|
210
|
-
openRouterCatalogStatus.textColor = .systemOrange
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
private func openRouterModelIds(_ json: [String: Any]) -> [String] {
|
|
214
|
-
let raw = (json["models"] as? [Any])
|
|
215
|
-
?? (json["data"] as? [Any])
|
|
216
|
-
?? ((json["catalog"] as? [String: Any])?["models"] as? [Any])
|
|
217
|
-
?? []
|
|
218
|
-
let values = raw.compactMap { entry -> String? in
|
|
219
|
-
if let value = entry as? String { return value }
|
|
220
|
-
guard let row = entry as? [String: Any] else { return nil }
|
|
221
|
-
return (row["id"] as? String) ?? (row["model"] as? String) ?? (row["slug"] as? String)
|
|
222
|
-
}.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }.filter { !$0.isEmpty }
|
|
223
|
-
return Array(Set(values)).sorted { $0.localizedCaseInsensitiveCompare($1) == .orderedAscending }
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
@objc private func selectOpenRouterModel(_ sender: NSPopUpButton) {
|
|
227
|
-
guard sender.indexOfSelectedItem > 0, let model = sender.titleOfSelectedItem else { return }
|
|
228
|
-
openRouterModelSelectionPending = true
|
|
229
|
-
openRouterActionRan = true
|
|
230
|
-
openRouterModelField.stringValue = model
|
|
231
|
-
openRouterStatus.stringValue = "Selected \(model) from the catalog. Next: test the connection; activation remains unchanged."
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
func controlTextDidChange(_ notification: Notification) {
|
|
235
|
-
guard let field = notification.object as? NSTextField else { return }
|
|
236
|
-
if field === openRouterModelField {
|
|
237
|
-
openRouterModelSelectionPending = true
|
|
238
|
-
openRouterActionRan = true
|
|
239
|
-
synchronizeOpenRouterPopupSelection()
|
|
240
|
-
openRouterStatus.stringValue = "Manual model selection changed. Next: test the connection; activation remains unchanged."
|
|
241
|
-
} else if field === multiProvider.model {
|
|
242
|
-
multiProvider.modelSelectionPending = true
|
|
243
|
-
synchronizeMultiProviderPopupSelection()
|
|
244
|
-
multiProvider.status.stringValue = "Manual routed model selection changed. Check the router before activation."
|
|
245
|
-
multiProvider.status.textColor = .secondaryLabelColor
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
private func synchronizeOpenRouterPopupSelection() {
|
|
250
|
-
let current = selectedOpenRouterModel()
|
|
251
|
-
if let index = openRouterModels.firstIndex(of: current) {
|
|
252
|
-
openRouterModelPopup.selectItem(at: index + 1)
|
|
253
|
-
} else {
|
|
254
|
-
openRouterModelPopup.selectItem(at: 0)
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
@objc func saveOpenRouterKey() {
|
|
259
|
-
guard let window = view.window else { return }
|
|
260
|
-
openRouterActionRan = true
|
|
261
|
-
promptForSecretKey(
|
|
262
|
-
window: window,
|
|
263
|
-
sheetTitle: "OpenRouter API Key",
|
|
264
|
-
sheetMessage: "Paste your OpenRouter API key. It is sent through stdin and never logged. Saving does not switch the active provider.",
|
|
265
|
-
placeholder: "sk-or-…",
|
|
266
|
-
args: ["codex-app", "set-openrouter-key", "--api-key-stdin", "--json"],
|
|
267
|
-
kind: "openrouter-set-key",
|
|
268
|
-
title: "Save OpenRouter key",
|
|
269
|
-
credential: .openRouterApiKey,
|
|
270
|
-
statusLabel: openRouterStatus,
|
|
271
|
-
successSummary: "OpenRouter key saved",
|
|
272
|
-
failSummary: "OpenRouter key save failed",
|
|
273
|
-
codePrefix: "E-OR"
|
|
274
|
-
)
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
@objc private func testOpenRouterConnection() {
|
|
278
|
-
let model = selectedOpenRouterModel()
|
|
279
|
-
openRouterActionRan = true
|
|
280
|
-
guard !model.isEmpty else {
|
|
281
|
-
openRouterStatus.stringValue = "Connection test blocked · model id is empty. Next: choose or enter a model."
|
|
43
|
+
func renderProviderProfiles(_ json: [String: Any]) {
|
|
44
|
+
guard let providers = json["providers"] as? [String: Any] else {
|
|
45
|
+
cliProviderStatus.stringValue = "Codex-LB · schema unavailable · no state assumed"
|
|
46
|
+
openRouterCredentialStatus.stringValue = "OpenRouter · schema unavailable · no state assumed"
|
|
47
|
+
cliProviderStatus.textColor = .systemRed
|
|
48
|
+
openRouterCredentialStatus.textColor = .systemRed
|
|
282
49
|
return
|
|
283
50
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
let
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
51
|
+
renderProviderProfile(providers["codex-lb"] as? [String: Any], id: "codex-lb", label: cliProviderStatus)
|
|
52
|
+
renderProviderProfile(providers["openrouter"] as? [String: Any], id: "openrouter", label: openRouterCredentialStatus)
|
|
53
|
+
let identity = json["native_identity"] as? [String: Any]
|
|
54
|
+
let identityState = identity?["state"] as? String ?? "not_attempted"
|
|
55
|
+
oauthCredentialStatus.stringValue = "ChatGPT OAuth Identity · \(identityState) · Codex-owned; never copied into provider profiles"
|
|
56
|
+
oauthCredentialStatus.textColor = identityState == "verified" ? .systemGreen : .secondaryLabelColor
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private func renderProviderProfile(_ profile: [String: Any]?, id: String, label: NSTextField) {
|
|
60
|
+
guard profile?["schema"] as? String == "sks.bridge-provider-profile-status.v1",
|
|
61
|
+
profile?["provider_id"] as? String == id,
|
|
62
|
+
let credential = profile?["credential"] as? [String: Any],
|
|
63
|
+
let catalog = profile?["catalog"] as? [String: Any],
|
|
64
|
+
catalog["schema"] as? String == "sks.catalog-sync-state.v2" else {
|
|
65
|
+
label.stringValue = "\(displayProvider(id)) · capability schema invalid"
|
|
66
|
+
label.textColor = .systemRed; return
|
|
67
|
+
}
|
|
68
|
+
let enabled = profile?["enabled"] as? Bool == true
|
|
69
|
+
providerEnabled[id] = enabled
|
|
70
|
+
let credentialState = credential["state"] as? String ?? "unavailable"
|
|
71
|
+
let endpoint = profile?["endpoint"] as? [String: Any]
|
|
72
|
+
let origin = ProviderSecretRedactor.redactEndpoint(endpoint?["origin_redacted"] as? String ?? "not configured")
|
|
73
|
+
let auth = endpoint?["auth_transport"] as? String ?? "unreported"
|
|
74
|
+
let catalogState = catalog["state"] as? String ?? "failed"
|
|
75
|
+
let checked = credential["checked_at"] as? String ?? catalog["checked_at"] as? String ?? "never"
|
|
76
|
+
label.stringValue = "\(displayProvider(id)) · \(enabled ? "enabled" : "disabled") · credential \(credentialState) · endpoint \(origin) · auth \(auth) · catalog \(catalogState) · last \(checked)"
|
|
77
|
+
label.textColor = credentialState == "ready" && enabled ? .systemGreen
|
|
78
|
+
: ["rejected", "unavailable"].contains(credentialState) ? .systemRed : .systemOrange
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@objc func configureCodexLbProfile() {
|
|
315
82
|
guard let window = view.window else { return }
|
|
316
|
-
AlertFactory.
|
|
317
|
-
|
|
318
|
-
title: "
|
|
319
|
-
message: "\(model) becomes the Codex main model and Codex App restarts. SKS remaps the Desktop thread sidebar so prior provider chats stay listed, snapshots the previous provider for Restore, and merges the model cache instead of wiping it. Codex Desktop may still label third-party models as Custom.",
|
|
320
|
-
destructive: false
|
|
321
|
-
) { [weak self] approved in
|
|
322
|
-
guard let self = self, approved else { return }
|
|
323
|
-
self.performUseOpenRouter(model: model)
|
|
83
|
+
AlertFactory.textSheet(window: window, title: "Codex-LB Endpoint", message: "Enter the Codex-LB endpoint. Its credential is stored independently from OpenRouter and ChatGPT OAuth.", secure: false, placeholder: "https://lb.example.com") { [weak self] host in
|
|
84
|
+
guard let self = self, let host = host?.trimmingCharacters(in: .whitespacesAndNewlines), !host.isEmpty else { return }
|
|
85
|
+
self.promptForSecretKey(window: window, sheetTitle: "Codex-LB API Key", sheetMessage: "Paste the Codex-LB key. It is sent through stdin and is never rendered in status, receipts, or logs.", placeholder: "sk-clb-…", args: ["bridge", "provider", "configure", "codex-lb", "--host", host, "--api-key-stdin", "--json"], kind: "bridge-provider-configure-codex-lb", title: "Configure Codex-LB profile", credential: .codexLbApiKey, statusLabel: self.cliProviderStatus, successSummary: "Codex-LB profile configured; OpenRouter preserved", failSummary: "Codex-LB profile configuration failed", providerId: "codex-lb")
|
|
324
86
|
}
|
|
325
87
|
}
|
|
326
88
|
|
|
327
|
-
@objc func
|
|
328
|
-
openRouterActionRan = true
|
|
329
|
-
guard !busy else { openRouterStatus.stringValue = "Another provider action is already running."; return }
|
|
89
|
+
@objc func configureOpenRouterProfile() {
|
|
330
90
|
guard let window = view.window else { return }
|
|
331
|
-
|
|
332
|
-
window: window,
|
|
333
|
-
title: "Restore previous provider?",
|
|
334
|
-
message: "Restores the last pre-OpenRouter Desktop provider/model/catalog snapshot, reverses sidebar remap when present, and restarts Codex App.",
|
|
335
|
-
destructive: false
|
|
336
|
-
) { [weak self] approved in
|
|
337
|
-
guard let self = self, approved else { return }
|
|
338
|
-
self.performRestorePreviousDesktopRouting()
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
private func performRestorePreviousDesktopRouting() {
|
|
343
|
-
guard let snapshot = operations.begin(kind: "desktop-routing-restore", mutationGroup: "codex-config", summary: "Restore previous Desktop provider") else {
|
|
344
|
-
openRouterStatus.stringValue = "Another guarded mutation is already running. Wait or open Diagnostics."
|
|
345
|
-
return
|
|
346
|
-
}
|
|
347
|
-
setBusy(true)
|
|
348
|
-
openRouterStatus.stringValue = "Restoring previous Desktop provider and restarting Codex App…"
|
|
349
|
-
_ = operations.update(snapshot, state: .running, stage: "restoring", progress: nil, summary: "Restore previous Desktop provider")
|
|
350
|
-
processClient.run(["codex-app", "restore-desktop-routing", "--restart-app", "--json"], timeout: NativeView.mutationTimeout) { [weak self] result in
|
|
351
|
-
guard let self = self else { return }
|
|
352
|
-
self.setBusy(false)
|
|
353
|
-
let json = self.json(result.output)
|
|
354
|
-
let ok = result.code == 0 && json?["ok"] as? Bool == true
|
|
355
|
-
let status = json?["status"] as? String ?? (ok ? "restored" : "failed")
|
|
356
|
-
let detail = self.structuredPublicDetail(json, fallback: result.output, codePrefix: "E-OR")
|
|
357
|
-
// Routing rolls back before the sidebar retag, so a blocked retag is
|
|
358
|
-
// partial success: the snapshot is kept and re-running finishes it.
|
|
359
|
-
let sidebarBlocked = status.hasPrefix("restored_sidebar")
|
|
360
|
-
_ = self.operations.update(snapshot, state: ok ? .succeeded : .failed, stage: "complete", progress: 1, summary: ok ? "Previous Desktop provider restored" : sidebarBlocked ? "Desktop provider restored; sidebar retag pending" : "Desktop provider restore needs action")
|
|
361
|
-
self.openRouterStatus.stringValue = ok
|
|
362
|
-
? "Previous provider restored · \(status). Fully quit and reopen Codex App if chats or the picker still look stale."
|
|
363
|
-
: sidebarBlocked
|
|
364
|
-
? "Provider restored, but prior-provider chats stay hidden · fully quit Codex App, then run Restore previous provider again."
|
|
365
|
-
: "Restore failed · \(status) · \(detail)"
|
|
366
|
-
self.refreshOpenRouterStatus()
|
|
367
|
-
self.refresh()
|
|
368
|
-
}
|
|
91
|
+
promptForSecretKey(window: window, sheetTitle: "OpenRouter API Key", sheetMessage: "Paste the OpenRouter key. Codex-LB and ChatGPT OAuth remain unchanged.", placeholder: "sk-or-…", args: ["bridge", "provider", "configure", "openrouter", "--api-key-stdin", "--json"], kind: "bridge-provider-configure-openrouter", title: "Configure OpenRouter profile", credential: .openRouterApiKey, statusLabel: openRouterCredentialStatus, successSummary: "OpenRouter profile configured; Codex-LB preserved", failSummary: "OpenRouter profile configuration failed", codePrefix: "E-OR", providerId: "openrouter")
|
|
369
92
|
}
|
|
370
93
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
}
|
|
376
|
-
guard let snapshot = beginProviderApply(kind: "openrouter-use", summary: "Use OpenRouter", mode: "openrouter", model: model) else {
|
|
377
|
-
openRouterStatus.stringValue = "Another guarded mutation is already running. Wait or open Diagnostics."
|
|
378
|
-
return
|
|
379
|
-
}
|
|
380
|
-
setBusy(true)
|
|
381
|
-
openRouterStatus.stringValue = "Activating OpenRouter main model \(model) and restarting Codex App…"
|
|
382
|
-
let running = operations.update(snapshot, state: .running, stage: "activating", progress: nil, summary: "Use OpenRouter")
|
|
383
|
-
processClient.run(["codex-app", "use-openrouter", "--model", model, "--restart-app", "--json"], timeout: NativeView.mutationTimeout) { [weak self] activation in
|
|
384
|
-
guard let self = self else { return }
|
|
385
|
-
_ = self.operations.update(running, state: .running, stage: "verifying", progress: nil, summary: "Verify OpenRouter main model")
|
|
386
|
-
self.processClient.run(["codex-app", "openrouter-status", "--json"], timeout: NativeView.statusTimeout) { [weak self] status in
|
|
387
|
-
guard let self = self else { return }
|
|
388
|
-
self.setBusy(false)
|
|
389
|
-
let activationJson = self.json(activation.output)
|
|
390
|
-
let statusJson = self.json(status.output)
|
|
391
|
-
let selected = status.code == 0 && statusJson?["selected"] as? Bool == true
|
|
392
|
-
let activeModel = statusJson?["model"] as? String
|
|
393
|
-
let configApplied = activation.code == 0
|
|
394
|
-
&& activationJson?["config_applied"] as? Bool == true
|
|
395
|
-
&& selected
|
|
396
|
-
&& activeModel == model
|
|
397
|
-
if configApplied {
|
|
398
|
-
self.openRouterModelSelectionPending = false
|
|
399
|
-
}
|
|
400
|
-
let restartOK = activationJson?["restart_ok"] as? Bool == true
|
|
401
|
-
let remapped = (activationJson?["thread_sidebar_remap"] as? [String: Any])?["remapped"] as? Int ?? 0
|
|
402
|
-
let restoreAvailable = activationJson?["previous_routing_restore_available"] as? Bool == true
|
|
403
|
-
var applyJson = activationJson ?? [:]
|
|
404
|
-
applyJson["catalog_refreshed"] = status.code == 0
|
|
405
|
-
&& statusJson?["key_validated"] as? Bool == true
|
|
406
|
-
&& activeModel == model
|
|
407
|
-
applyJson["new_session_ready"] = restartOK
|
|
408
|
-
let applied = self.recordProviderApplyResult(
|
|
409
|
-
running,
|
|
410
|
-
json: applyJson,
|
|
411
|
-
configurationSaved: configApplied,
|
|
412
|
-
proxyApplied: selected && activeModel == model
|
|
413
|
-
)
|
|
414
|
-
let complete = configApplied && restartOK && applied.providerApply?.allSucceeded == true
|
|
415
|
-
if complete {
|
|
416
|
-
var parts = ["Activation complete · OpenRouter is active · main model \(model)"]
|
|
417
|
-
if remapped > 0 { parts.append("sidebar kept \(remapped) prior-provider chats visible") }
|
|
418
|
-
if restoreAvailable { parts.append("Restore previous provider is available") }
|
|
419
|
-
parts.append("Desktop may still show Custom for third-party models")
|
|
420
|
-
self.openRouterStatus.stringValue = parts.joined(separator: " · ") + "."
|
|
421
|
-
} else if configApplied {
|
|
422
|
-
self.openRouterStatus.stringValue = "Configuration saved · main model \(model) is selected, but Codex App did not restart. Next: reopen Codex App, then verify status."
|
|
423
|
-
} else {
|
|
424
|
-
self.openRouterStatus.stringValue = "Activation incomplete · requested \(model), observed \(activeModel ?? "unknown") · \(self.structuredPublicDetail(activationJson, fallback: activation.output, codePrefix: "E-OR"))"
|
|
425
|
-
}
|
|
426
|
-
self.refreshOpenRouterStatus()
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
}
|
|
94
|
+
@objc func validateCodexLbProfile() { runProviderAction("codex-lb", args: ["bridge", "provider", "validate", "codex-lb", "--json"], title: "Validate Codex-LB", mutationGroup: nil) }
|
|
95
|
+
@objc func validateOpenRouterProfile() { runProviderAction("openrouter", args: ["bridge", "provider", "validate", "openrouter", "--json"], title: "Validate OpenRouter", mutationGroup: nil) }
|
|
96
|
+
@objc func toggleCodexLbProfile() { toggleProvider("codex-lb") }
|
|
97
|
+
@objc func toggleOpenRouterProfile() { toggleProvider("openrouter") }
|
|
430
98
|
|
|
431
|
-
private func
|
|
432
|
-
|
|
99
|
+
private func toggleProvider(_ id: String) {
|
|
100
|
+
let verb = providerEnabled[id] == true ? "disable" : "enable"
|
|
101
|
+
runProviderAction(id, args: ["bridge", "provider", verb, id, "--json"], title: "\(verb.capitalized) \(displayProvider(id))", mutationGroup: "codex-config")
|
|
433
102
|
}
|
|
434
103
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
let
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
let next = firstGuidance(json) ?? (json?["hint"] as? String) ?? fallbackNext
|
|
450
|
-
return "\(parts.joined(separator: " · ")). Next: \(next)."
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
func publicFailureCode(_ json: [String: Any]?, codePrefix: String = "E-LB") -> String {
|
|
454
|
-
let blockers = json?["blockers"] as? [String] ?? []
|
|
455
|
-
let truth = json?["routing_truth"] as? [String: Any]
|
|
456
|
-
let truthBlockers = truth?["blockers"] as? [String] ?? []
|
|
457
|
-
let all = blockers + truthBlockers
|
|
458
|
-
if all.contains(where: { $0.contains("auth_rejected") || $0.contains("gateway_auth_rejected") }) {
|
|
459
|
-
return "\(codePrefix)-AUTH"
|
|
104
|
+
private func runProviderAction(_ id: String, args: [String], title: String, mutationGroup: String?) {
|
|
105
|
+
guard !providerActionInFlight.contains(id), let snapshot = operations.begin(kind: "bridge-provider-\(id)", mutationGroup: mutationGroup, summary: title) else { return }
|
|
106
|
+
setProviderActionBusy(id, true)
|
|
107
|
+
let label = id == "codex-lb" ? cliProviderStatus : openRouterCredentialStatus
|
|
108
|
+
label.stringValue = "\(title)…"
|
|
109
|
+
_ = operations.update(snapshot, state: .running, stage: "running", progress: nil, summary: title)
|
|
110
|
+
processClient.run(args, timeout: NativeView.mutationTimeout) { [weak self] result in
|
|
111
|
+
guard let self = self else { return }
|
|
112
|
+
self.setProviderActionBusy(id, false)
|
|
113
|
+
let parsed = self.json(result.output)
|
|
114
|
+
let generated = parsed != nil && parsed?["schema"] != nil
|
|
115
|
+
_ = self.operations.update(snapshot, state: generated ? .succeeded : .failed, stage: "complete", progress: 1, summary: generated ? "\(title) report generated" : "\(title) failed")
|
|
116
|
+
if !generated { label.stringValue = "\(title) failed · \(self.structuredPublicDetail(parsed, fallback: result.output))" }
|
|
117
|
+
self.refresh()
|
|
460
118
|
}
|
|
461
|
-
if all.contains(where: { $0.contains("unreachable") }) { return "\(codePrefix)-UNREACHABLE" }
|
|
462
|
-
if all.contains(where: { $0.contains("legacy") && $0.contains("migration") }) { return "\(codePrefix)-LEGACY-MIGRATE" }
|
|
463
|
-
if let http = publicHttpStatus(json), http >= 400 { return "\(codePrefix)-HTTP-\(http)" }
|
|
464
|
-
if json == nil || json?["ok"] as? Bool == false { return "\(codePrefix)-FAILED" }
|
|
465
|
-
return "\(codePrefix)-INFO"
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
func publicHttpStatus(_ json: [String: Any]?) -> Int? {
|
|
469
|
-
if let value = json?["http_status"] as? Int { return value }
|
|
470
|
-
if let value = (json?["http_status"] as? NSNumber)?.intValue { return value }
|
|
471
|
-
let truth = json?["routing_truth"] as? [String: Any]
|
|
472
|
-
if let value = truth?["http_status"] as? Int { return value }
|
|
473
|
-
return (truth?["http_status"] as? NSNumber)?.intValue
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
func publicAuthTransport(_ json: [String: Any]?) -> String? {
|
|
477
|
-
if let value = json?["gateway_auth_transport"] as? String, !value.isEmpty { return value }
|
|
478
|
-
if let value = json?["auth_transport"] as? String, !value.isEmpty { return value }
|
|
479
|
-
let truth = json?["routing_truth"] as? [String: Any]
|
|
480
|
-
if let value = truth?["auth_transport"] as? String, !value.isEmpty { return value }
|
|
481
|
-
return nil
|
|
482
119
|
}
|
|
483
120
|
|
|
484
|
-
func
|
|
485
|
-
|
|
486
|
-
|
|
121
|
+
func structuredPublicDetail(_ json: [String: Any]?, fallback: String, codePrefix: String = "E-PROVIDER", fallbackNext: String = "Retry the operation") -> String {
|
|
122
|
+
let code = publicFailureCode(json, fallback: codePrefix)
|
|
123
|
+
if let blockers = json?["blockers"] as? [String], let first = blockers.first {
|
|
124
|
+
return "\(code) · \(ProviderSecretRedactor.redact(first)) · Next: \(fallbackNext)"
|
|
487
125
|
}
|
|
488
|
-
return
|
|
126
|
+
return "\(code) · \(ProviderSecretRedactor.redact(NativeView.redactPreview(fallback))) · Next: \(fallbackNext)"
|
|
489
127
|
}
|
|
490
128
|
|
|
491
|
-
func
|
|
492
|
-
if let
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
let message = value["message"] as? String
|
|
496
|
-
return [code, message].compactMap { $0 }.joined(separator: ": ")
|
|
129
|
+
func publicFailureCode(_ json: [String: Any]?, fallback: String = "E-PROVIDER") -> String {
|
|
130
|
+
if let code = json?["code"] as? String, !code.isEmpty { return ProviderSecretRedactor.redact(code) }
|
|
131
|
+
if let blocker = (json?["blockers"] as? [String])?.first, !blocker.isEmpty { return ProviderSecretRedactor.redact(blocker) }
|
|
132
|
+
return fallback
|
|
497
133
|
}
|
|
498
134
|
|
|
135
|
+
private func displayProvider(_ id: String) -> String { id == "codex-lb" ? "Codex-LB" : "OpenRouter" }
|
|
499
136
|
}
|