import type { DesktopSshEnvironmentBootstrap, DesktopSshEnvironmentTarget,} from "@t3tools/contracts";import * as NetService from "@t3tools/shared/Net";import { extractJsonObject, fromLenientJson } from "@t3tools/shared/schemaJson";import { satisfiesSemverRange } from "@t3tools/shared/semver";import { getUrlDiagnostics } from "@t3tools/shared/urlDiagnostics";import * as Cause from "effect/Cause";import * as Context from "effect/Context";import * as Deferred from "effect/Deferred";import * as Duration from "effect/Duration";import * as Effect from "effect/Effect";import * as Exit from "effect/Exit";import * as FileSystem from "effect/FileSystem";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as Path from "effect/Path";import * as Ref from "effect/Ref";import * as Schedule from "effect/Schedule";import * as Schema from "effect/Schema";import * as Scope from "effect/Scope";import * as Stream from "effect/Stream";import * as HttpClient from "effect/unstable/http/HttpClient";import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";import * as ChildProcess from "effect/unstable/process/ChildProcess";import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner"; import * as SshAuth from "./auth.ts";import { baseSshArgs, buildSshHostSpecEffect, collectProcessOutput, getLastNonEmptyOutputLine, remoteStateKey, resolveSshCommand, resolveSshTarget, runSshCommand, targetConnectionKey, utf8ByteLength,} from "./command.ts";import { SshAuthenticationHelperError, SshCommandCancelledError, SshCommandError, SshHttpBridgeInvalidUrlError, SshHttpBridgeMissingUrlError, SshHttpBridgeNonLoopbackUrlError, SshHttpBridgeError, SshInvalidTargetError, SshLaunchInvalidPortError, SshLaunchOutputParseError, SshLaunchPortMissingError, SshLaunchError, SshPairingCredentialMissingError, SshPairingInvalidCredentialError, SshPairingOutputParseError, SshPairingError, SshPasswordPromptCancelledError, SshPasswordPromptUnavailableError, SshPasswordPromptError, SshReadinessProbeError, SshReadinessProbeTimeoutError, SshReadinessTimeoutError, SshReadinessError, SshTunnelExitError, SshTunnelMonitorError, SshTunnelSpawnError,} from "./errors.ts"; const isSshReadinessError = Schema.is(SshReadinessError); export const DEFAULT_REMOTE_PORT = 3773;const REMOTE_PORT_SCAN_WINDOW = 200;const SSH_READY_TIMEOUT_MS = 20_000;const SSH_READY_PROBE_TIMEOUT_MS = 1_000;const TUNNEL_SHUTDOWN_TIMEOUT_MS = 2_000;const REMOTE_READY_TIMEOUT_MS = 15_000;const REMOTE_REUSE_READY_TIMEOUT_MS = 2_000; export interface RemoteT3RunnerOptions { readonly packageSpec?: string; readonly nodeScriptPath?: string | null; readonly nodeEngineRange?: string | null;} export interface SshEnvironmentManagerOptions { readonly resolveCliPackageSpec?: () => string; readonly resolveCliRunner?: Effect.Effect<RemoteT3RunnerOptions>;} interface SshTunnelEntry { readonly key: string; readonly target: DesktopSshEnvironmentTarget; readonly remotePort: number; readonly remoteServerKind: "external" | "managed" | null; readonly localPort: number; readonly httpBaseUrl: string; readonly wsBaseUrl: string; readonly process: ChildProcessSpawner.ChildProcessHandle; readonly scope: Scope.Scope;} type SshEnvironmentEffectContext = | ChildProcessSpawner.ChildProcessSpawner | FileSystem.FileSystem | Path.Path | HttpClient.HttpClient | NetService.NetService | SshAuth.SshPasswordPrompt; type SshEnvironmentEffectError = | SshCommandError | SshInvalidTargetError | SshLaunchError | SshPairingError | SshReadinessError | SshPasswordPromptError | NetService.NetError; function sshTargetLogFields(target: DesktopSshEnvironmentTarget) { return { alias: target.alias, hostname: target.hostname, username: target.username, port: target.port, };} function sshRunnerLogFields(runner: RemoteT3RunnerOptions | undefined) { if (runner?.nodeScriptPath?.trim()) { return { runner: "node-script", nodeScriptPath: runner.nodeScriptPath.trim() }; } if (runner?.packageSpec?.trim()) { return { runner: "package", packageSpec: runner.packageSpec.trim() }; } return { runner: "default" };} interface SshAuthOperationInput<T> { readonly key: string; readonly target: DesktopSshEnvironmentTarget; readonly operation: ( authOptions: SshAuth.SshAuthOptions, ) => Effect.Effect<T, SshEnvironmentEffectError, SshEnvironmentEffectContext>;} interface SshAuthAttemptInput<T> extends SshAuthOperationInput<T> { readonly promptCount: number; readonly authSecret: string | null;} /** * @effect-expect-leaking ChildProcessSpawner | FileSystem | HttpClient | NetService | Path | SshAuth.SshPasswordPrompt */export class SshEnvironmentManager extends Context.Service< SshEnvironmentManager, { readonly ensureEnvironment: ( target: DesktopSshEnvironmentTarget, options?: { readonly issuePairingToken?: boolean }, ) => Effect.Effect< DesktopSshEnvironmentBootstrap, SshEnvironmentEffectError, SshEnvironmentEffectContext >; readonly disconnectEnvironment: ( target: DesktopSshEnvironmentTarget, ) => Effect.Effect<void, SshEnvironmentEffectError, SshEnvironmentEffectContext>; }>()("@t3tools/ssh/tunnel/SshEnvironmentManager") {} const RemoteLaunchResult = Schema.Struct({ remotePort: Schema.Number, serverKind: Schema.optional(Schema.Literals(["external", "managed"])),}); const RemotePairingResult = Schema.Struct({ credential: Schema.String,}); const decodeRemoteLaunchResult = Schema.decodeEffect(fromLenientJson(RemoteLaunchResult));const decodeRemotePairingResult = Schema.decodeEffect(fromLenientJson(RemotePairingResult)); const decodeRemoteJsonOutput = <A, E>( stdout: string, decode: (input: string) => Effect.Effect<A, E>,): Effect.Effect<A, E> => decode(stdout).pipe( Effect.catch((error) => Effect.gen(function* () { const jsonObject = extractJsonObject(stdout); if (jsonObject === stdout.trim()) { return yield* Effect.fail(error); } const exit = yield* Effect.exit(decode(jsonObject)); if (Exit.isSuccess(exit)) { return exit.value; } return yield* Effect.fail(error); }), ), ); const decodeRemoteLaunchOutput = (stdout: string) => decodeRemoteJsonOutput(stdout, decodeRemoteLaunchResult); const decodeRemotePairingOutput = (stdout: string) => decodeRemoteJsonOutput(stdout, decodeRemotePairingResult); const remoteNodeEngineCheckMain = function remoteNodeEngineCheckMain() { const range = process.argv[2] || ""; const rawVersion = process.versions && process.versions.node ? process.versions.node : process.version; if (!satisfiesSemverRange(rawVersion, range)) { process.stderr.write( "Remote node " + rawVersion + " does not satisfy required range " + range + ".\n", ); process.exit(1); }}; function buildRemoteNodeEngineCheckScript(): string { return `${satisfiesSemverRange.toString()}(${remoteNodeEngineCheckMain.toString()})();`;} function stripTrailingNewlines(value: string): string { return value.replace(/\n+$/u, "");} function shellSingleQuote(value: string): string { return `'${value.replaceAll("'", "'\\''")}'`;} function applyScriptPlaceholders( template: string, replacements: Readonly<Record<string, string>>,): string { let result = template; for (const [token, value] of Object.entries(replacements)) { result = result.replaceAll(`@@${token}@@`, value); } return result;} const SSH_FAILURE_DIAGNOSTIC_DEPTH = 6; function describeReadinessCauseAtDepth(cause: unknown, depth: number): unknown { if (depth >= SSH_FAILURE_DIAGNOSTIC_DEPTH) return { truncated: true }; const describeNested = (nested: unknown) => describeReadinessCauseAtDepth(nested, depth + 1); if (Cause.isCause(cause)) { return { reasons: cause.reasons.map((reason) => { if (Cause.isFailReason(reason)) { return { _tag: reason._tag, error: describeNested(reason.error) }; } if (Cause.isDieReason(reason)) { return { _tag: reason._tag, defect: describeNested(reason.defect) }; } return { _tag: reason._tag, fiberId: reason.fiberId }; }), }; } if (isSshReadinessError(cause)) { const { cause: nestedCause, ...attributes } = cause as SshReadinessError & { readonly cause?: unknown; }; return { ...attributes, message: cause.message, ...(nestedCause === undefined ? {} : { cause: describeNested(nestedCause) }), }; } if (cause instanceof Error) { const tagged = cause as Error & { readonly _tag?: unknown; readonly reason?: unknown }; return { ...(typeof tagged._tag === "string" ? { _tag: tagged._tag } : { name: cause.name }), ...(tagged.reason === undefined ? {} : { reason: describeNested(tagged.reason) }), ...(cause.cause === undefined ? {} : { cause: describeNested(cause.cause) }), }; } if (typeof cause !== "object" || cause === null) { return { type: cause === null ? "null" : typeof cause }; } const record = cause as Readonly<Record<string, unknown>>; return { ...(typeof record._tag === "string" ? { _tag: record._tag } : {}), ...(record.reason === undefined ? {} : { reason: describeNested(record.reason) }), ...(record.cause === undefined ? {} : { cause: describeNested(record.cause) }), };} export function describeReadinessCause(cause: unknown): unknown { return describeReadinessCauseAtDepth(cause, 0);} function readinessUrlDiagnostics(input: string) { const url = new URL(input); const diagnostics = getUrlDiagnostics(input); return { protocol: diagnostics.protocol ?? url.protocol, hostname: diagnostics.hostname ?? url.hostname, ...(url.port === "" ? {} : { port: url.port }), urlLength: diagnostics.inputLength, pathnameLength: utf8ByteLength(url.pathname), hasQuery: url.search !== "", hasFragment: url.hash !== "", };} export const REMOTE_PICK_PORT_SCRIPT = `const fs = require("node:fs");const net = require("node:net");const filePath = process.argv[2] ?? "";const defaultPort = Number.parseInt(process.argv[3] ?? "", 10);const scanWindow = Number.parseInt(process.argv[4] ?? "", 10);const raw = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf8").trim() : "";const preferred = Number.parseInt(raw, 10);const start = Number.isInteger(preferred) ? preferred : defaultPort;const end = start + scanWindow; function tryPort(port) { return new Promise((resolve) => { const server = net.createServer(); server.unref(); server.once("error", () => resolve(false)); server.listen(port, "127.0.0.1", () => { server.close((error) => resolve(error ? false : port)); }); });} (async () => { for (let port = start; port < end; port += 1) { const available = await tryPort(port); if (available) { process.stdout.write(String(port)); return; } } process.exit(1);})().catch(() => process.exit(1));`; export const REMOTE_WAIT_READY_SCRIPT = `const http = require("node:http");const port = Number.parseInt(process.argv[2] ?? "", 10);const timeoutMs = Number.parseInt(process.argv[3] ?? "", 10);const probeTimeoutMs = Number.parseInt(process.argv[4] ?? "", 10);if (!Number.isInteger(port) || !Number.isInteger(timeoutMs) || !Number.isInteger(probeTimeoutMs)) { process.exit(1);}const deadline = Date.now() + timeoutMs; function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms));} function probe() { return new Promise((resolve) => { const request = http.get( { hostname: "127.0.0.1", port, path: "/", timeout: probeTimeoutMs, }, (response) => { response.resume(); response.once("end", () => { resolve(response.statusCode >= 200 && response.statusCode < 300); }); }, ); request.once("timeout", () => { request.destroy(); resolve(false); }); request.once("error", () => resolve(false)); });} (async () => { while (Date.now() < deadline) { if (await probe()) { process.exit(0); } await sleep(100); } process.exit(1);})().catch(() => process.exit(1));`; export const REMOTE_NODE_ENV_SCRIPT = `prepend_path_if_dir() { if [ -d "$1" ]; then case ":$PATH:" in *":$1:"*) ;; *) PATH="$1:$PATH" ;; esac fi} remote_node_satisfies_engine() { T3_NODE_ENGINE_RANGE=@@T3_NODE_ENGINE_RANGE@@ if [ -z "$T3_NODE_ENGINE_RANGE" ]; then return 0 fi node - "$T3_NODE_ENGINE_RANGE" <<'NODE'@@T3_NODE_ENGINE_CHECK_SCRIPT@@NODE} ensure_remote_node_path() { if command -v node >/dev/null 2>&1 && remote_node_satisfies_engine >/dev/null 2>&1; then return 0 fi prepend_path_if_dir "$HOME/.local/bin" prepend_path_if_dir "$HOME/bin" prepend_path_if_dir "/opt/homebrew/bin" prepend_path_if_dir "/usr/local/bin" prepend_path_if_dir "/usr/bin" prepend_path_if_dir "/bin" if [ -z "\${VOLTA_HOME:-}" ]; then VOLTA_HOME="$HOME/.volta" fi export VOLTA_HOME prepend_path_if_dir "$VOLTA_HOME/bin" prepend_path_if_dir "$HOME/.asdf/shims" prepend_path_if_dir "$HOME/.asdf/bin" if [ ! -x "$HOME/.asdf/shims/node" ] && [ -s "$HOME/.asdf/asdf.sh" ]; then # shellcheck disable=SC1090 . "$HOME/.asdf/asdf.sh" fi prepend_path_if_dir "$HOME/.local/share/mise/shims" prepend_path_if_dir "$HOME/.mise/shims" if ! command -v node >/dev/null 2>&1 && command -v mise >/dev/null 2>&1; then eval "$(mise activate sh)" >/dev/null 2>&1 || true fi if [ -z "\${FNM_DIR:-}" ]; then FNM_DIR="$HOME/.local/share/fnm" fi export FNM_DIR prepend_path_if_dir "$FNM_DIR" prepend_path_if_dir "$HOME/.fnm" if ! command -v node >/dev/null 2>&1 && command -v fnm >/dev/null 2>&1; then eval "$(fnm env --shell bash)" >/dev/null 2>&1 || true fnm use --silent-if-unchanged >/dev/null 2>&1 || fnm use default >/dev/null 2>&1 || true fi prepend_path_if_dir "$HOME/.nodenv/bin" prepend_path_if_dir "$HOME/.nodenv/shims" if ! command -v node >/dev/null 2>&1 && command -v nodenv >/dev/null 2>&1; then eval "$(nodenv init -)" >/dev/null 2>&1 || true fi if [ -z "\${NVM_DIR:-}" ]; then NVM_DIR="$HOME/.nvm" fi export NVM_DIR if [ -s "$NVM_DIR/nvm.sh" ]; then # shellcheck disable=SC1090 . "$NVM_DIR/nvm.sh" if ! command -v node >/dev/null 2>&1 && command -v nvm >/dev/null 2>&1; then nvm use --silent default >/dev/null 2>&1 || nvm use --silent node >/dev/null 2>&1 || nvm use --silent --lts >/dev/null 2>&1 || true fi fi if ! command -v node >/dev/null 2>&1 && [ -d "$NVM_DIR/versions/node" ]; then for T3_NODE_BIN in "$NVM_DIR"/versions/node/*/bin; do if [ -x "$T3_NODE_BIN/node" ]; then PATH="$T3_NODE_BIN:$PATH" export PATH fi done fi command -v node >/dev/null 2>&1 && remote_node_satisfies_engine}`; export const REMOTE_RUNNER_SCRIPT = `#!/bin/shset -eu@@T3_NODE_ENV_SCRIPT@@ensure_remote_node_path || trueT3_NODE_SCRIPT_PATH=@@T3_NODE_SCRIPT_PATH@@if [ -n "$T3_NODE_SCRIPT_PATH" ]; then if ! command -v node >/dev/null 2>&1; then printf 'Remote host is missing node on PATH. Install Node or configure a supported version manager for non-interactive shells.\\n' >&2 exit 1 fi exec node "$T3_NODE_SCRIPT_PATH" "$@"fiif command -v t3 >/dev/null 2>&1; thenLines 1–500 of 1742. Load more
import type { DesktopSshEnvironmentBootstrap, DesktopSshEnvironmentTarget, } from "@t3tools/contracts"; import * as NetService from "@t3tools/shared/Net"; import { extractJsonObject, fromLenientJson } from "@t3tools/shared/schemaJson"; import { satisfiesSemverRange } from "@t3tools/shared/semver"; import { getUrlDiagnostics } from "@t3tools/shared/urlDiagnostics"; import * as Cause from "effect/Cause"; import * as Context from "effect/Context"; import * as Deferred from "effect/Deferred"; import * as Duration from "effect/Duration"; import * as Effect from "effect/Effect"; import * as Exit from "effect/Exit"; import * as FileSystem from "effect/FileSystem"; import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Path from "effect/Path"; import * as Ref from "effect/Ref"; import * as Schedule from "effect/Schedule"; import * as Schema from "effect/Schema"; import * as Scope from "effect/Scope"; import * as Stream from "effect/Stream"; import * as HttpClient from "effect/unstable/http/HttpClient"; import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"; import * as ChildProcess from "effect/unstable/process/ChildProcess"; import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner"; import * as SshAuth from "./auth.ts"; import { baseSshArgs, buildSshHostSpecEffect, collectProcessOutput, getLastNonEmptyOutputLine, remoteStateKey, resolveSshCommand, resolveSshTarget, runSshCommand, targetConnectionKey, utf8ByteLength, } from "./command.ts"; import { SshAuthenticationHelperError, SshCommandCancelledError, SshCommandError, SshHttpBridgeInvalidUrlError, SshHttpBridgeMissingUrlError, SshHttpBridgeNonLoopbackUrlError, SshHttpBridgeError, SshInvalidTargetError, SshLaunchInvalidPortError, SshLaunchOutputParseError, SshLaunchPortMissingError, SshLaunchError, SshPairingCredentialMissingError, SshPairingInvalidCredentialError, SshPairingOutputParseError, SshPairingError, SshPasswordPromptCancelledError, SshPasswordPromptUnavailableError, SshPasswordPromptError, SshReadinessProbeError, SshReadinessProbeTimeoutError, SshReadinessTimeoutError, SshReadinessError, SshTunnelExitError, SshTunnelMonitorError, SshTunnelSpawnError, } from "./errors.ts"; const isSshReadinessError = Schema.is(SshReadinessError); export const DEFAULT_REMOTE_PORT = 3773; const REMOTE_PORT_SCAN_WINDOW = 200; const SSH_READY_TIMEOUT_MS = 20_000; const SSH_READY_PROBE_TIMEOUT_MS = 1_000; const TUNNEL_SHUTDOWN_TIMEOUT_MS = 2_000; const REMOTE_READY_TIMEOUT_MS = 15_000; const REMOTE_REUSE_READY_TIMEOUT_MS = 2_000; export interface RemoteT3RunnerOptions { readonly packageSpec?: string; readonly nodeScriptPath?: string | null; readonly nodeEngineRange?: string | null; } export interface SshEnvironmentManagerOptions { readonly resolveCliPackageSpec?: () => string; readonly resolveCliRunner?: Effect.Effect<RemoteT3RunnerOptions>; } interface SshTunnelEntry { readonly key: string; readonly target: DesktopSshEnvironmentTarget; readonly remotePort: number; readonly remoteServerKind: "external" | "managed" | null; readonly localPort: number; readonly httpBaseUrl: string; readonly wsBaseUrl: string; readonly process: ChildProcessSpawner.ChildProcessHandle; readonly scope: Scope.Scope; } type SshEnvironmentEffectContext = | ChildProcessSpawner.ChildProcessSpawner | FileSystem.FileSystem | Path.Path | HttpClient.HttpClient | NetService.NetService | SshAuth.SshPasswordPrompt; type SshEnvironmentEffectError = | SshCommandError | SshInvalidTargetError | SshLaunchError | SshPairingError | SshReadinessError | SshPasswordPromptError | NetService.NetError; function sshTargetLogFields(target: DesktopSshEnvironmentTarget) { return { alias: target.alias, hostname: target.hostname, username: target.username, port: target.port, }; } function sshRunnerLogFields(runner: RemoteT3RunnerOptions | undefined) { if (runner?.nodeScriptPath?.trim()) { return { runner: "node-script", nodeScriptPath: runner.nodeScriptPath.trim() }; } if (runner?.packageSpec?.trim()) { return { runner: "package", packageSpec: runner.packageSpec.trim() }; } return { runner: "default" }; } interface SshAuthOperationInput<T> { readonly key: string; readonly target: DesktopSshEnvironmentTarget; readonly operation: ( authOptions: SshAuth.SshAuthOptions, ) => Effect.Effect<T, SshEnvironmentEffectError, SshEnvironmentEffectContext>; } interface SshAuthAttemptInput<T> extends SshAuthOperationInput<T> { readonly promptCount: number; readonly authSecret: string | null; } /** * @effect-expect-leaking ChildProcessSpawner | FileSystem | HttpClient | NetService | Path | SshAuth.SshPasswordPrompt */ export class SshEnvironmentManager extends Context.Service< SshEnvironmentManager, { readonly ensureEnvironment: ( target: DesktopSshEnvironmentTarget, options?: { readonly issuePairingToken?: boolean }, ) => Effect.Effect< DesktopSshEnvironmentBootstrap, SshEnvironmentEffectError, SshEnvironmentEffectContext >; readonly disconnectEnvironment: ( target: DesktopSshEnvironmentTarget, ) => Effect.Effect<void, SshEnvironmentEffectError, SshEnvironmentEffectContext>; } >()("@t3tools/ssh/tunnel/SshEnvironmentManager") {} const RemoteLaunchResult = Schema.Struct({ remotePort: Schema.Number, serverKind: Schema.optional(Schema.Literals(["external", "managed"])), }); const RemotePairingResult = Schema.Struct({ credential: Schema.String, }); const decodeRemoteLaunchResult = Schema.decodeEffect(fromLenientJson(RemoteLaunchResult)); const decodeRemotePairingResult = Schema.decodeEffect(fromLenientJson(RemotePairingResult)); const decodeRemoteJsonOutput = <A, E>( stdout: string, decode: (input: string) => Effect.Effect<A, E>, ): Effect.Effect<A, E> => decode(stdout).pipe( Effect.catch((error) => Effect.gen(function* () { const jsonObject = extractJsonObject(stdout); if (jsonObject === stdout.trim()) { return yield* Effect.fail(error); } const exit = yield* Effect.exit(decode(jsonObject)); if (Exit.isSuccess(exit)) { return exit.value; } return yield* Effect.fail(error); }), ), ); const decodeRemoteLaunchOutput = (stdout: string) => decodeRemoteJsonOutput(stdout, decodeRemoteLaunchResult); const decodeRemotePairingOutput = (stdout: string) => decodeRemoteJsonOutput(stdout, decodeRemotePairingResult); const remoteNodeEngineCheckMain = function remoteNodeEngineCheckMain() { const range = process.argv[2] || ""; const rawVersion = process.versions && process.versions.node ? process.versions.node : process.version; if (!satisfiesSemverRange(rawVersion, range)) { process.stderr.write( "Remote node " + rawVersion + " does not satisfy required range " + range + ".\n", ); process.exit(1); } }; function buildRemoteNodeEngineCheckScript(): string { return `${satisfiesSemverRange.toString()} (${remoteNodeEngineCheckMain.toString()})();`; } function stripTrailingNewlines(value: string): string { return value.replace(/\n+$/u, ""); } function shellSingleQuote(value: string): string { return `'${value.replaceAll("'", "'\\''")}'`; } function applyScriptPlaceholders( template: string, replacements: Readonly<Record<string, string>>, ): string { let result = template; for (const [token, value] of Object.entries(replacements)) { result = result.replaceAll(`@@${token}@@`, value); } return result; } const SSH_FAILURE_DIAGNOSTIC_DEPTH = 6; function describeReadinessCauseAtDepth(cause: unknown, depth: number): unknown { if (depth >= SSH_FAILURE_DIAGNOSTIC_DEPTH) return { truncated: true }; const describeNested = (nested: unknown) => describeReadinessCauseAtDepth(nested, depth + 1); if (Cause.isCause(cause)) { return { reasons: cause.reasons.map((reason) => { if (Cause.isFailReason(reason)) { return { _tag: reason._tag, error: describeNested(reason.error) }; } if (Cause.isDieReason(reason)) { return { _tag: reason._tag, defect: describeNested(reason.defect) }; } return { _tag: reason._tag, fiberId: reason.fiberId }; }), }; } if (isSshReadinessError(cause)) { const { cause: nestedCause, ...attributes } = cause as SshReadinessError & { readonly cause?: unknown; }; return { ...attributes, message: cause.message, ...(nestedCause === undefined ? {} : { cause: describeNested(nestedCause) }), }; } if (cause instanceof Error) { const tagged = cause as Error & { readonly _tag?: unknown; readonly reason?: unknown }; return { ...(typeof tagged._tag === "string" ? { _tag: tagged._tag } : { name: cause.name }), ...(tagged.reason === undefined ? {} : { reason: describeNested(tagged.reason) }), ...(cause.cause === undefined ? {} : { cause: describeNested(cause.cause) }), }; } if (typeof cause !== "object" || cause === null) { return { type: cause === null ? "null" : typeof cause }; } const record = cause as Readonly<Record<string, unknown>>; return { ...(typeof record._tag === "string" ? { _tag: record._tag } : {}), ...(record.reason === undefined ? {} : { reason: describeNested(record.reason) }), ...(record.cause === undefined ? {} : { cause: describeNested(record.cause) }), }; } export function describeReadinessCause(cause: unknown): unknown { return describeReadinessCauseAtDepth(cause, 0); } function readinessUrlDiagnostics(input: string) { const url = new URL(input); const diagnostics = getUrlDiagnostics(input); return { protocol: diagnostics.protocol ?? url.protocol, hostname: diagnostics.hostname ?? url.hostname, ...(url.port === "" ? {} : { port: url.port }), urlLength: diagnostics.inputLength, pathnameLength: utf8ByteLength(url.pathname), hasQuery: url.search !== "", hasFragment: url.hash !== "", }; } export const REMOTE_PICK_PORT_SCRIPT = `const fs = require("node:fs"); const net = require("node:net"); const filePath = process.argv[2] ?? ""; const defaultPort = Number.parseInt(process.argv[3] ?? "", 10); const scanWindow = Number.parseInt(process.argv[4] ?? "", 10); const raw = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf8").trim() : ""; const preferred = Number.parseInt(raw, 10); const start = Number.isInteger(preferred) ? preferred : defaultPort; const end = start + scanWindow; function tryPort(port) { return new Promise((resolve) => { const server = net.createServer(); server.unref(); server.once("error", () => resolve(false)); server.listen(port, "127.0.0.1", () => { server.close((error) => resolve(error ? false : port)); }); }); } (async () => { for (let port = start; port < end; port += 1) { const available = await tryPort(port); if (available) { process.stdout.write(String(port)); return; } } process.exit(1); })().catch(() => process.exit(1)); `; export const REMOTE_WAIT_READY_SCRIPT = `const http = require("node:http"); const port = Number.parseInt(process.argv[2] ?? "", 10); const timeoutMs = Number.parseInt(process.argv[3] ?? "", 10); const probeTimeoutMs = Number.parseInt(process.argv[4] ?? "", 10); if (!Number.isInteger(port) || !Number.isInteger(timeoutMs) || !Number.isInteger(probeTimeoutMs)) { process.exit(1); } const deadline = Date.now() + timeoutMs; function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } function probe() { return new Promise((resolve) => { const request = http.get( { hostname: "127.0.0.1", port, path: "/", timeout: probeTimeoutMs, }, (response) => { response.resume(); response.once("end", () => { resolve(response.statusCode >= 200 && response.statusCode < 300); }); }, ); request.once("timeout", () => { request.destroy(); resolve(false); }); request.once("error", () => resolve(false)); }); } (async () => { while (Date.now() < deadline) { if (await probe()) { process.exit(0); } await sleep(100); } process.exit(1); })().catch(() => process.exit(1)); `; export const REMOTE_NODE_ENV_SCRIPT = `prepend_path_if_dir() { if [ -d "$1" ]; then case ":$PATH:" in *":$1:"*) ;; *) PATH="$1:$PATH" ;; esac fi } remote_node_satisfies_engine() { T3_NODE_ENGINE_RANGE=@@T3_NODE_ENGINE_RANGE@@ if [ -z "$T3_NODE_ENGINE_RANGE" ]; then return 0 fi node - "$T3_NODE_ENGINE_RANGE" <<'NODE' @@T3_NODE_ENGINE_CHECK_SCRIPT@@ NODE } ensure_remote_node_path() { if command -v node >/dev/null 2>&1 && remote_node_satisfies_engine >/dev/null 2>&1; then return 0 fi prepend_path_if_dir "$HOME/.local/bin" prepend_path_if_dir "$HOME/bin" prepend_path_if_dir "/opt/homebrew/bin" prepend_path_if_dir "/usr/local/bin" prepend_path_if_dir "/usr/bin" prepend_path_if_dir "/bin" if [ -z "\${VOLTA_HOME:-}" ]; then VOLTA_HOME="$HOME/.volta" fi export VOLTA_HOME prepend_path_if_dir "$VOLTA_HOME/bin" prepend_path_if_dir "$HOME/.asdf/shims" prepend_path_if_dir "$HOME/.asdf/bin" if [ ! -x "$HOME/.asdf/shims/node" ] && [ -s "$HOME/.asdf/asdf.sh" ]; then # shellcheck disable=SC1090 . "$HOME/.asdf/asdf.sh" fi prepend_path_if_dir "$HOME/.local/share/mise/shims" prepend_path_if_dir "$HOME/.mise/shims" if ! command -v node >/dev/null 2>&1 && command -v mise >/dev/null 2>&1; then eval "$(mise activate sh)" >/dev/null 2>&1 || true fi if [ -z "\${FNM_DIR:-}" ]; then FNM_DIR="$HOME/.local/share/fnm" fi export FNM_DIR prepend_path_if_dir "$FNM_DIR" prepend_path_if_dir "$HOME/.fnm" if ! command -v node >/dev/null 2>&1 && command -v fnm >/dev/null 2>&1; then eval "$(fnm env --shell bash)" >/dev/null 2>&1 || true fnm use --silent-if-unchanged >/dev/null 2>&1 || fnm use default >/dev/null 2>&1 || true fi prepend_path_if_dir "$HOME/.nodenv/bin" prepend_path_if_dir "$HOME/.nodenv/shims" if ! command -v node >/dev/null 2>&1 && command -v nodenv >/dev/null 2>&1; then eval "$(nodenv init -)" >/dev/null 2>&1 || true fi if [ -z "\${NVM_DIR:-}" ]; then NVM_DIR="$HOME/.nvm" fi export NVM_DIR if [ -s "$NVM_DIR/nvm.sh" ]; then # shellcheck disable=SC1090 . "$NVM_DIR/nvm.sh" if ! command -v node >/dev/null 2>&1 && command -v nvm >/dev/null 2>&1; then nvm use --silent default >/dev/null 2>&1 || nvm use --silent node >/dev/null 2>&1 || nvm use --silent --lts >/dev/null 2>&1 || true fi fi if ! command -v node >/dev/null 2>&1 && [ -d "$NVM_DIR/versions/node" ]; then for T3_NODE_BIN in "$NVM_DIR"/versions/node/*/bin; do if [ -x "$T3_NODE_BIN/node" ]; then PATH="$T3_NODE_BIN:$PATH" export PATH fi done fi command -v node >/dev/null 2>&1 && remote_node_satisfies_engine } `; export const REMOTE_RUNNER_SCRIPT = `#!/bin/sh set -eu @@T3_NODE_ENV_SCRIPT@@ ensure_remote_node_path || true T3_NODE_SCRIPT_PATH=@@T3_NODE_SCRIPT_PATH@@ if [ -n "$T3_NODE_SCRIPT_PATH" ]; then if ! command -v node >/dev/null 2>&1; then printf 'Remote host is missing node on PATH. Install Node or configure a supported version manager for non-interactive shells.\\n' >&2 exit 1 fi exec node "$T3_NODE_SCRIPT_PATH" "$@" fi if command -v t3 >/dev/null 2>&1; then