@alwaysai/device-agent 1.5.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 (273) 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 +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 +8 -7
  16. package/lib/application-control/install.js.map +1 -1
  17. package/lib/application-control/models.d.ts +0 -11
  18. package/lib/application-control/models.d.ts.map +1 -1
  19. package/lib/application-control/models.js +5 -54
  20. package/lib/application-control/models.js.map +1 -1
  21. package/lib/application-control/utils.d.ts +0 -4
  22. package/lib/application-control/utils.d.ts.map +1 -1
  23. package/lib/application-control/utils.js +1 -24
  24. package/lib/application-control/utils.js.map +1 -1
  25. package/lib/cloud-connection/bootstrap-provision.js +3 -2
  26. package/lib/cloud-connection/bootstrap-provision.js.map +1 -1
  27. package/lib/cloud-connection/device-agent-cloud-connection.d.ts +10 -15
  28. package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
  29. package/lib/cloud-connection/device-agent-cloud-connection.js +279 -250
  30. package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
  31. package/lib/cloud-connection/device-agent.d.ts.map +1 -1
  32. package/lib/cloud-connection/device-agent.js +11 -9
  33. package/lib/cloud-connection/device-agent.js.map +1 -1
  34. package/lib/cloud-connection/live-updates-handler.d.ts +18 -28
  35. package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
  36. package/lib/cloud-connection/live-updates-handler.js +54 -169
  37. package/lib/cloud-connection/live-updates-handler.js.map +1 -1
  38. package/lib/cloud-connection/live-updates-handler.test.js +71 -165
  39. package/lib/cloud-connection/live-updates-handler.test.js.map +1 -1
  40. package/lib/cloud-connection/passthrough-handler.d.ts +4 -1
  41. package/lib/cloud-connection/passthrough-handler.d.ts.map +1 -1
  42. package/lib/cloud-connection/passthrough-handler.js +30 -11
  43. package/lib/cloud-connection/passthrough-handler.js.map +1 -1
  44. package/lib/cloud-connection/shadow-handler.d.ts +5 -3
  45. package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
  46. package/lib/cloud-connection/shadow-handler.js +59 -27
  47. package/lib/cloud-connection/shadow-handler.js.map +1 -1
  48. package/lib/cloud-connection/shadow-handler.test.js +45 -57
  49. package/lib/cloud-connection/shadow-handler.test.js.map +1 -1
  50. package/lib/cloud-connection/shadow.d.ts.map +1 -1
  51. package/lib/cloud-connection/shadow.js +2 -1
  52. package/lib/cloud-connection/shadow.js.map +1 -1
  53. package/lib/cloud-connection/transaction-manager.d.ts +4 -2
  54. package/lib/cloud-connection/transaction-manager.d.ts.map +1 -1
  55. package/lib/cloud-connection/transaction-manager.js +18 -29
  56. package/lib/cloud-connection/transaction-manager.js.map +1 -1
  57. package/lib/cloud-connection/transaction-manager.test.js +3 -3
  58. package/lib/cloud-connection/transaction-manager.test.js.map +1 -1
  59. package/lib/device-control/device-control.d.ts +8 -8
  60. package/lib/device-control/device-control.d.ts.map +1 -1
  61. package/lib/device-control/device-control.js +95 -71
  62. package/lib/device-control/device-control.js.map +1 -1
  63. package/lib/docker/docker-compose.d.ts.map +1 -1
  64. package/lib/docker/docker-compose.js +2 -1
  65. package/lib/docker/docker-compose.js.map +1 -1
  66. package/lib/infrastructure/agent-config.d.ts +2 -1
  67. package/lib/infrastructure/agent-config.d.ts.map +1 -1
  68. package/lib/infrastructure/agent-config.js +7 -7
  69. package/lib/infrastructure/agent-config.js.map +1 -1
  70. package/lib/infrastructure/agent-config.test.js +3 -1
  71. package/lib/infrastructure/agent-config.test.js.map +1 -1
  72. package/lib/infrastructure/config-check-utility.d.ts +6 -0
  73. package/lib/infrastructure/config-check-utility.d.ts.map +1 -0
  74. package/lib/infrastructure/config-check-utility.js +67 -0
  75. package/lib/infrastructure/config-check-utility.js.map +1 -0
  76. package/lib/infrastructure/config-check-utility.test.d.ts +2 -0
  77. package/lib/infrastructure/config-check-utility.test.d.ts.map +1 -0
  78. package/lib/infrastructure/config-check-utility.test.js +109 -0
  79. package/lib/infrastructure/config-check-utility.test.js.map +1 -0
  80. package/lib/infrastructure/device-certificate.d.ts +10 -0
  81. package/lib/infrastructure/device-certificate.d.ts.map +1 -0
  82. package/lib/infrastructure/device-certificate.js +47 -0
  83. package/lib/infrastructure/device-certificate.js.map +1 -0
  84. package/lib/infrastructure/device-certificate.test.d.ts +2 -0
  85. package/lib/infrastructure/device-certificate.test.d.ts.map +1 -0
  86. package/lib/infrastructure/device-certificate.test.js +24 -0
  87. package/lib/infrastructure/device-certificate.test.js.map +1 -0
  88. package/lib/infrastructure/legacy-migration/legacy-file.test.d.ts +2 -0
  89. package/lib/infrastructure/legacy-migration/legacy-file.test.d.ts.map +1 -0
  90. package/lib/infrastructure/legacy-migration/legacy-file.test.js +61 -0
  91. package/lib/infrastructure/legacy-migration/legacy-file.test.js.map +1 -0
  92. package/lib/infrastructure/legacy-migration/legacy-files.d.ts +75 -0
  93. package/lib/infrastructure/legacy-migration/legacy-files.d.ts.map +1 -0
  94. package/lib/infrastructure/legacy-migration/legacy-files.js +75 -0
  95. package/lib/infrastructure/legacy-migration/legacy-files.js.map +1 -0
  96. package/lib/infrastructure/legacy-migration/legacy-migration.d.ts +6 -0
  97. package/lib/infrastructure/legacy-migration/legacy-migration.d.ts.map +1 -0
  98. package/lib/infrastructure/legacy-migration/legacy-migration.js +149 -0
  99. package/lib/infrastructure/legacy-migration/legacy-migration.js.map +1 -0
  100. package/lib/infrastructure/legacy-migration/legacy-migration.test.d.ts +2 -0
  101. package/lib/infrastructure/legacy-migration/legacy-migration.test.d.ts.map +1 -0
  102. package/lib/infrastructure/legacy-migration/legacy-migration.test.js +226 -0
  103. package/lib/infrastructure/legacy-migration/legacy-migration.test.js.map +1 -0
  104. package/lib/infrastructure/require-files-present-ready.test.d.ts +2 -0
  105. package/lib/infrastructure/require-files-present-ready.test.d.ts.map +1 -0
  106. package/lib/infrastructure/require-files-present-ready.test.js +44 -0
  107. package/lib/infrastructure/require-files-present-ready.test.js.map +1 -0
  108. package/lib/infrastructure/required-config-checks.d.ts +2 -0
  109. package/lib/infrastructure/required-config-checks.d.ts.map +1 -0
  110. package/lib/infrastructure/required-config-checks.js +30 -0
  111. package/lib/infrastructure/required-config-checks.js.map +1 -0
  112. package/lib/infrastructure/tokens-and-device-cfg.d.ts.map +1 -1
  113. package/lib/infrastructure/tokens-and-device-cfg.js +11 -8
  114. package/lib/infrastructure/tokens-and-device-cfg.js.map +1 -1
  115. package/lib/local-connection/rabbitmq-connection.d.ts.map +1 -1
  116. package/lib/local-connection/rabbitmq-connection.js +14 -14
  117. package/lib/local-connection/rabbitmq-connection.js.map +1 -1
  118. package/lib/secure-tunneling/secure-tunneling.d.ts +9 -9
  119. package/lib/secure-tunneling/secure-tunneling.d.ts.map +1 -1
  120. package/lib/secure-tunneling/secure-tunneling.js +21 -16
  121. package/lib/secure-tunneling/secure-tunneling.js.map +1 -1
  122. package/lib/secure-tunneling/secure-tunneling.test.js +11 -13
  123. package/lib/secure-tunneling/secure-tunneling.test.js.map +1 -1
  124. package/lib/subcommands/app/analytics.d.ts.map +1 -1
  125. package/lib/subcommands/app/analytics.js +1 -2
  126. package/lib/subcommands/app/analytics.js.map +1 -1
  127. package/lib/subcommands/app/env-vars.d.ts +4 -0
  128. package/lib/subcommands/app/env-vars.d.ts.map +1 -1
  129. package/lib/subcommands/app/env-vars.js +52 -6
  130. package/lib/subcommands/app/env-vars.js.map +1 -1
  131. package/lib/subcommands/app/index.d.ts.map +1 -1
  132. package/lib/subcommands/app/index.js +1 -3
  133. package/lib/subcommands/app/index.js.map +1 -1
  134. package/lib/subcommands/app/models.d.ts +0 -11
  135. package/lib/subcommands/app/models.d.ts.map +1 -1
  136. package/lib/subcommands/app/models.js +2 -58
  137. package/lib/subcommands/app/models.js.map +1 -1
  138. package/lib/subcommands/app/shadow.d.ts.map +1 -1
  139. package/lib/subcommands/app/shadow.js +6 -5
  140. package/lib/subcommands/app/shadow.js.map +1 -1
  141. package/lib/subcommands/app/version.d.ts.map +1 -1
  142. package/lib/subcommands/app/version.js +2 -4
  143. package/lib/subcommands/app/version.js.map +1 -1
  144. package/lib/subcommands/config.d.ts +2 -0
  145. package/lib/subcommands/config.d.ts.map +1 -0
  146. package/lib/subcommands/config.js +39 -0
  147. package/lib/subcommands/config.js.map +1 -0
  148. package/lib/subcommands/device/clean.d.ts +1 -1
  149. package/lib/subcommands/device/clean.d.ts.map +1 -1
  150. package/lib/subcommands/device/clean.js +23 -13
  151. package/lib/subcommands/device/clean.js.map +1 -1
  152. package/lib/subcommands/device/index.d.ts.map +1 -1
  153. package/lib/subcommands/device/index.js +3 -1
  154. package/lib/subcommands/device/index.js.map +1 -1
  155. package/lib/subcommands/device/init.js +8 -8
  156. package/lib/subcommands/device/init.js.map +1 -1
  157. package/lib/subcommands/device/migrate.d.ts +2 -0
  158. package/lib/subcommands/device/migrate.d.ts.map +1 -0
  159. package/lib/subcommands/device/migrate.js +24 -0
  160. package/lib/subcommands/device/migrate.js.map +1 -0
  161. package/lib/subcommands/device/refresh.d.ts.map +1 -1
  162. package/lib/subcommands/device/refresh.js +1 -0
  163. package/lib/subcommands/device/refresh.js.map +1 -1
  164. package/lib/subcommands/index.d.ts +1 -1
  165. package/lib/subcommands/index.d.ts.map +1 -1
  166. package/lib/subcommands/index.js +3 -1
  167. package/lib/subcommands/index.js.map +1 -1
  168. package/lib/subcommands/rabbitmq-connection.d.ts +1 -1
  169. package/lib/subcommands/rabbitmq-connection.d.ts.map +1 -1
  170. package/lib/util/aai-error.d.ts +12 -0
  171. package/lib/util/aai-error.d.ts.map +1 -0
  172. package/lib/util/aai-error.js +11 -0
  173. package/lib/util/aai-error.js.map +1 -0
  174. package/lib/util/aws-regions.d.ts +2 -0
  175. package/lib/util/aws-regions.d.ts.map +1 -0
  176. package/lib/util/{cloud-mode-ready.js → aws-regions.js} +2 -20
  177. package/lib/util/aws-regions.js.map +1 -0
  178. package/lib/util/check-for-updates.d.ts.map +1 -1
  179. package/lib/util/check-for-updates.js +5 -28
  180. package/lib/util/check-for-updates.js.map +1 -1
  181. package/lib/util/clean-certs.d.ts.map +1 -1
  182. package/lib/util/clean-certs.js +5 -4
  183. package/lib/util/clean-certs.js.map +1 -1
  184. package/lib/util/directories.d.ts +4 -18
  185. package/lib/util/directories.d.ts.map +1 -1
  186. package/lib/util/directories.js +18 -32
  187. package/lib/util/directories.js.map +1 -1
  188. package/lib/util/file.d.ts +4 -0
  189. package/lib/util/file.d.ts.map +1 -1
  190. package/lib/util/file.js +65 -4
  191. package/lib/util/file.js.map +1 -1
  192. package/lib/util/get-device-id.d.ts.map +1 -1
  193. package/lib/util/get-device-id.js +7 -1
  194. package/lib/util/get-device-id.js.map +1 -1
  195. package/lib/util/http-client.js +3 -3
  196. package/lib/util/http-client.js.map +1 -1
  197. package/package.json +19 -17
  198. package/readme.md +12 -32
  199. package/src/application-control/config.ts +9 -12
  200. package/src/application-control/environment-variables.test.ts +28 -7
  201. package/src/application-control/environment-variables.ts +13 -40
  202. package/src/application-control/index.ts +3 -16
  203. package/src/application-control/install.ts +15 -10
  204. package/src/application-control/models.ts +6 -87
  205. package/src/application-control/utils.ts +0 -28
  206. package/src/cloud-connection/bootstrap-provision.ts +7 -7
  207. package/src/cloud-connection/device-agent-cloud-connection.ts +639 -525
  208. package/src/cloud-connection/device-agent.ts +16 -7
  209. package/src/cloud-connection/live-updates-handler.test.ts +121 -189
  210. package/src/cloud-connection/live-updates-handler.ts +99 -234
  211. package/src/cloud-connection/passthrough-handler.ts +55 -18
  212. package/src/cloud-connection/shadow-handler.test.ts +45 -57
  213. package/src/cloud-connection/shadow-handler.ts +103 -57
  214. package/src/cloud-connection/shadow.ts +4 -1
  215. package/src/cloud-connection/transaction-manager.test.ts +3 -3
  216. package/src/cloud-connection/transaction-manager.ts +53 -39
  217. package/src/device-control/device-control.ts +102 -70
  218. package/src/docker/docker-compose.ts +3 -2
  219. package/src/infrastructure/agent-config.test.ts +6 -2
  220. package/src/infrastructure/agent-config.ts +8 -7
  221. package/src/infrastructure/config-check-utility.test.ts +154 -0
  222. package/src/infrastructure/config-check-utility.ts +77 -0
  223. package/src/infrastructure/device-certificate.test.ts +40 -0
  224. package/src/infrastructure/device-certificate.ts +58 -0
  225. package/src/infrastructure/legacy-migration/legacy-file.test.ts +88 -0
  226. package/src/infrastructure/legacy-migration/legacy-files.ts +101 -0
  227. package/src/infrastructure/legacy-migration/legacy-migration.test.ts +396 -0
  228. package/src/infrastructure/legacy-migration/legacy-migration.ts +229 -0
  229. package/src/infrastructure/require-files-present-ready.test.ts +53 -0
  230. package/src/infrastructure/required-config-checks.ts +33 -0
  231. package/src/infrastructure/tokens-and-device-cfg.ts +12 -10
  232. package/src/local-connection/rabbitmq-connection.ts +22 -17
  233. package/src/secure-tunneling/secure-tunneling.test.ts +20 -22
  234. package/src/secure-tunneling/secure-tunneling.ts +41 -29
  235. package/src/subcommands/app/analytics.ts +2 -4
  236. package/src/subcommands/app/env-vars.ts +72 -9
  237. package/src/subcommands/app/index.ts +3 -11
  238. package/src/subcommands/app/models.ts +5 -81
  239. package/src/subcommands/app/shadow.ts +6 -5
  240. package/src/subcommands/app/version.ts +3 -4
  241. package/src/subcommands/config.ts +42 -0
  242. package/src/subcommands/device/clean.ts +31 -17
  243. package/src/subcommands/device/index.ts +3 -1
  244. package/src/subcommands/device/init.ts +11 -11
  245. package/src/subcommands/device/migrate.ts +20 -0
  246. package/src/subcommands/device/refresh.ts +1 -0
  247. package/src/subcommands/index.ts +3 -1
  248. package/src/util/aai-error.ts +20 -0
  249. package/src/util/{cloud-mode-ready.ts → aws-regions.ts} +0 -24
  250. package/src/util/check-for-updates.ts +14 -30
  251. package/src/util/clean-certs.ts +8 -4
  252. package/src/util/directories.ts +23 -67
  253. package/src/util/file.ts +83 -3
  254. package/src/util/get-device-id.ts +7 -7
  255. package/src/util/http-client.ts +2 -2
  256. package/lib/util/cloud-mode-ready.d.ts +0 -3
  257. package/lib/util/cloud-mode-ready.d.ts.map +0 -1
  258. package/lib/util/cloud-mode-ready.js.map +0 -1
  259. package/lib/util/download-file.d.ts +0 -6
  260. package/lib/util/download-file.d.ts.map +0 -1
  261. package/lib/util/download-file.js +0 -25
  262. package/lib/util/download-file.js.map +0 -1
  263. package/lib/util/fetch-with-timeout.d.ts +0 -4
  264. package/lib/util/fetch-with-timeout.d.ts.map +0 -1
  265. package/lib/util/fetch-with-timeout.js +0 -30
  266. package/lib/util/fetch-with-timeout.js.map +0 -1
  267. package/lib/util/parsing.d.ts +0 -2
  268. package/lib/util/parsing.d.ts.map +0 -1
  269. package/lib/util/parsing.js +0 -17
  270. package/lib/util/parsing.js.map +0 -1
  271. package/src/util/download-file.ts +0 -25
  272. package/src/util/fetch-with-timeout.ts +0 -35
  273. package/src/util/parsing.ts +0 -11
