@alwaysai/device-agent 2.0.2-0 → 2.0.3
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 +1 -1
- package/lib/application-control/config.js.map +1 -1
- package/lib/application-control/install.d.ts.map +1 -1
- package/lib/application-control/install.js +7 -3
- package/lib/application-control/install.js.map +1 -1
- package/lib/cloud-connection/base-message-handler.d.ts.map +1 -1
- package/lib/cloud-connection/base-message-handler.js +5 -4
- package/lib/cloud-connection/base-message-handler.js.map +1 -1
- package/lib/cloud-connection/connection-manager.d.ts +3 -4
- package/lib/cloud-connection/connection-manager.d.ts.map +1 -1
- package/lib/cloud-connection/connection-manager.js +22 -40
- package/lib/cloud-connection/connection-manager.js.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts +3 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +52 -46
- package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
- package/lib/cloud-connection/device-agent-message-handler.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-message-handler.js +19 -18
- package/lib/cloud-connection/device-agent-message-handler.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
- package/lib/cloud-connection/live-updates-handler.js +11 -4
- package/lib/cloud-connection/live-updates-handler.js.map +1 -1
- package/lib/cloud-connection/passthrough-handler.d.ts +3 -3
- package/lib/cloud-connection/passthrough-handler.d.ts.map +1 -1
- package/lib/cloud-connection/passthrough-handler.js +97 -74
- package/lib/cloud-connection/passthrough-handler.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.js +3 -3
- package/lib/cloud-connection/shadow-handler.js.map +1 -1
- package/lib/cloud-connection/shadow.d.ts.map +1 -1
- package/lib/cloud-connection/shadow.js +1 -1
- package/lib/cloud-connection/shadow.js.map +1 -1
- package/lib/cloud-connection/transaction-manager.d.ts.map +1 -1
- package/lib/cloud-connection/transaction-manager.js +17 -7
- package/lib/cloud-connection/transaction-manager.js.map +1 -1
- package/lib/cloud-connection/transaction-manager.test.js +52 -44
- package/lib/cloud-connection/transaction-manager.test.js.map +1 -1
- package/lib/device-control/device-control.d.ts.map +1 -1
- package/lib/device-control/device-control.js +13 -9
- package/lib/device-control/device-control.js.map +1 -1
- package/lib/docker/docker-compose.d.ts.map +1 -1
- package/lib/docker/docker-compose.js +1 -1
- package/lib/docker/docker-compose.js.map +1 -1
- package/lib/environment.d.ts +3 -0
- package/lib/environment.d.ts.map +1 -1
- package/lib/environment.js +12 -1
- package/lib/environment.js.map +1 -1
- package/lib/index.js +12 -0
- package/lib/index.js.map +1 -1
- package/lib/infrastructure/config-check-utility.js +2 -2
- package/lib/infrastructure/config-check-utility.js.map +1 -1
- package/lib/infrastructure/legacy-migration/legacy-migration.d.ts.map +1 -1
- package/lib/infrastructure/legacy-migration/legacy-migration.js +6 -10
- package/lib/infrastructure/legacy-migration/legacy-migration.js.map +1 -1
- package/lib/jobs/job-handler.d.ts +1 -1
- package/lib/jobs/job-handler.d.ts.map +1 -1
- package/lib/jobs/job-handler.js +4 -4
- package/lib/jobs/job-handler.js.map +1 -1
- package/lib/local-connection/rabbitmq-container.d.ts +6 -0
- package/lib/local-connection/rabbitmq-container.d.ts.map +1 -0
- package/lib/local-connection/rabbitmq-container.js +111 -0
- package/lib/local-connection/rabbitmq-container.js.map +1 -0
- package/lib/local-connection/rabbitmq-container.test.d.ts +2 -0
- package/lib/local-connection/rabbitmq-container.test.d.ts.map +1 -0
- package/lib/local-connection/rabbitmq-container.test.js +219 -0
- package/lib/local-connection/rabbitmq-container.test.js.map +1 -0
- package/lib/subcommands/app/analytics.d.ts.map +1 -1
- package/lib/subcommands/app/analytics.js +2 -3
- 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 +4 -6
- 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 +2 -3
- package/lib/subcommands/app/models.js.map +1 -1
- package/lib/subcommands/app/shadow.d.ts.map +1 -1
- package/lib/subcommands/app/shadow.js +4 -6
- package/lib/subcommands/app/shadow.js.map +1 -1
- package/lib/subcommands/app/version.d.ts.map +1 -1
- package/lib/subcommands/app/version.js +6 -9
- package/lib/subcommands/app/version.js.map +1 -1
- package/lib/subcommands/device/clean.d.ts.map +1 -1
- package/lib/subcommands/device/clean.js +15 -17
- package/lib/subcommands/device/clean.js.map +1 -1
- package/lib/subcommands/device/index.d.ts.map +1 -1
- package/lib/subcommands/device/index.js +3 -1
- package/lib/subcommands/device/index.js.map +1 -1
- package/lib/subcommands/device/local-connection.d.ts +2 -0
- package/lib/subcommands/device/local-connection.d.ts.map +1 -0
- package/lib/subcommands/device/local-connection.js +17 -0
- package/lib/subcommands/device/local-connection.js.map +1 -0
- package/lib/subcommands/index.d.ts +4 -1
- package/lib/subcommands/index.d.ts.map +1 -1
- package/lib/subcommands/index.js +1 -3
- package/lib/subcommands/index.js.map +1 -1
- package/lib/util/check-for-updates.d.ts.map +1 -1
- package/lib/util/check-for-updates.js +2 -2
- package/lib/util/check-for-updates.js.map +1 -1
- package/lib/util/file.d.ts.map +1 -1
- package/lib/util/file.js +6 -1
- package/lib/util/file.js.map +1 -1
- package/lib/util/file.test.js +1 -1
- package/lib/util/file.test.js.map +1 -1
- package/lib/util/get-device-id.d.ts.map +1 -1
- package/lib/util/get-device-id.js +1 -1
- package/lib/util/get-device-id.js.map +1 -1
- package/package.json +2 -2
- package/readme.md +16 -4
- package/src/application-control/config.ts +1 -1
- package/src/application-control/install.ts +11 -5
- package/src/cloud-connection/base-message-handler.ts +10 -5
- package/src/cloud-connection/connection-manager.ts +23 -45
- package/src/cloud-connection/device-agent-cloud-connection.ts +97 -89
- package/src/cloud-connection/device-agent-message-handler.ts +10 -7
- package/src/cloud-connection/live-updates-handler.ts +12 -5
- package/src/cloud-connection/passthrough-handler.ts +79 -38
- package/src/cloud-connection/shadow-handler.ts +3 -3
- package/src/cloud-connection/shadow.ts +3 -1
- package/src/cloud-connection/transaction-manager.test.ts +60 -41
- package/src/cloud-connection/transaction-manager.ts +26 -12
- package/src/device-control/device-control.ts +23 -13
- package/src/docker/docker-compose.ts +3 -1
- package/src/environment.ts +17 -0
- package/src/index.ts +19 -0
- package/src/infrastructure/config-check-utility.ts +2 -2
- package/src/infrastructure/legacy-migration/legacy-migration.ts +8 -13
- package/src/jobs/job-handler.ts +4 -4
- package/src/local-connection/rabbitmq-container.test.ts +255 -0
- package/src/local-connection/rabbitmq-container.ts +151 -0
- package/src/subcommands/app/analytics.ts +2 -3
- package/src/subcommands/app/env-vars.ts +4 -6
- package/src/subcommands/app/models.ts +2 -3
- package/src/subcommands/app/shadow.ts +4 -6
- package/src/subcommands/app/version.ts +7 -8
- package/src/subcommands/device/clean.ts +20 -19
- package/src/subcommands/device/index.ts +3 -1
- package/src/subcommands/device/local-connection.ts +16 -0
- package/src/subcommands/index.ts +1 -3
- package/src/util/check-for-updates.ts +4 -2
- package/src/util/file.test.ts +1 -1
- package/src/util/file.ts +7 -1
- package/src/util/get-device-id.ts +3 -1
- package/lib/local-connection/rabbitmq-connection.d.ts +0 -7
- package/lib/local-connection/rabbitmq-connection.d.ts.map +0 -1
- package/lib/local-connection/rabbitmq-connection.js +0 -95
- package/lib/local-connection/rabbitmq-connection.js.map +0 -1
- package/lib/subcommands/rabbitmq-connection.d.ts +0 -2
- package/lib/subcommands/rabbitmq-connection.d.ts.map +0 -1
- package/lib/subcommands/rabbitmq-connection.js +0 -14
- package/lib/subcommands/rabbitmq-connection.js.map +0 -1
- package/src/local-connection/rabbitmq-connection.ts +0 -124
- package/src/subcommands/rabbitmq-connection.ts +0 -11
|
@@ -16,7 +16,8 @@ import {
|
|
|
16
16
|
appCleanDocker,
|
|
17
17
|
getPythonVenvPaths,
|
|
18
18
|
createPythonVenv,
|
|
19
|
-
installPythonReqs
|
|
19
|
+
installPythonReqs,
|
|
20
|
+
TargetHardware
|
|
20
21
|
} from 'alwaysai/lib/core/app';
|
|
21
22
|
import { DOCKER_IMAGE_ID_INITIAL_VALUE } from 'alwaysai/lib/constants';
|
|
22
23
|
import { runInDir } from '../util/run-in-dir';
|
|
@@ -30,8 +31,10 @@ import {
|
|
|
30
31
|
} from '../local-connection/constants';
|
|
31
32
|
import { DOCKERFILE, PYTHON_REQUIREMENTS_FILE_NAME } from 'alwaysai/lib/paths';
|
|
32
33
|
import { downloadToFile } from '../util/file';
|
|
34
|
+
import { ALWAYSAI_TARGET_HW_OVERRIDE, parseTargetHW } from '../environment';
|
|
33
35
|
import { setEnvInternal } from './environment-variables';
|
|
34
36
|
import { AppConfig } from '@alwaysai/app-configuration-schemas';
|
|
37
|
+
import { CliTerseError } from '@alwaysai/alwayscli';
|
|
35
38
|
|
|
36
39
|
type SignedUrlPayloadType = {
|
|
37
40
|
appInstallPayload: {
|
|
@@ -80,7 +83,7 @@ export async function installApp(props: {
|
|
|
80
83
|
await stopApp({ projectId });
|
|
81
84
|
} catch (e) {
|
|
82
85
|
logger.error(
|
|
83
|
-
`Could not stop the application. Old container might still be present after installation
|
|
86
|
+
`Could not stop the application. Old container might still be present after installation. Error:\n${stringifyError(
|
|
84
87
|
e
|
|
85
88
|
)}`
|
|
86
89
|
);
|
|
@@ -234,15 +237,18 @@ async function checkValidProjectFiles({ appDir }) {
|
|
|
234
237
|
if (!AppJsonFile(appDir).readIfExists()) {
|
|
235
238
|
throw new Error('App JSON file does not exist!');
|
|
236
239
|
}
|
|
237
|
-
|
|
238
240
|
// write target json
|
|
239
241
|
if (!fs.existsSync(path.join(appDir, DOCKERFILE))) {
|
|
240
242
|
throw new Error('No Dockerfile found for application!');
|
|
241
243
|
}
|
|
244
|
+
const targetHw: TargetHardware =
|
|
245
|
+
parseTargetHW(ALWAYSAI_TARGET_HW_OVERRIDE) ??
|
|
246
|
+
(await getTargetHardwareType({}));
|
|
247
|
+
logger.debug(`Target Hardware selected to: ${targetHw}`);
|
|
242
248
|
TargetJsonFile(appDir).write({
|
|
243
249
|
targetProtocol: 'docker:',
|
|
244
250
|
dockerImageId: DOCKER_IMAGE_ID_INITIAL_VALUE,
|
|
245
|
-
targetHardware:
|
|
251
|
+
targetHardware: targetHw
|
|
246
252
|
});
|
|
247
253
|
}
|
|
248
254
|
|
|
@@ -260,7 +266,7 @@ export async function uninstallApp(props: {
|
|
|
260
266
|
await stopApp({ projectId });
|
|
261
267
|
} catch (e) {
|
|
262
268
|
logger.warn(
|
|
263
|
-
`Failed to stop ${projectId}, may be left running
|
|
269
|
+
`Failed to stop ${projectId}, may be left running... Error:\n${stringifyError(
|
|
264
270
|
e
|
|
265
271
|
)}`
|
|
266
272
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { stringifyError } from 'alwaysai/lib/util';
|
|
2
2
|
import { uninstallApp, rollbackApp } from '../application-control';
|
|
3
3
|
import { createAppBackup } from '../application-control/backup';
|
|
4
4
|
import { AgentConfigFile } from '../infrastructure/agent-config';
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
ErrorFunction,
|
|
13
13
|
SuccessFunction
|
|
14
14
|
} from './transaction-manager';
|
|
15
|
+
import { logger } from '../util/logger';
|
|
15
16
|
|
|
16
17
|
export interface HandlerContext {
|
|
17
18
|
clientId: string;
|
|
@@ -52,7 +53,9 @@ export abstract class BaseHandler {
|
|
|
52
53
|
await uninstallApp({ projectId });
|
|
53
54
|
this.shadowHandler.clearProjectShadow(projectId);
|
|
54
55
|
} catch (e) {
|
|
55
|
-
logger.error(
|
|
56
|
+
logger.error(
|
|
57
|
+
`Failed to uninstall ${projectId}! Error:\n${stringifyError(e)}`
|
|
58
|
+
);
|
|
56
59
|
throw e;
|
|
57
60
|
}
|
|
58
61
|
}
|
|
@@ -73,7 +76,7 @@ export abstract class BaseHandler {
|
|
|
73
76
|
await createAppBackup({ projectId });
|
|
74
77
|
} catch (e) {
|
|
75
78
|
logger.error(
|
|
76
|
-
`Could not create a backup for the project: ${projectId}
|
|
79
|
+
`Could not create a backup for the project: ${projectId}! Error:\n${stringifyError(
|
|
77
80
|
e
|
|
78
81
|
)}`
|
|
79
82
|
);
|
|
@@ -87,14 +90,16 @@ export abstract class BaseHandler {
|
|
|
87
90
|
return out;
|
|
88
91
|
} catch (errorAppUpdate) {
|
|
89
92
|
logger.error(
|
|
90
|
-
`Failed to update ${projectId}
|
|
93
|
+
`Failed to update ${projectId}! Error:\n${stringifyError(
|
|
94
|
+
errorAppUpdate
|
|
95
|
+
)}`
|
|
91
96
|
);
|
|
92
97
|
// If something goes wrong, first try to rollback
|
|
93
98
|
try {
|
|
94
99
|
await rollbackApp({ projectId });
|
|
95
100
|
} catch (errorRollbackApp) {
|
|
96
101
|
logger.error(
|
|
97
|
-
`Application rollback failed for ${projectId}
|
|
102
|
+
`Application rollback failed for ${projectId}! Error:\n${stringifyError(
|
|
98
103
|
errorRollbackApp
|
|
99
104
|
)}`
|
|
100
105
|
);
|
|
@@ -16,40 +16,32 @@ export class ConnectionManager extends MessageDispatcher<any> {
|
|
|
16
16
|
private clientId: string;
|
|
17
17
|
private host: string;
|
|
18
18
|
private port: number;
|
|
19
|
-
private device: awsIot.device
|
|
19
|
+
private device: awsIot.device;
|
|
20
20
|
private subscribedTopics: Set<string> = new Set();
|
|
21
|
+
private connected = false;
|
|
21
22
|
|
|
22
23
|
constructor(clientId: string, host: string, port: number) {
|
|
23
24
|
super();
|
|
24
25
|
this.clientId = clientId;
|
|
25
26
|
this.host = host;
|
|
26
27
|
this.port = port;
|
|
28
|
+
this.device = awsIot.device({
|
|
29
|
+
keyPath: DEVICE_PRIVATE_KEY_FILE_PATH,
|
|
30
|
+
certPath: DEVICE_CERTIFICATE_FILE_PATH,
|
|
31
|
+
caPath: AWS_ROOT_CERTIFICATE_FILE_PATH,
|
|
32
|
+
clientId: this.clientId,
|
|
33
|
+
host: this.host,
|
|
34
|
+
port: this.port,
|
|
35
|
+
keepalive: 10 // time before re-connect attempt on dropped connection, default is 400 seconds
|
|
36
|
+
});
|
|
27
37
|
}
|
|
28
38
|
|
|
29
39
|
public getIoTDevice() {
|
|
30
40
|
return this.device;
|
|
31
41
|
}
|
|
32
42
|
|
|
33
|
-
public connect(cb): void {
|
|
34
|
-
try {
|
|
35
|
-
this.device = awsIot.device({
|
|
36
|
-
keyPath: DEVICE_PRIVATE_KEY_FILE_PATH,
|
|
37
|
-
certPath: DEVICE_CERTIFICATE_FILE_PATH,
|
|
38
|
-
caPath: AWS_ROOT_CERTIFICATE_FILE_PATH,
|
|
39
|
-
clientId: this.clientId,
|
|
40
|
-
host: this.host,
|
|
41
|
-
port: this.port,
|
|
42
|
-
keepalive: 10 // time before re-connect attempt on dropped connection, default is 400 seconds
|
|
43
|
-
});
|
|
44
|
-
this.setupHandlers(cb);
|
|
45
|
-
} catch (error) {
|
|
46
|
-
logger.error(`Error connecting to cloud!\n${stringifyError(error)}`);
|
|
47
|
-
this.handleReconnection();
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
43
|
public isConnected(): boolean {
|
|
52
|
-
return this.
|
|
44
|
+
return this.connected;
|
|
53
45
|
}
|
|
54
46
|
|
|
55
47
|
public registerHandler(topic: string, handler: MessageHandler) {
|
|
@@ -58,16 +50,11 @@ export class ConnectionManager extends MessageDispatcher<any> {
|
|
|
58
50
|
}
|
|
59
51
|
|
|
60
52
|
public disconnect(): void {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
logger.debug(`Device Agent has been disconnected from the AWS IoT Core.`);
|
|
64
|
-
}
|
|
53
|
+
this.device.end();
|
|
54
|
+
logger.debug(`Device Agent has been disconnected from the AWS IoT Core.`);
|
|
65
55
|
}
|
|
66
56
|
|
|
67
57
|
public subscribe(topic: string): void {
|
|
68
|
-
if (!this.device) {
|
|
69
|
-
throw new Error('Must call connect() before subscribe()!');
|
|
70
|
-
}
|
|
71
58
|
if (!this.subscribedTopics.has(topic)) {
|
|
72
59
|
this.device.subscribe(topic);
|
|
73
60
|
this.subscribedTopics.add(topic);
|
|
@@ -76,9 +63,6 @@ export class ConnectionManager extends MessageDispatcher<any> {
|
|
|
76
63
|
}
|
|
77
64
|
|
|
78
65
|
public unsubscribe(topic: string): void {
|
|
79
|
-
if (!this.device) {
|
|
80
|
-
throw new Error('Must call connect() before unsubscribe()!');
|
|
81
|
-
}
|
|
82
66
|
if (this.subscribedTopics.has(topic)) {
|
|
83
67
|
this.device.unsubscribe(topic);
|
|
84
68
|
this.subscribedTopics.delete(topic);
|
|
@@ -86,25 +70,16 @@ export class ConnectionManager extends MessageDispatcher<any> {
|
|
|
86
70
|
}
|
|
87
71
|
}
|
|
88
72
|
|
|
89
|
-
|
|
90
|
-
logger.debug(`Attempting to reconnect to AWS IoT Core...`);
|
|
91
|
-
setTimeout(
|
|
92
|
-
() =>
|
|
93
|
-
this.connect(() => {
|
|
94
|
-
// Do nothing
|
|
95
|
-
}),
|
|
96
|
-
5000
|
|
97
|
-
); // try in 5 seconds.
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
private setupHandlers(cb): void {
|
|
73
|
+
public initConnectionHandlers(connectCallback: () => void): void {
|
|
101
74
|
this.device.on('connect', (connack: any) => {
|
|
102
75
|
logger.info('Device Agent has connected to the cloud.');
|
|
103
|
-
|
|
76
|
+
this.connected = true;
|
|
77
|
+
connectCallback();
|
|
104
78
|
});
|
|
105
79
|
|
|
106
80
|
this.device.on('disconnect', () => {
|
|
107
81
|
logger.warn('Device Agent has been disconnected from the cloud');
|
|
82
|
+
this.connected = false;
|
|
108
83
|
});
|
|
109
84
|
|
|
110
85
|
this.device.on('reconnect', () => {
|
|
@@ -115,17 +90,20 @@ export class ConnectionManager extends MessageDispatcher<any> {
|
|
|
115
90
|
|
|
116
91
|
this.device.on('error', function (e) {
|
|
117
92
|
logger.error(
|
|
118
|
-
`Error connecting to the AWS IoT Core
|
|
93
|
+
`Error connecting to the AWS IoT Core! Error:\n${stringifyError(e)}`
|
|
119
94
|
);
|
|
95
|
+
this.connected = false;
|
|
120
96
|
});
|
|
121
97
|
|
|
122
98
|
this.device.on('close', () => {
|
|
123
99
|
logger.warn('Device Agent AWS IoT Core connection closed.');
|
|
100
|
+
this.connected = false;
|
|
124
101
|
});
|
|
125
102
|
|
|
126
103
|
this.device.on('offline', () => {
|
|
127
104
|
logger.warn(`Device Agent is offline ${new Date().toLocaleString()}`);
|
|
128
105
|
void this.logConnectionInfo();
|
|
106
|
+
this.connected = false;
|
|
129
107
|
});
|
|
130
108
|
|
|
131
109
|
this.device.on('message', async (topic: string, payload: string) => {
|
|
@@ -136,7 +114,7 @@ export class ConnectionManager extends MessageDispatcher<any> {
|
|
|
136
114
|
const jsonPacket = JSON.parse(payload);
|
|
137
115
|
this.dispatch(topic, jsonPacket);
|
|
138
116
|
} catch (e) {
|
|
139
|
-
logger.error(`Error parsing message
|
|
117
|
+
logger.error(`Error parsing message! Error:\n${stringifyError(e)}`);
|
|
140
118
|
}
|
|
141
119
|
});
|
|
142
120
|
}
|
|
@@ -41,8 +41,6 @@ export class DeviceAgentCloudConnection {
|
|
|
41
41
|
SecureTunnelHandlerSingleton.getInstance();
|
|
42
42
|
|
|
43
43
|
constructor() {
|
|
44
|
-
this.liveUpdatesHandler = new LiveUpdatesHandler();
|
|
45
|
-
|
|
46
44
|
// Initialize & setup the connection
|
|
47
45
|
this.connectionManager = new ConnectionManager(
|
|
48
46
|
this.clientId,
|
|
@@ -52,92 +50,91 @@ export class DeviceAgentCloudConnection {
|
|
|
52
50
|
|
|
53
51
|
this.publisher = new Publisher(this.connectionManager, this.clientId);
|
|
54
52
|
this.shadowHandler = new ShadowHandler(this.clientId, this.publisher);
|
|
55
|
-
|
|
56
|
-
this.connectionManager.connect(() => {
|
|
57
|
-
void this.shadowHandler.initShadows();
|
|
58
|
-
this.publisher.publish(JOB_HANDLER_TOPICS.START_NEXT, JSON.stringify({}));
|
|
59
|
-
});
|
|
60
|
-
|
|
53
|
+
this.liveUpdatesHandler = new LiveUpdatesHandler();
|
|
61
54
|
this.transactionManager = new TransactionManager(this.liveUpdatesHandler);
|
|
62
55
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
56
|
+
this.connectionManager.initConnectionHandlers(() => {
|
|
57
|
+
// Construct a HandlerContext used by all the message handlers
|
|
58
|
+
const handlerContext: HandlerContext = {
|
|
59
|
+
clientId: this.clientId,
|
|
60
|
+
txnMgr: this.transactionManager,
|
|
61
|
+
publisher: this.publisher,
|
|
62
|
+
shadowHandler: this.shadowHandler,
|
|
63
|
+
liveUpdatesHandler: this.liveUpdatesHandler,
|
|
64
|
+
secureTunnelHandler: this.secureTunnelHandler
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// Instantiate & register message handlers for Project Shadow topics
|
|
68
|
+
const projectShadowMessageHandler = new ProjectShadowMessageHandler(
|
|
69
|
+
handlerContext
|
|
70
|
+
);
|
|
71
|
+
this.shadowHandler.projectShadowTopics.forEach((topic) => {
|
|
72
|
+
this.connectionManager.registerHandler(
|
|
73
|
+
topic,
|
|
74
|
+
projectShadowMessageHandler
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Instantiate & register message handlers for to-device and secureTunnel topics
|
|
78
79
|
this.connectionManager.registerHandler(
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
this.toDeviceTopic,
|
|
81
|
+
new DeviceAgentMessageHandler(
|
|
82
|
+
handlerContext,
|
|
83
|
+
(txId: string, errorMsg: string) => {
|
|
84
|
+
const msg = buildToClientStatusResponseMessage(
|
|
85
|
+
this.publisher.getClientId(),
|
|
86
|
+
{
|
|
87
|
+
status: keyMirrors.statusResponse.failure,
|
|
88
|
+
message: errorMsg
|
|
89
|
+
},
|
|
90
|
+
txId
|
|
91
|
+
);
|
|
92
|
+
this.publisher.publishToClient(msg);
|
|
93
|
+
},
|
|
94
|
+
(txId: string) => {
|
|
95
|
+
const msg = buildToClientStatusResponseMessage(
|
|
96
|
+
this.publisher.getClientId(),
|
|
97
|
+
{ status: keyMirrors.statusResponse.success },
|
|
98
|
+
txId
|
|
99
|
+
);
|
|
100
|
+
this.publisher.publishToClient(msg);
|
|
101
|
+
}
|
|
102
|
+
)
|
|
81
103
|
);
|
|
82
|
-
});
|
|
83
104
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
this.publisher.getClientId(),
|
|
92
|
-
{
|
|
93
|
-
status: keyMirrors.statusResponse.failure,
|
|
94
|
-
message: errorMsg
|
|
95
|
-
},
|
|
96
|
-
txId
|
|
97
|
-
);
|
|
98
|
-
this.publisher.publishToClient(msg);
|
|
99
|
-
},
|
|
100
|
-
(txId: string) => {
|
|
101
|
-
const msg = buildToClientStatusResponseMessage(
|
|
102
|
-
this.publisher.getClientId(),
|
|
103
|
-
{ status: keyMirrors.statusResponse.success },
|
|
104
|
-
txId
|
|
105
|
-
);
|
|
106
|
-
this.publisher.publishToClient(msg);
|
|
107
|
-
}
|
|
108
|
-
)
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
const secureTunnelMessageHandler = new SecureTunnelMessageHandler(
|
|
112
|
-
handlerContext
|
|
113
|
-
);
|
|
114
|
-
this.connectionManager.registerHandler(
|
|
115
|
-
secureTunnelMessageHandler.getNotifyTopic(),
|
|
116
|
-
secureTunnelMessageHandler
|
|
117
|
-
);
|
|
105
|
+
const secureTunnelMessageHandler = new SecureTunnelMessageHandler(
|
|
106
|
+
handlerContext
|
|
107
|
+
);
|
|
108
|
+
this.connectionManager.registerHandler(
|
|
109
|
+
secureTunnelMessageHandler.getNotifyTopic(),
|
|
110
|
+
secureTunnelMessageHandler
|
|
111
|
+
);
|
|
118
112
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
113
|
+
this.connectionManager.registerHandler(
|
|
114
|
+
this.shadowHandler.shadowTopics.secureTunnel.updateDelta,
|
|
115
|
+
secureTunnelMessageHandler
|
|
116
|
+
);
|
|
117
|
+
this.connectionManager.registerHandler(
|
|
118
|
+
this.shadowHandler.shadowTopics.secureTunnel.deleteAccepted,
|
|
119
|
+
secureTunnelMessageHandler
|
|
120
|
+
);
|
|
127
121
|
|
|
128
|
-
|
|
122
|
+
const jobHandler = new JobHandler(handlerContext);
|
|
123
|
+
const JOB_HANDLER_TOPICS = jobHandler.getJobTopics();
|
|
129
124
|
|
|
130
|
-
|
|
125
|
+
this.connectionManager.registerHandler(
|
|
126
|
+
JOB_HANDLER_TOPICS.NOTIFY_NEXT,
|
|
127
|
+
jobHandler
|
|
128
|
+
);
|
|
131
129
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
130
|
+
this.connectionManager.registerHandler(
|
|
131
|
+
JOB_HANDLER_TOPICS.START_NEXT_ACCEPTED,
|
|
132
|
+
jobHandler
|
|
133
|
+
);
|
|
134
|
+
this.publisher.publish(JOB_HANDLER_TOPICS.START_NEXT, JSON.stringify({}));
|
|
136
135
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
jobHandler
|
|
140
|
-
);
|
|
136
|
+
void this.shadowHandler.initShadows();
|
|
137
|
+
});
|
|
141
138
|
}
|
|
142
139
|
|
|
143
140
|
/*=================================================================
|
|
@@ -158,7 +155,22 @@ export class DeviceAgentCloudConnection {
|
|
|
158
155
|
await sleep(1000);
|
|
159
156
|
this.connectionManager.disconnect();
|
|
160
157
|
}
|
|
161
|
-
|
|
158
|
+
|
|
159
|
+
// CLI methods
|
|
160
|
+
|
|
161
|
+
public async waitForConnection() {
|
|
162
|
+
while (!this.connectionManager.isConnected()) {
|
|
163
|
+
await sleep(1000);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
public async waitForCmd(projectId: string) {
|
|
168
|
+
while (this.isCmdInProgress(projectId)) {
|
|
169
|
+
await sleep(1000);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
public async sendCliCmd(topic: string, message: any) {
|
|
162
174
|
this.connectionManager.dispatch(topic, message);
|
|
163
175
|
}
|
|
164
176
|
}
|
|
@@ -179,16 +191,12 @@ export async function runDeviceAgentCloudInterface() {
|
|
|
179
191
|
}
|
|
180
192
|
|
|
181
193
|
if (await requiredConfigFilesPresentAndValid()) {
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
shadowHandler
|
|
189
|
-
);
|
|
190
|
-
await passthroughHandler.setup();
|
|
191
|
-
}
|
|
194
|
+
const cloudConnection = new DeviceAgentCloudConnection();
|
|
195
|
+
const passthroughHandler = new PassthroughHandler(
|
|
196
|
+
cloudConnection.publisher,
|
|
197
|
+
cloudConnection.shadowHandler
|
|
198
|
+
);
|
|
199
|
+
await passthroughHandler.run();
|
|
192
200
|
} else {
|
|
193
201
|
throw new Error(
|
|
194
202
|
"Set device agent to local mode and retry the 'aai-agent device init' command"
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
ModelsInstallResponseMessage
|
|
28
28
|
} from '@alwaysai/device-agent-schemas/lib/app-action-schema';
|
|
29
29
|
import { DeviceActionMessage } from '@alwaysai/device-agent-schemas/lib/device-action-schema';
|
|
30
|
-
import {
|
|
30
|
+
import { stringifyError } from 'alwaysai/lib/util';
|
|
31
31
|
import {
|
|
32
32
|
startApp,
|
|
33
33
|
stopApp,
|
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
AppConfig,
|
|
46
46
|
validateAppConfig
|
|
47
47
|
} from '@alwaysai/app-configuration-schemas';
|
|
48
|
+
import { logger } from '../util/logger';
|
|
48
49
|
|
|
49
50
|
export type AppContent = {
|
|
50
51
|
projectId: string;
|
|
@@ -160,7 +161,7 @@ class AppStateMessageHandler
|
|
|
160
161
|
});
|
|
161
162
|
} catch (e) {
|
|
162
163
|
logger.error(
|
|
163
|
-
`Error processing application state control request for ${projectId}
|
|
164
|
+
`Error processing application state control request for ${projectId}! Error:\n${stringifyError(
|
|
164
165
|
e
|
|
165
166
|
)}`
|
|
166
167
|
);
|
|
@@ -215,7 +216,7 @@ class AppVersionControlMessageHandler
|
|
|
215
216
|
});
|
|
216
217
|
} catch (e) {
|
|
217
218
|
logger.error(
|
|
218
|
-
`Error processing application install request for ${projectId}
|
|
219
|
+
`Error processing application install request for ${projectId}! Error:\n${stringifyError(
|
|
219
220
|
e
|
|
220
221
|
)}`
|
|
221
222
|
);
|
|
@@ -310,7 +311,9 @@ class AppVersionControlMessageHandler
|
|
|
310
311
|
}
|
|
311
312
|
} catch (e) {
|
|
312
313
|
logger.error(
|
|
313
|
-
`Could not parse the appConfig for transaction
|
|
314
|
+
`Could not parse the appConfig for transaction! Error:\n${stringifyError(
|
|
315
|
+
e
|
|
316
|
+
)}`
|
|
314
317
|
);
|
|
315
318
|
}
|
|
316
319
|
}
|
|
@@ -340,9 +343,9 @@ class AppVersionControlMessageHandler
|
|
|
340
343
|
appContent.envVars = envvarsUpdate;
|
|
341
344
|
}
|
|
342
345
|
} catch (e) {
|
|
343
|
-
// throw here
|
|
346
|
+
// TODO: throw here
|
|
344
347
|
logger.error(
|
|
345
|
-
`Could not parse the environment variables for transaction
|
|
348
|
+
`Could not parse the environment variables for transaction! Error:\n${stringifyError(
|
|
346
349
|
e
|
|
347
350
|
)}`
|
|
348
351
|
);
|
|
@@ -610,7 +613,7 @@ class DeviceActionMessageHandler
|
|
|
610
613
|
logger.error(
|
|
611
614
|
`There was a problem performing device action '${
|
|
612
615
|
message.payload.action
|
|
613
|
-
}'
|
|
616
|
+
}'! Error: \n${stringifyError(e)}`
|
|
614
617
|
);
|
|
615
618
|
this.publisher.publishToClient(
|
|
616
619
|
buildToClientStatusResponseMessage(
|
|
@@ -14,6 +14,9 @@ interface IntervalOptions {
|
|
|
14
14
|
ms?: number;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/*
|
|
18
|
+
Responsible for managing the lifecycle of periodic updates and streams.
|
|
19
|
+
*/
|
|
17
20
|
export class LiveUpdatesHandler {
|
|
18
21
|
private killAllTimeout: ReturnType<typeof setTimeout>;
|
|
19
22
|
private livingIntervals: Record<string, ReturnType<typeof setInterval>> = {};
|
|
@@ -31,12 +34,14 @@ export class LiveUpdatesHandler {
|
|
|
31
34
|
transactionId?: string,
|
|
32
35
|
options?: IntervalOptions
|
|
33
36
|
) {
|
|
37
|
+
logger.silly(`LiveUpdatesHandler: Enabling ${intervalType}`);
|
|
34
38
|
this.restartKillAllTimeout();
|
|
35
39
|
|
|
36
40
|
const key = this.generateIntervalKey(intervalType, transactionId);
|
|
37
41
|
|
|
38
42
|
this.safeSetInterval(key, publishingFn, options);
|
|
39
43
|
|
|
44
|
+
// Call publishing function right away for immediate results
|
|
40
45
|
await publishingFn();
|
|
41
46
|
}
|
|
42
47
|
|
|
@@ -44,6 +49,7 @@ export class LiveUpdatesHandler {
|
|
|
44
49
|
intervalType: ToClientMessageTypeValue,
|
|
45
50
|
transactionId?: string
|
|
46
51
|
) {
|
|
52
|
+
logger.silly(`LiveUpdatesHandler: Disabling ${intervalType}`);
|
|
47
53
|
const key = this.generateIntervalKey(intervalType, transactionId);
|
|
48
54
|
clearInterval(this.livingIntervals[key]);
|
|
49
55
|
delete this.livingIntervals[key];
|
|
@@ -54,7 +60,7 @@ export class LiveUpdatesHandler {
|
|
|
54
60
|
streamGetter: () => Promise<NodeJS.ReadableStream | null>,
|
|
55
61
|
publishingFn: (logChunk: string) => void
|
|
56
62
|
) {
|
|
57
|
-
logger.info(`Starting log stream for ${projectId}`);
|
|
63
|
+
logger.info(`LiveUpdatesHandler: Starting log stream for ${projectId}`);
|
|
58
64
|
|
|
59
65
|
this.livingStreams.add(projectId);
|
|
60
66
|
|
|
@@ -84,7 +90,7 @@ export class LiveUpdatesHandler {
|
|
|
84
90
|
});
|
|
85
91
|
|
|
86
92
|
readable.on('finished', () => {
|
|
87
|
-
logger.info(`
|
|
93
|
+
logger.info(`Stream complete. ProjectId: ${projectId}`);
|
|
88
94
|
});
|
|
89
95
|
}
|
|
90
96
|
|
|
@@ -105,8 +111,8 @@ export class LiveUpdatesHandler {
|
|
|
105
111
|
try {
|
|
106
112
|
return await streamGetter();
|
|
107
113
|
} catch (e) {
|
|
108
|
-
logger.
|
|
109
|
-
`Failed to start app logs, retrying in 1 second
|
|
114
|
+
logger.error(
|
|
115
|
+
`Failed to start app logs, retrying in 1 second. Error:\n${stringifyError(
|
|
110
116
|
e
|
|
111
117
|
)}`
|
|
112
118
|
);
|
|
@@ -117,7 +123,8 @@ export class LiveUpdatesHandler {
|
|
|
117
123
|
return null;
|
|
118
124
|
}
|
|
119
125
|
|
|
120
|
-
//
|
|
126
|
+
// Do not await any functions in setSafeInterval other than inside
|
|
127
|
+
// setInterval() as per EI-1694.
|
|
121
128
|
private safeSetInterval(
|
|
122
129
|
key: string,
|
|
123
130
|
publishingFn: () => Promise<void>,
|