@scrypted/server 0.0.138 → 0.0.142

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.

Files changed (66) hide show
  1. package/dist/http-interfaces.js +8 -4
  2. package/dist/http-interfaces.js.map +1 -1
  3. package/dist/plugin/media.js +29 -14
  4. package/dist/plugin/media.js.map +1 -1
  5. package/dist/plugin/plugin-console.js +13 -3
  6. package/dist/plugin/plugin-console.js.map +1 -1
  7. package/dist/plugin/plugin-device.js +1 -1
  8. package/dist/plugin/plugin-device.js.map +1 -1
  9. package/dist/plugin/plugin-host-api.js +1 -1
  10. package/dist/plugin/plugin-host-api.js.map +1 -1
  11. package/dist/plugin/plugin-host.js +36 -127
  12. package/dist/plugin/plugin-host.js.map +1 -1
  13. package/dist/plugin/plugin-remote-worker.js +5 -42
  14. package/dist/plugin/plugin-remote-worker.js.map +1 -1
  15. package/dist/plugin/plugin-remote.js +3 -3
  16. package/dist/plugin/plugin-remote.js.map +1 -1
  17. package/dist/plugin/runtime/child-process-worker.js +42 -0
  18. package/dist/plugin/runtime/child-process-worker.js.map +1 -0
  19. package/dist/plugin/runtime/node-fork-worker.js +51 -0
  20. package/dist/plugin/runtime/node-fork-worker.js.map +1 -0
  21. package/dist/plugin/runtime/node-thread-worker.js +73 -0
  22. package/dist/plugin/runtime/node-thread-worker.js.map +1 -0
  23. package/dist/plugin/runtime/python-worker.js +54 -0
  24. package/dist/plugin/runtime/python-worker.js.map +1 -0
  25. package/dist/plugin/runtime/runtime-worker.js +3 -0
  26. package/dist/plugin/runtime/runtime-worker.js.map +1 -0
  27. package/dist/plugin/system.js +3 -3
  28. package/dist/plugin/system.js.map +1 -1
  29. package/dist/rpc.js +79 -55
  30. package/dist/rpc.js.map +1 -1
  31. package/dist/runtime.js +1 -6
  32. package/dist/runtime.js.map +1 -1
  33. package/dist/scrypted-main.js +14 -7
  34. package/dist/scrypted-main.js.map +1 -1
  35. package/dist/scrypted-plugin-main.js +31 -4
  36. package/dist/scrypted-plugin-main.js.map +1 -1
  37. package/dist/scrypted-server-main.js +1 -1
  38. package/dist/scrypted-server-main.js.map +1 -1
  39. package/dist/threading.js +99 -0
  40. package/dist/threading.js.map +1 -0
  41. package/package.json +5 -3
  42. package/python/plugin-remote.py +31 -2
  43. package/python/rpc.py +6 -0
  44. package/src/http-interfaces.ts +10 -5
  45. package/src/plugin/media.ts +34 -16
  46. package/src/plugin/plugin-console.ts +15 -6
  47. package/src/plugin/plugin-device.ts +2 -2
  48. package/src/plugin/plugin-host-api.ts +2 -2
  49. package/src/plugin/plugin-host.ts +42 -146
  50. package/src/plugin/plugin-remote-websocket.ts +1 -1
  51. package/src/plugin/plugin-remote-worker.ts +7 -44
  52. package/src/plugin/plugin-remote.ts +5 -5
  53. package/src/plugin/runtime/child-process-worker.ts +49 -0
  54. package/src/plugin/runtime/node-fork-worker.ts +54 -0
  55. package/src/plugin/runtime/node-thread-worker.ts +76 -0
  56. package/src/plugin/runtime/python-worker.ts +67 -0
  57. package/src/plugin/runtime/runtime-worker.ts +28 -0
  58. package/src/plugin/system.ts +4 -4
  59. package/src/rpc.ts +92 -66
  60. package/src/runtime.ts +1 -8
  61. package/src/scrypted-main.ts +15 -7
  62. package/src/scrypted-plugin-main.ts +31 -5
  63. package/src/scrypted-server-main.ts +1 -1
  64. package/src/threading.ts +108 -0
  65. package/.vscode/launch.json +0 -62
  66. package/.vscode/settings.json +0 -8
