sneakoscope 8.1.2 → 8.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -58
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/bin/fast-inline.js +5 -51
- package/dist/cli/command-manifest-lite.js +2 -1
- package/dist/cli/command-registry.js +2 -1
- package/dist/cli/global-mode-router.js +1 -1
- package/dist/cli/install-helpers-codex-lb-config.js +112 -914
- package/dist/cli/install-helpers-codex-lb-shared.js +3 -44
- package/dist/cli/install-helpers-install-support.js +4 -20
- package/dist/cli/install-helpers.js +36 -2716
- package/dist/cli/router.js +9 -9
- package/dist/commands/bridge-command-output.js +111 -0
- package/dist/commands/bridge.js +320 -0
- package/dist/commands/codex-app.js +39 -123
- package/dist/commands/doctor-helpers.js +380 -0
- package/dist/commands/doctor-profile.js +3 -0
- package/dist/commands/doctor.js +114 -873
- package/dist/commands/mad-sks.js +0 -4
- package/dist/config/skills-manifest.json +1 -1
- package/dist/core/agents/agent-effort-policy.js +11 -21
- package/dist/core/agents/agent-recursion-guard.js +26 -0
- package/dist/core/agents/agent-runner-codex-exec.js +7 -9
- package/dist/core/architecture-hardening/contracts/contracts.js +225 -161
- package/dist/core/architecture-hardening/state/state-service.js +49 -13
- package/dist/core/auto-review.js +15 -9
- package/dist/core/codex/codex-config-guard.js +8 -0
- package/dist/core/codex-adapter.js +8 -10
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +7 -29
- package/dist/core/codex-app/codex-app-launcher.js +10 -12
- package/dist/core/codex-app/codex-app-restart.js +10 -12
- package/dist/core/codex-app/menubar/constants.js +2 -5
- package/dist/core/codex-app/menubar/global-install.js +3 -2
- package/dist/core/codex-app/menubar/installer/result.js +23 -0
- package/dist/core/codex-app/menubar/installer/runtime.js +28 -0
- package/dist/core/codex-app/menubar/installer.js +5 -96
- package/dist/core/codex-app/menubar/migration.js +1 -42
- package/dist/core/codex-app/openrouter-provider.js +0 -78
- package/dist/core/codex-app.js +97 -360
- package/dist/core/codex-control/codex-app-server-runtime-env.js +8 -28
- package/dist/core/codex-control/codex-current-image-path-real-probe.js +2 -3
- package/dist/core/codex-control/codex-current-web-search-probe.js +2 -3
- package/dist/core/codex-control/codex-sdk-adapter.js +5 -48
- package/dist/core/codex-control/codex-task-runner.js +16 -60
- package/dist/core/codex-control/desktop-bridge-launch-guard.js +227 -0
- package/dist/core/codex-exec-output-schema.js +11 -13
- package/dist/core/codex-lb/bridge-contracts.js +2 -0
- package/dist/core/codex-lb/bridge-runtime-validation/capability.js +104 -0
- package/dist/core/codex-lb/bridge-runtime-validation/catalog.js +52 -0
- package/dist/core/codex-lb/bridge-runtime-validation/shared.js +109 -0
- package/dist/core/codex-lb/bridge-runtime-validation/status.js +250 -0
- package/dist/core/codex-lb/bridge-runtime-validation.js +106 -0
- package/dist/core/codex-lb/capability-runner.js +505 -201
- package/dist/core/codex-lb/codex-lb-env.js +179 -12
- package/dist/core/codex-lb/combined-catalog/builder.js +86 -0
- package/dist/core/codex-lb/combined-catalog/contracts.js +5 -0
- package/dist/core/codex-lb/combined-catalog/normalize.js +170 -0
- package/dist/core/codex-lb/combined-catalog/persistence.js +207 -0
- package/dist/core/codex-lb/combined-catalog/shared.js +12 -0
- package/dist/core/codex-lb/combined-catalog/storage.js +171 -0
- package/dist/core/codex-lb/combined-catalog.js +12 -0
- package/dist/core/codex-lb/desktop-bridge/client.js +4 -1
- package/dist/core/codex-lb/desktop-bridge/header-policy.js +48 -63
- package/dist/core/codex-lb/desktop-bridge/http-forward.js +115 -83
- package/dist/core/codex-lb/desktop-bridge/launchd.js +20 -13
- package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +4 -1
- package/dist/core/codex-lb/desktop-bridge/security.js +381 -267
- package/dist/core/codex-lb/desktop-bridge/server.js +148 -19
- package/dist/core/codex-lb/desktop-bridge/state.js +56 -70
- package/dist/core/codex-lb/desktop-bridge/types.js +6 -1
- package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +281 -106
- package/dist/core/codex-lb/desktop-bridge-migration/historical-config.js +245 -0
- package/dist/core/codex-lb/desktop-bridge-migration/historical-toml.js +56 -0
- package/dist/core/codex-lb/desktop-bridge-migration/metadata.js +73 -0
- package/dist/core/codex-lb/desktop-bridge-migration/receipt.js +52 -0
- package/dist/core/codex-lb/desktop-bridge-migration/retired-runtime-cleanup.js +106 -0
- package/dist/core/codex-lb/desktop-bridge-migration/types.js +1 -0
- package/dist/core/codex-lb/desktop-bridge-migration.js +238 -0
- package/dist/core/codex-lb/desktop-controller-v3/catalog.js +257 -0
- package/dist/core/codex-lb/desktop-controller-v3/diagnostics.js +62 -0
- package/dist/core/codex-lb/desktop-controller-v3/lifecycle-commands.js +203 -0
- package/dist/core/codex-lb/desktop-controller-v3/live-probes.js +293 -0
- package/dist/core/codex-lb/desktop-controller-v3/preflight-probes.js +144 -0
- package/dist/core/codex-lb/desktop-controller-v3/provider-commands.js +182 -0
- package/dist/core/codex-lb/desktop-controller-v3/shared.js +279 -0
- package/dist/core/codex-lb/desktop-controller-v3/status.js +269 -0
- package/dist/core/codex-lb/desktop-controller-v3/types.js +1 -0
- package/dist/core/codex-lb/desktop-controller-v3/verification.js +89 -0
- package/dist/core/codex-lb/desktop-controller-v3.js +67 -0
- package/dist/core/codex-lb/desktop-controller.js +2 -1730
- package/dist/core/codex-lb/desktop-service.js +462 -627
- package/dist/core/codex-lb/migration-receipt.js +560 -118
- package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +37 -53
- package/dist/core/codex-lb/probes/bridge-probe.js +125 -64
- package/dist/core/codex-lb/probes/catalog-probe.js +36 -136
- package/dist/core/codex-lb/probes/computer-use-probe.js +63 -39
- package/dist/core/codex-lb/probes/image-generation-probe.js +98 -41
- package/dist/core/codex-lb/probes/probe-evidence.js +32 -33
- package/dist/core/codex-lb/probes/voice-realtime-probe.js +69 -43
- package/dist/core/codex-lb/provider-credentials/configuration.js +127 -0
- package/dist/core/codex-lb/provider-credentials/locks.js +11 -0
- package/dist/core/codex-lb/provider-credentials/private-snapshot.js +28 -0
- package/dist/core/codex-lb/provider-credentials/removal.js +33 -0
- package/dist/core/codex-lb/provider-credentials/resolution.js +59 -0
- package/dist/core/codex-lb/provider-credentials/runtime.js +55 -0
- package/dist/core/codex-lb/provider-credentials/types.js +1 -0
- package/dist/core/codex-lb/provider-credentials/validation-store.js +137 -0
- package/dist/core/codex-lb/provider-credentials.js +5 -0
- package/dist/core/codex-lb/provider-registry.js +323 -0
- package/dist/core/codex-lb/provider-route-policy.js +118 -0
- package/dist/core/codex-lb/request-route-resolver.js +172 -0
- package/dist/core/codex-lb/route-index.js +148 -0
- package/dist/core/codex-lb/trusted-deep-evidence.js +158 -286
- package/dist/core/commands/basic-cli.js +1 -1
- package/dist/core/commands/check-command.js +3 -10
- package/dist/core/commands/gate-result-contract.js +10 -14
- package/dist/core/commands/intent-normalization/normalizer.js +1 -1
- package/dist/core/commands/mad-sks-command.js +2 -6
- package/dist/core/commands/release-command.js +11 -1
- package/dist/core/commands/telegram-command.js +46 -6
- package/dist/core/doctor/doctor-readiness-matrix.js +9 -7
- package/dist/core/doctor/skill-legacy-surface.js +5 -5
- package/dist/core/evidence/v2/evidence-key.js +30 -3
- package/dist/core/feature-fixtures.js +8 -1
- package/dist/core/feature-registry.js +3 -2
- package/dist/core/hooks-runtime/stop-finalization.js +11 -1
- package/dist/core/hooks-runtime/subagent-context.js +15 -1
- package/dist/core/hooks-runtime/subagent-skill-availability-contract.js +1 -1
- package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
- package/dist/core/hooks-runtime.js +73 -87
- package/dist/core/image-ux-review/imagegen-adapter.js +108 -94
- package/dist/core/image-ux-review/real-callout-extractor.js +144 -14
- package/dist/core/imagegen/desktop-bridge-imagegen-target.js +77 -0
- package/dist/core/imagegen/imagegen-capability.js +43 -151
- package/dist/core/init/skills/filesystem.js +89 -0
- package/dist/core/init/skills/inventory.js +97 -0
- package/dist/core/init/skills/manifest.js +151 -0
- package/dist/core/init/skills.js +5 -325
- package/dist/core/init.js +0 -63
- package/dist/core/install/installed-package-smoke.js +1 -1
- package/dist/core/managed-assets/managed-assets-manifest.js +0 -1
- package/dist/core/policy/latest-version-guidance.js +15 -2
- package/dist/core/preflight/parallel-preflight-engine.js +27 -51
- package/dist/core/provider/provider-badge.js +9 -4
- package/dist/core/provider/provider-context.js +57 -107
- package/dist/core/providers/openrouter/openrouter-account.js +3 -1
- package/dist/core/providers/openrouter/openrouter-secret-store.js +91 -22
- package/dist/core/release/desktop-bridge-release-evidence.js +457 -0
- package/dist/core/release/gate-manifest.js +0 -1
- package/dist/core/release/main-push-guard.js +13 -0
- package/dist/core/release/main-push-receipt.js +11 -0
- package/dist/core/release/physical-release-gates.js +664 -61
- package/dist/core/release/publish-preflight.js +3 -3
- package/dist/core/release/release-gate-batch-runner.js +19 -6
- package/dist/core/release/release-gate-dag.js +33 -6
- package/dist/core/release/release-gate-node.js +1 -3
- package/dist/core/release/release-gate-output-contract.js +55 -0
- package/dist/core/release/release-pack-contract.js +1 -1
- package/dist/core/release/release-pack-receipt.js +209 -8
- package/dist/core/release/release-real-contract.js +1 -1
- package/dist/core/release/stage-publish.js +106 -28
- package/dist/core/retention/temp-lease.js +204 -0
- package/dist/core/retention.js +18 -195
- package/dist/core/routes.js +1 -2
- package/dist/core/safety/intent-contract/intent-contract.js +3 -3
- package/dist/core/subagents/naruto-host-credentials.js +6 -1
- package/dist/core/subagents/official-subagent-config.js +27 -9
- package/dist/core/subagents/official-subagent-runner.js +14 -31
- package/dist/core/subagents/wave-lifecycle.js +36 -8
- package/dist/core/subagents/wave-parent-guidance.js +2 -1
- package/dist/core/telegram/doctor.js +2 -2
- package/dist/core/telegram/liveness.js +10 -4
- package/dist/core/triwiki/context-graph/compiler/index.js +18 -18
- package/dist/core/update/update-migration-state/fast-mode-config.js +172 -0
- package/dist/core/update/update-migration-state/session-state-split.js +247 -0
- package/dist/core/update/update-migration-state/simple-stages.js +46 -0
- package/dist/core/update/update-migration-state.js +4 -481
- package/dist/core/version.js +1 -1
- package/dist/native/sks-menubar/Sources/OperationCoordinator.swift +28 -4
- package/dist/native/sks-menubar/Sources/OperationModels.swift +112 -2
- package/dist/native/sks-menubar/Sources/ProviderRouteExplanation.swift +21 -0
- package/dist/native/sks-menubar/Sources/ProvidersBridgeCatalog.swift +140 -0
- package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +107 -470
- package/dist/native/sks-menubar/Sources/ProvidersReliability.swift +27 -197
- package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +335 -269
- package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +233 -437
- package/dist/native/sks-menubar/Sources/RemoteCodingViewController.swift +16 -7
- package/dist/native/sks-menubar/Sources/SecureProcessEnvelope.swift +39 -9
- package/dist/native/sks-menubar/Sources/TelegramSupport.swift +56 -0
- package/dist/scripts/canonical-test-runner.js +7 -1
- package/dist/scripts/check-runtime-schemas.js +7 -2
- package/dist/scripts/codex-sdk-real-smoke-check.js +3 -1
- package/dist/scripts/desktop-bridge-capabilities-check.js +123 -0
- package/dist/scripts/{codex-lb-desktop-bridge-latency-check.js → desktop-bridge-latency-check.js} +61 -16
- package/dist/scripts/desktop-bridge-real-evidence-check.js +64 -0
- package/dist/scripts/{codex-lb-desktop-bridge-check.js → desktop-bridge-transport-check.js} +92 -155
- package/dist/scripts/desktop-bridge-transport-client.js +116 -0
- package/dist/scripts/desktop-bridge-unification-check.js +276 -0
- package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +12 -8
- package/dist/scripts/gate-policy-audit-check.js +52 -2
- package/dist/scripts/provider-badge-context-check.js +55 -12
- package/dist/scripts/publish-preflight.js +1 -2
- package/dist/scripts/release-affected-selector-check.js +1 -2
- package/dist/scripts/release-aggressive-resource-governor-check.js +1 -1
- package/dist/scripts/release-cache-glob-hashing-check.js +1 -0
- package/dist/scripts/release-cache-version-neutral-fixture-check.js +1 -0
- package/dist/scripts/release-full-parallelism-blackbox.js +1 -1
- package/dist/scripts/release-gate-batch-runner-check.js +9 -0
- package/dist/scripts/release-gate-dag-runner-check.js +6 -0
- package/dist/scripts/release-gate-dag-runner.js +4 -2
- package/dist/scripts/release-metadata-check.js +16 -3
- package/dist/scripts/release-pack-receipt.js +29 -6
- package/dist/scripts/release-physical-gates-check.js +39 -0
- package/dist/scripts/release-real-check.js +7 -5
- package/package.json +6 -8
- package/release-gates.v2.json +156 -157
- package/runtime-required-scripts.json +11 -11
- package/schemas/codex/desktop-bridge-status-v3.schema.json +286 -0
- package/schemas/codex/desktop-capabilities-v3.schema.json +198 -0
- package/schemas/release/release-gate-node.schema.json +2 -3
- package/dist/cli/codex-lb-setup-warning-output.js +0 -17
- package/dist/cli/install-helpers-codex-lb-chain.js +0 -529
- package/dist/commands/codex-lb.js +0 -1232
- package/dist/core/architecture-hardening/migration/migration.js +0 -111
- package/dist/core/codex-app/auxiliary-oauth-policy.js +0 -36
- package/dist/core/codex-app/catalog-compat/catalog-service.js +0 -104
- package/dist/core/codex-app/child-policy/child-policy.js +0 -35
- package/dist/core/codex-app/desktop-routing-snapshot.js +0 -233
- package/dist/core/codex-app/glm-model-profile.js +0 -1
- package/dist/core/codex-app/glm-profile-installer.js +0 -191
- package/dist/core/codex-app/glm-profile-schema.js +0 -42
- package/dist/core/codex-app/multi-provider-router.js +0 -391
- package/dist/core/codex-app/openrouter-activate.js +0 -405
- package/dist/core/codex-app/openrouter-model-catalog.js +0 -217
- package/dist/core/codex-app/provider-child-policy.js +0 -64
- package/dist/core/codex-app/provider-mode.js +0 -91
- package/dist/core/codex-app/provider-session-policy.js +0 -85
- package/dist/core/codex-app/session-policy/session-pinning.js +0 -68
- package/dist/core/codex-control/codex-lb-launch-recovery.js +0 -319
- package/dist/core/codex-lb/codex-lb-setup.js +0 -293
- package/dist/core/codex-lb/codex-lb-tool-catalog.js +0 -456
- package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +0 -290
- package/dist/core/codex-lb/desktop-center-credentials.js +0 -1083
- package/dist/core/codex-lb/desktop-mode.js +0 -32
- package/dist/core/codex-lb/desktop-real-evidence.js +0 -168
- package/dist/core/codex-lb/legacy-migration.js +0 -576
- package/dist/core/codex-lb/native-openai-transport/transport.js +0 -68
- package/dist/core/codex-lb/provider-routing/provider-router.js +0 -58
- package/dist/core/codex-lb/routing-truth.js +0 -327
- package/dist/core/imagegen/codex-lb-imagegen-target.js +0 -92
- package/dist/native/sks-menubar/Sources/ProvidersConnectTest.swift +0 -118
- package/dist/native/sks-menubar/Sources/ProvidersFastMode.swift +0 -17
- package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +0 -331
- package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +0 -260
- package/dist/scripts/codex-lb-catalog-passthrough-check.js +0 -120
- package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +0 -169
- package/dist/scripts/codex-lb-desktop-capabilities-check.js +0 -287
- package/dist/scripts/codex-lb-desktop-real-evidence-check.js +0 -123
- package/dist/scripts/codex-lb-fast-mode-truth-check.js +0 -114
- package/dist/scripts/codex-lb-fast-ui-preservation-check.js +0 -152
- package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +0 -231
|
@@ -1,51 +1,75 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import
|
|
3
|
-
import { constants as fsConstants } from 'node:fs';
|
|
4
|
-
import fsp from 'node:fs/promises';
|
|
5
|
-
import { randomBytes } from 'node:crypto';
|
|
6
|
-
import { assertTestHomeWriteAllowed, ensureDir, readText, writeBinaryAtomic } from '../core/fsx.js';
|
|
7
|
-
import { GLM_CODEX_CONFIG_PROVIDER_ID, GLM_52_OPENROUTER_MODEL, OPENROUTER_AUTH_COMMAND, OPENROUTER_AUTH_REFRESH_INTERVAL_MS, OPENROUTER_AUTH_TIMEOUT_MS, openRouterAuthCommandArgs, OPENROUTER_DEFAULT_PROFILE_ID, RETIRED_GLM_DESKTOP_CONFIG_PROFILE_IDS } from '../core/codex-app/openrouter-provider.js';
|
|
8
|
-
import { openRouterSecretPaths, resolveOpenRouterApiKey } from '../core/providers/openrouter/openrouter-secret-store.js';
|
|
9
|
-
import { reconcileRetiredSksConfigText } from '../core/auto-review.js';
|
|
10
|
-
import { ensureTrailingNewline, removeTopLevelTomlKeyIfValue, safeWriteCodexConfigToml, upsertTopLevelTomlString, upsertTomlTable } from '../core/codex-runtime/codex-desktop-config-policy.js';
|
|
11
|
-
import { codexLbConfigPath, normalizeCodexLbBaseUrl } from './install-helpers-codex-lb-shared.js';
|
|
2
|
+
import { ensureTrailingNewline, upsertTopLevelTomlString } from '../core/codex-runtime/codex-desktop-config-policy.js';
|
|
12
3
|
import { escapeRegExp } from '../core/text/regex.js';
|
|
13
|
-
|
|
14
|
-
export const
|
|
15
|
-
export const
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
4
|
+
export const DESKTOP_BRIDGE_MANAGED_MARKER = '# sks-desktop-bridge-managed';
|
|
5
|
+
export const DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER = '# sks-desktop-bridge-managed-base-url';
|
|
6
|
+
export const DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER = '# sks-desktop-bridge-managed-model-catalog';
|
|
7
|
+
export function upsertDesktopBridgeManagedConfig(text, input) {
|
|
8
|
+
const bridgeBaseUrl = normalizeManagedBridgeBaseUrl(input.bridgeBaseUrl);
|
|
9
|
+
const combinedCatalogPath = String(input.combinedCatalogPath || '').trim();
|
|
10
|
+
if (!combinedCatalogPath || !path.isAbsolute(combinedCatalogPath)) {
|
|
11
|
+
throw new Error('desktop_bridge_combined_catalog_path_invalid');
|
|
12
|
+
}
|
|
13
|
+
if (path.basename(combinedCatalogPath) !== 'sks-bridge-catalog.json') {
|
|
14
|
+
throw new Error('desktop_bridge_combined_catalog_path_invalid');
|
|
15
|
+
}
|
|
16
|
+
const orphanCleanup = removeDesktopBridgeOrphanManagedMarkers(String(text || ''));
|
|
17
|
+
let next = orphanCleanup.text;
|
|
18
|
+
const selectedProvider = topLevelTomlString(next, 'model_provider');
|
|
19
|
+
if (selectedProvider && (selectedProvider !== 'openai'
|
|
20
|
+
|| !topLevelHasLine(next, DESKTOP_BRIDGE_MANAGED_MARKER))) {
|
|
21
|
+
throw new Error('historical_user_owned_config_conflict:model_provider');
|
|
22
|
+
}
|
|
23
|
+
const existingBaseUrl = topLevelTomlString(next, 'openai_base_url');
|
|
24
|
+
const baseUrlOwned = topLevelHasLine(next, DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER);
|
|
25
|
+
if (existingBaseUrl && !baseUrlOwned) {
|
|
26
|
+
throw new Error('historical_user_owned_config_conflict:openai_base_url');
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
const existingCatalog = topLevelTomlString(next, 'model_catalog_json');
|
|
29
|
+
const catalogOwned = topLevelHasLine(next, DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER);
|
|
30
|
+
if (existingCatalog && !catalogOwned) {
|
|
31
|
+
throw new Error('historical_user_owned_config_conflict:model_catalog_json');
|
|
32
|
+
}
|
|
33
|
+
next = removeManagedBridgeTopLevelBindings(next);
|
|
34
|
+
next = upsertTopLevelTomlString(next, 'model_provider', 'openai');
|
|
35
|
+
next = addTopLevelMarkerBeforeKey(next, 'model_provider', DESKTOP_BRIDGE_MANAGED_MARKER);
|
|
36
|
+
next = upsertTopLevelTomlString(next, 'openai_base_url', bridgeBaseUrl);
|
|
37
|
+
next = addTopLevelMarkerBeforeKey(next, 'openai_base_url', DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER);
|
|
38
|
+
next = upsertTopLevelTomlString(next, 'model_catalog_json', combinedCatalogPath);
|
|
39
|
+
next = addTopLevelMarkerBeforeKey(next, 'model_catalog_json', DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER);
|
|
40
|
+
return ensureTrailingNewline(next);
|
|
41
|
+
}
|
|
42
|
+
export function removeDesktopBridgeManagedConfig(text) {
|
|
43
|
+
let next = String(text || '');
|
|
44
|
+
const selectedProvider = topLevelTomlString(next, 'model_provider');
|
|
45
|
+
const baseUrl = topLevelTomlString(next, 'openai_base_url');
|
|
31
46
|
const catalog = topLevelTomlString(next, 'model_catalog_json');
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
47
|
+
const owned = [
|
|
48
|
+
[DESKTOP_BRIDGE_MANAGED_MARKER, 'model_provider', selectedProvider === 'openai'],
|
|
49
|
+
[DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER, 'openai_base_url', Boolean(baseUrl)],
|
|
50
|
+
[DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER, 'model_catalog_json', Boolean(catalog)]
|
|
51
|
+
];
|
|
52
|
+
for (const [marker, key, valid] of owned) {
|
|
53
|
+
if (!topLevelHasLine(next, marker)) {
|
|
54
|
+
const value = key === 'model_provider' ? selectedProvider : key === 'openai_base_url' ? baseUrl : catalog;
|
|
55
|
+
if (value)
|
|
56
|
+
throw new Error(`desktop_bridge_unmanage_ownership_missing:${key}`);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (!valid)
|
|
60
|
+
throw new Error(`desktop_bridge_unmanage_owned_value_invalid:${key}`);
|
|
61
|
+
next = removeTopLevelTomlKey(next, key);
|
|
62
|
+
next = removeTopLevelLine(next, marker);
|
|
36
63
|
}
|
|
37
|
-
return next;
|
|
64
|
+
return ensureTrailingNewline(next);
|
|
38
65
|
}
|
|
39
|
-
export function
|
|
66
|
+
export function removeDesktopBridgeOrphanManagedMarkers(text) {
|
|
40
67
|
const source = String(text || '');
|
|
41
68
|
const selectedProvider = topLevelTomlString(source, 'model_provider');
|
|
42
69
|
const markerTargets = [
|
|
43
|
-
[
|
|
44
|
-
[
|
|
45
|
-
[
|
|
46
|
-
[CODEX_LB_MODEL_CATALOG_MARKER, Boolean(topLevelTomlString(source, 'model_catalog_json'))],
|
|
47
|
-
[CODEX_LB_PROVIDER_SELECTION_MARKER, selectedProvider === 'codex-lb'],
|
|
48
|
-
[CODEX_LB_OAUTH_SELECTION_MARKER, selectedProvider === 'openai']
|
|
70
|
+
[DESKTOP_BRIDGE_MANAGED_MARKER, selectedProvider === 'openai'],
|
|
71
|
+
[DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER, Boolean(topLevelTomlString(source, 'openai_base_url'))],
|
|
72
|
+
[DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER, Boolean(topLevelTomlString(source, 'model_catalog_json'))]
|
|
49
73
|
];
|
|
50
74
|
const orphanMarkers = markerTargets
|
|
51
75
|
.filter(([marker, hasManagedContent]) => topLevelHasLine(source, marker) && !hasManagedContent)
|
|
@@ -54,891 +78,21 @@ export function removeCodexLbOrphanManagedMarkers(text) {
|
|
|
54
78
|
for (const marker of orphanMarkers)
|
|
55
79
|
next = removeTopLevelLine(next, marker);
|
|
56
80
|
return {
|
|
57
|
-
schema: 'sks.
|
|
81
|
+
schema: 'sks.desktop-bridge-orphan-managed-marker-cleanup.v1',
|
|
58
82
|
changed: orphanMarkers.length > 0,
|
|
59
83
|
orphan_markers: orphanMarkers,
|
|
60
84
|
text: next
|
|
61
85
|
};
|
|
62
86
|
}
|
|
63
|
-
|
|
64
|
-
let next = releaseSksManagedThirdPartySelection(removeCodexLbOrphanManagedMarkers(String(text || '')).text);
|
|
65
|
-
const selectedProvider = topLevelTomlString(next, 'model_provider');
|
|
66
|
-
if (selectedProvider && selectedProvider !== 'openai' && selectedProvider !== 'codex-lb') {
|
|
67
|
-
throw new Error('codex_lb_user_owned_model_provider_conflict');
|
|
68
|
-
}
|
|
69
|
-
if (selectedProvider === 'codex-lb'
|
|
70
|
-
&& !topLevelHasLine(next, CODEX_LB_DESKTOP_COMPAT_MARKER)
|
|
71
|
-
&& !topLevelHasLine(next, CODEX_LB_PROVIDER_SELECTION_MARKER)) {
|
|
72
|
-
throw new Error('codex_lb_legacy_desktop_config_requires_migration');
|
|
73
|
-
}
|
|
74
|
-
if (topLevelHasLine(next, LEGACY_CODEX_LB_OPENAI_ROUTING_MARKER)
|
|
75
|
-
&& !topLevelHasLine(next, CODEX_LB_DESKTOP_BRIDGE_MARKER)) {
|
|
76
|
-
throw new Error('codex_lb_legacy_desktop_config_requires_migration');
|
|
77
|
-
}
|
|
78
|
-
const modelCatalog = topLevelTomlString(next, 'model_catalog_json');
|
|
79
|
-
if (modelCatalog && !topLevelHasLine(next, CODEX_LB_MODEL_CATALOG_MARKER)) {
|
|
80
|
-
throw new Error('codex_lb_user_owned_model_catalog_json_conflict');
|
|
81
|
-
}
|
|
82
|
-
next = removeManagedCodexLbSelection(next);
|
|
83
|
-
next = removeTopLevelLine(next, CODEX_LB_DESKTOP_COMPAT_MARKER);
|
|
84
|
-
next = removeManagedModelCatalogJson(next);
|
|
85
|
-
next = upsertManagedTopLevelTomlString(next, 'openai_base_url', input.bridgeBaseUrl, CODEX_LB_DESKTOP_BRIDGE_MARKER, [LEGACY_CODEX_LB_OPENAI_ROUTING_MARKER]);
|
|
86
|
-
next = upsertTopLevelTomlString(next, 'model_provider', 'openai');
|
|
87
|
-
next = upsertTomlTable(next, 'model_providers.codex-lb', cliProviderBlock(input.remoteBaseUrl));
|
|
88
|
-
return ensureTrailingNewline(upsertExplicitCodexProviderMode(next, 'codex-lb'));
|
|
89
|
-
}
|
|
90
|
-
export function upsertCodexLbCompatDesktopConfig(text, input) {
|
|
91
|
-
let next = removeManagedDesktopBridgeRouting(String(text || ''));
|
|
92
|
-
next = removeManagedModelCatalogJson(next);
|
|
93
|
-
next = upsertManagedTopLevelTomlString(next, 'model_provider', 'codex-lb', CODEX_LB_DESKTOP_COMPAT_MARKER);
|
|
94
|
-
const block = [
|
|
95
|
-
'[model_providers.codex-lb]',
|
|
96
|
-
'name = "OpenAI"',
|
|
97
|
-
`base_url = ${JSON.stringify(input.remoteBaseUrl)}`,
|
|
98
|
-
'wire_api = "responses"',
|
|
99
|
-
'requires_openai_auth = true',
|
|
100
|
-
'supports_websockets = true',
|
|
101
|
-
'env_http_headers = { "X-Codex-LB-API-Key" = "CODEX_LB_API_KEY" }'
|
|
102
|
-
].join('\n');
|
|
103
|
-
next = upsertTomlTable(next, 'model_providers.codex-lb', block);
|
|
104
|
-
return ensureTrailingNewline(next);
|
|
105
|
-
}
|
|
106
|
-
export function upsertCodexLbCliProviderConfig(text, input) {
|
|
107
|
-
const cleaned = removeCodexLbOrphanManagedMarkers(String(text || '')).text;
|
|
108
|
-
if (topLevelHasLine(cleaned, LEGACY_CODEX_LB_OPENAI_ROUTING_MARKER)) {
|
|
109
|
-
throw new Error('codex_lb_legacy_desktop_config_requires_migration');
|
|
110
|
-
}
|
|
111
|
-
const cliBase = input.selectGlobally === true
|
|
112
|
-
? releaseSksManagedThirdPartySelection(removeCodexLbManagedDesktopConfig(cleaned))
|
|
113
|
-
: removeCodexLbManagedDesktopConfig(cleaned);
|
|
114
|
-
const claimedSelection = input.selectGlobally === true
|
|
115
|
-
&& topLevelTomlString(cliBase, 'model_provider') === 'codex-lb'
|
|
116
|
-
&& !topLevelHasLine(cliBase, CODEX_LB_PROVIDER_SELECTION_MARKER)
|
|
117
|
-
? addTopLevelMarkerBeforeKey(cliBase, 'model_provider', CODEX_LB_PROVIDER_SELECTION_MARKER)
|
|
118
|
-
: cliBase;
|
|
119
|
-
let next = input.selectGlobally === true
|
|
120
|
-
? upsertManagedTopLevelTomlString(removeTopLevelTomlKeyIfValue(removeManagedOAuthSelection(claimedSelection), 'model_provider', 'openai'), 'model_provider', 'codex-lb', CODEX_LB_PROVIDER_SELECTION_MARKER)
|
|
121
|
-
: removeManagedCodexLbSelection(cliBase);
|
|
122
|
-
next = upsertTomlTable(next, 'model_providers.codex-lb', cliProviderBlock(input.remoteBaseUrl));
|
|
123
|
-
return ensureTrailingNewline(next);
|
|
124
|
-
}
|
|
125
|
-
export function restoreCodexLbOAuthSelectionConfig(text) {
|
|
126
|
-
let next = removeManagedCodexLbSelection(String(text || ''));
|
|
127
|
-
next = upsertManagedTopLevelTomlString(next, 'model_provider', 'openai', CODEX_LB_OAUTH_SELECTION_MARKER);
|
|
128
|
-
return ensureTrailingNewline(upsertExplicitCodexProviderMode(next, 'chatgpt-oauth'));
|
|
129
|
-
}
|
|
130
|
-
export function removeManagedCodexLbSelection(text) {
|
|
131
|
-
let next = removeTopLevelTomlKeyIfValue(String(text || ''), 'model_provider', 'codex-lb');
|
|
132
|
-
next = removeTopLevelLine(next, CODEX_LB_PROVIDER_SELECTION_MARKER);
|
|
133
|
-
return next;
|
|
134
|
-
}
|
|
135
|
-
function removeManagedOAuthSelection(text) {
|
|
136
|
-
let next = String(text || '');
|
|
137
|
-
if (topLevelHasLine(next, CODEX_LB_OAUTH_SELECTION_MARKER)) {
|
|
138
|
-
next = removeTopLevelTomlKeyIfValue(next, 'model_provider', 'openai');
|
|
139
|
-
next = removeTopLevelLine(next, CODEX_LB_OAUTH_SELECTION_MARKER);
|
|
140
|
-
}
|
|
141
|
-
return next;
|
|
142
|
-
}
|
|
143
|
-
export function removeCodexLbManagedDesktopConfig(text) {
|
|
144
|
-
let next = removeManagedDesktopBridgeRouting(removeCodexLbOrphanManagedMarkers(String(text || '')).text);
|
|
145
|
-
if (topLevelHasLine(next, CODEX_LB_DESKTOP_COMPAT_MARKER)) {
|
|
146
|
-
next = removeTopLevelTomlKeyIfValue(next, 'model_provider', 'codex-lb');
|
|
147
|
-
next = removeTopLevelLine(next, CODEX_LB_DESKTOP_COMPAT_MARKER);
|
|
148
|
-
}
|
|
149
|
-
next = removeManagedModelCatalogJson(next);
|
|
150
|
-
if (topLevelTomlString(next, 'model_provider') === 'codex-lb') {
|
|
151
|
-
next = restoreCodexLbOAuthSelectionConfig(next);
|
|
152
|
-
}
|
|
153
|
-
return ensureTrailingNewline(next);
|
|
154
|
-
}
|
|
155
|
-
export function removeManagedModelCatalogJson(text) {
|
|
156
|
-
if (!topLevelHasLine(text, CODEX_LB_MODEL_CATALOG_MARKER))
|
|
157
|
-
return text;
|
|
158
|
-
const withoutKey = removeTopLevelTomlKey(text, 'model_catalog_json');
|
|
159
|
-
return removeTopLevelLine(withoutKey, CODEX_LB_MODEL_CATALOG_MARKER);
|
|
160
|
-
}
|
|
161
|
-
export function removeManagedDesktopBridgeRouting(text) {
|
|
162
|
-
const markers = [
|
|
163
|
-
CODEX_LB_DESKTOP_BRIDGE_MARKER,
|
|
164
|
-
LEGACY_CODEX_LB_OPENAI_ROUTING_MARKER
|
|
165
|
-
].filter((marker) => topLevelHasLine(text, marker));
|
|
166
|
-
if (markers.length === 0)
|
|
167
|
-
return text;
|
|
168
|
-
let next = removeTopLevelTomlKey(text, 'openai_base_url');
|
|
169
|
-
for (const marker of markers)
|
|
170
|
-
next = removeTopLevelLine(next, marker);
|
|
171
|
-
return next;
|
|
172
|
-
}
|
|
173
|
-
export function upsertCodexLbConfig(text = '', baseUrl, selectDefault = true) {
|
|
174
|
-
return upsertCodexLbCliProviderConfig(text, {
|
|
175
|
-
remoteBaseUrl: String(baseUrl || ''),
|
|
176
|
-
selectGlobally: selectDefault === true
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
function cliProviderBlock(remoteBaseUrl) {
|
|
180
|
-
return [
|
|
181
|
-
'[model_providers.codex-lb]',
|
|
182
|
-
'name = "codex-lb"',
|
|
183
|
-
`base_url = ${JSON.stringify(remoteBaseUrl)}`,
|
|
184
|
-
'wire_api = "responses"',
|
|
185
|
-
'env_key = "CODEX_LB_API_KEY"',
|
|
186
|
-
'supports_websockets = true',
|
|
187
|
-
'requires_openai_auth = false'
|
|
188
|
-
].join('\n');
|
|
189
|
-
}
|
|
190
|
-
function upsertManagedTopLevelTomlString(text, key, value, marker, legacyMarkers = []) {
|
|
191
|
-
const acceptedMarkers = [marker, ...legacyMarkers];
|
|
192
|
-
const existing = topLevelTomlString(text, key);
|
|
193
|
-
const ownedMarker = acceptedMarkers.find((candidate) => topLevelHasLine(text, candidate));
|
|
194
|
-
if (existing && !ownedMarker) {
|
|
195
|
-
if (existing === value)
|
|
196
|
-
return text;
|
|
197
|
-
throw new Error(`codex_lb_user_owned_${key}_conflict`);
|
|
198
|
-
}
|
|
199
|
-
let next = text;
|
|
200
|
-
for (const candidate of acceptedMarkers)
|
|
201
|
-
next = removeTopLevelLine(next, candidate);
|
|
202
|
-
next = upsertTopLevelTomlString(next, key, value);
|
|
203
|
-
return addTopLevelMarkerBeforeKey(next, key, marker);
|
|
204
|
-
}
|
|
205
|
-
function removeTopLevelTomlKey(text, key) {
|
|
206
|
-
const lines = String(text || '').split('\n');
|
|
207
|
-
const firstTable = lines.findIndex((line) => /^\s*\[.+\]\s*$/.test(line));
|
|
208
|
-
const end = firstTable === -1 ? lines.length : firstTable;
|
|
209
|
-
const keyPattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=`);
|
|
210
|
-
return lines
|
|
211
|
-
.filter((line, index) => index >= end || !keyPattern.test(line))
|
|
212
|
-
.join('\n')
|
|
213
|
-
.replace(/^\n+/, '')
|
|
214
|
-
.replace(/\n{3,}/g, '\n\n');
|
|
215
|
-
}
|
|
216
|
-
const CODEX_LB_SHARED_OPENAI_ROUTING_MARKER = '# sks-codex-lb-managed-openai-base-url';
|
|
217
|
-
export function codexLbSharedOpenAiRoutingState(text = '', baseUrl = '') {
|
|
218
|
-
const expectedBaseUrl = normalizeCodexLbBaseUrl(baseUrl);
|
|
219
|
-
const configuredBaseUrl = topLevelTomlString(text, 'openai_base_url');
|
|
220
|
-
return {
|
|
221
|
-
status: !configuredBaseUrl ? 'missing' : configuredBaseUrl === expectedBaseUrl ? 'matched' : 'conflict',
|
|
222
|
-
expected_base_url: expectedBaseUrl,
|
|
223
|
-
configured_base_url: configuredBaseUrl || null,
|
|
224
|
-
managed: topLevelHasLine(text, CODEX_LB_SHARED_OPENAI_ROUTING_MARKER)
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
export function upsertCodexLbSharedOpenAiRouting(text = '', baseUrl = '') {
|
|
228
|
-
const state = codexLbSharedOpenAiRoutingState(text, baseUrl);
|
|
229
|
-
if (!state.expected_base_url)
|
|
230
|
-
return { ...state, routing_status: state.status, ok: false, status: 'missing_base_url', text: String(text || '') };
|
|
231
|
-
if (state.status === 'conflict')
|
|
232
|
-
return { ...state, routing_status: state.status, ok: false, status: 'conflicting_user_openai_base_url', text: String(text || '') };
|
|
233
|
-
if (state.status === 'matched' && state.managed) {
|
|
234
|
-
return { ...state, routing_status: state.status, ok: true, status: 'present', text: String(text || '') };
|
|
235
|
-
}
|
|
236
|
-
const withValue = state.status === 'matched'
|
|
237
|
-
? String(text || '')
|
|
238
|
-
: upsertTopLevelTomlString(text, 'openai_base_url', state.expected_base_url);
|
|
239
|
-
const next = addTopLevelMarkerBeforeKey(withValue, 'openai_base_url', CODEX_LB_SHARED_OPENAI_ROUTING_MARKER);
|
|
240
|
-
return { ...state, routing_status: state.status, ok: true, status: 'added', text: `${next.trim()}\n`, managed: true };
|
|
241
|
-
}
|
|
242
|
-
export function removeCodexLbSharedOpenAiRouting(text = '', baseUrl = '') {
|
|
243
|
-
const state = codexLbSharedOpenAiRoutingState(text, baseUrl);
|
|
244
|
-
if (state.status === 'missing' && state.managed) {
|
|
245
|
-
return {
|
|
246
|
-
...state,
|
|
247
|
-
changed: true,
|
|
248
|
-
text: removeTopLevelLine(String(text || ''), CODEX_LB_SHARED_OPENAI_ROUTING_MARKER)
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
if (state.status !== 'matched' || !state.managed)
|
|
252
|
-
return { ...state, changed: false, text: String(text || '') };
|
|
253
|
-
const withoutValue = removeTopLevelTomlKeyIfValue(text, 'openai_base_url', state.expected_base_url);
|
|
254
|
-
return {
|
|
255
|
-
...state,
|
|
256
|
-
changed: true,
|
|
257
|
-
text: removeTopLevelLine(withoutValue, CODEX_LB_SHARED_OPENAI_ROUTING_MARKER)
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
export function upsertCodexAppGlmConfig(text = '', input = {}) {
|
|
261
|
-
let next = String(text || '');
|
|
262
|
-
const authEnv = { ...(input.env || process.env), ...(input.home ? { HOME: input.home } : {}) };
|
|
263
|
-
const authArgs = openRouterAuthCommandArgs(openRouterSecretPaths(authEnv).keyPath);
|
|
264
|
-
const providerBlock = [
|
|
265
|
-
`[model_providers.${GLM_CODEX_CONFIG_PROVIDER_ID}]`,
|
|
266
|
-
'name = "OpenRouter"',
|
|
267
|
-
'base_url = "https://openrouter.ai/api/v1"',
|
|
268
|
-
'wire_api = "responses"',
|
|
269
|
-
'requires_openai_auth = false'
|
|
270
|
-
].join('\n');
|
|
271
|
-
next = upsertTomlTable(next, `model_providers.${GLM_CODEX_CONFIG_PROVIDER_ID}`, providerBlock);
|
|
272
|
-
const authBlock = [
|
|
273
|
-
`[model_providers.${GLM_CODEX_CONFIG_PROVIDER_ID}.auth]`,
|
|
274
|
-
`command = ${JSON.stringify(OPENROUTER_AUTH_COMMAND)}`,
|
|
275
|
-
`args = [${authArgs.map((value) => JSON.stringify(value)).join(', ')}]`,
|
|
276
|
-
`timeout_ms = ${OPENROUTER_AUTH_TIMEOUT_MS}`,
|
|
277
|
-
`refresh_interval_ms = ${OPENROUTER_AUTH_REFRESH_INTERVAL_MS}`
|
|
278
|
-
].join('\n');
|
|
279
|
-
next = upsertTomlTable(next, `model_providers.${GLM_CODEX_CONFIG_PROVIDER_ID}.auth`, authBlock);
|
|
280
|
-
next = next.replace(new RegExp(`\\n+\\[model_providers\\.${GLM_CODEX_CONFIG_PROVIDER_ID}\\.auth\\]`), `\n\n[model_providers.${GLM_CODEX_CONFIG_PROVIDER_ID}.auth]`);
|
|
281
|
-
next = reconcileRetiredSksConfigText(next).text;
|
|
282
|
-
return `${next.trim()}\n`;
|
|
283
|
-
}
|
|
284
|
-
export async function ensureGlobalCodexAppGlmProfile(opts = {}) {
|
|
285
|
-
const env = opts.env || process.env;
|
|
286
|
-
if (env.SKS_SKIP_CODEX_GLM_PROFILE_REPAIR === '1' && opts.force !== true) {
|
|
287
|
-
return { ok: true, status: 'skipped', reason: 'SKS_SKIP_CODEX_GLM_PROFILE_REPAIR=1' };
|
|
288
|
-
}
|
|
289
|
-
const home = opts.home || env.HOME || os.homedir();
|
|
290
|
-
const configPath = opts.configPath || codexLbConfigPath(home);
|
|
291
|
-
try {
|
|
292
|
-
await ensureDir(path.dirname(configPath));
|
|
293
|
-
const current = await readText(configPath, '');
|
|
294
|
-
const next = upsertCodexAppGlmConfig(current, { home, env: opts.env });
|
|
295
|
-
const safeWrite = await safeWriteCodexConfigToml(configPath, current, next, 'openrouter-provider');
|
|
296
|
-
return {
|
|
297
|
-
...safeWrite,
|
|
298
|
-
status: safeWrite.status === 'written' ? 'updated' : safeWrite.status,
|
|
299
|
-
provider: GLM_CODEX_CONFIG_PROVIDER_ID,
|
|
300
|
-
model: GLM_52_OPENROUTER_MODEL,
|
|
301
|
-
codex_config_profile: OPENROUTER_DEFAULT_PROFILE_ID,
|
|
302
|
-
reasoning_profiles: [],
|
|
303
|
-
retired_glm_profiles: [...RETIRED_GLM_DESKTOP_CONFIG_PROFILE_IDS]
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
catch (err) {
|
|
307
|
-
return {
|
|
308
|
-
ok: false,
|
|
309
|
-
status: 'failed',
|
|
310
|
-
config_path: configPath,
|
|
311
|
-
error: err.message,
|
|
312
|
-
provider: GLM_CODEX_CONFIG_PROVIDER_ID,
|
|
313
|
-
model: GLM_52_OPENROUTER_MODEL,
|
|
314
|
-
codex_config_profile: OPENROUTER_DEFAULT_PROFILE_ID,
|
|
315
|
-
reasoning_profiles: [],
|
|
316
|
-
retired_glm_profiles: [...RETIRED_GLM_DESKTOP_CONFIG_PROFILE_IDS]
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
export async function ensureStoredOpenRouterProviderDuringInstall(opts = {}) {
|
|
321
|
-
const home = opts.home || opts.env?.HOME || process.env.HOME || os.homedir();
|
|
322
|
-
const env = { ...(opts.env || process.env), HOME: home };
|
|
323
|
-
const key = await resolveOpenRouterApiKey({ env });
|
|
324
|
-
if (!key.key) {
|
|
325
|
-
return {
|
|
326
|
-
schema: 'sks.openrouter-provider-upgrade-repair.v1',
|
|
327
|
-
ok: true,
|
|
328
|
-
status: 'skipped',
|
|
329
|
-
reason: 'openrouter_key_missing',
|
|
330
|
-
key_present: false,
|
|
331
|
-
key_source: null,
|
|
332
|
-
blockers: [],
|
|
333
|
-
warnings: key.warnings
|
|
334
|
-
};
|
|
335
|
-
}
|
|
336
|
-
const repair = await ensureGlobalCodexAppGlmProfile({
|
|
337
|
-
...opts,
|
|
338
|
-
home,
|
|
339
|
-
env
|
|
340
|
-
});
|
|
341
|
-
const configPath = repair?.config_path || opts.configPath || codexLbConfigPath(home);
|
|
342
|
-
const { ensureOpenRouterModelCatalog } = await import('../core/codex-app/openrouter-model-catalog.js');
|
|
343
|
-
const catalogRepair = await ensureOpenRouterModelCatalog({ configPath, home, env })
|
|
344
|
-
.catch((err) => ({ ok: false, status: 'failed', error: err?.message || String(err) }));
|
|
345
|
-
const ok = repair?.ok !== false && catalogRepair?.ok !== false;
|
|
346
|
-
return {
|
|
347
|
-
schema: 'sks.openrouter-provider-upgrade-repair.v1',
|
|
348
|
-
ok,
|
|
349
|
-
status: ok ? repair?.status || 'present' : 'failed',
|
|
350
|
-
reason: ok ? 'stored_openrouter_key_provider_reconciled' : 'stored_openrouter_key_provider_repair_failed',
|
|
351
|
-
key_present: true,
|
|
352
|
-
key_source: key.source,
|
|
353
|
-
config_path: configPath,
|
|
354
|
-
provider: GLM_CODEX_CONFIG_PROVIDER_ID,
|
|
355
|
-
blockers: ok ? [] : ['openrouter_provider_repair_failed'],
|
|
356
|
-
warnings: key.warnings,
|
|
357
|
-
repair,
|
|
358
|
-
model_catalog_repair: catalogRepair
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
export function detectCodexLbSetupDrift(state = {}) {
|
|
362
|
-
const drift = [];
|
|
363
|
-
if (state.useDefaultProvider && state.selected !== true)
|
|
364
|
-
drift.push('default_provider_not_selected');
|
|
365
|
-
if (!state.useDefaultProvider && state.selected === true)
|
|
366
|
-
drift.push('default_provider_selected_despite_no_default_provider');
|
|
367
|
-
if (state.writeEnvFile && state.envFile !== true)
|
|
368
|
-
drift.push('env_file_not_written');
|
|
369
|
-
if (!state.writeEnvFile && state.beforeState && state.afterState && state.beforeState.envHash !== state.afterState.envHash)
|
|
370
|
-
drift.push('env_file_changed_despite_no_env_file');
|
|
371
|
-
if (!state.writeEnvFile && !state.beforeState && state.envFile === true)
|
|
372
|
-
drift.push('env_file_written_despite_no_env_file');
|
|
373
|
-
if (!state.storeKeychain && state.keychain?.status && state.keychain.status !== 'skipped')
|
|
374
|
-
drift.push('keychain_touched_despite_no_keychain');
|
|
375
|
-
if (!state.syncLaunchctl && state.codexEnvironment?.launch_environment?.status === 'synced')
|
|
376
|
-
drift.push('launchctl_base_url_synced_despite_no_launchctl');
|
|
377
|
-
if (state.codexEnvironment?.launch_environment?.secret_env_cleanup?.status === 'partial')
|
|
378
|
-
drift.push('launchctl_secret_env_cleanup_incomplete');
|
|
379
|
-
if (state.shellProfile === 'skip' && state.shellProfileResult?.status === 'installed')
|
|
380
|
-
drift.push('shell_profile_written_despite_skip');
|
|
381
|
-
if (state.shellProfile === 'skip' && state.beforeState && state.afterState && state.beforeState.profileHash !== state.afterState.profileHash)
|
|
382
|
-
drift.push('shell_profile_changed_despite_skip');
|
|
383
|
-
return drift;
|
|
384
|
-
}
|
|
385
|
-
export async function captureCodexLbSetupWriteState({ home, configPath, envPath, metadataPath, shellProfile } = {}) {
|
|
386
|
-
const profileFiles = profileFilesForDrift(home, shellProfile);
|
|
387
|
-
const paths = [configPath, envPath, metadataPath, ...profileFiles].filter(Boolean);
|
|
388
|
-
const files = await Promise.all(paths.map(captureSetupFile));
|
|
389
|
-
const hashesByPath = new Map(await Promise.all(files.map(async (file) => [file.path, await capturedSetupFileHash(file)])));
|
|
390
|
-
const hashForPath = (file) => file ? hashesByPath.get(file) || 'missing' : 'missing';
|
|
391
|
-
return {
|
|
392
|
-
configHash: hashForPath(configPath),
|
|
393
|
-
envHash: hashForPath(envPath),
|
|
394
|
-
metadataHash: hashForPath(metadataPath),
|
|
395
|
-
profileHash: profileFiles.map(hashForPath).join('|'),
|
|
396
|
-
files,
|
|
397
|
-
stateHash: await sha256Text(JSON.stringify(files))
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
export async function writeCodexLbSetupFileIfUnchanged(input) {
|
|
401
|
-
const file = path.resolve(input.file);
|
|
402
|
-
if (path.resolve(String(input.expected.path || '')) !== file) {
|
|
403
|
-
return {
|
|
404
|
-
ok: false,
|
|
405
|
-
status: 'write_failed',
|
|
406
|
-
installed: false,
|
|
407
|
-
expected_after: {
|
|
408
|
-
path: file,
|
|
409
|
-
existed: true,
|
|
410
|
-
kind: 'regular',
|
|
411
|
-
bytes_base64: Buffer.from(input.text).toString('base64'),
|
|
412
|
-
mode: Number(input.mode) & 0o777
|
|
413
|
-
},
|
|
414
|
-
error: 'setup_snapshot_path_mismatch'
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
const expected = { ...input.expected, path: file };
|
|
418
|
-
const mode = Number(input.mode) & 0o777;
|
|
419
|
-
const expectedAfter = {
|
|
420
|
-
path: file,
|
|
421
|
-
existed: true,
|
|
422
|
-
kind: 'regular',
|
|
423
|
-
bytes_base64: Buffer.from(input.text).toString('base64'),
|
|
424
|
-
mode
|
|
425
|
-
};
|
|
426
|
-
if (expected.existed === true && expected.kind !== 'regular') {
|
|
427
|
-
return {
|
|
428
|
-
ok: false,
|
|
429
|
-
status: 'unsafe_setup_write_target',
|
|
430
|
-
installed: false,
|
|
431
|
-
expected_after: expectedAfter
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
|
-
assertTestHomeWriteAllowed(file);
|
|
435
|
-
await ensureDir(path.dirname(file));
|
|
436
|
-
const token = `${Date.now().toString(36)}-${process.pid}-${randomBytes(6).toString('hex')}`;
|
|
437
|
-
const claimedPath = `${file}.sks-setup-claimed-${token}`;
|
|
438
|
-
const candidatePath = `${file}.sks-setup-candidate-${token}`;
|
|
439
|
-
let claimed = false;
|
|
440
|
-
let installed = false;
|
|
441
|
-
try {
|
|
442
|
-
await writeBinaryAtomic(candidatePath, Buffer.from(input.text), { mode });
|
|
443
|
-
const observed = await captureSetupFile(file);
|
|
444
|
-
if (!setupFileStatesEqual(observed, expected)) {
|
|
445
|
-
return {
|
|
446
|
-
ok: false,
|
|
447
|
-
status: 'concurrent_change_detected',
|
|
448
|
-
installed: false,
|
|
449
|
-
expected_after: expectedAfter
|
|
450
|
-
};
|
|
451
|
-
}
|
|
452
|
-
if (setupFileStatesEqual(expected, expectedAfter)) {
|
|
453
|
-
return {
|
|
454
|
-
ok: true,
|
|
455
|
-
status: 'present',
|
|
456
|
-
installed: false,
|
|
457
|
-
expected_after: expectedAfter
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
if (expected.existed === true) {
|
|
461
|
-
try {
|
|
462
|
-
await fsp.rename(file, claimedPath);
|
|
463
|
-
claimed = true;
|
|
464
|
-
}
|
|
465
|
-
catch {
|
|
466
|
-
return {
|
|
467
|
-
ok: false,
|
|
468
|
-
status: 'concurrent_change_detected',
|
|
469
|
-
installed: false,
|
|
470
|
-
expected_after: expectedAfter
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
const claimedState = await captureSetupFile(claimedPath);
|
|
474
|
-
if (!setupFileStatesEqual(claimedState, { ...expected, path: claimedPath })) {
|
|
475
|
-
const recovered = await restoreClaimedSetupPathIfAbsent(claimedPath, file, claimedState);
|
|
476
|
-
claimed = !recovered;
|
|
477
|
-
const hardened = !claimed || await hardenCodexLbSetupRecoveryPath(claimedPath);
|
|
478
|
-
return {
|
|
479
|
-
ok: false,
|
|
480
|
-
status: hardened ? 'concurrent_change_detected' : 'write_failed',
|
|
481
|
-
installed: false,
|
|
482
|
-
expected_after: expectedAfter,
|
|
483
|
-
...(claimed ? { recovery_path: claimedPath } : {}),
|
|
484
|
-
...(hardened ? {} : { error: 'setup_recovery_mode_hardening_failed' })
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
await input.beforeReplacement?.({ path: file });
|
|
489
|
-
try {
|
|
490
|
-
await fsp.link(candidatePath, file);
|
|
491
|
-
installed = true;
|
|
492
|
-
}
|
|
493
|
-
catch {
|
|
494
|
-
if (claimed) {
|
|
495
|
-
const claimedState = await captureSetupFile(claimedPath).catch(() => null);
|
|
496
|
-
const recovered = claimedState
|
|
497
|
-
? await restoreClaimedSetupPathIfAbsent(claimedPath, file, claimedState)
|
|
498
|
-
: false;
|
|
499
|
-
claimed = !recovered;
|
|
500
|
-
}
|
|
501
|
-
const hardened = !claimed || await hardenCodexLbSetupRecoveryPath(claimedPath);
|
|
502
|
-
return {
|
|
503
|
-
ok: false,
|
|
504
|
-
status: hardened ? 'concurrent_change_detected' : 'write_failed',
|
|
505
|
-
installed: false,
|
|
506
|
-
expected_after: expectedAfter,
|
|
507
|
-
...(claimed ? { recovery_path: claimedPath } : {}),
|
|
508
|
-
...(hardened ? {} : { error: 'setup_recovery_mode_hardening_failed' })
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
const committed = await captureSetupFile(file);
|
|
512
|
-
if (!setupFileStatesEqual(committed, expectedAfter)) {
|
|
513
|
-
const hardened = !claimed || await hardenCodexLbSetupRecoveryPath(claimedPath);
|
|
514
|
-
return {
|
|
515
|
-
ok: false,
|
|
516
|
-
status: hardened ? 'concurrent_change_detected' : 'write_failed',
|
|
517
|
-
installed: true,
|
|
518
|
-
expected_after: expectedAfter,
|
|
519
|
-
...(claimed ? { recovery_path: claimedPath } : {}),
|
|
520
|
-
...(hardened ? {} : { error: 'setup_recovery_mode_hardening_failed' })
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
if (claimed && !await hardenCodexLbSetupRecoveryPath(claimedPath)) {
|
|
524
|
-
return {
|
|
525
|
-
ok: false,
|
|
526
|
-
status: 'write_failed',
|
|
527
|
-
installed: true,
|
|
528
|
-
expected_after: expectedAfter,
|
|
529
|
-
recovery_path: claimedPath,
|
|
530
|
-
error: 'setup_recovery_mode_hardening_failed'
|
|
531
|
-
};
|
|
532
|
-
}
|
|
533
|
-
return {
|
|
534
|
-
ok: true,
|
|
535
|
-
status: 'written',
|
|
536
|
-
installed: true,
|
|
537
|
-
expected_after: expectedAfter,
|
|
538
|
-
...(claimed ? { recovery_path: claimedPath } : {})
|
|
539
|
-
};
|
|
540
|
-
}
|
|
541
|
-
catch (error) {
|
|
542
|
-
if (claimed && !installed) {
|
|
543
|
-
const claimedState = await captureSetupFile(claimedPath).catch(() => null);
|
|
544
|
-
const recovered = claimedState
|
|
545
|
-
? await restoreClaimedSetupPathIfAbsent(claimedPath, file, claimedState)
|
|
546
|
-
: false;
|
|
547
|
-
claimed = !recovered;
|
|
548
|
-
}
|
|
549
|
-
const hardened = !claimed || await hardenCodexLbSetupRecoveryPath(claimedPath);
|
|
550
|
-
return {
|
|
551
|
-
ok: false,
|
|
552
|
-
status: 'write_failed',
|
|
553
|
-
installed,
|
|
554
|
-
expected_after: expectedAfter,
|
|
555
|
-
...(claimed ? { recovery_path: claimedPath } : {}),
|
|
556
|
-
error: [
|
|
557
|
-
error instanceof Error ? error.message : String(error),
|
|
558
|
-
...(hardened ? [] : ['setup_recovery_mode_hardening_failed'])
|
|
559
|
-
].join(':')
|
|
560
|
-
};
|
|
561
|
-
}
|
|
562
|
-
finally {
|
|
563
|
-
await fsp.rm(candidatePath, { force: true }).catch(() => undefined);
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
export async function hardenCodexLbSetupRecoveryPath(file) {
|
|
567
|
-
let handle = null;
|
|
568
|
-
try {
|
|
569
|
-
handle = await fsp.open(file, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
|
|
570
|
-
const before = await handle.stat();
|
|
571
|
-
if (!before.isFile())
|
|
572
|
-
return false;
|
|
573
|
-
await handle.chmod(0o600);
|
|
574
|
-
const hardened = await handle.stat();
|
|
575
|
-
const pathStat = await fsp.lstat(file);
|
|
576
|
-
return hardened.isFile()
|
|
577
|
-
&& (hardened.mode & 0o777) === 0o600
|
|
578
|
-
&& pathStat.isFile()
|
|
579
|
-
&& !pathStat.isSymbolicLink()
|
|
580
|
-
&& pathStat.dev === hardened.dev
|
|
581
|
-
&& pathStat.ino === hardened.ino;
|
|
582
|
-
}
|
|
583
|
-
catch {
|
|
584
|
-
return false;
|
|
585
|
-
}
|
|
586
|
-
finally {
|
|
587
|
-
await handle?.close().catch(() => undefined);
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
export async function removeCodexLbSetupRecoveryPath(file) {
|
|
591
|
-
if (!path.basename(file).includes('.sks-setup-claimed-'))
|
|
592
|
-
return false;
|
|
593
|
-
let handle = null;
|
|
594
|
-
try {
|
|
595
|
-
handle = await fsp.open(file, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
|
|
596
|
-
const opened = await handle.stat();
|
|
597
|
-
const pathStat = await fsp.lstat(file);
|
|
598
|
-
if (!opened.isFile()
|
|
599
|
-
|| !pathStat.isFile()
|
|
600
|
-
|| pathStat.isSymbolicLink()
|
|
601
|
-
|| pathStat.dev !== opened.dev
|
|
602
|
-
|| pathStat.ino !== opened.ino)
|
|
603
|
-
return false;
|
|
604
|
-
await fsp.unlink(file);
|
|
605
|
-
return await fsp.lstat(file).then(() => false, (error) => error.code === 'ENOENT');
|
|
606
|
-
}
|
|
607
|
-
catch {
|
|
608
|
-
return false;
|
|
609
|
-
}
|
|
610
|
-
finally {
|
|
611
|
-
await handle?.close().catch(() => undefined);
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
export async function restoreCodexLbSetupWriteState(state, expectedCurrentState, opts = {}) {
|
|
615
|
-
const restored = [];
|
|
616
|
-
const removed = [];
|
|
617
|
-
const blockers = [];
|
|
618
|
-
const recovery = [];
|
|
619
|
-
const expectedByPath = new Map((Array.isArray(expectedCurrentState?.files) ? expectedCurrentState.files : [])
|
|
620
|
-
.map((entry) => [String(entry?.path || ''), entry]));
|
|
621
|
-
for (const entry of Array.isArray(state?.files) ? [...state.files].reverse() : []) {
|
|
622
|
-
const file = String(entry?.path || '');
|
|
623
|
-
if (!file)
|
|
624
|
-
continue;
|
|
625
|
-
try {
|
|
626
|
-
const expected = expectedByPath.get(file);
|
|
627
|
-
if (!expected) {
|
|
628
|
-
blockers.push(`setup_rollback_missing_expected_state:${file}`);
|
|
629
|
-
continue;
|
|
630
|
-
}
|
|
631
|
-
if (expected && setupFileStatesEqual(entry, expected))
|
|
632
|
-
continue;
|
|
633
|
-
const commit = await commitSetupRollbackIfUnchanged({
|
|
634
|
-
file,
|
|
635
|
-
before: entry,
|
|
636
|
-
expected,
|
|
637
|
-
...(opts.beforeReplacement ? { beforeReplacement: opts.beforeReplacement } : {})
|
|
638
|
-
});
|
|
639
|
-
if (commit.recoveryPath)
|
|
640
|
-
recovery.push(commit.recoveryPath);
|
|
641
|
-
if (!commit.ok) {
|
|
642
|
-
blockers.push(`${commit.failed ? 'setup_rollback_failed' : 'setup_rollback_conflict'}:${file}`);
|
|
643
|
-
continue;
|
|
644
|
-
}
|
|
645
|
-
if (commit.restored)
|
|
646
|
-
restored.push(file);
|
|
647
|
-
if (commit.removed)
|
|
648
|
-
removed.push(file);
|
|
649
|
-
}
|
|
650
|
-
catch {
|
|
651
|
-
blockers.push(`setup_rollback_failed:${file}`);
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
return { ok: blockers.length === 0, restored, removed, blockers, recovery };
|
|
655
|
-
}
|
|
656
|
-
function setupFileStatesEqual(left, right) {
|
|
657
|
-
return left?.existed === right?.existed
|
|
658
|
-
&& String(left?.kind || (left?.existed ? 'regular' : 'missing')) === String(right?.kind || (right?.existed ? 'regular' : 'missing'))
|
|
659
|
-
&& String(left?.bytes_base64 || '') === String(right?.bytes_base64 || '')
|
|
660
|
-
&& (left?.existed !== true || Number(left?.mode) === Number(right?.mode));
|
|
661
|
-
}
|
|
662
|
-
async function captureSetupFile(file) {
|
|
663
|
-
let pathStat;
|
|
664
|
-
try {
|
|
665
|
-
pathStat = await fsp.lstat(file);
|
|
666
|
-
}
|
|
667
|
-
catch (error) {
|
|
668
|
-
if (error?.code === 'ENOENT') {
|
|
669
|
-
return { path: file, existed: false, kind: 'missing', bytes_base64: '', mode: null };
|
|
670
|
-
}
|
|
671
|
-
throw error;
|
|
672
|
-
}
|
|
673
|
-
if (pathStat.isSymbolicLink()) {
|
|
674
|
-
return { path: file, existed: true, kind: 'symlink', bytes_base64: '', mode: pathStat.mode & 0o777 };
|
|
675
|
-
}
|
|
676
|
-
if (!pathStat.isFile()) {
|
|
677
|
-
return { path: file, existed: true, kind: 'non_regular', bytes_base64: '', mode: pathStat.mode & 0o777 };
|
|
678
|
-
}
|
|
679
|
-
let handle = null;
|
|
680
|
-
try {
|
|
681
|
-
handle = await fsp.open(file, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
|
|
682
|
-
const stat = await handle.stat();
|
|
683
|
-
if (!stat.isFile()) {
|
|
684
|
-
return { path: file, existed: true, kind: 'non_regular', bytes_base64: '', mode: stat.mode & 0o777 };
|
|
685
|
-
}
|
|
686
|
-
const bytes = await handle.readFile();
|
|
687
|
-
return {
|
|
688
|
-
path: file,
|
|
689
|
-
existed: true,
|
|
690
|
-
kind: 'regular',
|
|
691
|
-
bytes_base64: bytes.toString('base64'),
|
|
692
|
-
mode: stat.mode & 0o777
|
|
693
|
-
};
|
|
694
|
-
}
|
|
695
|
-
catch (error) {
|
|
696
|
-
if (error?.code === 'ENOENT') {
|
|
697
|
-
return { path: file, existed: false, kind: 'missing', bytes_base64: '', mode: null };
|
|
698
|
-
}
|
|
699
|
-
if (error?.code === 'ELOOP') {
|
|
700
|
-
const stat = await fsp.lstat(file).catch(() => null);
|
|
701
|
-
return {
|
|
702
|
-
path: file,
|
|
703
|
-
existed: true,
|
|
704
|
-
kind: 'symlink',
|
|
705
|
-
bytes_base64: '',
|
|
706
|
-
mode: stat ? stat.mode & 0o777 : null
|
|
707
|
-
};
|
|
708
|
-
}
|
|
709
|
-
throw error;
|
|
710
|
-
}
|
|
711
|
-
finally {
|
|
712
|
-
await handle?.close().catch(() => undefined);
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
async function commitSetupRollbackIfUnchanged(input) {
|
|
716
|
-
const { file, before, expected } = input;
|
|
717
|
-
await ensureDir(path.dirname(file));
|
|
718
|
-
const token = `${Date.now().toString(36)}-${process.pid}-${randomBytes(6).toString('hex')}`;
|
|
719
|
-
const claimedPath = `${file}.sks-rollback-claimed-${token}`;
|
|
720
|
-
const candidatePath = `${file}.sks-rollback-candidate-${token}`;
|
|
721
|
-
let claimed = false;
|
|
722
|
-
try {
|
|
723
|
-
if (expected.existed === true) {
|
|
724
|
-
const currentStat = await fsp.lstat(file).catch((error) => {
|
|
725
|
-
if (error.code === 'ENOENT')
|
|
726
|
-
return null;
|
|
727
|
-
throw error;
|
|
728
|
-
});
|
|
729
|
-
if (!currentStat || currentStat.isSymbolicLink() || !currentStat.isFile()) {
|
|
730
|
-
return { ok: false, restored: false, removed: false };
|
|
731
|
-
}
|
|
732
|
-
try {
|
|
733
|
-
await fsp.rename(file, claimedPath);
|
|
734
|
-
claimed = true;
|
|
735
|
-
}
|
|
736
|
-
catch {
|
|
737
|
-
return { ok: false, restored: false, removed: false };
|
|
738
|
-
}
|
|
739
|
-
const claimedState = await captureSetupFile(claimedPath);
|
|
740
|
-
if (!setupFileStatesEqual(claimedState, { ...expected, path: claimedPath })) {
|
|
741
|
-
const recovered = await restoreClaimedSetupPathIfAbsent(claimedPath, file, claimedState);
|
|
742
|
-
claimed = !recovered;
|
|
743
|
-
return {
|
|
744
|
-
ok: false,
|
|
745
|
-
restored: false,
|
|
746
|
-
removed: false,
|
|
747
|
-
...(claimed ? { recoveryPath: claimedPath } : {})
|
|
748
|
-
};
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
else {
|
|
752
|
-
const current = await captureSetupFile(file);
|
|
753
|
-
if (!setupFileStatesEqual(current, expected)) {
|
|
754
|
-
return { ok: false, restored: false, removed: false };
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
await input.beforeReplacement?.({ path: file });
|
|
758
|
-
if (before.existed === true) {
|
|
759
|
-
if (String(before.kind || 'regular') !== 'regular') {
|
|
760
|
-
if (claimed) {
|
|
761
|
-
const claimedState = await captureSetupFile(claimedPath);
|
|
762
|
-
const recovered = await restoreClaimedSetupPathIfAbsent(claimedPath, file, claimedState);
|
|
763
|
-
claimed = !recovered;
|
|
764
|
-
}
|
|
765
|
-
return {
|
|
766
|
-
ok: false,
|
|
767
|
-
restored: false,
|
|
768
|
-
removed: false,
|
|
769
|
-
failed: true,
|
|
770
|
-
...(claimed ? { recoveryPath: claimedPath } : {})
|
|
771
|
-
};
|
|
772
|
-
}
|
|
773
|
-
const bytes = Buffer.from(String(before.bytes_base64 || ''), 'base64');
|
|
774
|
-
await writeBinaryAtomic(candidatePath, bytes, { mode: Number(before.mode || 0o600) });
|
|
775
|
-
try {
|
|
776
|
-
await fsp.link(candidatePath, file);
|
|
777
|
-
}
|
|
778
|
-
catch {
|
|
779
|
-
return {
|
|
780
|
-
ok: false,
|
|
781
|
-
restored: false,
|
|
782
|
-
removed: false,
|
|
783
|
-
...(claimed ? { recoveryPath: claimedPath } : {})
|
|
784
|
-
};
|
|
785
|
-
}
|
|
786
|
-
const committed = await captureSetupFile(file);
|
|
787
|
-
if (!setupFileStatesEqual(committed, before)) {
|
|
788
|
-
return {
|
|
789
|
-
ok: false,
|
|
790
|
-
restored: false,
|
|
791
|
-
removed: false,
|
|
792
|
-
...(claimed ? { recoveryPath: claimedPath } : {})
|
|
793
|
-
};
|
|
794
|
-
}
|
|
795
|
-
return {
|
|
796
|
-
ok: true,
|
|
797
|
-
restored: true,
|
|
798
|
-
removed: false,
|
|
799
|
-
...(claimed ? { recoveryPath: claimedPath } : {})
|
|
800
|
-
};
|
|
801
|
-
}
|
|
802
|
-
const committed = await captureSetupFile(file);
|
|
803
|
-
if (!setupFileStatesEqual(committed, before)) {
|
|
804
|
-
return {
|
|
805
|
-
ok: false,
|
|
806
|
-
restored: false,
|
|
807
|
-
removed: false,
|
|
808
|
-
...(claimed ? { recoveryPath: claimedPath } : {})
|
|
809
|
-
};
|
|
810
|
-
}
|
|
811
|
-
return {
|
|
812
|
-
ok: true,
|
|
813
|
-
restored: false,
|
|
814
|
-
removed: true,
|
|
815
|
-
...(claimed ? { recoveryPath: claimedPath } : {})
|
|
816
|
-
};
|
|
817
|
-
}
|
|
818
|
-
catch {
|
|
819
|
-
if (claimed) {
|
|
820
|
-
const claimedState = await captureSetupFile(claimedPath).catch(() => null);
|
|
821
|
-
const recovered = claimedState
|
|
822
|
-
? await restoreClaimedSetupPathIfAbsent(claimedPath, file, claimedState)
|
|
823
|
-
: false;
|
|
824
|
-
claimed = !recovered;
|
|
825
|
-
}
|
|
826
|
-
return {
|
|
827
|
-
ok: false,
|
|
828
|
-
restored: false,
|
|
829
|
-
removed: false,
|
|
830
|
-
failed: true,
|
|
831
|
-
...(claimed ? { recoveryPath: claimedPath } : {})
|
|
832
|
-
};
|
|
833
|
-
}
|
|
834
|
-
finally {
|
|
835
|
-
await fsp.rm(candidatePath, { force: true }).catch(() => undefined);
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
async function restoreClaimedSetupPathIfAbsent(claimedPath, file, claimedState) {
|
|
839
|
-
try {
|
|
840
|
-
if (claimedState?.kind === 'regular') {
|
|
841
|
-
await fsp.link(claimedPath, file);
|
|
842
|
-
}
|
|
843
|
-
else if (claimedState?.kind === 'symlink') {
|
|
844
|
-
await fsp.symlink(await fsp.readlink(claimedPath), file);
|
|
845
|
-
}
|
|
846
|
-
else {
|
|
847
|
-
return false;
|
|
848
|
-
}
|
|
849
|
-
await fsp.unlink(claimedPath);
|
|
850
|
-
return true;
|
|
851
|
-
}
|
|
852
|
-
catch {
|
|
853
|
-
return false;
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
async function capturedSetupFileHash(file) {
|
|
857
|
-
if (file.kind !== 'regular')
|
|
858
|
-
return 'missing';
|
|
859
|
-
return await sha256Text(Buffer.from(file.bytes_base64, 'base64').toString('utf8'));
|
|
860
|
-
}
|
|
861
|
-
function profileFilesForDrift(home, shellProfile) {
|
|
862
|
-
const targets = {
|
|
863
|
-
zsh: path.join(home, '.zshrc'),
|
|
864
|
-
bash: path.join(home, '.bashrc'),
|
|
865
|
-
fish: path.join(home, '.config', 'fish', 'config.fish')
|
|
866
|
-
};
|
|
867
|
-
if (shellProfile === 'zsh')
|
|
868
|
-
return [targets.zsh];
|
|
869
|
-
if (shellProfile === 'bash')
|
|
870
|
-
return [targets.bash];
|
|
871
|
-
if (shellProfile === 'fish')
|
|
872
|
-
return [targets.fish];
|
|
873
|
-
if (shellProfile === 'all')
|
|
874
|
-
return [targets.zsh, targets.bash, targets.fish];
|
|
875
|
-
return [targets.zsh, targets.bash, targets.fish];
|
|
876
|
-
}
|
|
877
|
-
export function appliedCodexLbPersistenceModes(state = {}) {
|
|
878
|
-
const modes = [];
|
|
879
|
-
if (state.writeEnvFile && state.envFile === true)
|
|
880
|
-
modes.push('durable_env_file');
|
|
881
|
-
if (state.storeKeychain && state.keychain?.ok === true)
|
|
882
|
-
modes.push('durable_keychain');
|
|
883
|
-
if (state.syncLaunchctl && state.codexEnvironment?.launch_environment?.status === 'synced')
|
|
884
|
-
modes.push('process_only_ephemeral');
|
|
885
|
-
if (state.shellProfile !== 'skip' && state.shellProfileResult?.status === 'installed')
|
|
886
|
-
modes.push('shell_profile');
|
|
887
|
-
if (!modes.length && state.apiKeySource === 'process.env')
|
|
888
|
-
modes.push('process_only_ephemeral');
|
|
889
|
-
if (!modes.length)
|
|
890
|
-
modes.push('none');
|
|
891
|
-
return modes;
|
|
892
|
-
}
|
|
893
|
-
export function shellSingleQuote(value) {
|
|
894
|
-
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
895
|
-
}
|
|
896
|
-
export function parseCodexLbEnvBaseUrl(text = '') {
|
|
897
|
-
const value = parseShellEnvValue(text, 'CODEX_LB_BASE_URL');
|
|
898
|
-
return value ? normalizeCodexLbBaseUrl(value) : '';
|
|
899
|
-
}
|
|
900
|
-
export function parseCodexSharedLoginApiKey(text = '') {
|
|
901
|
-
try {
|
|
902
|
-
const parsed = JSON.parse(String(text || ''));
|
|
903
|
-
const authMode = String(parsed?.auth_mode || parsed?.authMode || parsed?.mode || '').toLowerCase();
|
|
904
|
-
const key = parsed?.key || parsed?.api_key || parsed?.apiKey || parsed?.openai_api_key || parsed?.OPENAI_API_KEY;
|
|
905
|
-
if (!key || typeof key !== 'string')
|
|
906
|
-
return '';
|
|
907
|
-
if (authMode && !/api[-_]?key|apikey/.test(authMode))
|
|
908
|
-
return '';
|
|
909
|
-
return key.trim();
|
|
910
|
-
}
|
|
911
|
-
catch {
|
|
912
|
-
return '';
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
function parseShellEnvValue(text = '', key = '') {
|
|
916
|
-
const re = new RegExp(`^\\s*(?:export\\s+)?${escapeRegExp(key)}\\s*=\\s*(.+?)\\s*$`, 'm');
|
|
917
|
-
const envMatch = String(text || '').match(re);
|
|
918
|
-
const raw = envMatch?.[1]?.trim() || '';
|
|
919
|
-
if (!raw)
|
|
920
|
-
return '';
|
|
921
|
-
if (raw.startsWith("'"))
|
|
922
|
-
return raw.endsWith("'") && raw.length > 1 ? raw.slice(1, -1).replace(/'\\''/g, "'") : '';
|
|
923
|
-
if (raw.startsWith('"'))
|
|
924
|
-
return raw.endsWith('"') && raw.length > 1 ? raw.slice(1, -1).replace(/\\"/g, '"') : '';
|
|
925
|
-
if (raw.includes("'") || raw.includes('"') || /\s/.test(raw))
|
|
926
|
-
return '';
|
|
927
|
-
return raw;
|
|
928
|
-
}
|
|
929
|
-
export async function sha256Text(value = '') {
|
|
930
|
-
const { createHash } = await import('node:crypto');
|
|
931
|
-
return createHash('sha256').update(String(value || '')).digest('hex');
|
|
932
|
-
}
|
|
933
|
-
function topLevelTomlString(text = '', key) {
|
|
87
|
+
function topLevelTomlString(text, key) {
|
|
934
88
|
const topLevel = String(text || '').split(/\n\s*\[/)[0] || '';
|
|
935
89
|
return topLevel.match(new RegExp(`(^|\\n)\\s*${escapeRegExp(key)}\\s*=\\s*"([^"]+)"\\s*(?:#.*)?(?=\\n|$)`))?.[2] || '';
|
|
936
90
|
}
|
|
937
|
-
function topLevelHasLine(text
|
|
91
|
+
function topLevelHasLine(text, line) {
|
|
938
92
|
const topLevel = String(text || '').split(/\n\s*\[/)[0] || '';
|
|
939
93
|
return topLevel.split(/\r?\n/).some((candidate) => candidate.trim() === line);
|
|
940
94
|
}
|
|
941
|
-
function addTopLevelMarkerBeforeKey(text
|
|
95
|
+
function addTopLevelMarkerBeforeKey(text, key, marker) {
|
|
942
96
|
const lines = String(text || '').split('\n');
|
|
943
97
|
const firstTable = lines.findIndex((line) => /^\s*\[/.test(line));
|
|
944
98
|
const end = firstTable === -1 ? lines.length : firstTable;
|
|
@@ -947,7 +101,7 @@ function addTopLevelMarkerBeforeKey(text = '', key, marker) {
|
|
|
947
101
|
lines.splice(keyIndex, 0, marker);
|
|
948
102
|
return lines.join('\n');
|
|
949
103
|
}
|
|
950
|
-
function removeTopLevelLine(text
|
|
104
|
+
function removeTopLevelLine(text, target) {
|
|
951
105
|
const lines = String(text || '').split('\n');
|
|
952
106
|
const firstTable = lines.findIndex((line) => /^\s*\[/.test(line));
|
|
953
107
|
const end = firstTable === -1 ? lines.length : firstTable;
|
|
@@ -957,3 +111,47 @@ function removeTopLevelLine(text = '', target) {
|
|
|
957
111
|
}
|
|
958
112
|
return lines.join('\n').replace(/^\n+/, '').replace(/\n{3,}/g, '\n\n');
|
|
959
113
|
}
|
|
114
|
+
function removeTopLevelTomlKey(text, key) {
|
|
115
|
+
const lines = String(text || '').split('\n');
|
|
116
|
+
const firstTable = lines.findIndex((line) => /^\s*\[.+\]\s*$/.test(line));
|
|
117
|
+
const end = firstTable === -1 ? lines.length : firstTable;
|
|
118
|
+
const keyPattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=`);
|
|
119
|
+
return lines
|
|
120
|
+
.filter((line, index) => index >= end || !keyPattern.test(line))
|
|
121
|
+
.join('\n')
|
|
122
|
+
.replace(/^\n+/, '')
|
|
123
|
+
.replace(/\n{3,}/g, '\n\n');
|
|
124
|
+
}
|
|
125
|
+
function removeManagedBridgeTopLevelBindings(text) {
|
|
126
|
+
let next = String(text || '');
|
|
127
|
+
next = removeTopLevelTomlKey(next, 'model_provider');
|
|
128
|
+
next = removeTopLevelTomlKey(next, 'openai_base_url');
|
|
129
|
+
next = removeTopLevelTomlKey(next, 'model_catalog_json');
|
|
130
|
+
for (const marker of [
|
|
131
|
+
DESKTOP_BRIDGE_MANAGED_MARKER,
|
|
132
|
+
DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER,
|
|
133
|
+
DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER
|
|
134
|
+
])
|
|
135
|
+
next = removeTopLevelLine(next, marker);
|
|
136
|
+
return next;
|
|
137
|
+
}
|
|
138
|
+
function normalizeManagedBridgeBaseUrl(value) {
|
|
139
|
+
const normalized = String(value || '').trim().replace(/\/+$/, '');
|
|
140
|
+
let parsed;
|
|
141
|
+
try {
|
|
142
|
+
parsed = new URL(normalized);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
throw new Error('desktop_bridge_loopback_base_url_required');
|
|
146
|
+
}
|
|
147
|
+
const hostname = parsed.hostname.replace(/^\[|\]$/g, '').toLowerCase();
|
|
148
|
+
if (parsed.protocol !== 'http:'
|
|
149
|
+
|| !['127.0.0.1', 'localhost', '::1'].includes(hostname)
|
|
150
|
+
|| parsed.username
|
|
151
|
+
|| parsed.password
|
|
152
|
+
|| parsed.search
|
|
153
|
+
|| parsed.hash
|
|
154
|
+
|| !/^\/__sks\/client\/[A-Za-z0-9_-]{43}\/backend-api\/codex$/.test(parsed.pathname.replace(/\/+$/, '')))
|
|
155
|
+
throw new Error('desktop_bridge_loopback_base_url_required');
|
|
156
|
+
return normalized;
|
|
157
|
+
}
|