@alwaysai/device-agent 0.0.10 → 0.0.12
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 -4
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +89 -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 +3 -2
- 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 +177 -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,25 @@ import {
|
|
|
37
46
|
DeviceAgentMessage,
|
|
38
47
|
ClientMessage,
|
|
39
48
|
AppDetailsPacket,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
import {
|
|
45
|
-
import {
|
|
46
|
-
import {
|
|
47
|
-
|
|
49
|
+
getClientTopic,
|
|
50
|
+
getCloudTopic,
|
|
51
|
+
getDeviceTopic,
|
|
52
|
+
} from "@alwaysai/device-agent-schemas";
|
|
53
|
+
import { getDeviceId } from "../util/get-device-id";
|
|
54
|
+
import { JsSpawner, logger } from "alwaysai/lib/util";
|
|
55
|
+
import {
|
|
56
|
+
getCpuUtil,
|
|
57
|
+
getDiskUtil,
|
|
58
|
+
getMemUtil,
|
|
59
|
+
} from "../device-control/device-control";
|
|
60
|
+
import { AgentConfigFile } from "../infrastructure/agent-config";
|
|
61
|
+
import {
|
|
62
|
+
buildApp,
|
|
63
|
+
getAppConfig,
|
|
64
|
+
getAppDir,
|
|
65
|
+
} from "../application-control/utils";
|
|
66
|
+
import { updateModelsWithPresignedUrls } from "../application-control/models";
|
|
67
|
+
import { updateAppConfig } from "../application-control/config";
|
|
48
68
|
|
|
49
69
|
export class DeviceAgentCloudConnection {
|
|
50
70
|
private clientId = getDeviceId();
|
|
@@ -63,7 +83,6 @@ export class DeviceAgentCloudConnection {
|
|
|
63
83
|
[keyMirrors.agentMessageType.app_install_status]: 5000,
|
|
64
84
|
};
|
|
65
85
|
private appLogStreams = new Set<string>();
|
|
66
|
-
private deviceType = 'aai-device';
|
|
67
86
|
private readonly shadowPrefix = `$aws/things/${this.clientId}/shadow/name/`;
|
|
68
87
|
private readonly shadowTopics = {
|
|
69
88
|
projects: {
|
|
@@ -71,9 +90,9 @@ export class DeviceAgentCloudConnection {
|
|
|
71
90
|
getAccepted: `${this.shadowPrefix}projects/get/accepted`,
|
|
72
91
|
},
|
|
73
92
|
};
|
|
74
|
-
private readonly toCloudTopic =
|
|
75
|
-
private readonly toClientTopic =
|
|
76
|
-
private readonly toDeviceTopic =
|
|
93
|
+
private readonly toCloudTopic = getCloudTopic(this.clientId);
|
|
94
|
+
private readonly toClientTopic = getClientTopic(this.clientId);
|
|
95
|
+
private readonly toDeviceTopic = getDeviceTopic(this.clientId);
|
|
77
96
|
|
|
78
97
|
// device shadow utils
|
|
79
98
|
|
|
@@ -123,9 +142,9 @@ export class DeviceAgentCloudConnection {
|
|
|
123
142
|
this.appLogStreams.add(projectId);
|
|
124
143
|
const readable = await getAppLogs({
|
|
125
144
|
projectId,
|
|
126
|
-
args: [
|
|
145
|
+
args: ["--tail", "100", "--no-log-prefix"],
|
|
127
146
|
});
|
|
128
|
-
readable.on(
|
|
147
|
+
readable.on("data", (chunk: Buffer) => {
|
|
129
148
|
if (!this.appLogStreams.has(projectId)) {
|
|
130
149
|
// why doesn't typescript know about this function?
|
|
131
150
|
// @ts-ignore
|
|
@@ -144,16 +163,18 @@ export class DeviceAgentCloudConnection {
|
|
|
144
163
|
const packet = this.buildMessagePacket(
|
|
145
164
|
this.getClientId(),
|
|
146
165
|
this.toClientTopic,
|
|
147
|
-
message
|
|
166
|
+
message
|
|
148
167
|
);
|
|
149
168
|
this.publishMessage(this.toClientTopic, JSON.stringify(packet));
|
|
150
169
|
});
|
|
151
170
|
|
|
152
|
-
readable.on(
|
|
153
|
-
logger.error(
|
|
171
|
+
readable.on("error", (error) => {
|
|
172
|
+
logger.error(
|
|
173
|
+
`App log stream terminated for project ${projectId}: ${error}`
|
|
174
|
+
);
|
|
154
175
|
});
|
|
155
176
|
|
|
156
|
-
readable.on(
|
|
177
|
+
readable.on("finished", () => {
|
|
157
178
|
logger.info(`App logs finished piping for project ${projectId}`);
|
|
158
179
|
});
|
|
159
180
|
}
|
|
@@ -164,7 +185,7 @@ export class DeviceAgentCloudConnection {
|
|
|
164
185
|
}
|
|
165
186
|
|
|
166
187
|
private updateAppInstallStatus(
|
|
167
|
-
installationStatus: Omit<AppInstallStatusPacket,
|
|
188
|
+
installationStatus: Omit<AppInstallStatusPacket, "appReleaseHash">
|
|
168
189
|
) {
|
|
169
190
|
this.appInstallStatus.status = installationStatus.status;
|
|
170
191
|
this.appInstallStatus.message = installationStatus.message;
|
|
@@ -178,7 +199,7 @@ export class DeviceAgentCloudConnection {
|
|
|
178
199
|
private buildMessagePacket(
|
|
179
200
|
deviceId: string,
|
|
180
201
|
topic: string,
|
|
181
|
-
payload: DeviceAgentMessagePayload
|
|
202
|
+
payload: DeviceAgentMessagePayload
|
|
182
203
|
): DeviceAgentMessage {
|
|
183
204
|
const packet = {
|
|
184
205
|
timestamp: new Date().toUTCString(),
|
|
@@ -221,27 +242,34 @@ export class DeviceAgentCloudConnection {
|
|
|
221
242
|
}
|
|
222
243
|
|
|
223
244
|
// must be arrow function due to this context when function is passed as param
|
|
224
|
-
private getAppInstallStatusMessage =
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
245
|
+
private getAppInstallStatusMessage =
|
|
246
|
+
async (): Promise<AppInstallStatusMessage> => {
|
|
247
|
+
const appInstallStatus = this.getAppInstallStatus();
|
|
248
|
+
const appInstallStatusMessage = {
|
|
249
|
+
messageType: keyMirrors.agentMessageType.app_install_status,
|
|
250
|
+
appInstallStatus,
|
|
251
|
+
};
|
|
252
|
+
return appInstallStatusMessage;
|
|
229
253
|
};
|
|
230
|
-
return appInstallStatusMessage;
|
|
231
|
-
};
|
|
232
254
|
|
|
233
255
|
private async startPublishingLiveUpdates(
|
|
234
256
|
topic: string,
|
|
235
257
|
messageType: string,
|
|
236
|
-
getMessageData: () => Promise<DeviceAgentMessagePayload
|
|
258
|
+
getMessageData: () => Promise<DeviceAgentMessagePayload>
|
|
237
259
|
) {
|
|
238
260
|
while (true) {
|
|
239
261
|
try {
|
|
240
262
|
const message = await getMessageData();
|
|
241
|
-
const packet = this.buildMessagePacket(
|
|
263
|
+
const packet = this.buildMessagePacket(
|
|
264
|
+
this.getClientId(),
|
|
265
|
+
topic,
|
|
266
|
+
message
|
|
267
|
+
);
|
|
242
268
|
this.publishMessage(topic, JSON.stringify(packet));
|
|
243
269
|
} catch (e) {
|
|
244
|
-
logger.error(
|
|
270
|
+
logger.error(
|
|
271
|
+
`Error publishing live updates for ${messageType}: ${e.message}`
|
|
272
|
+
);
|
|
245
273
|
break;
|
|
246
274
|
}
|
|
247
275
|
if (!this.continuePublishing(messageType)) {
|
|
@@ -258,7 +286,10 @@ export class DeviceAgentCloudConnection {
|
|
|
258
286
|
case keyMirrors.agentMessageType.app_state:
|
|
259
287
|
return this.liveUpdatesAlive[flag];
|
|
260
288
|
case keyMirrors.agentMessageType.app_install_status:
|
|
261
|
-
return
|
|
289
|
+
return (
|
|
290
|
+
this.appInstallStatus.status ===
|
|
291
|
+
keyMirrors.appInstallStatus.in_progress
|
|
292
|
+
);
|
|
262
293
|
default:
|
|
263
294
|
logger.error(`Unrecognized publishable flag ${flag}`);
|
|
264
295
|
return false;
|
|
@@ -317,7 +348,9 @@ export class DeviceAgentCloudConnection {
|
|
|
317
348
|
this.liveUpdatesBroker(message.liveUpdatesToggles);
|
|
318
349
|
break;
|
|
319
350
|
default:
|
|
320
|
-
logger.error(
|
|
351
|
+
logger.error(
|
|
352
|
+
`Invalid agent action message type from message '${message}'`
|
|
353
|
+
);
|
|
321
354
|
}
|
|
322
355
|
}
|
|
323
356
|
|
|
@@ -352,7 +385,7 @@ export class DeviceAgentCloudConnection {
|
|
|
352
385
|
this.startPublishingLiveUpdates(
|
|
353
386
|
this.toClientTopic,
|
|
354
387
|
keyMirrors.agentMessageType.device_stats,
|
|
355
|
-
this.getDeviceStatsMessage
|
|
388
|
+
this.getDeviceStatsMessage
|
|
356
389
|
);
|
|
357
390
|
}
|
|
358
391
|
}
|
|
@@ -363,7 +396,7 @@ export class DeviceAgentCloudConnection {
|
|
|
363
396
|
this.startPublishingLiveUpdates(
|
|
364
397
|
this.toClientTopic,
|
|
365
398
|
keyMirrors.agentMessageType.app_state,
|
|
366
|
-
this.getAppStateMessage
|
|
399
|
+
this.getAppStateMessage
|
|
367
400
|
);
|
|
368
401
|
}
|
|
369
402
|
}
|
|
@@ -386,7 +419,10 @@ export class DeviceAgentCloudConnection {
|
|
|
386
419
|
},
|
|
387
420
|
},
|
|
388
421
|
};
|
|
389
|
-
this.publishMessage(
|
|
422
|
+
this.publishMessage(
|
|
423
|
+
`${this.shadowPrefix}projects/update`,
|
|
424
|
+
JSON.stringify(packet)
|
|
425
|
+
);
|
|
390
426
|
}
|
|
391
427
|
|
|
392
428
|
private async publishCloudRequest(payload: SignedUrlsRequestMessage) {
|
|
@@ -394,7 +430,7 @@ export class DeviceAgentCloudConnection {
|
|
|
394
430
|
const deviceRequestPacket = this.buildMessagePacket(
|
|
395
431
|
this.getClientId(),
|
|
396
432
|
topic,
|
|
397
|
-
payload
|
|
433
|
+
payload
|
|
398
434
|
);
|
|
399
435
|
this.publishMessage(topic, JSON.stringify({ deviceRequestPacket }));
|
|
400
436
|
}
|
|
@@ -407,9 +443,10 @@ export class DeviceAgentCloudConnection {
|
|
|
407
443
|
this.device = awsIot.device({
|
|
408
444
|
keyPath: getPrivateKeyFilePath(),
|
|
409
445
|
certPath: getCertificateFilePath(),
|
|
410
|
-
caPath:
|
|
446
|
+
caPath: AWS_ROOT_CERTIFICATE_FILE_PATH,
|
|
411
447
|
clientId: this.clientId,
|
|
412
448
|
host: this.host,
|
|
449
|
+
port: 8883,
|
|
413
450
|
});
|
|
414
451
|
|
|
415
452
|
this.device.subscribe(this.toDeviceTopic);
|
|
@@ -423,7 +460,7 @@ export class DeviceAgentCloudConnection {
|
|
|
423
460
|
|
|
424
461
|
public publishMessage(topic: string, message: string) {
|
|
425
462
|
// TODO: topic validation
|
|
426
|
-
this.device.publish(topic, message);
|
|
463
|
+
this.device.publish(topic, message, (err: any) => {});
|
|
427
464
|
}
|
|
428
465
|
|
|
429
466
|
public async handleClientMessage({
|
|
@@ -448,8 +485,12 @@ export class DeviceAgentCloudConnection {
|
|
|
448
485
|
break;
|
|
449
486
|
}
|
|
450
487
|
case keyMirrors.clientMessageType.app_install_cloud_response: {
|
|
451
|
-
const {
|
|
452
|
-
|
|
488
|
+
const {
|
|
489
|
+
projectId,
|
|
490
|
+
appReleaseHash,
|
|
491
|
+
appInstallPayload,
|
|
492
|
+
modelsInstallPayload,
|
|
493
|
+
} = payload.appInstallCloudResponse;
|
|
453
494
|
|
|
454
495
|
this.initAppInstallStatus({
|
|
455
496
|
status: keyMirrors.appInstallStatus.in_progress,
|
|
@@ -459,7 +500,7 @@ export class DeviceAgentCloudConnection {
|
|
|
459
500
|
this.startPublishingLiveUpdates(
|
|
460
501
|
this.toClientTopic,
|
|
461
502
|
keyMirrors.agentMessageType.app_install_status,
|
|
462
|
-
this.getAppInstallStatusMessage
|
|
503
|
+
this.getAppInstallStatusMessage
|
|
463
504
|
);
|
|
464
505
|
|
|
465
506
|
// Install the app and models
|
|
@@ -480,7 +521,7 @@ export class DeviceAgentCloudConnection {
|
|
|
480
521
|
// update app config shadow for project
|
|
481
522
|
await this.publishReportedState(projectId);
|
|
482
523
|
} catch (e) {
|
|
483
|
-
|
|
524
|
+
logger.error(e);
|
|
484
525
|
const message: string = e.message;
|
|
485
526
|
|
|
486
527
|
// uninstall the failed app to put system back in good state
|
|
@@ -491,7 +532,7 @@ export class DeviceAgentCloudConnection {
|
|
|
491
532
|
});
|
|
492
533
|
|
|
493
534
|
// delete shadow for project
|
|
494
|
-
this.publishMessage(`${this.shadowPrefix}${projectId}/delete`,
|
|
535
|
+
this.publishMessage(`${this.shadowPrefix}${projectId}/delete`, "");
|
|
495
536
|
}
|
|
496
537
|
break;
|
|
497
538
|
}
|
|
@@ -503,7 +544,7 @@ export class DeviceAgentCloudConnection {
|
|
|
503
544
|
|
|
504
545
|
await this.publishReportedState(projectId);
|
|
505
546
|
} catch (e) {
|
|
506
|
-
|
|
547
|
+
logger.error(e);
|
|
507
548
|
}
|
|
508
549
|
break;
|
|
509
550
|
}
|
|
@@ -512,8 +553,14 @@ export class DeviceAgentCloudConnection {
|
|
|
512
553
|
}
|
|
513
554
|
}
|
|
514
555
|
|
|
515
|
-
public async handleShadowTopic({
|
|
516
|
-
|
|
556
|
+
public async handleShadowTopic({
|
|
557
|
+
topic,
|
|
558
|
+
payload,
|
|
559
|
+
}: {
|
|
560
|
+
topic: string;
|
|
561
|
+
payload: string;
|
|
562
|
+
}) {
|
|
563
|
+
const shadowName = topic.split("/")[5];
|
|
517
564
|
const message = JSON.parse(payload);
|
|
518
565
|
if (topic === this.shadowTopics.projects.updateDelta) {
|
|
519
566
|
this.handleNamedShadowUpdate({ payload });
|
|
@@ -523,45 +570,94 @@ export class DeviceAgentCloudConnection {
|
|
|
523
570
|
payload: JSON.stringify(message.delta),
|
|
524
571
|
});
|
|
525
572
|
} else {
|
|
526
|
-
|
|
573
|
+
logger.info(`No delta updates in shadow ${shadowName}`);
|
|
527
574
|
}
|
|
528
575
|
}
|
|
529
576
|
}
|
|
530
577
|
}
|
|
531
578
|
|
|
532
|
-
export function runDeviceAgentCloudInterface() {
|
|
533
|
-
|
|
579
|
+
export async function runDeviceAgentCloudInterface() {
|
|
580
|
+
switch (existsSync(getCertificateFilePath())) {
|
|
581
|
+
case true:
|
|
582
|
+
{
|
|
583
|
+
const deviceAgent = new DeviceAgentCloudConnection();
|
|
534
584
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
console.log('Device Agent has connected to the cloud');
|
|
585
|
+
deviceAgent.device.on("connect", function (connack: any) {
|
|
586
|
+
logger.info("Device Agent has connected to the cloud");
|
|
538
587
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
588
|
+
// Get shadow updates
|
|
589
|
+
deviceAgent.publishMessage(
|
|
590
|
+
`${deviceAgent.getShadowPrefix()}projects/get`,
|
|
591
|
+
""
|
|
592
|
+
);
|
|
593
|
+
});
|
|
542
594
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
595
|
+
deviceAgent.device.on("disconnect", function () {
|
|
596
|
+
logger.info("Device Agent has been disconnected from the cloud");
|
|
597
|
+
});
|
|
546
598
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
599
|
+
deviceAgent.device.on(
|
|
600
|
+
"message",
|
|
601
|
+
function (topic: string, payload: string) {
|
|
602
|
+
console.log(topic);
|
|
603
|
+
console.log(JSON.parse(payload));
|
|
604
|
+
try {
|
|
605
|
+
const jsonPacket = JSON.parse(payload);
|
|
606
|
+
if (jsonPacket.hasOwnProperty("state")) {
|
|
607
|
+
deviceAgent.handleShadowTopic({
|
|
608
|
+
topic,
|
|
609
|
+
payload: JSON.stringify(jsonPacket.state),
|
|
610
|
+
});
|
|
611
|
+
} else {
|
|
612
|
+
const valid = validateClientMessage(jsonPacket);
|
|
613
|
+
if (!valid) {
|
|
614
|
+
console.error(JSON.stringify(validateClientMessage.errors));
|
|
615
|
+
} else {
|
|
616
|
+
deviceAgent.handleClientMessage({
|
|
617
|
+
topic,
|
|
618
|
+
message: jsonPacket,
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
} catch (error) {
|
|
623
|
+
logger.error(error);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
);
|
|
627
|
+
|
|
628
|
+
deviceAgent.device.on("packetsend", (packet: any) => {
|
|
629
|
+
console.log({ packet: packet });
|
|
554
630
|
});
|
|
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
631
|
}
|
|
563
|
-
|
|
564
|
-
|
|
632
|
+
|
|
633
|
+
break;
|
|
634
|
+
case false: {
|
|
635
|
+
//set timer
|
|
636
|
+
const clientId = getDeviceId();
|
|
637
|
+
const bootstrapConfig = {
|
|
638
|
+
keyPath: BOOTSTRAP_DEVICE_PRIVATE_KEY_FILE_PATH,
|
|
639
|
+
certPath: BOOTSTRAP_DEVICE_CERTIFICATE_FILE_PATH,
|
|
640
|
+
caPath: AWS_ROOT_CERTIFICATE_FILE_PATH,
|
|
641
|
+
clientId,
|
|
642
|
+
host: getIoTCoreEndpointUrl(),
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
const bootstrapAgent = new BootstrapAgent(bootstrapConfig);
|
|
646
|
+
bootstrapAgent.subscribeToAllTopics();
|
|
647
|
+
|
|
648
|
+
bootstrapAgent.publishMessage("$aws/certificates/create/json", "");
|
|
649
|
+
|
|
650
|
+
bootstrapAgent.device.on("connect", () => {
|
|
651
|
+
console.log("Your device is being provisioned");
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
bootstrapAgent.device.on("message", (topic: string, payload: string) => {
|
|
655
|
+
bootstrapAgent.handleAwsCertificateTopics(topic, payload);
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
bootstrapAgent.device.on("packetsend", (packet: any) => {
|
|
659
|
+
console.log({ packet: packet.subscriptions });
|
|
660
|
+
});
|
|
565
661
|
}
|
|
566
|
-
}
|
|
662
|
+
}
|
|
567
663
|
}
|
|
@@ -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
|
}
|