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
package/dist/core/init.js
CHANGED
|
@@ -670,7 +670,6 @@ export async function initProject(root, opts = {}) {
|
|
|
670
670
|
return `${next.trim()}\n`;
|
|
671
671
|
}
|
|
672
672
|
async function mergeGlobalCodexConfigIfAvailable(configText = '', configPath = '', opts = {}) {
|
|
673
|
-
const selectedRe = /(^|\n)\s*model_provider\s*=\s*"codex-lb"\s*(?:#.*)?(?=\n|$)/;
|
|
674
673
|
const home = opts.home || process.env.HOME || '';
|
|
675
674
|
if (!home)
|
|
676
675
|
return configText;
|
|
@@ -681,49 +680,8 @@ export async function initProject(root, opts = {}) {
|
|
|
681
680
|
const globalConfig = await readText(globalConfigPath, '');
|
|
682
681
|
let next = mergeGlobalMcpServers(configText, globalConfig);
|
|
683
682
|
next = mergeGlobalCodexAppRuntimeTables(next, globalConfig);
|
|
684
|
-
if (selectedRe.test(next) && /\[model_providers\.codex-lb\]/.test(next)) {
|
|
685
|
-
return `${next.trim()}\n`;
|
|
686
|
-
}
|
|
687
|
-
const envPath = path.join(codexHome, 'sks-codex-lb.env');
|
|
688
|
-
if (!(await exists(envPath)))
|
|
689
|
-
return next;
|
|
690
|
-
const envText = await readText(envPath, '');
|
|
691
|
-
const baseUrl = globalConfig.match(/(^|\n)\[model_providers\.codex-lb\][\s\S]*?\n\s*base_url\s*=\s*"([^"]+)"/)?.[2] || parseCodexLbEnvBaseUrl(envText);
|
|
692
|
-
if (!parseCodexLbEnvKey(envText) || !baseUrl)
|
|
693
|
-
return next;
|
|
694
|
-
const shouldSelectCodexLb = selectedRe.test(next) || selectedRe.test(globalConfig);
|
|
695
|
-
next = shouldSelectCodexLb
|
|
696
|
-
? upsertTopLevelTomlString(next, 'model_provider', 'codex-lb')
|
|
697
|
-
: removeTopLevelTomlKeyIfValue(next, 'model_provider', 'codex-lb');
|
|
698
|
-
next = upsertTomlTable(next, 'model_providers.codex-lb', `[model_providers.codex-lb]\nname = "codex-lb"\nbase_url = "${baseUrl}"\nwire_api = "responses"\nenv_key = "CODEX_LB_API_KEY"\nsupports_websockets = true\nrequires_openai_auth = false`);
|
|
699
683
|
return `${next.trim()}\n`;
|
|
700
684
|
}
|
|
701
|
-
function parseCodexLbEnvKey(text = '') {
|
|
702
|
-
return parseShellEnvValue(text, 'CODEX_LB_API_KEY');
|
|
703
|
-
}
|
|
704
|
-
function parseCodexLbEnvBaseUrl(text = '') {
|
|
705
|
-
const value = parseShellEnvValue(text, 'CODEX_LB_BASE_URL');
|
|
706
|
-
if (!value)
|
|
707
|
-
return '';
|
|
708
|
-
let host = value.trim();
|
|
709
|
-
if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(host))
|
|
710
|
-
host = `https://${host}`;
|
|
711
|
-
host = host.replace(/\/+$/, '');
|
|
712
|
-
return /\/backend-api\/codex$/i.test(host) ? host : `${host}/backend-api/codex`;
|
|
713
|
-
}
|
|
714
|
-
function parseShellEnvValue(text = '', key = '') {
|
|
715
|
-
const re = new RegExp(`^\\s*(?:export\\s+)?${escapeRegExp(key)}\\s*=\\s*(.+?)\\s*$`, 'm');
|
|
716
|
-
const raw = String(text || '').match(re)?.[1]?.trim() || '';
|
|
717
|
-
if (!raw)
|
|
718
|
-
return '';
|
|
719
|
-
if (raw.startsWith("'"))
|
|
720
|
-
return raw.endsWith("'") && raw.length > 1 ? raw.slice(1, -1).replace(/'\\''/g, "'") : '';
|
|
721
|
-
if (raw.startsWith('"'))
|
|
722
|
-
return raw.endsWith('"') && raw.length > 1 ? raw.slice(1, -1).replace(/\\"/g, '"') : '';
|
|
723
|
-
if (raw.includes("'") || raw.includes('"') || /\s/.test(raw))
|
|
724
|
-
return '';
|
|
725
|
-
return raw;
|
|
726
|
-
}
|
|
727
685
|
function mergeGlobalMcpServers(configText = '', globalConfig = '') {
|
|
728
686
|
let next = configText;
|
|
729
687
|
const re = /(?:^|\n)(\[(mcp_servers\.[^\]\r\n]+)\][\s\S]*?)(?=\n\[[^\]]+\]|\s*$)/g;
|
|
@@ -762,27 +720,6 @@ export async function initProject(root, opts = {}) {
|
|
|
762
720
|
}
|
|
763
721
|
return lines.filter((_, index) => index < start || index >= end).join('\n').replace(/^\n+/, '').replace(/\n{3,}/g, '\n\n');
|
|
764
722
|
}
|
|
765
|
-
function removeTopLevelTomlKeyIfValue(text = '', key = '', value = '') {
|
|
766
|
-
const lines = String(text || '').split('\n');
|
|
767
|
-
const firstTable = lines.findIndex((x) => /^\s*\[.+\]\s*$/.test(x));
|
|
768
|
-
const end = firstTable === -1 ? lines.length : firstTable;
|
|
769
|
-
const keyPattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*"${escapeRegExp(value)}"\\s*(?:#.*)?$`);
|
|
770
|
-
return lines.filter((line, index) => index >= end || !keyPattern.test(line)).join('\n').replace(/^\n+/, '').replace(/\n{3,}/g, '\n\n');
|
|
771
|
-
}
|
|
772
|
-
function upsertTopLevelTomlString(text, key, value) {
|
|
773
|
-
const line = `${key} = "${value}"`;
|
|
774
|
-
const lines = String(text || '').split('\n');
|
|
775
|
-
const firstTable = lines.findIndex((x) => /^\s*\[.+\]\s*$/.test(x));
|
|
776
|
-
const end = firstTable === -1 ? lines.length : firstTable;
|
|
777
|
-
for (let i = 0; i < end; i += 1) {
|
|
778
|
-
if (new RegExp(`^\\s*${escapeRegExp(key)}\\s*=`).test(lines[i] || '')) {
|
|
779
|
-
lines[i] = line;
|
|
780
|
-
return lines.join('\n').replace(/\n{3,}/g, '\n\n');
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
lines.splice(end, 0, line);
|
|
784
|
-
return lines.join('\n').replace(/^\n+/, '').replace(/\n{3,}/g, '\n\n');
|
|
785
|
-
}
|
|
786
723
|
function upsertTopLevelTomlBoolean(text, key, value) {
|
|
787
724
|
const line = `${key} = ${value ? 'true' : 'false'}`;
|
|
788
725
|
const lines = String(text || '').split('\n');
|
|
@@ -316,7 +316,7 @@ async function readReceiptBinding(receipt, tarball) {
|
|
|
316
316
|
const blockers = [];
|
|
317
317
|
const sha256 = normalizeSha256(parsed.sha256);
|
|
318
318
|
const version = typeof parsed.package_version === 'string' ? parsed.package_version : null;
|
|
319
|
-
if (parsed.schema !== 'sks.release-pack-receipt.
|
|
319
|
+
if (parsed.schema !== 'sks.release-pack-receipt.v2' || parsed.ok !== true)
|
|
320
320
|
blockers.push('pack_receipt_invalid');
|
|
321
321
|
if (!sha256)
|
|
322
322
|
blockers.push('pack_receipt_sha256_invalid');
|
|
@@ -50,8 +50,21 @@ const WINDOW_CHARS = 90;
|
|
|
50
50
|
function containsTrigger(haystack) {
|
|
51
51
|
const lower = haystack.toLowerCase();
|
|
52
52
|
for (const trigger of GUIDANCE_TRIGGERS) {
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
const needle = trigger.toLowerCase();
|
|
54
|
+
let fromIndex = 0;
|
|
55
|
+
while (fromIndex <= lower.length - needle.length) {
|
|
56
|
+
const index = lower.indexOf(needle, fromIndex);
|
|
57
|
+
if (index < 0)
|
|
58
|
+
break;
|
|
59
|
+
const before = index > 0 ? (lower[index - 1] ?? '') : '';
|
|
60
|
+
const afterIndex = index + needle.length;
|
|
61
|
+
const after = afterIndex < lower.length ? (lower[afterIndex] ?? '') : '';
|
|
62
|
+
const englishTrigger = /[a-z]/.test(needle);
|
|
63
|
+
const hasWordBoundaries = !englishTrigger || (!/[a-z0-9_]/.test(before) && !/[a-z0-9_]/.test(after));
|
|
64
|
+
if (hasWordBoundaries)
|
|
65
|
+
return trigger;
|
|
66
|
+
fromIndex = index + 1;
|
|
67
|
+
}
|
|
55
68
|
}
|
|
56
69
|
return null;
|
|
57
70
|
}
|
|
@@ -4,8 +4,7 @@ import { buildCodexExecArgs } from '../codex/codex-cli-syntax-builder.js';
|
|
|
4
4
|
import { inspectCodexConfigReadability } from '../codex/codex-config-readability.js';
|
|
5
5
|
import { repairCodexConfigEperm } from '../codex/codex-config-eperm-repair.js';
|
|
6
6
|
import { splitCodexProjectConfigPolicy } from '../codex/codex-project-config-policy.js';
|
|
7
|
-
import {
|
|
8
|
-
import { codexLbToolOutputRecoveryOverrideAcknowledged } from '../codex-lb/codex-lb-tool-output-recovery.js';
|
|
7
|
+
import { desktopBridgeStatusV3 } from '../codex-lb/desktop-controller-v3.js';
|
|
9
8
|
export const PARALLEL_PREFLIGHT_SCHEMA = 'sks.parallel-preflight.v1';
|
|
10
9
|
export async function runParallelPreflight(checks) {
|
|
11
10
|
const startedAt = nowIso();
|
|
@@ -34,7 +33,7 @@ export async function runCodexLaunchPreflight(rootInput = process.cwd(), opts =
|
|
|
34
33
|
const readonly = await runParallelPreflight([
|
|
35
34
|
{ id: 'codex_config_readability', run: () => inspectCodexConfigReadability(root, { ...opts, codexProbe: probeCodex, actualCodex: probeCodex, writeReport: false }) },
|
|
36
35
|
{ id: 'codex_project_config_policy', run: () => splitCodexProjectConfigPolicy(root, { ...opts, writeReport: false }) },
|
|
37
|
-
{ id: '
|
|
36
|
+
{ id: 'desktop_bridge_status', run: () => inspectDesktopBridgeForLaunch(opts) }
|
|
38
37
|
]);
|
|
39
38
|
const repair = opts.fix === true
|
|
40
39
|
? await repairCodexConfigEperm(root, { ...opts, codexProbe: probeCodex, actualCodex: probeCodex, fix: true, writeReport: false })
|
|
@@ -59,7 +58,7 @@ export async function runCodexLaunchPreflight(rootInput = process.cwd(), opts =
|
|
|
59
58
|
};
|
|
60
59
|
const blockers = [...new Set([...(readonly.blockers || []), ...(repair?.blockers || []), ...(fastTierProof.ok ? [] : ['service_tier_not_passed_to_codex'])])];
|
|
61
60
|
const operatorActions = [...new Set([...(readonly.operator_actions || []), ...(repair?.operator_actions || [])])];
|
|
62
|
-
const
|
|
61
|
+
const desktopBridgeStatus = readonly.results.find((result) => result.id === 'desktop_bridge_status')?.value || null;
|
|
63
62
|
const report = {
|
|
64
63
|
schema: 'sks.mad-launch-preflight.v1',
|
|
65
64
|
generated_at: nowIso(),
|
|
@@ -67,7 +66,7 @@ export async function runCodexLaunchPreflight(rootInput = process.cwd(), opts =
|
|
|
67
66
|
ok: blockers.length === 0,
|
|
68
67
|
readonly,
|
|
69
68
|
repair,
|
|
70
|
-
|
|
69
|
+
desktop_bridge_status: desktopBridgeStatus,
|
|
71
70
|
fast_tier_proof: fastTierProof,
|
|
72
71
|
blockers,
|
|
73
72
|
operator_actions: operatorActions
|
|
@@ -76,61 +75,38 @@ export async function runCodexLaunchPreflight(rootInput = process.cwd(), opts =
|
|
|
76
75
|
await writeJsonAtomic(reportPath, { ...report, report_path: reportPath });
|
|
77
76
|
return report;
|
|
78
77
|
}
|
|
79
|
-
export async function
|
|
80
|
-
if (opts.
|
|
78
|
+
export async function inspectDesktopBridgeForLaunch(opts = {}) {
|
|
79
|
+
if (opts.skipDesktopBridgeStatus === true) {
|
|
81
80
|
return {
|
|
82
|
-
schema: 'sks.
|
|
81
|
+
schema: 'sks.desktop-bridge-launch-preflight.v1',
|
|
83
82
|
ok: true,
|
|
84
|
-
status: '
|
|
85
|
-
|
|
83
|
+
status: 'skipped',
|
|
84
|
+
managed: false,
|
|
86
85
|
blockers: [],
|
|
87
86
|
operator_actions: []
|
|
88
87
|
};
|
|
89
88
|
}
|
|
90
|
-
const
|
|
91
|
-
|| codexLbToolOutputRecoveryOverrideAcknowledged({ env: opts.env || process.env });
|
|
92
|
-
const status = await codexLbStatus({
|
|
89
|
+
const status = await (opts.desktopBridgeStatusImpl || desktopBridgeStatusV3)({
|
|
93
90
|
...opts,
|
|
94
|
-
...(opts.home || opts.codexHome ? { home: opts.home || opts.codexHome } : {})
|
|
95
|
-
...(opts.codexLbConfigPath ? { configPath: opts.codexLbConfigPath } : {}),
|
|
96
|
-
...(opts.codexLbEnvPath ? { envPath: opts.codexLbEnvPath } : {}),
|
|
97
|
-
...(typeof opts.codexLbToolOutputRecoveryFetch === 'function'
|
|
98
|
-
? { toolOutputRecoveryFetch: opts.codexLbToolOutputRecoveryFetch }
|
|
99
|
-
: {}),
|
|
100
|
-
...(opts.codexLbToolOutputRecoveryTimeoutMs
|
|
101
|
-
? { toolOutputRecoveryTimeoutMs: opts.codexLbToolOutputRecoveryTimeoutMs }
|
|
102
|
-
: {}),
|
|
103
|
-
probeToolOutputRecovery: true,
|
|
104
|
-
allowUnverifiedToolOutputRecovery
|
|
91
|
+
...(opts.home || opts.codexHome ? { home: opts.home || opts.codexHome } : {})
|
|
105
92
|
});
|
|
106
|
-
const
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
93
|
+
const managed = status.management?.managed === true;
|
|
94
|
+
const ready = status.readiness?.ready === true;
|
|
95
|
+
const blockers = managed && !ready
|
|
96
|
+
? Array.isArray(status.readiness?.blockers)
|
|
97
|
+
? status.readiness.blockers.map(String).filter(Boolean)
|
|
98
|
+
: ['desktop_bridge_not_ready']
|
|
111
99
|
: [];
|
|
112
|
-
const ok = !required || (providerReady && recovery?.ok === true);
|
|
113
100
|
return {
|
|
114
|
-
schema: 'sks.
|
|
115
|
-
ok,
|
|
116
|
-
status: !
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
tool_output_recovery: recovery || null,
|
|
125
|
-
blockers: ok
|
|
126
|
-
? []
|
|
127
|
-
: !providerReady
|
|
128
|
-
? providerBlockers.length > 0
|
|
129
|
-
? providerBlockers
|
|
130
|
-
: ['codex_lb_provider_not_ready']
|
|
131
|
-
: recovery?.blockers || ['codex_lb_tool_output_recovery_version_unverified'],
|
|
132
|
-
operator_actions: ok ? [] : recovery?.operator_actions || [
|
|
133
|
-
'Upgrade codex-lb, or run `sks codex-lb disable`; SKS will not change routing or authentication silently.'
|
|
134
|
-
]
|
|
101
|
+
schema: 'sks.desktop-bridge-launch-preflight.v1',
|
|
102
|
+
ok: !managed || ready,
|
|
103
|
+
status: !managed ? 'not_managed' : ready ? 'ready' : 'blocked',
|
|
104
|
+
managed,
|
|
105
|
+
ready,
|
|
106
|
+
correlation_id: status.correlation_id || null,
|
|
107
|
+
blockers,
|
|
108
|
+
operator_actions: managed && !ready && Array.isArray(status.recovery_actions)
|
|
109
|
+
? status.recovery_actions.map(String).filter(Boolean)
|
|
110
|
+
: []
|
|
135
111
|
};
|
|
136
112
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export const PROVIDER_BADGE_SCHEMA = 'sks.provider-badge.v1';
|
|
2
2
|
export function providerBadgeText(context) {
|
|
3
|
+
const bridgeProvider = context.signals.desktop_bridge_provider;
|
|
3
4
|
const providerText = context.provider === 'openai'
|
|
4
|
-
? 'OpenAI'
|
|
5
|
-
: context.provider === '
|
|
6
|
-
? '
|
|
5
|
+
? 'OpenAI API'
|
|
6
|
+
: context.provider === 'desktop-bridge'
|
|
7
|
+
? `Desktop Bridge${bridgeProvider ? ` → ${bridgeProvider}` : ''}`
|
|
7
8
|
: context.provider === 'codex-app'
|
|
8
9
|
? 'Codex App OAuth'
|
|
9
10
|
: 'Unknown';
|
|
@@ -15,7 +16,11 @@ export function providerBadgeText(context) {
|
|
|
15
16
|
return `Provider: ${providerText} · ${tierText}`;
|
|
16
17
|
}
|
|
17
18
|
export function providerPaneLabel(context) {
|
|
18
|
-
const provider = context.provider === 'unknown'
|
|
19
|
+
const provider = context.provider === 'unknown'
|
|
20
|
+
? 'provider-unknown'
|
|
21
|
+
: context.provider === 'desktop-bridge' && context.signals.desktop_bridge_provider
|
|
22
|
+
? `desktop-bridge/${context.signals.desktop_bridge_provider}`
|
|
23
|
+
: context.provider;
|
|
19
24
|
const tier = context.service_tier === 'unknown' ? 'tier-unknown' : context.service_tier;
|
|
20
25
|
return `${tier} · ${provider}`;
|
|
21
26
|
}
|
|
@@ -1,58 +1,48 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { exists, nowIso, readJson,
|
|
3
|
-
|
|
4
|
-
export const PROVIDER_CONTEXT_SCHEMA = 'sks.provider-context.v1';
|
|
2
|
+
import { exists, nowIso, readJson, writeJsonAtomic } from '../fsx.js';
|
|
3
|
+
export const PROVIDER_CONTEXT_SCHEMA = 'sks.provider-context.v2';
|
|
5
4
|
export async function resolveProviderContext(input = {}) {
|
|
6
5
|
const env = input.env || process.env;
|
|
7
6
|
const root = path.resolve(input.root || process.cwd());
|
|
8
|
-
const codexHome = path.resolve(String(input.codexHome || env.CODEX_HOME || path.join(env.HOME ||
|
|
9
|
-
const configText = await readText(path.join(codexHome, 'config.toml'), '').catch(() => '');
|
|
10
|
-
const configModelProvider = readTopLevelTomlString(configText, 'model_provider');
|
|
11
|
-
const codexLbProviderBlockPresent = hasCodexLbProviderBlock(configText);
|
|
12
|
-
const codexLbBearerEnvKey = codexLbProviderBearerEnvKey(configText);
|
|
13
|
-
const codexLbGatewayHeaderEnvKey = codexLbProviderGatewayHeaderEnvKey(configText);
|
|
14
|
-
const codexLbEnvKey = codexLbGatewayHeaderEnvKey || codexLbBearerEnvKey;
|
|
15
|
-
const codexLbRequiresOpenAiAuth = codexLbProviderRequiresOpenAiAuth(configText);
|
|
16
|
-
const codexLbProviderValid = codexLbProviderBlockPresent
|
|
17
|
-
&& codexLbBearerEnvKey === 'CODEX_LB_API_KEY'
|
|
18
|
-
&& codexLbGatewayHeaderEnvKey === null
|
|
19
|
-
&& codexLbRequiresOpenAiAuth === false;
|
|
20
|
-
const openaiKey = Boolean(String(env.OPENAI_API_KEY || '').trim());
|
|
21
|
-
const lbKey = Boolean(String((codexLbEnvKey ? env[codexLbEnvKey] : env.CODEX_LB_API_KEY) || env.CODEX_LB_API_KEY || '').trim());
|
|
22
|
-
const envProvider = String(env.SKS_MODEL_PROVIDER || env.CODEX_MODEL_PROVIDER || env.OPENAI_MODEL_PROVIDER || '').trim();
|
|
23
|
-
const modelProvider = String(input.modelProvider || envProvider || configModelProvider || '').trim() || null;
|
|
24
|
-
const envLbExplicit = env.SKS_PROVIDER === 'codex-lb' || env.SKS_USE_CODEX_LB === '1';
|
|
25
|
-
const lbExplicit = modelProvider === 'codex-lb' || envLbExplicit;
|
|
7
|
+
const codexHome = path.resolve(String(input.codexHome || env.CODEX_HOME || path.join(env.HOME || root, '.codex')));
|
|
26
8
|
const auth = await readJson(path.join(codexHome, 'auth.json'), null).catch(() => null);
|
|
27
9
|
const appAuthPresent = Boolean(auth) || await exists(path.join(codexHome, 'auth.json'));
|
|
28
|
-
const
|
|
10
|
+
const openaiKeyPresent = Boolean(String(env.OPENAI_API_KEY || '').trim());
|
|
11
|
+
let bridgeStatus = null;
|
|
12
|
+
let bridgeStatusAvailable = false;
|
|
13
|
+
try {
|
|
14
|
+
bridgeStatus = Object.prototype.hasOwnProperty.call(input, 'desktopBridgeStatus')
|
|
15
|
+
? input.desktopBridgeStatus || null
|
|
16
|
+
: await (input.desktopBridgeStatusImpl || currentDesktopBridgeStatus)(input.desktopBridgeStatusOptions || {});
|
|
17
|
+
bridgeStatusAvailable = bridgeStatus?.schema === 'sks.desktop-bridge-status.v3';
|
|
18
|
+
if (!bridgeStatusAvailable)
|
|
19
|
+
bridgeStatus = null;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
bridgeStatus = null;
|
|
23
|
+
}
|
|
24
|
+
const bridgeManaged = bridgeStatus?.management.managed === true;
|
|
25
|
+
const bridgeReady = bridgeStatus?.readiness.ready === true;
|
|
26
|
+
const bridgeProvider = selectedBridgeProvider(bridgeStatus);
|
|
27
|
+
const nativeIdentityConfigured = bridgeStatus?.native_identity.configured === true || appAuthPresent;
|
|
28
|
+
const credentialState = bridgeProvider && bridgeStatus
|
|
29
|
+
? bridgeStatus.providers[bridgeProvider].credential.state
|
|
30
|
+
: null;
|
|
29
31
|
let provider = 'unknown';
|
|
30
32
|
let authMode = 'unknown';
|
|
31
33
|
let source = 'unknown';
|
|
32
34
|
let confidence = 'low';
|
|
33
|
-
if (
|
|
34
|
-
provider = '
|
|
35
|
-
authMode = '
|
|
36
|
-
source = '
|
|
37
|
-
confidence = 'high';
|
|
38
|
-
}
|
|
39
|
-
else if (modelProvider === 'codex-lb' && codexLbProviderValid && lbKey) {
|
|
40
|
-
provider = 'codex-lb';
|
|
41
|
-
authMode = 'codex_lb_key';
|
|
42
|
-
source = 'config';
|
|
43
|
-
confidence = 'high';
|
|
35
|
+
if (bridgeManaged) {
|
|
36
|
+
provider = 'desktop-bridge';
|
|
37
|
+
authMode = nativeIdentityConfigured ? 'chatgpt_oauth' : 'unknown';
|
|
38
|
+
source = 'desktop_bridge';
|
|
39
|
+
confidence = bridgeReady && nativeIdentityConfigured ? 'high' : 'medium';
|
|
44
40
|
}
|
|
45
|
-
else if (
|
|
46
|
-
provider = 'codex-lb';
|
|
47
|
-
authMode = 'unknown';
|
|
48
|
-
source = 'config';
|
|
49
|
-
confidence = 'low';
|
|
50
|
-
}
|
|
51
|
-
else if (openaiKey) {
|
|
41
|
+
else if (openaiKeyPresent) {
|
|
52
42
|
provider = 'openai';
|
|
53
43
|
authMode = 'api_key';
|
|
54
44
|
source = 'env';
|
|
55
|
-
confidence =
|
|
45
|
+
confidence = 'high';
|
|
56
46
|
}
|
|
57
47
|
else if (appAuthPresent) {
|
|
58
48
|
provider = 'codex-app';
|
|
@@ -60,11 +50,12 @@ export async function resolveProviderContext(input = {}) {
|
|
|
60
50
|
source = 'codex_app';
|
|
61
51
|
confidence = 'medium';
|
|
62
52
|
}
|
|
63
|
-
const warnings = [
|
|
64
|
-
...(
|
|
65
|
-
...(
|
|
66
|
-
...(
|
|
67
|
-
|
|
53
|
+
const warnings = unique([
|
|
54
|
+
...(!bridgeStatusAvailable ? ['desktop_bridge_status_unavailable'] : []),
|
|
55
|
+
...(bridgeManaged ? bridgeStatus?.readiness.blockers || [] : []),
|
|
56
|
+
...(bridgeManaged ? bridgeStatus?.routing.blockers || [] : []),
|
|
57
|
+
...(bridgeManaged && !nativeIdentityConfigured ? ['desktop_bridge_native_identity_required'] : [])
|
|
58
|
+
]);
|
|
68
59
|
return {
|
|
69
60
|
schema: PROVIDER_CONTEXT_SCHEMA,
|
|
70
61
|
generated_at: nowIso(),
|
|
@@ -74,19 +65,17 @@ export async function resolveProviderContext(input = {}) {
|
|
|
74
65
|
service_tier: normalizeServiceTier(input.serviceTier || env.SKS_SERVICE_TIER),
|
|
75
66
|
source,
|
|
76
67
|
confidence,
|
|
77
|
-
conflict,
|
|
68
|
+
conflict: false,
|
|
78
69
|
warnings,
|
|
79
70
|
signals: {
|
|
80
|
-
openai_api_key_present:
|
|
81
|
-
codex_lb_key_present: lbKey,
|
|
82
|
-
codex_lb_explicit: lbExplicit,
|
|
71
|
+
openai_api_key_present: openaiKeyPresent,
|
|
83
72
|
codex_app_auth_present: appAuthPresent,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
73
|
+
desktop_bridge_status_available: bridgeStatusAvailable,
|
|
74
|
+
desktop_bridge_managed: bridgeManaged,
|
|
75
|
+
desktop_bridge_ready: bridgeReady,
|
|
76
|
+
desktop_bridge_provider: bridgeProvider,
|
|
77
|
+
desktop_bridge_native_identity_configured: nativeIdentityConfigured,
|
|
78
|
+
desktop_bridge_credential_state: credentialState
|
|
90
79
|
}
|
|
91
80
|
};
|
|
92
81
|
}
|
|
@@ -96,6 +85,16 @@ export async function writeProviderContextReport(root = process.cwd(), input = {
|
|
|
96
85
|
await writeJsonAtomic(reportPath, report);
|
|
97
86
|
return { ...report, report_path: reportPath };
|
|
98
87
|
}
|
|
88
|
+
function selectedBridgeProvider(status) {
|
|
89
|
+
return status?.routing.selected_route?.provider_id
|
|
90
|
+
|| status?.routing.policy?.default_provider_id
|
|
91
|
+
|| status?.routing.session_pin?.provider_id
|
|
92
|
+
|| null;
|
|
93
|
+
}
|
|
94
|
+
async function currentDesktopBridgeStatus(options) {
|
|
95
|
+
const controller = await import('../codex-lb/desktop-controller.js');
|
|
96
|
+
return controller.desktopBridgeStatusV3(options);
|
|
97
|
+
}
|
|
99
98
|
function normalizeServiceTier(value) {
|
|
100
99
|
const text = String(value || '').toLowerCase();
|
|
101
100
|
if (text === 'fast' || text === 'priority')
|
|
@@ -104,55 +103,6 @@ function normalizeServiceTier(value) {
|
|
|
104
103
|
return 'standard';
|
|
105
104
|
return 'unknown';
|
|
106
105
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
for (const line of lines) {
|
|
110
|
-
if (/^\s*\[/.test(line))
|
|
111
|
-
break;
|
|
112
|
-
const match = line.match(new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*"([^"]*)"\\s*(?:#.*)?$`));
|
|
113
|
-
if (match?.[1] != null)
|
|
114
|
-
return match[1];
|
|
115
|
-
}
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
|
-
export function hasCodexLbProviderBlock(text) {
|
|
119
|
-
return codexLbProviderBody(text) != null;
|
|
120
|
-
}
|
|
121
|
-
export function codexLbProviderEnvKey(text) {
|
|
122
|
-
return codexLbProviderGatewayHeaderEnvKey(text) || codexLbProviderBearerEnvKey(text);
|
|
123
|
-
}
|
|
124
|
-
export function codexLbProviderGatewayHeaderEnvKey(text) {
|
|
125
|
-
const body = codexLbProviderBody(text);
|
|
126
|
-
if (body == null)
|
|
127
|
-
return null;
|
|
128
|
-
const inline = body.match(/(?:^|\n)\s*env_http_headers\s*=\s*\{([^}]*)\}/)?.[1] || '';
|
|
129
|
-
return inline.match(/"X-Codex-LB-API-Key"\s*=\s*"([^"]+)"/)?.[1] || null;
|
|
130
|
-
}
|
|
131
|
-
function codexLbProviderBearerEnvKey(text) {
|
|
132
|
-
const body = codexLbProviderBody(text);
|
|
133
|
-
return body == null ? null : readTopLevelTomlString(body, 'env_key');
|
|
134
|
-
}
|
|
135
|
-
export function codexLbProviderRequiresOpenAiAuth(text) {
|
|
136
|
-
const body = codexLbProviderBody(text);
|
|
137
|
-
if (body == null)
|
|
138
|
-
return null;
|
|
139
|
-
const match = body.match(/^\s*requires_openai_auth\s*=\s*(true|false)\s*(?:#.*)?$/m);
|
|
140
|
-
return match?.[1] === 'true' ? true : match?.[1] === 'false' ? false : null;
|
|
141
|
-
}
|
|
142
|
-
function codexLbProviderBody(text) {
|
|
143
|
-
const lines = String(text || '').split(/\r?\n/);
|
|
144
|
-
const out = [];
|
|
145
|
-
let inTable = false;
|
|
146
|
-
for (const line of lines) {
|
|
147
|
-
const table = line.match(/^\s*\[([^\]]+)\]\s*(?:#.*)?$/)?.[1]?.trim();
|
|
148
|
-
if (table) {
|
|
149
|
-
if (inTable)
|
|
150
|
-
break;
|
|
151
|
-
inTable = table === 'model_providers.codex-lb' || table === 'model_providers."codex-lb"' || table === '"model_providers"."codex-lb"';
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
if (inTable)
|
|
155
|
-
out.push(line);
|
|
156
|
-
}
|
|
157
|
-
return inTable ? out.join('\n') : null;
|
|
106
|
+
function unique(values) {
|
|
107
|
+
return [...new Set(values.map((value) => String(value || '').trim()).filter(Boolean))];
|
|
158
108
|
}
|
|
@@ -96,7 +96,9 @@ export async function testOpenRouterConnection(input = {}) {
|
|
|
96
96
|
warnings: []
|
|
97
97
|
};
|
|
98
98
|
}
|
|
99
|
-
const resolved =
|
|
99
|
+
const resolved = input.apiKey
|
|
100
|
+
? { key: String(input.apiKey), source: 'explicit-validation-snapshot', warnings: [] }
|
|
101
|
+
: await resolveOpenRouterApiKey({ env: input.env || process.env });
|
|
100
102
|
if (!resolved.key) {
|
|
101
103
|
return {
|
|
102
104
|
schema: 'sks.openrouter-test.v1',
|
|
@@ -3,6 +3,8 @@ import fs from 'node:fs/promises';
|
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { redactOpenRouterKey } from '../../security/redact-secrets.js';
|
|
6
|
+
import { readPrivateCredentialFile, writePrivateTextAtomic } from '../../security/private-credential-file.js';
|
|
7
|
+
import { inspectConfinedPath, isLexicallyConfined, removeManagedPathVerified } from '../../managed-path-safety.js';
|
|
6
8
|
export const OPENROUTER_KEY_ENV_NAMES = ['OPENROUTER_API_KEY', 'SKS_OPENROUTER_API_KEY'];
|
|
7
9
|
export function openRouterSecretPaths(env = process.env) {
|
|
8
10
|
const sksHome = path.resolve(env.SKS_HOME || path.join(env.HOME || os.homedir(), '.sneakoscope'));
|
|
@@ -49,7 +51,7 @@ export async function resolveOpenRouterApiKey(input = {}) {
|
|
|
49
51
|
}
|
|
50
52
|
export async function readStoredOpenRouterKey(paths) {
|
|
51
53
|
try {
|
|
52
|
-
const text = await
|
|
54
|
+
const text = (await readPrivateCredentialFile(paths.secretDir, paths.keyPath, 'openrouter_api_key')).bytes.toString('utf8');
|
|
53
55
|
const key = text.trim();
|
|
54
56
|
return key || null;
|
|
55
57
|
}
|
|
@@ -66,24 +68,7 @@ export async function writeStoredOpenRouterKey(value, input = {}) {
|
|
|
66
68
|
const timestamp = nowIso();
|
|
67
69
|
const previous = input.previousRecord ?? await readOpenRouterKeyRecord(paths);
|
|
68
70
|
await ensureSecretDir(paths.secretDir);
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
const handle = await fs.open(tmp, 'w', 0o600);
|
|
72
|
-
try {
|
|
73
|
-
await handle.writeFile(`${key}\n`, 'utf8');
|
|
74
|
-
await handle.sync().catch(() => undefined);
|
|
75
|
-
}
|
|
76
|
-
finally {
|
|
77
|
-
await handle.close().catch(() => undefined);
|
|
78
|
-
}
|
|
79
|
-
await fs.chmod(tmp, 0o600).catch(() => undefined);
|
|
80
|
-
await fs.rename(tmp, paths.keyPath);
|
|
81
|
-
await fs.chmod(paths.keyPath, 0o600).catch(() => undefined);
|
|
82
|
-
}
|
|
83
|
-
catch (err) {
|
|
84
|
-
await fs.rm(tmp, { force: true }).catch(() => undefined);
|
|
85
|
-
throw err;
|
|
86
|
-
}
|
|
71
|
+
await writePrivateTextAtomic(paths.secretDir, paths.keyPath, `${key}\n`, 'openrouter_api_key');
|
|
87
72
|
const record = {
|
|
88
73
|
schema: 'sks.openrouter-key.v1',
|
|
89
74
|
created_at: previous?.created_at || timestamp,
|
|
@@ -91,13 +76,13 @@ export async function writeStoredOpenRouterKey(value, input = {}) {
|
|
|
91
76
|
key_hash: crypto.createHash('sha256').update(key).digest('hex'),
|
|
92
77
|
key_preview: redactOpenRouterKey(key)
|
|
93
78
|
};
|
|
94
|
-
await
|
|
95
|
-
await fs.chmod(paths.metadataPath, 0o600).catch(() => undefined);
|
|
79
|
+
await writePrivateTextAtomic(paths.secretDir, paths.metadataPath, `${JSON.stringify(record, null, 2)}\n`, 'openrouter_api_key_metadata');
|
|
96
80
|
return record;
|
|
97
81
|
}
|
|
98
82
|
export async function readOpenRouterKeyRecord(paths) {
|
|
99
83
|
try {
|
|
100
|
-
const
|
|
84
|
+
const text = (await readPrivateCredentialFile(paths.secretDir, paths.metadataPath, 'openrouter_api_key_metadata')).bytes.toString('utf8');
|
|
85
|
+
const parsed = JSON.parse(text);
|
|
101
86
|
if (parsed.schema !== 'sks.openrouter-key.v1' || !parsed.key_hash || !parsed.key_preview)
|
|
102
87
|
return null;
|
|
103
88
|
return parsed;
|
|
@@ -106,6 +91,90 @@ export async function readOpenRouterKeyRecord(paths) {
|
|
|
106
91
|
return null;
|
|
107
92
|
}
|
|
108
93
|
}
|
|
94
|
+
export async function removeStoredOpenRouterKey(input) {
|
|
95
|
+
if (input.confirmed !== true) {
|
|
96
|
+
return {
|
|
97
|
+
schema: 'sks.openrouter-key-removal.v1',
|
|
98
|
+
removed: false,
|
|
99
|
+
removed_paths: [],
|
|
100
|
+
blockers: ['openrouter_credential_removal_confirmation_required']
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
const paths = input.paths || openRouterSecretPaths();
|
|
104
|
+
const secretDir = path.resolve(paths.secretDir);
|
|
105
|
+
const files = [path.resolve(paths.keyPath), path.resolve(paths.metadataPath)];
|
|
106
|
+
if (files.some((file) => file === secretDir || !isLexicallyConfined(secretDir, file))) {
|
|
107
|
+
return {
|
|
108
|
+
schema: 'sks.openrouter-key-removal.v1',
|
|
109
|
+
removed: false,
|
|
110
|
+
removed_paths: [],
|
|
111
|
+
blockers: ['openrouter_credential_path_outside_secret_store']
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
if (new Set(files).size !== files.length) {
|
|
115
|
+
return {
|
|
116
|
+
schema: 'sks.openrouter-key-removal.v1',
|
|
117
|
+
removed: false,
|
|
118
|
+
removed_paths: [],
|
|
119
|
+
blockers: ['openrouter_credential_path_collision']
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const secretDirStat = await fs.lstat(secretDir).catch((error) => {
|
|
123
|
+
if (error.code === 'ENOENT')
|
|
124
|
+
return null;
|
|
125
|
+
throw error;
|
|
126
|
+
});
|
|
127
|
+
if (!secretDirStat) {
|
|
128
|
+
return {
|
|
129
|
+
schema: 'sks.openrouter-key-removal.v1',
|
|
130
|
+
removed: false,
|
|
131
|
+
removed_paths: [],
|
|
132
|
+
blockers: []
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (!secretDirStat.isDirectory() || secretDirStat.isSymbolicLink()) {
|
|
136
|
+
return {
|
|
137
|
+
schema: 'sks.openrouter-key-removal.v1',
|
|
138
|
+
removed: false,
|
|
139
|
+
removed_paths: [],
|
|
140
|
+
blockers: ['openrouter_credential_secret_store_unsafe']
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
const existing = [];
|
|
144
|
+
for (const file of files) {
|
|
145
|
+
const inspected = await inspectConfinedPath(secretDir, file).catch(() => null);
|
|
146
|
+
if (!inspected) {
|
|
147
|
+
return {
|
|
148
|
+
schema: 'sks.openrouter-key-removal.v1',
|
|
149
|
+
removed: false,
|
|
150
|
+
removed_paths: [],
|
|
151
|
+
blockers: ['openrouter_credential_path_unsafe']
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
if (!inspected.exists)
|
|
155
|
+
continue;
|
|
156
|
+
if (inspected.leafSymlink || !inspected.stat?.isFile()) {
|
|
157
|
+
return {
|
|
158
|
+
schema: 'sks.openrouter-key-removal.v1',
|
|
159
|
+
removed: false,
|
|
160
|
+
removed_paths: [],
|
|
161
|
+
blockers: ['openrouter_credential_path_not_regular_file']
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
existing.push(file);
|
|
165
|
+
}
|
|
166
|
+
const removed = [];
|
|
167
|
+
for (const file of existing) {
|
|
168
|
+
await removeManagedPathVerified(secretDir, file);
|
|
169
|
+
removed.push(file);
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
schema: 'sks.openrouter-key-removal.v1',
|
|
173
|
+
removed: removed.length > 0,
|
|
174
|
+
removed_paths: removed,
|
|
175
|
+
blockers: []
|
|
176
|
+
};
|
|
177
|
+
}
|
|
109
178
|
async function ensureSecretDir(secretDir) {
|
|
110
179
|
await fs.mkdir(secretDir, { recursive: true, mode: 0o700 });
|
|
111
180
|
await fs.chmod(secretDir, 0o700).catch(() => undefined);
|