diff --git a/bin/cml.js b/bin/cml.js index ac4c3ba9b..0d47325e9 100755 --- a/bin/cml.js +++ b/bin/cml.js @@ -3,7 +3,6 @@ const { basename } = require('path'); const { pseudoexec } = require('pseudoexec'); -const camelcaseKeys = require('camelcase-keys'); const which = require('which'); const winston = require('winston'); const yargs = require('yargs'); @@ -78,40 +77,25 @@ const setupLogger = (opts) => { const setupTelemetry = async (opts, { parsed: { defaulted } }) => { const { cml, _: command } = opts; - const action = command.join(':'); - - const allowedTelemetryOptions = { - 'runner:launch': { - cloud: 'plain', - cloudStartupScript: 'masked' - }, - 'comment:create': { - native: 'plain', - publishUrl: 'masked' - } - // IMPORTANT! --no-watermark and runner launch --reuse - }; - const options = Object.fromEntries( - Object.entries(opts) - .filter( - ([key]) => - Object.prototype.hasOwnProperty.call( - allowedTelemetryOptions[action] || {}, - key - ) && - !Object.prototype.hasOwnProperty.call(camelcaseKeys(defaulted), key) - ) - .map(([key, value]) => [ - key, - (allowedTelemetryOptions[action] || {})[key] === 'plain' ? value : '***' - ]) - ); + const options = {}; + for (const [name, option] of Object.entries(opts.options)) { + if (opts[name] && !defaulted[name]) { + switch (option.telemetry) { + case 'presence': + options[name] = null; + break; + case 'full': + options[name] = opts[name]; + break; + } + } + } opts.telemetryEvent = await jitsuEventPayload({ - action, - cml, - extra: { options } + action: command.join(':'), + extra: { options }, + cml }); }; diff --git a/bin/cml/asset/publish.js b/bin/cml/asset/publish.js index 208deb2cd..babe4bdfa 100644 --- a/bin/cml/asset/publish.js +++ b/bin/cml/asset/publish.js @@ -23,7 +23,12 @@ exports.handler = async (opts) => { else await fs.writeFile(file, output); }; -exports.builder = (yargs) => yargs.env('CML_ASSET').options(exports.options); +exports.builder = (yargs) => + yargs + .env('CML_ASSET') + .option('options', { default: exports.options, hidden: true }) + .options(exports.options) + .options(exports.options); exports.options = kebabcaseKeys({ url: { @@ -51,7 +56,8 @@ exports.options = kebabcaseKeys({ }, rmWatermark: { type: 'boolean', - description: 'Avoid CML watermark.' + description: 'Avoid CML watermark.', + telemetry: 'presence' }, mimeType: { type: 'string', diff --git a/bin/cml/check/create.js b/bin/cml/check/create.js index 43637d7b3..5a3508329 100755 --- a/bin/cml/check/create.js +++ b/bin/cml/check/create.js @@ -10,7 +10,12 @@ exports.handler = async (opts) => { await cml.checkCreate({ ...opts, report }); }; -exports.builder = (yargs) => yargs.env('CML_CHECK').options(exports.options); +exports.builder = (yargs) => + yargs + .env('CML_CHECK') + .option('options', { default: exports.options, hidden: true }) + .options(exports.options) + .options(exports.options); exports.options = kebabcaseKeys({ token: { diff --git a/bin/cml/comment/create.js b/bin/cml/comment/create.js index 59ea2a60d..e87c214ca 100644 --- a/bin/cml/comment/create.js +++ b/bin/cml/comment/create.js @@ -8,7 +8,12 @@ exports.handler = async (opts) => { console.log(await cml.commentCreate(opts)); }; -exports.builder = (yargs) => yargs.env('CML_COMMENT').options(exports.options); +exports.builder = (yargs) => + yargs + .env('CML_COMMENT') + .option('options', { default: exports.options, hidden: true }) + .options(exports.options) + .options(exports.options); exports.options = kebabcaseKeys({ pr: { @@ -29,7 +34,8 @@ exports.options = kebabcaseKeys({ publishUrl: { type: 'string', default: 'https://asset.cml.dev', - description: 'Self-hosted image server URL' + description: 'Self-hosted image server URL', + telemetry: 'presence' }, watch: { type: 'boolean', @@ -43,7 +49,8 @@ exports.options = kebabcaseKeys({ native: { type: 'boolean', description: - "Uses driver's native capabilities to upload assets instead of CML's storage; not available on GitHub" + "Uses driver's native capabilities to upload assets instead of CML's storage; not available on GitHub", + telemetry: 'presence' }, update: { type: 'boolean', @@ -54,6 +61,7 @@ exports.options = kebabcaseKeys({ rmWatermark: { type: 'boolean', description: - 'Avoid watermark; CML needs a watermark to be able to distinguish CML comments from others' + 'Avoid watermark; CML needs a watermark to be able to distinguish CML comments from others', + telemetry: 'presence' } }); diff --git a/bin/cml/pr/create.js b/bin/cml/pr/create.js index 3e8274104..a6ce0cee8 100755 --- a/bin/cml/pr/create.js +++ b/bin/cml/pr/create.js @@ -15,7 +15,12 @@ exports.handler = async (opts) => { console.log(link); }; -exports.builder = (yargs) => yargs.env('CML_PR').options(exports.options); +exports.builder = (yargs) => + yargs + .env('CML_PR') + .option('options', { default: exports.options, hidden: true }) + .options(exports.options) + .options(exports.options); exports.options = kebabcaseKeys({ md: { diff --git a/bin/cml/repo/prepare.js b/bin/cml/repo/prepare.js index b3218f788..65e11fb39 100644 --- a/bin/cml/repo/prepare.js +++ b/bin/cml/repo/prepare.js @@ -10,7 +10,12 @@ exports.handler = async (opts) => { await cml.ci(opts); }; -exports.builder = (yargs) => yargs.env('CML_REPO').options(exports.options); +exports.builder = (yargs) => + yargs + .env('CML_REPO') + .option('options', { default: exports.options, hidden: true }) + .options(exports.options) + .options(exports.options); exports.options = kebabcaseKeys({ unshallow: { diff --git a/bin/cml/runner/launch.js b/bin/cml/runner/launch.js index 669db00b2..36cbd2cc6 100755 --- a/bin/cml/runner/launch.js +++ b/bin/cml/runner/launch.js @@ -426,7 +426,11 @@ exports.handler = async (opts) => { } }; -exports.builder = (yargs) => yargs.env('CML_RUNNER').options(exports.options); +exports.builder = (yargs) => + yargs + .env('CML_RUNNER') + .option('options', { default: exports.options, hidden: true }) + .options(exports.options); exports.options = kebabcaseKeys({ labels: { @@ -462,13 +466,15 @@ exports.options = kebabcaseKeys({ type: 'boolean', conflicts: ['single', 'reuseIdle'], description: - "Don't launch a new runner if an existing one has the same name or overlapping labels" + "Don't launch a new runner if an existing one has the same name or overlapping labels", + telemetry: 'presence' }, reuseIdle: { type: 'boolean', conflicts: ['reuse', 'single'], description: - "Creates a new runner only if the matching labels don't exist or are already busy" + "Creates a new runner only if the matching labels don't exist or are already busy", + telemetry: 'presence' }, workdir: { type: 'string', @@ -484,7 +490,8 @@ exports.options = kebabcaseKeys({ cloud: { type: 'string', choices: ['aws', 'azure', 'gcp', 'kubernetes'], - description: 'Cloud to deploy the runner' + description: 'Cloud to deploy the runner', + telemetry: 'full' }, cloudRegion: { type: 'string', @@ -537,12 +544,14 @@ exports.options = kebabcaseKeys({ type: 'number', default: -1, description: - 'Maximum spot instance bidding price in USD. Defaults to the current spot bidding price' + 'Maximum spot instance bidding price in USD. Defaults to the current spot bidding price', + telemetry: 'presence' }, cloudStartupScript: { type: 'string', description: - 'Run the provided Base64-encoded Linux shell script during the instance initialization' + 'Run the provided Base64-encoded Linux shell script during the instance initialization', + telemetry: 'presence' }, cloudAwsSecurityGroup: { type: 'string', diff --git a/bin/cml/tensorboard/connect.js b/bin/cml/tensorboard/connect.js index 3353fc6b3..de9c99032 100644 --- a/bin/cml/tensorboard/connect.js +++ b/bin/cml/tensorboard/connect.js @@ -96,7 +96,11 @@ exports.handler = async (opts) => { }; exports.builder = (yargs) => - yargs.env('CML_TENSORBOARD').options(exports.options); + yargs + .env('CML_TENSORBOARD') + .option('options', { default: exports.options, hidden: true }) + .options(exports.options) + .options(exports.options); exports.options = kebabcaseKeys({ credentials: { @@ -137,6 +141,7 @@ exports.options = kebabcaseKeys({ }, rmWatermark: { type: 'boolean', - description: 'Avoid CML watermark' + description: 'Avoid CML watermark', + telemetry: 'presence' } }); diff --git a/bin/cml/workflow/rerun.js b/bin/cml/workflow/rerun.js index 18c5f0936..49956c304 100644 --- a/bin/cml/workflow/rerun.js +++ b/bin/cml/workflow/rerun.js @@ -8,7 +8,12 @@ exports.handler = async (opts) => { await cml.pipelineRerun(opts); }; -exports.builder = (yargs) => yargs.env('CML_WORKFLOW').options(exports.options); +exports.builder = (yargs) => + yargs + .env('CML_WORKFLOW') + .option('options', { default: exports.options, hidden: true }) + .options(exports.options) + .options(exports.options); exports.options = kebabcaseKeys({ id: { diff --git a/package-lock.json b/package-lock.json index 28461ad26..d30d2a7db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,6 @@ "@octokit/plugin-throttling": "^3.5.2", "@octokit/rest": "18.0.0", "appdirs": "^1.1.0", - "camelcase-keys": "^7.0.2", "chokidar": "^3.5.3", "colors": "1.4.0", "exponential-backoff": "^3.1.0", @@ -33,7 +32,6 @@ "node-fetch": "^2.6.5", "node-ssh": "^12.0.0", "os-name": "^5.0.1", - "pascalcase-keys": "^1.0.1", "proxy-agent": "^5.0.0", "pseudoexec": "^0.2.0", "remark": "^13.0.0", @@ -1866,15 +1864,6 @@ "node": ">=6" } }, - "node_modules/camel-case": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-1.2.2.tgz", - "integrity": "sha512-rUug78lL8mqStaLehmH2F0LxMJ2TM9fnPFxb+gFkgyUjUM/1o2wKTQtalypHnkb2cFwH/DENBw7YEAOYLgSMxQ==", - "dependencies": { - "sentence-case": "^1.1.1", - "upper-case": "^1.1.1" - } - }, "node_modules/camelcase": { "version": "5.3.1", "dev": true, @@ -1883,67 +1872,6 @@ "node": ">=6" } }, - "node_modules/camelcase-keys": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-7.0.2.tgz", - "integrity": "sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==", - "dependencies": { - "camelcase": "^6.3.0", - "map-obj": "^4.1.0", - "quick-lru": "^5.1.1", - "type-fest": "^1.2.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase-keys/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase-keys/node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase-keys/node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase-keys/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/caniuse-lite": { "version": "1.0.30001373", "dev": true, @@ -5330,11 +5258,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" - }, "node_modules/lru-cache": { "version": "5.1.1", "license": "ISC", @@ -5902,35 +5825,6 @@ "dev": true, "license": "MIT" }, - "node_modules/pascal-case": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-1.1.2.tgz", - "integrity": "sha512-QWlbdQHdKWlcyTEuv/M0noJtlCa7qTmg5QFAqhx5X9xjAfCU1kXucL+rcOmd2HliESuRLIOz8521RAW/yhuQog==", - "dependencies": { - "camel-case": "^1.1.1", - "upper-case-first": "^1.1.0" - } - }, - "node_modules/pascalcase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pascalcase-keys/-/pascalcase-keys-1.0.1.tgz", - "integrity": "sha512-sp1fJdQiCmuQ9zO5o91EMuDYSr6wy5LPzOISDO/9KQjr81seJ7fxWKECugcczU1C/4PflGuc6kONgErYxH+7tA==", - "dependencies": { - "map-obj": "^1.0.0", - "pascal-case": "^1.1.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pascalcase-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-exists": { "version": "3.0.0", "dev": true, @@ -6539,14 +6433,6 @@ "version": "4.0.0", "license": "ISC" }, - "node_modules/sentence-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-1.1.3.tgz", - "integrity": "sha512-laa/UDTPXsrQnoN/Kc8ZO7gTeEjMsuPiDgUCk9N0iINRZvqAMCTXjGl8+tD27op1eF/JHbdUlEUmovDh6AX7sA==", - "dependencies": { - "lower-case": "^1.1.1" - } - }, "node_modules/setprototypeof": { "version": "1.2.0", "license": "ISC" @@ -7296,19 +7182,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==" - }, - "node_modules/upper-case-first": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz", - "integrity": "sha512-wINKYvI3Db8dtjikdAqoBbZoP6Q+PZUyfMR7pmwHzjC2quzSkUq5DmPrTtPEqHaz8AGtmsB4TqwapMTM1QAQOQ==", - "dependencies": { - "upper-case": "^1.1.1" - } - }, "node_modules/uri-js": { "version": "4.4.1", "dev": true, @@ -8980,52 +8853,10 @@ "version": "3.1.0", "dev": true }, - "camel-case": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-1.2.2.tgz", - "integrity": "sha512-rUug78lL8mqStaLehmH2F0LxMJ2TM9fnPFxb+gFkgyUjUM/1o2wKTQtalypHnkb2cFwH/DENBw7YEAOYLgSMxQ==", - "requires": { - "sentence-case": "^1.1.1", - "upper-case": "^1.1.1" - } - }, "camelcase": { "version": "5.3.1", "dev": true }, - "camelcase-keys": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-7.0.2.tgz", - "integrity": "sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==", - "requires": { - "camelcase": "^6.3.0", - "map-obj": "^4.1.0", - "quick-lru": "^5.1.1", - "type-fest": "^1.2.1" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" - }, - "map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==" - }, - "quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" - }, - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==" - } - } - }, "caniuse-lite": { "version": "1.0.30001373", "dev": true @@ -11177,11 +11008,6 @@ "longest-streak": { "version": "2.0.4" }, - "lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" - }, "lru-cache": { "version": "5.1.1", "requires": { @@ -11539,31 +11365,6 @@ "version": "6.0.1", "dev": true }, - "pascal-case": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-1.1.2.tgz", - "integrity": "sha512-QWlbdQHdKWlcyTEuv/M0noJtlCa7qTmg5QFAqhx5X9xjAfCU1kXucL+rcOmd2HliESuRLIOz8521RAW/yhuQog==", - "requires": { - "camel-case": "^1.1.1", - "upper-case-first": "^1.1.0" - } - }, - "pascalcase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pascalcase-keys/-/pascalcase-keys-1.0.1.tgz", - "integrity": "sha512-sp1fJdQiCmuQ9zO5o91EMuDYSr6wy5LPzOISDO/9KQjr81seJ7fxWKECugcczU1C/4PflGuc6kONgErYxH+7tA==", - "requires": { - "map-obj": "^1.0.0", - "pascal-case": "^1.1.2" - }, - "dependencies": { - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==" - } - } - }, "path-exists": { "version": "3.0.0", "dev": true @@ -11909,14 +11710,6 @@ "version": "3.1.4", "dev": true }, - "sentence-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-1.1.3.tgz", - "integrity": "sha512-laa/UDTPXsrQnoN/Kc8ZO7gTeEjMsuPiDgUCk9N0iINRZvqAMCTXjGl8+tD27op1eF/JHbdUlEUmovDh6AX7sA==", - "requires": { - "lower-case": "^1.1.1" - } - }, "setprototypeof": { "version": "1.2.0" }, @@ -12378,19 +12171,6 @@ "picocolors": "^1.0.0" } }, - "upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==" - }, - "upper-case-first": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz", - "integrity": "sha512-wINKYvI3Db8dtjikdAqoBbZoP6Q+PZUyfMR7pmwHzjC2quzSkUq5DmPrTtPEqHaz8AGtmsB4TqwapMTM1QAQOQ==", - "requires": { - "upper-case": "^1.1.1" - } - }, "uri-js": { "version": "4.4.1", "dev": true, diff --git a/package.json b/package.json index 14214743b..3cb9180a7 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,6 @@ "@octokit/plugin-throttling": "^3.5.2", "@octokit/rest": "18.0.0", "appdirs": "^1.1.0", - "camelcase-keys": "^7.0.2", "chokidar": "^3.5.3", "colors": "1.4.0", "exponential-backoff": "^3.1.0", @@ -89,7 +88,6 @@ "node-fetch": "^2.6.5", "node-ssh": "^12.0.0", "os-name": "^5.0.1", - "pascalcase-keys": "^1.0.1", "proxy-agent": "^5.0.0", "pseudoexec": "^0.2.0", "remark": "^13.0.0",