@alwaysai/device-agent 0.0.13 → 0.0.14

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 (119) hide show
  1. package/lib/application-control/backup.js +3 -3
  2. package/lib/application-control/backup.js.map +1 -1
  3. package/lib/application-control/index.d.ts +4 -4
  4. package/lib/application-control/index.d.ts.map +1 -1
  5. package/lib/application-control/index.js +1 -4
  6. package/lib/application-control/index.js.map +1 -1
  7. package/lib/application-control/install.d.ts +1 -1
  8. package/lib/application-control/install.d.ts.map +1 -1
  9. package/lib/application-control/install.js +41 -54
  10. package/lib/application-control/install.js.map +1 -1
  11. package/lib/application-control/models.d.ts +0 -4
  12. package/lib/application-control/models.d.ts.map +1 -1
  13. package/lib/application-control/models.js +13 -22
  14. package/lib/application-control/models.js.map +1 -1
  15. package/lib/application-control/status.d.ts +0 -6
  16. package/lib/application-control/status.d.ts.map +1 -1
  17. package/lib/application-control/status.js +3 -19
  18. package/lib/application-control/status.js.map +1 -1
  19. package/lib/application-control/utils.d.ts +3 -0
  20. package/lib/application-control/utils.d.ts.map +1 -1
  21. package/lib/application-control/utils.js +50 -21
  22. package/lib/application-control/utils.js.map +1 -1
  23. package/lib/cloud-connection/cmd-status.d.ts +16 -0
  24. package/lib/cloud-connection/cmd-status.d.ts.map +1 -0
  25. package/lib/cloud-connection/cmd-status.js +49 -0
  26. package/lib/cloud-connection/cmd-status.js.map +1 -0
  27. package/lib/cloud-connection/device-agent-cloud-connection.d.ts +10 -1
  28. package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
  29. package/lib/cloud-connection/device-agent-cloud-connection.js +73 -33
  30. package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
  31. package/lib/cloud-connection/passthrough-handler.d.ts +11 -0
  32. package/lib/cloud-connection/passthrough-handler.d.ts.map +1 -0
  33. package/lib/cloud-connection/passthrough-handler.js +59 -0
  34. package/lib/cloud-connection/passthrough-handler.js.map +1 -0
  35. package/lib/cloud-connection/publisher.d.ts +1 -0
  36. package/lib/cloud-connection/publisher.d.ts.map +1 -1
  37. package/lib/cloud-connection/publisher.js +14 -0
  38. package/lib/cloud-connection/publisher.js.map +1 -1
  39. package/lib/cloud-connection/shadow-handler.d.ts +2 -3
  40. package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
  41. package/lib/cloud-connection/shadow-handler.js +18 -4
  42. package/lib/cloud-connection/shadow-handler.js.map +1 -1
  43. package/lib/cloud-connection/shadow-handler.test.d.ts +2 -0
  44. package/lib/cloud-connection/shadow-handler.test.d.ts.map +1 -0
  45. package/lib/cloud-connection/shadow-handler.test.js +321 -0
  46. package/lib/cloud-connection/shadow-handler.test.js.map +1 -0
  47. package/lib/environment.d.ts +1 -0
  48. package/lib/environment.d.ts.map +1 -1
  49. package/lib/environment.js +2 -1
  50. package/lib/environment.js.map +1 -1
  51. package/lib/infrastructure/agent-config.d.ts +15 -48
  52. package/lib/infrastructure/agent-config.d.ts.map +1 -1
  53. package/lib/infrastructure/agent-config.js.map +1 -1
  54. package/lib/infrastructure/agent-config.test.js +0 -6
  55. package/lib/infrastructure/agent-config.test.js.map +1 -1
  56. package/lib/infrastructure/system-id.js +2 -2
  57. package/lib/infrastructure/system-id.js.map +1 -1
  58. package/lib/infrastructure/tokens-and-device-cfg.d.ts.map +1 -1
  59. package/lib/infrastructure/tokens-and-device-cfg.js +5 -9
  60. package/lib/infrastructure/tokens-and-device-cfg.js.map +1 -1
  61. package/lib/local-connection/rabbitmq-connection.d.ts +4 -0
  62. package/lib/local-connection/rabbitmq-connection.d.ts.map +1 -0
  63. package/lib/local-connection/rabbitmq-connection.js +58 -0
  64. package/lib/local-connection/rabbitmq-connection.js.map +1 -0
  65. package/lib/subcommands/app/app.d.ts +2 -1
  66. package/lib/subcommands/app/app.d.ts.map +1 -1
  67. package/lib/subcommands/app/app.js +56 -23
  68. package/lib/subcommands/app/app.js.map +1 -1
  69. package/lib/subcommands/device/clean.js +4 -4
  70. package/lib/subcommands/device/clean.js.map +1 -1
  71. package/lib/subcommands/device/device.d.ts +1 -1
  72. package/lib/subcommands/device/device.d.ts.map +1 -1
  73. package/lib/subcommands/device/device.js +7 -9
  74. package/lib/subcommands/device/device.js.map +1 -1
  75. package/lib/subcommands/index.d.ts +0 -1
  76. package/lib/subcommands/index.d.ts.map +1 -1
  77. package/lib/subcommands/login.d.ts +0 -1
  78. package/lib/subcommands/login.d.ts.map +1 -1
  79. package/lib/subcommands/login.js +1 -9
  80. package/lib/subcommands/login.js.map +1 -1
  81. package/lib/util/fetch-with-timeout.d.ts +4 -0
  82. package/lib/util/fetch-with-timeout.d.ts.map +1 -0
  83. package/lib/util/fetch-with-timeout.js +15 -0
  84. package/lib/util/fetch-with-timeout.js.map +1 -0
  85. package/lib/util/require-logged-in-and-paid-plan.d.ts +2 -0
  86. package/lib/util/require-logged-in-and-paid-plan.d.ts.map +1 -0
  87. package/lib/util/require-logged-in-and-paid-plan.js +18 -0
  88. package/lib/util/require-logged-in-and-paid-plan.js.map +1 -0
  89. package/lib/util/timer.d.ts +2 -0
  90. package/lib/util/timer.d.ts.map +1 -0
  91. package/lib/util/timer.js +6 -0
  92. package/lib/util/timer.js.map +1 -0
  93. package/package.json +20 -32
  94. package/readme.md +100 -89
  95. package/src/application-control/backup.ts +3 -3
  96. package/src/application-control/index.ts +0 -6
  97. package/src/application-control/install.ts +53 -73
  98. package/src/application-control/models.ts +7 -19
  99. package/src/application-control/status.ts +3 -19
  100. package/src/application-control/utils.ts +61 -22
  101. package/src/cloud-connection/cmd-status.ts +52 -0
  102. package/src/cloud-connection/device-agent-cloud-connection.ts +94 -47
  103. package/src/cloud-connection/passthrough-handler.ts +67 -0
  104. package/src/cloud-connection/publisher.ts +21 -0
  105. package/src/cloud-connection/shadow-handler.test.ts +361 -0
  106. package/src/cloud-connection/shadow-handler.ts +28 -7
  107. package/src/environment.ts +3 -0
  108. package/src/infrastructure/agent-config.test.ts +0 -7
  109. package/src/infrastructure/agent-config.ts +24 -2
  110. package/src/infrastructure/system-id.ts +1 -1
  111. package/src/infrastructure/tokens-and-device-cfg.ts +8 -13
  112. package/src/local-connection/rabbitmq-connection.ts +53 -0
  113. package/src/subcommands/app/app.ts +61 -27
  114. package/src/subcommands/device/clean.ts +4 -4
  115. package/src/subcommands/device/device.ts +8 -11
  116. package/src/subcommands/login.ts +1 -9
  117. package/src/util/fetch-with-timeout.ts +18 -0
  118. package/src/util/require-logged-in-and-paid-plan.ts +16 -0
  119. package/src/util/timer.ts +1 -0
