@grackle-ai/adapter-ssh 0.72.2
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 +24 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/ssh.d.ts +49 -0
- package/dist/ssh.d.ts.map +1 -0
- package/dist/ssh.js +223 -0
- package/dist/ssh.js.map +1 -0
- package/dist/tsdoc-metadata.json +11 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# @grackle-ai/adapter-ssh
|
|
2
|
+
|
|
3
|
+
Grackle environment adapter for managing remote environments via SSH.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The SSH adapter provisions PowerLine on a remote host, opens an SSH tunnel for gRPC communication, and a reverse tunnel so remote agents can reach the local MCP server.
|
|
8
|
+
|
|
9
|
+
## Configuration
|
|
10
|
+
|
|
11
|
+
| Field | Type | Default | Description |
|
|
12
|
+
|-------|------|---------|-------------|
|
|
13
|
+
| `host` | `string` | *required* | Remote hostname or IP |
|
|
14
|
+
| `user` | `string` | current OS user | SSH username |
|
|
15
|
+
| `sshPort` | `number` | `22` | SSH port |
|
|
16
|
+
| `identityFile` | `string` | — | Path to SSH private key |
|
|
17
|
+
| `sshOptions` | `Record<string, string>` | — | Extra SSH `-o Key=Value` options |
|
|
18
|
+
| `localPort` | `number` | auto | Override local tunnel port |
|
|
19
|
+
| `env` | `Record<string, string>` | — | Extra env vars for remote PowerLine |
|
|
20
|
+
|
|
21
|
+
## Prerequisites
|
|
22
|
+
|
|
23
|
+
- SSH access to the remote host (key-based authentication recommended)
|
|
24
|
+
- `ssh` and `scp` available on PATH
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,YAAY,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/ssh.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { EnvironmentAdapter, BaseEnvironmentConfig, PowerLineConnection, ProvisionEvent, AdapterDependencies } from "@grackle-ai/adapter-sdk";
|
|
2
|
+
/** SSH-specific environment configuration. */
|
|
3
|
+
export interface SshEnvironmentConfig extends BaseEnvironmentConfig {
|
|
4
|
+
/** Remote hostname or IP address (required). */
|
|
5
|
+
host: string;
|
|
6
|
+
/** SSH username (defaults to the current OS user). */
|
|
7
|
+
user?: string;
|
|
8
|
+
/** SSH port on the remote host (defaults to 22). */
|
|
9
|
+
sshPort?: number;
|
|
10
|
+
/** Path to an SSH private key file. */
|
|
11
|
+
identityFile?: string;
|
|
12
|
+
/** Extra SSH options passed as `-o Key=Value`. */
|
|
13
|
+
sshOptions?: Record<string, string>;
|
|
14
|
+
/** Override the local tunnel port (otherwise a free port is chosen). */
|
|
15
|
+
localPort?: number;
|
|
16
|
+
/** Additional environment variables forwarded to the remote PowerLine. */
|
|
17
|
+
env?: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
/** Environment adapter that provisions and manages remote environments via SSH. */
|
|
20
|
+
export declare class SshAdapter implements EnvironmentAdapter {
|
|
21
|
+
type: string;
|
|
22
|
+
private readonly execFn;
|
|
23
|
+
private readonly sleepFn;
|
|
24
|
+
private readonly isGitHubProviderEnabled;
|
|
25
|
+
constructor(deps?: AdapterDependencies);
|
|
26
|
+
/** Provision the remote host: test connectivity, bootstrap PowerLine, open tunnel. */
|
|
27
|
+
provision(environmentId: string, config: Record<string, unknown>, powerlineToken: string): AsyncGenerator<ProvisionEvent>;
|
|
28
|
+
/**
|
|
29
|
+
* Attempt fast reconnect: probe PowerLine, restart if needed, re-open tunnel.
|
|
30
|
+
*
|
|
31
|
+
* Any failure (SSH unreachable, PowerLine won't start, tunnel error) throws
|
|
32
|
+
* and falls through to the caller, which should trigger a full provision.
|
|
33
|
+
*
|
|
34
|
+
* Minimizes SSH round trips — probe and conditional restart run in a single
|
|
35
|
+
* SSH call via `startRemotePowerLine({ probeFirst: true })`.
|
|
36
|
+
*/
|
|
37
|
+
reconnect(environmentId: string, config: Record<string, unknown>, powerlineToken: string): AsyncGenerator<ProvisionEvent>;
|
|
38
|
+
/** Connect to the PowerLine through the SSH tunnel. */
|
|
39
|
+
connect(environmentId: string, config: Record<string, unknown>, powerlineToken: string): Promise<PowerLineConnection>;
|
|
40
|
+
/** Close the SSH tunnel without stopping the remote PowerLine. */
|
|
41
|
+
disconnect(environmentId: string): Promise<void>;
|
|
42
|
+
/** Stop the remote PowerLine process and close the tunnel. */
|
|
43
|
+
stop(environmentId: string, config: Record<string, unknown>): Promise<void>;
|
|
44
|
+
/** Stop the remote PowerLine, remove artifacts, and close the tunnel. */
|
|
45
|
+
destroy(environmentId: string, config: Record<string, unknown>): Promise<void>;
|
|
46
|
+
/** Check that the tunnel is alive and the PowerLine responds to a ping. */
|
|
47
|
+
healthCheck(connection: PowerLineConnection): Promise<boolean>;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=ssh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssh.d.ts","sourceRoot":"","sources":["../src/ssh.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,cAAc,EAAE,mBAAmB,EAAgB,MAAM,yBAAyB,CAAC;AA8BjK,8CAA8C;AAC9C,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;IACjE,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AA2ID,mFAAmF;AACnF,qBAAa,UAAW,YAAW,kBAAkB;IAC5C,IAAI,EAAE,MAAM,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAgB;gBAErC,IAAI,GAAE,mBAAwB;IAMjD,sFAAsF;IACxE,SAAS,CACrB,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,cAAc,EAAE,MAAM,GACrB,cAAc,CAAC,cAAc,CAAC;IAwCjC;;;;;;;;OAQG;IACW,SAAS,CACrB,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,cAAc,EAAE,MAAM,GACrB,cAAc,CAAC,cAAc,CAAC;IAqCjC,uDAAuD;IAC1C,OAAO,CAClB,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,mBAAmB,CAAC;IAQ/B,kEAAkE;IACrD,UAAU,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7D,8DAA8D;IACjD,IAAI,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAKxF,yEAAyE;IAC5D,OAAO,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAK3F,2EAA2E;IAC9D,WAAW,CAAC,UAAU,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC;CAG5E"}
|
package/dist/ssh.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { DEFAULT_POWERLINE_PORT, DEFAULT_MCP_PORT } from "@grackle-ai/common";
|
|
2
|
+
import { ProcessTunnel, bootstrapPowerLine, connectThroughTunnel, registerTunnel, getTunnel, closeTunnel, findFreePort, remoteStop, remoteDestroy, remoteHealthCheck, startRemotePowerLine, exec as defaultExec, sleep as defaultSleep, SSH_CONNECTIVITY_TIMEOUT_MS, REMOTE_EXEC_DEFAULT_TIMEOUT_MS, } from "@grackle-ai/adapter-sdk";
|
|
3
|
+
const REMOTE_COPY_TIMEOUT_MS = 120_000;
|
|
4
|
+
/** Delay for reverse tunnels to wait for SSH to establish. */
|
|
5
|
+
const REVERSE_TUNNEL_SETTLE_MS = 3_000;
|
|
6
|
+
// ─── SSH Helpers ────────────────────────────────────────────
|
|
7
|
+
/** Build the common SSH flags shared across exec, scp, and tunnel commands. */
|
|
8
|
+
function buildSshFlags(cfg) {
|
|
9
|
+
const flags = ["-o", "BatchMode=yes", "-o", "StrictHostKeyChecking=accept-new"];
|
|
10
|
+
if (cfg.sshPort) {
|
|
11
|
+
flags.push("-p", String(cfg.sshPort));
|
|
12
|
+
}
|
|
13
|
+
if (cfg.identityFile) {
|
|
14
|
+
flags.push("-i", cfg.identityFile);
|
|
15
|
+
}
|
|
16
|
+
if (cfg.sshOptions) {
|
|
17
|
+
for (const [key, value] of Object.entries(cfg.sshOptions)) {
|
|
18
|
+
flags.push("-o", `${key}=${value}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return flags;
|
|
22
|
+
}
|
|
23
|
+
/** Build the `user@host` destination string. */
|
|
24
|
+
function buildDestination(cfg) {
|
|
25
|
+
return cfg.user ? `${cfg.user}@${cfg.host}` : cfg.host;
|
|
26
|
+
}
|
|
27
|
+
// ─── Executor ───────────────────────────────────────────────
|
|
28
|
+
/** Execute commands on a remote host via SSH. */
|
|
29
|
+
class SshExecutor {
|
|
30
|
+
cfg;
|
|
31
|
+
execFn;
|
|
32
|
+
constructor(cfg, execFn) {
|
|
33
|
+
this.cfg = cfg;
|
|
34
|
+
this.execFn = execFn;
|
|
35
|
+
}
|
|
36
|
+
/** Execute a shell command on the remote host and return trimmed stdout. */
|
|
37
|
+
async exec(command, opts) {
|
|
38
|
+
const args = [...buildSshFlags(this.cfg), buildDestination(this.cfg), command];
|
|
39
|
+
const result = await this.execFn("ssh", args, { timeout: opts?.timeout ?? REMOTE_EXEC_DEFAULT_TIMEOUT_MS });
|
|
40
|
+
return result.stdout;
|
|
41
|
+
}
|
|
42
|
+
/** Copy a local file or directory to the remote host via scp. */
|
|
43
|
+
async copyTo(localPath, remotePath) {
|
|
44
|
+
const flags = buildSshFlags(this.cfg);
|
|
45
|
+
// scp uses -P (uppercase) instead of -p for port
|
|
46
|
+
const scpFlags = flags.map((f, i) => (f === "-p" && i > 0 && flags[i - 1] !== "-o") ? "-P" : f);
|
|
47
|
+
const args = ["-r", ...scpFlags, localPath, `${buildDestination(this.cfg)}:${remotePath}`];
|
|
48
|
+
await this.execFn("scp", args, { timeout: REMOTE_COPY_TIMEOUT_MS });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// ─── Tunnel ─────────────────────────────────────────────────
|
|
52
|
+
/** SSH tunnel that forwards a local port to the remote PowerLine port. */
|
|
53
|
+
class SshTunnel extends ProcessTunnel {
|
|
54
|
+
cfg;
|
|
55
|
+
constructor(localPort, cfg, processFactory, portProbe) {
|
|
56
|
+
super(localPort, undefined, processFactory, portProbe);
|
|
57
|
+
this.cfg = cfg;
|
|
58
|
+
}
|
|
59
|
+
/** Return the ssh command and arguments for the tunnel process. */
|
|
60
|
+
spawnArgs() {
|
|
61
|
+
const flags = buildSshFlags(this.cfg);
|
|
62
|
+
const args = [
|
|
63
|
+
"-N",
|
|
64
|
+
"-L", `${this.localPort}:127.0.0.1:${DEFAULT_POWERLINE_PORT}`,
|
|
65
|
+
"-o", "ExitOnForwardFailure=yes",
|
|
66
|
+
"-o", "ServerAliveInterval=15",
|
|
67
|
+
"-o", "ServerAliveCountMax=3",
|
|
68
|
+
...flags,
|
|
69
|
+
buildDestination(this.cfg),
|
|
70
|
+
];
|
|
71
|
+
return { command: "ssh", args };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Reverse SSH tunnel: binds a port on the remote host that tunnels back to a local port.
|
|
76
|
+
* Used so agents (running on the remote host) can reach the Grackle MCP server (on the host).
|
|
77
|
+
*/
|
|
78
|
+
class SshReverseTunnel extends ProcessTunnel {
|
|
79
|
+
cfg;
|
|
80
|
+
remotePort;
|
|
81
|
+
sleepFn;
|
|
82
|
+
constructor(localPort, remotePort, cfg, sleepFn, processFactory, portProbe) {
|
|
83
|
+
super(localPort, undefined, processFactory, portProbe);
|
|
84
|
+
this.cfg = cfg;
|
|
85
|
+
this.remotePort = remotePort;
|
|
86
|
+
this.sleepFn = sleepFn;
|
|
87
|
+
}
|
|
88
|
+
/** Return the ssh command with -R for reverse port forwarding. */
|
|
89
|
+
spawnArgs() {
|
|
90
|
+
const flags = buildSshFlags(this.cfg);
|
|
91
|
+
const args = [
|
|
92
|
+
"-N",
|
|
93
|
+
"-R", `${this.remotePort}:127.0.0.1:${this.localPort}`,
|
|
94
|
+
"-o", "ExitOnForwardFailure=yes",
|
|
95
|
+
"-o", "ServerAliveInterval=15",
|
|
96
|
+
"-o", "ServerAliveCountMax=3",
|
|
97
|
+
...flags,
|
|
98
|
+
buildDestination(this.cfg),
|
|
99
|
+
];
|
|
100
|
+
return { command: "ssh", args };
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Reverse tunnels bind on the remote side, not locally.
|
|
104
|
+
* We can't probe the remote port, so wait a fixed delay for SSH to establish.
|
|
105
|
+
*/
|
|
106
|
+
async waitForReady() {
|
|
107
|
+
await this.sleepFn(REVERSE_TUNNEL_SETTLE_MS);
|
|
108
|
+
if (this.process?.exitCode !== null) {
|
|
109
|
+
throw new Error(`Reverse tunnel exited immediately with code ${this.process?.exitCode}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// ─── Adapter ────────────────────────────────────────────────
|
|
114
|
+
/** Environment adapter that provisions and manages remote environments via SSH. */
|
|
115
|
+
export class SshAdapter {
|
|
116
|
+
type = "ssh";
|
|
117
|
+
execFn;
|
|
118
|
+
sleepFn;
|
|
119
|
+
isGitHubProviderEnabled;
|
|
120
|
+
constructor(deps = {}) {
|
|
121
|
+
this.execFn = deps.exec ?? defaultExec;
|
|
122
|
+
this.sleepFn = deps.sleep ?? defaultSleep;
|
|
123
|
+
this.isGitHubProviderEnabled = deps.isGitHubProviderEnabled ?? (() => false);
|
|
124
|
+
}
|
|
125
|
+
/** Provision the remote host: test connectivity, bootstrap PowerLine, open tunnel. */
|
|
126
|
+
async *provision(environmentId, config, powerlineToken) {
|
|
127
|
+
const cfg = config;
|
|
128
|
+
if (!cfg.host) {
|
|
129
|
+
throw new Error("SSH adapter requires a 'host' in the configuration");
|
|
130
|
+
}
|
|
131
|
+
const executor = new SshExecutor(cfg, this.execFn);
|
|
132
|
+
// Test SSH connectivity
|
|
133
|
+
yield { stage: "connecting", message: `Testing SSH connectivity to ${cfg.host}...`, progress: 0.05 };
|
|
134
|
+
try {
|
|
135
|
+
await executor.exec("echo ok", { timeout: SSH_CONNECTIVITY_TIMEOUT_MS });
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
throw new Error(`Cannot reach ${cfg.host} via SSH: ${err instanceof Error ? err.message : String(err)}`);
|
|
139
|
+
}
|
|
140
|
+
// Bootstrap PowerLine on the remote host
|
|
141
|
+
yield* bootstrapPowerLine(executor, powerlineToken, {
|
|
142
|
+
extraEnv: cfg.env,
|
|
143
|
+
isGitHubProviderEnabled: this.isGitHubProviderEnabled,
|
|
144
|
+
defaultRuntime: config.defaultRuntime || undefined,
|
|
145
|
+
});
|
|
146
|
+
// Open SSH tunnel
|
|
147
|
+
const localPort = cfg.localPort || await findFreePort();
|
|
148
|
+
yield { stage: "tunneling", message: `Opening SSH tunnel on local port ${localPort}...`, progress: 0.80 };
|
|
149
|
+
const tunnel = new SshTunnel(localPort, cfg);
|
|
150
|
+
await tunnel.open();
|
|
151
|
+
// Open reverse tunnel (remote → host MCP server) for agent tool calls
|
|
152
|
+
const mcpPort = parseInt(process.env.GRACKLE_MCP_PORT || String(DEFAULT_MCP_PORT), 10);
|
|
153
|
+
const reverseTunnel = new SshReverseTunnel(mcpPort, mcpPort, cfg, this.sleepFn);
|
|
154
|
+
await reverseTunnel.open();
|
|
155
|
+
registerTunnel(environmentId, { tunnel, reverseTunnel });
|
|
156
|
+
yield { stage: "connecting", message: `Tunnel open, connecting on port ${localPort}...`, progress: 0.90 };
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Attempt fast reconnect: probe PowerLine, restart if needed, re-open tunnel.
|
|
160
|
+
*
|
|
161
|
+
* Any failure (SSH unreachable, PowerLine won't start, tunnel error) throws
|
|
162
|
+
* and falls through to the caller, which should trigger a full provision.
|
|
163
|
+
*
|
|
164
|
+
* Minimizes SSH round trips — probe and conditional restart run in a single
|
|
165
|
+
* SSH call via `startRemotePowerLine({ probeFirst: true })`.
|
|
166
|
+
*/
|
|
167
|
+
async *reconnect(environmentId, config, powerlineToken) {
|
|
168
|
+
const cfg = config;
|
|
169
|
+
if (!cfg.host) {
|
|
170
|
+
throw new Error("SSH adapter requires a 'host' in the configuration");
|
|
171
|
+
}
|
|
172
|
+
const executor = new SshExecutor(cfg, this.execFn);
|
|
173
|
+
// 1. Close any stale tunnel
|
|
174
|
+
yield { stage: "reconnecting", message: "Closing stale tunnel...", progress: 0.10 };
|
|
175
|
+
await closeTunnel(environmentId);
|
|
176
|
+
// 2. Probe + conditional restart in a single SSH call.
|
|
177
|
+
yield { stage: "reconnecting", message: `Checking PowerLine on ${cfg.host}...`, progress: 0.30 };
|
|
178
|
+
const { alreadyRunning } = await startRemotePowerLine(executor, powerlineToken, {
|
|
179
|
+
extraEnv: cfg.env,
|
|
180
|
+
probeFirst: true,
|
|
181
|
+
});
|
|
182
|
+
if (!alreadyRunning) {
|
|
183
|
+
yield { stage: "reconnecting", message: "PowerLine restarted", progress: 0.50 };
|
|
184
|
+
}
|
|
185
|
+
// 3. Open new SSH tunnel + reverse tunnel for MCP
|
|
186
|
+
const localPort = cfg.localPort || await findFreePort();
|
|
187
|
+
yield { stage: "reconnecting", message: `Opening SSH tunnel on local port ${localPort}...`, progress: 0.70 };
|
|
188
|
+
const tunnel = new SshTunnel(localPort, cfg);
|
|
189
|
+
await tunnel.open();
|
|
190
|
+
const mcpPort = parseInt(process.env.GRACKLE_MCP_PORT || String(DEFAULT_MCP_PORT), 10);
|
|
191
|
+
const reverseTunnel = new SshReverseTunnel(mcpPort, mcpPort, cfg, this.sleepFn);
|
|
192
|
+
await reverseTunnel.open();
|
|
193
|
+
registerTunnel(environmentId, { tunnel, reverseTunnel });
|
|
194
|
+
yield { stage: "reconnecting", message: "Reconnected via SSH", progress: 0.90 };
|
|
195
|
+
}
|
|
196
|
+
/** Connect to the PowerLine through the SSH tunnel. */
|
|
197
|
+
async connect(environmentId, config, powerlineToken) {
|
|
198
|
+
const state = getTunnel(environmentId);
|
|
199
|
+
if (!state) {
|
|
200
|
+
throw new Error(`No tunnel registered for environment ${environmentId}`);
|
|
201
|
+
}
|
|
202
|
+
return connectThroughTunnel(environmentId, state.tunnel.localPort, powerlineToken);
|
|
203
|
+
}
|
|
204
|
+
/** Close the SSH tunnel without stopping the remote PowerLine. */
|
|
205
|
+
async disconnect(environmentId) {
|
|
206
|
+
await closeTunnel(environmentId);
|
|
207
|
+
}
|
|
208
|
+
/** Stop the remote PowerLine process and close the tunnel. */
|
|
209
|
+
async stop(environmentId, config) {
|
|
210
|
+
const cfg = config;
|
|
211
|
+
await remoteStop(environmentId, new SshExecutor(cfg, this.execFn));
|
|
212
|
+
}
|
|
213
|
+
/** Stop the remote PowerLine, remove artifacts, and close the tunnel. */
|
|
214
|
+
async destroy(environmentId, config) {
|
|
215
|
+
const cfg = config;
|
|
216
|
+
await remoteDestroy(environmentId, new SshExecutor(cfg, this.execFn));
|
|
217
|
+
}
|
|
218
|
+
/** Check that the tunnel is alive and the PowerLine responds to a ping. */
|
|
219
|
+
async healthCheck(connection) {
|
|
220
|
+
return remoteHealthCheck(connection);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=ssh.js.map
|
package/dist/ssh.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssh.js","sourceRoot":"","sources":["../src/ssh.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// This file is read by tools that parse documentation comments conforming to the TSDoc standard.
|
|
2
|
+
// It should be published with your NPM package. It should not be tracked by Git.
|
|
3
|
+
{
|
|
4
|
+
"tsdocVersion": "0.12",
|
|
5
|
+
"toolPackages": [
|
|
6
|
+
{
|
|
7
|
+
"packageName": "@microsoft/api-extractor",
|
|
8
|
+
"packageVersion": "7.57.7"
|
|
9
|
+
}
|
|
10
|
+
]
|
|
11
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@grackle-ai/adapter-ssh",
|
|
3
|
+
"version": "0.72.2",
|
|
4
|
+
"description": "Grackle SSH environment adapter",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/nick-pape/grackle.git",
|
|
9
|
+
"directory": "packages/adapter-ssh"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"grackle",
|
|
13
|
+
"adapter",
|
|
14
|
+
"ssh",
|
|
15
|
+
"environment"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=22.0.0"
|
|
19
|
+
},
|
|
20
|
+
"type": "module",
|
|
21
|
+
"main": "dist/index.js",
|
|
22
|
+
"types": "dist/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist/"
|
|
25
|
+
],
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@grackle-ai/adapter-sdk": "0.72.2",
|
|
28
|
+
"@grackle-ai/common": "0.72.2"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@rushstack/heft": "1.2.7",
|
|
32
|
+
"@types/node": "^22.0.0",
|
|
33
|
+
"vitest": "^3.1.1",
|
|
34
|
+
"@grackle-ai/heft-rig": "0.0.1"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "heft build --clean",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"clean": "heft clean",
|
|
40
|
+
"_phase:build": "heft run --only build -- --clean",
|
|
41
|
+
"_phase:test": "vitest run"
|
|
42
|
+
}
|
|
43
|
+
}
|