@alwaysai/device-agent 0.0.3 → 0.0.4-1
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 +8 -0
- package/lib/application-control/backup.d.ts.map +1 -0
- package/lib/application-control/backup.js +34 -0
- package/lib/application-control/backup.js.map +1 -0
- 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 +16 -0
- package/lib/application-control/install.d.ts.map +1 -0
- package/lib/application-control/install.js +117 -0
- package/lib/application-control/install.js.map +1 -0
- package/lib/application-control/models.d.ts +8 -0
- package/lib/application-control/models.d.ts.map +1 -1
- package/lib/application-control/models.js +44 -11
- package/lib/application-control/models.js.map +1 -1
- package/lib/application-control/status.d.ts +26 -0
- package/lib/application-control/status.d.ts.map +1 -0
- package/lib/application-control/status.js +138 -0
- package/lib/application-control/status.js.map +1 -0
- package/lib/application-control/types.d.ts +5 -0
- package/lib/application-control/types.d.ts.map +1 -0
- package/lib/{util/spawner → application-control}/types.js +0 -0
- package/lib/{util/spawner → application-control}/types.js.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 +14 -29
- package/lib/application-control/utils.js.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts +15 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +227 -9
- 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 +4 -0
- package/lib/infrastructure/certificates-and-tokens.d.ts.map +1 -0
- package/lib/infrastructure/certificates-and-tokens.js +58 -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 +31 -14
- package/lib/subcommands/app/app.d.ts.map +1 -1
- package/lib/subcommands/app/app.js +117 -62
- 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 +6 -1
- package/lib/subcommands/app/index.js.map +1 -1
- package/lib/subcommands/index.d.ts.map +1 -1
- package/lib/subcommands/index.js +1 -7
- 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.d.ts +1 -1
- package/lib/util/directories.d.ts.map +1 -1
- package/lib/util/directories.js +9 -14
- package/lib/util/directories.js.map +1 -1
- package/lib/util/get-device-id.d.ts +2 -0
- package/lib/util/get-device-id.d.ts.map +1 -0
- package/lib/util/get-device-id.js +24 -0
- package/lib/util/get-device-id.js.map +1 -0
- package/package.json +19 -14
- package/readme.md +149 -72
- package/src/application-control/backup.ts +32 -0
- package/src/application-control/environment-variables.ts +81 -0
- package/src/application-control/index.ts +40 -0
- package/src/application-control/install.ts +126 -0
- package/src/application-control/models.ts +51 -11
- package/src/application-control/status.ts +156 -0
- package/src/application-control/types.ts +1 -0
- package/src/application-control/utils.ts +12 -27
- package/src/cloud-connection/device-agent-cloud-connection.ts +265 -12
- 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 +60 -0
- package/src/{util → infrastructure}/urls.ts +1 -1
- package/src/root.ts +3 -3
- package/src/subcommands/app/app.ts +135 -62
- package/src/subcommands/app/index.ts +11 -1
- package/src/subcommands/index.ts +1 -7
- package/src/subcommands/login.ts +11 -4
- package/src/util/copy-dir.ts +1 -1
- package/src/util/directories.ts +12 -17
- package/src/util/get-device-id.ts +22 -0
- package/lib/application-control/application-control.d.ts +0 -46
- package/lib/application-control/application-control.d.ts.map +0 -1
- package/lib/application-control/application-control.js +0 -234
- package/lib/application-control/application-control.js.map +0 -1
- package/lib/constants.d.ts +0 -17
- package/lib/constants.d.ts.map +0 -1
- package/lib/constants.js +0 -24
- package/lib/constants.js.map +0 -1
- package/lib/subcommands/test-app.d.ts +0 -2
- package/lib/subcommands/test-app.d.ts.map +0 -1
- package/lib/subcommands/test-app.js +0 -29
- package/lib/subcommands/test-app.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-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 -74
- package/lib/web/web-interface.js.map +0 -1
- package/src/application-control/application-control.ts +0 -273
- package/src/constants.ts +0 -32
- package/src/subcommands/test-app.ts +0 -30
- 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 -89
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alwaysai/device-agent",
|
|
3
3
|
"description": "The alwaysAI Device Agent",
|
|
4
|
-
"version": "0.0.
|
|
5
|
-
"main": "lib/
|
|
6
|
-
"types": "lib/
|
|
4
|
+
"version": "0.0.4-1",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"types": "lib/index.d.ts",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public",
|
|
9
|
+
"tag": "next"
|
|
10
|
+
},
|
|
7
11
|
"bin": {
|
|
8
12
|
"aai-agent": "lib/index.js"
|
|
9
13
|
},
|
|
@@ -12,34 +16,35 @@
|
|
|
12
16
|
"node": ">=16.0.0"
|
|
13
17
|
},
|
|
14
18
|
"scripts": {
|
|
15
|
-
"build": "tsc --project tsconfig.build.json
|
|
19
|
+
"build": "tsc --project tsconfig.build.json",
|
|
16
20
|
"clean": "rimraf lib packages",
|
|
17
21
|
"build:clean": "npm run clean && npm run build",
|
|
18
22
|
"build:watch": "tsc --project . --watch",
|
|
19
23
|
"lint": "tslint --project tsconfig.build.json",
|
|
20
24
|
"lint:fix": "npm run lint -- --fix",
|
|
21
|
-
"test": "npm run
|
|
25
|
+
"test": "npm run test:unit",
|
|
22
26
|
"test:unit": "jest --coverage src",
|
|
23
27
|
"test:watch": "npm run test:unit -- --watch",
|
|
24
28
|
"preversion": "npm test && npm run build:clean",
|
|
25
29
|
"postversion": "npm publish"
|
|
26
30
|
},
|
|
27
31
|
"dependencies": {
|
|
28
|
-
"@alwaysai/alwayscli": "0.3.
|
|
29
|
-
"@alwaysai/config-nodejs": "0.0
|
|
32
|
+
"@alwaysai/alwayscli": "0.3.1",
|
|
33
|
+
"@alwaysai/config-nodejs": "0.1.0",
|
|
34
|
+
"@alwaysai/device-agent-schemas": "0.0.1",
|
|
30
35
|
"@carnesen/coded-error": "0.4.0",
|
|
31
36
|
"@types/mkdirp": "1.0.2",
|
|
32
37
|
"@types/pump": "1.1.1",
|
|
33
38
|
"@types/rimraf": "3.0.2",
|
|
34
39
|
"@types/signal-exit": "3.0.1",
|
|
35
40
|
"@types/tar": "6.1.1",
|
|
41
|
+
"ajv": "8.11.0",
|
|
42
|
+
"alwaysai": "1.8.0",
|
|
36
43
|
"aws-iot-device-sdk": "2.2.12",
|
|
37
|
-
"alwaysai": "1.5.1",
|
|
38
44
|
"docker-compose": "0.23.17",
|
|
39
45
|
"express": "4.17.3",
|
|
40
|
-
"fp-ts": "2.11.5",
|
|
41
|
-
"io-ts": "2.2.16",
|
|
42
46
|
"mkdirp": "1.0.4",
|
|
47
|
+
"node-fetch": "2.6.1",
|
|
43
48
|
"node-os-utils": "1.3.6",
|
|
44
49
|
"pump": "3.0.0",
|
|
45
50
|
"rimraf": "3.0.2",
|
|
@@ -48,17 +53,17 @@
|
|
|
48
53
|
"tar": "6.1.11",
|
|
49
54
|
"tree-kill": "1.2.2",
|
|
50
55
|
"winston": "3.3.3",
|
|
51
|
-
"winston-daily-rotate-file": "4.5.5"
|
|
56
|
+
"winston-daily-rotate-file": "4.5.5",
|
|
57
|
+
"yaml": "2.1.1"
|
|
52
58
|
},
|
|
53
59
|
"devDependencies": {
|
|
54
|
-
"@alwaysai/tsconfig": "0.0.
|
|
55
|
-
"@alwaysai/tslint-config": "0.0.
|
|
60
|
+
"@alwaysai/tsconfig": "0.0.1",
|
|
61
|
+
"@alwaysai/tslint-config": "0.0.4",
|
|
56
62
|
"@carnesen/run-and-catch": "0.4.3",
|
|
57
63
|
"@types/jest": "27.0.3",
|
|
58
64
|
"@types/node": "16.11.12",
|
|
59
65
|
"@types/sinon": "10.0.6",
|
|
60
66
|
"aws-sdk": "^2.1046.0",
|
|
61
|
-
"copyfiles": "^2.4.1",
|
|
62
67
|
"cp-cli": "2.0.0",
|
|
63
68
|
"get-stream": "6.0.1",
|
|
64
69
|
"jest": "27.4.3",
|
package/readme.md
CHANGED
|
@@ -1,115 +1,192 @@
|
|
|
1
1
|
# alwaysAI Device Agent
|
|
2
2
|
|
|
3
|
+
The alwaysAI Device Agent is a tool that runs on an alwaysAI device provisioned
|
|
4
|
+
for production remote deployment. It enables management of one or more alwaysAI
|
|
5
|
+
apps on a particular device via a command line interface.
|
|
6
|
+
|
|
7
|
+
Note that the Device Agent is still in an experimental phase and these commands
|
|
8
|
+
are likely to change. This guide will be updated with the latest usage as things
|
|
9
|
+
change.
|
|
10
|
+
|
|
3
11
|
## System Requirements
|
|
4
12
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
13
|
+
* Recent version of Debian or Ubuntu
|
|
14
|
+
* `npm` >= 7.0.0
|
|
15
|
+
* `node` >= 16.0.0
|
|
16
|
+
* Recent version of `docker` with user access
|
|
17
|
+
* On Linux: `$ sudo usermod -aG docker $USER`
|
|
18
|
+
* Recent version of `docker-compose`
|
|
19
|
+
* `curl` installed.
|
|
20
|
+
|
|
21
|
+
## Provision Device
|
|
22
|
+
|
|
23
|
+
### Create an alwaysAI device
|
|
9
24
|
|
|
10
|
-
|
|
25
|
+
On any machine running the alwaysAI CLI you can create a new production device
|
|
26
|
+
in the alwaysAI cloud:
|
|
11
27
|
|
|
12
28
|
```
|
|
13
|
-
$
|
|
14
|
-
|
|
29
|
+
$ aai generate-production-device --name test-dev-1 --description "Device for testing alwaysAI Device Agent"
|
|
30
|
+
✔ Create new production device
|
|
31
|
+
✔ Write device credentials to /home/alwaysai/abc8fbbf-5d7f-4e49-92b7-8feedffeedbf
|
|
32
|
+
Created test-dev-1: device ID=abc8fbbf-5d7f-4e49-92b7-8feedffeedbf
|
|
15
33
|
```
|
|
16
34
|
|
|
17
|
-
|
|
35
|
+
Note down the device ID for use with Device Agent commands. The device
|
|
36
|
+
credentials are also stored locally for you to keep in a safe place, but you
|
|
37
|
+
won't need those since the Device Agent will update them automatically for you.
|
|
18
38
|
|
|
19
|
-
|
|
39
|
+
### Provision the device
|
|
40
|
+
|
|
41
|
+
If you're system already meets all the system requirements, you can simply install the device agent via `npm`:
|
|
20
42
|
|
|
21
|
-
To enable the cloud IoT Core connection, set the following environment variable:
|
|
22
43
|
```
|
|
23
|
-
|
|
44
|
+
sudo npm install -g @alwaysai/device-agent@latest
|
|
24
45
|
```
|
|
25
46
|
|
|
26
|
-
|
|
47
|
+
Otherwise, you can run the provision script to install the agent and its dependencies:
|
|
48
|
+
|
|
27
49
|
```
|
|
28
|
-
|
|
50
|
+
curl -fsSL https://alwaysai-artifacts-prod.s3.us-west-1.amazonaws.com/device-agent/install-device-agent.sh | sudo -E bash -
|
|
29
51
|
```
|
|
30
52
|
|
|
31
|
-
|
|
53
|
+
The Device Agent will be available in the terminal as `aai-agent`.
|
|
54
|
+
|
|
55
|
+
Next, log in to the Device Agent with the following command:
|
|
32
56
|
|
|
33
|
-
On Mac you need to override the OS to remove some usage restrictions:
|
|
34
57
|
```
|
|
35
|
-
|
|
58
|
+
aai-agent login --email <username> --password <password> --device <device_id>
|
|
36
59
|
```
|
|
37
60
|
|
|
38
|
-
|
|
61
|
+
## Run an alwaysAI application on your device
|
|
62
|
+
|
|
63
|
+
### Publish an application release and install on device
|
|
64
|
+
|
|
65
|
+
First, you must publish your application to the alwaysAI cloud. From the root
|
|
66
|
+
of your application directory (where the `alwaysai.app.json` file is) on your
|
|
67
|
+
development host:
|
|
39
68
|
|
|
40
|
-
Here are the available commands:
|
|
41
69
|
```
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
70
|
+
aai app configure
|
|
71
|
+
aai app publish
|
|
72
|
+
```
|
|
45
73
|
|
|
46
|
-
|
|
74
|
+
The output of the final command will give you the release hash that was
|
|
75
|
+
published. You can run `aai release list --project <project_id>` to list all
|
|
76
|
+
release versions for the project, where project ID can be found in the
|
|
77
|
+
`alwaysai.project.json` file.
|
|
47
78
|
|
|
48
|
-
|
|
79
|
+
Now you can install the application on the device using the device agent. Run
|
|
80
|
+
the following on the device where the Device Agent is installed:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
aai-agent app install --project <project_id> [--release <release_hash>]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Control the application
|
|
87
|
+
|
|
88
|
+
Run the following commands on the device where the Device Agent is installed:
|
|
49
89
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
install-app : Install an alwaysAI app from a project
|
|
54
|
-
get-app-status : Get the status of an installed alwaysAI app
|
|
55
|
-
start-app : Start an installed alwaysAI app
|
|
56
|
-
stop-app : Stop a running alwaysAI app
|
|
57
|
-
uninstall-app : Remove an alwaysAI app
|
|
90
|
+
Get application status:
|
|
91
|
+
```
|
|
92
|
+
aai-agent app status --project <project_id>
|
|
58
93
|
```
|
|
59
94
|
|
|
60
|
-
|
|
95
|
+
Start the application:
|
|
61
96
|
```
|
|
62
|
-
|
|
97
|
+
aai-agent app start --project <project_id>
|
|
63
98
|
```
|
|
64
99
|
|
|
65
|
-
|
|
100
|
+
Show the application logs:
|
|
101
|
+
```
|
|
102
|
+
aai-agent app logs --project <project_id>
|
|
103
|
+
```
|
|
66
104
|
|
|
67
|
-
|
|
105
|
+
Stop the application:
|
|
68
106
|
```
|
|
69
|
-
|
|
70
|
-
Starting alwaysAI Device Agent
|
|
71
|
-
Starting app...
|
|
72
|
-
{
|
|
73
|
-
exitCode: 0,
|
|
74
|
-
err: 'Starting 16ad11bb-8c3a-4a15-923e-49f7a8311dc6_alwaysai_1 ... \r\n' +
|
|
75
|
-
'Starting 16ad11bb-8c3a-4a15-923e-49f7a8311dc6_alwaysai_1 ... done\r\n',
|
|
76
|
-
out: ''
|
|
77
|
-
}
|
|
78
|
-
$ node lib get-app-status --project 16ad11bb-8c3a-4a15-923e-49f7a8311dc6
|
|
79
|
-
Starting alwaysAI Device Agent
|
|
80
|
-
[
|
|
81
|
-
{
|
|
82
|
-
name: '16ad11bb-8c3a-4a15-923e-49f7a8311dc6_alwaysai_1',
|
|
83
|
-
command: 'tail -f /dev/null',
|
|
84
|
-
state: 'Up',
|
|
85
|
-
ports: []
|
|
86
|
-
}
|
|
87
|
-
]
|
|
88
|
-
$ node lib stop-app --project 16ad11bb-8c3a-4a15-923e-49f7a8311dc6
|
|
89
|
-
Starting alwaysAI Device Agent
|
|
90
|
-
{
|
|
91
|
-
exitCode: 0,
|
|
92
|
-
err: 'Stopping 16ad11bb-8c3a-4a15-923e-49f7a8311dc6_alwaysai_1 ... \r\n' +
|
|
93
|
-
'Stopping 16ad11bb-8c3a-4a15-923e-49f7a8311dc6_alwaysai_1 ... done\r\n' +
|
|
94
|
-
'Removing 16ad11bb-8c3a-4a15-923e-49f7a8311dc6_alwaysai_1 ... \r\n' +
|
|
95
|
-
'Removing 16ad11bb-8c3a-4a15-923e-49f7a8311dc6_alwaysai_1 ... done\r\n' +
|
|
96
|
-
'Removing network 16ad11bb-8c3a-4a15-923e-49f7a8311dc6_default\n',
|
|
97
|
-
out: ''
|
|
98
|
-
}
|
|
107
|
+
aai-agent app stop --project <project_id>
|
|
99
108
|
```
|
|
100
109
|
|
|
101
|
-
|
|
110
|
+
Uninstall the application:
|
|
111
|
+
```
|
|
112
|
+
aai-agent app uninstall --project <project_id>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Manage application models
|
|
116
|
+
|
|
117
|
+
There are several ways to manage the models for your application.
|
|
102
118
|
|
|
103
|
-
|
|
119
|
+
#### Update models to new version of the same model ID
|
|
120
|
+
|
|
121
|
+
If a new version of a model is published for an existing model ID, and an
|
|
122
|
+
application is already configured to be using that model ID, updating to the
|
|
123
|
+
new model can simply be done with:
|
|
104
124
|
|
|
105
125
|
```
|
|
106
|
-
|
|
126
|
+
aai-agent app stop --project <project_id>
|
|
127
|
+
aai-agent app update-models --project <project_id>
|
|
128
|
+
aai-agent app start --project <project_id>
|
|
107
129
|
```
|
|
108
130
|
|
|
109
|
-
|
|
131
|
+
#### Replace models for an application for new ID
|
|
132
|
+
|
|
133
|
+
If you'd like to install an entirely new model to an application, replacing the
|
|
134
|
+
model the app was originally configured with, run the following command:
|
|
110
135
|
|
|
111
|
-
If you are developing new functionality for the cli in tandem with the device agent, you can run
|
|
112
136
|
```
|
|
113
|
-
|
|
137
|
+
aai-agent app stop --project <project_id>
|
|
138
|
+
aai-agent app replace-models --project <project_id> --models <model_id_1> [<model_id_2> ...]
|
|
139
|
+
aai-agent app start --project <project_id>
|
|
114
140
|
```
|
|
115
|
-
|
|
141
|
+
|
|
142
|
+
If you plan on using this method, you can make your application source model ID
|
|
143
|
+
agnostic by providing the model ID to edgeIQ in the following way:
|
|
144
|
+
|
|
145
|
+
Select the first model in the config list:
|
|
146
|
+
```
|
|
147
|
+
obj_detect = edgeiq.ObjectDetection(edgeiq._globals.MODEL_ID_LIST[0])
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Set and update environment variables
|
|
151
|
+
|
|
152
|
+
The Device Agent enables you to set and update environment variables for all
|
|
153
|
+
services or a single service of you application. For example, set teh following
|
|
154
|
+
environment variable for the `alwaysai` service run the following command on the
|
|
155
|
+
device:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
aai-agent app set-env TEST_ENV=1 --project <project_id> --service alwaysai
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Device Agent Commands
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
$ aai-agent --help
|
|
165
|
+
Starting alwaysAI Device Agent
|
|
166
|
+
Usage: aai-agent <subcommand> ...
|
|
167
|
+
|
|
168
|
+
Manage your alwaysAI production device
|
|
169
|
+
|
|
170
|
+
Subcommands:
|
|
171
|
+
|
|
172
|
+
login : Login to alwaysAI (this is meant for scripted environments)
|
|
173
|
+
app list : List all installed apps
|
|
174
|
+
app list-releases : List all releases for a given app
|
|
175
|
+
app list-latest-release : List the latest release hash for a given app
|
|
176
|
+
app install : Install an alwaysAI app from a project
|
|
177
|
+
app status : Get the status of an installed alwaysAI app
|
|
178
|
+
app start : Start an installed alwaysAI app
|
|
179
|
+
app stop : Stop a running alwaysAI app
|
|
180
|
+
app restart : Restart running alwaysAI app
|
|
181
|
+
app logs : Get logs for an application
|
|
182
|
+
app uninstall : Remove an alwaysAI app
|
|
183
|
+
app rollback : Rollback an alwaysAI app to the previous version
|
|
184
|
+
app show-models : Show the application models
|
|
185
|
+
app add-model : Add a model to an alwaysAI app
|
|
186
|
+
app remove-model : Remove a model from an alwaysAI app
|
|
187
|
+
app replace-models : Replace all models of an alwaysAI app with new models
|
|
188
|
+
app update-models : Update all models for an alwaysAI app
|
|
189
|
+
app get-all-envs : Get environment variables for an application
|
|
190
|
+
app set-env : Set environment variables for a service
|
|
191
|
+
get-device-info : Get device info
|
|
192
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as rimraf from 'rimraf';
|
|
2
|
+
|
|
3
|
+
import { copyDir } from '../util/copy-dir';
|
|
4
|
+
import { buildApp, getAppDir } from './utils';
|
|
5
|
+
import { AgentConfigFile } from '../infrastructure/agent-config';
|
|
6
|
+
|
|
7
|
+
export const BACKUP_EXT = '.bak';
|
|
8
|
+
|
|
9
|
+
export async function createAppBackup(props: { projectId: string }) {
|
|
10
|
+
const { projectId } = props;
|
|
11
|
+
const appDir = getAppDir(projectId);
|
|
12
|
+
const backupAppDir = `${appDir}${BACKUP_EXT}`;
|
|
13
|
+
rimraf.sync(backupAppDir);
|
|
14
|
+
await copyDir({ srcPath: appDir, destPath: backupAppDir });
|
|
15
|
+
await AgentConfigFile().setAppBackup({ projectId });
|
|
16
|
+
console.log(`Backed up app ${projectId} to ${backupAppDir}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function rollbackApp(props: { projectId: string }) {
|
|
20
|
+
const { projectId } = props;
|
|
21
|
+
const config = await AgentConfigFile().getAppBackup({ projectId });
|
|
22
|
+
if (!config) {
|
|
23
|
+
throw new Error(`Backup doesn't exist for ${projectId}`);
|
|
24
|
+
}
|
|
25
|
+
const appDir = getAppDir(projectId);
|
|
26
|
+
const backupAppDir = `${appDir}${BACKUP_EXT}`;
|
|
27
|
+
rimraf.sync(appDir);
|
|
28
|
+
await copyDir({ srcPath: backupAppDir, destPath: appDir });
|
|
29
|
+
await buildApp({ appDir });
|
|
30
|
+
await AgentConfigFile().setAppInstalled({ projectId, version: config.version });
|
|
31
|
+
console.log(`Rolled back app ${projectId} to ${config.version}`);
|
|
32
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { JsSpawner } from 'alwaysai/lib/util';
|
|
2
|
+
import compose from 'docker-compose';
|
|
3
|
+
import { parse, stringify } from 'yaml';
|
|
4
|
+
import { AgentConfigFile } from '../infrastructure/agent-config';
|
|
5
|
+
import { getAppDir } from './utils';
|
|
6
|
+
|
|
7
|
+
export async function setEnv(props: {
|
|
8
|
+
projectId: string;
|
|
9
|
+
vars: string[];
|
|
10
|
+
service?: string;
|
|
11
|
+
}) {
|
|
12
|
+
const { projectId, vars } = props;
|
|
13
|
+
if (!AgentConfigFile().isAppReady({ projectId })) {
|
|
14
|
+
throw new Error(`App ${projectId} is not ready!`);
|
|
15
|
+
}
|
|
16
|
+
const appDir = getAppDir(projectId);
|
|
17
|
+
const spawner = JsSpawner({ path: appDir });
|
|
18
|
+
const composeContents = await spawner.readFile('docker-compose.yaml');
|
|
19
|
+
const composeParsed = parse(composeContents);
|
|
20
|
+
if ('services' in composeParsed) {
|
|
21
|
+
let services: string[] = Object.keys(composeParsed['services']);
|
|
22
|
+
if (props.service) {
|
|
23
|
+
console.log(services);
|
|
24
|
+
if (services.includes(props.service)) {
|
|
25
|
+
services = [props.service];
|
|
26
|
+
} else {
|
|
27
|
+
throw new Error(`Service ${props.service} not found in ${services}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
for (const s of services) {
|
|
31
|
+
const service = composeParsed['services'][s];
|
|
32
|
+
// The environment field overrides the env files, so by appending to
|
|
33
|
+
// the environment list we can assure the value will be used
|
|
34
|
+
if ('environment' in service) {
|
|
35
|
+
const environment: string[] = service['environment'];
|
|
36
|
+
composeParsed['services'][s]['environment'] = environment.concat(vars);
|
|
37
|
+
} else {
|
|
38
|
+
composeParsed['services'][s]['environment'] = vars;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const composeOutput = stringify(composeParsed);
|
|
43
|
+
// Validate new contents
|
|
44
|
+
await compose.config({ cwd: appDir, configAsString: composeOutput });
|
|
45
|
+
await spawner.writeFile('docker-compose.yaml', composeOutput);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function getAllEnvs(props: { projectId: string }) {
|
|
49
|
+
const { projectId } = props;
|
|
50
|
+
if (!AgentConfigFile().isAppReady({ projectId })) {
|
|
51
|
+
throw new Error(`App ${projectId} is not ready!`);
|
|
52
|
+
}
|
|
53
|
+
const appDir = getAppDir(projectId);
|
|
54
|
+
const spawner = JsSpawner({ path: appDir });
|
|
55
|
+
const envVars = {};
|
|
56
|
+
const composeContents = await spawner.readFile('docker-compose.yaml');
|
|
57
|
+
const composeParsed = parse(composeContents);
|
|
58
|
+
if ('services' in composeParsed) {
|
|
59
|
+
const services = Object.keys(composeParsed['services']);
|
|
60
|
+
for (const s of services) {
|
|
61
|
+
envVars[s] = [];
|
|
62
|
+
const service = composeParsed['services'][s];
|
|
63
|
+
if ('env_file' in service) {
|
|
64
|
+
const envFiles: string[] = service['env_file'];
|
|
65
|
+
for (const ef of envFiles) {
|
|
66
|
+
envVars[s] = envVars[s].concat((await spawner.readFile(ef)).split('\n'));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if ('environment' in service) {
|
|
70
|
+
const environment: string[] = service['environment'];
|
|
71
|
+
envVars[s] = envVars[s].concat(environment);
|
|
72
|
+
}
|
|
73
|
+
// Filter out empty lines and comment lines
|
|
74
|
+
envVars[s] = envVars[s].filter((v) => {
|
|
75
|
+
return v !== '' && !v.includes('#');
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return envVars;
|
|
81
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { installApp, uninstallApp } from './install';
|
|
2
|
+
import { rollbackApp } from './backup';
|
|
3
|
+
import {
|
|
4
|
+
listAppReleases,
|
|
5
|
+
listAppLatestRelease,
|
|
6
|
+
getAppStatus,
|
|
7
|
+
startApp,
|
|
8
|
+
getAppLogs,
|
|
9
|
+
stopApp,
|
|
10
|
+
restartApp,
|
|
11
|
+
} from './status';
|
|
12
|
+
import { ModelDetails } from './types';
|
|
13
|
+
import { getAllEnvs, setEnv } from './environment-variables';
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
installApp,
|
|
17
|
+
uninstallApp,
|
|
18
|
+
rollbackApp,
|
|
19
|
+
listAppReleases,
|
|
20
|
+
listAppLatestRelease,
|
|
21
|
+
getAppStatus,
|
|
22
|
+
startApp,
|
|
23
|
+
getAppLogs,
|
|
24
|
+
stopApp,
|
|
25
|
+
restartApp,
|
|
26
|
+
ModelDetails,
|
|
27
|
+
getAllEnvs,
|
|
28
|
+
setEnv,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// CLI-mode only
|
|
32
|
+
import {
|
|
33
|
+
addModel,
|
|
34
|
+
getAppModels,
|
|
35
|
+
removeModel,
|
|
36
|
+
replaceModels,
|
|
37
|
+
updateModels,
|
|
38
|
+
} from './models';
|
|
39
|
+
|
|
40
|
+
export { addModel, getAppModels, removeModel, replaceModels, updateModels };
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import * as rimraf from 'rimraf';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import nodeFetch, { Response } from 'node-fetch';
|
|
5
|
+
import { JsSpawner } from 'alwaysai/lib/util';
|
|
6
|
+
|
|
7
|
+
import { runCliCmd } from '../util/run-cli-cmd';
|
|
8
|
+
import { buildApp, getAppDir } from './utils';
|
|
9
|
+
import { AppDetails } from '@alwaysai/device-agent-schemas';
|
|
10
|
+
import { BACKUP_EXT, createAppBackup } from './backup';
|
|
11
|
+
import { stopApp } from './status';
|
|
12
|
+
import { AgentConfigFile } from '../infrastructure/agent-config';
|
|
13
|
+
|
|
14
|
+
export async function getInstalledApps(): Promise<AppDetails[]> {
|
|
15
|
+
const apps = await AgentConfigFile().getApps();
|
|
16
|
+
const appDetails: AppDetails[] = [];
|
|
17
|
+
for (const app of apps) {
|
|
18
|
+
const { projectId, version } = app;
|
|
19
|
+
appDetails.push({ projectId, version });
|
|
20
|
+
}
|
|
21
|
+
return appDetails;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type InstallAppProps = {
|
|
25
|
+
projectId: string;
|
|
26
|
+
releaseHash: string;
|
|
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;
|
|
51
|
+
const appDir = getAppDir(projectId);
|
|
52
|
+
const spawner = JsSpawner();
|
|
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 });
|
|
58
|
+
console.log('Application is already installed, updating');
|
|
59
|
+
await createAppBackup({ projectId });
|
|
60
|
+
await spawner.rimraf(appDir);
|
|
61
|
+
} else {
|
|
62
|
+
await AgentConfigFile().setAppInstalling({ projectId, version: releaseHash });
|
|
63
|
+
}
|
|
64
|
+
|
|
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
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Unpack app package
|
|
90
|
+
*/
|
|
91
|
+
await spawner.untar(fs.createReadStream(localDest), appDir);
|
|
92
|
+
console.log(`Unpacked application to ${appDir}`);
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Build app
|
|
96
|
+
* ToDo: migrate the model pull to use MQTT workflow
|
|
97
|
+
*/
|
|
98
|
+
await buildApp({ appDir });
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Remove tar file
|
|
102
|
+
*/
|
|
103
|
+
await spawner.rimraf(localDest);
|
|
104
|
+
|
|
105
|
+
await AgentConfigFile().setAppInstalled({ projectId, version: releaseHash });
|
|
106
|
+
console.log(`Installed ${projectId} to ${appDir}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function uninstallApp(props: { projectId: string }): Promise<void> {
|
|
110
|
+
const { projectId } = props;
|
|
111
|
+
if (!(await AgentConfigFile().isAppPresent({ projectId }))) {
|
|
112
|
+
console.log(`Application ${projectId} not installed`);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
await stopApp({ projectId });
|
|
117
|
+
} catch {
|
|
118
|
+
console.log('Failed to stop app, may be left running...');
|
|
119
|
+
}
|
|
120
|
+
await AgentConfigFile().setAppUninstalled({ projectId });
|
|
121
|
+
// Delete application directory and backup
|
|
122
|
+
const appDir = getAppDir(projectId);
|
|
123
|
+
rimraf.sync(appDir);
|
|
124
|
+
rimraf.sync(`${appDir}${BACKUP_EXT}`);
|
|
125
|
+
console.log('Uninstalled', projectId);
|
|
126
|
+
}
|