{"version":3,"file":"ssh-transport.d.ts","sourceRoot":"","sources":["../../../src/core/remote-execution/ssh-transport.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Remote Execution — SSH transport (2.14.0).\n *\n * The first real transport. It runs remote commands via non-interactive\n * OpenSSH (`BatchMode=yes`, bounded `ConnectTimeout`, no password prompts) and\n * uses PowerShell `-EncodedCommand` (UTF-16LE base64) for Windows command\n * payloads so no shell interpolation of untrusted data ever occurs.\n *\n * Materialisation + launch happen in a single SSH process: a PowerShell\n * preamble reads a JSON payload from stdin, writes base64-decoded files into an\n * execution-scoped temp directory (guarded against duplicate launch), then\n * execs the remote protocol runner. The runner's JSONL frames stream back over\n * the same channel.\n */\n\nimport { spawn } from \"node:child_process\";\nimport type { RemoteExecutionErrorCode } from \"./remote-execution-error.js\";\nimport { RemoteExecutionError } from \"./remote-execution-error.js\";\nimport { parseRemoteFrame, REMOTE_PROTOCOL_VERSION, type RemoteProtocolFrameType } from \"./remote-protocol.js\";\nimport { REMOTE_RUNNER_SOURCE } from \"./remote-runner-source.js\";\nimport type { RemoteExecutionTarget, RemoteTargetHealth } from \"./remote-target-types.js\";\nimport type {\n\tRemoteCommandRunner,\n\tRemoteExecutionHandle,\n\tRemoteExecutionTransport,\n\tRemoteLaunchSpec,\n\tRemoteTransportOutcome,\n} from \"./remote-transport.js\";\n\n// =============================================================================\n// Bounded ssh command runner (injectable for tests)\n// =============================================================================\n\nexport interface SshCommandResult {\n\texitCode: number | null;\n\tsignal?: string;\n\tstdout: string;\n\tstderr: string;\n\ttimedOut?: boolean;\n\tlaunchError?: string;\n}\n\nexport type SshCommandRunner = (\n\ttarget: RemoteExecutionTarget,\n\targs: readonly string[],\n\tinput: string | Buffer | undefined,\n\toptions: { timeoutMs?: number; signal?: AbortSignal; sshOptions?: readonly string[] },\n) => Promise<SshCommandResult>;\n\nconst DEFAULT_CONNECT_TIMEOUT_MS = 10_000;\nconst DEFAULT_LAUNCH_TIMEOUT_MS = 30_000;\nconst DEFAULT_HEARTBEAT_TIMEOUT_MS = 120_000;\nconst DEFAULT_EXECUTION_TIMEOUT_MS = 600_000;\n\nfunction defaultSshCommandRunner(\n\ttarget: RemoteExecutionTarget,\n\targs: readonly string[],\n\tinput: string | Buffer | undefined,\n\toptions: { timeoutMs?: number; signal?: AbortSignal; sshOptions?: readonly string[] },\n): Promise<SshCommandResult> {\n\tconst connectTimeoutMs = target.connection?.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;\n\tconst commandArgs = [\n\t\t\"-T\",\n\t\t\"-o\",\n\t\t\"BatchMode=yes\",\n\t\t\"-o\",\n\t\t`ConnectTimeout=${Math.max(1, Math.floor(connectTimeoutMs / 1000))}`,\n\t\t...(options.sshOptions ?? []),\n\t\t`${target.user}@${target.host}`,\n\t\t...args,\n\t];\n\n\treturn new Promise<SshCommandResult>((resolve) => {\n\t\tconst child = spawn(\"ssh\", commandArgs, {\n\t\t\tshell: false,\n\t\t\tstdio: [\"pipe\", \"pipe\", \"pipe\"],\n\t\t\tenv: { ...process.env },\n\t\t});\n\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\t\tlet launchError: string | undefined;\n\t\tlet timedOut = false;\n\t\tlet settled = false;\n\t\tlet forceTimer: NodeJS.Timeout | undefined;\n\n\t\tconst finish = (result: SshCommandResult) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timeout);\n\t\t\tif (forceTimer) clearTimeout(forceTimer);\n\t\t\tresolve(result);\n\t\t};\n\n\t\tconst timeout = setTimeout(() => {\n\t\t\ttimedOut = true;\n\t\t\ttry {\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t} catch {\n\t\t\t\t// already gone\n\t\t\t}\n\t\t\tforceTimer = setTimeout(() => {\n\t\t\t\tif (child.exitCode === null && child.signalCode === null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tchild.kill(\"SIGKILL\");\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// already gone\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, 5000);\n\t\t}, options.timeoutMs ?? DEFAULT_EXECUTION_TIMEOUT_MS);\n\n\t\tif (options.signal) {\n\t\t\tif (options.signal.aborted) {\n\t\t\t\ttry {\n\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t} catch {\n\t\t\t\t\t// already gone\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\toptions.signal.addEventListener(\n\t\t\t\t\t\"abort\",\n\t\t\t\t\t() => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// already gone\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t{ once: true },\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tchild.stdout.on(\"data\", (data: Buffer) => {\n\t\t\tstdout += data.toString();\n\t\t});\n\t\tchild.stderr.on(\"data\", (data: Buffer) => {\n\t\t\tstderr += data.toString();\n\t\t});\n\t\tchild.on(\"error\", (error) => {\n\t\t\tlaunchError = error.message;\n\t\t});\n\t\tchild.on(\"close\", (code, signal) => {\n\t\t\tif (options.signal) options.signal.removeEventListener(\"abort\", () => undefined);\n\t\t\tfinish({\n\t\t\t\texitCode: code,\n\t\t\t\tsignal: signal ?? undefined,\n\t\t\t\tstdout,\n\t\t\t\tstderr,\n\t\t\t\ttimedOut,\n\t\t\t\tlaunchError,\n\t\t\t});\n\t\t});\n\n\t\tif (input !== undefined) {\n\t\t\tchild.stdin.on(\"error\", () => undefined);\n\t\t\tchild.stdin.end(input);\n\t\t} else {\n\t\t\tchild.stdin.end();\n\t\t}\n\t});\n}\n\n// =============================================================================\n// PowerShell encoding\n// =============================================================================\n\n/** Encode a PowerShell script as UTF-16LE base64 for `-EncodedCommand`. */\nexport function encodePowerShellCommand(script: string): string {\n\tconst utf16le = Buffer.from(script, \"utf16le\");\n\treturn utf16le.toString(\"base64\");\n}\n\n/** Build a PowerShell single-quoted literal (escapes embedded single quotes). */\nexport function psLiteral(value: string): string {\n\treturn `'${value.replace(/'/g, \"''\")}'`;\n}\n\n/** Build the ssh argv for running a PowerShell encoded command. */\nexport function sshPowerShellArgs(encoded: string): string[] {\n\treturn [\"powershell.exe\", \"-NoProfile\", \"-NonInteractive\", \"-EncodedCommand\", encoded];\n}\n\n// =============================================================================\n// Materialisation payload\n// =============================================================================\n\ninterface MaterializeFile {\n\tpath: string;\n\tcontentB64: string;\n}\n\ninterface MaterializePayload {\n\tworkspaceDir: string;\n\trunnerPath: string;\n\tenvelopePath: string;\n\tfiles: MaterializeFile[];\n}\n\n/** PowerShell preamble that reads stdin JSON, writes files, then execs the runner. */\nconst MATERIALIZE_PREAMBLE = [\n\t\"$ErrorActionPreference = 'Stop'\",\n\t\"$ProgressPreference = 'SilentlyContinue'\",\n\t\"[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()\",\n\t\"$payload = [Console]::In.ReadToEnd()\",\n\t\"$data = $payload | ConvertFrom-Json\",\n\t\"if (Test-Path -LiteralPath $data.workspaceDir) {\",\n\t\"  Write-Output 'JENSEN_REMOTE_DUPLICATE_LAUNCH'\",\n\t\"  exit 70\",\n\t\"}\",\n\t\"New-Item -ItemType Directory -Path $data.workspaceDir -Force | Out-Null\",\n\t\"foreach ($f in $data.files) {\",\n\t\"  $dir = Split-Path -Parent $f.path\",\n\t\"  if (!(Test-Path -LiteralPath $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }\",\n\t\"  [IO.File]::WriteAllText($f.path, [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($f.contentB64)))\",\n\t\"}\",\n\t\"& node $data.runnerPath $data.envelopePath\",\n\t\"exit $LASTEXITCODE\",\n].join(\"\\r\\n\");\n\nconst DUPLICATE_LAUNCH_MARKER = \"JENSEN_REMOTE_DUPLICATE_LAUNCH\";\n\n// =============================================================================\n// Transport\n// =============================================================================\n\nexport interface SshRemoteExecutionTransportOptions {\n\t/** Injectable command runner (tests). Defaults to real OpenSSH spawn. */\n\trunner?: SshCommandRunner;\n\tlaunchTimeoutMs?: number;\n\theartbeatTimeoutMs?: number;\n\texecutionTimeoutMs?: number;\n}\n\nexport class SshRemoteExecutionTransport implements RemoteExecutionTransport, RemoteCommandRunner {\n\treadonly transportId = \"ssh\";\n\tprivate readonly _runner: SshCommandRunner;\n\tprivate readonly _launchTimeoutMs: number;\n\tprivate readonly _heartbeatTimeoutMs: number;\n\tprivate readonly _executionTimeoutMs: number;\n\n\tconstructor(options: SshRemoteExecutionTransportOptions = {}) {\n\t\tthis._runner = options.runner ?? defaultSshCommandRunner;\n\t\tthis._launchTimeoutMs = options.launchTimeoutMs ?? DEFAULT_LAUNCH_TIMEOUT_MS;\n\t\tthis._heartbeatTimeoutMs = options.heartbeatTimeoutMs ?? DEFAULT_HEARTBEAT_TIMEOUT_MS;\n\t\tthis._executionTimeoutMs = options.executionTimeoutMs ?? DEFAULT_EXECUTION_TIMEOUT_MS;\n\t}\n\n\tasync probe(target: RemoteExecutionTarget): Promise<RemoteTargetHealth> {\n\t\tconst commandTimeoutMs = target.connection?.commandTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;\n\t\tconst script = [\n\t\t\t\"$ErrorActionPreference = 'Stop'\",\n\t\t\t\"[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()\",\n\t\t\t\"$node = (Get-Command node -ErrorAction SilentlyContinue).Source\",\n\t\t\t\"$arch = if ([Environment]::Is64BitOperatingSystem) { 'x64' } else { 'x86' }\",\n\t\t\t\"$h = @{ host = [Environment]::MachineName; user = [Environment]::UserName; platform = [Environment]::OSVersion.VersionString; arch = $arch; node = $node }\",\n\t\t\t\"$h | ConvertTo-Json -Compress\",\n\t\t].join(\"\\r\\n\");\n\n\t\tconst result = await this._runner(target, sshPowerShellArgs(encodePowerShellCommand(script)), undefined, {\n\t\t\ttimeoutMs: commandTimeoutMs,\n\t\t});\n\n\t\tconst observedAtMs = Date.now();\n\t\tif (result.launchError) {\n\t\t\treturn {\n\t\t\t\ttargetId: target.targetId,\n\t\t\t\tstatus: \"unreachable\",\n\t\t\t\terrorCode: \"REMOTE_TARGET_UNAVAILABLE\",\n\t\t\t\tsummary: result.launchError,\n\t\t\t\tobservedAtMs,\n\t\t\t};\n\t\t}\n\t\tif (result.timedOut) {\n\t\t\treturn {\n\t\t\t\ttargetId: target.targetId,\n\t\t\t\tstatus: \"timeout\",\n\t\t\t\terrorCode: \"REMOTE_CONNECT_TIMEOUT\",\n\t\t\t\tsummary: \"remote probe timed out\",\n\t\t\t\tobservedAtMs,\n\t\t\t};\n\t\t}\n\t\tif (result.exitCode !== 0) {\n\t\t\tconst status = /permission denied|auth|publickey|authentication/i.test(result.stderr)\n\t\t\t\t? \"auth_failed\"\n\t\t\t\t: result.stderr.includes(\"Could not resolve\") || result.stderr.includes(\"Connection timed out\")\n\t\t\t\t\t? \"timeout\"\n\t\t\t\t\t: \"unreachable\";\n\t\t\treturn {\n\t\t\t\ttargetId: target.targetId,\n\t\t\t\tstatus,\n\t\t\t\terrorCode:\n\t\t\t\t\tstatus === \"auth_failed\"\n\t\t\t\t\t\t? \"REMOTE_AUTH_FAILED\"\n\t\t\t\t\t\t: status === \"timeout\"\n\t\t\t\t\t\t\t? \"REMOTE_CONNECT_TIMEOUT\"\n\t\t\t\t\t\t\t: \"REMOTE_TARGET_UNAVAILABLE\",\n\t\t\t\tsummary: (result.stderr || result.stdout).trim().slice(0, 400),\n\t\t\t\tobservedAtMs,\n\t\t\t};\n\t\t}\n\n\t\tlet identity: Record<string, unknown> | undefined;\n\t\ttry {\n\t\t\tidentity = JSON.parse(result.stdout.trim().split(\"\\n\").pop() ?? \"{}\");\n\t\t} catch {\n\t\t\tidentity = undefined;\n\t\t}\n\n\t\tconst platform = String(identity?.platform ?? \"\");\n\t\tconst nodeRuntime = identity?.node ? String(identity.node) : undefined;\n\t\tconst status: RemoteTargetHealth[\"status\"] = nodeRuntime ? \"reachable\" : \"runtime_unavailable\";\n\t\treturn {\n\t\t\ttargetId: target.targetId,\n\t\t\tstatus,\n\t\t\terrorCode: status === \"reachable\" ? undefined : \"REMOTE_RUNTIME_UNAVAILABLE\",\n\t\t\tsummary: status === \"reachable\" ? `reachable (${platform})` : \"remote Node runtime unavailable\",\n\t\t\tobservedAtMs,\n\t\t\tremoteHost: identity?.host ? String(identity.host) : undefined,\n\t\t\tremoteUser: identity?.user ? String(identity.user) : undefined,\n\t\t\tremotePlatform: platform,\n\t\t\tremoteRuntime: nodeRuntime,\n\t\t};\n\t}\n\n\t/**\n\t * Run a single bounded remote command with an explicit remote working\n\t * directory. The command + cwd are passed via stdin JSON (never shell\n\t * interpolation) to a fixed PowerShell preamble that sets location and runs\n\t * `cmd.exe /d /s /c`. Used for probe/verification operations.\n\t */\n\tasync runCommand(\n\t\ttarget: RemoteExecutionTarget,\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: { timeoutMs?: number; signal?: AbortSignal } = {},\n\t): Promise<{ exitCode: number | null; stdout: string; stderr: string; timedOut?: boolean; launchError?: string }> {\n\t\tconst script = [\n\t\t\t\"$ErrorActionPreference = 'Stop'\",\n\t\t\t\"$ProgressPreference = 'SilentlyContinue'\",\n\t\t\t\"[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()\",\n\t\t\t\"$payload = [Console]::In.ReadToEnd()\",\n\t\t\t\"$data = $payload | ConvertFrom-Json\",\n\t\t\t\"Set-Location -LiteralPath $data.cwd\",\n\t\t\t\"& cmd.exe /d /s /c $data.command\",\n\t\t\t\"exit $LASTEXITCODE\",\n\t\t].join(\"\\r\\n\");\n\n\t\tconst input = JSON.stringify({ cwd, command });\n\t\tconst result = await this._runner(target, sshPowerShellArgs(encodePowerShellCommand(script)), input, {\n\t\t\ttimeoutMs: options.timeoutMs ?? this._executionTimeoutMs,\n\t\t\tsignal: options.signal,\n\t\t});\n\t\treturn {\n\t\t\texitCode: result.exitCode,\n\t\t\tstdout: result.stdout,\n\t\t\tstderr: result.stderr,\n\t\t\ttimedOut: result.timedOut,\n\t\t\tlaunchError: result.launchError,\n\t\t};\n\t}\n\n\t/**\n\t * Run an arbitrary encoded PowerShell script with no stdin. Used for\n\t * runtime-sync operations (hash, directory checks, atomic swap) that are not\n\t * mission child launches.\n\t */\n\tasync runPowerShell(\n\t\ttarget: RemoteExecutionTarget,\n\t\tscript: string,\n\t\toptions: { timeoutMs?: number; signal?: AbortSignal } = {},\n\t): Promise<{ exitCode: number | null; stdout: string; stderr: string; timedOut?: boolean; launchError?: string }> {\n\t\tconst result = await this._runner(\n\t\t\ttarget,\n\t\t\tsshPowerShellArgs(encodePowerShellCommand(`$ProgressPreference = 'SilentlyContinue'\\r\\n${script}`)),\n\t\t\tundefined,\n\t\t\t{\n\t\t\t\ttimeoutMs: options.timeoutMs ?? this._executionTimeoutMs,\n\t\t\t\tsignal: options.signal,\n\t\t\t},\n\t\t);\n\t\treturn {\n\t\t\texitCode: result.exitCode,\n\t\t\tstdout: result.stdout,\n\t\t\tstderr: result.stderr,\n\t\t\ttimedOut: result.timedOut,\n\t\t\tlaunchError: result.launchError,\n\t\t};\n\t}\n\n\t/**\n\t * Save binary bytes to a remote file. Reads raw stdin (never JSON), so the\n\t * payload is byte-exact; used to transfer the runtime bundle tarball.\n\t */\n\tasync saveFile(\n\t\ttarget: RemoteExecutionTarget,\n\t\tremotePath: string,\n\t\tbytes: Buffer,\n\t\toptions: { timeoutMs?: number } = {},\n\t): Promise<{ exitCode: number | null; stdout: string; stderr: string; launchError?: string }> {\n\t\tconst script = [\n\t\t\t\"$ErrorActionPreference = 'Stop'\",\n\t\t\t\"$ProgressPreference = 'SilentlyContinue'\",\n\t\t\t\"[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()\",\n\t\t\t`$path = ${psLiteral(remotePath)}`,\n\t\t\t\"$dir = Split-Path -Parent $path\",\n\t\t\t\"if (!(Test-Path -LiteralPath $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }\",\n\t\t\t\"$in = [Console]::OpenStandardInput()\",\n\t\t\t\"$out = [System.IO.File]::Create($path)\",\n\t\t\t\"try { $in.CopyTo($out) } finally { $out.Close() }\",\n\t\t\t\"Write-Output 'JENSEN_FILE_SAVED'\",\n\t\t].join(\"\\r\\n\");\n\n\t\tconst result = await this._runner(target, sshPowerShellArgs(encodePowerShellCommand(script)), bytes, {\n\t\t\ttimeoutMs: options.timeoutMs ?? this._executionTimeoutMs,\n\t\t});\n\t\treturn {\n\t\t\texitCode: result.exitCode,\n\t\t\tstdout: result.stdout,\n\t\t\tstderr: result.stderr,\n\t\t\tlaunchError: result.launchError,\n\t\t};\n\t}\n\n\t/** Compute the SHA-256 of a remote file (hex). Throws on failure. */\n\tasync computeFileSha256(target: RemoteExecutionTarget, remotePath: string): Promise<string> {\n\t\tconst result = await this.runPowerShell(\n\t\t\ttarget,\n\t\t\t[\n\t\t\t\t\"$ErrorActionPreference = 'Stop'\",\n\t\t\t\t\"[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()\",\n\t\t\t\t`(Get-FileHash -Algorithm SHA256 -LiteralPath ${psLiteral(remotePath)}).Hash.ToLowerInvariant()`,\n\t\t\t].join(\"\\r\\n\"),\n\t\t\t{ timeoutMs: 120_000 },\n\t\t);\n\t\tif (result.exitCode !== 0) {\n\t\t\tthrow new Error(`remote hash failed: ${result.stderr || result.launchError || \"unknown\"}`);\n\t\t}\n\t\tconst hash = result.stdout.trim().split(/\\r?\\n/).pop()?.trim() ?? \"\";\n\t\tif (!/^[0-9a-f]{64}$/u.test(hash)) {\n\t\t\tthrow new Error(`remote hash returned unexpected output: ${result.stdout.slice(0, 200)}`);\n\t\t}\n\t\treturn hash;\n\t}\n\n\t/**\n\t * Stream-extract a gzipped tarball on the target via `tar -xzf -` (binary\n\t * stdin). This is the proven reliable path for large binary transfers; tar's\n\t * gzip CRC32 + tar checksums provide integrity, and the caller verifies the\n\t * sidecar manifest identity afterwards.\n\t */\n\tasync extractTarballFromStdin(\n\t\ttarget: RemoteExecutionTarget,\n\t\tdestDir: string,\n\t\tbytes: Buffer,\n\t\toptions: { timeoutMs?: number } = {},\n\t): Promise<{ exitCode: number | null; stdout: string; stderr: string; launchError?: string }> {\n\t\tconst args = [\"cmd.exe\", \"/d\", \"/s\", \"/c\", `tar -xzf - -C ${destDir.replace(/\\\\/g, \"/\")}`];\n\t\tconst result = await this._runner(target, args, bytes, { timeoutMs: options.timeoutMs ?? 600_000 });\n\t\treturn {\n\t\t\texitCode: result.exitCode,\n\t\t\tstdout: result.stdout,\n\t\t\tstderr: result.stderr,\n\t\t\tlaunchError: result.launchError,\n\t\t};\n\t}\n\n\tasync launch(\n\t\ttarget: RemoteExecutionTarget,\n\t\tspec: RemoteLaunchSpec,\n\t\toptions: { signal?: AbortSignal } = {},\n\t): Promise<RemoteExecutionHandle> {\n\t\tconst workspaceDir = spec.workspaceDir;\n\t\tconst runnerPath = `${workspaceDir}\\\\remote-runner.js`;\n\t\tconst envelopePath = `${workspaceDir}\\\\envelope.json`;\n\n\t\t// Build the runner envelope (materialised as a file, not re-serialised by\n\t\t// PowerShell, so its JSON is byte-exact).\n\t\tconst envelope = {\n\t\t\tprotocolVersion: REMOTE_PROTOCOL_VERSION,\n\t\t\texecutionId: spec.executionId,\n\t\t\tlaunchId: spec.launchId,\n\t\t\tremoteTargetId: spec.remoteTargetId,\n\t\t\tfencing: spec.fencing,\n\t\t\theartbeatMs: spec.heartbeatMs ?? 3000,\n\t\t\tlaunch: {\n\t\t\t\tcommand: spec.launch.command,\n\t\t\t\targs: spec.launch.args,\n\t\t\t\tcwd: spec.launch.cwd,\n\t\t\t\tenv: spec.launch.env,\n\t\t\t},\n\t\t\tevidenceFiles: spec.evidenceFiles ?? [],\n\t\t};\n\n\t\tconst files: MaterializeFile[] = [\n\t\t\t{ path: runnerPath, contentB64: Buffer.from(REMOTE_RUNNER_SOURCE, \"utf8\").toString(\"base64\") },\n\t\t\t{ path: envelopePath, contentB64: Buffer.from(JSON.stringify(envelope), \"utf8\").toString(\"base64\") },\n\t\t];\n\t\tif (spec.modelsJson) {\n\t\t\tfiles.push({\n\t\t\t\tpath: `${spec.agentDir}\\\\models.json`,\n\t\t\t\tcontentB64: Buffer.from(spec.modelsJson, \"utf8\").toString(\"base64\"),\n\t\t\t});\n\t\t}\n\t\tif (spec.childSessionId && spec.sessionFileContent !== undefined) {\n\t\t\tfiles.push({\n\t\t\t\tpath: `${spec.agentDir}\\\\child-sessions\\\\${spec.childSessionId}.jsonl`,\n\t\t\t\tcontentB64: Buffer.from(spec.sessionFileContent, \"utf8\").toString(\"base64\"),\n\t\t\t});\n\t\t}\n\t\tfor (const file of spec.workspaceFiles ?? []) {\n\t\t\tconst relative = file.path.replace(/\\//g, \"\\\\\");\n\t\t\tfiles.push({\n\t\t\t\tpath: `${workspaceDir}\\\\${relative}`,\n\t\t\t\tcontentB64: file.contentB64,\n\t\t\t});\n\t\t}\n\n\t\tconst payload: MaterializePayload = { workspaceDir, runnerPath, envelopePath, files };\n\t\tconst preamble = MATERIALIZE_PREAMBLE;\n\t\tconst encoded = encodePowerShellCommand(preamble);\n\t\tconst input = JSON.stringify(payload);\n\n\t\tconst controller = new AbortController();\n\t\tconst abortListener = () => controller.abort();\n\t\tif (options.signal) {\n\t\t\tif (options.signal.aborted) controller.abort();\n\t\t\telse options.signal.addEventListener(\"abort\", abortListener, { once: true });\n\t\t}\n\n\t\tconst start = Date.now();\n\t\tspec.callbacks?.onEvent?.({\n\t\t\teventId: `${spec.executionId}:${spec.launchId}:launch_started`,\n\t\t\ttype: \"connected\",\n\t\t\tatMs: start,\n\t\t\tpayload: { correlation: spec.correlation },\n\t\t});\n\t\tconst sshOptions = spec.admissionTunnel\n\t\t\t? [\"-R\", `${spec.admissionTunnel.remotePort}:127.0.0.1:${spec.admissionTunnel.localPort}`]\n\t\t\t: undefined;\n\n\t\tconst sshPromise = this._runner(target, sshPowerShellArgs(encoded), input, {\n\t\t\ttimeoutMs: spec.timeoutMs ?? this._executionTimeoutMs,\n\t\t\tsignal: controller.signal,\n\t\t\tsshOptions,\n\t\t});\n\n\t\tconst outcomePromise = this._consume(spec, sshPromise, start);\n\n\t\treturn {\n\t\t\texecutionId: spec.executionId,\n\t\t\tlaunchId: spec.launchId,\n\t\t\toutcomePromise,\n\t\t\tcancel: async (reason?: string) => {\n\t\t\t\tcontroller.abort(reason);\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate async _consume(\n\t\tspec: RemoteLaunchSpec,\n\t\tsshPromise: Promise<SshCommandResult>,\n\t\tstart: number,\n\t): Promise<RemoteTransportOutcome> {\n\t\tlet started = false;\n\t\tlet lastFrameAtMs = start;\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\t\tlet exitCode: number | null = null;\n\t\tlet signal: string | undefined;\n\t\tlet cancelled = false;\n\t\tconst timedOut = false;\n\t\tlet launchError: string | undefined;\n\t\tlet errorCode: string | undefined;\n\t\tlet location: RemoteTransportOutcome[\"location\"];\n\t\tlet evidence: unknown[] | undefined;\n\t\tlet remoteResult: { success: boolean; summary?: string } | undefined;\n\n\t\tconst launchTimeout = setTimeout(() => {\n\t\t\t// Launch acknowledgement never arrived.\n\t\t\tif (!started) {\n\t\t\t\terrorCode = \"REMOTE_LAUNCH_FAILED\";\n\t\t\t}\n\t\t}, this._launchTimeoutMs);\n\n\t\tconst heartbeatTimeout = setInterval(\n\t\t\t() => {\n\t\t\t\tif (started && Date.now() - lastFrameAtMs > this._heartbeatTimeoutMs) {\n\t\t\t\t\terrorCode = errorCode ?? \"REMOTE_HEARTBEAT_TIMEOUT\";\n\t\t\t\t}\n\t\t\t},\n\t\t\tMath.min(this._heartbeatTimeoutMs / 2, 5000),\n\t\t);\n\n\t\tconst ssh = await sshPromise;\n\t\tclearTimeout(launchTimeout);\n\t\tclearInterval(heartbeatTimeout);\n\n\t\t// Parse the raw ssh stdout for protocol frames. ssh may carry no frames if\n\t\t// materialisation failed (e.g. duplicate launch), which maps to a\n\t\t// structured launch error below.\n\t\tconst lines = ssh.stdout.split(\"\\n\");\n\t\tconst seenTypes = new Set<string>();\n\t\tfor (const line of lines) {\n\t\t\tconst frame = parseRemoteFrame(line);\n\t\t\tif (!frame) continue;\n\t\t\tseenTypes.add(frame.type);\n\t\t\tlastFrameAtMs = Date.now();\n\t\t\tspec.callbacks?.onEvent?.({\n\t\t\t\teventId: `${spec.executionId}:${spec.launchId}:frame:${frame.type}`,\n\t\t\t\ttype: \"frame\",\n\t\t\t\tatMs: lastFrameAtMs,\n\t\t\t\tpayload: { correlation: spec.correlation, frame: { type: frame.type, payload: frame.payload } },\n\t\t\t});\n\t\t\tthis._applyFrame(frame.type, frame.payload, {\n\t\t\t\tonStarted: (p) => {\n\t\t\t\t\tstarted = true;\n\t\t\t\t\tlocation = p.location;\n\t\t\t\t},\n\t\t\t\tonStream: (which, chunk) => {\n\t\t\t\t\tif (which === \"stdout\") stdout += chunk;\n\t\t\t\t\telse stderr += chunk;\n\t\t\t\t},\n\t\t\t\tonEvidence: (items) => {\n\t\t\t\t\tevidence = items;\n\t\t\t\t},\n\t\t\t\tonResult: (r) => {\n\t\t\t\t\tremoteResult = r;\n\t\t\t\t},\n\t\t\t\tonExit: (p) => {\n\t\t\t\t\texitCode = p.exitCode;\n\t\t\t\t\tsignal = p.signal;\n\t\t\t\t\tcancelled = p.cancelled ?? false;\n\t\t\t\t},\n\t\t\t\tonError: (p) => {\n\t\t\t\t\terrorCode = errorCode ?? p.code;\n\t\t\t\t},\n\t\t\t});\n\t\t\tspec.callbacks?.onFrame?.({ type: frame.type, payload: frame.payload });\n\t\t}\n\n\t\t// Materialisation/launch failure paths.\n\t\tif (ssh.stdout.includes(DUPLICATE_LAUNCH_MARKER)) {\n\t\t\tthrow new RemoteExecutionError(\"REMOTE_DUPLICATE_LAUNCH\", `Duplicate remote launch for ${spec.executionId}`, {\n\t\t\t\texecutionId: spec.executionId,\n\t\t\t});\n\t\t}\n\t\tif (ssh.launchError) {\n\t\t\tspec.callbacks?.onEvent?.({\n\t\t\t\teventId: `${spec.executionId}:${spec.launchId}:transport_error`,\n\t\t\t\ttype: \"transport_error\",\n\t\t\t\tatMs: Date.now(),\n\t\t\t\tpayload: { correlation: spec.correlation, errorCode: \"REMOTE_TARGET_UNAVAILABLE\" },\n\t\t\t});\n\t\t\tthrow new RemoteExecutionError(\"REMOTE_TARGET_UNAVAILABLE\", ssh.launchError, {\n\t\t\t\texecutionId: spec.executionId,\n\t\t\t});\n\t\t}\n\t\tif (!started && !seenTypes.has(\"REMOTE_STARTED\")) {\n\t\t\tthrow new RemoteExecutionError(\n\t\t\t\tssh.timedOut ? \"REMOTE_CONNECT_TIMEOUT\" : \"REMOTE_LAUNCH_FAILED\",\n\t\t\t\tssh.timedOut ? \"remote launch timed out\" : \"remote launch never acknowledged\",\n\t\t\t\t{ executionId: spec.executionId, stderr: ssh.stderr.slice(0, 1000) },\n\t\t\t);\n\t\t}\n\t\tif (ssh.timedOut && !seenTypes.has(\"REMOTE_EXIT\")) {\n\t\t\tthrow new RemoteExecutionError(\"REMOTE_EXECUTION_LOST\", \"remote execution lost before terminal result\", {\n\t\t\t\texecutionId: spec.executionId,\n\t\t\t});\n\t\t}\n\t\tif (!seenTypes.has(\"REMOTE_EXIT\")) {\n\t\t\t// Transport closed without a terminal result: not success.\n\t\t\tspec.callbacks?.onEvent?.({\n\t\t\t\teventId: `${spec.executionId}:${spec.launchId}:remote_retry:1`,\n\t\t\t\ttype: \"transport_error\",\n\t\t\t\tatMs: Date.now(),\n\t\t\t\tpayload: {\n\t\t\t\t\tcorrelation: spec.correlation,\n\t\t\t\t\tretryClass: \"remote_execution\",\n\t\t\t\t\tretryIndex: 1,\n\t\t\t\t\treason: \"transport closed without terminal result\",\n\t\t\t\t},\n\t\t\t});\n\t\t\tthrow new RemoteExecutionError(\"REMOTE_EXECUTION_LOST\", \"transport closed without terminal result\", {\n\t\t\t\texecutionId: spec.executionId,\n\t\t\t});\n\t\t}\n\n\t\tif (errorCode) {\n\t\t\t// A structured error was observed (heartbeat loss, remote error, ...).\n\t\t\treturn {\n\t\t\t\texitCode: exitCode,\n\t\t\t\tsignal,\n\t\t\t\tcancelled,\n\t\t\t\ttimedOut,\n\t\t\t\tlaunchError,\n\t\t\t\tstdout: stdout.slice(0, 128_000),\n\t\t\t\tstderr: stderr.slice(0, 64_000),\n\t\t\t\tlocation,\n\t\t\t\tevidence,\n\t\t\t\tremoteResult,\n\t\t\t\terrorCode,\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\texitCode,\n\t\t\tsignal,\n\t\t\tcancelled,\n\t\t\ttimedOut,\n\t\t\tlaunchError,\n\t\t\tstdout: stdout.slice(0, 128_000),\n\t\t\tstderr: stderr.slice(0, 64_000),\n\t\t\tlocation,\n\t\t\tevidence,\n\t\t\tremoteResult,\n\t\t\terrorCode,\n\t\t};\n\t}\n\n\tprivate _applyFrame(\n\t\ttype: RemoteProtocolFrameType,\n\t\tpayload: Record<string, unknown>,\n\t\thandlers: {\n\t\t\tonStarted: (p: { location: NonNullable<RemoteTransportOutcome[\"location\"]> }) => void;\n\t\t\tonStream: (which: \"stdout\" | \"stderr\", chunk: string) => void;\n\t\t\tonEvidence: (items: unknown[]) => void;\n\t\t\tonResult: (r: { success: boolean; summary?: string }) => void;\n\t\t\tonExit: (p: { exitCode: number | null; signal?: string; cancelled?: boolean }) => void;\n\t\t\tonError: (p: { code: string }) => void;\n\t\t},\n\t): void {\n\t\tswitch (type) {\n\t\t\tcase \"REMOTE_STARTED\": {\n\t\t\t\tconst loc = payload.location as Record<string, unknown> | undefined;\n\t\t\t\thandlers.onStarted({\n\t\t\t\t\tlocation: {\n\t\t\t\t\t\thost: String(loc?.host ?? \"\"),\n\t\t\t\t\t\tuser: String(loc?.user ?? \"\"),\n\t\t\t\t\t\tcwd: String(loc?.cwd ?? \"\"),\n\t\t\t\t\t\tpid: Number(loc?.pid ?? 0),\n\t\t\t\t\t\tplatform: String(loc?.platform ?? \"\"),\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"REMOTE_STDOUT\":\n\t\t\t\thandlers.onStream(\"stdout\", String(payload.chunk ?? \"\"));\n\t\t\t\tbreak;\n\t\t\tcase \"REMOTE_STDERR\":\n\t\t\t\thandlers.onStream(\"stderr\", String(payload.chunk ?? \"\"));\n\t\t\t\tbreak;\n\t\t\tcase \"REMOTE_EVIDENCE\":\n\t\t\t\thandlers.onEvidence(Array.isArray(payload.items) ? payload.items : []);\n\t\t\t\tbreak;\n\t\t\tcase \"REMOTE_RESULT\":\n\t\t\t\thandlers.onResult({\n\t\t\t\t\tsuccess: payload.success === true,\n\t\t\t\t\tsummary: payload.summary !== undefined ? String(payload.summary) : undefined,\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\tcase \"REMOTE_EXIT\":\n\t\t\t\thandlers.onExit({\n\t\t\t\t\texitCode: typeof payload.exitCode === \"number\" ? payload.exitCode : null,\n\t\t\t\t\tsignal: payload.signal !== undefined ? String(payload.signal) : undefined,\n\t\t\t\t\tcancelled: payload.cancelled === true,\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\tcase \"REMOTE_ERROR\":\n\t\t\t\thandlers.onError({ code: String(payload.code ?? \"REMOTE_PROTOCOL_ERROR\") });\n\t\t\t\tbreak;\n\t\t\tcase \"REMOTE_HEARTBEAT\":\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n\n// =============================================================================\n// Public error-code re-export (kept narrow: only codes the transport surfaces)\n// =============================================================================\n\nexport type { RemoteExecutionErrorCode };\n"]}