mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
refactor(orcad): orcad artifacts live under orcad/ and node-runtime/ (#27963)
Co-authored-by: m4air <m4air@Mac.localdomain>
This commit is contained in:
@@ -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-*'
|
||||
|
||||
@@ -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-*'
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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/',
|
||||
|
||||
@@ -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 <appDir> <signedListFile>
|
||||
*
|
||||
|
||||
@@ -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 }))
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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() }))
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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<string>): boolean {
|
||||
return present.has('g++') || present.has('c++') || present.has('clang++')
|
||||
}
|
||||
|
||||
function hasPython(present: ReadonlySet<string>): boolean {
|
||||
return present.has('python3') || present.has('python')
|
||||
}
|
||||
|
||||
// POSIX-sh probe: echo a `HAVE <tool>` line per resolvable build tool and a
|
||||
// single `PKG <manager>` 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<string>()
|
||||
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'
|
||||
]
|
||||
}
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
+2
-2
@@ -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,
|
||||
+2
-2
@@ -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)
|
||||
+2
-2
@@ -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', () => {
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<string>): boolean {
|
||||
return present.has('g++') || present.has('c++') || present.has('clang++')
|
||||
}
|
||||
|
||||
function hasPython(present: ReadonlySet<string>): boolean {
|
||||
return present.has('python3') || present.has('python')
|
||||
}
|
||||
|
||||
// POSIX-sh probe: echo a `HAVE <tool>` line per resolvable build tool and a
|
||||
// single `PKG <manager>` 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<string>()
|
||||
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 <code>`
|
||||
// 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 <prefix>/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://<mirror>/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://<mirror>/dist'
|
||||
]
|
||||
return [...lines, '', `Underlying install error: ${underlyingError}`].join('\n')
|
||||
}
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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<OrcadDeployResult, { outcome: 'installed-not-activated' }>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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' }))
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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' }))
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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 =
|
||||
| {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user