├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── package-lock.json ├── package.json └── src ├── cli.js ├── deploy.js ├── load-project.js ├── now-client.js ├── poll-logs.js ├── scheduler.js ├── server.js └── string-or-env.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | .env -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ragtag 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # now-again 2 | 3 | A tool for running jobs using Zeit's [now.sh](https://zeit.co/now) deployment platform. 4 | 5 | It supports both scheduled (cron-style) jobs, as well as triggering jobs via HTTP. 6 | 7 | ## Why? 8 | 9 | Running one-off or scheduled tasks is often annoying, expensive, and error prone. `now-again` lets you run a set of jobs, each written in any language, using a simple and cost effective deployment platform without any server configuration. 10 | 11 | It's simpler than running tasks with AWS Lambda, and doesn't have the 5-minute runtime limit. 12 | 13 | It's cheaper and easier to deploy and manage than an EC2 instance running cron. 14 | 15 | It's better than setting up a bunch of scripts on a server, because each job can have its own dependencies and run in its own Docker container. 16 | 17 | You can see a simple example at [now-again-example](https://github.com/RagtagOpen/now-again-example). 18 | 19 | ## Caveats 20 | 21 | Because each task is deployed from source, run, and then deleted, the startup time is not instantaneous. You can expect node tasks to start executing in ~20 seconds, and simple Docker tasks to start executing in 1-2 minutes. 22 | 23 | This is acceptable for most periodic jobs, but if you need something with a faster response time, you may want to keep a dedicated server running that handles your task, or use something like AWS Lambda. 24 | 25 | ## Installation 26 | 27 | `now-again` requires node 7.6 or higher, for native async/await support. 28 | 29 | Install with `npm install --save RagtaOpen/now-again` 30 | 31 | ## Usage 32 | 33 | To run the scheduler: 34 | 35 | const nowAgain = require('now-again') 36 | nowAgain.scheduler() 37 | 38 | *TODO: add instructions for the HTTP job runner* 39 | 40 | ## Tasks 41 | 42 | `now-again` will look for tasks in a `tasks` directory in your project. Tasks can either be node-based or Docker-based. 43 | 44 | The only requirement for a task is that it prints `EOF` to standard output when it's done. If you're using node, that can be as simple as: 45 | 46 | index.js: 47 | 48 | console.log('Hello world!') 49 | console.log('EOF') 50 | 51 | package.json: 52 | 53 | { 54 | "scripts": { 55 | "start": "node index.js" 56 | } 57 | } 58 | 59 | If you want a specific return value, you can pass a string after EOF, like `EOF: some return value`. It will handled as a string, but we recommend using JSON in most cases. 60 | 61 | ## Configuration 62 | 63 | If you have a `task.json` file in your task directory, you can set some handy values. 64 | 65 | { 66 | "schedule": "every hour", 67 | "input": "cron cow!", 68 | "webhook": { 69 | "env": "MOO_WEBHOOK" 70 | }, 71 | "exposeEnv": [ 72 | "MESSAGE_PREFIX" 73 | ] 74 | } 75 | 76 | ### Schedule 77 | 78 | The `schedule` can either be a cron pattern or a [friendly-cron](https://www.npmjs.com/package/friendly-cron) string. 79 | 80 | ### Input 81 | 82 | `input` will be the job input. It can either be a string or a reference to an environment variable in the parent environment, like: 83 | "input" : { 84 | "env" : "SOME_JOB_INPUT" 85 | } 86 | 87 | ### Webhook 88 | 89 | If a `webhook` value is provided, `now-again` will send an HTTP POST to that url on job completion, with the following payload format: 90 | 91 | { 92 | date: , 93 | finished: true, 94 | type: 'result', 95 | text: , 96 | duration: { 97 | ms: , 98 | friendly: 99 | } 100 | } 101 | 102 | Note: the webhook is currently only called at the end of a successful run, but it will eventually be used to report errors, as well. 103 | 104 | We recommend creating a webhook with something like [Zapier](https://zapier.com/) to handle job completion notifications. 105 | 106 | ### ExposeEnv 107 | 108 | If you add values to the `exposeEnv` array, `now-again` will map environment variables from the parent context to the task's `.env` file. This is the recommended way to pass secrets to a task. 109 | 110 | Example: 111 | 112 | Add a now secret: 113 | 114 | now secrets add my-api-key [api key value] 115 | 116 | Expose the environment variable to your task: 117 | 118 | task.json: 119 | 120 | { 121 | "exposeEnv": ["API_KEY"] 122 | } 123 | 124 | Deploy your server with the secret mapped to an environment variable 125 | 126 | now deploy -e API_KEY=@my-api-key -e NOW_TOKEN=@now-token 127 | 128 | 129 | ## Input 130 | 131 | When a task is run, `now-again` will create (or append to) a `.env` file, setting `INPUT` with the job input. You should use [dotenv](https://www.npmjs.com/package/dotenv) or your language's equivalent to read these values. 132 | 133 | 134 | ## Example project 135 | 136 | Check out the [example project](https://github.com/RagtagOpen/now-again-example) to see it in action. 137 | 138 | ## Deployment 139 | 140 | First, make sure you have the [now tools](https://zeit.co/download) installed. 141 | 142 | Then, copy your token value from `~/.now.json` and set that as a secret: 143 | 144 | now secrets add now-token your_token_value 145 | 146 | Deploy your project with `now deploy -e NOW_TOKEN=@now-token` to make that secret available to your app. 147 | 148 | When deploying to now, make sure to use [now scale](https://zeit.co/blog/scale) to set the number of instances to 1, otherwise the server will go to sleep and the jobs won't run. 149 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const deploy = require('./src/deploy') 2 | const scheduler = require('./src/scheduler') 3 | const server = require('./src/server') 4 | 5 | module.exports = { 6 | deploy, 7 | scheduler, 8 | server 9 | } -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "now-again", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/events": { 8 | "version": "1.2.0", 9 | "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", 10 | "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==" 11 | }, 12 | "@types/glob": { 13 | "version": "7.1.1", 14 | "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", 15 | "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", 16 | "requires": { 17 | "@types/events": "*", 18 | "@types/minimatch": "*", 19 | "@types/node": "*" 20 | } 21 | }, 22 | "@types/minimatch": { 23 | "version": "3.0.3", 24 | "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", 25 | "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" 26 | }, 27 | "@types/node": { 28 | "version": "10.12.18", 29 | "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", 30 | "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" 31 | }, 32 | "ajv": { 33 | "version": "6.6.2", 34 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz", 35 | "integrity": "sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==", 36 | "requires": { 37 | "fast-deep-equal": "^2.0.1", 38 | "fast-json-stable-stringify": "^2.0.0", 39 | "json-schema-traverse": "^0.4.1", 40 | "uri-js": "^4.2.2" 41 | } 42 | }, 43 | "ansi-align": { 44 | "version": "2.0.0", 45 | "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", 46 | "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", 47 | "requires": { 48 | "string-width": "^2.0.0" 49 | } 50 | }, 51 | "ansi-regex": { 52 | "version": "2.1.1", 53 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 54 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 55 | }, 56 | "ansi-styles": { 57 | "version": "2.2.1", 58 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 59 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" 60 | }, 61 | "any-promise": { 62 | "version": "1.3.0", 63 | "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", 64 | "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" 65 | }, 66 | "args": { 67 | "version": "2.6.1", 68 | "resolved": "https://registry.npmjs.org/args/-/args-2.6.1.tgz", 69 | "integrity": "sha1-slkO1BaM0xtiREGZvcUWa7GSDC8=", 70 | "requires": { 71 | "camelcase": "4.1.0", 72 | "chalk": "1.1.3", 73 | "minimist": "1.2.0", 74 | "pkginfo": "0.4.0", 75 | "string-similarity": "1.1.0" 76 | } 77 | }, 78 | "asn1": { 79 | "version": "0.2.4", 80 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", 81 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", 82 | "requires": { 83 | "safer-buffer": "~2.1.0" 84 | } 85 | }, 86 | "assert-plus": { 87 | "version": "1.0.0", 88 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 89 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 90 | }, 91 | "async-to-gen": { 92 | "version": "1.3.3", 93 | "resolved": "https://registry.npmjs.org/async-to-gen/-/async-to-gen-1.3.3.tgz", 94 | "integrity": "sha1-1SyftIAfDfRKvE0t4YcLSLYOILs=", 95 | "requires": { 96 | "babylon": "^6.14.0", 97 | "magic-string": "^0.19.0" 98 | } 99 | }, 100 | "asynckit": { 101 | "version": "0.4.0", 102 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 103 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 104 | }, 105 | "aws-sign2": { 106 | "version": "0.7.0", 107 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 108 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" 109 | }, 110 | "aws4": { 111 | "version": "1.8.0", 112 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", 113 | "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" 114 | }, 115 | "babylon": { 116 | "version": "6.17.2", 117 | "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.17.2.tgz", 118 | "integrity": "sha1-IB0l71+JLEG65JSIsI2w3Udun1w=" 119 | }, 120 | "balanced-match": { 121 | "version": "1.0.0", 122 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 123 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 124 | }, 125 | "bcrypt-pbkdf": { 126 | "version": "1.0.2", 127 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 128 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 129 | "requires": { 130 | "tweetnacl": "^0.14.3" 131 | } 132 | }, 133 | "bluebird": { 134 | "version": "3.5.0", 135 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", 136 | "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=" 137 | }, 138 | "boxen": { 139 | "version": "1.1.0", 140 | "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.1.0.tgz", 141 | "integrity": "sha1-sbad1SIwXoB6md7ud329blFnsQI=", 142 | "requires": { 143 | "ansi-align": "^2.0.0", 144 | "camelcase": "^4.0.0", 145 | "chalk": "^1.1.1", 146 | "cli-boxes": "^1.0.0", 147 | "string-width": "^2.0.0", 148 | "term-size": "^0.1.0", 149 | "widest-line": "^1.0.0" 150 | } 151 | }, 152 | "brace-expansion": { 153 | "version": "1.1.11", 154 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 155 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 156 | "requires": { 157 | "balanced-match": "^1.0.0", 158 | "concat-map": "0.0.1" 159 | } 160 | }, 161 | "bytes": { 162 | "version": "2.4.0", 163 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz", 164 | "integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk=" 165 | }, 166 | "camelcase": { 167 | "version": "4.1.0", 168 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", 169 | "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" 170 | }, 171 | "capture-stack-trace": { 172 | "version": "1.0.0", 173 | "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", 174 | "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=" 175 | }, 176 | "caseless": { 177 | "version": "0.12.0", 178 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 179 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 180 | }, 181 | "chalk": { 182 | "version": "1.1.3", 183 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 184 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 185 | "requires": { 186 | "ansi-styles": "^2.2.1", 187 | "escape-string-regexp": "^1.0.2", 188 | "has-ansi": "^2.0.0", 189 | "strip-ansi": "^3.0.0", 190 | "supports-color": "^2.0.0" 191 | } 192 | }, 193 | "cli-boxes": { 194 | "version": "1.0.0", 195 | "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", 196 | "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=" 197 | }, 198 | "clipboardy": { 199 | "version": "1.1.1", 200 | "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-1.1.1.tgz", 201 | "integrity": "sha1-dbWnrFDYPJgCX7YwMpjGqQB8Fsc=", 202 | "requires": { 203 | "execa": "^0.6.0" 204 | }, 205 | "dependencies": { 206 | "execa": { 207 | "version": "0.6.3", 208 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.6.3.tgz", 209 | "integrity": "sha1-V7aaWU8IF1nGnlNw8NF7nLEWWP4=", 210 | "requires": { 211 | "cross-spawn": "^5.0.1", 212 | "get-stream": "^3.0.0", 213 | "is-stream": "^1.1.0", 214 | "npm-run-path": "^2.0.0", 215 | "p-finally": "^1.0.0", 216 | "signal-exit": "^3.0.0", 217 | "strip-eof": "^1.0.0" 218 | } 219 | }, 220 | "npm-run-path": { 221 | "version": "2.0.2", 222 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", 223 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", 224 | "requires": { 225 | "path-key": "^2.0.0" 226 | } 227 | }, 228 | "path-key": { 229 | "version": "2.0.1", 230 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 231 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" 232 | } 233 | } 234 | }, 235 | "code-point-at": { 236 | "version": "1.1.0", 237 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 238 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" 239 | }, 240 | "combined-stream": { 241 | "version": "1.0.7", 242 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", 243 | "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", 244 | "requires": { 245 | "delayed-stream": "~1.0.0" 246 | } 247 | }, 248 | "concat-map": { 249 | "version": "0.0.1", 250 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 251 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 252 | }, 253 | "configstore": { 254 | "version": "3.1.0", 255 | "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.0.tgz", 256 | "integrity": "sha1-Rd+QcHPibfoc9LLVL1tgVF6qEdE=", 257 | "requires": { 258 | "dot-prop": "^4.1.0", 259 | "graceful-fs": "^4.1.2", 260 | "make-dir": "^1.0.0", 261 | "unique-string": "^1.0.0", 262 | "write-file-atomic": "^2.0.0", 263 | "xdg-basedir": "^3.0.0" 264 | } 265 | }, 266 | "core-util-is": { 267 | "version": "1.0.2", 268 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 269 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 270 | }, 271 | "create-error-class": { 272 | "version": "3.0.2", 273 | "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", 274 | "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", 275 | "requires": { 276 | "capture-stack-trace": "^1.0.0" 277 | } 278 | }, 279 | "cron": { 280 | "version": "1.6.0", 281 | "resolved": "https://registry.npmjs.org/cron/-/cron-1.6.0.tgz", 282 | "integrity": "sha512-8OkXbeK3qF42ndzkIkCo3zfCDg2TxGjQiCQqVE+ZFFHa4vAcw0PdBc5i/6aJ9FppLncyKZmDuZdJ9/uuXEYZaw==", 283 | "requires": { 284 | "moment-timezone": "^0.5.x" 285 | } 286 | }, 287 | "cron-parser": { 288 | "version": "2.7.3", 289 | "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-2.7.3.tgz", 290 | "integrity": "sha512-t9Kc7HWBWPndBzvbdQ1YG9rpPRB37Tb/tTviziUOh1qs3TARGh3b1p+tnkOHNe1K5iI3oheBPgLqwotMM7+lpg==", 291 | "requires": { 292 | "is-nan": "^1.2.1", 293 | "moment-timezone": "^0.5.23" 294 | } 295 | }, 296 | "cross-spawn": { 297 | "version": "5.1.0", 298 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", 299 | "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", 300 | "requires": { 301 | "lru-cache": "^4.0.1", 302 | "shebang-command": "^1.2.0", 303 | "which": "^1.2.9" 304 | } 305 | }, 306 | "cross-spawn-async": { 307 | "version": "2.2.5", 308 | "resolved": "https://registry.npmjs.org/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz", 309 | "integrity": "sha1-hF/wwINKPe2dFg2sptOQkGuyiMw=", 310 | "requires": { 311 | "lru-cache": "^4.0.0", 312 | "which": "^1.2.8" 313 | } 314 | }, 315 | "crypto-random-string": { 316 | "version": "1.0.0", 317 | "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", 318 | "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=" 319 | }, 320 | "dashdash": { 321 | "version": "1.14.1", 322 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 323 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 324 | "requires": { 325 | "assert-plus": "^1.0.0" 326 | } 327 | }, 328 | "deep-extend": { 329 | "version": "0.6.0", 330 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 331 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" 332 | }, 333 | "define-properties": { 334 | "version": "1.1.3", 335 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 336 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", 337 | "requires": { 338 | "object-keys": "^1.0.12" 339 | } 340 | }, 341 | "delayed-stream": { 342 | "version": "1.0.0", 343 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 344 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 345 | }, 346 | "dot-prop": { 347 | "version": "4.1.1", 348 | "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.1.1.tgz", 349 | "integrity": "sha1-qEk/C3te7sglJbXHWH+n3nyoWcE=", 350 | "requires": { 351 | "is-obj": "^1.0.0" 352 | } 353 | }, 354 | "duplexer3": { 355 | "version": "0.1.4", 356 | "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", 357 | "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" 358 | }, 359 | "ecc-jsbn": { 360 | "version": "0.1.2", 361 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 362 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 363 | "requires": { 364 | "jsbn": "~0.1.0", 365 | "safer-buffer": "^2.1.0" 366 | } 367 | }, 368 | "escape-string-regexp": { 369 | "version": "1.0.5", 370 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 371 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 372 | }, 373 | "execa": { 374 | "version": "0.4.0", 375 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.4.0.tgz", 376 | "integrity": "sha1-TrZGejaglfq7KXD/nV4/t7zm68M=", 377 | "requires": { 378 | "cross-spawn-async": "^2.1.1", 379 | "is-stream": "^1.1.0", 380 | "npm-run-path": "^1.0.0", 381 | "object-assign": "^4.0.1", 382 | "path-key": "^1.0.0", 383 | "strip-eof": "^1.0.0" 384 | } 385 | }, 386 | "extend": { 387 | "version": "3.0.2", 388 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 389 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 390 | }, 391 | "extsprintf": { 392 | "version": "1.3.0", 393 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 394 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" 395 | }, 396 | "fast-deep-equal": { 397 | "version": "2.0.1", 398 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", 399 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" 400 | }, 401 | "fast-json-stable-stringify": { 402 | "version": "2.0.0", 403 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 404 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" 405 | }, 406 | "forever-agent": { 407 | "version": "0.6.1", 408 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 409 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 410 | }, 411 | "form-data": { 412 | "version": "2.3.3", 413 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 414 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 415 | "requires": { 416 | "asynckit": "^0.4.0", 417 | "combined-stream": "^1.0.6", 418 | "mime-types": "^2.1.12" 419 | } 420 | }, 421 | "friendly-cron": { 422 | "version": "0.0.1", 423 | "resolved": "https://registry.npmjs.org/friendly-cron/-/friendly-cron-0.0.1.tgz", 424 | "integrity": "sha512-gy+4SRLOlfFZZzr/GhIeJGTcyT9PxH1MMv9EUVJAO6C/oivyvbZwvRnsI4srpCiSio3KKNH+TpsfKjPczzrWVg==" 425 | }, 426 | "fs.realpath": { 427 | "version": "1.0.0", 428 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 429 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 430 | }, 431 | "get-port": { 432 | "version": "3.1.0", 433 | "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.1.0.tgz", 434 | "integrity": "sha1-7wGxioTKZIaXD/meVERhQac//T4=" 435 | }, 436 | "get-stream": { 437 | "version": "3.0.0", 438 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", 439 | "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" 440 | }, 441 | "getpass": { 442 | "version": "0.1.7", 443 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 444 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 445 | "requires": { 446 | "assert-plus": "^1.0.0" 447 | } 448 | }, 449 | "glob": { 450 | "version": "7.1.3", 451 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", 452 | "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", 453 | "requires": { 454 | "fs.realpath": "^1.0.0", 455 | "inflight": "^1.0.4", 456 | "inherits": "2", 457 | "minimatch": "^3.0.4", 458 | "once": "^1.3.0", 459 | "path-is-absolute": "^1.0.0" 460 | } 461 | }, 462 | "glob-promise": { 463 | "version": "3.4.0", 464 | "resolved": "https://registry.npmjs.org/glob-promise/-/glob-promise-3.4.0.tgz", 465 | "integrity": "sha512-q08RJ6O+eJn+dVanerAndJwIcumgbDdYiUT7zFQl3Wm1xD6fBKtah7H8ZJChj4wP+8C+QfeVy8xautR7rdmKEw==", 466 | "requires": { 467 | "@types/glob": "*" 468 | } 469 | }, 470 | "got": { 471 | "version": "6.7.1", 472 | "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", 473 | "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", 474 | "requires": { 475 | "create-error-class": "^3.0.0", 476 | "duplexer3": "^0.1.4", 477 | "get-stream": "^3.0.0", 478 | "is-redirect": "^1.0.0", 479 | "is-retry-allowed": "^1.0.0", 480 | "is-stream": "^1.0.0", 481 | "lowercase-keys": "^1.0.0", 482 | "safe-buffer": "^5.0.1", 483 | "timed-out": "^4.0.0", 484 | "unzip-response": "^2.0.1", 485 | "url-parse-lax": "^1.0.0" 486 | } 487 | }, 488 | "graceful-fs": { 489 | "version": "4.1.11", 490 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 491 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" 492 | }, 493 | "har-schema": { 494 | "version": "2.0.0", 495 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 496 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" 497 | }, 498 | "har-validator": { 499 | "version": "5.1.3", 500 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", 501 | "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", 502 | "requires": { 503 | "ajv": "^6.5.5", 504 | "har-schema": "^2.0.0" 505 | } 506 | }, 507 | "has-ansi": { 508 | "version": "2.0.0", 509 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 510 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 511 | "requires": { 512 | "ansi-regex": "^2.0.0" 513 | } 514 | }, 515 | "http-signature": { 516 | "version": "1.2.0", 517 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 518 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 519 | "requires": { 520 | "assert-plus": "^1.0.0", 521 | "jsprim": "^1.2.2", 522 | "sshpk": "^1.7.0" 523 | } 524 | }, 525 | "humanize-duration": { 526 | "version": "3.16.0", 527 | "resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.16.0.tgz", 528 | "integrity": "sha512-LDnNSe8EkJjhRsijWgUK12UK7ivbCww2R3LW9quKdBDoQjeAewqnGHch5AJTCOny1cPLhnXTzvniXVP3B9O6Ew==" 529 | }, 530 | "iconv-lite": { 531 | "version": "0.4.15", 532 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", 533 | "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=" 534 | }, 535 | "imurmurhash": { 536 | "version": "0.1.4", 537 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 538 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" 539 | }, 540 | "inflight": { 541 | "version": "1.0.6", 542 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 543 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 544 | "requires": { 545 | "once": "^1.3.0", 546 | "wrappy": "1" 547 | } 548 | }, 549 | "inherits": { 550 | "version": "2.0.3", 551 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 552 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 553 | }, 554 | "ini": { 555 | "version": "1.3.5", 556 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", 557 | "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" 558 | }, 559 | "ip": { 560 | "version": "1.1.5", 561 | "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", 562 | "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" 563 | }, 564 | "is-async-supported": { 565 | "version": "1.2.0", 566 | "resolved": "https://registry.npmjs.org/is-async-supported/-/is-async-supported-1.2.0.tgz", 567 | "integrity": "sha1-INWKxNZwfrHLNxLdOEgMBTbyfAc=" 568 | }, 569 | "is-fullwidth-code-point": { 570 | "version": "2.0.0", 571 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 572 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" 573 | }, 574 | "is-nan": { 575 | "version": "1.2.1", 576 | "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.2.1.tgz", 577 | "integrity": "sha1-n69ltvttskt/XAYoR16nH5iEAeI=", 578 | "requires": { 579 | "define-properties": "^1.1.1" 580 | } 581 | }, 582 | "is-npm": { 583 | "version": "1.0.0", 584 | "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", 585 | "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=" 586 | }, 587 | "is-obj": { 588 | "version": "1.0.1", 589 | "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", 590 | "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" 591 | }, 592 | "is-redirect": { 593 | "version": "1.0.0", 594 | "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", 595 | "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=" 596 | }, 597 | "is-retry-allowed": { 598 | "version": "1.1.0", 599 | "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", 600 | "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=" 601 | }, 602 | "is-stream": { 603 | "version": "1.1.0", 604 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 605 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" 606 | }, 607 | "is-typedarray": { 608 | "version": "1.0.0", 609 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 610 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 611 | }, 612 | "isexe": { 613 | "version": "2.0.0", 614 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 615 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" 616 | }, 617 | "isstream": { 618 | "version": "0.1.2", 619 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 620 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 621 | }, 622 | "jsbn": { 623 | "version": "0.1.1", 624 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 625 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" 626 | }, 627 | "json-schema": { 628 | "version": "0.2.3", 629 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 630 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 631 | }, 632 | "json-schema-traverse": { 633 | "version": "0.4.1", 634 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 635 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" 636 | }, 637 | "json-stringify-safe": { 638 | "version": "5.0.1", 639 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 640 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 641 | }, 642 | "jsprim": { 643 | "version": "1.4.1", 644 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 645 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 646 | "requires": { 647 | "assert-plus": "1.0.0", 648 | "extsprintf": "1.3.0", 649 | "json-schema": "0.2.3", 650 | "verror": "1.10.0" 651 | } 652 | }, 653 | "latest-version": { 654 | "version": "3.1.0", 655 | "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", 656 | "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", 657 | "requires": { 658 | "package-json": "^4.0.0" 659 | } 660 | }, 661 | "lazy-req": { 662 | "version": "2.0.0", 663 | "resolved": "https://registry.npmjs.org/lazy-req/-/lazy-req-2.0.0.tgz", 664 | "integrity": "sha1-yUUKNj7N2i5vDHATKtTzf48G8rQ=" 665 | }, 666 | "lodash": { 667 | "version": "4.17.14", 668 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", 669 | "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==" 670 | }, 671 | "lowercase-keys": { 672 | "version": "1.0.0", 673 | "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", 674 | "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=" 675 | }, 676 | "lru-cache": { 677 | "version": "4.0.2", 678 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", 679 | "integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4=", 680 | "requires": { 681 | "pseudomap": "^1.0.1", 682 | "yallist": "^2.0.0" 683 | } 684 | }, 685 | "magic-string": { 686 | "version": "0.19.1", 687 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.19.1.tgz", 688 | "integrity": "sha1-FNdoATyvLsj96hakmvgvw3fnUgE=", 689 | "requires": { 690 | "vlq": "^0.2.1" 691 | } 692 | }, 693 | "make-dir": { 694 | "version": "1.0.0", 695 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.0.0.tgz", 696 | "integrity": "sha1-l6ARdR6R3YfPre9Ygy67BJNt6Xg=", 697 | "requires": { 698 | "pify": "^2.3.0" 699 | } 700 | }, 701 | "media-typer": { 702 | "version": "0.3.0", 703 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 704 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 705 | }, 706 | "micro": { 707 | "version": "7.3.3", 708 | "resolved": "https://registry.npmjs.org/micro/-/micro-7.3.3.tgz", 709 | "integrity": "sha1-gmHFbSoxp9+TmG7/hkQTlvK0sHA=", 710 | "requires": { 711 | "args": "2.6.1", 712 | "async-to-gen": "1.3.3", 713 | "bluebird": "3.5.0", 714 | "boxen": "1.1.0", 715 | "chalk": "1.1.3", 716 | "clipboardy": "1.1.1", 717 | "get-port": "3.1.0", 718 | "ip": "1.1.5", 719 | "is-async-supported": "1.2.0", 720 | "isstream": "0.1.2", 721 | "media-typer": "0.3.0", 722 | "node-version": "1.0.0", 723 | "raw-body": "2.2.0", 724 | "update-notifier": "2.1.0" 725 | } 726 | }, 727 | "mime-db": { 728 | "version": "1.37.0", 729 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", 730 | "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" 731 | }, 732 | "mime-types": { 733 | "version": "2.1.21", 734 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", 735 | "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", 736 | "requires": { 737 | "mime-db": "~1.37.0" 738 | } 739 | }, 740 | "minimatch": { 741 | "version": "3.0.4", 742 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 743 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 744 | "requires": { 745 | "brace-expansion": "^1.1.7" 746 | } 747 | }, 748 | "minimist": { 749 | "version": "1.2.0", 750 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 751 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" 752 | }, 753 | "moment": { 754 | "version": "2.23.0", 755 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.23.0.tgz", 756 | "integrity": "sha512-3IE39bHVqFbWWaPOMHZF98Q9c3LDKGTmypMiTM2QygGXXElkFWIH7GxfmlwmY2vwa+wmNsoYZmG2iusf1ZjJoA==" 757 | }, 758 | "moment-timezone": { 759 | "version": "0.5.23", 760 | "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.23.tgz", 761 | "integrity": "sha512-WHFH85DkCfiNMDX5D3X7hpNH3/PUhjTGcD0U1SgfBGZxJ3qUmJh5FdvaFjcClxOvB3rzdfj4oRffbI38jEnC1w==", 762 | "requires": { 763 | "moment": ">= 2.9.0" 764 | } 765 | }, 766 | "mz": { 767 | "version": "2.7.0", 768 | "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", 769 | "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", 770 | "requires": { 771 | "any-promise": "^1.0.0", 772 | "object-assign": "^4.0.1", 773 | "thenify-all": "^1.0.0" 774 | } 775 | }, 776 | "native-or-bluebird": { 777 | "version": "1.2.0", 778 | "resolved": "https://registry.npmjs.org/native-or-bluebird/-/native-or-bluebird-1.2.0.tgz", 779 | "integrity": "sha1-OcR7/Xgl0fuf+tMiEK4l2q3xAck=" 780 | }, 781 | "node-version": { 782 | "version": "1.0.0", 783 | "resolved": "https://registry.npmjs.org/node-version/-/node-version-1.0.0.tgz", 784 | "integrity": "sha1-G5uVhKmn96YSPyFc0UplK/IasZ4=" 785 | }, 786 | "now-client": { 787 | "version": "0.7.0", 788 | "resolved": "https://registry.npmjs.org/now-client/-/now-client-0.7.0.tgz", 789 | "integrity": "sha1-DxHYdIdzO7xqCdbcW8Hd6T0DKvQ=", 790 | "requires": { 791 | "request": "^2.76.0", 792 | "request-promise-native": "^1.0.3" 793 | } 794 | }, 795 | "npm-run-path": { 796 | "version": "1.0.0", 797 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-1.0.0.tgz", 798 | "integrity": "sha1-9cMr9ZX+ga6Sfa7FLoL4sACsPI8=", 799 | "requires": { 800 | "path-key": "^1.0.0" 801 | } 802 | }, 803 | "number-is-nan": { 804 | "version": "1.0.1", 805 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 806 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" 807 | }, 808 | "oauth-sign": { 809 | "version": "0.9.0", 810 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 811 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" 812 | }, 813 | "object-assign": { 814 | "version": "4.1.1", 815 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 816 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 817 | }, 818 | "object-keys": { 819 | "version": "1.0.12", 820 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", 821 | "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==" 822 | }, 823 | "once": { 824 | "version": "1.4.0", 825 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 826 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 827 | "requires": { 828 | "wrappy": "1" 829 | } 830 | }, 831 | "p-finally": { 832 | "version": "1.0.0", 833 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 834 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" 835 | }, 836 | "package-json": { 837 | "version": "4.0.1", 838 | "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", 839 | "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", 840 | "requires": { 841 | "got": "^6.7.1", 842 | "registry-auth-token": "^3.0.1", 843 | "registry-url": "^3.0.3", 844 | "semver": "^5.1.0" 845 | } 846 | }, 847 | "path-is-absolute": { 848 | "version": "1.0.1", 849 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 850 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 851 | }, 852 | "path-key": { 853 | "version": "1.0.0", 854 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-1.0.0.tgz", 855 | "integrity": "sha1-XVPVeAGWRsDWiADbThRua9wqx68=" 856 | }, 857 | "performance-now": { 858 | "version": "2.1.0", 859 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 860 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" 861 | }, 862 | "pify": { 863 | "version": "2.3.0", 864 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 865 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" 866 | }, 867 | "pkginfo": { 868 | "version": "0.4.0", 869 | "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.0.tgz", 870 | "integrity": "sha1-NJ27f/04CB/K3AhT32h/DHdEzWU=" 871 | }, 872 | "prepend-http": { 873 | "version": "1.0.4", 874 | "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", 875 | "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" 876 | }, 877 | "pseudomap": { 878 | "version": "1.0.2", 879 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 880 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" 881 | }, 882 | "psl": { 883 | "version": "1.1.31", 884 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", 885 | "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" 886 | }, 887 | "punycode": { 888 | "version": "2.1.1", 889 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 890 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 891 | }, 892 | "qs": { 893 | "version": "6.5.2", 894 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 895 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 896 | }, 897 | "raw-body": { 898 | "version": "2.2.0", 899 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.2.0.tgz", 900 | "integrity": "sha1-mUl2z2pQlqQRYoQEkvC9xdbn+5Y=", 901 | "requires": { 902 | "bytes": "2.4.0", 903 | "iconv-lite": "0.4.15", 904 | "unpipe": "1.0.0" 905 | } 906 | }, 907 | "rc": { 908 | "version": "1.2.8", 909 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 910 | "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 911 | "requires": { 912 | "deep-extend": "^0.6.0", 913 | "ini": "~1.3.0", 914 | "minimist": "^1.2.0", 915 | "strip-json-comments": "~2.0.1" 916 | } 917 | }, 918 | "registry-auth-token": { 919 | "version": "3.3.1", 920 | "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.1.tgz", 921 | "integrity": "sha1-+w0yie4Nmtosu1KvXf5mywcNMAY=", 922 | "requires": { 923 | "rc": "^1.1.6", 924 | "safe-buffer": "^5.0.1" 925 | } 926 | }, 927 | "registry-url": { 928 | "version": "3.1.0", 929 | "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", 930 | "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", 931 | "requires": { 932 | "rc": "^1.0.1" 933 | } 934 | }, 935 | "request": { 936 | "version": "2.88.0", 937 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", 938 | "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", 939 | "requires": { 940 | "aws-sign2": "~0.7.0", 941 | "aws4": "^1.8.0", 942 | "caseless": "~0.12.0", 943 | "combined-stream": "~1.0.6", 944 | "extend": "~3.0.2", 945 | "forever-agent": "~0.6.1", 946 | "form-data": "~2.3.2", 947 | "har-validator": "~5.1.0", 948 | "http-signature": "~1.2.0", 949 | "is-typedarray": "~1.0.0", 950 | "isstream": "~0.1.2", 951 | "json-stringify-safe": "~5.0.1", 952 | "mime-types": "~2.1.19", 953 | "oauth-sign": "~0.9.0", 954 | "performance-now": "^2.1.0", 955 | "qs": "~6.5.2", 956 | "safe-buffer": "^5.1.2", 957 | "tough-cookie": "~2.4.3", 958 | "tunnel-agent": "^0.6.0", 959 | "uuid": "^3.3.2" 960 | }, 961 | "dependencies": { 962 | "safe-buffer": { 963 | "version": "5.1.2", 964 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 965 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 966 | } 967 | } 968 | }, 969 | "request-promise-core": { 970 | "version": "1.1.1", 971 | "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", 972 | "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", 973 | "requires": { 974 | "lodash": "^4.13.1" 975 | } 976 | }, 977 | "request-promise-native": { 978 | "version": "1.0.5", 979 | "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz", 980 | "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=", 981 | "requires": { 982 | "request-promise-core": "1.1.1", 983 | "stealthy-require": "^1.1.0", 984 | "tough-cookie": ">=2.3.3" 985 | }, 986 | "dependencies": { 987 | "tough-cookie": { 988 | "version": "2.5.0", 989 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", 990 | "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", 991 | "requires": { 992 | "psl": "^1.1.28", 993 | "punycode": "^2.1.1" 994 | } 995 | } 996 | } 997 | }, 998 | "safe-buffer": { 999 | "version": "5.1.0", 1000 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.0.tgz", 1001 | "integrity": "sha512-aSLEDudu6OoRr/2rU609gRmnYboRLxgDG1z9o2Q0os7236FwvcqIOO8r8U5JUEwivZOhDaKlFO4SbPTJYyBEyQ==" 1002 | }, 1003 | "safer-buffer": { 1004 | "version": "2.1.2", 1005 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1006 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1007 | }, 1008 | "semver": { 1009 | "version": "5.3.0", 1010 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", 1011 | "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" 1012 | }, 1013 | "semver-diff": { 1014 | "version": "2.1.0", 1015 | "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", 1016 | "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", 1017 | "requires": { 1018 | "semver": "^5.0.3" 1019 | } 1020 | }, 1021 | "shebang-command": { 1022 | "version": "1.2.0", 1023 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 1024 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 1025 | "requires": { 1026 | "shebang-regex": "^1.0.0" 1027 | } 1028 | }, 1029 | "shebang-regex": { 1030 | "version": "1.0.0", 1031 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 1032 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" 1033 | }, 1034 | "signal-exit": { 1035 | "version": "3.0.2", 1036 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 1037 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" 1038 | }, 1039 | "slide": { 1040 | "version": "1.1.6", 1041 | "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", 1042 | "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=" 1043 | }, 1044 | "sshpk": { 1045 | "version": "1.16.0", 1046 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.0.tgz", 1047 | "integrity": "sha512-Zhev35/y7hRMcID/upReIvRse+I9SVhyVre/KTJSJQWMz3C3+G+HpO7m1wK/yckEtujKZ7dS4hkVxAnmHaIGVQ==", 1048 | "requires": { 1049 | "asn1": "~0.2.3", 1050 | "assert-plus": "^1.0.0", 1051 | "bcrypt-pbkdf": "^1.0.0", 1052 | "dashdash": "^1.12.0", 1053 | "ecc-jsbn": "~0.1.1", 1054 | "getpass": "^0.1.1", 1055 | "jsbn": "~0.1.0", 1056 | "safer-buffer": "^2.0.2", 1057 | "tweetnacl": "~0.14.0" 1058 | } 1059 | }, 1060 | "stealthy-require": { 1061 | "version": "1.1.1", 1062 | "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", 1063 | "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" 1064 | }, 1065 | "string-similarity": { 1066 | "version": "1.1.0", 1067 | "resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-1.1.0.tgz", 1068 | "integrity": "sha1-PGZJiFikZex8QMfYFzm72ZWQSRQ=", 1069 | "requires": { 1070 | "lodash": "^4.13.1" 1071 | } 1072 | }, 1073 | "string-width": { 1074 | "version": "2.0.0", 1075 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.0.0.tgz", 1076 | "integrity": "sha1-Y1xUNsxypuDDh87KJ41OLuxSaH4=", 1077 | "requires": { 1078 | "is-fullwidth-code-point": "^2.0.0", 1079 | "strip-ansi": "^3.0.0" 1080 | } 1081 | }, 1082 | "strip-ansi": { 1083 | "version": "3.0.1", 1084 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 1085 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 1086 | "requires": { 1087 | "ansi-regex": "^2.0.0" 1088 | } 1089 | }, 1090 | "strip-eof": { 1091 | "version": "1.0.0", 1092 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", 1093 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" 1094 | }, 1095 | "strip-json-comments": { 1096 | "version": "2.0.1", 1097 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1098 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" 1099 | }, 1100 | "supports-color": { 1101 | "version": "2.0.0", 1102 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 1103 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" 1104 | }, 1105 | "term-size": { 1106 | "version": "0.1.1", 1107 | "resolved": "https://registry.npmjs.org/term-size/-/term-size-0.1.1.tgz", 1108 | "integrity": "sha1-hzYLljlsq1dgljcUzaDQy+7K2co=", 1109 | "requires": { 1110 | "execa": "^0.4.0" 1111 | } 1112 | }, 1113 | "then-sleep": { 1114 | "version": "1.0.1", 1115 | "resolved": "https://registry.npmjs.org/then-sleep/-/then-sleep-1.0.1.tgz", 1116 | "integrity": "sha1-dZgjvcTeVroqIIEoaOuHKoA+0fk=", 1117 | "requires": { 1118 | "native-or-bluebird": "^1.2.0" 1119 | } 1120 | }, 1121 | "thenify": { 1122 | "version": "3.3.0", 1123 | "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", 1124 | "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", 1125 | "requires": { 1126 | "any-promise": "^1.0.0" 1127 | } 1128 | }, 1129 | "thenify-all": { 1130 | "version": "1.6.0", 1131 | "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", 1132 | "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", 1133 | "requires": { 1134 | "thenify": ">= 3.1.0 < 4" 1135 | } 1136 | }, 1137 | "timed-out": { 1138 | "version": "4.0.1", 1139 | "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", 1140 | "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" 1141 | }, 1142 | "tough-cookie": { 1143 | "version": "2.4.3", 1144 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", 1145 | "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", 1146 | "requires": { 1147 | "psl": "^1.1.24", 1148 | "punycode": "^1.4.1" 1149 | }, 1150 | "dependencies": { 1151 | "punycode": { 1152 | "version": "1.4.1", 1153 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 1154 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" 1155 | } 1156 | } 1157 | }, 1158 | "tunnel-agent": { 1159 | "version": "0.6.0", 1160 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 1161 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 1162 | "requires": { 1163 | "safe-buffer": "^5.0.1" 1164 | } 1165 | }, 1166 | "tweetnacl": { 1167 | "version": "0.14.5", 1168 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 1169 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" 1170 | }, 1171 | "unique-string": { 1172 | "version": "1.0.0", 1173 | "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", 1174 | "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", 1175 | "requires": { 1176 | "crypto-random-string": "^1.0.0" 1177 | } 1178 | }, 1179 | "unpipe": { 1180 | "version": "1.0.0", 1181 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1182 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 1183 | }, 1184 | "unzip-response": { 1185 | "version": "2.0.1", 1186 | "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", 1187 | "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=" 1188 | }, 1189 | "update-notifier": { 1190 | "version": "2.1.0", 1191 | "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.1.0.tgz", 1192 | "integrity": "sha1-7AweU1NrdmR6JLd8uDlm2TFRI9k=", 1193 | "requires": { 1194 | "boxen": "^1.0.0", 1195 | "chalk": "^1.0.0", 1196 | "configstore": "^3.0.0", 1197 | "is-npm": "^1.0.0", 1198 | "latest-version": "^3.0.0", 1199 | "lazy-req": "^2.0.0", 1200 | "semver-diff": "^2.0.0", 1201 | "xdg-basedir": "^3.0.0" 1202 | } 1203 | }, 1204 | "uri-js": { 1205 | "version": "4.2.2", 1206 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 1207 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 1208 | "requires": { 1209 | "punycode": "^2.1.0" 1210 | } 1211 | }, 1212 | "url-parse-lax": { 1213 | "version": "1.0.0", 1214 | "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", 1215 | "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", 1216 | "requires": { 1217 | "prepend-http": "^1.0.1" 1218 | } 1219 | }, 1220 | "uuid": { 1221 | "version": "3.3.2", 1222 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 1223 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 1224 | }, 1225 | "verror": { 1226 | "version": "1.10.0", 1227 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 1228 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 1229 | "requires": { 1230 | "assert-plus": "^1.0.0", 1231 | "core-util-is": "1.0.2", 1232 | "extsprintf": "^1.2.0" 1233 | } 1234 | }, 1235 | "vlq": { 1236 | "version": "0.2.2", 1237 | "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.2.tgz", 1238 | "integrity": "sha1-4xbVJXtAuGu0PLjV/qXX9U1rDKE=" 1239 | }, 1240 | "which": { 1241 | "version": "1.2.14", 1242 | "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", 1243 | "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", 1244 | "requires": { 1245 | "isexe": "^2.0.0" 1246 | } 1247 | }, 1248 | "widest-line": { 1249 | "version": "1.0.0", 1250 | "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-1.0.0.tgz", 1251 | "integrity": "sha1-DAnIXCqUaD0Nfq+O4JfVZL8OEFw=", 1252 | "requires": { 1253 | "string-width": "^1.0.1" 1254 | }, 1255 | "dependencies": { 1256 | "is-fullwidth-code-point": { 1257 | "version": "1.0.0", 1258 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 1259 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 1260 | "requires": { 1261 | "number-is-nan": "^1.0.0" 1262 | } 1263 | }, 1264 | "string-width": { 1265 | "version": "1.0.2", 1266 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 1267 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 1268 | "requires": { 1269 | "code-point-at": "^1.0.0", 1270 | "is-fullwidth-code-point": "^1.0.0", 1271 | "strip-ansi": "^3.0.0" 1272 | } 1273 | } 1274 | } 1275 | }, 1276 | "wrappy": { 1277 | "version": "1.0.2", 1278 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1279 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1280 | }, 1281 | "write-file-atomic": { 1282 | "version": "2.1.0", 1283 | "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.1.0.tgz", 1284 | "integrity": "sha512-0TZ20a+xcIl4u0+Mj5xDH2yOWdmQiXlKf9Hm+TgDXjTMsEYb+gDrmb8e8UNAzMCitX8NBqG4Z/FUQIyzv/R1JQ==", 1285 | "requires": { 1286 | "graceful-fs": "^4.1.11", 1287 | "imurmurhash": "^0.1.4", 1288 | "slide": "^1.1.5" 1289 | } 1290 | }, 1291 | "xdg-basedir": { 1292 | "version": "3.0.0", 1293 | "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", 1294 | "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=" 1295 | }, 1296 | "yallist": { 1297 | "version": "2.1.2", 1298 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 1299 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" 1300 | } 1301 | } 1302 | } 1303 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "now-again", 3 | "version": "0.0.1", 4 | "description": "Run jobs with now.sh", 5 | "main": "index.js", 6 | "repository": "git@github.com:jkriss/now-again.git", 7 | "author": "Jesse Kriss ", 8 | "license": "MIT", 9 | "bin": { 10 | "now-again": "./src/cli.js" 11 | }, 12 | "dependencies": { 13 | "cron": "^1.6.0", 14 | "cron-parser": "^2.7.3", 15 | "friendly-cron": "^0.0.1", 16 | "glob": "^7.1.3", 17 | "glob-promise": "^3.4.0", 18 | "humanize-duration": "^3.16.0", 19 | "micro": "^7.3.3", 20 | "mz": "^2.7.0", 21 | "now-client": "^0.7.0", 22 | "request-promise-native": "^1.0.4", 23 | "request": "^2.81.0", 24 | "then-sleep": "^1.0.1" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const argv = require('minimist')(process.argv.slice(2)) 4 | const fs = require('fs') 5 | const deploy = require('./deploy') 6 | const NowClient = require('./now-client') 7 | 8 | const command = argv._[0] 9 | 10 | switch (command) { 11 | case 'run': 12 | const jobName = argv._[1] 13 | const input = argv.input 14 | console.log(`Running ${jobName} with input ${input}`) 15 | deploy(jobName, input) 16 | break; 17 | case 'add-token-secret': 18 | const client = new NowClient() 19 | const token = client.getToken() 20 | client.addSecret('now-token', token) 21 | .then(() => console.log("added token secret as @now-token")) 22 | .catch(err => console.log("Error adding token:", err.message || err)) 23 | break; 24 | 25 | default: 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/deploy.js: -------------------------------------------------------------------------------- 1 | const NowClient = require('./now-client') 2 | const loadProject = require('./load-project') 3 | const pollLogs = require('./poll-logs') 4 | const fs = require('mz/fs') 5 | const Path = require('path') 6 | 7 | const run = async (jobName, input, onMessage) => { 8 | 9 | const now = new NowClient() 10 | 11 | let cancel 12 | 13 | process.on('SIGINT', async () => { 14 | console.log("-- canceled, deleting deployment") 15 | if (typeof cancel === 'function') await cancel() 16 | process.exit() 17 | }) 18 | 19 | const projectFiles = await loadProject(`tasks/${jobName}`) 20 | const log = (message) => { 21 | if (onMessage) onMessage({ type: 'status', text: message, date: new Date().toISOString()}) 22 | else console.log(message) 23 | } 24 | if (!projectFiles['.env']) projectFiles['.env'] = "" 25 | if (input) { 26 | console.log("input:", input) 27 | projectFiles['.env'] += `\nINPUT=${JSON.stringify(input)}` 28 | } 29 | // if there's a task.json, respect the env mappings 30 | try { 31 | const taskOpts = JSON.parse(await fs.readFile(Path.join(process.cwd(), `./tasks/${jobName}/task.json`))) 32 | if (taskOpts.exposeEnv) { 33 | taskOpts.exposeEnv.forEach(envVarName => { 34 | projectFiles['.env'] += `\n${envVarName}=${process.env[envVarName]}` 35 | }) 36 | } 37 | } catch (e) { 38 | console.log("error reading task.json, skipping", e) 39 | } 40 | // console.log("project files:", projectFiles) 41 | log("-- deploying") 42 | const deployment = await now.createDeployment(projectFiles) 43 | console.log("-- deployment info:", deployment) 44 | cancel = () => now.deleteDeployment(deployment.uid) 45 | 46 | try { 47 | log("-- polling for logs") 48 | 49 | const printLogs = (logs) => { 50 | console.log(logs.map(line => `${line.type}: ${line.text}`).join("\n")) 51 | if (onMessage) logs.map(onMessage) 52 | } 53 | 54 | const jobResult = await pollLogs(deployment.uid, {}, printLogs) 55 | console.log("-- result:", jobResult) 56 | console.log("-- deleting deployment") 57 | 58 | await now.deleteDeployment(deployment.uid) 59 | console.log("done!") 60 | 61 | try { 62 | return JSON.parse(jobResult) 63 | } catch (e) { 64 | return jobResult 65 | } 66 | } catch (runError) { 67 | await now.deleteDeployment(deployment.uid) 68 | throw runError 69 | } 70 | } 71 | 72 | module.exports = run 73 | -------------------------------------------------------------------------------- /src/load-project.js: -------------------------------------------------------------------------------- 1 | const glob = require('glob') 2 | const Path = require('path') 3 | const fs = require('fs') 4 | 5 | function loadProject(path) { 6 | const pattern = `${Path.join(process.cwd(), path)}/**` 7 | // console.log("loading", pattern) 8 | return new Promise((resolve, reject) => { 9 | glob(pattern, function(err, files) { 10 | // console.log("got files:", files) 11 | if (err) return reject(err) 12 | let data = {} 13 | files.forEach(function(f) { 14 | // TODO read .gitignore 15 | if (f.match(/node_modules/)) return 16 | if (!fs.statSync(f).isDirectory()) { 17 | let relativePath = Path.relative(path, f) 18 | if (relativePath === 'package.json') relativePath = 'package' 19 | data[relativePath] = fs.readFileSync(f, 'utf8') 20 | if (relativePath === 'package') data[relativePath] = JSON.parse(data[relativePath]) 21 | // console.log(relativePath) 22 | } 23 | }) 24 | resolve(data) 25 | }) 26 | }) 27 | } 28 | 29 | if (require.main === module) { 30 | loadProject('./tasks/python-test').then(console.log) 31 | } 32 | 33 | module.exports = loadProject -------------------------------------------------------------------------------- /src/now-client.js: -------------------------------------------------------------------------------- 1 | const rp = require('request-promise-native') 2 | const path = require('path') 3 | const os = require('os') 4 | 5 | class NowClient { 6 | constructor(token) { 7 | this.token = token 8 | } 9 | 10 | getToken() { 11 | if (!this.token) this.token = process.env.NOW_TOKEN 12 | 13 | if (!this.token) { 14 | try { 15 | const configPath = path.join(os.homedir(), '.now.json') 16 | this.token = require(configPath).token // eslint-disable-line global-require, import/no-dynamic-require 17 | } catch (err) { 18 | console.error(`Error: ${err}`) 19 | } 20 | } 21 | 22 | if (!this.token) throw new Error("Couldn't get token from NOW_TOKEN or ~/.now.json") 23 | 24 | return this.token 25 | } 26 | 27 | reqOpts(method, uri, body) { 28 | return { 29 | method: method, 30 | body: body, 31 | uri: `https://api.zeit.co/now${uri}`, 32 | headers: { 33 | Authorization: `Bearer ${this.getToken()}` 34 | }, 35 | json: true 36 | } 37 | } 38 | 39 | get(uri) { 40 | return rp(this.reqOpts('GET', uri)) 41 | } 42 | 43 | addSecret(name, value) { 44 | return rp(this.reqOpts('POST', '/secrets', {name, value})) 45 | } 46 | 47 | createDeployment(body) { 48 | return rp(this.reqOpts('POST', '/deployments', body)) 49 | } 50 | 51 | deleteDeployment(id) { 52 | return rp(this.reqOpts('DELETE', `/deployments/${id}`)) 53 | } 54 | 55 | 56 | } 57 | 58 | module.exports = NowClient 59 | -------------------------------------------------------------------------------- /src/poll-logs.js: -------------------------------------------------------------------------------- 1 | const Now = require('./now-client') 2 | const sleep = require('then-sleep') 3 | 4 | const EOF_PREFIX = /EOF:/ 5 | 6 | const pollLogs = async function(uid, opts, onMessage, filterFn) { 7 | const now = new Now(opts.token) 8 | let logIds = [] 9 | const read = async (since, filterFn) => { 10 | if (!since) since = new Date(0) 11 | const url = `/deployments/${uid}/logs?since=${since.getTime()}` 12 | // console.log(url) 13 | let lines = [] 14 | const response = await now.get(url) 15 | response.logs.forEach(log => { 16 | if (logIds.includes(log.id)) return 17 | const logLines = log.text.trim().split('\n') 18 | logLines.forEach(line => { 19 | lines.push(Object.assign({}, log, { text: line })) 20 | }) 21 | logIds.push(log.id) 22 | }) 23 | if (lines.length > 0) since = new Date(lines[lines.length-1].date) 24 | if (filterFn) lines = lines.filter(filterFn) 25 | if (lines.length > 0) onMessage(lines) 26 | const lastLine = lines.find(line => line.text.match(/^EOF/) && line.type === 'stdout') 27 | if (lastLine) { 28 | if (lastLine.text.match(EOF_PREFIX)) { 29 | return lastLine.text.replace(EOF_PREFIX,'').trim() 30 | } else { 31 | return 32 | } 33 | } else { 34 | await sleep(1000) 35 | return read(since, filterFn) 36 | } 37 | } 38 | return read(0, filterFn) 39 | } 40 | 41 | module.exports = pollLogs 42 | -------------------------------------------------------------------------------- /src/scheduler.js: -------------------------------------------------------------------------------- 1 | const glob = require('glob-promise') 2 | const fs = require('mz/fs') 3 | const cronParser = require('cron-parser') 4 | const CronJob = require('cron').CronJob 5 | const friendlyCron = require('friendly-cron') 6 | const deploy = require('./deploy') 7 | const humanizeDuration = require('humanize-duration') 8 | const rp = require('request-promise-native') 9 | const stringOrEnv = require('./string-or-env') 10 | 11 | const loadSchedules = async (stuff) => { 12 | const configFiles = await glob('tasks/*/task.json') 13 | return await Promise.all(configFiles.map(async f => { 14 | const config = JSON.parse(await fs.readFile(f, 'utf8')) 15 | const name = f.split('/')[1] 16 | const pattern = friendlyCron(config.schedule) ? friendlyCron(config.schedule) : config.schedule 17 | const interval = cronParser.parseExpression(pattern) 18 | console.log(` 19 | ${name} will run at: 20 | ${interval.next().toString()}, 21 | ${interval.next().toString()}, 22 | ${interval.next().toString()}, 23 | etc. 24 | `) 25 | return { 26 | name, 27 | schedule: config.schedule, 28 | pattern, 29 | input: stringOrEnv(config.input), 30 | webhook: stringOrEnv(config.webhook) } 31 | })) 32 | } 33 | 34 | const runJobs = (jobs) => { 35 | console.log("running jobs", jobs) 36 | jobs.forEach(job => { 37 | new CronJob(job.pattern, function() { 38 | // console.log("pretending to run", job.name) 39 | const startTime = new Date() 40 | deploy(job.name, job.input, (logLine) => { 41 | // console.log("server saw:", logLine) 42 | const { type, text, date } = logLine 43 | console.log(job.name, date, type, text) 44 | }).then(result => { 45 | if (job.webhook) { 46 | console.log("-- posting to webhook") 47 | const endTime = new Date() 48 | const duration = endTime.getTime() - startTime.getTime() 49 | const resultLog = { 50 | date: endTime.toISOString(), 51 | finished: true, 52 | type: 'result', 53 | text: result, 54 | duration: { 55 | ms: duration, 56 | friendly: humanizeDuration(duration) 57 | } 58 | } 59 | rp({ 60 | uri: job.webhook, 61 | method: 'POST', 62 | body: resultLog, 63 | json: true 64 | }).then(console.log) 65 | } 66 | }).catch(err => { 67 | console.log("Error running job:", err, err.stack) 68 | }) 69 | 70 | }, null, true, 'Etc/UTC') 71 | }) 72 | } 73 | 74 | const run = () => { 75 | loadSchedules().then(runJobs) 76 | } 77 | 78 | if (require.main === module) { 79 | run() 80 | } 81 | 82 | module.exports = run 83 | -------------------------------------------------------------------------------- /src/server.js: -------------------------------------------------------------------------------- 1 | const querystring = require('querystring') 2 | const { json, send } = require('micro') 3 | const deploy = require('./deploy') 4 | const humanizeDuration = require('humanize-duration') 5 | const rp = require('request-promise-native') 6 | 7 | module.exports = async (req, res) => { 8 | try { 9 | let [path, qs] = req.url.split('?') 10 | let job, input, webhook 11 | if (qs) { 12 | qs = querystring.parse(qs) 13 | job = qs.job 14 | input = qs.input 15 | webhook = qs.webhook 16 | } else { 17 | const body = await json(req) 18 | job = body.job 19 | input = body.input 20 | webhook = body.webhook 21 | } 22 | res.writeHead(200, {"Content-Type":"text/event-stream", "Cache-Control":"no-cache", "Connection":"keep-alive"}) 23 | const startTime = new Date() 24 | res.write(JSON.stringify({ date: startTime.toISOString(), type: 'start', text: 'starting job'})+'\n') 25 | const result = await deploy(job, input, (logLine) => { 26 | // console.log("server saw:", logLine) 27 | const { type, text, date } = logLine 28 | res.write(JSON.stringify({ date, type, text })+'\n') 29 | }) 30 | const endTime = new Date() 31 | const duration = endTime.getTime() - startTime.getTime() 32 | const resultLog = { 33 | date: endTime.toISOString(), 34 | finished: true, 35 | type: 'result', 36 | text: result, 37 | duration: { 38 | ms: duration, 39 | friendly: humanizeDuration(duration) 40 | } 41 | } 42 | res.write(JSON.stringify(resultLog)) 43 | if (webhook) { 44 | const webhookResult = await rp({ 45 | uri: webhook, 46 | method: 'POST', 47 | body: resultLog, 48 | json: true 49 | }) 50 | // console.log("webhook payload:", resultLog) 51 | console.log("webhook result:", webhookResult) 52 | } 53 | res.end() 54 | } catch (err) { 55 | console.log(err.stack) 56 | res.write(JSON.stringify({ date: new Date().toISOString, type: 'error', text: err.message })+'\n') 57 | res.end() 58 | } 59 | } -------------------------------------------------------------------------------- /src/string-or-env.js: -------------------------------------------------------------------------------- 1 | module.exports = function(option) { 2 | console.log("type of option", typeof option) 3 | if (typeof option === 'object' && option.env) return process.env[option.env] 4 | return option 5 | } --------------------------------------------------------------------------------