@judaharagao/opencode-ssh 1.0.0 → 1.2.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/README.md +44 -1
- package/dist/config/schema.d.ts +16 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +12 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -2
- package/dist/index.js.map +1 -1
- package/dist/ssh/config.d.ts +60 -0
- package/dist/ssh/config.d.ts.map +1 -0
- package/dist/ssh/config.js +174 -0
- package/dist/ssh/config.js.map +1 -0
- package/dist/ssh/connection.d.ts +27 -7
- package/dist/ssh/connection.d.ts.map +1 -1
- package/dist/ssh/connection.js +111 -46
- package/dist/ssh/connection.js.map +1 -1
- package/dist/ssh/known_hosts.d.ts +25 -0
- package/dist/ssh/known_hosts.d.ts.map +1 -0
- package/dist/ssh/known_hosts.js +160 -0
- package/dist/ssh/known_hosts.js.map +1 -0
- package/dist/ssh/proxy.d.ts +30 -0
- package/dist/ssh/proxy.d.ts.map +1 -0
- package/dist/ssh/proxy.js +133 -0
- package/dist/ssh/proxy.js.map +1 -0
- package/dist/ssh/ratelimit.d.ts +32 -0
- package/dist/ssh/ratelimit.d.ts.map +1 -0
- package/dist/ssh/ratelimit.js +63 -0
- package/dist/ssh/ratelimit.js.map +1 -0
- package/dist/tools.d.ts +7 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +200 -55
- package/dist/tools.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -12,6 +12,11 @@ SSH access plugin for OpenCode — secure remote command execution with command
|
|
|
12
12
|
- 📤 **File Transfer** — Upload and download files via SCP/SFTP
|
|
13
13
|
- 🎯 **Command Safety Checker** — Preview command safety before execution
|
|
14
14
|
- ⚙️ **Configurable Policies** — Add custom blocklist/allowlist patterns
|
|
15
|
+
- 🛤️ **Proxy Support** — `ProxyJump` and `ProxyCommand` from your ssh config
|
|
16
|
+
- 🛡️ **Host Key Verification** — optional `strict_host_key` MITM protection via `known_hosts`
|
|
17
|
+
- ⏳ **Rate Limiting** — per-host command rate limit and cooldown
|
|
18
|
+
- 🔁 **Auto-Reconnect** — dropped sessions reconnect automatically on the next command
|
|
19
|
+
- 🔐 **Credential Hygiene** — passwords are cleared from memory after a successful handshake
|
|
15
20
|
|
|
16
21
|
## Installation
|
|
17
22
|
|
|
@@ -48,6 +53,37 @@ Or with configuration:
|
|
|
48
53
|
| `audit_enabled` | boolean | `true` | Enable audit logging |
|
|
49
54
|
| `blocklist_extra` | string[] | `[]` | Additional regex patterns to block |
|
|
50
55
|
| `allowlist` | string[] | `[]` | Additional allowed patterns (restricted mode) |
|
|
56
|
+
| `ssh_config_path` | string | `~/.ssh/config` | Path to the SSH config file used to resolve host aliases, defaults, proxies, and auto-connect targets |
|
|
57
|
+
| `auto_connect` | boolean | `false` | Connect automatically at startup to every `Host` entry in the ssh config that has a `HostName` (with retry/backoff) |
|
|
58
|
+
| `auto_reconnect` | boolean | `true` | Reconnect a dropped session automatically before the next `ssh.exec`/`ssh.upload`/`ssh.download` (key-authenticated sessions) |
|
|
59
|
+
| `strict_host_key` | boolean | `false` | Reject connections whose host key is not an exact match in `~/.ssh/known_hosts` (MITM protection) |
|
|
60
|
+
| `rate_limit_per_minute` | number | `120` | Max commands allowed per host per minute |
|
|
61
|
+
| `cooldown_seconds` | number | `0` | Minimum delay (seconds) between commands on the same host |
|
|
62
|
+
|
|
63
|
+
## SSH Config Integration
|
|
64
|
+
|
|
65
|
+
The plugin can reuse your existing `~/.ssh/config` (or a custom path via `ssh_config_path`):
|
|
66
|
+
|
|
67
|
+
- **Host resolution** — `ssh.connect(host="myalias")` resolves the alias to its `HostName`, and automatically applies the `User`, `Port` and `IdentityFile` from the config. `username`, `port` and `auth_method`/`key_path` become optional when configured.
|
|
68
|
+
- **ProxyJump / ProxyCommand** — `ProxyJump` and `ProxyCommand` directives in the ssh config are honored, so connections route through bastion/jump hosts transparently.
|
|
69
|
+
- **Auto-connect** — with `"auto_connect": true`, the plugin connects at startup to every `Host` entry that has a `HostName`, using the configured key (or `~/.ssh/id_rsa`). Unreachable hosts are retried with backoff and skipped without blocking startup.
|
|
70
|
+
- **Missing file alert** — if `ssh_config_path` points to a file that does not exist, the plugin warns on connect instead of silently ignoring the setting.
|
|
71
|
+
|
|
72
|
+
Example ssh config:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
Host prod-web
|
|
76
|
+
HostName 10.0.0.10
|
|
77
|
+
User deploy
|
|
78
|
+
Port 2222
|
|
79
|
+
IdentityFile ~/.ssh/prod_key
|
|
80
|
+
|
|
81
|
+
Host prod-db
|
|
82
|
+
HostName 10.0.0.50
|
|
83
|
+
ProxyJump jumpuser@bastion:2200
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
With `ssh.connect(host="prod-web")` the plugin connects to `deploy@10.0.0.10:2222` using `~/.ssh/prod_key`. `prod-db` is reached tunneled through `bastion`.
|
|
51
87
|
|
|
52
88
|
## Security Modes
|
|
53
89
|
|
|
@@ -136,6 +172,12 @@ ssh.audit_log(session_id="prod-server", command_filter="docker")
|
|
|
136
172
|
|
|
137
173
|
## Security
|
|
138
174
|
|
|
175
|
+
### Host Key Verification
|
|
176
|
+
With `"strict_host_key": true` the plugin verifies the remote server's host key against `~/.ssh/known_hosts` before completing the handshake:
|
|
177
|
+
- a **matching** key → connection allowed;
|
|
178
|
+
- a **changed** key (possible MITM) → connection refused;
|
|
179
|
+
- an **unknown** host → connection refused (add the host key to `known_hosts` first, e.g. via `ssh-keyscan host >> ~/.ssh/known_hosts`).
|
|
180
|
+
|
|
139
181
|
### Destructive Commands (100% Blocked)
|
|
140
182
|
These commands are **permanently blocked** with no exceptions:
|
|
141
183
|
|
|
@@ -162,7 +204,8 @@ These commands require your explicit approval each time:
|
|
|
162
204
|
### Credential Safety
|
|
163
205
|
- Passwords and SSH keys are **never** stored in logs or output
|
|
164
206
|
- Session info only contains host, username, and port
|
|
165
|
-
-
|
|
207
|
+
- Passwords are cleared from memory right after a successful handshake
|
|
208
|
+
- For this reason password-authenticated sessions do **not** auto-reconnect (re-issue `ssh.connect`); key-authenticated sessions reconnect automatically from the on-disk key
|
|
166
209
|
- All sessions are destroyed when the plugin is disposed
|
|
167
210
|
|
|
168
211
|
## Audit Log
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -27,6 +27,14 @@ export declare const SshPluginConfig: z.ZodObject<{
|
|
|
27
27
|
ssh_config_path: z.ZodOptional<z.ZodString>;
|
|
28
28
|
/** Auto-connect on plugin boot (requires saved session) */
|
|
29
29
|
auto_connect: z.ZodDefault<z.ZodBoolean>;
|
|
30
|
+
/** Reconnect a dropped session automatically on the next command */
|
|
31
|
+
auto_reconnect: z.ZodDefault<z.ZodBoolean>;
|
|
32
|
+
/** Reject hosts whose key is not in known_hosts (MITM protection) */
|
|
33
|
+
strict_host_key: z.ZodDefault<z.ZodBoolean>;
|
|
34
|
+
/** Max commands allowed per host per minute (rate limiting) */
|
|
35
|
+
rate_limit_per_minute: z.ZodDefault<z.ZodNumber>;
|
|
36
|
+
/** Minimum delay in seconds between commands on the same host (cooldown) */
|
|
37
|
+
cooldown_seconds: z.ZodDefault<z.ZodNumber>;
|
|
30
38
|
}, "strict", z.ZodTypeAny, {
|
|
31
39
|
mode: "full" | "restricted" | "read_only";
|
|
32
40
|
max_sessions: number;
|
|
@@ -35,6 +43,10 @@ export declare const SshPluginConfig: z.ZodObject<{
|
|
|
35
43
|
blocklist_extra: string[];
|
|
36
44
|
allowlist: string[];
|
|
37
45
|
auto_connect: boolean;
|
|
46
|
+
auto_reconnect: boolean;
|
|
47
|
+
strict_host_key: boolean;
|
|
48
|
+
rate_limit_per_minute: number;
|
|
49
|
+
cooldown_seconds: number;
|
|
38
50
|
ssh_config_path?: string | undefined;
|
|
39
51
|
}, {
|
|
40
52
|
mode?: "full" | "restricted" | "read_only" | undefined;
|
|
@@ -45,6 +57,10 @@ export declare const SshPluginConfig: z.ZodObject<{
|
|
|
45
57
|
allowlist?: string[] | undefined;
|
|
46
58
|
ssh_config_path?: string | undefined;
|
|
47
59
|
auto_connect?: boolean | undefined;
|
|
60
|
+
auto_reconnect?: boolean | undefined;
|
|
61
|
+
strict_host_key?: boolean | undefined;
|
|
62
|
+
rate_limit_per_minute?: number | undefined;
|
|
63
|
+
cooldown_seconds?: number | undefined;
|
|
48
64
|
}>;
|
|
49
65
|
export type SshPluginConfigType = z.infer<typeof SshPluginConfig>;
|
|
50
66
|
export declare const DEFAULT_CONFIG: SshPluginConfigType;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;GAKG;AACH,eAAO,MAAM,YAAY,gDAA8C,CAAA;AACvE,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA;AAEvD;;GAEG;AACH,eAAO,MAAM,eAAe;IAExB,0CAA0C;;IAG1C,sCAAsC;;IAGtC,yCAAyC;;IAGzC,sCAAsC;;IAGtC,2DAA2D;;IAG3D,qEAAqE;;IAGrE,8CAA8C;;IAG9C,2DAA2D
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;GAKG;AACH,eAAO,MAAM,YAAY,gDAA8C,CAAA;AACvE,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA;AAEvD;;GAEG;AACH,eAAO,MAAM,eAAe;IAExB,0CAA0C;;IAG1C,sCAAsC;;IAGtC,yCAAyC;;IAGzC,sCAAsC;;IAGtC,2DAA2D;;IAG3D,qEAAqE;;IAGrE,8CAA8C;;IAG9C,2DAA2D;;IAG3D,oEAAoE;;IAGpE,qEAAqE;;IAGrE,+DAA+D;;IAG/D,4EAA4E;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGrE,CAAA;AAEX,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AAEjE,eAAO,MAAM,cAAc,EAAE,mBAY5B,CAAA"}
|
package/dist/config/schema.js
CHANGED
|
@@ -27,6 +27,14 @@ export const SshPluginConfig = z
|
|
|
27
27
|
ssh_config_path: z.string().optional(),
|
|
28
28
|
/** Auto-connect on plugin boot (requires saved session) */
|
|
29
29
|
auto_connect: z.boolean().default(false),
|
|
30
|
+
/** Reconnect a dropped session automatically on the next command */
|
|
31
|
+
auto_reconnect: z.boolean().default(true),
|
|
32
|
+
/** Reject hosts whose key is not in known_hosts (MITM protection) */
|
|
33
|
+
strict_host_key: z.boolean().default(false),
|
|
34
|
+
/** Max commands allowed per host per minute (rate limiting) */
|
|
35
|
+
rate_limit_per_minute: z.number().int().min(1).max(10000).default(120),
|
|
36
|
+
/** Minimum delay in seconds between commands on the same host (cooldown) */
|
|
37
|
+
cooldown_seconds: z.number().int().min(0).max(3600).default(0),
|
|
30
38
|
})
|
|
31
39
|
.strict();
|
|
32
40
|
export const DEFAULT_CONFIG = {
|
|
@@ -37,5 +45,9 @@ export const DEFAULT_CONFIG = {
|
|
|
37
45
|
blocklist_extra: [],
|
|
38
46
|
allowlist: [],
|
|
39
47
|
auto_connect: false,
|
|
48
|
+
auto_reconnect: true,
|
|
49
|
+
strict_host_key: false,
|
|
50
|
+
rate_limit_per_minute: 120,
|
|
51
|
+
cooldown_seconds: 0,
|
|
40
52
|
};
|
|
41
53
|
//# sourceMappingURL=schema.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC,CAAA;AAGvE;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC;KAC7B,MAAM,CAAC;IACN,0CAA0C;IAC1C,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC;IAElC,sCAAsC;IACtC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAExD,yCAAyC;IACzC,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAE7D,sCAAsC;IACtC,aAAa,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAExC,2DAA2D;IAC3D,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAEhD,qEAAqE;IACrE,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAE1C,8CAA8C;IAC9C,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAEtC,2DAA2D;IAC3D,YAAY,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC,CAAA;AAGvE;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC;KAC7B,MAAM,CAAC;IACN,0CAA0C;IAC1C,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC;IAElC,sCAAsC;IACtC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAExD,yCAAyC;IACzC,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAE7D,sCAAsC;IACtC,aAAa,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAExC,2DAA2D;IAC3D,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAEhD,qEAAqE;IACrE,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAE1C,8CAA8C;IAC9C,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAEtC,2DAA2D;IAC3D,YAAY,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAExC,oEAAoE;IACpE,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAEzC,qEAAqE;IACrE,eAAe,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAE3C,+DAA+D;IAC/D,qBAAqB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;IAEtE,4EAA4E;IAC5E,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;CAC/D,CAAC;KACD,MAAM,EAAE,CAAA;AAIX,MAAM,CAAC,MAAM,cAAc,GAAwB;IACjD,IAAI,EAAE,MAAM;IACZ,YAAY,EAAE,CAAC;IACf,eAAe,EAAE,EAAE;IACnB,aAAa,EAAE,IAAI;IACnB,eAAe,EAAE,EAAE;IACnB,SAAS,EAAE,EAAE;IACb,YAAY,EAAE,KAAK;IACnB,cAAc,EAAE,IAAI;IACpB,eAAe,EAAE,KAAK;IACtB,qBAAqB,EAAE,GAAG;IAC1B,gBAAgB,EAAE,CAAC;CACpB,CAAA"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;;;;;AA4DjD,wBAGC"}
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,41 @@
|
|
|
1
1
|
import { createSshTools } from "./tools.js";
|
|
2
2
|
import { createSshHooks } from "./hooks.js";
|
|
3
3
|
import { resolveConfig } from "./config/defaults.js";
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Validate that the host opencode runtime exposes the tool-building API this
|
|
6
|
+
* plugin depends on. If the runtime is too old/incompatible, tools are
|
|
7
|
+
* disabled instead of crashing at call time.
|
|
8
|
+
*/
|
|
9
|
+
function validatePluginApi(ctx) {
|
|
10
|
+
const c = ctx;
|
|
11
|
+
if (!c || typeof c !== "object") {
|
|
12
|
+
return { ok: false, reason: "Plugin context is invalid" };
|
|
13
|
+
}
|
|
14
|
+
if (typeof c.tool !== "function") {
|
|
15
|
+
return { ok: false, reason: "ctx.tool is not available — runtime too old for tool-based plugins" };
|
|
16
|
+
}
|
|
17
|
+
const toolNs = c.tool;
|
|
18
|
+
if (!toolNs.schema || typeof toolNs.schema !== "object") {
|
|
19
|
+
return { ok: false, reason: "ctx.tool.schema (Zod-backed schema helpers) is missing — incompatible @opencode-ai/plugin" };
|
|
20
|
+
}
|
|
21
|
+
return { ok: true };
|
|
22
|
+
}
|
|
23
|
+
const SshPlugin = async (ctx, options) => {
|
|
5
24
|
// ── Resolve configuration ──
|
|
6
25
|
const config = resolveConfig(options);
|
|
26
|
+
// ── Compatibility check against the host runtime ──
|
|
27
|
+
const api = validatePluginApi(ctx);
|
|
28
|
+
if (!api.ok) {
|
|
29
|
+
console.warn(`[opencode-ssh] Plugin API incompatible: ${api.reason}. SSH tools are disabled.`);
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
7
32
|
// ── Create tools and hooks with resolved config ──
|
|
8
|
-
const tools = createSshTools(config.mode, config.max_sessions, config.default_timeout, config.blocklist_extra
|
|
33
|
+
const tools = createSshTools(config.mode, config.max_sessions, config.default_timeout, config.blocklist_extra, config.ssh_config_path, config.auto_connect, {
|
|
34
|
+
strictHostKey: config.strict_host_key,
|
|
35
|
+
rateLimitPerMinute: config.rate_limit_per_minute,
|
|
36
|
+
cooldownSeconds: config.cooldown_seconds,
|
|
37
|
+
autoReconnect: config.auto_reconnect,
|
|
38
|
+
});
|
|
9
39
|
const hooks = createSshHooks(config.mode, config.blocklist_extra);
|
|
10
40
|
return {
|
|
11
41
|
tool: tools,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAGpD,MAAM,SAAS,GAAW,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAGpD;;;;GAIG;AACH,SAAS,iBAAiB,CAAC,GAAY;IACrC,MAAM,CAAC,GAAG,GAA0C,CAAA;IACpD,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,2BAA2B,EAAE,CAAA;IAC3D,CAAC;IACD,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QACjC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,oEAAoE,EAAE,CAAA;IACpG,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,CAAC,IAA4B,CAAA;IAC7C,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,2FAA2F,EAAE,CAAA;IAC3H,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAA;AACrB,CAAC;AAED,MAAM,SAAS,GAAW,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;IAC/C,8BAA8B;IAC9B,MAAM,MAAM,GAAwB,aAAa,CAAC,OAAkC,CAAC,CAAA;IAErF,qDAAqD;IACrD,MAAM,GAAG,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAA;IAClC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,2CAA2C,GAAG,CAAC,MAAM,2BAA2B,CAAC,CAAA;QAC9F,OAAO,EAAE,CAAA;IACX,CAAC;IAED,oDAAoD;IACpD,MAAM,KAAK,GAAG,cAAc,CAC1B,MAAM,CAAC,IAAI,EACX,MAAM,CAAC,YAAY,EACnB,MAAM,CAAC,eAAe,EACtB,MAAM,CAAC,eAAe,EACtB,MAAM,CAAC,eAAe,EACtB,MAAM,CAAC,YAAY,EACnB;QACE,aAAa,EAAE,MAAM,CAAC,eAAe;QACrC,kBAAkB,EAAE,MAAM,CAAC,qBAAqB;QAChD,eAAe,EAAE,MAAM,CAAC,gBAAgB;QACxC,aAAa,EAAE,MAAM,CAAC,cAAc;KACrC,CACF,CAAA;IACD,MAAM,KAAK,GAAG,cAAc,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,eAAe,CAAC,CAAA;IAEjE,OAAO;QACL,IAAI,EAAE,KAAK;QACX,GAAG,KAAK;KACT,CAAA;AACH,CAAC,CAAA;AAED,eAAe;IACb,EAAE,EAAE,cAAc;IAClB,MAAM,EAAE,SAAS;CAClB,CAAA"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export interface SshConfigEntry {
|
|
2
|
+
/** Host patterns from the "Host" line (aliases / globs). */
|
|
3
|
+
patterns: string[];
|
|
4
|
+
/** Real hostname to connect to (HostName). */
|
|
5
|
+
hostName?: string;
|
|
6
|
+
/** User (User). */
|
|
7
|
+
user?: string;
|
|
8
|
+
/** Port (Port). */
|
|
9
|
+
port?: number;
|
|
10
|
+
/** Identity file (IdentityFile). */
|
|
11
|
+
identityFile?: string;
|
|
12
|
+
/** ProxyJump: comma-separated list of user@host:port hops. */
|
|
13
|
+
proxyJump?: string;
|
|
14
|
+
/** ProxyCommand: external command to open the tunnel. */
|
|
15
|
+
proxyCommand?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface LoadedSshConfig {
|
|
18
|
+
entries: SshConfigEntry[];
|
|
19
|
+
path: string;
|
|
20
|
+
/** Whether the configured ssh config file exists on disk. */
|
|
21
|
+
exists: boolean;
|
|
22
|
+
}
|
|
23
|
+
/** Expand a leading `~` to the current user's home directory. */
|
|
24
|
+
export declare function expandHomePath(p: string): string;
|
|
25
|
+
/** Resolve the ssh config file path, using the override or ~/.ssh/config. */
|
|
26
|
+
export declare function resolveSshConfigPath(configuredPath?: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Parse the content of an OpenSSH config file.
|
|
29
|
+
* Handles `Host` blocks and `Match` blocks, and captures
|
|
30
|
+
* HostName / User / Port / IdentityFile directives.
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseSshConfig(content: string): SshConfigEntry[];
|
|
33
|
+
/**
|
|
34
|
+
* Find the first ssh config entry matching a host name.
|
|
35
|
+
* Prefers exact aliases, then falls back to `*`/`?` glob patterns.
|
|
36
|
+
*/
|
|
37
|
+
export declare function findHostConfig(entries: SshConfigEntry[], host: string): SshConfigEntry | undefined;
|
|
38
|
+
/** Load and parse the ssh config file. Empty entry list if it does not exist. */
|
|
39
|
+
export declare function loadSshConfig(configuredPath?: string): LoadedSshConfig;
|
|
40
|
+
/**
|
|
41
|
+
* Parse a ProxyJump value ("user@host:port,user2@host2").
|
|
42
|
+
*/
|
|
43
|
+
export declare function parseProxyJump(value: string, defaultUser?: string, defaultKeyPath?: string): Array<{
|
|
44
|
+
host: string;
|
|
45
|
+
port: number;
|
|
46
|
+
username?: string;
|
|
47
|
+
keyPath?: string;
|
|
48
|
+
}>;
|
|
49
|
+
/**
|
|
50
|
+
* Concrete machines suitable for auto-connecting: `Host` entries that
|
|
51
|
+
* resolve to a real `HostName`.
|
|
52
|
+
*/
|
|
53
|
+
export declare function getAutoConnectHosts(configuredPath?: string): Array<{
|
|
54
|
+
alias: string;
|
|
55
|
+
hostName: string;
|
|
56
|
+
user?: string;
|
|
57
|
+
port?: number;
|
|
58
|
+
identityFile?: string;
|
|
59
|
+
}>;
|
|
60
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/ssh/config.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,mBAAmB;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,mBAAmB;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,oCAAoC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,cAAc,EAAE,CAAA;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,6DAA6D;IAC7D,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAKhD;AAED,6EAA6E;AAC7E,wBAAgB,oBAAoB,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CAMpE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,EAAE,CA0DhE;AAaD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAelG;AAED,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,eAAe,CAItE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,MAAM,EACpB,cAAc,CAAC,EAAE,MAAM,GACtB,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA2B5E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,cAAc,CAAC,EAAE,MAAM,GACtB,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAiBjG"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSH config (~/.ssh/config) parser.
|
|
3
|
+
* Reads alias, HostName, User, Port and IdentityFile so ssh.connect
|
|
4
|
+
* can reuse your normal SSH configuration and auto_connect knows what to hook.
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync, existsSync } from "fs";
|
|
7
|
+
import { resolve as pathResolve } from "path";
|
|
8
|
+
/** Expand a leading `~` to the current user's home directory. */
|
|
9
|
+
export function expandHomePath(p) {
|
|
10
|
+
if (p.startsWith("~/")) {
|
|
11
|
+
return pathResolve(process.env.HOME || "", p.slice(2));
|
|
12
|
+
}
|
|
13
|
+
return p;
|
|
14
|
+
}
|
|
15
|
+
/** Resolve the ssh config file path, using the override or ~/.ssh/config. */
|
|
16
|
+
export function resolveSshConfigPath(configuredPath) {
|
|
17
|
+
if (configuredPath && configuredPath.trim()) {
|
|
18
|
+
const p = configuredPath.trim();
|
|
19
|
+
return p.startsWith("~/") ? expandHomePath(p) : pathResolve(p);
|
|
20
|
+
}
|
|
21
|
+
return pathResolve(process.env.HOME || "", ".ssh", "config");
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Parse the content of an OpenSSH config file.
|
|
25
|
+
* Handles `Host` blocks and `Match` blocks, and captures
|
|
26
|
+
* HostName / User / Port / IdentityFile directives.
|
|
27
|
+
*/
|
|
28
|
+
export function parseSshConfig(content) {
|
|
29
|
+
const entries = [];
|
|
30
|
+
let current = null;
|
|
31
|
+
for (const rawLine of content.split("\n")) {
|
|
32
|
+
const line = rawLine.trim();
|
|
33
|
+
if (!line || line.startsWith("#"))
|
|
34
|
+
continue;
|
|
35
|
+
const match = line.match(/^(\S+)\s+(.+)$/);
|
|
36
|
+
if (!match)
|
|
37
|
+
continue;
|
|
38
|
+
const key = match[1].toLowerCase();
|
|
39
|
+
const value = match[2].trim();
|
|
40
|
+
if (key === "host") {
|
|
41
|
+
const tokens = value.match(/"[^"]*"|\S+/g) || [];
|
|
42
|
+
current = {
|
|
43
|
+
patterns: tokens.map((t) => t.startsWith('"') && t.endsWith('"') ? t.slice(1, -1) : t),
|
|
44
|
+
};
|
|
45
|
+
entries.push(current);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (key === "match") {
|
|
49
|
+
current = { patterns: [] };
|
|
50
|
+
entries.push(current);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (!current)
|
|
54
|
+
continue;
|
|
55
|
+
switch (key) {
|
|
56
|
+
case "hostname":
|
|
57
|
+
current.hostName = value;
|
|
58
|
+
break;
|
|
59
|
+
case "user":
|
|
60
|
+
current.user = value;
|
|
61
|
+
break;
|
|
62
|
+
case "port": {
|
|
63
|
+
const num = Number.parseInt(value, 10);
|
|
64
|
+
if (!Number.isNaN(num))
|
|
65
|
+
current.port = num;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
case "identityfile":
|
|
69
|
+
current.identityFile = value;
|
|
70
|
+
break;
|
|
71
|
+
case "proxyjump":
|
|
72
|
+
current.proxyJump = value;
|
|
73
|
+
break;
|
|
74
|
+
case "proxycommand":
|
|
75
|
+
current.proxyCommand = value;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return entries;
|
|
80
|
+
}
|
|
81
|
+
/** Turn an OpenSSH Host pattern (may contain `*` / `?`) into a RegExp. */
|
|
82
|
+
function hostPatternToRegex(pattern) {
|
|
83
|
+
let out = "";
|
|
84
|
+
for (const ch of pattern) {
|
|
85
|
+
if (ch === "*")
|
|
86
|
+
out += ".*";
|
|
87
|
+
else if (ch === "?")
|
|
88
|
+
out += ".";
|
|
89
|
+
else
|
|
90
|
+
out += ch.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
91
|
+
}
|
|
92
|
+
return new RegExp(`^${out}$`, "i");
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Find the first ssh config entry matching a host name.
|
|
96
|
+
* Prefers exact aliases, then falls back to `*`/`?` glob patterns.
|
|
97
|
+
*/
|
|
98
|
+
export function findHostConfig(entries, host) {
|
|
99
|
+
const normalized = host.toLowerCase();
|
|
100
|
+
for (const entry of entries) {
|
|
101
|
+
for (const pattern of entry.patterns) {
|
|
102
|
+
if (pattern.toLowerCase() === normalized)
|
|
103
|
+
return entry;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
for (const entry of entries) {
|
|
107
|
+
for (const pattern of entry.patterns) {
|
|
108
|
+
if (pattern.includes("*") || pattern.includes("?")) {
|
|
109
|
+
if (hostPatternToRegex(pattern).test(host))
|
|
110
|
+
return entry;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
/** Load and parse the ssh config file. Empty entry list if it does not exist. */
|
|
117
|
+
export function loadSshConfig(configuredPath) {
|
|
118
|
+
const path = resolveSshConfigPath(configuredPath);
|
|
119
|
+
if (!existsSync(path))
|
|
120
|
+
return { entries: [], path, exists: false };
|
|
121
|
+
return { entries: parseSshConfig(readFileSync(path, "utf-8")), path, exists: true };
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Parse a ProxyJump value ("user@host:port,user2@host2").
|
|
125
|
+
*/
|
|
126
|
+
export function parseProxyJump(value, defaultUser, defaultKeyPath) {
|
|
127
|
+
return value
|
|
128
|
+
.split(",")
|
|
129
|
+
.map((hop) => hop.trim())
|
|
130
|
+
.filter(Boolean)
|
|
131
|
+
.map((hop) => {
|
|
132
|
+
let user;
|
|
133
|
+
let rest = hop;
|
|
134
|
+
if (hop.includes("@")) {
|
|
135
|
+
const at = hop.lastIndexOf("@");
|
|
136
|
+
user = hop.slice(0, at);
|
|
137
|
+
rest = hop.slice(at + 1);
|
|
138
|
+
}
|
|
139
|
+
let port = 22;
|
|
140
|
+
let host = rest;
|
|
141
|
+
const colon = rest.match(/^(.*):(\d+)$/);
|
|
142
|
+
if (colon) {
|
|
143
|
+
host = colon[1];
|
|
144
|
+
port = Number(colon[2]);
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
host,
|
|
148
|
+
port,
|
|
149
|
+
username: user || defaultUser,
|
|
150
|
+
keyPath: defaultKeyPath,
|
|
151
|
+
};
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Concrete machines suitable for auto-connecting: `Host` entries that
|
|
156
|
+
* resolve to a real `HostName`.
|
|
157
|
+
*/
|
|
158
|
+
export function getAutoConnectHosts(configuredPath) {
|
|
159
|
+
const { entries } = loadSshConfig(configuredPath);
|
|
160
|
+
const hosts = [];
|
|
161
|
+
for (const entry of entries) {
|
|
162
|
+
if (entry.patterns.length > 0 && entry.hostName) {
|
|
163
|
+
hosts.push({
|
|
164
|
+
alias: entry.patterns[0],
|
|
165
|
+
hostName: entry.hostName,
|
|
166
|
+
user: entry.user,
|
|
167
|
+
port: entry.port,
|
|
168
|
+
identityFile: entry.identityFile,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return hosts;
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/ssh/config.ts"],"names":[],"mappings":"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"}
|
package/dist/ssh/connection.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { type ProxySpec } from "./proxy.js";
|
|
1
2
|
export interface SshConnectionConfig {
|
|
2
3
|
host: string;
|
|
3
4
|
port: number;
|
|
4
|
-
username
|
|
5
|
+
/** SSH username, resolved from args or the ssh config. */
|
|
6
|
+
username?: string;
|
|
5
7
|
authMethod: "password" | "key";
|
|
6
8
|
password?: string;
|
|
7
9
|
keyPath?: string;
|
|
@@ -10,6 +12,16 @@ export interface SshConnectionConfig {
|
|
|
10
12
|
timeout?: number;
|
|
11
13
|
/** Connection alias for display */
|
|
12
14
|
alias?: string;
|
|
15
|
+
/** Reject hosts whose key is not (an identical match) in known_hosts. */
|
|
16
|
+
strictHostKey?: boolean;
|
|
17
|
+
/** Custom path to a known_hosts file. */
|
|
18
|
+
knownHostsPath?: string;
|
|
19
|
+
/** SSH config default path, used to locate known_hosts next to it. */
|
|
20
|
+
sshConfigPath?: string;
|
|
21
|
+
/** ProxyJump / ProxyCommand support. */
|
|
22
|
+
proxy?: ProxySpec;
|
|
23
|
+
/** Automatically reconnect a dropped session on the next exec/sftp. */
|
|
24
|
+
autoReconnect?: boolean;
|
|
13
25
|
}
|
|
14
26
|
export interface SshConnectionInfo {
|
|
15
27
|
id: string;
|
|
@@ -25,21 +37,28 @@ export declare class SshConnection {
|
|
|
25
37
|
private _connected;
|
|
26
38
|
private _connectedAt?;
|
|
27
39
|
private _lastActivity?;
|
|
28
|
-
private
|
|
29
|
-
private
|
|
40
|
+
private _maxAttempts;
|
|
41
|
+
private _attempts;
|
|
42
|
+
private _proxyCleanup?;
|
|
43
|
+
private _hostKeyVerdict?;
|
|
30
44
|
constructor(id: string, config: SshConnectionConfig);
|
|
31
45
|
get id(): string;
|
|
32
46
|
get config(): SshConnectionConfig;
|
|
33
47
|
get connected(): boolean;
|
|
34
48
|
get connectedAt(): Date | undefined;
|
|
35
49
|
get lastActivity(): Date | undefined;
|
|
50
|
+
/** Result of the last known_hosts check (undefined when disabled). */
|
|
51
|
+
get hostKeyVerdict(): "ok" | "changed" | "unknown" | undefined;
|
|
36
52
|
/**
|
|
37
|
-
* Connect to the SSH server.
|
|
53
|
+
* Connect to the SSH server, with bounded retries.
|
|
38
54
|
*/
|
|
39
55
|
connect(): Promise<void>;
|
|
56
|
+
private connectWithBackoff;
|
|
57
|
+
private buildConnectConfig;
|
|
58
|
+
private openConnection;
|
|
40
59
|
/**
|
|
41
|
-
* Execute a command on the remote server.
|
|
42
|
-
*
|
|
60
|
+
* Execute a command on the remote server. Automatically reconnects a
|
|
61
|
+
* dropped session before running.
|
|
43
62
|
*/
|
|
44
63
|
exec(command: string, options?: {
|
|
45
64
|
cwd?: string;
|
|
@@ -58,7 +77,8 @@ export declare class SshConnection {
|
|
|
58
77
|
timeout?: number;
|
|
59
78
|
}): NodeJS.ReadableStream | null;
|
|
60
79
|
/**
|
|
61
|
-
* Get an SFTP session for file transfers.
|
|
80
|
+
* Get an SFTP session for file transfers. Automatically reconnects a
|
|
81
|
+
* dropped session before returning.
|
|
62
82
|
*/
|
|
63
83
|
sftp(): Promise<any>;
|
|
64
84
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/ssh/connection.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/ssh/connection.ts"],"names":[],"mappings":"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"}
|