@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.
- package/dist/http-interfaces.js +8 -4
- package/dist/http-interfaces.js.map +1 -1
- package/dist/plugin/media.js +29 -14
- package/dist/plugin/media.js.map +1 -1
- package/dist/plugin/plugin-console.js +13 -3
- package/dist/plugin/plugin-console.js.map +1 -1
- package/dist/plugin/plugin-device.js +1 -1
- package/dist/plugin/plugin-device.js.map +1 -1
- package/dist/plugin/plugin-host-api.js +1 -1
- package/dist/plugin/plugin-host-api.js.map +1 -1
- package/dist/plugin/plugin-host.js +36 -127
- package/dist/plugin/plugin-host.js.map +1 -1
- package/dist/plugin/plugin-remote-worker.js +5 -42
- package/dist/plugin/plugin-remote-worker.js.map +1 -1
- package/dist/plugin/plugin-remote.js +3 -3
- package/dist/plugin/plugin-remote.js.map +1 -1
- package/dist/plugin/runtime/child-process-worker.js +42 -0
- package/dist/plugin/runtime/child-process-worker.js.map +1 -0
- package/dist/plugin/runtime/node-fork-worker.js +51 -0
- package/dist/plugin/runtime/node-fork-worker.js.map +1 -0
- package/dist/plugin/runtime/node-thread-worker.js +73 -0
- package/dist/plugin/runtime/node-thread-worker.js.map +1 -0
- package/dist/plugin/runtime/python-worker.js +54 -0
- package/dist/plugin/runtime/python-worker.js.map +1 -0
- package/dist/plugin/runtime/runtime-worker.js +3 -0
- package/dist/plugin/runtime/runtime-worker.js.map +1 -0
- package/dist/plugin/system.js +3 -3
- package/dist/plugin/system.js.map +1 -1
- package/dist/rpc.js +79 -55
- package/dist/rpc.js.map +1 -1
- package/dist/runtime.js +1 -6
- package/dist/runtime.js.map +1 -1
- package/dist/scrypted-main.js +14 -7
- package/dist/scrypted-main.js.map +1 -1
- package/dist/scrypted-plugin-main.js +31 -4
- package/dist/scrypted-plugin-main.js.map +1 -1
- package/dist/scrypted-server-main.js +1 -1
- package/dist/scrypted-server-main.js.map +1 -1
- package/dist/threading.js +99 -0
- package/dist/threading.js.map +1 -0
- package/package.json +5 -3
- package/python/plugin-remote.py +31 -2
- package/python/rpc.py +6 -0
- package/src/http-interfaces.ts +10 -5
- package/src/plugin/media.ts +34 -16
- package/src/plugin/plugin-console.ts +15 -6
- package/src/plugin/plugin-device.ts +2 -2
- package/src/plugin/plugin-host-api.ts +2 -2
- package/src/plugin/plugin-host.ts +42 -146
- package/src/plugin/plugin-remote-websocket.ts +1 -1
- package/src/plugin/plugin-remote-worker.ts +7 -44
- package/src/plugin/plugin-remote.ts +5 -5
- package/src/plugin/runtime/child-process-worker.ts +49 -0
- package/src/plugin/runtime/node-fork-worker.ts +54 -0
- package/src/plugin/runtime/node-thread-worker.ts +76 -0
- package/src/plugin/runtime/python-worker.ts +67 -0
- package/src/plugin/runtime/runtime-worker.ts +28 -0
- package/src/plugin/system.ts +4 -4
- package/src/rpc.ts +92 -66
- package/src/runtime.ts +1 -8
- package/src/scrypted-main.ts +15 -7
- package/src/scrypted-plugin-main.ts +31 -5
- package/src/scrypted-server-main.ts +1 -1
- package/src/threading.ts +108 -0
- package/.vscode/launch.json +0 -62
- package/.vscode/settings.json +0 -8
package/src/plugin/system.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
21
|
-
global
|
|
22
|
-
|
|
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
|
|
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
|
-
|
|
356
|
-
|
|
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 =
|
|
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];
|
package/src/scrypted-main.ts
CHANGED
|
@@ -9,16 +9,24 @@ if (!semver.gte(process.version, '16.0.0')) {
|
|
|
9
9
|
|
|
10
10
|
startPeriodicGarbageCollection();
|
|
11
11
|
|
|
12
|
-
process.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
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
|
|
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[
|
|
4
|
-
startPluginRemote(process.argv[3])
|
|
5
|
-
|
|
6
|
-
|
|
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,
|
package/src/threading.ts
ADDED
|
@@ -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();
|
package/.vscode/launch.json
DELETED
|
@@ -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
|
-
}
|