@alwaysai/device-agent 1.3.0 → 1.3.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/environment-variables.d.ts +1 -0
- package/lib/application-control/environment-variables.d.ts.map +1 -1
- package/lib/application-control/environment-variables.js +22 -20
- package/lib/application-control/environment-variables.js.map +1 -1
- package/lib/application-control/environment-variables.test.js +37 -2
- package/lib/application-control/environment-variables.test.js.map +1 -1
- package/lib/application-control/install.js +1 -1
- package/lib/application-control/install.js.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts +2 -2
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +116 -99
- package/lib/cloud-connection/device-agent-cloud-connection.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
- package/lib/cloud-connection/live-updates-handler.js +30 -25
- package/lib/cloud-connection/live-updates-handler.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.test.js +15 -0
- package/lib/cloud-connection/live-updates-handler.test.js.map +1 -1
- package/lib/cloud-connection/messages.d.ts +1 -3
- package/lib/cloud-connection/messages.d.ts.map +1 -1
- package/lib/cloud-connection/messages.js +1 -9
- package/lib/cloud-connection/messages.js.map +1 -1
- package/lib/cloud-connection/publisher.d.ts +1 -0
- package/lib/cloud-connection/publisher.d.ts.map +1 -1
- package/lib/cloud-connection/publisher.js +3 -0
- package/lib/cloud-connection/publisher.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
- package/lib/cloud-connection/shadow-handler.js +10 -3
- package/lib/cloud-connection/shadow-handler.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.test.js +79 -28
- package/lib/cloud-connection/shadow-handler.test.js.map +1 -1
- package/lib/cloud-connection/transaction-manager.d.ts +26 -6
- package/lib/cloud-connection/transaction-manager.d.ts.map +1 -1
- package/lib/cloud-connection/transaction-manager.js +103 -22
- package/lib/cloud-connection/transaction-manager.js.map +1 -1
- package/lib/cloud-connection/transaction-manager.test.js +179 -13
- package/lib/cloud-connection/transaction-manager.test.js.map +1 -1
- package/lib/subcommands/app/analytics.d.ts +10 -0
- package/lib/subcommands/app/analytics.d.ts.map +1 -0
- package/lib/subcommands/app/analytics.js +83 -0
- package/lib/subcommands/app/analytics.js.map +1 -0
- package/lib/subcommands/app/index.d.ts.map +1 -1
- package/lib/subcommands/app/index.js +3 -1
- package/lib/subcommands/app/index.js.map +1 -1
- package/lib/subcommands/app/models.d.ts +0 -5
- package/lib/subcommands/app/models.d.ts.map +1 -1
- package/lib/subcommands/app/models.js +11 -47
- package/lib/subcommands/app/models.js.map +1 -1
- package/lib/subcommands/app/status.d.ts +1 -0
- package/lib/subcommands/app/status.d.ts.map +1 -1
- package/lib/subcommands/app/status.js +14 -3
- package/lib/subcommands/app/status.js.map +1 -1
- package/lib/subcommands/app/version.d.ts +2 -1
- package/lib/subcommands/app/version.d.ts.map +1 -1
- package/lib/subcommands/app/version.js +16 -3
- package/lib/subcommands/app/version.js.map +1 -1
- package/lib/util/parsing.d.ts +2 -0
- package/lib/util/parsing.d.ts.map +1 -0
- package/lib/util/parsing.js +17 -0
- package/lib/util/parsing.js.map +1 -0
- package/package.json +4 -6
- package/readme.md +146 -92
- package/src/application-control/environment-variables.test.ts +43 -3
- package/src/application-control/environment-variables.ts +29 -19
- package/src/application-control/install.ts +1 -1
- package/src/cloud-connection/device-agent-cloud-connection.ts +155 -141
- package/src/cloud-connection/live-updates-handler.test.ts +20 -0
- package/src/cloud-connection/live-updates-handler.ts +45 -52
- package/src/cloud-connection/messages.ts +1 -14
- package/src/cloud-connection/publisher.ts +4 -0
- package/src/cloud-connection/shadow-handler.test.ts +88 -28
- package/src/cloud-connection/shadow-handler.ts +13 -3
- package/src/cloud-connection/transaction-manager.test.ts +193 -18
- package/src/cloud-connection/transaction-manager.ts +174 -26
- package/src/subcommands/app/analytics.ts +99 -0
- package/src/subcommands/app/index.ts +4 -3
- package/src/subcommands/app/models.ts +13 -49
- package/src/subcommands/app/status.ts +20 -3
- package/src/subcommands/app/version.ts +19 -4
- package/src/util/parsing.ts +11 -0
- package/lib/cloud-connection/cmd-status.d.ts +0 -8
- package/lib/cloud-connection/cmd-status.d.ts.map +0 -1
- package/lib/cloud-connection/cmd-status.js +0 -62
- package/lib/cloud-connection/cmd-status.js.map +0 -1
- package/lib/cloud-connection/message-builder.d.ts +0 -7
- package/lib/cloud-connection/message-builder.d.ts.map +0 -1
- package/lib/cloud-connection/message-builder.js +0 -63
- package/lib/cloud-connection/message-builder.js.map +0 -1
- package/src/cloud-connection/cmd-status.ts +0 -71
- package/src/cloud-connection/message-builder.ts +0 -117
package/readme.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# alwaysAI Device Agent
|
|
1
|
+
# alwaysAI Device Agent <!-- omit from toc -->
|
|
2
2
|
|
|
3
3
|
The alwaysAI Device Agent enables provisioning devices and managing devices and
|
|
4
4
|
applications in production deployments of alwaysAI Computer Vision applications.
|
|
@@ -9,28 +9,60 @@ Note that the Device Agent is still in an experimental phase and these commands
|
|
|
9
9
|
are likely to change. This guide will be updated with the latest usage as things
|
|
10
10
|
change.
|
|
11
11
|
|
|
12
|
+
- [System Requirements \& Prerequisites](#system-requirements--prerequisites)
|
|
13
|
+
- [Provision Device](#provision-device)
|
|
14
|
+
- [Install the alwaysAI Device Agent and dependencies](#install-the-alwaysai-device-agent-and-dependencies)
|
|
15
|
+
- [Provision the device](#provision-the-device)
|
|
16
|
+
- [Run an alwaysAI application on your device](#run-an-alwaysai-application-on-your-device)
|
|
17
|
+
- [Enable Analytics through the alwaysAI Device Agent](#enable-analytics-through-the-alwaysai-device-agent)
|
|
18
|
+
- [Configure the Device Agent](#configure-the-device-agent)
|
|
19
|
+
- [Configure the Application](#configure-the-application)
|
|
20
|
+
- [Using the alwaysAI Console](#using-the-alwaysai-console)
|
|
21
|
+
- [In a docker-compose.yaml](#in-a-docker-composeyaml)
|
|
22
|
+
- [Enable Publishing to Cloud](#enable-publishing-to-cloud)
|
|
23
|
+
- [In your app source](#in-your-app-source)
|
|
24
|
+
- [From the alwaysAI Console](#from-the-alwaysai-console)
|
|
25
|
+
- [Capture the Analytics Stream](#capture-the-analytics-stream)
|
|
26
|
+
- [The alwaysAI Device Agent Command Line interface](#the-alwaysai-device-agent-command-line-interface)
|
|
27
|
+
- [Install the application on the device](#install-the-application-on-the-device)
|
|
28
|
+
- [Control the application](#control-the-application)
|
|
29
|
+
- [Manage application models](#manage-application-models)
|
|
30
|
+
- [Update models to new version of the same model ID](#update-models-to-new-version-of-the-same-model-id)
|
|
31
|
+
- [Replace models for an application for new ID](#replace-models-for-an-application-for-new-id)
|
|
32
|
+
- [Manually download a model package](#manually-download-a-model-package)
|
|
33
|
+
- [Set and update environment variables](#set-and-update-environment-variables)
|
|
34
|
+
- [Device Cleaning and Uninstalling](#device-cleaning-and-uninstalling)
|
|
35
|
+
- [Stop PM2 instance of the device agent](#stop-pm2-instance-of-the-device-agent)
|
|
36
|
+
- [Remove the device configuration](#remove-the-device-configuration)
|
|
37
|
+
- [Uninstall the Device Agent from the device](#uninstall-the-device-agent-from-the-device)
|
|
38
|
+
- [Remove the device from the alwaysAI Dashboard](#remove-the-device-from-the-alwaysai-dashboard)
|
|
39
|
+
|
|
40
|
+
|
|
12
41
|
## System Requirements & Prerequisites
|
|
13
42
|
|
|
14
43
|
* Supported OS:
|
|
15
|
-
* Debian
|
|
16
|
-
* Ubuntu 20.04, 18.04
|
|
17
|
-
* NVIDIA Jetpack 4.6.x
|
|
44
|
+
* Debian Bookworm, Bullseye
|
|
45
|
+
* Ubuntu 23.10, 22.04, 20.04, 18.04
|
|
46
|
+
* NVIDIA Jetpack 5.1, 4.6.x
|
|
18
47
|
* Supported target architecture
|
|
19
48
|
* amd64
|
|
20
49
|
* aarch64
|
|
21
|
-
|
|
22
|
-
* `docker`
|
|
50
|
+
* `docker` >= 19.03
|
|
51
|
+
* `docker-compose` >= 1.29.0; < 2.0.0
|
|
23
52
|
* `curl` installed (required to download provisioning scripts)
|
|
24
53
|
* Passwordless `sudo` for `npm` if using `pm2`
|
|
25
54
|
* Passwordless `sudo` for `/sbin/shutdown` for device restart functionality
|
|
26
55
|
|
|
27
|
-
To enable passwordless `sudo` for `npm` for the current
|
|
28
|
-
and add the following
|
|
56
|
+
To enable passwordless `sudo` for `npm` and `sbin/shutdown` for the current
|
|
57
|
+
user, run `sudo visudo` and add the following lines to the end of the file:
|
|
29
58
|
|
|
30
59
|
```bash
|
|
31
60
|
<username> <hostname> = (root) NOPASSWD: /usr/bin/npm
|
|
61
|
+
<username> <hostname> = (root) NOPASSWD: /sbin/shutdown
|
|
32
62
|
```
|
|
33
63
|
|
|
64
|
+
On a linux system `username` can be obtained by typing `whoami` into the terminal. Similarly, if you don't know your `hostname` you can simply type `hostname`.
|
|
65
|
+
|
|
34
66
|
## Provision Device
|
|
35
67
|
|
|
36
68
|
### Install the alwaysAI Device Agent and dependencies
|
|
@@ -122,12 +154,14 @@ Now you can deploy to your device from the alwaysAI Dashboard.
|
|
|
122
154
|
## Enable Analytics through the alwaysAI Device Agent
|
|
123
155
|
|
|
124
156
|
### Configure the Device Agent
|
|
157
|
+
|
|
125
158
|
You can send information from your device to the alwaysAI cloud securely using
|
|
126
159
|
the Device Agent. These instructions assume you have provisioned your device
|
|
127
160
|
using the default script parameters and have the Device agent running (i.e. the
|
|
128
161
|
provisioning script was not run with the `--provision-only` flag set).
|
|
129
162
|
|
|
130
|
-
|
|
163
|
+
The Device Agent must be configured to enable Analytics Pass-through support.
|
|
164
|
+
Confirm that the `ALWAYSAI_ANALYTICS_PASSTHROUGH` environment variable is set:
|
|
131
165
|
|
|
132
166
|
```bash
|
|
133
167
|
$ pm2 env 0 | grep ALWAYSAI
|
|
@@ -147,31 +181,38 @@ CONTAINER ID IMAGE COMMAND CREATED STATUS
|
|
|
147
181
|
|
|
148
182
|
### Configure the Application
|
|
149
183
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
184
|
+
First, you must publish analytics from your application using
|
|
185
|
+
`publish_analytics`. Whenever this command is used, it will publish the contents
|
|
186
|
+
of the analytics message to the analytics endpoints you have enabled. Each core
|
|
187
|
+
computer vision service has it's own `publish_analytics` method, which is called
|
|
188
|
+
on the instance of the class. Or, you can published any JSON-serializable
|
|
189
|
+
message with `edgeiq.publish_analytics()`. For instance, to publish object
|
|
190
|
+
detection results you can use:
|
|
157
191
|
|
|
158
192
|
```python
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
193
|
+
results = obj_detect.detect_objects(frame, confidence_level=.5)
|
|
194
|
+
try:
|
|
195
|
+
obj_detect.publish_analytics(results, tag=frame_count)
|
|
196
|
+
except edgeiq.PublishError as e:
|
|
197
|
+
print(e)
|
|
162
198
|
```
|
|
163
|
-
You can import any other modules after `edgeiq`, but the other orders must be
|
|
164
|
-
maintained.
|
|
165
199
|
|
|
166
|
-
|
|
200
|
+
Next, the `ALWAYSAI_CONNECT_TO_DEVICE_AGENT` environment variable must be set.
|
|
201
|
+
There are a few options:
|
|
167
202
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
203
|
+
#### Using the alwaysAI Console
|
|
204
|
+
|
|
205
|
+
Once an app has been deployed to a device, you can add, modify, and remove
|
|
206
|
+
environment variables using the environment variable tab of the app details
|
|
207
|
+
panel. Add a new environment variable called `ALWAYSAI_CONNECT_TO_DEVICE_AGENT`
|
|
208
|
+
and set it to `1`.
|
|
171
209
|
|
|
172
210
|
#### In a docker-compose.yaml
|
|
173
211
|
|
|
174
|
-
Add the following section to the service for your app
|
|
212
|
+
Add the following section to the service for your app in a `docker-compose.yaml`
|
|
213
|
+
file. If you don't yet have a `docker-compose.yaml` file in your app source, you
|
|
214
|
+
can generate a template one to edit by running the `aai app generate docker-compose`
|
|
215
|
+
command. Note that this will only take effect in production deployments.
|
|
175
216
|
|
|
176
217
|
```bash
|
|
177
218
|
environment:
|
|
@@ -180,12 +221,31 @@ environment:
|
|
|
180
221
|
|
|
181
222
|
### Enable Publishing to Cloud
|
|
182
223
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
224
|
+
The application analytics configuration can be set either in the app source when
|
|
225
|
+
you publish it or once the app has been deployed to a device using the alwaysAI
|
|
226
|
+
Console.
|
|
227
|
+
|
|
228
|
+
#### In your app source
|
|
229
|
+
|
|
230
|
+
To enable cloud publishing in your app configurations run the following commands
|
|
231
|
+
in your app directory:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
$ aai app enable-cloud-publish
|
|
235
|
+
$ aai app publish
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
This can be added to the app release process so that cloud publishing is always
|
|
239
|
+
enabled for production deployments.
|
|
240
|
+
|
|
241
|
+
#### From the alwaysAI Console
|
|
242
|
+
|
|
243
|
+
In the alwaysAI Console, navigate to the device the application has been
|
|
244
|
+
deployed to and open the configuration tab in the app panel. In the app config,
|
|
245
|
+
add an `analytics` section that looks like the following:
|
|
186
246
|
|
|
187
247
|
```json
|
|
188
|
-
"analytics: {
|
|
248
|
+
"analytics": {
|
|
189
249
|
"enable_cloud_publish": true
|
|
190
250
|
}
|
|
191
251
|
```
|
|
@@ -206,30 +266,12 @@ So, a valid `alwaysai.app.json` for publishing analytics might look like this:
|
|
|
206
266
|
}
|
|
207
267
|
```
|
|
208
268
|
|
|
209
|
-
|
|
210
|
-
command is used, it will publish the contents of the analytics message to the
|
|
211
|
-
cloud -- you can choose to call this every frame, or once every event
|
|
212
|
-
occurrence, however your app is designed. Each core computer vision service has
|
|
213
|
-
it's own `publish_analytics` method, which is called on the instance of the
|
|
214
|
-
class. Or, you can published a JSON-serializable message with
|
|
215
|
-
`edgeiq.publish_analytics()`. For instance, to publish object detection results
|
|
216
|
-
you can use:
|
|
269
|
+
Press "Update" to update the application configuration on the device.
|
|
217
270
|
|
|
218
|
-
|
|
219
|
-
results = obj_detect.detect_objects(frame, confidence_level=.5)
|
|
220
|
-
try:
|
|
221
|
-
obj_detect.publish_analytics(results, tag=frame_count)
|
|
222
|
-
except edgeiq.PublishError as e:
|
|
223
|
-
print(e)
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
Finally, make sure to save all of your changes, and publish your application with
|
|
227
|
-
|
|
228
|
-
```bash
|
|
229
|
-
$ aai app publish
|
|
230
|
-
```
|
|
271
|
+
### Capture the Analytics Stream
|
|
231
272
|
|
|
232
|
-
You can test that analytics are being viewed with `wscat`, using your
|
|
273
|
+
You can test that analytics are being viewed with `wscat`, using your
|
|
274
|
+
application's project ID and a secure API key:
|
|
233
275
|
|
|
234
276
|
```bash
|
|
235
277
|
$ wscat -c "wss://analytics.alwaysai.co?projectId=[PROJECT_ID]&apiKey=[API_KEY]"
|
|
@@ -239,7 +281,8 @@ Please contact the alwaysAI team if you need a secure API key.
|
|
|
239
281
|
|
|
240
282
|
## The alwaysAI Device Agent Command Line interface
|
|
241
283
|
|
|
242
|
-
The Device Agent can also be used directly on the device with it's command line
|
|
284
|
+
The Device Agent can also be used directly on the device with it's command line
|
|
285
|
+
interface.
|
|
243
286
|
|
|
244
287
|
```bash
|
|
245
288
|
$ aai-agent --help
|
|
@@ -249,30 +292,29 @@ Usage: aai-agent <subcommand> ...
|
|
|
249
292
|
|
|
250
293
|
Subcommands:
|
|
251
294
|
|
|
252
|
-
login
|
|
253
|
-
app list
|
|
254
|
-
app install
|
|
255
|
-
app status
|
|
256
|
-
app start
|
|
257
|
-
app stop
|
|
258
|
-
app restart
|
|
259
|
-
app logs
|
|
260
|
-
app uninstall
|
|
261
|
-
app show-models
|
|
262
|
-
app add-model
|
|
263
|
-
app remove-model
|
|
264
|
-
app get-all-envs
|
|
265
|
-
app set-env
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
$ export ALWAYSAI_LOG_TO_CONSOLE=1
|
|
295
|
+
login : Login to alwaysAI (this is meant for scripted environments)
|
|
296
|
+
app list : List all installed apps
|
|
297
|
+
app install : Install an alwaysAI app from a project
|
|
298
|
+
app status : Get the status of an installed alwaysAI app
|
|
299
|
+
app start : Start an installed alwaysAI app
|
|
300
|
+
app stop : Stop a running alwaysAI app
|
|
301
|
+
app restart : Restart running alwaysAI app
|
|
302
|
+
app logs : Get logs for an application
|
|
303
|
+
app uninstall : Remove an alwaysAI app
|
|
304
|
+
app show-models : Show the application models
|
|
305
|
+
app add-model : Add a model to an alwaysAI app
|
|
306
|
+
app remove-model : Remove a model from an alwaysAI app
|
|
307
|
+
app get-all-envs : Get environment variables for an application
|
|
308
|
+
app set-env : Set environment variables for a service
|
|
309
|
+
app get-analytics-cfg : Get analytics configuration for an application
|
|
310
|
+
app set-analytics-cfg : Set analytics configuration for an application. Note that this resets the config so all desired options must be set
|
|
311
|
+
app get-shadow : Get the current shadow
|
|
312
|
+
app update-shadow : Update the shadow with the current application configuration
|
|
313
|
+
device init : Initialize device
|
|
314
|
+
device get-info : Get device info
|
|
315
|
+
device clean : Remove all provisioning files
|
|
316
|
+
device restart : Restart the device
|
|
317
|
+
get-model-package : Download and unpack a model package
|
|
276
318
|
```
|
|
277
319
|
|
|
278
320
|
### Install the application on the device
|
|
@@ -350,7 +392,8 @@ obj_detect = edgeiq.ObjectDetection(edgeiq._globals.MODEL_ID_LIST[0])
|
|
|
350
392
|
|
|
351
393
|
#### Manually download a model package
|
|
352
394
|
|
|
353
|
-
To download a model package from the alwaysAI cloud and unpack to a specific
|
|
395
|
+
To download a model package from the alwaysAI cloud and unpack to a specific
|
|
396
|
+
directory, run:
|
|
354
397
|
|
|
355
398
|
```bash
|
|
356
399
|
$ aai-agent get-model-package <model ID> [--path <destination path>]
|
|
@@ -380,11 +423,16 @@ service run the following command on the device:
|
|
|
380
423
|
```bash
|
|
381
424
|
$ aai-agent app set-env TEST_ENV=1 --project <project_id> --service alwaysai
|
|
382
425
|
```
|
|
426
|
+
|
|
383
427
|
## Device Cleaning and Uninstalling
|
|
428
|
+
|
|
384
429
|
In order to fully clean the device and uninstall the agent, use the following steps:
|
|
385
430
|
|
|
386
|
-
###
|
|
387
|
-
|
|
431
|
+
### Stop PM2 instance of the device agent
|
|
432
|
+
|
|
433
|
+
Using `pm2 list`, display the list of current pm2 instances. The output should
|
|
434
|
+
look like this:
|
|
435
|
+
|
|
388
436
|
```bash
|
|
389
437
|
$ pm2 list
|
|
390
438
|
┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
|
|
@@ -393,19 +441,25 @@ $ pm2 list
|
|
|
393
441
|
│ 0 │ aai-agent │ fork │ 15 │ online │ 0% │ 2.8mb │
|
|
394
442
|
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
|
|
395
443
|
```
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
444
|
+
Run the following commands where `<id>` is the ID for `aai-agent`.
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
$ pm2 stop <id>
|
|
448
|
+
$ pm2 delete 0
|
|
449
|
+
$ pm2 flush
|
|
450
|
+
$ pm2 unstartup
|
|
451
|
+
```
|
|
452
|
+
### Remove the device configuration
|
|
453
|
+
|
|
454
|
+
Open a new terminal and run `aai-agent device clean`.
|
|
400
455
|
|
|
401
|
-
###
|
|
402
|
-
Open a new terminal.
|
|
403
|
-
Run `aai-agent device clean`
|
|
456
|
+
### Uninstall the Device Agent from the device
|
|
404
457
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
458
|
+
Run `sudo npm uninstall -g @alwaysai/device-agent`. Verify successful removal by
|
|
459
|
+
running `aai-agent`. You should see a response similar to
|
|
460
|
+
`-bash:/usr/bin/aai-agent: No such file or directory`.
|
|
408
461
|
|
|
409
|
-
###
|
|
410
|
-
Go to https://console.alwaysai.co/dashboard/devices, Go to Devices, find the device in question, and remove the device using the trashcan icon.
|
|
462
|
+
### Remove the device from the alwaysAI Dashboard
|
|
411
463
|
|
|
464
|
+
Go to the [Devices](https://console.alwaysai.co/dashboard/devices) tab, find the
|
|
465
|
+
device in question, and remove the device using the trashcan icon.
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { AgentConfigFile } from '../infrastructure/agent-config';
|
|
2
2
|
import { readDockerCompose, writeDockerCompose } from './config';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
convertStringEnvsToKeyVal,
|
|
5
|
+
getAllEnvs,
|
|
6
|
+
setEnv
|
|
7
|
+
} from './environment-variables';
|
|
4
8
|
import { isAppStarted, restartApp } from './status';
|
|
5
9
|
import { buildApp, requireAppReady } from './utils';
|
|
6
10
|
|
|
@@ -132,7 +136,7 @@ describe('Test environment variable get and set', () => {
|
|
|
132
136
|
dockerCompose: {
|
|
133
137
|
services: {
|
|
134
138
|
alwaysai: {
|
|
135
|
-
environment: ['TEST1=
|
|
139
|
+
environment: ['TEST1=2', 'TEST2=2']
|
|
136
140
|
}
|
|
137
141
|
}
|
|
138
142
|
}
|
|
@@ -158,7 +162,7 @@ describe('Test environment variable get and set', () => {
|
|
|
158
162
|
dockerCompose: {
|
|
159
163
|
services: {
|
|
160
164
|
alwaysai: {
|
|
161
|
-
environment: ['TEST1=
|
|
165
|
+
environment: ['TEST1=2', 'TEST2=4']
|
|
162
166
|
},
|
|
163
167
|
other: {
|
|
164
168
|
environment: ['OTHER_TEST=1']
|
|
@@ -167,5 +171,41 @@ describe('Test environment variable get and set', () => {
|
|
|
167
171
|
}
|
|
168
172
|
});
|
|
169
173
|
});
|
|
174
|
+
test('Remove one env', async () => {
|
|
175
|
+
const environment = ['TEST1=1', 'TEST2=2'];
|
|
176
|
+
const compose = {
|
|
177
|
+
services: {
|
|
178
|
+
alwaysai: { environment }
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
jest.mocked(readDockerCompose).mockResolvedValue(compose);
|
|
182
|
+
|
|
183
|
+
const envVars = { alwaysai: { TEST1: null } };
|
|
184
|
+
await setEnv({ projectId: projectId1, envVars });
|
|
185
|
+
expect(jest.mocked(readDockerCompose)).toBeCalledWith({
|
|
186
|
+
projectId: projectId1
|
|
187
|
+
});
|
|
188
|
+
expect(jest.mocked(writeDockerCompose)).toBeCalledWith({
|
|
189
|
+
projectId: projectId1,
|
|
190
|
+
dockerCompose: {
|
|
191
|
+
services: {
|
|
192
|
+
alwaysai: {
|
|
193
|
+
environment: ['TEST1=', 'TEST2=2']
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
describe('Test helpers', () => {
|
|
201
|
+
test('convertStringEnvsToKeyVal', () => {
|
|
202
|
+
const stringEnvs: string[] = ['TEST1=', 'TEST2=test2', 'TEST3=null'];
|
|
203
|
+
const envVars = convertStringEnvsToKeyVal(stringEnvs);
|
|
204
|
+
expect(envVars).toEqual({
|
|
205
|
+
TEST1: '',
|
|
206
|
+
TEST2: 'test2',
|
|
207
|
+
TEST3: 'null'
|
|
208
|
+
});
|
|
209
|
+
});
|
|
170
210
|
});
|
|
171
211
|
});
|
|
@@ -4,6 +4,7 @@ import { buildApp, getAppDir, requireAppReady } from './utils';
|
|
|
4
4
|
import { logger } from '../util/logger';
|
|
5
5
|
import { AgentConfigFile } from '../infrastructure/agent-config';
|
|
6
6
|
import { isAppStarted, restartApp } from './status';
|
|
7
|
+
import { replaceFalseyWithNull } from '../util/parsing';
|
|
7
8
|
|
|
8
9
|
export interface EnvVars {
|
|
9
10
|
[service: string]: {
|
|
@@ -37,26 +38,32 @@ export async function setEnv(props: { projectId: string; envVars: EnvVars }) {
|
|
|
37
38
|
)}`
|
|
38
39
|
);
|
|
39
40
|
}
|
|
40
|
-
|
|
41
|
+
|
|
42
|
+
const service = composeParsed['services'][s];
|
|
43
|
+
const oldEnv: string[] | undefined = service['environment'];
|
|
44
|
+
|
|
45
|
+
const newEnvVarsObj = {};
|
|
46
|
+
oldEnv?.forEach((envVarStr: string) => {
|
|
47
|
+
const envVarSplit = envVarStr.split('=');
|
|
48
|
+
const key = envVarSplit[0];
|
|
49
|
+
const value = envVarSplit[1];
|
|
50
|
+
newEnvVarsObj[key] = value;
|
|
51
|
+
});
|
|
52
|
+
|
|
41
53
|
for (const envVar of Object.keys(envVars[s])) {
|
|
42
|
-
|
|
54
|
+
newEnvVarsObj[envVar] = envVars[s][envVar];
|
|
43
55
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// environment variable list, which will override but not replace
|
|
51
|
-
// previous instances of the same environment variable.
|
|
52
|
-
if ('environment' in service) {
|
|
53
|
-
const environment: string[] = service['environment'];
|
|
54
|
-
composeParsed['services'][s]['environment'] =
|
|
55
|
-
environment.concat(envVarList);
|
|
56
|
-
} else {
|
|
57
|
-
composeParsed['services'][s]['environment'] = envVarList;
|
|
56
|
+
|
|
57
|
+
const envVarList: string[] = [];
|
|
58
|
+
for (const envVar of Object.keys(newEnvVarsObj)) {
|
|
59
|
+
envVarList.push(
|
|
60
|
+
`${envVar}=${newEnvVarsObj[envVar] ? newEnvVarsObj[envVar] : ''}`
|
|
61
|
+
);
|
|
58
62
|
}
|
|
63
|
+
|
|
64
|
+
service['environment'] = envVarList;
|
|
59
65
|
}
|
|
66
|
+
|
|
60
67
|
await writeDockerCompose({ projectId, dockerCompose: composeParsed });
|
|
61
68
|
|
|
62
69
|
const appDir = getAppDir(projectId);
|
|
@@ -79,7 +86,7 @@ export async function setEnv(props: { projectId: string; envVars: EnvVars }) {
|
|
|
79
86
|
);
|
|
80
87
|
}
|
|
81
88
|
|
|
82
|
-
|
|
89
|
+
export function convertStringEnvsToKeyVal(stringEnvs: string[]) {
|
|
83
90
|
const envVars = {};
|
|
84
91
|
stringEnvs.forEach((env: string) => {
|
|
85
92
|
const keyVal = env.split('=');
|
|
@@ -117,17 +124,20 @@ export async function getAllEnvs(props: {
|
|
|
117
124
|
envFileLines = envFileLines.filter((v) => {
|
|
118
125
|
return v !== '' && !v.includes('#');
|
|
119
126
|
});
|
|
120
|
-
const newEnvVars =
|
|
127
|
+
const newEnvVars = convertStringEnvsToKeyVal(envFileLines);
|
|
121
128
|
envVars[s] = { ...envVars[s], ...newEnvVars };
|
|
122
129
|
}
|
|
123
130
|
}
|
|
124
131
|
if ('environment' in service) {
|
|
125
132
|
const environment: string[] = service['environment'];
|
|
126
|
-
const newEnvVars =
|
|
133
|
+
const newEnvVars = convertStringEnvsToKeyVal(environment);
|
|
127
134
|
envVars[s] = { ...envVars[s], ...newEnvVars };
|
|
128
135
|
}
|
|
129
136
|
}
|
|
130
137
|
}
|
|
131
138
|
|
|
139
|
+
// Device shadow needs null to delete
|
|
140
|
+
replaceFalseyWithNull(envVars, true);
|
|
141
|
+
|
|
132
142
|
return envVars;
|
|
133
143
|
}
|
|
@@ -65,7 +65,7 @@ export async function installApp(props: {
|
|
|
65
65
|
if (!(await AgentConfigFile().isAppReady({ projectId }))) {
|
|
66
66
|
throw new Error('Application already has installation in progress!');
|
|
67
67
|
}
|
|
68
|
-
logger.info('Application is already installed, updating');
|
|
68
|
+
logger.info('Application is already installed, updating...');
|
|
69
69
|
|
|
70
70
|
// make sure the app is stopped if it's running, to clear the docker container.
|
|
71
71
|
if (await isAppStarted({ projectId })) {
|