@@ -0,0 +1,361 @@
1
+ import { AppConfig } from '@alwaysai/app-configuration-schemas';
2
+ import { readAppCfgFile } from '../application-control';
3
+ import { Publisher } from './publisher';
4
+ import { ShadowHandler } from './shadow-handler';
5
+
6
+ jest.mock('../application-control');
7
+ jest.mock('./publisher');
8
+ const mockClient = jest.fn();
9
+ const clientId = 'test-client';
10
+ const projectId1 = 'test-project';
11
+ const projectId2 = 'test-project-2';
12
+
13
+ describe('Test Shadow Handler', () => {
14
+ let publisher: Publisher;
15
+ let shadowHandler: ShadowHandler;
16
+
17
+ beforeEach(() => {
18
+ publisher = new Publisher(mockClient, clientId);
19
+ shadowHandler = new ShadowHandler(clientId, publisher);
20
+ });
21
+
22
+ test.skip('reject buffer payload', async () => {
23
+ //FIXME: Invalid input is silently ignored, need input validation
24
+ expect(() => {
25
+ shadowHandler.handleShadowTopic({
26
+ topic: shadowHandler.shadowTopics.projects.updateDelta,
27
+ payload: Buffer.from('test-payload')
28
+ });
29
+ }).toThrow(Error);
30
+ });
31
+
32
+ test('ignore message from self', async () => {
33
+ const ogAppCfg1: AppConfig = {
34
+ scripts: {
35
+ start: 'python app.py'
36
+ },
37
+ models: {
38
+ 'alwaysai/mobilenet_ssd': 2
39
+ }
40
+ };
41
+ jest.mocked(readAppCfgFile).mockResolvedValue(ogAppCfg1);
42
+
43
+ const appCfg1: AppConfig = {
44
+ scripts: {
45
+ start: 'python app.py'
46
+ },
47
+ models: {
48
+ 'alwaysai/yolo_v3': 4
49
+ }
50
+ };
51
+ const payload = {
52
+ [projectId1]: {
53
+ appConfig: JSON.stringify(appCfg1)
54
+ },
55
+ clientToken: clientId
56
+ };
57
+
58
+ const appCfgUpdates = await shadowHandler.handleShadowTopic({
59
+ topic: shadowHandler.shadowTopics.projects.updateDelta,
60
+ payload
61
+ });
62
+ expect(appCfgUpdates.length).toBe(0);
63
+ });
64
+
65
+ test('handle shadow get response with updated models', async () => {
66
+ const ogAppCfg1: AppConfig = {
67
+ scripts: {
68
+ start: 'python app.py'
69
+ },
70
+ models: {
71
+ 'alwaysai/mobilenet_ssd': 3
72
+ }
73
+ };
74
+
75
+ jest.mocked(readAppCfgFile).mockResolvedValue(ogAppCfg1);
76
+ const appCfg1: AppConfig = {
77
+ scripts: {
78
+ start: 'python app.py'
79
+ },
80
+ models: {
81
+ 'alwaysai/yolo_v3': 4
82
+ }
83
+ };
84
+
85
+ const payload = {
86
+ delta: {
87
+ [projectId1]: {
88
+ appConfig: JSON.stringify(appCfg1)
89
+ }
90
+ }
91
+ };
92
+
93
+ const appCfgUpdates = await shadowHandler.handleShadowTopic({
94
+ topic: shadowHandler.shadowTopics.projects.getAccepted,
95
+ payload
96
+ });
97
+ expect(appCfgUpdates.length).toBe(1);
98
+ expect(appCfgUpdates[0]).toEqual({
99
+ projectId: projectId1,
100
+ newAppCfg: appCfg1,
101
+ updatedModels: {
102
+ 'alwaysai/yolo_v3': 4
103
+ }
104
+ });
105
+ });
106
+
107
+ test('handle shadow delta without app config', async () => {
108
+ const ogAppCfg1: AppConfig = {
109
+ scripts: {
110
+ start: 'python app.py'
111
+ },
112
+ models: {
113
+ 'alwaysai/mobilenet_ssd': 2
114
+ }
115
+ };
116
+ jest.mocked(readAppCfgFile).mockResolvedValue(ogAppCfg1);
117
+
118
+ const payload = {
119
+ [projectId1]: {}
120
+ };
121
+
122
+ const appCfgUpdates = await shadowHandler.handleShadowTopic({
123
+ topic: shadowHandler.shadowTopics.projects.updateDelta,
124
+ payload
125
+ });
126
+ expect(appCfgUpdates.length).toBe(0);
127
+ });
128
+
129
+ test('handle shadow delta with updated models', async () => {
130
+ const ogAppCfg1: AppConfig = {
131
+ scripts: {
132
+ start: 'python app.py'
133
+ },
134
+ models: {
135
+ 'alwaysai/mobilenet_ssd': 2
136
+ }
137
+ };
138
+
139
+ jest.mocked(readAppCfgFile).mockResolvedValue(ogAppCfg1);
140
+ const appCfg1: AppConfig = {
141
+ scripts: {
142
+ start: 'python app.py'
143
+ },
144
+ models: {
145
+ 'alwaysai/mobilenet_ssd': 3,
146
+ 'alwaysai/yolo_v4': 5
147
+ }
148
+ };
149
+
150
+ const payload = {
151
+ [projectId1]: {
152
+ appConfig: JSON.stringify(appCfg1)
153
+ }
154
+ };
155
+
156
+ const appCfgUpdates = await shadowHandler.handleShadowTopic({
157
+ topic: shadowHandler.shadowTopics.projects.updateDelta,
158
+ payload
159
+ });
160
+ expect(appCfgUpdates.length).toBe(1);
161
+ expect(appCfgUpdates[0]).toEqual({
162
+ projectId: projectId1,
163
+ newAppCfg: appCfg1,
164
+ updatedModels: {
165
+ 'alwaysai/mobilenet_ssd': 3,
166
+ 'alwaysai/yolo_v4': 5
167
+ }
168
+ });
169
+ });
170
+
171
+ test('handle shadow delta with updated models for two apps', async () => {
172
+ const ogAppCfg1: AppConfig = {
173
+ scripts: {
174
+ start: 'python app.py'
175
+ },
176
+ models: {
177
+ 'alwaysai/mobilenet_ssd': 2
178
+ }
179
+ };
180
+ jest.mocked(readAppCfgFile).mockResolvedValueOnce(ogAppCfg1);
181
+ const ogAppCfg2: AppConfig = {
182
+ scripts: {
183
+ start: 'python app.py'
184
+ },
185
+ models: {
186
+ 'alwaysai/yolo_v4': 5
187
+ }
188
+ };
189
+ jest.mocked(readAppCfgFile).mockResolvedValueOnce(ogAppCfg2);
190
+
191
+ const appCfg1: AppConfig = {
192
+ scripts: {
193
+ start: 'python app.py'
194
+ },
195
+ models: {
196
+ 'alwaysai/mobilenet_ssd': 3,
197
+ 'alwaysai/yolo_v4': 5
198
+ }
199
+ };
200
+ const appCfg2: AppConfig = {
201
+ scripts: {
202
+ start: 'python app.py'
203
+ },
204
+ models: {
205
+ 'alwaysai/yolo_v4': 5,
206
+ 'alwaysai/human_pose': 7
207
+ }
208
+ };
209
+ const payload = {
210
+ [projectId1]: {
211
+ appConfig: JSON.stringify(appCfg1)
212
+ },
213
+ [projectId2]: {
214
+ appConfig: JSON.stringify(appCfg2)
215
+ }
216
+ };
217
+
218
+ const appCfgUpdates = await shadowHandler.handleShadowTopic({
219
+ topic: shadowHandler.shadowTopics.projects.updateDelta,
220
+ payload
221
+ });
222
+ expect(appCfgUpdates.length).toBe(2);
223
+ expect(appCfgUpdates[0]).toEqual({
224
+ projectId: projectId1,
225
+ newAppCfg: appCfg1,
226
+ updatedModels: {
227
+ 'alwaysai/mobilenet_ssd': 3,
228
+ 'alwaysai/yolo_v4': 5
229
+ }
230
+ });
231
+ expect(appCfgUpdates[1]).toEqual({
232
+ projectId: projectId2,
233
+ newAppCfg: appCfg2,
234
+ updatedModels: {
235
+ 'alwaysai/human_pose': 7
236
+ }
237
+ });
238
+ });
239
+
240
+ test('handle shadow delta with updated analytics', async () => {
241
+ const ogAppCfg1: AppConfig = {
242
+ scripts: {
243
+ start: 'python app.py'
244
+ },
245
+ models: {}
246
+ };
247
+ jest.mocked(readAppCfgFile).mockResolvedValue(ogAppCfg1);
248
+
249
+ const appCfg1: AppConfig = {
250
+ scripts: {
251
+ start: 'python app.py'
252
+ },
253
+ models: {},
254
+ analytics: {
255
+ enable_cloud_publish: true
256
+ }
257
+ };
258
+
259
+ const payload = {
260
+ [projectId1]: {
261
+ appConfig: JSON.stringify(appCfg1)
262
+ }
263
+ };
264
+
265
+ const appCfgUpdates = await shadowHandler.handleShadowTopic({
266
+ topic: shadowHandler.shadowTopics.projects.updateDelta,
267
+ payload
268
+ });
269
+ expect(appCfgUpdates.length).toBe(1);
270
+ expect(appCfgUpdates[0]).toEqual({
271
+ projectId: projectId1,
272
+ newAppCfg: appCfg1
273
+ });
274
+ });
275
+
276
+ test('handle shadow delta with invalid app config', async () => {
277
+ const ogAppCfg1: AppConfig = {
278
+ scripts: {
279
+ start: 'python app.py'
280
+ },
281
+ models: {
282
+ 'alwaysai/mobilenet_ssd': 2
283
+ }
284
+ };
285
+
286
+ jest.mocked(readAppCfgFile).mockResolvedValue(ogAppCfg1);
287
+ const appCfg1 = {
288
+ scripts: {
289
+ start: 'python app.py'
290
+ },
291
+ models: {
292
+ 'alwaysai/mobilenet_ssd': '3',
293
+ 'alwaysai/yolo_v4': '5'
294
+ }
295
+ };
296
+
297
+ const payload = {
298
+ [projectId1]: {
299
+ appConfig: JSON.stringify(appCfg1)
300
+ }
301
+ };
302
+
303
+ const appCfgUpdates = await shadowHandler.handleShadowTopic({
304
+ topic: shadowHandler.shadowTopics.projects.updateDelta,
305
+ payload
306
+ });
307
+ expect(appCfgUpdates.length).toBe(0);
308
+ });
309
+
310
+ test.skip('publish app state', async () => {
311
+ // FIXME: For some reason publisher is not being called...
312
+ const testAppCfg: AppConfig = {
313
+ scripts: {
314
+ start: ''
315
+ },
316
+ models: {}
317
+ };
318
+ jest.mocked(readAppCfgFile).mockResolvedValue(testAppCfg);
319
+
320
+ shadowHandler.publishAppState(projectId1);
321
+ expect(jest.mocked(readAppCfgFile)).toBeCalledWith({ projectId1 });
322
+ const packet = {
323
+ state: {
324
+ reported: {
325
+ [projectId1]: { appConfig: JSON.stringify(testAppCfg) }
326
+ }
327
+ },
328
+ clientToken: clientId
329
+ };
330
+ expect(jest.mocked(publisher.publish)).toBeCalledWith(
331
+ shadowHandler.shadowTopics.projects.update,
332
+ JSON.stringify(packet)
333
+ );
334
+ });
335
+
336
+ test('get shadow updates', async () => {
337
+ shadowHandler.getShadowUpdates();
338
+ expect(jest.mocked(publisher.publish)).toBeCalledWith(
339
+ shadowHandler.shadowTopics.projects.get,
340
+ JSON.stringify({
341
+ clientToken: clientId
342
+ })
343
+ );
344
+ });
345
+
346
+ test('delete project shadow', async () => {
347
+ shadowHandler.deleteProjectShadow(projectId1);
348
+ const packet = {
349
+ state: {
350
+ reported: {
351
+ [projectId1]: null
352
+ }
353
+ },
354
+ clientToken: clientId
355
+ };
356
+ expect(jest.mocked(publisher.publish)).toBeCalledWith(
357
+ shadowHandler.shadowTopics.projects.update,
358
+ JSON.stringify(packet)
359
+ );
360
+ });
361
+ });
@@ -7,7 +7,7 @@ import { logger } from '../util/logger';
7
7
  import { Publisher } from './publisher';
