@alwaysai/device-agent 1.4.0 → 2.0.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.
Files changed (313) hide show
  1. package/lib/application-control/config.d.ts.map +1 -1
  2. package/lib/application-control/config.js +10 -5
  3. package/lib/application-control/config.js.map +1 -1
  4. package/lib/application-control/environment-variables.d.ts +1 -5
  5. package/lib/application-control/environment-variables.d.ts.map +1 -1
  6. package/lib/application-control/environment-variables.js +9 -26
  7. package/lib/application-control/environment-variables.js.map +1 -1
  8. package/lib/application-control/environment-variables.test.js +27 -7
  9. package/lib/application-control/environment-variables.test.js.map +1 -1
  10. package/lib/application-control/index.d.ts +4 -4
  11. package/lib/application-control/index.d.ts.map +1 -1
  12. package/lib/application-control/index.js +1 -4
  13. package/lib/application-control/index.js.map +1 -1
  14. package/lib/application-control/install.d.ts.map +1 -1
  15. package/lib/application-control/install.js +9 -7
  16. package/lib/application-control/install.js.map +1 -1
  17. package/lib/application-control/models.d.ts +5 -11
  18. package/lib/application-control/models.d.ts.map +1 -1
  19. package/lib/application-control/models.js +27 -64
  20. package/lib/application-control/models.js.map +1 -1
  21. package/lib/application-control/status.d.ts.map +1 -1
  22. package/lib/application-control/status.js +10 -12
  23. package/lib/application-control/status.js.map +1 -1
  24. package/lib/application-control/utils.d.ts +0 -4
  25. package/lib/application-control/utils.d.ts.map +1 -1
  26. package/lib/application-control/utils.js +3 -26
  27. package/lib/application-control/utils.js.map +1 -1
  28. package/lib/cloud-connection/bootstrap-provision.js +3 -2
  29. package/lib/cloud-connection/bootstrap-provision.js.map +1 -1
  30. package/lib/cloud-connection/device-agent-cloud-connection.d.ts +11 -16
  31. package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
  32. package/lib/cloud-connection/device-agent-cloud-connection.js +295 -246
  33. package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
  34. package/lib/cloud-connection/device-agent.d.ts.map +1 -1
  35. package/lib/cloud-connection/device-agent.js +11 -9
  36. package/lib/cloud-connection/device-agent.js.map +1 -1
  37. package/lib/cloud-connection/live-updates-handler.d.ts +18 -27
  38. package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
  39. package/lib/cloud-connection/live-updates-handler.js +58 -170
  40. package/lib/cloud-connection/live-updates-handler.js.map +1 -1
  41. package/lib/cloud-connection/live-updates-handler.test.js +76 -54
  42. package/lib/cloud-connection/live-updates-handler.test.js.map +1 -1
  43. package/lib/cloud-connection/passthrough-handler.d.ts +9 -4
  44. package/lib/cloud-connection/passthrough-handler.d.ts.map +1 -1
  45. package/lib/cloud-connection/passthrough-handler.js +95 -62
  46. package/lib/cloud-connection/passthrough-handler.js.map +1 -1
  47. package/lib/cloud-connection/shadow-handler.d.ts +5 -1
  48. package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
  49. package/lib/cloud-connection/shadow-handler.js +63 -31
  50. package/lib/cloud-connection/shadow-handler.js.map +1 -1
  51. package/lib/cloud-connection/shadow-handler.test.js +45 -57
  52. package/lib/cloud-connection/shadow-handler.test.js.map +1 -1
  53. package/lib/cloud-connection/shadow.d.ts.map +1 -1
  54. package/lib/cloud-connection/shadow.js +2 -1
  55. package/lib/cloud-connection/shadow.js.map +1 -1
  56. package/lib/cloud-connection/transaction-manager.d.ts +7 -2
  57. package/lib/cloud-connection/transaction-manager.d.ts.map +1 -1
  58. package/lib/cloud-connection/transaction-manager.js +29 -29
  59. package/lib/cloud-connection/transaction-manager.js.map +1 -1
  60. package/lib/cloud-connection/transaction-manager.test.js +105 -3
  61. package/lib/cloud-connection/transaction-manager.test.js.map +1 -1
  62. package/lib/device-control/device-control.d.ts +14 -6
  63. package/lib/device-control/device-control.d.ts.map +1 -1
  64. package/lib/device-control/device-control.js +172 -72
  65. package/lib/device-control/device-control.js.map +1 -1
  66. package/lib/docker/docker-compose.d.ts +14 -0
  67. package/lib/docker/docker-compose.d.ts.map +1 -0
  68. package/lib/docker/docker-compose.js +57 -0
  69. package/lib/docker/docker-compose.js.map +1 -0
  70. package/lib/index.js +2 -5
  71. package/lib/index.js.map +1 -1
  72. package/lib/infrastructure/agent-config.d.ts +46 -14
  73. package/lib/infrastructure/agent-config.d.ts.map +1 -1
  74. package/lib/infrastructure/agent-config.js +36 -21
  75. package/lib/infrastructure/agent-config.js.map +1 -1
  76. package/lib/infrastructure/agent-config.test.js +6 -1
  77. package/lib/infrastructure/agent-config.test.js.map +1 -1
  78. package/lib/infrastructure/config-check-utility.d.ts +6 -0
  79. package/lib/infrastructure/config-check-utility.d.ts.map +1 -0
  80. package/lib/infrastructure/config-check-utility.js +67 -0
  81. package/lib/infrastructure/config-check-utility.js.map +1 -0
  82. package/lib/infrastructure/config-check-utility.test.d.ts +2 -0
  83. package/lib/infrastructure/config-check-utility.test.d.ts.map +1 -0
  84. package/lib/infrastructure/config-check-utility.test.js +109 -0
  85. package/lib/infrastructure/config-check-utility.test.js.map +1 -0
  86. package/lib/infrastructure/device-certificate.d.ts +10 -0
  87. package/lib/infrastructure/device-certificate.d.ts.map +1 -0
  88. package/lib/infrastructure/device-certificate.js +47 -0
  89. package/lib/infrastructure/device-certificate.js.map +1 -0
  90. package/lib/infrastructure/device-certificate.test.d.ts +2 -0
  91. package/lib/infrastructure/device-certificate.test.d.ts.map +1 -0
  92. package/lib/infrastructure/device-certificate.test.js +24 -0
  93. package/lib/infrastructure/device-certificate.test.js.map +1 -0
  94. package/lib/infrastructure/legacy-migration/legacy-file.test.d.ts +2 -0
  95. package/lib/infrastructure/legacy-migration/legacy-file.test.d.ts.map +1 -0
  96. package/lib/infrastructure/legacy-migration/legacy-file.test.js +61 -0
  97. package/lib/infrastructure/legacy-migration/legacy-file.test.js.map +1 -0
  98. package/lib/infrastructure/legacy-migration/legacy-files.d.ts +75 -0
  99. package/lib/infrastructure/legacy-migration/legacy-files.d.ts.map +1 -0
  100. package/lib/infrastructure/legacy-migration/legacy-files.js +75 -0
  101. package/lib/infrastructure/legacy-migration/legacy-files.js.map +1 -0
  102. package/lib/infrastructure/legacy-migration/legacy-migration.d.ts +6 -0
  103. package/lib/infrastructure/legacy-migration/legacy-migration.d.ts.map +1 -0
  104. package/lib/infrastructure/legacy-migration/legacy-migration.js +149 -0
  105. package/lib/infrastructure/legacy-migration/legacy-migration.js.map +1 -0
  106. package/lib/infrastructure/legacy-migration/legacy-migration.test.d.ts +2 -0
  107. package/lib/infrastructure/legacy-migration/legacy-migration.test.d.ts.map +1 -0
  108. package/lib/infrastructure/legacy-migration/legacy-migration.test.js +226 -0
  109. package/lib/infrastructure/legacy-migration/legacy-migration.test.js.map +1 -0
  110. package/lib/infrastructure/require-files-present-ready.test.d.ts +2 -0
  111. package/lib/infrastructure/require-files-present-ready.test.d.ts.map +1 -0
  112. package/lib/infrastructure/require-files-present-ready.test.js +44 -0
  113. package/lib/infrastructure/require-files-present-ready.test.js.map +1 -0
  114. package/lib/infrastructure/required-config-checks.d.ts +2 -0
  115. package/lib/infrastructure/required-config-checks.d.ts.map +1 -0
  116. package/lib/infrastructure/required-config-checks.js +30 -0
  117. package/lib/infrastructure/required-config-checks.js.map +1 -0
  118. package/lib/infrastructure/tokens-and-device-cfg.d.ts.map +1 -1
  119. package/lib/infrastructure/tokens-and-device-cfg.js +11 -8
  120. package/lib/infrastructure/tokens-and-device-cfg.js.map +1 -1
  121. package/lib/local-connection/rabbitmq-connection.d.ts.map +1 -1
  122. package/lib/local-connection/rabbitmq-connection.js +21 -21
  123. package/lib/local-connection/rabbitmq-connection.js.map +1 -1
  124. package/lib/secure-tunneling/secure-tunneling.d.ts +15 -23
  125. package/lib/secure-tunneling/secure-tunneling.d.ts.map +1 -1
  126. package/lib/secure-tunneling/secure-tunneling.js +52 -47
  127. package/lib/secure-tunneling/secure-tunneling.js.map +1 -1
  128. package/lib/secure-tunneling/secure-tunneling.test.js +29 -31
  129. package/lib/secure-tunneling/secure-tunneling.test.js.map +1 -1
  130. package/lib/subcommands/app/analytics.d.ts.map +1 -1
  131. package/lib/subcommands/app/analytics.js +1 -2
  132. package/lib/subcommands/app/analytics.js.map +1 -1
  133. package/lib/subcommands/app/env-vars.d.ts +4 -0
  134. package/lib/subcommands/app/env-vars.d.ts.map +1 -1
  135. package/lib/subcommands/app/env-vars.js +52 -6
  136. package/lib/subcommands/app/env-vars.js.map +1 -1
  137. package/lib/subcommands/app/index.d.ts.map +1 -1
  138. package/lib/subcommands/app/index.js +1 -3
  139. package/lib/subcommands/app/index.js.map +1 -1
  140. package/lib/subcommands/app/models.d.ts +0 -11
  141. package/lib/subcommands/app/models.d.ts.map +1 -1
  142. package/lib/subcommands/app/models.js +2 -58
  143. package/lib/subcommands/app/models.js.map +1 -1
  144. package/lib/subcommands/app/shadow.d.ts.map +1 -1
  145. package/lib/subcommands/app/shadow.js +6 -5
  146. package/lib/subcommands/app/shadow.js.map +1 -1
  147. package/lib/subcommands/app/version.d.ts.map +1 -1
  148. package/lib/subcommands/app/version.js +2 -4
  149. package/lib/subcommands/app/version.js.map +1 -1
  150. package/lib/subcommands/config.d.ts +2 -0
  151. package/lib/subcommands/config.d.ts.map +1 -0
  152. package/lib/subcommands/config.js +39 -0
  153. package/lib/subcommands/config.js.map +1 -0
  154. package/lib/subcommands/device/clean.d.ts +1 -1
  155. package/lib/subcommands/device/clean.d.ts.map +1 -1
  156. package/lib/subcommands/device/clean.js +25 -15
  157. package/lib/subcommands/device/clean.js.map +1 -1
  158. package/lib/subcommands/device/get-info.d.ts +2 -0
  159. package/lib/subcommands/device/get-info.d.ts.map +1 -0
  160. package/lib/subcommands/device/get-info.js +36 -0
  161. package/lib/subcommands/device/get-info.js.map +1 -0
  162. package/lib/subcommands/device/index.d.ts.map +1 -1
  163. package/lib/subcommands/device/index.js +13 -2
  164. package/lib/subcommands/device/index.js.map +1 -1
  165. package/lib/subcommands/device/init.d.ts +5 -0
  166. package/lib/subcommands/device/init.d.ts.map +1 -0
  167. package/lib/subcommands/device/{device.js → init.js} +10 -49
  168. package/lib/subcommands/device/init.js.map +1 -0
  169. package/lib/subcommands/device/migrate.d.ts +2 -0
  170. package/lib/subcommands/device/migrate.d.ts.map +1 -0
  171. package/lib/subcommands/device/migrate.js +24 -0
  172. package/lib/subcommands/device/migrate.js.map +1 -0
  173. package/lib/subcommands/device/refresh.d.ts +2 -0
  174. package/lib/subcommands/device/refresh.d.ts.map +1 -0
  175. package/lib/subcommands/device/refresh.js +25 -0
  176. package/lib/subcommands/device/refresh.js.map +1 -0
  177. package/lib/subcommands/device/restart.d.ts +2 -0
  178. package/lib/subcommands/device/restart.d.ts.map +1 -0
  179. package/lib/subcommands/device/restart.js +14 -0
  180. package/lib/subcommands/device/restart.js.map +1 -0
  181. package/lib/subcommands/index.d.ts +1 -1
  182. package/lib/subcommands/index.d.ts.map +1 -1
  183. package/lib/subcommands/index.js +3 -1
  184. package/lib/subcommands/index.js.map +1 -1
  185. package/lib/subcommands/rabbitmq-connection.d.ts +1 -1
  186. package/lib/subcommands/rabbitmq-connection.d.ts.map +1 -1
  187. package/lib/util/aai-error.d.ts +12 -0
  188. package/lib/util/aai-error.d.ts.map +1 -0
  189. package/lib/util/aai-error.js +11 -0
  190. package/lib/util/aai-error.js.map +1 -0
  191. package/lib/util/aws-regions.d.ts +2 -0
  192. package/lib/util/aws-regions.d.ts.map +1 -0
  193. package/lib/util/{cloud-mode-ready.js → aws-regions.js} +2 -20
  194. package/lib/util/aws-regions.js.map +1 -0
  195. package/lib/util/check-for-updates.d.ts +3 -0
  196. package/lib/util/check-for-updates.d.ts.map +1 -0
  197. package/lib/util/check-for-updates.js +46 -0
  198. package/lib/util/check-for-updates.js.map +1 -0
  199. package/lib/util/clean-certs.d.ts.map +1 -1
  200. package/lib/util/clean-certs.js +5 -4
  201. package/lib/util/clean-certs.js.map +1 -1
  202. package/lib/util/directories.d.ts +4 -18
  203. package/lib/util/directories.d.ts.map +1 -1
  204. package/lib/util/directories.js +18 -32
  205. package/lib/util/directories.js.map +1 -1
  206. package/lib/util/file.d.ts +11 -0
  207. package/lib/util/file.d.ts.map +1 -0
  208. package/lib/util/file.js +127 -0
  209. package/lib/util/file.js.map +1 -0
  210. package/lib/util/file.test.d.ts +2 -0
  211. package/lib/util/file.test.d.ts.map +1 -0
  212. package/lib/util/file.test.js +87 -0
  213. package/lib/util/file.test.js.map +1 -0
  214. package/lib/util/get-device-id.d.ts.map +1 -1
  215. package/lib/util/get-device-id.js +7 -1
  216. package/lib/util/get-device-id.js.map +1 -1
  217. package/lib/util/http-client.js +3 -3
  218. package/lib/util/http-client.js.map +1 -1
  219. package/package.json +22 -19
  220. package/readme.md +15 -35
  221. package/src/application-control/config.ts +10 -13
  222. package/src/application-control/environment-variables.test.ts +28 -7
  223. package/src/application-control/environment-variables.ts +13 -40
  224. package/src/application-control/index.ts +3 -16
  225. package/src/application-control/install.ts +16 -10
  226. package/src/application-control/models.ts +40 -98
  227. package/src/application-control/status.ts +9 -7
  228. package/src/application-control/utils.ts +1 -29
  229. package/src/cloud-connection/bootstrap-provision.ts +7 -7
  230. package/src/cloud-connection/device-agent-cloud-connection.ts +647 -509
  231. package/src/cloud-connection/device-agent.ts +16 -7
  232. package/src/cloud-connection/live-updates-handler.test.ts +137 -64
  233. package/src/cloud-connection/live-updates-handler.ts +103 -234
  234. package/src/cloud-connection/passthrough-handler.ts +134 -75
  235. package/src/cloud-connection/shadow-handler.test.ts +45 -57
  236. package/src/cloud-connection/shadow-handler.ts +114 -56
  237. package/src/cloud-connection/shadow.ts +4 -1
  238. package/src/cloud-connection/transaction-manager.test.ts +127 -3
  239. package/src/cloud-connection/transaction-manager.ts +68 -39
  240. package/src/device-control/device-control.ts +179 -72
  241. package/src/docker/docker-compose.ts +61 -0
  242. package/src/index.ts +2 -6
  243. package/src/infrastructure/agent-config.test.ts +9 -2
  244. package/src/infrastructure/agent-config.ts +45 -46
  245. package/src/infrastructure/config-check-utility.test.ts +154 -0
  246. package/src/infrastructure/config-check-utility.ts +77 -0
  247. package/src/infrastructure/device-certificate.test.ts +40 -0
  248. package/src/infrastructure/device-certificate.ts +58 -0
  249. package/src/infrastructure/legacy-migration/legacy-file.test.ts +88 -0
  250. package/src/infrastructure/legacy-migration/legacy-files.ts +101 -0
  251. package/src/infrastructure/legacy-migration/legacy-migration.test.ts +396 -0
  252. package/src/infrastructure/legacy-migration/legacy-migration.ts +229 -0
  253. package/src/infrastructure/require-files-present-ready.test.ts +53 -0
  254. package/src/infrastructure/required-config-checks.ts +33 -0
  255. package/src/infrastructure/tokens-and-device-cfg.ts +12 -10
  256. package/src/local-connection/rabbitmq-connection.ts +28 -23
  257. package/src/secure-tunneling/secure-tunneling.test.ts +37 -39
  258. package/src/secure-tunneling/secure-tunneling.ts +74 -69
  259. package/src/subcommands/app/analytics.ts +2 -4
  260. package/src/subcommands/app/env-vars.ts +72 -9
  261. package/src/subcommands/app/index.ts +3 -11
  262. package/src/subcommands/app/models.ts +5 -81
  263. package/src/subcommands/app/shadow.ts +6 -5
  264. package/src/subcommands/app/version.ts +3 -4
  265. package/src/subcommands/config.ts +42 -0
  266. package/src/subcommands/device/clean.ts +32 -18
  267. package/src/subcommands/device/get-info.ts +49 -0
  268. package/src/subcommands/device/index.ts +13 -2
  269. package/src/subcommands/device/{device.ts → init.ts} +11 -69
  270. package/src/subcommands/device/migrate.ts +20 -0
  271. package/src/subcommands/device/refresh.ts +23 -0
  272. package/src/subcommands/device/restart.ts +11 -0
  273. package/src/subcommands/index.ts +3 -1
  274. package/src/util/aai-error.ts +20 -0
  275. package/src/util/{cloud-mode-ready.ts → aws-regions.ts} +0 -24
  276. package/src/util/check-for-updates.ts +53 -0
  277. package/src/util/clean-certs.ts +8 -4
  278. package/src/util/directories.ts +23 -67
  279. package/src/util/file.test.ts +90 -0
  280. package/src/util/file.ts +156 -0
  281. package/src/util/get-device-id.ts +7 -7
  282. package/src/util/http-client.ts +2 -2
  283. package/lib/docker/docker-compose-cmd.d.ts +0 -5
  284. package/lib/docker/docker-compose-cmd.d.ts.map +0 -1
  285. package/lib/docker/docker-compose-cmd.js +0 -16
  286. package/lib/docker/docker-compose-cmd.js.map +0 -1
  287. package/lib/subcommands/device/device.d.ts +0 -7
  288. package/lib/subcommands/device/device.d.ts.map +0 -1
  289. package/lib/subcommands/device/device.js.map +0 -1
  290. package/lib/util/cloud-mode-ready.d.ts +0 -3
  291. package/lib/util/cloud-mode-ready.d.ts.map +0 -1
  292. package/lib/util/cloud-mode-ready.js.map +0 -1
  293. package/lib/util/download-file.d.ts +0 -6
  294. package/lib/util/download-file.d.ts.map +0 -1
  295. package/lib/util/download-file.js +0 -25
  296. package/lib/util/download-file.js.map +0 -1
  297. package/lib/util/fetch-with-timeout.d.ts +0 -4
  298. package/lib/util/fetch-with-timeout.d.ts.map +0 -1
  299. package/lib/util/fetch-with-timeout.js +0 -30
  300. package/lib/util/fetch-with-timeout.js.map +0 -1
  301. package/lib/util/parsing.d.ts +0 -2
  302. package/lib/util/parsing.d.ts.map +0 -1
  303. package/lib/util/parsing.js +0 -17
  304. package/lib/util/parsing.js.map +0 -1
  305. package/lib/util/safe-rimraf.d.ts +0 -2
  306. package/lib/util/safe-rimraf.d.ts.map +0 -1
  307. package/lib/util/safe-rimraf.js +0 -16
  308. package/lib/util/safe-rimraf.js.map +0 -1
  309. package/src/docker/docker-compose-cmd.ts +0 -15
  310. package/src/util/download-file.ts +0 -25
  311. package/src/util/fetch-with-timeout.ts +0 -35
  312. package/src/util/parsing.ts +0 -11
  313. package/src/util/safe-rimraf.ts +0 -14
