diff --git a/README.md b/README.md new file mode 100644 index 0000000..a223f58 --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# spies + +Spy on a running node program by having a man on the inside + + npm install spies + +The first thing you need is to setup a spy inside your program + +``` js +var spies = require('spies'); + +spies(9999, function(spy) { + spy.on('echo', function(value) { + spy.log(value); + }); + spy.on('load-avg', function() { + spy.log(require('os').loadavg()); + }); +}); +``` + +Afterwards you can use `netcat` to contact and debrief your spy + + nc localhost 9999 + +This starts a `repl` where you can type in commands + + help + $ : help + : watch + : echo + : load-avg + +`help` and `watch` are build in commands than print the help and runs the same command every 1 second. +You invoke a command simply typing it and pressing `enter` and the result will be pretty printed below. + + load-avg + $ : 0.30126953125 + : 0.3203125 + : 0.33642578125 + +To pass arguments to the commands simply seperate them them by a `space` + + echo hello + $ : hello \ No newline at end of file diff --git a/example.js b/example.js new file mode 100644 index 0000000..f0d3c15 --- /dev/null +++ b/example.js @@ -0,0 +1,10 @@ +var spies = require('spies'); + +spies(9999, function(spy) { + spy.on('echo', function(value) { + spy.log(value); + }); + spy.on('load-avg', function() { + spy.log(require('os').loadavg()); + }); +}); \ No newline at end of file diff --git a/format.js b/format.js new file mode 100644 index 0000000..13e9721 --- /dev/null +++ b/format.js @@ -0,0 +1,42 @@ +var format = function(obj) { + var res = []; + + var visit = function(prev, val) { + if (val === undefined || val === null) return res.push([prev, '(nil)']); + if (typeof val !== 'object') return res.push([prev, ''+val]); + if (Array.isArray(val) && !val.length) return res.push([prev, '(empty)']); + + if (Array.isArray(val)) { + val.forEach(function(item) { + visit(prev, item); + }); + return; + } + + Object.keys(val).forEach(function(key) { + visit(prev ? prev+'.'+key : key, val[key]); + }); + }; + + if (typeof obj !== 'object' || !obj || (Array.isArray(obj) && typeof obj[0] !== 'object')) { + obj = {'$':obj}; + } + + visit('', obj); + + var prev = []; + var max = res.reduce(function(sofar, line) { + return line[0].length > sofar.length ? line[0] : sofar; + }, '').replace(/./g, ' ')+' \x1B[90m:\x1B[39m '; + + return res.map(function(line) { + var prefix = line[0].split('.').map(function(l, i) { + return l === prev[i] ? l.replace(/./g, ' ') : l; + }).join('.').replace(/ \. /g, ' '); + var suffix = max.slice(-(max.length-line[0].length)); + prev = line[0].split('.'); + return '\x1B[36m'+prefix+'\x1B[39m'+suffix+line[1]+'\n'; + }).join(''); +}; + +module.exports = format; \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..82ae02d --- /dev/null +++ b/index.js @@ -0,0 +1,87 @@ +var net = require('net'); +var format = require('./format'); +var Stream = require('stream'); + +var CLEAR = new Buffer('G1tIG1sySg==', 'base64'); + +var noop = function() {}; + +var Shell = function() { + this.buffer = ''; + this.once('pipe', function(stream) { + stream.on('error', noop); // ignore errors yo + stream.setEncoding('utf-8'); + }); + + this.readable = true; + this.writable = true; +}; + +Shell.prototype.__proto__ = Stream.prototype; + +Shell.prototype.write = function(data) { + var self = this; + var messages = (this.buffer+data).split('\n'); + this.buffer = messages.pop(); + messages.forEach(function(message) { + if (!message.trim()) return self.emit('help'); + if (!self.readable) return; + message = message.split(/\s+/g).map(function(item) { + if (/^\d+$/.test(item)) return parseInt(item, 10); + return item; + }); + self.emit.apply(self, message); + }); +}; + +Shell.prototype.end = function() { + this.finish('end'); +}; + +Shell.prototype.destroy = function() { + this.finish('close'); +}; + +Shell.prototype.finish = function(name) { + if (!this.readable) return; + this.readable = false; + this.writable = false; + this.emit(name); +}; + +Shell.prototype.log = function(value) { + this.emit('data', format(value)); +}; + +module.exports = function(port, onshell) { + net.createServer(function(socket) { + var sh = new Shell(); + + socket.pipe(sh).pipe(socket); + + var cmds = []; + + sh.on('newListener', function(name) { + cmds.push(name); + }); + sh.on('help', function() { + sh.log(cmds); + }); + sh.on('watch', function() { + var args = arguments; + var watch = setInterval(function() { + sh.emit('data', CLEAR); + sh.emit.apply(sh, args); + }, 1000); + + sh.once('close', function() { + clearInterval(watch); + }); + }); + + onshell(sh); + }).listen(port); +}; +module.exports.sh = function() { + return new Shell(); +}; \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..efccf4f --- /dev/null +++ b/package.json @@ -0,0 +1,9 @@ +{ + "name":"spies", + "version":"0.1.0", + "dependencies": {}, + "repository": "git://github.com/mafintosh/spies", + "description":"spy on a running program", + "keywords": ["repl","shell","nc"], + "author": "Mathias Buus Madsen " +}