@alwaysai/device-agent 1.3.0 → 1.3.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/environment-variables.d.ts +1 -0
- package/lib/application-control/environment-variables.d.ts.map +1 -1
- package/lib/application-control/environment-variables.js +22 -20
- package/lib/application-control/environment-variables.js.map +1 -1
- package/lib/application-control/environment-variables.test.js +37 -2
- package/lib/application-control/environment-variables.test.js.map +1 -1
- package/lib/application-control/install.js +1 -1
- package/lib/application-control/install.js.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts +5 -5
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +203 -178
- 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 +30 -25
- package/lib/cloud-connection/live-updates-handler.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.test.js +15 -0
- package/lib/cloud-connection/live-updates-handler.test.js.map +1 -1
- package/lib/cloud-connection/messages.d.ts +1 -3
- package/lib/cloud-connection/messages.d.ts.map +1 -1
- package/lib/cloud-connection/messages.js +1 -9
- package/lib/cloud-connection/messages.js.map +1 -1
- package/lib/cloud-connection/publisher.d.ts +1 -0
- package/lib/cloud-connection/publisher.d.ts.map +1 -1
- package/lib/cloud-connection/publisher.js +3 -0
- package/lib/cloud-connection/publisher.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.d.ts +10 -21
- package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
- package/lib/cloud-connection/shadow-handler.js +154 -100
- package/lib/cloud-connection/shadow-handler.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.test.js +140 -72
- package/lib/cloud-connection/shadow-handler.test.js.map +1 -1
- package/lib/cloud-connection/transaction-manager.d.ts +26 -6
- package/lib/cloud-connection/transaction-manager.d.ts.map +1 -1
- package/lib/cloud-connection/transaction-manager.js +103 -22
- package/lib/cloud-connection/transaction-manager.js.map +1 -1
- package/lib/cloud-connection/transaction-manager.test.js +179 -13
- package/lib/cloud-connection/transaction-manager.test.js.map +1 -1
- package/lib/device-control/device-control.d.ts +2 -2
- package/lib/device-control/device-control.d.ts.map +1 -1
- package/lib/device-control/device-control.js.map +1 -1
- package/lib/secure-tunneling/secure-tunneling.d.ts +105 -0
- package/lib/secure-tunneling/secure-tunneling.d.ts.map +1 -0
- package/lib/secure-tunneling/secure-tunneling.js +435 -0
- package/lib/secure-tunneling/secure-tunneling.js.map +1 -0
- package/lib/secure-tunneling/secure-tunneling.test.d.ts +2 -0
- package/lib/secure-tunneling/secure-tunneling.test.d.ts.map +1 -0
- package/lib/secure-tunneling/secure-tunneling.test.js +1070 -0
- package/lib/secure-tunneling/secure-tunneling.test.js.map +1 -0
- package/lib/secure-tunneling/spawner-detached.d.ts +6 -0
- package/lib/secure-tunneling/spawner-detached.d.ts.map +1 -0
- package/lib/secure-tunneling/spawner-detached.js +107 -0
- package/lib/secure-tunneling/spawner-detached.js.map +1 -0
- package/lib/subcommands/app/analytics.d.ts +10 -0
- package/lib/subcommands/app/analytics.d.ts.map +1 -0
- package/lib/subcommands/app/analytics.js +79 -0
- package/lib/subcommands/app/analytics.js.map +1 -0
- package/lib/subcommands/app/env-vars.d.ts.map +1 -1
- package/lib/subcommands/app/env-vars.js +11 -16
- 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 +3 -1
- package/lib/subcommands/app/index.js.map +1 -1
- package/lib/subcommands/app/models.d.ts +0 -5
- package/lib/subcommands/app/models.d.ts.map +1 -1
- package/lib/subcommands/app/models.js +16 -56
- package/lib/subcommands/app/models.js.map +1 -1
- package/lib/subcommands/app/status.d.ts +1 -0
- package/lib/subcommands/app/status.d.ts.map +1 -1
- package/lib/subcommands/app/status.js +14 -3
- package/lib/subcommands/app/status.js.map +1 -1
- package/lib/subcommands/app/version.d.ts +2 -1
- package/lib/subcommands/app/version.d.ts.map +1 -1
- package/lib/subcommands/app/version.js +16 -3
- package/lib/subcommands/app/version.js.map +1 -1
- package/lib/util/cloud-mode-ready.d.ts +1 -0
- package/lib/util/cloud-mode-ready.d.ts.map +1 -1
- package/lib/util/cloud-mode-ready.js +36 -1
- package/lib/util/cloud-mode-ready.js.map +1 -1
- package/lib/util/parsing.d.ts +2 -0
- package/lib/util/parsing.d.ts.map +1 -0
- package/lib/util/parsing.js +17 -0
- package/lib/util/parsing.js.map +1 -0
- package/package.json +4 -6
- package/readme.md +146 -92
- package/src/application-control/environment-variables.test.ts +43 -3
- package/src/application-control/environment-variables.ts +29 -19
- package/src/application-control/install.ts +1 -1
- package/src/cloud-connection/device-agent-cloud-connection.ts +272 -247
- package/src/cloud-connection/live-updates-handler.test.ts +20 -0
- package/src/cloud-connection/live-updates-handler.ts +45 -52
- package/src/cloud-connection/messages.ts +1 -14
- package/src/cloud-connection/publisher.ts +4 -0
- package/src/cloud-connection/shadow-handler.test.ts +150 -73
- package/src/cloud-connection/shadow-handler.ts +247 -126
- package/src/cloud-connection/transaction-manager.test.ts +193 -18
- package/src/cloud-connection/transaction-manager.ts +174 -26
- package/src/device-control/device-control.ts +3 -3
- package/src/secure-tunneling/secure-tunneling.test.ts +1239 -0
- package/src/secure-tunneling/secure-tunneling.ts +606 -0
- package/src/secure-tunneling/spawner-detached.ts +123 -0
- package/src/subcommands/app/analytics.ts +102 -0
- package/src/subcommands/app/env-vars.ts +18 -16
- package/src/subcommands/app/index.ts +4 -3
- package/src/subcommands/app/models.ts +25 -57
- package/src/subcommands/app/status.ts +20 -3
- package/src/subcommands/app/version.ts +19 -4
- package/src/util/cloud-mode-ready.ts +36 -0
- package/src/util/parsing.ts +11 -0
- package/lib/cloud-connection/cmd-status.d.ts +0 -8
- package/lib/cloud-connection/cmd-status.d.ts.map +0 -1
- package/lib/cloud-connection/cmd-status.js +0 -62
- package/lib/cloud-connection/cmd-status.js.map +0 -1
- package/lib/cloud-connection/message-builder.d.ts +0 -7
- package/lib/cloud-connection/message-builder.d.ts.map +0 -1
- package/lib/cloud-connection/message-builder.js +0 -63
- package/lib/cloud-connection/message-builder.js.map +0 -1
- package/lib/secure-tunneling/index.d.ts +0 -5
- package/lib/secure-tunneling/index.d.ts.map +0 -1
- package/lib/secure-tunneling/index.js +0 -64
- package/lib/secure-tunneling/index.js.map +0 -1
- package/src/cloud-connection/cmd-status.ts +0 -71
- package/src/cloud-connection/message-builder.ts +0 -117
- package/src/secure-tunneling/index.ts +0 -74
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { ChildProcess, spawn } from 'child_process';
|
|
2
|
+
import { JsSpawner } from 'alwaysai/lib/util';
|
|
3
|
+
import { logger } from '../util/logger';
|
|
4
|
+
|
|
5
|
+
const DEFAULT_TIMEOUT_MS = 5000;
|
|
6
|
+
|
|
7
|
+
export async function runDetachedProcess(
|
|
8
|
+
command: string,
|
|
9
|
+
args: string[],
|
|
10
|
+
timeoutMs = DEFAULT_TIMEOUT_MS
|
|
11
|
+
): Promise<ChildProcess> {
|
|
12
|
+
logger.debug('-> runDetachedProcess');
|
|
13
|
+
|
|
14
|
+
let isSpawned = false; // Flag to indicate if process has been successfully spawned
|
|
15
|
+
|
|
16
|
+
const child = spawn(command, args, {
|
|
17
|
+
detached: true,
|
|
18
|
+
stdio: 'ignore'
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
child.unref();
|
|
22
|
+
|
|
23
|
+
// Create a promise that resolves after the specified timeout
|
|
24
|
+
const timeoutPromise = new Promise<ChildProcess>((resolve, reject) => {
|
|
25
|
+
setTimeout(() => {
|
|
26
|
+
if (!isSpawned) {
|
|
27
|
+
logger.error('Timeout spawning off detached process');
|
|
28
|
+
resolve(child); // Resolve the promise with the child process even if timeout occurs
|
|
29
|
+
}
|
|
30
|
+
}, timeoutMs);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Handle 'spawn' event
|
|
34
|
+
child.once('spawn', () => {
|
|
35
|
+
isSpawned = true;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Race between the actual process spawning and the timeout
|
|
39
|
+
await Promise.race([
|
|
40
|
+
new Promise<ChildProcess>((resolve) => {
|
|
41
|
+
if (isSpawned) {
|
|
42
|
+
resolve(child); // Resolve immediately if process has already been spawned
|
|
43
|
+
} else {
|
|
44
|
+
child.once('spawn', () => resolve(child));
|
|
45
|
+
}
|
|
46
|
+
}),
|
|
47
|
+
timeoutPromise
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
logger.debug('<- runDetachedProcess');
|
|
51
|
+
return child;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function killDetachedProcess(
|
|
55
|
+
detachedProcess: ChildProcess,
|
|
56
|
+
processName?: string[],
|
|
57
|
+
timeoutMs = DEFAULT_TIMEOUT_MS
|
|
58
|
+
): Promise<void> {
|
|
59
|
+
logger.debug('-> killDetachedProcess');
|
|
60
|
+
return new Promise<void>((resolve, reject) => {
|
|
61
|
+
const timeout = setTimeout(() => {
|
|
62
|
+
if (processName) {
|
|
63
|
+
killDetachedProcessByName(processName, timeoutMs)
|
|
64
|
+
.then(() => resolve())
|
|
65
|
+
.catch((err) => reject(err));
|
|
66
|
+
} else {
|
|
67
|
+
reject(new Error('killDetachedProcess timeout'));
|
|
68
|
+
}
|
|
69
|
+
}, timeoutMs);
|
|
70
|
+
|
|
71
|
+
detachedProcess.on('exit', (code) => {
|
|
72
|
+
clearTimeout(timeout);
|
|
73
|
+
logger.debug(`Process exited with code ${code}`);
|
|
74
|
+
resolve();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
detachedProcess.on('error', (error) => {
|
|
78
|
+
clearTimeout(timeout);
|
|
79
|
+
if (processName) {
|
|
80
|
+
killDetachedProcessByName(processName, timeoutMs)
|
|
81
|
+
.then(() => resolve())
|
|
82
|
+
.catch((err) => reject(err));
|
|
83
|
+
} else {
|
|
84
|
+
logger.error('killDetachedProcess error occurred:', error);
|
|
85
|
+
reject(error);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
detachedProcess.kill('SIGTERM');
|
|
90
|
+
logger.debug('<- killDetachedProcess');
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export async function killDetachedProcessByName(
|
|
95
|
+
processName: string[],
|
|
96
|
+
timeoutMs = DEFAULT_TIMEOUT_MS
|
|
97
|
+
): Promise<void> {
|
|
98
|
+
logger.debug('-> killDetachedProcessByName');
|
|
99
|
+
return new Promise<void>((resolve, reject) => {
|
|
100
|
+
const timeout = setTimeout(() => {
|
|
101
|
+
logger.error('Timeout waiting for process kill by name');
|
|
102
|
+
reject(new Error('killDetachedProcessByName timeout'));
|
|
103
|
+
}, timeoutMs);
|
|
104
|
+
|
|
105
|
+
JsSpawner()
|
|
106
|
+
.run({
|
|
107
|
+
exe: 'pkill',
|
|
108
|
+
args: processName
|
|
109
|
+
})
|
|
110
|
+
.then(() => {
|
|
111
|
+
clearTimeout(timeout);
|
|
112
|
+
resolve();
|
|
113
|
+
})
|
|
114
|
+
.catch((error) => {
|
|
115
|
+
clearTimeout(timeout);
|
|
116
|
+
logger.error('killDetachedProcessByName error occurred:', error);
|
|
117
|
+
reject(error);
|
|
118
|
+
})
|
|
119
|
+
.finally(() => {
|
|
120
|
+
logger.debug('<- killDetachedProcessByName');
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CliFlagInput,
|
|
3
|
+
CliLeaf,
|
|
4
|
+
CliNumberInput,
|
|
5
|
+
CliStringInput
|
|
6
|
+
} from '@alwaysai/alwayscli';
|
|
7
|
+
import { readAppCfgFile } from '../../application-control';
|
|
8
|
+
import { DeviceAgentCloudConnection } from '../../cloud-connection/device-agent-cloud-connection';
|
|
9
|
+
import sleep from '../../util/sleep';
|
|
10
|
+
import { logger } from '../../util/logger';
|
|
11
|
+
import { assign, merge } from 'lodash';
|
|
12
|
+
import {
|
|
13
|
+
buildUpdateProjectShadowMessage,
|
|
14
|
+
getShadowTopic
|
|
15
|
+
} from '@alwaysai/device-agent-schemas';
|
|
16
|
+
|
|
17
|
+
export const getAnalyticsCfgCliLeaf = CliLeaf({
|
|
18
|
+
name: 'get-analytics-cfg',
|
|
19
|
+
description: 'Get analytics configuration for an application',
|
|
20
|
+
namedInputs: {
|
|
21
|
+
project: CliStringInput({
|
|
22
|
+
description: 'Project Id',
|
|
23
|
+
required: true
|
|
24
|
+
})
|
|
25
|
+
},
|
|
26
|
+
async action(_, opts) {
|
|
27
|
+
const { project } = opts;
|
|
28
|
+
const appCfg = await readAppCfgFile({ projectId: project });
|
|
29
|
+
if (appCfg.analytics !== undefined) {
|
|
30
|
+
console.log(JSON.stringify(appCfg.analytics, null, 2));
|
|
31
|
+
} else {
|
|
32
|
+
console.log('No analytics configuration for app!');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export const setAnalyticsCfgCliLeaf = CliLeaf({
|
|
38
|
+
name: 'set-analytics-cfg',
|
|
39
|
+
description:
|
|
40
|
+
'Set analytics configuration for an application. Note that this resets the config so all desired options must be set',
|
|
41
|
+
namedInputs: {
|
|
42
|
+
project: CliStringInput({
|
|
43
|
+
description: 'Project Id',
|
|
44
|
+
required: true
|
|
45
|
+
}),
|
|
46
|
+
'enable-cloud-publish': CliFlagInput({
|
|
47
|
+
description: 'Enable publishing analytics to cloud'
|
|
48
|
+
}),
|
|
49
|
+
'enable-file-publish': CliFlagInput({
|
|
50
|
+
description: 'Enable publishing analytics to file'
|
|
51
|
+
}),
|
|
52
|
+
'file-size-bytes': CliNumberInput({
|
|
53
|
+
description: 'Set the max file size in bytes for analytics file writing',
|
|
54
|
+
required: false
|
|
55
|
+
})
|
|
56
|
+
},
|
|
57
|
+
async action(
|
|
58
|
+
_,
|
|
59
|
+
{
|
|
60
|
+
project,
|
|
61
|
+
'enable-cloud-publish': enableCLoudPublish,
|
|
62
|
+
'enable-file-publish': enableFilePublish,
|
|
63
|
+
'file-size-bytes': fileSizeBytes
|
|
64
|
+
}
|
|
65
|
+
) {
|
|
66
|
+
const deviceAgent = new DeviceAgentCloudConnection();
|
|
67
|
+
await deviceAgent.setupHandlers();
|
|
68
|
+
|
|
69
|
+
const newAppCfg = {
|
|
70
|
+
analytics: {
|
|
71
|
+
enable_cloud_publish: enableCLoudPublish,
|
|
72
|
+
enable_file_publish: enableFilePublish,
|
|
73
|
+
file_size_bytes: fileSizeBytes
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const existingAppCfg = await readAppCfgFile({ projectId: project });
|
|
77
|
+
const appCfg = assign(existingAppCfg, merge(existingAppCfg, newAppCfg));
|
|
78
|
+
|
|
79
|
+
// Update the shadow as a client
|
|
80
|
+
const toDesire = {
|
|
81
|
+
[project]: {
|
|
82
|
+
appConfig: JSON.stringify(appCfg) // Pack app config as string as dictated by schema
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
deviceAgent.publisher.publish(
|
|
86
|
+
getShadowTopic(deviceAgent.getClientId(), 'projects', 'update'),
|
|
87
|
+
JSON.stringify(
|
|
88
|
+
buildUpdateProjectShadowMessage({
|
|
89
|
+
clientId: 'client',
|
|
90
|
+
desired: toDesire
|
|
91
|
+
})
|
|
92
|
+
)
|
|
93
|
+
);
|
|
94
|
+
// Sleep for extra time to ensure time for shadow response
|
|
95
|
+
await sleep(10000);
|
|
96
|
+
|
|
97
|
+
while (deviceAgent.isCmdInProgress(project)) {
|
|
98
|
+
await sleep(1000);
|
|
99
|
+
}
|
|
100
|
+
await deviceAgent.stop();
|
|
101
|
+
}
|
|
102
|
+
});
|
|
@@ -8,6 +8,10 @@ import { EnvVars, getAllEnvs } from '../../application-control';
|
|
|
8
8
|
import { DeviceAgentCloudConnection } from '../../cloud-connection/device-agent-cloud-connection';
|
|
9
9
|
import sleep from '../../util/sleep';
|
|
10
10
|
import { logger } from '../../util/logger';
|
|
11
|
+
import {
|
|
12
|
+
buildUpdateProjectShadowMessage,
|
|
13
|
+
getShadowTopic
|
|
14
|
+
} from '@alwaysai/device-agent-schemas';
|
|
11
15
|
|
|
12
16
|
export const getAllEnvsCliLeaf = CliLeaf({
|
|
13
17
|
name: 'get-all-envs',
|
|
@@ -43,7 +47,7 @@ export const setEnvCliLeaf = CliLeaf({
|
|
|
43
47
|
})
|
|
44
48
|
},
|
|
45
49
|
async action(args, opts) {
|
|
46
|
-
const { project, service } = opts;
|
|
50
|
+
const { project: projectId, service } = opts;
|
|
47
51
|
const envVars: EnvVars = { [service]: {} };
|
|
48
52
|
args.forEach((arg: string) => {
|
|
49
53
|
const nameVal = arg.split('=');
|
|
@@ -56,26 +60,24 @@ export const setEnvCliLeaf = CliLeaf({
|
|
|
56
60
|
const deviceAgent = new DeviceAgentCloudConnection();
|
|
57
61
|
await deviceAgent.setupHandlers();
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
desired: {
|
|
64
|
-
[project]: {
|
|
65
|
-
envVars
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
clientToken: 'client'
|
|
63
|
+
const toDesire = {
|
|
64
|
+
[projectId]: {
|
|
65
|
+
envVars
|
|
66
|
+
}
|
|
70
67
|
};
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
deviceAgent.publisher.publish(
|
|
69
|
+
getShadowTopic(deviceAgent.getClientId(), 'projects', 'update'),
|
|
70
|
+
JSON.stringify(
|
|
71
|
+
buildUpdateProjectShadowMessage({
|
|
72
|
+
clientId: 'client',
|
|
73
|
+
desired: toDesire
|
|
74
|
+
})
|
|
75
|
+
)
|
|
73
76
|
);
|
|
74
|
-
deviceAgent.publisher.publish(topic, JSON.stringify(packet));
|
|
75
77
|
// Sleep for extra time to ensure time for shadow response
|
|
76
78
|
await sleep(10000);
|
|
77
79
|
|
|
78
|
-
while (deviceAgent.isCmdInProgress(
|
|
80
|
+
while (deviceAgent.isCmdInProgress(projectId)) {
|
|
79
81
|
await sleep(1000);
|
|
80
82
|
}
|
|
81
83
|
await deviceAgent.stop();
|
|
@@ -5,8 +5,7 @@ import {
|
|
|
5
5
|
addModelCliLeaf,
|
|
6
6
|
removeModelCliLeaf,
|
|
7
7
|
replaceModelsCliLeaf,
|
|
8
|
-
updateModelsCliLeaf
|
|
9
|
-
installModelCliLeaf
|
|
8
|
+
updateModelsCliLeaf
|
|
10
9
|
} from './models';
|
|
11
10
|
import {
|
|
12
11
|
getAppStatusCliLeaf,
|
|
@@ -22,6 +21,7 @@ import {
|
|
|
22
21
|
rollbackAppCliLeaf
|
|
23
22
|
} from './version';
|
|
24
23
|
import { getShadowCliLeaf, updateShadowCliLeaf } from './shadow';
|
|
24
|
+
import { getAnalyticsCfgCliLeaf, setAnalyticsCfgCliLeaf } from './analytics';
|
|
25
25
|
|
|
26
26
|
export const appCliBranch = CliBranch({
|
|
27
27
|
name: 'app',
|
|
@@ -40,10 +40,11 @@ export const appCliBranch = CliBranch({
|
|
|
40
40
|
addModelCliLeaf,
|
|
41
41
|
removeModelCliLeaf,
|
|
42
42
|
replaceModelsCliLeaf,
|
|
43
|
-
installModelCliLeaf,
|
|
44
43
|
updateModelsCliLeaf,
|
|
45
44
|
getAllEnvsCliLeaf,
|
|
46
45
|
setEnvCliLeaf,
|
|
46
|
+
getAnalyticsCfgCliLeaf,
|
|
47
|
+
setAnalyticsCfgCliLeaf,
|
|
47
48
|
getShadowCliLeaf,
|
|
48
49
|
updateShadowCliLeaf
|
|
49
50
|
]
|
|
@@ -13,6 +13,12 @@ import {
|
|
|
13
13
|
} from '../../application-control';
|
|
14
14
|
import { DeviceAgentCloudConnection } from '../../cloud-connection/device-agent-cloud-connection';
|
|
15
15
|
import sleep from '../../util/sleep';
|
|
16
|
+
import { logger } from '../../util/logger';
|
|
17
|
+
import {
|
|
18
|
+
buildUpdateProjectShadowMessage,
|
|
19
|
+
getShadowTopic,
|
|
20
|
+
validateShadowProjectsUpdateAll
|
|
21
|
+
} from '@alwaysai/device-agent-schemas';
|
|
16
22
|
|
|
17
23
|
export const showAppModelsCliLeaf = CliLeaf({
|
|
18
24
|
name: 'show-models',
|
|
@@ -48,71 +54,33 @@ export const addModelCliLeaf = CliLeaf({
|
|
|
48
54
|
})
|
|
49
55
|
},
|
|
50
56
|
async action(_, opts) {
|
|
51
|
-
const { project, model, version } = opts;
|
|
57
|
+
const { project: projectId, model, version } = opts;
|
|
52
58
|
const deviceAgent = new DeviceAgentCloudConnection();
|
|
53
59
|
await deviceAgent.setupHandlers();
|
|
54
60
|
|
|
55
|
-
|
|
61
|
+
// Update the shadow as a client
|
|
56
62
|
|
|
57
|
-
const newAppCfg = await readAppCfgFile({ projectId:
|
|
63
|
+
const newAppCfg = await readAppCfgFile({ projectId: projectId });
|
|
58
64
|
newAppCfg.models[model] = Number(version);
|
|
59
65
|
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
[project]: {
|
|
65
|
-
appConfig: JSON.stringify(newAppCfg)
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
clientToken: 'not-self'
|
|
66
|
+
const toDesire = {
|
|
67
|
+
[projectId]: {
|
|
68
|
+
appConfig: JSON.stringify(newAppCfg)
|
|
69
|
+
}
|
|
69
70
|
};
|
|
71
|
+
deviceAgent.publisher.publish(
|
|
72
|
+
getShadowTopic(deviceAgent.getClientId(), 'projects', 'update'),
|
|
73
|
+
JSON.stringify(
|
|
74
|
+
buildUpdateProjectShadowMessage({
|
|
75
|
+
clientId: 'client',
|
|
76
|
+
desired: toDesire
|
|
77
|
+
})
|
|
78
|
+
)
|
|
79
|
+
);
|
|
80
|
+
// Sleep for extra time to ensure time for shadow response
|
|
81
|
+
await sleep(10000);
|
|
70
82
|
|
|
71
|
-
|
|
72
|
-
while (deviceAgent.isCmdInProgress(project)) {
|
|
73
|
-
await sleep(1000);
|
|
74
|
-
}
|
|
75
|
-
await deviceAgent.stop();
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
export const installModelCliLeaf = CliLeaf({
|
|
80
|
-
name: 'install-model',
|
|
81
|
-
description: 'Install an alwaysAI model to a project',
|
|
82
|
-
namedInputs: {
|
|
83
|
-
project: CliStringInput({
|
|
84
|
-
description: 'Project ID',
|
|
85
|
-
required: true
|
|
86
|
-
}),
|
|
87
|
-
modelName: CliStringInput({
|
|
88
|
-
description: 'Model Name',
|
|
89
|
-
required: true
|
|
90
|
-
}),
|
|
91
|
-
modelVersion: CliNumberInput({
|
|
92
|
-
description: 'Model Version',
|
|
93
|
-
required: true
|
|
94
|
-
})
|
|
95
|
-
},
|
|
96
|
-
async action(_, opts) {
|
|
97
|
-
const { project, modelName, modelVersion } = opts;
|
|
98
|
-
const deviceAgent = new DeviceAgentCloudConnection();
|
|
99
|
-
await deviceAgent.setupHandlers();
|
|
100
|
-
const topic = deviceAgent.getShadowTopics().projects.getAccepted;
|
|
101
|
-
const newAppCfg = await readAppCfgFile({ projectId: project });
|
|
102
|
-
newAppCfg['models'][modelName] = modelVersion;
|
|
103
|
-
|
|
104
|
-
const message = {
|
|
105
|
-
state: {
|
|
106
|
-
delta: {
|
|
107
|
-
[project]: {
|
|
108
|
-
appConfig: JSON.stringify(newAppCfg)
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
},
|
|
112
|
-
clientToken: deviceAgent.getClientId()
|
|
113
|
-
};
|
|
114
|
-
await deviceAgent.handleMessage(topic, message);
|
|
115
|
-
while (deviceAgent.isCmdInProgress(project)) {
|
|
83
|
+
while (deviceAgent.isCmdInProgress(projectId)) {
|
|
116
84
|
await sleep(1000);
|
|
117
85
|
}
|
|
118
86
|
await deviceAgent.stop();
|
|
@@ -32,13 +32,30 @@ export const startAppCliLeaf = CliLeaf({
|
|
|
32
32
|
description: 'Project ID',
|
|
33
33
|
required: true
|
|
34
34
|
}),
|
|
35
|
-
|
|
35
|
+
'docker-login-token': CliStringInput({
|
|
36
36
|
description: 'Docker login token'
|
|
37
|
+
}),
|
|
38
|
+
dockerLoginToken: CliStringInput({
|
|
39
|
+
description: 'Docker login token',
|
|
40
|
+
hidden: true
|
|
37
41
|
})
|
|
38
42
|
},
|
|
39
43
|
async action(_, opts) {
|
|
40
|
-
const {
|
|
41
|
-
|
|
44
|
+
const {
|
|
45
|
+
project,
|
|
46
|
+
dockerLoginToken,
|
|
47
|
+
'docker-login-token': dockerLoginTokenNew
|
|
48
|
+
} = opts;
|
|
49
|
+
if (dockerLoginToken) {
|
|
50
|
+
logger.warn(
|
|
51
|
+
`--dockerLoginToken is deprecated and will be removed in a future release. Please switch to --docker-login-token`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
const dockerLoginTokenResolved = dockerLoginTokenNew || dockerLoginToken;
|
|
55
|
+
await startApp({
|
|
56
|
+
projectId: project,
|
|
57
|
+
dockerLoginToken: dockerLoginTokenResolved
|
|
58
|
+
});
|
|
42
59
|
}
|
|
43
60
|
});
|
|
44
61
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CliLeaf, CliStringInput } from '@alwaysai/alwayscli';
|
|
1
|
+
import { CliLeaf, CliStringInput, CliTerseError } from '@alwaysai/alwayscli';
|
|
2
2
|
import {
|
|
3
3
|
AppVersionControlMessage,
|
|
4
4
|
generateTxId,
|
|
@@ -8,6 +8,7 @@ import { rollbackApp } from '../../application-control';
|
|
|
8
8
|
import { DeviceAgentCloudConnection } from '../../cloud-connection/device-agent-cloud-connection';
|
|
9
9
|
import { AgentConfigFile } from '../../infrastructure/agent-config';
|
|
10
10
|
import sleep from '../../util/sleep';
|
|
11
|
+
import { logger } from '../../util/logger';
|
|
11
12
|
|
|
12
13
|
export const listAppsCliLeaf = CliLeaf({
|
|
13
14
|
name: 'list',
|
|
@@ -27,13 +28,27 @@ export const installAppCliLeaf = CliLeaf({
|
|
|
27
28
|
description: 'Project ID',
|
|
28
29
|
required: true
|
|
29
30
|
}),
|
|
31
|
+
'release-hash': CliStringInput({
|
|
32
|
+
description: 'Release Hash',
|
|
33
|
+
required: false
|
|
34
|
+
}),
|
|
30
35
|
releaseHash: CliStringInput({
|
|
31
36
|
description: 'Release Hash',
|
|
32
|
-
required:
|
|
37
|
+
required: false,
|
|
38
|
+
hidden: true
|
|
33
39
|
})
|
|
34
40
|
},
|
|
35
41
|
async action(_, opts) {
|
|
36
|
-
const { project, releaseHash } = opts;
|
|
42
|
+
const { project, releaseHash, 'release-hash': releaseHashNew } = opts;
|
|
43
|
+
if (releaseHash) {
|
|
44
|
+
logger.warn(
|
|
45
|
+
`--releaseHash is deprecated and will be removed in a future release. Please switch to --release-hash`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const releaseHashResolved = releaseHashNew || releaseHash;
|
|
49
|
+
if (releaseHashResolved === undefined) {
|
|
50
|
+
throw new CliTerseError('--release-hash flag is required!');
|
|
51
|
+
}
|
|
37
52
|
const deviceAgent = new DeviceAgentCloudConnection();
|
|
38
53
|
await deviceAgent.setupHandlers();
|
|
39
54
|
const topic = deviceAgent.getToDeviceTopic();
|
|
@@ -45,7 +60,7 @@ export const installAppCliLeaf = CliLeaf({
|
|
|
45
60
|
payload: {
|
|
46
61
|
baseCommand: keyMirrors.appVersionControl.install,
|
|
47
62
|
projectId: project,
|
|
48
|
-
appReleaseHash:
|
|
63
|
+
appReleaseHash: releaseHashResolved
|
|
49
64
|
}
|
|
50
65
|
};
|
|
51
66
|
await deviceAgent.handleMessage(topic, message);
|
|
@@ -5,6 +5,42 @@ import {
|
|
|
5
5
|
DEVICE_CERTIFICATE_FILE_PATH
|
|
6
6
|
} from '../util/directories';
|
|
7
7
|
|
|
8
|
+
const VALID_AWS_REGIONS = [
|
|
9
|
+
// 'af-south-1', // Africa (Cape Town)
|
|
10
|
+
// 'ap-east-1', // Asia Pacific (Hong Kong)
|
|
11
|
+
// 'ap-south-2', // Asia Pacific (Hyderabad)
|
|
12
|
+
// 'ap-northeast-1', // Asia Pacific (Tokyo)
|
|
13
|
+
// 'ap-northeast-2', // Asia Pacific (Seoul)
|
|
14
|
+
// 'ap-northeast-3', // Asia Pacific (Osaka)
|
|
15
|
+
// 'ap-south-1', // Asia Pacific (Mumbai)
|
|
16
|
+
// 'ap-southeast-1', // Asia Pacific (Singapore)
|
|
17
|
+
// 'ap-southeast-2', // Asia Pacific (Sydney)
|
|
18
|
+
// 'ap-southeast-3', // Asia Pacific (Jakarta)
|
|
19
|
+
// 'ap-southeast-4', // Asia Pacific (Melbourne)
|
|
20
|
+
// 'ca-central-1', // Canada (Central)
|
|
21
|
+
// 'ca-west-1', // Canada West (Calgary)
|
|
22
|
+
// 'eu-west-2', // Europe (London)
|
|
23
|
+
// 'eu-west-3', // Europe (Paris)
|
|
24
|
+
// 'eu-central-1', // Europe (Frankfurt)
|
|
25
|
+
// 'eu-central-2', // Europe (Zurich)
|
|
26
|
+
// 'eu-north-1', // Europe (Stockholm)
|
|
27
|
+
// 'eu-south-1', // Europe (Milan)
|
|
28
|
+
// 'eu-south-2', // Europe (Spain)
|
|
29
|
+
// 'eu-west-1', // Europe (Ireland)
|
|
30
|
+
// 'il-central-1', // Israel (Tel Aviv)
|
|
31
|
+
// 'me-south-1', // Middle East (Bahrain)
|
|
32
|
+
// 'me-central-1', // Middle East (UAE)
|
|
33
|
+
// 'sa-east-1' // South America (São Paulo)
|
|
34
|
+
// 'us-east-2', // US East (Ohio)
|
|
35
|
+
// 'us-east-1', // US East (Virginia)
|
|
36
|
+
// 'us-west-1', // US West (N. California)
|
|
37
|
+
'us-west-2' // US West (Oregon)
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
export function isValidAwsRegion(region: string): boolean {
|
|
41
|
+
return VALID_AWS_REGIONS.includes(region);
|
|
42
|
+
}
|
|
43
|
+
|
|
8
44
|
export function cloudModeReady() {
|
|
9
45
|
let ready = true;
|
|
10
46
|
const requiredFiles = [
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function replaceFalseyWithNull(object: object, recurse?: boolean) {
|
|
2
|
+
if (typeof object === 'string' || object instanceof String) return;
|
|
3
|
+
|
|
4
|
+
object &&
|
|
5
|
+
Object.keys(object).forEach((key: string) => {
|
|
6
|
+
if (recurse) {
|
|
7
|
+
replaceFalseyWithNull(object[key], recurse);
|
|
8
|
+
}
|
|
9
|
+
if (!object[key]) object[key] = null;
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cmd-status.d.ts","sourceRoot":"","sources":["../../src/cloud-connection/cmd-status.ts"],"names":[],"mappings":"AA0BA,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,IAAI,CAA0C;IAEzC,KAAK,CAAC,SAAS,EAAE,MAAM;IAevB,IAAI,CAAC,SAAS,EAAE,MAAM;IAW5B,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAO3C,gBAAgB;CAQxB"}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CmdStatusManager = void 0;
|
|
4
|
-
const logger_1 = require("../util/logger");
|
|
5
|
-
class CmdStatus {
|
|
6
|
-
constructor(projectId, status) {
|
|
7
|
-
this.projectId = projectId;
|
|
8
|
-
this.status = status;
|
|
9
|
-
}
|
|
10
|
-
getProjectId() {
|
|
11
|
-
return this.projectId;
|
|
12
|
-
}
|
|
13
|
-
update(status) {
|
|
14
|
-
this.status = status;
|
|
15
|
-
}
|
|
16
|
-
getStatus() {
|
|
17
|
-
return this.status;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
class CmdStatusManager {
|
|
21
|
-
constructor() {
|
|
22
|
-
this.apps = {};
|
|
23
|
-
}
|
|
24
|
-
async start(projectId) {
|
|
25
|
-
if (!(projectId in this.apps)) {
|
|
26
|
-
const cmdStatus = new CmdStatus(projectId, 'in_progress');
|
|
27
|
-
this.apps[projectId] = cmdStatus;
|
|
28
|
-
}
|
|
29
|
-
else if (this.apps[projectId].getStatus() !== 'in_progress') {
|
|
30
|
-
this.apps[projectId].update('in_progress');
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
logger_1.logger.debug(`Ignoring start for ${projectId} since it already has a command in progress`);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
logger_1.logger.debug(`Started command for ${projectId}`);
|
|
37
|
-
}
|
|
38
|
-
async stop(projectId) {
|
|
39
|
-
if (!(projectId in this.apps) ||
|
|
40
|
-
this.apps[projectId].getStatus() === 'idle') {
|
|
41
|
-
throw new Error(`No ongoing command to stop for ${projectId}`);
|
|
42
|
-
}
|
|
43
|
-
this.apps[projectId].update('idle');
|
|
44
|
-
logger_1.logger.debug(`Stopped command for ${projectId}`);
|
|
45
|
-
}
|
|
46
|
-
isCmdInProgress(projectId) {
|
|
47
|
-
if (!(projectId in this.apps)) {
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
return this.apps[projectId].getStatus() === 'in_progress';
|
|
51
|
-
}
|
|
52
|
-
anyCmdInProgress() {
|
|
53
|
-
for (const projectId in this.apps) {
|
|
54
|
-
if (this.apps[projectId].getStatus() === 'in_progress') {
|
|
55
|
-
return true;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
exports.CmdStatusManager = CmdStatusManager;
|
|
62
|
-
//# sourceMappingURL=cmd-status.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cmd-status.js","sourceRoot":"","sources":["../../src/cloud-connection/cmd-status.ts"],"names":[],"mappings":";;;AAAA,2CAAwC;AAIxC,MAAM,SAAS;IAIb,YAAY,SAAiB,EAAE,MAAqB;QAClD,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAEM,YAAY;QACjB,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAEM,MAAM,CAAC,MAAqB;QACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAEM,SAAS;QACd,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;CACF;AAED,MAAa,gBAAgB;IAA7B;QACU,SAAI,GAAuC,EAAE,CAAC;IA2CxD,CAAC;IAzCQ,KAAK,CAAC,KAAK,CAAC,SAAiB;QAClC,IAAI,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE;YAC7B,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;YAC1D,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;SAClC;aAAM,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,KAAK,aAAa,EAAE;YAC7D,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;SAC5C;aAAM;YACL,eAAM,CAAC,KAAK,CACV,sBAAsB,SAAS,6CAA6C,CAC7E,CAAC;YACF,OAAO;SACR;QACD,eAAM,CAAC,KAAK,CAAC,uBAAuB,SAAS,EAAE,CAAC,CAAC;IACnD,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,SAAiB;QACjC,IACE,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC;YACzB,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,KAAK,MAAM,EAC3C;YACA,MAAM,IAAI,KAAK,CAAC,kCAAkC,SAAS,EAAE,CAAC,CAAC;SAChE;QACD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACpC,eAAM,CAAC,KAAK,CAAC,uBAAuB,SAAS,EAAE,CAAC,CAAC;IACnD,CAAC;IAEM,eAAe,CAAC,SAAiB;QACtC,IAAI,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE;YAC7B,OAAO,KAAK,CAAC;SACd;QACD,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,KAAK,aAAa,CAAC;IAC5D,CAAC;IAEM,gBAAgB;QACrB,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,IAAI,EAAE;YACjC,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,KAAK,aAAa,EAAE;gBACtD,OAAO,IAAI,CAAC;aACb;SACF;QACD,OAAO,KAAK,CAAC;IACf,CAAC;CACF;AA5CD,4CA4CC"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { AppLogsMessage, AppLogsPayload, AppStateMessage, AppStatePayload, DeviceStatsMessage, DeviceStatsPayload, SignedUrlsRequestMessage, SignedUrlsRequestPayload, StatusResponseMessage, StatusResponsePayload } from '@alwaysai/device-agent-schemas';
|
|
2
|
-
export declare function buildAppLogsMessage(payload: AppLogsPayload, txId?: string): Promise<AppLogsMessage>;
|
|
3
|
-
export declare function buildAppStateMessage(payload: AppStatePayload, txId?: string): Promise<AppStateMessage>;
|
|
4
|
-
export declare function buildSignedUrlsRequestMessage(payload: SignedUrlsRequestPayload, txId?: string): Promise<SignedUrlsRequestMessage>;
|
|
5
|
-
export declare function buildStatusResponseMessage(payload: StatusResponsePayload, txId?: string): Promise<StatusResponseMessage>;
|
|
6
|
-
export declare function buildDeviceStatsMessage(payload: DeviceStatsPayload, txId?: string): Promise<DeviceStatsMessage>;
|
|
7
|
-
//# sourceMappingURL=message-builder.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"message-builder.d.ts","sourceRoot":"","sources":["../../src/cloud-connection/message-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EAEd,cAAc,EACd,eAAe,EAEf,eAAe,EAEf,kBAAkB,EAElB,kBAAkB,EAClB,wBAAwB,EAExB,wBAAwB,EACxB,qBAAqB,EAErB,qBAAqB,EAKtB,MAAM,gCAAgC,CAAC;AAiBxC,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,cAAc,EACvB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,cAAc,CAAC,CAUzB;AAGD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,eAAe,EACxB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,eAAe,CAAC,CAU1B;AAGD,wBAAsB,6BAA6B,CACjD,OAAO,EAAE,wBAAwB,EACjC,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,wBAAwB,CAAC,CAUnC;AAGD,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,qBAAqB,EAC9B,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,qBAAqB,CAAC,CAWhC;AAGD,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,kBAAkB,EAC3B,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,kBAAkB,CAAC,CAU7B"}
|