@@ -4,32 +4,37 @@ exports.runDeviceAgentCloudInterface = exports.DeviceAgentCloudConnection = void
4
4
  // eslint-disable-next-line
5
5
  const awsIot = require('aws-iot-device-sdk');
6
6
  const device_agent_schemas_1 = require("@alwaysai/device-agent-schemas");
7
+ const infrastructure_1 = require("alwaysai/lib/infrastructure");
8
+ const util_1 = require("alwaysai/lib/util");
9
+ const child_process_1 = require("child_process");
7
10
  const fs_1 = require("fs");
11
+ const util_2 = require("util");
8
12
  const application_control_1 = require("../application-control");
9
13
  const backup_1 = require("../application-control/backup");
14
+ const models_1 = require("../application-control/models");
10
15
  const device_control_1 = require("../device-control/device-control");
11
16
  const environment_1 = require("../environment");
12
17
  const agent_config_1 = require("../infrastructure/agent-config");
18
+ const device_certificate_1 = require("../infrastructure/device-certificate");
19
+ const legacy_migration_1 = require("../infrastructure/legacy-migration/legacy-migration");
20
+ const required_config_checks_1 = require("../infrastructure/required-config-checks");
13
21
  const urls_1 = require("../infrastructure/urls");
14
22
  const secure_tunneling_1 = require("../secure-tunneling/secure-tunneling");
