@alwaysai/device-agent 0.0.16 → 0.0.18

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 (80) hide show
  1. package/lib/application-control/config.d.ts.map +1 -1
  2. package/lib/application-control/config.js +4 -4
  3. package/lib/application-control/config.js.map +1 -1
  4. package/lib/application-control/install.d.ts.map +1 -1
  5. package/lib/application-control/install.js +1 -1
  6. package/lib/application-control/install.js.map +1 -1
  7. package/lib/application-control/models.js +1 -1
  8. package/lib/application-control/utils.d.ts.map +1 -1
  9. package/lib/application-control/utils.js +3 -3
  10. package/lib/application-control/utils.js.map +1 -1
  11. package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
  12. package/lib/cloud-connection/device-agent-cloud-connection.js +28 -15
  13. package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
  14. package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
  15. package/lib/cloud-connection/live-updates-handler.js +9 -8
  16. package/lib/cloud-connection/live-updates-handler.js.map +1 -1
  17. package/lib/cloud-connection/messages.js +1 -1
  18. package/lib/cloud-connection/messages.js.map +1 -1
  19. package/lib/cloud-connection/passthrough-handler.d.ts.map +1 -1
  20. package/lib/cloud-connection/passthrough-handler.js +0 -1
  21. package/lib/cloud-connection/passthrough-handler.js.map +1 -1
  22. package/lib/cloud-connection/publisher.d.ts.map +1 -1
  23. package/lib/cloud-connection/publisher.js +0 -1
  24. package/lib/cloud-connection/publisher.js.map +1 -1
  25. package/lib/cloud-connection/shadow-handler.d.ts +9 -5
  26. package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
  27. package/lib/cloud-connection/shadow-handler.js +14 -6
  28. package/lib/cloud-connection/shadow-handler.js.map +1 -1
  29. package/lib/cloud-connection/shadow-handler.test.js +24 -14
  30. package/lib/cloud-connection/shadow-handler.test.js.map +1 -1
  31. package/lib/cloud-connection/shadow.js +1 -1
  32. package/lib/cloud-connection/shadow.js.map +1 -1
  33. package/lib/subcommands/app/env-vars.d.ts +8 -0
  34. package/lib/subcommands/app/env-vars.d.ts.map +1 -0
  35. package/lib/subcommands/app/env-vars.js +43 -0
  36. package/lib/subcommands/app/env-vars.js.map +1 -0
  37. package/lib/subcommands/app/index.d.ts.map +1 -1
  38. package/lib/subcommands/app/index.js +20 -19
  39. package/lib/subcommands/app/index.js.map +1 -1
  40. package/lib/subcommands/app/models.d.ts +20 -0
  41. package/lib/subcommands/app/models.d.ts.map +1 -0
  42. package/lib/subcommands/app/models.js +114 -0
  43. package/lib/subcommands/app/models.js.map +1 -0
  44. package/lib/subcommands/app/status.d.ts +17 -0
  45. package/lib/subcommands/app/status.d.ts.map +1 -0
  46. package/lib/subcommands/app/status.js +103 -0
  47. package/lib/subcommands/app/status.js.map +1 -0
  48. package/lib/subcommands/app/version.d.ts +12 -0
  49. package/lib/subcommands/app/version.d.ts.map +1 -0
  50. package/lib/subcommands/app/version.js +103 -0
  51. package/lib/subcommands/app/version.js.map +1 -0
  52. package/lib/util/get-device-id.js +1 -1
  53. package/lib/util/get-device-id.js.map +1 -1
  54. package/lib/util/http-client.js +1 -1
  55. package/lib/util/http-client.js.map +1 -1
  56. package/package.json +1 -1
  57. package/src/application-control/config.ts +8 -4
  58. package/src/application-control/install.ts +3 -1
  59. package/src/application-control/models.ts +1 -1
  60. package/src/application-control/utils.ts +3 -4
  61. package/src/cloud-connection/device-agent-cloud-connection.ts +33 -21
  62. package/src/cloud-connection/live-updates-handler.ts +14 -13
  63. package/src/cloud-connection/messages.ts +1 -1
  64. package/src/cloud-connection/passthrough-handler.ts +0 -1
  65. package/src/cloud-connection/publisher.ts +0 -1
  66. package/src/cloud-connection/shadow-handler.test.ts +24 -14
  67. package/src/cloud-connection/shadow-handler.ts +21 -8
  68. package/src/cloud-connection/shadow.ts +1 -1
  69. package/src/subcommands/app/env-vars.ts +46 -0
  70. package/src/subcommands/app/index.ts +16 -17
  71. package/src/subcommands/app/models.ts +129 -0
  72. package/src/subcommands/app/status.ts +92 -0
  73. package/src/subcommands/app/version.ts +103 -0
  74. package/src/util/get-device-id.ts +1 -1
  75. package/src/util/http-client.ts +1 -1
  76. package/lib/subcommands/app/app.d.ts +0 -60
  77. package/lib/subcommands/app/app.d.ts.map +0 -1
  78. package/lib/subcommands/app/app.js +0 -370
  79. package/lib/subcommands/app/app.js.map +0 -1
  80. package/src/subcommands/app/app.ts +0 -390
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rollbackAppCliLeaf = exports.uninstallAppCliLeaf = exports.installAppCliLeaf = exports.listAppsCliLeaf = void 0;
4
+ const alwayscli_1 = require("@alwaysai/alwayscli");
5
+ const device_agent_schemas_1 = require("@alwaysai/device-agent-schemas");
6
+ const application_control_1 = require("../../application-control");
7
+ const device_agent_cloud_connection_1 = require("../../cloud-connection/device-agent-cloud-connection");
8
+ const agent_config_1 = require("../../infrastructure/agent-config");
9
+ const sleep_1 = require("../../util/sleep");
10
+ exports.listAppsCliLeaf = (0, alwayscli_1.CliLeaf)({
11
+ name: 'list',
12
+ description: 'List all installed apps',
13
+ namedInputs: {},
14
+ async action(_, opts) {
15
+ const apps = await (0, agent_config_1.AgentConfigFile)().getApps();
16
+ console.table(apps);
17
+ }
18
+ });
19
+ exports.installAppCliLeaf = (0, alwayscli_1.CliLeaf)({
20
+ name: 'install',
21
+ description: 'Install an alwaysAI app from a project',
22
+ namedInputs: {
23
+ project: (0, alwayscli_1.CliStringInput)({
24
+ description: 'Project ID',
25
+ required: true
26
+ }),
27
+ releaseHash: (0, alwayscli_1.CliStringInput)({
28
+ description: 'Release Hash',
29
+ required: true
30
+ })
31
+ },
32
+ async action(_, opts) {
33
+ const { project, releaseHash } = opts;
34
+ const deviceAgent = new device_agent_cloud_connection_1.DeviceAgentCloudConnection();
35
+ await deviceAgent.setupHandlers();
36
+ const topic = deviceAgent.getToDeviceTopic();
37
+ const message = {
38
+ timestamp: '',
39
+ topic,
40
+ payload: {
41
+ messageType: device_agent_schemas_1.keyMirrors.clientMessageType.app_version_control,
42
+ appVersionControl: {
43
+ baseCommand: device_agent_schemas_1.keyMirrors.appVersionControl.install,
44
+ projectId: project,
45
+ appReleaseHash: releaseHash
46
+ }
47
+ }
48
+ };
49
+ await deviceAgent.handleMessage(topic, message);
50
+ while (deviceAgent.isCmdInProgress(project)) {
51
+ await (0, sleep_1.default)(1000);
52
+ }
53
+ deviceAgent.stop();
54
+ }
55
+ });
56
+ exports.uninstallAppCliLeaf = (0, alwayscli_1.CliLeaf)({
57
+ name: 'uninstall',
58
+ description: 'Remove an alwaysAI app',
59
+ namedInputs: {
60
+ project: (0, alwayscli_1.CliStringInput)({
61
+ description: 'Project ID',
62
+ required: true
63
+ })
64
+ },
65
+ async action(_, opts) {
66
+ const { project } = opts;
67
+ const deviceAgent = new device_agent_cloud_connection_1.DeviceAgentCloudConnection();
68
+ await deviceAgent.setupHandlers();
69
+ const topic = deviceAgent.getToDeviceTopic();
70
+ const message = {
71
+ timestamp: '',
72
+ topic,
73
+ payload: {
74
+ messageType: device_agent_schemas_1.keyMirrors.clientMessageType.app_version_control,
75
+ appVersionControl: {
76
+ baseCommand: device_agent_schemas_1.keyMirrors.appVersionControl.uninstall,
77
+ projectId: project
78
+ }
79
+ }
80
+ };
81
+ await deviceAgent.handleMessage(topic, message);
82
+ while (deviceAgent.isCmdInProgress(project)) {
83
+ await (0, sleep_1.default)(1000);
84
+ }
85
+ deviceAgent.stop();
86
+ }
87
+ });
88
+ exports.rollbackAppCliLeaf = (0, alwayscli_1.CliLeaf)({
89
+ name: 'rollback',
90
+ description: 'Rollback an alwaysAI app to the previous version',
91
+ namedInputs: {
92
+ project: (0, alwayscli_1.CliStringInput)({
93
+ description: 'Project ID',
94
+ required: true
95
+ })
96
+ },
97
+ hidden: true,
98
+ async action(_, opts) {
99
+ const { project } = opts;
100
+ await (0, application_control_1.rollbackApp)({ projectId: project });
101
+ }
102
+ });
103
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/subcommands/app/version.ts"],"names":[],"mappings":"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"}
@@ -11,7 +11,7 @@ function getDeviceUuid() {
11
11
  const cfg = deviceCfgFile.read();
12
12
  return cfg.deviceUuid;
13
13
  }
