@alwaysai/device-agent 0.0.4 → 0.0.5
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.d.ts.map +1 -1
- package/lib/application-control/backup.js +10 -6
- package/lib/application-control/backup.js.map +1 -1
- package/lib/application-control/environment-variables.d.ts +9 -0
- package/lib/application-control/environment-variables.d.ts.map +1 -0
- package/lib/application-control/environment-variables.js +82 -0
- package/lib/application-control/environment-variables.js.map +1 -0
- package/lib/application-control/index.d.ts +9 -0
- package/lib/application-control/index.d.ts.map +1 -0
- package/lib/application-control/index.js +27 -0
- package/lib/application-control/index.js.map +1 -0
- package/lib/application-control/install.d.ts +8 -2
- package/lib/application-control/install.d.ts.map +1 -1
- package/lib/application-control/install.js +72 -42
- package/lib/application-control/install.js.map +1 -1
- package/lib/application-control/models.d.ts.map +1 -1
- package/lib/application-control/models.js +7 -17
- package/lib/application-control/models.js.map +1 -1
- package/lib/application-control/status.d.ts +3 -3
- package/lib/application-control/status.d.ts.map +1 -1
- package/lib/application-control/status.js +19 -20
- package/lib/application-control/status.js.map +1 -1
- package/lib/application-control/types.d.ts +0 -13
- package/lib/application-control/types.d.ts.map +1 -1
- package/lib/application-control/utils.d.ts +2 -9
- package/lib/application-control/utils.d.ts.map +1 -1
- package/lib/application-control/utils.js +13 -28
- package/lib/application-control/utils.js.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts +15 -2
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +200 -27
- package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
- package/lib/docker/docker-cmd.js +2 -2
- package/lib/docker/docker-cmd.js.map +1 -1
- package/lib/docker/docker-compose-cmd.js +2 -2
- package/lib/docker/docker-compose-cmd.js.map +1 -1
- package/lib/environment.d.ts +2 -0
- package/lib/environment.d.ts.map +1 -1
- package/lib/environment.js +3 -1
- package/lib/environment.js.map +1 -1
- package/lib/index.js +10 -8
- package/lib/index.js.map +1 -1
- package/lib/infrastructure/agent-config.d.ts +73 -0
- package/lib/infrastructure/agent-config.d.ts.map +1 -0
- package/lib/infrastructure/agent-config.js +186 -0
- package/lib/infrastructure/agent-config.js.map +1 -0
- package/lib/infrastructure/agent-config.test.d.ts +2 -0
- package/lib/infrastructure/agent-config.test.d.ts.map +1 -0
- package/lib/infrastructure/agent-config.test.js +135 -0
- package/lib/infrastructure/agent-config.test.js.map +1 -0
- package/lib/infrastructure/certificates-and-tokens.d.ts +6 -0
- package/lib/infrastructure/certificates-and-tokens.d.ts.map +1 -0
- package/lib/infrastructure/certificates-and-tokens.js +69 -0
- package/lib/infrastructure/certificates-and-tokens.js.map +1 -0
- package/lib/{util → infrastructure}/urls.d.ts +0 -0
- package/lib/infrastructure/urls.d.ts.map +1 -0
- package/lib/{util → infrastructure}/urls.js +3 -3
- package/lib/infrastructure/urls.js.map +1 -0
- package/lib/root.js +3 -3
- package/lib/root.js.map +1 -1
- package/lib/subcommands/app/app.d.ts +8 -1
- package/lib/subcommands/app/app.d.ts.map +1 -1
- package/lib/subcommands/app/app.js +58 -24
- package/lib/subcommands/app/app.js.map +1 -1
- package/lib/subcommands/app/index.d.ts.map +1 -1
- package/lib/subcommands/app/index.js +2 -0
- package/lib/subcommands/app/index.js.map +1 -1
- package/lib/subcommands/device/device.d.ts +6 -0
- package/lib/subcommands/device/device.d.ts.map +1 -0
- package/lib/subcommands/device/device.js +62 -0
- package/lib/subcommands/device/device.js.map +1 -0
- package/lib/subcommands/device/index.d.ts +2 -0
- package/lib/subcommands/device/index.d.ts.map +1 -0
- package/lib/subcommands/device/index.js +11 -0
- package/lib/subcommands/device/index.js.map +1 -0
- package/lib/subcommands/get-model-package.d.ts +5 -0
- package/lib/subcommands/get-model-package.d.ts.map +1 -0
- package/lib/subcommands/get-model-package.js +51 -0
- package/lib/subcommands/get-model-package.js.map +1 -0
- package/lib/subcommands/index.d.ts +8 -1
- package/lib/subcommands/index.d.ts.map +1 -1
- package/lib/subcommands/index.js +9 -3
- package/lib/subcommands/index.js.map +1 -1
- package/lib/subcommands/login.d.ts +3 -2
- package/lib/subcommands/login.d.ts.map +1 -1
- package/lib/subcommands/login.js +11 -4
- package/lib/subcommands/login.js.map +1 -1
- package/lib/util/copy-dir.js +3 -3
- package/lib/util/copy-dir.js.map +1 -1
- package/lib/util/directories.js +5 -5
- package/lib/util/directories.js.map +1 -1
- package/package.json +19 -14
- package/readme.md +176 -79
- package/src/application-control/backup.ts +10 -6
- package/src/application-control/environment-variables.ts +81 -0
- package/src/application-control/index.ts +40 -0
- package/src/application-control/install.ts +79 -55
- package/src/application-control/models.ts +12 -17
- package/src/application-control/status.ts +26 -24
- package/src/application-control/types.ts +0 -4
- package/src/application-control/utils.ts +10 -25
- package/src/cloud-connection/device-agent-cloud-connection.ts +243 -40
- package/src/docker/docker-cmd.ts +1 -1
- package/src/docker/docker-compose-cmd.ts +1 -1
- package/src/environment.ts +2 -0
- package/src/index.ts +10 -7
- package/src/infrastructure/agent-config.test.ts +143 -0
- package/src/infrastructure/agent-config.ts +217 -0
- package/src/infrastructure/certificates-and-tokens.ts +71 -0
- package/src/{util → infrastructure}/urls.ts +1 -1
- package/src/root.ts +3 -3
- package/src/subcommands/app/app.ts +57 -19
- package/src/subcommands/app/index.ts +4 -0
- package/src/subcommands/device/device.ts +63 -0
- package/src/subcommands/device/index.ts +8 -0
- package/src/subcommands/get-model-package.ts +60 -0
- package/src/subcommands/index.ts +9 -3
- package/src/subcommands/login.ts +11 -4
- package/src/util/copy-dir.ts +1 -1
- package/src/util/directories.ts +8 -8
- package/lib/constants.d.ts +0 -13
- package/lib/constants.d.ts.map +0 -1
- package/lib/constants.js +0 -18
- package/lib/constants.js.map +0 -1
- package/lib/subcommands/device-control.d.ts +0 -2
- package/lib/subcommands/device-control.d.ts.map +0 -1
- package/lib/subcommands/device-control.js +0 -19
- package/lib/subcommands/device-control.js.map +0 -1
- package/lib/util/spawner/gnu-spawner.d.ts +0 -9
- package/lib/util/spawner/gnu-spawner.d.ts.map +0 -1
- package/lib/util/spawner/gnu-spawner.js +0 -102
- package/lib/util/spawner/gnu-spawner.js.map +0 -1
- package/lib/util/spawner/js-spawner.d.ts +0 -5
- package/lib/util/spawner/js-spawner.d.ts.map +0 -1
- package/lib/util/spawner/js-spawner.js +0 -89
- package/lib/util/spawner/js-spawner.js.map +0 -1
- package/lib/util/spawner/types.d.ts +0 -28
- package/lib/util/spawner/types.d.ts.map +0 -1
- package/lib/util/spawner/types.js +0 -3
- package/lib/util/spawner/types.js.map +0 -1
- package/lib/util/spawner-base/index.d.ts +0 -17
- package/lib/util/spawner-base/index.d.ts.map +0 -1
- package/lib/util/spawner-base/index.js +0 -30
- package/lib/util/spawner-base/index.js.map +0 -1
- package/lib/util/spawner-base/run-foreground-sync.d.ts +0 -3
- package/lib/util/spawner-base/run-foreground-sync.d.ts.map +0 -1
- package/lib/util/spawner-base/run-foreground-sync.js +0 -18
- package/lib/util/spawner-base/run-foreground-sync.js.map +0 -1
- package/lib/util/spawner-base/run-foreground.d.ts +0 -3
- package/lib/util/spawner-base/run-foreground.d.ts.map +0 -1
- package/lib/util/spawner-base/run-foreground.js +0 -49
- package/lib/util/spawner-base/run-foreground.js.map +0 -1
- package/lib/util/spawner-base/run-streaming.d.ts +0 -4
- package/lib/util/spawner-base/run-streaming.d.ts.map +0 -1
- package/lib/util/spawner-base/run-streaming.js +0 -35
- package/lib/util/spawner-base/run-streaming.js.map +0 -1
- package/lib/util/spawner-base/run.d.ts +0 -4
- package/lib/util/spawner-base/run.d.ts.map +0 -1
- package/lib/util/spawner-base/run.js +0 -56
- package/lib/util/spawner-base/run.js.map +0 -1
- package/lib/util/urls.d.ts.map +0 -1
- package/lib/util/urls.js.map +0 -1
- package/lib/web/index.html +0 -229
- package/lib/web/static/Karla.css +0 -18
- package/lib/web/static/bootstrap-4.3.1.min.css +0 -7
- package/lib/web/static/bootstrap-4.3.1.min.js +0 -7
- package/lib/web/static/favicon.ico +0 -0
- package/lib/web/static/jquery-3.3.1.slim.min.js +0 -2
- package/lib/web/static/popper-1.14.7.min.js +0 -5
- package/lib/web/web-interface.d.ts +0 -2
- package/lib/web/web-interface.d.ts.map +0 -1
- package/lib/web/web-interface.js +0 -75
- package/lib/web/web-interface.js.map +0 -1
- package/src/constants.ts +0 -22
- package/src/subcommands/device-control.ts +0 -16
- package/src/util/spawner/gnu-spawner.ts +0 -114
- package/src/util/spawner/js-spawner.ts +0 -110
- package/src/util/spawner/types.ts +0 -28
- package/src/util/spawner-base/index.ts +0 -28
- package/src/util/spawner-base/run-foreground-sync.ts +0 -16
- package/src/util/spawner-base/run-foreground.ts +0 -49
- package/src/util/spawner-base/run-streaming.ts +0 -40
- package/src/util/spawner-base/run.ts +0 -60
- package/src/web/index.html +0 -229
- package/src/web/static/Karla.css +0 -18
- package/src/web/static/bootstrap-4.3.1.min.css +0 -7
- package/src/web/static/bootstrap-4.3.1.min.js +0 -7
- package/src/web/static/favicon.ico +0 -0
- package/src/web/static/jquery-3.3.1.slim.min.js +0 -2
- package/src/web/static/popper-1.14.7.min.js +0 -5
- package/src/web/web-interface.ts +0 -85
|
@@ -1,92 +1,114 @@
|
|
|
1
1
|
import * as rimraf from 'rimraf';
|
|
2
2
|
import * as fs from 'fs';
|
|
3
|
-
import
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import nodeFetch, { Response } from 'node-fetch';
|
|
5
|
+
import { JsSpawner } from 'alwaysai/lib/util';
|
|
4
6
|
|
|
5
7
|
import { runCliCmd } from '../util/run-cli-cmd';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
APP_ROOT,
|
|
9
|
-
buildApp,
|
|
10
|
-
getAppDir,
|
|
11
|
-
getAppVersion,
|
|
12
|
-
isAppInstalled,
|
|
13
|
-
setAppVersion,
|
|
14
|
-
} from './utils';
|
|
15
|
-
import { AppDetails } from './types';
|
|
8
|
+
import { buildApp, getAppDir } from './utils';
|
|
9
|
+
import { AppDetails } from '@alwaysai/device-agent-schemas';
|
|
16
10
|
import { BACKUP_EXT, createAppBackup } from './backup';
|
|
17
11
|
import { stopApp } from './status';
|
|
12
|
+
import { AgentConfigFile } from '../infrastructure/agent-config';
|
|
18
13
|
|
|
19
14
|
export async function getInstalledApps(): Promise<AppDetails[]> {
|
|
20
|
-
|
|
21
|
-
return [];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const projectIds = fs.readdirSync(APP_ROOT).filter(file => {
|
|
25
|
-
const isDir = fs.statSync(`${APP_ROOT}/${file}`).isDirectory();
|
|
26
|
-
const isBak = `${APP_ROOT}/${file}`.includes(BACKUP_EXT);
|
|
27
|
-
return isDir && !isBak;
|
|
28
|
-
});
|
|
29
|
-
|
|
15
|
+
const apps = await AgentConfigFile().getApps();
|
|
30
16
|
const appDetails: AppDetails[] = [];
|
|
31
|
-
for (const
|
|
32
|
-
const
|
|
33
|
-
const version = await getAppVersion({ appDir });
|
|
17
|
+
for (const app of apps) {
|
|
18
|
+
const { projectId, version } = app;
|
|
34
19
|
appDetails.push({ projectId, version });
|
|
35
20
|
}
|
|
36
21
|
return appDetails;
|
|
37
22
|
}
|
|
38
23
|
|
|
39
|
-
export
|
|
24
|
+
export type InstallAppProps = {
|
|
40
25
|
projectId: string;
|
|
41
26
|
releaseHash: string;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
27
|
+
presignedAppUrl?: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export async function installAppPackage({ localDest, presignedUrl }): Promise<void> {
|
|
31
|
+
const response = await nodeFetch(presignedUrl);
|
|
32
|
+
if (response.status !== 200) {
|
|
33
|
+
// If the URL is invalid; I think we shouldn't get here with the new changes
|
|
34
|
+
throw new Error(
|
|
35
|
+
`Status Code: ${response.status}, ${response.statusText}. Response: ${response.body}`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Write the app package to the hash directory
|
|
41
|
+
*/
|
|
42
|
+
const stream = response.body.pipe(fs.createWriteStream(localDest));
|
|
43
|
+
await new Promise((resolve, reject) => {
|
|
44
|
+
stream.on('finish', resolve);
|
|
45
|
+
stream.on('error', reject);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function installApp(props: InstallAppProps): Promise<void> {
|
|
50
|
+
const { projectId, releaseHash, presignedAppUrl } = props;
|
|
45
51
|
const appDir = getAppDir(projectId);
|
|
46
|
-
const TEMP_DIR = join(APP_ROOT, 'temp');
|
|
47
52
|
const spawner = JsSpawner();
|
|
48
|
-
await
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
if (await AgentConfigFile().isAppPresent({ projectId })) {
|
|
54
|
+
if (!(await AgentConfigFile().isAppReady({ projectId }))) {
|
|
55
|
+
throw new Error('Application already has installation in progress!');
|
|
56
|
+
}
|
|
57
|
+
await AgentConfigFile().setAppInstalling({ projectId, version: releaseHash });
|
|
52
58
|
console.log('Application is already installed, updating');
|
|
53
59
|
await createAppBackup({ projectId });
|
|
54
60
|
await spawner.rimraf(appDir);
|
|
61
|
+
} else {
|
|
62
|
+
await AgentConfigFile().setAppInstalling({ projectId, version: releaseHash });
|
|
55
63
|
}
|
|
56
|
-
// Create app directory
|
|
57
|
-
await spawner.mkdirp(appDir);
|
|
58
64
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Prep directory and fetch the app package
|
|
67
|
+
*/
|
|
68
|
+
await spawner.mkdirp(appDir);
|
|
69
|
+
const localDest = path.join(appDir, `${path.basename(releaseHash)}.tgz`);
|
|
70
|
+
if (!presignedAppUrl) {
|
|
71
|
+
// Download the application using the CLI
|
|
72
|
+
await runCliCmd({
|
|
73
|
+
cmd: [
|
|
74
|
+
'release',
|
|
75
|
+
'pull',
|
|
76
|
+
'--yes',
|
|
77
|
+
'--project',
|
|
78
|
+
projectId,
|
|
79
|
+
'--releaseHash',
|
|
80
|
+
releaseHash,
|
|
81
|
+
],
|
|
82
|
+
cwd: appDir,
|
|
83
|
+
});
|
|
84
|
+
} else {
|
|
85
|
+
await installAppPackage({ localDest, presignedUrl: presignedAppUrl });
|
|
86
|
+
}
|
|
72
87
|
|
|
73
|
-
|
|
74
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Unpack app package
|
|
90
|
+
*/
|
|
91
|
+
await spawner.untar(fs.createReadStream(localDest), appDir);
|
|
75
92
|
console.log(`Unpacked application to ${appDir}`);
|
|
76
93
|
|
|
77
|
-
|
|
78
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Build app
|
|
96
|
+
* ToDo: migrate the model pull to use MQTT workflow
|
|
97
|
+
*/
|
|
79
98
|
await buildApp({ appDir });
|
|
80
99
|
|
|
81
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Remove tar file
|
|
102
|
+
*/
|
|
103
|
+
await spawner.rimraf(localDest);
|
|
82
104
|
|
|
105
|
+
await AgentConfigFile().setAppInstalled({ projectId, version: releaseHash });
|
|
83
106
|
console.log(`Installed ${projectId} to ${appDir}`);
|
|
84
107
|
}
|
|
85
108
|
|
|
86
109
|
export async function uninstallApp(props: { projectId: string }): Promise<void> {
|
|
87
110
|
const { projectId } = props;
|
|
88
|
-
|
|
89
|
-
if (!(await isAppInstalled({ appDir }))) {
|
|
111
|
+
if (!(await AgentConfigFile().isAppPresent({ projectId }))) {
|
|
90
112
|
console.log(`Application ${projectId} not installed`);
|
|
91
113
|
return;
|
|
92
114
|
}
|
|
@@ -95,7 +117,9 @@ export async function uninstallApp(props: { projectId: string }): Promise<void>
|
|
|
95
117
|
} catch {
|
|
96
118
|
console.log('Failed to stop app, may be left running...');
|
|
97
119
|
}
|
|
120
|
+
await AgentConfigFile().setAppUninstalled({ projectId });
|
|
98
121
|
// Delete application directory and backup
|
|
122
|
+
const appDir = getAppDir(projectId);
|
|
99
123
|
rimraf.sync(appDir);
|
|
100
124
|
rimraf.sync(`${appDir}${BACKUP_EXT}`);
|
|
101
125
|
console.log('Uninstalled', projectId);
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
+
import { JsSpawner } from 'alwaysai/lib/util';
|
|
1
2
|
import { existsSync } from 'fs';
|
|
2
3
|
import { join } from 'path';
|
|
3
4
|
|
|
4
5
|
import { runCliCmd } from '../util/run-cli-cmd';
|
|
5
|
-
import { JsSpawner } from '../util/spawner/js-spawner';
|
|
6
6
|
import { ModelDetails } from './types';
|
|
7
|
-
import { buildApp, getAppDir,
|
|
7
|
+
import { buildApp, getAppDir, requireAppInstalled } from './utils';
|
|
8
8
|
|
|
9
9
|
export async function getAppModels(props: { projectId: string }) {
|
|
10
10
|
const { projectId } = props;
|
|
11
|
+
await requireAppInstalled({ projectId });
|
|
12
|
+
|
|
11
13
|
const appDir = getAppDir(projectId);
|
|
12
|
-
if (!(await isAppInstalled({ appDir }))) {
|
|
13
|
-
throw new Error('Application is not installed');
|
|
14
|
-
}
|
|
15
14
|
const appCfgPath = join(appDir, 'alwaysai.app.json');
|
|
16
15
|
if (!existsSync(appCfgPath)) {
|
|
17
16
|
throw new Error('Application config not found!');
|
|
@@ -29,10 +28,9 @@ export async function getAppModels(props: { projectId: string }) {
|
|
|
29
28
|
|
|
30
29
|
export async function addModel(props: { projectId: string; modelId: string }) {
|
|
31
30
|
const { projectId, modelId } = props;
|
|
31
|
+
await requireAppInstalled({ projectId });
|
|
32
|
+
|
|
32
33
|
const appDir = getAppDir(projectId);
|
|
33
|
-
if (!(await isAppInstalled({ appDir }))) {
|
|
34
|
-
throw new Error('Application is not installed');
|
|
35
|
-
}
|
|
36
34
|
await runCliCmd({
|
|
37
35
|
cmd: ['app', 'models', 'add', modelId],
|
|
38
36
|
cwd: appDir,
|
|
@@ -42,10 +40,9 @@ export async function addModel(props: { projectId: string; modelId: string }) {
|
|
|
42
40
|
|
|
43
41
|
export async function removeModel(props: { projectId: string; modelId: string }) {
|
|
44
42
|
const { projectId, modelId } = props;
|
|
43
|
+
await requireAppInstalled({ projectId });
|
|
44
|
+
|
|
45
45
|
const appDir = getAppDir(projectId);
|
|
46
|
-
if (!(await isAppInstalled({ appDir }))) {
|
|
47
|
-
throw new Error('Application is not installed');
|
|
48
|
-
}
|
|
49
46
|
await runCliCmd({
|
|
50
47
|
cmd: ['app', 'models', 'remove', modelId, '--purge'],
|
|
51
48
|
cwd: appDir,
|
|
@@ -54,10 +51,9 @@ export async function removeModel(props: { projectId: string; modelId: string })
|
|
|
54
51
|
|
|
55
52
|
export async function replaceModels(props: { projectId: string; modelIds: string[] }) {
|
|
56
53
|
const { projectId, modelIds } = props;
|
|
54
|
+
await requireAppInstalled({ projectId });
|
|
55
|
+
|
|
57
56
|
const appDir = getAppDir(projectId);
|
|
58
|
-
if (!(await isAppInstalled({ appDir }))) {
|
|
59
|
-
throw new Error('Application is not installed');
|
|
60
|
-
}
|
|
61
57
|
await runCliCmd({
|
|
62
58
|
cmd: ['app', 'models', 'remove-all', '--purge'],
|
|
63
59
|
cwd: appDir,
|
|
@@ -73,10 +69,9 @@ export async function replaceModels(props: { projectId: string; modelIds: string
|
|
|
73
69
|
|
|
74
70
|
export async function updateModels(props: { projectId: string }) {
|
|
75
71
|
const { projectId } = props;
|
|
72
|
+
await requireAppInstalled({ projectId });
|
|
73
|
+
|
|
76
74
|
const appDir = getAppDir(projectId);
|
|
77
|
-
if (!(await isAppInstalled({ appDir }))) {
|
|
78
|
-
throw new Error('Application is not installed');
|
|
79
|
-
}
|
|
80
75
|
await runCliCmd({
|
|
81
76
|
cmd: ['app', 'models', 'update'],
|
|
82
77
|
cwd: appDir,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import compose from 'docker-compose';
|
|
2
|
-
import { fetchAppReleaseHistory } from 'alwaysai';
|
|
2
|
+
import { fetchAppReleaseHistory } from 'alwaysai/lib/infrastructure';
|
|
3
|
+
import { JsSpawner } from 'alwaysai/lib/util';
|
|
3
4
|
|
|
4
5
|
import { runDockerLogin } from '../docker/docker-cmd';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { getAppDir, requireAppInstalled } from './utils';
|
|
7
|
+
import { AppStateValue, ServiceStatus, AppState } from '@alwaysai/device-agent-schemas';
|
|
8
|
+
import { AgentConfigFile } from '../infrastructure/agent-config';
|
|
8
9
|
|
|
9
10
|
export async function listAppReleases(props: { projectId: string }) {
|
|
10
11
|
const { projectId } = props;
|
|
@@ -21,14 +22,23 @@ export async function listAppLatestRelease(props: { projectId: string }) {
|
|
|
21
22
|
return undefined;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
export async function getAppStatus(props: { projectId: string }): Promise<
|
|
25
|
+
export async function getAppStatus(props: { projectId: string }): Promise<AppState> {
|
|
25
26
|
const { projectId } = props;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (!(await isAppInstalled({ appDir }))) {
|
|
27
|
+
if (!(await AgentConfigFile().isAppPresent({ projectId }))) {
|
|
29
28
|
throw new Error('Application is not installed');
|
|
30
29
|
}
|
|
31
30
|
|
|
31
|
+
const appDetails = {
|
|
32
|
+
projectId,
|
|
33
|
+
version: await AgentConfigFile().getAppVersion({ projectId }),
|
|
34
|
+
};
|
|
35
|
+
if (!(await AgentConfigFile().isAppReady({ projectId }))) {
|
|
36
|
+
// App is being installed or updated
|
|
37
|
+
return { appDetails, services: [] };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const appDir = getAppDir(projectId);
|
|
41
|
+
|
|
32
42
|
// Check if app is running
|
|
33
43
|
const output = await compose.ps({ cwd: appDir });
|
|
34
44
|
|
|
@@ -38,7 +48,6 @@ export async function getAppStatus(props: { projectId: string }): Promise<AppSta
|
|
|
38
48
|
);
|
|
39
49
|
}
|
|
40
50
|
|
|
41
|
-
const appDetails = { projectId, version: await getAppVersion({ appDir }) };
|
|
42
51
|
const services: ServiceStatus[] = [];
|
|
43
52
|
if (output.data.services.length === 0) {
|
|
44
53
|
// If the app has never been started (or has an empty docker-compose.yaml)
|
|
@@ -54,7 +63,7 @@ export async function getAppStatus(props: { projectId: string }): Promise<AppSta
|
|
|
54
63
|
.replace(/\d+$/, '')
|
|
55
64
|
.replace(/_/g, '');
|
|
56
65
|
|
|
57
|
-
let state:
|
|
66
|
+
let state: AppStateValue;
|
|
58
67
|
if (service.state.includes('Up')) {
|
|
59
68
|
state = 'Up';
|
|
60
69
|
} else if (service.state.includes('Stopped') || service.state.includes('Exit')) {
|
|
@@ -75,15 +84,13 @@ export async function getAppLogs(props: {
|
|
|
75
84
|
services?: string[];
|
|
76
85
|
}): Promise<NodeJS.ReadableStream> {
|
|
77
86
|
const { projectId, services } = props;
|
|
78
|
-
|
|
87
|
+
await requireAppInstalled({ projectId });
|
|
79
88
|
|
|
80
|
-
|
|
81
|
-
throw new Error('Application is not installed');
|
|
82
|
-
}
|
|
89
|
+
const appDir = getAppDir(projectId);
|
|
83
90
|
|
|
84
91
|
const serviceList = services
|
|
85
92
|
? services
|
|
86
|
-
: await (async function() {
|
|
93
|
+
: await (async function () {
|
|
87
94
|
const appStatus = await getAppStatus({ projectId });
|
|
88
95
|
const services: string[] = [];
|
|
89
96
|
for (const service of appStatus.services) {
|
|
@@ -105,11 +112,9 @@ export async function startApp(props: {
|
|
|
105
112
|
dockerLoginToken?: string;
|
|
106
113
|
}): Promise<void> {
|
|
107
114
|
const { projectId, dockerLoginToken } = props;
|
|
115
|
+
await requireAppInstalled({ projectId });
|
|
116
|
+
|
|
108
117
|
const appDir = getAppDir(projectId);
|
|
109
|
-
// Check if app is installed
|
|
110
|
-
if (!(await isAppInstalled({ appDir }))) {
|
|
111
|
-
throw new Error('Application is not installed');
|
|
112
|
-
}
|
|
113
118
|
if (dockerLoginToken !== undefined) {
|
|
114
119
|
const result = await runDockerLogin({ token: dockerLoginToken });
|
|
115
120
|
console.log(result);
|
|
@@ -129,12 +134,9 @@ export async function startApp(props: {
|
|
|
129
134
|
|
|
130
135
|
export async function stopApp(props: { projectId: string }): Promise<void> {
|
|
131
136
|
const { projectId } = props;
|
|
137
|
+
await requireAppInstalled({ projectId });
|
|
138
|
+
|
|
132
139
|
const appDir = getAppDir(projectId);
|
|
133
|
-
// Check if app is installed
|
|
134
|
-
if (!(await isAppInstalled({ appDir }))) {
|
|
135
|
-
console.log('Application is not installed');
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
140
|
// TODO: Check if app is running
|
|
139
141
|
// Stop app
|
|
140
142
|
const output = await compose.down({ cwd: appDir });
|
|
@@ -1,5 +1 @@
|
|
|
1
|
-
export type AppDetails = { projectId: string; version: string };
|
|
2
|
-
export type ServiceState = 'Stopped' | 'Up' | 'Restarting';
|
|
3
|
-
export type ServiceStatus = { name: string; state: ServiceState };
|
|
4
|
-
export type AppStatus = { appDetails: AppDetails; services: ServiceStatus[] };
|
|
5
1
|
export type ModelDetails = { modelId: string; version: number };
|
|
@@ -1,41 +1,27 @@
|
|
|
1
1
|
import compose from 'docker-compose';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import * as fs from 'fs';
|
|
4
|
+
import { AAI_DIR } from 'alwaysai/lib/constants';
|
|
4
5
|
|
|
5
|
-
import { JsSpawner } from '../util/spawner/js-spawner';
|
|
6
|
-
import { AAI_DIR } from 'alwaysai';
|
|
7
6
|
import { runCliCmd } from '../util/run-cli-cmd';
|
|
7
|
+
import { AgentConfigFile } from '../infrastructure/agent-config';
|
|
8
8
|
|
|
9
9
|
export const APP_ROOT = path.join(AAI_DIR, 'applications');
|
|
10
|
-
const RELEASE_FILENAME = 'alwaysai.release.json';
|
|
11
10
|
const DOCKER_COMPOSE_FILENAME = 'docker-compose.yaml';
|
|
12
11
|
|
|
13
12
|
export function getAppDir(projectId: string): string {
|
|
14
13
|
return path.join(APP_ROOT, projectId);
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
export async function
|
|
18
|
-
const {
|
|
19
|
-
//
|
|
20
|
-
if (
|
|
21
|
-
|
|
16
|
+
export async function requireAppInstalled(props: { projectId: string }) {
|
|
17
|
+
const { projectId } = props;
|
|
18
|
+
// Ensure an app that is being installed or updated is not modified
|
|
19
|
+
if (!(await AgentConfigFile().isAppPresent({ projectId }))) {
|
|
20
|
+
throw new Error('Application is not installed');
|
|
21
|
+
}
|
|
22
|
+
if (!(await AgentConfigFile().isAppReady({ projectId }))) {
|
|
23
|
+
throw new Error('Application is not done installing or updating');
|
|
22
24
|
}
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export async function setAppVersion(props: { appDir: string; version: string }) {
|
|
27
|
-
const { appDir, version } = props;
|
|
28
|
-
// TODO: Switch to node-config
|
|
29
|
-
const versionPath = path.join(appDir, RELEASE_FILENAME);
|
|
30
|
-
const versionData = { version };
|
|
31
|
-
await JsSpawner().writeFile(versionPath, JSON.stringify(versionData, null, 2));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export async function getAppVersion(props: { appDir: string }) {
|
|
35
|
-
const { appDir } = props;
|
|
36
|
-
const versionPath = path.join(appDir, RELEASE_FILENAME);
|
|
37
|
-
const releaseData = JSON.parse(await JsSpawner().readFile(versionPath));
|
|
38
|
-
return releaseData.version;
|
|
39
25
|
}
|
|
40
26
|
|
|
41
27
|
export async function buildApp(props: { appDir: string }) {
|
|
@@ -55,7 +41,6 @@ export async function buildApp(props: { appDir: string }) {
|
|
|
55
41
|
}
|
|
56
42
|
|
|
57
43
|
const buildOut = await compose.buildAll({ cwd: appDir });
|
|
58
|
-
console.log(buildOut);
|
|
59
44
|
if (buildOut.exitCode !== 0) {
|
|
60
45
|
throw new Error(
|
|
61
46
|
`Failed to build application! stdout=${buildOut.out} stderr=${buildOut.err}`,
|