@scrypted/server 0.123.31 → 0.123.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/plugin/device.d.ts +60 -0
  2. package/dist/plugin/device.js +249 -0
  3. package/dist/plugin/device.js.map +1 -0
  4. package/dist/plugin/endpoint.d.ts +45 -0
  5. package/dist/plugin/endpoint.js +97 -0
  6. package/dist/plugin/endpoint.js.map +1 -0
  7. package/dist/plugin/plugin-host.d.ts +1 -1
  8. package/dist/plugin/plugin-host.js +11 -10
  9. package/dist/plugin/plugin-host.js.map +1 -1
  10. package/dist/plugin/plugin-remote-worker.js +18 -16
  11. package/dist/plugin/plugin-remote-worker.js.map +1 -1
  12. package/dist/plugin/plugin-remote.d.ts +2 -51
  13. package/dist/plugin/plugin-remote.js +6 -337
  14. package/dist/plugin/plugin-remote.js.map +1 -1
  15. package/dist/plugin/runtime/child-process-worker.d.ts +1 -1
  16. package/dist/plugin/runtime/child-process-worker.js +2 -2
  17. package/dist/plugin/runtime/child-process-worker.js.map +1 -1
  18. package/dist/plugin/runtime/cluster-fork-worker.d.ts +4 -4
  19. package/dist/plugin/runtime/cluster-fork-worker.js +8 -3
  20. package/dist/plugin/runtime/cluster-fork-worker.js.map +1 -1
  21. package/dist/plugin/runtime/custom-worker.d.ts +1 -1
  22. package/dist/plugin/runtime/custom-worker.js +3 -3
  23. package/dist/plugin/runtime/custom-worker.js.map +1 -1
  24. package/dist/plugin/runtime/node-fork-worker.d.ts +1 -1
  25. package/dist/plugin/runtime/node-fork-worker.js +2 -2
  26. package/dist/plugin/runtime/node-fork-worker.js.map +1 -1
  27. package/dist/plugin/runtime/python-worker.d.ts +1 -1
  28. package/dist/plugin/runtime/python-worker.js +3 -3
  29. package/dist/plugin/runtime/python-worker.js.map +1 -1
  30. package/dist/plugin/runtime/runtime-host.d.ts +1 -1
  31. package/dist/plugin/runtime/runtime-host.js +3 -3
  32. package/dist/plugin/runtime/runtime-host.js.map +1 -1
  33. package/dist/plugin/system.js +0 -1
  34. package/dist/plugin/system.js.map +1 -1
  35. package/dist/runtime.d.ts +4 -4
  36. package/dist/runtime.js +1 -1
  37. package/dist/runtime.js.map +1 -1
  38. package/dist/scrypted-cluster-main.d.ts +14 -3
  39. package/dist/scrypted-cluster-main.js +42 -18
  40. package/dist/scrypted-cluster-main.js.map +1 -1
  41. package/dist/scrypted-server-main.js +1 -0
  42. package/dist/scrypted-server-main.js.map +1 -1
  43. package/dist/services/cluster-fork.d.ts +4 -3
  44. package/dist/services/cluster-fork.js +7 -5
  45. package/dist/services/cluster-fork.js.map +1 -1
  46. package/package.json +2 -2
  47. package/python/cluster_labels.py +2 -2
  48. package/python/cluster_setup.py +1 -1
  49. package/python/plugin_console.py +8 -0
  50. package/python/plugin_remote.py +116 -34
  51. package/python/rpc.py +5 -4
  52. package/src/plugin/device.ts +261 -0
  53. package/src/plugin/endpoint.ts +109 -0
  54. package/src/plugin/plugin-host.ts +25 -21
  55. package/src/plugin/plugin-remote-worker.ts +30 -20
  56. package/src/plugin/plugin-remote.ts +6 -364
  57. package/src/plugin/runtime/child-process-worker.ts +3 -1
  58. package/src/plugin/runtime/cluster-fork-worker.ts +20 -12
  59. package/src/plugin/runtime/custom-worker.ts +3 -3
  60. package/src/plugin/runtime/node-fork-worker.ts +2 -2
  61. package/src/plugin/runtime/python-worker.ts +3 -3
  62. package/src/plugin/runtime/runtime-host.ts +4 -4
  63. package/src/plugin/system.ts +0 -1
  64. package/src/runtime.ts +4 -4
  65. package/src/scrypted-cluster-main.ts +54 -29
  66. package/src/scrypted-server-main.ts +1 -0
  67. package/src/services/cluster-fork.ts +10 -8
