@scrypted/server 0.94.22 → 0.94.24

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 (40) hide show
  1. package/dist/plugin/plugin-api.d.ts +3 -10
  2. package/dist/plugin/plugin-host.d.ts +2 -0
  3. package/dist/plugin/plugin-host.js +30 -48
  4. package/dist/plugin/plugin-host.js.map +1 -1
  5. package/dist/plugin/plugin-lazy-remote.d.ts +1 -1
  6. package/dist/plugin/plugin-lazy-remote.js +2 -2
  7. package/dist/plugin/plugin-lazy-remote.js.map +1 -1
  8. package/dist/plugin/plugin-remote-worker.js +26 -48
  9. package/dist/plugin/plugin-remote-worker.js.map +1 -1
  10. package/dist/plugin/plugin-remote.d.ts +1 -2
  11. package/dist/plugin/plugin-remote.js +2 -2
  12. package/dist/plugin/plugin-remote.js.map +1 -1
  13. package/dist/plugin/runtime/custom-worker.d.ts +12 -0
  14. package/dist/plugin/runtime/custom-worker.js +72 -0
  15. package/dist/plugin/runtime/custom-worker.js.map +1 -0
  16. package/dist/plugin/runtime/node-thread-worker.d.ts +1 -1
  17. package/dist/plugin/runtime/node-thread-worker.js +2 -2
  18. package/dist/plugin/runtime/node-thread-worker.js.map +1 -1
  19. package/dist/plugin/runtime/node-worker-common.d.ts +13 -0
  20. package/dist/plugin/runtime/node-worker-common.js +76 -0
  21. package/dist/plugin/runtime/node-worker-common.js.map +1 -0
  22. package/dist/plugin/runtime/python-worker.js.map +1 -1
  23. package/dist/plugin/runtime/runtime-worker.d.ts +3 -0
  24. package/dist/runtime.d.ts +2 -4
  25. package/dist/runtime.js +3 -72
  26. package/dist/runtime.js.map +1 -1
  27. package/package.json +3 -3
  28. package/python/plugin_remote.py +34 -27
  29. package/python/plugin_volume.py +45 -0
  30. package/src/plugin/plugin-api.ts +3 -10
  31. package/src/plugin/plugin-host.ts +35 -50
  32. package/src/plugin/plugin-lazy-remote.ts +2 -2
  33. package/src/plugin/plugin-remote-worker.ts +28 -53
  34. package/src/plugin/plugin-remote.ts +3 -5
  35. package/src/plugin/runtime/custom-worker.ts +81 -0
  36. package/src/plugin/runtime/node-thread-worker.ts +2 -4
  37. package/src/plugin/runtime/node-worker-common.ts +77 -0
  38. package/src/plugin/runtime/python-worker.ts +1 -1
  39. package/src/plugin/runtime/runtime-worker.ts +3 -0
  40. package/src/runtime.ts +4 -81
@@ -1,10 +1,10 @@
1
1
  import { ScryptedStatic, SystemManager } from '@scrypted/types';
2
- import AdmZip from 'adm-zip';
3
2
  import { once } from 'events';
4
3
  import fs from 'fs';
5
4
  import net from 'net';
6
5
  import path from 'path';
7
6
  import { install as installSourceMapSupport } from 'source-map-support';
7
+ import worker_threads from 'worker_threads';
8
8
  import { computeClusterObjectHash } from '../cluster/cluster-hash';
9
9
  import { ClusterObject, ConnectRPCObject } from '../cluster/connect-rpc-object';
10
10
  import { listenZero } from '../listen-zero';
@@ -14,11 +14,12 @@ import { MediaManagerImpl } from './media';
14
14
  import { PluginAPI, PluginAPIProxy, PluginRemote, PluginRemoteLoadZipOptions } from './plugin-api';
15
15
  import { prepareConsoles } from './plugin-console';
16
16
  import { getPluginNodePath, installOptionalDependencies } from './plugin-npm-dependencies';
17
- import { DeviceManagerImpl, PluginReader, attachPluginRemote, setupPluginRemote } from './plugin-remote';
17
+ import { DeviceManagerImpl, attachPluginRemote, setupPluginRemote } from './plugin-remote';
18
18
  import { PluginStats, startStatsUpdater } from './plugin-remote-stats';
19
19
  import { createREPLServer } from './plugin-repl';
20
+ import { getPluginVolume } from './plugin-volume';
20
21
  import { NodeThreadWorker } from './runtime/node-thread-worker';