14
- catch (err) {
14
+ catch (e) {
15
15
  throw new Error(`Failed to read device configuration: ${JSON.stringify(deviceCfgFile.getErrors(), null, 2)}`);
16
16
  }
17
17
  }
@@ -1 +1 @@
1
- {"version":3,"file":"get-device-id.js","sourceRoot":"","sources":["../../src/util/get-device-id.ts"],"names":[],"mappings":";;;AAAA,qDAA4D;AAE5D,SAAgB,aAAa;IAC3B,MAAM,aAAa,GAAG,IAAA,yBAAgB,GAAE,CAAC;IACzC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE;QAC3B,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;KACpD;IACD,IAAI;QACF,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC;QACjC,OAAO,GAAG,CAAC,UAAU,CAAC;KACvB;IAAC,OAAO,GAAG,EAAE;QACZ,MAAM,IAAI,KAAK,CACb,wCAAwC,IAAI,CAAC,SAAS,CACpD,aAAa,CAAC,SAAS,EAAE,EACzB,IAAI,EACJ,CAAC,CACF,EAAE,CACJ,CAAC;KACH;AACH,CAAC;AAjBD,sCAiBC"}
1
+ {"version":3,"file":"get-device-id.js","sourceRoot":"","sources":["../../src/util/get-device-id.ts"],"names":[],"mappings":";;;AAAA,qDAA4D;AAE5D,SAAgB,aAAa;IAC3B,MAAM,aAAa,GAAG,IAAA,yBAAgB,GAAE,CAAC;IACzC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE;QAC3B,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;KACpD;IACD,IAAI;QACF,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC;QACjC,OAAO,GAAG,CAAC,UAAU,CAAC;KACvB;IAAC,OAAO,CAAC,EAAE;QACV,MAAM,IAAI,KAAK,CACb,wCAAwC,IAAI,CAAC,SAAS,CACpD,aAAa,CAAC,SAAS,EAAE,EACzB,IAAI,EACJ,CAAC,CACF,EAAE,CACJ,CAAC;KACH;AACH,CAAC;AAjBD,sCAiBC"}
@@ -18,7 +18,7 @@ async function httpClient(url, method, headers, data) {
18
18
  }
