@alwaysai/device-agent 0.1.0 → 0.1.2

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 (100) hide show
  1. package/lib/application-control/config.d.ts +0 -1
  2. package/lib/application-control/config.d.ts.map +1 -1
  3. package/lib/application-control/config.js +15 -29
  4. package/lib/application-control/config.js.map +1 -1
  5. package/lib/application-control/environment-variables.d.ts +7 -3
  6. package/lib/application-control/environment-variables.d.ts.map +1 -1
  7. package/lib/application-control/environment-variables.js +71 -35
  8. package/lib/application-control/environment-variables.js.map +1 -1
  9. package/lib/application-control/environment-variables.test.d.ts +2 -0
  10. package/lib/application-control/environment-variables.test.d.ts.map +1 -0
  11. package/lib/application-control/environment-variables.test.js +163 -0
  12. package/lib/application-control/environment-variables.test.js.map +1 -0
  13. package/lib/application-control/index.d.ts +3 -3
  14. package/lib/application-control/index.d.ts.map +1 -1
  15. package/lib/application-control/index.js +1 -3
  16. package/lib/application-control/index.js.map +1 -1
  17. package/lib/application-control/models.d.ts +0 -1
  18. package/lib/application-control/models.d.ts.map +1 -1
  19. package/lib/application-control/models.js +12 -26
  20. package/lib/application-control/models.js.map +1 -1
  21. package/lib/application-control/status.d.ts +3 -0
  22. package/lib/application-control/status.d.ts.map +1 -1
  23. package/lib/application-control/status.js +19 -1
  24. package/lib/application-control/status.js.map +1 -1
  25. package/lib/application-control/utils.d.ts.map +1 -1
  26. package/lib/application-control/utils.js +2 -2
  27. package/lib/application-control/utils.js.map +1 -1
  28. package/lib/cloud-connection/device-agent-cloud-connection.d.ts +6 -3
  29. package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
  30. package/lib/cloud-connection/device-agent-cloud-connection.js +205 -151
  31. package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
  32. package/lib/cloud-connection/live-updates-handler.d.ts +3 -0
  33. package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
  34. package/lib/cloud-connection/live-updates-handler.js +23 -7
  35. package/lib/cloud-connection/live-updates-handler.js.map +1 -1
  36. package/lib/cloud-connection/live-updates-handler.test.d.ts +2 -0
  37. package/lib/cloud-connection/live-updates-handler.test.d.ts.map +1 -0
  38. package/lib/cloud-connection/live-updates-handler.test.js +57 -0
  39. package/lib/cloud-connection/live-updates-handler.test.js.map +1 -0
  40. package/lib/cloud-connection/passthrough-handler.d.ts.map +1 -1
  41. package/lib/cloud-connection/passthrough-handler.js +6 -3
  42. package/lib/cloud-connection/passthrough-handler.js.map +1 -1
  43. package/lib/cloud-connection/shadow-handler.d.ts +11 -3
  44. package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
  45. package/lib/cloud-connection/shadow-handler.js +22 -7
  46. package/lib/cloud-connection/shadow-handler.js.map +1 -1
  47. package/lib/cloud-connection/shadow-handler.test.js +313 -228
  48. package/lib/cloud-connection/shadow-handler.test.js.map +1 -1
  49. package/lib/cloud-connection/shadow.js +1 -1
  50. package/lib/cloud-connection/shadow.js.map +1 -1
  51. package/lib/environment.d.ts +1 -0
  52. package/lib/environment.d.ts.map +1 -1
  53. package/lib/environment.js +2 -1
  54. package/lib/environment.js.map +1 -1
  55. package/lib/infrastructure/agent-config.d.ts +3 -1
  56. package/lib/infrastructure/agent-config.d.ts.map +1 -1
  57. package/lib/subcommands/app/env-vars.d.ts +1 -1
  58. package/lib/subcommands/app/env-vars.d.ts.map +1 -1
  59. package/lib/subcommands/app/env-vars.js +32 -5
  60. package/lib/subcommands/app/env-vars.js.map +1 -1
  61. package/lib/subcommands/app/index.d.ts.map +1 -1
  62. package/lib/subcommands/app/index.js +4 -1
  63. package/lib/subcommands/app/index.js.map +1 -1
  64. package/lib/subcommands/app/models.d.ts.map +1 -1
  65. package/lib/subcommands/app/models.js +6 -1
  66. package/lib/subcommands/app/models.js.map +1 -1
  67. package/lib/subcommands/app/shadow.d.ts +7 -0
  68. package/lib/subcommands/app/shadow.d.ts.map +1 -0
  69. package/lib/subcommands/app/shadow.js +48 -0
  70. package/lib/subcommands/app/shadow.js.map +1 -0
  71. package/lib/subcommands/app/version.js +2 -2
  72. package/lib/subcommands/app/version.js.map +1 -1
  73. package/lib/util/cloud-mode-ready.d.ts +2 -0
  74. package/lib/util/cloud-mode-ready.d.ts.map +1 -0
  75. package/lib/util/cloud-mode-ready.js +22 -0
  76. package/lib/util/cloud-mode-ready.js.map +1 -0
  77. package/package.json +1 -1
  78. package/readme.md +2 -2
  79. package/src/application-control/config.ts +30 -31
  80. package/src/application-control/environment-variables.test.ts +171 -0
  81. package/src/application-control/environment-variables.ts +102 -43
  82. package/src/application-control/index.ts +3 -9
  83. package/src/application-control/models.ts +14 -29
  84. package/src/application-control/status.ts +20 -0
  85. package/src/application-control/utils.ts +4 -2
  86. package/src/cloud-connection/device-agent-cloud-connection.ts +222 -153
  87. package/src/cloud-connection/live-updates-handler.test.ts +68 -0
  88. package/src/cloud-connection/live-updates-handler.ts +30 -7
  89. package/src/cloud-connection/passthrough-handler.ts +10 -3
  90. package/src/cloud-connection/shadow-handler.test.ts +329 -239
  91. package/src/cloud-connection/shadow-handler.ts +38 -12
  92. package/src/cloud-connection/shadow.ts +1 -1
  93. package/src/environment.ts +2 -0
  94. package/src/infrastructure/agent-config.ts +1 -1
  95. package/src/subcommands/app/env-vars.ts +38 -8
  96. package/src/subcommands/app/index.ts +4 -1
  97. package/src/subcommands/app/models.ts +10 -1
  98. package/src/subcommands/app/shadow.ts +48 -0
  99. package/src/subcommands/app/version.ts +2 -2
  100. package/src/util/cloud-mode-ready.ts +23 -0