15
- const cloud_mode_ready_1 = require("../util/cloud-mode-ready");
23
+ const aai_error_1 = require("../util/aai-error");
24
+ const check_for_updates_1 = require("../util/check-for-updates");
16
25
  const directories_1 = require("../util/directories");
17
26
  const get_device_id_1 = require("../util/get-device-id");
18
27
  const logger_1 = require("../util/logger");
19
28
  const sleep_1 = require("../util/sleep");
20
29
  const bootstrap_provision_1 = require("./bootstrap-provision");
21
30
  const live_updates_handler_1 = require("./live-updates-handler");
31
+ const messages_1 = require("./messages");
22
32
  const passthrough_handler_1 = require("./passthrough-handler");
23
33
  const publisher_1 = require("./publisher");
24
34
  const shadow_handler_1 = require("./shadow-handler");
25
35
  const transaction_manager_1 = require("./transaction-manager");
26
- const child_process_1 = require("child_process");
27
- const util_1 = require("util");
28
- const exec_promise = (0, util_1.promisify)(child_process_1.exec);
36
+ const exec_promise = (0, util_2.promisify)(child_process_1.exec);
29
37
  class DeviceAgentCloudConnection {
30
- /*=================================================================
31
- Public interface
32
- =================================================================*/
33
38
  constructor() {
34
39
  this.device = awsIot.device;
35
40
  this.clientId = (0, get_device_id_1.getDeviceUuid)();
@@ -38,8 +43,6 @@ class DeviceAgentCloudConnection {
38
43
  this.toDeviceTopic = (0, device_agent_schemas_1.getToDeviceTopic)(this.clientId);
39
44
  this.secureTunnelNotifyTopic = `$aws/things/${this.clientId}/tunnels/notify`;
40
45
  this.secureTunnelHandler = secure_tunneling_1.SecureTunnelHandlerSingleton.getInstance();
41
- // FIXME: Add support for multiple simultaneous project updates
42
- this.appCfgUpdateQueue = [];
43
46
  this.handleAppStateControl = async (payload) => {
44
47
  const { baseCommand, projectId } = payload;
45
48
  switch (baseCommand) {
@@ -91,13 +94,13 @@ class DeviceAgentCloudConnection {
91
94
  }, projectId);
92
95
  return true;
93
96
  };
94
- this.handleModelsInstallCloudResponsePayload = async (payload) => {
95
- const update = this.appCfgUpdateQueue.shift();
97
+ this.handleModelsInstallCloudResponsePayload = async (payload, txId) => {
98
+ const projectId = payload.modelsInstallResponse.projectId;
99
+ const update = this.txnMgr.getAppCfgUpdateFromTxID(txId);
96
100
  if (update === undefined) {
97
101
  throw new Error('Unknown error while updating models via application config! No config present for model update.');
98
102
  }
99
103
  const { appCfgUpdate, envVarUpdate } = update;
100
- const projectId = payload.modelsInstallResponse.projectId;
101
104
  if (appCfgUpdate) {
102
105
  await this.atomicApplicationUpdate(async () => await (0, application_control_1.updateModelsWithPresignedUrls)({
103
106
  projectId,
@@ -128,14 +131,20 @@ class DeviceAgentCloudConnection {
128
131
  };
129
132
  const message = (0, device_agent_schemas_1.buildSignedUrlsRequestMessage)(this.clientId, modelsOnlyUrlsRequestPayload, txId);
130
133
  this.publisher.publishToCloud(message);
131
- this.appCfgUpdateQueue.push(update);
134
+ this.txnMgr.setAppCfgUpdateToTx(txId, update);
132
135
  return false;
133
136
  }
134
137
  if (appCfgUpdate) {
135
- await this.atomicApplicationUpdate(async () => await (0, application_control_1.updateAppCfg)({
136
- projectId,
137
- newAppCfg: appCfgUpdate.newAppCfg
138
- }), projectId);
138
+ await this.atomicApplicationUpdate(async () => {
139
+ await (0, models_1.pruneModels)({
140
+ projectId,
141
+ appCfg: appCfgUpdate.newAppCfg
142
+ });
143
+ await (0, application_control_1.updateAppCfg)({
144
+ projectId,
145
+ newAppCfg: appCfgUpdate.newAppCfg
146
+ });
147
+ }, projectId);
139
148
  }
140
149
  if (envVarUpdate) {
141
150
  await this.atomicApplicationUpdate(async () => await this.shadowHandler.updateProjectEnvVars({
@@ -146,17 +155,17 @@ class DeviceAgentCloudConnection {
146
155
  return true;
147
156
  };
148
157
  this.device = awsIot.device({
149
- keyPath: directories_1.DEVICE_PRIVATE_KEY_FILE_PATH,
150
- certPath: directories_1.DEVICE_CERTIFICATE_FILE_PATH,
158
+ keyPath: infrastructure_1.DEVICE_PRIVATE_KEY_FILE_PATH,
159
+ certPath: infrastructure_1.DEVICE_CERTIFICATE_FILE_PATH,
151
160
  caPath: directories_1.AWS_ROOT_CERTIFICATE_FILE_PATH,
152
161
  clientId: this.clientId,
153
162
  host: this.host,
154
163
  port: this.port,
155
- keepalive: 1 // time before re-connect attempt on dropped connection, default is 400 seconds
164
+ keepalive: 10 // time before re-connect attempt on dropped connection, default is 400 seconds
156
165
  });
157
166
  this.publisher = new publisher_1.Publisher(this.device, this.clientId);
158
167
  this.shadowHandler = new shadow_handler_1.ShadowHandler(this.clientId, this.publisher);
159
- this.liveUpdatesHandler = new live_updates_handler_1.LiveUpdatesHandler(this.publisher, this.clientId);
168
+ this.liveUpdatesHandler = new live_updates_handler_1.LiveUpdatesHandler();
160
169
  this.txnMgr = new transaction_manager_1.TransactionManager(this.publisher, this.liveUpdatesHandler);
161
170
  this.subscribe(this.toDeviceTopic);
162
171
  this.subscribe(this.secureTunnelNotifyTopic);
@@ -165,112 +174,134 @@ class DeviceAgentCloudConnection {
165
174
  }
166
175
  this.subscribe(this.shadowHandler.shadowTopics.secureTunnel.updateDelta);
167
176
  this.subscribe(this.shadowHandler.shadowTopics.secureTunnel.deleteAccepted);
177
+ this.setupHandlers();
168
178
  }
169
- async handleDeviceAction(payload) {
170
- const { system_restart } = device_agent_schemas_1.keyMirrors.deviceAction;
171
- switch (payload.action) {
172
- case system_restart: {
173
- await (0, device_control_1.reboot)();
174
- break;
175
- }
176
- default: {
177
- logger_1.logger.info(`Unrecognized device action requested: '${payload.action}'.`);
178
- }
179
- }
180
- }
181
- async publishCloudRequest(message) {
182
- this.publisher.publishToCloud(message);
179
+ /*=================================================================
180
+ Public interface
181
+ =================================================================*/
182
+ getClientId() {
183
+ return this.clientId;
183
184
  }
184
- subscribe(topic) {
185
- logger_1.logger.info(`Subscribing to ${topic}`);
186
- this.device.subscribe(topic);
185
+ isCmdInProgress(projectId) {
186
+ return this.txnMgr.isOngoingTransactionForProjectID(projectId);
187
187
  }
188
- async atomicApplicationUninstall(projectId) {
189
- try {
190
- await (0, application_control_1.uninstallApp)({ projectId });
191
- this.shadowHandler.clearProjectShadow(projectId);
188
+ async handleMessage(topic, message) {
189
+ logger_1.logger.debug(`Received message: ${JSON.stringify({ topic, message }, null, 2)}`);
190
+ // ProjectShadow messages
191
+ if (this.shadowHandler.projectShadowTopics.includes(topic)) {
192
+ await this.handleProjectShadowMessage(topic, message);
192
193
  }
193
- catch (e) {
194
- logger_1.logger.error(`Failed to uninstall ${projectId}: ${e.message}`);
195
- throw e;
194
+ else if (topic === this.toDeviceTopic) {
195
+ await this.handleDeviceAgentMessage({
196
+ topic,
197
+ message
198
+ });
199
+ // SecureTunnelNotify messages
200
+ }
201
+ else if (topic === this.secureTunnelNotifyTopic) {
202
+ await this.secureTunnelHandler.secureTunnelNotifyHandler(message);
203
+ // SecureTunnel messages
204
+ }
205
+ else if (topic === this.shadowHandler.shadowTopics.secureTunnel.updateDelta) {
206
+ await this.handleSecureTunnelMessage(message);
207
+ }
208
+ else if (topic === this.shadowHandler.shadowTopics.secureTunnel.deleteAccepted) {
209
+ logger_1.logger.info(`Received secure tunnel deleteAccepted: ${message}`);
210
+ await this.secureTunnelHandler.destroy();
211
+ }
212
+ else {
213
+ logger_1.logger.error(`Unexpected topic, ignoring! ${topic}`);
196
214
  }
197
215
  }
198
- // eslint-disable-next-line
199
- async atomicApplicationUpdate(func, projectId, skipUpdateShadow) {
200
- // First try to create a backup, so that there is one available if something goes wrong in the next try:catch.
201
- if (await (0, agent_config_1.AgentConfigFile)().isAppPresent({ projectId })) {
216
+ async stop() {
217
+ // This method is currently only used by the CLI, and shadow messages can be
218
+ // lost since we aren't waiting for responses so sleep for a short time to
219
+ // receive them
220
+ await (0, sleep_1.default)(1000);
221
+ this.device.end();
222
+ }
223
+ /*=================================================================
224
+ Private interface
225
+ =================================================================*/
226
+ setupHandlers() {
227
+ this.device.on('connect', (connack) => {
228
+ logger_1.logger.info('Device Agent has connected to the cloud');
229
+ // FIXME: EI-709 Skip this request for now to prevent kicking off another
230
+ // shadow update process if IoT Core disconnect occurs during app config update
231
+ //this.shadowHandler.getShadowUpdates();
232
+ void this.shadowHandler.updateSystemInfoShadow();
233
+ });
234
+ this.device.on('disconnect', () => {
235
+ logger_1.logger.warn('Device Agent has been disconnected from the cloud');
236
+ });
237
+ this.device.on('reconnect', () => {
238
+ logger_1.logger.info(`Device Agent attempting to re-connect ${new Date().toLocaleString()}`);
239
+ });
240
+ this.device.on('error', function (e) {
241
+ logger_1.logger.error(`Error connecting to cloud!\n${(0, util_1.stringifyError)(e)}`);
242
+ });
243
+ this.device.on('message', async (topic, payload) => {
202
244
  try {
203
- await (0, backup_1.createAppBackup)({ projectId });
245
+ const jsonPacket = JSON.parse(payload);
246
+ await this.handleMessage(topic, jsonPacket);
204
247
  }
205
248
  catch (e) {
206
- logger_1.logger.error(`Could not create a backup for the project: ${projectId}:\n${e.message}\n${e.stack}`);
249
+ logger_1.logger.error(`Error parsing message!\n${(0, util_1.stringifyError)(e)}`);
207
250
  }
208
- }
251
+ });
252
+ this.device.on('offline', () => {
253
+ logger_1.logger.warn(`Device Agent is offline ${new Date().toLocaleString()}`);
254
+ void this.logConnectionInfo();
255
+ });
256
+ }
257
+ async logConnectionInfo() {
209
258
  try {
210
- const out = await func();
211
- if (!skipUpdateShadow)
212
- await this.shadowHandler.updateProjectShadow(projectId);
213
- return out;
259
+ /**
260
+ * We're using the 'netcat' or 'nc' command to test the connection to the IoT Core endpoint.
261
+ * This command doesn't always exit (see below), so
262
+ * we use timeout to break out of the prompt
263
+ * and catch the resulting error/parse the resulting stderr
264
+ *
265
+ * Sample command for current host and port:
266
+ * nc -zv -w 1 a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 8883
267
+ *
268
+ * Sample output when port is not blocked and host is reachable:
269
+ * $ nc -zv -w 1 a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 443
270
+ * Connection to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 443 port [tcp/https] succeeded!
271
+ *
272
+ *
273
+ * Sample output when port is blocked (will repeatedly try until ctrl-C out):
274
+ * $ nc -zv -w 1 a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 8883
275
+ * nc: connect to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com port 8883 (tcp) timed out: Operation now in progress
276
+ * nc: connect to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com port 8883 (tcp) timed out: Operation now in progress
277
+ * nc: connect to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com port 8883 (tcp) timed out: Operation now in progress
278
+ * ^C
279
+ *
280
+ *
281
+ * Sample command/output when the port isn't enable on that host:
282
+ * $ nc -zv -w 1 localhost 8883
283
+ * nc: connect to localhost port 8883 (tcp) failed: Connection refused
284
+ */
285
+ await exec_promise(`nc -zv -w 1 ${this.host} ${this.port}`, {
286
+ timeout: 2000
287
+ });
214
288
  }
215
- catch (errorAppUpdate) {
216
- logger_1.logger.error(`Failed to update ${projectId}:\n${JSON.stringify(errorAppUpdate)}}`);
217
- // If something goes wrong, first try to rollback
218
- try {
219
- await (0, backup_1.rollbackApp)({ projectId });
220
- logger_1.logger.error(`Application update failed, rolled back to previous version: ${errorAppUpdate}`);
289
+ catch (err) {
290
+ const output = JSON.stringify(err['stderr']);
291
+ if (output.indexOf('not known') !== -1) {
292
+ logger_1.logger.warn('Iot Core endpoint appears to be unreachable, internet connection may be unstable or the host may be down.');
221
293
  }
222
- catch (errorRollbackApp) {
223
- // and if that fails, uninstall the app as a last resort.
224
- try {
225
- await this.atomicApplicationUninstall(projectId);
226
- }
227
- catch (_a) {
228
- // atomicApplicationUninstall handles failing, so there's nothing to handle here.
229
- }
230
- logger_1.logger.error(`Application update failed, rolled back to previous version: ${errorAppUpdate}`);
231
- throw new Error(`Application update and rollback failed, uninstalled the application: ${errorAppUpdate}`);
294
+ else if (output.indexOf('timed out') !== -1) {
295
+ logger_1.logger.warn(`Internet connection appears fine, however the endpoint was not reachable on the current connection port: ${this.port}\nPlease check if a firewall is in place.`);
232
296
  }
233
- throw new Error(`Application update failed, rolled the application back: ${errorAppUpdate}`);
234
- }
235
- }
236
- async handleProjectShadowMessage(topic, message) {
237
- const shadowUpdates = await this.shadowHandler.handleProjectShadow({
238
- topic,
239
- payload: message,
240
- clientToken: message.clientToken
241
- });
242
- if (shadowUpdates.length) {
243
- const shadowUpdatePromises = [];
244
- for (const shadowUpdate of shadowUpdates) {
245
- const projectId = shadowUpdate.projectId;
246
- const txId = shadowUpdate.txId;
247
- shadowUpdatePromises.push(this.txnMgr
248
- .runTransactionStep({
249
- func: () => this.handleProjectShadowConfigUpdate(shadowUpdate, txId),
250
- projectId,
251
- txId,
252
- start: true,
253
- stepName: topic
254
- })
255
- .catch((e) => {
256
- logger_1.logger.error(`There was an issue updating project shadow config: ${JSON.stringify(e)}`);
257
- }));
297
+ else if (output.indexOf('refused') !== -1) {
298
+ logger_1.logger.warn(`The connection was refused, likely ${this.host} is not running a service on ${this.port}.`);
299
+ }
300
+ else {
301
+ logger_1.logger.warn(`Output from checking connection to ${this.host} on ${this.port}: ${output}`);
258
302
  }
259
- await Promise.all(shadowUpdatePromises);
260
303
  }
261
304
  }
262
- getClientId() {
263
- return this.clientId;
264
- }
265
- getToDeviceTopic() {
266
- return this.toDeviceTopic;
267
- }
268
- isCmdInProgress(projectId) {
269
- return this.txnMgr.isOngoingTransactionForProjectID(projectId);
270
- }
271
- async updateProjectShadow(projectId) {
272
- await this.shadowHandler.updateProjectShadow(projectId);
273
- }
274
305
  async handleDeviceAgentMessage({ topic, message }) {
275
306
  const valid = (0, device_agent_schemas_1.validateToDeviceAgentMessage)(message);
276
307
  if (!valid) {
@@ -291,11 +322,12 @@ class DeviceAgentCloudConnection {
291
322
  projectId,
292
323
  txId,
293
324
  start: true,
325
+ liveUpdatesPublishFn: async () => this.publisher.publishToClient((0, device_agent_schemas_1.buildToClientStatusResponseMessage)(this.clientId, { status: device_agent_schemas_1.keyMirrors.statusResponse.in_progress }, txId), logger_1.logger.silly),
294
326
  stepName: payload.baseCommand
295
327
  });
296
328
  }
297
329
  catch (e) {
298
- logger_1.logger.error(`Error processing application state control request: ${e}!`);
330
+ logger_1.logger.error(`Error processing application state control request for ${projectId}!\n${(0, util_1.stringifyError)(e)}`);
299
331
  }
300
332
  break;
301
333
  }
@@ -309,18 +341,47 @@ class DeviceAgentCloudConnection {
309
341
  projectId,
310
342
  txId,
311
343
  start: true,
344
+ liveUpdatesPublishFn: async () => this.publisher.publishToClient((0, device_agent_schemas_1.buildToClientStatusResponseMessage)(this.clientId, { status: device_agent_schemas_1.keyMirrors.statusResponse.in_progress }, txId), logger_1.logger.silly),
312
345
  stepName: payload.baseCommand
313
346
  });
314
347
  }
315
348
  catch (e) {
316
- logger_1.logger.error(`Error processing application install request: ${e}!`);
349
+ logger_1.logger.error(`Error processing application install request for ${projectId}!\n${(0, util_1.stringifyError)(e)}`);
317
350
  }
318
351
  break;
319
352
  }
320
353
  case live_state_updates: {
321
- const payload = message.payload;
322
- // TODO: Send response?
323
- void this.liveUpdatesHandler.handleToggles(payload, txId);
354
+ const { deviceStats, appState, appLogs } = message.payload;
355
+ if (deviceStats !== undefined) {
356
+ if (deviceStats) {
357
+ await this.liveUpdatesHandler.enable(device_agent_schemas_1.keyMirrors.toClientMessageType.device_stats, async () => this.publisher.publishToClient((0, device_agent_schemas_1.buildDeviceStatsMessage)(this.clientId, await (0, messages_1.getDeviceStatsPayload)(), txId), logger_1.logger.silly));
358
+ }
359
+ else {
360
+ this.liveUpdatesHandler.disable(device_agent_schemas_1.keyMirrors.toClientMessageType.device_stats);
361
+ }
362
+ }
363
+ if (appState !== undefined) {
364
+ if (appState) {
365
+ await this.liveUpdatesHandler.enable(device_agent_schemas_1.keyMirrors.toClientMessageType.app_state, async () => this.publisher.publishToClient((0, device_agent_schemas_1.buildAppStateMessage)(this.clientId, await (0, messages_1.getAppStatePayload)(), txId), logger_1.logger.silly));
366
+ }
367
+ else {
368
+ this.liveUpdatesHandler.disable(device_agent_schemas_1.keyMirrors.toClientMessageType.app_state);
369
+ }
370
+ }
371
+ if (appLogs !== undefined) {
372
+ if (appLogs.toggle) {
373
+ await this.liveUpdatesHandler.startStream(appLogs.projectId, async () => await (0, application_control_1.getAppLogs)({
374
+ projectId: appLogs.projectId,
375
+ args: ['--tail', '100', '--no-log-prefix']
376
+ }), async (logChunk) => this.publisher.publishToClient((0, device_agent_schemas_1.buildAppLogsMessage)(this.clientId, {
377
+ projectId: appLogs.projectId,
378
+ logChunk
379
+ }, txId), logger_1.logger.silly));
380
+ }
381
+ else {
382
+ this.liveUpdatesHandler.stopStream(appLogs.projectId);
383
+ }
384
+ }
324
385
  break;
325
386
  }
326
387
  case app_install_response: {
@@ -347,7 +408,7 @@ class DeviceAgentCloudConnection {
347
408
  throw new Error(`Model install response received a message for a transaction ID ${txId} that is not currently underway (${this.txnMgr.getTransactionFromProject(projectId)})!`);
348
409
  }
349
410
  await this.txnMgr.runTransactionStep({
350
- func: () => this.handleModelsInstallCloudResponsePayload(payload),
411
+ func: () => this.handleModelsInstallCloudResponsePayload(payload, txId),
351
412
  projectId,
352
413
  txId,
353
414
  start: false,
@@ -358,39 +419,29 @@ class DeviceAgentCloudConnection {
358
419
  case status_response: {
359
420
  const { failure } = device_agent_schemas_1.keyMirrors.statusResponse;
360
421
  if (message.payload.status === failure) {
361
- this.txnMgr.completeTransaction(txId);
362
- const failureStatusResponsePayload = {
422
+ this.txnMgr.completeTransaction(txId, (0, device_agent_schemas_1.buildToClientStatusResponseMessage)(this.clientId, {
363
423
  status: device_agent_schemas_1.keyMirrors.statusResponse.failure,
364
424
  message: message.payload.message
365
- };
366
- // Send final status message
367
- const failureStatusResponseMessage = (0, device_agent_schemas_1.buildToClientStatusResponseMessage)(this.clientId, failureStatusResponsePayload, txId);
368
- this.publisher.publishToClient(failureStatusResponseMessage);
425
+ }, txId));
369
426
  }
370
427
  break;
371
428
  }
372
429
  case device_action: {
373
430
  try {
374
- const statusResponsePayload = {
431
+ this.publisher.publishToClient((0, device_agent_schemas_1.buildToClientStatusResponseMessage)(this.clientId, {
375
432
  status: device_agent_schemas_1.keyMirrors.statusResponse.in_progress
376
- };
377
- const statusResponseMessage = (0, device_agent_schemas_1.buildToClientStatusResponseMessage)(this.clientId, statusResponsePayload, txId);
378
- this.publisher.publishToClient(statusResponseMessage);
433
+ }, txId));
379
434
  await this.handleDeviceAction(message.payload);
380
- const successStatusResponsePayload = {
435
+ this.publisher.publishToClient((0, device_agent_schemas_1.buildToClientStatusResponseMessage)(this.clientId, {
381
436
  status: device_agent_schemas_1.keyMirrors.statusResponse.success
382
- };
383
- const successStatusResponseMessage = (0, device_agent_schemas_1.buildToClientStatusResponseMessage)(this.clientId, successStatusResponsePayload, txId);
384
- this.publisher.publishToClient(successStatusResponseMessage);
437
+ }, txId));
385
438
  }
386
439
  catch (e) {
387
- logger_1.logger.error(`There was a problem performing device action '${message.payload.action}': ${e.message}`);
388
- const failureStatusResponsePayload = {
440
+ logger_1.logger.error(`There was a problem performing device action '${message.payload.action}'!\n${(0, util_1.stringifyError)(e)}`);
441
+ this.publisher.publishToClient((0, device_agent_schemas_1.buildToClientStatusResponseMessage)(this.clientId, {
389
442
  status: device_agent_schemas_1.keyMirrors.statusResponse.failure,
390
443
  message: e.message
391
- };
392
- const failureStatusResponseMessage = (0, device_agent_schemas_1.buildToClientStatusResponseMessage)(this.clientId, failureStatusResponsePayload, txId);
393
- this.publisher.publishToClient(failureStatusResponseMessage);
444
+ }, txId));
394
445
  }
395
446
  break;
396
447
  }
@@ -398,142 +449,140 @@ class DeviceAgentCloudConnection {
398
449
  logger_1.logger.error(`Invalid client message: '${JSON.stringify({ topic, message, txId }, null, 2)}'`);
399
450
  }
400
451
  }
401
- async handleMessage(topic, message) {
402
- logger_1.logger.debug(`Received message: ${JSON.stringify({ topic, message }, null, 2)}`);
403
- // ProjectShadow messages
404
- if (this.shadowHandler.projectShadowTopics.includes(topic)) {
405
- await this.handleProjectShadowMessage(topic, message);
406
- }
407
- else if (topic === this.toDeviceTopic) {
408
- await this.handleDeviceAgentMessage({
409
- topic,
410
- message
411
- });
412
- // SecureTunnelNotify messages
413
- }
414
- else if (topic === this.secureTunnelNotifyTopic) {
415
- await this.secureTunnelHandler.secureTunnelNotifyHandler(message);
416
- // SecureTunnel messages
417
- }
418
- else if (topic === this.shadowHandler.shadowTopics.secureTunnel.updateDelta) {
419
- logger_1.logger.info(`Received secure tunnel update: ${message}`);
420
- const reported = await this.secureTunnelHandler.syncShadowToDeviceState(message);
421
- this.publisher.publish(this.shadowHandler.shadowTopics.secureTunnel.update, JSON.stringify({ state: { reported } }));
452
+ async handleDeviceAction(payload) {
453
+ const { system_restart } = device_agent_schemas_1.keyMirrors.deviceAction;
454
+ switch (payload.action) {
455
+ case system_restart: {
456
+ await (0, device_control_1.reboot)();
457
+ break;
458
+ }
459
+ default: {
460
+ logger_1.logger.info(`Unrecognized device action requested: '${payload.action}'.`);
461
+ }
422
462
  }
423
- else if (topic === this.shadowHandler.shadowTopics.secureTunnel.deleteAccepted) {
424
- logger_1.logger.info(`Received secure tunnel deleteAccepted: ${message}`);
425
- await this.secureTunnelHandler.destroy();
463
+ }
464
+ async publishCloudRequest(message) {
465
+ this.publisher.publishToCloud(message);
466
+ }
467
+ subscribe(topic) {
468
+ logger_1.logger.info(`Subscribing to ${topic}`);
469
+ this.device.subscribe(topic);
470
+ }
471
+ async atomicApplicationUninstall(projectId) {
472
+ try {
473
+ await (0, application_control_1.uninstallApp)({ projectId });
474
+ this.shadowHandler.clearProjectShadow(projectId);
426
475
  }
427
- else {
428
- logger_1.logger.error(`Unexpected topic, ignoring! ${topic}`);
476
+ catch (e) {
477
+ logger_1.logger.error(`Failed to uninstall ${projectId}!\n${(0, util_1.stringifyError)(e)}`);
478
+ throw e;
429
479
  }
430
480
  }
431
- async setupHandlers() {
432
- this.device.on('connect', (connack) => {
433
- logger_1.logger.info('Device Agent has connected to the cloud');
434
- // FIXME: EI-709 Skip this request for now to prevent kicking off another
435
- // shadow update process if IoT Core disconnect occurs during app config update
436
- //this.shadowHandler.getShadowUpdates();
437
- void this.shadowHandler.updateSystemInfoShadow();
438
- });
439
- this.device.on('disconnect', () => {
440
- logger_1.logger.warn('Device Agent has been disconnected from the cloud');
441
- });
442
- this.device.on('reconnect', () => {
443
- logger_1.logger.info(`Device Agent attempting to re-connect ${new Date().toLocaleString()}`);
444
- });
445
- this.device.on('error', function (error) {
446
- const errorString = error.message.toString();
447
- logger_1.logger.error(`${errorString}`);
448
- });
449
- this.device.on('message', async (topic, payload) => {
481
+ // eslint-disable-next-line
482
+ async atomicApplicationUpdate(func, projectId, skipUpdateShadow) {
483
+ if (await (0, agent_config_1.AgentConfigFile)().isAppPresent({ projectId })) {
484
+ // Reject the application update if app is present but not ready
485
+ if (!(await (0, agent_config_1.AgentConfigFile)().isAppReady({ projectId }))) {
486
+ throw new Error('Application already has installation in progress!');
487
+ }
488
+ // Try to create a backup, so that there is one available if something goes wrong in the next try:catch.
450
489
  try {
451
- const jsonPacket = JSON.parse(payload);
452
- await this.handleMessage(topic, jsonPacket);
490
+ await (0, backup_1.createAppBackup)({ projectId });
453
491
  }
454
492
  catch (e) {
455
- logger_1.logger.error(`Error parsing message: ${e.message}`);
493
+ logger_1.logger.error(`Could not create a backup for the project: ${projectId}!\n${(0, util_1.stringifyError)(e)}`);
456
494
  }
457
- });
458
- this.device.on('offline', () => {
459
- logger_1.logger.warn(`Device Agent is offline ${new Date().toLocaleString()}`);
460
- void this.logConnectionInfo();
461
- });
462
- }
463
- async logConnectionInfo() {
495
+ }
464
496
  try {
465
- /**
466
- * We're using the 'netcat' or 'nc' command to test the connection to the IoT Core endpoint.
467
- * This command doesn't always exit (see below), so
468
- * we use timeout to break out of the prompt
469
- * and catch the resulting error/parse the resulting stderr
470
- *
471
- * Sample command for current host and port:
472
- * nc -zv -w 1 a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 8883
473
- *
474
- * Sample output when port is not blocked and host is reachable:
475
- * $ nc -zv -w 1 a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 443
476
- * Connection to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 443 port [tcp/https] succeeded!
477
- *
478
- *
479
- * Sample output when port is blocked (will repeatedly try until ctrl-C out):
480
- * $ nc -zv -w 1 a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com 8883
481
- * nc: connect to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com port 8883 (tcp) timed out: Operation now in progress
482
- * nc: connect to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com port 8883 (tcp) timed out: Operation now in progress
483
- * nc: connect to a3tzi5g7sq5zsj-ats.iot.us-west-2.amazonaws.com port 8883 (tcp) timed out: Operation now in progress
484
- * ^C
485
- *
486
- *
487
- * Sample command/output when the port isn't enable on that host:
488
- * $ nc -zv -w 1 localhost 8883
489
- * nc: connect to localhost port 8883 (tcp) failed: Connection refused
490
- */
491
- await exec_promise(`nc -zv -w 1 ${this.host} ${this.port}`, {
492
- timeout: 2000
493
- });
497
+ const out = await func();
498
+ if (!skipUpdateShadow)
499
+ await this.shadowHandler.updateProjectShadow(projectId);
500
+ return out;
494
501
  }
495
- catch (err) {
496
- const output = JSON.stringify(err['stderr']);
497
- if (output.indexOf('not known') !== -1) {
498
- logger_1.logger.warn('Iot Core endpoint appears to be unreachable, internet connection may be unstable or the host may be down.');
499
- }
500
- else if (output.indexOf('timed out') !== -1) {
501
- logger_1.logger.warn(`Internet connection appears fine, however the endpoint was not reachable on the current connection port: ${this.port}\nPlease check if a firewall is in place.`);
502
+ catch (errorAppUpdate) {
503
+ logger_1.logger.error(`Failed to update ${projectId}!\n${(0, util_1.stringifyError)(errorAppUpdate)}`);
504
+ // If something goes wrong, first try to rollback
505
+ try {
506
+ await (0, backup_1.rollbackApp)({ projectId });
502
507
  }
503
- else if (output.indexOf('refused') !== -1) {
504
- logger_1.logger.warn(`The connection was refused, likely ${this.host} is not running a service on ${this.port}.`);
508
+ catch (errorRollbackApp) {
509
+ logger_1.logger.error(`Application rollback failed for ${projectId}!\n${(0, util_1.stringifyError)(errorRollbackApp)}`);
510
+ // and if that fails, uninstall the app as a last resort.
511
+ try {
512
+ await this.atomicApplicationUninstall(projectId);
513
+ }
514
+ catch (_a) {
515
+ // atomicApplicationUninstall logs failure, so there's nothing to do here.
516
+ }
517
+ throw new aai_error_1.default('Application update and rollback failed, uninstalled the application!', { cause: errorAppUpdate });
505
518
  }
506
- else {
507
- logger_1.logger.warn(`Output from checking connection to ${this.host} on ${this.port}: ${output}`);
519
+ throw new Error('Application update failed, rolled the application back!', { cause: errorAppUpdate });
520
+ }
521
+ }
522
+ async handleProjectShadowMessage(topic, message) {
523
+ const shadowUpdates = await this.shadowHandler.handleProjectShadow({
524
+ topic,
525
+ payload: message,
526
+ clientToken: message.clientToken
527
+ });
528
+ if (shadowUpdates.length) {
529
+ const shadowUpdatePromises = [];
530
+ for (const shadowUpdate of shadowUpdates) {
531
+ const projectId = shadowUpdate.projectId;
532
+ const txId = shadowUpdate.txId;
533
+ shadowUpdatePromises.push(this.txnMgr
534
+ .runTransactionStep({
535
+ func: () => this.handleProjectShadowConfigUpdate(shadowUpdate, txId),
536
+ projectId,
537
+ txId,
538
+ start: true,
539
+ liveUpdatesPublishFn: async () => this.publisher.publishToClient((0, device_agent_schemas_1.buildToClientStatusResponseMessage)(this.clientId, { status: device_agent_schemas_1.keyMirrors.statusResponse.in_progress }, txId), logger_1.logger.silly),
540
+ stepName: topic
541
+ })
542
+ .catch((e) => {
543
+ logger_1.logger.error(`There was an issue updating project shadow config for ${projectId}!\n${(0, util_1.stringifyError)(e)}`);
544
+ }));
508
545
  }
546
+ await Promise.all(shadowUpdatePromises);
509
547
  }
510
548
  }
511
- async stop() {
512
- // This method is currently only used by the CLI, and shadow messages can be
513
- // lost since we aren't waiting for responses so sleep for a short time to
514
- // receive them
515
- await (0, sleep_1.default)(1000);
516
- this.device.end();
549
+ async handleSecureTunnelMessage(payload) {
550
+ logger_1.logger.info(`Received secure tunnel update: ${JSON.stringify(payload)}`);
551
+ const state = (0, device_agent_schemas_1.getUpdateDeltaStateFromMessage)(payload);
552
+ if (!state) {
553
+ logger_1.logger.debug(`No state found in message: ${JSON.stringify(payload)}`);
554
+ return;
555
+ }
556
+ const valid = (0, device_agent_schemas_1.validateSecureTunnelShadowUpdate)(state);
557
+ if (!valid) {
558
+ logger_1.logger.error(`Error validating message: ${JSON.stringify({ payload, errors: device_agent_schemas_1.validateSecureTunnelShadowUpdate.errors }, null, 2)}`);
559
+ return;
560
+ }
561
+ const secureTunnelUpdate = await this.secureTunnelHandler.syncShadowToDeviceState(payload);
562
+ await this.shadowHandler.updateSecureTunnelShadow(secureTunnelUpdate);
517
563
  }
518
564
  }
519
565
  exports.DeviceAgentCloudConnection = DeviceAgentCloudConnection;
520
566
  async function runDeviceAgentCloudInterface() {
521
- if ((0, cloud_mode_ready_1.cloudModeReady)()) {
567
+ logger_1.logger.info(`Starting alwaysAI Device Agent v${await (0, check_for_updates_1.getDeviceAgentVersion)()}`);
568
+ if ((0, fs_1.existsSync)((0, device_certificate_1.getBootstrapPrivateKeyFilePath)())) {
569
+ await (0, bootstrap_provision_1.bootstrapProvision)();
570
+ return;
571
+ }
572
+ const filesAlreadyMigrated = await (0, required_config_checks_1.requiredConfigFilesPresentAndValid)();
573
+ if (!filesAlreadyMigrated) {
574
+ logger_1.logger.debug('Attempting configuration file migration.');
575
+ await (0, legacy_migration_1.migrateFromLegacyCertsAndTokens)();
576
+ }
577
+ if (await (0, required_config_checks_1.requiredConfigFilesPresentAndValid)()) {
522
578
  const deviceAgent = new DeviceAgentCloudConnection();
523
- await deviceAgent.setupHandlers();
524
579
  if (environment_1.ALWAYSAI_ANALYTICS_PASSTHROUGH === true) {
580
+ const shadowHandler = deviceAgent.shadowHandler;
525
581
  const publisher = deviceAgent.publisher;
526
- const passthroughHandler = new passthrough_handler_1.PassthroughHandler(publisher);
582
+ const passthroughHandler = new passthrough_handler_1.PassthroughHandler(publisher, shadowHandler);
527
583
  await passthroughHandler.setup();
528
- await (0, passthrough_handler_1.runChannel)(passthroughHandler);
529
584
  }
530
585
  }
531
- else if ((0, fs_1.existsSync)((0, directories_1.BOOTSTRAP_PRIVATE_KEY_FILE_PATH)())) {
532
- await (0, bootstrap_provision_1.bootstrapProvision)();
533
- }
534
- else if ((0, fs_1.existsSync)((0, directories_1.BOOTSTRAP_CERTIFICATES_DIR_PATH)())) {
535
- throw new Error("Device has not been created using 'aai-agent device init' or there has been an issue with device initialization");
536
- }
537
586
  else {
538
587
  throw new Error("Set device agent to local mode and retry the 'aai-agent device init' command");
539
588
  }