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,321 +1,387 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
enum MeasuredState: Equatable {
|
|
9
|
-
case inactive
|
|
10
|
-
case active
|
|
11
|
-
case degraded
|
|
12
|
-
case unverified
|
|
3
|
+
enum ProviderFacadeError: Error, Equatable, CustomStringConvertible {
|
|
4
|
+
case schemaInvalid(String)
|
|
5
|
+
|
|
6
|
+
var description: String {
|
|
7
|
+
switch self { case .schemaInvalid(let reason): return reason }
|
|
13
8
|
}
|
|
9
|
+
}
|
|
14
10
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
let fresh: Bool
|
|
19
|
-
let ok: Bool
|
|
20
|
-
let status: String
|
|
21
|
-
let configuredHost: String?
|
|
22
|
-
let actualHost: String?
|
|
23
|
-
let authTransport: String?
|
|
24
|
-
let authOutcome: String?
|
|
25
|
-
let httpStatus: Int?
|
|
26
|
-
let measuredAt: String?
|
|
27
|
-
let latencyMs: Int?
|
|
28
|
-
let blockers: [String]
|
|
11
|
+
enum CapabilityProbeState: String, Codable, CaseIterable {
|
|
12
|
+
case notAttempted = "not_attempted", running, verified, degraded, blocked, failed, unsupported, stale
|
|
13
|
+
}
|
|
29
14
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
15
|
+
enum CapabilityProbeStage: String, Codable {
|
|
16
|
+
case preflight, process, tcpConnect = "tcp_connect", httpHealth = "http_health"
|
|
17
|
+
case websocketUpgrade = "websocket_upgrade", websocketProtocol = "websocket_protocol"
|
|
18
|
+
case frameRoundTrip = "frame_round_trip", cleanClose = "clean_close"
|
|
19
|
+
case providerAuth = "provider_auth", catalogSync = "catalog_sync", modelRoute = "model_route"
|
|
20
|
+
case featureRequest = "feature_request", featureResponse = "feature_response"
|
|
21
|
+
case artifactValidation = "artifact_validation", complete
|
|
22
|
+
}
|
|
33
23
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
24
|
+
enum CapabilityScope: String, Codable, CaseIterable {
|
|
25
|
+
case bridge
|
|
26
|
+
case nativeIdentity = "native-identity"
|
|
27
|
+
case codexLb = "provider:codex-lb"
|
|
28
|
+
case openRouter = "provider:openrouter"
|
|
29
|
+
case combinedCatalog = "catalog:combined"
|
|
30
|
+
|
|
31
|
+
var displayName: String {
|
|
32
|
+
switch self {
|
|
33
|
+
case .bridge: return "Bridge"
|
|
34
|
+
case .nativeIdentity: return "OAuth Identity"
|
|
35
|
+
case .codexLb: return "Codex-LB"
|
|
36
|
+
case .openRouter: return "OpenRouter"
|
|
37
|
+
case .combinedCatalog: return "Combined Catalog"
|
|
41
38
|
}
|
|
42
39
|
}
|
|
40
|
+
}
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
let desktopFullRouting: Bool
|
|
47
|
-
let legacyCodexLbSelected: Bool
|
|
48
|
-
let chatgptOauthPresent: Bool
|
|
49
|
-
let cliProviderStored: Bool
|
|
50
|
-
let cliCredentialsConfigured: Bool
|
|
51
|
-
let legacyDestructive: Bool
|
|
52
|
-
let oauthPreservedFlag: Bool?
|
|
53
|
-
let authMutated: Bool?
|
|
54
|
-
}
|
|
42
|
+
enum JSONValue: Codable, Equatable {
|
|
43
|
+
case string(String), number(Double), boolean(Bool), object([String: JSONValue]), array([JSONValue]), null
|
|
55
44
|
|
|
56
|
-
|
|
57
|
-
let
|
|
58
|
-
|
|
59
|
-
let
|
|
60
|
-
let
|
|
61
|
-
let
|
|
62
|
-
let
|
|
63
|
-
|
|
64
|
-
?? (oauth?["present"] as? Bool == true
|
|
65
|
-
|| ["chatgpt_oauth", "mixed"].contains(oauth?["mode"] as? String ?? ""))
|
|
66
|
-
let legacyCodexLbSelected = json["legacy_codex_lb_selected"] as? Bool
|
|
67
|
-
?? isLegacyCodexLbSelected(json: json, mode: mode, provider: provider)
|
|
68
|
-
let cliProviderStored = ["cli-provider", "desktop-native-bridge", "desktop-dual-auth-compat"].contains(mode ?? "")
|
|
69
|
-
|| legacyPayload["provider_configured"] as? Bool == true
|
|
70
|
-
|| ((provider?["contract"] as? String)?.hasPrefix("codex-lb") == true)
|
|
71
|
-
let keyFingerprint = bridge?["key_fingerprint"] as? String
|
|
72
|
-
let cliCredentialsConfigured = legacyPayload["env_key_configured"] as? Bool == true
|
|
73
|
-
|| legacyPayload["key_configured"] as? Bool == true
|
|
74
|
-
|| json["configured"] as? Bool == true
|
|
75
|
-
|| (keyFingerprint?.isEmpty == false)
|
|
76
|
-
let oauthPreservedFlag = (json["oauth_preserved"] as? Bool)
|
|
77
|
-
?? (capabilities?["oauth_preserved"] as? Bool)
|
|
78
|
-
?? (oauth?["preserved"] as? Bool)
|
|
79
|
-
return Snapshot(
|
|
80
|
-
mode: mode,
|
|
81
|
-
desktopFullRouting: mode == "desktop-native-bridge",
|
|
82
|
-
legacyCodexLbSelected: legacyCodexLbSelected,
|
|
83
|
-
chatgptOauthPresent: chatgptOauthPresent,
|
|
84
|
-
cliProviderStored: cliProviderStored,
|
|
85
|
-
cliCredentialsConfigured: cliCredentialsConfigured,
|
|
86
|
-
legacyDestructive: isLegacyDestructive(json),
|
|
87
|
-
oauthPreservedFlag: oauthPreservedFlag,
|
|
88
|
-
authMutated: json["auth_mutated"] as? Bool ?? json["desktop_auth_mutated"] as? Bool
|
|
89
|
-
)
|
|
45
|
+
init(from decoder: Decoder) throws {
|
|
46
|
+
let value = try decoder.singleValueContainer()
|
|
47
|
+
if value.decodeNil() { self = .null }
|
|
48
|
+
else if let boolean = try? value.decode(Bool.self) { self = .boolean(boolean) }
|
|
49
|
+
else if let number = try? value.decode(Double.self) { self = .number(number) }
|
|
50
|
+
else if let string = try? value.decode(String.self) { self = .string(string) }
|
|
51
|
+
else if let object = try? value.decode([String: JSONValue].self) { self = .object(object) }
|
|
52
|
+
else { self = .array(try value.decode([JSONValue].self)) }
|
|
90
53
|
}
|
|
91
54
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
55
|
+
func encode(to encoder: Encoder) throws {
|
|
56
|
+
var value = encoder.singleValueContainer()
|
|
57
|
+
switch self {
|
|
58
|
+
case .string(let raw): try value.encode(raw)
|
|
59
|
+
case .number(let raw): try value.encode(raw)
|
|
60
|
+
case .boolean(let raw): try value.encode(raw)
|
|
61
|
+
case .object(let raw): try value.encode(raw)
|
|
62
|
+
case .array(let raw): try value.encode(raw)
|
|
63
|
+
case .null: try value.encodeNil()
|
|
96
64
|
}
|
|
97
|
-
return MeasuredRoute(
|
|
98
|
-
selected: truth["selected"] as? Bool == true,
|
|
99
|
-
measured: truth["measured"] as? Bool == true,
|
|
100
|
-
fresh: truth["fresh"] as? Bool == true,
|
|
101
|
-
ok: truth["ok"] as? Bool == true,
|
|
102
|
-
status: truth["status"] as? String ?? "unavailable",
|
|
103
|
-
configuredHost: truth["configured_host"] as? String,
|
|
104
|
-
actualHost: truth["actual_host"] as? String,
|
|
105
|
-
authTransport: truth["auth_transport"] as? String,
|
|
106
|
-
authOutcome: truth["auth_outcome"] as? String,
|
|
107
|
-
httpStatus: (truth["http_status"] as? NSNumber)?.intValue,
|
|
108
|
-
measuredAt: truth["measured_at"] as? String ?? truth["checked_at"] as? String,
|
|
109
|
-
latencyMs: (truth["latency_ms"] as? NSNumber)?.intValue,
|
|
110
|
-
blockers: truth["blockers"] as? [String] ?? []
|
|
111
|
-
)
|
|
112
65
|
}
|
|
66
|
+
}
|
|
113
67
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
68
|
+
struct CapabilityProbeResultV3: Codable, Equatable {
|
|
69
|
+
let schema: String
|
|
70
|
+
let capability: String
|
|
71
|
+
let scope: CapabilityScope
|
|
72
|
+
let requestedLevel: String
|
|
73
|
+
let stage: CapabilityProbeStage
|
|
74
|
+
let state: CapabilityProbeState
|
|
75
|
+
let checkedAt: String
|
|
76
|
+
let reportId: String
|
|
77
|
+
let correlationId: String
|
|
78
|
+
let sessionId: String
|
|
79
|
+
let attemptId: Int
|
|
80
|
+
let terminal: Bool
|
|
81
|
+
let rootCause: String?
|
|
82
|
+
let blockers: [String]
|
|
83
|
+
let warnings: [String]
|
|
84
|
+
let retryable: Bool
|
|
85
|
+
let recoveryAction: String?
|
|
86
|
+
let source: String
|
|
87
|
+
let evidence: [String: JSONValue]
|
|
88
|
+
|
|
89
|
+
enum CodingKeys: String, CodingKey {
|
|
90
|
+
case schema, capability, scope, stage, state, terminal, blockers, warnings, retryable, source, evidence
|
|
91
|
+
case requestedLevel = "requested_level", checkedAt = "checked_at", reportId = "report_id"
|
|
92
|
+
case correlationId = "correlation_id", sessionId = "session_id", attemptId = "attempt_id"
|
|
93
|
+
case rootCause = "root_cause", recoveryAction = "recovery_action"
|
|
122
94
|
}
|
|
95
|
+
}
|
|
123
96
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
97
|
+
struct ScopeCapabilitySummaryV3: Codable, Equatable {
|
|
98
|
+
let schema: String
|
|
99
|
+
let scope: CapabilityScope
|
|
100
|
+
let state: CapabilityProbeState
|
|
101
|
+
let checkedAt: String
|
|
102
|
+
let capabilities: [String: CapabilityProbeResultV3]
|
|
103
|
+
let blockers: [String]
|
|
104
|
+
let warnings: [String]
|
|
105
|
+
|
|
106
|
+
enum CodingKeys: String, CodingKey { case schema, scope, state, capabilities, blockers, warnings; case checkedAt = "checked_at" }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
struct CatalogSyncStateV2: Codable, Equatable {
|
|
110
|
+
let schema: String
|
|
111
|
+
let providerId: String
|
|
112
|
+
let state: String
|
|
113
|
+
let source: String?
|
|
114
|
+
let generation: String?
|
|
115
|
+
let digest: String?
|
|
116
|
+
let modelCount: Int?
|
|
117
|
+
let checkedAt: String?
|
|
118
|
+
let expiresAt: String?
|
|
119
|
+
let blockers: [String]
|
|
120
|
+
let warnings: [String]
|
|
121
|
+
let recoveryAction: String?
|
|
122
|
+
|
|
123
|
+
enum CodingKeys: String, CodingKey {
|
|
124
|
+
case schema, state, source, generation, digest, blockers, warnings
|
|
125
|
+
case providerId = "provider_id", modelCount = "model_count", checkedAt = "checked_at"
|
|
126
|
+
case expiresAt = "expires_at", recoveryAction = "recovery_action"
|
|
143
127
|
}
|
|
128
|
+
}
|
|
144
129
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
130
|
+
struct CombinedCatalogSyncStatusV1: Codable, Equatable {
|
|
131
|
+
let schema: String
|
|
132
|
+
let state: String
|
|
133
|
+
let generation: String?
|
|
134
|
+
let digest: String?
|
|
135
|
+
let modelCount: Int?
|
|
136
|
+
let routeCount: Int?
|
|
137
|
+
let conflictCount: Int
|
|
138
|
+
let checkedAt: String?
|
|
139
|
+
let providers: [String: CatalogSyncStateV2]
|
|
140
|
+
let blockers: [String]
|
|
141
|
+
let warnings: [String]
|
|
142
|
+
let recoveryAction: String?
|
|
143
|
+
|
|
144
|
+
enum CodingKeys: String, CodingKey {
|
|
145
|
+
case schema, state, generation, digest, providers, blockers, warnings
|
|
146
|
+
case modelCount = "model_count", routeCount = "route_count", conflictCount = "conflict_count"
|
|
147
|
+
case checkedAt = "checked_at", recoveryAction = "recovery_action"
|
|
158
148
|
}
|
|
159
149
|
}
|
|
160
150
|
|
|
161
|
-
struct
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
151
|
+
struct DesktopCapabilityReportV3: Codable, Equatable {
|
|
152
|
+
struct Execution: Codable, Equatable { let ok: Bool; let status: String; let blockers: [String] }
|
|
153
|
+
struct Summary: Codable, Equatable {
|
|
154
|
+
let bridgeReady: Bool
|
|
155
|
+
let activeRoutesReady: Bool
|
|
156
|
+
let levelSatisfied: Bool
|
|
157
|
+
let transportLevelSatisfied: Bool
|
|
158
|
+
let deepLevelSatisfied: Bool
|
|
159
|
+
let fullFeatureVerified: Bool
|
|
160
|
+
let inactiveProviderFailures: [String]
|
|
161
|
+
let blockers: [String]
|
|
162
|
+
let warnings: [String]
|
|
163
|
+
enum CodingKeys: String, CodingKey {
|
|
164
|
+
case blockers, warnings
|
|
165
|
+
case bridgeReady = "bridge_ready", activeRoutesReady = "active_routes_ready", levelSatisfied = "level_satisfied"
|
|
166
|
+
case transportLevelSatisfied = "transport_level_satisfied", deepLevelSatisfied = "deep_level_satisfied"
|
|
167
|
+
case fullFeatureVerified = "full_feature_verified", inactiveProviderFailures = "inactive_provider_failures"
|
|
170
168
|
}
|
|
171
|
-
if let array = value as? [Any] { return array.contains { deepEvidenceTrusted(in: $0) } }
|
|
172
|
-
return false
|
|
173
169
|
}
|
|
174
170
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
171
|
+
let schema: String
|
|
172
|
+
let reportId: String
|
|
173
|
+
let correlationId: String
|
|
174
|
+
let sessionId: String
|
|
175
|
+
let requestedLevel: String
|
|
176
|
+
let checkedAt: String
|
|
177
|
+
let catalogGeneration: String?
|
|
178
|
+
let execution: Execution
|
|
179
|
+
let bridge: ScopeCapabilitySummaryV3
|
|
180
|
+
let nativeIdentity: ScopeCapabilitySummaryV3
|
|
181
|
+
let providers: [String: ScopeCapabilitySummaryV3]
|
|
182
|
+
let combinedCatalog: ScopeCapabilitySummaryV3
|
|
183
|
+
let summary: Summary
|
|
184
|
+
let catalogSync: CombinedCatalogSyncStatusV1
|
|
185
|
+
|
|
186
|
+
enum CodingKeys: String, CodingKey {
|
|
187
|
+
case schema, execution, bridge, providers, summary
|
|
188
|
+
case reportId = "report_id", correlationId = "correlation_id", sessionId = "session_id"
|
|
189
|
+
case requestedLevel = "requested_level", checkedAt = "checked_at", catalogGeneration = "catalog_generation"
|
|
190
|
+
case nativeIdentity = "native_identity", combinedCatalog = "combined_catalog", catalogSync = "catalog_sync"
|
|
179
191
|
}
|
|
180
192
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
} else if let array = value as? [Any] {
|
|
186
|
-
for child in array { collectBlockers(child, into: &result) }
|
|
187
|
-
}
|
|
193
|
+
var maximumAttemptId: Int { allProbes.map(\.attemptId).max() ?? 0 }
|
|
194
|
+
var allProbes: [CapabilityProbeResultV3] { scopeSummaries.flatMap { $0.capabilities.values } }
|
|
195
|
+
var scopeSummaries: [ScopeCapabilitySummaryV3] {
|
|
196
|
+
[bridge, nativeIdentity, providers["codex-lb"], providers["openrouter"], combinedCatalog].compactMap { $0 }
|
|
188
197
|
}
|
|
189
|
-
}
|
|
190
198
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
199
|
+
static func decode(from json: [String: Any]) throws -> DesktopCapabilityReportV3 {
|
|
200
|
+
guard json["schema"] as? String == "sks.desktop-capabilities.v3",
|
|
201
|
+
json["catalog_sync"] is [String: Any] else {
|
|
202
|
+
throw ProviderFacadeError.schemaInvalid("capability_schema_invalid: catalog_sync missing")
|
|
203
|
+
}
|
|
204
|
+
do {
|
|
205
|
+
let report = try JSONDecoder().decode(Self.self, from: JSONSerialization.data(withJSONObject: json))
|
|
206
|
+
try report.validate()
|
|
207
|
+
return report
|
|
208
|
+
} catch let error as ProviderFacadeError { throw error }
|
|
209
|
+
catch { throw ProviderFacadeError.schemaInvalid("capability_schema_invalid: \(error)") }
|
|
201
210
|
}
|
|
202
211
|
|
|
203
|
-
func
|
|
204
|
-
guard
|
|
205
|
-
|
|
206
|
-
|
|
212
|
+
private func validate() throws {
|
|
213
|
+
guard schema == "sks.desktop-capabilities.v3", !reportId.isEmpty, !correlationId.isEmpty, !sessionId.isEmpty,
|
|
214
|
+
["shallow", "transport", "deep"].contains(requestedLevel),
|
|
215
|
+
providers["codex-lb"] != nil, providers["openrouter"] != nil,
|
|
216
|
+
bridge.scope == .bridge, nativeIdentity.scope == .nativeIdentity,
|
|
217
|
+
providers["codex-lb"]?.scope == .codexLb, providers["openrouter"]?.scope == .openRouter,
|
|
218
|
+
combinedCatalog.scope == .combinedCatalog else {
|
|
219
|
+
throw ProviderFacadeError.schemaInvalid("capability_schema_invalid")
|
|
220
|
+
}
|
|
221
|
+
guard catalogSync.schema == "sks.combined-catalog-sync.v1",
|
|
222
|
+
catalogSync.providers["codex-lb"]?.schema == "sks.catalog-sync-state.v2",
|
|
223
|
+
catalogSync.providers["openrouter"]?.schema == "sks.catalog-sync-state.v2" else {
|
|
224
|
+
throw ProviderFacadeError.schemaInvalid("capability_schema_invalid: catalog_sync invalid")
|
|
225
|
+
}
|
|
226
|
+
for scope in scopeSummaries {
|
|
227
|
+
guard scope.schema == "sks.scope-capability-summary.v1" else { throw ProviderFacadeError.schemaInvalid("capability_schema_invalid: scope summary invalid") }
|
|
228
|
+
for (key, probe) in scope.capabilities {
|
|
229
|
+
guard probe.schema == "sks.capability-probe.v3", probe.scope == scope.scope,
|
|
230
|
+
probe.capability == key, probe.reportId == reportId,
|
|
231
|
+
probe.correlationId == correlationId, probe.sessionId == sessionId, probe.attemptId >= 0 else {
|
|
232
|
+
throw ProviderFacadeError.schemaInvalid("capability_schema_invalid: probe identity invalid")
|
|
233
|
+
}
|
|
207
234
|
}
|
|
208
|
-
return
|
|
209
235
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
case .unverified:
|
|
217
|
-
ControlKit.setBadge(activeProviderBadge, text: "Codex LB · unverified · \(describeRoutingTruth(route))", tone: .warning)
|
|
218
|
-
case .inactive:
|
|
219
|
-
break
|
|
236
|
+
let correlations = Set(allProbes.map(\.correlationId))
|
|
237
|
+
guard correlations.count == 1 else {
|
|
238
|
+
throw ProviderFacadeError.schemaInvalid("capability_schema_invalid: correlation mismatch")
|
|
239
|
+
}
|
|
240
|
+
guard catalogGeneration == catalogSync.generation else {
|
|
241
|
+
throw ProviderFacadeError.schemaInvalid("capability_schema_invalid: catalog generation mismatch")
|
|
220
242
|
}
|
|
221
243
|
}
|
|
222
244
|
}
|
|
223
245
|
|
|
224
|
-
struct
|
|
225
|
-
static let
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
let inputTokens: Int
|
|
235
|
-
let outputTokens: Int
|
|
236
|
-
let totalTokens: Int
|
|
237
|
-
let httpStatus: Int
|
|
246
|
+
struct DesktopBridgeStatusV3Truth {
|
|
247
|
+
private static let keys: Set<String> = [
|
|
248
|
+
"schema", "checked_at", "correlation_id", "management", "service",
|
|
249
|
+
"http_probe", "websocket_probe", "native_identity", "providers", "routing",
|
|
250
|
+
"catalog_sync", "capabilities", "readiness", "recovery_actions"
|
|
251
|
+
]
|
|
252
|
+
let raw: [String: Any]
|
|
253
|
+
let checkedAt: String
|
|
254
|
+
let correlationId: String
|
|
255
|
+
let capabilities: DesktopCapabilityReportV3?
|
|
238
256
|
|
|
239
|
-
|
|
240
|
-
|
|
257
|
+
static func decode(from json: [String: Any]) throws -> DesktopBridgeStatusV3Truth {
|
|
258
|
+
guard Set(json.keys) == keys,
|
|
259
|
+
json["schema"] as? String == "sks.desktop-bridge-status.v3",
|
|
260
|
+
let checkedAt = nonempty(json["checked_at"]), let correlationId = nonempty(json["correlation_id"]),
|
|
261
|
+
let management = json["management"] as? [String: Any],
|
|
262
|
+
let service = json["service"] as? [String: Any],
|
|
263
|
+
let identity = json["native_identity"] as? [String: Any],
|
|
264
|
+
let providers = json["providers"] as? [String: Any],
|
|
265
|
+
let routing = json["routing"] as? [String: Any],
|
|
266
|
+
let catalog = json["catalog_sync"] as? [String: Any],
|
|
267
|
+
let readiness = json["readiness"] as? [String: Any],
|
|
268
|
+
json["recovery_actions"] is [String] else {
|
|
269
|
+
throw ProviderFacadeError.schemaInvalid("desktop_bridge_status_schema_invalid")
|
|
241
270
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
model: model,
|
|
257
|
-
latencyMs: latencyMs,
|
|
258
|
-
responseId: responseId,
|
|
259
|
-
reply: boundedReply(rawReply),
|
|
260
|
-
inputTokens: inputTokens,
|
|
261
|
-
outputTokens: outputTokens,
|
|
262
|
-
totalTokens: totalTokens,
|
|
263
|
-
httpStatus: httpStatus
|
|
264
|
-
)
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
static func validationFailure(from json: [String: Any]) -> String {
|
|
268
|
-
guard json["schema"] as? String == schema else { return "unexpected or missing schema" }
|
|
269
|
-
guard json["ok"] as? Bool == true else { return publicFailure(from: json) }
|
|
270
|
-
guard json["status"] as? String == "connected" else { return "status is not connected" }
|
|
271
|
-
guard nonemptyString(json["model"]) != nil else { return "model evidence is missing" }
|
|
272
|
-
guard let latencyMs = integer(json["latency_ms"]), latencyMs >= 0 else { return "latency evidence is invalid" }
|
|
273
|
-
guard nonemptyString(json["response_id"]) != nil else { return "response id is missing" }
|
|
274
|
-
guard nonemptyString(json["result"]) != nil else { return "returned reply is empty" }
|
|
275
|
-
guard json["result_truncated"] as? Bool != nil else { return "result bound evidence is missing" }
|
|
276
|
-
guard let httpStatus = integer(json["http_status"]), (200..<300).contains(httpStatus) else {
|
|
277
|
-
return "HTTP success evidence is invalid"
|
|
271
|
+
let managed = management["managed"] as? Bool == true
|
|
272
|
+
let managementValid = managed
|
|
273
|
+
? management["runtime"] as? String == "desktop-bridge" && management["reason"] is NSNull
|
|
274
|
+
: management["runtime"] is NSNull && ["not_installed", "stopped"].contains(management["state"] as? String ?? "")
|
|
275
|
+
guard managementValid,
|
|
276
|
+
service["installed"] is Bool, service["loaded"] is Bool, service["running"] is Bool,
|
|
277
|
+
nonempty(service["checked_at"]) != nil, identity["configured"] is Bool,
|
|
278
|
+
routing["fallback"] as? String == "none", readiness["ready"] is Bool,
|
|
279
|
+
["ready", "awaiting_provider", "degraded", "blocked", "unmanaged"].contains(readiness["state"] as? String ?? ""),
|
|
280
|
+
profileValid(providers["codex-lb"], id: "codex-lb"),
|
|
281
|
+
profileValid(providers["openrouter"], id: "openrouter"),
|
|
282
|
+
transportProbeValid(json["http_probe"], websocket: false),
|
|
283
|
+
transportProbeValid(json["websocket_probe"], websocket: true) else {
|
|
284
|
+
throw ProviderFacadeError.schemaInvalid("desktop_bridge_status_schema_invalid")
|
|
278
285
|
}
|
|
279
|
-
|
|
280
|
-
|
|
286
|
+
if let policy = routing["policy"] as? [String: Any] {
|
|
287
|
+
guard policy["schema"] as? String == "sks.bridge-routing-policy.v1", policy["fallback"] as? String == "none" else {
|
|
288
|
+
throw ProviderFacadeError.schemaInvalid("desktop_bridge_status_schema_invalid: routing policy")
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
guard let data = try? JSONSerialization.data(withJSONObject: catalog),
|
|
292
|
+
let decodedCatalog = try? JSONDecoder().decode(CombinedCatalogSyncStatusV1.self, from: data),
|
|
293
|
+
decodedCatalog.schema == "sks.combined-catalog-sync.v1" else {
|
|
294
|
+
throw ProviderFacadeError.schemaInvalid("desktop_bridge_status_schema_invalid: catalog_sync")
|
|
281
295
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
return "none"
|
|
296
|
+
let report: DesktopCapabilityReportV3?
|
|
297
|
+
if json["capabilities"] is NSNull || json["capabilities"] == nil { report = nil }
|
|
298
|
+
else if let capabilities = json["capabilities"] as? [String: Any] {
|
|
299
|
+
report = try DesktopCapabilityReportV3.decode(from: capabilities)
|
|
300
|
+
} else { throw ProviderFacadeError.schemaInvalid("desktop_bridge_status_schema_invalid: capabilities") }
|
|
301
|
+
return DesktopBridgeStatusV3Truth(raw: json, checkedAt: checkedAt, correlationId: correlationId, capabilities: report)
|
|
289
302
|
}
|
|
290
303
|
|
|
291
|
-
static func
|
|
292
|
-
|
|
293
|
-
guard
|
|
294
|
-
|
|
304
|
+
private static func transportProbeValid(_ value: Any?, websocket: Bool) -> Bool {
|
|
305
|
+
if value is NSNull { return true }
|
|
306
|
+
guard let probe = value as? [String: Any] else { return false }
|
|
307
|
+
let expected: Set<String> = websocket
|
|
308
|
+
? ["schema", "state", "terminal_stage", "root_cause", "status_code", "negotiated_protocol",
|
|
309
|
+
"upgrade_verified", "protocol_verified", "frame_round_trip_verified", "clean_close_verified",
|
|
310
|
+
"latency_ms", "blockers", "warnings"]
|
|
311
|
+
: ["schema", "state", "terminal_stage", "root_cause", "status_code", "latency_ms", "blockers", "warnings"]
|
|
312
|
+
guard Set(probe.keys) == expected,
|
|
313
|
+
probe["schema"] as? String == (websocket ? "sks.desktop-bridge-websocket-probe.v2" : "sks.desktop-bridge-http-probe.v1"),
|
|
314
|
+
let state = probe["state"] as? String,
|
|
315
|
+
let stage = probe["terminal_stage"] as? String,
|
|
316
|
+
probe["root_cause"] is NSNull || probe["root_cause"] is String,
|
|
317
|
+
probe["status_code"] is NSNull || probe["status_code"] is NSNumber,
|
|
318
|
+
probe["latency_ms"] is NSNull || probe["latency_ms"] is NSNumber,
|
|
319
|
+
probe["blockers"] is [String], probe["warnings"] is [String] else { return false }
|
|
320
|
+
if websocket {
|
|
321
|
+
guard ["not_attempted", "verified", "degraded", "blocked", "failed", "unsupported"].contains(state),
|
|
322
|
+
["tcp_connect", "websocket_upgrade", "websocket_protocol", "frame_round_trip", "clean_close", "complete"].contains(stage),
|
|
323
|
+
probe["negotiated_protocol"] is NSNull || probe["negotiated_protocol"] is String,
|
|
324
|
+
probe["upgrade_verified"] is Bool, probe["protocol_verified"] is Bool,
|
|
325
|
+
probe["frame_round_trip_verified"] is Bool, probe["clean_close_verified"] is Bool else { return false }
|
|
326
|
+
if state == "verified" {
|
|
327
|
+
return stage == "complete" && probe["root_cause"] is NSNull
|
|
328
|
+
&& probe["upgrade_verified"] as? Bool == true
|
|
329
|
+
&& probe["protocol_verified"] as? Bool == true
|
|
330
|
+
&& probe["frame_round_trip_verified"] as? Bool == true
|
|
331
|
+
&& probe["clean_close_verified"] as? Bool == true
|
|
332
|
+
}
|
|
333
|
+
return state != "not_attempted"
|
|
334
|
+
|| probe["root_cause"] is NSNull && (probe["blockers"] as? [String])?.isEmpty == true
|
|
335
|
+
}
|
|
336
|
+
guard ["verified", "blocked", "failed", "unsupported"].contains(state),
|
|
337
|
+
["tcp_connect", "http_health", "complete"].contains(stage) else { return false }
|
|
338
|
+
return state != "verified" || stage == "complete" && probe["root_cause"] is NSNull
|
|
295
339
|
}
|
|
296
340
|
|
|
297
|
-
private static func
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
return "
|
|
341
|
+
private static func profileValid(_ value: Any?, id: String) -> Bool {
|
|
342
|
+
guard let profile = value as? [String: Any], profile["schema"] as? String == "sks.bridge-provider-profile-status.v1",
|
|
343
|
+
profile["provider_id"] as? String == id, profile["enabled"] is Bool,
|
|
344
|
+
profile["credential"] is [String: Any], profile["endpoint"] is [String: Any],
|
|
345
|
+
let catalog = profile["catalog"] as? [String: Any],
|
|
346
|
+
let capabilities = profile["capabilities"] as? [String: Any] else { return false }
|
|
347
|
+
return catalog["schema"] as? String == "sks.catalog-sync-state.v2"
|
|
348
|
+
&& capabilities["schema"] as? String == "sks.scope-capability-summary.v1"
|
|
349
|
+
&& capabilities["scope"] as? String == "provider:\(id)"
|
|
304
350
|
}
|
|
305
351
|
|
|
306
|
-
private static func
|
|
307
|
-
guard let
|
|
308
|
-
let trimmed =
|
|
352
|
+
private static func nonempty(_ value: Any?) -> String? {
|
|
353
|
+
guard let string = value as? String else { return nil }
|
|
354
|
+
let trimmed = string.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
309
355
|
return trimmed.isEmpty ? nil : trimmed
|
|
310
356
|
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
struct CapabilityDisplayRow: Equatable {
|
|
360
|
+
let scope: CapabilityScope
|
|
361
|
+
let capability: String
|
|
362
|
+
let state: CapabilityProbeState
|
|
363
|
+
let route: String
|
|
364
|
+
let oauthRequirement: String
|
|
365
|
+
let checkedAt: String
|
|
366
|
+
let stage: CapabilityProbeStage
|
|
367
|
+
let rootCause: String?
|
|
368
|
+
let recoveryAction: String?
|
|
369
|
+
|
|
370
|
+
static func rows(from report: DesktopCapabilityReportV3) -> [CapabilityDisplayRow] {
|
|
371
|
+
report.scopeSummaries.flatMap { summary in
|
|
372
|
+
summary.capabilities.keys.sorted().compactMap { key in
|
|
373
|
+
guard let probe = summary.capabilities[key] else { return nil }
|
|
374
|
+
let route = evidenceString(probe.evidence["route"]) ?? evidenceString(probe.evidence["provider_route"]) ?? "none"
|
|
375
|
+
let oauth = evidenceString(probe.evidence["oauth_requirement"]) ?? (summary.scope == .nativeIdentity ? "required" : "not required")
|
|
376
|
+
return CapabilityDisplayRow(scope: summary.scope, capability: key, state: probe.state, route: route,
|
|
377
|
+
oauthRequirement: oauth, checkedAt: probe.checkedAt, stage: probe.stage,
|
|
378
|
+
rootCause: probe.rootCause, recoveryAction: probe.recoveryAction)
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
311
382
|
|
|
312
|
-
private static func
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
guard double.isFinite,
|
|
316
|
-
double.rounded(.towardZero) == double,
|
|
317
|
-
double >= Double(Int.min),
|
|
318
|
-
double <= Double(Int.max) else { return nil }
|
|
319
|
-
return number.intValue
|
|
383
|
+
private static func evidenceString(_ value: JSONValue?) -> String? {
|
|
384
|
+
if case .string(let raw)? = value { return raw }
|
|
385
|
+
return nil
|
|
320
386
|
}
|
|
321
387
|
}
|