From 4ad01299845946bc662995e6136630322ca6a985 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Mon, 12 Oct 2026 04:16:28 -0700 Subject: [PATCH] refactor(orcad): orcad artifacts live under orcad/ and node-runtime/ (#27963) Co-authored-by: m4air --- .github/workflows/ssh-hostile-hosts.yml | 3 + .github/workflows/ssh-windows-hosts.yml | 3 + ...heck-runtime-launcher-protocol-ratchet.mjs | 4 +- config/scripts/ci-shard-timings.json | 10 +- config/scripts/node-server-qualification.mjs | 9 +- config/scripts/packaged-orcad-template.cjs | 2 +- .../opencode-wsl-runtime-preparation.test.ts | 2 +- .../opencode-wsl-runtime-preparation.ts | 2 +- .../ssh-host-server-on-connect-wiring.test.ts | 4 +- .../ipc/ssh-host-server-on-connect-wiring.ts | 2 +- .../{ssh => node-runtime}/orcad-cache-path.ts | 0 .../pinned-runtime-materializer-node.test.ts | 0 .../pinned-runtime-materializer.test.ts | 0 .../pinned-runtime-materializer.ts | 0 .../runtime-archive-download.test.ts | 0 .../runtime-archive-download.ts | 0 src/main/orcad/build-toolchain-diagnosis.ts | 97 ++++++++ src/main/orcad/node-pty-precondition.ts | 2 +- .../orcad/orcad-artifact-cache-retention.ts | 2 +- .../orcad-artifact-materializer.test.ts | 4 +- .../orcad-artifact-materializer.ts | 4 +- .../orcad-host-unavailable.test.ts | 4 +- .../{ssh => orcad}/orcad-host-unavailable.ts | 0 src/main/orcad/orcad-local-serve-selection.ts | 4 +- ...ckaged-structured-chat.integration.test.ts | 2 +- src/main/ssh/build-toolchain-diagnosis.ts | 228 ------------------ src/main/ssh/orcad-activation-gate.test.ts | 2 +- src/main/ssh/orcad-installed-activation.ts | 2 +- .../ssh/orcad-managed-auto-update.test.ts | 6 +- src/main/ssh/orcad-managed-auto-update.ts | 7 +- src/main/ssh/orcad-remote-context.ts | 2 +- src/main/ssh/orcad-remote-deploy.ts | 4 +- src/main/ssh/orcad-remote-launch-windows.ts | 2 +- src/main/ssh/orcad-remote-windows-node.ts | 2 +- src/main/ssh/orcad-runtime-deployment.test.ts | 2 +- src/main/ssh/orcad-runtime-deployment.ts | 2 +- .../ssh/orcad-runtime-maintenance.test.ts | 2 +- src/main/ssh/orcad-runtime-maintenance.ts | 2 +- src/main/ssh/orcad-runtime-target.ts | 2 +- .../ssh/orcad-stdio-bridge-provider.test.ts | 2 +- src/main/ssh/orcad-stdio-bridge-provider.ts | 2 +- src/main/ssh/orcad-stdio-bridge.test.ts | 2 +- src/main/ssh/orcad-stdio-bridge.ts | 2 +- .../ssh/orcad-windows-host-preparation.ts | 2 +- .../ssh/ssh-host-server-connect-events.ts | 2 +- ...h-host-server-on-connect-telemetry.test.ts | 2 +- .../ssh/ssh-host-server-on-connect.test.ts | 2 +- src/main/ssh/ssh-host-server-on-connect.ts | 2 +- src/main/ssh/ssh-hostile-host-cells.ts | 2 +- .../ssh/ssh-hostile-host-managed-orcad.ts | 2 +- src/main/wsl/wsl-pinned-runtime.test.ts | 2 +- src/main/wsl/wsl-pinned-runtime.ts | 2 +- 52 files changed, 169 insertions(+), 282 deletions(-) rename src/main/{ssh => node-runtime}/orcad-cache-path.ts (100%) rename src/main/{ssh => node-runtime}/pinned-runtime-materializer-node.test.ts (100%) rename src/main/{ssh => node-runtime}/pinned-runtime-materializer.test.ts (100%) rename src/main/{ssh => node-runtime}/pinned-runtime-materializer.ts (100%) rename src/main/{ssh => node-runtime}/runtime-archive-download.test.ts (100%) rename src/main/{ssh => node-runtime}/runtime-archive-download.ts (100%) create mode 100644 src/main/orcad/build-toolchain-diagnosis.ts rename src/main/{ssh => orcad}/orcad-artifact-materializer.test.ts (98%) rename src/main/{ssh => orcad}/orcad-artifact-materializer.ts (98%) rename src/main/{ssh => orcad}/orcad-host-unavailable.test.ts (88%) rename src/main/{ssh => orcad}/orcad-host-unavailable.ts (100%) delete mode 100644 src/main/ssh/build-toolchain-diagnosis.ts diff --git a/.github/workflows/ssh-hostile-hosts.yml b/.github/workflows/ssh-hostile-hosts.yml index 1c1505e677e..b5f55598322 100644 --- a/.github/workflows/ssh-hostile-hosts.yml +++ b/.github/workflows/ssh-hostile-hosts.yml @@ -13,6 +13,9 @@ on: - 'src/main/ssh/ssh-relay-*' - 'src/main/ssh/*runtime*' - 'src/main/ssh/orcad-*' + - 'src/main/orcad/orcad-artifact-materializer.ts' + - 'src/main/orcad/orcad-host-unavailable.ts' + - 'src/main/node-runtime/**' - 'src/main/ssh/remote-install-*' - 'src/main/ssh/ssh-remote-*' - 'src/main/ssh/ssh-hostile-host-*' diff --git a/.github/workflows/ssh-windows-hosts.yml b/.github/workflows/ssh-windows-hosts.yml index abea728f2da..1f0e0bdba68 100644 --- a/.github/workflows/ssh-windows-hosts.yml +++ b/.github/workflows/ssh-windows-hosts.yml @@ -11,6 +11,9 @@ on: - 'src/main/ssh/ssh-relay-*' - 'src/main/ssh/*runtime*' - 'src/main/ssh/orcad-*' + - 'src/main/orcad/orcad-artifact-materializer.ts' + - 'src/main/orcad/orcad-host-unavailable.ts' + - 'src/main/node-runtime/**' - 'src/main/ssh/remote-install-*' - 'src/main/ssh/ssh-remote-*' - 'src/main/ssh/ssh-hostile-host-*' diff --git a/config/scripts/check-runtime-launcher-protocol-ratchet.mjs b/config/scripts/check-runtime-launcher-protocol-ratchet.mjs index d289819fe67..950b0cfbac9 100644 --- a/config/scripts/check-runtime-launcher-protocol-ratchet.mjs +++ b/config/scripts/check-runtime-launcher-protocol-ratchet.mjs @@ -15,8 +15,8 @@ export const RUNTIME_LAUNCHER_PATHS = [ 'src/main/orcad/launcher.ts', 'config/scripts/orcad-entry-build.mjs', 'src/shared/node-runtime-pin.ts', - 'src/main/ssh/pinned-runtime-materializer.ts', - 'src/main/ssh/runtime-archive-download.ts', + 'src/main/node-runtime/pinned-runtime-materializer.ts', + 'src/main/node-runtime/runtime-archive-download.ts', 'src/main/ssh/orcad-remote-node-runtime.ts', // orcad slot layout: which runtime file a packaged slot carries. 'src/shared/orcad-artifacts.ts', diff --git a/config/scripts/ci-shard-timings.json b/config/scripts/ci-shard-timings.json index 1d99a4b26e9..38ddcfe1572 100644 --- a/config/scripts/ci-shard-timings.json +++ b/config/scripts/ci-shard-timings.json @@ -5281,7 +5281,7 @@ "src/main/ssh/orcad-activation-outcome-log.test.ts": 55, "src/main/ssh/orcad-activation-record.test.ts": 254, "src/main/ssh/orcad-activation-transaction.test.ts": 398, - "src/main/ssh/orcad-artifact-materializer.test.ts": 722, + "src/main/orcad/orcad-artifact-materializer.test.ts": 722, "src/main/ssh/orcad-decommission-released-peer-recovery.test.ts": 295, "src/main/ssh/orcad-decommission-transaction.test.ts": 232, "src/main/ssh/orcad-deployment-target.test.ts": 234, @@ -5290,7 +5290,7 @@ "src/main/ssh/orcad-exited-own-install-lock.test.ts": 4492, "src/main/ssh/orcad-exited-own-lock-windows.test.ts": 350, "src/main/ssh/orcad-fence-lost-classification.test.ts": 287, - "src/main/ssh/orcad-host-unavailable.test.ts": 325, + "src/main/orcad/orcad-host-unavailable.test.ts": 325, "src/main/ssh/orcad-managed-auto-update.test.ts": 319, "src/main/ssh/orcad-managed-bound-port.test.ts": 386, "src/main/ssh/orcad-managed-serving.test.ts": 480, @@ -5364,8 +5364,8 @@ "src/main/ssh/orcad-windows-host-fence-ops.test.ts": 763, "src/main/ssh/orcad-windows-host-lane.test.ts": 745, "src/main/ssh/orcad-windows-host-state-ops.test.ts": 2035, - "src/main/ssh/pinned-runtime-materializer-node.test.ts": 114, - "src/main/ssh/pinned-runtime-materializer.test.ts": 3192, + "src/main/node-runtime/pinned-runtime-materializer-node.test.ts": 114, + "src/main/node-runtime/pinned-runtime-materializer.test.ts": 3192, "src/main/ssh/relay-bundle-paths.test.ts": 62, "src/main/ssh/relay-protocol.test.ts": 120, "src/main/ssh/relay-socket-path-limit-shell.integration.test.ts": 107, @@ -5378,7 +5378,7 @@ "src/main/ssh/remote-node-runtime-store-lock-host-release.test.ts": 2390, "src/main/ssh/remote-node-runtime-store-windows.test.ts": 12643, "src/main/ssh/removed-ssh-target-tombstone-retention.test.ts": 54, - "src/main/ssh/runtime-archive-download.test.ts": 99, + "src/main/node-runtime/runtime-archive-download.test.ts": 99, "src/main/ssh/runtime-ssh-access-verification.test.ts": 228, "src/main/ssh/runtime-ssh-access.test.ts": 548, "src/main/ssh/sftp-namespace-resolution.test.ts": 91, diff --git a/config/scripts/node-server-qualification.mjs b/config/scripts/node-server-qualification.mjs index f63b7a9bd6f..f636d78f83c 100644 --- a/config/scripts/node-server-qualification.mjs +++ b/config/scripts/node-server-qualification.mjs @@ -17,7 +17,14 @@ const BUILD_PREFIXES = [ '.github/actions/prepare-orcad-prebuilds/' ] // A remote target's OS does not identify the client platform that builds its commands. -const CROSS_HOST_PREFIXES = ['src/main/ssh/', 'src/main/providers/', 'src/wsl-guest/'] +const CROSS_HOST_PREFIXES = [ + 'src/main/ssh/', + 'src/main/node-runtime/', + 'src/main/orcad/orcad-artifact-materializer.ts', + 'src/main/orcad/orcad-host-unavailable.ts', + 'src/main/providers/', + 'src/wsl-guest/' +] const PLATFORM_PREFIXES = [ 'src/main/persistence/', 'src/main/sqlite/', diff --git a/config/scripts/packaged-orcad-template.cjs b/config/scripts/packaged-orcad-template.cjs index 6a7eecdbc67..3041934a3f9 100644 --- a/config/scripts/packaged-orcad-template.cjs +++ b/config/scripts/packaged-orcad-template.cjs @@ -1,7 +1,7 @@ /** * The orcad deployment template inside desktop builds (design D2): JS plus every target's * addons, never a Node runtime. SSH relays and managed orcad deploys materialize a target's - * slot from it (src/main/ssh/orcad-artifact-materializer.ts, `process.resourcesPath`). + * slot from it (src/main/orcad/orcad-artifact-materializer.ts, `process.resourcesPath`). * * node config/scripts/packaged-orcad-template.cjs --reseal-signed * diff --git a/src/main/ai-vault/opencode-wsl-runtime-preparation.test.ts b/src/main/ai-vault/opencode-wsl-runtime-preparation.test.ts index e2f8781202e..bf0f6753e91 100644 --- a/src/main/ai-vault/opencode-wsl-runtime-preparation.test.ts +++ b/src/main/ai-vault/opencode-wsl-runtime-preparation.test.ts @@ -24,7 +24,7 @@ vi.mock('../wsl/wsl-runner', () => ({ runWslProcess: mocks.run })) vi.mock('../wsl-running-path-filter', () => ({ filterPathsToRunningWslDistrosAsync: mocks.running })) -vi.mock('../ssh/pinned-runtime-materializer', () => ({ +vi.mock('../node-runtime/pinned-runtime-materializer', () => ({ materializeNodeRuntimeArchive: mocks.download })) vi.mock('../ssh/relay-bundle-paths', () => ({ relayBundleCandidates: mocks.bundles })) diff --git a/src/main/ai-vault/opencode-wsl-runtime-preparation.ts b/src/main/ai-vault/opencode-wsl-runtime-preparation.ts index 2316b6e5d5a..2fee3d0732a 100644 --- a/src/main/ai-vault/opencode-wsl-runtime-preparation.ts +++ b/src/main/ai-vault/opencode-wsl-runtime-preparation.ts @@ -8,7 +8,7 @@ import { OPENCODE_SQLITE_READER_FILENAME } from '../../shared/remote-bundle-arti import { parseWslUncPath, toWindowsWslUncPath } from '../../shared/wsl-paths' import { NODE_SQLITE_READER_API_SOURCE } from '../sqlite/node-sqlite-reader-api' import { relayBundleCandidates } from '../ssh/relay-bundle-paths' -import { materializeNodeRuntimeArchive } from '../ssh/pinned-runtime-materializer' +import { materializeNodeRuntimeArchive } from '../node-runtime/pinned-runtime-materializer' import { parseOrcadLinuxLibc } from '../ssh/orcad-deployment-target' import { installNodeRuntimeFromHostArchiveCommand, diff --git a/src/main/ipc/ssh-host-server-on-connect-wiring.test.ts b/src/main/ipc/ssh-host-server-on-connect-wiring.test.ts index 76ab2bab8cf..51d05f5a4e8 100644 --- a/src/main/ipc/ssh-host-server-on-connect-wiring.test.ts +++ b/src/main/ipc/ssh-host-server-on-connect-wiring.test.ts @@ -5,7 +5,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type { SshTarget } from '../../shared/ssh-types' import { closeTestStores, createSqliteTestStore } from '../persistence-test-harness' import { Store } from '../persistence/loading-store/store' -import { OrcadHostUnsupportedError } from '../ssh/orcad-host-unavailable' +import { OrcadHostUnsupportedError } from '../orcad/orcad-host-unavailable' import { allowsDirectSshRelay, SshConnectionStore } from '../ssh/ssh-connection-store' import { resolveHostServerOnConnect } from '../ssh/ssh-host-server-on-connect' @@ -30,7 +30,7 @@ vi.mock('../ssh/orcad-runtime-conversion', () => ({ convertSshTargetToManagedOrcad: mocks.convert })) vi.mock('../ssh/orcad-managed-tunnel', () => ({ ensureOrcadManagedTunnel: mocks.tunnel })) -vi.mock('../ssh/orcad-artifact-materializer', () => ({ hasOrcadTemplate: () => true })) +vi.mock('../orcad/orcad-artifact-materializer', () => ({ hasOrcadTemplate: () => true })) vi.mock('./ssh-renderer-broadcast', () => ({ broadcastSshState: mocks.broadcast })) vi.mock('./ssh-ipc-context', () => ({ getCurrentMainWindow: () => null })) vi.mock('./ssh-session-teardown', () => ({ disconnectRegisteredSshTarget: vi.fn() })) diff --git a/src/main/ipc/ssh-host-server-on-connect-wiring.ts b/src/main/ipc/ssh-host-server-on-connect-wiring.ts index 331e70556c6..3c7e310e8f4 100644 --- a/src/main/ipc/ssh-host-server-on-connect-wiring.ts +++ b/src/main/ipc/ssh-host-server-on-connect-wiring.ts @@ -8,7 +8,7 @@ import { listEnvironments } from '../../shared/runtime-environment-store' import { findOrcadMigrationSourceCutoverForTarget } from '../ssh/orcad-migration-cutover-journal' import { abandonOrcadConversion } from '../ssh/orcad-conversion-abandon' import { retainOrcadMigrationSource } from '../ssh/orcad-migration-source-retention' -import { hasOrcadTemplate } from '../ssh/orcad-artifact-materializer' +import { hasOrcadTemplate } from '../orcad/orcad-artifact-materializer' import { managedServerUpdateDeps } from '../ssh/managed-server-update-deps' import { ensureOrcadManagedTunnel } from '../ssh/orcad-managed-tunnel' import { verifyOrcadManagedServing } from '../ssh/orcad-managed-serving-verify' diff --git a/src/main/ssh/orcad-cache-path.ts b/src/main/node-runtime/orcad-cache-path.ts similarity index 100% rename from src/main/ssh/orcad-cache-path.ts rename to src/main/node-runtime/orcad-cache-path.ts diff --git a/src/main/ssh/pinned-runtime-materializer-node.test.ts b/src/main/node-runtime/pinned-runtime-materializer-node.test.ts similarity index 100% rename from src/main/ssh/pinned-runtime-materializer-node.test.ts rename to src/main/node-runtime/pinned-runtime-materializer-node.test.ts diff --git a/src/main/ssh/pinned-runtime-materializer.test.ts b/src/main/node-runtime/pinned-runtime-materializer.test.ts similarity index 100% rename from src/main/ssh/pinned-runtime-materializer.test.ts rename to src/main/node-runtime/pinned-runtime-materializer.test.ts diff --git a/src/main/ssh/pinned-runtime-materializer.ts b/src/main/node-runtime/pinned-runtime-materializer.ts similarity index 100% rename from src/main/ssh/pinned-runtime-materializer.ts rename to src/main/node-runtime/pinned-runtime-materializer.ts diff --git a/src/main/ssh/runtime-archive-download.test.ts b/src/main/node-runtime/runtime-archive-download.test.ts similarity index 100% rename from src/main/ssh/runtime-archive-download.test.ts rename to src/main/node-runtime/runtime-archive-download.test.ts diff --git a/src/main/ssh/runtime-archive-download.ts b/src/main/node-runtime/runtime-archive-download.ts similarity index 100% rename from src/main/ssh/runtime-archive-download.ts rename to src/main/node-runtime/runtime-archive-download.ts diff --git a/src/main/orcad/build-toolchain-diagnosis.ts b/src/main/orcad/build-toolchain-diagnosis.ts new file mode 100644 index 00000000000..987b5ac9baf --- /dev/null +++ b/src/main/orcad/build-toolchain-diagnosis.ts @@ -0,0 +1,97 @@ +/** + * Diagnose a host's C/C++ build toolchain from a POSIX-sh probe. Pure: it builds a shell + * command and parses its output, so the Node-only orcad bundle runs it over a local shell. + */ +// Why: node-pty@1.1.0 ships no Linux prebuild, so the remote `npm install` falls +// back to `node-gyp rebuild` and needs a C/C++ toolchain. A missing toolchain is +// the dominant first-connect failure on Linux relays (#1693); node-gyp surfaces +// it as an opaque `not found: make`. We probe for the tools so we can replace +// that with an actionable "install build-essential" message. +const PROBED_TOOLS = [ + 'make', + 'gcc', + 'g++', + 'cc', + 'c++', + 'clang', + 'clang++', + 'python3', + 'python' +] as const + +// Package managers mapped to the one-liner that installs a C/C++ toolchain on +// the matching distro family. Ordered by detection priority. +const PACKAGE_MANAGER_HINTS: readonly { bin: string; install: string }[] = [ + { bin: 'apt-get', install: 'sudo apt-get install -y build-essential python3' }, + { bin: 'dnf', install: 'sudo dnf install -y make gcc gcc-c++ python3' }, + { bin: 'yum', install: 'sudo yum install -y make gcc gcc-c++ python3' }, + { bin: 'pacman', install: 'sudo pacman -S --needed base-devel python' }, + { bin: 'apk', install: 'sudo apk add build-base python3' }, + { bin: 'zypper', install: 'sudo zypper install -y gcc gcc-c++ make python3' } +] + +type BuildToolchainStatus = { + present: string[] + packageManager: string | null + // node-gyp needs make, Python, and a C++ compiler. The caller only uses this + // verdict after npm/node-gyp output already points at a native-build failure, + // so custom Python paths do not make unrelated npm failures look toolchainy. + toolchainMissing: boolean +} + +function hasCxxCompiler(present: ReadonlySet): boolean { + return present.has('g++') || present.has('c++') || present.has('clang++') +} + +function hasPython(present: ReadonlySet): boolean { + return present.has('python3') || present.has('python') +} + +// POSIX-sh probe: echo a `HAVE ` line per resolvable build tool and a +// single `PKG ` line for the host's package manager. Runs under +// `/bin/sh -c` (see wrapRemoteCommandForPosixShell), so it stays portable. +export function buildToolchainProbeCommand(): string { + const toolLoop = `for t in ${PROBED_TOOLS.join( + ' ' + )}; do if command -v "$t" >/dev/null 2>&1; then echo "HAVE $t"; fi; done` + const pkgList = PACKAGE_MANAGER_HINTS.map((hint) => hint.bin).join(' ') + const pkgLoop = `for p in ${pkgList}; do if command -v "$p" >/dev/null 2>&1; then echo "PKG $p"; break; fi; done` + return `${toolLoop}; ${pkgLoop}` +} + +export function parseBuildToolchainProbe(output: string): BuildToolchainStatus { + const present = new Set() + let packageManager: string | null = null + for (const line of output.split('\n')) { + const haveMatch = line.trim().match(/^HAVE (\S+)$/) + if (haveMatch) { + present.add(haveMatch[1]) + continue + } + const pkgMatch = line.trim().match(/^PKG (\S+)$/) + if (pkgMatch && !packageManager) { + packageManager = pkgMatch[1] + } + } + return { + present: PROBED_TOOLS.filter((tool) => present.has(tool)), + packageManager, + toolchainMissing: !present.has('make') || !hasCxxCompiler(present) || !hasPython(present) + } +} + +/** Install hint for the host's package manager, or the cross-distro list when it is unknown. */ +export function toolchainInstallHintLines(status: BuildToolchainStatus): string[] { + const tailored = status.packageManager + ? PACKAGE_MANAGER_HINTS.find((hint) => hint.bin === status.packageManager)?.install + : null + if (tailored) { + return [` ${tailored}`] + } + return [ + ' Debian/Ubuntu: sudo apt-get install -y build-essential python3', + ' Fedora/RHEL: sudo dnf install -y make gcc gcc-c++ python3', + ' Arch: sudo pacman -S --needed base-devel python', + ' Alpine: sudo apk add build-base python3' + ] +} diff --git a/src/main/orcad/node-pty-precondition.ts b/src/main/orcad/node-pty-precondition.ts index 9c62bcf0c93..85c1e4b7886 100644 --- a/src/main/orcad/node-pty-precondition.ts +++ b/src/main/orcad/node-pty-precondition.ts @@ -25,7 +25,7 @@ import { buildToolchainProbeCommand, parseBuildToolchainProbe, toolchainInstallHintLines -} from '../ssh/build-toolchain-diagnosis' +} from './build-toolchain-diagnosis' import { detectNativeHostAbi, nativeSlotName, type NativeHostAbi } from './native-host-abi' import { classifyNodePtyLoaderMessage, firstErrorLine } from './node-pty-loader-diagnosis' import { installPrebuiltSlot, type PrebuiltSlotOutcome } from './node-pty-prebuilt-slot' diff --git a/src/main/orcad/orcad-artifact-cache-retention.ts b/src/main/orcad/orcad-artifact-cache-retention.ts index e09a052b2d2..500e33411eb 100644 --- a/src/main/orcad/orcad-artifact-cache-retention.ts +++ b/src/main/orcad/orcad-artifact-cache-retention.ts @@ -10,7 +10,7 @@ import { readdir, rm, stat } from 'node:fs/promises' import { join } from 'node:path' import { nodeRuntimeAsset } from '../../shared/node-runtime-pin' import { ORCAD_LOCK_FILE_NAME, readOrcadInstanceLockRecord } from './orcad-instance-lock' -import { materializedOrcadArtifactVersions } from '../ssh/orcad-artifact-materializer' +import { materializedOrcadArtifactVersions } from './orcad-artifact-materializer' import { isProcessAlive } from '../daemon/daemon-process-inspection' import { liveDaemonOrcadSlots } from './orcad-daemon-slot-pins' diff --git a/src/main/ssh/orcad-artifact-materializer.test.ts b/src/main/orcad/orcad-artifact-materializer.test.ts similarity index 98% rename from src/main/ssh/orcad-artifact-materializer.test.ts rename to src/main/orcad/orcad-artifact-materializer.test.ts index 301d023b668..cfe08e43b55 100644 --- a/src/main/ssh/orcad-artifact-materializer.test.ts +++ b/src/main/orcad/orcad-artifact-materializer.test.ts @@ -13,7 +13,7 @@ import { import { tmpdir } from 'node:os' import { basename, dirname, join } from 'node:path' import { afterEach, describe, expect, it, vi } from 'vitest' -import { pruneOrcadArtifactCache } from '../orcad/orcad-artifact-cache-retention' +import { pruneOrcadArtifactCache } from './orcad-artifact-cache-retention' import { z } from 'zod' import { NODE_RUNTIME_ASSETS, @@ -33,7 +33,7 @@ import { orcadTemplateCommonFilenames, orcadTemplateTargetFilenames } from '../../shared/orcad-artifacts' -import { readOrcadArtifactIdentity } from '../orcad/orcad-artifact-identity' +import { readOrcadArtifactIdentity } from './orcad-artifact-identity' import { getAppEnvironment, hasAppEnvironment, diff --git a/src/main/ssh/orcad-artifact-materializer.ts b/src/main/orcad/orcad-artifact-materializer.ts similarity index 98% rename from src/main/ssh/orcad-artifact-materializer.ts rename to src/main/orcad/orcad-artifact-materializer.ts index f9b95c38c2b..23a6a866fc0 100644 --- a/src/main/ssh/orcad-artifact-materializer.ts +++ b/src/main/orcad/orcad-artifact-materializer.ts @@ -17,12 +17,12 @@ import { orcadTemplateTargetFilenames } from '../../shared/orcad-artifacts' import { pinnedNodeRuntimeAsset, type NodeRuntimeTarget } from '../../shared/node-runtime-pin' -import { findOrcadCachePath } from './orcad-cache-path' +import { findOrcadCachePath } from '../node-runtime/orcad-cache-path' import { fileSha256, verifyFileSha256, type PinnedRuntimeMaterializeOptions -} from './pinned-runtime-materializer' +} from '../node-runtime/pinned-runtime-materializer' import { OrcadArtifactsUnavailableError, OrcadHostUnsupportedError } from './orcad-host-unavailable' const Sha256Schema = z.string().regex(/^[a-f0-9]{64}$/u) diff --git a/src/main/ssh/orcad-host-unavailable.test.ts b/src/main/orcad/orcad-host-unavailable.test.ts similarity index 88% rename from src/main/ssh/orcad-host-unavailable.test.ts rename to src/main/orcad/orcad-host-unavailable.test.ts index 752019ff014..864b37832a3 100644 --- a/src/main/ssh/orcad-host-unavailable.test.ts +++ b/src/main/orcad/orcad-host-unavailable.test.ts @@ -3,8 +3,8 @@ import { classifyOrcadHostUnavailable, orcadCandidateLaunchFailureCode } from './orcad-host-unavailable' -import { OrcadWindowsLaunchRefusedError } from './orcad-remote-launch-windows' -import { OrcadWindowsCommandLineError } from './orcad-remote-windows-node' +import { OrcadWindowsLaunchRefusedError } from '../ssh/orcad-remote-launch-windows' +import { OrcadWindowsCommandLineError } from '../ssh/orcad-remote-windows-node' describe('classifyOrcadHostUnavailable', () => { it('sends a Windows host that cannot launch orcad back to the relay', () => { diff --git a/src/main/ssh/orcad-host-unavailable.ts b/src/main/orcad/orcad-host-unavailable.ts similarity index 100% rename from src/main/ssh/orcad-host-unavailable.ts rename to src/main/orcad/orcad-host-unavailable.ts diff --git a/src/main/orcad/orcad-local-serve-selection.ts b/src/main/orcad/orcad-local-serve-selection.ts index 79b81c46bac..8b18969f275 100644 --- a/src/main/orcad/orcad-local-serve-selection.ts +++ b/src/main/orcad/orcad-local-serve-selection.ts @@ -29,8 +29,8 @@ import { } from '../../shared/orcad-local-serve-selection' import { resolveBundledOrcadRuntime } from './orcad-bundled-runtime' import { detectNativeHostAbi, nativeSlotName } from './native-host-abi' -import { materializeOrcadArtifact } from '../ssh/orcad-artifact-materializer' -import { materializeCachedNodeRuntime } from '../ssh/pinned-runtime-materializer' +import { materializeOrcadArtifact } from './orcad-artifact-materializer' +import { materializeCachedNodeRuntime } from '../node-runtime/pinned-runtime-materializer' import { serveProfileHasSshTargets } from './serve-profile-ssh-targets' import { errorMessage } from '../../shared/error-message' diff --git a/src/main/orcad/orcad-packaged-structured-chat.integration.test.ts b/src/main/orcad/orcad-packaged-structured-chat.integration.test.ts index 6c24a299f41..f5523e89c87 100644 --- a/src/main/orcad/orcad-packaged-structured-chat.integration.test.ts +++ b/src/main/orcad/orcad-packaged-structured-chat.integration.test.ts @@ -36,7 +36,7 @@ import { } from '../../shared/remote-runtime-client' import { claudeSessionIdForOrcaSession } from '../claude/claude-structured-session-id' import { shellEscape } from '../ssh/ssh-connection-utils' -import { materializeOrcadArtifact } from '../ssh/orcad-artifact-materializer' +import { materializeOrcadArtifact } from './orcad-artifact-materializer' import { hostServerTarget, installPackagedOrcadSlotForTests, diff --git a/src/main/ssh/build-toolchain-diagnosis.ts b/src/main/ssh/build-toolchain-diagnosis.ts deleted file mode 100644 index 77d20ce475a..00000000000 --- a/src/main/ssh/build-toolchain-diagnosis.ts +++ /dev/null @@ -1,228 +0,0 @@ -/** - * Diagnose a host's C/C++ build toolchain from a POSIX-sh probe. - * - * Why this is separate from `ssh-relay-build-toolchain.ts`: everything here is pure — - * it builds a shell command, parses its output, and formats messages. The relay reaches - * it over SSH; `orcad` reaches it over a local shell. Keeping it transport-free is what - * lets the Node-only bundle reuse it without pulling `ssh2` in behind it. - */ -// Why: node-pty@1.1.0 ships no Linux prebuild, so the remote `npm install` falls -// back to `node-gyp rebuild` and needs a C/C++ toolchain. A missing toolchain is -// the dominant first-connect failure on Linux relays (#1693); node-gyp surfaces -// it as an opaque `not found: make`. We probe for the tools so we can replace -// that with an actionable "install build-essential" message. -const PROBED_TOOLS = [ - 'make', - 'gcc', - 'g++', - 'cc', - 'c++', - 'clang', - 'clang++', - 'python3', - 'python' -] as const - -// Package managers mapped to the one-liner that installs a C/C++ toolchain on -// the matching distro family. Ordered by detection priority. -const PACKAGE_MANAGER_HINTS: readonly { bin: string; install: string }[] = [ - { bin: 'apt-get', install: 'sudo apt-get install -y build-essential python3' }, - { bin: 'dnf', install: 'sudo dnf install -y make gcc gcc-c++ python3' }, - { bin: 'yum', install: 'sudo yum install -y make gcc gcc-c++ python3' }, - { bin: 'pacman', install: 'sudo pacman -S --needed base-devel python' }, - { bin: 'apk', install: 'sudo apk add build-base python3' }, - { bin: 'zypper', install: 'sudo zypper install -y gcc gcc-c++ make python3' } -] - -export type BuildToolchainStatus = { - present: string[] - packageManager: string | null - // node-gyp needs make, Python, and a C++ compiler. The caller only uses this - // verdict after npm/node-gyp output already points at a native-build failure, - // so custom Python paths do not make unrelated npm failures look toolchainy. - toolchainMissing: boolean -} - -function hasCxxCompiler(present: ReadonlySet): boolean { - return present.has('g++') || present.has('c++') || present.has('clang++') -} - -function hasPython(present: ReadonlySet): boolean { - return present.has('python3') || present.has('python') -} - -// POSIX-sh probe: echo a `HAVE ` line per resolvable build tool and a -// single `PKG ` line for the host's package manager. Runs under -// `/bin/sh -c` (see wrapRemoteCommandForPosixShell), so it stays portable. -export function buildToolchainProbeCommand(): string { - const toolLoop = `for t in ${PROBED_TOOLS.join( - ' ' - )}; do if command -v "$t" >/dev/null 2>&1; then echo "HAVE $t"; fi; done` - const pkgList = PACKAGE_MANAGER_HINTS.map((hint) => hint.bin).join(' ') - const pkgLoop = `for p in ${pkgList}; do if command -v "$p" >/dev/null 2>&1; then echo "PKG $p"; break; fi; done` - return `${toolLoop}; ${pkgLoop}` -} - -export function parseBuildToolchainProbe(output: string): BuildToolchainStatus { - const present = new Set() - let packageManager: string | null = null - for (const line of output.split('\n')) { - const haveMatch = line.trim().match(/^HAVE (\S+)$/) - if (haveMatch) { - present.add(haveMatch[1]) - continue - } - const pkgMatch = line.trim().match(/^PKG (\S+)$/) - if (pkgMatch && !packageManager) { - packageManager = pkgMatch[1] - } - } - return { - present: PROBED_TOOLS.filter((tool) => present.has(tool)), - packageManager, - toolchainMissing: !present.has('make') || !hasCxxCompiler(present) || !hasPython(present) - } -} - -export function shouldProbeBuildToolchainAfterNativeDepsFailure(message: string): boolean { - const lower = message.toLowerCase() - if (!lower.includes('gyp') && !lower.includes('node-gyp')) { - return false - } - return ( - /\bnot found:\s*(make|gmake|gcc|g\+\+|cc|c\+\+|clang|clang\+\+|python|python3)\b/i.test( - message - ) || - /\b(make|gmake|gcc|g\+\+|cc|c\+\+|clang|clang\+\+|python|python3)\b.*\bnot found\b/i.test( - message - ) || - lower.includes('could not find any python installation') || - lower.includes('no xcode or clt version detected') - ) -} - -function missingToolNames(status: BuildToolchainStatus): string[] { - const present = new Set(status.present) - const missing: string[] = [] - if (!present.has('make')) { - missing.push('make') - } - if (!hasCxxCompiler(present)) { - missing.push('a C++ compiler (g++ or clang++)') - } - if (!hasPython(present)) { - missing.push('python3') - } - return missing -} - -/** Install hint for the host's package manager, or the cross-distro list when it is unknown. */ -export function toolchainInstallHintLines(status: BuildToolchainStatus): string[] { - const tailored = status.packageManager - ? PACKAGE_MANAGER_HINTS.find((hint) => hint.bin === status.packageManager)?.install - : null - if (tailored) { - return [` ${tailored}`] - } - return [ - ' Debian/Ubuntu: sudo apt-get install -y build-essential python3', - ' Fedora/RHEL: sudo dnf install -y make gcc gcc-c++ python3', - ' Arch: sudo pacman -S --needed base-devel python', - ' Alpine: sudo apk add build-base python3' - ] -} - -/** One-line summary for the deploy log when node-pty is skipped rather than compiled. */ -export function formatSkippedNodePtyWarning(status: BuildToolchainStatus): string { - // Why: with no package manager detected the hint list is the cross-distro menu, whose first line - // is Debian's — quoting it alone would name the wrong distro, so stay neutral instead. - const hintLines = toolchainInstallHintLines(status) - const hint = - hintLines.length === 1 - ? hintLines[0].trim() - : 'install a C/C++ toolchain (make, a C++ compiler, python3)' - return ( - `missing build tools (${missingToolNames(status).join(', ')}); skipping node-pty so the ` + - `connection still serves files and git. Remote terminals need: ${hint}` - ) -} - -export function formatMissingToolchainError( - status: BuildToolchainStatus, - underlyingError: string -): string { - const lines = [ - `The remote host is missing the C/C++ build tools (${missingToolNames(status).join(', ')}) ` + - `needed to compile Orca's relay native modules (node-pty, @parcel/watcher). node-pty has no ` + - `prebuilt binary for Linux, so they must be compiled on the remote host.`, - '', - 'Install the build tools on the remote host, then reconnect:', - ...toolchainInstallHintLines(status), - '', - `Underlying install error: ${underlyingError}` - ] - return lines.join('\n') -} - -const NODE_HEADERS_TARBALL_RE = /node-v[0-9.]+-headers\.tar\.gz/i - -/** - * Whether a native-deps failure is node-gyp failing to download Node headers from nodejs.org. - * - * Why it needs naming: the raw output is forty lines of `gyp http` and stack frames around one - * `ECONNREFUSED`, and it reads as a broken host or a broken Orca. Which of two things it is - * depends on what the local-headers export found first, so the formatter takes that answer. - */ -export function isNodeHeadersDownloadFailure(message: string): boolean { - // Why `configure error` is required: node-gyp's fetch client logs `attempt N failed with ` - // on retries it then recovers from, so a network token alone also matches a build that got its - // headers and died later for an unrelated reason. Only the configure step downloads headers. - return ( - /gyp ERR! configure error/i.test(message) && - NODE_HEADERS_TARBALL_RE.test(message) && - /\b(ECONNREFUSED|ENOTFOUND|ETIMEDOUT|EHOSTUNREACH|ENETUNREACH|EAI_AGAIN|ECONNRESET)\b/.test( - message - ) - ) -} - -const NODE_HEADERS_CONTEXT = - 'node-pty has no prebuilt binary for Linux, so it must be compiled on the remote host, and ' + - 'node-gyp fetches the Node.js headers from nodejs.org unless the Node install provides them ' + - 'at /include/node.' - -/** - * @param localHeadersDir what the local-headers export found: a dir it exported, `null` when - * the host's Node ships no matching headers, `undefined` when the answer never came back. - * - * Why the exported-dir case is its own message: the export is the fix, so node-gyp downloading - * anyway means its `nodedir` env keys were not honoured (a future npm dropping the passthrough, - * a wrapper scrubbing the env). That is an Orca defect, not a host problem, and must not be - * reported as one -- it names the dir so the report is checkable. - */ -export function formatNodeHeadersDownloadError( - underlyingError: string, - localHeadersDir: string | null | undefined -): string { - const lines = localHeadersDir - ? [ - `The remote host could not download the Node.js headers needed to compile node-pty, even ` + - `though its Node install ships matching headers at ${localHeadersDir}/include/node and ` + - `Orca pointed node-gyp at them. node-gyp ignored that setting; this is an Orca defect, ` + - `please report it with the log below.`, - '', - 'Workaround on the remote host until then: allow outbound HTTPS to nodejs.org, or point ' + - 'npm at a mirror: npm config set disturl https:///dist' - ] - : [ - 'The remote host could not download the Node.js headers needed to compile node-pty, and ' + - `its Node install has no local headers matching its own version. ${NODE_HEADERS_CONTEXT}`, - '', - 'Fix one of the following on the remote host, then reconnect:', - ' - Install Node.js from an official build or a version manager (nvm, fnm, volta, n), ' + - 'which ship headers for exactly the Node they run; or', - ' - Allow outbound HTTPS to nodejs.org, or point npm at a mirror: ' + - 'npm config set disturl https:///dist' - ] - return [...lines, '', `Underlying install error: ${underlyingError}`].join('\n') -} diff --git a/src/main/ssh/orcad-activation-gate.test.ts b/src/main/ssh/orcad-activation-gate.test.ts index 43ca270448a..fcd53b3a4e3 100644 --- a/src/main/ssh/orcad-activation-gate.test.ts +++ b/src/main/ssh/orcad-activation-gate.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest' import { evaluateOrcadActivation } from './orcad-activation-gate' -import { classifyOrcadHostUnavailable } from './orcad-host-unavailable' +import { classifyOrcadHostUnavailable } from '../orcad/orcad-host-unavailable' import type { ServeReadiness } from '../server/serve-readiness' import type { OrcadHealth, TerminalDaemonHealth } from '../orcad/orcad-health' diff --git a/src/main/ssh/orcad-installed-activation.ts b/src/main/ssh/orcad-installed-activation.ts index 4d105bebd4f..bb3d608cbd2 100644 --- a/src/main/ssh/orcad-installed-activation.ts +++ b/src/main/ssh/orcad-installed-activation.ts @@ -57,7 +57,7 @@ import { stopTransactionIncumbent } from './orcad-transaction-incumbent' import { withOrcadLogTail } from './orcad-remote-log-tail' -import { orcadCandidateLaunchFailureCode } from './orcad-host-unavailable' +import { orcadCandidateLaunchFailureCode } from '../orcad/orcad-host-unavailable' import { errorMessage } from '../../shared/error-message' type Outcome = Extract diff --git a/src/main/ssh/orcad-managed-auto-update.test.ts b/src/main/ssh/orcad-managed-auto-update.test.ts index 71c6f4719a0..56951474ce3 100644 --- a/src/main/ssh/orcad-managed-auto-update.test.ts +++ b/src/main/ssh/orcad-managed-auto-update.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import { emptyOrcadActivationRecord, type OrcadActivationRecord } from './orcad-activation-record' -import { OrcadHostUnsupportedError } from './orcad-host-unavailable' +import { OrcadHostUnsupportedError } from '../orcad/orcad-host-unavailable' const mocks = vi.hoisted(() => { const state: { record: OrcadActivationRecord | null } = { record: null } @@ -29,7 +29,9 @@ vi.mock('./orcad-managed-runtime-context', () => ({ vi.mock('./orcad-managed-migration-status', () => ({ findIncompleteManagedOrcadMigration: mocks.migrating })) -vi.mock('./orcad-artifact-materializer', () => ({ materializeOrcadArtifact: mocks.materialize })) +vi.mock('../orcad/orcad-artifact-materializer', () => ({ + materializeOrcadArtifact: mocks.materialize +})) vi.mock('./remote-versioned-install', () => ({ readLocalFullVersion: () => '0.1.0+new' })) import { diff --git a/src/main/ssh/orcad-managed-auto-update.ts b/src/main/ssh/orcad-managed-auto-update.ts index f9d9cd154de..9afd08e7c99 100644 --- a/src/main/ssh/orcad-managed-auto-update.ts +++ b/src/main/ssh/orcad-managed-auto-update.ts @@ -10,8 +10,11 @@ import { refuseOrcadHostDowngrade, type OrcadHostVersionRefusal } from './orcad-host-version-admission' -import { materializeOrcadArtifact } from './orcad-artifact-materializer' -import { OrcadArtifactsUnavailableError, OrcadHostUnsupportedError } from './orcad-host-unavailable' +import { materializeOrcadArtifact } from '../orcad/orcad-artifact-materializer' +import { + OrcadArtifactsUnavailableError, + OrcadHostUnsupportedError +} from '../orcad/orcad-host-unavailable' import { findIncompleteManagedOrcadMigration } from './orcad-managed-migration-status' import { ORCAD_ACTIVATION_FENCE_BUSY_CODE } from './orcad-activation-fence-hold' import { resolveLinkedOrcadContext } from './orcad-managed-runtime-context' diff --git a/src/main/ssh/orcad-remote-context.ts b/src/main/ssh/orcad-remote-context.ts index 002f540a8bd..503888c611d 100644 --- a/src/main/ssh/orcad-remote-context.ts +++ b/src/main/ssh/orcad-remote-context.ts @@ -16,7 +16,7 @@ import { type RemoteHostPlatform } from './ssh-remote-platform' import { detectRemoteHostPlatform } from './ssh-remote-platform-detection' -import { OrcadHostUnsupportedError } from './orcad-host-unavailable' +import { OrcadHostUnsupportedError } from '../orcad/orcad-host-unavailable' import { rememberSshHostPlatform } from './ssh-host-platform-memo' export type OrcadRemoteContext = { diff --git a/src/main/ssh/orcad-remote-deploy.ts b/src/main/ssh/orcad-remote-deploy.ts index 4944e9444fa..4293effd36c 100644 --- a/src/main/ssh/orcad-remote-deploy.ts +++ b/src/main/ssh/orcad-remote-deploy.ts @@ -20,10 +20,10 @@ import { installOrcadBundle } from './orcad-remote-install' import { getAppEnvironment } from '../../shared/app-environment' import type { NodeRuntimeTarget } from '../../shared/node-runtime-pin' import { ORCAD_STARTUP_READINESS_TIMEOUT_MS } from '../../shared/orcad-profile-preflight' -import { materializeOrcadArtifact } from './orcad-artifact-materializer' +import { materializeOrcadArtifact } from '../orcad/orcad-artifact-materializer' import { readOrcadBundleTarget } from './orcad-deployment-target' import { resolveOrcadRuntimeTarget } from './orcad-runtime-target' -import { materializeNodeRuntimeArchive } from './pinned-runtime-materializer' +import { materializeNodeRuntimeArchive } from '../node-runtime/pinned-runtime-materializer' import { orcadActivationFenceExists, resolveOrcadActivationReadinessTimeout, diff --git a/src/main/ssh/orcad-remote-launch-windows.ts b/src/main/ssh/orcad-remote-launch-windows.ts index acb65862894..0f8c89df637 100644 --- a/src/main/ssh/orcad-remote-launch-windows.ts +++ b/src/main/ssh/orcad-remote-launch-windows.ts @@ -37,7 +37,7 @@ import { ORCAD_WINDOWS_PROCESS_FILENAME } from './orcad-remote-host-support' import { orcadManagedLaunchEnv, type OrcadLaunchSpec } from './orcad-remote-launch' -import { ORCAD_WINDOWS_LAUNCH_REFUSED_CODE } from './orcad-host-unavailable' +import { ORCAD_WINDOWS_LAUNCH_REFUSED_CODE } from '../orcad/orcad-host-unavailable' export class OrcadWindowsLaunchRefusedError extends Error { readonly code = ORCAD_WINDOWS_LAUNCH_REFUSED_CODE diff --git a/src/main/ssh/orcad-remote-windows-node.ts b/src/main/ssh/orcad-remote-windows-node.ts index af9f097de26..4ca1737e226 100644 --- a/src/main/ssh/orcad-remote-windows-node.ts +++ b/src/main/ssh/orcad-remote-windows-node.ts @@ -23,7 +23,7 @@ import { execCommand, isUnconfirmedSshCommandTermination } from './ssh-relay-exe import { RELAY_REMOTE_DIR } from './relay-protocol' import { joinRemotePath, remoteDirname, type RemoteHostPlatform } from './ssh-remote-platform' import { powerShellLiteral } from './ssh-remote-powershell' -import { ORCAD_WINDOWS_COMMAND_LINE_UNSAFE_CODE } from './orcad-host-unavailable' +import { ORCAD_WINDOWS_COMMAND_LINE_UNSAFE_CODE } from '../orcad/orcad-host-unavailable' import { ORCAD_WINDOWS_HOST_SCRIPT, ORCAD_WINDOWS_HOST_SCRIPT_FILENAME, diff --git a/src/main/ssh/orcad-runtime-deployment.test.ts b/src/main/ssh/orcad-runtime-deployment.test.ts index f143ab80363..6d694eabfe5 100644 --- a/src/main/ssh/orcad-runtime-deployment.test.ts +++ b/src/main/ssh/orcad-runtime-deployment.test.ts @@ -49,7 +49,7 @@ vi.mock('./orcad-activation-transaction-store', () => ({ readOrcadActivationTransaction: mocks.readTransaction })) vi.mock('./orcad-remote-deploy', () => ({ deployOrcad: mocks.deploy })) -vi.mock('./orcad-artifact-materializer', () => ({ +vi.mock('../orcad/orcad-artifact-materializer', () => ({ materializeOrcadArtifact: async () => '/local/orcad' })) vi.mock('./orcad-local-build-hash', () => ({ computeLocalOrcadBuildHash: () => 'build-hash' })) diff --git a/src/main/ssh/orcad-runtime-deployment.ts b/src/main/ssh/orcad-runtime-deployment.ts index 6c86c19c6d7..3f0e78621d0 100644 --- a/src/main/ssh/orcad-runtime-deployment.ts +++ b/src/main/ssh/orcad-runtime-deployment.ts @@ -15,7 +15,7 @@ import { } from '../../shared/orcad-managed-runtime' import { getManagedOrcadFenceEnvironmentId } from '../../shared/managed-orcad-ssh-owner' import { runTargetLifecycle } from '../ipc/ssh-target-lifecycle-queue' -import { materializeOrcadArtifact } from './orcad-artifact-materializer' +import { materializeOrcadArtifact } from '../orcad/orcad-artifact-materializer' import { closeOrcadManagedTunnel, ensureOrcadManagedTunnel, diff --git a/src/main/ssh/orcad-runtime-maintenance.test.ts b/src/main/ssh/orcad-runtime-maintenance.test.ts index 33506c397ca..b1eaa02f54d 100644 --- a/src/main/ssh/orcad-runtime-maintenance.test.ts +++ b/src/main/ssh/orcad-runtime-maintenance.test.ts @@ -38,7 +38,7 @@ vi.mock('./orcad-remote-rollback', () => ({ rollbackOrcad: mocks.rollback })) vi.mock('./orcad-activation-recovery', () => ({ recoverInterruptedOrcadActivation: mocks.recover })) vi.mock('./orcad-active-readiness', () => ({ probeActiveOrcadReadiness: mocks.probe })) vi.mock('./orcad-remote-build-hash', () => ({ readRemoteOrcadBuildHash: mocks.buildHash })) -vi.mock('./orcad-artifact-materializer', () => ({ +vi.mock('../orcad/orcad-artifact-materializer', () => ({ materializeOrcadArtifact: async () => '/local/orcad' })) vi.mock('./orcad-local-build-hash', () => ({ computeLocalOrcadBuildHash: () => 'local-hash' })) diff --git a/src/main/ssh/orcad-runtime-maintenance.ts b/src/main/ssh/orcad-runtime-maintenance.ts index b9970f16c38..7e959299024 100644 --- a/src/main/ssh/orcad-runtime-maintenance.ts +++ b/src/main/ssh/orcad-runtime-maintenance.ts @@ -19,7 +19,7 @@ import { runTargetLifecycle } from '../ipc/ssh-target-lifecycle-queue' import type { ServeReadiness } from '../server/serve-readiness' import { recoverInterruptedOrcadActivation } from './orcad-activation-recovery' import { probeActiveOrcadReadiness } from './orcad-active-readiness' -import { materializeOrcadArtifact } from './orcad-artifact-materializer' +import { materializeOrcadArtifact } from '../orcad/orcad-artifact-materializer' import { CURRENT_ORCAD_DAEMON_PROTOCOL } from './orcad-daemon-protocol-crossing' import { ensureOrcadManagedTunnel } from './orcad-managed-tunnel' import { diff --git a/src/main/ssh/orcad-runtime-target.ts b/src/main/ssh/orcad-runtime-target.ts index a342142c48c..4c29f94c06d 100644 --- a/src/main/ssh/orcad-runtime-target.ts +++ b/src/main/ssh/orcad-runtime-target.ts @@ -1,6 +1,6 @@ import type { NodeRuntimeTarget } from '../../shared/node-runtime-pin' import { resolveOrcadDeploymentTargetFacts } from './orcad-deployment-target' -import { OrcadHostUnsupportedError } from './orcad-host-unavailable' +import { OrcadHostUnsupportedError } from '../orcad/orcad-host-unavailable' import type { OrcadHostTransport } from './orcad-host-transport' import { pinnedRuntimeTargetForHost } from './pinned-runtime-host-target' import type { RemoteHostPlatform } from './ssh-remote-platform' diff --git a/src/main/ssh/orcad-stdio-bridge-provider.test.ts b/src/main/ssh/orcad-stdio-bridge-provider.test.ts index 66936ddff65..8bb84f44855 100644 --- a/src/main/ssh/orcad-stdio-bridge-provider.test.ts +++ b/src/main/ssh/orcad-stdio-bridge-provider.test.ts @@ -4,7 +4,7 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { PassThrough } from 'node:stream' import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest' -import { OrcadStdioBridgeUnavailableError } from './orcad-host-unavailable' +import { OrcadStdioBridgeUnavailableError } from '../orcad/orcad-host-unavailable' import type { OrcadStdioBridge } from './orcad-stdio-bridge' import { ORCAD_STDIO_BRIDGE_MAX_CHANNELS } from './orcad-stdio-bridge-listener' import { OrcadStdioBridgePortForwardProvider } from './orcad-stdio-bridge-provider' diff --git a/src/main/ssh/orcad-stdio-bridge-provider.ts b/src/main/ssh/orcad-stdio-bridge-provider.ts index 76632cd14a1..7bef1cee123 100644 --- a/src/main/ssh/orcad-stdio-bridge-provider.ts +++ b/src/main/ssh/orcad-stdio-bridge-provider.ts @@ -6,7 +6,7 @@ * Disconnects match the forward: a lost channel drops its socket and the listener stays, so the * tunnel's own reconnect logic decides, and a lost transport never reads as a dead server. */ -import { OrcadStdioBridgeUnavailableError } from './orcad-host-unavailable' +import { OrcadStdioBridgeUnavailableError } from '../orcad/orcad-host-unavailable' import type { OrcadHostTransport } from './orcad-host-transport' import { checkOrcadStdioBridge, diff --git a/src/main/ssh/orcad-stdio-bridge.test.ts b/src/main/ssh/orcad-stdio-bridge.test.ts index 603a3c1730a..3518ebc13f0 100644 --- a/src/main/ssh/orcad-stdio-bridge.test.ts +++ b/src/main/ssh/orcad-stdio-bridge.test.ts @@ -5,7 +5,7 @@ import type { Server } from 'node:net' import type { EventEmitter } from 'node:events' import { PassThrough } from 'node:stream' import { afterAll, beforeAll, describe, expect, it } from 'vitest' -import { OrcadStdioBridgeUnavailableError } from './orcad-host-unavailable' +import { OrcadStdioBridgeUnavailableError } from '../orcad/orcad-host-unavailable' import { checkOrcadStdioBridge, type OrcadStdioBridge } from './orcad-stdio-bridge' import { orcadPosixStdioBridgeCommand, diff --git a/src/main/ssh/orcad-stdio-bridge.ts b/src/main/ssh/orcad-stdio-bridge.ts index 01814e28893..7be1463ffac 100644 --- a/src/main/ssh/orcad-stdio-bridge.ts +++ b/src/main/ssh/orcad-stdio-bridge.ts @@ -2,7 +2,7 @@ import { OrcadHostUnsupportedError, OrcadStdioBridgeUnavailableError -} from './orcad-host-unavailable' +} from '../orcad/orcad-host-unavailable' import { resolveOrcadRemoteContext } from './orcad-remote-context' import { orcadWindowsHomeOpCommand } from './orcad-remote-windows-node' import { diff --git a/src/main/ssh/orcad-windows-host-preparation.ts b/src/main/ssh/orcad-windows-host-preparation.ts index 36c67f6d0fb..0e1d5c92e14 100644 --- a/src/main/ssh/orcad-windows-host-preparation.ts +++ b/src/main/ssh/orcad-windows-host-preparation.ts @@ -10,7 +10,7 @@ import { getAppEnvironment } from '../../shared/app-environment' import type { NodeRuntimeTarget } from '../../shared/node-runtime-pin' import { ensureRemoteOrcadNodeRuntime } from './orcad-remote-node-runtime' import { installOrcadWindowsHostScript, orcadRemoteBaseDir } from './orcad-remote-windows-node' -import { materializeNodeRuntimeArchive } from './pinned-runtime-materializer' +import { materializeNodeRuntimeArchive } from '../node-runtime/pinned-runtime-materializer' import type { OrcadHostTransport } from './orcad-host-transport' import { joinRemotePath, type RemoteHostPlatform } from './ssh-remote-platform' diff --git a/src/main/ssh/ssh-host-server-connect-events.ts b/src/main/ssh/ssh-host-server-connect-events.ts index ca0032d7b74..6245fe5cc9f 100644 --- a/src/main/ssh/ssh-host-server-connect-events.ts +++ b/src/main/ssh/ssh-host-server-connect-events.ts @@ -8,7 +8,7 @@ import type { } from '../../shared/orcad-managed-runtime' import type { SshTarget } from '../../shared/ssh-types' import type { HostServerOnConnectResult } from './ssh-host-server-on-connect' -import { classifyOrcadHostUnavailable } from './orcad-host-unavailable' +import { classifyOrcadHostUnavailable } from '../orcad/orcad-host-unavailable' export type HostServerConnectEvent = | { diff --git a/src/main/ssh/ssh-host-server-on-connect-telemetry.test.ts b/src/main/ssh/ssh-host-server-on-connect-telemetry.test.ts index ef870210dae..e88f755ff37 100644 --- a/src/main/ssh/ssh-host-server-on-connect-telemetry.test.ts +++ b/src/main/ssh/ssh-host-server-on-connect-telemetry.test.ts @@ -3,7 +3,7 @@ import type { SshTarget } from '../../shared/ssh-types' import { OrcadHostUnsupportedError, OrcadStdioBridgeUnavailableError -} from './orcad-host-unavailable' +} from '../orcad/orcad-host-unavailable' import type { ManagedOrcadAutoUpdateOutcome } from './orcad-managed-auto-update' import type { HostServerConnectEvent } from './ssh-host-server-connect-events' import { diff --git a/src/main/ssh/ssh-host-server-on-connect.test.ts b/src/main/ssh/ssh-host-server-on-connect.test.ts index dbce90ed896..1c0a9618017 100644 --- a/src/main/ssh/ssh-host-server-on-connect.test.ts +++ b/src/main/ssh/ssh-host-server-on-connect.test.ts @@ -3,7 +3,7 @@ import type { SshTarget } from '../../shared/ssh-types' import { OrcadHostUnsupportedError, OrcadStdioBridgeUnavailableError -} from './orcad-host-unavailable' +} from '../orcad/orcad-host-unavailable' import { resolveHostServerOnConnect, type HostServerOnConnectDeps diff --git a/src/main/ssh/ssh-host-server-on-connect.ts b/src/main/ssh/ssh-host-server-on-connect.ts index f32e502318c..cbf9d66bf9d 100644 --- a/src/main/ssh/ssh-host-server-on-connect.ts +++ b/src/main/ssh/ssh-host-server-on-connect.ts @@ -28,7 +28,7 @@ import { managedResultAfterUpdate, unservedManagedResult } from './managed-serve import { classifyOrcadHostUnavailable, ORCAD_TUNNEL_UNAVAILABLE_REASON -} from './orcad-host-unavailable' +} from '../orcad/orcad-host-unavailable' import { decidedEvent, reportHostServerConversion, diff --git a/src/main/ssh/ssh-hostile-host-cells.ts b/src/main/ssh/ssh-hostile-host-cells.ts index 7efc9f1a2e4..58c1e54c1fd 100644 --- a/src/main/ssh/ssh-hostile-host-cells.ts +++ b/src/main/ssh/ssh-hostile-host-cells.ts @@ -4,7 +4,7 @@ * client-side deploy against each one; `.github/workflows/ssh-hostile-hosts.yml` runs it. */ import type { NodeRuntimeTarget } from '../../shared/node-runtime-pin' -import type { OrcadHostUnavailableReason } from './orcad-host-unavailable' +import type { OrcadHostUnavailableReason } from '../orcad/orcad-host-unavailable' /** Every shimmed toolchain command appends its name here; a managed deploy must leave it empty. */ export const FORBIDDEN_TOOL_LOG = '/tmp/orca-forbidden-tool-calls.log' diff --git a/src/main/ssh/ssh-hostile-host-managed-orcad.ts b/src/main/ssh/ssh-hostile-host-managed-orcad.ts index 528dd9707da..513fe7c6b05 100644 --- a/src/main/ssh/ssh-hostile-host-managed-orcad.ts +++ b/src/main/ssh/ssh-hostile-host-managed-orcad.ts @@ -10,7 +10,7 @@ import { ORCAD_MANAGED_REMOTE_PORT } from '../../shared/orcad-managed-runtime' import { ORCAD_RUNTIMES_DIRNAME } from '../../shared/orcad-artifacts' import type { SshTarget } from '../../shared/ssh-types' import { readOrcadActivationRecord } from './orcad-activation-record-store' -import { classifyOrcadHostUnavailable } from './orcad-host-unavailable' +import { classifyOrcadHostUnavailable } from '../orcad/orcad-host-unavailable' import { managedOrcadSlot } from './orcad-managed-runtime-context' import { orcadSlotDir, type OrcadSlotOptions } from './orcad-recovery-slot' import { resolveOrcadRemoteContext } from './orcad-remote-context' diff --git a/src/main/wsl/wsl-pinned-runtime.test.ts b/src/main/wsl/wsl-pinned-runtime.test.ts index 7a263f2b530..04da86f1bee 100644 --- a/src/main/wsl/wsl-pinned-runtime.test.ts +++ b/src/main/wsl/wsl-pinned-runtime.test.ts @@ -7,7 +7,7 @@ const mocks = vi.hoisted(() => ({ 'C:/cache/pinned.tar.gz' ) })) -vi.mock('../ssh/pinned-runtime-materializer', () => ({ +vi.mock('../node-runtime/pinned-runtime-materializer', () => ({ materializeNodeRuntimeArchive: mocks.download })) function runner(present = false, libc = 'glibc 2.31', promoted = 'ORCA_NODE_RUNTIME_READY') { diff --git a/src/main/wsl/wsl-pinned-runtime.ts b/src/main/wsl/wsl-pinned-runtime.ts index cde42016d80..3497c67f97c 100644 --- a/src/main/wsl/wsl-pinned-runtime.ts +++ b/src/main/wsl/wsl-pinned-runtime.ts @@ -1,7 +1,7 @@ import { randomBytes } from 'node:crypto' import { basename } from 'node:path' import { waitForPromiseWithSignal } from '../../shared/abort-signal-reason' -import { materializeNodeRuntimeArchive } from '../ssh/pinned-runtime-materializer' +import { materializeNodeRuntimeArchive } from '../node-runtime/pinned-runtime-materializer' import { parseGlibcVersion, parseOrcadLinuxLibc } from '../ssh/orcad-deployment-target' import { installNodeRuntimeFromHostArchiveCommand,