@alwaysai/device-agent 0.0.13 → 0.0.15
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/backup.js +3 -3
- package/lib/application-control/backup.js.map +1 -1
- package/lib/application-control/index.d.ts +4 -4
- package/lib/application-control/index.d.ts.map +1 -1
- package/lib/application-control/index.js +1 -4
- package/lib/application-control/index.js.map +1 -1
- package/lib/application-control/install.d.ts +1 -1
- package/lib/application-control/install.d.ts.map +1 -1
- package/lib/application-control/install.js +41 -54
- package/lib/application-control/install.js.map +1 -1
- package/lib/application-control/models.d.ts +0 -4
- package/lib/application-control/models.d.ts.map +1 -1
- package/lib/application-control/models.js +13 -22
- package/lib/application-control/models.js.map +1 -1
- package/lib/application-control/status.d.ts +0 -6
- package/lib/application-control/status.d.ts.map +1 -1
- package/lib/application-control/status.js +3 -19
- package/lib/application-control/status.js.map +1 -1
- package/lib/application-control/utils.d.ts +3 -0
- package/lib/application-control/utils.d.ts.map +1 -1
- package/lib/application-control/utils.js +50 -21
- package/lib/application-control/utils.js.map +1 -1
- package/lib/cloud-connection/bootstrap-provision.d.ts +1 -1
- package/lib/cloud-connection/bootstrap-provision.d.ts.map +1 -1
- package/lib/cloud-connection/bootstrap-provision.js +9 -9
- package/lib/cloud-connection/bootstrap-provision.js.map +1 -1
- package/lib/cloud-connection/cmd-status.d.ts +8 -0
- package/lib/cloud-connection/cmd-status.d.ts.map +1 -0
- package/lib/cloud-connection/cmd-status.js +62 -0
- package/lib/cloud-connection/cmd-status.js.map +1 -0
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts +10 -2
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +156 -66
- package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
- package/lib/cloud-connection/device-agent.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent.js +4 -3
- package/lib/cloud-connection/device-agent.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.d.ts +10 -18
- package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
- package/lib/cloud-connection/live-updates-handler.js +50 -50
- package/lib/cloud-connection/live-updates-handler.js.map +1 -1
- package/lib/cloud-connection/messages.d.ts +3 -1
- package/lib/cloud-connection/messages.d.ts.map +1 -1
- package/lib/cloud-connection/messages.js +13 -1
- package/lib/cloud-connection/messages.js.map +1 -1
- package/lib/cloud-connection/passthrough-handler.d.ts +11 -0
- package/lib/cloud-connection/passthrough-handler.d.ts.map +1 -0
- package/lib/cloud-connection/passthrough-handler.js +59 -0
- package/lib/cloud-connection/passthrough-handler.js.map +1 -0
- 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 +14 -0
- package/lib/cloud-connection/publisher.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.d.ts +2 -3
- package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
- package/lib/cloud-connection/shadow-handler.js +18 -4
- package/lib/cloud-connection/shadow-handler.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.test.d.ts +2 -0
- package/lib/cloud-connection/shadow-handler.test.d.ts.map +1 -0
- package/lib/cloud-connection/shadow-handler.test.js +321 -0
- package/lib/cloud-connection/shadow-handler.test.js.map +1 -0
- package/lib/environment.d.ts +1 -0
- package/lib/environment.d.ts.map +1 -1
- package/lib/environment.js +3 -2
- package/lib/environment.js.map +1 -1
- package/lib/index.js +2 -2
- package/lib/index.js.map +1 -1
- package/lib/infrastructure/agent-config.d.ts +15 -48
- package/lib/infrastructure/agent-config.d.ts.map +1 -1
- package/lib/infrastructure/agent-config.js.map +1 -1
- package/lib/infrastructure/agent-config.test.js +0 -6
- package/lib/infrastructure/agent-config.test.js.map +1 -1
- package/lib/infrastructure/system-id.js +2 -2
- package/lib/infrastructure/system-id.js.map +1 -1
- package/lib/infrastructure/tokens-and-device-cfg.d.ts.map +1 -1
- package/lib/infrastructure/tokens-and-device-cfg.js +5 -9
- package/lib/infrastructure/tokens-and-device-cfg.js.map +1 -1
- package/lib/local-connection/rabbitmq-connection.d.ts +4 -0
- package/lib/local-connection/rabbitmq-connection.d.ts.map +1 -0
- package/lib/local-connection/rabbitmq-connection.js +58 -0
- package/lib/local-connection/rabbitmq-connection.js.map +1 -0
- package/lib/subcommands/app/app.d.ts +4 -3
- package/lib/subcommands/app/app.d.ts.map +1 -1
- package/lib/subcommands/app/app.js +78 -27
- package/lib/subcommands/app/app.js.map +1 -1
- package/lib/subcommands/app/index.js +2 -2
- package/lib/subcommands/device/clean.js +4 -4
- package/lib/subcommands/device/clean.js.map +1 -1
- package/lib/subcommands/device/device.d.ts +1 -1
- package/lib/subcommands/device/device.d.ts.map +1 -1
- package/lib/subcommands/device/device.js +9 -10
- package/lib/subcommands/device/device.js.map +1 -1
- package/lib/subcommands/index.d.ts +0 -1
- package/lib/subcommands/index.d.ts.map +1 -1
- package/lib/subcommands/login.d.ts +0 -1
- package/lib/subcommands/login.d.ts.map +1 -1
- package/lib/subcommands/login.js +1 -9
- package/lib/subcommands/login.js.map +1 -1
- package/lib/util/directories.d.ts +11 -12
- package/lib/util/directories.d.ts.map +1 -1
- package/lib/util/directories.js +24 -29
- package/lib/util/directories.js.map +1 -1
- package/lib/util/fetch-with-timeout.d.ts +4 -0
- package/lib/util/fetch-with-timeout.d.ts.map +1 -0
- package/lib/util/fetch-with-timeout.js +15 -0
- package/lib/util/fetch-with-timeout.js.map +1 -0
- package/lib/util/logger.js +1 -0
- package/lib/util/logger.js.map +1 -1
- package/lib/util/require-logged-in-and-paid-plan.d.ts +2 -0
- package/lib/util/require-logged-in-and-paid-plan.d.ts.map +1 -0
- package/lib/util/require-logged-in-and-paid-plan.js +18 -0
- package/lib/util/require-logged-in-and-paid-plan.js.map +1 -0
- package/package.json +20 -32
- package/readme.md +100 -89
- package/src/application-control/backup.ts +3 -3
- package/src/application-control/index.ts +0 -6
- package/src/application-control/install.ts +53 -73
- package/src/application-control/models.ts +7 -19
- package/src/application-control/status.ts +3 -19
- package/src/application-control/utils.ts +61 -22
- package/src/cloud-connection/bootstrap-provision.ts +13 -10
- package/src/cloud-connection/cmd-status.ts +71 -0
- package/src/cloud-connection/device-agent-cloud-connection.ts +211 -102
- package/src/cloud-connection/device-agent.ts +7 -4
- package/src/cloud-connection/live-updates-handler.ts +79 -86
- package/src/cloud-connection/messages.ts +22 -1
- package/src/cloud-connection/passthrough-handler.ts +67 -0
- package/src/cloud-connection/publisher.ts +21 -0
- package/src/cloud-connection/shadow-handler.test.ts +361 -0
- package/src/cloud-connection/shadow-handler.ts +28 -7
- package/src/environment.ts +4 -1
- package/src/index.ts +2 -2
- package/src/infrastructure/agent-config.test.ts +0 -7
- package/src/infrastructure/agent-config.ts +24 -2
- package/src/infrastructure/system-id.ts +1 -1
- package/src/infrastructure/tokens-and-device-cfg.ts +8 -13
- package/src/local-connection/rabbitmq-connection.ts +53 -0
- package/src/subcommands/app/app.ts +82 -31
- package/src/subcommands/app/index.ts +4 -4
- package/src/subcommands/device/clean.ts +4 -4
- package/src/subcommands/device/device.ts +13 -13
- package/src/subcommands/login.ts +1 -9
- package/src/util/directories.ts +31 -29
- package/src/util/fetch-with-timeout.ts +18 -0
- package/src/util/logger.ts +2 -0
- package/src/util/require-logged-in-and-paid-plan.ts +16 -0
- package/lib/cloud-connection/app-install-status.d.ts +0 -16
- package/lib/cloud-connection/app-install-status.d.ts.map +0 -1
- package/lib/cloud-connection/app-install-status.js +0 -53
- package/lib/cloud-connection/app-install-status.js.map +0 -1
- package/src/cloud-connection/app-install-status.ts +0 -62
|
@@ -7,7 +7,7 @@ import { logger } from '../util/logger';
|
|
|
7
7
|
import { Publisher } from './publisher';
|
|
8
8
|
import { AppConfigModels, getAppCfgModelsDiff } from './shadow';
|
|
9
9
|
|
|
10
|
-
interface ShadowTopics {
|
|
10
|
+
export interface ShadowTopics {
|
|
11
11
|
projects: {
|
|
12
12
|
update: string;
|
|
13
13
|
get: string;
|
|
@@ -59,8 +59,9 @@ export class ShadowHandler {
|
|
|
59
59
|
if (projectShadow.appConfig) {
|
|
60
60
|
const newAppCfg = JSON.parse(projectShadow.appConfig);
|
|
61
61
|
if (!validateAppConfig(newAppCfg)) {
|
|
62
|
+
// FIXME: Raise an exception to be handled at higher layer
|
|
62
63
|
logger.error(
|
|
63
|
-
`Received invalid app config for ${projectId}
|
|
64
|
+
`Received invalid app config for ${projectId}!\n${JSON.stringify(
|
|
64
65
|
validateAppConfig.errors,
|
|
65
66
|
null,
|
|
66
67
|
2
|
|
@@ -78,6 +79,10 @@ export class ShadowHandler {
|
|
|
78
79
|
} else {
|
|
79
80
|
appConfigUpdates.push({ projectId, newAppCfg });
|
|
80
81
|
}
|
|
82
|
+
} else {
|
|
83
|
+
logger.warn(
|
|
84
|
+
`Ignoring shadow update for ${projectId} due to no app config`
|
|
85
|
+
);
|
|
81
86
|
}
|
|
82
87
|
}
|
|
83
88
|
return appConfigUpdates;
|
|
@@ -96,6 +101,16 @@ export class ShadowHandler {
|
|
|
96
101
|
const shadowName = topic.split('/')[5];
|
|
97
102
|
switch (topic) {
|
|
98
103
|
case this.shadowTopics.projects.updateDelta:
|
|
104
|
+
if (payload.clientToken === this.clientId) {
|
|
105
|
+
logger.debug(
|
|
106
|
+
`Ignoring message sent from self: ${JSON.stringify(
|
|
107
|
+
{ topic, payload },
|
|
108
|
+
null,
|
|
109
|
+
2
|
|
110
|
+
)}`
|
|
111
|
+
);
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
99
114
|
return await this.handleNamedShadowUpdate({ delta: payload });
|
|
100
115
|
case this.shadowTopics.projects.getAccepted:
|
|
101
116
|
if (payload['delta']) {
|
|
@@ -143,12 +158,18 @@ export class ShadowHandler {
|
|
|
143
158
|
);
|
|
144
159
|
}
|
|
145
160
|
|
|
146
|
-
public deleteProjectShadow() {
|
|
161
|
+
public deleteProjectShadow(projectId: string) {
|
|
162
|
+
const packet = {
|
|
163
|
+
state: {
|
|
164
|
+
reported: {
|
|
165
|
+
[projectId]: null
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
clientToken: this.clientId
|
|
169
|
+
};
|
|
147
170
|
this.publisher.publish(
|
|
148
|
-
|
|
149
|
-
JSON.stringify(
|
|
150
|
-
clientToken: this.clientId
|
|
151
|
-
})
|
|
171
|
+
this.shadowTopics.projects.update,
|
|
172
|
+
JSON.stringify(packet)
|
|
152
173
|
);
|
|
153
174
|
}
|
|
154
175
|
}
|
package/src/environment.ts
CHANGED
|
@@ -8,8 +8,11 @@ export const ALWAYSAI_SHOW_HIDDEN = parseBoolean(
|
|
|
8
8
|
);
|
|
9
9
|
export const ALWAYSAI_DEVICE_AGENT_MODE =
|
|
10
10
|
process.env.ALWAYSAI_DEVICE_AGENT_MODE;
|
|
11
|
-
export const ALWAYSAI_LOG_LEVEL = process.env.
|
|
11
|
+
export const ALWAYSAI_LOG_LEVEL = process.env.ALWAYSAI_LOG_LEVEL;
|
|
12
12
|
export const ALWAYSAI_LOG_TO_CONSOLE = process.env.ALWAYSAI_LOG_TO_CONSOLE;
|
|
13
|
+
export const ALWAYSAI_ANALYTICS_PASSTHROUGH = parseBoolean(
|
|
14
|
+
process.env.ALWAYSAI_ANALYTICS_PASSTHROUGH
|
|
15
|
+
);
|
|
13
16
|
|
|
14
17
|
function parseOsPlatform(str: string | undefined): NodeJS.Platform {
|
|
15
18
|
switch (str) {
|
package/src/index.ts
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import * as tempy from 'tempy';
|
|
2
2
|
import { AgentConfigFile } from './agent-config';
|
|
3
3
|
|
|
4
|
-
const mockAgentModeGetter = jest.fn().mockReturnValue(undefined);
|
|
5
|
-
jest.mock('../environment', () => ({
|
|
6
|
-
get ALWAYSAI_DEVICE_AGENT_MODE() {
|
|
7
|
-
return mockAgentModeGetter();
|
|
8
|
-
}
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
4
|
const configFile = AgentConfigFile(tempy.directory());
|
|
12
5
|
|
|
13
6
|
describe('Test Agent Config', () => {
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ConfigFileSchema,
|
|
3
|
+
ConfigFileSchemaReturnType
|
|
4
|
+
} from '@alwaysai/config-nodejs';
|
|
2
5
|
import Ajv, { JSONSchemaType } from 'ajv';
|
|
3
6
|
import { homedir } from 'os';
|
|
4
7
|
import { join } from 'path';
|
|
@@ -55,7 +58,26 @@ const ALWAYSAI_CONFIG_DIR = join(homedir(), '.config', 'alwaysai');
|
|
|
55
58
|
|
|
56
59
|
const AGENT_CONFIG_FILE_NAME = 'alwaysai.agent.json';
|
|
57
60
|
|
|
58
|
-
export
|
|
61
|
+
export interface AgentJsonFileReturnType
|
|
62
|
+
extends ConfigFileSchemaReturnType<AgentConfig> {
|
|
63
|
+
name: string;
|
|
64
|
+
getApps: () => Promise<InstalledAppConfig[]>;
|
|
65
|
+
getReadyApps;
|
|
66
|
+
getApp;
|
|
67
|
+
isAppPresent;
|
|
68
|
+
isAppReady;
|
|
69
|
+
removeApp;
|
|
70
|
+
setAppInstalling;
|
|
71
|
+
setAppInstalled;
|
|
72
|
+
setAppUninstalled;
|
|
73
|
+
getAppVersion;
|
|
74
|
+
setAppBackup;
|
|
75
|
+
getAppBackup;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function AgentConfigFile(
|
|
79
|
+
dir = ALWAYSAI_CONFIG_DIR
|
|
80
|
+
): AgentJsonFileReturnType {
|
|
59
81
|
const path = join(dir, AGENT_CONFIG_FILE_NAME);
|
|
60
82
|
const initialValue: AgentConfig = {
|
|
61
83
|
applications: []
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { SystemId } from 'alwaysai/lib/constants';
|
|
1
2
|
import { DeviceConfigFile } from 'alwaysai/lib/core/device';
|
|
2
3
|
import { ALWAYSAI_SYSTEM_ID } from 'alwaysai/lib/environment';
|
|
3
|
-
import { SystemId } from 'alwaysai/lib/infrastructure';
|
|
4
4
|
|
|
5
5
|
export function getSystemId() {
|
|
6
6
|
if (ALWAYSAI_SYSTEM_ID) {
|
|
@@ -1,23 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DeviceTokens,
|
|
3
|
-
writeOrValidateDeviceCfgFile
|
|
4
|
-
} from 'alwaysai/lib/components/device';
|
|
5
|
-
import { checkUserIsLoggedInComponent } from 'alwaysai/lib/components/user';
|
|
6
1
|
import { LOCAL_AAI_CFG_DIR } from 'alwaysai/lib/constants';
|
|
7
|
-
import { checkPaidPlan } from 'alwaysai/lib/core/project';
|
|
8
|
-
import { JsSpawner, writeTokens } from 'alwaysai/lib/util';
|
|
9
2
|
import { logger } from '../util/logger';
|
|
10
3
|
import { microServiceHttpClient } from '../util/http-client';
|
|
4
|
+
import { requireLoggedInAndPaidPlan } from '../util/require-logged-in-and-paid-plan';
|
|
5
|
+
import {
|
|
6
|
+
DeviceTokens,
|
|
7
|
+
writeOrValidateDeviceCfgFile,
|
|
8
|
+
writeTokens
|
|
9
|
+
} from 'alwaysai/lib/core/device';
|
|
10
|
+
import { JsSpawner } from 'alwaysai/lib/util';
|
|
11
11
|
|
|
12
12
|
// NOTE: This closely follows the flow of deviceCheckAndUpdateComponent in the CLI
|
|
13
13
|
export async function writeTokenAndDeviceCfg(props: { deviceUuid: string }) {
|
|
14
14
|
const { deviceUuid } = props;
|
|
15
|
-
await
|
|
16
|
-
if (!(await checkPaidPlan())) {
|
|
17
|
-
throw new Error(
|
|
18
|
-
`This action only supported for Enterprise alwaysAI accounts!`
|
|
19
|
-
);
|
|
20
|
-
}
|
|
15
|
+
await requireLoggedInAndPaidPlan();
|
|
21
16
|
const { accessToken, refreshToken, idToken } = await microServiceHttpClient(
|
|
22
17
|
'token-service',
|
|
23
18
|
'create-device-tokens',
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { JsSpawner } from 'alwaysai/lib/util';
|
|
2
|
+
import { logger } from '../util/logger';
|
|
3
|
+
import sleep from '../util/sleep';
|
|
4
|
+
|
|
5
|
+
export async function checkRabbitMQContainerRunning() {
|
|
6
|
+
const spawner = JsSpawner();
|
|
7
|
+
return await spawner.run({
|
|
8
|
+
exe: 'docker',
|
|
9
|
+
args: ['ps', '-q', '--filter', 'ancestor=rabbitmq']
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function setupRabbitMQContainer() {
|
|
14
|
+
const spawner = JsSpawner();
|
|
15
|
+
const rabbitMqContainerRunning = await checkRabbitMQContainerRunning();
|
|
16
|
+
logger.debug('Checking for running RabbitMQ container');
|
|
17
|
+
if (!rabbitMqContainerRunning) {
|
|
18
|
+
logger.debug('Setting up RabbitMQ container');
|
|
19
|
+
await spawner.run({
|
|
20
|
+
exe: 'docker',
|
|
21
|
+
args: [
|
|
22
|
+
'run',
|
|
23
|
+
'--rm',
|
|
24
|
+
'-p',
|
|
25
|
+
'5672:5672',
|
|
26
|
+
'-d',
|
|
27
|
+
'--hostname',
|
|
28
|
+
'my-rabbit',
|
|
29
|
+
'rabbitmq'
|
|
30
|
+
]
|
|
31
|
+
});
|
|
32
|
+
await sleep(8000);
|
|
33
|
+
while (!(await checkRabbitMQContainerRunning())) {
|
|
34
|
+
await sleep(5000);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function stopRabbitMQContainer() {
|
|
40
|
+
const spawner = JsSpawner();
|
|
41
|
+
const rabbitMqContainer = await spawner.run({
|
|
42
|
+
exe: 'docker',
|
|
43
|
+
args: ['ps', '-q', '--filter', 'ancestor=rabbitmq']
|
|
44
|
+
});
|
|
45
|
+
if (!rabbitMqContainer) {
|
|
46
|
+
logger.debug('No RabbitMQ container running');
|
|
47
|
+
} else {
|
|
48
|
+
await spawner.run({
|
|
49
|
+
exe: 'docker',
|
|
50
|
+
args: ['stop', `${rabbitMqContainer}`]
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -4,15 +4,13 @@ import {
|
|
|
4
4
|
CliStringInput,
|
|
5
5
|
CliTerseError
|
|
6
6
|
} from '@alwaysai/alwayscli';
|
|
7
|
+
import { ClientMessage, keyMirrors } from '@alwaysai/device-agent-schemas';
|
|
7
8
|
import {
|
|
8
|
-
addModel,
|
|
9
9
|
getAllEnvs,
|
|
10
10
|
getAppLogs,
|
|
11
11
|
getAppModels,
|
|
12
12
|
getAppStatus,
|
|
13
|
-
|
|
14
|
-
listAppLatestRelease,
|
|
15
|
-
listAppReleases,
|
|
13
|
+
readAppCfgFile,
|
|
16
14
|
removeModel,
|
|
17
15
|
replaceModels,
|
|
18
16
|
restartApp,
|
|
@@ -20,11 +18,12 @@ import {
|
|
|
20
18
|
setEnv,
|
|
21
19
|
startApp,
|
|
22
20
|
stopApp,
|
|
23
|
-
uninstallApp,
|
|
24
21
|
updateModels
|
|
25
22
|
} from '../../application-control';
|
|
23
|
+
import { DeviceAgentCloudConnection } from '../../cloud-connection/device-agent-cloud-connection';
|
|
26
24
|
import { AgentConfigFile } from '../../infrastructure/agent-config';
|
|
27
25
|
import { logger } from '../../util/logger';
|
|
26
|
+
import sleep from '../../util/sleep';
|
|
28
27
|
|
|
29
28
|
export const listAppsCliLeaf = CliLeaf({
|
|
30
29
|
name: 'list',
|
|
@@ -45,10 +44,9 @@ export const listAppReleasesCliLeaf = CliLeaf({
|
|
|
45
44
|
required: true
|
|
46
45
|
})
|
|
47
46
|
},
|
|
47
|
+
hidden: true,
|
|
48
48
|
async action(_, opts) {
|
|
49
|
-
|
|
50
|
-
const releaseHistory = await listAppReleases({ projectId: project });
|
|
51
|
-
logger.info(releaseHistory);
|
|
49
|
+
throw new CliTerseError('This command is yet currently implemented!');
|
|
52
50
|
}
|
|
53
51
|
});
|
|
54
52
|
|
|
@@ -61,15 +59,9 @@ export const listAppLatestReleaseCliLeaf = CliLeaf({
|
|
|
61
59
|
required: true
|
|
62
60
|
})
|
|
63
61
|
},
|
|
62
|
+
hidden: true,
|
|
64
63
|
async action(_, opts) {
|
|
65
|
-
|
|
66
|
-
const latestReleaseHash = await listAppLatestRelease({
|
|
67
|
-
projectId: project
|
|
68
|
-
});
|
|
69
|
-
if (latestReleaseHash === undefined) {
|
|
70
|
-
throw new CliTerseError('This application has not been published yet');
|
|
71
|
-
}
|
|
72
|
-
logger.info(latestReleaseHash);
|
|
64
|
+
throw new CliTerseError('This command is yet currently implemented!');
|
|
73
65
|
}
|
|
74
66
|
});
|
|
75
67
|
|
|
@@ -82,19 +74,32 @@ export const installAppCliLeaf = CliLeaf({
|
|
|
82
74
|
required: true
|
|
83
75
|
}),
|
|
84
76
|
releaseHash: CliStringInput({
|
|
85
|
-
description: 'Release Hash'
|
|
77
|
+
description: 'Release Hash',
|
|
78
|
+
required: true
|
|
86
79
|
})
|
|
87
80
|
},
|
|
88
81
|
async action(_, opts) {
|
|
89
|
-
const project = opts
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
82
|
+
const { project, releaseHash } = opts;
|
|
83
|
+
const deviceAgent = new DeviceAgentCloudConnection();
|
|
84
|
+
await deviceAgent.setupHandlers();
|
|
85
|
+
const topic = deviceAgent.getToDeviceTopic();
|
|
86
|
+
const message: ClientMessage = {
|
|
87
|
+
timestamp: '',
|
|
88
|
+
topic,
|
|
89
|
+
payload: {
|
|
90
|
+
messageType: keyMirrors.clientMessageType.app_version_control,
|
|
91
|
+
appVersionControl: {
|
|
92
|
+
baseCommand: keyMirrors.appVersionControl.install,
|
|
93
|
+
projectId: project,
|
|
94
|
+
appReleaseHash: releaseHash
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
await deviceAgent.handleMessage(topic, message);
|
|
99
|
+
while (deviceAgent.isCmdInProgress(project)) {
|
|
100
|
+
await sleep(1000);
|
|
93
101
|
}
|
|
94
|
-
|
|
95
|
-
throw new CliTerseError('This application has not been published yet');
|
|
96
|
-
}
|
|
97
|
-
await installApp({ projectId: project, appReleaseHash: releaseHash });
|
|
102
|
+
deviceAgent.stop();
|
|
98
103
|
}
|
|
99
104
|
});
|
|
100
105
|
|
|
@@ -110,7 +115,7 @@ export const getAppStatusCliLeaf = CliLeaf({
|
|
|
110
115
|
async action(_, opts) {
|
|
111
116
|
const { project } = opts;
|
|
112
117
|
const appStatus = await getAppStatus({ projectId: project });
|
|
113
|
-
logger.info(appStatus);
|
|
118
|
+
logger.info(JSON.stringify(appStatus, null, 2));
|
|
114
119
|
}
|
|
115
120
|
});
|
|
116
121
|
|
|
@@ -192,7 +197,25 @@ export const uninstallAppCliLeaf = CliLeaf({
|
|
|
192
197
|
},
|
|
193
198
|
async action(_, opts) {
|
|
194
199
|
const { project } = opts;
|
|
195
|
-
|
|
200
|
+
const deviceAgent = new DeviceAgentCloudConnection();
|
|
201
|
+
await deviceAgent.setupHandlers();
|
|
202
|
+
const topic = deviceAgent.getToDeviceTopic();
|
|
203
|
+
const message: ClientMessage = {
|
|
204
|
+
timestamp: '',
|
|
205
|
+
topic,
|
|
206
|
+
payload: {
|
|
207
|
+
messageType: keyMirrors.clientMessageType.app_version_control,
|
|
208
|
+
appVersionControl: {
|
|
209
|
+
baseCommand: keyMirrors.appVersionControl.uninstall,
|
|
210
|
+
projectId: project
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
await deviceAgent.handleMessage(topic, message);
|
|
215
|
+
while (deviceAgent.isCmdInProgress(project)) {
|
|
216
|
+
await sleep(1000);
|
|
217
|
+
}
|
|
218
|
+
deviceAgent.stop();
|
|
196
219
|
}
|
|
197
220
|
});
|
|
198
221
|
|
|
@@ -205,6 +228,7 @@ export const rollbackAppCliLeaf = CliLeaf({
|
|
|
205
228
|
required: true
|
|
206
229
|
})
|
|
207
230
|
},
|
|
231
|
+
hidden: true,
|
|
208
232
|
async action(_, opts) {
|
|
209
233
|
const { project } = opts;
|
|
210
234
|
await rollbackApp({ projectId: project });
|
|
@@ -238,11 +262,38 @@ export const addModelCliLeaf = CliLeaf({
|
|
|
238
262
|
model: CliStringInput({
|
|
239
263
|
description: 'Model ID',
|
|
240
264
|
required: true
|
|
265
|
+
}),
|
|
266
|
+
version: CliStringInput({
|
|
267
|
+
description: 'Model version',
|
|
268
|
+
required: true
|
|
241
269
|
})
|
|
242
270
|
},
|
|
243
271
|
async action(_, opts) {
|
|
244
|
-
const { project, model } = opts;
|
|
245
|
-
|
|
272
|
+
const { project, model, version } = opts;
|
|
273
|
+
const deviceAgent = new DeviceAgentCloudConnection();
|
|
274
|
+
await deviceAgent.setupHandlers();
|
|
275
|
+
|
|
276
|
+
const topic = deviceAgent.getShadowTopics().projects.updateDelta;
|
|
277
|
+
|
|
278
|
+
const newAppCfg = await readAppCfgFile({ projectId: project });
|
|
279
|
+
newAppCfg.models[model] = Number(version);
|
|
280
|
+
|
|
281
|
+
const message = {
|
|
282
|
+
version: 3,
|
|
283
|
+
timestamp: 0,
|
|
284
|
+
state: {
|
|
285
|
+
[project]: {
|
|
286
|
+
appConfig: JSON.stringify(newAppCfg)
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
clientToken: 'not-self'
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
await deviceAgent.handleMessage(topic, message);
|
|
293
|
+
while (deviceAgent.isCmdInProgress(project)) {
|
|
294
|
+
await sleep(1000);
|
|
295
|
+
}
|
|
296
|
+
deviceAgent.stop();
|
|
246
297
|
}
|
|
247
298
|
});
|
|
248
299
|
|
|
@@ -299,7 +350,7 @@ export const updateModelsCliLeaf = CliLeaf({
|
|
|
299
350
|
}
|
|
300
351
|
});
|
|
301
352
|
|
|
302
|
-
export const
|
|
353
|
+
export const getAllEnvsCliLeaf = CliLeaf({
|
|
303
354
|
name: 'get-all-envs',
|
|
304
355
|
description: 'Get environment variables for an application',
|
|
305
356
|
namedInputs: {
|
|
@@ -314,7 +365,7 @@ export const getAllEnvsCLiLeaf = CliLeaf({
|
|
|
314
365
|
}
|
|
315
366
|
});
|
|
316
367
|
|
|
317
|
-
export const
|
|
368
|
+
export const setEnvCliLeaf = CliLeaf({
|
|
318
369
|
name: 'set-env',
|
|
319
370
|
description: 'Set environment variables for a service',
|
|
320
371
|
positionalInput: CliStringArrayInput({
|
|
@@ -16,8 +16,8 @@ import {
|
|
|
16
16
|
restartAppCliLeaf,
|
|
17
17
|
replaceModelsCliLeaf,
|
|
18
18
|
showAppModelsCliLeaf,
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
getAllEnvsCliLeaf,
|
|
20
|
+
setEnvCliLeaf
|
|
21
21
|
} from './app';
|
|
22
22
|
|
|
23
23
|
export const appCliBranch = CliBranch({
|
|
@@ -40,7 +40,7 @@ export const appCliBranch = CliBranch({
|
|
|
40
40
|
removeModelCliLeaf,
|
|
41
41
|
replaceModelsCliLeaf,
|
|
42
42
|
updateModelsCliLeaf,
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
getAllEnvsCliLeaf,
|
|
44
|
+
setEnvCliLeaf
|
|
45
45
|
]
|
|
46
46
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CliLeaf } from '@alwaysai/alwayscli';
|
|
2
|
-
import
|
|
2
|
+
import { rimraf } from 'rimraf';
|
|
3
3
|
import { LOCAL_CERT_AND_KEY_DIR } from 'alwaysai/lib/constants';
|
|
4
4
|
import { logger } from '../../util/logger';
|
|
5
5
|
import { AgentConfigFile } from '../../infrastructure/agent-config';
|
|
@@ -12,15 +12,15 @@ export const cleanCliLeaf = CliLeaf({
|
|
|
12
12
|
async action(_, opts) {
|
|
13
13
|
logger.info('Cleaning device configuration');
|
|
14
14
|
logger.debug(`Removing ${LOCAL_CERT_AND_KEY_DIR}`);
|
|
15
|
-
rimraf
|
|
15
|
+
await rimraf(LOCAL_CERT_AND_KEY_DIR);
|
|
16
16
|
logger.debug(`Removing ${AgentConfigFile().path}`);
|
|
17
17
|
AgentConfigFile().remove();
|
|
18
18
|
logger.debug(`Removing ${DeviceConfigFile().path}`);
|
|
19
19
|
DeviceConfigFile().remove();
|
|
20
20
|
logger.debug(`Removing ${CREDENTIALS_FILE_PATH}`);
|
|
21
|
-
rimraf
|
|
21
|
+
await rimraf(CREDENTIALS_FILE_PATH);
|
|
22
22
|
logger.debug(`Removing ${APP_ROOT}`);
|
|
23
|
-
rimraf
|
|
23
|
+
await rimraf(APP_ROOT);
|
|
24
24
|
logger.info('Device configuration cleaned');
|
|
25
25
|
}
|
|
26
26
|
});
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { CliLeaf, CliStringInput } from '@alwaysai/alwayscli';
|
|
2
|
-
import { checkUserIsLoggedInComponent } from 'alwaysai/lib/components/user';
|
|
3
2
|
import { getTargetHardwareUuid } from 'alwaysai/lib/core/app';
|
|
4
|
-
import { checkPaidPlan } from 'alwaysai/lib/core/project';
|
|
5
3
|
import { v4 as uuidv4 } from 'uuid';
|
|
6
4
|
import { CliAuthenticationClient } from 'alwaysai/lib/infrastructure';
|
|
7
5
|
import { existsSync } from 'fs';
|
|
@@ -9,7 +7,7 @@ import { httpClient, microServiceHttpClient } from '../../util/http-client';
|
|
|
9
7
|
import {
|
|
10
8
|
AWS_ROOT_CERTIFICATE_FILE_NAME,
|
|
11
9
|
BOOTSTRAP_CERTIFICATES_DIR_PATH,
|
|
12
|
-
|
|
10
|
+
DEVICE_PRIVATE_KEY_FILE_PATH
|
|
13
11
|
} from '../../util/directories';
|
|
14
12
|
|
|
15
13
|
import { LOCAL_CERT_AND_KEY_DIR } from 'alwaysai/lib/constants';
|
|
@@ -22,6 +20,7 @@ import {
|
|
|
22
20
|
import { writeTokenAndDeviceCfg } from '../../infrastructure/tokens-and-device-cfg';
|
|
23
21
|
import { logger } from '../../util/logger';
|
|
24
22
|
import { DeviceConfigFile } from 'alwaysai/lib/core/device';
|
|
23
|
+
import { requireLoggedInAndPaidPlan } from '../../util/require-logged-in-and-paid-plan';
|
|
25
24
|
|
|
26
25
|
export const initCliLeaf = CliLeaf({
|
|
27
26
|
name: 'init',
|
|
@@ -29,7 +28,7 @@ export const initCliLeaf = CliLeaf({
|
|
|
29
28
|
namedInputs: {
|
|
30
29
|
name: CliStringInput({
|
|
31
30
|
description: 'Device name',
|
|
32
|
-
required:
|
|
31
|
+
required: false
|
|
33
32
|
}),
|
|
34
33
|
description: CliStringInput({
|
|
35
34
|
description: 'Device description',
|
|
@@ -37,19 +36,19 @@ export const initCliLeaf = CliLeaf({
|
|
|
37
36
|
})
|
|
38
37
|
},
|
|
39
38
|
async action(_, opts) {
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
let { name } = opts;
|
|
40
|
+
name = name || uuidv4();
|
|
41
|
+
|
|
42
|
+
if (
|
|
43
|
+
DeviceConfigFile().exists() ||
|
|
44
|
+
existsSync(DEVICE_PRIVATE_KEY_FILE_PATH)
|
|
45
|
+
) {
|
|
42
46
|
throw new Error(
|
|
43
47
|
"Device has been previously provisioned. Run 'aai-agent device clean' to re-provision"
|
|
44
48
|
);
|
|
45
49
|
}
|
|
46
50
|
logger.info(`Initializing device ${name}`);
|
|
47
|
-
await
|
|
48
|
-
if (!(await checkPaidPlan())) {
|
|
49
|
-
throw new Error(
|
|
50
|
-
`This action only supported for Enterprise alwaysAI accounts!`
|
|
51
|
-
);
|
|
52
|
-
}
|
|
51
|
+
await requireLoggedInAndPaidPlan();
|
|
53
52
|
const { username } = await CliAuthenticationClient().getInfo();
|
|
54
53
|
const hardwareId = await getTargetHardwareUuid(JsSpawner());
|
|
55
54
|
const device = {
|
|
@@ -60,6 +59,7 @@ export const initCliLeaf = CliLeaf({
|
|
|
60
59
|
friendlyName: name
|
|
61
60
|
};
|
|
62
61
|
|
|
62
|
+
// FIXME: Use JSON schema to validate input
|
|
63
63
|
const response: { deviceUuid: string; claimCertificate: string[] } =
|
|
64
64
|
await microServiceHttpClient(
|
|
65
65
|
'fleet-provision',
|
|
@@ -69,7 +69,7 @@ export const initCliLeaf = CliLeaf({
|
|
|
69
69
|
);
|
|
70
70
|
logger.debug(`addDevice Response: ${JSON.stringify(response, null, 2)}`);
|
|
71
71
|
|
|
72
|
-
if (!
|
|
72
|
+
if (!('claimCertificate' in response)) {
|
|
73
73
|
throw new Error(
|
|
74
74
|
"Device cannot be provisioned. Run 'aai-agent device clean' and retry."
|
|
75
75
|
);
|
package/src/subcommands/login.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { CliLeaf, CliStringInput } from '@alwaysai/alwayscli';
|
|
2
2
|
import { alwaysaiUserLoginYesComponent } from 'alwaysai/lib/components/user';
|
|
3
|
-
import { writeTokenAndDeviceCfg } from '../infrastructure/tokens-and-device-cfg';
|
|
4
3
|
|
|
5
4
|
export const loginCliLeaf = CliLeaf({
|
|
6
5
|
name: 'login',
|
|
@@ -13,21 +12,14 @@ export const loginCliLeaf = CliLeaf({
|
|
|
13
12
|
password: CliStringInput({
|
|
14
13
|
description: 'Account password',
|
|
15
14
|
required: true
|
|
16
|
-
}),
|
|
17
|
-
device: CliStringInput({
|
|
18
|
-
description: 'The device UUID',
|
|
19
|
-
required: false
|
|
20
15
|
})
|
|
21
16
|
},
|
|
22
17
|
async action(_, opts) {
|
|
23
|
-
const { email, password
|
|
18
|
+
const { email, password } = opts;
|
|
24
19
|
|
|
25
20
|
await alwaysaiUserLoginYesComponent({
|
|
26
21
|
alwaysaiUserEmail: email,
|
|
27
22
|
alwaysaiUserPassword: password
|
|
28
23
|
});
|
|
29
|
-
if (device) {
|
|
30
|
-
await writeTokenAndDeviceCfg({ deviceUuid: device });
|
|
31
|
-
}
|
|
32
24
|
}
|
|
33
25
|
});
|
package/src/util/directories.ts
CHANGED
|
@@ -33,52 +33,54 @@ export const shortenSystemId = () => {
|
|
|
33
33
|
}
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
+
/*===================================================================
|
|
37
|
+
Bootstrap Certificates
|
|
38
|
+
===================================================================*/
|
|
39
|
+
|
|
36
40
|
export const BOOTSTRAP_DIR_NAME = 'bootstrap-certificates';
|
|
37
|
-
export const
|
|
41
|
+
export const BOOTSTRAP_PRIVATE_KEY_FILE_NAME = () =>
|
|
38
42
|
`aai-claim-private-key_${shortenSystemId()}.pem.key`;
|
|
39
|
-
export const
|
|
43
|
+
export const BOOTSTRAP_CERTIFICATE_FILE_NAME = () =>
|
|
40
44
|
`aai-claim-cert_${shortenSystemId()}.pem.crt`;
|
|
41
|
-
export const
|
|
45
|
+
export const BOOTSTRAP_ID_FILE_NAME = () =>
|
|
42
46
|
`aai-claim-cert-id_${shortenSystemId()}.txt`;
|
|
43
47
|
export const CERTIFICATE_OWNERSHIP_TOKEN_FILE_NAME =
|
|
44
48
|
'certificate-ownership-token.txt';
|
|
45
|
-
export const DEVICE_PRIVATE_KEY_FILE_NAME = () =>
|
|
46
|
-
`aai-claim-private-key_${shortenSystemId()}.pem.key`;
|
|
47
|
-
export const DEVICE_CERTIFICATE_FILE_NAME = () =>
|
|
48
|
-
`aai-claim-cert_${shortenSystemId()}.pem.crt`;
|
|
49
|
-
export const DEVICE_CLAIM_ID_FILE_NAME = () =>
|
|
50
|
-
`aai-claim-cert-id_${shortenSystemId()}.txt`;
|
|
51
|
-
|
|
52
|
-
export function getPrivateKeyFilePath() {
|
|
53
|
-
return join(LOCAL_CERT_AND_KEY_DIR, DEVICE_PRIVATE_KEY_FILE_NAME());
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function getCertificateFilePath() {
|
|
57
|
-
return join(LOCAL_CERT_AND_KEY_DIR, DEVICE_CERTIFICATE_FILE_NAME());
|
|
58
|
-
}
|
|
59
49
|
|
|
60
50
|
export const BOOTSTRAP_CERTIFICATES_DIR_PATH = () =>
|
|
61
51
|
join(LOCAL_CERT_AND_KEY_DIR, BOOTSTRAP_DIR_NAME);
|
|
62
|
-
export const DEVICE_CLAIM_ID_FILE_PATH = () =>
|
|
63
|
-
join(LOCAL_CERT_AND_KEY_DIR, DEVICE_TOKEN_FILE_NAME);
|
|
64
52
|
|
|
65
|
-
export const
|
|
53
|
+
export const BOOTSTRAP_PRIVATE_KEY_FILE_PATH = () =>
|
|
66
54
|
join(
|
|
67
55
|
LOCAL_CERT_AND_KEY_DIR,
|
|
68
56
|
BOOTSTRAP_DIR_NAME,
|
|
69
|
-
|
|
57
|
+
BOOTSTRAP_PRIVATE_KEY_FILE_NAME()
|
|
70
58
|
);
|
|
71
59
|
|
|
72
|
-
export const
|
|
60
|
+
export const BOOTSTRAP_CERTIFICATE_FILE_PATH = () =>
|
|
73
61
|
join(
|
|
74
62
|
LOCAL_CERT_AND_KEY_DIR,
|
|
75
63
|
BOOTSTRAP_DIR_NAME,
|
|
76
|
-
|
|
64
|
+
BOOTSTRAP_CERTIFICATE_FILE_NAME()
|
|
77
65
|
);
|
|
78
66
|
|
|
79
|
-
export const
|
|
80
|
-
join(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
67
|
+
export const BOOTSTRAP_ID_FILE_PATH = () =>
|
|
68
|
+
join(LOCAL_CERT_AND_KEY_DIR, BOOTSTRAP_DIR_NAME, BOOTSTRAP_ID_FILE_NAME());
|
|
69
|
+
|
|
70
|
+
/*===================================================================
|
|
71
|
+
Device Certificates
|
|
72
|
+
===================================================================*/
|
|
73
|
+
|
|
74
|
+
export const DEVICE_PRIVATE_KEY_FILE_NAME = 'aai-device-private-key.pem.key';
|
|
75
|
+
export const DEVICE_CERTIFICATE_FILE_NAME = 'aai-device-cert.pem.crt';
|
|
76
|
+
export const DEVICE_ID_FILE_NAME = 'aai-device-cert-id.txt';
|
|
77
|
+
|
|
78
|
+
export const DEVICE_PRIVATE_KEY_FILE_PATH = join(
|
|
79
|
+
LOCAL_CERT_AND_KEY_DIR,
|
|
80
|
+
DEVICE_PRIVATE_KEY_FILE_NAME
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
export const DEVICE_CERTIFICATE_FILE_PATH = join(
|
|
84
|
+
LOCAL_CERT_AND_KEY_DIR,
|
|
85
|
+
DEVICE_CERTIFICATE_FILE_NAME
|
|
86
|
+
);
|