@scrypted/server 0.0.83 → 0.0.84

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.

Potentially problematic release.


This version of @scrypted/server might be problematic. Click here for more details.

@@ -5,15 +5,11 @@ import { ScryptedRuntime } from '../runtime';
5
5
  import { Plugin } from '../db-types';
6
6
  import io from 'engine.io';
7
7
  import { attachPluginRemote, setupPluginRemote } from './plugin-remote';
8
- import { PluginRemote, PluginRemoteLoadZipOptions } from './plugin-api';
8
+ import { PluginAPI, PluginRemote, PluginRemoteLoadZipOptions } from './plugin-api';
9
9
  import { Logger } from '../logger';
10
10
  import { MediaManagerHostImpl, MediaManagerImpl } from './media';
11
11
  import { getState } from '../state';
12
12
  import WebSocket, { EventEmitter } from 'ws';
13
- import { listenZero } from './listen-zero';
14
- import { Server } from 'net';
15
- import repl from 'repl';
16
- import { once } from 'events';
17
13
  import { PassThrough } from 'stream';
18
14
  import { Console } from 'console'
19
15
  import { sleep } from '../sleep';
@@ -27,6 +23,8 @@ import readline from 'readline';
27
23
  import { Readable, Writable } from 'stream';
28
24
  import { ensurePluginVolume } from './plugin-volume';
29
25
  import { installOptionalDependencies } from './plugin-npm-dependencies';
26
+ import { ConsoleServer, createConsoleServer } from './plugin-console';
27
+ import { createREPLServer } from './plugin-repl';
30
28
 
31
29
  export class PluginHost {
32
30
  worker: child_process.ChildProcess;
@@ -49,6 +47,7 @@ export class PluginHost {
49
47
  memoryUsage: NodeJS.MemoryUsage,
50
48
  };
51
49
  killed = false;
50
+ consoleServer: Promise<ConsoleServer>;
52
51
 
53
52
  kill() {
54
53
  this.killed = true;
@@ -73,6 +72,14 @@ export class PluginHost {
73
72
  }
74
73
  }
75
74
  }
75
+
76
+ this.consoleServer?.then(server => {
77
+ server.readServer.close();
78
+ server.writeServer.close();
79
+ for (const s of server.sockets) {
80
+ s.destroy();
81
+ }
82
+ });
76
83
  setTimeout(() => this.peer.kill('plugin killed'), 500);
77
84
  }
78
85
 
@@ -228,9 +235,9 @@ export class PluginHost {
228
235
  path.join(__dirname, '../../python', 'plugin-remote.py'),
229
236
  )
230
237
 
