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 +56 -0
- package/README.md +3 -12
- package/package.json +1 -1
- package/src/connection/list.ts +3 -1
- package/src/helpers/connection.ts +3 -1
- package/src/helpers/fzf.ts +4 -15
- package/src/ssh.ts +0 -23
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
|
-
-
|
|
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
|
|
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`.
|
|
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
package/src/connection/list.ts
CHANGED
|
@@ -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(
|
|
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(
|
|
33
|
+
parts.push(remoteCommand);
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
console.log(parts.join(' '));
|
package/src/helpers/fzf.ts
CHANGED
|
@@ -6,7 +6,7 @@ export function printFzfInstructions() {
|
|
|
6
6
|
const snippet = [
|
|
7
7
|
'_fzf_sshya() {',
|
|
8
8
|
' local line alias userhost command oldstty',
|
|
9
|
-
' #
|
|
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
|
-
"
|
|
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
|
-
'#
|
|
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
|
-
}
|