From 2fd0ca6a0203c30639cc981cf572bf2aa375b881 Mon Sep 17 00:00:00 2001
From: Thodoris Greasidis <thgreasi@gmail.com>
Date: Thu, 29 Dec 2022 00:26:14 +0200
Subject: [PATCH] Stop using the deprecated balena-sync module

Change-type: patch
---
 lib/commands/preload.ts                       |   4 +-
 lib/commands/scan.ts                          |  29 +-
 lib/utils/discover.ts                         |  40 ++
 lib/utils/promote.ts                          |  57 ++-
 npm-shrinkwrap.json                           | 468 ++++++++----------
 package.json                                  |   4 +-
 repo.yml                                      |   2 -
 .../pkg/expected-warnings-darwin.txt          |  18 -
 .../test-data/pkg/expected-warnings-linux.txt |  18 -
 .../test-data/pkg/expected-warnings-win32.txt |  18 -
 typings/balena-sync/index.d.ts                |  41 --
 typings/docker-toolbelt/index.d.ts            |  23 +
 typings/resin-cli-visuals/index.d.ts          |  23 +-
 13 files changed, 348 insertions(+), 397 deletions(-)
 create mode 100644 lib/utils/discover.ts
 delete mode 100644 typings/balena-sync/index.d.ts
 create mode 100644 typings/docker-toolbelt/index.d.ts

