@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.
- package/dist/plugin/device.d.ts +60 -0
- package/dist/plugin/device.js +249 -0
- package/dist/plugin/device.js.map +1 -0
- package/dist/plugin/endpoint.d.ts +45 -0
- package/dist/plugin/endpoint.js +97 -0
- package/dist/plugin/endpoint.js.map +1 -0
- package/dist/plugin/plugin-host.d.ts +1 -1
- package/dist/plugin/plugin-host.js +11 -10
- package/dist/plugin/plugin-host.js.map +1 -1
- package/dist/plugin/plugin-remote-worker.js +18 -16
- package/dist/plugin/plugin-remote-worker.js.map +1 -1
- package/dist/plugin/plugin-remote.d.ts +2 -51
- package/dist/plugin/plugin-remote.js +6 -337
- package/dist/plugin/plugin-remote.js.map +1 -1
- package/dist/plugin/runtime/child-process-worker.d.ts +1 -1
- package/dist/plugin/runtime/child-process-worker.js +2 -2
- package/dist/plugin/runtime/child-process-worker.js.map +1 -1
- package/dist/plugin/runtime/cluster-fork-worker.d.ts +4 -4
- package/dist/plugin/runtime/cluster-fork-worker.js +8 -3
- package/dist/plugin/runtime/cluster-fork-worker.js.map +1 -1
- package/dist/plugin/runtime/custom-worker.d.ts +1 -1
- package/dist/plugin/runtime/custom-worker.js +3 -3
- package/dist/plugin/runtime/custom-worker.js.map +1 -1
- package/dist/plugin/runtime/node-fork-worker.d.ts +1 -1
- package/dist/plugin/runtime/node-fork-worker.js +2 -2
- package/dist/plugin/runtime/node-fork-worker.js.map +1 -1
- package/dist/plugin/runtime/python-worker.d.ts +1 -1
- package/dist/plugin/runtime/python-worker.js +3 -3
- package/dist/plugin/runtime/python-worker.js.map +1 -1
- package/dist/plugin/runtime/runtime-host.d.ts +1 -1
- package/dist/plugin/runtime/runtime-host.js +3 -3
- package/dist/plugin/runtime/runtime-host.js.map +1 -1
- package/dist/plugin/system.js +0 -1
- package/dist/plugin/system.js.map +1 -1
- package/dist/runtime.d.ts +4 -4
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/scrypted-cluster-main.d.ts +14 -3
- package/dist/scrypted-cluster-main.js +42 -18
- package/dist/scrypted-cluster-main.js.map +1 -1
- package/dist/scrypted-server-main.js +1 -0
- package/dist/scrypted-server-main.js.map +1 -1
- package/dist/services/cluster-fork.d.ts +4 -3
- package/dist/services/cluster-fork.js +7 -5
- package/dist/services/cluster-fork.js.map +1 -1
- package/package.json +2 -2
- package/python/cluster_labels.py +2 -2
- package/python/cluster_setup.py +1 -1
- package/python/plugin_console.py +8 -0
- package/python/plugin_remote.py +116 -34
- package/python/rpc.py +5 -4
- package/src/plugin/device.ts +261 -0
- package/src/plugin/endpoint.ts +109 -0
- package/src/plugin/plugin-host.ts +25 -21
- package/src/plugin/plugin-remote-worker.ts +30 -20
- package/src/plugin/plugin-remote.ts +6 -364
- package/src/plugin/runtime/child-process-worker.ts +3 -1
- package/src/plugin/runtime/cluster-fork-worker.ts +20 -12
- package/src/plugin/runtime/custom-worker.ts +3 -3
- package/src/plugin/runtime/node-fork-worker.ts +2 -2
- package/src/plugin/runtime/python-worker.ts +3 -3
- package/src/plugin/runtime/runtime-host.ts +4 -4
- package/src/plugin/system.ts +0 -1
- package/src/runtime.ts +4 -4
- package/src/scrypted-cluster-main.ts +54 -29
- package/src/scrypted-server-main.ts +1 -0
- 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,
|
32
|
-
super(
|
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(
|
45
|
-
super(
|
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,
|
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', (_,
|
13
|
-
pluginHosts.set('python', (_,
|
14
|
-
pluginHosts.set('node', (mainFilename,
|
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
|
}
|
package/src/plugin/system.ts
CHANGED
@@ -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 {
|
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 {
|
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,
|
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
|
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
|
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 = (
|
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.
|
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
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
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
|
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
|
1
|
+
import { ClusterWorker } from "@scrypted/types";
|
2
2
|
import { matchesClusterLabels } from "../cluster/cluster-labels";
|
3
|
-
import {
|
4
|
-
import {
|
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
|
7
|
+
export class ClusterForkService {
|
7
8
|
constructor(public runtime: ScryptedRuntime) { }
|
8
9
|
|
9
|
-
async fork(
|
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:
|
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(
|
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 = {};
|