ssh-mcp-pro 1.5.0 → 1.6.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/CHANGELOG.md +34 -0
- package/MIGRATION.md +1 -1
- package/README.md +1 -1
- package/dist/agent-bin.js +2 -2
- package/dist/agent-bin.js.map +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/remote/action-record-sanitize.d.ts +4 -0
- package/dist/remote/action-record-sanitize.d.ts.map +1 -0
- package/dist/remote/action-record-sanitize.js +11 -0
- package/dist/remote/action-record-sanitize.js.map +1 -0
- package/dist/remote/agent-bounded-io.d.ts +10 -0
- package/dist/remote/agent-bounded-io.d.ts.map +1 -1
- package/dist/remote/agent-bounded-io.js +34 -0
- package/dist/remote/agent-bounded-io.js.map +1 -1
- package/dist/remote/agent-browser-enrollment.d.ts +16 -0
- package/dist/remote/agent-browser-enrollment.d.ts.map +1 -0
- package/dist/remote/agent-browser-enrollment.js +190 -0
- package/dist/remote/agent-browser-enrollment.js.map +1 -0
- package/dist/remote/agent-cli.d.ts +5 -0
- package/dist/remote/agent-cli.d.ts.map +1 -1
- package/dist/remote/agent-cli.js +91 -14
- package/dist/remote/agent-cli.js.map +1 -1
- package/dist/remote/agent-content-search.d.ts +20 -0
- package/dist/remote/agent-content-search.d.ts.map +1 -0
- package/dist/remote/agent-content-search.js +61 -0
- package/dist/remote/agent-content-search.js.map +1 -0
- package/dist/remote/agent-executor.d.ts +5 -0
- package/dist/remote/agent-executor.d.ts.map +1 -1
- package/dist/remote/agent-executor.js +98 -10
- package/dist/remote/agent-executor.js.map +1 -1
- package/dist/remote/agent-file-search.d.ts +22 -0
- package/dist/remote/agent-file-search.d.ts.map +1 -0
- package/dist/remote/agent-file-search.js +243 -0
- package/dist/remote/agent-file-search.js.map +1 -0
- package/dist/remote/agent-managed-process.d.ts +43 -0
- package/dist/remote/agent-managed-process.d.ts.map +1 -0
- package/dist/remote/agent-managed-process.js +291 -0
- package/dist/remote/agent-managed-process.js.map +1 -0
- package/dist/remote/control-plane.d.ts.map +1 -1
- package/dist/remote/control-plane.js +2 -1
- package/dist/remote/control-plane.js.map +1 -1
- package/dist/remote/dashboard-enrollment-commands.d.ts +1 -0
- package/dist/remote/dashboard-enrollment-commands.d.ts.map +1 -1
- package/dist/remote/dashboard-enrollment-commands.js +8 -0
- package/dist/remote/dashboard-enrollment-commands.js.map +1 -1
- package/dist/remote/dashboard-enrollment-feedback-script.d.ts +3 -0
- package/dist/remote/dashboard-enrollment-feedback-script.d.ts.map +1 -0
- package/dist/remote/dashboard-enrollment-feedback-script.js +227 -0
- package/dist/remote/dashboard-enrollment-feedback-script.js.map +1 -0
- package/dist/remote/mcp-tools.d.ts +1 -0
- package/dist/remote/mcp-tools.d.ts.map +1 -1
- package/dist/remote/mcp-tools.js +207 -3
- package/dist/remote/mcp-tools.js.map +1 -1
- package/dist/remote/safe-file.d.ts +19 -1
- package/dist/remote/safe-file.d.ts.map +1 -1
- package/dist/remote/safe-file.js +77 -3
- package/dist/remote/safe-file.js.map +1 -1
- package/dist/remote/schemas.d.ts +16 -0
- package/dist/remote/schemas.d.ts.map +1 -1
- package/dist/remote/schemas.js +1 -0
- package/dist/remote/schemas.js.map +1 -1
- package/dist/remote/store.d.ts +2 -3
- package/dist/remote/store.d.ts.map +1 -1
- package/dist/remote/store.js +15 -3
- package/dist/remote/store.js.map +1 -1
- package/dist/remote/types.d.ts +3 -2
- package/dist/remote/types.d.ts.map +1 -1
- package/dist/remote/types.js +15 -0
- package/dist/remote/types.js.map +1 -1
- package/dist/remote/web-dashboard-html.d.ts.map +1 -1
- package/dist/remote/web-dashboard-html.js +25 -27
- package/dist/remote/web-dashboard-html.js.map +1 -1
- package/dist/remote/web-dashboard-styles.d.ts +1 -1
- package/dist/remote/web-dashboard-styles.d.ts.map +1 -1
- package/dist/remote/web-dashboard-styles.js +2 -0
- package/dist/remote/web-dashboard-styles.js.map +1 -1
- package/dist/remote/web-dashboard.d.ts.map +1 -1
- package/dist/remote/web-dashboard.js +9 -2
- package/dist/remote/web-dashboard.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/docs/adding-a-device.md +187 -11
- package/docs/backup-restore-verification.md +45 -0
- package/docs/coolify.md +9 -0
- package/docs/development/local-setup.md +1 -1
- package/docs/reference/compatibility.md +1 -1
- package/docs/tutorials/getting-started.md +1 -1
- package/mcp.json +1 -1
- package/package.json +2 -2
- package/registry/ssh-mcp-pro/mcp.json +1 -1
- package/server.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,40 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.6.0](https://github.com/oaslananka/ssh-mcp-pro/compare/ssh-mcp-pro-v1.5.0...ssh-mcp-pro-v1.6.0) (2026-11-12)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **agent:** approve Linux enrollment in local browser ([#239](https://github.com/oaslananka/ssh-mcp-pro/issues/239)) ([7ba75d4](https://github.com/oaslananka/ssh-mcp-pro/commit/7ba75d42a19a07ae2de18a7872d2fa54eb0415b8))
|
|
14
|
+
* **agent:** install Node LTS on first Windows enrollment ([#219](https://github.com/oaslananka/ssh-mcp-pro/issues/219)) ([8d2f82f](https://github.com/oaslananka/ssh-mcp-pro/commit/8d2f82f8696751bcf469fd42245327d5ed4007e7))
|
|
15
|
+
* **dashboard:** confirm first device is Live Online during setup ([#227](https://github.com/oaslananka/ssh-mcp-pro/issues/227)) ([7447971](https://github.com/oaslananka/ssh-mcp-pro/commit/74479716133c8d34f7233d681d58d3881b3e5f47))
|
|
16
|
+
* **install:** verified Node LTS bootstrap for clean Linux and WSL ([#237](https://github.com/oaslananka/ssh-mcp-pro/issues/237)) ([600b6f3](https://github.com/oaslananka/ssh-mcp-pro/commit/600b6f3f67ffd522817719be70c7839cb864d59e))
|
|
17
|
+
* **recovery:** verify encrypted backup restore offline ([#233](https://github.com/oaslananka/ssh-mcp-pro/issues/233)) ([4187749](https://github.com/oaslananka/ssh-mcp-pro/commit/41877496c8233c1c02958fc1579ba96d0745fb7c))
|
|
18
|
+
* **remote:** discover allowlisted files by name ([#217](https://github.com/oaslananka/ssh-mcp-pro/issues/217)) ([0f04209](https://github.com/oaslananka/ssh-mcp-pro/commit/0f04209464fd83b2d34ca8713b244969c8aeba80))
|
|
19
|
+
* **remote:** manage interactive agent processes with bounded output ([#221](https://github.com/oaslananka/ssh-mcp-pro/issues/221)) ([1beaddb](https://github.com/oaslananka/ssh-mcp-pro/commit/1beaddb1ff96f3d2bcbeb407322b0a65aa87f218))
|
|
20
|
+
* **remote:** page bounded file reads with UTF-8 safe cursors ([#215](https://github.com/oaslananka/ssh-mcp-pro/issues/215)) ([cce420b](https://github.com/oaslananka/ssh-mcp-pro/commit/cce420bcda302289fc92638e778f42848218cad1))
|
|
21
|
+
* **remote:** preview and apply guarded file text replacements ([#220](https://github.com/oaslananka/ssh-mcp-pro/issues/220)) ([eb3a837](https://github.com/oaslananka/ssh-mcp-pro/commit/eb3a8374942c1fe939a71f1abf3ccd130e0d9d48))
|
|
22
|
+
* **remote:** search allowed file contents by literal phrase ([#223](https://github.com/oaslananka/ssh-mcp-pro/issues/223)) ([f82034d](https://github.com/oaslananka/ssh-mcp-pro/commit/f82034d1c2cbad1706effc398c8666b48f9c1df7))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Bug Fixes
|
|
26
|
+
|
|
27
|
+
* **agent:** make Windows enrollment work with restricted PowerShell ([#218](https://github.com/oaslananka/ssh-mcp-pro/issues/218)) ([de97766](https://github.com/oaslananka/ssh-mcp-pro/commit/de977665266ea7437ad885134d37b2ccb2f69025))
|
|
28
|
+
* **agent:** reconnect safely after transient remote socket loss ([#230](https://github.com/oaslananka/ssh-mcp-pro/issues/230)) ([1f79e6e](https://github.com/oaslananka/ssh-mcp-pro/commit/1f79e6ec3a915eef66668c2dda90e7afe9be706b))
|
|
29
|
+
* **agent:** remove Windows scheduled task runtime cap ([#229](https://github.com/oaslananka/ssh-mcp-pro/issues/229)) ([e0956bf](https://github.com/oaslananka/ssh-mcp-pro/commit/e0956bf084b4133b278ee5d3aaf8b315f95d3b92))
|
|
30
|
+
* **agent:** report failure to persist Linux user service ([#224](https://github.com/oaslananka/ssh-mcp-pro/issues/224)) ([5976050](https://github.com/oaslananka/ssh-mcp-pro/commit/59760503acc5576c49bb7d48cea9220acff5f291))
|
|
31
|
+
* **agent:** report Linux status without uptime utility ([#231](https://github.com/oaslananka/ssh-mcp-pro/issues/231)) ([8868741](https://github.com/oaslananka/ssh-mcp-pro/commit/8868741439407a3dbe6d10cb57a3b06efdfd865e))
|
|
32
|
+
* **dashboard:** allow browser enrollment callback to loopback ([#242](https://github.com/oaslananka/ssh-mcp-pro/issues/242)) ([7ccf4d1](https://github.com/oaslananka/ssh-mcp-pro/commit/7ccf4d13485ca99aaff9142cf0a50f9fb656c8d1))
|
|
33
|
+
* **dashboard:** explain provider-scoped device inventory ([#234](https://github.com/oaslananka/ssh-mcp-pro/issues/234)) ([ac5a10c](https://github.com/oaslananka/ssh-mcp-pro/commit/ac5a10c10ee81aae54e8c1dfae03d61e66639d6a))
|
|
34
|
+
* **dashboard:** show tenant-scoped device names in audit history ([#232](https://github.com/oaslananka/ssh-mcp-pro/issues/232)) ([ef0b8eb](https://github.com/oaslananka/ssh-mcp-pro/commit/ef0b8eb9398216cc88b0deaa4c8de80e58f77665))
|
|
35
|
+
* **docker:** bundle installer scripts in standard runtime image ([#226](https://github.com/oaslananka/ssh-mcp-pro/issues/226)) ([ba6fffe](https://github.com/oaslananka/ssh-mcp-pro/commit/ba6fffe78877da2b7abaf2676663f15ebde36e7b))
|
|
36
|
+
* **install:** clarify WSL agent lifetime after setup ([#241](https://github.com/oaslananka/ssh-mcp-pro/issues/241)) ([e1215b1](https://github.com/oaslananka/ssh-mcp-pro/commit/e1215b19d5f4870ccaf99c938f12b5525f5ee2d0))
|
|
37
|
+
* **install:** explain missing system Node and npm ([#240](https://github.com/oaslananka/ssh-mcp-pro/issues/240)) ([249ee4c](https://github.com/oaslananka/ssh-mcp-pro/commit/249ee4c8f4327bfaa20e90acb09ee6c66f8208cb))
|
|
38
|
+
* **remote:** describe mutation risks and output schemas ([#213](https://github.com/oaslananka/ssh-mcp-pro/issues/213)) ([c7d18f4](https://github.com/oaslananka/ssh-mcp-pro/commit/c7d18f4bcb0f9c69537277db23ce971ad051f9ec))
|
|
39
|
+
* **remote:** expire abandoned pending agents ([#225](https://github.com/oaslananka/ssh-mcp-pro/issues/225)) ([43a730b](https://github.com/oaslananka/ssh-mcp-pro/commit/43a730b543ce8d8d67111dfe0391a39c57aacee4))
|
|
40
|
+
* **windows:** provide tokenless legacy agent task repair ([#235](https://github.com/oaslananka/ssh-mcp-pro/issues/235)) ([5a267e8](https://github.com/oaslananka/ssh-mcp-pro/commit/5a267e8cb60a4f25b3cb0fea2911439b8774d2c5))
|
|
41
|
+
|
|
8
42
|
## [1.5.0](https://github.com/oaslananka/ssh-mcp-pro/compare/ssh-mcp-pro-v1.4.3...ssh-mcp-pro-v1.5.0) (2026-11-10)
|
|
9
43
|
|
|
10
44
|
|
package/MIGRATION.md
CHANGED
|
@@ -7,7 +7,7 @@ Version `1.0.0` is the initial public baseline for this repository. There are no
|
|
|
7
7
|
When upgrading within `1.x`, preserve these compatibility expectations:
|
|
8
8
|
|
|
9
9
|
- Node.js must satisfy `>=22.22.2` (Node 24 and 26 are tested in CI).
|
|
10
|
-
- pnpm must satisfy `^11.28.
|
|
10
|
+
- pnpm must satisfy `^11.28.11` for current development and build tooling.
|
|
11
11
|
- Existing stdio MCP client configs can continue to launch `ssh-mcp-pro` with no arguments.
|
|
12
12
|
- HTTP deployments should keep explicit authentication, allowed origins, host allowlists, strict host-key verification, and a remote-safe tool profile.
|
|
13
13
|
|
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ ssh-mcp-pro is a secure Model Context Protocol (MCP) server for SSH automation.
|
|
|
38
38
|
## Prerequisites
|
|
39
39
|
|
|
40
40
|
- Node.js `>=22.22.2` or `>=24.15.0` or `>=26.3.0`
|
|
41
|
-
- pnpm `^11.28.
|
|
41
|
+
- pnpm `^11.28.11` (use Corepack for development)
|
|
42
42
|
- SSH access to the target hosts
|
|
43
43
|
- Docker, only for local integration tests and container image builds
|
|
44
44
|
|
package/dist/agent-bin.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { runAgentCliWithReconnect } from "./remote/agent-cli.js";
|
|
3
3
|
try {
|
|
4
|
-
await
|
|
4
|
+
await runAgentCliWithReconnect(process.argv.slice(2));
|
|
5
5
|
}
|
|
6
6
|
catch (error) {
|
|
7
7
|
const message = error instanceof Error ? error.message : String(error);
|
package/dist/agent-bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-bin.js","sourceRoot":"","sources":["../src/agent-bin.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"agent-bin.js","sourceRoot":"","sources":["../src/agent-bin.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAEjE,IAAI,CAAC;IACH,MAAM,wBAAwB,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC;AAAC,OAAO,KAAc,EAAE,CAAC;IACxB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
|
package/dist/mcp.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/server";
|
|
2
2
|
import type { Transport } from "@modelcontextprotocol/server";
|
|
3
3
|
import type { AppContainer } from "./container.js";
|
|
4
|
-
export declare const SERVER_VERSION = "1.
|
|
4
|
+
export declare const SERVER_VERSION = "1.6.0";
|
|
5
5
|
export declare const SERVER_NAME = "io.github.oaslananka/ssh-mcp-pro";
|
|
6
6
|
export declare class SSHMCPServer {
|
|
7
7
|
private readonly container;
|
package/dist/mcp.js
CHANGED
|
@@ -5,7 +5,7 @@ import { listResources, readResource } from "./resources.js";
|
|
|
5
5
|
import { getMCPPrompt, listMCPPrompts } from "./prompts.js";
|
|
6
6
|
import { withSpan } from "./telemetry.js";
|
|
7
7
|
import { createToolRegistry } from "./tools/index.js";
|
|
8
|
-
export const SERVER_VERSION = "1.
|
|
8
|
+
export const SERVER_VERSION = "1.6.0"; // x-release-please-version
|
|
9
9
|
export const SERVER_NAME = "io.github.oaslananka/ssh-mcp-pro";
|
|
10
10
|
function getSessionIdFromArgs(args) {
|
|
11
11
|
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RemoteToolName } from "./types.js";
|
|
2
|
+
/** Keep managed process command and stdin payloads out of durable action metadata. */
|
|
3
|
+
export declare function safeStoredActionArgs(tool: RemoteToolName, args: Record<string, unknown>): Record<string, unknown>;
|
|
4
|
+
//# sourceMappingURL=action-record-sanitize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-record-sanitize.d.ts","sourceRoot":"","sources":["../../src/remote/action-record-sanitize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,sFAAsF;AACtF,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKzB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Keep managed process command and stdin payloads out of durable action metadata. */
|
|
2
|
+
export function safeStoredActionArgs(tool, args) {
|
|
3
|
+
if (tool === "process_start")
|
|
4
|
+
return { ...args, command: "[redacted]" };
|
|
5
|
+
if (tool === "process_input")
|
|
6
|
+
return { ...args, input: "[redacted]" };
|
|
7
|
+
if (tool === "file_content_search")
|
|
8
|
+
return { ...args, query: "[redacted]" };
|
|
9
|
+
return args;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=action-record-sanitize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-record-sanitize.js","sourceRoot":"","sources":["../../src/remote/action-record-sanitize.ts"],"names":[],"mappings":"AAEA,sFAAsF;AACtF,MAAM,UAAU,oBAAoB,CAClC,IAAoB,EACpB,IAA6B;IAE7B,IAAI,IAAI,KAAK,eAAe;QAAE,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;IACxE,IAAI,IAAI,KAAK,eAAe;QAAE,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;IACtE,IAAI,IAAI,KAAK,qBAAqB;QAAE,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;IAC5E,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -13,6 +13,16 @@ export declare function readFileBounded(file: FileHandle, maxBytes: number): Pro
|
|
|
13
13
|
value: Buffer;
|
|
14
14
|
truncated: boolean;
|
|
15
15
|
}>;
|
|
16
|
+
/**
|
|
17
|
+
* Read one page using byte offsets without buffering the entire file.
|
|
18
|
+
* Unlike a legacy read, a page must not include a truncation marker in text.
|
|
19
|
+
* Valid UTF-8 is kept at code-point boundaries when a page ends mid-character.
|
|
20
|
+
*/
|
|
21
|
+
export declare function readFilePageBounded(file: FileHandle, offsetBytes: number, maxBytes: number): Promise<{
|
|
22
|
+
value: Buffer;
|
|
23
|
+
truncated: boolean;
|
|
24
|
+
nextOffsetBytes?: number;
|
|
25
|
+
}>;
|
|
16
26
|
export declare function readFileTailBounded(file: FileHandle, maxBytes: number, maxLines?: number): Promise<{
|
|
17
27
|
value: Buffer;
|
|
18
28
|
truncated: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-bounded-io.d.ts","sourceRoot":"","sources":["../../src/remote/agent-bounded-io.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB;AAkCD,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,iBAAiB,EAC9B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GACf,IAAI,CAcN;AAED,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,iBAAiB,EAC9B,QAAQ,EAAE,MAAM,GACf;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAMvC;AAoBD,wBAAsB,eAAe,CACnC,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC,CAID;AAyCD,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,SAAM,GACb,OAAO,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC,CAcD"}
|
|
1
|
+
{"version":3,"file":"agent-bounded-io.d.ts","sourceRoot":"","sources":["../../src/remote/agent-bounded-io.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB;AAkCD,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,iBAAiB,EAC9B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GACf,IAAI,CAcN;AAED,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,iBAAiB,EAC9B,QAAQ,EAAE,MAAM,GACf;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAMvC;AAoBD,wBAAsB,eAAe,CACnC,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC,CAID;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,UAAU,EAChB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAgC1E;AAyCD,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,SAAM,GACb,OAAO,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC,CAcD"}
|
|
@@ -60,6 +60,40 @@ export async function readFileBounded(file, maxBytes) {
|
|
|
60
60
|
const buffer = await readHandleRange(file, 0, cappedBytes + 1);
|
|
61
61
|
return truncateBuffer(buffer, cappedBytes);
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Read one page using byte offsets without buffering the entire file.
|
|
65
|
+
* Unlike a legacy read, a page must not include a truncation marker in text.
|
|
66
|
+
* Valid UTF-8 is kept at code-point boundaries when a page ends mid-character.
|
|
67
|
+
*/
|
|
68
|
+
export async function readFilePageBounded(file, offsetBytes, maxBytes) {
|
|
69
|
+
const raw = await readHandleRange(file, offsetBytes, maxBytes + 1);
|
|
70
|
+
let page = raw.subarray(0, maxBytes);
|
|
71
|
+
let truncated = raw.byteLength > maxBytes;
|
|
72
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
73
|
+
let validPage = false;
|
|
74
|
+
for (let removed = 0; removed <= (truncated ? Math.min(3, page.byteLength) : 0); removed += 1) {
|
|
75
|
+
try {
|
|
76
|
+
decoder.decode(page.subarray(0, page.byteLength - removed));
|
|
77
|
+
page = page.subarray(0, page.byteLength - removed);
|
|
78
|
+
validPage = true;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// A UTF-8 code point may cross the page boundary.
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (!validPage || (truncated && page.byteLength === 0)) {
|
|
86
|
+
throw Object.assign(new Error("Page cannot advance at this UTF-8 boundary; increase max_bytes or use a valid offset"), {
|
|
87
|
+
code: "POLICY_DENIED",
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
truncated ||= page.byteLength < raw.byteLength;
|
|
91
|
+
return {
|
|
92
|
+
value: page,
|
|
93
|
+
truncated,
|
|
94
|
+
...(truncated && page.byteLength > 0 ? { nextOffsetBytes: offsetBytes + page.byteLength } : {}),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
63
97
|
const TAIL_SCAN_CHUNK_BYTES = 64 * 1024;
|
|
64
98
|
const MAX_TAIL_SCAN_BYTES = 8 * 1024 * 1024;
|
|
65
99
|
async function findTailStart(file, size, maxLines, maxScanBytes) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-bounded-io.js","sourceRoot":"","sources":["../../src/remote/agent-bounded-io.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"agent-bounded-io.js","sourceRoot":"","sources":["../../src/remote/agent-bounded-io.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface LocalBrowserEnrollment {
|
|
2
|
+
verificationUrl: string;
|
|
3
|
+
callbackUrl: string;
|
|
4
|
+
port: number;
|
|
5
|
+
finished: Promise<void>;
|
|
6
|
+
close(): void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A same-device HTTPS dashboard may hand its one-time token directly to a
|
|
10
|
+
* loopback listener. Only the bound browser origin, random challenge and
|
|
11
|
+
* local socket are accepted; the one-time token never appears in the URL.
|
|
12
|
+
*/
|
|
13
|
+
export declare function startLocalBrowserEnrollment(serverUrl: string, alias: string, onToken: (token: string) => Promise<void>, options?: {
|
|
14
|
+
timeoutMs?: number;
|
|
15
|
+
}): Promise<LocalBrowserEnrollment>;
|
|
16
|
+
//# sourceMappingURL=agent-browser-enrollment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-browser-enrollment.d.ts","sourceRoot":"","sources":["../../src/remote/agent-browser-enrollment.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,sBAAsB;IACrC,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,KAAK,IAAI,IAAI,CAAC;CACf;AA8DD;;;;GAIG;AACH,wBAAsB,2BAA2B,CAC/C,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,EACzC,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACnC,OAAO,CAAC,sBAAsB,CAAC,CAkIjC"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
const MAX_BODY_BYTES = 4_096;
|
|
4
|
+
const DEFAULT_TIMEOUT_MS = 600_000;
|
|
5
|
+
function safeEqual(expected, actual) {
|
|
6
|
+
const left = Buffer.from(expected);
|
|
7
|
+
const right = Buffer.from(actual);
|
|
8
|
+
return left.length === right.length && timingSafeEqual(left, right);
|
|
9
|
+
}
|
|
10
|
+
function validServerOrigin(serverUrl) {
|
|
11
|
+
const url = new URL(serverUrl);
|
|
12
|
+
if (url.username ||
|
|
13
|
+
url.password ||
|
|
14
|
+
url.search ||
|
|
15
|
+
url.hash ||
|
|
16
|
+
(url.protocol !== "https:" &&
|
|
17
|
+
!(url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname)))) {
|
|
18
|
+
throw new Error("Browser enrollment requires an HTTPS server or local loopback URL");
|
|
19
|
+
}
|
|
20
|
+
return url.origin;
|
|
21
|
+
}
|
|
22
|
+
function send(res, code, message, origin, preflight = false) {
|
|
23
|
+
res.writeHead(code, {
|
|
24
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
25
|
+
"Cache-Control": "no-store",
|
|
26
|
+
...(origin
|
|
27
|
+
? {
|
|
28
|
+
"Access-Control-Allow-Origin": origin,
|
|
29
|
+
Vary: "Origin",
|
|
30
|
+
...(preflight
|
|
31
|
+
? {
|
|
32
|
+
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
|
33
|
+
"Access-Control-Allow-Headers": "content-type",
|
|
34
|
+
"Access-Control-Max-Age": "0",
|
|
35
|
+
}
|
|
36
|
+
: {}),
|
|
37
|
+
}
|
|
38
|
+
: {}),
|
|
39
|
+
});
|
|
40
|
+
res.end(message);
|
|
41
|
+
}
|
|
42
|
+
async function readLimitedJson(req) {
|
|
43
|
+
const chunks = [];
|
|
44
|
+
let size = 0;
|
|
45
|
+
for await (const chunk of req) {
|
|
46
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
47
|
+
size += bytes.length;
|
|
48
|
+
if (size > MAX_BODY_BYTES)
|
|
49
|
+
throw new Error("Request body exceeds limit");
|
|
50
|
+
chunks.push(bytes);
|
|
51
|
+
}
|
|
52
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A same-device HTTPS dashboard may hand its one-time token directly to a
|
|
56
|
+
* loopback listener. Only the bound browser origin, random challenge and
|
|
57
|
+
* local socket are accepted; the one-time token never appears in the URL.
|
|
58
|
+
*/
|
|
59
|
+
export async function startLocalBrowserEnrollment(serverUrl, alias, onToken, options = {}) {
|
|
60
|
+
const origin = validServerOrigin(serverUrl);
|
|
61
|
+
const state = randomBytes(32).toString("base64url");
|
|
62
|
+
let complete;
|
|
63
|
+
let fail;
|
|
64
|
+
let settled = false;
|
|
65
|
+
const finished = new Promise((resolve, reject) => {
|
|
66
|
+
complete = () => {
|
|
67
|
+
if (settled)
|
|
68
|
+
return;
|
|
69
|
+
settled = true;
|
|
70
|
+
resolve();
|
|
71
|
+
};
|
|
72
|
+
fail = (error) => {
|
|
73
|
+
if (settled)
|
|
74
|
+
return;
|
|
75
|
+
settled = true;
|
|
76
|
+
reject(error);
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
// Closing early is allowed without an unhandled rejection, while callers
|
|
80
|
+
// awaiting finished still receive the same timeout/cancellation failure.
|
|
81
|
+
void finished.catch(() => { });
|
|
82
|
+
let enrolled = false;
|
|
83
|
+
const listener = createServer((req, res) => {
|
|
84
|
+
void handleRequest(req, res).catch(() => {
|
|
85
|
+
if (!res.headersSent)
|
|
86
|
+
send(res, 500, "Local approval failed");
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
async function handleRequest(req, res) {
|
|
90
|
+
const remote = req.socket.remoteAddress ?? "";
|
|
91
|
+
const localOnly = remote === "127.0.0.1";
|
|
92
|
+
if (!localOnly || req.headers.origin !== origin) {
|
|
93
|
+
send(res, 403, "Browser origin is not allowed");
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const address = listener.address();
|
|
97
|
+
if (req.headers.host !== `127.0.0.1:${address.port}` ||
|
|
98
|
+
(req.url !== "/enroll" && req.url !== "/ready")) {
|
|
99
|
+
send(res, 403, "Unexpected callback destination", origin);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (req.method === "GET" && req.url === "/ready") {
|
|
103
|
+
send(res, 204, "", origin);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (req.method === "OPTIONS") {
|
|
107
|
+
send(res, 204, "", origin, true);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (req.method !== "POST" || enrolled) {
|
|
111
|
+
send(res, 405, "Enrollment is not available", origin);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (!(req.headers["content-type"] ?? "").startsWith("application/json")) {
|
|
115
|
+
send(res, 415, "JSON required", origin);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
let body;
|
|
119
|
+
try {
|
|
120
|
+
body = await readLimitedJson(req);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
send(res, 400, "Invalid enrollment request", origin);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
127
|
+
send(res, 400, "Invalid enrollment request", origin);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const data = body;
|
|
131
|
+
if (typeof data.state !== "string" || !safeEqual(state, data.state)) {
|
|
132
|
+
send(res, 403, "Enrollment link does not match", origin);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (typeof data.token !== "string" || !/^[A-Za-z0-9_-]{30,256}$/u.test(data.token)) {
|
|
136
|
+
send(res, 400, "Enrollment token is missing or malformed", origin);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
enrolled = true;
|
|
140
|
+
try {
|
|
141
|
+
await onToken(data.token);
|
|
142
|
+
send(res, 200, "Enrollment accepted", origin);
|
|
143
|
+
complete();
|
|
144
|
+
listener.close();
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
enrolled = false;
|
|
148
|
+
send(res, 502, "Device enrollment failed; check the agent terminal", origin);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
await new Promise((resolve, reject) => {
|
|
152
|
+
listener.once("error", reject);
|
|
153
|
+
listener.listen(0, "127.0.0.1", () => {
|
|
154
|
+
listener.off("error", reject);
|
|
155
|
+
resolve();
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
const address = listener.address();
|
|
159
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
160
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > DEFAULT_TIMEOUT_MS) {
|
|
161
|
+
listener.close();
|
|
162
|
+
throw new Error("Invalid local browser enrollment timeout");
|
|
163
|
+
}
|
|
164
|
+
const timer = setTimeout(() => {
|
|
165
|
+
fail(new Error("Browser enrollment expired; restart the installer"));
|
|
166
|
+
listener.close();
|
|
167
|
+
}, timeoutMs);
|
|
168
|
+
timer.unref();
|
|
169
|
+
void finished
|
|
170
|
+
.finally(() => {
|
|
171
|
+
clearTimeout(timer);
|
|
172
|
+
})
|
|
173
|
+
.catch(() => { });
|
|
174
|
+
const dashboardUrl = new URL("/dashboard", origin);
|
|
175
|
+
dashboardUrl.searchParams.set("local_enroll_port", String(address.port));
|
|
176
|
+
dashboardUrl.searchParams.set("local_enroll_state", state);
|
|
177
|
+
dashboardUrl.searchParams.set("local_enroll_alias", alias);
|
|
178
|
+
return {
|
|
179
|
+
verificationUrl: dashboardUrl.toString(),
|
|
180
|
+
callbackUrl: `http://127.0.0.1:${address.port}/enroll`,
|
|
181
|
+
port: address.port,
|
|
182
|
+
finished,
|
|
183
|
+
close() {
|
|
184
|
+
fail(new Error("Browser enrollment cancelled"));
|
|
185
|
+
clearTimeout(timer);
|
|
186
|
+
listener.close();
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=agent-browser-enrollment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-browser-enrollment.js","sourceRoot":"","sources":["../../src/remote/agent-browser-enrollment.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
+
/** Recoverable network failure; invalid enrollment/configuration must not be retried. */
|
|
2
|
+
export declare class RetryableAgentTransportError extends Error {
|
|
3
|
+
}
|
|
1
4
|
export declare function runAgentCli(argv: string[]): Promise<void>;
|
|
5
|
+
/** Keep installed agents online across transient socket failures without re-enrollment. */
|
|
6
|
+
export declare function runAgentCliWithReconnect(argv: string[], run?: (args: string[]) => Promise<void>, pause?: (delayMs: number) => Promise<void>): Promise<void>;
|
|
2
7
|
//# sourceMappingURL=agent-cli.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-cli.d.ts","sourceRoot":"","sources":["../../src/remote/agent-cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agent-cli.d.ts","sourceRoot":"","sources":["../../src/remote/agent-cli.ts"],"names":[],"mappings":"AAwDA,yFAAyF;AACzF,qBAAa,4BAA6B,SAAQ,KAAK;CAAG;AAkf1D,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAiC/D;AACD,2FAA2F;AAC3F,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,GAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAe,EACpD,KAAK,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CACW,GAClD,OAAO,CAAC,IAAI,CAAC,CA2Bf"}
|
package/dist/remote/agent-cli.js
CHANGED
|
@@ -3,10 +3,14 @@ import os from "node:os";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { AgentExecutor, defaultHostMetadata } from "./agent-executor.js";
|
|
6
|
+
import { startLocalBrowserEnrollment } from "./agent-browser-enrollment.js";
|
|
6
7
|
import { AgentActivityLog, agentActivityLogPath, showAgentActivityLogs, } from "./agent-activity-log.js";
|
|
7
8
|
import { ensurePemKeyPair, nowIso, randomToken, signEnvelope, verifyEnvelope, } from "./crypto.js";
|
|
8
9
|
import { rememberAcceptedPolicyUpdate, validatePolicyUpdate, } from "./policy-update-guard.js";
|
|
9
10
|
import { parseControlPlaneEnvelope, parseAgentPolicy } from "./schemas.js";
|
|
11
|
+
/** Recoverable network failure; invalid enrollment/configuration must not be retried. */
|
|
12
|
+
export class RetryableAgentTransportError extends Error {
|
|
13
|
+
}
|
|
10
14
|
function output(line) {
|
|
11
15
|
process.stdout.write(`${line}\n`);
|
|
12
16
|
}
|
|
@@ -123,13 +127,7 @@ function removeTrailingSlashes(value) {
|
|
|
123
127
|
}
|
|
124
128
|
return value.slice(0, end);
|
|
125
129
|
}
|
|
126
|
-
async function
|
|
127
|
-
const server = removeTrailingSlashes(parseFlag(argv, "--server") ?? "");
|
|
128
|
-
const token = parseFlag(argv, "--token");
|
|
129
|
-
const alias = parseFlag(argv, "--alias") ?? os.hostname();
|
|
130
|
-
if (!server || !token) {
|
|
131
|
-
throw new Error("Usage: ssh-mcp-pro-agent enroll --server <url> --token <token> --alias <alias>");
|
|
132
|
-
}
|
|
130
|
+
async function enrollWithToken(server, token, alias) {
|
|
133
131
|
const keyPair = ensurePemKeyPair(keyPath());
|
|
134
132
|
const response = await postJson(`${server}/api/agents/enroll`, {
|
|
135
133
|
token,
|
|
@@ -153,6 +151,31 @@ async function enroll(argv) {
|
|
|
153
151
|
output(`Agent enrolled: ${config.agentId} (${config.alias})`);
|
|
154
152
|
output(`Config: ${configPath()}`);
|
|
155
153
|
}
|
|
154
|
+
async function enroll(argv) {
|
|
155
|
+
const server = removeTrailingSlashes(parseFlag(argv, "--server") ?? "");
|
|
156
|
+
const token = parseFlag(argv, "--token");
|
|
157
|
+
const browser = argv.includes("--browser");
|
|
158
|
+
const alias = parseFlag(argv, "--alias") ?? os.hostname();
|
|
159
|
+
if (!server || (!token && !browser) || (token !== undefined && browser)) {
|
|
160
|
+
throw new Error("Usage: ssh-mcp-pro-agent enroll --server <url> --token <token> --alias <alias> (or --browser without --token)");
|
|
161
|
+
}
|
|
162
|
+
if (!browser) {
|
|
163
|
+
await enrollWithToken(server, token, alias);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const local = await startLocalBrowserEnrollment(server, alias, async (oneTimeToken) => {
|
|
167
|
+
await enrollWithToken(server, oneTimeToken, alias);
|
|
168
|
+
});
|
|
169
|
+
output("Open this URL in a browser on this same device to approve read-only or selected device access:");
|
|
170
|
+
output(local.verificationUrl);
|
|
171
|
+
output("Waiting for browser approval; the enrollment token stays on this device (10-minute limit).");
|
|
172
|
+
try {
|
|
173
|
+
await local.finished;
|
|
174
|
+
}
|
|
175
|
+
finally {
|
|
176
|
+
local.close();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
156
179
|
/** A server-side ready acknowledgement is distinct from an open WebSocket. */
|
|
157
180
|
function handleAgentControlMessage(payload, config, activity) {
|
|
158
181
|
if (payload.type === "agent.ready") {
|
|
@@ -299,16 +322,44 @@ async function runAgent() {
|
|
|
299
322
|
activity.event("error", "action_failed", "Action processing failed; details withheld.");
|
|
300
323
|
});
|
|
301
324
|
};
|
|
325
|
+
// An error can precede the close event carrying a non-retryable policy code.
|
|
326
|
+
let errorCloseTimer;
|
|
327
|
+
let closed = false;
|
|
302
328
|
ws.onerror = () => {
|
|
303
|
-
|
|
304
|
-
|
|
329
|
+
if (closed)
|
|
330
|
+
return;
|
|
331
|
+
activity.event("error", "socket_error", "Connection error. Checking close reason.");
|
|
332
|
+
errorCloseTimer ??= setTimeout(() => {
|
|
333
|
+
errorCloseTimer = undefined;
|
|
334
|
+
closed = true;
|
|
335
|
+
reject(new RetryableAgentTransportError("WebSocket transport error"));
|
|
336
|
+
// If a runtime never delivers close after error, do not retain a stale socket.
|
|
337
|
+
try {
|
|
338
|
+
ws.close();
|
|
339
|
+
}
|
|
340
|
+
catch {
|
|
341
|
+
// The transport may already be unusable.
|
|
342
|
+
}
|
|
343
|
+
}, 2_000);
|
|
305
344
|
};
|
|
306
|
-
//
|
|
307
|
-
ws.onclose = () => {
|
|
308
|
-
|
|
309
|
-
|
|
345
|
+
// Policy-denied agents must not loop; ordinary transport losses are recoverable.
|
|
346
|
+
ws.onclose = (event) => {
|
|
347
|
+
if (closed)
|
|
348
|
+
return;
|
|
349
|
+
closed = true;
|
|
350
|
+
if (errorCloseTimer) {
|
|
351
|
+
clearTimeout(errorCloseTimer);
|
|
352
|
+
errorCloseTimer = undefined;
|
|
353
|
+
}
|
|
354
|
+
if (event?.code === 1008) {
|
|
355
|
+
activity.event("error", "connection_denied", "Server rejected the agent connection.");
|
|
356
|
+
reject(new Error("Agent connection rejected by server"));
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
activity.event("warn", "disconnected", "Connection closed. Reconnecting automatically.");
|
|
360
|
+
reject(new RetryableAgentTransportError("Agent WebSocket connection closed"));
|
|
310
361
|
};
|
|
311
|
-
});
|
|
362
|
+
}).finally(() => executor.dispose());
|
|
312
363
|
}
|
|
313
364
|
function status() {
|
|
314
365
|
if (!existsSync(configPath())) {
|
|
@@ -357,6 +408,7 @@ function uninstallService() {
|
|
|
357
408
|
function printAgentUsage() {
|
|
358
409
|
output("Usage:");
|
|
359
410
|
output(" ssh-mcp-pro-agent enroll --server <url> --token <token> --alias <alias>");
|
|
411
|
+
output(" ssh-mcp-pro-agent enroll --server <url> --browser --alias <alias>");
|
|
360
412
|
output(" ssh-mcp-pro-agent run");
|
|
361
413
|
output(" ssh-mcp-pro-agent status");
|
|
362
414
|
output(" ssh-mcp-pro-agent logs [--tail 60] [--follow]");
|
|
@@ -406,4 +458,29 @@ export async function runAgentCli(argv) {
|
|
|
406
458
|
throw new Error("Unknown agent command. Run ssh-mcp-pro-agent --help for usage.");
|
|
407
459
|
}
|
|
408
460
|
}
|
|
461
|
+
/** Keep installed agents online across transient socket failures without re-enrollment. */
|
|
462
|
+
export function runAgentCliWithReconnect(argv, run = runAgentCli, pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
|
|
463
|
+
let delayMs = 1_000;
|
|
464
|
+
// Schedule each attempt independently so long-lived agents do not retain retry stacks.
|
|
465
|
+
return new Promise((resolve, reject) => {
|
|
466
|
+
function handleFailure(error) {
|
|
467
|
+
if (argv[0] !== "run" || !(error instanceof RetryableAgentTransportError)) {
|
|
468
|
+
reject(error);
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
const currentDelayMs = delayMs;
|
|
472
|
+
delayMs = Math.min(delayMs * 2, 30_000);
|
|
473
|
+
process.stderr.write(`Agent connection lost; retrying in ${currentDelayMs / 1_000} seconds.\n`);
|
|
474
|
+
void Promise.resolve()
|
|
475
|
+
.then(() => pause(currentDelayMs))
|
|
476
|
+
.then(attempt, reject);
|
|
477
|
+
}
|
|
478
|
+
function attempt() {
|
|
479
|
+
void Promise.resolve()
|
|
480
|
+
.then(() => run(argv))
|
|
481
|
+
.then(resolve, handleFailure);
|
|
482
|
+
}
|
|
483
|
+
attempt();
|
|
484
|
+
});
|
|
485
|
+
}
|
|
409
486
|
//# sourceMappingURL=agent-cli.js.map
|