@artenx/dshb 0.1.7 → 0.1.9

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.
Files changed (42) hide show
  1. package/README.md +1 -1
  2. package/lib/auth/index.js +2 -1
  3. package/lib/auth/index.js.map +1 -1
  4. package/lib/client.js +1 -1
  5. package/lib/client.js.map +1 -1
  6. package/lib/core/index.js +1 -1
  7. package/lib/core/workspace-instruction-context.d.ts +281 -122
  8. package/lib/core/workspace-instruction-context.js +1 -1
  9. package/lib/exec-docker/docker-backend.d.ts +1 -1
  10. package/lib/exec-docker/docker-client.d.ts +1 -1
  11. package/lib/exec-docker/docker-client.js +4 -1
  12. package/lib/exec-docker/docker-client.js.map +1 -1
  13. package/lib/exec-docker/process-provider.d.ts +3 -1
  14. package/lib/exec-docker/process-provider.js +68 -5
  15. package/lib/exec-docker/process-provider.js.map +1 -1
  16. package/lib/exec-docker/remote-docker-cli.d.ts +1 -1
  17. package/lib/exec-docker/remote-docker-cli.js +1 -1
  18. package/lib/exec-docker/remote-docker-cli.js.map +1 -1
  19. package/lib/exec-docker/world.d.ts +1 -1
  20. package/lib/exec-ssh/executor.d.ts +1 -2
  21. package/lib/exec-ssh/executor.js +7 -5
  22. package/lib/exec-ssh/executor.js.map +1 -1
  23. package/lib/exec-ssh/process-provider.d.ts +3 -1
  24. package/lib/exec-ssh/process-provider.js +65 -5
  25. package/lib/exec-ssh/process-provider.js.map +1 -1
  26. package/lib/exec-ssh/world.d.ts +1 -1
  27. package/lib/router/decorate-shell.d.ts +11 -2
  28. package/lib/router/decorate-shell.js +80 -18
  29. package/lib/router/decorate-shell.js.map +1 -1
  30. package/lib/router/index.d.ts +1 -1
  31. package/lib/router/index.js +20 -0
  32. package/lib/router/index.js.map +1 -1
  33. package/lib/router/resolve.d.ts +1 -1
  34. package/lib/router/router-shell.d.ts +2 -3
  35. package/lib/router/router-shell.js +4 -13
  36. package/lib/router/router-shell.js.map +1 -1
  37. package/lib/router/types.d.ts +1 -1
  38. package/lib/{types-C0TtEO7z.d.ts → types-ekJQQ4x6.d.ts} +4 -2
  39. package/lib/{workspace-instruction-context-CxUldQkP.js → workspace-instruction-context-DzHFTN87.js} +156 -76
  40. package/lib/workspace-instruction-context-DzHFTN87.js.map +1 -0
  41. package/package.json +34 -33
  42. package/lib/workspace-instruction-context-CxUldQkP.js.map +0 -1
@@ -1,3 +1,3 @@
1
1
  import "./workspace-instructions.js";
2
- import { a as registerWorkspaceInstructionInjector, i as name, n as escapeInstructionFrameBody, o as renderInstructionMessageText, r as inject, s as resolveSnapshotForSession, t as createInstructionMessage } from "../workspace-instruction-context-CxUldQkP.js";
2
+ import { a as registerWorkspaceInstructionInjector, i as name, n as escapeInstructionFrameBody, o as renderInstructionMessageText, r as inject, s as resolveSnapshotForSession, t as createInstructionMessage } from "../workspace-instruction-context-DzHFTN87.js";
3
3
  export { createInstructionMessage, escapeInstructionFrameBody, inject, name, registerWorkspaceInstructionInjector, renderInstructionMessageText, resolveSnapshotForSession };
