mirror of
https://github.com/stablyai/orca.git
synced 2026-10-18 16:03:17 +00:00
feat: detect remote macOS translation
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -265,6 +265,7 @@ jobs:
|
||||
src/main/ssh/ssh-relay-libc-detection.test.ts \
|
||||
src/main/ssh/ssh-relay-linux-kernel-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-artifact-download.test.ts \
|
||||
src/main/ssh/ssh-relay-artifact-extraction.test.ts \
|
||||
src/main/ssh/ssh-relay-artifact-cache-lock-release.test.ts \
|
||||
@@ -813,6 +814,7 @@ jobs:
|
||||
src/main/ssh/ssh-relay-libc-detection.test.ts `
|
||||
src/main/ssh/ssh-relay-linux-kernel-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-artifact-download.test.ts `
|
||||
src/main/ssh/ssh-relay-artifact-extraction.test.ts `
|
||||
src/main/ssh/ssh-relay-artifact-cache-lock-release.test.ts `
|
||||
|
||||
@@ -123,6 +123,7 @@ describe('SSH relay runtime artifact workflow', () => {
|
||||
'ssh-relay-libc-detection.test.ts',
|
||||
'ssh-relay-linux-kernel-detection.test.ts',
|
||||
'ssh-relay-darwin-version-detection.test.ts',
|
||||
'ssh-relay-darwin-translation-detection.test.ts',
|
||||
'ssh-relay-artifact-download.test.ts',
|
||||
'ssh-relay-artifact-extraction.test.ts',
|
||||
'ssh-relay-artifact-cache-lock-release.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 next smallest
|
||||
remaining disconnected host-evidence boundary after exact-head acceptance of macOS product-version
|
||||
detection, 2026-07-15, Codex implementation owner.**
|
||||
Active checkpoint: **Milestone 5 / Work Package 4 desktop resolver/cache — implement the
|
||||
disconnected marked Darwin process-translation probe fixed by
|
||||
`E-M5-DARWIN-TRANSLATION-DETECTION-AUDIT-001`, 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,
|
||||
@@ -177,6 +177,25 @@ computer-use pass. Every native client runs the 21-case purpose suite; POSIX job
|
||||
the complete 85,213,511-byte runtime plus Node/PTY/watcher/resource settlement and retains only the
|
||||
known hosted build-26200 rejection against required 26100. This is contract evidence, not live SSH/
|
||||
macOS or Rosetta proof; composition and every production/default path remain absent.
|
||||
`E-M5-DARWIN-TRANSLATION-DETECTION-AUDIT-001` fixes the next smallest package: one disconnected,
|
||||
15-second cancellable POSIX-shell/no-Node marked probe of `sysctl.proc_translated` plus
|
||||
`hw.optional.arm64`. Explicit non-conflicting 1/0 values return translated/native; a missing
|
||||
translation key with explicit non-arm64 hardware proves native Intel; missing/conflicting/
|
||||
malformed evidence remains unknown. The detector does not change selector types or compose an
|
||||
unknown into a boolean. Live Rosetta SSH remains a separate required cell; product callers,
|
||||
transfer/install, settings, fallback wiring, tuple enablement, publication, and defaults stay
|
||||
absent.
|
||||
`E-M5-DARWIN-TRANSLATION-DETECTION-LOCAL-RED-001` records the expected missing-module failure and
|
||||
the native-workflow oracle's 6/7 result before implementation. Neither runner command contains the
|
||||
new suite, so its source occurrence count is one instead of three. Protected resolver files remain
|
||||
untouched; no selector, composer, caller, or default behavior is connected.
|
||||
`E-M5-DARWIN-TRANSLATION-DETECTION-LOCAL-001` is green: 23/23 purpose tests, 85/85 focused+workflow
|
||||
tests, 438/438 non-full-size relay tests with three declared skips, 280/280 release-script tests,
|
||||
typecheck, lint, format, max-lines, diff, and protected-resolver gates pass. Explicit
|
||||
non-conflicting Darwin sysctl evidence returns translated/native; Intel absent-key evidence is
|
||||
handled without coercing arm64-capable or unknown probe loss to native. Both native workflow
|
||||
families require the suite. Exact Node 24/all-six native proof is next; live Rosetta SSH,
|
||||
composition, and all production/default behavior remain absent.
|
||||
`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
|
||||
|
||||
+101
-5
@@ -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 next smallest remaining disconnected host-evidence boundary after exact-head acceptance of macOS product-version detection**. Keep every capability disconnected from host-evidence composition and 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: **CI CLOSED / NEXT AUDIT — 2026-07-15, Codex implementation owner** — `E-M5-DARWIN-VERSION-DETECTION-CI-001` closes implementation commit `bcda04389`: all six primary native Node 24 jobs, both Linux supplements, Windows x64 baseline, PR Checks, Golden E2E, and computer-use pass. Windows arm64 verifies the complete runtime tree and exact-byte Node/PTY/watcher/resource-settlement smoke, then retains only the declared hosted build-26200 rejection against required 26100. Every native client runs the new 21-case contract. Rosetta/process translation, real SSH/macOS, host-evidence composition, product callers, settings, transfer/install, fallback, tuple enablement, publication, and default behavior remain absent. 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: implement the disconnected marked Darwin process-translation probe fixed by E-M5-DARWIN-TRANSLATION-DETECTION-AUDIT-001**. Keep every capability disconnected from host-evidence composition and 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: **LOCAL GREEN / EXACT-HEAD CI NEXT — 2026-07-15, Codex implementation owner** — `E-M5-DARWIN-TRANSLATION-DETECTION-LOCAL-001` passes 23 purpose tests, 85 focused/workflow tests, 438 relay tests with three declared skips, 280 release-script tests, typecheck, lint, format, max-lines, diff, and protected-resolver gates. The detector returns translated/native/unknown evidence without changing the selector type or composing host evidence. Exact Node 24/all-six native execution remains open. Live Rosetta SSH remains mandatory before any translated tuple could be considered and translated hosts remain legacy-only. No product caller, setting, transfer/install, fallback wiring, tuple enablement, publication, or default behavior is connected. 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)
|
||||
|
||||
@@ -15362,6 +15362,102 @@ config/scripts/ssh-relay-runtime-*.test.mjs` — PASS, 50 files / 280 tests in 1
|
||||
fallback classifier, tuple enablement, publication, or default behavior is added; legacy remains
|
||||
the sole production path and no tuple is enabled.
|
||||
|
||||
### E-M5-DARWIN-TRANSLATION-DETECTION-AUDIT-001 — disconnected Darwin translation evidence
|
||||
|
||||
- Date/owner: 2026-07-15, Codex implementation owner.
|
||||
- Audit: current platform detection reports the remote process architecture from marked `uname -sm`
|
||||
but cannot distinguish an x64 process on Intel hardware from an x64 process translated by Rosetta
|
||||
on Apple Silicon. The selector already rejects explicit `processTranslated: true` as
|
||||
`translated-process`, and the reviewed policy keeps Rosetta legacy-only until a separate live
|
||||
Rosetta SSH cell passes. No desktop SSH module currently invokes `sysctl.proc_translated`.
|
||||
- Finding: add one purpose-named `ssh-relay-darwin-translation-detection` module that consumes an
|
||||
explicit SSH connection and optional cancellation signal, executes one 15-second POSIX-shell/
|
||||
no-Node marked probe, and returns `true`, `false`, or undefined. Query
|
||||
`sysctl.proc_translated` and `hw.optional.arm64` within the same bounded command. An explicit
|
||||
translation value of 1 is translated unless it conflicts with explicit non-arm64 hardware; an
|
||||
explicit 0 is native. When the translation key is absent, explicit `hw.optional.arm64=0` proves
|
||||
native Intel; missing translation on arm64-capable or unknown hardware remains unknown. Any
|
||||
conflicting, malformed, duplicate, incomplete, noisy, or oversized evidence remains unknown.
|
||||
- Local audit evidence: native macOS arm64 reports `uname -m` = `arm64`,
|
||||
`/usr/sbin/sysctl -in sysctl.proc_translated` = `0`, and
|
||||
`/usr/sbin/sysctl -in hw.optional.arm64` = `1`. This proves only the local native response, not
|
||||
Rosetta or SSH execution.
|
||||
- Safety contract: startup noise outside exact markers is ignored; only one complete unique line
|
||||
containing the two exact bounded fields counts. Ordinary command failure is unavailable evidence;
|
||||
`AbortError` propagates. The probe uses no Node, npm, Python, Perl, archive, checksum, or
|
||||
localization-sensitive parsing. It does not change `SshRelayHostEvidence` or invent a value for
|
||||
unknown evidence; conservative composition remains a separate package.
|
||||
- Required RED: missing purpose module, native Apple Silicon, translated Apple Silicon, native
|
||||
Intel-key-absent, unknown/missing/conflicting/malformed/multi-line/duplicate/unterminated/
|
||||
concatenated/oversized evidence, unavailable command, cancellation, exact signal/15-second exec,
|
||||
no-Node command construction, and both native-workflow families requiring the suite.
|
||||
- Deliberately absent: live Rosetta/Intel/macOS SSH cells, full host-evidence composition, Linux
|
||||
libstdc++/GLIBCXX, Windows evidence, Electron/startup/product callers, transfer/install, settings,
|
||||
fallback wiring, tuple enablement, publication, and defaults. Legacy remains the sole production
|
||||
path and the primary HTML plan is unchanged.
|
||||
|
||||
### E-M5-DARWIN-TRANSLATION-DETECTION-LOCAL-RED-001 — Darwin translation probe fails first
|
||||
|
||||
- Date/owner: 2026-07-15, Codex implementation owner.
|
||||
- Source: uncommitted RED tests atop evidence head `5e7f8e5f5`; protected resolver files have zero
|
||||
diff.
|
||||
- Purpose command: `pnpm vitest run
|
||||
src/main/ssh/ssh-relay-darwin-translation-detection.test.ts` — expected FAIL, 1 failed suite / zero
|
||||
collected tests because `ssh-relay-darwin-translation-detection` does not exist; 336 ms Vitest.
|
||||
- Native-workflow command: `pnpm vitest run
|
||||
config/scripts/ssh-relay-runtime-workflow.test.mjs` — expected FAIL, 1 file / 6 passed / 1 failed
|
||||
in 393 ms. The source split has length 1 rather than the required 3 until both POSIX and
|
||||
PowerShell native commands include the purpose suite.
|
||||
- Environment: local macOS arm64, Node 26.0.0 / pnpm 10.24.0 with the expected Node-24 engine
|
||||
warning. Exact Node 24 and all-six native execution remain required after local green.
|
||||
- Boundary: failure-before-implementation evidence only. It authorizes only the audited disconnected
|
||||
probe, purpose tests, and native-workflow inclusion; no selector/composer/product behavior is
|
||||
connected.
|
||||
|
||||
### E-M5-DARWIN-TRANSLATION-DETECTION-LOCAL-001 — bounded Darwin translation evidence
|
||||
|
||||
- Date/owner: 2026-07-15, Codex implementation owner.
|
||||
- Implementation: `ssh-relay-darwin-translation-detection.ts` executes one 15-second cancellable
|
||||
marked POSIX command, guards `sysctl`, and captures `sysctl.proc_translated` plus
|
||||
`hw.optional.arm64` in one exact bounded line. Explicit non-conflicting translation/native values
|
||||
return true/false; absent translation with explicit non-arm64 hardware returns false for native
|
||||
Intel; arm64-capable/unknown missing evidence and conflicts return undefined. Startup noise is
|
||||
ignored outside markers; empty, multi-line, duplicate, unterminated, marker-concatenated, extra,
|
||||
non-canonical, invalid, or oversized evidence returns undefined. Ordinary execution failure is
|
||||
unavailable and cancellation propagates. No selector/composer is connected.
|
||||
- Purpose command: `/usr/bin/time -l pnpm exec vitest run --config config/vitest.config.ts
|
||||
--maxWorkers=1 src/main/ssh/ssh-relay-darwin-translation-detection.test.ts` — PASS, 1 file / 23
|
||||
tests in 212 ms Vitest, 1.08 s wall, 132,104,192-byte maximum RSS, zero swaps. Translated/native
|
||||
Apple Silicon, explicit native/translated signals, Intel absent-key behavior, malformed/conflict/
|
||||
unavailable/cancellation boundaries, exact 15-second signal-qualified exec, and no forbidden
|
||||
runtime dependency pass.
|
||||
- Focused plus workflow command: `/usr/bin/time -l pnpm exec vitest run --config
|
||||
config/vitest.config.ts --maxWorkers=1
|
||||
src/main/ssh/ssh-relay-darwin-translation-detection.test.ts
|
||||
src/main/ssh/ssh-relay-darwin-version-detection.test.ts
|
||||
src/main/ssh/ssh-relay-artifact-selector.test.ts
|
||||
src/main/ssh/ssh-remote-platform-detection.test.ts
|
||||
config/scripts/ssh-relay-runtime-workflow.test.mjs` — PASS, 5 files / 85 tests in 2.23 s Vitest,
|
||||
3.21 s wall, 136,921,088-byte maximum RSS, zero swaps. Both native workflow command families
|
||||
require the purpose suite exactly once; current platform and selector behavior remains unchanged.
|
||||
- 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, 36 files passed / 3 declared
|
||||
skipped, 438 tests passed / 3 declared skipped in 16.43 s Vitest, 17.55 s wall, 303,955,968-byte
|
||||
maximum RSS, zero swaps. Expected stale-lock and local Node 26 deprecation diagnostics remain.
|
||||
- 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 14.47 s Vitest,
|
||||
16.26 s wall, 195,428,352-byte maximum RSS, zero swaps.
|
||||
- Static gates: `pnpm typecheck`, targeted `oxlint`, targeted `oxfmt --check` after formatting its two
|
||||
reported files, `pnpm lint`, `pnpm run check:max-lines-ratchet`, and `git diff --check` pass. Full
|
||||
lint includes switch exhaustiveness, 41 reliability gates, 355 grandfathered max-lines entries
|
||||
with no new bypass, bundled-skill verification, 9,837 localization references and locale parity,
|
||||
and zero coverage candidates. Its 26 warnings are in untouched files. Protected resolver files
|
||||
have zero diff.
|
||||
- Residual gap: exact Node 24/all-six native client execution, live Intel/Apple Silicon/Rosetta SSH,
|
||||
host composition, and every production/default path remain unproven and disconnected. Unknown
|
||||
evidence is not coerced to false. No tuple is enabled and legacy remains the only default.
|
||||
|
||||
## Accepted Gaps
|
||||
|
||||
No product gap is accepted merely because it appears in this list. Each entry requires explicit
|
||||
@@ -15420,9 +15516,9 @@ The project is not complete until every applicable item below is checked with ev
|
||||
|
||||
## Next Required Action
|
||||
|
||||
Audit the next smallest disconnected host-evidence slice after exact-head acceptance of macOS
|
||||
product-version detection. Do not compose full host evidence, provision production secrets or
|
||||
environments, add
|
||||
Commit the locally proven disconnected Darwin process-translation package, push it to the draft PR,
|
||||
and require exact-head all-six native artifact CI before advancing. Do not compose full host evidence,
|
||||
provision production secrets or environments, add
|
||||
Rosetta detection, provision production secrets/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.
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { SshConnection } from './ssh-connection'
|
||||
|
||||
const execCommandMock = vi.hoisted(() => vi.fn())
|
||||
|
||||
vi.mock('./ssh-relay-deploy-helpers', () => ({
|
||||
execCommand: execCommandMock
|
||||
}))
|
||||
|
||||
const { detectSshRelayDarwinProcessTranslation } =
|
||||
await import('./ssh-relay-darwin-translation-detection')
|
||||
|
||||
const conn = {} as SshConnection
|
||||
const marker = '__ORCA_SSH_RELAY_DARWIN_TRANSLATION__'
|
||||
|
||||
function segment(line: string): string {
|
||||
return [`${marker} BEGIN`, line, `${marker} END`].join('\n')
|
||||
}
|
||||
|
||||
describe('detectSshRelayDarwinProcessTranslation', () => {
|
||||
beforeEach(() => {
|
||||
execCommandMock.mockReset()
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ line: 'translated=1 arm64=1', expected: true, label: 'translated Apple Silicon' },
|
||||
{ line: 'translated=1 arm64=', expected: true, label: 'authoritative translated signal' },
|
||||
{ line: 'translated=0 arm64=1', expected: false, label: 'native Apple Silicon' },
|
||||
{ line: 'translated=0 arm64=0', expected: false, label: 'explicit native Intel' },
|
||||
{ line: 'translated=0 arm64=', expected: false, label: 'authoritative native signal' },
|
||||
{ line: 'translated= arm64=0', expected: false, label: 'Intel without the translation key' }
|
||||
])('returns $expected for $label', async ({ line, expected }) => {
|
||||
execCommandMock.mockResolvedValueOnce(
|
||||
['Last login: ignored', segment(line), 'post-command noise'].join('\n')
|
||||
)
|
||||
|
||||
await expect(detectSshRelayDarwinProcessTranslation(conn)).resolves.toBe(expected)
|
||||
})
|
||||
|
||||
it.each([
|
||||
['', 'empty output'],
|
||||
[segment('translated= arm64=1'), 'missing translation on arm64 hardware'],
|
||||
[segment('translated= arm64='), 'missing both values'],
|
||||
[segment('translated=1 arm64=0'), 'conflicting translated Intel evidence'],
|
||||
[segment('translated=2 arm64=1'), 'invalid translation value'],
|
||||
[segment('translated=1 arm64=2'), 'invalid hardware value'],
|
||||
[segment('translated=1 arm64=1 extra=1'), 'extra field'],
|
||||
[segment('translated=1 arm64=1'), 'non-canonical whitespace'],
|
||||
[segment('translated=1\narm64=1'), 'multiple marked lines'],
|
||||
[`${marker} BEGIN\ntranslated=1 arm64=1`, 'unterminated segment'],
|
||||
[segment(`translated=${'1'.repeat(65)} arm64=1`), 'oversized value'],
|
||||
[[segment('translated=1 arm64=1'), segment('translated=1 arm64=1')].join('\n'), 'duplicate'],
|
||||
[
|
||||
`startup${marker} BEGIN\ntranslated=1 arm64=1\n${marker} END`,
|
||||
'marker concatenated to startup noise'
|
||||
],
|
||||
['translated=1 arm64=1', 'unmarked evidence']
|
||||
])('returns unknown for %s', async (output) => {
|
||||
execCommandMock.mockResolvedValueOnce(output)
|
||||
|
||||
await expect(detectSshRelayDarwinProcessTranslation(conn)).resolves.toBeUndefined()
|
||||
})
|
||||
|
||||
it('classifies an unavailable probe as unknown without inventing composition policy', async () => {
|
||||
execCommandMock.mockRejectedValueOnce(new Error('remote command unavailable'))
|
||||
|
||||
await expect(detectSshRelayDarwinProcessTranslation(conn)).resolves.toBeUndefined()
|
||||
})
|
||||
|
||||
it('propagates cancellation and passes the signal into the single bounded probe', async () => {
|
||||
const signal = new AbortController().signal
|
||||
const abortError = Object.assign(new Error('cancelled'), { name: 'AbortError' })
|
||||
execCommandMock.mockRejectedValueOnce(abortError)
|
||||
|
||||
await expect(detectSshRelayDarwinProcessTranslation(conn, { signal })).rejects.toBe(abortError)
|
||||
expect(execCommandMock).toHaveBeenCalledTimes(1)
|
||||
expect(execCommandMock).toHaveBeenCalledWith(conn, expect.any(String), {
|
||||
signal,
|
||||
timeoutMs: 15_000
|
||||
})
|
||||
})
|
||||
|
||||
it('constructs one marked POSIX-shell sysctl probe without runtime dependencies', async () => {
|
||||
execCommandMock.mockResolvedValueOnce(segment('translated=0 arm64=1'))
|
||||
|
||||
await detectSshRelayDarwinProcessTranslation(conn)
|
||||
|
||||
const command = execCommandMock.mock.calls[0]?.[1] ?? ''
|
||||
expect(command).toContain('command -v sysctl')
|
||||
expect(command).toContain('sysctl -in sysctl.proc_translated')
|
||||
expect(command).toContain('sysctl -in hw.optional.arm64')
|
||||
expect(command).toContain(`${marker} BEGIN`)
|
||||
expect(command).toContain(`${marker} END`)
|
||||
expect(command).not.toMatch(/\b(?:node|npm|python|perl|tar|sha256sum|shasum)\b/u)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,111 @@
|
||||
import {
|
||||
getProcessOutputFields,
|
||||
iterateProcessOutputLines
|
||||
} from '../../shared/process-output-field-scanner'
|
||||
import type { SshConnection } from './ssh-connection'
|
||||
import { execCommand } from './ssh-relay-deploy-helpers'
|
||||
|
||||
const PROBE_MARKER = '__ORCA_SSH_RELAY_DARWIN_TRANSLATION__'
|
||||
const PROBE_TIMEOUT_MS = 15_000
|
||||
const EVIDENCE_MAX_CHARS = 64
|
||||
|
||||
const PROBE_COMMAND = [
|
||||
`printf '\n%s\n' '${PROBE_MARKER} BEGIN'`,
|
||||
'if command -v sysctl >/dev/null 2>&1; then',
|
||||
' translated=$(sysctl -in sysctl.proc_translated 2>/dev/null) || translated=',
|
||||
' arm64=$(sysctl -in hw.optional.arm64 2>/dev/null) || arm64=',
|
||||
` printf 'translated=%s arm64=%s\n' "$translated" "$arm64"`,
|
||||
'fi',
|
||||
`printf '%s\n' '${PROBE_MARKER} END'`
|
||||
].join('\n')
|
||||
|
||||
function parseMarker(line: string): 'BEGIN' | 'END' | null {
|
||||
const fields = getProcessOutputFields(line, 3)
|
||||
if (fields.length !== 2 || fields[0] !== PROBE_MARKER) {
|
||||
return null
|
||||
}
|
||||
return fields[1] === 'BEGIN' || fields[1] === 'END' ? fields[1] : null
|
||||
}
|
||||
|
||||
function classifyTranslation(line: string | undefined): boolean | undefined {
|
||||
if (!line || line.length > EVIDENCE_MAX_CHARS) {
|
||||
return undefined
|
||||
}
|
||||
const match = /^translated=(0|1)? arm64=(0|1)?$/.exec(line)
|
||||
if (!match) {
|
||||
return undefined
|
||||
}
|
||||
const translated = match[1]
|
||||
const arm64 = match[2]
|
||||
if (translated === '1') {
|
||||
return arm64 === '0' ? undefined : true
|
||||
}
|
||||
if (translated === '0') {
|
||||
return false
|
||||
}
|
||||
return arm64 === '0' ? false : undefined
|
||||
}
|
||||
|
||||
function parseTranslation(output: string): boolean | undefined {
|
||||
let activeLines: string[] | null = null
|
||||
let evidenceLine: string | undefined
|
||||
let complete = false
|
||||
let invalid = false
|
||||
|
||||
for (const line of iterateProcessOutputLines(output)) {
|
||||
const marker = parseMarker(line)
|
||||
if (marker === 'BEGIN') {
|
||||
if (activeLines || complete) {
|
||||
invalid = true
|
||||
}
|
||||
activeLines = []
|
||||
continue
|
||||
}
|
||||
if (marker === 'END') {
|
||||
if (!activeLines || activeLines.length !== 1 || complete) {
|
||||
invalid = true
|
||||
activeLines = null
|
||||
continue
|
||||
}
|
||||
evidenceLine = activeLines[0]
|
||||
complete = true
|
||||
activeLines = null
|
||||
continue
|
||||
}
|
||||
if (activeLines) {
|
||||
if (activeLines.length === 1) {
|
||||
invalid = true
|
||||
} else {
|
||||
activeLines.push(line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (invalid || activeLines || !complete) {
|
||||
return undefined
|
||||
}
|
||||
return classifyTranslation(evidenceLine)
|
||||
}
|
||||
|
||||
function isAbortError(error: unknown): boolean {
|
||||
return error instanceof Error && error.name === 'AbortError'
|
||||
}
|
||||
|
||||
export async function detectSshRelayDarwinProcessTranslation(
|
||||
conn: SshConnection,
|
||||
{ signal }: { signal?: AbortSignal } = {}
|
||||
): Promise<boolean | undefined> {
|
||||
try {
|
||||
const output = await execCommand(conn, PROBE_COMMAND, {
|
||||
signal,
|
||||
timeoutMs: PROBE_TIMEOUT_MS
|
||||
})
|
||||
return parseTranslation(output)
|
||||
} catch (error) {
|
||||
// Why: cancellation must settle the caller's work instead of becoming compatibility evidence.
|
||||
if (isAbortError(error)) {
|
||||
throw error
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user