@alwaysai/device-agent 1.5.0 → 2.0.1

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 (305) hide show
  1. package/lib/application-control/config.d.ts.map +1 -1
  2. package/lib/application-control/config.js +8 -3
  3. package/lib/application-control/config.js.map +1 -1
  4. package/lib/application-control/environment-variables.d.ts +5 -5
  5. package/lib/application-control/environment-variables.d.ts.map +1 -1
  6. package/lib/application-control/environment-variables.js +25 -38
  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 +4 -1
  15. package/lib/application-control/install.d.ts.map +1 -1
  16. package/lib/application-control/install.js +24 -8
  17. package/lib/application-control/install.js.map +1 -1
  18. package/lib/application-control/models.d.ts +0 -11
  19. package/lib/application-control/models.d.ts.map +1 -1
  20. package/lib/application-control/models.js +5 -54
  21. package/lib/application-control/models.js.map +1 -1
  22. package/lib/application-control/utils.d.ts +0 -4
  23. package/lib/application-control/utils.d.ts.map +1 -1
  24. package/lib/application-control/utils.js +12 -22
  25. package/lib/application-control/utils.js.map +1 -1
  26. package/lib/cloud-connection/base-message-handler.d.ts +27 -0
  27. package/lib/cloud-connection/base-message-handler.d.ts.map +1 -0
  28. package/lib/cloud-connection/base-message-handler.js +72 -0
  29. package/lib/cloud-connection/base-message-handler.js.map +1 -0
  30. package/lib/cloud-connection/bootstrap-provision.js +3 -2
  31. package/lib/cloud-connection/bootstrap-provision.js.map +1 -1
  32. package/lib/cloud-connection/connection-manager.d.ts +21 -0
  33. package/lib/cloud-connection/connection-manager.d.ts.map +1 -0
  34. package/lib/cloud-connection/connection-manager.js +158 -0
  35. package/lib/cloud-connection/connection-manager.js.map +1 -0
  36. package/lib/cloud-connection/device-agent-cloud-connection.d.ts +9 -30
  37. package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
  38. package/lib/cloud-connection/device-agent-cloud-connection.js +69 -508
  39. package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
  40. package/lib/cloud-connection/device-agent-message-handler.d.ts +22 -0
  41. package/lib/cloud-connection/device-agent-message-handler.d.ts.map +1 -0
  42. package/lib/cloud-connection/device-agent-message-handler.js +357 -0
  43. package/lib/cloud-connection/device-agent-message-handler.js.map +1 -0
  44. package/lib/cloud-connection/device-agent.d.ts.map +1 -1
  45. package/lib/cloud-connection/device-agent.js +11 -9
  46. package/lib/cloud-connection/device-agent.js.map +1 -1
  47. package/lib/cloud-connection/live-updates-handler.d.ts +19 -28
  48. package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
  49. package/lib/cloud-connection/live-updates-handler.js +60 -172
  50. package/lib/cloud-connection/live-updates-handler.js.map +1 -1
  51. package/lib/cloud-connection/live-updates-handler.test.js +71 -165
  52. package/lib/cloud-connection/live-updates-handler.test.js.map +1 -1
  53. package/lib/cloud-connection/message-dispatcher.d.ts +10 -0
  54. package/lib/cloud-connection/message-dispatcher.d.ts.map +1 -0
  55. package/lib/cloud-connection/message-dispatcher.js +27 -0
  56. package/lib/cloud-connection/message-dispatcher.js.map +1 -0
  57. package/lib/cloud-connection/passthrough-handler.d.ts +4 -1
  58. package/lib/cloud-connection/passthrough-handler.d.ts.map +1 -1
  59. package/lib/cloud-connection/passthrough-handler.js +30 -11
  60. package/lib/cloud-connection/passthrough-handler.js.map +1 -1
  61. package/lib/cloud-connection/shadow-handler.d.ts +11 -3
  62. package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
  63. package/lib/cloud-connection/shadow-handler.js +133 -28
  64. package/lib/cloud-connection/shadow-handler.js.map +1 -1
  65. package/lib/cloud-connection/shadow-handler.test.js +45 -57
  66. package/lib/cloud-connection/shadow-handler.test.js.map +1 -1
  67. package/lib/cloud-connection/shadow.d.ts.map +1 -1
  68. package/lib/cloud-connection/shadow.js +2 -1
  69. package/lib/cloud-connection/shadow.js.map +1 -1
  70. package/lib/cloud-connection/transaction-manager.d.ts +12 -3
  71. package/lib/cloud-connection/transaction-manager.d.ts.map +1 -1
  72. package/lib/cloud-connection/transaction-manager.js +29 -28
  73. package/lib/cloud-connection/transaction-manager.js.map +1 -1
  74. package/lib/cloud-connection/transaction-manager.test.js +46 -5
  75. package/lib/cloud-connection/transaction-manager.test.js.map +1 -1
  76. package/lib/device-control/device-control.d.ts +8 -8
  77. package/lib/device-control/device-control.d.ts.map +1 -1
  78. package/lib/device-control/device-control.js +95 -71
  79. package/lib/device-control/device-control.js.map +1 -1
  80. package/lib/docker/docker-compose.d.ts.map +1 -1
  81. package/lib/docker/docker-compose.js +2 -1
  82. package/lib/docker/docker-compose.js.map +1 -1
  83. package/lib/infrastructure/agent-config.d.ts +2 -1
  84. package/lib/infrastructure/agent-config.d.ts.map +1 -1
  85. package/lib/infrastructure/agent-config.js +7 -7
  86. package/lib/infrastructure/agent-config.js.map +1 -1
  87. package/lib/infrastructure/agent-config.test.js +3 -1
  88. package/lib/infrastructure/agent-config.test.js.map +1 -1
  89. package/lib/infrastructure/config-check-utility.d.ts +6 -0
  90. package/lib/infrastructure/config-check-utility.d.ts.map +1 -0
  91. package/lib/infrastructure/config-check-utility.js +67 -0
  92. package/lib/infrastructure/config-check-utility.js.map +1 -0
  93. package/lib/infrastructure/config-check-utility.test.d.ts +2 -0
  94. package/lib/infrastructure/config-check-utility.test.d.ts.map +1 -0
  95. package/lib/infrastructure/config-check-utility.test.js +109 -0
  96. package/lib/infrastructure/config-check-utility.test.js.map +1 -0
  97. package/lib/infrastructure/device-certificate.d.ts +10 -0
  98. package/lib/infrastructure/device-certificate.d.ts.map +1 -0
  99. package/lib/infrastructure/device-certificate.js +47 -0
  100. package/lib/infrastructure/device-certificate.js.map +1 -0
  101. package/lib/infrastructure/device-certificate.test.d.ts +2 -0
  102. package/lib/infrastructure/device-certificate.test.d.ts.map +1 -0
  103. package/lib/infrastructure/device-certificate.test.js +24 -0
  104. package/lib/infrastructure/device-certificate.test.js.map +1 -0
  105. package/lib/infrastructure/legacy-migration/legacy-file.test.d.ts +2 -0
  106. package/lib/infrastructure/legacy-migration/legacy-file.test.d.ts.map +1 -0
  107. package/lib/infrastructure/legacy-migration/legacy-file.test.js +61 -0
  108. package/lib/infrastructure/legacy-migration/legacy-file.test.js.map +1 -0
  109. package/lib/infrastructure/legacy-migration/legacy-files.d.ts +75 -0
  110. package/lib/infrastructure/legacy-migration/legacy-files.d.ts.map +1 -0
  111. package/lib/infrastructure/legacy-migration/legacy-files.js +75 -0
  112. package/lib/infrastructure/legacy-migration/legacy-files.js.map +1 -0
  113. package/lib/infrastructure/legacy-migration/legacy-migration.d.ts +6 -0
  114. package/lib/infrastructure/legacy-migration/legacy-migration.d.ts.map +1 -0
  115. package/lib/infrastructure/legacy-migration/legacy-migration.js +149 -0
  116. package/lib/infrastructure/legacy-migration/legacy-migration.js.map +1 -0
  117. package/lib/infrastructure/legacy-migration/legacy-migration.test.d.ts +2 -0
  118. package/lib/infrastructure/legacy-migration/legacy-migration.test.d.ts.map +1 -0
  119. package/lib/infrastructure/legacy-migration/legacy-migration.test.js +226 -0
  120. package/lib/infrastructure/legacy-migration/legacy-migration.test.js.map +1 -0
  121. package/lib/infrastructure/require-files-present-ready.test.d.ts +2 -0
  122. package/lib/infrastructure/require-files-present-ready.test.d.ts.map +1 -0
  123. package/lib/infrastructure/require-files-present-ready.test.js +44 -0
  124. package/lib/infrastructure/require-files-present-ready.test.js.map +1 -0
  125. package/lib/infrastructure/required-config-checks.d.ts +2 -0
  126. package/lib/infrastructure/required-config-checks.d.ts.map +1 -0
  127. package/lib/infrastructure/required-config-checks.js +30 -0
  128. package/lib/infrastructure/required-config-checks.js.map +1 -0
  129. package/lib/infrastructure/tokens-and-device-cfg.d.ts.map +1 -1
  130. package/lib/infrastructure/tokens-and-device-cfg.js +11 -8
  131. package/lib/infrastructure/tokens-and-device-cfg.js.map +1 -1
  132. package/lib/jobs/job-handler.d.ts +23 -0
  133. package/lib/jobs/job-handler.d.ts.map +1 -0
  134. package/lib/jobs/job-handler.js +131 -0
  135. package/lib/jobs/job-handler.js.map +1 -0
  136. package/lib/local-connection/rabbitmq-connection.d.ts.map +1 -1
  137. package/lib/local-connection/rabbitmq-connection.js +14 -14
  138. package/lib/local-connection/rabbitmq-connection.js.map +1 -1
  139. package/lib/secure-tunneling/secure-tunnel-message-handler.d.ts +8 -0
  140. package/lib/secure-tunneling/secure-tunnel-message-handler.d.ts.map +1 -0
  141. package/lib/secure-tunneling/secure-tunnel-message-handler.js +42 -0
  142. package/lib/secure-tunneling/secure-tunnel-message-handler.js.map +1 -0
  143. package/lib/secure-tunneling/secure-tunneling.d.ts +9 -9
  144. package/lib/secure-tunneling/secure-tunneling.d.ts.map +1 -1
  145. package/lib/secure-tunneling/secure-tunneling.js +21 -16
  146. package/lib/secure-tunneling/secure-tunneling.js.map +1 -1
  147. package/lib/secure-tunneling/secure-tunneling.test.js +11 -13
  148. package/lib/secure-tunneling/secure-tunneling.test.js.map +1 -1
  149. package/lib/subcommands/app/analytics.d.ts.map +1 -1
  150. package/lib/subcommands/app/analytics.js +1 -2
  151. package/lib/subcommands/app/analytics.js.map +1 -1
  152. package/lib/subcommands/app/env-vars.d.ts +4 -0
  153. package/lib/subcommands/app/env-vars.d.ts.map +1 -1
  154. package/lib/subcommands/app/env-vars.js +52 -6
  155. package/lib/subcommands/app/env-vars.js.map +1 -1
  156. package/lib/subcommands/app/index.d.ts.map +1 -1
  157. package/lib/subcommands/app/index.js +1 -3
  158. package/lib/subcommands/app/index.js.map +1 -1
  159. package/lib/subcommands/app/models.d.ts +0 -11
  160. package/lib/subcommands/app/models.d.ts.map +1 -1
  161. package/lib/subcommands/app/models.js +2 -58
  162. package/lib/subcommands/app/models.js.map +1 -1
  163. package/lib/subcommands/app/shadow.d.ts.map +1 -1
  164. package/lib/subcommands/app/shadow.js +6 -5
  165. package/lib/subcommands/app/shadow.js.map +1 -1
  166. package/lib/subcommands/app/version.d.ts +2 -0
  167. package/lib/subcommands/app/version.d.ts.map +1 -1
  168. package/lib/subcommands/app/version.js +16 -6
  169. package/lib/subcommands/app/version.js.map +1 -1
  170. package/lib/subcommands/config.d.ts +2 -0
  171. package/lib/subcommands/config.d.ts.map +1 -0
  172. package/lib/subcommands/config.js +39 -0
  173. package/lib/subcommands/config.js.map +1 -0
  174. package/lib/subcommands/device/clean.d.ts +1 -1
  175. package/lib/subcommands/device/clean.d.ts.map +1 -1
  176. package/lib/subcommands/device/clean.js +23 -13
  177. package/lib/subcommands/device/clean.js.map +1 -1
  178. package/lib/subcommands/device/index.d.ts.map +1 -1
  179. package/lib/subcommands/device/index.js +3 -1
  180. package/lib/subcommands/device/index.js.map +1 -1
  181. package/lib/subcommands/device/init.js +8 -8
  182. package/lib/subcommands/device/init.js.map +1 -1
  183. package/lib/subcommands/device/migrate.d.ts +2 -0
  184. package/lib/subcommands/device/migrate.d.ts.map +1 -0
  185. package/lib/subcommands/device/migrate.js +24 -0
  186. package/lib/subcommands/device/migrate.js.map +1 -0
  187. package/lib/subcommands/device/refresh.d.ts.map +1 -1
  188. package/lib/subcommands/device/refresh.js +1 -0
  189. package/lib/subcommands/device/refresh.js.map +1 -1
  190. package/lib/subcommands/index.d.ts +1 -1
  191. package/lib/subcommands/index.d.ts.map +1 -1
  192. package/lib/subcommands/index.js +3 -1
  193. package/lib/subcommands/index.js.map +1 -1
  194. package/lib/subcommands/rabbitmq-connection.d.ts +1 -1
  195. package/lib/subcommands/rabbitmq-connection.d.ts.map +1 -1
  196. package/lib/util/aai-error.d.ts +12 -0
  197. package/lib/util/aai-error.d.ts.map +1 -0
  198. package/lib/util/aai-error.js +11 -0
  199. package/lib/util/aai-error.js.map +1 -0
  200. package/lib/util/aws-regions.d.ts +2 -0
  201. package/lib/util/aws-regions.d.ts.map +1 -0
  202. package/lib/util/{cloud-mode-ready.js → aws-regions.js} +2 -20
  203. package/lib/util/aws-regions.js.map +1 -0
  204. package/lib/util/check-for-updates.d.ts.map +1 -1
  205. package/lib/util/check-for-updates.js +5 -28
  206. package/lib/util/check-for-updates.js.map +1 -1
  207. package/lib/util/clean-certs.d.ts.map +1 -1
  208. package/lib/util/clean-certs.js +5 -4
  209. package/lib/util/clean-certs.js.map +1 -1
  210. package/lib/util/directories.d.ts +4 -18
  211. package/lib/util/directories.d.ts.map +1 -1
  212. package/lib/util/directories.js +18 -32
  213. package/lib/util/directories.js.map +1 -1
  214. package/lib/util/file.d.ts +4 -0
  215. package/lib/util/file.d.ts.map +1 -1
  216. package/lib/util/file.js +65 -4
  217. package/lib/util/file.js.map +1 -1
  218. package/lib/util/get-device-id.d.ts.map +1 -1
  219. package/lib/util/get-device-id.js +7 -1
  220. package/lib/util/get-device-id.js.map +1 -1
  221. package/lib/util/http-client.js +3 -3
  222. package/lib/util/http-client.js.map +1 -1
  223. package/package.json +19 -17
  224. package/readme.md +12 -32
  225. package/src/application-control/config.ts +9 -12
  226. package/src/application-control/environment-variables.test.ts +28 -7
  227. package/src/application-control/environment-variables.ts +42 -59
  228. package/src/application-control/index.ts +3 -16
  229. package/src/application-control/install.ts +39 -13
  230. package/src/application-control/models.ts +6 -87
  231. package/src/application-control/utils.ts +10 -25
  232. package/src/cloud-connection/base-message-handler.ts +118 -0
  233. package/src/cloud-connection/bootstrap-provision.ts +7 -7
  234. package/src/cloud-connection/connection-manager.ts +187 -0
  235. package/src/cloud-connection/device-agent-cloud-connection.ts +130 -723
  236. package/src/cloud-connection/device-agent-message-handler.ts +642 -0
  237. package/src/cloud-connection/device-agent.ts +16 -7
  238. package/src/cloud-connection/live-updates-handler.test.ts +121 -189
  239. package/src/cloud-connection/live-updates-handler.ts +105 -232
  240. package/src/cloud-connection/message-dispatcher.ts +33 -0
  241. package/src/cloud-connection/passthrough-handler.ts +55 -18
  242. package/src/cloud-connection/shadow-handler.test.ts +45 -57
  243. package/src/cloud-connection/shadow-handler.ts +224 -54
  244. package/src/cloud-connection/shadow.ts +4 -1
  245. package/src/cloud-connection/transaction-manager.test.ts +68 -6
  246. package/src/cloud-connection/transaction-manager.ts +69 -41
  247. package/src/device-control/device-control.ts +102 -70
  248. package/src/docker/docker-compose.ts +3 -2
  249. package/src/infrastructure/agent-config.test.ts +6 -2
  250. package/src/infrastructure/agent-config.ts +8 -7
  251. package/src/infrastructure/config-check-utility.test.ts +154 -0
  252. package/src/infrastructure/config-check-utility.ts +77 -0
  253. package/src/infrastructure/device-certificate.test.ts +40 -0
  254. package/src/infrastructure/device-certificate.ts +58 -0
  255. package/src/infrastructure/legacy-migration/legacy-file.test.ts +88 -0
  256. package/src/infrastructure/legacy-migration/legacy-files.ts +101 -0
  257. package/src/infrastructure/legacy-migration/legacy-migration.test.ts +396 -0
  258. package/src/infrastructure/legacy-migration/legacy-migration.ts +229 -0
  259. package/src/infrastructure/require-files-present-ready.test.ts +53 -0
  260. package/src/infrastructure/required-config-checks.ts +33 -0
  261. package/src/infrastructure/tokens-and-device-cfg.ts +12 -10
  262. package/src/jobs/job-handler.ts +146 -0
  263. package/src/local-connection/rabbitmq-connection.ts +22 -17
  264. package/src/secure-tunneling/secure-tunnel-message-handler.ts +56 -0
  265. package/src/secure-tunneling/secure-tunneling.test.ts +20 -22
  266. package/src/secure-tunneling/secure-tunneling.ts +41 -29
  267. package/src/subcommands/app/analytics.ts +2 -4
  268. package/src/subcommands/app/env-vars.ts +72 -9
  269. package/src/subcommands/app/index.ts +3 -11
  270. package/src/subcommands/app/models.ts +5 -81
  271. package/src/subcommands/app/shadow.ts +6 -5
  272. package/src/subcommands/app/version.ts +23 -6
  273. package/src/subcommands/config.ts +42 -0
  274. package/src/subcommands/device/clean.ts +31 -17
  275. package/src/subcommands/device/index.ts +3 -1
  276. package/src/subcommands/device/init.ts +11 -11
  277. package/src/subcommands/device/migrate.ts +20 -0
  278. package/src/subcommands/device/refresh.ts +1 -0
  279. package/src/subcommands/index.ts +3 -1
  280. package/src/util/aai-error.ts +20 -0
  281. package/src/util/{cloud-mode-ready.ts → aws-regions.ts} +0 -24
  282. package/src/util/check-for-updates.ts +14 -30
  283. package/src/util/clean-certs.ts +8 -4
  284. package/src/util/directories.ts +23 -67
  285. package/src/util/file.ts +83 -3
  286. package/src/util/get-device-id.ts +7 -7
  287. package/src/util/http-client.ts +2 -2
  288. package/lib/util/cloud-mode-ready.d.ts +0 -3
  289. package/lib/util/cloud-mode-ready.d.ts.map +0 -1
  290. package/lib/util/cloud-mode-ready.js.map +0 -1
  291. package/lib/util/download-file.d.ts +0 -6
  292. package/lib/util/download-file.d.ts.map +0 -1
  293. package/lib/util/download-file.js +0 -25
  294. package/lib/util/download-file.js.map +0 -1
  295. package/lib/util/fetch-with-timeout.d.ts +0 -4
  296. package/lib/util/fetch-with-timeout.d.ts.map +0 -1
  297. package/lib/util/fetch-with-timeout.js +0 -30
  298. package/lib/util/fetch-with-timeout.js.map +0 -1
  299. package/lib/util/parsing.d.ts +0 -2
  300. package/lib/util/parsing.d.ts.map +0 -1
  301. package/lib/util/parsing.js +0 -17
  302. package/lib/util/parsing.js.map +0 -1
  303. package/src/util/download-file.ts +0 -25
  304. package/src/util/fetch-with-timeout.ts +0 -35
  305. package/src/util/parsing.ts +0 -11