@@ -28,8 +28,8 @@ export function isNodePluginChildProcess() {
28
28
 
29
29
  export class NodeForkWorker extends ChildProcessWorker {
30
30
 
31
- constructor(mainFilename: string, pluginId: string, options: RuntimeWorkerOptions) {
32
- super(pluginId, options);
31
+ constructor(mainFilename: string, options: RuntimeWorkerOptions) {
32
+ super(options);
33
33
 
34
34
  const { env, pluginDebug } = options;
35
35
 
@@ -41,8 +41,8 @@ export class PythonRuntimeWorker extends ChildProcessWorker {
41
41
  return this._stderr;
42
42
  }
43
43
 
44
- constructor(pluginId: string, options: RuntimeWorkerOptions) {
45
- super(pluginId, options);
44
+ constructor(options: RuntimeWorkerOptions) {
45
+ super(options);
46
46
 
47
47
  const { env, pluginDebug } = options;
48
48
  const args: string[] = [
@@ -148,7 +148,7 @@ export class PythonRuntimeWorker extends ChildProcessWorker {
148
148
  };
149
149
 
150
150
  const pyVersion = require('py/package.json').version;
151
- const pyPath = path.join(getPluginVolume(pluginId), 'py');
151
+ const pyPath = path.join(getPluginVolume(this.pluginId), 'py');
152
152
  const portableInstallPath = path.join(pyPath, pyVersion);
153
153
 
154
154
  const py = new PortablePython(pluginPythonVersion, portableInstallPath, portablePythonOptions);
@@ -4,14 +4,14 @@ import { NodeForkWorker } from "./node-fork-worker";
4
4
  import { PythonRuntimeWorker } from "./python-worker";
5
5
  import type { RuntimeWorker, RuntimeWorkerOptions } from "./runtime-worker";
6
6
 
7
- export type RuntimeHost = (mainFilename: string, pluginId: string, options: RuntimeWorkerOptions, runtime: ScryptedRuntime) => RuntimeWorker;
7
+ export type RuntimeHost = (mainFilename: string, options: RuntimeWorkerOptions, runtime: ScryptedRuntime) => RuntimeWorker;
8
8
 
9
9
  export function getBuiltinRuntimeHosts() {
10
10
  const pluginHosts = new Map<string, RuntimeHost>();
11
11
 
12
- pluginHosts.set('custom', (_, pluginId, options, runtime) => new CustomRuntimeWorker(pluginId, options, runtime));
13
- pluginHosts.set('python', (_, pluginId, options) => new PythonRuntimeWorker(pluginId, options));
14
- pluginHosts.set('node', (mainFilename, pluginId, options) => new NodeForkWorker(mainFilename, pluginId, options));
12
+ pluginHosts.set('custom', (_, options, runtime) => new CustomRuntimeWorker(options, runtime));
13
+ pluginHosts.set('python', (_, options) => new PythonRuntimeWorker(options));
14
+ pluginHosts.set('node', (mainFilename, options) => new NodeForkWorker(mainFilename, options));
15
15
 
16
16
  return pluginHosts;
17
17
  }
@@ -1,7 +1,6 @@
1
1
  import { EventListener, EventListenerOptions, EventListenerRegister, Logger, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, ScryptedInterfaceDescriptor, ScryptedInterfaceDescriptors, ScryptedInterfaceProperty, ScryptedNativeId, SystemDeviceState, SystemManager } from "@scrypted/types";
2
2
  import { EventRegistry } from "../event-registry";
3
3
  import { PrimitiveProxyHandler, RpcPeer } from '../rpc';
4
- // import type { PluginComponent } from "../services/plugin";
5
4
  import { getInterfaceMethods, getInterfaceProperties, getPropertyInterfaces, isValidInterfaceMethod, propertyInterfaces } from "./descriptor";
6
5
  import { PluginAPI } from "./plugin-api";
7
6
 
package/src/runtime.ts CHANGED
@@ -37,7 +37,7 @@ import { getIpAddress, SCRYPTED_INSECURE_PORT, SCRYPTED_SECURE_PORT } from './se
37
37
  import { AddressSettings } from './services/addresses';
38
38
  import { Alerts } from './services/alerts';
39
39
  import { Backup } from './services/backup';
40
- import { ClusterFork } from './services/cluster-fork';
40
+ import { ClusterForkService } from './services/cluster-fork';
41
41
  import { CORSControl } from './services/cors';
42
42
  import { Info } from './services/info';
43
43
  import { getNpmPackageInfo, PluginComponent } from './services/plugin';
@@ -45,7 +45,7 @@ import { ServiceControl } from './services/service-control';
45
45
  import { UsersService } from './services/users';
46
46
  import { getState, ScryptedStateManager, setState } from './state';
47
47
  import { isClusterAddress } from './cluster/cluster-setup';
48
- import { ClusterWorker } from './scrypted-cluster-main';
48
+ import { RunningClusterWorker } from './scrypted-cluster-main';
49
49
 
50
50
  interface DeviceProxyPair {
51
51
  handler: PluginDeviceProxyHandler;
@@ -63,7 +63,7 @@ interface HttpPluginData {
63
63
  export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
64
64
  clusterId = crypto.randomBytes(3).toString('hex');
65
65
  clusterSecret = process.env.SCRYPTED_CLUSTER_SECRET || crypto.randomBytes(16).toString('hex');
66
- clusterWorkers = new Map<string, ClusterWorker>();
66
+ clusterWorkers = new Map<string, RunningClusterWorker>();
67
67
  plugins: { [id: string]: PluginHost } = {};
68
68
  pluginDevices: { [id: string]: PluginDevice } = {};
69
69
  devices: { [id: string]: DeviceProxyPair } = {};
@@ -89,7 +89,7 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
89
89
  corsControl = new CORSControl(this);
90
90
  addressSettings = new AddressSettings(this);
91
91
  usersService = new UsersService(this);
92
- clusterFork = new ClusterFork(this);
92
+ clusterFork = new ClusterForkService(this);
93
93
  info = new Info();
94
94
  backup = new Backup(this);
95
95
  pluginHosts = getBuiltinRuntimeHosts();
@@ -1,4 +1,4 @@
1
- import type { ForkOptions } from '@scrypted/types';
1
+ import type { ClusterManager, ClusterWorker, ForkOptions } from '@scrypted/types';
2
2
  import crypto from 'crypto';
3
3
  import { once } from 'events';
4
4
  import net from 'net';
@@ -11,13 +11,15 @@ import { computeClusterObjectHash } from './cluster/cluster-hash';
11
11
  import { getClusterLabels } from './cluster/cluster-labels';
12
12
  import { getScryptedClusterMode, InitializeCluster, setupCluster } from './cluster/cluster-setup';
13
13
  import type { ClusterObject } from './cluster/connect-rpc-object';
14
+ import type { PluginAPI } from './plugin/plugin-api';
14
15
  import { getPluginVolume, getScryptedVolume } from './plugin/plugin-volume';
15
16
  import { prepareZip } from './plugin/runtime/node-worker-common';
16
17
  import { getBuiltinRuntimeHosts } from './plugin/runtime/runtime-host';
17
- import { RuntimeWorker } from './plugin/runtime/runtime-worker';
18
+ import type { RuntimeWorker, RuntimeWorkerOptions } from './plugin/runtime/runtime-worker';
18
19
  import { RpcPeer } from './rpc';
19
20
  import { createRpcDuplexSerializer } from './rpc-serializer';
20
21
  import type { ScryptedRuntime } from './runtime';
22
+ import type { ClusterForkService } from './services/cluster-fork';
21
23
  import { sleep } from './sleep';
22
24
 
23
25
  installSourceMapSupport({
@@ -64,13 +66,13 @@ export interface ClusterForkOptions {
64
66
  clusterWorkerId?: ForkOptions['clusterWorkerId'];
65
67
  }
66
68
 
67
- type ConnectForkWorker = (auth: ClusterObject, properties: ClusterWorkerProperties) => Promise<{ clusterId: string }>;
69
+ type ConnectForkWorker = (auth: ClusterObject, properties: ClusterWorkerProperties) => Promise<{ clusterId: string, clusterWorkerId: string }>;
68
70
 
69
71
  export interface ClusterWorkerProperties {
70
72
  labels: string[];
71
73
  }
72
74
 
73
- export interface ClusterWorker extends ClusterWorkerProperties {
75
+ export interface RunningClusterWorker extends ClusterWorkerProperties {
74
76
  id: string;
75
77
  peer: RpcPeer;
76
78
  forks: Set<ClusterForkOptions>;
@@ -101,9 +103,10 @@ export class ClusterForkResult extends PeerLiveness {
101
103
  }
102
104
  }
103
105
 
104
- export type ClusterForkParam = (peerLiveness: PeerLiveness, runtime: string, packageJson: any, zipHash: string, getZip: () => Promise<Buffer>) => Promise<ClusterForkResult>;
106
+ export type ClusterForkParam = (runtime: string, options: RuntimeWorkerOptions, peerLiveness: PeerLiveness, getZip: () => Promise<Buffer>) => Promise<ClusterForkResult>;
105
107
 
106
108
  export function startClusterClient(mainFilename: string) {
109
+ console.log('Cluster client starting.');
107
110
  const originalClusterAddress = process.env.SCRYPTED_CLUSTER_ADDRESS;
108
111
  const labels = getClusterLabels();
109
112
 
@@ -157,12 +160,11 @@ export function startClusterClient(mainFilename: string) {
157
160
  });
158
161
 
159
162
  try {
160
-
161
163
  const connectForkWorker: ConnectForkWorker = await peer.getParam('connectForkWorker');
162
164
  const auth: ClusterObject = {
163
165
  address: socket.localAddress,
164
166
  port: socket.localPort,
165
- id: process.env.SCRYPTED_CLUSTER_ID || os.hostname(),
167
+ id: process.env.SCRYPTED_CLUSTER_CLIENT_NAME || os.hostname(),
166
168
  proxyId: undefined,
167
169
  sourceKey: undefined,
168
170
  sha256: undefined,
@@ -173,16 +175,12 @@ export function startClusterClient(mainFilename: string) {
173
175
  labels,
174
176
  };
175
177
 
176
- const { clusterId } = await connectForkWorker(auth, properties);
178
+ const { clusterId, clusterWorkerId } = await connectForkWorker(auth, properties);
179
+ process.env.SCRYPTED_CLUSTER_WORKER_ID = clusterWorkerId;
177
180
  const clusterPeerSetup = setupCluster(peer);
178
181
  await clusterPeerSetup.initializeCluster({ clusterId, clusterSecret });
179
182
 
180
- const clusterForkParam: ClusterForkParam = async (
181
- peerLiveness: PeerLiveness,
182
- runtime: string,
183
- packageJson: any,
184
- zipHash: string,
185
- getZip: () => Promise<Buffer>) => {
183
+ const clusterForkParam: ClusterForkParam = async (runtime, runtimeWorkerOptions, peerLiveness, getZip) => {
186
184
  let runtimeWorker: RuntimeWorker;
187
185
 
188
186
  const builtins = getBuiltinRuntimeHosts();
@@ -190,23 +188,24 @@ export function startClusterClient(mainFilename: string) {
190
188
  if (!rt)
191
189
  throw new Error('unknown runtime ' + runtime);
192
190
 
193
- const pluginId: string = packageJson.name;
194
- const { zipFile, unzippedPath } = await prepareZip(getPluginVolume(pluginId), zipHash, getZip);
191
+ const pluginId: string = runtimeWorkerOptions.packageJson.name;
192
+ const { zipFile, unzippedPath } = await prepareZip(getPluginVolume(pluginId), runtimeWorkerOptions.zipHash, getZip);
195
193
 
196
194
  const volume = getScryptedVolume();
197
195
  const pluginVolume = getPluginVolume(pluginId);
198
196
 
199
- runtimeWorker = rt(mainFilename, pluginId, {
200
- packageJson,
201
- env: {
202
- SCRYPTED_VOLUME: volume,
203
- SCRYPTED_PLUGIN_VOLUME: pluginVolume,
204
- },
205
- pluginDebug: undefined,
206
- zipFile,
207
- unzippedPath,
208
- zipHash,
209
- }, undefined);
197
+ runtimeWorkerOptions.zipFile = zipFile;
198
+ runtimeWorkerOptions.unzippedPath = unzippedPath;
199
+
200
+ runtimeWorkerOptions.env = {
201
+ ...runtimeWorkerOptions.env,
202
+ SCRYPTED_VOLUME: volume,
203
+ SCRYPTED_PLUGIN_VOLUME: pluginVolume,
204
+ };
205
+
206
+ runtimeWorker = rt(mainFilename, runtimeWorkerOptions, undefined);
207
+ runtimeWorker.stdout.on('data', data => console.log(data.toString()));
208
+ runtimeWorker.stderr.on('data', data => console.error(data.toString()));
210
209
 
211
210
  const threadPeer = new RpcPeer('main', 'thread', (message, reject, serializationContext) => runtimeWorker.send(message, reject, serializationContext));
212
211
  runtimeWorker.setupRpcPeer(threadPeer);
@@ -288,6 +287,7 @@ export function createClusterServer(runtime: ScryptedRuntime, certificate: Retur
288
287
  const peer = preparePeer(socket, 'server');
289
288
 
290
289
  const connectForkWorker: ConnectForkWorker = async (auth: ClusterObject, properties: ClusterWorkerProperties) => {
290
+ const id = crypto.randomUUID();
291
291
  try {
292
292
  const sha256 = computeClusterObjectHash(auth, runtime.clusterSecret);
293
293
  if (sha256 !== auth.sha256)
@@ -301,8 +301,7 @@ export function createClusterServer(runtime: ScryptedRuntime, certificate: Retur
301
301
  if (auth.port !== socket.remotePort || !socket.remoteAddress.endsWith(auth.address))
302
302
  throw new Error('cluster object address mismatch');
303
303
  }
304
- const id = crypto.randomUUID();
305
- const worker: ClusterWorker = {
304
+ const worker: RunningClusterWorker = {
306
305
  ...properties,
307
306
  // generate a random uuid.
308
307
  id,
@@ -325,6 +324,7 @@ export function createClusterServer(runtime: ScryptedRuntime, certificate: Retur
325
324
 
326
325
  return {
327
326
  clusterId: runtime.clusterId,
327
+ clusterWorkerId: id,
328
328
  }
329
329
  }
330
330
  peer.params['connectForkWorker'] = connectForkWorker;
@@ -332,3 +332,28 @@ export function createClusterServer(runtime: ScryptedRuntime, certificate: Retur
332
332
 
333
333
  return server;
334
334
  }
335
+
336
+ export class ClusterManagerImpl implements ClusterManager {
337
+ private clusterServicePromise: Promise<ClusterForkService>;
338
+
339
+ constructor(private api: PluginAPI) {
340
+ }
341
+
342
+ getClusterWorkerId(): string {
343
+ return process.env.SCRYPTED_CLUSTER_WORKER_ID;
344
+ }
345
+
346
+ getClusterMode(): 'server' | 'client' | undefined {
347
+ return getScryptedClusterMode()[0];
348
+ }
349
+
350
+ async getClusterWorkers(): Promise<Record<string, ClusterWorker>> {
351
+ const clusterFork = await this.getClusterService();
352
+ return clusterFork.getClusterWorkers();
353
+ }
354
+
355
+ private getClusterService() {
356
+ this.clusterServicePromise ||= this.api.getComponent('cluster-fork');
357
+ return this.clusterServicePromise;
358
+ }
359
+ }
@@ -100,6 +100,7 @@ app.use(bodyParser.raw({ type: 'application/*', limit: 100000000 }) as any)
100
100
  async function start(mainFilename: string, options?: {
101
101
  onRuntimeCreated?: (runtime: ScryptedRuntime) => Promise<void>,
102
102
  }) {
103
+ console.log('Cluster server starting.');
103
104
  const volumeDir = getScryptedVolume();
104
105
  await fs.promises.mkdir(volumeDir, {
105
106
  recursive: true
@@ -1,12 +1,13 @@
1
- import type { ScryptedRuntime } from "../runtime";
1
+ import { ClusterWorker } from "@scrypted/types";
2
2
  import { matchesClusterLabels } from "../cluster/cluster-labels";
3
- import { ClusterForkOptions, ClusterForkParam, ClusterWorker, PeerLiveness } from "../scrypted-cluster-main";
4
- import { RpcPeer } from "../rpc";
3
+ import type { ScryptedRuntime } from "../runtime";
4
+ import type { ClusterForkOptions, ClusterForkParam, PeerLiveness, RunningClusterWorker } from "../scrypted-cluster-main";
5
+ import type { RuntimeWorkerOptions } from "../plugin/runtime/runtime-worker";
5
6
 
6
- export class ClusterFork {
7
+ export class ClusterForkService {
7
8
  constructor(public runtime: ScryptedRuntime) { }
8
9
 
9
- async fork(peerLiveness: PeerLiveness, options: ClusterForkOptions, packageJson: any, zipHash: string, getZip: () => Promise<Buffer>) {
10
+ async fork(runtimeWorkerOptions: RuntimeWorkerOptions, options: ClusterForkOptions, peerLiveness: PeerLiveness, getZip: () => Promise<Buffer>) {
10
11
  const matchingWorkers = [...this.runtime.clusterWorkers.entries()].map(([id, worker]) => ({
11
12
  worker,
12
13
  matches: matchesClusterLabels(options, worker.labels),
@@ -18,7 +19,7 @@ export class ClusterFork {
18
19
  });
19
20
  matchingWorkers.sort((a, b) => b.worker.labels.length - a.worker.labels.length);
20
21
 
21
- let worker: ClusterWorker;
22
+ let worker: RunningClusterWorker;
22
23
 
23
24
  // try to keep fork id affinity to single worker if present. this presents the opportunity for
24
25
  // IPC.
@@ -35,7 +36,8 @@ export class ClusterFork {
35
36
  }
36
37
 
37
38
  const fork: ClusterForkParam = await worker.peer.getParam('fork');
38
- const forkResult = await fork(peerLiveness, options.runtime, packageJson, zipHash, getZip);
39
+ const forkResult = await fork(options.runtime, runtimeWorkerOptions, peerLiveness, getZip);
40
+ options.id ||= this.runtime.findPluginDevice(runtimeWorkerOptions.packageJson.name)?._id;
39
41
  worker.forks.add(options);
40
42
  forkResult.waitKilled().catch(() => { }).finally(() => {
41
43
  worker.forks.delete(options);
@@ -43,7 +45,7 @@ export class ClusterFork {
43
45
 
44
46
  forkResult.clusterWorkerId = worker.id;
45
47
  return forkResult;
46
- }
48
+ };
47
49
 
48
50
  async getClusterWorkers() {
49
51
  const ret: any = {};