@@ -1,6 +1,6 @@
1
1
  import { EventListenerOptions, EventDetails, EventListenerRegister, ScryptedDevice, ScryptedInterface, ScryptedInterfaceDescriptors, SystemDeviceState, SystemManager, ScryptedInterfaceProperty, ScryptedDeviceType, Logger } from "@scrypted/types";
2
2
  import { PluginAPI } from "./plugin-api";
3
- import { handleFunctionInvocations, PrimitiveProxyHandler, PROPERTY_PROXY_ONEWAY_METHODS } from '../rpc';
3
+ import { PrimitiveProxyHandler, RpcPeer } from '../rpc';
4
4
  import { EventRegistry } from "../event-registry";
5
5
  import { allInterfaceProperties, isValidInterfaceMethod } from "./descriptor";
6
6
 
@@ -24,7 +24,7 @@ class DeviceProxyHandler implements PrimitiveProxyHandler<any>, ScryptedDevice {
24
24
  if (p === 'id')
25
25
  return this.id;
26
26
 
27
- const handled = handleFunctionInvocations(this, target, p, receiver);
27
+ const handled = RpcPeer.handleFunctionInvocations(this, target, p, receiver);
28
28
  if (handled)
29
29
  return handled;
30
30
 
@@ -96,10 +96,10 @@ class EventListenerRegisterImpl implements EventListenerRegister {
96
96
 
97
97
  function makeOneWayCallback<T>(input: T): T {
98
98
  const f: any = input;
99
- const oneways: string[] = f[PROPERTY_PROXY_ONEWAY_METHODS] || [];
99
+ const oneways: string[] = f[RpcPeer.PROPERTY_PROXY_ONEWAY_METHODS] || [];
100
100
  if (!oneways.includes(null))
101
101
  oneways.push(null);
102
- f[PROPERTY_PROXY_ONEWAY_METHODS] = oneways;
102
+ f[RpcPeer.PROPERTY_PROXY_ONEWAY_METHODS] = oneways;
103
103
  return input;
104
104
  }
105
105
 
package/src/rpc.ts CHANGED
@@ -1,25 +1,21 @@
1
1
  import vm from 'vm';
2
2
 
3
- const finalizerIdSymbol = Symbol('rpcFinalizerId');
4
-
5
- function getDefaultTransportSafeArgumentTypes() {
6
- const jsonSerializable = new Set<string>();
7
- jsonSerializable.add(Number.name);
8
- jsonSerializable.add(String.name);
9
- jsonSerializable.add(Object.name);
10
- jsonSerializable.add(Boolean.name);
11
- jsonSerializable.add(Array.name);
12
- return jsonSerializable;
13
- }
14
-
15
3
  export function startPeriodicGarbageCollection() {
16
4
  if (!global.gc) {
17
5
  console.warn('rpc peer garbage collection not available: global.gc is not exposed.');
18
6
  return;
19
7
  }
20
- return setInterval(() => {
21
- global?.gc();
22
- }, 10000);
8
+ try {
9
+ const g = global;
10
+ if (g.gc) {
11
+ return setInterval(() => {
12
+ g.gc!();
13
+ }, 10000);
14
+ }
15
+ }
16
+ catch (e) {
17
+
18
+ }
23
19
  }
24
20
 
25
21
  export interface RpcMessage {
@@ -32,7 +28,7 @@ interface RpcParam extends RpcMessage {
32
28
  }
33
29
 
34
30
  interface RpcApply extends RpcMessage {
35
- id: string;
31
+ id: string | undefined;
36
32
  proxyId: string;
37
33
  args: any[];
38
34
  method: string;
@@ -51,8 +47,8 @@ interface RpcOob extends RpcMessage {
51
47
  }
52
48
 
53
49
  interface RpcRemoteProxyValue {
54
- __remote_proxy_id: string;
55
- __remote_proxy_finalizer_id: string;
50
+ __remote_proxy_id: string | undefined;
51
+ __remote_proxy_finalizer_id: string | undefined;
56
52
  __remote_constructor_name: string;
57
53
  __remote_proxy_props: any;
58
54
  __remote_proxy_oneway_methods: string[];
@@ -65,7 +61,7 @@ interface RpcLocalProxyValue {
65
61
 
66
62
  interface RpcFinalize extends RpcMessage {
67
63
  __local_proxy_id: string;
68
- __local_proxy_finalizer_id: string;
64
+ __local_proxy_finalizer_id: string | undefined;
69
65
  }
70
66
 
71
67
  interface Deferred {
@@ -73,29 +69,6 @@ interface Deferred {
73
69
  reject: any;
74
70
  }
75
71
 
76
- export function handleFunctionInvocations(thiz: PrimitiveProxyHandler<any>, target: any, p: PropertyKey, receiver: any): any {
77
- if (p === 'apply') {
78
- return (thisArg: any, args: any[]) => {
79
- return thiz.apply(target, thiz, args);
80
- }
81
- }
82
- else if (p === 'call') {
83
- return (thisArg: any, ...args: any[]) => {
84
- return thiz.apply(target, thiz, args);
85
- }
86
- }
87
- else if (p === 'toString' || p === Symbol.toPrimitive) {
88
- return (thisArg: any, ...args: any[]) => {
89
- return thiz.toPrimitive();
90
- }
91
- }
92
- }
93
-
94
- export const PROPERTY_PROXY_ONEWAY_METHODS = '__proxy_oneway_methods';
95
- export const PROPERTY_JSON_DISABLE_SERIALIZATION = '__json_disable_serialization';
96
- export const PROPERTY_PROXY_PROPERTIES = '__proxy_props';
97
- export const PROPERTY_JSON_COPY_SERIALIZE_CHILDREN = '__json_copy_serialize_children';
98
-
99
72
  export interface PrimitiveProxyHandler<T extends object> extends ProxyHandler<T> {
100
73
  toPrimitive(): any;
101
74
  }
@@ -120,11 +93,11 @@ class RpcProxy implements PrimitiveProxyHandler<any> {
120
93
  return this.constructorName;
121
94
  if (p === '__proxy_peer')
122
95
  return this.peer;
123
- if (p === PROPERTY_PROXY_PROPERTIES)
96
+ if (p === RpcPeer.PROPERTY_PROXY_PROPERTIES)
124
97
  return this.proxyProps;
125
- if (p === PROPERTY_PROXY_ONEWAY_METHODS)
98
+ if (p === RpcPeer.PROPERTY_PROXY_ONEWAY_METHODS)
126
99
  return this.proxyOneWayMethods;
127
- if (p === PROPERTY_JSON_DISABLE_SERIALIZATION || p === PROPERTY_JSON_COPY_SERIALIZE_CHILDREN)
100
+ if (p === RpcPeer.PROPERTY_JSON_DISABLE_SERIALIZATION || p === RpcPeer.PROPERTY_JSON_COPY_SERIALIZE_CHILDREN)
128
101
  return;
129
102
  if (p === 'then')
130
103
  return;
@@ -132,14 +105,14 @@ class RpcProxy implements PrimitiveProxyHandler<any> {
132
105
  return;
133
106
  if (this.proxyProps?.[p] !== undefined)
134
107
  return this.proxyProps?.[p];
135
- const handled = handleFunctionInvocations(this, target, p, receiver);
108
+ const handled = RpcPeer.handleFunctionInvocations(this, target, p, receiver);
136
109
  if (handled)
137
110
  return handled;
138
111
  return new Proxy(() => p, this);
139
112
  }
140
113
 
141
114
  set(target: any, p: string | symbol, value: any, receiver: any): boolean {
142
- if (p === finalizerIdSymbol)
115
+ if (p === RpcPeer.finalizerIdSymbol)
143
116
  this.entry.finalizerId = value;
144
117
  return true;
145
118
  }
@@ -180,7 +153,7 @@ class RpcProxy implements PrimitiveProxyHandler<any> {
180
153
 
181
154
  // todo: error constructor adds a "cause" variable in Chrome 93, Node v??
182
155
  export class RPCResultError extends Error {
183
- constructor(peer: RpcPeer, message: string, public cause?: Error, options?: { name: string, stack: string}) {
156
+ constructor(peer: RpcPeer, message: string, public cause?: Error, options?: { name: string, stack: string | undefined }) {
184
157
  super(`${peer.selfName}:${peer.peerName}: ${message}`);
185
158
 
186
159
  if (options?.name) {
@@ -225,12 +198,12 @@ export interface RpcSerializer {
225
198
 
226
199
  interface LocalProxiedEntry {
227
200
  id: string;
228
- finalizerId: string;
201
+ finalizerId: string | undefined;
229
202
  }
230
203
 
231
204
  export class RpcPeer {
232
205
  idCounter = 1;
233
- onOob: (oob: any) => void;
206
+ onOob?: (oob: any) => void;
234
207
  params: { [name: string]: any } = {};
235
208
  pendingResults: { [id: string]: Deferred } = {};
236
209
  proxyCounter = 1;
@@ -240,7 +213,42 @@ export class RpcPeer {
240
213
  finalizers = new FinalizationRegistry(entry => this.finalize(entry as LocalProxiedEntry));
241
214
  nameDeserializerMap = new Map<string, RpcSerializer>();
242
215
  constructorSerializerMap = new Map<string, string>();
243
- transportSafeArgumentTypes = getDefaultTransportSafeArgumentTypes();
216
+ transportSafeArgumentTypes = RpcPeer.getDefaultTransportSafeArgumentTypes();
217
+
218
+ static readonly finalizerIdSymbol = Symbol('rpcFinalizerId');
219
+
220
+ static getDefaultTransportSafeArgumentTypes() {
221
+ const jsonSerializable = new Set<string>();
222
+ jsonSerializable.add(Number.name);
223
+ jsonSerializable.add(String.name);
224
+ jsonSerializable.add(Object.name);
225
+ jsonSerializable.add(Boolean.name);
226
+ jsonSerializable.add(Array.name);
227
+ return jsonSerializable;
228
+ }
229
+
230
+ static handleFunctionInvocations(thiz: PrimitiveProxyHandler<any>, target: any, p: PropertyKey, receiver: any): any {
231
+ if (p === 'apply') {
232
+ return (thisArg: any, args: any[]) => {
233
+ return thiz.apply!(target, thiz, args);
234
+ }
235
+ }
236
+ else if (p === 'call') {
237
+ return (thisArg: any, ...args: any[]) => {
238
+ return thiz.apply!(target, thiz, args);
239
+ }
240
+ }
241
+ else if (p === 'toString' || p === Symbol.toPrimitive) {
242
+ return (thisArg: any, ...args: any[]) => {
243
+ return thiz.toPrimitive();
244
+ }
245
+ }
246
+ }
247
+
248
+ static readonly PROPERTY_PROXY_ONEWAY_METHODS = '__proxy_oneway_methods';
249
+ static readonly PROPERTY_JSON_DISABLE_SERIALIZATION = '__json_disable_serialization';
250
+ static readonly PROPERTY_PROXY_PROPERTIES = '__proxy_props';
251
+ static readonly PROPERTY_JSON_COPY_SERIALIZE_CHILDREN = '__json_copy_serialize_children';
244
252
 
245
253
  constructor(public selfName: string, public peerName: string, public send: (message: RpcMessage, reject?: (e: Error) => void) => void) {
246
254
  }
@@ -327,7 +335,7 @@ export class RpcPeer {
327
335
  if (!value)
328
336
  return value;
329
337
 
330
- const copySerializeChildren = value[PROPERTY_JSON_COPY_SERIALIZE_CHILDREN];
338
+ const copySerializeChildren = value[RpcPeer.PROPERTY_JSON_COPY_SERIALIZE_CHILDREN];
331
339
  if (copySerializeChildren) {
332
340
  const ret: any = {};
333
341
  for (const [key, val] of Object.entries(value)) {
@@ -341,7 +349,7 @@ export class RpcPeer {
341
349
  let proxy = this.remoteWeakProxies[__remote_proxy_id]?.deref();
342
350
  if (!proxy)
343
351
  proxy = this.newProxy(__remote_proxy_id, __remote_constructor_name, __remote_proxy_props, __remote_proxy_oneway_methods);
344
- proxy[finalizerIdSymbol] = __remote_proxy_finalizer_id;
352
+ proxy[RpcPeer.finalizerIdSymbol] = __remote_proxy_finalizer_id;
345
353
  return proxy;
346
354
  }
347
355
 
@@ -352,15 +360,16 @@ export class RpcPeer {
352
360
  return ret;
353
361
  }
354
362
 
355
- if (this.nameDeserializerMap.has(__remote_constructor_name)) {
356
- return this.nameDeserializerMap.get(__remote_constructor_name).deserialize(__serialized_value);
363
+ const deserializer = this.nameDeserializerMap.get(__remote_constructor_name);
364
+ if (deserializer) {
365
+ return deserializer.deserialize(__serialized_value);
357
366
  }
358
367
 
359
368
  return value;
360
369
  }
361
370
 
362
371
  serialize(value: any): any {
363
- if (value?.[PROPERTY_JSON_COPY_SERIALIZE_CHILDREN] === true) {
372
+ if (value?.[RpcPeer.PROPERTY_JSON_COPY_SERIALIZE_CHILDREN] === true) {
364
373
  const ret: any = {};
365
374
  for (const [key, val] of Object.entries(value)) {
366
375
  ret[key] = this.serialize(val);
@@ -368,7 +377,7 @@ export class RpcPeer {
368
377
  return ret;
369
378
  }
370
379
 
371
- if (!value || (!value[PROPERTY_JSON_DISABLE_SERIALIZATION] && this.transportSafeArgumentTypes.has(value.constructor?.name))) {
380
+ if (!value || (!value[RpcPeer.PROPERTY_JSON_DISABLE_SERIALIZATION] && this.transportSafeArgumentTypes.has(value.constructor?.name))) {
372
381
  return value;
373
382
  }
374
383
 
@@ -382,8 +391,8 @@ export class RpcPeer {
382
391
  __remote_proxy_id: proxiedEntry.id,
383
392
  __remote_proxy_finalizer_id,
384
393
  __remote_constructor_name,
385
- __remote_proxy_props: value?.[PROPERTY_PROXY_PROPERTIES],
386
- __remote_proxy_oneway_methods: value?.[PROPERTY_PROXY_ONEWAY_METHODS],
394
+ __remote_proxy_props: value?.[RpcPeer.PROPERTY_PROXY_PROPERTIES],
395
+ __remote_proxy_oneway_methods: value?.[RpcPeer.PROPERTY_PROXY_ONEWAY_METHODS],
387
396
  }
388
397
  return ret;
389
398
  }
@@ -400,13 +409,15 @@ export class RpcPeer {
400
409
  if (serializerMapName) {
401
410
  __remote_constructor_name = serializerMapName;
402
411
  const serializer = this.nameDeserializerMap.get(serializerMapName);
412
+ if (!serializer)
413
+ throw new Error('serializer not found for ' + serializerMapName);
403
414
  const serialized = serializer.serialize(value);
404
415
  const ret: RpcRemoteProxyValue = {
405
416
  __remote_proxy_id: undefined,
406
417
  __remote_proxy_finalizer_id: undefined,
407
418
  __remote_constructor_name,
408
- __remote_proxy_props: value?.[PROPERTY_PROXY_PROPERTIES],
409
- __remote_proxy_oneway_methods: value?.[PROPERTY_PROXY_ONEWAY_METHODS],
419
+ __remote_proxy_props: value?.[RpcPeer.PROPERTY_PROXY_PROPERTIES],
420
+ __remote_proxy_oneway_methods: value?.[RpcPeer.PROPERTY_PROXY_ONEWAY_METHODS],
410
421
  __serialized_value: serialized,
411
422
  }
412
423
  return ret;
@@ -424,8 +435,8 @@ export class RpcPeer {
424
435
  __remote_proxy_id,
425
436
  __remote_proxy_finalizer_id: __remote_proxy_id,
426
437
  __remote_constructor_name,
427
- __remote_proxy_props: value?.[PROPERTY_PROXY_PROPERTIES],
428
- __remote_proxy_oneway_methods: value?.[PROPERTY_PROXY_ONEWAY_METHODS],
438
+ __remote_proxy_props: value?.[RpcPeer.PROPERTY_PROXY_PROPERTIES],
439
+ __remote_proxy_oneway_methods: value?.[RpcPeer.PROPERTY_PROXY_ONEWAY_METHODS],
429
440
  }
430
441
 
431
442
  return ret;
@@ -462,7 +473,7 @@ export class RpcPeer {
462
473
  const rpcApply = message as RpcApply;
463
474
  const result: RpcResult = {
464
475
  type: 'result',
465
- id: rpcApply.id,
476
+ id: rpcApply.id || '',
466
477
  };
467
478
 
468
479
  try {
@@ -504,7 +515,7 @@ export class RpcPeer {
504
515
  if (!deferred)
505
516
  throw new Error(`unknown result ${rpcResult.id}`);
506
517
  if (rpcResult.message || rpcResult.stack) {
507
- const e = new RPCResultError(this, rpcResult.message, undefined, {
518
+ const e = new RPCResultError(this, rpcResult.message || 'no message', undefined, {
508
519
  name: rpcResult.result,
509
520
  stack: rpcResult.stack,
510
521
  });
@@ -542,4 +553,19 @@ export class RpcPeer {
542
553
  return;
543
554
  }
544
555
  }
545
- }
556
+ }
557
+
558
+ export function getEvalSource() {
559
+ return `
560
+ (() => {
561
+ ${RpcProxy}
562
+
563
+ ${RpcPeer}
564
+
565
+ return {
566
+ RpcPeer,
567
+ RpcProxy,
568
+ };
569
+ })();
570
+ `;
571
+ }
package/src/runtime.ts CHANGED
@@ -353,11 +353,6 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
353
353
  const existing = this.plugins[pluginId];
354
354
  if (existing) {
355
355
  delete this.plugins[pluginId];
356
-
357
- if (existing.worker === PluginHost.sharedWorker) {
358
- PluginHost.sharedWorkerImmediateRestart = true;
359
- setTimeout(() => PluginHost.sharedWorkerImmediateRestart = false, 10000);
360
- }
361
356
  existing.kill();
362
357
  }
363
358
  }
@@ -496,13 +491,11 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
496
491
  }
497
492
 
498
493
  setupPluginHostAutoRestart(pluginHost: PluginHost) {
499
- const usingSharedWorker = pluginHost.worker === PluginHost.sharedWorker;
500
-
501
494
  pluginHost.worker.once('exit', () => {
502
495
  if (pluginHost.killed)
503
496
  return;
504
497
  pluginHost.kill();
505
- const timeout = usingSharedWorker && PluginHost.sharedWorkerImmediateRestart ? 0 : 60000;
498
+ const timeout = 60000;
506
499
  console.error(`plugin unexpectedly exited, restarting in ${timeout}ms`, pluginHost.pluginId);
507
500
  setTimeout(async () => {
508
501
  const existing = this.plugins[pluginHost.pluginId];
@@ -9,16 +9,24 @@ if (!semver.gte(process.version, '16.0.0')) {
9
9
 
10
10
  startPeriodicGarbageCollection();
11
11
 
12
- process.on('unhandledRejection', error => {
13
- if (error?.constructor !== RPCResultError && error?.constructor !== PluginError) {
14
- throw error;
15
- }
16
- console.warn('unhandled rejection of RPC Result', error);
17
- });
12
+ if (process.argv[2] === 'child' || process.argv[2] === 'child-thread') {
13
+ process.on('uncaughtException', e => {
14
+ console.error('uncaughtException', e);
15
+ });
16
+ process.on('unhandledRejection', e => {
17
+ console.error('unhandledRejection', e);
18
+ });
18
19
 
19
- if (process.argv[2] === 'child') {
20
20
  require('./scrypted-plugin-main');
21
21
  }
22
22
  else {
23
+ process.on('unhandledRejection', error => {
24
+ if (error?.constructor !== RPCResultError && error?.constructor !== PluginError) {
25
+ console.error('wtf', error);
26
+ throw error;
27
+ }
28
+ console.warn('unhandled rejection of RPC Result', error);
29
+ });
30
+
23
31
  require('./scrypted-server-main');
24
32
  }
@@ -1,6 +1,32 @@
1
- import { startPluginRemote, startSharedPluginRemote } from "./plugin/plugin-remote-worker";
1
+ import { startPluginRemote } from "./plugin/plugin-remote-worker";
2
+ import { RpcMessage } from "./rpc";
3
+ import worker_threads from "worker_threads";
4
+ import v8 from 'v8';
2
5
 
3
- if (process.argv[3] !== '@scrypted/shared')
4
- startPluginRemote(process.argv[3]);
5
- else
6
- startSharedPluginRemote();
6
+ if (process.argv[2] === 'child-thread') {
7
+ const peer = startPluginRemote(process.argv[3], (message, reject) => {
8
+ try {
9
+ worker_threads.parentPort.postMessage(v8.serialize(message));
10
+ }
11
+ catch (e) {
12
+ reject?.(e);
13
+ }
14
+ });
15
+ peer.transportSafeArgumentTypes.add(Buffer.name);
16
+ worker_threads.parentPort.on('message', message => peer.handleMessage(v8.deserialize(message)));
17
+ }
18
+ else {
19
+ const peer = startPluginRemote(process.argv[3], (message, reject) => process.send(message, undefined, {
20
+ swallowErrors: !reject,
21
+ }, e => {
22
+ if (e)
23
+ reject?.(e);
24
+ }));
25
+
26
+ peer.transportSafeArgumentTypes.add(Buffer.name);
27
+ process.on('message', message=> peer.handleMessage(message as RpcMessage));
28
+ process.on('disconnect', () => {
29
+ console.error('peer host disconnected, exiting.');
30
+ process.exit(1);
31
+ });
32
+ }
@@ -378,7 +378,7 @@ async function start() {
378
378
  const hash = crypto.createHash('sha256');
379
379
  hash.update(salted);
380
380
  const sha = hash.digest().toString('hex');
381
- if (user.passwordHash !== sha) {
381
+ if (user.passwordHash !== sha && user.token !== password) {
382
382
  res.send({
383
383
  error: 'Incorrect password.',
384
384
  hasLogin,
@@ -0,0 +1,108 @@
1
+ import worker_threads from 'worker_threads';
2
+ import { getEvalSource, RpcPeer } from './rpc';
3
+ import v8 from 'v8';
4
+
5
+ export async function newThread<T>(thread: () => Promise<T>): Promise<T>;
6
+ export async function newThread<V, T>(params: V, thread: (params: V) => Promise<T>): Promise<T>;
7
+
8
+ export async function newThread<T>(...args: any[]): Promise<T> {
9
+ let thread: () => Promise<T> = args[1];
10
+ let params: { [key: string]: any } = {};
11
+ if (thread) {
12
+ params = args[0];
13
+ }
14
+ else {
15
+ thread = args[0];
16
+ }
17
+
18
+ const m = (customRequire: string, RpcPeer: any) => {
19
+ if (customRequire) {
20
+ const g = global as any;
21
+ g[customRequire] = g.require;
22
+ }
23
+ const v8 = global.require('v8');
24
+ const worker_threads = global.require('worker_threads');
25
+ const vm = global.require('vm');
26
+ const mainPeer = new RpcPeer('thread', 'main', (message: any, reject: any) => {
27
+ try {
28
+ worker_threads.parentPort.postMessage(v8.serialize(message));
29
+ }
30
+ catch (e) {
31
+ reject?.(e);
32
+ }
33
+ });
34
+ worker_threads.parentPort.on('message', (message: any) => mainPeer.handleMessage(v8.deserialize(message)));
35
+
36
+ mainPeer.params.eval = async (script: string, paramNames: string[], ...paramValues: any[]) => {
37
+ const f = vm.compileFunction(`return (${script})`, paramNames, {
38
+ filename: 'script.js',
39
+ });
40
+ const params: any = {};
41
+ for (let i = 0; i < paramNames.length; i++) {
42
+ params[paramNames[i]] = paramValues[i];
43
+ }
44
+ const c = await f(...paramValues);
45
+ return await c(params);
46
+ }
47
+ };
48
+ const rpcSource = getEvalSource();
49
+
50
+ let customRequire = params.customRequire || '';
51
+
52
+ const workerSource = `
53
+ const {RpcPeer} = ${rpcSource};
54
+
55
+ (${m})("${customRequire}", RpcPeer)`;
56
+
57
+ const worker = new worker_threads.Worker(workerSource, {
58
+ eval: true,
59
+ });
60
+
61
+ const threadPeer = new RpcPeer('main', 'thread', (message, reject) => {
62
+ try {
63
+ worker.postMessage(v8.serialize(message));
64
+ }
65
+ catch (e) {
66
+ reject?.(e);
67
+ }
68
+ });
69
+ worker.on('message', (message: any) => threadPeer.handleMessage(v8.deserialize(message)));
70
+
71
+ const e = await threadPeer.getParam('eval');
72
+ const paramNames = Object.keys(params);
73
+ const paramValues = Object.values(params);
74
+ try {
75
+ return await e(thread.toString(), paramNames, ...paramValues);
76
+ }
77
+ finally {
78
+ worker.terminate();
79
+ }
80
+ }
81
+
82
+ async function test() {
83
+ const foo = 5;
84
+ const bar = 6;
85
+
86
+ console.log(await newThread({
87
+ foo, bar,
88
+ }, async () => {
89
+ return foo + bar;
90
+ }));
91
+
92
+
93
+ console.log(await newThread({
94
+ foo, bar,
95
+ }, async ({foo,bar}) => {
96
+ return foo + bar;
97
+ }));
98
+
99
+ const sayHelloInMainThread = () => console.log('hello! main thread:', worker_threads.isMainThread);
100
+ await newThread({
101
+ sayHelloInMainThread,
102
+ }, async () => {
103
+ sayHelloInMainThread();
104
+ })
105
+ }
106
+
107
+ // if (true)
108
+ // test();
@@ -1,62 +0,0 @@
1
- {
2
- // Use IntelliSense to learn about possible attributes.
3
- // Hover to view descriptions of existing attributes.
4
- // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5
- "version": "0.2.0",
6
- "configurations": [
7
- {
8
- "autoAttachChildProcesses": false,
9
- "type": "pwa-node",
10
- "request": "launch",
11
- "name": "Launch Program",
12
- "skipFiles": [
13
- "<node_internals>/**"
14
- ],
15
- "preLaunchTask": "npm: build",
16
- "program": "${workspaceFolder}/dist/scrypted-main.js",
17
- "runtimeArgs": [
18
- "--expose-gc",
19
- "--nolazy",
20
- ],
21
- "sourceMaps": true,
22
- "resolveSourceMapLocations": [
23
- "${workspaceFolder}/**",
24
- "!**/node_modules/**"
25
- ],
26
- "outFiles": [
27
- "${workspaceFolder}/**/*.js"
28
- ],
29
- "env": {
30
- // "SCRYPTED_SHARED_WORKER": "true",
31
- "DEBUG": "/scrypted/*",
32
- }
33
- },
34
- {
35
- "autoAttachChildProcesses": false,
36
- "type": "pwa-node",
37
- "request": "launch",
38
- "name": "Launch Program TS Node",
39
- "skipFiles": [
40
- "<node_internals>/**"
41
- ],
42
- "program": "${workspaceFolder}/src/scrypted-main.ts",
43
- "runtimeArgs": [
44
- "--expose-gc",
45
- "--nolazy",
46
- "-r",
47
- "ts-node/register"
48
- ],
49
- "sourceMaps": true,
50
- "resolveSourceMapLocations": [
51
- "${workspaceFolder}/**",
52
- "!**/node_modules/**"
53
- ],
54
- "outFiles": [
55
- "${workspaceFolder}/**/*.js"
56
- ],
57
- "env": {
58
- "DEBUG": "/scrypted/*",
59
- }
60
- },
61
- ]
62
- }
@@ -1,8 +0,0 @@
1
- {
2
- // could be useful for vscode ssh remote debugging. it forwards 10443, etc. to the remote host.
3
- "remote.autoForwardPorts": false,
4
- "python.analysis.extraPaths": [
5
- "./node_modules/@scrypted/types",
6
- "./python",
7
- ]
8
- }