sshya 0.2.5 → 0.3.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # SSHya CLI - SSH Connection Manager
2
2
 
3
- **Purpose**: CLI tool for managing SSH connections with aliases, interactive prompts, and fzf integration.
3
+ **Purpose**: CLI tool for managing SSH connections with aliases, interactive prompts, and optional fzf selection.
4
4
 
5
5
  ## Core Features
6
6
  - **Connection Management**: Add, remove, update, list SSH connections with custom aliases
@@ -15,19 +15,21 @@
15
15
  ### Database (`src/database.ts`)
16
16
  - `Connection` interface with id, alias, user, host, key_path, port, remote_path
17
17
 
18
- ### Commands (`index.ts`)
18
+ ### Commands (`index.ts` / `s.ts`)
19
19
  - `add`/`remove`/`update`/`list`: Manage connections
20
- - `print [alias]`: Output SSH command for scripting/fzf
20
+ - `print [alias]`: Output SSH command for scripting
21
21
  - `import`/`export`: JSON file operations
22
- - `fzf`/`instructions`: Shell integration setup
22
+ - `fzf`: In-process fzf picker that connects to the selected alias
23
+ - `s`: Shortcut entrypoint that always runs `connect` (interactive picker or alias)
23
24
 
24
25
  ## Usage Patterns
25
26
  1. **Direct**: `sshya add` → Interactive prompt flow
26
- 2. **fzf Integration**: Shell function with `sshya print` for fast selection
27
- 3. **Scripting**: `sshya print my-server` → Outputs `ssh -p 2222 -t -i ~/.ssh/key user@host`
27
+ 2. **Fast connect**: `s` or `sshya connect` built-in fuzzy picker (no shell rc changes)
28
+ 3. **fzf**: `sshya fzf` → fzf-based selection, then the same connect flow
29
+ 4. **Scripting**: `sshya print my-server` → Outputs `ssh -p 2222 -t -i ~/.ssh/key user@host`
28
30
 
29
31
  ## Notable Implementation Details
30
- - Shell-specific argument parsing (zsh vs bash) for fzf integration
32
+ - `package.json` bins: `sshya` `index.ts`, `s` `s.ts`
31
33
  - Usage tracking via `lastUsed` timestamps
32
34
  - Always includes `-t` flag for proper pseudo-terminal allocation
33
35
  - **High-performance optimizations**: ~0.2ms processing time for 8 connections with caching and pre-computation
package/README.md CHANGED
@@ -4,11 +4,11 @@ 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, remote path).
7
- - Connect to saved connections with `sshya connect` (aliases: `ssh`, `go`).
7
+ - Connect to saved connections with `s` or `sshya connect` (aliases: `ssh`, `go`).
8
8
  - Copy files through saved connections with `sshya copy` (aliases: `sc`, `scp`).
9
9
  - Generate SSH command strings for scripting with `sshya print`.
10
10
  - Import and export connections from/to a JSON file.
11
- - fzf-based launcher for fast, keyboard-driven connection selection.
11
+ - Optional fzf picker with `sshya fzf`.
12
12
 
13
13
  ## Technology Stack
14
14
  - **Runtime:** Bun
@@ -23,47 +23,25 @@ This project is a command-line interface (CLI) tool named `sshya` for managing S
23
23
  ## Implementation Details
24
24
  The main application logic is in `index.ts`. SSH arguments are built in-process and passed directly to your system `ssh` binary, avoiding fragile shell string parsing.
25
25
 
26
- ## fzf-based launcher
26
+ ## Connecting
27
27
 
28
- You can enable a fast, keyboard-driven launcher for your saved connections using `fzf`.
29
-
30
- ### Quick setup
31
-
32
- 1. Ensure `fzf` is installed.
33
- 2. Print the shell snippet:
34
-
35
- ```bash
36
- sshya fzf
37
- # or the aliases
38
- sshya instructions
39
- sshya install
40
- ```
41
-
42
- 3. Copy the printed snippet into your `~/.zshrc` or `~/.bashrc` and reload your shell.
43
-
44
- ### How it works
45
-
46
- - The function displays your connections via `sshya list --oneline --names`, showing alias and user@host.
47
- - After you pick an entry with `fzf`, it runs `sshya connect <alias>`.
48
- - `sshya connect` builds SSH arguments in-process and spawns your system `ssh` with inherited stdio, so TTY behavior matches typing the command yourself.
49
-
50
- ### Usage
51
-
52
- After adding the snippet to your shell rc and reloading, simply run:
28
+ Install exposes two commands: `sshya` for management, and `s` as a shortcut to connect. No shell rc changes are required.
53
29
 