diff --git a/lib/commands/preload.ts b/lib/commands/preload.ts
index db40480d..a50d4d82 100644
--- a/lib/commands/preload.ts
+++ b/lib/commands/preload.ts
@@ -187,7 +187,7 @@ Can be repeated to add multiple certificates.\
 			: undefined;
 
 		const progressBars: {
-			[key: string]: ReturnType<typeof getVisuals>['Progress'];
+			[key: string]: InstanceType<ReturnType<typeof getVisuals>['Progress']>;
 		} = {};
 
 		const progressHandler = function (event: {
@@ -201,7 +201,7 @@ Can be repeated to add multiple certificates.\
 		};
 
 		const spinners: {
-			[key: string]: ReturnType<typeof getVisuals>['Spinner'];
+			[key: string]: InstanceType<ReturnType<typeof getVisuals>['Spinner']>;
 		} = {};
 
 		const spinnerHandler = function (event: { name: string; action: string }) {
diff --git a/lib/commands/scan.ts b/lib/commands/scan.ts
index 89fbc8aa..b6196421 100644
--- a/lib/commands/scan.ts
+++ b/lib/commands/scan.ts
@@ -16,7 +16,6 @@
  */
 
 import { flags } from '@oclif/command';
-import type { LocalBalenaOsDevice } from 'balena-sync';
 import Command from '../command';
 import * as cf from '../utils/common-flags';
 import { getCliUx, stripIndent } from '../utils/lazy';
@@ -72,7 +71,7 @@ export default class ScanCmd extends Command {
 
 	public async run() {
 		const _ = await import('lodash');
-		const { discover } = await import('balena-sync');
+		const { discoverLocalBalenaOsDevices } = await import('../utils/discover');
 		const prettyjson = await import('prettyjson');
 		const dockerUtils = await import('../utils/docker');
 
@@ -88,8 +87,7 @@ export default class ScanCmd extends Command {
 		const ux = getCliUx();
 		ux.action.start('Scanning for local balenaOS devices');
 
-		const localDevices: LocalBalenaOsDevice[] =
-			await discover.discoverLocalBalenaOsDevices(discoverTimeout);
+		const localDevices = await discoverLocalBalenaOsDevices(discoverTimeout);
 		const engineReachableDevices: boolean[] = await Promise.all(
 			localDevices.map(async ({ address }: { address: string }) => {
 				const docker = await dockerUtils.createClient({
@@ -106,7 +104,7 @@ export default class ScanCmd extends Command {
 			}),
 		);
 
-		const developmentDevices: LocalBalenaOsDevice[] = localDevices.filter(
+		const developmentDevices = localDevices.filter(
 			(_localDevice, index) => engineReachableDevices[index],
 		);
 
@@ -116,18 +114,15 @@ export default class ScanCmd extends Command {
 			_.isEqual,
 		);
 
-		const productionDevicesInfo = _.map(
-			productionDevices,
-			(device: LocalBalenaOsDevice) => {
-				return {
-					host: device.host,
-					address: device.address,
-					osVariant: 'production',
-					dockerInfo: undefined,
-					dockerVersion: undefined,
-				};
-			},
-		);
+		const productionDevicesInfo = productionDevices.map((device) => {
+			return {
+				host: device.host,
+				address: device.address,
+				osVariant: 'production',
+				dockerInfo: undefined,
+				dockerVersion: undefined,
+			};
+		});
 
 		// Query devices for info
 		const devicesInfo = await Promise.all(
diff --git a/lib/utils/discover.ts b/lib/utils/discover.ts
new file mode 100644
index 00000000..b3bafe09
--- /dev/null
+++ b/lib/utils/discover.ts
@@ -0,0 +1,40 @@
+import { enumerateServices, findServices } from 'resin-discoverable-services';
+
+interface LocalBalenaOsDevice {
+	address: string;
+	host: string;
+	osVariant?: string;
+	port: number;
+}
+
+// Although we only check for 'balena-ssh', we know, implicitly, that balenaOS
+// devices come with 'rsync' installed that can be used over SSH.
+const avahiBalenaSshTag = 'resin-ssh';
+
+export async function discoverLocalBalenaOsDevices(
+	timeout = 4000,
+): Promise<LocalBalenaOsDevice[]> {
+	const availableServices = await enumerateServices();
+	const serviceDefinitions = Array.from(availableServices)
+		.filter((s) => Array.from(s.tags).includes(avahiBalenaSshTag))
+		.map((s) => s.service);
+
+	if (serviceDefinitions.length === 0) {
+		throw new Error(
+			`Could not find any available '${avahiBalenaSshTag}' services`,
+		);
+	}
+
+	const services = await findServices(serviceDefinitions, timeout);
+	return services.map(function (service) {
+		// User referer address to get device IP. This will work fine assuming that
+		// a device only advertises own services.
+		const {
+			referer: { address },
+			host,
+			port,
+		} = service;
+
+		return { address, host, port };
+	});
+}
diff --git a/lib/utils/promote.ts b/lib/utils/promote.ts
index 43f97621..b0bf3583 100644
--- a/lib/utils/promote.ts
+++ b/lib/utils/promote.ts
@@ -163,12 +163,61 @@ async function getOsVersion(deviceIp: string): Promise<string> {
 	return match[1];
 }
 
+const dockerPort = 2375;
+const dockerTimeout = 2000;
+
+async function selectLocalBalenaOsDevice(timeout = 4000): Promise<string> {
+	const { discoverLocalBalenaOsDevices } = await import('../utils/discover');
+	const { SpinnerPromise } = getVisuals();
+	const devices = await new SpinnerPromise({
+		promise: discoverLocalBalenaOsDevices(timeout),
+		startMessage: 'Discovering local balenaOS devices..',
+		stopMessage: 'Reporting discovered devices',
+	});
+
+	const responsiveDevices: typeof devices = [];
+	const Docker = await import('docker-toolbelt');
+	await Promise.all(
+		devices.map(async function (device) {
+			const address = device?.address;
+			if (!address) {
+				return;
+			}
+
+			try {
+				const docker = new Docker({
+					host: address,
+					port: dockerPort,
+					timeout: dockerTimeout,
+				});
+				await docker.ping();
+				responsiveDevices.push(device);
+			} catch {
+				return;
+			}
+		}),
+	);
+
+	if (!responsiveDevices.length) {
+		throw new Error('Could not find any local balenaOS devices');
+	}
+
+	return getCliForm().ask({
+		message: 'select a device',
+		type: 'list',
+		default: devices[0].address,
+		choices: responsiveDevices.map((device) => ({
+			name: `${device.host || 'untitled'} (${device.address})`,
+			value: device.address,
+		})),
+	});
+}
+
 async function selectLocalDevice(): Promise<string> {
-	const { forms } = await import('balena-sync');
-	let hostnameOrIp;
 	try {
-		hostnameOrIp = await forms.selectLocalBalenaOsDevice();
+		const hostnameOrIp = await selectLocalBalenaOsDevice();
 		console.error(`==> Selected device: ${hostnameOrIp}`);
+		return hostnameOrIp;
 	} catch (e) {
 		if (e.message.toLowerCase().includes('could not find any')) {
 			throw new ExpectedError(e);
@@ -176,8 +225,6 @@ async function selectLocalDevice(): Promise<string> {
 			throw e;
 		}
 	}
-
-	return hostnameOrIp;
 }
 
 async function selectAppFromList(
diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json
index 3225df4b..8d67cd19 100644
--- a/npm-shrinkwrap.json
+++ b/npm-shrinkwrap.json
@@ -1370,8 +1370,7 @@
         "nan": {
           "version": "2.16.0",
           "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz",
-          "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==",
-          "optional": true
+          "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA=="
         },
         "readable-stream": {
           "version": "3.6.0",
@@ -1383,17 +1382,6 @@
             "util-deprecate": "^1.0.1"
           }
         },
-        "ssh2": {
-          "version": "1.11.0",
-          "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.11.0.tgz",
-          "integrity": "sha512-nfg0wZWGSsfUe/IBJkXVll3PEZ//YH2guww+mP88gTpuSU4FtZN7zu9JoeTGOyCNx2dTDtT9fOpWwlzyj4uOOw==",
-          "requires": {
-            "asn1": "^0.2.4",
-            "bcrypt-pbkdf": "^1.0.2",
-            "cpu-features": "~0.0.4",
-            "nan": "^2.16.0"
-          }
-        },
         "stream-to-promise": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/stream-to-promise/-/stream-to-promise-3.0.0.tgz",
@@ -2830,11 +2818,6 @@
       "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz",
       "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw=="
     },
-    "@types/memoizee": {
-      "version": "0.4.6",
-      "resolved": "https://registry.npmjs.org/@types/memoizee/-/memoizee-0.4.6.tgz",
-      "integrity": "sha512-qJezGqoi3pW9Pset2w1Gfv8jATvmHHHnpO9Dq8x8pJGyYIpiUZJqRU0NM7xenmN0AcXEe7vqshI8H98KeFLYcg=="
-    },
     "@types/mime": {
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
@@ -3266,6 +3249,16 @@
       "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.3.tgz",
       "integrity": "sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q=="
     },
+    "abstract-socket": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/abstract-socket/-/abstract-socket-2.1.1.tgz",
+      "integrity": "sha512-YZJizsvS1aBua5Gd01woe4zuyYBGgSMeqDOB6/ChwdTI904KP6QGtJswXl4hcqWxbz86hQBe++HWV0hF1aGUtA==",
+      "optional": true,
+      "requires": {
+        "bindings": "^1.2.1",
+        "nan": "^2.12.1"
+      }
+    },
     "accepts": {
       "version": "1.3.7",
       "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
@@ -3805,9 +3798,9 @@
       }
     },
     "balena-hup-action-utils": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/balena-hup-action-utils/-/balena-hup-action-utils-4.0.3.tgz",
-      "integrity": "sha512-PdHMpSjaQriB4y4zmeAmm0Mxudencc/BVjI3jHVH3/SCZ6OqIIGlyFJU2tS0vsghED8PiEyQSjUdDCGzv7zUiQ==",
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/balena-hup-action-utils/-/balena-hup-action-utils-4.1.3.tgz",
+      "integrity": "sha512-98SK5oTPgTKWsbEmPk0juI/ivT5qADsj/y+/B39I47lbDfPuhF/kHpgMI+xQCtT/GS+Dy3omkgY4nEcRI4CeoQ==",
       "requires": {
         "balena-semver": "^2.0.0",
         "tslib": "^2.0.0"
@@ -3836,17 +3829,6 @@
         "rimraf": "^3.0.2"
       }
     },
-    "balena-pine": {
-      "version": "12.4.0",
-      "resolved": "https://registry.npmjs.org/balena-pine/-/balena-pine-12.4.0.tgz",
-      "integrity": "sha512-ap4WvIwwEsLl5rh7badxiu/4pDqbgT3DdeSkKl03T9U4XQ8fgFuqcl//lvpUG1jzhSe/ExQfkv1ZebrUd/kvqA==",
-      "requires": {
-        "@balena/es-version": "^1.0.0",
-        "balena-errors": "^4.2.1",
-        "pinejs-client-core": "^6.9.0",
-        "tslib": "^2.0.1"
-      }
-    },
     "balena-preload": {
       "version": "12.2.0",
       "resolved": "https://registry.npmjs.org/balena-preload/-/balena-preload-12.2.0.tgz",
@@ -3968,27 +3950,32 @@
       }
     },
     "balena-register-device": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/balena-register-device/-/balena-register-device-7.2.0.tgz",
-      "integrity": "sha512-Uo8iceob2Zg8gBedZKzSZWTyr5XoDkUN+2oSyyuKVuhZYcZS623Lsj/+I8QdJQutlObgVHjD2k3mM1Pa6loFxA==",
+      "version": "8.0.4",
+      "resolved": "https://registry.npmjs.org/balena-register-device/-/balena-register-device-8.0.4.tgz",
+      "integrity": "sha512-4ziyefKEkdrzerwrrdEGq3hvY8wbzm+0iH3F+ZJAIVgxpYELfudNXQj8GJXHWzclUNbdBun7G5yiYpmdLs5G+g==",
       "requires": {
         "@types/uuid": "^8.3.0",
         "tslib": "^2.2.0",
         "typed-error": "^3.2.1",
-        "uuid": "^8.3.2"
+        "uuid": "^9.0.0"
       },
       "dependencies": {
         "tslib": {
-          "version": "2.3.1",
-          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
-          "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+          "version": "2.4.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
+          "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
+        },
+        "uuid": {
+          "version": "9.0.0",
+          "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
+          "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg=="
         }
       }
     },
     "balena-request": {
-      "version": "11.5.0",
-      "resolved": "https://registry.npmjs.org/balena-request/-/balena-request-11.5.0.tgz",
-      "integrity": "sha512-mLf5NQU2qqGKyjZAmz8fFOzyUjwF9uptcDPUe56ADAQfLvQYML8izW7/9Q7DBRGpvlZrBZ/OnjWLK3vUs1tNdA==",
+      "version": "11.5.9",
+      "resolved": "https://registry.npmjs.org/balena-request/-/balena-request-11.5.9.tgz",
+      "integrity": "sha512-SOnqpdySUdFNO6MLv187Q64EgJjytLxfkpst8amiPYt6EA591U0DQiZwwkUwNl+cg+YvkytEBWRYcWOhoTFcrg==",
       "requires": {
         "@balena/node-web-streams": "^0.2.3",
         "balena-errors": "^4.7.1",
@@ -4028,40 +4015,6 @@
           "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
           "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="
         },
-        "balena-hup-action-utils": {
-          "version": "4.1.3",
-          "resolved": "https://registry.npmjs.org/balena-hup-action-utils/-/balena-hup-action-utils-4.1.3.tgz",
-          "integrity": "sha512-98SK5oTPgTKWsbEmPk0juI/ivT5qADsj/y+/B39I47lbDfPuhF/kHpgMI+xQCtT/GS+Dy3omkgY4nEcRI4CeoQ==",
-          "requires": {
-            "balena-semver": "^2.0.0",
-            "tslib": "^2.0.0"
-          }
-        },
-        "balena-register-device": {
-          "version": "8.0.4",
-          "resolved": "https://registry.npmjs.org/balena-register-device/-/balena-register-device-8.0.4.tgz",
-          "integrity": "sha512-4ziyefKEkdrzerwrrdEGq3hvY8wbzm+0iH3F+ZJAIVgxpYELfudNXQj8GJXHWzclUNbdBun7G5yiYpmdLs5G+g==",
-          "requires": {
-            "@types/uuid": "^8.3.0",
-            "tslib": "^2.2.0",
-            "typed-error": "^3.2.1",
-            "uuid": "^9.0.0"
-          }
-        },
-        "balena-request": {
-          "version": "11.5.9",
-          "resolved": "https://registry.npmjs.org/balena-request/-/balena-request-11.5.9.tgz",
-          "integrity": "sha512-SOnqpdySUdFNO6MLv187Q64EgJjytLxfkpst8amiPYt6EA591U0DQiZwwkUwNl+cg+YvkytEBWRYcWOhoTFcrg==",
-          "requires": {
-            "@balena/node-web-streams": "^0.2.3",
-            "balena-errors": "^4.7.1",
-            "fetch-ponyfill": "^7.1.0",
-            "fetch-readablestream": "^0.2.0",
-            "progress-stream": "^2.0.0",
-            "qs": "^6.9.4",
-            "tslib": "^2.0.0"
-          }
-        },
         "date-fns": {
           "version": "2.29.3",
           "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz",
@@ -4138,116 +4091,6 @@
         }
       }
     },
-    "balena-sync": {
-      "version": "11.0.2",
-      "resolved": "https://registry.npmjs.org/balena-sync/-/balena-sync-11.0.2.tgz",
-      "integrity": "sha512-z096L+1hYO4S7n5L5GUzn8VA5VGqFh22Nrp8iidUFw91Lo9jzO4yL/hL+2HOxkTFg1L5x8m+bxd4qr9GXgmMVg==",
-      "requires": {
-        "JSONStream": "^1.3.5",
-        "balena-sdk": "^15.2.1",
-        "balena-semver": "^2.3.0",
-        "balena-settings-client": "^4.0.5",
-        "bluebird": "^3.7.2",
-        "chalk": "^4.1.0",
-        "docker-toolbelt": "^3.3.8",
-        "js-yaml": "^3.14.0",
-        "lodash": "^4.17.19",
-        "resin-cli-form": "^2.0.2",
-        "resin-cli-visuals": "^1.7.0",
-        "resin-discoverable-services": "git+https://github.com/resin-io-modules/resin-discoverable-services.git#find-on-all-interfaces",
-        "revalidator": "^0.3.1",
-        "rindle": "^1.3.6",
-        "rsync": "^0.4.0",
-        "shellwords": "^0.1.1",
-        "ssh2": "^0.5.5",
-        "tar-fs": "^2.1.0",
-        "typed-error": "^2.0.0"
-      },
-      "dependencies": {
-        "@types/node": {
-          "version": "10.17.60",
-          "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
-          "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="
-        },
-        "balena-sdk": {
-          "version": "15.59.2",
-          "resolved": "https://registry.npmjs.org/balena-sdk/-/balena-sdk-15.59.2.tgz",
-          "integrity": "sha512-pMNwqqnqtets6PoYxRQhOHBnk7Say4gNhInAvS69UlEhraCR0Xau8rJk9G2IthWDA4tpR2In3u4SQJMIHYj84w==",
-          "requires": {
-            "@balena/es-version": "^1.0.0",
-            "@types/lodash": "^4.14.168",
-            "@types/memoizee": "^0.4.5",
-            "@types/node": "^10.17.55",
-            "abortcontroller-polyfill": "^1.7.1",
-            "balena-auth": "^4.1.0",
-            "balena-errors": "^4.7.1",
-            "balena-hup-action-utils": "~4.0.2",
-            "balena-pine": "^12.4.0",
-            "balena-register-device": "^7.1.0",
-            "balena-request": "^11.5.0",
-            "balena-semver": "^2.3.0",
-            "balena-settings-client": "^4.0.6",
-            "lodash": "^4.17.21",
-            "memoizee": "^0.4.15",
-            "moment": "^2.29.1",
-            "ndjson": "^2.0.0",
-            "semver": "^7.3.4",
-            "tslib": "^2.1.0"
-          },
-          "dependencies": {
-            "tslib": {
-              "version": "2.3.1",
-              "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
-              "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
-            }
-          }
-        },
-        "chalk": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
-          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
-          "requires": {
-            "ansi-styles": "^4.1.0",
-            "supports-color": "^7.1.0"
-          }
-        },
-        "has-flag": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
-        },
-        "js-yaml": {
-          "version": "3.14.1",
-          "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
-          "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
-          "requires": {
-            "argparse": "^1.0.7",
-            "esprima": "^4.0.0"
-          }
-        },
-        "supports-color": {
-          "version": "7.1.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
-          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
-          "requires": {
-            "has-flag": "^4.0.0"
-          }
-        },
-        "tslib": {
-          "version": "1.13.0",
-          "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
-          "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q=="
-        },
-        "typed-error": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/typed-error/-/typed-error-2.0.0.tgz",
-          "integrity": "sha1-05j9hin8K3nIOfm30b/Ay7ZSYBI=",
-          "requires": {
-            "tslib": "^1.7.1"
-          }
-        }
-      }
-    },
     "base": {
       "version": "0.11.2",
       "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
@@ -5884,6 +5727,33 @@
       "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
       "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q=="
     },
+    "dbus-native": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmjs.org/dbus-native/-/dbus-native-0.2.5.tgz",
+      "integrity": "sha512-ocxMKCV7QdiNhzhFSeEMhj258OGtvpANSb3oWGiotmI5h1ZIse0TMPcSLiXSpqvbYvQz2Y5RsYPMNYLWhg9eBw==",
+      "optional": true,
+      "requires": {
+        "abstract-socket": "^2.0.0",
+        "event-stream": "^3.1.7",
+        "hexy": "^0.2.10",
+        "long": "^3.0.1",
+        "optimist": "^0.6.1",
+        "put": "0.0.6",
+        "safe-buffer": "^5.1.1",
+        "xml2js": "0.1.14"
+      },
+      "dependencies": {
+        "xml2js": {
+          "version": "0.1.14",
+          "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.1.14.tgz",
+          "integrity": "sha512-pbdws4PPPNc1HPluSUKamY4GWMk592K7qwcj6BExbVOhhubub8+pMda/ql68b6L3luZs/OGjGSB5goV7SnmgnA==",
+          "optional": true,
+          "requires": {
+            "sax": ">=0.1.1"
+          }
+        }
+      }
+    },
     "debug": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
