pty-serv 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/cli.js CHANGED
@@ -1,7 +1,12 @@
1
+ import { unlinkSync } from 'node:fs';
1
2
  import { createServer } from 'node:http';
2
3
  import { program } from 'commander';
3
- import * as pty from 'node-pty';
4
+ import { spawn } from 'node-pty';
5
+ import { handlePtyRequest } from './request-handler.js';
4
6
  program
7
+ .enablePositionalOptions()
8
+ .passThroughOptions()
9
+ .name('pty-serv')
5
10
  .option('-o, --options <string>', 'JSON options forwarded to `node-pty`')
6
11
  .option('-s, --socket <string>', 'Unix socket path to listen on')
7
12
  .option('-h, --hostname <string>', 'Hostname for TCP listener')
@@ -10,79 +15,40 @@ program
10
15
  .parse();
11
16
  const opts = program.opts();
12
17
  const server = createServer();
13
- const ptyProcess = pty.spawn(program.args[0], program.args.slice(1), opts.options && JSON.parse(opts.options));
14
- console.log('PTY spawned with PID:', ptyProcess.pid);
15
- ptyProcess.onData((chunk) => {
16
- process.stdout.write(chunk);
17
- });
18
- let hasExit = false;
19
- ptyProcess.onExit((info) => {
20
- hasExit = true;
21
- server.close();
22
- process.exit(info.exitCode);
23
- });
24
- const EXIT_SIGNALS = ['SIGINT', 'SIGTERM'];
25
- EXIT_SIGNALS.forEach((signal) => {
26
- process.on(signal, () => {
27
- if (!hasExit) {
28
- ptyProcess.kill(process.platform === 'win32' ? undefined : signal);
29
- }
30
- server.close();
31
- process.exit(0);
18
+ server.on('listening', () => {
19
+ const ptyProcess = spawn(program.args[0], program.args.slice(1), opts.options && JSON.parse(opts.options));
20
+ ptyProcess.onData((chunk) => {
21
+ process.stdout.write(chunk);
22
+ });
23
+ ptyProcess.onExit((info) => {
24
+ server.close(() => {
25
+ process.exit(info.exitCode);
26
+ });
27
+ setTimeout(() => {
28
+ process.exit(info.exitCode);
29
+ }, 0).unref();
30
+ });
31
+ server.on('request', (req, res) => {
32
+ handlePtyRequest(req, res, ptyProcess);
32
33
  });
33
- });
34
- server.on('request', async (req, res) => {
35
- try {
36
- await handleRequest(req);
37
- res.writeHead(200, { 'Content-Type': 'application/json' });
38
- res.end(JSON.stringify({ code: 0 }));
39
- }
40
- catch (error) {
41
- res.writeHead(400, { 'Content-Type': 'application/json' });
42
- res.end(JSON.stringify({ code: -1, message: String(error) }));
43
- }
44
34
  });
45
35
  if (opts.socket) {
36
+ tryUnlink(opts.socket);
46
37
  server.listen(opts.socket);
47
38
  }
48
- else {
39
+ else if (opts.port != null) {
49
40
  server.listen(opts.port, opts.hostname);
50
41
  }
51
- async function handleRequest(req) {
52
- switch (req.url) {
53
- case '/resize': {
54
- const data = await parseBody(req);
55
- ptyProcess.resize(data.cols, data.rows);
56
- return;
57
- }
58
- case '/clear':
59
- ptyProcess.clear();
60
- return;
61
- case '/write':
62
- for await (const chunk of req) {
63
- ptyProcess.write(chunk);
64
- }
65
- return;
66
- case '/kill': {
67
- const data = await parseBody(req);
68
- ptyProcess.kill(data.signal);
69
- return;
70
- }
71
- case '/pause':
72
- ptyProcess.pause();
73
- return;
74
- case '/resume':
75
- ptyProcess.resume();
76
- return;
77
- }
42
+ else {
43
+ program
44
+ .addHelpText('after', '\nEither --socket or --port must be specified')
45
+ .help({ error: true });
78
46
  }
79
- async function parseBody(req) {
80
- if (req.method !== 'POST') {
81
- throw new Error(`Can't parse body for ${req.method} request`);
47
+ function tryUnlink(filename) {
48
+ try {
49
+ unlinkSync(filename);
82
50
  }
83
- const chunks = [];
84
- for await (const chunk of req) {
85
- chunks.push(chunk);
51
+ catch {
52
+ // noop
86
53
  }
87
- return JSON.parse(Buffer.concat(chunks).toString());
88
54
  }
@@ -0,0 +1,2 @@
1
+ export * from './request-handler.js';
2
+ export * from './spawn.js';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from './request-handler.js';
2
+ export * from './spawn.js';
@@ -0,0 +1,3 @@
1
+ import { IncomingMessage, ServerResponse } from 'http';
2
+ import { IPty } from 'node-pty';
3
+ export declare function handlePtyRequest(req: IncomingMessage, res: ServerResponse, ptyProcess: IPty): Promise<void>;
@@ -0,0 +1,49 @@
1
+ export async function handlePtyRequest(req, res, ptyProcess) {
2
+ try {
3
+ await handleRequest(ptyProcess, req);
4
+ res.writeHead(200, { 'Content-Type': 'application/json' });
5
+ res.end(JSON.stringify({ code: 0 }));
6
+ }
7
+ catch (error) {
8
+ res.writeHead(400, { 'Content-Type': 'application/json' });
9
+ res.end(JSON.stringify({ code: -1, message: String(error) }));
10
+ }
11
+ }
12
+ async function handleRequest(ptyProcess, req) {
13
+ switch (req.url) {
14
+ case '/resize': {
15
+ const data = await parseBody(req);
16
+ ptyProcess.resize(data.cols, data.rows);
17
+ return;
18
+ }
19
+ case '/clear':
20
+ ptyProcess.clear();
21
+ return;
22
+ case '/write':
23
+ for await (const chunk of req) {
24
+ ptyProcess.write(chunk);
25
+ }
26
+ return;
27
+ case '/kill': {
28
+ const data = await parseBody(req);
29
+ ptyProcess.kill(data.signal);
30
+ return;
31
+ }
32
+ case '/pause':
33
+ ptyProcess.pause();
34
+ return;
35
+ case '/resume':
36
+ ptyProcess.resume();
37
+ return;
38
+ }
39
+ }
40
+ async function parseBody(req) {
41
+ if (req.method !== 'POST') {
42
+ throw new Error(`Can't parse body for ${req.method} request`);
43
+ }
44
+ const chunks = [];
45
+ for await (const chunk of req) {
46
+ chunks.push(chunk);
47
+ }
48
+ return JSON.parse(Buffer.concat(chunks).toString());
49
+ }
@@ -0,0 +1,20 @@
1
+ import { IPtyForkOptions, IWindowsPtyForkOptions } from 'node-pty';
2
+ export interface SpawnPtyOptions {
3
+ /**
4
+ * Options forwarded to `node-pty`
5
+ */
6
+ ptyOptions?: IPtyForkOptions | IWindowsPtyForkOptions;
7
+ /**
8
+ * Unix socket path to listen on
9
+ */
10
+ socket?: string;
11
+ /**
12
+ * Hostname for TCP listener
13
+ */
14
+ hostname?: string;
15
+ /**
16
+ * TCP port to listen on
17
+ */
18
+ port?: number;
19
+ }
20
+ export declare function getSpawnPtyArgs(command: string, args?: string[], options?: SpawnPtyOptions): string[];
package/dist/spawn.js ADDED
@@ -0,0 +1,21 @@
1
+ import path from 'node:path';
2
+ export function getSpawnPtyArgs(command, args, options) {
3
+ const nodeArgs = [path.resolve(import.meta.dirname, 'cli.js')];
4
+ if (options?.ptyOptions) {
5
+ nodeArgs.push('--options', JSON.stringify(options.ptyOptions));
6
+ }
7
+ if (options?.socket) {
8
+ nodeArgs.push('--socket', options.socket);
9
+ }
10
+ if (options?.hostname) {
11
+ nodeArgs.push('--hostname', options.hostname);
12
+ }
13
+ if (options?.port != null) {
14
+ nodeArgs.push('--port', String(options.port));
15
+ }
16
+ nodeArgs.push(command);
17
+ if (args) {
18
+ nodeArgs.push(...args);
19
+ }
20
+ return nodeArgs;
21
+ }
package/package.json CHANGED
@@ -1,11 +1,20 @@
1
1
  {
2
2
  "name": "pty-serv",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/gzzhanghao/node-pty-serv.git"
7
7
  },
8
8
  "type": "module",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "main": "./dist/index.js",
16
+ "module": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
9
18
  "bin": {
10
19
  "pty-serv": "./bin/cli.js"
11
20
  },