dshb-exec-ssh 0.0.1

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.
@@ -0,0 +1,2 @@
1
+ import { n as SshSubprocessRuntime, t as SshShellExecutor } from "./process-provider-CUgC5gyu.js";
2
+ export { SshShellExecutor, SshSubprocessRuntime };
@@ -0,0 +1,75 @@
1
+ //#region src/process-provider.ts
2
+ var SshShellExecutor = class {
3
+ executor;
4
+ constructor(executor) {
5
+ this.executor = executor;
6
+ }
7
+ resolve(request) {
8
+ return {
9
+ command: request.command,
10
+ workdir: request.workdir ?? "/",
11
+ timeoutMs: request.timeoutMs ?? 6e4,
12
+ stdoutMaxBytes: request.stdoutMaxBytes ?? 1048576,
13
+ signal: request.signal,
14
+ stdin: request.stdin,
15
+ env: request.env,
16
+ sandboxPolicy: request.sandboxPolicy
17
+ };
18
+ }
19
+ async run(spec) {
20
+ const result = await this.executor.exec([
21
+ "sh",
22
+ "-c",
23
+ spec.command
24
+ ], spec.workdir, spec.env ?? {}, spec.stdin !== void 0 ? Buffer.from(spec.stdin) : void 0);
25
+ const collect = (text) => ({
26
+ text,
27
+ truncated: false
28
+ });
29
+ return {
30
+ exitCode: result.code,
31
+ signal: result.signal ?? null,
32
+ timedOut: false,
33
+ aborted: false,
34
+ timeoutMs: spec.timeoutMs,
35
+ stdout: collect(result.stdout),
36
+ stderr: collect(result.stderr)
37
+ };
38
+ }
39
+ start(spec) {
40
+ return {
41
+ pid: -1,
42
+ done: this.run(spec),
43
+ kill: () => {}
44
+ };
45
+ }
46
+ };
47
+ var SshSubprocessRuntime = class {
48
+ executor;
49
+ constructor(executor) {
50
+ this.executor = executor;
51
+ }
52
+ spawn(spec) {
53
+ return {
54
+ pid: -1,
55
+ done: this.executor.exec(spec.argv, spec.cwd, spec.env ?? {}).then((r) => ({
56
+ exitCode: r.code,
57
+ signal: r.signal ?? null
58
+ })),
59
+ kill: () => {}
60
+ };
61
+ }
62
+ async spawnTerminal(spec) {
63
+ const pty = await this.executor.pty(spec.argv, spec.cwd, spec.env ?? {}, spec.cols ?? 80, spec.rows ?? 24);
64
+ return {
65
+ write: (data) => pty.channel.stdin.write(data),
66
+ resize: (cols, rows) => pty.resize(cols, rows),
67
+ onData: (cb) => pty.onData((chunk) => cb(chunk.toString("utf8"))),
68
+ kill: () => pty.kill()
69
+ };
70
+ }
71
+ };
72
+ //#endregion
73
+ export { SshShellExecutor, SshSubprocessRuntime };
74
+
75
+ //# sourceMappingURL=process-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-provider.js","names":[],"sources":["../src/process-provider.ts"],"sourcesContent":["import type { ShellExecRequest, ShellExecSpec, ShellProcess, ShellRunResult } from '@deepseek-ai/dsh-shell'\nimport type { CollectedOutput, SubprocessHandle, SubprocessSpawnSpec, SubprocessTerminalHandle, SubprocessTerminalSpawnSpec } from '@deepseek-ai/dsh-subprocess'\nimport type { SshExecutor } from './executor.js'\n\nexport class SshShellExecutor {\n constructor(private readonly executor: SshExecutor) {}\n\n resolve(request: ShellExecRequest): ShellExecSpec {\n return {\n command: request.command,\n workdir: request.workdir ?? '/',\n timeoutMs: request.timeoutMs ?? 60_000,\n stdoutMaxBytes: request.stdoutMaxBytes ?? 1024 * 1024,\n signal: request.signal,\n stdin: request.stdin,\n env: request.env,\n sandboxPolicy: request.sandboxPolicy,\n } as ShellExecSpec\n }\n\n async run(spec: ShellExecSpec): Promise<ShellRunResult> {\n const result = await this.executor.exec(['sh', '-c', spec.command], spec.workdir, (spec.env ?? {}) as Record<string, string>, spec.stdin !== undefined ? Buffer.from(spec.stdin) : undefined)\n const collect = (text: string): CollectedOutput => ({ text, truncated: false })\n return {\n exitCode: result.code,\n signal: (result.signal ?? null) as NodeJS.Signals | null,\n timedOut: false,\n aborted: false,\n timeoutMs: spec.timeoutMs,\n stdout: collect(result.stdout),\n stderr: collect(result.stderr),\n } as unknown as ShellRunResult\n }\n\n start(spec: ShellExecSpec): ShellProcess {\n const promise = this.run(spec)\n const proc: ShellProcess = {\n pid: -1,\n done: promise,\n kill: () => {\n void promise\n },\n } as unknown as ShellProcess\n return proc\n }\n}\n\nexport class SshSubprocessRuntime {\n constructor(private readonly executor: SshExecutor) {}\n\n spawn(spec: SubprocessSpawnSpec): SubprocessHandle {\n const promise = this.executor.exec(spec.argv as string[], spec.cwd, (spec.env ?? {}) as Record<string, string>)\n const handle: SubprocessHandle = {\n pid: -1,\n done: promise.then((r) => ({ exitCode: r.code, signal: (r.signal ?? null) as NodeJS.Signals | null })),\n kill: () => {\n void promise\n },\n } as unknown as SubprocessHandle\n return handle\n }\n\n async spawnTerminal(spec: SubprocessTerminalSpawnSpec): Promise<SubprocessTerminalHandle> {\n const pty = await this.executor.pty(spec.argv as string[], spec.cwd, (spec.env ?? {}) as Record<string, string>, (spec as { cols?: number }).cols ?? 80, (spec as { rows?: number }).rows ?? 24)\n const handle: SubprocessTerminalHandle = {\n write: (data: string) => pty.channel.stdin.write(data),\n resize: (cols: number, rows: number) => pty.resize(cols, rows),\n onData: (cb: (data: string) => void) => pty.onData((chunk) => cb(chunk.toString('utf8'))),\n kill: () => pty.kill(),\n } as unknown as SubprocessTerminalHandle\n return handle\n }\n}\n"],"mappings":";AAIA,IAAa,mBAAb,MAA8B;CACC;CAA7B,YAAY,UAAwC;EAAvB,KAAA,WAAA;CAAwB;CAErD,QAAQ,SAA0C;EAChD,OAAO;GACL,SAAS,QAAQ;GACjB,SAAS,QAAQ,WAAW;GAC5B,WAAW,QAAQ,aAAa;GAChC,gBAAgB,QAAQ,kBAAkB;GAC1C,QAAQ,QAAQ;GAChB,OAAO,QAAQ;GACf,KAAK,QAAQ;GACb,eAAe,QAAQ;EACzB;CACF;CAEA,MAAM,IAAI,MAA8C;EACtD,MAAM,SAAS,MAAM,KAAK,SAAS,KAAK;GAAC;GAAM;GAAM,KAAK;EAAO,GAAG,KAAK,SAAU,KAAK,OAAO,CAAC,GAA8B,KAAK,UAAU,KAAA,IAAY,OAAO,KAAK,KAAK,KAAK,IAAI,KAAA,CAAS;EAC5L,MAAM,WAAW,UAAmC;GAAE;GAAM,WAAW;EAAM;EAC7E,OAAO;GACL,UAAU,OAAO;GACjB,QAAS,OAAO,UAAU;GAC1B,UAAU;GACV,SAAS;GACT,WAAW,KAAK;GAChB,QAAQ,QAAQ,OAAO,MAAM;GAC7B,QAAQ,QAAQ,OAAO,MAAM;EAC/B;CACF;CAEA,MAAM,MAAmC;EASvC,OAAO;GANL,KAAK;GACL,MAHc,KAAK,IAAI,IAGX;GACZ,YAAY,CAEZ;EAEQ;CACZ;AACF;AAEA,IAAa,uBAAb,MAAkC;CACH;CAA7B,YAAY,UAAwC;EAAvB,KAAA,WAAA;CAAwB;CAErD,MAAM,MAA6C;EASjD,OAAO;GANL,KAAK;GACL,MAHc,KAAK,SAAS,KAAK,KAAK,MAAkB,KAAK,KAAM,KAAK,OAAO,CAAC,CAGpE,CAAC,CAAC,MAAM,OAAO;IAAE,UAAU,EAAE;IAAM,QAAS,EAAE,UAAU;GAA+B,EAAE;GACrG,YAAY,CAEZ;EAEU;CACd;CAEA,MAAM,cAAc,MAAsE;EACxF,MAAM,MAAM,MAAM,KAAK,SAAS,IAAI,KAAK,MAAkB,KAAK,KAAM,KAAK,OAAO,CAAC,GAA+B,KAA2B,QAAQ,IAAK,KAA2B,QAAQ,EAAE;EAO/L,OAAO;GALL,QAAQ,SAAiB,IAAI,QAAQ,MAAM,MAAM,IAAI;GACrD,SAAS,MAAc,SAAiB,IAAI,OAAO,MAAM,IAAI;GAC7D,SAAS,OAA+B,IAAI,QAAQ,UAAU,GAAG,MAAM,SAAS,MAAM,CAAC,CAAC;GACxF,YAAY,IAAI,KAAK;EAEX;CACd;AACF"}
@@ -0,0 +1,13 @@
1
+ //#region src/runner.d.ts
2
+ interface RunnerPayload {
3
+ argv: string[];
4
+ cwd: string;
5
+ env: Record<string, string>;
6
+ }
7
+ declare function encodeRunnerPayload(payload: RunnerPayload): Buffer;
8
+ declare const REMOTE_RUNNER_SCRIPT = "set -u\nif ! command -v base64 >/dev/null 2>&1; then echo \"runner: base64 not found\" >&2; exit 127; fi\ndec() {\n printf '%s' \"$1\" | base64 -d 2>/dev/null || printf '%s' \"$1\" | base64 -D 2>/dev/null\n}\nreadline_crlf() {\n IFS= read -r line\n line=${line%$'\\r'}\n}\nreadline_crlf\ncwd=$(dec \"$line\")\nreadline_crlf\nargc=$(dec \"$line\")\nargv=()\ni=0\nwhile [ \"$i\" -lt \"$argc\" ]; do\n readline_crlf\n argv+=(\"$(dec \"$line\")\")\n i=$((i + 1))\ndone\nreadline_crlf\nenvc=$(dec \"$line\")\nenv_vars=()\ni=0\nwhile [ \"$i\" -lt \"$envc\" ]; do\n readline_crlf\n kv=$(dec \"$line\")\n k=${kv%%=*}\n v=${kv#*=}\n env_vars+=(\"$k=$v\")\n i=$((i + 1))\ndone\nreadline_crlf\n[ \"$line\" = \".\" ] || { echo \"runner: protocol error\" >&2; exit 126; }\ncd \"$cwd\" 2>/dev/null || { echo \"runner: cwd not found: $cwd\" >&2; exit 126; }\nexec env -i HOME=\"$HOME\" PATH=\"${PATH:-/usr/local/bin:/usr/bin:/bin}\" ${env_vars[@]+\"${env_vars[@]}\"} \"${argv[@]}\"\n";
9
+ declare const REMOTE_RUNNER_COMMAND: string;
10
+ declare function isRunnerSafe(command: string): boolean;
11
+ //#endregion
12
+ export { REMOTE_RUNNER_COMMAND, REMOTE_RUNNER_SCRIPT, RunnerPayload, encodeRunnerPayload, isRunnerSafe };
13
+ //# sourceMappingURL=runner.d.ts.map
package/lib/runner.js ADDED
@@ -0,0 +1,62 @@
1
+ //#region src/runner.ts
2
+ function encodeLine(value) {
3
+ return Buffer.from(`${value}\n`, "utf8").toString("base64");
4
+ }
5
+ function encodeRunnerPayload(payload) {
6
+ const lines = [];
7
+ lines.push(encodeLine(payload.cwd));
8
+ lines.push(encodeLine(String(payload.argv.length)));
9
+ for (const arg of payload.argv) lines.push(encodeLine(arg));
10
+ const envEntries = Object.entries(payload.env);
11
+ lines.push(encodeLine(String(envEntries.length)));
12
+ for (const [k, v] of envEntries) lines.push(encodeLine(`${k}=${v}`));
13
+ lines.push(".");
14
+ return Buffer.from(`${lines.join("\n")}\n`, "utf8");
15
+ }
16
+ const RUNNER_SCRIPT = `set -u
17
+ if ! command -v base64 >/dev/null 2>&1; then echo "runner: base64 not found" >&2; exit 127; fi
18
+ dec() {
19
+ printf '%s' "$1" | base64 -d 2>/dev/null || printf '%s' "$1" | base64 -D 2>/dev/null
20
+ }
21
+ readline_crlf() {
22
+ IFS= read -r line
23
+ line=\${line%$'\\r'}
24
+ }
25
+ readline_crlf
26
+ cwd=$(dec "$line")
27
+ readline_crlf
28
+ argc=$(dec "$line")
29
+ argv=()
30
+ i=0
31
+ while [ "$i" -lt "$argc" ]; do
32
+ readline_crlf
33
+ argv+=("$(dec "$line")")
34
+ i=$((i + 1))
35
+ done
36
+ readline_crlf
37
+ envc=$(dec "$line")
38
+ env_vars=()
39
+ i=0
40
+ while [ "$i" -lt "$envc" ]; do
41
+ readline_crlf
42
+ kv=$(dec "$line")
43
+ k=\${kv%%=*}
44
+ v=\${kv#*=}
45
+ env_vars+=("$k=$v")
46
+ i=$((i + 1))
47
+ done
48
+ readline_crlf
49
+ [ "$line" = "." ] || { echo "runner: protocol error" >&2; exit 126; }
50
+ cd "$cwd" 2>/dev/null || { echo "runner: cwd not found: $cwd" >&2; exit 126; }
51
+ exec env -i HOME="$HOME" PATH="\${PATH:-/usr/local/bin:/usr/bin:/bin}" \${env_vars[@]+"\${env_vars[@]}"} "\${argv[@]}"
52
+ `;
53
+ const RUNNER_SCRIPT_B64 = Buffer.from(RUNNER_SCRIPT, "utf8").toString("base64");
54
+ const REMOTE_RUNNER_SCRIPT = RUNNER_SCRIPT;
55
+ const REMOTE_RUNNER_COMMAND = `bash -c "$(echo ${RUNNER_SCRIPT_B64} | base64 -d)"`;
56
+ function isRunnerSafe(command) {
57
+ return command === REMOTE_RUNNER_COMMAND || command.includes(RUNNER_SCRIPT_B64);
58
+ }
59
+ //#endregion
60
+ export { REMOTE_RUNNER_COMMAND, REMOTE_RUNNER_SCRIPT, encodeRunnerPayload, isRunnerSafe };
61
+
62
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","names":[],"sources":["../src/runner.ts"],"sourcesContent":["export interface RunnerPayload {\n argv: string[]\n cwd: string\n env: Record<string, string>\n}\n\nfunction encodeLine(value: string): string {\n return Buffer.from(`${value}\\n`, 'utf8').toString('base64')\n}\n\nexport function encodeRunnerPayload(payload: RunnerPayload): Buffer {\n const lines: string[] = []\n lines.push(encodeLine(payload.cwd))\n lines.push(encodeLine(String(payload.argv.length)))\n for (const arg of payload.argv) lines.push(encodeLine(arg))\n const envEntries = Object.entries(payload.env)\n lines.push(encodeLine(String(envEntries.length)))\n for (const [k, v] of envEntries) lines.push(encodeLine(`${k}=${v}`))\n lines.push('.')\n return Buffer.from(`${lines.join('\\n')}\\n`, 'utf8')\n}\n\nconst RUNNER_SCRIPT = `set -u\nif ! command -v base64 >/dev/null 2>&1; then echo \"runner: base64 not found\" >&2; exit 127; fi\ndec() {\n printf '%s' \"$1\" | base64 -d 2>/dev/null || printf '%s' \"$1\" | base64 -D 2>/dev/null\n}\nreadline_crlf() {\n IFS= read -r line\n line=\\${line%$'\\\\r'}\n}\nreadline_crlf\ncwd=$(dec \"$line\")\nreadline_crlf\nargc=$(dec \"$line\")\nargv=()\ni=0\nwhile [ \"$i\" -lt \"$argc\" ]; do\n readline_crlf\n argv+=(\"$(dec \"$line\")\")\n i=$((i + 1))\ndone\nreadline_crlf\nenvc=$(dec \"$line\")\nenv_vars=()\ni=0\nwhile [ \"$i\" -lt \"$envc\" ]; do\n readline_crlf\n kv=$(dec \"$line\")\n k=\\${kv%%=*}\n v=\\${kv#*=}\n env_vars+=(\"$k=$v\")\n i=$((i + 1))\ndone\nreadline_crlf\n[ \"$line\" = \".\" ] || { echo \"runner: protocol error\" >&2; exit 126; }\ncd \"$cwd\" 2>/dev/null || { echo \"runner: cwd not found: $cwd\" >&2; exit 126; }\nexec env -i HOME=\"$HOME\" PATH=\"\\${PATH:-/usr/local/bin:/usr/bin:/bin}\" \\${env_vars[@]+\"\\${env_vars[@]}\"} \"\\${argv[@]}\"\n`\n\nconst RUNNER_SCRIPT_B64 = Buffer.from(RUNNER_SCRIPT, 'utf8').toString('base64')\n\nexport const REMOTE_RUNNER_SCRIPT = RUNNER_SCRIPT\n\nexport const REMOTE_RUNNER_COMMAND = `bash -c \"$(echo ${RUNNER_SCRIPT_B64} | base64 -d)\"`\n\nexport function isRunnerSafe(command: string): boolean {\n return command === REMOTE_RUNNER_COMMAND || command.includes(RUNNER_SCRIPT_B64)\n}\n"],"mappings":";AAMA,SAAS,WAAW,OAAuB;CACzC,OAAO,OAAO,KAAK,GAAG,MAAM,KAAK,MAAM,CAAC,CAAC,SAAS,QAAQ;AAC5D;AAEA,SAAgB,oBAAoB,SAAgC;CAClE,MAAM,QAAkB,CAAC;CACzB,MAAM,KAAK,WAAW,QAAQ,GAAG,CAAC;CAClC,MAAM,KAAK,WAAW,OAAO,QAAQ,KAAK,MAAM,CAAC,CAAC;CAClD,KAAK,MAAM,OAAO,QAAQ,MAAM,MAAM,KAAK,WAAW,GAAG,CAAC;CAC1D,MAAM,aAAa,OAAO,QAAQ,QAAQ,GAAG;CAC7C,MAAM,KAAK,WAAW,OAAO,WAAW,MAAM,CAAC,CAAC;CAChD,KAAK,MAAM,CAAC,GAAG,MAAM,YAAY,MAAM,KAAK,WAAW,GAAG,EAAE,GAAG,GAAG,CAAC;CACnE,MAAM,KAAK,GAAG;CACd,OAAO,OAAO,KAAK,GAAG,MAAM,KAAK,IAAI,EAAE,KAAK,MAAM;AACpD;AAEA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCtB,MAAM,oBAAoB,OAAO,KAAK,eAAe,MAAM,CAAC,CAAC,SAAS,QAAQ;AAE9E,MAAa,uBAAuB;AAEpC,MAAa,wBAAwB,mBAAmB,kBAAkB;AAE1E,SAAgB,aAAa,SAA0B;CACrD,OAAO,YAAY,yBAAyB,QAAQ,SAAS,iBAAiB;AAChF"}
package/lib/world.d.ts ADDED
@@ -0,0 +1,79 @@
1
+ import { n as SshSubprocessRuntime, t as SshShellExecutor } from "./process-provider-CUgC5gyu.js";
2
+ import { SshConnectionPool } from "./connection-pool.js";
3
+ import { SshExecutor } from "./executor.js";
4
+ import { SshFileSystem } from "./fs-provider.js";
5
+ import { ExecutionWorldProvider, NodeTestReport } from "dshb-router/types";
6
+ //#region src/world.d.ts
7
+ interface SshNodeConfig {
8
+ nodeId: string;
9
+ host: string;
10
+ port: number;
11
+ username: string;
12
+ auth: {
13
+ kind: 'password' | 'key' | 'agent';
14
+ keyPath?: string;
15
+ };
16
+ secrets: {
17
+ password?: string;
18
+ privateKey?: string;
19
+ passphrase?: string;
20
+ };
21
+ jump?: Array<{
22
+ host: string;
23
+ port?: number;
24
+ username?: string;
25
+ keyPath?: string;
26
+ }>;
27
+ }
28
+ declare class SshExecutionWorld implements ExecutionWorldProvider {
29
+ readonly nodeId: string;
30
+ private readonly config;
31
+ readonly fs: SshFileSystem;
32
+ readonly subprocess: SshSubprocessRuntime;
33
+ readonly shell: SshShellExecutor;
34
+ readonly executor: SshExecutor;
35
+ constructor(nodeId: string, config: SshNodeConfig, pool: SshConnectionPool, audit?: (nodeId: string, op: string, target: string, result: string) => void);
36
+ ensureDir(remotePath: string): Promise<void>;
37
+ testConnection(): Promise<NodeTestReport>;
38
+ }
39
+ interface NodeRegistryLike {
40
+ get(id: string): {
41
+ id: string;
42
+ type: string;
43
+ ssh?: {
44
+ host: string;
45
+ port: number;
46
+ username: string;
47
+ auth: {
48
+ kind: 'password' | 'key' | 'agent';
49
+ keyPath?: string;
50
+ };
51
+ jump?: Array<{
52
+ host: string;
53
+ port?: number;
54
+ username?: string;
55
+ keyPath?: string;
56
+ }>;
57
+ };
58
+ } | undefined;
59
+ getSecrets(id: string): Promise<{
60
+ password?: string;
61
+ privateKey?: string;
62
+ passphrase?: string;
63
+ } | undefined>;
64
+ }
65
+ declare class SshWorldRegistry {
66
+ private readonly pool;
67
+ private readonly nodeRegistry?;
68
+ private readonly audit?;
69
+ private readonly worlds;
70
+ constructor(pool: SshConnectionPool, nodeRegistry?: NodeRegistryLike | undefined, audit?: ((nodeId: string, op: string, target: string, result: string) => void) | undefined);
71
+ register(config: SshNodeConfig): SshExecutionWorld;
72
+ get(nodeId: string): SshExecutionWorld | undefined;
73
+ ensure(nodeId: string): Promise<SshExecutionWorld | undefined>;
74
+ remove(nodeId: string): void;
75
+ clear(): void;
76
+ }
77
+ //#endregion
78
+ export { SshExecutionWorld, SshNodeConfig, SshWorldRegistry };
79
+ //# sourceMappingURL=world.d.ts.map
package/lib/world.js ADDED
@@ -0,0 +1,95 @@
1
+ import { SshExecutor } from "./executor.js";
2
+ import { SshFileSystem } from "./fs-provider.js";
3
+ import { SshShellExecutor, SshSubprocessRuntime } from "./process-provider.js";
4
+ //#region src/world.ts
5
+ var SshExecutionWorld = class {
6
+ nodeId;
7
+ config;
8
+ fs;
9
+ subprocess;
10
+ shell;
11
+ executor;
12
+ constructor(nodeId, config, pool, audit) {
13
+ this.nodeId = nodeId;
14
+ this.config = config;
15
+ const target = {
16
+ host: config.host,
17
+ port: config.port,
18
+ username: config.username,
19
+ auth: config.auth,
20
+ jump: config.jump
21
+ };
22
+ const connection = pool.acquire(target, config.secrets);
23
+ this.executor = new SshExecutor(connection, nodeId, audit ? (op, t, r) => audit(nodeId, op, t, r) : void 0);
24
+ this.fs = new SshFileSystem(this.executor);
25
+ this.subprocess = new SshSubprocessRuntime(this.executor);
26
+ this.shell = new SshShellExecutor(this.executor);
27
+ }
28
+ async ensureDir(remotePath) {
29
+ await this.executor.exec([
30
+ "mkdir",
31
+ "-p",
32
+ remotePath
33
+ ], "/");
34
+ }
35
+ async testConnection() {
36
+ try {
37
+ return {
38
+ ok: (await this.executor.exec(["echo", "ok"], "/")).stdout.trim() === "ok",
39
+ reachable: true
40
+ };
41
+ } catch (err) {
42
+ return {
43
+ ok: false,
44
+ reachable: false,
45
+ error: err instanceof Error ? err.message : String(err)
46
+ };
47
+ }
48
+ }
49
+ };
50
+ var SshWorldRegistry = class {
51
+ pool;
52
+ nodeRegistry;
53
+ audit;
54
+ worlds = /* @__PURE__ */ new Map();
55
+ constructor(pool, nodeRegistry, audit) {
56
+ this.pool = pool;
57
+ this.nodeRegistry = nodeRegistry;
58
+ this.audit = audit;
59
+ }
60
+ register(config) {
61
+ const world = new SshExecutionWorld(config.nodeId, config, this.pool, this.audit);
62
+ this.worlds.set(config.nodeId, world);
63
+ return world;
64
+ }
65
+ get(nodeId) {
66
+ return this.worlds.get(nodeId);
67
+ }
68
+ async ensure(nodeId) {
69
+ const existing = this.worlds.get(nodeId);
70
+ if (existing) return existing;
71
+ if (!this.nodeRegistry) return void 0;
72
+ const node = this.nodeRegistry.get(nodeId);
73
+ if (!node || node.type !== "remote-ssh" && node.type !== "remote-docker" || !node.ssh) return void 0;
74
+ const secrets = await this.nodeRegistry.getSecrets(nodeId) ?? {};
75
+ return this.register({
76
+ nodeId,
77
+ host: node.ssh.host,
78
+ port: node.ssh.port,
79
+ username: node.ssh.username,
80
+ auth: node.ssh.auth,
81
+ secrets,
82
+ jump: node.ssh.jump
83
+ });
84
+ }
85
+ remove(nodeId) {
86
+ if (this.worlds.get(nodeId)) this.worlds.delete(nodeId);
87
+ }
88
+ clear() {
89
+ this.worlds.clear();
90
+ }
91
+ };
92
+ //#endregion
93
+ export { SshExecutionWorld, SshWorldRegistry };
94
+
95
+ //# sourceMappingURL=world.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"world.js","names":[],"sources":["../src/world.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis'\nimport type { ExecutionWorldProvider, NodeTestReport } from 'dshb-router/types'\nimport { sharedWorldResolver } from 'dshb-router/resolve'\nimport { SshConnectionPool } from './connection-pool.js'\nimport { SshExecutor } from './executor.js'\nimport { SshFileSystem } from './fs-provider.js'\nimport { SshShellExecutor, SshSubprocessRuntime } from './process-provider.js'\n\nexport interface SshNodeConfig {\n nodeId: string\n host: string\n port: number\n username: string\n auth: { kind: 'password' | 'key' | 'agent'; keyPath?: string }\n secrets: { password?: string; privateKey?: string; passphrase?: string }\n jump?: Array<{ host: string; port?: number; username?: string; keyPath?: string }>\n}\n\nexport class SshExecutionWorld implements ExecutionWorldProvider {\n readonly fs: SshFileSystem\n readonly subprocess: SshSubprocessRuntime\n readonly shell: SshShellExecutor\n readonly executor: SshExecutor\n\n constructor(\n readonly nodeId: string,\n private readonly config: SshNodeConfig,\n pool: SshConnectionPool,\n audit?: (nodeId: string, op: string, target: string, result: string) => void,\n ) {\n const target = {\n host: config.host,\n port: config.port,\n username: config.username,\n auth: config.auth,\n jump: config.jump,\n }\n const connection = pool.acquire(target, config.secrets)\n this.executor = new SshExecutor(connection, nodeId, audit ? (op, t, r) => audit(nodeId, op, t, r) : undefined)\n this.fs = new SshFileSystem(this.executor)\n this.subprocess = new SshSubprocessRuntime(this.executor)\n this.shell = new SshShellExecutor(this.executor)\n }\n\n async ensureDir(remotePath: string): Promise<void> {\n await this.executor.exec(['mkdir', '-p', remotePath], '/')\n }\n\n async testConnection(): Promise<NodeTestReport> {\n try {\n const result = await this.executor.exec(['echo', 'ok'], '/')\n return { ok: result.stdout.trim() === 'ok', reachable: true }\n } catch (err) {\n return { ok: false, reachable: false, error: err instanceof Error ? err.message : String(err) }\n }\n }\n}\n\ninterface NodeRegistryLike {\n get(id: string): { id: string; type: string; ssh?: { host: string; port: number; username: string; auth: { kind: 'password' | 'key' | 'agent'; keyPath?: string }; jump?: Array<{ host: string; port?: number; username?: string; keyPath?: string }> } } | undefined\n getSecrets(id: string): Promise<{ password?: string; privateKey?: string; passphrase?: string } | undefined>\n}\n\nexport class SshWorldRegistry {\n private readonly worlds = new Map<string, SshExecutionWorld>()\n\n constructor(\n private readonly pool: SshConnectionPool,\n private readonly nodeRegistry?: NodeRegistryLike,\n private readonly audit?: (nodeId: string, op: string, target: string, result: string) => void,\n ) {}\n\n register(config: SshNodeConfig): SshExecutionWorld {\n const world = new SshExecutionWorld(config.nodeId, config, this.pool, this.audit)\n this.worlds.set(config.nodeId, world)\n return world\n }\n\n get(nodeId: string): SshExecutionWorld | undefined {\n return this.worlds.get(nodeId)\n }\n\n async ensure(nodeId: string): Promise<SshExecutionWorld | undefined> {\n const existing = this.worlds.get(nodeId)\n if (existing) return existing\n if (!this.nodeRegistry) return undefined\n const node = this.nodeRegistry.get(nodeId)\n if (!node || (node.type !== 'remote-ssh' && node.type !== 'remote-docker') || !node.ssh) return undefined\n const secrets = (await this.nodeRegistry.getSecrets(nodeId)) ?? {}\n return this.register({\n nodeId,\n host: node.ssh.host,\n port: node.ssh.port,\n username: node.ssh.username,\n auth: node.ssh.auth,\n secrets,\n jump: node.ssh.jump,\n })\n }\n\n remove(nodeId: string): void {\n const world = this.worlds.get(nodeId)\n if (world) {\n this.worlds.delete(nodeId)\n }\n }\n\n clear(): void {\n this.worlds.clear()\n }\n}\n"],"mappings":";;;;AAkBA,IAAa,oBAAb,MAAiE;CAOpD;CACQ;CAPnB;CACA;CACA;CACA;CAEA,YACE,QACA,QACA,MACA,OACA;EAJS,KAAA,SAAA;EACQ,KAAA,SAAA;EAIjB,MAAM,SAAS;GACb,MAAM,OAAO;GACb,MAAM,OAAO;GACb,UAAU,OAAO;GACjB,MAAM,OAAO;GACb,MAAM,OAAO;EACf;EACA,MAAM,aAAa,KAAK,QAAQ,QAAQ,OAAO,OAAO;EACtD,KAAK,WAAW,IAAI,YAAY,YAAY,QAAQ,SAAS,IAAI,GAAG,MAAM,MAAM,QAAQ,IAAI,GAAG,CAAC,IAAI,KAAA,CAAS;EAC7G,KAAK,KAAK,IAAI,cAAc,KAAK,QAAQ;EACzC,KAAK,aAAa,IAAI,qBAAqB,KAAK,QAAQ;EACxD,KAAK,QAAQ,IAAI,iBAAiB,KAAK,QAAQ;CACjD;CAEA,MAAM,UAAU,YAAmC;EACjD,MAAM,KAAK,SAAS,KAAK;GAAC;GAAS;GAAM;EAAU,GAAG,GAAG;CAC3D;CAEA,MAAM,iBAA0C;EAC9C,IAAI;GAEF,OAAO;IAAE,KAAI,MADQ,KAAK,SAAS,KAAK,CAAC,QAAQ,IAAI,GAAG,GAAG,EAAA,CACvC,OAAO,KAAK,MAAM;IAAM,WAAW;GAAK;EAC9D,SAAS,KAAK;GACZ,OAAO;IAAE,IAAI;IAAO,WAAW;IAAO,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;GAAE;EAChG;CACF;AACF;AAOA,IAAa,mBAAb,MAA8B;CAIT;CACA;CACA;CALnB,yBAA0B,IAAI,IAA+B;CAE7D,YACE,MACA,cACA,OACA;EAHiB,KAAA,OAAA;EACA,KAAA,eAAA;EACA,KAAA,QAAA;CAChB;CAEH,SAAS,QAA0C;EACjD,MAAM,QAAQ,IAAI,kBAAkB,OAAO,QAAQ,QAAQ,KAAK,MAAM,KAAK,KAAK;EAChF,KAAK,OAAO,IAAI,OAAO,QAAQ,KAAK;EACpC,OAAO;CACT;CAEA,IAAI,QAA+C;EACjD,OAAO,KAAK,OAAO,IAAI,MAAM;CAC/B;CAEA,MAAM,OAAO,QAAwD;EACnE,MAAM,WAAW,KAAK,OAAO,IAAI,MAAM;EACvC,IAAI,UAAU,OAAO;EACrB,IAAI,CAAC,KAAK,cAAc,OAAO,KAAA;EAC/B,MAAM,OAAO,KAAK,aAAa,IAAI,MAAM;EACzC,IAAI,CAAC,QAAS,KAAK,SAAS,gBAAgB,KAAK,SAAS,mBAAoB,CAAC,KAAK,KAAK,OAAO,KAAA;EAChG,MAAM,UAAW,MAAM,KAAK,aAAa,WAAW,MAAM,KAAM,CAAC;EACjE,OAAO,KAAK,SAAS;GACnB;GACA,MAAM,KAAK,IAAI;GACf,MAAM,KAAK,IAAI;GACf,UAAU,KAAK,IAAI;GACnB,MAAM,KAAK,IAAI;GACf;GACA,MAAM,KAAK,IAAI;EACjB,CAAC;CACH;CAEA,OAAO,QAAsB;EAE3B,IADc,KAAK,OAAO,IAAI,MACtB,GACN,KAAK,OAAO,OAAO,MAAM;CAE7B;CAEA,QAAc;EACZ,KAAK,OAAO,MAAM;CACpB;AACF"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "dshb-exec-ssh",
3
+ "version": "0.0.1",
4
+ "description": "DSHB 远程 SSH 执行世界(exec 行协议 / SFTP / PTY over ssh2)",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "exports": {
8
+ ".": "./lib/index.js",
9
+ "./connection": "./lib/connection.js",
10
+ "./connection-pool": "./lib/connection-pool.js",
11
+ "./env-scrub": "./lib/env-scrub.js",
12
+ "./executor": "./lib/executor.js",
13
+ "./fs-provider": "./lib/fs-provider.js",
14
+ "./process-provider": "./lib/process-provider.js",
15
+ "./world": "./lib/world.js",
16
+ "./runner": "./lib/runner.js",
17
+ "./package.json": "./package.json"
18
+ },
19
+ "files": [
20
+ "lib",
21
+ "cordis.patch.yml"
22
+ ],
23
+ "dsh": {
24
+ "bundle": {
25
+ "patch": "./cordis.patch.yml"
26
+ }
27
+ },
28
+ "dependencies": {
29
+ "dshb-router": "0.0.1",
30
+ "ssh2": "^1.16.0"
31
+ },
32
+ "devDependencies": {
33
+ "@deepseek-ai/cordis": "^4.0.2",
34
+ "@deepseek-ai/dsh-fs": "^0.1.1-rc.2",
35
+ "@deepseek-ai/dsh-sandbox": "^0.1.1-rc.2",
36
+ "@deepseek-ai/dsh-shell": "^0.1.1-rc.2",
37
+ "@deepseek-ai/dsh-subprocess": "^0.1.1-rc.2",
38
+ "@types/ssh2": "^1.15.6"
39
+ },
40
+ "types": "lib/index.d.ts",
41
+ "scripts": {
42
+ "build": "tsdown",
43
+ "typecheck": "tsc --noEmit"
44
+ }
45
+ }