├── .editorconfig ├── .gitignore ├── cli.js ├── demo.json ├── demo.svg ├── license.md ├── package.json └── readme.md /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | end_of_line = lf 3 | insert_final_newline = true 4 | trim_trailing_whitespace = true 5 | indent_style = space 6 | indent_width = 2 7 | 8 | [*.js] 9 | indent_style = tab 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | /** 5 | * remote-share-cli 6 | * Quickly share files from command line with the world 7 | * Author: Mario Nebl 8 | * License: MIT 9 | */ 10 | const fs = require('fs'); 11 | const http = require('http'); 12 | const os = require('os'); 13 | const path = require('path'); 14 | const util = require('util'); 15 | 16 | const copyPaste = require('copy-paste'); 17 | const chalk = require('chalk'); 18 | const localtunnel = require('localtunnel'); 19 | const fp = require('lodash/fp'); 20 | const meow = require('meow'); 21 | const mime = require('mime'); 22 | const portscanner = require('portscanner'); 23 | const generate = require('project-name-generator'); 24 | 25 | const log = util.debuglog('remote-share-cli'); 26 | 27 | const cli = meow(` 28 | Usage 29 | $ remote-share [file] 30 | 31 | Options 32 | -n, --name Forced download name of the file 33 | 34 | Examples 35 | $ remote-share shared.png 36 | https://mysteriouswomen.localtunnel.me 37 | 38 | $ cat shared.png | remote-share --name=shared.png 39 | https://humbleappliance.localtunnel.me 40 | `, { 41 | alias: { 42 | n: 'name' 43 | } 44 | }); 45 | 46 | let __timer = null; 47 | let connection; 48 | const stdin = process.stdin; 49 | 50 | // Kill process after 5 minutes of inactivity 51 | function resetTimer() { 52 | log('Reseting activitiy timer'); 53 | killTimer(); 54 | setTimer(); 55 | } 56 | 57 | function setTimer() { 58 | log('Setting activitiy timer, timing out after 5 minutes'); 59 | __timer = setTimeout(() => { 60 | log('Timeout without after 5 minutes without activitiy, killing process'); 61 | process.exit(0); 62 | }, 300000); 63 | } 64 | 65 | function killTimer() { 66 | if (__timer) { 67 | clearTimeout(__timer); 68 | } 69 | } 70 | 71 | function tunnel(port, options) { 72 | return new Promise((resolve, reject) => { 73 | localtunnel(port, options, (error, connection) => { 74 | if (error) { 75 | return reject(error); 76 | } 77 | resolve(connection); 78 | }); 79 | }); 80 | } 81 | 82 | /** 83 | * Copy input to clipboard 84 | * @param {String} input 85 | * @return {Promise} 86 | */ 87 | function copy(input) { 88 | return new Promise((resolve, reject) => { 89 | copyPaste.copy(`${input}\r\n`, (error) => { 90 | if (error) { 91 | return reject(error); 92 | } 93 | resolve(input); 94 | }); 95 | }); 96 | } 97 | 98 | /** 99 | * Check if an ip adress is external 100 | * 101 | * @return {Boolean} 102 | */ 103 | function isExternalAddress(networkInterface) { 104 | return networkInterface.family === 'IPv4' && 105 | networkInterface.internal === false; 106 | } 107 | 108 | /** 109 | * Get the local ip addresses 110 | * 111 | * @return {Object[]} 112 | */ 113 | function getAdresses() { 114 | return fp.flatten(fp.values(os.networkInterfaces())); 115 | } 116 | 117 | /** 118 | * Get the local ip address 119 | * 120 | * @return {String} 121 | */ 122 | function getLocalAddress() { 123 | const found = fp.find(isExternalAddress)(getAdresses()); 124 | return found ? found.address : 'localhost'; 125 | } 126 | 127 | /** 128 | * Get an open port on localhost 129 | * 130 | * @return {Promise} 131 | */ 132 | function getOpenPort() { 133 | return new Promise((resolve, reject) => { 134 | portscanner.findAPortNotInUse(1337, 65535, '127.0.0.1', (error, port) => { 135 | if (error) { 136 | return reject(error); 137 | } 138 | resolve(port); 139 | }); 140 | }); 141 | } 142 | 143 | /** 144 | * Get a file object 145 | * 146 | * @param {Object} options 147 | * @param {Boolean} options.isStdin - If the input is given via stdin 148 | * @param {String} [options.filePath] - Absolute path of file to read 149 | * @param {String} [options.fileName] - Basename of file to read, defaults to path.basename(option.filePath) 150 | * 151 | * @return {Promise} 152 | */ 153 | function getFile(options) { 154 | return new Promise((resolve, reject) => { 155 | const stream = options.isStdin ? 156 | stdin : fs.createReadStream(options.filePath); 157 | 158 | const name = options.isStdin ? 159 | options.fileName : path.basename(options.filePath); 160 | 161 | if (options.isStdin) { 162 | return resolve({ 163 | stream, 164 | name, 165 | size: null, 166 | ino: null, 167 | mtime: null 168 | }); 169 | } 170 | 171 | fs.stat(options.filePath, (error, stat) => { 172 | if (error) { 173 | return reject(error); 174 | } 175 | resolve({ 176 | stream, 177 | name, 178 | size: stat.size, 179 | ino: stat.ino, 180 | mtime: Date.parse(stat.mtime) 181 | }); 182 | }); 183 | }); 184 | } 185 | 186 | /** 187 | * Serve a File object on address with port on path id 188 | * 189 | * @param {Object} options 190 | * @param {File} options.file 191 | * @param {Number} options.port 192 | * @param {String} options.address 193 | * @param {String} options.id 194 | * @return {Promise} - started server instance 195 | */ 196 | function serve(options) { 197 | return new Promise((resolve, reject) => { 198 | const file = options.file; 199 | const downloadName = file.name || options.id; 200 | const subdomain = options.id.split('-').join('').slice(0, 20); 201 | 202 | const server = http.createServer((request, response) => { 203 | // Only HEAD and GET are allowed 204 | if (['GET', 'HEAD'].indexOf(request.method) === -1) { 205 | response.writeHead(405); 206 | return response.end('Method not Allowed.'); 207 | } 208 | 209 | resetTimer(); 210 | 211 | response.setHeader('Content-Type', mime.lookup(downloadName)); 212 | response.setHeader('Content-Disposition', `attachment; filename=${downloadName}`); 213 | 214 | if (file.size) { 215 | response.setHeader('Content-Length', file.size); 216 | } 217 | 218 | // Do not send a body for HEAD requests 219 | if (request.method === 'HEAD') { 220 | response.setHeader('Connection', 'close'); 221 | return response.end(); 222 | } 223 | 224 | const start = new Date(); 225 | 226 | file.stream.on('data', () => { 227 | resetTimer(); 228 | }); 229 | 230 | file.stream.pipe(response) 231 | .on('finish', () => { 232 | const duration = new Date() - start; 233 | const ttl = duration / 2; 234 | 235 | // Give the downloader the half absolute download time 236 | // to get remaining data from localtunnel.me 237 | setTimeout(() => { 238 | log(`Download completed after ${duration}ms, killing process in ${ttl}ms`); 239 | process.exit(0); 240 | }, ttl); 241 | }); 242 | }); 243 | 244 | server.on('error', reject); 245 | 246 | server.listen(options.port, () => { 247 | tunnel(options.port, { 248 | subdomain 249 | }) 250 | .then(tunneled => { 251 | connection = tunneled; 252 | return connection; 253 | }) 254 | .then(resolve) 255 | .catch(reject); 256 | }); 257 | }); 258 | } 259 | 260 | /** 261 | * Serve a File object on an open port 262 | * 263 | * @param {File} file 264 | * @return {Promise} - shareable address 265 | */ 266 | function serveFile(file) { 267 | return getOpenPort() 268 | .then(port => { 269 | const address = getLocalAddress(); 270 | const id = generate().dashed; 271 | const options = { 272 | address, 273 | port, 274 | id, 275 | file 276 | }; 277 | 278 | return serve(options) 279 | .then(connection => copy(connection.url)); 280 | }); 281 | } 282 | 283 | /** 284 | * Execute remote-share-cli main procedure 285 | * 286 | * @param {String[]} input - non-flag arguments 287 | * @param {Object} args - flag arguments 288 | * @return {Promise} - shareable address 289 | */ 290 | function main(filePath, args) { 291 | return new Promise((resolve, reject) => { 292 | // Start the inactivity timer 293 | setTimer(); 294 | 295 | // Sanitize input 296 | if (stdin.isTTY && typeof filePath === 'undefined') { 297 | const error = new Error('Either stdin or [file] have to be given'); 298 | error.cli = true; 299 | return reject(error); 300 | } 301 | 302 | const isStdin = stdin.isTTY !== true && typeof filePath === 'undefined'; 303 | 304 | // Get a file object 305 | const gettingFile = getFile({ 306 | isStdin, 307 | filePath, 308 | fileName: args.name 309 | }); 310 | 311 | gettingFile 312 | .then(serveFile) 313 | .then(resolve) 314 | .catch(reject); 315 | }); 316 | } 317 | 318 | main(cli.input[0], cli.flags) 319 | .then(output => { 320 | if (output) { 321 | console.log(output); 322 | } 323 | }) 324 | .catch(error => { 325 | if (error.cli) { 326 | if (error.message) { 327 | console.error(chalk.red(error.message)); 328 | } 329 | cli.showHelp(1); 330 | } 331 | 332 | setTimeout(() => { 333 | throw error; 334 | }); 335 | }); 336 | 337 | /** 338 | * @typedef {Object} File 339 | * @property {Stream} File.stream - Readstream of the file 340 | * @property {String} File.name - Basename of the file 341 | */ 342 | -------------------------------------------------------------------------------- /demo.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "width": 115, 4 | "height": 29, 5 | "duration": 21.018536, 6 | "command": null, 7 | "title": null, 8 | "env": { 9 | "TERM": "ansi", 10 | "SHELL": "/usr/local/bin/fish" 11 | }, 12 | "stdout": [ 13 | [ 14 | 0.316832, 15 | "\u001b]7;file://marneb-macbook.fritz.box/Users/marneb/Projects/labs/remote-share-cli\u0007" 16 | ], 17 | [ 18 | 0.051669, 19 | "\u001b[1m\u001b[30m⏎\u001b[0;10m \r⏎ \r\u001b[2K" 20 | ], 21 | [ 22 | 0.000195, 23 | "\u001b[34m~/Projects/labs/remote-share-cli\u001b[30m\u001b[0;10m \u001b[90mmaster*\u001b[30m\u001b[0;10m\t\u001b[36m\u001b[30m\u001b[0;10m\r\n\u001b[32m❯\u001b[30m\u001b[0;10m \u001b[K" 24 | ], 25 | [ 26 | 1.607333, 27 | "r" 28 | ], 29 | [ 30 | 0.000295, 31 | "\u001b[D\u001b[31m\u001b[1mr\u001b[0;10m" 32 | ], 33 | [ 34 | 0.00046, 35 | "\u001b[33memote-share readme.md \u001b[22D\u001b[30m\u001b[0;10m" 36 | ], 37 | [ 38 | 0.082962, 39 | "\u001b[31m\u001b[1me\u001b[0;10m\u001b[33mmote-share readme.md \u001b[21D\u001b[30m\u001b[0;10m" 40 | ], 41 | [ 42 | 0.07669, 43 | "\u001b[31m\u001b[1mm\u001b[0;10m\u001b[33mote-share readme.md \u001b[20D\u001b[30m\u001b[0;10m" 44 | ], 45 | [ 46 | 0.118756, 47 | "\u001b[31m\u001b[1mo\u001b[0;10m\u001b[33mte-share readme.md \u001b[19D\u001b[30m\u001b[0;10m" 48 | ], 49 | [ 50 | 0.064925, 51 | "\u001b[31m\u001b[1mt\u001b[0;10m\u001b[33me-share readme.md \u001b[18D\u001b[30m\u001b[0;10m" 52 | ], 53 | [ 54 | 0.087572, 55 | "\u001b[31m\u001b[1me\u001b[0;10m\u001b[33m-share readme.md \u001b[17D\u001b[30m\u001b[0;10m" 56 | ], 57 | [ 58 | 0.215781, 59 | "\u001b[31m\u001b[1m-\u001b[0;10m\u001b[33mshare readme.md \u001b[16D\u001b[30m\u001b[0;10m" 60 | ], 61 | [ 62 | 0.168405, 63 | "\u001b[31m\u001b[1ms\u001b[0;10m\u001b[33mhare readme.md \u001b[15D\u001b[30m\u001b[0;10m" 64 | ], 65 | [ 66 | 0.176219, 67 | "\u001b[31m\u001b[1mh\u001b[0;10m\u001b[33mare readme.md \u001b[14D\u001b[30m\u001b[0;10m" 68 | ], 69 | [ 70 | 0.072786, 71 | "\u001b[31m\u001b[1ma\u001b[0;10m\u001b[33mre readme.md \u001b[13D\u001b[30m\u001b[0;10m" 72 | ], 73 | [ 74 | 0.103439, 75 | "\u001b[31m\u001b[1mr\u001b[0;10m\u001b[33me readme.md \u001b[12D\u001b[30m\u001b[0;10m" 76 | ], 77 | [ 78 | 0.087727, 79 | "\u001b[31m\u001b[1me\u001b[0;10m\u001b[33m readme.md \u001b[11D\u001b[30m\u001b[0;10m" 80 | ], 81 | [ 82 | 0.000352, 83 | "\u001b[12D\u001b[35mremote-share\u001b[33m readme.md \u001b[11D\u001b[30m\u001b[0;10m" 84 | ], 85 | [ 86 | 0.855753, 87 | "\u001b[35m \u001b[33mreadme.md \u001b[10D\u001b[30m\u001b[0;10m" 88 | ], 89 | [ 90 | 0.000262, 91 | "\u001b[D \u001b[33mreadme.md \u001b[10D\u001b[30m\u001b[0;10m" 92 | ], 93 | [ 94 | 0.111804, 95 | "r\u001b[33meadme.md \u001b[9D\u001b[30m\u001b[0;10m" 96 | ], 97 | [ 98 | 0.000356, 99 | "\u001b[D\u001b[36m\u001b[4mr\u001b[33m\u001b[meadme.md \u001b[9D\u001b[30m\u001b[0;10m" 100 | ], 101 | [ 102 | 0.064531, 103 | "\u001b[36m\u001b[4me\u001b[33m\u001b[madme.md \u001b[8D\u001b[30m\u001b[0;10m" 104 | ], 105 | [ 106 | 0.080069, 107 | "\u001b[36m\u001b[4ma\u001b[33m\u001b[mdme.md \u001b[7D\u001b[30m\u001b[0;10m" 108 | ], 109 | [ 110 | 0.150728, 111 | "\u001b[36m\u001b[4md\u001b[33m\u001b[mme.md \u001b[6D\u001b[30m\u001b[0;10m" 112 | ], 113 | [ 114 | 0.808238, 115 | "\u001b[36m\u001b[4mm\u001b[33m\u001b[me.md \u001b[5D\u001b[30m\u001b[0;10m" 116 | ], 117 | [ 118 | 0.120108, 119 | "\u001b[36m\u001b[4me\u001b[33m\u001b[m.md \u001b[4D\u001b[30m\u001b[0;10m" 120 | ], 121 | [ 122 | 0.167853, 123 | "\u001b[36m\u001b[4m.\u001b[33m\u001b[mmd \u001b[3D\u001b[30m\u001b[0;10m" 124 | ], 125 | [ 126 | 0.335381, 127 | "\u001b[36m\u001b[4md\u001b[30m\u001b[0;10m\u001b[K" 128 | ], 129 | [ 130 | 0.000396, 131 | "\u001b[8D\u001b[36mreadme.d\u001b[30m\u001b[0;10m" 132 | ], 133 | [ 134 | 0.383606, 135 | "\u001b[D\u001b[K" 136 | ], 137 | [ 138 | 0.000333, 139 | "\u001b[7D\u001b[36m\u001b[4mreadme.\u001b[30m\u001b[0;10m" 140 | ], 141 | [ 142 | 0.20044, 143 | "\u001b[36m\u001b[4mm\u001b[30m\u001b[0;10m" 144 | ], 145 | [ 146 | 0.000277, 147 | "\u001b[33md \u001b[D\u001b[D\u001b[30m\u001b[0;10m" 148 | ], 149 | [ 150 | 0.095647, 151 | "\u001b[36m\u001b[4md\u001b[33m\u001b[m \u001b[D\u001b[30m\u001b[0;10m" 152 | ], 153 | [ 154 | 0.520011, 155 | "\u001b[36m\u001b[4m \u001b[30m\u001b[0;10m" 156 | ], 157 | [ 158 | 0.000294, 159 | "\u001b[D " 160 | ], 161 | [ 162 | 0.520919, 163 | "&" 164 | ], 165 | [ 166 | 0.000285, 167 | "\u001b[11D\u001b[36mreadme.md\u001b[30m\u001b[0;10m \u001b[32m&\u001b[30m\u001b[0;10m" 168 | ], 169 | [ 170 | 0.621977, 171 | "\r\n\u001b[30m\u001b[0;10m" 172 | ], 173 | [ 174 | 0.034, 175 | "\u001b[1m\u001b[30m⏎\u001b[0;10m \r⏎ \r\u001b[2K" 176 | ], 177 | [ 178 | 9.3e-05, 179 | "\r\n\u001b[34m~/Projects/labs/remote-share-cli\u001b[30m\u001b[0;10m \u001b[90mmaster*\u001b[30m\u001b[0;10m\t\u001b[36m\u001b[30m\u001b[0;10m\r\n\u001b[32m❯\u001b[30m\u001b[0;10m \u001b[K" 180 | ], 181 | [ 182 | 1.051059, 183 | "https://learnedchance.localtunnel.me\r\n" 184 | ], 185 | [ 186 | 1.458789, 187 | "\r\r" 188 | ], 189 | [ 190 | 0.000127, 191 | "\r\n\u001b[34m~/Projects/labs/remote-share-cli\u001b[30m\u001b[0;10m \u001b[90mmaster*\u001b[30m\u001b[0;10m\t\u001b[36m\u001b[30m\u001b[0;10m\r\n\u001b[32m❯\u001b[30m\u001b[0;10m \u001b[K\r\n" 192 | ], 193 | [ 194 | 4.5e-05, 195 | "\u001b[30m\u001b[0;10m" 196 | ], 197 | [ 198 | 0.039321, 199 | "\u001b[1m\u001b[30m⏎\u001b[0;10m \r⏎ \r\u001b[2K" 200 | ], 201 | [ 202 | 3.1e-05, 203 | "\r\n\u001b[34m~/Projects/labs/remote-share-cli\u001b[30m\u001b[0;10m \u001b[90mmaster*\u001b[30m\u001b[0;10m\t\u001b[36m\u001b[30m\u001b[0;10m\r\n\u001b[32m❯\u001b[30m\u001b[0;10m \u001b[K" 204 | ], 205 | [ 206 | 4.865556, 207 | "c" 208 | ], 209 | [ 210 | 0.000244, 211 | "\u001b[D\u001b[31m\u001b[1mc\u001b[0;10m" 212 | ], 213 | [ 214 | 0.000566, 215 | "\u001b[33murl https://thunderingzipper.localtunnel.me\u001b[43D\u001b[30m\u001b[0;10m" 216 | ], 217 | [ 218 | 0.079314, 219 | "\u001b[31m\u001b[1mu\u001b[0;10m\u001b[33mrl https://thunderingzipper.localtunnel.me\u001b[42D\u001b[30m\u001b[0;10m" 220 | ], 221 | [ 222 | 0.000603, 223 | "\u001b[D\u001b[D\u001b[35mcu\u001b[33mrl https://thunderingzipper.localtunnel.me\u001b[42D\u001b[30m\u001b[0;10m" 224 | ], 225 | [ 226 | 0.135428, 227 | "\u001b[35mr\u001b[33ml https://thunderingzipper.localtunnel.me\u001b[41D\u001b[30m\u001b[0;10m" 228 | ], 229 | [ 230 | 0.000612, 231 | "\u001b[3D\u001b[31m\u001b[1mcur\u001b[0;10m\u001b[33ml https://thunderingzipper.localtunnel.me\u001b[41D\u001b[30m\u001b[0;10m" 232 | ], 233 | [ 234 | 0.087402, 235 | "\u001b[31m\u001b[1ml\u001b[0;10m\u001b[33m https://thunderingzipper.localtunnel.me\u001b[40D\u001b[30m\u001b[0;10m" 236 | ], 237 | [ 238 | 0.000587, 239 | "\u001b[4D\u001b[35mcurl\u001b[33m https://thunderingzipper.localtunnel.me\u001b[40D\u001b[30m\u001b[0;10m" 240 | ], 241 | [ 242 | 0.247816, 243 | "\u001b[35m \u001b[33mhttps://thunderingzipper.localtunnel.me\u001b[39D\u001b[30m\u001b[0;10m" 244 | ], 245 | [ 246 | 0.000662, 247 | "\u001b[D \u001b[33mhttps://thunderingzipper.localtunnel.me\u001b[39D\u001b[30m\u001b[0;10m" 248 | ], 249 | [ 250 | 0.281935, 251 | "https://learnedchance.localtunnel.me\u001b[K" 252 | ], 253 | [ 254 | 0.000315, 255 | "\u001b[36D\u001b[36mhttps://learnedchance.localtunnel.me\u001b[30m\u001b[0;10m" 256 | ], 257 | [ 258 | 1.01278, 259 | "\r\n\u001b[30m\u001b[0;10m" 260 | ], 261 | [ 262 | 0.879898, 263 | "> Quickly share files from command line with the world\r\n\r\n![remote-share-cli Demo](./demo.gif)\r\n\r\n# remote-share-cli\r\n\r\n* :rocket: Dead Simple\r\n* :sparkles: Just works\r\n* :earth_africa: Globally accessible\r\n\r\nremote-share-cli exposes single files to the internet via https, either from stdin or a given file.\r\nThe exposing address is copied to your clipboard automatically.\r\n\r\nAfer a complete download or 5 minutes of inactivity remote-share-cli closes automatically.\r\n\r\n## Installation\r\n\r\n```\r\nnpm install -g remote-share-cli\r\n```\r\n\r\n## Usage\r\n\r\n```\r\n❯ remote-share --help\r\n\r\n Quickly share files from command line with the world\r\n\r\n Usage\r\n $ remote-share [file]\r\n\r\n Options\r\n -n, --name Forced download name of the file\r\n\r\n Examples\r\n $ remote-share shared.png\r\n https://mysteriouswomen.localtunnel.me\r\n\r\n $ cat shared.png | remote-share --name=shared.png\r\n https://humbleappliance.localtunnel.me\r\n```\r\n\r\n## Related projects\r\n\r\n* [share-cli](https://github.com/marionebl/share-cli) - Quickly shar" 264 | ], 265 | [ 266 | 0.000154, 267 | "e files from command line to your local network\r\n\r\n---\r\nremote-share-cli is built by [Mario Nebl](https://github.com/marionebl) and released\r\nunder the [MIT](./license.md) license.\r\n" 268 | ], 269 | [ 270 | 0.013287, 271 | "\r'remote-share readme.md &' has ended\u001b[K\r\n" 272 | ], 273 | [ 274 | 0.037482, 275 | "\u001b[1m\u001b[30m⏎\u001b[0;10m \r⏎ \r\u001b[2K" 276 | ], 277 | [ 278 | 0.000118, 279 | "\r\n\u001b[34m~/Projects/labs/remote-share-cli\u001b[30m\u001b[0;10m \u001b[90mmaster*\u001b[30m\u001b[0;10m\t\u001b[36m\u001b[30m\u001b[0;10m\r\n\u001b[32m❯\u001b[30m\u001b[0;10m \u001b[K" 280 | ], 281 | [ 282 | 2.516969, 283 | "\r\n\u001b[30m\u001b[0" 284 | ], 285 | [ 286 | 5.1e-05, 287 | ";10m\u001b[30m\u001b[0;10m" 288 | ] 289 | ] 290 | } -------------------------------------------------------------------------------- /demo.svg: -------------------------------------------------------------------------------- 1 | ~/Projects/labs/remote-share-climaster*remote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.md&https://learnedchance.localtunnel.mecurlhttps://thunderingzipper.localtunnel.mecurlhttps://learnedchance.localtunnel.me-n,--nameForceddownloadnameofthefileExamples$remote-shareshared.pnghttps://mysteriouswomen.localtunnel.me$catshared.png|remote-share--name=shared.pnghttps://humbleappliance.localtunnel.me```##Relatedprojects*[share-cli](https://github.com/marionebl/share-cli)-Quicklysharefilesfromcommandlinetoyourlocalnetwork---remote-share-cliisbuiltby[MarioNebl](https://github.com/marionebl)andreleasedunderthe[MIT](./license.md)license.'remote-sharereadme.md&'hasendedrrremote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.mdremote-sharereadme.dremote-sharereadme.dremote-sharereadme.remote-sharereadme.remote-sharereadme.mremote-sharereadme.mdremote-sharereadme.md&cccurlhttps://thunderingzipper.localtunnel.mecurlhttps://thunderingzipper.localtunnel.mecurlhttps://thunderingzipper.localtunnel.mecurlhttps://thunderingzipper.localtunnel.mecurlhttps://thunderingzipper.localtunnel.mecurlhttps://thunderingzipper.localtunnel.mecurlhttps://learnedchance.localtunnel.meQuicklysharefilesfromcommandlinewiththeworldUsage$remote-share[file]Options*[share-cli](https://github.com/marionebl/share-cli)-Quicklyshar -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Mario Nebl and [contributors](./graphs/contributors) 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 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remote-share-cli", 3 | "version": "1.0.4", 4 | "description": "Quickly share files from command line with the world", 5 | "bin": { 6 | "remote-share": "./cli.js" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/marionebl/remote-share-cli.git" 11 | }, 12 | "keywords": [ 13 | "share", 14 | "cli", 15 | "remote" 16 | ], 17 | "author": "Mario Nebl ", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/marionebl/remote-share-cli/issues" 21 | }, 22 | "homepage": "https://github.com/marionebl/remote-share-cli#readme", 23 | "dependencies": { 24 | "chalk": "^1.1.3", 25 | "copy-paste": "^1.3.0", 26 | "localtunnel": "^1.8.1", 27 | "lodash": "^4.13.1", 28 | "meow": "^3.7.0", 29 | "mime": "^1.3.4", 30 | "portscanner": "^1.0.0", 31 | "project-name-generator": "^2.1.2" 32 | }, 33 | "devDependencies": { 34 | "xo": "^0.16.0" 35 | }, 36 | "engines": { 37 | "node": ">=4" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | > Quickly share files from command line with the world 2 | 3 |

