@alwaysai/device-agent 1.3.1 → 1.5.0
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/lib/application-control/config.js +2 -2
- package/lib/application-control/config.js.map +1 -1
- package/lib/application-control/environment-variables.d.ts.map +1 -1
- package/lib/application-control/environment-variables.js +9 -4
- package/lib/application-control/environment-variables.js.map +1 -1
- package/lib/application-control/environment-variables.test.js +1 -1
- package/lib/application-control/environment-variables.test.js.map +1 -1
- package/lib/application-control/install.d.ts.map +1 -1
- package/lib/application-control/install.js +7 -2
- package/lib/application-control/install.js.map +1 -1
- package/lib/application-control/models.d.ts +5 -0
- package/lib/application-control/models.d.ts.map +1 -1
- package/lib/application-control/models.js +28 -14
- package/lib/application-control/models.js.map +1 -1
- package/lib/application-control/status.d.ts.map +1 -1
- package/lib/application-control/status.js +14 -17
- package/lib/application-control/status.js.map +1 -1
- package/lib/application-control/utils.js +2 -2
- package/lib/application-control/utils.js.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts +5 -5
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +140 -105
- package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.d.ts +4 -2
- package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
- package/lib/cloud-connection/live-updates-handler.js +46 -25
- package/lib/cloud-connection/live-updates-handler.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.test.js +132 -16
- package/lib/cloud-connection/live-updates-handler.test.js.map +1 -1
- package/lib/cloud-connection/messages.d.ts.map +1 -1
- package/lib/cloud-connection/messages.js +3 -4
- package/lib/cloud-connection/messages.js.map +1 -1
- package/lib/cloud-connection/passthrough-handler.d.ts +5 -3
- package/lib/cloud-connection/passthrough-handler.d.ts.map +1 -1
- package/lib/cloud-connection/passthrough-handler.js +76 -62
- package/lib/cloud-connection/passthrough-handler.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.d.ts +16 -21
- package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
- package/lib/cloud-connection/shadow-handler.js +162 -108
- package/lib/cloud-connection/shadow-handler.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.test.js +100 -83
- package/lib/cloud-connection/shadow-handler.test.js.map +1 -1
- package/lib/cloud-connection/transaction-manager.d.ts +3 -0
- package/lib/cloud-connection/transaction-manager.d.ts.map +1 -1
- package/lib/cloud-connection/transaction-manager.js +11 -0
- package/lib/cloud-connection/transaction-manager.js.map +1 -1
- package/lib/cloud-connection/transaction-manager.test.js +102 -0
- package/lib/cloud-connection/transaction-manager.test.js.map +1 -1
- package/lib/device-control/device-control.d.ts +16 -15
- package/lib/device-control/device-control.d.ts.map +1 -1
- package/lib/device-control/device-control.js +117 -18
- package/lib/device-control/device-control.js.map +1 -1
- package/lib/docker/docker-compose.d.ts +14 -0
- package/lib/docker/docker-compose.d.ts.map +1 -0
- package/lib/docker/docker-compose.js +56 -0
- package/lib/docker/docker-compose.js.map +1 -0
- package/lib/index.js +2 -5
- package/lib/index.js.map +1 -1
- package/lib/infrastructure/agent-config.d.ts +45 -14
- package/lib/infrastructure/agent-config.d.ts.map +1 -1
- package/lib/infrastructure/agent-config.js +30 -15
- package/lib/infrastructure/agent-config.js.map +1 -1
- package/lib/infrastructure/agent-config.test.js +3 -0
- package/lib/infrastructure/agent-config.test.js.map +1 -1
- package/lib/local-connection/rabbitmq-connection.js +11 -11
- package/lib/local-connection/rabbitmq-connection.js.map +1 -1
- package/lib/secure-tunneling/secure-tunneling.d.ts +97 -0
- package/lib/secure-tunneling/secure-tunneling.d.ts.map +1 -0
- package/lib/secure-tunneling/secure-tunneling.js +435 -0
- package/lib/secure-tunneling/secure-tunneling.js.map +1 -0
- package/lib/secure-tunneling/secure-tunneling.test.d.ts +2 -0
- package/lib/secure-tunneling/secure-tunneling.test.d.ts.map +1 -0
- package/lib/secure-tunneling/secure-tunneling.test.js +1070 -0
- package/lib/secure-tunneling/secure-tunneling.test.js.map +1 -0
- package/lib/secure-tunneling/spawner-detached.d.ts +6 -0
- package/lib/secure-tunneling/spawner-detached.d.ts.map +1 -0
- package/lib/secure-tunneling/spawner-detached.js +107 -0
- package/lib/secure-tunneling/spawner-detached.js.map +1 -0
- package/lib/subcommands/app/analytics.d.ts.map +1 -1
- package/lib/subcommands/app/analytics.js +9 -13
- package/lib/subcommands/app/analytics.js.map +1 -1
- package/lib/subcommands/app/env-vars.d.ts.map +1 -1
- package/lib/subcommands/app/env-vars.js +11 -16
- package/lib/subcommands/app/env-vars.js.map +1 -1
- package/lib/subcommands/app/models.d.ts.map +1 -1
- package/lib/subcommands/app/models.js +12 -16
- package/lib/subcommands/app/models.js.map +1 -1
- package/lib/subcommands/device/clean.d.ts.map +1 -1
- package/lib/subcommands/device/clean.js +8 -6
- package/lib/subcommands/device/clean.js.map +1 -1
- package/lib/subcommands/device/get-info.d.ts +2 -0
- package/lib/subcommands/device/get-info.d.ts.map +1 -0
- package/lib/subcommands/device/get-info.js +36 -0
- package/lib/subcommands/device/get-info.js.map +1 -0
- package/lib/subcommands/device/index.d.ts.map +1 -1
- package/lib/subcommands/device/index.js +11 -2
- package/lib/subcommands/device/index.js.map +1 -1
- package/lib/subcommands/device/init.d.ts +5 -0
- package/lib/subcommands/device/init.d.ts.map +1 -0
- package/lib/subcommands/device/{device.js → init.js} +5 -36
- package/lib/subcommands/device/init.js.map +1 -0
- package/lib/subcommands/device/refresh.d.ts +2 -0
- package/lib/subcommands/device/refresh.d.ts.map +1 -0
- package/lib/subcommands/device/refresh.js +24 -0
- package/lib/subcommands/device/refresh.js.map +1 -0
- package/lib/subcommands/device/restart.d.ts +2 -0
- package/lib/subcommands/device/restart.d.ts.map +1 -0
- package/lib/subcommands/device/restart.js +14 -0
- package/lib/subcommands/device/restart.js.map +1 -0
- package/lib/util/check-for-updates.d.ts +3 -0
- package/lib/util/check-for-updates.d.ts.map +1 -0
- package/lib/util/check-for-updates.js +69 -0
- package/lib/util/check-for-updates.js.map +1 -0
- package/lib/util/cloud-mode-ready.d.ts +1 -0
- package/lib/util/cloud-mode-ready.d.ts.map +1 -1
- package/lib/util/cloud-mode-ready.js +36 -1
- package/lib/util/cloud-mode-ready.js.map +1 -1
- package/lib/util/file.d.ts +7 -0
- package/lib/util/file.d.ts.map +1 -0
- package/lib/util/file.js +66 -0
- package/lib/util/file.js.map +1 -0
- package/lib/util/file.test.d.ts +2 -0
- package/lib/util/file.test.d.ts.map +1 -0
- package/lib/util/file.test.js +87 -0
- package/lib/util/file.test.js.map +1 -0
- package/package.json +8 -7
- package/readme.md +3 -3
- package/src/application-control/config.ts +1 -1
- package/src/application-control/environment-variables.test.ts +1 -1
- package/src/application-control/environment-variables.ts +9 -6
- package/src/application-control/install.ts +8 -3
- package/src/application-control/models.ts +47 -19
- package/src/application-control/status.ts +16 -14
- package/src/application-control/utils.ts +1 -1
- package/src/cloud-connection/device-agent-cloud-connection.ts +202 -148
- package/src/cloud-connection/live-updates-handler.test.ts +161 -20
- package/src/cloud-connection/live-updates-handler.ts +63 -31
- package/src/cloud-connection/messages.ts +3 -4
- package/src/cloud-connection/passthrough-handler.ts +98 -76
- package/src/cloud-connection/shadow-handler.test.ts +101 -84
- package/src/cloud-connection/shadow-handler.ts +287 -133
- package/src/cloud-connection/transaction-manager.test.ts +124 -0
- package/src/cloud-connection/transaction-manager.ts +15 -0
- package/src/device-control/device-control.ts +125 -23
- package/src/docker/docker-compose.ts +60 -0
- package/src/index.ts +2 -6
- package/src/infrastructure/agent-config.test.ts +3 -0
- package/src/infrastructure/agent-config.ts +38 -40
- package/src/local-connection/rabbitmq-connection.ts +8 -8
- package/src/secure-tunneling/secure-tunneling.test.ts +1239 -0
- package/src/secure-tunneling/secure-tunneling.ts +599 -0
- package/src/secure-tunneling/spawner-detached.ts +123 -0
- package/src/subcommands/app/analytics.ts +16 -13
- package/src/subcommands/app/env-vars.ts +18 -16
- package/src/subcommands/app/models.ts +20 -16
- package/src/subcommands/device/clean.ts +5 -2
- package/src/subcommands/device/get-info.ts +49 -0
- package/src/subcommands/device/index.ts +11 -2
- package/src/subcommands/device/{device.ts → init.ts} +5 -47
- package/src/subcommands/device/refresh.ts +22 -0
- package/src/subcommands/device/restart.ts +11 -0
- package/src/util/check-for-updates.ts +69 -0
- package/src/util/cloud-mode-ready.ts +36 -0
- package/src/util/file.test.ts +90 -0
- package/src/util/file.ts +76 -0
- package/lib/docker/docker-compose-cmd.d.ts +0 -5
- package/lib/docker/docker-compose-cmd.d.ts.map +0 -1
- package/lib/docker/docker-compose-cmd.js +0 -16
- package/lib/docker/docker-compose-cmd.js.map +0 -1
- package/lib/secure-tunneling/index.d.ts +0 -5
- package/lib/secure-tunneling/index.d.ts.map +0 -1
- package/lib/secure-tunneling/index.js +0 -64
- package/lib/secure-tunneling/index.js.map +0 -1
- package/lib/subcommands/device/device.d.ts +0 -7
- package/lib/subcommands/device/device.d.ts.map +0 -1
- package/lib/subcommands/device/device.js.map +0 -1
- package/lib/util/safe-rimraf.d.ts +0 -2
- package/lib/util/safe-rimraf.d.ts.map +0 -1
- package/lib/util/safe-rimraf.js +0 -16
- package/lib/util/safe-rimraf.js.map +0 -1
- package/src/docker/docker-compose-cmd.ts +0 -15
- package/src/secure-tunneling/index.ts +0 -74
- package/src/util/safe-rimraf.ts +0 -14
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { ChildProcess, spawn } from 'child_process';
|
|
2
|
+
import { JsSpawner } from 'alwaysai/lib/util';
|
|
3
|
+
import { logger } from '../util/logger';
|
|
4
|
+
|
|
5
|
+
const DEFAULT_TIMEOUT_MS = 5000;
|
|
6
|
+
|
|
7
|
+
export async function runDetachedProcess(
|
|
8
|
+
command: string,
|
|
9
|
+
args: string[],
|
|
10
|
+
timeoutMs = DEFAULT_TIMEOUT_MS
|
|
11
|
+
): Promise<ChildProcess> {
|
|
12
|
+
logger.debug('-> runDetachedProcess');
|
|
13
|
+
|
|
14
|
+
let isSpawned = false; // Flag to indicate if process has been successfully spawned
|
|
15
|
+
|
|
16
|
+
const child = spawn(command, args, {
|
|
17
|
+
detached: true,
|
|
18
|
+
stdio: 'ignore'
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
child.unref();
|
|
22
|
+
|
|
23
|
+
// Create a promise that resolves after the specified timeout
|
|
24
|
+
const timeoutPromise = new Promise<ChildProcess>((resolve, reject) => {
|
|
25
|
+
setTimeout(() => {
|
|
26
|
+
if (!isSpawned) {
|
|
27
|
+
logger.error('Timeout spawning off detached process');
|
|
28
|
+
resolve(child); // Resolve the promise with the child process even if timeout occurs
|
|
29
|
+
}
|
|
30
|
+
}, timeoutMs);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Handle 'spawn' event
|
|
34
|
+
child.once('spawn', () => {
|
|
35
|
+
isSpawned = true;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Race between the actual process spawning and the timeout
|
|
39
|
+
await Promise.race([
|
|
40
|
+
new Promise<ChildProcess>((resolve) => {
|
|
41
|
+
if (isSpawned) {
|
|
42
|
+
resolve(child); // Resolve immediately if process has already been spawned
|
|
43
|
+
} else {
|
|
44
|
+
child.once('spawn', () => resolve(child));
|
|
45
|
+
}
|
|
46
|
+
}),
|
|
47
|
+
timeoutPromise
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
logger.debug('<- runDetachedProcess');
|
|
51
|
+
return child;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function killDetachedProcess(
|
|
55
|
+
detachedProcess: ChildProcess,
|
|
56
|
+
processName?: string[],
|
|
57
|
+
timeoutMs = DEFAULT_TIMEOUT_MS
|
|
58
|
+
): Promise<void> {
|
|
59
|
+
logger.debug('-> killDetachedProcess');
|
|
60
|
+
return new Promise<void>((resolve, reject) => {
|
|
61
|
+
const timeout = setTimeout(() => {
|
|
62
|
+
if (processName) {
|
|
63
|
+
killDetachedProcessByName(processName, timeoutMs)
|
|
64
|
+
.then(() => resolve())
|
|
65
|
+
.catch((err) => reject(err));
|
|
66
|
+
} else {
|
|
67
|
+
reject(new Error('killDetachedProcess timeout'));
|
|
68
|
+
}
|
|
69
|
+
}, timeoutMs);
|
|
70
|
+
|
|
71
|
+
detachedProcess.on('exit', (code) => {
|
|
72
|
+
clearTimeout(timeout);
|
|
73
|
+
logger.debug(`Process exited with code ${code}`);
|
|
74
|
+
resolve();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
detachedProcess.on('error', (error) => {
|
|
78
|
+
clearTimeout(timeout);
|
|
79
|
+
if (processName) {
|
|
80
|
+
killDetachedProcessByName(processName, timeoutMs)
|
|
81
|
+
.then(() => resolve())
|
|
82
|
+
.catch((err) => reject(err));
|
|
83
|
+
} else {
|
|
84
|
+
logger.error('killDetachedProcess error occurred:', error);
|
|
85
|
+
reject(error);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
detachedProcess.kill('SIGTERM');
|
|
90
|
+
logger.debug('<- killDetachedProcess');
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export async function killDetachedProcessByName(
|
|
95
|
+
processName: string[],
|
|
96
|
+
timeoutMs = DEFAULT_TIMEOUT_MS
|
|
97
|
+
): Promise<void> {
|
|
98
|
+
logger.debug('-> killDetachedProcessByName');
|
|
99
|
+
return new Promise<void>((resolve, reject) => {
|
|
100
|
+
const timeout = setTimeout(() => {
|
|
101
|
+
logger.error('Timeout waiting for process kill by name');
|
|
102
|
+
reject(new Error('killDetachedProcessByName timeout'));
|
|
103
|
+
}, timeoutMs);
|
|
104
|
+
|
|
105
|
+
JsSpawner()
|
|
106
|
+
.run({
|
|
107
|
+
exe: 'pkill',
|
|
108
|
+
args: processName
|
|
109
|
+
})
|
|
110
|
+
.then(() => {
|
|
111
|
+
clearTimeout(timeout);
|
|
112
|
+
resolve();
|
|
113
|
+
})
|
|
114
|
+
.catch((error) => {
|
|
115
|
+
clearTimeout(timeout);
|
|
116
|
+
logger.error('killDetachedProcessByName error occurred:', error);
|
|
117
|
+
reject(error);
|
|
118
|
+
})
|
|
119
|
+
.finally(() => {
|
|
120
|
+
logger.debug('<- killDetachedProcessByName');
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
@@ -9,6 +9,10 @@ import { DeviceAgentCloudConnection } from '../../cloud-connection/device-agent-
|
|
|
9
9
|
import sleep from '../../util/sleep';
|
|
10
10
|
import { logger } from '../../util/logger';
|
|
11
11
|
import { assign, merge } from 'lodash';
|
|
12
|
+
import {
|
|
13
|
+
buildUpdateProjectShadowMessage,
|
|
14
|
+
getShadowTopic
|
|
15
|
+
} from '@alwaysai/device-agent-schemas';
|
|
12
16
|
|
|
13
17
|
export const getAnalyticsCfgCliLeaf = CliLeaf({
|
|
14
18
|
name: 'get-analytics-cfg',
|
|
@@ -73,21 +77,20 @@ export const setAnalyticsCfgCliLeaf = CliLeaf({
|
|
|
73
77
|
const appCfg = assign(existingAppCfg, merge(existingAppCfg, newAppCfg));
|
|
74
78
|
|
|
75
79
|
// Update the shadow as a client
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
[project]: {
|
|
81
|
-
appConfig: JSON.stringify(appCfg) // Pack app config as string as dictated by schema
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
clientToken: 'client'
|
|
80
|
+
const toDesire = {
|
|
81
|
+
[project]: {
|
|
82
|
+
appConfig: JSON.stringify(appCfg) // Pack app config as string as dictated by schema
|
|
83
|
+
}
|
|
86
84
|
};
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
deviceAgent.publisher.publish(
|
|
86
|
+
getShadowTopic(deviceAgent.getClientId(), 'projects', 'update'),
|
|
87
|
+
JSON.stringify(
|
|
88
|
+
buildUpdateProjectShadowMessage({
|
|
89
|
+
clientId: 'client',
|
|
90
|
+
desired: toDesire
|
|
91
|
+
})
|
|
92
|
+
)
|
|
89
93
|
);
|
|
90
|
-
deviceAgent.publisher.publish(topic, JSON.stringify(packet));
|
|
91
94
|
// Sleep for extra time to ensure time for shadow response
|
|
92
95
|
await sleep(10000);
|
|
93
96
|
|
|
@@ -8,6 +8,10 @@ import { EnvVars, getAllEnvs } from '../../application-control';
|
|
|
8
8
|
import { DeviceAgentCloudConnection } from '../../cloud-connection/device-agent-cloud-connection';
|
|
9
9
|
import sleep from '../../util/sleep';
|
|
10
10
|
import { logger } from '../../util/logger';
|
|
11
|
+
import {
|
|
12
|
+
buildUpdateProjectShadowMessage,
|
|
13
|
+
getShadowTopic
|
|
14
|
+
} from '@alwaysai/device-agent-schemas';
|
|
11
15
|
|
|
12
16
|
export const getAllEnvsCliLeaf = CliLeaf({
|
|
13
17
|
name: 'get-all-envs',
|
|
@@ -43,7 +47,7 @@ export const setEnvCliLeaf = CliLeaf({
|
|
|
43
47
|
})
|
|
44
48
|
},
|
|
45
49
|
async action(args, opts) {
|
|
46
|
-
const { project, service } = opts;
|
|
50
|
+
const { project: projectId, service } = opts;
|
|
47
51
|
const envVars: EnvVars = { [service]: {} };
|
|
48
52
|
args.forEach((arg: string) => {
|
|
49
53
|
const nameVal = arg.split('=');
|
|
@@ -56,26 +60,24 @@ export const setEnvCliLeaf = CliLeaf({
|
|
|
56
60
|
const deviceAgent = new DeviceAgentCloudConnection();
|
|
57
61
|
await deviceAgent.setupHandlers();
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
desired: {
|
|
64
|
-
[project]: {
|
|
65
|
-
envVars
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
clientToken: 'client'
|
|
63
|
+
const toDesire = {
|
|
64
|
+
[projectId]: {
|
|
65
|
+
envVars
|
|
66
|
+
}
|
|
70
67
|
};
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
deviceAgent.publisher.publish(
|
|
69
|
+
getShadowTopic(deviceAgent.getClientId(), 'projects', 'update'),
|
|
70
|
+
JSON.stringify(
|
|
71
|
+
buildUpdateProjectShadowMessage({
|
|
72
|
+
clientId: 'client',
|
|
73
|
+
desired: toDesire
|
|
74
|
+
})
|
|
75
|
+
)
|
|
73
76
|
);
|
|
74
|
-
deviceAgent.publisher.publish(topic, JSON.stringify(packet));
|
|
75
77
|
// Sleep for extra time to ensure time for shadow response
|
|
76
78
|
await sleep(10000);
|
|
77
79
|
|
|
78
|
-
while (deviceAgent.isCmdInProgress(
|
|
80
|
+
while (deviceAgent.isCmdInProgress(projectId)) {
|
|
79
81
|
await sleep(1000);
|
|
80
82
|
}
|
|
81
83
|
await deviceAgent.stop();
|
|
@@ -14,6 +14,11 @@ import {
|
|
|
14
14
|
import { DeviceAgentCloudConnection } from '../../cloud-connection/device-agent-cloud-connection';
|
|
15
15
|
import sleep from '../../util/sleep';
|
|
16
16
|
import { logger } from '../../util/logger';
|
|
17
|
+
import {
|
|
18
|
+
buildUpdateProjectShadowMessage,
|
|
19
|
+
getShadowTopic,
|
|
20
|
+
validateShadowProjectsUpdateAll
|
|
21
|
+
} from '@alwaysai/device-agent-schemas';
|
|
17
22
|
|
|
18
23
|
export const showAppModelsCliLeaf = CliLeaf({
|
|
19
24
|
name: 'show-models',
|
|
@@ -49,34 +54,33 @@ export const addModelCliLeaf = CliLeaf({
|
|
|
49
54
|
})
|
|
50
55
|
},
|
|
51
56
|
async action(_, opts) {
|
|
52
|
-
const { project, model, version } = opts;
|
|
57
|
+
const { project: projectId, model, version } = opts;
|
|
53
58
|
const deviceAgent = new DeviceAgentCloudConnection();
|
|
54
59
|
await deviceAgent.setupHandlers();
|
|
55
60
|
|
|
56
61
|
// Update the shadow as a client
|
|
57
|
-
const topic = deviceAgent.getShadowTopics().projects.update;
|
|
58
62
|
|
|
59
|
-
const newAppCfg = await readAppCfgFile({ projectId:
|
|
63
|
+
const newAppCfg = await readAppCfgFile({ projectId: projectId });
|
|
60
64
|
newAppCfg.models[model] = Number(version);
|
|
61
65
|
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
appConfig: JSON.stringify(newAppCfg)
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
clientToken: 'client'
|
|
66
|
+
const toDesire = {
|
|
67
|
+
[projectId]: {
|
|
68
|
+
appConfig: JSON.stringify(newAppCfg)
|
|
69
|
+
}
|
|
71
70
|
};
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
deviceAgent.publisher.publish(
|
|
72
|
+
getShadowTopic(deviceAgent.getClientId(), 'projects', 'update'),
|
|
73
|
+
JSON.stringify(
|
|
74
|
+
buildUpdateProjectShadowMessage({
|
|
75
|
+
clientId: 'client',
|
|
76
|
+
desired: toDesire
|
|
77
|
+
})
|
|
78
|
+
)
|
|
74
79
|
);
|
|
75
|
-
deviceAgent.publisher.publish(topic, JSON.stringify(packet));
|
|
76
80
|
// Sleep for extra time to ensure time for shadow response
|
|
77
81
|
await sleep(10000);
|
|
78
82
|
|
|
79
|
-
while (deviceAgent.isCmdInProgress(
|
|
83
|
+
while (deviceAgent.isCmdInProgress(projectId)) {
|
|
80
84
|
await sleep(1000);
|
|
81
85
|
}
|
|
82
86
|
await deviceAgent.stop();
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from '../../local-connection/rabbitmq-connection';
|
|
15
15
|
import { stopApp } from '../../application-control';
|
|
16
16
|
import { LOCAL_CERT_AND_KEY_DIR } from 'alwaysai/lib/paths';
|
|
17
|
-
import safeRimraf from '../../util/
|
|
17
|
+
import { safeRimraf } from '../../util/file';
|
|
18
18
|
|
|
19
19
|
export const cleanCliLeaf = CliLeaf({
|
|
20
20
|
name: 'clean',
|
|
@@ -36,10 +36,13 @@ export const cleanCliLeaf = CliLeaf({
|
|
|
36
36
|
|
|
37
37
|
logger.debug('Checking for alwaysAI applications still running');
|
|
38
38
|
const apps = await AgentConfigFile().getApps();
|
|
39
|
+
const stopAppPromises: Promise<void>[] = [];
|
|
39
40
|
for (const app of apps) {
|
|
40
41
|
const { projectId } = app;
|
|
41
|
-
|
|
42
|
+
stopAppPromises.push(stopApp({ projectId }));
|
|
42
43
|
}
|
|
44
|
+
await Promise.all(stopAppPromises);
|
|
45
|
+
|
|
43
46
|
await safeRimraf(LOCAL_CERT_AND_KEY_DIR);
|
|
44
47
|
|
|
45
48
|
logger.debug(`Removing ${AgentConfigFile().path}`);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { CliLeaf } from '@alwaysai/alwayscli';
|
|
2
|
+
import {
|
|
3
|
+
getCpuDetails,
|
|
4
|
+
getDiskDetails,
|
|
5
|
+
getMemDetails,
|
|
6
|
+
getSystemInformation
|
|
7
|
+
} from '../../device-control/device-control';
|
|
8
|
+
|
|
9
|
+
export const getInfoCliLeaf = CliLeaf({
|
|
10
|
+
name: 'get-info',
|
|
11
|
+
description: 'Get device info',
|
|
12
|
+
namedInputs: {},
|
|
13
|
+
async action(_, opts) {
|
|
14
|
+
const cpuDetails = await getCpuDetails();
|
|
15
|
+
const diskDetails = await getDiskDetails();
|
|
16
|
+
const memDetails = await getMemDetails();
|
|
17
|
+
const out = {
|
|
18
|
+
'CPU Utilization': `${
|
|
19
|
+
cpuDetails?.usedPerc && cpuDetails.temperature
|
|
20
|
+
? `Used ${cpuDetails?.usedPerc?.toFixed(2)}%, Temperature ${
|
|
21
|
+
cpuDetails?.temperature
|
|
22
|
+
} °C`
|
|
23
|
+
: 'Unavailable'
|
|
24
|
+
}`,
|
|
25
|
+
'Disk Utilization': `${
|
|
26
|
+
diskDetails?.usedGb && diskDetails.freeGb
|
|
27
|
+
? `${diskDetails?.usedGb} GB / ${
|
|
28
|
+
diskDetails?.usedGb + diskDetails?.freeGb
|
|
29
|
+
} GB`
|
|
30
|
+
: 'Unavailable'
|
|
31
|
+
}`,
|
|
32
|
+
'Memory Utilization': `${
|
|
33
|
+
memDetails?.usedMb && memDetails.freeMb
|
|
34
|
+
? `${memDetails.usedMb} MB / ${
|
|
35
|
+
memDetails.usedMb + memDetails.freeMb
|
|
36
|
+
} MB`
|
|
37
|
+
: 'Unavailable'
|
|
38
|
+
}`
|
|
39
|
+
};
|
|
40
|
+
console.table(out);
|
|
41
|
+
const systemInfo = await getSystemInformation();
|
|
42
|
+
console.table(systemInfo.os);
|
|
43
|
+
console.table(systemInfo.cpu);
|
|
44
|
+
console.table(systemInfo.disk?.drives);
|
|
45
|
+
console.table(systemInfo.device);
|
|
46
|
+
console.table(systemInfo.network);
|
|
47
|
+
console.table(systemInfo.versions);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { CliBranch } from '@alwaysai/alwayscli';
|
|
2
|
-
import { getInfoCliLeaf
|
|
2
|
+
import { getInfoCliLeaf } from './get-info';
|
|
3
3
|
import { cleanCliLeaf } from './clean';
|
|
4
|
+
import { initCliLeaf } from './init';
|
|
5
|
+
import { restartCliLeaf } from './restart';
|
|
6
|
+
import { refreshCliLeaf } from './refresh';
|
|
4
7
|
|
|
5
8
|
export const deviceCliBranch = CliBranch({
|
|
6
9
|
name: 'device',
|
|
7
10
|
description: 'Manage current device',
|
|
8
|
-
subcommands: [
|
|
11
|
+
subcommands: [
|
|
12
|
+
initCliLeaf,
|
|
13
|
+
getInfoCliLeaf,
|
|
14
|
+
refreshCliLeaf,
|
|
15
|
+
cleanCliLeaf,
|
|
16
|
+
restartCliLeaf
|
|
17
|
+
]
|
|
9
18
|
});
|
|
@@ -11,13 +11,6 @@ import {
|
|
|
11
11
|
} from '../../util/directories';
|
|
12
12
|
|
|
13
13
|
import { JsSpawner } from 'alwaysai/lib/util';
|
|
14
|
-
import {
|
|
15
|
-
getCpuDetails,
|
|
16
|
-
getDiskDetails,
|
|
17
|
-
getMemDetails,
|
|
18
|
-
getSystemInformation,
|
|
19
|
-
reboot
|
|
20
|
-
} from '../../device-control/device-control';
|
|
21
14
|
import { writeTokenAndDeviceCfg } from '../../infrastructure/tokens-and-device-cfg';
|
|
22
15
|
import { logger } from '../../util/logger';
|
|
23
16
|
import { DeviceConfigFile } from 'alwaysai/lib/core/device';
|
|
@@ -95,49 +88,14 @@ export const initCliLeaf = CliLeaf({
|
|
|
95
88
|
const certSpawner = JsSpawner({
|
|
96
89
|
path: BOOTSTRAP_CERTIFICATES_DIR_PATH()
|
|
97
90
|
});
|
|
91
|
+
const writeCertFilePromises: Promise<void>[] = [];
|
|
98
92
|
for (const key in response.claimCertificate) {
|
|
99
|
-
|
|
93
|
+
writeCertFilePromises.push(
|
|
94
|
+
certSpawner.writeFile(key, response.claimCertificate[key])
|
|
95
|
+
);
|
|
100
96
|
}
|
|
97
|
+
await Promise.all(writeCertFilePromises);
|
|
101
98
|
|
|
102
99
|
logger.info(`Initialized device ${name}: UUID=${response.deviceUuid}`);
|
|
103
100
|
}
|
|
104
101
|
});
|
|
105
|
-
|
|
106
|
-
export const getInfoCliLeaf = CliLeaf({
|
|
107
|
-
name: 'get-info',
|
|
108
|
-
description: 'Get device info',
|
|
109
|
-
namedInputs: {},
|
|
110
|
-
async action(_, opts) {
|
|
111
|
-
const cpuDetails = await getCpuDetails();
|
|
112
|
-
const diskDetails = await getDiskDetails();
|
|
113
|
-
const memDetails = await getMemDetails();
|
|
114
|
-
const out = {
|
|
115
|
-
'CPU Utilization': `Used ${cpuDetails.usedPerc.toFixed(
|
|
116
|
-
2
|
|
117
|
-
)}%, Temperature ${cpuDetails.temperature} °C`,
|
|
118
|
-
'Disk Utilization': `${diskDetails.usedGb} GB / ${
|
|
119
|
-
diskDetails.usedGb + diskDetails.freeGb
|
|
120
|
-
} GB`,
|
|
121
|
-
'Memory Utilization': `${memDetails.usedMb} MB / ${
|
|
122
|
-
memDetails.usedMb + memDetails.freeMb
|
|
123
|
-
} MB`
|
|
124
|
-
};
|
|
125
|
-
console.table(out);
|
|
126
|
-
const systemInfo = await getSystemInformation();
|
|
127
|
-
console.table(systemInfo.os);
|
|
128
|
-
console.table(systemInfo.cpu);
|
|
129
|
-
console.table(systemInfo.disk?.drives);
|
|
130
|
-
console.table(systemInfo.device);
|
|
131
|
-
console.table(systemInfo.network);
|
|
132
|
-
console.table(systemInfo.versions);
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
export const restartCliLeaf = CliLeaf({
|
|
137
|
-
name: 'restart',
|
|
138
|
-
description: 'Restart the device',
|
|
139
|
-
namedInputs: {},
|
|
140
|
-
async action(_, opts) {
|
|
141
|
-
await reboot();
|
|
142
|
-
}
|
|
143
|
-
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { CliLeaf, CliTerseError } from '@alwaysai/alwayscli';
|
|
2
|
+
import { writeTokenAndDeviceCfg } from '../../infrastructure/tokens-and-device-cfg';
|
|
3
|
+
import { logger } from '../../util/logger';
|
|
4
|
+
import { DeviceConfigFile } from 'alwaysai/lib/core/device';
|
|
5
|
+
|
|
6
|
+
export const refreshCliLeaf = CliLeaf({
|
|
7
|
+
name: 'refresh',
|
|
8
|
+
description: 'Refresh the device tokens',
|
|
9
|
+
namedInputs: {},
|
|
10
|
+
async action(_, opts) {
|
|
11
|
+
logger.info(`Refreshing device tokens`);
|
|
12
|
+
const cfg = DeviceConfigFile().readIfExists();
|
|
13
|
+
if (cfg === undefined) {
|
|
14
|
+
throw new CliTerseError('Configuration not found on current device!');
|
|
15
|
+
}
|
|
16
|
+
const deviceUuid = cfg.deviceUuid;
|
|
17
|
+
|
|
18
|
+
await writeTokenAndDeviceCfg({
|
|
19
|
+
deviceUuid
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { CliLeaf } from '@alwaysai/alwayscli';
|
|
2
|
+
import { reboot } from '../../device-control/device-control';
|
|
3
|
+
|
|
4
|
+
export const restartCliLeaf = CliLeaf({
|
|
5
|
+
name: 'restart',
|
|
6
|
+
description: 'Restart the device',
|
|
7
|
+
namedInputs: {},
|
|
8
|
+
async action(_, opts) {
|
|
9
|
+
await reboot();
|
|
10
|
+
}
|
|
11
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { logger } from '../util/logger';
|
|
2
|
+
import { JsSpawner } from 'alwaysai/lib/util';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { get } from 'https';
|
|
5
|
+
|
|
6
|
+
const appPrompt =
|
|
7
|
+
'Please restart the Device Agent or reboot the device to update. ';
|
|
8
|
+
const updatePrompt = `A new version of the Device Agent is available! \n\n`;
|
|
9
|
+
|
|
10
|
+
function getLatestVersion(packageName: string): Promise<string> {
|
|
11
|
+
return new Promise<string>((resolve, reject) => {
|
|
12
|
+
const url = `https://registry.npmjs.org/${packageName}/latest`;
|
|
13
|
+
|
|
14
|
+
get(url, (res) => {
|
|
15
|
+
let data = '';
|
|
16
|
+
res.on('data', (chunk) => {
|
|
17
|
+
data += chunk;
|
|
18
|
+
});
|
|
19
|
+
res.on('end', () => {
|
|
20
|
+
try {
|
|
21
|
+
const latestPackage = JSON.parse(data);
|
|
22
|
+
resolve(latestPackage.version);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
reject(error);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}).on('error', (error) => {
|
|
28
|
+
reject(error);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function getDeviceAgentVersion(): Promise<string> {
|
|
34
|
+
let daVersion = '';
|
|
35
|
+
try {
|
|
36
|
+
const packagePath = path.join(__dirname, '../../');
|
|
37
|
+
const spawner = JsSpawner({ path: packagePath });
|
|
38
|
+
const output = await spawner.readFile('package.json');
|
|
39
|
+
const parsed = JSON.parse(output);
|
|
40
|
+
daVersion = parsed.version;
|
|
41
|
+
} catch (e) {
|
|
42
|
+
logger.error(`Could not retrieve the Device Agent version: $ {e}`);
|
|
43
|
+
}
|
|
44
|
+
return daVersion;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function constructPrompt(props: { currentVer: string; latestVer: string }) {
|
|
48
|
+
const { currentVer, latestVer } = props;
|
|
49
|
+
const prompt = `${updatePrompt}${appPrompt}\nYour version: ${currentVer}. Newest version: ${latestVer}`;
|
|
50
|
+
return prompt;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function checkForUpdatesAndPrompt() {
|
|
54
|
+
try {
|
|
55
|
+
logger.debug('Checking for updates...');
|
|
56
|
+
const latestVersion = await getLatestVersion('@alwaysai/device-agent');
|
|
57
|
+
const version = await getDeviceAgentVersion();
|
|
58
|
+
if (version !== latestVersion) {
|
|
59
|
+
logger.warn(
|
|
60
|
+
constructPrompt({
|
|
61
|
+
currentVer: version,
|
|
62
|
+
latestVer: latestVersion
|
|
63
|
+
})
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
logger.error(`Could not check for updates: ${error}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -5,6 +5,42 @@ import {
|
|
|
5
5
|
DEVICE_CERTIFICATE_FILE_PATH
|
|
6
6
|
} from '../util/directories';
|
|
7
7
|
|
|
8
|
+
const VALID_AWS_REGIONS = [
|
|
9
|
+
// 'af-south-1', // Africa (Cape Town)
|
|
10
|
+
// 'ap-east-1', // Asia Pacific (Hong Kong)
|
|
11
|
+
// 'ap-south-2', // Asia Pacific (Hyderabad)
|
|
12
|
+
// 'ap-northeast-1', // Asia Pacific (Tokyo)
|
|
13
|
+
// 'ap-northeast-2', // Asia Pacific (Seoul)
|
|
14
|
+
// 'ap-northeast-3', // Asia Pacific (Osaka)
|
|
15
|
+
// 'ap-south-1', // Asia Pacific (Mumbai)
|
|
16
|
+
// 'ap-southeast-1', // Asia Pacific (Singapore)
|
|
17
|
+
// 'ap-southeast-2', // Asia Pacific (Sydney)
|
|
18
|
+
// 'ap-southeast-3', // Asia Pacific (Jakarta)
|
|
19
|
+
// 'ap-southeast-4', // Asia Pacific (Melbourne)
|
|
20
|
+
// 'ca-central-1', // Canada (Central)
|
|
21
|
+
// 'ca-west-1', // Canada West (Calgary)
|
|
22
|
+
// 'eu-west-2', // Europe (London)
|
|
23
|
+
// 'eu-west-3', // Europe (Paris)
|
|
24
|
+
// 'eu-central-1', // Europe (Frankfurt)
|
|
25
|
+
// 'eu-central-2', // Europe (Zurich)
|
|
26
|
+
// 'eu-north-1', // Europe (Stockholm)
|
|
27
|
+
// 'eu-south-1', // Europe (Milan)
|
|
28
|
+
// 'eu-south-2', // Europe (Spain)
|
|
29
|
+
// 'eu-west-1', // Europe (Ireland)
|
|
30
|
+
// 'il-central-1', // Israel (Tel Aviv)
|
|
31
|
+
// 'me-south-1', // Middle East (Bahrain)
|
|
32
|
+
// 'me-central-1', // Middle East (UAE)
|
|
33
|
+
// 'sa-east-1' // South America (São Paulo)
|
|
34
|
+
// 'us-east-2', // US East (Ohio)
|
|
35
|
+
// 'us-east-1', // US East (Virginia)
|
|
36
|
+
// 'us-west-1', // US West (N. California)
|
|
37
|
+
'us-west-2' // US West (Oregon)
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
export function isValidAwsRegion(region: string): boolean {
|
|
41
|
+
return VALID_AWS_REGIONS.includes(region);
|
|
42
|
+
}
|
|
43
|
+
|
|
8
44
|
export function cloudModeReady() {
|
|
9
45
|
let ready = true;
|
|
10
46
|
const requiredFiles = [
|