appium 2.3.0 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +129 -158
  2. package/build/lib/appium.d.ts +72 -0
  3. package/build/lib/appium.d.ts.map +1 -1
  4. package/build/lib/appium.js +326 -0
  5. package/build/lib/appium.js.map +1 -1
  6. package/build/lib/cli/args.d.ts.map +1 -1
  7. package/build/lib/cli/args.js +24 -2
  8. package/build/lib/cli/args.js.map +1 -1
  9. package/build/lib/cli/driver-command.d.ts +16 -0
  10. package/build/lib/cli/driver-command.d.ts.map +1 -1
  11. package/build/lib/cli/driver-command.js +16 -0
  12. package/build/lib/cli/driver-command.js.map +1 -1
  13. package/build/lib/cli/extension-command.d.ts +26 -2
  14. package/build/lib/cli/extension-command.d.ts.map +1 -1
  15. package/build/lib/cli/extension-command.js +127 -1
  16. package/build/lib/cli/extension-command.js.map +1 -1
  17. package/build/lib/cli/parser.d.ts.map +1 -1
  18. package/build/lib/cli/parser.js +11 -7
  19. package/build/lib/cli/parser.js.map +1 -1
  20. package/build/lib/cli/plugin-command.d.ts +16 -0
  21. package/build/lib/cli/plugin-command.d.ts.map +1 -1
  22. package/build/lib/cli/plugin-command.js +16 -0
  23. package/build/lib/cli/plugin-command.js.map +1 -1
  24. package/build/lib/cli/utils.d.ts.map +1 -1
  25. package/build/lib/cli/utils.js +3 -4
  26. package/build/lib/cli/utils.js.map +1 -1
  27. package/build/lib/config.d.ts +1 -1
  28. package/build/lib/config.d.ts.map +1 -1
  29. package/build/lib/config.js +25 -29
  30. package/build/lib/config.js.map +1 -1
  31. package/build/lib/constants.d.ts +10 -0
  32. package/build/lib/constants.d.ts.map +1 -1
  33. package/build/lib/constants.js +11 -1
  34. package/build/lib/constants.js.map +1 -1
  35. package/build/lib/doctor/doctor.d.ts +55 -0
  36. package/build/lib/doctor/doctor.d.ts.map +1 -0
  37. package/build/lib/doctor/doctor.js +201 -0
  38. package/build/lib/doctor/doctor.js.map +1 -0
  39. package/build/lib/extension/index.d.ts.map +1 -1
  40. package/build/lib/extension/index.js +10 -4
  41. package/build/lib/extension/index.js.map +1 -1
  42. package/build/lib/grid-register.d.ts.map +1 -1
  43. package/build/lib/grid-register.js +1 -2
  44. package/build/lib/grid-register.js.map +1 -1
  45. package/build/lib/main.d.ts.map +1 -1
  46. package/build/lib/main.js +6 -0
  47. package/build/lib/main.js.map +1 -1
  48. package/build/lib/utils.d.ts +1 -0
  49. package/build/lib/utils.d.ts.map +1 -1
  50. package/build/lib/utils.js +2 -1
  51. package/build/lib/utils.js.map +1 -1
  52. package/build/types/cli.d.ts +1 -1
  53. package/build/types/cli.d.ts.map +1 -1
  54. package/lib/appium.js +387 -1
  55. package/lib/cli/args.js +27 -2
  56. package/lib/cli/driver-command.js +18 -0
  57. package/lib/cli/extension-command.js +152 -2
  58. package/lib/cli/parser.js +22 -8
  59. package/lib/cli/plugin-command.js +18 -0
  60. package/lib/cli/utils.js +5 -7
  61. package/lib/config.js +18 -24
  62. package/lib/constants.js +12 -0
  63. package/lib/doctor/doctor.js +209 -0
  64. package/lib/extension/index.js +10 -4
  65. package/lib/grid-register.js +1 -2
  66. package/lib/main.js +6 -0
  67. package/lib/utils.js +2 -1
  68. package/package.json +13 -13
  69. package/types/cli.ts +1 -1
@@ -66,6 +66,18 @@ export default class DriverCliCommand extends ExtensionCliCommand {
66
66
  });
67
67
  }
68
68
 
