@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.
- package/lib/application-control/config.d.ts +0 -1
- package/lib/application-control/config.d.ts.map +1 -1
- package/lib/application-control/config.js +15 -29
- package/lib/application-control/config.js.map +1 -1
- package/lib/application-control/environment-variables.d.ts +7 -3
- package/lib/application-control/environment-variables.d.ts.map +1 -1
- package/lib/application-control/environment-variables.js +71 -35
- package/lib/application-control/environment-variables.js.map +1 -1
- package/lib/application-control/environment-variables.test.d.ts +2 -0
- package/lib/application-control/environment-variables.test.d.ts.map +1 -0
- package/lib/application-control/environment-variables.test.js +163 -0
- package/lib/application-control/environment-variables.test.js.map +1 -0
- package/lib/application-control/index.d.ts +3 -3
- package/lib/application-control/index.d.ts.map +1 -1
- package/lib/application-control/index.js +1 -3
- package/lib/application-control/index.js.map +1 -1
- package/lib/application-control/models.d.ts +0 -1
- package/lib/application-control/models.d.ts.map +1 -1
- package/lib/application-control/models.js +12 -26
- package/lib/application-control/models.js.map +1 -1
- package/lib/application-control/status.d.ts +3 -0
- package/lib/application-control/status.d.ts.map +1 -1
- package/lib/application-control/status.js +19 -1
- package/lib/application-control/status.js.map +1 -1
- package/lib/application-control/utils.d.ts.map +1 -1
- package/lib/application-control/utils.js +2 -2
- package/lib/application-control/utils.js.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts +6 -3
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +205 -151
- package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.d.ts +3 -0
- package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
- package/lib/cloud-connection/live-updates-handler.js +23 -7
- package/lib/cloud-connection/live-updates-handler.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.test.d.ts +2 -0
- package/lib/cloud-connection/live-updates-handler.test.d.ts.map +1 -0
- package/lib/cloud-connection/live-updates-handler.test.js +57 -0
- package/lib/cloud-connection/live-updates-handler.test.js.map +1 -0
- package/lib/cloud-connection/passthrough-handler.d.ts.map +1 -1
- package/lib/cloud-connection/passthrough-handler.js +6 -3
- package/lib/cloud-connection/passthrough-handler.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.d.ts +11 -3
- package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
- package/lib/cloud-connection/shadow-handler.js +22 -7
- package/lib/cloud-connection/shadow-handler.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.test.js +313 -228
- 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/environment.d.ts +1 -0
- package/lib/environment.d.ts.map +1 -1
- package/lib/environment.js +2 -1
- package/lib/environment.js.map +1 -1
- package/lib/infrastructure/agent-config.d.ts +3 -1
- package/lib/infrastructure/agent-config.d.ts.map +1 -1
- package/lib/subcommands/app/env-vars.d.ts +1 -1
- package/lib/subcommands/app/env-vars.d.ts.map +1 -1
- package/lib/subcommands/app/env-vars.js +32 -5
- package/lib/subcommands/app/env-vars.js.map +1 -1
- package/lib/subcommands/app/index.d.ts.map +1 -1
- package/lib/subcommands/app/index.js +4 -1
- package/lib/subcommands/app/index.js.map +1 -1
- package/lib/subcommands/app/models.d.ts.map +1 -1
- package/lib/subcommands/app/models.js +6 -1
- package/lib/subcommands/app/models.js.map +1 -1
- package/lib/subcommands/app/shadow.d.ts +7 -0
- package/lib/subcommands/app/shadow.d.ts.map +1 -0
- package/lib/subcommands/app/shadow.js +48 -0
- package/lib/subcommands/app/shadow.js.map +1 -0
- package/lib/subcommands/app/version.js +2 -2
- package/lib/subcommands/app/version.js.map +1 -1
- package/lib/util/cloud-mode-ready.d.ts +2 -0
- package/lib/util/cloud-mode-ready.d.ts.map +1 -0
- package/lib/util/cloud-mode-ready.js +22 -0
- package/lib/util/cloud-mode-ready.js.map +1 -0
- package/package.json +1 -1
- package/readme.md +2 -2
- package/src/application-control/config.ts +30 -31
- package/src/application-control/environment-variables.test.ts +171 -0
- package/src/application-control/environment-variables.ts +102 -43
- package/src/application-control/index.ts +3 -9
- package/src/application-control/models.ts +14 -29
- package/src/application-control/status.ts +20 -0
- package/src/application-control/utils.ts +4 -2
- package/src/cloud-connection/device-agent-cloud-connection.ts +222 -153
- package/src/cloud-connection/live-updates-handler.test.ts +68 -0
- package/src/cloud-connection/live-updates-handler.ts +30 -7
- package/src/cloud-connection/passthrough-handler.ts +10 -3
- package/src/cloud-connection/shadow-handler.test.ts +329 -239
- package/src/cloud-connection/shadow-handler.ts +38 -12
- package/src/cloud-connection/shadow.ts +1 -1
- package/src/environment.ts +2 -0
- package/src/infrastructure/agent-config.ts +1 -1
- package/src/subcommands/app/env-vars.ts +38 -8
- package/src/subcommands/app/index.ts +4 -1
- package/src/subcommands/app/models.ts +10 -1
- package/src/subcommands/app/shadow.ts +48 -0
- package/src/subcommands/app/version.ts +2 -2
- 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<
|
|
58
|
-
const
|
|
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
|
-
|
|
94
|
+
shadowUpdate.appCfgUpdate = { newAppCfg, updatedModels };
|
|
85
95
|
} else {
|
|
86
|
-
|
|
96
|
+
shadowUpdate.appCfgUpdate = { newAppCfg };
|
|
87
97
|
}
|
|
88
98
|
} else {
|
|
89
|
-
logger.
|
|
90
|
-
`Ignoring shadow update for ${projectId} due to no
|
|
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
|
|
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<
|
|
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
|
|
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]: {
|
|
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(
|
|
46
|
+
logger.error(`Error parsing app config update: ${e.message}`);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
return { scripts: newScripts, updatedModels, untouchedModels };
|
package/src/environment.ts
CHANGED
|
@@ -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) {
|
|
@@ -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 {
|
|
7
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
}
|