@@ -1,288 +1,153 @@
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
+ clearInterval(this.livingIntervals[key]);
39
+
40
+ // initial publish
41
+ await publishingFn();
42
+
43
+ this.livingIntervals[key] = setInterval(
44
+ async () => {
45
+ try {
46
+ await publishingFn();
47
+ } catch (e) {
48
+ logger.error(
49
+ `Error getting live updates: ${JSON.stringify(
50
+ e
51
+ )}: intervalKey: ${key}. Continuing...`
52
+ );
53
+ }
54
+ },
55
+ options?.ms ? options.ms : DEFAULT_INTERVALS_MS
56
+ );
57
+ }
58
+
59
+ public disable(
60
+ intervalType: ToClientMessageTypeValue,
61
+ transactionId?: string
62
+ ) {
63
+ const key = this.generateIntervalKey(intervalType, transactionId);
64
+ clearInterval(this.livingIntervals[key]);
65
+ delete this.livingIntervals[key];
64
66
  }
65
67
 
66
- private async startAppLogStream(projectId: string, txId: string) {
68
+ public async startStream(
69
+ projectId: string,
70
+ streamGetter: () => Promise<NodeJS.ReadableStream | null>,
71
+ publishingFn: (logChunk: string) => void
72
+ ) {
67
73
  logger.info(`Starting log stream for ${projectId}`);
68
74
 
69
- this.appLogStreams.add(projectId);
75
+ this.livingStreams.add(projectId);
70
76
 
71
- const readable = await this.getAppLogsWithRetry(projectId);
77
+ const readable = await this.getStreamWithRetry(projectId, streamGetter);
72
78
 
73
79
  if (readable === null) {
74
80
  logger.info(
75
- `App log stream terminated for project ${projectId} prior to starting`
81
+ `Stream terminated prior to starting. Readable stream is null. ProjectId: ${projectId}`
76
82
  );
77
83
  return;
78
84
  }
79
85
 
80
86
  readable.on('data', async (chunk: Buffer) => {
81
- if (!this.appLogStreams.has(projectId)) {
87
+ if (!this.livingStreams.has(projectId)) {
82
88
  // why doesn't typescript know about this function?
83
89
  // @ts-ignore
84
90
  readable.destroy();
85
- logger.info(`App log stream terminated for project ${projectId}`);
91
+ logger.info(`Stream terminated. ProjectId: ${projectId}`);
86
92
  return;
87
93
  }
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);
94
+
95
+ publishingFn(chunk.toString());
95
96
  });
96
97
 
97
98
  readable.on('error', (error) => {
98
- logger.error(
99
- `App log stream terminated for project ${projectId}: ${error}`
100
- );
99
+ logger.error(`Stream terminated. ProjectId: ${projectId} - ${error}`);
101
100
  });
102
101
 
103
102
  readable.on('finished', () => {
104
- logger.info(`App logs finished piping for project ${projectId}`);
103
+ logger.info(`Strean complete. ProjectId: ${projectId}`);
105
104
  });
106
105
  }
107
106
 
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;
107
+ public stopStream(streamId: string) {
108
+ this.livingStreams.delete(streamId);
135
109
  }
136
110
 
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
- }
111
+ /*=================================================================
112
+ Private interface
113
+ =================================================================*/
145
114
 
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)) {
115
+ private async getStreamWithRetry(
116
+ streamId: string,
117
+ streamGetter: () => Promise<NodeJS.ReadableStream | null>
118
+ ): Promise<NodeJS.ReadableStream | null> {
119
+ // Retry starting logs until it starts successfully or is terminated
120
+ while (this.livingStreams.has(streamId)) {
153
121
  try {
154
- const message = await getMessage();
155
- this.publisher.publishToClient(message, logger.silly);
156
-
157
- await sleep(this.getLiveUpdatesInterval(messageType));
122
+ return await streamGetter();
158
123
  } catch (e) {
159
- logger.error(
160
- `Error publishing live updates for ${messageType}: ${e.message}`
124
+ logger.info(
125
+ `Failed to start app logs, retrying in 1 second.\n${stringifyError(
126
+ e
127
+ )}`
161
128
  );
129
+ await sleep(1000);
162
130
  }
163
131
  }
164
- logger.info(`Turned off live updates for ${messageType}`);
165
- }
166
-
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`
176
- );
177
- }
178
-
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;
132
+ // Case where logs were disabled prior to connecting
133
+ return null;
199
134
  }
200
135
 
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
136
+ private restartKillAllTimeout() {
137
+ clearTimeout(this.killAllTimeout);
138
+ this.killAllTimeout = setTimeout(() => {
139
+ Object.values(this.livingIntervals).forEach((interval) =>
140
+ clearInterval(interval)
220
141
  );
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
- }
142
+ this.livingIntervals = {};
143
+ this.livingStreams.clear();
144
+ }, KILL_ALL_TIMEOUT_MS);
231
145
  }
232
146
 
233
- public async handleToggles(
234
- toggles: LiveStateUpdatesTogglePayload,
235
- txId: string
147
+ private generateIntervalKey(
148
+ intervalType: ToClientMessageTypeValue,
149
+ transactionId?: string
236
150
  ) {
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;
151
+ return intervalType + `${transactionId ? ':' + transactionId : ''}`;
287
152
  }
288
153
  }
@@ -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
  }