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
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { BRIDGE_ACTIVE_GENERATION_FILENAME, BRIDGE_ACTIVE_GENERATION_SCHEMA } from './contracts.js';
|
|
5
|
+
import { safeErrorCode, unique } from './shared.js';
|
|
6
|
+
import { assertPrivateDirectory, catalogObservationGeneration, emptyCatalog, emptyRouteIndex, fsyncDirectory, generationBundleLayout, isPrivateRegular, readVerifiedPair, restoreSnapshot, snapshot, stagePrivateFile, verifyStagedPair } from './storage.js';
|
|
7
|
+
import { writeTextAtomic } from '../../fsx.js';
|
|
8
|
+
import { withFileLock } from '../../locks/file-lock.js';
|
|
9
|
+
export async function stageCombinedBridgeCatalog(input) {
|
|
10
|
+
const previous = await readActiveCombinedBridgeCatalog(input.catalogPath, input.routeIndexPath);
|
|
11
|
+
const layout = generationBundleLayout(input.catalogPath, input.routeIndexPath, input.build);
|
|
12
|
+
if (!layout.ok) {
|
|
13
|
+
return stagingFailure(layout.pointerPath, previous, layout.blockers);
|
|
14
|
+
}
|
|
15
|
+
if (!input.build.ok) {
|
|
16
|
+
return stagingFailure(layout.pointerPath, previous, unique(input.build.blockers.length > 0
|
|
17
|
+
? input.build.blockers
|
|
18
|
+
: ['combined_catalog_build_not_verified']));
|
|
19
|
+
}
|
|
20
|
+
const catalogText = `${JSON.stringify(input.build.catalog, null, 2)}\n`;
|
|
21
|
+
const routeText = `${JSON.stringify(input.build.route_index, null, 2)}\n`;
|
|
22
|
+
const tempBundle = `${layout.bundleDirectory}.${process.pid}.${crypto.randomBytes(6).toString('hex')}.tmp`;
|
|
23
|
+
const tempCatalog = path.join(tempBundle, layout.catalogFilename);
|
|
24
|
+
const tempRouteIndex = path.join(tempBundle, layout.routeIndexFilename);
|
|
25
|
+
try {
|
|
26
|
+
await fs.mkdir(layout.generationsRoot, { recursive: true, mode: 0o700 });
|
|
27
|
+
await assertPrivateDirectory(layout.generationsRoot);
|
|
28
|
+
await fs.mkdir(tempBundle, { recursive: false, mode: 0o700 });
|
|
29
|
+
await Promise.all([
|
|
30
|
+
stagePrivateFile(tempCatalog, catalogText),
|
|
31
|
+
stagePrivateFile(tempRouteIndex, routeText)
|
|
32
|
+
]);
|
|
33
|
+
await verifyStagedPair(tempCatalog, tempRouteIndex, input.build.catalog.generation, input.build.route_index.generation);
|
|
34
|
+
await fsyncDirectory(tempBundle);
|
|
35
|
+
const existingBundle = await fs.lstat(layout.bundleDirectory).catch(() => null);
|
|
36
|
+
if (existingBundle) {
|
|
37
|
+
if (!existingBundle.isDirectory() || existingBundle.isSymbolicLink()) {
|
|
38
|
+
throw new Error('combined_catalog_generation_bundle_invalid');
|
|
39
|
+
}
|
|
40
|
+
await assertPrivateDirectory(layout.bundleDirectory);
|
|
41
|
+
const existing = await readVerifiedPair(layout.catalogPath, layout.routeIndexPath);
|
|
42
|
+
if (existing.catalog.generation !== input.build.catalog.generation
|
|
43
|
+
|| existing.route_index.generation !== input.build.route_index.generation) {
|
|
44
|
+
throw new Error('combined_catalog_generation_bundle_mismatch');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
await fs.rename(tempBundle, layout.bundleDirectory);
|
|
49
|
+
await fsyncDirectory(layout.generationsRoot);
|
|
50
|
+
}
|
|
51
|
+
const pointer = {
|
|
52
|
+
schema: BRIDGE_ACTIVE_GENERATION_SCHEMA,
|
|
53
|
+
catalog_generation: input.build.catalog.generation,
|
|
54
|
+
route_index_generation: input.build.route_index.generation,
|
|
55
|
+
observation_generation: catalogObservationGeneration(input.build.catalog),
|
|
56
|
+
bundle_directory: path.relative(layout.parentDirectory, layout.bundleDirectory),
|
|
57
|
+
catalog_filename: layout.catalogFilename,
|
|
58
|
+
route_index_filename: layout.routeIndexFilename
|
|
59
|
+
};
|
|
60
|
+
return {
|
|
61
|
+
schema: 'sks.bridge-combined-catalog-staging.v1',
|
|
62
|
+
staged: true,
|
|
63
|
+
generation: input.build.catalog.generation,
|
|
64
|
+
previous_generation: previous.ok ? previous.catalog.generation : null,
|
|
65
|
+
catalog_path: layout.catalogPath,
|
|
66
|
+
route_index_path: layout.routeIndexPath,
|
|
67
|
+
pointer_path: layout.pointerPath,
|
|
68
|
+
pointer_text: `${JSON.stringify(pointer, null, 2)}\n`,
|
|
69
|
+
blockers: []
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
return stagingFailure(layout.pointerPath, previous, [safeErrorCode(error, 'combined_catalog_staging_failed')]);
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
await fs.rm(tempBundle, { recursive: true, force: true }).catch(() => undefined);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export async function activateCombinedBridgeCatalog(input) {
|
|
80
|
+
const staged = await stageCombinedBridgeCatalog(input);
|
|
81
|
+
if (!staged.staged || !staged.pointer_text) {
|
|
82
|
+
return activationResult(staged, false, null, staged.catalog_path, staged.route_index_path, staged.blockers);
|
|
83
|
+
}
|
|
84
|
+
const pointerText = staged.pointer_text;
|
|
85
|
+
return withFileLock({
|
|
86
|
+
lockPath: `${path.resolve(staged.pointer_path)}.lock`,
|
|
87
|
+
timeoutMs: 10_000,
|
|
88
|
+
staleMs: 60_000
|
|
89
|
+
}, async () => {
|
|
90
|
+
const priorPointer = await snapshot(staged.pointer_path);
|
|
91
|
+
try {
|
|
92
|
+
await input.testHooks?.afterCatalogRename?.();
|
|
93
|
+
await input.testHooks?.beforeRouteIndexRename?.();
|
|
94
|
+
await writeTextAtomic(staged.pointer_path, pointerText, { mode: 0o600 });
|
|
95
|
+
await fsyncDirectory(path.dirname(staged.pointer_path));
|
|
96
|
+
const activated = await readActiveCombinedBridgeCatalog(input.catalogPath, input.routeIndexPath);
|
|
97
|
+
if (!activated.ok
|
|
98
|
+
|| activated.catalog.generation !== input.build.catalog.generation
|
|
99
|
+
|| activated.route_index.generation !== input.build.route_index.generation) {
|
|
100
|
+
throw new Error('combined_catalog_activation_verification_failed');
|
|
101
|
+
}
|
|
102
|
+
return activationResult(staged, true, staged.generation, staged.catalog_path, staged.route_index_path, []);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
const rollbackBlockers = [];
|
|
106
|
+
try {
|
|
107
|
+
await restoreSnapshot(staged.pointer_path, priorPointer);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
rollbackBlockers.push('combined_catalog_previous_generation_restore_failed');
|
|
111
|
+
}
|
|
112
|
+
const restored = rollbackBlockers.length === 0
|
|
113
|
+
? await readActiveCombinedBridgeCatalog(input.catalogPath, input.routeIndexPath)
|
|
114
|
+
: null;
|
|
115
|
+
return activationResult(staged, false, null, restored?.ok ? restored.catalog_path : null, restored?.ok ? restored.route_index_path : null, unique([safeErrorCode(error, 'combined_catalog_activation_failed'), ...rollbackBlockers]));
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
export async function readActiveCombinedBridgeCatalog(catalogPath, routeIndexPath) {
|
|
120
|
+
const parentDirectory = path.dirname(path.resolve(catalogPath));
|
|
121
|
+
const pointerPath = path.join(parentDirectory, BRIDGE_ACTIVE_GENERATION_FILENAME);
|
|
122
|
+
try {
|
|
123
|
+
if (path.dirname(path.resolve(routeIndexPath)) !== parentDirectory) {
|
|
124
|
+
throw new Error('combined_catalog_paths_must_share_directory');
|
|
125
|
+
}
|
|
126
|
+
const pointerStat = await fs.lstat(pointerPath).catch(() => null);
|
|
127
|
+
if (!pointerStat) {
|
|
128
|
+
const direct = await readVerifiedPair(path.resolve(catalogPath), path.resolve(routeIndexPath));
|
|
129
|
+
return {
|
|
130
|
+
...direct,
|
|
131
|
+
catalog_path: path.resolve(catalogPath),
|
|
132
|
+
route_index_path: path.resolve(routeIndexPath),
|
|
133
|
+
pointer_path: pointerPath
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (!isPrivateRegular(pointerStat))
|
|
137
|
+
throw new Error('combined_catalog_active_pointer_insecure');
|
|
138
|
+
const pointer = JSON.parse(await fs.readFile(pointerPath, 'utf8'));
|
|
139
|
+
if (pointer.schema !== BRIDGE_ACTIVE_GENERATION_SCHEMA) {
|
|
140
|
+
throw new Error('combined_catalog_active_pointer_schema_invalid');
|
|
141
|
+
}
|
|
142
|
+
const generationsRoot = path.join(parentDirectory, '.sks-bridge-generations');
|
|
143
|
+
const bundleDirectory = path.resolve(parentDirectory, pointer.bundle_directory);
|
|
144
|
+
const expectedBundleDirectory = path.join(generationsRoot, pointer.observation_generation
|
|
145
|
+
? `${pointer.catalog_generation}.${pointer.route_index_generation}.${pointer.observation_generation}`
|
|
146
|
+
: `${pointer.catalog_generation}.${pointer.route_index_generation}`);
|
|
147
|
+
if (bundleDirectory !== expectedBundleDirectory
|
|
148
|
+
|| !bundleDirectory.startsWith(`${generationsRoot}${path.sep}`)
|
|
149
|
+
|| pointer.catalog_filename !== path.basename(catalogPath)
|
|
150
|
+
|| pointer.route_index_filename !== path.basename(routeIndexPath)
|
|
151
|
+
|| path.dirname(path.resolve(bundleDirectory, pointer.catalog_filename)) !== bundleDirectory
|
|
152
|
+
|| path.dirname(path.resolve(bundleDirectory, pointer.route_index_filename)) !== bundleDirectory) {
|
|
153
|
+
throw new Error('combined_catalog_active_pointer_path_invalid');
|
|
154
|
+
}
|
|
155
|
+
await assertPrivateDirectory(generationsRoot);
|
|
156
|
+
await assertPrivateDirectory(bundleDirectory);
|
|
157
|
+
const activeCatalogPath = path.join(bundleDirectory, pointer.catalog_filename);
|
|
158
|
+
const activeRouteIndexPath = path.join(bundleDirectory, pointer.route_index_filename);
|
|
159
|
+
const pair = await readVerifiedPair(activeCatalogPath, activeRouteIndexPath);
|
|
160
|
+
if (pair.catalog.generation !== pointer.catalog_generation
|
|
161
|
+
|| pair.route_index.generation !== pointer.route_index_generation) {
|
|
162
|
+
throw new Error('combined_catalog_active_pointer_generation_mismatch');
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
...pair,
|
|
166
|
+
catalog_path: activeCatalogPath,
|
|
167
|
+
route_index_path: activeRouteIndexPath,
|
|
168
|
+
pointer_path: pointerPath
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
return {
|
|
173
|
+
ok: false,
|
|
174
|
+
catalog: emptyCatalog(),
|
|
175
|
+
route_index: emptyRouteIndex(),
|
|
176
|
+
catalog_path: null,
|
|
177
|
+
route_index_path: null,
|
|
178
|
+
pointer_path: pointerPath,
|
|
179
|
+
blockers: [safeErrorCode(error, 'combined_catalog_active_generation_missing')]
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function stagingFailure(pointerPath, previous, blockers) {
|
|
184
|
+
return {
|
|
185
|
+
schema: 'sks.bridge-combined-catalog-staging.v1',
|
|
186
|
+
staged: false,
|
|
187
|
+
generation: null,
|
|
188
|
+
previous_generation: previous.ok ? previous.catalog.generation : null,
|
|
189
|
+
catalog_path: previous.ok ? previous.catalog_path : null,
|
|
190
|
+
route_index_path: previous.ok ? previous.route_index_path : null,
|
|
191
|
+
pointer_path: pointerPath,
|
|
192
|
+
pointer_text: null,
|
|
193
|
+
blockers
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function activationResult(staged, activated, generation, catalogPath, routeIndexPath, blockers) {
|
|
197
|
+
return {
|
|
198
|
+
schema: 'sks.bridge-combined-catalog-activation.v1',
|
|
199
|
+
activated,
|
|
200
|
+
generation,
|
|
201
|
+
previous_generation: staged.previous_generation,
|
|
202
|
+
catalog_path: catalogPath,
|
|
203
|
+
route_index_path: routeIndexPath,
|
|
204
|
+
pointer_path: staged.pointer_path,
|
|
205
|
+
blockers
|
|
206
|
+
};
|
|
207
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function safeErrorCode(error, fallback) {
|
|
2
|
+
const message = error instanceof Error ? error.message : String(error || '');
|
|
3
|
+
return /^[a-z0-9_:-]{1,160}$/i.test(message) ? message : fallback;
|
|
4
|
+
}
|
|
5
|
+
export function compareModels(left, right) {
|
|
6
|
+
return left.public_id.localeCompare(right.public_id)
|
|
7
|
+
|| left.provider_id.localeCompare(right.provider_id)
|
|
8
|
+
|| left.upstream_model.localeCompare(right.upstream_model);
|
|
9
|
+
}
|
|
10
|
+
export function unique(values) {
|
|
11
|
+
return [...new Set(values.map((value) => String(value || '').trim()).filter(Boolean))];
|
|
12
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { buildBridgeRouteIndex, routeIndexMatchesGeneration, sha256Stable } from '../route-index.js';
|
|
4
|
+
import { writeTextAtomic } from '../../fsx.js';
|
|
5
|
+
import { BRIDGE_ACTIVE_GENERATION_FILENAME, COMBINED_BRIDGE_CATALOG_SCHEMA } from './contracts.js';
|
|
6
|
+
import { unique } from './shared.js';
|
|
7
|
+
export function generationBundleLayout(catalogPath, routeIndexPath, build) {
|
|
8
|
+
const resolvedCatalog = path.resolve(catalogPath);
|
|
9
|
+
const resolvedRoute = path.resolve(routeIndexPath);
|
|
10
|
+
const parentDirectory = path.dirname(resolvedCatalog);
|
|
11
|
+
const generationsRoot = path.join(parentDirectory, '.sks-bridge-generations');
|
|
12
|
+
const generationName = `${build.catalog.generation}.${build.route_index.generation}.${catalogObservationGeneration(build.catalog)}`;
|
|
13
|
+
const bundleDirectory = path.join(generationsRoot, generationName);
|
|
14
|
+
const catalogFilename = path.basename(resolvedCatalog);
|
|
15
|
+
const routeIndexFilename = path.basename(resolvedRoute);
|
|
16
|
+
const blockers = unique([
|
|
17
|
+
...(path.dirname(resolvedRoute) === parentDirectory ? [] : ['combined_catalog_paths_must_share_directory']),
|
|
18
|
+
...(catalogFilename !== routeIndexFilename ? [] : ['combined_catalog_paths_must_be_distinct']),
|
|
19
|
+
...(/^[a-f0-9]{64}\.[a-f0-9]{64}\.[a-f0-9]{64}$/.test(generationName) ? [] : ['combined_catalog_generation_invalid'])
|
|
20
|
+
]);
|
|
21
|
+
return {
|
|
22
|
+
ok: blockers.length === 0,
|
|
23
|
+
parentDirectory,
|
|
24
|
+
generationsRoot,
|
|
25
|
+
bundleDirectory,
|
|
26
|
+
catalogFilename,
|
|
27
|
+
routeIndexFilename,
|
|
28
|
+
catalogPath: path.join(bundleDirectory, catalogFilename),
|
|
29
|
+
routeIndexPath: path.join(bundleDirectory, routeIndexFilename),
|
|
30
|
+
pointerPath: path.join(parentDirectory, BRIDGE_ACTIVE_GENERATION_FILENAME),
|
|
31
|
+
blockers
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export async function readVerifiedPair(catalogPath, routeIndexPath) {
|
|
35
|
+
const [catalogStat, routeStat] = await Promise.all([fs.lstat(catalogPath), fs.lstat(routeIndexPath)]);
|
|
36
|
+
if (!isPrivateRegular(catalogStat) || !isPrivateRegular(routeStat)) {
|
|
37
|
+
throw new Error('combined_catalog_active_file_insecure');
|
|
38
|
+
}
|
|
39
|
+
const [catalog, routeIndex] = await Promise.all([
|
|
40
|
+
fs.readFile(catalogPath, 'utf8').then((text) => JSON.parse(text)),
|
|
41
|
+
fs.readFile(routeIndexPath, 'utf8').then((text) => JSON.parse(text))
|
|
42
|
+
]);
|
|
43
|
+
const validCatalog = catalog.schema === COMBINED_BRIDGE_CATALOG_SCHEMA
|
|
44
|
+
&& catalog.digest === catalog.generation
|
|
45
|
+
&& sha256Stable({ models: catalog.models }) === catalog.digest
|
|
46
|
+
&& validProviderStatuses(catalog.provider_statuses);
|
|
47
|
+
const validRoute = routeIndexMatchesGeneration(routeIndex, routeIndex.generation);
|
|
48
|
+
const providerBindingsValid = validCatalog && ['codex-lb', 'openrouter'].every((providerId) => catalog.provider_statuses[providerId].generation === routeIndex.providers[providerId].catalog_generation);
|
|
49
|
+
if (!validCatalog || !validRoute || !providerBindingsValid) {
|
|
50
|
+
throw new Error('combined_catalog_active_generation_invalid');
|
|
51
|
+
}
|
|
52
|
+
return { ok: true, catalog, route_index: routeIndex, blockers: [] };
|
|
53
|
+
}
|
|
54
|
+
export async function stagePrivateFile(file, text) {
|
|
55
|
+
await fs.mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
56
|
+
const handle = await fs.open(file, 'wx', 0o600);
|
|
57
|
+
try {
|
|
58
|
+
await handle.writeFile(text, 'utf8');
|
|
59
|
+
await handle.sync();
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
await handle.close();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export async function verifyStagedPair(catalogPath, routePath, catalogGeneration, routeGeneration) {
|
|
66
|
+
const [catalog, route] = await Promise.all([
|
|
67
|
+
fs.readFile(catalogPath, 'utf8').then((text) => JSON.parse(text)),
|
|
68
|
+
fs.readFile(routePath, 'utf8').then((text) => JSON.parse(text))
|
|
69
|
+
]);
|
|
70
|
+
if (catalog.generation !== catalogGeneration || route.generation !== routeGeneration) {
|
|
71
|
+
throw new Error('combined_catalog_staged_generation_mismatch');
|
|
72
|
+
}
|
|
73
|
+
if (sha256Stable({ models: catalog.models }) !== catalog.digest
|
|
74
|
+
|| !routeIndexMatchesGeneration(route, routeGeneration)) {
|
|
75
|
+
throw new Error('combined_catalog_staged_digest_mismatch');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export async function snapshot(file) {
|
|
79
|
+
try {
|
|
80
|
+
return { exists: true, text: await fs.readFile(file, 'utf8') };
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return { exists: false, text: '' };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export async function restoreSnapshot(file, prior) {
|
|
87
|
+
if (prior.exists)
|
|
88
|
+
await writeTextAtomic(file, prior.text, { mode: 0o600 });
|
|
89
|
+
else
|
|
90
|
+
await fs.rm(file, { force: true });
|
|
91
|
+
}
|
|
92
|
+
export async function fsyncDirectory(directory) {
|
|
93
|
+
const handle = await fs.open(directory, 'r');
|
|
94
|
+
try {
|
|
95
|
+
await handle.sync();
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
await handle.close();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export async function assertPrivateDirectory(directory) {
|
|
102
|
+
const stat = await fs.lstat(directory);
|
|
103
|
+
const expectedUid = typeof process.getuid === 'function' ? process.getuid() : null;
|
|
104
|
+
if (!stat.isDirectory()
|
|
105
|
+
|| stat.isSymbolicLink()
|
|
106
|
+
|| (expectedUid !== null && Number(stat.uid) !== expectedUid)
|
|
107
|
+
|| (process.platform !== 'win32' && (Number(stat.mode) & 0o777) !== 0o700)) {
|
|
108
|
+
throw new Error('combined_catalog_generation_directory_insecure');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export function isPrivateRegular(stat) {
|
|
112
|
+
return stat.isFile()
|
|
113
|
+
&& !stat.isSymbolicLink()
|
|
114
|
+
&& (typeof process.getuid !== 'function' || Number(stat.uid) === process.getuid())
|
|
115
|
+
&& (process.platform === 'win32' || (Number(stat.mode) & 0o777) === 0o600);
|
|
116
|
+
}
|
|
117
|
+
export function emptyCatalog() {
|
|
118
|
+
const digest = sha256Stable({ models: [] });
|
|
119
|
+
return {
|
|
120
|
+
schema: COMBINED_BRIDGE_CATALOG_SCHEMA,
|
|
121
|
+
generation: digest,
|
|
122
|
+
created_at: new Date(0).toISOString(),
|
|
123
|
+
digest,
|
|
124
|
+
models: [],
|
|
125
|
+
provider_statuses: {
|
|
126
|
+
'codex-lb': emptyProviderStatus('codex-lb'),
|
|
127
|
+
openrouter: emptyProviderStatus('openrouter')
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export function emptyRouteIndex() {
|
|
132
|
+
return buildBridgeRouteIndex({
|
|
133
|
+
models: [],
|
|
134
|
+
providers: {
|
|
135
|
+
'codex-lb': { catalog_generation: null, credential_fingerprint: null, state: 'unknown' },
|
|
136
|
+
openrouter: { catalog_generation: null, credential_fingerprint: null, state: 'unknown' }
|
|
137
|
+
},
|
|
138
|
+
created_at: new Date(0).toISOString()
|
|
139
|
+
}).route_index;
|
|
140
|
+
}
|
|
141
|
+
export function catalogObservationGeneration(catalog) {
|
|
142
|
+
return sha256Stable(catalog.provider_statuses);
|
|
143
|
+
}
|
|
144
|
+
function validProviderStatuses(value) {
|
|
145
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
146
|
+
return false;
|
|
147
|
+
const rows = value;
|
|
148
|
+
return ['codex-lb', 'openrouter'].every((providerId) => {
|
|
149
|
+
const row = rows[providerId];
|
|
150
|
+
return Boolean(row
|
|
151
|
+
&& row.schema === 'sks.catalog-sync-state.v2'
|
|
152
|
+
&& row.provider_id === providerId
|
|
153
|
+
&& (row.expires_at === null || Number.isFinite(Date.parse(row.expires_at))));
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
function emptyProviderStatus(providerId) {
|
|
157
|
+
return {
|
|
158
|
+
schema: 'sks.catalog-sync-state.v2',
|
|
159
|
+
provider_id: providerId,
|
|
160
|
+
state: 'not_started',
|
|
161
|
+
source: providerId === 'codex-lb' ? 'gateway' : 'openrouter',
|
|
162
|
+
generation: null,
|
|
163
|
+
digest: null,
|
|
164
|
+
model_count: 0,
|
|
165
|
+
checked_at: null,
|
|
166
|
+
expires_at: null,
|
|
167
|
+
blockers: [],
|
|
168
|
+
warnings: [],
|
|
169
|
+
recovery_action: null
|
|
170
|
+
};
|
|
171
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export { BRIDGE_ACTIVE_GENERATION_FILENAME, BRIDGE_ACTIVE_GENERATION_SCHEMA, BRIDGE_ROUTE_INDEX_FILENAME, COMBINED_BRIDGE_CATALOG_FILENAME, COMBINED_BRIDGE_CATALOG_SCHEMA } from './combined-catalog/contracts.js';
|
|
4
|
+
export { buildCombinedBridgeCatalog } from './combined-catalog/builder.js';
|
|
5
|
+
export { activateCombinedBridgeCatalog, readActiveCombinedBridgeCatalog, stageCombinedBridgeCatalog } from './combined-catalog/persistence.js';
|
|
6
|
+
import { BRIDGE_ROUTE_INDEX_FILENAME, COMBINED_BRIDGE_CATALOG_FILENAME } from './combined-catalog/contracts.js';
|
|
7
|
+
export function combinedBridgeCatalogPath(codexHome = process.env.CODEX_HOME || path.join(os.homedir(), '.codex')) {
|
|
8
|
+
return path.join(path.resolve(codexHome), 'sks', COMBINED_BRIDGE_CATALOG_FILENAME);
|
|
9
|
+
}
|
|
10
|
+
export function bridgeRouteIndexPath(codexHome = process.env.CODEX_HOME || path.join(os.homedir(), '.codex')) {
|
|
11
|
+
return path.join(path.resolve(codexHome), 'sks', BRIDGE_ROUTE_INDEX_FILENAME);
|
|
12
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readDesktopBridgeState } from './state.js';
|
|
1
|
+
import { isDesktopBridgeStateFresh, readDesktopBridgeState } from './state.js';
|
|
2
2
|
import { safeBridgeErrorCode } from './security.js';
|
|
3
3
|
export const desktopBridgeProcessExists = (pid) => {
|
|
4
4
|
try {
|
|
@@ -30,5 +30,8 @@ export async function getDesktopBridgeStatus(input) {
|
|
|
30
30
|
if (!processExists(state.pid)) {
|
|
31
31
|
return { status: 'stale', state, blocker: 'bridge_process_not_running' };
|
|
32
32
|
}
|
|
33
|
+
if (!isDesktopBridgeStateFresh(state)) {
|
|
34
|
+
return { status: 'stale', state, blocker: 'bridge_state_stale' };
|
|
35
|
+
}
|
|
33
36
|
return { status: 'running', state };
|
|
34
37
|
}
|
|
@@ -1,106 +1,87 @@
|
|
|
1
1
|
import { rewriteLocationHeader } from './location-rewrite.js';
|
|
2
2
|
import { DesktopBridgeError } from './types.js';
|
|
3
|
-
const HOP_BY_HOP = new Set([
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
'
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
'
|
|
10
|
-
'
|
|
11
|
-
'upgrade',
|
|
3
|
+
const HOP_BY_HOP = new Set(['connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization', 'te', 'trailer', 'transfer-encoding', 'upgrade']);
|
|
4
|
+
const INTERNAL_PREFIX = 'x-sks-';
|
|
5
|
+
const REQUEST_HEADER_ALLOWLIST = new Set([
|
|
6
|
+
'accept', 'accept-encoding', 'cache-control', 'content-encoding', 'content-length', 'content-type',
|
|
7
|
+
]);
|
|
8
|
+
const CODEX_LOCAL_HEADERS = new Set([
|
|
9
|
+
'thread-id', 'session-id', 'x-client-request-id', 'x-codex-installation-id',
|
|
10
|
+
'x-codex-turn-metadata', 'x-codex-window-id', 'originator',
|
|
12
11
|
]);
|
|
13
12
|
const NEVER_FORWARD_FROM_CLIENT = new Set([
|
|
14
|
-
'authorization',
|
|
15
|
-
'
|
|
16
|
-
'forwarded',
|
|
17
|
-
'proxy-authorization',
|
|
18
|
-
'x-codex-lb-api-key',
|
|
19
|
-
'x-forwarded-for',
|
|
20
|
-
'x-forwarded-host',
|
|
21
|
-
'x-forwarded-port',
|
|
22
|
-
'x-forwarded-proto',
|
|
23
|
-
'x-real-ip',
|
|
24
|
-
'x-sks-child-model',
|
|
25
|
-
'x-sks-child-policy-hash',
|
|
26
|
-
'x-sks-child-request',
|
|
27
|
-
'x-sks-parent-snapshot-hash',
|
|
28
|
-
'x-sks-provider-mode',
|
|
29
|
-
'x-sks-session-id',
|
|
13
|
+
'authorization', 'cookie', 'forwarded', 'proxy-authorization', 'x-api-key', 'x-codex-lb-api-key',
|
|
14
|
+
'x-forwarded-for', 'x-forwarded-host', 'x-forwarded-port', 'x-forwarded-proto', 'x-real-ip',
|
|
30
15
|
]);
|
|
31
16
|
const NEVER_FORWARD_TO_CLIENT = new Set([
|
|
32
|
-
'authorization',
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
'
|
|
37
|
-
'x-codex-lb-api-key',
|
|
17
|
+
'authorization', 'cookie', 'proxy-authenticate', 'proxy-authorization', 'set-cookie', 'x-api-key', 'x-codex-lb-api-key',
|
|
18
|
+
]);
|
|
19
|
+
const OPENROUTER_CLIENT_HEADERS = new Set(['http-referer', 'x-title']);
|
|
20
|
+
const WEBSOCKET_HEADER_ALLOWLIST = new Set([
|
|
21
|
+
'sec-websocket-key', 'sec-websocket-version', 'sec-websocket-protocol', 'sec-websocket-extensions',
|
|
38
22
|
]);
|
|
39
23
|
function connectionTokens(inbound) {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
return new Set(values.flatMap((value) => value.split(',')).map((value) => value.trim().toLowerCase()).filter(Boolean));
|
|
24
|
+
const values = Array.isArray(inbound.connection) ? inbound.connection : inbound.connection ? [inbound.connection] : [];
|
|
25
|
+
return new Set(values.flatMap((v) => v.split(',')).map((v) => v.trim().toLowerCase()).filter(Boolean));
|
|
43
26
|
}
|
|
44
|
-
function
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
return;
|
|
27
|
+
function injectCredential(result, providerId, transport, credential) {
|
|
28
|
+
if (credential.provider_id !== providerId || !credential.value || /[\r\n\0]/.test(credential.value)) {
|
|
29
|
+
throw new DesktopBridgeError('bridge_provider_credential_invalid');
|
|
48
30
|
}
|
|
49
|
-
|
|
31
|
+
if (providerId === 'openrouter' && transport !== 'openrouter-bearer')
|
|
32
|
+
throw new DesktopBridgeError('bridge_provider_auth_transport_mismatch');
|
|
33
|
+
if (providerId === 'codex-lb' && transport === 'openrouter-bearer')
|
|
34
|
+
throw new DesktopBridgeError('bridge_provider_auth_transport_mismatch');
|
|
35
|
+
if (transport === 'x-codex-lb-api-key')
|
|
36
|
+
result['x-codex-lb-api-key'] = credential.value;
|
|
37
|
+
else
|
|
38
|
+
result.authorization = `Bearer ${credential.value}`;
|
|
50
39
|
}
|
|
51
|
-
export function
|
|
40
|
+
export function buildProviderUpstreamHeaders(inbound, context, upstreamHost) {
|
|
52
41
|
const result = {};
|
|
53
|
-
const dynamicHopByHop = connectionTokens(inbound);
|
|
54
42
|
for (const [rawName, rawValue] of Object.entries(inbound)) {
|
|
55
43
|
if (rawValue === undefined)
|
|
56
44
|
continue;
|
|
57
45
|
const name = rawName.toLowerCase();
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (NEVER_FORWARD_FROM_CLIENT.has(name) || name === 'host')
|
|
46
|
+
const providerAllowed = context.providerId === 'openrouter' && OPENROUTER_CLIENT_HEADERS.has(name);
|
|
47
|
+
if (!REQUEST_HEADER_ALLOWLIST.has(name) && !providerAllowed)
|
|
61
48
|
continue;
|
|
62
49
|
result[name] = rawValue;
|
|
63
50
|
}
|
|
64
51
|
result.host = upstreamHost;
|
|
65
|
-
|
|
52
|
+
injectCredential(result, context.providerId, context.authTransport, context.credential);
|
|
66
53
|
return result;
|
|
67
54
|
}
|
|
68
|
-
export function
|
|
69
|
-
const result =
|
|
55
|
+
export function buildProviderWebSocketHeaders(inbound, context, upstreamHost) {
|
|
56
|
+
const result = buildProviderUpstreamHeaders(inbound, context, upstreamHost);
|
|
70
57
|
for (const [rawName, rawValue] of Object.entries(inbound)) {
|
|
71
58
|
if (rawValue === undefined)
|
|
72
59
|
continue;
|
|
73
60
|
const name = rawName.toLowerCase();
|
|
74
|
-
if (
|
|
61
|
+
if (WEBSOCKET_HEADER_ALLOWLIST.has(name))
|
|
75
62
|
result[name] = rawValue;
|
|
76
|
-
}
|
|
77
63
|
}
|
|
78
64
|
result.connection = 'Upgrade';
|
|
79
65
|
result.upgrade = 'websocket';
|
|
80
66
|
return result;
|
|
81
67
|
}
|
|
82
|
-
export function rewriteResponseHeaders(inbound,
|
|
68
|
+
export function rewriteResponseHeaders(inbound, providerBaseUrl, localOrigin) {
|
|
83
69
|
const result = {};
|
|
84
|
-
const
|
|
70
|
+
const dynamic = connectionTokens(inbound);
|
|
85
71
|
for (const [rawName, rawValue] of Object.entries(inbound)) {
|
|
86
72
|
if (rawValue === undefined)
|
|
87
73
|
continue;
|
|
88
74
|
const name = rawName.toLowerCase();
|
|
89
|
-
if (HOP_BY_HOP.has(name) ||
|
|
90
|
-
continue;
|
|
91
|
-
if (name.startsWith('access-control-'))
|
|
75
|
+
if (HOP_BY_HOP.has(name) || dynamic.has(name) || NEVER_FORWARD_TO_CLIENT.has(name) || name.startsWith('access-control-'))
|
|
92
76
|
continue;
|
|
93
77
|
if (name === 'location') {
|
|
94
|
-
|
|
78
|
+
const values = Array.isArray(rawValue) ? rawValue : [rawValue];
|
|
79
|
+
if (values.length !== 1 || !values[0])
|
|
95
80
|
throw new DesktopBridgeError('bridge_location_header_invalid');
|
|
96
|
-
|
|
97
|
-
const location = Array.isArray(rawValue) ? rawValue[0] : rawValue;
|
|
98
|
-
if (!location)
|
|
99
|
-
throw new DesktopBridgeError('bridge_location_header_invalid');
|
|
100
|
-
result.location = rewriteLocationHeader(location, config.remoteBaseUrl, localOrigin);
|
|
101
|
-
continue;
|
|
81
|
+
result.location = rewriteLocationHeader(values[0], providerBaseUrl, localOrigin);
|
|
102
82
|
}
|
|
103
|
-
|
|
83
|
+
else
|
|
84
|
+
result[name] = rawValue;
|
|
104
85
|
}
|
|
105
86
|
return result;
|
|
106
87
|
}
|
|
@@ -108,5 +89,9 @@ export function isRedactedHeaderName(name) {
|
|
|
108
89
|
const normalized = name.toLowerCase();
|
|
109
90
|
return NEVER_FORWARD_FROM_CLIENT.has(normalized)
|
|
110
91
|
|| NEVER_FORWARD_TO_CLIENT.has(normalized)
|
|
111
|
-
|| normalized
|
|
92
|
+
|| CODEX_LOCAL_HEADERS.has(normalized)
|
|
93
|
+
|| normalized.startsWith(INTERNAL_PREFIX);
|
|
94
|
+
}
|
|
95
|
+
export function redactHeaderValue(name, value) {
|
|
96
|
+
return isRedactedHeaderName(name) ? '[REDACTED]' : String(value ?? '');
|
|
112
97
|
}
|