@@ -1,288 +1,161 @@
1
- import {
2
- AppLogsPayload,
3
- keyMirrors,
4
- LiveStateUpdatesTogglePayload,
5
- ToClientMessage,
6
- buildAppLogsMessage,
7
- buildAppStateMessage,
8
- buildDeviceStatsMessage,
9
- StatusResponsePayload,
10
- buildToClientStatusResponseMessage,
11
- ToClientMessageTypeValue
12
- } from '@alwaysai/device-agent-schemas';
13
- import { getAppLogs } from '../application-control';
1
+ import { ToClientMessageTypeValue } from '@alwaysai/device-agent-schemas';
14
2
  import { logger } from '../util/logger';
15
3
  import sleep from '../util/sleep';
16
- import { Publisher } from './publisher';
17
- import { getAppStatePayload, getDeviceStatsPayload } from './messages';
18
4
  import { ALWAYSAI_LIVE_UPDATES_TIMEOUT_MS } from '../environment';
5
+ import { stringifyError } from 'alwaysai/lib/util';
19
6
 
20
- const LIVE_UPDATES_TIMEOUT = ALWAYSAI_LIVE_UPDATES_TIMEOUT_MS
7
+ const KILL_ALL_TIMEOUT_MS = ALWAYSAI_LIVE_UPDATES_TIMEOUT_MS
21
8
  ? parseInt(ALWAYSAI_LIVE_UPDATES_TIMEOUT_MS)