231
- this.worker = child_process.spawn('python', args, {
238
+ this.worker = child_process.spawn('python3', args, {
232
239
  // stdin, stdout, stderr, peer in, peer out
233
- stdio: ['pipe', 'inherit', 'inherit', 'pipe', 'pipe'],
240
+ stdio: ['pipe', 'pipe', 'pipe', 'pipe', 'pipe'],
234
241
  env: Object.assign({}, process.env, env),
235
242
  });
236
243
 
@@ -264,7 +271,7 @@ export class PluginHost {
264
271
  }
265
272
 
266
273
  this.worker = child_process.fork(require.main.filename, ['child'], {
267
- stdio: ['pipe', 'inherit', 'inherit', 'ipc'],
274
+ stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
268
275
  env: Object.assign({}, process.env, env),
269
276
  serialization: 'advanced',
270
277
  execArgv,
@@ -286,8 +293,9 @@ export class PluginHost {
286
293
  this.worker.on('message', message => this.peer.handleMessage(message as any));
287
294
  }
288
295
 
289
- // this.worker.stdout.on('data', data => console.log(data.toString()));
290
- // this.worker.stderr.on('data', data => console.error(data.toString()));
296
+ this.worker.stdout.on('data', data => console.log(data.toString()));
297
+ this.worker.stderr.on('data', data => console.error(data.toString()));
298
+ this.consoleServer = createConsoleServer(this.worker.stdout, this.worker.stderr);
291
299
 
292
300
  this.worker.on('disconnect', () => {
293
301
  connected = false;
@@ -310,157 +318,6 @@ export class PluginHost {
310
318
  }
311
319
  }
312
320
 
313
- async function createConsoleServer(events: EventEmitter): Promise<number[]> {
314
- const outputs = new Map<string, Buffer[]>();
315
- const appendOutput = (data: Buffer, nativeId: ScryptedNativeId) => {
316
- if (!nativeId)
317
- nativeId = undefined;
318
- let buffers = outputs.get(nativeId);
319
- if (!buffers) {
320
- buffers = [];
321
- outputs.set(nativeId, buffers);
322
- }
323
- buffers.push(data);
324
- // when we're over 4000 lines or whatever these buffer are,
325
- // truncate down to 2000.
326
- if (buffers.length > 4000)
327
- outputs.set(nativeId, buffers.slice(buffers.length - 2000))
328
- };
329
- events.on('stdout', appendOutput);
330
- events.on('stderr', appendOutput);
331
-
332
- const server = new Server(async (socket) => {
333
- let [filter] = await once(socket, 'data');
334
- filter = filter.toString().trim();
335
- if (filter === 'undefined')
336
- filter = undefined;
337
-
338
- const buffers = outputs.get(filter);
339
- if (buffers) {
340
- const concat = Buffer.concat(buffers);
341
- outputs.set(filter, [concat]);
342
- socket.write(concat);
343
- }
344
-
345
- const cb = (data: Buffer, nativeId: ScryptedNativeId) => {
346
- if (nativeId !== filter)
347
- return;
348
- socket.write(data);
349
- };
350
- events.on('stdout', cb)
351
- events.on('stderr', cb)
352
-
353
- const cleanup = () => {
354
- events.removeListener('stdout', cb);
355
- events.removeListener('stderr', cb);
356
- };
357
-
358
- socket.on('close', cleanup);
359
- socket.on('error', cleanup);
360
- socket.on('end', cleanup);
361
- });
362
-
363
-
364
- const writeServer = new Server(async (socket) => {
365
- const [data] = await once(socket, 'data');
366
- let filter: string = data.toString();
367
- const newline = filter.indexOf('\n');
368
- if (newline !== -1) {
369
- socket.unshift(Buffer.from(filter.substring(newline + 1)));
370
- }
371
- filter = filter.substring(0, newline);
372
-
373
- if (filter === 'undefined')
374
- filter = undefined;
375
-
376
- const cb = (data: Buffer) => events.emit('stdout', data, filter);
377
-
378
- socket.on('data', cb);
379
-
380
- const cleanup = () => {
381
- events.removeListener('data', cb);
382
- };
383
-
384
- socket.on('close', cleanup);
385
- socket.on('error', cleanup);
386
- socket.on('end', cleanup);
387
- });
388
- const consoleReader = await listenZero(server);
389
- const consoleWriter = await listenZero(writeServer);
390
-
391
- return [consoleReader, consoleWriter];
392
- }
393
-
394
- async function createREPLServer(events: EventEmitter): Promise<number> {
395
- const [[scrypted], [params], [plugin]] = await Promise.all([once(events, 'scrypted'), once(events, 'params'), once(events, 'plugin')]);
396
- const { deviceManager, systemManager } = scrypted;
397
- const server = new Server(async (socket) => {
398
- let [filter] = await once(socket, 'data');
399
- filter = filter.toString().trim();
400
- if (filter === 'undefined')
401
- filter = undefined;
402
-
403
- const chain: string[] = [];
404
- const nativeIds: Map<string, any> = deviceManager.nativeIds;
405
- const reversed = new Map<string, string>();
406
- for (const nativeId of nativeIds.keys()) {
407
- reversed.set(nativeIds.get(nativeId).id, nativeId);
408
- }
409
-
410
- while (filter) {
411
- const { id } = nativeIds.get(filter);
412
- const d = await systemManager.getDeviceById(id);
413
- chain.push(filter);
414
- filter = reversed.get(d.providerId);
415
- }
416
-
417
- chain.reverse();
418
- let device = plugin;
419
- for (const c of chain) {
420
- device = await device.getDevice(c);
421
- }
422
-
423
-
424
- const ctx = Object.assign(params, {
425
- device
426
- });
427
- delete ctx.console;
428
- delete ctx.window;
429
- delete ctx.WebSocket;
430
- delete ctx.pluginHostAPI;
431
-
432
- const replFilter = new Set<string>(['require', 'localStorage'])
433
- const replVariables = Object.keys(ctx).filter(key => !replFilter.has(key));
434
-
435
- const welcome = `JavaScript REPL variables:\n${replVariables.map(key => ' ' + key).join('\n')}\n\n`;
436
- socket.write(welcome);
437
-
438
- const r = repl.start({
439
- terminal: true,
440
- input: socket,
441
- output: socket,
442
- // writer(this: REPLServer, obj: any) {
443
- // const ret = util.inspect(obj, {
444
- // colors: true,
445
- // });
446
- // return ret;//.replaceAll('\n', '\r\n');
447
- // },
448
- preview: false,
449
- });
450
-
451
- Object.assign(r.context, ctx);
452
-
453
- const cleanup = () => {
454
- r.close();
455
- };
456
-
457
- socket.on('close', cleanup);
458
- socket.on('error', cleanup);
459
- socket.on('end', cleanup);
460
- });
461
- return listenZero(server);
462
- }
463
-
464
321
  export function startPluginRemote() {
465
322
  const peer = new RpcPeer('unknown', 'host', (message, reject) => process.send(message, undefined, {
466
323
  swallowErrors: !reject,
@@ -471,10 +328,10 @@ export function startPluginRemote() {
471
328
  peer.transportSafeArgumentTypes.add(Buffer.name);
472
329
  process.on('message', message => peer.handleMessage(message as RpcMessage));
473
330
 
474
- const events = new EventEmitter();
475
-
476
331
  let systemManager: SystemManager;
477
332
  let deviceManager: DeviceManager;
333
+ let api: PluginAPI;
334
+ let pluginId: string;
478
335
 
479
336
  function idForNativeId(nativeId: ScryptedNativeId) {
480
337
  if (!deviceManager)
@@ -528,16 +385,31 @@ export function startPluginRemote() {
528
385
 
529
386
  const getDeviceConsole = (nativeId?: ScryptedNativeId) => {
530
387
  return getConsole(async (stdout, stderr) => {
531
- stdout.on('data', data => events.emit('stdout', data, nativeId));
532
- stderr.on('data', data => events.emit('stderr', data, nativeId));
388
+ const plugins = await api.getComponent('plugins');
389
+ const connect = async () => {
390
+ const port = await plugins.getRemoteServicePort(peer.selfName, 'console-writer');
391
+ const socket = net.connect(port);
392
+ socket.write(nativeId + '\n');
393
+ const writer = (data: Buffer) => {
394
+ socket.write(data);
395
+ };
396
+ stdout.on('data', writer);
397
+ stderr.on('data', writer);
398
+ socket.on('error', () => {
399
+ stdout.removeAllListeners();
400
+ stderr.removeAllListeners();
401
+ stdout.pause();
402
+ stderr.pause();
403
+ setTimeout(connect, 10000);
404
+ });
405
+ };
406
+ connect();
533
407
  }, undefined, undefined);
534
408
  }
535
409
 
536
410
  const getMixinConsole = (mixinId: string, nativeId?: ScryptedNativeId) => {
537
411
  return getConsole(async (stdout, stderr) => {
538
412
  if (!mixinId || !systemManager.getDeviceById(mixinId).mixins.includes(idForNativeId(nativeId))) {
539
- stdout.on('data', data => events.emit('stdout', data, nativeId));
540
- stderr.on('data', data => events.emit('stderr', data, nativeId));
541
413
  return;
542
414
  }
543
415
  const plugins = await systemManager.getComponent('plugins');
@@ -583,28 +455,40 @@ export function startPluginRemote() {
583
455
  global?.gc();
584
456
  }, 10000);
585
457
 
586
- const consolePorts = createConsoleServer(events);
587
- const replPort = createREPLServer(events);
458
+ let replPort: Promise<number>;
588
459
 
589
- const pluginConsole = getDeviceConsole(undefined);
460
+ let _pluginConsole: Console;
461
+ const getPluginConsole = () => {
462
+ if (_pluginConsole)
463
+ return _pluginConsole;
464
+ _pluginConsole = getDeviceConsole(undefined);
465
+ }
590
466
 
591
467
  attachPluginRemote(peer, {
592
- createMediaManager: async (systemManager) => new MediaManagerImpl(systemManager, pluginConsole),
593
- events,
468
+ createMediaManager: async (sm) => {
469
+ systemManager = sm;
470
+ return new MediaManagerImpl(systemManager, getPluginConsole());
471
+ },
472
+ onGetRemote: async (_api, _pluginId) => {
473
+ api = _api;
474
+ pluginId = _pluginId;
475
+ peer.selfName = pluginId;
476
+ },
477
+ onPluginReady: async(scrypted, params, plugin) => {
478
+ replPort = createREPLServer(scrypted, params, plugin);
479
+ },
480
+ getPluginConsole,
594
481
  getDeviceConsole,
595
482
  getMixinConsole,
596
483
  async getServicePort(name) {
597
- if (name === 'repl')
484
+ if (name === 'repl') {
485
+ if (!replPort)
486
+ throw new Error('REPL unavailable: Plugin not loaded.')
598
487
  return replPort;
599
- if (name === 'console')
600
- return (await consolePorts)[0];
601
- if (name === 'console-writer')
602
- return (await consolePorts)[1];
488
+ }
603
489
  throw new Error(`unknown service ${name}`);
604
490
  },
605
- async beforeLoadZip(zip: AdmZip, packageJson: any) {
606
- const pluginId = packageJson.name;
607
- peer.selfName = pluginId;
491
+ async onLoadZip(zip: AdmZip, packageJson: any) {
608
492
  installSourceMapSupport({
609
493
  environment: 'node',
610
494
  retrieveSourceMap(source) {
@@ -621,29 +505,18 @@ export function startPluginRemote() {
621
505
  return null;
622
506
  }
623
507
  });
624
- const cp = await consolePorts;
625
- const writer = cp[1];
626
- const socket = net.connect(writer);
627
- await once(socket, 'connect');
628
- try {
629
- await installOptionalDependencies(pluginConsole, socket, packageJson);
630
- }
631
- finally {
632
- socket.destroy();
633
- }
508
+ await installOptionalDependencies(getPluginConsole(), packageJson);
634
509
  }
635
510
  }).then(scrypted => {
636
511
  systemManager = scrypted.systemManager;
637
512
  deviceManager = scrypted.deviceManager;
638
513
 
639
- events.emit('scrypted', scrypted);
640
-
641
514
  process.on('uncaughtException', e => {
642
- pluginConsole.error('uncaughtException', e);
515
+ getPluginConsole().error('uncaughtException', e);
643
516
  scrypted.log.e('uncaughtException ' + e?.toString());
644
517
  });
645
518
  process.on('unhandledRejection', e => {
646
- pluginConsole.error('unhandledRejection', e);
519
+ getPluginConsole().error('unhandledRejection', e);
647
520
  scrypted.log.e('unhandledRejection ' + e?.toString());
648
521
  });
649
522
  })
@@ -3,9 +3,8 @@ import fs from 'fs';
3
3
  import child_process from 'child_process';
4
4
  import path from 'path';
5
5
  import { once } from 'events';
6
- import { Socket } from "net";
7
6
 
8
- export async function installOptionalDependencies(console: Console, socket: Socket, packageJson: any) {
7
+ export async function installOptionalDependencies(console: Console, packageJson: any) {
9
8
  const pluginVolume = ensurePluginVolume(packageJson.name);
10
9
  const optPj = path.join(pluginVolume, 'package.json');
11
10
 
@@ -40,7 +39,7 @@ export async function installOptionalDependencies(console: Console, socket: Sock
40
39
 
41
40
  const cp = child_process.spawn('npm', ['--prefix', pluginVolume, 'install'], {
42
41
  cwd: pluginVolume,
43
- stdio: ['inherit', socket, socket],
42
+ stdio: 'inherit',
44
43
  });
45
44
 
46
45
  await once(cp, 'exit');
@@ -135,7 +135,6 @@ class DeviceManagerImpl implements DeviceManager {
135
135
  nativeIds = new Map<string, DeviceManagerDevice>();
136
136
 
137
137
  constructor(public systemManager: SystemManagerImpl,
138
- public events?: EventEmitter,
139
138
  public getDeviceConsole?: (nativeId?: ScryptedNativeId) => Console,
140
139
  public getMixinConsole?: (mixinId: string, nativeId?: ScryptedNativeId) => Console) {
141
140
  }
@@ -283,13 +282,15 @@ export interface PluginRemoteAttachOptions {
283
282
  createMediaManager?: (systemManager: SystemManager) => Promise<MediaManager>;
284
283
  getServicePort?: (name: string) => Promise<number>;
285
284
  getDeviceConsole?: (nativeId?: ScryptedNativeId) => Console;
285
+ getPluginConsole?: () => Console;
286
286
  getMixinConsole?: (id: string, nativeId?: ScryptedNativeId) => Console;
287
- events?: EventEmitter;
288
- beforeLoadZip?: (zip: AdmZip, packageJson: any) => Promise<void>;
287
+ onLoadZip?: (zip: AdmZip, packageJson: any) => Promise<void>;
288
+ onGetRemote?: (api: PluginAPI, pluginId: string) => Promise<void>;
289
+ onPluginReady?: (scrypted: ScryptedStatic, params: any, plugin: any) => Promise<void>;
289
290
  }
290
291
 
291
292
  export function attachPluginRemote(peer: RpcPeer, options?: PluginRemoteAttachOptions): Promise<ScryptedStatic> {
292
- const { createMediaManager, getServicePort, events, getDeviceConsole, getMixinConsole } = options || {};
293
+ const { createMediaManager, getServicePort, getDeviceConsole, getMixinConsole, getPluginConsole } = options || {};
293
294
 
294
295
  peer.addSerializer(Buffer, 'Buffer', new BufferSerializer());
295
296
 
@@ -297,8 +298,10 @@ export function attachPluginRemote(peer: RpcPeer, options?: PluginRemoteAttachOp
297
298
  const retPromise = new Promise<ScryptedStatic>(resolve => done = resolve);
298
299
 
299
300
  peer.params.getRemote = async (api: PluginAPI, pluginId: string) => {
301
+ await options?.onGetRemote?.(api, pluginId);
302
+
300
303
  const systemManager = new SystemManagerImpl();
301
- const deviceManager = new DeviceManagerImpl(systemManager, events, getDeviceConsole, getMixinConsole);
304
+ const deviceManager = new DeviceManagerImpl(systemManager, getDeviceConsole, getMixinConsole);
302
305
  const endpointManager = new EndpointManagerImpl();
303
306
  const ioSockets: { [id: string]: WebSocketCallbacks } = {};
304
307
  const mediaManager = await api.getMediaManager() || await createMediaManager(systemManager);
@@ -403,10 +406,10 @@ export function attachPluginRemote(peer: RpcPeer, options?: PluginRemoteAttachOp
403
406
  },
404
407
 
405
408
  async loadZip(packageJson: any, zipData: Buffer, zipOptions?: PluginRemoteLoadZipOptions) {
406
- const pluginConsole = getDeviceConsole?.(undefined);
409
+ const pluginConsole = getPluginConsole?.();
407
410
  pluginConsole?.log('starting plugin', pluginId, packageJson.version);
408
411
  const zip = new AdmZip(zipData);
409
- await options?.beforeLoadZip?.(zip, packageJson);
412
+ await options?.onLoadZip?.(zip, packageJson);
410
413
  const main = zip.getEntry('main.nodejs.js');
411
414
  const script = main.getData().toString();
412
415
  const window: any = {};
@@ -482,12 +485,10 @@ export function attachPluginRemote(peer: RpcPeer, options?: PluginRemoteAttachOp
482
485
 
483
486
  params.console = pluginConsole;
484
487
 
485
- events?.emit('params', params);
486
-
487
488
  try {
488
489
  peer.evalLocal(script, zipOptions?.filename || '/plugin/main.nodejs.js', params);
489
- events?.emit('plugin', exports.default);
490
490
  pluginConsole?.log('plugin successfully loaded');
491
+ await options?.onPluginReady?.(ret, params, exports.default);
491
492
  return exports.default;
492
493
  }
493
494
  catch (e) {
@@ -0,0 +1,74 @@
1
+ import { EventEmitter } from 'ws';
2
+ import { listenZero } from './listen-zero';
3
+ import { Server } from 'net';
4
+ import { once } from 'events';
5
+ import repl from 'repl';
6
+ import { ScryptedStatic } from '@scrypted/sdk';
7
+
8
+ export async function createREPLServer(scrypted: ScryptedStatic, params: any, plugin: any): Promise<number> {
9
+ const { deviceManager, systemManager } = scrypted;
10
+ const server = new Server(async (socket) => {
11
+ let [filter] = await once(socket, 'data');
12
+ filter = filter.toString().trim();
13
+ if (filter === 'undefined')
14
+ filter = undefined;
15
+
16
+ const chain: string[] = [];
17
+ const nativeIds: Map<string, any> = (deviceManager as any).nativeIds;
18
+ const reversed = new Map<string, string>();
19
+ for (const nativeId of nativeIds.keys()) {
20
+ reversed.set(nativeIds.get(nativeId).id, nativeId);
21
+ }
22
+
23
+ while (filter) {
24
+ const { id } = nativeIds.get(filter);
25
+ const d = await systemManager.getDeviceById(id);
26
+ chain.push(filter);
27
+ filter = reversed.get(d.providerId);
28
+ }
29
+
30
+ chain.reverse();
31
+ let device = plugin;
32
+ for (const c of chain) {
33
+ device = await device.getDevice(c);
34
+ }
35
+
36
+ const ctx = Object.assign(params, {
37
+ device
38
+ });
39
+ delete ctx.console;
40
+ delete ctx.window;
41
+ delete ctx.WebSocket;
42
+ delete ctx.pluginHostAPI;
43
+
44
+ const replFilter = new Set<string>(['require', 'localStorage'])
45
+ const replVariables = Object.keys(ctx).filter(key => !replFilter.has(key));
46
+
47
+ const welcome = `JavaScript REPL variables:\n${replVariables.map(key => ' ' + key).join('\n')}\n\n`;
48
+ socket.write(welcome);
49
+
50
+ const r = repl.start({
51
+ terminal: true,
52
+ input: socket,
53
+ output: socket,
54
+ // writer(this: REPLServer, obj: any) {
55
+ // const ret = util.inspect(obj, {
56
+ // colors: true,
57
+ // });
58
+ // return ret;//.replaceAll('\n', '\r\n');
59
+ // },
60
+ preview: false,
61
+ });
62
+
63
+ Object.assign(r.context, ctx);
64
+
65
+ const cleanup = () => {
66
+ r.close();
67
+ };
68
+
69
+ socket.on('close', cleanup);
70
+ socket.on('error', cleanup);
71
+ socket.on('end', cleanup);
72
+ });
73
+ return listenZero(server);
74
+ }
@@ -63,43 +63,19 @@ else {
63
63
  })
64
64
  }
65
65
 
66
- const debuggers = new Map<number, net.Socket[]>();
67
66
  const debugServer = net.createServer(async (socket) => {
68
67
  if (!workerInspectPort) {
69
68
  socket.destroy();
70
69
  return;
71
70
  }
72
71
 
73
- const entry = debuggers.get(workerInspectPort);
74
- if (!entry) {
75
- debuggers.set(workerInspectPort, [socket]);
76
- }
77
- else {
78
- entry.push(socket);
79
- }
80
-
81
72
  for (let i = 0; i < 10; i++) {
82
73
  try {
83
74
  const target = await doconnect();
84
75
  socket.pipe(target).pipe(socket);
85
76
  const destroy = () => {
86
- socket.end();
87
77
  socket.destroy();
88
- target.end();
89
78
  target.destroy();
90
-
91
- if (!entry) {
92
- const sockets = debuggers.get(workerInspectPort);
93
- if (!debuggers.delete(workerInspectPort))
94
- return;
95
- setTimeout(() => {
96
- if (debuggers.has(workerInspectPort))
97
- return;
98
- for (const s of sockets) {
99
- s.destroy();
100
- }
101
- }, 500)
102
- }
103
79
  }
104
80
  socket.on('error', destroy);
105
81
  target.on('error', destroy);
@@ -185,7 +161,7 @@ else {
185
161
 
186
162
  // legacy secure port 9443 is now in use by portainer.
187
163
  let shownLegacyPortAlert = false
188
- const legacySecure = https.createServer({ key: keys.serviceKey, cert: keys.certificate }, (req, res) => {
164
+ const legacySecure = https.createServer(mergedHttpsServerOptions, (req, res) => {
189
165
  if (!shownLegacyPortAlert) {
190
166
  const core = scrypted.findPluginDevice('@scrypted/core');
191
167
  if (core) {
@@ -273,8 +249,8 @@ else {
273
249
  console.log('Ports can be changed with environment variables.')
274
250
  console.log('https: $SCRYPTED_SECURE_PORT')
275
251
  console.log('http : $SCRYPTED_INSECURE_PORT')
276
- console.log('Certificate can be by providing tls.createSecureContext options')
277
- console.log('JSON file in the SCRYPTED_HTTPS_OPTIONS_FILE environment variable:');
252
+ console.log('Certificate can be modified via tls.createSecureContext options in')
253
+ console.log('JSON file located at SCRYPTED_HTTPS_OPTIONS_FILE environment variable:');
278
254
  console.log('export SCRYPTED_HTTPS_OPTIONS_FILE=/path/to/options.json');
279
255
  console.log('https://nodejs.org/api/tls.html#tlscreatesecurecontextoptions')
280
256
  console.log('#######################################################');
@@ -119,6 +119,14 @@ export class PluginComponent {
119
119
  }
120
120
 
121
121
  async getRemoteServicePort(pluginId: string, name: string): Promise<number> {
122
+ if (name === 'console') {
123
+ const consoleServer = await this.scrypted.plugins[pluginId].consoleServer;
124
+ return consoleServer.readPort;
125
+ }
126
+ if (name === 'console-writer') {
127
+ const consoleServer = await this.scrypted.plugins[pluginId].consoleServer;
128
+ return consoleServer.writePort;
129
+ }
122
130
  return this.scrypted.plugins[pluginId].remote.getServicePort(name);
123
131
  }
124
132
  }