4 | 5 |

6 | 7 | > Demo created with [svg-term-cli](https://github.com/marionebl/svg-term-cli): 8 | > `cat demo.json | svg-term -o demo.svg` 9 | 10 | # remote-share-cli 11 | 12 | * :rocket: Dead Simple 13 | * :sparkles: Just works 14 | * :earth_africa: Globally accessible 15 | 16 | remote-share-cli exposes single files to the internet via https, either from stdin or a given file. 17 | The exposing address is copied to your clipboard automatically. 18 | 19 | Afer a complete download or 5 minutes of inactivity remote-share-cli closes automatically. 20 | 21 | ## Installation 22 | 23 | ``` 24 | npm install -g remote-share-cli 25 | ``` 26 | 27 | ## Usage 28 | 29 | ``` 30 | ❯ remote-share --help 31 | 32 | Quickly share files from command line with the world 33 | 34 | Usage 35 | $ remote-share [file] 36 | 37 | Options 38 | -n, --name Forced download name of the file 39 | 40 | Examples 41 | $ remote-share shared.png 42 | https://mysteriouswomen.localtunnel.me 43 | 44 | $ cat shared.png | remote-share --name=shared.png 45 | https://humbleappliance.localtunnel.me 46 | ``` 47 | 48 | ## Related projects 49 | 50 | * [share-cli](https://github.com/marionebl/share-cli) - Quickly share files from command line to your local network 51 | 52 | --- 53 | remote-share-cli is built by [Mario Nebl](https://github.com/marionebl) and released 54 | under the [MIT](./license.md) license. 55 | --------------------------------------------------------------------------------