@@ -6177,7 +6047,7 @@
     "dns-equal": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
-      "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0="
+      "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg=="
     },
     "dns-packet": {
       "version": "1.3.4",
@@ -6191,7 +6061,7 @@
     "dns-txt": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz",
-      "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=",
+      "integrity": "sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ==",
       "requires": {
         "buffer-indexof": "^1.0.0"
       }
@@ -6229,16 +6099,6 @@
           "requires": {
             "ssh2-streams": "~0.4.10"
           }
-        },
-        "ssh2-streams": {
-          "version": "0.4.10",
-          "resolved": "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.4.10.tgz",
-          "integrity": "sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ==",
-          "requires": {
-            "asn1": "~0.2.0",
-            "bcrypt-pbkdf": "^1.0.2",
-            "streamsearch": "~0.1.2"
-          }
         }
       }
     },
@@ -6253,9 +6113,9 @@
       }
     },
     "docker-toolbelt": {
-      "version": "3.3.8",
-      "resolved": "https://registry.npmjs.org/docker-toolbelt/-/docker-toolbelt-3.3.8.tgz",
-      "integrity": "sha512-iyx/XpHQMh2qZmHsWjtSnOzAKhdPjLoyMcZQ4apb3uVqbKqnEw7AdmoYNpDxEjXneRrucFLXtetLzEMrOikHlg==",
+      "version": "3.3.10",
+      "resolved": "https://registry.npmjs.org/docker-toolbelt/-/docker-toolbelt-3.3.10.tgz",
+      "integrity": "sha512-IDQywPA3CcOzAtXoQmRYiryfZ23kTDr263lrn9R9SGtGwpoDgZHW3NQfpO9o92nJ6zVYJxsFdk3gBIziCSbw9w==",
       "requires": {
         "@types/bluebird": "^3.5.30",
         "@types/dockerode": "^2.5.24",
@@ -6278,7 +6138,7 @@
         "JSONStream": {
           "version": "1.3.2",
           "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.2.tgz",
-          "integrity": "sha1-wQI3G27Dp887hHygDCC7D85Mbeo=",
+          "integrity": "sha512-mn0KSip7N4e0UDPZHnqDsHECo5uGQrixQKnAskOM1BIB8hd7QKbd6il8IPRPudPHOeHiECoCFqhyMaRO9+nWyA==",
           "requires": {
             "jsonparse": "^1.2.0",
             "through": ">=2.2.7 <3"
@@ -6296,7 +6156,7 @@
             "isarray": {
               "version": "1.0.0",
               "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-              "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+              "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
             },
             "readable-stream": {
               "version": "2.3.7",
@@ -6368,7 +6228,20 @@
         "isarray": {
           "version": "0.0.1",
           "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
-          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+          "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
+        },
+        "minimist": {
+          "version": "1.2.7",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
+          "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g=="
+        },
+        "mkdirp": {
+          "version": "0.5.6",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+          "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+          "requires": {
+            "minimist": "^1.2.6"
+          }
         },
         "pump": {
           "version": "1.0.3",
@@ -6382,7 +6255,7 @@
         "readable-stream": {
           "version": "1.0.34",
           "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
-          "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+          "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==",
           "requires": {
             "core-util-is": "~1.0.0",
             "inherits": "~2.0.1",
@@ -6393,7 +6266,7 @@
         "string_decoder": {
           "version": "0.10.31",
           "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
-          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+          "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="
         },
         "tar-fs": {
           "version": "1.16.3",
@@ -6409,15 +6282,7 @@
             "isarray": {
               "version": "1.0.0",
               "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-              "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
-            },
-            "mkdirp": {
-              "version": "0.5.5",
-              "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
-              "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
-              "requires": {
-                "minimist": "^1.2.5"
-              }
+              "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
             },
             "readable-stream": {
               "version": "2.3.7",
@@ -6773,6 +6638,7 @@
       "version": "1.17.6",
       "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
       "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+      "dev": true,
       "requires": {
         "es-to-primitive": "^1.2.1",
         "function-bind": "^1.1.1",
@@ -6791,6 +6657,7 @@
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
       "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+      "dev": true,
       "requires": {
         "is-callable": "^1.1.4",
         "is-date-object": "^1.0.1",
@@ -8055,6 +7922,11 @@
       "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
       "dev": true
     },
+    "functions-have-names": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+      "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="
+    },
     "gar": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/gar/-/gar-1.0.4.tgz",
@@ -8559,7 +8431,6 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
       "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
-      "dev": true,
       "requires": {
         "has-symbols": "^1.0.2"
       },
@@ -8567,8 +8438,7 @@
         "has-symbols": {
           "version": "1.0.2",
           "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
-          "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
-          "dev": true
+          "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw=="
         }
       }
     },
@@ -8635,6 +8505,12 @@
       "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
       "dev": true
     },
+    "hexy": {
+      "version": "0.2.11",
+      "resolved": "https://registry.npmjs.org/hexy/-/hexy-0.2.11.tgz",
+      "integrity": "sha512-ciq6hFsSG/Bpt2DmrZJtv+56zpPdnq+NQ4ijEFrveKN0ZG1mhl/LdT1NQZ9se6ty1fACcI4d4vYqC9v8EYpH2A==",
+      "optional": true
+    },
     "hidepath": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/hidepath/-/hidepath-1.0.1.tgz",
@@ -9345,9 +9221,9 @@
       "integrity": "sha512-igASwWWkDY757OutNcM6zTtdJf/eTZYkoe2ymsX2qpm5bKZLo74FJYjsCtMQOEdY7dRHLLEulCyFQwdN69GBCg=="
     },
     "ip": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
-      "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz",
+      "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg=="
     },
     "ipaddr.js": {
       "version": "1.9.1",
@@ -9381,9 +9257,13 @@
       }
     },
     "is-arguments": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
-      "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA=="
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+      "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+      "requires": {
+        "call-bind": "^1.0.2",
+        "has-tostringtag": "^1.0.0"
+      }
     },
     "is-arrayish": {
       "version": "0.2.1",
@@ -9425,7 +9305,8 @@
     "is-callable": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz",
-      "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw=="
+      "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==",
+      "dev": true
     },
     "is-ci": {
       "version": "2.0.0",
@@ -9647,6 +9528,7 @@
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
       "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+      "dev": true,
       "requires": {
         "has-symbols": "^1.0.1"
       }
@@ -10418,6 +10300,12 @@
         }
       }
     },
