sshya 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md ADDED
@@ -0,0 +1,56 @@
1
+ # SSHya CLI - SSH Connection Manager
2
+
3
+ **Purpose**: CLI tool for managing SSH connections with aliases, interactive prompts, and fzf integration.
4
+
5
+ ## Core Features
6
+ - **Connection Management**: Add, remove, update, list SSH connections with custom aliases
7
+ - **Interactive Interface**: Uses inquirer.js for user-friendly prompts
8
+ - **fzf Integration**: Fast keyboard-driven connection selection via shell snippets
9
+ - **Import/Export**: JSON-based connection backup and restore
10
+
11
+ ## Architecture
12
+ - **Runtime**: Bun (JavaScript runtime)
13
+ - **Language**: TypeScript with ESM modules
14
+ - **Storage**: JSON file at `~/.sshya/sshm.json`
15
+ - **Dependencies**: commander, inquirer, chalk, fuse.js, zod
16
+
17
+ ## Key Components
18
+
19
+ ### Database (`src/database.ts`)
20
+ - `Connection` interface with id, alias, user, host, key_path, port, remote_path
21
+ - Zod schema validation for data integrity
22
+ - Home directory expansion for paths (`~` → `/home/user`)
23
+ - CRUD operations with automatic normalization
24
+
25
+ ### Commands (`index.ts`)
26
+ - `add`/`remove`/`update`/`list`: Manage connections
27
+ - `print [alias]`: Output SSH command for scripting/fzf
28
+ - `import`/`export`: JSON file operations
29
+ - `fzf`/`instructions`: Shell integration setup
30
+
31
+ ### Interactive Features
32
+ - Fuzzy search for connection selection (fuse.js)
33
+ - Real-time validation with Zod schemas
34
+ - Colorized output (chalk)
35
+ - Graceful error handling with process.exit codes
36
+
37
+ ## Usage Patterns
38
+ 1. **Direct**: `sshya add` → Interactive prompt flow
39
+ 2. **fzf Integration**: Shell function with `sshya print` for fast selection
40
+ 3. **Scripting**: `sshya print my-server` → Outputs `ssh -p 2222 -t -i ~/.ssh/key user@host`
41
+
42
+ ## Notable Implementation Details
43
+ - Interactive prompt handling with raw mode TTY for user input
44
+ - Shell-specific argument parsing (zsh vs bash) for fzf integration
45
+ - Automatic path normalization and validation
46
+ - Usage tracking via `lastUsed` timestamps
47
+ - Always includes `-t` flag for proper pseudo-terminal allocation
48
+ - Enhanced error handling and debugging for fzf integration issues
49
+
50
+ ## Build & Installation
51
+ ```bash
52
+ bun run build # Creates bin/sshya binary
53
+ sudo mv bin/sshya /usr/local/bin/s # Global access
54
+ ```
55
+
56
+ This tool prioritizes developer experience with fast, keyboard-driven workflows while maintaining robust error handling and cross-platform compatibility.
package/README.md CHANGED
@@ -4,9 +4,9 @@ This project is a command-line interface (CLI) tool named `sshya` for managing S
4
4
 
5
5
  ## Core Functionality
6
6
  - Add, remove, update, and list SSH connection configurations (alias, user, host, port, key path).
7
- - Connect to saved SSH connections.
7
+ - Generate SSH command strings for saved connections.
8
8
  - Import and export connections from/to a JSON file.
9
- - A key feature is the interactive `ssh` connection, which has been optimized to provide a smooth, responsive typing experience and correct terminal resizing behavior.
9
+ - A key feature is the fzf-based launcher that provides fast, keyboard-driven connection selection and execution using your system's SSH client.
10
10
 
11
11
  ## Technology Stack
12
12
  - **Runtime:** Bun
@@ -19,16 +19,7 @@ This project is a command-line interface (CLI) tool named `sshya` for managing S
19
19
  - `zod`: for validating user input and imported data.
20
20
 
21
21
  ## Implementation Details
22
- The main application logic is in `index.ts`. It uses `child_process.spawn` to run the `ssh` command. Significant effort was made to fix issues with the interactive SSH session, including:
23
- - Laggy typing and missing keystrokes.
24
- - Incorrect terminal behavior on window resizing.
25
-
26
- The final solution involves:
27
- 1. Spawning the `ssh` process with `stdio: 'pipe'`.
28
- 2. Setting the local terminal to `rawMode` (`process.stdin.setRawMode(true)`).
29
- 3. Piping `stdin`, `stdout`, and `stderr` between the main process and the child `ssh` process.
30
- 4. Manually listening for `resize` events on `process.stdout` and forwarding them to the `ssh` child process by sending a `SIGWINCH` signal.
31
- 5. Using specific `ssh` command-line options (`-tt`, `ServerAliveInterval`, etc.) to ensure a stable and responsive interactive session.
22
+ The main application logic is in `index.ts`. The tool generates SSH command strings that are executed by your system's SSH client, providing a clean separation between connection management and actual SSH execution.
32
23
 
33
24
  ## fzf-based launcher
34
25
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "sshya",
3
3
  "module": "index.ts",
4
4
  "type": "module",