69
+ /**
70
+ * Runs doctor checks for the given driver
71
+ *
72
+ * @param {DriverDoctorOptions} opts
73
+ * @returns {Promise<import('@appium/types').IDoctorCheck[]>}
74
+ */
75
+ async doctor({driver}) {
76
+ return await super._doctor({
77
+ installSpec: driver,
78
+ });
79
+ }
80
+
69
81
  /**
70
82
  *
71
83
  * @param {import('./extension-command').ExtensionArgs} opts
@@ -151,3 +163,9 @@ export default class DriverCliCommand extends ExtensionCliCommand {
151
163
  * @property {string} scriptName - name of the script to run
152
164
  * @property {string[]} [extraArgs] - arguments to pass to the script
153
165
  */
166
+
167
+ /**
168
+ * Options for {@linkcode DriverCliCommand.doctor}.
169
+ * @typedef DriverDoctorOptions
170
+ * @property {string} driver - name of the driver to run doctor checks for
171
+ */
@@ -2,7 +2,7 @@
2
2
  import B from 'bluebird';
3
3
  import _ from 'lodash';
4
4
  import path from 'path';
5
- import {npm, util, env, console} from '@appium/support';
5
+ import {npm, util, env, console, fs, system} from '@appium/support';
6
6
  import {spinWith, RingBuffer} from './utils';