@@ -2,7 +2,7 @@ import {
2
2
  AppConfig,
3
3
  validateAppConfig
4
4
  } from '@alwaysai/app-configuration-schemas';
5
- import { readAppCfgFile } from '../application-control';
5
+ import { EnvVars, getAllEnvs, readAppCfgFile } from '../application-control';
6
6
  import { logger } from '../util/logger';
7
7
  import { Publisher } from './publisher';
8
8
  import { AppConfigModels, getAppCfgModelsDiff } from './shadow';
@@ -21,11 +21,20 @@ export interface ShadowTopics {
21
21
  }
22
22
 
23
23
  export type AppConfigUpdate = {
24
- projectId: string;
25
24
  newAppCfg: AppConfig;
26
25
  updatedModels?: AppConfigModels;
27
26
  };
28
27
 
28
+ export type EnvVarUpdate = {
29
+ envVars: EnvVars;
30
+ };
31
+
32
+ export type ShadowUpdate = {
33
+ projectId: string;
34
+ appCfgUpdate?: AppConfigUpdate;
35
+ envVarUpdate?: EnvVarUpdate;
36
+ };
37
+
29
38
  export class ShadowHandler {
30
39
  private clientId: string;
31
40
  private publisher: Publisher;
@@ -54,13 +63,14 @@ export class ShadowHandler {
54
63
  delta
55
64
  }: {
56
65
  delta: any;
57
- }): Promise<AppConfigUpdate[]> {
58
- const appConfigUpdates: AppConfigUpdate[] = [];
66
+ }): Promise<ShadowUpdate[]> {
67
+ const updates: ShadowUpdate[] = [];
59
68
 
60
69
  const deltaKeys = Object.keys(delta);
61
70
 
62
71
  for (const projectId of deltaKeys) {
63
72
  const projectShadow = delta[projectId];
73
+ const shadowUpdate: ShadowUpdate = { projectId };
64
74
 
65
75
  if (projectShadow.appConfig) {
66
76
  const newAppCfg = JSON.parse(projectShadow.appConfig);
@@ -81,17 +91,29 @@ export class ShadowHandler {
81
91
  });
82
92
 
83
93
  if (updatedModels && Object.keys(updatedModels).length) {
84
- appConfigUpdates.push({ projectId, newAppCfg, updatedModels });
94
+ shadowUpdate.appCfgUpdate = { newAppCfg, updatedModels };
85
95
  } else {
86
- appConfigUpdates.push({ projectId, newAppCfg });
96
+ shadowUpdate.appCfgUpdate = { newAppCfg };
87
97
  }
88
98
  } else {
89
- logger.warn(
90
- `Ignoring shadow update for ${projectId} due to no app config`
99
+ logger.info(
100
+ `Ignoring app config shadow update for ${projectId} due to no delta`
91
101
  );
92
102
  }
103
+
104
+ if (projectShadow.envVars) {
105
+ const envVars = projectShadow.envVars;
106
+ shadowUpdate.envVarUpdate = { envVars };
107
+ } else {
108
+ logger.info(
109
+ `Ignoring app environment variable shadow update for ${projectId} due to no delta`
110
+ );
111
+ }
112
+ if (shadowUpdate.appCfgUpdate || shadowUpdate.envVarUpdate) {
113
+ updates.push(shadowUpdate);
114
+ }
93
115
  }
94
- return appConfigUpdates;
116
+ return updates;
95
117
  }
