ssh-mcp-pro 1.4.3 → 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 +41 -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 +30 -0
- package/dist/remote/agent-bounded-io.d.ts.map +1 -0
- package/dist/remote/agent-bounded-io.js +135 -0
- package/dist/remote/agent-bounded-io.js.map +1 -0
- 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-enrollment.d.ts +12 -0
- package/dist/remote/agent-enrollment.d.ts.map +1 -0
- package/dist/remote/agent-enrollment.js +87 -0
- package/dist/remote/agent-enrollment.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 +96 -107
- 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 +3 -2
- package/dist/remote/control-plane.d.ts.map +1 -1
- package/dist/remote/control-plane.js +24 -80
- package/dist/remote/control-plane.js.map +1 -1
- package/dist/remote/dashboard-enrollment-commands.d.ts +8 -0
- package/dist/remote/dashboard-enrollment-commands.d.ts.map +1 -0
- package/dist/remote/dashboard-enrollment-commands.js +23 -0
- package/dist/remote/dashboard-enrollment-commands.js.map +1 -0
- 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/oauth-handler.d.ts +5 -0
- package/dist/remote/oauth-handler.d.ts.map +1 -1
- package/dist/remote/oauth-handler.js +110 -65
- package/dist/remote/oauth-handler.js.map +1 -1
- package/dist/remote/oauth-provider-exchange.d.ts +11 -0
- package/dist/remote/oauth-provider-exchange.d.ts.map +1 -0
- package/dist/remote/oauth-provider-exchange.js +73 -0
- package/dist/remote/oauth-provider-exchange.js.map +1 -0
- 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-schema.d.ts +4 -0
- package/dist/remote/store-schema.d.ts.map +1 -0
- package/dist/remote/store-schema.js +147 -0
- package/dist/remote/store-schema.js.map +1 -0
- package/dist/remote/store.d.ts +9 -5
- package/dist/remote/store.d.ts.map +1 -1
- package/dist/remote/store.js +96 -133
- package/dist/remote/store.js.map +1 -1
- package/dist/remote/types.d.ts +16 -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-oauth-users.d.ts.map +1 -1
- package/dist/remote/web-dashboard-oauth-users.js +4 -38
- package/dist/remote/web-dashboard-oauth-users.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 +0 -2
- package/dist/remote/web-dashboard.d.ts.map +1 -1
- package/dist/remote/web-dashboard.js +12 -20
- package/dist/remote/web-dashboard.js.map +1 -1
- package/dist/session-auth-config.d.ts +8 -0
- package/dist/session-auth-config.d.ts.map +1 -0
- package/dist/session-auth-config.js +67 -0
- package/dist/session-auth-config.js.map +1 -0
- package/dist/session.d.ts +0 -12
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +3 -64
- package/dist/session.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/docs/adding-a-device.md +187 -11
- package/docs/api-overview.md +67 -0
- 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/security/hosted-data-inventory.md +3 -1
- package/docs/theme/favicon.svg +12 -0
- package/docs/theme/ssh-docs.css +670 -0
- package/docs/theme/ssh-docs.js +58 -0
- package/docs/tutorials/getting-started.md +1 -1
- package/mcp.json +1 -1
- package/package.json +12 -12
- package/registry/ssh-mcp-pro/mcp.json +1 -1
- package/server.json +3 -3
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AgentPolicy } from "./types.js";
|
|
2
|
+
type SessionStatus = "running" | "exited" | "stopped" | "timed_out" | "failed";
|
|
3
|
+
type StreamName = "stdout" | "stderr";
|
|
4
|
+
interface OutputEvent {
|
|
5
|
+
cursor: number;
|
|
6
|
+
stream: StreamName;
|
|
7
|
+
text: string;
|
|
8
|
+
}
|
|
9
|
+
/** Agent-local bounded process registry. The control plane never owns a shell process. */
|
|
10
|
+
export declare class ManagedProcessRegistry {
|
|
11
|
+
private readonly sessions;
|
|
12
|
+
private closed;
|
|
13
|
+
start(ownerId: string, agentId: string, args: Record<string, unknown>, stillAuthorized: () => boolean): Promise<{
|
|
14
|
+
process_id: string;
|
|
15
|
+
status: SessionStatus;
|
|
16
|
+
max_runtime_seconds: number;
|
|
17
|
+
}>;
|
|
18
|
+
read(ownerId: string, agentId: string, args: Record<string, unknown>, policy: AgentPolicy): {
|
|
19
|
+
process_id: string;
|
|
20
|
+
status: SessionStatus;
|
|
21
|
+
exit_code: number | null;
|
|
22
|
+
dropped_before_cursor: number;
|
|
23
|
+
next_cursor: number;
|
|
24
|
+
chunks: OutputEvent[];
|
|
25
|
+
};
|
|
26
|
+
input(ownerId: string, agentId: string, args: Record<string, unknown>): {
|
|
27
|
+
process_id: string;
|
|
28
|
+
written_bytes: number;
|
|
29
|
+
stdin_closed: boolean;
|
|
30
|
+
};
|
|
31
|
+
stop(ownerId: string, agentId: string, args: Record<string, unknown>): {
|
|
32
|
+
process_id: string;
|
|
33
|
+
status: SessionStatus;
|
|
34
|
+
};
|
|
35
|
+
stopAll(): void;
|
|
36
|
+
dispose(): void;
|
|
37
|
+
private lookup;
|
|
38
|
+
private cancel;
|
|
39
|
+
private append;
|
|
40
|
+
private pruneFinished;
|
|
41
|
+
}
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=agent-managed-process.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-managed-process.d.ts","sourceRoot":"","sources":["../../src/remote/agent-managed-process.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAmB,MAAM,YAAY,CAAC;AAE/D,KAAK,aAAa,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;AAC/E,KAAK,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;AACtC,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAmGD,0FAA0F;AAC1F,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqC;IAC9D,OAAO,CAAC,MAAM,CAAS;IAEjB,KAAK,CACT,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,eAAe,EAAE,MAAM,OAAO,GAC7B,OAAO,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,aAAa,CAAC;QAAC,mBAAmB,EAAE,MAAM,CAAA;KAAE,CAAC;IAyFtF,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,WAAW;;;;;;gBAmBvE,WAAW,EAAE;;IAgB/B,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;IA8BrE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;IAMpE,OAAO,IAAI,IAAI;IAIf,OAAO,IAAI,IAAI;IAUf,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,MAAM;IAOd,OAAO,CAAC,MAAM;IAoBd,OAAO,CAAC,aAAa;CAStB"}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { access } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { StringDecoder } from "node:string_decoder";
|
|
6
|
+
const MAX_ACTIVE = 4;
|
|
7
|
+
const MAX_RETAINED = 20;
|
|
8
|
+
const MAX_BUFFER_BYTES = 64 * 1024;
|
|
9
|
+
const MAX_BUFFER_EVENTS = 512;
|
|
10
|
+
const MAX_INPUT_BYTES = 64 * 1024;
|
|
11
|
+
const MAX_INPUT_PER_CALL = 4096;
|
|
12
|
+
const MAX_RUNTIME_SECONDS = 600;
|
|
13
|
+
const RETAIN_AFTER_EXIT_MS = 2 * 60 * 1000;
|
|
14
|
+
function denied(message, code = "POLICY_DENIED") {
|
|
15
|
+
return Object.assign(new Error(message), { code });
|
|
16
|
+
}
|
|
17
|
+
function integerArg(value, fallback, min, max, name) {
|
|
18
|
+
if (value === undefined)
|
|
19
|
+
return fallback;
|
|
20
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < min || value > max) {
|
|
21
|
+
throw denied(name + " must be an integer between " + min + " and " + max);
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
function executableShell(command) {
|
|
26
|
+
if (process.platform === "win32") {
|
|
27
|
+
return {
|
|
28
|
+
command: "powershell.exe",
|
|
29
|
+
args: ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", command],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return { command: "/bin/sh", args: ["-lc", command] };
|
|
33
|
+
}
|
|
34
|
+
function terminateTree(child) {
|
|
35
|
+
if (!child.pid) {
|
|
36
|
+
child.kill("SIGTERM");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (process.platform === "win32") {
|
|
40
|
+
// Never resolve a privileged OS utility through a potentially writable PATH.
|
|
41
|
+
const configuredRoot = process.env.SystemRoot;
|
|
42
|
+
const windowsRoot = configuredRoot && /^[A-Z]:\\Windows$/iu.test(configuredRoot)
|
|
43
|
+
? configuredRoot
|
|
44
|
+
: String.raw `C:\Windows`;
|
|
45
|
+
const taskkill = path.win32.join(windowsRoot, "System32", "taskkill.exe");
|
|
46
|
+
const killer = spawn(taskkill, ["/PID", String(child.pid), "/T", "/F"], {
|
|
47
|
+
windowsHide: true,
|
|
48
|
+
stdio: "ignore",
|
|
49
|
+
});
|
|
50
|
+
killer.on("error", () => {
|
|
51
|
+
child.kill("SIGTERM");
|
|
52
|
+
});
|
|
53
|
+
killer.on("close", (code) => {
|
|
54
|
+
if (code !== 0 && child.exitCode === null && child.signalCode === null) {
|
|
55
|
+
child.kill("SIGTERM");
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
killer.unref();
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
process.kill(-child.pid, "SIGTERM");
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
child.kill("SIGTERM");
|
|
66
|
+
}
|
|
67
|
+
const timer = setTimeout(() => {
|
|
68
|
+
try {
|
|
69
|
+
if (child.pid && child.exitCode === null && child.signalCode === null) {
|
|
70
|
+
process.kill(-child.pid, "SIGKILL");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
child.kill("SIGKILL");
|
|
75
|
+
}
|
|
76
|
+
}, 1500);
|
|
77
|
+
timer.unref();
|
|
78
|
+
}
|
|
79
|
+
/** Agent-local bounded process registry. The control plane never owns a shell process. */
|
|
80
|
+
export class ManagedProcessRegistry {
|
|
81
|
+
sessions = new Map();
|
|
82
|
+
closed = false;
|
|
83
|
+
async start(ownerId, agentId, args, stillAuthorized) {
|
|
84
|
+
if (this.closed)
|
|
85
|
+
throw denied("Agent process manager is closed");
|
|
86
|
+
const command = args.command;
|
|
87
|
+
if (typeof command !== "string" || !command.trim() || command.length > 4096) {
|
|
88
|
+
throw denied("command must be a nonempty string of at most 4096 characters");
|
|
89
|
+
}
|
|
90
|
+
const maxRuntime = integerArg(args.max_runtime_seconds, 300, 1, MAX_RUNTIME_SECONDS, "max_runtime_seconds");
|
|
91
|
+
const cwd = args.cwd;
|
|
92
|
+
if (cwd !== undefined && (typeof cwd !== "string" || !cwd || cwd.length > 4096)) {
|
|
93
|
+
throw denied("cwd must be a nonempty path");
|
|
94
|
+
}
|
|
95
|
+
if (cwd !== undefined) {
|
|
96
|
+
try {
|
|
97
|
+
await access(cwd);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
throw denied("cwd is not accessible");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (!stillAuthorized())
|
|
104
|
+
throw denied("shell.exec was revoked before process start", "CAPABILITY_DENIED");
|
|
105
|
+
this.pruneFinished();
|
|
106
|
+
const active = [...this.sessions.values()].filter((x) => x.status === "running").length;
|
|
107
|
+
if (active >= MAX_ACTIVE)
|
|
108
|
+
throw denied("Too many active managed processes on this agent");
|
|
109
|
+
const shell = executableShell(command);
|
|
110
|
+
const child = spawn(shell.command, shell.args, {
|
|
111
|
+
cwd,
|
|
112
|
+
stdio: "pipe",
|
|
113
|
+
windowsHide: true,
|
|
114
|
+
detached: process.platform !== "win32",
|
|
115
|
+
});
|
|
116
|
+
const processId = "proc_" + randomUUID().replaceAll("-", "");
|
|
117
|
+
const session = {
|
|
118
|
+
id: processId,
|
|
119
|
+
ownerId,
|
|
120
|
+
agentId,
|
|
121
|
+
child,
|
|
122
|
+
status: "running",
|
|
123
|
+
exitCode: null,
|
|
124
|
+
chunks: [],
|
|
125
|
+
nextCursor: 0,
|
|
126
|
+
droppedBeforeCursor: 0,
|
|
127
|
+
bufferedBytes: 0,
|
|
128
|
+
inputBytes: 0,
|
|
129
|
+
timeout: setTimeout(() => {
|
|
130
|
+
if (session.status === "running") {
|
|
131
|
+
session.status = "timed_out";
|
|
132
|
+
terminateTree(child);
|
|
133
|
+
}
|
|
134
|
+
}, maxRuntime * 1000),
|
|
135
|
+
decoders: { stdout: new StringDecoder("utf8"), stderr: new StringDecoder("utf8") },
|
|
136
|
+
};
|
|
137
|
+
session.timeout.unref();
|
|
138
|
+
this.sessions.set(processId, session);
|
|
139
|
+
for (const stream of ["stdout", "stderr"]) {
|
|
140
|
+
child[stream].on("data", (chunk) => {
|
|
141
|
+
this.append(session, stream, session.decoders[stream].write(chunk));
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
// Writing to a process that exits concurrently must not crash the agent.
|
|
145
|
+
child.stdin.on("error", () => undefined);
|
|
146
|
+
child.on("error", () => {
|
|
147
|
+
session.status = "failed";
|
|
148
|
+
session.exitCode = 127;
|
|
149
|
+
this.append(session, "stderr", "Process could not be started");
|
|
150
|
+
});
|
|
151
|
+
child.on("close", (code) => {
|
|
152
|
+
clearTimeout(session.timeout);
|
|
153
|
+
for (const stream of ["stdout", "stderr"]) {
|
|
154
|
+
this.append(session, stream, session.decoders[stream].end());
|
|
155
|
+
}
|
|
156
|
+
if (session.status === "running")
|
|
157
|
+
session.status = "exited";
|
|
158
|
+
session.exitCode ??= code;
|
|
159
|
+
if (!this.closed) {
|
|
160
|
+
session.retention = setTimeout(() => {
|
|
161
|
+
this.sessions.delete(session.id);
|
|
162
|
+
}, RETAIN_AFTER_EXIT_MS);
|
|
163
|
+
session.retention.unref();
|
|
164
|
+
this.pruneFinished();
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
return { process_id: processId, status: "running", max_runtime_seconds: maxRuntime };
|
|
168
|
+
}
|
|
169
|
+
read(ownerId, agentId, args, policy) {
|
|
170
|
+
const session = this.lookup(ownerId, agentId, args.process_id);
|
|
171
|
+
const cursor = integerArg(args.cursor, 0, 0, Number.MAX_SAFE_INTEGER, "cursor");
|
|
172
|
+
const maxBytes = integerArg(args.max_bytes, Math.min(8192, policy.maxOutputBytes), 1024, 32768, "max_bytes");
|
|
173
|
+
if (maxBytes > policy.maxOutputBytes)
|
|
174
|
+
throw denied("Output budget is below requested max_bytes");
|
|
175
|
+
const start = Math.max(cursor, session.droppedBeforeCursor);
|
|
176
|
+
const result = {
|
|
177
|
+
process_id: session.id,
|
|
178
|
+
status: session.status,
|
|
179
|
+
exit_code: session.exitCode,
|
|
180
|
+
dropped_before_cursor: session.droppedBeforeCursor,
|
|
181
|
+
next_cursor: start,
|
|
182
|
+
chunks: [],
|
|
183
|
+
};
|
|
184
|
+
for (const entry of session.chunks) {
|
|
185
|
+
if (entry.cursor < start)
|
|
186
|
+
continue;
|
|
187
|
+
const candidate = {
|
|
188
|
+
...result,
|
|
189
|
+
next_cursor: entry.cursor + 1,
|
|
190
|
+
chunks: [...result.chunks, entry],
|
|
191
|
+
};
|
|
192
|
+
if (Buffer.byteLength(JSON.stringify(candidate), "utf8") > maxBytes)
|
|
193
|
+
break;
|
|
194
|
+
result.next_cursor = entry.cursor + 1;
|
|
195
|
+
result.chunks.push(entry);
|
|
196
|
+
}
|
|
197
|
+
return result;
|
|
198
|
+
}
|
|
199
|
+
input(ownerId, agentId, args) {
|
|
200
|
+
const session = this.lookup(ownerId, agentId, args.process_id);
|
|
201
|
+
const value = args.input;
|
|
202
|
+
if (typeof value !== "string" || Buffer.byteLength(value) > MAX_INPUT_PER_CALL) {
|
|
203
|
+
throw denied("input must be a string of at most 4096 bytes");
|
|
204
|
+
}
|
|
205
|
+
if (session.status !== "running" ||
|
|
206
|
+
session.child.stdin.destroyed ||
|
|
207
|
+
session.child.stdin.writableEnded) {
|
|
208
|
+
throw denied("Managed process is no longer accepting input");
|
|
209
|
+
}
|
|
210
|
+
const bytes = Buffer.byteLength(value);
|
|
211
|
+
if (session.inputBytes + bytes > MAX_INPUT_BYTES || session.child.stdin.writableLength > 8192) {
|
|
212
|
+
throw denied("Managed process input budget exceeded");
|
|
213
|
+
}
|
|
214
|
+
if (args.close_stdin !== undefined && typeof args.close_stdin !== "boolean") {
|
|
215
|
+
throw denied("close_stdin must be boolean");
|
|
216
|
+
}
|
|
217
|
+
session.inputBytes += bytes;
|
|
218
|
+
session.child.stdin.write(value);
|
|
219
|
+
if (args.close_stdin === true)
|
|
220
|
+
session.child.stdin.end();
|
|
221
|
+
return {
|
|
222
|
+
process_id: session.id,
|
|
223
|
+
written_bytes: bytes,
|
|
224
|
+
stdin_closed: args.close_stdin === true,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
stop(ownerId, agentId, args) {
|
|
228
|
+
const session = this.lookup(ownerId, agentId, args.process_id);
|
|
229
|
+
this.cancel(session);
|
|
230
|
+
return { process_id: session.id, status: session.status };
|
|
231
|
+
}
|
|
232
|
+
stopAll() {
|
|
233
|
+
for (const session of this.sessions.values())
|
|
234
|
+
this.cancel(session);
|
|
235
|
+
}
|
|
236
|
+
dispose() {
|
|
237
|
+
this.closed = true;
|
|
238
|
+
for (const session of this.sessions.values()) {
|
|
239
|
+
clearTimeout(session.timeout);
|
|
240
|
+
if (session.retention)
|
|
241
|
+
clearTimeout(session.retention);
|
|
242
|
+
this.cancel(session);
|
|
243
|
+
}
|
|
244
|
+
this.sessions.clear();
|
|
245
|
+
}
|
|
246
|
+
lookup(ownerId, agentId, id) {
|
|
247
|
+
const item = typeof id === "string" ? this.sessions.get(id) : undefined;
|
|
248
|
+
if (item?.ownerId !== ownerId || item.agentId !== agentId) {
|
|
249
|
+
throw denied("Process not found for this agent and user");
|
|
250
|
+
}
|
|
251
|
+
return item;
|
|
252
|
+
}
|
|
253
|
+
cancel(session) {
|
|
254
|
+
if (session.status !== "running")
|
|
255
|
+
return;
|
|
256
|
+
session.status = "stopped";
|
|
257
|
+
clearTimeout(session.timeout);
|
|
258
|
+
terminateTree(session.child);
|
|
259
|
+
}
|
|
260
|
+
append(session, stream, text) {
|
|
261
|
+
if (this.closed || !text)
|
|
262
|
+
return;
|
|
263
|
+
const characters = Array.from(text);
|
|
264
|
+
for (let offset = 0; offset < characters.length; offset += 128) {
|
|
265
|
+
const part = characters.slice(offset, offset + 128).join("");
|
|
266
|
+
const entry = { cursor: session.nextCursor++, stream, text: part };
|
|
267
|
+
session.chunks.push(entry);
|
|
268
|
+
session.bufferedBytes += Buffer.byteLength(part, "utf8");
|
|
269
|
+
while (session.bufferedBytes > MAX_BUFFER_BYTES ||
|
|
270
|
+
session.chunks.length > MAX_BUFFER_EVENTS) {
|
|
271
|
+
const removed = session.chunks.shift();
|
|
272
|
+
if (!removed)
|
|
273
|
+
break;
|
|
274
|
+
session.bufferedBytes -= Buffer.byteLength(removed.text, "utf8");
|
|
275
|
+
session.droppedBeforeCursor = removed.cursor + 1;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
pruneFinished() {
|
|
280
|
+
const finished = [...this.sessions.values()].filter((session) => session.status !== "running");
|
|
281
|
+
while (finished.length > MAX_RETAINED) {
|
|
282
|
+
const removed = finished.shift();
|
|
283
|
+
if (!removed)
|
|
284
|
+
break;
|
|
285
|
+
if (removed.retention)
|
|
286
|
+
clearTimeout(removed.retention);
|
|
287
|
+
this.sessions.delete(removed.id);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=agent-managed-process.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-managed-process.js","sourceRoot":"","sources":["../../src/remote/agent-managed-process.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,11 +19,14 @@ export declare class RemoteControlPlane {
|
|
|
19
19
|
readonly oauth: OAuthHandler;
|
|
20
20
|
readonly agentHandler: AgentWebSocketHandler;
|
|
21
21
|
readonly dashboard: WebDashboardHandler;
|
|
22
|
+
private readonly enrollment;
|
|
22
23
|
constructor(config?: RemoteConfig);
|
|
23
24
|
initialize(): Promise<void>;
|
|
24
25
|
close(): void;
|
|
25
26
|
private cleanupEphemeralState;
|
|
26
27
|
private handleGoogleOAuthCallback;
|
|
28
|
+
/** Keep credential-exchange routes together without inflating OAuth dispatch complexity. */
|
|
29
|
+
private handleOAuthGrantRequest;
|
|
27
30
|
private handleOAuthRequest;
|
|
28
31
|
handleHttp(req: IncomingMessage, res: ServerResponse, pathname: string): Promise<boolean>;
|
|
29
32
|
handleUpgrade(req: IncomingMessage, socket: Duplex, head: Buffer<ArrayBufferLike>, pathname: string): boolean;
|
|
@@ -41,8 +44,6 @@ export declare class RemoteControlPlane {
|
|
|
41
44
|
private authenticate;
|
|
42
45
|
private sendUnauthorized;
|
|
43
46
|
private callRemoteTool;
|
|
44
|
-
private createEnrollmentToken;
|
|
45
|
-
private installCommand;
|
|
46
47
|
private updateAgentPolicy;
|
|
47
48
|
private revokeAgent;
|
|
48
49
|
private dispatchAction;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-plane.d.ts","sourceRoot":"","sources":["../../src/remote/control-plane.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAI1C,OAAO,EAWL,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"control-plane.d.ts","sourceRoot":"","sources":["../../src/remote/control-plane.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAI1C,OAAO,EAWL,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AAiBrB,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,YAAY,EAAyB,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD,OAAO,EACL,qBAAqB,EAGtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAMV,YAAY,EAKb,MAAM,YAAY,CAAC;AAKpB,qBAAa,kBAAkB;IAC7B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAuC;IAC7E,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAsC;IACvE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA0C;IAC3E,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAoC;IACnE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiB;IACjD,OAAO,CAAC,UAAU,CAAyB;IAC3C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAa;IACjD,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC;IAC7C,QAAQ,CAAC,SAAS,EAAE,mBAAmB,CAAC;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyB;gBAExC,MAAM,eAAqB;IAwCjC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIjC,KAAK,IAAI,IAAI;IAiBb,OAAO,CAAC,qBAAqB;YAaf,yBAAyB;IASvC,4FAA4F;YAC9E,uBAAuB;YAgBvB,kBAAkB;IAmC1B,UAAU,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgD/F,aAAa,CACX,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,CAAC,eAAe,CAAC,EAC7B,QAAQ,EAAE,MAAM,GACf,OAAO;YAII,SAAS;IAoEvB;;;;OAIG;YACW,eAAe;YA8Cf,iBAAiB;YA8CjB,SAAS;YAyCT,mBAAmB;YAkCnB,iBAAiB;YAsDjB,YAAY;IAiB1B,OAAO,CAAC,gBAAgB;YAWV,cAAc;IA4D5B,OAAO,CAAC,iBAAiB;IAsDzB,OAAO,CAAC,WAAW;YAkBL,cAAc;IA4H5B,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,KAAK;IASb,OAAO,CAAC,iBAAiB;CAM1B;AAED,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAI5E;AAED,wBAAgB,uBAAuB,CAAC,mBAAmB,EAAE,UAAU,GAAG,MAAM,CAE/E"}
|
|
@@ -3,14 +3,16 @@ import { createMcpHandler, isLegacyRequest, McpServer } from "@modelcontextproto
|
|
|
3
3
|
import { toMcpWebRequest, sendMcpWebResponse } from "../mcp-http-web.js";
|
|
4
4
|
import { ensurePemKeyPair, hashSecret, id, keyId, loadJwtKeyPair, nowIso, randomToken, signEnvelope, verifyRemoteAccessToken, } from "./crypto.js";
|
|
5
5
|
import { loadRemoteConfig } from "./config.js";
|
|
6
|
-
import { isRecord, asString, negotiateMcpProtocolVersion,
|
|
6
|
+
import { isRecord, asString, negotiateMcpProtocolVersion, addNoStore, safeError, isValidEd25519PublicKey, readJson, sanitizeAgent, } from "./http-util.js";
|
|
7
7
|
import { listRemoteToolDescriptors } from "./mcp-tools.js";
|
|
8
|
-
import {
|
|
8
|
+
import { mergeCustomPolicy } from "./policy.js";
|
|
9
|
+
import { safeStoredActionArgs } from "./action-record-sanitize.js";
|
|
9
10
|
import { parseAgentHostMetadata } from "./schemas.js";
|
|
10
11
|
import { hasCapability } from "./scopes.js";
|
|
11
12
|
import { RemoteStore } from "./store.js";
|
|
12
13
|
import { OAuthHandler } from "./oauth-handler.js";
|
|
13
14
|
import { WebDashboardHandler } from "./web-dashboard.js";
|
|
15
|
+
import { AgentEnrollmentService } from "./agent-enrollment.js";
|
|
14
16
|
import { AgentWebSocketHandler, } from "./agent-handler.js";
|
|
15
17
|
import { TOOL_CAPABILITY_MAP } from "./types.js";
|
|
16
18
|
import { jsonResponse } from "./util.js";
|
|
@@ -28,12 +30,14 @@ export class RemoteControlPlane {
|
|
|
28
30
|
oauth;
|
|
29
31
|
agentHandler;
|
|
30
32
|
dashboard;
|
|
33
|
+
enrollment;
|
|
31
34
|
constructor(config = loadRemoteConfig()) {
|
|
32
35
|
this.config = config;
|
|
33
36
|
this.store = new RemoteStore(config.databaseUrl);
|
|
34
37
|
this.controlPlaneKeyPair = ensurePemKeyPair(config.controlPlaneSigningKeyPath);
|
|
35
38
|
this.oauth = new OAuthHandler(config, this.store, this.authorizeTransactions, () => this.requireJwtKeyPair(), (event) => this.audit(event));
|
|
36
39
|
this.agentHandler = new AgentWebSocketHandler(config, this.store, this.agentConnections, this.agentHelloNonces, this.pendingActions, (event) => this.audit(event));
|
|
40
|
+
this.enrollment = new AgentEnrollmentService(this.config, this.store, (event) => this.audit(event));
|
|
37
41
|
this.dashboard = new WebDashboardHandler({
|
|
38
42
|
config: this.config,
|
|
39
43
|
store: this.store,
|
|
@@ -41,7 +45,7 @@ export class RemoteControlPlane {
|
|
|
41
45
|
agentHandler: this.agentHandler,
|
|
42
46
|
getJwtKeyPair: () => this.requireJwtKeyPair(),
|
|
43
47
|
audit: (event) => this.audit(event),
|
|
44
|
-
createEnrollmentToken: (principal, args) => this.createEnrollmentToken(principal, args),
|
|
48
|
+
createEnrollmentToken: (principal, args) => this.enrollment.createEnrollmentToken(principal, args),
|
|
45
49
|
updateAgentPolicy: (principal, agent, policy) => this.updateAgentPolicy(principal, agent, policy),
|
|
46
50
|
revokeAgent: (principal, agent) => this.revokeAgent(principal, agent),
|
|
47
51
|
resolveAgent: (userId, value) => this.resolveAgent(userId, value),
|
|
@@ -87,6 +91,18 @@ export class RemoteControlPlane {
|
|
|
87
91
|
return;
|
|
88
92
|
await this.oauth.handleGoogleCallback(req, res);
|
|
89
93
|
}
|
|
94
|
+
/** Keep credential-exchange routes together without inflating OAuth dispatch complexity. */
|
|
95
|
+
async handleOAuthGrantRequest(req, res, pathname) {
|
|
96
|
+
if (pathname === "/oauth/token" && req.method === "POST") {
|
|
97
|
+
await this.oauth.handleToken(req, res);
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
if (pathname === "/oauth/revoke" && req.method === "POST") {
|
|
101
|
+
await this.oauth.handleRevoke(req, res);
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
90
106
|
async handleOAuthRequest(req, res, pathname) {
|
|
91
107
|
if (pathname === "/oauth/register" && req.method === "POST") {
|
|
92
108
|
await this.oauth.handleRegister(req, res);
|
|
@@ -108,8 +124,7 @@ export class RemoteControlPlane {
|
|
|
108
124
|
await this.handleGoogleOAuthCallback(req, res);
|
|
109
125
|
return true;
|
|
110
126
|
}
|
|
111
|
-
if (
|
|
112
|
-
await this.oauth.handleToken(req, res);
|
|
127
|
+
if (await this.handleOAuthGrantRequest(req, res, pathname)) {
|
|
113
128
|
return true;
|
|
114
129
|
}
|
|
115
130
|
if (pathname === "/oauth/jwks.json" && req.method === "GET") {
|
|
@@ -315,7 +330,7 @@ export class RemoteControlPlane {
|
|
|
315
330
|
const principal = await this.authenticate(req);
|
|
316
331
|
if (pathname === "/api/agents/enrollment-tokens" && req.method === "POST") {
|
|
317
332
|
const body = await readJson(req);
|
|
318
|
-
const result = this.createEnrollmentToken(principal, body);
|
|
333
|
+
const result = this.enrollment.createEnrollmentToken(principal, body);
|
|
319
334
|
jsonResponse(res, 201, result, addNoStore());
|
|
320
335
|
return;
|
|
321
336
|
}
|
|
@@ -456,11 +471,11 @@ export class RemoteControlPlane {
|
|
|
456
471
|
return { agents: this.store.listAgents(principal.userId).map(sanitizeAgent) };
|
|
457
472
|
}
|
|
458
473
|
if (tool === "create_enrollment_token") {
|
|
459
|
-
return this.createEnrollmentToken(principal, args);
|
|
474
|
+
return this.enrollment.createEnrollmentToken(principal, args);
|
|
460
475
|
}
|
|
461
476
|
if (tool === "get_agent_install_command") {
|
|
462
477
|
const agent = this.requireAgent(principal.userId, args);
|
|
463
|
-
return this.installCommand(agent, undefined);
|
|
478
|
+
return this.enrollment.installCommand(agent, undefined);
|
|
464
479
|
}
|
|
465
480
|
if (tool === "update_agent_policy") {
|
|
466
481
|
const agent = this.requireAgent(principal.userId, args);
|
|
@@ -490,77 +505,6 @@ export class RemoteControlPlane {
|
|
|
490
505
|
const actionResult = await this.dispatchAction(principal, agent, tool, capability, args);
|
|
491
506
|
return { action: actionResult };
|
|
492
507
|
}
|
|
493
|
-
createEnrollmentToken(principal, args) {
|
|
494
|
-
if (!hasCapability(principal.capabilities, "agents.admin")) {
|
|
495
|
-
throw safeError("INVALID_SCOPE", "agents:admin scope is required", 403);
|
|
496
|
-
}
|
|
497
|
-
const alias = asString(args.alias)?.trim();
|
|
498
|
-
if (!alias) {
|
|
499
|
-
throw safeError("FORBIDDEN", "alias is required");
|
|
500
|
-
}
|
|
501
|
-
const profile = asString(args.requested_profile) ?? "read-only";
|
|
502
|
-
const policy = createAgentPolicy(profile === "operations" || profile === "full-admin" ? profile : "read-only");
|
|
503
|
-
const now = nowIso();
|
|
504
|
-
const existing = this.store.getAgentByAlias(principal.userId, alias);
|
|
505
|
-
if (existing && existing.status !== "revoked") {
|
|
506
|
-
throw safeError("FORBIDDEN", "Agent alias already exists");
|
|
507
|
-
}
|
|
508
|
-
const agent = {
|
|
509
|
-
id: id("agt"),
|
|
510
|
-
userId: principal.userId,
|
|
511
|
-
alias,
|
|
512
|
-
status: "pending",
|
|
513
|
-
profile: policy.profile,
|
|
514
|
-
policy,
|
|
515
|
-
policyVersion: policy.version,
|
|
516
|
-
createdAt: now,
|
|
517
|
-
updatedAt: now,
|
|
518
|
-
};
|
|
519
|
-
const token = randomToken(32);
|
|
520
|
-
this.store.insertAgent(agent);
|
|
521
|
-
this.store.insertEnrollmentToken({
|
|
522
|
-
id: id("enr"),
|
|
523
|
-
agentId: agent.id,
|
|
524
|
-
userId: principal.userId,
|
|
525
|
-
tokenHash: hashSecret(token),
|
|
526
|
-
expiresAt: new Date(Date.now() + this.config.enrollmentTokenTtlSeconds * 1000).toISOString(),
|
|
527
|
-
createdAt: now,
|
|
528
|
-
});
|
|
529
|
-
this.audit({
|
|
530
|
-
userId: principal.userId,
|
|
531
|
-
agentId: agent.id,
|
|
532
|
-
eventType: "enrollment_token_created",
|
|
533
|
-
severity: "info",
|
|
534
|
-
metadata: { alias, profile: policy.profile },
|
|
535
|
-
});
|
|
536
|
-
return { ...this.installCommand(agent, token), enrollment_token: token };
|
|
537
|
-
}
|
|
538
|
-
installCommand(agent, token) {
|
|
539
|
-
const tokenArgument = token ?? "<create-a-new-enrollment-token>";
|
|
540
|
-
const posixBase = [
|
|
541
|
-
"npx --yes --package ssh-mcp-pro@latest ssh-mcp-pro-agent enroll",
|
|
542
|
-
`--server ${quotePosixArg(this.config.publicBaseUrl)}`,
|
|
543
|
-
`--token ${quotePosixArg(tokenArgument)}`,
|
|
544
|
-
`--alias ${quotePosixArg(agent.alias)}`,
|
|
545
|
-
].join(" ");
|
|
546
|
-
const powershellBase = [
|
|
547
|
-
"npx --yes --package ssh-mcp-pro@latest ssh-mcp-pro-agent enroll",
|
|
548
|
-
`--server ${quotePowerShellArg(this.config.publicBaseUrl)}`,
|
|
549
|
-
`--token ${quotePowerShellArg(tokenArgument)}`,
|
|
550
|
-
`--alias ${quotePowerShellArg(agent.alias)}`,
|
|
551
|
-
].join(" ");
|
|
552
|
-
return {
|
|
553
|
-
agent_id: agent.id,
|
|
554
|
-
alias: agent.alias,
|
|
555
|
-
token_recoverable: Boolean(token),
|
|
556
|
-
commands: {
|
|
557
|
-
npm: posixBase,
|
|
558
|
-
run: "npx --yes --package ssh-mcp-pro@latest ssh-mcp-pro-agent run",
|
|
559
|
-
windows: powershellBase,
|
|
560
|
-
},
|
|
561
|
-
expires_in_seconds: token ? this.config.enrollmentTokenTtlSeconds : undefined,
|
|
562
|
-
};
|
|
563
|
-
}
|
|
564
508
|
updateAgentPolicy(principal, agent, value) {
|
|
565
509
|
if (!hasCapability(principal.capabilities, "agents.admin")) {
|
|
566
510
|
throw safeError("INVALID_SCOPE", "agents:admin scope is required", 403);
|
|
@@ -654,7 +598,7 @@ export class RemoteControlPlane {
|
|
|
654
598
|
agentId: agent.id,
|
|
655
599
|
tool,
|
|
656
600
|
capability,
|
|
657
|
-
args,
|
|
601
|
+
args: safeStoredActionArgs(tool, args),
|
|
658
602
|
status: "sent",
|
|
659
603
|
issuedAt,
|
|
660
604
|
deadline,
|