19
19
  }
20
20
  catch (e) {
21
- logger_1.logger.error(`HTTP Client error for ${url}: ${e}`);
21
+ logger_1.logger.error(`HTTP Client error for ${url}: ${e.message}`);
22
22
  }
23
23
  }
24
24
  exports.httpClient = httpClient;
@@ -1 +1 @@
1
- {"version":3,"file":"http-client.js","sourceRoot":"","sources":["../../src/util/http-client.ts"],"names":[],"mappings":";;;AAAA,2CAA+B;AAC/B,4CAAsD;AACtD,gEAAsE;AACtE,2CAAwC;AAEjC,KAAK,UAAU,UAAU,CAC9B,GAAW,EACX,MAAc,EACd,OAAY,EACZ,IAAa;IAEb,MAAM,OAAO,mBACX,MAAM,IACH,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAClD,CAAC;IACF,IAAI;QACF,MAAM,QAAQ,GAAG,MAAM,IAAA,oBAAK,EAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC3C,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACzD,IAAI,WAAW,KAAK,kBAAkB,EAAE;YACtC,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;SAC9B;QACD,IAAI,WAAY,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE;YACvC,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;SAC9B;KACF;IAAC,OAAO,CAAC,EAAE;QACV,eAAM,CAAC,KAAK,CAAC,yBAAyB,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC;KACpD;AACH,CAAC;AAtBD,gCAsBC;AAEM,KAAK,UAAU,sBAAsB,CAC1C,OAAe,EACf,IAAY,EACZ,MAAc,EACd,IAAa;IAEb,MAAM,QAAQ,GAAG,IAAA,kCAAsB,EAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACvD,MAAM,EAAE,wBAAwB,EAAE,GAAG,IAAA,wCAAuB,GAAE,CAAC;IAC/D,MAAM,mBAAmB,GAAG,MAAM,wBAAwB,EAAE,CAAC;IAC7D,OAAO,MAAM,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,mBAAmB,EAAE,IAAI,CAAC,CAAC;AACvE,CAAC;AAVD,wDAUC"}
1
+ {"version":3,"file":"http-client.js","sourceRoot":"","sources":["../../src/util/http-client.ts"],"names":[],"mappings":";;;AAAA,2CAA+B;AAC/B,4CAAsD;AACtD,gEAAsE;AACtE,2CAAwC;AAEjC,KAAK,UAAU,UAAU,CAC9B,GAAW,EACX,MAAc,EACd,OAAY,EACZ,IAAa;IAEb,MAAM,OAAO,mBACX,MAAM,IACH,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAClD,CAAC;IACF,IAAI;QACF,MAAM,QAAQ,GAAG,MAAM,IAAA,oBAAK,EAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC3C,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACzD,IAAI,WAAW,KAAK,kBAAkB,EAAE;YACtC,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;SAC9B;QACD,IAAI,WAAY,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE;YACvC,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;SAC9B;KACF;IAAC,OAAO,CAAC,EAAE;QACV,eAAM,CAAC,KAAK,CAAC,yBAAyB,GAAG,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;KAC5D;AACH,CAAC;AAtBD,gCAsBC;AAEM,KAAK,UAAU,sBAAsB,CAC1C,OAAe,EACf,IAAY,EACZ,MAAc,EACd,IAAa;IAEb,MAAM,QAAQ,GAAG,IAAA,kCAAsB,EAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACvD,MAAM,EAAE,wBAAwB,EAAE,GAAG,IAAA,wCAAuB,GAAE,CAAC;IAC/D,MAAM,mBAAmB,GAAG,MAAM,wBAAwB,EAAE,CAAC;IAC7D,OAAO,MAAM,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,mBAAmB,EAAE,IAAI,CAAC,CAAC;AACvE,CAAC;AAVD,wDAUC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@alwaysai/device-agent",
3
3
  "description": "The alwaysAI Device Agent",
4
- "version": "0.0.16",
4
+ "version": "0.0.18",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
7
7
  "publishConfig": {
@@ -23,9 +23,11 @@ export async function readAppCfgFile(props: {
23
23
  }
24
24
  try {
25
25
  return appJson.read();
26
- } catch (err) {
26
+ } catch (e) {
27
27
  throw new Error(
28
- `Error reading app config for ${projectId}:\n${err}\n${appJson.getErrors()}`
28
+ `Error reading app config for ${projectId}:\n${
29
+ e.message
30
+ }\n${appJson.getErrors()}`
29
31
  );
30
32
  }
31
33
  }
@@ -42,9 +44,11 @@ export async function writeAppCfgFile(props: {
42
44
  const appJson = AppJsonFile(appDir);
43
45
  try {
44
46
  appJson.write(appCfg);
45
- } catch (err) {
47
+ } catch (e) {
46
48
  throw new Error(
47
- `Error writing app config for ${projectId}:\n${err}\n${appJson.getErrors()}`
49
+ `Error writing app config for ${projectId}:\n${
50
+ e.message
51
+ }\n${appJson.getErrors()}`
48
52
  );
49
53
  }
50
54
  }
@@ -201,7 +201,9 @@ export async function uninstallApp(props: {
201
201
  try {
202
202
  await stopApp({ projectId });
203
203
  } catch (e) {
204
- logger.warn(`Failed to stop ${projectId}, may be left running...\n${e}`);
204
+ logger.warn(
205
+ `Failed to stop ${projectId}, may be left running...\n${e.message}`
206
+ );
205
207
  }
206
208
  await AgentConfigFile().setAppUninstalled({ projectId });
207
209
  // Delete application directory and backup
@@ -213,7 +213,7 @@ export async function updateModelsWithPresignedUrls(props: {
213
213
  } catch (e) {
214
214
  logger.error(
215
215
  'Error updating app models from presigned URL, restoring models.',
216
- e
216
+ e.message
217
217
  );
218
218
  await spawner.rimraf(ogDir);
219
219
  await copyDir({ srcPath: restoreDir, destPath: ogDir });
@@ -104,11 +104,10 @@ export async function downloadPackageUsingPresignedUrl(props: {
104
104
  let response: any;
105
105
  try {
106
106
  response = await fetchWithTimeout(presignedUrl);
107
- } catch (error) {
108
- const errorBody =
109
- error.type === 'aborted' ? error : await error.response.text();
107
+ } catch (e) {
108
+ const errorBody = e.type === 'aborted' ? e : await e.response.text();
110
109
  throw new Error(
111
- `downloadPackageUsingPresignedUrl: Error=${error}\n${errorBody}`
110
+ `downloadPackageUsingPresignedUrl: Error=${e}\n${errorBody}`
112
111
  );
113
112
  }
114
113
 
@@ -138,7 +138,7 @@ export class DeviceAgentCloudConnection {
138
138
  });
139
139
  try {
140
140
  await uninstallApp({ projectId });
141
- this.shadowHandler.deleteProjectShadow(projectId);
141
+ this.shadowHandler.clearAppConfig(projectId);
142
142
 
143
143
  await this.cmdStatusMgr.stop(projectId);
144
144
  await this.liveUpdatesHandler.disableAppInstallStatus({
@@ -153,7 +153,7 @@ export class DeviceAgentCloudConnection {
153
153
  )
154
154
  );
155
155
  } catch (e) {
156
- logger.error(`Failed to uninstall ${projectId}: ${e}`);
156
+ logger.error(`Failed to uninstall ${projectId}: ${e.message}`);
157
157
  const message: string = e.message;
158
158
  await this.cmdStatusMgr.stop(projectId);
159
159
  await this.liveUpdatesHandler.disableAppInstallStatus({
@@ -200,29 +200,32 @@ export class DeviceAgentCloudConnection {
200
200
  );
201
201
 
202
202
  // update app config shadow for project
203
- await this.shadowHandler.publishAppState(projectId);
203
+ await this.shadowHandler.publishAppConfig(projectId);
204
204
  return out;
205
205
  } catch (e) {
206
- logger.error(`Failed to install ${projectId}: ${e}`);
206
+ logger.error(`Failed to install ${projectId}: ${e.message}`);
207
207
  const message: string = e.message;
208
208
 
209
209
  // uninstall the failed app to put system back in good state
210
210
  // TODO: Replace this with rollback
211
- await uninstallApp({ projectId });
212
- this.shadowHandler.deleteProjectShadow(projectId);
211
+ try {
212
+ await uninstallApp({ projectId });
213
+ } finally {
214
+ this.shadowHandler.clearAppConfig(projectId);
213
215
 
214
- await this.cmdStatusMgr.stop(projectId);
215
- await this.liveUpdatesHandler.disableAppInstallStatus({
216
- projectId
217
- });
218
- // Send final status message
219
- this.publisher.publishToClient(
220
- await getAppInstallStatusMessage(
221
- keyMirrors.appInstallStatus.failure,
222
- message,
223
- appReleaseHash
224
- )
225
- );
216
+ await this.cmdStatusMgr.stop(projectId);
217
+ await this.liveUpdatesHandler.disableAppInstallStatus({
218
+ projectId
219
+ });
220
+ // Send final status message
221
+ this.publisher.publishToClient(
222
+ await getAppInstallStatusMessage(
223
+ keyMirrors.appInstallStatus.failure,
224
+ message,
225
+ appReleaseHash
226
+ )
227
+ );
228
+ }
226
229
  }
227
230
  }
228
231
 
@@ -298,7 +301,10 @@ export class DeviceAgentCloudConnection {
298
301
 
299
302
  this.subscribe(this.toDeviceTopic);
300
303
  this.subscribe(this.shadowHandler.shadowTopics.projects.getAccepted);
304
+ this.subscribe(this.shadowHandler.shadowTopics.projects.getRejected);
301
305
  this.subscribe(this.shadowHandler.shadowTopics.projects.updateDelta);
306
+ this.subscribe(this.shadowHandler.shadowTopics.projects.updateAccepted);
307
+ this.subscribe(this.shadowHandler.shadowTopics.projects.updateRejected);
302
308
  }
303
309
 
304
310
  public getClientId(): string {
@@ -419,11 +425,17 @@ export class DeviceAgentCloudConnection {
419
425
  case this.shadowHandler.shadowTopics.projects.updateDelta: {
420
426
  const appConfigUpdates = await this.shadowHandler.handleShadowTopic({
421
427
  topic,
422
- payload: message.state
428
+ payload: message.state,
429
+ clientToken: message.clientToken
423
430
  });
424
431
  await this.handleAppConfigUpdates(appConfigUpdates);
425
432
  break;
426
433
  }
434
+ case this.shadowHandler.shadowTopics.projects.getRejected:
435
+ case this.shadowHandler.shadowTopics.projects.updateAccepted:
436
+ case this.shadowHandler.shadowTopics.projects.updateRejected:
437
+ // Not handling these for now
438
+ break;
427
439
  case this.toDeviceTopic:
428
440
  await this.handleClientMessage({
429
441
  topic,
@@ -461,8 +473,8 @@ export class DeviceAgentCloudConnection {
461
473
  try {
462
474
  const jsonPacket = JSON.parse(payload);
463
475
  await this.handleMessage(topic, jsonPacket);
464
- } catch (error) {
465
- logger.error(`Error parsing message: ${error}`);
476
+ } catch (e) {
477
+ logger.error(`Error parsing message: ${e.message}`);
466
478
  }
467
479
  });
468
480
 
@@ -132,16 +132,15 @@ export class LiveUpdatesHandler {
132
132
  while (true) {
133
133
  try {
134
134
  const message = await getMessageData(...args);
135
+ if (!this.continuePublishing(messageType, projectId)) {
136
+ logger.info(`Turned off live updates for ${messageType}`);
137
+ break;
138
+ }
135
139
  this.publisher.publishToClient(message);
136
140
  } catch (e) {
137
141
  logger.error(
138
142
  `Error publishing live updates for ${messageType}: ${e.message}`
139
143
  );
140
- break;
141
- }
142
- if (!this.continuePublishing(messageType, projectId)) {
143
- logger.info(`Turned off live updates for ${messageType}`);
144
- break;
145
144
  }
146
145
  await sleep(this.getLiveUpdatesInterval(messageType));
147
146
  }
@@ -161,14 +160,16 @@ export class LiveUpdatesHandler {
161
160
  }) {
162
161
  const { projectId, appReleaseHash } = props;
163
162
  this.liveUpdatesAlive.app_install_status = true;
164
- this.appInstallStatuses.add(projectId);
165
- // Don't wait for this call to finish since it loops until disabled
166
- void this.startPublishingLiveUpdates(
167
- keyMirrors.agentMessageType.app_install_status,
168
- getAppInstallStatusMessage,
169
- [keyMirrors.appInstallStatus.in_progress, '', appReleaseHash],
170
- projectId
171
- );
163
+ if (!this.appInstallStatuses.has(projectId)) {
164
+ this.appInstallStatuses.add(projectId);
165
+ // Don't wait for this call to finish since it loops until disabled
166
+ void this.startPublishingLiveUpdates(
167
+ keyMirrors.agentMessageType.app_install_status,
168
+ getAppInstallStatusMessage,
169
+ [keyMirrors.appInstallStatus.in_progress, '', appReleaseHash],
170
+ projectId
171
+ );
172
+ }
172
173
  }
173
174
 
174
175
  public async disableAppInstallStatus(props: { projectId: string }) {
@@ -14,7 +14,7 @@ import { AgentConfigFile } from '../infrastructure/agent-config';
14
14
 
15
15
  export async function getAppStateMessage() {
16
16
  const appStateMessage: AppStatePacket[] = [];
17
- const apps = await AgentConfigFile().getReadyApps();
17
+ const apps = await AgentConfigFile().getApps();
18
18
  for (const app of apps) {
19
19
  const projectId = app.projectId;
20
20
  const status = await getAppStatus({ projectId });
@@ -36,7 +36,6 @@ function processPublish(passthroughHandler: PassthroughHandler) {
36
36
  ackQueue.push(msg);
37
37
  // FIXME: put real topic here
38
38
  passthroughHandler.publisher.publishToCloudWithAck(packet, (errOrResp) => {
39
- logger.debug('packet published to cloud?', errOrResp);
40
39
  while (ackQueue.length > 0) {
41
40
  const msg = ackQueue.shift();
42
41
  if (errOrResp === true) {
@@ -48,7 +48,6 @@ export class Publisher {
48
48
  ackNackCallback: CallableFunction
49
49
  ) {
50
50
  const topic = this.toCloudTopic;
51
- logger.debug('payload received to publishWithAck', payload);
52
51
  this.client.publish(topic, payload, { qos: 1 }, (err: any, resp: any) => {
53
52
  if (err) {
54
53
  logger.error(
@@ -24,7 +24,8 @@ describe('Test Shadow Handler', () => {
24
24
  expect(async () => {
25
25
  await shadowHandler.handleShadowTopic({
26
26
  topic: shadowHandler.shadowTopics.projects.updateDelta,
27
- payload: Buffer.from('test-payload')
27
+ payload: Buffer.from('test-payload'),
28
+ clientToken: ''
28
29
  });
29
30
  }).toThrow(Error);
30
31
  });
@@ -51,13 +52,13 @@ describe('Test Shadow Handler', () => {
51
52
  const payload = {
52
53
  [projectId1]: {
53
54
  appConfig: JSON.stringify(appCfg1)
54
- },
55
- clientToken: clientId
55
+ }
56
56
  };
57
57
 
58
58
  const appCfgUpdates = await shadowHandler.handleShadowTopic({
59
59
  topic: shadowHandler.shadowTopics.projects.updateDelta,
60
- payload
60
+ payload,
61
+ clientToken: clientId
61
62
  });
62
63
  expect(appCfgUpdates.length).toBe(0);
63
64
  });
@@ -92,7 +93,8 @@ describe('Test Shadow Handler', () => {
92
93
 
93
94
  const appCfgUpdates = await shadowHandler.handleShadowTopic({
94
95
  topic: shadowHandler.shadowTopics.projects.getAccepted,
95
- payload
96
+ payload,
97
+ clientToken: ''
96
98
  });
97
99
  expect(appCfgUpdates.length).toBe(1);
98
100
  expect(appCfgUpdates[0]).toEqual({
@@ -121,7 +123,8 @@ describe('Test Shadow Handler', () => {
121
123
 
122
124
  const appCfgUpdates = await shadowHandler.handleShadowTopic({
123
125
  topic: shadowHandler.shadowTopics.projects.updateDelta,
124
- payload
126
+ payload,
127
+ clientToken: ''
125
128
  });
126
129
  expect(appCfgUpdates.length).toBe(0);
127
130
  });
@@ -155,7 +158,8 @@ describe('Test Shadow Handler', () => {
155
158
 
156
159
  const appCfgUpdates = await shadowHandler.handleShadowTopic({
157
160
  topic: shadowHandler.shadowTopics.projects.updateDelta,
158
- payload
161
+ payload,
162
+ clientToken: ''
159
163
  });
160
164
  expect(appCfgUpdates.length).toBe(1);
161
165
  expect(appCfgUpdates[0]).toEqual({
@@ -217,7 +221,8 @@ describe('Test Shadow Handler', () => {
217
221
 
218
222
  const appCfgUpdates = await shadowHandler.handleShadowTopic({
219
223
  topic: shadowHandler.shadowTopics.projects.updateDelta,
220
- payload
224
+ payload,
225
+ clientToken: ''
221
226
  });
222
227
  expect(appCfgUpdates.length).toBe(2);
223
228
  expect(appCfgUpdates[0]).toEqual({
@@ -264,7 +269,8 @@ describe('Test Shadow Handler', () => {
264
269
 
265
270
  const appCfgUpdates = await shadowHandler.handleShadowTopic({
266
271
  topic: shadowHandler.shadowTopics.projects.updateDelta,
267
- payload
272
+ payload,
273
+ clientToken: ''
268
274
  });
269
275
  expect(appCfgUpdates.length).toBe(1);
270
276
  expect(appCfgUpdates[0]).toEqual({
@@ -302,12 +308,13 @@ describe('Test Shadow Handler', () => {
302
308
 
303
309
  const appCfgUpdates = await shadowHandler.handleShadowTopic({
304
310
  topic: shadowHandler.shadowTopics.projects.updateDelta,
305
- payload
311
+ payload,
312
+ clientToken: ''
306
313
  });
307
314
  expect(appCfgUpdates.length).toBe(0);
308
315
  });
309
316
 
310
- test.skip('publish app state', async () => {
317
+ test.skip('publish app config', async () => {
311
318
  // FIXME: For some reason publisher is not being called...
312
319
  const testAppCfg: AppConfig = {
313
320
  scripts: {
@@ -317,7 +324,7 @@ describe('Test Shadow Handler', () => {
317
324
  };
318
325
  jest.mocked(readAppCfgFile).mockResolvedValue(testAppCfg);
319
326
 
320
- await shadowHandler.publishAppState(projectId1);
327
+ await shadowHandler.publishAppConfig(projectId1);
321
328
  expect(jest.mocked(readAppCfgFile)).toBeCalledWith({ projectId1 });
322
329
  const packet = {
323
330
  state: {
@@ -343,10 +350,13 @@ describe('Test Shadow Handler', () => {
343
350
  );
344
351
  });
345
352
 
346
- test('delete project shadow', async () => {
347
- shadowHandler.deleteProjectShadow(projectId1);
353
+ test('clear project shadow', async () => {
354
+ shadowHandler.clearAppConfig(projectId1);
348
355
  const packet = {
349
356
  state: {
357
+ desired: {
358
+ [projectId1]: null
359
+ },
350
360
  reported: {
351
361
  [projectId1]: null
352
362
  }
@@ -9,10 +9,13 @@ import { AppConfigModels, getAppCfgModelsDiff } from './shadow';
9
9
 
10
10
  export interface ShadowTopics {
11
11
  projects: {
12
- update: string;
13
12
  get: string;
14
- updateDelta: string;
15
13
  getAccepted: string;
14
+ getRejected: string;
15
+ update: string;
16
+ updateDelta: string;
17
+ updateAccepted: string;
18
+ updateRejected: string;
16
19
  delete: string;
17
20
  };
18
21
  }
@@ -35,10 +38,13 @@ export class ShadowHandler {
35
38
  this.shadowPrefix = `$aws/things/${this.clientId}/shadow/name/`;
36
39
  this.shadowTopics = {
37
40
  projects: {
38
- update: `${this.shadowPrefix}projects/update`,
39
41
  get: `${this.shadowPrefix}projects/get`,
40
- updateDelta: `${this.shadowPrefix}projects/update/delta`,
41
42
  getAccepted: `${this.shadowPrefix}projects/get/accepted`,
43
+ getRejected: `${this.shadowPrefix}projects/get/rejected`,
44
+ update: `${this.shadowPrefix}projects/update`,
45
+ updateDelta: `${this.shadowPrefix}projects/update/delta`,
46
+ updateAccepted: `${this.shadowPrefix}projects/update/accepted`,
47
+ updateRejected: `${this.shadowPrefix}projects/update/rejected`,
42
48
  delete: `${this.shadowPrefix}projects/delete`
43
49
  }
44
50
  };
@@ -92,16 +98,18 @@ export class ShadowHandler {
92
98
 
93
99
  public async handleShadowTopic({
94
100
  topic,
95
- payload
101
+ payload,
102
+ clientToken
96
103
  }: {
97
104
  topic: string;
98
105
  payload: any;
106
+ clientToken: string;
99
107
  }): Promise<AppConfigUpdate[]> {
100
108
  // TODO: make use a function like the other topic getters
101
109
  const shadowName = topic.split('/')[5];
102
110
  switch (topic) {
103
111
  case this.shadowTopics.projects.updateDelta:
104
- if (payload.clientToken === this.clientId) {
112
+ if (clientToken === this.clientId) {
105
113
  logger.debug(
106
114
  `Ignoring message sent from self: ${JSON.stringify(
107
115
  { topic, payload },
@@ -133,7 +141,7 @@ export class ShadowHandler {
133
141
  return [];
134
142
  }
135
143
 
136
- public async publishAppState(projectId: string) {
144
+ public async publishAppConfig(projectId: string) {
137
145
  const appCfg = await readAppCfgFile({ projectId });
138
146
  const packet = {
139
147
  state: {
@@ -161,10 +169,15 @@ export class ShadowHandler {
161
169
  this.publisher.publish(topic, JSON.stringify(packet));
162
170
  }
163
171
 
164
- public deleteProjectShadow(projectId: string) {
172
+ public clearAppConfig(projectId: string) {
165
173
  const topic = this.shadowTopics.projects.update;
174
+ // TODO: We should actually send only desired and handle the delta
175
+ // to update reported
166
176
  const packet = {
167
177
  state: {
178
+ desired: {
179
+ [projectId]: null
180
+ },
168
181
  reported: {
169
182
  [projectId]: null
170
183
  }
@@ -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);
46
+ logger.error('Error parsing app config update: ', e.message);
47
47
  }
48
48
 
49
49
  return { scripts: newScripts, updatedModels, untouchedModels };
@@ -0,0 +1,46 @@
1
+ import {
2
+ CliLeaf,
3
+ CliStringArrayInput,
4
+ CliStringInput
5
+ } from '@alwaysai/alwayscli';
6
+ import { getAllEnvs, setEnv } from '../../application-control';
7
+ import { logger } from '../../util/logger';
8
+
9
+ export const getAllEnvsCliLeaf = CliLeaf({
10
+ name: 'get-all-envs',
11
+ description: 'Get environment variables for an application',
12
+ namedInputs: {
13
+ project: CliStringInput({
14
+ description: 'Project Id',
15
+ required: true
16
+ })
17
+ },
18
+ async action(_, opts) {
19
+ const { project } = opts;
20
+ logger.info(await getAllEnvs({ projectId: project }));
21
+ }
22
+ });
23
+
24
+ export const setEnvCliLeaf = CliLeaf({
25
+ name: 'set-env',
26
+ description: 'Set environment variables for a service',
27
+ positionalInput: CliStringArrayInput({
28
+ placeholder: '<NAME=VALUE> [<NAME=VALUE> ...]',
29
+ required: true
30
+ }),
31
+ namedInputs: {
32
+ project: CliStringInput({
33
+ description: 'Project Id',
34
+ required: true
35
+ }),
36
+ service: CliStringInput({
37
+ description:
38
+ 'The name of the docker-compose service to apply environment variable to',
39
+ required: false
40
+ })
41
+ },
42
+ async action(args, opts) {
43
+ const { project, service } = opts;
44
+ await setEnv({ projectId: project, vars: args, service });
45
+ }
46
+ });