96
118
 
97
119
  // Public interface
@@ -104,7 +126,7 @@ export class ShadowHandler {
104
126
  topic: string;
105
127
  payload: any;
106
128
  clientToken: string;
107
- }): Promise<AppConfigUpdate[]> {
129
+ }): Promise<ShadowUpdate[]> {
108
130
  // TODO: make use a function like the other topic getters
109
131
  const shadowName = topic.split('/')[5];
110
132
  switch (topic) {
@@ -141,12 +163,16 @@ export class ShadowHandler {
141
163
  return [];
142
164
  }
143
165
 
144
- public async publishAppConfig(projectId: string) {
166
+ public async updateProjectShadow(projectId: string) {
145
167
  const appCfg = await readAppCfgFile({ projectId });
168
+ const envVars = await getAllEnvs({ projectId });
146
169
  const packet = {
147
170
  state: {
148
171
  reported: {
149
- [projectId]: { appConfig: JSON.stringify(appCfg) }
172
+ [projectId]: {
173
+ appConfig: JSON.stringify(appCfg),
174
+ envVars
175
+ }
150
176
  }
151
177
  },
152
178
  clientToken: this.clientId
@@ -43,7 +43,7 @@ export const getAppCfgModelsDiff = async ({
43
43
  newScripts[scriptName] = shadowScripts[scriptName];
44
44
  });
45
45
  } catch (e) {
46
- logger.error('Error parsing app config update: ', e.message);
46
+ logger.error(`Error parsing app config update: ${e.message}`);
47
47
  }
48
48
 
49
49
  return { scripts: newScripts, updatedModels, untouchedModels };
@@ -13,6 +13,8 @@ export const ALWAYSAI_LOG_TO_CONSOLE = process.env.ALWAYSAI_LOG_TO_CONSOLE;
13
13
  export const ALWAYSAI_ANALYTICS_PASSTHROUGH = parseBoolean(
14
14
  process.env.ALWAYSAI_ANALYTICS_PASSTHROUGH
15
15
  );
16
+ export const ALWAYSAI_LIVE_UPDATES_TIMEOUT_MS =
17
+ process.env.ALWAYSAI_LIVE_UPDATES_TIMEOUT_MS;
16
18
 
17
19
  function parseOsPlatform(str: string | undefined): NodeJS.Platform {
18
20
  switch (str) {
@@ -70,7 +70,7 @@ export interface AgentJsonFileReturnType
70
70
  setAppInstalling;
71
71
  setAppInstalled;
72
72
  setAppUninstalled;
73
- getAppVersion;
73
+ getAppVersion: (props: { projectId: string }) => Promise<string>;
74
74
  setAppBackup;
75
75
  getAppBackup;
76
76
  }
@@ -1,10 +1,12 @@
1
1
  import {
2
2
  CliLeaf,
3
3
  CliStringArrayInput,
4
- CliStringInput
4
+ CliStringInput,
5
+ CliTerseError
5
6
  } from '@alwaysai/alwayscli';
6
- import { getAllEnvs, setEnv } from '../../application-control';
7
- import { logger } from '../../util/logger';
7
+ import { EnvVars, getAllEnvs } from '../../application-control';
8
+ import { DeviceAgentCloudConnection } from '../../cloud-connection/device-agent-cloud-connection';
9
+ import sleep from '../../util/sleep';
8
10
 
9
11
  export const getAllEnvsCliLeaf = CliLeaf({
10
12
  name: 'get-all-envs',
@@ -17,7 +19,8 @@ export const getAllEnvsCliLeaf = CliLeaf({
17
19
  },
18
20
  async action(_, opts) {
19
21
  const { project } = opts;
20
- logger.info(await getAllEnvs({ projectId: project }));
22
+ const envVars = await getAllEnvs({ projectId: project });
23
+ console.log(JSON.stringify(envVars, null, 2));
21
24
  }
22
25
  });
23
26
 
@@ -34,13 +37,40 @@ export const setEnvCliLeaf = CliLeaf({
34
37
  required: true
35
38
  }),
36
39
  service: CliStringInput({
37
- description:
38
- 'The name of the docker-compose service to apply environment variable to',
39
- required: false
40
+ description: 'Docker compose service to set environment variables for',
41
+ required: true
40
42
  })
41
43
  },
42
44
  async action(args, opts) {
43
45
  const { project, service } = opts;
44
- await setEnv({ projectId: project, vars: args, service });
46
+ const envVars: EnvVars = { [service]: {} };
47
+ args.forEach((arg: string) => {
48
+ const nameVal = arg.split('=');
49
+ if (nameVal.length !== 2) {
50
+ throw new CliTerseError(`Invalid argument: ${arg}`);
51
+ }
52
+ envVars[service][nameVal[0]] = nameVal[1];
53
+ });
54
+ const deviceAgent = new DeviceAgentCloudConnection();
55
+ await deviceAgent.setupHandlers();
56
+
57
+ const topic = deviceAgent.getShadowTopics().projects.updateDelta;
58
+
59
+ const message = {
60
+ version: 3,
61
+ timestamp: 0,
62
+ state: {
63
+ [project]: {
64
+ envVars
65
+ }
66
+ },
67
+ clientToken: 'not-self'
68
+ };
69
+
70
+ await deviceAgent.handleMessage(topic, message);
71
+ while (deviceAgent.isCmdInProgress(project)) {
72
+ await sleep(1000);
73
+ }
74
+ await deviceAgent.stop();
45
75
  }
46
76
  });
@@ -20,6 +20,7 @@ import {
20
20
  uninstallAppCliLeaf,
21
21
  rollbackAppCliLeaf
22
22
  } from './version';
23
+ import { getShadowCliLeaf, updateShadowCliLeaf } from './shadow';
23
24
 
24
25
  export const appCliBranch = CliBranch({
25
26
  name: 'app',
@@ -40,6 +41,8 @@ export const appCliBranch = CliBranch({
40
41
  replaceModelsCliLeaf,
41
42
  updateModelsCliLeaf,
42
43
  getAllEnvsCliLeaf,
43
- setEnvCliLeaf
44
+ setEnvCliLeaf,
45
+ getShadowCliLeaf,
46
+ updateShadowCliLeaf
44
47
  ]
45
48
  });
@@ -71,7 +71,7 @@ export const addModelCliLeaf = CliLeaf({
71
71
  while (deviceAgent.isCmdInProgress(project)) {
72
72
  await sleep(1000);
73
73
  }
74
- deviceAgent.stop();
74
+ await deviceAgent.stop();
75
75
  }
76
76
  });
77
77
 
@@ -90,6 +90,7 @@ export const removeModelCliLeaf = CliLeaf({
90
90
  },
91
91
  async action(_, opts) {
92
92
  const { project, model } = opts;
93
+ // TODO: Replace with handleMessage()
93
94
  await removeModel({ projectId: project, modelId: model });
94
95
  }
95
96
  });
@@ -107,8 +108,12 @@ export const replaceModelsCliLeaf = CliLeaf({
107
108
  required: true
108
109
  })
109
110
  },
111
+ hidden: true,
110
112
  async action(_, opts) {
111
113
  const { project, models } = opts;
114
+ console.log(
115
+ 'This command is deprecated and will be removed in a future release'
116
+ );
112
117
  await replaceModels({ projectId: project, modelIds: models });
113
118
  }
114
119
  });