+    "long": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz",
+      "integrity": "sha512-ZYvPPOMqUwPoDsbJaR10iQJYnMuZhRTvHYl62ErLIEX7RgFlziSBUUvrt3OVfc47QlHHpzPZYP17g3Fv7oeJkg==",
+      "optional": true
+    },
     "loose-envify": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -11464,7 +11352,7 @@
     "multicast-dns-service-types": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
-      "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE="
+      "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ=="
     },
     "multimatch": {
       "version": "4.0.0",
@@ -12061,15 +11949,16 @@
     "object-inspect": {
       "version": "1.8.0",
       "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
-      "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA=="
+      "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
+      "dev": true
     },
     "object-is": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz",
-      "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==",
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
+      "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
       "requires": {
-        "define-properties": "^1.1.3",
-        "es-abstract": "^1.17.5"
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3"
       }
     },
     "object-keys": {
@@ -12094,6 +11983,7 @@
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
       "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+      "dev": true,
       "requires": {
         "define-properties": "^1.1.2",
         "function-bind": "^1.1.1",
@@ -12616,6 +12506,24 @@
         }
       }
     },
+    "optimist": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+      "integrity": "sha512-snN4O4TkigujZphWLN0E//nQmm7790RYaE53DdL7ZYwee2D8DDo9/EyYiKUfN3rneWUjhJnueija3G9I2i0h3g==",
+      "optional": true,
+      "requires": {
+        "minimist": "~0.0.1",
+        "wordwrap": "~0.0.2"
+      },
+      "dependencies": {
+        "minimist": {
+          "version": "0.0.10",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+          "integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==",
+          "optional": true
+        }
+      }
+    },
     "optionator": {
       "version": "0.8.3",
       "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
@@ -13842,6 +13750,12 @@
         "escape-goat": "^2.0.0"
       }
     },
