mirror of
https://github.com/stablyai/orca.git
synced 2026-10-18 16:03:17 +00:00
feat: compose SSH relay host evidence
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -264,6 +264,7 @@ jobs:
|
||||
src/main/ssh/ssh-relay-artifact-selector.test.ts \
|
||||
src/main/ssh/ssh-relay-libc-detection.test.ts \
|
||||
src/main/ssh/ssh-relay-linux-kernel-detection.test.ts \
|
||||
src/main/ssh/ssh-relay-host-evidence-detection.test.ts \
|
||||
src/main/ssh/ssh-relay-darwin-version-detection.test.ts \
|
||||
src/main/ssh/ssh-relay-darwin-translation-detection.test.ts \
|
||||
src/main/ssh/ssh-relay-linux-libstdcxx-detection.test.ts \
|
||||
@@ -815,6 +816,7 @@ jobs:
|
||||
src/main/ssh/ssh-relay-artifact-selector.test.ts `
|
||||
src/main/ssh/ssh-relay-libc-detection.test.ts `
|
||||
src/main/ssh/ssh-relay-linux-kernel-detection.test.ts `
|
||||
src/main/ssh/ssh-relay-host-evidence-detection.test.ts `
|
||||
src/main/ssh/ssh-relay-darwin-version-detection.test.ts `
|
||||
src/main/ssh/ssh-relay-darwin-translation-detection.test.ts `
|
||||
src/main/ssh/ssh-relay-linux-libstdcxx-detection.test.ts `
|
||||
|
||||
@@ -122,6 +122,7 @@ describe('SSH relay runtime artifact workflow', () => {
|
||||
'ssh-relay-artifact-selector.test.ts',
|
||||
'ssh-relay-libc-detection.test.ts',
|
||||
'ssh-relay-linux-kernel-detection.test.ts',
|
||||
'ssh-relay-host-evidence-detection.test.ts',
|
||||
'ssh-relay-darwin-version-detection.test.ts',
|
||||
'ssh-relay-darwin-translation-detection.test.ts',
|
||||
'ssh-relay-linux-libstdcxx-detection.test.ts',
|
||||
|
||||
+22
-3
@@ -9,9 +9,9 @@ keeps commands, hashes, runner identities, timings, and failure details.
|
||||
A checked box means the work has evidence in the detailed ledger. Design approval alone does not
|
||||
complete a box.
|
||||
|
||||
Active checkpoint: **Milestone 5 / Work Package 4 desktop resolver/cache — audit the smallest
|
||||
disconnected full host-evidence composition boundary after native acceptance of every individual
|
||||
compatibility detector, 2026-07-15, Codex implementation owner.**
|
||||
Active checkpoint: **Milestone 5 / Work Package 4 desktop resolver/cache — disconnected host-
|
||||
evidence composition is locally green; exact-head all-six native CI is next, 2026-07-15, Codex
|
||||
implementation owner.**
|
||||
`E-M5-ARTIFACT-CACHE-RESOLUTION-CI-001` closes warm-cache commit
|
||||
`22031fa68` on all six native clients, both Linux supplements, Windows x64 baseline, PR Checks,
|
||||
Golden E2E, and computer-use; Windows arm64 remains the expected build-26200 rejection against 26100. `E-M5-ARTIFACT-CACHE-RESOLUTION-LOCAL-001` passes 9/9 purpose tests,
|
||||
@@ -269,6 +269,25 @@ format, max-lines, diff, and protected-resolver gates pass; replacement native C
|
||||
clients, both Linux supplements, Windows x64 baseline, PR Checks, Golden E2E, and computer-use pass.
|
||||
Both Windows architectures pass all 32 detector cases under native Windows PowerShell. Windows
|
||||
arm64 completes full runtime proof and retains only the declared hosted build-26200 rejection against 26100. No live SSH, composer, product caller, setting/fallback, tuple, publication, or default exists.
|
||||
`E-M5-HOST-EVIDENCE-COMPOSITION-AUDIT-001` fixes the next disconnected boundary: consume an explicit
|
||||
canonical detected platform, SSH connection, and signal; run only that OS family's already-proven
|
||||
detectors with at most three concurrent bounded channels; and return frozen selector-shaped evidence.
|
||||
Darwin translation unknown returns no evidence rather than inventing native/translated state.
|
||||
Inconsistent platform identity, product/startup callers, selector changes, live SSH, settings,
|
||||
fallback, transfer/install, tuple enablement, publication, defaults, and SignPath stay absent.
|
||||
RED is recorded before implementation: the purpose suite exits 1 because its module is absent
|
||||
(0 tests, 316 ms, 132,235,264-byte max RSS), and the workflow oracle exits 1 because the suite has
|
||||
only its POSIX native-family occurrence (6 passed/1 failed, 409 ms, 131,743,744-byte max RSS).
|
||||
Pre-RED diff and protected-resolver checks pass.
|
||||
`E-M5-HOST-EVIDENCE-COMPOSITION-LOCAL-001` is green: 20/20 purpose and 193/193 focused assertions,
|
||||
512/512 broader relay assertions, 280/280 release-script assertions, typecheck, lint, format,
|
||||
max-lines, diff, no-product-import, and protected-resolver gates pass. The disconnected composer
|
||||
validates canonical platform identity before probes; invokes only Linux's three, Darwin's two, or
|
||||
Windows' one accepted bounded detector; runs multi-probe families concurrently; preserves
|
||||
conservative unknowns; refuses unknown Darwin translation; propagates cancellation/errors; and
|
||||
deeply freezes evidence. It is required in both native workflow families. Exact-head all-six Node 24
|
||||
client proof remains mandatory; no live SSH, product caller, settings/fallback, transfer/install,
|
||||
tuple, publication, default, or SignPath work is included.
|
||||
`E-M5-ARTIFACT-CACHE-ROOT-CI-001` closes the pure cache-root contract at exact head `aefcaa9a9`:
|
||||
all six primary native Node 24 jobs, both Linux supplements, Windows x64 baseline, PR Checks, Golden
|
||||
E2E, and computer-use pass; Windows arm64 build 26200 remains correctly gated against 26100. The
|
||||
|
||||
+112
-11
@@ -8,8 +8,8 @@ work; keep exact commands, runner identities, hashes, metrics, and residual gaps
|
||||
|
||||
Date created: 2026-07-14<br>
|
||||
Last updated: 2026-07-15<br>
|
||||
Current phase: Milestone 5 / Work Package 4 desktop resolver/cache — **In progress — 2026-07-15, Codex implementation owner: audit the smallest disconnected full host-evidence composition boundary after native acceptance of every individual detector**. Keep every capability disconnected from every production/default SSH path. Real Apple/SignPath rehearsals remain an explicit late gate and are not on this package's critical path.<br>
|
||||
Session checkpoint: **WINDOWS COMPATIBILITY CI CLOSED / HOST COMPOSITION AUDIT NEXT — 2026-07-15, Codex implementation owner** — `E-M5-WINDOWS-COMPATIBILITY-DETECTION-CI-001` proves both native Windows architectures pass the strict detector, all six primary clients and both Linux supplements pass, Windows x64 baseline passes, and Windows arm64 retains only the declared build-26200/26100 runner mismatch after full runtime proof. Audit only the next disconnected composition boundary; do not add a product caller, settings/fallback wiring, tuple enablement, publication, or default behavior. Merging to `main` remains prohibited, SignPath is deferred, and legacy remains the production default.<br>
|
||||
Current phase: Milestone 5 / Work Package 4 desktop resolver/cache — **In progress — 2026-07-15, Codex implementation owner: disconnected host-evidence composition is locally green; exact-head native CI is the next gate**. Keep every capability disconnected from every production/default SSH path. Real Apple/SignPath rehearsals remain an explicit late gate and are not on this package's critical path.<br>
|
||||
Session checkpoint: **HOST COMPOSITION LOCAL GREEN / NATIVE CI NEXT — 2026-07-15, Codex implementation owner** — `E-M5-HOST-EVIDENCE-COMPOSITION-LOCAL-001` proves canonical six-tuple mapping, conservative unknowns, Darwin translation policy, bounded concurrency, cancellation/error propagation, deep freezing, workflow-family inclusion, and all local static/broad/release gates. Commit and push this isolated package, then require exact-head all-six native client proof before accepting it or opening another package. Do not add a product caller, settings/fallback wiring, tuple enablement, publication, or default behavior. Merging to `main` remains prohibited, SignPath is deferred, and legacy remains the production default.<br>
|
||||
Primary design: [SSH relay GitHub Release plan](./2026-07-14-ssh-relay-github-release-plan.html)<br>
|
||||
Motivating issues: [#8450](https://github.com/stablyai/orca/issues/8450), [#1693](https://github.com/stablyai/orca/issues/1693)
|
||||
|
||||
@@ -147,11 +147,10 @@ same change as the work it records.
|
||||
local gates, and all-six native Node 24 proof are green under
|
||||
E-M5-ARTIFACT-CACHE-ROOT-LOCAL-RED-001, E-M5-ARTIFACT-CACHE-ROOT-LOCAL-001, and
|
||||
E-M5-ARTIFACT-CACHE-ROOT-CI-001. No Electron or product caller exists.
|
||||
- Active package: **In progress — 2026-07-15, Codex implementation owner** — audit the smallest
|
||||
disconnected full host-evidence composition boundary after
|
||||
E-M5-WINDOWS-COMPATIBILITY-DETECTION-CI-001 closes every individual detector on native clients.
|
||||
Keep live SSH, transfer/install, settings, fallback wiring, tuple enablement, publication,
|
||||
defaults, and SignPath separately gated.
|
||||
- Active package: **In progress — 2026-07-15, Codex implementation owner** — disconnected full host-
|
||||
evidence composition is locally green under E-M5-HOST-EVIDENCE-COMPOSITION-LOCAL-001. Exact-head
|
||||
all-six native client proof is the next acceptance gate. Keep live SSH, transfer/install,
|
||||
settings, fallback wiring, tuple enablement, publication, defaults, and SignPath separately gated.
|
||||
- Completed Work Package 2 gate: target-native Windows source-signature reports from exact-head
|
||||
artifact jobs 87267322867 and 87267322870 were independently downloaded and matched to their
|
||||
identities and signing-stage reports under E-M3-WINDOWS-SOURCE-SIGNATURE-CI-001. PR Checks
|
||||
@@ -15926,6 +15925,108 @@ config/scripts/ssh-relay-runtime-workflow.test.mjs` — PASS, two files, 38 pass
|
||||
tuple enablement, release publication, defaults, and SignPath remain absent. Legacy remains the
|
||||
sole production/default path and every tuple stays disabled.
|
||||
|
||||
### E-M5-HOST-EVIDENCE-COMPOSITION-AUDIT-001 — disconnected platform-specific evidence composition
|
||||
|
||||
- Date/owner: 2026-07-15, Codex implementation owner.
|
||||
- Audit: complete reads of the canonical remote-platform type/detector and tests, all six accepted
|
||||
platform-specific compatibility detectors and purpose suites, selector host-evidence union and
|
||||
conservative legacy classifications, native workflow commands/oracle, and the current production-
|
||||
caller graph. The detector functions remain imported only by purpose tests and no host composer
|
||||
exists. The existing platform detector has no cancellation input and must remain unchanged here.
|
||||
- Finding: add one purpose-named `ssh-relay-host-evidence-detection` module consuming an explicit
|
||||
already-detected `RemoteHostPlatform`, SSH connection, and optional cancellation signal. First
|
||||
require the platform object's relay-platform identity to agree with its canonical OS/architecture;
|
||||
inconsistent input returns undefined without a probe. Then invoke only the accepted detector family:
|
||||
Linux kernel/libc/libstdc++ concurrently (at most three channels), Darwin version/translation
|
||||
concurrently (at most two), or the single Windows compatibility detector. Return a deeply frozen
|
||||
`SshRelayHostEvidence` object with canonical OS/architecture.
|
||||
- Conservative mapping: Linux and Windows are not subject to the reviewed Darwin/Rosetta process-
|
||||
translation policy, so their composed `processTranslated` value is false. Linux preserves
|
||||
`{ family: 'unknown' }` and missing optional fields for later selector classification. A missing
|
||||
complete Windows segment yields Windows evidence with all optional compatibility fields absent so
|
||||
selection remains legacy. Darwin preserves explicit true/false translation and optional version;
|
||||
unknown translation returns no host evidence because inventing either boolean could select a native
|
||||
artifact or misdiagnose a translated process. No selector/type change is needed.
|
||||
- Failure/concurrency contract: pass the exact signal to every invoked detector. Pre-abort and
|
||||
`AbortError` propagate; unexpected detector rejection propagates unchanged rather than becoming
|
||||
fallback eligibility. Ordinary probe failures retain each accepted detector's existing unknown
|
||||
return. All OS-specific calls start before composition awaits their results, bounding the composed
|
||||
probe to the slowest existing 15-second detector instead of their sum. No unsupported detector,
|
||||
platform re-probe, retry, network/download, Node, or filesystem operation is introduced.
|
||||
- Required RED/green proof: canonical Linux/Darwin/Windows x64 and arm64 mapping; exact detector
|
||||
family/call count; complete and missing/unknown fields; native/translated/unknown Darwin behavior;
|
||||
inconsistent platform rejection with zero probes; deep freezing; all relevant calls starting
|
||||
concurrently; exact signal forwarding; cancellation/unexpected rejection propagation; absence of
|
||||
production imports; and both native workflow families requiring the purpose suite.
|
||||
- Deliberately absent: platform-detection modification, full startup/official-manifest/cache
|
||||
orchestration, live SSH/remote cells, semantic transfer capability, transfer/install, settings,
|
||||
fallback classification/wiring, Electron, `ORCA_RELAY_PATH`, tuple enablement, publication,
|
||||
defaults, and SignPath. Legacy remains the sole production/default path and every tuple stays
|
||||
disabled.
|
||||
- RED evidence, 2026-07-15:
|
||||
- `/usr/bin/time -l pnpm exec vitest run --config config/vitest.config.ts --maxWorkers=1
|
||||
src/main/ssh/ssh-relay-host-evidence-detection.test.ts` — EXPECTED FAIL, exit 1: purpose suite
|
||||
could not import absent `ssh-relay-host-evidence-detection`; zero tests collected in 316 ms,
|
||||
1.76 s wall time, 132,235,264-byte maximum RSS, 96,490,992-byte peak memory footprint.
|
||||
- `/usr/bin/time -l pnpm exec vitest run --config config/vitest.config.ts --maxWorkers=1
|
||||
config/scripts/ssh-relay-runtime-workflow.test.mjs` — EXPECTED FAIL, exit 1: 6 passed and the
|
||||
native-family inclusion oracle failed because the purpose suite had only the POSIX workflow
|
||||
occurrence (`source.split(...)` length 2 instead of 3); 409 ms suite duration, 1.84 s wall time,
|
||||
131,743,744-byte maximum RSS, 95,851,800-byte peak memory footprint.
|
||||
- Scope check before RED: `git diff --check` passed; protected
|
||||
`ssh-remote-node-resolution.ts` and `.test.ts` diff remained empty. No implementation or product
|
||||
caller existed.
|
||||
|
||||
### E-M5-HOST-EVIDENCE-COMPOSITION-LOCAL-001 — bounded immutable host evidence is locally green
|
||||
|
||||
- Date/owner/environment: 2026-07-15, Codex implementation owner; local macOS arm64, Node 26.0.0,
|
||||
pnpm 10.24.0, atop pushed head `cfe56d0fd` plus this isolated uncommitted package. The expected
|
||||
Node-24 engine warning means exact Node 24 remains a native CI gate.
|
||||
- Implementation: the new 92-line `ssh-relay-host-evidence-detection.ts` accepts only an explicit
|
||||
already-detected platform, connection, and optional signal. It rejects canonical OS/architecture
|
||||
inconsistency before any probe; starts only the selected OS family's accepted bounded detectors;
|
||||
runs Linux's three and Darwin's two probes concurrently; preserves Linux/Windows unknowns for
|
||||
conservative legacy selection; refuses to invent Darwin translation; propagates cancellation and
|
||||
unexpected rejection; and returns deeply frozen selector-shaped evidence. It has no production
|
||||
import or caller.
|
||||
- Purpose command: `/usr/bin/time -l pnpm exec vitest run --config config/vitest.config.ts
|
||||
--maxWorkers=1 src/main/ssh/ssh-relay-host-evidence-detection.test.ts` — PASS, one file / 20 tests in
|
||||
337 ms Vitest, 1.69 seconds wall, 132,431,872-byte maximum RSS, zero swaps. It covers all six
|
||||
canonical tuples and exact family call counts, full/unknown/missing mapping, native/translated/
|
||||
unknown Darwin behavior, inconsistent input with zero probes, deep freezing, concurrency, exact
|
||||
signal forwarding, cancellation, and unexpected rejection.
|
||||
- Workflow-oracle command: `/usr/bin/time -l pnpm exec vitest run --config config/vitest.config.ts
|
||||
--maxWorkers=1 config/scripts/ssh-relay-runtime-workflow.test.mjs` — PASS, one file / seven tests in
|
||||
384 ms Vitest, 1.73 seconds wall, 132,972,544-byte maximum RSS, zero swaps. The purpose suite now
|
||||
occurs exactly once in both POSIX and PowerShell native contract commands.
|
||||
- Post-format focused command: `/usr/bin/time -l pnpm exec vitest run --config
|
||||
config/vitest.config.ts --maxWorkers=1` plus the composer, Windows compatibility, libc, kernel,
|
||||
Darwin version/translation, libstdc++, selector, platform detection, and workflow-oracle suites —
|
||||
PASS, 10 files / 193 passed / one declared native-Windows skip in 4.66 seconds Vitest, 7.18 seconds
|
||||
wall, 138,493,952-byte maximum RSS, zero swaps.
|
||||
- Broader relay command: `/usr/bin/time -l pnpm exec vitest run --config config/vitest.config.ts
|
||||
--maxWorkers=1 --silent=true src/main/ssh/ssh-relay-*.test.ts` — PASS, 39 files passed / three
|
||||
declared skipped files, 512 tests passed / four declared platform skips in 25.68 seconds Vitest,
|
||||
27.42 seconds wall, 281,608,192-byte maximum RSS, zero swaps. Only the established stale-lock
|
||||
diagnostic and local Node 26 `module.register()` deprecation warning appear.
|
||||
- Release-script command: `/usr/bin/time -l pnpm exec vitest run --config config/vitest.config.ts
|
||||
--maxWorkers=1 --silent=true config/scripts/ssh-relay-runtime-*.test.mjs` — PASS, 50 files / 280 tests
|
||||
in 24.51 seconds Vitest, 27.61 seconds wall, 188,825,600-byte maximum RSS, zero swaps.
|
||||
- Static gates: `/usr/bin/time -l pnpm typecheck` passes in 5.81 seconds wall with
|
||||
1,262,075,904-byte maximum RSS. `/usr/bin/time -l pnpm lint` passes in 27.03 seconds wall with
|
||||
2,034,597,888-byte maximum RSS; all switch-exhaustiveness, 41 reliability, 355-entry max-lines,
|
||||
bundled-skill, 9,837 localization-reference, locale-parity, and zero-candidate localization gates
|
||||
pass, with the existing 26 warnings only in untouched files. Explicit max-lines, targeted
|
||||
`oxlint`, targeted `oxfmt --check`, `git diff --check`, no-production-import, and protected-
|
||||
resolver empty-diff checks pass. The 92-line implementation and 229-line purpose suite add no
|
||||
max-lines suppression or vague module.
|
||||
- Boundary/residual gaps: this is local mock/composition proof, not live SSH or native detector
|
||||
execution. Exact-head all-six Node 24 client proof is mandatory next. Platform detection,
|
||||
manifest/cache startup composition, live SSH/remote cells, semantic transfer capability,
|
||||
transfer/install, Electron/settings, fallback classification/wiring, `ORCA_RELAY_PATH`, tuple
|
||||
enablement, publication, defaults, and SignPath remain absent. Legacy remains the sole production/
|
||||
default path and every tuple stays disabled.
|
||||
|
||||
## Accepted Gaps
|
||||
|
||||
No product gap is accepted merely because it appears in this list. Each entry requires explicit
|
||||
@@ -15984,10 +16085,10 @@ The project is not complete until every applicable item below is checked with ev
|
||||
|
||||
## Next Required Action
|
||||
|
||||
Audit only the smallest disconnected Windows compatibility-evidence boundary for the already-reviewed
|
||||
OS build, OpenSSH for Windows, Windows PowerShell, and .NET Framework selector fields. Record the
|
||||
exact acquisition/parsing/failure contract before adding purpose tests or implementation. Do not
|
||||
compose full host evidence, provision production secrets or environments, add an Electron/startup
|
||||
Commit and push only the locally green disconnected host-evidence composition package under
|
||||
E-M5-HOST-EVIDENCE-COMPOSITION-LOCAL-001, then require exact-head all-six Node 24 native client proof
|
||||
and adjacent PR checks before accepting it or auditing the next package. Do not modify platform
|
||||
detection or selectors, provision production secrets or environments, add an Electron/startup
|
||||
consumer, embed unreviewed keys or manifest bytes, add SSH transfer/install, mode wiring, fallback,
|
||||
tuple enablement, release publication, or default behavior.
|
||||
Keep Node upstream `.tar.xz` inputs, Windows ZIP, `ORCA_RELAY_PATH`, existing desktop required-assets
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import type { SshConnection } from './ssh-connection'
|
||||
import { detectSshRelayHostEvidence } from './ssh-relay-host-evidence-detection'
|
||||
import { getRemoteHostPlatform, type RemoteHostPlatform } from './ssh-remote-platform'
|
||||
|
||||
const detectorMocks = vi.hoisted(() => ({
|
||||
kernel: vi.fn(),
|
||||
libc: vi.fn(),
|
||||
libstdcxx: vi.fn(),
|
||||
darwinVersion: vi.fn(),
|
||||
darwinTranslation: vi.fn(),
|
||||
windowsCompatibility: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('./ssh-relay-linux-kernel-detection', () => ({
|
||||
detectSshRelayLinuxKernelRelease: detectorMocks.kernel
|
||||
}))
|
||||
vi.mock('./ssh-relay-libc-detection', () => ({
|
||||
detectSshRelayLinuxLibc: detectorMocks.libc
|
||||
}))
|
||||
vi.mock('./ssh-relay-linux-libstdcxx-detection', () => ({
|
||||
detectSshRelayLinuxLibstdcxx: detectorMocks.libstdcxx
|
||||
}))
|
||||
vi.mock('./ssh-relay-darwin-version-detection', () => ({
|
||||
detectSshRelayDarwinVersion: detectorMocks.darwinVersion
|
||||
}))
|
||||
vi.mock('./ssh-relay-darwin-translation-detection', () => ({
|
||||
detectSshRelayDarwinProcessTranslation: detectorMocks.darwinTranslation
|
||||
}))
|
||||
vi.mock('./ssh-relay-windows-compatibility-detection', () => ({
|
||||
detectSshRelayWindowsCompatibility: detectorMocks.windowsCompatibility
|
||||
}))
|
||||
|
||||
const connection = {} as SshConnection
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks()
|
||||
detectorMocks.kernel.mockResolvedValue('6.8.0-63-generic')
|
||||
detectorMocks.libc.mockResolvedValue({ family: 'glibc', version: '2.39' })
|
||||
detectorMocks.libstdcxx.mockResolvedValue({
|
||||
libstdcxxVersion: '6.0.33',
|
||||
glibcxxVersion: '3.4.33'
|
||||
})
|
||||
detectorMocks.darwinVersion.mockResolvedValue('15.5')
|
||||
detectorMocks.darwinTranslation.mockResolvedValue(false)
|
||||
detectorMocks.windowsCompatibility.mockResolvedValue({
|
||||
build: 26100,
|
||||
openSshVersion: '9.8p1',
|
||||
powerShellVersion: '5.1',
|
||||
dotNetFrameworkRelease: 533325
|
||||
})
|
||||
})
|
||||
|
||||
function allDetectorCallCounts(): number[] {
|
||||
return Object.values(detectorMocks).map((mock) => mock.mock.calls.length)
|
||||
}
|
||||
|
||||
describe('SSH relay host evidence detection', () => {
|
||||
it.each([
|
||||
['linux-x64', 'linux', 'x64', [1, 1, 1, 0, 0, 0]],
|
||||
['linux-arm64', 'linux', 'arm64', [1, 1, 1, 0, 0, 0]],
|
||||
['darwin-x64', 'darwin', 'x64', [0, 0, 0, 1, 1, 0]],
|
||||
['darwin-arm64', 'darwin', 'arm64', [0, 0, 0, 1, 1, 0]],
|
||||
['win32-x64', 'win32', 'x64', [0, 0, 0, 0, 0, 1]],
|
||||
['win32-arm64', 'win32', 'arm64', [0, 0, 0, 0, 0, 1]]
|
||||
] as const)(
|
||||
'maps canonical %s and invokes only its detector family',
|
||||
async (relayPlatform, os, architecture, expectedCalls) => {
|
||||
const result = await detectSshRelayHostEvidence(
|
||||
getRemoteHostPlatform(relayPlatform),
|
||||
connection
|
||||
)
|
||||
|
||||
expect(result).toMatchObject({ os, architecture, processTranslated: false })
|
||||
expect(allDetectorCallCounts()).toEqual(expectedCalls)
|
||||
}
|
||||
)
|
||||
|
||||
it('composes complete Linux evidence and deeply freezes owned values', async () => {
|
||||
const result = await detectSshRelayHostEvidence(getRemoteHostPlatform('linux-x64'), connection)
|
||||
|
||||
expect(result).toEqual({
|
||||
os: 'linux',
|
||||
architecture: 'x64',
|
||||
processTranslated: false,
|
||||
kernelVersion: '6.8.0-63-generic',
|
||||
libc: { family: 'glibc', version: '2.39' },
|
||||
libstdcxxVersion: '6.0.33',
|
||||
glibcxxVersion: '3.4.33'
|
||||
})
|
||||
expect(Object.isFrozen(result)).toBe(true)
|
||||
expect(result?.os === 'linux' && Object.isFrozen(result.libc)).toBe(true)
|
||||
})
|
||||
|
||||
it('preserves unknown Linux libc and missing optional detector evidence', async () => {
|
||||
detectorMocks.kernel.mockResolvedValue(undefined)
|
||||
detectorMocks.libc.mockResolvedValue({ family: 'unknown' })
|
||||
detectorMocks.libstdcxx.mockResolvedValue(undefined)
|
||||
|
||||
await expect(
|
||||
detectSshRelayHostEvidence(getRemoteHostPlatform('linux-arm64'), connection)
|
||||
).resolves.toEqual({
|
||||
os: 'linux',
|
||||
architecture: 'arm64',
|
||||
processTranslated: false,
|
||||
libc: { family: 'unknown' }
|
||||
})
|
||||
})
|
||||
|
||||
it.each([false, true])('preserves Darwin process translation %s', async (translated) => {
|
||||
detectorMocks.darwinTranslation.mockResolvedValue(translated)
|
||||
|
||||
await expect(
|
||||
detectSshRelayHostEvidence(getRemoteHostPlatform('darwin-arm64'), connection)
|
||||
).resolves.toEqual({
|
||||
os: 'darwin',
|
||||
architecture: 'arm64',
|
||||
processTranslated: translated,
|
||||
version: '15.5'
|
||||
})
|
||||
})
|
||||
|
||||
it('keeps a known Darwin translation result when the version is unavailable', async () => {
|
||||
detectorMocks.darwinVersion.mockResolvedValue(undefined)
|
||||
|
||||
await expect(
|
||||
detectSshRelayHostEvidence(getRemoteHostPlatform('darwin-x64'), connection)
|
||||
).resolves.toEqual({
|
||||
os: 'darwin',
|
||||
architecture: 'x64',
|
||||
processTranslated: false
|
||||
})
|
||||
})
|
||||
|
||||
it('returns no Darwin evidence when process translation is unknown', async () => {
|
||||
detectorMocks.darwinTranslation.mockResolvedValue(undefined)
|
||||
|
||||
await expect(
|
||||
detectSshRelayHostEvidence(getRemoteHostPlatform('darwin-arm64'), connection)
|
||||
).resolves.toBeUndefined()
|
||||
})
|
||||
|
||||
it('preserves complete Windows compatibility evidence', async () => {
|
||||
await expect(
|
||||
detectSshRelayHostEvidence(getRemoteHostPlatform('win32-arm64'), connection)
|
||||
).resolves.toEqual({
|
||||
os: 'win32',
|
||||
architecture: 'arm64',
|
||||
processTranslated: false,
|
||||
build: 26100,
|
||||
openSshVersion: '9.8p1',
|
||||
powerShellVersion: '5.1',
|
||||
dotNetFrameworkRelease: 533325
|
||||
})
|
||||
})
|
||||
|
||||
it('returns conservative Windows evidence when compatibility detection is unavailable', async () => {
|
||||
detectorMocks.windowsCompatibility.mockResolvedValue(undefined)
|
||||
|
||||
await expect(
|
||||
detectSshRelayHostEvidence(getRemoteHostPlatform('win32-x64'), connection)
|
||||
).resolves.toEqual({
|
||||
os: 'win32',
|
||||
architecture: 'x64',
|
||||
processTranslated: false
|
||||
})
|
||||
})
|
||||
|
||||
it('rejects inconsistent platform identity without starting any detector', async () => {
|
||||
const inconsistent = {
|
||||
...getRemoteHostPlatform('linux-x64'),
|
||||
os: 'win32'
|
||||
} as RemoteHostPlatform
|
||||
|
||||
await expect(detectSshRelayHostEvidence(inconsistent, connection)).resolves.toBeUndefined()
|
||||
expect(allDetectorCallCounts()).toEqual([0, 0, 0, 0, 0, 0])
|
||||
})
|
||||
|
||||
it.each([
|
||||
['linux-x64', ['kernel', 'libc', 'libstdcxx']],
|
||||
['darwin-x64', ['darwinVersion', 'darwinTranslation']]
|
||||
] as const)(
|
||||
'starts all %s family probes before awaiting a result',
|
||||
async (relayPlatform, names) => {
|
||||
const settlements = names.map(() => Promise.withResolvers<unknown>())
|
||||
names.forEach((name, index) =>
|
||||
detectorMocks[name].mockReturnValue(settlements[index].promise)
|
||||
)
|
||||
|
||||
const pending = detectSshRelayHostEvidence(getRemoteHostPlatform(relayPlatform), connection)
|
||||
|
||||
expect(names.map((name) => detectorMocks[name].mock.calls.length)).toEqual(names.map(() => 1))
|
||||
settlements.forEach((settlement) => settlement.resolve(undefined))
|
||||
await pending
|
||||
}
|
||||
)
|
||||
|
||||
it('forwards the exact signal to every selected detector', async () => {
|
||||
const controller = new AbortController()
|
||||
|
||||
await detectSshRelayHostEvidence(getRemoteHostPlatform('linux-x64'), connection, {
|
||||
signal: controller.signal
|
||||
})
|
||||
|
||||
for (const mock of [detectorMocks.kernel, detectorMocks.libc, detectorMocks.libstdcxx]) {
|
||||
expect(mock).toHaveBeenCalledWith(connection, { signal: controller.signal })
|
||||
}
|
||||
})
|
||||
|
||||
it('propagates cancellation without converting it to host evidence', async () => {
|
||||
const abortError = new Error('cancel host evidence')
|
||||
abortError.name = 'AbortError'
|
||||
detectorMocks.windowsCompatibility.mockRejectedValue(abortError)
|
||||
|
||||
await expect(
|
||||
detectSshRelayHostEvidence(getRemoteHostPlatform('win32-x64'), connection)
|
||||
).rejects.toBe(abortError)
|
||||
})
|
||||
|
||||
it('propagates unexpected detector rejection unchanged', async () => {
|
||||
const failure = new Error('unexpected kernel detector failure')
|
||||
detectorMocks.kernel.mockRejectedValue(failure)
|
||||
|
||||
await expect(
|
||||
detectSshRelayHostEvidence(getRemoteHostPlatform('linux-x64'), connection)
|
||||
).rejects.toBe(failure)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,92 @@
|
||||
import type { SshConnection } from './ssh-connection'
|
||||
import { detectSshRelayDarwinProcessTranslation } from './ssh-relay-darwin-translation-detection'
|
||||
import { detectSshRelayDarwinVersion } from './ssh-relay-darwin-version-detection'
|
||||
import { detectSshRelayLinuxLibc } from './ssh-relay-libc-detection'
|
||||
import { detectSshRelayLinuxKernelRelease } from './ssh-relay-linux-kernel-detection'
|
||||
import { detectSshRelayLinuxLibstdcxx } from './ssh-relay-linux-libstdcxx-detection'
|
||||
import { detectSshRelayWindowsCompatibility } from './ssh-relay-windows-compatibility-detection'
|
||||
import type { SshRelayHostEvidence } from './ssh-relay-artifact-selector'
|
||||
import { getRemoteHostPlatform, type RemoteHostPlatform } from './ssh-remote-platform'
|
||||
|
||||
type DetectionOptions = { signal?: AbortSignal }
|
||||
|
||||
function isCanonicalPlatform(platform: RemoteHostPlatform): boolean {
|
||||
const canonical = getRemoteHostPlatform(platform.relayPlatform)
|
||||
return canonical.os === platform.os && canonical.arch === platform.arch
|
||||
}
|
||||
|
||||
async function detectLinuxHostEvidence(
|
||||
platform: RemoteHostPlatform,
|
||||
connection: SshConnection,
|
||||
options: DetectionOptions
|
||||
): Promise<SshRelayHostEvidence> {
|
||||
// Why: independent bounded probes run together so composition adds no serial bootstrap latency.
|
||||
const [kernelVersion, libc, libstdcxx] = await Promise.all([
|
||||
detectSshRelayLinuxKernelRelease(connection, options),
|
||||
detectSshRelayLinuxLibc(connection, options),
|
||||
detectSshRelayLinuxLibstdcxx(connection, options)
|
||||
])
|
||||
const frozenLibc = Object.freeze({ ...libc })
|
||||
return Object.freeze({
|
||||
os: 'linux',
|
||||
architecture: platform.arch,
|
||||
processTranslated: false,
|
||||
...(kernelVersion === undefined ? {} : { kernelVersion }),
|
||||
libc: frozenLibc,
|
||||
...(libstdcxx === undefined ? {} : libstdcxx)
|
||||
})
|
||||
}
|
||||
|
||||
async function detectDarwinHostEvidence(
|
||||
platform: RemoteHostPlatform,
|
||||
connection: SshConnection,
|
||||
options: DetectionOptions
|
||||
): Promise<SshRelayHostEvidence | undefined> {
|
||||
const [version, processTranslated] = await Promise.all([
|
||||
detectSshRelayDarwinVersion(connection, options),
|
||||
detectSshRelayDarwinProcessTranslation(connection, options)
|
||||
])
|
||||
// Why: guessing native or translated could select incompatible executable bytes.
|
||||
if (processTranslated === undefined) {
|
||||
return undefined
|
||||
}
|
||||
return Object.freeze({
|
||||
os: 'darwin',
|
||||
architecture: platform.arch,
|
||||
processTranslated,
|
||||
...(version === undefined ? {} : { version })
|
||||
})
|
||||
}
|
||||
|
||||
async function detectWindowsHostEvidence(
|
||||
platform: RemoteHostPlatform,
|
||||
connection: SshConnection,
|
||||
options: DetectionOptions
|
||||
): Promise<SshRelayHostEvidence> {
|
||||
const compatibility = await detectSshRelayWindowsCompatibility(connection, options)
|
||||
return Object.freeze({
|
||||
os: 'win32',
|
||||
architecture: platform.arch,
|
||||
processTranslated: false,
|
||||
...compatibility
|
||||
})
|
||||
}
|
||||
|
||||
export async function detectSshRelayHostEvidence(
|
||||
platform: RemoteHostPlatform,
|
||||
connection: SshConnection,
|
||||
options: DetectionOptions = {}
|
||||
): Promise<SshRelayHostEvidence | undefined> {
|
||||
// Why: inconsistent caller evidence must not start probes or select a runtime for another tuple.
|
||||
if (!isCanonicalPlatform(platform)) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (platform.os === 'linux') {
|
||||
return detectLinuxHostEvidence(platform, connection, options)
|
||||
}
|
||||
if (platform.os === 'darwin') {
|
||||
return detectDarwinHostEvidence(platform, connection, options)
|
||||
}
|
||||
return detectWindowsHostEvidence(platform, connection, options)
|
||||
}
|
||||
Reference in New Issue
Block a user