22
9
  : 80000;
23
10
 
11
+ export const DEFAULT_INTERVALS_MS = 5000;
12
+
13
+ interface IntervalOptions {
14
+ ms?: number;
15
+ }
16
+
24
17
  export class LiveUpdatesHandler {
25
- private publisher: Publisher;
26
- private clientId: string;
18
+ private killAllTimeout: ReturnType<typeof setTimeout>;
19
+ private livingIntervals: Record<string, ReturnType<typeof setInterval>> = {};
20
+ private livingStreams = new Set<string>();
27
21
 
28
- private liveUpdatesTimeout: ReturnType<typeof setTimeout>;
29
- private liveUpdatesAlive = {
30
- [keyMirrors.toClientMessageType.device_stats]: false,
31
- [keyMirrors.toClientMessageType.app_state]: false,
32
- [keyMirrors.toClientMessageType.app_logs]: false,
33
- [keyMirrors.toClientMessageType.status_response]: false
34
- };
35
- private liveUpdatesSleepIntervals = {
36
- [keyMirrors.toClientMessageType.device_stats]: 5000,
37
- [keyMirrors.toClientMessageType.app_state]: 5000,
38
- [keyMirrors.toClientMessageType.app_logs]: 5000,
39
- [keyMirrors.toClientMessageType.status_response]: 5000
40
- };
22
+ constructor() {
23
+ logger.debug(
24
+ `Toggle live updates timeout set to ${KILL_ALL_TIMEOUT_MS} ms`
25
+ );
26
+ }
41
27
 
42
- private appLogStreams = new Set<string>();
43
- private transactionStatuses = new Set<string>();
28
+ public async enable(
29
+ intervalType: ToClientMessageTypeValue,
30
+ publishingFn: () => Promise<void>,
31
+ transactionId?: string,
32
+ options?: IntervalOptions
33
+ ) {
34
+ this.restartKillAllTimeout();
44
35
 
45
- private async getAppLogsWithRetry(
46
- projectId: string
47
- ): Promise<NodeJS.ReadableStream | null> {
48
- // Retry starting logs until it starts successfully or is terminated
49
- while (this.appLogStreams.has(projectId)) {
50
- try {
51
- return await getAppLogs({
52
- projectId,
53
- args: ['--tail', '100', '--no-log-prefix']
54
- });
55
- } catch (e) {
56
- logger.info(
57
- `Failed to start app logs, retrying in 1 second. Error: ${e}`
58
- );
59
- await sleep(1000);
60
- }
61
- }
62
- // Case where logs were disabled prior to connecting
63
- return null;
36
+ const key = this.generateIntervalKey(intervalType, transactionId);
37
+
38
+ this.safeSetInterval(key, publishingFn, options);
39
+
40
+ await publishingFn();
64
41
  }
65
42
 
66
- private async startAppLogStream(projectId: string, txId: string) {
43
+ public disable(
44
+ intervalType: ToClientMessageTypeValue,
45
+ transactionId?: string
46
+ ) {
47
+ const key = this.generateIntervalKey(intervalType, transactionId);
48
+ clearInterval(this.livingIntervals[key]);
49
+ delete this.livingIntervals[key];
50
+ }
51
+
52
+ public async startStream(
53
+ projectId: string,
54
+ streamGetter: () => Promise<NodeJS.ReadableStream | null>,
55
+ publishingFn: (logChunk: string) => void
56
+ ) {
67
57
  logger.info(`Starting log stream for ${projectId}`);
68
58
 
69
- this.appLogStreams.add(projectId);
59
+ this.livingStreams.add(projectId);
70
60
 
71
- const readable = await this.getAppLogsWithRetry(projectId);
61
+ const readable = await this.getStreamWithRetry(projectId, streamGetter);
72
62
 
73
63
  if (readable === null) {
74
64
  logger.info(
75
- `App log stream terminated for project ${projectId} prior to starting`
65
+ `Stream terminated prior to starting. Readable stream is null. ProjectId: ${projectId}`
76
66
  );
77
67
  return;
78
68
  }
79
69
 
80
70
  readable.on('data', async (chunk: Buffer) => {
81
- if (!this.appLogStreams.has(projectId)) {
71
+ if (!this.livingStreams.has(projectId)) {
82
72
  // why doesn't typescript know about this function?
83
73
  // @ts-ignore
84
74
  readable.destroy();
85
- logger.info(`App log stream terminated for project ${projectId}`);
75
+ logger.info(`Stream terminated. ProjectId: ${projectId}`);
86
76
  return;
87
77
  }
88
- const logStr = chunk.toString();
89
- const payload: AppLogsPayload = {
90
- projectId,
91
- logChunk: logStr
92
- };
93
- const message = buildAppLogsMessage(this.clientId, payload, txId);
94
- this.publisher.publishToClient(message, logger.silly);
78
+
79
+ publishingFn(chunk.toString());
95
80
  });
96
81
 
97
82
  readable.on('error', (error) => {
98
- logger.error(
99
- `App log stream terminated for project ${projectId}: ${error}`
100
- );
83
+ logger.error(`Stream terminated. ProjectId: ${projectId} - ${error}`);
101
84
  });
102
85
 
103
86
  readable.on('finished', () => {
104
- logger.info(`App logs finished piping for project ${projectId}`);
87
+ logger.info(`Strean complete. ProjectId: ${projectId}`);
105
88
  });
106
89
  }
107
90
 
108
- private continuePublishing(
109
- flag: ToClientMessageTypeValue,
110
- txId?: string
111
- ): boolean {
112
- switch (flag) {
113
- case keyMirrors.toClientMessageType.device_stats:
114
- case keyMirrors.toClientMessageType.app_state:
115
- return this.liveUpdatesAlive[flag];
116
- case keyMirrors.toClientMessageType.status_response: {
117
- if (!txId) {
118
- throw new Error(`Transaction ID not provided to continuePublishing!`);
119
- }
120
- return this.transactionStatuses.has(txId);
121
- }
122
- default:
123
- logger.error(`Unrecognized publishable flag ${flag}`);
124
- return false;
125
- }
126
- }
127
-
128
- private getLiveUpdatesInterval(flag: ToClientMessageTypeValue): number {
129
- const exists = this.liveUpdatesSleepIntervals[flag];
130
- if (exists) {
131
- return exists;
132
- }
133
- logger.error(`Unrecognized live updates flag ${flag}`);
134
- return -1;
91
+ public stopStream(streamId: string) {
92
+ this.livingStreams.delete(streamId);
135
93
  }
136
94
 
137
- private restartLiveUpdatesTimeout() {
138
- clearTimeout(this.liveUpdatesTimeout);
139
- this.liveUpdatesTimeout = setTimeout(() => {
140
- this.disableAppStateLiveUpdates();
141
- this.disableDeviceStatsLiveUpdates();
142
- this.appLogStreams.clear();
143
- }, LIVE_UPDATES_TIMEOUT);
144
- }
95
+ /*=================================================================
96
+ Private interface
97
+ =================================================================*/
145
98
 
146
- private async startPublishingLiveUpdates(
147
- messageType: ToClientMessageTypeValue,
148
- getMessage: () => Promise<ToClientMessage>,
149
- txId: string
150
- ) {
151
- logger.info(`Turned on live updates for ${messageType}`);
152
- while (this.continuePublishing(messageType, txId)) {
99
+ private async getStreamWithRetry(
100
+ streamId: string,
101
+ streamGetter: () => Promise<NodeJS.ReadableStream | null>
102
+ ): Promise<NodeJS.ReadableStream | null> {
103
+ // Retry starting logs until it starts successfully or is terminated
104
+ while (this.livingStreams.has(streamId)) {
153
105
  try {
154
- const message = await getMessage();
155
- this.publisher.publishToClient(message, logger.silly);
156
-
157
- await sleep(this.getLiveUpdatesInterval(messageType));
106
+ return await streamGetter();
158
107
  } catch (e) {
159
- logger.error(
160
- `Error publishing live updates for ${messageType}: ${e.message}`
108
+ logger.info(
109
+ `Failed to start app logs, retrying in 1 second.\n${stringifyError(
110
+ e
111
+ )}`
161
112
  );
113
+ await sleep(1000);
162
114
  }
163
115
  }
164
- logger.info(`Turned off live updates for ${messageType}`);
116
+ // Case where logs were disabled prior to connecting
117
+ return null;
165
118
  }
166
119
 
167
- /*=================================================================
168
- Public interface
169
- =================================================================*/
170
-
171
- constructor(publisher: Publisher, clientId: string) {
172
- this.publisher = publisher;
173
- this.clientId = clientId;
174
- logger.debug(
175
- `Toggle live updates timeout set to ${LIVE_UPDATES_TIMEOUT} ms`
120
+ // do not await any functions in the setSafeInterval other than inside setInterval() as per EI-1694.
121
+ private safeSetInterval(
122
+ key: string,
123
+ publishingFn: () => Promise<void>,
124
+ options?: IntervalOptions
125
+ ) {
126
+ clearInterval(this.livingIntervals[key]);
127
+
128
+ this.livingIntervals[key] = setInterval(
129
+ async () => {
130
+ try {
131
+ await publishingFn();
132
+ } catch (e) {
133
+ logger.error(
134
+ `Error getting live updates: ${JSON.stringify(
135
+ e
136
+ )}: intervalKey: ${key}. Continuing...`
137
+ );
138
+ }
139
+ },
140
+ options?.ms ? options.ms : DEFAULT_INTERVALS_MS
176
141
  );
177
142
  }
178
143
 
179
- public getDeviceStatsLiveUpdates(): boolean {
180
- return this.liveUpdatesAlive.device_stats;
181
- }
182
-
183
- public disableDeviceStatsLiveUpdates() {
184
- logger.info('Disabled live updates for device_stats');
185
- this.liveUpdatesAlive.device_stats = false;
186
- }
187
-
188
- public getAppStateLiveUpdates(): boolean {
189
- return this.liveUpdatesAlive.app_state;
190
- }
191
-
192
- public disableAppStateLiveUpdates() {
193
- logger.info('Disabled live updates for app_state');
194
- this.liveUpdatesAlive.app_state = false;
195
- }
196
-
197
- public getAppLogsLiveUpdates() {
198
- return this.liveUpdatesAlive.app_logs;
199
- }
200
-
201
- public async enableTransactionStatus(props: { txId: string }) {
202
- const { txId } = props;
203
- this.liveUpdatesAlive.status_response = true;
204
- if (!this.transactionStatuses.has(txId)) {
205
- this.transactionStatuses.add(txId);
206
- // Don't wait for this call to finish since it loops until disabled
207
- void this.startPublishingLiveUpdates(
208
- keyMirrors.toClientMessageType.status_response,
209
- async () => {
210
- const payload: StatusResponsePayload = {
211
- status: keyMirrors.statusResponse.in_progress
212
- };
213
- return buildToClientStatusResponseMessage(
214
- this.clientId,
215
- payload,
216
- txId
217
- );
218
- },
219
- txId
144
+ private restartKillAllTimeout() {
145
+ clearTimeout(this.killAllTimeout);
146
+ this.killAllTimeout = setTimeout(() => {
147
+ Object.values(this.livingIntervals).forEach((interval) =>
148
+ clearInterval(interval)
220
149
  );
221
- }
222
- }
223
-
224
- public async disableTransactionStatus(props: { txId: string }) {
225
- const { txId } = props;
226
- this.transactionStatuses.delete(txId);
227
-
228
- if (this.transactionStatuses.size === 0) {
229
- this.liveUpdatesAlive.status_response = false;
230
- }
150
+ this.livingIntervals = {};
151
+ this.livingStreams.clear();
152
+ }, KILL_ALL_TIMEOUT_MS);
231
153
  }
232
154
 
233
- public async handleToggles(
234
- toggles: LiveStateUpdatesTogglePayload,
235
- txId: string
236
- ) {
237
- const { deviceStats, appState, appLogs } = toggles;
238
- this.restartLiveUpdatesTimeout();
239
-
240
- const promises: Promise<void>[] = [];
241
-
242
- if (deviceStats !== undefined) {
243
- const currentDeviceStats = this.getDeviceStatsLiveUpdates();
244
- this.liveUpdatesAlive.device_stats = deviceStats;
245
- if (deviceStats && currentDeviceStats !== true) {
246
- // Don't wait for this call to finish since it loops until disabled
247
- const deviceStatsPromise = this.startPublishingLiveUpdates(
248
- keyMirrors.toClientMessageType.device_stats,
249
- async () => {
250
- const payload = await getDeviceStatsPayload();
251
- return buildDeviceStatsMessage(this.clientId, payload, txId);
252
- },
253
- txId
254
- );
255
- promises.push(deviceStatsPromise);
256
- }
257
- }
258
-
259
- if (appState !== undefined) {
260
- const currentAppState = this.getAppStateLiveUpdates();
261
- this.liveUpdatesAlive.app_state = appState;
262
- if (appState && currentAppState !== true) {
263
- // Don't wait for this call to finish since it loops until disabled
264
- const appStatePromise = this.startPublishingLiveUpdates(
265
- keyMirrors.toClientMessageType.app_state,
266
- async () => {
267
- const payload = await getAppStatePayload();
268
- return buildAppStateMessage(this.clientId, payload, txId);
269
- },
270
- txId
271
- );
272
- promises.push(appStatePromise);
273
- }
274
- }
275
-
276
- if (appLogs !== undefined) {
277
- const currentAppLogs = this.getAppLogsLiveUpdates();
278
- if (appLogs.toggle && currentAppLogs !== true) {
279
- // Don't wait for this call to finish since it loops until disabled
280
- const appLogPromise = this.startAppLogStream(appLogs.projectId, txId);
281
- promises.push(appLogPromise);
282
- } else {
283
- this.appLogStreams.delete(appLogs.projectId);
284
- }
285
- }
286
- return promises;
155
+ private generateIntervalKey(
156
+ intervalType: ToClientMessageTypeValue,
157
+ transactionId?: string
158
+ ): string {
159
+ return intervalType + `${transactionId ? ':' + transactionId : ''}`;
287
160
  }
288
161
  }
@@ -0,0 +1,33 @@
1
+ import { logger } from '../util/logger';
2
+
3
+ export interface MessageHandler<T = any> {
4
+ handle(payload: T, topic?: string): void;
5
+ }
6
+
7
+ export class MessageDispatcher<T = any> {
8
+ private handlers: Map<string, MessageHandler<T>> = new Map();
9
+
10
+ public registerHandler(topic: string, handler: MessageHandler): void {
11
+ this.handlers.set(topic, handler);
12
+ }
13
+
14
+ public dispatch(topic: string, payload: T): void {
15
+ const handler = this.handlers.get(topic);
16
+ if (handler) {
17
+ handler.handle(payload, topic);
18
+ } else {
19
+ this.handleUnknownMessage(topic, payload);
20
+ }
21
+ }
22
+
23
+ // Handle unknown or unregistered message topic
24
+ private handleUnknownMessage(topic: string, payload: T): void {
25
+ logger.error(
26
+ `No handler found for topic/type: ${topic} for message ${JSON.stringify(
27
+ payload,
28
+ null,
29
+ 2
30
+ )}`
31
+ );
32
+ }
33
+ }
@@ -1,16 +1,23 @@
1
+ import {
2
+ DeviceAgentStatusShadowUpdate,
3
+ PassthroughStatusValue
4
+ } from '@alwaysai/device-agent-schemas/lib/shadow-schema';
5
+ import { stringifyError } from 'alwaysai/lib/util';
1
6
  import * as amqp from 'amqplib';
7
+ import { ALWAYSAI_ANALYTICS_PASSTHROUGH } from '../environment';
2
8
  import {
3
9
  LOCAL_CONNECTION_HOST,
4
10
  LOCAL_CONNECTION_PORT,
5
11
  LOCAL_CONNECTION_ROUTING_KEY
6
12
  } from '../local-connection/constants';
7
13
  import {
8
- stopRabbitMQContainer,
9
- setupRabbitMQContainer
14
+ setupRabbitMQContainer,
15
+ stopRabbitMQContainer
10
16
  } from '../local-connection/rabbitmq-connection';
11
17
  import { logger } from '../util/logger';
12
18
  import sleep from '../util/sleep';
13
19
  import { Publisher } from './publisher';
20
+ import { ShadowHandler } from './shadow-handler';
14
21
 
15
22
  const messageQueue: any[] = [];
16
23
  const ackQueue: any[] = [];
@@ -18,12 +25,14 @@ const MAX_LOCAL_CONNECTION_ATTEMPTS = 10;
18
25
 
19
26
  export class PassthroughHandler {
20
27
  public publisher: Publisher;
28
+ public shadowHandler: ShadowHandler;
21
29
  public connection: amqp.Connection | undefined;
22
30
  public channel: amqp.Channel;
23
31
  public packetQueue;
24
32
 
25
- constructor(publisher: Publisher) {
33
+ constructor(publisher: Publisher, shadowHandler: ShadowHandler) {
26
34
  this.publisher = publisher;
35
+ this.shadowHandler = shadowHandler;
27
36
  }
28
37
 
29
38
  runChannel = async () => {
@@ -79,9 +88,13 @@ export class PassthroughHandler {
79
88
  );
80
89
  }
81
90
  } catch (e) {
82
- logger.error(`There was a problem parsing RabbitMQ packet ${e}`);
91
+ logger.error(
92
+ `There was a problem parsing RabbitMQ packet!\n${stringifyError(
93
+ e
94
+ )}`
95
+ );
83
96
  this.channel.ack(msg);
84
- logger.debug(`Problematic packet was acknowledged`);
97
+ logger.debug('Problematic packet was acknowledged');
85
98
  }
86
99
  }
87
100
  }
@@ -138,21 +151,45 @@ export class PassthroughHandler {
138
151
  }
139
152
  }
140
153
 
141
- public async setup() {
142
- logger.debug(
143
- `Setting up alwaysAI Local Connection on host: ${LOCAL_CONNECTION_HOST} and channel key: ${LOCAL_CONNECTION_ROUTING_KEY}`
154
+ private async publishPassthroughStatusUpdate(
155
+ status: PassthroughStatusValue,
156
+ message?: string
157
+ ) {
158
+ const deviceAgentPassthroughStatus: DeviceAgentStatusShadowUpdate = {
159
+ passthrough: {
160
+ status: status,
161
+ message: message ?? ''
162
+ }
163
+ };
164
+ await this.shadowHandler.updateDeviceAgentStatusShadow(
165
+ deviceAgentPassthroughStatus
144
166
  );
145
- await setupRabbitMQContainer();
146
- try {
147
- await this.establishLocalConnection();
148
- await this.runChannel();
149
- } catch (error) {
150
- logger.error(
151
- `There was a problem maintaining RabbitMQ connection: ${error}`
152
- );
153
- throw new Error(
154
- `There was a problem maintaining RabbitMQ connection: ${error}`
167
+ }
168
+
169
+ async setup() {
170
+ if (ALWAYSAI_ANALYTICS_PASSTHROUGH === true) {
171
+ logger.debug(
172
+ `Setting up alwaysAI Local Connection on host: ${LOCAL_CONNECTION_HOST} and channel key: ${LOCAL_CONNECTION_ROUTING_KEY}`
155
173
  );
174
+ await this.publishPassthroughStatusUpdate('starting');
175
+ await setupRabbitMQContainer();
176
+ try {
177
+ await this.establishLocalConnection();
178
+ await this.runChannel();
179
+ await this.publishPassthroughStatusUpdate(
180
+ 'running',
181
+ `Passthrough running on host: ${LOCAL_CONNECTION_HOST} and channel key: ${LOCAL_CONNECTION_ROUTING_KEY}`
182
+ );
183
+ } catch (e) {
184
+ logger.error(
185
+ `There was a problem maintaining RabbitMQ connection!\n${stringifyError(
186
+ e
187
+ )}`
188
+ );
189
+ await this.publishPassthroughStatusUpdate('error', stringifyError(e));
190
+ }
191
+ } else {
192
+ await this.publishPassthroughStatusUpdate('disabled');
156
193
  }
157
194
  }
158
195
  }