5
- "version": "0.2.0",
5
+ "version": "0.2.1",
6
6
  "devDependencies": {
7
7
  "@types/bun": "latest",
8
8
  "@types/chalk": "^2.2.4"
@@ -17,6 +17,8 @@ export async function listConnectionsPrompt(options?: { oneline?: boolean; names
17
17
  if (c.port) {
18
18
  parts.push('-p', String(c.port));
19
19
  }
20
+ // Always add -t for interactive terminal allocation (required for fzf integration)
21
+ parts.push('-t');
20
22
  parts.push(`${c.user}@${c.host}`);
21
23
  if (c.remote_path) {
22
24
  const remoteCommand = buildRemoteCommand({
@@ -24,7 +26,7 @@ export async function listConnectionsPrompt(options?: { oneline?: boolean; names
24
26
  postCommand: 'exec "$SHELL" -l',
25
27
  });
26
28
  if (remoteCommand) {
27
- parts.push('-t', remoteCommand);
29
+ parts.push(remoteCommand);
28
30
  }
29
31
  }
30
32
  const args = parts.join(' ');
@@ -21,6 +21,8 @@ export async function printConnectionsPrompt(alias?: string) {
21
21
  if (connection.port) {
22
22
  parts.push('-p', String(connection.port));
23
23
  }
24
+ // Always add -t for interactive terminal allocation (required for fzf integration)
25
+ parts.push('-t');
24
26
  parts.push(`${connection.user}@${connection.host}`);
25
27
  if (connection.remote_path) {
26
28
  const remoteCommand = buildRemoteCommand({
@@ -28,7 +30,7 @@ export async function printConnectionsPrompt(alias?: string) {
28
30
  postCommand: 'exec "$SHELL" -l',
29
31
  });
30
32
  if (remoteCommand) {
31
- parts.push('-t', remoteCommand);
33
+ parts.push(remoteCommand);
32
34
  }
33
35
  }
34
36
  console.log(parts.join(' '));
@@ -6,7 +6,7 @@ export function printFzfInstructions() {
6
6
  const snippet = [
7
7
  '_fzf_sshya() {',
8
8
  ' local line alias userhost command oldstty',
9
- ' # Temporarily disable XON/XOFF so Ctrl-S works, then restore',
9
+ ' # Disable XON/XOFF for Ctrl-S binding',
10
10
  ' oldstty=$(stty -g 2>/dev/null)',
11
11
  ' stty -ixon 2>/dev/null || true',
12
12
  ' run_sshya() {',
@@ -21,20 +21,9 @@ export function printFzfInstructions() {
21
21
  " userhost=${line#*$'\\t'}; userhost=${userhost%%$'\\t'*}",
22
22
  " command=$(run_sshya print \"$alias\")",
23
23
  " echo \"Connecting: $alias ($userhost)\"",
24
- " # Ensure ZLE releases the terminal before starting interactive ssh",
25
24
  " if [ -n \"$ZSH_VERSION\" ]; then",
26
- " local -a _args",
27
- " local remote_cmd=\"\"",
28
- " # Check if command contains -t for remote command",
29
- " if [[ \"$command\" == *' -t '* ]]; then",
30
- " # Split at -t, parse only SSH args, keep remote command as-is",
31
- " _args=(${(zQ)command%% -t *})",
32
- " remote_cmd=\"${command#* -t }\"",
33
- " ssh \"${_args[@]}\" -t \"$remote_cmd\"",
34
- " else",
35
- " _args=(${(zQ)command})",
36
- " ssh \"${_args[@]}\"",
37
- " fi",
25
+ " local -a _args; _args=(${(zQ)command})",
26
+ " ssh \"${_args[@]}\"",
38
27
  " zle -R -c",
39
28
  " else",
40
29
  " read -r -a __args <<< \"$command\"",
@@ -42,7 +31,7 @@ export function printFzfInstructions() {
42
31
  " fi",
43
32
  " stty \"$oldstty\" 2>/dev/null || true",
44
33
  '}',
45
- '# zsh widget binding (recommended):',
34
+ '# Zsh widget binding',
46
35
  'zle -N _fzf_sshya',
47
36
  "bindkey '^S' _fzf_sshya",
48
37
  ].join('\n');
package/src/ssh.ts DELETED
@@ -1,23 +0,0 @@
1
- import type { Connection } from './database';
2
- import { expandHomePath } from './database';
3
- import { buildRemoteCommand } from './helpers/shell';
4
-
5
- export function buildSSHArgs(connection: Connection) {
6
- const args: (string | number)[] = [];
7
- if (connection.key_path) {
8
- const expandedKeyPath = expandHomePath(connection.key_path);
9
- args.push('-i', expandedKeyPath ?? connection.key_path);
10
- }
11
- if (connection.port) args.push('-p', connection.port);
12
- args.push(`${connection.user}@${connection.host}`);
13
- if (connection.remote_path) {
14
- const remoteCommand = buildRemoteCommand({
15
- remotePath: connection.remote_path,
16
- postCommand: 'exec "$SHELL" -l',
17
- });
18
- if (remoteCommand) {
19
- args.push('-t', remoteCommand);
20
- }
21
- }
22
- return args.map(String);
23
- }