21
- import worker_threads from 'worker_threads';
22
+ import { prepareZip } from './runtime/node-worker-common';
22
23
 
23
24
  const serverVersion = require('../../package.json').version;
24
25
 
@@ -75,8 +76,9 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
75
76
  }
76
77
  throw new Error(`unknown service ${name}`);
77
78
  },
78
- async onLoadZip(scrypted: ScryptedStatic, params: any, packageJson: any, zipData: Buffer | string, zipOptions: PluginRemoteLoadZipOptions) {
79
- const { clusterId, clusterSecret } = zipOptions;
79
+ async onLoadZip(scrypted: ScryptedStatic, params: any, packageJson: any, getZip: () => Promise<Buffer>, zipOptions: PluginRemoteLoadZipOptions) {
80
+ const { clusterId, clusterSecret, zipHash } = zipOptions;
81
+ const { zipFile, unzippedPath } = await prepareZip(getPluginVolume(pluginId), zipHash, getZip);
80
82
 
81
83
  const onProxySerialization = (value: any, proxyId: string, sourcePeerPort?: number) => {
82
84
  const properties = RpcPeer.prepareProxyProperties(value) || {};
@@ -194,50 +196,20 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
194
196
  return value;
195
197
  }
196
198
  }
197
-
198
- // let volume: any;
199
- let pluginReader: PluginReader;
200
- if (zipOptions?.unzippedPath && fs.existsSync(zipOptions?.unzippedPath)) {
201
- if (worker_threads.isMainThread) {
202
- const fsDir = path.join(zipOptions.unzippedPath, 'fs')
203
- if (fs.existsSync(fsDir))
204
- process.chdir(fsDir);
205
- else
206
- process.chdir(zipOptions.unzippedPath);
207
- }
208
-
209
- // volume = link(fs, ['', path.join(zipOptions.unzippedPath, 'fs')]);
210
- pluginReader = name => {
211
- const filename = path.join(zipOptions.unzippedPath, name);
212
- if (!fs.existsSync(filename))
213
- return;
214
- return fs.readFileSync(filename);
215
- };
199
+ if (worker_threads.isMainThread) {
200
+ const fsDir = path.join(unzippedPath, 'fs')
201
+ if (fs.existsSync(fsDir))
202
+ process.chdir(fsDir);
203
+ else
204
+ process.chdir(unzippedPath);
216
205
  }
217
- else {
218
- // this code path was used in testing and should be unreachable.
219
-
220
- const admZip = new AdmZip(zipData);
221
- // volume = new Volume();
222
- // for (const entry of admZip.getEntries()) {
223
- // if (entry.isDirectory)
224
- // continue;
225
- // if (!entry.entryName.startsWith('fs/'))
226
- // continue;
227
- // const name = entry.entryName.substring('fs/'.length);
228
- // volume.mkdirpSync(path.dirname(name));
229
- // const data = entry.getData();
230
- // volume.writeFileSync(name, data);
231
- // }
232
-
233
- pluginReader = name => {
234
- const entry = admZip.getEntry(name);
235
- if (!entry)
236
- return;
237
- return entry.getData();
238
- }
239
- }
240
- zipData = undefined;
206
+
207
+ const pluginReader = (name: string) => {
208
+ const filename = path.join(unzippedPath, name);
209
+ if (!fs.existsSync(filename))
210
+ return;
211
+ return fs.readFileSync(filename);
212
+ };
241
213
 
242
214
  const pluginConsole = getPluginConsole?.();
243
215
  params.console = pluginConsole;
@@ -248,9 +220,9 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
248
220
  return require('fs');
249
221
  }
250
222
  try {
251
- if (name.startsWith('.') && zipOptions?.unzippedPath) {
223
+ if (name.startsWith('.') && unzippedPath) {
252
224
  try {
253
- const c = path.join(zipOptions.unzippedPath, name);
225
+ const c = path.join(unzippedPath, name);
254
226
  const module = require(c);
255
227
  return module;
256
228
  }
@@ -314,7 +286,6 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
314
286
  peer.getParam('updateStats').then(updateStats => startStatsUpdater(allMemoryStats, updateStats));
315
287
 
316
288
  const main = pluginReader('main.nodejs.js');
317
- pluginReader = undefined;
318
289
  const script = main.toString();
319
290
 
320
291
  scrypted.connect = (socket, options) => {
@@ -328,6 +299,9 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
328
299
  packageJson,
329
300
  env: process.env,
330
301
  pluginDebug: undefined,
302
+ zipFile,
303
+ unzippedPath,
304
+ zipHash,
331
305
  });
332
306
 
333
307
  const result = (async () => {
@@ -368,7 +342,7 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
368
342
 
369
343
  const forkOptions = Object.assign({}, zipOptions);
370
344
  forkOptions.fork = true;
371
- return remote.loadZip(packageJson, zipData, forkOptions)
345
+ return remote.loadZip(packageJson, getZip, forkOptions)
372
346
  })();
373
347
 
374
348
  result.catch(() => ntw.kill());
@@ -380,7 +354,8 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
380
354
  }
381
355
 
382
356
  try {
383
- peer.evalLocal(script, zipOptions?.filename || '/plugin/main.nodejs.js', params);
357
+ const filename = zipOptions?.debug ? '/plugin/main.nodejs.js' : `/${pluginId}/main.nodejs.js`;
358
+ peer.evalLocal(script, filename, params);
384
359
 
385
360
  if (zipOptions?.fork) {
386
361
  // pluginConsole?.log('plugin forked');
@@ -455,15 +455,13 @@ export interface WebSocketCustomHandler {
455
455
  methods: WebSocketMethods;
456
456
  }
457
457
 
458
- export type PluginReader = (name: string) => Buffer;
459
-
460
458
  export interface PluginRemoteAttachOptions {
461
459
  createMediaManager?: (systemManager: SystemManager, deviceManager: DeviceManagerImpl) => Promise<MediaManager>;
462
460
  getServicePort?: (name: string, ...args: any[]) => Promise<number>;
463
461
  getDeviceConsole?: (nativeId?: ScryptedNativeId) => Console;
464
462
  getPluginConsole?: () => Console;
465
463
  getMixinConsole?: (id: string, nativeId?: ScryptedNativeId) => Console;
466
- onLoadZip?: (scrypted: ScryptedStatic, params: any, packageJson: any, zipData: Buffer | string, zipOptions: PluginRemoteLoadZipOptions) => Promise<any>;
464
+ onLoadZip?: (scrypted: ScryptedStatic, params: any, packageJson: any, getZip: () => Promise<Buffer>, zipOptions: PluginRemoteLoadZipOptions) => Promise<any>;
467
465
  onGetRemote?: (api: PluginAPI, pluginId: string) => Promise<void>;
468
466
  }
469
467
 
@@ -637,7 +635,7 @@ export function attachPluginRemote(peer: RpcPeer, options?: PluginRemoteAttachOp
637
635
  done(ret);
638
636
  },
639
637
 
640
- async loadZip(packageJson: any, zipData: Buffer | string, zipOptions?: PluginRemoteLoadZipOptions) {
638
+ async loadZip(packageJson: any, getZip: () => Promise<Buffer>, zipOptions?: PluginRemoteLoadZipOptions) {
641
639
  const params: any = {
642
640
  __filename: undefined,
643
641
  deviceManager,
@@ -660,7 +658,7 @@ export function attachPluginRemote(peer: RpcPeer, options?: PluginRemoteAttachOp
660
658
  params.pluginRuntimeAPI = ret;
661
659
 
662
660
  try {
663
- return await options.onLoadZip(ret, params, packageJson, zipData, zipOptions);
661
+ return await options.onLoadZip(ret, params, packageJson, getZip, zipOptions);
664
662
  }
665
663
  catch (e) {
666
664
  console.error('plugin start/fork failed', e)
@@ -0,0 +1,81 @@
1
+ import { ScryptedRuntimeArguments } from '@scrypted/types';
2
+ import child_process from 'child_process';
3
+ import { Readable, Writable } from 'stream';
4
+ import { RpcMessage, RpcPeer } from "../../rpc";
5
+ import { createRpcDuplexSerializer } from '../../rpc-serializer';
6
+ import type { ScryptedRuntime } from '../../runtime';
7
+ import { ChildProcessWorker } from "./child-process-worker";
8
+ import { RuntimeWorkerOptions } from "./runtime-worker";
9
+
10
+ export class CustomRuntimeWorker extends ChildProcessWorker {
11
+ serializer: ReturnType<typeof createRpcDuplexSerializer>;
12
+ fork: boolean;
13
+
14
+ constructor(pluginId: string, options: RuntimeWorkerOptions, runtime: ScryptedRuntime) {
15
+ super(pluginId, options);
16
+
17
+ const pluginDevice = runtime.findPluginDevice(this.pluginId);
18
+ const scryptedRuntimeArguments: ScryptedRuntimeArguments = pluginDevice.state.scryptedRuntimeArguments?.value;
19
+ if (!scryptedRuntimeArguments)
20
+ throw new Error('custom runtime requires scryptedRuntimeArguments');
21
+
22
+ const { env, pluginDebug } = options;
23
+
24
+ const args = [...scryptedRuntimeArguments.arguments || []];
25
+
26
+ const opts: child_process.ForkOptions | child_process.SpawnOptions = {
27
+ // stdin, stdout, stderr, peer in, peer out
28
+ stdio: ['pipe', 'pipe', 'pipe', 'pipe', 'pipe', 'pipe'],
29
+ env: Object.assign({
30
+ SCRYYPTED_PLUGIN_ID: pluginId,
31
+ SCRYPTED_DEBUG_PORT: pluginDebug?.inspectPort?.toString(),
32
+ SCRYPTED_UNZIPPED_PATH: options.unzippedPath,
33
+ SCRYPTED_ZIP_FILE: options.zipFile,
34
+ SCRYPTED_ZIP_HASH: options.zipHash,
35
+ }, process.env, env),
36
+ };
37
+
38
+ if (!scryptedRuntimeArguments.executable) {
39
+ this.fork = true;
40
+ const modulePath = args.shift();
41
+ if (!modulePath)
42
+ throw new Error('fork runtime requires modulePath in first argument.');
43
+
44
+ (opts.stdio as any)[5] = 'ipc';
45
+ this.worker = child_process.fork(modulePath, args, opts);
46
+ }
47
+ else {
48
+ this.worker = child_process.spawn(scryptedRuntimeArguments.executable, args, opts);
49
+ }
50
+
51
+ this.setupWorker();
52
+ }
53
+
54
+ setupRpcPeer(peer: RpcPeer): void {
55
+ const peerin = this.worker.stdio[3] as Writable;
56
+ const peerout = this.worker.stdio[this.fork ? 3 : 4] as Readable;
57
+
58
+ const serializer = this.serializer = createRpcDuplexSerializer(peerin);
59
+ serializer.setupRpcPeer(peer);
60
+ peerout.on('data', data => serializer.onData(data));
61
+ peerin.on('error', e => {
62
+ this.emit('error', e);
63
+ serializer.onDisconnected();
64
+ });
65
+ peerout.on('error', e => {
66
+ this.emit('error', e)
67
+ serializer.onDisconnected();
68
+ });
69
+ }
70
+
71
+ send(message: RpcMessage, reject?: (e: Error) => void, serializationContext?: any): void {
72
+ try {
73
+ if (!this.worker)
74
+ throw new Error('python worker has been killed');
75
+ this.serializer.sendMessage(message, reject, serializationContext);
76
+ }
77
+ catch (e) {
78
+ reject?.(e);
79
+ }
80
+ }
81
+ }
@@ -1,10 +1,8 @@
1
+ import v8 from 'v8';
2
+ import worker_threads from "worker_threads";
1
3
  import { EventEmitter } from "ws";
2
4
  import { RpcMessage, RpcPeer } from "../../rpc";
3
5
  import { RuntimeWorker, RuntimeWorkerOptions } from "./runtime-worker";
4
- import worker_threads from "worker_threads";
5
- import path from 'path';
6
- import { getPluginNodePath } from "../plugin-npm-dependencies";
7
- import v8 from 'v8';
8
6
 
9
7
  export class NodeThreadWorker extends EventEmitter implements RuntimeWorker {
10
8
  terminated: boolean;
@@ -0,0 +1,77 @@
1
+ import AdmZip from 'adm-zip';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ function createAdmZipHash(hash: string) {
6
+ const extractVersion = "1-";
7
+ return extractVersion + hash;
8
+ }
9
+
10
+ function prep(pluginVolume: string, hash: string) {
11
+ hash = createAdmZipHash(hash);
12
+
13
+ const zipFilename = `${hash}.zip`;
14
+ const zipDir = path.join(pluginVolume, 'zip');
15
+ const zipFile = path.join(zipDir, zipFilename);
16
+ const unzippedPath = path.join(zipDir, 'unzipped')
17
+ const zipDirTmp = zipDir + '.tmp';
18
+
19
+ return {
20
+ unzippedPath,
21
+ zipFilename,
22
+ zipDir,
23
+ zipFile,
24
+ zipDirTmp,
25
+ };
26
+ }
27
+
28
+ export async function prepareZip(pluginVolume: string, h: string, getZip: () => Promise<Buffer>) {
29
+ const { zipFile, unzippedPath } = prep(pluginVolume, h);
30
+ if (fs.existsSync(zipFile)) {
31
+ return {
32
+ zipFile,
33
+ unzippedPath,
34
+ }
35
+ }
36
+
37
+ const zipBuffer = await getZip();
38
+ return extractZip(pluginVolume, h, zipBuffer);
39
+ }
40
+
41
+ export function prepareZipSync(pluginVolume: string, h: string, getZip: () => Buffer) {
42
+ const { zipFile, unzippedPath } = prep(pluginVolume, h);
43
+ if (fs.existsSync(zipFile)) {
44
+ return {
45
+ zipFile,
46
+ unzippedPath,
47
+ }
48
+ }
49
+
50
+ const zipBuffer = getZip();
51
+ return extractZip(pluginVolume, h, zipBuffer);
52
+ }
53
+
54
+ export function extractZip(pluginVolume: string, h: string, zipBuffer: Buffer) {
55
+ const { zipDir, zipDirTmp, zipFilename, zipFile, unzippedPath } = prep(pluginVolume, h);
56
+
57
+ fs.rmSync(zipDirTmp, {
58
+ recursive: true,
59
+ force: true,
60
+ });
61
+ fs.rmSync(zipDir, {
62
+ recursive: true,
63
+ force: true,
64
+ });
65
+ fs.mkdirSync(zipDirTmp, {
66
+ recursive: true,
67
+ });
68
+ fs.writeFileSync(path.join(zipDirTmp, zipFilename), zipBuffer);
69
+ const admZip = new AdmZip(zipBuffer);
70
+ admZip.extractAllTo(path.join(zipDirTmp, 'unzipped'), true);
71
+ fs.renameSync(zipDirTmp, zipDir);
72
+
73
+ return {
74
+ zipFile,
75
+ unzippedPath,
76
+ }
77
+ }
@@ -2,12 +2,12 @@ import child_process from 'child_process';
2
2
  import fs from "fs";
3
3
  import os from "os";
4
4
  import path from 'path';
5
+ import type { PortablePython as PortablePythonType } from 'py';
5
6
  import { Readable, Writable } from 'stream';
6
7
  import { RpcMessage, RpcPeer } from "../../rpc";
7
8
  import { createRpcDuplexSerializer } from '../../rpc-serializer';
8
9
  import { ChildProcessWorker } from "./child-process-worker";
9
10
  import { RuntimeWorkerOptions } from "./runtime-worker";
10
- import type {PortablePython as PortablePythonType} from 'py'
11
11
 
12
12
  export class PythonRuntimeWorker extends ChildProcessWorker {
13
13
  static {
@@ -6,6 +6,9 @@ import net from "net";
6
6
  export interface RuntimeWorkerOptions {
7
7
  packageJson: any;
8
8
  pluginDebug: PluginDebug;
9
+ zipFile: string,
10
+ unzippedPath: string;
11
+ zipHash: string;
9
12
  env: any;
10
13
  }
11
14
 
package/src/runtime.ts CHANGED
@@ -9,7 +9,6 @@ import fs from 'fs';
9
9
  import http, { ServerResponse } from 'http';
10
10
  import https from 'https';
11
11
  import net from 'net';
12
- import type { spawn as ptySpawn } from '@homebridge/node-pty-prebuilt-multiarch';
13
12
  import path from 'path';
14
13
  import { ParsedQs } from 'qs';
15
14
  import semver from 'semver';
@@ -40,13 +39,14 @@ import { RuntimeWorker, RuntimeWorkerOptions } from './plugin/runtime/runtime-wo
40
39
  import { getIpAddress, SCRYPTED_INSECURE_PORT, SCRYPTED_SECURE_PORT } from './server-settings';
41
40
  import { AddressSettings } from './services/addresses';
42
41
  import { Alerts } from './services/alerts';
42
+ import { Backup } from './services/backup';
43
43
  import { CORSControl } from './services/cors';
44
44
  import { Info } from './services/info';
45
45
  import { getNpmPackageInfo, PluginComponent } from './services/plugin';
46
46
  import { ServiceControl } from './services/service-control';
47
47
  import { UsersService } from './services/users';
48
48
  import { getState, ScryptedStateManager, setState } from './state';
49
- import { Backup } from './services/backup';
49
+ import { CustomRuntimeWorker } from './plugin/runtime/custom-worker';
50
50
 
51
51
  interface DeviceProxyPair {
52
52
  handler: PluginDeviceProxyHandler;
@@ -61,7 +61,7 @@ interface HttpPluginData {
61
61
  pluginDevice: PluginDevice
62
62
  }
63
63
 
64
- export type RuntimeHost = (mainFilename: string, pluginId: string, options: RuntimeWorkerOptions) => RuntimeWorker;
64
+ export type RuntimeHost = (mainFilename: string, pluginId: string, options: RuntimeWorkerOptions, runtime: ScryptedRuntime) => RuntimeWorker;
65
65
 
66
66
  export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
67
67
  clusterId = crypto.randomBytes(3).toString('hex');
@@ -74,17 +74,6 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
74
74
  devicesLogger = this.logger.getLogger('device', 'Devices');
75
75
  wss = new WebSocketServer({ noServer: true });
76
76
  wsAtomic = 0;
77
- shellio: IOServer = new io.Server({
78
- pingTimeout: 120000,
79
- perMessageDeflate: true,
80
- cors: (req, callback) => {
81
- const header = this.getAccessControlAllowOrigin(req.headers);
82
- callback(undefined, {
83
- origin: header,
84
- credentials: true,
85
- })
86
- },
87
- });
88
77
  connectRPCObjectIO: IOServer = new io.Server({
89
78
  pingTimeout: 120000,
90
79
  perMessageDeflate: true,
@@ -111,6 +100,7 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
111
100
  // ensure that all the users are loaded from the db.
112
101
  this.usersService.getAllUsers();
113
102
 
103
+ this.pluginHosts.set('custom', (_, pluginId, options, runtime) => new CustomRuntimeWorker(pluginId, options, runtime));
114
104
  this.pluginHosts.set('python', (_, pluginId, options) => new PythonRuntimeWorker(pluginId, options));
115
105
  this.pluginHosts.set('node', (mainFilename, pluginId, options) => new NodeForkWorker(mainFilename, pluginId, options));
116
106
 
@@ -118,46 +108,6 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
118
108
 
119
109
  this.addMiddleware();
120
110
 
121
- app.all('/engine.io/shell', (req, res) => {
122
- if (res.locals.aclId) {
123
- res.writeHead(401);
124
- res.end();
125
- return;
126
- }
127
- this.shellHandler(req, res);
128
- });
129
-
130
- this.shellio.on('connection', connection => {
131
- try {
132
- const spawn = require('@homebridge/node-pty-prebuilt-multiarch').spawn as typeof ptySpawn;
133
- const cp = spawn(process.env.SHELL, [], {
134
- });
135
- cp.onData(data => connection.send(data));
136
- connection.on('message', message => {
137
- if (Buffer.isBuffer(message)) {
138
- cp.write(message.toString());
139
- return;
140
- }
141
-
142
- try {
143
- const parsed = JSON.parse(message.toString());
144
- if (parsed.dim) {
145
- cp.resize(parsed.dim.cols, parsed.dim.rows);
146
- }
147
- } catch (e) {
148
- // we should only get here if an outdated core plugin
149
- // is sending us string data instead of buffer data
150
- cp.write(message.toString());
151
- }
152
- });
153
- connection.on('close', () => cp.kill());
154
- cp.onExit(() => connection.close());
155
- }
156
- catch (e) {
157
- connection.close();
158
- }
159
- });
160
-
161
111
  app.all('/engine.io/connectRPCObject', (req, res) => this.connectRPCObjectHandler(req, res));
162
112
 
163
113
  /*
@@ -305,33 +255,6 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
305
255
  };
306
256
  }
307
257
 
308
- async shellHandler(req: Request, res: Response) {
309
- const isUpgrade = isConnectionUpgrade(req.headers);
310
-
311
- const end = (code: number, message: string) => {
312
- if (isUpgrade) {
313
- const socket = res.socket;
314
- socket.write(`HTTP/1.1 ${code} ${message}\r\n` +
315
- '\r\n');
316
- socket.destroy();
317
- }
318
- else {
319
- res.status(code);
320
- res.send(message);
321
- }
322
- };
323
-
324
- if (!res.locals.username) {
325
- end(401, 'Not Authorized');
326
- return;
327
- }
328
-
329
- if ((req as any).upgradeHead)
330
- this.shellio.handleUpgrade(req, res.socket, (req as any).upgradeHead)
331
- else
332
- this.shellio.handleRequest(req, res);
333
- }
334
-
335
258
  async connectRPCObjectHandler(req: Request, res: Response) {
336
259
  const isUpgrade = isConnectionUpgrade(req.headers);
337
260