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,26 +1,108 @@
|
|
|
1
|
-
import { randomInt } from 'node:crypto';
|
|
1
|
+
import { createHash, randomInt, timingSafeEqual } from 'node:crypto';
|
|
2
2
|
import http, {} from 'node:http';
|
|
3
3
|
import net, {} from 'node:net';
|
|
4
|
-
import { forwardHttp,
|
|
5
|
-
import { assertAllowedOrigin, assertAllowedPath,
|
|
6
|
-
import { createDesktopBridgePublicState, desktopBridgeStatePath, removeDesktopBridgeStateIfOwned, writeDesktopBridgeState, } from './state.js';
|
|
4
|
+
import { forwardHttp, prepareDesktopBridgeRequest } from './http-forward.js';
|
|
5
|
+
import { assertAllowedOrigin, assertAllowedPath, assertLoopbackPeer, assertLoopbackListenHost, assertWebSocketUpgrade, prepareDesktopBridgeConfig, safeBridgeErrorCode, validatePreparedDesktopBridgeConfig, } from './security.js';
|
|
6
|
+
import { createDesktopBridgePublicState, desktopBridgeListenOrigin, desktopBridgeStatePath, refreshDesktopBridgeState, removeDesktopBridgeStateIfOwned, writeDesktopBridgeState, } from './state.js';
|
|
7
7
|
import { DesktopBridgeError } from './types.js';
|
|
8
|
+
import { DESKTOP_BRIDGE_CLIENT_PATH_PREFIX, DESKTOP_BRIDGE_DIAGNOSTIC_HEALTH_PATH, DESKTOP_BRIDGE_DIAGNOSTIC_PATH, DESKTOP_BRIDGE_DIAGNOSTIC_PROTOCOL } from './types.js';
|
|
8
9
|
import { forwardWebSocket } from './websocket-forward.js';
|
|
9
|
-
function
|
|
10
|
+
function authenticateDesktopBridgeClient(req, input) {
|
|
11
|
+
const raw = String(req.url || '/');
|
|
12
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(raw) || raw.startsWith('//') || /[\r\n\0]/.test(raw)) {
|
|
13
|
+
throw new DesktopBridgeError('bridge_request_target_invalid');
|
|
14
|
+
}
|
|
15
|
+
let parsed;
|
|
10
16
|
try {
|
|
11
|
-
|
|
17
|
+
parsed = new URL(raw, 'http://bridge.invalid');
|
|
12
18
|
}
|
|
13
19
|
catch {
|
|
14
20
|
throw new DesktopBridgeError('bridge_request_target_invalid');
|
|
15
21
|
}
|
|
22
|
+
const tokenPrefix = `${DESKTOP_BRIDGE_CLIENT_PATH_PREFIX}/`;
|
|
23
|
+
if (!parsed.pathname.startsWith(tokenPrefix))
|
|
24
|
+
throw new DesktopBridgeError('bridge_client_capability_required');
|
|
25
|
+
const remainder = parsed.pathname.slice(tokenPrefix.length);
|
|
26
|
+
const separator = remainder.indexOf('/');
|
|
27
|
+
const capability = separator > 0 ? remainder.slice(0, separator) : '';
|
|
28
|
+
const canonicalPathname = separator > 0 ? remainder.slice(separator) : '';
|
|
29
|
+
if (!/^[A-Za-z0-9_-]{43}$/.test(capability) || !canonicalPathname.startsWith('/')) {
|
|
30
|
+
throw new DesktopBridgeError('bridge_client_capability_invalid');
|
|
31
|
+
}
|
|
32
|
+
const actual = createHash('sha256').update(capability).digest();
|
|
33
|
+
const expected = Buffer.from(input.clientCapabilitySha256, 'hex');
|
|
34
|
+
if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) {
|
|
35
|
+
throw new DesktopBridgeError('bridge_client_capability_invalid');
|
|
36
|
+
}
|
|
37
|
+
req.url = `${canonicalPathname}${parsed.search}`;
|
|
38
|
+
return {
|
|
39
|
+
pathname: canonicalPathname,
|
|
40
|
+
clientBasePath: `${DESKTOP_BRIDGE_CLIENT_PATH_PREFIX}/${capability}`,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function handleDiagnosticWebSocket(req, socket, head) {
|
|
44
|
+
const protocols = String(req.headers['sec-websocket-protocol'] || '').split(',').map((value) => value.trim());
|
|
45
|
+
if (!protocols.includes(DESKTOP_BRIDGE_DIAGNOSTIC_PROTOCOL))
|
|
46
|
+
throw new DesktopBridgeError('bridge_websocket_protocol_mismatch');
|
|
47
|
+
const key = String(req.headers['sec-websocket-key'] || '');
|
|
48
|
+
const accept = createHash('sha1').update(`${key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11`).digest('base64');
|
|
49
|
+
socket.write('HTTP/1.1 101 Switching Protocols\r\n'
|
|
50
|
+
+ 'Upgrade: websocket\r\nConnection: Upgrade\r\n'
|
|
51
|
+
+ `Sec-WebSocket-Accept: ${accept}\r\n`
|
|
52
|
+
+ `Sec-WebSocket-Protocol: ${DESKTOP_BRIDGE_DIAGNOSTIC_PROTOCOL}\r\n\r\n`);
|
|
53
|
+
let buffered = head;
|
|
54
|
+
const consume = (chunk) => {
|
|
55
|
+
if (chunk)
|
|
56
|
+
buffered = Buffer.concat([buffered, chunk]);
|
|
57
|
+
while (buffered.length >= 6) {
|
|
58
|
+
const opcode = buffered[0] & 0x0f;
|
|
59
|
+
const masked = (buffered[1] & 0x80) !== 0;
|
|
60
|
+
const length = buffered[1] & 0x7f;
|
|
61
|
+
if (!masked || length > 125) {
|
|
62
|
+
socket.destroy();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (buffered.length < 6 + length)
|
|
66
|
+
return;
|
|
67
|
+
const mask = buffered.subarray(2, 6);
|
|
68
|
+
const raw = buffered.subarray(6, 6 + length);
|
|
69
|
+
const payload = Buffer.alloc(length);
|
|
70
|
+
for (let index = 0; index < length; index += 1)
|
|
71
|
+
payload[index] = (raw[index] || 0) ^ (mask[index % 4] || 0);
|
|
72
|
+
buffered = buffered.subarray(6 + length);
|
|
73
|
+
if (opcode === 1 || opcode === 2)
|
|
74
|
+
socket.write(Buffer.concat([Buffer.from([0x80 | opcode, length]), payload]));
|
|
75
|
+
else if (opcode === 8) {
|
|
76
|
+
socket.end(Buffer.concat([Buffer.from([0x88, payload.length]), payload]));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
socket.on('data', consume);
|
|
82
|
+
consume();
|
|
16
83
|
}
|
|
17
84
|
function rejectionStatus(code) {
|
|
18
85
|
if (code === 'bridge_path_not_allowed')
|
|
19
86
|
return 404;
|
|
20
|
-
if (code
|
|
87
|
+
if (code === 'bridge_request_capacity_exhausted')
|
|
88
|
+
return 503;
|
|
89
|
+
if (code === 'bridge_session_pin_persist_failed' || code.startsWith('bridge_upstream_'))
|
|
90
|
+
return 502;
|
|
91
|
+
if (code.includes('origin') || code.includes('peer') || code.includes('loopback') || code.includes('capability'))
|
|
21
92
|
return 403;
|
|
22
93
|
return 400;
|
|
23
94
|
}
|
|
95
|
+
function rejectionStatusText(status) {
|
|
96
|
+
if (status === 404)
|
|
97
|
+
return 'Not Found';
|
|
98
|
+
if (status === 403)
|
|
99
|
+
return 'Forbidden';
|
|
100
|
+
if (status === 502)
|
|
101
|
+
return 'Bad Gateway';
|
|
102
|
+
if (status === 503)
|
|
103
|
+
return 'Service Unavailable';
|
|
104
|
+
return 'Bad Request';
|
|
105
|
+
}
|
|
24
106
|
function writeBridgeRejection(res, error) {
|
|
25
107
|
if (res.headersSent) {
|
|
26
108
|
res.destroy(error instanceof Error ? error : undefined);
|
|
@@ -39,13 +121,32 @@ function writeUpgradeRejection(socket, error) {
|
|
|
39
121
|
return;
|
|
40
122
|
const code = safeBridgeErrorCode(error);
|
|
41
123
|
const status = rejectionStatus(code);
|
|
42
|
-
socket.end(`HTTP/1.1 ${status} ${status
|
|
124
|
+
socket.end(`HTTP/1.1 ${status} ${rejectionStatusText(status)}\r\n`
|
|
43
125
|
+ 'Content-Type: application/json\r\n'
|
|
44
126
|
+ 'Cache-Control: no-store\r\n'
|
|
45
127
|
+ 'Connection: close\r\n'
|
|
46
128
|
+ '\r\n'
|
|
47
129
|
+ JSON.stringify({ error: { type: 'sks_bridge_rejection', code, message: code } }));
|
|
48
130
|
}
|
|
131
|
+
function writeDiagnosticHealth(req, res, input) {
|
|
132
|
+
if (req.method !== 'GET')
|
|
133
|
+
throw new DesktopBridgeError('bridge_diagnostic_method_not_allowed');
|
|
134
|
+
const payload = {
|
|
135
|
+
schema: 'sks.desktop-bridge-health.v1',
|
|
136
|
+
runtime: 'desktop-bridge',
|
|
137
|
+
state: 'ready',
|
|
138
|
+
provider_registry_generation: input.providerRegistry?.generation || null,
|
|
139
|
+
route_policy_generation: input.routePolicy?.policy_generation || null,
|
|
140
|
+
catalog_generation: input.routePolicy?.catalog_generation || null,
|
|
141
|
+
secret_fields_redacted: true
|
|
142
|
+
};
|
|
143
|
+
res.writeHead(200, {
|
|
144
|
+
'content-type': 'application/json',
|
|
145
|
+
'cache-control': 'no-store',
|
|
146
|
+
connection: 'close'
|
|
147
|
+
});
|
|
148
|
+
res.end(JSON.stringify(payload));
|
|
149
|
+
}
|
|
49
150
|
async function listenExact(server, host, port) {
|
|
50
151
|
await new Promise((resolve, reject) => {
|
|
51
152
|
const onError = (error) => {
|
|
@@ -101,23 +202,43 @@ export const preflightDesktopBridge = prepareDesktopBridgeConfig;
|
|
|
101
202
|
export async function startPreparedDesktopBridge(input, options = {}) {
|
|
102
203
|
validatePreparedDesktopBridgeConfig(input);
|
|
103
204
|
const sockets = new Set();
|
|
205
|
+
const maxConcurrentRequests = input.maxConcurrentRequests ?? 64;
|
|
206
|
+
let activeRequests = 0;
|
|
104
207
|
const server = http.createServer((req, res) => {
|
|
105
208
|
void (async () => {
|
|
209
|
+
let admitted = false;
|
|
106
210
|
try {
|
|
211
|
+
if (activeRequests >= maxConcurrentRequests)
|
|
212
|
+
throw new DesktopBridgeError('bridge_request_capacity_exhausted');
|
|
213
|
+
activeRequests += 1;
|
|
214
|
+
admitted = true;
|
|
107
215
|
assertLoopbackPeer(req.socket.remoteAddress);
|
|
216
|
+
const authenticated = authenticateDesktopBridgeClient(req, input);
|
|
108
217
|
assertAllowedOrigin(req.headers, input.allowedOrigins);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
218
|
+
if (authenticated.pathname === DESKTOP_BRIDGE_DIAGNOSTIC_HEALTH_PATH) {
|
|
219
|
+
writeDiagnosticHealth(req, res, input);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
assertAllowedPath(authenticated.pathname, input.allowedPathPrefixes);
|
|
223
|
+
const prepared = await prepareDesktopBridgeRequest(req, input);
|
|
224
|
+
await forwardHttp(req, res, input, prepared, `${desktopBridgeListenOrigin(input)}${authenticated.clientBasePath}`);
|
|
113
225
|
}
|
|
114
226
|
catch (error) {
|
|
115
|
-
req.
|
|
227
|
+
req.pause();
|
|
228
|
+
res.once('finish', () => {
|
|
229
|
+
if (!req.complete)
|
|
230
|
+
req.destroy();
|
|
231
|
+
});
|
|
116
232
|
writeBridgeRejection(res, error);
|
|
117
233
|
}
|
|
234
|
+
finally {
|
|
235
|
+
if (admitted)
|
|
236
|
+
activeRequests -= 1;
|
|
237
|
+
}
|
|
118
238
|
})();
|
|
119
239
|
});
|
|
120
|
-
server.
|
|
240
|
+
server.maxConnections = input.maxConnections ?? 128;
|
|
241
|
+
server.requestTimeout = input.requestTimeoutMs ?? 30_000;
|
|
121
242
|
server.headersTimeout = Math.max(5_000, input.connectTimeoutMs);
|
|
122
243
|
server.keepAliveTimeout = Math.min(input.idleTimeoutMs, 60_000);
|
|
123
244
|
server.on('connection', (socket) => {
|
|
@@ -131,14 +252,15 @@ export async function startPreparedDesktopBridge(input, options = {}) {
|
|
|
131
252
|
server.on('upgrade', (req, socket, head) => {
|
|
132
253
|
try {
|
|
133
254
|
assertLoopbackPeer(req.socket.remoteAddress);
|
|
255
|
+
const authenticated = authenticateDesktopBridgeClient(req, input);
|
|
134
256
|
assertAllowedOrigin(req.headers, input.allowedOrigins);
|
|
135
|
-
assertAllowedPath(pathnameFromRequest(req), input.allowedPathPrefixes);
|
|
136
|
-
assertDesktopBridgeRequestPolicy({ headers: req.headers, config: input });
|
|
137
257
|
assertWebSocketUpgrade(req.headers, req.method);
|
|
138
|
-
if (
|
|
139
|
-
|
|
258
|
+
if (authenticated.pathname === DESKTOP_BRIDGE_DIAGNOSTIC_PATH) {
|
|
259
|
+
handleDiagnosticWebSocket(req, socket, head);
|
|
260
|
+
return;
|
|
140
261
|
}
|
|
141
|
-
|
|
262
|
+
assertAllowedPath(authenticated.pathname, input.allowedPathPrefixes);
|
|
263
|
+
void forwardWebSocket(req, socket, head, input, `${desktopBridgeListenOrigin(input)}${authenticated.clientBasePath}`).catch((error) => writeUpgradeRejection(socket, error));
|
|
142
264
|
}
|
|
143
265
|
catch (error) {
|
|
144
266
|
writeUpgradeRejection(socket, error);
|
|
@@ -159,6 +281,11 @@ export async function startPreparedDesktopBridge(input, options = {}) {
|
|
|
159
281
|
await closeServer(server, sockets);
|
|
160
282
|
throw error;
|
|
161
283
|
}
|
|
284
|
+
const freshnessMs = input.stateFreshnessMs ?? 5 * 60_000;
|
|
285
|
+
const heartbeat = statePath ? setInterval(() => {
|
|
286
|
+
void refreshDesktopBridgeState(statePath, state, new Date(), freshnessMs).catch(() => undefined);
|
|
287
|
+
}, Math.max(1_000, Math.floor(freshnessMs / 3))) : null;
|
|
288
|
+
heartbeat?.unref();
|
|
162
289
|
let stopped = false;
|
|
163
290
|
const handle = {
|
|
164
291
|
server,
|
|
@@ -169,6 +296,8 @@ export async function startPreparedDesktopBridge(input, options = {}) {
|
|
|
169
296
|
if (stopped)
|
|
170
297
|
return;
|
|
171
298
|
stopped = true;
|
|
299
|
+
if (heartbeat)
|
|
300
|
+
clearInterval(heartbeat);
|
|
172
301
|
await closeServer(server, sockets);
|
|
173
302
|
if (statePath)
|
|
174
303
|
await removeDesktopBridgeStateIfOwned(statePath, state);
|
|
@@ -3,85 +3,73 @@ import fsp from 'node:fs/promises';
|
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { DESKTOP_BRIDGE_STATE_SCHEMA, DesktopBridgeError } from './types.js';
|
|
6
|
+
const DEFAULT_FRESHNESS_MS = 5 * 60_000;
|
|
6
7
|
export function desktopBridgeStatePath(home = os.homedir()) {
|
|
7
|
-
return path.join(home, '.codex', 'sks', '
|
|
8
|
-
}
|
|
9
|
-
export function sha256Hex(value) {
|
|
10
|
-
return createHash('sha256').update(value).digest('hex');
|
|
8
|
+
return path.join(home, '.codex', 'sks', 'desktop-bridge-state.json');
|
|
11
9
|
}
|
|
10
|
+
export function sha256Hex(value) { return createHash('sha256').update(value).digest('hex'); }
|
|
12
11
|
export function desktopBridgeListenOrigin(config) {
|
|
13
|
-
|
|
14
|
-
return `http://${host}:${config.listenPort}`;
|
|
12
|
+
return `http://${config.listenHost === '::1' ? '[::1]' : config.listenHost}:${config.listenPort}`;
|
|
15
13
|
}
|
|
16
14
|
export function desktopBridgeConfigGeneration(config) {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
gatewayAuthTransport: config.gatewayAuthTransport,
|
|
29
|
-
allowedPathPrefixes: [...config.allowedPathPrefixes],
|
|
30
|
-
allowedOrigins: [...config.allowedOrigins],
|
|
31
|
-
connectTimeoutMs: config.connectTimeoutMs,
|
|
32
|
-
idleTimeoutMs: config.idleTimeoutMs,
|
|
33
|
-
});
|
|
34
|
-
return sha256Hex(stable);
|
|
15
|
+
const registry = config.providerRegistry;
|
|
16
|
+
return sha256Hex(JSON.stringify({
|
|
17
|
+
listen_host: config.listenHost, listen_port: config.listenPort,
|
|
18
|
+
provider_registry_generation: registry.generation,
|
|
19
|
+
provider_credential_generations: Object.fromEntries(Object.keys(registry.providers).map((id) => [id, registry.providers[id].credential_generation])),
|
|
20
|
+
route_policy_generation: config.routePolicy.policy_generation,
|
|
21
|
+
catalog_generation: config.routePolicy.catalog_generation,
|
|
22
|
+
client_capability_sha256: config.clientCapabilitySha256,
|
|
23
|
+
allowed_paths: [...config.allowedPathPrefixes], allowed_origins: [...config.allowedOrigins].sort(),
|
|
24
|
+
connect_timeout_ms: config.connectTimeoutMs, idle_timeout_ms: config.idleTimeoutMs,
|
|
25
|
+
}));
|
|
35
26
|
}
|
|
36
27
|
export function createDesktopBridgePublicState(config, options = {}) {
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const
|
|
28
|
+
const now = options.now ?? new Date();
|
|
29
|
+
const registry = config.providerRegistry;
|
|
30
|
+
const enabled = Object.keys(registry.providers).filter((id) => registry.providers[id].enabled);
|
|
31
|
+
const credentialGenerations = Object.fromEntries(Object.keys(registry.providers).map((id) => [id, registry.providers[id].credential_generation]));
|
|
32
|
+
const listen = desktopBridgeListenOrigin(config);
|
|
41
33
|
return {
|
|
42
|
-
schema: DESKTOP_BRIDGE_STATE_SCHEMA,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
listen_origin:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
provider_mode: providerMode,
|
|
50
|
-
allowed_models_sha256: sha256Hex(JSON.stringify(allowedModels)),
|
|
51
|
-
...(config.providerPolicy ? { provider_policy_sha256: sha256Hex(JSON.stringify(config.providerPolicy)) } : {}),
|
|
52
|
-
...(config.childPolicy ? { child_policy_sha256: sha256Hex(JSON.stringify(config.childPolicy)) } : {}),
|
|
53
|
-
session_pin_enforcement: config.requireSessionPin === true ? 'required' : 'compatibility',
|
|
54
|
-
remote_origin_sha256: sha256Hex(remote.origin),
|
|
55
|
-
gateway_auth_transport: config.gatewayAuthTransport,
|
|
56
|
-
config_generation: desktopBridgeConfigGeneration(config),
|
|
34
|
+
schema: DESKTOP_BRIDGE_STATE_SCHEMA, runtime: 'desktop-bridge', pid: options.pid ?? process.pid,
|
|
35
|
+
started_at: now.toISOString(), updated_at: now.toISOString(),
|
|
36
|
+
stale_after: new Date(now.getTime() + (config.stateFreshnessMs ?? DEFAULT_FRESHNESS_MS)).toISOString(),
|
|
37
|
+
listen_origin: listen, codex_base_url: `${listen}/backend-api/codex`, process_generation: randomUUID(),
|
|
38
|
+
provider_registry_generation: registry.generation, route_policy_generation: config.routePolicy.policy_generation,
|
|
39
|
+
catalog_generation: config.routePolicy.catalog_generation, enabled_providers: enabled,
|
|
40
|
+
provider_credential_generations: credentialGenerations, last_verified_probe_ids: [], config_generation: desktopBridgeConfigGeneration(config),
|
|
57
41
|
};
|
|
58
42
|
}
|
|
43
|
+
function isGeneration(value) { return typeof value === 'string' && value.length > 0 && value.length <= 256 && !/[\r\n\0]/.test(value); }
|
|
59
44
|
export function isDesktopBridgePublicState(value) {
|
|
60
45
|
if (!value || typeof value !== 'object')
|
|
61
46
|
return false;
|
|
62
47
|
const row = value;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
&& typeof row.listen_origin === 'string'
|
|
69
|
-
&&
|
|
70
|
-
&&
|
|
71
|
-
&& (row.
|
|
72
|
-
&&
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
48
|
+
if (/"(?:api_?key|secret|authorization|cookie|access_token|refresh_token)"\s*:/i.test(JSON.stringify(row)))
|
|
49
|
+
return false;
|
|
50
|
+
const dates = ['started_at', 'updated_at', 'stale_after'].every((key) => typeof row[key] === 'string' && Number.isFinite(Date.parse(String(row[key]))));
|
|
51
|
+
const credentialRows = row.provider_credential_generations;
|
|
52
|
+
return row.schema === DESKTOP_BRIDGE_STATE_SCHEMA && row.runtime === 'desktop-bridge' && Number.isInteger(row.pid) && Number(row.pid) > 0 && dates
|
|
53
|
+
&& typeof row.listen_origin === 'string' && /^http:\/\/(?:127\.0\.0\.1|\[::1\]):\d+$/.test(row.listen_origin)
|
|
54
|
+
&& row.codex_base_url === `${row.listen_origin}/backend-api/codex` && isGeneration(row.process_generation)
|
|
55
|
+
&& isGeneration(row.provider_registry_generation) && isGeneration(row.route_policy_generation) && isGeneration(row.catalog_generation)
|
|
56
|
+
&& Array.isArray(row.enabled_providers) && row.enabled_providers.every((id) => id === 'codex-lb' || id === 'openrouter')
|
|
57
|
+
&& !!credentialRows && typeof credentialRows === 'object' && isGeneration(credentialRows['codex-lb']) && isGeneration(credentialRows.openrouter)
|
|
58
|
+
&& Array.isArray(row.last_verified_probe_ids) && row.last_verified_probe_ids.every(isGeneration)
|
|
59
|
+
&& typeof row.config_generation === 'string' && /^[a-f0-9]{64}$/.test(row.config_generation);
|
|
60
|
+
}
|
|
61
|
+
export async function refreshDesktopBridgeState(file, state, now = new Date(), freshnessMs = DEFAULT_FRESHNESS_MS) {
|
|
62
|
+
const current = await readDesktopBridgeState(file).catch(() => null);
|
|
63
|
+
if (!current || current.schema !== DESKTOP_BRIDGE_STATE_SCHEMA || current.pid !== state.pid
|
|
64
|
+
|| current.process_generation !== state.process_generation || current.config_generation !== state.config_generation)
|
|
65
|
+
return false;
|
|
66
|
+
state.updated_at = now.toISOString();
|
|
67
|
+
state.stale_after = new Date(now.getTime() + freshnessMs).toISOString();
|
|
68
|
+
await writeDesktopBridgeState(file, state);
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
export function isDesktopBridgeStateFresh(state, now = new Date()) {
|
|
72
|
+
return Date.parse(state.stale_after) > now.getTime();
|
|
85
73
|
}
|
|
86
74
|
export async function writeDesktopBridgeState(file, state) {
|
|
87
75
|
if (!isDesktopBridgePublicState(state))
|
|
@@ -109,7 +97,7 @@ export async function readDesktopBridgeState(file) {
|
|
|
109
97
|
return null;
|
|
110
98
|
throw error;
|
|
111
99
|
}
|
|
112
|
-
if (!stat.isFile())
|
|
100
|
+
if (!stat.isFile() || stat.isSymbolicLink())
|
|
113
101
|
throw new DesktopBridgeError('bridge_state_not_file');
|
|
114
102
|
if ((stat.mode & 0o077) !== 0)
|
|
115
103
|
throw new DesktopBridgeError('bridge_state_permissions_unsafe');
|
|
@@ -131,9 +119,7 @@ export async function removeDesktopBridgeStateIfOwned(file, expected) {
|
|
|
131
119
|
const current = await readDesktopBridgeState(file).catch(() => null);
|
|
132
120
|
if (!current || current.pid !== expected.pid || current.config_generation !== expected.config_generation)
|
|
133
121
|
return false;
|
|
134
|
-
await fsp.unlink(file).catch((error) => {
|
|
135
|
-
|
|
136
|
-
throw error;
|
|
137
|
-
});
|
|
122
|
+
await fsp.unlink(file).catch((error) => { if (error.code !== 'ENOENT')
|
|
123
|
+
throw error; });
|
|
138
124
|
return true;
|
|
139
125
|
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
export const DESKTOP_BRIDGE_STATE_SCHEMA = 'sks.
|
|
1
|
+
export const DESKTOP_BRIDGE_STATE_SCHEMA = 'sks.desktop-bridge-state.v2';
|
|
2
|
+
export const DESKTOP_BRIDGE_REGISTRY_SCHEMA = 'sks.desktop-bridge-provider-registry.v1';
|
|
3
|
+
export const DESKTOP_BRIDGE_DIAGNOSTIC_HEALTH_PATH = '/__sks/diagnostics/health';
|
|
4
|
+
export const DESKTOP_BRIDGE_DIAGNOSTIC_PATH = '/__sks/diagnostics/websocket';
|
|
5
|
+
export const DESKTOP_BRIDGE_DIAGNOSTIC_PROTOCOL = 'sks.desktop-bridge.probe.v2';
|
|
6
|
+
export const DESKTOP_BRIDGE_CLIENT_PATH_PREFIX = '/__sks/client';
|
|
2
7
|
export const DESKTOP_BRIDGE_ALLOWED_PATH_PREFIXES = [
|
|
3
8
|
'/backend-api/codex/',
|
|
4
9
|
'/backend-api/files',
|