@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.
- package/lib/application-control/config.d.ts.map +1 -1
- package/lib/application-control/config.js +4 -4
- package/lib/application-control/config.js.map +1 -1
- package/lib/application-control/install.d.ts.map +1 -1
- package/lib/application-control/install.js +1 -1
- package/lib/application-control/install.js.map +1 -1
- package/lib/application-control/models.js +1 -1
- package/lib/application-control/utils.d.ts.map +1 -1
- package/lib/application-control/utils.js +3 -3
- package/lib/application-control/utils.js.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +28 -15
- package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
- package/lib/cloud-connection/live-updates-handler.js +9 -8
- package/lib/cloud-connection/live-updates-handler.js.map +1 -1
- package/lib/cloud-connection/messages.js +1 -1
- package/lib/cloud-connection/messages.js.map +1 -1
- package/lib/cloud-connection/passthrough-handler.d.ts.map +1 -1
- package/lib/cloud-connection/passthrough-handler.js +0 -1
- package/lib/cloud-connection/passthrough-handler.js.map +1 -1
- package/lib/cloud-connection/publisher.d.ts.map +1 -1
- package/lib/cloud-connection/publisher.js +0 -1
- package/lib/cloud-connection/publisher.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.d.ts +9 -5
- package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
- package/lib/cloud-connection/shadow-handler.js +14 -6
- package/lib/cloud-connection/shadow-handler.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.test.js +24 -14
- package/lib/cloud-connection/shadow-handler.test.js.map +1 -1
- package/lib/cloud-connection/shadow.js +1 -1
- package/lib/cloud-connection/shadow.js.map +1 -1
- package/lib/subcommands/app/env-vars.d.ts +8 -0
- package/lib/subcommands/app/env-vars.d.ts.map +1 -0
- package/lib/subcommands/app/env-vars.js +43 -0
- package/lib/subcommands/app/env-vars.js.map +1 -0
- package/lib/subcommands/app/index.d.ts.map +1 -1
- package/lib/subcommands/app/index.js +20 -19
- package/lib/subcommands/app/index.js.map +1 -1
- package/lib/subcommands/app/models.d.ts +20 -0
- package/lib/subcommands/app/models.d.ts.map +1 -0
- package/lib/subcommands/app/models.js +114 -0
- package/lib/subcommands/app/models.js.map +1 -0
- package/lib/subcommands/app/status.d.ts +17 -0
- package/lib/subcommands/app/status.d.ts.map +1 -0
- package/lib/subcommands/app/status.js +103 -0
- package/lib/subcommands/app/status.js.map +1 -0
- package/lib/subcommands/app/version.d.ts +12 -0
- package/lib/subcommands/app/version.d.ts.map +1 -0
- package/lib/subcommands/app/version.js +103 -0
- package/lib/subcommands/app/version.js.map +1 -0
- package/lib/util/get-device-id.js +1 -1
- package/lib/util/get-device-id.js.map +1 -1
- package/lib/util/http-client.js +1 -1
- package/lib/util/http-client.js.map +1 -1
- package/package.json +1 -1
- package/src/application-control/config.ts +8 -4
- package/src/application-control/install.ts +3 -1
- package/src/application-control/models.ts +1 -1
- package/src/application-control/utils.ts +3 -4
- package/src/cloud-connection/device-agent-cloud-connection.ts +33 -21
- package/src/cloud-connection/live-updates-handler.ts +14 -13
- package/src/cloud-connection/messages.ts +1 -1
- package/src/cloud-connection/passthrough-handler.ts +0 -1
- package/src/cloud-connection/publisher.ts +0 -1
- package/src/cloud-connection/shadow-handler.test.ts +24 -14
- package/src/cloud-connection/shadow-handler.ts +21 -8
- package/src/cloud-connection/shadow.ts +1 -1
- package/src/subcommands/app/env-vars.ts +46 -0
- package/src/subcommands/app/index.ts +16 -17
- package/src/subcommands/app/models.ts +129 -0
- package/src/subcommands/app/status.ts +92 -0
- package/src/subcommands/app/version.ts +103 -0
- package/src/util/get-device-id.ts +1 -1
- package/src/util/http-client.ts +1 -1
- package/lib/subcommands/app/app.d.ts +0 -60
- package/lib/subcommands/app/app.d.ts.map +0 -1
- package/lib/subcommands/app/app.js +0 -370
- package/lib/subcommands/app/app.js.map +0 -1
- 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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"}
|
|
@@ -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,
|
|
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"}
|
package/lib/util/http-client.js
CHANGED
|
@@ -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;
|
|
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
|
@@ -23,9 +23,11 @@ export async function readAppCfgFile(props: {
|
|
|
23
23
|
}
|
|
24
24
|
try {
|
|
25
25
|
return appJson.read();
|
|
26
|
-
} catch (
|
|
26
|
+
} catch (e) {
|
|
27
27
|
throw new Error(
|
|
28
|
-
`Error reading app config for ${projectId}:\n${
|
|
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 (
|
|
47
|
+
} catch (e) {
|
|
46
48
|
throw new Error(
|
|
47
|
-
`Error writing app config for ${projectId}:\n${
|
|
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(
|
|
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 (
|
|
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=${
|
|
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.
|
|
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.
|
|
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
|
-
|
|
212
|
-
|
|
211
|
+
try {
|
|
212
|
+
await uninstallApp({ projectId });
|
|
213
|
+
} finally {
|
|
214
|
+
this.shadowHandler.clearAppConfig(projectId);
|
|
213
215
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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 (
|
|
465
|
-
logger.error(`Error parsing message: ${
|
|
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.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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().
|
|
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
|
|
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.
|
|
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('
|
|
347
|
-
shadowHandler.
|
|
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 (
|
|
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
|
|
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
|
|
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
|
+
});
|