sneakoscope 7.3.0 → 7.6.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 +129 -10
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/cli/codex-app-command.js +1 -1
- package/dist/cli/command-manifest-lite.js +4 -1
- package/dist/cli/command-registry.js +2 -0
- package/dist/cli/install-helpers-codex-lb-chain.js +42 -0
- package/dist/cli/install-helpers-codex-lb-config.js +114 -9
- package/dist/cli/install-helpers-install-support.js +6 -16
- package/dist/cli/install-helpers.js +788 -600
- package/dist/cli/router.js +30 -2
- package/dist/commands/codex-app.js +37 -1
- package/dist/commands/codex-lb.js +491 -300
- package/dist/config/skills-manifest.json +57 -71
- package/dist/core/align/align-route.js +127 -0
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +8 -4
- package/dist/core/codex-app/codex-app-restart.js +83 -0
- package/dist/core/codex-app/codex-app-ui-state-snapshot.js +8 -4
- package/dist/core/codex-app/desktop-routing-snapshot.js +1 -1
- package/dist/core/codex-app/menubar/constants.js +5 -0
- package/dist/core/codex-app/menubar/generation-transaction-journal.js +1 -3
- package/dist/core/codex-app/menubar/installer.js +18 -0
- package/dist/core/codex-app/multi-provider-router.js +22 -10
- package/dist/core/codex-app/openrouter-activate.js +26 -1
- package/dist/core/codex-app.js +118 -26
- package/dist/core/codex-control/codex-app-server-runtime-env.js +86 -0
- package/dist/core/codex-control/codex-app-server-v2-client.js +78 -8
- package/dist/core/codex-control/codex-task-runner.js +2 -2
- package/dist/core/codex-lb/capability-runner.js +204 -0
- package/dist/core/codex-lb/capability-types.js +1 -0
- package/dist/core/codex-lb/codex-lb-env.js +9 -13
- package/dist/core/codex-lb/codex-lb-setup.js +45 -8
- package/dist/core/codex-lb/codex-lb-tool-catalog.js +52 -74
- package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +1 -1
- package/dist/core/codex-lb/desktop-auth-invariant.js +185 -0
- package/dist/core/codex-lb/desktop-bridge/client.js +34 -0
- package/dist/core/codex-lb/desktop-bridge/header-policy.js +106 -0
- package/dist/core/codex-lb/desktop-bridge/http-forward.js +101 -0
- package/dist/core/codex-lb/desktop-bridge/index.js +10 -0
- package/dist/core/codex-lb/desktop-bridge/launchd.js +81 -0
- package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +27 -0
- package/dist/core/codex-lb/desktop-bridge/security.js +274 -0
- package/dist/core/codex-lb/desktop-bridge/server.js +179 -0
- package/dist/core/codex-lb/desktop-bridge/state.js +119 -0
- package/dist/core/codex-lb/desktop-bridge/types.js +16 -0
- package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +151 -0
- package/dist/core/codex-lb/desktop-center-credentials.js +189 -0
- package/dist/core/codex-lb/desktop-controller.js +1410 -0
- package/dist/core/codex-lb/desktop-mode.js +32 -0
- package/dist/core/codex-lb/desktop-real-evidence.js +168 -0
- package/dist/core/codex-lb/desktop-service.js +613 -0
- package/dist/core/codex-lb/legacy-migration.js +575 -0
- package/dist/core/codex-lb/migration-receipt.js +246 -0
- package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +60 -0
- package/dist/core/codex-lb/probes/bridge-probe.js +75 -0
- package/dist/core/codex-lb/probes/catalog-probe.js +128 -0
- package/dist/core/codex-lb/probes/computer-use-probe.js +50 -0
- package/dist/core/codex-lb/probes/image-generation-probe.js +47 -0
- package/dist/core/codex-lb/probes/probe-evidence.js +39 -0
- package/dist/core/codex-lb/probes/voice-realtime-probe.js +44 -0
- package/dist/core/codex-lb/trusted-deep-evidence.js +403 -0
- package/dist/core/codex-native/core-skill-manifest.js +22 -6
- package/dist/core/codex-runtime/codex-desktop-config-policy.js +2 -1
- package/dist/core/commands/align-command.js +220 -0
- package/dist/core/commands/image-ux-review-command.js +280 -68
- package/dist/core/commands/naruto-command.js +257 -8
- package/dist/core/commands/ppt-command.js +2 -2
- package/dist/core/commands/telegram-command.js +402 -34
- package/dist/core/commands/triwiki-command.js +11 -1
- package/dist/core/commands/triwiki-graph-command.js +160 -0
- package/dist/core/commands/wiki-command.js +100 -20
- package/dist/core/doctor/skill-legacy-surface.js +1 -3
- package/dist/core/errors/message.js +3 -0
- package/dist/core/evidence/evidence-freshness.js +14 -0
- package/dist/core/feature-fixtures.js +9 -1
- package/dist/core/feature-registry.js +5 -3
- package/dist/core/fsx.js +7 -3
- package/dist/core/hooks-runtime/codex-commit-hooks-selftest.js +82 -0
- package/dist/core/hooks-runtime/context-graph-freshness-preflight.js +113 -0
- package/dist/core/hooks-runtime/hook-context.js +15 -2
- package/dist/core/hooks-runtime/managed-skill-generation-heal.js +46 -0
- package/dist/core/hooks-runtime/naruto-terminal-finalization.js +1 -0
- package/dist/core/hooks-runtime/official-subagent-lifecycle.js +1 -1
- package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
- package/dist/core/hooks-runtime.js +33 -89
- package/dist/core/image-ux-review/callout-extraction.js +6 -0
- package/dist/core/image-ux-review/imagegen-adapter.js +64 -23
- package/dist/core/image-ux-review/real-callout-extractor.js +1 -0
- package/dist/core/image-ux-review.js +230 -12
- package/dist/core/imagegen/codex-lb-imagegen-target.js +21 -7
- package/dist/core/imagegen/imagegen-capability.js +10 -4
- package/dist/core/imagegen/require-imagegen.js +29 -5
- package/dist/core/init/skills.js +25 -13
- package/dist/core/init.js +1 -1
- package/dist/core/install/installed-package-smoke.js +157 -4
- package/dist/core/managed-assets/managed-assets-manifest.js +2 -1
- package/dist/core/managed-path-safety.js +8 -10
- package/dist/core/mcp-config/backup.js +2 -4
- package/dist/core/mcp-config/config-reader.js +2 -4
- package/dist/core/mcp-config/scope.js +3 -5
- package/dist/core/naruto/context-graph-advisor-scope.js +242 -0
- package/dist/core/naruto/context-graph-advisor.js +0 -0
- package/dist/core/perf/release-latency-slo.js +12 -6
- package/dist/core/pipeline-internals/runtime-core.js +78 -0
- package/dist/core/pipeline-internals/runtime-gates.js +5 -2
- package/dist/core/policy/latest-version-guidance.js +113 -0
- package/dist/core/ppt.js +8 -8
- package/dist/core/preflight/parallel-preflight-engine.js +1 -1
- package/dist/core/proof/auto-finalize.js +98 -6
- package/dist/core/proof/route-proof-gate.js +12 -1
- package/dist/core/proof/route-proof-policy.js +8 -8
- package/dist/core/release/gate-manifest.js +32 -1
- package/dist/core/release/npm-stage-contract.js +21 -0
- package/dist/core/release/package-size-budget.js +2 -2
- package/dist/core/release/release-gate-contract.js +5 -0
- package/dist/core/release/release-proof-io.js +1 -3
- package/dist/core/release/release-real-contract.js +5 -4
- package/dist/core/release/release-stamp-proof.js +1 -3
- package/dist/core/release/stage-publish.js +27 -0
- package/dist/core/remote/audit-idempotency.js +50 -18
- package/dist/core/remote/codex-public-activity.js +283 -0
- package/dist/core/remote/index.js +1 -0
- package/dist/core/remote/ssh-worker-client.js +4 -1
- package/dist/core/remote/worker.js +206 -27
- package/dist/core/research/research-adversarial-review.js +1 -3
- package/dist/core/research/research-claim-synthesizer.js +1 -3
- package/dist/core/research/research-falsification-runner.js +1 -3
- package/dist/core/research/research-source-evidence.js +1 -3
- package/dist/core/responses-stream.js +65 -19
- package/dist/core/routes/constants.js +1 -1
- package/dist/core/routes/design-policy.js +7 -2
- package/dist/core/routes/dollar-manifest-lite.js +2 -4
- package/dist/core/routes/evidence.js +1 -1
- package/dist/core/routes.js +30 -10
- package/dist/core/safety/command-contract/contracts.js +2 -0
- package/dist/core/safety/command-contract/types.js +1 -1
- package/dist/core/search/context-graph-seeds.js +205 -0
- package/dist/core/search/context.js +0 -0
- package/dist/core/search/provider.js +2 -2
- package/dist/core/secret-redaction.js +6 -0
- package/dist/core/subagents/naruto-help-contract.js +2 -1
- package/dist/core/subagents/naruto-host-credentials.js +161 -0
- package/dist/core/subagents/official-subagent-preparation.js +4 -1
- package/dist/core/subagents/official-subagent-prompt.js +44 -2
- package/dist/core/subagents/official-subagent-runner.js +26 -8
- package/dist/core/subagents/triwiki-attention.js +135 -123
- package/dist/core/telegram/bot-api.js +85 -10
- package/dist/core/telegram/center-snapshot.js +718 -0
- package/dist/core/telegram/config.js +158 -31
- package/dist/core/telegram/hub.js +288 -33
- package/dist/core/telegram/index.js +2 -0
- package/dist/core/telegram/ingress-spool.js +419 -0
- package/dist/core/telegram/ledgers.js +188 -29
- package/dist/core/telegram/messages.js +26 -6
- package/dist/core/telegram/owner-lock.js +10 -7
- package/dist/core/telegram/runtime-projection.js +38 -3
- package/dist/core/telegram/runtime.js +298 -63
- package/dist/core/telegram/service.js +137 -8
- package/dist/core/telegram/setup.js +291 -73
- package/dist/core/text/strings.js +3 -0
- package/dist/core/triwiki/code-pack.js +12 -120
- package/dist/core/triwiki/context-graph/compiler/cache-key.js +277 -0
- package/dist/core/triwiki/context-graph/compiler/extract.js +104 -0
- package/dist/core/triwiki/context-graph/compiler/freshness.js +108 -0
- package/dist/core/triwiki/context-graph/compiler/index.js +237 -0
- package/dist/core/triwiki/context-graph/compiler/merge.js +0 -0
- package/dist/core/triwiki/context-graph/compiler/serialize.js +115 -0
- package/dist/core/triwiki/context-graph/contracts.js +236 -0
- package/dist/core/triwiki/context-graph/extractors/code/builders.js +150 -0
- package/dist/core/triwiki/context-graph/extractors/code/declarations.js +161 -0
- package/dist/core/triwiki/context-graph/extractors/code/index.js +265 -0
- package/dist/core/triwiki/context-graph/extractors/code/inventory.js +237 -0
- package/dist/core/triwiki/context-graph/extractors/code/module-graph.js +252 -0
- package/dist/core/triwiki/context-graph/extractors/code/modules.js +79 -0
- package/dist/core/triwiki/context-graph/extractors/code/references.js +184 -0
- package/dist/core/triwiki/context-graph/extractors/code/selection.js +100 -0
- package/dist/core/triwiki/context-graph/extractors/code/ts-config.js +161 -0
- package/dist/core/triwiki/context-graph/extractors/code/types.js +18 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/claims.js +289 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/index.js +54 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/proof-index.js +213 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/proofs.js +266 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/redaction.js +177 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/shared.js +0 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/sources.js +154 -0
- package/dist/core/triwiki/context-graph/extractors/index.js +7 -0
- package/dist/core/triwiki/context-graph/extractors/topology/commands.js +207 -0
- package/dist/core/triwiki/context-graph/extractors/topology/gate-edges.js +201 -0
- package/dist/core/triwiki/context-graph/extractors/topology/gates.js +182 -0
- package/dist/core/triwiki/context-graph/extractors/topology/globs.js +124 -0
- package/dist/core/triwiki/context-graph/extractors/topology/index.js +92 -0
- package/dist/core/triwiki/context-graph/extractors/topology/routes.js +135 -0
- package/dist/core/triwiki/context-graph/extractors/topology/shared.js +215 -0
- package/dist/core/triwiki/context-graph/graph-index.js +143 -0
- package/dist/core/triwiki/context-graph/ids.js +0 -0
- package/dist/core/triwiki/context-graph/lint/index.js +0 -0
- package/dist/core/triwiki/context-graph/lint/rules.js +232 -0
- package/dist/core/triwiki/context-graph/lint/warnings.js +93 -0
- package/dist/core/triwiki/context-graph/paths.js +116 -0
- package/dist/core/triwiki/context-graph/profiles.js +78 -0
- package/dist/core/triwiki/context-graph/projections/anchors.js +91 -0
- package/dist/core/triwiki/context-graph/projections/attention.js +58 -0
- package/dist/core/triwiki/context-graph/projections/code-pack-workspace.js +62 -0
- package/dist/core/triwiki/context-graph/projections/code-pack.js +144 -0
- package/dist/core/triwiki/context-graph/projections/index.js +7 -0
- package/dist/core/triwiki/context-graph/projections/module-view.js +140 -0
- package/dist/core/triwiki/context-graph/projections/node-summary.js +118 -0
- package/dist/core/triwiki/context-graph/projections/pack-contract.js +6 -0
- package/dist/core/triwiki/context-graph/query/explain.js +0 -0
- package/dist/core/triwiki/context-graph/query/index.js +178 -0
- package/dist/core/triwiki/context-graph/query/load.js +116 -0
- package/dist/core/triwiki/context-graph/query/pack.js +117 -0
- package/dist/core/triwiki/context-graph/query/rank.js +142 -0
- package/dist/core/triwiki/context-graph/query/ranking-config.js +64 -0
- package/dist/core/triwiki/context-graph/query/seeds.js +241 -0
- package/dist/core/triwiki/context-graph/query/snapshot-cache.js +72 -0
- package/dist/core/triwiki/context-graph/query/traverse.js +154 -0
- package/dist/core/triwiki/context-graph/query-types.js +29 -0
- package/dist/core/triwiki/context-graph/store/compile-lock.js +21 -0
- package/dist/core/triwiki/context-graph/store/event-log.js +48 -0
- package/dist/core/triwiki/context-graph/store/fragment-cache.js +85 -0
- package/dist/core/triwiki/context-graph/store/graph-status.js +85 -0
- package/dist/core/triwiki/context-graph/store/snapshot-store.js +155 -0
- package/dist/core/triwiki/triwiki-proof-bank-index-repair.js +115 -0
- package/dist/core/triwiki/triwiki-proof-bank-index-store.js +290 -0
- package/dist/core/triwiki/triwiki-proof-bank-index.js +126 -0
- package/dist/core/triwiki/triwiki-proof-bank.js +7 -1
- package/dist/core/verification/context-graph-affected.js +271 -0
- package/dist/core/version.js +1 -1
- package/dist/native/sks-menubar/Sources/AlertFactory.swift +11 -3
- package/dist/native/sks-menubar/Sources/AppDelegate.swift +9 -0
- package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +25 -3
- package/dist/native/sks-menubar/Sources/ControlKit.swift +19 -8
- package/dist/native/sks-menubar/Sources/DiagnosticsViewController.swift +1 -2
- package/dist/native/sks-menubar/Sources/MCPServersViewController.swift +45 -17
- package/dist/native/sks-menubar/Sources/OverviewSummary.swift +162 -0
- package/dist/native/sks-menubar/Sources/OverviewViewController.swift +136 -190
- package/dist/native/sks-menubar/Sources/ProcessClient.swift +107 -11
- package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +5 -1
- package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +16 -4
- package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +4 -1
- package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +131 -0
- package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +288 -341
- package/dist/native/sks-menubar/Sources/RemoteTelegramCenterState.swift +447 -0
- package/dist/native/sks-menubar/Sources/RemoteTelegramRendering.swift +484 -0
- package/dist/native/sks-menubar/Sources/RemoteTelegramViewController.swift +364 -115
- package/dist/native/sks-menubar/Sources/SettingsViewController.swift +73 -12
- package/dist/native/sks-menubar/Sources/SidebarItem.swift +3 -3
- package/dist/native/sks-menubar/Sources/StatusItemController.swift +13 -13
- package/dist/native/sks-menubar/Sources/TelegramHubSupervisor.swift +469 -0
- package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +1 -6
- package/dist/native/sks-menubar/Tests/AppRuntimeTestSupport.swift +12 -0
- package/dist/native/sks-menubar/Tests/NativeTelegramRegressionRunner.swift +55 -0
- package/dist/native/sks-menubar/Tests/ProcessClientTests.swift +33 -0
- package/dist/native/sks-menubar/Tests/ProvidersViewControllerTests.swift +142 -0
- package/dist/native/sks-menubar/Tests/RemoteTelegramCenterStateTests.swift +56 -0
- package/dist/native/sks-menubar/Tests/TelegramHubSupervisorTests.swift +55 -0
- package/dist/scripts/codex-lb-catalog-passthrough-check.js +120 -0
- package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +169 -0
- package/dist/scripts/codex-lb-desktop-bridge-check.js +394 -0
- package/dist/scripts/codex-lb-desktop-bridge-latency-check.js +196 -0
- package/dist/scripts/codex-lb-desktop-capabilities-check.js +287 -0
- package/dist/scripts/codex-lb-desktop-real-evidence-check.js +123 -0
- package/dist/scripts/codex-lb-fast-mode-truth-check.js +10 -3
- package/dist/scripts/codex-lb-fast-ui-preservation-check.js +81 -40
- package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +99 -37
- package/dist/scripts/context-graph-check.js +191 -0
- package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +1 -1
- package/dist/scripts/gate-policy-audit-check.js +1 -1
- package/dist/scripts/helper-dedup-check.js +0 -2
- package/dist/scripts/installed-package-smoke-check.js +4 -1
- package/dist/scripts/latest-version-guidance-check.js +74 -0
- package/dist/scripts/release-metadata-check.js +21 -4
- package/dist/scripts/release-real-check.js +7 -0
- package/dist/scripts/ux-review-imagegen-blackbox-check.js +25 -1
- package/dist/scripts/ux-review-run-wires-imagegen-check.js +9 -1
- package/package.json +8 -3
- package/release-gates.v2.json +176 -1
- package/runtime-required-scripts.json +30 -0
- package/schemas/triwiki/context-graph.schema.json +119 -0
- package/dist/cli/install-helpers-codex-lb-selftest-chain.js +0 -112
- package/dist/cli/install-helpers-codex-lb-selftest.js +0 -481
- package/dist/core/triwiki/code-index-scanner.js +0 -309
|
@@ -0,0 +1,613 @@
|
|
|
1
|
+
import fsp from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { ensureDir, exists, runProcess, which, writeTextAtomic } from '../fsx.js';
|
|
5
|
+
import { loadCodexLbEnv } from './codex-lb-env.js';
|
|
6
|
+
import { DESKTOP_BRIDGE_ALLOWED_PATH_PREFIXES, DESKTOP_BRIDGE_LAUNCHD_LABEL, desktopBridgeConfigGeneration, desktopBridgeLaunchdPlistPath, desktopBridgeProcessExists, desktopBridgeStatePath, getDesktopBridgeStatus, preflightDesktopBridge, readDesktopBridgeState, renderDesktopBridgeLaunchdPlist, safeBridgeErrorCode, selectAvailableDesktopBridgePort, startPreparedDesktopBridge } from './desktop-bridge/index.js';
|
|
7
|
+
import { DEFAULT_CODEX_LB_GATEWAY_AUTH_TRANSPORT, parseCodexLbGatewayAuthTransport } from './desktop-mode.js';
|
|
8
|
+
export const DEFAULT_CODEX_LB_DESKTOP_BRIDGE_HOST = '127.0.0.1';
|
|
9
|
+
export const DEFAULT_CODEX_LB_DESKTOP_BRIDGE_PORT = 49_152;
|
|
10
|
+
export const CODEX_LB_DESKTOP_BRIDGE_SETTINGS_SCHEMA = 'sks.codex-lb-desktop-bridge-settings.v1';
|
|
11
|
+
export const CODEX_LB_DESKTOP_BRIDGE_SERVICE_SCHEMA = 'sks.codex-lb-desktop-bridge-service.v1';
|
|
12
|
+
const DEFAULT_ALLOWED_ORIGINS = ['app://codex'];
|
|
13
|
+
const DEFAULT_CONNECT_TIMEOUT_MS = 10_000;
|
|
14
|
+
const DEFAULT_IDLE_TIMEOUT_MS = 5 * 60_000;
|
|
15
|
+
export function desktopBridgeServicePaths(home = process.env.HOME || os.homedir()) {
|
|
16
|
+
const resolvedHome = path.resolve(home);
|
|
17
|
+
const runtimeDir = path.join(resolvedHome, '.codex', 'sks');
|
|
18
|
+
const logDir = path.join(runtimeDir, 'logs');
|
|
19
|
+
return {
|
|
20
|
+
settings_path: path.join(runtimeDir, 'codex-lb-desktop-bridge-settings.json'),
|
|
21
|
+
state_path: desktopBridgeStatePath(resolvedHome),
|
|
22
|
+
launch_agent_path: desktopBridgeLaunchdPlistPath(resolvedHome),
|
|
23
|
+
stdout_log_path: path.join(logDir, 'codex-lb-desktop-bridge.out.log'),
|
|
24
|
+
stderr_log_path: path.join(logDir, 'codex-lb-desktop-bridge.err.log')
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function defaultDesktopBridgeServiceSettings(input = {}) {
|
|
28
|
+
return validateDesktopBridgeServiceSettings({
|
|
29
|
+
schema: CODEX_LB_DESKTOP_BRIDGE_SETTINGS_SCHEMA,
|
|
30
|
+
listen_host: input.listen_host || DEFAULT_CODEX_LB_DESKTOP_BRIDGE_HOST,
|
|
31
|
+
listen_port: input.listen_port ?? DEFAULT_CODEX_LB_DESKTOP_BRIDGE_PORT,
|
|
32
|
+
gateway_auth_transport: parseCodexLbGatewayAuthTransport(input.gateway_auth_transport || DEFAULT_CODEX_LB_GATEWAY_AUTH_TRANSPORT),
|
|
33
|
+
allowed_origins: [...(input.allowed_origins || DEFAULT_ALLOWED_ORIGINS)],
|
|
34
|
+
connect_timeout_ms: input.connect_timeout_ms ?? DEFAULT_CONNECT_TIMEOUT_MS,
|
|
35
|
+
idle_timeout_ms: input.idle_timeout_ms ?? DEFAULT_IDLE_TIMEOUT_MS
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
export async function resolveDesktopBridgeActivationSettings(options = {}) {
|
|
39
|
+
const home = options.home || options.env?.HOME || process.env.HOME || os.homedir();
|
|
40
|
+
const paths = overridePaths(desktopBridgeServicePaths(home), options);
|
|
41
|
+
const persisted = await readDesktopBridgeServiceSettings(paths.settings_path);
|
|
42
|
+
const listenHost = options.settings?.listen_host
|
|
43
|
+
|| persisted?.listen_host
|
|
44
|
+
|| DEFAULT_CODEX_LB_DESKTOP_BRIDGE_HOST;
|
|
45
|
+
const listenPort = options.settings?.listen_port
|
|
46
|
+
?? persisted?.listen_port
|
|
47
|
+
?? await (options.selectAvailablePort || selectAvailableDesktopBridgePort)(listenHost);
|
|
48
|
+
return defaultDesktopBridgeServiceSettings({
|
|
49
|
+
...(persisted || {}),
|
|
50
|
+
...(options.settings || {}),
|
|
51
|
+
listen_host: listenHost,
|
|
52
|
+
listen_port: listenPort
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
export async function readDesktopBridgeServiceSettings(file) {
|
|
56
|
+
let stat;
|
|
57
|
+
try {
|
|
58
|
+
stat = await fsp.lstat(file);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
if (errorCode(error) === 'ENOENT')
|
|
62
|
+
return null;
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
66
|
+
throw new Error('desktop_bridge_settings_not_regular_file');
|
|
67
|
+
}
|
|
68
|
+
if ((stat.mode & 0o077) !== 0)
|
|
69
|
+
throw new Error('desktop_bridge_settings_permissions_unsafe');
|
|
70
|
+
const raw = await fsp.readFile(file, 'utf8');
|
|
71
|
+
if (Buffer.byteLength(raw) > 64 * 1024)
|
|
72
|
+
throw new Error('desktop_bridge_settings_too_large');
|
|
73
|
+
let parsed;
|
|
74
|
+
try {
|
|
75
|
+
parsed = JSON.parse(raw);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
throw new Error('desktop_bridge_settings_invalid_json');
|
|
79
|
+
}
|
|
80
|
+
return validateDesktopBridgeServiceSettings(parsed);
|
|
81
|
+
}
|
|
82
|
+
export async function writeDesktopBridgeServiceSettings(file, settings) {
|
|
83
|
+
const validated = validateDesktopBridgeServiceSettings(settings);
|
|
84
|
+
await writeTextAtomic(file, `${JSON.stringify(validated, null, 2)}\n`, { mode: 0o600 });
|
|
85
|
+
await fsp.chmod(file, 0o600);
|
|
86
|
+
}
|
|
87
|
+
export async function resolveDesktopBridgeRuntimeConfig(options = {}) {
|
|
88
|
+
const home = options.home || options.env?.HOME || process.env.HOME || os.homedir();
|
|
89
|
+
const defaults = desktopBridgeServicePaths(home);
|
|
90
|
+
const paths = overridePaths(defaults, options);
|
|
91
|
+
const persisted = await readDesktopBridgeServiceSettings(paths.settings_path);
|
|
92
|
+
const settings = defaultDesktopBridgeServiceSettings({
|
|
93
|
+
...(persisted || {}),
|
|
94
|
+
...(options.settings || {})
|
|
95
|
+
});
|
|
96
|
+
const loadedEnv = await loadCodexLbEnv({
|
|
97
|
+
home,
|
|
98
|
+
...(options.envPath ? { envPath: options.envPath } : {}),
|
|
99
|
+
...(options.metadataPath ? { metadataPath: options.metadataPath } : {}),
|
|
100
|
+
...(options.env ? { processEnv: options.env } : {})
|
|
101
|
+
});
|
|
102
|
+
if (!loadedEnv.base_url || !loadedEnv.secret_api_key || !loadedEnv.configured) {
|
|
103
|
+
throw new Error(loadedEnv.credential_binding.blockers[0]
|
|
104
|
+
|| loadedEnv.missing[0]
|
|
105
|
+
|| 'codex_lb_credentials_unavailable');
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
config: {
|
|
109
|
+
listenHost: settings.listen_host,
|
|
110
|
+
listenPort: settings.listen_port,
|
|
111
|
+
remoteBaseUrl: loadedEnv.base_url,
|
|
112
|
+
gatewayKey: loadedEnv.secret_api_key,
|
|
113
|
+
gatewayAuthTransport: settings.gateway_auth_transport,
|
|
114
|
+
allowedPathPrefixes: DESKTOP_BRIDGE_ALLOWED_PATH_PREFIXES,
|
|
115
|
+
allowedOrigins: settings.allowed_origins,
|
|
116
|
+
connectTimeoutMs: settings.connect_timeout_ms,
|
|
117
|
+
idleTimeoutMs: settings.idle_timeout_ms
|
|
118
|
+
},
|
|
119
|
+
settings,
|
|
120
|
+
loaded_env: loadedEnv,
|
|
121
|
+
paths
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
export async function desktopBridgeServiceStatus(options = {}) {
|
|
125
|
+
const home = options.home || options.env?.HOME || process.env.HOME || os.homedir();
|
|
126
|
+
const paths = overridePaths(desktopBridgeServicePaths(home), options);
|
|
127
|
+
const service = desktopBridgeLaunchService(options.uid);
|
|
128
|
+
if ((options.platform || process.platform) !== 'darwin') {
|
|
129
|
+
return {
|
|
130
|
+
schema: CODEX_LB_DESKTOP_BRIDGE_SERVICE_SCHEMA,
|
|
131
|
+
ok: false,
|
|
132
|
+
supported: false,
|
|
133
|
+
installed: await exists(paths.launch_agent_path),
|
|
134
|
+
loaded: false,
|
|
135
|
+
running: false,
|
|
136
|
+
status: 'unsupported',
|
|
137
|
+
service,
|
|
138
|
+
paths,
|
|
139
|
+
state: await readDesktopBridgeState(paths.state_path).catch(() => null),
|
|
140
|
+
settings: await readDesktopBridgeServiceSettings(paths.settings_path).catch(() => null),
|
|
141
|
+
expected_config_generation: null,
|
|
142
|
+
credential_source: null,
|
|
143
|
+
blockers: ['desktop_bridge_service_requires_macos']
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
const installed = await exists(paths.launch_agent_path);
|
|
147
|
+
const settings = await readDesktopBridgeServiceSettings(paths.settings_path).catch(() => null);
|
|
148
|
+
let expectedConfigGeneration = null;
|
|
149
|
+
let credentialSource = null;
|
|
150
|
+
const credentialBlockers = [];
|
|
151
|
+
if (settings) {
|
|
152
|
+
try {
|
|
153
|
+
const runtime = await resolveDesktopBridgeRuntimeConfig({ ...options, home, settingsPath: paths.settings_path });
|
|
154
|
+
expectedConfigGeneration = desktopBridgeConfigGeneration(runtime.config);
|
|
155
|
+
credentialSource = runtime.loaded_env.source;
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
credentialBlockers.push(safeServiceError(error));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const bridgeStatus = await getDesktopBridgeStatus({
|
|
162
|
+
statePath: paths.state_path,
|
|
163
|
+
...(expectedConfigGeneration ? { expectedConfigGeneration } : {}),
|
|
164
|
+
...(options.processExists ? { processExists: options.processExists } : {})
|
|
165
|
+
});
|
|
166
|
+
const launchd = await inspectLaunchdService(options, service);
|
|
167
|
+
const blockers = [
|
|
168
|
+
...(settings ? [] : ['desktop_bridge_settings_missing']),
|
|
169
|
+
...credentialBlockers,
|
|
170
|
+
...(bridgeStatus.status === 'running' ? [] : [bridgeStatusBlocker(bridgeStatus)]),
|
|
171
|
+
...(launchd.loaded && !launchd.running ? ['desktop_bridge_launchd_not_running'] : [])
|
|
172
|
+
].filter(Boolean);
|
|
173
|
+
const running = bridgeStatus.status === 'running';
|
|
174
|
+
return {
|
|
175
|
+
schema: CODEX_LB_DESKTOP_BRIDGE_SERVICE_SCHEMA,
|
|
176
|
+
ok: running && blockers.length === 0,
|
|
177
|
+
supported: true,
|
|
178
|
+
installed,
|
|
179
|
+
loaded: launchd.loaded,
|
|
180
|
+
running,
|
|
181
|
+
status: !settings
|
|
182
|
+
? 'settings_missing'
|
|
183
|
+
: credentialBlockers.length
|
|
184
|
+
? 'credentials_unavailable'
|
|
185
|
+
: bridgeStatus.status,
|
|
186
|
+
service,
|
|
187
|
+
paths,
|
|
188
|
+
state: bridgeStatus.state,
|
|
189
|
+
settings,
|
|
190
|
+
expected_config_generation: expectedConfigGeneration,
|
|
191
|
+
credential_source: credentialSource,
|
|
192
|
+
blockers
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
export async function installAndStartDesktopBridgeService(options = {}) {
|
|
196
|
+
const platform = options.platform || process.platform;
|
|
197
|
+
const home = options.home || options.env?.HOME || process.env.HOME || os.homedir();
|
|
198
|
+
const paths = overridePaths(desktopBridgeServicePaths(home), options);
|
|
199
|
+
if (platform !== 'darwin') {
|
|
200
|
+
return desktopBridgeServiceStatus({ ...options, home, platform });
|
|
201
|
+
}
|
|
202
|
+
const settings = await resolveDesktopBridgeActivationSettings({
|
|
203
|
+
...options,
|
|
204
|
+
home,
|
|
205
|
+
settingsPath: paths.settings_path
|
|
206
|
+
});
|
|
207
|
+
const loadedEnv = await loadCodexLbEnv({
|
|
208
|
+
home,
|
|
209
|
+
...(options.envPath ? { envPath: options.envPath } : {}),
|
|
210
|
+
...(options.metadataPath ? { metadataPath: options.metadataPath } : {}),
|
|
211
|
+
...(options.env ? { processEnv: options.env } : {})
|
|
212
|
+
});
|
|
213
|
+
if (!loadedEnv.base_url || !loadedEnv.secret_api_key || !loadedEnv.configured) {
|
|
214
|
+
return {
|
|
215
|
+
schema: CODEX_LB_DESKTOP_BRIDGE_SERVICE_SCHEMA,
|
|
216
|
+
ok: false,
|
|
217
|
+
supported: true,
|
|
218
|
+
installed: await exists(paths.launch_agent_path),
|
|
219
|
+
loaded: false,
|
|
220
|
+
running: false,
|
|
221
|
+
status: 'credentials_unavailable',
|
|
222
|
+
service: desktopBridgeLaunchService(options.uid),
|
|
223
|
+
paths,
|
|
224
|
+
state: await readDesktopBridgeState(paths.state_path).catch(() => null),
|
|
225
|
+
settings,
|
|
226
|
+
expected_config_generation: null,
|
|
227
|
+
credential_source: loadedEnv.source,
|
|
228
|
+
blockers: [
|
|
229
|
+
loadedEnv.credential_binding.blockers[0]
|
|
230
|
+
|| loadedEnv.missing[0]
|
|
231
|
+
|| 'codex_lb_credentials_unavailable'
|
|
232
|
+
]
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
const config = {
|
|
236
|
+
listenHost: settings.listen_host,
|
|
237
|
+
listenPort: settings.listen_port,
|
|
238
|
+
remoteBaseUrl: loadedEnv.base_url,
|
|
239
|
+
gatewayKey: loadedEnv.secret_api_key,
|
|
240
|
+
gatewayAuthTransport: settings.gateway_auth_transport,
|
|
241
|
+
allowedPathPrefixes: DESKTOP_BRIDGE_ALLOWED_PATH_PREFIXES,
|
|
242
|
+
allowedOrigins: settings.allowed_origins,
|
|
243
|
+
connectTimeoutMs: settings.connect_timeout_ms,
|
|
244
|
+
idleTimeoutMs: settings.idle_timeout_ms
|
|
245
|
+
};
|
|
246
|
+
try {
|
|
247
|
+
await preflightDesktopBridge(config);
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
return failedServiceStatus({
|
|
251
|
+
paths,
|
|
252
|
+
settings,
|
|
253
|
+
service: desktopBridgeLaunchService(options.uid),
|
|
254
|
+
status: 'credentials_unavailable',
|
|
255
|
+
credentialSource: loadedEnv.source,
|
|
256
|
+
expectedConfigGeneration: desktopBridgeConfigGeneration(config),
|
|
257
|
+
blocker: safeBridgeErrorCode(error)
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
const launchCommand = await resolveDesktopBridgeLaunchCommand(options);
|
|
261
|
+
if (!launchCommand) {
|
|
262
|
+
return failedServiceStatus({
|
|
263
|
+
paths,
|
|
264
|
+
settings,
|
|
265
|
+
service: desktopBridgeLaunchService(options.uid),
|
|
266
|
+
status: 'settings_missing',
|
|
267
|
+
credentialSource: loadedEnv.source,
|
|
268
|
+
expectedConfigGeneration: desktopBridgeConfigGeneration(config),
|
|
269
|
+
blocker: 'desktop_bridge_sks_executable_missing'
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
await Promise.all([
|
|
273
|
+
ensureDir(path.dirname(paths.settings_path)),
|
|
274
|
+
ensureDir(path.dirname(paths.launch_agent_path)),
|
|
275
|
+
ensureDir(path.dirname(paths.stdout_log_path))
|
|
276
|
+
]);
|
|
277
|
+
await writeDesktopBridgeServiceSettings(paths.settings_path, settings);
|
|
278
|
+
const launchArguments = [
|
|
279
|
+
...launchCommand.arguments,
|
|
280
|
+
'codex-lb',
|
|
281
|
+
'bridge',
|
|
282
|
+
'serve',
|
|
283
|
+
'--settings',
|
|
284
|
+
paths.settings_path,
|
|
285
|
+
'--json'
|
|
286
|
+
];
|
|
287
|
+
const plist = renderDesktopBridgeLaunchdPlist({
|
|
288
|
+
executablePath: launchCommand.executable,
|
|
289
|
+
arguments: launchArguments,
|
|
290
|
+
stdoutPath: paths.stdout_log_path,
|
|
291
|
+
stderrPath: paths.stderr_log_path
|
|
292
|
+
});
|
|
293
|
+
await writeTextAtomic(paths.launch_agent_path, plist, { mode: 0o600 });
|
|
294
|
+
await fsp.chmod(paths.launch_agent_path, 0o600);
|
|
295
|
+
const run = options.run || runProcess;
|
|
296
|
+
const launchctl = options.launchctl || '/bin/launchctl';
|
|
297
|
+
const domain = desktopBridgeLaunchDomain(options.uid);
|
|
298
|
+
const service = desktopBridgeLaunchService(options.uid);
|
|
299
|
+
await run(launchctl, ['bootout', service], {
|
|
300
|
+
timeoutMs: 5_000,
|
|
301
|
+
maxOutputBytes: 16 * 1024
|
|
302
|
+
}).catch(() => undefined);
|
|
303
|
+
await run(launchctl, ['bootout', domain, paths.launch_agent_path], {
|
|
304
|
+
timeoutMs: 5_000,
|
|
305
|
+
maxOutputBytes: 16 * 1024
|
|
306
|
+
}).catch(() => undefined);
|
|
307
|
+
await removeStaleBridgeState(paths.state_path, options.processExists);
|
|
308
|
+
const bootstrap = await run(launchctl, ['bootstrap', domain, paths.launch_agent_path], {
|
|
309
|
+
timeoutMs: 10_000,
|
|
310
|
+
maxOutputBytes: 32 * 1024
|
|
311
|
+
}).catch((error) => failedProcess(error));
|
|
312
|
+
if (bootstrap.code !== 0 && !bootstrap.timedOut) {
|
|
313
|
+
return failedServiceStatus({
|
|
314
|
+
paths,
|
|
315
|
+
settings,
|
|
316
|
+
service,
|
|
317
|
+
status: 'missing',
|
|
318
|
+
credentialSource: loadedEnv.source,
|
|
319
|
+
expectedConfigGeneration: desktopBridgeConfigGeneration(config),
|
|
320
|
+
blocker: 'desktop_bridge_launchd_bootstrap_failed'
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
await run(launchctl, ['kickstart', '-k', service], {
|
|
324
|
+
timeoutMs: 10_000,
|
|
325
|
+
maxOutputBytes: 32 * 1024
|
|
326
|
+
}).catch(() => undefined);
|
|
327
|
+
const status = await waitForDesktopBridge({
|
|
328
|
+
...options,
|
|
329
|
+
home,
|
|
330
|
+
platform,
|
|
331
|
+
settingsPath: paths.settings_path,
|
|
332
|
+
statePath: paths.state_path,
|
|
333
|
+
launchAgentPath: paths.launch_agent_path,
|
|
334
|
+
stdoutLogPath: paths.stdout_log_path,
|
|
335
|
+
stderrLogPath: paths.stderr_log_path
|
|
336
|
+
});
|
|
337
|
+
if (!status.ok) {
|
|
338
|
+
await run(launchctl, ['bootout', service], {
|
|
339
|
+
timeoutMs: 5_000,
|
|
340
|
+
maxOutputBytes: 16 * 1024
|
|
341
|
+
}).catch(() => undefined);
|
|
342
|
+
}
|
|
343
|
+
return status;
|
|
344
|
+
}
|
|
345
|
+
export async function bootstrapExistingDesktopBridgeService(options = {}) {
|
|
346
|
+
const home = options.home || options.env?.HOME || process.env.HOME || os.homedir();
|
|
347
|
+
const paths = overridePaths(desktopBridgeServicePaths(home), options);
|
|
348
|
+
if ((options.platform || process.platform) !== 'darwin') {
|
|
349
|
+
return desktopBridgeServiceStatus({ ...options, home });
|
|
350
|
+
}
|
|
351
|
+
if (!(await exists(paths.launch_agent_path)) || !(await exists(paths.settings_path))) {
|
|
352
|
+
return desktopBridgeServiceStatus({ ...options, home });
|
|
353
|
+
}
|
|
354
|
+
const run = options.run || runProcess;
|
|
355
|
+
const launchctl = options.launchctl || '/bin/launchctl';
|
|
356
|
+
const domain = desktopBridgeLaunchDomain(options.uid);
|
|
357
|
+
const service = desktopBridgeLaunchService(options.uid);
|
|
358
|
+
await run(launchctl, ['bootout', service], {
|
|
359
|
+
timeoutMs: 5_000,
|
|
360
|
+
maxOutputBytes: 16 * 1024
|
|
361
|
+
}).catch(() => undefined);
|
|
362
|
+
const bootstrap = await run(launchctl, ['bootstrap', domain, paths.launch_agent_path], {
|
|
363
|
+
timeoutMs: 10_000,
|
|
364
|
+
maxOutputBytes: 32 * 1024
|
|
365
|
+
}).catch((error) => failedProcess(error));
|
|
366
|
+
if (bootstrap.code === 0 || bootstrap.timedOut) {
|
|
367
|
+
await run(launchctl, ['kickstart', '-k', service], {
|
|
368
|
+
timeoutMs: 10_000,
|
|
369
|
+
maxOutputBytes: 32 * 1024
|
|
370
|
+
}).catch(() => undefined);
|
|
371
|
+
}
|
|
372
|
+
return waitForDesktopBridge({ ...options, home });
|
|
373
|
+
}
|
|
374
|
+
export async function stopDesktopBridgeService(options = {}) {
|
|
375
|
+
const home = options.home || options.env?.HOME || process.env.HOME || os.homedir();
|
|
376
|
+
const paths = overridePaths(desktopBridgeServicePaths(home), options);
|
|
377
|
+
if ((options.platform || process.platform) !== 'darwin') {
|
|
378
|
+
return desktopBridgeServiceStatus({ ...options, home });
|
|
379
|
+
}
|
|
380
|
+
const run = options.run || runProcess;
|
|
381
|
+
const launchctl = options.launchctl || '/bin/launchctl';
|
|
382
|
+
const service = desktopBridgeLaunchService(options.uid);
|
|
383
|
+
await run(launchctl, ['bootout', service], {
|
|
384
|
+
timeoutMs: 8_000,
|
|
385
|
+
maxOutputBytes: 32 * 1024
|
|
386
|
+
}).catch(() => undefined);
|
|
387
|
+
for (let attempt = 0; attempt < 20; attempt += 1) {
|
|
388
|
+
const state = await readDesktopBridgeState(paths.state_path).catch(() => null);
|
|
389
|
+
if (!state || !(options.processExists || desktopBridgeProcessExists)(state.pid))
|
|
390
|
+
break;
|
|
391
|
+
await delay(100);
|
|
392
|
+
}
|
|
393
|
+
await removeStaleBridgeState(paths.state_path, options.processExists);
|
|
394
|
+
if (options.removePlist === true)
|
|
395
|
+
await fsp.unlink(paths.launch_agent_path).catch(() => undefined);
|
|
396
|
+
if (options.removeSettings === true)
|
|
397
|
+
await fsp.unlink(paths.settings_path).catch(() => undefined);
|
|
398
|
+
const status = await desktopBridgeServiceStatus({ ...options, home });
|
|
399
|
+
return {
|
|
400
|
+
...status,
|
|
401
|
+
ok: !status.running,
|
|
402
|
+
blockers: status.running ? ['desktop_bridge_process_still_running'] : []
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
export async function serveDesktopBridge(options = {}) {
|
|
406
|
+
let handle = null;
|
|
407
|
+
try {
|
|
408
|
+
const runtime = await resolveDesktopBridgeRuntimeConfig(options);
|
|
409
|
+
const prepared = await preflightDesktopBridge(runtime.config);
|
|
410
|
+
handle = await startPreparedDesktopBridge(prepared, { statePath: runtime.paths.state_path });
|
|
411
|
+
process.stdout.write(`${JSON.stringify({
|
|
412
|
+
schema: 'sks.codex-lb-desktop-bridge-log.v1',
|
|
413
|
+
event: 'sks.codex_lb.bridge.started',
|
|
414
|
+
pid: handle.state.pid,
|
|
415
|
+
listen_origin: handle.state.listen_origin,
|
|
416
|
+
remote_origin_sha256: handle.state.remote_origin_sha256,
|
|
417
|
+
gateway_auth_transport: handle.state.gateway_auth_transport,
|
|
418
|
+
secret_fields_redacted: true
|
|
419
|
+
})}\n`);
|
|
420
|
+
await waitForShutdownSignal(handle);
|
|
421
|
+
return {
|
|
422
|
+
schema: 'sks.codex-lb-desktop-bridge-serve.v1',
|
|
423
|
+
ok: true,
|
|
424
|
+
status: 'stopped',
|
|
425
|
+
state: handle.state
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
return {
|
|
430
|
+
schema: 'sks.codex-lb-desktop-bridge-serve.v1',
|
|
431
|
+
ok: false,
|
|
432
|
+
status: 'failed',
|
|
433
|
+
state: handle?.state || null,
|
|
434
|
+
blocker: safeServiceError(error)
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
finally {
|
|
438
|
+
if (handle)
|
|
439
|
+
await handle.stop().catch(() => undefined);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
function validateDesktopBridgeServiceSettings(value) {
|
|
443
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
444
|
+
throw new Error('desktop_bridge_settings_invalid');
|
|
445
|
+
}
|
|
446
|
+
const row = value;
|
|
447
|
+
if (row.schema !== CODEX_LB_DESKTOP_BRIDGE_SETTINGS_SCHEMA) {
|
|
448
|
+
throw new Error('desktop_bridge_settings_schema_invalid');
|
|
449
|
+
}
|
|
450
|
+
const listenHost = row.listen_host;
|
|
451
|
+
const listenPort = Number(row.listen_port);
|
|
452
|
+
const gatewayAuthTransport = parseCodexLbGatewayAuthTransport(row.gateway_auth_transport);
|
|
453
|
+
const allowedOrigins = Array.isArray(row.allowed_origins)
|
|
454
|
+
? row.allowed_origins.map((entry) => String(entry || '').trim()).filter(Boolean)
|
|
455
|
+
: [];
|
|
456
|
+
const connectTimeoutMs = Number(row.connect_timeout_ms);
|
|
457
|
+
const idleTimeoutMs = Number(row.idle_timeout_ms);
|
|
458
|
+
if (listenHost !== '127.0.0.1' && listenHost !== '::1') {
|
|
459
|
+
throw new Error('desktop_bridge_settings_listen_host_invalid');
|
|
460
|
+
}
|
|
461
|
+
if (!Number.isInteger(listenPort) || listenPort < 49_152 || listenPort > 65_535) {
|
|
462
|
+
throw new Error('desktop_bridge_settings_listen_port_invalid');
|
|
463
|
+
}
|
|
464
|
+
if (!allowedOrigins.length)
|
|
465
|
+
throw new Error('desktop_bridge_settings_allowed_origins_empty');
|
|
466
|
+
if (!Number.isFinite(connectTimeoutMs) || connectTimeoutMs < 100 || connectTimeoutMs > 120_000) {
|
|
467
|
+
throw new Error('desktop_bridge_settings_connect_timeout_invalid');
|
|
468
|
+
}
|
|
469
|
+
if (!Number.isFinite(idleTimeoutMs) || idleTimeoutMs < 1_000 || idleTimeoutMs > 86_400_000) {
|
|
470
|
+
throw new Error('desktop_bridge_settings_idle_timeout_invalid');
|
|
471
|
+
}
|
|
472
|
+
return {
|
|
473
|
+
schema: CODEX_LB_DESKTOP_BRIDGE_SETTINGS_SCHEMA,
|
|
474
|
+
listen_host: listenHost,
|
|
475
|
+
listen_port: listenPort,
|
|
476
|
+
gateway_auth_transport: gatewayAuthTransport,
|
|
477
|
+
allowed_origins: [...new Set(allowedOrigins)],
|
|
478
|
+
connect_timeout_ms: connectTimeoutMs,
|
|
479
|
+
idle_timeout_ms: idleTimeoutMs
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
function overridePaths(defaults, options) {
|
|
483
|
+
return {
|
|
484
|
+
settings_path: options.settingsPath || defaults.settings_path,
|
|
485
|
+
state_path: options.statePath || defaults.state_path,
|
|
486
|
+
launch_agent_path: options.launchAgentPath || defaults.launch_agent_path,
|
|
487
|
+
stdout_log_path: options.stdoutLogPath || defaults.stdout_log_path,
|
|
488
|
+
stderr_log_path: options.stderrLogPath || defaults.stderr_log_path
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
function desktopBridgeLaunchDomain(uid = typeof process.getuid === 'function' ? process.getuid() : 0) {
|
|
492
|
+
return `gui/${uid}`;
|
|
493
|
+
}
|
|
494
|
+
function desktopBridgeLaunchService(uid) {
|
|
495
|
+
return `${desktopBridgeLaunchDomain(uid)}/${DESKTOP_BRIDGE_LAUNCHD_LABEL}`;
|
|
496
|
+
}
|
|
497
|
+
async function resolveDesktopBridgeLaunchCommand(options) {
|
|
498
|
+
if (options.executablePath) {
|
|
499
|
+
const executable = path.resolve(options.executablePath);
|
|
500
|
+
return await exists(executable)
|
|
501
|
+
? { executable, arguments: [...(options.executableArguments || [])] }
|
|
502
|
+
: null;
|
|
503
|
+
}
|
|
504
|
+
const entry = String(process.argv[1] || '').trim();
|
|
505
|
+
const entryName = path.basename(entry).replace(/\.js$/i, '');
|
|
506
|
+
if (entry
|
|
507
|
+
&& (entryName === 'sks' || entryName === 'sneakoscope')
|
|
508
|
+
&& await exists(entry)) {
|
|
509
|
+
return {
|
|
510
|
+
executable: path.resolve(process.execPath),
|
|
511
|
+
arguments: [path.resolve(entry)]
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
const sks = await which('sks').catch(() => null);
|
|
515
|
+
if (sks)
|
|
516
|
+
return { executable: path.resolve(sks), arguments: [] };
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
async function inspectLaunchdService(options, service) {
|
|
520
|
+
if ((options.platform || process.platform) !== 'darwin')
|
|
521
|
+
return { loaded: false, running: false };
|
|
522
|
+
const run = options.run || runProcess;
|
|
523
|
+
const launchctl = options.launchctl || '/bin/launchctl';
|
|
524
|
+
const result = await run(launchctl, ['print', service], {
|
|
525
|
+
timeoutMs: 3_000,
|
|
526
|
+
maxOutputBytes: 32 * 1024
|
|
527
|
+
}).catch(() => null);
|
|
528
|
+
if (!result || result.code !== 0)
|
|
529
|
+
return { loaded: false, running: false };
|
|
530
|
+
const text = `${result.stdout || ''}\n${result.stderr || ''}`;
|
|
531
|
+
const state = text.match(/^[ \t]*state = ([^\n]+)/m)?.[1]?.trim() || null;
|
|
532
|
+
const pid = Number(text.match(/^[ \t]*pid = (\d+)/m)?.[1] || 0);
|
|
533
|
+
return { loaded: true, running: state === 'running' && pid > 0 };
|
|
534
|
+
}
|
|
535
|
+
async function waitForDesktopBridge(options) {
|
|
536
|
+
let status = await desktopBridgeServiceStatus(options);
|
|
537
|
+
for (let attempt = 0; attempt < 40 && !status.ok; attempt += 1) {
|
|
538
|
+
await delay(100);
|
|
539
|
+
status = await desktopBridgeServiceStatus(options);
|
|
540
|
+
}
|
|
541
|
+
return status;
|
|
542
|
+
}
|
|
543
|
+
async function removeStaleBridgeState(statePath, processExists) {
|
|
544
|
+
const state = await readDesktopBridgeState(statePath).catch(() => null);
|
|
545
|
+
if (!state || !(processExists || desktopBridgeProcessExists)(state.pid)) {
|
|
546
|
+
await fsp.unlink(statePath).catch(() => undefined);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
async function waitForShutdownSignal(handle) {
|
|
550
|
+
await new Promise((resolve) => {
|
|
551
|
+
let settled = false;
|
|
552
|
+
const finish = () => {
|
|
553
|
+
if (settled)
|
|
554
|
+
return;
|
|
555
|
+
settled = true;
|
|
556
|
+
process.off('SIGINT', finish);
|
|
557
|
+
process.off('SIGTERM', finish);
|
|
558
|
+
resolve();
|
|
559
|
+
};
|
|
560
|
+
process.once('SIGINT', finish);
|
|
561
|
+
process.once('SIGTERM', finish);
|
|
562
|
+
handle.server.once('close', finish);
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
function failedServiceStatus(input) {
|
|
566
|
+
return {
|
|
567
|
+
schema: CODEX_LB_DESKTOP_BRIDGE_SERVICE_SCHEMA,
|
|
568
|
+
ok: false,
|
|
569
|
+
supported: true,
|
|
570
|
+
installed: false,
|
|
571
|
+
loaded: false,
|
|
572
|
+
running: false,
|
|
573
|
+
status: input.status,
|
|
574
|
+
service: input.service,
|
|
575
|
+
paths: input.paths,
|
|
576
|
+
state: null,
|
|
577
|
+
settings: input.settings,
|
|
578
|
+
expected_config_generation: input.expectedConfigGeneration,
|
|
579
|
+
credential_source: input.credentialSource,
|
|
580
|
+
blockers: [input.blocker]
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
function bridgeStatusBlocker(status) {
|
|
584
|
+
if (status.status === 'missing')
|
|
585
|
+
return 'desktop_bridge_state_missing';
|
|
586
|
+
if (status.status === 'running')
|
|
587
|
+
return '';
|
|
588
|
+
return status.blocker;
|
|
589
|
+
}
|
|
590
|
+
function safeServiceError(error) {
|
|
591
|
+
const bridge = safeBridgeErrorCode(error);
|
|
592
|
+
if (bridge !== 'bridge_upstream_unavailable')
|
|
593
|
+
return bridge;
|
|
594
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
595
|
+
return /^[a-z0-9_:-]+$/i.test(message) ? message : 'desktop_bridge_service_failed';
|
|
596
|
+
}
|
|
597
|
+
function failedProcess(error) {
|
|
598
|
+
return {
|
|
599
|
+
code: 1,
|
|
600
|
+
stdout: '',
|
|
601
|
+
stderr: safeServiceError(error),
|
|
602
|
+
stdoutBytes: 0,
|
|
603
|
+
stderrBytes: 0,
|
|
604
|
+
truncated: false,
|
|
605
|
+
timedOut: false
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
function errorCode(error) {
|
|
609
|
+
return error && typeof error === 'object' && 'code' in error ? String(error.code) : '';
|
|
610
|
+
}
|
|
611
|
+
function delay(ms) {
|
|
612
|
+
return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
613
|
+
}
|