@alwaysai/device-agent 0.0.9 → 0.0.11
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/backup.d.ts.map +1 -1
- package/lib/application-control/backup.js +4 -3
- package/lib/application-control/backup.js.map +1 -1
- package/lib/application-control/install.d.ts.map +1 -1
- package/lib/application-control/install.js +6 -5
- package/lib/application-control/install.js.map +1 -1
- package/lib/application-control/models.d.ts.map +1 -1
- package/lib/application-control/models.js +3 -2
- 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 +6 -5
- package/lib/application-control/status.js.map +1 -1
- package/lib/application-control/utils.d.ts.map +1 -1
- package/lib/application-control/utils.js +3 -2
- package/lib/application-control/utils.js.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts +3 -3
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +90 -50
- package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
- package/lib/cloud-connection/device-agent.d.ts +21 -0
- package/lib/cloud-connection/device-agent.d.ts.map +1 -0
- package/lib/cloud-connection/device-agent.js +69 -0
- package/lib/cloud-connection/device-agent.js.map +1 -0
- package/lib/endpoints.d.ts +3 -0
- package/lib/endpoints.d.ts.map +1 -0
- package/lib/endpoints.js +28 -0
- package/lib/endpoints.js.map +1 -0
- package/lib/environment.d.ts +1 -0
- package/lib/environment.d.ts.map +1 -1
- package/lib/environment.js +2 -1
- package/lib/environment.js.map +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/infrastructure/certificates-and-tokens.d.ts +1 -1
- package/lib/infrastructure/certificates-and-tokens.d.ts.map +1 -1
- package/lib/infrastructure/certificates-and-tokens.js +13 -39
- package/lib/infrastructure/certificates-and-tokens.js.map +1 -1
- package/lib/subcommands/app/app.d.ts.map +1 -1
- package/lib/subcommands/app/app.js +6 -5
- package/lib/subcommands/app/app.js.map +1 -1
- package/lib/subcommands/device/device.d.ts.map +1 -1
- package/lib/subcommands/device/device.js +31 -22
- package/lib/subcommands/device/device.js.map +1 -1
- package/lib/subcommands/get-model-package.d.ts.map +1 -1
- package/lib/subcommands/get-model-package.js +2 -1
- package/lib/subcommands/get-model-package.js.map +1 -1
- package/lib/util/directories.d.ts +15 -0
- package/lib/util/directories.d.ts.map +1 -1
- package/lib/util/directories.js +19 -4
- package/lib/util/directories.js.map +1 -1
- package/lib/util/http-client.d.ts +3 -0
- package/lib/util/http-client.d.ts.map +1 -0
- package/lib/util/http-client.js +30 -0
- package/lib/util/http-client.js.map +1 -0
- package/lib/util/logger.d.ts +4 -0
- package/lib/util/logger.d.ts.map +1 -0
- package/lib/util/logger.js +25 -0
- package/lib/util/logger.js.map +1 -0
- package/package.json +2 -1
- package/src/application-control/backup.ts +4 -3
- package/src/application-control/install.ts +6 -5
- package/src/application-control/models.ts +4 -3
- package/src/application-control/status.ts +6 -5
- package/src/application-control/utils.ts +3 -2
- package/src/cloud-connection/device-agent-cloud-connection.ts +175 -81
- package/src/cloud-connection/device-agent.ts +122 -0
- package/src/endpoints.ts +24 -0
- package/src/environment.ts +1 -0
- package/src/index.ts +2 -1
- package/src/infrastructure/certificates-and-tokens.ts +31 -49
- package/src/subcommands/app/app.ts +6 -5
- package/src/subcommands/device/device.ts +67 -30
- package/src/subcommands/get-model-package.ts +2 -1
- package/src/util/directories.ts +49 -6
- package/src/util/http-client.ts +35 -0
- package/src/util/logger.ts +28 -0
|
@@ -1,23 +1,32 @@
|
|
|
1
|
-
const awsIot = require(
|
|
2
|
-
import { getIoTCoreEndpointUrl } from
|
|
1
|
+
const awsIot = require("aws-iot-device-sdk");
|
|
2
|
+
import { getIoTCoreEndpointUrl } from "../infrastructure/urls";
|
|
3
|
+
import { existsSync, createReadStream } from "fs";
|
|
3
4
|
import {
|
|
4
5
|
getPrivateKeyFilePath,
|
|
5
6
|
getCertificateFilePath,
|
|
6
7
|
getRootCertificateFilePath,
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
BOOTSTRAP_CLAIM_ID_FILE_PATH,
|
|
9
|
+
DEVICE_CLAIM_ID_FILE_PATH,
|
|
10
|
+
BOOTSTRAP_DEVICE_CERTIFICATE_FILE_PATH,
|
|
11
|
+
BOOTSTRAP_DEVICE_PRIVATE_KEY_FILE_PATH,
|
|
12
|
+
AWS_ROOT_CERTIFICATE_FILE_PATH,
|
|
13
|
+
} from "../util/directories";
|
|
14
|
+
|
|
15
|
+
import { BootstrapAgent } from "./device-agent";
|
|
16
|
+
|
|
17
|
+
import sleep from "../util/sleep";
|
|
9
18
|
import {
|
|
10
19
|
startApp,
|
|
11
20
|
stopApp,
|
|
12
21
|
restartApp,
|
|
13
22
|
getAppLogs,
|
|
14
23
|
getAppStatus,
|
|
15
|
-
} from
|
|
24
|
+
} from "../application-control/status";
|
|
16
25
|
import {
|
|
17
26
|
getInstalledApps,
|
|
18
27
|
installApp,
|
|
19
28
|
uninstallApp,
|
|
20
|
-
} from
|
|
29
|
+
} from "../application-control/install";
|
|
21
30
|
import {
|
|
22
31
|
keyMirrors,
|
|
23
32
|
validateClientMessage,
|
|
@@ -37,14 +46,22 @@ import {
|
|
|
37
46
|
DeviceAgentMessage,
|
|
38
47
|
ClientMessage,
|
|
39
48
|
AppDetailsPacket,
|
|
40
|
-
} from
|
|
41
|
-
import { getDeviceId } from
|
|
42
|
-
import { JsSpawner, logger } from
|
|
43
|
-
import {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
} from "@alwaysai/device-agent-schemas";
|
|
50
|
+
import { getDeviceId } from "../util/get-device-id";
|
|
51
|
+
import { JsSpawner, logger } from "alwaysai/lib/util";
|
|
52
|
+
import {
|
|
53
|
+
getCpuUtil,
|
|
54
|
+
getDiskUtil,
|
|
55
|
+
getMemUtil,
|
|
56
|
+
} from "../device-control/device-control";
|
|
57
|
+
import { AgentConfigFile } from "../infrastructure/agent-config";
|
|
58
|
+
import {
|
|
59
|
+
buildApp,
|
|
60
|
+
getAppConfig,
|
|
61
|
+
getAppDir,
|
|
62
|
+
} from "../application-control/utils";
|
|
63
|
+
import { updateModelsWithPresignedUrls } from "../application-control/models";
|
|
64
|
+
import { updateAppConfig } from "../application-control/config";
|
|
48
65
|
|
|
49
66
|
export class DeviceAgentCloudConnection {
|
|
50
67
|
private clientId = getDeviceId();
|
|
@@ -63,7 +80,7 @@ export class DeviceAgentCloudConnection {
|
|
|
63
80
|
[keyMirrors.agentMessageType.app_install_status]: 5000,
|
|
64
81
|
};
|
|
65
82
|
private appLogStreams = new Set<string>();
|
|
66
|
-
private deviceType =
|
|
83
|
+
private deviceType = "aai-device";
|
|
67
84
|
private readonly shadowPrefix = `$aws/things/${this.clientId}/shadow/name/`;
|
|
68
85
|
private readonly shadowTopics = {
|
|
69
86
|
projects: {
|
|
@@ -71,9 +88,9 @@ export class DeviceAgentCloudConnection {
|
|
|
71
88
|
getAccepted: `${this.shadowPrefix}projects/get/accepted`,
|
|
72
89
|
},
|
|
73
90
|
};
|
|
74
|
-
private readonly toCloudTopic = `
|
|
75
|
-
private readonly toClientTopic = `
|
|
76
|
-
private readonly toDeviceTopic = `
|
|
91
|
+
private readonly toCloudTopic = `to-cloud/${this.deviceType}/${this.clientId}`;
|
|
92
|
+
private readonly toClientTopic = `to-client/${this.deviceType}/${this.clientId}`;
|
|
93
|
+
private readonly toDeviceTopic = `to-device/${this.deviceType}/${this.clientId}`;
|
|
77
94
|
|
|
78
95
|
// device shadow utils
|
|
79
96
|
|
|
@@ -123,9 +140,9 @@ export class DeviceAgentCloudConnection {
|
|
|
123
140
|
this.appLogStreams.add(projectId);
|
|
124
141
|
const readable = await getAppLogs({
|
|
125
142
|
projectId,
|
|
126
|
-
args: [
|
|
143
|
+
args: ["--tail", "100", "--no-log-prefix"],
|
|
127
144
|
});
|
|
128
|
-
readable.on(
|
|
145
|
+
readable.on("data", (chunk: Buffer) => {
|
|
129
146
|
if (!this.appLogStreams.has(projectId)) {
|
|
130
147
|
// why doesn't typescript know about this function?
|
|
131
148
|
// @ts-ignore
|
|
@@ -144,16 +161,18 @@ export class DeviceAgentCloudConnection {
|
|
|
144
161
|
const packet = this.buildMessagePacket(
|
|
145
162
|
this.getClientId(),
|
|
146
163
|
this.toClientTopic,
|
|
147
|
-
message
|
|
164
|
+
message
|
|
148
165
|
);
|
|
149
166
|
this.publishMessage(this.toClientTopic, JSON.stringify(packet));
|
|
150
167
|
});
|
|
151
168
|
|
|
152
|
-
readable.on(
|
|
153
|
-
logger.error(
|
|
169
|
+
readable.on("error", (error) => {
|
|
170
|
+
logger.error(
|
|
171
|
+
`App log stream terminated for project ${projectId}: ${error}`
|
|
172
|
+
);
|
|
154
173
|
});
|
|
155
174
|
|
|
156
|
-
readable.on(
|
|
175
|
+
readable.on("finished", () => {
|
|
157
176
|
logger.info(`App logs finished piping for project ${projectId}`);
|
|
158
177
|
});
|
|
159
178
|
}
|
|
@@ -164,7 +183,7 @@ export class DeviceAgentCloudConnection {
|
|
|
164
183
|
}
|
|
165
184
|
|
|
166
185
|
private updateAppInstallStatus(
|
|
167
|
-
installationStatus: Omit<AppInstallStatusPacket,
|
|
186
|
+
installationStatus: Omit<AppInstallStatusPacket, "appReleaseHash">
|
|
168
187
|
) {
|
|
169
188
|
this.appInstallStatus.status = installationStatus.status;
|
|
170
189
|
this.appInstallStatus.message = installationStatus.message;
|
|
@@ -178,7 +197,7 @@ export class DeviceAgentCloudConnection {
|
|
|
178
197
|
private buildMessagePacket(
|
|
179
198
|
deviceId: string,
|
|
180
199
|
topic: string,
|
|
181
|
-
payload: DeviceAgentMessagePayload
|
|
200
|
+
payload: DeviceAgentMessagePayload
|
|
182
201
|
): DeviceAgentMessage {
|
|
183
202
|
const packet = {
|
|
184
203
|
timestamp: new Date().toUTCString(),
|
|
@@ -221,27 +240,34 @@ export class DeviceAgentCloudConnection {
|
|
|
221
240
|
}
|
|
222
241
|
|
|
223
242
|
// must be arrow function due to this context when function is passed as param
|
|
224
|
-
private getAppInstallStatusMessage =
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
243
|
+
private getAppInstallStatusMessage =
|
|
244
|
+
async (): Promise<AppInstallStatusMessage> => {
|
|
245
|
+
const appInstallStatus = this.getAppInstallStatus();
|
|
246
|
+
const appInstallStatusMessage = {
|
|
247
|
+
messageType: keyMirrors.agentMessageType.app_install_status,
|
|
248
|
+
appInstallStatus,
|
|
249
|
+
};
|
|
250
|
+
return appInstallStatusMessage;
|
|
229
251
|
};
|
|
230
|
-
return appInstallStatusMessage;
|
|
231
|
-
};
|
|
232
252
|
|
|
233
253
|
private async startPublishingLiveUpdates(
|
|
234
254
|
topic: string,
|
|
235
255
|
messageType: string,
|
|
236
|
-
getMessageData: () => Promise<DeviceAgentMessagePayload
|
|
256
|
+
getMessageData: () => Promise<DeviceAgentMessagePayload>
|
|
237
257
|
) {
|
|
238
258
|
while (true) {
|
|
239
259
|
try {
|
|
240
260
|
const message = await getMessageData();
|
|
241
|
-
const packet = this.buildMessagePacket(
|
|
261
|
+
const packet = this.buildMessagePacket(
|
|
262
|
+
this.getClientId(),
|
|
263
|
+
topic,
|
|
264
|
+
message
|
|
265
|
+
);
|
|
242
266
|
this.publishMessage(topic, JSON.stringify(packet));
|
|
243
267
|
} catch (e) {
|
|
244
|
-
logger.error(
|
|
268
|
+
logger.error(
|
|
269
|
+
`Error publishing live updates for ${messageType}: ${e.message}`
|
|
270
|
+
);
|
|
245
271
|
break;
|
|
246
272
|
}
|
|
247
273
|
if (!this.continuePublishing(messageType)) {
|
|
@@ -258,7 +284,10 @@ export class DeviceAgentCloudConnection {
|
|
|
258
284
|
case keyMirrors.agentMessageType.app_state:
|
|
259
285
|
return this.liveUpdatesAlive[flag];
|
|
260
286
|
case keyMirrors.agentMessageType.app_install_status:
|
|
261
|
-
return
|
|
287
|
+
return (
|
|
288
|
+
this.appInstallStatus.status ===
|
|
289
|
+
keyMirrors.appInstallStatus.in_progress
|
|
290
|
+
);
|
|
262
291
|
default:
|
|
263
292
|
logger.error(`Unrecognized publishable flag ${flag}`);
|
|
264
293
|
return false;
|
|
@@ -317,7 +346,9 @@ export class DeviceAgentCloudConnection {
|
|
|
317
346
|
this.liveUpdatesBroker(message.liveUpdatesToggles);
|
|
318
347
|
break;
|
|
319
348
|
default:
|
|
320
|
-
logger.error(
|
|
349
|
+
logger.error(
|
|
350
|
+
`Invalid agent action message type from message '${message}'`
|
|
351
|
+
);
|
|
321
352
|
}
|
|
322
353
|
}
|
|
323
354
|
|
|
@@ -352,7 +383,7 @@ export class DeviceAgentCloudConnection {
|
|
|
352
383
|
this.startPublishingLiveUpdates(
|
|
353
384
|
this.toClientTopic,
|
|
354
385
|
keyMirrors.agentMessageType.device_stats,
|
|
355
|
-
this.getDeviceStatsMessage
|
|
386
|
+
this.getDeviceStatsMessage
|
|
356
387
|
);
|
|
357
388
|
}
|
|
358
389
|
}
|
|
@@ -363,7 +394,7 @@ export class DeviceAgentCloudConnection {
|
|
|
363
394
|
this.startPublishingLiveUpdates(
|
|
364
395
|
this.toClientTopic,
|
|
365
396
|
keyMirrors.agentMessageType.app_state,
|
|
366
|
-
this.getAppStateMessage
|
|
397
|
+
this.getAppStateMessage
|
|
367
398
|
);
|
|
368
399
|
}
|
|
369
400
|
}
|
|
@@ -386,7 +417,10 @@ export class DeviceAgentCloudConnection {
|
|
|
386
417
|
},
|
|
387
418
|
},
|
|
388
419
|
};
|
|
389
|
-
this.publishMessage(
|
|
420
|
+
this.publishMessage(
|
|
421
|
+
`${this.shadowPrefix}projects/update`,
|
|
422
|
+
JSON.stringify(packet)
|
|
423
|
+
);
|
|
390
424
|
}
|
|
391
425
|
|
|
392
426
|
private async publishCloudRequest(payload: SignedUrlsRequestMessage) {
|
|
@@ -394,7 +428,7 @@ export class DeviceAgentCloudConnection {
|
|
|
394
428
|
const deviceRequestPacket = this.buildMessagePacket(
|
|
395
429
|
this.getClientId(),
|
|
396
430
|
topic,
|
|
397
|
-
payload
|
|
431
|
+
payload
|
|
398
432
|
);
|
|
399
433
|
this.publishMessage(topic, JSON.stringify({ deviceRequestPacket }));
|
|
400
434
|
}
|
|
@@ -407,9 +441,10 @@ export class DeviceAgentCloudConnection {
|
|
|
407
441
|
this.device = awsIot.device({
|
|
408
442
|
keyPath: getPrivateKeyFilePath(),
|
|
409
443
|
certPath: getCertificateFilePath(),
|
|
410
|
-
caPath:
|
|
444
|
+
caPath: AWS_ROOT_CERTIFICATE_FILE_PATH,
|
|
411
445
|
clientId: this.clientId,
|
|
412
446
|
host: this.host,
|
|
447
|
+
port: 8883,
|
|
413
448
|
});
|
|
414
449
|
|
|
415
450
|
this.device.subscribe(this.toDeviceTopic);
|
|
@@ -423,7 +458,7 @@ export class DeviceAgentCloudConnection {
|
|
|
423
458
|
|
|
424
459
|
public publishMessage(topic: string, message: string) {
|
|
425
460
|
// TODO: topic validation
|
|
426
|
-
this.device.publish(topic, message);
|
|
461
|
+
this.device.publish(topic, message, (err: any) => {});
|
|
427
462
|
}
|
|
428
463
|
|
|
429
464
|
public async handleClientMessage({
|
|
@@ -448,8 +483,12 @@ export class DeviceAgentCloudConnection {
|
|
|
448
483
|
break;
|
|
449
484
|
}
|
|
450
485
|
case keyMirrors.clientMessageType.app_install_cloud_response: {
|
|
451
|
-
const {
|
|
452
|
-
|
|
486
|
+
const {
|
|
487
|
+
projectId,
|
|
488
|
+
appReleaseHash,
|
|
489
|
+
appInstallPayload,
|
|
490
|
+
modelsInstallPayload,
|
|
491
|
+
} = payload.appInstallCloudResponse;
|
|
453
492
|
|
|
454
493
|
this.initAppInstallStatus({
|
|
455
494
|
status: keyMirrors.appInstallStatus.in_progress,
|
|
@@ -459,7 +498,7 @@ export class DeviceAgentCloudConnection {
|
|
|
459
498
|
this.startPublishingLiveUpdates(
|
|
460
499
|
this.toClientTopic,
|
|
461
500
|
keyMirrors.agentMessageType.app_install_status,
|
|
462
|
-
this.getAppInstallStatusMessage
|
|
501
|
+
this.getAppInstallStatusMessage
|
|
463
502
|
);
|
|
464
503
|
|
|
465
504
|
// Install the app and models
|
|
@@ -480,7 +519,7 @@ export class DeviceAgentCloudConnection {
|
|
|
480
519
|
// update app config shadow for project
|
|
481
520
|
await this.publishReportedState(projectId);
|
|
482
521
|
} catch (e) {
|
|
483
|
-
|
|
522
|
+
logger.error(e);
|
|
484
523
|
const message: string = e.message;
|
|
485
524
|
|
|
486
525
|
// uninstall the failed app to put system back in good state
|
|
@@ -491,7 +530,7 @@ export class DeviceAgentCloudConnection {
|
|
|
491
530
|
});
|
|
492
531
|
|
|
493
532
|
// delete shadow for project
|
|
494
|
-
this.publishMessage(`${this.shadowPrefix}${projectId}/delete`,
|
|
533
|
+
this.publishMessage(`${this.shadowPrefix}${projectId}/delete`, "");
|
|
495
534
|
}
|
|
496
535
|
break;
|
|
497
536
|
}
|
|
@@ -503,7 +542,7 @@ export class DeviceAgentCloudConnection {
|
|
|
503
542
|
|
|
504
543
|
await this.publishReportedState(projectId);
|
|
505
544
|
} catch (e) {
|
|
506
|
-
|
|
545
|
+
logger.error(e);
|
|
507
546
|
}
|
|
508
547
|
break;
|
|
509
548
|
}
|
|
@@ -512,8 +551,14 @@ export class DeviceAgentCloudConnection {
|
|
|
512
551
|
}
|
|
513
552
|
}
|
|
514
553
|
|
|
515
|
-
public async handleShadowTopic({
|
|
516
|
-
|
|
554
|
+
public async handleShadowTopic({
|
|
555
|
+
topic,
|
|
556
|
+
payload,
|
|
557
|
+
}: {
|
|
558
|
+
topic: string;
|
|
559
|
+
payload: string;
|
|
560
|
+
}) {
|
|
561
|
+
const shadowName = topic.split("/")[5];
|
|
517
562
|
const message = JSON.parse(payload);
|
|
518
563
|
if (topic === this.shadowTopics.projects.updateDelta) {
|
|
519
564
|
this.handleNamedShadowUpdate({ payload });
|
|
@@ -523,45 +568,94 @@ export class DeviceAgentCloudConnection {
|
|
|
523
568
|
payload: JSON.stringify(message.delta),
|
|
524
569
|
});
|
|
525
570
|
} else {
|
|
526
|
-
|
|
571
|
+
logger.info(`No delta updates in shadow ${shadowName}`);
|
|
527
572
|
}
|
|
528
573
|
}
|
|
529
574
|
}
|
|
530
575
|
}
|
|
531
576
|
|
|
532
|
-
export function runDeviceAgentCloudInterface() {
|
|
533
|
-
|
|
577
|
+
export async function runDeviceAgentCloudInterface() {
|
|
578
|
+
switch (existsSync(getCertificateFilePath())) {
|
|
579
|
+
case true:
|
|
580
|
+
{
|
|
581
|
+
const deviceAgent = new DeviceAgentCloudConnection();
|
|
534
582
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
console.log('Device Agent has connected to the cloud');
|
|
583
|
+
deviceAgent.device.on("connect", function (connack: any) {
|
|
584
|
+
logger.info("Device Agent has connected to the cloud");
|
|
538
585
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
586
|
+
// Get shadow updates
|
|
587
|
+
deviceAgent.publishMessage(
|
|
588
|
+
`${deviceAgent.getShadowPrefix()}projects/get`,
|
|
589
|
+
""
|
|
590
|
+
);
|
|
591
|
+
});
|
|
542
592
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
593
|
+
deviceAgent.device.on("disconnect", function () {
|
|
594
|
+
logger.info("Device Agent has been disconnected from the cloud");
|
|
595
|
+
});
|
|
546
596
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
597
|
+
deviceAgent.device.on(
|
|
598
|
+
"message",
|
|
599
|
+
function (topic: string, payload: string) {
|
|
600
|
+
console.log(topic);
|
|
601
|
+
console.log(JSON.parse(payload));
|
|
602
|
+
try {
|
|
603
|
+
const jsonPacket = JSON.parse(payload);
|
|
604
|
+
if (jsonPacket.hasOwnProperty("state")) {
|
|
605
|
+
deviceAgent.handleShadowTopic({
|
|
606
|
+
topic,
|
|
607
|
+
payload: JSON.stringify(jsonPacket.state),
|
|
608
|
+
});
|
|
609
|
+
} else {
|
|
610
|
+
const valid = validateClientMessage(jsonPacket);
|
|
611
|
+
if (!valid) {
|
|
612
|
+
console.error(JSON.stringify(validateClientMessage.errors));
|
|
613
|
+
} else {
|
|
614
|
+
deviceAgent.handleClientMessage({
|
|
615
|
+
topic,
|
|
616
|
+
message: jsonPacket,
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
} catch (error) {
|
|
621
|
+
logger.error(error);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
);
|
|
625
|
+
|
|
626
|
+
deviceAgent.device.on("packetsend", (packet: any) => {
|
|
627
|
+
//console.log({ packet: packet });
|
|
554
628
|
});
|
|
555
|
-
} else {
|
|
556
|
-
const valid = validateClientMessage(jsonPacket);
|
|
557
|
-
if (!valid) {
|
|
558
|
-
console.error(JSON.stringify(validateClientMessage.errors));
|
|
559
|
-
} else {
|
|
560
|
-
deviceAgent.handleClientMessage({ topic, message: jsonPacket });
|
|
561
|
-
}
|
|
562
629
|
}
|
|
563
|
-
|
|
564
|
-
|
|
630
|
+
|
|
631
|
+
break;
|
|
632
|
+
case false: {
|
|
633
|
+
//set timer
|
|
634
|
+
const clientId = getDeviceId();
|
|
635
|
+
const bootstrapConfig = {
|
|
636
|
+
keyPath: BOOTSTRAP_DEVICE_PRIVATE_KEY_FILE_PATH,
|
|
637
|
+
certPath: BOOTSTRAP_DEVICE_CERTIFICATE_FILE_PATH,
|
|
638
|
+
caPath: AWS_ROOT_CERTIFICATE_FILE_PATH,
|
|
639
|
+
clientId,
|
|
640
|
+
host: getIoTCoreEndpointUrl(),
|
|
641
|
+
};
|
|
642
|
+
|
|
643
|
+
const bootstrapAgent = new BootstrapAgent(bootstrapConfig);
|
|
644
|
+
bootstrapAgent.subscribeToAllTopics();
|
|
645
|
+
|
|
646
|
+
bootstrapAgent.publishMessage("$aws/certificates/create/json", "");
|
|
647
|
+
|
|
648
|
+
bootstrapAgent.device.on("connect", () => {
|
|
649
|
+
console.log("Your device is being provisioned");
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
bootstrapAgent.device.on("message", (topic: string, payload: string) => {
|
|
653
|
+
bootstrapAgent.handleAwsCertificateTopics(topic, payload);
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
bootstrapAgent.device.on("packetsend", (packet: any) => {
|
|
657
|
+
console.log({ packet: packet.subscriptions });
|
|
658
|
+
});
|
|
565
659
|
}
|
|
566
|
-
}
|
|
660
|
+
}
|
|
567
661
|
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
const awsIot = require("aws-iot-device-sdk");
|
|
2
|
+
import {
|
|
3
|
+
BOOTSTRAP_CLAIM_ID_FILE_NAME,
|
|
4
|
+
BOOTSTRAP_CLAIM_ID_FILE_PATH,
|
|
5
|
+
DEVICE_PRIVATE_KEY_FILE_NAME,
|
|
6
|
+
CERTIFICATE_OWNERSHIP_TOKEN_FILE_NAME,
|
|
7
|
+
DEVICE_CLAIM_ID_FILE_NAME,
|
|
8
|
+
DEVICE_CERTIFICATE_FILE_NAME,
|
|
9
|
+
} from "../util/directories";
|
|
10
|
+
import { getTargetHardwareUuid } from "../infrastructure/certificates-and-tokens";
|
|
11
|
+
import { getDeviceId } from "../util/get-device-id";
|
|
12
|
+
import { LOCAL_CERT_AND_KEY_DIR } from "alwaysai/lib/constants";
|
|
13
|
+
import { JsSpawner } from "alwaysai/lib/util";
|
|
14
|
+
import { logger } from "alwaysai/lib/util/logger";
|
|
15
|
+
import { DeviceAgentCloudConnection } from "./device-agent-cloud-connection";
|
|
16
|
+
const process = require("process");
|
|
17
|
+
|
|
18
|
+
interface DeviceAgentConfigType {
|
|
19
|
+
keyPath: string;
|
|
20
|
+
certPath: string;
|
|
21
|
+
caPath: string;
|
|
22
|
+
clientId: string;
|
|
23
|
+
host: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface FleetProvisionTemplateMessageType {
|
|
27
|
+
certificateOwnershipToken: String;
|
|
28
|
+
parameters: {
|
|
29
|
+
hardwareId: String;
|
|
30
|
+
deviceUuid: String;
|
|
31
|
+
certificateId: String;
|
|
32
|
+
deviceType: String;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class DeviceAgent {
|
|
37
|
+
constructor(config: DeviceAgentConfigType) {
|
|
38
|
+
this.device = awsIot.device(config);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public deviceType = "aai-device";
|
|
42
|
+
public device = awsIot.device;
|
|
43
|
+
public hardwareId = async () => await getTargetHardwareUuid(JsSpawner());
|
|
44
|
+
public deviceId = getDeviceId();
|
|
45
|
+
|
|
46
|
+
public publishMessage(topic: string, message: string) {
|
|
47
|
+
this.device.publish(topic, message);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class BootstrapAgent extends DeviceAgent {
|
|
52
|
+
public async subscribeToAllTopics() {
|
|
53
|
+
const AWS_CERTIFICATE_ACCEPT_TOPIC =
|
|
54
|
+
"$aws/certificates/create/json/accepted";
|
|
55
|
+
const PROVISIONING_ACCEPTED_TOPIC =
|
|
56
|
+
"$aws/provisioning-templates/FleetProvisionTemplate/provision/json/accepted";
|
|
57
|
+
|
|
58
|
+
const topics = [AWS_CERTIFICATE_ACCEPT_TOPIC, PROVISIONING_ACCEPTED_TOPIC];
|
|
59
|
+
const resp = this.device.subscribe(
|
|
60
|
+
topics,
|
|
61
|
+
function (err: any, granted: { topic: string; qos: number }[]) {
|
|
62
|
+
logger.debug(granted);
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public async handleAwsCertificateTopics(topic: string, payload: any) {
|
|
68
|
+
switch (topic) {
|
|
69
|
+
case "$aws/certificates/create/json/accepted": {
|
|
70
|
+
const {
|
|
71
|
+
certificateId,
|
|
72
|
+
certificatePem,
|
|
73
|
+
privateKey,
|
|
74
|
+
certificateOwnershipToken,
|
|
75
|
+
} = JSON.parse(payload);
|
|
76
|
+
console.log(certificateId);
|
|
77
|
+
|
|
78
|
+
const certSpawner = JsSpawner({ path: LOCAL_CERT_AND_KEY_DIR });
|
|
79
|
+
|
|
80
|
+
await certSpawner.writeFile(
|
|
81
|
+
DEVICE_CERTIFICATE_FILE_NAME,
|
|
82
|
+
certificatePem
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
await certSpawner.writeFile(DEVICE_PRIVATE_KEY_FILE_NAME, privateKey);
|
|
86
|
+
|
|
87
|
+
await certSpawner.writeFile(DEVICE_CLAIM_ID_FILE_NAME, certificateId);
|
|
88
|
+
|
|
89
|
+
await certSpawner.writeFile(
|
|
90
|
+
CERTIFICATE_OWNERSHIP_TOKEN_FILE_NAME,
|
|
91
|
+
certificateOwnershipToken
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const content: FleetProvisionTemplateMessageType = {
|
|
95
|
+
certificateOwnershipToken,
|
|
96
|
+
parameters: {
|
|
97
|
+
hardwareId: await this.hardwareId(),
|
|
98
|
+
deviceUuid: this.deviceId,
|
|
99
|
+
certificateId,
|
|
100
|
+
deviceType: this.deviceType,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
console.log(content);
|
|
104
|
+
|
|
105
|
+
this.publishMessage(
|
|
106
|
+
"$aws/provisioning-templates/FleetProvisionTemplate/provision/json",
|
|
107
|
+
JSON.stringify(content)
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case "$aws/provisioning-templates/FleetProvisionTemplate/provision/json/accepted": {
|
|
113
|
+
console.log("success");
|
|
114
|
+
// Resolve the `child_process` module, and `spawn`
|
|
115
|
+
// a new process.
|
|
116
|
+
// The `child_process` module lets us
|
|
117
|
+
// access OS functionalities by running any bash command.`.
|
|
118
|
+
process.exit();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
package/src/endpoints.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { getSystemId } from "alwaysai/lib/infrastructure";
|
|
2
|
+
|
|
3
|
+
export const getSecondLevelDomain = () => {
|
|
4
|
+
let domain = "";
|
|
5
|
+
switch (getSystemId()) {
|
|
6
|
+
case "development":
|
|
7
|
+
domain = "a6i0.net";
|
|
8
|
+
break;
|
|
9
|
+
case "qa":
|
|
10
|
+
domain = "a6i1.net";
|
|
11
|
+
break;
|
|
12
|
+
case "production":
|
|
13
|
+
domain = "alwaysai.co";
|
|
14
|
+
break;
|
|
15
|
+
default:
|
|
16
|
+
domain = "alwaysai.co";
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
return domain;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const serviceEndpointBuilder = (service: string, path: string) => {
|
|
23
|
+
return `https://${service}.${getSecondLevelDomain()}/${path}`;
|
|
24
|
+
};
|
package/src/environment.ts
CHANGED
|
@@ -4,6 +4,7 @@ export const ALWAYSAI_OS_PLATFORM = parseOsPlatform(process.env.ALWAYSAI_OS_PLAT
|
|
|
4
4
|
export const ALWAYSAI_SHOW_HIDDEN = parseBoolean(process.env.ALWAYSAI_SHOW_HIDDEN);
|
|
5
5
|
export const ALWAYSAI_DEVICE_AGENT_MODE = process.env.ALWAYSAI_DEVICE_AGENT_MODE;
|
|
6
6
|
export const ALWAYSAI_LOG_LEVEL = process.env.AAI_LOG_LEVEL;
|
|
7
|
+
export const ALWAYSAI_LOG_TO_CONSOLE = process.env.ALWAYSAI_LOG_TO_CONSOLE;
|
|
7
8
|
|
|
8
9
|
function parseOsPlatform(str: string | undefined): NodeJS.Platform {
|
|
9
10
|
switch (str) {
|
package/src/index.ts
CHANGED
|
@@ -10,9 +10,10 @@ import { root } from './root';
|
|
|
10
10
|
import { runDeviceAgentCloudInterface } from './cloud-connection/device-agent-cloud-connection';
|
|
11
11
|
import { AgentConfigFile } from './infrastructure/agent-config';
|
|
12
12
|
import { ALWAYSAI_DEVICE_AGENT_MODE } from './environment';
|
|
13
|
+
import { logger } from './util/logger';
|
|
13
14
|
|
|
14
15
|
if (module === require.main) {
|
|
15
|
-
|
|
16
|
+
logger.info('Starting alwaysAI Device Agent');
|
|
16
17
|
if (!AgentConfigFile().exists()) {
|
|
17
18
|
AgentConfigFile().initialize();
|
|
18
19
|
}
|