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 +9 -7
- package/README.md +16 -42
- package/index.ts +3 -6
- package/package.json +3 -3
- package/s.ts +24 -0
- package/src/helpers/fzf.ts +62 -22
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
|
|
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
|
|
20
|
+
- `print [alias]`: Output SSH command for scripting
|
|
21
21
|
- `import`/`export`: JSON file operations
|
|
22
|
-
- `fzf
|
|
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. **
|
|
27
|
-
3. **
|
|
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
|
-
-
|
|
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
|
|
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
|
-
##
|
|
26
|
+
## Connecting
|
|
27
27
|
|
|
28
|
-
|
|
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
|
-
|
|
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
|
|
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`.
|
|
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
|
|
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 {
|
|
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
|
-
.
|
|
97
|
-
.
|
|
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.
|
|
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": "
|
|
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);
|
package/src/helpers/fzf.ts
CHANGED
|
@@ -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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
+
}
|