@alwaysai/device-agent 1.4.0 → 2.0.0
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/config.d.ts.map +1 -1
- package/lib/application-control/config.js +10 -5
- package/lib/application-control/config.js.map +1 -1
- package/lib/application-control/environment-variables.d.ts +1 -5
- package/lib/application-control/environment-variables.d.ts.map +1 -1
- package/lib/application-control/environment-variables.js +9 -26
- package/lib/application-control/environment-variables.js.map +1 -1
- package/lib/application-control/environment-variables.test.js +27 -7
- package/lib/application-control/environment-variables.test.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.map +1 -1
- package/lib/application-control/install.js +9 -7
- package/lib/application-control/install.js.map +1 -1
- package/lib/application-control/models.d.ts +5 -11
- package/lib/application-control/models.d.ts.map +1 -1
- package/lib/application-control/models.js +27 -64
- package/lib/application-control/models.js.map +1 -1
- package/lib/application-control/status.d.ts.map +1 -1
- package/lib/application-control/status.js +10 -12
- package/lib/application-control/status.js.map +1 -1
- package/lib/application-control/utils.d.ts +0 -4
- package/lib/application-control/utils.d.ts.map +1 -1
- package/lib/application-control/utils.js +3 -26
- package/lib/application-control/utils.js.map +1 -1
- package/lib/cloud-connection/bootstrap-provision.js +3 -2
- package/lib/cloud-connection/bootstrap-provision.js.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts +11 -16
- package/lib/cloud-connection/device-agent-cloud-connection.d.ts.map +1 -1
- package/lib/cloud-connection/device-agent-cloud-connection.js +295 -246
- 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 +11 -9
- package/lib/cloud-connection/device-agent.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.d.ts +18 -27
- package/lib/cloud-connection/live-updates-handler.d.ts.map +1 -1
- package/lib/cloud-connection/live-updates-handler.js +58 -170
- package/lib/cloud-connection/live-updates-handler.js.map +1 -1
- package/lib/cloud-connection/live-updates-handler.test.js +76 -54
- package/lib/cloud-connection/live-updates-handler.test.js.map +1 -1
- package/lib/cloud-connection/passthrough-handler.d.ts +9 -4
- package/lib/cloud-connection/passthrough-handler.d.ts.map +1 -1
- package/lib/cloud-connection/passthrough-handler.js +95 -62
- package/lib/cloud-connection/passthrough-handler.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.d.ts +5 -1
- package/lib/cloud-connection/shadow-handler.d.ts.map +1 -1
- package/lib/cloud-connection/shadow-handler.js +63 -31
- package/lib/cloud-connection/shadow-handler.js.map +1 -1
- package/lib/cloud-connection/shadow-handler.test.js +45 -57
- package/lib/cloud-connection/shadow-handler.test.js.map +1 -1
- package/lib/cloud-connection/shadow.d.ts.map +1 -1
- package/lib/cloud-connection/shadow.js +2 -1
- package/lib/cloud-connection/shadow.js.map +1 -1
- package/lib/cloud-connection/transaction-manager.d.ts +7 -2
- package/lib/cloud-connection/transaction-manager.d.ts.map +1 -1
- package/lib/cloud-connection/transaction-manager.js +29 -29
- package/lib/cloud-connection/transaction-manager.js.map +1 -1
- package/lib/cloud-connection/transaction-manager.test.js +105 -3
- package/lib/cloud-connection/transaction-manager.test.js.map +1 -1
- package/lib/device-control/device-control.d.ts +14 -6
- package/lib/device-control/device-control.d.ts.map +1 -1
- package/lib/device-control/device-control.js +172 -72
- package/lib/device-control/device-control.js.map +1 -1
- package/lib/docker/docker-compose.d.ts +14 -0
- package/lib/docker/docker-compose.d.ts.map +1 -0
- package/lib/docker/docker-compose.js +57 -0
- package/lib/docker/docker-compose.js.map +1 -0
- package/lib/index.js +2 -5
- package/lib/index.js.map +1 -1
- package/lib/infrastructure/agent-config.d.ts +46 -14
- package/lib/infrastructure/agent-config.d.ts.map +1 -1
- package/lib/infrastructure/agent-config.js +36 -21
- package/lib/infrastructure/agent-config.js.map +1 -1
- package/lib/infrastructure/agent-config.test.js +6 -1
- package/lib/infrastructure/agent-config.test.js.map +1 -1
- package/lib/infrastructure/config-check-utility.d.ts +6 -0
- package/lib/infrastructure/config-check-utility.d.ts.map +1 -0
- package/lib/infrastructure/config-check-utility.js +67 -0
- package/lib/infrastructure/config-check-utility.js.map +1 -0
- package/lib/infrastructure/config-check-utility.test.d.ts +2 -0
- package/lib/infrastructure/config-check-utility.test.d.ts.map +1 -0
- package/lib/infrastructure/config-check-utility.test.js +109 -0
- package/lib/infrastructure/config-check-utility.test.js.map +1 -0
- package/lib/infrastructure/device-certificate.d.ts +10 -0
- package/lib/infrastructure/device-certificate.d.ts.map +1 -0
- package/lib/infrastructure/device-certificate.js +47 -0
- package/lib/infrastructure/device-certificate.js.map +1 -0
- package/lib/infrastructure/device-certificate.test.d.ts +2 -0
- package/lib/infrastructure/device-certificate.test.d.ts.map +1 -0
- package/lib/infrastructure/device-certificate.test.js +24 -0
- package/lib/infrastructure/device-certificate.test.js.map +1 -0
- package/lib/infrastructure/legacy-migration/legacy-file.test.d.ts +2 -0
- package/lib/infrastructure/legacy-migration/legacy-file.test.d.ts.map +1 -0
- package/lib/infrastructure/legacy-migration/legacy-file.test.js +61 -0
- package/lib/infrastructure/legacy-migration/legacy-file.test.js.map +1 -0
- package/lib/infrastructure/legacy-migration/legacy-files.d.ts +75 -0
- package/lib/infrastructure/legacy-migration/legacy-files.d.ts.map +1 -0
- package/lib/infrastructure/legacy-migration/legacy-files.js +75 -0
- package/lib/infrastructure/legacy-migration/legacy-files.js.map +1 -0
- package/lib/infrastructure/legacy-migration/legacy-migration.d.ts +6 -0
- package/lib/infrastructure/legacy-migration/legacy-migration.d.ts.map +1 -0
- package/lib/infrastructure/legacy-migration/legacy-migration.js +149 -0
- package/lib/infrastructure/legacy-migration/legacy-migration.js.map +1 -0
- package/lib/infrastructure/legacy-migration/legacy-migration.test.d.ts +2 -0
- package/lib/infrastructure/legacy-migration/legacy-migration.test.d.ts.map +1 -0
- package/lib/infrastructure/legacy-migration/legacy-migration.test.js +226 -0
- package/lib/infrastructure/legacy-migration/legacy-migration.test.js.map +1 -0
- package/lib/infrastructure/require-files-present-ready.test.d.ts +2 -0
- package/lib/infrastructure/require-files-present-ready.test.d.ts.map +1 -0
- package/lib/infrastructure/require-files-present-ready.test.js +44 -0
- package/lib/infrastructure/require-files-present-ready.test.js.map +1 -0
- package/lib/infrastructure/required-config-checks.d.ts +2 -0
- package/lib/infrastructure/required-config-checks.d.ts.map +1 -0
- package/lib/infrastructure/required-config-checks.js +30 -0
- package/lib/infrastructure/required-config-checks.js.map +1 -0
- package/lib/infrastructure/tokens-and-device-cfg.d.ts.map +1 -1
- package/lib/infrastructure/tokens-and-device-cfg.js +11 -8
- package/lib/infrastructure/tokens-and-device-cfg.js.map +1 -1
- package/lib/local-connection/rabbitmq-connection.d.ts.map +1 -1
- package/lib/local-connection/rabbitmq-connection.js +21 -21
- package/lib/local-connection/rabbitmq-connection.js.map +1 -1
- package/lib/secure-tunneling/secure-tunneling.d.ts +15 -23
- package/lib/secure-tunneling/secure-tunneling.d.ts.map +1 -1
- package/lib/secure-tunneling/secure-tunneling.js +52 -47
- package/lib/secure-tunneling/secure-tunneling.js.map +1 -1
- package/lib/secure-tunneling/secure-tunneling.test.js +29 -31
- package/lib/secure-tunneling/secure-tunneling.test.js.map +1 -1
- package/lib/subcommands/app/analytics.d.ts.map +1 -1
- package/lib/subcommands/app/analytics.js +1 -2
- package/lib/subcommands/app/analytics.js.map +1 -1
- package/lib/subcommands/app/env-vars.d.ts +4 -0
- package/lib/subcommands/app/env-vars.d.ts.map +1 -1
- package/lib/subcommands/app/env-vars.js +52 -6
- package/lib/subcommands/app/env-vars.js.map +1 -1
- package/lib/subcommands/app/index.d.ts.map +1 -1
- package/lib/subcommands/app/index.js +1 -3
- package/lib/subcommands/app/index.js.map +1 -1
- package/lib/subcommands/app/models.d.ts +0 -11
- package/lib/subcommands/app/models.d.ts.map +1 -1
- package/lib/subcommands/app/models.js +2 -58
- package/lib/subcommands/app/models.js.map +1 -1
- package/lib/subcommands/app/shadow.d.ts.map +1 -1
- package/lib/subcommands/app/shadow.js +6 -5
- package/lib/subcommands/app/shadow.js.map +1 -1
- package/lib/subcommands/app/version.d.ts.map +1 -1
- package/lib/subcommands/app/version.js +2 -4
- package/lib/subcommands/app/version.js.map +1 -1
- package/lib/subcommands/config.d.ts +2 -0
- package/lib/subcommands/config.d.ts.map +1 -0
- package/lib/subcommands/config.js +39 -0
- package/lib/subcommands/config.js.map +1 -0
- package/lib/subcommands/device/clean.d.ts +1 -1
- package/lib/subcommands/device/clean.d.ts.map +1 -1
- package/lib/subcommands/device/clean.js +25 -15
- package/lib/subcommands/device/clean.js.map +1 -1
- package/lib/subcommands/device/get-info.d.ts +2 -0
- package/lib/subcommands/device/get-info.d.ts.map +1 -0
- package/lib/subcommands/device/get-info.js +36 -0
- package/lib/subcommands/device/get-info.js.map +1 -0
- package/lib/subcommands/device/index.d.ts.map +1 -1
- package/lib/subcommands/device/index.js +13 -2
- package/lib/subcommands/device/index.js.map +1 -1
- package/lib/subcommands/device/init.d.ts +5 -0
- package/lib/subcommands/device/init.d.ts.map +1 -0
- package/lib/subcommands/device/{device.js → init.js} +10 -49
- package/lib/subcommands/device/init.js.map +1 -0
- package/lib/subcommands/device/migrate.d.ts +2 -0
- package/lib/subcommands/device/migrate.d.ts.map +1 -0
- package/lib/subcommands/device/migrate.js +24 -0
- package/lib/subcommands/device/migrate.js.map +1 -0
- package/lib/subcommands/device/refresh.d.ts +2 -0
- package/lib/subcommands/device/refresh.d.ts.map +1 -0
- package/lib/subcommands/device/refresh.js +25 -0
- package/lib/subcommands/device/refresh.js.map +1 -0
- package/lib/subcommands/device/restart.d.ts +2 -0
- package/lib/subcommands/device/restart.d.ts.map +1 -0
- package/lib/subcommands/device/restart.js +14 -0
- package/lib/subcommands/device/restart.js.map +1 -0
- package/lib/subcommands/index.d.ts +1 -1
- package/lib/subcommands/index.d.ts.map +1 -1
- package/lib/subcommands/index.js +3 -1
- package/lib/subcommands/index.js.map +1 -1
- package/lib/subcommands/rabbitmq-connection.d.ts +1 -1
- package/lib/subcommands/rabbitmq-connection.d.ts.map +1 -1
- package/lib/util/aai-error.d.ts +12 -0
- package/lib/util/aai-error.d.ts.map +1 -0
- package/lib/util/aai-error.js +11 -0
- package/lib/util/aai-error.js.map +1 -0
- package/lib/util/aws-regions.d.ts +2 -0
- package/lib/util/aws-regions.d.ts.map +1 -0
- package/lib/util/{cloud-mode-ready.js → aws-regions.js} +2 -20
- package/lib/util/aws-regions.js.map +1 -0
- package/lib/util/check-for-updates.d.ts +3 -0
- package/lib/util/check-for-updates.d.ts.map +1 -0
- package/lib/util/check-for-updates.js +46 -0
- package/lib/util/check-for-updates.js.map +1 -0
- package/lib/util/clean-certs.d.ts.map +1 -1
- package/lib/util/clean-certs.js +5 -4
- package/lib/util/clean-certs.js.map +1 -1
- package/lib/util/directories.d.ts +4 -18
- package/lib/util/directories.d.ts.map +1 -1
- package/lib/util/directories.js +18 -32
- package/lib/util/directories.js.map +1 -1
- package/lib/util/file.d.ts +11 -0
- package/lib/util/file.d.ts.map +1 -0
- package/lib/util/file.js +127 -0
- package/lib/util/file.js.map +1 -0
- package/lib/util/file.test.d.ts +2 -0
- package/lib/util/file.test.d.ts.map +1 -0
- package/lib/util/file.test.js +87 -0
- package/lib/util/file.test.js.map +1 -0
- package/lib/util/get-device-id.d.ts.map +1 -1
- package/lib/util/get-device-id.js +7 -1
- package/lib/util/get-device-id.js.map +1 -1
- package/lib/util/http-client.js +3 -3
- package/lib/util/http-client.js.map +1 -1
- package/package.json +22 -19
- package/readme.md +15 -35
- package/src/application-control/config.ts +10 -13
- package/src/application-control/environment-variables.test.ts +28 -7
- package/src/application-control/environment-variables.ts +13 -40
- package/src/application-control/index.ts +3 -16
- package/src/application-control/install.ts +16 -10
- package/src/application-control/models.ts +40 -98
- package/src/application-control/status.ts +9 -7
- package/src/application-control/utils.ts +1 -29
- package/src/cloud-connection/bootstrap-provision.ts +7 -7
- package/src/cloud-connection/device-agent-cloud-connection.ts +647 -509
- package/src/cloud-connection/device-agent.ts +16 -7
- package/src/cloud-connection/live-updates-handler.test.ts +137 -64
- package/src/cloud-connection/live-updates-handler.ts +103 -234
- package/src/cloud-connection/passthrough-handler.ts +134 -75
- package/src/cloud-connection/shadow-handler.test.ts +45 -57
- package/src/cloud-connection/shadow-handler.ts +114 -56
- package/src/cloud-connection/shadow.ts +4 -1
- package/src/cloud-connection/transaction-manager.test.ts +127 -3
- package/src/cloud-connection/transaction-manager.ts +68 -39
- package/src/device-control/device-control.ts +179 -72
- package/src/docker/docker-compose.ts +61 -0
- package/src/index.ts +2 -6
- package/src/infrastructure/agent-config.test.ts +9 -2
- package/src/infrastructure/agent-config.ts +45 -46
- package/src/infrastructure/config-check-utility.test.ts +154 -0
- package/src/infrastructure/config-check-utility.ts +77 -0
- package/src/infrastructure/device-certificate.test.ts +40 -0
- package/src/infrastructure/device-certificate.ts +58 -0
- package/src/infrastructure/legacy-migration/legacy-file.test.ts +88 -0
- package/src/infrastructure/legacy-migration/legacy-files.ts +101 -0
- package/src/infrastructure/legacy-migration/legacy-migration.test.ts +396 -0
- package/src/infrastructure/legacy-migration/legacy-migration.ts +229 -0
- package/src/infrastructure/require-files-present-ready.test.ts +53 -0
- package/src/infrastructure/required-config-checks.ts +33 -0
- package/src/infrastructure/tokens-and-device-cfg.ts +12 -10
- package/src/local-connection/rabbitmq-connection.ts +28 -23
- package/src/secure-tunneling/secure-tunneling.test.ts +37 -39
- package/src/secure-tunneling/secure-tunneling.ts +74 -69
- package/src/subcommands/app/analytics.ts +2 -4
- package/src/subcommands/app/env-vars.ts +72 -9
- package/src/subcommands/app/index.ts +3 -11
- package/src/subcommands/app/models.ts +5 -81
- package/src/subcommands/app/shadow.ts +6 -5
- package/src/subcommands/app/version.ts +3 -4
- package/src/subcommands/config.ts +42 -0
- package/src/subcommands/device/clean.ts +32 -18
- package/src/subcommands/device/get-info.ts +49 -0
- package/src/subcommands/device/index.ts +13 -2
- package/src/subcommands/device/{device.ts → init.ts} +11 -69
- package/src/subcommands/device/migrate.ts +20 -0
- package/src/subcommands/device/refresh.ts +23 -0
- package/src/subcommands/device/restart.ts +11 -0
- package/src/subcommands/index.ts +3 -1
- package/src/util/aai-error.ts +20 -0
- package/src/util/{cloud-mode-ready.ts → aws-regions.ts} +0 -24
- package/src/util/check-for-updates.ts +53 -0
- package/src/util/clean-certs.ts +8 -4
- package/src/util/directories.ts +23 -67
- package/src/util/file.test.ts +90 -0
- package/src/util/file.ts +156 -0
- package/src/util/get-device-id.ts +7 -7
- package/src/util/http-client.ts +2 -2
- package/lib/docker/docker-compose-cmd.d.ts +0 -5
- package/lib/docker/docker-compose-cmd.d.ts.map +0 -1
- package/lib/docker/docker-compose-cmd.js +0 -16
- package/lib/docker/docker-compose-cmd.js.map +0 -1
- package/lib/subcommands/device/device.d.ts +0 -7
- package/lib/subcommands/device/device.d.ts.map +0 -1
- package/lib/subcommands/device/device.js.map +0 -1
- package/lib/util/cloud-mode-ready.d.ts +0 -3
- package/lib/util/cloud-mode-ready.d.ts.map +0 -1
- package/lib/util/cloud-mode-ready.js.map +0 -1
- package/lib/util/download-file.d.ts +0 -6
- package/lib/util/download-file.d.ts.map +0 -1
- package/lib/util/download-file.js +0 -25
- package/lib/util/download-file.js.map +0 -1
- package/lib/util/fetch-with-timeout.d.ts +0 -4
- package/lib/util/fetch-with-timeout.d.ts.map +0 -1
- package/lib/util/fetch-with-timeout.js +0 -30
- package/lib/util/fetch-with-timeout.js.map +0 -1
- package/lib/util/parsing.d.ts +0 -2
- package/lib/util/parsing.d.ts.map +0 -1
- package/lib/util/parsing.js +0 -17
- package/lib/util/parsing.js.map +0 -1
- package/lib/util/safe-rimraf.d.ts +0 -2
- package/lib/util/safe-rimraf.d.ts.map +0 -1
- package/lib/util/safe-rimraf.js +0 -16
- package/lib/util/safe-rimraf.js.map +0 -1
- package/src/docker/docker-compose-cmd.ts +0 -15
- package/src/util/download-file.ts +0 -25
- package/src/util/fetch-with-timeout.ts +0 -35
- package/src/util/parsing.ts +0 -11
- package/src/util/safe-rimraf.ts +0 -14
|
@@ -4,7 +4,10 @@ import * as osu from 'node-os-utils';
|
|
|
4
4
|
import * as si from 'systeminformation';
|
|
5
5
|
import { exec } from 'child_process';
|
|
6
6
|
import { promisify } from 'util';
|
|
7
|
+
import { JsSpawner, stringifyError } from 'alwaysai/lib/util';
|
|
7
8
|
import { DeviceStatsPayload } from '@alwaysai/device-agent-schemas';
|
|
9
|
+
import { getDeviceAgentVersion } from '../util/check-for-updates';
|
|
10
|
+
import AaiError from '../util/aai-error';
|
|
8
11
|
|
|
9
12
|
const exec_promise = promisify(exec);
|
|
10
13
|
|
|
@@ -12,63 +15,84 @@ const exec_promise = promisify(exec);
|
|
|
12
15
|
export async function getCpuDetails(): Promise<
|
|
13
16
|
DeviceStatsPayload['cpuDetails']
|
|
14
17
|
> {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
let cpuFree, cpuTemp;
|
|
19
|
+
try {
|
|
20
|
+
[cpuFree, cpuTemp] = await Promise.all([
|
|
21
|
+
osu.cpu.free(),
|
|
22
|
+
si.cpuTemperature()
|
|
23
|
+
]);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
logger.error('Error fetching CPU details:', error);
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
usedPerc: cpuFree !== null ? 100 - cpuFree : undefined,
|
|
31
|
+
temperature: cpuTemp?.main !== null ? cpuTemp.main : undefined
|
|
32
|
+
};
|
|
21
33
|
}
|
|
22
34
|
|
|
23
35
|
export async function getDiskDetails(): Promise<
|
|
24
36
|
DeviceStatsPayload['diskDetails']
|
|
25
37
|
> {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
let driveInfo;
|
|
39
|
+
try {
|
|
40
|
+
// Types incorrectly specify diskname as required instead of optional
|
|
41
|
+
// @ts-expect-error
|
|
42
|
+
driveInfo = await osu.drive.info();
|
|
43
|
+
} catch (error) {
|
|
44
|
+
logger.error('Error fetching disk details:', error);
|
|
45
|
+
return {};
|
|
34
46
|
}
|
|
35
|
-
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
usedGb:
|
|
50
|
+
driveInfo?.usedGb !== null ? parseFloat(driveInfo.usedGb) : undefined,
|
|
51
|
+
freeGb:
|
|
52
|
+
driveInfo?.freeGb !== null ? parseFloat(driveInfo.freeGb) : undefined
|
|
53
|
+
};
|
|
36
54
|
}
|
|
37
55
|
|
|
38
56
|
export async function getMemDetails(): Promise<
|
|
39
57
|
DeviceStatsPayload['memDetails']
|
|
40
58
|
> {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
59
|
+
let memInfo;
|
|
60
|
+
try {
|
|
61
|
+
memInfo = await osu.mem.info();
|
|
62
|
+
} catch (error) {
|
|
63
|
+
logger.error('Error fetching memory details:', error);
|
|
64
|
+
return {};
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
usedMb: memInfo?.usedMemMb !== null ? memInfo.usedMemMb : undefined,
|
|
68
|
+
freeMb: memInfo?.freeMemMb !== null ? memInfo.freeMemMb : undefined
|
|
69
|
+
};
|
|
46
70
|
}
|
|
47
71
|
|
|
48
|
-
// System information
|
|
72
|
+
// System information casted to a string
|
|
49
73
|
export async function getOsInfo() {
|
|
50
74
|
const osInfo = await si.osInfo();
|
|
51
75
|
return {
|
|
52
|
-
platform: osInfo.platform,
|
|
53
|
-
distro: osInfo.distro,
|
|
54
|
-
release: osInfo.release,
|
|
55
|
-
kernel: osInfo.kernel,
|
|
56
|
-
architecture: osInfo.arch,
|
|
57
|
-
hostname: osInfo.hostname
|
|
76
|
+
platform: String(osInfo.platform),
|
|
77
|
+
distro: String(osInfo.distro),
|
|
78
|
+
release: String(osInfo.release),
|
|
79
|
+
kernel: String(osInfo.kernel),
|
|
80
|
+
architecture: String(osInfo.arch),
|
|
81
|
+
hostname: String(osInfo.hostname)
|
|
58
82
|
};
|
|
59
83
|
}
|
|
60
84
|
|
|
61
85
|
export async function getCpuInfo() {
|
|
62
86
|
const cpuInfo = await si.cpu();
|
|
63
87
|
return {
|
|
64
|
-
manufacturer: cpuInfo.manufacturer,
|
|
65
|
-
brand: cpuInfo.brand,
|
|
66
|
-
vendor: cpuInfo.vendor,
|
|
67
|
-
model: cpuInfo.model,
|
|
68
|
-
cores: cpuInfo.cores,
|
|
69
|
-
physicalCores: cpuInfo.physicalCores,
|
|
70
|
-
efficiencyCores: cpuInfo.efficiencyCores,
|
|
71
|
-
processors: cpuInfo.processors
|
|
88
|
+
manufacturer: String(cpuInfo.manufacturer),
|
|
89
|
+
brand: String(cpuInfo.brand),
|
|
90
|
+
vendor: String(cpuInfo.vendor),
|
|
91
|
+
model: String(cpuInfo.model),
|
|
92
|
+
cores: String(cpuInfo.cores),
|
|
93
|
+
physicalCores: String(cpuInfo.physicalCores),
|
|
94
|
+
efficiencyCores: String(cpuInfo.efficiencyCores),
|
|
95
|
+
processors: String(cpuInfo.processors)
|
|
72
96
|
};
|
|
73
97
|
}
|
|
74
98
|
|
|
@@ -76,11 +100,11 @@ export async function getDiskInfo() {
|
|
|
76
100
|
const diskInfo = await si.diskLayout();
|
|
77
101
|
return {
|
|
78
102
|
drives: diskInfo.map((drive) => ({
|
|
79
|
-
device: drive.device,
|
|
80
|
-
type: drive.type,
|
|
81
|
-
name: drive.name,
|
|
82
|
-
vendor: drive.vendor,
|
|
83
|
-
size: drive.size
|
|
103
|
+
device: String(drive.device),
|
|
104
|
+
type: String(drive.type),
|
|
105
|
+
name: String(drive.name),
|
|
106
|
+
vendor: String(drive.vendor),
|
|
107
|
+
size: String(drive.size)
|
|
84
108
|
}))
|
|
85
109
|
};
|
|
86
110
|
}
|
|
@@ -88,14 +112,14 @@ export async function getDiskInfo() {
|
|
|
88
112
|
export async function getDeviceInfo() {
|
|
89
113
|
const deviceInfo = await si.system();
|
|
90
114
|
return {
|
|
91
|
-
manufacturer: deviceInfo.manufacturer,
|
|
92
|
-
model: deviceInfo.model,
|
|
93
|
-
version: deviceInfo.version,
|
|
115
|
+
manufacturer: String(deviceInfo.manufacturer),
|
|
116
|
+
model: String(deviceInfo.model),
|
|
117
|
+
version: String(deviceInfo.version),
|
|
94
118
|
serial:
|
|
95
119
|
deviceInfo.serial && deviceInfo.serial !== '-'
|
|
96
|
-
? deviceInfo.serial
|
|
120
|
+
? String(deviceInfo.serial)
|
|
97
121
|
: undefined,
|
|
98
|
-
virtual: deviceInfo.virtual
|
|
122
|
+
virtual: deviceInfo.virtual // this should be a boolean
|
|
99
123
|
};
|
|
100
124
|
}
|
|
101
125
|
|
|
@@ -106,53 +130,137 @@ export async function getNetworkInfo() {
|
|
|
106
130
|
? networkInterfaces.filter((iface: any) => iface.ip4 !== '127.0.0.1')[0]
|
|
107
131
|
: networkInterfaces;
|
|
108
132
|
return {
|
|
109
|
-
ipv4Address: defaultNetworkInterface.ip4,
|
|
110
|
-
ipv6Address: defaultNetworkInterface.ip6,
|
|
111
|
-
macAddress: defaultNetworkInterface.mac
|
|
133
|
+
ipv4Address: String(defaultNetworkInterface.ip4),
|
|
134
|
+
ipv6Address: String(defaultNetworkInterface.ip6),
|
|
135
|
+
macAddress: String(defaultNetworkInterface.mac)
|
|
112
136
|
};
|
|
113
137
|
}
|
|
114
138
|
|
|
139
|
+
export async function getDockerVersion() {
|
|
140
|
+
try {
|
|
141
|
+
const spawner = JsSpawner();
|
|
142
|
+
const result = JSON.parse(
|
|
143
|
+
await spawner.run({
|
|
144
|
+
exe: 'docker',
|
|
145
|
+
args: ['info', '--format', 'json']
|
|
146
|
+
})
|
|
147
|
+
);
|
|
148
|
+
return String(result.ClientInfo.Version);
|
|
149
|
+
} catch (e) {
|
|
150
|
+
logger.warn(`Cannot get Docker version!\n${stringifyError(e)}`);
|
|
151
|
+
return 'Not found';
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export async function getDockerComposeVersion() {
|
|
156
|
+
try {
|
|
157
|
+
const spawner = JsSpawner();
|
|
158
|
+
return await spawner.run({
|
|
159
|
+
exe: 'docker',
|
|
160
|
+
args: ['compose', 'version']
|
|
161
|
+
});
|
|
162
|
+
} catch (e) {
|
|
163
|
+
logger.warn(`Cannot get Docker Compose version!\n${stringifyError(e)}`);
|
|
164
|
+
return 'Not found';
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export async function getNpmVersion() {
|
|
169
|
+
try {
|
|
170
|
+
const spawner = JsSpawner();
|
|
171
|
+
return await spawner.run({
|
|
172
|
+
exe: 'npm',
|
|
173
|
+
args: ['--version']
|
|
174
|
+
});
|
|
175
|
+
} catch (e) {
|
|
176
|
+
logger.warn(`Cannot get npm version!\n${stringifyError(e)}`);
|
|
177
|
+
return 'Not found';
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export async function getNodeVersion() {
|
|
182
|
+
try {
|
|
183
|
+
const spawner = JsSpawner();
|
|
184
|
+
return await spawner.run({
|
|
185
|
+
exe: 'node',
|
|
186
|
+
args: ['-v']
|
|
187
|
+
});
|
|
188
|
+
} catch (e) {
|
|
189
|
+
logger.warn(`Cannot get Node version!\n${stringifyError(e)}`);
|
|
190
|
+
return 'Not found';
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
115
194
|
export async function getPackageVersions() {
|
|
116
|
-
|
|
117
|
-
const agentJson = require('../../package.json');
|
|
195
|
+
const agentVersion = await getDeviceAgentVersion();
|
|
118
196
|
// eslint-disable-next-line
|
|
119
197
|
const deviceAgentSchemasJson = require('../../node_modules/@alwaysai/device-agent-schemas/package.json');
|
|
198
|
+
|
|
199
|
+
const [dockerVersion, dockerComposeVersion, nodeVersion, npmVersion] =
|
|
200
|
+
await Promise.all([
|
|
201
|
+
getDockerVersion(),
|
|
202
|
+
getDockerComposeVersion(),
|
|
203
|
+
getNodeVersion(),
|
|
204
|
+
getNpmVersion()
|
|
205
|
+
]);
|
|
206
|
+
|
|
120
207
|
return {
|
|
121
|
-
agent:
|
|
122
|
-
deviceAgentSchemas: deviceAgentSchemasJson.version
|
|
208
|
+
agent: agentVersion,
|
|
209
|
+
deviceAgentSchemas: deviceAgentSchemasJson.version,
|
|
210
|
+
docker: dockerVersion,
|
|
211
|
+
dockerCompose: dockerComposeVersion,
|
|
212
|
+
node: nodeVersion,
|
|
213
|
+
npm: npmVersion
|
|
123
214
|
};
|
|
124
215
|
}
|
|
125
216
|
|
|
126
217
|
export async function getLastBootTime() {
|
|
127
218
|
try {
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (test.stderr) {
|
|
132
|
-
logger.error(`Stderr when getting last boot time: ${test.stderr}`);
|
|
219
|
+
const out = await exec_promise('journalctl --list-boots');
|
|
220
|
+
if (!out || out.stderr) {
|
|
221
|
+
logger.error(`Stderr when getting last boot time: ${out.stderr}`);
|
|
133
222
|
return undefined;
|
|
134
223
|
}
|
|
135
|
-
|
|
224
|
+
|
|
225
|
+
const bootTimes = out.stdout.split('\n');
|
|
226
|
+
const latestBootStdout = (bootTimes.pop() || bootTimes.pop())?.trim(); // possible last \n causes '' at end of array
|
|
227
|
+
if (!latestBootStdout) return undefined;
|
|
228
|
+
|
|
229
|
+
const tokens = latestBootStdout.trim().split(' ');
|
|
230
|
+
|
|
231
|
+
return String(new Date(`${tokens[2]} ${tokens[3]} ${tokens[4]}`));
|
|
136
232
|
} catch (e) {
|
|
137
|
-
logger.error(`Issue getting last boot time
|
|
233
|
+
logger.error(`Issue getting last boot time!\n${stringifyError(e)}`);
|
|
138
234
|
return undefined;
|
|
139
235
|
}
|
|
140
236
|
}
|
|
141
237
|
|
|
142
238
|
export async function getSystemInformation(): Promise<SystemInformationShadowUpdate> {
|
|
143
239
|
try {
|
|
240
|
+
const [os, cpu, disk, device, network, versions, lastBootTime] =
|
|
241
|
+
await Promise.all([
|
|
242
|
+
getOsInfo(),
|
|
243
|
+
getCpuInfo(),
|
|
244
|
+
getDiskInfo(),
|
|
245
|
+
getDeviceInfo(),
|
|
246
|
+
getNetworkInfo(),
|
|
247
|
+
getPackageVersions(),
|
|
248
|
+
getLastBootTime()
|
|
249
|
+
]);
|
|
144
250
|
const systemInfo: SystemInformationShadowUpdate = {
|
|
145
|
-
os
|
|
146
|
-
cpu
|
|
147
|
-
disk
|
|
148
|
-
device
|
|
149
|
-
network
|
|
150
|
-
versions
|
|
151
|
-
lastBootTime
|
|
251
|
+
os,
|
|
252
|
+
cpu,
|
|
253
|
+
disk,
|
|
254
|
+
device,
|
|
255
|
+
network,
|
|
256
|
+
versions,
|
|
257
|
+
lastBootTime
|
|
152
258
|
};
|
|
153
259
|
return systemInfo;
|
|
154
260
|
} catch (e) {
|
|
155
|
-
logger.error(
|
|
261
|
+
logger.error(
|
|
262
|
+
`There was a problem getting system information!\n${stringifyError(e)}`
|
|
263
|
+
);
|
|
156
264
|
}
|
|
157
265
|
return {};
|
|
158
266
|
}
|
|
@@ -167,11 +275,10 @@ export async function reboot() {
|
|
|
167
275
|
timeout: 5000
|
|
168
276
|
});
|
|
169
277
|
logger.info(result.stdout.trim());
|
|
170
|
-
} catch (
|
|
171
|
-
throw new
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
)}`
|
|
278
|
+
} catch (e) {
|
|
279
|
+
throw new AaiError(
|
|
280
|
+
"Could not reboot device. You may need to add passwordless access to '/sbin/shutdown'.",
|
|
281
|
+
{ cause: e }
|
|
175
282
|
);
|
|
176
283
|
}
|
|
177
284
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as DockerComposeV1 from 'docker-compose';
|
|
2
|
+
import { v2 as DockerComposeV2 } from 'docker-compose';
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import { logger } from '../util/logger';
|
|
5
|
+
import { JsSpawner, stringifyError } from 'alwaysai/lib/util';
|
|
6
|
+
|
|
7
|
+
let dockerCmd = 'docker';
|
|
8
|
+
|
|
9
|
+
export function importDockerCompose():
|
|
10
|
+
| typeof DockerComposeV1
|
|
11
|
+
| typeof DockerComposeV2 {
|
|
12
|
+
try {
|
|
13
|
+
execSync('docker compose version').toString();
|
|
14
|
+
logger.debug('Using Docker Compose V2.');
|
|
15
|
+
return DockerComposeV2;
|
|
16
|
+
} catch (e) {
|
|
17
|
+
// TODO: Log this error as well
|
|
18
|
+
try {
|
|
19
|
+
execSync('docker-compose -v').toString();
|
|
20
|
+
logger.warn('Using docker-compose V1. Please consider updating to V2.');
|
|
21
|
+
} catch (e) {
|
|
22
|
+
logger.warn(`Could not determine compose!\n${stringifyError(e)}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
dockerCmd = 'docker-compose';
|
|
26
|
+
return DockerComposeV1;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const compose = importDockerCompose();
|
|
30
|
+
|
|
31
|
+
export async function runDockerComposeCmd(props: {
|
|
32
|
+
args: string[];
|
|
33
|
+
dir: string;
|
|
34
|
+
}) {
|
|
35
|
+
const { args, dir } = props;
|
|
36
|
+
const spawner = JsSpawner();
|
|
37
|
+
if (dockerCmd === 'docker') {
|
|
38
|
+
args.unshift('compose');
|
|
39
|
+
}
|
|
40
|
+
const output = await spawner.run({
|
|
41
|
+
exe: dockerCmd,
|
|
42
|
+
args,
|
|
43
|
+
cwd: dir
|
|
44
|
+
});
|
|
45
|
+
return output;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function runStreamingDockerComposeCmd(props: {
|
|
49
|
+
args: string[];
|
|
50
|
+
dir: string;
|
|
51
|
+
}) {
|
|
52
|
+
const { args, dir } = props;
|
|
53
|
+
if (dockerCmd === 'docker') {
|
|
54
|
+
args.unshift('compose');
|
|
55
|
+
}
|
|
56
|
+
return await JsSpawner().runStreaming({
|
|
57
|
+
exe: dockerCmd,
|
|
58
|
+
args,
|
|
59
|
+
cwd: dir
|
|
60
|
+
});
|
|
61
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -10,13 +10,9 @@ import { root } from './root';
|
|
|
10
10
|
import { runDeviceAgentCloudInterface } from './cloud-connection/device-agent-cloud-connection';
|
|
11
11
|
import { AgentConfigFile } from './infrastructure/agent-config';
|
|
12
12
|
import { ALWAYSAI_DEVICE_AGENT_MODE } from './environment';
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
16
|
-
const { version } = require('../package.json');
|
|
13
|
+
import { checkForUpdatesAndPrompt } from './util/check-for-updates';
|
|
17
14
|
|
|
18
15
|
if (module === require.main) {
|
|
19
|
-
logger.info(`Starting alwaysAI Device Agent v${version}`);
|
|
20
16
|
if (!AgentConfigFile().exists()) {
|
|
21
17
|
AgentConfigFile().initialize();
|
|
22
18
|
}
|
|
@@ -24,6 +20,6 @@ if (module === require.main) {
|
|
|
24
20
|
if (ALWAYSAI_DEVICE_AGENT_MODE === 'cloud') {
|
|
25
21
|
void runDeviceAgentCloudInterface();
|
|
26
22
|
} else {
|
|
27
|
-
void runCliAndExit(root, {});
|
|
23
|
+
void runCliAndExit(root, { postRun: checkForUpdatesAndPrompt });
|
|
28
24
|
}
|
|
29
25
|
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import { getDeviceConfigPath } from 'alwaysai/lib/infrastructure';
|
|
2
|
+
import { join } from 'path';
|
|
1
3
|
import * as tempy from 'tempy';
|
|
2
|
-
import { AgentConfigFile } from './agent-config';
|
|
4
|
+
import { AGENT_CONFIG_FILE_NAME, AgentConfigFile } from './agent-config';
|
|
3
5
|
|
|
4
|
-
const configFile = AgentConfigFile(
|
|
6
|
+
const configFile = AgentConfigFile(
|
|
7
|
+
join(tempy.directory(), getDeviceConfigPath(), AGENT_CONFIG_FILE_NAME)
|
|
8
|
+
);
|
|
5
9
|
|
|
6
10
|
describe('Test Agent Config', () => {
|
|
7
11
|
beforeEach(() => {
|
|
@@ -95,6 +99,9 @@ describe('Test Agent Config', () => {
|
|
|
95
99
|
expect(await configFile.getAppVersion({ projectId })).toEqual(version);
|
|
96
100
|
await configFile.setAppInstalled({ projectId, version });
|
|
97
101
|
expect(await configFile.getAppVersion({ projectId })).toEqual(version);
|
|
102
|
+
expect(await configFile.isAppReady({ projectId })).toEqual(true);
|
|
103
|
+
await configFile.setAppUninstalling({ projectId });
|
|
104
|
+
expect(await configFile.isAppReady({ projectId })).toEqual(false);
|
|
98
105
|
await configFile.setAppUninstalled({ projectId });
|
|
99
106
|
const apps = await configFile.getApps();
|
|
100
107
|
expect(apps).toEqual([]);
|
|
@@ -3,7 +3,8 @@ import {
|
|
|
3
3
|
ConfigFileSchemaReturnType
|
|
4
4
|
} from '@alwaysai/config-nodejs';
|
|
5
5
|
import Ajv, { JSONSchemaType } from 'ajv';
|
|
6
|
-
import {
|
|
6
|
+
import { getDeviceConfigPath } from 'alwaysai/lib/infrastructure';
|
|
7
|
+
import { LOCAL_AAI_CFG_DIR } from 'alwaysai/lib/paths';
|
|
7
8
|
import { join } from 'path';
|
|
8
9
|
|
|
9
10
|
export interface AppBackupConfig {
|
|
@@ -54,65 +55,29 @@ const schema: JSONSchemaType<AgentConfig> = {
|
|
|
54
55
|
const ajv = new Ajv();
|
|
55
56
|
const validateFunction = ajv.compile(schema);
|
|
56
57
|
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
const AGENT_CONFIG_FILE_NAME = 'alwaysai.agent.json';
|
|
60
|
-
|
|
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: (props: { projectId: string }) => Promise<string>;
|
|
74
|
-
setAppBackup;
|
|
75
|
-
getAppBackup;
|
|
76
|
-
}
|
|
58
|
+
export const AGENT_CONFIG_FILE_NAME = 'alwaysai.agent.json';
|
|
77
59
|
|
|
78
|
-
export function AgentConfigFile(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
60
|
+
export function AgentConfigFile(filePath?: string) {
|
|
61
|
+
const path =
|
|
62
|
+
filePath ??
|
|
63
|
+
join(LOCAL_AAI_CFG_DIR, getDeviceConfigPath(), AGENT_CONFIG_FILE_NAME);
|
|
82
64
|
const initialValue: AgentConfig = {
|
|
83
65
|
applications: []
|
|
84
66
|
};
|
|
85
|
-
const configFile = ConfigFileSchema({
|
|
67
|
+
const configFile = ConfigFileSchema<AgentConfig>({
|
|
86
68
|
path,
|
|
87
69
|
validateFunction,
|
|
88
70
|
initialValue
|
|
89
71
|
});
|
|
90
72
|
|
|
91
|
-
return {
|
|
92
|
-
...configFile,
|
|
93
|
-
name: AGENT_CONFIG_FILE_NAME,
|
|
94
|
-
getApps,
|
|
95
|
-
getReadyApps,
|
|
96
|
-
getApp,
|
|
97
|
-
isAppPresent,
|
|
98
|
-
isAppReady,
|
|
99
|
-
removeApp,
|
|
100
|
-
setAppInstalling,
|
|
101
|
-
setAppInstalled,
|
|
102
|
-
setAppUninstalled,
|
|
103
|
-
getAppVersion,
|
|
104
|
-
setAppBackup,
|
|
105
|
-
getAppBackup
|
|
106
|
-
};
|
|
107
|
-
|
|
108
73
|
async function getApps() {
|
|
109
74
|
const config = configFile.read();
|
|
110
|
-
return config
|
|
75
|
+
return config?.applications ? config.applications : [];
|
|
111
76
|
}
|
|
112
77
|
|
|
113
78
|
async function getReadyApps() {
|
|
114
79
|
const config = configFile.read();
|
|
115
|
-
return config
|
|
80
|
+
return config?.applications
|
|
116
81
|
? config.applications.filter((app) => {
|
|
117
82
|
return app.ready;
|
|
118
83
|
})
|
|
@@ -205,6 +170,23 @@ export function AgentConfigFile(
|
|
|
205
170
|
}
|
|
206
171
|
}
|
|
207
172
|
|
|
173
|
+
async function setAppUninstalling(props: { projectId: string }) {
|
|
174
|
+
const { projectId } = props;
|
|
175
|
+
const app = await getApp({ projectId });
|
|
176
|
+
if (app) {
|
|
177
|
+
await removeApp({ projectId });
|
|
178
|
+
const config = configFile.read();
|
|
179
|
+
config.applications.push({ ...app, ...{ ready: false } });
|
|
180
|
+
configFile.write(config);
|
|
181
|
+
} else {
|
|
182
|
+
// NOTE: we should never be setting an app as installed
|
|
183
|
+
// if it doesn't exist (setAppInstalling was never called)
|
|
184
|
+
throw new Error(
|
|
185
|
+
`App ${projectId} was not previously configured and could not be set to installed!`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
208
190
|
async function setAppUninstalled(props: { projectId: string }) {
|
|
209
191
|
const { projectId } = props;
|
|
210
192
|
await removeApp({ projectId });
|
|
@@ -235,9 +217,26 @@ export function AgentConfigFile(
|
|
|
235
217
|
async function getAppBackup(props: { projectId: string }) {
|
|
236
218
|
const { projectId } = props;
|
|
237
219
|
const app = await getApp({ projectId });
|
|
238
|
-
if (app
|
|
220
|
+
if (app?.backup) {
|
|
239
221
|
return app.backup;
|
|
240
222
|
}
|
|
241
223
|
return null;
|
|
242
224
|
}
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
...configFile,
|
|
228
|
+
name: AGENT_CONFIG_FILE_NAME,
|
|
229
|
+
getApps,
|
|
230
|
+
getReadyApps,
|
|
231
|
+
getApp,
|
|
232
|
+
isAppPresent,
|
|
233
|
+
isAppReady,
|
|
234
|
+
setAppInstalling,
|
|
235
|
+
setAppInstalled,
|
|
236
|
+
setAppUninstalling,
|
|
237
|
+
setAppUninstalled,
|
|
238
|
+
getAppVersion,
|
|
239
|
+
setAppBackup,
|
|
240
|
+
getAppBackup
|
|
241
|
+
};
|
|
243
242
|
}
|