@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,12 +1,12 @@
1
1
  import {
2
- StatusResponsePayload,
2
+ ToClientStatusResponseMessage,
3
3
  buildToClientStatusResponseMessage,
4
4
  keyMirrors
5
5
  } from '@alwaysai/device-agent-schemas';
6
6
  import { LiveUpdatesHandler } from './live-updates-handler';
7
7
  import { Publisher } from './publisher';
8
8
  import { logger } from '../util/logger';
9
- import { keyMirror } from 'alwaysai/lib/util';
9
+ import { keyMirror, stringifyError } from 'alwaysai/lib/util';
10
10
  import { CodedError } from '@carnesen/coded-error';
11
11
  import { ShadowUpdate } from './shadow-handler';
12
12
 
@@ -26,11 +26,12 @@ export class TransactionManager {
26
26
  private liveUpdatesHandler: LiveUpdatesHandler;
27
27
  private publisher: Publisher;
28
28
 
29
- private startTransaction(
29
+ private async startTransaction(
30
30
  txId: string,
31
31
  projectId: string,
32
+ liveUpdatesPublishFn?: () => Promise<void>,
32
33
  stepName?: string
33
- ): void {
34
+ ): Promise<void> {
34
35
  // Check if the transaction already exists
35
36
  if (this.detailsByTx[txId]) {
36
37
  const txnDetails = this.detailsByTx[txId];
@@ -53,7 +54,7 @@ export class TransactionManager {
53
54
  null,
54
55
  2
55
56
  )}`,
56
- this.Errors.PROJECT_ONGOING
57
+ this.Errors.PROJECT_TRANSACTION_ONGOING
57
58
  );
58
59
  }
59
60
 
@@ -67,10 +68,14 @@ export class TransactionManager {
67
68
  this.detailsByTx[txId] = txDetails;
68
69
  this.detailsByProject[projectId] = txDetails;
69
70
  logger.info(`Started transaction:\n${JSON.stringify(txDetails, null, 2)}`);
70
- // send live updates
71
- void this.liveUpdatesHandler.enableTransactionStatus({
72
- txId
73
- });
71
+
72
+ if (liveUpdatesPublishFn) {
73
+ await this.liveUpdatesHandler.enable(
74
+ keyMirrors.toClientMessageType.status_response,
75
+ liveUpdatesPublishFn,
76
+ txId
77
+ );
78
+ }
74
79
  }
75
80
 
76
81
  private updateTransaction(txId: string, stepName?: string): void {
@@ -95,7 +100,7 @@ export class TransactionManager {
95
100
 
96
101
  public Errors = keyMirror({
97
102
  TRANSACTION_ONGOING: null,
98
- PROJECT_ONGOING: null,
103
+ PROJECT_TRANSACTION_ONGOING: null,
99
104
  TRANSACTION_NOT_ONGOING: null
100
105
  });
101
106
 
@@ -104,48 +109,49 @@ export class TransactionManager {
104
109
  projectId: string;
105
110
  txId: string;
106
111
  start: boolean;
112
+ liveUpdatesPublishFn?: () => Promise<void>;
107
113
  stepName?: string;
108
114
  }) {
109
- const { func, projectId, txId, start, stepName } = props;
115
+ const { func, projectId, txId, start, liveUpdatesPublishFn, stepName } =
116
+ props;
110
117
  if (start) {
111
- this.startTransaction(txId, projectId, stepName);
118
+ await this.startTransaction(
119
+ txId,
120
+ projectId,
121
+ liveUpdatesPublishFn,
122
+ stepName
123
+ );
112
124
  } else {
113
125
  this.updateTransaction(txId, stepName);
114
126
  }
115
127
  try {
116
128
  const completed = await func();
117
129
  if (completed) {
118
- this.completeTransaction(txId);
119
- const successStatusResponsePayload: StatusResponsePayload = {
120
- status: keyMirrors.statusResponse.success
121
- };
122
- // Send final status message
123
- const message = buildToClientStatusResponseMessage(
124
- this.publisher.getClientId(),
125
- successStatusResponsePayload,
126
- txId
130
+ this.completeTransaction(
131
+ txId,
132
+ buildToClientStatusResponseMessage(
133
+ this.publisher.getClientId(),
134
+ { status: keyMirrors.statusResponse.success },
135
+ txId
136
+ )
127
137
  );
128
- this.publisher.publishToClient(message);
129
138
  }
130
139
  } catch (e) {
131
- const message: string = e.message;
132
140
  logger.error(
133
- `Failed to execute cmd for ${projectId}:\n${message}\n${e.stack}`
141
+ `Failed to execute cmd for ${projectId}!\n${stringifyError(e)}`
134
142
  );
135
143
 
136
- this.completeTransaction(txId);
137
-
138
- const failureStatusResponsePayload: StatusResponsePayload = {
139
- status: keyMirrors.statusResponse.failure,
140
- message
141
- };
142
- // Send final status message
143
- const failureStatusResponseMessage = buildToClientStatusResponseMessage(
144
- this.publisher.getClientId(),
145
- failureStatusResponsePayload,
146
- txId
144
+ this.completeTransaction(
145
+ txId,
146
+ buildToClientStatusResponseMessage(
147
+ this.publisher.getClientId(),
148
+ {
149
+ status: keyMirrors.statusResponse.failure,
150
+ message: e.message
151
+ },
152
+ txId
153
+ )
147
154
  );
148
- this.publisher.publishToClient(failureStatusResponseMessage);
149
155
  }
150
156
  }
151
157
 
@@ -184,7 +190,10 @@ export class TransactionManager {
184
190
  );
185
191
  }
186
192
 
187
- public completeTransaction(txId: string): void {
193
+ public completeTransaction(
194
+ txId: string,
195
+ messageToPublish?: ToClientStatusResponseMessage
196
+ ): void {
188
197
  const txDetails = this.detailsByTx[txId];
189
198
  if (txDetails === undefined) {
190
199
  throw new CodedError(
@@ -199,8 +208,13 @@ export class TransactionManager {
199
208
  delete this.detailsByTx[txId];
200
209
  delete this.detailsByProject[txDetails.projectId];
201
210
 
202
- void this.liveUpdatesHandler.disableTransactionStatus({
211
+ this.liveUpdatesHandler.disable(
212
+ keyMirrors.toClientMessageType.status_response,
203
213
  txId
204
- });
214
+ );
215
+
216
+ if (messageToPublish) {
217
+ this.publisher.publishToClient(messageToPublish);
218
+ }
205
219
  }
206
220
  }
@@ -4,9 +4,10 @@ import * as osu from 'node-os-utils';
4
4
  import * as si from 'systeminformation';
5
5
  import { exec } from 'child_process';
6
6
  import { promisify } from 'util';
7
- import { JsSpawner } from 'alwaysai/lib/util';
7
+ import { JsSpawner, stringifyError } from 'alwaysai/lib/util';
8
8
  import { DeviceStatsPayload } from '@alwaysai/device-agent-schemas';
9
9
  import { getDeviceAgentVersion } from '../util/check-for-updates';
10
+ import AaiError from '../util/aai-error';
10
11
 
11
12
  const exec_promise = promisify(exec);
12
13
 
@@ -14,63 +15,84 @@ const exec_promise = promisify(exec);
14
15
  export async function getCpuDetails(): Promise<
15
16
  DeviceStatsPayload['cpuDetails']
16
17
  > {
17
- const cpuFree = await osu.cpu.free();
18
- const cpuTemp = await si.cpuTemperature();
19
- const cpuDetails: DeviceStatsPayload['cpuDetails'] = {};
20
- if (cpuFree !== null) cpuDetails.usedPerc = 100 - cpuFree;
21
- if (cpuTemp?.main !== null) cpuDetails.temperature = cpuTemp.main;
22
- return cpuDetails;
18
+ let cpuFree, cpuTemp;
19
+ try {
20
+ [cpuFree, cpuTemp] = await Promise.all([
21
+ osu.cpu.free(),
22
+ si.cpuTemperature()
23
+ ]);
24
+ } catch (error) {
25
+ logger.error('Error fetching CPU details:', error);
26
+ return {};
27
+ }
28
+
29
+ return {
30
+ usedPerc: cpuFree !== null ? 100 - cpuFree : undefined,
31
+ temperature: cpuTemp?.main !== null ? cpuTemp.main : undefined
32
+ };
23
33
  }
24
34
 
25
35
  export async function getDiskDetails(): Promise<
26
36
  DeviceStatsPayload['diskDetails']
27
37
  > {
28
- // Types incorrectly specify diskname as required instead of optional
29
- // @ts-expect-error
30
- const driveInfo = await osu.drive.info();
31
- const diskDetails: DeviceStatsPayload['diskDetails'] = {};
32
- if (driveInfo?.usedGb !== null)
33
- diskDetails.usedGb = parseFloat(driveInfo.usedGb);
34
- if (driveInfo?.freeGb !== null) {
35
- diskDetails.freeGb = parseFloat(driveInfo.freeGb);
38
+ let driveInfo;
39
+ try {
40
+ // Types incorrectly specify diskname as required instead of optional
41
+ // @ts-expect-error
42
+ driveInfo = await osu.drive.info();
43
+ } catch (error) {
44
+ logger.error('Error fetching disk details:', error);
45
+ return {};
36
46
  }
37
- return diskDetails;
47
+
48
+ return {
49
+ usedGb:
50
+ driveInfo?.usedGb !== null ? parseFloat(driveInfo.usedGb) : undefined,
51
+ freeGb:
52
+ driveInfo?.freeGb !== null ? parseFloat(driveInfo.freeGb) : undefined
53
+ };
38
54
  }
39
55
 
40
56
  export async function getMemDetails(): Promise<
41
57
  DeviceStatsPayload['memDetails']
42
58
  > {
43
- const memInfo = await osu.mem.info();
44
- const memDetails: DeviceStatsPayload['memDetails'] = {};
45
- if (memInfo?.usedMemMb !== null) memDetails.usedMb = memInfo.usedMemMb;
46
- if (memInfo?.freeMemMb !== null) memDetails.freeMb = memInfo.freeMemMb;
47
- return memDetails;
59
+ let memInfo;
60
+ try {
61
+ memInfo = await osu.mem.info();
62
+ } catch (error) {
63
+ logger.error('Error fetching memory details:', error);
64
+ return {};
65
+ }
66
+ return {
67
+ usedMb: memInfo?.usedMemMb !== null ? memInfo.usedMemMb : undefined,
68
+ freeMb: memInfo?.freeMemMb !== null ? memInfo.freeMemMb : undefined
69
+ };
48
70
  }
49
71
 
50
- // System information
72
+ // System information casted to a string
51
73
  export async function getOsInfo() {
52
74
  const osInfo = await si.osInfo();
53
75
  return {
54
- platform: osInfo.platform,
55
- distro: osInfo.distro,
56
- release: osInfo.release,
57
- kernel: osInfo.kernel,
58
- architecture: osInfo.arch,
59
- hostname: osInfo.hostname
76
+ platform: String(osInfo.platform),
77
+ distro: String(osInfo.distro),
78
+ release: String(osInfo.release),
79
+ kernel: String(osInfo.kernel),
80
+ architecture: String(osInfo.arch),
81
+ hostname: String(osInfo.hostname)
60
82
  };
61
83
  }
62
84
 
63
85
  export async function getCpuInfo() {
64
86
  const cpuInfo = await si.cpu();
65
87
  return {
66
- manufacturer: cpuInfo.manufacturer,
67
- brand: cpuInfo.brand,
68
- vendor: cpuInfo.vendor,
69
- model: cpuInfo.model,
70
- cores: cpuInfo.cores,
71
- physicalCores: cpuInfo.physicalCores,
72
- efficiencyCores: cpuInfo.efficiencyCores,
73
- processors: cpuInfo.processors
88
+ manufacturer: String(cpuInfo.manufacturer),
89
+ brand: String(cpuInfo.brand),
90
+ vendor: String(cpuInfo.vendor),
91
+ model: String(cpuInfo.model),
92
+ cores: String(cpuInfo.cores),
93
+ physicalCores: String(cpuInfo.physicalCores),
94
+ efficiencyCores: String(cpuInfo.efficiencyCores),
95
+ processors: String(cpuInfo.processors)
74
96
  };
75
97
  }
76
98
 
@@ -78,11 +100,11 @@ export async function getDiskInfo() {
78
100
  const diskInfo = await si.diskLayout();
79
101
  return {
80
102
  drives: diskInfo.map((drive) => ({
81
- device: drive.device,
82
- type: drive.type,
83
- name: drive.name,
84
- vendor: drive.vendor,
85
- size: drive.size
103
+ device: String(drive.device),
104
+ type: String(drive.type),
105
+ name: String(drive.name),
106
+ vendor: String(drive.vendor),
107
+ size: String(drive.size)
86
108
  }))
87
109
  };
88
110
  }
@@ -90,14 +112,14 @@ export async function getDiskInfo() {
90
112
  export async function getDeviceInfo() {
91
113
  const deviceInfo = await si.system();
92
114
  return {
93
- manufacturer: deviceInfo.manufacturer,
94
- model: deviceInfo.model,
95
- version: deviceInfo.version,
115
+ manufacturer: String(deviceInfo.manufacturer),
116
+ model: String(deviceInfo.model),
117
+ version: String(deviceInfo.version),
96
118
  serial:
97
119
  deviceInfo.serial && deviceInfo.serial !== '-'
98
- ? deviceInfo.serial
120
+ ? String(deviceInfo.serial)
99
121
  : undefined,
100
- virtual: deviceInfo.virtual
122
+ virtual: deviceInfo.virtual // this should be a boolean
101
123
  };
102
124
  }
103
125
 
@@ -108,9 +130,9 @@ export async function getNetworkInfo() {
108
130
  ? networkInterfaces.filter((iface: any) => iface.ip4 !== '127.0.0.1')[0]
109
131
  : networkInterfaces;
110
132
  return {
111
- ipv4Address: defaultNetworkInterface.ip4,
112
- ipv6Address: defaultNetworkInterface.ip6,
113
- macAddress: defaultNetworkInterface.mac
133
+ ipv4Address: String(defaultNetworkInterface.ip4),
134
+ ipv6Address: String(defaultNetworkInterface.ip6),
135
+ macAddress: String(defaultNetworkInterface.mac)
114
136
  };
115
137
  }
116
138
 
@@ -123,9 +145,9 @@ export async function getDockerVersion() {
123
145
  args: ['info', '--format', 'json']
124
146
  })
125
147
  );
126
- return result.ClientInfo.Version;
148
+ return String(result.ClientInfo.Version);
127
149
  } catch (e) {
128
- logger.warn(`Cannot get Docker version: ${e}`);
150
+ logger.warn(`Cannot get Docker version!\n${stringifyError(e)}`);
129
151
  return 'Not found';
130
152
  }
131
153
  }
@@ -138,7 +160,7 @@ export async function getDockerComposeVersion() {
138
160
  args: ['compose', 'version']
139
161
  });
140
162
  } catch (e) {
141
- logger.warn(`Cannot get Docker Compose version: ${e}`);
163
+ logger.warn(`Cannot get Docker Compose version!\n${stringifyError(e)}`);
142
164
  return 'Not found';
143
165
  }
144
166
  }
@@ -151,7 +173,7 @@ export async function getNpmVersion() {
151
173
  args: ['--version']
152
174
  });
153
175
  } catch (e) {
154
- logger.warn(`Cannot get npm version: ${e}`);
176
+ logger.warn(`Cannot get npm version!\n${stringifyError(e)}`);
155
177
  return 'Not found';
156
178
  }
157
179
  }
@@ -164,7 +186,7 @@ export async function getNodeVersion() {
164
186
  args: ['-v']
165
187
  });
166
188
  } catch (e) {
167
- logger.warn(`Cannot get Node version: ${e}`);
189
+ logger.warn(`Cannot get Node version!\n${stringifyError(e)}`);
168
190
  return 'Not found';
169
191
  }
170
192
  }
@@ -174,7 +196,6 @@ export async function getPackageVersions() {
174
196
  // eslint-disable-next-line
175
197
  const deviceAgentSchemasJson = require('../../node_modules/@alwaysai/device-agent-schemas/package.json');
176
198
 
177
- // Concurrent asynchronous function call
178
199
  const [dockerVersion, dockerComposeVersion, nodeVersion, npmVersion] =
179
200
  await Promise.all([
180
201
  getDockerVersion(),
@@ -207,27 +228,39 @@ export async function getLastBootTime() {
207
228
 
208
229
  const tokens = latestBootStdout.trim().split(' ');
209
230
 
210
- return new Date(`${tokens[2]} ${tokens[3]} ${tokens[4]}`);
231
+ return String(new Date(`${tokens[2]} ${tokens[3]} ${tokens[4]}`));
211
232
  } catch (e) {
212
- logger.error(`Issue getting last boot time: ${e.message}`);
233
+ logger.error(`Issue getting last boot time!\n${stringifyError(e)}`);
213
234
  return undefined;
214
235
  }
215
236
  }
216
237
 
217
238
  export async function getSystemInformation(): Promise<SystemInformationShadowUpdate> {
218
239
  try {
240
+ const [os, cpu, disk, device, network, versions, lastBootTime] =
241
+ await Promise.all([
242
+ getOsInfo(),
243
+ getCpuInfo(),
244
+ getDiskInfo(),
245
+ getDeviceInfo(),
246
+ getNetworkInfo(),
247
+ getPackageVersions(),
248
+ getLastBootTime()
249
+ ]);
219
250
  const systemInfo: SystemInformationShadowUpdate = {
220
- os: await getOsInfo(),
221
- cpu: await getCpuInfo(),
222
- disk: await getDiskInfo(),
223
- device: await getDeviceInfo(),
224
- network: await getNetworkInfo(),
225
- versions: await getPackageVersions(),
226
- lastBootTime: (await getLastBootTime())?.toString()
251
+ os,
252
+ cpu,
253
+ disk,
254
+ device,
255
+ network,
256
+ versions,
257
+ lastBootTime
227
258
  };
228
259
  return systemInfo;
229
260
  } catch (e) {
230
- logger.error('There was a problem getting system information: ', e);
261
+ logger.error(
262
+ `There was a problem getting system information!\n${stringifyError(e)}`
263
+ );
231
264
  }
232
265
  return {};
233
266
  }
@@ -242,11 +275,10 @@ export async function reboot() {
242
275
  timeout: 5000
243
276
  });
244
277
  logger.info(result.stdout.trim());
245
- } catch (err) {
246
- throw new Error(
247
- `Could not reboot device. You may need to add passwordless access to '/sbin/shutdown'. ${JSON.stringify(
248
- err
249
- )}`
278
+ } catch (e) {
279
+ throw new AaiError(
280
+ "Could not reboot device. You may need to add passwordless access to '/sbin/shutdown'.",
281
+ { cause: e }
250
282
  );
251
283
  }
252
284
  }
@@ -2,7 +2,7 @@ import * as DockerComposeV1 from 'docker-compose';
2
2
  import { v2 as DockerComposeV2 } from 'docker-compose';
3
3
  import { execSync } from 'child_process';
4
4
  import { logger } from '../util/logger';
5
- import { JsSpawner } from 'alwaysai/lib/util';
5
+ import { JsSpawner, stringifyError } from 'alwaysai/lib/util';
6
6
 
7
7
  let dockerCmd = 'docker';
8
8
 
@@ -14,11 +14,12 @@ export function importDockerCompose():
14
14
  logger.debug('Using Docker Compose V2.');
15
15
  return DockerComposeV2;
16
16
  } catch (e) {
17
+ // TODO: Log this error as well
17
18
  try {
18
19
  execSync('docker-compose -v').toString();
19
20
  logger.warn('Using docker-compose V1. Please consider updating to V2.');
20
21
  } catch (e) {
21
- logger.warn(`Could not determine compose: ${e}`);
22
+ logger.warn(`Could not determine compose!\n${stringifyError(e)}`);
22
23
  }
23
24
  }
24
25
  dockerCmd = 'docker-compose';
@@ -1,7 +1,11 @@
1
+ import { getDeviceConfigPath } from 'alwaysai/lib/infrastructure';
2
+ import { join } from 'path';
1
3
  import * as tempy from 'tempy';
2
- import { AgentConfigFile } from './agent-config';
4
+ import { AGENT_CONFIG_FILE_NAME, AgentConfigFile } from './agent-config';
3
5
 
4
- const configFile = AgentConfigFile(tempy.directory());
6
+ const configFile = AgentConfigFile(
7
+ join(tempy.directory(), getDeviceConfigPath(), AGENT_CONFIG_FILE_NAME)
8
+ );
5
9
 
6
10
  describe('Test Agent Config', () => {
7
11
  beforeEach(() => {
@@ -3,7 +3,8 @@ import {
3
3
  ConfigFileSchemaReturnType
4
4
  } from '@alwaysai/config-nodejs';
5
5
  import Ajv, { JSONSchemaType } from 'ajv';
6
- import { homedir } from 'os';
6
+ import { getDeviceConfigPath } from 'alwaysai/lib/infrastructure';
7
+ import { LOCAL_AAI_CFG_DIR } from 'alwaysai/lib/paths';
7
8
  import { join } from 'path';
8
9
 
9
10
  export interface AppBackupConfig {
@@ -54,16 +55,16 @@ const schema: JSONSchemaType<AgentConfig> = {
54
55
  const ajv = new Ajv();
55
56
  const validateFunction = ajv.compile(schema);
56
57
 
57
- const ALWAYSAI_CONFIG_DIR = join(homedir(), '.config', 'alwaysai');
58
+ export const AGENT_CONFIG_FILE_NAME = 'alwaysai.agent.json';
58
59
 
59
- const AGENT_CONFIG_FILE_NAME = 'alwaysai.agent.json';
60
-
61
- export function AgentConfigFile(dir = ALWAYSAI_CONFIG_DIR) {
62
- const path = join(dir, AGENT_CONFIG_FILE_NAME);
60
+ export function AgentConfigFile(filePath?: string) {
61
+ const path =
62
+ filePath ??
63
+ join(LOCAL_AAI_CFG_DIR, getDeviceConfigPath(), AGENT_CONFIG_FILE_NAME);
63
64
  const initialValue: AgentConfig = {
64
65
  applications: []
65
66
  };
66
- const configFile = ConfigFileSchema({
67
+ const configFile = ConfigFileSchema<AgentConfig>({
67
68
  path,
68
69
  validateFunction,
69
70
  initialValue
@@ -0,0 +1,154 @@
1
+ import { DeviceConfig, DeviceConfigFile } from 'alwaysai/lib/core/device';
2
+ import {
3
+ DEVICE_CONFIG_FILE_NAME,
4
+ DeviceCertificates,
5
+ getDeviceConfigPath
6
+ } from 'alwaysai/lib/infrastructure';
7
+ import { JsSpawner } from 'alwaysai/lib/util';
8
+ import { join } from 'path';
9
+ import { rimraf } from 'rimraf';
10
+ import * as tempy from 'tempy';
11
+ import {
12
+ checkAllFilesArePresent,
13
+ checkCertificatesPresent,
14
+ checkDeviceConfigPresent
15
+ } from './config-check-utility';
16
+ import { LegacyFiles } from './legacy-migration/legacy-files';
17
+
18
+ class TestDeviceCertificates extends DeviceCertificates {
19
+ constructor(baseDir) {
20
+ super(baseDir);
21
+ }
22
+ }
23
+
24
+ describe('test requiredConfigFilesPresentAndValid', () => {
25
+ let tmpDir;
26
+
27
+ beforeEach(async () => {
28
+ tmpDir = tempy.directory();
29
+ });
30
+
31
+ afterEach(async () => {
32
+ await rimraf(tmpDir);
33
+ jest.clearAllMocks();
34
+ });
35
+
36
+ it('test checkAllFilesArePresent returns false in empty directory', async () => {
37
+ const legacyFiles = LegacyFiles(tmpDir);
38
+ const filesPresent = await checkAllFilesArePresent(
39
+ Object.values(legacyFiles.getLegacyFileMigrationPaths())
40
+ );
41
+ expect(filesPresent).toBe(false);
42
+ });
43
+
44
+ it('test device config present and valid, checkDeviceConfigPresent returns true', async () => {
45
+ // write device credentials data
46
+ const deviceCredentialsData: DeviceConfig = {
47
+ systemId: 'production',
48
+ deviceUuid: '123456-78-901-2345',
49
+ refreshToken: 'refresh',
50
+ accessToken: 'access',
51
+ idToken: 'id'
52
+ };
53
+ const spawner = JsSpawner({ path: tmpDir });
54
+ await spawner.mkdirp(join(tmpDir, getDeviceConfigPath()));
55
+ const filePath = join(
56
+ tmpDir,
57
+ getDeviceConfigPath(),
58
+ DEVICE_CONFIG_FILE_NAME
59
+ );
60
+ await spawner.writeFile(filePath, JSON.stringify(deviceCredentialsData));
61
+ const newConfigsPresent = await checkDeviceConfigPresent(tmpDir);
62
+ expect(newConfigsPresent).toBe(true);
63
+ });
64
+
65
+ it.each([
66
+ {
67
+ // invalid systemId
68
+ systemId: 'blah',
69
+ deviceUuid: '123456-78-901-2345',
70
+ refreshToken: 'refresh',
71
+ accessToken: 'access',
72
+ idToken: 'id'
73
+ },
74
+ {
75
+ // missing refreshToken
76
+ systemId: 'production',
77
+ deviceUuid: '123456-78-901-2345',
78
+ accessToken: 'access',
79
+ idToken: 'id'
80
+ },
81
+ {
82
+ // missing accessToken
83
+ systemId: 'development',
84
+ deviceUuid: '123456-78-901-2345',
85
+ refreshToken: 'refresh',
86
+ idToken: 'id'
87
+ },
88
+ {
89
+ // missing idToken
90
+ systemId: 'development',
91
+ deviceUuid: '123456-78-901-2345',
92
+ refreshToken: 'refresh'
93
+ }
94
+ ])(
95
+ 'test device config has validation errors, checkDeviceConfigPresent returns false',
96
+ async (deviceCredentialsData) => {
97
+ const spawner = JsSpawner({ path: tmpDir });
98
+ await spawner.mkdirp(join(tmpDir, getDeviceConfigPath()));
99
+ const filePath = join(
100
+ tmpDir,
101
+ getDeviceConfigPath(),
102
+ DEVICE_CONFIG_FILE_NAME
103
+ );
104
+ await spawner.writeFile(filePath, JSON.stringify(deviceCredentialsData));
105
+ const deviceConfig = DeviceConfigFile(
106
+ join(tmpDir, getDeviceConfigPath(), DEVICE_CONFIG_FILE_NAME)
107
+ );
108
+ expect(deviceConfig.exists()).toBe(true);
109
+ const newConfigsPresent = await checkDeviceConfigPresent(tmpDir);
110
+ expect(newConfigsPresent).toBe(false);
111
+ }
112
+ );
113
+
114
+ it('test device config is missing, checkDeviceConfigPresent returns false', async () => {
115
+ const deviceConfig = DeviceConfigFile(
116
+ join(tmpDir, getDeviceConfigPath(), DEVICE_CONFIG_FILE_NAME)
117
+ );
118
+ expect(deviceConfig.exists()).toBe(false);
119
+ const newConfigsPresent = await checkDeviceConfigPresent(tmpDir);
120
+ expect(newConfigsPresent).toBe(false);
121
+ });
122
+
123
+ it('test empty certificates folder, checkCertificatesPresent returns false', async () => {
124
+ const certsPresent = await checkCertificatesPresent(tmpDir);
125
+ expect(certsPresent).toBe(false);
126
+ });
127
+
128
+ test('test full certificates folder, checkCertificatesPresent returns true', async () => {
129
+ // write certificates
130
+ const testCerts = new TestDeviceCertificates(tmpDir);
131
+ const spawner = JsSpawner({ path: tmpDir });
132
+ const certFilePath = testCerts.getCertificateDirectoryName();
133
+ await spawner.mkdirp(join(tmpDir, certFilePath));
134
+ await spawner.writeFile(
135
+ join(tmpDir, certFilePath, testCerts.getCertificateFileName()),
136
+ 'certificate data'
137
+ );
138
+ console.log(
139
+ 'join(tmpDir, certFilePath, testCerts.getCertificateFileName())',
140
+ join(tmpDir, certFilePath, testCerts.getCertificateFileName())
141
+ );
142
+ await spawner.writeFile(
143
+ join(tmpDir, certFilePath, testCerts.getPrivateKeyFileName()),
144
+ 'private key data'
145
+ );
146
+ await spawner.writeFile(
147
+ join(tmpDir, certFilePath, testCerts.getRootCertificateFileName()),
148
+ 'root cert data'
149
+ );
150
+
151
+ const certsPresent = await checkCertificatesPresent(tmpDir);
152
+ expect(certsPresent).toBe(true);
153
+ });
154
+ });