+    "put": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/put/-/put-0.0.6.tgz",
+      "integrity": "sha512-w0szIZ2NkqznMFqxYPRETCIi+q/S8UKis9F4yOl6/N9NDCZmbjZZT85aI4FgJf3vIPrzMPX60+odCLOaYxNWWw==",
+      "optional": true
+    },
     "q": {
       "version": "0.9.7",
       "resolved": "https://registry.npmjs.org/q/-/q-0.9.7.tgz",
@@ -14048,11 +13962,19 @@
       }
     },
     "randomstring": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/randomstring/-/randomstring-1.1.5.tgz",
-      "integrity": "sha1-bfBij3XL1ZMpMNn+OrTpVqGFGMM=",
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/randomstring/-/randomstring-1.2.3.tgz",
+      "integrity": "sha512-3dEFySepTzp2CvH6W/ASYGguPPveBuz5MpZ7MuoUkoVehmyNl9+F9c9GFVrz2QPbM9NXTIHGcmJDY/3j4677kQ==",
       "requires": {
-        "array-uniq": "1.0.2"
+        "array-uniq": "1.0.2",
+        "randombytes": "2.0.3"
+      },
+      "dependencies": {
+        "randombytes": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.3.tgz",
+          "integrity": "sha512-lDVjxQQFoCG1jcrP06LNo2lbWp4QTShEXnhActFBwYuHprllQV6VUpwreApsYqCgD+N1mHoqJ/BI/4eV4R2GYg=="
+        }
       }
     },
     "range-parser": {
@@ -14425,12 +14347,13 @@
       }
     },
     "regexp.prototype.flags": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz",
