@scrypted/server 0.114.0 → 0.115.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fetch/http-fetch.js +6 -27
 - package/dist/fetch/http-fetch.js.map +1 -1
 - package/dist/fetch/index.d.ts +5 -2
 - package/dist/fetch/index.js +6 -26
 - package/dist/fetch/index.js.map +1 -1
 - package/dist/listen-zero.d.ts +2 -2
 - package/dist/listen-zero.js +1 -1
 - package/dist/listen-zero.js.map +1 -1
 - package/dist/plugin/plugin-console.js +2 -2
 - package/dist/plugin/plugin-console.js.map +1 -1
 - package/dist/plugin/plugin-npm-dependencies.js +5 -1
 - package/dist/plugin/plugin-npm-dependencies.js.map +1 -1
 - package/dist/plugin/plugin-remote-worker.js +22 -17
 - package/dist/plugin/plugin-remote-worker.js.map +1 -1
 - package/dist/plugin/plugin-remote.js +0 -1
 - package/dist/plugin/plugin-remote.js.map +1 -1
 - package/dist/plugin/plugin-repl.js +1 -1
 - package/dist/plugin/plugin-repl.js.map +1 -1
 - package/dist/rpc-peer-eval.d.ts +4 -0
 - package/dist/rpc-peer-eval.js +25 -0
 - package/dist/rpc-peer-eval.js.map +1 -0
 - package/dist/rpc-serializer.js.map +1 -1
 - package/dist/rpc.d.ts +4 -4
 - package/dist/rpc.js +6 -23
 - package/dist/rpc.js.map +1 -1
 - package/dist/runtime.js +7 -5
 - package/dist/runtime.js.map +1 -1
 - package/dist/scrypted-server-main.js +14 -0
 - package/dist/scrypted-server-main.js.map +1 -1
 - package/package.json +12 -12
 - package/python/plugin_remote.py +39 -11
 - package/python/rpc.py +10 -10
 - package/src/fetch/http-fetch.ts +7 -32
 - package/src/fetch/index.ts +10 -33
 - package/src/listen-zero.ts +3 -3
 - package/src/plugin/plugin-console.ts +2 -2
 - package/src/plugin/plugin-npm-dependencies.ts +5 -1
 - package/src/plugin/plugin-remote-worker.ts +25 -17
 - package/src/plugin/plugin-remote.ts +1 -2
 - package/src/plugin/plugin-repl.ts +1 -1
 - package/src/rpc-peer-eval.ts +27 -0
 - package/src/rpc-serializer.ts +2 -2
 - package/src/rpc.ts +16 -32
 - package/src/runtime.ts +9 -5
 - package/src/scrypted-server-main.ts +16 -0
 
    
        package/package.json
    CHANGED
    
    | 
         @@ -1,17 +1,17 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            {
         
     | 
| 
       2 
2 
     | 
    
         
             
              "name": "@scrypted/server",
         
     | 
| 
       3 
     | 
    
         
            -
              "version": "0. 
     | 
| 
      
 3 
     | 
    
         
            +
              "version": "0.115.1",
         
     | 
| 
       4 
4 
     | 
    
         
             
              "description": "",
         
     | 
| 
       5 
5 
     | 
    
         
             
              "dependencies": {
         
     | 
| 
       6 
6 
     | 
    
         
             
                "@mapbox/node-pre-gyp": "^1.0.11",
         
     | 
| 
       7 
7 
     | 
    
         
             
                "@scrypted/ffmpeg-static": "^6.1.0-build1",
         
     | 
| 
       8 
     | 
    
         
            -
                "@scrypted/node-pty": "^1.0. 
     | 
| 
       9 
     | 
    
         
            -
                "@scrypted/types": "^0.3. 
     | 
| 
      
 8 
     | 
    
         
            +
                "@scrypted/node-pty": "^1.0.18",
         
     | 
| 
      
 9 
     | 
    
         
            +
                "@scrypted/types": "^0.3.43",
         
     | 
| 
       10 
10 
     | 
    
         
             
                "adm-zip": "^0.5.14",
         
     | 
| 
       11 
11 
     | 
    
         
             
                "body-parser": "^1.20.2",
         
     | 
| 
       12 
12 
     | 
    
         
             
                "cookie-parser": "^1.4.6",
         
     | 
| 
       13 
13 
     | 
    
         
             
                "dotenv": "^16.4.5",
         
     | 
| 
       14 
     | 
    
         
            -
                "engine.io": "^6. 
     | 
| 
      
 14 
     | 
    
         
            +
                "engine.io": "^6.6.0",
         
     | 
| 
       15 
15 
     | 
    
         
             
                "express": "^4.19.2",
         
     | 
| 
       16 
16 
     | 
    
         
             
                "follow-redirects": "^1.15.6",
         
     | 
| 
       17 
17 
     | 
    
         
             
                "http-auth": "^4.2.0",
         
     | 
| 
         @@ -21,17 +21,17 @@ 
     | 
|
| 
       21 
21 
     | 
    
         
             
                "nan": "^2.20.0",
         
     | 
| 
       22 
22 
     | 
    
         
             
                "node-dijkstra": "^2.5.0",
         
     | 
| 
       23 
23 
     | 
    
         
             
                "node-forge": "^1.3.1",
         
     | 
| 
       24 
     | 
    
         
            -
                "node-gyp": "^10. 
     | 
| 
       25 
     | 
    
         
            -
                "py": "npm:@bjia56/portable-python@^0.1. 
     | 
| 
      
 24 
     | 
    
         
            +
                "node-gyp": "^10.2.0",
         
     | 
| 
      
 25 
     | 
    
         
            +
                "py": "npm:@bjia56/portable-python@^0.1.54",
         
     | 
| 
       26 
26 
     | 
    
         
             
                "router": "^1.3.8",
         
     | 
| 
       27 
     | 
    
         
            -
                "semver": "^7.6. 
     | 
| 
      
 27 
     | 
    
         
            +
                "semver": "^7.6.3",
         
     | 
| 
       28 
28 
     | 
    
         
             
                "sharp": "^0.33.4",
         
     | 
| 
       29 
29 
     | 
    
         
             
                "source-map-support": "^0.5.21",
         
     | 
| 
       30 
     | 
    
         
            -
                "tar": "^7.4. 
     | 
| 
      
 30 
     | 
    
         
            +
                "tar": "^7.4.3",
         
     | 
| 
       31 
31 
     | 
    
         
             
                "tslib": "^2.6.3",
         
     | 
| 
       32 
     | 
    
         
            -
                "typescript": "^5.5. 
     | 
| 
      
 32 
     | 
    
         
            +
                "typescript": "^5.5.4",
         
     | 
| 
       33 
33 
     | 
    
         
             
                "whatwg-mimetype": "^4.0.0",
         
     | 
| 
       34 
     | 
    
         
            -
                "ws": "^8. 
     | 
| 
      
 34 
     | 
    
         
            +
                "ws": "^8.18.0"
         
     | 
| 
       35 
35 
     | 
    
         
             
              },
         
     | 
| 
       36 
36 
     | 
    
         
             
              "devDependencies": {
         
     | 
| 
       37 
37 
     | 
    
         
             
                "@types/adm-zip": "^0.5.5",
         
     | 
| 
         @@ -40,13 +40,13 @@ 
     | 
|
| 
       40 
40 
     | 
    
         
             
                "@types/follow-redirects": "^1.14.4",
         
     | 
| 
       41 
41 
     | 
    
         
             
                "@types/http-auth": "^4.1.4",
         
     | 
| 
       42 
42 
     | 
    
         
             
                "@types/ip": "^1.1.3",
         
     | 
| 
       43 
     | 
    
         
            -
                "@types/lodash": "^4.17. 
     | 
| 
      
 43 
     | 
    
         
            +
                "@types/lodash": "^4.17.7",
         
     | 
| 
       44 
44 
     | 
    
         
             
                "@types/node-dijkstra": "^2.5.6",
         
     | 
| 
       45 
45 
     | 
    
         
             
                "@types/node-forge": "^1.3.11",
         
     | 
| 
       46 
46 
     | 
    
         
             
                "@types/semver": "^7.5.8",
         
     | 
| 
       47 
47 
     | 
    
         
             
                "@types/source-map-support": "^0.5.10",
         
     | 
| 
       48 
48 
     | 
    
         
             
                "@types/whatwg-mimetype": "^3.0.2",
         
     | 
| 
       49 
     | 
    
         
            -
                "@types/ws": "^8.5. 
     | 
| 
      
 49 
     | 
    
         
            +
                "@types/ws": "^8.5.11"
         
     | 
| 
       50 
50 
     | 
    
         
             
              },
         
     | 
| 
       51 
51 
     | 
    
         
             
              "bin": {
         
     | 
| 
       52 
52 
     | 
    
         
             
                "scrypted-serve": "bin/scrypted-serve"
         
     | 
    
        package/python/plugin_remote.py
    CHANGED
    
    | 
         @@ -29,6 +29,7 @@ from plugin_pip import install_with_pip, need_requirements, remove_pip_dirs 
     | 
|
| 
       29 
29 
     | 
    
         
             
            from scrypted_python.scrypted_sdk import PluginFork, ScryptedStatic
         
     | 
| 
       30 
30 
     | 
    
         
             
            from scrypted_python.scrypted_sdk.types import (Device, DeviceManifest,
         
     | 
| 
       31 
31 
     | 
    
         
             
                                                            EventDetails,
         
     | 
| 
      
 32 
     | 
    
         
            +
                                                            ScryptedInterface,
         
     | 
| 
       32 
33 
     | 
    
         
             
                                                            ScryptedInterfaceMethods,
         
     | 
| 
       33 
34 
     | 
    
         
             
                                                            ScryptedInterfaceProperty,
         
     | 
| 
       34 
35 
     | 
    
         
             
                                                            Storage)
         
     | 
| 
         @@ -139,6 +140,19 @@ class SystemManager(scrypted_python.scrypted_sdk.types.SystemManager): 
     | 
|
| 
       139 
140 
     | 
    
         
             
                        state = self.systemState.get(check, None)
         
     | 
| 
       140 
141 
     | 
    
         
             
                        if not state:
         
     | 
| 
       141 
142 
     | 
    
         
             
                            continue
         
     | 
| 
      
 143 
     | 
    
         
            +
                        checkInterfaces = state.get('interfaces', None)
         
     | 
| 
      
 144 
     | 
    
         
            +
                        if not checkInterfaces:
         
     | 
| 
      
 145 
     | 
    
         
            +
                            continue
         
     | 
| 
      
 146 
     | 
    
         
            +
                        interfaces = checkInterfaces.get('value', [])
         
     | 
| 
      
 147 
     | 
    
         
            +
                        if ScryptedInterface.ScryptedPlugin.value in interfaces:
         
     | 
| 
      
 148 
     | 
    
         
            +
                            checkPluginId = state.get('pluginId', None)
         
     | 
| 
      
 149 
     | 
    
         
            +
                            if not checkPluginId:
         
     | 
| 
      
 150 
     | 
    
         
            +
                                continue
         
     | 
| 
      
 151 
     | 
    
         
            +
                            pluginId = checkPluginId.get('value', None)
         
     | 
| 
      
 152 
     | 
    
         
            +
                            if not pluginId:
         
     | 
| 
      
 153 
     | 
    
         
            +
                                continue
         
     | 
| 
      
 154 
     | 
    
         
            +
                            if pluginId == name:
         
     | 
| 
      
 155 
     | 
    
         
            +
                                return self.getDeviceById(check)
         
     | 
| 
       142 
156 
     | 
    
         
             
                        checkName = state.get('name', None)
         
     | 
| 
       143 
157 
     | 
    
         
             
                        if not checkName:
         
     | 
| 
       144 
158 
     | 
    
         
             
                            continue
         
     | 
| 
         @@ -260,6 +274,13 @@ class DeviceState(scrypted_python.scrypted_sdk.types.DeviceState): 
     | 
|
| 
       260 
274 
     | 
    
         
             
                    self.systemManager.api.setState(self.nativeId, property, value)
         
     | 
| 
       261 
275 
     | 
    
         | 
| 
       262 
276 
     | 
    
         | 
| 
      
 277 
     | 
    
         
            +
            class WritableDeviceState(scrypted_python.scrypted_sdk.types.WritableDeviceState):
         
     | 
| 
      
 278 
     | 
    
         
            +
             
     | 
| 
      
 279 
     | 
    
         
            +
                def __init__(self, id, setState) -> None:
         
     | 
| 
      
 280 
     | 
    
         
            +
                    self.id = id
         
     | 
| 
      
 281 
     | 
    
         
            +
                    self.setState = setState
         
     | 
| 
      
 282 
     | 
    
         
            +
             
     | 
| 
      
 283 
     | 
    
         
            +
             
     | 
| 
       263 
284 
     | 
    
         
             
            class DeviceStorage(Storage):
         
     | 
| 
       264 
285 
     | 
    
         
             
                id: str
         
     | 
| 
       265 
286 
     | 
    
         
             
                nativeId: str
         
     | 
| 
         @@ -390,12 +411,17 @@ class PluginRemote: 
     | 
|
| 
       390 
411 
     | 
    
         | 
| 
       391 
412 
     | 
    
         
             
                    def computeClusterObjectHash(o: ClusterObject) -> str:
         
     | 
| 
       392 
413 
     | 
    
         
             
                        m = hashlib.sha256()
         
     | 
| 
       393 
     | 
    
         
            -
                        m.update(bytes(f"{o['id']}{o['port']}{o.get('sourcePort' 
     | 
| 
      
 414 
     | 
    
         
            +
                        m.update(bytes(f"{o['id']}{o['port']}{o.get('sourcePort') or ''}{o['proxyId']}{clusterSecret}", 'utf8'))
         
     | 
| 
       394 
415 
     | 
    
         
             
                        return base64.b64encode(m.digest()).decode('utf-8')
         
     | 
| 
       395 
416 
     | 
    
         | 
| 
       396 
     | 
    
         
            -
                    def onProxySerialization(value: Any,  
     | 
| 
      
 417 
     | 
    
         
            +
                    def onProxySerialization(value: Any, sourcePeerPort: int = None):
         
     | 
| 
       397 
418 
     | 
    
         
             
                        properties: dict = rpc.RpcPeer.prepareProxyProperties(value) or {}
         
     | 
| 
       398 
419 
     | 
    
         
             
                        clusterEntry = properties.get('__cluster', None)
         
     | 
| 
      
 420 
     | 
    
         
            +
                        proxyId: str = (clusterEntry and clusterEntry.get('proxyId', None)) or rpc.RpcPeer.generateId()
         
     | 
| 
      
 421 
     | 
    
         
            +
             
     | 
| 
      
 422 
     | 
    
         
            +
                        if clusterEntry and clusterPort == clusterEntry['port'] and sourcePeerPort != clusterEntry.get('sourcePort', None):
         
     | 
| 
      
 423 
     | 
    
         
            +
                            clusterEntry = None
         
     | 
| 
      
 424 
     | 
    
         
            +
             
     | 
| 
       399 
425 
     | 
    
         
             
                        if not properties.get('__cluster', None):
         
     | 
| 
       400 
426 
     | 
    
         
             
                            clusterEntry: ClusterObject = {
         
     | 
| 
       401 
427 
     | 
    
         
             
                                'id': clusterId,
         
     | 
| 
         @@ -406,7 +432,7 @@ class PluginRemote: 
     | 
|
| 
       406 
432 
     | 
    
         
             
                            clusterEntry['sha256'] = computeClusterObjectHash(clusterEntry)
         
     | 
| 
       407 
433 
     | 
    
         
             
                            properties['__cluster'] = clusterEntry
         
     | 
| 
       408 
434 
     | 
    
         | 
| 
       409 
     | 
    
         
            -
                        return properties
         
     | 
| 
      
 435 
     | 
    
         
            +
                        return proxyId, properties
         
     | 
| 
       410 
436 
     | 
    
         | 
| 
       411 
437 
     | 
    
         
             
                    self.peer.onProxySerialization = onProxySerialization
         
     | 
| 
       412 
438 
     | 
    
         | 
| 
         @@ -423,8 +449,8 @@ class PluginRemote: 
     | 
|
| 
       423 
449 
     | 
    
         
             
                        rpcTransport = rpc_reader.RpcStreamTransport(reader, writer)
         
     | 
| 
       424 
450 
     | 
    
         
             
                        peer: rpc.RpcPeer
         
     | 
| 
       425 
451 
     | 
    
         
             
                        peer, peerReadLoop = await rpc_reader.prepare_peer_readloop(self.loop, rpcTransport)
         
     | 
| 
       426 
     | 
    
         
            -
                        peer.onProxySerialization = lambda value 
     | 
| 
       427 
     | 
    
         
            -
                            value,  
     | 
| 
      
 452 
     | 
    
         
            +
                        peer.onProxySerialization = lambda value: onProxySerialization(
         
     | 
| 
      
 453 
     | 
    
         
            +
                            value, clusterPeerPort)
         
     | 
| 
       428 
454 
     | 
    
         
             
                        future: asyncio.Future[rpc.RpcPeer] = asyncio.Future()
         
     | 
| 
       429 
455 
     | 
    
         
             
                        future.set_result(peer)
         
     | 
| 
       430 
456 
     | 
    
         
             
                        clusterPeers[clusterPeerPort] = future
         
     | 
| 
         @@ -458,9 +484,8 @@ class PluginRemote: 
     | 
|
| 
       458 
484 
     | 
    
         
             
                                rpcTransport = rpc_reader.RpcStreamTransport(
         
     | 
| 
       459 
485 
     | 
    
         
             
                                    reader, writer)
         
     | 
| 
       460 
486 
     | 
    
         
             
                                clusterPeer, peerReadLoop = await rpc_reader.prepare_peer_readloop(self.loop, rpcTransport)
         
     | 
| 
       461 
     | 
    
         
            -
                                clusterPeer. 
     | 
| 
       462 
     | 
    
         
            -
             
     | 
| 
       463 
     | 
    
         
            -
                                    value, proxyId, clusterPeerPort)
         
     | 
| 
      
 487 
     | 
    
         
            +
                                clusterPeer.onProxySerialization = lambda value: onProxySerialization(
         
     | 
| 
      
 488 
     | 
    
         
            +
                                    value, clusterPeerPort)
         
     | 
| 
       464 
489 
     | 
    
         | 
| 
       465 
490 
     | 
    
         
             
                                async def run_loop():
         
     | 
| 
       466 
491 
     | 
    
         
             
                                    try:
         
     | 
| 
         @@ -494,8 +519,11 @@ class PluginRemote: 
     | 
|
| 
       494 
519 
     | 
    
         | 
| 
       495 
520 
     | 
    
         
             
                        try:
         
     | 
| 
       496 
521 
     | 
    
         
             
                            clusterPeer = await clusterPeerPromise
         
     | 
| 
       497 
     | 
    
         
            -
                             
     | 
| 
       498 
     | 
    
         
            -
             
     | 
| 
      
 522 
     | 
    
         
            +
                            weakref = clusterPeer.remoteWeakProxies.get(proxyId, None)
         
     | 
| 
      
 523 
     | 
    
         
            +
                            existing = weakref() if weakref else None
         
     | 
| 
      
 524 
     | 
    
         
            +
                            if existing:
         
     | 
| 
      
 525 
     | 
    
         
            +
                                return existing
         
     | 
| 
      
 526 
     | 
    
         
            +
             
     | 
| 
       499 
527 
     | 
    
         
             
                            peerConnectRPCObject = clusterPeer.tags.get('connectRPCObject')
         
     | 
| 
       500 
528 
     | 
    
         
             
                            if not peerConnectRPCObject:
         
     | 
| 
       501 
529 
     | 
    
         
             
                                peerConnectRPCObject = await clusterPeer.getParam('connectRPCObject')
         
     | 
| 
         @@ -743,7 +771,7 @@ class PluginRemote: 
     | 
|
| 
       743 
771 
     | 
    
         
             
                    pass
         
     | 
| 
       744 
772 
     | 
    
         | 
| 
       745 
773 
     | 
    
         
             
                async def createDeviceState(self, id, setState):
         
     | 
| 
       746 
     | 
    
         
            -
                     
     | 
| 
      
 774 
     | 
    
         
            +
                    return WritableDeviceState(id, setState)
         
     | 
| 
       747 
775 
     | 
    
         | 
| 
       748 
776 
     | 
    
         
             
                async def getServicePort(self, name):
         
     | 
| 
       749 
777 
     | 
    
         
             
                    if name == "repl":
         
     | 
    
        package/python/rpc.py
    CHANGED
    
    | 
         @@ -126,7 +126,7 @@ class RpcPeer: 
     | 
|
| 
       126 
126 
     | 
    
         
             
                    self.pendingResults: Mapping[str, Future] = {}
         
     | 
| 
       127 
127 
     | 
    
         
             
                    self.remoteWeakProxies: Mapping[str, any] = {}
         
     | 
| 
       128 
128 
     | 
    
         
             
                    self.nameDeserializerMap: Mapping[str, RpcSerializer] = {}
         
     | 
| 
       129 
     | 
    
         
            -
                    self.onProxySerialization: Callable[[Any, str], Any] = None
         
     | 
| 
      
 129 
     | 
    
         
            +
                    self.onProxySerialization: Callable[[Any, str], tuple[str, Any]] = None
         
     | 
| 
       130 
130 
     | 
    
         
             
                    self.killed = False
         
     | 
| 
       131 
131 
     | 
    
         
             
                    self.tags = {}
         
     | 
| 
       132 
132 
     | 
    
         | 
| 
         @@ -274,7 +274,7 @@ class RpcPeer: 
     | 
|
| 
       274 
274 
     | 
    
         | 
| 
       275 
275 
     | 
    
         
             
                    proxiedEntry = self.localProxied.get(value, None)
         
     | 
| 
       276 
276 
     | 
    
         
             
                    if proxiedEntry:
         
     | 
| 
       277 
     | 
    
         
            -
                        proxiedEntry['finalizerId'] =  
     | 
| 
      
 277 
     | 
    
         
            +
                        proxiedEntry['finalizerId'] = RpcPeer.generateId()
         
     | 
| 
       278 
278 
     | 
    
         
             
                        ret = {
         
     | 
| 
       279 
279 
     | 
    
         
             
                            '__remote_proxy_id': proxiedEntry['id'],
         
     | 
| 
       280 
280 
     | 
    
         
             
                            '__remote_proxy_finalizer_id': proxiedEntry['finalizerId'],
         
     | 
| 
         @@ -292,7 +292,12 @@ class RpcPeer: 
     | 
|
| 
       292 
292 
     | 
    
         
             
                        }
         
     | 
| 
       293 
293 
     | 
    
         
             
                        return ret
         
     | 
| 
       294 
294 
     | 
    
         | 
| 
       295 
     | 
    
         
            -
                     
     | 
| 
      
 295 
     | 
    
         
            +
                    if self.onProxySerialization:
         
     | 
| 
      
 296 
     | 
    
         
            +
                        proxyId, __remote_proxy_props = self.onProxySerialization(value)
         
     | 
| 
      
 297 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 298 
     | 
    
         
            +
                        __remote_proxy_props = RpcPeer.prepareProxyProperties(value)
         
     | 
| 
      
 299 
     | 
    
         
            +
                        proxyId = RpcPeer.generateId()
         
     | 
| 
      
 300 
     | 
    
         
            +
             
     | 
| 
       296 
301 
     | 
    
         
             
                    proxiedEntry = {
         
     | 
| 
       297 
302 
     | 
    
         
             
                        'id': proxyId,
         
     | 
| 
       298 
303 
     | 
    
         
             
                        'finalizerId': proxyId,
         
     | 
| 
         @@ -300,11 +305,6 @@ class RpcPeer: 
     | 
|
| 
       300 
305 
     | 
    
         
             
                    self.localProxied[value] = proxiedEntry
         
     | 
| 
       301 
306 
     | 
    
         
             
                    self.localProxyMap[proxyId] = value
         
     | 
| 
       302 
307 
     | 
    
         | 
| 
       303 
     | 
    
         
            -
                    if self.onProxySerialization:
         
     | 
| 
       304 
     | 
    
         
            -
                        __remote_proxy_props = self.onProxySerialization(value, proxyId)
         
     | 
| 
       305 
     | 
    
         
            -
                    else:
         
     | 
| 
       306 
     | 
    
         
            -
                        __remote_proxy_props = RpcPeer.prepareProxyProperties(value)
         
     | 
| 
       307 
     | 
    
         
            -
             
     | 
| 
       308 
308 
     | 
    
         
             
                    ret = {
         
     | 
| 
       309 
309 
     | 
    
         
             
                        '__remote_proxy_id': proxyId,
         
     | 
| 
       310 
310 
     | 
    
         
             
                        '__remote_proxy_finalizer_id': proxyId,
         
     | 
| 
         @@ -491,7 +491,7 @@ class RpcPeer: 
     | 
|
| 
       491 
491 
     | 
    
         | 
| 
       492 
492 
     | 
    
         
             
                randomDigits = string.ascii_uppercase + string.ascii_lowercase + string.digits
         
     | 
| 
       493 
493 
     | 
    
         | 
| 
       494 
     | 
    
         
            -
                def generateId( 
     | 
| 
      
 494 
     | 
    
         
            +
                def generateId():
         
     | 
| 
       495 
495 
     | 
    
         
             
                    return ''.join(random.choices(RpcPeer.randomDigits, k=8))
         
     | 
| 
       496 
496 
     | 
    
         | 
| 
       497 
497 
     | 
    
         
             
                async def createPendingResult(self, cb: Callable[[str, Callable[[Exception], None]], None]):
         
     | 
| 
         @@ -500,7 +500,7 @@ class RpcPeer: 
     | 
|
| 
       500 
500 
     | 
    
         
             
                        future.set_exception(RPCResultError(None, 'RpcPeer has been killed (createPendingResult)'))
         
     | 
| 
       501 
501 
     | 
    
         
             
                        return future
         
     | 
| 
       502 
502 
     | 
    
         | 
| 
       503 
     | 
    
         
            -
                    id =  
     | 
| 
      
 503 
     | 
    
         
            +
                    id = RpcPeer.generateId()
         
     | 
| 
       504 
504 
     | 
    
         
             
                    self.pendingResults[id] = future
         
     | 
| 
       505 
505 
     | 
    
         
             
                    await cb(id, lambda e: future.set_exception(RPCResultError(e, None)))
         
     | 
| 
       506 
506 
     | 
    
         
             
                    return await future
         
     | 
    
        package/src/fetch/http-fetch.ts
    CHANGED
    
    | 
         @@ -89,11 +89,11 @@ export async function httpFetch<T extends HttpFetchOptions<Readable>>(options: T 
     | 
|
| 
       89 
89 
     | 
    
         
             
                    controller = new AbortController();
         
     | 
| 
       90 
90 
     | 
    
         
             
                    timeout = setTimeout(() => controller.abort(), options.timeout);
         
     | 
| 
       91 
91 
     | 
    
         | 
| 
       92 
     | 
    
         
            -
                    options.signal?.addEventListener('abort', () => controller.abort( 
     | 
| 
      
 92 
     | 
    
         
            +
                    options.signal?.addEventListener('abort', () => controller.abort(options.signal?.reason));
         
     | 
| 
       93 
93 
     | 
    
         
             
                }
         
     | 
| 
       94 
94 
     | 
    
         | 
| 
       95 
95 
     | 
    
         
             
                const signal = controller?.signal || options.signal;
         
     | 
| 
       96 
     | 
    
         
            -
                signal?.addEventListener('abort', () => request.destroy(new Error('abort')));
         
     | 
| 
      
 96 
     | 
    
         
            +
                signal?.addEventListener('abort', () => request.destroy(new Error(options.signal?.reason || 'abort')));
         
     | 
| 
       97 
97 
     | 
    
         | 
| 
       98 
98 
     | 
    
         
             
                const nodeHeaders: Record<string, string[]> = {};
         
     | 
| 
       99 
99 
     | 
    
         
             
                for (const [k, v] of headers) {
         
     | 
| 
         @@ -122,9 +122,12 @@ export async function httpFetch<T extends HttpFetchOptions<Readable>>(options: T 
     | 
|
| 
       122 
122 
     | 
    
         
             
                try {
         
     | 
| 
       123 
123 
     | 
    
         
             
                    const [response] = await once(request, 'response') as [IncomingMessage];
         
     | 
| 
       124 
124 
     | 
    
         | 
| 
       125 
     | 
    
         
            -
             
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                    if (options?.checkStatusCode === undefined || options?.checkStatusCode) {
         
     | 
| 
       126 
127 
     | 
    
         
             
                        try {
         
     | 
| 
       127 
     | 
    
         
            -
                            checkStatus 
     | 
| 
      
 128 
     | 
    
         
            +
                            const checker = typeof options?.checkStatusCode === 'function' ? options.checkStatusCode : checkStatus;
         
     | 
| 
      
 129 
     | 
    
         
            +
                            if (!checker(response.statusCode))
         
     | 
| 
      
 130 
     | 
    
         
            +
                                throw new Error(`http response statusCode ${response.statusCode}`);
         
     | 
| 
       128 
131 
     | 
    
         
             
                        }
         
     | 
| 
       129 
132 
     | 
    
         
             
                        catch (e) {
         
     | 
| 
       130 
133 
     | 
    
         
             
                            readMessageBuffer(response).catch(() => { });
         
     | 
| 
         @@ -150,31 +153,3 @@ export async function httpFetch<T extends HttpFetchOptions<Readable>>(options: T 
     | 
|
| 
       150 
153 
     | 
    
         
             
                }
         
     | 
| 
       151 
154 
     | 
    
         
             
            }
         
     | 
| 
       152 
155 
     | 
    
         | 
| 
       153 
     | 
    
         
            -
            function ensureType<T>(v: T) {
         
     | 
| 
       154 
     | 
    
         
            -
            }
         
     | 
| 
       155 
     | 
    
         
            -
             
     | 
| 
       156 
     | 
    
         
            -
            async function test() {
         
     | 
| 
       157 
     | 
    
         
            -
                const a = await httpFetch({
         
     | 
| 
       158 
     | 
    
         
            -
                    url: 'http://example.com',
         
     | 
| 
       159 
     | 
    
         
            -
                });
         
     | 
| 
       160 
     | 
    
         
            -
             
     | 
| 
       161 
     | 
    
         
            -
                ensureType<Buffer>(a.body);
         
     | 
| 
       162 
     | 
    
         
            -
             
     | 
| 
       163 
     | 
    
         
            -
                const b = await httpFetch({
         
     | 
| 
       164 
     | 
    
         
            -
                    url: 'http://example.com',
         
     | 
| 
       165 
     | 
    
         
            -
                    responseType: 'json',
         
     | 
| 
       166 
     | 
    
         
            -
                });
         
     | 
| 
       167 
     | 
    
         
            -
                ensureType<any>(b.body);
         
     | 
| 
       168 
     | 
    
         
            -
             
     | 
| 
       169 
     | 
    
         
            -
                const c = await httpFetch({
         
     | 
| 
       170 
     | 
    
         
            -
                    url: 'http://example.com',
         
     | 
| 
       171 
     | 
    
         
            -
                    responseType: 'readable',
         
     | 
| 
       172 
     | 
    
         
            -
                });
         
     | 
| 
       173 
     | 
    
         
            -
                ensureType<IncomingMessage>(c.body);
         
     | 
| 
       174 
     | 
    
         
            -
             
     | 
| 
       175 
     | 
    
         
            -
                const d = await httpFetch({
         
     | 
| 
       176 
     | 
    
         
            -
                    url: 'http://example.com',
         
     | 
| 
       177 
     | 
    
         
            -
                    responseType: 'buffer',
         
     | 
| 
       178 
     | 
    
         
            -
                });
         
     | 
| 
       179 
     | 
    
         
            -
                ensureType<Buffer>(d.body);
         
     | 
| 
       180 
     | 
    
         
            -
            }
         
     | 
    
        package/src/fetch/index.ts
    CHANGED
    
    | 
         @@ -7,7 +7,10 @@ export interface HttpFetchOptionsBase<B> { 
     | 
|
| 
       7 
7 
     | 
    
         
             
                signal?: AbortSignal,
         
     | 
| 
       8 
8 
     | 
    
         
             
                timeout?: number;
         
     | 
| 
       9 
9 
     | 
    
         
             
                rejectUnauthorized?: boolean;
         
     | 
| 
       10 
     | 
    
         
            -
                 
     | 
| 
      
 10 
     | 
    
         
            +
                /**
         
     | 
| 
      
 11 
     | 
    
         
            +
                 * Checks the status code. Defaults to true.
         
     | 
| 
      
 12 
     | 
    
         
            +
                 */
         
     | 
| 
      
 13 
     | 
    
         
            +
                checkStatusCode?: boolean | ((statusCode: number) => boolean);
         
     | 
| 
       11 
14 
     | 
    
         
             
                body?: B | string | ArrayBufferView | any;
         
     | 
| 
       12 
15 
     | 
    
         
             
                withCredentials?: boolean;
         
     | 
| 
       13 
16 
     | 
    
         
             
            }
         
     | 
| 
         @@ -40,6 +43,7 @@ export function fetchStatusCodeOk(statusCode: number) { 
     | 
|
| 
       40 
43 
     | 
    
         
             
            export function checkStatus(statusCode: number) {
         
     | 
| 
       41 
44 
     | 
    
         
             
                if (!fetchStatusCodeOk(statusCode))
         
     | 
| 
       42 
45 
     | 
    
         
             
                    throw new Error(`http response statusCode ${statusCode}`);
         
     | 
| 
      
 46 
     | 
    
         
            +
                return true;
         
     | 
| 
       43 
47 
     | 
    
         
             
            }
         
     | 
| 
       44 
48 
     | 
    
         | 
| 
       45 
49 
     | 
    
         
             
            export function getFetchMethod(options: HttpFetchOptions<any>) {
         
     | 
| 
         @@ -177,7 +181,7 @@ export async function domFetch<T extends HttpFetchOptions<BodyInit>>(options: T) 
     | 
|
| 
       177 
181 
     | 
    
         
             
                    controller = new AbortController();
         
     | 
| 
       178 
182 
     | 
    
         
             
                    timeout = setTimeout(() => controller.abort(), options.timeout);
         
     | 
| 
       179 
183 
     | 
    
         | 
| 
       180 
     | 
    
         
            -
                    options.signal?.addEventListener('abort', () => controller.abort( 
     | 
| 
      
 184 
     | 
    
         
            +
                    options.signal?.addEventListener('abort', () => controller.abort(options.signal?.reason));
         
     | 
| 
       181 
185 
     | 
    
         
             
                }
         
     | 
| 
       182 
186 
     | 
    
         | 
| 
       183 
187 
     | 
    
         
             
                try {
         
     | 
| 
         @@ -190,9 +194,11 @@ export async function domFetch<T extends HttpFetchOptions<BodyInit>>(options: T) 
     | 
|
| 
       190 
194 
     | 
    
         
             
                        body,
         
     | 
| 
       191 
195 
     | 
    
         
             
                    });
         
     | 
| 
       192 
196 
     | 
    
         | 
| 
       193 
     | 
    
         
            -
                    if ( 
     | 
| 
      
 197 
     | 
    
         
            +
                    if (options?.checkStatusCode === undefined || options?.checkStatusCode) {
         
     | 
| 
       194 
198 
     | 
    
         
             
                        try {
         
     | 
| 
       195 
     | 
    
         
            -
                            checkStatus 
     | 
| 
      
 199 
     | 
    
         
            +
                            const checker = typeof options?.checkStatusCode === 'function' ? options.checkStatusCode : checkStatus;
         
     | 
| 
      
 200 
     | 
    
         
            +
                            if (!checker(response.status))
         
     | 
| 
      
 201 
     | 
    
         
            +
                                throw new Error(`http response statusCode ${response.status}`);
         
     | 
| 
       196 
202 
     | 
    
         
             
                        }
         
     | 
| 
       197 
203 
     | 
    
         
             
                        catch (e) {
         
     | 
| 
       198 
204 
     | 
    
         
             
                            response.arrayBuffer().catch(() => { });
         
     | 
| 
         @@ -210,32 +216,3 @@ export async function domFetch<T extends HttpFetchOptions<BodyInit>>(options: T) 
     | 
|
| 
       210 
216 
     | 
    
         
             
                    clearTimeout(timeout);
         
     | 
| 
       211 
217 
     | 
    
         
             
                }
         
     | 
| 
       212 
218 
     | 
    
         
             
            }
         
     | 
| 
       213 
     | 
    
         
            -
             
     | 
| 
       214 
     | 
    
         
            -
            function ensureType<T>(v: T) {
         
     | 
| 
       215 
     | 
    
         
            -
            }
         
     | 
| 
       216 
     | 
    
         
            -
             
     | 
| 
       217 
     | 
    
         
            -
            async function test() {
         
     | 
| 
       218 
     | 
    
         
            -
                const a = await domFetch({
         
     | 
| 
       219 
     | 
    
         
            -
                    url: 'http://example.com',
         
     | 
| 
       220 
     | 
    
         
            -
                });
         
     | 
| 
       221 
     | 
    
         
            -
             
     | 
| 
       222 
     | 
    
         
            -
                ensureType<Buffer>(a.body);
         
     | 
| 
       223 
     | 
    
         
            -
             
     | 
| 
       224 
     | 
    
         
            -
                const b = await domFetch({
         
     | 
| 
       225 
     | 
    
         
            -
                    url: 'http://example.com',
         
     | 
| 
       226 
     | 
    
         
            -
                    responseType: 'json',
         
     | 
| 
       227 
     | 
    
         
            -
                });
         
     | 
| 
       228 
     | 
    
         
            -
                ensureType<any>(b.body);
         
     | 
| 
       229 
     | 
    
         
            -
             
     | 
| 
       230 
     | 
    
         
            -
                const c = await domFetch({
         
     | 
| 
       231 
     | 
    
         
            -
                    url: 'http://example.com',
         
     | 
| 
       232 
     | 
    
         
            -
                    responseType: 'readable',
         
     | 
| 
       233 
     | 
    
         
            -
                });
         
     | 
| 
       234 
     | 
    
         
            -
                ensureType<Response>(c.body);
         
     | 
| 
       235 
     | 
    
         
            -
             
     | 
| 
       236 
     | 
    
         
            -
                const d = await domFetch({
         
     | 
| 
       237 
     | 
    
         
            -
                    url: 'http://example.com',
         
     | 
| 
       238 
     | 
    
         
            -
                    responseType: 'buffer',
         
     | 
| 
       239 
     | 
    
         
            -
                });
         
     | 
| 
       240 
     | 
    
         
            -
                ensureType<Buffer>(d.body);
         
     | 
| 
       241 
     | 
    
         
            -
            }
         
     | 
    
        package/src/listen-zero.ts
    CHANGED
    
    | 
         @@ -7,13 +7,13 @@ export class ListenZeroSingleClientTimeoutError extends Error { 
     | 
|
| 
       7 
7 
     | 
    
         
             
                }
         
     | 
| 
       8 
8 
     | 
    
         
             
            }
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
            export async function listenZero(server: net.Server, hostname 
     | 
| 
       11 
     | 
    
         
            -
                server.listen(0, hostname);
         
     | 
| 
      
 10 
     | 
    
         
            +
            export async function listenZero(server: net.Server, hostname: string) {
         
     | 
| 
      
 11 
     | 
    
         
            +
                server.listen(0, hostname || '127.0.0.1');
         
     | 
| 
       12 
12 
     | 
    
         
             
                await once(server, 'listening');
         
     | 
| 
       13 
13 
     | 
    
         
             
                return (server.address() as net.AddressInfo).port;
         
     | 
| 
       14 
14 
     | 
    
         
             
            }
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
            export async function listenZeroSingleClient(hostname 
     | 
| 
      
 16 
     | 
    
         
            +
            export async function listenZeroSingleClient(hostname: string, options?: net.ServerOpts, listenTimeout = 30000) {
         
     | 
| 
       17 
17 
     | 
    
         
             
                const server = new net.Server(options);
         
     | 
| 
       18 
18 
     | 
    
         
             
                const port = await listenZero(server, hostname);
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
         @@ -295,8 +295,8 @@ export async function createConsoleServer(remoteStdout: Readable, remoteStderr: 
     | 
|
| 
       295 
295 
     | 
    
         
             
                    socket.once('error', cleanup);
         
     | 
| 
       296 
296 
     | 
    
         
             
                    socket.once('end', cleanup);
         
     | 
| 
       297 
297 
     | 
    
         
             
                });
         
     | 
| 
       298 
     | 
    
         
            -
                const readPort = await listenZero(readServer);
         
     | 
| 
       299 
     | 
    
         
            -
                const writePort = await listenZero(writeServer);
         
     | 
| 
      
 298 
     | 
    
         
            +
                const readPort = await listenZero(readServer, '127.0.0.1');
         
     | 
| 
      
 299 
     | 
    
         
            +
                const writePort = await listenZero(writeServer, '127.0.0.1');
         
     | 
| 
       300 
300 
     | 
    
         | 
| 
       301 
301 
     | 
    
         
             
                return {
         
     | 
| 
       302 
302 
     | 
    
         
             
                    clear(nativeId: ScryptedNativeId) {
         
     | 
| 
         @@ -9,8 +9,12 @@ import { ensurePluginVolume } from "./plugin-volume"; 
     | 
|
| 
       9 
9 
     | 
    
         | 
| 
       10 
10 
     | 
    
         
             
            export function defaultNpmExec(args: string[], options: child_process.SpawnOptions) {
         
     | 
| 
       11 
11 
     | 
    
         
             
                let npm = 'npm';
         
     | 
| 
       12 
     | 
    
         
            -
                if (os.platform() === 'win32')
         
     | 
| 
      
 12 
     | 
    
         
            +
                if (os.platform() === 'win32') {
         
     | 
| 
       13 
13 
     | 
    
         
             
                    npm += '.cmd';
         
     | 
| 
      
 14 
     | 
    
         
            +
                    // wrap each argument in a quote to handle spaces in paths
         
     | 
| 
      
 15 
     | 
    
         
            +
                    // https://github.com/nodejs/node/issues/38490#issuecomment-927330248
         
     | 
| 
      
 16 
     | 
    
         
            +
                    args = args.map(arg => '"' + arg + '"');
         
     | 
| 
      
 17 
     | 
    
         
            +
                }
         
     | 
| 
       14 
18 
     | 
    
         
             
                const cp = child_process.spawn(npm, args, options);
         
     | 
| 
       15 
19 
     | 
    
         
             
                return cp;
         
     | 
| 
       16 
20 
     | 
    
         
             
            }
         
     | 
| 
         @@ -9,6 +9,7 @@ import { computeClusterObjectHash } from '../cluster/cluster-hash'; 
     | 
|
| 
       9 
9 
     | 
    
         
             
            import { ClusterObject, ConnectRPCObject } from '../cluster/connect-rpc-object';
         
     | 
| 
       10 
10 
     | 
    
         
             
            import { listenZero } from '../listen-zero';
         
     | 
| 
       11 
11 
     | 
    
         
             
            import { RpcMessage, RpcPeer } from '../rpc';
         
     | 
| 
      
 12 
     | 
    
         
            +
            import { evalLocal } from '../rpc-peer-eval';
         
     | 
| 
       12 
13 
     | 
    
         
             
            import { createDuplexRpcPeer } from '../rpc-serializer';
         
     | 
| 
       13 
14 
     | 
    
         
             
            import { MediaManagerImpl } from './media';
         
     | 
| 
       14 
15 
     | 
    
         
             
            import { PluginAPI, PluginAPIProxy, PluginRemote, PluginRemoteLoadZipOptions } from './plugin-api';
         
     | 
| 
         @@ -95,10 +96,19 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe 
     | 
|
| 
       95 
96 
     | 
    
         
             
                        const { clusterId, clusterSecret, zipHash } = zipOptions;
         
     | 
| 
       96 
97 
     | 
    
         
             
                        const { zipFile, unzippedPath } = await prepareZip(getPluginVolume(pluginId), zipHash, getZip);
         
     | 
| 
       97 
98 
     | 
    
         | 
| 
       98 
     | 
    
         
            -
                        const onProxySerialization = (value: any,  
     | 
| 
      
 99 
     | 
    
         
            +
                        const onProxySerialization = (value: any, sourcePeerPort?: number) => {
         
     | 
| 
       99 
100 
     | 
    
         
             
                            const properties = RpcPeer.prepareProxyProperties(value) || {};
         
     | 
| 
       100 
101 
     | 
    
         
             
                            let clusterEntry: ClusterObject = properties.__cluster;
         
     | 
| 
       101 
102 
     | 
    
         | 
| 
      
 103 
     | 
    
         
            +
                            // ensure globally stable proxyIds.
         
     | 
| 
      
 104 
     | 
    
         
            +
                            const proxyId = clusterEntry?.proxyId || RpcPeer.generateId();
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                            // if the cluster entry already exists, check if it belongs to this node.
         
     | 
| 
      
 107 
     | 
    
         
            +
                            // if it belongs to this node, the entry must also be for this peer.
         
     | 
| 
      
 108 
     | 
    
         
            +
                            // relying on the liveness/gc of a different peer may cause race conditions.
         
     | 
| 
      
 109 
     | 
    
         
            +
                            if (clusterEntry && clusterPort === clusterEntry.port && sourcePeerPort !== clusterEntry.sourcePort)
         
     | 
| 
      
 110 
     | 
    
         
            +
                                clusterEntry = undefined;
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
       102 
112 
     | 
    
         
             
                            // set the cluster identity if it does not exist.
         
     | 
| 
       103 
113 
     | 
    
         
             
                            if (!clusterEntry) {
         
     | 
| 
       104 
114 
     | 
    
         
             
                                clusterEntry = {
         
     | 
| 
         @@ -111,12 +121,11 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe 
     | 
|
| 
       111 
121 
     | 
    
         
             
                                clusterEntry.sha256 = computeClusterObjectHash(clusterEntry, clusterSecret);
         
     | 
| 
       112 
122 
     | 
    
         
             
                                properties.__cluster = clusterEntry;
         
     | 
| 
       113 
123 
     | 
    
         
             
                            }
         
     | 
| 
       114 
     | 
    
         
            -
             
     | 
| 
       115 
     | 
    
         
            -
                             
     | 
| 
       116 
     | 
    
         
            -
             
     | 
| 
       117 
     | 
    
         
            -
             
     | 
| 
       118 
     | 
    
         
            -
                             
     | 
| 
       119 
     | 
    
         
            -
                            return properties;
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                            return {
         
     | 
| 
      
 126 
     | 
    
         
            +
                                proxyId,
         
     | 
| 
      
 127 
     | 
    
         
            +
                                properties,
         
     | 
| 
      
 128 
     | 
    
         
            +
                            };
         
     | 
| 
       120 
129 
     | 
    
         
             
                        }
         
     | 
| 
       121 
130 
     | 
    
         
             
                        peer.onProxySerialization = onProxySerialization;
         
     | 
| 
       122 
131 
     | 
    
         | 
| 
         @@ -133,7 +142,7 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe 
     | 
|
| 
       133 
142 
     | 
    
         
             
                        const clusterRpcServer = net.createServer(client => {
         
     | 
| 
       134 
143 
     | 
    
         
             
                            const clusterPeer = createDuplexRpcPeer(peer.selfName, 'cluster-client', client, client);
         
     | 
| 
       135 
144 
     | 
    
         
             
                            const clusterPeerPort = client.remotePort;
         
     | 
| 
       136 
     | 
    
         
            -
                            clusterPeer.onProxySerialization = (value 
     | 
| 
      
 145 
     | 
    
         
            +
                            clusterPeer.onProxySerialization = (value) => onProxySerialization(value, clusterPeerPort);
         
     | 
| 
       137 
146 
     | 
    
         
             
                            clusterPeers.set(clusterPeerPort, Promise.resolve(clusterPeer));
         
     | 
| 
       138 
147 
     | 
    
         
             
                            startPluginRemoteOptions?.onClusterPeer?.(clusterPeer);
         
     | 
| 
       139 
148 
     | 
    
         
             
                            const connectRPCObject: ConnectRPCObject = async (o) => {
         
     | 
| 
         @@ -164,8 +173,7 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe 
     | 
|
| 
       164 
173 
     | 
    
         
             
                                        const sourcePort = (socket.address() as net.AddressInfo).port;
         
     | 
| 
       165 
174 
     | 
    
         | 
| 
       166 
175 
     | 
    
         
             
                                        const clusterPeer = createDuplexRpcPeer(peer.selfName, 'cluster-server', socket, socket);
         
     | 
| 
       167 
     | 
    
         
            -
                                        clusterPeer. 
     | 
| 
       168 
     | 
    
         
            -
                                        clusterPeer.onProxySerialization = (value, proxyId) => onProxySerialization(value, proxyId, sourcePort);
         
     | 
| 
      
 176 
     | 
    
         
            +
                                        clusterPeer.onProxySerialization = (value) => onProxySerialization(value, sourcePort);
         
     | 
| 
       169 
177 
     | 
    
         
             
                                        return clusterPeer;
         
     | 
| 
       170 
178 
     | 
    
         
             
                                    }
         
     | 
| 
       171 
179 
     | 
    
         
             
                                    catch (e) {
         
     | 
| 
         @@ -192,10 +200,10 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe 
     | 
|
| 
       192 
200 
     | 
    
         
             
                            try {
         
     | 
| 
       193 
201 
     | 
    
         
             
                                const clusterPeerPromise = ensureClusterPeer(port);
         
     | 
| 
       194 
202 
     | 
    
         
             
                                const clusterPeer = await clusterPeerPromise;
         
     | 
| 
       195 
     | 
    
         
            -
                                //  
     | 
| 
       196 
     | 
    
         
            -
                                 
     | 
| 
       197 
     | 
    
         
            -
                                if ( 
     | 
| 
       198 
     | 
    
         
            -
                                    return  
     | 
| 
      
 203 
     | 
    
         
            +
                                // the proxy id is guaranteed to be unique in all peers in a cluster
         
     | 
| 
      
 204 
     | 
    
         
            +
                                const existing = clusterPeer.remoteWeakProxies[proxyId]?.deref();
         
     | 
| 
      
 205 
     | 
    
         
            +
                                if (existing)
         
     | 
| 
      
 206 
     | 
    
         
            +
                                    return existing;
         
     | 
| 
       199 
207 
     | 
    
         
             
                                let peerConnectRPCObject: ConnectRPCObject = clusterPeer.tags['connectRPCObject'];
         
     | 
| 
       200 
208 
     | 
    
         
             
                                if (!peerConnectRPCObject) {
         
     | 
| 
       201 
209 
     | 
    
         
             
                                    peerConnectRPCObject = await clusterPeer.getParam('connectRPCObject');
         
     | 
| 
         @@ -269,11 +277,11 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe 
     | 
|
| 
       269 
277 
     | 
    
         | 
| 
       270 
278 
     | 
    
         
             
                            process.on('uncaughtException', e => {
         
     | 
| 
       271 
279 
     | 
    
         
             
                                getPluginConsole().error('uncaughtException', e);
         
     | 
| 
       272 
     | 
    
         
            -
                                scrypted.log.e('uncaughtException ' + e?.toString());
         
     | 
| 
      
 280 
     | 
    
         
            +
                                scrypted.log.e('uncaughtException ' + (e.stack || e?.toString()));
         
     | 
| 
       273 
281 
     | 
    
         
             
                            });
         
     | 
| 
       274 
282 
     | 
    
         
             
                            process.on('unhandledRejection', e => {
         
     | 
| 
       275 
283 
     | 
    
         
             
                                getPluginConsole().error('unhandledRejection', e);
         
     | 
| 
       276 
     | 
    
         
            -
                                scrypted.log.e('unhandledRejection ' + e?.toString());
         
     | 
| 
      
 284 
     | 
    
         
            +
                                scrypted.log.e('unhandledRejection ' + ((e as Error).stack || e?.toString()));
         
     | 
| 
       277 
285 
     | 
    
         
             
                            });
         
     | 
| 
       278 
286 
     | 
    
         | 
| 
       279 
287 
     | 
    
         
             
                            installSourceMapSupport({
         
     | 
| 
         @@ -376,7 +384,7 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe 
     | 
|
| 
       376 
384 
     | 
    
         | 
| 
       377 
385 
     | 
    
         
             
                        try {
         
     | 
| 
       378 
386 
     | 
    
         
             
                            const filename = zipOptions?.debug ? '/plugin/main.nodejs.js' : `/${pluginId}/main.nodejs.js`;
         
     | 
| 
       379 
     | 
    
         
            -
                             
     | 
| 
      
 387 
     | 
    
         
            +
                            evalLocal(peer, script, filename, params);
         
     | 
| 
       380 
388 
     | 
    
         | 
| 
       381 
389 
     | 
    
         
             
                            if (zipOptions?.fork) {
         
     | 
| 
       382 
390 
     | 
    
         
             
                                // pluginConsole?.log('plugin forked');
         
     | 
| 
         @@ -179,7 +179,6 @@ class DeviceStateProxyHandler implements ProxyHandler<any> { 
     | 
|
| 
       179 
179 
     | 
    
         | 
| 
       180 
180 
     | 
    
         
             
                set?(target: any, p: PropertyKey, value: any, receiver: any) {
         
     | 
| 
       181 
181 
     | 
    
         
             
                    checkProperty(p.toString(), value);
         
     | 
| 
       182 
     | 
    
         
            -
                    const now = Date.now();
         
     | 
| 
       183 
182 
     | 
    
         
             
                    this.deviceManager.systemManager.state[this.id][p as string] = {
         
     | 
| 
       184 
183 
     | 
    
         
             
                        value,
         
     | 
| 
       185 
184 
     | 
    
         
             
                    };
         
     | 
| 
         @@ -446,7 +445,7 @@ export async function setupPluginRemote(peer: RpcPeer, api: PluginAPI, pluginId: 
     | 
|
| 
       446 
445 
     | 
    
         
             
                    return remote;
         
     | 
| 
       447 
446 
     | 
    
         
             
                }
         
     | 
| 
       448 
447 
     | 
    
         
             
                catch (e) {
         
     | 
| 
       449 
     | 
    
         
            -
                    throw new RPCResultError(peer, 'error while retrieving PluginRemote', e);
         
     | 
| 
      
 448 
     | 
    
         
            +
                    throw new RPCResultError(peer, 'error while retrieving PluginRemote', e as Error);
         
     | 
| 
       450 
449 
     | 
    
         
             
                }
         
     | 
| 
       451 
450 
     | 
    
         
             
            }
         
     | 
| 
       452 
451 
     | 
    
         | 
| 
         @@ -0,0 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import type { CompileFunctionOptions } from 'vm';
         
     | 
| 
      
 2 
     | 
    
         
            +
            import { RpcPeer } from "./rpc";
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            type CompileFunction = (code: string, params?: ReadonlyArray<string>, options?: CompileFunctionOptions) => Function;
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            function compileFunction(code: string, params?: ReadonlyArray<string>, options?: CompileFunctionOptions): any {
         
     | 
| 
      
 7 
     | 
    
         
            +
                params = params || [];
         
     | 
| 
      
 8 
     | 
    
         
            +
                const f = `(function(${params.join(',')}) {;${code};})`;
         
     | 
| 
      
 9 
     | 
    
         
            +
                return eval(f);
         
     | 
| 
      
 10 
     | 
    
         
            +
            }
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            export function evalLocal<T>(peer: RpcPeer, script: string, filename?: string, coercedParams?: { [name: string]: any }): T {
         
     | 
| 
      
 13 
     | 
    
         
            +
                const params = Object.assign({}, peer.params, coercedParams);
         
     | 
| 
      
 14 
     | 
    
         
            +
                let compile: CompileFunction;
         
     | 
| 
      
 15 
     | 
    
         
            +
                try {
         
     | 
| 
      
 16 
     | 
    
         
            +
                    // prevent bundlers from trying to include non-existent vm module.
         
     | 
| 
      
 17 
     | 
    
         
            +
                    compile = module[`require`]('vm').compileFunction;
         
     | 
| 
      
 18 
     | 
    
         
            +
                }
         
     | 
| 
      
 19 
     | 
    
         
            +
                catch (e) {
         
     | 
| 
      
 20 
     | 
    
         
            +
                    compile = compileFunction;
         
     | 
| 
      
 21 
     | 
    
         
            +
                }
         
     | 
| 
      
 22 
     | 
    
         
            +
                const f = compile(script, Object.keys(params), {
         
     | 
| 
      
 23 
     | 
    
         
            +
                    filename,
         
     | 
| 
      
 24 
     | 
    
         
            +
                });
         
     | 
| 
      
 25 
     | 
    
         
            +
                const value = f(...Object.values(params));
         
     | 
| 
      
 26 
     | 
    
         
            +
                return value;
         
     | 
| 
      
 27 
     | 
    
         
            +
            }
         
     | 
    
        package/src/rpc-serializer.ts
    CHANGED
    
    | 
         @@ -10,7 +10,7 @@ export function createDuplexRpcPeer(selfName: string, peerName: string, readable 
     | 
|
| 
       10 
10 
     | 
    
         
             
                        serializer.sendMessage(message, reject, serializationContext);
         
     | 
| 
       11 
11 
     | 
    
         
             
                    }
         
     | 
| 
       12 
12 
     | 
    
         
             
                    catch (e) {
         
     | 
| 
       13 
     | 
    
         
            -
                        reject?.(e);
         
     | 
| 
      
 13 
     | 
    
         
            +
                        reject?.(e as Error);
         
     | 
| 
       14 
14 
     | 
    
         
             
                        readable.destroy();
         
     | 
| 
       15 
15 
     | 
    
         
             
                    }
         
     | 
| 
       16 
16 
     | 
    
         
             
                });
         
     | 
| 
         @@ -165,7 +165,7 @@ export function createRpcDuplexSerializer(writable: { 
     | 
|
| 
       165 
165 
     | 
    
         
             
                                serializer.onMessageFinish(message);
         
     | 
| 
       166 
166 
     | 
    
         
             
                            }
         
     | 
| 
       167 
167 
     | 
    
         
             
                            catch (e) {
         
     | 
| 
       168 
     | 
    
         
            -
                                serializer.kill('message parse failure ' + e.message);
         
     | 
| 
      
 168 
     | 
    
         
            +
                                serializer.kill('message parse failure ' + (e as Error).message);
         
     | 
| 
       169 
169 
     | 
    
         
             
                            }
         
     | 
| 
       170 
170 
     | 
    
         
             
                        }
         
     | 
| 
       171 
171 
     | 
    
         
             
                        else {
         
     |