7
7
  import {
8
8
  INSTALL_TYPE_NPM,
@@ -15,6 +15,10 @@ import {SubProcess} from 'teen_process';
15
15
  import {packageDidChange} from '../extension/package-changed';
16
16
  import {spawn} from 'child_process';
17
17
  import {inspect} from 'node:util';
18
+ import {pathToFileURL} from 'url';
19
+ import {Doctor} from '../doctor/doctor';
20
+ import {npmPackage} from '../utils';
21
+ import semver from 'semver';
18
22
 
19
23
  const UPDATE_ALL = 'installed';
20
24
 
@@ -31,6 +35,23 @@ function receiptToManifest(receipt) {
31
35
  return /** @type {ExtManifest<ExtType>} */ (_.omit(receipt, 'driverName', 'pluginName'));
32
36
  }
33
37
 
38
+ /**
39
+ * Fetches the remote extension version requirements
40
+ *
41
+ * @param {string} pkgName Extension name
42
+ * @param {string} [pkgVer] Extension version (if not provided then the latest is assumed)
43
+ * @returns {Promise<[string, string|null]>}
44
+ */
45
+ async function getRemoteExtensionVersionReq(pkgName, pkgVer) {
46
+ const allDeps = await npm.getPackageInfo(
47
+ `${pkgName}${pkgVer ? `@${pkgVer}` : ``}`,
48
+ ['peerDependencies', 'dependencies']
49
+ );
50
+ const requiredVersionPair = _.flatMap(_.values(allDeps).map(_.toPairs))
51
+ .find(([name]) => name === 'appium');
52
+ return [npmPackage.version, requiredVersionPair ? requiredVersionPair[1] : null];
53
+ }
54
+
34
55
  /**
35
56
  * @template {ExtensionType} ExtType
36
57
  */
@@ -223,6 +244,29 @@ class ExtensionCliCommand {
223
244
  return listData;
224
245
  }
225
246
 
247
+ /**
248
+ * Checks whether the given extension is compatible with the currently installed server
249
+ *
250
+ * @param {InstallViaNpmArgs} installViaNpmOpts
251
+ * @returns {Promise<void>}
252
+ */
253
+ async _checkInstallCompatibility({installSpec, pkgName, pkgVer, installType}) {
254
+ if (INSTALL_TYPE_NPM !== installType) {
255
+ return;
256
+ }
257
+
258
+ await spinWith(this.isJsonOutput, `Checking if '${pkgName}' is compatible`, async () => {
259
+ const [serverVersion, extVersionRequirement] = await getRemoteExtensionVersionReq(pkgName, pkgVer);
260
+ if (serverVersion && extVersionRequirement && !semver.satisfies(serverVersion, extVersionRequirement)) {
261
+ throw this._createFatalError(
262
+ `'${installSpec}' cannot be installed because the server version it requires (${extVersionRequirement}) ` +
263
+ `does not meet the currently installed one (${serverVersion}). Please install ` +
264
+ `a compatible server version first.`
265
+ );
266
+ }
267
+ });
268
+ }
269
+
226
270
  /**
227
271
  * Install an extension
228
272
  *
@@ -332,6 +376,8 @@ class ExtensionCliCommand {
332
376
  );
333
377
  }
334
378
 
379
+ await this._checkInstallCompatibility(installViaNpmOpts);
380
+
335
381
  receipt = await this.installViaNpm(installViaNpmOpts);
336
382
 
337
383
  // this _should_ be the same as `probablyExtName` as the one derived above unless
@@ -695,6 +741,85 @@ class ExtensionCliCommand {
695
741
  });
696
742
  }
697
743
 
744
+ /**
745
+ * Runs doctor checks for the given extension
746
+ *
747
+ * @param {DoctorOptions} opts
748
+ * @returns {Promise<import('@appium/types').IDoctorCheck[]>}
749
+ */
750
+ async _doctor({installSpec}) {
751
+ if (!this.config.isInstalled(installSpec)) {
752
+ throw this._createFatalError(`The ${this.type} "${installSpec}" is not installed`);
753
+ }
754
+
755
+ const moduleRoot = this.config.getInstallPath(installSpec);
756
+ const packageJsonPath = path.join(moduleRoot, 'package.json');
757
+ if (!await fs.exists(packageJsonPath)) {
758
+ throw this._createFatalError(
759
+ `No package.json could be found for "${installSpec}" ${this.type}`
760
+ );
761
+ }
762
+ let doctorSpec;
763
+ try {
764
+ doctorSpec = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')).appium?.doctor;
765
+ } catch (e) {
766
+ throw this._createFatalError(
767
+ `The manifest at '${packageJsonPath}' cannot be parsed: ${e.message}`
768
+ );
769
+ }
770
+ if (!doctorSpec) {
771
+ this.log.info(`The ${this.type} "${installSpec}" does not export any doctor checks`);
772
+ return [];
773
+ }
774
+ if (!_.isPlainObject(doctorSpec) || !_.isArray(doctorSpec.checks)) {
775
+ throw this._createFatalError(
776
+ `The 'doctor' entry in the package manifest '${packageJsonPath}' must be a proper object ` +
777
+ `containing the 'checks' key with the array of script paths`
778
+ );
779
+ }
780
+ const paths = doctorSpec.checks.map((/** @type {string} */ p) => {
781
+ const scriptPath = path.resolve(moduleRoot, p);
782
+ if (!path.normalize(scriptPath).startsWith(path.normalize(moduleRoot))) {
783
+ this.log.error(
784
+ `The doctor check script '${p}' from the package manifest '${packageJsonPath}' must be located ` +
785
+ `in the '${moduleRoot}' root folder. It will be skipped`
786
+ );
787
+ return null;
788
+ }
789
+ return scriptPath;
790
+ }).filter(Boolean);
791
+ /** @type {Promise[]} */
792
+ const loadChecksPromises = [];
793
+ for (const p of paths) {
794
+ const promise = (async () => {
795
+ // https://github.com/nodejs/node/issues/31710
796
+ const scriptPath = system.isWindows() ? pathToFileURL(p).href : p;
797
+ try {
798
+ return await import(scriptPath);
799
+ } catch (e) {
800
+ this.log.warn(`Unable to load doctor checks from '${p}': ${e.message}`);
801
+ }
802
+ })();
803
+ loadChecksPromises.push(promise);
804
+ }
805
+ const isDoctorCheck = (/** @type {any} */ x) =>
806
+ ['diagnose', 'fix', 'hasAutofix', 'isOptional'].every((method) => _.isFunction(x?.[method]));
807
+ /** @type {import('@appium/types').IDoctorCheck[]} */
808
+ const checks = _.flatMap((await B.all(loadChecksPromises)).filter(Boolean).map(_.toPairs))
809
+ .map(([, value]) => value)
810
+ .filter(isDoctorCheck);
811
+ if (_.isEmpty(checks)) {
812
+ this.log.info(`The ${this.type} "${installSpec}" exports no valid doctor checks`);
813
+ return [];
814
+ }
815
+ this.log.debug(
816
+ `Running ${util.pluralize('doctor check', checks.length, true)} ` +
817
+ `for the "${installSpec}" ${this.type}`
818
+ );
819
+ await new Doctor(checks).run();
820
+ return checks;
821
+ }
822
+
698
823
  /**
699
824
  * Runs a script cached inside the `scripts` field under `appium`
700
825
  * inside of the extension's `package.json` file. Will throw
@@ -729,6 +854,24 @@ class ExtensionCliCommand {
729
854
  );
730
855
  }
731
856
 
857
+ if (!scriptName) {
858
+ const allScripts = _.toPairs(extScripts);
859
+ const root = this.config.getInstallPath(installSpec);
860
+ const existingScripts = await B.filter(
861
+ allScripts,
862
+ async ([, p]) => await fs.exists(path.join(root, p))
863
+ );
864
+ if (_.isEmpty(existingScripts)) {
865
+ this.log.info(`The ${this.type} named '${installSpec}' does not contain any scripts`);
866
+ } else {
867
+ this.log.info(`The ${this.type} named '${installSpec}' contains ` +
868
+ `${util.pluralize('script', existingScripts.length, true)}:`);
869
+ existingScripts.forEach(([name]) => this.log.info(` - ${name}`));
870
+ }
871
+ this.log.ok(`Successfully retrieved the list of scripts`.green);
872
+ return {};
873
+ }
874
+
732
875
  if (!(scriptName in /** @type {Record<string,string>} */ (extScripts))) {
733
876
  throw this._createFatalError(
734
877
  `The ${this.type} named '${installSpec}' does not support the script: '${scriptName}'`
@@ -874,11 +1017,18 @@ export {ExtensionCliCommand as ExtensionCommand};
874
1017
  * Options for {@linkcode ExtensionCliCommand._run}.
875
1018
  * @typedef RunOptions
876
1019
  * @property {string} installSpec - name of the extension to run a script from
877
- * @property {string} scriptName - name of the script to run
1020
+ * @property {string} [scriptName] - name of the script to run. If not provided
1021
+ * then all available script names will be printed
878
1022
  * @property {string[]} [extraArgs] - arguments to pass to the script
879
1023
  * @property {boolean} [bufferOutput] - if true, will buffer the output of the script and return it
880
1024
  */
881
1025
 
1026
+ /**
1027
+ * Options for {@linkcode ExtensionCliCommand.doctor}.
1028
+ * @typedef DoctorOptions
1029
+ * @property {string} installSpec - name of the extension to run doctor checks for
1030
+ */
1031
+
882
1032
  /**
883
1033
  * Return value of {@linkcode ExtensionCliCommand._run}
884
1034
  *
package/lib/cli/parser.js CHANGED
@@ -2,7 +2,17 @@ import {fs} from '@appium/support';
2
2
  import {ArgumentParser} from 'argparse';
3
3
  import _ from 'lodash';
4
4
  import path from 'path';
5
- import {DRIVER_TYPE, PLUGIN_TYPE, SERVER_SUBCOMMAND} from '../constants';
5
+ import {
6
+ DRIVER_TYPE,
7
+ EXT_SUBCOMMAND_DOCTOR,
8
+ EXT_SUBCOMMAND_INSTALL,
9
+ EXT_SUBCOMMAND_LIST,
10
+ EXT_SUBCOMMAND_RUN,
11
+ EXT_SUBCOMMAND_UNINSTALL,
12
+ EXT_SUBCOMMAND_UPDATE,
13
+ PLUGIN_TYPE,
14
+ SERVER_SUBCOMMAND
15
+ } from '../constants';
6
16
  import {finalizeSchema, getArgSpec, hasArgSpec} from '../schema';
7
17
  import {rootDir} from '../config';
8
18
  import {getExtensionArgs, getServerArgs} from './args';
@@ -191,8 +201,7 @@ class ArgParser {
191
201
 
192
202
  const serverArgs = getServerArgs();
193
203
  for (const [flagsOrNames, opts] of serverArgs) {
194
- // TS doesn't like the spread operator here.
195
- // @ts-ignore
204
+ // @ts-ignore TS doesn't like the spread operator here.
196
205
  serverParser.add_argument(...flagsOrNames, {...opts});
197
206
  }
198
207
 
@@ -221,33 +230,38 @@ class ArgParser {
221
230
  */
222
231
  const parserSpecs = [
223
232
  {
224
- command: 'list',
233
+ command: EXT_SUBCOMMAND_LIST,
225
234
  args: extensionArgs[type].list,
226
235
  help: `List available and installed ${type}s`,
227
236
  aliases: ['ls'],
228
237
  },
229
238
  {
230
- command: 'install',
239
+ command: EXT_SUBCOMMAND_INSTALL,
231
240
  args: extensionArgs[type].install,
232
241
  help: `Install a ${type}`,
233
242
  },
234
243
  {
235
- command: 'uninstall',
244
+ command: EXT_SUBCOMMAND_UNINSTALL,
236
245
  args: extensionArgs[type].uninstall,
237
246
  help: `Uninstall a ${type}`,
238
247
  },
239
248
  {
240
- command: 'update',
249
+ command: EXT_SUBCOMMAND_UPDATE,
241
250
  args: extensionArgs[type].update,
242
251
  help: `Update installed ${type}s to the latest version`,
243
252
  },
244
253
  {
245
- command: 'run',
254
+ command: EXT_SUBCOMMAND_RUN,
246
255
  args: extensionArgs[type].run,
247
256
  help:
248
257
  `Run a script (defined inside the ${type}'s package.json under the ` +
249
258
  `“scripts” field inside the “appium” field) from an installed ${type}`,
250
259
  },
260
+ {
261
+ command: EXT_SUBCOMMAND_DOCTOR,
262
+ args: extensionArgs[type].doctor,
263
+ help: `Run doctor checks (if any defined) for the given ${type}`,
264
+ },
251
265
  ];
252
266
 
253
267
  for (const {command, args, help, aliases} of parserSpecs) {
@@ -65,6 +65,18 @@ export default class PluginCliCommand extends ExtensionCliCommand {
65
65
  });
66
66
  }
67
67
 
68
+ /**
69
+ * Runs doctor checks for the given plugin
70
+ *
71
+ * @param {PluginDoctorOptions} opts
72
+ * @returns {Promise<import('@appium/types').IDoctorCheck[]>}
73
+ */
74
+ async doctor({plugin}) {
75
+ return await super._doctor({
76
+ installSpec: plugin,
77
+ });
78
+ }
79
+
68
80
  /**
69
81
  *
70
82
  * @param {import('./extension-command').ExtensionArgs} opts
@@ -141,3 +153,9 @@ export default class PluginCliCommand extends ExtensionCliCommand {
141
153
  * @property {string} scriptName - name of the script to run
142
154
  * @property {string[]} [extraArgs] - arguments to pass to the script
143
155
  */
156
+
157
+ /**
158
+ * Options for {@linkcode PluginCliCommand.doctor}.
159
+ * @typedef PluginDoctorOptions
160
+ * @property {string} plugin - name of the plugin to run doctor checks for
161
+ */
package/lib/cli/utils.js CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  import ora from 'ora';
4
4
 
5
- const JSON_SPACES = 4;
5
+ export const JSON_SPACES = 4;
6
6
 
7
7
  /***
8
8
  * Log an error to the console and exit the process.
9
9
  * @param {boolean} json - whether we should log json or text
10
10
  * @param {any} msg - error message, object, Error instance, etc.
11
11
  */
12
- function errAndQuit(json, msg) {
12
+ export function errAndQuit(json, msg) {
13
13
  if (json) {
14
14
  console.log(JSON.stringify({error: `${msg}`}, null, JSON_SPACES));
15
15
  } else {
@@ -26,7 +26,7 @@ function errAndQuit(json, msg) {
26
26
  * @param {boolean} json - whether we are in json mode (and should therefore not log)
27
27
  * @param {string} msg - string to log
28
28
  */
29
- function log(json, msg) {
29
+ export function log(json, msg) {
30
30
  !json && console.log(msg);
31
31
  }
32
32
 
@@ -36,7 +36,7 @@ function log(json, msg) {
36
36
  * @param {string} msg - string to log
37
37
  * @param {function} fn - function to wrap with spinning
38
38
  */
39
- async function spinWith(json, msg, fn) {
39
+ export async function spinWith(json, msg, fn) {
40
40
  if (json) {
41
41
  return await fn();
42
42
  }
@@ -52,7 +52,7 @@ async function spinWith(json, msg, fn) {
52
52
  }
53
53
  }
54
54
 
55
- class RingBuffer {
55
+ export class RingBuffer {
56
56
  constructor(size = 50) {
57
57
  this.size = size;
58
58
  this.buffer = [];
@@ -70,5 +70,3 @@ class RingBuffer {
70
70
  this.buffer.push(item);
71
71
  }
72
72
  }
73
-
74
- export {errAndQuit, log, spinWith, JSON_SPACES, RingBuffer};
package/lib/config.js CHANGED
@@ -4,17 +4,14 @@ import {system, fs} from '@appium/support';
4
4
  import axios from 'axios';
5
5
  import {exec} from 'teen_process';
6
6
  import semver from 'semver';
7
- import findUp from 'find-up';
8
7
  import os from 'node:os';
8
+ import {npmPackage} from './utils';
9
9
  import {getDefaultsForSchema, getAllArgSpecs} from './schema/schema';
10
10
 
11
- const npmPackage = fs.readPackageJsonFrom(__dirname);
12
-
13
- const APPIUM_VER = npmPackage.version;
11
+ export const APPIUM_VER = npmPackage.version;
14
12
  const ENGINES = /** @type {Record<string,string>} */ (npmPackage.engines);
15
13
  const MIN_NODE_VERSION = ENGINES.node;
16
14
 
17
- const GIT_META_ROOT = '.git';
18
15
  const GIT_BINARY = `git${system.isWindows() ? '.exe' : ''}`;
19
16
  const GITHUB_API = 'https://api.github.com/repos/appium/appium';
20
17
 
@@ -44,27 +41,25 @@ async function updateBuildInfo(useGithubApiFallback = false) {
44
41
  }
45
42
  }
46
43
 
47
- /**
48
- * Finds the Git metadata dir (see `GIT_META_ROOT`)
49
- *
50
- * This is needed because Appium cannot assume `package.json` and `.git` are in the same
51
- * directory. Monorepos, see?
52
- * @returns {Promise<string|undefined>} Path to dir or `undefined` if not found
53
- */
54
- async function findGitRoot() {
55
- return await findUp(GIT_META_ROOT, {cwd: rootDir, type: 'directory'});
56
- }
44
+ /** @type {() => Promise<string?>} */
45
+ const getFullGitPath = _.memoize(async function getFullGitPath() {
46
+ try {
47
+ return await fs.which(GIT_BINARY);
48
+ } catch {
49
+ return null;
50
+ }
51
+ });
57
52
 
58
53
  /**
59
54
  * @param {boolean} [useGithubApiFallback]
60
55
  * @returns {Promise<string?>}
61
56
  */
62
57
  async function getGitRev(useGithubApiFallback = false) {
63
- const gitRoot = await findGitRoot();
64
- if (gitRoot) {
58
+ const fullGitPath = await getFullGitPath();
59
+ if (fullGitPath) {
65
60
  try {
66
- const {stdout} = await exec(GIT_BINARY, ['rev-parse', 'HEAD'], {
67
- cwd: gitRoot,
61
+ const {stdout} = await exec(fullGitPath, ['rev-parse', 'HEAD'], {
62
+ cwd: __dirname,
68
63
  });
69
64
  return stdout.trim();
70
65
  } catch (ign) {}
@@ -94,11 +89,11 @@ async function getGitRev(useGithubApiFallback = false) {
94
89
  * @returns {Promise<string?>}
95
90
  */
96
91
  async function getGitTimestamp(commitSha, useGithubApiFallback = false) {
97
- const gitRoot = await findGitRoot();
98
- if (gitRoot) {
92
+ const fullGitPath = await getFullGitPath();
93
+ if (fullGitPath) {
99
94
  try {
100
- const {stdout} = await exec(GIT_BINARY, ['show', '-s', '--format=%ci', commitSha], {
101
- cwd: gitRoot,
95
+ const {stdout} = await exec(fullGitPath, ['show', '-s', '--format=%ci', commitSha], {
96
+ cwd: __dirname,
102
97
  });
103
98
  return stdout.trim();
104
99
  } catch (ign) {}
@@ -321,7 +316,6 @@ export {
321
316
  showBuildInfo,
322
317
  getNonDefaultServerArgs,
323
318
  getGitRev,
324
- APPIUM_VER,
325
319
  updateBuildInfo,
326
320
  showConfig,
327
321
  rootDir,
package/lib/constants.js CHANGED
@@ -64,6 +64,7 @@ export const EXT_SUBCOMMAND_INSTALL = 'install';
64
64
  export const EXT_SUBCOMMAND_UNINSTALL = 'uninstall';
65
65
  export const EXT_SUBCOMMAND_UPDATE = 'update';
66
66
  export const EXT_SUBCOMMAND_RUN = 'run';
67
+ export const EXT_SUBCOMMAND_DOCTOR = 'doctor';
67
68
 
68
69
  /**
69
70
  * Current revision of the manifest (`extensions.yaml`) schema
@@ -77,3 +78,14 @@ export const CURRENT_SCHEMA_REV = 4;
77
78
  * memory usage, perf, and/or log output, and higher limits may be difficult to scan.
78
79
  */
79
80
  export const LONG_STACKTRACE_LIMIT = 100;
81
+
82
+ /**
83
+ * Where should the bidi websocket handler live on the server?
84
+ */
85
+ export const BIDI_BASE_PATH = '/bidi';
86
+
87
+ /**
88
+ * The name of the event for drivers to emit when they want to send bidi events to a client over
89
+ * a bidi socket
90
+ */
91
+ export const BIDI_EVENT_NAME = 'bidiEvent';