54
30
  ```bash
55
- s
31
+ s # interactive picker (type to filter)
32
+ s my-server # connect by alias
33
+ sshya connect # same picker
34
+ sshya connect my-server
56
35
  ```
57
36
 
58
- This will launch the fzf interface for selecting and connecting to your saved SSH connections.
59
-
60
- You can also connect directly without fzf:
37
+ `s` and `sshya connect` use the built-in fuzzy picker. If you prefer fzf:
61
38
 
62
39
  ```bash
63
- sshya connect my-server
64
- sshya connect # interactive picker
40
+ sshya fzf
65
41
  ```
66
42
 
43
+ That requires `fzf` on your PATH. After you pick an entry, it runs the same in-process `connect` flow and spawns your system `ssh` with inherited stdio.
44
+
67
45
  ### Copy files
68
46
 
69
47
  Use stored connections for `scp` uploads and downloads:
@@ -75,18 +53,14 @@ sshya copy --download my-server /tmp/remote.txt ./
75
53
  sshya copy --recursive my-server ./dist /var/www
76
54
  ```
77
55
 
78
- The `copy` command also works as `sshya sc` or `sshya scp`. For a short no-fzf wrapper:
79
-
80
- ```bash
81
- sc() {
82
- sshya copy "$@"
83
- }
84
- ```
56
+ The `copy` command also works as `sshya sc` or `sshya scp`.
85
57
 
86
58
  Relative remote paths are resolved from the connection's saved remote working directory when one is configured.
87
59
  If an exact alias is not found, `connect` and `copy` use the first fuzzy match from the same search used by the interactive picker.
88
60
 
89
61
  ### Troubleshooting
90
62
 
91
- - If fzf filtering looks wrong, reload your shell after updating the snippet. The launcher should call `sshya connect`, not parse `sshya print` output.
63
+ - If `s` is not found, reinstall or relink the package so both bins (`sshya` and `s`) are on your PATH.
64
+ - If `s` still opens an old fzf shell function, remove the `s()` snippet from `~/.zshrc` or `~/.bashrc` so the installed binary is used.
65
+ - If `sshya fzf` fails, install `fzf` or use `s` / `sshya connect` instead.
92
66
  - If the port or key path is ignored, verify the connection with `sshya test <alias>`.
package/index.ts CHANGED
@@ -7,7 +7,7 @@ import { addConnectionPrompt, connectConnectionPrompt, copyConnectionPrompt, exp
7
7
  import { initDB } from './src/database';
8
8
  import { printConnectionsPrompt } from './src/helpers/connection';
9
9
  import { enableEscapeExit } from './src/helpers/escExit';
10
- import { printFzfInstructions } from './src/helpers/fzf';
10
+ import { runFzfLauncher } from './src/helpers/fzf';
11
11
  // built-in SSH handler removed
12
12
 
13
13
  const version = pkg?.version ?? '1.0.0';
@@ -93,11 +93,8 @@ program
93
93
 
94
94
  program
95
95
  .command('fzf')
96
- .aliases(['instructions', 'install'])
97
- .description('Show instructions to enable fzf-based connection launcher')
98
- .action(() => {
99
- printFzfInstructions();
100
- });
96
+ .description('Select a connection with fzf and connect')
97
+ .action(runFzfLauncher);
101
98
 
102
99
  program
103
100
  .command('path')
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.5",
5
+ "version": "0.3.0",
6
6
  "description": "CLI for managing SSH connections with aliases, interactive prompts, and fzf integration",
7
7
  "devDependencies": {
8
8
  "@types/bun": "latest",
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "scripts": {
15
15
  "start": "bun run index.ts",
16
- "build": "bun build index.ts --compile --outfile bin/sshya"
16
+ "build": "bun build index.ts --compile --outfile bin/sshya && bun build s.ts --compile --outfile bin/s"
17
17
  },
18
18
  "dependencies": {
19
19
  "@inquirer/search": "^3.0.17",
@@ -28,6 +28,6 @@
28
28
  },
29
29
  "bin": {
30
30
  "sshya": "index.ts",
31
- "s": "index.ts"
31
+ "s": "s.ts"
32
32
  }
33
33
  }
package/s.ts ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { connectConnectionPrompt } from './src/connection';
4
+ import { initDB } from './src/database';
5
+ import { enableEscapeExit } from './src/helpers/escExit';
6
+
7
+ if (process.stdin.isTTY && process.stdout.isTTY) {
8
+ enableEscapeExit();
9
+ }
10
+
11
+ initDB();
12
+
13
+ const rawArgs = process.argv.slice(2);
14
+ const looksLikeBunShim = rawArgs[0] === 'bun' && rawArgs[1] === 'run';
15
+ const userArgs = looksLikeBunShim ? rawArgs.slice(3) : rawArgs;
16
+ const arg = userArgs[0];
17
+
18
+ if (arg === '-h' || arg === '--help') {
19
+ console.log('Usage: s [alias]');
20
+ console.log('Connect to a saved SSH connection. Omit alias to pick interactively.');
21
+ process.exit(0);
22
+ }
23
+
24
+ await connectConnectionPrompt(arg);
@@ -1,27 +1,67 @@
1
1
  import chalk from 'chalk';
2
+ import { spawn } from 'child_process';
3
+ import { connectConnectionPrompt } from '../connection/connect';
4
+ import { getConnections } from '../database';
2
5
 
3
- export function printFzfInstructions() {
4
- console.log(chalk.yellow('To enable a quick fzf-powered SSH launcher, add this to your shell rc:'));
5
- console.log('\n' + chalk.bold('~/.bashrc or ~/.zshrc') + '\n');
6
- const snippet = [
7
- 's() {',
8
- ' local line alias userhost',
9
- ' run_sshya() {',
10
- ' stdbuf -oL sshya "$@"',
11
- ' }',
12
- " line=$(run_sshya list --oneline --names | fzf --with-nth=1,2 --nth=1,2 --delimiter=$'\\t') || return",
13
- " alias=${line%%$'\\t'*}",
14
- " userhost=${line#*$'\\t'}; userhost=${userhost%%$'\\t'*}",
15
- " echo \"Connecting: $alias ($userhost)\"",
16
- " run_sshya connect \"$alias\"",
17
- '}',
18
- ].join('\n');
19
- console.log(chalk.cyan(snippet));
20
- console.log('\n' + chalk.green('Notes:'));
21
- console.log('- This expects ' + chalk.bold('fzf') + ' to be installed.');
22
- console.log('- After adding this to your shell rc, reload with: ' + chalk.cyan('source ~/.zshrc') + ' or ' + chalk.cyan('source ~/.bashrc'));
23
- console.log('- Then simply run ' + chalk.bold('s') + ' to launch the fzf interface');
24
- process.exit(0);
6
+ const FZF_ARGS = [
7
+ '--with-nth=1,2',
8
+ '--nth=1,2',
9
+ '--delimiter=\t',
10
+ ];
11
+
12
+ export async function runFzfLauncher(): Promise<void> {
13
+ const connections = getConnections()
14
+ .sort((a, b) => (b.lastUsed ?? 0) - (a.lastUsed ?? 0));
15
+
16
+ if (connections.length === 0) {
17
+ console.log(chalk.yellow('No connections found. Add one with "sshya add"'));
18
+ process.exit(0);
19
+ }
20
+
21
+ const input = connections
22
+ .map((c) => `${c.alias}\t${c.user}@${c.host}`)
23
+ .join('\n');
24
+
25
+ const alias = await pickAliasWithFzf(input);
26
+ if (!alias) {
27
+ process.exit(0);
28
+ }
29
+
30
+ await connectConnectionPrompt(alias);
25
31
  }
26
32
 
33
+ function pickAliasWithFzf(input: string): Promise<string | undefined> {
34
+ return new Promise((resolve) => {
35
+ const child = spawn('fzf', FZF_ARGS, { stdio: ['pipe', 'pipe', 'inherit'] });
27
36
 
37
+ child.on('error', (error: NodeJS.ErrnoException) => {
38
+ if (error.code === 'ENOENT') {
39
+ console.error(chalk.red('fzf is not installed. Use "s" or "sshya connect" instead.'));
40
+ process.exit(1);
41
+ }
42
+ console.error(chalk.red(`Failed to start fzf: ${error.message}`));
43
+ process.exit(1);
44
+ });
45
+
46
+ let stdout = '';
47
+ child.stdout.on('data', (chunk: Buffer | string) => {
48
+ stdout += chunk.toString();
49
+ });
50
+
51
+ child.stdin.on('error', () => {
52
+ // fzf may close stdin early when the user cancels.
53
+ });
54
+ child.stdin.write(input);
55
+ child.stdin.end();
56
+
57
+ child.on('close', (code) => {
58
+ if (code !== 0) {
59
+ resolve(undefined);
60
+ return;
61
+ }
62
+
63
+ const alias = stdout.trim().split('\t')[0];
64
+ resolve(alias || undefined);
65
+ });
66
+ });
67
+ }