@@ -30,7 +30,7 @@ interface DockerBackend {
30
30
  remove(path: string): Promise<void>;
31
31
  rename(src: string, dest: string): Promise<void>;
32
32
  ensureRg(): Promise<void>;
33
- pty(argv: string[], cwd: string, cols: number, rows: number): Promise<{
33
+ pty(argv: string[], cwd: string, cols: number, rows: number, env?: Record<string, string>, terminalType?: string): Promise<{
34
34
  stream: NodeJS.ReadWriteStream;
35
35
  resize: (c: number, r: number) => void;
36
36
  kill: () => void;
@@ -34,7 +34,7 @@ declare class DockerClient implements DockerBackend {
34
34
  mkdir(path: string): Promise<void>;
35
35
  remove(path: string): Promise<void>;
36
36
  rename(src: string, dest: string): Promise<void>;
37
- pty(argv: string[], cwd: string, cols: number, rows: number): Promise<{
37
+ pty(argv: string[], cwd: string, cols: number, rows: number, env?: Record<string, string>, terminalType?: string): Promise<{
38
38
  stream: NodeJS.ReadWriteStream;
39
39
  resize: (c: number, r: number) => void;
40
40
  kill: () => void;
@@ -250,13 +250,16 @@ var DockerClient = class {
250
250
  dest
251
251
  ], "/");
252
252
  }
253
- async pty(argv, cwd, cols, rows) {
253
+ async pty(argv, cwd, cols, rows, env = {}, terminalType = "xterm-256color") {
254
+ const environment = Object.entries(env).map(([key, value]) => `${key}=${value}`);
255
+ if (!environment.some((entry) => entry.startsWith("TERM="))) environment.push(`TERM=${terminalType}`);
254
256
  const createRes = await dockerRequest("POST", `/containers/${this.containerId}/exec`, {
255
257
  AttachStdout: true,
256
258
  AttachStderr: true,
257
259
  AttachStdin: true,
258
260
  Tty: true,
259
261
  WorkingDir: cwd,
262
+ Env: environment,
260
263
  Cmd: argv
261
264
  });
262
265
  if (createRes.statusCode !== 201) throw new Error("docker exec create failed");
@@ -1 +1 @@
1
- {"version":3,"file":"docker-client.js","names":["httpRequest","httpsRequest"],"sources":["../../src/exec-docker/docker-client.ts"],"sourcesContent":["import type { IncomingMessage, ClientRequest } from 'node:http'\nimport { request as httpRequest } from 'node:http'\nimport { request as httpsRequest } from 'node:https'\nimport type { DockerBackend } from './docker-backend.js'\nimport { parseDaemonHost } from './daemon-runner.js'\n\nconst DAEMON = parseDaemonHost()\n\ninterface ExecResult {\n code: number\n stdout: string\n stderr: string\n}\n\ninterface Demuxed {\n stdout: Buffer[]\n stderr: Buffer[]\n}\n\nfunction requestRaw(\n opts: { method: string; path: string; headers: Record<string, string> },\n onResponse: (res: IncomingMessage) => void,\n): ClientRequest {\n const common = { ...opts }\n if (DAEMON.socketPath) return httpRequest({ ...common, socketPath: DAEMON.socketPath } as never, onResponse)\n const mod = DAEMON.tls ? httpsRequest : httpRequest\n return mod({ ...common, hostname: DAEMON.host, port: DAEMON.port } as never, onResponse)\n}\n\nfunction dockerRequest(method: string, path: string, body?: unknown): Promise<{ statusCode: number; headers: Record<string, string>; body: Buffer }> {\n const headers = body ? { 'content-type': 'application/json' } : {}\n if (DAEMON.socketPath) {\n return new Promise((resolve, reject) => {\n const req = httpRequest({ method, path, socketPath: DAEMON.socketPath as string, headers }, (res) => {\n const chunks: Buffer[] = []\n res.on('data', (c: Buffer) => chunks.push(c))\n res.on('end', () => {\n resolve({\n statusCode: res.statusCode ?? 0,\n headers: res.headers as Record<string, string>,\n body: Buffer.concat(chunks),\n })\n })\n res.on('error', reject)\n })\n req.on('error', reject)\n if (body) req.write(JSON.stringify(body))\n req.end()\n })\n }\n const mod = DAEMON.tls ? httpsRequest : httpRequest\n return new Promise((resolve, reject) => {\n const req = mod({ method, path, hostname: DAEMON.host, port: DAEMON.port, headers }, (res) => {\n const chunks: Buffer[] = []\n res.on('data', (c: Buffer) => chunks.push(c))\n res.on('end', () => {\n resolve({\n statusCode: res.statusCode ?? 0,\n headers: res.headers as Record<string, string>,\n body: Buffer.concat(chunks),\n })\n })\n res.on('error', reject)\n })\n req.on('error', reject)\n if (body) req.write(JSON.stringify(body))\n req.end()\n })\n}\n\nfunction demux(data: Buffer): Demuxed {\n const stdout: Buffer[] = []\n const stderr: Buffer[] = []\n let pos = 0\n while (pos + 8 <= data.length) {\n const streamType = data[pos]\n const length = data.readUInt32BE(pos + 4)\n pos += 8\n if (pos + length > data.length) break\n const payload = data.subarray(pos, pos + length)\n pos += length\n if (streamType === 1) stdout.push(payload)\n else if (streamType === 2) stderr.push(payload)\n }\n return { stdout, stderr }\n}\n\nfunction shellQuote(s: string): string {\n return `'${s.replace(/'/g, \"'\\\\''\")}'`\n}\n\nexport class DockerClient implements DockerBackend {\n private readonly containerId: string\n\n constructor(containerId: string) {\n this.containerId = containerId\n }\n\n async ping(): Promise<boolean> {\n try {\n const res = await dockerRequest('GET', '/_ping')\n return res.statusCode === 200\n } catch {\n return false\n }\n }\n\n async inspect(): Promise<{ running: boolean; name: string } | undefined> {\n const res = await dockerRequest('GET', `/containers/${this.containerId}/json`)\n if (res.statusCode !== 200) return undefined\n const data = JSON.parse(res.body.toString('utf8'))\n return { running: data.State?.Running ?? false, name: data.Name ?? this.containerId }\n }\n\n async exec(argv: string[], cwd: string, env: Record<string, string> = {}, stdinData?: Buffer): Promise<ExecResult> {\n const createRes = await dockerRequest('POST', `/containers/${this.containerId}/exec`, {\n AttachStdout: true,\n AttachStderr: true,\n AttachStdin: stdinData !== undefined,\n Tty: false,\n WorkingDir: cwd,\n Env: Object.entries(env).map(([k, v]) => `${k}=${v}`),\n Cmd: argv,\n })\n if (createRes.statusCode !== 201) {\n const msg = createRes.body.toString('utf8')\n return { code: 126, stdout: '', stderr: `docker exec create failed: ${msg}` }\n }\n const execId = JSON.parse(createRes.body.toString('utf8')).Id\n if (!execId) return { code: 126, stdout: '', stderr: 'docker exec: no Id returned' }\n\n return new Promise<ExecResult>((resolve, reject) => {\n const req = requestRaw(\n {\n method: 'POST',\n path: `/exec/${execId}/start`,\n headers: { 'content-type': 'application/json', connection: 'upgrade' },\n },\n (res) => {\n const chunks: Buffer[] = []\n res.on('data', (c: Buffer) => chunks.push(c))\n res.on('end', () => {\n const raw = Buffer.concat(chunks)\n const demuxed = demux(raw)\n const inspectReq = requestRaw(\n { method: 'GET', path: `/exec/${execId}/json`, headers: {} },\n (inspectRes) => {\n const ichunks: Buffer[] = []\n inspectRes.on('data', (c: Buffer) => ichunks.push(c))\n inspectRes.on('end', () => {\n let code = 0\n try {\n const info = JSON.parse(Buffer.concat(ichunks).toString('utf8'))\n code = info.ExitCode ?? 0\n } catch {}\n resolve({\n code,\n stdout: Buffer.concat(demuxed.stdout).toString('utf8'),\n stderr: Buffer.concat(demuxed.stderr).toString('utf8'),\n })\n })\n inspectRes.on('error', () => resolve({ code: 0, stdout: Buffer.concat(demuxed.stdout).toString('utf8'), stderr: Buffer.concat(demuxed.stderr).toString('utf8') }))\n },\n )\n inspectReq.on('error', () => resolve({ code: 0, stdout: Buffer.concat(demuxed.stdout).toString('utf8'), stderr: Buffer.concat(demuxed.stderr).toString('utf8') }))\n inspectReq.end()\n })\n res.on('error', reject)\n },\n )\n req.on('error', reject)\n req.write(JSON.stringify({ Detach: false, Tty: false }))\n if (stdinData) req.write(stdinData)\n req.end()\n })\n }\n\n async execShell(command: string, cwd: string): Promise<ExecResult> {\n return this.exec(['bash', '-c', command], cwd)\n }\n\n async readFile(path: string): Promise<Buffer> {\n const result = await this.exec(['cat', path], '/')\n return Buffer.from(result.stdout, 'utf8')\n }\n\n async writeFile(path: string, content: Buffer | string): Promise<void> {\n const data = Buffer.isBuffer(content) ? content : Buffer.from(content, 'utf8')\n const b64 = data.toString('base64')\n await this.execShell(`printf '%s' '${b64}' | base64 -d > ${shellQuote(path)}`, '/')\n }\n\n async listDir(path: string): Promise<Array<{ name: string; isDir: boolean; isFile: boolean; isSymlink: boolean; size: number; mtime: number }>> {\n const result = await this.execShell(`ls -1pa --time-style=+%s ${shellQuote(path)} 2>/dev/null`, '/')\n return result.stdout\n .split('\\n')\n .filter((line) => line.trim())\n .map((line) => {\n const name = line.replace(/[/@*]$/, '').trim()\n if (!name) return null\n const isDir = line.endsWith('/')\n const isSymlink = line.endsWith('@')\n return {\n name,\n isDir,\n isFile: !isDir && !isSymlink,\n isSymlink,\n size: 0,\n mtime: 0,\n }\n })\n .filter((e): e is NonNullable<typeof e> => e !== null)\n }\n\n async stat(path: string): Promise<{ size: number; mtime: number; isDirectory: boolean; isFile: boolean } | undefined> {\n const result = await this.execShell(`stat -c '%s %Y %F' ${shellQuote(path)} 2>/dev/null`, '/')\n const line = result.stdout.trim()\n if (!line) return undefined\n const parts = line.split(/\\s+/)\n if (parts.length < 3) return undefined\n const size = Number(parts[0])\n const mtime = Number(parts[1]) * 1000\n const ftype = parts.slice(2).join(' ')\n return {\n size,\n mtime,\n isDirectory: ftype.includes('directory'),\n isFile: ftype.includes('regular file') || ftype.includes('regular empty file'),\n }\n }\n\n async mkdir(path: string): Promise<void> {\n await this.exec(['mkdir', '-p', path], '/')\n }\n\n async remove(path: string): Promise<void> {\n await this.exec(['rm', '-f', path], '/')\n }\n\n async rename(src: string, dest: string): Promise<void> {\n await this.exec(['mv', src, dest], '/')\n }\n\n async pty(argv: string[], cwd: string, cols: number, rows: number): Promise<{ stream: NodeJS.ReadWriteStream; resize: (c: number, r: number) => void; kill: () => void }> {\n const createRes = await dockerRequest('POST', `/containers/${this.containerId}/exec`, {\n AttachStdout: true,\n AttachStderr: true,\n AttachStdin: true,\n Tty: true,\n WorkingDir: cwd,\n Cmd: argv,\n })\n if (createRes.statusCode !== 201) throw new Error('docker exec create failed')\n const execId = JSON.parse(createRes.body.toString('utf8')).Id\n\n return new Promise((resolve, reject) => {\n const req = requestRaw(\n {\n method: 'POST',\n path: `/exec/${execId}/start`,\n headers: { 'content-type': 'application/json', connection: 'upgrade' },\n },\n (res) => {\n resolve({\n stream: res as unknown as NodeJS.ReadWriteStream,\n resize: (c: number, r: number) => {\n const r2 = requestRaw({ method: 'POST', path: `/exec/${execId}/resize?h=${r}&w=${c}`, headers: {} }, () => {})\n r2.end()\n },\n kill: () => {\n res.destroy()\n },\n })\n },\n )\n req.on('error', reject)\n req.write(JSON.stringify({ Detach: false, Tty: true }))\n req.end()\n })\n }\n\n async ensureRg(): Promise<void> {\n const result = await this.execShell('command -v rg || echo __missing__', '/')\n if (result.stdout.includes('__missing__')) {\n await this.execShell('apk add --no-cache ripgrep 2>/dev/null || apt-get install -y ripgrep 2>/dev/null || true', '/')\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"docker-client.js","names":["httpRequest","httpsRequest"],"sources":["../../src/exec-docker/docker-client.ts"],"sourcesContent":["import type { IncomingMessage, ClientRequest } from 'node:http'\nimport { request as httpRequest } from 'node:http'\nimport { request as httpsRequest } from 'node:https'\nimport type { DockerBackend } from './docker-backend.js'\nimport { parseDaemonHost } from './daemon-runner.js'\n\nconst DAEMON = parseDaemonHost()\n\ninterface ExecResult {\n code: number\n stdout: string\n stderr: string\n}\n\ninterface Demuxed {\n stdout: Buffer[]\n stderr: Buffer[]\n}\n\nfunction requestRaw(\n opts: { method: string; path: string; headers: Record<string, string> },\n onResponse: (res: IncomingMessage) => void,\n): ClientRequest {\n const common = { ...opts }\n if (DAEMON.socketPath) return httpRequest({ ...common, socketPath: DAEMON.socketPath } as never, onResponse)\n const mod = DAEMON.tls ? httpsRequest : httpRequest\n return mod({ ...common, hostname: DAEMON.host, port: DAEMON.port } as never, onResponse)\n}\n\nfunction dockerRequest(method: string, path: string, body?: unknown): Promise<{ statusCode: number; headers: Record<string, string>; body: Buffer }> {\n const headers = body ? { 'content-type': 'application/json' } : {}\n if (DAEMON.socketPath) {\n return new Promise((resolve, reject) => {\n const req = httpRequest({ method, path, socketPath: DAEMON.socketPath as string, headers }, (res) => {\n const chunks: Buffer[] = []\n res.on('data', (c: Buffer) => chunks.push(c))\n res.on('end', () => {\n resolve({\n statusCode: res.statusCode ?? 0,\n headers: res.headers as Record<string, string>,\n body: Buffer.concat(chunks),\n })\n })\n res.on('error', reject)\n })\n req.on('error', reject)\n if (body) req.write(JSON.stringify(body))\n req.end()\n })\n }\n const mod = DAEMON.tls ? httpsRequest : httpRequest\n return new Promise((resolve, reject) => {\n const req = mod({ method, path, hostname: DAEMON.host, port: DAEMON.port, headers }, (res) => {\n const chunks: Buffer[] = []\n res.on('data', (c: Buffer) => chunks.push(c))\n res.on('end', () => {\n resolve({\n statusCode: res.statusCode ?? 0,\n headers: res.headers as Record<string, string>,\n body: Buffer.concat(chunks),\n })\n })\n res.on('error', reject)\n })\n req.on('error', reject)\n if (body) req.write(JSON.stringify(body))\n req.end()\n })\n}\n\nfunction demux(data: Buffer): Demuxed {\n const stdout: Buffer[] = []\n const stderr: Buffer[] = []\n let pos = 0\n while (pos + 8 <= data.length) {\n const streamType = data[pos]\n const length = data.readUInt32BE(pos + 4)\n pos += 8\n if (pos + length > data.length) break\n const payload = data.subarray(pos, pos + length)\n pos += length\n if (streamType === 1) stdout.push(payload)\n else if (streamType === 2) stderr.push(payload)\n }\n return { stdout, stderr }\n}\n\nfunction shellQuote(s: string): string {\n return `'${s.replace(/'/g, \"'\\\\''\")}'`\n}\n\nexport class DockerClient implements DockerBackend {\n private readonly containerId: string\n\n constructor(containerId: string) {\n this.containerId = containerId\n }\n\n async ping(): Promise<boolean> {\n try {\n const res = await dockerRequest('GET', '/_ping')\n return res.statusCode === 200\n } catch {\n return false\n }\n }\n\n async inspect(): Promise<{ running: boolean; name: string } | undefined> {\n const res = await dockerRequest('GET', `/containers/${this.containerId}/json`)\n if (res.statusCode !== 200) return undefined\n const data = JSON.parse(res.body.toString('utf8'))\n return { running: data.State?.Running ?? false, name: data.Name ?? this.containerId }\n }\n\n async exec(argv: string[], cwd: string, env: Record<string, string> = {}, stdinData?: Buffer): Promise<ExecResult> {\n const createRes = await dockerRequest('POST', `/containers/${this.containerId}/exec`, {\n AttachStdout: true,\n AttachStderr: true,\n AttachStdin: stdinData !== undefined,\n Tty: false,\n WorkingDir: cwd,\n Env: Object.entries(env).map(([k, v]) => `${k}=${v}`),\n Cmd: argv,\n })\n if (createRes.statusCode !== 201) {\n const msg = createRes.body.toString('utf8')\n return { code: 126, stdout: '', stderr: `docker exec create failed: ${msg}` }\n }\n const execId = JSON.parse(createRes.body.toString('utf8')).Id\n if (!execId) return { code: 126, stdout: '', stderr: 'docker exec: no Id returned' }\n\n return new Promise<ExecResult>((resolve, reject) => {\n const req = requestRaw(\n {\n method: 'POST',\n path: `/exec/${execId}/start`,\n headers: { 'content-type': 'application/json', connection: 'upgrade' },\n },\n (res) => {\n const chunks: Buffer[] = []\n res.on('data', (c: Buffer) => chunks.push(c))\n res.on('end', () => {\n const raw = Buffer.concat(chunks)\n const demuxed = demux(raw)\n const inspectReq = requestRaw(\n { method: 'GET', path: `/exec/${execId}/json`, headers: {} },\n (inspectRes) => {\n const ichunks: Buffer[] = []\n inspectRes.on('data', (c: Buffer) => ichunks.push(c))\n inspectRes.on('end', () => {\n let code = 0\n try {\n const info = JSON.parse(Buffer.concat(ichunks).toString('utf8'))\n code = info.ExitCode ?? 0\n } catch {}\n resolve({\n code,\n stdout: Buffer.concat(demuxed.stdout).toString('utf8'),\n stderr: Buffer.concat(demuxed.stderr).toString('utf8'),\n })\n })\n inspectRes.on('error', () => resolve({ code: 0, stdout: Buffer.concat(demuxed.stdout).toString('utf8'), stderr: Buffer.concat(demuxed.stderr).toString('utf8') }))\n },\n )\n inspectReq.on('error', () => resolve({ code: 0, stdout: Buffer.concat(demuxed.stdout).toString('utf8'), stderr: Buffer.concat(demuxed.stderr).toString('utf8') }))\n inspectReq.end()\n })\n res.on('error', reject)\n },\n )\n req.on('error', reject)\n req.write(JSON.stringify({ Detach: false, Tty: false }))\n if (stdinData) req.write(stdinData)\n req.end()\n })\n }\n\n async execShell(command: string, cwd: string): Promise<ExecResult> {\n return this.exec(['bash', '-c', command], cwd)\n }\n\n async readFile(path: string): Promise<Buffer> {\n const result = await this.exec(['cat', path], '/')\n return Buffer.from(result.stdout, 'utf8')\n }\n\n async writeFile(path: string, content: Buffer | string): Promise<void> {\n const data = Buffer.isBuffer(content) ? content : Buffer.from(content, 'utf8')\n const b64 = data.toString('base64')\n await this.execShell(`printf '%s' '${b64}' | base64 -d > ${shellQuote(path)}`, '/')\n }\n\n async listDir(path: string): Promise<Array<{ name: string; isDir: boolean; isFile: boolean; isSymlink: boolean; size: number; mtime: number }>> {\n const result = await this.execShell(`ls -1pa --time-style=+%s ${shellQuote(path)} 2>/dev/null`, '/')\n return result.stdout\n .split('\\n')\n .filter((line) => line.trim())\n .map((line) => {\n const name = line.replace(/[/@*]$/, '').trim()\n if (!name) return null\n const isDir = line.endsWith('/')\n const isSymlink = line.endsWith('@')\n return {\n name,\n isDir,\n isFile: !isDir && !isSymlink,\n isSymlink,\n size: 0,\n mtime: 0,\n }\n })\n .filter((e): e is NonNullable<typeof e> => e !== null)\n }\n\n async stat(path: string): Promise<{ size: number; mtime: number; isDirectory: boolean; isFile: boolean } | undefined> {\n const result = await this.execShell(`stat -c '%s %Y %F' ${shellQuote(path)} 2>/dev/null`, '/')\n const line = result.stdout.trim()\n if (!line) return undefined\n const parts = line.split(/\\s+/)\n if (parts.length < 3) return undefined\n const size = Number(parts[0])\n const mtime = Number(parts[1]) * 1000\n const ftype = parts.slice(2).join(' ')\n return {\n size,\n mtime,\n isDirectory: ftype.includes('directory'),\n isFile: ftype.includes('regular file') || ftype.includes('regular empty file'),\n }\n }\n\n async mkdir(path: string): Promise<void> {\n await this.exec(['mkdir', '-p', path], '/')\n }\n\n async remove(path: string): Promise<void> {\n await this.exec(['rm', '-f', path], '/')\n }\n\n async rename(src: string, dest: string): Promise<void> {\n await this.exec(['mv', src, dest], '/')\n }\n\n async pty(argv: string[], cwd: string, cols: number, rows: number, env: Record<string, string> = {}, terminalType = 'xterm-256color'): Promise<{ stream: NodeJS.ReadWriteStream; resize: (c: number, r: number) => void; kill: () => void }> {\n const environment = Object.entries(env).map(([key, value]) => `${key}=${value}`)\n if (!environment.some((entry) => entry.startsWith('TERM='))) environment.push(`TERM=${terminalType}`)\n const createRes = await dockerRequest('POST', `/containers/${this.containerId}/exec`, {\n AttachStdout: true,\n AttachStderr: true,\n AttachStdin: true,\n Tty: true,\n WorkingDir: cwd,\n Env: environment,\n Cmd: argv,\n })\n if (createRes.statusCode !== 201) throw new Error('docker exec create failed')\n const execId = JSON.parse(createRes.body.toString('utf8')).Id\n\n return new Promise((resolve, reject) => {\n const req = requestRaw(\n {\n method: 'POST',\n path: `/exec/${execId}/start`,\n headers: { 'content-type': 'application/json', connection: 'upgrade' },\n },\n (res) => {\n resolve({\n stream: res as unknown as NodeJS.ReadWriteStream,\n resize: (c: number, r: number) => {\n const r2 = requestRaw({ method: 'POST', path: `/exec/${execId}/resize?h=${r}&w=${c}`, headers: {} }, () => {})\n r2.end()\n },\n kill: () => {\n res.destroy()\n },\n })\n },\n )\n req.on('error', reject)\n req.write(JSON.stringify({ Detach: false, Tty: true }))\n req.end()\n })\n }\n\n async ensureRg(): Promise<void> {\n const result = await this.execShell('command -v rg || echo __missing__', '/')\n if (result.stdout.includes('__missing__')) {\n await this.execShell('apk add --no-cache ripgrep 2>/dev/null || apt-get install -y ripgrep 2>/dev/null || true', '/')\n }\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { DockerBackend } from "./docker-backend.js";
2
- import { SubprocessHandle, SubprocessSpawnSpec, SubprocessTerminalHandle, SubprocessTerminalSpawnSpec } from "@deepseek-ai/dsh-subprocess";
2
+ import { SubprocessHandle, SubprocessSpawnSpec, SubprocessTerminalEnvironment, SubprocessTerminalHandle, SubprocessTerminalSpawnSpec } from "@deepseek-ai/dsh-subprocess";
3
3
  import { ShellExecRequest, ShellExecSpec, ShellProcess, ShellRunResult } from "@deepseek-ai/dsh-shell";
4
4
  //#region src/exec-docker/process-provider.d.ts
5
5
  declare class DockerShellExecutor {
@@ -12,6 +12,8 @@ declare class DockerShellExecutor {
12
12
  declare class DockerSubprocessRuntime {
13
13
  private readonly client;
14
14
  constructor(client: DockerBackend);
15
+ terminalEnvironment(signal?: AbortSignal): Promise<SubprocessTerminalEnvironment>;
16
+ resolveExecutable(command: string, env?: Readonly<Record<string, string>>, signal?: AbortSignal): Promise<string>;
15
17
  spawn(spec: SubprocessSpawnSpec): SubprocessHandle;
16
18
  spawnTerminal(spec: SubprocessTerminalSpawnSpec): Promise<SubprocessTerminalHandle>;
17
19
  }
@@ -1,3 +1,4 @@
1
+ import { SubprocessExecutableNotFoundError } from "@deepseek-ai/dsh-subprocess";
1
2
  //#region src/exec-docker/process-provider.ts
2
3
  var DockerShellExecutor = class {
3
4
  client;
@@ -49,6 +50,34 @@ var DockerSubprocessRuntime = class {
49
50
  constructor(client) {
50
51
  this.client = client;
51
52
  }
53
+ async terminalEnvironment(signal) {
54
+ signal?.throwIfAborted();
55
+ const result = await this.client.exec([
56
+ "sh",
57
+ "-c",
58
+ `user=$(id -un); shell=$(getent passwd "$user" 2>/dev/null | cut -d: -f7); if [ -z "$shell" ]; then shell=$(awk -F: -v user="$user" '$1 == user {print $7; exit}' /etc/passwd); fi; printf "%s" "$shell"`
59
+ ], "/");
60
+ signal?.throwIfAborted();
61
+ const defaultShell = result.stdout.trim().split("\n")[0];
62
+ return {
63
+ platform: "posix",
64
+ ...defaultShell ? { defaultShell } : {}
65
+ };
66
+ }
67
+ async resolveExecutable(command, env = {}, signal) {
68
+ signal?.throwIfAborted();
69
+ const result = await this.client.exec([
70
+ "sh",
71
+ "-c",
72
+ "command -v \"$1\"",
73
+ "sh",
74
+ command
75
+ ], "/", { ...env });
76
+ signal?.throwIfAborted();
77
+ const path = result.stdout.trim().split("\n")[0];
78
+ if (result.code !== 0 || !path) throw new SubprocessExecutableNotFoundError(`executable not found in Docker node: ${command}`);
79
+ return path;
80
+ }
52
81
  spawn(spec) {
53
82
  return {
54
83
  pid: -1,
@@ -60,12 +89,46 @@ var DockerSubprocessRuntime = class {
60
89
  };
61
90
  }
62
91
  async spawnTerminal(spec) {
63
- const pty = await this.client.pty(spec.argv, spec.cwd, spec.cols ?? 80, spec.rows ?? 24);
92
+ if (spec.signal?.aborted) throw spec.signal.reason;
93
+ const pty = await this.client.pty(spec.argv, spec.cwd, spec.cols, spec.rows, spec.env, spec.terminalType);
94
+ if (spec.signal?.aborted) {
95
+ pty.kill();
96
+ throw spec.signal.reason;
97
+ }
98
+ let settled = false;
99
+ const done = new Promise((resolve, reject) => {
100
+ const finish = () => {
101
+ if (settled) return;
102
+ settled = true;
103
+ resolve({
104
+ exitCode: null,
105
+ signal: null
106
+ });
107
+ };
108
+ pty.stream.once("end", finish);
109
+ pty.stream.once("close", finish);
110
+ pty.stream.once("error", reject);
111
+ });
64
112
  return {
65
- write: (data) => pty.stream.write(data),
66
- resize: (cols, rows) => pty.resize(cols, rows),
67
- onData: (cb) => pty.stream.on("data", (chunk) => cb(chunk.toString("utf8"))),
68
- kill: () => pty.kill()
113
+ pid: -1,
114
+ output: pty.stream,
115
+ done,
116
+ write: (data) => new Promise((resolve, reject) => {
117
+ pty.stream.write(data, (error) => error ? reject(error) : resolve());
118
+ }),
119
+ resize: async (cols, rows) => pty.resize(cols, rows),
120
+ inspectForeground: async () => void 0,
121
+ inspectActivity: async () => ({
122
+ state: "unknown",
123
+ revision: 0
124
+ }),
125
+ signalForeground: async () => {
126
+ throw new Error("foreground signaling is unsupported for Docker terminals");
127
+ },
128
+ terminate: async () => {
129
+ pty.kill();
130
+ await done.catch(() => {});
131
+ }
69
132
  };
70
133
  }
71
134
  };
@@ -1 +1 @@
1
- {"version":3,"file":"process-provider.js","names":[],"sources":["../../src/exec-docker/process-provider.ts"],"sourcesContent":["import type { DockerBackend } from './docker-backend.js'\nimport type { ShellExecRequest, ShellExecSpec, ShellProcess, ShellRunResult } from '@deepseek-ai/dsh-shell'\nimport type { CollectedOutput, SubprocessHandle, SubprocessSpawnSpec, SubprocessTerminalHandle, SubprocessTerminalSpawnSpec } from '@deepseek-ai/dsh-subprocess'\n\nexport class DockerShellExecutor {\n constructor(private readonly client: DockerBackend) {}\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.client.exec(['bash', '-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: 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 return {\n pid: -1,\n done: promise,\n kill: () => {\n void promise\n },\n } as unknown as ShellProcess\n }\n}\n\nexport class DockerSubprocessRuntime {\n constructor(private readonly client: DockerBackend) {}\n\n spawn(spec: SubprocessSpawnSpec): SubprocessHandle {\n const promise = this.client.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: 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.client.pty(spec.argv as string[], spec.cwd, (spec as { cols?: number }).cols ?? 80, (spec as { rows?: number }).rows ?? 24)\n const handle: SubprocessTerminalHandle = {\n write: (data: string) => pty.stream.write(data),\n resize: (cols: number, rows: number) => pty.resize(cols, rows),\n onData: (cb: (data: string) => void) => pty.stream.on('data', (chunk: Buffer) => cb(chunk.toString('utf8'))),\n kill: () => pty.kill(),\n } as unknown as SubprocessTerminalHandle\n return handle\n }\n}\n"],"mappings":";AAIA,IAAa,sBAAb,MAAiC;CACF;CAA7B,YAAY,QAAwC;EAAvB,KAAA,SAAA;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,OAAO,KAAK;GAAC;GAAQ;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,QAAQ;GACR,UAAU;GACV,SAAS;GACT,WAAW,KAAK;GAChB,QAAQ,QAAQ,OAAO,MAAM;GAC7B,QAAQ,QAAQ,OAAO,MAAM;EAC/B;CACF;CAEA,MAAM,MAAmC;EAEvC,OAAO;GACL,KAAK;GACL,MAHc,KAAK,IAAI,IAGX;GACZ,YAAY,CAEZ;EACF;CACF;AACF;AAEA,IAAa,0BAAb,MAAqC;CACN;CAA7B,YAAY,QAAwC;EAAvB,KAAA,SAAA;CAAwB;CAErD,MAAM,MAA6C;EASjD,OAAO;GANL,KAAK;GACL,MAHc,KAAK,OAAO,KAAK,KAAK,MAAkB,KAAK,KAAM,KAAK,OAAO,CAAC,CAGlE,CAAC,CAAC,MAAM,OAAO;IAAE,UAAU,EAAE;IAAM,QAAQ;GAA8B,EAAE;GACvF,YAAY,CAEZ;EAEU;CACd;CAEA,MAAM,cAAc,MAAsE;EACxF,MAAM,MAAM,MAAM,KAAK,OAAO,IAAI,KAAK,MAAkB,KAAK,KAAM,KAA2B,QAAQ,IAAK,KAA2B,QAAQ,EAAE;EAOjJ,OAAO;GALL,QAAQ,SAAiB,IAAI,OAAO,MAAM,IAAI;GAC9C,SAAS,MAAc,SAAiB,IAAI,OAAO,MAAM,IAAI;GAC7D,SAAS,OAA+B,IAAI,OAAO,GAAG,SAAS,UAAkB,GAAG,MAAM,SAAS,MAAM,CAAC,CAAC;GAC3G,YAAY,IAAI,KAAK;EAEX;CACd;AACF"}
1
+ {"version":3,"file":"process-provider.js","names":[],"sources":["../../src/exec-docker/process-provider.ts"],"sourcesContent":["import type { DockerBackend } from './docker-backend.js'\nimport type { ShellExecRequest, ShellExecSpec, ShellProcess, ShellRunResult } from '@deepseek-ai/dsh-shell'\nimport { SubprocessExecutableNotFoundError, type CollectedOutput, type SubprocessHandle, type SubprocessOutcome, type SubprocessSpawnSpec, type SubprocessTerminalEnvironment, type SubprocessTerminalHandle, type SubprocessTerminalSpawnSpec } from '@deepseek-ai/dsh-subprocess'\n\nexport class DockerShellExecutor {\n constructor(private readonly client: DockerBackend) {}\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.client.exec(['bash', '-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: 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 return {\n pid: -1,\n done: promise,\n kill: () => {\n void promise\n },\n } as unknown as ShellProcess\n }\n}\n\nexport class DockerSubprocessRuntime {\n constructor(private readonly client: DockerBackend) {}\n\n async terminalEnvironment(signal?: AbortSignal): Promise<SubprocessTerminalEnvironment> {\n signal?.throwIfAborted()\n const script = `user=$(id -un); shell=$(getent passwd \"$user\" 2>/dev/null | cut -d: -f7); if [ -z \"$shell\" ]; then shell=$(awk -F: -v user=\"$user\" '$1 == user {print $7; exit}' /etc/passwd); fi; printf \"%s\" \"$shell\"`\n const result = await this.client.exec(['sh', '-c', script], '/')\n signal?.throwIfAborted()\n const defaultShell = result.stdout.trim().split('\\n')[0]\n return { platform: 'posix', ...(defaultShell ? { defaultShell } : {}) }\n }\n\n async resolveExecutable(command: string, env: Readonly<Record<string, string>> = {}, signal?: AbortSignal): Promise<string> {\n signal?.throwIfAborted()\n const result = await this.client.exec(['sh', '-c', 'command -v \"$1\"', 'sh', command], '/', { ...env })\n signal?.throwIfAborted()\n const path = result.stdout.trim().split('\\n')[0]\n if (result.code !== 0 || !path) throw new SubprocessExecutableNotFoundError(`executable not found in Docker node: ${command}`)\n return path\n }\n\n spawn(spec: SubprocessSpawnSpec): SubprocessHandle {\n const promise = this.client.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: 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 if (spec.signal?.aborted) throw spec.signal.reason\n const pty = await this.client.pty(spec.argv as string[], spec.cwd, spec.cols, spec.rows, spec.env, spec.terminalType)\n if (spec.signal?.aborted) {\n pty.kill()\n throw spec.signal.reason\n }\n let settled = false\n const done = new Promise<SubprocessOutcome>((resolve, reject) => {\n const finish = (): void => {\n if (settled) return\n settled = true\n resolve({ exitCode: null, signal: null })\n }\n pty.stream.once('end', finish)\n pty.stream.once('close', finish)\n pty.stream.once('error', reject)\n })\n const handle: SubprocessTerminalHandle = {\n pid: -1,\n output: pty.stream as unknown as import('node:stream').Readable,\n done,\n write: (data) => new Promise<void>((resolve, reject) => {\n pty.stream.write(data, (error) => error ? reject(error) : resolve())\n }),\n resize: async (cols, rows) => pty.resize(cols, rows),\n inspectForeground: async () => undefined,\n inspectActivity: async () => ({ state: 'unknown', revision: 0 }),\n signalForeground: async () => { throw new Error('foreground signaling is unsupported for Docker terminals') },\n terminate: async () => {\n pty.kill()\n await done.catch(() => {})\n },\n }\n return handle\n }\n}\n"],"mappings":"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"}
@@ -41,7 +41,7 @@ declare class RemoteDockerCli implements DockerBackend {
41
41
  remove(path: string): Promise<void>;
42
42
  rename(src: string, dest: string): Promise<void>;
43
43
  ensureRg(): Promise<void>;
44
- pty(argv: string[], cwd: string, cols: number, rows: number): Promise<{
44
+ pty(_argv: string[], _cwd: string, _cols: number, _rows: number, _env?: Record<string, string>, _terminalType?: string): Promise<{
45
45
  stream: NodeJS.ReadWriteStream;
46
46
  resize: (c: number, r: number) => void;
47
47
  kill: () => void;
@@ -145,7 +145,7 @@ var RemoteDockerCli = class {
145
145
  "apk add --no-cache ripgrep 2>/dev/null || apt-get install -y ripgrep 2>/dev/null || true"
146
146
  ], "/");
147
147
  }
148
- async pty(argv, cwd, cols, rows) {
148
+ async pty(_argv, _cwd, _cols, _rows, _env, _terminalType) {
149
149
  throw new Error("remote docker PTY not yet supported");
150
150
  }
151
151
  };
@@ -1 +1 @@
1
- {"version":3,"file":"remote-docker-cli.js","names":[],"sources":["../../src/exec-docker/remote-docker-cli.ts"],"sourcesContent":["import type { DockerBackend } from './docker-backend.js'\n\ninterface SshExecutorLike {\n exec(argv: string[], cwd: string, env?: Record<string, string>, stdinData?: Buffer): Promise<{ code: number; stdout: string; stderr: string }>\n}\n\nexport class RemoteDockerCli implements DockerBackend {\n constructor(\n private readonly ssh: SshExecutorLike,\n private readonly containerId: string,\n ) {}\n\n async exec(argv: string[], cwd: string, env: Record<string, string> = {}, stdinData?: Buffer): Promise<{ code: number; stdout: string; stderr: string }> {\n const envFlags = Object.entries(env).map(([k, v]) => ['-e', `${k}=${v}`]).flat()\n return this.ssh.exec(['docker', 'exec', '-w', cwd, ...envFlags, this.containerId, ...argv], '/', {}, stdinData)\n }\n\n async execShell(command: string, cwd: string): Promise<{ code: number; stdout: string; stderr: string }> {\n return this.ssh.exec(['docker', 'exec', '-w', cwd, this.containerId, 'bash', '-c', command], '/')\n }\n\n async readFile(path: string): Promise<Buffer> {\n const result = await this.ssh.exec(['docker', 'exec', this.containerId, 'cat', path], '/')\n return Buffer.from(result.stdout, 'utf8')\n }\n\n async writeFile(path: string, content: Buffer | string): Promise<void> {\n const data = Buffer.isBuffer(content) ? content : Buffer.from(content, 'utf8')\n const b64 = data.toString('base64')\n await this.ssh.exec(['docker', 'exec', this.containerId, 'bash', '-c', `printf '%s' '${b64}' | base64 -d > '${path.replace(/'/g, \"'\\\\''\")}'`], '/')\n }\n\n async listDir(path: string): Promise<Array<{ name: string; isDir: boolean; isFile: boolean; isSymlink: boolean; size: number; mtime: number }>> {\n const result = await this.ssh.exec(['docker', 'exec', this.containerId, 'ls', '-1pa', '--time-style=+%s', path], '/')\n return result.stdout\n .split('\\n')\n .filter((line) => line.trim())\n .map((line) => {\n const name = line.replace(/[/@*]$/, '').trim()\n if (!name) return null\n const isDir = line.endsWith('/')\n const isSymlink = line.endsWith('@')\n return { name, isDir, isFile: !isDir && !isSymlink, isSymlink, size: 0, mtime: 0 }\n })\n .filter((e): e is NonNullable<typeof e> => e !== null)\n }\n\n async stat(path: string): Promise<{ size: number; mtime: number; isDirectory: boolean; isFile: boolean } | undefined> {\n const result = await this.ssh.exec(['docker', 'exec', this.containerId, 'stat', '-c', '%s %Y %F', path], '/')\n const line = result.stdout.trim()\n if (!line) return undefined\n const parts = line.split(/\\s+/)\n if (parts.length < 3) return undefined\n const size = Number(parts[0])\n const mtime = Number(parts[1]) * 1000\n const ftype = parts.slice(2).join(' ')\n return { size, mtime, isDirectory: ftype.includes('directory'), isFile: ftype.includes('regular') }\n }\n\n async mkdir(path: string): Promise<void> {\n await this.ssh.exec(['docker', 'exec', this.containerId, 'mkdir', '-p', path], '/')\n }\n\n async remove(path: string): Promise<void> {\n await this.ssh.exec(['docker', 'exec', this.containerId, 'rm', '-f', path], '/')\n }\n\n async rename(src: string, dest: string): Promise<void> {\n await this.ssh.exec(['docker', 'exec', this.containerId, 'mv', src, dest], '/')\n }\n\n async ensureRg(): Promise<void> {\n const result = await this.ssh.exec(['docker', 'exec', this.containerId, 'bash', '-c', 'command -v rg || echo __missing__'], '/')\n if (result.stdout.includes('__missing__')) {\n await this.ssh.exec(['docker', 'exec', this.containerId, 'bash', '-c', 'apk add --no-cache ripgrep 2>/dev/null || apt-get install -y ripgrep 2>/dev/null || true'], '/')\n }\n }\n\n async pty(argv: string[], cwd: string, cols: number, rows: number): Promise<{ stream: NodeJS.ReadWriteStream; resize: (c: number, r: number) => void; kill: () => void }> {\n throw new Error('remote docker PTY not yet supported')\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"remote-docker-cli.js","names":[],"sources":["../../src/exec-docker/remote-docker-cli.ts"],"sourcesContent":["import type { DockerBackend } from './docker-backend.js'\n\ninterface SshExecutorLike {\n exec(argv: string[], cwd: string, env?: Record<string, string>, stdinData?: Buffer): Promise<{ code: number; stdout: string; stderr: string }>\n}\n\nexport class RemoteDockerCli implements DockerBackend {\n constructor(\n private readonly ssh: SshExecutorLike,\n private readonly containerId: string,\n ) {}\n\n async exec(argv: string[], cwd: string, env: Record<string, string> = {}, stdinData?: Buffer): Promise<{ code: number; stdout: string; stderr: string }> {\n const envFlags = Object.entries(env).map(([k, v]) => ['-e', `${k}=${v}`]).flat()\n return this.ssh.exec(['docker', 'exec', '-w', cwd, ...envFlags, this.containerId, ...argv], '/', {}, stdinData)\n }\n\n async execShell(command: string, cwd: string): Promise<{ code: number; stdout: string; stderr: string }> {\n return this.ssh.exec(['docker', 'exec', '-w', cwd, this.containerId, 'bash', '-c', command], '/')\n }\n\n async readFile(path: string): Promise<Buffer> {\n const result = await this.ssh.exec(['docker', 'exec', this.containerId, 'cat', path], '/')\n return Buffer.from(result.stdout, 'utf8')\n }\n\n async writeFile(path: string, content: Buffer | string): Promise<void> {\n const data = Buffer.isBuffer(content) ? content : Buffer.from(content, 'utf8')\n const b64 = data.toString('base64')\n await this.ssh.exec(['docker', 'exec', this.containerId, 'bash', '-c', `printf '%s' '${b64}' | base64 -d > '${path.replace(/'/g, \"'\\\\''\")}'`], '/')\n }\n\n async listDir(path: string): Promise<Array<{ name: string; isDir: boolean; isFile: boolean; isSymlink: boolean; size: number; mtime: number }>> {\n const result = await this.ssh.exec(['docker', 'exec', this.containerId, 'ls', '-1pa', '--time-style=+%s', path], '/')\n return result.stdout\n .split('\\n')\n .filter((line) => line.trim())\n .map((line) => {\n const name = line.replace(/[/@*]$/, '').trim()\n if (!name) return null\n const isDir = line.endsWith('/')\n const isSymlink = line.endsWith('@')\n return { name, isDir, isFile: !isDir && !isSymlink, isSymlink, size: 0, mtime: 0 }\n })\n .filter((e): e is NonNullable<typeof e> => e !== null)\n }\n\n async stat(path: string): Promise<{ size: number; mtime: number; isDirectory: boolean; isFile: boolean } | undefined> {\n const result = await this.ssh.exec(['docker', 'exec', this.containerId, 'stat', '-c', '%s %Y %F', path], '/')\n const line = result.stdout.trim()\n if (!line) return undefined\n const parts = line.split(/\\s+/)\n if (parts.length < 3) return undefined\n const size = Number(parts[0])\n const mtime = Number(parts[1]) * 1000\n const ftype = parts.slice(2).join(' ')\n return { size, mtime, isDirectory: ftype.includes('directory'), isFile: ftype.includes('regular') }\n }\n\n async mkdir(path: string): Promise<void> {\n await this.ssh.exec(['docker', 'exec', this.containerId, 'mkdir', '-p', path], '/')\n }\n\n async remove(path: string): Promise<void> {\n await this.ssh.exec(['docker', 'exec', this.containerId, 'rm', '-f', path], '/')\n }\n\n async rename(src: string, dest: string): Promise<void> {\n await this.ssh.exec(['docker', 'exec', this.containerId, 'mv', src, dest], '/')\n }\n\n async ensureRg(): Promise<void> {\n const result = await this.ssh.exec(['docker', 'exec', this.containerId, 'bash', '-c', 'command -v rg || echo __missing__'], '/')\n if (result.stdout.includes('__missing__')) {\n await this.ssh.exec(['docker', 'exec', this.containerId, 'bash', '-c', 'apk add --no-cache ripgrep 2>/dev/null || apt-get install -y ripgrep 2>/dev/null || true'], '/')\n }\n }\n\n async pty(_argv: string[], _cwd: string, _cols: number, _rows: number, _env?: Record<string, string>, _terminalType?: string): Promise<{ stream: NodeJS.ReadWriteStream; resize: (c: number, r: number) => void; kill: () => void }> {\n throw new Error('remote docker PTY not yet supported')\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { DockerBackend } from "./docker-backend.js";
2
2
  import { DockerFileSystem } from "./fs-provider.js";
3
- import { a as NodeTestReport, t as ExecutionWorldProvider } from "../types-C0TtEO7z.js";
3
+ import { a as NodeTestReport, t as ExecutionWorldProvider } from "../types-ekJQQ4x6.js";
4
4
  import { DockerShellExecutor, DockerSubprocessRuntime } from "./process-provider.js";
5
5
  //#region src/exec-docker/world.d.ts
6
6
  interface DockerNodeConfig {
@@ -34,9 +34,8 @@ declare class SshExecutor {
34
34
  remove(path: string): Promise<void>;
35
35
  rename(src: string, dest: string): Promise<void>;
36
36
  ensureRg(): Promise<void>;
37
- pty(argv: string[], cwd: string, _env: Record<string, string>, cols: number, rows: number): Promise<{
37
+ pty(argv: string[], cwd: string, env: Record<string, string>, cols: number, rows: number, terminalType?: string): Promise<{
38
38
  channel: import('ssh2').ClientChannel;
39
- onData: (cb: (chunk: Buffer) => void) => void;
40
39
  resize: (cols: number, rows: number) => void;
41
40
  kill: () => void;
42
41
  }>;
@@ -171,16 +171,19 @@ var SshExecutor = class {
171
171
  ], "/");
172
172
  }
173
173
  }
174
- async pty(argv, cwd, _env, cols, rows) {
174
+ async pty(argv, cwd, env, cols, rows, terminalType = "xterm-256color") {
175
175
  const client = await this.connection.getClient();
176
176
  const q = (s) => `'${s.replace(/'/g, "'\\''")}'`;
177
- const cmdParts = argv.map(q);
178
- const command = `bash -c 'cd "$0" && exec env -i HOME="$HOME" PATH="\${PATH:-/usr/local/bin:/usr/bin:/bin}" "$@"' ${q(cwd)} ${cmdParts.join(" ")}`;
177
+ const environment = Object.entries(env).map(([key, value]) => {
178
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) throw new Error(`invalid terminal environment key: ${key}`);
179
+ return `${key}=${value}`;
180
+ });
181
+ const command = `bash -c 'cd "$0" && exec env -i HOME="$HOME" PATH="\${PATH:-/usr/local/bin:/usr/bin:/bin}" "$@"' ${q(cwd)} ${[...environment, ...argv].map(q).join(" ")}`;
179
182
  return new Promise((resolve, reject) => {
180
183
  client.exec(command, { pty: {
181
184
  cols,
182
185
  rows,
183
- term: "xterm-256color"
186
+ term: terminalType
184
187
  } }, (err, channel) => {
185
188
  if (err) {
186
189
  reject(err);
@@ -188,7 +191,6 @@ var SshExecutor = class {
188
191
  }
189
192
  resolve({
190
193
  channel,
191
- onData: (cb) => channel.on("data", cb),
192
194
  resize: (c, r) => channel.setWindow(c, r, 0, 0),
193
195
  kill: () => channel.close()
194
196
  });
@@ -1 +1 @@
1
- {"version":3,"file":"executor.js","names":[],"sources":["../../src/exec-ssh/executor.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { encodeRunnerPayload, REMOTE_RUNNER_COMMAND, type RunnerPayload } from './runner.js'\nimport type { SshConnection } from './connection.js'\n\nexport interface ExecResult {\n code: number\n signal?: string\n stdout: string\n stderr: string\n}\n\nexport class SshExecutor {\n private sftpCache: Promise<import('ssh2').SFTPWrapper> | null = null\n\n constructor(\n private readonly connection: SshConnection,\n private readonly nodeId = 'unknown',\n private readonly audit?: (op: string, target: string, result: string) => void,\n ) {}\n\n async exec(argv: string[], cwd: string, env: Record<string, string> = {}, stdinData?: Buffer): Promise<ExecResult> {\n const client = await this.connection.getClient()\n const payload: RunnerPayload = { argv, cwd, env }\n const body = encodeRunnerPayload(payload)\n const input = stdinData ? Buffer.concat([body, stdinData]) : body\n return new Promise((resolve, reject) => {\n client.exec(REMOTE_RUNNER_COMMAND, (err, channel) => {\n if (err) {\n reject(err)\n return\n }\n const stdout: Buffer[] = []\n const stderr: Buffer[] = []\n channel.stdout.on('data', (chunk: Buffer) => stdout.push(chunk))\n channel.stderr.on('data', (chunk: Buffer) => stderr.push(chunk))\n channel.on('close', (code: number, signal: string) => {\n this.audit?.('exec', argv.join(' '), `code=${code ?? 0}`)\n resolve({\n code: code ?? 0,\n signal,\n stdout: Buffer.concat(stdout).toString('utf8'),\n stderr: Buffer.concat(stderr).toString('utf8'),\n })\n })\n channel.on('error', reject)\n channel.end(input)\n })\n })\n }\n\n async sftp(): Promise<import('ssh2').SFTPWrapper> {\n if (this.sftpCache) return this.sftpCache\n const client = await this.connection.getClient()\n this.sftpCache = new Promise((resolve, reject) => {\n client.sftp((err, sftp) => {\n if (err) reject(err)\n else resolve(sftp)\n })\n })\n return this.sftpCache\n }\n\n async readFile(path: string): Promise<Buffer> {\n const sftp = await this.sftp()\n return new Promise((resolve, reject) => {\n sftp.readFile(path, (err, data) => (err ? reject(err) : resolve(data)))\n })\n }\n\n async writeFile(path: string, content: Buffer | string): Promise<void> {\n const sftp = await this.sftp()\n const tmp = `${path}.dshb-tmp-${process.pid}-${Date.now()}`\n return new Promise((resolve, reject) => {\n sftp.writeFile(tmp, content, (writeErr) => {\n if (writeErr) {\n reject(writeErr)\n return\n }\n sftp.ext_openssh_rename(tmp, path, (renameErr) => {\n if (renameErr) reject(renameErr)\n else {\n this.audit?.('write', path, 'ok')\n resolve()\n }\n })\n })\n })\n }\n\n async listDir(path: string): Promise<Array<{ name: string; isDir: boolean; isFile: boolean; isSymlink: boolean; size: number; mtime: number }>> {\n const sftp = await this.sftp()\n return new Promise((resolve, reject) => {\n sftp.readdir(path, (err, list) => {\n if (err) {\n reject(err)\n return\n }\n resolve(\n list.map((e) => ({\n name: e.filename,\n isDir: e.attrs.isDirectory(),\n isFile: e.attrs.isFile(),\n isSymlink: e.attrs.isSymbolicLink(),\n size: Number(e.attrs.size),\n mtime: e.attrs.mtime * 1000,\n })),\n )\n })\n })\n }\n\n async stat(path: string): Promise<{ size: number; mtime: number; isDirectory: boolean; isFile: boolean } | undefined> {\n const sftp = await this.sftp()\n return new Promise((resolve) => {\n sftp.stat(path, (err, stats) => {\n if (err) {\n resolve(undefined)\n return\n }\n resolve({ size: Number(stats.size), mtime: stats.mtime * 1000, isDirectory: stats.isDirectory(), isFile: stats.isFile() })\n })\n })\n }\n\n async mkdir(path: string): Promise<void> {\n const sftp = await this.sftp()\n return new Promise((resolve, reject) => {\n sftp.mkdir(path, (err) => (err ? reject(err) : resolve()))\n })\n }\n\n async remove(path: string): Promise<void> {\n const sftp = await this.sftp()\n return new Promise((resolve, reject) => {\n sftp.unlink(path, (err) => {\n if (err) reject(err)\n else {\n this.audit?.('remove', path, 'ok')\n resolve()\n }\n })\n })\n }\n\n async rename(src: string, dest: string): Promise<void> {\n const sftp = await this.sftp()\n return new Promise((resolve, reject) => {\n sftp.ext_openssh_rename(src, dest, (err) => {\n if (err) reject(err)\n else {\n this.audit?.('move', `${src} -> ${dest}`, 'ok')\n resolve()\n }\n })\n })\n }\n\n async ensureRg(): Promise<void> {\n const result = await this.exec(['sh', '-c', 'command -v rg || echo \"__DShB_RG_MISSING__\"'], '/')\n if (result.stdout.includes('__DShB_RG_MISSING__')) {\n const localRg = process.env.DSHB_LOCAL_RG ?? findLocalRg()\n if (!localRg) return\n const remoteRgDir = '~/.dshb/bin'\n await this.exec(['mkdir', '-p', remoteRgDir], '/')\n const remoteRg = `${remoteRgDir}/rg`\n const content = readFileSync(localRg)\n await this.writeFile(remoteRg, content)\n await this.exec(['chmod', '+x', remoteRg], '/')\n }\n }\n\n async pty(argv: string[], cwd: string, _env: Record<string, string>, cols: number, rows: number): Promise<{ channel: import('ssh2').ClientChannel; onData: (cb: (chunk: Buffer) => void) => void; resize: (cols: number, rows: number) => void; kill: () => void }> {\n const client = await this.connection.getClient()\n const q = (s: string) => `'${s.replace(/'/g, \"'\\\\''\")}'`\n const cmdParts = argv.map(q)\n const command = `bash -c 'cd \"$0\" && exec env -i HOME=\"$HOME\" PATH=\"\\${PATH:-/usr/local/bin:/usr/bin:/bin}\" \"$@\"' ${q(cwd)} ${cmdParts.join(' ')}`\n return new Promise((resolve, reject) => {\n client.exec(\n command,\n { pty: { cols, rows, term: 'xterm-256color' } },\n (err, channel) => {\n if (err) {\n reject(err)\n return\n }\n resolve({\n channel,\n onData: (cb) => channel.on('data', cb),\n resize: (c, r) => channel.setWindow(c, r, 0, 0),\n kill: () => channel.close(),\n })\n },\n )\n })\n }\n}\n\nfunction findLocalRg(): string | undefined {\n const candidates = [\n join(homedir(), '.dsh', 'node_modules', '.bin', 'rg'),\n join(homedir(), '.dsh', 'node_modules', '@vscode', 'ripgrep', 'bin', 'rg'),\n join(homedir(), 'go', 'bin', 'rg'),\n '/usr/local/bin/rg',\n '/usr/bin/rg',\n ]\n for (const c of candidates) if (existsSync(c)) return c\n return undefined\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"executor.js","names":[],"sources":["../../src/exec-ssh/executor.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { encodeRunnerPayload, REMOTE_RUNNER_COMMAND, type RunnerPayload } from './runner.js'\nimport type { SshConnection } from './connection.js'\n\nexport interface ExecResult {\n code: number\n signal?: string\n stdout: string\n stderr: string\n}\n\nexport class SshExecutor {\n private sftpCache: Promise<import('ssh2').SFTPWrapper> | null = null\n\n constructor(\n private readonly connection: SshConnection,\n private readonly nodeId = 'unknown',\n private readonly audit?: (op: string, target: string, result: string) => void,\n ) {}\n\n async exec(argv: string[], cwd: string, env: Record<string, string> = {}, stdinData?: Buffer): Promise<ExecResult> {\n const client = await this.connection.getClient()\n const payload: RunnerPayload = { argv, cwd, env }\n const body = encodeRunnerPayload(payload)\n const input = stdinData ? Buffer.concat([body, stdinData]) : body\n return new Promise((resolve, reject) => {\n client.exec(REMOTE_RUNNER_COMMAND, (err, channel) => {\n if (err) {\n reject(err)\n return\n }\n const stdout: Buffer[] = []\n const stderr: Buffer[] = []\n channel.stdout.on('data', (chunk: Buffer) => stdout.push(chunk))\n channel.stderr.on('data', (chunk: Buffer) => stderr.push(chunk))\n channel.on('close', (code: number, signal: string) => {\n this.audit?.('exec', argv.join(' '), `code=${code ?? 0}`)\n resolve({\n code: code ?? 0,\n signal,\n stdout: Buffer.concat(stdout).toString('utf8'),\n stderr: Buffer.concat(stderr).toString('utf8'),\n })\n })\n channel.on('error', reject)\n channel.end(input)\n })\n })\n }\n\n async sftp(): Promise<import('ssh2').SFTPWrapper> {\n if (this.sftpCache) return this.sftpCache\n const client = await this.connection.getClient()\n this.sftpCache = new Promise((resolve, reject) => {\n client.sftp((err, sftp) => {\n if (err) reject(err)\n else resolve(sftp)\n })\n })\n return this.sftpCache\n }\n\n async readFile(path: string): Promise<Buffer> {\n const sftp = await this.sftp()\n return new Promise((resolve, reject) => {\n sftp.readFile(path, (err, data) => (err ? reject(err) : resolve(data)))\n })\n }\n\n async writeFile(path: string, content: Buffer | string): Promise<void> {\n const sftp = await this.sftp()\n const tmp = `${path}.dshb-tmp-${process.pid}-${Date.now()}`\n return new Promise((resolve, reject) => {\n sftp.writeFile(tmp, content, (writeErr) => {\n if (writeErr) {\n reject(writeErr)\n return\n }\n sftp.ext_openssh_rename(tmp, path, (renameErr) => {\n if (renameErr) reject(renameErr)\n else {\n this.audit?.('write', path, 'ok')\n resolve()\n }\n })\n })\n })\n }\n\n async listDir(path: string): Promise<Array<{ name: string; isDir: boolean; isFile: boolean; isSymlink: boolean; size: number; mtime: number }>> {\n const sftp = await this.sftp()\n return new Promise((resolve, reject) => {\n sftp.readdir(path, (err, list) => {\n if (err) {\n reject(err)\n return\n }\n resolve(\n list.map((e) => ({\n name: e.filename,\n isDir: e.attrs.isDirectory(),\n isFile: e.attrs.isFile(),\n isSymlink: e.attrs.isSymbolicLink(),\n size: Number(e.attrs.size),\n mtime: e.attrs.mtime * 1000,\n })),\n )\n })\n })\n }\n\n async stat(path: string): Promise<{ size: number; mtime: number; isDirectory: boolean; isFile: boolean } | undefined> {\n const sftp = await this.sftp()\n return new Promise((resolve) => {\n sftp.stat(path, (err, stats) => {\n if (err) {\n resolve(undefined)\n return\n }\n resolve({ size: Number(stats.size), mtime: stats.mtime * 1000, isDirectory: stats.isDirectory(), isFile: stats.isFile() })\n })\n })\n }\n\n async mkdir(path: string): Promise<void> {\n const sftp = await this.sftp()\n return new Promise((resolve, reject) => {\n sftp.mkdir(path, (err) => (err ? reject(err) : resolve()))\n })\n }\n\n async remove(path: string): Promise<void> {\n const sftp = await this.sftp()\n return new Promise((resolve, reject) => {\n sftp.unlink(path, (err) => {\n if (err) reject(err)\n else {\n this.audit?.('remove', path, 'ok')\n resolve()\n }\n })\n })\n }\n\n async rename(src: string, dest: string): Promise<void> {\n const sftp = await this.sftp()\n return new Promise((resolve, reject) => {\n sftp.ext_openssh_rename(src, dest, (err) => {\n if (err) reject(err)\n else {\n this.audit?.('move', `${src} -> ${dest}`, 'ok')\n resolve()\n }\n })\n })\n }\n\n async ensureRg(): Promise<void> {\n const result = await this.exec(['sh', '-c', 'command -v rg || echo \"__DShB_RG_MISSING__\"'], '/')\n if (result.stdout.includes('__DShB_RG_MISSING__')) {\n const localRg = process.env.DSHB_LOCAL_RG ?? findLocalRg()\n if (!localRg) return\n const remoteRgDir = '~/.dshb/bin'\n await this.exec(['mkdir', '-p', remoteRgDir], '/')\n const remoteRg = `${remoteRgDir}/rg`\n const content = readFileSync(localRg)\n await this.writeFile(remoteRg, content)\n await this.exec(['chmod', '+x', remoteRg], '/')\n }\n }\n\n async pty(argv: string[], cwd: string, env: Record<string, string>, cols: number, rows: number, terminalType = 'xterm-256color'): Promise<{ channel: import('ssh2').ClientChannel; resize: (cols: number, rows: number) => void; kill: () => void }> {\n const client = await this.connection.getClient()\n const q = (s: string) => `'${s.replace(/'/g, \"'\\\\''\")}'`\n const environment = Object.entries(env).map(([key, value]) => {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) throw new Error(`invalid terminal environment key: ${key}`)\n return `${key}=${value}`\n })\n const command = `bash -c 'cd \"$0\" && exec env -i HOME=\"$HOME\" PATH=\"\\${PATH:-/usr/local/bin:/usr/bin:/bin}\" \"$@\"' ${q(cwd)} ${[...environment, ...argv].map(q).join(' ')}`\n return new Promise((resolve, reject) => {\n client.exec(\n command,\n { pty: { cols, rows, term: terminalType } },\n (err, channel) => {\n if (err) {\n reject(err)\n return\n }\n resolve({\n channel,\n resize: (c, r) => channel.setWindow(c, r, 0, 0),\n kill: () => channel.close(),\n })\n },\n )\n })\n }\n}\n\nfunction findLocalRg(): string | undefined {\n const candidates = [\n join(homedir(), '.dsh', 'node_modules', '.bin', 'rg'),\n join(homedir(), '.dsh', 'node_modules', '@vscode', 'ripgrep', 'bin', 'rg'),\n join(homedir(), 'go', 'bin', 'rg'),\n '/usr/local/bin/rg',\n '/usr/bin/rg',\n ]\n for (const c of candidates) if (existsSync(c)) return c\n return undefined\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { SshExecutor } from "./executor.js";
2
- import { SubprocessHandle, SubprocessSpawnSpec, SubprocessTerminalHandle, SubprocessTerminalSpawnSpec } from "@deepseek-ai/dsh-subprocess";
2
+ import { SubprocessHandle, SubprocessSpawnSpec, SubprocessTerminalEnvironment, SubprocessTerminalHandle, SubprocessTerminalSpawnSpec } from "@deepseek-ai/dsh-subprocess";
3
3
  import { ShellExecRequest, ShellExecSpec, ShellProcess, ShellRunResult } from "@deepseek-ai/dsh-shell";
4
4
  //#region src/exec-ssh/process-provider.d.ts
5
5
  declare class SshShellExecutor {
@@ -12,6 +12,8 @@ declare class SshShellExecutor {
12
12
  declare class SshSubprocessRuntime {
13
13
  private readonly executor;
14
14
  constructor(executor: SshExecutor);
15
+ terminalEnvironment(signal?: AbortSignal): Promise<SubprocessTerminalEnvironment>;
16
+ resolveExecutable(command: string, env?: Readonly<Record<string, string>>, signal?: AbortSignal): Promise<string>;
15
17
  spawn(spec: SubprocessSpawnSpec): SubprocessHandle;
16
18
  spawnTerminal(spec: SubprocessTerminalSpawnSpec): Promise<SubprocessTerminalHandle>;
17
19
  }
@@ -1,3 +1,4 @@
1
+ import { SubprocessExecutableNotFoundError } from "@deepseek-ai/dsh-subprocess";
1
2
  //#region src/exec-ssh/process-provider.ts
2
3
  var SshShellExecutor = class {
3
4
  executor;
@@ -49,6 +50,34 @@ var SshSubprocessRuntime = class {
49
50
  constructor(executor) {
50
51
  this.executor = executor;
51
52
  }
53
+ async terminalEnvironment(signal) {
54
+ signal?.throwIfAborted();
55
+ const result = await this.executor.exec([
56
+ "sh",
57
+ "-c",
58
+ `user=$(id -un); shell=$(getent passwd "$user" 2>/dev/null | cut -d: -f7); if [ -z "$shell" ]; then shell=$(awk -F: -v user="$user" '$1 == user {print $7; exit}' /etc/passwd); fi; printf "%s" "$shell"`
59
+ ], "/");
60
+ signal?.throwIfAborted();
61
+ const defaultShell = result.stdout.trim().split("\n")[0];
62
+ return {
63
+ platform: "posix",
64
+ ...defaultShell ? { defaultShell } : {}
65
+ };
66
+ }
67
+ async resolveExecutable(command, env = {}, signal) {
68
+ signal?.throwIfAborted();
69
+ const result = await this.executor.exec([
70
+ "sh",
71
+ "-c",
72
+ "command -v \"$1\"",
73
+ "sh",
74
+ command
75
+ ], "/", { ...env });
76
+ signal?.throwIfAborted();
77
+ const path = result.stdout.trim().split("\n")[0];
78
+ if (result.code !== 0 || !path) throw new SubprocessExecutableNotFoundError(`executable not found on SSH node: ${command}`);
79
+ return path;
80
+ }
52
81
  spawn(spec) {
53
82
  return {
54
83
  pid: -1,
@@ -60,12 +89,43 @@ var SshSubprocessRuntime = class {
60
89
  };
61
90
  }
62
91
  async spawnTerminal(spec) {
63
- const pty = await this.executor.pty(spec.argv, spec.cwd, spec.env ?? {}, spec.cols ?? 80, spec.rows ?? 24);
92
+ if (spec.signal?.aborted) throw spec.signal.reason;
93
+ const pty = await this.executor.pty(spec.argv, spec.cwd, spec.env ?? {}, spec.cols, spec.rows, spec.terminalType);
94
+ if (spec.signal?.aborted) {
95
+ pty.kill();
96
+ throw spec.signal.reason;
97
+ }
98
+ let exitCode = null;
99
+ const done = new Promise((resolve, reject) => {
100
+ pty.channel.once("exit", (code) => {
101
+ if (typeof code === "number") exitCode = code;
102
+ });
103
+ pty.channel.once("error", reject);
104
+ pty.channel.once("close", () => resolve({
105
+ exitCode,
106
+ signal: null
107
+ }));
108
+ });
64
109
  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()
110
+ pid: -1,
111
+ output: pty.channel,
112
+ done,
113
+ write: (data) => new Promise((resolve, reject) => {
114
+ pty.channel.write(data, (error) => error ? reject(error) : resolve());
115
+ }),
116
+ resize: async (cols, rows) => pty.resize(cols, rows),
117
+ inspectForeground: async () => void 0,
118
+ inspectActivity: async () => ({
119
+ state: "unknown",
120
+ revision: 0
121
+ }),
122
+ signalForeground: async () => {
123
+ throw new Error("foreground signaling is unsupported for SSH terminals");
124
+ },
125
+ terminate: async () => {
126
+ pty.kill();
127
+ await done.catch(() => {});
128
+ }
69
129
  };
70
130
  }
71
131
  };
@@ -1 +1 @@
1
- {"version":3,"file":"process-provider.js","names":[],"sources":["../../src/exec-ssh/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"}
1
+ {"version":3,"file":"process-provider.js","names":[],"sources":["../../src/exec-ssh/process-provider.ts"],"sourcesContent":["import type { ShellExecRequest, ShellExecSpec, ShellProcess, ShellRunResult } from '@deepseek-ai/dsh-shell'\nimport { SubprocessExecutableNotFoundError, type CollectedOutput, type SubprocessHandle, type SubprocessOutcome, type SubprocessSpawnSpec, type SubprocessTerminalEnvironment, type SubprocessTerminalHandle, type 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 async terminalEnvironment(signal?: AbortSignal): Promise<SubprocessTerminalEnvironment> {\n signal?.throwIfAborted()\n const script = `user=$(id -un); shell=$(getent passwd \"$user\" 2>/dev/null | cut -d: -f7); if [ -z \"$shell\" ]; then shell=$(awk -F: -v user=\"$user\" '$1 == user {print $7; exit}' /etc/passwd); fi; printf \"%s\" \"$shell\"`\n const result = await this.executor.exec(['sh', '-c', script], '/')\n signal?.throwIfAborted()\n const defaultShell = result.stdout.trim().split('\\n')[0]\n return { platform: 'posix', ...(defaultShell ? { defaultShell } : {}) }\n }\n\n async resolveExecutable(command: string, env: Readonly<Record<string, string>> = {}, signal?: AbortSignal): Promise<string> {\n signal?.throwIfAborted()\n const result = await this.executor.exec(['sh', '-c', 'command -v \"$1\"', 'sh', command], '/', { ...env })\n signal?.throwIfAborted()\n const path = result.stdout.trim().split('\\n')[0]\n if (result.code !== 0 || !path) throw new SubprocessExecutableNotFoundError(`executable not found on SSH node: ${command}`)\n return path\n }\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 if (spec.signal?.aborted) throw spec.signal.reason\n const pty = await this.executor.pty(spec.argv as string[], spec.cwd, spec.env ?? {}, spec.cols, spec.rows, spec.terminalType)\n if (spec.signal?.aborted) {\n pty.kill()\n throw spec.signal.reason\n }\n let exitCode: number | null = null\n const done = new Promise<SubprocessOutcome>((resolve, reject) => {\n pty.channel.once('exit', (code) => {\n if (typeof code === 'number') exitCode = code\n })\n pty.channel.once('error', reject)\n pty.channel.once('close', () => resolve({ exitCode, signal: null }))\n })\n const handle: SubprocessTerminalHandle = {\n pid: -1,\n output: pty.channel,\n done,\n write: (data) => new Promise<void>((resolve, reject) => {\n pty.channel.write(data, (error) => error ? reject(error) : resolve())\n }),\n resize: async (cols, rows) => pty.resize(cols, rows),\n inspectForeground: async () => undefined,\n inspectActivity: async () => ({ state: 'unknown', revision: 0 }),\n signalForeground: async () => { throw new Error('foreground signaling is unsupported for SSH terminals') },\n terminate: async () => {\n pty.kill()\n await done.catch(() => {})\n },\n }\n return handle\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { a as NodeTestReport, t as ExecutionWorldProvider } from "../types-C0TtEO7z.js";
1
+ import { a as NodeTestReport, t as ExecutionWorldProvider } from "../types-ekJQQ4x6.js";
2
2
  import { SshConnectionPool } from "./connection-pool.js";
3
3
  import { SshExecutor } from "./executor.js";
4
4
  import { SshFileSystem } from "./fs-provider.js";
@@ -1,6 +1,15 @@
1
- import { ShellExecutor } from "@deepseek-ai/dsh-shell";
1
+ import { o as ShellDelegate } from "../types-ekJQQ4x6.js";
2
+ import { ShellExecSpec, ShellExecution, ShellExecutor } from "@deepseek-ai/dsh-shell";
2
3
  //#region src/router/decorate-shell.d.ts
4
+ /**
5
+ * DSH 0.1.6+ collapses the shell seam into `resolve`/`execute`, and the
6
+ * `ShellExecution` handle replaces the old `run`/`start` pair. Remote worlds
7
+ * still expose a foreground `run`, so this adapts one remote run into a live
8
+ * execution handle: the foreground projection is the run's result, and the
9
+ * handle's status, exit classification, and collectors settle from it.
10
+ */
11
+ declare function executionFromRemoteRun(shell: ShellDelegate, spec: ShellExecSpec): Promise<ShellExecution>;
3
12
  declare function decorateShell(shell: ShellExecutor): () => void;
4
13
  //#endregion
5
- export { decorateShell };
14
+ export { decorateShell, executionFromRemoteRun };
6
15
  //# sourceMappingURL=decorate-shell.d.ts.map