8
8
  import { AppConfigModels, getAppCfgModelsDiff } from './shadow';
9
9
 
10
- interface ShadowTopics {
10
+ export interface ShadowTopics {
11
11
  projects: {
12
12
  update: string;
13
13
  get: string;
@@ -59,8 +59,9 @@ export class ShadowHandler {
59
59
  if (projectShadow.appConfig) {
60
60
  const newAppCfg = JSON.parse(projectShadow.appConfig);
61
61
  if (!validateAppConfig(newAppCfg)) {
62
+ // FIXME: Raise an exception to be handled at higher layer
62
63
  logger.error(
63
- `Received invalid app config for ${projectId}!/n${JSON.stringify(
64
+ `Received invalid app config for ${projectId}!\n${JSON.stringify(
64
65
  validateAppConfig.errors,
65
66
  null,
66
67
  2
@@ -78,6 +79,10 @@ export class ShadowHandler {
78
79
  } else {
79
80
  appConfigUpdates.push({ projectId, newAppCfg });
80
81
  }
82
+ } else {
83
+ logger.warn(
84
+ `Ignoring shadow update for ${projectId} due to no app config`
85
+ );
81
86
  }
82
87
  }
83
88
  return appConfigUpdates;
@@ -96,6 +101,16 @@ export class ShadowHandler {
96
101
  const shadowName = topic.split('/')[5];
97
102
  switch (topic) {
98
103
  case this.shadowTopics.projects.updateDelta:
104
+ if (payload.clientToken === this.clientId) {
105
+ logger.debug(
106
+ `Ignoring message sent from self: ${JSON.stringify(
107
+ { topic, payload },
108
+ null,
109
+ 2
110
+ )}`
111
+ );
112
+ break;
113
+ }
99
114
  return await this.handleNamedShadowUpdate({ delta: payload });
100
115
  case this.shadowTopics.projects.getAccepted:
101
116
  if (payload['delta']) {
@@ -143,12 +158,18 @@ export class ShadowHandler {
143
158
  );
144
159
  }
145
160
 
146
- public deleteProjectShadow() {
161
+ public deleteProjectShadow(projectId: string) {
162
+ const packet = {
163
+ state: {
164
+ reported: {
165
+ [projectId]: null
166
+ }
167
+ },
168
+ clientToken: this.clientId
169
+ };
147
170
  this.publisher.publish(
148
- `${this.shadowTopics.projects.delete}`,
149
- JSON.stringify({
150
- clientToken: this.clientId
151
- })
171
+ this.shadowTopics.projects.update,
172
+ JSON.stringify(packet)
152
173
  );
153
174
  }
154
175
  }
@@ -10,6 +10,9 @@ export const ALWAYSAI_DEVICE_AGENT_MODE =
10
10
  process.env.ALWAYSAI_DEVICE_AGENT_MODE;
11
11
  export const ALWAYSAI_LOG_LEVEL = process.env.AAI_LOG_LEVEL;
12
12
  export const ALWAYSAI_LOG_TO_CONSOLE = process.env.ALWAYSAI_LOG_TO_CONSOLE;
13
+ export const ALWAYSAI_ANALYTICS_PASSTHROUGH = parseBoolean(
14
+ process.env.ALWAYSAI_ANALYTICS_PASSTHROUGH
15
+ );
13
16
 
14
17
  function parseOsPlatform(str: string | undefined): NodeJS.Platform {
15
18
  switch (str) {
@@ -1,13 +1,6 @@
1
1
  import * as tempy from 'tempy';
2
2
  import { AgentConfigFile } from './agent-config';
3
3
 
4
- const mockAgentModeGetter = jest.fn().mockReturnValue(undefined);
5
- jest.mock('../environment', () => ({
6
- get ALWAYSAI_DEVICE_AGENT_MODE() {
7
- return mockAgentModeGetter();
8
- }
9
- }));
10
-
11
4
  const configFile = AgentConfigFile(tempy.directory());
12
5
 
13
6
  describe('Test Agent Config', () => {
@@ -1,4 +1,7 @@
1
- import { ConfigFileSchema } from '@alwaysai/config-nodejs';
1
+ import {
2
+ ConfigFileSchema,
3
+ ConfigFileSchemaReturnType
4
+ } from '@alwaysai/config-nodejs';
2
5
  import Ajv, { JSONSchemaType } from 'ajv';
3
6
  import { homedir } from 'os';
4
7
  import { join } from 'path';
@@ -55,7 +58,26 @@ const ALWAYSAI_CONFIG_DIR = join(homedir(), '.config', 'alwaysai');
55
58
 
56
59
  const AGENT_CONFIG_FILE_NAME = 'alwaysai.agent.json';
57
60
 
58
- export function AgentConfigFile(dir = ALWAYSAI_CONFIG_DIR) {
61
+ export interface AgentJsonFileReturnType
62
+ extends ConfigFileSchemaReturnType<AgentConfig> {
63
+ name: string;
64
+ getApps: () => Promise<InstalledAppConfig[]>;
65
+ getReadyApps;
66
+ getApp;
67
+ isAppPresent;
68
+ isAppReady;
69
+ removeApp;
70
+ setAppInstalling;
71
+ setAppInstalled;
72
+ setAppUninstalled;
73
+ getAppVersion;
74
+ setAppBackup;
75
+ getAppBackup;
76
+ }
77
+
78
+ export function AgentConfigFile(
79
+ dir = ALWAYSAI_CONFIG_DIR
80
+ ): AgentJsonFileReturnType {
59
81
  const path = join(dir, AGENT_CONFIG_FILE_NAME);
60
82
  const initialValue: AgentConfig = {
61
83
  applications: []
@@ -1,6 +1,6 @@
1
+ import { SystemId } from 'alwaysai/lib/constants';
1
2
  import { DeviceConfigFile } from 'alwaysai/lib/core/device';
2
3
  import { ALWAYSAI_SYSTEM_ID } from 'alwaysai/lib/environment';
3
- import { SystemId } from 'alwaysai/lib/infrastructure';
4
4
 
5
5
  export function getSystemId() {
6
6
  if (ALWAYSAI_SYSTEM_ID) {
@@ -1,23 +1,18 @@
1
- import {
2
- DeviceTokens,
3
- writeOrValidateDeviceCfgFile
4
- } from 'alwaysai/lib/components/device';
5
- import { checkUserIsLoggedInComponent } from 'alwaysai/lib/components/user';
6
1
  import { LOCAL_AAI_CFG_DIR } from 'alwaysai/lib/constants';
7
- import { checkPaidPlan } from 'alwaysai/lib/core/project';
8
- import { JsSpawner, writeTokens } from 'alwaysai/lib/util';
9
2
  import { logger } from '../util/logger';
10
3
  import { microServiceHttpClient } from '../util/http-client';
4
+ import { requireLoggedInAndPaidPlan } from '../util/require-logged-in-and-paid-plan';
5
+ import {
6
+ DeviceTokens,
7
+ writeOrValidateDeviceCfgFile,
8
+ writeTokens
9
+ } from 'alwaysai/lib/core/device';
10
+ import { JsSpawner } from 'alwaysai/lib/util';
11
11
 
12
12
  // NOTE: This closely follows the flow of deviceCheckAndUpdateComponent in the CLI
13
13
  export async function writeTokenAndDeviceCfg(props: { deviceUuid: string }) {
14
14
  const { deviceUuid } = props;
15
- await checkUserIsLoggedInComponent({ yes: true });
16
- if (!(await checkPaidPlan())) {
17
- throw new Error(
18
- `This action only supported for Enterprise alwaysAI accounts!`
19
- );
20
- }
15
+ await requireLoggedInAndPaidPlan();
21
16
  const { accessToken, refreshToken, idToken } = await microServiceHttpClient(
22
17
  'token-service',
23
18
  'create-device-tokens',
@@ -0,0 +1,53 @@
1
+ import { JsSpawner } from 'alwaysai/lib/util';
2
+ import { logger } from '../util/logger';
3
+ import sleep from '../util/sleep';
4
+
5
+ export async function checkRabbitMQContainerRunning() {
6
+ const spawner = JsSpawner();
7
+ return await spawner.run({
8
+ exe: 'docker',
9
+ args: ['ps', '-q', '--filter', 'ancestor=rabbitmq']
10
+ });
11
+ }
12
+
13
+ export async function setupRabbitMQContainer() {
14
+ const spawner = JsSpawner();
15
+ const rabbitMqContainerRunning = await checkRabbitMQContainerRunning();
16
+ logger.debug('Checking for running RabbitMQ container');
17
+ if (!rabbitMqContainerRunning) {
18
+ logger.debug('Setting up RabbitMQ container');
19
+ await spawner.run({
20
+ exe: 'docker',
21
+ args: [
22
+ 'run',
23
+ '--rm',
24
+ '-p',
25
+ '5672:5672',
26
+ '-d',
27
+ '--hostname',
28
+ 'my-rabbit',
29
+ 'rabbitmq'
30
+ ]
31
+ });
32
+ await sleep(8000);
33
+ while (!(await checkRabbitMQContainerRunning())) {
34
+ await sleep(5000);
35
+ }
36
+ }
37
+ }
38
+
39
+ export async function stopRabbitMQContainer() {
40
+ const spawner = JsSpawner();
41
+ const rabbitMqContainer = await spawner.run({
42
+ exe: 'docker',
43
+ args: ['ps', '-q', '--filter', 'ancestor=rabbitmq']
44
+ });
45
+ if (!rabbitMqContainer) {
46
+ logger.debug('No RabbitMQ container running');
47
+ } else {
48
+ await spawner.run({
49
+ exe: 'docker',
50
+ args: ['stop', `${rabbitMqContainer}`]
51
+ });
52
+ }
53
+ }