| author | |
| committer | |
| log | 3fbc497b764ed904c4e50655da5b7a722d611fe5 |
| tree | fdadaa639d21d84dcf8cc5d1b8a352e88dcf4c3c |
| parent | a324cabc04019be14e8b66ec983fabf510ae2b2e |
| signature | Signed by PGP key B5690EEEBB952194 |
4 files changed, 37 insertions(+), 39 deletions(-)
packages/ssh/src/auth.ts+26-28| ... | ... | @@ -17,7 +17,7 @@ export interface SshPasswordRequest { |
| 17 | 17 | readonly attempt: number; |
| 18 | 18 | } |
| 19 | 19 | |
| 20 | export interface SshAskpassFile { | |
| 20 | interface SshAskpassFile { | |
| 21 | 21 | readonly path: string; |
| 22 | 22 | readonly contents: string; |
| 23 | 23 | readonly mode?: number; |
| ... | ... | @@ -71,7 +71,7 @@ function joinSshAskpassPath( |
| 71 | 71 | return platform === "win32" ? `${trimmed}\\${fileName}` : `${trimmed}/${fileName}`; |
| 72 | 72 | } |
| 73 | 73 | |
| 74 | export const ASKPASS_POSIX_SCRIPT = `#!/bin/sh | |
| 74 | const ASKPASS_POSIX_SCRIPT = `#!/bin/sh | |
| 75 | 75 | # Invoked by ssh via SSH_ASKPASS when T3 Code re-runs ssh with a cached password |
| 76 | 76 | # from the renderer's in-app prompt. We never expose a native dialog here - if |
| 77 | 77 | # T3_SSH_AUTH_SECRET is missing, that's a caller bug and we fail loudly. |
| ... | ... | @@ -83,11 +83,11 @@ printf 'T3 Code ssh-askpass invoked without T3_SSH_AUTH_SECRET.\\n' >&2 |
| 83 | 83 | exit 1 |
| 84 | 84 | `; |
| 85 | 85 | |
| 86 | export const ASKPASS_WINDOWS_LAUNCHER_SCRIPT = `@echo off\r | |
| 86 | const ASKPASS_WINDOWS_LAUNCHER_SCRIPT = `@echo off\r | |
| 87 | 87 | powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0ssh-askpass.ps1" %*\r |
| 88 | 88 | `; |
| 89 | 89 | |
| 90 | export const ASKPASS_WINDOWS_SCRIPT = `# Invoked by ssh via SSH_ASKPASS (through ssh-askpass.cmd) when T3 Code re-runs\r | |
| 90 | const ASKPASS_WINDOWS_SCRIPT = `# Invoked by ssh via SSH_ASKPASS (through ssh-askpass.cmd) when T3 Code re-runs\r | |
| 91 | 91 | # ssh with a cached password from the renderer's in-app prompt. We never expose\r |
| 92 | 92 | # a native dialog here - if T3_SSH_AUTH_SECRET is missing, that's a caller bug\r |
| 93 | 93 | # and we fail loudly.\r |
| ... | ... | @@ -99,7 +99,7 @@ if ($null -ne $env:T3_SSH_AUTH_SECRET) {\r |
| 99 | 99 | exit 1\r |
| 100 | 100 | `; |
| 101 | 101 | |
| 102 | export const getDefaultSshAskpassDirectory = Effect.fn("ssh/auth.getDefaultSshAskpassDirectory")( | |
| 102 | const getDefaultSshAskpassDirectory = Effect.fn("ssh/auth.getDefaultSshAskpassDirectory")( | |
| 103 | 103 | function* () { |
| 104 | 104 | const fs = yield* FileSystem.FileSystem; |
| 105 | 105 | const path = yield* Path.Path; |
| ... | ... | @@ -146,31 +146,29 @@ export const buildSshAskpassHelperDescriptor = Effect.fn( |
| 146 | 146 | }; |
| 147 | 147 | }); |
| 148 | 148 | |
| 149 | export const ensureSshAskpassHelpers = Effect.fn("ssh/auth.ensureSshAskpassHelpers")( | |
| 150 | function* (input: { | |
| 151 | readonly directory: string; | |
| 152 | }): Effect.fn.Return<string, PlatformError.PlatformError, FileSystem.FileSystem | Path.Path> { | |
| 153 | const fs = yield* FileSystem.FileSystem; | |
| 154 | const path = yield* Path.Path; | |
| 155 | const descriptor = yield* buildSshAskpassHelperDescriptor(input); | |
| 156 | const platform = yield* HostProcessPlatform; | |
| 157 | ||
| 158 | yield* fs.makeDirectory(path.dirname(descriptor.launcherPath), { recursive: true }); | |
| 159 | ||
| 160 | for (const file of descriptor.files) { | |
| 161 | const existing = yield* fs.exists(file.path); | |
| 162 | const current = existing ? yield* fs.readFileString(file.path) : null; | |
| 163 | if (current !== file.contents) { | |
| 164 | yield* fs.writeFileString(file.path, file.contents); | |
| 165 | } | |
| 166 | if (file.mode !== undefined && platform !== "win32") { | |
| 167 | yield* fs.chmod(file.path, file.mode); | |
| 168 | } | |
| 149 | const ensureSshAskpassHelpers = Effect.fn("ssh/auth.ensureSshAskpassHelpers")(function* (input: { | |
| 150 | readonly directory: string; | |
| 151 | }): Effect.fn.Return<string, PlatformError.PlatformError, FileSystem.FileSystem | Path.Path> { | |
| 152 | const fs = yield* FileSystem.FileSystem; | |
| 153 | const path = yield* Path.Path; | |
| 154 | const descriptor = yield* buildSshAskpassHelperDescriptor(input); | |
| 155 | const platform = yield* HostProcessPlatform; | |
| 156 | ||
| 157 | yield* fs.makeDirectory(path.dirname(descriptor.launcherPath), { recursive: true }); | |
| 158 | ||
| 159 | for (const file of descriptor.files) { | |
| 160 | const existing = yield* fs.exists(file.path); | |
| 161 | const current = existing ? yield* fs.readFileString(file.path) : null; | |
| 162 | if (current !== file.contents) { | |
| 163 | yield* fs.writeFileString(file.path, file.contents); | |
| 164 | } | |
| 165 | if (file.mode !== undefined && platform !== "win32") { | |
| 166 | yield* fs.chmod(file.path, file.mode); | |
| 169 | 167 | } |
| 168 | } | |
| 170 | 169 | |
| 171 | return descriptor.launcherPath; | |
| 172 | }, | |
| 173 | ); | |
| 170 | return descriptor.launcherPath; | |
| 171 | }); | |
| 174 | 172 | |
| 175 | 173 | export const buildSshChildEnvironment = Effect.fn("ssh/auth.buildSshChildEnvironment")(function* ( |
| 176 | 174 | input: SshChildEnvironmentOptions = {}, |
packages/ssh/src/command.ts+1-1| ... | ... | @@ -80,7 +80,7 @@ export function remoteStateKey(target: DesktopSshEnvironmentTarget): string { |
| 80 | 80 | .slice(0, 16); |
| 81 | 81 | } |
| 82 | 82 | |
| 83 | export function buildSshHostSpec(target: DesktopSshEnvironmentTarget): string { | |
| 83 | function buildSshHostSpec(target: DesktopSshEnvironmentTarget): string { | |
| 84 | 84 | const destination = target.alias.trim() || target.hostname.trim(); |
| 85 | 85 | if (destination.length === 0) { |
| 86 | 86 | throw new Error("SSH target is missing its alias/hostname."); |
packages/ssh/src/config.ts+1-1| ... | ... | @@ -89,7 +89,7 @@ const expandGlob = Effect.fnUntraced(function* (pattern: string) { |
| 89 | 89 | return matchedPaths.toSorted((left, right) => left.localeCompare(right)); |
| 90 | 90 | }); |
| 91 | 91 | |
| 92 | export const collectSshConfigAliasesFromFile = Effect.fnUntraced(function* ( | |
| 92 | const collectSshConfigAliasesFromFile = Effect.fnUntraced(function* ( | |
| 93 | 93 | filePath: string, |
| 94 | 94 | visited = new Set<string>(), |
| 95 | 95 | homeDir: string, |
packages/ssh/src/tunnel.ts+9-9| ... | ... | @@ -49,7 +49,7 @@ import { |
| 49 | 49 | SshReadinessError, |
| 50 | 50 | } from "./errors.ts"; |
| 51 | 51 | |
| 52 | export const DEFAULT_REMOTE_PORT = 3773; | |
| 52 | const DEFAULT_REMOTE_PORT = 3773; | |
| 53 | 53 | const REMOTE_PORT_SCAN_WINDOW = 200; |
| 54 | 54 | const SSH_READY_TIMEOUT_MS = 20_000; |
| 55 | 55 | const SSH_READY_PROBE_TIMEOUT_MS = 1_000; |
| ... | ... | @@ -209,7 +209,7 @@ function buildRemoteNodeEngineCheckScript(): string { |
| 209 | 209 | (${remoteNodeEngineCheckMain.toString()})();`; |
| 210 | 210 | } |
| 211 | 211 | |
| 212 | export function normalizeSshErrorMessage(stderr: string, fallbackMessage: string): string { | |
| 212 | function normalizeSshErrorMessage(stderr: string, fallbackMessage: string): string { | |
| 213 | 213 | const cleaned = stderr.trim(); |
| 214 | 214 | return cleaned.length > 0 ? cleaned : fallbackMessage; |
| 215 | 215 | } |
| ... | ... | @@ -270,7 +270,7 @@ function tryPort(port) { |
| 270 | 270 | })().catch(() => process.exit(1)); |
| 271 | 271 | `; |
| 272 | 272 | |
| 273 | export const REMOTE_WAIT_READY_SCRIPT = `const http = require("node:http"); | |
| 273 | const REMOTE_WAIT_READY_SCRIPT = `const http = require("node:http"); | |
| 274 | 274 | const port = Number.parseInt(process.argv[2] ?? "", 10); |
| 275 | 275 | const timeoutMs = Number.parseInt(process.argv[3] ?? "", 10); |
| 276 | 276 | const probeTimeoutMs = Number.parseInt(process.argv[4] ?? "", 10); |
| ... | ... | @@ -318,7 +318,7 @@ function probe() { |
| 318 | 318 | })().catch(() => process.exit(1)); |
| 319 | 319 | `; |
| 320 | 320 | |
| 321 | export const REMOTE_NODE_ENV_SCRIPT = `prepend_path_if_dir() { | |
| 321 | const REMOTE_NODE_ENV_SCRIPT = `prepend_path_if_dir() { | |
| 322 | 322 | if [ -d "$1" ]; then |
| 323 | 323 | case ":$PATH:" in |
| 324 | 324 | *":$1:"*) ;; |
| ... | ... | @@ -411,7 +411,7 @@ ensure_remote_node_path() { |
| 411 | 411 | } |
| 412 | 412 | `; |
| 413 | 413 | |
| 414 | export const REMOTE_RUNNER_SCRIPT = `#!/bin/sh | |
| 414 | const REMOTE_RUNNER_SCRIPT = `#!/bin/sh | |
| 415 | 415 | set -eu |
| 416 | 416 | @@T3_NODE_ENV_SCRIPT@@ |
| 417 | 417 | ensure_remote_node_path || true |
| ... | ... | @@ -456,7 +456,7 @@ printf 'Remote host is missing the t3 CLI and could not install @@T3_PACKAGE_SPE |
| 456 | 456 | exit 1 |
| 457 | 457 | `; |
| 458 | 458 | |
| 459 | export const REMOTE_LAUNCH_SCRIPT = `set -eu | |
| 459 | const REMOTE_LAUNCH_SCRIPT = `set -eu | |
| 460 | 460 | @@T3_NODE_ENV_SCRIPT@@ |
| 461 | 461 | STATE_KEY="$1" |
| 462 | 462 | STATE_DIR="$HOME/.t3/ssh-launch/$STATE_KEY" |
| ... | ... | @@ -615,7 +615,7 @@ fi |
| 615 | 615 | printf '{"remotePort":%s,"serverKind":"%s"}\\n' "$REMOTE_PORT" "\${REMOTE_MANAGED:-managed}" |
| 616 | 616 | `; |
| 617 | 617 | |
| 618 | export const REMOTE_PAIRING_SCRIPT = `set -eu | |
| 618 | const REMOTE_PAIRING_SCRIPT = `set -eu | |
| 619 | 619 | STATE_DIR="$HOME/.t3/ssh-launch/@@T3_STATE_KEY@@" |
| 620 | 620 | DEFAULT_SERVER_HOME="$HOME/.t3" |
| 621 | 621 | RUNNER_FILE="$STATE_DIR/run-t3.sh" |
| ... | ... | @@ -628,7 +628,7 @@ PAIRING_BASE_DIR="$DEFAULT_SERVER_HOME" |
| 628 | 628 | "$RUNNER_FILE" auth pairing create --base-dir "$PAIRING_BASE_DIR" --json |
| 629 | 629 | `; |
| 630 | 630 | |
| 631 | export const REMOTE_STOP_SCRIPT = `set -eu | |
| 631 | const REMOTE_STOP_SCRIPT = `set -eu | |
| 632 | 632 | STATE_DIR="$HOME/.t3/ssh-launch/@@T3_STATE_KEY@@" |
| 633 | 633 | PID_FILE="$STATE_DIR/pid" |
| 634 | 634 | PORT_FILE="$STATE_DIR/port" |
| ... | ... | @@ -823,7 +823,7 @@ export const issueRemotePairingToken = Effect.fn("ssh/tunnel.issueRemotePairingT |
| 823 | 823 | }; |
| 824 | 824 | }); |
| 825 | 825 | |
| 826 | export const stopRemoteServer = Effect.fn("ssh/tunnel.stopRemoteServer")(function* ( | |
| 826 | const stopRemoteServer = Effect.fn("ssh/tunnel.stopRemoteServer")(function* ( | |
| 827 | 827 | target: DesktopSshEnvironmentTarget, |
| 828 | 828 | input?: SshAuthOptions, |
| 829 | 829 | ): Effect.fn.Return< |