@@ -122,8 +127,12 @@ export const updateModelsCliLeaf = CliLeaf({
122
127
  required: true
123
128
  })
124
129
  },
130
+ hidden: true,
125
131
  async action(_, opts) {
126
132
  const { project } = opts;
133
+ console.log(
134
+ 'This command is deprecated and will be removed in a future release'
135
+ );
127
136
  await updateModels({ projectId: project });
128
137
  }
129
138
  });
@@ -0,0 +1,48 @@
1
+ import { CliLeaf, CliStringInput } from '@alwaysai/alwayscli';
2
+ import { DeviceAgentCloudConnection } from '../../cloud-connection/device-agent-cloud-connection';
3
+ import sleep from '../../util/sleep';
4
+
5
+ export const getShadowCliLeaf = CliLeaf({
6
+ name: 'get-shadow',
7
+ description: 'Get the current shadow',
8
+ namedInputs: {
9
+ project: CliStringInput({
10
+ description: 'Project Id',
11
+ required: true
12
+ })
13
+ },
14
+ async action(_, opts) {
15
+ const { project } = opts;
16
+ const deviceAgent = new DeviceAgentCloudConnection();
17
+ await deviceAgent.setupHandlers();
18
+
19
+ // The Device Agent always gets the latest shadow and updates
20
+ // itself. 5 seconds should cover the initial response wait time
21
+ await sleep(5000);
22
+ while (deviceAgent.isCmdInProgress(project)) {
23
+ await sleep(1000);
24
+ }
25
+ await deviceAgent.stop();
26
+ }
27
+ });
28
+
29
+ export const updateShadowCliLeaf = CliLeaf({
30
+ name: 'update-shadow',
31
+ description: 'Update the shadow with the current application configuration',
32
+ namedInputs: {
33
+ project: CliStringInput({
34
+ description: 'Project Id',
35
+ required: true
36
+ })
37
+ },
38
+ async action(_, opts) {
39
+ const { project } = opts;
40
+ const deviceAgent = new DeviceAgentCloudConnection();
41
+ await deviceAgent.setupHandlers();
42
+ await deviceAgent.updateProjectShadow(project);
43
+
44
+ // 5 seconds should cover the response wait time
45
+ await sleep(5000);
46
+ await deviceAgent.stop();
47
+ }
48
+ });
@@ -49,7 +49,7 @@ export const installAppCliLeaf = CliLeaf({
49
49
  while (deviceAgent.isCmdInProgress(project)) {
50
50
  await sleep(1000);
51
51
  }
52
- deviceAgent.stop();
52
+ await deviceAgent.stop();
53
53
  }
54
54
  });
55
55
 
@@ -82,7 +82,7 @@ export const uninstallAppCliLeaf = CliLeaf({
82
82
  while (deviceAgent.isCmdInProgress(project)) {
83
83
  await sleep(1000);
84
84
  }
85
- deviceAgent.stop();
85
+ await deviceAgent.stop();
86
86
  }
87
87
  });
88
88
 
@@ -0,0 +1,23 @@
1
+ import { existsSync } from 'fs';
2
+ import {
3
+ AWS_ROOT_CERTIFICATE_FILE_PATH,
4
+ DEVICE_PRIVATE_KEY_FILE_PATH,
5
+ DEVICE_CERTIFICATE_FILE_PATH
6
+ } from '../util/directories';
7
+
8
+ export function cloudModeReady() {
9
+ let ready = true;
10
+ const requiredFiles = [
11
+ DEVICE_CERTIFICATE_FILE_PATH,
12
+ DEVICE_PRIVATE_KEY_FILE_PATH,
13
+ AWS_ROOT_CERTIFICATE_FILE_PATH
14
+ ];
15
+
16
+ for (const path of requiredFiles) {
17
+ if (!existsSync(path)) {
18
+ ready = false;
19
+ break;
20
+ }
21
+ }
22
+ return ready;
23
+ }