-      "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==",
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
+      "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
       "requires": {
+        "call-bind": "^1.0.2",
         "define-properties": "^1.1.3",
-        "es-abstract": "^1.17.0-next.1"
+        "functions-have-names": "^1.2.2"
       }
     },
     "regexpp": {
@@ -14628,11 +14551,13 @@
       }
     },
     "resin-discoverable-services": {
-      "version": "git+https://github.com/resin-io-modules/resin-discoverable-services.git#afca9e4700ec5ef82aa897f14bd5a46f06518061",
-      "from": "git+https://github.com/resin-io-modules/resin-discoverable-services.git#find-on-all-interfaces",
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/resin-discoverable-services/-/resin-discoverable-services-2.0.3.tgz",
+      "integrity": "sha512-JdQHlV2TrkXEUofo8D9u3a2aVvz4W0jEBVqzJL1K+V7mNkue4UIPbKtM7e1K0k0vCAim6rjPsPiIdkttK8p4Qw==",
       "requires": {
         "bluebird": "^3.0.0",
         "bonjour": "git+https://github.com/resin-io/bonjour.git#fixed-mdns",
+        "dbus-native": "^0.2.2",
         "ip": "^1.1.4",
         "lodash": "^4.17.4"
       }
@@ -14765,11 +14690,6 @@
       "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
       "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="
     },
-    "revalidator": {
-      "version": "0.3.1",
-      "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.3.1.tgz",
-      "integrity": "sha1-/yzEz3zHxjhaxxAXgnbm280Ddi8="
-    },
     "rewire": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/rewire/-/rewire-5.0.0.tgz",
@@ -14816,11 +14736,6 @@
         }
       }
     },
-    "rsync": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/rsync/-/rsync-0.4.0.tgz",
-      "integrity": "sha1-AnTzFjAHqUOW2Isc3rS73pLLncE="
-    },
     "run-async": {
       "version": "2.4.1",
       "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
@@ -15121,11 +15036,6 @@
         "rechoir": "^0.6.2"
       }
     },
-    "shellwords": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
-      "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww=="
-    },
     "side-channel": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
