@deepseek-ai/dsh-ssh 0.2.0-rc.2 → 0.2.1-alpha.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.i18n.yaml +6 -6
- package/README.md +7 -6
- package/README.zh.md +7 -6
- package/lib/helper.js +12 -8
- package/lib/index.js +58 -25
- package/lib/{protocol-C43fD--H.js → protocol-CDhW_IHU.js} +1 -1
- package/lib/protocol.js +1 -1
- package/lib/{schemas-l5hGvwBb.js → schemas-BD3YbDxe.js} +4 -2
- package/lib/schemas.js +1 -1
- package/lib/types/helper.d.ts +2 -0
- package/lib/types/index.d.ts +25 -10
- package/lib/types/protocol.d.ts +1 -1
- package/lib/types/schemas.d.ts +6 -2
- package/package.json +33 -31
package/README.i18n.yaml
CHANGED
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
en: 8fbb876fefb42f3c
|
|
19
19
|
zh: ddd38f747088e032
|
|
20
20
|
/deepseek-ai-dsh-ssh/use-this-package/deployment-prerequisites:
|
|
21
|
-
en:
|
|
22
|
-
zh:
|
|
21
|
+
en: 9be90e9dad103726
|
|
22
|
+
zh: af9e050b0ab50d04
|
|
23
23
|
/deepseek-ai-dsh-ssh/understand-the-implementation:
|
|
24
|
-
en:
|
|
25
|
-
zh:
|
|
24
|
+
en: 01fedb6b36ac94af
|
|
25
|
+
zh: 9353c40acd9075d8
|
|
26
26
|
/deepseek-ai-dsh-ssh/further-exploration:
|
|
27
27
|
en: 1d179b90a32af26b
|
|
28
28
|
zh: 683cf3c0085147e1
|
|
@@ -36,5 +36,5 @@
|
|
|
36
36
|
en: c173949c197e6deb
|
|
37
37
|
zh: a951c8cfeca150ab
|
|
38
38
|
/deepseek-ai-dsh-ssh/known-limitations-and-deferred-work/dev-note:
|
|
39
|
-
en:
|
|
40
|
-
zh:
|
|
39
|
+
en: e67f633dc6fe7773
|
|
40
|
+
zh: 8ec3e73528ddb1a6
|
package/README.md
CHANGED
|
@@ -31,20 +31,21 @@ Compose this service with [`fs-ssh`](../fs-ssh/README.md), [`subprocess-ssh`](..
|
|
|
31
31
|
|
|
32
32
|
Both endpoints require Linux or macOS. The local `ssh` command must support connection multiplexing and Unix-socket forwarding; the server must permit that forwarding. Configure the alias, credentials and known-host entry before startup: the service enables `BatchMode`, requires strict host-key checking, disables agent forwarding and adds no interactive authentication flow.
|
|
33
33
|
|
|
34
|
-
Install
|
|
34
|
+
Install an [SSH helper executable archive](../ssh-helper-runtime/README.md), or the built helper script with its matching Node dependencies. Keep the runtime outside the workspace and writable temporary roots. They must also remain outside a backend’s replaced temporary tree, such as bwrap’s private `/tmp`; the workspace may still be under `/tmp`. Digest verification detects an unexpected installed artifact after helper startup; it does not make writable deployment files safe to execute or authenticate a malicious SSH host.
|
|
35
35
|
|
|
36
36
|
| Field | Default | Meaning |
|
|
37
37
|
|---|---|---|
|
|
38
38
|
| `host` | required | Existing OpenSSH host alias |
|
|
39
|
-
| `
|
|
39
|
+
| `launch` | required | `{ kind: "executable" }`, or `{ kind: "node-script", node: "/absolute/node" }` |
|
|
40
|
+
| `helper`, `workspace` | required | Absolute remote helper entry and default workspace |
|
|
40
41
|
| `helperHash` | required | Lowercase SHA-256 of the installed helper entry |
|
|
41
|
-
| `bootstrapPath`, `bootstrapHash` | omitted | Paired remote PTC
|
|
42
|
+
| `launch.bootstrapPath`, `launch.bootstrapHash` | omitted | Paired remote PTC script and digest; only for `node-script` |
|
|
42
43
|
| `requestTimeoutMs` | `30000` | Connection and administrative-request deadline, from 1 through 2,147,483,647 ms |
|
|
43
44
|
| `maxFrameBytes` | `67108864` | Per-message JSON payload ceiling, at most 64 MiB |
|
|
44
45
|
| `maxPending` | `128` | Ordinary outstanding requests; heartbeat and bounded cleanup requests have reserved capacity |
|
|
45
46
|
| `leaseMs` | `30000` | Helper heartbeat lease, from 3000 to 600000 ms |
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
Pass the verified `ctx.ssh.ptcLaunch` as the `launch` configuration of [`NodePtcRuntime`](../../ptc-runtime/ptc-runtime-node/README.md). Executable deployments include their worker. Script deployments require both bootstrap fields for PTC; filesystem and Bash use may omit them. The getter refuses an unconfigured script bootstrap. The client rejects mismatched protocol versions, runtime kinds and helper digests before readiness.
|
|
48
49
|
|
|
49
50
|
-----
|
|
50
51
|
|
|
@@ -64,7 +65,7 @@ Failed startup and process results release their reservations after native quies
|
|
|
64
65
|
|
|
65
66
|
For terminals opting into shell activity observation, root exit retains the reservation and its remaining work. Activity RPC continues to reach the provider; explicit termination awaits quiescence before releasing endpoints and recording the completed result. Helper connection disposal and lease expiry retain their existing termination authority.
|
|
66
67
|
|
|
67
|
-
The helper
|
|
68
|
+
The connection disables the helper’s SIGUSR1 debugger activation: the script invocation uses `--disable-sigusr1`, and the executable invocation supplies that flag through `NODE_OPTIONS`.
|
|
68
69
|
|
|
69
70
|
</details>
|
|
70
71
|
|
|
@@ -101,6 +102,6 @@ This provider contributes no request-prefix content. Its consumers own model-vis
|
|
|
101
102
|
<details>
|
|
102
103
|
<summary>Working context for maintainers — click to expand</summary>
|
|
103
104
|
|
|
104
|
-
|
|
105
|
+
None.
|
|
105
106
|
|
|
106
107
|
</details>
|
package/README.zh.md
CHANGED
|
@@ -31,20 +31,21 @@ kind: "package-reference"
|
|
|
31
31
|
|
|
32
32
|
两端均需运行 Linux 或 macOS。本地 `ssh` 命令必须支持连接复用与 Unix 套接字转发,服务器也必须允许该转发。启动前配置主机别名、凭据与已知主机记录:本服务启用 `BatchMode`、要求严格检查主机密钥、禁用认证代理转发,且不提供交互认证流程。
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
安装 [SSH helper 可执行文件发行包](../ssh-helper-runtime/README.zh.md),或已构建的 helper 脚本及其匹配的 Node 依赖。运行时必须位于工作区和可写临时目录之外,也必须位于后端会替换的临时目录树之外,例如 bwrap 的私有 `/tmp`;工作区仍可位于 `/tmp` 下。摘要校验在辅助程序启动后发现非预期的已安装产物;它不能保证可写部署文件的执行安全,也不能认证恶意 SSH 主机。
|
|
35
35
|
|
|
36
36
|
| 字段 | 默认值 | 含义 |
|
|
37
37
|
|---|---|---|
|
|
38
38
|
| `host` | 必填 | 已有的 OpenSSH 主机别名 |
|
|
39
|
-
| `
|
|
39
|
+
| `launch` | 必填 | `{ kind: "executable" }`,或 `{ kind: "node-script", node: "/absolute/node" }` |
|
|
40
|
+
| `helper`、`workspace` | 必填 | 远端 helper 入口和默认工作区的绝对路径 |
|
|
40
41
|
| `helperHash` | 必填 | 已安装辅助程序入口的小写 SHA-256 |
|
|
41
|
-
| `bootstrapPath`、`bootstrapHash` | 省略 | 成对提供的远端 PTC
|
|
42
|
+
| `launch.bootstrapPath`、`launch.bootstrapHash` | 省略 | 成对提供的远端 PTC 脚本及摘要;仅用于 `node-script` |
|
|
42
43
|
| `requestTimeoutMs` | `30000` | 连接与管理请求的截止时限,范围为 1 至 2,147,483,647 毫秒 |
|
|
43
44
|
| `maxFrameBytes` | `67108864` | 每条 JSON 消息的负载上限,最大为 64 MiB |
|
|
44
45
|
| `maxPending` | `128` | 普通未完成请求的数量上限;心跳与有界清理请求使用预留容量 |
|
|
45
46
|
| `leaseMs` | `30000` | 辅助进程心跳租期,范围为 3000 至 600000 毫秒 |
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
将验证后的 `ctx.ssh.ptcLaunch` 作为 [`NodePtcRuntime`](../../ptc-runtime/ptc-runtime-node/README.zh.md) 的 `launch` 配置。可执行文件部署包含 worker;脚本部署使用 PTC 时需要两个引导字段,仅使用文件系统和 Bash 时可以省略。脚本引导未配置时,getter 会拒绝访问。客户端在就绪前拒绝不匹配的协议版本、运行形式和 helper 摘要。
|
|
48
49
|
|
|
49
50
|
-----
|
|
50
51
|
|
|
@@ -64,7 +65,7 @@ OpenSSH 主连接承载私有管理 RPC。每条程序流使用独立转发的 U
|
|
|
64
65
|
|
|
65
66
|
终端启用 shell 活动观察时,根进程退出会保留 reservation 及其剩余工作。活动 RPC 继续访问 provider;明确终止时先等待进程范围完全停稳,再释放端点并记录完成结果。helper 连接卸载和租约到期仍保留原有的终止权限。
|
|
66
67
|
|
|
67
|
-
|
|
68
|
+
连接会禁用 helper 的 SIGUSR1 调试器激活:脚本调用使用 `--disable-sigusr1`,可执行文件调用通过 `NODE_OPTIONS` 提供该标志。
|
|
68
69
|
|
|
69
70
|
</details>
|
|
70
71
|
|
|
@@ -101,6 +102,6 @@ OpenSSH 主连接承载私有管理 RPC。每条程序流使用独立转发的 U
|
|
|
101
102
|
<details>
|
|
102
103
|
<summary>维护者工作上下文 — 点击展开</summary>
|
|
103
104
|
|
|
104
|
-
|
|
105
|
+
无。
|
|
105
106
|
|
|
106
107
|
</details>
|
package/lib/helper.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as SshRpcPeer, t as RemoteOperationError } from "./protocol-
|
|
2
|
-
import { S as textStreamIdSchema, _ as remotePath, a as environmentSchema, b as targetSchema, d as outputSnapshotFrameLimit, g as processIdSchema, l as intentSchema, m as policySchema, r as editSchema, v as spawnSchema } from "./schemas-
|
|
1
|
+
import { a as SshRpcPeer, t as RemoteOperationError } from "./protocol-CDhW_IHU.js";
|
|
2
|
+
import { S as textStreamIdSchema, _ as remotePath, a as environmentSchema, b as targetSchema, d as outputSnapshotFrameLimit, g as processIdSchema, l as intentSchema, m as policySchema, r as editSchema, v as spawnSchema } from "./schemas-BD3YbDxe.js";
|
|
3
3
|
import { t as SSH_STREAM_TLS_OPTIONS } from "./stream-security-DJlQsPG-.js";
|
|
4
4
|
import { chmod, mkdir, mkdtemp, rm } from "node:fs/promises";
|
|
5
5
|
import { join } from "node:path";
|
|
@@ -8,10 +8,10 @@ import { z } from "zod";
|
|
|
8
8
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
9
9
|
import { createServer } from "node:tls";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { isSea } from "node:sea";
|
|
11
12
|
import { readFileSync } from "node:fs";
|
|
12
13
|
import { FsError } from "@deepseek-ai/dsh-fs";
|
|
13
14
|
import { SandboxedFileSystem } from "@deepseek-ai/dsh-fs-sandbox";
|
|
14
|
-
import { SubprocessExecutableNotFoundError } from "@deepseek-ai/dsh-subprocess";
|
|
15
15
|
import { LocalSubprocessRuntime } from "@deepseek-ai/dsh-subprocess-local";
|
|
16
16
|
import { LocalSandboxProvider } from "@deepseek-ai/dsh-sandbox-local";
|
|
17
17
|
import { SandboxPolicyService } from "@deepseek-ai/dsh-sandbox-policy";
|
|
@@ -605,21 +605,23 @@ async function runSshHelper(transport) {
|
|
|
605
605
|
if (method === "hello") {
|
|
606
606
|
if (initialized) throw new Error("SSH helper handshake already completed");
|
|
607
607
|
const input = z.object({
|
|
608
|
-
protocol: z.literal(
|
|
608
|
+
protocol: z.literal(2),
|
|
609
609
|
workspace: remotePath,
|
|
610
610
|
leaseMs: z.number().int().min(3e3).max(6e5),
|
|
611
611
|
bootstrapPath: remotePath.optional()
|
|
612
612
|
}).strict().parse(raw);
|
|
613
|
+
if (transport.kind === "executable" && input.bootstrapPath !== void 0) throw new Error("Executable SSH helper uses its embedded PTC bootstrap");
|
|
613
614
|
workspace = ctx.fs.processPath(await ctx.fs.resolve(input.workspace, { signal }));
|
|
614
615
|
leaseMs = input.leaseMs;
|
|
615
616
|
initialized = true;
|
|
616
617
|
touchLease();
|
|
617
618
|
return {
|
|
618
|
-
protocol:
|
|
619
|
+
protocol: 2,
|
|
619
620
|
hash: createHash("sha256").update(readFileSync(transport.entryPath)).digest("hex"),
|
|
621
|
+
kind: transport.kind,
|
|
620
622
|
platform: process.platform,
|
|
621
623
|
nodeVersion: process.version,
|
|
622
|
-
|
|
624
|
+
executable: process.execPath,
|
|
623
625
|
root,
|
|
624
626
|
workspace,
|
|
625
627
|
...input.bootstrapPath === void 0 ? {} : { bootstrapHash: createHash("sha256").update(readFileSync(input.bootstrapPath)).digest("hex") }
|
|
@@ -673,7 +675,7 @@ async function runSshHelper(transport) {
|
|
|
673
675
|
try {
|
|
674
676
|
return await ctx.subprocess.resolveExecutable(input.command, env, signal);
|
|
675
677
|
} catch (error) {
|
|
676
|
-
if (error
|
|
678
|
+
if (typeof error === "object" && error !== null && "name" in error && error.name === "SubprocessExecutableNotFoundError" && "message" in error && typeof error.message === "string") throw new RemoteOperationError(error.message, "SUBPROCESS_EXECUTABLE_NOT_FOUND");
|
|
677
679
|
throw error;
|
|
678
680
|
}
|
|
679
681
|
}
|
|
@@ -822,10 +824,12 @@ for (const signal of [
|
|
|
822
824
|
]) process.once(signal, stop);
|
|
823
825
|
try {
|
|
824
826
|
if (process.argv.length !== 2) throw new Error("SSH helper accepts no command arguments");
|
|
827
|
+
const packaged = isSea();
|
|
825
828
|
await runSshHelper({
|
|
826
829
|
input: process.stdin,
|
|
827
830
|
output: process.stdout,
|
|
828
|
-
entryPath: fileURLToPath(import.meta.url),
|
|
831
|
+
entryPath: packaged ? process.execPath : fileURLToPath(import.meta.url),
|
|
832
|
+
kind: packaged ? "executable" : "node-script",
|
|
829
833
|
signal: controller.signal
|
|
830
834
|
});
|
|
831
835
|
} catch (error) {
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as SshRpcPeer } from "./protocol-
|
|
2
|
-
import { s as helloSchema } from "./schemas-
|
|
1
|
+
import { a as SshRpcPeer } from "./protocol-CDhW_IHU.js";
|
|
2
|
+
import { s as helloSchema } from "./schemas-BD3YbDxe.js";
|
|
3
3
|
import { n as authenticateStream } from "./stream-security-DJlQsPG-.js";
|
|
4
4
|
import { execFile, spawn } from "node:child_process";
|
|
5
5
|
import { mkdtemp, rm } from "node:fs/promises";
|
|
@@ -8,18 +8,22 @@ import { createConnection } from "node:net";
|
|
|
8
8
|
import { Service } from "@deepseek-ai/cordis";
|
|
9
9
|
import schema from "@deepseek-ai/schemastery";
|
|
10
10
|
import { z } from "zod";
|
|
11
|
+
import { assertNever } from "@deepseek-ai/dsh-util-values";
|
|
11
12
|
//#region lib/types/index.js
|
|
12
13
|
/** OpenSSH connection owner for one version-matched POSIX helper and its independent forwarded streams. */
|
|
13
14
|
/** One non-reconnecting SSH session; loss invalidates all active operations. */
|
|
14
15
|
var SshConnection = class extends Service {
|
|
15
16
|
static Config = schema.object({
|
|
16
17
|
host: schema.string().required(),
|
|
17
|
-
node: schema.string().required(),
|
|
18
18
|
helper: schema.string().required(),
|
|
19
|
+
launch: schema.union([schema.object({
|
|
20
|
+
kind: schema.const("node-script").required(),
|
|
21
|
+
node: schema.string().required(),
|
|
22
|
+
bootstrapPath: schema.string(),
|
|
23
|
+
bootstrapHash: schema.string()
|
|
24
|
+
}), schema.object({ kind: schema.const("executable").required() })]).required(),
|
|
19
25
|
helperHash: schema.string().required(),
|
|
20
26
|
workspace: schema.string().required(),
|
|
21
|
-
bootstrapPath: schema.string(),
|
|
22
|
-
bootstrapHash: schema.string(),
|
|
23
27
|
requestTimeoutMs: schema.number().default(3e4),
|
|
24
28
|
maxFrameBytes: schema.number().default(64 * 1024 * 1024),
|
|
25
29
|
maxPending: schema.number().default(128),
|
|
@@ -46,17 +50,20 @@ var SshConnection = class extends Service {
|
|
|
46
50
|
if (process.platform !== "linux" && process.platform !== "darwin") throw new Error("SSH runtime requires a POSIX client");
|
|
47
51
|
this.config = z.object({
|
|
48
52
|
host: z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_.@-]*$/),
|
|
49
|
-
|
|
53
|
+
launch: z.discriminatedUnion("kind", [z.object({
|
|
54
|
+
kind: z.literal("node-script"),
|
|
55
|
+
node: z.string().startsWith("/"),
|
|
56
|
+
bootstrapPath: z.string().startsWith("/").optional(),
|
|
57
|
+
bootstrapHash: z.string().regex(/^[0-9a-f]{64}$/).optional()
|
|
58
|
+
}).strict().refine((value) => value.bootstrapPath === void 0 === (value.bootstrapHash === void 0), "bootstrapPath and bootstrapHash must be paired"), z.object({ kind: z.literal("executable") }).strict()]),
|
|
50
59
|
helper: z.string().startsWith("/"),
|
|
51
60
|
helperHash: z.string().regex(/^[0-9a-f]{64}$/),
|
|
52
61
|
workspace: z.string().startsWith("/"),
|
|
53
62
|
requestTimeoutMs: z.number().int().positive().max(2147483647),
|
|
54
|
-
bootstrapPath: z.string().startsWith("/").optional(),
|
|
55
|
-
bootstrapHash: z.string().regex(/^[0-9a-f]{64}$/).optional(),
|
|
56
63
|
maxFrameBytes: z.number().int().positive().max(64 * 1024 * 1024),
|
|
57
64
|
maxPending: z.number().int().positive().max(128),
|
|
58
65
|
leaseMs: z.number().int().min(3e3).max(6e5)
|
|
59
|
-
}).
|
|
66
|
+
}).strict().parse(config);
|
|
60
67
|
this.ready = this.start();
|
|
61
68
|
this.ready.catch((error) => {
|
|
62
69
|
this.fail(error);
|
|
@@ -67,15 +74,25 @@ var SshConnection = class extends Service {
|
|
|
67
74
|
async [Service.init]() {
|
|
68
75
|
await this.ready;
|
|
69
76
|
}
|
|
70
|
-
/** Verified remote
|
|
71
|
-
get
|
|
77
|
+
/** Verified remote PTC launch configuration; script deployments require a verified bootstrap. */
|
|
78
|
+
get ptcLaunch() {
|
|
72
79
|
if (this.remote === void 0) throw new Error("SSH helper is not ready");
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
const launch = this.config.launch;
|
|
81
|
+
switch (launch.kind) {
|
|
82
|
+
case "executable": return {
|
|
83
|
+
kind: "embedded",
|
|
84
|
+
executable: this.remote.executable
|
|
85
|
+
};
|
|
86
|
+
case "node-script":
|
|
87
|
+
if (launch.bootstrapPath === void 0) throw new Error("SSH PTC requires a verified bootstrapPath and bootstrapHash");
|
|
88
|
+
return {
|
|
89
|
+
kind: "node-script",
|
|
90
|
+
executable: this.remote.executable,
|
|
91
|
+
bootstrapPath: launch.bootstrapPath
|
|
92
|
+
};
|
|
93
|
+
/* v8 ignore next -- Config parsing admits only these two launch kinds. */
|
|
94
|
+
default: return assertNever(launch);
|
|
95
|
+
}
|
|
79
96
|
}
|
|
80
97
|
/**
|
|
81
98
|
* Send a helper operation; cancellation never replays an ambiguous mutation.
|
|
@@ -285,11 +302,26 @@ var SshConnection = class extends Service {
|
|
|
285
302
|
this.directory = await mkdtemp("/tmp/dsh-ssh-");
|
|
286
303
|
if (this.closed) throw new Error("SSH connection closed before startup");
|
|
287
304
|
const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
305
|
+
const launch = this.config.launch;
|
|
306
|
+
let command;
|
|
307
|
+
let bootstrapPath;
|
|
308
|
+
let bootstrapHash;
|
|
309
|
+
switch (launch.kind) {
|
|
310
|
+
case "node-script":
|
|
311
|
+
command = [
|
|
312
|
+
launch.node,
|
|
313
|
+
"--disable-sigusr1",
|
|
314
|
+
this.config.helper
|
|
315
|
+
].map(quote).join(" ");
|
|
316
|
+
bootstrapPath = launch.bootstrapPath;
|
|
317
|
+
bootstrapHash = launch.bootstrapHash;
|
|
318
|
+
break;
|
|
319
|
+
case "executable":
|
|
320
|
+
command = `NODE_OPTIONS='--disable-sigusr1' ${quote(this.config.helper)}`;
|
|
321
|
+
break;
|
|
322
|
+
/* v8 ignore next -- Config parsing admits only these two launch kinds. */
|
|
323
|
+
default: return assertNever(launch);
|
|
324
|
+
}
|
|
293
325
|
const child = spawn("ssh", [
|
|
294
326
|
"-T",
|
|
295
327
|
"-M",
|
|
@@ -335,13 +367,14 @@ var SshConnection = class extends Service {
|
|
|
335
367
|
this.fail(error);
|
|
336
368
|
});
|
|
337
369
|
const hello = await rpc.request("hello", {
|
|
338
|
-
protocol:
|
|
370
|
+
protocol: 2,
|
|
339
371
|
workspace: this.config.workspace,
|
|
340
372
|
leaseMs: this.config.leaseMs,
|
|
341
|
-
...
|
|
373
|
+
...bootstrapPath === void 0 ? {} : { bootstrapPath }
|
|
342
374
|
}, helloSchema, AbortSignal.timeout(this.config.requestTimeoutMs));
|
|
343
375
|
if (hello.hash !== this.config.helperHash) throw new Error("SSH helper digest differs from the configured artifact");
|
|
344
|
-
if (hello.
|
|
376
|
+
if (hello.kind !== launch.kind) throw new Error("SSH helper runtime differs from the configured launch kind");
|
|
377
|
+
if (hello.bootstrapHash !== bootstrapHash) throw new Error("SSH PTC bootstrap digest differs from the configured artifact");
|
|
345
378
|
this.remote = hello;
|
|
346
379
|
let heartbeatPending;
|
|
347
380
|
this.heartbeat = setInterval(() => {
|
|
@@ -4,7 +4,7 @@ import { EventEmitter } from "node:events";
|
|
|
4
4
|
//#region lib/types/protocol.js
|
|
5
5
|
/** Bounded, versioned requests between an SSH client and its private remote helper. */
|
|
6
6
|
/** Wire version shared by the installed helper and client package. */
|
|
7
|
-
const SSH_PROTOCOL_VERSION =
|
|
7
|
+
const SSH_PROTOCOL_VERSION = 2;
|
|
8
8
|
/** Maximum prepared or running process handles owned by one helper. */
|
|
9
9
|
const SSH_MAX_PROCESS_HANDLES = 128;
|
|
10
10
|
/** Maximum open text iterators owned by one helper. */
|
package/lib/protocol.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as SshRpcPeer, i as SSH_PROTOCOL_VERSION, n as SSH_MAX_PROCESS_HANDLES, r as SSH_MAX_TEXT_STREAMS, t as RemoteOperationError } from "./protocol-
|
|
1
|
+
import { a as SshRpcPeer, i as SSH_PROTOCOL_VERSION, n as SSH_MAX_PROCESS_HANDLES, r as SSH_MAX_TEXT_STREAMS, t as RemoteOperationError } from "./protocol-CDhW_IHU.js";
|
|
2
2
|
export { RemoteOperationError, SSH_MAX_PROCESS_HANDLES, SSH_MAX_TEXT_STREAMS, SSH_PROTOCOL_VERSION, SshRpcPeer };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "./protocol-CDhW_IHU.js";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
//#region lib/types/schemas.js
|
|
3
4
|
/** Strict JSON validation for SSH helper requests and remote observations. */
|
|
@@ -114,11 +115,12 @@ const spawnSchema = z.object({
|
|
|
114
115
|
}).strict().refine((value) => value.stdio === void 0 !== (value.terminal === void 0), "select ordinary or terminal execution");
|
|
115
116
|
/** Connection handshake binds sockets and workspace to one helper process. */
|
|
116
117
|
const helloSchema = z.object({
|
|
117
|
-
protocol: z.literal(
|
|
118
|
+
protocol: z.literal(2),
|
|
118
119
|
hash: z.string().regex(/^[0-9a-f]{64}$/),
|
|
120
|
+
kind: z.enum(["node-script", "executable"]),
|
|
119
121
|
platform: z.enum(["linux", "darwin"]),
|
|
120
122
|
nodeVersion: z.string(),
|
|
121
|
-
|
|
123
|
+
executable: remotePath,
|
|
122
124
|
root: remotePath,
|
|
123
125
|
workspace: remotePath,
|
|
124
126
|
bootstrapHash: z.string().regex(/^[0-9a-f]{64}$/).optional()
|
package/lib/schemas.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as writeResultSchema, S as textStreamIdSchema, _ as remotePath, a as environmentSchema, b as targetSchema, c as infoSchema, d as outputSnapshotFrameLimit, f as outputSnapshotSchema, g as processIdSchema, h as preparedSchema, i as entriesSchema, l as intentSchema, m as policySchema, n as editResultSchema, o as foregroundSchema, p as pathInfoSchema, r as editSchema, s as helloSchema, t as doneSchema, u as outcomeSchema, v as spawnSchema, x as terminalActivitySchema, y as streamEndpointSchema } from "./schemas-
|
|
1
|
+
import { C as writeResultSchema, S as textStreamIdSchema, _ as remotePath, a as environmentSchema, b as targetSchema, c as infoSchema, d as outputSnapshotFrameLimit, f as outputSnapshotSchema, g as processIdSchema, h as preparedSchema, i as entriesSchema, l as intentSchema, m as policySchema, n as editResultSchema, o as foregroundSchema, p as pathInfoSchema, r as editSchema, s as helloSchema, t as doneSchema, u as outcomeSchema, v as spawnSchema, x as terminalActivitySchema, y as streamEndpointSchema } from "./schemas-BD3YbDxe.js";
|
|
2
2
|
export { doneSchema, editResultSchema, editSchema, entriesSchema, environmentSchema, foregroundSchema, helloSchema, infoSchema, intentSchema, outcomeSchema, outputSnapshotFrameLimit, outputSnapshotSchema, pathInfoSchema, policySchema, preparedSchema, processIdSchema, remotePath, spawnSchema, streamEndpointSchema, targetSchema, terminalActivitySchema, textStreamIdSchema, writeResultSchema };
|
package/lib/types/helper.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export interface HelperTransport {
|
|
|
7
7
|
output: Writable;
|
|
8
8
|
/** Installed entry whose digest is compared by the client. */
|
|
9
9
|
entryPath: string;
|
|
10
|
+
/** Whether the installed entry includes its own Node and worker runtime. */
|
|
11
|
+
kind: 'node-script' | 'executable';
|
|
10
12
|
/** Process termination joins the same cleanup as transport loss. */
|
|
11
13
|
signal: AbortSignal;
|
|
12
14
|
}
|
package/lib/types/index.d.ts
CHANGED
|
@@ -5,22 +5,32 @@ import schema from '@deepseek-ai/schemastery';
|
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import { helloSchema, type SshStreamEndpoint } from './schemas.ts';
|
|
7
7
|
type Hello = z.infer<typeof helloSchema>;
|
|
8
|
+
/** Installed helper invocation; script deployments may also install a PTC bootstrap. */
|
|
9
|
+
export type HelperLaunch = {
|
|
10
|
+
/** Run the installed script with a separately installed Node executable. */
|
|
11
|
+
kind: 'node-script';
|
|
12
|
+
/** Absolute remote Node executable. */
|
|
13
|
+
node: string;
|
|
14
|
+
/** Absolute remote PTC bootstrap; requires bootstrapHash. */
|
|
15
|
+
bootstrapPath?: string;
|
|
16
|
+
/** Lowercase SHA-256 of bootstrapPath; requires that path. */
|
|
17
|
+
bootstrapHash?: string;
|
|
18
|
+
} | {
|
|
19
|
+
/** Run the helper executable with its embedded Node and PTC worker. */
|
|
20
|
+
kind: 'executable';
|
|
21
|
+
};
|
|
8
22
|
/** Deployment-owned SSH identity and installed helper; no model argument selects these values. */
|
|
9
23
|
export interface Config {
|
|
10
24
|
/** OpenSSH host alias, including its existing user, key and known-host configuration. */
|
|
11
25
|
host: string;
|
|
12
|
-
/**
|
|
13
|
-
|
|
26
|
+
/** Explicit script or self-contained executable invocation. */
|
|
27
|
+
launch: HelperLaunch;
|
|
14
28
|
/** Absolute path to the installed, bundled helper entry. */
|
|
15
29
|
helper: string;
|
|
16
30
|
/** SHA-256 of that bundled helper; mismatches refuse the connection. */
|
|
17
31
|
helperHash: string;
|
|
18
32
|
/** Absolute remote default workspace. */
|
|
19
33
|
workspace: string;
|
|
20
|
-
/** Optional preinstalled built PTC entry, paired with its expected digest. */
|
|
21
|
-
bootstrapPath?: string;
|
|
22
|
-
/** SHA-256 of bootstrapPath; both fields must be supplied together. */
|
|
23
|
-
bootstrapHash?: string;
|
|
24
34
|
/** Connection and administrative-request deadline, at most 2,147,483,647 milliseconds. */
|
|
25
35
|
requestTimeoutMs?: number;
|
|
26
36
|
/** Maximum JSON payload bytes per helper request or response. */
|
|
@@ -57,10 +67,15 @@ export declare class SshConnection extends Service {
|
|
|
57
67
|
constructor(ctx: Context, config: Config);
|
|
58
68
|
/** Hold plugin readiness until the remote identity and helper digest are verified. */
|
|
59
69
|
[Service.init](): Promise<void>;
|
|
60
|
-
/** Verified remote
|
|
61
|
-
get
|
|
62
|
-
|
|
63
|
-
|
|
70
|
+
/** Verified remote PTC launch configuration; script deployments require a verified bootstrap. */
|
|
71
|
+
get ptcLaunch(): {
|
|
72
|
+
kind: 'embedded';
|
|
73
|
+
executable: string;
|
|
74
|
+
} | {
|
|
75
|
+
kind: 'node-script';
|
|
76
|
+
executable: string;
|
|
77
|
+
bootstrapPath: string;
|
|
78
|
+
};
|
|
64
79
|
/**
|
|
65
80
|
* Send a helper operation; cancellation never replays an ambiguous mutation.
|
|
66
81
|
* @param method - the private helper operation.
|
package/lib/types/protocol.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { EventEmitter } from 'node:events';
|
|
|
3
3
|
import type { Readable, Writable } from 'node:stream';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
/** Wire version shared by the installed helper and client package. */
|
|
6
|
-
export declare const SSH_PROTOCOL_VERSION =
|
|
6
|
+
export declare const SSH_PROTOCOL_VERSION = 2;
|
|
7
7
|
/** Maximum prepared or running process handles owned by one helper. */
|
|
8
8
|
export declare const SSH_MAX_PROCESS_HANDLES = 128;
|
|
9
9
|
/** Maximum open text iterators owned by one helper. */
|
package/lib/types/schemas.d.ts
CHANGED
|
@@ -126,14 +126,18 @@ export declare const spawnSchema: z.ZodObject<{
|
|
|
126
126
|
}, z.core.$strict>;
|
|
127
127
|
/** Connection handshake binds sockets and workspace to one helper process. */
|
|
128
128
|
export declare const helloSchema: z.ZodObject<{
|
|
129
|
-
protocol: z.ZodLiteral<
|
|
129
|
+
protocol: z.ZodLiteral<2>;
|
|
130
130
|
hash: z.ZodString;
|
|
131
|
+
kind: z.ZodEnum<{
|
|
132
|
+
executable: "executable";
|
|
133
|
+
"node-script": "node-script";
|
|
134
|
+
}>;
|
|
131
135
|
platform: z.ZodEnum<{
|
|
132
136
|
linux: "linux";
|
|
133
137
|
darwin: "darwin";
|
|
134
138
|
}>;
|
|
135
139
|
nodeVersion: z.ZodString;
|
|
136
|
-
|
|
140
|
+
executable: z.ZodString;
|
|
137
141
|
root: z.ZodString;
|
|
138
142
|
workspace: z.ZodString;
|
|
139
143
|
bootstrapHash: z.ZodOptional<z.ZodString>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-ssh",
|
|
3
3
|
"description": "Shared OpenSSH connection and versioned POSIX remote helper",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.1-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -45,39 +45,41 @@
|
|
|
45
45
|
],
|
|
46
46
|
"license": "MIT",
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"@deepseek-ai/cordis": "~4.0.
|
|
49
|
-
"@deepseek-ai/dsh-fs": "0.2.
|
|
50
|
-
"@deepseek-ai/dsh-fs-local": "0.2.
|
|
51
|
-
"@deepseek-ai/dsh-
|
|
52
|
-
"@deepseek-ai/dsh-
|
|
53
|
-
"@deepseek-ai/dsh-subprocess-local": "0.2.
|
|
54
|
-
"@deepseek-ai/dsh-sandbox": "0.2.
|
|
55
|
-
"@deepseek-ai/dsh-sandbox-
|
|
56
|
-
"@deepseek-ai/dsh-
|
|
57
|
-
"@deepseek-ai/dsh-
|
|
58
|
-
"@deepseek-ai/dsh-brand": "0.2.
|
|
48
|
+
"@deepseek-ai/cordis": "~4.0.5-alpha.1",
|
|
49
|
+
"@deepseek-ai/dsh-fs": "0.2.1-alpha.2",
|
|
50
|
+
"@deepseek-ai/dsh-fs-local": "0.2.1-alpha.2",
|
|
51
|
+
"@deepseek-ai/dsh-fs-sandbox": "0.2.1-alpha.2",
|
|
52
|
+
"@deepseek-ai/dsh-subprocess": "0.2.1-alpha.2",
|
|
53
|
+
"@deepseek-ai/dsh-subprocess-local": "0.2.1-alpha.2",
|
|
54
|
+
"@deepseek-ai/dsh-sandbox": "0.2.1-alpha.2",
|
|
55
|
+
"@deepseek-ai/dsh-sandbox-local": "0.2.1-alpha.2",
|
|
56
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.2.1-alpha.2",
|
|
57
|
+
"@deepseek-ai/dsh-session-projection": "0.2.1-alpha.2",
|
|
58
|
+
"@deepseek-ai/dsh-brand": "0.2.1-alpha.2",
|
|
59
|
+
"@deepseek-ai/dsh-util-values": "0.2.1-alpha.2"
|
|
59
60
|
},
|
|
60
61
|
"dependencies": {
|
|
61
|
-
"
|
|
62
|
-
"
|
|
62
|
+
"@deepseek-ai/schemastery": "~3.18.5-alpha.1",
|
|
63
|
+
"zod": "^4.4.3"
|
|
63
64
|
},
|
|
64
65
|
"devDependencies": {
|
|
65
|
-
"@deepseek-ai/cordis": "~4.0.
|
|
66
|
-
"@deepseek-ai/dsh-fs": "0.2.
|
|
67
|
-
"@deepseek-ai/dsh-fs-local": "0.2.
|
|
68
|
-
"@deepseek-ai/dsh-fs-sandbox": "0.2.
|
|
69
|
-
"@deepseek-ai/dsh-subprocess": "0.2.
|
|
70
|
-
"@deepseek-ai/dsh-subprocess-local": "0.2.
|
|
71
|
-
"@deepseek-ai/dsh-sandbox": "0.2.
|
|
72
|
-
"@deepseek-ai/dsh-sandbox-local": "0.2.
|
|
73
|
-
"@deepseek-ai/dsh-sandbox-policy": "0.2.
|
|
74
|
-
"@deepseek-ai/dsh-session-projection": "0.2.
|
|
75
|
-
"@deepseek-ai/dsh-ptc-runtime-node": "0.2.
|
|
76
|
-
"@deepseek-ai/dsh-fs-ssh": "0.2.
|
|
77
|
-
"@deepseek-ai/dsh-subprocess-ssh": "0.2.
|
|
78
|
-
"@deepseek-ai/dsh-sandbox-ssh": "0.2.
|
|
79
|
-
"@deepseek-ai/dsh-lsp": "0.2.
|
|
80
|
-
"@deepseek-ai/dsh-lsp-stdio": "0.2.
|
|
81
|
-
"@deepseek-ai/dsh-brand": "0.2.
|
|
66
|
+
"@deepseek-ai/cordis": "~4.0.5-alpha.1",
|
|
67
|
+
"@deepseek-ai/dsh-fs": "0.2.1-alpha.2",
|
|
68
|
+
"@deepseek-ai/dsh-fs-local": "0.2.1-alpha.2",
|
|
69
|
+
"@deepseek-ai/dsh-fs-sandbox": "0.2.1-alpha.2",
|
|
70
|
+
"@deepseek-ai/dsh-subprocess": "0.2.1-alpha.2",
|
|
71
|
+
"@deepseek-ai/dsh-subprocess-local": "0.2.1-alpha.2",
|
|
72
|
+
"@deepseek-ai/dsh-sandbox": "0.2.1-alpha.2",
|
|
73
|
+
"@deepseek-ai/dsh-sandbox-local": "0.2.1-alpha.2",
|
|
74
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.2.1-alpha.2",
|
|
75
|
+
"@deepseek-ai/dsh-session-projection": "0.2.1-alpha.2",
|
|
76
|
+
"@deepseek-ai/dsh-ptc-runtime-node": "0.2.1-alpha.2",
|
|
77
|
+
"@deepseek-ai/dsh-fs-ssh": "0.2.1-alpha.2",
|
|
78
|
+
"@deepseek-ai/dsh-subprocess-ssh": "0.2.1-alpha.2",
|
|
79
|
+
"@deepseek-ai/dsh-sandbox-ssh": "0.2.1-alpha.2",
|
|
80
|
+
"@deepseek-ai/dsh-lsp": "0.2.1-alpha.2",
|
|
81
|
+
"@deepseek-ai/dsh-lsp-stdio": "0.2.1-alpha.2",
|
|
82
|
+
"@deepseek-ai/dsh-brand": "0.2.1-alpha.2",
|
|
83
|
+
"@deepseek-ai/dsh-util-values": "0.2.1-alpha.2"
|
|
82
84
|
}
|
|
83
85
|
}
|