@@ -15588,28 +15498,32 @@
       "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
     },
     "ssh2": {
-      "version": "0.5.5",
-      "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-0.5.5.tgz",
-      "integrity": "sha1-x3gezS7OcwSiU89iD6taXCK7IjU=",
+      "version": "1.11.0",
+      "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.11.0.tgz",
+      "integrity": "sha512-nfg0wZWGSsfUe/IBJkXVll3PEZ//YH2guww+mP88gTpuSU4FtZN7zu9JoeTGOyCNx2dTDtT9fOpWwlzyj4uOOw==",
       "requires": {
-        "ssh2-streams": "~0.1.18"
+        "asn1": "^0.2.4",
+        "bcrypt-pbkdf": "^1.0.2",
+        "cpu-features": "~0.0.4",
+        "nan": "^2.16.0"
+      },
+      "dependencies": {
+        "nan": {
+          "version": "2.17.0",
+          "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz",
+          "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==",
+          "optional": true
+        }
       }
     },
     "ssh2-streams": {
-      "version": "0.1.20",
-      "resolved": "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.1.20.tgz",
-      "integrity": "sha1-URGNFUVV31Rp7h9n4M8efoosDjo=",
+      "version": "0.4.10",
+      "resolved": "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.4.10.tgz",
+      "integrity": "sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ==",
       "requires": {
         "asn1": "~0.2.0",
-        "semver": "^5.1.0",
+        "bcrypt-pbkdf": "^1.0.2",
         "streamsearch": "~0.1.2"
-      },
-      "dependencies": {
-        "semver": {
-          "version": "5.7.1",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
-          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
-        }
       }
     },
     "sshpk": {
@@ -15792,6 +15706,7 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
       "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
+      "dev": true,
       "requires": {
         "define-properties": "^1.1.3",
         "es-abstract": "^1.17.5"
@@ -15801,6 +15716,7 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
       "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
+      "dev": true,
       "requires": {
         "define-properties": "^1.1.3",
         "es-abstract": "^1.17.5"
@@ -16359,7 +16275,7 @@
     "thunky": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz",
-      "integrity": "sha1-vzAUaCTituZ7Dy16Ssi+smkIaE4="
+      "integrity": "sha512-vquTt/sKNzFqFK8DKLg33U7deg93WKYH4CE2Ul9hOyMCfm7VXgM7GJQRpPAgnmgnrf407Fcq8TQVEKlbavAu+A=="
     },
     "timed-out": {
       "version": "4.0.1",
@@ -17377,6 +17293,12 @@
       "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
       "dev": true
     },
+    "wordwrap": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+      "integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==",
+      "optional": true
+    },
     "workerpool": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz",
diff --git a/package.json b/package.json
index 5e2debf6..a98fa730 100644
--- a/package.json
+++ b/package.json
@@ -27,7 +27,6 @@
     "scripts": [
       "build/**/*.js",
       "node_modules/balena-sdk/es2018/index.js",
-      "node_modules/balena-sync/build/**/*.js",
       "node_modules/pinejs-client-request/node_modules/pinejs-client-core/es2018/index.js",
       "node_modules/@balena/compose/dist/parse/schemas/*.json"
     ],
@@ -212,7 +211,6 @@
     "balena-semver": "^2.3.0",
     "balena-settings-client": "^4.0.7",
     "balena-settings-storage": "^7.0.0",
-    "balena-sync": "^11.0.2",
     "bluebird": "^3.7.2",
     "body-parser": "^1.19.1",
     "chalk": "^3.0.0",
@@ -225,6 +223,7 @@
     "denymount": "^2.3.0",
     "docker-modem": "3.0.0",
     "docker-progress": "^5.1.3",
+    "docker-toolbelt": "^3.3.10",
     "dockerode": "^3.3.1",
     "ejs": "^3.1.6",
     "etcher-sdk": "^6.2.1",
@@ -262,6 +261,7 @@
     "request": "^2.88.2",
     "resin-cli-form": "^2.0.2",
     "resin-cli-visuals": "^1.8.0",
+    "resin-discoverable-services": "^2.0.3",
     "resin-doodles": "^0.2.0",
     "resin-stream-logger": "^0.1.2",
     "rimraf": "^3.0.2",
diff --git a/repo.yml b/repo.yml
index fb680504..27a0e9b6 100644
--- a/repo.yml
+++ b/repo.yml
@@ -10,8 +10,6 @@ upstream:
     url: 'https://github.com/balena-io-modules/balena-image-manager'
   - repo: 'balena-preload'
     url: 'https://github.com/balena-io-modules/balena-preload'
-  - repo: 'balena-sync'
-    url: 'https://github.com/balena-io-modules/balena-sync'
   - repo: 'etcher-sdk'
     url: 'https://github.com/balena-io-modules/etcher-sdk/'
   - repo: 'balena-compose'
diff --git a/tests/test-data/pkg/expected-warnings-darwin.txt b/tests/test-data/pkg/expected-warnings-darwin.txt
index 3d3cb304..da4c5ce2 100644
--- a/tests/test-data/pkg/expected-warnings-darwin.txt
+++ b/tests/test-data/pkg/expected-warnings-darwin.txt
@@ -1,21 +1,3 @@
-> Warning Cannot resolve 'module'
-  node_modules/balena-sync/build/index.js
-  Dynamic require may fail at run time, because the requested file
-  is unknown at compilation time and not included into executable.
-  Use a string literal as an argument for 'require', or leave it
-  as is and specify the resolved file name in 'scripts' option.
-> Warning Cannot resolve ''./' + command'
-  node_modules/balena-sync/build/capitano/index.js
-  Dynamic require may fail at run time, because the requested file
-  is unknown at compilation time and not included into executable.
-  Use a string literal as an argument for 'require', or leave it
-  as is and specify the resolved file name in 'scripts' option.
-> Warning Cannot resolve ''./' + target'
-  node_modules/balena-sync/build/sync/index.js
-  Dynamic require may fail at run time, because the requested file
-  is unknown at compilation time and not included into executable.
-  Use a string literal as an argument for 'require', or leave it
-  as is and specify the resolved file name in 'scripts' option.
 > Warning Cannot include file %1 into executable.
   The file must be distributed with executable as %2.
   %1: node_modules/open/xdg-open
diff --git a/tests/test-data/pkg/expected-warnings-linux.txt b/tests/test-data/pkg/expected-warnings-linux.txt
index 3d3cb304..da4c5ce2 100644
--- a/tests/test-data/pkg/expected-warnings-linux.txt
+++ b/tests/test-data/pkg/expected-warnings-linux.txt
@@ -1,21 +1,3 @@
-> Warning Cannot resolve 'module'
-  node_modules/balena-sync/build/index.js
-  Dynamic require may fail at run time, because the requested file
-  is unknown at compilation time and not included into executable.
-  Use a string literal as an argument for 'require', or leave it
-  as is and specify the resolved file name in 'scripts' option.
-> Warning Cannot resolve ''./' + command'
-  node_modules/balena-sync/build/capitano/index.js
-  Dynamic require may fail at run time, because the requested file
-  is unknown at compilation time and not included into executable.
-  Use a string literal as an argument for 'require', or leave it
-  as is and specify the resolved file name in 'scripts' option.
-> Warning Cannot resolve ''./' + target'
-  node_modules/balena-sync/build/sync/index.js
-  Dynamic require may fail at run time, because the requested file
-  is unknown at compilation time and not included into executable.
-  Use a string literal as an argument for 'require', or leave it
-  as is and specify the resolved file name in 'scripts' option.
 > Warning Cannot include file %1 into executable.
   The file must be distributed with executable as %2.
   %1: node_modules/open/xdg-open
diff --git a/tests/test-data/pkg/expected-warnings-win32.txt b/tests/test-data/pkg/expected-warnings-win32.txt
index 3dcf74e8..02504c40 100644
--- a/tests/test-data/pkg/expected-warnings-win32.txt
+++ b/tests/test-data/pkg/expected-warnings-win32.txt
@@ -1,21 +1,3 @@
-> Warning Cannot resolve 'module'
-  node_modules\balena-sync\build\index.js
-  Dynamic require may fail at run time, because the requested file
-  is unknown at compilation time and not included into executable.
-  Use a string literal as an argument for 'require', or leave it
-  as is and specify the resolved file name in 'scripts' option.
-> Warning Cannot resolve ''./' + command'
-  node_modules\balena-sync\build\capitano\index.js
-  Dynamic require may fail at run time, because the requested file
-  is unknown at compilation time and not included into executable.
-  Use a string literal as an argument for 'require', or leave it
-  as is and specify the resolved file name in 'scripts' option.
-> Warning Cannot resolve ''./' + target'
-  node_modules\balena-sync\build\sync\index.js
-  Dynamic require may fail at run time, because the requested file
-  is unknown at compilation time and not included into executable.
-  Use a string literal as an argument for 'require', or leave it
-  as is and specify the resolved file name in 'scripts' option.
 > Warning Cannot include file %1 into executable.
   The file must be distributed with executable as %2.
   %1: node_modules\open\xdg-open
diff --git a/typings/balena-sync/index.d.ts b/typings/balena-sync/index.d.ts
deleted file mode 100644
index 56c4e709..00000000
--- a/typings/balena-sync/index.d.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * @license
- * Copyright 2019 Balena Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-declare module 'balena-sync' {
-	import { CommandDefinition } from 'capitano';
-
-	export function capitano(tool: 'balena-cli'): CommandDefinition;
-
-	export interface LocalBalenaOsDevice {
-		address: string;
-		host: string;
-		osVariant: string;
-		port: number;
-	}
-
-	declare namespace forms {
-		export function selectLocalBalenaOsDevice(
-			timeout?: number,
-		): Promise<string>;
-	}
-
-	declare namespace discover {
-		export function discoverLocalBalenaOsDevices(
-			timeout?: number,
-		): Promise<LocalBalenaOsDevice[]>;
-	}
-}
diff --git a/typings/docker-toolbelt/index.d.ts b/typings/docker-toolbelt/index.d.ts
new file mode 100644
index 00000000..312ce210
--- /dev/null
+++ b/typings/docker-toolbelt/index.d.ts
@@ -0,0 +1,23 @@
+declare module 'docker-toolbelt' {
+	import * as Docker from 'dockerode';
+
+	interface ImageSpec {
+		registry?: string;
+		imageName: string;
+		tagName: string;
+		digest?: string;
+	}
+
+	type ProgressCallback = (event: any) => void;
+
+	class DockerToolbelt extends Docker {
+		public getRegistryAndName(image: string): Promise<ImageSpec>;
+		public createDeltaAsync(
+			src: string,
+			dest: string,
+			onProgress?: ProgressCallback,
+		): Promise<string>;
+	}
+
+	export = DockerToolbelt;
+}
diff --git a/typings/resin-cli-visuals/index.d.ts b/typings/resin-cli-visuals/index.d.ts
index f5f51a76..04cf8cc4 100644
--- a/typings/resin-cli-visuals/index.d.ts
+++ b/typings/resin-cli-visuals/index.d.ts
@@ -15,4 +15,25 @@
  * limitations under the License.
  */
 
-declare module 'resin-cli-visuals';
+declare module 'resin-cli-visuals' {
+	export const Progress: new (...options: any[]) => any;
+
+	export class Spinner {
+		constructor(message?: string);
+		spinner: any;
+		start(): void;
+		stop(): void;
+	}
+
+	export const SpinnerPromise: new <T>(options: {
+		promise: T;
+		startMessage: string;
+		stopMessage: string;
+	}) => T;
+
+	export const table: {
+		horizontal: (...options: any[]) => any;
+		vertical: (...options: any[]) => any;
+	};
+	export const drive: (...options: any[]) => any;
+}